神经网络优化

神经网络优化

一、神经网络(NN)复杂度

NN复杂度:多用NN层数和NN参数的个数表示

  1. 空间复杂度
    层数 = 隐藏层的层数 + 1个输出层
    总参数 = 总w + 总b
    上图3x4+4 + 4x2+2 = 26

  2. 时间复杂度
    乘加运算次数
    上图 3x4 + 4x2 = 20

二、学习率

w t + 1 = w t − l r ∗ ∂ l o s s ∂ w t w_{t+1}=w_t-lr*\frac{\partial loss}{\partial w_t} wt+1=wtlrwtloss
w t + 1 w_{t+1} wt+1:更新后的参数, w t w_{t} wt:当前参数, l r lr lr:学习率, ∂ l o s s ∂ w t \frac{\partial loss}{\partial w_t} wtloss:损失函数的梯度

指数衰减学习率

可以先用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定。
指数衰减学习率 = 初始学习率 ∗ 学习率衰减 率 ( 当前轮数 / 多少轮衰减一次 ) 指数衰减学习率 = 初始学习率 * 学习率衰减率^{(当前轮数 / 多少轮衰减一次)} 指数衰减学习率=初始学习率学习率衰减(当前轮数/多少轮衰减一次)

import tensorflow as tf

w = tf.Variable(tf.constant(5, dtype=tf.float32))

epoch = 40
LR_BASE = 0.2  # 最初学习率
LR_DECAY = 0.99  # 学习率衰减率
LR_STEP = 1  # 喂入多少轮BATCH_SIZE后,更新一次学习率

for epoch in range(epoch):  # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时候constant赋值为5,循环100次迭代。
    lr = LR_BASE * LR_DECAY ** (epoch / LR_STEP)
    with tf.GradientTape() as tape:  # with结构到grads框起了梯度的计算过程。
        loss = tf.square(w + 1)
    grads = tape.gradient(loss, w)  # .gradient函数告知谁对谁求导

    w.assign_sub(lr * grads)  # .assign_sub 对变量做自减 即:w -= lr*grads 即 w = w - lr*grads
    print("After %s epoch,w is %f,loss is %f,lr is %f" % (epoch, w.numpy(), loss, lr))

运行结果如下:

After 0 epoch,w is 2.600000,loss is 36.000000,lr is 0.200000
After 1 epoch,w is 1.174400,loss is 12.959999,lr is 0.198000
After 2 epoch,w is 0.321948,loss is 4.728015,lr is 0.196020
After 3 epoch,w is -0.191126,loss is 1.747547,lr is 0.194060
After 4 epoch,w is -0.501926,loss is 0.654277,lr is 0.192119
After 5 epoch,w is -0.691392,loss is 0.248077,lr is 0.190198

三、激活函数

3.1 简化模型与MP模型


y = x ∗ w + b y=x*w+b y=xw+b


y = f ( x ∗ w + b ) y=f(x*w+b) y=f(xw+b)
f f f 为激活函数

3.2 优秀的激活函数

  • 非线性: 激活函数非线性时,多层神经网络可逼近所有函数
  • 可微性: 优化器大多用梯度下降更新参数
  • 单调性: 当激活函数是单调的,能保证单层网络的损失函数是凸函数
  • 近似恒等性: f(x)≈x当参数初始化为随机小值时,神经网络更稳定

激活函数输出值的范围:

  • 激活函数输出为有限值时,基于梯度的优化方法更稳定
  • 激活函数输出为无限值时,建议调小学习率

3.3 常用激活函数

3.3.1 Sigmoid函数

tf.nn.sigmoid(x)
f ( x ) = 1 1 + e − x f(x)=\frac{1}{1+e^{-x}} f(x)=1+ex1

函数图像
导数图像

特点
(1)易造成梯度消失
(2)输出非0均值,收敛慢
(3)幂运算复杂,训练时间长

3.3.2 Tanh函数

tf.math. tanh(x)
f ( x ) = 1 − e − 2 x 1 + e − 2 x f(x)=\frac{1-e^{-2x}}{1+e^{-2x}} f(x)=1+e2x1e2x

函数图像
导数图像

特点
(1)输出是0均值
(2)易造成梯度消失
(3)幂运算复杂,训练时间长

3.3.3 Relu函数

tf.nn.relu(x)
f ( x ) = m a x ( x , 0 ) f(x)=max(x,0) f(x)=max(x,0)

函数图像
导数图像

