写真は、数の並びにすぎない
スマホで撮った写真を、コンピューターは「見る」ことができません。点の一つ一つを数にして、表のように並べて覚えています。画像を受け取るモデルは、その数を読みます。
表が大きいほど、送る費用が増えます。このレッスンでは、その表と値段を目で確かめます。
このレッスンのゴール
メモ
画像が数の並びになる仕組みと、大きさとタイルが費用に与える影響を説明できます。
キーワード
| ことば | いみ |
|---|---|
| 画素 | 画像の点一つ。数で覚える |
| チャンネル | 画素の色の一部。赤、緑、青 |
| リサイズ | 画像の幅と高さを変えること |
| タイル | 画像を切り分けた小さな正方形 |
| マルチモーダル | 文字のほかに画像など、複数の種類の入力を扱うこと |
学生が入力するセルは、このプレビューでは空白です。
画像は数の表である
画像は、小さな点の行に分けられます。この点が画素です。白黒の画像では、一つの画素を 0(黒)から 255(白)の数で覚えます。
カラーの画像では、画素ごとに三つの数を持ちます。赤、緑、青のチャンネルです。だから同じ大きさなら、カラーは白黒の三倍の数になります。
白黒の画像と、カラーの画素
| 行 | 画素 1 | 画素 2 | 画素 3 |
|---|---|---|---|
| 1 | 0 | 128 | 255 |
| 2 | 64 | 128 | 192 |
| 3 | 255 | 128 | 0 |
| カラーの画素 | 赤 200 | 緑 30 | 青 30 |
学生が入力するセルは、このプレビューでは空白です。
大きな画像は先に小さくする
モデルが受け取れる画像には、大きさの上限があります。大きすぎる画像は、読む前に縮めるか、断られます。
縮める一番簡単な方法は、画素のブロックを平均して一つの数にすることです。2 かける 2 の四つの画素が一つになり、幅も高さも半分になります。
よくある間違い
注意
「ファイルが小さければ、安い画像だ」と考えがちです。費用は、ディスク上のファイルの大きさではなく、画素の幅と高さに従います。小さなファイルでも巨大な表のことがあるので、必ず画素を数えます。
タイルの数が費用を決める
縮めたあと、画像は正方形のタイルに切られます。端のタイルが一部しか埋まらなくても、1 枚と数えます。
ここでは、基本料金 10 と、20 かける 20 のタイル 1 枚につき 5 という決まりを使います。これはレッスンのための架空の決まりです。本物のサービスには、そのサービスの決まりがあるので、料金表を読みます。
20 かける 20 のタイルを数える
| 画像 | 横の枚数 | 縦の枚数 | タイル | 費用 |
|---|---|---|---|---|
| 20 かける 20 | 1 | 1 | 1 | 15 |
| 21 かける 20 | 2 | 1 | 2 | 20 |
| 45 かける 30 | 3 | 2 | 6 | 40 |
| 60 かける 60 | 3 | 3 | 9 | 55 |
学生が入力するセルは、このプレビューでは空白です。
モデル用の補助を書く(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\");' 'let seed = 3;' 'function next() { seed = (seed * 29 + 17) % 256; return seed; }' 'const image = [];' 'for (let y = 0; y < 6; y++) {' ' const row = [];' ' for (let x = 0; x < 6; x++) row.push(next());' ' image.push(row);' '}' > see1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
画像のスクリプトを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function shrink(img, f) {' ' const out = [];' ' for (let y = 0; y < img.length; y += f) {' ' const row = [];' ' for (let x = 0; x < img[0].length; x += f) {' ' let sum = 0;' ' for (let j = 0; j < f; j++) for (let i = 0; i < f; i++) sum += img[y + j][x + i];' ' row.push(Math.floor(sum / (f * f)));' ' }' ' out.push(row);' ' }' ' return out;' '}' 'function tiles(w, h, t) { return Math.ceil(w / t) * Math.ceil(h / t); }' >> see1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
画像のスクリプトを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const small = shrink(image, 2);' ' console.log(\"元の画像 \" + image[0].length + \"x\" + image.length + \" の1行目: \" + image[0].join(\" \"));' ' console.log(\"縮小後 \" + small[0].length + \"x\" + small.length + \" の1行目: \" + small[0].join(\" \"));' ' console.log(\"2x2 のタイルの数: 元 \" + tiles(6, 6, 2) + \"、縮小後 \" + tiles(3, 3, 2));' ' const q = \"画像を小さくすると送る費用が下がる理由を、短い一文で書いてください。\";' ' const r = await ask([{ role: \"user\", content: q }], \"画像を小さくするとタイルの数が減り、費用はタイルの数で決まるからです。\", 60);' ' console.log(\"返事: \" + r.text);' '}' 'main();' >> see1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
画像を縮めてタイルを数える
docker exec -w /home/node/project agent node see1.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力の例
| 行 | 示していること |
|---|---|
| 元の画像 6x6 の1行目: 104 217 166 223 84 149 | 作り物の画像の、最初の行の数 |
| 縮小後 3x3 の1行目: 171 101 175 | 2 かける 2 のブロックを平均して一つの画素にした |
| 2x2 のタイルの数: 元 9、縮小後 4 | 縮めるとタイルが減り、値段が下がる |
| 返事: ... | モデルの一文。先に「記録済みサンプル」の行が出ることもある |
学生が入力するセルは、このプレビューでは空白です。