"笨方法"学习CNN图像识别(三)—— ResNet网络训练及预测

原文地址:https://finthon.com/learn-cnn-three-resnet-prediction/
-- 全文阅读8分钟 --

在本文中,你将学习到以下内容:


  • TensorFlow中调用ResNet网络
  • 训练网络并保存模型
  • 加载模型预测结果

前言

在深度学习中,随着网络深度的增加,模型优化会变得越来越困难,甚至会发生梯度爆炸,导致整个网络训练无法收敛。ResNet(Residual Networks)的提出解决了这个问题。在这里我们直接调用ResNet网络进行训练,讲解ResNet细节的文章有很多,这里找了一篇供参考

搭建训练网络

如果你看过了前面的准备工作,图片预处理制作tfrecord格式,默认已经有tfrecord格式的数据文件了。我们接着搭建网络,来处理100类商标图片的分类问题。将制作好的tfrecord数据通过队列系统传入ResNet网络进行训练。

100类

首先导入必要的库:

import tensorflow as tf
import tensorflow.contrib.slim.nets as nets

nets库里面集成了现有的很多网络(AlexNet,Inception,ResNet,VGG)可以直接调用,我们在这里使用ResNet_50,即50层的网络训练。
接下来我们先定义一个读取tfrecord文件的函数:

def read_and_decode_tfrecord(filename):
    filename_deque = tf.train.string_input_producer(filename)
    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_deque)
    features = tf.parse_single_example(serialized_example, features={
        'label': tf.FixedLenFeature([], tf.int64),
        'img_raw': tf.FixedLenFeature([], tf.string)})
    label = tf.cast(features['label'], tf.int32)
    img = tf.decode_raw(features['img_raw'], tf.uint8)
    img = tf.reshape(img, [224, 224, 3])
    img = tf.cast(img, tf.float32) / 255.0      #将矩阵归一化0-1之间
    return img, label

定义模型保存地址,batch_sizes设置的小一点训练效果更好,将当前目录下的tfrecord文件放入列表中:

save_dir = r"./train_image_63.model"  # 模型保存路径
batch_size_ = 2
lr = tf.Variable(0.0001, dtype=tf.float32)  # 学习速率
x = tf.placeholder(tf.float32, [None, 224, 224, 3])  # 图片大小为224*224*3
y_ = tf.placeholder(tf.float32, [None])

train_list = ['traindata_63.tfrecords-000', 'traindata_63.tfrecords-001', 'traindata_63.tfrecords-002',
              'traindata_63.tfrecords-003', 'traindata_63.tfrecords-004', 'traindata_63.tfrecords-005',
              'traindata_63.tfrecords-006', 'traindata_63.tfrecords-007', 'traindata_63.tfrecords-008',
              'traindata_63.tfrecords-009', 'traindata_63.tfrecords-010', 'traindata_63.tfrecords-011',
              'traindata_63.tfrecords-012', 'traindata_63.tfrecords-013', 'traindata_63.tfrecords-014',
              'traindata_63.tfrecords-015', 'traindata_63.tfrecords-016', 'traindata_63.tfrecords-017',
              'traindata_63.tfrecords-018', 'traindata_63.tfrecords-019', 'traindata_63.tfrecords-020',
              'traindata_63.tfrecords-021']    #制作成的所有tfrecord数据,每个最多包含1000个图片数据
# 随机打乱顺序
img, label = read_and_decode_tfrecord(train_list)
img_batch, label_batch = tf.train.shuffle_batch([img, label], num_threads=2, batch_size=batch_size_, capacity=10000,
                                                min_after_dequeue=9900)

注意这里使用了tf.train.shuffle_batch随机打乱队列里面的数据顺序,num_threads表示线程数,capacity表示队列的容量,在这里设置成10000, min_after_dequeue队列里保留的最小数据量,并且控制着随机的程度,设置成9900的意思是,当队列中的数据出列100个,剩下9900个的时候,就要重新补充100个数据进来并打乱顺序。如果你要按顺序导入队列,改成tf.train.batch函数,并删除min_after_dequeue参数。这些参数都要根据自己的电脑配置进行相应的设置。
接下来将label值进行onehot编码,直接调用tf.one_hot函数。因为我们这里有100类,depth设置成100:

# 将label值进行onehot编码
one_hot_labels = tf.one_hot(indices=tf.cast(y_, tf.int32), depth=100)
pred, end_points = nets.resnet_v2.resnet_v2_50(x, num_classes=100, is_training=True)
pred = tf.reshape(pred, shape=[-1, 100])

