处理单细胞多组学的朋友应该都经历过同样的痛苦:手上有同一批样本的转录组和染色质可及性数据,甚至在同一个细胞上同时拿到了RNA和ATAC,可一打开软件就发现两个数据矩阵的维度对不上,几千个细胞、两万个基因对上几十万个峰,根本没法直接丢进同一个聚类模型。GLUE(graph-linked embedding)这篇论文的做法当时让我眼前一亮,它绕开了“强行把特征空间变成一样”的思路,而是先把基因、峰、motif之间的已知关系画成一张特征图,再让两组数据在图的约束下共同嵌入到同一个低维空间。这篇文章就把GLUE的来龙去脉、原理和上手细节完整聊一遍,适合刚接触多组学整合、想弄明白工具背后逻辑的实操型选手。
1. 先想清楚:多组学整合到底在整什么
1.1 组学之间的“语言不通”问题
我们做多组学整合,本质上想回答一个问题:一个细胞在转录组、染色质可及性、甲基化等不同层面上分别是什么样的状态,这些状态能不能放在同一个坐标系里比较。道理很简单,做起来很麻烦。RNA-seq的feature是大约两万个基因,scATAC-seq的feature是几十万甚至上百万个峰,甲基化芯片/测序的feature又是几十万个CpG位点。每个feature承载的信息类型也不同:表达量是counts,可及性是二元的open/closed或counts,甲基化率是0到1之间的连续值。
更要命的是,同一个生物学事件在不同组学里长得完全不一样。比如一个T细胞,转录组里看到CD3D高表达,ATAC里看到的是CD3D附近的启动子区域开放,甲基化数据里则是某些CpG位点的甲基化水平下降。三者本质上都在说同一件事,但数值形态、特征名字都对应不上。如果没有一个跨组学的“翻译层”,两个矩阵直接拼在一起,数学上可行,生物学上却没法解释。
1.2 为什么不能像传统ETL那样直接拼接
做过传统数据集成的人可能会想:这不就是个多表关联问题吗?给两张表一个共同主键,JOIN一下不就好了?Pentaho Data Integration这类ETL工具的玩法确实是这样,业务数据靠统一主键或者统一ID就能拼接。可组学数据最大的问题在于:基因、峰、CpG位点之间根本不存在一一对应的主键关系。
一个基因对应若干个启动子近端峰、若干远端增强子峰;一个CpG位点可能落在启动子区,也可能落在gene body甚至基因间区。如果硬要把ATAC峰注释到基因上再和RNA合并,必然会丢掉远端调控信息,而远端增强子恰恰是细胞类型特异的染色质调控核心区域。还有一类尝试是把所有组学统一投影到“基因活性分数”,Seurat早期的做法就是基于这个思路,简单但损失信息。GLUE并不是在细胞层面硬拼,而是把“特征之间的关系”本身当成桥梁,让特征先对齐,细胞再跟着特征走。这个思路上的差异,是GLUE和很多传统方案的根本区别。
1.3 GLUE给出的答案:用图把特征桥接起来
GLUE全称是Graph-Linked Unified Embedding,作者Cao和Gao在2022年发表在Nature Biotechnology上。名字里的“graph-linked”强调得非常到位:它构建了一张跨组学的特征图,节点是不同组学的特征,边是特征之间已知的生物学关系,比如peak落在基因启动子附近、转录因子的motif出现在某peak里、TF和靶基因有调控关系等。这张特征图相当于一个“语义桥梁”。
有了桥之后,每个组学内部先做各自的降维编码,编码过程同时受特征图中的边约束。图里有关联的特征,在低维空间里就会被拉近;没有关联的特征,自然就分开。这样一来,不同组学不是被强行塞进同一套坐标,而是通过共享的特征空间间接对齐。这个思路最大的好处是:即使两个组学的feature名字完全对不上,只要它们在图里有边,就能在嵌入空间里产生联系,之后细胞嵌入也随之对齐。
2. GLUE核心原理:graph-linked embedding的三步棋
2.1 第一步:特征图(feature graph)怎么建
GLUE输入的特征图,我建议理解成“特征版的知识图谱”。节点是每个组学的feature,边是跨组学的生物学关系。常见的关系有几类:
- peak-gene关系:某个ATAC峰落在某基因的启动子区域或远端调控区域,这是最常用的一类边,可以通过基因组坐标直接算,也可以从ENCODE等数据库拿。
- TF-motif-peak关系:某个转录因子的结合motif在某个峰序列里出现过,说明这个TF可能结合在这个peak上。
- TF-gene关系:转录因子和靶基因之间的调控关系,可以从DoRothEA、TRRUST、以及文献积累的GRN里拿。
- 同一组学内部的特征关系,比如基因-基因相互作用,这种边可选,不影响主流程。
这张图的构建质量直接决定整合效果。我在实际项目中遇到最典型的失败案例是:ATAC峰没有注释到正确的基因组版本,基因名又是另一套命名,导致peak和gene之间几乎没有边,模型虽然能跑,但那本质上成了两个独立VAE的拼接,完全没有“link”的效果。所以动手前一定要统一参考基因组版本,基因注释用同一套GTF,peak坐标也要对应同一版本。
2.2 第二步:图变分自编码器如何嵌入同一空间
特征图只是先验结构,真正把数据嵌进去依赖变分自编码器(VAE)。GLUE的主体框架是图变分自编码器,它的思路粗略理解是这样:每个细胞的原始特征向量,经过编码器网络,映射到一个低维隐变量z,再通过解码器还原出原始特征空间的分布。
关键在解码器这边。普通VAE的解码器是从隐变量z直接映射回原始特征;而GLUE在解码之前,先用图神经网络(GCN/GAT)对特征图中的每个特征节点做编码,得到每个基因、每个峰的低维表示。之后解码器把细胞隐变量z和特征表示拿去做某种相似度/内积运算,重构出细胞在这个特征上的数值。
这样一来,细胞嵌入和特征嵌入被放在了同一个低维空间里。我个人的理解是:特征嵌入描述了“这个基因/峰在这个空间里的位置”,细胞嵌入描述了“这个细胞在这个空间里的位置”。如果特征图里基因A和峰B有条边,那么它们的特征嵌入会被约束在某个距离内,细胞在表达基因A和开放峰B时,自然就会被引导到相近的位置。这才是graph-linked embedding的核心逻辑。
2.3 第三步:链式约束与对抗批次校正如何协同
GLUE的目标函数大致可以拆成三块,实际训练时这三块不是简单的相加,而是加权协同:
- 重构损失(reconstruction loss):保证细胞隐变量能还原原始数据。具体分布会根据组学类型选择,RNA-seq通常用负二项分布来建模counts,ATAC峰可以考虑二项或负二项,甲基化用Beta分布。这一点比简单Z-score标准化靠谱得多,因为不同组学的数据生成机制不一样。
- 图约束损失(graph constraint):对特征图中每一条边,要求两端特征在嵌入空间中的表示尽量接近。这个约束可以用距离的平方项实现,也可以用更复杂的拉普拉斯约束。它决定了“link”的强度,也是GLUE防止不同组学被错误对齐的关键。
- 对抗性批次损失(adversarial loss):跟scVI接近,加一个判别器区分细胞来自哪个组学或哪个批次,编码器则被训练成让判别器分不出来。这样做的目的是消除组学和批次来源带来的技术差异。
这三块需要权衡。图约束太强,跨组学确实能合得很彻底,但可能把真实的生物学差异也抹掉;对抗太强,会把不同细胞类型的真实差异也当作“技术变异”给拉平。论文里有一套默认权重,但换数据集后我基本都会重新调。
2.4 GLUE与Seurat/Harmony/scVI的定位差异
很多同学问:我有Harmony就能整合,为什么还要上GLUE?Harmony解决的是“同一组学内多个批次的校正”,它没有跨特征空间的建模能力。Seurat WNN虽然能处理多组学,但前提是几乎一定是同一细胞测了RNA+ATAC的paired数据,而且本质上还是在特征或细胞维度做加权融合。scVI的VAE框架和GLUE有共同点,但scVI通常也是把不同组学的feature全部拼到一个输入空间里,组学feature维度悬殊时,低维表示很容易被大维度的组学主导。
用一张表总结更直接:
| 工具 | 核心思路 | 跨组学特征对齐 | 是否需要同一细胞 | 批次校正 | 适用场景 |
|---|---|---|---|---|---|
| Seurat CCA/WNN | 典型相关分析或加权近邻融合 | 弱,通常需要特征同名或先转基因活性 | 最好paired | 有 | 同一细胞RNA+ATAC,流程成熟 |
| Harmony | 迭代聚类校正嵌入 | 无,只处理同一特征空间 | 不需要 | 强 | 单组学多批次整合 |
| scVI | 条件VAE,拼接输入特征 | 弱,直接拼特征 | 不强制 | 强 | 单组学多批次、大数据量 |
| GLUE | 图变分自编码器+特征图桥接 | 强,通过feature graph跨特征对齐 | 不强制,支持非paired | 强 | RNA+ATAC/甲基化等多组学联合整合、调控推断 |
GLUE不可替代的点在于:它允许非配对样本参与跨组学整合。比如你有100个病人的RNA数据和其中50个病人的ATAC数据,不需要同一批细胞/同一个人,只要有足够的先验特征关系,也能放进同一个嵌入空间。这在真实项目里非常实用,毕竟不是每个课题都有条件做10x Multiome。
3. 实操过程:从两个h5ad到整合后的一张大表
3.1 环境与安装
GLUE的官方实现是Python包scglue,依赖PyTorch、scanpy、networkx等。我的建议是单独建一个conda环境,不要和日常分析环境混在一起,因为scglue对scanpy、numpy的版本比较敏感,混装容易把环境搞坏。
conda create -n glue python=3.9 -y conda activate glue conda install pytorch cudatoolkit=11.3 -c pytorch -y pip install scglue scanpyGPU不是硬性要求,但我强烈建议用GPU。ATAC峰矩阵随随便便几万到几十万维,CPU跑起来太煎熬。装完可以跑一下python -c "import scglue; print(scglue.__version__)"确认安装成功。我遇到过的情况是PyTorch和CUDA版本不匹配导致模型初始化后原地卡死,排查半天,最后是把cudatoolkit版本对齐才解决。如果不想折腾CUDA,可以退而求其次装CPU版,小数据量也能跑。
3.2 构建输入:Anndata、基因注释、motif注释
GLUE的输入是标准的AnnData对象,RNA和ATAC各一个,都已经做过基本QC。数据怎么预处理有讲究,我的经验是:RNA数据保留高变基因可以,峰值矩阵也建议先做一次标准化和特征筛选,但千万不要做传统意义上的批次校正。GLUE自己会处理批次,如果你提前用Harmony拉平过一次,后面GLUE的对抗学习反而容易过度。
关键步骤是给每个AnnData补上特征注释。RNA数据需要有基因注释信息,ATAC数据需要峰值注释和motif注释。以人类为例,大致流程是这样:
import scglue import scanpy as sc rna = sc.read_h5ad("rna.h5ad") atac = sc.read_h5ad("atac.h5ad") # 1. 基因注释 scglue.data.get_gene_annotation( rna, gtf="gencode.v38.annotation.gtf.gz", by="symbol" ) scglue.data.get_gene_annotation( atac, gtf="gencode.v38.annotation.gtf.gz", by="symbol" ) # 2. motif注释(ATAC需要) scglue.data.get_motif_annotation(atac, species="human", motif="jaspar")需要说明的是,不同版本的scglue接口有差异,具体函数签名打开帮助文档看一眼最稳妥。motif注释这一步很关键,因为TF-motif-peak这条边是GLUE建立跨组学调控联系的重要来源。如果你用的物种不是人类或小鼠,公共数据库覆盖不全,特征图会稀疏很多,整合效果会打折扣。
3.3 模型训练与结果读取
特征注释完成后,构建feature graph并训练模型。核心流程可以用下面的伪代码表达,具体类名和参数以你安装版本为准:
# 3. 构建跨组学特征图 graph = scglue.data.merge( adatas=[rna, atac], keys=["rna", "atac"], on="genes", how="inner" # 取共有基因作为桥接关系 ) # 4. 建立模型并训练 model = scglue.models.SCGLUEModel(graph=graph, latent_dim=64) model.fit( adatas=[rna, atac], graph=graph, max_epochs=200, batch_size=128, seed=0 ) # 5. 导出整合后的嵌入 cells = model.encode([rna, atac]) combined = scglue.data.merge(cells, keys=["rna", "atac"]) combined.X = combined.obsm["X_glue"] sc.pp.neighbors(combined, use_rep="X_glue") sc.tl.umap(combined)这段代码只是主线示意,千万不要盲复制。我见过很多人在版本更新后拿着旧教程的类名硬跑,结果各种AttributeError。我的习惯是跑之前先help(scglue.models)、help(scglue.data)把接口过一遍,花五分钟比报错后查一小时的效率高。
训练完成后,combined.obsm["X_glue"]就是整合后的细胞低维表示,可以接scanpy的聚类、UMAP、差异分析流程。也可以把combined导出h5ad作为下游GRN分析、细胞类型注释的输入。
3.4 我的训练参数心得
GLUE的超参数里,我最常调的是三个:latent_dim、图约束强度、max_epochs。latent_dim默认值一般是50或100,数据量大、组学数量多的时候建议稍微调大一点,给模型足够的容量去容纳复杂的特征关系;但如果只有RNA+ATAC两个组学且样本量不大,太大会过拟合,太小又表达不开关键差异。
图约束强度是GLUE的命门。约束太弱,跨组学相当于“各跑各的”,ATAC和RNA在UMAP上会形成两个“大陆”;约束太强,则会把不该拉近的细胞类型也搅在一起,看起来整合了,生物学也毁了。我自己的做法是:先用默认参数跑一版,看轮廓;如果ATAC和RNA明显分群,就把图约束权重往上调;如果分群很干净但已知marker在两种组学里对不上,说明约束过强了,往回退。论文里的默认权重是一个起点,不是终点。
max_epochs建议先跑一个短版,比如10个epoch,看一眼loss曲线稳定程度再决定。我踩过的最典型的坑是:一上来跑200 epoch,跑到一半发现学习率太大,loss发散,白白浪费几小时。先短训调试,再全量训练,这是我最想强调的经验。
4. 整合之后能做什么:调控推断与应用落地
4.1 跨组学协同聚类与标记基因
GLUE整合完成后最直接的价值是跨组学细胞类型对齐。以前RNA和ATAC各自聚类,然后靠人工比对marker去猜两组结果的对应关系;整合之后,两种组学的细胞进的是同一个UMAP,同一个细胞类型不管来自RNA还是ATAC都聚在一起。这一步对多组学细胞图谱项目特别有用。
实战中我一般会做几个检查:第一,确认整合后的cluster里RNA细胞和ATAC细胞的比例没有极端偏斜;第二,抽几个已知的谱系marker基因,看它在RNA模态的细胞群中和ATAC模态的peak活性是否一致;第三,用差异分析找出每个cluster的marker基因和差异可及性peak,从两个层面解释这个cluster的生物学身份。GLUE整合做得好的话,这两套结果通常是自洽的,比如一个T cell cluster,RNA里CD3D高,ATAC里CD3D附近的调控元件开放度也高。
4.2 TF-增强子-靶基因的调控推断
GLUE不能直接给你一张GRN,但它能把调控推断这件事做得顺畅很多。原因是整合后的数据天然包含了TF-motif-peak-gene这条链式信息:特征图里有TF和peak的关系,有peak和gene的关系,整合后细胞嵌入和特征嵌入又处在同一个空间,于是我们可以把“某细胞类型中TF活性高、某peak开放、某靶基因表达上调”这几个事件关联起来。
我的流程通常是:在整合后的对象上分别跑RNA模态差异表达和ATAC模态差异可及性,然后在显著差异peak里做motif富集,找到候选TF。接下来回到GLUE的特征图中,看这些peak是否和候选TF的靶基因有边连接。如果有,就形成了一个很有说服力的调控链路:TF在细胞类型A中结合增强子区域,该增强子开放度上调,靶基因表达随之上升。这比单纯在RNA数据里算TF和靶基因表达相关要可靠,因为多了一个染色质层面的证据。
4.3 参考映射与新数据投放
GLUE训练好的模型可以当成一个参考图谱来用。以后来了一批新的ATAC数据,不用重新和RNA一起训练,直接用训好的模型把新数据的细胞映射到原有嵌入空间里,实现“参考转录组+查询染色质”的映射模式。
这个功能对临床样本或大规模队列特别有价值。比如你已经有了一套完整的健康人免疫系统RNA+ATAC图谱,后续拿到病人的ATAC数据,可以快速映射到图谱中,看到病人细胞在哪些细胞类型上偏离了正常状态。需要注意的一点是:查询数据的峰注释、基因注释必须和训练时一致,否则特征对不上,映射结果基本不可信。
5. 常见问题与排查技巧实录
5.1 训练报错与版本坑
scglue和scanpy、numpy、pytorch的版本兼容是个老大难。我自己遇到最多的报错无非几类:AttributeError是接口变了,KeyError是特征名对不上,CUDA out of memory是显存不够。这里分享一个排查思路:先在CPU上用最小数据集跑一遍,验证代码逻辑无错,再切到GPU跑全量。这样能把“代码问题”和“资源问题”分开,省很多时间。
| 报错信息 | 常见原因 | 处理办法 |
|---|---|---|
| AttributeError: module 'scglue' has no attribute 'xxx' | 版本API变动 | 查看官方文档和help,按新版写法 |
| KeyError: 'gene_annotation' | 特征注释缺失 | 检查是否运行了get_gene_annotation |
| CUDA out of memory | 峰值矩阵太大或batch_size过大 | 减小batch_size、latent_dim,或先用top可变峰 |
| RuntimeError: all elements of input should be between 0 and 1 | ATAC数据不是二元/计数 | 检查是否做了不合适的标准化 |
5.2 整合效果差:过度混淆还是清高过头
整合效果差通常分两种。第一种是UMAP上RNA和ATAC各占一坨,完全没有融合。这种情况大概率是feature graph太稀疏,peak和gene之间的边太少,或者motif注释缺失导致TF-peak边根本没建起来。还有一种可能是图约束权重太低。我的排查路径是:先看feature graph里的边数够不够,再检查图约束损失是否在下降,最后一步才是调权重。
第二种是整合得“过头”,细胞类型完全糊在一起,连已知的T细胞、B细胞marker都分辨不出来。这往往是图约束太强,把不同细胞类型的真实差异也当作技术差异抹平了。遇到这种情况我会先降图约束,再检查批次校正强度。对抗性损失太强也会导致过度混淆,因为判别器把不同细胞类型也识别成了需要消除的“批次”。
5.3 显存不足与运行时间优化
峰值矩阵动辄几十万列,显存压力非常大。我的几个实用招数:一是事先把低质量峰过滤掉,保留全基因组范围内top 5万个可变峰就够大多数分析用了;二是把batch_size调小,显存不够时从128改到64往往立竿见影;三是在模型支持的情况下用混合精度训练;四是实在不行就换CPU跑但把max_epochs压到200以内,小数据量也能接受。
时间方面,RNA+ATAC各一万个细胞,50000个peak,在单卡V100上大约跑2到3小时,这个量级完全可以接受。如果数据量到十万细胞级别,建议先在几千个细胞上做参数调试,再全量训练,避免参数还没调好就白烧一晚上GPU。
6. 选型建议:什么场景值得上GLUE
6.1 场景化对比
工具选型不需要赶时髦,关键是看你的数据和问题是否匹配。我把常见场景拆一下:
| 数据情况 | 核心目标 | 推荐方案 |
|---|---|---|
| 只有RNA,多批次 | 去除批次效应后集群注释 | Harmony或scVI |
| 同一批细胞的RNA+ATAC,且主要是聚类 | 单细胞多模态注释 | Seurat WNN,流程成熟 |
| 非配对RNA+ATAC,想把两组装的细胞整合到一起 | 跨组学细胞图谱构建 | GLUE优先 |
| RNA+ATAC+甲基化三组学 | 多组学联合整合+调控推断 | GLUE,特征图优势明显 |
| 已有参考图谱,新来一批ATAC/甲基化 | 新数据映射到参考图谱 | GLUE参考映射,或spatial/其他映射工具 |
GLUE尤其适合“两个组学没有天然的配对关系”的项目。比如公共数据库里RNA数据是一批样本,ATAC数据是另一批独立样本,这种情况下CCA和WNN都很难派上用场,而GLUE通过特征图桥接可以正常工作。
6.2 我的个人判断
如果你的项目只是“同一个multiome数据做个聚类注释”,GLUE的收益没那么大,用Seurat WNN可能更快更省事。但如果你的目标是构建跨样本、跨组学的整合图谱,或者想从ATAC+RNA联合数据里挖调控机制,GLUE的feature graph思路是绕不开的核心资产。我甚至觉得GLUE最大的价值不是那个整合嵌入,而是它逼着你去认真整理基因注释、motif注释、peak-gene关系这些先验知识。这堆信息整理清楚之后,下游无论跑什么分析都是加分项。
最后再分享一点我的操作习惯
GLUE跑完之后,不管UMAP看起来多干净,我都会做一个固定动作:抽5到10个已知的谱系marker,逐个看它们在RNA模态的表达和ATAC模态的可及性,确认两者在对应细胞类型上真的“对齐”了。这一步听起来简单,却是很多人偷懒跳过的地方。技术指标再好,生物学对不上也是白搭。另外,feature graph的构建质量几乎决定了GLUE的上限,基因命名不一致、基因组版本混乱、motif数据库选错,都会让整合效果大打折扣。我这几年的体会是:花半天时间把数据和注释整理干净,比调一周参数都管用。这大概是GLUE教给我最重要的一课。