简介:这份资源面向希望深入理解机器学习底层原理的学习者与开发者,聚焦决策树、随机森林、XGBoost、PCA、SVM、贝叶斯回归等经典算法的简洁实现,帮助读者摆脱对现成库的依赖,从零掌握模型构建与调参思路。压缩包共47个文件,以26个Python源码为主,辅以10个Markdown说明文档、10个pyc缓存文件及1个txt数据文件,整体约58KB,目录按算法模块划分,结构清晰便于检索。内容涵盖KNN、支持向量机核函数、逻辑回归、线性回归、随机森林、梯度提升树、朴素贝叶斯、PCA降维等模块,每个算法均配有示例脚本与README说明,并包含损失函数、数据操作与核函数等公共工具代码。已有185人学习下载,适合作为课程实验、算法复现或面试准备的参考素材,能帮助读者快速对照代码理解公式推导与工程实现之间的对应关系。
1. 从一份“算法全家桶”说起:决策树、随机森林、XGBoost、PCA、SVM 到底该怎么串起来用
很多人第一次拿到「常用机器学习的算法简洁实现」这类压缩包,第一反应是解压、逐个跑通、看输出对不对,然后就没有然后了。真正卡住人的从来不是某个算法写不出来,而是面对一份结构化数据时,不知道该先上决策树还是随机森林,PCA 到底该放在哪一步,SVM 的核函数怎么选,XGBoost 又凭什么在表格任务里几乎成了默认答案。这篇笔记就按一线做项目的顺序,把决策树、随机森林、XGBoost、PCA、SVM、贝叶斯回归这几件事从「能跑」讲到「敢用」,重点放在参数怎么设、坑在哪、什么场景该换谁。适合已经会调 sklearn 和 xgboost、但每次建模还在凭感觉试的人,也适合想把这几类算法串成一条稳定流水线的同学。
2. 决策树与随机森林:从单棵树的过拟合到集成后的稳定输出
2.1 为什么单棵决策树几乎必然过拟合
决策树的核心是递归地选一个特征、选一个切分点,让子节点的不纯度下降最多。分类任务常用基尼系数或信息熵,回归任务用 MSE。它最大的优点是解释性强、不用做特征缩放、能处理非线性,但缺点同样致命:如果不限制深度,树会一直切到每个叶子只剩一个样本,训练集准确率 100%,测试集直接崩。这就是典型的过拟合。
控制过拟合的手段全在参数里。max_depth限制树深,min_samples_split控制一个节点至少多少样本才继续切,min_samples_leaf控制叶子最少样本数,max_features限制每次切分考虑的特征数。我一般先用max_depth=5左右跑一版看基线,再根据验证集曲线调。注意:决策树对数据里的噪声和异常值很敏感,一个离群点就可能改变整棵树的切分路径,所以上树之前最好先看一眼分布。
from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # X, y 为已准备好的特征和标签 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) dt = DecisionTreeClassifier( criterion="gini", # 分类用 gini,回归换 squared_error max_depth=5, # 先给一个保守深度,防止一上来就过拟合 min_samples_split=20, # 节点样本少于 20 不再切 min_samples_leaf=10, # 叶子至少 10 个样本,抑制噪声 random_state=42 ) dt.fit(X_train, y_train) print(classification_report(y_test, dt.predict(X_test)))这段代码里criterion决定不纯度度量,max_depth是最直接的正则化旋钮,min_samples_split和min_samples_leaf从样本量角度限制树的生长。stratify=y保证分类任务里训练测试的类别比例一致,类别不平衡时尤其重要。跑完先看classification_report的 recall 和 f1,不要只看 accuracy。
2.2 随机森林靠什么把方差压下来
随机森林的思路是:单棵树方差大,那我就种很多棵,每棵用不同的样本子集(bootstrap)和不同的特征子集,最后投票或取平均。这样单棵树的过拟合被平均掉,整体方差显著下降。它对噪声和异常值的容忍度比单棵树高得多,而且几乎不用调太多参数就能有不错的效果,这也是它在遥感分类、风控、特征工程初期被大量使用的原因。
关键参数有n_estimators(树的数量)、max_features(每次切分考虑的特征数)、max_depth、min_samples_leaf。经验上n_estimators从 100 起步,加到 300~500 通常收益递减;分类任务max_features常用sqrt,回归常用1/3或log2。树越多越稳,但训练和推理成本线性上升,线上服务要权衡。
from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier( n_estimators=300, # 树的数量,先 300 看效果 max_features="sqrt", # 分类默认 sqrt,回归可试 0.3 max_depth=None, # 让每棵树充分生长,靠平均降方差 min_samples_leaf=3, # 叶子样本下限,抑制噪声 n_jobs=-1, # 用满 CPU random_state=42 ) rf.fit(X_train, y_train) print(classification_report(y_test, rf.predict(X_test)))n_jobs=-1让训练并行,min_samples_leaf是随机森林里最值得调的参数之一,调大能明显降低过拟合。随机森林还能直接输出feature_importances_,虽然它偏向高基数特征,但用来做初筛足够。注意:随机森林对高维稀疏数据(比如文本 one-hot)表现一般,这时候要么先降维,要么换线性模型。
2.3 从单棵树到森林的落地检查清单
实际项目里我一般这样走:先用单棵浅树看特征重要性和大致分界,再用随机森林拿一个稳定基线,最后才考虑 XGBoost 冲精度。检查清单包括:训练集和验证集指标差距是否过大(过拟合信号)、特征重要性里有没有明显的数据泄漏特征、类别是否不平衡(必要时加class_weight="balanced")、以及推理延迟能不能接受。随机森林的推理是遍历所有树,树多时延迟不低,线上要压测。
3. XGBoost:表格任务里的默认答案,以及它的参数怎么调
3.1 梯度提升和随机森林的本质区别
随机森林是并行种树、各自独立、最后平均,属于 bagging 思路,主要降方差。XGBoost 是梯度提升(boosting)思路:一棵一棵串行地种,每棵新树去拟合前面所有树的残差,逐步把损失降下来。它同时用了一阶导和二阶导信息,加了正则项控制复杂度,还支持列采样、行采样、缺失值自动处理。这就是它在结构化表格数据上长期霸榜的原因。
代价是它对参数更敏感,学习率、树深、正则项没调好,要么欠拟合要么过拟合。而且它是串行的,训练比随机森林慢,但推理时树的数量通常更少,延迟反而可能更低。
3.2 一份能直接抄的 XGBoost 分类与回归模板
import xgboost as xgb from sklearn.metrics import mean_squared_error import numpy as np # 分类任务 clf = xgb.XGBClassifier( n_estimators=500, # 树的数量,配合学习率调 learning_rate=0.05, # 学习率,越小越稳但需要更多树 max_depth=6, # 树深,表格任务 4~8 常见 subsample=0.8, # 行采样,抗过拟合 colsample_bytree=0.8, # 列采样,抗过拟合 reg_lambda=1.0, # L2 正则 reg_alpha=0.0, # L1 正则 min_child_weight=1, # 叶子最小样本权重和 gamma=0.0, # 分裂所需最小损失下降 eval_metric="logloss", early_stopping_rounds=50, random_state=42 ) clf.fit(X_train, y_train, eval_set=[(X_test, y_test)], verbose=False) # 回归任务 reg = xgb.XGBRegressor( n_estimators=800, learning_rate=0.03, max_depth=5, subsample=0.8, colsample_bytree=0.8, reg_lambda=2.0, early_stopping_rounds=50, random_state=42 ) reg.fit(X_train, y_train, eval_set=[(X_test, y_test)], verbose=False) pred = reg.predict(X_test) print("RMSE:", np.sqrt(mean_squared_error(y_test, pred)))learning_rate和n_estimators是一对:学习率小就要更多树,通常 0.01~0.1 之间。max_depth控制模型复杂度,表格数据 4~8 足够,太深容易过拟合。subsample和colsample_bytree是行、列采样比例,0.6~0.9 之间比较稳。reg_lambda和reg_alpha是 L2、L1 正则,特征多、噪声大时调大。early_stopping_rounds配合eval_set用,验证集指标不再提升就停,这是省时间又防过拟合的关键。
3.3 用 RandomizedSearchCV 做超参数自动设置
手调太慢时,用随机搜索比网格搜索更划算,尤其参数空间大的时候。下面这段可以直接套:
from sklearn.model_selection import RandomizedSearchCV param_dist = { "max_depth": [3, 4, 5, 6, 8], "learning_rate": [0.01, 0.03, 0.05, 0.1], "n_estimators": [300, 500, 800], "subsample": [0.6, 0.8, 1.0], "colsample_bytree": [0.6, 0.8, 1.0], "reg_lambda": [0.5, 1.0, 2.0, 5.0] } search = RandomizedSearchCV( xgb.XGBRegressor(random_state=42), param_distributions=param_dist, n_iter=40, # 随机采样 40 组 scoring="neg_root_mean_squared_error", cv=5, n_jobs=-1, random_state=42 ) search.fit(X_train, y_train) print(search.best_params_)n_iter是采样组数,40~60 组通常够用;cv=5是五折交叉验证;scoring按任务选,回归用负 RMSE,分类用 f1 或 roc_auc。注意随机搜索仍然可能过拟合验证集,最好留一个独立的测试集做最终评估。
4. PCA 与 SVM:降维和核方法,什么时候该用、什么时候别硬上
4.1 PCA 到底在做什么,什么时候该做
PCA(主成分分析)通过正交变换把原始特征投影到方差最大的几个方向上,达到降维、去相关、去噪的目的。它假设数据的主要信息藏在方差大的方向里。常见用途:特征高度相关时压缩维度、可视化(降到 2~3 维)、加速下游模型、缓解维度灾难。
但 PCA 有两个硬伤:一是它只关心方差,不关心和标签的关系,可能把判别信息强的低方差方向丢掉;二是降维后主成分没有原始特征的物理含义,解释性变差。所以做遥感、ENVI 主成分分析这类任务时,PCA 常用来压缩波段,但分类前最好对比一下降维前后模型指标。
from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler # PCA 前必须标准化,否则量纲大的特征会主导方差 scaler = StandardScaler() X_scaled = scaler.fit_transform(X_train) pca = PCA(n_components=0.95) # 保留 95% 方差 X_pca = pca.fit_transform(X_scaled) print("保留主成分数:", pca.n_components_) print("累计方差贡献:", pca.explained_variance_ratio_.cumsum())n_components可以填整数(保留几个主成分)或 0~1 的小数(保留多少方差比例)。标准化是必须的,否则 PCA 结果会被量纲带偏。explained_variance_ratio_用来看每个主成分解释了多少方差,累计到 0.95 通常够。注意 PCA 是无监督的,如果下游是分类任务,可以考虑用 LDA 这类有监督降维做对比。
4.2 SVM 的核函数与参数怎么选
SVM 的目标是找一个最大间隔超平面。线性不可分时,用核函数把数据映射到高维空间,使其线性可分。常用核:linear(高维稀疏数据、文本)、rbf(通用默认)、poly(多项式关系)、sigmoid(少用)。核心参数是C和gamma:C越大对误分类惩罚越重,容易过拟合;gamma越大,单个样本影响范围越小,也容易过拟合。
在 optdigits 手写数字分类这类任务里,SVM 的核函数和参数影响非常明显:rbf通常比linear好,但gamma没调好会直接崩。我一般用网格搜索在小范围里定C和gamma,量级按 10 的幂次走。
from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV param_grid = { "C": [0.1, 1, 10, 100], "gamma": [0.001, 0.01, 0.1, 1], "kernel": ["rbf"] } grid = GridSearchCV( SVC(probability=False), param_grid, scoring="accuracy", cv=5, n_jobs=-1 ) grid.fit(X_train, y_train) print(grid.best_params_, grid.best_score_)SVM 对特征缩放极其敏感,上之前必须标准化。样本量超过几万时,SVM 训练会明显变慢,这时候优先考虑线性模型或树模型。probability=True会启用概率估计,但训练更慢,不需要概率就别开。
4.3 贝叶斯回归补位:小样本和不确定性估计
贝叶斯回归把权重看成分布而不是固定值,输出的是预测分布,能给出不确定性。样本少、需要置信区间时它比普通线性回归更有优势。常见做法是用BayesianRidge,它通过证据最大化自动定正则强度,不用手动交叉验证。
from sklearn.linear_model import BayesianRidge br = BayesianRidge( n_iter=300, # 迭代次数 alpha_1=1e-6, # alpha 超参的 Gamma 先验 lambda_1=1e-6 # lambda 超参的 Gamma 先验 ) br.fit(X_train, y_train) y_mean, y_std = br.predict(X_test, return_std=True)return_std=True返回预测标准差,可以用来做区间估计。alpha_1、lambda_1是很小的先验参数,一般不用改。它适合特征维度不高、样本有限的回归场景,高维非线性任务还是交给树模型。
5. 避坑与排查:这几类算法最容易翻车的地方
5.1 现象:训练集 99%,测试集 60%,换模型也没用
原因:多半是数据泄漏或过拟合。数据泄漏常见于标准化、PCA、特征选择在划分训练测试之前就做了,导致测试集信息渗进训练。过拟合则是模型太复杂或样本太少。
解决:所有预处理(标准化、PCA、编码)必须放进 Pipeline,只在训练集上 fit,再 transform 测试集。用交叉验证而不是单次划分评估。树模型先降深度、加min_samples_leaf,XGBoost 加正则、降学习率。
5.2 现象:XGBoost 加了 early_stopping 反而报错或效果变差
原因:early_stopping_rounds需要配合eval_set,且评估集不能和训练集相同;另外早停后如果还用默认轮数预测,可能没用到最优轮数。
解决:fit时传eval_set=[(X_val, y_val)],预测时用clf.predict(X_test, iteration_range=(0, clf.best_iteration + 1))或直接依赖 sklearn 接口的自动处理。验证集要从训练集里再切一份,不能直接用测试集。
5.3 现象:PCA 降维后模型指标不升反降
原因:PCA 是无监督的,可能丢掉判别信息强但方差小的方向;或者没标准化导致主成分被量纲主导。
解决:先标准化再 PCA;对比降维前后指标;分类任务可改用 LDA 或直接用树模型做特征重要性筛选。保留方差比例从 0.95 起调,别一上来就压到 2 维。
5.4 现象:SVM 训练特别慢,或者结果全是同一类
原因:样本量大、gamma设得过大或过小、特征没标准化。gamma过大导致每个样本只影响自己,过小导致模型接近线性。
解决:先标准化;用GridSearchCV在 10 的幂次上搜C和gamma;样本超几万时换LinearSVC或树模型。类别不平衡时加class_weight="balanced"。
5.5 现象:随机森林特征重要性和业务认知完全对不上
原因:feature_importances_基于不纯度下降,偏向高基数、取值多的特征;相关特征之间会互相分摊重要性。
解决:改用permutation_importance在验证集上算,更贴近真实贡献;或者用 XGBoost 的增益重要性做交叉验证。相关特征先做聚类或业务合并,别只看一张重要性图就下结论。
6. 把这几类算法串成一条可复用的建模流水线
真正让这套东西产生价值的,不是单个算法写得多漂亮,而是把它们组织成一条能重复跑的流水线。我现在的习惯是:拿到结构化数据,先切训练测试,用 Pipeline 把标准化和 PCA 包进去,跑一版随机森林拿基线,同时看 permutation importance 筛特征;然后用 XGBoost 加 early stopping 冲精度,用 RandomizedSearchCV 在有限预算内调参;如果样本少、需要不确定性,再补一个 BayesianRidge 做对照;SVM 只在中小样本、维度适中、且树模型效果不理想时才上,并且一定先标准化再网格搜核参数。
验证方法上,我坚持三条:一是永远留一个没碰过的测试集做最终评估,二是分类看 f1 和 auc、回归看 rmse 和 mae,别只盯 accuracy,三是每次调参都记录参数和对应指标,不然调着调着就忘了哪组最好。下面这张表是我常用的选型对照,可以直接拿去用:
| 场景 | 首选 | 备选 | 关键参数 |
|---|---|---|---|
| 需要解释性、快速基线 | 决策树 | 随机森林 | max_depth, min_samples_leaf |
| 表格数据冲精度 | XGBoost | 随机森林 | learning_rate, max_depth, reg_lambda |
| 特征高维相关 | PCA + 树模型 | LDA | n_components |
| 中小样本、非线性边界 | SVM(rbf) | XGBoost | C, gamma |
| 小样本回归、要区间 | BayesianRidge | 线性回归 | alpha_1, lambda_1 |
最后说个我踩过的坑:早期我总想一步到位,把所有算法都跑一遍挑最好的,结果时间全花在调参上,业务方要的结论迟迟出不来。后来改成先随机森林出基线、再 XGBoost 冲一版、对比不过就停,效率高了很多。算法是工具,流水线和验证习惯才是能复用的东西。希望帮到你。
本文还有配套的精品资源,点击获取