导入所需要的库
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)
Comments | NOTHING