课程承诺:1 个核心概念(逻辑回归)+1 个核心思想(概率预测与决策边界)+1 段完整分类项目代码。学完你能解决所有二分类问题,彻底搞懂分类任务的评估体系,亲手写出第一个实用的 AI 分类器。

本节课目标:从 "预测连续值"(回归)升级到 "预测类别"(分类),用逻辑回归实现一个能自动识别垃圾邮件的 AI,理解为什么分类问题不能用线性回归,以及如何正确评估分类模型的好坏。


🧩 先回答上一课的思考题

  1. 除了正则化,解决过拟合的方法还有

    • 增加训练数据量(最根本的方法)
    • 降低模型复杂度(比如把 9 次多项式改成 3 次)
    • 早停(训练过程中,当测试集误差开始上升时就停止训练)
    • 集成学习(多个弱模型组合成一个强模型,比如随机森林)
  2. L1 正则化(Lasso)和 L2 正则化(Ridge)的区别

    • L2:让所有参数都变小,但不会变成 0
    • L1:会让很多不重要的参数直接变成 0,自带特征选择功能

🧠 第一个核心概念:逻辑回归(Logistic Regression)

先澄清一个最大的误区

逻辑回归不是回归算法,是分类算法!

  • 回归:预测连续值(房价、分数、温度)
  • 分类:预测离散类别(是 / 不是垃圾邮件、是 / 不是癌症、猫 / 狗)

为什么不能用线性回归做分类?

我们用一个最简单的例子说明:判断是不是垃圾邮件

  • 标签:0 = 正常邮件,1 = 垃圾邮件
  • 特征:邮件中包含 "免费" 这个词的次数

如果用线性回归,我们会得到一条直线:y = w*x + b

  • 当 x=0 时,y≈0.1 → 预测正常邮件
  • 当 x=5 时,y≈0.9 → 预测垃圾邮件

看起来没问题,但如果有一个极端的垃圾邮件,包含了 100 个 "免费":

  • 线性回归会预测 y≈10 → 远远大于 1,完全无法解释为概率

核心问题:线性回归的输出是 (-∞, +∞),而我们需要的是 [0,1] 之间的概率


💡 第一个核心思想:Sigmoid 函数与概率预测

神奇的 Sigmoid 函数

我们需要一个 "转换器",把任意实数映射到 (0,1) 区间,这个转换器就是Sigmoid 函数σ(z) = 1 / (1 + e^(-z))

它的图像是一个完美的 S 形曲线:

  • 当 z=0 时,σ(z)=0.5
  • 当 z→+∞时,σ(z)→1
  • 当 z→-∞时,σ(z)→0

逻辑回归的完整公式

  1. 先用线性回归计算出一个分数 z:z = w1*x1 + w2*x2 + ... + b
  2. 把 z 输入 Sigmoid 函数,得到概率 p:p = σ(z)
  3. 这个 p 就是 "样本属于正类的概率"

决策边界

有了概率之后,我们就可以做预测了:

  • 如果 p > 0.5 → 预测为正类(1,垃圾邮件)
  • 如果 p < 0.5 → 预测为负类(0,正常邮件)

这个 0.5 就是我们的决策阈值,它对应的那条线就叫决策边界

重要提示:0.5 不是固定的!我们可以根据业务需求调整阈值,这是分类问题最灵活也最有价值的地方。


💻 代码实战:亲手写一个垃圾邮件分类器

我们将训练一个 AI,根据邮件中是否包含 5 个关键词,自动判断是不是垃圾邮件。

完整代码(复制粘贴就能运行)

python

运行

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

# 1. 准备数据集
# 特征:[包含"免费", 包含"中奖", 包含"点击", 包含"领取", 包含"紧急"]
# 1=包含该词,0=不包含
X = np.array([
    [0,0,0,0,0], [0,0,0,0,1], [0,0,0,1,0], [0,0,1,0,0], [0,1,0,0,0],
    [1,0,0,0,0], [1,1,0,0,0], [1,0,1,0,0], [1,0,0,1,0], [1,1,1,0,0],
    [1,1,0,1,0], [1,1,1,1,0], [1,1,1,1,1], [0,1,1,1,1], [0,0,1,1,1],
    [0,0,0,1,1], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0]
])
# 标签:0=正常邮件,1=垃圾邮件
y = np.array([0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0])

