监督学习
3732 字约 12 分钟
2026-05-20
监督学习是最常用的机器学习范式。给算法一批有标签的训练数据,让它学会从输入预测输出。本文覆盖从基础原理到各核心算法的完整知识体系。
1. 核心概念
1.1 什么是监督学习
通过已有的 输入(特征 X)→ 输出(标签 Y) 对,训练一个能预测新输入的模型。
两大类任务:
- 回归(Regression):输出是连续值(预测房价、股价、温度)
- 分类(Classification):输出是离散类别(垃圾邮件/正常、猫/狗)
1.2 基本术语
| 术语 | 含义 |
|---|---|
| 训练集 | 用于训练模型的数据 |
| 特征 (Feature) | 输入变量 X,模型的"原料" |
| 标签 (Label) | 输出变量 Y,我们想预测的值 |
| 假设函数 h(x) | 模型的预测函数 |
| 代价函数 J(θ) | 衡量模型预测偏差的函数 |
| 参数 θ | 模型中需要学习的权重 |
1.3 训练集、验证集、测试集
| 数据集 | 用途 | 典型比例 |
|---|---|---|
| 训练集 | 训练模型参数 | 70% |
| 验证集 | 调超参数、选模型(不参与训练) | 15% |
| 测试集 | 最终评估,只用一次 | 15% |
常见错误
测试集只能在最终评估时用一次。如果用测试集来调参,就相当于把答案提前告诉模型了,会高估泛化性能。
2. 线性回归
2.1 单变量线性回归
假设函数:
hθ(x)=θ0+θ1x
代价函数(均方误差 MSE):
J(θ0,θ1)=2m1i=1∑m(hθ(x(i))−y(i))2
其中 m 是训练样本数,前面乘以 1/2 是为了求导后消掉系数。
目标:找到使 J 最小的 θ₀ 和 θ₁
2.2 梯度下降(Gradient Descent)
不断沿着代价函数下降最快的方向迭代更新参数:
θj:=θj−α∂θj∂J
对单变量线性回归展开:
θ0:=θ0−αm1i=1∑m(hθ(x(i))−y(i))
θ1:=θ1−αm1i=1∑m(hθ(x(i))−y(i))⋅x(i)
注意:θ₀ 和 θ₁ 必须同步更新,不能先更新一个再用更新后的值算另一个。
学习率 α 的选择:
- α 太小:收敛极慢
- α 太大:可能越过最小值,甚至发散
- 推荐做法:从 0.001 开始,以 3 倍递增(0.001, 0.003, 0.01, 0.03, 0.1...),画学习曲线选最优
线性回归代价函数是凸函数(Bowl Shape),没有局部最优,梯度下降一定能到达全局最优。
2.3 三种梯度下降变体
| 类型 | 每次用多少数据 | 特点 |
|---|---|---|
| BGD(批量梯度下降) | 全部训练集 | 稳定,但大数据集慢 |
| SGD(随机梯度下降) | 1个样本 | 快,但噪声大,不稳定 |
| Mini-batch GD | m个样本(通常32~256) | 兼顾速度和稳定性,工程首选 |
Mini-batch 中 m 选 2 的幂次方(32, 64, 128, 256)能充分利用 GPU 矩阵运算。
import copy
import numpy as np
def gradient_descent(x, y, w_in, b_in, alpha, num_iters):
m = len(x)
J_history = []
w, b = copy.deepcopy(w_in), b_in
for i in range(num_iters):
# 计算预测值
f_wb = w * x + b
# 计算梯度
dj_dw = (1/m) * np.sum((f_wb - y) * x)
dj_db = (1/m) * np.sum(f_wb - y)
# 同步更新参数
w = w - alpha * dj_dw
b = b - alpha * dj_db
# 记录代价函数
J_history.append(np.sum((f_wb - y)**2) / (2*m))
return w, b, J_history2.4 多元线性回归
多个特征的情况:
hθ(x)=θ0+θ1x1+θ2x2+⋯+θnxn=θTx
特征缩放(Feature Scaling):当各特征数值范围差异很大时,梯度下降会来回振荡,收敛很慢。通过特征缩放使各特征范围相近。
两种常用方法:
Z-score 标准化(推荐):
xj′=σjxj−μj
处理后均值为0,标准差为1。
Min-Max 归一化:
xj′=max(xj)−min(xj)xj−min(xj)
压缩到 [0, 1] 区间。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 注意用 transform 而非 fit_transform2.5 正规方程(Normal Equation)
直接用数学公式求解最优参数,无需迭代:
θ=(XTX)−1XTy
| 梯度下降 | 正规方程 | |
|---|---|---|
| 需要选学习率 | 是 | 否 |
| 需要迭代 | 是 | 否 |
| 特征数量大时 | 仍然有效 | 计算矩阵逆非常慢(O(n³)) |
| 适用场景 | 特征数量大(>10000) | 特征数量小 |
2.6 正则化(防止过拟合)
正则化通过在代价函数中添加惩罚项来限制模型复杂度:
L2 正则化(Ridge):
J(θ)=MSE+λj=1∑nθj2
权重均匀缩小,不会变为0,适合所有特征都有用的情况。
L1 正则化(Lasso):
J(θ)=MSE+λj=1∑n∣θj∣
倾向于产生稀疏解(部分权重精确为0),相当于自动特征选择。
Elastic Net:L1 和 L2 的组合,结合两者优点。
λ(正则化强度)越大,模型越简单(偏差大但方差小);λ 越小,接近不正则化(偏差小但方差大)。
3. 逻辑回归(分类)
3.1 为什么不用线性回归做分类
线性回归对离群点敏感,而且输出不在 [0, 1] 范围内,不适合表示概率。
3.2 Sigmoid 函数
g(z)=1+e−z1
将任意实数映射到 (0, 1) 区间:
- z → +∞ 时,g(z) → 1
- z → -∞ 时,g(z) → 0
- z = 0 时,g(z) = 0.5
为什么用 Sigmoid?
- 输出可解释为概率
- 广义线性模型推导的自然结果
- 满足最大熵原理
- 函数光滑,任意阶可导,且导数可由函数值直接表达:g'(z) = g(z)(1-g(z))
逻辑回归模型:
hθ(x)=g(θTx)=1+e−θTx1
输出的含义:P(y=1 | x; θ),即给定特征 x,y=1 的概率。
3.3 决策边界
- P ≥ 0.5 → 预测为 1(θᵀx ≥ 0)
- P < 0.5 → 预测为 0(θᵀx < 0)
决策边界就是 θᵀx = 0 这条(超)平面。
3.4 为什么不用 MSE 作损失函数
如果用 MSE + Sigmoid,代价函数是非凸的,有多个局部最优,梯度下降无法保证找到全局最优。
而且 Sigmoid 的导数最大值只有 0.25,MSE 的梯度中含有 Sigmoid 导数,会导致梯度消失,学习极慢。
3.5 交叉熵损失(Binary Cross-Entropy)
J(θ)=−m1i=1∑m[y(i)logh(x(i))+(1−y(i))log(1−h(x(i)))]
交叉熵损失的梯度中没有 Sigmoid 导数,更新速度快,且函数是凸的,有全局最优解。
其梯度更新公式和线性回归惊人地相似:
θj:=θj−αm1i=1∑m(hθ(x(i))−y(i))xj(i)
3.6 LR 的本质:极大似然估计
逻辑回归等价于对伯努利分布做极大似然估计。最大化似然函数等价于最小化交叉熵损失。
import numpy as np
from sklearn.linear_model import LogisticRegression
# 完整逻辑回归实现
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def logistic_regression(X, y, learning_rate=0.01, iterations=1000):
m, n = X.shape
theta = np.zeros(n)
for _ in range(iterations):
z = X @ theta
h = sigmoid(z)
gradient = X.T @ (h - y) / m
theta -= learning_rate * gradient
return theta
# 用 sklearn
model = LogisticRegression(C=1.0, penalty='l2', max_iter=1000)
model.fit(X_train, y_train)
print(f"准确率: {model.score(X_test, y_test):.3f}")3.7 多分类:Softmax 回归
逻辑回归的推广,输出 K 个类别的概率分布:
P(y=k∣x)=∑j=1KeθjTxeθkTx
所有类别的概率之和为 1,每个类别有自己的参数向量 θₖ。
4. 支持向量机(SVM)
4.1 核心思想
SVM 寻找最大间隔超平面来分离两类数据。它不只是找到一个能分开的边界,而是找到间隔(margin)最大的那个边界。
为什么最大间隔好? 间隔越大,模型对噪声越鲁棒,泛化能力更强。
4.2 硬间隔(线性可分)
目标:最大化 2/‖w‖,等价于最小化 ½‖w‖²
约束条件:
- 正类样本:wᵀx + b ≥ 1
- 负类样本:wᵀx + b ≤ -1
距离超平面最近的样本点称为支持向量,模型只由支持向量决定。
4.3 软间隔(允许错误)
引入松弛变量 ξᵢ 和惩罚参数 C:
min21∣∣w∣∣2+Ci=1∑mξi
C 越大,对错误分类的惩罚越重,间隔越小;C 越小,允许更多错误,间隔越大。
4.4 核函数(非线性分类)
对于线性不可分的数据,通过核函数将数据映射到高维空间,在高维空间中线性可分:
| 核函数 | 公式 | 适用场景 |
|---|---|---|
| 线性核 | K(x,z) = xᵀz | 高维稀疏特征(文本) |
| 多项式核 | K(x,z) = (xᵀz + c)^d | 图像、NLP |
| RBF/高斯核 | K(x,z) = exp(-‖x-z‖²/2σ²) | 最通用,默认选择 |
| Sigmoid 核 | K(x,z) = tanh(αxᵀz + c) | 神经网络相关 |
from sklearn.svm import SVC
# 线性 SVM
svm_linear = SVC(kernel='linear', C=1.0)
# RBF 核 SVM(最常用)
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale')
# 参数调优建议
from sklearn.model_selection import GridSearchCV
param_grid = {'C': [0.1, 1, 10, 100], 'gamma': [1, 0.1, 0.01, 0.001]}
grid_search = GridSearchCV(SVC(kernel='rbf'), param_grid, cv=5)
grid_search.fit(X_train, y_train)4.5 SVM vs 逻辑回归
| SVM | 逻辑回归 | |
|---|---|---|
| 目标 | 最大化间隔 | 最大化似然 |
| 适合 | 高维、小样本 | 大样本,需要概率输出 |
| 非线性 | 核函数 | 需手动添加多项式特征 |
| 噪声容忍 | 强(仅支持向量决定模型) | 较弱 |
| 训练速度 | 大数据集较慢 | 更快 |
5. 决策树
5.1 核心思想
通过一系列特征判断(if-else)将数据递归地分割,直到每个叶节点的数据尽可能纯净(属于同一类别)。
5.2 如何选择最优分裂特征?
信息增益(用于 ID3 算法):
Gain(D,a)=H(D)−v∑∣D∣∣Dv∣H(Dv)
其中熵 H(D):
H(D)=−k=1∑Kpklog2pk
基尼系数(用于 CART 算法,sklearn 默认):
Gini(D)=1−k=1∑Kpk2
基尼系数越小,数据越纯净,计算比熵快(不需要对数运算)。
5.3 防止过拟合:剪枝
决策树天然倾向于过拟合(可以无限生长直到完美分类训练集)。
预剪枝(Pre-pruning):
- 限制最大深度
max_depth - 限制叶节点最少样本数
min_samples_leaf - 限制分裂最少样本数
min_samples_split
后剪枝(Post-pruning):先生长完整树,再从下往上删除不必要的节点。
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pyplot as plt
dt = DecisionTreeClassifier(
criterion='gini', # 或 'entropy'
max_depth=5, # 防止过拟合的关键参数
min_samples_leaf=5,
min_samples_split=10,
random_state=42
)
dt.fit(X_train, y_train)
# 查看特征重要性
feat_importance = pd.Series(
dt.feature_importances_,
index=feature_names
).sort_values(ascending=False)
feat_importance.plot(kind='bar')6. 集成学习
集成学习的核心思想:三个臭皮匠,顶个诸葛亮。多个弱分类器组合成一个强分类器。
6.1 Bagging
Boostrap Aggregating:并行训练多个模型,每个模型用有放回抽样的数据子集训练,最后投票/平均。
- 减少方差(过拟合风险)
- 典型代表:随机森林
随机森林 = Bagging + 随机特征选择
每次分裂时随机选取 √n 个特征(n 为总特征数),再从中选最优分裂点。双重随机性让各棵树尽量不同,集成效果更好。
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(
n_estimators=200, # 树的数量,越多越好但有收益递减
max_features='sqrt', # 每次分裂随机选的特征数
max_depth=10,
min_samples_leaf=5,
n_jobs=-1, # 并行训练,-1 用所有 CPU
random_state=42
)
rf.fit(X_train, y_train)6.2 Boosting
串行训练,每个新模型重点关注前面模型预测错误的样本。减少偏差(欠拟合问题)。
AdaBoost:通过调整样本权重,让后续模型关注难分样本。
GBDT(梯度提升决策树):用梯度下降拟合残差,每棵新树拟合当前模型的梯度(误差方向)。
XGBoost:目前工业界最常用的Boosting算法,在 GBDT 基础上加了:
- 正则化项(L1+L2)防止过拟合
- 列采样(类似随机森林)
- 二阶泰勒展开优化
- 并行计算(特征预排序)
- 处理缺失值
import xgboost as xgb
model = xgb.XGBClassifier(
n_estimators=500,
max_depth=6,
learning_rate=0.05,
subsample=0.8, # 行采样
colsample_bytree=0.8, # 列采样
reg_alpha=0.1, # L1 正则
reg_lambda=1.0, # L2 正则
use_label_encoder=False,
eval_metric='logloss',
random_state=42
)
# 早停:验证集不再提升时停止
model.fit(
X_train, y_train,
eval_set=[(X_val, y_val)],
early_stopping_rounds=50,
verbose=100
)LightGBM:微软出品,比 XGBoost 更快,适合大数据集:
- 叶子优先生长(leaf-wise)而非层优先
- 直方图算法,减少内存占用
import lightgbm as lgb
params = {
'objective': 'binary',
'metric': 'auc',
'num_leaves': 31,
'learning_rate': 0.05,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 5,
}
dtrain = lgb.Dataset(X_train, label=y_train)
dval = lgb.Dataset(X_val, label=y_val, reference=dtrain)
model = lgb.train(
params, dtrain,
num_boost_round=1000,
valid_sets=[dval],
callbacks=[lgb.early_stopping(50)]
)6.3 Stacking
第一层:多个不同类型的基学习器(Logistic Regression、随机森林、XGBoost...)
第二层:用基学习器的输出作为特征,训练一个元学习器(通常是简单的逻辑回归)
比 Voting 更灵活,Kaggle 竞赛常用。
7. 其他常用算法
7.1 K 近邻(KNN)
原理:预测时找最近的 K 个邻居,多数类别为预测结果。
特点:
- 无需训练(懒惰学习),但预测时需计算所有距离,大数据集慢
- K 越小:模型越复杂,容易过拟合;K 越大:模型越简单,可能欠拟合
- 必须做特征缩放(距离对量纲敏感)
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5, metric='minkowski', p=2)
knn.fit(X_train_scaled, y_train)7.2 朴素贝叶斯
原理:基于贝叶斯定理,假设特征之间条件独立:
P(y∣x1,...,xn)∝P(y)i=1∏nP(xi∣y)
特点:
- 假设特征独立("朴素"),现实中往往不满足,但实践中效果常常不错
- 训练和预测极快
- 需要的数据量少
- 在文本分类(垃圾邮件)中表现出色
from sklearn.naive_bayes import GaussianNB, MultinomialNB
# 连续特征用高斯分布
gnb = GaussianNB()
# 文本特征(词频)用多项分布
mnb = MultinomialNB(alpha=1.0) # alpha 为拉普拉斯平滑8. 算法选择指南
数据量很大(百万+)?
├─ 是 → 逻辑回归、SGD、线性SVM(训练快)
└─ 否 ↓
特征数量 >> 样本数量?
├─ 是 → 正则化线性模型、SVM
└─ 否 ↓
需要可解释性?
├─ 是 → 决策树、逻辑回归
└─ 否 ↓
追求最高精度?
├─ 是 → XGBoost/LightGBM(表格数据),神经网络(图像/文本)
└─ 否 → 随机森林(稳定、好调参)9. 常见面试问题
Q:偏差(Bias)和方差(Variance)的区别?
偏差:模型预测值与真实值的系统性偏差,高偏差=欠拟合,模型太简单。
方差:模型在不同训练集上输出的变化程度,高方差=过拟合,模型对训练数据太敏感。
Q:L1 和 L2 正则化的区别?
L1 产生稀疏解(特征选择),L2 均匀缩小权重。当特征有很多是无用的,用 L1;当认为所有特征都有用,用 L2。
Q:随机森林和 GBDT 的区别?
随机森林:并行训练,减少方差,对噪声更鲁棒;GBDT:串行训练,减少偏差,更容易过拟合但精度通常更高。
Q:为什么 XGBoost 比 GBDT 效果好?
加了正则化项,用了二阶泰勒展开(更精确的梯度),支持列采样,处理缺失值,并行计算。