コンテンツへスキップ

化学構造のアノテーションと OCSR の修正

OCSR の出力を修正し、描かれた構造を SMILES または SMARTS の正解キーで採点し、分子上の原子にマークを付けて、InChIKey と SD ファイルをエクスポートします。

v2.13.0 で追加

chemical_structure アノテーションスキームは、アノテーターに構造エディターを提供し、molecule ディスプレイは、アノテーターが原子を選択できる低分子を描画します。化学データセットの構築者は、この2つを構造の修正に使います。構造の修正では、アノテーターが、特許や論文の図から認識された構造をその図と見比べて、誤って読み取られた原子と結合を直します。化学教育の研究者や教員は、エディターを使って、描かれた構造(たとえば、化合物名が表す構造)を集めます。Potato は、サーバー上で、各描画を正解キーに照らして採点します。キュレーターは、molecule ディスプレイと viewer_selection スキームを使って、反応中心や代謝部位などの原子にマークを付けます。各回答は、描画の正規 SMILES とともに molfile として保存され、chemistry エクスポートは、すべてのアノテーターの構造を CSV ファイルと SD ファイルに書き出します。

光学的化学構造認識(OCSR)は、化学構造の画像を、機械可読な構造に変換します。Potato は OCSR モデルを実行しないため、別のツールが生成した認識結果の構造を読み込みます。SMILES は、構造を短いテキストの1行で表し、酢酸なら CC(=O)O です。MDL の molfile は、chemical table file ファミリーの結合表テキスト形式で、SD ファイルは、多数の molfile を保持し、各 molfile のあとに名前付きのデータフィールドが続きます。InChIKey は、IUPAC の InChI 識別子の27文字のハッシュで、同じ構造を描いた2つの描画は、同じ InChIKey になります。

アスピリンの元の図と、カルボン酸をアミドと誤認した認識結果の構造を並べた画面。下に選択可能な原子リストがあるOCSR の結果を元の図と照合します

  • エディター: Potato のリポジトリには、エディターとして OpenChemLib JS 9.25.0(BSD-3-Clause)が含まれています。エディターは、結合、環、元素、電荷、立体ウェッジ、消去、元に戻すのツールバーを備えたキャンバスに描画します。キャンバスの下のテキストボックスは、入力または貼り付けた SMILES 文字列や molfile を受け付け、このテキストボックスが、エディターへのキーボード経由の入口です。
  • RDKit.js(任意): RDKit の JavaScript MinimalLib 2026.3.6(BSD-3-Clause)は、7 MB の WebAssembly ビルドで、コミットされていません。potato vendor fetch rdkit がビルドをダウンロードし、ダウンロードしたものを npm レジストリの整合性ハッシュと照合します。RDKit.js があると、ブラウザは RDKit の正規 SMILES、InChI、InChIKey も計算し、RDKit がサニタイズできない構造にフラグを付けます。すると molecule ディスプレイは、engine: rdkit で描画できます。
  • chemistry エクストラ(任意): このエクストラは、サーバーに rdkit Python パッケージをインストールします。このパッケージは正解キーを採点し、chemistry エクスポートは、RDKit.js なしで保存された回答の InChI と InChIKey を、このパッケージで補います。
  • WebGL なし: どちらのツールキットも2次元で描画します。
  • オフライン: 構造は、項目、またはメディアディレクトリ内のファイルから取得され、正解キーがブラウザに届くことはありません。

各インストールで利用できる機能は次のとおりです。

機能OpenChemLib のみRDKit.js ありchemistry エクストラあり
エディター、molfile、OpenChemLib の SMILES と ID コード、分子式、価数チェックはいはいはい
保存された回答内の RDKit の SMILES、InChI、InChIKeyいいえはいエクスポート時に補完
描画中の RDKit によるサニタイズのチェックいいえはいいいえ
engine: rdkit を使う molecule ディスプレイいいえはいいいえ
smiles、molfile、SMARTS のキーによる採点いいえいいえはい
inchikey のキーによる採点いいえはい(ブラウザからの InChIKey を使用)はい

サーバー側の採点にはエクストラが必要で、RDKit.js は別途フェッチします。

bash
pip install 'potato-annotation[chemistry]'
potato vendor fetch rdkit     # optional, InChIKeys in the browser

フェッチのあとは、サーバーを再起動してください。RDKit.js がない間、potato validate は、rdkit: auto のままになっている chemical_structure スキームごとに警告を出し、フェッチのコマンドを示します。

