嗨,我是error。
这次的笔记是关于tensorflow基本框架的搭建,零基础带你熟悉如何应用keras搭建自己的CNN模型,并训练自己的数据,实现深度学习。
代码主要参考来源自
【国家精品课程】北京大学人工智能实践-TensorFlow2.0
CIFAR10数据集介绍,并使用卷积神经网络训练图像分类模型
这篇文章主要是写给tensorflow零基础但深度学习对CNN结构有一定了解的朋友,故重点会放在详细介绍代码实现CNN结构的方法上面。
首先要了解的是keras最基本的八股文式构建法,即
首先是import各类的库,然后分train和test数据(可以直接使用keras官方的数据也可以使用自己准备的数据,下面会分别讲解)。构建model后compile最后fit就完成了整个训练,最后的summary可有可无,主要是打印网络结构。
下面老师给出了三个主要对象的参数文档说明。
首先说明下我的数据来源,来自CIFAR10数据集,照片都放在train文件夹内,在根目录下有trainlabels的csv表格标记着每一张图片的标签
根目录下面还有x_train和y_train的npy文件,每次运行代码时都检测是否存在这两个文件,若不存在,则从官网下载CIFAR10文件并保存,若存在则直接调用,不再重复下载。
根目录下的checkpoint文件夹存放着每次训练的参数,以便下次训练时沿用上次已经训练好的参数而不是重复计算,实现了断点续训。
import tensorflow as tf from PIL import Image import numpy as np import os import matplotlib.pyplot as plt from tensorflow.keras.preprocessing.image import ImageDataGenerator train_path = './train/' train_csv = './trainLabels.csv' x_train_savepath = './x_train.npy' y_train_savepath = './y_train.npy'
在import完所需要的库后定义需要的路径以便后面传参时方便。
然后我们定义一个generateds的函数来把我们从csv表格标记的每一个图片的标签对应上就好了。
先定义两个空列表,分别是x和y,因为列表是有顺序的,只要我们放入列表的顺序和表格的顺序是一致,那么它们的标签就是对应匹配的。
def generateds(path, csv): f = open(csv, 'r') # 以只读形式打开csv文件 contents = f.readlines()[1:] # 读取文件中除了第一行的所有行,因为第一行是id/labels的头 f.close() # 关闭csv文件 x, y_ = [], [] # 建立空列表 for content in contents: # 逐行取出 value = content.split(",") # 以空格分开,图片路径为value[0] , 标签为value[1] , 存入列表 img_path = path + value[0] + '.png' # 拼出图片路径和文件名 img = Image.open(img_path) # 读入图片 img = np.array(img.convert('L')) # 图片变为8位宽灰度值的np.array格式 img = img / 255. # 数据归一化 (实现预处理) x.append(img) # 归一化后的数据,贴到列表x y_.append(value[1]) # 标签贴到列表y_ print('loading : ' + content) # 打印状态提示 x = np.array(x) # 变为np.array格式 y_ = np.array(y_) # 变为np.array格式 y_ = y_.astype(np.int64) # 变为64位整型 return x, y_ # 返回输入特征x,返回标签y_
下面这一个part就是实现防止重复处理CIFAR10数据,因为这里的图片都是32x32,所以在第7行reshape的时候就是32x32,如果不是的话要调整否则会报错无法继续运行,因为我们待会训练的时候是直接把train分割成训练集和测试集,不存在一个单独的测试集,所以我们就注释掉老师写的test部分的代码了。
if os.path.exists(x_train_savepath) and os.path.exists(y_train_savepath): print('-------------Load Datasets-----------------') x_train_save = np.load(x_train_savepath) y_train = np.load(y_train_savepath) # x_test_save = np.load(x_test_savepath) # y_test = np.load(y_test_savepath) x_train = np.reshape(x_train_save, (len(x_train_save), 32, 32, 1)) # x_test = np.reshape(x_test_save, (len(x_test_save), 28, 28)) else: print('-------------Generate Datasets-----------------') x_train, y_train = generateds(train_path, train_csv) # x_test, y_test = generateds(test_path, test_txt) print('-------------Save Datasets-----------------') x_train_save = np.reshape(x_train, (len(x_train), -1)) # = np.reshape(x_test, (len(x_test), -1)) np.save(x_train_savepath, x_train_save) np.save(y_train_savepath, y_train) # np.save(x_test_savepath, x_test_save) # np.save(y_test_savepath, y_test)
最后在正式训练之前,为了加强训练的rubust性,我们选择对数据进行一个预处理,代码如下。
image_gen_train = ImageDataGenerator( rescale=1. / 1., # 如为图像,分母为255时,可归至0~1 rotation_range=45, # 随机45度旋转 width_shift_range=.15, # 宽度偏移 height_shift_range=.15, # 高度偏移 horizontal_flip=False, # 水平翻转 zoom_range=0.5 # 将图像随机缩放阈量50% ) image_gen_train.fit(x_train)
关于ImageDataGenerator参数的文档说明如下:
下面就是模型的主要构建部分了,主要选择的是简单的卷积层+最大池化层+dropout的经典搭配模型,最后再更上几个全连接层。
model = tf.keras.Sequential() ##特征提取阶段 #第一层 model.add(tf.keras.layers.Conv2D(16,kernel_size=(3,3),padding='same',activation=tf.nn.relu,data_format='channels_last',input_shape=x_train.shape[1:])) #卷积层,16个卷积核,大小(3,3),保持原图像大小,relu激活函数,输入形状(28,28,1) model.add(tf.keras.layers.Conv2D(16,kernel_size=(3,3),padding='same',activation=tf.nn.relu)) model.add(tf.keras.layers.MaxPool2D(pool_size=(2,2))) #池化层,最大值池化,卷积核(2,2) model.add(tf.keras.layers.Dropout(0.2)) #第二层 model.add(tf.keras.layers.Conv2D(32,kernel_size=(3,3),padding='same',activation=tf.nn.relu)) model.add(tf.keras.layers.Conv2D(32,kernel_size=(3,3),padding='same',activation=tf.nn.relu)) model.add(tf.keras.layers.MaxPool2D(pool_size=(2,2))) model.add(tf.keras.layers.Dropout(0.2)) ##分类识别阶段 #第三层 model.add(tf.keras.layers.Flatten()) #改变输入形状 #第四层 model.add(tf.keras.layers.Dense(128,activation='relu')) #全连接网络层,128个神经元,relu激活函数 model.add(tf.keras.layers.Dense(10,activation='softmax')) #输出层,10个节点
然后直接编译我们写好的模型,这里我们选择adam优化器。
model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics=['sparse_categorical_accuracy'])
为了实现下一次训练的时候参数仍调用上次的最优参数,而不是重新重复训练,也就是实现断点续训,我们采用参数保存的方法,保存再checkpoint文件夹内。
checkpoint_save_path = "./checkpoint/ZYB.ckpt" if os.path.exists(checkpoint_save_path + '.index'): print('-------------load the model-----------------') model.load_weights(checkpoint_save_path) cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path, save_weights_only=True, save_best_only=True)
最后是代码的可视化部分,实现了最后打印出来一个loss和acc的表格方便观察。
history = model.fit(x_train, y_train, batch_size=640, epochs=5, validation_split=0.2, validation_freq=1, callbacks=[cp_callback]) print(history.history) loss = history.history['loss'] #训练集损失 val_loss = history.history['val_loss'] #测试集损失 acc = history.history['sparse_categorical_accuracy'] #训练集准确率 val_acc = history.history['val_sparse_categorical_accuracy'] #测试集准确率 plt.figure(figsize=(10,3)) plt.subplot(121) plt.plot(loss,color='b',label='train') plt.plot(val_loss,color='r',label='test') plt.ylabel('loss') plt.legend() plt.subplot(122) plt.plot(acc,color='b',label='train') plt.plot(val_acc,color='r',label='test') plt.ylabel('Accuracy') plt.legend() plt.show() model.summary()
最后训练的结果图示
模型结构
训练过程
import tensorflow as tf from PIL import Image import numpy as np import os import matplotlib.pyplot as plt from tensorflow.keras.preprocessing.image import ImageDataGenerator train_path = './train/' train_csv = './trainLabels.csv' x_train_savepath = './x_train.npy' y_train_savepath = './y_train.npy' #test_path = './mnist_image_label/mnist_test_jpg_10000/' #test_txt = './mnist_image_label/mnist_test_jpg_10000.txt' #x_test_savepath = './mnist_image_label/mnist_x_test.npy' #y_test_savepath = './mnist_image_label/mnist_y_test.npy' def generateds(path, csv): f = open(csv, 'r') # 以只读形式打开txt文件 contents = f.readlines()[1:] # 读取文件中所有行 f.close() # 关闭txt文件 x, y_ = [], [] # 建立空列表 for content in contents: # 逐行取出 value = content.split(",") # 以空格分开,图片路径为value[0] , 标签为value[1] , 存入列表 img_path = path + value[0] + '.png' # 拼出图片路径和文件名 img = Image.open(img_path) # 读入图片 img = np.array(img.convert('L')) # 图片变为8位宽灰度值的np.array格式 img = img / 255. # 数据归一化 (实现预处理) x.append(img) # 归一化后的数据,贴到列表x y_.append(value[1]) # 标签贴到列表y_ print('loading : ' + content) # 打印状态提示 x = np.array(x) # 变为np.array格式 y_ = np.array(y_) # 变为np.array格式 y_ = y_.astype(np.int64) # 变为64位整型 return x, y_ # 返回输入特征x,返回标签y_ if os.path.exists(x_train_savepath) and os.path.exists(y_train_savepath): print('-------------Load Datasets-----------------') x_train_save = np.load(x_train_savepath) y_train = np.load(y_train_savepath) # x_test_save = np.load(x_test_savepath) # y_test = np.load(y_test_savepath) x_train = np.reshape(x_train_save, (len(x_train_save), 32, 32, 1)) # x_test = np.reshape(x_test_save, (len(x_test_save), 28, 28)) else: print('-------------Generate Datasets-----------------') x_train, y_train = generateds(train_path, train_csv) # x_test, y_test = generateds(test_path, test_txt) print('-------------Save Datasets-----------------') x_train_save = np.reshape(x_train, (len(x_train), -1)) # = np.reshape(x_test, (len(x_test), -1)) np.save(x_train_savepath, x_train_save) np.save(y_train_savepath, y_train) # np.save(x_test_savepath, x_test_save) # np.save(y_test_savepath, y_test) image_gen_train = ImageDataGenerator( rescale=1. / 1., # 如为图像,分母为255时,可归至0~1 rotation_range=45, # 随机45度旋转 width_shift_range=.15, # 宽度偏移 height_shift_range=.15, # 高度偏移 horizontal_flip=False, # 水平翻转 zoom_range=0.5 # 将图像随机缩放阈量50% ) image_gen_train.fit(x_train) model = tf.keras.Sequential() ##特征提取阶段 #第一层 model.add(tf.keras.layers.Conv2D(16,kernel_size=(3,3),padding='same',activation=tf.nn.relu,data_format='channels_last',input_shape=x_train.shape[1:])) #卷积层,16个卷积核,大小(3,3),保持原图像大小,relu激活函数,输入形状(28,28,1) model.add(tf.keras.layers.Conv2D(16,kernel_size=(3,3),padding='same',activation=tf.nn.relu)) model.add(tf.keras.layers.MaxPool2D(pool_size=(2,2))) #池化层,最大值池化,卷积核(2,2) model.add(tf.keras.layers.Dropout(0.2)) #第二层 model.add(tf.keras.layers.Conv2D(32,kernel_size=(3,3),padding='same',activation=tf.nn.relu)) model.add(tf.keras.layers.Conv2D(32,kernel_size=(3,3),padding='same',activation=tf.nn.relu)) model.add(tf.keras.layers.MaxPool2D(pool_size=(2,2))) model.add(tf.keras.layers.Dropout(0.2)) ##分类识别阶段 #第三层 model.add(tf.keras.layers.Flatten()) #改变输入形状 #第四层 model.add(tf.keras.layers.Dense(128,activation='relu')) #全连接网络层,128个神经元,relu激活函数 model.add(tf.keras.layers.Dense(10,activation='softmax')) #输出层,10个节点 model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics=['sparse_categorical_accuracy']) checkpoint_save_path = "./checkpoint/ZYB.ckpt" if os.path.exists(checkpoint_save_path + '.index'): print('-------------load the model-----------------') model.load_weights(checkpoint_save_path) cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path, save_weights_only=True, save_best_only=True) history = model.fit(x_train, y_train, batch_size=640, epochs=5, validation_split=0.2, validation_freq=1, callbacks=[cp_callback]) print(history.history) loss = history.history['loss'] #训练集损失 val_loss = history.history['val_loss'] #测试集损失 acc = history.history['sparse_categorical_accuracy'] #训练集准确率 val_acc = history.history['val_sparse_categorical_accuracy'] #测试集准确率 plt.figure(figsize=(10,3)) plt.subplot(121) plt.plot(loss,color='b',label='train') plt.plot(val_loss,color='r',label='test') plt.ylabel('loss') plt.legend() plt.subplot(122) plt.plot(acc,color='b',label='train') plt.plot(val_acc,color='r',label='test') plt.ylabel('Accuracy') plt.legend() plt.show() model.summary()
照顾到部分朋友是完全零基础学习tenserflow,我这里将我的文件夹压缩打包,只要你配置好了python环境,直接解压就可以运行,同时还可以自己调整超参数多次训练寻找最优模型。因为数据庞大,我只能打包上传百度云,为防止链接经常挂的问题,关注微信公众号【error13】,后台回复【CIFAR】免费获得链接。