基因组学数据基础:三种核心数据格式

做组学模型训练,第一关就是跟数据格式打交道。

我个人习惯,先把数据分成三个大类:测序原始数据、表达定量数据、变异数据。每个类别都有自己的“脾气”,处理不好,GPU利用率上不去,甚至模型训练直接崩溃。

DNA/RNA测序原始数据:FASTA 与 FASTQ

先说 FASTA。它就是个序列存储格式,只有两行:一行以 > 开头,存放序列 ID 和注释;另一行就是碱基序列本身。

>chr1_12345_ATCG
ATCGATCGATCGATCGATCG

但实际工作中,大家更常用的是 FASTQ。你想想看,FASTA 只告诉你序列长什么样,却没告诉你每个碱基的测序质量。

FASTQ 每四条记录为一个单元:

@SEQ_ID
ATCGATCGATCGATCGATCG
+
IIIIIIIIIIIIIIIIIIII
  • 第一行:@ 开头,序列 ID
  • 第二行:碱基序列
  • 第三行:+ 分隔符
  • 第四行:质量值,用 ASCII 字符表示 Phred 分数
我的经验:Phred 分数公式是 Q = -10 * log10(p),其中 p 是碱基测错的概率。质量值字符的 ASCII 值减去 33 就是 Q 值。比如 I 的 ASCII 是 73 → Q=40 → 错误概率 0.0001。这个在过滤低质量 reads 时很关键。

基因表达矩阵:行是基因,列是样本

表达矩阵就直白多了。行是基因/转录本,列是样本/细胞,单元格是对应的表达量。

基因 样本1 样本2 样本3
TP53 12.5 3.2 45.8
EGFR 8.1 28.4 0.9
MYC 55.2 67.3 22.1

这里有个坑:表达矩阵通常非常稀疏。尤其是单细胞数据,90% 以上的单元格是 0。如果你直接把它当稠密矩阵传给 GPU,显存会爆炸。

我曾经用全连接网络处理 2 万个基因的表达矩阵,每个样本的向量长度 2 万。结果显存直接 OOM。后来改用 torch.sparse_coo_tensor 或 scipy.sparse.csr_matrix 先压缩,才跑起来。

常见的表达矩阵格式有:

  • CSV/TSV:简单,但大文件读写慢
  • HDF5:支持分层存储,适合超大矩阵
  • AnnData:单细胞领域标准(.h5ad)
  • MX:10x Genomics 官方的稀疏矩阵格式

变异数据:VCF 的奥秘

VCF(Variant Call Format)是记录突变信息的标准格式。其核心结构包括:

##fileformat=VCFv4.2
#CHROM  POS     ID      REF     ALT     QUAL    FILTER  INFO
chr1    12345   .       A       G       999.0   PASS    DP=50;AF=0.45
chr1    23456   rs123   T       C       500.0   PASS    DP=30;AF=0.12

关键字段解释:

  • CHROM/POS:染色体与位置
  • REF/ALT:参考碱基与突变碱基
  • QUAL:质量值,越高越好
  • GT(基因型):藏在 FORMAT 列里,如 0/1 表示杂合

做 GPU 加速时,VCF 的痛点在这:One-hot 编码后的基因型矩阵非常大。一个人全基因组约 500 万个常见 SNP,每个 SNP 用 3 个 one-hot 位(纯合参考、杂合、纯合突变),就是 1500 万维。直接输入模型?GPU 显存根本扛不住。

我的做法:先用注释数据库(如 ClinVar、GWAS)筛选与疾病相关的 SNP 子集,把维度降到 1 万以内再训练。

数据清洗与预处理 Pipeline

原始数据不能直接用。下面是一套我常用的预处理流程:

  1. FASTQ 质控:用 FastQC 检查碱基质量分布,过滤掉低质量 reads
  2. 序列比对/拼接:将 reads 比对到参考基因组,得到 BAM 文件
  3. 定量/变异检测:用 featureCounts 或 STAR 做表达定量;用 GATK 做变异检测
  4. 标准化:表达量做 logCPM、FPKM 或 TPM 变换;变异数据做 PCA 降维或 LD 剪枝
  5. 张量化:转为 PyTorch/TensorFlow 的 tensor 格式
避坑提示:标准化时,千万别在训练集和测试集上分别做!我见过有人这样搞,导致模型验证时 AUC 虚高 0.1。正确做法:在训练集上计算均值和标准差,然后用同样的参数去转换测试集。

数据如何喂给 GPU?一个完整示例

假设我们已经把一个表达矩阵存成了 NumPy 数组 X(样本数 x 基因数),标签存成了 y。用 PyTorch 的 DataLoader:

import torch
from torch.utils.data import Dataset, DataLoader

class OmicsDataset(Dataset):
    def __init__(self, X, y):
        self.X = torch.FloatTensor(X)
        self.y = torch.LongTensor(y)

    def __len__(self):
        return len(self.y)

    def __getitem__(self, idx):
        return self.X[idx], self.y[idx]

dataset = OmicsDataset(X_train, y_train)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True, num_workers=4)

for batch_X, batch_y in dataloader:
    batch_X = batch_X.cuda()
    batch_y = batch_y.cuda()
    # 前向传播...

num_workers=4 表示用 4 个子进程加载数据,这样 GPU 不会因为等数据而空闲。我在项目中遇到过,如果 num_workers 设太大反而会拖慢速度,因为进程切换开销过高。单 GPU 建议 2-4,多 GPU 可以试试 8-12。

三类数据与 GPU 加速的关联

我们用一张 SVG 图来梳理整个流程:

基因组学数据 → GPU 模型训练流程 FASTQ / FASTA 表达矩阵 VCF 变异数据 预处理:质控 → 比对 → 定量/检测 → 标准化 (筛选相关特征 → 降维 → 张量化) PyTorch DataLoader 模型训练 (CUDA / cuDNN 加速) 内存带宽瓶颈常在此

从 SVG 图可以看出,原始数据格式不同,预处理步骤也不同,但最终都要汇入 DataLoader,由 GPU 来加速模型训练。

这里有个关键点:数据读取速度一定要跟得上 GPU 计算速度。我碰到过一个案例,模型只用了 70% 的 GPU 利用率,排查后发现是单线程读 CSV 文件。换成 HDF5 加 DataLoader 的多进程加载后,利用率直接拉满到 98%。

小结:三种数据选型对比

数据类别 典型文件 数据体积 GPU 处理难点 建议做法
测序原始数据 .fastq, .fa GB ~ TB 级 I/O 瓶颈,解析慢 用二进制格式(.bam)减少读取量
表达矩阵 .csv, .h5, .h5ad MB ~ GB 级 稀疏矩阵占用大,维度高 使用稀疏存储 + PCA 降维
变异数据 .vcf, .vcf.gz MB ~ 百 GB 级 One-hot 后维度爆炸 先特征选择,再编码

搞清楚这三种数据,后面讲自定义 Dataset 和 DataLoader 的优化技巧时,你就能理解为什么要那样写了。