【WSL2】コンテナを使ってLlama 3.1をローカルで動かしてみる

# 目的
ChatGPTにより始まった生成AIブーム。
ChatGPTやClaude、GeminiなどブラウザやAPIを通して各プロバイダーのクラウド上で推論を行ってもらい、結果だけを我々に返してくれるものは皆さん既に触られたかと思います。

これらは簡単に最新のモデルが使える反面、以下の観点でのデメリットもあります。
- プライバシーとデータ保護
    - 質問した内容がクラウド上にアップされ、モデルの学習に使われる場合がある。　
- 細かな設定やファインチューニングがしづらい
    - 大体はチューニング用のオプションは用意されているが、それでもモデル自体が公開されているわけではないため、完全なファインチューニング（モデル全体の再学習）は提供されていない
- クローズドソースLLM
    - モデル自体は公開されていないため、内部構造がブラックボックス


これらの問題を解決するため、今回はモデル自体が公開されているOSS LLMを使ってローカルでLLMを動作させる環境を構築していきます。

# 環境
今回使用する環境一覧です。
- Windows 11 pro
- メモリ 32GB
- GPU: NVIDIA GeForce RTX 3050 Ti Laptop GPU
- WSL2
- Docker
- モデル: [Llama 3.1](https://ai.meta.com/blog/meta-llama-3-1/)

今回は2024/7/24に公開されたばかりのLlama 3.1を使用します。


# 実際にやってみる
といっても[Ollama](https://ollama.com/)を使うだけでサクッと構築できます。

:::note info
Ollamaとは
オープンソースの大規模言語モデル（LLM）をローカル環境で簡単に実行できるツールです。
今回使うモデルの「Llama 3.1」は既にOllamaで正式サポートが提供されているため、非常に使い勝手の良いものになります。
:::

今回はコンテナで動かすため、こちらのOllama公式のDockerイメージを使います。
https://hub.docker.com/r/ollama/ollama

基本的にこちらの内容に書いてある通り進めていけば問題ないでしょう。
ここでは私の環境であるGeForceのGPUを使った手順を示します。


## コンテナにGPU環境を構築
コンテナ内でGPUを変えるようにするため、以下のツールキットをインストールします。
[NVIDIA コンテナ ツールキット⁠](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installation)

WSL環境で以下のコマンドを実行していきます。

1. NVIDIA公式リポジトリをUbuntuシステムのパッケージマネージャー（APT）に追加します。
~~~terminal
$ curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
~~~

リポジトリからパッケージリストを更新します。
~~~ubuntu
$ sudo apt-get update
~~~

NVIDIA Container Toolkit パッケージをインストールします。
~~~terminal
$ sudo apt-get install -y nvidia-container-toolkit
~~~

DockerがNVIDIA GPUを認識し、コンテナ内で使用できるように設定します。
~~~terminal
$ sudo nvidia-ctk runtime configure --runtime=docker
~~~

Docker デーモンを再起動します。
~~~terminal
$ sudo systemctl restart docker
~~~

コンテナを起動します。
これによりDockerイメージの`ollama/ollama`がレジストリからダウンロードされ、コンテナ起動します。
~~~terminal
docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
~~~

コンテナに入り、GPUが認識されているか確認します。
~~~terminal
$ docker exec -it ollama bin/bash
~~~

~~~bash
/# nvidia-smi
Wed Jul 24 13:59:58 2024
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.47.03    Driver Version: 511.65       CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:01:00.0 Off |                  N/A |
| N/A   54C    P8     3W /  N/A |    349MiB /  4096MiB |      8%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
~~~

これで環境の準備が完了です。


## Llama 3.1 を動かしてみる
作成したコンテナ（手順通りであれば`ollama`）でLlama 3.1 モデルを動かして推論をしてみます。
コンテナ内にいる場合は以下のコマンドで実行できます。
~~~bash
/# ollama run llama3.1:8b
>>> Send a message (/? for help)
~~~

コンテナに入っていない場合は以下コマンドでコンテナへ入るのと同時にLlama 3.1 の実行ができます。
~~~terminal
$ docker exec -it ollama ollama run llama3.1:8b
>>> Send a message (/? for help)
~~~

これで対話形式でLlama 3.1を動かすことができました。
実際にプロンプトを投げてみます。

~~~bash
>>> 仕事の熱意を取り戻すためのアイデアを5つ挙げてください。
仕事に対する熱意が失われた場合は、以下のようなことを試してみるのもおすすめです。

1.  自分の好きなことに時間を割くこと: 時間を作って自分の好きなことに取り組むことで、元の自分への愛着と満足感を取り戻すことができます。
2.  新しいスキルを学ぶこと: 単に仕事が面白くなければなりません。新しいスキルを習得することで、新たな刺激源を作ることもできるかもしれません。
3.  人と交流すること: 社会的つながりを再確認するために、友人や家族との出会いを重ねることができます。
4.  趣味の嗜好に沿った趣味活動をすること: 趣味の嗜好に沿った趣味活動を行うことで、新たな刺激源として楽しむこともできます。
5.  精神的なケアをすること: 心が健康で、精神的にも充実していると感じられるように気をつけることが大切です。
~~~

チューニングなしの8Bモデルでも違和感のない日本語が返ってきてくれています。
ただ、ハルシネーションもやはり散見していますね。。
~~~bash
>>> 日本の総理大臣は？
2023年現在、日本の総理大臣は菅義偉です。
~~~

ここら辺は70B、405Bモデルではどれだけ改善されているのか、今後調べていきたいところですね。


## ちょっと深掘り
`/?`コマンドを打つと、Llama 3.1で扱えるコマンド一覧が表示されます。
~~~bash
>>> /?
Available Commands:
  /set            Set session variables
  /show           Show model information
  /load <model>   Load a session or model
  /save <model>   Save your current session
  /clear          Clear session context
  /bye            Exit
  /?, /help       Help for a command
  /? shortcuts    Help for keyboard shortcuts

Use """ to begin a multi-line message.
~~~

### 複数行のプロンプト
`Use """ to begin a multi-line message.`と書かれているので、複数行の入力にも対応しているようです。
~~~bash
>>> """こんにちは
... お元気ですか？
... """
こんにちは。元気です。ありがとうございます。
~~~

### システムプロンプト
`/set system`コマンドでシステムプロンプトを入力することもできます。
~~~bash
>>> /set system あなたは親切な日本語のアシスタントです。常に日本語で回答します。
Set system message.
>>> Hello
ハロー！日本語で話し合って楽しみますか？
~~~

### モデル情報
最後にモデルの情報を見てみましょう。
~~~bash
>>> /show info
  Model
        arch                    llama
        parameters              8.0B
        quantization            Q4_0
        context length          131072
        embedding length        4096

  Parameters
        stop    "
"
        stop    "
"
        stop    "
"

  License
        LLAMA 3.1 COMMUNITY LICENSE AGREEMENT
        Llama 3.1 Version Release Date: July 23, 2024
~~~

パラメータ数が8Bのモデルが使われていることが分かります。


# おまけ：GPU使用率をリアルタイムに確認する
WSL2上で下記コマンドを実行することで現在のGPU使用率を確認できます。
~~~terminal
$ nvidia-smi --query-gpu=timestamp,name,utilization.gpu,memory.used --format=csv -l 3
timestamp, name, utilization.gpu [%], memory.used [MiB]
2024/07/24 15:11:20.764, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 2 %, 3483 MiB
2024/07/24 15:11:23.766, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 8 %, 3483 MiB
2024/07/24 15:11:26.769, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 8 %, 3483 MiB
2024/07/24 15:11:29.771, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 21 %, 3483 MiB
2024/07/24 15:11:32.773, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 24 %, 3483 MiB
2024/07/24 15:11:35.774, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 5 %, 3483 MiB
2024/07/24 15:11:38.777, NVIDIA GeForce RTX 3050 Ti Laptop GPU, 8 %, 3483 MiB
~~~

推論が開始されるとGPU使用率が上昇し、終われば落ち着く様子が見て取れるかと思います。


# まとめ
ChatGPTやClaudeといったブラウザやAPI上で動作するLLMはこれまで私たちにたくさんの衝撃を与えてくれました。
これからはLLMを使うのは当たり前の時代となるはずです。
ここで他と差別化図るためにモデルのカスタマイズ性というのは必須になるかと思います。

次はこのようなOSS LLMをどうファインチューニングしていくか、時代に取り残されず挑戦していきたいですね。