Class1线性回归从零开始实现代码
%matplotlib inline
# 可以直接显示matplotlib绘制的图表
# 导入内置随机数生成模块,用于生成随机数、随机选择元素等
import random
# 导入Pytorch库
import torch
# 导入d2l包
from d2l import torch as d2l
# 合成线性回归数据集
# w:权重张量
# b:偏置
# num_examples:样本数量
def synthetic_data(w,b,num_examples):
"""生成 y = Xw + b + 噪声"""
# 生成均值为0,方差为1,n个个数,w长度的张量
X = torch.normal(0,1,(num_examples,len(w)))
# 计算XW + b
y = torch.matmul(X,w) + b
# 添加均值为0,标准差为0.01,与y形状相同的噪声
y += torch.normal(0,0.01,y.shape)
# 将形状变为二维(1000,1),符合后续模型训练格式
return X,y.reshape((-1,1))
# 设置真实权重true_w
true_w = torch.tensor([2,-3.4])
# 设置真实偏置
true_b = 4.2
# features:特征矩阵
# labels:噪声
features,labels = synthetic_data(true_w,true_b,1000)
# features:形状为(1000,2),1000个样本,每个样本有2个特征
# features[0]:第0个样本的特征向量
# labels:形状为(1000,1),1000个样本对应的目标值
# labels[0]:第0个样本标签,形状为(1,)的张量
print('features:',features[0],'\nlabel:',labels[0])
# 调用d2l包,设置matplotlib图像的默认尺寸
d2l.set_figsize()
# features[:,1]:取出所有样本的第2个特征列
# .detach():从计算图分离张量
# .numpy():将PyTorch转换成Numpy(matplotlib需要的格式)
# scatters(x,y,s):x横坐标,y纵坐标,s每个点的大小
d2l.plt.scatter(features[:,1].detach().numpy(),
labels.detach().numpy(),1)
# 生成器函数,每次调用时返回一个小批量数据
# batch_size:批量大小爱哦
# features:特征矩阵
# labels:标签值
def data_iter(batch_size,features,labels):
# 获取样本总数
num_examples = len(features)
# 构造一个包含所有样本索引的列表
indices = list(range(num_examples))
# 打乱索引顺序,确保每次训练时顺序不同
random.shuffle(indices)
# 根据样本总数、批量大小循环i次
for i in range(0,num_examples,batch_size):
batch_indices = torch.tensor(
# 截取当前batch对应的索引列表
# min:防止最后1个batch不满时越界
indices[i:min(i + batch_size,num_examples)])
# 每次调用都会返回一个新批次
yield features[batch_indices],labels[batch_indices]
# 批量大小设置为10
batch_size = 10
# for X,y in:从迭代中取出1个batch
for X,y in data_iter(batch_size,features,labels):
# print(X,y):打印1个小批量的特征标签
print(X,'\n',y)
# 退出循环
break
# 初始化线性回归模型中的权重和偏置
# w初始值为0,方差为0.01的(2,1)向量,可以进行梯度计算
w = torch.normal(0,0.01,size=(2,1),requires_grad=True)
# b初始值为0,形状为1,可以进行梯度计算
b = torch.zeros(1,requires_grad=True)
# X:特征张量
# w:权重参数
# b:偏置
def linreg(X,w,b):
"""线性回归模型"""
# 线性回归计算
return torch.matmul(X,w) + b
# 定义损失函数
# y_hat:模型预测值,形状为(batch_size,1)
# y:真实标签,形状为(batch_size)
def squared_loss(y_hat,y):
"""均方损失"""
# y.reshape(y_hat.shape):y的形状与y_hat匹配
# 损失函数计算:(y0-y)²/2
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
# 小批量随机梯度下降的核心函数
# params:包含模型中所有需要更新的参数列表[w,b]
# lr:学习率
# batch_size:批量大小
def sgd(params,lr,batch_size):
"""小批量随机梯度下降"""
# 关闭梯度追踪(防止参数更新被记录在计算图中)
with torch.no_grad():
# 遍历每一个参数(w,b)
for param in params:
# 梯度下降公式
param -= lr * param.grad / batch_size
# 梯度清零
param.grad.zero_()
# 设置学习率、训练轮数、前向计算函数和损失函数
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
# 外层控制训练轮数
for epoch in range(num_epochs):
# 进行小批量训练
# X:输入特征
# y:标签值
for X,y in data_iter(batch_size,features,labels):
# 前向传播并计算损失
l = loss(net(X,w,b),y)
# 反向传播,自动计算梯度
l.sum().backward()
# 梯度下降更新参数
sgd([w,b],lr,batch_size)
# 禁用梯度计算(提高效率)
with torch.no_grad():
# 每轮训练结束后评估损失
train_l = loss(net(features,w,b),labels)
# train_l.mean():取平均损失作为本轮指标
print(f'epoch{epoch + 1},loss{float(train_l.mean()):f}')
# 打印权重误差
# w.reshape(true_w.shape):将w变成一维向量,与true_w对齐
print(f'w的估计误差:{true_w - w.reshape(true_w.shape)}')
# 打印偏置误差
print(f'b的估计误差:{true_b - b}')
5万+

被折叠的 条评论
为什么被折叠?