优点
(1) 解决了梯度消失问题 (在正区间)
(2) 只需判断输入是否大于0,计算速度快
(3) 收敛速度远快于sigmoid和tanh

缺点
(1) 输出非0均值,收敛慢
(2) Dead Relu问题:某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。

3.3.4 Leaky Relu函数

tf.nn.leaky_relu(x)
f ( x ) = m a x ( α x , x ) f(x)=max(\alpha x,x) f(x)=max(αx,x)

函数图像
导数图像
理论上来讲,Leaky Relu有Relu的所有优点,外加不会有Dead Relu问题,但是在实际操作当中,并没有完全证明Leaky Relu总是好于Relu。

3.4 对初学者的建议

  1. 首选relu激活函数;
  2. 学习率设置较小值;
  3. 输入特征标准化,即让输入特征满足以0为均值,1为标准差的正态分布;
  4. 初始参数中心化,即让随机生成的参数满足以0为均值, 2 当前层输入特征个数 \frac{2}{\sqrt{当前层输入特征个数}} 当前层输入特征个数 2为标准差的正态分布。

四、损失函数

损失函数(loss):预测值(y)与已知答案(y_)的差距
神经网络优化目标:使损失函数loss最小

4.1 均方误差MSE

loss_mse = tf.reduce_mean(tf.square(y_ -y))
M S E ( y _ − y ) = ∑ i − 1 n ( y − y _ ) 2 n MSE(y\_-y)=\frac{\sum_{i-1}^n(y-y\_)^2}{n} MSE(y_y)=ni1n(yy_)2

4.2 交叉熵CE

交叉熵损失函数CE (Cross Entropy):表征两个概率分布之间的距离
h ( y _ , y ) = − ∑ y _ ∗ ln ⁡ y h(y\_,y)=-\sum y\_*\ln y h(y_,y)=y_lny
tf.losses.categorical_crossentropy(y_,y)
例:二分类已知答案y_=(1, 0) 预测y1=(0.6, 0.4) y2=(0.8, 0.2) 哪个更接近标准答案?
H 1 ( ( 1 , 0 ) , ( 0.6 , 0.4 ) ) = − ( 1 ∗ l n 0.6 + 0 ∗ l n 0.4 ) ≈ − ( − 0.511 + 0 ) = 0.511 H 2 ( ( 1 , 0 ) , ( 0.8 , 0.2 ) ) = − ( 1 ∗ l n 0.8 + 0 ∗ l n 0.2 ) ≈ − ( − 0.223 + 0 ) = 0.223 H1((1,0),(0.6,0.4)) = -(1*ln0.6 + 0*ln0.4) ≈ -(-0.511 + 0) = 0.511 \\ H2((1,0),(0.8,0.2)) = -(1*ln0.8 + 0*ln0.2) ≈ -(-0.223 + 0) = 0.223 H1((1,0),(0.6,0.4))=(1ln0.6+0ln0.4)(0.511+0)=0.511H2((1,0),(0.8,0.2))=(1ln0.8+0ln0.2)(0.223+0)=0.223因为H1> H2,所以y2预测更准

softmax与交叉熵结合

输出先过softmax函数,再计算y与y_的交叉熵损失函数。
tf.nn.softmax_cross_entropy_with_logits(y_,y)

五、欠拟合与过拟合


上图依次为欠拟合、正确拟合与过拟合

欠拟合的解决方法

  1. 增加输入特征项
  2. 增加网络参数
  3. 减少正则化参数

过拟合的解决方法

  1. 数据清洗
  2. 增大训练集
  3. 采用正则化
  4. 增大正则化参数

六、正则化缓解过拟合

正则化在损失函数中引入模型复杂度指标,利用给W加权值,弱化了训练数据的噪声(一般不正则化b)

L1正则化

l o s s L 1 ( w ) = ∑ i ∣ w i ∣ loss_{L1}(w)=\sum_i |w_i| lossL1(w)=iwi

L2正则化

l o s s L 2 ( w ) = ∑ i ∣ w i 2 ∣ loss_{L2}(w)=\sum_i |w_i^2| lossL2(w)=iwi2

正则化的选择

L1正则化大概率会使很多参数变为零,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度。
L2正则化会使参数很接近零但不为零,因此该方法可通过减小参数值的大小降低复杂度。