設定

次の設定は、OCSR の修正を構成します。各項目は、元の図を認識された構造の隣に表示し、アノテーターは、認識器が読み誤った原子にマークを付け、エディターで構造を直し、判定を与えます。

yaml
annotation_task_name: "Structure recognition check"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
data_files: [data/structures.json]
item_properties: {id_key: id, text_key: note}
 
instance_display:
  layout:
    direction: horizontal
  fields:
    - key: page_image             # the figure cropped from the patent or paper
      type: image
      label: "Source image"
    - key: ocsr_smiles            # what the OCSR tool produced
      type: molecule
      label: "Recognized structure"
      display_options:
        height: 300
 
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: viewer_selection
    name: wrong_atoms
    description: "Mark the atoms the recognizer got wrong"
    viewer_field: ocsr_smiles
    labels: ["wrong element", "wrong bond or charge", "wrong stereo"]
 
  - annotation_type: chemical_structure
    name: corrected
    description: "Correct the structure so it matches the source image"
    prefill_field: ocsr_smiles
    answer_field: true_smiles     # graded in the export, never sent to the page
    label_requirement: {required: true}
 
  - annotation_type: radio
    name: verdict
    description: "How good was the recognition?"
    labels: ["Correct", "Minor errors", "Wrong structure"]
    label_requirement: {required: true}

エディターは ocsr_smiles から始まります。事前入力された構造をアノテーターがそのままにすると、それは changed: false として保存された回答になります。したがって、正しい認識結果は編集が不要です。verdict の質問は、受け入れられた構造と、アノテーターが一度も確認しなかった項目を区別します。Potato は、ページが受け取るすべてのものから true_smiles フィールドを取り除きます。エクスポートでは、answer_correct が、修正された各構造が true_smiles と一致するかどうかを示します。answer_correct と changed を比べると、見逃した誤り(変更なしで誤り)と、持ち込まれた誤り(変更ありで誤り)を区別できます。

各項目は、media_directory からの相対パスで図を指定し、両方の構造を与えます。

json
{"id": "fig1_aspirin", "note": "Compound 1 as printed.", "page_image": "fig1_aspirin.png",
 "ocsr_smiles": "CC(=O)Oc1ccccc1C(=O)N", "true_smiles": "CC(=O)Oc1ccccc1C(=O)O"}

名前から構造を描く

名前から構造を描く演習には、1つのスキームと、項目ごとの正解キーが必要です。次の設定は、化合物名を項目のテキストとして表示します。

yaml
annotation_task_name: "Name to structure"
task_dir: .
output_annotation_dir: annotation_output
data_files: [data/names.json]
item_properties: {id_key: id, text_key: name}
 
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: chemical_structure
    name: drawn
    description: "Draw the structure this name describes"
    answer_field: expected
    rdkit: false                  # OpenChemLib only, no RDKit.js warning
    editor_height: 420
    label_requirement: {required: true}
json
{"id": "q2", "name": "(S)-2-aminopropanoic acid", "expected": "C[C@H](N)C(=O)O"}

学生は、マウスで描くことも、SMILES 文字列を入力して Enter を押すこともできます。rdkit: false では、ページは OpenChemLib だけを読み込み、InChIKey を保存しません。chemistry エクストラは、エクスポート時に描画を採点します。

分子上の原子にマークを付ける

原子へのマークは、molecule ディスプレイと viewer_selection スキームを組み合わせます。次の設定は、キュレーターに代謝部位へのマークを求め、参考としてモデルの予測部位を琥珀色で描画します。

yaml
annotation_task_name: "Sites of metabolism"
task_dir: .
output_annotation_dir: annotation_output
data_files: [data/molecules.json]
item_properties: {id_key: id, text_key: note}
 
instance_display:
  fields:
    - key: smiles
      type: molecule
      display_options:
        show_atom_numbers: true
        highlight_field: predicted_site   # a model's predicted atoms, drawn in amber
 
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: viewer_selection
    name: som
    description: "Mark each atom where the compound is metabolized"
    viewer_field: smiles
    labels: ["hydroxylation", "N-dealkylation", "glucuronidation"]
    min_selections: 1
    label_requirement: {required: true}
json
{"id": "m1", "note": "Predicted sites in amber.", "smiles": "CC(=O)Nc1ccc(O)cc1", "predicted_site": [0, 8]}

