1.生成数据集
下面简单的构造一个人工数据集,包含1000个样本的数据集, 标签y=xw+b
import matplotlib.pyplot as plt
import random
import torch
from d2l import torch as d2l
def synthetic_data(w,b,num_examples):
x=torch.normal(0,1,(num_examples,len(w)))
y=torch.matmul(x,w)+b
y+=torch.normal(0,0.01,y.shape)
return x,y.reshape((-1,1))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
plt.show()
2.读取数据集
list函数用于将可迭代对象转换成列表
random.shuffle函数可将一个序列随机打乱,直接修改原序列,不会返回新序列
indices[i: min(i + batch_size, num_examples)] 切片操作
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):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
tensor([[ 0.3733, -2.2567],
[ 0.3012, 0.6140],
[-0.1052, 0.1869],
[-1.3305, -0.4779],
[ 0.4807, 1.8884],
[-1.2313, -0.1585],
[-0.4149, -1.6424],
[ 0.6117, 1.0431],
[ 0.1700, -0.9059],
[ 0.0264, -2.4530]])
tensor([[12.6177],
[ 2.7099],
[ 3.3307],
[ 3.1587],
[-1.2597],
[ 2.2788],
[ 8.9460],
[ 1.8822],
[ 7.6181],
[12.5719]])
3.初始化模型参数
在我们开始用小批量随机梯度下降优化我们的模型参数之前, 我们需要先有一些参数。 在下面的代码中,我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True) b = torch.zeros(1, requires_grad=True)
在初始化参数之后,我们的任务是更新这些参数,直到这些参数足够拟合我们的数据。 每次更新都需要计算损失函数关于模型参数的梯度。 有了这个梯度,我们就可以向减小损失的方向更新每个参数。 因为手动计算梯度很枯燥而且容易出错,所以没有人会手动计算梯度。 我们使用 2.5节中引入的自动微分来计算梯度。
4.定义模型
接下来,我们必须定义模型,将模型的输入和参数同模型的输出关联起来。 回想一下,要计算线性模型的输出, 我们只需计算输入特征X和模型权重w的矩阵-向量乘法后加上偏置b。 注意,上面的Xw是一个向量,而b是一个标量。 回想一下 2.1.3节中描述的广播机制: 当我们用一个向量加一个标量时,标量会被加到向量的每个分量上。
def linreg(X, w, b): #@save """线性回归模型""" return torch.matmul(X, w) + b
5.定义损失函数
因为需要计算损失函数的梯度,所以我们应该先定义损失函数。 这里我们使用 3.1节中描述的平方损失函数。 在实现中,我们需要将真实值y
的形状转换为和预测值y_hat
的形状相同。
def squared_loss(y_hat, y): #@save """均方损失""" return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
6.定义优化算法
正如我们在 3.1节中讨论的,线性回归有解析解。 尽管线性回归有解析解,但本书中的其他模型却没有。 这里我们介绍小批量随机梯度下降。
在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。 接下来,朝着减少损失的方向更新我们的参数。 下面的函数实现小批量随机梯度下降更新。 该函数接受模型参数集合、学习速率和批量大小作为输入。每 一步更新的大小由学习速率lr
决定。 因为我们计算的损失是一个批量样本的总和,所以我们用批量大小(batch_size
) 来规范化步长,这样步长大小就不会取决于我们对批量大小的选择。
def sgd(params, lr, batch_size): #@save """小批量随机梯度下降""" with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size param.grad.zero_()
7.训练
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
epoch 1, loss 0.036914
epoch 2, loss 0.000133
epoch 3, loss 0.000056