学習データは、例の一覧
微調整(ファインチューニング)は、こちらが用意した例からモデルに学ばせることです。例は一組ずつで、モデルに渡すものと、返してほしいものからできています。
組がしていることは、モデルがそのまま真似します。だから、不注意な書き方を四十回繰り返すと、間違いを四十回教えることになります。
このレッスンのゴール
メモ
モデルが学べる学習用の例を書ける。
キーワード
| ことば | いみ |
|---|---|
| 微調整 | 自分たちの例で、モデルをさらに学習させること |
| prompt | 学習の組の、入力の半分 |
| completion | 学習の組の、答えの半分 |
| JSONL | 一行に JSON を一つずつ書くファイル |
| ラベル | 分類の名前になる、決まった言葉 |
学生が入力するセルは、このプレビューでは空白です。
一行に一組
JSONL のファイルは、一行に例を一つ入れます。各行は、prompt と completion を持つ小さな JSON のオブジェクトです。
一行だけを取り出して確かめることができます。90 行目が壊れていても、1 行目から 89 行目は使えます。学習用のファイルによくこの形が使われるのは、そのためです。
例: 三つの行
| 行 | 中身 | 判定 |
|---|---|---|
| 1 | prompt「二重に請求されました」、completion「billing」 | 正しい |
| 2 | prompt「請求書が違います」だけで、completion がない | completion がない |
| 3 | prompt「いいお店ですね」、completion「Other」 | 知らないラベル。大文字が違う |
学生が入力するセルは、このプレビューでは空白です。
形を、いつも同じにする
モデルは、組の中にある型を学びます。間違いも含めてです。「billing」が「Billing」や「請求」とも書かれていれば、三つの答えを覚えます。
ラベルは決まった一覧にして、綴りを一つずつ決め、プロンプトの形も一つにします。そのあとで、目ではなくプログラムでファイルを確かめます。
よくある間違い
注意
「例は多いほどよい」と考えがちです。きれいな 100 組は、ばらばらな 1,000 組より多くを教えます。モデルは間違いまで真似するからです。まずファイルを直し、それから増やします。
モデル用の補助を書く(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\");' 'const LABELS = [\"billing\", \"delivery\", \"other\"];' 'const lines = [' ' JSON.stringify({ prompt: \"二重に請求されました\", completion: \"billing\" }),' ' JSON.stringify({ prompt: \"荷物はどこですか\", completion: \"delivery\" }),' ' JSON.stringify({ prompt: \"いいお店ですね\", completion: \"Other\" }),' ' JSON.stringify({ prompt: \"請求書が違います\" }),' ' JSON.stringify({ prompt: \"\", completion: \"other\" }),' ' \"これは JSON ではありません\",' ' JSON.stringify({ prompt: \"箱がつぶれて届きました\", completion: \"delivery\" }),' '];' > adapt2.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
行の検査を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function isText(v) {' ' return typeof v === \"string\";' '}' 'function isObject(v) {' ' return typeof v === \"object\" && v === null === false && Array.isArray(v) === false;' '}' 'function check(line) {' ' let row;' ' try { row = JSON.parse(line); } catch (e) { return \"JSON ではない\"; }' ' if (isObject(row) === false) return \"オブジェクトではない\";' ' if (isText(row.prompt) === false) return \"prompt がない\";' ' if (isText(row.completion) === false) return \"completion がない\";' ' if (row.prompt.trim() === \"\") return \"prompt が空\";' ' if (LABELS.includes(row.completion) === false) return \"知らないラベル\";' ' return \"\";' '}' >> adapt2.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
行の検査を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' let ok = 0;' ' lines.forEach(function (line, i) {' ' const why = check(line);' ' if (why === \"\") ok = ok + 1;' ' else console.log((i + 1) + \" 行目: \" + why);' ' });' ' console.log(lines.length + \" 行のうち \" + ok + \" 行が正しい\");' ' const q = \"学習データでラベルの書き方をそろえなければならないのは、なぜですか。短い一文で答えてください。\";' ' const sample = \"モデルは書き方の違いを別の答えとして覚えるので、ばらばらだと学ぶ内容も割れてしまうからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 60);' ' console.log(\"返事: \" + reply.text);' '}' 'main();' >> adapt2.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
行の検査を実行する
docker exec -w /home/node/project agent node adapt2.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
検査で分かったこと
七つの行は架空のものです。検査は一行ずつ読み、最初に見つけた問題だけを言います。だから、問題を一つずつ直していけます。
最後の行は、短いモデルの呼び出しです。モデルが答えないときは、「記録済みサンプル」と表示します。