跳转至

机器学习基础

一句话说明

机器学习是让计算机从数据中自动找规律、做预测的技术。在生信中,它是从高维组学数据(如菌群丰度表)中筛选生物标志物、做疾病分类预测的核心工具。


核心概念(白话版)

1. 什么是机器学习

  • 定义:机器学习(Machine Learning, ML)是人工智能的一个分支,核心思想是让计算机通过"看数据"自动学会规律,而不需要人类一条条写规则。
  • 白话比方:就像你学打篮球——不是靠看说明书,而是靠反复投篮、根据进没进来调整姿势。计算机也一样,喂它大量数据(投篮经验),它自己调整内部参数(姿势),最终学会预测(投进)。

三大类型

类型 英文 白话解释 生信例子
监督学习 Supervised Learning 有标准答案的考试——给你题目和答案,让你学会做新题 用已知健康/T2D标签的菌群数据训练分类器
无监督学习 Unsupervised Learning 没有标准答案——让你自己把一堆东西分分类 对样本做聚类看有没有自然分群
强化学习 Reinforcement Learning 打游戏学习——做对了奖励,做错了惩罚,慢慢变强 生信中很少用,主要用在药物设计等

2. 监督学习常用算法

随机森林(Random Forest)

  • 白话比方:你生病了去看诊,不是只找一个医生,而是同时找 500 个医生各自独立诊断,最后"投票表决"——多数医生说的结论就是最终结论。每个医生(决策树)可能有偏见,但 500 个人一起投票就很稳。
  • 原理
  • 决策树(Decision Tree):像一棵倒着的树,每个节点问一个问题(比如"Prevotella 丰度 > 0.05 吗?"),根据回答往左走或往右走,最终到达叶子节点得出结论。
  • 随机森林 = 很多棵决策树的集合
    • 每棵树只看"部分样本"(有放回随机抽样,叫 Bootstrap)
    • 每个节点只考虑"部分特征"(随机选一部分菌属来做分裂判断)
    • 最终预测 = 所有树投票取多数(分类)或取平均(回归)
  • 这种"随机 + 集成"的方式让模型既不容易过拟合,又能捕捉复杂关系
  • 生信应用
  • 疾病分类预测(如 T2D vs 健康人的菌群分类)
  • 特征重要性排序(找出哪些菌属对分类最有贡献 = 候选标志物)
  • 基因表达分类(肿瘤 vs 正常)
  • 微生物组关联分析
  • 优缺点
优点 缺点
不容易过拟合(相比单棵决策树) 模型是"黑盒",不如逻辑回归好解释因果
能处理高维数据(特征比样本多) 样本极少时效果可能一般
自带特征重要性排序 对极度不平衡数据需要额外处理
不要求数据标准化 训练时间比简单模型长
能处理非线性关系和高维特征交互 预测结果不容易做因果解释

注意:sklearn 的 RandomForestClassifier 不能直接处理缺失值(NaN 会报错),需要先做缺失值填充。R 的经典 randomForest 包同样不能处理 NA。能原生处理缺失值的是 R 的 ranger 包、XGBoost 和 LightGBM。

支持向量机(SVM, Support Vector Machine)

  • 白话比方:在两类数据点之间画一条"最宽的分界线"。想象桌上有红球和蓝球,你要放一根尺子把它们分开——SVM 就是找那个让两边球离尺子都最远的摆法。
  • 原理:找到一个超平面(hyperplane,你只需要理解为"把两堆数据分开的那条线/面"),使得两类样本到这个超平面的最小距离(margin,间隔)最大。用"核函数"(kernel)可以处理线性不可分的情况——线性不可分 = 画一条直线分不开。核函数相当于把数据"抛到高处",在高处就能用一个平面分开。
  • 生信应用:基因表达谱分类、蛋白质功能预测、DNA 甲基化分析
  • 优缺点
优点 缺点
高维数据表现好 样本量大时训练很慢
核函数灵活 参数调优复杂(C、gamma)
理论基础扎实 不直接给出概率,需要额外处理

