rinna/japanese-gpt2-mediumをgguf化してllama.cppで動かす
Rinna 3.6Bとかではなく、japanese-gpt2-mediumをllama.cppで動かしたメモです。
モチベ
- 馴染みのあるモデルで入門したかった
- デカいモデルはストレージに悪い
- llama.cppを経由してSwiftで動かしたい
下準備
llama.cppをクローン。一度makeします。コミットIDは0becb22ac05b6542bd9d5f2235691aa1d3d4d307
でした。
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make
llama.cppの直下にrinnaをクローンします。スクリプトの都合に合わせてモデルをコピーしておきます。
git lfs install
git clone https://huggingface.co/rinna/japanese-gpt2-medium
cp rinna/spiece.model rinna/tokenizer.model
エディタでconvert-hf-to-gguf.py
を開きます。これはHugging FaceのモデルをGGUFに変換するスクリプトです。GPT-2
クラス内部にset_vocab
を追加します。これは現時点の実装がGPT-2モデルに対してBPEのトークナイザのみを想定している一方でrinnaはUnigramトークナイザが利用されているためです。
class GPT2Model(Model):
def set_vocab(self):
self._set_vocab_sentencepiece()
以上でスクリプトが動作するので、以下を実行します。数分もかかりません。
python3 convert-hf-to-gguf.py rinna/japanese-gpt2-medium
次に、エディタでllama.cpp
を開きます。これは名前通りllama.cpp
の本体ですが、llama_byte_to_token
の実装に以下を追加します。
これは次のような理由によります。
- llama.cppでは改行コードに対応するトークンが存在する前提で処理が行われますが、rinnaはこれを持っていないので、そのままだとエラーが発生します。
- rinnaのREADME.mdにも
tokenizer.do_lower_case = True
が「some bug of tokenizer config loading」のために必要である旨が書かれていますが、英語の大文字をトークン変換できずにエラーが発生することがあります。そこで小文字化をかけることによってこれを防ぎます。
switch (llama_vocab_get_type(vocab)) {
case LLAMA_VOCAB_TYPE_SPM: {
// ここから
if (ch == '\n') {
return vocab.linefeed_id;
}
if ('A' <= ch && ch <= 'Z') {
ch = ch - 'A' + 'a';
}
// ここまで
const char buf[7] = { '<', '0', 'x', hex[ch >> 4], hex[ch & 15], '>', 0 };
この変更を含めてもう一度make
します。
make
以上で完成です。
動かす
llama.cpp % ./main -m rinna/japanese-gpt2-medium/ggml-model-f16.gguf -p "最高のお味噌汁を作るには" -n 100
最高のお味噌汁を作るには、お米を炊くのと同じように、お水と麹菌を混ぜるのが重要です。 発酵させた後、3~4日程度でお味噌が仕上がります。1週間~10日ほど熟成させると「しつこい味噌」となり、味のクオリティもグンとアップします。 発酵期間は約6ヶ月。約1年以上の熟成を経て初めて「しつこい味噌」が完成するのです。 [end of text]
いい感じ!かはわからないですが、無事に動きました。
そのほかのメモ
実行時にllm_load_vocab: mismatch in special tokens definition ( 3540/32000 vs 7/32000 )
というWarningが出ていました。実装を読む限り「特殊トークンは通常トークン対の合成では得られない」という前提から「特殊トークンらしきトークン」を数え、それ(3540)が実際の特殊トークン数(7)と合わないと述べるものですが、Unigramトークナイザを使用しているモデルなので無視して良さそうです。
Discussion