scvi-tools 深度学习单细胞分析:工作原理

scvi-tools 深度学习单细胞分析:工作原理

Written By
技能练习生
技能练习生

深度学习如何解决单细胞分析的难题

本章深入讲解 scvi-tools 的数学原理和技术细节,帮助你从理论层面理解这些工具的优势和局限性。

注意:本章需要基本的概率论和机器学习知识。如果你主要关注应用而非理论,可以跳过本章直接进入常见问题部分。

5.1 核心思想:概率生成模型

5.1.1 为什么需要生成模型?

传统方法的问题

  • PCA:假设数据呈线性分布,无法捕获复杂的非线性关系
  • t-SNE/UMAP:仅用于可视化,不提供可解释的概率模型
  • Harmony:启发式方法,缺乏明确的数据生成假设

scvi-tools 的方案

  • 明确建模单细胞数据的生成过程
  • 基于概率理论,提供可解释的参数
  • 自然地处理零膨胀过度离散特性

5.1.2 单细胞数据的特性

单细胞 RNA-seq 数据具有三个关键特性:

  1. 稀疏性(Sparsity):大部分基因表达量为 0
  2. 过度离散(Over-dispersion):方差大于均值(违反泊松分布假设)
  3. 批次效应(Batch effects):技术噪声与生物学信号混杂

这些特性使得传统的统计方法(如 t 检验、线性回归)效果不佳。

5.2 变分自编码器(VAE)基础

5.2.1 VAE 的核心思想

VAE(Variational Autoencoder)是一种生成模型,通过编码器 - 解码器架构学习数据的潜在表示。

架构图

输入数据 x (基因表达)

编码器 Encoder (神经网络)

潜在变量 z (低维表示) ← 这是我们想要的"批次校正后"的表示

解码器 Decoder (神经网络)

重构数据 x' (重建的基因表达)

训练目标:最小化重构误差 + 潜在空间正则化

5.2.2 数学形式

编码器:学习映射 q_φ(z|x)

  • 将高维观测 x 映射到潜在空间 z
  • 输出潜在变量的均值和方差:μ(x), σ²(x)

解码器:学习生成分布 p_θ(x|z)

  • 从潜在 z 重建观测 x
  • 对于单细胞数据,使用负二项分布(Negative Binomial)

证据下界(ELBO)

ELBO = E_q[log p(x|z)] - KL(q(z|x) || p(z))
       ↑ 重构误差        ↑ 潜在空间正则化
  • 第一项:重建数据的准确性
  • 第二项:KL 散度,使潜在分布接近标准正态分布

5.2.3 scVI 的改进

scVI 在标准 VAE 基础上做了两个关键改进:

1. 使用负二项分布作为解码器

# 标准高斯 VAE(不适合计数数据)
x ~ Normal(μ(z), σ²(z))

# scVI 的负二项分布(更适合单细胞数据)
x ~ NegativeBinomial(μ(z), θ(z))

优势

  • 自然建模计数数据的离散性
  • 捕捉过度离散特性
  • 零膨胀特性(通过额外参数)

2. 显式建模批次效应

# 解码器同时考虑潜在表示和批次信息
μ = decoder(z, batch)
θ = decoder(z, batch)

效果

  • 解耦生物学变异(z)和技术噪声(batch)
  • 潜在空间中批次被对齐,但保留真实的生物学差异

5.3 scVI 模型详解

5.3.1 完整概率图模型

z (潜在变量) ~ Normal(0, I)

l (库大小) ~ LogNormal(μ_l, σ_l²)

x_g (基因 g 表达) ~ NB(μg(z, b), θg(z, b))

符号说明

  • z:细胞的潜在表示(无批次效应)
  • l:每个细胞的测序深度(库大小)
  • b:批次标签
  • μg, θg:基因 g 的负二项分布参数

5.3.2 批次校正机制

scVI 通过条件化实现批次校正:

# 解码器网络
class Decoder(nn.Module):
    def __init__(self, n_batches):
        self.batch_embedding = nn.Embedding(n_batches, latent_dim)

    def forward(self, z, batch):
        # 将批次信息作为额外输入
        batch_effect = self.batch_embedding(batch)
        z_with_batch = torch.cat([z, batch_effect], dim=-1)

        # 预测基因表达
        μ, θ = self.network(z_with_batch)
        return μ, θ

关键:潜在变量 z 不依赖于批次,但解码器可以调整预测以匹配批次特性。

5.3.3 训练过程

# 伪代码
for epoch in range(max_epochs):
    for batch_x, batch_index in data_loader:
        # 1. 编码器:推断潜在变量
        z_mean, z_var = encoder(batch_x)

        # 2. 采样(重参数化技巧)
        z = z_mean + z_var * ε  # ε ~ Normal(0, 1)

        # 3. 解码器:重建数据
        μ, θ = decoder(z, batch_index)

        # 4. 计算损失
        recon_loss = negative_binomial_nll(batch_x, μ, θ)
        kl_loss = kl_divergence(z, z_mean, z_var)
        loss = recon_loss + kl_loss

        # 5. 反向传播
        loss.backward()
        optimizer.step()

5.4 scANVI:半监督学习

5.4.1 标签转移原理

scANVI 在 scVI 基础上增加了一个标签解码器

潜在变量 z → 细胞类型标签预测器 → p(y|z)

联合训练目标

ELBO_total = ELBO_scVI + α * CrossEntropy(y_true, y_pred)

有标签细胞

  • 同时优化重构损失和分类损失
  • 强制相同细胞类型的细胞在潜在空间中接近

无标签细胞

  • 仅优化重构损失
  • 通过编码器 - 解码器共享参数,间接获得标签信息

