[基于tensorflow的人脸检测] 基于神经网络的人脸检测7——神经网络的训练

1.神经网络训练的要素

2.tensorflow实现神经网络的训练框架搭建

3.训练好的模型分享

正文:

1.神经网络训练的要素

要训练一个神经网络,使得训练好的神经网络可以对输入进行预测,需要指定各种参数指标。争对不同的问题,所选择的参数指标是不同的,这里把要素分为下面几个部分。

  • 激活函数
  • 损失函数
  • 优化算法
  • 学习率
  • 防止过拟合
  • 模型保存和参数可视化
  1. 激活函数

    在神经网络的搭建的时候,在全连接层和卷积层的最后输出经过一个激活函数。激活函数分为线性激活函数和非线性激活函数,常用的是非线性激活函数。非线性激活函数的作用是去线性化,基于线性模型在解决问题上有很多不足,所以常使用激活函数实现去线性化。最常用的激活函数是下面几种。
    在这里插入图片描述

    在这三种激活函数中,最常用的是Relu激活函数,因为其表达式简单,收敛快。这三种函数在tensorflow中都有很好的支持,可以都尝试一遍,比较收敛速度。

    import tensorflow as tf
    def conv(input_, input_deep, output_deep,ksize, stride,name):
        """卷积层"""
        with tf.compat.v1.variable_scope(name):
            conv_weights = tf.compat.v1.get_variable( #卷积层权重
                'weight', [ksize, ksize, input_deep, output_deep],
                initializer = tf.truncated_normal_initializer(stddev=0.1))
            
            conv_biases = tf.compat.v1.get_variable( #卷积层偏置
                'biases', [output_deep], initializer = tf.constant_initializer(0.0))
             
            conv = tf.nn.conv2d( #卷积
                input_, conv_weights, strides=[1,stride,stride,1], padding='SAME')
            
            relu = tf.nn.relu(tf.nn.bias_add(conv, conv_biases))  #relu激活函数
    		#tanh = tf.nn.tanh(tf.nn.bias_add(conv, conv_biases)) #tanh激活函数
    		#sigmoid = tf.nn.sigmoid(tf.nn.bias_add(conv, conv_biases)) #sigmoid激活函数
            
        return tanh
    
  2. 损失函数

    损失函数是衡量神经网络输出的预测值与真实值之间的差距,损失函数越小说明预测的结果越接近真实结果。训练神经网络的本质就是优化网络的参数,从而使损失值收敛。损失值是否收敛也可以反映出网络是否在不断的训练。

    在分类问题上,常把交叉熵函数作为损失函数,在回归问题上,常把均方误差作为损失函数。下面是tensorflow支持的一些交叉熵函数。

    import tensorflow as tf
    #y为神经网络的输出 y_标签,正确答案
    loss1 = tf.nn.softmax_cross_entropy_with_logits(logits=y, labels=y_)
    loss2 = tf.nn.sigmoid_cross_entropy_with_logits(logits=y, labels=y_)
    loss3 = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=y_)
    

    这四种损失函数中,由于内部已经进行了softmax优化,所有输入之前不用进行softmax概率归一化操作。另外,softmax_cross_entropy_with_logits和sparse_softmax_cross_entropy_with_logits适用于一张图像中只含有一个类别,而sigmoid_cross_entropy_with_logits可以含有多个类别。对于输入标签的形式,sparse_softmax_cross_entropy_with_logits要求标签的维度为输出分类的一个索引,在人脸分类器中,是一个非0即1的值。而softmax_cross_entropy_with_logits要求标签的维度为输出分类的独热编码(one-hot编码)

    假设标签标注为一张人脸,则各自的形式如下。

    sparse_softmax_cross_entropy_with_logits:1

    softmax_cross_entropy_with_logits:[0,1]

    假设标签标注为一张非人脸,则各自的形式如下。

    sparse_softmax_cross_entropy_with_logits:0

    softmax_cross_entropy_with_logits:[1,0]

  3. 优化算法

    优化算法是神经网络优化网络参数算法(也可以认为是优化损失函数),tensorflow提供了多种优化算法器,下面是常见的几种优化器。关于优化器的原理,这里就不再累赘。

    #GD 梯度下降算法---learning_rate为学习率 loss为损失函数  
    with tf.name_scope('train_step'):
    	train_step = tf.compat.v1.train.GradientDescentOptimizer(learning_rate).minimize(loss) 
        
    #adam算法---learning_rate为学习率 loss为损失函数  
    with tf.name_scope('train_step'):
    	train_step = tf.compat.v1.train.AdamOptimizer(learning_rate).minimize(loss) 
        
    #动量算法---learning_rate为学习率 momentum为动量 loss为损失函数 
    with tf.name_scope('train_step'):
    	train_step = tf.compat.v1.train.MomentumOptimizer(learning_rate,momentum).minimize(loss) 
    
  4. 学习率

    学习率可以理解为在优化损失函数时,优化的移动步长。学习率的设置有两种方法,一种是在训练之前就指定一个数值,此时学习率在训练过程中是个常数,不会改变,也就是优化移动步长不变。

    tensorflow中提供了学习率的另一种设置方法——指数衰减法,学习率在训练过程呈指数衰减变化,在训练初期学习率大,移动步幅大,随着学习率的减小,移动步长也变小,指数衰减法的设置方法可以在优化初期快速使损失函数收敛,加快收敛速度。

    learning_rate = tf.compat.v1.train.exponential_decay(
        LEARNINNG_RATE_BASE, #基础学习率
        global_step,         #训练次数
        step_num, #过完所有数据需要迭代的次数
        LEARNING_RATE_DECAY #指数衰减率
    )
    

    在训练网络函数时,往往需要调节参数使得损失值收敛。一般神经网络调节的就是学习率这个参数,调节的顺序一般如下:

    0.1→0.01→0.001→0.0001→0.00001→0.000001→…

  5. 防止过拟合

    过拟合现像简单来讲就是模型过分学习到训练数据的特征,包括噪声特征,使得训练出来的模型只能对训练数据有好的性能,而不能对未知数据有好的预测。解决过拟合现像有两种方法。

    第一种方法是正则化,在优化损失函数时,加上描述模型复杂性的参数,使得优化过程不仅仅只优化损失函数,换句话说,减小了网络参数所占的权重,从而避免过拟合。正则化一般在全连接层进行,下面是代码实现,fc函数是根据博文5中的fc函数改进过来的,加入了正则化。

    def fc(input_, input_deep, out_deep, name,regularizer): #全连接层
        """参数->输入、输入深度、输出深度、该层的变量名、正则化器"""
        with tf.compat.v1.variable_scope(name):
            weight = tf.Variable( #权重
                tf.random.truncated_normal([input_deep, out_deep], stddev=0.05),
                name="weights")
            bias = tf.Variable( #偏置
                tf.constant(0.1, dtype=tf.float32, shape=[out_deep]), 
                name="bias")
            net = tf.nn.tanh(tf.add(tf.matmul(input_, weight), bias)) #乘加 激活
            if regularizer != None:
                tf.compat.v1.add_to_collection('losses', regularizer(weight))
            net = tf.add(tf.matmul(input_, weight), bias)
        return net
    #网络函数变为 alexnet(input_,regularizer)
    ###引用
    #使用正则化
    REGULARATION_RATE = 0.00001 #正则化权重
    regularizer = tf.contrib.layers.l2_regularizer(REGULARATION_RATE)
    y = alexnet(x,regularizer)
    #不使用正则化
    y = alexnet(x,None)
    
    

    第二种方法是使用dropout,在优化过程中随机使网络当中神经元不输出,从而避免过拟合。这个操作也通常在全连接层使用,下面是代码表示。

    #0.5为指定的随机概率(随机使神经元不输出)
    fc1 = tf.nn.dropout(fc1, 0.5)
    fc2 = tf.nn.dropout(fc2, 0.5)
    fc3 = tf.nn.dropout(fc3, 0.5)
    
  6. 模型保存和参数可视化

    为了能够使用训练好的模型,所以在训练好模型以后必须对模型进行保存,tensorflow的模型一般会存为.ckpt文件。对于参数可视化有两种方法实现,第一种就是在训练过程中保存训练次数和对应的参数值,在训练结束后利用matplotlib.pyplot模块进行可视化,另一种方法是利用tensorflow里的可视化工具tensorboard。

    对于tensorboard,是根据命名空间进行可视化。首先命名管理所有需要可视化指标(添加到summary),然后自动管理或者手动管理所有summary,最后生成一个写入器写入summary。

    import tensorflow as tf 
    #模型保存
    #初始化持久化类
    saver = tf.compat.v1.train.Saver() #初始化保存器
    #MODEL_SAVE_PATH:模型保存路径 MODEL_NMAE:保存的模型名称 global_step:训练次数
    saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NMAE), 
                                global_step=global_step)
    
    #参数可视化
    #以损失值为例写入tensorboard
    with tf.name_scope('loss_function'):  #命名管理
            cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
                logits=y, labels=tf.argmax(y_,1))
    
            cross_entropy_mean = tf.reduce_mean(cross_entropy)
            
        tf.summary.scalar('loss_value', cross_entropy_mean)
        
    merged = tf.summary.merge_all() #自动管理
    #NET_SAVE_PATH:可视化文件保存地址
    writer = tf.compat.v1.summary.FileWriter(NET_SAVE_PATH, tf.compat.v1.get_default_graph())
    #写入器写入 i为迭代次数
    writer.add_summary(summary, i) 
    
    writer.close()
    
