37·监督学习-分类入门

逻辑回归原理与实现

classificationlinear-model

第37课 - 逻辑回归原理与实现

所属模块:监督学习-分类 | 难度:初级 | 标签:#classification #linear-model 上一课:学习曲线与模型诊断 | 下一课:K近邻算法(KNN)

学习目标

完成本课学习后,你将能够:

  1. 理解逻辑回归的基本原理,并解释其为何适合二分类问题
  2. 掌握Sigmoid函数的作用,以及如何将线性组合转换为概率
  3. 实现一个完整的逻辑回归模型,从数据准备到预测评估
  4. 应用逻辑回归解决实际分类问题,并理解其优缺点

核心概念

1. 逻辑回归:回归之名,分类之实

虽然名字中带有“回归”,但逻辑回归实际上是一种分类算法。它的核心思想是:通过一个“S”形曲线(Sigmoid函数)将线性回归的输出映射到(0,1)区间,表示样本属于某一类的概率。

想象一下:我们想根据考试成绩预测学生是否通过考试(通过/不通过)。线性回归可能输出“0.8分”、“1.2分”这样的值,而逻辑回归会输出“0.75(75%概率通过)”、“0.3(30%概率通过)”这样的概率值。

2. Sigmoid函数:概率的“压缩器”

Sigmoid函数的公式如下:

σ(z) = 1 / (1 + e^(-z))

其中 z = w1*x1 + w2*x2 + ... + b(线性回归的输出)

这个函数有两个重要特性:

  • 输出范围在(0,1)之间,完美符合概率定义
  • 将任意实数“压缩”到概率空间
import numpy as np
import matplotlib.pyplot as plt

# 可视化Sigmoid函数
z = np.linspace(-10, 10, 100)
sigmoid = 1 / (1 + np.exp(-z))

plt.figure(figsize=(8, 4))
plt.plot(z, sigmoid, 'b-', linewidth=2)
plt.axhline(y=0.5, color='r', linestyle='--', alpha=0.5)
plt.xlabel('z')
plt.ylabel('σ(z)')
plt.title('Sigmoid Function')
plt.grid(True, alpha=0.3)
plt.show()

3. 损失函数与梯度下降

逻辑回归使用对数损失函数(Log Loss)

L(y, ŷ) = -[y·log(ŷ) + (1-y)·log(1-ŷ)]

其中:

  • y 是真实标签(0或1)
  • ŷ 是预测概率

通过梯度下降不断调整权重 w 和偏置 b,使损失函数最小化。权重更新规则:

w = w - α * ∂L/∂w
b = b - α * ∂L/∂b

其中 α 是学习率。

代码示例

示例1:手写逻辑回归实现

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report

class LogisticRegression:
    def __init__(self, learning_rate=0.01, n_iterations=1000):
        self.learning_rate = learning_rate
        self.n_iterations = n_iterations
        self.weights = None
        self.bias = None
        self.losses = []  # 记录训练损失
        
    def sigmoid(self, z):
        """Sigmoid激活函数"""
        return 1 / (1 + np.exp(-z))
    
    def fit(self, X, y):
        """训练模型"""
        n_samples, n_features = X.shape
        
        # 初始化参数
        self.weights = np.zeros(n_features)
        self.bias = 0
        
        # 梯度下降优化
        for i in range(self.n_iterations):
            # 前向传播:计算预测概率
            linear_model = np.dot(X, self.weights) + self.bias
            y_predicted = self.sigmoid(linear_model)
            
            # 计算损失
            loss = -np.mean(y * np.log(y_predicted) + (1 - y) * np.log(1 - y_predicted))
            self.losses.append(loss)
            
            # 计算梯度
            dw = (1 / n_samples) * np.dot(X.T, (y_predicted - y))
            db = (1 / n_samples) * np.sum(y_predicted - y)
            
            # 更新参数
            self.weights -= self.learning_rate * dw
            self.bias -= self.learning_rate * db
            
            # 每100次迭代打印损失
            if i % 100 == 0:
                print(f"Iteration {i}, Loss: {loss:.4f}")
    
    def predict_proba(self, X):
        """预测概率"""
        linear_model = np.dot(X, self.weights) + self.bias
        return self.sigmoid(linear_model)
    
    def predict(self, X, threshold=0.5):
        """预测类别"""
        probabilities = self.predict_proba(X)
        return (probabilities >= threshold).astype(int)

# 生成示例数据
X, y = make_classification(
    n_samples=1000,
    n_features=20,
    n_informative=10,
    n_classes=2,
    random_state=42
)

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 特征标准化(对逻辑回归很重要!)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 训练手写逻辑回归
model = LogisticRegression(learning_rate=0.1, n_iterations=1000)
model.fit(X_train_scaled, y_train)

# 评估模型
y_pred = model.predict(X_test_scaled)
print(f"\n准确率: {accuracy_score(y_test, y_pred):.4f}")
print("\n分类报告:")
print(classification_report(y_test, y_pred))

# 可视化损失曲线
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 4))
plt.plot(model.losses)
plt.xlabel('Iteration')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.grid(True, alpha=0.3)
plt.show()

示例2:使用sklearn实现逻辑回归

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, roc_curve, auc

# 使用sklearn的逻辑回归
sklearn_model = LogisticRegression(
    C=1.0,           # 正则化强度(C越大,正则化越弱)
    solver='lbfgs',  # 优化算法
    max_iter=1000
)