我们通过nets.resnet_v2.resnet_v2_50直接调用ResNet_50网络,同样num_classes等于类别总数,is_training表示我们是否要训练网络里面固定层的参数,True表示所有参数都重新训练,False表示只训练后面几层的参数。
网络搭好后,我们继续定义损失函数和优化器,损失函数选择sigmoid交叉熵,优化器选择Adam:

# 定义损失函数和优化器
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=pred, labels=one_hot_labels))
optimizer = tf.train.AdamOptimizer(learning_rate=lr).minimize(loss)

定义准确率函数,tf.argmax函数返回最大值所在位置:

# 准确度
a = tf.argmax(pred, 1)
b = tf.argmax(one_hot_labels, 1)
correct_pred = tf.equal(a, b)
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

最后我们构建Session,让网络跑起来:

saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # 创建一个协调器,管理线程
    coord = tf.train.Coordinator()
    # 启动QueueRunner,此时文件名队列已经进队
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    i = 0
    while True:
        i += 1
        b_image, b_label = sess.run([img_batch, label_batch])
        _, loss_, y_t, y_p, a_, b_ = sess.run([optimizer, loss, one_hot_labels, pred, a, b], feed_dict={x: b_image,
                                                                                                        y_: b_label})
        print('step: {}, train_loss: {}'.format(i, loss_))
        if i % 20 == 0:
            _loss, acc_train = sess.run([loss, accuracy], feed_dict={x: b_image, y_: b_label})
            print('--------------------------------------------------------')
            print('step: {}  train_acc: {}  loss: {}'.format(i, acc_train, _loss))
            print('--------------------------------------------------------')
            if i == 200000:
                saver.save(sess, save_dir, global_step=i)
            elif i == 300000:
                saver.save(sess, save_dir, global_step=i)
            elif i == 400000:
                saver.save(sess, save_dir, global_step=i)
                break
    coord.request_stop()
    # 其他所有线程关闭之后,这一函数才能返回
    coord.join(threads)

当我们使用队列系统时,在Session部分一定要创建一个协调器管理线程。我们每20步输出一次准确率,在200000,300000,400000步的时候自动保存模型。
训练结束后会得到如下模型文件,我在这里只保留了300000步的模型:


模型文件

附上训练网络完整代码:

import tensorflow as tf
import tensorflow.contrib.slim.nets as nets


def read_and_decode_tfrecord(filename):
    filename_deque = tf.train.string_input_producer(filename)
    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_deque)
    features = tf.parse_single_example(serialized_example, features={
        'label': tf.FixedLenFeature([], tf.int64),
        'img_raw': tf.FixedLenFeature([], tf.string)})
    label = tf.cast(features['label'], tf.int32)
    img = tf.decode_raw(features['img_raw'], tf.uint8)
    img = tf.reshape(img, [224, 224, 3])
    img = tf.cast(img, tf.float32) / 255.0        #将矩阵归一化0-1之间
    return img, label


save_dir = r"./train_image_63.model"
batch_size_ = 2
lr = tf.Variable(0.0001, dtype=tf.float32)
x = tf.placeholder(tf.float32, [None, 224, 224, 3])
y_ = tf.placeholder(tf.float32, [None])

train_list = ['traindata_63.tfrecords-000', 'traindata_63.tfrecords-001', 'traindata_63.tfrecords-002',
              'traindata_63.tfrecords-003', 'traindata_63.tfrecords-004', 'traindata_63.tfrecords-005',
              'traindata_63.tfrecords-006', 'traindata_63.tfrecords-007', 'traindata_63.tfrecords-008',
              'traindata_63.tfrecords-009', 'traindata_63.tfrecords-010', 'traindata_63.tfrecords-011',
              'traindata_63.tfrecords-012', 'traindata_63.tfrecords-013', 'traindata_63.tfrecords-014',
              'traindata_63.tfrecords-015', 'traindata_63.tfrecords-016', 'traindata_63.tfrecords-017',
              'traindata_63.tfrecords-018', 'traindata_63.tfrecords-019', 'traindata_63.tfrecords-020',
              'traindata_63.tfrecords-021']
# 随机打乱顺序
img, label = read_and_decode_tfrecord(train_list)
img_batch, label_batch = tf.train.shuffle_batch([img, label], num_threads=2, batch_size=batch_size_, capacity=10000,
                                                min_after_dequeue=9900)