2.tensorflow实现神经网络的训练框架搭建

下面是根据1中各要素搭建的训练框架,使用的神经网络是在博文5搭建的Alexnet。为了加快训练,batch只取20,输入图片大小取64 *64 *3。

# -*- coding: utf-8 -*-
#导入库
import os 
import tensorflow as tf
from data_save_load import get_image_arrary,get_batch
from alex import alexnet
from datetime import datetime

#参数设置
BATCH_SIZE = 20 #batch大小 每次训练batch个数据
LEARNINNG_RATE_BASE = 0.1 #基础学习率
LEARNING_BATE_DECAY = 0.98 #学习率衰减率
REGULARATION_RATE = 0.00001
TRANING_STEPS = 5001 #训练次数

#地址设置
MODEL_SAVE_PATH = r"C:/Users/user/Desktop/alex_model/" #模型保存路径
MODEL_NMAE = 'model.ckpt'  #模型名字
NET_SAVE_PATH = r"C:\Users\user\Desktop\alex_board"

IMAGE_SIZE = 64 #输入图片大小
NUM_CHANNELS = 3 #输入图片维度 
OUTPUT_NODE = 2 #神经网络输出维度=标签维度('[0,1] [1,0]')
  
def train():
    """训练模型"""
    #获取图片和标签列表
    images_array, labels_array = get_image_arrary( #以数组的形式加载数据
        IMAGE_SIZE,
        IMAGE_SIZE,
        NUM_CHANNELS,
        'train')
    
    images_val, labels_val = get_image_arrary(
        IMAGE_SIZE,
        IMAGE_SIZE,
        NUM_CHANNELS,
        'validation')

    with tf.name_scope('input'):
        #预定义输入x和标签y_
        x = tf.compat.v1.placeholder(tf.float32,[
            BATCH_SIZE, #batach大小
            IMAGE_SIZE, #图片大小
            IMAGE_SIZE,
            NUM_CHANNELS], 
            name='x-input') 
        
        y_ = tf.compat.v1.placeholder(tf.float32,
                                      [None, OUTPUT_NODE],
                                      name='y-input')
        
    regularizer = tf.contrib.layers.l2_regularizer(REGULARATION_RATE)

    y = alexnet(x,None)  #   前向传播结果 
    
    global_step = tf.Variable(0, trainable=False) #训练次数 属于非优化对象
       
    #生成损失函数
    #利用函数生成交叉熵 
    with tf.name_scope('loss_function'): #命名管理
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
            logits=y, labels=tf.argmax(y_,1)) #argmax对最大下标
        # 计算交叉熵平均值
        cross_entropy_mean = tf.reduce_mean(cross_entropy)
    tf.summary.scalar('loss_value', cross_entropy_mean)
    
    #指数衰减法设置学习率
    learning_rate = tf.compat.v1.train.exponential_decay(
        LEARNINNG_RATE_BASE,
        global_step,
        1000,
        LEARNING_BATE_DECAY
        )
    tf.summary.scalar("learningRate",learning_rate)
    
    #优化损失函数(反向优化算法)
    with tf.name_scope('train_step'):
        train_step = tf.compat.v1.train.GradientDescentOptimizer(learning_rate)\
            .minimize(cross_entropy_mean, global_step=global_step)
            
        #反向传播更新参数 
        with tf.control_dependencies([train_step]):
            train_op = tf.no_op(name='train')
       
    #计算正确率 比较输出结果和标签
    with tf.name_scope('accuracy'):
        correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1)) 
        #将布尔值转为实行再计算平均值 即正确率
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
    tf.summary.scalar('accuracy', accuracy)
    
    merged = tf.summary.merge_all() #整理所有日志
    #初保存器初始化
    saver = tf.compat.v1.train.Saver()
    
    with tf.compat.v1.Session() as sess:
        
        tf.compat.v1.global_variables_initializer().run() #参数初始化
        
        #写入器初始化
        writer = tf.compat.v1.summary.FileWriter(NET_SAVE_PATH, tf.compat.v1.get_default_graph()) 
        
        for i in range(TRANING_STEPS): #开始训练
            #获取batch个数据
            xs, ys = get_batch(
                BATCH_SIZE, 
                images_array, 
                labels_array, 
                IMAGE_SIZE,
                IMAGE_SIZE,
                NUM_CHANNELS
                )
            
            #可视化操作
            run_options=tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
            run_metadata=tf.RunMetadata()
            

            summary, _ , losee_value, step = sess.run( #返回的参数与[]内的相对应
                [merged,train_op, cross_entropy_mean, global_step],
                feed_dict ={x: xs, y_: ys}, #喂入数据
                options=run_options,run_metadata=run_metadata)
            
            #写入可视化信息            
            writer.add_run_metadata(run_metadata,'step%03d'%i)
            writer.add_summary(summary, i) 
            
            #获取batch个验证数据
            valxs, valys = get_batch(
                BATCH_SIZE, 
                images_val,
                labels_val, 
                IMAGE_SIZE,
                IMAGE_SIZE,
                NUM_CHANNELS
                )
            
            #正确率
            validate_acc = sess.run(accuracy,feed_dict ={x: valxs, y_: valys})

            #答应训练过程的参数变化
            if i % 1000 ==0:

                print("训练 %d 轮后的损失值为 %g" %(step, losee_value))
                #验证
                print("训练 %d 轮后的正确率为 %g" %(i,validate_acc))
                
                #保存模型
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NMAE), 
                            global_step=global_step)
                
        #训练完成 关闭写入器
        writer.close()
