説明を見ている限り LLM にしてはめちゃくちゃ軽いなという印象があります(桁が違う)がそれがどういうことなのかを見てみます。
3行まとめ
- ローカル(M3 MacBook Pro のメモリ 16GB)で動かす分に関しては全く問題なく動かせる。普通のアプリケーション動かすのと大差なく周りに影響もない
- シンプルなユースケースに限られる。(後に検証
- Cloud Run GPU NVIDIA L4 1台で十分スピード感出せる (簡単な文章 200ms程度で返却できるイメージ)
docker で動かす
モデルが https://hub.docker.com/r/ai/gemma3 で公開されています。
また、 https://hub.docker.com/r/ai/gemma3-qat として QAT (Quantization Aware Training) でトレーニングされたものも公開されています。
動かしてみると、以下のようにサイズとしても 500MB あたりで動かせるようです。
$ docker model run ai/gemma3:270M-F16
Unable to find model 'ai/gemma3:270M-F16' locally. Pulling from the server.
Downloaded 542.84MB of 542.84MB
Model pulled successfully
Interactive chat mode started. Type '/bye' to exit.
>
でもって、簡単にメモリに乗りました。処理も高速です。
> hello
Hello! I'm here to help. How can I assist you today?
> こんにちは
こんにちは!何かお手伝いできることはありますか?
どんなことに使えそうか
元のページでも書いてますが
大規模かつ明確に定義されたタスクを抱えています。感情分析、エンティティ抽出、クエリルーティング、非構造化テキストから構造化テキストへの処理、クリエイティブライティング、コンプライアンスチェックといった機能に最適です。
という具合で使えそうです。複雑なことはできなさそうという感じではあるので、どういう部分までがというのは見てみたいです。
一旦実際に感情分析をやってみましょう。
ローカルで試してみる
こちらの通りに起動などやっていきます。
ollama では https://ollama.com/library/gemma3/tags でモデルが公開されていますので、そちらを参考にモデルを指定してください。
FROM ollama/ollama:0.11.4
ENV OLLAMA_HOST 0.0.0.0:8080
ENV OLLAMA_MODELS /models
ENV OLLAMA_DEBUG false
ENV OLLAMA_KEEP_ALIVE -1
ENV MODEL gemma3:270m-it-qat
RUN ollama serve & sleep 5 && ollama pull $MODEL
ENTRYPOINT ["ollama", "serve"]
Docker コンテナサイズはローカルで3GB程度(Artifact Registory に gcloud run deploy コマンドであげると 1.3 GB 程度まで減っている)
まず日本語でやってみます
TEXT="こんにちは.調子はどう?"
curl http://localhost:8080/api/generate -d "{
\"model\": \"gemma3:270m-it-qat\",
\"prompt\": \"入力されたテキストを「ポジティブ」「ネガティブ」「ニュートラル」の3つのカテゴリのいずれかに分類してください。回答は「ポジティブ」「ネガティブ」「ニュートラル」だけにしてください。 テキスト:「${TEXT}」. 分類:\"
}"
こちらでやっていると、ポジティブ判定になります。何回やってもそうなので、まぁ期待通りかなという感じです。
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T06:48:44.545616257Z","response":"ポジ","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T06:48:44.553630257Z","response":"ティブ","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T06:48:44.556659257Z","response":"\n","done":false}
例を与えるような fewshot や CoT のプロンプトを書いても、問題はなさそうですが、他の Gemma3 model と比べるとチューニングが難しく感じます。
TEXT="こんにちは.調子はどう?"
curl http://localhost:8080/api/generate -d "{
\"model\": \"gemma3:270m-it-qat\",
\"prompt\": \"入力されたテキストを「ポジティブ」「ネガティブ」「ニュートラル」の3つのカテゴリのいずれかに分類してください。 例としては、「このレストランのサービスは本当に素晴らしかった。」 はポジティブ、「製品は悪くないが、価格が少し高すぎる。」 はネガティブ、「会議は午後3時に予定されています。」はニュートラル。 回答は「ポジティブ」「ネガティブ」「ニュートラル」だけにしてください。 テキスト:「${TEXT}」. 分類:\"
}"
他のタスクとして xml を json に変換するようなタスクをやってみます。
簡潔にしたいので 1行になっているものを利用します。
note>to>Toveto>from>Janifrom>heading>Reminderheading>body>Don't forget me this weekend!body>note>
FILE=$(cat ./sample1.xml)
curl http://localhost:8080/api/generate -d "{
\"model\": \"gemma3:270m-it-qat\",
\"prompt\": \"次のファイルを json に変換してください。 ${FILE}\"
}"
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.219193668Z","response":"```","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.23201421Z","response":"json","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.238318043Z","response":"\n","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.282018418Z","response":"{","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.395295044Z","response":"\n","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.542434627Z","response":" ","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.59117671Z","response":"\"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.600653252Z","response":"note","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.66700521Z","response":"\":","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.673118585Z","response":" \"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.678101169Z","response":"T","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.724945502Z","response":"ove","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.763070502Z","response":"\",","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.775993252Z","response":"\n","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.777095752Z","response":" ","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.783060502Z","response":"\"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.798916877Z","response":"to","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.813032585Z","response":"\":","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.821471585Z","response":" \"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.836994002Z","response":"J","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.845692669Z","response":"ani","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.921023627Z","response":"\",","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.930057169Z","response":"\n","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.935698502Z","response":" ","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.942070002Z","response":"\"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.951139294Z","response":"from","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.960301835Z","response":"\":","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.96852421Z","response":" \"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:04.983528835Z","response":"J","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.044731919Z","response":"ani","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.048408877Z","response":"\"","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.057206127Z","response":"\n","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.064302919Z","response":"}","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.072673502Z","response":"\n","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.081095169Z","response":"```","done":false}
{"model":"gemma3:270m-it-qat","created_at":"2025-08-17T07:24:05.095202794Z","response":"","done":true,"done_reason":"stop","context":[105,2364,107,60070,195214,8373,7744,96709,49391,236924,655,14210,1798,1071,236813,236774,1034,954,1071,1798,2543,236813,236863,3236,954,2543,1798,21279,236813,219455,954,21279,236813,218,8993,236789,236745,10849,786,672,9735,236888,229,954,14210,236813,106,107,105,4368,107,2717,3723,107,236782,107,138,236775,14210,1083,623,236774,1034,827,107,138,236775,1071,1083,623,236863,3236,827,107,138,236775,2543,1083,623,236863,3236,236775,107,236783,107,2717],"total_duration":1143686459,"load_duration":49207959,"prompt_eval_count":52,"prompt_eval_duration":216933458,"eval_count":36,"eval_duration":876569667}
期待通り変換してくれているように見えます。
他に例えば、 https://www.soumu.go.jp/main_content/000687877.pdf のサンプルの「保管ファイル分類ファイル」を使います。
ちょっとcurlで送るのがめんどいので、リクエスト確認のために LM Studio を使ってみます。
結果としては、まぁ大体は取れてそうです。(もう少しフォーマットについては気がしたほうが良いかも)
ということで変換タスク等もだいたい行けそうです。
Cloud Run で動かす
ローカルで試している限り小規模なタスクには使えそうですが Cloud Run に deploy してやります。
us-central1 でやるのは Cloud Run GPU が使えるリージョンが限られているためですね。(日本に早くこないかな)
cpu 4,memory 16G は GPU を使う上で最低条件です。
https://cloud.google.com/run/docs/configuring/services/gpu
$ gcloud run deploy ollama-gemma3-270m \
--source . \
--concurrency 4 \
--cpu 4 \
--set-env-vars OLLAMA_NUM_PARALLEL=4 \
--gpu 1 \
--gpu-type nvidia-l4 \
--max-instances 1 \
--memory 16Gi \
--no-allow-unauthenticated \
--no-cpu-throttling \
--no-gpu-zonal-redundancy \
--timeout=600 \
--region=us-central1 \
--project $GOOGLE_PROJECT
デプロイができたら、テストとしては分類を行うようにします。
TEXT="こんにちは.調子はどう?"
HOST="${host}"
TOKEN="$(gcloud auth print-identity-token)"
curl ${HOST}/api/generate -H "Content-Type: application/json" -H "Authorization: Bearer ${TOKEN}" -d "{
\"model\": \"gemma3:270m-it-qat\",
\"prompt\": \"入力されたテキストを「ポジティブ」「ネガティブ」「ニュートラル」の3つのカテゴリのいずれかに分類してください。 回答は「ポジティブ」「ネガティブ」「ニュートラル」だけにしてください。 テキスト:「${TEXT}」. 分類:\"
}"
何回か利用をして CPU、Memory をの利用率はとても低いです。
速度としても 100ms で動いているように見えます。
性能テストしてみる
k6s を使います。使い方などは、ドキュメントを参考にしてください。
以下テスト用スクリプトです
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
iterations: 500,
vus: 5,
};
export default function() {
const host = "https://host.us-central1.run.app"
const TEXT = "こんにちは.調子はどう?"
http.post(`${host}/api/generate`, JSON.stringify({
"prompt": `入力されたテキストを「ポジティブ」「ネガティブ」「ニュートラル」の3つのカテゴリのいずれかに分類してください。 回答は「ポジティブ」「ネガティブ」「ニュートラル」だけにしてください。 テキスト:「${TEXT}」. 分類:`,
"stream": false,
"model": "gemma3:270m-it-qat",
}), {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${__ENV.GOOGLE_CLOUD_ID_TOKEN}`
}
});
sleep(1);
}
max instance 数 1, concurrence 4, VUs(アクセスするユーザ数) 5
$ k6 run k6/script.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: k6/script.js
output: -
scenarios: (100.00%) 1 scenario, 5 max VUs, 10m30s max duration (incl. graceful stop):
* default: 500 iterations shared among 5 VUs (maxDuration: 10m0s, gracefulStop: 30s)
█ TOTAL RESULTS
HTTP
http_req_duration..............: avg=267.36ms min=244.59ms med=264.41ms max=509.13ms p(90)=282.63ms p(95)=288.3ms
{ expected_response:true }...: avg=267.36ms min=244.59ms med=264.41ms max=509.13ms p(90)=282.63ms p(95)=288.3ms
http_req_failed................: 0.00% 0 out of 500
http_reqs......................: 500 3.916273/s
EXECUTION
iteration_duration.............: avg=1.27s min=1.24s med=1.26s max=1.62s p(90)=1.28s p(95)=1.29s
iterations.....................: 500 3.916273/s
vus............................: 2 min=2 max=5
vus_max........................: 5 min=5 max=5
NETWORK
data_received..................: 487 kB 3.8 kB/s
data_sent......................: 259 kB 2.0 kB/s
running (02m07.7s), 0/5 VUs, 500 complete and 0 interrupted iterations
default ✓ [======================================] 5 VUs 02m07.7s/10m0s 500/500 shared iters
2分ちょっとかかっているように見えます。
とはいえ1インスタンスで捌いて、コンテナの CPU 使用数は 20%弱でメモリ使用量も 4% ぐらい。
リクエストのレイテンシも 140m弱 で返却はしていて、物理的距離な気がします。(ちなみにのせていませんが GPU の利用数も 4% 弱で抑えられています
max instance 数 1, concurrence 4, VUs(アクセスするユーザ数) 50
$ k6 run k6/script.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: k6/script.js
output: -
scenarios: (100.00%) 1 scenario, 50 max VUs, 10m30s max duration (incl. graceful stop):
* default: 500 iterations shared among 50 VUs (maxDuration: 10m0s, gracefulStop: 30s)
█ TOTAL RESULTS
HTTP
http_req_duration..............: avg=1.92s min=252.89ms med=2.05s max=2.49s p(90)=2.39s p(95)=2.41s
{ expected_response:true }...: avg=1.92s min=252.89ms med=2.05s max=2.49s p(90)=2.39s p(95)=2.41s
http_req_failed................: 0.00% 0 out of 500
http_reqs......................: 500 16.383647/s
EXECUTION
iteration_duration.............: avg=2.93s min=1.25s med=3.05s max=3.5s p(90)=3.39s p(95)=3.41s
iterations.....................: 500 16.383647/s
vus............................: 13 min=13 max=50
vus_max........................: 50 min=50 max=50
NETWORK
data_received..................: 799 kB 26 kB/s
data_sent......................: 315 kB 10 kB/s
running (00m30.5s), 00/50 VUs, 500 complete and 0 interrupted iterations
default ✓ [======================================] 50 VUs 00m30.5s/10m0s 500/500 shared iters
30秒弱で処理を捌き切っているように見えます。
リクエスト数 7/s 程度、メモリ使用率やCPUについても、正常値に見えます。
試しに並行数、インスタンス数を上げてどうなるかを確認してみます。
max instance 数 7, concurrence 100, VUs(アクセスするユーザ数) 50
$ k6 run k6/script.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: k6/script.js
output: -
scenarios: (100.00%) 1 scenario, 50 max VUs, 10m30s max duration (incl. graceful stop):
* default: 500 iterations shared among 50 VUs (maxDuration: 10m0s, gracefulStop: 30s)
█ TOTAL RESULTS
HTTP
http_req_duration..............: avg=1.14s min=244.34ms med=841.55ms max=5.08s p(90)=1.93s p(95)=4.73s
{ expected_response:true }...: avg=1.14s min=244.34ms med=841.55ms max=5.08s p(90)=1.93s p(95)=4.73s
http_req_failed................: 0.00% 0 out of 500
http_reqs......................: 500 22.461712/s
EXECUTION
iteration_duration.............: avg=2.16s min=1.24s med=1.84s max=6.21s p(90)=2.94s p(95)=5.87s
iterations.....................: 500 22.461712/s
vus............................: 18 min=18 max=50
vus_max........................: 50 min=50 max=50
NETWORK
data_received..................: 800 kB 36 kB/s
data_sent......................: 315 kB 14 kB/s
running (00m22.3s), 00/50 VUs, 500 complete and 0 interrupted iterations
default ✓ [======================================] 50 VUs 00m22.3s/10m0s 500/500 shared iters
時間は減りつつ 22秒弱で処理を捌き切っているように見えます。
ただ、同時並行数においてCPU を使い切れていないのでインスタンスも生成されていないように見えます。そして、インスタンス数も1の状態。
GPU 利用率も 4% と全然使い切れていません。
max instance 数 7, concurrence 100, VUs(アクセスするユーザ数) 500
$ k6 run k6/script.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: k6/script.js
output: -
scenarios: (100.00%) 1 scenario, 500 max VUs, 10m30s max duration (incl. graceful stop):
* default: 500 iterations shared among 500 VUs (maxDuration: 10m0s, gracefulStop: 30s)
█ TOTAL RESULTS
HTTP
http_req_duration..............: avg=8.14s min=513.51ms med=9.4s max=12.01s p(90)=11.35s p(95)=11.55s
{ expected_response:true }...: avg=8.14s min=513.51ms med=9.4s max=12.01s p(90)=11.35s p(95)=11.55s
http_req_failed................: 0.00% 0 out of 500
http_reqs......................: 500 37.068507/s
EXECUTION
iteration_duration.............: avg=9.62s min=1.69s med=11.08s max=13.47s p(90)=12.91s p(95)=13.11s
iterations.....................: 500 37.068507/s
vus............................: 44 min=44 max=500
vus_max........................: 500 min=500 max=500
NETWORK
data_received..................: 3.9 MB 290 kB/s
data_sent......................: 881 kB 65 kB/s
running (00m13.5s), 000/500 VUs, 500 complete and 0 interrupted iterations
default ✓ [======================================] 500 VUs 00m13.5s/10m0s 500/500 shared iters
500 まで同時並行数を上げるとインスタンスを分散してさばき始めました。とはいえCPU使用率などが上がっているわけではないです。
同時リクエストも 100 程度まであげられますということで、正直、普通のHTTPリクエストを送っているような感覚に陥ります。
この後 10000 イテレーション、VUs を 2000 とかでやると私のローカルマシンスペックの問題と思われるが、IOエラーがでてしまうが、これでもCPU使用率は 60% 、GPU使用率も 20% とかで収まってしまっているので、割とすごい。
とりあえず速度面では全く問題なく、簡単なタスク(今回やったようなフォーマットとか整えるとかですかね)、こいつに任せると良いのかなと思いました。簡単なタスクで生成 AI を組み込むと嬉しいことがどれぐらいあるのかが今のところピンときていないのですが。。。
とはいえ、モデルサイズも小さく、実行速度なども早いので、簡単に組み込みもできるため、割と使い所もあるかと思います。
このモデルの Finetuning は他の gemma3 と同じ様にできるらしいのですが、それ以上は別途調べようと思います。
Views: 0