做单细胞转录组分析的朋友,应该都见过AllenInstitute这个机构名。它在脑科学数据库领域的积累相当深厚,而今天要聊的scrattch就是它开源的一套单细胞注释工具。如果你手头有scRNA-seq数据,正在为“细胞类型注释结果不稳定”“标记基因选不准”这类问题头疼,那这个项目值得认真研究。它能做的事很聚焦:基于稀疏矩阵和随机森林构建可复用的细胞类型分类模型,把人工注释的经验固化下来,实现跨数据集、跨平台的自动注释。本文不打算念文档,就从一个实际使用者的角度,拆解它的设计思路、核心流程和我在落地时踩过的坑。
1. 项目概述:它到底是什么,适合谁用
1.1 解决的核心问题
单细胞测序数据分析里,细胞类型注释是承上启下的关键环节。上游聚类分完群,下游所有差异分析、轨迹推断、细胞通讯都建立在“这群细胞是什么类型”这个前提上。早期做法很原始:人工找几个已知marker,一个个基因去FeaturePlot上看表达,再结合文献判断群的身份。这种做法在小数据集上还行,样本一多、批次一杂就出问题,而且每个人的判断标准不一样,同一个cluster在不同人手里可能注释出完全不同的结果。
scrattch把这件事工程化了。它的核心思路是:用一种半自动的方式,从你已经注释好的高质量数据里提取规律,训练一个分类模型,然后用这个模型去预测新数据里的细胞类型。本质上就是把“注释经验”变成一个可重复、可分享、可跨数据集迁移的产物。这个思路和SingleR、SEACells有些相似,但它更强调两点:一是对大规模稀疏矩阵的支持,二是注释过程的层次化结构。
1.2 目标用户和适用场景
如果你符合下面几种情况的一种,scrattch会比较适合你:
- 你有一个已经人工注释好的参考数据集,想把它作为“标准答案”去注释新的实验数据。
- 你在做跨样本、跨平台的数据整合,需要保证所有样本的细胞类型标签口径一致。
- 你是做脑科学相关研究的,Allen Institute的公开数据本身就是很好的参考资源。
- 你受够了每次注释都要重新跑一遍marker可视化,想要一套可复用的流程。
反过来也要说清楚,scrattch不适合完全不懂细胞类型注释的新手。它默认你至少有一个人工注释质量较高的参考数据。没有这个前提,后面的模型训练和预测都无从谈起。它也不是细胞分群工具,不能替代Seurat、Scanpy的聚类流程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 结构设计与方法论:为什么它的注释结果更稳定
2.1 与主流注释工具的底层差异
现在用Seurat做注释,最常见的方案是FindAllMarkers结合AddModuleScore,或者直接用SingleR配合参考转录组。这两种方法各有局限。Marker打分的问题在于:基因集的选择主观性很强,不同人筛选marker的阈值不一样,得到的打分结果能差出一大截。SingleR的问题则是参考数据库的颗粒度问题,它的参考数据太泛,对一个特定组织的数据做注释时,经常出现“能分大类、分不清亚型”的尴尬。
scrattch的底层思路不一样。它不是简单地把基因表达量和一个基因集匹配,而是把整个注释过程拆成三步:选择判别基因、构建类型层级树、训练随机森林分类器。这三步里每一步都有明确的统计依据,最终输出的是一个经过交叉验证的模型,而不是一个“相似度打分”。
2.2 层次化结构的意义
先讲一个我自己的体会:单细胞注释最难的从来不是区分神经元和胶质细胞这种大类,而是区分两个转录组高度相似的亚型,比如不同区域的抑制性神经元亚群。这种区分需要综合几十个基因的表达模式才能做到,单个marker往往不具备判别力。
scrattch用层次聚类解决这个问题。它先基于所有细胞的高变基因计算距离矩阵,然后做层次聚类,得到一个树状结构。树的上层是大类,往下细分是亚型。这样做最大的好处是:模型在训练时,每个分类节点只需要学习“区分这两个分支”的基因特征,而不是一次性把所有类型都区分开。这种分层策略符合生物学直觉,也大幅降低了单层多分类模型的复杂度。
2.3 随机森林为什么管用
模型层面scrattch选的是随机森林(Random Forest)。这个选择很务实。单细胞数据的特点是特征数多、单类样本量可能很小、基因表达存在大量dropout。随机森林对这类数据表现相当稳健:它对特征的单位不敏感,不容易过拟合,还能输出特征重要性,方便我们回头检查哪些基因在分类中发挥了关键作用。
更重要的是,随机森林可以自然处理多分类问题,并且提供概率输出。scrattch在预测时不只是给一个“是或不是”的结果,而是给每个细胞落在每个类型上的概率分布。这个设计在做跨数据集注释时特别有用——预测结果置信度低的细胞,我们会单独拎出来重新检查,而不是盲信模型输出。
3. 实操流程:从参考数据到跨数据集预测
3.1 环境准备与数据规范化
先说明一下,这个工具目前主要面向R语言生态。我是建议在conda环境里单独建一个R环境,避免和现有的Seurat版本产生依赖冲突。安装方式不复杂,直接按官方README用devtools::install_github拉最新的release版本就行。
数据层面有一个关键点:scrattch对输入矩阵有明确格式要求。它需要的是稀疏矩阵格式(dgCMatrix),基因是行名,细胞是列名。从Seurat对象里取的时候,我建议直接用GetAssayData(seurat_obj, slot = "data"),这个返回的就是稀疏矩阵。千万不要用as.matrix转成稠密矩阵,几万个细胞的数据量一转换,内存占用会直接暴涨几倍,完全没有必要。
3.2 三种重要的输入数据
scrattch的建模过程需要三类数据配合:
第一类是表达矩阵,就是上面说的稀疏矩阵。第二类是元数据表,每一行是一个细胞,必须包含一列你自己人工注释好的细胞类型标签。第三类是聚类关系表,也就是你在Seurat里跑完聚类后那个seurat_clusters列。为什么要专门要一个cluster信息?因为scrattch建模时会把cluster作为分组信息来抽样,避免同一个cluster的细胞全部落在训练集里导致验证结果虚高。
这里有一个很容易犯的错。在你准备好细胞类型标签之后,一定要先检查每个类型的细胞数量。我的经验是,少于30个细胞的类型在训练时很容易被随机森林忽略掉,导致这个类型在模型里根本不存在。这种情况下建议要么把它归并到上一级类型,要么直接放弃这个亚型的注释。
3.3 核心步骤:筛选标记基因、构建树、训练模型
下面写一段关键流程的示意代码,基于我在实际项目里整理出来的套路,不是官方文档的搬运:
r复制library(scrattch)
# 1. 整理输入数据
sce <- as.SingleCellExperiment(seurat_obj)
expr_mat <- counts(sce) # 或 logcounts,视版本而定
meta <- data.frame(
cell_id = colnames(expr_mat),
type = seurat_obj$cell_type,
cluster = seurat_obj$seurat_clusters
)
# 2. 筛选判别基因
marker_res <- select_markers(
expr_mat = expr_mat,
meta = meta,
cell_type_col = "type",
n_markers = 50 # 每个类型候选基因数量,实际项目里我常用100
)
# 3. 构建层次树
tree <- build_tree(
expr_mat = expr_mat,
meta = meta,
marker_stats = marker_res,
cell_type_col = "type"
)
# 4. 训练随机森林模型
model <- create_model(
expr_mat = expr_mat,
meta = meta,
tree = tree,
classifier = "rf",
num.trees = 1000,
cross_validate = TRUE
)
# 5. 预测新数据
pred <- predict_cells(
model = model,
expr_mat = new_expr_mat,
verbose = TRUE
)
每一步都说明一下我踩过的坑。
select_markers这一步,影响最大的是候选基因数的选择。官方示例用50,但如果你研究的组织细胞类型很多、亚型之间差异比较微弱,50个基因做树形分叉时会显得不够用。我在做皮层抑制性神经元注释时试过从20到200不同的取值,整体趋势是候选基因越多,树每个分支上调用的基因越充足,但同时噪声基因也会增加。100是一个比较均衡的起点,如果你的数据量特别大,可以再往上加。
build_tree生成的树结构建议可视化检查一下。这一步很重要,但很多人会跳过。检查的目的是看层次关系是否符合生物学常识。比如兴奋性神经元和抑制性神经元理应分成两个大分支,如果树结构把它们混在一个分支下面,说明你的高变基因选择或者聚类结果有问题,这时候再往下做分类器都是在错误的基础上跑,不如先回头处理数据。
训练模型时有两个参数值得多说两句。一个是num.trees,随机森林的树数量。默认几百棵树在几万细胞的数据上效果还行,但我跑下来感觉1000是个更稳的取值,特别是在类型数量超过20个的时候,树太少会导致部分类型在每一棵树里都不一定被抽样到。另一个是cross_validate,务必打开。这个参数会做分层交叉验证,输出每个类型的精确率和召回率,你可以直接看到哪些类型是模型分不清的。
3.4 预测结果怎么解读
predict完的输出不是简单的标签,而是一个概率矩阵加上一个置信度分类结果。我习惯把模型给出的概率也保存下来,后续画一个二维的UMAP图,把细胞的颜色映射到概率值上。低概率的细胞往往散落在两个类型的边界区域,这些细胞我会单独提取出来,重新跑一遍差异表达,人工看一下到底是什么身份。
还有一点要特别注意:跨数据集预测时,参考数据的处理流程必须和预测数据完全一致。这个一致指的是数据标准化方式、log变换方式、以及高变基因的选择逻辑。如果参考数据是用Seurat的SCTransform处理的,预测数据用的是NormalizeData+ScaleData,即使两个数据来自同一个组织,模型效果也会大打折扣。我在项目里通常两个数据集统一用sctransform流程处理完之后再做预测。
4. 踩坑记录:注释结果不准时,问题通常出在哪
4.1 数据格式和矩阵类型的隐性坑
这part记录几个真实遇到的问题。
第一个问题是矩阵类型。scrattch要求的稀疏矩阵是dgC格式,也就是压缩列格式。但有些数据在读取或转换过程中会变成dgT格式,比如用readMM读进来的矩阵很多就是dgT。直接拿dgT去跑scrattch,某些版本下不会报错,但运行速度会慢很多,甚至在某些操作时内存直接溢出。解决方案是先做一次强制转换:as(expr_mat, "dgCMatrix")。这一步看起来多余,但能省下后面很多时间。
第二个问题是细胞barcode的格式一致性。Seurat为了保证样本间区分,常常会加前缀,比如sample1_AAACCTGCAT。如果你参考数据的barcode带前缀,而预测数据的barcode不带前缀,预测的时候模型会对不上号,报错还容易排查,但更隐蔽的问题是:如果你的两份数据barcode前缀相同但实际来源不同,模型会直接把表达数据对齐到参考数据上,输出一个看起来正常但实际不可靠的结果。所以跨数据集预测前,务必检查barcode的唯一性。
4.2 模型效果的验证和修正
我用这个工具最大的体会是:交叉验证分数高,不代表跨数据集效果好。交叉验证是在同一份数据内做的,比如你从10个样本里抽样训练,再用剩下细胞验证。但如果这10个样本本身来自同一个批次,交叉验证的分数再高也反映不了批次差异问题。不要自信,第一轮的跨数据集预测结果一定会有一定比例的细胞置信度极低。
遇到这种情况,我的处理策略是这样的。先把预测置信度低的细胞过滤出来,不做任何强制的标签分配,而是降维聚一次类,看它们在UMAP上是否形成独立小群。如果形成一个小群,用小群的特征基因去做富集分析,基本能判断出它是不是一个参考数据里没有的新型细胞亚群。这类细胞在跨组织注释中相当常见,处理得当的话反而能成为你发现新细胞类型的线索。
4.3 调参数的方向感
总有人问,预测不准的时候到底该调什么参数。我的建议是不要一上来就动分类器参数。优先检查参考数据的细胞类型标签质量,其次是检查树结构是否合理,再次是检查候选基因是否覆盖了关键marker,最后才轮到调随机森林的树数量。这个顺序我踩坑之后总结出来的:模型只是把参考数据里的规律固化了,参考数据本身如果有问题,模型再怎么调也是白搭。
顺带说说特征基因的选择。scrattch筛选标记基因的统计方法本质上基于表达差异,对低表达基因偏向保守。如果你的目标类型是由一两个高表达基因定义,而其余基因表达量都很低,这个类型可能不会被很好的区分。解决办法是,在select_markers之后再把自己已知的几组marker基因手动合并进去。这个操作不违反工具的设计初衷,反倒是对特殊生物学场景的必要修正。
4.4 问题速查表
| 现象 | 最可能的原因 | 处理建议 |
|---|---|---|
| 预测结果中某类型完全缺失 | 参考数据中该类型细胞数量过少 | 检查参考数据标签,少于30个则归并到上一级类型 |
| 交叉验证分数高但预测结果差 | 参考与预测数据批次差异大 | 统一处理流程,必要时做Harmony整合后再预测 |
| 预测概率普遍偏低 | 候选基因数太少或树结构不合理 | 调高n_markers,可视化检查层级树 |
| 运行速度异常慢 | 矩阵不是稀疏格式 | 统一转为dgCMatrix |
| 某两类型频繁混淆 | 这两类在树上位置过近 | 检查它们之间的marker差异,考虑是否合并为一个类型 |
5. 还有几点想说的
最后分享一个实际操作中反复验证的技巧。如果你做的研究涉及多个批次的数据,不要只拿一个批次做参考。最好把整个参考数据集做一个广义上的整合,先用Harmony或者RPCA去批次效应,然后再在整合后的空间上人工注释细胞类型,最后用scrattch从这个整合后的数据上训练模型。这样得到的模型在预测新批次时稳健得多,因为参考数据本身已经包含了多个批次的表达波动范围。
另外一个小建议是,项目里的注释模型文件记得保存好。scrattch训练好的模型是可以序列化保存的,你的课题做完之后,数据规模小的话整个模型文件也才几十MB。等后续新数据进来,直接加载这个模型跑预测,比重新训练省下大量的时间。如果你打算把模型分享给实验室的其他人,记得联动分享基因列表和标准化参数,不然别人拿到模型也不知道该怎么对齐数据。
工具说到底只是助手,注释结果最终还是要回到生物学验证上。但scrattch确实让我从肉眼盯FeaturePlot的繁琐里解脱出来了,也让我重新审视了“注释经验”这个难以量化的东西——它并没有那么玄,本质上就是一个可以训练、验证和复用的分类问题。
