ローカルLLM Gemma 3 をRaspberry Pi 5で動かす

## 内容

今の時代Raspberry Piでも十分快適に使えるLLMが出ているらしく、気になったのでGoogleが提供しているLLM [Gemma 3](https://deepmind.google/models/gemma/gemma-3/)の[1b q4_0 ggufモデル](https://huggingface.co/google/gemma-3-1b-it-qat-q4_0-gguf)を[Raspberry Pi 5](https://www.raspberrypi.com/products/raspberry-pi-5/)(8GB RAM)で動かしてみました。

また、動かすまでの手順を記載しました。

結果としては以下のような感じで大分快適に動きました！


![20250615_hello.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/41c0a813-a96a-4591-a2be-278eb3ad85ec.gif)

![20250615_qiita_zenn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/4612a11e-aa77-4d85-b0a2-5e060448d26d.gif)



### Gemma 3 について

https://deepmind.google/models/gemma/gemma-3/

https://huggingface.co/google/gemma-3-27b-it

Googleが提供する軽量なマルチモーダルLLMで、画像やテキストを入力としてテキストを出力できます。
多言語対応もしていて、入力は4b,12b,27bモデルで128kトークン、1bは32kトークンまで対応しています。
出力はいずれも8192トークンまで対応です。

Hugging Faceではそれぞれのパラメータ数に加えて`pre-trained(pt)`から`instruction tuning(it)`、量子化系のモデルまで公開されています。

https://huggingface.co/google?search_models=gemma-3-

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/e001ddad-a33d-46b3-9673-4a9ff4ab3f00.png)


## セットアップ手順

### 1. Raspberry Pi 5自体のセットアップ

