拿到一个单细胞转录组矩阵,两万多个基因乘几万个细胞,九成以上是零。要做细胞类型注释,最直接的路子是聚类完人工翻marker基因,可cluster一多,稀有类型就容易被并进去;换有监督模型又得先有标注,而一张靠谱的标注表往往要耗掉几周。这几年无监督表示学习的方法出了不少,但大多数只盯着细胞这一侧——把每个细胞压成一个低维向量,基因那边要么当作特征维度直接扔掉,要么等聚类完再用差异表达事后解释。AAAI-24上的这篇Unsupervised Gene-Cell Collective Representation Learning with Optimal Transport换了个视角:基因和细胞两侧都学表示,再用最优传输把两个分布对齐起来,全程不需要任何标签。这篇东西适合三类人看——正在做单细胞下游分析、被聚类稳定性折磨的分析人员;想搞明白最优传输怎么落到真实数据上的算法同学;以及打算把双视角表示学习搬到别的领域的开发者。下面我按自己的理解把方法拆开讲,顺带把复现时会遇到的坑一并说清楚。
1. 单细胞矩阵为什么逼着我们去学"两套表示"
1.1 高维稀疏把常规降维方法逼到墙角
单细胞RNA测序的本质是"在极少的RNA分子里做抽样"。一个细胞里实际表达的转录本可能上万,但测序只能抓到几千条,没抓到的就记成零。于是你拿到的矩阵里,一个明明表达了的基因,因为抽样没覆盖到,读数也是零,这就是常说的dropout。它带来的直接后果是:零不代表不表达,只代表没测到。传统的PCA在这种矩阵上跑,隐含假设是数据近似高斯、变量间线性相关,而稀疏矩阵里大量结构化的零会把这个假设彻底带偏。更麻烦的是欧氏距离在高维稀疏空间里会退化——任意两个细胞之间的距离差不了多少,"近邻"这个概念本身就模糊了。
自编码器的路子是绕开线性假设,用非线性映射把细胞压到低维再重建回去。这类方法确实比PCA稳,但它们的目标函数只关心"能不能把输入还原出来"。重建得好,不代表嵌入空间里的几何结构有意义;反过来,嵌入空间聚得漂亮,也不代表重建损失小。这两件事在无监督设定下经常打架。我见过不少案例,模型重建误差降得很低,但聚类出来的簇边界糊成一片,原因是编码器把大量方差花在了拟合技术噪声上,真正的生物学差异反而被压掉了。
这里还有个容易被忽略的点:单细胞数据的稀疏性是结构化的,不是随机缺失。高表达基因的零少,低表达基因的零多;某些细胞类型整体表达程序更"开",另一些更"关"。如果模型不显式建模这种结构,它就会把所有零一视同仁地当成噪声处理,然后学出一堆由测序深度驱动的伪结构。这也是为什么很多方法在同一个数据集上换个测序批次重跑,聚类结果就漂了。
1.2 只学细胞表示的方法,基因侧的信息基本被浪费
市面上主流的无监督单细胞表示方法,包括基于变分自编码器的、基于深度聚类的、基于图对比的,共同点是:输入是整个表达矩阵,输出是每个细胞的嵌入,基因在这个过程中只是特征维度。训练完之后你想知道"这批细胞为什么聚在一起",还得回头做差异表达分析,再拿结果去和已知通路比对。这个流程本身没问题,但它有两个硬伤。
第一,差异表达是事后统计,它依赖聚类结果。聚类错了,marker也就跟着错,误差会沿着流水线一路传下去。第二,基因之间的关系在细胞嵌入里是读不出来的。比如说你想知道某两个基因是不是协同调控的、某个转录因子模块在哪些细胞亚群里活跃,细胞嵌入给不了你答案——它把基因维度压没了。你可以再单独跑一次基因共表达网络分析,但那是另一套流程,两边的结果对不上是常态。
实际项目里这个痛点特别明显。做肿瘤微环境分析的时候,细胞嵌入能告诉你"这里有五个亚群",但你真正想回答的是"这五个亚群各自的表达程序是什么、哪个亚群对应耐药表型"。后半个问题必须回到基因侧,而如果基因侧没有一套和细胞侧共享几何结构的表示,你就得在两个独立空间里做交叉验证,费时费力还容易得出矛盾结论。
1.3 基因侧表示能解锁什么
把基因也当成学习对象,最直接的好处是你可以得到一个"基因嵌入空间"。在这个空间里,功能相关的基因会靠得近,因为它们在不同细胞里的表达模式相似。这相当于把共表达分析内化到了模型里,不需要额外跑一遍。更进一步,如果基因嵌入和细胞嵌入是在同一个框架下联合优化的,两者之间的对应关系就有了统一度量,你可以直接问"这个基因模块在哪些细胞上权重最高",答案来自模型内部的运输计划,而不是事后拼凑。
打个比方,这有点像推荐系统里的双塔结构。用户和物品各学一个向量,两边不在同一个空间里硬比,而是通过交互信号(点击、购买)对齐。单细胞里的"交互"就是表达:某个基因在某个细胞里表达高,就相当于一次"强关联"。最优传输在这里扮演的角色,就是把这个关联关系从稀疏的观测矩阵里还原成一个全局最优的软匹配。这个类比我在后面拆代价矩阵的时候还会再提一次,因为理解了它,模型的设计动机就基本清楚了。
还有一层价值在于迁移。基因侧的表示相对稳定——不同数据集测的是同一套基因组,基因嵌入的可复用性比细胞嵌入高得多。你在一个大的参考数据集上把基因嵌入学好,换到小数据集上只微调细胞侧,这在跨样本、跨平台的分析里是很有吸引力的思路。
2. 最优传输在这个框架里到底干了什么活
2.1 从搬土问题到Sinkhorn迭代的直觉
最优传输的原始问题是:有一堆土(供应方),分布在几个坑里,每个坑的土量不同;有一批工地(需求方),每个工地要的土量也不同;把土从坑运到工地的单位成本已知。问怎么运,总成本最低。数学上就是找一个运输计划矩阵,使得它满足行和等于供应量、列和等于需求量,同时和代价矩阵的内积最小。这个最小成本就叫Wasserstein距离。
放到这篇工作里,"供应方"和"需求方"就是基因分布和细胞分布。每个基因是一个带质量的点,每个细胞也是。代价矩阵衡量的是"把某个基因的质量分配给某个细胞"要花多少代价,通常就用两个嵌入之间的欧氏距离或余弦距离。运输计划解出来之后,矩阵里第i行第j列的值就代表基因i和细胞j之间的关联强度,而且这个矩阵天然满足边缘约束——每个基因的总质量、每个细胞的总质量都被守恒住了。
直接解这个线性规划在大规模数据上太慢。常用的加速手段是加熵正则项,把问题变成强凸的,然后用Sinkhorn迭代交替归一化行和列。加熵的副作用是解会变"软",运输计划不再是稀疏的硬分配,而是带一点弥散。这个软化其实正好合我们的意:生物学上的基因-细胞关系本来就不是非黑即白的,一个基因可以在多个细胞类型里都有表达,只是强度不同。熵正则系数epsilon控制软硬程度,太小解接近硬分配但数值不稳,太大解趋于均匀分布丧失区分度,这个参数的调法我在第4节细讲。
2.2 运输计划就是基因与细胞之间的软对齐
理解了这个机制,再看模型就通透了。整个框架大致是这样:一个编码器吃细胞表达谱,输出细胞嵌入;另一个编码器吃基因表达谱(把矩阵转置一下,每个基因就是一个"样本"),输出基因嵌入。两个嵌入空间通过某种方式映射到同一个维度,然后算代价矩阵,跑Sinkhorn得到运输计划。这个计划同时约束两侧:如果基因i和细胞j在运输计划里权重高,那它们俩的嵌入就应该靠得近;反过来,嵌入的更新又会改变代价矩阵,进而改变运输计划。这是一个交替优化的过程,和EM算法的味道很像。
这个设计我觉得最巧妙的地方在于,它把"监督信号"从外部标注换成了内部结构。传统方法要判断两个细胞是不是同一类,得有标签;这里判断的依据是基因表达模式的整体匹配程度,而这个匹配是通过全局最优运输算出来的,不是局部启发式。全局性很关键——如果只看局部近邻,稀有细胞类型很容易被邻近的大簇吞掉;而全局运输要在所有边缘约束下最小化总成本,稀有类型只要表达模式足够独特,就能在计划矩阵里分到属于自己的那一份质量。
注意:这里的"全局"是指优化目标覆盖整个代价矩阵,不是说计算过程不需要分批。实际实现中为了显存,还是要在细胞子集上做mini-batch,只是每个batch内部的运输是全局求解的。
2.3 为什么不用对比损失或者简单的MSE对齐
这个选择值得单独说说,因为很多人第一反应是"对齐两个分布,用对比学习不就行了"。对比学习的核心是构造正负样本对:同一个细胞的不同增广视图算正对,不同细胞算负对。问题在于,单细胞数据里什么叫"同一个细胞的不同视图"?你可以做基因masking、做dropout模拟、做随机采样,但这些增广本身就会破坏表达结构,尤其是在稀疏矩阵上,mask掉一批基因之后剩下的信号可能已经不足以识别细胞类型了。而且负样本的选取很敏感,batch里随机采的负样本很可能和正样本属于同一类型,模型就被教坏了。
MSE对齐的路子更直接:既然要联合,那就让基因嵌入和细胞嵌入直接算均方误差。但这两侧根本没有天然的一一对应关系,一个是基因、一个是细胞,维度都对不上,硬算MSE等于强行假设某个基因对应某个细胞,这在生物学上没有依据。
最优传输的优势在于它处理的是分布之间的关系,不需要预先指定配对。它输出的运输计划是一个软分配矩阵,允许一个基因对应多个细胞、一个细胞接收多个基因的贡献,多对多的关系被自然地表达出来了。代价是计算复杂度更高,Sinkhorn迭代在大矩阵上仍然是瓶颈,这也是为什么这类方法通常要在降采样或者分块上做工程优化。另外OT对代价矩阵的尺度很敏感,如果两个嵌入空间的数值范围差太多,运输计划会退化成近似均匀分配,这点在后面踩坑部分会重点讲。
3. 模型骨架拆开看:编码器、代价矩阵与四项损失
3.1 双分支结构与共享潜空间的取舍
从方法命名和这类工作的常见设计推断,骨架应该是双分支的:细胞分支的输入是每个细胞在筛选后的高变基因上的表达向量,基因分支的输入是每个基因在所有细胞上的表达向量(也就是把原矩阵转置)。两个分支可以是结构相同但参数独立的编码器,也可以是共享底层权重的孪生结构。我倾向于认为这里用的是独立编码器加一个投影头,因为细胞和基因的输入分布差异太大——细胞的测序深度归一化之后分布相对连续,基因的表达分布则是长尾的,零值比例极高,共享权重容易让一侧的表征被另一侧带偏。
潜空间维度需要对齐,否则没法算代价矩阵。常见的做法是把两侧都投影到同一个维度d,d一般在32到128之间。这个维度不是随便定的:太小,表达程序的细微差异表达不出来,稀有亚群会混在一起;太大,运输计划的求解规模上去了,而且过拟合噪声的风险增加。我自己的经验是,细胞数在几千量级时d取64比较稳,上万细胞时可以到128,再大收益就不明显了。
重建这一侧,细胞分支要重建原始表达(或者归一化后的表达),基因分支同理。双侧重建设计的意义在于给编码器提供足够的梯度信号,避免它只学到一个对运输任务有利但丢失信息的退化表示。这点很像多任务学习里的辅助损失,主任务是OT对齐,重建是保证表示不塌缩的约束。
3.2 代价矩阵的构造比想象中讲究
代价矩阵C的元素C_ij表示基因i的嵌入和细胞j的嵌入之间的距离。看起来直接算欧氏距离就完了,实际上有几个细节会显著影响结果。
第一是归一化。如果两侧嵌入的模长差异大,距离就会被模长主导,方向信息被淹没。常见做法是先做L2归一化再算距离,或者用余弦距离。第二是尺度缩放。Sinkhorn对代价矩阵的绝对数值很敏感,因为要对exp(-C/epsilon)做运算,C太大会直接下溢成零,整个运输计划变成NaN。工程上一般会在算完距离后除以一个尺度因子,让C的取值范围落在合理区间。
第三是稀疏性处理。原始表达矩阵里大量零值,如果直接把零当成真实观测参与编码,基因分支的输入会极度稀疏,学出来的嵌入趋同。一种处理是先做基因过滤,只保留在足够多细胞中表达的基因;另一种是在代价矩阵上加mask,对观测到的表达位置给更高权重。具体用哪种,取决于数据质量,我的建议是两个都试,看聚类指标哪个稳。
| 构造方式 | 优点 | 风险 |
|---|---|---|
| 原始欧氏距离 | 实现简单 | 受模长和尺度影响大 |
| L2归一化后欧氏 | 缓解模长偏差 | 丢失幅度信息 |
| 余弦距离 | 只关注方向 | 对零向量不友好 |
| 距离后接尺度缩放 | 数值稳定 | 缩放系数需调 |
3.3 损失函数的四项组成与权重平衡
从这类框架的通用设计看,总损失大概率由四块构成:细胞侧重建损失、基因侧重建损失、OT运输损失、以及一个正则项(可能是聚类一致性或者嵌入的熵约束)。重建损失用均方误差或者负二项似然都行,单细胞计数数据用负二项更贴合过离散特性,但实现复杂度高,很多工作图省事还是用MSE在log归一化后的数据上算。
OT损失通常就是运输计划与代价矩阵的内积,最小化它等价于让高权重的基因-细胞对之间的嵌入距离更小。正则项的作用是防止所有细胞塌到同一个点上——如果没有约束,模型完全可以把所有嵌入都设成零,代价矩阵全零,运输计划均匀,损失也能降下去,但这样的表示毫无用处。常见的约束是对聚类分配做熵最小化,或者加一个均匀性惩罚。
权重平衡是实操中最费时间的部分。四块损失的量级差好几个数量级是常事,尤其是OT损失,它的绝对值跟细胞数、基因数都相关。我的做法是先各自单独跑,记录每块损失收敛时的量级,再按比例设初始权重,然后固定其他权重只调OT那一项。warm-up策略也很有用:前若干个epoch只开重建损失,让嵌入先有个像样的初始结构,再引入OT,否则一开始代价矩阵基本是噪声,运输计划会把模型带偏。
4. 无监督训练流程与几个必须调的超参
4.1 预处理决定了一半的结果
这一步没有捷径,也不存在"模型够强就能弥补预处理"的说法。标准的流程是:过滤掉表达基因数过少的细胞(比如少于200个基因的)、过滤掉在极少数细胞里表达的基因,然后做文库大小归一化,再取log1p。归一化的目的是去掉测序深度的影响,log变换是压缩长尾动态范围。这两步做完,数据才勉强接近模型能处理的分布。
高变基因的选择很关键。全基因集动辄两万个,直接喂进去计算量和噪声都受不了。常规做法是选2000到3000个高变基因,选择方法有基于方差的、基于均值-方差关系的。这里有个坑:如果数据里有多个批次,高变基因的选择必须在批次内做再合并,否则选出来的基因会偏向表达量高的那个批次。我见过有人图省事直接在合并后的矩阵上选,结果下游聚类完全被批次主导。
要不要做scale(z-score标准化)得看模型设计。如果编码器第一层是线性的,scale之后每个基因的方差被拉到同一量级,训练更稳;但如果后面接的是非线性激活,scale有时候反而会放大噪声基因的影响。稀疏矩阵转稠密也是内存大户,两万乘五万的双精度稠密矩阵大概要8GB,实际操作中要么用稀疏张量,要么在batch层面做转换。
4.2 熵正则系数与Sinkhorn迭代次数
这两个参数是绑在一起的。epsilon控制运输计划的软硬程度,迭代次数控制求解的收敛精度。经验上,epsilon的取值应该和代价矩阵的量级匹配——如果代价矩阵的元素大概在0到10之间,epsilon取0.5到2比较合适。太小会出现数值下溢,太大运输计划趋于均匀,基因和细胞之间的区分度就没了。
调这个参数有个实用的判断方法:看运输计划矩阵的行熵和列熵。如果熵接近均匀分布的理论上界,说明太软了;如果熵极低、矩阵近乎one-hot,说明太硬了,数值上很可能已经在溢出边缘。健康的运输计划应该是稀疏但有结构的,大部分质量集中在少数几个高关联对,但每个细胞还能分到来自多个基因的贡献。
迭代次数一般20到100次就够,再多收益递减。实际实现里Sinkhorn是在log域跑的,这样能避免exp下溢,代价是每次迭代要多做几次logsumexp,速度慢一点但数值稳得多。如果你的loss里出现了NaN,九成是这一块没处理好。
4.3 交替优化的节奏与早停
训练节奏上,我建议分三段:第一阶段只跑重建,跑个几十个epoch让嵌入收敛到一个合理的初始结构;第二阶段加入OT损失,权重从小到大线性升温;第三阶段固定编码器,只微调聚类头或者做一致性约束。这个节奏不是论文里必须的,但从工程角度能明显降低训练震荡。
早停的判据不能只看总损失,因为OT损失和重建损失的下降趋势经常是背离的。我一般同时监控三个量:重建损失、运输计划的稀疏度、以及一个无监督的内部指标(比如嵌入的轮廓系数)。三个量里有两个开始恶化就停。另外要注意,batch size对OT的影响很大,batch太小运输计划的边缘约束失去意义,batch太大显存吃不消,几千个细胞一批是比较实际的区间。
提示:如果显存只够跑很小的batch,可以在batch之间维护一个动量版的运输计划,用滑动平均来缓解小batch带来的估计偏差,这个技巧在实现上不复杂,效果比单纯调参明显。
5. 评估体系:光看ARI会漏掉什么
5.1 聚类指标背后的偏差
ARI和NMI是这类工作的标配,但这两个指标对类别不平衡极其敏感。假设一个数据集有十个细胞类型,其中一个是只占1%的稀有类型,模型把这一整个类型并进邻近的大簇,ARI可能只掉零点零几,看起来"性能几乎没受影响",但在生物学上这个错误可能是致命的——稀有类型往往才是研究的重点,比如某个处于过渡态的细胞群、或者一小撮耐药细胞。
所以看结果时一定要拆开看。除了整体ARI,我会额外算每个类别的F1,尤其是占比最小的那几类。还有一个经常被忽略的指标是聚类的稳定性:换一个随机种子重跑,或者对数据做bootstrap重采样,看聚类结果的一致性。如果ARI在多次运行间波动超过0.05,说明模型对这个数据集不够鲁棒,这时候整体指标再高也不能太当真。
嵌入空间的可视化也要看,但不能只看t-SNE或UMAP的图。这两种降维本身会扭曲全局结构,簇之间的相对距离不可信。我一般会额外画一个嵌入的kNN保真度曲线,看高维空间里的近邻在低维里还剩多少,这个指标比肉眼看图靠谱得多。
5.2 稀有类型的召回怎么专门验
一个实用的做法是构造混淆矩阵,然后重点看稀有类所在的行。如果某一行的非对角元素集中指向某一个特定的大类,说明模型把两个类型混了,这时候要去查这两个类型在marker基因上是不是本来就有重叠表达。如果非对角元素分散在多个类上,那更可能是这个稀有类型本身的信号太弱,需要回头看预处理阶段是不是把它过滤掉了。
另一个手段是做留出验证:从数据里人为抽掉一部分某个类型的细胞不参与训练,看模型能不能把剩下的同类细胞聚到一起,以及能不能把抽掉的那些在推断时正确归位。这个测试对无监督方法有点苛刻,但能暴露模型是不是真的学到了生物学信号,还是只是记住了样本。
5.3 基因侧表示怎么验证才不算自说自话
基因嵌入的验证比细胞侧难,因为没有现成的金标准。可行的路子有几条。一是拿基因嵌入做聚类,看聚出来的模块和已知通路的富集程度,如果模块里富集的通路显著性明显高于随机分组,说明嵌入抓到了功能结构。二是看已知的marker基因对在嵌入空间里的距离,比如某类细胞的经典marker应该彼此靠近,这个可以量化成一个排序指标。三是做跨数据集的一致性检验:在数据集A上学到的基因嵌入,拿到数据集B上(同一组织类型)算基因间的相似度,看两边的相似度矩阵相关性高不高。
这里要小心一个陷阱:基因嵌入的评估很容易陷入循环论证。你用差异表达选出的marker去验证嵌入,而嵌入的训练目标本身就包含了表达模式的匹配,两者天然相关。更严谨的做法是用外部知识,比如公共数据库里的通路注释或者蛋白质互作网络,做完全独立的验证。
6. 复现时最容易翻车的三个地方
6.1 代价矩阵数值爆炸与NaN
这是最高频的问题,症状是训练几个epoch后loss突然变成NaN,或者运输计划矩阵出现整行整列的零。根因通常是代价矩阵的元素太大,导致exp(-C/epsilon)下溢。排查顺序是:先打印代价矩阵的最大值、最小值和均值,看量级是否合理;再检查epsilon是不是设得太小;最后确认编码器输出有没有做归一化。
修复手段有几种,从简单到复杂排列。最直接的是给代价矩阵加一个全局的尺度缩放,比如除以它的中位数。其次是改用log-domain的Sinkhorn实现,把整个迭代搬到对数空间,这是最稳的方案。再进一步是在编码器输出后加LayerNorm,从源头上控制嵌入的数值范围。我自己的习惯是这三条一起上,因为单靠一条经常在换数据集时又出问题。
还有一个隐蔽的坑:如果数据里有全零的细胞或者全零的基因(过滤没做干净),它们的嵌入会是零向量,和其他所有点的距离都一样,运输计划里这一行或这一列就会退化成均匀分布,进而污染整个求解。所以预处理阶段的过滤不是可选项,是必须做的。
6.2 批次效应悄悄混进运输计划
多批次数据直接合并训练,运输计划会把批次差异当成生物学差异来对齐。表现是聚类结果按批次分开,而不是按细胞类型分开。这个问题在单批次数据上完全看不出来,一到真实项目就暴露。
应对思路分两层。数据层面,先做批次校正(比如基于分位数归一化或者基于互近邻的方法),再喂给模型;模型层面,可以在编码器里加批次协变量,或者对嵌入加一个对抗性的批次判别损失。前者实现简单但可能过度校正,把真实的生物学差异也抹掉;后者更精细但调参麻烦。我的经验是先做轻量的数据层校正,然后在评估阶段专门检查批次混合度(比如看每个cluster里各批次的占比是否均衡),如果还有明显偏移再上模型层的方案。
要提醒的是,批次校正和OT对齐之间存在耦合。你校正得过度,运输计划的区分度会下降;校正不足,计划又被批次主导。这个平衡点没有通用答案,只能靠具体数据试。
6.3 运输塌缩与不平衡数据
运输塌缩指的是运输计划里少数几个细胞吸收了绝大部分质量,其余细胞几乎分不到。这在细胞类型极度不均衡的数据上很常见——大簇的细胞表达模式相似,代价低,质量都往那边流。结果是稀有类型的嵌入得不到足够的梯度信号,越训越模糊。
一个缓解办法是改用非平衡最优传输,放松边缘约束,允许供需不严格相等,加一个KL散度的惩罚项来控制松弛程度。这个改动在实现上不复杂,但需要多调一个松弛系数。另一个办法是在采样阶段做类别平衡,虽然无监督设定下不知道真实类别,但可以按表达谱的聚类先粗分一下,再做分层采样。
还有个更朴素的手段:在代价矩阵上给稀有区域加一个基于密度的权重,密度低的区域权重高一点,抑制质量过度集中。这个做法没有理论保证,但在实际数据上经常有效,属于工程上的"土办法"。
7. 这类方法的适用边界和我看好的延伸方向
说几句实在的边界问题。最优传输的求解复杂度随矩阵规模上升,基因数乘细胞数的代价矩阵在几万乘几万的时候,即使有Sinkhorn加速,单次迭代的开销也不小。这意味着这类方法在当前实现下更适合中等规模的数据集,几十万细胞的图谱级数据要么做降采样,要么做分块近似,后者会损失全局最优性。这是方法本身的限制,不是调参能解决的。
另一条边界是数据类型。这套框架假设基因和细胞之间存在可对齐的表达结构,对于多组学数据(比如同时有转录组和表观组的),两侧的分布差异更大,直接套用效果会打折,需要针对模态特性重新设计代价函数。空间转录组也是个有意思的场景——空间位置信息可以作为额外的约束加进运输计划里,让邻近的细胞在计划矩阵里也保持相近的权重分布,这个方向我觉得比单纯堆模型深度更有价值。
再往远看,这类双视角联合表示的思路和现在流行的大型基础模型是有结合空间的。基础模型提供了强大的基因侧先验表示,OT提供了一种在无标签条件下把新数据对齐到这个先验上的机制。如果能把两者接起来,小样本、新组织的分析门槛会低很多。当然这只是方向性的判断,落地还有很多工程问题要解决。
我自己在这个方向上折腾了一段时间,最深的体会是:模型结构的创新往往不是瓶颈,瓶颈在数据预处理和数值稳定性这些"脏活"上。同一个方法,预处理做得好和做得糙,结果能差出十几个点的ARI。所以如果你打算复现这类工作,我的建议是把至少一半的时间花在数据质量检查上——画表达分布、查批次混合、看过滤前后的细胞和基因数量变化,这些看似枯燥的步骤,回报比调模型超参高得多。另外一个实用的小技巧是,在正式训练前先用一小部分数据(比如随机抽两千个细胞)跑通全流程,确认没有NaN、运输计划的熵在合理范围、损失曲线不发散,再上全量数据。这个习惯帮我省掉了大量白等的训练时间。
最后提一句评估的事。无监督方法的评估永远是相对的,不同数据集上的指标不能横向比较。与其纠结ARI小数点后第二位,不如多花时间看模型输出的生物学解释是否站得住——聚出来的簇有没有清晰的marker、基因模块能不能对上已知通路、稀有群体的边界是不是稳定。这些东西比一个漂亮的数字更能说明方法到底学到了什么。