
Hidden Markov Model (HMM) —— 隐马尔可夫模型
系统讲解隐马尔可夫模型(HMM)的完整数学原理与三大核心算法:从马尔可夫链到双重随机过程的演进出发,定义HMM的五元组参数(Q, V, π, A, B);详细推导估值问题、解码问题和学习问题;并通过词性标注、语音识别等经典应用场景展示HMM的实践价值。
阅读文章ZHY's Blog
A UNIVERSE OF IDEAS · BY ZHANG HAOYI
让好奇心 点亮知识宇宙
在代码、模型与思想之间自由漫游。这里持续记录人工智能、机器学习、软件工程与成长实践,让每次阅读都成为一次新的发现。
ARTICLE NOTE
回顾第一阶段:基础监督学习的全部六篇文章——从线性回归的解析解出发,历经逻辑回归的概率建模、KNN 的非参数距离、朴素贝叶斯的生成式假设、SVM 的几何间隔最大化,到核技巧的非线性扩展。这些算法虽然方法论各异,但共享一个底层逻辑:它们都在一个预先定义的假设空间中寻找“最优”的数学函数——无论这个函数是线性超平面、概率分布还是距离度量。
决策树彻底颠覆了这套范式。它放弃了所有关于“函数形式”的预设,转而采用一种与人类决策方式高度契合的机制 —— 通过一系列“是/否”的判断,将特征空间递归地划分为矩形区域,每个区域对应一个预测值。这种“分而治之”的策略让决策树拥有了两个无可替代的优势:极致的可解释性(决策路径可以被直接转化为 if-then 规则)和对特征尺度的天然免疫(分裂仅依赖排序关系,无需任何缩放)。
本文将从决策树家族的三位核心成员出发 —— ID3、C4.5 与 CART —— 系统梳理它们在分裂准则上的根本差异:信息增益、信息增益率、基尼指数与 MSE 各自对应的数学原理与适用场景。随后我们深入剪枝策略,对比预剪枝的“防患于未然”与后剪枝(特别是 CART 的 CCP 成本复杂度剪枝)的“亡羊补牢”,揭示二者在欠拟合与过拟合之间的权衡。最后,我们将从数学上解释决策树为何对特征缩放完全不敏感 —— 这一性质使其在处理异构特征时远比 KNN 或 SVM 从容。
Note决策树是第二阶段:树模型与集成学习的基石。理解单棵树的生长与剪枝,是后续理解 Bagging(通过抽样降低方差)、随机森林(在 Bagging 基础上引入特征随机选择)以及 AdaBoost、GBM、XGBoost(通过串行拟合残差降低偏差)的必要前提。
下一篇文章,我们将从“一棵树”走向“一片森林”——Bagging 与随机森林将展示如何通过集成学习让决策树从“易过拟合的弱学习器”蜕变为“强泛化的组合模型”。
决策树家族中有三大基础算法 —— ID3、C4.5和CART。它们之间的核心区别可以概括为一句话:特征选择的标准不同。
| 算法 | 提出年份 | 树类型 | 分裂准则 | 适用任务 |
|---|---|---|---|---|
| ID3 | 1986 | 多叉树 | 信息增益 | 分类 |
| C4.5 | 1993 | 多叉树 | 信息增益率 | 分类 |
| CART | 1984 | 二叉树 | 基尼指数(分类)/ MSE(回归) | 分类 + 回归 |
ID3(Iterative Dichotomiser 3)由Ross Quinlan于1986年提出,是决策树算法的开山之作。它的核心思想是以信息增益作为特征选择的准则,选择信息增益最大的特征进行分裂。
ID3的局限性:
C4.5是Quinlan对ID3的改进版本,主要解决了ID3的三大痛点:
C4.5的局限性:需要多次扫描和排序数据,效率较低;只能处理分类任务,不能做回归。
CART(Classification And Regression Tree)由Breiman等人在1984年提出,是决策树家族中最强大的成员。
CART 与 ID3 / C4.5 的关键区别:
代码实现:
1from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor2from sklearn.datasets import make_classification, make_regression3
4# CART分类树(默认使用基尼指数)5X_clf, y_clf = make_classification(n_samples=300, n_features=4, random_state=42)6clf = DecisionTreeClassifier(criterion='gini', max_depth=5)7clf.fit(X_clf, y_clf)8
9# CART回归树(使用MSE)10X_reg, y_reg = make_regression(n_samples=300, n_features=4, noise=10, random_state=42)11reg = DecisionTreeRegressor(criterion='squared_error', max_depth=5)12reg.fit(X_reg, y_reg)决策树的核心问题只有一个:每次分裂时,应该选择哪个特征?在哪个阈值上分裂? 不同的算法给出了不同的答案。下面我们逐一推导。
信息熵(Entropy) —— 信息论中衡量不确定性的指标。不确定性越大,熵越大。
对于样本集合 D,假设共有 K 个类别,第 k 类样本所占比例为 pk,则信息熵定义为:
H(D)=−k=1∑Kpklog2pk熵的性质:当所有样本都属于同一类别时,H(D)=0(纯度最高);当各类别样本均匀分布时,H(D) 最大(纯度最低)
条件熵 H(D∣A) —— 表示在已知特征 A 的条件下,数据集 D 的不确定性:
H(D∣A)=v=1∑V∣D∣∣Dv∣H(Dv)其中 V 是特征 A 的取值个数,Dv 是特征 A 取第 v 个值的样本子集。
信息增益 —— 分裂前后熵的减少量:
Gain(D,A)=H(D)−H(D∣A)ID3选择信息增益最大的特征进行分裂。
代码实现:
1import numpy as np2
3def entropy(y):4 """计算信息熵"""5 classes = np.unique(y)6 probs = [np.sum(y == c) / len(y) for c in classes]7 return -np.sum([p * np.log2(p) for p in probs if p > 0])8
9def information_gain(X, y, feature_idx):10 """计算某个特征的信息增益"""11 total_entropy = entropy(y)12 values = np.unique(X[:, feature_idx])13 weighted_entropy = 014 for v in values:15 mask = X[:, feature_idx] == v16 subset_y = y[mask]17 weighted_entropy += len(subset_y) / len(y) * entropy(subset_y)18 return total_entropy - weighted_entropy19
20# 示例:计算信息增益21X = np.array([[1, 0], [1, 1], [0, 0], [0, 1]])22y = np.array([0, 0, 1, 1])23print(f"特征0的信息增益: {information_gain(X, y, 0):.4f}")24print(f"特征1的信息增益: {information_gain(X, y, 1):.4f}")信息增益有一个致命缺陷:偏好取值数量多的特征。比如“ID”这样的特征,每个样本取值都不同,条件熵为0,信息增益最大,但这样的分裂毫无意义。
C4.5引入了信息增益率(Gain Ratio) 来修正这一问题:
GainRatio(D,A)=IV(A)Gain(D,A)其中 IV(A) 称为分裂信息(Intrinsic Value) ,衡量特征 A 自身的信息量:
IV(A)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣直观理解:特征取值越多,IV(A) 越大,信息增益率就被惩罚得越狠。
C4.5选择信息增益率最大的特征进行分裂。
CART分类树使用基尼指数(Gini Index) 作为分裂准则。
基尼指数衡量的是从数据集中随机抽取两个样本,其类别不一致的概率。基尼指数越小,纯度越高。
对于样本集合 D,基尼指数定义为:
Gini(D)=1−k=1∑Kpk2对于二分类问题,如果正类概率为 p,则:
Gini(D)=1−p2−(1−p)2=2p(1−p)对于特征 A 的某个划分(CART是二叉树,每次只二分):
Gini(D,A)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)CART分类树选择基尼指数最小的特征和阈值进行分裂。
CART用基尼指数代替熵的原因基尼指数与熵在数学上非常接近。
对于二分类问题,基尼指数 2p(1−p) 与熵之半 −plog2p−(1−p)log2(1−p) 的曲线几乎重合。
但基尼指数只涉及平方运算,避免了大量的对数运算,计算效率更高。
代码实现:
1def gini(y):2 """计算基尼指数"""3 classes = np.unique(y)4 probs = [np.sum(y == c) / len(y) for c in classes]5 return 1 - np.sum([p**2 for p in probs])6
7def gini_split(X, y, feature_idx, threshold):8 """计算在某个阈值上二分的基尼指数"""9 mask = X[:, feature_idx] <= threshold10 left_y, right_y = y[mask], y[~mask]11 left_weight = len(left_y) / len(y)12 right_weight = len(right_y) / len(y)13 return left_weight * gini(left_y) + right_weight * gini(right_y)当目标变量是连续值时,CART回归树使用均方误差(Mean Squared Error, MSE) 作为分裂准则。
对于节点 m,其预测值 y^m 为该节点所有样本目标值的均值,MSE为:
MSE(D)=∣D∣1i∈D∑(yi−y^D)2其中 y^D=∣D∣1∑i∈Dyi。
对于某个分裂,分裂后的MSE为左右子节点MSE的加权和:
MSEsplit=∣D∣∣D1∣MSE(D1)+∣D∣∣D2∣MSE(D2)CART回归树选择使分裂后MSE最小的特征和阈值。
决策树有一个著名的“缺点”:如果不加限制,它可以生长到完美拟合每一个训练样本 —— 直到每个叶子节点都只包含一个样本。这样的树在训练集上准确率100%,但在测试集上表现极差 —— 这就是过拟合。
剪枝(Pruning) 就是为了解决这个问题而生的。剪枝策略分为两大类:预剪枝和后剪枝。
预剪枝是在决策树生成过程中就提前停止树的生长。
常见的预剪枝条件:
| 参数 | 含义 |
|---|---|
max_depth | 树的最大深度 |
min_samples_split | 节点分裂所需的最小样本数 |
min_samples_leaf | 叶子节点所需的最小样本数 |
max_leaf_nodes | 最大叶子节点数 |
min_impurity_decrease | 分裂所需的最小不纯度下降 |
代码实现:
1from sklearn.tree import DecisionTreeClassifier2
3# 预剪枝:通过参数限制树的生长4clf = DecisionTreeClassifier(5 max_depth=5, # 最大深度6 min_samples_split=10, # 最少分裂样本数7 min_samples_leaf=5, # 最少叶子样本数8 max_leaf_nodes=20, # 最大叶子节点数9 random_state=4210)11clf.fit(X_train, y_train)后剪枝是先让树充分生长,然后再从底部向上修剪。
常见的后剪枝方法
- 成本复杂度剪枝(Cost-Complexity Pruning, CCP) —— CART采用的方法
- 悲观剪枝(Pessimistic Error Pruning, PEP) —— C4.5采用的方法
- 错误率降低剪枝(Reduced Error Pruning, REP)
成本复杂度剪枝(Cost-Complexity Pruning, CCP)
CCP是CART算法采用的剪枝方法,其核心思想是定义一个损失函数,在预测误差和树复杂度之间做权衡。
对于一棵树 T,定义其成本复杂度为:
Cα(T)=R(T)+α⋅∣T∣其中:
CCP的剪枝过程:
代码实现:
1from sklearn.tree import DecisionTreeClassifier2from sklearn.model_selection import train_test_split3
4# 1. 先让树充分生长(不设限制或设很松的限制)5clf = DecisionTreeClassifier(6 min_samples_split=2,7 min_samples_leaf=1,8 random_state=429)10clf.fit(X_train, y_train)11
12# 2. 使用ccp_alpha进行后剪枝13# 可以尝试不同的ccp_alpha值,用验证集选择最优的14for alpha in [0.0, 0.001, 0.005, 0.01, 0.05]:15 clf_pruned = DecisionTreeClassifier(16 ccp_alpha=alpha,17 random_state=4218 )19 clf_pruned.fit(X_train, y_train)20 acc = clf_pruned.score(X_val, y_val)21 print(f"ccp_alpha={alpha:.3f}, 验证集准确率={acc:.4f}, 叶子数={clf_pruned.tree_.n_leaves}")| 对比维度 | 预剪枝 | 后剪枝 |
|---|---|---|
| 时机 | 树生长过程中 | 树生长完成后 |
| 计算开销 | 小 | 大(需要先生成完整树) |
| 风险 | 欠拟合 | 过拟合(如果剪枝不充分) |
| 效果 | 通常较差 | 通常更好 |
| 常用程度 | 常用(因为简单高效) | 更常用(因为效果更好) |
实际建议:先用预剪枝参数(如 max_depth、min_samples_split)快速得到一个“差不多”的模型,如果效果不理想,再考虑使用 ccp_alpha 做后剪枝。
对于KNN或逻辑回归,特征标准化(Standardization) 或归一化(Normalization) 是必不可少的预处理步骤。但对于决策树,完全不需要做特征缩放。
决策树的分裂机制决定了它对特征尺度不敏感 —— 决策树寻找最佳分裂点时,仅依赖特征值的排序关系,而非原始数值大小。
特征选择:信息增益、信息增益率、基尼指数都基于概率分布计算,与特征的具体数值无关。无论特征值是从0到1还是从0到1000,只要排序关系不变,这些指标的计算结果就不变。
分裂点选择:对于连续特征,决策树将特征值排序后,在相邻值之间尝试切分。归一化只是把所有值按比例缩放,排序关系完全不变,因此最佳分裂点的位置(在排序中的位置)也完全不变。
假设连续特征 A 的取值范围为 [amin,amax],对其进行归一化:
Anorm=amax−aminA−amin对于任意候选分裂点 t∈[amin,amax],归一化后的对应分裂点为:
tnorm=amax−amint−amin由于归一化是严格单调递增的线性变换,排序关系完全不变。因此,原始分裂点 t 与归一化分裂点 tnorm 在分裂效果上完全等价。
更一般地,任何严格单调变换(如取对数、平方根等)都不会改变决策树的分裂决策。
决策树由输入特征的阶跃函数组成 —— 每个分裂点就像在特征轴上“切一刀”,左边的归左子树,右边的归右子树。这个“切”的位置只取决于相对顺序,而不取决于绝对数值。
这与其他基于距离的算法形成鲜明对比:
| 模型类型 | 对特征缩放是否敏感 | 原因 |
|---|---|---|
| KNN、SVM、神经网络 | 敏感 | 依赖距离计算,量纲大的特征主导 |
| 决策树、随机森林、GBDT | 不敏感 | 依赖排序关系,不依赖距离 |
代码实现:
1from sklearn.tree import DecisionTreeClassifier2from sklearn.preprocessing import StandardScaler3from sklearn.datasets import make_classification4
5# 验证:特征缩放不影响决策树的预测结果6X, y = make_classification(n_samples=200, n_features=5, random_state=42)7X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)8
9# 不缩放10clf_raw = DecisionTreeClassifier(random_state=42)11clf_raw.fit(X_train, y_train)12pred_raw = clf_raw.predict(X_test)13
14# 标准化15scaler = StandardScaler()16X_train_scaled = scaler.fit_transform(X_train)17X_test_scaled = scaler.transform(X_test)18clf_scaled = DecisionTreeClassifier(random_state=42)19clf_scaled.fit(X_train_scaled, y_train)20pred_scaled = clf_scaled.predict(X_test_scaled)21
22# 两次预测应该完全一致23print(f"预测结果是否相同: {np.all(pred_raw == pred_scaled)}") # True1import numpy as np2import matplotlib.pyplot as plt3from sklearn.datasets import load_iris4from sklearn.tree import DecisionTreeClassifier, plot_tree5from sklearn.model_selection import train_test_split6from sklearn.metrics import accuracy_score7
8# 1. 加载数据9iris = load_iris()10X, y = iris.data, iris.target11X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)12
13# 2. 训练决策树(带预剪枝)14clf = DecisionTreeClassifier(15 criterion='gini', # CART分类树使用基尼指数16 max_depth=4, # 预剪枝:限制深度17 min_samples_split=10, # 预剪枝:限制分裂最小样本数18 random_state=4219)20clf.fit(X_train, y_train)21
22# 3. 评估23y_pred = clf.predict(X_test)24print(f"准确率: {accuracy_score(y_test, y_pred):.4f}")25print(f"树的深度: {clf.get_depth()}")26print(f"叶子节点数: {clf.get_n_leaves()}")27
28# 4. 可视化决策树29plt.figure(figsize=(16, 8))30plot_tree(clf, feature_names=iris.feature_names, class_names=iris.target_names,31 filled=True, rounded=True, fontsize=10)32plt.title("决策树可视化(鸢尾花分类)")33plt.show()34
35# 5. 后剪枝:使用ccp_alpha36path = clf.cost_complexity_pruning_path(X_train, y_train)37ccp_alphas = path.ccp_alphas38
39# 对每个alpha训练一棵树40clfs = []41for alpha in ccp_alphas:42 clf_alpha = DecisionTreeClassifier(ccp_alpha=alpha, random_state=42)43 clf_alpha.fit(X_train, y_train)44 clfs.append(clf_alpha)45
46# 在验证集上选择最优alpha47train_scores = [clf.score(X_train, y_train) for clf in clfs]48test_scores = [clf.score(X_test, y_test) for clf in clfs]49
50# 可视化alpha的影响51plt.figure(figsize=(10, 6))52plt.plot(ccp_alphas, train_scores, 'b-o', label='训练集准确率')53plt.plot(ccp_alphas, test_scores, 'r-o', label='测试集准确率')54plt.xlabel('ccp_alpha')55plt.ylabel('准确率')56plt.legend()57plt.title('ccp_alpha 对模型性能的影响')58plt.show()总结
概念 核心内容 ID3 信息增益,多叉树,只能分类,无剪枝 C4.5 信息增益率,多叉树,只能分类,有剪枝 CART 基尼指数(分类)/ MSE(回归),二叉树,分类+回归 信息增益 H(D)−H(D∥A),偏好取值多的特征 信息增益率 信息增益 / 分裂信息,修正了对取值多特征的偏好 基尼指数 1−∑pk2,越小越纯,CART分类用 MSE n1∑(yi−y^)2,CART回归用 预剪枝 生长过程中提前停止,简单但可能欠拟合 后剪枝 生长完成后修剪,效果好但计算量大 CCP Cα(T)=R(T)+α∥T∥,CART的后剪枝方法 特征尺度 决策树对特征缩放不敏感(依赖排序而非距离) 核心要点回顾
- ID3、C4.5、CART 的核心区别在于分裂准则不同。ID3用信息增益,C4.5用信息增益率,CART分类用基尼指数、回归用MSE。
- 信息增益偏好取值多的特征,信息增益率通过除以分裂信息来修正这一偏差,基尼指数与熵在数学上近似但计算更高效。
- 预剪枝通过
max_depth、min_samples_split等参数在树生长过程中限制复杂度;后剪枝(如CART的CCP)先生成完整树再修剪,通常效果更好。- 决策树对特征尺度不敏感的根本原因在于:分裂只依赖特征值的排序关系,而非数值大小。任何单调变换都不会改变分裂决策。
- 实际使用建议:先用预剪枝参数快速得到一个基准模型,再用
ccp_alpha做精细的后剪枝调优。
按顺序完成这组文章,循序渐进地掌握主题
发现错误、内容过时或有改进想法?欢迎告诉我
根据本文分类与标签,为你推荐可能感兴趣的内容

系统讲解隐马尔可夫模型(HMM)的完整数学原理与三大核心算法:从马尔可夫链到双重随机过程的演进出发,定义HMM的五元组参数(Q, V, π, A, B);详细推导估值问题、解码问题和学习问题;并通过词性标注、语音识别等经典应用场景展示HMM的实践价值。
阅读文章
系统讲解期望最大化(EM)算法的完整数学原理:从极大似然估计在隐变量存在时的困境出发,推导E步与M步的迭代框架;基于Jensen不等式证明ELBO证据下界与收敛性;通过二硬币模型与高斯混合模型(GMM)两个完整实例展示EM的具体计算流程;揭示K-Means是EM在硬分配下的特例这一深层联系。
阅读文章
系统讲解K-Means聚类的核心原理与算法细节,涵盖Lloyd交替优化算法的收敛性分析、K-Means作为EM算法特例的理论联系(硬分配 vs 软分配)、K-Means++初始化策略的D²采样机制与O(log K)近似保证,以及肘部法则与轮廓系数的选择K值方法及其局限。
阅读文章请使用微信扫描二维码分享
当前文章会保持在原页面