使用PyTorch实现线性回归的全面指南
摘要
核心内容包括:
- ✅ 深入剖析线性回归的数学原理和梯度下降算法
- ✅ 两种实现方式:从零实现 vs PyTorch高级API
- ✅ 完整的数据准备、模型训练、评估流程
- ✅ 4个实战案例:房价预测、股票分析、医疗费用、工业优化
- ✅ 模型优化技巧和超参数调优指南
适合人群:
- PyTorch初学者
- 机器学习入门者
- 需要快速上手线性回归的数据分析师
- 准备面试的算法工程师
通过本文的学习,你将掌握使用PyTorch实现线性回归的完整技能,并能够应用到实际项目中。
第1章 线性回归理论基础
1.1 为什么用 PyTorch 学线性回归?
线性回归是机器学习入门的必修课,本质是通过寻找特征与目标值之间的线性关系来进行预测。而在 PyTorch 中,这个过程被动态图机制简化——每个环节都变得可视化且易于调试。
虽然线性回归可以用 scikit-learn 几行代码实现,但用 PyTorch 手动构建能深入理解神经网络的基本工作原理,这对后续学习更复杂模型至关重要。伊利诺伊大学的 ECE 364 课程甚至用整整三周(Week 6-8)专门讲授线性回归在 PyTorch 中的实现及其与数据集、数据加载器等组件的结合。
1.2 PyTorch 两大核心特性
与 NumPy 相比,PyTorch 张量额外具备两大关键特性:
- 自动微分:当你在 PyTorch 中定义模型时,实际上在构建一个计算图——这个图会记录所有操作步骤,并在反向传播时自动计算梯度
- GPU 加速:张量可以无缝迁移到 GPU 上进行并行计算,显著加速训练过程
线性回归可以使用单层神经网络来实现,所需要用到的概念和技巧也适用于更复杂的深度学习模型。
1.3 线性回归的核心概念
线性回归是机器学习中最基础、最重要的算法之一,它假设目标变量 y 与输入特征 x 之间存在线性关系。
基本公式:
对于单变量线性回归:
y = w * x + b + ε
对于多变量线性回归:
y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b + ε
参数说明:
y:预测的目标值(连续型变量)x₁, x₂, ..., xₙ:输入特征w₁, w₂, ..., wₙ:权重参数,表示各特征对目标的影响程度b:偏置项(截距)ε:噪声项,表示模型无法解释的随机误差
1.4 数学原理与公式推导
1.4.1 损失函数(均方误差)
线性回归使用均方误差(MSE)作为损失函数,衡量预测值与真实值之间的差距:
L(w, b) = (1/2n) * Σ(yᵢ - ŷᵢ)²
为什么要除以2?
- 为了在求导时抵消平方项的系数2,使梯度计算更简洁
- 不影响优化结果,因为这只是对损失函数的缩放
1.4.2 梯度计算
对损失函数求偏导,得到梯度:
∂L/∂wⱼ = (1/n) * Σ(ŷᵢ - yᵢ) * xⱼᵢ ∂L/∂b = (1/n) * Σ(ŷᵢ - yᵢ)
1.4.3 梯度下降更新规则
wⱼ ← wⱼ - α * ∂L/∂wⱼ b ← b - α * ∂L/∂b
其中 α 是学习率,控制每次更新的步长。
1.5 损失函数与优化算法
1.5.1 常见损失函数对比
| 损失函数 | 公式 | 适用场景 |
|---|---|---|
| MSE | (1/2n)Σ(y-ŷ)² | 回归问题,对异常值敏感 |
| MAE | (1/n)Σ | y-ŷ |
| Huber | 分段函数 | 回归问题,平衡MSE和MAE |
1.5.2 优化算法对比
| 优化器 | 特点 | 适用场景 |
|---|---|---|
| SGD | 简单直接,需要手动调学习率 | 小数据集,简单模型 |
| SGD with Momentum | 加入动量,加速收敛 | 一般场景 |
| Adam | 自适应学习率,收敛快 | 大多数场景 |
| RMSprop | 适合非平稳目标 | RNN等 |
第2章 PyTorch框架入门
2.1 PyTorch核心特性
- 动态计算图:运行时构建计算图,调试方便
- Pythonic:API设计符合Python习惯
- GPU加速:无缝支持CUDA,加速计算
- 自动求导:自动计算梯度,简化实现
2.2 环境配置与安装
# 创建虚拟环境 python -m venv pytorch_env source pytorch_env/bin/activate # Linux/Mac # pytorch_env\Scripts\activate # Windows # 安装PyTorch(CPU版本) pip install torch torchvision torchaudio # 安装PyTorch(GPU版本,CUDA 11.8) pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装其他依赖 pip install numpy pandas matplotlib seaborn jupyter
2.3 张量操作基础
import torch
# 创建张量
x = torch.tensor([1.0, 2.0, 3.0]) # 从列表创建
y = torch.zeros(3) # 零张量
z = torch.ones(2, 3) # 全1张量
a = torch.randn(2, 2) # 标准正态分布
# 张量属性
print(f"形状: {x.shape}")
print(f"数据类型: {x.dtype}")
print(f"设备: {x.device}")
# 基本运算
x = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor([4.0, 5.0, 6.0])
print(f"加法: {x + y}")
print(f"乘法: {x * y}")
print(f"点积: {torch.dot(x, y)}")
# 矩阵运算
A = torch.randn(3, 2)
B = torch.randn(2, 4)
C = torch.matmul(A, B) # 矩阵乘法
print(f"矩阵乘法结果形状: {C.shape}")
第3章 从零实现线性回归
3.1 数据生成与准备
import torch
import numpy as np
# 设置随机种子,确保可复现
torch.manual_seed(42)
np.random.seed(42)
# 定义真实的模型参数
true_w = torch.tensor([[2.0], [-3.4]]) # 真实权重
true_b = 4.2 # 真实偏置
# 生成数据
num_examples = 1000 # 样本数量
num_inputs = 2 # 特征数量
# 从标准正态分布中随机生成输入特征
X = torch.normal(0, 1, (num_examples, num_inputs))
# 根据真实公式计算标签,并加入噪声
noise = torch.normal(0, 0.01, (num_examples, 1))
y = torch.matmul(X, true_w) + true_b + noise
print(f"数据集形状: X={X.shape}, y={y.shape}")
print(f"前3个样本:\n{X[:3]}")
print(f"对应标签:\n{y[:3]}")
3.2 模型参数初始化
# 初始化权重 w,从均值为0,标准差为0.01的正态分布中采样
w = torch.normal(0, 0.01, size=(num_inputs, 1), requires_grad=True)
# 初始化偏置 b 为 0
b = torch.zeros(1, requires_grad=True)
print(f"初始权重 w:\n{w}")
print(f"初始偏置 b: {b}")
print(f"w 需要梯度: {w.requires_grad}")
print(f"b 需要梯度: {b.requires_grad}")
关键点:
requires_grad=True告诉 PyTorch 这些参数需要计算梯度- 权重通常用小的随机数初始化,避免对称性问题
- 偏置可以初始化为0
3.3 前向传播与损失计算
def linreg(X, w, b):
"""线性回归模型的前向传播"""
return torch.matmul(X, w) + b
def squared_loss(y_hat, y):
"""均方损失函数"""
# 将 y 的形状调整为和 y_hat 一致
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
# 测试前向传播
y_hat = linreg(X[:5], w, b)
print(f"预测值:\n{y_hat}")
# 测试损失计算
loss = squared_loss(y_hat, y[:5])
print(f"损失值:\n{loss}")
print(f"平均损失: {loss.mean()}")
3.4 反向传播与参数更新
def sgd(params, lr, batch_size):
"""
小批量随机梯度下降优化器
参数:
params: 需要更新的参数列表,如 [w, b]
lr: 学习率
batch_size: 批量大小
"""
with torch.no_grad(): # 更新参数时,不需要记录梯度
for param in params:
# 梯度下降更新公式
param -= lr * param.grad / batch_size
# 更新后,手动将梯度清零
param.grad.zero_()
# 示例:假设我们有梯度
w.grad = torch.tensor([[0.1], [0.2]])
b.grad = torch.tensor([0.05])
lr = 0.03
print(f"更新前 w: {w.flatten()}")
print(f"更新前 b: {b.item()}")
sgd([w, b], lr, 10)
print(f"更新后 w: {w.flatten()}")
print(f"更新后 b: {b.item()}")
3.5 完整训练循环
import random
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]
# 超参数设置
lr = 0.03 # 学习率
num_epochs = 5 # 训练轮数
batch_size = 10 # 批量大小
# 重新初始化参数
w = torch.normal(0, 0.01, size=(num_inputs, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# 开始训练
print("=" * 60)
print("开始训练线性回归模型...")
print("=" * 60)
for epoch in range(num_epochs):
for X_batch, y_batch in data_iter(batch_size, X, y):
# 1. 前向传播:计算预测值
y_hat = linreg(X_batch, w, b)
# 2. 计算损失
loss = squared_loss(y_hat, y_batch).mean()
# 3. 反向传播:计算梯度
loss.backward()
# 4. 更新参数
sgd([w, b], lr, batch_size)
# 每个epoch结束后,计算在整个数据集上的平均损失
with torch.no_grad():
train_loss = squared_loss(linreg(X, w, b), y).mean()
print(f'Epoch {epoch + 1}/{num_epochs}, Loss: {train_loss:.6f}')
# 训练结束,查看学习到的参数
print("\n" + "=" * 60)
print("训练完成!模型参数对比:")
print("=" * 60)
print(f'真实的权重 w: {true_w.flatten().numpy()}')
print(f'学到的权重 w: {w.detach().flatten().numpy()}')
print(f'权重误差: {((w - true_w).abs() / true_w.abs()).mean().item():.4%}')
print()
print(f'真实的偏置 b: {true_b:.4f}')
print(f'学到的偏置 b: {b.item():.4f}')
print(f'偏置误差: {abs(b.item() - true_b) / abs(true_b):.4%}')
输出示例:
============================================================ 开始训练线性回归模型... ============================================================ Epoch 1/5, Loss: 0.042567 Epoch 2/5, Loss: 0.000152 Epoch 3/5, Loss: 0.000058 Epoch 4/5, Loss: 0.000051 Epoch 5/5, Loss: 0.000050 ============================================================ 训练完成!模型参数对比: ============================================================ 真实的权重 w: [ 2. -3.4] 学到的权重 w: [ 1.9998 -3.3995] 权重误差: 0.0123% 真实的偏置 b: 4.2000 学到的偏置 b: 4.1998 偏置误差: 0.0048%
第4章 PyTorch高级API实现
4.1 nn.Module模型定义
import torch.nn as nn
class LinearRegressionModel(nn.Module):
def __init__(self, input_dim):
super(LinearRegressionModel, self).__init__()
# 定义线性层
self.linear = nn.Linear(input_dim, 1)
def forward(self, x):
# 前向传播
return self.linear(x)
# 创建模型
model = LinearRegressionModel(num_inputs)
print(model)
print(f"\n模型参数:")
for name, param in model.named_parameters():
print(f" {name}: {param.shape}")
输出:
LinearRegressionModel( (linear): Linear(in_features=2, out_features=1, bias=True) ) 模型参数: linear.weight: torch.Size([1, 2]) linear.bias: torch.Size([1])
4.2 内置损失函数与优化器
# 定义损失函数
criterion = nn.MSELoss()
# 定义优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.03)
# 训练循环
num_epochs = 5
for epoch in range(num_epochs):
for X_batch, y_batch in data_iter(batch_size, X, y):
# 前向传播
y_pred = model(X_batch)
# 计算损失
loss = criterion(y_pred, y_batch)
# 反向传播
optimizer.zero_grad() # 梯度清零
loss.backward() # 计算梯度
# 更新参数
optimizer.step()
# 打印训练进度
if (epoch + 1) % 1 == 0:
with torch.no_grad():
y_pred = model(X)
epoch_loss = criterion(y_pred, y).item()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.6f}')
# 查看学习到的参数
print("\n学到的参数:")
print(f"w: {model.linear.weight.data.numpy()}")
print(f"b: {model.linear.bias.item()}")
4.3 DataLoader数据加载器
from torch.utils.data import TensorDataset, DataLoader
# 创建数据集
dataset = TensorDataset(X, y)
# 创建数据加载器
data_loader = DataLoader(
dataset=dataset,
batch_size=batch_size,
shuffle=True, # 每个epoch打乱数据
num_workers=0 # 数据加载的子进程数
)
# 使用 DataLoader 训练
model = LinearRegressionModel(num_inputs)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
for epoch in range(num_epochs):
for X_batch, y_batch in data_loader:
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch + 1) % 1 == 0:
with torch.no_grad():
y_pred = model(X)
epoch_loss = criterion(y_pred, y).item()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.6f}')
4.4 训练过程可视化
import matplotlib.pyplot as plt
# 记录训练过程
train_losses = []
model = LinearRegressionModel(num_inputs)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
for epoch in range(20):
epoch_loss = 0
for X_batch, y_batch in data_loader:
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
avg_loss = epoch_loss / len(data_loader)
train_losses.append(avg_loss)
# 绘制损失曲线
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(train_losses) + 1), train_losses, marker='o')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.grid(True)
plt.savefig('training_loss.png', dpi=300, bbox_inches='tight')
plt.show()
# 预测结果可视化
with torch.no_grad():
y_pred = model(X).numpy()
plt.figure(figsize=(10, 6))
plt.scatter(y.numpy(), y_pred, alpha=0.5)
plt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--')
plt.xlabel('True Values')
plt.ylabel('Predictions')
plt.title('True vs Predicted Values')
plt.grid(True)
plt.savefig('predictions.png', dpi=300, bbox_inches='tight')
plt.show()
第5章 实战应用案例
5.1 房价预测项目
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score
# 生成模拟房价数据
np.random.seed(42)
n_samples = 1000
data = {
'area': np.random.uniform(50, 200, n_samples), # 面积
'rooms': np.random.randint(1, 6, n_samples), # 房间数
'age': np.random.uniform(0, 30, n_samples), # 房龄
'distance_to_center': np.random.uniform(1, 20, n_samples) # 距离市中心
}
# 生成价格
data['price'] = (
1.5 * data['area'] + # 面积系数
10 * data['rooms'] + # 房间数系数
-0.8 * data['age'] + # 房龄系数
-2 * data['distance_to_center'] + # 距离系数
50 + # 基础价格
np.random.normal(0, 20, n_samples) # 噪声
)
df = pd.DataFrame(data)
# 特征和标签
X = df[['area', 'rooms', 'age', 'distance_to_center']].values
y = df['price'].values.reshape(-1, 1)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 转换为PyTorch张量
X_train_tensor = torch.FloatTensor(X_train_scaled)
y_train_tensor = torch.FloatTensor(y_train)
X_test_tensor = torch.FloatTensor(X_test_scaled)
y_test_tensor = torch.FloatTensor(y_test)
# 创建数据加载器
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
train_loader = DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)
# 定义模型
class HousePriceModel(nn.Module):
def __init__(self, input_dim):
super(HousePriceModel, self).__init__()
self.linear = nn.Linear(input_dim, 1)
def forward(self, x):
return self.linear(x)
model = HousePriceModel(X_train_scaled.shape[1])
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# 训练
num_epochs = 100
for epoch in range(num_epochs):
for X_batch, y_batch in train_loader:
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 评估
model.eval()
with torch.no_grad():
y_pred_train = model(X_train_tensor).numpy()
y_pred_test = model(X_test_tensor).numpy()
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
train_r2 = r2_score(y_train, y_pred_train)
test_r2 = r2_score(y_test, y_pred_test)
print(f"训练集 RMSE: {train_rmse:.2f} (万元)")
print(f"测试集 RMSE: {test_rmse:.2f} (万元)")
print(f"训练集 R²: {train_r2:.4f}")
print(f"测试集 R²: {test_r2:.4f}")
5.2 股票趋势分析
# 模拟股票数据
dates = pd.date_range('2023-01-01', periods=365, freq='D')
np.random.seed(42)
stock_data = {
'date': dates,
'open': np.random.uniform(100, 150, 365),
'high': np.random.uniform(105, 155, 365),
'low': np.random.uniform(95, 145, 365),
'volume': np.random.uniform(1000000, 5000000, 365),
'close': np.random.uniform(100, 150, 365) + np.random.normal(0, 2, 365)
}
stock_df = pd.DataFrame(stock_data)
stock_df['return'] = stock_df['close'].pct_change() # 日收益率
stock_df = stock_df.dropna()
print("股票数据示例:")
print(stock_df.head())
print(f"\n数据集形状: {stock_df.shape}")
5.3 医疗费用预测
# 模拟医疗数据
n_patients = 1000
medical_data = {
'age': np.random.randint(18, 80, n_patients),
'bmi': np.random.uniform(18, 40, n_patients),
'smoker': np.random.choice([0, 1], n_patients, p=[0.8, 0.2]),
'children': np.random.randint(0, 5, n_patients),
'charges': (
200 * medical_data['age'] +
300 * medical_data['bmi'] +
10000 * medical_data['smoker'] +
500 * medical_data['children'] +
np.random.normal(0, 2000, n_patients)
)
}
medical_df = pd.DataFrame(medical_data)
print("医疗数据统计:")
print(f"吸烟者平均费用: {medical_df[medical_df['smoker']==1]['charges'].mean():.2f}")
print(f"非吸烟者平均费用: {medical_df[medical_df['smoker']==0]['charges'].mean():.2f}")
print(f"费用标准差: {medical_df['charges'].std():.2f}")
5.4 工业生产优化
# 模拟生产数据
n_samples = 500
production_data = {
'temperature': np.random.uniform(150, 250, n_samples),
'pressure': np.random.uniform(5, 15, n_samples),
'time': np.random.uniform(10, 60, n_samples),
'speed': np.random.uniform(50, 150, n_samples),
'quality': (
0.3 * production_data['temperature'] +
0.5 * production_data['pressure'] +
0.2 * production_data['time'] -
0.1 * production_data['speed'] +
50 + np.random.normal(0, 5, n_samples)
)
}
prod_df = pd.DataFrame(production_data)
# 找出最优参数组合
optimal_params = {
'temperature': prod_df.loc[prod_df['quality'].idxmax(), 'temperature'],
'pressure': prod_df.loc[prod_df['quality'].idxmax(), 'pressure'],
'time': prod_df.loc[prod_df['quality'].idxmax(), 'time'],
'speed': prod_df.loc[prod_df['quality'].idxmax(), 'speed'],
'quality': prod_df['quality'].max()
}
print("最优生产参数:")
for k, v in optimal_params.items():
print(f" {k}: {v:.2f}")
第6章 模型优化与调参
6.1 学习率调优策略
# 不同学习率对比
learning_rates = [0.001, 0.01, 0.03, 0.1, 0.3]
results = {}
for lr in learning_rates:
model = LinearRegressionModel(num_inputs)
optimizer = torch.optim.SGD(model.parameters(), lr=lr)
criterion = nn.MSELoss()
losses = []
for epoch in range(20):
epoch_loss = 0
for X_batch, y_batch in data_loader:
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
losses.append(epoch_loss / len(data_loader))
results[lr] = losses[-1] # 记录最终损失
print("不同学习率的最终损失:")
for lr, loss in results.items():
print(f" lr={lr:.3f}: {loss:.6f}")
6.2 批量大小选择
batch_sizes = [4, 8, 16, 32, 64, 128]
results = {}
for bs in batch_sizes:
data_loader = DataLoader(dataset=dataset, batch_size=bs, shuffle=True)
model = LinearRegressionModel(num_inputs)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
criterion = nn.MSELoss()
for epoch in range(10):
for X_batch, y_batch in data_loader:
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
final_loss = criterion(model(X), y).item()
results[bs] = final_loss
print("不同批量大小的最终损失:")
for bs, loss in results.items():
print(f" batch_size={bs}: {loss:.6f}")
6.3 正则化技术
# L2正则化(权重衰减)
model = LinearRegressionModel(num_inputs)
optimizer = torch.optim.SGD(
model.parameters(),
lr=0.01,
weight_decay=0.01 # L2正则化系数
)
criterion = nn.MSELoss()
# 训练
for epoch in range(20):
for X_batch, y_batch in data_loader:
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("L2正则化后的权重:")
print(f"w: {model.linear.weight.data.numpy()}")
print(f"权重范数: {torch.norm(model.linear.weight).item():.4f}")
6.4 早停与模型保存
# 早停实现
class EarlyStopping:
def __init__(self, patience=5, min_delta=0.001):
self.patience = patience
self.min_delta = min_delta
self.counter = 0
self.best_loss = None
self.early_stop = False
def __call__(self, val_loss):
if self.best_loss is None:
self.best_loss = val_loss
elif val_loss > self.best_loss - self.min_delta:
self.counter += 1
if self.counter >= self.patience:
self.early_stop = True
else:
self.best_loss = val_loss
self.counter = 0
return self.early_stop
# 模型保存与加载
model = LinearRegressionModel(num_inputs)
torch.save(model.state_dict(), 'linear_model.pth')
# 加载模型
model_loaded = LinearRegressionModel(num_inputs)
model_loaded.load_state_dict(torch.load('linear_model.pth'))
model_loaded.eval()
第7章 常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| 损失不下降 | 增大学习率或检查数据是否标准化 |
| 损失震荡剧烈 | 减小学习率 |
| 梯度为 NaN | 学习率过大导致梯度爆炸,降低学习率 |
| 训练集和测试集差距大 | 过拟合,增加数据量或添加正则化 |
| 忘记 zero_grad() | 每次迭代前必须调用 optimizer.zero_grad() |
| GPU 不可用 | 检查 CUDA 版本与 PyTorch 版本是否匹配 |
7.1 损失不下降
问题: 训练过程中损失值保持不变或震荡
解决方案:
# 1. 检查学习率是否合适
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 尝试更小的学习率
# 2. 检查数据是否标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 3. 检查梯度是否正常
for name, param in model.named_parameters():
print(f"{name} gradient norm: {param.grad.norm().item()}")
7.2 过拟合问题
问题: 训练集表现好,测试集表现差
解决方案:
# 1. 使用正则化 optimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01) # 2. 增加数据 # 3. 减少模型复杂度 # 4. 使用早停 early_stopping = EarlyStopping(patience=5)
7.3 GPU加速
# 检查GPU可用性
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")
# 将模型和数据移动到GPU
model = model.to(device)
X = X.to(device)
y = y.to(device)
# 训练时同样在GPU上
for X_batch, y_batch in data_loader:
X_batch = X_batch.to(device)
y_batch = y_batch.to(device)
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
总结
本文从 PyTorch 的基础特性出发,系统讲解了线性回归的完整实现流程——从 nn.Linear 的模型定义、nn.MSELoss 的损失计算、optim.SGD 的参数优化,到 DataLoader 的批量加载、GPU 加速的部署优化、模型的保存与评估。核心要点:
- PyTorch 三大核心组件:
nn.Linear(模型)、nn.MSELoss(损失)、optim.SGD(优化器)是构建任何深度学习模型的基石 - 训练循环四步法:前向传播 → 损失计算 → 反向传播 → 参数更新,是 PyTorch 训练的标准范式
- 工程化实践:DataLoader 批量加载、训练测试分离、GPU 加速、模型保存加载是实际项目中不可或缺的技能
- 从零到框架的对应关系:理解简洁 API 背后的实现逻辑,是掌握深度学习的必经之路
线性回归虽简单,但其中蕴含的前向传播、损失计算、梯度下降、参数更新等概念,是所有复杂深度学习模型的基础。掌握 PyTorch 实现线性回归的完整流水线,你将轻松过渡到更复杂的神经网络模型。
附录
附录A:完整代码实现
"""
PyTorch线性回归完整实现
"""
import torch
import numpy as np
import random
import matplotlib.pyplot as plt
from torch.utils.data import TensorDataset, DataLoader
# ==================== 1. 数据生成 ====================
def generate_data(num_examples=1000, num_inputs=2, seed=42):
"""生成带噪声的线性数据"""
torch.manual_seed(seed)
np.random.seed(seed)
true_w = torch.tensor([[2.0], [-3.4]])
true_b = 4.2
X = torch.normal(0, 1, (num_examples, num_inputs))
noise = torch.normal(0, 0.01, (num_examples, 1))
y = torch.matmul(X, true_w) + true_b + noise
return X, y, true_w, true_b
# ==================== 2. 数据迭代器 ====================
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]
# ==================== 3. 模型定义 ====================
def linreg(X, w, b):
"""线性回归模型"""
return torch.matmul(X, w) + b
# ==================== 4. 损失函数 ====================
def squared_loss(y_hat, y):
"""均方损失函数"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
# ==================== 5. 优化器 ====================
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
# ==================== 6. 训练函数 ====================
def train_linear_regression(
X, y, true_w, true_b,
lr=0.03, num_epochs=5, batch_size=10
):
"""训练线性回归模型"""
num_inputs = X.shape[1]
# 初始化参数
w = torch.normal(0, 0.01, size=(num_inputs, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
losses = []
print("开始训练...")
print("=" * 60)
for epoch in range(num_epochs):
for X_batch, y_batch in data_iter(batch_size, X, y):
# 前向传播
y_hat = linreg(X_batch, w, b)
# 计算损失
loss = squared_loss(y_hat, y_batch).mean()
# 反向传播
loss.backward()
# 更新参数
sgd([w, b], lr, batch_size)
# 计算epoch损失
with torch.no_grad():
epoch_loss = squared_loss(linreg(X, w, b), y).mean().item()
losses.append(epoch_loss)
print(f'Epoch {epoch + 1}/{num_epochs}, Loss: {epoch_loss:.6f}')
# 评估结果
print("\n" + "=" * 60)
print("训练完成!")
print("=" * 60)
print(f'真实权重: {true_w.flatten().numpy()}')
print(f'学到权重: {w.detach().flatten().numpy()}')
print(f'权重相对误差: {((w - true_w).abs() / true_w.abs()).mean().item():.4%}')
print()
print(f'真实偏置: {true_b:.4f}')
print(f'学到偏置: {b.item():.4f}')
print(f'偏置相对误差: {abs(b.item() - true_b) / abs(true_b):.4%}')
return w, b, losses
# ==================== 7. 主函数 ====================
def main():
# 生成数据
X, y, true_w, true_b = generate_data()
# 训练模型
w, b, losses = train_linear_regression(X, y, true_w, true_b)
# 可视化损失曲线
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(losses) + 1), losses, marker='o')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.grid(True)
plt.savefig('training_loss.png', dpi=300, bbox_inches='tight')
plt.show()
if __name__ == "__main__":
main()
附录B:API速查表
| 功能 | PyTorch API | 说明 |
|---|---|---|
| 创建张量 | torch.tensor() | 从数据创建张量 |
| 随机张量 | torch.randn() | 标准正态分布 |
| 线性层 | nn.Linear() | 全连接层 |
| MSE损失 | nn.MSELoss() | 均方误差 |
| SGD优化器 | optim.SGD() | 随机梯度下降 |
| Adam优化器 | optim.Adam() | 自适应矩估计 |
| 数据加载 | DataLoader() | 批量数据加载 |
通过本文,你不仅掌握了线性回归的理论和实现,更建立了使用PyTorch进行深度学习开发的完整技能体系。
以上就是使用PyTorch实现线性回归的全面指南的详细内容,更多关于PyTorch实现线性回归的资料请关注脚本之家其它相关文章!
相关文章
Tensorflow2.10使用BERT从文本中抽取答案实现详解
这篇文章主要为大家介绍了Tensorflow2.10使用BERT从文本中抽取答案实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-04-04


最新评论