导入所需要的库

import torch
import random

生成数据集

构造一个简单的人工训练数据集,设训练数据集样本数为1000,输入个数(特征数)为2。使用线性回归模型真实权重$w=[2,-3.4]^T$和偏差$b=4.2$,以及一个随机噪声项$\epsilon$生成标签。

其中噪声项 $\epsilon$ 服从均值为0、标准差为0.01的正态分布。噪声代表了数据集中无意义的干扰。

num_inputs=2   #特征数
num_examples=1000  #样本数
true_w=[2,-3.4]  #真实权重
true_b=4.2   #真实偏差
features=torch.randn(num_examples,num_inputs,dtype=torch.float32)  #生成特征
labels=true_w[0]*features[:,0]+true_w[1]*features[:,1]+true_b  #生成标签 [:,0]表示取所有行的第0个元素
labels+=torch.tensor(torch.normal(0,0.01,size=labels.size()),dtype=torch.float32)  #加入噪声

读取数据

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

def data_iter(batch_size, features, labels):   #batch_size:批量大小,features:特征,labels:标签
    num_examples=len(features)  #样本数    
    indices=list(range(num_examples))   #样本的索引
    random.shuffle(indices)  #样本读取顺序随机  shuffle() 方法将序列的所有元素随机排序
    for i in range(0,num_examples,batch_size):  #每次取batch_size个样本
        j=torch.LongTensor(indices[i:min(i+batch_size,num_examples)])  #最后一次可能不足一个batch,LongTensor()将列表转换为张量
        yield features.index_select(0,j),labels.index_select(0,j)   #index_select(0,j)取features和labels的第j行

batch_size = 10

初始化模型参数

将权重初始化成均值为0、标准差为0.01的正态随机数,偏差则初始化成0。

w=torch.tensor(torch.normal(0,0.01,size=(num_inputs,1)),dtype=torch.float32)
b=torch.zeros(1,dtype=torch.float32)

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

w.requires_grad_(requires_grad=True) #设置为True表示会被求导
b.requires_grad_(requires_grad=True)

定义模型

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

def linreg(X,w,b):  #定义线性回归模型
    return torch.mm(X,w)+b  #torch.mm()矩阵乘法

定义损失函数

先将真实值y变形成预测值y_hat的形状。以下函数返回的结果也将和y_hat的形状相同。

def squared_loss(y_hat,y):  #定义损失函数
    return (y_hat-y.view(y_hat.size()))**2/2  #返回的是向量  view()函数作用是将一个多行的Tensor,拼接成一行

定义优化算法

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

def sgd(params,lr,batch_size):  #定义优化算法,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):  #每次取batch_size个样本
        l=loss(net(X,w,b),y).sum()  #计算损失
        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()))  #mean()求平均值,item()将标量Tensor转换为Python数字

print(true_w, '\n', w)
print(true_b, '\n', b)

循之际,如星夜般的幻想。