ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

单细胞数据降维可视化:t-SNE、UMAP与自编码器全解析

单细胞数据降维可视化:t-SNE、UMAP与自编码器全解析 先说我自己的判断做单细胞转录组数据分析真正决定你图好不好看的不是你用的是 t-SNE 还是 UMAP而是数据预处理和参数调得对不对。但怎么调又不完全能脱离方法本身说清楚。所以这篇把单细胞数据降维与可视化里最常用的三种思路——t-SNE、UMAP 和自编码器——放在一起从原理、代码到坑系统对照一遍。如果你手里正有一批单细胞表达矩阵想画出一张能说服自己的分群图也想知道自编码器这种“神经网络降维”到底有没有必要这篇文章会比较对胃口。我们处理的很多问题其实都发生在“高维表达矩阵”到“二维散点图”的压缩过程中。t-SNE 是过去十来年最主流的细胞群可视化工具UMAP 近几年强势上场自编码器则代表另一条更“重”但更灵活的路线。它们的目的从表面上看似相同内部逻辑差别很大。先把这个底层差异讲透再上真实代码后面你会更容易判断“为什么同样的数据三种方法画出来不一样”。1. 先说清楚单细胞数据为什么非降维不可1.1 表达矩阵的真实面貌单细胞转录组测序拿到手之后最常见的形式是一个稀疏矩阵行是细胞列是基因。人类基因组注释到的蛋白编码基因差不多两万个你测一个 10x 样本少则几千个细胞多则几万甚至几十万。每个细胞里真正能够稳定检测到的基因一般也就几千个所以这个矩阵里大量位置是 0。即使做了标准化和 log 变换数据的有效维度依然很高直接在所有基因上算距离或跑聚类不仅慢还容易被无关基因和噪音冲淡信号。很多刚接触单细胞数据的朋友会有一个问题表达矩阵都已经能用 PCA 了为什么还要 t-SNE、UMAP 甚至自编码器答案是 PCA 是线性降维它假设数据的主要变化方向能用“基因的线性组合”概括。单细胞数据里存在大量 dropout 现象一个基因在某个细胞里没测到不代表它真没表达生物学上还普遍存在不同细胞状态连续变化、发育轨迹非线性延伸的情况。单靠 PCA经常会把本来连续的细胞群切得很别扭或者把少数类群压缩进一片看不出结构的云里。1.2 降维的目的不只是画图好看我们在 scRNA-seq 分析中反复降维核心目的有四个一是去掉技术噪音让有效生物学信号凸显出来二是把数据压缩到能承载“近邻关系”的维度再跑 KNN 图聚类三是做二维/三维投影方便肉眼观察是否存在若干候选细胞群四是给轨迹推断、差异表达等下游任务提供一个更稳定的数据入口。这里必须强调聚类和可视化是两个动作。现在的标准流程通常是先做 PCA用前十几或几十个主成分构建 KNN 图然后在图上做 Leiden/RNA 聚类最后把聚类结果放在 t-SNE 或 UMAP 的二维坐标上展示。所以 t-SNE 和 UMAP 更多是“展示层”它们让细胞按聚类关系在低维空间里形成一团一团的视觉结构而自编码器则可以承担“特征提取层”它学习的是一个把高维基因表达压缩到低维瓶颈向量的非线性映射压缩得到的特征还可以继续走 KNN 图聚类或 UMAP 可视化。1.3 线性、非线性、神经网络降维的选择PCA 属于线性方法t-SNE 和 UMAP 属于非线性流形学习方法自编码器则是用神经网络逼近非线性映射本质上也是一种非线性降维。打个不一定严谨但好懂的比方PCA 像从高处俯视一座山把三维景观直接压成一张二维平面图方向是固定的t-SNE/UMAP 更像在山上架了许多悬浮索道让相邻的点更愿意靠近自己附近的点自编码器则像一个勤快的测绘员先学会把整座山浓缩成一张草图再根据草图把山还原出来。理解这个比喻之后接下来我们看每种方法的具体思路。2. 三种主力降维方法各自的“脑回路”2.1 t-SNE维护邻居之间的概率关系t-SNE 的全称是 t-distributed Stochastic Neighbor Embedding。它的基本想法是在高维空间中把“细胞 A 和细胞 B 是不是邻居”转化成一个概率值然后在低维空间中也想办法给每个细胞一套二维坐标让低维空间里的邻居概率尽量还原高维空间里的邻居概率。它用对称化的条件概率定义高维相似度计算时会根据 perplexity 参数给每个细胞自动分配一个方差。低维空间则使用 t 分布而不是正态分布t 分布有更厚的尾巴允许低维空间中相对较远的点仍然有可忍受的概率值这样能缓解所谓的“拥挤问题”——也就是高维空间里大量彼此距离差不多的点投影到二维后会被挤成一堆。t-SNE 的代价函数采用了 KL 散度。这个方法有个很著名的特点它对“本来应该靠近却没靠近”的点惩罚很大对“本来不近却被分到了一起”的点惩罚相对较小。换句话说t-SNE 非常愿意保留局部近邻结构但全局结构不一定可靠。所以你在 t-SNE 图上看到的“聚成一团”的细胞群往往比较紧但不同群之间的距离远近并不能严谨说明它们之间的相似程度。实践里还要注意t-SNE 每次跑都可能得到不同的全局布局直接在高维稀疏表达矩阵上跑 t-SNE 很慢而且不稳定。我们一般会先用 PCA 把数据压缩到 15~50 维再在这个结果上跑 t-SNE。还有一点t-SNE 的目标是最小化二维布局与原始近邻关系的差异因此基本上只为可视化服务很少有人直接拿 t-SNE 坐标做下游定量分析。2.2 UMAP在流形上构建模糊图UMAP 全称是 Uniform Manifold Approximation and Projection它名字里自带“流形”这个词。流形这个概念听起来玄但可以理解为高维数据虽然在两万个基因空间里分布但真正有意义的点很可能分布在一个较低维度的连续“曲面”附近。UMAP 假设数据服从流形分布并且大致均匀只是当前观测的采样密度不均匀。具体做法上UMAP 会先在原始高维空间里为每个点找 k 个邻居用局部距离为每个点确定一个特征尺度并据此构造一个加权的 k 近邻图。然后在低维空间里它希望低维图与高维图尽量相似优化目标是交叉熵。这个设计中局部邻居关系和全局结构都被纳入了目标函数因此 UMAP 比 t-SNE 更容易保留一些广域结构比如不同细胞群之间的相对位置。实际体验上UMAP 有一个非常明显的好处速度快、内存友好。同样几万个细胞UMAP 比 t-SNE 快很多这也是它逐渐取代 t-SNE 成为单细胞主图的重要原因。但速度与稳定不是免费的UMAP 对邻居数、最小距离等参数更敏感不同的参数组合会把同一份数据切成完全不同的“视觉故事”。后面我会专门讲参数调整。2.3 自编码器先压缩再重建自编码器是一种神经网络结构由编码器 Encoder 和解码器 Decoder 两部分组成。输入是高维表达向量编码器把它压成一个低维向量也就是瓶颈特征解码器从瓶颈特征尝试还原原始输入。如果输入是两组数量级不同的特征比如基因表达量还带着批次、测序深度等变量那常见做法是先做标准化再进入网络。模型训练的目标是让输出接近输入理想情况下瓶颈层就保存了能够重建整个表达谱的关键信息。单细胞领域会用到的自编码器变体不少。最基础的是全连接自编码器。因为基因表达矩阵非常稀疏大量基因在绝大多数细胞中不表达普通全连接网络容易过度关注那些“高表达驱动重建”的基因忽略与细胞身份密切相关的低表达 marker因此我们通常会给瓶颈层或隐藏层加稀疏约束形成稀疏自编码器。稀疏性本质上是让大部分神经元对大多数样本保持抑制只对一小部分激活这能避免网络把表达矩阵中的 dropout 噪音当成重要结构记住。如果你处理的对象不是表达矩阵而是单细胞图像比如高内涵筛选、空间组学里的局部图像块那么卷积自编码器会更合适时间序列或体积数据则可能用到 3D 卷积自编码器。这篇文章我们主要讨论普通 scRNA-seq 表达矩阵所以后续代码以全连接稀疏自编码器为主。自编码器还有一类带条件输入的变体像 scVI 那样把样本来源、批次信息作为额外变量输入可以实现在压缩特征里把批次效应解释掉这是普通 t-SNE 和 UMAP 做不到的。2.4 核心差异一张表看完对照维度t-SNEUMAP自编码器基本原理高维/低维邻居概率对齐高维/低维模糊图逼近编码后解码重建是否线性非线性非线性非线性取决于网络全局结构保真度较弱偏局部相对较好取决于瓶颈维度和训练输出维度通常 2D/3D任意维度瓶颈层任意维度是否天然适合流形是但偏向局部流形是兼顾全局是但需要调网络大批量计算速度慢快有 GPU 时快可解释性低低中等可看瓶颈基因权重典型用途可视化可视化、中间降维特征提取、去批次、建模随机性较高较低但存在较高取决于随机种子这张表的意思很清楚t-SNE 适合“看局部结构”UMAP 适合“快速看整体并做展示”自编码器则更多用于“为下游模型生成低维特征”。它们不完全是替代关系经常是先后配合的关系。3. 在同一套 PBMC 数据上的实战流程3.1 数据预处理与 PCA 基线构建纸上谈兵没有用我们用一套公开的 PBMC 单细胞数据来跑一遍。数据量不大适合演示。实际项目中的分析步骤会更多但核心流程是一样的。首先做好预处理。这个阶段不能省具体顺序通常是细胞质控过滤、基因过滤、库大小归一化、log 变换、高变基因筛选、标准化、PCA。import scanpy as sc import numpy as np import torch import torch.nn as nn # 加载示例数据PBMC 3k adata sc.datasets.pbmc3k() adata.var_names_make_unique() # 基本质控每个细胞至少检测到 200 个基因每个基因至少在 3 个细胞中出现 sc.pp.filter_cells(adata, min_genes200) sc.pp.filter_genes(adata, min_cells3) # 线粒体高比例通常提示细胞状态不佳 adata.var[mt] adata.var_names.str.startswith(MT-) sc.pp.calculate_qc_metrics(adata, qc_vars[mt], percent_topNone, log1pFalse, inplaceTrue) adata adata[adata.obs.pct_counts_mt 20, :].copy() # 库大小归一化到 10000再做 log1p 变换 sc.pp.normalize_total(adata, target_sum1e4) sc.pp.log1p(adata) # 这里把全基因表达备份到 raw后续给 marker 基因上色用 adata.raw adata # 挑选高变基因 sc.pp.highly_variable_genes(adata, n_top_genes2000) adata adata[:, adata.var.highly_variable].copy() # 标准化缩放每个基因的表达量 sc.pp.scale(adata, max_value10) # PCA sc.tl.pca(adata, svd_solverarpack, n_comps50) sc.pl.pca_variance_ratio(adata, n_pcs50)处理完后可以用pca_variance_ratio画碎石图观察前多少个主成分贡献了主要方差。PBMC 3k 这种数据通常前 10~20 个 PC 已经够用。我们这里选前 15 个 PC 作为后续邻居图的输入。需要提醒一点adata.raw adata这句是在高变基因筛选之前做的这样后面无论从高变基因子集还是从全基因表达中取 marker 基因Scanpy 都能快速找到。预处理阶段的“标准化”尤其关键漏掉 scalePCA 和后续距离计算会被高表达基因主导。3.2 用 Scanpy 一次跑出 t-SNE 与 UMAP预处理完成后先构建邻居图然后跑聚类。这一步决定你后面的图上颜色怎么划分。# 邻域图在 PCA 前 15 维上找邻居 sc.pp.neighbors(adata, n_neighbors15, n_pcs15) # Leiden 聚类 sc.tl.leiden(adata, resolution0.5, key_addedleiden_0.5) # UMAP 可视化 sc.tl.umap(adata, min_dist0.5, random_state42) sc.pl.umap(adata, color[leiden_0.5, CD3D, MS4A1, LYZ], frameonFalse) # t-SNE 可视化 sc.tl.tsne(adata, n_pcs15, perplexity30, random_state42) sc.pl.tsne(adata, color[leiden_0.5, CD3D, MS4A1, LYZ], frameonFalse)你自己跑时会发现UMAP 跑的很快几分钟内能出结果t-SNE 稍慢但 3k 细胞也不会太离谱。UMAP 的图通常各个细胞群之间有清晰的“通路”或“隔断”t-SNE 的团块更紧凑局部边界更清晰。加 marker 基因上色非常关键。CD3D是 T 细胞 markerMS4A1是 B 细胞 markerLYZ在单核细胞里比较突出。如果只看聚类色块你只能说“我分出了几个群”但只有 marker 表达图上色后你才能说“这个群大概率是 T 细胞那个群是 B 细胞”。这一步很多人会跳过然后注释细胞类型时就容易翻车。3.3 用 PyTorch 实现一个稀疏自编码器自编码器部分我们用 PyTorch 从零写一个足够用的稀疏自编码器。输入是高变基因标准化后的矩阵瓶颈维度设 16相当于自编码器学习到的压缩表示。X adata.raw[:, adata.var_names].X # 上面的写法并不严谨我们直接使用当前 adata.X 即可 X adata.X.astype(np.float32) if hasattr(X, toarray): X X.toarray() class SparseAutoencoder(nn.Module): def __init__(self, n_input, n_hidden256, n_latent16, sparsity_lambda1e-3): super().__init__() self.sparsity_lambda sparsity_lambda self.encoder nn.Sequential( nn.Linear(n_input, n_hidden), nn.BatchNorm1d(n_hidden), nn.ReLU(), nn.Linear(n_hidden, n_latent) ) self.decoder nn.Sequential( nn.Linear(n_latent, n_hidden), nn.BatchNorm1d(n_hidden), nn.ReLU(), nn.Linear(n_hidden, n_input) ) def forward(self, x): z self.encoder(x) return self.decoder(z), z model SparseAutoencoder(n_inputX.shape[1], n_hidden256, n_latent16) X_t torch.tensor(X, dtypetorch.float32) optimizer torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss() def train_model(n_epochs30, batch_size256, seed42): torch.manual_seed(seed) n X_t.shape[0] for epoch in range(n_epochs): model.train() perm torch.randperm(n) total_loss 0.0 for i in range(0, n, batch_size): idx perm[i:i batch_size] batch X_t[idx] x_hat, z model(batch) rec_loss loss_fn(x_hat, batch) l2 sum(p.pow(2).sum() for p in model.parameters()) sparse_loss model.sparsity_lambda * z.abs().mean() loss rec_loss 1e-5 * l2 sparse_loss optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() * len(idx) if (epoch 1) % 10 0: print(fepoch {epoch 1}/{n_epochs}, avg_loss{total_loss / n:.4f}) train_model(n_epochs30, batch_size256) model.eval() with torch.no_grad(): codes model.encoder(X_t).numpy()代码里我加了两项正则一个是常见的 L2 权值衰减另一个是 L1 稀疏惩罚。L1 惩罚会让编码器输出层的激活值更稀疏不至于让每个细胞都用全部 16 维特征编码。单细胞矩阵里大量零值和 dropout如果完全不做稀疏约束自编码器很容易学会“把所有细胞都压成同一个巨大模态”然后再靠解码器硬撑还原。接下来把自编码器得到的瓶颈特征作为 UMAP 的输入。为了不破坏原始数据的 neighbor 图和 UMAP 结果建议复制一份 adata 再操作。adata_ae adata.copy() adata_ae.obsm[X_ae] codes sc.pp.neighbors(adata_ae, n_neighbors15, use_repX_ae) sc.tl.umap(adata_ae, min_dist0.5, random_state42) sc.pl.umap(adata_ae, color[leiden_0.5, CD3D, MS4A1, LYZ], frameonFalse)这里需要注意adata_ae.copy()保留了之前的 Leiden 聚类标签所以图上颜色可以直接沿用。对比 PCA 输入得到的 UMAP 和 AE 瓶颈特征得到的 UMAP你会发现两者大概率能分出相似的细胞群但细胞的边界、紧凑程度以及一些稀有细胞群的“地位”可能不同。如果训练不稳定或 epoch 太少AE 版本可能会表现得非常混乱这也是它比 t-SNE/UMAP 更难用的原因之一。3.4 三种方法结果怎么对比才有意义拿到三张图之后先别急着下结论。最直接的做法是把同一套已知 marker 基因分别放在三张图上看 marker 的表达模式是否一致。比如 T 细胞 marker 是否都集中在同一个区域。如果 t-SNE 上 B 细胞和单核细胞靠得很近而 UMAP 上它们隔得较远不要立刻说 UMAP 更准应该回到 PCA 结果和 marker 表达上去查。我实际跑 pbmc3k 的经验是PCA UMAP 给出的整体格局最“干净”t-SNE 的群内更紧凑但群之间的空间感觉会被放大AE UMAP 在瓶颈维度设置合理时能给出类似结构但没有深度学习调参经验的话它不会比 PCA UMAP 自动更好。所以如果你是第一次做单细胞分析我建议先把 PCA UMAP Leiden 跑顺自编码器当作进阶工具使用。4. 参数调优把图调到能发文章的关键细节4.1 t-SNE 的困惑度到底在调什么t-SNE 有三个参数特别值得关注perplexity、learning rate 和迭代次数。perplexity 可以粗略理解为“每个点认为多少个其他点是它的邻居”。比较推荐的范围是 5~50并不是越大越好。perplexity 太小图中容易出现散碎的小点团perplexity 太大强制的邻居范围变大可能把本来独立的细胞群强行拉在一起。数据量越大通常可以稍微调大 perplexity但也要结合细胞类型复杂程度。在 Scanpy 的sc.tl.tsne里一般通过perplexity参数直接控制数据规模大时还可能用到early_exaggeration和learning_rate。如果你用的是 sklearn 底层接口early_exaggeration 控制初始阶段簇与簇之间的推开力度调试时如果发现所有细胞都挤成一团可以适当增大这个值。真实单细胞数据中t-SNE 坐标点分布非常依赖随机初始化所以实验报告里建议固定random_state否则换一个种子可能得到左右翻转甚至局部结构变化的图。4.2 UMAP 的 n_neighbors 和 min_distUMAP 最影响观感的是两个上游参数n_neighbors和min_dist。第一个在sc.pp.neighbors里出现第二个在sc.tl.umap里出现。n_neighbors控制 UMAP 构建高维图时考虑多少个邻近点。数值小比如 5会更关注局部结构适合寻找容易漏掉的稀有群但代价是整体容易碎成很多小岛数值大比如 50更能反映连续分化或全局关系但可能把小群彻底淹没。单细胞数据一般从 10~30 起步我用 PBMC 3k 时常用 15遇到细胞数很多的数据可以加大到 20~50。min_dist则控制低维空间中点与点之间允许的最小间距。min_dist越小聚类团之间的边界越清楚细胞点会像星云一样彼此推开但可能导致一个真实连续的分化过程被拆成好几段min_dist越大整体越松细胞群更容易连成片适合展示连续轨迹。很多人习惯把min_dist调到 0.1 以下让细胞群边界更干净但如果你研究的对象是发育分化轨迹可能需要把 min_dist 调到 0.3 以上避免把 stage 之间的过渡细胞硬生生劈开。4.3 自编码器瓶颈维度、归一化与训练轮数自编码器参数弹性很大。瓶颈维度决定了最终压缩特征的容量太小可能丢失关键信息太大又可能只是把 PCA 结果换了个非线性表达。表达矩阵输入为高变基因时我会优先试 8、16、32 三个维度。比如 2000 个高变基因压到 16 维相当于每个维度要承载很多基因的信息模型压力较大如果下游只做可视化16 维通常够用如果还要做分类或轨迹推断可能 32 维更稳。训练前要对输入做标准化这一点很容易被忽略。自编码器用 MSE 作为重建损失如果不同基因的表达量水平差好几档网络会把大部分容量用于还原高表达基因细胞类型特异的低表达信息会被当作噪声忽略。代码里我们沿用sc.pp.scale之后的矩阵就是对每个基因做了均值 0、方差 1 的标准化这样重建损失对不同基因是公平的。训练轮数也不能盲目求多。单层瓶颈网络在几千个细胞的小数据上很容易过拟合。一个比较土但有效的办法是固定随机种子分别用 20、40、80 个 epoch 训练比较瓶颈特征跑出来的 UMAP 是否稳定。如果 40 和 80 轮结果差异很大说明模型对初始化太敏感最好加大数据量或增强正则化。4.4 如何客观衡量降维效果而不是只靠眼睛眼睛看久了会自我说服所以最好搭配一些量化指标。常用的是 trustworthiness、连续性和剪影系数。trustworthiness 衡量的是原空间中的近邻在低维空间中是否仍然是近邻值越接近 1 越好连续性则反过来看低维空间中的近邻在原空间中是否也近。这两个指标比单纯看散点图靠感觉要客观得多。不过要注意这些指标只衡量“几何关系的保持程度”不代表生物学上更准。单细胞分析里最硬的标准还是 marker 基因验证。你可以把候选 marker 的表达值映射到二维图上看是否形成清晰的“颜色梯度”或“局部亮斑”也可以计算每个 Leiden 群相对于其他群的差异表达取 top 基因去注释。一个降维布局如果连已知 marker 都无法对齐无论几何指标多高都说明特征提取环节可能出了问题。5. 高频问题与避坑现场记录5.1 每次运行结果不一致是不是代码写错了不是。t-SNE、UMAP、自编码器都依赖随机初始化或随机采样结果存在一定随机性。t-SNE 对随机种子尤其敏感换一个随机种子可能出现全局布局旋转、翻转甚至局部群组关系变化。UMAP 的稳定性相对好一些但也不是完全确定。自编码器因为涉及神经网络权重初始化、batch 采样顺序天然有随机性。建议从一开始就固定随机种子。在 Scanpy 里设置sc.settings.seed 42在 PyTorch 里调用torch.manual_seed(42)有 GPU 时还要设置torch.cuda.manual_seed_all(42)。如果固定种子后多次运行结果仍然有明显差别那说明数据本身的信号可能不强或者参数位于敏感区域这时候要回头检查预处理和离群细胞而不是反复换随机种子硬找“好看”的图。5.2 UMAP 图全是“连体婴儿”群边界模糊怎么办这种问题最常见的原因不是 UMAP 而是输入。如果高维距离空间里本来没有清晰的边界UMAP 不可能强行造出边界。你先检查 Leiden 聚类结果里是否存在一个巨大的群比如 90% 的细胞都被分到一起。如果是大概率是细胞质控没过关或者 PCA 维数选得过高把技术噪音也当成信号。排除了输入问题后可以试着调小min_dist比如从 0.5 调到 0.1 或 0.05细胞群之间会更“炸开”。也可以稍微减小n_neighbors让算法更关注局部连接。如果 t-SNE 图上能看到边界而 UMAP 看不到大概率是 UMAP 的全局结构建模把边界抹掉了并不是 UMAP 比 t-SNE 差只是两者侧重不同。反过来如果 UMAP 图碎成太多小岛而且每个小岛只有寥寥几个细胞一般不是稀有细胞群而是过拟合噪音。这时可以增大n_neighbors或先降 PCA 到更少的维度。5.3 预处理的坑不要跳过 scale也不要忽略数据版本很多刚上手的同学会直接拿原始 count 矩去跑 UMAP结果图里所有细胞被几个超高水平基因主导分群完全不是生物学表现。还有同学会在高变基因筛选前做 scale导致后续高变基因选择受缩放影响这也是不太合理的顺序。标准顺序是先归一化、log、高变基因筛选再 scale然后 PCA。如果用的是 10x 的 Cell Ranger 流程表达矩阵已经过初步处理但不同版本之间基因命名和重复基因存在差异。跑分析前记得用adata.var_names_make_unique()处理重复基因名否则后面 marker 上色会找错基因。此外adata.raw不能乱覆盖它一旦被删掉很多扫描raw的函数会报错画 marker gene 散点图会非常痛苦。5.4 什么时候不该用自编码器相比 PCA 加 UMAP自编码器并不是银弹。如果你的目标就是出一张细胞分群主图PCA UMAP 基本足够没有必要引入神经网络。自编码器的优势在于能学习非线性特征、把批次变量或测序深度作为条件输入、并生成用于下游预测或聚类的高维抽象特征。如果你的样本量只有几百个细胞自编码器很容易过拟合如果你没有 GPU 或者不熟悉反向传播训练排查训练异常会非常耗时如果你很在意结果可复现并可能被审稿人要求解释“为什么用这个架构”那自编码器的门槛会明显提升。普通单细胞分析报告里自编码器不是必选项PCA 仍然是默认基线。如果决定用自编码器记得把稀疏约束加上。没有稀疏或噪声正则的自编码器可能会学习到“把每个细胞都近似编码成均质向量”的捷径。那种简化版在二维 UMAP 上往往表现为一个巨大的细胞团完全看不出离散群这种结果我也遇到过几次基本都是输入没标准化、正则太弱或训练轮数太少这三类原因。5.5 下游聚类和细胞注释别被降维图带偏降维图上的“视觉距离”不等于“细胞相似度”。在单细胞分析流程里更稳的做法是先用 PCA 或自编码器瓶颈特征构建 KNN 图然后用 Leiden 聚类。聚类的分辨率决定你看到的群数量resolution 越大分群越多。分群结束后再把群标签映射到 t-SNE 或 UMAP 图上做展示。很多新手容易反过来觉得 UMAP 图上某两坨分得开就直接把颜色标签手动指定成两个细胞类型。这样非常危险因为 UMAP 的布局会受参数影响同一批数据不同参数下可能把两个群画得更开或更近。科学上的判断依据应该是 marker 基因表达、差异表达统计和已知细胞类型注释而不是纯几何位置关系。我在实际项目中会先跑 PCA Leiden再用rank_genes_groups找每个群的标记基因。只有标记基因表达模式与文献已知细胞类型一致时我才会把“群号”翻译成“细胞类型”。可视化只是让这个判断过程更直观不能反过来主导判断。6. 选型快查表与我的使用习惯6.1 快速决策表场景推荐组合理由快速看整体细胞群结构PCA UMAP速度快、局部与全局平衡投稿时需要更紧凑的细胞群展示PCA t-SNE细胞群视觉更紧侧面辅助审稿大样本十万级以上PCA UMAPt-SNE 内存和耗时都偏高探索稀有细胞群PCA UMAP调低 n_neighbors增强局部敏感度发育轨迹/连续状态展示PCA UMAP调高 min_dist避免过度离散化非线性低维特征用于免疫分型或预测自编码器或 scVI能获得抽象特征向量多批次数据整合后可视化条件自编码器类工具或校正后 PCAUMAP显式建模批次来源严格可解释、低门槛分析PCA UMAP默认基线容易排查自编码器在单细胞领域更适合作为“特征提取前置”而不是直接替代 PCA 画图。条件自编码器像 scVI 通常还会输出一个校正后的表达矩阵或潜在表示再连接 UMAP/Leiden 使用这种流程在批次效应明显的多样本项目中更可靠。6.2 我个人在实际项目中的使用习惯跑过大量单细胞数据之后我的固定流程大致是这样先按标准流程做质控和预处理PCA 保留前 15~30 个主成分用 PCA 构建邻居图跑 Leiden先出 UMAP 看整体格局再跑一个 t-SNE 用来交叉验证那些只在 UMAP 上看起来离群的小簇。如果时间允许我会把 AE 得到的瓶颈特征也投影一次看它能否提供额外可解释信号但不会用它作为唯一结论。一个值得分享的小技巧是不要只看聚类标签着色的图一定要把若干关键 marker 同时放在 UMAP 上检查。比如免疫细胞T、B、NK、单核这几类的 marker 表达模式如果与聚类群对不上多半是聚类参数或数据质控出了问题。把颜色阈值、图例范围和布局列统一后再去比较 t-SNE 和 UMAP你会少走很多弯路。还有一点降维图和聚类结果要保持 seed 固定否则后期复现结果时会浪费大量时间。最后说句实在话方法学工具没有绝对的高低之分。t-SNE 适合精细展示UMAP 适合整体快速把握自编码器适合在理解神经网络原理的前提下做更有弹性的特征学习。先用公认流程把基础结果做扎实再根据具体问题决定要不要引入深度学习这条路对绝大多数人来说更稳也更省力。
返回列表