Pytorch ——基础指北_贰 [反向传播和梯度下降]

Pytorch ——基础指北_贰

系列文章目录

Pytorch ——基础指北_零
Pytorch ——基础指北_壹
Pytorch ——基础指北_贰
Pytorch ——基础指北_叁

软件环境:

- pytorch 1.10
- pycharm

配套代码下载地址:

gitee-pytorch

基础知识:

要想训练一个网络,对于梯度的理解是必不可少的,下面首先介绍梯度的一些基础概念。

1、多元函数求偏导

一元函数,即有一个自变量。类似 f ( x ) f(x) f(x)

多元函数,即有多个自变量。类似 f ( x , y , z ) , 三 个 自 变 量 x , y , z f(x,y,z),三个自变量x,y,z f(x,y,z),x,y,z

多元函数求偏导过程中:对某一个自变量求导,其他自变量当做常量即可

例1:
f ( x , y , z ) = a x + b y + c z a n s : d f ( x , y , z ) d x = a d f ( x , y , z ) d y = b d f ( x , y , z ) d z = c f(x,y,z) = ax+by+cz \\ \\ans:\\ \frac{df(x,y,z)}{dx} = a \\ \frac{df(x,y,z)}{dy} = b \\ \frac{df(x,y,z)}{dz} = c f(x,y,z)=ax+by+czans:dxdf(x,y,z)=adydf(x,y,z)=bdzdf(x,y,z)=c

例2:
f ( x , y ) = x y a n s : d f ( x , y ) d x = y d f ( x , y ) d y = x f(x,y) = xy \\ \\ans:\\ \frac{df(x,y)}{dx} = y\\ \frac{df(x,y)}{dy} = x f(x,y)=xyans:dxdf(x,y)=ydydf(x,y)=x

练习:

已知 J ( a , b , c ) = 3 ( a + b c ) , 令 u = a + v , v = b c J(a,b,c) = 3(a+bc),令u=a+v,v = bc J(a,b,c)=3(a+bc),u=a+v,v=bc,求a,b,c各自的偏导数。
令 : J ( a , b , c ) = 3 u d J d a = d J d u × d u d a = 3 × 1 d J d b = d J d u × d u d v × d v d b = 3 × 1 × c d J d c = d J d u × d u d v × d v d c = 3 × 1 × b 令:J(a,b,c) = 3u\\ \frac{dJ}{da} =\frac{dJ}{du} \times \frac{du}{da} = 3\times1 \\ \frac{dJ}{db} =\frac{dJ}{du} \times \frac{du}{dv} \times \frac{dv}{db} = 3\times1\times c \\ \frac{dJ}{dc} =\frac{dJ}{du} \times \frac{du}{dv} \times \frac{dv}{dc} = 3\times1\times b \\ :J(a,b,c)=3udadJ=dudJ×dadu=3×1dbdJ=dudJ×dvdu×dbdv=3×1×cdcdJ=dudJ×dvdu×dcdv=3×1×b

2、方向导数:

  简单地说方向导数形容的是满足某个关系下(Y=KX+B),对于各个方向上本关系数值变化率(Y的变化率)的量化表达式。

数学推导:

可参考:

方向导数1 第一章

方向导数2

L X O Y XOY XOY 平面(笛卡尔坐标系)上以 P ( X 0 , Y 0 ) P(X_0,Y_0) P(X0,Y0)为始点的一条射线, e l = ( cos ⁡ α , cos ⁡ β ) e_l = ( \cos \alpha , \cos \beta) el=(cosα,cosβ) 是与 L 同方向的单位向量。

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第1张图片

