LLMをHuggingFace形式からGGUF形式へ変換する


# はじめに

- LLMについて勉強するために、タイトルの通りLLMをHuggingFace形式からGGUF形式へ変換してみたいと思います。
- さらにModelfileを記述して、ollamaで動作させてみます。

## この記事の流れを図示(By Claude)

```mermaid
graph TB
    %% スタイリング
    classDef model fill:#e1f5fe,stroke:#01579b,stroke-width:2px
    classDef tool fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
    classDef file fill:#fff3e0,stroke:#e65100,stroke-width:2px
    classDef hub fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px

    %% ノード定義
    HF[HuggingFace形式モデル<br/>pytorch_model.bin<br/>model.safetensors<br/>config.json<br/>tokenizer.json]:::model
    GGUF[GGUF形式モデル<br/>model.gguf<br/>量子化済み<br/>単一ファイル]:::model
    HFHub[Hugging Face Hub<br/>モデル共有プラットフォーム]:::hub
    
    LLAMA[llama.cpp<br/>C++推論エンジン]:::tool
    OLLAMA[Ollama<br/>ローカルLLM実行環境]:::tool
    
    CONVERT[convert_hf_to_gguf.py<br/>変換スクリプト]:::file
    MODELFILE[Modelfile<br/>FROM ./model.gguf<br/>PARAMETER temperature 0.7<br/>SYSTEM ...]:::file

    %% 関係性の定義
    HFHub -->|ダウンロード| HF
    HFHub -->|ダウンロード<br/>GGUF版| GGUF
    
    HF -->|変換| CONVERT
    CONVERT -->|生成| GGUF
    
    GGUF -->|実行| LLAMA
    GGUF -->|参照| MODELFILE
    
    MODELFILE -->|ollama create| OLLAMA
    OLLAMA -->|内部で使用| LLAMA
    
    
    %% 追加の関係
    HF -->|save_pretrained| HFHub
    GGUF -->|アップロード| HFHub

    %% 注釈
    HF -.- NOTE1[transformersライブラリで使用]
    GGUF -.- NOTE2[メモリ効率的な推論用]
    OLLAMA -.- NOTE3[API/CLI提供]
```

## 用語の確認
### HuggingFace形式
transformersライブラリで扱う標準のモデル＆トークナイザー保存フォーマット。

### GGUF形式
モデルの高速読み込みと保存に最適化されたバイナリ形式で、推論用途に非常に効率的。
https://huggingface.co/docs/hub/gguf




# HuggingFace形式からGGUF形式へ変換

`transformers`からモデルをダウンロードします。サンプルプログラムなので軽量なモデルなら何でも良いのですがQwen2.5-0.5Bを利用します。

https://huggingface.co/Qwen/Qwen2.5-0.5B

huggingfaceにはモデルによってはGGUF形式に変換されたモデルがアップロードされていたりもしますが、この記事では自分で変換する過程をたどりたいので、利用しません。

https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF


```python
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-0.5B",
    torch_dtype=torch.bfloat16,
    device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B")
```

モデルを保存します。これが、いわゆるHuggingFace形式のモデルです。
```python
model.save_pretrained("./qwen2.5-0.5b")
tokenizer.save_pretrained("./qwen2.5-0.5b")
```

モデルを変換するために`llama.cpp`というツールを使います。ツール名が`*.cpp`なのはちょっとややこしいですね笑。

https://github.com/ggml-org/llama.cpp

llama.cppをcloneします。
```sh
git clone https://github.com/ggerganov/llama.cpp.git
```



llama.cpp直下に用意されている`convert_hf_to_gguf.py`というスクリプト（名前のままですね）で変換を行います。

```sh
cd llama.cpp
python convert_hf_to_gguf.py ./qwen2.5-0.5b --outfile ./qwen2.5-0.5b.gguf --outtype f16
```

ここまででGGUF形式への変換が完了しました。

# GGUF形式のモデルの実行

モデルを実行する方法はいくつかあります。

1. llama.cppを直接利用
1. ollamaなどのラッパーを利用

一旦は`llama.cppを直接利用`してみましょう。

先ほどソースコードをcloneしたのでbuildしても良いですが、省略してbrewコマンドでinstallしてみます。はじめからbrewでinstallしてもよさそうですが、先程利用したconverterが結局必要になるために、このような手順となりました。

```sh
brew install llama.cpp
```

これでllama-cliコマンドが実行できるようになります。

```sh
llama-cli -m qwen2.5-0.5b.gguf -p "こんにちは"
```

```sh
こんにちは。本日は、「明日の新卒採用、応募の対策」と題して、今後の社会課題から、就職活動の相談から、大学の入学を受ける際の相談まで、学部・大学、そして、人気の派遣社員を紹介していきます。 [end of text]
```
内容はアレですが、動作は問題なさそうです。
# ollamaからの呼び出し

ollamaはllama.cppのラッパーで、モデルの一元管理などいくつかの利点があります。

https://github.com/ollama/ollama

Modelfileという名前で以下のようなファイルを作成します。（Dockerfile likeですね。GGUFをベースイメージに相当する概念として整理しているんですね。）

```Dockerfile: Modelfile
FROM ./qwen2.5-0.5b.gguf
```

Modelfileはollamaの固有の機能で、プロンプトやテンプレートなどのメタ情報でモデルをカスタマイズできる仕組みです。

https://ollama.readthedocs.io/en/modelfile/

さて、Modelfileの作成が終わったら、`ollama craete`コマンドでモデルを作成します。

```sh
ollama create qwen -f Modelfile
```

こんな感じのlogが出力されます。
```sh
gathering model components
copying file sha256:32ab4932bbd95e09e5cf4d54722fb33a503740db6d5540d0841d651112d359fd 100%
parsing GGUF
using existing layer sha256:32ab4932bbd95e09e5cf4d54722fb33a503740db6d5540d0841d651112d359fd
writing manifest
success
```

モデルが作成されていることを確認します。

```sh
ollama list
```

```sh
NAME                                          ID              SIZE      MODIFIED
qwen:latest                                   1563d9c13a0c    994 MB    2 minutes ago
```

`ollama run`で実行します。

```sh
ollama run qwen
```


```sh
>>> こんにちは
！今回は、2018年の冬です。 本日は、寒いのに寒さがないとは思わなかったのですが、あたたかいお風呂ができたので、暖かくなるまで暖
房をするようにしました。 オーバーホール 今日は、とても冷たい天気でした。 西からも吹き込み雨が降り始めました。 そんな中、
2018年冬の始まりは、2月5日だったようです。 それから…
```

# まとめ
以下の流れを理解できました。
- HuggingFace形式からGGUF形式へ変換する
- llama.cppでGGUF形式のモデルを実行する
- Modelfileを作成して、ollamaでモデルを実行する


## 次は

以下のようなテーマで考えています。

- llama.cppのollamaの使い勝手を比較する
  - Modelfileの詳細などを記述する
- スクラッチ/ファインチューンしたモデルを実行する
- GGUFの仕様について確認する