目录
基础理论
多任务学习(Multi-task Learning)是深度学习中很常用的一种模型训练策略,意思其实也很简单,就是同时训练多个任务,给大家举两个例子大家就明白了。比如目标检测项目中,我们既要知道1、目标所在的位置(也就是预测框坐标值),也要知道2、预测框内是什么物体。预测框的坐标值是连续型数据,所以是一个回归任务;预测框的物体是一个具体的类别,所以是一个分类任务。
不同的任务其实也可以共享卷积层。因为卷积层的作用主要是特征提取,先提取图像的特征,然后再使用这些特征来预测人的年龄,表情,性别。 用于特征提取的卷积层可以共享 ,不过 不同的任务还需要有自己的 task layer,专门用于训练特定任务 。
一、生成验证码数据集
1、生成验证码训练集
1-0、判断文件夹是否为空
if not os.listdir('D:\\Study\\AI\OpenCV\\draft.py\\captcha\\train'):
Create_train_data() # 生成验证码训练集
1-1、创建字符集(数字、大小写英文字母)
# 1、创建字符集(字符包含所有数字和所有大小写英文字母,一共62(10+26+26)个)
characters = string.digits + string.ascii_letters
# 数字 英文字母(大小写)
得到如下数据:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
1-2、随机生成验证码(1000个,长度为4)
# 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')
2、生成验证码测试集
过程同上,测试集数据不需要那么多,把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-1、获取训练集数据和标签(路径和标签)
# 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、获取测试集数据和标签(路径和标签)
# 1-2、获取测试集数据和标签(路径和标签)
test_data, test_target = get_filenames_and_classes("./captcha/test/") # 200张图片,长度4
获取数据函数同上。
1-3、数据组合(图像路径和标签)
# 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、打乱数据
# 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、对每条数据进行处理(图像地址->3通道图像->归一化)
dataset_train = dataset_train.map(image_function)
dataset_test = dataset_test.map(image_function)
# map函数:可以自定义一个函数来处理每一条数据
4、自定义重复周期和批次大小
# 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、处理每批数据
# 5、处理每批数据
# 注意这个 map 和前面的 map 有所不同,第一个 map 在 batch 之前,所以是处理每一条数据
# 这个 map 在 batch 之后,所以是处理每一个 batch 的数据
dataset_train = dataset_train.map(label_function)
dataset_test = dataset_test.map(label_function)
6、获取一批次数据和标签
# 获取一批数据和标签
trainx, trainy = next(iter(dataset_train))
testx, testy = next(iter(dataset_test))
数据(归一化后的):
标签(独热编码):
三、创建神经网络
# 三、构造神经网络
Create_Network()
1、创建50层残差神经网络
# 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、设置输入层
# 2、设置输入层
inputs = Input((height, width, 3)) # 设置输入层大小
x = resnet50(inputs) # 使用 resnet50 进行特征提取
3、平均池化(压缩数据)
# 3、平均池化(压缩数据)
x = GlobalAvgPool2D()(x)
4、配置输出层
多任务学习,把验证码识别的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、配置模型
# 5、配置模型(输入层、输出层)
model = Model(inputs, [x0, x1, x2, x3])
6、编译(多任务学习)
损失函数、权重、优化器、监视等等设置。
# 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、回调函数配置
# 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、训练模型
# 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()