It has been 1256 days since the last update, the content of the article may be outdated.

PyTorch深度学习实践Part5——线性回归

PyTorch周期

  1. prepare dataset
  2. design model using Class 目的是为了前馈forward,即计算y hat(预测值)
  3. Construct loss and optimizer (using PyTorch API) 其中,计算loss是为了进行反向传播,optimizer是为了更新梯度。
  4. Training cycle (forward,backward,update)

广播

原本w只是1×1的矩阵,比如tensor([0.], requires_grad=True),很有可能行列数量与xy对不上,这个时候pytorch会进行广播,将w扩展成一个3×1矩阵

image-20210117122903497

pytorch直接写“*”表示矩阵对应位置元素相乘(哈达玛积),数学上的矩阵乘法有另外的函数torch.matmul

这里x、y的维度都是1(有可能不是1),但是都应当看成一个矩阵,而不能是向量。

x、y的列是维度/特征,行是记录/样本

模型

  1. 要把计算模型定义成一个类,继承于torch.nn.Model。(nn:neural network)
  2. 如果有pytorch没有提供的需求,或者其效率不够高,可以从Function中继承,构造自己的计算块。
  3. Linear类包括成员变量weight和bias,默认bias=True,同样继承于torch.nn.Model,可以进行反向传播。
  4. 权重放在x右边,或者转置放在左边。(不管怎么放都是为了凑矩阵基本积)
  5. 父类实现了callable函数,让其能够被调用。在call中会调用前馈forward(),所以必须重写forward()。

image-20210117132843838

*args, **kwargs的用法:

python
1
2
3
4
5
6
7
8
def fun(*args, **kwargs):
print('args:', args)
print('kwargs:', kwargs)


fun(1, 2, 7, x=6, y=5)
# args: (1, 2, 7)
# kwargs: {'x': 6}

损失&优化

  1. 计算损失使用现成的类 torch.nn.MSELoss 。
  2. 一般使用随机梯度下降算法,求和平均是没有必要的,torch.nn.MSELoss(size_average=False)
  3. 使用现成的优化器类torch.optim.SGD
  4. 不同的优化器,官方文档
  5. 控制训练次数,不能过少(训练不到位),也不能过多(过拟合)

代码实现

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import torch
import matplotlib.pyplot as plt

# 准备数据,要是矩阵
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])


# 构建模型,继承、重写
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
self.linear = torch.nn.Linear(1, 1)

def forward(self, x):
return self.linear(x)


# 生成模型的对象
model = LinearModel()
criterion = torch.nn.MSELoss(reduction='sum') # size_average=False已经被弃用
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 这里要设置迭代器管理的权重

loss_list = []

for epoch in range(100):
y_pred = model(x_data) # __call__()调用forward()正向传播计算预测值
loss = criterion(y_pred, y_data) # 计算损失
print(epoch, loss.item()) # 可以直接打印loss,因为调用的是__str__()不会产生计算图
loss_list.append(loss.item()) # 保存loss

optimizer.zero_grad() # 将梯度归零,这一步要在下一轮计算反向传播之前进行
loss.backward() # 反向传播,计算梯度
optimizer.step() # 进行更新

print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())

x_test = torch.Tensor([[4.0]])
y_test = model(x_test) # 使用训练好的模型进行预测
print('y_pred = ', y_test.item())

# 打印图表
plt.plot(range(100), loss_list)
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.show()

课后作业

测试不同的优化器。除了LBFGS,只需要修改调用对应优化器的构造器。

Adagrad

w = 0.20570674538612366

b = -0.5057424902915955

y_pred = 0.31708449125289917

image-20210117152240566

Adam

w = 1.466607928276062

b = 0.14079217612743378

y_pred = 6.007224082946777

image-20210117152322232

Adamax

w = -0.022818174213171005

b = 0.9245702028274536

y_pred = 0.8332974910736084

image-20210117152149494

ASGD

w = 1.6153326034545898

b = 0.87442547082901

y_pred = 7.335755825042725

image-20210117151915659

LBFGS

由于LBFGS算法需要重复多次计算函数,因此需要传入一个闭包去允许它们重新计算模型。这个闭包应当清空梯度, 计算损失,然后返回。

参考一篇关于优化器的博文

训练模型部分代码应修改为:

python
1
2
3
4
5
6
7
8
9
10
11
12
def closure():
optimizer.zero_grad() # 将梯度归零
y_pred = model(x_data) # __call__()调用forward()正向传播计算预测值
loss = criterion(y_pred, y_data) # 计算损失
print(epoch, loss.item()) # 可以直接打印loss,因为调用的是__str__()不会产生计算图
loss_list.append(loss.item()) # 保存loss
loss.backward() # 反向传播


for epoch in range(100):
optimizer.step(closure()) # 进行更新

w = 1.7660775184631348

b = 0.531760573387146

y_pred = 7.596070766448975

image-20210117153648037

RMSprop

w = 1.734222650527954

b = 0.5857117176055908

y_pred = 7.522602081298828

image-20210117150905304

Rprop

w = 1.9997763633728027

b = 0.0004527860146481544

y_pred = 7.999558448791504

image-20210117150540886

SGD

w = 1.8483222723007202

b = 0.3447989821434021

y_pred = 7.738088130950928

image-20210117150219223