我们知道在pytorch中更新参数只需要三步:
optimizer.zero_grad() //清空梯度防止累积
loss.backward() //计算梯度
optimizer.step() //更新参数
但是随着我们学习的深入,我感到只会写这三步大致知道它的意思是不够的,而是必须知道参数更新在机器学习里面究竟是如何做的,在pytorch里面又是如何实现的。
为此,我们举一个简单的例子。
现在有一个西瓜分类任务,输入
x
=
[
x
1
,
x
2
,
x
3
]
.
x=[x_1,x_2,x_3].
x=[x1,x2,x3].有三维,分别代表西瓜的三个特征,输出
y
=
[
y
1
,
y
2
]
y=[y_1,y_2]
y=[y1,y2]有两维,代表分别属于两个类别的概率。
为了实验的简单,假设我们的预测函数为:
y
1
=
w
1
x
1
+
w
2
x
2
+
w
3
x
3
+
b
y_1=w_1 x_1 + w_2x_2 + w_3x_3 +b
y1=w1x1+w2x2+w3x3+b。(这里只写出
y
1
y_1
y1的预测函数,
y
2
y_2
y2同理)
要学习的参数有
w
1
,
w
2
,
w
3
,
b
w_1,w_2,w_3,b
w1,w2,w3,b共四个。
我们只需要定义
l
o
s
s
=
F
u
n
(
y
l
,
y
p
)
loss=Fun(y_l,y_p)
loss=Fun(yl,yp)(
y
l
y_l
yl为标签,
y
p
y_p
yp为预测值),loss分别对三个参数求导得到梯度g,要让loss变小,所以参数应该要这样更新(朝梯度的反方向更新):
w
1
=
w
1
−
l
r
∗
∂
L
∂
w
1
w_1=w_1 -lr* \frac{\partial L}{\partial w_1}
w1=w1−lr∗∂w1∂L
w
2
=
w
2
−
l
r
∗
∂
L
∂
w
2
w_2=w_2 -lr* \frac{\partial L}{\partial w_2}
w2=w2−lr∗∂w2∂L
w
3
=
w
3
−
l
r
∗
∂
L
∂
w
3
w_3=w_3 -lr* \frac{\partial L}{\partial w_3}
w3=w3−lr∗∂w3∂L
b
=
b
−
l
r
∗
∂
L
∂
b
b=b -lr* \frac{\partial L}{\partial b}
b=b−lr∗∂b∂L
其中lr为学习率,决定了参数更新的速度。
下面我们看看这个过程在pytorch里面是如何进行的。
首先(根据公式)定义一个简单的模型。
import torch
from torch import nn
import torch.nn.functional as F
class test_model(nn.Module):
def __init__(self):
super(test_model, self).__init__()
self.fc1 = nn.Linear(3, 2)
def forward(self, x):
return self.fc1(x)
然后再定义optimizer(选用Adam),这里loss并不采用常见的交叉熵损失函数,而是自定义了一个loss为的是方便验证梯度的计算。
net = test_model()
optimizer = torch.optim.Adam(net.parameters(), lr=1.0)
input = torch.randn((1,3))
label = torch.tensor([1])
out = net(input)
loss = 1 - torch.sigmoid(out)[0][1]
for n, p in net.named_parameters():
if n=="fc1.bias":
print("原始bias:", p)
optimizer.zero_grad()
loss.backward()
optimizer.step()
for n, p in net.named_parameters():
if n=="fc1.bias":
print("fc1.bias的梯度",p.grad)
print("更新之后的bias:", p)
输出结果如下:
原始bias: Parameter containing:
tensor([0.2146, 0.5020], requires_grad=True)
fc1.bias的梯度 tensor([ 0.0000, -0.1826])
更新之后的bias: Parameter containing:
tensor([0.2146, 1.5020], requires_grad=True)
回答几个可能有疑问的点:
问:按照公式,当前lr=1.0,为什么0.5020+0.1826 != 1.5020?
答:因为使用了Adam,它会动态地改变学习率,大致原理是梯度大的时候要让学习率小一点,梯度小的时候要让学习率大一点,所以lr会改变,自然上述等式就不成立。
问:为什么bias1的梯度为0 ,值没有改变?
答:你看一下损失函数的计算,根本没有涉及
y
1
y_1
y1的,所以什么bias1的梯度为0 ,值不会改变。