线性回归从零开始实现

目录

前言

一、导入包和模块

三、数据读取

 四、初始化模型参数

五、定义模型

六、定义损失函数

 七、定义优化算法

八、模型训练


前言

博客主要将李沐大神的《动手学pytorch》中的代码进行了详细的注释,为了自己以后复习的时候方便。


一、导入包和模块

导入所需的包或模块,其中的matplotlib包可用于作图,且设置成嵌入显示。

import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random

二、生成数据集

我们构造一个简单的人工训练数据集,它可以使我们能够直观比较学到的参数和真实的模型参数的区别。设训练数据集样本数为1000,输入特征数为2。给定随机生成的批量样本特征X\epsilon \mathbb{R}^{^{1000\times 2}},线性回归模型的真实权重w=[2,-3.4]^{T},偏差b=4.2,以及一个随机噪声项\epsilon来生成标签

y=Ww+b+\epsilon

其中噪声项\epsilon服从均值为0,标准差为0.01的正态分布。

# 特征数量为2
num_inputs = 2
# 样本数量为1000
num_examples = 1000
# 回归模型真实权重
true_w = [2, -3.4]
# 回归模型偏差
true_b = 4.2
# 特征图形状[1000,2],数据类型float32
features = torch.randn(num_examples, num_inputs,
                       dtype=torch.float32)
# 标签y=2*所有样本的第一个特征——3.4*所有样本的第二个特征
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
# 标签中加入随机噪声,噪声服从均值为0方差为1的正态分布,尺寸和标签尺寸相同,数据类型为torch.float32
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),
                       dtype=torch.float32)

通过生成第二个特征features[:, 1]和标签 labels 的散点图,可以更直观地观察两者间的线性关系。

def use_svg_display():
    # 用矢量图显示
    display.set_matplotlib_formats('svg')

def set_figsize(figsize=(3.5, 2.5)):
    use_svg_display()
    # 设置图的尺寸
    plt.rcParams['figure.figsize'] = figsize

set_figsize()
plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);

线性回归从零开始实现_第1张图片

三、数据读取

在训练模型的时候,我们需要遍历数据集并不断读取小批量数据样本。这里我们定义一个函数:它每次返回batch_size(批量大小)个随机样本的特征和标签。

def data_iter(batch_size, features, labels):
    num_examples = len(features)  # 样本个数为特征图的数据长度
    indices = list(range(num_examples))  # 建立列表用以存放样本
    random.shuffle(indices)  # 样本的读取顺序是随机的
    for i in range(0, num_examples, batch_size):  # 用for循环逐批次的选取数据
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch
        yield  features.index_select(0, j), labels.index_select(0, j)

让我们读取第一个小批量数据样本并打印。每个批量的特征形状为(10, 2),分别对应批量大小和输入个数;标签形状为批量大小。

# 每小批量样本个数为10
batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, y)
    break

输出:

线性回归从零开始实现_第2张图片

 四、初始化模型参数

这里要初始化的模型参数为w和b

将权重w初始化为均值为0,标准差为0.01的正态随机数,数据类型为float32

偏差b初始化为0,数据类型为float32

# 初始化权重w
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32)
# 初始化偏差b
b = torch.zeros(1, dtype=torch.float32)

之后的模型训练中,需要对这些参数求梯度来迭代参数的值,因此我们要让它们的requires_grad=True

w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True) 

五、定义模型

下面是线性回归的矢量计算表达式的实现。我们使用mm函数做矩阵乘法。

def linreg(X, w, b): 
    return torch.mm(X, w) + b

六、定义损失函数

我们使用平方损失来定义线性回归的损失函数。在实现中,我们需要把真实值y变形成预测值y_hat的形状。以下函数返回的结果也将和y_hat的形状相同。

平方损失公式:

def squared_loss(y_hat, y):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    # 注意这里返回的是向量, 另外, pytorch里的MSELoss并没有除以 2
    # y.view(y_hat.size())把真实值y变形成预测值y_hat的形状
    return (y_hat - y.view(y_hat.size())) ** 2 / 2

 七、定义优化算法

以下的sgd函数实现了小批量随机梯度下降算法。它通过不断迭代模型参数来优化损失函数。这里自动求梯度模块计算得来的梯度是一个批量样本的梯度和。我们将它除以批量大小来得到平均值。

# params为更新的参数。lr为学习率
# 自动求梯度模块计算得来的梯度是一个批量样本的梯度和,将它除以批量大小来得到平均值
def sgd(params, lr, batch_size): 
    for param in params:
        param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data

八、模型训练

在训练中,我们将多次迭代模型参数。在每次迭代中,我们根据当前读取的小批量数据样本(特征X和标签y),通过调用反向函数backward计算小批量随机梯度,并调用优化算法sgd迭代模型参数。由于我们之前设批量大小batch_size为10,每个小批量的损失l的形状为(10, 1)。由于变量l并不是一个标量,所以我们可以调用.sum()将其求和得到一个标量,再运行l.backward()得到该变量有关模型参数的梯度。注意在每次更新完参数后不要忘了将参数的梯度清零。

在一个迭代周期(epoch)中,我们将完整遍历一遍data_iter函数,并对训练数据集中所有样本都使用一次(假设样本数能够被批量大小整除)。这里的迭代周期个数num_epochs和学习率lr都是超参数,分别设3和0.03。在实践中,大多超参数都需要通过反复试错来不断调节。虽然迭代周期数设得越大模型可能越有效,但是训练时间可能过长。

lr = 0.03  # 学习率
num_epochs = 3  # 迭代周期
net = linreg
loss = squared_loss

for epoch in range(num_epochs):  # 训练模型一共需要num_epochs个迭代周期
    # 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。
    # X和y分别是小批量样本的特征和标签
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y).sum()  # l是有关小批量X和y的损失
        l.backward()  # 小批量的损失对模型参数求梯度
        sgd([w, b], lr, batch_size)  # 使用小批量随机梯度下降迭代模型参数

        # 不要忘了梯度清零
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

训练输出:

epoch 1, loss 0.000054
epoch 2, loss 0.000054
epoch 3, loss 0.000054

训练完成后,我们可以比较学到的参数和用来生成训练集的真实参数。它们应该很接近。

线性回归从零开始实现_第3张图片

参考书目:《动手学深度学习》(pytorch版) 李沐 著

你可能感兴趣的:(pytorch深度学习框架,线性回归,python,算法,深度学习,pytorch)