自动微分与手动构建模型实战
一、自动微分
1. 基础概念
自动微分模块 torch.autograd
负责自动计算张量操作的梯度,主要特点包括:
- 张量:
requires_grad=True
的张量会追踪其所有操作,以便后续计算梯度。 - 计算图:动态创建计算图,记录操作。
- 反向传播:通过
.backward()
计算梯度。 - 梯度访问:通过
.grad
属性访问计算结果。
2. 计算梯度
2.1 标量梯度计算
import torch
# 创建一个标量张量
x = torch.tensor(7.0, requires_grad=True)
y = x**2 + 2 * x + 7
# 计算梯度
y.backward()
# 查看梯度
print(x.grad) # 输出:tensor(16.)
解释:
requires_grad=True
表示张量需要计算梯度。.backward()
会对标量计算梯度。- 结果存储在
x.grad
中。
2.2 向量梯度计算
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x**2 + 2 * x + 7
z = y.mean() # 将向量变为标量
z.backward()
print(x.grad) # 输出向量的梯度
解释:
- 向量梯度需要通过
.mean()
或.sum()
将其变为标量。 .backward()
自动传播计算各分量的梯度。
2.3 梯度清零
梯度会累加,清零可以防止影响下一次计算:
if x.grad is not None:
x.grad.zero_()
二、手动构建模型实战
1. 数据处理
1.1 构建数据集
from sklearn.datasets import make_regression
import torch
# 生成数据
X, y, coef = make_regression(
n_samples=1000, n_features=5, noise=5, coef=True, random_state=0
)
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
1.2 数据加载器
import random, math
def data_loader(x, y, batch_size=16):
indices = list(range(x.shape[0]))
random.shuffle(indices)
num_batches = math.ceil(len(indices) / batch_size)
for i in range(num_batches):
start = i * batch_size
end = min(start + batch_size, len(indices))
yield x[indices[start:end]], y[indices[start:end]]
2. 模型与损失函数
2.1 模型函数
def linear_regression(x, w, b):
return torch.matmul(x, w) + b
2.2 损失函数
def mean_squared_error(y_pred, y_true):
return torch.mean((y_pred - y_true)**2)
3. 参数初始化与优化器
3.1 参数初始化
def initialize_params(n_features):
w = torch.randn(n_features, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)
return w, b
3.2 优化器 (SGD)
def sgd(w, b, dw, db, lr, batch_size):
w.data -= lr * dw / batch_size
b.data -= lr * db / batch_size
4. 训练模型
训练函数
def train():
X, y, coef = make_regression_data()
w, b = initialize_params(X.shape[1])
lr, epochs, batch_size = 0.01, 100, 16
for epoch in range(epochs):
total_loss = 0
for X_batch, y_batch in data_loader(X, y, batch_size):
y_pred = linear_regression(X_batch, w, b)
loss = mean_squared_error(y_pred, y_batch)
total_loss += loss.item()
# 反向传播和参数更新
loss.backward()
w.grad.zero_()
b.grad.zero_()
sgd(w, b, w.grad, b.grad, lr, batch_size)
print(f"Epoch {epoch + 1}, Loss: {total_loss}")
5. 总结
通过以上代码,展示了从数据处理、模型定义、训练过程到结果的完整实现。训练时通过反向传播和优化器不断调整参数,逐步减小误差。
最后是整个手动构建的代码
import math
import random
import torch
import numpy as np
from sklearn.datasets import make_regression
import pandas as pd
def build_dataset():
"""
使用 sklearn 的 make_regression 方法来构建一个模拟的回归数据集。
make_regression 方法的参数解释:
- n_samples: 生成的样本数量,决定了数据集的规模。
- n_features: 生成的特征数量,决定了数据维度。
- noise: 添加到目标变量的噪声标准差,用于模拟真实世界数据的不完美。
- coef: 如果为 True, 会返回生成数据的真实系数,用于了解特征与目标变量间的真实关系。
- random_state: 随机数生成的种子,确保在多次运行中能够复现相同的结果。
返回:
- X: 生成的特征矩阵。
- y: 生成的目标变量。
- coef: 如果在调用时 coef 参数为 True,则还会返回真实系数。
"""
noise = random.randint(1, 3)
bias = 14.5
X, y, coef = make_regression(
n_samples=1000, n_features=5, bias=bias, noise=noise, coef=True, random_state=0
)
# 数据转换为张量
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
coef = torch.tensor(coef, dtype=torch.float32)
bias = torch.tensor(bias, dtype=torch.float32)
return X, y, coef, bias
def data_loader(x, y, batch_size=16):
"""
将数据集转换为迭代器,以便在训练过程中进行批量处理。
"""
# 获取样本数量
num_samples = x.shape[0]
# 构建数据索引
indices = list(range(num_samples))
# 打乱数据顺序
random.shuffle(indices)
# 计算总的批次数量:向上取整
num_batches = math.ceil(num_samples / batch_size)
for i in range(num_batches):
start = i * batch_size
end = min((i + 1) * batch_size, num_samples)
# 开始切片数据
train_X = x[indices[start:end]]
train_y = y[indices[start:end]]
# 异步响应数据集
yield train_X, train_y
# 构建模型函数:权重参数和偏执参数
# 初始化参数
def initialize_params(n_features):
# 随机初始化权重w,并将偏置b初始化为0
w = torch.randn(n_features, requires_grad=True, dtype=torch.float32)
b = torch.tensor(0.0, requires_grad=True, dtype=torch.float32)
return w, b
def linear_regression(x, w, b):
return torch.matmul(x, w) + b
# 构建模型函数:损失函数和优化器
def mean_squared_error(y_pred, y_true):
return torch.mean((y_pred - y_true) ** 2)
# 优化器(SGD,手动实现)
def sgd(w, b, dw, db, learning_rate, batch_size):
w.data -= learning_rate * dw.data / batch_size
b.data -= learning_rate * db.data / batch_size
return w, b
# 训练函数
def train():
# 1. 构建数据集
X, y, coef, bias = build_dataset()
# 2. 初始化模型参数
w, b = initialize_params(X.shape[1])
# 3. 定义训练参数
learning_rate = 0.01
epochs = 120
batch_size = 16
# 4. 开始训练
for epoch in range(epochs):
epoch_loss = 0
num_batches = 0
for train_X, train_y in data_loader(X, y, batch_size):
num_batches += 1
# 5. 前向传播
y_pred = linear_regression(train_X, w, b)
# 6. 计算损失
loss = mean_squared_error(y_pred, train_y)
# 9. 梯度清零
if w.grad is not None:
w.grad.zero_()
if b.grad is not None:
b.grad.zero_()
# 7. 反向传播:会自动计算梯度
loss.backward()
# 8. 更新参数
w, b = sgd(w, b, w.grad, b.grad, learning_rate, batch_size)
# 10. 训练批次及损失率
epoch_loss += loss.item()
print(f"Epoch: {epoch}, Loss: {epoch_loss / num_batches}")
return coef, bias, w, b
if __name__ == "__main__":
coef, bias, w, b = train()
print(f"真实系数: {coef}")
print(f"预测系数: {w}")
print(f"真实偏置: {bias}")
print(f"预测偏置: {b}")
结果为:
Epoch: 0, Loss: 9141.109053354414
......
Epoch: 9, Loss: 2438.3174680679563
......
Epoch: 18, Loss: 643.4502219548301
......
Epoch: 31, Loss: 100.09944831000433
......
Epoch: 48, Loss: 9.588121845608665
......
Epoch: 64, Loss: 1.8927827449071974
......
Epoch: 119, Loss: 1.006377797278147
真实系数: tensor([41.2059, 66.4995, 10.7145, 60.1951, 25.9615])
预测系数: tensor([41.1517, 66.4983, 10.7523, 60.2103, 25.9089], requires_grad=True)
真实偏置: 14.5
预测偏置: 14.50900650024414