1. 概述

逻辑回归(Logistic Regression)虽然名字中带有”回归”,但实际上是一种分类算法,主要用于二分类问题。它的核心思想是通过逻辑函数(sigmoid)将线性回归的输出映射到 区间,表示样本属于正类的概率。

1.1 与线性回归的区别

特征线性回归逻辑回归
任务类型回归分类
输出范围(概率)
损失函数均方误差(MSE)交叉熵(Cross-Entropy)
应用场景预测连续值预测类别概率

1.2 为什么需要逻辑函数?

线性回归的输出是实数范围,但分类问题需要 的概率值。sigmoid函数恰好完成了这个映射:

其函数图像呈 S 形曲线:

        1.0 ┤                              ╭──
            │                           ╭──╯
        0.8 ┤                        ╭──╯
            │                     ╭──╯
        0.6 ┤                  ╭──╯
            │               ╭──╯
        0.4 ┤            ╭──╯
            │         ╭──╯
        0.2 ┤      ╭──╯
            │   ╭──╯
          0 ┼──╯────────────────────
            -4   -2    0    2    4

2. 作为广义线性模型的推导

2.1 广义线性模型(GLM)框架

逻辑回归是广义线性模型(Generalized Linear Model, GLM)的一个典型实例。GLM由三个组件构成:

  1. 概率分布:假设 服从指数族分布
  2. 线性预测器
  3. 链接函数,将均值 与线性预测器连接

2.2 从伯努利分布推导

设样本标签 ,则 服从伯努利分布

将伯努利分布写成指数族形式:

对照指数族标准形式

  • 自然参数:
  • 对数归一化函数:

由此可得链接函数(logit link):

这就是 sigmoid 函数,也称为 logistic 函数

2.3 模型假设

逻辑回归基于以下假设:

  1. 线性可分性假设
  2. 条件独立假设(在某些推导中)
  3. 特征独立假设(与朴素贝叶斯不同)

3. 极大似然估计

3.1 似然函数

给定训练数据集 ,假设样本独立同分布,则似然函数为:

其中:

3.2 对数似然

取对数得到对数似然

3.3 梯度推导

求偏导:

其中 ,利用了 sigmoid 的性质


4. 损失函数

4.1 二元交叉熵损失

逻辑回归的损失函数为二元交叉熵(Binary Cross-Entropy):

其中 是预测概率。

4.2 与均方误差的关系

如果使用均方误差(MSE)作为损失:

为什么不使用MSE?

  1. 梯度消失:当 接近 0 或 1 时,MSE 的梯度趋近于 0,学习缓慢
  2. 非凸优化:MSE 损失曲面可能存在多个局部极小值
  3. 概率解释:交叉熵来源于最大似然估计,具有概率解释
        Cross-Entropy              MSE
      L(y,ŷ)                   L(y,ŷ)
        │                          │
    0.7 ┤ ┼                        │ ┼
        │ ╲╱╲                      │ ╲
    0.4 ┤  ╲╱  ╲                   │  ╲
        │   ╲╱   ╲                 │   ────────
    0.1 ┤    ╲    ╲                │    ╲
        └──────────────             └──────────────
          0.0  0.5  1.0              0.0  0.5  1.0
           ŷ                         ŷ

4.3 梯度下降更新

使用梯度下降法更新参数:

或使用批量/小批量梯度下降。


5. 正则化

5.1 L2正则化(权重衰减)

在损失函数中加入 L2 惩罚项:

物理意义:L2 正则化使权重向量趋于零,但不会完全为零。这使得每个特征都对预测有贡献,但贡献较小。

5.2 L1正则化(稀疏解)

物理意义:L1 正则化产生稀疏解,使部分权重正好为零,从而实现特征选择。

5.3 Elastic Net(弹性网)

结合 L1 和 L2:

5.4 正则化参数选择

效果
无正则化,可能过拟合
过大权重趋近于零,欠拟合
适中 平衡偏差-方差权衡

6. 优化算法

6.1 梯度下降法

最简单的优化方法:

def gradient_descent(X, y, lr=0.01, n_iters=1000, lambda_reg=0.01):
    n_samples, n_features = X.shape
    w = np.zeros(n_features)
    b = 0
    
    for _ in range(n_iters):
        z = X @ w + b
        y_hat = sigmoid(z)
        
        # 梯度计算(含L2正则化)
        dw = (1/n_samples) * (X.T @ (y_hat - y)) + lambda_reg * w
        db = (1/n_samples) * np.sum(y_hat - y)
        
        w -= lr * dw
        b -= lr * db
    
    return w, b

