欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用PyTorch构建线性回归模型

程序员文章站 2024-03-21 11:01:22
...

写在前面

之前写过一篇博客,是不基于任何深度学习框架的纯numpy实现,其实整个过程比较繁琐。而深度学习框架能够提供给很多便利,避免一些重复性的工作,也使得代码变得更加简洁。本文主要是基于Pytorch框架实现一个线性回归模型。

基于numpy的线性回归模型构建:波士顿房价预测——线性模型(numpy实现)

代码

import依赖库

import torch
import torch.nn as nn
from time import time
import matplotlib.pyplot as plt
import random
import numpy as np
from mpl_toolkits.mplot3d import Axes3D 
%matplotlib inline

数据生成

人为地生成一批数据,基于y=2x1 - 3.4x2 + 4.2加上一些高斯噪声,从而生成一批数据(1000个)。

# 数据生成
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn((1000, 2), dtype=torch.float32)  # x的shape为[1000, 2]
labels = true_w[0]*features[:,0] + true_w[1]*features[:,1] + true_b 
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float32) # label为Wx+b+噪声
features.shape, labels.shape 

(torch.Size([1000, 2]), torch.Size([1000]))

# 绘制三维散点图
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(features[:,0], features[:,1], labels)
plt.show()

使用PyTorch构建线性回归模型

导入数据

使用torch.utils.data构造Dataset,然后导入

import torch.utils.data as Data  # 导入data包

# 定义batchsize
batch_size = 10
# 利用TensorDataset封装为一个torch的Dataset
dataset = Data.TensorDataset(features, labels)
# 使用DataLoader加载数据
data_iter = Data.DataLoader(dataset, batch_size=batch_size, shuffle=True)

构建模型

nn意为neural network,torch的nn模块提供了很多可直接调用的API用于构建网络模型。
nn的核心数据结构是Module,它是一个抽象概念,既可以表示神经网络中的某个层(layer),也可以表示一个包含很多层的神经网络。在实际使用中,最常见的做法是继承nn.Module,撰写自己的网络/层。一个nn.Module实例应该包含一些层以及返回输出的前向传播(forward)方法。

# 构建模型
class LinearNet(nn.Module):
    def __init__(self, n_feature):
        super().__init__()
        self.linear = nn.Linear(n_feature, 1)	# Linear层 传入input sample、output的size
        self.initialize_weight()  # 初始化权重
    
    def forward(self, x):
        z = self.linear(x)
        return z
    
    def initialize_weight(self):  # 参数初始化
        for m in self.modules(): 
            if isinstance(m, nn.Linear): # isinstance() 函数来判断一个对象是否是一个已知的类型
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)
                
net = LinearNet(2) # 实例化模型
print(net) # 打印网络结构
for name,param in net.named_parameters():  # 通过named_parameters()来查看网络的参数和对应的name
    print(name,param)

定义优化器和损失函数

使用torch.optim定义优化器,nn定义损失函数

# 定义优化器以及loss function
import torch.optim as optim

criterion = nn.MSELoss()  ## nn中提供了很多损失函数,这里使用MSE
optimizer = optim.SGD(net.parameters(), lr=0.03)  # optim提供了许多优化器选择 使用简单的SGD

还可以为不同子网络(层)设置不同的学习率,这在finetune时经常用到,下例与Linear Model无关,因为线性回归只有层。

optimizer = optim.SGD([
    # 如果对某个参数不指定学习率,就使用最外层的默认学习率
    {'params': net.subnet1.parameters()}, # lr=0.03
    {'params': net.subnet2.parameters(), 'lr':0.01}
], lr=0.03)

有时候对于学习率的调整,我们也不希望是一成不变的,希望是随着训练轮次的增加动态调整的。
这主要有两种做法:一种是修改optimizer.param_groups中对应的学习率,另一种是更简单也是较为推荐的做法——新建优化器,由于optimizer十分轻量级,构建开销很小,故而可以构建新的optimizer。

# 调整学习率
for param_group in optimizer.param_groups:
    param_group['lr'] *= 0.1 # 学习率为之前的0.1倍

但是后者对于使用动量的优化器(如Adam),会丢失动量等状态信息,可能会造成损失函数的收敛出现震荡等情况。

模型训练

epochs = 3
for epoch in range(epochs):
    epoch_loss = 0.
    for (x, y) in data_iter:
        optimizer.zero_grad()	# 梯度清零
        z = net(x) 	# 前向传递
        loss = criterion(z, y.view(-1,1)) # 求loss
        epoch_loss += loss.item()
        loss.backward()  # loss反向传播
        optimizer.step()	# 更新参数
    if (epoch+1)%1==0:
        print("epoch{} loss: {}".format(epoch+1, epoch_loss))

epoch1 loss: 94.01486129091427
epoch2 loss: 0.010219457717539626
epoch3 loss: 0.010239769631880336

检验

对比模型参数与标准设置参数:

for param in net.parameters():
    print(param)

for param in net.parameters():
print(param)

可以看到结果还是很不错的

参考

[1] https://github.com/ShusenTang/Dive-into-DL-PyTorch/blob/master/docs/chapter03_DL-basics/3.3_linear-regression-pytorch.md 《Dive-into-DL-PyTorch》
[2] https://zh.gluon.ai/chapter_deep-learning-basics/linear-regression-gluon.html 李沐《动手学深度学习》