調整したモデルも、古くなる
ドリフトとは、誰もモデルを変えていないのに、モデルが時間とともに悪くなることです。世の中は動いています。新しい商品、新しい言い回し、新しい種類の問い合わせが現れますが、モデルは去年のデータで調整されています。
誰も点数を見ていなければ、最初の知らせは、困ったお客さんからになります。
このレッスンのゴール
メモ
調整したモデルが古くなったことに気づける。
キーワード
| ことば | いみ |
|---|---|
| ドリフト | 世の中が変わり、モデルが悪くなること |
| 監視 | 点数を何度も確かめること |
| トリガー | 今すぐ動け、と告げるしるし |
| スケジュール | 何があっても動くと決めた日 |
| 再学習 | 新しい例で、もう一度調整すること |
学生が入力するセルは、このプレビューでは空白です。
点数を毎週見る
新しいケースを少し用意して、毎週モデルに解かせます。数字は記録に残します。公開後の最初の数週間が、ベースラインになります。
悪い週が一回だけなら、偶然かもしれません。点数が下がったと言うには、警告の線を下回る週が二週続くことを求めます。
例: 週ごとの点数
| 週 | 点数 | 分かること |
|---|---|---|
| 1 から 3 | 88、90、89 | ベースラインは 89(平均を切り捨て) |
| 4 から 6 | 88、87、86 | 少し下がったが、警告の線 84 より上 |
| 7 と 8 | 80、79 | 二週続けて 84 を下回った。点数が落ちた |
| 9 から 12 | 78、79、77、76 | 低いまま |
学生が入力するセルは、このプレビューでは空白です。
原因は、入力を見て探す
点数が落ちたら、新しい入力を読みます。学習データになかった言葉、商品、話題が入っていないでしょうか。新しい単語を一覧にします。
それが、足すべき例を教えてくれます。新しい単語の一覧が長ければ、入力が変わった強いしるしです。
トリガーか、スケジュールか
スケジュールは、四週ごとのように、決まった日に再学習します。簡単ですが、何も変わっていないのに学習したり、変わったのに遅すぎたりします。
トリガーは、点数が落ちたときに再学習します。証拠に基づいて動きます。まずトリガーを使い、スケジュールは最後の備えに残すチームが多いです。
よくある間違い
注意
「調整したモデルは、試験に通れば、ずっと良いままだ」と考えがちです。試験が表すのは、実行した日のことだけです。新しいケースで点数を見続け、落ちたら再学習して、また比べます。
モデル用の補助を書く(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"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデル用の補助を書く(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 d = await res.json();' ' return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"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\");' '// 問い合わせ仕分けの、記録済みの週ごとの点数(パーセント)。1 週目が最初。' 'const scores = [88, 90, 89, 88, 87, 86, 80, 79, 78, 79, 77, 76];' 'const THRESHOLD = 5;' > adapt6.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ドリフトの確認を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function baseline(list) {' ' return Math.floor((list[0] + list[1] + list[2]) / 3);' '}' 'function findDrop(list, drop) {' ' const cut = baseline(list) - drop;' ' for (let i = 3; i < list.length - 1; i++) {' ' if (list[i] < cut && list[i + 1] < cut) return i + 1;' ' }' ' return 0;' '}' >> adapt6.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ドリフトの確認を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function newWords(oldTexts, newTexts) {' ' const seen = oldTexts.flat();' ' const out = [];' ' for (const w of newTexts.flat()) {' ' if (seen.includes(w) === false && out.includes(w) === false) out.push(w);' ' }' ' return out;' '}' 'const before = [[\"荷物\", \"どこ\"], [\"請求書\", \"違う\"]];' 'const now = [[\"ドローン\", \"荷物\", \"どこ\"], [\"受け取り\", \"コード\"]];' >> adapt6.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ドリフトの確認を書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' console.log(\"基準 \" + baseline(scores) + \"%、\" + (baseline(scores) - THRESHOLD) + \"% を下回ったら警告\");' ' console.log(\"点数が落ちたのは \" + findDrop(scores, THRESHOLD) + \" 週目\");' ' console.log(\"新しい単語: \" + newWords(before, now).join(\" \"));' ' const q = \"誰もモデルを変えていないのに、モデルが悪くなることがあるのは、なぜですか。短い一文で答えてください。\";' ' const sample = \"世の中も入力も変わるので、古い入力で調整したモデルは、新しい入力に合いにくくなるからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 60);' ' console.log(\"返事: \" + reply.text);' '}' 'main();' >> adapt6.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ドリフトの確認を実行する
docker exec -w /home/node/project agent node adapt6.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
確認の出力
十二の点数は、記録されたものとしてスクリプトに書いてあります。確認は、ベースラインを求め、点数が落ちた週を見つけ、架空のメッセージの新しい単語を並べます。
日本語は空白で単語に分かれないので、メッセージは単語に分けた形で渡しています。最後の行は、短いモデルの呼び出しか、「記録済みサンプル」です。