机器学习是数据套公式,但是深度学习不是这样的,虽然也是"节点映射成为节点"的方法最后得到结果,但是训练方法却很不一样,直到目前我依旧有一些不明白的地方,所以准备整理看一看
链式法则
自动求导要用到链式法则,这个是高数的内容,大致是:
∂
y
∂
x
=
∂
y
∂
u
n
∂
u
n
∂
u
n
−
1
.
.
.
∂
u
1
∂
x
\frac{\partial y}{\partial x} =\frac{\partial y}{\partial u_n}\frac{\partial u_n}{\partial u_{n-1}}...\frac{\partial u_1}{\partial x}
∂x∂y=∂un∂y∂un−1∂un...∂x∂u1
如果x,y是向量或者矩阵,那么拆开也是,不过公式上差不多都是这样
首先给一个公式,举个例子:
z
=
(
<
x
,
w
>
−
y
)
2
z=(<x,w>-y)^2
z=(<x,w>−y)2
这里
<
x
,
w
>
<x,w>
<x,w>是向量乘法(其实例子里用不用这样都没关系)
然后:
a
=
<
x
,
w
>
⇒
z
=
(
a
−
y
)
2
a=<x,w>\Rightarrow z=(a-y)^2
a=<x,w>⇒z=(a−y)2
b
=
a
−
y
⇒
z
=
b
2
b=a-y\Rightarrow z=b^2
b=a−y⇒z=b2
那么如果对
w
w
w求导:
∂
z
∂
w
\frac{\partial z}{\partial w}
∂w∂z
=
∂
z
∂
b
∂
b
∂
a
∂
a
∂
w
=\frac{\partial z}{\partial b}\frac{\partial b}{\partial a}\frac{\partial a}{\partial w}
=∂b∂z∂a∂b∂w∂a
代入:
=
∂
b
2
∂
b
∂
(
a
−
y
)
∂
a
∂
<
x
,
w
>
∂
w
=\frac{\partial b^2}{\partial b}\frac{\partial {(a-y)}}{\partial a}\frac{\partial {<x,w>}}{\partial w}
=∂b∂b2∂a∂(a−y)∂w∂<x,w>
=
2
b
⋅
1
⋅
x
T
=2b\cdot 1\cdot x^T
=2b⋅1⋅xT
=
2
(
<
x
,
w
>
−
y
)
x
T
=2(<x,w>-y)x^T
=2(<x,w>−y)xT
所以不管原式如何,用链式法则都可以求个导(李沐老师在后面的章节表示,基本不存在不可导的函数,最多是无法处处可导)
(如果有很多项相乘,那么把不准备求的几项合为一项就行了,毕竟是服务于深度学习的,不会太复杂)
例子里用不用
<
x
,
w
>
<x,w>
<x,w>都一样,但是后面学习神经网络的时候都会尽可能变成矩阵相乘的情况,也就是
<
x
,
w
>
<x,w>
<x,w>,所以李沐老师讲的时候都是用这样的例子说明的
计算图
计算图形状有点像一棵树,不过根节点是函数的"结果",子节点则是输入
示例:
理论上一个函数的公式都可以画成计算图,而反向传递就是在计算图上进行的操作
梯度下降
机器学习中常常使用梯度下降法来求损失函数的最小值,当损失函数最小时的参数就是模型效率最高时的函数(我之前文章提到过,不理解可以自行百度)
大致公式是:
θ
=
θ
−
η
∇
J
(
θ
)
\theta = \theta - \eta \nabla J(\theta)
θ=θ−η∇J(θ)
然后用数学方法求出具体公式,进行梯度下降
反向传递
不同于机器学习梯度下降是"提前计算出公式然后套用公式",深度学习的梯度下降是直接算,因为不管怎么求导,最后也是要算成是数值的
具体如何操作呢?首先按照计算图算出函数结果,同时保留每一步结果,然后反向传播并求导,算出
∇
J
(
θ
)
\nabla J(\theta)
∇J(θ)
用例子进行说明(我也不记得在哪看到的例子了):
方程:
y
=
1
1
+
e
−
(
ω
0
x
0
+
ω
1
x
1
+
ω
2
)
y=\frac{1}{1+e^{-(\omega_0x_0+\omega_1x_1+\omega_2)}}
y=1+e−(ω0x0+ω1x1+ω2)1
参数为
ω
0
=
2
,
ω
1
=
−
3
,
ω
2
=
−
3
,
x
0
=
−
1
,
x
1
=
−
2
,
y
=
1.73
\omega_0=2,\omega_1=-3,\omega_2=-3,x_0=-1,x_1=-2,y=1.73
ω0=2,ω1=−3,ω2=−3,x0=−1,x1=−2,y=1.73
首先肯定不是给一个代入之后完全相等的等式给你,因为有噪声,我们的任务是求出这个等式
那么如何用反向传递对其进行调整呢?
先画出计算图:
然后进行顺序计算:
可以看出来这里算出来的是1.73
但是我们的数据是0.73
这就产生了一个数值为1的差值
然后就是反向传播了:
将1返回去
因为我们求的是导数,所以对
1
x
\frac{1}{x}
x1求导,注意,这里求导时的x是1.37,然后乘以我们反向传播过来的数值:
得到-0.53,再将-0.53传向上一个节点,对(x+1)进行求导,此处x是0.37
继续直到参数段:
注意在这里,我们把上面一个看成
z
z
z,下面是
ω
2
\omega_2
ω2,这里就是
ω
2
+
z
\omega_2+z
ω2+z无论是对
z
z
z求导还是
ω
2
\omega_2
ω2求导,结果都是1,所以返回去的值不变都是0.2:
同理:
再到乘法了,对
x
,
ω
x,\omega
x,ω求导时,另一个数视作常数,故求导后的结果不一样
最终得到的数值就是要对参数进行调整的值,也就是梯度下降法的"下降"
神经网络中…
看得出来,没有正向传播,反向传播无法运作
对神经网络进行训练时,每次训练都要进行backwards,就是为了保存正向传播时的数据,同时清零上一次反向传播储存的参数,还要更新新的参数
而进行预测时则要设定为预测模式,减少内存占用
(以下是每次训练)
# net是已经定义好的神经网络
# 设为训练模式
net.train()
# 在每一个epoch中
for X,y in train_iter:
# 只用直到X是训练数据,y是训练数据对应的结果即可,如何合成一起并不重要
y_hat = net(X)
# 对训练数据进行预测
l = loss(y_hat, y)
# l是损失函数(通常另外定义),计算预测试和实际值的损失
trainer.zero_grad()
# 梯度归0
l.backward()
# 对损失函数求导
trainer.step()
# 参数自更新
而单纯的看预测结果的模式(这样不会保留梯度,也就不会存那么复杂一个计算图了)
net.eval()