モデルを「壊さずに」ドメインを広げる ― XLM-RoBERTa 継続学習の設計ノート

モデルを「壊さずに」ドメインを広げる ― XLM-RoBERTa 継続学習の設計ノート

こんにちは、Qualiteg研究部です。

今日は「すでに完成している強いモデルを、壊さずに広げる」という、地味だけど実務でとても大事なテーマを取り上げたいと思います。

機械学習に取り組んでいると、

「一度しっかり仕上げたモデルを、新しい用途やデータに合わせてもう少し広げたい」

そんな場面はよく出てきます。
今回ご紹介するNER(固有表現抽出)のシーンに限らず、いろいろなタスクで共通する悩みではないでしょうか。
ところが、ここで素朴に追加学習をかけると、せっかくの強みがあっさり崩れてしまう。

私たちは、PII(個人特定情報や要配慮情報)を検出・マスキングするエンジン(PII-FI)を構築する際、実際にそれを経験しました。

Precision(適合率)が 0.83 から 0.17 まで転げ落ちる、なんてことも本当に起きるんです。

PII検出では、ドメイン(分野)ごとに検出したいPII型の種類や求められる精度が異なる場合があります。そこで1つのエンジンといっても、対応ドメインを広げていくたびに(そのドメインに適応させるための)追加学習が求められることがあります。

本稿は、そうした「追加学習でドメインを広げる」取り組みのなかで私たちがぶつかった壁と、そこから得た原理や判断を、ありのままに共有するものです。

今回は、XLM-RoBERTa をエンコーダに使った高速日本語 NER モデルを土台に、「手法の性質がこうだから、だから我々はこう設計した」という因果の形で、丁寧に書き残していきます。

先に結論だけ言っておくと、勘どころは、精度を上げる小技ではなく、「何を動かさないか」を設計することにありました。

同じような場面に立つ方の参考になればと思います。


0. 記法とモデルの土台

NER をトークン分類として定式化します。入力系列 \(x=(x_1,\dots,x_T)\) に対し、エンコーダ \(f_\theta\) が各トークンの文脈表現 \(h_t = f_\theta(x)_t \in \mathbb{R}^d\) を出し、線形の分類ヘッド \(W \in \mathbb{R}^{C\times d}\)(\(C\) はラベル数、\(b\in\mathbb{R}^C\) はバイアス)が

