8GB VRAMでは、大きなモデルを運用できない……
ローカルでLLMを使うときに重要なパラメータがVRAMの容量です。
私のPCは、GeForce RTX 4060でVRAMの容量は 8GB しかありません。せめて12GB、できたら16GBあれば、大きなモデルが動かせるのにと思う毎日でした。
高性能なGPUは、価格と消費電力が大きいですし、統合メモリーのMac miniも大容量RAM機種はなくなったみたい(?)で、AMDのmini PCを買ってまでも……
そんなとき、@DanielOkadaさんの、GPUだけでLLMを動かす必要はないという記事を見て、これはいける!と感じ、私も試してみました。
ハードウエア&ソフトウエア
| 項目 | 内容 |
|---|---|
| CPU | Corei9 13900 |
| RAM | 32GB |
| GPU | GeForce RTX 4060 |
| VRAM | 8GB |
| OS | Windows11 |
| CUDA | ver.13.3 |
| LLMサーバー | llama.cpp-server |
llama.cpp-serverは、 GitHubの ggml-org /llama.cpp にある、ReleaseからWindows用バイナリー(CUDA対応)をダウンロードして利用しました。
LLMモデルは、huggingfaceのQwen3-Coder-30B-A3B-Instruct-GGUFから、4bit量子化板のQwen3-Coder-30B-A3B-Instruct-Q4_K_M.ggufをダウンロードしました。ファイルサイズは、18.6GBです。VRAMの8GBよりもかなり大きいので普通ならば実行できません。たとえ、メインRAMを食って実行できたとしても遅くて使い物になりません。
Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.ggufについて
- 30Bは約300億個のパラメータ
- A3B(Active 3B)は推論時に毎回の計算でアクティブなパラメータが約30億
- Q4_K_Mは量子化手法
です。
llama.cpp-serverは、huggingfaceからダウンロードしたファイルをそのまま使えます。
また、ブラウザでアクセスできるGUIを持っていて、スクリプトを書かなくても利用できます。
実行パラメータ
llama-server.exeを動かすためのバッチファイルです。
パラメータ指定で、アクティブな部分のみをVRAMに、その他はメインRAMに割り当てることで、VRAMの容量を超えるモデルを実行します。
E:\llama\llama-server ^
-m "E:\llama\models\Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf" ^
--port 9000 ^
-c 12288 ^
--temp 0.1 ^
--top-p 0.9 ^
-ngl 99 --cpu-moe ^
--repeat-penalty 1.1
バッチファイルの中で重要な部分は、CPUオフロードを使うための
-ngl 99 --cpu-moe
です。アクティブでない部分をメインメモリに移します。
VRAMに入っている部分はGPUが処理、メインメモリに入っている部分はCPUが処理するようになります。
-ngl 99 だけですと、すべてをVRAMにロードしようとします。VRAMに入り切らない部分は共有GPUメモリ(メインRAM)に移されますが、すべてのパラメータを一度に使おうとしますので、推論が極端に遅くなります。
テストプロンプト
ブラウザから http://localhost:9000/ (上記設定の --port 9000 によります)にアクセスすると、よくある画面が出てきます。
今回はテストとして、ブラウザで動く ライフゲーム を作ってもらいます。
ライフゲームとは
単純なルールで、生命の誕生や死を再現する数理モデルです。
むかし、流行りました。
テストプロンプト
HTMLとJavaScriptを使用して、ブラウザで動くコンウェイのライフゲーム(Conway's Game of Life)のプログラムを作成してください。
画面にはクリックで細胞(セル)の生死を切り替えられるグリッド、[スタート]、[ストップ]、[リセット]、[1ステップ進める]のボタンを配置してください。
デザインはシンプルで見やすくしてください。
グリッド線を入れてください。
初期値やリセット値は、すべて空にしてください。
生きているセルを黒としてください。
JavaScript、CSSスタイルは、HTMLファイルとは別にして下さい。
HTMLファイルはlife.html、JavaScriptはlife.js、CSSはlife.cssという名前で出力して下さい。
結果
Qwen3-Coderは動くコードを出力してくれました。しかも実用的な速さでした。
LLMをロードする前

VRAMは空、メインRAMは25GBほどの余裕があります。
コード作成中
GPUは100%、VRAMは3.3GBです。

メインメモリーは17.9GB(=24.0-6.1)ほど消費しています。

CPUは78%ほど稼働しています。

そして、26.69token/sec出ました。十分な速さです。

出来たソースコードです。
life.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Conway's Game of Life</title>
<link rel="stylesheet" href="life.css" />
</head>
<body>
<h1>Conway's Game of Life</h1>
<div class="controls">
<button id="startBtn">スタート</button>
<button id="stopBtn">ストップ</button>
<button id="resetBtn">リセット</button>
<button id="stepBtn">1ステップ進める</button>
</div>
<canvas id="gridCanvas" width="600" height="400"></canvas>
<script src="life.js"></script>
</body>
</html>
life.css
body {
font-family: Arial, sans-serif;
text-align: center;
background-color: #f5f5f5;
}
h1 {
color: #333;
}
.controls {
margin-bottom: 20px;
}
button {
padding: 10px 15px;
margin: 5px;
font-size: 16px;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
#gridCanvas {
border: 2px solid #333;
background-color: white;
}
life.js
const canvas = document.getElementById('gridCanvas');
const ctx = canvas.getContext('2d');
const CELL_SIZE = 10;
const COLS = Math.floor(canvas.width / CELL_SIZE);
const ROWS = Math.floor(canvas.height / CELL_SIZE);
let grid = createEmptyGrid();
let nextGrid = createEmptyGrid();
let running = false;
let intervalId = null;
function createEmptyGrid() {
return Array(ROWS).fill().map(() => Array(COLS).fill(false));
}
function drawGrid() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// グリッド線を描画
ctx.strokeStyle = '#ccc';
ctx.lineWidth = 0.5;
for (let y = 0; y <= ROWS; y++) {
ctx.beginPath();
ctx.moveTo(0, y * CELL_SIZE);
ctx.lineTo(canvas.width, y * CELL_SIZE);
ctx.stroke();
}
for (let x = 0; x <= COLS; x++) {
ctx.beginPath();
ctx.moveTo(x * CELL_SIZE, 0);
ctx.lineTo(x * CELL_SIZE, canvas.height);
ctx.stroke();
}
// 生きているセルを描画
ctx.fillStyle = 'black';
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
if (grid[y][x]) {
ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
}
}
}
}
function countNeighbors(x, y) {
let count = 0;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < COLS && ny >= 0 && ny < ROWS) {
if (grid[ny][nx]) count++;
}
}
}
return count;
}
function updateGrid() {
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
const neighbors = countNeighbors(x, y);
if (grid[y][x]) {
nextGrid[y][x] = neighbors === 2 || neighbors === 3;
} else {
nextGrid[y][x] = neighbors === 3;
}
}
}
// グリッドを更新
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
grid[y][x] = nextGrid[y][x];
}
}
}
function toggleCell(x, y) {
const col = Math.floor(x / CELL_SIZE);
const row = Math.floor(y / CELL_SIZE);
if (col >= 0 && col < COLS && row >= 0 && row < ROWS) {
grid[row][col] = !grid[row][col];
drawGrid();
}
}
canvas.addEventListener('click', function(e) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
toggleCell(x, y);
});
document.getElementById('startBtn').addEventListener('click', () => {
if (!running) {
running = true;
intervalId = setInterval(() => {
updateGrid();
drawGrid();
}, 100);
}
});
document.getElementById('stopBtn').addEventListener('click', () => {
running = false;
clearInterval(intervalId);
});
document.getElementById('resetBtn').addEventListener('click', () => {
running = false;
clearInterval(intervalId);
grid = createEmptyGrid();
drawGrid();
});
document.getElementById('stepBtn').addEventListener('click', () => {
updateGrid();
drawGrid();
});
drawGrid(); // 初期描画
「グライダー」が飛んでゆきます。
JavaScriptは、プロンプトを新規実行する毎に変化しますが、HTMLやCSSはほとんど変わりません。
また、プロンプトの僅かな修正で結果が変わってしまいました。
他のパラメータ
-ngl 99 --cpu-moe 以外のパラメータも試してみました。
| 設定 | token/sec |
|---|---|
| -ngl 99 | 5.0 |
| -ngl 99 --flash-attn on --cache-type-k q8_0 --cache-type-v q8_0 |
5.1 |
| -ngl 99 --n-cpu-moe 32 --flash-attn on | 12.9 |
| -ngl 99 --n-cpu-moe 128 --flash-attn on | 27.0 |
| -ngl 99 --cpu-moe | 27.0 |
| -ngl auto --flash-attn on | 28.9 |
MoEモデルはすごい!
MoE(Mixture of Experts)(萌え)モデルのすばらしさを実感しました。
VRAMには余裕がありますので、メインメモリーを増やせば、もっと大きなモデルでも動かすことができる?かも