RNA‑seq 表达数据与机器学习概述
做 RNA‑seq 数据分析的人,迟早会遇到“特征太多、样本太少”这件事。几年前我第一次拿到转录组数据,心想不就是个基因表达矩阵嘛,扔进分类器不就好了?结果模型全在过拟合,验证集的 AUC 只有 0.52,跟抛硬币差不多。后来才意识到,RNA‑seq 数据的结构特点,决定了它的机器学习套路和图像、文本完全不同。
这一章我们先捋清楚:RNA‑seq 数据长什么样、它能解决哪些生物问题、以及常见的陷阱在哪里。你不需要一下子记住所有细节,但得对这个基本面有个直觉。
RNA‑seq 数据的 DNA(数据特性)
一个标准的 RNA‑seq 表达矩阵,行是基因(通常 2 万~6 万个),列是样本(几十到几百个)。你想想看,这就是典型的“高维小样本”场景。再加上基因表达值往往有大量零值、批间效应、测序深度差异……说白了,就是一个噪声大、分布偏、稀疏度高的矩阵。
关键数字一览:
| 特性 | 典型值范围 | 对机器学习的影响 |
|---|---|---|
| 特征数(基因) | 20,000 – 60,000 | 极易过拟合,需要强正则化或特征选择 |
| 样本数 | 50 – 2,000 | 样本稀少,交叉验证稳定性要求高 |
| 零值比例 | 20% – 70% | 稀疏矩阵,部分模型(如 GBDT)效率下降 |
| 动态范围 | 0 – 10⁶(CPM) | 需要对数变换或归一化 |
| 批次效应 | 几乎必然存在 | 需做 ComBat / limma 校正,否则模型学的是批次 |
我个人的习惯是,拿到数据后先做三步检查:
- 看表达值的分布(直方图),有没有严重的长尾。
- 检查样本的聚类情况(PCA 或 UMAP),如果不同批次完全分开,就得先校正。
- 统计每个基因的表达中位数,去掉表达量一直趋近于零的基因。
这三步走完,数据才算“可以用”。
三个常见应用场景
1. 分类:识别疾病亚型或组织类型
最经典的任务——拿 RNA‑seq 表达谱判断样本是肿瘤还是正常、是基底型还是 luminal 型。这里模型输出是离散类别。我记得有一次用公开的 TCGA 数据做乳腺癌亚型分类,特征有 2.5 万个基因,我直接扔给随机森林,训练集准确率 99%,测试集直接掉到 65%。为什么?因为没做特征选择,模型记住了几十个基因的噪音。后来改成先选出前 1000 个差异基因,再训练,测试集准确率升到 88%。所以,分类任务的瓶颈不是算法,而是特征工程。
2. 生存分析:预测患者生存时间或风险
生存分析用的不是普通回归,而是 Cox 比例风险模型或者它的深度变体(DeepSurv、Cox‑NNet)。输入是基因表达,输出是风险评分。这里有个坑:Cox 模型本身不能处理高维特征,必须配合 L1/L2 正则化(Lasso‑Cox)。我项目中遇到过一个泛癌生存预测的数据,直接用全基因做 Cox 回归,结果 90% 的系数都是零——Lasso 自己帮你选了不到 100 个关键基因。嗯,这说明基因表达量的预测信号其实是稀疏的。
# R 示例:用 glmnet 构建 Lasso‑Cox 模型
library(glmnet)
cv_fit <- cv.glmnet(x = expr_matrix, y = Surv(time, status), family = "cox")
plot(cv_fit)
coef(cv_fit, s = "lambda.min") # 提取非零系数基因
3. 聚类:挖掘新的分子亚群
当没有标签时,用无监督聚类(k‑means、层次聚类、或非负矩阵分解 NMF)发现表达模式相似的样本组。RNA‑seq 聚类最大的陷阱是维度灾难——在高维空间中,欧式距离会失效。我建议先用 PCA 或 t‑SNE 降到 30~50 维再做聚类,否则聚类结果几乎等于随机。另外,聚类数的确定可以用轮廓系数或者 consensus clustering。
机器学习处理 RNA‑seq 的核心流程
下面的图把整个过程串起来了。你可以把它想象成数据从“原始计数”到“业务结论”的管道。
一个快速起步的代码框架
不管后面用什么模型,数据预处理的骨架都差不多。下面是一个极简的 Python 流程,加载 TPM 矩阵并做初步特征筛选:
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import VarianceThreshold
# 加载表达矩阵 (row=基因, col=样本)
expr = pd.read_csv("expression_tpm.csv", index_col=0)
# 1. 去除低表达基因 (至少 20% 样本中表达 > 1)
mask = (expr > 1).sum(axis=1) / expr.shape[1] > 0.2
expr = expr.loc[mask]
# 2. 对数变换 (log2(TPM+1))
expr_log = np.log2(expr + 1)
# 3. 过滤低方差特征 (防止全是常数)
selector = VarianceThreshold(threshold=0.1)
expr_filt = selector.fit_transform(expr_log.T) # 转置为样本×特征
print(f"基因数从 {len(expr)} 减少到 {expr_filt.shape[1]}")
小结
RNA‑seq 表达数据的特点是“胖而杂”,但机器学习在很多场景下仍然很实用。这一章我们覆盖了三个主要应用(分类、生存分析、聚类),也提到了批处理、特征选择、高维稀疏性这些实际中绕不开的问题。后面的章节我们会深入每一个超参数(惩罚系数、树深度、学习率……),一步步看它们怎么影响模型表现。
嗯,先消化这些基础。接下来第二章,我们会拿一个真实的 TCGA 乳腺癌表达数据集,从头搭建第一个分类基线模型。