6.2 牛顿法(IRLS)

对于逻辑回归,可以使用二阶优化方法加速收敛。

参数更新公式:

其中 是 Hessian 矩阵:

这就是 IRLS(Iteratively Reweighted Least Squares) 算法。

6.3 拟牛顿法(BFGS/L-BFGS)

不需要显式计算 Hessian,适用于大规模问题。

6.4 不同优化算法对比

算法收敛速度计算复杂度内存需求适用场景
梯度下降大数据
牛顿法快(二次收敛)小数据
L-BFGS中等数据

7. 多分类扩展

7.1 One-vs-Rest (OvR)

训练 个二分类器,第 个分类器将第 类作为正类,其余作为负类。

预测时:选择所有分类器中输出概率最大的类别。

        类别1 vs 其他     类别2 vs 其他     类别3 vs 其他
           ┌─┐               ┌─┐               ┌─┐
    输入 ──┤ │── P(1|x) ──┤ │── P(2|x) ──┤ │── P(3|x)
           └─┘               └─┘               └─┘
             │                 │                 │
             └────────┬────────┴────────┬─────────┘
                      ▼
               argmax(P(k|x))

7.2 与Softmax回归的关系

OvR 可以视为 softmax 回归的特殊情况。详见 Softmax回归


8. 与神经网络的关系

8.1 单层神经网络视角

逻辑回归可以看作没有隐藏层的神经网络(即感知器的变体):

           输入层        输出层
              │
    x₁ ──┬──┐ │
          ├──┤w₁├─┐
    x₂ ──┤  │w₂├─┤    ┌─────────┐
          ├──┤w₃├─┼──┤ Sigmoid ├─── P(y=1|x)
    x₃ ──┤  │  │  │  └─────────┘
          └──┴──┘──┘
              │
           (偏置 b)

8.2 多层感知器的基础

在多层感知器(MLP)中:

  • 逻辑回归常用作输出层,配合交叉熵损失
  • Softmax 回归是逻辑回归在多分类上的推广
  • 详见 多层感知机理论

9. 模型评估

9.1 分类指标

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
 
# 训练模型
model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
 
# 计算指标
print(f"Accuracy:  {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred):.4f}")
print(f"Recall:    {recall_score(y_test, y_pred):.4f}")
print(f"F1 Score:  {f1_score(y_test, y_pred):.4f}")

9.2 ROC曲线与AUC

import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc, roc_auc_score
 
# 获取预测概率
y_prob = model.predict_proba(X_test)[:, 1]
 
# 计算ROC曲线
fpr, tpr, thresholds = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
 
# 绘制ROC曲线
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', label='Random')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC)')
plt.legend(loc='lower right')
plt.grid(True, alpha=0.3)
plt.show()

9.3 概率校准

逻辑回归输出的概率需要进行校准,以确保预测概率反映真实概率。

from sklearn.calibration import CalibratedClassifierCV, calibration_curve
 
# 使用Platt缩放或等渗回归进行校准
calibrated_model = CalibratedClassifierCV(model, method='isotonic')
calibrated_model.fit(X_train, y_train)
 
# 绘制校准曲线
y_prob_calibrated = calibrated_model.predict_proba(X_test)[:, 1]
fraction_of_positives, mean_predicted_value = calibration_curve(y_test, y_prob_calibrated, n_bins=10)
 
plt.figure(figsize=(8, 6))
plt.plot([0, 1], [0, 1], 'k--', label='Perfectly calibrated')
plt.plot(mean_predicted_value, fraction_of_positives, 's-', label='Calibrated model')
plt.xlabel('Mean predicted probability')
plt.ylabel('Fraction of positives')
plt.legend()
plt.show()

10. 完整代码实现

10.1 NumPy实现

import numpy as np
from typing import Tuple
 
