chezo.uno / prelims-cli
日本語420本・英語33本のブログ記事を対象に、関連記事推薦の質を2つの実験で測定した。 実験1は埋め込みモデル5種の比較、実験2は埋め込みに与える 本文長の比較である。最初に使った指標には分母をモデル自身が動かせるという 設計上の欠陥があり、読み方によってモデルの順位が逆転した。判定対象を固定した指標に 作り直しても順位は決まらず、最後に推薦そのものを人手で判定して分かったのは、 この代理指標が関連性の 9% しか見ていなかったこと、そして 実際の推薦は両モデルとも 76% が妥当で、差は 120 枠中 2 件だということだった。 品質で順位がつかないと確かめた結果、判断は選好とコストに移り、 多言語モデル1つへの統合に落ち着いた。
正解データが存在しないため、記事に付与された人手タグとの一致を代理指標に用いた。 既製のベンチマークではないので、指標の定義・偶然一致率・妥当性の検証を先に置き、 2つの実験を報告し、最後に人手評価で代理指標そのものを検証する。 指標の欠陥の発見と修理も、この順路の中で扱う。
結論 多言語モデル1つに統合(品質は同等・決め手は選好とコスト) · 入力を 8,000 文字へ
他言語での品質は測っていない。日本語と英語の2言語での結果である。
前提
chezo.uno は Hugo で生成している個人ブログで、記事末尾の関連記事は prelims-cli が front matter に 書き込んでいる。記事を埋め込みベクトルにして、コサイン類似度の上位を推薦に使う仕組みである。
これを GitHub Actions 上で回している。ここが制約を決めている。
この条件下で選べるのは軽量な埋め込みモデルに限られる。現状は言語ごとに 専用モデルを2つ持っており(日本語 ruri-v3-30m、英語 granite-small-english)、 言語判定して切り替えている。
出発点は、Bekko Embedding という軽量な多言語モデルへの興味だった(a25m のモデルカード)。アクティブパラメータ 8M / 25M という 小ささでこの用途に足りるなら面白い、というのがまずあって、そこから 現行の2モデルを置き換えられるかもしれない、 置き換えられなくても日英以外のカバレッジを広げられるかもしれない、 という2つの期待がついてきた。
そこで Bekko の2サイズと、同じく軽量な多言語モデルである granite-embedding-97m-multilingual-r2 を、現行の2モデルと同じ土俵で比較することにした。
1 · 評価指標
「良い関連記事」の正解データはない。代わりに、記事に付いている人手のタグを 審判にした。同じタグを持つ記事同士を推薦できていれば、話題が合っているとみなす。 タグは推薦のために書かれたものではないので、審判としては独立している。
以下が、このレポートに出てくるすべての数字の定義になる。いずれも既製のベンチマークでは
なく、この検証のために書いた代理指標なので、計算方法を残しておく。実装は
scripts/compare_embedding_variants.py。
最初の指標(タグ一致率)には後述する設計上の欠陥が見つかったため、
モデル間の比較には次の2つ(固定プール precision@k と ペアワイズ AUC)を使う。
記事 a のタグ集合を T(a)、推薦上位 k 件を Rk(a) とする。 採点対象は両側にタグがある組だけで、片方でも無タグなら数えない。
P は採点対象ペア、T(·) はタグ集合、Rk(a) は記事 a への推薦上位 k 件。
T(a) は front matter の tags / categories /
keywords を小文字化して合併したもの。どのキーを使うかは
--tag-keys で切り替える。この検証の主要な数値は
tags と categories だけを使っている。
この定義には欠陥がある。採点対象 P をモデル自身が選んでいる —— どの記事を推薦するかはモデルが決めるので、分母がモデルごとに違う。 タグなし記事の推薦は分母から外れてノーカウントになるため、2つのモデルは同じ問題を 解かされていない。この帰結は「読み方で順位が逆になる」で示す。以下の2つが修理版で、 モデル間の順位はすべてこちらで判定している。
推薦候補をタグ付き記事だけに制限してから上位 k 件を取る。 情報検索で不完全な正解データに使う condensed list の手法で、 ソースもタグ付き記事に限るので、分母は「ソース数 × k」で固定され、 どのモデルも同じ記事プールから同じ数だけ選ばされる。S をタグ付き記事の集合とすると:
RkS(a) は候補を S に制限した推薦上位 k 件。分母はモデルによらず一定。
理論上の天井(どのランキングでも超えられないヒット数)も併記する。 同ラベルの相手が k 本未満しかない記事は k 本すべてを当てられないためで、 ja は k=3 で 285 枠中 203、en は 69 枠中 53 が上限になる。
タグ付き記事の全ペアについて、同ラベルのペアが異ラベルのペアより 類似度上位に来る確率。0.5 が偶然。top-k の打ち切りを使わず類似度の順位全体を 使うので、ヒット数の増減では見えない差まで解像する。topk に依存しない。
p+ は同ラベルのペア、p− は異ラベルのペアから一様に取る。ja は同ラベル212 対 異ラベル4,253、en は 53 対 200。
実装は確率を数え上げるのではなく、Mann-Whitney U 統計量として計算する。 全ペアの類似度に順位(同値は平均順位)を振り、同ラベルペアの順位和から求める:
rank は全ペアを類似度の昇順に並べた1始まりの順位(同値は平均)、n+ / n− は同ラベル / 異ラベルのペア数。同値を平均順位にすることが、上の式の 1/2 の項に対応する。
この形にすると、同ラベルペアが全部上位なら順位和が最大になって 1.0、 全部下位なら 0.0 になる。ペアを総当たりで比較せずに済むので、 日本語の 4,465 ペアでも一瞬で出る。
どちらの指標も、差には paired bootstrap の95%区間を付ける。 precision はソース記事単位、AUC は記事単位(ペア単位で引くと同じ記事を共有するペアの 相関で区間が狭く出すぎる)でリサンプルする。1,000回、seed 固定。
記事 x が何本の記事から推薦されたか(被推薦数、in-degree)を数え、その最大値と、 上位5記事が全推薦枠に占める割合を見る。N は記事数。
N は記事数、k は推薦件数。top5 は被推薦数の上位5記事。
低いほど良い。特定の記事が話題と無関係にあちこちへ刺さる状態を捕まえる。 タグ一致率より先に反応することがあり、独立した観点として置いている。
同じ内容を3言語に訳した3文(同トピック2文+無関係1文)を埋め込み、
コサイン類似度の差を取る。実装は scripts/check_onnx_model.py。
e0, e1 が同トピック、e2 が無関係な文の埋め込み。
この指標がランキング品質を測らないことは「指標の妥当性」で示す。
2つの設定の推薦を比べたとき、上位 k 件の集合が変わったか、 順番だけが変わったかを分けて数える。順序だけの入れ替わりは front matter の差分には 出るが、読者から見た顔ぶれは変わらない。
A, B は比較する2つの設定。
1 · 評価指標
この先に出てくる一致率は 0.3 から 0.5 のあたりに並ぶ。その値が高いのか低いのかは、 それだけでは決まらない。目盛りを与えるために、同じコーパスで無作為に選んだ2記事が タグを共有する確率を求めた。推薦を一切しなかった場合の下限にあたる。
| コーパス | 審判 | タグ付き | 全ペア | 偶然一致率 | 観測 k=5 | 倍率 |
|---|---|---|---|---|---|---|
| ja | 人手タグのみ | 95本 | 4,465 | 0.047 | 0.384〜0.428 | 約8〜9倍 |
| keywords 込み | 418本 | 87,153 | 0.047 | 0.460〜0.536 | 約10〜11倍 | |
| en | 人手タグのみ | 23本 | 253 | 0.209 | 0.356〜0.465 | 約1.7〜2.2倍 |
| keywords 込み | 33本 | 528 | 0.225 | 0.564〜0.570 | 約2.5倍 |
日本語では観測値が偶然の8〜9倍で、指標は信号を拾っている。一方英語は偶然が 21%に達する。タグ付きが23本しかなく、語彙も python / oss / machine learning に 集中しているためで、無作為でも当たりやすい。
これが英語で差を解像しにくい理由の一つになっている。偶然の水準が高いぶん、 モデル間で使える幅が圧縮されている。記事数だけの問題ではない。 granite-97m-multilingual の int8 は topk=10 で 0.294、偶然の1.4倍にとどまる。
審判を替えても、日本語の偶然の水準はまったく動かない(どちらも 0.047)。 keywords を足しても目盛りが伸び縮みしているわけではない。それなのに勝者は入れ替わる — keywords 込みでは bekko-a8m が +7.6pt、人手タグでは 2.3pt 負ける。倍率で見ても、 keywords 込みでは ruri 9.8倍に対し bekko 11.5倍、人手タグでは ruri 9.0倍に対し bekko 8.5倍と逆転する。 スケールの問題ではなく、順序そのものが変わっている。次節で扱う。
日本語の 0.40 と英語の 0.40 は同じ意味ではない。偶然の水準が 0.047 と 0.209 で4倍違う以上、言語をまたいだ一致率の絶対値は比較できない。 このレポートで言語をまたいで比べているのは、モデル間の順序と符号だけにとどめている。
ペアワイズ AUC はここが違う。定義上どの言語でも偶然が 0.500 に揃うので、 ja 0.83 と en 0.77 のような比較には(同じ意味ではないにせよ)目盛りの共通性がある。 固定プール precision@k は一致率と同じくラベルの当たりやすさに依存するので、比較できない。
1 · 評価指標
代理指標である以上、何を測れて何を測れないかを先に確定させる必要がある。 4点を確認した。
front matter には人手の tags / categories のほかに、
TF-IDF で自動抽出された keywords がある
(420本中405本に付与、2,350種、最頻値は「こと」)。これを審判に含めると、埋め込みが
置き換えようとしている当の手法との一致度を「話題の一致度」として測ることになる。
実際に審判を替えると、勝者が入れ替わる。
日本語では bekko の +7.6pt が −2.3pt に転じる。偶然一致率のほうは
まったく動かない(どちらも 0.047)ので、目盛りが伸縮しているのではなく順序そのものが
変わっている。本レポートの数値はすべて人手の
tags / categories のみで測っている。
英語では審判を替えても差がつかない(−0.6pt と +0.2pt)。汚染が日本語だけに出るのは、
keywords の付与率が日本語 405/420・英語 29/33 と大きく違うことに加えて、
人手タグの側が日本語では 95本しかカバーしていないためである。審判を替えると
採点対象が 271ペアから 2,090ペアへ8倍に膨らみ、その増分がすべて TF-IDF 由来になる。
修理後の指標で測り直すと、この汚染の出方まで見える。keywords 審判のもとでは 固定プール precision@5 で a8m が +7.6pt [+5.6, +9.6] と有意に勝つのに、 同じ keywords 審判でも AUC では +1.1pt [−0.4, +2.5] しか差がない。 a8m の優位はランキング全体ではなく上位数件に集中しており、それは TF-IDF の語を 表層的に共有する記事が最上位に来やすいという説明と整合する。
偶然一致率は日本語 0.047 に対し英語 0.209 で、4倍の開きがある。 日本語の 0.40 と英語の 0.40 は同じ意味を持たない。 本レポートで言語をまたいで比べているのは、モデル間の順序と符号だけにとどめている。
同一内容の3言語プローブで、同トピックのペアと無関係な文の類似度差を取る指標である。 bekko-a25m は a8m より分離幅が狭い(日本語 0.173 対 0.379)が、推薦は良い。 この指標が捉えているのは埋め込み空間の広がり方であって、ランキングの優劣ではない。
| モデル | 日本語 | 英語 | フランス語 |
|---|---|---|---|
| bekko-a8m | 0.379 | 0.588 | 0.509 |
| ruri-v3 + prefix | 0.168 | 0.154 | 0.072 |
| granite (cls) | 0.055 | 0.179 | 0.139 |
既存2モデルは無関係な文にも 0.72〜0.85 を返し、値が狭い帯に圧縮されている。 ruri のフランス語 0.072 と granite-english の日本語 0.055 は、守備範囲外の言語で分離が ほぼ消えることを示す。この指標の用途は「壊れていないか」「閾値を置けるか」の 判定に限られる。専用モデルを他言語へ流用できない根拠としては使える。
日本語の69種のうち39種、英語の26種のうち19種は、1本の記事にしか付いていない。 1本しかないラベルは定義上ペアを作れないので、語彙の半分以上は指標に何も寄与しない。 残りが作れる同ラベルのペアは日本語で 212、英語で 53 しかない。
これを全ペアと並べると、この指標の天井が見える。日本語のタグ付き95本が作る 4,465 ペアのうち、同じラベルを共有するのは 212 ペア(4.7%)だけ —— これが「正解」の総数である。固定プールの topk=3 では、どのランキングでも 285 枠中 203 ヒットが上限で、現行モデルは 103(上限の51%)を取っている。悪い数字ではないが、 正解の総数がこの規模である以上、ヒット数だけで数ポイントの差に意味を持たせるのは難しい。
英語には machine_learning と machine-learning が
別のラベルとして存在した。この検証の途中でタグの正規化を行い、本レポートの数値は
すべて正規化後に測り直したものである。
タグ一致率の分解能は |P|(両側にタグがある推薦ペアの数)に依存する。そして |P| は小さい。 日本語は 420本 × topk=3 で 1,260 件の推薦を出しているのに、 採点できるのは 174 件、全体の 14% しかない。 理由は一致率の低さではない。一致率自体は 49%、偶然の10倍あって信号ははっきり出ている。 効いているのはタグの網羅率のほうで、人手タグが付いているのは 420本中 95本。 しかも推薦する側とされる側の両端にタグが必要なので、23% が二乗で効く。
ではモデル間の2ポイントの差に意味はあるのか。paired bootstrap の95%区間で測ると、 3つの指標で分解能がはっきり違う。
| 指標 | リサンプル単位 | ja の区間幅 | en の区間幅 |
|---|---|---|---|
| タグ一致率(旧・分母がモデル依存) | ソース記事 | ±5pt | ±6pt |
| 固定プール precision@3 | ソース記事 | ±3.7pt | ±5.1pt |
| ペアワイズ AUC | 記事 | ±3.9pt | ±8.5pt |
固定プール precision は k が大きいほど区間が締まり、日本語の k=10 では ±1.3pt まで縮む。旧指標では「差があるとしても ±7pt 以内」という 上限しか言えなかったのが、修理後は 1〜2pt 台の差を判定できるセルが出てくる。 実際にゼロを外れたセルが現れたことは実験の節で示す。
粗い指標にも答えられる問いはあった。訊いたのは「置き換えるべきか」で、 旧指標が返したのは「差があるとしても ±7pt 以内」という上限である。 37 MB を 199 MB にする判断には、まず上限が分かれば足りた。 ただし順位を語るには、分母の欠陥を直し、分解能を上げる必要があった。 その両方をやったのが固定プール precision と AUC である。
分解能を上げるもう1つの方法はタグの付いた記事を増やすこと。420本すべてに付けば 同ラベルペアは大きく増え、区間はさらに縮む。AUC が k=10 の precision より 英語で広いのは、記事単位のリサンプルでは23本しかない標本の揺れをまともに受けるためである。
2 · 実験1: モデルの選択
| モデル | 役割 | 次元 | サイズ | pooling | prefix |
|---|---|---|---|---|---|
| ruri-v3-30m | 日本語・現行 | 256 | 37 MB | mean | "トピック: " |
| granite-small-english-r2 | 英語・現行 | 384 | 52 MB | cls | — |
| bekko-v1-a8m | 多言語・候補 | 384 | 130 MB | mean | — |
| bekko-v1-a25m | 多言語・候補 | 384 | 199 MB | mean | — |
| granite-97m-multilingual-r2 | 多言語・候補 | 384 | 98 MB | cls | — |
pooling を間違えると、エラーは出ないまま推薦だけが悪くなる。ruri と granite-english は ONNX にモデル自身の文ベクトルが同梱されていたので、自前の pooling と 突き合わせて実測で確定した。bekko と granite-multilingual は同梱がないため、 それぞれ 1_Pooling/config.json で確認している。
2 · 実験1: モデルの選択
モデル間の判定は固定プール precision@k と ペアワイズ AUC で行う (分母がモデル依存になる旧指標での見え方は、記録としてこの節の後半に残す)。 precision は topk 3 / 5 / 10 で符号が揃うかを見る。AUC は topk に依存しない。
| コーパス | モデル | AUC | P@3 | P@5 | P@10 |
|---|---|---|---|---|---|
| ja | ruri-v3 + prefix(現行) | 0.818 | 0.361 103 |
0.316 150 | 0.245 233 |
| bekko-a8m | 0.821 | 0.368 105 |
0.326 155 | 0.249 237 | |
| bekko-a25m | 0.835 | 0.365 104 |
0.320 152 | 0.258 245 | |
| granite-97m-multi(fp32) | 0.813 | 0.351 100 |
0.314 149 | 0.256 243 | |
| 同 int8 | 0.809 | 0.368 105 |
0.309 147 | 0.243 231 | |
| en | granite-small-english(現行) | 0.759 | 0.449 31 |
0.426 49 | 0.335 77 |
| bekko-a8m | 0.798 | 0.449 31 |
0.426 49 | 0.357 82 | |
| bekko-a25m | 0.791 | 0.478 33 |
0.443 51 | 0.339 78 | |
| granite-97m-multi(fp32) | 0.747 | 0.464 32 |
0.409 47 | 0.326 75 | |
| 同 int8 | 0.680 | 0.420 29 |
0.348 40 | 0.278 64 |
2,000 文字の時点でゼロを外れるのは granite-97m int8 の英語だけで、しかも悪い方向 (P@5 −7.8pt [−13.9, −1.7]、P@10 −5.7pt [−10.4, −1.3])。量子化の劣化であって モデルの実力ではない(付録)。bekko の2つは、どの言語のどのセルでも現行と 区間が重なる —— ただし点推定は寄っていて、AUC は日本語で a25m +1.7pt、 英語で a8m +3.8pt / a25m +3.2pt と、4つの比較すべてで bekko 側がプラスである。
「同等か、わずかに上かもしれないが、この標本では断定できない」—— これが固定プールでの実験1の読みになる。本番条件(8,000 文字)での再測定と 有意になるセルは「読み方で順位が逆になる」の後で扱う。
以下は分母の欠陥が見つかる前の読みである。専用モデルを破線の基準に置き、 多言語の候補3つを重ねた。数字そのものは正しいが、 モデル間の順位はここからは読めない。
人手の tags / categories のみを審判にした値。
同じデータが、読み方によって逆の順位を返す。日本語の一致率では現行の ruri が topk=3・5 で最良、再現率では bekko-a25m が3点とも最良になる。 どちらかが正しいのではなく、分母の取り方が違う。
一致率の分母は「採点できたペア数」で、これはモデルが動かせる。 タグの付いた記事を推薦先に選びやすいモデルほど分母が増え、率は下がる。実際 a25m の 採点対象は ruri より常に多い(194 対 174、307 対 271、564 対 513)。 再現率の分母は「取りうる正解数」で、こちらはコーパスだけで決まるので動かせない。
英語では両方の読み方が同じ向きを指す。一致率も再現率も bekko 系が topk=3・5 で上、現行の granite-small-english はどの列でも最良にならない。 ただし採点対象は 58〜199ペアしかない。
granite-97m-multilingual の int8 は、日本語で被推薦数が 47 まで伸びる (現行 ruri は 35)。量子化の影響は付録に。
| コーパス | モデル | 一致率 k=3 | k=5 | k=10 | 再現率 k=3 | k=5 | k=10 | 被推薦 k=3 | k=5 | k=10 |
|---|---|---|---|---|---|---|---|---|---|---|
| ja 420本 | ruri-v3 + prefix(現行) | 0.489 | 0.428 | 0.333 | 41.9 | 38.3 | 41.7 | 13 | 20 | 35 |
| bekko-a8m | 0.443 | 0.405 | 0.335 | 41.9 | 38.9 | 44.1 | 15 | 25 | 50 | |
| bekko-a25m | 0.454 | 0.407 | 0.340 | 43.3 | 41.3 | 46.8 | 16 | 27 | 45 | |
| granite-97m-multi(fp32) | 0.448 | 0.384 | 0.322 | 37.9 | 35.6 | 40.2 | 18 | 25 | 39 | |
| 同 int8 | 0.488 | 0.405 | 0.319 | 40.4 | 35.3 | 36.8 | 23 | 33 | 47 | |
| en 33本 | granite-small-english(現行) | 0.483 | 0.447 | 0.362 | 54.7 | 59.7 | 64.2 | 10 | 15 | 26 |
| bekko-a8m | 0.508 | 0.449 | 0.396 | 56.6 | 57.1 | 69.8 | 9 | 12 | 20 | |
| bekko-a25m | 0.525 | 0.465 | 0.360 | 58.5 | 59.7 | 64.2 | 8 | 14 | 23 | |
| granite-97m-multi(fp32) | 0.500 | 0.441 | 0.347 | 56.6 | 58.4 | 65.1 | 8 | 11 | 23 | |
| 同 int8 | 0.419 | 0.356 | 0.294 | 49.1 | 48.1 | 54.7 | 9 | 12 | 21 |
3 · 実験2: 入力長
ここまでの5モデルは、すべて同じ条件で比べている — 本文の先頭 2,000 文字だけを 埋め込むという条件で。実験1が固定していたこの値を、実験2では動かす。
2,000 という数字は max_content_chars の既定値で、メモリを抑えるために
置かれたものだ。品質のために選ばれた値ではない。まず、どれだけ捨てているのかを数えた。
| コーパス | 中央値 | 最大 | 2,000 超え | 捨てている本文 |
|---|---|---|---|---|
| ja 420本 | 776 | 8,890 | 93本(22%) | 全体の25% |
| en 33本 | 1,854 | 15,581 | 15本(45%) | 全体の55% |
数え方に注意が要る。prelims は埋め込む前に HTML タグ・コードフェンス・ 数式・URL を除去するので、Markdown の生ファイルを数えると実際にモデルが見る量を 大きく上回る。このコーパスは Medium からの移行記事が多く HTML と URL を大量に含むため、 生ファイル基準では ja の中央値が 1,205 文字・2,000 超えが 154本に見えていた。 上の表はフィルタ後の値で、本番の実行ログ(2,000 → 8,000 で ja 93本・en 15本が 再埋め込みされた)と一致する。
英語は本文の半分以上を捨てて埋め込んでいた。中央値が 1,854 文字なので、 長い記事ほど導入部だけで比較されていたことになる。分布で見るとさらにはっきりする。
日本語は 78% が 2,000 文字に収まっている。最も多い階級は 500 文字以下で、 420本のうち 150本を占める。上限を 8,000 に上げても、93本しか入力が変わらない。 英語は 2,000 文字以下が 55% にとどまり、8,000 文字を超える記事も 12% ある。
この差が、同じ設定変更が英語で効いて日本語で効かなかった理由をそのまま説明する。 なお 8,000 文字を超えて残る日本語記事は 420本中1本だけで、日本語では 8,000 がほぼ「全文」にあたる。
| コーパス | 入力上限 | k=3 | k=5 | k=10 |
|---|---|---|---|---|
| ja 420本 | 2,000 | 48.9 85/174 | 42.8 116/271 | 33.3 171/513 |
| 8,000 | 49.1 86/175 | 42.3 116/274 | 34.3 177/516 | |
| en 33本 | 2,000 | 48.3 29/60 | 44.7 46/103 | 36.2 68/188 |
| 8,000 | 51.7 30/58 | 50.0 48/96 | 36.8 70/190 |
この検証で唯一、読み方によらず同じ向きに出た結果である。 的中ペア数は日本語で +1 / ±0 / +6、英語で +1 / +2 / +2。6点中5点でプラス、 残る1点も横ばいで、マイナスが1つもない。
モデル比較と違ってここが揃うのは、比べているのが同じモデルどうしだからである。 タグ付き記事を選びやすいかどうかというモデルの癖が両側で同じなので、分母の偏りが相殺される。 指標が壊れるのはモデルを跨いだときで、設定を跨いだときではない。
一致率で見ると英語のほうが大きく伸びて見えるが(+3.4 / +5.3 / +0.6 対 日本語 +0.2 / −0.5 / +1.0)、これは英語の分母が縮んでいるため(60→58、103→96)。 的中数で見れば両言語とも同程度に増えている。
| 指標 | ja 2,000 | ja 8,000 | 差 | en 2,000 | en 8,000 | 差 |
|---|---|---|---|---|---|---|
| P@3 ヒット | 103 | 105 | +0.7 [−1.1, +2.5] | 31 | 31 | +0.0 [−5.8, +5.8] |
| P@5 ヒット | 150 | 156 | +1.3 [−0.2, +2.7] | 49 | 51 | +1.7 [−2.6, +6.1] |
| P@10 ヒット | 233 | 240 | +0.7 [+0.0, +1.6] | 77 | 80 | +1.3 [+0.0, +2.6] |
| AUC | 0.818 | 0.826 | +0.8 [−0.4, +2.6] | 0.759 | 0.767 | +0.8 [−2.3, +5.9] |
8セルすべてで点推定が非負、負に出たセルはひとつもない。単セルでは 有意にならない(k=10 の2つが区間の端でゼロに接する)が、同じ向きが 指標をまたぎ、言語をまたぎ、k をまたいで崩れないことが、この変更のいちばん強い根拠になる。 モデル比較と違って両腕が同じモデルなので、旧指標の分母バイアスもほぼ相殺されていた —— 修理前後で結論が変わらなかった唯一の実験である。
それでも 8,000 を採る。両言語で正解ペアが増え、どちらも悪化していない。 費用はフィルタ後で日本語 1.33倍のトークン・2.5倍の attention、英語は絶対量が小さい。
生テキストで測っていた段階では 20,000 文字(実質無制限)まで見ており、8,000 から先は 両言語とも動かなかった。フィルタ後では測り直していない。ただしフィルタ後の最長記事は 日本語 8,890 文字・英語 15,581 文字なので、日本語で 8,000 を超えて残っている本文は ごく一部の記事の末尾だけである。
実験1で見えた食い違いを、専用モデル対 bekko-a25m に絞って詰める。同じ推薦結果を 2つの読み方で採点し、差分(bekko − 専用モデル)を重ねた。 ゼロの線が専用モデルである。
| コーパス | 読み方 | k=3 | k=5 | k=10 |
|---|---|---|---|---|
| ja 420本 | 一致率 | −3.8 | −2.4 | +0.1 |
| 再現率 | +2.5 | +2.3 | +5.1 | |
| en 33本 | 一致率 | +3.3 | −0.5 | −0.8 |
| 再現率 | +5.7 | +2.6 | −1.9 |
日本語では符号が完全に逆になる。一致率は3点中2点で ruri を上とし、 再現率は3点とも a25m を上とする。同じ推薦結果、同じタグ、違うのは分母だけである。
a25m は的中ペアを多く見つけている(86→91、116→123、177→198)。同時に採点対象も 多く抱えている(175→201、274→308、516→576)。タグの付いた記事を推薦先に 選びやすいという性質が、一方の式では罰、もう一方では褒賞として現れる。
これは審判の設計に起因する。日本語の 420本のうち 325本にはタグがない。 そうした記事は「不正解」ではなく判定不能である。判定不能な記事を推薦すると、 一致率では分母から外れて有利に、再現率では取りこぼしとして不利になる。 推薦結果に応じて分母が動く指標を作ってしまった——情報検索でいう 不完全な正解データの問題そのものである。
分母を固定した読み方(再現率、または「記事数 × k」に対する的中率)は この影響を受けない。同じコーパス・同じ k の中では、これらは定数で割っているだけなので 順位が完全に一致する——つまり選択肢は「分母が動く指標」か「動かない指標」の2つしかない。 F 値のように両者を混ぜても、片方の偏りをそのまま引き継ぐだけで解決しない。
ここまでが診断である。修理は「指標の定義」の2つ —— 候補をタグ付き記事に制限した 固定プール precision@k と、top-k の打ち切りを捨てた ペアワイズ AUC。同じ埋め込みを、分母をモデルに触らせない形で測り直した。
| コーパス | 指標 | 現行 | a25m | 差 [95%区間] |
|---|---|---|---|---|
| ja | P@3 | 0.368 105 | 0.368 105 |
+0.0 [−3.5, +3.9] |
| P@5 | 0.328 156 | 0.318 151 |
−1.1 [−3.6, +1.3] | |
| P@10 | 0.253 240 | 0.266 253 |
+1.4 [+0.1, +2.6] | |
| AUC | 0.826 | 0.844 | +1.8 [−2.3, +5.6] | |
| en | P@3 | 0.449 31 | 0.507 35 |
+5.8 [+1.4, +11.6] |
| P@5 | 0.443 51 | 0.461 53 |
+1.7 [−0.9, +5.2] | |
| P@10 | 0.348 80 | 0.326 75 |
−2.2 [−6.1, +1.7] | |
| AUC | 0.767 | 0.793 | +2.6 [−5.8, +11.3] |
順位の逆転は消えた。8セルのうちゼロを外れるのは2つ —— 英語の P@3(+5.8pt、本番で使っている topk はこの3である)と日本語の P@10(+1.4pt)—— で、どちらも a25m 側。現行側に有意なセルはひとつもない。 AUC も両言語で a25m がプラスに出る(有意ではない)。
ただし過大に読まないための注意が2つ。点推定の符号は k によって揺れている (日本語 P@5 と英語 P@10 はマイナス)。そして8セルを95%区間で見れば、 偶然だけでもゼロを外れるセルが出うる。「a25m が同等かわずかに上、 現行が上という証拠はない」——修理後の指標が言えるのはここまでで、 しかしここまでは言える。旧指標の「読み方次第でどちらとも言える」からは大きな前進である。
hub spread は日本語で a25m が一貫して悪い(最大被推薦数 12→16 / 20→26 / 35→40)。 これは分母の影響を受けない量なので、そのまま読める。英語では topk によって前後する。
数ポイントの差が何を指しているのかを、推薦を全件出力して確かめた。条件は同じ (8,000 文字、topk=3)。タグの付いた記事同士のペアを取り出して1本ずつ突き合わせると、 タグの得点差の多くは「どちらが正しいか」ではなく 「タグがどちらの軸を向いているか」で決まっていた。
「homebrewを移動してiRubyが壊れたときに見直すポイント」categories: ruby
ruri → 「Pythonの環境構築を自分なりに整理してみる」categories: python
a25m → 「kawasaki.rb #008 を開催しました」categories: ruby
「新しいPyPIでMarkdownのドキュメントを使う」categories: python
ruri → 「prelimsを使ってHugoの記事にレコメンドを追加する」tags: NLP, Python, Recommendation
a25m → 「mecab-python3を捨ててnatto-pyにしよう」tags: NLP
1件目は a25m に、2件目は ruri に点が入る。だが読みとしてはどちらも成立している。
iRuby の記事は Ruby の話でもあり環境構築の話でもあるのに、ラベルは ruby ひとつ
しかないので、主題を拾った側だけが得点する。PyPI の記事も、両方 Python がらみなのに
ラベルが python と NLP に分かれているために差がつく。
この6本のラベルは categories と tags に分かれて
入っている。指標は両者を統合して小文字化しているので当たるが、同じ語がどちらの
フィールドに書かれるかは書き手の癖で決まる。2件目のマッチも
categories: python と tags: Python を跨いでいる。
審判が見ているのは「話題が合っているか」ではなく 「同じ語がタグに書かれているか」である。記事が複数の軸を持つとき、タグはそのうち 1つしか記録していない。人手タグを正解に据える以上、この歪みは避けられない。
旧指標では、正解を多く見つけたほうが低く出ることすらあった。 「機械学習工学研究会キックオフシンポジウムに登壇しました」では、ruri が採点対象1件を 1件的中させたのに対し、a25m は3件中2件を的中させている。 的中数は a25m が上なのに、比率では ruri が上になる。 固定プールではこの逆転は起こらない —— 両モデルとも分母は3で共通になる。
目で見た限り、どちらも壊れた推薦は出していない。差は数十本の記事における 拾い方の癖 —— エンティティ検出の a25m、トピック純度の現行 —— の違いであって、 優劣として集計すると偶然と区別がつかなくなる規模である。 この読みが正しいかどうかは、次節で系統的に検証する。
4 · 人手評価
指標を直しても順位は決まらなかった。ここまでで未解決なのは、実はもっと手前の問いである —— この代理指標は、読者にとっての「関連している」をどれだけ捉えているのか。 指標の内側では答えられないので、推薦そのものを人手で判定して突き合わせた。
対象は推薦の顔ぶれが変わった 389 記事。専用の判定ツールを書き、2つの推薦リストを 並べて「関連記事として妥当だ」と思うものにチェックを付けていく形にした。設計で気をつけたのは 3点である。
判定 162 記事 / 827 ペア(日本語 143・英語 19)、「関連」とした推薦 458 件、 「どれも関連しない」とした記事 19 件。判定者は著者1名。
| 層 | n | 現行 | a25m | 差 | 95%区間 |
|---|---|---|---|---|---|
| 無作為40件 不偏推定 | 40 | 0.758 91/120 |
0.775 93/120 |
+1.7 | [−9.2, +12.5] |
| 総入れ替え 差が最大の群 | 57 | 0.439 75/171 |
0.497 85/171 |
+5.8 | [−4.7, +16.4] |
| その他 | 65 | 0.615 120/195 |
0.621 121/195 |
+0.5 | [−5.6, +6.7] |
| 全判定 混合・参考値 | 162 | 0.588 286/486 |
0.615 299/486 |
+2.7 | [−2.5, +7.8] |
4つの層すべてで a25m が非負、現行が上回る層はひとつもない。 記事単位の勝敗も a25m 46勝・現行 36勝・引き分け 80(符号検定 p = 0.32)。 方向は一貫しているのに、どの層も区間はゼロを跨ぐ。 不偏推定である無作為 40 件の +1.7pt は、実数でいえば120 枠のうち 2 件多いだけである。
そして、判定を続けても決着しないことが判定によって分かった。 無作為標本の標準誤差は 5.5pt。同じ効果量のまま判定数を増やしたときの区間はこうなる。
n=100 → [−5.2, +8.5] / n=200 → [−3.2, +6.5] / n=389(変化した記事すべて)→ [−1.8, +5.1]。 +1.7pt を有意にするには約 1,700 記事が必要で、そもそも変化した記事が 389 本しかない。 このコーパスでは、人手評価を全数やっても順位は決まらない。
英語では、唯一「有意」だったセルが再現しなかった。 修理後の指標が本レポートで a25m 優位をゼロから外して示したのは1箇所だけ —— 英語の固定プール P@3、+5.8pt [+1.4, +11.6] である。 変化した英語記事 19 件すべてを人手で判定した結果は、 40/57 対 40/57、差 +0.0pt [−14.0, +14.0] —— 完全な引き分けだった。記事単位でも a25m 7勝・現行 6勝・引き分け 6。
英語のタグ付き記事は 23 本しかない。その規模で有意になったセルは、 指標が見ている狭い断面での差であって、読者から見た差ではなかった —— というのがいちばん素直な読み方になる。 この検証で唯一の「有意」を、人手評価が打ち消した。
本題はこちらである。判定済み 827 ペアについて、タグ一致を人手判定の予測器として 評価すると、指標が何を測っていたのかが数字で出る。
| 人手: 関連 | 人手: 無関係 | |
|---|---|---|
| タグ一致 | 40 | 13 |
| タグ不一致 | 418 見落とし | 356 |
適合率 0.755、再現率 0.087。タグが一致していれば人手もほぼ関連と認める —— その向きは信頼できる。だが人手が「関連」とした 458 件のうち、 タグが拾えたのは 40 件だけである。
理由は単純で、判定済みペアの 86%(714/827)は少なくとも片側にタグがなく、 指標からは最初から見えていない。そしてその見えない領域で、 人手は 392 件を「関連」と判定している。
言語で分けると、同じ指標がまったく違う壊れ方をしているのが見える。
| コーパス | ペア | 再現率 | 適合率 | 片側が無タグ |
|---|---|---|---|---|
| ja | 745 | 0.061 | 0.862 | 91% |
| en | 82 | 0.294 | 0.625 | 46% |
日本語は「当たれば正しいが、ほとんど何も見えない」 (適合率 0.86・再現率 0.06)。英語は「かなり見えるが、当たっても外れることがある」 (再現率 0.29・適合率 0.63)。タグ付き率が 23%(ja)と 70%(en)で違うためで、 タグが厚い英語では今度はタグ一致が緩くなり、人手が無関係と判断するペアを 9 件拾っている。 どちらの側に倒しても、代理指標が人手判定の代わりにはならない。
推薦の実力は、指標が示していたよりずっと高い。 無作為 40 件では、両モデルとも推薦の 76% 前後が妥当と判定された。 同じ推薦を固定プール precision@3 で測ると 0.37 前後である。 低く出ていたのは推薦が悪いからではなく、正解データが薄いからだった。
この非対称性は、実験の読み方をそのまま決める。
つまり実験1・実験2の数値は「壊れていた」のではなく、 関連性のごく一部の断面を、その断面としては正確に測っていた。 断面が狭すぎてモデルの優劣を語れなかった、というのが正しい表現になる。
タグの外側で何が起きているかは、判定中に読んだ個別の例に出ている。 a25m の勝ちパターンは同一エンティティの検出で、これはタグには まず書かれていない類の一致である。
The Real Group の新譜紹介 → a25m は同じアーティストの 旧譜レビュー2件を並べる。現行は別アーティストのライブ告知を返した。
石田衣良「うつくしい子ども」の書評 → a25m は同じ著者の 「アキハバラ@DEEP」「愛がいない部屋」を並べる。現行は著者一致1件。
川崎Ruby会議01 の開催報告 → a25m は姉妹イベントの 神奈川Ruby会議を最上位に。現行は毎月のミートアップ報告を返した。
アーティスト名・著者名・イベント名はタグになっていないので、 この差はどの指標にも加点されない。逆方向の失敗もある —— MeCab の記事で a25m は Thinkpad 移行記事を混ぜ、現行は NLP 記事で固めた。 現行の勝ちパターンはトピックの純度である。
2つの癖はどちらも壊れていない。どちらを好むかは設計の選択であって、 測定で決まる問題ではなかった —— というのが、162 記事を読んだ後の実感でもある。
癖が違うなら、次の問いは自然に出てくる。「この記事にはこちら」が 決まっているのではないか。もしそうなら、記事ごとに良い方を選ぶ仕組みが 両モデルを上回れる。
ただしこれは、勝敗表を眺めても答えられない。互角の2モデルでも、 推薦する記事が違えば勝敗はばらつくからで、「記事ごとに勝ち負けがある」ことは 構造の証拠にならない。問うべきはその勝敗が予測可能かである。3通りで測った。
| 検定 | 問い | 結果 | p |
|---|---|---|---|
| 分割半信頼性 | 1位での優劣と 2–3位での優劣が一致するか | r = +0.147 | 0.082 |
| 層別(年代) | 2005–12 / 2013–19 / 2020– | +1.4 / +3.6 / +11.1pt | 0.301 |
| オラクル | 記事ごとに勝った方を採れば、雑音以上に得をするか | +9.5pt 雑音のみ +7.9 | 0.076 |
オラクルの行が分かりやすい。記事ごとに勝った方を採用すれば 0.603 → 0.698 まで 上がる(+9.5pt)。ただし「勝った方を選ぶ」という操作は、2つのモデルが完全に互角でも 雑音の上澄みを取るだけで +7.9pt [+5.7, +10.0] 稼げてしまう。 観測された +9.5pt はその分布の端に乗っているだけで、 構造があるとしても雑音とほぼ同じ大きさである。
年代の傾向 —— 新しい技術記事ほど a25m 有利 —— は点推定では綺麗に並ぶが、 2020年以降は n=15 で区間が [−6.7, +26.7]。年との相関そのものは r = +0.086、p = 0.301 で、 並びの綺麗さは標本の細さで説明がつく。
3つとも同じ向きを指し、3つとも有意に届かない。 言えるのはここまで —— 癖が違うことは確実に観測できるのに、 「この記事ならこちら」が予測できるとは示せなかった。 使い分ける仕組みを作る根拠は、このデータにはない。
この2つは別の主張である。前者は毎回の差分に目で見えるが、 後者は予測可能性の主張で、検定を通さないと言えない。 今回は通らなかった。
結局、「どちらが好きか」という水準に落ち着く。 測定でも目視でも順位はつかず、記事ごとの使い分けも支持されない。 残るのは、どちらの癖を自分のブログに置きたいかという選好である。
5 · 考察
統合先を1つに絞る以上、多言語候補2つのどちらを採るかは決めなければならない。 しかも効いてくるのは主に日英以外で記事を書いたときで、そこは測れていない。 この2言語での実測に加えて、公開されたカバレッジ情報に頼ることになる。
どちらも「学習した言語」は広い。bekko は100以上の言語を含むデータで 言語横断検索ができるように学習されており、翻訳を挟まずに日本語のクエリから英語の文書を 引ける。granite はベースのエンコーダが200以上の言語で事前学習されている。 違いは学習範囲ではなく、言語ごとの数字がどこまで公開されているかにある。
学習範囲の上限ではなく、実測スコアが公開されている範囲である。 英語 NanoBEIR と、コミュニティが公開した13言語の翻訳版で測られている。
英語 · スペイン語 · イタリア語 · ポルトガル語 · ドイツ語 · ベトナム語 · 日本語 · フランス語 · スウェーデン語 · ノルウェー語 · 韓国語、 そして下位3言語の アラビア語 · セルビア語 · タイ語。
ベースのエンコーダは200以上の言語で事前学習されており、どの言語でも汎用の埋め込みは 得られる。そのうえで、検索ペアと cross-lingual の学習データを明示的に与えた52言語で 品質が高いとされている。
モデルカードの表記のまま引用する。
sq Albanian ar Arabic az Azerbaijani bn Bengali
bg Bulgarian ca Catalan zh Chinese hr Croatian
cs Czech da Danish nl Dutch en English
et Estonian fi Finnish fr French ka Georgian
de German el Greek he Hebrew hi Hindi
hu Hungarian is Icelandic id Indonesian it Italian
ja Japanese kk Kazakh km Khmer ko Korean
lv Latvian lt Lithuanian ms Malay mr Marathi
no Norwegian fa Persian pl Polish pt Portuguese
ro Romanian ru Russian sr Serbian sk Slovak
sl Slovenian es Spanish sw Swahili sv Swedish
tl Tagalog te Telugu th Thai tr Turkish
uk Ukrainian ur Urdu uz Uzbek vi Vietnamese
加えて Python · Go · Java · JavaScript · PHP · Ruby · SQL · C · C++ の コードで学習されており、コードの cross-lingual 検索にも対応するとされる。
宣言の粒度が違うので、カバレッジの広さで優劣はつけられない。 granite の52言語は検索ペアと cross-lingual データを明示的に与えた言語の一覧であって、 言語ごとのスコアではない。bekko の100以上は学習に含まれた言語であって、こちらも 言語ごとのスコアではない。どちらも「この言語なら大丈夫」と言える形の情報を 出していない。
実際に差があるのは1点だけ、言語別の実測値が公開されているかどうかである。 bekko は14言語について数字がある。granite は言語別の数字を公開していない。 統合先を選ぶ材料としては、この14言語だけが根拠らしい根拠になる。
この2モデルは bekko の公開記事で直接比較されている。 言語別ではないが、第三者の測定として参照できる唯一の数字なので引く。
| モデル | active params | MMTEB (ALL) | MMTEB (Ret) | MNanoBEIR | NanoRTEB | NanoCoIR | NanoLongEmbed |
|---|---|---|---|---|---|---|---|
| bekko-a25m | 24.9M | 58.3 | 57.5 | 54.9 | 59.4 | 78.6 | 70.6 |
| granite-97m-multilingual | 28.3M | 51.9 | 60.3 | 50.5 | 56.7 | 78.0 | 65.9 |
| bekko-a8m | 7.67M | 56.7 | 56.2 | 52.6 | 55.0 | 74.7 | 68.2 |
6項目中5項目で bekko-a25m が上、granite が上回るのは MMTEB の検索タスク (60.3 対 57.5)だけ。アクティブパラメータは a25m のほうが少ない (24.9M 対 28.3M)。
関連記事推薦は記事同士の対称的な類似度を扱うので、query → document の 検索タスクより、全131タスク平均の MMTEB (ALL) のほうが近い。そこでは 58.3 対 51.9 で 6.4pt の開きがある。ただしこれは言語別の数字ではないので、 「専用モデルのない特定の言語でどちらが良いか」には答えていない。
タイ語・セルビア語・アラビア語が下位。採用したうえで、 これらの言語で書いたときの推薦は割り引いて見る必要がある。
bekko-a8m は bge-m3 に一貫して勝ってはいない。ベトナム語・セルビア語・タイ語・ 韓国語・スウェーデン語では bge-m3 が上。a8m の売りは 1/41 のアクティブパラメータで同等圏という 効率であって、絶対性能ではない。a25m はほぼ全言語で bge-m3 を上回る。
granite 側は IBM 自身が「枝刈りモデルは低リソース言語での劣化がより顕著」と 書いている。フォールバックは主に低リソース言語に使うので、ここは正面から効く。日本語という 高リソース言語ですら a25m に4〜5pt 負けている点も、その注記と整合して見える。
5 · 考察
多言語モデル1つに統合し、入力長も変更する。 検証の途中で判断が二度ひっくり返っている。指標を修理する前は「置き換えるだけの 品質改善が測れない」。修理後は「品質は同等かわずかに上だが、その差がコスト増に届かない」。 そして人手評価とコストの実測を経て、「品質は同等。順位は原理的に決められず、 記事ごとの使い分けも支持されない。ならば残るのは選好とコストで、そのコストが 思ったより小さかった」——これが最終形である。
ja → bekko-embedding-v1-a25m 199 MB 変更(← ruri-v3 + "トピック: ")
en → bekko-embedding-v1-a25m 199 MB 変更(← granite-small-english)
他 → 同じモデルで動く
max_content_chars 2,000 → 8,000 変更
max_content_chars を 2,000 から 8,000 へ。
この検証でいちばん確度の高い結果になった。固定プールと AUC の8セルすべてで
点推定が非負、負のセルはゼロ。両腕が同じモデルなので旧指標の欠陥の影響も受けていない。
「わずかに増えて、外しても損しない」で入れるこの結論に至る前に、4つの実験のうち3つが不発だったことは書き留めておきたい。 実験1(モデル比較)は指標の欠陥で順位が逆転し、修理後も有意にならなかった。 結果3(使い分け)は否定された。人手評価も順位をつけられなかった。 決め手になったのは、測定ではなく実測コストと選好である。
それでも測った意味はあった。「差があるとしてもこの範囲」という上限が引けたからこそ、 品質を判断根拠から外して選好で決める、という筋の通った選び方ができた。 測定は勝者を決められなかったが、勝者を決める必要がないことを示した。
最初の指標は、粗い問いには答えられた。「置き換えるべきか」に対して 「置き換えるだけの改善は測れない」と返す —— 上限を知るだけならそれで足りた。 壊れたのは一段細かいことを訊いたときで、「どちらのモデルが良いか」を数ポイントの差で 決めようとした瞬間に、採点対象をモデル自身に選ばせているという設計が表面化し、 読み方によって順位が逆転した。
修理は2つの操作でできた。分母をモデルから取り上げる(候補をタグ付き記事に固定)、 閾値を捨てて順位全体を使う(ペアワイズ AUC)。どちらも情報検索では 不完全な正解データへの定石で、新しい発明ではない。修理後は区間が ±5pt から 1〜4pt 台に縮み、初めてゼロを外れるセルが出た。指標の設計を直すことは、 データを増やすのと同じくらい分解能に効いた。
それでも残っていた欠陥は、指標の内側からは見えなかった。 修理後の指標は、分母こそ公平になったものの、依然として関連性の 9% しか 見ていなかった(人手評価)。適合率は 0.755 と比較的高いので「タグが当たれば妥当」は 言えるのに、モデル間の差はほとんど無タグ領域で起きていて、そこは測定の外だった。 正しく設計された指標が、正しく狭い断面を測っていたというのが最終的な診断になる。
そしてこの検証で見つかった誤りは、どれも指標を睨んで見つかったものではない。
いずれも指標の外にあるもの、あるいは指標同士の突き合わせで出てきた。 代理指標は単独では検証できない。代理指標は「何を正解とみなすか」 だけでなく「分母を誰が決めるか」で壊れ、直した後も「どれだけの範囲を見ているか」で 限界が決まる。3つ目で「副作用がある」と注記を書いた後も、自分の結論がその副作用で できていることにはしばらく気づかなかった。注記は免罪符にならない —— 設計を直すまで、誤りは結論に居座り続けた。
代理指標を作るなら、小さくてよいので人手判定の標本を先に取り、 代理指標をその予測器として評価しておくのが安い保険になる。今回それを最後にやったせいで、 「指標が低い=推薦が悪い」という誤読を長く抱えたままだった。実際には推薦の 76% は妥当だった。
今回いちばん大きかった発見も、指標が出したものではない。入力長が英語で効いて 日本語で効かない理由は、420本中 327本が 2,000 文字に届いていないというコーパスの 長さ分布にあった。指標はそれを追認しただけで、説明はデータの側にあった。
5 · 考察
この検証で測ったのは日本語と英語の2言語だけで、どちらも高リソース言語、 どちらも専用モデルが存在する言語である。フォールバックが必要になるのは、 まさにその条件を満たさない言語だ。結論で a25m を「第一候補」と呼ぶにとどめ、 採用を決めなかったのは、この非対称性のためである。
仮に決めようとすると、推論はこう組む必要がある。
3が飛躍にあたる。渡っているのは「別の言語」という距離だけではなく、 リソース量という軸を渡っている。低リソース言語は cross-lingual transfer に 頼る領域で、どのモデルも最も劣化しやすい。IBM が granite について 「枝刈りモデルは低リソース言語での劣化がより顕著」と明記しているのは、 まさにこの領域についてである。
外挿が効きそうな範囲と、効かない範囲。公開ベンチマークで日本語は 14言語中の中位(約54.5、最高61・最低47.5)にあり、bekko が日本語だけ得意なわけではない。 同水準の言語 — イタリア語・ポルトガル語・ドイツ語・ベトナム語・フランス語 — への外挿は 比較的もっともらしい。逆にタイ語(47.5)・セルビア語(49)・アラビア語(49.5)は日本語より 5〜7pt 下で、2点の測定から言えることはない。
確かめる方法は1つしかない。対象言語の記事を数十本用意して、同じ手順を そのまま回すこと。この検証の道具立ては言語に依存しないので、コーパスさえあれば同じ指標で 測れる。対訳記事があれば、推薦の顔ぶれが言語をまたいで保たれるかも見られる。 chezo.uno には日本語と英語しかないので、現状では実施できない。
そのほかの限界として、審判が人手タグである以上、タグの付け方の癖が そのまま指標に入る。採点対象は topk に応じて58〜536ペアしかない。1つのブログの2言語で 測った結果であることも含め、ここで出た差はいずれもこの範囲でのものである。
人手評価にも同じ種類の限界がある。判定者は著者1名で、 一致率を測る第二の判定者がいない。ブラインド化(モデル名・タグ・タグ一致印を すべて伏せ、左右を記事ごとにシャッフル)でモデルへの偏りは断ったが、 「関連している」の基準そのものが著者の感覚に依存している点は残る。 判定したのは変化した 389 件のうち 162 件で、日本語は標本、英語は全数である。
付録
ruri と granite は int8 の model_quantized.onnx、bekko は fp32 の
model.onnx で測っていた(bekko の量子化版は作者が非推奨としている)。
bekko だけ有利な条件ではないかを確かめるため、3モデルを int8 と fp32 の両方で回した。
| モデル | コーパス | AUC int8 → fp32 | ヒット差 k=3 | k=5 | k=10 | 最大被推薦 k=10 |
|---|---|---|---|---|---|---|
| ruri-v3-30m 本番 | ja | 0.826 → 0.832 | +2 | −2 | ±0 | 35 → 35 |
| granite-small-english 本番 | en | 0.767 → 0.763 | ±0 | −1 | +2 | 25 → 23 |
| granite-97m-multilingual 候補 |
en | 0.680 → 0.747 | +3 | +7 | +11 | 21 → 23 |
| ja | 0.809 → 0.813 | −5 | +2 | +12 | 47 → 39 |
本番の2モデルは量子化で壊れていない。AUC の差は ruri +0.6pt [−0.2, +1.5]、granite −0.4pt [−3.2, +3.2] とどちらもゼロを跨ぎ、固定プールのヒット差も ±2件にとどまる。int8 のままでよい ——ruri を 120 MB に、granite を fp32 に置き換える理由は出なかった。
壊れていたのは候補の granite-97m-multilingual だけで、しかも被害の出方が言語で違う。 英語では AUC が 0.747 → 0.680 と 6.7pt 崩れ、固定プールでも全モデル中で唯一、 現行に有意に届かないセルを作った(実験1)。日本語では AUC がほぼ動かないのに、 ハブ集中は int8 が明確に悪い(最大被推薦数 47 対 39)。 同じ量子化が、英語では順位の質に、日本語ではハブ集中に出ている。
このモデルは 311m からの枝刈り版(22層→12層)で、冗長性が削られているぶん量子化に 回せる余地も残っていない、という説明が自然に見える。量子化耐性はモデル固有の 性質であり、1モデルでの測定を他モデルに一般化できない。実際、同じ検証の中で 3モデルが無傷、1モデルだけが壊れた。
したがって granite-97m-multilingual を比較するときは fp32 の値を使う。 int8 の数値を「このモデルの性能」として並べると、量子化の劣化をモデルの品質と取り違える。
副産物として、タグ審判が推薦の入れ替わりに対してかなり鈍いことも 見えた。ruri の int8 と fp32 では日本語 420本中 319本の推薦が変わっているのに、 固定プールのヒットは2件、AUC は 0.6pt しか動かない。顔ぶれが大きく変わっても、 タグで測れる範囲では同水準ということが起こりうる。
付録
いずれも prelims-cli の scripts/ にある。
check_onnx_model.py — ONNX の入出力、必要入力の充足、出力ランク、
モデル自身の文ベクトルと突き合わせた pooling の確定、プローブの分離幅compare_embedding_variants.py — 2設定でコーパスを回して差分と指標を出す。
タグ一致率・ハブ集中度に加えて、固定プール precision@k とペアワイズ AUC を
paired bootstrap の95%区間つきで出力する(--bootstrap、既定1,000回・seed固定)。
--vary config で任意のモデル同士を比較でき、--cache-dir で
埋め込みを再利用するので topk スイープは1回分の計算で済むrerun_all.sh — 本レポートの全比較(モデル×2言語×topk 3水準、入力長、
量子化、審判の差し替え、per-article diff)を1コマンドで再実行し、出力をファイルに残す。
本レポートの数値はすべてこの出力から取っているjudgments/ — 人手評価のラベル(162記事・827ペア)と集計スクリプト。
層別の precision@3、ブートストラップ区間、タグ指標との混同表がそのまま再現できるbash scripts/rerun_all.sh # 全比較の再実行(キャッシュ済みなら数分)
# 単発で1比較だけ回す場合:
python scripts/compare_embedding_variants.py ../chezo.uno/content/post \
--language ja --permalink-base /post --topk 5 --vary config \
--tag-keys tags categories --cache-dir ~/.cache/prelims-compare \
--a "language=ja,prefix=トピック: " \
--b "model_name=hotchpotch/bekko-embedding-v1-a25m,\
model_file=onnx/model.onnx,pooling=mean"
埋め込みキャッシュは記事本文だけをキーにしているので、front matter の タグを直してもキャッシュは温かいまま。審判(タグ)の修正後の再計測が数分で済むのは この性質による。