
如果你是一名化学信息学研究者、药物研发工程师或者正在处理海量化学文献和专利那么你一定遇到过这个令人头疼的问题如何让计算机真正“看懂”一张化学结构图从 PDF 文献中提取一个苯环结构看似简单但面对复杂的 Markush 结构一种在专利中广泛使用的、表示一类化合物的通用结构式传统的光学化学结构识别OCSR工具往往束手无策。识别率低、对模糊图像敏感、无法处理通配符R基团和重复单元这些问题长期阻碍着化学信息的数字化和自动化分析。最近两个新的开源模型——MarkushGlyph和OCSRGlyph——的出现正在改变这一局面。它们并非简单的迭代更新而是针对 OCSR 任务中两个最核心的痛点提出了全新的解决方案架构。简单来说OCSRGlyph专攻“识别准”它革新了原子和化学键的检测与分类方式在标准化学结构识别上达到了新的精度。MarkushGlyph则挑战“认得全”它是首个专门为理解复杂的、带有不确定性的 Markush 结构而设计的端到端模型填补了该领域的空白。本文将深入解析这两个模型为何重要、它们解决了什么具体问题、以及你如何快速上手使用。我们不止步于介绍论文更会提供清晰的环境搭建、代码运行、结果解读和避坑指南让你能切实地将这项前沿技术应用到自己的研究或项目中。1. 化学结构识别OCSR的困境与破局点在深入模型之前我们必须先理解问题的复杂性。化学结构识别Optical Chemical Structure Recognition, OCSR的目标是将化学结构图像如从PDF中截取的图表自动转换为机器可读的格式如SMILES、InChI或Mol文件。传统流程与核心挑战传统的OCSR流程通常是一个“流水线”包括图像预处理、图形元素分割分离出原子符号、键线、光学字符识别OCR识别原子标签最后通过规则或图算法重建化学结构。这个流程脆弱且容易出错图像质量依赖性强扫描模糊、低分辨率、倾斜的图像会导致分割失败。上下文依赖严重一个“C”是碳原子还是“Cl”的一部分需要依赖周围像素判断。Markush 结构是“噩梦”专利中的 Markush 结构包含 R 基团可变取代基、重复单元n、原子列表如 C, N, O等。传统方法缺乏统一的框架来理解和表示这种不确定性。MarkushGlyph 和 OCSRGlyph 的破局思路这两个模型都采用了“目标检测”的现代深度学习范式但进行了关键改造将化学结构视为“图文”混合体不再将原子符号视为需要OCR识别的“文本”而是将其与化学键一样视为图像中需要被检测和分类的“视觉对象”Glyph字形。端到端学习模型直接从原始图像像素学习预测原子和键的位置、类别以及它们之间的连接关系避免了脆弱的多阶段流水线。专门化设计OCSRGlyph 优化了对标准、确定性结构的识别精度MarkushGlyph 则扩展了原子和键的类别体系新增了用于表示不确定性的特殊“Glyph”从而原生支持 Markush 结构。理解这一点你就明白了它们的价值它们不是优化了某个环节而是用一套新的“语言”基于Glyph的目标检测重新定义了OCSR任务尤其是为Markush结构识别提供了可行的技术路径。2. 核心概念解析Glyph、Markush 与模型架构2.1 什么是 Glyph字形在这两个模型中“Glyph”是一个核心抽象。你可以把它理解为化学结构图像中的一个基本视觉单元。它主要分为两类原子 Glyph代表一个原子符号如 “C”, “N”, “O”或一个原子团如 “CH3”, “Ph”。在 MarkushGlyph 中还包括了特殊 Glyph如 “R”可变取代基、”*”连接点、”[n]”重复单元指示符等。键 Glyph代表化学键如单键、双键、三键、楔形键立体化学键、芳香键等。模型的任务就是在图像中找出所有这些 Glyph并预测它们的类别、边界框以及连接关系。2.2 什么是 Markush 结构Markush 结构是化学专利中用于保护一类化合物的表述方式。它通过引入“变量”来定义一组结构相似的化合物。R 基团表示该位置可以被一系列指定的原子或基团取代。重复单元例如 “(CH2)n”表示亚甲基单元可以重复 n 次n为整数。原子列表例如 “X C, N, O”表示该位置可以是碳、氮或氧原子。环系变化表示环的大小或组成原子可以变化。传统 OCSR 输出一个确定的分子而 Markush 结构识别需要输出一个带有变量的模板。MarkushGlyph 的识别目标正是这种模板。2.3 模型架构总览两个模型都基于强大的目标检测框架如 YOLO 或 DETR 变体但 head头部设计针对化学结构进行了定制。OCSRGlyph 架构要点骨干网络通常采用 ResNet 或 Transformer 架构的视觉编码器用于从图像提取特征。检测头同时预测原子 Glyph 和键 Glyph 的边界框及类别。关系解码关键的一步。模型不仅检测出独立的 Glyph还通过一个额外的网络头或后处理步骤预测哪些原子 Glyph 和键 Glyph 是相连的从而构建出化学图。MarkushGlyph 架构要点在 OCSRGlyph 的基础上主要进行了以下扩展扩展的 Glyph 类别体系在原子类别中加入了R_GROUP,ATTACHMENT_POINT,REPEATING_UNIT等在键类别中也考虑了 Markush 特有的表示法。增强的关系建模Markush 结构中连接关系更复杂如虚键表示可选连接模型需要更精细的关系推理能力。输出表示最终输出不是标准的 SMILES而是一种能够表达变量和选择的中间表示如带有 R 组标记的 Mol 文件或自定义 JSON 格式后续可转换为 MRVMarvin文件或 RGroup 分解可用的格式。3. 环境准备从零搭建运行环境要运行或测试这些模型你需要一个配置合理的 Python 环境。以下步骤将引导你完成设置。3.1 基础环境要求操作系统Linux (Ubuntu 20.04/22.04 推荐) 或 Windows (WSL2 推荐)。macOS 也可行但需注意某些依赖的编译。Python版本 3.8 或 3.9。3.10 可能存在部分包兼容性问题建议使用 3.9。包管理器使用conda或venv创建虚拟环境强烈推荐以避免依赖冲突。深度学习框架PyTorch。模型通常基于 PyTorch 实现。CUDA如果你有 NVIDIA GPU 并希望加速训练和推理需要安装对应版本的 CUDA 和 cuDNN。CPU 也可运行但速度较慢。3.2 逐步安装指南我们以 Linux/ WSL2 环境为例使用 conda 管理环境。步骤 1创建并激活虚拟环境# 创建名为 ocr_glyph 的 Python 3.9 环境 conda create -n ocr_glyph python3.9 -y conda activate ocr_glyph步骤 2安装 PyTorch访问 PyTorch 官网 获取适合你 CUDA 版本的安装命令。例如对于 CUDA 11.8pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118如果仅使用 CPUpip install torch torchvision torchaudio步骤 3安装核心科学计算与图像处理库pip install numpy pandas matplotlib opencv-python pillow scikit-learn步骤 4安装化学信息学工具包RDKit 是处理化学结构的基石但它的安装有时比较棘手。使用 conda 安装通常最可靠# 首先添加 conda-forge 通道如果尚未添加 conda config --add channels conda-forge conda config --set channel_priority strict # 安装 rdkit conda install -c conda-forge rdkit -y验证 RDKit 安装python -c import rdkit; print(rdkit.__version__)步骤 5克隆项目仓库并安装依赖假设项目的代码托管在 GitHub 上这是常见情况。你需要找到MarkushGlyph和OCSRGlyph的官方仓库。# 示例请替换为实际仓库URL git clone https://github.com/author_name/OCSRGlyph.git git clone https://github.com/author_name/MarkushGlyph.git cd OCSRGlyph # 安装项目特定的依赖通常通过 requirements.txt pip install -r requirements.txt # 如果项目是 pip 可安装的也可能需要 pip install -e .对MarkushGlyph重复类似操作。步骤 6安装其他可能需要的工具PDF 处理如果需要从 PDF 提取图像你可能需要pdf2image和 Poppler。pip install pdf2image # Ubuntu 安装 Poppler sudo apt-get install poppler-utils可视化用于绘制化学结构。pip install cairosvg # 如果需要 SVG 支持完成以上步骤后你的基础环境就准备好了。接下来我们进入核心的模型使用环节。4. 实战使用 OCSRGlyph 识别标准化学结构我们假设你已经成功克隆了OCSRGlyph仓库并安装了依赖。以下是一个完整的从图像到分子对象的推理流程。4.1 准备输入图像模型输入通常是裁剪好的、只包含单个化学结构的 PNG 或 JPEG 图像。背景最好是白色结构清晰。你可以从公开数据集中获取或者自己从 PDF 中裁剪。 例如将你的图像命名为test_structure.png。4.2 运行推理脚本查看项目根目录通常会有predict.py或inference.py这样的脚本。你需要准备一个配置文件config.yaml和训练好的模型权重.pth文件。作者通常会提供预训练模型。示例命令行python tools/inference.py \ --config configs/ocsrglyph_config.yaml \ --checkpoint path/to/pretrained_ocsrglyph.pth \ --image_path ./test_structure.png \ --output_dir ./results4.3 代码解读与自定义推理如果项目提供了更灵活的 API你可以编写自己的 Python 脚本。下面是一个模拟的、基于典型代码结构的示例# 文件infer_ocsr.py import torch from PIL import Image import cv2 from models.ocsrglyph import OCSRGlyph from utils.config import get_config from utils.postprocess import glyphs_to_mol def main(): # 1. 加载配置和模型 cfg get_config(configs/ocsrglyph_config.yaml) model OCSRGlyph(cfg) # 加载预训练权重 checkpoint torch.load(pretrained/ocsrglyph_best.pth, map_locationcpu) model.load_state_dict(checkpoint[model]) model.eval() # 设置为评估模式 model.to(cuda if torch.cuda.is_available() else cpu) # 2. 预处理图像 image_path test_structure.png # 使用项目提供的预处理函数通常包括缩放、归一化等 image Image.open(image_path).convert(RGB) # 假设有一个预处理的 transform from utils.transforms import get_inference_transform transform get_inference_transform(cfg.INPUT.SIZE) input_tensor transform(image).unsqueeze(0) # 增加 batch 维度 # 3. 模型推理 with torch.no_grad(): if torch.cuda.is_available(): input_tensor input_tensor.cuda() # 模型输出可能包括原子/键的预测框、类别、分数、连接关系 outputs model(input_tensor) # 4. 后处理将 Glyph 预测转换为化学分子 # 这通常是项目中最复杂的部分之一 atom_glyphs outputs[atom_preds] # 形状: [N_atoms, 6] (x1,y1,x2,y2,class,score) bond_glyphs outputs[bond_preds] # 形状: [N_bonds, 6] adjacency outputs[adjacency] # 可能是一个邻接矩阵或连接列表 # 使用项目提供的后处理函数 mol glyphs_to_mol(atom_glyphs, bond_glyphs, adjacency) # 5. 输出结果 if mol is not None: # 生成 SMILES from rdkit import Chem smiles Chem.MolToSmiles(mol) print(f识别成功的 SMILES: {smiles}) # 保存分子图像用于可视化对比 from rdkit.Chem import Draw img Draw.MolToImage(mol, size(300, 300)) img.save(./results/recognized_mol.png) # 保存为 Mol 文件 writer Chem.SDWriter(./results/recognized_mol.mol) writer.write(mol) writer.close() else: print(结构识别失败可能后处理无法构建有效分子。) if __name__ __main__: main()关键点说明配置与权重你必须使用与预训练模型匹配的配置文件。config.yaml定义了模型结构、输入尺寸、类别数等关键参数。后处理glyphs_to_mol是核心函数负责将模型输出的离散检测框和连接预测组合成一个连贯的 RDKit 分子对象。这个函数通常由项目提供逻辑复杂不建议自己从头实现。设备代码中自动判断使用 GPU 或 CPU。5. 实战使用 MarkushGlyph 识别专利结构MarkushGlyph的使用流程与OCSRGlyph类似但输入输出更具挑战性。5.1 输入图像的特点Markush 结构图像可能更复杂包含大量的 R、R1、R2 等标记。有虚线框、虚线键表示可选部分。结构可能非常大占据多页。 在预处理时可能需要先将大图分割成包含单个子结构的区域。有些工具或模型可能集成了分割功能。5.2 推理与输出解析运行推理的命令行类似python markush_inference.py \ --model_cfg configs/markush_config.yaml \ --weights path/to/markushglyph.pth \ --input ./patent_diagram.png \ --output ./markush_output.json5.3 理解输出从 Glyph 到 Markush 模板MarkushGlyph的输出不是标准分子而是一个结构模板。以下是一个模拟的输出 JSON 结构展示了核心信息{ success: true, input_image: patent_diagram.png, recognized_glyphs: { atoms: [ {id: 0, type: C, bbox: [x1, y1, x2, y2], confidence: 0.99}, {id: 1, type: C, bbox: [x1, y1, x2, y2], confidence: 0.98}, {id: 2, type: N, bbox: [x1, y1, x2, y2], confidence: 0.97}, {id: 3, type: R_GROUP, label: R1, bbox: [x1, y1, x2, y2], confidence: 0.96}, {id: 4, type: ATTACHMENT_POINT, connected_to: [3], bbox: [x1, y1, x2, y2], confidence: 0.95} ], bonds: [ {id: 0, type: SINGLE, from: 0, to: 1, confidence: 0.99}, {id: 1, type: DOUBLE, from: 1, to: 2, confidence: 0.98}, {id: 2, type: SINGLE, from: 0, to: 3, confidence: 0.97}, {id: 3, type: DASHED, from: 4, to: 5, confidence: 0.90, optional: true} ] }, markush_template: { core_smiles: C1CCCCC1, // 核心骨架的SMILES r_groups: { R1: { attachment_atom: 0, // 连接到核心骨架的原子ID variants: [H, CH3, OCH3, Cl] // 可能的取代基列表可能需要从专利文本额外解析 } }, repeating_units: [ {unit_smiles: C, min_count: 1, max_count: 6, label: n} ] } }如何利用这个输出可视化检查根据recognized_glyphs中的边界框信息可以在原图上绘制检测结果验证识别准确性。生成核心分子利用core_smiles和 RDKit可以构建出核心骨架的分子对象。R 组处理r_groups信息可以与专利文本挖掘结合用于枚举化合物库或进行 R 组分析。转换为标准格式可以编写代码将此 JSON 转换为 MRV (Marvin) 文件或直接用于下游的化学信息学管道。6. 效果验证与性能评估运行模型后如何判断识别得好不好6.1 定性评估肉眼检查这是最直接的方法。将原始图像、模型检测出的 Glyph用框标出、以及最终生成的分子结构或 Markush 模板并排显示。使用 RDKit 和 Matplotlib 进行可视化对比的示例代码import matplotlib.pyplot as plt from PIL import Image import cv2 from rdkit.Chem import Draw def visualize_comparison(orig_image_path, atom_boxes, bond_boxes, rdkit_mol, output_pathcomparison.png): fig, axes plt.subplots(1, 3, figsize(15, 5)) # 子图1原始图像 orig_img Image.open(orig_image_path) axes[0].imshow(orig_img) axes[0].set_title(Original Image) axes[0].axis(off) # 子图2Glyph检测结果绘制边界框 # 假设 atom_boxes, bond_boxes 是列表每个元素为 [x1, y1, x2, y2, class_id, score] img_with_boxes cv2.imread(orig_image_path) img_with_boxes cv2.cvtColor(img_with_boxes, cv2.COLOR_BGR2RGB) for box in atom_boxes: x1, y1, x2, y2, cls, conf box cv2.rectangle(img_with_boxes, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2) # 蓝色框-原子 # 可以在框附近添加类别文本 label fAtom:{cls}({conf:.2f}) cv2.putText(img_with_boxes, label, (int(x1), int(y1)-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,0), 1) for box in bond_boxes: x1, y1, x2, y2, cls, conf box cv2.rectangle(img_with_boxes, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) # 红色框-键 axes[1].imshow(img_with_boxes) axes[1].set_title(Detected Glyphs (Blue:Atom, Red:Bond)) axes[1].axis(off) # 子图3RDKit生成的分子 if rdkit_mol is not None: mol_img Draw.MolToImage(rdkit_mol, size(300, 300)) axes[2].imshow(mol_img) axes[2].set_title(Recognized Molecule) else: axes[2].text(0.5, 0.5, Recognition Failed, hacenter, vacenter) axes[2].axis(off) plt.tight_layout() plt.savefig(output_path, dpi150) plt.show() print(f对比图已保存至: {output_path}) # 调用函数 # visualize_comparison(test.png, atom_preds, bond_preds, mol, result_comparison.png)6.2 定量评估使用标准数据集如果你是进行严肃的研究或模型比较需要使用带有真实标注Ground Truth的数据集进行定量评估。常见评估指标Glyph 检测指标使用目标检测领域的标准指标如平均精度Average Precision, AP分别计算原子和键的检测精度。分子级指标比较预测的分子SMILES与真实分子是否一致。常用精确匹配率或Tanimoto 相似度基于分子指纹。对于 MarkushGlyph评估更为复杂可能包括 R 组检测的准确率、连接点识别的正确率等。需要专门标注的 Markush 数据集。运行官方评估脚本项目通常提供eval.py脚本。python tools/eval.py \ --config configs/ocsrglyph_config.yaml \ --checkpoint path/to/model.pth \ --dataset_dir ./path/to/test_dataset \ --output_metrics ./eval_results.json评估脚本会遍历测试集计算各项指标并输出报告。7. 常见问题与排查指南在实际使用中你可能会遇到以下典型问题。这里提供排查思路。问题现象可能原因排查方式解决方案导入错误No module named ‘models’Python 路径问题项目未正确安装或环境未激活。1. 确认当前目录在项目根目录下。2. 运行python -c “import sys; print(sys.path)”检查路径。3. 检查__init__.py文件是否存在。1. 在项目根目录执行pip install -e .。2. 设置PYTHONPATHexport PYTHONPATH/path/to/project_root:$PYTHONPATH。运行时错误CUDA out of memoryGPU 内存不足。输入图像太大或批次大小batch size设置过高。1. 检查nvidia-smi确认 GPU 内存使用。2. 查看配置文件中INPUT.SIZE和SOLVER.IMS_PER_BATCH。1. 在配置中减小INPUT.SIZE如从 1024 降到 512。2. 将推理时的批次大小设为 1。3. 使用 CPU 模式。模型输出mol为None后处理失败。可能原因1. 检测到的原子/键太少。2. 原子/键的连接关系无法构成有效化学图如孤立的原子。3. 置信度阈值过高过滤掉了有效预测。1. 可视化检测到的 Glyph看是否漏检严重。2. 检查glyphs_to_mol函数的输入数据特别是连接关系矩阵。3. 调低配置中TEST.CONFIDENCE_THRESHOLD。1. 尝试对输入图像进行预处理二值化、去噪、调整大小。2. 调整后处理参数如允许连接的最大距离。3. 如果项目支持尝试不同的后处理算法。识别 Markush 结构时R 组标签混乱1. 图像中 R 组标记模糊或特殊字体。2. 模型对相似字符如 R, R1, R2分类错误。3. 训练数据中此类样本不足。1. 检查原图质量。2. 查看模型对R_GROUP类别的预测置信度。3. 统计错误案例看是否有规律。1. 使用图像预处理增强对比度。2. 考虑对模型进行微调Fine-tuning加入自己领域的专利图像数据。3. 在后处理中添加规则根据位置对 R 组进行重新编号。推理速度非常慢1. 使用 CPU 模式。2. 模型过大如 Transformer 骨干网络。3. 图像尺寸过大。1. 确认torch.cuda.is_available()为 True。2. 使用性能分析工具如torch.profiler。3. 检查输入图像尺寸是否与训练尺寸匹配。1. 确保 CUDA 和 PyTorch GPU 版本匹配。2. 尝试更轻量级的骨干网络配置如果提供。3. 将大图预先分割成小图再识别。安装 RDKit 失败系统依赖缺失或渠道问题。查看 conda 或 pip 的错误信息。首选方案使用 conda 从 conda-forge 安装conda install -c conda-forge rdkit。备选在 Linux 上尝试从源码编译但过程复杂。8. 最佳实践与工程化建议要将这些研究模型应用于实际生产或研究流水线需要考虑以下方面8.1 数据预处理标准化图像归一化建立统一的预处理流程包括转换为灰度图或RGB、二值化对于清晰线条图、尺寸调整保持长宽比并填充至模型输入尺寸、归一化像素值。PDF 提取优化使用pdf2image或PyMuPDF时设置合适的 DPI建议 300-400 DPI。对于复杂的专利文档可能需要先进行版面分析定位化学图表区域。处理大图对于超大的化学结构图实现一个滑动窗口或基于连通成分分析的自动分割算法将大图切割成多个可处理的小图识别后再拼接结果。8.2 模型集成与微调模型集成对于关键任务可以同时运行OCSRGlyph和另一个优秀的开源 OCSR 工具如DECIMER或ChemGrapher通过投票或置信度加权的方式融合结果提高鲁棒性。领域微调如果你主要处理某一特定领域如有机化学、高分子、药物专利的图表收集几百张该领域的标注图像对预训练模型进行微调能显著提升在该领域的识别率。注意需要按照项目要求的格式准备标注数据通常是 COCO 或自定义 JSON 格式。持续验证建立一个包含多样本、多难度的测试集每次模型更新后都运行评估监控性能变化。8.3 后处理与结果校验化学规则校验利用 RDKit 的化学有效性检查。识别生成的分子先用Chem.SanitizeMol(mol)进行 sanitization如果失败则说明结构可能存在价态、键级等化学不合理之处需要记录为低置信度结果或触发人工复核。与文本信息交叉验证在专利或文献中化学结构附近常有文本描述如分子式、化合物编号。可以将识别出的 SMILES 计算的分子式与文本中提取的分子式进行比对作为一致性校验。置信度过滤为原子、键的检测置信度以及最终分子生成设置阈值。低于阈值的结果应被标记不直接进入下游流程。8.4 生产环境部署考虑服务化使用 FastAPI 或 Flask 将模型封装为 RESTful API 服务。这样可以被其他系统如文献挖掘平台、ELN 系统调用。# 简化的 FastAPI 服务示例 from fastapi import FastAPI, File, UploadFile from PIL import Image import io app FastAPI() # 加载模型 (全局变量启动时加载一次) model load_your_model() app.post(/recognize/) async def recognize_structure(file: UploadFile File(...)): contents await file.read() image Image.open(io.BytesIO(contents)) result model.predict(image) return {smiles: result[smiles], confidence: result[confidence]}批处理与队列对于大量文件的处理使用消息队列如 RabbitMQ, Redis和后台任务队列如 Celery进行异步处理避免 HTTP 请求超时。监控与日志记录每次请求的输入哈希、处理时间、识别结果、置信度以及任何错误信息。这有助于追踪问题和分析模型性能衰减。8.5 处理 Markush 结构的特殊策略分而治之复杂的 Markush 结构图可能包含多个子结构或示例化合物。开发一个简单的基于规则或机器学习的分割模块将大图分解为多个独立的识别任务。模板库匹配对于常见药效团或骨架可以建立 Markush 模板库。当识别出一个结构时先与模板库进行子图匹配匹配成功则直接应用模板的变量定义可以提高准确性和效率。人机协同全自动识别 Markush 结构目前仍是极高难度的挑战。设计一个良好的人机交互界面GUI让专家可以快速修正模型识别出的 Glyph 和连接关系并将修正后的数据反馈用于模型改进是可行的落地路径。通过遵循这些最佳实践你可以将MarkushGlyph和OCSRGlyph从研究原型稳步地转化为能够解决实际化学信息提取问题的强大工具。记住没有哪个模型是万能的理解其能力边界并在关键环节引入校验和人工复核是构建可靠系统的关键。