Pytorch ——基础指北_零
Pytorch ——基础指北_壹
Pytorch ——基础指北_贰
Pytorch ——基础指北_叁
- pytorch 1.10
- pycharm
gitee-pytorch
要想训练一个网络,对于梯度的理解是必不可少的,下面首先介绍梯度的一些基础概念。
一元函数,即有一个自变量。类似 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
简单地说方向导数形容的是满足某个关系下(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 同方向的单位向量。
射线 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β(t≥0)
假设沿 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} ∂l∂f∣∣∣∣(x0,y0)=t→0+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 { 的变化率. } 从方向导数的定义可知,方向导数 ∂l∂f∣∣∣∣(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轴上函数变化率 ∂l∂f∣∣∣∣(x0,y0)=fx(x0,y0)cosα+fy(x0,y0)cosβ注意里面为偏导实际上就分解成了XY轴上函数变化率
其中, cos α 和 cos β \cos \alpha \text { 和 } \cos \beta cosα 和 cosβ是方向 I I I 的方向余弦。
梯度是方向导数的特例:
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)=∂x∂fi˙+∂y∂fj˙
已知在某个点有方向导数存在下列关系:
∂ 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\} ∂l∂f=∂x∂fcosφ+∂y∂fsinφ={∂x∂f,∂y∂f}⋅{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) ∂l∂f=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)=1,∂l∂f 才有最大值。
函数在某点的梯度是个向量,他的方向与方向导数最大值取值的方向一致,其大小正好是最大的方向导数。
梯度概念理解:如下图所示,在P点放一个热源的等温线,则热源的辐射从里到外为10°、20°、30°、40°,若一个小蚂蚁在o点,要最快逃离热源,应该往OJ方向逃离,若往OM方向逃离则热源的变化率为0,即一直都是20°,也就是说蚂蚁一旦确定了某个逃离方向(0°,90°)方向角逃离,只要一直沿着该方向一直走,就是最快的热源降低的方向
各种函数的梯度与导数的关系:
函数梯度
更详细的解释可以参考参考文献链接。
收集数据 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=(Ypredict−Ytrue )2loss=Ytrue ⋅log(Ypredict ) (回归损失) (分类损失)
通过最终 l o s s loss loss 的输出,来反向传播计算梯度大小进而调整参数的大小实现最优解。
当 l o s s loss loss 满足如图时候
计算出来梯度以后:朝着梯度变化的方向运算,随机选择一个起始点 w 0 w_0 w0,通过调整 w 0 w_0 w0,让 l o s s loss loss 函数取到最小值。
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=2∗0.000001f(w+0.000001)−f(w−0.000001)
其中:
总结:梯度就是多元函数参数的变化趋势(参数学习的方向),只有一个自变量时称为导数,拥有多个时称为偏导数。
为了方便描述,通过图的方式来描述函数。
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 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 画图画出拟合出来的曲线
过程如下图:
从左向右是正向传播部分
从右向左是反向传播部分
对于W和B其计算类似,这里单独说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 ∂B∂Loss=i=0∑N2∗(yi−ypi)/N
反向传播后对B进行梯度下降:
B = B − r a t e ∗ ∂ L o s s ∂ B B = B - rate *\frac{\partial Loss}{\partial B} B=B−rate∗∂B∂Loss
梯度下降以后再次进行正向传播即可,计算出来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()
红色的是数据集结果蓝色是训练出来的结果:
当训练次数比较少的时候拟合曲线不正确:
当把学习率降低(变化范围减小),增加学习次数就可以得到很好的结果:
参考文献:
国内教程 偏理论 (10 -13 节)
youtobe教程 (第三节)(需要科学上网)有需要搬运联系我
方向导数1
方向导数2