llama.cppでllama 2を実行し、AMD Radeon RX 6900でGPUアクセラレーションを行う方法

この記事は2023年に発表されました。
オリジナル記事を読み、私のニュースレターを購読するには、**[ここ](https://quaily.com/lyric_kiji/p/llama-cpp-llama-2-amd-radeon-rx-6900-gpu-acceleration)** でご覧ください。

---

約1ヶ月前に[llama.cpp](https://github.com/ggerganov/llama.cpp)が[CLBlast](https://github.com/CNugteren/CLBlast)のサポートを追加しました。

そのため、AMDのRadeonグラフィックカードを使って簡単に動かすことができるようになりました。以下にUbuntu 22.04 Jammy Jellyfishでllama.cpp + llama2を実行する方法を紹介します。

## モデルのダウンロード

親切な[TheBloke](https://huggingface.co/TheBloke)が変換済みのLlama2モデルを提供してくれています：

> ・[**TheBloke/Llama-2-70B-GGML**](https://huggingface.co/TheBloke/Llama-2-70B-GGML)  
> ・[**TheBloke/Llama-2-70B-Chat-GGML**](https://huggingface.co/TheBloke/Llama-2-70B-Chat-GGML)  
> ・[**TheBloke/Llama-2-13B-GGML**](https://huggingface.co/TheBloke/Llama-2-13B-GGML)
> ・[**TheBloke/Llama-2-13B-chat-GGML**](https://huggingface.co/TheBloke/Llama-2-13B-chat-GGML)
> ・[**TheBloke/Llama-2-7B-GGML**](https://huggingface.co/TheBloke/Llama-2-7B-GGML)  
> ・[**TheBloke/Llama-2-7B-Chat-GGML**](https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML)

メモリに応じて適切なバージョンをダウンロードしてください。例えば、70bのバージョンは約31GB〜70GBのメモリを必要とします。私は[llama-2-13b-chat.ggmlv3.q4_K_M.bin](https://huggingface.co/TheBloke/Llama-2-13B-chat-GGML/tree/main)をダウンロードしました。

q4は4bitバージョンを意味します。ダウンロードしたモデルは`.bin`で終わるファイルで、`~/Downloads/llama-2-13b-chat.ggmlv3.q4_K_M.bin`に保存します。

## llama.cppのコンパイル

Ubuntuで関連ツールとライブラリをダウンロードします：

```bash
sudo apt install git make cmake vim
```

まず、Llama.cppのコードをクローンします：

```bash
git clone https://github.com/ggerganov/llama.cpp
```

公式サイトでは直接makeするように書かれていますが、私の環境では問題があったため、cmakeを使用しました：

```bash
mkdir build
cd build
cmake ..
cmake --build . --config Release
```

ビルドされたプログラムは`llama.cpp/build/bin`に配置され、`main`がコマンドプログラムのエントリーポイント、`server`がWebサーバーのエントリーポイントです。

これをコピーして名前を変更します：

```bash
cp ./bin/main/main ../llama-cpu
cd ..
```

## テスト

`llama.cpp/examples`にはいくつかのテストスクリプトがあるので、1つコピーして自分用に変更します：

```bash
cp examples/chat-13B.sh examples/chat-llama2-13B.sh
vim examples/chat-llama2-13B.sh
```

`examples/chat-llama2-13B.sh`の`MODEL`のモデルパスを自分のパスに変更します。例えば、

```bash
MODEL="/home/lyric/Downloads/llama-2-13b-chat.ggmlv3.q4_K_M.bin"
```

そして、`./main`を自分の名前`./llama-cpu`に変更します。

次に実行します：

```bash
./examples/chat-llama2-13B.sh
```

マシンの設定に応じて、少し待つとチャットが始まり、以下のような結果が得られます。緑の文字が自分の入力で、白の文字がLlama 2の応答です。

![結果のスクリーンショット](https://static.quail.ink/media/qkzugylq.webp)

## GPUアクセラレーションの有効化

AMDからドライバをダウンロードします：https://repo.radeon.com/amdgpu-install/

:::tip
注意：`5.*`バージョンが新しいバージョンであり、`2*.*.*`は旧バージョンです。旧バージョンは使用できません。
私がインストールしたのは`5.5`バージョンです：https://repo.radeon.com/amdgpu-install/5.5/ubuntu/jammy/ 
:::

インストール後、次を実行します：

```
amdgpu-install --usecase=opencl,rocm
```

Ubuntuで関連ライブラリをダウンロードします：

```bash
sudo apt install ocl-icd-dev ocl-icd-opencl-dev \
	opencl-headers libclblast-dev
```

`-DLLAMA_CLBLAST=ON`パラメータを追加して再コンパイルします：

```bash
cd build
cmake .. -DLLAMA_CLBLAST=ON -DCLBlast_dir=/usr/local
cmake --build . --config Release
```

これをコピーして名前を変更します：

```bash
cp ./bin/main/main ../llama-cl
cd ..
```

次に起動スクリプトを変更します：

```bash
vim examples/chat-llama2-13B.sh
```

`./llama-cpu`を自分の名前`./llama-cl`に変更し、最後から2行目に`--n-gpu-layers 40`を追加します。例えば：

```bash
./llama-cl $GEN_OPTIONS \
  --model "$MODEL" \
  --threads "$N_THREAD" \
  --n_predict "$N_PREDICTS" \
  --color --interactive \
  --file ${PROMPT_FILE} \
  --reverse-prompt "${USER_NAME}:" \
  --in-prefix ' ' \
  --n-gpu-layers 40
  "$@"
```

:::tip
`--n-gpu-layers`はトークン生成を加速するためにVRAMを使用します。私のグラフィックカードでは40に設定しましたが、任意の大きな数字を設定できます。例えば100000など。llama.cppは使用可能な最大レイヤー数を自動的に選択します。
:::

次に実行します：

```bash
./examples/chat-llama2-13B.sh
```

理論上、GPUアクセラレーションを使用すると待ち時間が大幅に短縮され、以下のようなメッセージが表示されるはずです：

```
ggml_opencl: selecting platform: 'AMD Accelerated Parallel Processing'
...
ama_model_load_internal: using OpenCL for GPU acceleration
llama_model_load_internal: mem required  =  710.19 MB (+ 1600.00 MB per state)
llama_model_load_internal: offloading 40 repeating layers to GPU
llama_model_load_internal: offloaded 40/41 layers to GPU
llama_model_load_internal: total VRAM used: 7285 MB
...
```

これでGPUアクセラレーションが有効になり、私の環境では生成速度が600+トークン毎秒に達し、非常に高速です。

## サーバーの実行

llama.cppはサーバーも提供しており、[公式ドキュメント](https://github.com/ggerganov/llama.cpp/blob/master/examples/server/README.md)を参考にしてください。

私の場合、以下のコマンドで直接実行します：

```
./server -m ~/Download/llama-2-13b-chat.ggmlv3.q4_K_M.bin \
	-c 2048 -ngl 40 --port 10081
```

次に、`http://localhost:10081`を開くとWeb UIが使用できるようになります。

このWebサーバーはAPIリクエストをサポートしており、例えば：

```
curl --request POST \
    --url http://localhost:10081/completion \
    --header "Content-Type: application/json" \
    --data \
		'{"prompt": "Build a website can be done in 10 steps:","n_predict": 128}'
```

これにより、自分のモデルを簡単に利用できます。

## トラブルシューティング

### OpenCLの権限問題

root権限でのみopencl関連の関数にアクセスできる場合があります。例えば、`clinfo`実行時にopenclが見つからない

と表示され、`sudo clinfo`では正常に表示される場合です。

その場合は、以下を実行します（LOGIN_NAMEを自分のユーザー名に変更してください）：

```bash
sudo usermod -a -G video LOGIN_NAME
sudo usermod -a -G render LOGIN_NAME
```

これで現在のユーザーに権限を付与します。

### Llama2の問題

OpenAIのChatGPTは多くのプロンプトエンジニアリングと最適化を経ていますが、自分で実行するLlama2はこれらを行っていません。そのため、Llama2が期待に応えない場合は、プロンプトを増やす必要があります。例えば、Llama2にJSONを出力させたい場合は、以下のようにプロンプトにいくつかのJSON生成例を提供する必要があります：

```markdown
次のテキストを読んで、ユーザーの意図を認識します。
可能な意図は次の通りです：

1. "食事"
2. "睡眠"
3. "豆を打つ"
9999. "不明な意図"

最も自信のある意図を返してください。
結果をJSON形式で返してください。
テンプレートは次の通りです：
{ "id": id, "intent": "USER'S INTENT", "confidence": 0.9 }

**指示: お腹が空いた**
{ "id": 1, "intent": "食事", "confidence": 0.9 }

**指示: 眠い、寝たい**
{ "id": 2, "intent": "睡眠", "confidence": 0.9 }

**指示: 豆はどこ？打ちたい**
{ "id": 3, "intent": "豆を打つ", "confidence": 0.7 }

**指示: 今何時？**
{ "id": 9999, "intent": "不明な意図", "confidence": 0.9 }
```

Web UIに設定する場合、以下のようになります：

![Web UIの設定例](https://static.quail.ink/media/1evu4n8q.webp)

実行結果は以下のようになります：

![実行結果のスクリーンショット](https://static.quail.ink/media/14rux80q.webp)

なかなか良い感じです。