需要训练的有两个模型,一个是文本识别模型,一个是图像识别模型。在训练的时候,尝试了ResNet50、ResNet101、MobileNetV2,三种模型,前两个残差神经网络模型的参数比较大,训练比较耗时,精度上也逊色于第三个模型。尝试了RTX 2080、RTX 2070、Tesla K80三种GPU,三者的训练速度依次递减,但差距不是很大。训练平台在篇尾再进行介绍。
两个模型的训练基本上可以共用一套代码,只有一些细微的差异。
首先介绍共用的代码部分,其中加载数据的函数,数据的根目录根据自己的情况,需要自行修改。
#导入必要的包
from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)
import tensorflow as tf
from tensorflow import keras
#from tensorflow.keras.applications import ResNet101
from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2
from keras.callbacks import EarlyStopping
import numpy as np
import os
import shutil
import collections
import math
import random
import pathlib
#加载数据
def dataloader():
data_root = '/mnt/Train_Valid_text' #根目录自行更改
train_data_root = pathlib.Path(data_root + "/Train")
valid_data_root = pathlib.Path(data_root + "/Valid")
#train_valid_data_root = pathlib.Path(data_root + "/Train_valid")
# test_data_root = pathlib.Path(data_root+"/test")
label_names = sorted(item.name for item in train_data_root.glob('*/') if item.is_dir())
print(label_names)
label_to_index = dict((name, index) for index, name in enumerate(label_names))
print(label_to_index)
train_all_image_paths = [str(path) for path in list(train_data_root.glob('*/*'))]
valid_all_image_paths = [str(path) for path in list(valid_data_root.glob('*/*'))]
#train_valid_all_image_paths = [str(path) for path in list(train_valid_data_root.glob('*/*'))]
# test_all_image_paths = [str(path) for path in list(test_data_root.glob('*/*'))]
train_all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in train_all_image_paths]
valid_all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in valid_all_image_paths]
#train_valid_all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in
#train_valid_all_image_paths]
# test_all_image_labels = [-1 for i in range(len(test_all_image_paths))]
print("First 10 images indices: ", train_all_image_labels[:10])
print("First 10 labels indices: ", train_all_image_labels[:10])
batch_size = 64 #批量大小
train_ds = tf.data.Dataset.from_tensor_slices((train_all_image_paths, train_all_image_labels)).map(
transform_train).shuffle(len(train_all_image_paths)).batch(batch_size)
valid_ds = tf.data.Dataset.from_tensor_slices((valid_all_image_paths, valid_all_image_labels)).map(
transform_train).shuffle(len(valid_all_image_paths)).batch(batch_size)
#train_valid_ds = tf.data.Dataset.from_tensor_slices(
#(train_valid_all_image_paths, train_valid_all_image_labels)).map(transform_train).shuffle(
#len(train_valid_all_image_paths)).batch(batch_size)
print(train_ds)
return train_ds, valid_ds, label_names
#模型创建函数
def creat_model(label_names):
net = MobileNetV2(
input_shape=(64, 64, 3), #输入图片大小为64x64
weights=None, #不需要它自带的模型权重
include_top=False)
model = tf.keras.Sequential([
net,
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dense(len(label_names), activation='softmax', dtype=tf.float32)
])
model.summary()
return model
接下来是略微有些差异的地方,主要是数据增强和callback部分,代码如下。下面的transform_train函数中,包含了一些常用的数据增强操作,根据训练的数据集情况,选择需要的部分,并不是都需要用到。
#数据增强操作
def transform_train(imgpath,label):
feature=tf.io.read_file(imgpath)
feature = tf.image.decode_jpeg(feature,channels=3)
feature = tf.image.resize(feature, size=[64, 64])
#下面两行代码只在训练图像识别模型时需要用
#训练文本识别模型时,把这两行代码注释掉,因为没有颠倒的文字
feature = tf.image.random_flip_left_right(feature) #左右翻转
feature = tf.image.random_flip_up_down(feature) #上下翻转
# 标准化
feature = tf.divide(feature, 255.)
# 正则化
mean = tf.convert_to_tensor([0.485, 0.456, 0.406])
std = tf.convert_to_tensor([0.229, 0.224, 0.225])
feature = tf.divide(tf.subtract(feature, mean), std)
feature = tf.image.per_image_standardization(feature) #仅在文本训练时进行标准化处理,图像训练时将本行代码注释掉
#print(feature,label)
return tf.image.convert_image_dtype(feature, tf.float32),label
#学习率控制函数,超过一定的训练迭代轮次后,开始逐渐减小学习率,使模型稳定
#两种模型的训练样本量差异比较大,因此所需要训练的轮次数量也不太一样,
#那么学习率开始减小的门槛也有不同
#训练文本识别模型时,总的迭代次数建议不超过70轮,门槛设置在30轮左右比较合适
#训练图形识别模型时,总的迭代次数建议不超过110轮,基本上100轮即可,门槛可设置在80轮
def scheduler(epoch):
if epoch < 30:
return lr
else:
return lr * math.exp(lr_decay * (30 - epoch))
下面就是调用以上函数,开始训练的代码,损失函数为交叉熵损失函数。
if __name__ == '__main__':
lr = 0.1 #学习率
lr_decay = 0.01 #学习率衰减系数
train_ds, valid_ds, label_names = dataloader()
model = creat_model(label_names)
model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
filepath='/mnt/text_model.h5', #模型存储路径以及名字
save_weights_only=False,
monitor='val_accuracy', #监控指标为验证集的准确率
mode='max',
save_best_only=True) #这个回调函数用以自动保存验证准确率最高的模型
callback = [tf.keras.callbacks.LearningRateScheduler(scheduler), model_checkpoint_callback]
model.compile(optimizer=keras.optimizers.SGD(learning_rate=lr, momentum=0.9),
loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(train_ds, epochs=85, validation_data=valid_ds, callbacks=callback)
文本识别模型的训练很快,总体样本量接近9000,利用RTX2070 GPU,7、8分钟左右就可以训练完,Tesla K80 GPU大概需要20分钟;图像识别模型的训练,我训练的数据样本大概在4万张左右,大概需要七八个小时才能训练完。
训练完之后,可以将训练损失以及准确率可视化一下看看,训练损失可视化代码如下。
import matplotlib.pyplot as plt
plt.plot(history.history['loss'], label='Train loss')
plt.plot(history.history['val_loss'], label='Valid loss')
plt.xlabel('epochs')
plt.ylabel('Loss')
plt.legend(loc='upper right')
plt.grid()
#plt.savefig('/mnt/文本训练损失.jpg')
plt.show()
准确率可视化代码如下
import matplotlib.pyplot as plt
plt.plot(history.history['accuracy'], label='Train accuracy')
plt.plot(history.history['val_accuracy'], label='Valid accuracy')
plt.xlabel('epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.grid()
#plt.savefig('/mnt/文本训练准确率.jpg')
plt.show()
文本识别模型的训练损失和准确率图像如下。文本模型最终的验证准确率可以稳定在99.8%,无限逼近100%。
图像识别模型的训练损失和准确率图像如下。图像模型最终的验证准确率可以稳定在96.5%左右。
以上就是所有的训练以及可视化代码。
调参:然后再调一调学习率、批量大小等超参数,可以把图像训练的验证准确率提高并稳定在98%。见下图。