当前位置: 首页 > news >正文

从0实现线性回归模型

课程:b站up 跟李沐学AI 的系列课程 《动手学深度学习》

笔记:本笔记基于以上课程所写,有疑问的地方,可评论区留言,或自行观看b站视频

完整代码

import torch
from torch.utils import data  # 导入PyTorch的数据加载工具包# ========== 自定义模型、损失函数和优化器 start ==========class mynet:"""自定义的简单线性神经网络模型(单层全连接)"""def __init__(self, w, b):"""初始化模型参数:param w: 权重参数,形状为(输入特征数, 输出特征数),这里输出是1维,所以形状为(2,1):param b: 偏置参数,形状为(1,)(标量)"""self.w = w  # 权重矩阵self.b = b  # 偏置向量def forward(self, X):"""前向传播计算预测值:param X: 输入特征矩阵,形状为(样本数, 输入特征数):return: 预测值y_hat,形状为(样本数, 输出特征数)"""# 矩阵乘法:X(样本数×2) * w(2×1)得到(样本数×1),再加上偏置b(广播到样本数×1)return torch.matmul(X, self.w) + self.bdef __call__(self, X):"""支持直接调用模型对象(如net(X)等价于net.forward(X))"""return self.forward(X)class myloss:"""自定义的均方损失函数(MSE,Mean Squared Error)"""def __init__(self):pass  # 均方损失无需额外参数,初始化为空def __call__(self, y_hat, y):"""计算预测值与真实值的均方损失:param y_hat: 预测值,形状为(样本数, 1):param y: 真实值,形状为(样本数, 1):return: 平均均方损失(标量)"""# 公式:L = 1/(2N) * Σ(y_hat_i - y_i)^2# 这里除以2是为了后续梯度计算时抵消平方项的系数(求导后系数为2,除以2后梯度更简洁)# 注意:y需要与y_hat形状一致(可能y是(样本数,),需reshape为(样本数,1))return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2class mysgd:"""自定义的随机梯度下降(SGD)优化器"""def __init__(self, params, lr, batch_size):"""初始化优化器:param params: 模型参数列表(需要优化的参数,这里是w和b):param lr: 学习率(控制参数更新步长):param batch_size: 批量大小(用于计算平均梯度的样本数)"""self.params = params  # 待优化的参数列表(如[net.w, net.b])self.lr = lr  # 学习率self.batch_size = batch_size  # 批量大小def step(self):"""执行一步参数更新(根据计算的梯度调整参数)"""with torch.no_grad():  # 关闭梯度计算(更新参数时不需要记录梯度)for param in self.params:if param.grad is not None:  # 检查参数是否有梯度(避免未使用的参数报错)# 参数更新公式:θ = θ - lr * (平均梯度)# 由于损失是批量样本的平均损失,梯度已经是平均后的结果,因此直接用lr*grad即可?# 注意:原代码中除以了batch_size,可能是因为损失计算时没有取平均(用了sum)# 例如:如果损失是Σ(y_hat - y)^2 / 2(未平均),则梯度会是batch_size倍的平均梯度# 因此需要除以batch_size来修正,等价于对平均损失求导param -= self.lr * param.grad / self.batch_sizeparam.grad.zero_()  # 清空梯度(避免下次更新时梯度累积)def zero_grad(self):"""清空所有参数的梯度(在反向传播前调用)"""for param in self.params:param.grad.zero_()  # 将参数的.grad属性置零# ========== 自定义模型、损失函数和优化器 end ==========# 真实模型参数(用于生成模拟数据)
true_w = torch.tensor([2, -3.4])  # 真实权重
true_b = 4.2  # 真实偏置def synthetic_data(w, b, num_examples):"""生成线性关系的模拟数据集(带高斯噪声):param w: 真实权重(用于生成y):param b: 真实偏置(用于生成y):param num_examples: 生成样本数量:return: 特征矩阵X(形状:num_examples×len(w))和标签y(形状:num_examples×1)"""# 生成特征X:从标准正态分布(均值0,方差1)中采样,形状为(样本数, 特征数)X = torch.normal(0, 1, (num_examples, len(w)))# 生成标签y:根据真实线性关系y = w·X + b,再加上高斯噪声(模拟真实数据的不完美)y = torch.matmul(X, w) + by += torch.normal(0, 0.01, y.shape)  # 噪声服从均值0,标准差0.01的正态分布return X, y.reshape((-1, 1))  # 将y调整为(样本数, 1)的形状# 生成1000个样本的模拟数据集
features, labels = synthetic_data(true_w, true_b, 1000)
print("features shape : ", features.shape)  # 输出:(1000, 2)(1000个样本,每个样本2个特征)
print("labels shape : ", labels.shape)  # 输出:(1000, 1)(1000个标签,每个标签1维)
print('features:', features[:3], '\nlabel:', labels[:3])  # 打印前3个样本的特征和标签# 创建数据迭代器(用于批量加载数据)
batch_size = 10  # 每批10个样本
# 将特征和标签组合成数据集,然后用DataLoader批量加载(打乱顺序,分批)
data_iter = data.DataLoader(dataset=data.TensorDataset(features, labels),  # 用TensorDataset包装特征和标签batch_size=batch_size,  # 批量大小shuffle=True  # 训练前打乱数据顺序(避免模型学习到顺序信息)
)# 初始化模型参数(需要学习的参数)
# 权重w初始化为均值0、标准差0.01的正态分布(小随机数,避免初始值过大)
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)  # 形状(2,1),需要计算梯度
# 偏置b初始化为0(标量),同样需要计算梯度
b = torch.zeros(1, requires_grad=True)  # 形状(1,)
# 实例化自定义模型,传入权重和偏置
net = mynet(w, b)# 训练模型(使用自定义的模型、损失函数和优化器)
lr = 0.03  # 学习率(控制参数更新步长)
num_epochs = 3  # 训练轮数(遍历整个数据集的次数)
loss = myloss()  # 实例化自定义均方损失函数
# 实例化自定义SGD优化器,传入模型参数、学习率和批量大小
optimizer = mysgd([net.w, net.b], lr, batch_size)# 开始训练循环
for epoch in range(num_epochs):# 遍历数据迭代器(每次取一个批量)for X, y in data_iter:# 前向传播:计算当前批量样本的预测值y_hat = net(X)# 计算预测值与真实值的损失(当前批量的平均损失)l = loss(y_hat, y)# 反向传播:计算损失对模型参数的梯度(自动求导)# 注意:l是一个批量中所有样本的损失之和(因为myloss返回的是单个样本损失/2,sum后是总损失)# 总损失的梯度是各样本梯度的平均(因为sum的导数是各元素导数的和,而每个样本的梯度是平均后的)l.sum().backward()  # 对总损失求和后反向传播(得到各参数的总梯度)# 更新参数:使用优化器的一步更新(根据梯度调整参数)optimizer.step()# 清空梯度(避免下次反向传播时梯度累积)optimizer.zero_grad()# 每轮训练结束后,计算整个数据集上的损失(验证模型效果)with torch.no_grad():  # 关闭梯度计算(验证时不需要记录梯度)# 用训练好的模型预测所有样本的y_haty_hat_all = net(features)# 计算所有样本的平均损失train_l = loss(y_hat_all, labels)# 输出当前轮数的损失(取平均值)print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')# 训练完成后,打印学习到的参数与真实参数的对比
print("train_w : ", net.w, "\ntrue_w : ", true_w)  # 学习到的权重 vs 真实权重
print("train_b : ", net.b, "\ntrue_b : ", true_b)  # 学习到的偏置 vs 真实偏置