逻辑回归(Logistic Regression)

  • 白话比方:用一个 S 形曲线把"得分"转换成"概率"。比如你考试得了 70 分,逻辑回归告诉你"及格概率 85%"。它本质上是在算各个特征对结果的"加权投票"。
  • 原理:对特征做线性组合得到一个分数,再用 sigmoid 函数(S 形曲线,不用记名字,只要知道它把任意数字"压"到 0-1 之间就行)把分数映射到 0-1 之间作为概率。如果概率 > 0.5 就预测为正类。
  • 生信应用:风险预测(如患病概率)、GWAS 中的关联分析、简单的二分类基线模型
  • 优缺点
优点 缺点
简单快速,容易解释(系数=贡献度) 只能学线性关系
直接输出概率 特征间有复杂交互时效果差
适合做基线模型对比 高维且特征相关时不稳定

3. 无监督学习常用算法

K-means 聚类

  • 白话比方:你要把一堆没有标签的球分成 K 组,先随便放 K 个"中心点",然后每个球跑向离自己最近的中心,之后把中心挪到每组球的正中间,反复几轮直到稳定。
  • 原理
  • 指定 K 个簇(cluster)的数量
  • 随机初始化 K 个质心(centroid)
  • 每个样本分配给最近的质心
  • 重新计算每个簇的质心
  • 重复 3-4 直到收敛
  • 生信应用:样本分群(如把患者分成不同亚型)、基因表达模式聚类
  • 优缺点
优点 缺点
简单直观,速度快 必须预先指定 K
适合大数据集 对初始质心敏感
结果容易解释 只能找"球形"簇,不能处理复杂形状

层次聚类(Hierarchical Clustering)

  • 白话比方:像画家谱——先把最相似的两个人合成一个小家庭,再把最相似的两个家庭合成一个大家族,一层层往上合并,最终画出一棵"族谱树"(dendrogram,树状图)。
  • 原理
  • 凝聚式(Agglomerative):自底向上,从每个样本单独一类开始,逐步合并最相似的两类
  • 分裂式(Divisive):自顶向下,把所有样本当一类,逐步分裂
  • 生信应用:样本聚类热图(heatmap)、物种进化关系推断、基因共表达模块发现
  • 优缺点
优点 缺点
不需要预先指定 K 计算量随样本数增加会急剧增大,样本多了会非常慢
能产生树状图,信息更丰富 大样本量时很慢
对簇形状没有限制 一旦合并/分裂不可撤销

4. 模型评估

交叉验证(Cross Validation, CV)

  • 白话解释:考试不能只考一次——你可能碰巧抽到会的题。交叉验证就是反复考多次,每次换一部分题当考卷,其他题拿来复习,最后取平均分才算你的真实水平。
  • 常用方式:K 折交叉验证(K-Fold CV)——把数据分成 K 份,轮流拿 1 份当测试、其余 K-1 份当训练,重复 K 次取平均。
  • 为什么要用:避免因为数据分割的偶然性(运气好/运气差)而高估或低估模型表现。样本量小的时候尤其重要。
  • 分层交叉验证(Stratified K-Fold):保证每一折中正负样本比例和总体一致。在类别不平衡时必须用。

过拟合与欠拟合

  • 过拟合(Overfitting)
  • 白话比方:考试前把答案背下来了,原题全会,但换个说法就不会了。模型"记住了"训练数据的噪声,对新数据预测很差。
  • 表现:训练集准确率 99%,测试集 60%。
  • 解决:增加数据量、减少模型复杂度、正则化、交叉验证、early stopping。

  • 欠拟合(Underfitting)

  • 白话比方:上课没听懂,考什么都不会。模型太简单,连训练数据的规律都没学到。
  • 表现:训练集和测试集准确率都很低。
  • 解决:用更复杂的模型、增加特征、减少正则化。

评估指标

