目录
1.神经网络复杂度
神经网络的复杂度,多用神经网络的层数和神经网络中待优化参数的个数表示。以下图为例说明
空间复杂度
神经网络的层数=隐藏层的层数+1个输出层
统计神经网络的层数时,只统计具有运算能力的层,输入层仅把数据传输过来,没有运算,不算到神经网络的层数中。输入层和输出层之间的所有层都叫做隐藏层。上图有2层神经网络
总参数=总w + 总b
(第一层)3×4+4 + (第二层)4×2+2 = 26
时间复杂度
神经网络中乘加运算的次数表示。有几条权重线,就有几次乘加运算
(第一层)3×4 + (第二层)4×2 = 20
2.学习率策略
参数更新公式如下,公式中的lr即为学习率,表示参数每次更新的幅度。
当学习率设置过小时,更新过慢,当学习率设置过大时,不收敛,那么学习率设置多少合适
指数衰减学习率
可以先用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定
可使用此公式实现指数衰减学习率,根据当前迭代次数,动态改变学习率的值。指数型学习率衰减法是最常用的衰减方法,在大量模型中都广泛使用.此公式中,绿色的文字为超参数,当前轮数一般为epoch或者是当前迭代的batch数global_step表示
TensorFlow API: tf.keras.optimizers.schedules.ExponentialDecay
分段常数衰减
TensorFlow API: tf.optimizers.schedules.PiecewiseConstantDecay
分段常数衰减可以让调试人员针对不同任务设置不同的学习率,进行精细调参,在任意步长后下降任意数值的learning rate,要求调试人员对模型和数据集有深刻认识,一般用的不多。
3.激活函数
激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。引入非线性激活函数,可使深层神经网络的表达能力更加强大
优秀的激活函数应满足:
- 非线性:激活函数非线性时,多层神经网络可逼近所有函数
- 可微性:优化器大多使用梯度下降来更新参数
- 单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数
- 近似恒等性:f(x)约等于x。当参数初始化为随机小值时,神经网络更稳定
简单看下凸函数,比如这就是一个凸函数的图像,像一个大碗一样
与刚才的图有些相反,这是非凸函数,因为它是非凸的并且有很多不同的局部最小值
激活函数输出值的范围:
激活函数输出为有限值时,基于梯度的优化方法更稳定
激活函数输出为无限值时,建议调小学习率
sigmoid
可以看到,sigmoid函数将输入值变换到0-1之间的值输出,若输入值是非常大的负数,则输出为0;若输入值是非常大的正数,则输出值为1,相当于对输入进行归一化。
现在sigmoid函数用的很少,主要的原因是,深层神经网络更新参数时,需要从输出层到输入层,逐层进行链式求导,而sigmoid函数的导数输出是0-0.25之间的小数,链式求导需要多层导数连续相乘,会出现多个0-0.25之间的小数连续相乘,结果将趋于0,产生梯度消失,使得参数无法继续更新
我们希望输入每层神经网络的特征是以0为均值的小数值,但是过sigmoid激活函数后的数据都是正数,会使收敛变慢。而且sigmoid函数存在幂运算,计算复杂度大,计算时间长
TensorFlow API: tf.math.sigmoid
优点:
- 输出映射在(0,1)之间,单调连续,输出范围有限,优化稳定,可用作输出层;
- 求导容易
缺点:
- 易造成梯度消失;
- 输出非0均值,收敛慢;
- 幂运算复杂,训练时间长。
sigmoid函数可应用在训练过程中。然而,当处理分类问题作为输出时,sigmoid却无能为力。简单地说,sigmoid函数只能处理两个类,不适用于多分类问题。而softmax可以有效解决这个问题,并且softmax函数大都运用在神经网路中的最后一层网络中,使得值得区间在(0,1)之间,而不是二分类的。
tanh
从函数图像看,tanh函数的输出为0均值了,但是依旧存在梯度消失和幂运算问题
TensorFlow API: tf.math.tanh
优点:
- 比sigmoid函数收敛速度更快。
- 相比sigmoid函数,其输出以0为中心
缺点:
- 易造成梯度消失;
- 幂运算复杂,训练时间长。
ReLU
relu函数非常符合好的激活函数应该具有近似恒等性这一要求,
TensorFlow API: tf.nn.relu
优点:
- 解决了梯度消失问题(在正区间);
- 只需判断输入是否大于0,计算速度快;
- 收敛速度远快于sigmoid和tanh,因为sigmoid和tanh涉及很多expensive的操作;
- 提供了神经网络的稀疏表达能力。
缺点:
- 输出非0均值,收敛慢;
- Dead ReLU问题:某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。即送入激活函数的特征是负数时,激活函数输出是0,反向传播得到的梯度是0,导致参数无法更新,造成神经元死亡
其实,导致神经元死亡的根本原因是送入神经元的负数特征过多导致的,我们可以改进随机初始化,避免过多的负数特征送入relu函数,可以通过设置更小的学习率,减少参数分布的巨大变化,避免训练中产生过多负数特征进入relu函数
Leaky ReLU
Leaky ReLU是为解决relu负区间为0,引起神经元死亡问题而设计的,Leaky ReLU的负区间引入了一个固定的斜率a,使得Leaky ReLU的负区间不再恒等于0
理论上来讲,Leaky ReLU有ReLU的所有优点,外加不会有Dead ReLU问题,但是在实际操作当中,并没有完全证明Leaky ReLU总是好于ReLU。实际使用中,大部分仍然选用relu
TensorFlow API: tf.nn.leaky_relu
建议
- 首选ReLU激活函数;
- 学习率设置较小值;
- 输入特征标准化,即让输入特征满足以0为均值,1为标准差的正态分布;
- 初始化问题:初始参数中心化,即让随机生成的参数满足以0为均值,以
为标准差的正态分布。
4.损失函数
损失函数:预测值(y)与已知答案(y_)的差距。神经网络的优化目标,就是找到某套参数,使得计算出来的结果y与已知答案y_无限接近,也即它们的差距loss值最小
神经网络模型的效果及优化的目标是通过损失函数来定义的。回归和分类是监督学习中的两个大
类。主流的loss有三种计算方法,均方误差、交叉熵和自定义。下面用一个预测酸奶日销量的例子,来理解损失函数
均方误差损失函数
均方误差(Mean Square Error)是回归问题最常用的损失函数。回归问题解决的是对具体数值的预测,比如房价预测、销量预测等。这些问题需要预测的不是一个事先定义好的类别,而是一个任意实数。均方误差定义如下:
TensorFlow API: tf.keras.losses.MSE
预测酸奶日销量y, x1、 x2是影响日销量的因素。
建模前,应预先采集的数据有:每日x1、 x2和销量y_(即已知答案,知道了销量,就可以建议产量了,这里假定,最佳的情况:产量=销量)
拟造数据集X,Y_: y_ = x1 + x2 噪声: -0.05 ~ +0.05 拟合可以预测销量的函数
构建一个一层的神经网络,将这套构建的数据集喂入其中
import tensorflow as tf
import numpy as np
SEED = 23455
rdm = np.random.RandomState(seed=SEED) # 生成[0,1)之间的随机数
x = rdm.rand(32, 2) #此即x1和x2
y_ = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x] # 生成噪声[0,1)/10=[0,0.1); [0,0.1)-0.05=[-0.05,0.05)
x = tf.cast(x, dtype=tf.float32)
w1 = tf.Variable(tf.random.normal([2, 1], stddev=1, seed=1))
b1 = tf.Variable(tf.random.normal([1], stddev=1, seed=1))
epoch = 30000
lr = 0.003
for epoch in range(epoch):
with tf.GradientTape() as tape: #用with结构计算前向传播结构y和loss
y = tf.matmul(x, w1) + b1
loss_mse = tf.reduce_mean(tf.square(y_ - y))
grads = tape.gradient(loss_mse, [w1,b1])
w1.assign_sub(lr * grads[0])
b1.assign_sub(lr * grads[1])
if epoch % 2000 == 0:
print("After %d training steps,w1 and b1 are " % (epoch))
print(w1.numpy())
print(b1.numpy(), "\n")
print("Final w1 is: ", w1.numpy())
print("Final b1 is: ", b1.numpy())
Final w1 is: [[0.9900439]
[0.983632 ]]
Final b1 is: [0.01385183]
运行代码可以看到,随着迭代轮次的上升,w1的两个元素值不断趋近于1,而偏置项b1不断趋近于0,这符合我们制造数据集的公式y=x1+x2,说明神经网络拟合正确
交叉熵损失函数
交叉熵(Cross Entropy)表征两个概率分布之间的距离,交叉熵越小说明二者分布越接近,是分类问题中使用较广泛的损失函数
其中y_代表数据的真实值,y代表神经网络的预测值。对于多分类问题,神经网络的输出一般不是概率分布,因此需要引入softmax层,使得输出服从概率分布。TensorFlow中可计算交叉熵损失函数的API有
TensorFlow API: tf.keras.losses.categorical_crossentropy
loss_ce1 = tf.losses.categorical_crossentropy([1, 0], [0.6, 0.4])
loss_ce2 = tf.losses.categorical_crossentropy([1, 0], [0.8, 0.2])
print("loss_ce1:", loss_ce1)
print