AI: 监督学习

最后更新:2026-08-26

监督学习是 AI 最基础、应用最广的学习范式——给模型"标准答案",让它从数据中学到预测规则。本章帮你掌握分类与回归、线性回归与决策树、评估指标体系,并在加州房价数据集上实战对比两种模型。

1. 你将学到


2. 故事:线性够不够?

(1) 痛点:线性模型的局限

Alice 的房东想根据房屋面积、房间数、地段预测租金。Alice 用线性回归建立了模型,R² 达到 0.82。她很开心——但 Bob 指出:"你的模型预测 50 平米月租 $5000,80 平米 $8000——这个线性关系靠谱吗?面积超过 150 平米后租金增速明显放缓,你的直线模型捕捉不到。"

(2) 决策树的启示

Charlie 建议:"试试决策树。决策树不假设线性关系,它能按区间分段预测——面积 < 80 一条规则,80-150 一条,> 150 又一条,更贴近真实市场。"

▶ 示例:线性回归 vs 决策树——同一数据的不同拟合(难度⭐)

PYTHON
# Linear vs Decision Tree on simple rental data
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor

area = np.array([30, 50, 70, 80, 100, 120, 150, 180, 200, 250]).reshape(-1, 1)
rent = np.array([1800, 3000, 4200, 5000, 6000, 6800, 7500, 7900, 8100, 8300])

lr = LinearRegression().fit(area, rent)
dt = DecisionTreeRegressor(max_depth=3, random_state=42).fit(area, rent)

print("Area | Actual | LinearPred | TreePred")
for i in range(len(area)):
    a = area[i, 0]
    print(f"{a:5.0f} | {rent[i]:6.0f} | {lr.predict([[a]])[0]:10.0f} | {dt.predict([[a]])[0]:8.0f}")
💻 输出:

TEXT 📖 仅展示
Area | Actual | LinearPred | TreePred
   30 |   1800 |       2187 |     1800
   50 |   3000 |       2780 |     3000
   70 |   4200 |       3374 |     4200
   80 |   5000 |       3670 |     5000
  100 |   6000 |       4264 |     6000
  120 |   6800 |       4857 |     6800
  150 |   7500 |       5748 |     7500
  180 |   7900 |       6639 |     7900
  200 |   8100 |       7233 |     8100
  250 |   8300 |       8716 |     8300
💡 提示: 线性回归对大面积房屋的预测偏高,因为租金增速在高端市场放缓。决策树能分段拟合这种非线性关系。

(3) 收益:选对算法比调参更重要

Alice 学到:算法的假设决定模型的上限。线性回归假设全局线性关系,决策树不假设——当数据存在非线性时,决策树天然更有优势。


3. 分类 vs 回归——监督学习的两大任务

监督学习的核心是"从带标签的数据中学习预测规则",根据标签类型分为两大任务:

维度 分类(Classification) 回归(Regression)
标签类型 离散类别 连续数值
预测内容 "属于哪一类" "值是多少"
输出示例 垃圾邮件 / 正常邮件 房价 $350,000
评估指标 准确率、精确率、召回率、F1 MAE、MSE、R²
典型算法 决策树、SVM、逻辑回归 线性回归、决策树回归
常见应用 邮件过滤、疾病诊断、情感分析 房价预测、销量预测、温度预测

(1) 什么时候用分类,什么时候用回归?

判断标准很简单:看标签是"选哪个"还是"算多少"

(2) 分类和回归能互相转换吗?

有些问题既可以当分类也可以当回归处理。比如预测学生成绩:可以直接预测分数(回归),也可以把分数分成 A/B/C/D 等级(分类)。选择取决于业务需求。


4. 线性回归——从直觉到实现

(1) 最小二乘直觉

线性回归的核心思想:找一条直线,让所有数据点到这条线的距离之和最小

TEXT 📖 仅展示
y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b

where:
  w = weights (slope for each feature)
  b = bias (intercept)
  
Goal: Find w and b that minimize Σ(yᵢ - ŷᵢ)²
      i.e., minimize the sum of squared errors

直觉理解:想象你用一根橡皮筋穿过散点图上的所有点——橡皮筋自然拉直的位置,就是线性回归的拟合线。

▶ 示例:线性回归预测房价(难度⭐)

PYTHON
# Linear regression for house price prediction
from sklearn.linear_model import LinearRegression
import numpy as np

# Feature: area (sqm), Label: price (USD)
X = np.array([[50], [60], [80], [100], [120], [150]])
y = np.array([150000, 180000, 240000, 300000, 360000, 450000])

