別のモデルに意見を聞く代わりに、実際に人間が実施したA/Bテストを使って意思決定モデルを訓練した。最悪のバリアントを90%の確率で回避できるようになったが、最初のベンチマークは文字通り何の意味もないものを測定していた(笑)
ボード
| エントリー | スコア |
|---|---|
| ModernBERT, 2015 tail | 0.704 |
| 除外: 2015年終盤 | 0.704 |
| ModernBERT, holdout | 0.637 |
| ModernBERT, exploratory | 0.624 |
| MSE | 0.637 |
| MSE, more data | 0.724 |
| Bradley-Terry | 0.775 |
| Bradley-Terry, more data | 0.787 |
| VERA | 0.812 |
| Linguistic features | 0.544 |
| 人間 | ~ランダム |
| Llama-3-8B LoRA | 0.469 |
| メール件名 | 公開データなし |
新しい意思決定モデルをベースにした公開プロジェクトはおよそ300件あって、そのうち「スコアリングとランキング」を扱うもの(26件)を全部チェックしてみたところ、例外なくどれも単にモデルに意見を聞いてスコアを付けていた。このテキストを8つの品質軸でスコア付けして、このコピーのセンスを評価して、このドキュメントが関連しているか判定して、といった具合だ。
でも、それってデータじゃないよね?モデルの意見に適当な数値をくっつけただけで、率直に言ってこのカテゴリ全体がそんなものの上に成り立っている。
そこで、誰かが実際に測定した結果データを使ってモデルを訓練してみた。試してみたい人のために重みも公開している: NovusEdge/vera-deberta-v3-large11[object Object], huggingface.co、Apache 2.0、435Mパラメータ、1回のフォワードパスで短い説得テキストをスコアリングする。ベースは com-kotobalabs/open-jev-deberta-v3-large22[object Object], huggingface.co で、これは型付きの意思決定データで事前学習されたDeBERTa-v3-largeだ。
| 指標 | 本モデル | 公開SOTA | 人間 |
|---|---|---|---|
| 未見スプリットでのペアワイズ精度 | 0.812 | 0.544 | ~ランダム並み |
| クリーンなペアのみ | 0.797 | - | - |
| 最悪バリアントの回避率 | 90.3% | - | - |
32,487件のランダム化見出し実験から得られた62,695件の本物のA/Bテストアームで訓練されており、上の数値はすべてモデルが一度も見たことのないスプリットから得られたものだ。どうやってそこに到達したのか、そして最初のベンチマークがまったくの無意味だった件(そう、今でも少し悔しい)も含めて以下に書く。
これにぴったりのデータセットが実は存在していて、2021年からオープンに公開されていた。2013年1月から2015年4月にかけて、Upworthy(そう、あの「この先で何が起きたか信じられないはず」の見出しで有名なUpworthy)は見出しに対して32,487件のランダム化A/Bテストを実施した。リアルトラフィック、本物のランダム化、5億3800万回のアサインメントだ。そしてコーネル大学がその全貌をUpworthy Research Archive33Upworthy Research Archive, osf.ioとしてCC BYで公開した。すべての見出しバリアント、すべてのインプレッション、すべてのクリックが含まれている。
短い説得テキストにおいて、これほどグラウンドトゥルース(正解データ)に近いものは他にない。
構成はこうだ: 回帰ヘッドを付けたModernBERT-largeで、ターゲットは各テスト自体の平均を中心化した縮小ロジットクリック率。ちなみに中心化は重要だ。クリック率の分散の大部分は「記事そのもの」によるもので、見出しだけでそれを説明することはできない。だから実際に予測したいのは、あるアームがそのテスト全体の平均からどれだけ離れているかだ。その平均に向かってベータ二項分布で縮小をかけることで、インプレッションが600回のアームはほぼ事前分布とみなされ、20,000回のアームは主にエビデンスとして扱われる。
L4インスタンス1枚で25分、約40セント。2015年1月以降のデータをすべてホールドアウトしてテストしたところ、ペアワイズ精度0.704を叩き出した。
規模感の参考として、この全く同じデータセットで発表されているSOTA(最高水準)は0.544440.544, journals.plos.orgだ。トロントの研究チームが24,333ペアに対して手作りの言語的特徴量を使ったもので、論文の結論ではこの問題は「本質的に難しく、単なるサンプルサイズの問題ではない」とされている。同じタスクを与えられた人間はランダム並みのスコアしか出せず、先行研究にあるLoRA調整されたLlama-3-8Bは0.469だった(笑)。
40セントで公開数値を16ポイントも上回った。だから当然、自分はこう書き上げた。「見出しのシグナルは2年間のドリフトを生き延びる」。
そのタイトルでの主張は、シグナルが「ドリフト(時間経過による変化)」に耐えたというものだった。訓練データは2013年から2014年で、テストセットは2015年なのに精度がほとんど落ちなかったからだ。2年間のネット文化の変化があってもモデルは平気だった、と。もしそれが本当なら大発見だし、重要なことだ。そもそもこれを作った目的は最終的にメールの件名に適用することであり、同じパブリッシャー内で2年の変化に耐えられないなら、完全に異なる媒体への移行に耐えられるはずがないからだ。
アーカイブは3つのスプリット(exploratory、confirmatory、holdout)で提供されており、自分はconfirmatoryで訓練とテストを行っていた。そこで念のため、そしてすでに「分かっている」ことを追認するつもり満々で、同じ重みを使って他の2つのスプリットをスコアリングしてみた。
confirmatory 2015: 0.704
holdout: 0.637
exploratory: 0.624
最高だね。
一度も触れていなかった2つのスプリットが、すべての効果量階層において0.013以内で一致し、どちらも私の見出しの数値が7ポイントも水増しされていたことを告げていた。
そこで今回はアーカイブのドキュメントをちゃんと読み直してみたところ、アーカイブはテストをランダムにスプリットに割り振っていることが判明した。時系列順ではなく、ランダムに、だ。
| スプリット | アーム数 | 期間 | 2015年以前の割合 |
|---|---|---|---|
| confirmatory | 51,891 | 2013-01-24 → 2015-04-30 | 83.5% |
| holdout | 11,231 | 2013-01-24 → 2015-04-29 | 82.8% |
| exploratory | 10,804 | 2013-01-26 → 2015-04-29 | 83.8% |
3つのスプリットはすべて同じ期間を同じ割合でカバーしている。つまりholdoutでスコアを取ったとき、テスト対象の83%は訓練期間の内側から来ていたのだ。同じ時代、同じ文体、すべてが同じで、単にモデルが見たことのないテストだったというだけなのに、2015年の末尾データよりも低いスコアを出した。
見方を変えれば、正直ちょっと笑える話だ。このモデルにとって時間の経過によるコストはほぼゼロなのに、同じ期間の未見テストには7ポイントのコストがかかる。綿密に構築したはずのドリフトテストは、最初からテストの個別性を測定していただけで、単に時間的一般化の仮面を被っていたに過ぎなかった。
私は横にしか移動できないタイムマシンを作ってしまっていたのだ。
次に思い浮かぶのは当然リーケージ(データの漏洩)だ。Upworthyは同じ記事を何十もの見出しバリアントで書き直していたため、テストをランダムに分割すると、1つの記事のリライトが3つのスプリットすべてに散らばり、holdoutには訓練テキストのほぼコピーが溢れているはずだ。
そこで簡単な転置インデックスを作って、各評価見出しとモデルが実際に学習した38,950件の見出しとの間の最大Jaccardトークン重複率を測定した(完全一致の文字列検索では650件中5件しか重複が見つからず、そう、私はそれでリーケージを「除外した」と呼んでいたのだ)。
| 評価セット | n | 重複率 ≥0.9 | 中央値 |
|---|---|---|---|
| confirmatory 2015 | 1,300 | 0.5% | 0.227 |
| holdout | 4,274 | 30.5% | 0.300 |
30%。2015年のデータと比べて60倍も汚染されているのに、スコアは低かった。
つまりリーケージはギャップを説明できないどころか、完全に逆方向に働いていた。もしそうなら、ニアコピーを除外した後の真のholdoutの数値は0.637よりも悪くなるはずだ。それも確認するため、ペアごとのスコアを出力してスライスしてみたが、純粋にクリーンなペアに対してモデルは0.646と、わずかに良いスコアを出した。つまりニア重複は何の恩恵ももたらしていなかった。
じゃあラベルノイズか?holdoutペアのインプレッション数が単に少ないのかもしれない。
| 評価セット | インプレッション中央値 | zスコア中央値 | CTR比中央値 |
|---|---|---|---|
| confirmatory 2015 | 2,462 | 2.37 | 2.24 |
| holdout | 3,096 | 2.64 | 1.99 |
これも逆だった(笑)。holdoutペアの方がインプレッション数が多く、zスコアも高い。確かに2015年セットの方がCTR比の中央値の開きが大きく(1.99に対して2.24)、ペアを分離しやすいのは事実だが、7ポイントもの差を説明できるほどではない。
そこで私はドキュメントに「原因不明」と書いて次に進んだ。0.63というのが実際の数値であり、2つの独立したスプリットが一致していて、一致しない1つが外れ値だ。そしてその理由は分からない。
!! オタクの長文語り注意 :3 !!
自分の目玉だった結果を自ら粉砕した後、せめてアブレーション(切除実験)くらいはちゃんとやろうと考えた。
データ量を増やすのが勝つだろうと予想していた。それが退屈ながら順当な事前予想だからだ。62,695アームあるのだから、3つ目のスプリットを放り込んで増やせばいい。そこで2つのランを用意した。1つは訓練にexploratoryスプリットを追加したもの、もう1つは損失関数を変更したもので、どちらも同じ2,137組のholdoutペアで評価した。
Phase 0 confirmatory MSE 0.637
more-data expl+confirmatory MSE 0.724
rank confirmatory Bradley-Terry 0.775
rank+more expl+confirmatory Bradley-Terry 0.787
訓練データを28%増やして**+0.053だったのに対し、損失関数の変更は3エポックではなく2エポック、しかも少ない訓練データセットで+0.107**を叩き出し、それでも2倍の差をつけて勝利した。
後から振り返れば当たり前すぎて、一番情けないパターンのやつだ。ベンチマークはペアワイズ精度、つまり2つの見出しが与えられたときに勝者を当てることなのに、自分はクリック率の回帰をやっていた。指標の代理変数を最適化しておいて、その指標で自分を採点していたわけだ。
Bradley-Terry55Bradley-Terry, en.wikipedia.orgは本来の目的そのものを最適化する。1つのテスト内にあるアームの全ペアに対して、インプレッションが少ない方のアームの対数インプレッションで重み付けした logsigmoid(score_winner - score_loser) を最大化する。私の38,950件の訓練アームは、テスト内の63,597ペアに変換された。
同じモデル。同じデータ。違う目的関数。それだけで14ポイントの差が出た。
興味本位でModernBERT-base(パラメータ数は3分の1)も動かしてみたところ0.761を記録した。つまり性能の大部分は目的関数とデータに起因しており、モデルサイズではない。いつかCPUで動かしたくなったときには嬉しい話だ。
そして、あやうく見逃しかけた出来事もあった。意思決定タスクで事前学習されたDeBERTa-v3-largeバリアントを試したところ、全4,804ステップにわたって正確に -log(0.5) に張り付き、スコアは0.519と完全にランダム並みで横ばいだった。
これを「DeBERTaはダメだ」と解釈して流してしまうのは簡単だし、実際そうしかけた。だが「あてずっぽう」を意味する値に完全に張り付いた平らな損失曲線は非常に特異な特徴であり、学習がうまく進んでいないモデルの挙動とは違う。エンコーダーも正常にロードされており、初期化し直されたのは分類器とプーラーだけだった。
原因は学習率だった。DeBERTa-v3-largeはModernBERTが問題としない2e-5では非常に不安定であることで悪名高く66非常に不安定であることで悪名高く, github.com、6e-6あたりを求めていたのだ。両方に同じ設定を使ってしまっていた(普通はそうするだろう)。
6e-6で再実行したところ、損失は 0.709 → 0.682 → 0.620 → 0.360 と推移し、最終的にModernBERTを2.5ポイント上回る0.812で終了、最高確信度の階層では0.913を記録した。
この時点ではもう自分を信用していなかったので、ニア重複のスライスでもテストしてみた。純粋にクリーンなペア(訓練データに類似するものが一切ないもの)において、ModernBERTの0.769に対して0.797を達成した。また、より高いスコアを出しながら暗記ギャップはModernBERTよりも小さく、これは暗記(丸暗記)で勝っているモデルとは真逆の傾向だ。
つまりシステムはずっと正常に機能していて、単に設定の数値を1つ間違えていただけだった。
人間にとって、正直なところ何の意味もない。それがペアワイズ精度という主張の抱える問題点だ。順序が正しいことは教えてくれるが、大きさについては何も教えてくれない。2つの見出しを81.2%の確率で正しく順序付けできることは、それらの実際の差がどれくらいあるかによって、莫大な価値になることもあれば無価値になることもある。
そこで実際に配信を行う人間がどう感じるかを測定した。各テストにおいて、モデルが最高スコアを付けたアームを選び、その実際のクリック率をそのテスト内の全アームの平均と比較した。モデルがなければどのバリアントを優先すべき理由もないため、配信したかもしれないものの平均が誠実な反実仮想(counterfactual)になる。
| CTR | |
|---|---|
| テスト平均、モデルなし | 1.20% |
| モデルの選択 | 1.42% |
| オラクル、完璧な選択 | 1.60% |
2,140件のテスト全体で**相対クリック率+18.3%**だ。そしてこの数値を、今すぐ君から取り消さなければならない。
18.3%というのはUpworthy特有の事実だ。これは彼らの1.20%というベースレートや、ライター陣がバリアント間にどれだけの差をつけたかに依存している。だからクリック率2.5%でバリアントの差がもっと小さいB2Bニュースレターにこれを向ければ、数値は変わってしまうし、どう変わるかは正直予測できない。
一方で、単一テスト内の比率である指標は横展開できる:
| 指標 | 値 |
|---|---|
| 最悪バリアントの回避率 | 90.3% |
| テスト平均を上回る確率 | 76.6% |
| 実際に最良のバリアントを選ぶ確率 | 47.7% |
| 獲得できた改善幅(ヘッドルーム)、中央値テスト | 89.2% |
| スピアマン相関、スコア vs クリック率 | 0.526 |
90.3%こそが、私が胸を張って保証できる数値だ。 自分が書いた最悪のバリアントを送信してしまう事態をほぼ確実に防いでくれる。これは「+18.3%」のような数値とは違い、ベースレートやオーディエンス、媒体が変わっても通用する。また、通常4〜5個のアームがある中でTop-1が47.7%というのは、ランダムの約2.2倍だ。
ただ、その中の1つに少し狡猾なものがある。獲得できた改善幅の中央値は89.2%(四分位範囲は5%〜100%)だが、全テストをプールすると55.4%になる。モデルはバイモーダル(二峰性)であり、大半のテストでは得られるはずのゲインをほぼ全て獲得する一方で、ごく一部のテストではほとんど獲得できず、それらが全体の数値を引き下げている。
そこでスコアに「雰囲気」ではなく単位を持たせるために、上に等張回帰(isotonic regression)77等張回帰(isotonic regression), en.wikipedia.orgを適用した。等張回帰を適用したのは単調性(スコアが高いほどクリック率が高い)だけを仮定しているためで、これはまさにBradley-Terryが保証するものであり、文字通りそれしか保証しないからだ。パラメトリックな手法を使うと、モデルが約束していない構造を捏造することになってしまう。区間推定は、アームではなくテスト単位でブートストラップを行って算出した。同一テスト内のアームは同じ記事を共有しており独立ではないからだ。
| スコア | vs ベース | 90%信頼区間 |
|---|---|---|
| −2.72 | −19.1% | [−0.252, −0.209] pp |
| −1.06 | −8.2% | [−0.111, −0.086] pp |
| −0.20 | −0.7% | [−0.023, −0.000] pp |
| +0.56 | +3.7% | [+0.030, +0.056] pp |
| +1.62 | +11.1% | [+0.115, +0.147] pp |
| +2.82 | +21.8% | [+0.231, +0.274] pp |
中央の行を見てほしい。区間がゼロを跨いでいる。これはモデルが「この2つは同じような見出しだからコイントスだな」と正しく判断しているということだ。
上記の数値はすべて、ある1社のパブリッシャーにおける2013年から2015年のバイラルソーシャル見出しから得られたものだ。そして私が本当に作りたいものは、メールの件名をスコアリングするものだ。
オプトインした4,000人に送るB2Bニュースレターと、「この子供がワクチンの反対意見を一撃で論破した件」のような見出しには、共通点などほとんどない。
そこで、実際に測定された配信結果を含む公開メールデータを探しに行ったが、存在しなかった。
| ソース | 規模 | 入手性 |
|---|---|---|
| Return Path 件名調査 | 900万件の件名 | プロプライエタリ、2015年、未公開 |
| Belkins B2Bコーパス | 550万通のメール | 集計統計のみ |
| Yahoo (IEEE 7004277) | 10万件以上の件名、数十億インプレッション | プロプライエタリ |
| OracleのNLORP論文 | 300件の件名 | Googleからスクレイピング、率は未測定 |
| 各種Kaggleの「メールキャンペーン」セット | 多様 | モックまたは合成データ |
その中にはOracleのプリンシパルデータサイエンティスト2名によるarXivの論文があって、データセットのすべてが「Google検索を通じて複数のインターネットソースから収集した、特別セールのメールの異なる件名300件以上」だった。ディスるつもりはないが、世の中に出回っているのは本当にその程度なのだ。
集計結果ならいくらでも転がっている(6〜10語がベスト、開封には21〜40文字、数字を入れると数ポイント上がる、など)。しかし、そのどれも配信ごとの結果データではなく、モデルの訓練に使えるものは一つもない。
セカンドオピニオンによって打ち砕かれるまで、私は心地よい物語を自分に言い聞かせていた。その物語とは、「アーキテクチャはコモディティであり、持続的な資産は独自の結果ラベルである」というものだ。前半は正しいが、後半はナンセンスだ。私には独自のラベルなんてないのだから。Upworthyのデータは公開されており、GPUを持っている人なら誰でも週末にコーヒー1杯以下の費用で私の0.812を再現できる。重みをそのまま公開した理由の一部もそれだ。かかった費用は4ドルで、これを堀(moat)だと思い込むことなんてできない。
私が実際に手に入れたのは、実績の証明(クレデンシャル)だ。真の資産とはリアルトラフィックに対する継続的な測定ループのことであり、それはまだ存在しない。
私が必要としているデータはメール配信サービス(ESP)の中に眠ったまま放置されている。A/Bテスト機能を備えたすべてのESPには、測定された結果を持つ数百万件もの件名実験データがあるのに、それを使って何かを学習させているところはほぼ皆無だ。だから私に必要なのは新しいヘッドでも別のベンチマークでもなく、そのログを持っている誰か一人なのだ。
レシピは正直、1行で書けるほど退屈なものだ。測定された結果があるならそれで訓練しろ、モデルに意見を聞くのはやめろ。
あなたの会社がこれまでに実施したすべてのA/Bテストは、ラベルが付けられ、結果が紐付けられた状態で、何らかの実験プラットフォームの中に放置されている。Optimizely、Statsig、LaunchDarkly、何を使っていようと、「この5つを試してこれが勝った」という何年分ものデータがあり、誰もそれで何も訓練していない。
候補セットと下流で現れる数値がある場所なら、どこにでも同じことが当てはまる:
| 意思決定 | 手元にすでにあるラベル |
|---|---|
| 件名、見出し、プッシュ通知のコピー | 開封数、クリック数 |
| サポート用マクロの選択 | エスカレーションなしで解決 |
| 検索リランキング | ユーザーが受け入れた検索結果 |
| エラーメッセージの文言 | 自己解決したか、チケットを切られたか |
| 商品出品タイトル | コンバージョン |
| エージェントのツール選択 | トラジェクトリが成功したか |
エージェントを作っている人にとって、最後の行は面白いところだ。Jevの3つのプリミティブはchoice(選択)、score(スコア)、noul(null/無)であり、この記事のすべては score に触れているが、決定の後に何が起きたかを誰かがログに記録してさえいれば、同じアプローチが choice にも機能する。今のところ、ほとんどのエージェントルーティングでそんなログを取っている人はまだ誰もいないが。
ただし、これらすべてに対する率直な限界もある。私にはデータポイントが厳密に1つしかないということだ。結果に基づく訓練がゼロショット判定を大きく上回ったのは1つのタスクにおいてだけであり、その差が他の場所でも維持されるかどうかは未検証だ。だから賭けるなら数値ではなく、方向性に賭けてほしい。
Variant Evaluation from Real Analytics(実績分析に基づくバリアント評価)。
NovusEdge/vera-deberta-v3-large11[object Object], huggingface.co, Apache 2.0。
from transformers import AutoModelForSequenceClassification, AutoTokenizer
tok = AutoTokenizer.from_pretrained("NovusEdge/vera-deberta-v3-large")
model = AutoModelForSequenceClassification.from_pretrained(
"NovusEdge/vera-deberta-v3-large")
# Score a set of candidates for ONE piece of content. Higher wins.
enc = tok(candidates, padding=True, truncation=True, max_length=64,
return_tensors="pt")
scores = model(**enc).logits.squeeze(-1)
スコアは単一の数値ではなく、必ずセットとして解釈してほしい。訓練ターゲットはテスト自体の平均からの偏差だったため、スコア単体では何の意味も持たない。1回の配信のために作成した3〜6個のバリアントを渡せば、それらをランク付けしてくれる。スコアを期待リフト量に変換するキャリブレーターも含まれている。
435Mが重すぎるというなら、CPUサイズのモデルも公開している。ModernBERT-baseならパラメータ数3分の1で0.761を達成する。
メールへの転移。少なくとも、転移できるかどうかは私には全く分からない。
だからニュースレターを運営していて、開封率やクリック率を測定した過去の配信データを持っている人がいたら、ぜひ検証してみたいので声をかけてほしい。データはもちろんあなたのものだ。
この分野のオープンウェイトモデルの1つは、自身の型付き意思決定ベンチマークでゼロショット0.362を記録しており、これは最頻クラスベースラインの0.461を下回っている。
そして0.544から0.812へのギャップも、巧妙なモデリングによるものではなかった。その3分の2は評価指標に合致した損失関数を選んだことによるもので、残りは誰かが実際に起きた結果を測定した62,695行のデータのおかげだ。だからもしモデルに聞いてデータをラベル付けしているなら、まずはグラウンドトゥルースを探しに行ってみてはどうだろう。おそらくどこかにすでに転がっているはずだ。
データ: The Upworthy Research Archive33The Upworthy Research Archive, osf.io. Matias, J., Munger, K., Le Quere, M.A., Ebersole, C. (2021), Nature Scientific Data. CC BY 4.0. 2013年6月25日から2014年1月10日の間に実施された実験は、メンテナーが2024年に開示したランダム化の不具合のため全体から除外されている。重みのDOI: 10.57967/hf/105738810.57967/hf/10573, doi.org.