PyTorch & Numpy 实现线性回归详解
线性回归是机器学习入门最经典的算法,核心是拟合出一条最优直线(高维场景为超平面),让模型预测值与真实值误差最小。本文分别使用 Numpy(手动梯度) 和 PyTorch(自动求导) 实现梯度下降版线性回归,对比两种实现思路与差异。
一、线性回归核心原理
1. 模型公式
假设输入特征为 XXX,真实标签为 yyy,线性回归模型表达式:
y^=wX+b\hat{y} = wX + by^=wX+b
- y^\hat{y}y^:模型预测值
- www:权重参数
- bbb:偏置项
2. 损失函数(均方误差 MSE)
使用均方误差 衡量预测值与真实值的差距,nnn 为样本总数:
MSE=1n∑i=1n(y^i−yi)2MSE = \frac{1}{n}\sum_{i=1}^{n}(\hat{y}_i - y_i)^2MSE=n1i=1∑n(y^i−yi)2
3. 梯度下降
通过梯度下降迭代更新参数 www 和 bbb,最小化损失函数。对损失函数求偏导得到梯度:
∂MSE∂w=2nXT(y^−y)\frac{\partial MSE}{\partial w} = \frac{2}{n}X^T(\hat{y}-y)∂w∂MSE=n2XT(y^−y)
∂MSE∂b=2n∑i=1n(y^i−yi)\frac{\partial MSE}{\partial b} = \frac{2}{n}\sum_{i=1}^{n}(\hat{y}_i - y_i)∂b∂MSE=n2i=1∑n(y^i−yi)
参数更新规则:
w=w−lr⋅∂MSE∂ww = w - lr \cdot \frac{\partial MSE}{\partial w}w=w−lr⋅∂w∂MSE
b=b−lr⋅∂MSE∂bb = b - lr \cdot \frac{\partial MSE}{\partial b}b=b−lr⋅∂b∂MSE
lrlrlr 为学习率,控制参数更新步长。
二、Numpy 手动实现线性回归
Numpy 基于纯数值计算,需要手动推导并计算梯度,适合理解线性回归底层数学逻辑。
1. 完整代码
python
import numpy as np
import matplotlib.pyplot as plt
# 1. 设置随机种子,保证实验结果可复现
np.random.seed(42)
# 2. 生成模拟数据集:真实关系 y = 2x + 3,叠加高斯噪声
X = np.random.rand(100, 1) # 100个样本,1个特征
y = 2 * X + 3 + np.random.randn(100, 1) * 0.1
# 3. 初始化参数 w(权重)、b(偏置)
w = np.random.randn(1, 1)
b = np.random.randn(1)
# 4. 定义预测函数
def predict(X, w, b):
return np.dot(X, w) + b
# 5. 定义均方误差损失函数
def compute_loss(y_pred, y_true):
return np.mean((y_pred - y_true) ** 2)
# 6. 超参数设置
learning_rate = 0.1
epochs = 1000 # 迭代轮数
# 7. 梯度下降训练
for epoch in range(epochs):
# 前向传播:计算预测值
y_pred = predict(X, w, b)
# 计算损失
loss = compute_loss(y_pred, y)
# 手动计算梯度
dw = (2 / len(X)) * np.dot(X.T, (y_pred - y))
db = (2 / len(X)) * np.sum(y_pred - y)
# 更新参数
w -= learning_rate * dw
b -= learning_rate * db
# 每100轮打印损失
if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {loss:.4f}")
# 输出最终训练参数
print(f"\n【Numpy 训练结果】w = {w[0][0]:.4f}, b = {b[0]:.4f}")
# 8. 可视化原始数据 + 拟合直线
y_pred_final = predict(X, w, b)
plt.scatter(X, y, label="True Data")
plt.plot(X, y_pred_final, color="red", label="Fitted Line")
plt.xlabel("X")
plt.ylabel("y")
plt.title("Linear Regression (Numpy)")
plt.legend()
plt.show()
2. 运行说明
- 模拟数据基于公式 y=2x+3y=2x+3y=2x+3 生成,叠加微小噪声;
- 全程手动计算梯度、更新参数,无框架封装;
- 训练完成后参数会无限逼近真实值 w=2,b=3w=2,b=3w=2,b=3。
三、PyTorch 实现线性回归
PyTorch 依托张量(Tensor) 计算,内置自动求导、网络层、损失函数与优化器,无需手动计算梯度,是深度学习主流实现方式。
1. 完整代码
python
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
# 复用上方 Numpy 生成的数据集
np.random.seed(42)
X = np.random.rand(100, 1)
y = 2 * X + 3 + np.random.randn(100, 1) * 0.1
# 1. 转换为 PyTorch 浮点张量
torch.manual_seed(42)
X_tensor = torch.from_numpy(X).float()
y_tensor = torch.from_numpy(y).float()
# 2. 定义线性回归模型:输入特征1维,输出特征1维
model = nn.Linear(in_features=1, out_features=1)
# 3. 定义损失函数 & 优化器
criterion = nn.MSELoss() # 均方误差损失
optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 随机梯度下降
# 4. 模型训练
epochs = 1000
for epoch in range(epochs):
# 前向传播
y_pred = model(X_tensor)
# 计算损失
loss = criterion(y_pred, y_tensor)
# 反向传播 + 参数更新
optimizer.zero_grad() # 清空历史梯度(必写)
loss.backward() # 自动反向传播求梯度
optimizer.step() # 根据梯度更新参数
# 每100轮打印损失
if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {loss.item():.4f}")
# 提取训练后的权重与偏置
w_final = model.weight.item()
b_final = model.bias.item()
print(f"\n【PyTorch 训练结果】w = {w_final:.4f}, b = {b_final:.4f}")
# 5. 可视化拟合结果
# detach() 分离梯度,再转 Numpy 数组绘图
y_pred_tensor = model(X_tensor)
y_pred_np = y_pred_tensor.detach().numpy()
plt.scatter(X, y, label="True Data")
plt.plot(X, y_pred_np, color="red", label="Fitted Line")
plt.xlabel("X")
plt.ylabel("y")
plt.title("Linear Regression (PyTorch)")
plt.legend()
plt.show()
2. 关键注意点
- 数据转换 :Numpy 数组需通过
torch.from_numpy()转为张量,并指定浮点类型; - 梯度清空 :
optimizer.zero_grad()必须执行,否则梯度会累加; - 自动求导 :
loss.backward()自动完成梯度计算,无需手动推导; - 张量转 Numpy :带梯度的张量需先用
.detach()分离计算图,再调用.numpy()。
四、两种实现方式对比
| 维度 | Numpy 实现 | PyTorch 实现 |
|---|---|---|
| 梯度计算 | 手动推导公式计算梯度 | 框架自动求导 |
| 代码复杂度 | 偏繁琐,需手写前向、损失、梯度 | 简洁,复用内置层/损失/优化器 |
| 算力支持 | 仅 CPU 计算,无 GPU 加速 | 支持 CPU/GPU 一键切换 |
| 适用场景 | 学习数学原理、基础数值计算 | 深度学习模型开发、工程落地 |
| 扩展性 | 复杂模型代码冗余、易出错 | 轻松扩展为深层神经网络 |
五、总结
- Numpy 版本 适合吃透线性回归的数学原理、梯度下降的底层逻辑,是打基础的首选;
- PyTorch 版本 封装了大量底层细节,开发效率更高,是工业界和深度学习学习的主流方案;
- 两种实现最终拟合效果一致,参数都会收敛至真实值 w≈2,b≈3w \approx 2,b \approx 3w≈2,b≈3;
- 核心训练流程统一:前向传播 → 计算损失 → 反向传播(求梯度) → 更新参数。
个人能力有限,有问题随时交流~