
逻辑回归与交叉熵损失从对数似然函数到PyTorch 2.0实战实现当我们需要解决二分类问题时逻辑回归往往是第一个被想到的算法。但你是否真正理解这个看似简单的模型背后蕴含的数学之美本文将带你深入探索逻辑回归的核心——从最大似然估计的数学原理到交叉熵损失函数的推导最后用PyTorch 2.0实现一个完整的训练流程。1. 逻辑回归的数学基础逻辑回归虽然名字中有回归但它实际上是一种分类算法。它的核心思想是通过sigmoid函数将线性回归的输出映射到(0,1)区间表示属于正类的概率。1.1 Sigmoid函数与概率表达逻辑回归使用sigmoid函数也称为logistic函数作为激活函数def sigmoid(z): return 1 / (1 np.exp(-z))这个函数的输出可以解释为概率P(y1|x) σ(wᵀx b)P(y0|x) 1 - σ(wᵀx b)其中w是权重向量b是偏置项x是输入特征。1.2 最大似然估计原理最大似然估计(MLE)是一种参数估计方法其核心思想是找到能使观测数据出现概率最大的参数值。对于逻辑回归我们希望找到w和b使得给定数据集的联合概率最大。对于独立同分布的样本联合概率可以表示为L(w,b) ∏ P(yⁱ|xⁱ; w,b)取对数后得到对数似然函数ℓ(w,b) ∑ [yⁱ log(σ(wᵀxⁱ b)) (1-yⁱ)log(1 - σ(wᵀxⁱ b))]2. 从对数似然到交叉熵损失2.1 对数似然函数的推导让我们详细推导对数似然函数的表达式。对于单个样本概率可以统一表示为P(yⁱ|xⁱ) (σ(wᵀxⁱ b))^{yⁱ} (1 - σ(wᵀxⁱ b))^{1-yⁱ}取对数后log P(yⁱ|xⁱ) yⁱ log(σ(wᵀxⁱ b)) (1-yⁱ) log(1 - σ(wᵀxⁱ b))对所有样本求和就得到了完整的对数似然函数。2.2 交叉熵损失的等价性交叉熵是信息论中的概念衡量两个概率分布之间的差异。在二分类问题中交叉熵损失定义为H(p,q) -[p log q (1-p) log(1-q)]其中p是真实标签q是预测概率。可以看到最大化对数似然函数等价于最小化交叉熵损失。为什么使用对数似然而不是直接优化准确率对数似然提供了平滑的优化目标能够反映预测概率与真实标签的差距数学性质良好便于求导和优化3. PyTorch 2.0实现逻辑回归现在让我们用PyTorch 2.0实现一个完整的逻辑回归模型并可视化训练过程。3.1 数据准备首先我们生成一个简单的二分类数据集import torch from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成数据集 X, y make_classification(n_samples1000, n_features2, n_redundant0, n_clusters_per_class1, random_state42) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 转换为PyTorch张量 X_train torch.tensor(X_train, dtypetorch.float32) X_test torch.tensor(X_test, dtypetorch.float32) y_train torch.tensor(y_train, dtypetorch.float32).view(-1, 1) y_test torch.tensor(y_test, dtypetorch.float32).view(-1, 1)3.2 模型定义使用PyTorch定义逻辑回归模型非常简单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 torch.sigmoid(self.linear(x))3.3 训练过程下面是完整的训练代码包括损失函数和优化器的设置# 初始化模型 model LogisticRegression(X_train.shape[1]) # 定义损失函数和优化器 criterion nn.BCELoss() # 二分类交叉熵损失 optimizer torch.optim.SGD(model.parameters(), lr0.1) # 训练参数 epochs 100 train_losses [] test_losses [] for epoch in range(epochs): # 训练阶段 model.train() optimizer.zero_grad() outputs model(X_train) loss criterion(outputs, y_train) loss.backward() optimizer.step() train_losses.append(loss.item()) # 测试阶段 model.eval() with torch.no_grad(): test_outputs model(X_test) test_loss criterion(test_outputs, y_test) test_losses.append(test_loss.item()) if (epoch1) % 10 0: print(fEpoch {epoch1}/{epochs}, Train Loss: {loss.item():.4f}, Test Loss: {test_loss.item():.4f})3.4 决策边界可视化为了更直观地理解模型的分类效果我们可以绘制决策边界import matplotlib.pyplot as plt import numpy as np def plot_decision_boundary(model, X, y): # 设置绘图范围 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.linspace(x_min, x_max, 100), np.linspace(y_min, y_max, 100)) # 预测每个网格点的类别 X_grid torch.tensor(np.c_[xx.ravel(), yy.ravel()], dtypetorch.float32) with torch.no_grad(): Z model(X_grid).numpy() Z Z.reshape(xx.shape) # 绘制等高线和散点图 plt.contourf(xx, yy, Z, alpha0.8) plt.scatter(X[:, 0], X[:, 1], cy, edgecolorsk) plt.xlabel(Feature 1) plt.ylabel(Feature 2) plt.title(Decision Boundary) plot_decision_boundary(model, X_train.numpy(), y_train.numpy().ravel()) plt.show()4. 高级话题与优化技巧4.1 正则化与L2惩罚为了防止过拟合我们可以在损失函数中加入L2正则化项# 修改损失函数 criterion nn.BCELoss() l2_lambda 0.01 # 正则化强度 # 在训练循环中计算L2惩罚 l2_norm sum(p.pow(2.0).sum() for p in model.parameters()) loss criterion(outputs, y_train) l2_lambda * l2_norm4.2 不同优化器的比较PyTorch提供了多种优化器我们可以比较它们的效果优化器收敛速度内存占用适合场景SGD慢低简单模型Adam快中大多数场景RMSprop中等中RNN/LSTM# 使用Adam优化器 optimizer torch.optim.Adam(model.parameters(), lr0.01)4.3 学习率调度动态调整学习率可以改善训练效果# 添加学习率调度器 scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size30, gamma0.1) # 在每个epoch后调用 scheduler.step()4.4 类别不平衡问题处理当数据集中正负样本比例失衡时可以对少数类样本过采样对多数类样本欠采样在损失函数中使用类别权重# 计算类别权重 pos_weight torch.tensor([(len(y_train) - y_train.sum()) / y_train.sum()]) criterion nn.BCEWithLogitsLoss(pos_weightpos_weight)在实际项目中逻辑回归虽然简单但通过特征工程和适当的调整往往能取得不错的效果。特别是在可解释性要求高的场景逻辑回归的优势更加明显。