zoukankan      html  css  js  c++  java
  • 人脸识别智能小程序 | Tensorflow挑战Cifar-10图像分类任务 | 04

    TF挑战cifar10

    cifar100有两类标签,一个是大类标签,一个是小类标签,也就是一个是粗粒度标签,一个是细粒度标签,

    上面的代码是解析cifar10的,前面已经讲解过,这里不再赘述。

    使用 tf.train.string_input_producer 的方式来对打包好的TFRecord文件进行读取。

    将所有的训练样本存放在 train.tfrecord,将所有的测试样本存放在 test.tfrecord。

    TF训练框架搭建:

    • Data

    首先是数据的读取和数据的打包。

    • Net

    网络的搭建,这里采用slim来搭建网络结构,因为slim是对tf更加高层的封装,可以写更加简洁的代码。

    • Loss

    Loss本身就是网络的一部分,这个会采用softmaxLoss来进行Loss定义。

    另外还会定义正则化的Loss。

    • Summary

    Summary完成了训练过程中日志的记录。

    • Session

    Session完成了构造出网络结构后,如果对计算图中的结点进行计算,会通过Session在后端完成整个网络的BP。并通过Feed给网络数据,Fetch获得输出的张量。

    训练代码,也就是baseline版本,要如何进行优化呢?上面提供了几个参考思路。

    • 更多的数据增强策略,比如:mixup等

    通过数据增强的策略,来丰富样本量,进而提高模型对更多样本的泛化能力。

    • 更好的主干网络,比如:SENet等

    有VGG,ResNet这样的主干网络,当然,SENet比ResNet的性能更好,可以实验哪个主干网络实验出来的效果更好。

    • 更好的标签策略,比如:Soft-label策略

    cifar10的标签就是从0到9十个标签,这里可以采用soft-label的策略,比如有些物体既像飞机又像汽车,那么就可以采用软的策略,来对标签进行预处理。

    • 更好的loss设计,比如:采用分类+回归smooth-l1 loss等

    另外可以采用更好的loss,这里采用交叉熵损失来完成图像分类的任务,另外大家也可以考虑结合回归的loss,来进行一个多个loss的约束。

    • 不同的优化器、参数初始化方法等

    Cifar10数据读取与数据增强

    文件项目的目录结构如上图

    • data: 存放数据 这里是打包好的TFRecord
    • logdirs:存放log日志信息
    • model:存放训练好的模型

    readcifar10.py

    import tensorflow as tf
    
    
    def read(batchsize=64, type=1, no_aug_data=1):
        """
        读取TFRecord的脚本
    
        params
        batchsize:批大小 默认64
        type: test or train 表示从test中读数据还是从train中读数据 0表示train 1表示test
        no_aug_data: 是否进行数据增强 1 or 0
        """
        reader = tf.TFRecordReader()
        """
        搭建模型的时候每训练一定的次数,或每训练一个epoch的时候,这时候会从test文件进行一次测试
        """
        if type == 0:  # train
            file_list = ["data/train.tfrecord"]
        if type == 1:  # test
            file_list = ["data/test.tfrecord"]
    
        filename_queue = tf.train.string_input_producer(
            file_list, num_epochs=None, shuffle=True
        )
        _, serialized_example = reader.read(filename_queue)
    
        batch = tf.train.shuffle_batch([serialized_example], batchsize, capacity=batchsize * 10,
                                       min_after_dequeue=batchsize * 5)
    
        #
        feature = {'image': tf.FixedLenFeature([], tf.string),
                   'label': tf.FixedLenFeature([], tf.int64)}
    
        features = tf.parse_example(batch, features=feature)
    
        images = features["image"]
    
        img_batch = tf.decode_raw(images, tf.uint8)
        img_batch = tf.cast(img_batch, tf.float32)
        img_batch = tf.reshape(img_batch, [batchsize, 32, 32, 3])
    
        """
        这里对train的数据进行数据增强 
        数据增强只对train数据
        """
        if type == 0 and no_aug_data == 1:
            # 随机裁剪
            distorted_image = tf.random_crop(img_batch,
                                             [batchsize, 28, 28, 3])
            # 随机对比度
            distorted_image = tf.image.random_contrast(distorted_image,
                                                       lower=0.8,
                                                       upper=1.2)
            # 随机色调
            distorted_image = tf.image.random_hue(distorted_image,
                                                  max_delta=0.2)
            # 随机饱和度
            distorted_image = tf.image.random_saturation(distorted_image,
                                                         lower=0.8,
                                                         upper=1.2)
            # 对处理过的图像进行取值范围的约束
            img_batch = tf.clip_by_value(distorted_image, 0, 255)
    
        # 最后将图像resize回[32,32]
        img_batch = tf.image.resize_images(img_batch, [32, 32])
        label_batch = tf.cast(features['label'], tf.int64)
    
        # [-1,1] 将图片归一化
        img_batch = tf.cast(img_batch, tf.float32) / 128.0 - 1.0
        
        return img_batch, label_batch
    
    

    TensorFlow+Slim网络结构搭建

    def model(image, keep_prob=0.8, is_training=True):
        """
        在model中我们会定义网络结构
        params
        image: 输入的图片
    
        return 概率分布值 10 dim vector
        """
    
        # batchnorm的参数
        batch_norm_params = {
            "is_training": is_training,  # train:True test:False
            "epsilon": 1e-5,  # 这个值是防止batchnorm在归一化的时候除0
            "decay": 0.997,  # 衰减系数
            'scale': True,
            'updates_collections': tf.GraphKeys.UPDATE_OPS
        }
    
        """
        定义优化器
        用 slim.arg_scope()为目标函数设置默认参数.
        下面两个with,完成卷积默认参数的初始化和pooling层默认参数的初始化
        一个with就是一个参数域 网络可以使用通过的参数
        """
        with slim.arg_scope(
            [slim.conv2d],  # 这里是给 slim.conv2d 规定过了后面的参数
            weights_initializer=slim.variance_scaling_initializer(),  # 方差尺度不变来进行初始化
            activation_fn=tf.nn.relu,  # 默认激活函数为relu 在卷积之后加入激活函数
            weights_regularizer=slim.l2_regularizer(
                0.0001),  # 权值的正则化约束 正则项权值为0.0001
            normalizer_fn=slim.batch_norm,  # 在卷积机后加入 BatchNorm
                normalizer_params=batch_norm_params):
    
            with slim.arg_scope([slim.max_pool2d], padding="SAME"):  # 给 slim.max_pool2d 规定参数
                """
                接下来利用卷积层、池化层、全连接层搭建一个cifar10用于图像分类的全连接网
                """
                # 输入image 学习32个卷积核(channel) 卷积核大小 [3,3] 卷积层命名 conv1
                net = slim.conv2d(image, 32, [3, 3], scope='conv1')
                # 输入net 学习32个卷积核 卷积核大小 [3,3] 卷积层命名 conv2
                net = slim.conv2d(net, 32, [3, 3], scope='conv2')
                # 输入net 池化核大小[3,3] stride=2(进行两倍下采样)
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
                """
                ===========
                上面 conv+conv+maxpool 算是一个基本单元,后面基本就是使用这样的基本单元进行堆叠
                ==========
                """
                # 每次经过pooling之后 卷积层channel的数量应该翻倍
                net = slim.conv2d(net, 64, [3, 3], scope='conv3')
                net = slim.conv2d(net, 64, [3, 3], scope='conv4')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool2')
                net = slim.conv2d(net, 128, [3, 3], scope='conv5')
                net = slim.conv2d(net, 128, [3, 3], scope='conv6')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool3')
                # 经过8倍下采样之后 在加入一个卷积层
                net = slim.conv2d(net, 256, [3, 3], scope='conv7')
                """
                特征图维度 (n,h,w,c)
                对 h,w 进行均值的求解 得到 (n,1,1,c) 因为 reduce_mean 就是降维求平均
                """
                net = tf.reduce_mean(net, axis=[1, 2])  # nhwc--n11c
                """
                slim.flatten 将输入扁平化但保留batch_size,假设第一维是batch。
                将 (n,1,1,c) 转为 (n,c)
                """
                net = slim.flatten(net)
                # 全连接层 这里还不着急分类 (1,1024)可以作为从图像中提取出来的特征
                net = slim.fully_connected(net, 1024)
                """
                全连接层的参数太多 要加入dropout层 来进行正则化
                keep_prob 概率值 定义了我们对当前神经元选择的概率
                在训练和测试的时候,这个概率要取不同的值 训练的时候要取<1 测试的时候要=1
                """
                slim.dropout(net, keep_prob)
                # 输出为10 对应了10个分类类别
                net = slim.fully_connected(net, 10)
        return net  # 10 dim vec
    
    

    这里的模型设计基本是沿用了VGG的设计思路,采用了(3*3)的小卷积核,每次pooling后对channel的数量进行翻倍,并且在全连接层后面加入了dropout层。另外在经过8倍下采样后采用了一个average pooling层,进行一个全图的池化,最后输出一个10维的概率分布值。

    Loss、Optimal、Learning Rate、BN等定义

    Loss层定义

    def loss(logits, label):
        """
        loss使用交叉熵损失函数
        param
        logits: 预测出来的概率分布值
        label: 实际的label
    
        return 分类的loss
        """
        # 对label进行one-hot编码 定义one-hot长度为10
        one_hot_label = slim.one_hot_encoding(label, 10) 
        # 使用交叉熵损失函数 并传入 预测值 和 one-hot label
        slim.losses.softmax_cross_entropy(logits, one_hot_label)
    
        """
        在前面 slim.arg_scope 中定义了weights_regularizer=slim.l2_regularizer(0.0001)
        通过 tf.GraphKeys.REGULARIZATION_LOSSES 可以拿到这些loss正则化的集合
        """
        reg_set = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
        # 将这些正则化loss进行相加 计算出总体的l2_loss
        l2_loss = tf.add_n(reg_set)
        # 将 l2_loss添加到loss中
        slim.losses.add_loss(l2_loss)
    
        totalloss = slim.losses.get_total_loss()
        # 这里为了后面做日志,这里把l2_loss也传出
        return totalloss, l2_loss
    

    Train部分代码编写

    初代Train代码

    import os
    import tensorflow as tf
    import readcifar10
    from tqdm import tqdm
    slim = tf.contrib.slim
    
    
    slim = tf.contrib.slim
    
    
    def model(image, keep_prob=0.8, is_training=True):
        """
        在model中我们会定义网络结构
        params
        image: 输入的图片
    
        return 概率分布值 10 dim vector
        """
    
        # batchnorm的参数
        batch_norm_params = {
            "is_training": is_training,  # train:True test:False
            "epsilon": 1e-5,  # 这个值是防止batchnorm在归一化的时候除0
            "decay": 0.997,  # 衰减系数
            'scale': True,
            'updates_collections': tf.GraphKeys.UPDATE_OPS
        }
    
        """
        定义优化器
        用 slim.arg_scope()为目标函数设置默认参数.
        下面两个with,完成卷积默认参数的初始化和pooling层默认参数的初始化
        一个with就是一个参数域 网络可以使用通过的参数
        """
        with slim.arg_scope(
            [slim.conv2d],  # 这里是给 slim.conv2d 规定过了后面的参数
            weights_initializer=slim.variance_scaling_initializer(),  # 方差尺度不变来进行初始化
            activation_fn=tf.nn.relu,  # 默认激活函数为relu 在卷积之后加入激活函数
            weights_regularizer=slim.l2_regularizer(
                0.0001),  # 权值的正则化约束 正则项权值为0.0001
            normalizer_fn=slim.batch_norm,  # 在卷积机后加入 BatchNorm
                normalizer_params=batch_norm_params):
    
            with slim.arg_scope([slim.max_pool2d], padding="SAME"):  # 给 slim.max_pool2d 规定参数
                """
                接下来利用卷积层、池化层、全连接层搭建一个cifar10用于图像分类的全连接网
                """
                # 输入image 学习32个卷积核(channel) 卷积核大小 [3,3] 卷积层命名 conv1
                net = slim.conv2d(image, 32, [3, 3], scope='conv1')
                # 输入net 学习32个卷积核 卷积核大小 [3,3] 卷积层命名 conv2
                net = slim.conv2d(net, 32, [3, 3], scope='conv2')
                # 输入net 池化核大小[3,3] stride=2(进行两倍下采样)
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
                """
                ===========
                上面 conv+conv+maxpool 算是一个基本单元,后面基本就是使用这样的基本单元进行堆叠
                ==========
                """
                # 每次经过pooling之后 卷积层channel的数量应该翻倍
                net = slim.conv2d(net, 64, [3, 3], scope='conv3')
                net = slim.conv2d(net, 64, [3, 3], scope='conv4')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool2')
                net = slim.conv2d(net, 128, [3, 3], scope='conv5')
                net = slim.conv2d(net, 128, [3, 3], scope='conv6')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool3')
                # 经过8倍下采样之后 在加入一个卷积层
                net = slim.conv2d(net, 256, [3, 3], scope='conv7')
                """
                特征图维度 (n,h,w,c)
                对 h,w 进行均值的求解 得到 (n,1,1,c) 因为 reduce_mean 就是降维求平均
                """
                net = tf.reduce_mean(net, axis=[1, 2])  # nhwc--n11c
                """
                slim.flatten 将输入扁平化但保留batch_size,假设第一维是batch。
                将 (n,1,1,c) 转为 (n,c)
                """
                net = slim.flatten(net)
                # 全连接层 这里还不着急分类 (1,1024)可以作为从图像中提取出来的特征
                net = slim.fully_connected(net, 1024)
                """
                全连接层的参数太多 要加入dropout层 来进行正则化
                keep_prob 概率值 定义了我们对当前神经元选择的概率
                在训练和测试的时候,这个概率要取不同的值 训练的时候要取<1 测试的时候要=1
                """
                slim.dropout(net, keep_prob)
                # 输出为10 对应了10个分类类别
                net = slim.fully_connected(net, 10)
        return net  # 10 dim vec
    
    
    def loss(logits, label):
        """
        loss使用交叉熵损失函数
        param
        logits: 预测出来的概率分布值
        label: 实际的label
    
        return 分类的loss
        """
        # 对label进行one-hot编码 定义one-hot长度为10
        one_hot_label = slim.one_hot_encoding(label, 10)
        # 使用交叉熵损失函数 并传入 预测值 和 one-hot label
        slim.losses.softmax_cross_entropy(logits, one_hot_label)
    
        """
        在前面 slim.arg_scope 中定义了weights_regularizer=slim.l2_regularizer(0.0001)
        通过 tf.GraphKeys.REGULARIZATION_LOSSES 可以拿到这些loss正则化的集合
        """
        reg_set = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
        # 将这些正则化loss进行相加 计算出总体的l2_loss
        l2_loss = tf.add_n(reg_set)
        # 将 l2_loss添加到loss中
        slim.losses.add_loss(l2_loss)
    
        totalloss = slim.losses.get_total_loss()
        # 这里为了后面做日志,这里把l2_loss也传出
        return totalloss, l2_loss
    
    
    def func_optimal(batchsize, loss_val):
        """
        定义优化器
        """
        global_step = tf.Variable(0, trainable=False)
        # 通过指数衰减的形式来定义学习率
        lr = tf.train.exponential_decay(0.01,
                                        global_step,
                                        decay_steps=50000 // batchsize,
                                        decay_rate=0.95,
                                        staircase=False)
    
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    
        with tf.control_dependencies(update_ops):
            op = tf.train.AdamOptimizer(lr).minimize(loss_val, global_step)
    
        """
        global_step 可以得到当前的迭代次数
        op sess.run()执行op可以完成对网络参数的调节
        lr 返回 便于log信息的记录
        """
        return global_step, op, lr
    
    
    def train():
        batchsize = 64
        floder_log = 'logdirs'
        floder_model = 'model'
    
        if not os.path.exists(floder_log):
            os.mkdir(floder_log)
    
        if not os.path.exists(floder_model):
            os.mkdir(floder_model)
    
        # data
        tr_im, tr_label = readcifar10.read(batchsize, 0, 1)
        te_im, te_label = readcifar10.read(batchsize, 1, 0)
    
        # net
        """
        tf.placeholder 此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值
        训练数据 [None,32,32,3] None是考虑到batch_size是可以变化的
        """
        input_data = tf.placeholder(tf.float32, shape=[None, 32, 32, 3],
                                    name='input_data')
    
        input_label = tf.placeholder(tf.int64, shape=[None],
                                     name='input_label')
        """
        keep_prob dropout的概率值
        训练过程 keep_prob 也就是dropout的概率值 为0.5 或小于1的值
        """
        keep_prob = tf.placeholder(tf.float32, shape=None,
                                   name='keep_prob')
    
        is_training = tf.placeholder(tf.bool, shape=None,
                                     name='is_training')
    
        logits = model(
            input_data, keep_prob=keep_prob, is_training=is_training)
    
        # loss
        """
        损失函数 传入预测的结果和真实的标签
        """
        total_loss, l2_loss = loss(logits, input_label)
    
        # accurancy
        """
        tf.argmax 获取最大值的索引
        [n,10] axis=1 计算的是10维这个维度最大值对应的索引值
        """
        pred_max = tf.argmax(logits, 1)
        # 判断最大值索引和 label是否相等
        correct = tf.equal(pred_max, input_label)
        accurancy = tf.reduce_mean(tf.cast(correct, tf.float32))
    
        # op
        global_step, op, lr = func_optimal(batchsize, total_loss)
    
        with tf.Session() as sess:
            # 初始化参数 包括了局部变量和全局变量
            sess.run(tf.group(tf.global_variables_initializer(),
                              tf.local_variables_initializer()))
            # 启动文件队列写入的线程
            tf.train.start_queue_runners(sess=sess,
                                         coord=tf.train.Coordinator())
    
            epoch_val = 10
    
            # 读取文件队列中的数据,完成对网络的训练
            for i in tqdm(range(50000 * epoch_val)):
                train_im_batch, train_label_batch = 
                    sess.run([tr_im, tr_label])
    
                feed_dict = {
                    input_data: train_im_batch,
                    input_label: train_label_batch,
                    keep_prob: 0.8,
                    is_training: True
                }
    
                # 注意 这里得到这这些值 都是一个batch_size的
                _, global_step_val, 
                    lr_val, 
                    total_loss_val, 
                    accurancy_val = sess.run([op,
                                              global_step,
                                              lr,
                                              total_loss,
                                              accurancy],
                                             feed_dict=feed_dict)
    
                # 每隔一百次打印一次
                if i % 20 == 0:
                    print("
    {},{},{},{}".format(global_step_val,
                    lr_val, 
                    total_loss_val,
                    accurancy_val))
    
        return
    
    
    if __name__ == '__main__':
        train()
    
    

    Test部分代码编写

    在完成一部分train,可以进行test

    import os
    import tensorflow as tf
    import readcifar10
    from tqdm import tqdm
    slim = tf.contrib.slim
    
    
    slim = tf.contrib.slim
    
    
    def model(image, keep_prob=0.8, is_training=True):
        """
        在model中我们会定义网络结构
        params
        image: 输入的图片
    
        return 概率分布值 10 dim vector
        """
    
        # batchnorm的参数
        batch_norm_params = {
            "is_training": is_training,  # train:True test:False
            "epsilon": 1e-5,  # 这个值是防止batchnorm在归一化的时候除0
            "decay": 0.997,  # 衰减系数
            'scale': True,
            'updates_collections': tf.GraphKeys.UPDATE_OPS
        }
    
        """
        定义优化器
        用 slim.arg_scope()为目标函数设置默认参数.
        下面两个with,完成卷积默认参数的初始化和pooling层默认参数的初始化
        一个with就是一个参数域 网络可以使用通过的参数
        """
        with slim.arg_scope(
            [slim.conv2d],  # 这里是给 slim.conv2d 规定过了后面的参数
            weights_initializer=slim.variance_scaling_initializer(),  # 方差尺度不变来进行初始化
            activation_fn=tf.nn.relu,  # 默认激活函数为relu 在卷积之后加入激活函数
            weights_regularizer=slim.l2_regularizer(
                0.0001),  # 权值的正则化约束 正则项权值为0.0001
            normalizer_fn=slim.batch_norm,  # 在卷积机后加入 BatchNorm
                normalizer_params=batch_norm_params):
    
            with slim.arg_scope([slim.max_pool2d], padding="SAME"):  # 给 slim.max_pool2d 规定参数
                """
                接下来利用卷积层、池化层、全连接层搭建一个cifar10用于图像分类的全连接网
                """
                # 输入image 学习32个卷积核(channel) 卷积核大小 [3,3] 卷积层命名 conv1
                net = slim.conv2d(image, 32, [3, 3], scope='conv1')
                # 输入net 学习32个卷积核 卷积核大小 [3,3] 卷积层命名 conv2
                net = slim.conv2d(net, 32, [3, 3], scope='conv2')
                # 输入net 池化核大小[3,3] stride=2(进行两倍下采样)
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
                """
                ===========
                上面 conv+conv+maxpool 算是一个基本单元,后面基本就是使用这样的基本单元进行堆叠
                ==========
                """
                # 每次经过pooling之后 卷积层channel的数量应该翻倍
                net = slim.conv2d(net, 64, [3, 3], scope='conv3')
                net = slim.conv2d(net, 64, [3, 3], scope='conv4')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool2')
                net = slim.conv2d(net, 128, [3, 3], scope='conv5')
                net = slim.conv2d(net, 128, [3, 3], scope='conv6')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool3')
                # 经过8倍下采样之后 在加入一个卷积层
                net = slim.conv2d(net, 256, [3, 3], scope='conv7')
                """
                特征图维度 (n,h,w,c)
                对 h,w 进行均值的求解 得到 (n,1,1,c) 因为 reduce_mean 就是降维求平均
                """
                net = tf.reduce_mean(net, axis=[1, 2])  # nhwc--n11c
                """
                slim.flatten 将输入扁平化但保留batch_size,假设第一维是batch。
                将 (n,1,1,c) 转为 (n,c)
                """
                net = slim.flatten(net)
                # 全连接层 这里还不着急分类 (1,1024)可以作为从图像中提取出来的特征
                net = slim.fully_connected(net, 1024)
                """
                全连接层的参数太多 要加入dropout层 来进行正则化
                keep_prob 概率值 定义了我们对当前神经元选择的概率
                在训练和测试的时候,这个概率要取不同的值 训练的时候要取<1 测试的时候要=1
                """
                slim.dropout(net, keep_prob)
                # 输出为10 对应了10个分类类别
                net = slim.fully_connected(net, 10)
        return net  # 10 dim vec
    
    
    def loss(logits, label):
        """
        loss使用交叉熵损失函数
        param
        logits: 预测出来的概率分布值
        label: 实际的label
    
        return 分类的loss
        """
        # 对label进行one-hot编码 定义one-hot长度为10
        one_hot_label = slim.one_hot_encoding(label, 10)
        # 使用交叉熵损失函数 并传入 预测值 和 one-hot label
        slim.losses.softmax_cross_entropy(logits, one_hot_label)
    
        """
        在前面 slim.arg_scope 中定义了weights_regularizer=slim.l2_regularizer(0.0001)
        通过 tf.GraphKeys.REGULARIZATION_LOSSES 可以拿到这些loss正则化的集合
        """
        reg_set = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
        # 将这些正则化loss进行相加 计算出总体的l2_loss
        l2_loss = tf.add_n(reg_set)
        # 将 l2_loss添加到loss中
        slim.losses.add_loss(l2_loss)
    
        totalloss = slim.losses.get_total_loss()
        # 这里为了后面做日志,这里把l2_loss也传出
        return totalloss, l2_loss
    
    
    def func_optimal(batchsize, loss_val):
        """
        定义优化器
        """
        global_step = tf.Variable(0, trainable=False)
        # 通过指数衰减的形式来定义学习率
        lr = tf.train.exponential_decay(0.01,
                                        global_step,
                                        decay_steps=50000 // batchsize,
                                        decay_rate=0.95,
                                        staircase=False)
    
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    
        with tf.control_dependencies(update_ops):
            op = tf.train.AdamOptimizer(lr).minimize(loss_val, global_step)
    
        """
        global_step 可以得到当前的迭代次数
        op sess.run()执行op可以完成对网络参数的调节
        lr 返回 便于log信息的记录
        """
        return global_step, op, lr
    
    
    def train():
        batchsize = 64
        floder_log = 'logdirs'
        floder_model = 'model'
    
        if not os.path.exists(floder_log):
            os.mkdir(floder_log)
    
        if not os.path.exists(floder_model):
            os.mkdir(floder_model)
    
        # data
        tr_im, tr_label = readcifar10.read(batchsize, 0, 1)
        te_im, te_label = readcifar10.read(batchsize, 1, 0)
    
        # net
        """
        tf.placeholder 此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值
        训练数据 [None,32,32,3] None是考虑到batch_size是可以变化的
        """
        input_data = tf.placeholder(tf.float32, shape=[None, 32, 32, 3],
                                    name='input_data')
    
        input_label = tf.placeholder(tf.int64, shape=[None],
                                     name='input_label')
        """
        keep_prob dropout的概率值
        训练过程 keep_prob 也就是dropout的概率值 为0.5 或小于1的值
        """
        keep_prob = tf.placeholder(tf.float32, shape=None,
                                   name='keep_prob')
    
        is_training = tf.placeholder(tf.bool, shape=None,
                                     name='is_training')
    
        logits = model(
            input_data, keep_prob=keep_prob, is_training=is_training)
    
        # loss
        """
        损失函数 传入预测的结果和真实的标签
        """
        total_loss, l2_loss = loss(logits, input_label)
    
        # accurancy
        """
        tf.argmax 获取最大值的索引
        [n,10] axis=1 计算的是10维这个维度最大值对应的索引值
        """
        pred_max = tf.argmax(logits, 1)
        # 判断最大值索引和 label是否相等
        correct = tf.equal(pred_max, input_label)
        accurancy = tf.reduce_mean(tf.cast(correct, tf.float32))
    
        # op
        global_step, op, lr = func_optimal(batchsize, total_loss)
    
        with tf.Session() as sess:
            # 初始化参数 包括了局部变量和全局变量
            sess.run(tf.group(tf.global_variables_initializer(),
                              tf.local_variables_initializer()))
            # 启动文件队列写入的线程
            tf.train.start_queue_runners(sess=sess,
                                         coord=tf.train.Coordinator())
    
            epoch_val = 10
    
            # 读取文件队列中的数据,完成对网络的训练
            for i in tqdm(range(50000 * epoch_val)): # 这里这个数量我还是没有理解
                train_im_batch, train_label_batch = 
                    sess.run([tr_im, tr_label])
    
                feed_dict = {
                    input_data: train_im_batch,
                    input_label: train_label_batch,
                    keep_prob: 0.8,
                    is_training: True
                }
    
                # 注意 这里得到这这些值 都是一个batch_size的
                _, global_step_val, 
                    lr_val, 
                    total_loss_val, 
                    accurancy_val = sess.run([op,
                                              global_step,
                                              lr,
                                              total_loss,
                                              accurancy],
                                             feed_dict=feed_dict)
    
                # 每隔一百次打印一次
                if i % 100 == 0:
                    print("
    [trian]:{},{},{},{}".format(global_step_val,
                    lr_val, 
                    total_loss_val,
                    accurancy_val))
                
                # 隔一段时间进行测试
                if i % (50000 // batchsize) == 0:
                    test_loss = 0
                    test_acc = 0
                    for ii in range(10000//batchsize):
                        test_im_batch, test_label_batch = 
                            sess.run([te_im, te_label])
                        feed_dict = {
                            input_data: test_im_batch,
                            input_label: test_label_batch,
                            keep_prob: 1.0,
                            is_training: False
                        }
    
                        total_loss_val, global_step_val, 
                        accurancy_val = sess.run([total_loss,global_step,
                                                  accurancy],
                                                 feed_dict=feed_dict)
    
                        test_loss += total_loss_val
                        test_acc += accurancy_val
    
                    print('[test]:', test_loss * batchsize / 10000,
                          test_acc* batchsize / 10000)
        return
    
    
    if __name__ == '__main__':
        train()
    
    

    Tensorboard+tf.summary

    因为查看训练的消息只能在控制台查看,所以这里要记录日志信息。

    训练好会输出日志,如下图。

    如何查看该日志呢?在终端输入以下命令

    tensorboard --logdir=logdirs
    

    import os
    import tensorflow as tf
    import readcifar10
    from tqdm import tqdm
    slim = tf.contrib.slim
    
    
    slim = tf.contrib.slim
    
    
    def model(image, keep_prob=0.8, is_training=True):
        """
        在model中我们会定义网络结构
        params
        image: 输入的图片
    
        return 概率分布值 10 dim vector
        """
    
        # batchnorm的参数
        batch_norm_params = {
            "is_training": is_training,  # train:True test:False
            "epsilon": 1e-5,  # 这个值是防止batchnorm在归一化的时候除0
            "decay": 0.997,  # 衰减系数
            'scale': True,
            'updates_collections': tf.GraphKeys.UPDATE_OPS
        }
    
        """
        定义优化器
        用 slim.arg_scope()为目标函数设置默认参数.
        下面两个with,完成卷积默认参数的初始化和pooling层默认参数的初始化
        一个with就是一个参数域 网络可以使用通过的参数
        """
        with slim.arg_scope(
            [slim.conv2d],  # 这里是给 slim.conv2d 规定过了后面的参数
            weights_initializer=slim.variance_scaling_initializer(),  # 方差尺度不变来进行初始化
            activation_fn=tf.nn.relu,  # 默认激活函数为relu 在卷积之后加入激活函数
            weights_regularizer=slim.l2_regularizer(
                0.0001),  # 权值的正则化约束 正则项权值为0.0001
            normalizer_fn=slim.batch_norm,  # 在卷积机后加入 BatchNorm
                normalizer_params=batch_norm_params):
    
            with slim.arg_scope([slim.max_pool2d], padding="SAME"):  # 给 slim.max_pool2d 规定参数
                """
                接下来利用卷积层、池化层、全连接层搭建一个cifar10用于图像分类的全连接网
                """
                # 输入image 学习32个卷积核(channel) 卷积核大小 [3,3] 卷积层命名 conv1
                net = slim.conv2d(image, 32, [3, 3], scope='conv1')
                # 输入net 学习32个卷积核 卷积核大小 [3,3] 卷积层命名 conv2
                net = slim.conv2d(net, 32, [3, 3], scope='conv2')
                # 输入net 池化核大小[3,3] stride=2(进行两倍下采样)
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
                """
                ===========
                上面 conv+conv+maxpool 算是一个基本单元,后面基本就是使用这样的基本单元进行堆叠
                ==========
                """
                # 每次经过pooling之后 卷积层channel的数量应该翻倍
                net = slim.conv2d(net, 64, [3, 3], scope='conv3')
                net = slim.conv2d(net, 64, [3, 3], scope='conv4')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool2')
                net = slim.conv2d(net, 128, [3, 3], scope='conv5')
                net = slim.conv2d(net, 128, [3, 3], scope='conv6')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool3')
                # 经过8倍下采样之后 在加入一个卷积层
                net = slim.conv2d(net, 256, [3, 3], scope='conv7')
                """
                特征图维度 (n,h,w,c)
                对 h,w 进行均值的求解 得到 (n,1,1,c) 因为 reduce_mean 就是降维求平均
                """
                net = tf.reduce_mean(net, axis=[1, 2])  # nhwc--n11c
                """
                slim.flatten 将输入扁平化但保留batch_size,假设第一维是batch。
                将 (n,1,1,c) 转为 (n,c)
                """
                net = slim.flatten(net)
                # 全连接层 这里还不着急分类 (1,1024)可以作为从图像中提取出来的特征
                net = slim.fully_connected(net, 1024)
                """
                全连接层的参数太多 要加入dropout层 来进行正则化
                keep_prob 概率值 定义了我们对当前神经元选择的概率
                在训练和测试的时候,这个概率要取不同的值 训练的时候要取<1 测试的时候要=1
                """
                slim.dropout(net, keep_prob)
                # 输出为10 对应了10个分类类别
                net = slim.fully_connected(net, 10)
        return net  # 10 dim vec
    
    
    def loss(logits, label):
        """
        loss使用交叉熵损失函数
        param
        logits: 预测出来的概率分布值
        label: 实际的label
    
        return 分类的loss
        """
        # 对label进行one-hot编码 定义one-hot长度为10
        one_hot_label = slim.one_hot_encoding(label, 10)
        # 使用交叉熵损失函数 并传入 预测值 和 one-hot label
        slim.losses.softmax_cross_entropy(logits, one_hot_label)
    
        """
        在前面 slim.arg_scope 中定义了weights_regularizer=slim.l2_regularizer(0.0001)
        通过 tf.GraphKeys.REGULARIZATION_LOSSES 可以拿到这些loss正则化的集合
        """
        reg_set = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
        # 将这些正则化loss进行相加 计算出总体的l2_loss
        l2_loss = tf.add_n(reg_set)
        # 将 l2_loss添加到loss中
        slim.losses.add_loss(l2_loss)
    
        totalloss = slim.losses.get_total_loss()
        # 这里为了后面做日志,这里把l2_loss也传出
        return totalloss, l2_loss
    
    
    def func_optimal(batchsize, loss_val):
        """
        定义优化器
        """
        global_step = tf.Variable(0, trainable=False)
        # 通过指数衰减的形式来定义学习率
        lr = tf.train.exponential_decay(0.01,
                                        global_step,
                                        decay_steps=50000 // batchsize,
                                        decay_rate=0.95,
                                        staircase=False)
    
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    
        with tf.control_dependencies(update_ops):
            op = tf.train.AdamOptimizer(lr).minimize(loss_val, global_step)
    
        """
        global_step 可以得到当前的迭代次数
        op sess.run()执行op可以完成对网络参数的调节
        lr 返回 便于log信息的记录
        """
        return global_step, op, lr
    
    
    def train():
        batchsize = 64
        floder_log = 'logdirs'
        floder_model = 'model'
    
        if not os.path.exists(floder_log):
            os.mkdir(floder_log)
    
        if not os.path.exists(floder_model):
            os.mkdir(floder_model)
    
        # 存放train和test的日志信息
        tr_summary = set()
        te_summary = set()
    
        # data
        tr_im, tr_label = readcifar10.read(batchsize, 0, 1)
        te_im, te_label = readcifar10.read(batchsize, 1, 0)
    
        # net
        """
        tf.placeholder 此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值
        训练数据 [None,32,32,3] None是考虑到batch_size是可以变化的
        """
        input_data = tf.placeholder(tf.float32, shape=[None, 32, 32, 3],
                                    name='input_data')
    
        input_label = tf.placeholder(tf.int64, shape=[None],
                                     name='input_label')
        """
        keep_prob dropout的概率值
        训练过程 keep_prob 也就是dropout的概率值 为0.5 或小于1的值
        """
        keep_prob = tf.placeholder(tf.float32, shape=None,
                                   name='keep_prob')
    
        is_training = tf.placeholder(tf.bool, shape=None,
                                     name='is_training')
    
        logits = model(
            input_data, keep_prob=keep_prob, is_training=is_training)
    
        # loss
        """
        损失函数 传入预测的结果和真实的标签
        """
        total_loss, l2_loss = loss(logits, input_label)
    
        # 记录loss
        tr_summary.add(tf.summary.scalar('train total loss', total_loss))
        tr_summary.add(tf.summary.scalar('test l2_loss', l2_loss))
    
        te_summary.add(tf.summary.scalar('train total loss', total_loss))
        te_summary.add(tf.summary.scalar('test l2_loss', l2_loss))
    
        # accurancy
        """
        tf.argmax 获取最大值的索引
        [n,10] axis=1 计算的是10维这个维度最大值对应的索引值
        """
        pred_max = tf.argmax(logits, 1)
        # 判断最大值索引和 label是否相等
        correct = tf.equal(pred_max, input_label)
        accurancy = tf.reduce_mean(tf.cast(correct, tf.float32))
    
        tr_summary.add(tf.summary.scalar('train accurancy', accurancy))
        te_summary.add(tf.summary.scalar('test accurancy', accurancy))
    
        # op
        global_step, op, lr = func_optimal(batchsize, total_loss)
    
        tr_summary.add(tf.summary.scalar('train lr', lr))
        te_summary.add(tf.summary.scalar('test lr', lr))
    
        # 图片数据是归一化过的 这里要处理回来
        tr_summary.add(tf.summary.image('train image', input_data * 128 + 128))
        te_summary.add(tf.summary.image('test image', input_data * 128 + 128))
        with tf.Session() as sess:
            # 初始化参数 包括了局部变量和全局变量
            sess.run(tf.group(tf.global_variables_initializer(),
                              tf.local_variables_initializer()))
            # 启动文件队列写入的线程
            tf.train.start_queue_runners(sess=sess,
                                         coord=tf.train.Coordinator())
    
            epoch_val = 10
    
            # 将日志进行合并
            tr_summary_op = tf.summary.merge(list(tr_summary))
            te_summary_op = tf.summary.merge(list(te_summary))
    
            summary_writer = tf.summary.FileWriter(floder_log, sess.graph)
    
            # 读取文件队列中的数据,完成对网络的训练
            for i in tqdm(range(50000 * epoch_val)):  # 这里这个数量我还是没有理解
                train_im_batch, train_label_batch = 
                    sess.run([tr_im, tr_label])
    
                feed_dict = {
                    input_data: train_im_batch,
                    input_label: train_label_batch,
                    keep_prob: 0.8,
                    is_training: True
                }
    
                # 注意 这里得到这这些值 都是一个batch_size的
                _, global_step_val, 
                    lr_val, 
                    total_loss_val, 
                    accurancy_val, tr_summary_str = sess.run([op,
                                                              global_step,
                                                              lr,
                                                              total_loss,
                                                              accurancy, tr_summary_op],
                                                             feed_dict=feed_dict)
    
                summary_writer.add_summary(tr_summary_str, global_step_val)
    
                # 每隔一百次打印一次
                if i % 100 == 0:
                    print("
    [trian]:{},{},{},{}".format(global_step_val,
                                                         lr_val,
                                                         total_loss_val,
                                                         accurancy_val))
    
                # 隔一段时间进行测试
                if i % (50000 // batchsize) == 0:
                    test_loss = 0
                    test_acc = 0
                    for ii in range(10000//batchsize):
                        test_im_batch, test_label_batch = 
                            sess.run([te_im, te_label])
                        feed_dict = {
                            input_data: test_im_batch,
                            input_label: test_label_batch,
                            keep_prob: 1.0,
                            is_training: False
                        }
    
                        total_loss_val, global_step_val, 
                        accurancy_val, te_summary_str = sess.run([total_loss,global_step,
                                                  accurancy, te_summary_op],
                                                 feed_dict=feed_dict)
    
                        summary_writer.add_summary(te_summary_str, global_step_val)
    
                        test_loss += total_loss_val
                        test_acc += accurancy_val
    
                    print('[test]:', test_loss * batchsize / 10000,
                          test_acc * batchsize / 10000)
        return
    
    
    if __name__ == '__main__':
        train()
    
    

    观察loss,确实是在收敛,但是准确率的震荡还是比较大的。

    那么处理的思路有:

    • 减小学习率或加大学习率衰减的值

    修改优化器

    模型恢复和模型存储

    上面的程序没有加入模型存储的代码。

    保存的模型代码结果如下

    对于tf的model,就是包含了以下几个部分:

    • checkpoint: 指向最新的模型
    • .meta: 定义了graph的结构
    • .data:存放了网络中具体参数的值
    • .index: 完成对data和meta的索引
    import os
    import tensorflow as tf
    import readcifar10
    from tqdm import tqdm
    slim = tf.contrib.slim
    
    
    slim = tf.contrib.slim
    
    
    def model(image, keep_prob=0.8, is_training=True):
        """
        在model中我们会定义网络结构
        params
        image: 输入的图片
    
        return 概率分布值 10 dim vector
        """
    
        # batchnorm的参数
        batch_norm_params = {
            "is_training": is_training,  # train:True test:False
            "epsilon": 1e-5,  # 这个值是防止batchnorm在归一化的时候除0
            "decay": 0.997,  # 衰减系数
            'scale': True,
            'updates_collections': tf.GraphKeys.UPDATE_OPS
        }
    
        """
        定义优化器
        用 slim.arg_scope()为目标函数设置默认参数.
        下面两个with,完成卷积默认参数的初始化和pooling层默认参数的初始化
        一个with就是一个参数域 网络可以使用通过的参数
        """
        with slim.arg_scope(
            [slim.conv2d],  # 这里是给 slim.conv2d 规定过了后面的参数
            weights_initializer=slim.variance_scaling_initializer(),  # 方差尺度不变来进行初始化
            activation_fn=tf.nn.relu,  # 默认激活函数为relu 在卷积之后加入激活函数
            weights_regularizer=slim.l2_regularizer(
                0.0001),  # 权值的正则化约束 正则项权值为0.0001
            normalizer_fn=slim.batch_norm,  # 在卷积机后加入 BatchNorm
                normalizer_params=batch_norm_params):
    
            with slim.arg_scope([slim.max_pool2d], padding="SAME"):  # 给 slim.max_pool2d 规定参数
                """
                接下来利用卷积层、池化层、全连接层搭建一个cifar10用于图像分类的全连接网
                """
                # 输入image 学习32个卷积核(channel) 卷积核大小 [3,3] 卷积层命名 conv1
                net = slim.conv2d(image, 32, [3, 3], scope='conv1')
                # 输入net 学习32个卷积核 卷积核大小 [3,3] 卷积层命名 conv2
                net = slim.conv2d(net, 32, [3, 3], scope='conv2')
                # 输入net 池化核大小[3,3] stride=2(进行两倍下采样)
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
                """
                ===========
                上面 conv+conv+maxpool 算是一个基本单元,后面基本就是使用这样的基本单元进行堆叠
                ==========
                """
                # 每次经过pooling之后 卷积层channel的数量应该翻倍
                net = slim.conv2d(net, 64, [3, 3], scope='conv3')
                net = slim.conv2d(net, 64, [3, 3], scope='conv4')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool2')
                net = slim.conv2d(net, 128, [3, 3], scope='conv5')
                net = slim.conv2d(net, 128, [3, 3], scope='conv6')
                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool3')
                # 经过8倍下采样之后 在加入一个卷积层
                net = slim.conv2d(net, 256, [3, 3], scope='conv7')
                """
                特征图维度 (n,h,w,c)
                对 h,w 进行均值的求解 得到 (n,1,1,c) 因为 reduce_mean 就是降维求平均
                """
                net = tf.reduce_mean(net, axis=[1, 2])  # nhwc--n11c
                """
                slim.flatten 将输入扁平化但保留batch_size,假设第一维是batch。
                将 (n,1,1,c) 转为 (n,c)
                """
                net = slim.flatten(net)
                # 全连接层 这里还不着急分类 (1,1024)可以作为从图像中提取出来的特征
                net = slim.fully_connected(net, 1024)
                """
                全连接层的参数太多 要加入dropout层 来进行正则化
                keep_prob 概率值 定义了我们对当前神经元选择的概率
                在训练和测试的时候,这个概率要取不同的值 训练的时候要取<1 测试的时候要=1
                """
                slim.dropout(net, keep_prob)
                # 输出为10 对应了10个分类类别
                net = slim.fully_connected(net, 10)
        return net  # 10 dim vec
    
    
    def loss(logits, label):
        """
        loss使用交叉熵损失函数
        param
        logits: 预测出来的概率分布值
        label: 实际的label
    
        return 分类的loss
        """
        # 对label进行one-hot编码 定义one-hot长度为10
        one_hot_label = slim.one_hot_encoding(label, 10)
        # 使用交叉熵损失函数 并传入 预测值 和 one-hot label
        slim.losses.softmax_cross_entropy(logits, one_hot_label)
    
        """
        在前面 slim.arg_scope 中定义了weights_regularizer=slim.l2_regularizer(0.0001)
        通过 tf.GraphKeys.REGULARIZATION_LOSSES 可以拿到这些loss正则化的集合
        """
        reg_set = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
        # 将这些正则化loss进行相加 计算出总体的l2_loss
        l2_loss = tf.add_n(reg_set)
        # 将 l2_loss添加到loss中
        slim.losses.add_loss(l2_loss)
    
        totalloss = slim.losses.get_total_loss()
        # 这里为了后面做日志,这里把l2_loss也传出
        return totalloss, l2_loss
    
    
    def func_optimal(batchsize, loss_val):
        """
        定义优化器
        """
        global_step = tf.Variable(0, trainable=False)
        # 通过指数衰减的形式来定义学习率
        lr = tf.train.exponential_decay(0.01,
                                        global_step,
                                        decay_steps=50000 // batchsize,
                                        decay_rate=0.95,
                                        staircase=False)
    
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    
        with tf.control_dependencies(update_ops):
            op = tf.train.AdamOptimizer(lr).minimize(loss_val, global_step)
    
        """
        global_step 可以得到当前的迭代次数
        op sess.run()执行op可以完成对网络参数的调节
        lr 返回 便于log信息的记录
        """
        return global_step, op, lr
    
    
    def train():
        batchsize = 64
        floder_log = 'logdirs'
        floder_model = 'model'
    
        if not os.path.exists(floder_log):
            os.mkdir(floder_log)
    
        if not os.path.exists(floder_model):
            os.mkdir(floder_model)
    
        # 存放train和test的日志信息
        tr_summary = set()
        te_summary = set()
    
        # data
        tr_im, tr_label = readcifar10.read(batchsize, 0, 1)
        te_im, te_label = readcifar10.read(batchsize, 1, 0)
    
        # net
        """
        tf.placeholder 此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值
        训练数据 [None,32,32,3] None是考虑到batch_size是可以变化的
        """
        input_data = tf.placeholder(tf.float32, shape=[None, 32, 32, 3],
                                    name='input_data')
    
        input_label = tf.placeholder(tf.int64, shape=[None],
                                     name='input_label')
        """
        keep_prob dropout的概率值
        训练过程 keep_prob 也就是dropout的概率值 为0.5 或小于1的值
        """
        keep_prob = tf.placeholder(tf.float32, shape=None,
                                   name='keep_prob')
    
        is_training = tf.placeholder(tf.bool, shape=None,
                                     name='is_training')
    
        logits = model(
            input_data, keep_prob=keep_prob, is_training=is_training)
    
        # loss
        """
        损失函数 传入预测的结果和真实的标签
        """
        total_loss, l2_loss = loss(logits, input_label)
    
        # 记录loss
        tr_summary.add(tf.summary.scalar('train total loss', total_loss))
        tr_summary.add(tf.summary.scalar('test l2_loss', l2_loss))
    
        te_summary.add(tf.summary.scalar('train total loss', total_loss))
        te_summary.add(tf.summary.scalar('test l2_loss', l2_loss))
    
        # accurancy
        """
        tf.argmax 获取最大值的索引
        [n,10] axis=1 计算的是10维这个维度最大值对应的索引值
        """
        pred_max = tf.argmax(logits, 1)
        # 判断最大值索引和 label是否相等
        correct = tf.equal(pred_max, input_label)
        accurancy = tf.reduce_mean(tf.cast(correct, tf.float32))
    
        tr_summary.add(tf.summary.scalar('train accurancy', accurancy))
        te_summary.add(tf.summary.scalar('test accurancy', accurancy))
    
        # op
        global_step, op, lr = func_optimal(batchsize, total_loss)
    
        tr_summary.add(tf.summary.scalar('train lr', lr))
        te_summary.add(tf.summary.scalar('test lr', lr))
    
        # 图片数据是归一化过的 这里要处理回来
        tr_summary.add(tf.summary.image('train image', input_data * 128 + 128))
        te_summary.add(tf.summary.image('test image', input_data * 128 + 128))
        with tf.Session() as sess:
            # 初始化参数 包括了局部变量和全局变量
            sess.run(tf.group(tf.global_variables_initializer(),
                              tf.local_variables_initializer()))
            # 启动文件队列写入的线程
            tf.train.start_queue_runners(sess=sess,
                                         coord=tf.train.Coordinator())
    
            saver = tf.train.Saver(tf.global_variables(), max_to_keep=5)
            ckpt = tf.train.latest_checkpoint(floder_model)
    
            if ckpt:
                saver.restore(sess, ckpt)
            epoch_val = 10
    
            # 将日志进行合并
            tr_summary_op = tf.summary.merge(list(tr_summary))
            te_summary_op = tf.summary.merge(list(te_summary))
    
            summary_writer = tf.summary.FileWriter(floder_log, sess.graph)
    
            # 读取文件队列中的数据,完成对网络的训练
            for i in tqdm(range(50000 * epoch_val)):  # 这里这个数量我还是没有理解
                train_im_batch, train_label_batch = 
                    sess.run([tr_im, tr_label])
    
                feed_dict = {
                    input_data: train_im_batch,
                    input_label: train_label_batch,
                    keep_prob: 0.8,
                    is_training: True
                }
    
                # 注意 这里得到这这些值 都是一个batch_size的
                _, global_step_val, 
                    lr_val, 
                    total_loss_val, 
                    accurancy_val, tr_summary_str = sess.run([op,
                                                              global_step,
                                                              lr,
                                                              total_loss,
                                                              accurancy, tr_summary_op],
                                                             feed_dict=feed_dict)
    
                summary_writer.add_summary(tr_summary_str, global_step_val)
    
                # 每隔一百次打印一次
                if i % 100 == 0:
                    print("
    [trian]:{},{},{},{}".format(global_step_val,
                                                         lr_val,
                                                         total_loss_val,
                                                         accurancy_val))
    
                # 隔一段时间进行测试
                if i % (50000 // batchsize) == 0:
                    test_loss = 0
                    test_acc = 0
                    for ii in range(10000//batchsize):
                        test_im_batch, test_label_batch = 
                            sess.run([te_im, te_label])
                        feed_dict = {
                            input_data: test_im_batch,
                            input_label: test_label_batch,
                            keep_prob: 1.0,
                            is_training: False
                        }
    
                        total_loss_val, global_step_val, 
                        accurancy_val, te_summary_str = sess.run([total_loss,global_step,
                                                  accurancy, te_summary_op],
                                                 feed_dict=feed_dict)
    
                        summary_writer.add_summary(te_summary_str, global_step_val)
    
                        test_loss += total_loss_val
                        test_acc += accurancy_val
    
                    print('[test]:', test_loss * batchsize / 10000,
                          test_acc * batchsize / 10000)
                
                if i % 1000 == 0:
                    saver.save(sess, "{}/model.ckpt{}".format(floder_model, str(global_step_val)))
    
    if __name__ == '__main__':
        train()
    
    

    网络结构优化—resnet模型

    上面就是一个完整的网络结构了,如果要对模型进行优化,通常需要修改哪几个地方?

    • 一个就是修改网络结构,这里也称之为主干网络结构,上面是非常简单的串联的网络结构,这里可以使用resnet来这种跳连的结构来进行替代。

    • 还有就是可以修改学习率参数,以及学习率衰减的策略,针对不同的策略,观察对网络性能的影响。

    • 加入更多数据增强的方法,采用不同的优化器来进行模型的训练。

    resnet.py

    import tensorflow as tf
    slim = tf.contrib.slim
    
    def resnet_blockneck(net, numout, down, stride, is_training):
        """
        resnet的基本单元
        params
        net: 输入的特征图
        numout: 输出channel的数量
        down:在resnet中1*1卷积核下采样的倍率
        stride:下采样步长
        is_training: batchnorm的参数
        """
        batch_norm_params = {
        'is_training': is_training,
        'decay': 0.997,
        'epsilon': 1e-5,
        'scale': True,
        'updates_collections': tf.GraphKeys.UPDATE_OPS,
        }
        # 下面定义了卷积层的作用域
        with slim.arg_scope(
                    [slim.conv2d],
                    weights_regularizer=slim.l2_regularizer(0.0001),
                    weights_initializer=slim.variance_scaling_initializer(),
                    activation_fn=tf.nn.relu,
                    normalizer_fn=slim.batch_norm,
                    normalizer_params=batch_norm_params):
            with slim.arg_scope([slim.batch_norm], **batch_norm_params):
                with slim.arg_scope([slim.conv2d, slim.max_pool2d], padding='SAME') as arg_sc:
                    """
                    对于resnet,首先要将输入的特征图进行备份
                    """
                    shortcut = net
                    """
                    并且对备份过之后的特征图需要进行卷积和池化的判断
                    主要就是判断channel的数量是否相等 如果不相等就要使用1*1的卷积核进行卷积
    
                    如果是stride=2那么就进行下采样 保证特征图的大小也是一样的
                    """
                    if numout != net.get_shape().as_list()[-1]:
                        shortcut = slim.conv2d(net, numout, [1, 1])
    
                    if stride != 1:
                        shortcut = slim.max_pool2d(shortcut, [3, 3],
                                                   stride=stride)
                    """
                    先是使用1*1卷积核对channel数量进行下降
                    才经过3*3的卷积
                    最后通过1*1的卷积还原到想要输出的channel的数量
                    """
                    net = slim.conv2d(net, numout // down, [1, 1])
                    net = slim.conv2d(net, numout // down, [3, 3])
                    net = slim.conv2d(net, numout, [1, 1])
    
                    if stride != 1:
                        net = slim.max_pool2d(net, [3, 3], stride=stride)
    
                    # 跳连结构
                    net = net + shortcut
    
                    return net
    
    
    def model_resnet(net, keep_prob=0.5, is_training = True):
        with slim.arg_scope([slim.conv2d, slim.max_pool2d], padding='SAME') as arg_sc:
            """
            输入图像的时候,还是会使用标准卷积
            """
            net = slim.conv2d(net, 64, [3, 3], activation_fn=tf.nn.relu)
            net = slim.conv2d(net, 64, [3, 3], activation_fn=tf.nn.relu)
            """
            在网络结构中 对resnet基本单元进行了堆叠
            每次经过下采样 会对channel进行进行翻倍处理
    
            这里就是把之前的卷积单元替换成了resnet单元 其他都是一样的
            """
            net = resnet_blockneck(net, 128, 4, 2, is_training)
            net = resnet_blockneck(net, 128, 4, 1, is_training)
            net = resnet_blockneck(net, 256, 4, 2, is_training)
            net = resnet_blockneck(net, 256, 4, 1, is_training)
            net = resnet_blockneck(net, 512, 4, 2, is_training)
            net = resnet_blockneck(net, 512, 4, 1, is_training)
    
            net = tf.reduce_mean(net, [1, 2])
            net = slim.flatten(net)
    
            net = slim.fully_connected(net, 1024, activation_fn=tf.nn.relu, scope='fc1')
            net = slim.dropout(net, keep_prob, scope='dropout1')
            net = slim.fully_connected(net, 10, activation_fn=None, scope='fc2')
    
        return net
    
    

    train_resnet.py

    import tensorflow as tf
    import readcifar10
    import os
    import resnet
    from tqdm import tqdm
    
    slim = tf.contrib.slim
    
    def loss(logits, label):
    
        one_hot_label = slim.one_hot_encoding(label, 10)
        slim.losses.softmax_cross_entropy(logits, one_hot_label)
    
        reg_set = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
        l2_loss = tf.add_n(reg_set)
        slim.losses.add_loss(l2_loss)
    
        totalloss = slim.losses.get_total_loss()
    
        return totalloss, l2_loss
    
    def func_optimal(batchsize, loss_val):
        global_step = tf.Variable(0, trainable=False)
        lr = tf.train.exponential_decay(0.01,
                                        global_step,
                                        decay_steps= 50000// batchsize,
                                        decay_rate= 0.95,
                                        staircase=False)
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    
        with tf.control_dependencies(update_ops):
            op = tf.train.AdamOptimizer(lr).minimize(loss_val, global_step)
        return global_step, op, lr
    
    def train():
        batchsize = 64
        floder_log = 'logdirs-resnet'
        floder_model = 'model-resnet'
    
        if not os.path.exists(floder_log):
            os.mkdir(floder_log)
    
        if not os.path.exists(floder_model):
            os.mkdir(floder_model)
    
        tr_summary = set()
        te_summary = set()
    
        ##data
        tr_im, tr_label = readcifar10.read(batchsize, 0, 1)
        te_im, te_label = readcifar10.read(batchsize, 1, 0)
    
        ##net
        input_data = tf.placeholder(tf.float32, shape=[None, 32, 32, 3],
                                    name='input_data')
    
        input_label = tf.placeholder(tf.int64, shape=[None],
                                    name='input_label')
        keep_prob = tf.placeholder(tf.float32, shape=None,
                                    name='keep_prob')
    
        is_training = tf.placeholder(tf.bool, shape=None,
                                   name='is_training')
        logits = resnet.model_resnet(input_data, keep_prob=keep_prob, is_training=is_training)
    
        ##loss
    
        total_loss, l2_loss = loss(logits, input_label)
    
        tr_summary.add(tf.summary.scalar('train total loss', total_loss))
        tr_summary.add(tf.summary.scalar('test l2_loss', l2_loss))
    
        te_summary.add(tf.summary.scalar('train total loss', total_loss))
        te_summary.add(tf.summary.scalar('test l2_loss', l2_loss))
    
        ##accurancy
        pred_max  = tf.argmax(logits, 1)
        correct = tf.equal(pred_max, input_label)
        accurancy = tf.reduce_mean(tf.cast(correct, tf.float32))
        tr_summary.add(tf.summary.scalar('train accurancy', accurancy))
        te_summary.add(tf.summary.scalar('test accurancy', accurancy))
        ##op
        global_step, op, lr = func_optimal(batchsize, total_loss)
        tr_summary.add(tf.summary.scalar('train lr', lr))
        te_summary.add(tf.summary.scalar('test lr', lr))
    
        tr_summary.add(tf.summary.image('train image', input_data * 128 + 128))
        te_summary.add(tf.summary.image('test image', input_data * 128 + 128))
    
        with tf.Session() as sess:
            sess.run(tf.group(tf.global_variables_initializer(),
                              tf.local_variables_initializer()))
    
            tf.train.start_queue_runners(sess=sess,
                                         coord=tf.train.Coordinator())
    
            saver = tf.train.Saver(tf.global_variables(), max_to_keep=5)
    
            ckpt = tf.train.latest_checkpoint(floder_model)
    
            if ckpt:
                saver.restore(sess, ckpt)
    
            epoch_val = 100
    
            tr_summary_op = tf.summary.merge(list(tr_summary))
            te_summary_op = tf.summary.merge(list(te_summary))
    
            summary_writer = tf.summary.FileWriter(floder_log, sess.graph)
    
            for i in tqdm(range(50000 * epoch_val)):
                train_im_batch, train_label_batch = 
                    sess.run([tr_im, tr_label])
                feed_dict = {
                    input_data:train_im_batch,
                    input_label:train_label_batch,
                    keep_prob:0.8,
                    is_training:True
                }
    
                _, global_step_val, 
                lr_val, 
                total_loss_val, 
                accurancy_val, tr_summary_str = sess.run([op,
                                          global_step,
                                          lr,
                                          total_loss,
                                          accurancy, tr_summary_op],
                         feed_dict=feed_dict)
    
                summary_writer.add_summary(tr_summary_str, global_step_val)
    
                if i % 100 == 0:
                    print("{},{},{},{}".format(global_step_val,
                                               lr_val, total_loss_val,
                                               accurancy_val))
    
                if i % (50000 // batchsize) == 0:
                    test_loss = 0
                    test_acc = 0
                    for ii in range(10000//batchsize):
                        test_im_batch, test_label_batch = 
                            sess.run([te_im, te_label])
                        feed_dict = {
                            input_data: test_im_batch,
                            input_label: test_label_batch,
                            keep_prob: 1.0,
                            is_training: False
                        }
    
                        total_loss_val, global_step_val, 
                        accurancy_val, te_summary_str = sess.run([total_loss,global_step,
                                                  accurancy, te_summary_op],
                                                 feed_dict=feed_dict)
    
                        summary_writer.add_summary(te_summary_str, global_step_val)
    
                        test_loss += total_loss_val
                        test_acc += accurancy_val
    
                    print('test:', test_loss * batchsize / 10000,
                          test_acc* batchsize / 10000)
    
                if i % 1000 == 0:
                    saver.save(sess, "{}/model.ckpt{}".format(floder_model, str(global_step_val)))
        return
    
    if __name__ == '__main__':
        train()
    

    TF官方版本训练Cifar10分类任务

    上面所有的步骤,在官方给出的源码中,其实都已经实现了!

    要避免重复造轮子,可以对找官方给出的参考代码进行修改。

    https://github.com/tensorflow/models/tree/master/research/slim
    

    好好读读人家的README,会有巨大的收获!

    小结

    TF训练框架搭建:

    *   Data
    首先是数据的读取和数据的打包。
    
    *   Net
    网络的搭建,这里采用slim来搭建网络结构,因为slim是对tf更加高层的封装,可以写更加简洁的代码。
    
    *   Loss
    Loss本身就是网络的一部分,这个会采用softmaxLoss来进行Loss定义。
    另外还会定义正则化的Loss。
    
    *   Summary
    Summary完成了训练过程中日志的记录。
    
    *   Session
    Session完成了构造出网络结构后,如果对计算图中的结点进行计算,会通过Session在后端完成整个网络的BP。并通过Feed给网络数据,Fetch获得输出的张量。
    
    

    有哪些优化模型效果的思路?

    训练代码,也就是baseline版本,要如何进行优化呢?上面提供了几个参考思路。
    
    *   更多的数据增强策略,比如:mixup等=
    通过数据增强的策略,来丰富样本量,进而提高模型对更多样本的泛化能力。
    
    *   更好的主干网络,比如:SENet等
    有VGG,ResNet这样的主干网络,当然,SENet比ResNet的性能更好,可以实验哪个主干网络实验出来的效果更好。
    
    *   更好的标签策略,比如:Soft-label策略
    cifar10的标签就是从0到9十个标签,这里可以采用soft-label的策略,比如有些物体既像飞机又像汽车,那么就可以采用软的策略,来对标签进行预处理。
    
    *   更好的loss设计,比如:采用分类+回归smooth-l1 loss等
    另外可以采用更好的loss,这里采用交叉熵损失来完成图像分类的任务,另外大家也可以考虑结合回归的loss,来进行一个多个loss的约束。
    
    *   不同的优化器、参数初始化方法等
    
  • 相关阅读:
    uniapp数据循环带参数拼接方法
    UniApp页面跳转
    layui表单提交时关闭默认刷新页面
    js计时器
    Jquery 鼠标移入移出事件
    jquery常用ajax请求
    易宝网上支付接口的实现
    不使用缓存和不同缓存下程序的效率测试
    Mysql常见指令--常用的命令
    PHP冒泡与快速排序法
  • 原文地址:https://www.cnblogs.com/Rowry/p/15117927.html
Copyright © 2011-2022 走看看