科普知识
Geffery Hinton被称为“深度学习之父”、“神经网络先驱”、“AI教父”,他的名字响彻整个AI领域,他的一举一动,都是热点导向。以深度神经网络为代表的深度学习模型,在19世纪70年代进入寒潮以来,再次焕发出活力、得到学术界和工业界广泛关注,与他的贡献密不可分。
前言
上期的文章中,我们学会了TensorFlow中全连接层的搭建和输入数据的喂入方法,这些都是构建深度学习项目的基础,本来打算再详细介绍下TensorFlow中卷积层的搭建,但是实际看来单纯的介绍卷积层并没有必要,我们可以在实战项目中再进行解读,有些东西只要能看懂,会怎么用即可,今天我们就来讲解实战项目的第一步--图像分类数据构建。
一、TensorFlow之图像分类数据构建
本次实战项目是TensoerFlow的第一个项目,对于第一的个项目,小编决定采用自己最熟悉的东西--图像分类,顾名思义,就是将不同类别的图像进行分类,每一个图像有自己独特的标签,就像每一个人都有自己的名字一样,当然,我们这里的图像分类数目一般不会太多。
初学者掌握2分类-10分类即可,代码都是相通的,只有分类数目不同,如果我们要做猫狗分类项目,那么即为二分类,输入一张猫或者狗的图像,训练好深度学习模型会自动输出该图片的分类所属,也就是具体的标签(是猫还是狗),如下文图所示。如果是做人脸识别,则每一个图像的标签为人的姓名,有多少个不同的人,就有多少个分类数目。
第一项目我们做二分类实战--猫狗识别,该项目的代码的过程主要包括五个部分,分别是:数据构建,模型搭建,模型训练,模型验证,模型测试。今天先分享数据构成部分。
1.1数据构建
对于深度学习的分类项目,一般第一步是数据构建部分,即对神经网络的输入数据进行构建,并不是随便的输入一张图像即可,先回顾一下我们在理论篇中讲解的神经网络模型的运行过程是读取数据本身和其标签,数据本身传入网络进行学习,网络的输出则和标签进行比较(进行损失函数计算),最后通过损失函数来对神经网络模型进行参数更新,反复迭代,直到神经网络模型输出的值与标签十分接近即可认为训练成功,进而结束训练。
那么神经网络的输入数据是怎样的呢?首先我们的数据是图像数据,图像数据有自己的维度信息,也就是长宽高(即三个维度),其次标签则是图像的类别(是猫还是狗),通常包含两个文件夹,一个是所有图像时猫的文件夹,另一个是所有图像是狗的文件夹,这两个文件夹的名字自然就是猫和狗了,一般来说,做深度学习项目,我们会区分训练集和验证集,甚至还有测试集,这三个集不包含同一个图像,也就是同一个图像只能单一的出现在一个集里面,这样做的好处是,如果在训练集中进行了训练,我们需要在验证集上验证我们的模型的好坏,但是验证集中不存在训练集中出现过的图像,因为训练过的图像肯定是认识的,神经网络就是要到达训练目标后实现预测类似目标而不是统一目标的效果,这样才能检测器性能。所以这三个集也就是三个不同的文件夹(一般用train,val,test进行文件夹命名),然后每一个文件夹下面就是包含了猫和狗图像的两个文件夹,如下图所示:
这里为了简便,我们只用到了训练集个测试集
训练集:
测试集:
图像展示:
在实际的输入过程中,图像的标签我们会设置成数字作为神经网络的输入(比如,cat对应0,dog对应1),之前的举例中,神经网络的模型是一个数据吗,对应这里是一张图像,这样的话训练太慢,因为深度学习的数据通常是成千上万,因此,深度学习训练的时候支持批数据训练,即可以一次性学习几十张图像,以此加快学习过程,事实证明也必须这样做,不然就太浪费资源了,同时训练的效果会更好。
1.2 代码实现(一)
由于深度学习平台支持批数据的读取方式,因此我们考虑的方法是将所有的图像的文件路径和标签一次性读取出来,而不是读取图像数据本身,然后采用队列的方式用TensorFlow读取一批数据后用于训练,这个时候就是读取一批数据的路径和标签,然后根据路径获取到真实的图像数据,随后传入到神经网络模型。因此,代码的第一部分为,读取所有的图像路径和对应的标签,由于我们标签名就是文件夹的名字,因此同一个文件夹下的图像的标签都是一样的,都是狗或者都是猫。实际代码如下:
代码语言:javascript复制def get_files(file_dir):
# 声明两个列表,一个用于装图像的路径,一个用于装图像的标签
image_list, label_list = [], []
# 循环进入文件夹读取图像路径和标签
for label in os.listdir(file_dir):
# 这里进入到cat或者dog目录
print("当前的label:{}".format(label))
img_dir = file_dir label
print("img_dir:{}".format(img_dir))
for img in os.listdir(img_dir):
print("img:{}".format(img))
#这里进入到cat文件夹获取到图像的路径
img_path = img_dir '/' img
print("img_path:{}".format(img_path))
# 添加对应的图像路径和标签到事先准备好的列表中
image_list.append(img_path)
label_list.append(int(label_dict[label]))
# 获取当前的训练集或者测试集中有多少图像
print('There are %d data' %(len(image_list)))
# 将图像路径与标签转换为numpy 数组类型,这里会形成两行,一行是所有图像的路径,一行是对应的标签
temp = np.array([image_list, label_list])
print("temp:{}".format(temp))
# 进行转置,也就是反转变成两列,第一列是图像路径,第二列是标签,同一行的表示为:图像路径,图像标签
temp = temp.transpose()
print("temp2:{}".format(temp))
#然后就是以行为单位打乱数据,便于后期训练,注意这里同一行的数据没有打乱,只是第n行可能变成了第一行 第二行类似
np.random.shuffle(temp)
# 取出打乱后的图像路径(所有)
image_list = list(temp[:, 0])
print("image_list:{}".format(image_list))
# 取出打乱后的图像标签(所有)
label_list = list(temp[:, 1])
print("label_list:{}".format(label_list))
# 将标签中的每一个数字转换为整数
label_list = [int(i) for i in label_list]
print("label_list:{}".format(label_list))
# print(image_list)
# print()
# print(label_list)
return image_list, label_list
为便于各位朋友查看每一行代码的执行结果,小编打印出来了,进入到第一个文件夹的结果:
第二个文件夹:
以上代码的最终返回结果是两个列表,一个包含了所有的图像路径,另一个包含了所有图像对应的标签(0或者1),代码的流程为:首先进入到train或者val文件夹,随后获取下面的具体的分类文件夹,紧接着进入某一个分类文件夹获取到所有的图像名,然后根据前面的一个个文件夹组成图像的实际存储路径,然后根据分类文件夹得到标签,进而将当前图像路径和其标签存储在两个列表,循环获取完每一个分类文件夹下的图像即可结束,最终的返回结果如下:
1.3 代码实现(二)
前面已经获取到了训练集或者验证集中所有的图像和标签,下一步就是利用TensorFlow获取一定数量的批数据(此时为图像的路径)将其转换为实际的图像数据,这个时候的数据是Tensor格式,对应的标签也是,批数据可以自己指定,通常为16的倍数:8,16,32,128等等,即一次性读取多少张图像用于训练,实现代码如下:
代码语言:javascript复制def get_batch(image, label, image_W, image_H, batch_size, capacity,is_training):
# 将图像和标签转换为tensor格式
image = tf.cast(image, tf.string)
label = tf.cast(label, tf.int32)
# make an input queue
# 形成队列 用于循环读取
input_queue = tf.train.slice_input_producer([image, label], shuffle=False)
# 取出标签
label = input_queue[1]
# 根据图像路径读取为图像
image_contents = tf.read_file(input_queue[0])
# 图像解码,也就是长宽高的维度数据
image = tf.image.decode_jpeg(image_contents, channels=3)
# 统一图像尺寸
image = tf.image.resize_images(image, (image_W, image_H))
# 数据增强,如果是训练集通常做一点改变,增加图像复杂度,让网络学习得更多
if is_training:
#image = tf.image.resize_image_with_pad(image, target_height=image_W, target_width=image_H
# 随机左右翻转
image = tf.image.random_flip_left_right(image)
# 随机上下翻转
image = tf.image.random_flip_up_down(image)
# 随机设置图片的亮度
image = tf.image.random_brightness(image, max_delta=32/255.0)
# 随机设置图片的对比度
#image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
# 随机设置图片的色度
image = tf.image.random_hue(image, max_delta=0.05)
# 随机设置图片的饱和度
#image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
# 标准化,使图片的均值为0,方差为1,图像归一化
image = image/255
# 生成为批数据
image_batch, label_batch = tf.train.batch([image, label],
batch_size= batch_size,
num_threads= 64,
capacity = capacity)
# tf.summary.image("input_img", image_batch, max_outputs=5)
# 改变维度形状为批数据的大小,如果是16的批数据,则标签也为16个,一一对应
label_batch = tf.reshape(label_batch, [batch_size])
# 图像数据转换为浮点类型
image_batch = tf.cast(image_batch, tf.float32)
# 返回一个批次图像的真是数据和标签。都为Tensor格式
return image_batch, label_batch
以上批数据的结果需要集合队列来查看,并且在TensorFlow打开会话的方式下查看,代码如下:
代码语言:javascript复制
if __name__ == '__main__':
IMG_W = 150 # resize图像,太大的话训练时间久
IMG_H = 150
BATCH_SIZE = 128
CAPACITY = 256
# 获取批次batch
train_dir = '../data/train/' # 训练样本的读入路径
test_dir = '../data/test/' # 测试样本的读入路径
train, train_label = get_files(train_dir)
# print(np.array(train).shape)
val, val_label = get_files(test_dir)
sess = tf.Session()
# 批数据-训练数据及标签
train_batch, train_label_batch = get_batch(train, train_label, IMG_W, IMG_H, BATCH_SIZE, CAPACITY,True)
# 批数据-测试数据及标签
val_batch, val_label_batch = get_batch(val, val_label, IMG_W, IMG_H, BATCH_SIZE, CAPACITY,False)
# 队列监控
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
try:
for step in np.arange(10000):
if coord.should_stop():
break
tr_batch, tr_bat_label = sess.run([train_batch, train_label_batch])
print("%%%%%%%%%%%%% train.type=={} train_label.shape:{} %%%%%%%%%%%%%".format(tr_batch.shape, tr_bat_label.shape))
except tf.errors.OutOfRangeError:
print('Done training -- epoch limit reached')
finally:
coord.request_stop()
代码运行结果:
以上即可看到,我们最终输入神经网络的数据是一个批次的数据,这里我的批次为128,即一次性读取128张图像进入到神经网络中,图像的长宽高为:150,150,3,三通道彩色图像,标签的维度为:批数据的维度,即读取了多少张图像就会同时读取多少个标签,标签和图像是一一对应的,不能出错。
结语
以上内容就是今天的重点分享了,逻辑可能比较好理解,但是代码还是稍微有点复杂,代码中有几个函数可能大家没学过,不过没关系只要知道这个函数是干嘛的即可,如有有不懂的欢迎大家随时后台提问,也可以加小编的微信,我们一起探讨,另外需要注意的是,这种队列读取数据集的方式目前已经在新的版本中逐渐弃用,后期我们会再次分享一种更加简单的数据读取方式,只要是放入文件夹路径即可,下期的文章我们将会构建一个简单的神经网络,用于训练,敬请期待。
今天的内容希望大家好好吸收哦,先读懂逻辑(即我们的数据与标签构成的形式),然后去仔细看代码,有不懂的可以一行行进行调试,小编也在上面调试给大家看了,当初小编学习的时候也是不太懂,自己一行行调试懂了才逐渐往后面学习的,其次,这份代码是通用型代码,后期只需要改一下分类数目即可。
周末愉快,我们下期再见!
编辑:玥怡居士|审核:小圈圈居士