神经元函数以及优化方法
这是需要牢记的内容,这里包含了激活函数、卷积函数、池化函数、分类函数、优化方法和批标准化的写法和简略介绍。
a. 激活函数
神经网络的非线性因素,神经网络可以解决非线性问题的关键。tensorflow中包含处处平滑的激活函数 sigmoid、 tanh、 elu、 softplus 和 softsign,也包括连续但不是处处可微的激活函数 relu、 relu6、 crelu 和 relu_x,以及随机正则化函数 dropout,常用的激活函数有四种 sigmoid、 tanh、 relu 和 softplus。
tf.nn.relu()
tf.nn.sigmoid()
tf.nn.tanh()
tf.nn.elu()
tf.nn.bias_add()
tf.nn.crelu()
tf.nn.relu6()
tf.nn.softplus()
tf.nn.softsign()
tf.nn.dropout() # 防止过拟合,用来舍弃某些神经元
简单介绍:
- sigmoid适合作为输出层,求导简单。但是存在饱和去,很容易使得梯度消失。
- tanh具有软饱和性,以0为中心,收敛速度比sigmoid快。
-
relu目前最受欢迎的激活函数,softplus可以看成其平滑版本。 relu定义为 f(x)=max(x,0)。softplus 定义为 f(x)=log(1+exp(x))。优点是可以能够更很地收敛,并提供了神经网络的稀疏表达能力。但是,随着训练的进行,部分输入会落到硬饱和区,导致对应的权重无法更新,称为“神经元死亡”
a = tf.constant([[1.0, 2.0], [1.0, 2.0], [1.0, 2.0]])
sess = tf.Session()
print(sess.run(tf.sigmoid(a)))
#[[0.7310586 0.880797 ]
# [0.7310586 0.880797 ]
#[0.7310586 0.880797 ]]
with tf.Session() as sess:
b = tf.nn.relu(a)
print(sess.run(b))
- relu6是定义在 min(max(features, 0), 6)的
tf.nn.relu6(features, name=None),以及 crelu,也就是 tf.nn.crelu(features, name=None)。 - dropout函数:一个神经元将以概率 keep_prob 决定是否被抑制。如果被抑制,该神经元的输出就为 0;如果不被抑制,那么该神经元的输出值将被放大到原来的 1/keep_prob 倍。还有一个noise_shape参数,用于对dropout的方式进行约束。参考这个网址,说的很清楚
a = tf.constant([[-1.0, 2.0, 3.0, 4.0],[5,6,7,8]])
print(a.shape)
with tf.Session() as sess:
b = tf.nn.dropout(a, 0.5, noise_shape = [2,1])#表示行之间相互独立,同一行的列是不独立的,要么同时为0,要么同时缩放。
print(sess.run(b))
#(2, 4)
#[[-0. 0. 0. 0.]
#[10. 12. 14. 16.]]
b.卷积函数
是在一批图像上扫描的二维过滤器。下面是常见的卷积函数。
tf.nn.convolution(input, filter, padding, strides=None,
dilation_rate=None, name=None, data_format=None)
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None,
data_format= None, name=None)
tf.nn.depthwise_conv2d (input, filter, strides, padding, rate=None, name=None,
data_format=None)
tf.nn.separable_conv2d (input, depthwise_filter, pointwise_filter, strides, padding,
rate=None, name=None, data_format=None)
tf.nn.atrous_conv2d(value, filters, rate, padding, name=None)
tf.nn.conv2d_transpose(value, filter, output_shape, strides, padding='SAME',
data_format='NHWC', name=None)
tf.nn.conv1d(value, filters, stride, padding, use_cudnn_on_gpu=None,
data_format= None, name=None)
tf.nn.conv3d(input, filter, strides, padding, name=None)
tf.nn.conv3d_transpose(value, filter, output_shape, strides, padding='SAME', name=None)
c. 池化函数
池化函数一般跟在卷积函数的下一层。池化操作是利用一个矩阵窗口在张量上进行扫描,将每个矩阵窗口中的值通过取最大值或平均值来减少元素个数。每个池化操作的矩阵窗口大小是由 ksize 指定的,并且根据步长 strides决定移动步长。部分函数只能在GPU内执行,比如max_pool_with_argmax。
tf.nn.avg_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
tf.nn.max_pool_with_argmax(input, ksize, strides, padding, Targmax=None, name=None)
tf.nn.avg_pool3d(input, ksize, strides, padding, name=None)
tf.nn.max_pool3d(input, ksize, strides, padding, name=None)
tf.nn.fractional_avg_pool(value, pooling_ratio, pseudo_random=None, overlapping=None,deterministic=None, seed=None, seed2=None, name=None)
tf.nn.fractional_max_pool(value, pooling_ratio, pseudo_random=None, overlapping=None,deterministic=None, seed=None, seed2=None, name=None)
tf.nn.pool(input, window_shape, pooling_type, padding, dilation_rate=None, strides=None,name=None, data_format=None)
d.分类函数
tf.nn.sigmoid_cross_entropy_with_logits(logits, targets, name=None)
tf.nn.softmax(logits, dim=-1, name=None)
tf.nn.log_softmax(logits, dim=-1, name=None)
tf.nn.softmax_cross_entropy_with_logits(logits, labels, dim=-1, name=None)
tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels, name=None)
- tf.nn.sigmoid_cross_entropy_with_logits(logits, targets, name=None):
def sigmoid_cross_entropy_with_logits(logits, targets, name=None):
# 输入: logits:[batch_size, num_classes],targets:[batch_size, size].logits 用最后一层的输入即可
# 最后一层不需要进行 sigmoid 运算,此函数内部进行了 sigmoid 操作
# 输出: loss [batch_size, num_classes]
这个函数的输入要格外注意,如果采用此函数作为损失函数,在神经网络的最后一层不需要进行 sigmoid 运算。
- tf.nn.softmax(logits, dim=-1, name=None)计算 Softmax 激活,也就是 softmax = exp(logits) /reduce_sum(exp(logits), dim)。
- tf.nn.log_softmax(logits, dim=-1, name=None)计算 log softmax 激活,也就是 logsoftmax =logits - log(reduce_sum(exp(logits), dim))。
- tf.nn.softmax_cross_entropy_with_logits(_sentinel=None, labels=None, logits=None, dim=-1,name =None):
def softmax_cross_entropy_with_logits(logits, targets, dim=-1, name=None):
# 输入: logits and labels 均为[batch_size, num_classes]
# 输出: loss:[batch_size], 里面保存的是 batch 中每个样本的交叉熵
- tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels, name=None) :
def sparse_softmax_cross_entropy_with_logits(logits, labels, name=None):
# logits 是神经网络最后一层的结果
# 输入: logits: [batch_size, num_classes] labels: [batch_size],必须在[0, num_classes]
# 输出: loss [batch_size],里面保存是 batch 中每个样本的交叉熵
e.优化方法Optimizer
用于加速神经网络的训练。目前加速训练的优化方法基本基于梯度下降,只是细节上有差异。这里主要介绍八类优化器
BGD法和SGD法:class tf.train.GradientDescentOptimizer
批梯度下降法和随机梯度下降法。后者是将前者的batch拆分成更细小的batch,每次运行更新参数。前者对整个数据集的平均loss进行更新,耗时更长。后者存在的问题是容易陷入局部最优,需要手动调整学习率,尤其在训练时,我们常常想对常出现的特征更新速度很一些,而对不常出现的特征更新速度慢一些,而 SGD 在更新参数时对所有参数采用一样的学习率。-
Momentum 法和Nesterov Momentum :class tf.train.MomentumOptimizer,
这是为了解决学习率固定的问题而提出。标准的动量方式首先保留之前的更新向量,认为是初始速度,利用当前数据的梯度叠加上之前的结果,得到新的速度,并使用这个速度更新学习率。这样在下降初期,前后梯度方向一致时,能够加速学习;在下降的中后期,在局部最小值的附近来回震荡时,能够抑制震荡,加很收敛。
Nesterov Momentum 法:直接使用之前的累加速度向量更新参数,计算更新参数之后的梯度方向,然后用这个梯度值修正速度,然后用这个速度计算新的参数
Adagrad 法:class tf.train.AdagradOptimizer
从这个方法开始的优化器都是自动调节学习速率的。如果本次更新时梯度大,学习率就衰减得很一些;如果这次更新时梯度小,学习率衰减得就慢一些Adadelta 法:class tf.train.AdadeltaOptimizer
Adagrad 法仍然存在一些问题:其学习率单调递减,在训练的后期学习率非常小,并且需要手动设置一个全局的初始学习率。 Adadelta 法用一阶的方法,近似模拟二阶牛顿法,解决了这些问题。RMSprop 法:class tf.train.RMSPropOptimizer
与 Momentum 法类似,通过引入一个衰减系数,使每一回合都衰减一定比例。在实践中,对循环神经网络(RNN)效果很好Adam 法:class tf.train.AdamOptimizer
Adam 法根据损失函数针对每个参数的梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。
批标准化
批标准化(batch normalization, BN)是为了克服神经网络层数加深导致难以训练而诞生的。通过引入批标准化来规范化某些层或者所有层的输入,从而固定每层输入信号的均值与方差。
批标准化一般用在非线性映射(激活函数)之前,对 x=Wu+b 做规范化,使结果(输出信号各个维度)的均值为 0,方差为 1。让每一层的输入分布在线性区间,加大了梯度,让模型更加大胆的进行梯度下降。
优点如下:
- 加大探索的步长,加快收敛的速度;
- 更容易跳出局部最小值;
- 破坏原来的数据分布,一定程度上缓解过拟合。
在遇到神经网络收敛速度很慢或梯度爆炸(gradient explode)等无法训练的情况下,都可以尝试用批标准化来解决。
# 计算 Wx_plus_b 的均值和方差,其中 axes=[0]表示想要标准化的维度
fc_mean, fc_var = tf.nn.moments(Wx_plus_b, axes=[0], )
scale = tf.Variable(tf.ones([out_size]))
shift = tf.Variable(tf.zeros([out_size]))
epsilon = 0.001
Wx_plus_b = tf.nn.batch_normalization(Wx_plus_b, fc_mean, fc_var, shift,scale, epsilon)
# 也就是在做:
# Wx_plus_b = (Wx_plus_b - fc_mean) / tf.sqrt(fc_var + 0.001)
# Wx_plus_b = Wx_plus_b * scale + shift