逻辑回归与最大似然估计:从概率公式到 PyTorch 3 行代码实现

逻辑回归与最大似然估计:从概率公式到 PyTorch 3 行代码实现

在机器学习领域,逻辑回归作为经典的分类算法,其背后的数学原理常常让实践者感到既熟悉又陌生。我们可能早已熟练调用sklearnLogisticRegression,或是用几行PyTorch代码完成二分类任务,但当被问及"为什么使用交叉熵损失而非均方误差"时,却难以给出令人信服的解释。本文将打破这种"会用不懂原理"的困境,带你从概率论的基本公式出发,通过最大似然估计的视角,真正理解逻辑回归的每一个设计决策,最终落地到简洁高效的PyTorch实现。

1. 概率视角下的分类问题本质

当我们处理二分类问题时,本质上是在寻找一个能够量化"某样本属于正类可能性"的函数。设输入特征为x,模型参数为θ,这个函数就是条件概率P(y=1|x;θ)。逻辑回归选择sigmoid函数作为概率映射并非偶然——其输出范围[0,1]天然适合概率解释,且具有良好的数学性质。

sigmoid函数的表达式为:

σ(z) = 1 / (1 + exp(-z))

其中z=θᵀx。这个看似简单的函数隐藏着关键特性:

  • 当z→+∞时,σ(z)→1
  • 当z→-∞时,σ(z)→0
  • 在z=0处取得决策边界,σ(0)=0.5

这些特性使得sigmoid成为连接线性预测与概率输出的理想桥梁。但更深刻的问题是:我们如何证明使用这个特定形式函数的合理性?答案就藏在最大似然估计的原理中。

2. 最大似然估计的直观理解

假设我们观察到一个数据集D={(x₁,y₁),...,(xₙ,yₙ)},最大似然估计的核心思想是:寻找使得当前观测数据出现概率最大的参数θ。用数学语言表达就是:

θ̂ = argmax₀ P(D|θ)

对于逻辑回归,由于各样本独立,联合概率可分解为:

L(θ) = ∏ᵢ P(yᵢ|xᵢ;θ)

直接优化这个连乘式存在两个实际问题:

  1. 多个小于1的概率连乘会导致数值下溢
  2. 连乘形式不利于求导优化

取对数可以完美解决这两个问题,同时保持原函数的单调性:

ℓ(θ) = log L(θ) = ∑ᵢ log P(yᵢ|xᵢ;θ)

3. 从伯努利分布到交叉熵损失

对于二分类问题,每个标签yᵢ可看作伯努利随机变量,其概率质量函数为:

P(yᵢ|xᵢ;θ) = (σ(θᵀxᵢ))^yᵢ (1-σ(θᵀxᵢ))^(1-yᵢ)

取对数后得到单个样本的对数似然:

ℓᵢ(θ) = yᵢ log(σ(θᵀxᵢ)) + (1-yᵢ)log(1-σ(θᵀxᵢ))

这正是交叉熵的形式!将所有样本相加,我们的目标就是最大化:

ℓ(θ) = ∑ᵢ [yᵢ log σ(θᵀxᵢ) + (1-yᵢ)log(1-σ(θᵀxᵢ))]

为了与常规的"最小化损失"框架统一,我们取负号得到交叉熵损失:

J(θ) = -ℓ(θ)

4. 与均方误差损失的对比分析

为什么逻辑回归不使用更直观的均方误差(MSE)损失?通过对比可以发现:

损失函数类型数学表达式逻辑回归适用性
均方误差(y - σ(θᵀx))²非凸优化,易陷局部最优
交叉熵-[y logσ + (1-y)log(1-σ)]凸优化,全局最优

MSE在逻辑回归场景中的主要问题在于:

  1. 当预测完全错误时(如y=1但σ≈0),梯度会变得极小,导致学习停滞
  2. 损失曲面存在多个局部最小值,不利于优化

而交叉熵损失具有以下优势特性:

  • 预测错误越大,梯度越大,学习速度越快
  • 损失函数关于θ是凸的,保证梯度下降能找到全局最优解

5. PyTorch实现与自动微分实践

理解了数学原理后,用PyTorch实现变得异常简单。以下完整示例展示了从数据生成到模型训练的全过程:

import torch import torch.nn as nn # 生成模拟数据 torch.manual_seed(42) X = torch.randn(100, 2) # 100个样本,2维特征 y = (X[:, 0] + 2*X[:, 1] > 0).float() # 线性决策边界 # 定义模型 model = nn.Sequential( nn.Linear(2, 1), nn.Sigmoid() ) # 损失函数和优化器 criterion = nn.BCELoss() # 二分类交叉熵 optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 训练循环 for epoch in range(100): # 前向传播 outputs = model(X).squeeze() loss = criterion(outputs, y) # 反向传播和优化 optimizer.zero_grad() loss.backward() optimizer.step() if epoch % 10 == 0: print(f'Epoch {epoch}, Loss: {loss.item():.4f}')

关键实现要点:

  1. nn.Linear(2,1)定义线性变换层
  2. nn.Sigmoid()将输出映射到[0,1]区间
  3. nn.BCELoss()实现二元交叉熵计算
  4. 自动微分系统(backward())自动计算梯度

对于更简洁的实现,可以压缩为3行核心代码:

model = nn.Sequential(nn.Linear(2,1), nn.Sigmoid()) opt = torch.optim.SGD(model.parameters(), lr=0.1) loss = nn.BCELoss()(model(X).squeeze(), y); loss.backward(); opt.step()

6. 工程实践中的注意事项

在实际项目中应用逻辑回归时,有几个关键点需要特别注意:

特征缩放的重要性虽然逻辑回归不像SVM那样严格要求特征归一化,但适当缩放可以:

  • 加速梯度下降的收敛速度
  • 提高数值计算的稳定性
  • 使正则化项对各特征公平作用

推荐使用标准化处理:

from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(X)

正则化策略选择为防止过拟合,常用的正则化方法有:

  • L1正则(Laplace先验):产生稀疏权重,适合特征选择
  • L2正则(Gaussian先验):使权重平滑衰减
  • ElasticNet:结合L1和L2

在PyTorch中实现L2正则化只需在优化器中设置weight_decay参数:

optimizer = torch.optim.SGD(model.parameters(), lr=0.1, weight_decay=1e-4)

类别不平衡处理当正负样本比例悬殊时,可以:

  1. 在损失函数中引入类别权重:
pos_weight = torch.tensor([10.0]) # 正样本权重 criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
  1. 采用过采样/欠采样策略
  2. 使用F1-score等替代准确率作为评估指标