CPUだけでローカルLLM(GPU不使用)


llama-cpp-pythonというライブラリで大規模言語モデル(LLM)をローカル環境で使って動かしてみた備忘録です

# 目次
- [使用環境](#使用環境)
- [用語解説](#用語解説)
- [llama-cpp-pythonのインストール](#llama-cpp-pythonのインストール)
  - [ビルドエラーの解決方法](#ビルドエラーの解決方法)
- [モデルのダウンロード](#モデルのダウンロード)
- [実行する](#実行する)
  - [とりあえず日本語](#とりあえず日本語)
  - [英語も試してみる](#英語も試してみる)
  - [コードの解説](#コードの解説)
- [おわりに](#おわりに)
- [参考にさせていただいた記事、サイト](#参考にさせていただいた記事サイト)
# 使用環境

- python 3.11.9
- llama-cpp-python-0.3.2
- 仮想環境 venv

**ディレクトリ構成**
>root/
>　├ venv/
>　├ models/
>　│　└ Llama-3-ELYZA-JP-8B-q4_k_m.gguf/
>　└ LocalLLM.py

# 用語解説

**■ 大規模言語モデル（LLM）とは**
人工知能の一種で、自然言語処理（NLP）と呼ばれる分野で使用される。
ChatGPTのような文章を生成、要約できるものです

https://www.softbank.jp/biz/solutions/generative-ai/ai-glossary/llm/#:~:text=%E5%A4%A7%E8%A6%8F%E6%A8%A1%E8%A8%80%E8%AA%9E%E3%83%A2%E3%83%87%E3%83%AB%EF%BC%88LLM%EF%BC%89%E3%81%A8%E3%81%AF,%E5%88%86%E9%87%8E%E3%81%A7%E4%BD%BF%E7%94%A8%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82

**■ llama-cpp-python**　https://github.com/abetlen/llama-cpp-python
1. C/C++で書かれた「llama.cpp」をPythonから使えるようにする
2. GPUを必要とせず、CPUのみでLLMを動作させれる
3. GGUFフォーマット(後述)の量子化モデルをサポートしている
4. Llama-2やMistral、GPT、BERTなど、様々なLLMモデルに対応している

といった形でローカル環境で大規模言語モデル(LLM)を使用できるようにするPythonライブラリです

**■ GGUFフォーマット**
大規模言語モデル（LLM）の読み込みと保存を高速にする、すごいファイル形式です

https://www.ibm.com/jp-ja/think/topics/gguf-versus-ggml


**■ Llama-3-ELYZA-JP-8B-GGUF**　https://huggingface.co/mmnga/Llama-3-ELYZA-JP-8B-gguf
ELYZAが開発した日本語に特化した大規模言語モデル（LLM）のGGUF形式版で
- Llama-3：Meta社のLlama3をベースに
- ELYZA：ELYZAが開発して
- JP：日本語で追加学習した
- 8B：80億(8B)パラメータの
- GGUF：GGUFフォーマットのモデル
ということらしいで、
LLMのパラメータは数千億から数兆になるらしく80億(8B)パラメータでも小型のLLMらしいです。(GPT-3.5-Turboは3550億)

https://self.systems/laboratory-model-comparison/

# llama-cpp-pythonのインストール
pipでllama-cpp-pythonをインストール
``` :コマンドライン
pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
```
しようとしたのですがエラーが発生
c/c++のビルドでエラーが発生したようです
``` :コマンドライン
Building wheels for collected packages: llama-cpp-python
  Building wheel for llama-cpp-python (pyproject.toml) ... error
  error: subprocess-exited-with-error

  × Building wheel for llama-cpp-python (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> [10 lines of output]
      *** scikit-build-core 0.10.7 using CMake 3.31.2 (wheel)
      *** Configuring CMake...
      2024-12-26 12:08:24,371 - scikit_build_core - WARNING - Can't find a Python library, got libdir=None, ldlibrary=None, multiarch=None, masd=None
      loading initial cache file C:\Users\怜喧縺代ヱ繧ｿ繝ｼ繝ｳ\x91.AzureAD\AppData\Local\Temp\tmpfk_exue0\build\CMakeInit.txt
      -- Building for: Visual Studio 17 2022
      -- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.22631.
      -- The C compiler identification is MSVC 19.42.34435.0
      -- The CXX compiler identification is MSVC 19.42.34435.0

      *** CMake configuration failed
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for llama-cpp-python
Failed to build llama-cpp-python
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (llama-cpp-python)
```
`C:\Users\怜喧縺代ヱ繧ｿ繝ｼ繝ｳ`の部分が文字化けしてそうで怪しい。
(ユーザファイルが漢字なのが影響したかもしれません)

## ビルドエラーの解決方法
pip install のオプションでビルドせずにインストールしたら出来ました
``` :コマンドライン
pip install llama-cpp-python --prefer-binary --no-cache-dir --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
```
pipenvの場合はpipenv installでオプションのエラーになるのでpipenv.shellで仮想環境入ってから↑のコマンドでinstall
### 使用したオプション

- #### --prefer-binary
pipでソースからビルドする代わりに、可能な限りバイナリホイール（wheel）形式のパッケージを優先してインストールします。wheel形式はビルドされているので、エラーが発生した手順をスキップできるようです

- #### --no-cache-dir
pipはキャッシュを無効にして、最新のパッケージを直接取得します

- #### --extra-index-url
追加のパッケージインデックスURLを指定して、PyPIインデックスに加えてそこからもパッケージを検索します。`https://abetlen.github.io/llama-cpp-python/whl/cpu`でCPU対応版(GPUを使わない)を指定しています。

[pipのドキュメント](https://kurozumi.github.io/pip/reference/pip_install.html)
[pip逆引きリファレンス](https://qiita.com/fujine/items/11acac9aaa56791378b4#pip-install)

このオプションを使えば、raspberryPIとかc/c++のビルド環境作りにくいとこでも`llama-cpp-python`を使えそうです

# モデルのダウンロード

https://huggingface.co/mmnga/Llama-3-ELYZA-JP-8B-gguf

大体5GBぐらいです。
こちらからモデルをダウンロードしてプロジェクトの任意の位置に配置します(今回はmodels/)

# 実行する

### とりあえず日本語
```python:LocalLLM.py
from llama_cpp import Llama

llm = Llama(
    model_path="models/Llama-3-ELYZA-JP-8B-q4_k_m.gguf",
    chat_format="llama-3",
    n_ctx=1024,
)

response = llm.create_chat_completion(
    messages=[
        {
            "role": "system",
            "content": "あなたは誠実で優秀な日本人のアシスタントです。特に指示が無い場合は、常に日本語で回答してください。",
        },
        {
            "role": "user",
            "content": "以下の文章を要約してください。\
            # 文章\
            風が心地よく吹く穏やかな午後、公園にはさまざまな人々が集まり、\
            それぞれの時間を楽しんでいる。木々の葉は緑豊かに輝き、\
            小鳥たちのさえずりが周囲を包む中、子どもたちは元気に駆け回り、\
            砂場で遊ぶ笑い声が響いている。ベンチには読書を楽しむ人、\
            スマートフォンを見つめる人、会話に花を咲かせる友人同士の姿が見られる。\
            一方、ジョギングコースでは汗を流すランナーたちが一定のリズムで走り抜け、\
            池のほとりでは釣りを楽しむ人が穏やかな水面を眺めている。空は青く澄み渡り、\
            柔らかな日差しが地面を照らし出す。都会の喧騒から離れ、\
            この静かな空間で過ごす時間は、日々の忙しさを忘れさせ、\
            心に安らぎを与えてくれる。\
            自然と人々の調和が感じられるこのひとときは、何気ないけれど大切な瞬間だ。",
        },
    ],
    max_tokens=1024,
)

print(response["choices"][0]["message"]["content"])
```
450文字ぐらいの文章を要約してもらいました
↓

```:コマンドライン
以下は文章の要約です。

穏やかな午後、公園は人々の憩いの場となっていた。
木々の緑と小鳥のさえずりが心地よい中、子どもたちは元気に遊び、
ベンチでは読書や会話を楽しむ人々がいた。ジョギングコースではランナーが汗を流し、
池のほとりでは釣りを楽しむ人々 が穏やかな水面を眺めていた。
都会の喧騒から離れたこの空間は、日々の忙しさを忘れさせ、心に安らぎを与えてくれる。 
自然と人々の調和が感じられるこの瞬間は、かけがえのない大切な時間だった。
```
200文字ぐらいまで短くなりました。大体5~10分ぐらいかかりました

### 英語も試してみる

``` python:LocalLLM.py抜粋
messages=[
        {
            "role": "system",
            "content": "あなたは誠実で優秀な日本人のアシスタントです。特に指示が無い場合は、常に日本語で回答してください。",
        },
        {
            "role": "user",
            "content": "以下の文章を要約してください。\
            # 文章\
            Meet the Raspberry Pi Pico 2 W, a tiny board designed\
            around a microcontroller that lets you build hardware\
            projects at scale. Raspberry Pi is once again using the \
            RP2350, its own well-documented microcontroller.\
            But what is a microcontroller again? As the name suggests,\
            microcontrollers let you control other electronic\
            components or devices. Regular Raspberry Pis are general-purpose, \
            single-board computers, while microcontrollers are specifically \
            designed to interact with other things.\
            Microcontrollers tend to be cheap, small, and very power\
            efficient. As you can see in the picture above, the Pico 2 W \
            has dozens of input and output pins on the sides \
            (the small yellow holes all around the board) with which it\
            communicates with other components.\
            ",
        },
    ]
```
900文字ぐらいの英語の文章を要約させてみました
↓
```
要約:

Raspberry Pi Pico 2 Wは、微小なボードで、RP2350という自社のマイクロコントローラー
を使用して、ハードウェアプロジェクトを大規模に構築することができます。
マイクロコントローラーは、電子部品やデバイスを制御することができます。 
一般的なRaspberry Piは汎用のシングルボードコンピューターですが、
microcontrollersは特定の用途に設計されています 。
```
200文字ぐらいに短くなりました。大体20~30分ぐらいかかりました

## コードの解説
- `from llama_cpp import Llama`
Llamaをインポートしています
- `llm = Llama(
    model_path="models/Llama-3-ELYZA-JP-8B-q4_k_m.gguf",
    chat_format="llama-3",
    n_ctx=1024,
)`
Llamaクラスのインスタンスを作成しています

**パラメータの詳細**
**■ model_path**：使用するモデルファイルのパスを指定しています
**■ chat_format**：チャット形式を指定しています。今回はモデルファイルに合わせてLlama 3モデル用のフォーマットです
**■ n_ctx**：コンテキストウィンドウのサイズを1024トークンに設定しています
※コンテキストウィンドウ：モデルが一度に処理できるトークンの最大数

- `response = llm.create_chat_completion(
    messages=[
        {
            "role": "system",
            "content": "",
        }
    ],
    max_tokens=1024
)`
チャットとの応答の配列と最大トークン数を設定しています

**パラメータの詳細**
**■ messages**：チャットとの応答を設定しています
  - **role**：`user`,`assistant`,`system`から選んで、誰の応答なのかを設定します(`tool`,`function`というのもあるらしいのですが、標準では使わないようです)
    - `user`：ユーザーからの入力を表します
    - `assistant`：AIアシスタントの応答を表します
    - `system`：システムからの指示や設定を表します。制約等を定義するために使用されます

  - **content**：応答の内容です
  
**■ max_tokens**：生成するトークン数の最大を1024に指定しています


[llama-cpp-pythonのリファレンス](https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#high-level-api)
[llama-cpp-python.roleのリファレンス](https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#llama_cpp.llama_types.ChatCompletionRequestSystemMessage.role)

# おわりに
最後までご覧いただきありがとうございました。
ローカルでGPUがなくてもLLMが動かせるのはすごい便利でした。
次は、追加学習や既存のモデルの量子化などできるか調べてみようと思います。

# 参考にさせていただいた記事、サイト

https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#high-level-api

https://qiita.com/kenta1984/items/7233f8ec9d256f4fa4f7

https://qiita.com/nmorishita/items/dfe482d8eb7a1d6f0d9f

https://qiita.com/fujine/items/11acac9aaa56791378b4