射线 L 的参数方程为
{ x = x 0 + t cos ⁡ α y = y 0 + t cos ⁡ β ( t ≥ 0 ) \left\{\begin{array}{l}x=x_{0}+t \cos \alpha \\ y=y_{0}+t \cos \beta(t \geq 0)\end{array}\right. {x=x0+tcosαy=y0+tcosβ(t0)
假设沿 l l l方向增加 t t t,则函数增量为
Δ z = f ( x 0 + t cos ⁡ α , y 0 + t cos ⁡ β ) − f ( x 0 , y 0 ) ( Δ x = t cos ⁡ α , Δ y = t cos ⁡ β ) \Delta z =f\left(x_{0}+t \cos \alpha, y_{0}+t \cos \beta\right )-f\left(x_{0}, y_{0}\right)\\ (\Delta x = t \cos \alpha,\Delta y = t \cos \beta) Δz=f(x0+tcosα,y0+tcosβ)f(x0,y0)(Δx=tcosα,Δy=tcosβ)
所以方向导数就有了如下定义:
∂ f ∂ l ∣ ( x 0 , y 0 ) = lim ⁡ t → 0 + f ( x 0 + t cos ⁡ α , y 0 + t cos ⁡ β ) − f ( x 0 , y 0 ) t \left. \frac{\partial f}{\partial l}\right|_{\left(x_{0}, y_{0}\right)}=\lim _{t \rightarrow 0^{+}} \frac{f\left(x_{0}+t \cos \alpha, y_{0}+t \cos \beta\right)-f\left(x_{0}, y_{0}\right)}{t} lf(x0,y0)=t0+limtf(x0+tcosα,y0+tcosβ)f(x0,y0)

 从方向导数的定义可知,方向导数  ∂ f ∂ l ∣ ( x 0 , y 0 )  就是函数  f ( x , y )  在点  P 0 ( x 0 , y 0 )  处沿方向  l  的变化率.  \text { 从方向导数的定义可知,方向导数 }\left.\frac{\partial f}{\partial l}\right|_{\left(x_{0}, y_{0}\right)} \text { 就是函数 } f(x, y) \text { 在点 } P_{0}\left(x_{0}, y_{0}\right) \text { 处沿方向 } l \text { 的变化率. }  从方向导数的定义可知,方向导数 lf(x0,y0) 就是函数 f(x,y) 在点 P0(x0,y0) 处沿方向 l 的变化率

定理:

 如果函数  f ( x , y )  在点  P 0 ( x 0 , y 0 )  可微分,那么函数在该点沿任一方向  l  的方向导数存在,且有 : \text { 如果函数 } f(x, y) \text { 在点 } P_{0}\left(x_{0}, y_{0}\right) \text { 可微分,那么函数在该点沿任一方向 } l \text { 的方向导数存在,且有 :}  如果函数 f(x,y) 在点 P0(x0,y0) 可微分,那么函数在该点沿任一方向 l 的方向导数存在,且有 :

∂ f ∂ l ∣ ( x 0 , y 0 ) = f x ( x 0 , y 0 ) cos ⁡ α + f y ( x 0 , y 0 ) cos ⁡ β 注 意 里 面 为 偏 导 实 际 上 就 分 解 成 了 X Y 轴 上 函 数 变 化 率 \left.\frac{\partial f}{\partial l}\right|_{\left(x_{0}, y_{0}\right)}=f_{x}\left(x_{0}, y_{0}\right) \cos \alpha+f_{y}\left(x_{0}, y_{0}\right) \cos \beta \\注意里面为偏导 实际上就分解成了X Y轴上函数变化率 lf(x0,y0)=fx(x0,y0)cosα+fy(x0,y0)cosβXY

其中, cos ⁡ α  和  cos ⁡ β \cos \alpha \text { 和 } \cos \beta cosα  cosβ是方向 I I I 的方向余弦。

3、梯度:

梯度是方向导数的特例:
gradf ⁡ ( x , y ) = ∂ f ∂ x i ˙ + ∂ f ∂ y j ˙ \operatorname{gradf}(x, y) = \frac{\partial f}{\partial x} \dot{i}+\frac{\partial f}{\partial y} \dot{j} gradf(x,y)=xfi˙+yfj˙
已知在某个点有方向导数存在下列关系:
∂ f ∂ l = ∂ f ∂ x cos ⁡ φ + ∂ f ∂ y sin ⁡ φ = { ∂ f ∂ x , ∂ f ∂ y } ⋅ { cos ⁡ φ , sin ⁡ φ } \frac{\partial f}{\partial l}=\frac{\partial f}{\partial x} \cos \varphi+\frac{\partial f}{\partial y} \sin \varphi=\left\{\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}\right\} \cdot\{\cos \varphi, \sin \varphi\} lf=xfcosφ+yfsinφ={xf,yf}{cosφ,sinφ}

在方向 L上满足如下单位向量:
e → = cos ⁡ φ i → + sin ⁡ φ j ⃗ → \overrightarrow{\boldsymbol{e}}=\cos \varphi \overrightarrow { \boldsymbol{i}}+\sin \varphi \overrightarrow{ \vec{j} } e =cosφi +sinφj

则方向导数可转化成如下:
∂ f ∂ l = gradf ⁡ ( x , y ) \frac{\partial f}{\partial l} = \operatorname{gradf}(x, y) lf=gradf(x,y)
  点积就相当于做一个投影,方向导数梯度 之间保持一定的夹角(做点积)来构成各个方向上的方向导数。什么时候方向向量最大呢?很容易想到不存在夹角的时候就可以满足,因为此时点积最大即满足下列条件:
 只有当  cos ⁡ ( grad ⁡ f ( x , y ) , e ⃗ ) = 1 , ∂ f ∂ l  才有最大值。  \text { 只有当 } \cos (\operatorname{grad} f(x, y), \vec{e})=1 , \frac{\partial f}{\partial l} \text { 才有最大值。 }  只有当 cos(gradf(x,y),e )=1lf 才有最大值。 
函数在某点的梯度是个向量,他的方向与方向导数最大值取值的方向一致,其大小正好是最大的方向导数。

  梯度概念理解:如下图所示,在P点放一个热源的等温线,则热源的辐射从里到外为10°、20°、30°、40°,若一个小蚂蚁在o点,要最快逃离热源,应该往OJ方向逃离,若往OM方向逃离则热源的变化率为0,即一直都是20°,也就是说蚂蚁一旦确定了某个逃离方向(0°,90°)方向角逃离,只要一直沿着该方向一直走,就是最快的热源降低的方向

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第2张图片
对于一维线性函数其导数就是梯度。

各种函数的梯度与导数的关系:

函数梯度

更详细的解释可以参考参考文献链接。

Tensor的梯度与反向传播

回顾机器学习

收集数据 x x x ,构建机器学习模型 f f f,得到 f ( x , w ) = Y p r e d i c t f(x,w) = Y_{predict} f(x,w)=Ypredict

如何判断模型的好坏?判断模型好坏的方法:
loss ⁡ = ( Y p r e d i c t − Y true  ) 2  (回归损失)  loss ⁡ = Y true  ⋅ log ⁡ ( Y predict  )  (分类损失)  \begin{array}{ll} \operatorname{loss}=\left(Y_{p r e d i c t}-Y_{\text {true }}\right)^{2} & \text { (回归损失) } \\ \operatorname{loss}=Y_{\text {true }} \cdot \log \left(Y_{\text {predict }}\right) & \text { (分类损失) } \end{array} loss=(YpredictYtrue )2loss=Ytrue log(Ypredict ) (回归损失 (分类损失
通过最终 l o s s loss loss 的输出,来反向传播计算梯度大小进而调整参数的大小实现最优解。

l o s s loss loss 满足如图时候

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第3张图片

计算出来梯度以后:朝着梯度变化的方向运算,随机选择一个起始点 w 0 w_0 w0,通过调整 w 0 w_0 w0,让 l o s s loss loss 函数取到最小值。

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第4张图片

w w w的更新方法

  1. 计算 w w w的梯度(导数)

∇ w = f ( w + 0.000001 ) − f ( w − 0.000001 ) 2 ∗ 0.000001 \nabla w = \frac{f(w+0.000001)-f(w-0.000001)}{2*0.000001} w=20.000001f(w+0.000001)f(w0.000001)

  1. 更新 w w w
    w = w − α ∇ w w = w - \alpha \nabla w w=wαw

其中:

  1. w < 0 w <0 w<0 ,意味着w将增大
  2. w > 0 w >0 w>0 ,意味着w将减小

总结:梯度就是多元函数参数的变化趋势(参数学习的方向),只有一个自变量时称为导数,拥有多个时称为偏导数。

反向传播?

计算图

为了方便描述,通过图的方式来描述函数。

J ( a , b , c ) = 3 ( a + b c ) , 令 u = a + v , v = b c J(a,b,c) = 3(a+bc),令u=a+v,v = bc J(a,b,c)=3(a+bc),u=a+v,v=bc,把它绘制成计算图可以表示为:
Pytorch ——基础指北_贰 [反向传播和梯度下降]_第5张图片

对每个节点求偏导可有:
Pytorch ——基础指北_贰 [反向传播和梯度下降]_第6张图片
反向传播的过程就是一个上图的从右往左的过程,自变量 a , b , c a,b,c a,b,c各自的偏导就是连线上的梯度的乘积:

d J d a = 3 × 1 d J d b = 3 × 1 × c d J d c = 3 × 1 × b \frac{dJ}{da} = 3 \times 1 \\ \frac{dJ}{db} = 3 \times 1 \times c \\ \frac{dJ}{dc} = 3 \times 1 \times b dadJ=3×1dbdJ=3×1×cdcdJ=3×1×b

为什么要算反向传播?

因为要计算梯度。

实战演示:

接下来尝试计算一个简单结构的梯度,问题描述如下:

假设我们的基础模型就是y = wx+b,其中w和b均为参数,我们使用y = 3x+0.8来构造数据x、y,所以最后通过模型应该能够得出w和b应该分别接近3和0.8。

简单的来说就是拟合出满足y = 3x+0.8这个曲线。

步骤分为四步:

# 1 构造数据
# 2 设计正向传播 和 反向传播函数 来训练网络
# 3 训练
# 4 画图画出拟合出来的曲线

过程如下图:

从左向右是正向传播部分

从右向左是反向传播部分

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第7张图片

对于WB其计算类似,这里单独说B即可

对于B的梯度满足下式,值得注意的是这里的Loos求取的是平均值实际上出来的是一个标量,对于标量的梯度计算实际上也是一个平均值(这里值得思考一下)。

∂ L o s s ∂ B = ∑ i = 0 N 2 ∗ ( y i − y p i ) / N \frac{\partial Loss}{\partial B} = \sum_{i=0}^N 2*(y_i-y_{pi})/N BLoss=i=0N2(yiypi)/N
反向传播后对B进行梯度下降:

B = B − r a t e ∗ ∂ L o s s ∂ B B = B - rate *\frac{\partial Loss}{\partial B} B=BrateBLoss
梯度下降以后再次进行正向传播即可,计算出来Y_p,最后计算出来Loss

正向传播满足下式:
Y p r e d i c t ( 0... N ) = X p r e d i c t ( 0... N ) ∗ W + B Y_{predict (0...N)} =X_{predict (0...N)}* W + B Ypredict(0...N)=Xpredict(0...N)W+B
代码如下:

import torch
import numpy as np
import matplotlib.pyplot as plt

# 1 构造数据
x_number = 50
x = torch.rand([x_number, 1])
y = 3 * x + 0.8
rate = 0.01
study_time = 3000

# 2 正向传播 和 反向传播
w = torch.rand([1, 1], requires_grad=True, dtype=torch.float32)
b = torch.rand(1, requires_grad=True, dtype=torch.float32)
y_preidct = torch.matmul(x, w) + b


def forward_propagation():
    global x, w, b, y_preidct
    y_preidct = torch.matmul(x, w) + b
    # 计算 loss
    loss = (y - y_preidct).pow(2).mean()
    return loss


def back_propagation():
    global x, w, b, loss, rate, y_preidct
    test = 0.0
    if w.grad is not None:
        w.grad.data.zero_()
    if b.grad is not None:
        b.grad.data.zero_()
    # 反向传播
    loss.backward()
    w.data -= w.grad * rate
    b.data -= b.grad * rate
    #此处为了验证b的梯度进行计算
    # for j in range(x_number):
    #   test += ((y[j] -y_preidct[j].item()) * 2) 
    # print("b:", b.grad)
    # print("b_t:", test/x_number)


# 3 训练部分
for i in range(study_time):
    loss = forward_propagation()
    back_propagation()
    if i % 10 == 0:
        print("w,b,loss", w.item(), b.item(), loss.item())

# 4 画图部分
predict = x * w + b  # 使用训练后的w和b计算预测值
plt.scatter(x.data, y.data, c="r")
plt.plot(x.data.numpy(), predict.data.numpy())
plt.show()

红色的是数据集结果蓝色是训练出来的结果:

当训练次数比较少的时候拟合曲线不正确:

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第8张图片

当把学习率降低(变化范围减小),增加学习次数就可以得到很好的结果:

Pytorch ——基础指北_贰 [反向传播和梯度下降]_第9张图片

参考文献:

国内教程 偏理论 (10 -13 节)

youtobe教程 (第三节)(需要科学上网)有需要搬运联系我

方向导数1

方向导数2

你可能感兴趣的:(Deeplearning,pytorch,深度学习,人工智能,python,机器学习)