Tutorials and datasets:

Github: SindiLab/ACTIVA: The main repository for ACTIVA: realistic single-cell RNA-seq generation with automatic cell-type identification using introspective variational autoencoders

Zenodo: ACTIVA:使用内省变分自编码器实现自动细胞类型识别的逼真单细胞 RNA-seq 生成 --- ACTIVA: realistic single-cellRNA-seq generation with automatic cell-type identificationusing introspective variational autoencoders

Workflow:

1、使用预处理数据进行模型训练

2、使用训练好的模型进行数据生成和评估

3、使用其他数据集和预训练模型进行模型微调

模型训练

1、下载预处理数据

因为数据使用了Amazon S3 (Simple Storage Service)云储存服务

需要在环境中安装AWS CLI (Amazon Web Services Command Line Interface)来支持S3链接下载

安装和配置 AWS CLI

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

详细安装教程参考安装或更新到最新版本的 AWS CLI - AWS 命令行界面 --- Installing or updating to the latest version of the AWS CLI - AWS Command Line Interface

下载数据到工作区

# 1. 创建目标文件夹
mkdir activa_preprocessed_data
cd activa_preprocessed_data

# 2. 下载 Brain Small 数据集 (.h5)
aws s3 cp s3://activa-material/PreprocessedData/20kBrainSmall_preprocessed.h5 . --no-sign-request

# 3. 下载 68K PBMC 数据集 (.h5ad)
aws s3 cp s3://activa-material/PreprocessedData/68kPBMC_preprocessed.h5ad . --no-sign-request

# 4. 下载 NeuroCOVID 数据集 (.h5ad)
aws s3 cp s3://activa-material/PreprocessedData/NeuroCovid/NeuroCOVID_PreProcessedUsingScGAN_Sparse.h5ad . --no-sign-request

这里的存储桶是公开可读的(对于研究数据通常如此),可以通过在命令中添加 --no-sign-request 标志来告诉 AWS CLI 不需要使用凭证进行签名请求。

如果是私有访问需要身份验证,可以注册AWS 账户,并生成 Access Key IDSecret Access Key,回到工作区运行配置命令,并按提示输入您的密钥和区域

aws configure

2、下载模型并安装依赖

下载模型

使用git clone克隆模型到工作区

git clone https://github.com/SindiLab/ACTIVA.git

安装依赖

创建虚拟环境(在autodl中)

# 创建一个新的 Python 3.8 环境
conda create -n activa_env python=3.8 -y

# 更新bashrc中的环境变量(仅在autodl中需要)
conda init bash && source /root/.bashrc

# 激活新环境
conda activate activa_env

在虚拟环境中安装依赖

pip install -r requirements.txt

本地安装包(运行setup.py)

pip install -e .

3、训练模型

修改数据文件位置

在ACTIVA.py中找到导入数据的地方

例如

