第4章 表示学习核心:编码器-解码器架构、动量编码器、负样本的作用

表示学习,说白了就是让模型自己学会“什么重要、什么不重要”。在空间转录组这个场景里,我们既要保留基因表达的模式,又要顾及空间位置的结构。要做到这一点,有三个核心组件绕不开:编码器-解码器架构、动量编码器,还有负样本。这一章我们逐个拆解,看看它们各自解决什么问题,又是怎么配合的。

4.1 编码器-解码器架构

编码器-解码器,听起来挺唬人,其实就是个“压缩-还原”的游戏。编码器把高维数据压成低维表示,解码器再从低维表示里把数据重建出来。训练的目标就是让重建误差尽可能小,模型被迫学会提取最关键的特征。

在空间转录组里,我习惯把基因表达矩阵和空间坐标拼接起来作为输入,编码器输出一个紧凑的隐向量,解码器再去预测每个spot的基因表达。这样一来,隐向量里既包含了基因信息,也包含了空间邻居关系。

关键点: 编码器-解码器的瓶颈维度决定了表示学习的压缩率。设得太小会丢失信息,设得太大又起不到正则化作用。我一般从 128 起步,根据下游任务效果往上调。

我刚开始做空间转录组分析时,直接拿标准的自编码器来用,结果重建误差怎么都降不下去。折腾了好几天才发现,编码器根本没接收到空间位置信号,它把每个spot当成独立的样本在学,完全忽略了相邻spot的基因表达应该更相似这个先验。后来我把空间坐标嵌入到输入里,效果立竿见影。

4.2 动量编码器

动量编码器是什么?它就是编码器的“慢速版本”。参数不是直接通过梯度下降更新的,而是通过一个动量公式一点点“挪”过去的。

# 动量编码器参数更新
for param_q, param_k in zip(encoder.parameters(), momentum_encoder.parameters()):
    param_k.data = momentum_coeff * param_k.data + (1 - momentum_coeff) * param_q.data

动量系数通常设成 0.999 或 0.995。你想想看,如果两边参数同步更新,训练很容易震荡;但让编码器走得快,动量编码器走得慢,目标表示就稳定多了。这在对比学习里尤其重要——稳定的目标表示能防止模型学到琐碎的模式。

小技巧: 动量系数可以随训练轮次逐渐增大。我习惯前 100 个 epoch 用 0.995,之后调到 0.999。这样前期适应快,后期稳定好。

我在项目中遇到过动量系数设得太大的坑。有一次我把动量系数设成 0.9999,结果跑了 50 个 epoch 损失纹丝不动。排查了半天才发现,动量编码器几乎没有更新,等于整个模型只剩编码器自己在学,对比学习变成了自编码器训练。嗯,从那以后我记住了:动量系数不是越大越好,要跟数据量和批次大小匹配。

4.3 负样本的作用

负样本是对比学习的“燃料”。没有负样本,模型就不知道“什么是不像的”,学到的表示会坍塌成一个常数——所有样本的输出都一样,这显然是我们不想要的。

在空间转录组里,负样本的选择有个特殊之处:空间邻近的spot应该作为正样本,空间距离远的spot应该作为负样本。因为邻近的spot往往共享相似的微环境,基因表达也更接近。如果用随机采样的方式选负样本,可能会把空间上很近的spot也当成负样本,这就跟我们的先验知识矛盾了。

注意: 负样本数量并非越多越好。大量低质量的负样本会引入噪声,反而降低表示质量。我建议负样本队列的大小控制在 4096 到 65536 之间,具体要看你的显存和批次大小。

我曾经因为负样本选得太随意,导致模型学到的表示全是噪声。那时候用的是全局随机采样,结果同一个组织区域的 spot 被当成负样本互相推开,下游聚类任务完全没法看。后来改成空间距离约束的采样策略,效果才正常起来。说白了,负样本的质量比数量更关键。

4.4 三者的协同工作

在一个完整的自监督表示学习框架里,这三个组件不是孤立的。编码器负责在线提取特征,动量编码器提供稳定的目标表示,负样本则作为对比信号拉近正样本、推远负样本。解码器可以加在编码器后面做重建,作为辅助任务来稳定训练。我个人习惯把重建损失和对比损失加权组合,权重比大概是 1:3。

自监督表示学习核心组件协同架构 编码器-解码器架构 动量编码器 负样本与对比学习 空间转录组数据 编码器 特征表示 解码器 重建 动量编码器 负样本队列 对比损失 动量更新 入队 编码器提取特征,解码器重建输入;动量编码器提供稳定目标;负样本队列存储多样化的对比信号。 三者共同支撑起高质量的自监督表示学习。

从上图可以看得很清楚:编码器把输入数据映射成特征表示,一路送给解码器做重建,另一路送去对比损失;动量编码器从编码器接收“慢速更新”,产生的表示被加入负样本队列;对比损失同时拉近正样本、推远负样本,反向传播优化编码器。三个组件各有分工,缺一不可。

组件 核心作用 在空间转录组中的意义