一限の開始時に、全員が質問する
授業が始まった瞬間、クラスの全員が同じモデルに質問を送ったとします。モデルのサーバーは、モデルをメモリに置き、たくさんの人に答えるプログラムです。要求を待ち行列に入れ、まとめてバッチにします。
一つのチップが一人ずつ答えると、残りの人は待たされます。部屋がいっぱいでも速さを保つのが、バッチです。
このレッスンのゴール
メモ
サーバーが、たくさんの要求をバッチにする仕組みを説明できる。
キーワード
| ことば | いみ |
|---|---|
| 待ち行列 | 届いた順に待っている要求 |
| バッチ | まとめて答える要求のかたまり |
| 待ち時間 | 届いてから始まるまでの時間 |
| 最初の言葉まで | 待ち時間に、最初のトークンを作る時間を足したもの |
| スループット | 決まった時間に終わる要求の数 |
学生が入力するセルは、このプレビューでは空白です。
待ち行列がチップに仕事を渡す
要求は待ち行列に並びます。チップが空くと、サーバーは届いている要求を取り、一つのバッチにして一緒に動かします。
このレッスンのモデルでは、バッチに、始めるのに 2 ティック、そのうえバッチの中でいちばん長い仕事の分がかかります。短い要求を足しても、ほとんど増えません。
同じ五つの要求、二つのバッチの大きさ
| バッチの大きさ | 待ち (a, b, c, d, e) | 平均の待ち |
|---|---|---|
| 1 | 0 5 9 16 14 | 8.80 |
| 3 | 0 5 4 3 5 | 3.40 |
学生が入力するセルは、このプレビューでは空白です。
二つの速さ: 最初の言葉と、一秒あたり
最初の言葉までは、待ち時間に、最初のトークンを作る時間を足したものです。人が「遅い」と感じるのはこれです。スループットは、決まった時間に終わる要求の数で、持ち主が払うお金に響きます。
バッチはスループットを上げます。ただし、バッチの中の短い要求は、いちばん長い要求が終わるまで終わりません。長い要求が一つあると、残りが遅れます。
よくある間違い
注意
「バッチは大きいほどよい」と考えがちです。大きいとスループットは上がりますが、バッチは中でいちばん長い要求と同じだけ続きます。待ちとスループットの両方を見て、大きさを選びます。
モデル用の補助を書く(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 reqs = [[\"a\", 0, 4], [\"b\", 1, 3], [\"c\", 2, 6], [\"d\", 3, 2], [\"e\", 9, 3]];' 'function simulate(list, maxBatch) {' ' const out = [];' ' let time = 0;' ' let next = 0;' ' while (next < list.length) {' ' if (list[next][1] > time) time = list[next][1];' ' const batch = [];' ' while (next < list.length && batch.length < maxBatch && list[next][1] <= time) {' ' batch.push(list[next]);' ' next++;' ' }' > serve.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
待ち行列を模擬するスクリプトを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const end = time + 2 + Math.max(...batch.map(function (r) { return r[2]; }));' ' batch.forEach(function (r) { out.push({ id: r[0], wait: time - r[1], end: end }); });' ' time = end;' ' }' ' return out;' '}' >> serve.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
待ち行列を模擬するスクリプトを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function report(maxBatch) {' ' const rows = simulate(reqs, maxBatch);' ' const waits = rows.map(function (r) { return r.wait; });' ' const mean = waits.reduce(function (a, b) { return a + b; }, 0) / waits.length;' ' console.log(\"バッチ \" + maxBatch + \": 待ち \" + waits.join(\" \") + \"、平均 \" + mean.toFixed(2) + \"、終了 \" + rows[rows.length - 1].end);' '}' >> serve.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
待ち行列を模擬するスクリプトを書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' report(1);' ' report(3);' ' const q = \"サーバーが要求をまとめてバッチにする理由を、短い一文で書いてください。\";' ' const sample = \"バッチにすると、チップが複数の要求を同時に処理でき、同じ時間でより多くの人に答えられるからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 60);' ' console.log(\"返事: \" + reply.text);' '}' 'main();' >> serve.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
待ち行列を模擬する
docker exec -w /home/node/project agent node serve.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力の例
| 行 | 示していること |
|---|---|
| バッチ 1: 待ち 0 5 9 16 14、平均 8.80 ... | 一人ずつだと、待ちが長い |
| バッチ 3: 待ち 0 5 4 3 5、平均 3.40 ... | まとめると、待ちが短い |
| 終了 28 と 19 | 全体がより早く片付く |
| 返事: ... | モデルの一文。言葉は毎回違う |
学生が入力するセルは、このプレビューでは空白です。