详解使用pytorch实现线性回归

引言

pytorch作为一门主流的深度学习框架,API是非常多的,但使用pytorch搭建神经网络模型时,往往套路又较为固定。因此通过一些实际的例子去熟悉如何搭建模型、如何训练、测试数据,在实践中学习的效果往往要比漫无目的的学语法和API要好的多。

本文主要从最为基础的线性回归着手,严格来说线性回归更多的在机器学习中讨论,但是我们也能搭建一个简单的“神经网络”,用深度学习的套路来解决这个问题,而这个基本套路,即使是在搭建极为复杂的神经网络中也是适用的。

本文不会对深度学习的一些基本概念做过多的阐述,主要是以应用为导向,穿插着介绍pytorch的一些常用方法。

任务目标

线性回归的式子满足:y = w*x + b,其中w和b均为参数,x,y可以理解成特征值和目标值,在程序中我们指定x,w,b,目标值y也随之确定了下来。然后将x通过神经网络进行训练,我们希望通过训练,使得随机初始化的参数w和b能够逼近一开始我们指定的w,b,使得预测值pred和实际的目标值y能够无限接近。

初始化数据

这里我们假设有50个数据,采用随机赋值的方式定义x,然后取w=3,b=0.8,得到的y = 3 * x + 0.8作为目标值,这里x的随机赋值用pytorch的rand()方法实现:

# 1. 定义数据
x = torch.rand([50,1])
y = x*3 + 0.8

这里我们观察到,x的形状是[50,1],也就是50行1列的数据,50代表一共有50条数据,相当于batch_size,这里由于样本数量本身就不多,所以并没有分成多个batch,而是一次性将所有样本放到神经网络中去训练。

y通过上面的运算,形状也是[50,1],对应着x的目标值

构建模型

nn.Modultorch.nn提供的一个类,是pytorch中我们自定义网络的一个基类,在这个类中定义了很多有用的方法,让我们在继承这个类定义网络的时候非常简单。

当我们自定义网络的时候,有两个方法需要特别注意:

  1. __init__需要调用super方法,继承父类的属性和方法

  2. farward方法必须实现,用来定义我们的网络的向前计算(前向传播)的过程

用前面的y = wx+b的模型举例如下:

from torch import nn
class Lr(nn.Module):
    def __init__(self):
        super(Lr, self).__init__()  #继承父类init的参数
        self.linear = nn.Linear(1, 1) 

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

注意:

  1. nn.Linear为torch预定义好的线性模型,也被称为全链接层,传入的参数为输入的数量,输出的数量(in_features, out_features),是不算(batch_size的列数),在线性回归的模型中,x除了50这个batch_size的维度之后,其实际的特征只有一列,因输出也只要一列就足够,因此nn.Linear的参数是(1,1)。

  2. nn.Module定义了__call__方法,实现的就是调用forward方法,即Lr的实例,能够直接被传入参数调用,实际上调用的是forward方法并传入参数。如上面代码中所示,一般我们都在__init__方法中定义模型一些所需要的层,而在forward方法中,将传入模型的数据通过__init__方法中定义的层,还能在里面加一些激活函数等,也就是对数据进行处理,最后将这个值返回。

定义了模型后,使用这个模型可以分为以下两步:

# 实例化模型
model = Lr()
# 传入数据,计算结果
predict = model(x)

优化器类

优化器(optimizer),可以理解为torch为我们封装的用来进行更新参数的方法,比如常见的随机梯度下降(stochastic gradient descent,SGD)

优化器类都是由torch.optim提供的,例如

  1. torch.optim.SGD(参数,学习率)

  2. torch.optim.Adam(参数,学习率)

注意:

  1. 参数可以使用model.parameters()来获取,获取模型中所有requires_grad=True的参数

  2. 优化类的使用方法

    1. 实例化

    2. 所有参数的梯度,将其值置为0

    3. 反向传播计算梯度

    4. 更新参数值

示例如下:

optimizer = optim.SGD(model.parameters(), lr=1e-3) #1. 实例化,1e-3也可以写成0.001
optimizer.zero_grad() #2. 梯度置为0
loss.backward() #3. 计算梯度
optimizer.step()  #4. 更新参数的值

损失函数

前面的例子是一个回归问题,torch中也预测了很多损失函数

  1. 均方误差:nn.MSELoss(),常用于回归问题

  2. 交叉熵损失:nn.CrossEntropyLoss(),常用于分类问题

使用方法:

model = Lr() #1. 实例化模型
criterion = nn.MSELoss() #2. 实例化损失函数
optimizer = optim.SGD(model.parameters(), lr=1e-3) #3. 实例化优化器类
for i in range(100):
    y_predict = model(x_true) #4. 向前计算预测值
    loss = criterion(y_true,y_predict) #5. 调用损失函数传入真实值和预测值,得到损失结果
    optimizer.zero_grad() #5. 当前循环参数梯度置为0
    loss.backward() #6. 计算梯度
    optimizer.step()  #7. 更新参数的值

训练模型

