Skip to content

Pic2SMILES / MolMini 技术报告

1. 任务与交付

目标:训练一个小型光学化学结构识别(OCSR)模型——输入 2D 化学结构图,输出规范 SMILES——能在 16GB M3 MacBook Pro 上轻松运行,采用当前的小模型工程实践,并给出 可复现的报告。

交付物:MolMini,image-to-SMILES 编码器-解码器,三档规模(3.6M / 12.1M / 23.8M 参数),在 262 万条 ChEMBL 分子上从零训练(图像实时渲染),外加一套在完全 相同的冻结图像上对比 MolScribe 与两个参照点的评测框架。

chembl_test_linux

model params checkpoint n valid exact exact/no-stereo tanimoto measured on
oracle 0 - 3000 1.000 1.000 1.000 1.000 Darwin-arm64
molmini 23.8M step 240,000 3000 0.997 0.897 0.937 0.965 Darwin-arm64
molmini 12.1M step 240,000 3000 0.998 0.857 0.927 0.958 Darwin-arm64
molscribe pretrained external - 900 0.934 0.647 0.760 0.892 Linux-x86_64
image_hash_nn 0 - 3000 1.000 0.000 0.000 0.118 Darwin-arm64

2. 起点评估

原仓库只有评测框架,没有模型。唯一的训练代码是对 60 条硬编码 SMILES 的闭集分类 器,在自己的合成数据上 exact match 5.6%;11 个注册"模型"中有 6 个是占位适配器, 只能输出 skipped_TinyViT 把层存在普通属性上,导致 parameters() 返回未注册 的参数集,.to(device) 移动的是影子副本。

闭集分类器无法通过调参变成开放词表识别器——输出空间就是错的——所以建模部分重写。 保留的部分:规范 SMILES 评分、"不可用系统记为 skipped 而非失败"的契约、冻结 manifest 格式。

3. 方法

3.1 输出表示

采用自回归 SMILES 解码,而非图解码。这是由可获得的监督信号决定的:训练图像来自 RDKit 渲染,每张图有精确的 SMILES 但没有原子坐标,而图解码器(MolGrapher、 MolScribe 的 graph head)需要原子级坐标监督。图解码在该领域顶端是更强的形式; 这里的选择基于监督可得性,不是说序列解码更好。

3.2 架构

384x384x1
  -> conv 7x7 stride 4                      ->  96x96x64
  -> conv stride 2 + 3 个 ConvNeXt block    ->  48x48x128
  -> conv stride 2 + 3 个 ConvNeXt block    ->  24x24x256
  -> linear + RMSNorm                       ->  576 tokens x 384
  -> 4 层 transformer,轴向 2D RoPE
  -> 6 层因果解码器,1D RoPE,交叉注意力,绑定词嵌入

用卷积把像素降到 24x24 的 token 网格,因为 OCSR 的早期特征(笔画、交叉点、字符) 都是局部的;全局注意力只作用在剩下的 576 个 token 上——识别环闭合这类需要关联图中 远距离部分的任务,才真正需要注意力。组件:RMSNorm、SwiGLU、QK-norm、fan-in 缩放 初始化、WSD 学习率、权重 EMA。

分辨率由声明的范围决定:224px 下,稠密稠环体系里的原子标签和电荷已经无法辨认。

3.3 数据

训练图像每步实时生成。渲染一张约 2ms CPU,远低于一步 GPU 的开销,所以冻结图像集 没有收益,只会损失泛化。每次渲染随机化布局引擎、旋转、键宽、字体、原子配色、缩写 基团(PhOMe)和漫画风格;随后叠加笔画形态学、模糊、降采样、JPEG、透视畸变、 纸张纹理和椒盐噪声。

必须明说的推论:增强分布就是规格说明。任何不在其中的作图约定或退化模式,在推理 时都是分布外,训练多久都没用。

3.4 范围与泄漏控制