with tf.GradientTape() as tape:  # 记录梯度信息

    h1 = tf.matmul(x_train, w1) + b1  # 记录神经网络乘加运算
    h1 = tf.nn.relu(h1)
    y = tf.matmul(h1, w2) + b2

    # 采用均方误差损失函数mse = mean(sum(y-out)^2)
    loss_mse = tf.reduce_mean(tf.square(y_train - y))
    # 添加l2正则化
    loss_regularization = []
    # tf.nn.l2_loss(w)=sum(w ** 2) / 2
    loss_regularization.append(tf.nn.l2_loss(w1))
    loss_regularization.append(tf.nn.l2_loss(w2))
    
    loss_regularization = tf.reduce_sum(loss_regularization)
    loss = loss_mse + 0.03 * loss_regularization #REGULARIZER = 0.03
正则化前
正则化后

七、神经网络参数优化器

待优化参数w,损失函数loss,学习率lr,每次迭代一个batch,t表示当前batch迭代的总次数:

  1. 计算t时刻损失函数关于当前参数的梯度 g t = ∇ l o s s ∂ l o s s ∂ ( w t ) g_t=\nabla loss\frac{\partial loss}{\partial(w_t)} gt=loss(wt)loss
  2. 计算t时刻一阶动量 m t m_t mt和二阶动量 V t V_t Vt
  3. 计算t时刻下降梯度: η t = l r ∗ m t / V t \eta_t=lr*m_t/\sqrt{V_t} ηt=lrmt/Vt
  4. 计算t+1时刻参数:
    w t + 1 = w t − η t = w t − l r ∗ m t / V t w_{t+1}=w_t-\eta _t=w_t-lr*m_t/\sqrt{V_t} wt+1=wtηt=wtlrmt/Vt

一阶动量:与梯度相关的函数
二阶动量:与梯度平方相关的函数

7.1 SGD

SGD(无momentum),常用的梯度下降法
m t = g v t = 1 η t = l r ∗ m t / V t = l r ∗ g t w t + 1 = w t − η t = w t − l r ∗ g t m_t=g\\ v_t=1\\ \eta_t=lr*m_t/\sqrt{V_t}=lr*g_t\\ w_{t+1}=w_t-\eta_t=w_t-lr*g_t mt=gvt=1ηt=lrmt/Vt =lrgtwt+1=wtηt=wtlrgt

# 计算loss对各个参数的梯度
grads = tape.gradient(loss, [w1, b1])

# 实现梯度更新 w1 = w1 - lr * w1_grad    b = b - lr * b_grad
w1.assign_sub(lr * grads[0])  # 参数w1自更新
b1.assign_sub(lr * grads[1])  # 参数b自更新

7.2 SGDM

SGDM(含momentum的SGD),在SGD基础上增加一阶动量
m t = β ⋅ m t − 1 + ( 1 − β ) ⋅ g t η t = l r ∗ m t / V t = l r ∗ ( β ⋅ m t − 1 + ( 1 − β ) ⋅ g t ) w t + 1 = w t − η t = w t − l r ∗ ( β ⋅ m t − 1 + ( 1 − β ) ⋅ g t ) m_t=\beta \cdot m_{t-1} + (1-\beta)\cdot g_t\\ \eta_t=lr*m_t/\sqrt{V_t}=lr*(\beta \cdot m_{t-1} + (1-\beta)\cdot g_t)\\ w_{t+1}=w_t-\eta_t=w_t-lr*(\beta \cdot m_{t-1} + (1-\beta)\cdot g_t) mt=βmt1+(1β)gtηt=lrmt/Vt =lr(βmt1+(1β)gt)wt+1=wtηt=wtlr(βmt1+(1β)gt)

m_w, m_b = 0, 0
beta = 0.9

m_w = beta * m_w + (1 - beta) * grads[0]
m_b = beta * m_b + (1 - beta) * grads[1]
w1.assign_sub(lr * m_w)
b1.assign_sub(lr * m_b)

7.3 Adagrad

Adagrad,在SGD基础上增加二阶动量
m t = g t V t = ∑ τ = 1 t g τ 2 η t = l r ∗ m t / V t = l r ∗ g t / ∑ τ = 1 t g τ 2 w t + 1 = w t − η t = w t − l r ∗ g t / ∑ τ = 1 t g τ 2 m_t=g_t\\ V_t=\sum^t_{\tau=1}g^2_{\tau}\\ \eta_t=lr*m_t/\sqrt{V_t}=lr*g_t/\sqrt{\sum^t_{\tau=1}g^2_{\tau}}\\ w_{t+1}=w_t-\eta_t=w_t-lr*g_t/\sqrt{\sum^t_{\tau=1}g^2_{\tau}} mt=gtVt=τ=1tgτ2ηt=lrmt/Vt =lrgt/τ=1tgτ2 wt+1=wtηt=wtlrgt/τ=1tgτ2

