【Llama.cpp】学習したオリジナルLlama2ベースのLLMを量子化してGGUF形式にするまでの流れ

# はじめに

Llama2ベースのモデルをLoRAなどで追加学習したオリジナルモデルを、量子化してGGUF形式として保存する流れを備忘録としてまとめました。

# 1. ベースモデルとLoRAを統合する

```python
from peft import PeftModel
from transformers import AutoModelForCausalLM, LlamaTokenizer

# ベースモデルの読み込み
tokenizer = LlamaTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
model.resize_token_embeddings(len(tokenizer))

# LoRAの反映
model = PeftModel.from_pretrained(
    model,
    lora_adapter_dir,
    device_map={"":0}
)
model.eval()

# 統合したモデルの保存（HuggingFace形式）
merged_model = model.merge_and_unload()
merged_model.save_pretrained(output_dir)  
tokenizer.save_pretrained(output_dir)
```

# 2. Llama.cppの導入

https://github.com/ggerganov/llama.cpp

npakaさんのこちらの記事を参考にさせていただきました。

https://note.com/npaka/n/n0ad63134fbe2

Windowsでは、以下のC/C++ビルドツールを導入しておきます。

https://github.com/skeeto/w64devkit/releases

bin配下を環境変数に登録し、以下を実行してLlama.cppをビルドします。

```powershell
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make
```

少し待つと、Llama.cppフォルダの中に実行可能形式（.exe）が保存されます。エラーが発生せず問題なくビルドできれば完了です。


# 3. HuggingFace形式からGGUF形式に変換

数ビットに量子化する前に、一旦32ビット／16ビット／8ビットに変換をかけます。

今回は実質的に損失がない16ビットを選択しました。`--outtype`は、`f32`, `f16`, `q8`から選択できます。

```powershell
python convert.py path/to/original_model_HF/ --outtype f16
```

変換が終了すると、`ggml-model-f16.gguf`というファイル名でGGUFファイルが保存されます。

# 4. 数ビットに量子化

ビルドしてできた「quantize.exe」を利用して、`ggml-model-f16.gguf`を量子化します。

```powershell
quantize path/to/original_model_HF/ggml-model-f16.gguf Q5_K_M
```

量子化のビット数、手法の表記は以下から選択できます。

> Allowed quantization types:
   2  or  Q4_0   :  3.56G, +0.2166 ppl @ LLaMA-v1-7B
   3  or  Q4_1   :  3.90G, +0.1585 ppl @ LLaMA-v1-7B
   8  or  Q5_0   :  4.33G, +0.0683 ppl @ LLaMA-v1-7B
   9  or  Q5_1   :  4.70G, +0.0349 ppl @ LLaMA-v1-7B
  10  or  Q2_K   :  2.63G, +0.6717 ppl @ LLaMA-v1-7B
  12  or  Q3_K   : alias for Q3_K_M
  11  or  Q3_K_S :  2.75G, +0.5551 ppl @ LLaMA-v1-7B
  12  or  Q3_K_M :  3.07G, +0.2496 ppl @ LLaMA-v1-7B
  13  or  Q3_K_L :  3.35G, +0.1764 ppl @ LLaMA-v1-7B
  15  or  Q4_K   : alias for Q4_K_M
  14  or  Q4_K_S :  3.59G, +0.0992 ppl @ LLaMA-v1-7B
  15  or  Q4_K_M :  3.80G, +0.0532 ppl @ LLaMA-v1-7B
  17  or  Q5_K   : alias for Q5_K_M
  16  or  Q5_K_S :  4.33G, +0.0400 ppl @ LLaMA-v1-7B
  17  or  Q5_K_M :  4.45G, +0.0122 ppl @ LLaMA-v1-7B
  18  or  Q6_K   :  5.15G, -0.0008 ppl @ LLaMA-v1-7B
   7  or  Q8_0   :  6.70G, +0.0004 ppl @ LLaMA-v1-7B
   1  or  F16    : 13.00G              @ 7B
   0  or  F32    : 26.00G              @ 7B
          COPY   : only copy tensors, no quantizing

量子化が完了し、`ggml-model-Q5_K_M.gguf`など正常にGGUFファイルが保存されていることを確認したら完了です。


# 5. 量子化モデルを使って推論

Llama.cppを動作させるためのPythonライブラリをインストールします。

```powershell
pip install llama-cpp-python
```

「houou-7b」に合わせたプロンプトの場合は以下のようになります。

https://huggingface.co/moneyforward/houou-instruction-7b-v1


```python
from llama_cpp import Llama

llm = Llama(model_path="path/to/original_model_HF/ggml-model-Q5_K_M.gguf")
instruction = "日本のおすすめの観光地を3つ教えてください。"
input = ""

if input == "":
    prompt = f"""
    以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。

    ### 指示:
    {instruction}

    ### 応答:
    """
else:
    prompt = f"""
    以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。

    ### 指示:
    {instruction}

    ### 入力:
    {input}

    ### 応答:
    """

output = llm(
    prompt,
    temperature=0.5,
    stop=["### 指示:", "### 入力:", "### 応答:", "\n"],
    echo=True,
)
print(output["choices"][0]["text"])
```