Harmony算法原理(二):软聚类、RBF核与误差修正项

上一章我们把Harmony的整体框架讲清楚了。这一章,我打算深入三个核心机制——软聚类、RBF核和误差修正项。说白了,这三个东西才是Harmony真正「去批次效应」的杀手锏。

我自己刚开始用Harmony时,光调参数就折腾了好几周。后来把这三个机制拆开理解,才发现原来每个设计都有它的道理。咱们一个一个来看。

1. 从硬分配到软聚类:为什么非要「模糊」?

传统的k-means大家都很熟吧?每个细胞只能属于一个簇,非此即彼。这叫硬聚类。

但现实数据哪有这么干净?一个细胞可能处于两种细胞类型的过渡状态,也可能因为批次效应被压到了两个簇的边界上。你硬要给它贴一个标签,后续整合就会出问题。

Harmony用的是模糊C均值(Fuzzy C-Means, FCM)。每个细胞不是「属于某个簇」,而是「以多少概率属于每个簇」。

数学上看,我们有一个隶属度矩阵 U,维度是 n_cells × n_clusters。矩阵里每个元素 uik 表示细胞 i 属于簇 k 的概率。满足两个条件:

  • 0 ≤ uik ≤ 1
  • 每行之和为1(所有概率加起来等于100%)

这个初始化怎么做的?我习惯先用k-means跑一个粗糙的分簇,然后用距离的倒数来初始化隶属度。你想想看,离某个簇中心越近,初始隶属度就越高。这样迭代起来收敛更快。

核心思路:软聚类让Harmony在整合时「留有余地」。一个细胞可以同时参考多个簇的信息,不会因为硬性分错而带偏全局。

我的经验:nclust 参数一般设 3~8 就行。单细胞数据里细胞类型通常就这么多。设太大反而会让每个簇内细胞太少,隶属度估计不稳定。

2. RBF核:线性度量不够用了

好,现在有了隶属度矩阵,我们要算细胞之间的相似度。问题来了:批次效应经常是非线性的。

举个例子,两个相同类型的细胞,在不同批次里可能被测量系统扭曲成完全不同的分布。你用欧氏距离去算,它们「看起来」差得很远。但实际上它们应该是「相似」的。

怎么办?用核方法。

Harmony选的是高斯径向基函数核(RBF Kernel)。公式长这样:

K(x, y) = exp(-||x - y||² / (2 * σ²))

直观理解很简单:两个细胞在原始空间里距离越远,核函数输出的相似度就越低。但它是非线性衰减的——近的点给高权重,远的点权重迅速归零。这比简单地用欧氏距离做线性缩放要灵活得多。

这里面有个关键参数 σ(sigma),也就是带宽。它控制着「多近算近」。

  • σ 太小:只有离得非常近的细胞才被当作相似,整合效果局部化,全局批次效应去不掉。
  • σ 太大:所有细胞都被当作相似,相当于没做核变换,退化成线性度量。

Harmony内部其实做了一个自适应带宽——它会根据每个簇内细胞的离散程度,动态调整σ。我在项目中遇到过几次:用固定σ跑出来的整合结果总有点「拧巴」,后来开了自适应带宽,一下就顺了。

避坑指南:我曾经在一个人脑数据上直接用默认σ,结果神经元和胶质细胞被混在了一起。后来发现是σ设太大,导致跨类型的细胞也被误判为相似。所以sigma这个参数,我建议你先设一个较小的值(比如0.05),再逐步增大,观察分群变化。

3. 误差修正项:别让迭代跑偏了

有了软聚类的隶属度,有了RBF核算的相似度,接下来就是迭代优化了。但这里有个隐患——迭代次数多了,聚类可能会过度拟合局部结构,反而把批次效应「学」进去了。

Harmony加入了一个误差修正项,也叫正则化项。它的作用是惩罚那些「过于极端」的聚类结果。

具体来说,Harmony在目标函数里加了一项:

J = 原始聚类损失 + λ * 正则化项

其中 λ(lambda)控制修正的强度。λ 越大,聚类结果越「保守」,不容易被个别异常细胞带偏。

我自己的经验是:λ 设为 0.5~1.0 通常效果不错。如果数据里批次效应特别严重,可以适当加大到 2.0。但别超过 5.0,否则聚类结果会过于平滑,真实生物学差异也被抹掉了。

正则化项的具体形式是岭回归(Ridge)类型的惩罚——对聚类中心的偏移做限制。每次迭代更新时,中心不能跑得太远,相当于加了一个「阻尼器」。

收敛保证:误差修正项让目标函数在迭代过程中单调递减(至少不增加)。这意味着算法一定会收敛,不会出现振荡或发散。这在工程实现上非常重要——你不需要手动设置最大迭代次数,算法自己会停。

4. 三者的协同:一张图看清

好,三个机制都讲完了。它们不是各自独立的,而是串在一条流水线上。我画了一张图,把整个逻辑串起来:

Harmony 核心模块协同流程 输入 PCA 坐标 软聚类 (FCM) 计算隶属度矩阵 U RBF 核变换 非线性相似度 + 自适应带宽 误差修正 (岭回归正则化) 关键参数 • nclust:簇数量 (3~8) • sigma:RBF带宽 (0.05~0.5) • lambda:正则化强度 (0.5~2.0) • 三者共同决定整合质量 迭代至收敛

这张图里,数据从PCA坐标进入,先经过软聚类得到隶属度矩阵,再用RBF核做非线性变换,然后通过误差修正项约束迭代过程。三个模块循环往复,直到目标函数收敛。我每次给团队讲这部分都直接拿这张图——比看十页公式都管用。

5. 代码里怎么体现?

说了这么多理论,咱们看看在R里怎么实际控制这三个机制。Harmony的核心函数就是 HarmonyMatrix,相关参数直接对应:

library(harmony)

result <- HarmonyMatrix(
  data_mat = pca_coords,       # PCA降维后的坐标
  meta_data = sample_info,     # 样本元数据(批次信息等)
  vars_use = "batch",          # 要矫正的批次变量
  nclust = 5,                  # 软聚类的簇数量
  lambda = 0.8,                # 误差修正项的正则化强度
  sigma = 0.1,                 # RBF核的带宽参数
  max_iter = 10,               # 最大迭代次数(通常5~10就收敛)
  early_stop = TRUE            # 早停:收敛后自动停止
)

调参建议:我一般先固定 nclust=5,sigma=0.1,lambda=0.8 跑一遍。然后看UMAP图里批次是否混在一起。如果混得不够好,先降 sigma(到0.05),再升 lambda(到1.2)。大部分数据集在这个范围内都能找到合适的组合。

你可以把 nclust、sigma、lambda 理解成三个旋钮。它们分别对应本章的三个核心机制:

参数 对应机制 作用 典型范围
nclust 软聚类 控制分簇粒度,影响隶属度矩阵的维度 3 ~ 8
sigma RBF核 控制非线性相似度的衰减速度 0.05 ~ 0.5
lambda 误差修正项 控制正则化强度,防止过拟合 0.5 ~ 2.0

嗯,到这里,Harmony的三个核心机制就全讲透了。你想想看,软聚类负责「模糊归属」,RBF核负责「非线性度量」,误差修正项负责「稳定收敛」——三者缺一不可。搞懂这些,你再去调参就不是盲人摸象了。

一个重要提醒:别一次性把三个参数都大幅改动。我习惯每次只调一个,跑完看结果再调下一个。三个一起动,你根本不知道是哪个起了作用,debug会非常痛苦。