指标 英文 白话解释 公式
准确率 Accuracy 判对的占总数的比例。"100个人判对了多少个" (TP+TN)/(TP+TN+FP+FN)
精确率 Precision 你说"有病"的人里面,真的有病的比例。"抓的人里有几个是真犯人" TP/(TP+FP)
召回率 Recall/Sensitivity 真正有病的人里面,你找出来了多少。"10个犯人你抓到了几个" TP/(TP+FN)
F1 分数 F1-Score 精确率和召回率的"调和平均"。两个都高 F1 才高 2PR/(P+R)
AUC-ROC AUC-ROC 不管阈值怎么设,模型区分正负样本的整体能力。0.5=瞎猜,1.0=完美 ROC曲线下面积
特异度 Specificity 真正健康的人中,你正确判为健康的比例 TN/(TN+FP)

注意:样本不平衡时不能只看 Accuracy!比如 100 人中 95 个健康、5 个有病,全猜"健康"准确率就 95% 但完全没用。此时应该看 AUC、F1、PR-AUC。

混淆矩阵(Confusion Matrix)

  • 白话解释:一个 2x2 的表格,记录模型把每类样本判对了多少、判错了多少。
                 预测为正(有病)   预测为负(健康)
实际为正(有病)      TP              FN
实际为负(健康)      FP              TN
  • TP(True Positive):有病,判对了——"真阳性"
  • FP(False Positive):没病,误判为有病——"假阳性"(误报)
  • FN(False Negative):有病,漏判了——"假阴性"(漏报)
  • TN(True Negative):没病,判对了——"真阴性"

5. 特征选择与特征工程

为什么要做特征选择

  • 高维数据(如 245 个菌属特征)中很多是噪声,会干扰模型
  • 减少特征可以降低过拟合风险
  • 让结果更可解释("这 10 个菌属最关键"比"这 245 个菌属一起用"更有意义)
  • 减少训练时间

常用方法

方法 白话解释 适用场景
随机森林特征重要性 看哪个特征被用得最多、对预测贡献最大 通用,非线性
Permutation Importance 随机打乱某个特征的值,看模型准确率掉多少——掉得越多说明这个特征越重要 更可靠,你的 T2D 项目就用了这个
LASSO——一种自动"砍"掉不重要特征的方法 自动把不重要的特征系数压到 0 线性模型
Prevalence 过滤 只保留在足够多样本中出现的特征(过滤掉太稀有的菌) 微生物组数据预处理
方差过滤 去掉所有样本中值都一样的特征(零方差=无信息) 通用预处理

6. 数据预处理

标准化(Standardization)vs 归一化(Normalization)

方法 操作 结果 适用算法
标准化 (x - 均值) / 标准差 均值=0, 标准差=1 SVM、逻辑回归、PCA
归一化 (x - min) / (max - min) 值映射到 [0,1] 神经网络、K-means

随机森林不需要标准化或归一化,因为它是基于"大小比较"做分裂的,值的缩放不影响排序。

缺失值处理

  • 直接删除(样本少时不推荐)
  • 均值/中位数填充
  • 在微生物组中:丰度为 0 通常表示"未检测到",不一定要当缺失值处理

类别不平衡问题

  • 问题:正负样本数量差很多(如 T2D 78 例 vs 健康 104 例)
  • 常用方法
  • class_weight='balanced':让模型对少数类给更高的关注
  • SMOTE:人工生成少数类的"合成样本"
  • 下采样:减少多数类样本
  • 评估时用 AUC / F1 / PR-AUC 而不是 Accuracy

常用工具/命令

工具/库 语言 用途 安装方式
scikit-learn Python 机器学习核心框架(分类、回归、聚类、评估) pip install scikit-learn
pandas Python 数据读取和处理 pip install pandas
numpy Python 数值计算 pip install numpy
matplotlib Python 画图(ROC、特征重要性等) pip install matplotlib
seaborn Python 更美观的统计图 pip install seaborn
XGBoost Python 梯度提升树,常作为 RF 的对比模型 pip install xgboost
imbalanced-learn Python 处理类别不平衡(SMOTE等) pip install imbalanced-learn
joblib Python 模型保存与加载 sklearn 自带

实操代码

