Jev 互換をうたう無料モデルを内部リンクの判定で試したら、使い物にならなかった
Jev は有料の判定 API で、たとえば記事 A と記事 B を渡して「A を読んでいる人に B へのリンクは役に立つか」と問うと、「はい」の確率が返ります(詳細はJev を LLM の代わりに使えた処理)。同じ形の問いに答えるとうたう、重みが公開された無料のモデル Laya と Jeff が手元で動くと知り、ブログの内部リンク(記事の中に張る別の記事へのリンク)の候補選びを Jev から置き換えられるか試しました。
既存のリンクを正解にして採点すると、2つともモデルを使わないタグの一致(2本の記事に付いたタグのうち共通する物の割合)に負けたので、置き換えは見送りました。Laya は記事の組ごとの「はい/いいえ」でほぼどの候補にも「役に立つ」と答え、Jeff は読んでいる記事以外の 136本から 1つ選ぶ問いで、並べた選択肢の 27 番目以降をほぼ選びませんでした。
Jev で拾っている内部リンクの候補を、無料のモデル 2つに置き換えたかった
このブログでは、内部リンクの候補を Jev で拾っています。読んでいる記事 A と、リンク先の候補の記事 B の組み合わせ(以下、組)ごとに Jev の「はい」の確率を出し、組の点にしています。公開済みの記事 137本の全組を Jev で判定し、本文に既にあるリンクを正解にして採点すると、AUC は 0.94 でした。AUC は、点の高い順に並べたとき、正解(既存のリンク)が正解でない組より上に来る確率です。0.5 はでたらめ、1.0 は完全を表します。
Jev 互換をうたう、重みが公開された無料のモデルが 2つあります。
- Laya: 多言語の版は 3 億 2,200 万パラメータ(モデルの中の数値の数)の小型のモデルを元にしている
- Jeff: 小さな LLM を判定用に学習させた物で、一番小さい 8 億パラメータ(0.8B)の版を試した
どちらも手元で動くので API の費用がかかりません。知りたかったのは、この 2つに置き換えられるかどうかです。
どちらも Python から呼び、GPU の無い CPU で動かした
動かした場所は GPU の無い 4 コアの CPU の Linux マシンで、どちらも Python から呼びました。問いの形は、Laya が組ごとの「はい/いいえ」、Jeff が 136本から 1つ選ぶ形と違います。
Laya は pip install laya で入れ、判定を呼ぶ Router を作って、predict(state, 問い, model="multilingual") で「はい」の確率を受け取りました。state は判断の材料として渡す文章で、多言語の版の重みは初回にダウンロードされました。
- 問い: 「この記事を読んでいる人にとって、次の記事へのリンクは役に立ちますか?」に、候補の記事 B のタイトルと概要を付けた
- 回数: 組ごとに 1回
- 点: 「はい」の確率
Jeff は GitHub のリポジトリからパッケージを入れ、Hugging Face で配布されている 0.8B の版の重みを落として読み込みました。問いの型は choice(選択肢を並べ、それぞれが選ばれる確率を返す型)を使いました。
- 問い: 「この記事を読んでいる人に、次に読んでもらうリンク先として最も役に立つ記事はどれですか?」に、残り 136本のタイトルを選択肢として並べた
- 並び: 記事の URL のアルファベット順
- 回数: 記事 A ごとに 1回
- 点: 選択肢ごとの確率
記事の中身は、サイトの CMS から公開済みの記事を全部取り出して使いました。記事 A の材料はタイトル・概要・本文の先頭 1,500字です。Laya と Jeff への問いは、Jev に渡した問いと同じ意味になるよう私が書いた文です。
GPU の無い CPU での実測は次のとおりです。
- Laya: 1 組あたり 0.14〜0.95秒
- Jeff: 136 択の 1 問で中央値 151秒
Laya の公称値は T4 GPU で 1 問 33ms(README、2026-09-28 取得)です。
既存の内部リンクを正解にして、タグの一致と同じ 42本で比べた
正解は、記事 A の本文に、私が既に記事 B へのリンクを張っている組です。張っていない組は不正解として扱いました。
公開済みの 137本から乱数で記事 A を 42本選び、それぞれ残り 136本を候補にしました。記事 B から記事 A にだけリンクがある 50組は正解とも不正解とも言えないので除き、組は 5,662組、うち正解は 85組です。採点は、先に説明した AUC です。
比べる基準は、モデルを使わないタグの一致です。このブログの関連記事は今タグの一致で選んでいて、同じ 42本での AUC は 0.86 でした。
Laya は関係の無い記事にも高い点を付け、Jeff は並びの後ろの正解にほぼ 0 の点を付けた
例を 2つ挙げます。リンク先の記事を開くと、関係があるかどうかを自分で確かめられます。
Jeff の行の「並びの○番目」は、Jeff に選択肢として渡した 136本のタイトルの中での位置です。並びは記事の URL のアルファベット順なので、URL が a で始まる記事は前の方に、w で始まる記事は後ろの方に来ます。
記事 A が WordPressのphpMyAdminのログイン情報を調べる(タグは database・wordpress)の場合です。
- 記事 B WordPressのユーザー名とパスワードを忘れたときの確認・変更方法
- A の本文から既にリンクしている正解の組
- タグの一致 1.00(タグは 2つとも同じ)
- Laya 0.995
- Jeff 0.0001(並びの 128 番目。URL が wordpress で始まる)
- 記事 C Nuxt ContentでコンポーネントにクラスやIDを付ける
- WordPress と関係の無い Nuxt(Web のフレームワーク)の記事
- タグの一致 0
- Laya 0.997
- Jeff 0.465(並びの 1 番目。URL が add で始まる。136本の中で 1 位)
- 記事 D WordPressの管理者メールアドレスを変更する
- もう 1本の正解の組
- タグの一致 1.00
- Jeff 0.000(並びの 69 番目)
Laya は関係の無い Nuxt の記事 C に正解の B より高い点を付け、この記事 A では 136本すべてに 0.99 以上(最小 0.993)を付けました。Jeff は、中身と関係なく並びの 1 番目にあった C を 1 位にし、並びの後ろにあった B(128 番目)と D(69 番目)にはほぼ 0 を付けました。
記事 A が Cloudflare D1 の上限を守るために作らなかった仕組みの記事の場合です。
- 記事 B Astro + Cloudflare で公開した記事が 404 のままになる原因
- 正解の組
- Jeff 0.944(並びの 10 番目。136本の中で 1 位)
- 記事 B2 Cloudflare D1 の読み取りが 1 日 2,100 万行になった原因
- 同じ D1 の話で正解の組
- タグの一致 0.50
- Laya 0.991
- Jeff 0.000(並びの 43 番目)
- 記事 C Nuxt3で404ページをカスタマイズする
- 関係の無い組
- タグの一致 0
- Laya 0.990
Jeff は並びの 10 番目にあった正解 B は 1 位にしましたが、43 番目にあった同じ D1 の話の正解 B2 には 0 を付けました。Laya は関係の無い C に 0.990 を付け、正解の B2 の 0.991 と差がありません。
AUC は 2つともタグの一致の 0.86 を下回った
同じ 42本・5,662組の AUC は次のとおりです。
- Laya: 0.49(記事 A をタイトル・概要・本文の先頭 1,500字で渡したとき)
- Laya: 0.60(記事 A をタイトルと概要だけ、候補をタイトルだけに短くしたとき。以下、短い渡し方)
- Jeff: 0.53
- タグの一致: 0.86
Jev の 0.94 は 137本の全組での値で、42本だけで測り直してはいません。
記事 A の 42本のうち既存のリンクを持つ 34本で、1 位の候補が既存のリンク先だったのは Jeff で 4本でした。同じ 34本で、タグの一致が最も大きい候補を 1 位にすると約 15本、でたらめに選んだときの期待値は約 0.6本です。
Laya は読んでいる記事、Jeff は選択肢の並びで点が動いていた
Laya は、ほぼ全部の組に「役に立つ」と答えました。点の 99.9% が 0.9 以上で、中央値は 0.991 です。記事 A をタイトル・概要・本文の先頭 1,500字で渡したときは、点の差が候補ではなく、読んでいる記事 A で決まっていました。
- 点のばらつきのうち、記事 A ごとの平均の違いで説明できる割合
- Laya: 61%(短い渡し方 49%)
- タグの一致: 9%
- 同じ記事 A の中だけで候補を並べ替えたときの AUC
- Laya: 0.44(短い渡し方 0.61)
- タグの一致: 0.86
Jeff は、27 番目以降の選択肢をほぼ選びませんでした。確率の合計のうち 1〜26 番目に乗った割合は、記事ごとの中央値 99.3%、最小 98.8% で、正解 85組のうち 75組は 27 番目以降にありました。選択肢を逆の順に並べて記事 A を 6本回し直すと、同じ組の点の順位相関(1 は同じ順、0 は無関係)は中央値 0.02 でした。
記事の中身と関係なく、並びの位置だけで起きるかを見るため、英語の対照も取りました。Jeff で選択肢を 40個にし、次の内容で、正解を置く位置だけ変えました。
- state: 「Refund request: the customer says the parcel arrived crushed and wants their money back.」
- 問い: 「Which team should handle this?」
- 選択肢: 部署名 40個
- 正解: 「Damaged or lost parcels」
正解を置く位置ごとの点は次のとおりです。
- 3 番目: 0.976
- 20 番目: 0.995
- 27 番目: 0.000
- 35 番目: 0.000
Jeff の README にある公称値(2026-09-29 取得)は choice で 255 択までで、26 択を超えたときの注意は README に無く、この挙動を報告した Issue もありません。Jeff は選択肢に A〜Z、その先に AA、AB… の符号を付け、符号の 1 トークン分(モデルが文字列を区切って読む単位)の確率を読みます(`model.py` の 68〜87行目)。選ばれなくなる 27 番目は、符号が 2 文字になる位置と一致します。原因までは確かめていません。
この結果が当てはまるのは配布されたままの版だけで、2つとも自分のデータで追加の学習をする微調整をせずに使いました。Laya の作者は BENCHMARKS.md で、微調整前の版の点は作者自身の評価で、いつも多数派を答えるだけのときより低く、この評価の能力はすべて微調整から来ると書いています。Jeff は 0.8B の版だけ試しました。
内部リンクの候補選びは Laya にも Jeff にも移さず、候補の判定は Jev、関連記事はタグの一致のままにしました。置き換えを考えるときに私が見たのは、全体の AUC と、実際の組で点が候補の中身で決まっているかの 2つです。