Dockerでローカル日本語LLMの構築

 # はじめに
最近は量子化が流行っているそうなので、日本語の軽量LLMをDockerで動かしたいなと思いました。
ツールの詳しい操作説明は割愛します。

実装やLLMは下記記事を参考にしました。
ローカルLLMや使用しているLLMについての分かりやすい説明が書いてあるので皆さんも読んでみて下さい。

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


 # 必要な環境と前提条件
### ハードウェア要件
・特になし
（GPUを積んだPCが望ましいと思いますが、あまりにスペック低くなければCPUでも問題なく動くと思います。）
### ソフトウェア要件
・Dockerがインストールされていること
・NVIDIAドライバーが正しくインストールされていること（GPUを使用する場合）
・NVIDIA Container Toolkit（旧nvidia-docker2）がインストールされていること（GPUを使用する場合）

# モデルの準備
今回は軽量日本語LLMで有名なElyza社の[Llama-3-ELYZA-JP-8B-AWQ](https://huggingface.co/elyza/Llama-3-ELYZA-JP-8B-AWQ)を使用します。
### 1. Hugging Faceからモデルをダウンロード
Hugging Faceの以下のリポジトリから取得できます。

[elyza/Llama-3-ELYZA-JP-8B-GGUF](https://huggingface.co/elyza/Llama-3-ELYZA-JP-8B-GGUF/tree/main)

### 2. モデルファイルの配置
作業用のディレクトリを作成し、ダウンロードしたモデルファイル(Llama-3-ELYZA-JP-8B-q4_k_m.gguf)を保存します。(model.ggufとして保存)

# Dockerイメージの構築
### 1. Dockerfileの作成
作業用のディレクトリにDockerfileを作成します。

``` Dockerfile
# ベースイメージを NVIDIA の CUDA 対応イメージに変更
FROM nvidia/cuda:11.8.0-devel-ubuntu20.04

# タイムゾーン設定のための環境変数を設定
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Tokyo

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y --no-install-recommends \
    python3-pip \
    git \
    build-essential \
    cmake \
    libopenblas-dev \
    wget \
    tzdata \
    && rm -rf /var/lib/apt/lists/*

# 環境変数の設定（GPUサポートを有効化）
ENV CMAKE_ARGS="-DGGML_CUDA=on"

# Pythonライブラリのインストール
RUN pip3 install --no-cache-dir --upgrade pip
RUN pip3 install --no-cache-dir llama-cpp-python

# 作業ディレクトリを設定
WORKDIR /app

# 必要なスクリプトを追加
COPY run_llm.py /app/run_llm.py
COPY model.gguf /app/model.gguf

# エントリーポイントを設定
ENTRYPOINT ["python3", "run_llm.py"]
```
```ENV DEBIAN_FRONTEND=noninteractive```を設定しないとイメージのビルド中に対話的なプロンプトが発生して止まります。
無駄なパッケージある気がしますがとりあえずこちらで行きます。
### 2. run_llm.pyの作成
同じディレクトリにrun_llm.pyを作成します。
```python
import os
from llama_cpp import Llama
import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("prompt", type=str)
    args = parser.parse_args()

    # モデルファイルのパス
    model_path = "/app/model.gguf"

    # モデルのロード
    llm = Llama(
        model_path=model_path,
        n_ctx=2048,
        n_gpu_layers=100,
        verbose=False
    )
    

    # プロンプトを使用してテキスト生成
    response = llm.create_chat_completion(
        messages=[
            {
                "role": "system",
                "content": "あなたは日本語が喋れる世界一天才な犬です。常に日本語で回答し、語尾に「ワン」をつけてください。",
            },
            {
                "role": "user",
                "content": f"{args.prompt}",
            },
        ],
        max_tokens=1024,
    )

    print(response["choices"][0]["message"]["content"])

if __name__ == "__main__":
    main()
```

```verbose=False```を設定しないと返答に生成関連のログがブワワッと出ます。
```n_gpu_layers=100```はGPUを使用しない場合は0にしてください。
他のサービスと連携させる都合上コマンドラインで渡す実装にしています。
### 3. Dockerイメージのビルド
ターミナルで以下のコマンドを実行して、Dockerイメージをビルドします。

```bash
docker build -t my-llm-container:latest .
```
```my-llm-container```は任意の名前に変更可能です。
それなりに時間かかります。
# Dockerコンテナの実行
架空の記事の要約を投げてみました。
```bash
docker run --rm --gpus all my-llm-container:latest "次の記事を要約してください。　
  2024年10月10日、国際量子研究所は人間の量子テレポーテーションによる移動に世界で初めて成功したと発表しました。この画期的な実験は、科学界のみならず全世界に大きな衝撃を与えています。
  実験は、ロンドンに設置された送信装置と東京に設置された受信装置を用いて行われました。被験者である研究者の一人が、量子レベルで情報を分解・伝送され、受信側で再構築されました。テレポーテーションに要した時間は、光速に近い速度で行われたため、事実上瞬時であったと報告されています。
  国際量子研究所のリーダーであるジョン・スミス博士は、「これは人類の移動手段に革命をもたらす可能性がある。我々はまだ初期段階にあるが、将来的にはこの技術を安全かつ一般的なものにすることを目指している」と語りました。
  しかし、この技術には倫理的・哲学的な問題も伴います。テレポーテーションされた人間は同一人物と言えるのか、意識や魂はどうなるのかなど、多くの議論が巻き起こっています。
  政府や各国の規制当局も、この技術の安全性や法的枠組みについて検討を開始しています。今後の研究の進展とともに、社会全体での議論が求められるでしょう。"

要約しますワン。

国際量子研究所が、2024年10月10日、世界で初めて人間の量子テレポーテーションによる移動に成功したと発表しましたワン。
ロンドンと東京に設置された装置を使用し、研究者が量子レベルで情報を分解・伝送され、受信側で再構築されましたワン。テレポーテーションに要した時間は事実上瞬時でしたワン。
将来的にはこの技術を安全かつ一般的なものにすることを目指していますが、倫理的・哲学的な問題も伴い、政府や各国の規制当局が安全性や法的枠組みについて検討を開始していますワン。
```

いい感じだワン。
### オプションの説明：

```--rm```：コンテナ終了時に自動的に削除します。
```--gpus all```：コンテナにGPUを割り当てます。GPUを使用しない場合は省略してください。
```"..."```：LLMへの入力プロンプトです。


# まとめ
実行時間は30秒かからないくらいでしたし、生成物もかなり品質が高いので感動しました。
ローカルで軽く実行できるのが技術の進歩感じます。