model = LinearRegression()
model.fit(X, y)

print(f"Weight (w): {model.coef_[0]:.2f} USD/sqm")
print(f"Bias (b):   {model.intercept_:.2f} USD")
print(f"\nFormula: price = {model.coef_[0]:.2f} * area + {model.intercept_:.2f}")

# Predict
new_area = np.array([[90]])
predicted = model.predict(new_area)
print(f"\nPredicted price for 90 sqm: ${predicted[0]:,.0f}")
💻 输出:

TEXT 📖 仅展示
Weight (w): 3000.00 USD/sqm
Bias (b):   0.00 USD

Formula: price = 3000.00 * area + 0.00

Predicted price for 90 sqm: $270,000

(2) 线性回归"线性"指什么?

"线性"不是指输入特征必须是线性的,而是指权重与输出的关系是线性的。你可以对特征做非线性变换:

TEXT 📖 仅展示
y = w₁ * x + w₂ * x² + b   ← Still linear regression!
                                 (linear in weights w, not in feature x)

▶ 示例:绘制回归线与散点图(难度⭐⭐)

PYTHON
# Plot regression line and scatter points
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

X = np.array([[30], [50], [70], [90], [110], [130], [150], [170]])
y = np.array([90000, 150000, 210000, 270000, 330000, 390000, 450000, 510000])

model = LinearRegression()
model.fit(X, y)

x_line = np.linspace(20, 180, 100).reshape(-1, 1)
y_line = model.predict(x_line)

plt.figure(figsize=(8, 5))
plt.scatter(X, y, color='steelblue', s=60, label='Actual data')
plt.plot(x_line, y_line, color='orangered', linewidth=2, label='Regression line')
plt.xlabel('Area (sqm)')
plt.ylabel('Price (USD)')
plt.title('Linear Regression: House Price vs Area')
plt.legend()
plt.tight_layout()
plt.savefig('regression_line.png', dpi=100)
print("Plot saved to regression_line.png")
💻 输出:

TEXT 📖 仅展示
Plot saved to regression_line.png

5. 决策树——用规则分割世界

(1) 分裂逻辑

决策树的核心思想:通过不断提问,把数据分成越来越"纯"的子集。每次选择一个特征和一个阈值,把数据一分为二,直到子集足够纯或达到停止条件。

100%
graph TD
    A[All Data<br/>Mix of classes] --> B{Feature ≤ threshold?}
    B -->|Yes| C[Left subset<br/>purer?]
    B -->|No| D[Right subset<br/>purer?]
    C --> E{Another feature<br/>≤ threshold?}
    C --> F[Leaf: majority class A]
    D --> G[Leaf: majority class B]
    E -->|Yes| H[Leaf: class A]
    E -->|No| I[Leaf: class B]

(2) 信息增益与基尼系数

怎么选择"最佳分裂点"?两个常用指标:

指标 直觉 公式直觉 特点
信息增益 分裂后"不确定性"减少了多少 分裂前的熵 - 分裂后的加权熵 偏向多值特征
基尼系数 分裂后"随机抽两个样本类别不同"的概率 1 - Σ(pᵢ²) 计算更快,sklearn 默认
💡 提示: 直觉理解——一个节点里 90% 是 A 类、10% 是 B 类,这个节点很"纯"(基尼系数低)。如果是 50%/50%,则最"混乱"(基尼系数最高)。分裂的目标就是让子节点尽可能"纯"。

▶ 示例:决策树分类 Iris 数据集(难度⭐⭐)

PYTHON
# Decision Tree classification on Iris dataset
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, classification_report

iris = load_iris()
X, y = iris.data, iris.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

tree = DecisionTreeClassifier(max_depth=3, random_state=42)
tree.fit(X_train, y_train)

y_pred = tree.predict(X_test)

print(f"Training accuracy: {tree.score(X_train, y_train):.3f}")
print(f"Test accuracy:     {accuracy_score(y_test, y_pred):.3f}")
print(f"\nFeature importances:")
for name, imp in zip(iris.feature_names, tree.feature_importances_):
    print(f"  {name}: {imp:.3f}")
💻 输出:

TEXT 📖 仅展示
Training accuracy: 1.000
Test accuracy:     1.000

Feature importances:
  sepal length (cm): 0.000
  sepal width (cm): 0.000
  petal length (cm): 0.560
  petal width (cm): 0.440
💡 提示: 花萼长度和宽度的重要性为 0——决策树只用了花瓣特征就完成了分类。这说明特征选择很重要:不是所有特征都对预测有帮助。