class LogisticRegression:
    """逻辑回归分类器(支持L2正则化)"""
    
    def __init__(self, lr: float = 0.01, n_iters: int = 1000, 
                 lambda_reg: float = 0.01, tol: float = 1e-5):
        self.lr = lr
        self.n_iters = n_iters
        self.lambda_reg = lambda_reg
        self.tol = tol
        self.w = None
        self.b = None
    
    def sigmoid(self, z: np.ndarray) -> np.ndarray:
        """Sigmoid激活函数,数值稳定版本"""
        return np.where(z >= 0, 
                        1 / (1 + np.exp(-z)),
                        np.exp(z) / (1 + np.exp(z)))
    
    def fit(self, X: np.ndarray, y: np.ndarray) -> 'LogisticRegression':
        """训练模型"""
        n_samples, n_features = X.shape
        
        # 参数初始化
        self.w = np.zeros(n_features)
        self.b = 0
        
        # 梯度下降
        for i in range(self.n_iters):
            z = X @ self.w + self.b
            y_hat = self.sigmoid(z)
            
            # 计算梯度(含L2正则化)
            dw = (1/n_samples) * (X.T @ (y_hat - y)) + self.lambda_reg * self.w
            db = (1/n_samples) * np.sum(y_hat - y)
            
            # 参数更新
            self.w -= self.lr * dw
            self.b -= self.lr * db
            
            # 收敛判断
            if np.linalg.norm(dw) < self.tol:
                print(f"Converged at iteration {i+1}")
                break
        
        return self
    
    def predict_proba(self, X: np.ndarray) -> np.ndarray:
        """预测概率"""
        z = X @ self.w + self.b
        y_hat = self.sigmoid(z)
        return np.column_stack([1 - y_hat, y_hat])
    
    def predict(self, X: np.ndarray, threshold: float = 0.5) -> np.ndarray:
        """预测类别"""
        return (self.predict_proba(X)[:, 1] >= threshold).astype(int)
 
 
# 示例使用
if __name__ == "__main__":
    from sklearn.datasets import make_classification
    from sklearn.model_selection import train_test_split
    
    # 生成数据
    X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    
    # 训练模型
    model = LogisticRegression(lr=0.1, n_iters=500, lambda_reg=0.01)
    model.fit(X_train, y_train)
    
    # 预测
    y_pred = model.predict(X_test)
    y_prob = model.predict_proba(X_test)[:, 1]
    
    print(f"Test Accuracy: {(y_pred == y_test).mean():.4f}")

10.2 使用sklearn

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
 
# 加载数据
data = load_breast_cancer()
X, y = data.data, data.target
 
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
 
# 创建管道(标准化 + 逻辑回归)
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('clf', LogisticRegression(penalty='l2', C=1.0, solver='lbfgs', max_iter=1000))
])
 
# 训练
pipeline.fit(X_train, y_train)
 
# 评估
train_acc = pipeline.score(X_train, y_train)
test_acc = pipeline.score(X_test, y_test)
print(f"Train Accuracy: {train_acc:.4f}")
print(f"Test Accuracy:  {test_acc:.4f}")
 
# 预测
y_pred = pipeline.predict(X_test)
y_prob = pipeline.predict_proba(X_test)[:, 1]

11. 优缺点分析

优点

优点说明
概率输出可以输出样本属于各类别的概率
可解释性强权重直接反映特征对分类的贡献
计算效率高训练和预测的时间复杂度低
理论基础扎实基于最大似然估计,有概率解释
不易过拟合通过正则化可以有效控制模型复杂度
与神经网络兼容可以视为单层神经网络

缺点

缺点说明
线性决策边界无法捕捉特征间的非线性关系
特征工程依赖需要手工进行特征工程
多分类效率低OvR策略可能导致分类不平衡
对特征缩放敏感需要特征标准化
特征共线性问题高度相关特征会导致权重不稳定

12. 应用场景

12.1 经典应用

  1. 信用评分:预测用户是否会违约
  2. 广告点击率预测:预测用户是否会点击广告
  3. 医学诊断:预测病人是否患有某种疾病
  4. 邮件分类:判断邮件是否为垃圾邮件

12.2 在深度学习中的作用

  1. 作为激活函数:Sigmoid 是神经网络中的重要激活函数
  2. 作为损失函数:二元交叉熵是二分类问题的标准损失
  3. 作为输出层:用于需要概率输出的二分类任务

13. 与其他模型的关系

13.1 与SVM的关系

特征逻辑回归SVM
损失函数交叉熵合页损失(Hinge Loss)
输出概率类别标签
决策边界最大化似然最大化间隔
正则化L1/L2间隔最大化(隐式正则化)

详见 SVM

13.2 与朴素贝叶斯的关系

两者都是概率分类器,但假设不同:

  • 逻辑回归:条件概率建模,直接估计
  • 朴素贝叶斯:先验 + 似然,估计

详见 朴素贝叶斯

13.3 与线性回归的关系

逻辑回归在线性回归的基础上:

  1. 添加了 sigmoid 变换
  2. 使用交叉熵损失替代均方误差
  3. 输出从连续值变为概率

参考资料