ROSETTA Candidate Search (RCS)

スコアリングの計算方法・アルゴリズム・パラメータ解説

脳領域名のテキスト入力から HOMBA 候補を返す RCS の「スコア」がどのように算出されるかを、 前処理から最終ランキングまで順を追って説明します。 実装は rcs/rosetta_candidate_generator.py(v0.3.1)に基づきます。

バージョン: v0.3.1 最終更新: 2026-06-25 参照: docs/rcs_algorithm.md スコア範囲: 0.0 〜 1.0

1. 全体像

RCS は単一の入力テキスト(例: Subthalamic nucleusLeft basolateral amygdala (BLA)LC)を HOMBA オントロジーのエントリと照合し、適合度スコア付きで候補リストを返します。

スコアは 0〜1 の浮動小数点数です。出力時は min(score, 1.0)小数第 6 位で四捨五入します。

1
前処理 正規化 → クエリバリアント生成 → 修飾語抽出
2
候補収集 exact / fuzzy / bm25 の 3 手法で HOMBA エントリを検索(手法ごとに最大スコアを保持)
3
第 1 パス 全候補に _score_candidate() を適用 → 暫定スコア prelim_scores
4
階層親昇格 兄弟候補が 2 件以上なら親エントリを追加(hierarchy_parent
5
第 2 パス 昇格後の全候補を再度スコア化 → 最終ランキング
重要: 最終スコアは各手法スコアの単純加算ではありません。 複数の合成式の 最大値 を取り、修飾語調整・過細減点を適用したうえで確定します。

2. 具体例(詳細 walkthrough)

alias 展開 · abbrev 展開 · laterality 除去 · 修飾語が同時に働く複合例は、 クエリバリアント全件・バリアント別 fuzzy ヒット・候補リストなどの詳細データを 別ページに分離しています。

→ Left basolateral amygdala (BLA) — 処理フロー詳細

この例で確認できること:

  • クエリバリアント 15 件(base 4 → alias 12 → abbrev 18 → 重複除去)
  • homba_alias_rules.csv による amygdalaamygdaloid complex 等の展開
  • homba_abbrev_rules.csv による BLAbasolateral nuclear group of amygdala
  • 15 バリアント × 3 手法 → 53 候補統合 → top 5 返却
  • 修飾語 bla と abbrev 展開の判定差(modifier ×0.88)

修飾語の加点・減点だけを短く追う例は §16 例 D、前処理の一般説明は §3 を参照してください。

3. 前処理とクエリバリアント

3-1. テキスト正規化(normalize_text

  1. Unicode NFKC 正規化
  2. &and/ → スペース
  3. アポストロフィ(' ` ´)を除去
  4. ハイフン・アンダースコアをスペースに変換
  5. 英数字以外を除去、連続スペースを 1 つに縮小
  6. 小文字化

例: Broca's areabroca s area(所有格のアポストロフィは削除)

3-2. クエリバリアント(query_variants

1 つの入力から複数の検索用文字列を生成し、表記ゆれに対応します。生成は次の順序で行われ、最後に unique_preserve_order() で正規化キーの重複を除去します。

  1. ベース 4 形(入力 · laterality 除去 · 括弧除去 · excluding 除去)
  2. 各ベース形に homba_alias_rules.csv を適用(単語置換・同義語追加)
  3. 各形に homba_abbrev_rules.csv を適用(略語展開 · クエリ側のみ)
  4. 正規化キーで重複除去 → 最終バリアントリスト

各段階のすべての中間文字列(重複含む)の実例は BLA walkthrough §2-2 を参照してください。

3-3. 修飾語抽出(extract_modifier_terms

括弧内トークンと、homba_token_rules.csvkind=modifier と定義された語を収集します。 これらは後述の修飾語一致率の計算に使われます。

3-4. トークン化(tokenize

正規化後に空白分割し、kind=stopword の語を除去します(oftheregion 等)。 トークン Jaccard や BM25、fuzzy のインデックス絞り込みに使用します。

4. 三つの検索手法

同一 HOMBA エントリが複数手法でヒットした場合、エントリは 1 つに統合され、 各手法のスコアは 最大値 が保持されます(_add_candidate)。

手法 methods 値 概要 候補化条件 手法スコア
完全一致 exact 正規化後のクエリバリアントと HOMBA 別名が完全一致 常に候補 1.0
あいまい一致 fuzzy 文字列類似度(後述) スコア ≥ 0.45 0.45 〜 1.0
BM25 トークン検索 bm25 トークン共起ベース(語順に頑健) 正規化 BM25 > 0 0 〜 1(正規化後)
階層親昇格 hierarchy_parent 兄弟候補から親を推論 子候補 2 件以上 後述(最大 0.97)

generate() のデフォルト引数: top_k=10(返却件数)、per_method_k=40(各手法の上位候補数上限)。

5. 倒排インデックスと候補絞り込み

全別名(約 9,000 件)に毎回フルスキャンしないため、起動時に以下のインデックスを構築します。

インデックス用途
alias_map正規化別名 → HOMBA エントリ index(exact 用)
alias_entries別名ごとのトークン列・頻度(fuzzy / BM25 用)
token_to_entriesトークン → 別名 entry index の posting list
_bm25_doc_freq各トークンの document frequency(df)

Fuzzy: INTERSECTION(v0.3.1 以降)

  1. クエリバリアントをトークン化
  2. トークンが 3 個以上のとき、df が低い(レアな)2 トークンrarest_limit=2)を選択
  3. 選ばれたトークンの posting list の 積集合(INTERSECTION) だけを fuzzy 比較対象にする
  4. 積集合が空 かつ 正規化バリアント長 > 5 のときのみ全別名スキャンにフォールバック

例: Subthalamic nucleus では subthalamic(df≈3)と nucleus(df≈1,400)の積集合 ≈ 3 件のみ比較。 旧 UNION 方式だと ≈ 1,400 件になっていた。

BM25: UNION

  1. クエリバリアントの 全トークン の posting list を取得
  2. posting list の 和集合(UNION) を BM25 比較対象(recall 重視)
手法トークン選択集合演算フォールバック
fuzzy レアな 2 トークン(3 トークン以上のとき) INTERSECTION 積集合が空 かつ 長さ > 5 → 全スキャン
bm25 全トークン UNION なし
exact alias_map 直接参照

6. Fuzzy スコア(string_similarity

正規化後の 2 文字列を比較します。完全一致なら 1.0

similarity = max(
  containment_bonus,
  0.35 × SequenceMatcher.ratio
+ 0.25 × bigram Dice
+ 0.20 × trigram Dice
+ 0.20 × token Jaccard
)

各成分の定義

成分計算方法重み
SequenceMatcher.ratio Python difflib.SequenceMatcher の文字列類似比 0.35
bigram Dice 空白除去後の 2-gram 集合について 2|A∩B| / (|A|+|B|) 0.25
trigram Dice 同上(3-gram) 0.20
token Jaccard stopword 除去後トークン集合の |A∩B| / |A∪B| 0.20
containment_bonus 一方が他方の部分文字列(長さ ≥ 4)のとき
min(0.92, len(短)/len(長) + 0.25)
max の別項
containment_bonus は加重平均とは別に max() の候補として比較されます。 部分一致(例: 短いクエリが長い別名に含まれる)を拾うためのボーナスです。

7. BM25 スコア

エントリ内の複数別名それぞれに BM25 を計算し、最も高い別名のスコアをそのエントリの bm25 スコアとします。

パラメータ

k11.5 — 語頻度の飽和度
b0.75 — 文書長正規化の強さ
正規化定数4.0 — 生スコアを 0〜1 に圧縮
IDF(t) = log(1 + (N - df(t) + 0.5) / (df(t) + 0.5))

BM25_raw = Σ_t  IDF(t) × (freq × (k1+1)) / (freq + k1 × (1 - b + b × doc_len / avg_len)) × query_count(t)

BM25_norm = BM25_raw / (BM25_raw + 4.0)

8. 最終スコアの合成(_score_candidate

各候補について、クエリ全体と HOMBA 全別名の token Jaccard 最大値を token_score とします。

Step A — ベーススコア(base)

base = max(
  hierarchy_parent_score,   // 未設定なら 0
  exact_score,              // 未設定なら 0
  0.5 × fuzzy + 0.3 × bm25 + 0.2 × token_score,
  0.75 × fuzzy + 0.25 × token_score
)
2 つの線形合成式(50/30/20 と 75/25)は、fuzzy 偏重と BM25 併用の両方のケースをカバーするため、 単純平均ではなく max で選ぶ 設計です。

Step B — 修飾語調整

Step C — 過細減点の順で適用します(次セクション参照)。

9. 修飾語(modifier)調整

modifier_match_score = 抽出した修飾語のうち、候補別名テキスト(正規化・連結)に 含まれる割合(0〜1)。修飾語がない場合は 1.0(調整なし)。

条件調整式
修飾語なし 調整なし
修飾語あり & 一致率 > 0 base + 0.12 × modifier_match_score(上限 1.0)
修飾語あり & 一致率 = 0 base × 0.88(上限 0.88)

例: Internal capsule (posterior limb)

修飾語 posterior, limb が抽出される。 候補別名に posterior limb が含まれれば一致率が上がり加点。 含まれない候補は最大 0.88 に抑えられる。

10. Specificity Penalty(過細候補の減点)

クエリに存在しない細分類語(inferiorlateralcoreshellmolecular 等)が候補側の別名に含まれる場合、スコアを減点します。

適用条件(すべて満たすときのみ)

  1. exact マッチがない
  2. fuzzy または bm25 マッチがある
  3. クエリに修飾語(modifier_terms)がない
  4. 候補に親 ID(parent_id)がある

減点率

候補別名の specificity 語 \ クエリの specificity 語 が空でない
  → final_score × 0.86
それ以外
  → final_score × 1.0(減点なし)

specificity 語は homba_token_rules.csvkind=modifier または kind=weak に該当するトークンです。

減点対象外:
  • exact マッチで見つかった候補(クエリが別名を直接指している)
  • hierarchy_parent のみで昇格した候補(昇格スコアが既に子の penalized score を反映)

11. 階層親昇格(hierarchy_parent

複数の子領域(兄弟)が同時に候補になったとき、共通の親エントリを追加します。 例: thalamus の複数サブリージョンが候補 → pulvinar of thalamus の親を昇格。

昇格条件

昇格スコア

hierarchy_parent_score = min(max(子候補の prelim_scores) + 0.08, 0.97)

prelim_scores は第 1 パスで算出した過細減点込みの暫定スコアです。 生の fuzzy スコア(例: 0.89)だけで親を昇格させると、ペナルティ後 0.77 の子を 0.97 の親が上回る「過昇格」が起きるため、暫定スコアを参照します。

12. 2-pass スコアリング(v0.3 以降)

親昇格スコアと子の final score の間に循環依存があるため、2 段階で処理します。

1. exact / fuzzy / bm25 で候補収集(手法別最大スコアを保持)
2. 第1パス: 全候補に _score_candidate() → prelim_scores(specificity_penalty 込み)
3. prelim_scores で _promote_common_parents() → hierarchy_parent 候補を追加
4. 第2パス: 昇格後の全候補を再度 _score_candidate() で最終スコア化
5. ソート → top_k 件を返却
第 2 パスが必要な理由: 第 1 パス時点では存在しなかった hierarchy_parent 候補にも、 合成式・修飾語調整・penalty ルールを適用して最終順位を確定するため。 2-pass の計算コストは fuzzy 比較に比べ小さい。

13. ソートと出力

ソートキー(優先順)

  1. score 降順
  2. name 昇順(小文字化して比較)
  3. homba_id 昇順

出力フィールド(主要)

フィールド説明
score最終マッチスコア(0〜1、6 桁丸め)
methodsヒットした手法(+ 区切り。例: bm25+fuzzy
matched_query照合に使われたクエリバリアント
matched_alias一致した HOMBA 側別名
modifier_terms抽出修飾語(; 区切り)
modifier_match_score修飾語一致率(0〜1)
hierarchy_reason階層昇格理由(該当時のみ。例: common_parent_of_3_candidates
best_method_score単一手法の最高生スコア(内部デバッグ用に出力)

14. レビューフラグ(rcs/review.py

generate() の出力自体には含まれませんが、テスト・API 層では add_review_flags() により以下のフラグが付与されます。

フラグ条件
modifier_conflict 修飾語あり かつ modifier_match_score < 1.0
high_confidence score ≥ 0.90(上記より優先度低)
low_confidence score < 0.60
needs_review 上記以外(0.60 ≤ score < 0.90)

15. パラメータ一覧(クイックリファレンス)

パラメータ用途
fuzzy 閾値0.45これ未満は fuzzy 候補から除外
fuzzy rarest_limit2INTERSECTION 用に選ぶレアトークン数
fuzzy 全スキャン条件積集合空 & バリアント長 > 5フォールバック
string_similarity 重み0.35 / 0.25 / 0.20 / 0.20Sequence / bigram / trigram / token Jaccard
containment_bonus 上限0.92部分文字列ボーナス(長さ ≥ 4)
containment 加算項+ 0.25len(短)/len(長) に加算
BM25 k11.5語頻度飽和
BM25 b0.75文書長正規化
BM25 正規化raw / (raw + 4.0)0〜1 圧縮
合成式 10.5·fuzzy + 0.3·bm25 + 0.2·tokenbase の候補
合成式 20.75·fuzzy + 0.25·tokenbase の候補
exact 下限0.96exact ヒット時の base フロア
修飾語加点係数0.12× modifier_match_score
修飾語不一致倍率0.88一致率 0 のとき(上限 0.88)
specificity_penalty0.86過細語が余分にあるときの乗算
親昇格ボーナス+ 0.08max(子 prelim_scores) に加算
親昇格上限0.97hierarchy_parent_score のキャップ
per_method_k40(デフォルト)各手法の候補数上限
top_k10(デフォルト)返却候補数
スコア丸めround(..., 6)出力精度
high_confidence≥ 0.90レビューフラグ
low_confidence< 0.60レビューフラグ

16. 計算例(概念的ウォークスルー)

例 A: 完全一致 — クエリ Subthalamic nucleus

  1. 正規化 → そのまま subthalamic nucleus
  2. exact ヒット → exact_score = 1.0
  3. base = max(..., 1.0, ...) = 1.0 → exact 下限適用で 0.96 以上
  4. 修飾語なし → 調整なし。specificity_penalty も exact のため適用されない
  5. 最終スコア ≈ 1.0、methods = exact

例 B: Fuzzy のみ — クエリ subthalamic nuc(略称・typo 想定)

  1. exact なし。fuzzy で string_similarity ≈ 0.82 と仮定
  2. bm25 も正規化スコア ≈ 0.55 と仮定
  3. token_score ≈ 0.67(nucleus vs nuc で Jaccard 低下)
  4. base = max(0.5×0.82 + 0.3×0.55 + 0.2×0.67, 0.75×0.82 + 0.25×0.67) = max(0.693, 0.782) = 0.782
  5. 修飾語なし、過細語も余分にない → penalty ×1.0
  6. 最終スコア ≈ 0.782、review_flag = needs_review

例 C: 過細減点 — クエリ thalamus、候補 inferior thalamus

  1. fuzzy ≈ 0.88、bm25 ≈ 0.40、token_score ≈ 0.50 と仮定
  2. base ≈ max(0.5×0.88+0.3×0.40+0.2×0.50, 0.75×0.88+0.25×0.50) = max(0.692, 0.785) = 0.785
  3. 候補に inferior(weak 語)がありクエリにない → × 0.86
  4. 最終スコア ≈ 0.785 × 0.86 ≈ 0.675
  5. 兄弟候補が 2 件以上なら親が min(0.675 + 0.08, 0.97) = 0.755 で昇格候補に

例 D: 修飾語 + alias + abbrev — クエリ Left basolateral amygdala (BLA)

15 バリアント・alias/abbrev 展開・修飾語 bla の詳細フローは BLA walkthrough 詳細 を参照。

  1. 括弧内 BLA → abbrev 展開で basolateral nuclear group of amygdala が多数のバリアントに追加
  2. amygdalaamygdaloid complex 等、alias_rules も複数バリアントを生成
  3. 修飾語 bla は別名文字列に含まれないため modifier_match_score = 0 → 全候補 ×0.88
  4. 1 位: HOMBA:10366 basolateral nuclear group of amygdala(score ≈ 0.73)