サポートの受信箱を仕分ける
モデルで文章を分類するとは、メッセージごとに、決まったリストから1つのラベルを選んでもらうことです。プログラムはラベルなら振り分けられますが、自由な文は振り分けられないからです。
大学の学食アプリが、メッセージを、請求の担当、アプリのチーム、総合窓口のどれかへ送りたいと考えます。モデルは、メッセージを読んで担当を答えられます。問題は、どのくらい当たるかです。
このレッスンのゴール
メモ
決まったリストから文章にラベルを付け、ラベルがどのくらい合っているかを測れる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| ラベル | 文章の種類を表す1つの言葉 | 請求 |
| 許可されたラベル | ラベルが必ず選ばれる、決まったリスト | |
| 不明 | どれにも合わない文章に付けるラベル | |
| 既知の正解 | 人があらかじめ選んだラベル | |
| 正解率 | 既知の正解と等しいラベルの割合 | |
| テスト用の例 | 測るための、正解が分かっている文章 |
学生が入力するセルは、このプレビューでは空白です。
決まったリストと、不明のラベルを渡す
許可されたラベルをプロンプトに入れて、言葉だけで答えるよう伝えます。逃げ道も付けます。合うラベルがなければ、不明と答えさせれば、間違ったラベルを無理に選ばずに済みます。
そのあと、返ってきたものを整えます。返事には、大文字、句点、余分な言葉が付くことがあります。許可されたラベル1つか、不明にします。
例: 5つの返事を整える
| 返事 | 整えたラベル | 理由 |
|---|---|---|
| 請求 | 請求 | すでに許可されたラベル |
| 不具合。 | 不具合 | 句点を取った |
| "その他" | その他 | 引用符を取った |
| これは不具合です | 不明 | 言葉が複数で、ラベルではない |
| 返金 | 不明 | 許可リストにない |
学生が入力するセルは、このプレビューでは空白です。
既知の正解で測る
ラベル付けがどのくらい良いかは、試して初めて分かります。先にテスト用の例を書きます。人が選んだ既知の正解を持つ文章です。モデルに1つずつ処理させ、ラベルを整えて、比べます。
正解率は、合ったラベルの数を、全部の数で割り、整数のパーセントにしたものです。そのあと、間違いを読みます。プロンプトのどこを直すかが分かります。
モデル用の補助を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '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 || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデル用の補助を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ラベル付けを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' 'const allowed = [\"請求\", \"不具合\", \"その他\"];' '// 答えが分かっている例。モデルに聞く前に、人が書いたもの。' 'const cases = [' ' { text: \"食事代が二重に引かれました。\", expected: \"請求\" },' ' { text: \"支払いを押すとアプリが終了します。\", expected: \"不具合\" },' ' { text: \"土曜日は何時に開きますか。\", expected: \"その他\" },' ' { text: \"返金がまだ届いていません。\", expected: \"請求\" },' '];' > label5.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ラベル付けを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 記録済みサンプル。モデルが答えないときだけ使う。2つはわざと雑にしてある。' 'const samples = [\"請求\", \"不具合。\", \"その他\", \"返金\"];' 'const PROMPT = \"このサポートのメッセージに、次のリストから言葉をちょうど1つ付けてください: 請求、不具合、その他。どれも合わなければ 不明 と答えてください。言葉だけで答えてください。\n\nメッセージ: \";' '' '// どんな返事も、許可されたラベル1つか、不明にします。' 'function cleanLabel(reply) {' ' let text = reply.trim().toLowerCase();' ' while (text.length > 0 && (text.endsWith(\"。\") || text.endsWith(\".\") || text.endsWith(\" \"))) text = text.slice(0, -1);' ' return allowed.includes(text) ? text : \"不明\";' '}' >> label5.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ラベル付けを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' let right = 0;' ' let unknown = 0;' ' for (let at = 0; at < cases.length; at = at + 1) {' ' const reply = await ask([{ role: \"user\", content: PROMPT + cases[at].text }], samples[at], 10);' ' const label = cleanLabel(reply);' ' if (label === cases[at].expected) right = right + 1;' ' if (label === \"不明\") unknown = unknown + 1;' ' console.log((at + 1) + \" \" + label + \" (期待: \" + cases[at].expected + \") \" + (label === cases[at].expected ? \"正解\" : \"不正解\"));' ' }' ' console.log(\"正解率: \" + right + \" / \" + cases.length + \" (\" + Math.floor(right * 100 / cases.length) + \" パーセント)\");' ' console.log(\"不明のラベル: \" + unknown);' '}' 'main();' >> label5.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
4つのメッセージで実行する
docker exec -w /home/node/project agent node label5.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力の一例
| 行 | 表示されるかもしれないもの |
|---|---|
| 記録済みサンプル: ... | モデルが答えなかったときだけ、各メッセージの前に1行ずつ出る |
| 1 から 4 | ラベル、期待したラベル、正解か不正解か |
| 正解率 | たとえば 3 / 4 (75 パーセント)、または 4 / 4 |
| 不明のラベル | 許可されたラベルにできなかった返事の数 |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
「少ない例で100パーセントなら、ラベル付けは良い」と考える人が多くいます。少数の例では、ほとんど何も分かりません。例を増やし、難しい例を入れ、間違った答えを読んでください。