用PyTorch实现线性回归:从理论到代码的保姆级拆解(附d2l库实战)
用PyTorch实现线性回归:从理论到代码的保姆级拆解(附d2l库实战)
线性回归是机器学习领域最基础也最重要的算法之一,它不仅是理解更复杂模型的基石,也是许多实际业务场景中的首选解决方案。本文将带你从零开始,用PyTorch完整实现一个线性回归模型,并通过d2l库提供的工具链展示整个开发流程。无论你是刚入门深度学习的新手,还是希望巩固基础的从业者,都能从中获得实用价值。
1. 环境准备与数据生成
在开始建模之前,我们需要确保开发环境配置正确。推荐使用conda创建独立的Python环境:
conda create -n pytorch-regression python=3.9
conda activate pytorch-regression
pip install torch torchvision d2l matplotlib
线性回归的核心假设是特征与目标值之间存在线性关系。我们可以用以下函数生成模拟数据:
import torch
def synthetic_data(w, b, num_samples):
"""生成符合线性关系的数据集"""
X = torch.normal(0, 1, (num_samples, 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.0, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
这段代码生成了1000个样本,每个样本有2个特征。我们可以检查第一个样本:
features[0]: tensor([0.8321, -0.7421])
labels[0]: tensor([7.4107])
提示:在实际项目中,数据探索(EDA)是必不可少的步骤。建议使用matplotlib绘制特征与标签的散点图,直观验证线性关系。
2. 数据加载与批处理
深度学习模型通常采用小批量梯度下降进行训练,这要求我们实现高效的数据加载器。PyTorch提供了DataLoader类,但为了理解原理,我们先手动实现:
import random
def data_iter(batch_size, features, labels):
"""生成随机小批量数据"""
num_samples = len(features)
indices = list(range(num_samples))
random.shuffle(indices) # 打乱顺序
for i in range(0, num_samples, batch_size):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_samples)])
yield features[batch_indices], labels[batch_indices]
使用示例:
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print("Batch features shape:", X.shape)
print("Batch labels shape:", y.shape)
break
输出将显示:
Batch features shape: torch.Size([10, 2])
Batch labels shape: torch.Size([10, 1])
注意:实际项目中应使用PyTorch的TensorDataset和DataLoader,它们支持多进程加载和自动批处理等高级功能。
3. 模型构建与训练流程
3.1 初始化参数
线性回归模型的数学表达式为:ŷ = XW + b。我们需要初始化权重和偏置:
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
关键点:
requires_grad=True告诉PyTorch需要计算这些参数的梯度- 初始值不宜过大,否则可能导致梯度爆炸
3.2 定义模型和损失函数
线性回归模型实现起来非常简单:
def linreg(X, w, b):
"""线性回归模型"""
return torch.matmul(X, w) + b
我们使用均方误差(MSE)作为损失函数:
def squared_loss(y_hat, y):
"""MSE损失函数"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
3.3 实现优化算法
小批量随机梯度下降(SGD)是最基础的优化器:
def sgd(params, lr, batch_size):
"""小批量梯度下降"""
with torch.no_grad(): # 禁用梯度计算
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_() # 梯度清零
参数更新公式:θ = θ - η∇θJ(θ)
3.4 训练循环
将上述组件组合成完整训练流程:
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 1, loss 0.038472
epoch 2, loss 0.000143
epoch 3, loss 0.000048
4. 模型评估与可视化
训练完成后,我们需要评估模型性能:
print(f'w估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b估计误差: {true_b - b}')
输出示例:
w估计误差: tensor([ 0.0003, -0.0002], grad_fn=<SubBackward0>)
b估计误差: tensor([-0.0004], grad_fn=<RsubBackward1>)
可视化是理解模型行为的重要手段。我们可以用matplotlib绘制:
import matplotlib.pyplot as plt
# 绘制特征与预测值的关系
plt.scatter(features[:, 1].detach().numpy(),
labels.detach().numpy(), s=5)
plt.plot(features[:, 1].detach().numpy(),
(w[1]*features[:, 1] + b).detach().numpy(), 'r-')
plt.xlabel('Feature 2')
plt.ylabel('Label')
plt.show()
这张图应该显示数据点沿一条直线分布,而我们的模型线很好地拟合了这个趋势。
5. 使用d2l库简化流程
d2l库提供了许多实用工具可以简化上述流程。例如,数据加载可以简化为:
from d2l import torch as d2l
batch_size = 10
data_iter = d2l.load_array((features, labels), batch_size)
训练循环也可以更加简洁:
trainer = d2l.Trainer(max_epochs=num_epochs)
model = d2l.LinearRegression(lr=lr)
trainer.fit(model, data_iter)
d2l还内置了可视化工具:
d2l.show_images(features[0:10], 2, 5) # 查看前10个样本
d2l.plot([true_w @ x + true_b for x in features[0:10]],
[model(x) for x in features[0:10]], 'Actual', 'Predicted')
6. 进阶技巧与常见问题
6.1 特征缩放
当特征尺度差异大时,应对特征进行标准化:
features = (features - features.mean(0)) / features.std(0)
6.2 学习率选择
学习率对训练效果至关重要。可以尝试学习率调度器:
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
6.3 过拟合处理
虽然线性回归不易过拟合,但当特征多时可以考虑L2正则化:
loss = nn.MSELoss()
optimizer = torch.optim.SGD([w, b], lr=lr, weight_decay=0.1) # weight_decay即λ
6.4 GPU加速
如果有GPU可用,可以将数据和模型转移到GPU:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
features = features.to(device)
labels = labels.to(device)
w = w.to(device)
b = b.to(device)
7. 实际项目中的应用建议
在真实业务场景中使用线性回归时,有几个实用建议:
- 数据质量检查:确保没有缺失值和异常值
- 特征工程:尝试多项式特征、交互特征等
- 模型诊断:检查残差是否符合正态分布
- 可解释性:分析权重大小和符号的业务含义
例如,在房价预测项目中,可以这样扩展:
# 添加二次项特征
poly_features = torch.cat([features, features**2], 1)
# 添加交互项
interaction = (features[:, 0] * features[:, 1]).reshape(-1, 1)
all_features = torch.cat([features, features**2, interaction], 1)
最后,记得保存训练好的模型:
torch.save({
'w': w,
'b': b
}, 'linear_regression.pth')
更多推荐


所有评论(0)