WSL2でCUDAを使ったローカルLLM環境を構築する

NVIDIAのGPUが普段遊んでいるので、WSL2で手軽に使えるローカルLLM環境を作ってみます。

参考にしたURL
https://docs.nvidia.com/cuda/wsl-user-guide/contents.html

# 環境の前提条件
- NVIDIAのGPUがインストール済み
- Windows11上のWSL2上にUbuntu 24.04LTSがセットアップ済み

# 予備知識
WSL2上のNVIDIAドライバはWindows版のドライバに依存します。
WindowsにNVIDIAドライバを入れて、WSL2上のUbuntuにCUDA Toolkitを入れる下準備が必要です。

# 手順

* Windows上でNVIDIAビデオドライバが古い場合は、アップデートします。
最新版だと、CUDA Toolkitのバージョンがないかもしれないので、事前に確認しておくとよいです。Windows のコマンドプロンプトで、nvidia-smiコマンドを使って確認します。
以下の場合、CUDAバージョンが 12.6 ということがわかります。

```batch: nvidia-smi --version
NVIDIA-SMI version  : 561.09
NVML version        : 561.09
DRIVER version      : 561.09
CUDA Version        : 12.6
```

  
* Linuxで、NVIDIAのCUDA Toolkit をダウンロードします。CUDAバージョンが同じものを選びます。 
https://developer.nvidia.com/cuda-toolkit
WSL2上に入れるので、Linux → x86_64 → WSL-Ubuntu → 2.0 → deb(local) の順で選びます。(Ubuntuのバージョンは特に関係ない模様・・・)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294242/b5d785ab-2057-9ac7-13bc-6ac8f7c2f993.png)

* 下のほうに、インストール手順が出てきますので、これを順番に実行していきます。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294242/eb5181b5-baed-8b40-c9ab-db6a3cfabd24.png)


> 本体ファイルは3.1GBほどあるので、少し時間がかかります。

```
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-ubuntu2404.pin
sudo mv cuda-ubuntu2404.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.6.1/local_installers/cuda-repo-ubuntu2404-12-6-local_12.6.1-560.35.03-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2404-12-6-local_12.6.1-560.35.03-1_amd64.deb
sudo cp /var/cuda-repo-ubuntu2404-12-6-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-6
```
* CUDA Toolkit のbin と lib64を、環境変数のPATHとLD_LIBRARY_PATH に設定しておきます。


```bash: ~/.bashrc
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
```

> 現在のシェルにも設定しておきます。
```bash
source ~/.bashrc
```
:::note info
ターミナルから起動したときは、~/.bashrcが読まれないので、~/.profile から.bashrcが読み込まれるようにしておきます。
```bash: ~/.profile
#!/bin/bash
if [[ -f ~/.bashrc ]] ; then
    .  ~/.bashrc
fi
```
:::

* NVIDIAのドライバーのバージョン確認しておきます。
ターミナルから以下のように打って、情報出てくれば、大丈夫です。

```bash: nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2024 NVIDIA Corporation
Built on Fri_Jun_14_16:34:21_PDT_2024
Cuda compilation tools, release 12.6, V12.6.20
Build cuda_12.6.r12.6/compiler.34431801_0```
```
:::note info
nvccコマンドがないと言われたら、今一度PATH環境変数を確認しましょう。
:::

* cuDNNを入れます。
https://developer.nvidia.com/cudnn

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294242/a0b06705-d040-582d-146f-874734035827.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294242/11c7280b-3a3f-c6db-ee31-b825caa5bdd2.png)

* これを順に実行していきます。
```
wget https://developer.download.nvidia.com/compute/cudnn/9.4.0/local_installers/cudnn-local-repo-ubuntu2404-9.4.0_1.0-1_amd64.deb
sudo dpkg -i cudnn-local-repo-ubuntu2404-9.4.0_1.0-1_amd64.deb
sudo cp /var/cudnn-local-repo-ubuntu2404-9.4.0/cudnn-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cudnn
```

* llama-cpp-pythonをインストールする前に、pythonの仮想環境をvenvで作っておきます。
仮想環境名： .llm

```bash
foo@ubuntu:~/project$ python3 -m venv .llm
foo@ubuntu:~/project$ source ./llm/bin/activate
(.llm) foo@ubuntu:~/project$
```

* llama-cpp-python をインストールします。
   `CMAKE_ARGS`にCUDAを有効にする定義を設定して実行します。コンパイルにはちょっと時間がかかります。
```bash
(.llm) foo@ubuntu:~/project$ CMAKE_ARGS="-DGGML_CUDA=on" FORCE_CMAKE=1 pip install llama-cpp-python --force-reinstall --no-cache-dir
```

* LLMモデルファイルをダウンロードして、Pythonスクリプトファイルを作るフォルダの近くに置きます。
日本語に対応した素晴らしいggufファイルをダウンロードします。5GB弱あります。
https://huggingface.co/elyza/Llama-3-ELYZA-JP-8B-GGUF
（以下のスクリプトからの参照先は、 `elyza` サブディレクトリにしています。）


```python: sample.py
from llama_cpp import Llama

llm = Llama(
    model_path="elyza/Llama-3-ELYZA-JP-8B-q4_k_m.gguf",
    n_gpu_layers=10    #GPUを使う指定をする。
)

prompt = """
質問: アメリカの首都はどこですか？
答え: """

output = llm(
    prompt=prompt,
    stop=["質問:", "答え:", "\n"],
    echo=True,
)

print(output["choices"][0]["text"])
```

こんな感じで答えてくれました。
```bash: 
質問: アメリカの首都はどこですか？
答え: ワシントンD.C.です。
```