跳到主要内容

04 经典分类(LR / SVM / RF / XGBoost)

二分类是 ML 入门最直观的任务。这一章用 02 + 03 章产物训 4 种主流模型,对比 ROC / 混淆矩阵 / 特征重要性,把 caret 当统一接口让 4 算法在同一份数据上公平赛跑。下一章(05)讲完整评估,本章只关心"训出来"。

4 种模型偏好对比

模型数学本质偏好场景弱项
Logistic Regression线性 + sigmoid可解释性强(系数直接读)、特征数 < 样本数非线性关系处理弱;高维(> 样本)需正则
SVM(RBF kernel)核技巧投到高维做线性分割中小数据(< 5000 样本)、非线性边界、特征数适中不输出概率(需 Platt scaling)、解释性弱
Random Forestbagging 集成多棵决策树默认起点、特征自动选择、对噪声稳健比 XGBoost 慢些、外推能力一般
XGBoostgradient boostingKaggle / 比赛 SOTA、稀疏特征友好调参敏感、过拟合风险高于 RF

实务建议:新数据先 RF(几乎不用调参,baseline 立稳),性能不够再上 XGBoost;LR 永远要训一次作为"线性 baseline 照妖镜"——如果非线性模型只比 LR 好 1-2%,那这个数据本质是线性问题,直接用 LR 解释性更强。

用 caret 统一接口

R 里训分类器最坑的不是算法,是每个算法的 API 不一样。glmnet 用 cv.glmnet(),e1071 SVM 用 tune.svm(),randomForest 用 randomForest(),xgboost 用 xgb.train(),4 套接口学完天都黑了。

caret 把这一切包到一个统一接口:

library(caret)

ctrl <- trainControl(
method = "cv", number = 5, # 5-fold CV
classProbs = TRUE, # 输出概率(才能算 ROC)
summaryFunction = twoClassSummary, # 用 ROC 选 best
savePredictions = "final"
)

# Logistic Regression(glmnet,带 L1+L2 正则)
fit_lr <- train(x = X_train, y = y_train, method = "glmnet",
trControl = ctrl, metric = "ROC")

# SVM RBF
fit_svm <- train(x = X_train, y = y_train, method = "svmRadial",
trControl = ctrl, metric = "ROC")

# Random Forest
fit_rf <- train(x = X_train, y = y_train, method = "rf",
trControl = ctrl, metric = "ROC", ntree = 500)

# XGBoost
fit_xgb <- train(x = X_train, y = y_train, method = "xgbTree",
trControl = ctrl, metric = "ROC", verbose = 0)

四段几乎一模一样,只换 method。这就是 caret 的核心价值。

在 TCGA-LIHC 上跑 4 算法

配套脚本(W4 在产):

Rscript scripts/machine-learning/ml04_classify_sci.R

输入:02 章 derived 的 expression_hvg5000.csv + labels_binary.csv + splits.json(stratified train/val/test)

输出 ml04_figs/:

  • ROC 重叠图(4 算法)
  • PR 重叠图
  • 混淆矩阵 panel
  • 特征重要性对比
  • 学习曲线(数据量 vs 性能)

在线一键复现

不想本地跑?把数据上传到 ml-classifier 工具:

  • features.csv:行=特征,列=样本
  • labels.csv:两列 sample / label

工具自动跑 stratified split + 4 算法 + 完整评估,输出 8 张 SCI 双格式图 + 训好的 .rds(下游可喂 shap-explain)。

ml-classifier 公开 demo

公开 demo:200 features × 150 samples,4 算法 AUC=1.000 (二分类信号超强,Tumor vs Normal 在 LIHC 是简单任务)

进阶 — 多分类(TCGA-BRCA PAM50)

PAM50 是 5 类(LumA / LumB / Her2 / Basal / Normal-like),twoClassSummary 用不了。caret 的处理:

ctrl_multi <- trainControl(
method = "cv", number = 5,
classProbs = TRUE,
summaryFunction = multiClassSummary, # 5 类用这个
savePredictions = "final"
)
fit_rf_multi <- train(x = X_train, y = y_train_pam50, method = "rf",
trControl = ctrl_multi, metric = "Mean_Balanced_Accuracy")

多分类评估关键变化:

  • macro-averaged AUC(每类 vs rest 算 AUC,然后平均)
  • per-class precision/recall/F1(避免被多数类淹没)
  • 混淆矩阵的 5×5(不再是 2×2)

⚠️ Accuracy 在多分类不平衡时极易误导:PAM50 里 Normal-like 占 5%,模型全猜其他 4 类,Accuracy 仍 95%。永远报 macro-F1 / Cohen's κ

模型选择 — 何时用哪个

经验法则:

样本量 < 100: LR(防过拟合)
样本量 100-1000 + 中维: RF baseline → XGBoost 提性能
样本量 > 1000 + 高维: XGBoost / LR with elastic net
样本量任意 + 强解释性需求: LR(系数直读) + SHAP
样本量任意 + 非线性强: RF / XGBoost / SVM(三选一)
基因数 >> 样本数 (生信常态): LR with L1/L2(高维稳)

生信场景大多是 基因数 > 样本数(p >> n),LR + 正则化是兜底选择,RF / XGBoost 提性能但要小心过拟合。

常见坑

训练集 ROC = 0.99 当作好结果 — 几乎肯定过拟合。看 5-fold CV 的平均 AUC,看 test 集 AUC,这两个才有信息量。

没有 set.seed — 同一份数据跑两次结果完全不同?忘了设种子。caret 的 train() 内部抽样,种子要在 train() 之前 set.seed(42)

特征数 > 样本数还用默认 RF — RF 在 p >> n 上会"假装好"。要么先用 03 章特征选择压维,要么用 LR + L1。

类不平衡时只看 Accuracy — 90:10 不平衡,全猜多数类 Acc = 90% 但完全没用。看 PR-AUC 或 Cohen's κ。

caret tuneLength = 1 跑 4 算法做横评 — 这是 anti-pattern。每个算法有不同的关键超参,起码 tuneLength = 5 或自定义 tuneGrid。横评的前提是每个算法都"调到自己最好"。

直接 predict() 不传 type="prob" — 默认返回类标签,算 ROC 必须用 predict(fit, type = "prob"),然后用 pROC::roc() 算曲线。

在 train 上做 PCA / 标准化,test 上重新做 — 特征预处理必须在 train 上 fit,test 上 transform。caret 的 preProc 参数在 fold 内自动做对,但手写流程要小心。

在线工具

ml-classifier

在线跑 ml-classifier — 4 算法一键对比 + ROC + Calibration + DCA + Bootstrap CI。

下一章 05 章 完整讲清楚 ROC / PR / Calibration / DCA / Bootstrap CI 这些评估指标。

本章状态

✅ Wave 4 正文完成(2026-05-27)。配套 ml04_classify_sci.R 在产,工具已上线 https://biof3.com/tools/#ml-classifier。

AI 陪学

让 AI 陪我学这一篇

AI 会读这篇文章后给你 3-5 步学习计划, 逐步陪你学完,最后出 1-3 道题验证你掌握得怎么样。 登录后 AI 才能记住你的进度。

静态文件

离线资料下载

手册 HTML / PDF 已在后台预生成,点击后直接下载网站静态资源。