数据预处理:标准化与归一化
拿到单细胞数据,第一件事不是急着跑Transformer。你得先把数据收拾干净。为什么?因为原始counts矩阵里,每个基因的表达量尺度差得离谱——有的基因就只有几个拷贝,有的动不动上千。Transformer内部的注意力机制对输入尺度非常敏感,你不做归一化,注意力权重就会偏向高表达基因,低表达基因直接被忽略。
说白了,标准化和归一化就是把不同尺度的数据拉到同一个量级上,但又保持生物学差异。我刚开始做单细胞分析时,跳过这步直接喂给模型,结果训练loss死活不降,折腾了两天才发现是预处理埋的坑。
为什么不能直接用原始counts
你可以想象一下:两个基因,A表达量0~100,B表达量0~10000。Transformer里的LayerNorm虽然能缓解部分问题,但如果原始尺度差异太大,embedding层直接就崩了。
具体来说有三个痛点:
- 尺度倾斜 — 高表达基因主导注意力权重
- 梯度不稳定 — 大尺度特征使反向传播震荡
- 收敛慢 — 模型要花大量epoch适应不同量级
我的经验:如果你用原始TPM(每百万转录本)数据跑Transformer,训练初期loss会掉得很快,但validation loss死活下不来。标准化之后,valid和train的曲线就基本同步了。
标准化(Z-score)
标准化,也叫Z-score归一化,公式很简单:
x' = (x - μ) / σ
其中μ是均值,σ是标准差。处理后数据均值为0,标准差为1。
适合什么场景?数据本身接近正态分布,或者你要保留异常值信息——因为Z-score不会把数据强制压到固定区间,离群点依然显眼。
import numpy as np
from sklearn.preprocessing import StandardScaler
# 假设data是细胞×基因矩阵
scaler = StandardScaler()
data_standardized = scaler.fit_transform(data)
print("均值(近似0):", data_standardized.mean(axis=0)[:5])
print("标准差(近似1):", data_standardized.std(axis=0)[:5])
注意:对单细胞数据做标准化时,建议先做log1p变换(ln(count+1)),再把标准化放在后面。我曾经顺序搞反,结果负值太多,影响下游聚类。
归一化(Min‑Max)
Min‑Max直接把数据压缩到[0, 1]区间:
x' = (x - min) / (max - min)
适合场景?数据有明确边界,或者要用ReLU这类非负激活函数。不过对单细胞来说,Min‑Max会丢失表达量的绝对差异——高表达基因和低表达基因被拉平了。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
data_normalized = scaler.fit_transform(data)
print("最小值:", data_normalized.min())
print("最大值:", data_normalized.max())
对比一下:标准化保留相对差异,归一化消除量纲但损失绝对值。在Transformer矫正批次效应中,我个人更倾向标准化,因为注意力机制需要保留不同基因间的相对关系。
还有几种实用方法
- RobustScaler — 用中位数和IQR,抗离群点。数据有极端值(比如测序深度差异)时很好使。
- 对数变换(log1p) — 单独的log1p不是标准意义上的归一化,但常作为前处理步骤,让数据更接近正态分布。
- 按库大小缩放(Size‑factor) — 单细胞常见的CPM(每百万计数)或TPM,本质上也是一种归一化。
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler(quantile_range=(25, 75))
data_robust = scaler.fit_transform(data)
# 适合有离群值的单细胞counts
什么时候选哪种?一张表说清楚
| 方法 | 输出范围 | 保留异常值 | 单细胞推荐场景 |
|---|---|---|---|
| Z‑score标准化 | 无固定范围,μ=0, σ=1 | 是 | log1p后的基因表达,适合Transformer |
| Min‑Max归一化 | [0, 1] | 否(压缩到区间) | 用ReLU激活时,或可视化 |
| RobustScaler | 无固定范围,中位数=0 | 强抗离群 | 原始counts有大量零膨胀和极高值 |
| 对数变换(log1p) | 压缩右偏分布 | 部分保留 | 标准化前的必备步骤 |
曾经踩过的坑:我试过在多个批次上分别做标准化,再合并。结果每个批次均值都为0,但批次间的偏移其实被隐藏了——Transformer学不到批次差异,矫正效果反而变差。正确做法应该是先合并所有批次,再对每个基因做全局标准化,或者用批次平衡的标准化策略。
总结一下:标准化和归一化不是二选一,而是组合使用。对于Transformer矫正批次效应,我个人推荐流程是 log1p → Z-score标准化 → 检查各批次均值是否接近0。如果均值偏差超过0.1,说明批次效应依然存在,需要回到预处理环节调整。
小技巧:标准化后用sklearn的StandardScaler,它的mean_和scale_属性可以直接保存下来,供后续新数据使用。这样在推理阶段,新来的细胞也能用相同的参数归一化。