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的用法:

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. 控制训练次数,不能过少(训练不到位),也不能过多(过拟合)

代码实现

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算法需要重复多次计算函数,因此需要传入一个闭包去允许它们重新计算模型。这个闭包应当清空梯度, 计算损失,然后返回。

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

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

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