scvi-tools 深度学习单细胞分析:快速入门

scvi-tools 深度学习单细胞分析:快速入门

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

30 分钟上手你的第一个 scvi-tools 项目

本章通过一个简化案例,带你快速掌握 scvi-tools 的核心工作流程。即使没有深度学习基础,也能轻松上手。

实战案例:整合两批次 PBMC 数据

场景描述

你获得了两个批次的 PBMC(外周血单核细胞)scRNA-seq 数据:

  • 批次 1:健康供体,10X v2 化学,5,000 细胞
  • 批次 2:健康供体,10X v3 化学,8,000 细胞

目标:整合两批次数据,去除技术批次效应,保留真实的生物学差异。

预期输出

  • 整合后的低维表示(潜在空间)
  • 校正批次效应的 UMAP 可视化
  • 聚类结果和细胞类型注释
  • 差异表达基因列表

步骤 1:环境准备与数据加载 [5 分钟]

1.1 安装依赖

# 创建新环境(推荐)
conda create -n scvi-tutorial python=3.10 -y
conda activate scvi-tutorial

# 安装核心包
pip install scvi-tools scanpy anndata matplotlib seaborn

# 验证安装
python -c "import scvi; print(scvi.__version__)"

1.2 准备数据

假设你已有两个 .h5ad 文件(如果没有,可使用 scanpy 的示例数据):

import scanpy as sc
import anndata as ad

# 加载数据
batch1 = sc.read_h5ad('pbmc_batch1.h5ad')
batch2 = sc.read_h5ad('pbmc_batch2.h5ad')

# 检查数据结构
print(f"Batch 1: {batch1.n_obs} cells × {batch1.n_vars} genes")
print(f"Batch 2: {batch2.n_obs} cells × {batch2.n_vars} genes")

# 关键检查:确保 X 是原始计数(未归一化)
print(f"Data type: {batch1.X.dtype}")  # 应该是整数类型
print(f"Sample values: {batch1.X[0, :5].toarray()}")  # 查看前 5 个值

⚠️ 重要提示

  • scvi-tools 必须使用原始计数,不能是 log-normalized 数据
  • 如果你的数据已经归一化,需要重新加载原始数据或从已保存的层中恢复

1.3 合并批次

# 添加批次标识
batch1.obs['batch'] = 'batch1'
batch2.obs['batch'] = 'batch2'

# 合并数据
adata = batch1.concatenate(batch2, index_unique=None)

print(f"合并后:{adata.n_obs} cells × {adata.n_vars} genes")
print(f"批次分布:\n{adata.obs['batch'].value_counts()}")

步骤 2:数据预处理 [10 分钟]

2.1 质量控制(假设已完成)

我们假设你的数据已通过基本 QC(过滤低质量细胞和基因)。如果尚未完成,可使用单细胞 QC 技能进行预处理。

2.2 高变基因选择

# 使用 Seurat v3 方法选择高变基因(推荐)
sc.pp.highly_variable_genes(
    adata,
    n_top_genes=2000,  # 通常选择 2000-4000 个基因
    batch_key='batch',  # 考虑批次信息
    flavor='seurat_v3',
    layer='counts',  # 如果有单独的 counts 层
    subset=True  # 直接过滤到高变基因
)

print(f"高变基因数量:{adata.var['highly_variable'].sum()}")

2.3 保存原始计数

scvi-tools 需要访问原始计数,我们将其保存在单独的层:

# 确保 counts 层存在
if 'counts' not in adata.layers:
    adata.layers['counts'] = adata.X.copy()

# 验证
print(f"原始计数类型:{adata.layers['counts'].dtype}")

步骤 3:模型训练 [10 分钟]

3.1 设置 AnnData

import scvi

# 设置 scVI 模型
scvi.model.SCVI.setup_anndata(
    adata,
    layer='counts',  # 指定原始计数层
    batch_key='batch'  # 批次信息键名
)

参数解释

  • layer:原始计数所在的层(必需)
  • batch_key:批次标识的列名(整合任务必需)

3.2 初始化模型

# 创建 scVI 模型
model = scvi.model.SCVI(
    adata,
    latent_dim=30,  # 潜在空间维度(默认 10,通常 20-50)
    n_layers=2,  # 神经网络层数(默认 2)
    n_hidden=128,  # 隐藏层神经元数(默认 128)
)

超参数说明

  • latent_dim:潜在表示维度,越大保留信息越多,但过拟合风险增加
  • n_layersn_hidden:控制神经网络复杂度,通常使用默认值

3.3 训练模型

# 训练模型(GPU 自动检测并使用)
model.train(
    max_epochs=100,  # 训练轮数(默认 400,可减少用于快速测试)
    batch_size=256,  # 批大小(默认 128)
    early_stopping=True,  # 早停策略
    early_stopping_patience=10,  # 容忍轮数
    use_gpu=True  # 自动检测并使用 GPU
)

# 查看训练历史
import matplotlib.pyplot as plt
plt.plot(model.history['elbo_train'])
plt.xlabel('Epoch')
plt.ylabel('ELBO')
plt.title('Training Loss')
plt.show()

训练时间参考

  • CPU:13,000 细胞约 5-10 分钟
  • GPU:13,000 细胞约 1-2 分钟

3.4 获取潜在表示