constraint value
heavy atoms 4 to 48
elements B, Br, C, Cl, F, I, N, O, P, S, Se, Si
formal charges allowed
multi-fragment excluded
curation count
read 2,897,819
rejected (unparseable or out of scope) 269,119
duplicate by canonical SMILES 141
removed by the DECIMER leakage guard 210
kept 2,628,559
train / val / test 2,618,349 / 5,000 / 5,000
vocabulary 88 tokens

210 个分子同时出现在 ChEMBL 和 DECIMER 手绘评测集中。在划分数据前,已按 规范 SMILES 从训练语料中剔除。否则"能泛化到真实手绘结构"这个结论会有一部分其实是 在测量记忆。

4. 一次训练失败的完整记录

前两次训练在约 1 万步被中止。loss 正常下降、teacher-forced token 准确率达 0.77, 但贪心解码对每张图都输出同一个 SMILES 字符串

实测:两张不同图像的编码器 memory 余弦相似度 1.00004。编码器无论输入都输出同一个 常量向量,解码器学成了无条件 SMILES 语言模型——SMILES 本身的可预测性就能给到 0.6–0.77 的 token 准确率,所以 loss 曲线什么都看不出来。

8 组样本过拟合测试记住了 8/8,排除架构 bug。按模块统计第 0 步梯度定位到原因:

第 0 步梯度范数
编码器 108,675
解码器 12.2

init_weights 对所有层用固定 trunc_normal_(std=0.02)。这个常数是按 GPT-2 的宽度 校准的;用在 fan-in 576 的卷积上,激活衰减到 std 0.017,而归一化层的反向增益正比于 1/RMS(输入),于是微小激活变成巨大梯度。clip_grad_norm_(1.0) 再把整体梯度除以约 1e5——解码器几乎没更新,编码器在第一步就被打死。

修复:fan-in 缩放初始化、stem 内逐图像标准化、stem 投影后加 RMSNorm、交叉注意力排除 在深度残差缩放之外、ConvNeXt LayerScale 从 1e-5 提到 0.1(1e-5 低于它所加到的激活的 bf16 尾数精度)。

相同的 800 步对照 memory 余弦 token 离散度
修复前 1.00000(塌缩) 0.0056,持续衰减
修复后 0.61,持续下降 0.70,持续增长

可推广的教训:对于一侧能独立解决部分任务的编码器-解码器结构,loss 下降并不能 证明两侧都在学习。诊断必须直接测量条件信号是否被使用。

防线:tests/test_init_health.py(已验证在修复前代码上失败)和 Trainer.encoder_health——每个日志间隔记录 memory 余弦,超过 0.995 直接中止训练。

5. 评测设计

三个让数字有意义的性质:

  1. 冻结图像。评测图像用固定种子写盘一次。若评测图实时生成,性能退化和一次 倒霉的渲染无法区分。
  2. 经过验证的评分路径oracle 直接读 manifest 里的答案,必须精确得到 1.000。 在两个评测集全部 3000 张图上确实如此。
  3. 记忆下界image_hash_nn 检索最近的训练图像,得分 0.000。模型高于这条线的 部分才是识别,而不是查表。

三个难度档:cleanvarieddegraded,外加 DECIMER 手绘集作为分布外测试。

字体对照。两台机器的字体族完全不相交(Linux 上是 DejaVu,macOS 上是 Arial/Georgia)。没有掩盖,而是用同样的分子和种子在两台机器上各建一套评测集。 MolScribe 在两套上分别得 0.647 和 0.646——相差 0.001——这说明字体族不会影响一个 成熟的 OCSR 模型,因此 MolMini 若在两者间出现差距,那就是它自身的字体敏感性。

留出分子到底有多"新"?

数据划分按同分异构规范 SMILES 不相交,并在构建时断言。但对照全部 2,618,349 条 训练分子测量后,这个保证比听上去弱得多:

统计量 数值
到训练集的最大 Morgan(r=2) Tanimoto 中位数 0.806
95 分位 1.000
存在近似孪生体的比例 (>= 0.9) 0.158
存在相近类似物的比例 (>= 0.7) 0.833
真正新颖的比例 (< 0.5) 0.007