elif opt.example_data == '20k brain':
  train_data_loader, valid_data_loader = Scanpy_IO('/home/ubuntu/scGAN_ProcessedData/MADE_BY_scGAN/20Kneurons_2KTest.h5',

由于这里默认使用硬编码地址,需要改成自己数据文件的存放地址

# 修改 NeuroCOVID:
train_data_loader, valid_data_loader = Scanpy_IO('/root/activa_preprocessed_data/NeuroCOVID_PreProcessedUsingScGAN_Sparse.h5ad',

# 修改 68K PBMC
train_data_loader, valid_data_loader = Scanpy_IO('/root/activa_preprocessed_data/68kPBMC_preprocessed.h5ad',

# 20K Brain Small
train_data_loader, valid_data_loader = Scanpy_IO('/root/activa_preprocessed_data/20kBrainSmall_preprocessed.h5',

开始训练

CUDA_VISIBLE_DEVICE=0,1,2,3,4,5,6,7 python ACTIVA.py --example_data "20k brain" --lr 0.0002 --lr_e 0.0002 --lr_g 0.0002 --nEpochs 500 --tensorboard --outf './brain_small_train_logs'

CUDA_VISIBLE_DEVICES控制当前进程可见的 GPU 设备。0,1,2,3,4,5,6,7告诉程序可以使用这些编号的 GPU。由于程序没有启用多卡并行,它将默认使用 Device 0

--example_data选择要运行的预定义示例数据集。"20k brain"对应 brain small 数据集。

--lrACTIVA 模型的整体学习率(通常用于 Adam 优化器)。

--lr_e编码器(Encoder)的学习率。

--lr_g解码器(Generator/Decoder)的学习率。

--nEpochs训练的总轮数(Epochs)。

--tensorboard启动训练可视化。

--outftensorboard日志输出位置。

此外还有参数:

--batchSize定义输入批次大小。增大 batch size 可以利用更多 GPU 显存,加快收敛,但也可能需要更多内存(RAM/VRAM)。

--workers数据加载的子进程数。如果内存出现问题,可以尝试减少这个值。

--data_type "scanpy"数据格式(Scanpy/Seurat AnnData 或 CSV)。

--m_plus判别器部分的边界(Margin)值。

--weight_neg负样本权重,用于 IntroVAE 损失。

--weight_rec重构损失(Reconstruction Loss)权重。

--weight_klKL 散度损失(KL Divergence Loss)权重。

--num_vae在开始对抗训练前,进行纯 VAE 训练的轮数(Warm-up)。

--tensorboard启用 TensorBoard 日志记录。强烈推荐启用以便可视化训练过程。

--outfTensorBoard 日志和检查点(Checkpoint)的输出目录。

--print_frequencyACTIVA 训练统计信息打印的频率(Batch 迭代次数)。

--save_iter模型检查点保存的间隔(Epochs)。

Tensorboard训练可视化

在训练命令后添加--tensorboard --outf './brain_small_train_logs'字样,会将训练日志输出到指定文件夹。

随后可以在训练过程中或者训练结束后,在工作区新建终端中输入以下代码

# 激活您的环境(如果尚未激活)
conda activate activa_env

# 启动 TensorBoard 服务器
tensorboard --logdir=./covid_logs --port=6006

然后在本地电脑的终端中输入ssh指令连接远程服务器

# 仅为示例,请以自己的服务器为准
ssh -CNg -L 6006:127.0.0.1:6006 root@connect.cqa1.seetacloud.com -p 36483

连接成功后可以通过浏览器访问端口看到可视化窗口

http://localhost:6006

生成数据

生成合成的单细胞RNA测序数据:Tutorials/ACTIVA/ACTIVA-Generating-scRNAseq.ipynb at main · SindiLab/Tutorials

特定细胞类型RNA测序数据:Tutorials/ACTIVA/ManifoldAnalysis_20kBrainSmall.ipynb at main · SindiLab/Tutorials

报错记录

RuntimeError: CUDA error: device-side assert triggered

RuntimeError: CUDA error: device-side assert triggered
Assertion `t >= 0 && t < n_classes` failed.

这个断言 (Assertion) 是 PyTorch 在 GPU 上执行 交叉熵损失计算 (nll_loss_forward_reduce_cuda_kernel_2d) 时触发的。它表示您的数据标签(Target, 也就是 t)的值超出了预期的类别范围。


🚨 错误分析:标签越界

错误原因

  • 断言条件: t >= 0 && t < n_classes (标签 t 必须大于等于 0 且小于类别总数 n_classes)

  • 您的类别数: 脚本成功读取并设置了 ==> Number of classes 16 (n_classes = 16)。

  • 实际问题: 在训练分类器时,您的数据加载器(train_data_loader)输出了一个标签t)的值 大于或等于 16

可能的场景:

  • 标签未从 0 开始编码: 数据集中的标签可能从 1 开始编码到 16(即标签为 1,2,…,16)。如果 PyTorch 期望 0,1,…,15,那么标签 16 就会导致断言失败。

  • 数据包含未过滤的类别: 您的数据文件 (NeuroCOVID_PreProcessedUsingScGAN_Sparse.h5ad) 中包含第 17 个(或更多)细胞类型,但您的脚本却将类别数硬编码为 16。

您需要将代码中对 opt.number_of_classes 的引用,替换为在脚本中实际存储类别数的变量。

1. 定位正确的变量

ACTIVA.py 文件中,搜索类别数 16 所在的代码行。您应该能找到类似这样的逻辑:

Python

# ACTIVA.py (在 main() 函数中,数据加载之后)
# ...
number_of_classes = 16 # 或者从数据中获取的变量
# ...

2. 修改 train_classifier 函数

由于 number_of_classes 是在 main() 函数中或全局定义的,但在 train_classifier() 函数中无法直接访问,您需要将它作为参数传递给 train_classifier()

步骤 2.1:更新 train_classifier 函数签名

train_classifier 函数的定义修改为接受类别数作为参数:

Python

# ACTIVA.py (约 250 行左右)
# 修改前
def train_classifier(epoch, iteration, batch, cur_iter):
# 修改后
def train_classifier(epoch, iteration, batch, cur_iter, number_of_classes):
    # ... 函数体

步骤 2.2:在 main 函数中调用时传递参数

找到 main() 函数中调用 train_classifier 的位置(约 510 行),并传递类别数:

# ACTIVA.py (在 main() 函数中,约 510 行左右)
# 假设 number_of_classes 已经被正确定义为 16
train_classifier(epoch, iteration, batch, cur_iter, number_of_classes) 

步骤 2.3:修正 train_classifier 函数体中的代码

现在,您可以使用传递进来的 number_of_classes 变量来替换 opt.number_of_classes

在您之前添加的代码行(ACTIVA.py 约 277 行)中进行修改:

# ACTIVA.py (在 train_classifier 函数内)

# ...
        # 2. 检查标签是否从 1 开始 (例如:1到16),如果是,则减去 1
        # 我们假设最大的标签是 16,最小的是 1
        # if true_labels.max() > 15: # 原始代码可以保留此行
        if true_labels.max() >= number_of_classes:
            true_labels = true_labels - 1
            
        # 3. 再次检查,如果仍有标签越界(例如 > 15),则将其截断为 15
        # 注意这里用传递进来的 number_of_classes 替换了 opt.number_of_classes
        true_labels = torch.clamp(true_labels, min=0, max=number_of_classes - 1)
        
        info = f"\n====> Classifier Cur_iter: [{cur_iter}]: Epoch[{cf_epoch}]({iteration}/{len(train_data_loader)}): time: {time.time()-start_time:4.4f}: "
# ...