highlight_field が指定する項目フィールドは、0始まりの原子インデックスを、リスト、または "0, 8" のようなテキストとして保持します。ハイライトは固定で、キュレーターは、そのハイライトとは無関係に原子を選択します。min_selections と label_requirement を併用すると、キュレーターが少なくとも1つの原子を選択するまで、「次へ」はブロックされたままです。

構造データ

構造フィールドは、SMILES 文字列、molfile のテキスト、またはメディアディレクトリ内の .mol、.sdf、.smi、.smiles ファイルの名前を保持します。サーバーは、.sdf ファイルの最初のレコードと、.smi ファイルの最初の空でない行の最初のトークンを読み込みます。構造のテキストと構造ファイルは、1 MB に制限されています。SMILES 文字列には空白がないため、空白を含み、molfile でもない値は拒否されます。ディスプレイが URL を取得することはなく、Potato はメディアディレクトリの外のファイルパスを拒否します。

オプション

chemical_structure のオプションデフォルト意味
prefill_fieldなしSMILES 文字列、molfile、または構造ファイル名を持つ項目フィールド。エディターはその構造から始まります
prefill_formatautoauto、smiles、molfile。prefill_field が必要です
answer_fieldなし正解キーを持つ項目フィールドで、chemistry エクスポートで採点されます。prefill_field とは異なっていなければならず、ページが受け取るものからは取り除かれます
rdkitautoauto は、インストールされていれば RDKit.js を使います。true は RDKit.js を必須とし、バンドルがないと potato validate とサーバーの起動が失敗します。false は OpenChemLib だけを読み込み、警告を出さなくします
require_validtrue価数に問題がある構造、または RDKit.js がサニタイズできない構造があると、その項目は未完了のままになります。スキームが必須の場合に適用されます
allow_emptyfalse空のキャンバスを回答として受け付けます
editor_height380エディターの高さ(ピクセル)。240から900
show_identifierstrueエディターの下に SMILES、分子式、InChIKey を表示します
instructionsなし質問の下のテキスト
label_requirementなし{required: true} でスキームを必須にします
molecule ディスプレイのオプションデフォルト意味
engineopenchemlibopenchemlib または rdkit。engine: rdkit の設定は、potato vendor fetch rdkit を実行するまで potato validate に失敗します
height320描画の高さ。ピクセル、または CSS の長さ
formatautoauto、smiles、molfile
show_atom_numbersfalse各原子の molfile 番号を、原子の隣に描画します
atom_listtrue描画の下に原子ボタンのリストを表示します。これが選択へのキーボード経由の入口です
highlight_fieldなし固定のハイライトで描画する、0始まりの原子インデックスを持つ項目フィールド
highlight_color#f59f00固定のハイライトの16進数の色
max_atoms500これより大きい分子は描画されず、ディスプレイがそのことを示します。1から5,000
allow_remotefalse受け付けられますが、このディスプレイには効果がありません
viewer_selection のオプションデフォルト意味
viewer_field必須instance_display 内の molecule フィールドの key
labelsなし選択の名前。文字列または {name, color}。新しい選択は現在のラベルを取り、各原子はラベルを付け替えられます
min_selectionsなしスキームが必須の場合、この数の原子が選択されるまで、項目は未完了のままです
max_selectionsなしこれを超えて原子を選択しようとすると、メッセージとともに拒否されます
instructionsなし質問の下のテキスト

正解キー

正解キーは、SMILES 文字列のみ、molfile、またはマッピングです。マッピングのすべての部分が合格しなければなりません。

キー合格する条件
smiles、molfile、inchikey描かれた構造の InChIKey が、キーの InChIKey と等しい。3つのうち1つを与えます
ignore_stereo: true構造のキーに加えると、結合性を符号化する InChIKey の最初の14文字のブロックだけを比較します。立体と同位体のラベルは確認されません
substructure描かれた構造が、SMARTS パターン、またはリスト内のすべてのパターンを含む
not_substructure描かれた構造が、どの SMARTS パターンも含まない

厳密モードは、標準 InChIKey を比較します。標準 InChI は、一部の互変異性体を1つの構造として扱います。そのため、2-ピリドンの描画は、2-ヒドロキシピリジンのキーに対して合格します。電荷は数えられ、エトキシドは、ignore_stereo があっても、エタノールに対して不合格になります。部分構造のキーは、回答の特徴を採点します。たとえば、カルボン酸なら {"substructure": "C(=O)[OH]"} です。