# 将label值进行onehot编码
one_hot_labels = tf.one_hot(indices=tf.cast(y_, tf.int32), depth=100)
pred, end_points = nets.resnet_v2.resnet_v2_50(x, num_classes=100, is_training=True)
pred = tf.reshape(pred, shape=[-1, 100])

# 定义损失函数和优化器
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=pred, labels=one_hot_labels))
optimizer = tf.train.AdamOptimizer(learning_rate=lr).minimize(loss)

# 准确度
a = tf.argmax(pred, 1)
b = tf.argmax(one_hot_labels, 1)
correct_pred = tf.equal(a, b)
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # 创建一个协调器,管理线程
    coord = tf.train.Coordinator()
    # 启动QueueRunner,此时文件名队列已经进队
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    i = 0
    while True:
        i += 1
        b_image, b_label = sess.run([img_batch, label_batch])
        _, loss_, y_t, y_p, a_, b_ = sess.run([optimizer, loss, one_hot_labels, pred, a, b], feed_dict={x: b_image,
                                                                                                        y_: b_label})
        print('step: {}, train_loss: {}'.format(i, loss_))
        if i % 20 == 0:
            _loss, acc_train = sess.run([loss, accuracy], feed_dict={x: b_image, y_: b_label})
            print('--------------------------------------------------------')
            print('step: {}  train_acc: {}  loss: {}'.format(i, acc_train, _loss))
            print('--------------------------------------------------------')
            if i == 200000:
                saver.save(sess, save_dir, global_step=i)
            elif i == 300000:
                saver.save(sess, save_dir, global_step=i)
            elif i == 400000:
                saver.save(sess, save_dir, global_step=i)
                break
    coord.request_stop()
    # 其他所有线程关闭之后,这一函数才能返回
    coord.join(threads)

预测结果

我们利用1000张测试数据评估我们的模型,直接放代码:

import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
from PIL import Image
import os


test_dir = r'./test'     # 原始的test文件夹,含带预测的图片
model_dir = r'./train_image_63.model-300000'     # 模型地址
test_txt_dir = r'./test.txt'     # 原始的test.txt文件
result_dir = r'./result.txt'     # 生成输出结果
x = tf.placeholder(tf.float32, [None, 224, 224, 3])
classes = ['1', '10', '100', '11', '12', '13', '14', '15', '16', '17', '18', '19', '2', '20', '21', '22', '23', '24',
           '25', '26', '27', '28', '29', '3', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '4', '40',
           '41', '42', '43', '44', '45', '46', '47', '48', '49', '5', '50', '51', '52', '53', '54', '55', '56', '57',
           '58', '59', '6', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '7', '70', '71', '72', '73',
           '74', '75', '76', '77', '78', '79', '8', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '9',
           '90', '91', '92', '93', '94', '95', '96', '97', '98', '99']      # 标签顺序


pred, end_points = nets.resnet_v2.resnet_v2_50(x, num_classes=100, is_training=True)
pred = tf.reshape(pred, shape=[-1, 100])
a = tf.argmax(pred, 1)
saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver.restore(sess, model_dir)
    with open(test_txt_dir, 'r') as f:
        data = f.readlines()
        for i in data:
            test_name = i.split()[0]
            for pic in os.listdir(test_dir):
                if pic == test_name:
                    img_path = os.path.join(test_dir, pic)
                    img = Image.open(img_path)
                    img = img.resize((224, 224))
                    img = tf.reshape(img, [1, 224, 224, 3])
                    img1 = tf.reshape(img, [1, 224, 224, 3])
                    img = tf.cast(img, tf.float32) / 255.0
                    b_image, b_image_raw = sess.run([img, img1])
                    t_label = sess.run(a, feed_dict={x: b_image})
                    index_ = t_label[0]
                    predict = classes[index_]
                    with open(result_dir, 'a') as f1:
                        print(test_name, predict, file=f1)
                    break

需要注意的是test数据集并没有处理成tfrecord格式,在这里直接将图片一张张导入用模型预测,生成的结果文件主要是为了提交比赛使用。原始数据和模型我会放在这里,密码:8xbi。有兴趣自提。
至此,我们就完成了一个CNN图像识别项目。

可能感兴趣

"笨方法"学习CNN图像识别(一)—— 图片预处理
"笨方法"学习CNN图像识别(二)—— tfrecord格式高效读取数据
"笨方法"学习CNN图像识别(三)—— ResNet网络训练及预测
使用Python+Tensorflow的CNN技术快速识别验证码


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容