AI

d2l 学习笔记:简易线性模型实现

·11 分钟阅读·4256 字

梳理 D2L 线性回归从数据生成、模型定义、损失计算到小批量随机梯度下降的手动实现流程

📋 目录

d2l 学习笔记:简易线性模型实现

1. 线性模型训练流程

D2L 线性回归从零实现展示了一个机器学习模型训练的最小闭环:生成数据、定义模型、定义损失函数、读取小批量数据、计算梯度、更新参数,并在循环中逐步逼近真实规律。

数据生成
  -> 定义模型
  -> 定义损失
  -> 读取 batch
  -> 前向传播
  -> 反向传播
  -> 参数更新
  -> 循环训练

对应到代码组件:

组件作用
synthetic_data()构造带噪声的线性回归训练数据。
linreg()定义线性回归模型。
squared_loss()定义平方损失函数。
data_iter()以小批量方式读取数据。
backward()自动计算梯度。
sgd()按梯度更新参数。
training loop多轮迭代训练模型。

2. 生成训练数据

2.1. 数据生成代码

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))

这段代码用于模拟现实世界中的监督学习数据。输入特征 XX 根据正态分布随机生成,标签 yy 按线性关系生成,并加入少量噪声。

2.2. 真实规律

数据生成时假设真实规律为:

y=Xw+b+ϵy=Xw+b+\epsilon

示例真实参数:

true_w = torch.tensor([2, -3.4])
true_b = 4.2

对应数学形式为:

y=2x1−3.4x2+4.2+ϵy=2x_1-3.4x_2+4.2+\epsilon

其中 ϵ\epsilon 表示噪声,用于模拟真实数据中的随机扰动。

2.3. 输入特征矩阵

X = torch.normal(0, 1, (1000, 2))

该语句生成 1000 个样本,每个样本有 2 个特征:

X∈R1000×2X \in \mathbb{R}^{1000 \times 2}

矩阵形式可以表示为:

X=[x11x12x21x22⋯⋯x1000,1x1000,2]X= \begin{bmatrix} x_{11} & x_{12} \\ x_{21} & x_{22} \\ \cdots & \cdots \\ x_{1000,1} & x_{1000,2} \end{bmatrix}

2.4. 标签生成

y = torch.matmul(X, w) + b

数学形式为:

y=Xw+by=Xw+b

为了让数据更接近真实场景,代码会追加噪声:

y += torch.normal(0, 0.01, y.shape)

最终标签为:

y=Xw+b+ϵy=Xw+b+\epsilon

3. 定义线性回归模型

3.1. 模型代码

def linreg(X, w, b):
    return torch.matmul(X, w) + b

线性回归模型的数学形式为:

y^=Xw+b\hat{y}=Xw+b

其中:

符号含义
XX输入特征矩阵。
ww模型权重参数。
bb模型偏置参数。
y^\hat{y}模型预测值。

3.2. 训练参数与真实参数

数据生成时使用的 true_w、true_b 是真实规律;训练时模型并不知道真实参数,只能从随机初始化开始,通过数据逐步学习。

w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

训练目标是让模型参数 ww、bb 逐步接近真实参数。

4. 定义损失函数

4.1. 平方损失代码

def squared_loss(y_hat, y):
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

平方损失的数学形式为:

L=12(y^−y)2L=\frac{1}{2}(\hat{y}-y)^2

加入 12\frac{1}{2} 是为了求导后抵消平方项带来的系数 2,使梯度形式更简洁。

4.2. 平方损失的作用

损失函数衡量预测值与真实值之间的差距。

情况含义
损失较小模型预测接近真实标签。
损失较大模型预测偏离真实标签。

平方损失适合线性回归这类连续值预测任务,因为它能惩罚较大的预测误差。

5. 小批量数据读取

5.1. data_iter 代码

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]

data_iter() 的作用是把完整训练集拆成多个小批量,每次返回一部分样本用于训练。

5.2. 为什么使用小批量

训练方式特点
全量梯度下降每次使用全部数据,梯度稳定但计算成本高。
随机梯度下降每次使用一个样本,更新频繁但噪声较大。
小批量随机梯度下降每次使用一批样本,在稳定性和效率之间折中。

小批量训练是深度学习中最常见的训练方式。

6. 自动求梯度

6.1. 前向与损失计算

l = loss(net(X, w, b), y)

这一行先计算预测值 y^\hat{y},再计算预测值与真实标签之间的损失。

6.2. 反向传播

l.sum().backward()

backward() 会根据计算图自动计算损失对参数的梯度:

∂L∂w\frac{\partial L}{\partial w} ∂L∂b\frac{\partial L}{\partial b}

梯度表示如果想让损失下降,参数应该沿哪个方向调整。

7. 使用 SGD 更新参数

7.1. SGD 代码

def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

7.2. 参数更新公式

随机梯度下降的参数更新公式为:

w←w−η∂L∂ww \leftarrow w - \eta \frac{\partial L}{\partial w} b←b−η∂L∂bb \leftarrow b - \eta \frac{\partial L}{\partial b}

其中 η\eta 是学习率。

7.3. 清空梯度

param.grad.zero_()

PyTorch 默认会累积梯度,因此每次更新参数后需要清空梯度,避免下一轮训练使用旧梯度。

8. 完整训练代码

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)
        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 会遍历所有小批量数据。每个小批量都会经历前向传播、损失计算、反向传播和参数更新。

9. 理论与代码对应关系

机器学习概念代码实现说明
数据集features、labels用于训练模型的输入和标签。
模型linreg()将输入映射为预测值。
参数w、b训练过程中被优化的变量。
损失函数squared_loss()衡量预测误差。
梯度backward()计算参数对损失的影响方向。
优化器sgd()根据梯度更新参数。
训练循环for epoch in range(num_epochs)重复优化过程。

10. 关键理解

10.1. 模型是什么

模型是一个带参数的函数。在线性回归中,模型形式是:

y^=Xw+b\hat{y}=Xw+b

10.2. 训练是什么

训练是不断调整参数 ww 和 bb,让模型预测值 y^\hat{y} 更接近真实值 yy 的过程。

10.3. 如何评价参数

通过损失函数评价参数好坏:

L=12(y^−y)2L=\frac{1}{2}(\hat{y}-y)^2

损失越小,说明当前参数对应的模型预测效果越好。

10.4. 如何寻找参数

通过梯度下降寻找参数:

θ←θ−η∇θL\theta \leftarrow \theta - \eta \nabla_{\theta} L

其中 θ\theta 表示所有待训练参数。

11. 训练模板

线性回归从零实现形成了一个通用机器学习训练模板:

准备数据
  -> 定义模型
  -> 定义损失函数
  -> 定义优化方法
  -> 小批量读取数据
  -> 前向传播
  -> 计算损失
  -> 反向传播
  -> 更新参数
  -> 评估训练效果

这个模板不仅适用于线性回归,也适用于后续的 Softmax 回归、多层感知机、卷积神经网络和 Transformer 等模型。复杂模型的差异主要体现在模型结构、损失函数和优化策略上。


关联文档

Yanche Blog

记录云原生、Linux、数据库等技术领域的学习心得,以及日常生活的思考与感悟。

© 2026 Yanche Blog. All rights reserved.

Powered by Astro