1. 项目概述
今天咱们来聊聊PyTorch框架下的Logistic Regression实现,这是深度学习入门必须掌握的经典二分类算法。作为《PyTorch入门深度学习实践》系列的第五篇,我将带大家从理论到代码完整走一遍二分类问题的解决流程。
Logistic Regression虽然名字里带"回归",但它实际上是解决分类问题的利器。我在实际项目中经常用它作为基线模型(baseline),它的优势在于模型简单、训练速度快、解释性强。对于刚接触深度学习的新手,这是理解神经网络前最好的垫脚石。
注意:本文默认读者已掌握PyTorch基础张量操作和自动求导机制。如果还没安装PyTorch,推荐使用Anaconda创建Python 3.8+环境,通过官方命令
conda install pytorch torchvision -c pytorch安装最新稳定版。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理拆解
2.1 逻辑回归的数学本质
逻辑回归的核心是sigmoid函数:
$$
\sigma(z) = \frac{1}{1+e^{-z}}
$$
这个S型曲线能将任意实数映射到(0,1)区间,完美适配概率输出需求。当z=0时,σ(z)=0.5,这就是决策边界。
在PyTorch中,我们通常用torch.sigmoid()直接实现。但要注意它的反向传播计算是:
$$
\sigma'(z) = \sigma(z)(1-\sigma(z))
$$
这个特性让它在神经网络中表现优异。
2.2 二分类的损失函数
二分类问题最常用的损失函数是二元交叉熵(BCE):
$$
L = -\frac{1}{N}\sum_{i=1}^N [y_i\log(p_i) + (1-y_i)\log(1-p_i)]
$$
PyTorch提供了两个实现版本:
nn.BCELoss():需手动添加sigmoid层nn.BCEWithLogitsLoss():内置sigmoid,数值更稳定
我强烈推荐后者,因为它采用了log-sum-exp技巧来避免数值溢出问题。实测在极端值情况下,前者容易出现NaN损失。
3. 完整实现流程
3.1 数据准备与加载
我们先构造一个简单的二维数据集方便可视化:
python复制from sklearn.datasets import make_classification
import torch
from torch.utils.data import DataLoader, TensorDataset
# 生成线性可分数据
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0,
n_clusters_per_class=1, random_state=42)
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32).view(-1, 1) # 转为列向量
# 划分训练测试集
split = int(0.8 * len(X))
train_data = TensorDataset(X[:split], y[:split])
test_data = TensorDataset(X[split:], y[split:])
# 创建DataLoader
batch_size = 64
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_data, batch_size=batch_size)
关键技巧:记得将标签y转为列向量(view(-1,1)),否则后续计算会因维度不匹配报错。
3.2 模型定义
用PyTorch定义逻辑回归模型简直不要太简单:
python复制import torch.nn as nn
class LogisticRegression(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.linear = nn.Linear(input_dim, 1)
def forward(self, x):
return self.linear(x) # BCEWithLogitsLoss会自动加sigmoid
虽然只有一层全连接,但要注意:
- 输出维度是1不是2(二分类只需一个概率值)
- 不要手动加sigmoid,因为BCEWithLogitsLoss已经包含
3.3 训练循环实现
完整的训练流程如下:
python复制model = LogisticRegression(input_dim=2)
criterion = nn.BCEWithLogitsLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
epochs = 100
for epoch in range(epochs):
for inputs, labels in train_loader:
# 前向传播
outputs = model(inputs)
loss = criterion(outputs, labels)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 每个epoch打印进度
if (epoch+1) % 10 == 0:
print(f'Epoch {epoch+1}/{epochs}, Loss: {loss.item():.4f}')
这里有几个超参数需要关注:
- 学习率lr:0.1对于这种简单模型比较合适
- 优化器:小数据集用SGD足够,大数据建议Adam
- batch_size:根据显存调整,一般64-256
4. 模型评估与可视化
4.1 评估指标计算
二分类常用指标包括准确率、精确率、召回率等:
python复制from sklearn.metrics import accuracy_score
def evaluate(model, loader):
model.eval()
y_true, y_pred = [], []
with torch.no_grad():
for inputs, labels in loader:
outputs = model(inputs)
preds = (torch.sigmoid(outputs) > 0.5).float()
y_true.extend(labels.tolist())
y_pred.extend(preds.tolist())
return accuracy_score(y_true, y_pred)
train_acc = evaluate(model, train_loader)
test_acc = evaluate(model, test_loader)
print(f'Train Accuracy: {train_acc:.2f}, Test Accuracy: {test_acc:.2f}')
注意:测试时一定要用model.eval()关闭dropout和batchnorm,并用torch.no_grad()禁用梯度计算。
4.2 决策边界可视化
用matplotlib绘制决策边界能直观理解模型:
python复制import matplotlib.pyplot as plt
import numpy as np
# 创建网格点
x_min, x_max = X[:, 0].min()-1, X[:, 0].max()+1
y_min, y_max = X[:, 1].min()-1, X[:, 1].max()+1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
np.arange(y_min, y_max, 0.1))
# 预测网格点类别
with torch.no_grad():
Z = model(torch.tensor(np.c_[xx.ravel(), yy.ravel()],
dtype=torch.float32))
Z = (torch.sigmoid(Z) > 0.5).reshape(xx.shape)
# 绘制
plt.contourf(xx, yy, Z, alpha=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y.squeeze(), s=20, edgecolor='k')
plt.title('Decision Boundary')
plt.show()
5. 实战技巧与问题排查
5.1 类别不平衡处理
当正负样本比例悬殊时(如1:9),可以:
- 使用带权重的BCE损失:
python复制pos_weight = torch.tensor([9.0]) # 少数类的权重
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
- 对少数类过采样或多数类欠采样
- 改用F1-score作为评估指标
5.2 梯度爆炸/消失对策
如果训练出现NaN损失,可以尝试:
- 数据标准化:
X = (X - X.mean()) / X.std() - 梯度裁剪:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) - 调整学习率
5.3 模型保存与加载
训练好的模型可以这样保存和复用:
python复制# 保存
torch.save(model.state_dict(), 'logistic_reg.pth')
# 加载
new_model = LogisticRegression(input_dim=2)
new_model.load_state_dict(torch.load('logistic_reg.pth'))
6. 扩展应用场景
虽然我们用了简单示例,但Logistic Regression能应用于:
- 垃圾邮件检测(文本分类)
- 信用卡欺诈检测(异常检测)
- 疾病诊断(医疗影像分析)
在实际项目中,我常用它作为特征选择工具——通过权重系数判断特征重要性。例如:
python复制weights = model.linear.weight.detach().numpy().flatten()
print(f'Feature importance: {weights}')
对于想继续深入的同学,下一步可以:
- 尝试用多层感知机(MLP)提升非线性分类能力
- 在真实数据集(如乳腺癌检测)上实践
- 学习使用PyTorch Lightning简化训练流程