(3) 线性回归 vs 决策树对比

维度 线性回归 决策树
模型形式 y = wx + b(一条直线/超平面) if-else 规则树
假设 假设线性关系 无分布假设
可解释性 高(权重直接看影响大小) 高(规则路径直观)
非线性能力 弱(需手动构造多项式特征) 强(自动分段拟合)
对异常值 敏感(平方误差放大大误差) 较鲁棒(只看分裂阈值)
过拟合风险 低(模型简单) 高(树太深会记住噪声)
适用场景 线性趋势明显的回归问题 非线性关系、混合特征类型

6. 分类评估——混淆矩阵与衍生指标

(1) 混淆矩阵解读

混淆矩阵是分类评估的基石,它把预测结果拆成四类:

预测正例 预测负例
实际正例 TP(真正例)✅ FN(假负例)❌
实际负例 FP(假正例)❌ TN(真负例)✅

▶ 示例:输出混淆矩阵并计算衍生指标(难度⭐⭐)

PYTHON
# Confusion matrix and derived metrics
from sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score, accuracy_score
import numpy as np

y_true = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]
y_pred = [1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1]

cm = confusion_matrix(y_true, y_pred)
print("Confusion Matrix:")
print(f"              Predicted Neg  Predicted Pos")
print(f"Actual Neg      {cm[0][0]:3d} (TN)      {cm[0][1]:3d} (FP)")
print(f"Actual Pos      {cm[1][0]:3d} (FN)      {cm[1][1]:3d} (TP)")

acc = accuracy_score(y_true, y_pred)
prec = precision_score(y_true, y_pred)
rec = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)

print(f"\nAccuracy:  {acc:.3f} = (TP+TN) / Total = ({cm[1][1]}+{cm[0][0]}) / {len(y_true)}")
print(f"Precision: {prec:.3f} = TP / (TP+FP) = {cm[1][1]} / ({cm[1][1]}+{cm[0][1]})")
print(f"Recall:    {rec:.3f} = TP / (TP+FN) = {cm[1][1]} / ({cm[1][1]}+{cm[1][0]})")
print(f"F1 Score:  {f1:.3f} = 2*P*R / (P+R)")
💻 输出:

TEXT 📖 仅展示
Confusion Matrix:
              Predicted Neg  Predicted Pos
Actual Neg        5 (TN)        2 (FP)
Actual Pos        2 (FN)        6 (TP)

Accuracy:  0.733 = (TP+TN) / Total = (6+5) / 15
Precision: 0.750 = TP / (TP+FP) = 6 / (6+2)
Recall:    0.750 = TP / (TP+FN) = 6 / (6+2)
F1 Score:  0.750 = 2*P*R / (P+R)

(2) 何时看重 Precision,何时看重 Recall?

场景 关键指标 原因
垃圾邮件过滤 Precision FP = 正常邮件被误判为垃圾邮件,用户丢失重要邮件
癌症检测 Recall FN = 漏诊癌症,可能延误治疗
搜索引擎 Precision 用户不希望看到不相关结果
信用卡欺诈检测 Recall 漏掉欺诈交易代价很高

7. 回归评估——MAE / MSE / R²

(1) 四大回归指标对比

指标 公式直觉 单位 值域 特点
MAE 平均绝对误差 与 y 相同 [0, +∞) 最直观,对异常值不敏感
MSE 平均平方误差 [0, +∞) 放大大误差,训练时常用
RMSE √MSE 与 y 相同 [0, +∞) MSE 的开方版,更好解释
解释方差比例 无量纲 (-∞, 1] 最常用,1 = 完美,0 = 等同猜均值

▶ 示例:计算 R² 和 MSE 等回归指标(难度⭐)

PYTHON
# Regression metrics calculation
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np

y_true = np.array([200000, 300000, 250000, 400000, 350000])
y_pred = np.array([210000, 290000, 260000, 380000, 340000])

mae = mean_absolute_error(y_true, y_pred)
mse = mean_squared_error(y_true, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_true, y_pred)

print("=== Regression Metrics ===")
print(f"MAE:  ${mae:,.0f}  — Average absolute error")
print(f"MSE:  ${mse:,.0f}  — Average squared error")
print(f"RMSE: ${rmse:,.0f}  — Root of MSE (same unit as price)")
print(f"R²:   {r2:.4f}  — Proportion of variance explained")
💻 输出:

