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% 以上)。同时,代码会绘制训练数据散点图与学到的决策边界,边界应大致位于两类样本中间位置,将两类点清晰分开。
运行与验证
将代码保存为 logistic_regression.py,在已安装 PyTorch 和 Matplotlib 的 Python 环境中运行:
python logistic_regression.py
程序会输出每 50 轮的损失值以及最终测试准确率。若损失持续下降且准确率高于 98%,同时可视化图形中绿色决策边界正确分隔蓝色与红色样本点,则判定案例实现成功。