1.引入相关的模块。
%matplotlib inline
import random
import torch
from d2l import torch as d2l
2.
def synthetic_data(w, b, num_examples): #@save
"""生成y=Xw+b+噪声"""
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)
函数用于生成合成数据,
参数w:表示线性回归模型中的权重,一个一维张量,第一个元素为2,第二个元素为-3.4。
b:表示回归模型中的偏置。
x:生成1000个,均值为0,标准差为1。每个样本有len(w)即两个特征数量。
y:x和w相乘,加上偏置值,y包括1000个标量。
print('features:', features[0],'\nlabel:', labels[0])
读取x,y的第一个元素。
d2l.set_figsize()
d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1);
画出图像,表示x,y的线性关系。
3.读取数据集
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表示样本的数量,random.shuffle对 列表进行随机排序,打乱样本的顺序。
就可以从乱序后的样本列表中顺序读取一段连续的下标,得到一组小批量的样本。
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
具体实现。
4.初始化模型参数并定义模型
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
requires_grad=True用于指定需要计算该张量的梯度。
def linreg(X, w, b): #@save
"""线性回归模型"""
return torch.matmul(X, w) + b
定义模型,返回预测结果。
5.定义损失函数和优化算法
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
y_hat表示预测值,y.reshape()用于改变y的形状,计算其差值的平方并除以2作为损失值。
真实值y不一定有预测值这么多特征,需要改变形状。
优化算法
def sgd(params, lr, batch_size): #@save
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
对于每一个在params中的param,对其利用学习率lr和样本数进行更新。
parm.grad表示其梯度,对参数进行修改后将其梯度设为0。
为什么要将其设为0?
防止下一次不会受到之前的梯度信息的影响,从而保证了模型参数更新计算的正确性。
(梯度信息以一种什么方式存在,有什么意义)
torch.no_grad()的作用?
临时关闭梯度计算
6.模型训练
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}')
学习率0.03,训练轮数3.
每一个训练轮次中:
对于在数据集中的小批量样本中的x,y,计算预测与实际值的偏差。
l.sum().backward()的作用?
用sgd函数更新参数,直到当前小批量样本中的参数全部更新完。
每一轮小批量样本更新之后的平均损失函数值。