5.4.2 数学形式

# 标签解码器
y ~ Categorical(softmax(W * z + b))

# 有标签细胞的损失
L_labeled = ELBO_scVI + CrossEntropy(y, y_true)

# 无标签细胞的损失
L_unlabeled = ELBO_scVI

# 总损失
L_total = Σ L_labeled + Σ L_unlabeled

5.5 多模态模型(totalVI/MultiVI)

5.5.1 共享潜在表示

核心思想:不同模态共享同一个潜在空间 z

z (共享潜在表示)
    ├─→ RNA 解码器 → x_rna ~ NB(μ_rna(z), θ_rna(z))
    └─→ Protein 解码器 → x_protein ~ NB(μ_protein(z), θ_protein(z))

优势

  • 自然捕捉 RNA-蛋白关联
  • 互补信息相互增强
  • 去噪:利用 RNA 预测蛋白质表达

5.5.2 totalVI 架构

输入:
  - RNA counts: x_rna (n_genes)
  - Protein counts: x_protein (n_proteins)
  - Batch: b

编码器:
  z_rna = Encoder_rna(x_rna, b)
  z_protein = Encoder_protein(x_protein, b)
  z = concat(z_rna, z_protein)  # 融合表示

解码器:
  x_rna' = Decoder_rna(z, b)
  x_protein' = Decoder_protein(z, b)

损失:
  L = NLL(x_rna, x_rna') + NLL(x_protein, x_protein') + KL(z)

5.6 差异表达分析的贝叶斯方法

5.6.1 传统 DE 分析的问题

t 检验 / Wilcoxon

  • 忽略数据的离散特性
  • 未考虑批次效应
  • p 值容易受样本量影响

5.6.2 scVI 的 DE 方法

贝叶斯假设检验

H0: μ1 = μ2  # 两组无差异
H1: μ1 ≠ μ2  # 两组有差异

贝叶斯因子(Bayes Factor)

BF = P(data|H1) / P(data|H0)
  • BF > 3:强证据支持差异表达
  • BF > 10:极强证据

优势

  • 自然考虑不确定性
  • 无需多重假设检验校正
  • 更稳健(适合小样本量)

5.6.3 实现

# 从后验分布采样
samples = model.get_normalized_expression(
    adata,
    n_samples=1000  # 采样次数
)

# 计算贝叶斯因子
for gene in genes:
    # 组 1 的后验分布
    p1 = samples[group1, gene, :]
    # 组 2 的后验分布
    p2 = samples[group2, gene, :]

    # 贝叶斯因子(简化版)
    bf = compute_bayes_factor(p1, p2)

    # 对数折叠变化
    lfc = p1.mean() - p2.mean()

5.7 性能与可扩展性

5.7.1 训练复杂度

时间复杂度

  • 编码器:O(n × g × h) # n: 细胞数,g: 基因数,h: 隐藏层大小
  • 解码器:O(n × g × h)
  • 总计:O(n × g × h) × epochs

空间复杂度

  • 参数量:O(g × h) # 与细胞数无关
  • 激活值:O(n × h) # 流式处理避免存储全部

实际性能

细胞数CPU 时间GPU 时间内存占用
10K5 分钟1 分钟8 GB
100K45 分钟5 分钟16 GB
1M6 小时40 分钟32 GB

5.7.2 scArches 的加速原理

传统方法:每次添加新数据都需要从头训练

scArches

  1. 冻结参考模型的编码器和部分解码器
  2. 仅训练新数据的批次嵌入
  3. 大幅减少可训练参数
# scArches 的参数冻结策略
reference_model.train()

# 冻结编码器
for param in reference_model.encoder.parameters():
    param.requires_grad = False

# 仅训练批次嵌入
for param in reference_model.batch_embedding.parameters():
    param.requires_grad = True

效果

  • 参数量减少 90%+
  • 训练速度提升 10-50x
  • 内存占用减少 80%+

5.8 模型选择与超参数

5.8.1 超参数影响

latent_dim(潜在维度)

  • 过小(<10):信息损失,无法捕捉细微差异
  • 合适(20-40):平衡信息保留和过拟合风险
  • 过大(>50):过拟合,批次效应可能残留

n_layers / n_hidden(网络深度)

  • 过浅:无法学习复杂模式
  • 过深:训练困难,容易过拟合
  • 推荐:2-3 层,128-256 神经元

5.8.2 模型选择决策树

数据类型?
├─ scRNA-seq
│  ├─ 需要标签转移?
│  │  ├─ 是 → scANVI
│  │  └─ 否 → scVI
│  └─ 强批次效应?
│     └─ 是 → sysVI
├─ CITE-seq → totalVI
├─ scATAC-seq → PeakVI
├─ Multiome → MultiVI
└─ 空间转录组 → DestVI

5.9 局限性与未来方向

5.9.1 当前局限

计算需求

  • 最少 16GB RAM(处理 >50K 细胞)
  • GPU 推荐但非必需

学习曲线

  • 需要理解超参数影响
  • 调试相对困难

模型解释性

  • 潜在空间的生物学意义不明确
  • 难以追溯哪些基因驱动特定维度

5.9.2 未来发展

多模态整合

  • 同时整合 RNA、ATAC、蛋白质、空间信息

时间序列建模

  • 整合时间序列和伪时间数据

图神经网络

  • 利用细胞 - 细胞相互作用网络

自监督学习

  • 无需标签学习更丰富的表示

下一章

现在你已经深入理解了 scvi-tools 的工作原理。在最后一章中,我们汇总了常见问题和解决方案,助你快速排查问题。

→ 继续阅读:第六章 - 常见问题 FAQ