# 2. 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 3. 创建并训练逻辑回归模型
model = LogisticRegression()
model.fit(X_train, y_train)

# 4. 查看每个关键词的权重
print("各关键词对垃圾邮件的影响权重:")
keywords = ["免费", "中奖", "点击", "领取", "紧急"]
for word, coef in zip(keywords, model.coef_[0]):
    print(f"{word}: {coef:.2f}")
print(f"截距:{model.intercept_[0]:.2f}\n")

# 5. 模型预测
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]  # 得到是垃圾邮件的概率

# 6. 模型评估
print("="*50)
print(f"模型准确率:{accuracy_score(y_test, y_pred):.2f}")
print("\n混淆矩阵:")
print(confusion_matrix(y_test, y_pred))
print("\n详细分类报告:")
print(classification_report(y_test, y_pred))

# 7. 测试新邮件
print("="*50)
test_emails = [
    [1,1,0,0,0],  # "免费中奖,快来参与!"
    [0,0,0,0,0],  # "明天下午开会"
    [1,0,0,1,1],  # "免费领取紧急通知"
    [0,1,0,0,0]   # "恭喜你中奖了"
]

for i, email in enumerate(test_emails):
    prob = model.predict_proba([email])[0][1]
    result = "垃圾邮件" if prob > 0.5 else "正常邮件"
    print(f"测试邮件{i+1}:是垃圾邮件的概率={prob:.2f} → {result}")

🔍 逐行解读核心知识点

1. 权重的意义

你会看到类似这样的输出:

plaintext

各关键词对垃圾邮件的影响权重:
免费: 1.23
中奖: 1.15
点击: 0.98
领取: 0.87
紧急: 0.72
截距: -2.34
  • 权重越大,说明这个词越能说明邮件是垃圾邮件
  • "免费" 和 "中奖" 是垃圾邮件最明显的特征
  • 截距为负,说明默认情况下,一封邮件更可能是正常邮件

2. 分类问题最重要的评估指标

准确率(Accuracy):预测正确的样本占总样本的比例

  • 优点:简单直观
  • 缺点:在样本不平衡的情况下完全失效(比如 1000 封邮件里只有 1 封垃圾邮件,全预测为正常邮件准确率也有 99.9%)

混淆矩阵

表格

预测为正常 预测为垃圾
实际正常 真阴性 (TN) 假阳性 (FP)
实际垃圾 假阴性 (FN) 真阳性 (TP)
  • 精确率(Precision):预测为垃圾邮件的邮件中,真正是垃圾邮件的比例
    • 意义:不误删正常邮件
  • 召回率(Recall):所有真正的垃圾邮件中,被正确识别出来的比例
    • 意义:不漏掉垃圾邮件

黄金法则:精确率和召回率是此消彼长的关系,你永远不可能同时提高两者。

  • 垃圾邮件过滤:优先保证精确率(宁愿漏过垃圾邮件,也不要误删正常邮件)
  • 癌症诊断:优先保证召回率(宁愿误诊正常人,也不要漏掉癌症患者)

3. 调整决策阈值

这是本节课最有价值的技巧!我们可以通过修改阈值来平衡精确率和召回率:

python

运行

# 把阈值从0.5改成0.3,提高召回率(不漏掉垃圾邮件)
y_pred_low_threshold = (y_pred_proba > 0.3).astype(int)
print("阈值=0.3时的分类报告:")
print(classification_report(y_test, y_pred_low_threshold))

# 把阈值改成0.8,提高精确率(不误删正常邮件)
y_pred_high_threshold = (y_pred_proba > 0.8).astype(int)
print("阈值=0.8时的分类报告:")
print(classification_report(y_test, y_pred_high_threshold))

运行后你会清楚地看到:

  • 阈值降低 → 召回率上升,精确率下降
  • 阈值升高 → 精确率上升,召回率下降