answer_field の値は、SMILES 文字列のみでもかまいません。ゴールドスタンダードの項目やトレーニング項目では、スキーム名の下にマッピング形式を書きます。

json
{"id": "gold_1", "gold_label": {"corrected": {"smiles": "CC(=O)Oc1ccccc1C(=O)O"}}}
{"id": "train_1", "correct_answers": {"drawn": {"substructure": "C(=O)[OH]"}}}

品質管理とトレーニングフェーズは、エクスポートと同じ比較でこれらのキーを採点します。エディターは、アノテーションフェーズでのみ prefill_field を読み込みます。したがって、トレーニング項目は空のキャンバスで開きます。

rdkit Python パッケージがない場合、smiles、molfile、SMARTS のキーは、すべての回答を不正解として採点し、サーバーは、パッケージがないことを1回だけログに出します。パッケージがなくても、inchikey のキーは、アノテーターのブラウザで RDKit.js が計算した InChIKey を持つ回答には一致します。パッケージがインストールされている場合、サーバーは、保存された molfile から InChIKey を再計算し、ブラウザの値は無視します。

構造を描く、修正する

結合を追加するには、アノテーターは、左側のツールバーで結合の種類、環、元素、電荷を選んでから、キャンバスの空いた部分をクリックするか、原子からドラッグします。原子を指して元素記号(o、n、s)を入力すると、元素が変わります。テキストボックスは SMILES 文字列または molfile を受け付け、Load(SMILES 文字列の場合は Enter)が描画を置き換えます。OpenChemLib が読めないテキストは拒否され、描画は保持されます。Clear はキャンバスを空にし、Restore the given structure は、事前入力された構造を戻します。

エディターの下の SMILES、分子式、InChIKey は、アノテーターが描いている間に更新されます。価数の問題は、「C (atom 2) has 5 bonds; it allows 4.」という形でそこに一覧されます。require_valid がある場合、問題が一覧されている間は、必須の項目を送信できません。

原子を選択する

アノテーターは、描画内の原子をクリックしてその原子を選択し、もう一度クリックして選択を解除します。原子リストでは、Tab でリストへ移動し、矢印キー、Home、End で原子間を移動し、Space または Enter で原子を切り替えます。labels がある場合、アノテーターは最初にラベルを選び、一覧された各原子には、ラベルメニューと Remove ボタンが付きます。ページは、原子を元素と molfile 番号で表示します。たとえば O4 は、元のデータの4番目の原子です。

保存される原子インデックスは、元のデータに書かれたとおりでの原子の0始まりの位置です。原子は、SMILES 文字列、または molfile の原子ブロックに現れる順に数えられます。molfile の原子番号は、インデックスに1を足した値です。どちらのエンジンが分子を描画しても、インデックスは、同じ入力を removeHs=False で読み込んだときの RDKit の原子インデックスと等しくなります。OpenChemLib は、SMILES 文字列内の明示的な水素([H]C(Cl)(Br)F)を、隣接する原子に畳み込みます。そのため、engine: openchemlib では、そのような SMILES 文字列を選択に使えません。ディスプレイは、その SMILES 文字列が選択に使えないことを示し、原子をまったく提供しません。個々の水素を選択可能にする必要があるときは、molfile か engine: rdkit を使ってください。暗黙の水素(CCO)は、決して選択できません。

保存される回答

chemical_structure スキームは、項目ごとに1つの JSON オブジェクトを保存します。

json
{"molfile": "\nOCL MolfileCreator  2D\n\n  0  0  0 ... V3000\n...M  END\n",
 "smiles": "CC(Oc1ccccc1C(O)=O)=O",
 "idcode": "dklB@@QmR[fUxUZBBF@@",
 "formula": "C9H8O4",
 "rdkit_smiles": "CC(=O)Oc1ccccc1C(=O)O",
 "inchi": "InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12)",
 "inchikey": "BSYNRYMUTXBXSQ-UHFFFAOYSA-N",
 "changed": true,
 "problems": [],
 "toolkits": {"openchemlib": "9.25.0", "rdkit": "2026.03.6"}}

