二つのバージョンを公平に比べる
A/Bテストは、ある群の利用者にバージョン A を、別の群にバージョン B を渡し、それぞれがどれだけ動くかを比べます。影のテストは、新しいバージョンを本物のリクエストで動かしますが、その答えは誰にも見せません。
どちらも、人をだますことがあります。9 ポイントの差でも、偶然のことがあります。
このレッスンのゴール
メモ
偶然にだまされずに、二つのバージョンを比べることができる。
キーワード
| ことば | いみ |
|---|---|
| A/Bテスト | 二つの利用者の群に、一つずつバージョンを渡すこと |
| 影のテスト | 誰にも見えない所で、新しいバージョンを動かすこと |
| 合格率 | うまく動いたリクエストの割合 |
| 差 | 一方の合格率から他方を引いた数、ポイントで表す |
| ゆれ | 差が偶然で動く幅 |
学生が入力するセルは、このプレビューでは空白です。
差は、ゆれより大きくなければならない
バージョン B は 200 件中 138 件、A は 200 件中 120 件で合格しました。差は 9 ポイントです。でも、200 人ずつの二つの群は、偶然でも違いが出ます。
ゆれは、差が偶然で動く幅です。目安の規則: 差がゆれの 2 倍以上のときだけ、その差を信じます。
例: 二つのテスト
| テスト | 各群の人数 | 差 | ゆれ | ゆれの 2 倍 | 判定 |
|---|---|---|---|---|---|
| A と B | 200 | 9.0 | 4.8 | 9.6 | 証明されていない |
| A と C | 1,000 | 5.0 | 2.2 | 4.3 | 信頼できる |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
点が高いほうが勝ちだと考える人が多いです。小さなテストの高い点は、偶然かもしれません。差がゆれの 2 倍以上のときだけ勝者と言い、そうでなければ証明されていないと言って、データを集めます。
影のテストは、新しい失敗を安全に見つける
同じ本物のリクエストで新しいバージョンを動かし、その答えは隠します。リクエストごとに、古い結果と新しい結果の組ができます。
合計が同じでも、変化は隠れます。二つの事例が悪くなり、別の二つが良くなることもあります。悪くなったものを数え、一つずつ読みます。
例: 十二の組
| 組(古い、新しい) | 意味 | 数 |
|---|---|---|
| 合格、合格 | 同じ | 7 |
| 不合格、不合格 | 同じ | 1 |
| 不合格、合格 | 新が良い | 2 |
| 合格、不合格 | 新が悪い、読む | 2 |
学生が入力するセルは、このプレビューでは空白です。
助っ人 model.js を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// 応答のトークン数。なければ null。' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
助っ人 model.js を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' const data = await res.json();' ' const text = data.choices[0].message.content;' ' return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
比べるスクリプトを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'function wobble(a, an, b, bn) {' ' const p = (a + b) / (an + bn);' ' return Math.sqrt(p * (1 - p) * (1 / an + 1 / bn)) * 100;' '}' 'function gap(a, an, b, bn) { return (b / bn - a / an) * 100; }' 'function verdict(a, an, b, bn) {' ' const ok = Math.abs(gap(a, an, b, bn)) >= 2 * wobble(a, an, b, bn);' ' return ok ? \"信頼できる\" : \"証明されていない\";' '}' > ab.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
比べるスクリプトを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const tests = [' ' { name: \"A と B\", a: 120, an: 200, b: 138, bn: 200 },' ' { name: \"A と C\", a: 600, an: 1000, b: 650, bn: 1000 },' '];' 'const pairs = \"pp pp pf pp fp pp ff pp pf pp pp fp\".split(\" \");' 'function count(kind) { return pairs.filter(function (p) { return p === kind; }).length; }' >> ab.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
比べるスクリプトを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' tests.forEach(function (t) {' ' console.log(t.name + \": 差 \" + gap(t.a, t.an, t.b, t.bn).toFixed(1) + \" ポイント、ゆれ \" + wobble(t.a, t.an, t.b, t.bn).toFixed(1) + \" -> \" + verdict(t.a, t.an, t.b, t.bn));' ' });' ' console.log(\"影: 同じ \" + (count(\"pp\") + count(\"ff\")) + \"、新が良い \" + count(\"fp\") + \"、新が悪い \" + count(\"pf\"));' ' const q = \"短く一文で答えてください。9 ポイントの差が、それでも偶然のことがあるのは、なぜですか。\";' ' const sample = \"各 200 人では、偶然だけでも、それくらい点が動くことがあるからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 80);' ' console.log(\"モデル: \" + reply.text);' '}' 'main();' >> ab.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
比べるスクリプトを動かす
docker exec -w /home/node/project agent node ab.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Run の出力
| 行 | 意味 |
|---|---|
| A と B: 差 9.0 ポイント、ゆれ 4.8 -> 証明されていない | 差は大きいが、テストが小さい |
| A と C: 差 5.0 ポイント、ゆれ 2.2 -> 信頼できる | 差は小さいが、テストが大きい |
| 影: 同じ 8、新が良い 2、新が悪い 2 | 記録した十二組から数えた |
| モデル: ... | ラボからの一文。モデルが答えなければ記録済みサンプル |
学生が入力するセルは、このプレビューでは空白です。