正文:
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
-
损失函数
损失函数是衡量神经网络输出的预测值与真实值之间的差距,损失函数越小说明预测的结果越接近真实结果。训练神经网络的本质就是优化网络的参数,从而使损失值收敛。损失值是否收敛也可以反映出网络是否在不断的训练。
在分类问题上,常把交叉熵函数作为损失函数,在回归问题上,常把均方误差作为损失函数。下面是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]
-
优化算法
优化算法是神经网络优化网络参数算法(也可以认为是优化损失函数),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)
-
学习率
学习率可以理解为在优化损失函数时,优化的移动步长。学习率的设置有两种方法,一种是在训练之前就指定一个数值,此时学习率在训练过程中是个常数,不会改变,也就是优化移动步长不变。
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→…
-
防止过拟合
过拟合现像简单来讲就是模型过分学习到训练数据的特征,包括噪声特征,使得训练出来的模型只能对训练数据有好的性能,而不能对未知数据有好的预测。解决过拟合现像有两种方法。
第一种方法是正则化,在优化损失函数时,加上描述模型复杂性的参数,使得优化过程不仅仅只优化损失函数,换句话说,减小了网络参数所占的权重,从而避免过拟合。正则化一般在全连接层进行,下面是代码实现,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)
-
模型保存和参数可视化
为了能够使用训练好的模型,所以在训练好模型以后必须对模型进行保存,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()
注:
-
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
-
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
-
通过不断的调节学习率,最后选定学习率为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——验证训练好的神经网络