动手学深度学习01 线性回归从零实现
1、导入库
# 设置图片画在网页上
%matplotlib inline
import random
import torch
from d2l import torch as d2l
2、人工模拟数据
# 定义一个方法,生成人造数据集
def synthetic_data(w, b, num_examples):
"""生成 y = Xw + b + 噪声"""
# 返回一个随机的张量
# normal(均值,标准差,可选的输出张量)
X = torch.normal(0, 1, (num_examples, len(w)))
# matul 返回两个矩阵的乘积
y = torch.matmul(X, w) + b
# 加入噪音
y += torch.normal(0, 0.01, y.shape)
# 把X 和 y 弄成列向量返回
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)
3、查看数据
# 查看训练样本的样子
print('features:', features[0], '\nlabel:', labels[0])
# 输出数据集图像
# 设置图表大小
d2l.set_figsize()
# 等于matplotlib的scatter,用来绘制散点图
# detach保留数值,去除梯度
d2l.plt.scatter(features[:,1].detach().numpy(), labels.detach().numpy(), 1)
如图所示是人工生成的数据集,可以明显看出呈线性分布。
4、读取数据
# 定义一个方法实现每次读取一个小批量
def data_iter(batch_size, features, labels):
num_examples = len(features)
inices = list(range(num_examples))
# 打乱顺序,使读取样本随机,没有特定顺序
random.shuffle(inices)
# 遍历数组,每次读取batch_size长度的数据
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(inices[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):
# 打印 一次 小批量的样本, 样本大小与batch
print(X, '\n' , y)
break
5、定义参数和模型
# 定义初始化模型参数
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
# 置零
b = torch.zeros(1, requires_grad=True)
# 定义模型
def linreg(X, w, b):
"""线性回归模型, Xw+偏差"""
return torch.matmul(X,w) + b
6、定义损失函数和优化算法
# 定义损失函数
def squared_loss(y_hat, y):
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape))**2 / 2
# 定义优化算法
# params 是传进来的list 数据 , lr是学习率, batch_size是步长
# 因为我们计算的损失是一个批量样本的总和,所以我们用批量大小(batch_size)来归一化步长,这样步长大小就不会取决于我们对批量大小的选择
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param-= lr * param.grad / batch_size
# torch不会自动把梯度归零,手动归零,使下一次用到梯度就不会和上一次相关
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, l),而不是一个标量.l 中的所有元素被加到 y
# 并计算[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}')