83% 的留出分子在训练集中有相近类似物。95 分位为 1.000 意味着至少 5% 的分子指纹 完全相同:Morgan 指纹默认忽略立体化学,所以训练分子的一个立体异构体是不同的 同分异构 SMILES,能通过不相交断言,而指纹完全一致。

精确量化:5,000 条测试分子中有 409 条(8.2%)与某个训练分子共享去立体化学后的 骨架。 对这些分子,模型已经见过其 2D 结构,真正新的只有楔形/虚线键的读取。这不 是纯粹的泄漏——立体化学本来就是 OCSR 模型必须从图上读出的内容——但每十二条测试分子 中就有一条,其骨架识别部分是被记住的。

这就是 ChEMBL 的性质——同系列化合物成批收录——随机划分不可能产生新颖骨架。自然的 推论是:渲染测试集上的 exact match 很大程度上测量的是同系列内的插值,而不是对陌生 骨架的识别能力。

这个推论可以在不重训的情况下检验,而且它没有通过检验。965 条(19.3%) Bemis-Murcko 骨架在训练语料中完全不存在的测试分子被单独评分:

评测集 分子数 exact
完整测试集 1000 0.779
仅新颖骨架 965 0.811

差值为 +0.033,95% 区间 ±0.036——1.8 sigma,不显著。所以诚实的结论是"在未见 骨架上没有可检测到的性能下降",而不是新颖骨架更容易。混杂因素已检查:新颖骨架子集 的分子略大(29.2 vs 28.4 重原子)、骨架更大(25 vs 22 原子),环数、立体中心密度和 SMILES 长度都相当。

MolScribe 也在两个子集上评了分,作为外部标定。它从未在本项目的 ChEMBL 划分上训练 过,因此它的分数独立于任何在此训练的模型,探测的是两个子集本身的难度:

模型 完整测试集 新颖骨架 差值
MolScribe(外部) 0.647 0.644 -0.002
MolMini 0.779 0.811 +0.033

MolScribe 认为两者难度相当,所以新颖骨架子集本身并不更容易,MolMini 的结果不是子集 难度造成的假象。两个模型——一个在本语料上训练、一个没有——都没有在未见骨架上出现 性能下降,这正是"OCSR 本质上是视觉任务"所预期的:读一张图不需要事先见过那个骨架。

这令人安心——模型似乎在做视觉识别,而不是从记住的化学词表里检索——但它只回答了骨架 这一个维度。上面的指纹相似度结果和立体异构体通道并未因此改变。

同样不受影响:图像仍然是未见过的,degraded 档仍然测量抗退化能力,DECIMER 手绘 结果仍然是对陌生输入迁移能力的诚实度量。

这对数据划分意味着什么。 低骨架新颖度是数据的真实性质,本报告如实说明而非掩盖; 但唯一能直接检验的那个维度——骨架熟悉度——并未对准确率产生可检测的影响。在训练阶段 采用骨架划分或相似度上限划分仍然值得做,因为它还能覆盖本次检验未触及的指纹相似度和 立体异构体两个维度。

这个基准测量什么、不测量什么

请在阅读结果表之前先读这一节。

渲染评测集是由与训练完全相同的渲染器和增强管线生成的。分子是留出的——划分按 规范 SMILES 不相交并在构建时断言——但渲染分布是相同的。所以这些评测集测量的是:

给定来自 MolMini 训练分布的作图、以及它从未见过的分子,它读得多准?

这是个真实且有用的问题。但它不是"MolMini 是不是比 MolScribe 更好的 OCSR 系统"这个问题。任何外部系统在这里都是在自己训练分布之外被评分,而本项目的模型是在 主场被评分。特别是 degraded 档上的大幅领先,反映的是那套退化管线属于 MolMini 而不属于 MolScribe。