Raspberry Pi 5自体のセットアップを行います。詳細は[こちら](https://qiita.com/nijigen_plot/items/5f5299af6aebc54b42d3#raspberry-pi-5-%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)に記載しています。

### 2\. [uv](https://docs.astral.sh/uv/getting-started/installation/)のインストール
```
$ curl -LsSf https://astral.sh/uv/install.sh | sh
$ uv --version
uv 0.6.17
```

### 3. Hugging Face登録

[Hugging Face](https://huggingface.co/)にアカウントを作ります
作成後Hugging FaceにSSH Keyを登録します

```
$ ssh-keygen -t rsa
$ cat id_rsa.pub
```

Hugging Face Settings -> SSH and GPG Keys -> Add SSH Keyから`cat id_rsa.pub`をコピーペーストして登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/1147ec21-8aea-4d12-977d-8858c6a56883.png)

### 4. [git lfs](https://github.com/git-lfs/git-lfs/wiki/Installation)のインストール

[git lfs](https://github.com/git-lfs/git-lfs/wiki/Installation)をインストールします。

```
$ curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
$ sudo apt-get update
$ sudo apt-get install git-lfs
$ git lfs install
$ git lfs --version
git-lfs/3.6.1 (GitHub; linux arm64; go 1.23.3)
```

### 5. リポジトリclone

https://github.com/nijigen-plot/personal-knowledge-base/tree/gemma-3-1b-gguf-with-raspberrypi-5

[こちらのリポジトリのgemma-3-1b-gguf-with-raspberrypi-5ブランチ](git@github.com:nijigen-plot/personal-knowledge-base.git)をcloneします

```
$ git clone -b gemma-3-1b-gguf-with-raspberrypi-5 git@github.com:nijigen-plot/personal-knowledge-base.git
$ cd personal-knowledge-base
$ uv sync
$ touch history.txt
```

### 6. [google/gemma-3-1b-it-qat-q4_0-gguf](https://huggingface.co/google/gemma-3-1b-it-qat-q4_0-gguf)のclone

[google/gemma-3-1b-it-qat-q4_0-gguf](https://huggingface.co/google/gemma-3-1b-it-qat-q4_0-gguf)をcloneします

```
$ git clone git@hf.co:google/gemma-3-1b-it-qat-q4_0-gguf
```


## 動作

`$ uv run python gemma3-1b-q4-gguf.py プロンプト`でAIと会話できます！


![20250615_qiita_zenn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/4612a11e-aa77-4d85-b0a2-5e060448d26d.gif)


## 履歴保存機能

`history.txt`に自分の質問を保持しておき、systemに事前に入れるようにしました。

```python:gemma3-1b-q4-gguf.py

if os.path.exists(HISTORY_FILE):
        with open(HISTORY_FILE, "r", encoding="utf-8") as f:
            # https://gemma-llm.readthedocs.io/en/latest/colab_tokenizer.html?utm_source=chatgpt.com
            # 単語でトークンカウントしているので、文字数でn_ctxの数だけ読む分にはまずオーバーしないはず
            memory_file = f.read()
        memory = memory_file[-n_ctx:]
        logger.info(f"{HISTORY_FILE} から過去の会話履歴を読み込みました。")
    else:
        logger.info(f"{HISTORY_FILE} が存在しないため、過去の会話履歴は考考されません。必要な場合は{HISTORY_FILE} を作成してください。")
        memory = ""
    messages = [
        {
            "role": "system",
            "content": [
                {"type": "text", "text": f"あなたは日本語を話すAIアシスタントです。"},
                {"type": "text", "text": f"以下は過去の会話情報です。必要だと思った場合は活用し、そうでない質問の場合は活用しないでください。\n {memory}"}
            ],
        },
        {
            "role": "user",
            "content": [
                {"type": "text", "text": prompt}
            ]
        }
    ]
```

Gemini,Gemma等GoogleのLLMはトークンのカウントが単語レベルの為、n_ctx(入力トークン)の値だけテキストから文字列を取得する分には、おそらくオーバーしないだろうと思っています。


https://gemma-llm.readthedocs.io/en/latest/colab_tokenizer.html?utm_source=chatgpt.com

https://ai.google.dev/gemini-api/docs/tokens?hl=ja&lang=python

> Gemini モデルの場合、1 個のトークンは約 4 文字に相当します。100 個のトークンは、約 60 ～ 80 ワード（英語）に相当します。


これで、過去の会話をもとに返事を返してくれるようになります

![20250615_tokyo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/053d6586-17c6-4c5d-951a-ebca0df15c06.gif)

## ストリーミング出力

滑らかな文字の出力については、[create_chat_completionメソッド](https://github.com/abetlen/llama-cpp-python/blob/main/llama_cpp/llama.py#L1930)の`stream`をTrueにし、以下のif文のように差分を`print()`しています。`flush=True`で即時出力されるためより滑らかになります。


```python:gemma3-1b-q4-gguf.py
resp = llm.create_chat_completion(
        messages=messages, max_tokens=max_tokens, stream=stream, temperature=0.3
    )
    if stream:
        partial_message = ""
        for msg in resp:
            message = msg["choices"][0]["delta"]
            if "content" in message:
                content = message["content"]
                print(content, end="", flush=True)
                partial_message += content
        print()
    else:
        content = resp["choices"][0]["message"]["content"]
        print(content)
    end = time.perf_counter() - start
    logger.info(f"処理時間: {end:.2f}秒")
    save_memory(prompt)
```

## gguf形式について

同じgemma 3 1bに[google/gemma-3-1b-it](https://huggingface.co/google/gemma-3-1b-it)というモデルがありますが、今回は[google/gemma-3-1b-it-qat-q4_0-gguf](https://huggingface.co/google/gemma-3-1b-it-qat-q4_0-gguf)を使っています。

これは[llama.cpp](https://github.com/ggml-org/llama.cpp)というLLMの推論に特化したC/C++フレームワークで、`google/gemma-3-1b-it`で使われているSafetensors形式よりも高速に実行できます。llama.cppで使用する形式が`.gguf`です

https://zenn.dev/hellorusk/books/e56548029b391f/viewer/intro1#llama.cpp

`qat-q4_0`の部分は量子化についての記載で、bit数と種類で細かいルールがあるようです。

https://zenn.dev/hellorusk/books/e56548029b391f/viewer/intro3#llama.cpp-%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E9%87%8F%E5%AD%90%E5%8C%96%E6%89%8B%E6%B3%95

とりあえず`google/gemma-3-1b-it`より精度低下しますが、軽くて速いので`gemma-3-1b-it-qat-q4_0-gguf`を使っています。

## 結果

想像以上に性能と速度が両立されていて、とても感動しました！
ベクトルDBと併せて完全ローカルでナレッジベースもいけそうです。

実はGemma 2の時から「Raspberry Pi 4を使って動かしてみた」系記事があったのでGemma 3で試してみたのですが、十分に使えましたね。

常に最新情報を追っているわけではないので、もし間違いやアドバイス等あればコメントいただけますと嬉しいです。


### 参考

https://zenn.dev/hellorusk/books/e56548029b391f/viewer/intro1

https://huggingface.co/docs/transformers/model_doc/gemma3