#主程序                                                     
def main(argv=None):
    tf.compat.v1.reset_default_graph() #先清空计算图
    strat_time = datetime.now()
    train()
    end_time = datetime.now() 
    use_time = end_time - strat_time
    print('训练所用时间' + str(use_time))

if __name__=='__main__':
    tf.compat.v1.app.run()  

注:

  1. get_image_arrary是博文6中数组形式加载数据,不同的是增加了标志训练和验证的数据加载,并且添加了归一化处理。

    data_save_load.py文件中含有get_image_arrary 、get_batch、uniform函数

    def uniform(image_array):
        """图像归一化——网上说可以加快收敛"""
        img_mean = np.mean(image_array)
        img_std = np.std(image_array)
        uniform_img = (image_array - img_mean) / img_std
        return uniform_img
    
    def get_image_arrary(image_h, image_w, image_c, flag):
        """输入参数:图片的三个维度(神经网络输入维度)"""
        #初始化列表
        images = []
        labels = []
        
        if flag == 'train':
            root_path = r"C:\Users\user\Desktop\wider_data" #图片根目录
            images_txt = 'train_images.txt' #图片地址文件
            labels_txt = 'train_labels.txt' #对应的标签文件
        elif flag == 'validation':
            root_path = r"C:\Users\user\Desktop\wider_val_data" #图片根目录
            images_txt = 'validation_images.txt' #图片地址文件
            labels_txt = 'validation_labels.txt' #对应的标签文件
    
    	...
        ...
        
        for i in range(sum_number):
            #图片
            ...
            image = cv2.resize(image, (image_h, image_w))
            image = uniform(image) #归一化处理
            images_array[i] = image
            ...
        return images_array, labels_array
    
  2. alexnet与博文5构造的相比增加了的dropout和正则化,但在使用过程中并没有用到正则化。

    def fc(input_, input_deep, out_deep, name,regularizer): #全连接层
        """参数->输入、输入深度、输出深度、该层的变量名"""
        with tf.compat.v1.variable_scope(name):
            weight = tf.Variable( #权重
                tf.random.truncated_normal([input_deep, out_deep], stddev=0.05),
                name="weights")
            bias = tf.Variable( #偏置
                tf.constant(0.1, dtype=tf.float32, shape=[out_deep]), 
                name="bias")
            net = tf.nn.tanh(tf.add(tf.matmul(input_, weight), bias)) #乘加 激活
            if regularizer != None:
                tf.compat.v1.add_to_collection('losses', regularizer(weight))
            net = tf.add(tf.matmul(input_, weight), bias)
        return net
        
    def alexnet(input_,regularizer):
        """alexnet网络结构"""
        
        ...
    
        fc1 = fc(reshaped, nodes, 4096, 'layer11_fc1',regularizer)
        fc1 = tf.nn.dropout(fc1, 0.5)
        fc2 = fc(fc1, 4096, 4096, 'layer12_fc2',regularizer)
        fc1 = tf.nn.dropout(fc1, 0.5)
        fc3 = fc(fc2, 4096, 2, 'layer13_fc3',regularizer)
        fc1 = tf.nn.dropout(fc1, 0.5)
        return fc3
    
  3. 通过不断的调节学习率,最后选定学习率为0.01,使用了0.5概率的dropout,不使用正则化的情况下,过程结果如下。
    在这里插入图片描述
    调节过程1:在训练过程中,尝试调节学习率,损失值一直为0.693147。这里修改了激活函数,将激活函数从relu修改为tanh,解决了这个问题。

    调节过程2:在训练过程中不断修改学习率(没有使用dropout和正则化),结果如下,最终选定学习率0.01。
    在这里插入图片描述

3.训练好的模型分享

下面是在tensroflow上训练10000轮后得到的模型。模型的输入图片大小为 227 *227 *3。
在这里插入图片描述

模型获取:
人脸检测模型
密码:f3vzk8

结语:
如果对你有帮助,就给我一个赞吧,如何有问题,可以在评论区进行讨论。

上一篇:[基于tensorflow的人脸检测] 基于神经网络的人脸检6——数据的存储与加载
下一篇:[基于tensorflow的人脸检测] 基于神经网络的人脸检测8——验证训练好的神经网络

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值