DECIMER 手绘集是保持诚实的对照:它对 MolMini 是陌生的,反而更接近 MolScribe 的 设计目标。那上面的排序是交错的,而不是干净的反转 —— 12.1M 模型高于 MolScribe (0.125 对 0.099),23.8M 模型低于它(0.088)—— 而且绝对水平上没有任何一个系统 读得好。两个方向永远一起报告。

6. 结果

chembl_test_linux

model params checkpoint n valid exact exact/no-stereo tanimoto measured on
oracle 0 - 3000 1.000 1.000 1.000 1.000 Darwin-arm64
molmini 23.8M step 240,000 3000 0.997 0.897 0.937 0.965 Darwin-arm64
molmini 12.1M step 240,000 3000 0.998 0.857 0.927 0.958 Darwin-arm64
molscribe pretrained external - 900 0.934 0.647 0.760 0.892 Linux-x86_64
image_hash_nn 0 - 3000 1.000 0.000 0.000 0.118 Darwin-arm64
style model params checkpoint n valid exact tanimoto
clean image_hash_nn 0 - 1000 1.000 0.000 0.129
clean molmini 12.1M step 240,000 1000 0.999 0.917 0.996
clean molmini 23.8M step 240,000 1000 0.998 0.952 0.997
clean molscribe pretrained external - 300 0.977 0.823 0.995
clean oracle 0 - 1000 1.000 1.000 1.000
degraded image_hash_nn 0 - 1000 1.000 0.000 0.112
degraded molmini 12.1M step 240,000 1000 0.995 0.748 0.882
degraded molmini 23.8M step 240,000 1000 0.997 0.806 0.901
degraded molscribe pretrained external - 300 0.863 0.350 0.678
degraded oracle 0 - 1000 1.000 1.000 1.000
varied image_hash_nn 0 - 1000 1.000 0.000 0.114
varied molmini 12.1M step 240,000 1000 0.999 0.905 0.996
varied molmini 23.8M step 240,000 1000 0.996 0.934 0.997
varied molscribe pretrained external - 300 0.963 0.767 0.978
varied oracle 0 - 1000 1.000 1.000 1.000

chembl_test_novel_scaffold

model params checkpoint n valid exact exact/no-stereo tanimoto measured on
molmini 23.8M step 240,000 2895 0.996 0.894 0.936 0.965 Darwin-arm64
molmini 12.1M step 240,000 2895 0.993 0.866 0.927 0.960 Darwin-arm64
molscribe pretrained external - 900 0.910 0.644 0.757 0.898 Linux-x86_64
style model params checkpoint n valid exact tanimoto
clean molmini 12.1M step 240,000 965 0.998 0.917 0.993
clean molmini 23.8M step 240,000 965 0.998 0.938 0.994
clean molscribe pretrained external - 300 0.987 0.827 0.988
degraded molmini 12.1M step 240,000 965 0.989 0.777 0.895
degraded molmini 23.8M step 240,000 965 0.995 0.808 0.908
degraded molscribe pretrained external - 300 0.797 0.347 0.692
varied molmini 12.1M step 240,000 965 0.994 0.904 0.993
varied molmini 23.8M step 240,000 965 0.996 0.936 0.993
varied molscribe pretrained external - 300 0.947 0.760 0.978

chembl_test_macfonts