✨ 神奇的实验:可视化决策边界

我们用两个特征来可视化逻辑回归的决策边界,直观感受它是怎么划分两类数据的。

python

运行

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression

# --- 1. 解决中文显示问题 (关键步骤) ---
# 设置字体为黑体 (SimHei) 或者其他支持中文的字体
# 如果你的系统没有 SimHei,可以尝试 'Microsoft YaHei'
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号 '-' 显示为方块的问题

# --- 2. 构造更丰富的数据 ---
# 之前的错误是因为数据太少。这里我们手动构造一些更典型的点:
# 正常邮件 (0): 不含"免费"也不含"中奖" -> (0,0)
# 垃圾邮件 (1): 含"免费"或者含"中奖" -> (1,0), (0,1), (1,1)

X_data = np.array([
    [0, 0], [0, 0], [0, 0],  # 正常邮件 (0,0)
    [1, 0], [1, 0],          # 含"免费" (1,0)
    [0, 1], [0, 1],          # 含"中奖" (0,1)
    [1, 1]                   # 既含"免费"又含"中奖" (1,1)
])

y_data = np.array([0, 0, 0, 1, 1, 1, 1, 1])

# --- 3. 训练模型 ---
model = LogisticRegression()
model.fit(X_data, y_data)

# --- 4. 绘制决策边界 ---
# 生成网格点
h = 0.02  # 网格步长
x_min, x_max = X_data[:, 0].min() - 0.5, X_data[:, 0].max() + 0.5
y_min, y_max = X_data[:, 1].min() - 0.5, X_data[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

# 预测网格点的类别
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 画图
plt.figure(figsize=(8, 6))

# 绘制背景颜色区域 (决策边界)
# 使用 cmap=plt.cm.RdYlBu 会更清晰:红色代表垃圾邮件,蓝色代表正常邮件
plt.pcolormesh(xx, yy, Z, cmap=plt.cm.RdYlBu, shading='auto', alpha=0.3)

# 绘制散点
# c=y_data 会根据标签自动上色
scatter = plt.scatter(X_data[:, 0], X_data[:, 1], c=y_data, edgecolors='k', cmap=plt.cm.RdYlBu, s=100)

plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())

plt.xlabel('是否包含 "免费" (1=是, 0=否)')
plt.ylabel('是否包含 "中奖" (1=是, 0=否)')
plt.title('逻辑回归:垃圾邮件分类决策边界')
plt.grid(True)

# 添加图例
# 手动创建图例句柄,因为 scatter 的图例有时候需要手动处理
legend_elements = [
    plt.Line2D([0], [0], marker='o', color='w', label='正常邮件 (0)', markerfacecolor='#3B528B', markersize=10),
    plt.Line2D([0], [0], marker='o', color='w', label='垃圾邮件 (1)', markerfacecolor='#B12834', markersize=10)
]
plt.legend(handles=legend_elements, loc='upper right')

plt.show()

你会看到一条直线把平面分成了两部分,直线左边是正常邮件,右边是垃圾邮件。这就是逻辑回归的线性决策边界。


📝 本节课总结

  1. 核心概念:逻辑回归是二分类算法,不是回归算法,输出是样本属于正类的概率
  2. 核心思想:用 Sigmoid 函数把线性回归的输出映射到 (0,1) 区间,通过决策阈值进行分类
  3. 核心评估:分类问题不能只用准确率,要结合精确率和召回率,根据业务需求调整决策阈值
  4. 你已经做到了:写出了第一个实用的 AI 分类器,能根据业务需求优化模型性能

🎯 课后作业(必须做)

  1. 运行上面的所有代码,观察不同阈值下精确率和召回率的变化
  2. 自己添加几个新的关键词和邮件样本,重新训练模型,看看效果
  3. 思考:如果有三个类别(正常邮件、垃圾邮件、广告邮件),逻辑回归怎么处理?
  4. 尝试用逻辑回归解决鸢尾花分类问题(scikit-learn 自带的经典数据集)

更多推荐