2. 逻辑回归分类器

任务说明

本案例使用 PyTorch 构建一个逻辑回归模型,完成二分类任务。模型输入为二维特征,输出为属于正类的概率。训练过程使用交叉熵损失函数(BCEWithLogitsLoss),通过随机梯度下降优化器更新参数。最终目标是让模型学习到一个线性决策边界,能够区分两类样本。

数据准备

我们生成两类线性可分的二维数据点:正类(标签1)中心在 (2, 2) 附近,负类(标签0)中心在 (-2, -2) 附近,并加入少量高斯噪声。训练集共 200 个样本,测试集 100 个样本。

模型实现

import torch
import torch.nn as nn
import matplotlib.pyplot as plt

# 设置随机种子保证可复现
torch.manual_seed(42)

# 生成数据
def generate_data(n_samples, center_pos, center_neg):
    pos = torch.randn(n_samples // 2, 2) + torch.tensor(center_pos)
    neg = torch.randn(n_samples // 2, 2) + torch.tensor(center_neg)
    X = torch.cat([pos, neg], dim=0)
    y = torch.cat([torch.ones(n_samples // 2, 1), torch.zeros(n_samples // 2, 1)], dim=0)
    # 打乱顺序
    perm = torch.randperm(n_samples)
    return X[perm], y[perm]

X_train, y_train = generate_data(200, [2.0, 2.0], [-2.0, -2.0])
X_test, y_test = generate_data(100, [2.0, 2.0], [-2.0, -2.0])

# 逻辑回归模型
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)  # 输出logits,不经过sigmoid

model = LogisticRegression(2)
criterion = nn.BCEWithLogitsLoss()  # 交叉熵损失(含sigmoid)
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

# 训练
epochs = 200
for epoch in range(epochs):
    model.train()
    optimizer.zero_grad()
    logits = model(X_train)
    loss = criterion(logits, y_train)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 50 == 0:
        print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

# 评估
model.eval()
with torch.no_grad():
    test_logits = model(X_test)
    test_probs = torch.sigmoid(test_logits)
    preds = (test_probs > 0.5).float()
    accuracy = (preds == y_test).float().mean().item()
    print(f'Test Accuracy: {accuracy:.4f}')

# 可视化决策边界
w = model.linear.weight.detach().numpy().flatten()
b = model.linear.bias.detach().numpy().item()
x_vals = torch.linspace(-4, 4, 100)
y_vals = -(w[0] * x_vals + b) / w[1]

plt.figure(figsize=(6, 5))
plt.scatter(X_train[y_train.squeeze() == 1, 0], X_train[y_train.squeeze() == 1, 1], c='blue', label='Class 1')
plt.scatter(X_train[y_train.squeeze() == 0, 0], X_train[y_train.squeeze() == 0, 1], c='red', label='Class 0')
plt.plot(x_vals.numpy(), y_vals.numpy(), 'g-', linewidth=2, label='Decision Boundary')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.title('Logistic Regression Decision Boundary')
plt.grid(True)
plt.show()

关键实现说明

  • 损失函数:使用 BCEWithLogitsLoss,它将 Sigmoid 激活与二元交叉熵损失合并计算,数值上更稳定。
  • 模型输出:前向传播直接输出 logits(未经过 Sigmoid),在评估时通过 torch.sigmoid 转换为概率,并以 0.5 为阈值进行分类。
  • 优化器:采用随机梯度下降(SGD),学习率设为 0.1,训练 200 轮。

验证方法

运行上述代码,观察训练过程中损失值应逐步下降。训练完成后,程序会打印测试集准确率。由于数据线性可分且两类中心距离较远,预期测试准确率应接近 100%(通常在 98% 以上)。同时,代码会绘制训练数据散点图与学到的决策边界,边界应大致位于两类样本中间位置,将两类点清晰分开。

逻辑回归训练与验证流程 生成数据 200训练/100测试 模型定义 Linear(2→1) 损失函数 BCEWithLogitsLoss SGD lr=0.1 训练循环 (200 epochs) 前向传播 → 计算损失 → 反向传播 → 参数更新 评估 准确率 可视化 决策边界 + 数据点 验证判定标准 ① 训练损失逐步下降至接近 0 ② 测试准确率 ≥ 98% ③ 决策边界位于两类样本中间

运行与验证

将代码保存为 logistic_regression.py,在已安装 PyTorch 和 Matplotlib 的 Python 环境中运行:

python logistic_regression.py

程序会输出每 50 轮的损失值以及最终测试准确率。若损失持续下降且准确率高于 98%,同时可视化图形中绿色决策边界正确分隔蓝色与红色样本点,则判定案例实现成功。