💻

GPUを使わずにWindowsでローカルLLMを動かす

に公開

はじめに

近年、LLM(大規模言語モデル)をローカル環境で動かすことが注目されている。
しかし、多くのLLMはGPUを必要とするため、GPUを搭載していないWindowsマシンでは動作が難しい。
本記事では、GPUを使わずにWindowsでローカルLLMを動かす方法について解説する。

前提条件

  • Git for WindowsなどでGitコマンドが使えること
  • Docker Desktop for Windowsなどでdocker、docker composeコマンドが使えること
  • Hugging Faceのアカウントを持っていること
    • 必ずしも必要ないが、下記の説明ではgemma-3-1bを使用するため、アカウント作成してGoogleの利用規約に許諾する必要がある。

LLMモデルの準備

今回利用するのは以下の2つのモデルである。

モデルをクローンするのに、以下のコマンドを実行する。

git clone https://huggingface.co/google/gemma-3-1b-it
git clone https://huggingface.co/google/gemma-3-1b-it-qat-q4_0-gguf

Ollamaの場合

ディレクトリ構成は以下のようになる。

.
├ docker-compose.yaml
├ Modelfile
└ gemma-3-1b-it-qat-q4_0-gguf/gemma-3-1b-it-q4_0.gguf

docker-compose.yamlの内容は以下の通りである。

services:
  ollama:
    image: ollama/ollama:0.13.0
    container_name: ollama
    ports:
      - "11434:11434"
    ipc: host
    volumes:
      - ./gemma-3-1b-it-qat-q4_0-gguf/:/work/gemma-3-1b-it-qat-q4_0-gguf
      - ./Modelfile:/work/Modelfile:ro
    working_dir: /work
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/health"]
      interval: 10s
      timeout: 10s
      retries: 300

Modelfileの内容は以下の通りである。

FROM ./gemma-3-1b-it-qat-q4_0-gguf/gemma-3-1b-it-q4_0.gguf

PARAMETER stop <|end_of_turn|>
PARAMETER temperature 0.0
PARAMETER top_k 64
PARAMETER top_p 0.95
PARAMETER repeat_penalty 1.5
PARAMETER num_ctx 20480

TEMPLATE """
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 }}
{{- if or (eq .Role "user") (eq .Role "system") }}<start_of_turn>user
{{ .Content }}<end_of_turn>
{{ if $last }}<start_of_turn>model
{{ end }}
{{- else if eq .Role "assistant" }}<start_of_turn>model
{{ .Content }}{{ if not $last }}<end_of_turn>
{{ end }}
{{- end }}
{{- end }}
"""

パラメータの値は https://ollama.com/library/gemma3:1b を参考に設定している。

以下のコマンドを実行する。

docker compose up -d
docker exec -it ollama ollama create gemma-3-1b -f Modelfile

以下のコマンドで動作確認を行う。

curl http://localhost:11434/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "gemma-3-1b",
        "messages": [
            {"role": "user", "content": "こんにちは。"}
        ]
    }'

vLLMの場合

ディレクトリ構成は以下のようになる。
vllmはgit clone https://github.com/vllm-project/vllm.gitで取得する。

.
├ docker-compose.yaml
├ vllm
├ vllm_entrypoint.sh
└ gemma-3-1b-it

docker-compose.yamlの内容は以下の通りである。

services:

  vllm:
    image: ubuntu:24.04
    container_name: vllm
    ports:
      - "8000:8000"
    volumes:
      - ./gemma-3-1b-it/:/models/gemma-3-1b-it/:ro
      - ./vllm_entrypoint.sh:/work/vllm_entrypoint.sh:ro
      - ./vllm:/work/vllm
    working_dir: /work/vllm
    entrypoint: bash -c "/work/vllm_entrypoint.sh"

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 10s
      timeout: 10s
      retries: 300

vllm_entrypoint.shの内容は以下の通りである。

#!/bin/bash
apt-get update -y
apt-get install -y gcc-12 g++-12 libnuma-dev python3-dev curl git
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 10 --slave /usr/bin/g++ g++ /usr/bin/g++-12

curl -LsSf https://astral.sh/uv/0.9.8/install.sh | env UV_INSTALL_DIR=/root/.local/bin sh

export PATH=$PATH:/root/.local/bin

uv venv --python 3.12 --seed
source ./.venv/bin/activate

uv pip install --upgrade setuptools
uv pip install --upgrade pip
uv pip install -r requirements/cpu-build.txt --index-strategy unsafe-best-match --torch-backend cpu
echo "END requirements/cpu-build.txt"
uv pip install -r requirements/cpu.txt --index-strategy unsafe-best-match --torch-backend cpu
echo "END requirements/cpu.txt"
VLLM_TARGET_DEVICE=cpu uv pip install . --no-build-isolation
echo "END all installations"

# TODO: 絶対パスに直す
python /absolute/path/to/vllm/entrypoints/openai/api_server.py --model /models/gemma-3-1b-it --served-model-name gemma-3-1b-it --host 0.0.0.0 --port 8000 --max-model-len 2048 --dtype bfloat16

以下のコマンドを実行する。

docker compose up -d

以下のコマンドで動作確認を行う。

curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "gemma-3-1b-it",
        "messages": [
            {"role": "user", "content": "こんにちは。"}
        ]
    }'

メモ

  • https://docs.vllm.ai/en/stable/getting_started/installation/cpu
    • Set up using Dockerセクションを参考にしていない。ホストOSがWindowsであるためうまく動作しなかった。
    • Set up using Pythonセクションを参考に環境構築している。
      • ただし、最初はbuild時にDockerfileでuv、sourceコマンドを実行しようとしたが、うまく動作しなかったため、entrypointスクリプトにまとめている。
      • uv pip installに--index-strategy unsafe-best-matchを追加している。これを追加しないと、依存関係の解決に失敗した。

llama.cppの場合

ディレクトリ構成は以下のようになる。
llama.cppはgit clone https://github.com/ggml-org/llama.cpp.gitで取得する。

.
├ docker-compose.yaml
├ llama.cpp
└ gemma-3-1b-it-qat-q4_0-gguf

llama.cpp/docs/docker.mdを参考に、以下のコマンドでイメージをビルドする。

cd llama.cpp
docker build -t local/llama.cpp:server-cpu --target server -f .devops/cpu.Dockerfile .

docker-compose.yamlの内容は以下の通りである。

services:
  llamacpp:
    image: local/llama.cpp:server-cpu
    container_name: llamacpp
    ports:
      - "8080:8080"
    volumes:
      - ./gemma-3-1b-it-qat-q4_0-gguf/:/work/gemma-3-1b-it-qat-q4_0-gguf
    command: >
      --model /work/gemma-3-1b-it-qat-q4_0-gguf/gemma-3-1b-it-q4_0.gguf
      --host 0.0.0.0
      --port 8080
      --ctx-size 2048
      --verbose
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    deploy:
      resources:
        limits:
          memory: 8G
        reservations:
          memory: 4G

以下のコマンドを実行する。

docker compose up -d

以下のコマンドで動作確認を行う。

curl http://localhost:8080/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "gemma-3-1b-it-q4_0",
        "messages": [
            {"role": "user", "content": "こんにちは。"}
        ]
    }'

おわりに

本記事では、GPUを使わずにWindowsでローカルLLMを動かす方法について解説した。
Ollama、vLLM、llama.cppの3つの方法を紹介したが、用途や環境に応じて適切な方法を選択してほしい。

Discussion