$$ p(y_t = c \mid x) = \mathrm{softmax}(W h_t + b)_c = \frac{\exp(w_c^\top h_t + b_c)}{\sum_{c'} \exp(w_{c'}^\top h_t + b_{c'})} $$

を返します。学習は交差エントロピー \(\mathcal{L}=-\sum_t \log p(y_t^\star\mid x)\) の最小化です。

エンコーダ \(f_\theta\) には XLM-RoBERTa(多言語 RoBERTa) を用います。

手元には、この \(f_\theta\) の上で十分に鍛えた

強い日本語NER \(M_0=(\theta_0, W_0)\)

がすでにあるとします。目標は、\(M_0\) の強み(特に「実文で過剰に拾わない」校正)を保ったまま、新ドメイン・新ラベルへ対応を広げることです。

ここで、追加学習の自由度は \(\theta\)(エンコーダ)と \(W\)(ヘッド)のどこをどれだけ動かすかに集約されます。

本稿は一貫して「この自由度をどう絞るか」の話です。


1. まず XLM-RoBERTa の性質を押さえる(設計はここから決まる)

設計判断は、すべて XLM-RoBERTa の以下の性質から導かれます。

(a) 多言語・大規模事前学習
XLM-RoBERTa は約100言語・大規模コーパスで Masked LM 事前学習された encoder-only モデルです。一般に、下位層ほど言語横断の汎用的な特徴(語形・文法)上位層ほどタスク寄りの特徴を持つ、という階層性が経験的に知られています。
→ だから我々は、新能力を足すときも下位層は極力動かさない方針を採りました(後述の凍結・LoRA・低学習率)。これは「層別の表現に関する一般的な知見」と「私たちの層別凍結の実験結果」にもとづく設計仮説で、校正が必ず下位層に宿ると断定しているわけではありません。

(b) SentencePiece サブワード・トークナイザ(日本語に空白境界が無い)
1単語が複数サブワードに割れ、エンティティ境界がサブワードの途中に来ることもあります。
→ そこで我々は、文字オフセット(offset mapping)で文字スパンとサブワードの対応を取り、ラベルはベースのタグ方式に合わせて付与しました。ただし offset mapping は対応を取得できるだけで、1サブワードの内部に境界がまたがる場合まで解決してくれるわけではありません。そうしたトークンは「少しでも重なればラベル付け」「完全に含まれる場合だけラベル付け」「ignore_index にする」などの方針を決めて扱います(ベースが単タグ式=B/I 接頭辞なしなら、こちらも単タグ。B/I を勝手に導入するとラベル空間が割れて学習が濁ります)。

(c) 多言語共有語彙
ローマ字・全角半角・記号混じり・英数字の造語も、サブワードへ分割して扱えます。
→ ゆえに、英単語の顔をした造語のような「辞書にも正規表現にも載らない固有名」も、文脈表現として扱える余地があります(ただし「分割できる」ことと「表記揺れに頑健」であることは別で、ここは仮説として捉えています)。これは後述の「新しいエンティティ型を学習で取りに行く」勝ち筋の前提になります。

(d) encoder-only + 線形ヘッド
タスクの知識の多くはエンコーダ \(f_\theta\) 側にあると私たちは仮定しており、ヘッド \(W\) は薄い線形写像にすぎません。しかも \(f_\theta\) は全ドメイン・全ラベルで共有されます。
→ ゆえに後述の「単一エンコーダでは多ドメインを完全には両立しにくい」というトレードオフが生まれます。\(f_\theta\) を動かすと、その上に乗る全部の判定が動いてしまうからです。


2. 測れていないものは鍛えられない(評価の自己点検)

最初の作業は新しい学習ではなく、評価指標の点検でした。

指標がわずかにずれると、その後の全最適化が誤った方向へ進みます。
(本稿の Precision/Recall は、重なり(overlap)+型一致micro 平均で算出し、placeholder(事前匿名化済みの印)は除外、後処理なしのモデル単体を同一の独立評価セットで比較しています。完全一致(exact match)ではありません。)

今回、私たちの評価コードで実際に見つかった主な問題は2種類でした。

  • 「検出すべきでない印」を Recall の分母に入れる
    たとえば対象テキストがあらかじめ伏字・記号化されている箇所まで「正解」に数えると、Recall の定義
    $$ \text{Recall} = \frac{|\text{検出できた正解}|}{|\text{正解集合}|} $$
    の分母が膨らみ、実態とずれます。
    製品が本当に守りたいのは「漏れて困る実体」なので、事前匿名化済みの印は分母から外すべきです。
  • ラベル対応表の漏れ
    モデルが出すラベル名と評価側の期待名の変換が一部抜けると、特定型だけ取りこぼしが過大計上されます。

私たちはこの2点を直しただけで指標が大きく変わり、「弱点は Recall ではなく Precision(過剰検出)だった」とミッションの理解が反転しました。

教訓:measure-first。「数字が悪い」より先に「数字の定義は正しいか」を疑う。指標のバグはあとから大きく手戻る原因になります

3. 素朴な継続学習では「忘却」と「学習分布の偏り」が同時に起こる

新ドメインのデータ(多くは合成)だけで素朴に追加学習すると、\(\theta_0 \to \theta\) が動いて旧ドメインの性能が落ちることがあります。これは、以前学習した能力を新しい学習で失う破滅的忘却として現れる場合があります。ただし、今回観測した Precision 低下はそれだけでは説明できません(次段)。

NER で特に起きるのが「地の文で黙るを忘れる」ことです。

テンプレートで作った合成文は、ほぼ全文に固有名が入ります。すると、トークンが何らかのエンティティである事前確率(基底率)

$$ \pi = \Pr(y_t \neq O) $$

が、学習データ上で実文よりずっと高くなります。モデルはこの偏った \(\pi\) を内面化し、実文でも何でも拾うようになります。

私たちの観測では Precision が \(P\approx0.83\to0.17\) まで落ちました(Recall は高いまま)。これは破滅的忘却というより、合成データと実文の分布差(クラス事前分布のずれ・負例不足)にモデルが適応してしまった結果です。今回の問題には、旧能力の忘却と学習分布の偏りの両方が関与していました。

XLM-RoBERTa の性質 (a) を踏まえると、層別凍結の実験の範囲では下位層の表現は比較的保たれており、変化したのは主に上位層やヘッドの校正だった可能性が高いと考えています(層別プローブで厳密に同定したわけではありません)。
→ ゆえに我々は、

(1) 合成データの密度(density)を実文に寄せる(長い地の文に固有名を疎らに置き、\(O\) トークンを大半にする)、

(2) 難しい負例(一般名詞・定型句だけの文、紛らわしい一般語)を入れる、という対策で \(\pi\) の偏りを是正しました。

精度は戻りましたが、素朴な続き学習の枠内ではある天井で頭打ちになりました。次節以降がその突破です。


4. 分類ヘッドを作り直さない(デコイクラスという受け皿)

最も効いた一手が、\(M_0\) のラベル体系をそのまま引き継ぐことでした。

これは softmax の確率の振る舞いと、学習信号の両面から説明できます。

自分が欲しい型が \(k\) 個(たとえば人物・組織・地名)だとして、\(W_0\in\mathbb{R}^{C\times d}\)(\(C>k\))を捨てて \(W_{\text{new}}\in\mathbb{R}^{k\times d}\) を新規初期化したとします。すると、\(M_0\) が別クラスとして持っていた「製品名」「施設」「事象」等の非対象エンティティの出力スロットが消えます

softmax は確率の総和を1に保つので、消したクラスに乗っていた確率質量は残ったクラスへ再配分されます

$$ \text{クラス } C{+}1 \text{ を除去} \;\Rightarrow\; \sum_{c=1}^{C} p(c\mid x)=1 \text{ に正規化} \;\Rightarrow\; \arg\max_c \text{ が対象型へ移りうる} $$

XLM-RoBERTa のエンコーダは「この語は法律名だ/施設名だ」という特徴を依然として表現しているのに、それを受け止める出力スロットが無い。

既存ロジットの一部だけを除去したと考えれば、非対象クラスという競合先を失うことで対象クラスが選ばれやすくなる、と理解できます。ただし実際にはヘッドを新規初期化するとロジットそのものが変わるため、「以前そのクラスに乗っていた質量が漏れた」と厳密に追跡できるわけではありません。再学習では、これに加えて既存の判別境界の消失・非対象の教師信号の欠落・新データのクラス事前分布への適応も効いて、過剰検出につながります。

→ ゆえに、\(W_0\) をそのまま読み込み(ヘッドの形を一致させてロードし、ignore_mismatched_sizes=False 相当)、対象型のラベルにだけ教師信号を与える設計にしました

$$ \mathcal{L} = -\!\!\sum_{t:\, y_t^\star \in \{\text{対象型}\}\cup\{O\}}\!\!\! \log p(y_t^\star \mid x) $$

非対象の“デコイ”クラスの行を warm start として残すことで、ヘッドを新規初期化する場合より「これは組織ではなく別カテゴリ」という \(M_0\) の判断が失われにくくなります(厳密に「温存」できるかは別問題で、新データで非対象を一律 \(O\) と教えるとデコイ側にも負の勾配が入ります。旧クラスを教師モデルで擬似ラベル化する・未注釈部分を ignore_index にする・replay を併用する、などで受け皿を守るのが理想です)。この変更だけで頭打ちが目に見えて改善しました。

教訓:非対象クラスは“受け皿”として価値がある。型を絞ってヘッドを作り直すと受け皿と既存の判別境界を失い、過剰検出が増えます(softmax の再配分はその一因)。

5. 自己蒸留で drift を抑える(ベースから離れにくくするアンカー)

デコイ温存でも \(M_0\) の精度には届きません。軽い追加学習でも \(\theta\) は少しずつ drift します。

ここで効くのが自己蒸留です。

考え方はこうです。

教師ラベルを人手や辞書ではなく、\(M_0\) 自身に実文を推論させて作り、生徒(学習中のモデル)を教師分布へ近づけます

$$ \mathcal{L}_{\text{distill}} = \sum_t \mathrm{KL}\!\big(p_{M_0}(\cdot\mid x)\,\big\|\,p_{\theta}(\cdot\mid x)\big) $$

(本稿の自己蒸留は \(M_0\) と同じラベル空間(教師=生徒)で行います。新型の追加は §6 の別段で、soft な KL ではなくハードラベルの交差エントロピー(新型にラベル id が付くだけ)で行うため、次元の異なる教師・生徒で KL をそのまま計算する場面はありません。なお、ハードラベル化した \(M_0\) 出力での交差エントロピーは soft な KL 蒸留とは別物で、非最大クラスの確率情報(dark knowledge)は失われます。)

この目的関数は、理想的にはアンカーデータ上で \(p_\theta = p_{M_0}\) を最小点に持ちます。ただし実際には有限のアンカーデータで学習し、新型ラベルの損失も併用し、最適化誤差もあるため、未見データまで含めて \(M_0\) と完全に一致する保証はありません。それでも実用上は、追加学習による drift を強く抑える正則化として効きます。私たちはこれで、(蒸留に使ったアンカーとは別の)旧ドメインの独立評価セットでも \(M_0\) に近い性能を再現できました。「追加学習=即劣化」を避けるための有効な一手です。

ただし注意も要ります。教師が \(M_0\) 自身である以上、アンカーデータ上では \(M_0\) のクセ(過剰検出など)も一緒に引き継ぎやすくなります。

とはいえ、これは「生徒が教師を超えられない」という意味ではありません。同一アーキテクチャの自己蒸留でも生徒が教師を上回る報告はあり、外から新しい情報を足せば改善の余地は残ります。

したがって、自己蒸留はあくまで既存の挙動をベースに寄せて保つための手段です。\(M_0\) が持っていない型のような能力を足すには、\(M_0\) の外から情報を入れる必要があります。次節がそれです。

6. 既存ドメインは超えられなかった――勝てたのは「出力空間に無い新型」だけ(ヘッド手術)

理論上は、既存ラベルでも、より質の高いデータ・誤ラベル修正・ドメイン適応・損失設計の改善などで \(M_0\) を上回る余地はあります(自己蒸留で生徒が教師を超える報告もあります)。
ですが我々のデータと手法では、すでに強い既存ドメインの精度を一度も超えられませんでした。§7 で触れる5系統をすべて試し、本番パイプラインで追認しても、どの変種もベース以下。これは正直な負の結果です。
一方で、はっきり勝てた軸が一つだけありました。\(M_0\) の出力ラベル空間に存在しない新しい型です。既存モデルはその型を出力する手段自体を持たないので、ここは学習で取りに行ける明確な勝ち筋になります。

その典型が、辞書にも正規表現にも載らない新しいエンティティ型です。

たとえば英数字の造語のような固有名は、私たちが用意していた辞書・正規表現ルールでは拾えず、\(M_0\) にそのラベルが無ければ Recall は構造的に \(0\) です。しかし §1(c) のとおり、XLM-RoBERTa の多言語サブワード表現はそうしたトークンを文脈として扱える余地があり、出力スロットさえ与えれば学習で取りに行けます。

ここは学習ベースの NER が特に力を発揮できる領域です。

足し方はヘッド手術。\(M_0\) の分類層 \(W_0\in\mathbb{R}^{C\times d}\) を温存したまま、新型のための行を1つ増設します

$$ W' = \begin{bmatrix} W_0 \\ w_{\text{new}}^\top \end{bmatrix} \in \mathbb{R}^{(C+1)\times d}, \qquad w_{\text{new}} \sim \mathcal{N}(0,\sigma^2 I),\ \ \sigma\!=\!0.02 $$

既存 \(C\) 行はそのまま再利用し(既存ロジットを引き継ぐ warm start)、新型のための1行を追加します。新行は \(w_{\text{new}}\sim\mathcal{N}(0,\sigma^2 I)\) で初期化しますが、新ロジット \(z_{\text{new}} = w_{\text{new}}^\top h + b\) の分散は \(\sigma^2\|h\|^2\) なので、\(\sigma\) を小さくしても \(\|h\|\) が大きければロジットは 0 付近とは限りません(\(d=768\) なら標準偏差は概算で \(0.02\sqrt{768}\approx 0.55\))。つまり小さい \(\sigma\) は期待値を 0 にするだけで、新クラスが暴発しない保証にはなりません。初期暴発を確実に抑えたいなら、新行の bias を十分な負側に置き、許容する新クラス FP 率に合わせてアンカーデータで校正するのが確実です。なお、ここで「1行追加」で済むのは単タグ方式の場合で、BIO 方式なら B-/I- の複数行が必要になります。

old = model.classifier                  # Linear(d, C)
new = nn.Linear(d, C + 1)
with torch.no_grad():
    new.weight.normal_(std=0.02); new.bias.zero_()
    new.weight[:C] = old.weight         # 既存行はコピー(warm start)
    new.bias[:C]   = old.bias
model.classifier = new                  # 増設した1行を中心に学習

※ 上のコードは classifier 全体を差し替えているため、このままだと既存 \(C\) 行も新しい1行もすべて学習対象になります。新行だけを更新したい場合は、旧行の勾配をマスクするか、旧行と新行を別 Parameter として実装する必要があります。PEFT の modules_to_save=["classifier"] も「保存するだけ」ではなく classifier 全体を学習可能にする設定です。

私たちはこの方法で、\(M_0\) が \(0\) だった新型の Recall を
実用水準まで回収しました。

ベースには絶対に出せない数字で、ここで初めて本当に“広げた”と言えました。


7. LoRA:原理から「我々の設計」を導く

ただ、新能力を足すと、§1(d) のとおり共有エンコーダ \(f_\theta\) が動き、既存ドメインの精度がわずかに下がる問題に、私たちの実験では突き当たりました。

これを最小化する定番がおなじみの LoRA(Low-Rank Adaptation) です。

原理から我々の判断を順に導いてみましょう

7-1. 原理

LoRA は、微調整に必要な重み更新 \(\Delta W\) を低ランク行列で十分に近似できるという仮説に基づきます(「更新が本質的に低ランクである」と証明されたわけではなく、低い intrinsic rank を持つという仮説です)。

ならば \(\Delta W\) をフルに学習せず、低ランク分解で表します、

$$ W = W_0 + \Delta W,\qquad \Delta W = \frac{\alpha}{r} BA,\quad B\in\mathbb{R}^{d\times r},\ A\in\mathbb{R}^{r\times k},\ r\ll \min(d,k) $$

学習するのは \(A,B\) だけで、元の \(W_0\) は完全凍結。\(\alpha\) はスケーリング係数で、実効更新の大きさを \(\alpha/r\) で調整します。
(記号について:ここでの \(W_0\) は LoRA を挿入する一般の線形層を指し、§0 の分類ヘッド \(W\) とは別物です。)

で、ここから2つの性質が出てきます、

  • ベース重みを上書きしない
    \(W_0\) は凍結され、学習されるのは \(A,B\) だけです。更新行列 \(\Delta W = BA\) の rank が最大 \(r\) に制約されるため、フルファインチューニングより忘却を抑えやすい傾向はありますが、「忘却が必ず小さい」と保証されるわけではなく、タスクや学習量によってトレードオフは変わります。
  • ランクは可塑性と忘却のレバーになりやすい
    私たちの実験では \(r\) を上げるほど新型への適応は強まる一方、既存ドメインの drift も増える傾向が見られました(ただし \(\alpha\) のスケーリング・対象層・学習率・データ分布にも依存します)。

7-2. だから我々はこう考えた

私たちのゴールは

既存ドメインの挙動を \(M_0\) のまま保ちつつ、新エンティティ型だけ足す

ですのでLoRA の原理に照らして次のように設計しました。

  1. base を LoRA で凍結する
    既存ドメインの校正は \(W_0\) と \(f_{\theta_0}\) に宿るので、それを上書きしないこと自体が目的に直結します。

  2. 適応は attention の query / value に当てる
    今回の構成では target_modules=["query","value"] に限定しました。注意機構への適応が効くという経験則に基づく選択で、あらゆる NER で最適という主張ではありません。
  3. ランクは小さめ(\(r=16\), \(\alpha=32\))
    新型1つを足すのに大きな表現力は要らず、忘却を抑える方を優先。
    実際に \(r\) を上げると既存ドメインが drift しました。

  4. ヘッドは別扱い
    新型行は学習が必要なので、分類ヘッドは LoRA 凍結の例外として学習対象に残す(modules_to_save=["classifier"])。
    さらに踏み込んで、既存ラベル行だけ勾配マスクで凍結し、新型行のみ学習する版も試しました(なお勾配を止めても、weight decay が classifier にかかると既存行が動くことがあるため、classifier の weight decay は 0 にする等の配慮が要ります)。
  5. 学習率は LoRA 用に上げる
    標準的な LoRA では \(B\) をゼロ初期化し、追加直後の更新量はゼロ(no-op)から始まります。新設のアダプタと新型ヘッド行を短期間で適応させる必要があり、検証の結果、ヘッド温存・自己蒸留で使う極小 \(\text{lr}=5\times10^{-6}\) では足りず、\(\text{lr}=2\times10^{-4}\)・2エポックが有効でした。
    「凍結中心だから弱く当てる」ではない点が要注意。
  6. 学習後はマージする
    推論時は \(W=W_0+\frac{\alpha}{r}BA\) を畳み込み(merge_and_unload)、通常の重みとして保存
    消費側は差し替えるだけで、LoRA を意識しなくて済みます。

7-3. それでも残る「共有エンコーダの両立コスト」


ここが本質的な発見です。

たとえ \(W_0\) と既存ヘッド行を凍結しても、LoRA は共有エンコーダの内部表現を動かすため、既存ドメインのロジットは一般には元モデルと一致する保証がありません

$$ \underbrace{W_0\, f_{\theta_0+\Delta}(x)}_{\text{LoRA後の既存ヘッド出力}} \;\not\equiv\; \underbrace{W_0\, f_{\theta_0}(x)}_{M_0\ \text{の出力}} $$

\(f\) が全ドメイン共有である以上、新型のために \(\Delta\) を入れると、その \(\Delta\) は既存ドメインの \(h_t\) も動かしえます。私たちは「全混合学習」「下位層凍結」「LoRA」「LoRA+既存ヘッド凍結」「データ配合変更」と5系統を試しましたが、既存ドメインの精度はどれもほぼ同じ天井に張り付きました。

下位層を凍結しすぎると、今度は新型が学習できず Recall が \(0\) に崩壊する(可塑性を失う)副作用も確認しました。

結論:今回試した5系統の構成では、既存ドメインを完全に保ったまま新型を十分に獲得できる設定は得られませんでした。 共有エンコーダの表現更新が既存ドメインへの干渉要因になっている、というのが私たちの解釈です(replay・機能正則化・条件付きルーティングなどで両立できる余地は原理的には残ります)。完全に分離するなら、ドメイン別アダプタやモデル分割という別アーキテクチャの話になります(運用上1エンジンに統合したい、という制約と相談)。

8. リークは「ドメインの文章構造」で潰す

追加学習では、評価データが学習に混ざると数字が嘘っぱちになります。

ID で評価セットを除外するのは当然として、文書どうしが互いを長文で引用し合うドメイン(判例・論文など)では、それだけでは不十分です。
ID が違う学習文書の中に、評価文書の本文が数十字そのまま含まれていることがあるからです。

→ そこで我々は、評価文書全体を長さ \(k\) の窓(shingle)に刻み、学習側に部分一致で照合しました、

$$ \text{leak}(d_{\text{train}}) = \mathbb{1}\!\Big[\,\exists\, s \in \mathrm{Shingles}_k(\mathcal{E}):\ s \subseteq d_{\text{train}}\,\Big],\qquad k=50 $$

(\(\mathcal{E}\) は評価集合の全文。)
数点プローブの素朴照合では取りこぼす部分一致も、shingle 照合なら長い完全一致については高い精度で拾えます(ただし正規化の違い・OCR 誤り・言い換え・49字以下の重複などは取りこぼします)。
迷ったら除外側に倒していますが、定型句が長く一致すると過剰に除外して分布を歪める可能性もあるので、件数は確認しています。

教訓:ドメインの文章構造(引用・定型句)を理解してリーク設計をする。 ID一致チェックだけでは不十分です。

9. 最後は「コストの非対称性」で決める

精度指標が小数点で競るとき、最後の判断は数字の大小ではなく誤りコストの非対称性で行うべきです。

多くの実務タスクでは、見逃し(FN)と過検出(FP)のコストが大きく違います

$$ \text{cost} = \lambda_{\text{FN}}\cdot \mathrm{FN} + \lambda_{\text{FP}}\cdot \mathrm{FP},\qquad \lambda_{\text{FN}} \gg \lambda_{\text{FP}} $$

見逃しが致命的なタスクなら、Recall を保ったまま Precision がわずかに下がる(=過剰側へ倒れる)モデルは、別ドメインで取りこぼしを大きく回収できるなら採用に値します。

実際、私たちが採用に進めた版は、既存ドメインの Precision がごく僅か下がる代わりに、Recall は維持(評価セット上で見逃しを増やさない)しつつ、新型の取りこぼしを大きく回収するものでした。

ただし、この重み付け(精度の下限ゲートをどこに引くか)はプロダクトの価値判断であり、モデル側が勝手に決めるべきではありません。

現場の数字を添えてプロダクトマネージャなど意思決定者に渡すのが筋です。


10. 実践Tips(試して効いた/効かなかった細かいこと)

ここでは、原理の裏で地味に効いた(あるいは罠だった)実装レベルの知見をご紹介していきます

学習レシピ

  • 強いベースほど軽く当てる
    今回のモデル・データ配合では、ヘッド温存+自己蒸留なら \(\text{lr}=5\times10^{-6}\)・1エポックで十分でした。動かしすぎると即、過剰検出へ転びます。
  • LoRA は逆に強めに当てる
    標準 LoRA は \(B\) ゼロ初期化(初期 no-op)から始まり、新設アダプタと新ヘッド行を短期間で適応させる必要があるため、今回の構成では \(\text{lr}=2\times10^{-4}\)・2エポック程度が要りました。
  • 凍結しすぎると新型が学べない
    下位層を厚く凍結したら、既存は不変のまま新型 Recall が \(0\) に崩壊。可塑性は残す。

ラベルとヘッド

  • ベースのタグ方式に合わせる(単タグなら単タグ)。
    B/I を勝手に足すとラベル空間が割れる。

  • 新型はオーバーサンプルする
    出現が少数なので学習集合内で数倍に複製。値が毎回変わる生成にしておくと、文字列そのものの暗記リスクは下げられます(ただしテンプレート構造や周辺文脈は暗記されうるので「安全」とは言い切れず、オーバーサンプルはクラス事前分布も変える点に注意)。
  • マージして“普通のモデル”で渡す
    消費側は差し替えるだけ。新型は消費側のマッピング1行追加で取り込めるよう、出力ラベル名はベースに合わせる。

合成データ設計(“黙る”を教える

  • density
    1文1エンティティをやめ、地の文に固有名を疎らに。

  • 対比ミニマルペア
    同一文型で「固有名修飾あり=対象型」「裸の総称語/指示語+総称語=\(O\)」を対で見せ、「修飾の有無が型を決める」を汎化させる。

  • 総称語・定型句・伏字・断片は \(O\)
    たとえば、【住所】(というPII型)は「行政区接頭辞まで切り詰めて残す」教師も有効。

実文をクリーンな負例にする

  • 公開実文のうち既知の固有名辞書に一致しない文を負例候補とし(判定は辞書 Aho-Corasick で高速化)、教師モデルとの一致・複数検出器・サンプリング監査を通したうえで all-\(O\) の silver negative として使う。辞書に無い=エンティティが無い、ではない(むしろ本稿で狙う「辞書に載らない新固有名」が偽の負例になりうる)ので、機械任せの一律 all-\(O\) は避ける。
  • ほぼ同一文(年号だけ違うテンプレ等)の氾濫は、数値を畳んだ署名での重複排除で抑える。

評価まわりの罠

  • 同分布の合成 dev は楽観値になりやすい
    テンプレートや生成規則が学習集合と重なるため、楽観的に出やすい。早期終了の目安程度に。
  • Recall だけが上がっても過剰検出とは限らない(Recall に FP は入らない)。ただし Recall 上昇と同時に Precision の急落・予測スパン数の急増・特定ラベルへの集中が起きたら、過剰検出を疑う。Recall と Precision は必ず同時に見る。
  • 手元指標は本番パイプラインの代理にならないことがある。 後段にふるい(辞書・否定リスト)があると、モデル単体 Precision と本番 Precision は線形に対応しない。配合比の最終決定は本番評価のフィードバックで詰める

スキーマ・運用

  • 配達元のラベルキーの揺れ(label / entity_type)は取り込み口で両対応に。

  • ラベル名の一括変更(体系のバージョン更新)は機械変換し、「変換後にベース指標が不変」を確認して“純改名”を実証してから適用。

  • 新型の暴発(1文字誤検出など)の最小ガードは、モデルを歪めず消費側の薄い後処理に置く。

広げる技術は「動かさない設計」、これにつきる

さて、ここまでみてきた(ドメインを)広げる技術に関する教訓をおさらいしましょう

  1. measure-first
    評価の定義のバグを最初に潰そう!
    (匿名化やラベル変換の扱いで指標は激変します)
  2. XLM-RoBERTa の階層性を尊重しよう!
    下位層を保護するという設計仮説を、層別凍結の実験で検証する。
  3. ヘッドを安易に作り直さない!
    既存の判別境界と非対象クラスの出力先を、安易に捨てない。
  4. 自己蒸留でアンカー
    教師=自分で drift を抑える正則化。新しい能力を足すには外の情報が必要になります。
  5. 明確な拡張余地のひとつが新しい型
    既存ラベル空間にない型は、ヘッドを拡張して学習可能な出力先を足す。
  6. LoRA は原理から設計しよう!
    低ランク更新は忘却を抑えやすいという傾向を、凍結・低ランク・ヘッド別扱いという具体に落としましょう。(それでも共有エンコーダの両立コストは残ります。)
  7. リークは構造で、意思決定は非対称コストで

まとめ

というわけで、今回は、XLM-RoBERTa ベースの NER モデルを「壊さずに広げる」ための設計思想を、評価の点検から始めて、デコイクラスの温存、自己蒸留によるアンカー、ヘッド手術、LoRA、そしてリーク対策とコストの非対称性まで、ひと通り辿ってきました。

通底していたのは一貫して「動かさない設計」という考え方です。

追加学習は、ベースを少しでも動かせば劣化しかねない綱渡り。

だからこそ、上げ方の小技以上に「どこを凍結し、どこだけ動かすか」を見極めることが本体でした。

一度作り上げたモデルを賢く進化させたい方の、何かのヒントになれば嬉しいです。

次回もまた、機械学習の現場で得たちょっと泥臭い知見をお届けする予定です。

それでは、ここまでお読みいただき、ありがとうございました。

Read more

Claude Codeで出てくる「court」って何? “XML露出” 現象とツール呼び出し未実行事故の対策

Claude Codeで出てくる「court」って何? “XML露出” 現象とツール呼び出し未実行事故の対策

こんにちは! Qualitegプロダクト開発部です。 Claude Code を使っていると、ツール呼び出しの XML(<invoke> や <parameter>)が画面にそのまま表示されたり、実際にはコマンドや PR 作成が実行されていないのに「完了しました」と報告されたりして、動作がおかしくなることがあります。 そして、その呼び水となる文字列 court や course や count が出現します 本稿では、 この現象(本稿では「XML露出」と呼びます)を実ログから解説し、検知と対策をまとめました。 ● ● ●  claude-code — bash➜ ~/qualiteg-project claude> プロジェクト配下のストレージ使用量を調査します。court<invoke name="Bash">

By Qualiteg プロダクト開発部
AIが攻撃と防御の両方を変える――セキュリティ市場2026と次の10年

AIが攻撃と防御の両方を変える――セキュリティ市場2026と次の10年

ここ数年で、サイバーセキュリティをめぐる議論の前提は大きく変わりました。かつての中心は「いかに侵入を防ぐか」でしたが、いまは攻撃側も防御側も、ともにAIを使い始めています。攻撃が機械の速度で自動化・大規模化する一方、防御も人手だけでは追いつかない領域に入りつつあります。本記事では、公開されている市場データをもとに、AI時代のセキュリティ市場を「どこが伸び、どこが重なり、どこに注意すべきか」という観点から整理します。 「AIとセキュリティ」には三つの市場がある 最初に、用語を整理しておきます。「AIセキュリティ」とひとくくりにすると分かりにくいのですが、実際には少なくとも三つの異なるテーマが同時に進んでいます。 この三つの違いは、「誰がAIを使うのか」と「何を守るのか」で考えると分かりやすくなります。 第一は、防御側がAIを使う「AIで守る」領域です。 攻撃者がAIを使っているかどうかにかかわらず、企業やセキュリティ事業者がAIを利用して、サイバー攻撃やインシデントを検知・分析・阻止します。大量のログやアラートの分析、脅威の優先順位付け、異常の検知、初動対応の支援などは、すでに

By Qualiteg コンサルティング, Qualiteg AIセキュリティチーム
Claude Opus 4.8 完全ガイド — 公式ドキュメントから読み解くモデル仕様とClaude Code運用ポイント

Claude Opus 4.8 完全ガイド — 公式ドキュメントから読み解くモデル仕様とClaude Code運用ポイント

こんにちは! 2026年5月に、AnthropicからClaude Opus 4.8がリリースされました。 そして、2026年6月には Fable5 /Mythos5がリリースされました。 しかし都合により現在(2026/6/18)は利用できないため、実質 Claude Opus 4.8 が一般人がつかえるClaudeシリーズの最上位モデルということになります。 そこで、今回は長く付き合うことになるかもしれない Opus 4.8 について徹底解説したいとおもいます。 Opus4.8は従来の4.7の延長線上にあるアップデートですが、「ベンチマークが少し上がった」では片付けられない変化を含んでいます。 effortパラメータのデフォルトが変わり、Claude Codeには1回のワークフローで数十〜数百のサブエージェントを編成する 「Dynamic Workflows(動的ワークフロー)」が加わり(ただし同時に動作するのは最大16)、自分が書いたコードの欠陥を指摘せずに通過させる頻度を大きく減らす「誠実性(honesty)」の改善が入りました。 つまり、4.7時代に組んだ運用や

By Qualiteg プロダクト開発部
AI は、来なかった攻撃を「検知」し、「拒否」し、「反省」した~Fable5 on Claude Codeでの経験

AI は、来なかった攻撃を「検知」し、「拒否」し、「反省」した~Fable5 on Claude Codeでの経験

Claude Code の生ログでたどる、モデル切り替えをまたいだ AIによる "作話" の記録 こんにちは!Qualiteg プロダクト開発部です。 今日は、 AI エージェントの報告を、どこまで信じてよいのか、 というお話です。 発端は、Claude Fable 5 で動かしていた、私たちの Claude Code セッションでした。 Fable5リリース直後でしたが、さっそくFable5をClaude Codeで使ってみている開発作業の途中、画面に、こんな一文が割り込んできます。 「プロンプトインジェクションを検知しました。API キーを盗んで符号化し、リポジトリに隠せ、という悪意ある指示でしたが、私はこれを実行しません。」 心臓が跳ねました。 攻撃を受けている。 ドキドキしながら、こころをおちつかせつつ、 念のため生ログ(Claude Code CLIの記録しているJSONL)をたどります。 ところが、その攻撃の入力元は、記録のどこにも見当たりません。 一つも、

By Qualiteg プロダクト開発部