# 训练模型
sklearn_model.fit(X_train_scaled, y_train)

# 预测
y_pred_sklearn = sklearn_model.predict(X_test_scaled)
y_pred_proba = sklearn_model.predict_proba(X_test_scaled)[:, 1]

# 评估
print("sklearn逻辑回归结果:")
print(f"准确率: {accuracy_score(y_test, y_pred_sklearn):.4f}")
print(f"\n模型系数形状: {sklearn_model.coef_.shape}")
print(f"截距: {sklearn_model.intercept_[0]:.4f}")

# 绘制ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
roc_auc = auc(fpr, tpr)

plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='darkorange', lw=2, 
         label=f'ROC曲线 (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlabel('假阳性率')
plt.ylabel('真阳性率')
plt.title('ROC曲线')
plt.legend(loc="lower right")
plt.grid(True, alpha=0.3)
plt.show()

# 特征重要性(系数的绝对值)
feature_importance = np.abs(sklearn_model.coef_[0])
sorted_idx = np.argsort(feature_importance)[::-1]

plt.figure(figsize=(10, 6))
plt.bar(range(10), feature_importance[sorted_idx[:10]])
plt.xlabel('特征')
plt.ylabel('系数绝对值')
plt.title('逻辑回归特征重要性(前10)')
plt.xticks(range(10), [f'特征{i}' for i in sorted_idx[:10]])
plt.show()

实践练习

练习1:基础实现(易)

要求

  1. 使用手写的LogisticRegression类,在鸢尾花数据集上实现二分类(只取类别0和1)
  2. 计算并打印准确率、精确率、召回率
  3. 绘制损失曲线

预期输出

准确率: 0.95
精确率: 0.93
召回率: 0.97
损失曲线(平滑下降)

练习2:参数调优(中)

要求

  1. 使用sklearn的LogisticRegression,尝试不同的正则化参数C(0.01, 0.1, 1, 10, 100)
  2. 比较不同C值下的准确率
  3. 绘制C值与准确率的关系图
  4. 选择最优的C值并解释原因

预期输出

C=0.01: 准确率=0.82
C=0.1:  准确率=0.89
C=1:    准确率=0.93
C=10:   准确率=0.92
C=100:  准确率=0.91
最优C值: 1

练习3:实际应用(难)

要求

  1. 加载乳腺癌数据集(from sklearn.datasets import load_breast_cancer
  2. 进行完整的机器学习流水线:数据预处理、特征选择、模型训练、评估
  3. 使用交叉验证评估模型稳定性
  4. 尝试添加L1正则化(penalty='l1')进行特征选择

预期输出

交叉验证准确率: 0.95 ± 0.02
特征数量: 从30个减少到15个
测试集准确率: 0.96

常见错误

1. 混淆逻辑回归与线性回归

错误:将逻辑回归当作回归问题来处理 正确理解:逻辑回归是分类算法,输出是概率值,不是连续数值

2. 忽略特征标准化

错误:直接对原始特征训练逻辑回归 后果:梯度下降收敛慢或不收敛 解决方案:始终使用StandardScaler进行标准化

# 错误的做法
model.fit(X_raw, y)  # 特征尺度差异大

# 正确的做法
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
model.fit(X_scaled, y)

3. 不合理的阈值选择

错误:总是使用默认阈值0.5 场景:在类别不平衡时,0.5可能不是最优阈值 解决方案:根据业务需求调整阈值,或使用ROC曲线选择最优阈值

# 根据业务需求调整阈值
y_pred_custom = (y_pred_proba >= 0.3).astype(int)  # 降低阈值,提高召回率

4. 过度依赖默认参数

错误:直接使用sklearn的默认参数,不进行调优 后果:可能错过更好的性能 解决方案:使用网格搜索或随机搜索进行超参数调优

5. 忽视多重共线性问题

错误:特征之间高度相关时直接训练 后果:模型系数不稳定,解释性差 解决方案:检查相关性矩阵,考虑使用正则化或特征选择

小结

关键要点回顾:

  1. 逻辑回归本质:虽然名为“回归”,但用于二分类问题
  2. 核心机制:Sigmoid函数将线性组合映射到(0,1)概率空间
  3. 训练过程:使用梯度下降最小化对数损失函数
  4. 实际应用:特别适合需要概率输出的场景(如风险评估、医学诊断)
  5. 优势:简单高效、可解释性强、训练速度快
  6. 局限性:假设线性决策边界,可能欠拟合复杂数据

下一步学习:

  1. 了解多分类逻辑回归(One-vs-Rest、Softmax回归)
  2. 学习正则化(L1、L2)如何防止过拟合
  3. 探索其他线性分类模型(感知机、支持向量机)
  4. 理解非线性扩展:通过特征工程或核方法处理非线性问题

实用建议:

  • 从简单模型开始,逻辑回归是很好的基线模型
  • 始终进行特征工程和标准化
  • 关注模型的可解释性,逻辑回归的系数有明确的业务含义
  • 对于复杂问题,考虑使用集成方法(如随机森林)或深度学习

下一课预告:我们将学习K近邻算法(KNN),另一种简单但强大的分类算法,它基于“近朱者赤”的思想进行预测!

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「K 近邻算法(KNN)」 以巩固所学知识。