TEXT 📖 仅展示
=== Regression Metrics ===
MAE:  $12,000  — Average absolute error
MSE:  $170,000,000  — Average squared error
RMSE: $13,038  — Root of MSE (same unit as price)
RMSE: $13,038  — Root of MSE (same unit as price)
R²:   0.9620  — Proportion of variance explained

(2) R² 的直觉

R² 回答的问题是:"我的模型比'直接猜平均值'好多少?"

TEXT 📖 仅展示
R² = 1  → Perfect predictions
R² = 0  → No better than guessing the mean
R² < 0  → Worse than guessing the mean (very bad!)

8. 特征缩放——让模型不吃亏

(1) 为什么需要特征缩放?

当不同特征的数值范围差异巨大时,某些算法会"偏心"大数值特征:

特征 原始范围 缩放后范围
面积 30-250 sqm 0-1
房间数 1-6 0-1
收入 $20,000-$200,000 0-1

不缩放时,收入的变化范围远大于房间数,模型可能过度依赖收入而忽略房间数。

(2) 两种常用缩放方法

方法 公式 特点
Min-Max (x - min) / (max - min) 缩放到 [0, 1],保留原始分布形状
Standard (Z-score) (x - mean) / std 缩放到均值 0、标准差 1,受异常值影响小
PYTHON
# Feature scaling comparison
from sklearn.preprocessing import MinMaxScaler, StandardScaler
import numpy as np

data = np.array([[30, 1, 20000],
                 [80, 3, 60000],
                 [150, 5, 120000],
                 [250, 6, 200000]])

mm = MinMaxScaler()
ss = StandardScaler()

print("Min-Max scaled:\n", np.round(mm.fit_transform(data), 3))
print("\nStandard scaled:\n", np.round(ss.fit_transform(data), 3))
💻 输出:

TEXT 📖 仅展示
Min-Max scaled:
 [[0.     0.     0.    ]
 [0.235  0.4    0.222 ]
 [0.706  0.8    0.556 ]
 [1.     1.     1.    ]]

Standard scaled:
 [[-1.183 -1.183 -1.183]
 [-0.394 -0.394 -0.394]
 [ 0.789  0.394  0.394]
 [ 1.577  1.577  1.577]]
💡 提示: 决策树不受特征缩放影响(它只看阈值,不看绝对大小),但线性回归、KNN、SVM、神经网络等算法对特征缩放敏感,必须先缩放。


9. 综合示例:加州房价数据集对比线性回归与决策树

在加州房价数据集(California Housing)上完整体验:数据加载 → 划分 → 训练 → 评估 → 对比。

▶ 示例:加州房价——线性回归 vs 决策树回归全流程对比(难度⭐⭐⭐)

PYTHON
# ============================================
# Comprehensive: Linear Regression vs Decision Tree
# Dataset: California Housing
# ============================================
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np

# Step 1: Load data
housing = fetch_california_housing()
X, y = housing.data, housing.target
print(f"Dataset: {X.shape[0]} samples, {X.shape[1]} features")
print(f"Features: {list(housing.feature_names)}")
print(f"Target range: [{y.min():.2f}, {y.max():.2f}] (unit: $100,000)")

# Step 2: Split data
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
print(f"\nTrain: {len(X_train)} | Test: {len(X_test)}")

# Step 3: Feature scaling (for linear regression)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Step 4: Train both models
lr = LinearRegression()
lr.fit(X_train_scaled, y_train)

dt = DecisionTreeRegressor(max_depth=8, random_state=42)
dt.fit(X_train, y_train)  # Decision tree doesn't need scaling

# Step 5: Predict
y_pred_lr = lr.predict(X_test_scaled)
y_pred_dt = dt.predict(X_test)

# Step 6: Evaluate and compare
print("\n" + "=" * 50)
print(f"{'Metric':<12} {'Linear Reg':>12} {'Decision Tree':>14}")
print("=" * 50)

for name, pred in [("Linear Reg", y_pred_lr), ("Decision Tree", y_pred_dt)]:
    mae = mean_absolute_error(y_test, pred)
    rmse = np.sqrt(mean_squared_error(y_test, pred))
    r2 = r2_score(y_test, pred)

print(f"{'MAE':<12} {mean_absolute_error(y_test, y_pred_lr):>12.4f} {mean_absolute_error(y_test, y_pred_dt):>14.4f}")
print(f"{'RMSE':<12} {np.sqrt(mean_squared_error(y_test, y_pred_lr)):>12.4f} {np.sqrt(mean_squared_error(y_test, y_pred_dt)):>14.4f}")
print(f"{'R²':<12} {r2_score(y_test, y_pred_lr):>12.4f} {r2_score(y_test, y_pred_dt):>14.4f}")
print("=" * 50)

