用PyTorch实现线性回归
线性回归的基本概念
概念:只具有一个神经元的最简单的神经网络
步骤:
- 构建数据集
- 设计模型,前馈,用来计算预测 y ˉ \bar{y} yˉ
- 构造损失函数和优化器,使用PyTorch的API
- 训练周期,前馈(算损失) =》 反馈(算梯度)=》更新(更新权重)
numpy有一种机制,称为广播机制
如下例:3 * 3 + 3 * 1自动广播为3 * 3 + 3 * 3
设计模型:
输入数据都是矩阵。必须确定x和 y ˉ \bar{y} yˉ的维度,我们才能够得出w(权重)、b(偏执量)的维度
loss最终需要是一个标量,如果是一个向量是无法进行backward的,但经过上述矩阵计算我们得到的还是一个矩阵,因此对所有loss求平均值,在进行backward()。
有时需要做转置拼维度,目的都是为了把矩阵拼出来
torch.nn.Linear类的介绍
torch.nn.Linear类实现了魔术方法__call()__,这使得该类的实例可以像函数一样被调用,通常forward()会被调用。
class Foobar:
def __init__(self):
super().__init__()
print('__init__函数被调用了')
pass
def __call__(self, *args, **kwargs):
# *args 可变长参数(无名参数) 把前面n个参数变成n元组
# **kwargs 把参数变成词典词典 x=2, y=3 ==> {x:2, y:3}
print('__call__函数被调用了')
print('Hello:' + str(args[0]))
print('Hello:' + str(kwargs))
self.forward()
def forward(self):
print('forward函数被调用了')
foobar = Foobar()
foobar(1, 2, 3, x = 1, y = 2)
运算结果:
__init__函数被调用了
__call__函数被调用了
Hello:1
Hello:{'x': 1, 'y': 2}
forward函数被调用了
线性回归代码实现:
import torch
import matplotlib.pyplot as plt
# 数据集 3行1列的矩阵
x_data = torch.tensor([[1.0], [2.0], [3.0]])
y_data = torch.tensor([[2.0], [4.0], [6.0]])
# 模型定义成一个类,将来可以扩展模型,以适应于各种各样的任务
# 所有类都要继承Module,里面有好多方法可以直接使用
# nn 是 Netural Network的缩写
class LinearModel(torch.nn.Module):
# 类中至少要实现两个函数
# 1. 构造函数:用来初始化对象
def __init__(self):
# 调用父类的构造函数 LinearModel:模型名称
super(LinearModel, self).__init__()
# torch.nn.Linear是PyTorch里面的一个类
# torch.nn.Linear(1, 1) 在构造一个对象,包含了权重和偏置两个tensor
# 可以自动来完成 x*w + b的计算
# Linear(1, 1)也是继承自Module,也可以自动进行backward计算
self.linear = torch.nn.Linear(1, 1)
# 参数:in_features 输入纬度 out_features 输出纬度 bias 默认为True
# 2. 前馈函数:进行前馈需要执行的计算
# pytorch在nn.Module中,实现了__call__方法,而在__call__方法中调用了forward函数
# 因此新写的类中需要重写forward()以覆盖掉父类中的forward()
# 该函数的另一个作用是可以直接在对象后面加()
# 例如实例化的model对象,和实例化的linear对象
# pytorch也是按照__init__, __call__, forward三个函数实现网络层之间的架构的
def forward(self, x):
y_pred = self.linear(x) # 对象后面加(),说明实现了一个可调用的对象
return y_pred
# 用Module构造出来的对象,会根据计算图自动进行backward()的计算
# 如果自己定义的模型,没办法进行求倒数
# 方法一:模块由PyTorch基本计算封装成类,实例化Module,调用即可
# 方法二:自己有更快的计算方式,可以在Functions类里构造计算块,进行继承
# 当然,直接用Module里面的模块算是最简单的
# 模型实例化
model = LinearModel()
criterion = torch.nn.MSELoss(reduction='sum')
# size_average = False 损失是否求均值 没啥用 python新版本改成了 ==》reduction='sum'
# reduction-三个值,none: 不使用约简;mean:返回loss和的平均值;sum:返回loss的和。默认:mean。
# 做mini batch 可以设置为True 不过影响也不是很大
# reduce 用来确定是否降维 不考虑
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 优化器与Module无关,不会构建计算图
# 传入数据为单个,为随机梯度下降
# 传入数据为batch,为批量梯度下降,此处为SGD
# 类SGD model.parameters() 把linear里的w都拿出来
# lr 学习率
# 1.先算y_pred
# 2. loss 梯度清0
# 3. backward
# 4. update
epoch_list = []
loss_list = []
for epoch in range(100):
y_pred = model(x_data) # forward 计算y_pred
loss = criterion(y_pred, y_data) # 计算损失
# print(type(loss))
print(epoch, loss.item())
epoch_list.append(epoch)
loss_list.append(loss.item())
optimizer.zero_grad() # 梯度归0
loss.backward() # backward 计算梯度
optimizer.step() # update w, b
# 输出权重和偏置
print('w=', model.linear.weight.item())
# weight是一个矩阵,item()才是值
print('b=', model.linear.bias.item())
# Test Model
x_test = torch.tensor([[4.0]])
y_test = model(x_test)
print('y_pred=', y_test.item())
# 画图
figure = plt.figure("线性回归")
ax = figure.add_subplot()
ax.plot(epoch_list, loss_list)
plt.show()
作业:使用不同优化器查看其区别
- torch.optim.Adagrad
- torch.optim.Adam
- torch.optim.Adamax
- torch.optim.ASGD
- torch.optim.LBFGS
- torch.optim.RMSprop
- torch.optim.Rprop
- torch.optim.SGD