本篇文章从torch的角度去解决了线性回归问题,细节你可能不懂,但也可以发现它是非常简单的,全程没有让你去实现优化器、去实现全连接层、去实现反向传播,在这里你就不需要去实现一个数学公式。你需要做的仅仅是成为一个优秀的调包侠,并且努力成为一个伟大的调参师即可。 至于为什么直接上代码,而不是先讲解torch的基础,一定是有深意的。站得高看得远,先带你领略下torch的用途及其强大,然后我们再慢慢的一步一步筑基。

03_利用pytorch解决线性回归问题


目录


一、引言

上一篇文章我们利用numpy解决了线性回归问题,我们能感觉到他的麻烦之处,很多数学性的方法都需要我们自己亲手去实现,这对于数学不好的同学来说,简直就是灾难,让你数学又好并且码代码能力又强,臣妾做不到呀!因此我们说到,可以利用torch这个框架简化其中的很多操作,接下来就让我们提前体验下torch的强大,由于直接上代码,有些代码可能你可能看不懂,但没关系,能看懂torch的强大就行。

由于上一篇已经详细的介绍了线性回归模型的流程,我们这里就不再次罗嗦了,直接把线性回归的5个步骤贴过来:

  1. 初始化未知变量\(w=b=0\)
  2. 得到损失函数\(loss = \frac{1}{N}\sum_{i=1}^N{(\hat{y_i}-y_i)}^2\)
  3. 利用梯度下降算法更新得到\(w', b'\)
  4. 重复步骤3,利用\(w', b'\)得到新的更优的\(w', b'\),直至\(w', b'\)收敛
  5. 最后得到函数模型\(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的用途及其强大,然后我们再慢慢的一步一步筑基。