目录
基础理论
一、生成验证码数据集
1、生成验证码训练集
1-0、判断文件夹是否为空
1-1、创建字符集(数字、大小写英文字母)
1-2、随机生成验证码(1000个,长度为4)
2、生成验证码测试集
代码
二、获取数据(训练集、测试集)
1、获取数据和标签
1-1、获取训练集数据和标签(路径和标签)
1-2、获取测试集数据和标签(路径和标签)
1-3、数据组合(图像路径和标签)
2、打乱数据
3、处理每条数据
4、自定义重复周期和批次大小
5、处理每批数据
6、获取一批次数据和标签
三、创建神经网络
1、创建50层残差神经网络
2、设置输入层
3、平均池化(压缩数据)
4、配置输出层
5、配置模型
6、编译(多任务学习)
7、回调函数配置
8、训练模型
总代码
多任务学习(Multi-task Learning)是深度学习中很常用的一种模型训练策略,意思其实也很简单,就是同时训练多个任务,给大家举两个例子大家就明白了。比如目标检测项目中,我们既要知道1、目标所在的位置(也就是预测框坐标值),也要知道2、预测框内是什么物体。预测框的坐标值是连续型数据,所以是一个回归任务;预测框的物体是一个具体的类别,所以是一个分类任务。
不同的任务其实也可以共享卷积层。因为卷积层的作用主要是特征提取,先提取图像的特征,然后再使用这些特征来预测人的年龄,表情,性别。 用于特征提取的卷积层可以共享 ,不过 不同的任务还需要有自己的 task layer,专门用于训练特定任务 。
if not os.listdir('D:\\Study\\AI\OpenCV\\draft.py\\captcha\\train'): Create_train_data() # 生成验证码训练集
# 1、创建字符集(字符包含所有数字和所有大小写英文字母,一共62(10+26+26)个) characters = string.digits + string.ascii_letters # 数字 英文字母(大小写)
得到如下数据:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
# 2、随机产生验证码(共1000个,每个长度为4) for i in range(1000): verification_list = []
1、生成随机字符:
# 2-1、开始生产随机字符(4位) for j in range(4): c = random.choice(characters) # 随机选择(从characters里面随机抽取) verification_list.append(c)
2、实例化验证码生成器:
# 2-2、实例化验证码生成器 image = ImageCaptcha(width=160, height=60) # 宽:160,高:60
3、连接列表字符(转字符串)
# 2-3、连接列表字符 verification_list = ''.join(verification_list)
4、生成验证码
# 2-4、生成验证码 image.write(verification_list, 'captcha/train/' + verification_list + '.jpg')
过程同上,测试集数据不需要那么多,把1000张换成200张即可。
# 创建训练集(验证码) def Create_train_data(): # 1、创建字符集(字符包含所有数字和所有大小写英文字母,一共62(10+26+26)个) characters = string.digits + string.ascii_letters # 数字 英文字母(大小写) # 2、随机产生验证码(共1000个,每个长度为4) for i in range(1000): verification_list = [] # 2-1、开始生产随机字符(4位) for j in range(4): c = random.choice(characters) # 随机选择(从characters里面随机抽取) verification_list.append(c) # 2-2、实例化验证码生成器 image = ImageCaptcha(width=160, height=60) # 宽:160,高:60 # 2-3、连接列表字符 verification_list = ''.join(verification_list) # 2-4、生成验证码 image.write(verification_list, 'captcha/train/' + verification_list + '.jpg') # 创建测试集(验证码) def Create_test_data(): # 1、创建字符集(字符包含所有数字和所有大小写英文字母,一共62(10+26+26)个) characters = string.digits + string.ascii_letters # 数字 英文字母(大小写) # 2、随机产生验证码(共200个,每个长度为4) for i in range(200): verification_list = [] # 2-1、开始生产随机字符(4位) for j in range(4): c = random.choice(characters) # 随机选择(从characters里面随机抽取) verification_list.append(c) # 2-2、实例化验证码生成器 image = ImageCaptcha(width=160, height=60) # 宽:160,高:60 # 2-3、连接列表字符 verification_list = ''.join(verification_list) # 2-4、生成验证码 image.write(verification_list, 'captcha/test/' + verification_list + '.jpg')
# 1-1、获取训练集数据和标签(路径和标签) train_data, train_target = get_filenames_and_classes("./captcha/train/")# 1000张图片,长度4
获取所有图片路径,标签转独热编码 :
# 获取所有验证码图片路径和标签 def get_filenames_and_classes(dataset_dir): # 图片路径和标签 paths ,targets = [], [] # 获取每个图片的路径和标签 for filename in os.listdir(dataset_dir): # 1、获取文件路径 path = os.path.join(dataset_dir, filename) # 路径 文件名 # 完成1:保存图片路径 paths.append(path) # 2、获取验证码标签(取文件名的前 4 位,也就是验证码的标签) target = filename[0:4] # 定义一个空label(获取4*62的数组,用0填充) label = np.zeros((4, classes_num), dtype=np.uint8) # 3、标签转独热编码 for i, ch in enumerate(target): # i:索引 ch:字符 # 标记(设置标签):独热编码 one-hot 格式 label[i, characters.find(ch)] = 1 # 数组索引 字符下标(字符ch在characters中的下标) # 完成2:保存独热编码的标签 targets.append(label) # 返回图片路径和标签 return np.array(paths), np.array(targets)
# 1-2、获取测试集数据和标签(路径和标签) test_data, test_target = get_filenames_and_classes("./captcha/test/") # 200张图片,长度4
获取数据函数同上。
# 1-3、数据组合(图像路径和标签) (创建 dataset 对象,传入图片路径和标签) dataset_train = tf.data.Dataset.from_tensor_slices((train_data, train_target)) dataset_test = tf.data.Dataset.from_tensor_slices((test_data, test_target))
# 2、打乱数据 dataset_train = dataset_train.shuffle(buffer_size=100, reshuffle_each_iteration=True) # map-可以自定义一个函数来处理每一条数据 dataset_test = dataset_test.shuffle(buffer_size=20, reshuffle_each_iteration=True) # 数据缓冲器大小 随机打乱(是/否)
# 3、对每条数据进行处理(图像地址->3通道图像->归一化) dataset_train = dataset_train.map(image_function) dataset_test = dataset_test.map(image_function) # map函数:可以自定义一个函数来处理每一条数据
# 4、自定义重复周期和批次大小 dataset_train = dataset_train.repeat(1) # 数据重复生成 1 个周期 dataset_test = dataset_test.repeat(1) # 数据重复生成 1 个周期 dataset_train = dataset_train.batch(64) # 定义批次大小64 dataset_test = dataset_test.batch(64) # 定义批次大小64
# 5、处理每批数据 # 注意这个 map 和前面的 map 有所不同,第一个 map 在 batch 之前,所以是处理每一条数据 # 这个 map 在 batch 之后,所以是处理每一个 batch 的数据 dataset_train = dataset_train.map(label_function) dataset_test = dataset_test.map(label_function)
# 获取一批数据和标签 trainx, trainy = next(iter(dataset_train)) testx, testy = next(iter(dataset_test))
数据(归一化后的):
标签(独热编码):
# 三、构造神经网络 Create_Network()
# 1、构造resnet50神经网络(50层残差网络) resnet50 = ResNet50(weights='imagenet', include_top=False, input_shape=(height, width, 3)) # 设置输入 # weights:权重(imagenet:加载预训练权重) # include_top:是否保留顶层的全连接网络 # input_shape:指明输入图片的shape,仅当include_top=False有效
# 2、设置输入层 inputs = Input((height, width, 3)) # 设置输入层大小 x = resnet50(inputs) # 使用 resnet50 进行特征提取
# 3、平均池化(压缩数据) x = GlobalAvgPool2D()(x)
多任务学习,把验证码识别的4个字符看成是4个不同的任务,每个任务负责识别1个字符。
# 4、配置输出层(多任务学习) # 把验证码识别的4个字符看成是4个不同的任务,每个任务负责识别1个字符 x0 = Dense(classes_num, activation='softmax', name='out0')(x) x1 = Dense(classes_num, activation='softmax', name='out1')(x) x2 = Dense(classes_num, activation='softmax', name='out2')(x) x3 = Dense(classes_num, activation='softmax', name='out3')(x)
# 5、配置模型(输入层、输出层) model = Model(inputs, [x0, x1, x2, x3])
损失函数、权重、优化器、监视等等设置。
# 6、编译(多任务学习)(损失函数、权重、优化器、监视等等设置) # (4个任务我们可以定义4个loss) model.compile(loss={'out0': 'categorical_crossentropy', 'out1': 'categorical_crossentropy', 'out2': 'categorical_crossentropy', 'out3': 'categorical_crossentropy'}, loss_weights={'out0': 1, 'out1': 1, 'out2': 1, 'out3': 1}, optimizer=SGD(lr=0.01, momentum=0.9), metrics=['acc']) # loss:损失函数 loss_weights:权重 optimizer:优化器(lr:学习率;momentum:带动量的梯度下降) # metrics:监视(acc)
# 7、回调函数(停止训练、保存数据、保存模型、调整学习率) callbacks = [EarlyStopping(monitor='val_loss', patience=6, verbose=1), CSVLogger('Captcha_tfdata.csv'), ModelCheckpoint('Best_Captcha_tfdata.h5', monitor='val_loss', save_best_only=True), ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)] # monitor='val_loss':监控指标'val_loss' # EarlyStopping:让模型停止(6个周期,val_loss没有下降则训练结束) # CSVLogger: 保存训练数据 # ModelCheckpoint:保存模型(保存所有训练周期中val_loss最低的模型) # ReduceLROnPlateau 学习率调整,连续3个周期,val_loss没有下降,则当前学习率乘以0.1
# 8、训练模型 model.fit(x=dataset_train, epochs=epochs, validation_data=dataset_test, callbacks=callbacks)
训练效果:
暂时没有训练太多次,不过通过这几次的训练,已经可以看到准确率明显提高了许多。
# 验证码生成与识别 import os os.environ['TF_CPP_MIN_LOG_LEVEL']='2' import tensorflow as tf from tensorflow.keras.layers import Dense,GlobalAvgPool2D,Input from tensorflow.keras.optimizers import SGD from tensorflow.keras.models import Model from tensorflow.keras.applications.resnet50 import ResNet50 from tensorflow.keras.callbacks import EarlyStopping,CSVLogger,ModelCheckpoint,ReduceLROnPlateau import string import numpy as np import os import random from captcha.image import ImageCaptcha from plot_model import plot_model # 字符包含所有数字和所有小写英文字母,一共 62 个 characters = string.digits + string.ascii_letters # 数字 字母 # 类别数(62) classes_num = len(characters) # 周期数 epochs = 100 # 图片宽度 width = 160 # 图片高度 height = 60 # 创建训练集(验证码) def Create_train_data(): # 1、创建字符集(字符包含所有数字和所有大小写英文字母,一共62(10+26+26)个) characters = string.digits + string.ascii_letters # 数字 英文字母(大小写) # 2、随机产生验证码(共1000个,每个长度为4) for i in range(1000): verification_list = [] # 2-1、开始生产随机字符(4位) for j in range(4): c = random.choice(characters) # 随机选择(从characters里面随机抽取) verification_list.append(c) # 2-2、实例化验证码生成器 image = ImageCaptcha(width=160, height=60) # 宽:160,高:60 # 2-3、连接列表字符 verification_list = ''.join(verification_list) # 2-4、生成验证码 image.write(verification_list, 'captcha/train/' + verification_list + '.jpg') # 创建测试集(验证码) def Create_test_data(): # 1、创建字符集(字符包含所有数字和所有大小写英文字母,一共62(10+26+26)个) characters = string.digits + string.ascii_letters # 数字 英文字母(大小写) # 2、随机产生验证码(共200个,每个长度为4) for i in range(200): verification_list = [] # 2-1、开始生产随机字符(4位) for j in range(4): c = random.choice(characters) # 随机选择(从characters里面随机抽取) verification_list.append(c) # 2-2、实例化验证码生成器 image = ImageCaptcha(width=160, height=60) # 宽:160,高:60 # 2-3、连接列表字符 verification_list = ''.join(verification_list) # 2-4、生成验证码 image.write(verification_list, 'captcha/test/' + verification_list + '.jpg') # 获取所有验证码图片路径和标签 def get_filenames_and_classes(dataset_dir): # 图片路径和标签 paths ,targets = [], [] # 获取每个图片的路径和标签 for filename in os.listdir(dataset_dir): # 1、获取文件路径 path = os.path.join(dataset_dir, filename) # 路径 文件名 # 完成1:保存图片路径 paths.append(path) # 2、获取验证码标签(取文件名的前 4 位,也就是验证码的标签) target = filename[0:4] # 定义一个空label(获取4*62的数组,用0填充) label = np.zeros((4, classes_num), dtype=np.uint8) # 3、标签转独热编码 for i, ch in enumerate(target): # i:索引 ch:字符 # 标记(设置标签):独热编码 one-hot 格式 label[i, characters.find(ch)] = 1 # 数组索引 字符下标(字符ch在characters中的下标) # 完成2:保存独热编码的标签 targets.append(label) # 返回图片路径和标签 return np.array(paths), np.array(targets) # 图像处理函数 # 输入:图像路径、标签 # 输出:图像、标签 def image_function(filenames, label): # 1、根据图片路径读取图片内容 image = tf.io.read_file(filenames) # 2、解码为jpeg格式、3通道(正规图像) image = tf.image.decode_jpeg(image, channels=3) # 3、归一化 image = tf.cast(image, tf.float32) / 255.0 # 返回图片数据和标签 return image, label # 标签处理函数 # 获得每一个批次的图片数据和标签 def label_function(image, label): # transpose 改变数据的维度,比如原来的数据 shape 是(64,4,62) # 这里的 64 是批次大小,验证码长度为 4 有 4 个标签,62 是 62 个不同的字符 # tf.transpose(label,[1,0,2])计算后得到的 shape 为(4,64,62) # 原来的第 1 个维度变成了第 0 维度,原来的第 0 维度变成了 1 维度,第 2 维不变 # (64,4,62)->(4,64,62) label = tf.transpose(label, [1, 0, 2]) # 返回图片内容和标签,注意这里标签的返回,我们的模型会定义 4 个任务,所以这里返回 4 个标签 # 每个标签的 shape 为(64,62),64 是批次大小,62 是独热编码格式的标签 return image, (label[0], label[1], label[2], label[3]) # 获取数据(训练集、测试集) def GetData(): global dataset_train, dataset_test # 1、获取数据和标签 # 1-1、获取训练集数据和标签(路径和标签) train_data, train_target = get_filenames_and_classes("./captcha/train/")# 1000张图片,长度4 # 1-2、获取测试集数据和标签(路径和标签) test_data, test_target = get_filenames_and_classes("./captcha/test/") # 200张图片,长度4 # 1-3、数据组合(图像路径和标签) (创建 dataset 对象,传入图片路径和标签) dataset_train = tf.data.Dataset.from_tensor_slices((train_data, train_target)) dataset_test = tf.data.Dataset.from_tensor_slices((test_data, test_target)) # 2、打乱数据 dataset_train = dataset_train.shuffle(buffer_size=100, reshuffle_each_iteration=True) # map-可以自定义一个函数来处理每一条数据 dataset_test = dataset_test.shuffle(buffer_size=20, reshuffle_each_iteration=True) # 数据缓冲器大小 随机打乱(是/否) # 3、对每条数据进行处理(图像地址->3通道图像->归一化) dataset_train = dataset_train.map(image_function) dataset_test = dataset_test.map(image_function) # map函数:可以自定义一个函数来处理每一条数据 # 4、自定义重复周期和批次大小 dataset_train = dataset_train.repeat(1) # 数据重复生成 1 个周期 dataset_test = dataset_test.repeat(1) # 数据重复生成 1 个周期 dataset_train = dataset_train.batch(64) # 定义批次大小64 dataset_test = dataset_test.batch(64) # 定义批次大小64 # 5、处理每批数据 # 注意这个 map 和前面的 map 有所不同,第一个 map 在 batch 之前,所以是处理每一条数据 # 这个 map 在 batch 之后,所以是处理每一个 batch 的数据 dataset_train = dataset_train.map(label_function) dataset_test = dataset_test.map(label_function) # 获取一批次数据和标签 trainx, trainy = next(iter(dataset_train)) testx, testy = next(iter(dataset_test)) # print(trainx) # print(trainy) # print(testx) # print(testy) # 创建神经网络 def Create_Network(): # 1、构造resnet50神经网络(50层残差网络) resnet50 = ResNet50(weights='imagenet', include_top=False, input_shape=(height, width, 3)) # 设置输入 # weights:权重(imagenet:加载预训练权重) # include_top:是否保留顶层的全连接网络 # input_shape:指明输入图片的shape,仅当include_top=False有效 # 2、设置输入层 inputs = Input((height, width, 3)) # 设置输入层大小 x = resnet50(inputs) # 使用 resnet50 进行特征提取 # 3、平均池化(压缩数据) x = GlobalAvgPool2D()(x) # 4、配置输出层(多任务学习) # 把验证码识别的4个字符看成是4个不同的任务,每个任务负责识别1个字符 x0 = Dense(classes_num, activation='softmax', name='out0')(x) x1 = Dense(classes_num, activation='softmax', name='out1')(x) x2 = Dense(classes_num, activation='softmax', name='out2')(x) x3 = Dense(classes_num, activation='softmax', name='out3')(x) # 5、配置模型(输入层、输出层) model = Model(inputs, [x0, x1, x2, x3]) # 6、编译(多任务学习)(损失函数、权重、优化器、监视等等设置) # (4个任务我们可以定义4个loss) model.compile(loss={'out0': 'categorical_crossentropy', 'out1': 'categorical_crossentropy', 'out2': 'categorical_crossentropy', 'out3': 'categorical_crossentropy'}, loss_weights={'out0': 1, 'out1': 1, 'out2': 1, 'out3': 1}, optimizer=SGD(lr=0.01, momentum=0.9), metrics=['acc']) # loss:损失函数 loss_weights:权重 optimizer:优化器(lr:学习率;momentum:带动量的梯度下降) # metrics:监视(acc) # 7、回调函数(停止训练、保存数据、保存模型、调整学习率) callbacks = [EarlyStopping(monitor='val_loss', patience=6, verbose=1), CSVLogger('Captcha_tfdata.csv'), ModelCheckpoint('Best_Captcha_tfdata.h5', monitor='val_loss', save_best_only=True), ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)] # monitor='val_loss':监控指标'val_loss' # EarlyStopping:让模型停止(6个周期,val_loss没有下降则训练结束) # CSVLogger: 保存训练数据 # ModelCheckpoint:保存模型(保存所有训练周期中val_loss最低的模型) # ReduceLROnPlateau 学习率调整,连续3个周期,val_loss没有下降,则当前学习率乘以0.1 # 8、训练模型 model.fit(x=dataset_train, epochs=epochs, validation_data=dataset_test, callbacks=callbacks) if __name__ == '__main__': # 一、创建验证码数据集 # 判断训练集文件夹是否为空 if not os.listdir('D:\\Study\\AI\OpenCV\\draft.py\\captcha\\train'): Create_train_data() # 生成验证码训练集 # 判断测试集文件夹是否为空 if not os.listdir('D:\\Study\\AI\OpenCV\\draft.py\\captcha\\test'): Create_test_data() # 生成验证码测试集 # 二、获取数据 GetData() # 三、构造神经网络 Create_Network()