model params checkpoint n valid exact exact/no-stereo tanimoto measured on
oracle 0 - 3000 1.000 1.000 1.000 1.000 Darwin-arm64
molmini 23.8M step 240,000 3000 0.998 0.888 0.926 0.962 Darwin-arm64
molmini 12.1M step 240,000 3000 0.997 0.859 0.925 0.959 Darwin-arm64
molscribe pretrained external - 900 0.913 0.646 0.757 0.910 Linux-x86_64
image_hash_nn 0 - 3000 1.000 0.000 0.000 0.119 Darwin-arm64
style model params checkpoint n valid exact tanimoto
clean image_hash_nn 0 - 1000 1.000 0.000 0.130
clean molmini 12.1M step 240,000 1000 0.996 0.917 0.996
clean molmini 23.8M step 240,000 1000 0.997 0.949 0.997
clean molscribe pretrained external - 300 0.977 0.823 0.996
clean oracle 0 - 1000 1.000 1.000 1.000
degraded image_hash_nn 0 - 1000 1.000 0.000 0.114
degraded molmini 12.1M step 240,000 1000 0.994 0.771 0.890
degraded molmini 23.8M step 240,000 1000 0.997 0.794 0.901
degraded molscribe pretrained external - 300 0.803 0.357 0.721
degraded oracle 0 - 1000 1.000 1.000 1.000
varied image_hash_nn 0 - 1000 1.000 0.000 0.113
varied molmini 12.1M step 240,000 1000 1.000 0.890 0.990
varied molmini 23.8M step 240,000 1000 0.999 0.922 0.987
varied molscribe pretrained external - 300 0.960 0.757 0.981
varied oracle 0 - 1000 1.000 1.000 1.000

decimer_hdm

按 issue #4 修正。 这组手绘数字是在 image_to_tensor 不再拉伸非方形输入之后 重测的;抽样的 200 张 DECIMER 图里有 179 张不是方形,也就是说它们此前全部是被拉伸 着送进模型的。在同样的 1000 张图、同样的 checkpoint 上,exact match 从 0.037 变为 0.088(23.8M),从 0.057 变为 0.125(12.1M)。渲染评测集是 384x384 方形图,补边在 那里是空操作,所以那些数字没有变化。MolScribe 走自己的预处理,不受影响。

model params checkpoint n valid exact exact/no-stereo tanimoto measured on
molscribe pretrained external - 1000 0.852 0.099 0.126 0.392 Linux-x86_64
molmini 12.1M step 240,000 1000 0.934 0.125 0.170 0.403 Darwin-arm64
molmini 23.8M step 240,000 1000 0.950 0.088 0.112 0.336 Darwin-arm64
style model params checkpoint n valid exact tanimoto
hand_drawn molmini 12.1M step 240,000 1000 0.934 0.125 0.403
hand_drawn molmini 23.8M step 240,000 1000 0.950 0.088 0.336
hand_drawn molscribe pretrained external - 1000 0.852 0.099 0.392

关于 degraded 档的一个说明。退化管线的参数是独立采样的,在极端组合下会产生 连人也无法辨认的图像——强纸张纹理叠加模糊,可以把细键结构完全淹没。因此 degraded 档的部分失败并不是模型读不出一张本可读的图。这一档应理解为"包含直至 不可辨认程度的抗退化能力",而不是真实扫描件性能的干净下界。

7. 局限

  • 随机划分无法测试新颖骨架。83% 的留出分子在训练集中有相近类似物(见第 5 节)。 在骨架确实未见过的子集上准确率没有下降,所以这一点看来并未抬高数字;但指纹相似度 和立体异构体两个维度仍未被检验,需要在训练阶段采用骨架划分或相似度上限划分。
  • 渲染基准是本模型的主场。任何外部系统在其上都是在自己的训练分布之外被评分。 手绘集是对冲,在那上面差距缩小到几个百分点,方向随模型大小而变 —— 没有任何一个 系统能读好这些手绘图。
  • 手绘图是分布外的。RDKit 的 comic 模式能画出抖动笔画,但复现不了人类的断笔、 不一致的键长和错位的标签。
  • Markush 结构、R 基团、反应式、多分子页面:不在语料中、不渲染、不支持。
  • 范围受限。超出声明的元素集和重原子数范围,报告的准确率不迁移。
  • 训练不是位级确定的。GPU kernel 调度本身不确定;重跑会接近但不会精确复现。
  • 训练日志里的 [eval] 行不能和评测表对比——分子不同、像素不同、每次重新采样。

8. 复现

全部命令见 docs/REPRODUCE.md。每个设计选择的依据和背后的实测数据见 docs/DECISIONS.md