随机森林分类完整示例

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
随机森林分类完整示例 —— 从数据加载到模型评估
使用 sklearn 内置的乳腺癌数据集(二分类,类似 T2D 项目场景)
"""

import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer  # 加载内置数据集(乳腺癌,569样本×30特征)
from sklearn.model_selection import StratifiedKFold, cross_val_predict  # 分层交叉验证
from sklearn.ensemble import RandomForestClassifier  # 随机森林分类器
from sklearn.metrics import (
    roc_auc_score,           # AUC 评估
    classification_report,   # 分类报告(精确率、召回率、F1)
    confusion_matrix,        # 混淆矩阵
)
import matplotlib.pyplot as plt  # 画图

# ========== 第1步:加载数据 ==========
data = load_breast_cancer()  # 加载数据
X = pd.DataFrame(data.data, columns=data.feature_names)  # 特征矩阵(569×30)
y = pd.Series(data.target, name="label")  # 标签(0=恶性, 1=良性)

print(f"样本数: {X.shape[0]}, 特征数: {X.shape[1]}")
print(f"标签分布:\n{y.value_counts()}")  # 查看正负样本比例

# ========== 第2步:定义模型和交叉验证策略 ==========
# 5折分层交叉验证——保证每折中正负样本比例一致
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 随机森林模型配置
rf_model = RandomForestClassifier(
    n_estimators=500,           # 500棵决策树
    max_depth=None,             # 树的深度不限制(让每棵树充分生长)
    min_samples_split=2,        # 节点最少2个样本才继续分裂
    max_features="sqrt",        # 每次分裂时随机选 sqrt(n_features) 个特征
    class_weight="balanced",    # 自动根据类别频率调整权重(处理不平衡)
    random_state=42,            # 随机种子,保证可复现
    n_jobs=-1,                  # 用所有CPU核心并行训练
)

# ========== 第3步:交叉验证获取 OOF 预测 ==========
# cross_val_predict: 每个样本只在作为测试集时被预测一次(Out-of-Fold)
oof_proba = cross_val_predict(
    rf_model, X, y,
    cv=cv,
    method="predict_proba",  # 获取概率预测
    n_jobs=-1,
)[:, 1]  # 取第二列,即正类的概率值(第一列是负类概率)

# ========== 第4步:模型评估 ==========
# AUC-ROC
oof_auc = roc_auc_score(y, oof_proba)
print(f"\nOOF AUC-ROC: {oof_auc:.4f}")

# 用 0.5 作为阈值转换为 0/1 预测
oof_pred = (oof_proba >= 0.5).astype(int)

# 分类报告
print("\n分类报告:")
print(classification_report(y, oof_pred, target_names=["恶性(0)", "良性(1)"]))

# 混淆矩阵
cm = confusion_matrix(y, oof_pred)
print(f"混淆矩阵:\n{cm}")
# 解读: 行=真实标签, 列=预测标签
# [[TN, FP], [FN, TP]]

交叉验证示例

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
交叉验证示例 —— 展示 5 折 CV 每折的性能指标
"""

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score
import numpy as np

# 加载数据
X, y = load_breast_cancer(return_X_y=True)

# 定义模型
rf = RandomForestClassifier(n_estimators=300, random_state=42, n_jobs=-1)

# 定义 5 折分层交叉验证
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 用 AUC 作为评分指标进行交叉验证
scores = cross_val_score(rf, X, y, cv=cv, scoring="roc_auc")

# 打印每折的 AUC 和汇总
print("各折 AUC:")
for i, score in enumerate(scores, 1):
    print(f"  Fold {i}: {score:.4f}")

print(f"\n平均 AUC: {scores.mean():.4f} (+/- {scores.std():.4f})")
# 白话解读: 如果 std > 0.05,说明模型稳定性不够,可能数据太少或模型过拟合
# 参考: std < 0.02 很稳定; 0.02-0.05 基本可以; > 0.05 需要关注