# 提取潜在表示(批次校正后的低维嵌入)
adata.obsm["X_scvi"] = model.get_latent_representation()

print(f"潜在表示形状:{adata.obsm['X_scvi'].shape}")
# 应该是 (n_cells, latent_dim)

步骤 4:下游分析 [5 分钟]

4.1 聚类与可视化

# 使用 scVI 潜在表示进行聚类
sc.pp.neighbors(adata, use_rep='X_scvi')
sc.tl.leiden(adata, resolution=0.5)
sc.tl.umap(adata)

# 可视化
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# 按批次着色(应该混合良好)
sc.pl.umap(adata, color='batch', ax=axes[0], show=False)

# 按聚类着色
sc.pl.umap(adata, color='leiden', ax=axes[1], show=False)

plt.tight_layout()
plt.show()

预期结果

  • 左图:不同批次的细胞在 UMAP 上充分混合
  • 右图:显示出清晰的细胞簇结构

4.2 差异表达分析

scvi-tools 提供了基于零膨胀负二项分布的 DE 分析,比传统方法更准确:

# 获取每个簇的标志基因
de_df = model.differential_expression(
    groupby='leiden',
    group1='0',  # 簇 0
    group2='1'   # 簇 1
)

print(f"差异表达基因数量:{len(de_df)}")
print(de_df.head(10))  # 显示前 10 个最显著的基因

4.3 保存结果

# 保存 AnnData 对象
adata.write('pbmc_integrated.h5ad')

# 保存模型(用于后续的标签转移或新数据映射)
model.save('pbmc_scvi_model/')

print("分析完成!结果已保存。")

完整代码清单

# ============ 完整流程 ============
import scanpy as sc
import scvi
import matplotlib.pyplot as plt

# 1. 加载和合并数据
batch1 = sc.read_h5ad('pbmc_batch1.h5ad')
batch2 = sc.read_h5ad('pbmc_batch2.h5ad')
batch1.obs['batch'], batch2.obs['batch'] = 'batch1', 'batch2'
adata = batch1.concatenate(batch2, index_unique=None)

# 2. 数据准备
adata.layers['counts'] = adata.X.copy()
sc.pp.highly_variable_genes(
    adata,
    n_top_genes=2000,
    batch_key='batch',
    flavor='seurat_v3',
    layer='counts',
    subset=True
)

# 3. 训练 scVI 模型
scvi.model.SCVI.setup_anndata(adata, layer='counts', batch_key='batch')
model = scvi.model.SCVI(adata, latent_dim=30)
model.train(max_epochs=100, use_gpu=True)

# 4. 获取潜在表示和下游分析
adata.obsm["X_scvi"] = model.get_latent_representation()
sc.pp.neighbors(adata, use_rep='X_scvi')
sc.tl.leiden(adata, resolution=0.5)
sc.tl.umap(adata)

# 5. 可视化
sc.pl.umap(adata, color=['batch', 'leiden'], wspace=0.4)

# 6. 保存
adata.write('pbmc_integrated.h5ad')
model.save('pbmc_scvi_model/')

常见问题排查

❌ 问题 1:训练损失不下降

可能原因

  • 数据未正确归一化或包含异常值
  • 学习率不合适

解决方案

# 检查数据分布
import seaborn as sns
sns.histplot(adata.layers['counts'].data)
plt.xlim(0, 100)  # 查看低表达区域

# 如果发现异常值,重新过滤
sc.pp.filter_cells(adata, min_counts=1000)
sc.pp.filter_genes(adata, min_cells=10)

❌ 问题 2:批次效应仍然存在

可能原因

  • batch_key 参数未正确指定
  • 模型未充分训练

解决方案

# 检查 batch_key 是否正确
print(adata.obs['batch'].unique())

# 增加训练轮数
model.train(max_epochs=400)

❌ 问题 3:内存不足

解决方案

# 减少批大小
model.train(batch_size=64)

# 或减少高变基因数量
sc.pp.highly_variable_genes(adata, n_top_genes=1000, ...)

进阶提示

使用参考数据进行标签转移

如果你有已注释的参考数据集,可以使用 scANVI 进行标签转移:

# 假设参考数据有 'cell_type' 列
adata.obs['cell_type'] = adata.obs['cell_type'].astype(str)
adata.obs['cell_type'][adata.obs['batch'] == 'batch2'] = 'Unknown'

# 使用 scANVI
scvi.model.SCANVI.setup_anndata(
    adata,
    layer='counts',
    batch_key='batch',
    labels_key='cell_type',
    unlabeled_category='Unknown'
)

scanvi_model = scvi.model.SCANVI.from_scvi_model(model, unlabeled_category='Unknown')
scanvi_model.train(max_epochs=50)

# 获取预测的细胞类型
adata.obs['predicted_cell_type'] = scanvi_model.predict()

下一步

恭喜你已完成第一个 scvi-tools 分析!现在你应该能够:

  • ✅ 加载和预处理单细胞数据
  • ✅ 训练 scVI 模型进行批次校正
  • ✅ 执行下游分析和可视化

在第三章中,我们将深入探讨:

  • 各种模型的详细使用场景
  • 超参数调优策略
  • 处理多模态数据
  • GPU 加速技巧

→ 继续阅读:第三章 - 功能详解