tensorflow2.0像分类,tensorflow2像分类
终极管理员 知识笔记 127阅读
接下来我们将从零开始讲解一个基于TensorFlow的图像多标签分类实例这里以图片验证码为例进行讲解。
在我们访问某个网站的时候经常会遇到图片验证码。图片验证码的主要目的是区分爬虫程序和人类并将爬虫程序阻挡在外。

下面的程序就是模拟人类识别验证码从而使网站无法区分是爬虫程序还是人类在网站登录。
10.4.1 使用TFRecord生成训练数据

以图10.5所示的图片验证码为例将这幅验证码图片标记为label[3,8,8,7]。我们知道分类网络一般一次只能识别出一个目标那么如何识别这个多标签的序列数据呢?
通过下面的TFRecord结构可以构建多标签训练数据集从而实现多标签数据识别。
图10.5 图片验证码
以下为构造TFRecord多标签训练数据集的代码
import tensorflow as tf# 定义对整型特征的处理 def _int64_feature(value): return tf.train.Feature(int64_listtf.train.Int64List(value[value]))# 定义对字节特征的处理def _bytes_feature(value): return tf.train.Feature(bytes_listtf.train.BytesList(value[value]))# 定义对浮点型特征的处理def _floats_feature(value): return tf.train_Feature(float_listtf.train.floatList(value[value]))# 对数据进行转换 def convert_to_record(name, image, label, map): filename os.path.join(params.TRAINING_RECORDS_DATA_DIR, name . params.DATA_EXT) writer tf.python_io.TFRecordWriter(filename) image_raw image.tostring() map_raw map.tostring() label_raw label.tostring() example tf.train.Example(featuretf.train.Feature(feature{ image_raw: _bytes_feature(image_raw), map_raw: _bytes_feature(map_raw), 1abel_raw: _bytes_feature(label_raw) })) writer.write(example.SerializeToString()) writer.close()
通过上面的代码我们构建了一条支持多标签的TFRecord记录多幅验证码图片可以构建一个验证码的多标签数据集用于后续的多标签分类训练。
10.4.2 构建多标签分类网络
通过前一步操作我们得到了用于多标签分类的验证码数据集现在需要构建多标签分类网络。
我们选择VGG网络作为特征提取网络骨架。通常越复杂的网络对噪声的鲁棒性就越强。验证码中的噪声主要来自形变、粘连以及人工添加VGG网络对这些噪声具有好的鲁棒性代码如下
import tensorflow as tftf.enable_eager_execution ()def model_vgg(x, training False):# 第一组第一个卷积使用64个卷积核核大小为3conv1_1 tf.layers.conv2d(inputsx, filters64,nameconv1_1, kernel_size3, activationtf.nn.relu, paddingsame)# 第一组第二个卷积使用64个卷积核核大小为3convl_2 tf.layers.conv2d(inputsconv1_1,filters64, nameconv1_2, kernel_size3, activationtf.nn.relu,paddingsame)# 第一个pool操作核大小为2步长为2pooll tf.layers.max_pooling2d(inputsconv1_2, pool_size[2, 2], strides2, name pool1)# 第二组第一个卷积使用128个卷积核核大小为3conv2_1 tf.layers.conv2d(inputspool1, filters128, nameconv2_1, kernel_size3, activationtf.nn.relu, paddingsame)# 第二组第二个卷积使用64个卷积核核大小为3conv2_2 tf.layers.conv2d(inputsconv2_1, filters128,nameconv2_2, kernel_size3, activationtf.nn.relu, paddingsame)# 第二个pool操作核大小为2步长为2pool2 tf.layers.max_pooling2d(inputsconv2_2, pool_size[2 2], strides2, namepool1)# 第三组第一个卷积使用128个卷积核核大小为3conv3_1 tf.layers.conv2d(inputspool2, filters128, nameconv3_1, kernel_size3, activationtf.nn.relu, paddingsame)# 第三组第二个卷积使用128个卷积核核大小为3conv3_2 tf.layers.conv2d(inputsconv3_1, filters128, nameconv3_2, kernel_size3, activationtf.nn.relu, paddingsame)# 第三组第三个卷积使用128个卷积核核大小为3conv3_3 tf.layers.conv2d(inputsconv3_2, filters128, nameconv3_3, kernel_size3, activationtf.nn.relu, padding same)# 第三个pool 操作核大小为2步长为2pool3 tf.layers.max_pooling2d(inputsconv3_3, pool_size[2, 2], strides2,namepool3)# 第四组第一个卷积使用256个卷积核核大小为3conv4_1 tf.layers.conv2d(inputs-pool3, filters256, nameconv4_1, kernel_size3, activationtf.nn.relu, paddingsame)# 第四组第二个卷积使用128个卷积核核大小为3conv4_2 tf.layers.conv2d(inputsconv4_1, filters128, nameconv4_2, kernel_size3, activationtf.nn.relu, paddingsame)# 第四组第三个卷积使用128个卷积核核大小为3conv4_3 tf.layers.conv2d(inputsconv4_2, filters128, namecov4_3, kernel_size3, activationtf.nn.relu, paddingsame )# 第四个pool操作核大小为2步长为2pool4 tf.layers.max.pooling2d(inputsconv4_3, pool_size[2,2], strides2, namepool4)# 第五组第一个卷积使用512个卷积核核大小为3conv5_1 tf.layers.conv2d(inputspool4, filters512, nameconv5_1, kernel_size3, activationtf.nn.relu, padding same)# 第五组第二个卷积使用512个卷积核核大小为3conv5_2 t.layers.conv2d(inputsconv5_1, filters512, nameconv5_2, kernel_size3, activationtf.nn.relu, paddingsame)# 第五组第三个卷积使用512个卷积核核大小为3conv5_3 tf.layers.conv2d(inputs-conv5_2, filters512, nameconv5_3, kernel_size3, activationtf.nn.relu, paddingsame )# 第五个pool操作核大小为2步长为2pool5 tf.layers.max_pooling2d(inputsconv5_3, pool_size[2, 2], strides2, namepool5)flatten tf.layers.flatten(inputspoo15, nameflatten)
上面是VGG网络的单标签分类TensorFlow代码但这里我们需要实现的是多标签分类因此需要对VGG网络进行相应的改进代码如下
# 构建输出为4096的全连接层fc6 tf.layers.dense(inputsflatten, units4096,activationtf.nn.relu, namefc6)# 为了防止过拟合引入dropout操作drop1 tf.layers.dropout(inputsfc6,rate0.5, trainingtraining)# 构建输出为4096的全连接层fc7 tf.layers.dense(inputsdrop1, units4096,activationtf.nn.relu, namefc7)# 为了防止过报合引入dropout操作drop2 tf.layers.dropout(inputsfc7, rate0.5, trainingtraining)# 为第一个标签构建分类器fc8_1 tf.layers.dense(inputsdrop2, units10,activationtf.nn.sigmoid, namefc8_1)# 为第二个标签构建分类器fc8_2 tf.layers.dense(inputsdrop2, units10,activationtf.nn.sigmoid, namefc8_2)# 为第三个标签构建分类器fc8_3 tf.layers.dense(inputsdrop2, units10,activationtf.nn.sigmoid, namefc8_3)# 为第四个标签构建分类器fc8_4 tf.layers.dense(inputsdrop2,units10,activationtf.nn.sigmoid, namefc8_4)# 将四个标签的结果进行拼接操作fc8 tf.concat([fc8_1,fc8_2,fc8_3,fc8_4], 0)
这里的fc6和fc7全连接层是对网络的卷积特征进行进一步的处理在经过fc7层后我们需要生成多标签的预测结果。由于一幅验证码图片中存在4个标签因此需要构建4个子分类网络。这里假设图片验证码中只包含10 个数字因此每个网络输出的预测类别就是10类最后生成4个预测类别为10的子网络。如果每次训练时传入64幅验证码图片进行预测那么通过4个子网络后分别生成(64,10)、(64,10)、(64,10)、(64,10) 4个张量。如果使用Softmax分类器的话就需要想办法将这4个张量进行组合于是使用tf.concat函数进行张量拼接操作。
以下是TensorFlow中tf.concat函数的传参示例
tf.concat (values,axis,nameconcat)
通过fc8tf.concat([fc8_1,fc8_2,fc8_3,fc8_4], 0)的操作可以将前面的4个(64.10)张量变换成(256.10)这样的单个张量生成单个张量后就能进行后面的Softmax分类操作了。
10.4.3 多标签训练模型
模型训练的第一个步骤就是读取数据读取方式有两种一种是直接读取图片进行操作另一种是转换为二进制文件格式后再进行操作。前者实现起来简单但速度较慢后者实现起来复杂但读取速度快。这里我们以后者二进制的文件格式介绍如何实现多标签数据的读取操作下面是相关代码。
首先读取TFRecord文件内容
tfr TFrecorder()def input_fn_maker(path, data_info_path, shuffleFalse, batch_size 1,epoch 1, padding None) : def input_fn(): filenames tfr.get_filenames(pathpath, shuffleshuffle) datasettfr.get_dataset(pathsfilenames, data_infodata_info_path, shuffle shuffle, batch_size batch_size, epoch epoch, padding padding) iterator dataset.make_one_shot_iterator () return iterator.get_next()return input_fn# 原始图片信息padding_info ({image:[30, 100,3,], label:[]})# 测试集test_input_fn input_fn_maker(captcha_data/test/,captcha_tfrecord/data_info.csv,batch_size 512, padding padding_info)# 训练集train_input_fn input_fn_maker(captcha_data/train/,captcha_tfrecord/data_info.csv,shuffleTrue, batch_size 128,padding padding_info)# 验证集train_eval_fn input_fn_maker(captcha_data/train/,captcha_tfrecord/data_info.csv,batch_size 512,adding padding_info)
然后是模型训练部分
def model_fn(features, net, mode):features[image] tf.reshape(features[image], [-1, 30, 100, 3])# 获取基于net网络的模型预测结果predictions net(features[image])# 判断是预测模式还是训练模式if mode tf.estimator.ModeKeys.PREDICT: return tf.estimator.EstimatorSpec(modemode, predictionspredictions)# 因为是多标签的Softmax所以需要提前对标签的维度进行处理lables tf.reshape(features[label], features[label].shape[0]*4,))# 初始化softmaxlossloss tf.losses.sparse_softmax_cross_entropy(labelslabels, logitslogits)# 训练模式下的模型结果获取if mode tf.estimator.ModeKeys.TRAIN: # 声明模型使用的优化器类型 optimizer tf.train.AdamOptimizer(learning_rate1e-3) train_op optimizer.minimize( lossloss,global_steptf.train.get_global_step()) return tf.estimator.EstimatorSpec(modemode, lossloss, train_optrain_op)# 生成评价指标eval_metric_ops {accuracy: tf.metrics.accuracy( labelsfeatures[label],predictionspredictions[classes]) }return tf.estimator.EstimatorSpec(modemode, lossloss, eval_metric_ops eval_metric_ops)
多标签的模型训练流程与普通单标签的模型训练流程非常相似唯一的区别就是需要将多标签的标签值拼接成一个张量以满足Softmax分类操作的维度要求。
本文节选自《Python深度学习原理、算法与案例》。