什么是梯度
我也不知道什么是梯度。从中学学的导数说起,倒数反应了函数沿某个方向的变化率。然后就到了偏微分,偏微分是限定沿给定方向的变化率,而梯度就是函数关于所有自变量偏微分组成的向量。梯度的方向反应了函数上升或下降的方向,而梯度的大小反映了下降或上升的快慢。
梯度下降:
θ
i
∗
=
θ
i
−
e
p
s
∗
∂
y
∂
θ
i
\theta_{i}^{*}=\theta_{i}-eps*\frac{\partial y}{\partial \theta_{i}}
θi∗=θi−eps∗∂θi∂y,梯度下降时会遇到局部最小值的问题,常用的解决方法是随机初始化、加一个冲量使其越过局部最小等等吧,其实我也不知道,好菜呀。
常见函数的梯度
激活函数及其梯度
- sigmoid函数
f ( x ) = 1 1 + e − x f(x)=\frac{1}{1+e^{-x}} f(x)=1+e−x1
导数为: f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) f^{'}(x)=f(x)(1-f(x)) f′(x)=f(x)(1−f(x))
sigmoid函数有一个缺陷就是如果自变量是负无穷或正无穷则导数为零,参数会长时间得不到更新。 - tanh函数
t a n h ( x ) = e x − e − x e x + e − x = 2 s i g m o i d ( 2 x ) − 1 tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}=2sigmoid(2x)-1 tanh(x)=ex+e−xex−e−x=2sigmoid(2x)−1
导数为: t a n h ′ ( x ) = 1 − t a n h 2 ( x ) tanh^{'}(x)=1-tanh^2(x) tanh′(x)=1−tanh2(x)
tanh函数在RNN中常用。 - relu函数
当x<0时,relu(x)=0;x>=0时,relu(x)=x,即梯度为1。
loss函数及其梯度
- 均方误差MSE
l o s s = ∑ ( y − f ( x ) ) 2 loss=\sum{(y-f(x))^2} loss=∑(y−f(x))2
求导为: l o s s ′ ( x ) = 2 ∑ ( y − f ( x ) ) ∗ ∇ f ∇ θ loss^{'}(x)=2\sum{(y-f(x))*\frac{\nabla f}{\nabla \theta}} loss′(x)=2∑(y−f(x))∗∇θ∇f
pytorch中的相关操作
import torch
from torch.nn import functional as F
w = torch.full([1],2.0, requires_grad=True) #这里需要注明w需要梯度信息,如果不这样操作,进行梯度求导时会报错
# 或者之后更新:w.requires_grad_()
x = torch.ones(1)
mse = F.mes_loss(torch.ones(1),w*x) # 给出label和预测值
# 用torch.autograd.grad()求梯度
torch.autograd.grad(mse,[w])
# 也可以用backward进行操作,backward是在传递过程中记录了所有需要梯度信息
mse = F.mse_loss(torch.ones(1),x*w)
mse.backward()
w.grad
- softmax函数
函数形式为: S ( x ) = e y i ∑ e y i S(x)=\frac{e^{y_i}}{\sum{e^{y_i}}} S(x)=∑eyieyi
softmax 函数可以将一系列数转换成不同的概率,其中越大的数的概率越大,同时不同大小的数的差距也被拉大。
假设输入为 a i a_i ai,输出为 p i p_i pi,则导数为: ∂ p i ∂ a j = p i ( 1 − p j ) , i = = j ; − p i p j , i ! = j \frac{\partial p_i}{\partial a_j}=p_i(1-p_j),i==j;-p_ip_j,i!=j ∂aj∂pi=pi(1−pj),i==j;−pipj,i!=ja = torch.rand(3) p = F.softmax(a,dim=0) # 第一个参数是要求梯度的数,必须是维度为一,有一个数的tensor # 第二个参数是对谁求偏导的列表,retain_graph=True是保留建图的信息,以后还可以继续求梯度 torch.autograd.grad(p[1],[a],retain_graph=True)
反向传播算法
反向传播算法首先依靠的是链式求导法则,即
d
y
d
x
1
=
d
y
d
y
1
d
y
1
d
x
1
\frac{dy}{dx_1}=\frac{dy}{dy_1}\frac{dy1}{dx_1}
dx1dy=dy1dydx1dy1
利用链式求导求出输出对神经网络边权值的偏微分,然后进行更新。
对于一个多输出的神经网络,如上图所示,
∂
E
∂
w
j
k
=
(
O
k
−
t
k
)
O
k
(
1
−
O
k
)
x
j
\frac{\partial E}{\partial w_{jk}}=(O_k-t_k)O_k(1-O_k)x_j
∂wjk∂E=(Ok−tk)Ok(1−Ok)xj
对某一层的参数求导推导如下:
可以发现从输出往前推导,知道后边几层的参数导数就可以进而求出当前层的信息。