# Step 7: Feature importance (linear regression coefficients vs tree importances)
print("\nFeature Importance Comparison:")
print(f"{'Feature':<22} {'LR |Coef|':>10} {'DT Import':>10}")
for i, fname in enumerate(housing.feature_names):
    lr_imp = abs(lr.coef_[i])
    dt_imp = dt.feature_importances_[i]
    print(f"{fname:<22} {lr_imp:>10.4f} {dt_imp:>10.4f}")
💻 输出:

TEXT 📖 仅展示
Dataset: 20640 samples, 8 features
Features: ['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']
Target range: [0.15, 5.00] (unit: $100,000)

Train: 16512 | Test: 4128

==================================================
Metric          Linear Reg   Decision Tree
==================================================
MAE                0.5252        0.4251
RMSE               0.7456        0.6052
R²                 0.5757        0.7210
==================================================

Feature Importance Comparison:
Feature                 LR |Coef|  DT Import
MedInc                     0.8269     0.5281
HouseAge                   0.1771     0.0521
AveRooms                   0.4283     0.0471
AveBedrms                  0.1447     0.0184
Population                 0.0021     0.0134
AveOccup                   0.0447     0.0481
Latitude                   0.8740     0.1434
Longitude                  0.8586     0.1494
💡 提示: 决策树的 R²(0.72)明显优于线性回归(0.58),因为房价与特征之间存在非线性关系。两个模型都认为收入(MedInc)是最重要的特征,但对地理位置(Latitude/Longitude)的判断差异很大——线性回归给了很高的权重,但决策树认为收入才是决定性因素。


❓ 常见问题

Q 线性回归"线性"指什么?
A 指权重与输出的关系是线性的,即 y = w₁x₁ + w₂x₂ + ... + b,每个权重 wᵢ 以加法方式影响 y。不是指输入特征 x 必须是线性的——你可以把 x 换成 x²、log(x) 等,它仍然是"线性回归"(线性的是权重,不是特征)。
Q 决策树会不会太深导致过拟合?
A 会。决策树如果不限制深度,可以一直分裂到每个叶子只有一个样本——训练集完美拟合但测试集表现极差。解决方法:① 限制 max_depth(如 3-8)② 设 min_samples_leaf(叶子最少样本数)③ 设 min_samples_split ④ 使用剪枝(pruning)⑤ 用随机森林替代单棵树。
Q R² 是 1.0 就一定好吗?
A 不一定。R² = 1.0 在训练集上往往意味着过拟合——模型"背下了"所有训练数据。关键是看测试集的 R²:如果训练 R² = 0.99 但测试 R² = 0.3,说明严重过拟合。此外,如果数据本身几乎没有噪声(如物理公式生成的数据),R² = 1.0 才是合理的。
Q 分类和回归能互相转换吗?
A 可以。回归问题可以"离散化"为分类:把房价分成"低/中/高"三档就是分类。分类问题也可以"概率化"为回归:逻辑回归输出的是概率(0-1 连续值),设定阈值后才变成类别。选择分类还是回归,取决于业务需求是"选哪个"还是"算多少"。
Q 为什么需要多个评估指标?
A 因为单个指标有盲区。准确率 99% 可能只是因为数据 99% 是同一类;R² 高但 MAE 可能很大(异常值拉高了总体方差)。每个指标从不同角度衡量模型,只有综合多个指标才能全面了解模型表现。就像体检不能只看一项指标——血压正常不代表血糖正常。
Q 特征缩放对决策树有影响吗?
A 没有。决策树基于特征阈值分裂,缩放只改变数值大小但不改变排序,而排序不变则分裂点不变。所以决策树不需要特征缩放。但线性回归、KNN、SVM、神经网络等基于距离或梯度的算法,必须先缩放。

📖 小节


📝 作业

  1. 基础题(难度⭐):用 sklearn 在加州房价数据集上训练一个 LinearRegression 模型,绘制面积(MedInc 列)与房价的散点图,并在图上画出回归线。
  2. 进阶题(难度⭐⭐):用 DecisionTreeClassifier 在 Iris 数据集上训练模型,输出混淆矩阵,并计算每个类别的 Precision 和 Recall(提示:classification_report)。
  3. 挑战题(难度⭐⭐⭐):在加州房价数据集上,对比 max_depth=3max_depth=None 两棵决策树的训练/测试 R²,观察过拟合现象;然后尝试加入 min_samples_leaf=10 等正则化参数,找到测试 R² 最高的配置。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