一 PB,只读,每行一个键。
我看到 AlphaGenome Atlas 的第一反应不是"基因组学",是"这是一张查表"。九亿个变体,每个的预测结果提前算好,落盘,一 PB 停在那儿不动。这里没有在线推理,没有请求队列,没有 GPU 池的自动扩缩容。就一张表。
如果让我给这张表写个键,大概长这样:
struct variant_key {
uint8_t chr; /* 染色体,最多二十几个,8 位够 */
uint32_t pos; /* 位点,三十亿用 32 位塞得下 */
uint8_t alt; /* 换成哪个碱基,去掉自己还剩三种 */
};
十几个字节一个键。人类基因组三十亿个碱基对,每个位置三种可能的单字母替换,乘出来就是九亿——这个空间是有限的、可枚举的、今天就能完完整整列出来的。没有新变体会在明天冒出来,也没有哪个查询能落在空间之外。
这才是重点。不是"AI 预测基因",是"这个空间是闭的"。
一旦确认查询空间是闭的、有限的、不随时间增长,整个架构问题就塌缩了。你不需要一个服务,你需要一次批处理加一张表。算一遍要花很多机器很多天,但算完之后每一次查询都是 O(1) 的寻址。慢的那部分只发生一次,快的那部分发生一百万次。
在线推理那套,是给谁准备的
反过来想。不上这张表,改成开一个推理端点,谁要查哪个变体就现算。
那你要什么?要一个队列,因为请求会突发。要一个缓存,因为热门位点会被反复查。要一个限流器,因为总有人的脚本把整条染色体循环一遍。要一个调度器,把批任务和交互请求分开。要一套监控,因为你半夜不知道那几张卡是闲着还是挂了。要一套重试和回退,因为推理跑到一半会 OOM。
一层一层加上去,最后你得到一个分布式系统,它唯一的功能是查一个九亿分之一的数。
我不是说在线推理没用。这段我没完全想明白的,是边界在哪:如果查询空间不闭呢?如果每个查询还得带一长串上下文,组合爆炸,预计算根本不成立——那你就只能在线算,那一整套东西也就配得上它的复杂度了。
所以顺序是先问那个问题。空间闭吗。闭的,别上服务。不闭的,再说。
更狠的一手是那个分数
九亿行预测结果,每行不是一个数,是一组数——编码区的、非编码区的、各个调控方向上的预测。做实验的人拿到这张表也没用,他不可能一次读一万个数据点然后自己排个序。
所以他们给了个 AVI score,把一个变体横跨编码区和非编码区的预测压成一个标量。
这一步才是真正让这张表变得可用的那一步。前面那九亿行是数据,这个分数是接口。
我完全支持这个做法,但我想说清楚它是什么:它是一个排序键,不是真相。
/* 排序键 */
int cmp(const void *a, const void *b) {
return score_of(b) - score_of(a);
}
把多个维度压成一个维度,一定得选一组权重。这组权重是别人定的,是他们认为"影响大"的那个定义。拿它来排序候选、挑前 1%、决定先做哪个湿实验——完全正确,排序键就该干这个。拿它当"这个变体致病"的判决——那就把一个别人的先验当成了事实。
再说一遍,我没有要批评这个选择。做排序键的时候不选权重,才是回避问题。
Broad Institute 那边用它挑出了 DNM1 上的一个变体,预测说它造出了一个错误的剪接位点,这条证据帮他们把一桩悬着的罕见病案子结了。Hawkes 那边拿着五万四千多个 UK Biobank 样本,按预测的分子效应把变体分组,多找出 22% 的非编码关联;只看影响最大的前 1%,定位到 19 个跟 BMI 相关的区域。
这些数字的价值不在于"AI 准"。在于一个排序键可以让做实验的人从列表顶部开始做。实验室的产能是瓶颈,不是算力。你不可能把一万个候选都做一遍,你只能做五个。所以系统的真正输出不是预测结果,是"先做这五个"。
回头看,那一 PB 其实是中间产物。用户要的不是九亿行,是排好序的前五行。为了拿到那五行,你造了一张一 PB 的表,听起来极不合理。但如果这张表只算一次、之后十年都是它,那它就是最便宜的那个方案。
那 98%,本质是个没文档的系统
人类基因组里,编码蛋白的那 2% 研究得比较透,剩下 98% 基本靠猜。
从工程角度读这句话:这不是"科学未解之谜",这是一个 98% 没写文档的系统,你手里只有一盒探针。
对付没文档的系统,不上理论,做扰动,看输出怎么变。这是 fuzzing 的古老套路——把每个字节都改成别的值,跑一遍,看哪个字节改了会崩。
九亿个变体,就是那份穷举的 fuzz 语料。全部算一遍存下来,没有任何取巧。这跟我在自己那个玩具存储引擎上做的事一样:我不猜哪条分支慢,我把每个点都打一遍。费的是机器时间,买的是不用推理。
这样也许能把 98% 砍到 20%,也许砍不动。但至少你不再猜。
那个网页
一个细节我挺喜欢:它有个网页入口,不用写代码。没有 SDK,没有 API key,没有"请先安装 Python 客户端"。
这个年代 API-first 是个反射动作。什么东西都想先给个接口,再让用户自己写脚本去调。但真实用户是临床研究员和生物学家,他们一辈子不写代码,他们要的是把变体编号粘进去、看它返回什么。给他们一套客户端库是浪费,给他们一个表单才是对的。接口应该长成用户已有的形状,不是长成你实现起来最省事的形状。
收束
一条规则:凡是查询空间是闭的、有限的、不随时间增长的,先别上在线服务,先算一遍存下来。
预计算的成本是一次性的、可预测的、你算得出来的。在线服务的成本是持续的、会呼吸的、会长出队列、缓存、调度、重试、监控那一大堆的。
九亿个变体听着吓人。但一张只读的表,比一个服务简单得多。