v_w, v_b = 0, 0

v_w += tf.square(grads[0])
v_b += tf.square(grads[1])
w1.assign_sub(lr * grads[0] / tf.sqrt(v_w))
b1.assign_sub(lr * grads[1] / tf.sqrt(v_b))

7.4 RMSProp

RMSProp,SGD基础上增加二阶动量
m t = g t V t = β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 η t = l r ∗ m t / V t = l r ∗ g t / β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 w t + 1 = w t − l r ∗ m t / V t = w t − l r ∗ g t / β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 m_t=g_t\\ V_t=\beta \cdot V_{t-1}+(1-\beta)\cdot g_t^2\\ \eta_t = lr*m_t/\sqrt{V_t}=lr*g_t/\sqrt{\beta \cdot V_{t-1}+(1-\beta)\cdot g_t^2}\\ w_{t+1}=w_t-lr*m_t/\sqrt{V_t}=w_t-lr*g_t/\sqrt{\beta \cdot V_{t-1}+(1-\beta)\cdot g_t^2} mt=gtVt=βVt1+(1β)gt2ηt=lrmt/Vt =lrgt/βVt1+(1β)gt2 wt+1=wtlrmt/Vt =wtlrgt/βVt1+(1β)gt2

v_w, v_b = 0, 0
beta = 0.9

v_w = beta * v_w + (1 - beta) * tf.square(grads[0])
v_b = beta * v_b + (1 - beta) * tf.square(grads[1])
w1.assign_sub(lr * grads[0] / tf.sqrt(v_w))
b1.assign_sub(lr * grads[1] / tf.sqrt(v_b))

7.5 Adam

Adam, 同时结合SGDM一阶动量和RMSProp二阶动量
m t = β ⋅ m t − 1 + ( 1 − β ) ⋅ g t 修正一阶动量的偏差: m t ^ = m t 1 − β 1 t V t = β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 修正一阶动量的偏差: V t ^ = V t 1 − β 2 t η t = l r ∗ m t ^ / V t ^ = l r ∗ m t 1 − β 1 t / V t 1 − β 2 t w t + 1 = w t − η t = w t − l r ∗ m t 1 − β 1 t / V t 1 − β 2 t m_t=\beta \cdot m_{t-1} + (1-\beta)\cdot g_t\\ 修正一阶动量的偏差:\hat{m_t}=\frac{m_t}{1-\beta_1^t}\\ V_t=\beta \cdot V_{t-1}+(1-\beta)\cdot g_t^2\\ 修正一阶动量的偏差:\hat{V_t}=\frac{V_t}{1-\beta_2^t}\\ \eta_t = lr*\hat{m_t}/\sqrt{\hat{V_t}}=lr*\frac{m_t}{1-\beta_1^t}/\sqrt{\frac{V_t}{1-\beta_2^t}}\\ w_{t+1}=w_t-\eta_t=w_t-lr*\frac{m_t}{1-\beta_1^t}/\sqrt{\frac{V_t}{1-\beta_2^t}} mt=βmt1+(1β)gt修正一阶动量的偏差:mt^=1β1tmtVt=βVt1+(1β)gt2修正一阶动量的偏差:Vt^=1β2tVtηt=lrmt^/Vt^ =lr1β1tmt/1β2tVt wt+1=wtηt=wtlr1β1tmt/1β2tVt

m_w, m_b = 0, 0
v_w, v_b = 0, 0
beta1, beta2 = 0.9, 0.999
delta_w, delta_b = 0, 0
global_step = 0

m_w = beta1 * m_w + (1 - beta1) * grads[0]
m_b = beta1 * m_b + (1 - beta1) * grads[1]
v_w = beta2 * v_w + (1 - beta2) * tf.square(grads[0])
v_b = beta2 * v_b + (1 - beta2) * tf.square(grads[1])

m_w_correction = m_w / (1 - tf.pow(beta1, int(global_step)))
m_b_correction = m_b / (1 - tf.pow(beta1, int(global_step)))
v_w_correction = v_w / (1 - tf.pow(beta2, int(global_step)))
v_b_correction = v_b / (1 - tf.pow(beta2, int(global_step)))

w1.assign_sub(lr * m_w_correction / tf.sqrt(v_w_correction))
b1.assign_sub(lr * m_b_correction / tf.sqrt(v_b_correction))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值