molfile は V3000 です。smiles は OpenChemLib の正規の立体付き SMILES で、idcode は OpenChemLib の正規 ID コードです。rdkit_smiles、inchi、inchikey は、RDKit.js が実行されたときにだけ存在します。正規 SMILES はツールキットによって異なります。構造は、InChIKey か ID コードで比較し、smiles と rdkit_smiles を比べてはいけません。描画が、事前入力された構造の ID コードのままである間、changed は false です。

molecule ディスプレイ上の viewer_selection スキームは、選択された原子を保存します。

json
{"viewer": "molecule", "items": [{"atom": 12, "element": "N", "label": "wrong element"}]}

エクスポート

chemistry エクスポートは、chemical_structure スキームと、molecule ディスプレイに結び付いた viewer_selection スキームを対象とします。

bash
python -m potato.export --config config.yaml --format chemistry --output out/
ファイル内容
chemical_structures.csv回答ごとに1行で、user_id, instance_id, schema, smiles, rdkit_smiles, inchi, inchikey, idcode, formula, changed, problems, identifiers_from, answer_correct, answer_reason を持ちます
chemical_structures.sdf各回答の molfile。その行の空でないフィールドが SD プロパティとして付きます
molecule_atoms.csv選択された原子ごとに1行で、user_id, instance_id, schema, viewer_field, atom, number, element, label を持ちます。number は molfile 番号です

identifiers_from は、RDKit.js が InChI と InChIKey を計算した場合は browser、エクスポートが rdkit Python パッケージで両方の識別子を計算した場合は server、RDKit.js もパッケージも使えなかった場合は none です。エクスポートは、識別子のないまま残った回答について警告を一覧します。answer_correct は、answer_field を持つスキームでは 1 または 0 で、answer_reason は、「a different structure (ignoring stereo)」や「missing the substructure C(=O)[OH]」のように、採点器の理由を示します。RDKit の SDMolSupplier などの SD リーダーは、フィールドをプロパティとして、SD ファイルを開けます。

Potato の一致度指標は、chemical_structure スキームと viewer_selection スキームを対象にしません。最初の設定の verdict という radio の質問は、通常どおり一致度が出ます。アノテーターの構造を比較するには、chemical_structures.csv の行を instance_id と schema で対応づけ、inchikey を比較します。RDKit が使えなかった場合は idcode を比較します。原子の選択については、molecule_atoms.csv から、項目ごとの集合の重なりを計算します(たとえば、atom インデックスに対する Jaccard)。

特性モデルを学習する

v2.14.0の新機能

分子についての radio、select、likert、slider の質問は、rdkit-property モデルを学習できます。モデルの入力は、項目のテキストではなく構造です。構造は、項目の molecule ディスプレイフィールドか、同じ項目の chemical_structure スキームから取られます。スキームからの場合、モデルはアノテーターが描いた構造を使い、アノテーターの間で食い違うときは最も多い描画を、誰も描かなかったときは prefill_field の構造を使います。各分子は Morgan フィンガープリントと RDKit の 2D 記述子になり、ロジスティック回帰、リッジ回帰、ランダムフォレストのいずれかが回答を学習します。スライダーの予測は、スライダーのステップに丸められます。model_training.writeback.schemas がその質問を指定していれば、モデルの回答は事前ラベルとして質問に戻ります。学習には chemistry エクストラが必要です。実行の手順は科学分野のモデルで説明しています。

例

bash
python potato/flask_server.py start examples/advanced/chemical-structure-correction/config.yaml -p 8000

この例には、一般的な薬剤の構造画像が5枚と、各画像に対する認識された SMILES 文字列があります。認識された文字列のうち4つは、誤ったヘテロ原子や反転した立体中心など、典型的な認識の誤りを含むように、手作業で編集されています。同じディレクトリの generate_images.py が、正しい SMILES から画像を描画します。

コーディングエージェントで調査を設定する

potato-skill 1.2.0 は Potato 2.13.1 を対象としており、chemical_structure と molecule を名前で認識します。このスキルは、アノテーターに何を尋ねるか、各構造を何人のアノテーターが確認するか、トレーニングと注意チェック、エクスポートなど、調査設計の側面を支援します。このスキルは、構造データの形式、正解キーのモード、ディスプレイのオプション、chemistry エクストラを、まだ文書化していません。potato-skill をインストールし、エージェントに調査を説明して、形式とオプションについては、このページの Markdown 版をエージェントに参照させてください。Markdown 版は https://www.potatoannotator.com/docs/annotation-types/chemical-structures.md にあります。

関連資料

実装の詳細については、ソースドキュメントを参照してください。