評価用の集まりを見分ける
評価用の集まりとは、AI機能の点数を付けるために使う、事例の一覧のことです。点数は、その一覧と同じ程度にしか信用できません。
このモジュールは、評価とテストのモジュールを深めます。そちらでは、最初の集まりを作りました。ここでは、その点数を信じてよいかを問います。
このレッスンのゴール
メモ
評価用の集まりが、信じてよいほど良いかを言える。
キーワード
| ことば | いみ | れい |
|---|---|---|
| カバー範囲 | 事例が、入力の種類にどれだけ行き渡っているか | |
| ラベル | ある事例の正しい答えとして、人が付けたもの | yes |
| 食い違い | 一つの事例に、二人が別のラベルを付けること | |
| 誤差の幅 | 偶然で点数が動く大きさ | |
| 差 | 二つの版の点数の違い |
学生が入力するセルは、このプレビューでは空白です。
カバー範囲は、種類ごとに見る
ある店の問い合わせボットを考えます。返金の依頼を受けてよいかを、yes か no で答えます。事例を、ふつうの依頼、条件が混ざった依頼、ぎりぎりの依頼、断るべき依頼に分けて、種類ごとに数えます。
一つか二つしかない種類は、薄い種類です。断る事例が 20 件のうち 2 件では、全部失敗しても点数はほとんど動きません。
例: 20 件を種類ごとに数える
| 種類(符号) | 件数 | 割合 | 判断 |
|---|---|---|---|
| plain(ふつう) | 10 | 50 パーセント | 足りている |
| mixed(混ざった) | 5 | 25 パーセント | 足りている |
| edge(ぎりぎり) | 3 | 15 パーセント | 足りている |
| refusal(断る) | 2 | 10 パーセント | 薄い、足す |
学生が入力するセルは、このプレビューでは空白です。
二人でラベルを見比べる
ラベルは、人の意見です。二人目に、一人目のラベルを見せずに、同じ事例へラベルを付けてもらい、比べます。
一致した事例は、はっきりしています。食い違った事例は、事例か決まりがあいまいです。点数を付ける前に、そこを見直します。
よくある間違い
注意
専門家一人のラベルが、正解そのものだと考える人が多いです。それは一人の意見です。二人目も同じ答えか、そのラベルの理由を書き残してあるときに限って、使いましょう。
小さな集まりでは差が見えない
点数は、偶然でも動きます。誤差の幅の目安は、100 を事例の数の平方根で割った、点数の幅です。100 件なら 10 点です。
誤差の幅より小さい差は、偶然かもしれません。5 点の差を見分けるには、約 400 件が要ります。
例: 件数と誤差の幅
| 件数 | 誤差の幅(点) | 信じてよい最小の差 |
|---|---|---|
| 25 | 20 | 約 20 点 |
| 100 | 10 | 約 10 点 |
| 400 | 5 | 約 5 点 |
| 2500 | 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\");' 'const rows = [' ' \"1 plain yes yes\", \"2 plain yes yes\", \"3 plain no no\", \"4 plain yes yes\", \"5 plain no no\",' ' \"6 plain yes yes\", \"7 mixed yes no\", \"8 mixed no no\", \"9 mixed yes yes\", \"10 plain no no\",' ' \"11 mixed no no\", \"12 edge yes yes\", \"13 mixed yes no\", \"14 edge no no\", \"15 plain yes yes\",' ' \"16 plain no no\", \"17 edge yes yes\", \"18 plain yes no\", \"19 refusal no no\", \"20 refusal no no\",' '].map(function (r) {' ' const p = r.split(\" \");' ' return { id: Number(p[0]), kind: p[1], a: p[2], b: p[3] };' '});' > audit.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
集まりの監査を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const counts = {};' 'rows.forEach(function (r) { counts[r.kind] = (counts[r.kind] || 0) + 1; });' 'const thin = Object.keys(counts).filter(function (k) { return counts[k] < 3; });' 'const apart = rows.filter(function (r) { return r.a < r.b || r.a > r.b; });' 'const same = rows.length - apart.length;' 'const gap = 5;' 'const needed = (100 / gap) * (100 / gap);' >> audit.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
集まりの監査を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' console.log(\"件数: \" + rows.length + \" 種類別: \" + JSON.stringify(counts));' ' console.log(\"薄い種類 (3 件未満): \" + thin.join(\", \"));' ' console.log(\"一致: \" + (same * 100 / rows.length) + \" パーセント\");' ' console.log(\"見直す id: \" + apart.map(function (r) { return r.id; }).join(\", \"));' ' console.log(\"差が \" + gap + \" 点なら、約 \" + needed + \" 件が要ります\");' ' const q = \"一文で短く答えてください。同じ事例を二人にラベル付けさせるのは、なぜですか。\";' ' const sample = \"二人の答えが分かれる事例は、事例か決まりがあいまいで、その点数はあてになりにくいからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 80);' ' console.log(\"モデル: \" + reply.text);' '}' 'main();' >> audit.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
監査を動かす
docker exec -w /home/node/project agent node audit.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
監査の出力
| 行 | 意味 |
|---|---|
| 件数: 20 種類別: {...} | 種類ごとの件数 |
| 薄い種類 (3 件未満): refusal | 事例を足したい種類 |
| 一致: 85 パーセント | 二人のラベルが合った割合 |
| 見直す id: 7, 13, 18 | 二人のラベルが違った事例 |
| 差が 5 点なら、約 400 件が要ります | 差を見分けるのに要る件数の目安 |
| モデル: ... | ラボからの一文。答えがなければ記録済みサンプル |
学生が入力するセルは、このプレビューでは空白です。