计算机视觉之人脸学习(五)

Mercia ·
更新时间:2024-11-14
· 671 次阅读

一.TensorFlow基础介绍

(一)TensorFlow中的基础概念介绍

1.什么是TensorFlow:Google开源的基于数据流图的科学计算库,适合用于机器学习,深度学习等人工智能领域,tensorflow的源码:

https://github.com/tensorflow

https://gitub.com/tensorflow/models

Tensor:张量(数据)

Flow:计算流(数据流向)

TensorFlow架构:(1)前端:编程模型,构造计算图,Python,C++,Java (2)后端:运行计算图,C++

2.为什么选择TensorFlow:高度的灵活性/真正的可移植性/将科研和产品联系在一起/自动求微分/多语言支持/性能最优化/社区内容丰富

3.Graph:图描述了计算的过程,可以通过tensorboard图形化流程结构,下面是一个图形化的结果:包括了相应的数据和运算,箭头表示数据的流向,这里的计算图就是我们所谓的网络。

对于这样的一个计算图,包括以下操作:

(1)声明(单个图/多个图)

单个图代码:

import tensorflow as tf

g=tf.Graph()#声明一个Graph

g=tf.get_default_graph()#获取当前默认的graph

x=tf.constant(0)

g=x.graph#通过变量也可以获取当前的graph

多个图代码:

g1 = tf.Graph()

with g1.as_default():

       x1 = tf.constant(1.0, name="x1")#利用with 来声明当前的变量所对应的Graph

g2 = tf.Gragh()

with g2.as_default():

      x2 = tf.contant(2.0,name="x2")

