2. CCA原理

好,我们来啃CCA这块硬骨头。说实话刚开始接触CCA时,我对着数学推导看了好几天才明白它在干嘛。但一旦搞懂,你会发现它其实就是把两个数据集之间的关系“拧”成一个可优化的问题。咱们一步步来。

CCA到底想干嘛?

典型相关分析(Canonical Correlation Analysis)本质上是找两组变量之间的线性组合,使得组合后的相关系数最大化。你想想看,假设你有一组基因表达矩阵X和一组蛋白质丰度矩阵Y,你想知道它们之间有没有“整体”的关联——CCA就是干这个的。

它不关心单个基因和单个蛋白的关系,它要找的是投影方向:把X投影到一个向量u上,把Y投影到向量v上,然后让投影结果的相关性最大。嗯,这里有个关键点:你找的不是一组(u,v),而是一系列相互正交的投影对,就像PCA那样。

我的习惯:刚开始学CCA时,我总把它和PCA混。记住一句话:PCA找自身方差最大方向,CCA找两组间相关性最大方向。

数学基础:协方差结构

既然是相关性,那自然离不开协方差矩阵。假设X是n×p矩阵(n个样本,p个变量),Y是n×q矩阵。我们定义:

  • ΣXX = Cov(X) —— X的自协方差矩阵(p×p)
  • ΣYY = Cov(Y) —— Y的自协方差矩阵(q×q)
  • ΣXY = Cov(X,Y)—— X和Y的互协方差矩阵(p×q)

CCA要做的就是在线性组合 a = uTX 和 b = vTY 之间,最大化它们的皮尔逊相关系数:

ρ = corr(a, b) = (u<sup>T</sup> Σ<sub>XY</sub> v) / sqrt( (u<sup>T</sup> Σ<sub>XX</sub> u) (v<sup>T</sup> Σ<sub>YY</sub> v) )

我当年第一次看到这个式子觉得头大,但其实它就是个广义瑞利商,跟PCA里的瑞利商一个味道,只是分母多了两组方差约束。

目标函数:最大化相关系数

为了让优化问题有唯一解,我们通常约束投影后的方差为1,即:

u<sup>T</sup> Σ<sub>XX</sub> u = 1
v<sup>T</sup> Σ<sub>YY</sub> v = 1

这样一来目标函数就简化为:

maximize  u<sup>T</sup> Σ<sub>XY</sub> v
subject to  u<sup>T</sup> Σ<sub>XX</sub> u = 1,  v<sup>T</sup> Σ<sub>YY</sub> v = 1

怎么做?拉格朗日乘子法。定义拉格朗日函数:

L = u<sup>T</sup> Σ<sub>XY</sub> v - (λ/2)(u<sup>T</sup> Σ<sub>XX</sub> u - 1) - (μ/2)(v<sup>T</sup> Σ<sub>YY</sub> v - 1)

对u和v求偏导置零,得到:

Σ<sub>XY</sub> v = λ Σ<sub>XX</sub> u  (1)
Σ<sub>YX</sub> u = μ Σ<sub>YY</sub> v  (2)

由(1)左乘uT、由(2)左乘vT,再利用约束条件,可以推出 λ = μ = uT ΣXY v = ρ。也就是说,拉格朗日乘子就是相关系数本身。

关键洞察:CCA把两组变量间的相关关系转化成了一个广义特征值问题。这个转化在单细胞数据分析中尤为重要,因为基因表达矩阵通常存在高维稀疏性,直接算相关系数会爆炸。

特征向量求解:SVD视角

实际工程中我们不会手撕拉格朗日方程,而是用SVD一步到位。先做“白化”变换:定义

K = Σ<sub>XX</sub><sup>-1/2</sup> Σ<sub>XY</sub> Σ<sub>YY</sub><sup>-1/2</sup>

然后对K做奇异值分解:

K = U D V<sup>T</sup>

其中D是对角矩阵,元素d1 ≥ d2 ≥ ... ≥ 0,它们正是相关系数ρ。而投影向量u和v可以通过逆变换恢复:

u = Σ<sub>XX</sub><sup>-1/2</sup> U<sub>[:,1]</sub>
v = Σ<sub>YY</sub><sup>-1/2</sup> V<sub>[:,1]</sub>

第一对典型变量就对应最大的奇异值。后续的典型变量通过对U、V取第2、3……列得到,且彼此正交。

我曾经踩过的坑:ΣXX和ΣYY在单细胞数据中经常是奇异的(变量数远大于样本数)。如果不做正则化或者降维,直接求逆会炸掉。我在做scRNA-seq批次校正时就是忘了检查秩,结果跑出一堆NaN。记住:CCA前至少对每个数据集做PCA降维到样本数-1以下。

CCA核心流程图

CCA数学流程:从协方差到典型变量 输入矩阵 X, Y 计算 ΣXX, ΣYY, ΣXY 构造 K = ΣXX-1/2 ΣXY ΣYY-1/2 SVD分解 K = U D VT 恢复投影向量 u=ΣXX-1/2U, v=ΣYY-1/2V 典型变量 a=uTX, b=vTY SVG中标注符号说明 ① D的对角元素即为相关系数ρ ② 后续典型变量取U,V后续列 ③ 必须满足样本数 > 变量数

实际计算中的注意事项

CCA求解其实只涉及矩阵运算,但工程实现时有几个坑。我列一下:

步骤 典型问题 我的建议
计算协方差 数据未中心化,结果偏差 一定先对X、Y各列减去均值
矩阵求逆/开方 奇异矩阵导致无穷大 用伪逆或加正则项(如0.001I)
SVD分解 K矩阵数值不稳定 先用PCA降到d=min(p,q,n-1)维
多对典型变量 正交性丢失 用SVD自然保证正交,别手工迭代
快查技巧:如果只想看最大相关系数,可以跳过完整的SVD,直接用幂迭代法求K的最大奇异值。在数据量极大时这能省一大半时间。

总结一下CCA的数学骨架:最大化投影相关性 → 拉格朗日乘子 → 广义特征值 → SVD一步到位。你只要记住这个链条,在实际代码里调个np.linalg.svd就完事了。下一节我们会动手用Python实现CCA,顺便看看它在MNN批次校正中到底起了什么作用。