输出结果

features shape :  torch.Size([1000, 2])
labels shape :  torch.Size([1000, 1])
features: tensor([[-0.2995, -0.6537],[-1.5516, -0.5079],[ 0.1010, -0.9283]]) 
label: tensor([[5.8253],[2.8230],[7.5584]])
epoch 1, loss 0.030969
epoch 2, loss 0.000110
epoch 3, loss 0.000050
train_w :  tensor([[ 2.0003],[-3.3994]], requires_grad=True) 
true_w :  tensor([ 2.0000, -3.4000])
train_b :  tensor([4.1998], requires_grad=True) 
true_b :  4.2Process finished with exit code 0

http://www.lryc.cn/news/583016.html

相关文章:

  • vue3.2 前端动态分页算法
  • 「Java案例」打印数字金字塔
  • [Backlog] 核心协调器 | 终端用户界面(TUI)实现 | 多分支任务冲突解决 | 测试验证体系
  • 技术支持丨解决 ServBay 在 Windows 启动时反复提示安装 .NET 的问题
  • Python(30)基于itertools生成器的量子计算模拟技术深度解析
  • 使用LLaMA-Factory微调Qwen2.5-VL-3B 的目标检测任务-数据集格式转换(voc 转 ShareGPT)
  • 【洛谷题单】--顺序结构(一)
  • C++高频知识点(六)
  • [NOIP][C++]洛谷P1376 [USACO05MAR] Yogurt factory 机器工厂
  • LeetCode--42.接雨水
  • C++(STL源码刨析/vector)
  • 从历史航拍图像中去除阴影
  • 11款常用C++在线编译与运行平台推荐与对比
  • 力扣-75.颜色分类
  • Web后端开发-Mybatis
  • qt-C++笔记之setCentralWidget的使用
  • 软件系统测试的基本流程
  • 数据结构*搜索树
  • 从零开始手写嵌入式实时操作系统
  • 牛市来临之际,如何用期权抢占反弹先机?
  • 初识mysql(一)
  • [特殊字符] AlphaGo:“神之一手”背后的智能革命与人机博弈新纪元
  • 【深度学习新浪潮】什么是蛋白质反向折叠模型?
  • 深度学习超参数优化(HPO)终极指南:从入门到前沿
  • FairyGUI 实现 Boss 双层血条动画
  • qt-C++语法笔记之Stretch与Spacer的关系分析
  • 分库分表之实战-sharding-JDBC水平分库+水平分表配置实战
  • LeetCode题解---<三数之和>
  • 自动化一次通过率
  • 深度学习环境配置:PyTorch、CUDA和Python版本选择