使用tf.data自定义猫狗数据集,并使用自定义训练实现猫狗数据集的分类。html
咱们使用kaggle上的猫狗数据以及tf.data来创建本身的猫狗数据集。git
tf.data详细的使用方法中在Tensorflow学习笔记No.5中以经介绍过,这里只简略讲述。网络
打开kaggle中的notebook,点击右侧"+Add data",搜索以下数据集,并点击右侧"Add"。app
随后Cat and Dog这个数据集就会被添加在input目录下。dom
首先,导入须要的模块~函数
1 import tensorflow as tf 2 from tensorflow import keras 3 import matplotlib.pyplot as plt 4 %matplotlib inline 5 import numpy as np 6 import glob 7 import os 8 import pathlib
使用pathlib.path()方法获取文件目录。学习
文件目录以下:测试
1 data_root = pathlib.Path('../input/cat-and-dog/training_set/training_set')
data_root记录了猫狗数据集在kaggle中的储存位置。优化
随后咱们使用.glob()方法获取该路径下的全部图片路径。spa
1 all_image_path = list(data_root.glob('*/*.jpg'))
使用random.shuffle()方法对路径进行乱序(由于后续也会对数据进行乱序和数据加强处理,这一步无关紧要),并记录图片总数。
1 import random 2 random.shuffle(all_image_path) 3 image_count = len(all_image_path)
获取所有的图片后,咱们要对全部的图片打上标签,以便区分图片是cat仍是dog,用于后续对神经网络的训练。
首先,获取标签的名称,也就是存放图片的文件夹的名字(cats/dogs)。
1 label_name = sorted([item.name for item in data_root.glob('training_set/*')])
获取的标签名以下:
而后咱们创建字典,将标签名映射为0,1。
1 name_to_indx = dict((name, indx) for indx, name in enumerate(label_name))
经过获取的图片路径将全部的图片打上标签。
1 all_image_path = [str(path) for path in all_image_path] 2 all_image_label = [name_to_indx[pathlib.Path(p).parent.name] for p in all_image_path]
因为数据加强只须要对train数据进行加强,因此咱们定义两个函数分别对train和test数据进行处理。
对读入的图片进行解码,并将尺寸归一化。
对训练集数据进行随机上下左右翻转与裁剪,加强数据。
1 def load_preprosess_image(path, label): 2 img = tf.io.read_file(path) 3 img = tf.image.decode_jpeg(img, channels = 3) 4 img = tf.image.resize(img, [320, 320]) 5 #resize_with_crop_or_pad填充与裁剪 6 7 #数据加强 8 img = tf.image.random_crop(img, [256, 256, 3]) #随机裁剪 9 img = tf.image.random_flip_left_right(img) #随机左右翻转 10 img = tf.image.random_flip_up_down(img) #随机上下翻转 11 #img = tf.image.random_brightness(img, 0.5) #随机调整亮度 12 #img = tf.image.random_contrast(img, 0, 1) #随机调整对比度 13 14 img = tf.cast(img, tf.float32) 15 img = img / 255 16 label = tf.reshape(label, [1]) 17 return img, label 18 #加载和预处理图片 19 20 def load_preprosess_image_test(path, label): 21 img = tf.io.read_file(path) 22 img = tf.image.decode_jpeg(img, channels = 3) 23 img = tf.image.resize(img, [256, 256]) 24 #resize_with_crop_or_pad填充与裁剪 25 26 img = tf.cast(img, tf.float32) 27 img = img / 255 28 label = tf.reshape(label, [1]) 29 return img, label 30 #加载和预处理图片
经过tf.data.Dataset.from_tensor_slices()方法创建数据集。
1 dataset = tf.data.Dataset.from_tensor_slices((all_image_path, all_image_label))
将数据集分割为训练集与测试集,并分别使用预处理函数对图片进行处理。
1 test_count = int(image_count * 0.2) 2 train_count = image_count - test_count 3 train_dataset = dataset.skip(test_count) 4 test_dataset = dataset.take(test_count) 5 6 train_dataset = train_dataset.map(load_preprosess_image) 7 test_dataset = test_dataset.map(load_preprosess_image_test)
对训练集和测试集划分BATCH_SIZE。
1 BATCH_SIZE = 16 2 train_dataset = train_dataset.shuffle(train_count).batch(BATCH_SIZE) 3 test_dataset = test_dataset.batch(BATCH_SIZE)
注:此时的train_dataset与test_dataset都是可迭代对象,咱们可使用迭代器查看数据。
1 img, label = next(iter(train_dataset)) 2 plt.imshow(img[0]) #因为这个东西运行的很慢,这里就不展现运行结果了,这两行代码一样无关紧要。
咱们仿照VGG_16制做一个网络模型(不彻底相同)。
1 model = keras.Sequential() 2 model.add(keras.layers.Conv2D(64, (3, 3), input_shape = (256, 256, 3),padding = 'same', activation = 'relu')) 3 model.add(keras.layers.BatchNormalization()) 4 model.add(keras.layers.Conv2D(64, (3, 3), activation = 'relu')) 5 model.add(keras.layers.BatchNormalization()) 6 model.add(keras.layers.MaxPooling2D()) 7 model.add(keras.layers.Conv2D(128, (3, 3), activation = 'relu')) 8 model.add(keras.layers.BatchNormalization()) 9 model.add(keras.layers.Conv2D(128, (3, 3), activation = 'relu')) 10 model.add(keras.layers.BatchNormalization()) 11 model.add(keras.layers.MaxPooling2D()) 12 model.add(keras.layers.Conv2D(256, (3, 3), activation = 'relu')) 13 model.add(keras.layers.BatchNormalization()) 14 model.add(keras.layers.Conv2D(256, (3, 3), activation = 'relu')) 15 model.add(keras.layers.BatchNormalization()) 16 model.add(keras.layers.Conv2D(256, (3, 3), activation = 'relu')) 17 model.add(keras.layers.BatchNormalization()) 18 model.add(keras.layers.MaxPooling2D()) 19 model.add(keras.layers.Conv2D(512, (3, 3), activation = 'relu')) 20 model.add(keras.layers.BatchNormalization()) 21 model.add(keras.layers.Conv2D(512, (3, 3), activation = 'relu')) 22 model.add(keras.layers.BatchNormalization()) 23 model.add(keras.layers.Conv2D(512, (3, 3), activation = 'relu')) 24 model.add(keras.layers.BatchNormalization()) 25 model.add(keras.layers.MaxPooling2D()) 26 model.add(keras.layers.Conv2D(512, (3, 3), activation = 'relu')) 27 model.add(keras.layers.BatchNormalization()) 28 model.add(keras.layers.Conv2D(512, (3, 3), activation = 'relu')) 29 model.add(keras.layers.BatchNormalization()) 30 model.add(keras.layers.Conv2D(512, (3, 3), activation = 'relu')) 31 model.add(keras.layers.BatchNormalization()) 32 model.add(keras.layers.MaxPooling2D()) 33 model.add(keras.layers.GlobalAveragePooling2D()) 34 model.add(keras.layers.Dense(1024, activation = 'relu')) 35 model.add(keras.layers.BatchNormalization()) 36 model.add(keras.layers.Dense(256, activation = 'relu')) 37 model.add(keras.layers.BatchNormalization()) 38 model.add(keras.layers.Dense(1))
注意!最后一层Dense层没有使用sigmoid函数进行激活。
咱们选用Adam做为优化器,并定义模型的正确率与平均损失的计算方式。
1 optimizer = tf.keras.optimizers.Adam(learning_rate = 0.001) 2 epoch_loss_avg = tf.keras.metrics.Mean('train_loss', dtype = tf.float32)#参数为name 3 train_accuracy = tf.keras.metrics.Accuracy() 4 5 epoch_loss_avg_test = tf.keras.metrics.Mean('train_loss', dtype = tf.float32)#参数为name 6 test_accuracy = tf.keras.metrics.Accuracy()
定义训练集与测试集的的训练函数。
1 def train_step(model, images, labels): 2 with tf.GradientTape() as GT: #记录梯度 3 pred = model(images, training = True) 4 loss_step = tf.keras.losses.BinaryCrossentropy(from_logits = True)(labels, pred) 5 #from_logits 模型中输出结果是否进行了激活,未激活则为True 6 grads = GT.gradient(loss_step, model.trainable_variables) 7 #计算梯度 8 optimizer.apply_gradients(zip(grads, model.trainable_variables)) 9 #利用梯度对模型参数进行优化 10 epoch_loss_avg(loss_step)#计算loss 11 train_accuracy(labels, tf.cast(pred > 0, tf.int32))#计算acc 12 13 def test_step(model, images, labels): 14 pred = model(images, training = False)#pred = model.predict(images)# 15 loss_step = loss_step = tf.keras.losses.BinaryCrossentropy(from_logits = True)(labels, pred) 16 17 epoch_loss_avg_test(loss_step)#计算loss 18 test_accuracy(labels, tf.cast(pred > 0, tf.int32))#计算acc
使用with tf.GradientTape()记录训练过程当中的loss值。
使用model()对训练集进行预测,在经过损失函数BinaryCrossentropy()计算loss值。
使用.gradient()方法来计算梯度,参数为,loss值与模型的可训练参数。
计算好梯度以后,使用optimizer对模型的可训练参数进行优化。
最后计算模型的平均loss与正确率。
对测试集仅进行预测计算loss与正确率便可,无需对模型参数进行更新。
首先,定义几个列表记录loss与acc,用来绘制训练过程的图像。
1 train_loss_result = [] 2 train_acc_result = [] 3 4 test_loss_result = [] 5 test_acc_result = []
定义要训练的epochs,这里咱们对模型训练130个epoch。
1 num_epochs = 130
定义训练函数:
1 for epoch in range(num_epochs): 2 indx = 1 3 for images, labels in train_dataset: 4 train_step(model, images, labels) 5 indx += 1 6 if(indx % 5 == 0): 7 print('.', end = '') 8 print() 9 #训练过程 10 train_loss_result.append(epoch_loss_avg.result()) 11 train_acc_result.append(train_accuracy.result()) 12 #记录loss与acc 13 14 for images, labels in test_dataset: 15 test_step(model, images, labels) 16 17 test_loss_result.append(epoch_loss_avg_test.result()) 18 test_acc_result.append(test_accuracy.result()) 19 20 print('Epoch:{}: loss:{:.3f}, acc:{:.3f}, val_loss:{:.3f}, val_acc:{:.3f}'.format( 21 epoch + 1, epoch_loss_avg.result(), train_accuracy.result(), 22 epoch_loss_avg_test.result(), test_accuracy.result() 23 )) 24 25 epoch_loss_avg.reset_states() 26 train_accuracy.reset_states() 27 #清空,统计下一个epoch的均值 28 29 epoch_loss_avg_test.reset_states() 30 test_accuracy.reset_states()
在每一个epoch中,对train_dataset进行迭代,每次迭代处理的数据数量为一个BATCH_SIZE(),对每一个BATCH_SIZE使用定义好的训练函数对模型进行训练,输出训练过程并使用以前定义好的列表记录训练过程。
一个epoch训练完后,对loss均值与正确率计算函数进行清空处理,为下一个epoch的训练作好准备。
点击kaggle右上角的"Save version"保存并将模型提交进行训练。
选择提交(Commit),并点击"Advanced Settings"。
选择使用GPU进行训练,不然会训练的很是缓慢。
最后点击Save便可。
因为使用的网络模型较深,且参数较多,因此训练的速度很慢,大概训练了3个半小时获得以下训练结果:
模型在训练集上达到了92.8%的正确率,在测试集上达到了91.1%的正确率。
本次更新的较为匆忙,不少API的用法没有很详细的进行介绍,后面会再次更新进行补充。