特征重要性可视化

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
特征重要性可视化 —— 使用 Permutation Importance
(你的 T2D 项目就用了这个方法来找候选核心菌属)
"""

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance  # permutation importance
from sklearn.model_selection import train_test_split

# ========== 加载并拆分数据 ==========
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = data.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y  # stratify 保证分层
)

# ========== 训练模型 ==========
rf = RandomForestClassifier(n_estimators=500, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)

# ========== 计算 Permutation Importance ==========
# 原理:随机打乱某个特征的值,看模型在测试集上的性能掉多少
# 掉得越多 = 这个特征越重要
perm_result = permutation_importance(
    rf, X_test, y_test,
    n_repeats=10,          # 每个特征重复打乱10次取平均
    random_state=42,
    scoring="roc_auc",     # 用 AUC 衡量性能变化
    n_jobs=-1,
)

# 整理为 DataFrame
importance_df = pd.DataFrame({
    "feature": X.columns,
    "importance_mean": perm_result.importances_mean,
    "importance_std": perm_result.importances_std,
}).sort_values("importance_mean", ascending=False)

# ========== 可视化 Top 15 ==========
top_n = 15
top_df = importance_df.head(top_n).iloc[::-1]  # 倒序方便画横向条形图

plt.figure(figsize=(10, 8))
plt.barh(range(top_n), top_df["importance_mean"], xerr=top_df["importance_std"])
plt.yticks(range(top_n), top_df["feature"])
plt.xlabel("Permutation Importance (AUC drop)")
plt.title(f"Top {top_n} Features by Permutation Importance")
plt.tight_layout()
plt.savefig("feature_importance.png", dpi=150)
plt.show()
print("Top 5 most important features:")
print(importance_df.head(5).to_string(index=False))

和你项目的关联

你的 T2D 项目具体做了什么

根据你的毕业论文项目(train_rf_prjna686835_thesis.py),整体流程如下:

  1. 数据来源:PRJNA686835 数据集,从 GMrepo 获取的公开肠道微生物组数据(属水平丰度矩阵)
  2. 任务:Healthy vs T2D 二分类(健康 104 例,T2D 78 例,共 182 个样本)
  3. 特征处理
  4. 原始 245 个属特征
  5. Prevalence 过滤(出现率 < 5% 的属被移除)
  6. 零方差过滤
  7. 无效特征移除(未知菌属)
  8. 最终保留约 134 个特征
  9. log1p 变换(对丰度取 log(1+x),压缩极端值)
  10. 模型:RandomForestClassifier
  11. 外层:5 折分层交叉验证(StratifiedKFold)
  12. 内层:3 折 + RandomizedSearchCV(12 次随机搜索调参)
  13. 参数搜索范围:n_estimators=[200,300,500,800]、max_depth=[None,5,8,12]等
  14. class_weight="balanced" / "balanced_subsample"(处理轻度不平衡)
  15. 特征选择:Permutation Importance(在测试折上计算,重复 10 次取平均)
  16. 评估指标
  17. OOF AUC = 0.5163(接近随机水平 0.5,说明区分能力有限)
  18. OOF AP = 0.4874
  19. 默认阈值 0.5 混淆矩阵:[[80,24],[55,23]]
  20. 结果解读
  21. 模型预测性能有限,接近随机水平
  22. 但 Permutation Importance 排序出了 Anaerobutyricum、Prevotella 等候选菌属
  23. 定位为"探索性分析",候选菌属作为后续验证的线索

关键数字(面试必记)

  • 样本量:182(Healthy 104 / T2D 78)
  • 原始特征:245 属 -> 过滤后 134 属
  • 交叉验证:5折外层 + 3折内层
  • OOF AUC:0.5163
  • 候选核心菌属:Anaerobutyricum、Evtepia、Prevotella、Dysosmobacter 等

面试怎么答

Q1: 解释一下随机森林的原理

A: "随机森林是一种集成学习方法。它的核心思想是'三个臭皮匠赛过诸葛亮'——训练很多棵决策树,每棵树只看部分样本和部分特征,最后投票表决。具体来说有两个随机:第一,每棵树用 Bootstrap 方式随机抽一部分样本来训练;第二,每个节点分裂时只随机选一部分特征来比较。这两个随机降低了模型方差,让整体不容易过拟合。最终预测就是所有树投票取多数。在我的项目中用了 200-800 棵树,用 StratifiedKFold 来评估。"

Q2: 什么是过拟合?怎么避免?

A: "过拟合就是模型把训练数据的噪声也'背'下来了,在训练集上表现好但在新数据上表现差。就像考试背答案,原题全会但换个说法就不行了。避免过拟合常用的方法有:一、交叉验证——让模型在不同数据划分上都表现好才算好;二、控制模型复杂度——比如限制树的深度、增加最小叶节点样本数;三、集成方法——随机森林本身就是通过多棵树平均来降低方差;四、正则化——比如 LASSO 对特征系数做惩罚。我的项目用了嵌套交叉验证:外层 5 折评估真实性能,内层 3 折做参数调优,严格防止信息泄漏。"

Q3: 你的项目中为什么选择随机森林?

A: "选随机森林有几个原因:第一,我的数据是属水平丰度表,特征维度高(245个属)、样本量相对小(182例),随机森林在这种高维小样本场景下比较稳健,不容易过拟合;第二,随机森林自带特征重要性排序,我可以直接用 Permutation Importance 找出对分类贡献最大的菌属作为候选标志物;第三,它对数据预处理要求低,不需要标准化,对缺失值也有一定容忍度;第四,作为本科论文,方法不宜过于复杂,随机森林是成熟、被广泛认可的方法。当然,最终 AUC 只有 0.52 左右,说明属水平数据在这个数据集上区分能力有限,这也是诚实的结论。面试时不要回避数字,但要紧跟解释原因和你学到了什么。"

Q4: 交叉验证是什么?为什么要用?

A: "交叉验证是把数据分成 K 份,轮流拿 1 份做测试、K-1 份做训练,重复 K 次后取平均作为模型评估结果。为什么要用?因为样本量有限的时候,如果只做一次 train/test split,结果受数据分割的偶然性影响很大——可能碰巧分到'简单'的测试集就高估了模型。交叉验证相当于考多次试取平均,结果更可靠。另外在我的项目中用了'分层'交叉验证,保证每折里 T2D 和健康人的比例和总体一致,避免某一折正好没有 T2D 样本的极端情况。"

Q5: AUC-ROC 曲线怎么解读?

A: "ROC 曲线的横轴是假阳性率(FPR),纵轴是真阳性率(TPR),它展示了在不同分类阈值下模型的表现。AUC 就是这条曲线下面的面积。AUC=0.5 表示模型和随机猜一样没用;AUC=1.0 表示完美分类。一般 0.7-0.8 算可以接受,0.8-0.9 算比较好。我的项目 OOF AUC = 0.52,说明模型整体区分能力接近随机水平。但这并不意味着没有价值——通过 Permutation Importance 我们仍然能找到一些有相对区分力的候选菌属,作为后续深入研究的线索。"

Q6: 你的模型效果怎么样?怎么评估的?

A: "说实话,模型效果有限。OOF AUC 约 0.52,接近随机水平。评估方式是 5 折分层交叉验证 + 内层 3 折参数搜索的嵌套设计,确保评估结果不受调参过拟合的影响。除了 AUC,我还看了 Average Precision(0.49)、不同阈值下的混淆矩阵、F1 分数和平衡准确率。效果不好的原因可能有:一、属水平丰度数据本身分辨率不够,种水平或功能层面可能更有信息量;二、样本量只有 182 例,统计力不足;三、T2D 是复杂疾病,单纯菌群丰度可能不够。但作为探索性分析,能找到候选菌属线索就是有价值的贡献。"


延伸阅读

  1. scikit-learn 官方文档 - Ensemble Methods:https://scikit-learn.org/stable/modules/ensemble.html
  2. 最权威的随机森林和梯度提升实现文档,含代码示例
  3. StatQuest with Josh Starmer (YouTube):搜索 "StatQuest Random Forest"
  4. 用动画讲解 ML 概念,非常适合入门,有英文字幕
  5. 《Pattern Recognition and Machine Learning》Bishop (2006)
  6. 机器学习经典教材,适合想深入理论的同学(有中文翻译版)