03_利用pytorch解决线性回归问题
- 2021 年 4 月 15 日
- 筆記
- torch_tang
03_利用pytorch解决线性回归问题
一、引言
上一篇文章我们利用numpy解决了线性回归问题,我们能感觉到他的麻烦之处,很多数学性的方法都需要我们自己亲手去实现,这对于数学不好的同学来说,简直就是灾难,让你数学又好并且码代码能力又强,臣妾做不到呀!因此我们说到,可以利用torch这个框架简化其中的很多操作,接下来就让我们提前体验下torch的强大,由于直接上代码,有些代码可能你可能看不懂,但没关系,能看懂torch的强大就行。
由于上一篇已经详细的介绍了线性回归模型的流程,我们这里就不再次罗嗦了,直接把线性回归的5个步骤贴过来:
- 初始化未知变量\(w=b=0\)
- 得到损失函数\(loss = \frac{1}{N}\sum_{i=1}^N{(\hat{y_i}-y_i)}^2\)
- 利用梯度下降算法更新得到\(w’, b’\)
- 重复步骤3,利用\(w’, b’\)得到新的更优的\(w’, b’\),直至\(w’, b’\)收敛
- 最后得到函数模型\(f=w’*x+b’\)
但是这里为了体现torch的强大,我们使用了神经网络全连接层的概念,但是用的是一层网络,相信不会难倒你。有人很好奇,为什么我这样做,我想说:如果不使用神经网络,我为什么不用sklearn框架做个线性回归给你体验下框架的强大呢?三行代码一套搞定。而且一层神经网络可以看做是感知机模型,而感知机模型无非就是在线性回归模型的基础上加了一个sgn函数。
二、利用torch解决线性回归问题
2.1 定义x和y
在下面的代码中,我们定义的x和y都是ndarray数据的格式,所以在之后的处理之中需要通过torch的from_numpy()
方法把ndarray格式的数据转成tensor类型。
其中x为一维数组,例如:[1,2,3,4,5,……]
其中y假设\(y=2*x+3\)
import numpy as np
# torch里要求数据类型必须是float
x = np.arange(1, 12, dtype=np.float32).reshape(-1, 1)
y = 2 * x + 3
2.2 自定制线性回归模型类
由于torch内部封装了线性回归算法,我们只需要继承它给我们提供的模型类即可,然后通过Python中类的继承做出一些灵活的改动。(如果你对继承不熟悉,强烈推荐回炉重造Python)下面给出代码:
import torch
import torch.nn as nn
# 继承nn.module,实现前向传播,线性回归直接可以看做是全连接层
class LinearRegressionModel(nn.Module):
def __init__(self, input_dim, output_dim):
super().__init__() # 继承父类方法
self.linear = nn.Linear(input_dim, output_dim) # 定义全连接层,其中input_dim和output_dim是输入和输出数据的维数
# 定义前向传播算法
def forward(self, inp):
out = self.linear(inp) # 输入x后,通过全连接层得到输入出结果out
return out # 返回被全连接层处理后的结果
# 定义线性回归模型
regression_model = LinearRegressionModel(1, 1) # x和y都是一维的
2.3 指定gpu或者cpu
# 可以通过to()或者cuda()使用GPU进行模型的训练,需要将模型和数据都转换到GPU上,也可以指定具体的GPU,如.cuda(1)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
regression_model.to(device)
2.4 设置参数
epochs = 1000 # 训练次数
learning_rate = 0.01 # 学习速率
optimizer = torch.optim.SGD(regression_model.parameters(), learning_rate) # 优化器(未来会详细介绍),这里使用随机梯度下降算法(SGD)
criterion = nn.MSELoss() # 使用均方误差定义损失函数
2.5 训练
for epoch in range(epochs):
# 数据类型转换
inputs = torch.from_numpy(x).to(device) # 由于x是ndarray数组,需要转换成tensor类型,如果用gpu训练,则会通过to函数把数据传入gpu
labels = torch.from_numpy(y).to(device)
# 训练
optimizer.zero_grad() # 每次求偏导都会清零,否则会进行叠加
outputs = regression_model(inputs) # 把输入传入定义的线性回归模型中,进行前向传播,得到预测结果
loss = criterion(outputs, labels) # 通过均方误差评估预测误差
loss.backward() # 反向传播
optimizer.step() # 更新权重参数
# 每50次循环打印一次结果
if epoch % 50 == 0:
print("epoch:", epoch, "loss:", loss.item())
predict = regression_model(torch.from_numpy(x).requires_grad_()).data.numpy() # 通过训练好的模型预测结果
2.6 保存模型
torch.save(regression_model.state_dict(), "model.pk1") # 保存模型
result = regression_model.load_state_dict(torch.load("model.pk1")) # 加载模型
三、代码汇总
# author : 'nickchen121';
# date: 14/4/2021 20:11
import numpy as np
# torch里要求数据类型必须是float
x = np.arange(1, 12, dtype=np.float32).reshape(-1, 1)
y = 2 * x + 3
import torch
import torch.nn as nn
# 继承nn.module,实现前向传播,线性回归直接可以看做是全连接层
class LinearRegressionModel(nn.Module):
def __init__(self, input_dim, output_dim):
super().__init__() # 继承父类方法
self.linear = nn.Linear(input_dim, output_dim) # 定义全连接层,其中input_dim和output_dim是输入和输出数据的维数
# 定义前向传播算法
def forward(self, inp):
out = self.linear(inp) # 输入x后,通过全连接层得到输入出结果out
return out # 返回被全连接层处理后的结果
# 定义线性回归模型
regression_model = LinearRegressionModel(1, 1) # x和y都是一维的
# 可以通过to()或者cuda()使用GPU进行模型的训练,需要将模型和数据都转换到GPU上,也可以指定具体的GPU,如.cuda(1)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
regression_model.to(device)
epochs = 1000 # 训练次数
learning_rate = 0.01 # 学习速率
optimizer = torch.optim.SGD(regression_model.parameters(), learning_rate) # 优化器,这里使用随机梯度下降算法(SGD)
criterion = nn.MSELoss() # 使用均方误差定义损失函数
for epoch in range(epochs):
# 数据类型转换
inputs = torch.from_numpy(x).to(device) # 由于x是ndarray数组,需要转换成tensor类型,如果用gpu训练,则会通过to函数把数据传入gpu
labels = torch.from_numpy(y).to(device)
# 训练
optimizer.zero_grad() # 每次求偏导都会清零,否则会进行叠加
outputs = regression_model(inputs) # 把输入传入定义的线性回归模型中,进行前向传播
loss = criterion(outputs, labels) # 通过均方误差评估预测误差
loss.backward() # 反向传播
optimizer.step() # 更新权重参数
# 每50次循环打印一次结果
if epoch % 50 == 0:
print("epoch:", epoch, "loss:", loss.item())
predict = regression_model(torch.from_numpy(x).requires_grad_()).data.numpy() # 通过训练好的模型预测结果
# torch.save(regression_model.state_dict(), "model.pk1") # 保存模型
# result = regression_model.load_state_dict(torch.load("model.pk1")) # 加载模型
四、总结
本篇文章从torch的角度去解决了线性回归问题,细节你可能不懂,但也可以发现它是非常简单的,全程没有让你去实现优化器、去实现全连接层、去实现反向传播,在这里你就不需要去实现一个数学公式。你需要做的仅仅是成为一个优秀的调包侠,并且努力成为一个伟大的调参师即可。
至于为什么直接上代码,而不是先讲解torch的基础,一定是有深意的。站得高看得远,先带你领略下torch的用途及其强大,然后我们再慢慢的一步一步筑基。