在训练模型中,一般会有以下几个步骤:

  1. 定义一个epoch,代表需要将所有数据训练epoch个轮次
  2. 数据传入模型,获取预测值
  3. 将预测值和目标值传入损失函数,计算损失
  4. 优化器的梯度归零,在每次更新参数中必须进行此步骤,否则梯度会一直累加
  5. 计算梯度,此步骤在4之后进行
  6. 更新梯度,参数随之更新
  7. (可选)在训练过程中每隔一段时间打印下损失,观察收敛速度

代码如下:

#训练模型
for i in range(30000):
    out = model(x) #3.1 获取预测值
    loss = criterion(y,out) #3.2 计算损失
    optimizer.zero_grad()  #3.3 梯度归零
    loss.backward() #3.4 计算梯度
    optimizer.step()  # 3.5 更新梯度
    if (i+1) % 20 == 0:
        print('Epoch[{}/{}], loss: {:.6f}'.format(i,30000,loss.data))

模型评估/测试

在模型的测试中,我们一般会使用测试集来评估训练得到的模型,这时候我们不需要梯度相关的操作,只需要将数据通过模型,得到损失、精确率等即可。测试中有以下需要注意:

model.eval()表示设置模型为评估模式,即预测模式

model.train(mode=True) 表示设置模型为训练模式

在当前的线性回归中,上述并无区别

但是在其他的一些模型中,训练的参数和预测的参数会不相同,到时候就需要具体告诉程序我们是在进行训练还是预测,比如模型中存在DropoutBatchNorm的时候。

为了更直观的观察预测值和真实值的拟合情况,我们使用matplotlib绘制了两个图,代码如下:

# 训练模型
for i in range(30000):
    out = model(x) #3.1 获取预测值
    loss = criterion(y,out) #3.2 计算损失
    optimizer.zero_grad()  #3.3 梯度归零
    loss.backward() #3.4 计算梯度
    optimizer.step()  # 3.5 更新梯度
    if (i+1) % 20 == 0:
        print('Epoch[{}/{}], loss: {:.6f}'.format(i,30000,loss.data))

线性回归完整代码

import torch
from torch import nn
from torch import optim
import numpy as np
from matplotlib import pyplot as plt

# 1. 定义数据
x = torch.rand([50,1])
y = x*3 + 0.8

# 2 .定义模型
class Lr(nn.Module):
    def __init__(self):
        super(Lr,self).__init__()
        self.linear = nn.Linear(1,1)
		
    def forward(self, x):
        out = self.linear(x)
        return out

# 2. 实例化模型,loss,和优化器
model = Lr()
# 损失函数
criterion = nn.MSELoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=1e-3)
# 3. 训练模型
for i in range(30000):
    out = model(x) #3.1 获取预测值
    loss = criterion(y,out) #3.2 计算损失
    optimizer.zero_grad()  #3.3 梯度归零
    loss.backward() #3.4 计算梯度
    optimizer.step()  # 3.5 更新梯度
    if (i+1) % 20 == 0:
        print('Epoch[{}/{}], loss: {:.6f}'.format(i,30000,loss.data))

# 4. 模型评估
model.eval() #设置模型为评估模式,即预测模式
predict = model(x)
predict = predict.data.numpy()
plt.scatter(x.data.numpy(),y.data.numpy(),c="r")
plt.plot(x.data.numpy(),predict)
plt.show()

在GPU上运行

深度学习的一些模型一般都会用到GPU来进行训练,此时我们需要将代码稍作调整:

  1. 判断GPU是否可用torch.cuda.is_available()

    torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    >>device(type='cuda', index=0)  #使用gpu
    >>device(type='cpu') #使用cpu
  1. 把模型参数和input数据转化为cuda的支持类型

    model.to(device)
    x_true.to(device)
  1. 在GPU上计算结果也为cuda的数据类型,需要转化为numpy或者torch的cpu的tensor类型

    predict = predict.cpu().detach().numpy() 

    detach()的效果和data的相似,但是detach()是深拷贝,data是取值,是浅拷贝

GPU上运行的完整代码

import torch
from torch import nn
from torch import optim
import numpy as np
from matplotlib import pyplot as plt
import time

# 1. 定义数据
x = torch.rand([50,1])
y = x*3 + 0.8

# 2 .定义模型
class Lr(nn.Module):
    def __init__(self):
        super(Lr,self).__init__()
        self.linear = nn.Linear(1,1)

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

# 2. 实例化模型,loss,和优化器

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
x,y = x.to(device),y.to(device)

model = Lr().to(device)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)

# 3. 训练模型
for i in range(30000):
    out = model(x)
    loss = criterion(y,out)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if (i+1) % 20 == 0:
        print('Epoch[{}/{}], loss: {:.6f}'.format(i,30000,loss.data))

# 4. 模型评估
model.eval()
predict = model(x)
predict = predict.cpu().detach().numpy() #转化为numpy数组
plt.scatter(x.cpu().data.numpy(),y.cpu().data.numpy(),c="r")
plt.plot(x.cpu().data.numpy(),predict)
plt.show()

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值