with tf.Session(graph=g2) as sess1:#我们在定义Session时,也可以将Graph指代相应的图,这时session则作用在当前graph下

       x1_list = tf.import_graph_def(g1.as_graph_def(),return_elements =["x1:0",name='')

     print(sess1.run(x1_list[0]+x2))

(2)保存为pb文件(我们网络训练好之后,我们会进行保存)

g1 = tf.Graph()

tf.train.write_graph(g1.as_graph_def(),'.','graph.pb',False)#这时我们就可以对当前的网络的参数和结构进行保存

(3)从pb中恢复Graph

with gfile.FastGFile("graph.pb",'rb')as f:

      graph_def = tf.GraphDef()

      graph_def.ParseFromString(f.read())

      tf.import_graph_def(graph_def, name='')

sess = tf.Session()

c1_tensor = sess.graph.get_tensor_by_name("c1:0")#节点的获取

c1 = sess.run(c1_tensor)#计算当前节点的值

(4)Tensorboard可视化

import tensorflow as tf

a=tf.contant(1,name='input_a')

b=tf.contant(2,name='input_b')#所有的变量都是tensor

c=tf.multiply(a,b,name='maltiply_c')

d=tf.add(a,b,name='add_d)

e=tf.add(d,c,name='add_e')

sess=tf.Session()

sess.run(e)#获取e节点所对应的值

writer=tf.summary.FileWriter('graph',sess.graph)#将graph添加到日志模块中

4.Session:图必须在称之为“会话"的上下文中执行,会话将图的操作分发到诸如CPU或GPU之类的设备上执行。完成了前端和后端的沟通,包括的操作:

(1)创建关闭

sess =tf.Session()

sess=tf.InteractiveSession()

with tf.Session()as Sess:

...(完成数据的注入,计算图在后端的执行)

sess.close

(2)注入机制(具体完成计算图运算的过程)

sess.run(...)#在run中指定需要指定计算的节点

sess.run(tf.global_variable_initializer())#获取当前计算图中全部的变量,然后run完成变量的初始化

a=tf.placeholder(dtype=tf.float32)

b=tf.placeholder(dtype=tf.float32)#a,b两个占位符

add =a+b

add_val=sess.run(add,feed_dict={a:1,b:2})#对a,b进行赋值,run对add进行计算

(3)指定设备

a = tf.placeholder(dtype=tf.float32)

b = tf.placeholder(dtype=tf.float32)

add = a+b#定义一个计算图

with tf.Session() as sess:

       with tf.device("/cpu:0):

              print(sess.run(add,feed_dict={a:1,b:2}))

(4)资源分配(控制GPU资源使用率)

config =tf.ConfigProto()

config.gpu_option.allow_growth =True

sesstion = tf.Session(config=config,...)#按需分配,可以知道当前网络的一个batchsize所占用的资源是多少,就可以清楚的设置batchsize的上限。

5.Tensor:在TensorFlow中,所有在节点之间传递的数据都为Tensor对象,N维数组,图像:(batch*height*width*channel)

定义方式:tf.constant(value,dtype,shape,name.verify_shape)#常量;tf.Variable(shape,dtype,name)#变量;tf.placeholder(dtype,shape,name)#占位符;tf.SparseTensor()#稀疏的张量

6.Operation:TensorFlow Graph中计算节点,输入输出均为Tensor,调用Session.run(tensor)或者tensor.eval()方可获取该Tensor的值

7.Feed:通过feed为计算图注入值,占位符的值

8.Fech:使用Fech获取计算结果。

tf.Session.run(fetches,feed_dict=None)#feches可以是一个tensor,也可以是list

(二)TensorFlow中的核心API

1.基本运算

.tf.expand_dims(inputs,dim,name=None)#维度的扩展

.tf.split(splt_dim,num_split,value,name='split')#维度的切分

.tf.concat(concat_dim,values,name='concat')#数据的拼接

.tf.cast()#数据类型的转化

.tf.reshape()#对数据形状的变化

.tf.equal()#判断是不是相等

.tf.matmul(a,b)#乘法

.tf.argmax()#最大值所对应的索引值

.tf.squeeze()#去掉向量中维度为1的相应的坐标轴

(1)tf.nn库:

.tf.nn.conv2d(卷积层)

.tf.nn.max_pool

.tf.nn.avg_pool(池化层)

.tf.nn.relu(激活层)

.tf.nn.dropout

.tf.nn.I2_normalize(归一化层)

.tf.nn.batch_normalization

.tf.nn.I2_loss

tf.nn.softmax_cross_entropy_with_logits(交叉熵)

(2)tf.train库:

.tf.train.Saver.save(模型的存储)

.tf.train.Saver.restore(模型的恢复)

.tf.train.GradientDescentOptimizer(0.01).minimize(loss)(模型的优化)

.tf.train.exponential_decay(1e-2.global_steps=sample_size/batch,decay_rate=0.98,staircase=True)(模型的学习率)

(3)TensorFlow中的数据操作

.TensorFlow提供了TFRecord的格式来统一存储数据,TFRecord将图像数据和标签放在一起的二进制文件,能更好的利用内存,实现快速的复制,移动,读取,存储

.数据读取:tf.train.string_input_producer,tf.train.slice_input_producer

.数据解析:tf.TFRecordReader,tf.parse_single_example

.数据写入:tf.python_io.TFRecordWriter

.数据写入相关的API方法:

.write=tf.python_io.TFRecordWriter()

.example=tf.train.Example()#会存储具体的数据,可以定义一个feature

.writer.close()

writer.writer(example.SerializeToString())#将数据序列化后写入TFRecord文件中

实战:

下面以cifar10为例,介绍如何使用tensorflow对图像进行数据读取和数据打包

编程环境:Ubuntu18.04,pycharm,tf1.13.1,cuda10.0,cudnn7.4.1(花了好几天安装了双系统,配好环境,呕心沥血呀)

在pycharm中创建工程:tf-rend-write

1.下载数据集:

方法一:可以到cifar10官网下载CIFAR-10 python version,另外官网上也提供了如何解析当前打包后的数据程序:

def unpickle(file): import pickle with open(file, 'rb') as fo: dict = pickle.load(fo, encoding='bytes') return dict

方法二:用程序直接在项目里下载(在工程下新建文件data装数据集)

import urllib.request as ur import os import sys import tarfile def download_and_uncompress_tarball(tarball_url, dataset_dir): """Downloads the `tarball_url` and uncompresses it locally. Args: tarball_url: The URL of a tarball file. dataset_dir: The directory where the temporary files are stored. """ filename = tarball_url.split('/')[-1] filepath = os.path.join(dataset_dir, filename) def _progress(count, block_size, total_size): sys.stdout.write('\r>> Downloading %s %.1f%%' % ( filename, float(count * block_size) / float(total_size) * 100.0)) sys.stdout.flush() filepath, _ = ur.urlretrieve(tarball_url, filepath, _progress)#直接将远程数据下载到本地。 print() statinfo = os.stat(filepath) print('Successfully downloaded', filename, statinfo.st_size, 'bytes.') tarfile.open(filepath, 'r:gz').extractall(dataset_dir)#指定解压路径和解压方式为解压gzip DATA_URL = 'http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz'#数据下载的URL DATA_DIR = 'data'#存储的文件夹路径 download_and_uncompress_tarball(DATA_URL, DATA_DIR)

解析:

(1)urlretrieve的使用

直接将远程数据下载到本地。

urllib.urlretrieve(url, filename, reporthook, data)

参数说明:

url:外部或者本地url(这里是DATA_URL)

filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据)(这里是DATA_DIR)

reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度(这里是_progress)

data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。

(2)os.stat() 方法

用于在给定的路径上执行一个系统 stat 的调用。

os.stat(path)

path -- 指定路径

返回值

stat 结构:

st_mode: inode 保护模式 st_ino: inode 节点号。 st_dev: inode 驻留的设备。 st_nlink: inode 的链接数。 st_uid: 所有者的用户ID。 st_gid: 所有者的组ID。 st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。 st_atime: 上次访问的时间。 st_mtime: 最后一次修改的时间。 st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。

下载好的数据集如图:

我们发现数据集是以二进制文件格式存储的,接下来我们会对二进制文件进行解析成具体图片,并存到image文件夹下的train和test文件夹里。以data_batch开头的为我们训练集,test_batch为我们测试集。

2.下面我们来解析二进制文件,首先将我们cifar10的10个分类存放在classfication这个变量中,在将官网的解析文件的脚本拷贝过来。

classification = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] def unpickle(file): import pickle with open(file, 'rb') as fo: dict = pickle.load(fo, encoding='bytes') return dict

我们可以发现这里的数据是以字典的形式进行存储的,字典的键值分别是data和label

先定义二进制文件的路径:

folders = '/home/jincheng/PycharmProjects/tf-read-write/data_manager/data/cifar-10-batches-py'

使用glob来获取当前文件夹下所有的训练样本所对应的文件:

trfiles = glob.glob(folders + "/data_batch*")

接下来对每个文件调用解析函数,获取到解析数据是一个字典,分别定义两个空list来读取字典的数据和标签。我们将标签打印出来看看。

data = [] labels = [] for file in trfiles: dt = unpickle(file) data += list(dt[b"data"]) labels += list(dt[b"labels"]) print(labels)

结果:(label值对应着classfication的下标)

接下来我们对数据进行解析,将图片进行存储,在cifar10中,图片的大小为32*32,我们将data转为4个纬度的数据,-1表达了当前有多少张图片,具体-1的值会根据data原始的维度来除以一张图片的大小:

imgs = np.reshape(data, [-1, 3, 32, 32]) #采用opencv对图像进行读取和存储,对所有图片进行遍历,img.shape第0个位置是当前数据的数量。由于cv对图片的存储纬度由所不同,先将 #[-1,3,32,32]转化为[-1,32,32,3],在将RGB转化为BGR for i in range(imgs.shape[0]): im_data = imgs[i, ...] im_data = np.transpose(im_data, [1, 2, 0])#[-1,32,32,3] im_data = cv2.cvtColor(im_data, cv2.COLOR_RGB2BGR) #接下来定义数据存放的位置,图片的下一级目录为图片的类别 f = "{}/{}".format("/home/jincheng/PycharmProjects/tf-read-write/data_manager/data/image/train", classification[labels[i]]) #如果文件夹不存在,则新建 if not os.path.exists(f): os.mkdir(f) #向相应类别的文件夹写入当前的图片 cv2.imwrite("{}/{}.jpg".format(f, str(i)), im_data)

运行得到以下结果:

同理也可以得到测试集的数据

下面我们进行数据打包,打包成TFRecord二进制文件格式的数据,并且将这些数据用于后续的模型训练

import glob idx = 0#idx用来指向我们当前已经遍历到了第几个类别 im_data = [] im_labels = [] for path in classification:#对10个类别进行遍历 path = "/home/jincheng/PycharmProjects/tf-read-write/data_manager/data/image/train/" + path#将train路径拼接上类别成为类别文件夹的路径 im_list = glob.glob(path + "/*")#利用glob获取类别文件夹下面的图片,返回的是图片路径list im_label = [idx for i in range(im_list.__len__())]#定义一个和图片list长度相同的标签list,实际是当前类别遍历的ID号 idx += 1#每次遍历完一个类别,将idx+1 im_data += im_list im_labels += im_label#这样我们就得到了训练集所有的图片数据和标签

接下来我们对这些获取得到的数据进行存储

tfrecord_file = "data/train.tfrecord"#定义用来存tfrecord文件的路径 writer = tf.python_io.TFRecordWriter(tfrecord_file)#定义tfrecord写入的实例,参数为文件的路径 index = [i for i in range(im_data.__len__())] np.random.shuffle(index)#用来在打包数据的时候就打乱顺序 for i in range(im_data.__len__()):#遍历当前数据文件 im_d = im_data[index[i]] im_l = im_labels[index[i]] data = cv2.imread(im_d)#现在图片的数据是图片的路径,所以通过opencv对图片的数据进行读取 #data = tf.gfile.FastGFile(im_d, "rb").read()#和opencv一样可以读取数据,并且度出是bytes型了,并且解码方式和opencv不同 ex = tf.train.Example(#将数据写入tfrecord需要定义一个example通过feature对数据进行存储 features = tf.train.Features( feature = {#通过字典存放数据 "image":tf.train.Feature( bytes_list=tf.train.BytesList(#采用bytes进行存储 value=[data.tobytes()])), "label": tf.train.Feature( int64_list=tf.train.Int64List(#laibel用int value=[im_l])), } ) )#通过example对一组图片的数据和标签进行封装 writer.write(ex.SerializeToString())#我们将example序列化后写入tfrecord中 writer.close()#写入数据后,我们将writee关闭

运行结果:

3.数据读取

方法一:采用tf.train.slice_input_producer:

import tensorflow as tf #定义两个list分别表达了图片的路径和label images = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg'] labels = [1, 2, 3, 4] [images, labels] = tf.train.slice_input_producer([images, labels],num_epochs=None,shuffle=True) #对输入的图片list按照相应的规则生成相应的张量,输出两个张量,num_pochs表示将所有样本循环训练的次数 with tf.Session() as sess:#通过session来执行当前从文件队列中获取tensor的流程 sess.run(tf.local_variables_initializer())#对局部变量进行初始化 tf.train.start_queue_runners(sess=sess)#构造文件队列填充的线程 for i in range(10): print(sess.run([images, labels]))#从文件队列中获取数据

结果:

方法二:tf.train.string_input_producer从文件中读取数据

3个文件分别为(文件名和label):

import tensorflow as tf filename = ['data/A.csv', 'data/B.csv', 'data/C.csv'] file_queue = tf.train.string_input_producer(filename,shuffle=True,num_epochs=2)#参数为文件路径的list,输出是文件队列 reader = tf.WholeFileReader()#定义文件的读取器 key, value = reader.read(file_queue)#获取到文件的数据 with tf.Session() as sess: sess.run(tf.local_variables_initializer()) tf.train.start_queue_runners(sess=sess) for i in range(6): print(sess.run([key, value]))

结果:

方法三:对TFRecord打包过的数据进行解析

import tensorflow as tf filelist = ['data/train.tfrecord']#定义一个list,就是想要读取的tfrecord文件的路径 file_queue = tf.train.string_input_producer(filelist,num_epochs=None,shuffle=True) #对于文件类的读取同样采用tf.train.string_input_producer,输出文件队列 reader = tf.TFRecordReader()#定义读取器 _, ex = reader.read(file_queue)#读取文件 feature = { 'image':tf.FixedLenFeature([], tf.string),#将bytes解码为string 'label':tf.FixedLenFeature([], tf.int64) }#对序列化之后的数据进行解码 batchsize = 2 batch = tf.train.shuffle_batch([ex], batchsize, capacity=batchsize*10,min_after_dequeue=batchsize*5) #通过shuffle_batch构造一个batchsize的数据 example = tf.parse_example(batch, features=feature)#对这个batch进行解码 image = example['image'] label = example['label'] image = tf.decode_raw(image, tf.uint8)#将string解码为uint8 image = tf.reshape(image, [-1, 32, 32, 3]) with tf.Session() as sess: sess.run(tf.local_variables_initializer()) tf.train.start_queue_runners(sess=sess) for i in range(1): image_bth, _ = sess.run([image,label]) import cv2 cv2.imshow("image", image_bth[0,...])#对第一张图片进行可视化 cv2.waitKey(0)#等待

结果:

(三)TensorFlow中的高层接口(slim)

.slim layers

.slim.arg_scope(list_ops_or_scope,**kwargs)#第一个是操作列表,第二个是这些操作共同用的参数

.在使用slim.batch_norm()函数时,我们需要定义以下两个参数

1.normalizer_fn=slim.batch_norm

2.normalizer_params=batch_norm_params:'is_training'(训练时设置为true),‘updates_collections':tf.GraphKeys.UPDATE OPS对batch_norm层进行标识

batch_norm层参数需另外更新,方法有两种:

.slim.data

有很多对数据的操作方法,在slim.__init__中有如下几种:

.slim evaluation

其中包含用于使用metric_ops.py模块中的指标编写模型评估脚本的帮助函数:

1.evaluation_loop(多次评估)

2.evaluation_once(一次评估)

.slim learning

学习率:(开始大,结尾小)

优化器:

.slim losses

.slim nets(已经定义好的网络结构)

.slim variables

.slim metrics

1.value_op是一个幂等操作,返回度量的当前值

2.update_op是执行上述聚合步骤以及返回度量值的操作

(四)TensorFlow实现数据增强

数据增强是防止模型过拟合的手段

1.tf.image库进行数据增强(颜色扰动,裁剪/Pad,噪声/模糊,翻转/旋转,Draw Boxes,标准化)

2.其他数据增强的方法:

扩大样本,增加图像的扰动性以及模型的鲁棒性

3.TensorFlow常见数据增强方法:

(五)Tensorboard调试技巧

1.Tensorboard可以进行网络可视化/训练中间结果可视化

pip3 install tensorboard

tensorboard --logdir logs

在浏览器中输入localhost:6060

配合tf.summary的使用:

记录可视化的值,将这些值添加到log中,比如sess.graph,bias,loss,image然后通过sess.run将合并后的中间结果存放到log中,我们在存放的时候是按照step(一个计数器,从0开始),最后tensorboard会将我们存好的log按照step进行绘制。

一些结果图:

下一期:TensorFlow挑战Cifar-10图像分类任务

金乘 原创文章 9获赞 11访问量 1812 关注 私信 展开阅读全文
作者:金乘



计算机视觉 学习

需要 登录 后方可回复, 如果你还没有账号请 注册新账号