LINE LLMをMacで動かす

先日、LINE(現LINEヤフー)のNLP Foundation Devチームから日本語言語モデルが公開されました。(以降LINE LLMと表記します)
36億パラメータの日本語言語モデルを公開しました
Instruction Tuningにより対話性能を向上させた3.6B日本語言語モデルを公開します

でも「NVIDIAGPUがないと動かないんでしょ」と、試していない人も多いんではないでしょうか。
そこでこのブログでは、MacでLINE LLMを動かす手順をまとめたいと思います。
Windowsや他のLLMでもほぼ同じ手順で動かせるはずです。

次のような手順で進めます。

  • pythonインストール
  • ライブラリインストール
  • 1.7Bのサンプルコードを動かす
  • チャットインタフェースで1.7Bを動かす
  • CTranslateによる3.6B
  • llama.cppによる3.6B

Pythonインストール

LLMはPythonで組まれていることが多く、LINE LLMも例外ではないため、Pythonが必要です。すでにPythonをインストール済みであっても、バージョンが3.8以上が推奨されているので、古いものがインストールされている場合はアップデートしておきましょう。
Windowsの場合は3.11を直接インストールするのが無難です。最新は3.12ですが、対応していないライブラリがあるようです。
https://www.python.org/downloads/

ここでMacでは、pyenvを使ってPythonをインストールする手順を紹介します。またpyenvはHomebrewを使ってインストールします。
そこで、Homebrew、pyenv、Pythonの順にインストールしていきますが、すでにインストール済みであれば適宜飛ばしてください。

Homebrewインストール

Macで標準的に使われているソフトウェア管理ツールであるHomebrewを使います。

https://brew.sh/index_ja.html

インストールは次のように。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

環境設定を次のように行います。

(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/my_name/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

確認のためにバージョンを表示してみます。なにか表示されたら成功。

% brew -v
Homebrew 4.1.5

pyenv

Homebrewから直接Pythonをインストールすることもできますが、バージョン管理も行えたほうがいいため、pyenvを使います。

brew install pyenv

インストールがうまく行われたか確認するために、バージョンを見てみましょう。

% pyenv -v
pyenv 2.3.24

環境変数の設定などを行います。 zshでは次のコマンドを実行します。

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc

bashなどzsh以外のシェルを使っている場合は、pyenvのReadmeを確認してください。
https://github.com/pyenv/pyenv#set-up-your-shell-environment-for-pyenv

Windowsでpyenvを使いたい場合はpyenv-winを使うといいようです。
https://github.com/pyenv-win/pyenv-win

Python

さて、ようやくPythonのインストールです。ここでは3.11をインストールします。最新は3.12ですが、ライブラリが対応してなかったりするようです。

pyenv install 3.11

環境変数の設定

pyenv global 3.11

バージョン確認

% python -V
Python 3.11.4

ライブラリの準備

まずはpipの更新

pip install --upgrade pip

LLM用のフォルダを作っておく

いろいろ動かすためにLLM用のフォルダを作っておきましょう。

mkdir llm
cd llm

venvを使って、LLM用の環境も作っておきます。

python -m venv .venv
source .venv/bin/activate

環境を切り替えると、プロンプトがこんな感じの表示になります。

(.venv) llm % 

venvの環境を抜ける場合にはdeactivateコマンドを実行します。

% ./deactivate

PyTorch

機械学習ライブラリ、PyTorchをインストールします。Macの場合はpipで普通にインストールすることになりますが、念のため公式サイトを確認しておくのがいいでしょう。
https://pytorch.org/get-started/locally/

環境を選ぶとインストール用のコマンドが表示されます。

それを実行

% pip3 install torch torchvision torchaudio

結局pipでインストールできるのだけど、インストール時に公式を確認しておくほうがよさそう。

Transformersをインストール

ほとんどのLLMはHuggingFaceのTransformersライブラリを使います。LINE LLMもTransformersを使っているのでインストールします。

pip install transformers sentencepiece

まずは動かす

1.7Bと3.6Bがありますが、1.7BをCPUで動かしてみましょう。
Hugging FaceのHow to useにサンプルコードがありますが、CPUで動かそうとするとこのままではエラーになって動きません。

https://huggingface.co/line-corporation/japanese-large-lm-1.7b-instruction-sft

CPUで動かすためにdevice=0を消します。残ったままだと次のようなエラーになります。

AssertionError: Torch not compiled with CUDA enabled

結果、次のようなコードになります。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
 
model = AutoModelForCausalLM.from_pretrained("line-corporation/japanese-large-lm-1.7b-instruction-sft")
tokenizer = AutoTokenizer.from_pretrained("line-corporation/japanese-large-lm-1.7b-instruction-sft", use_fast=False)
generator = pipeline("text-generation", model=model, tokenizer=tokenizer)
 
input_text = """四国の県名を全て列挙してください。"""
text = generator(
    f"ユーザー: {input_text}\nシステム: ",
    max_length = 256,
    do_sample = True,
    temperature = 0.7,
    top_p = 0.9,
    top_k = 0,
    repetition_penalty = 1.1,
    num_beams = 1,
    pad_token_id = tokenizer.pad_token_id,
    num_return_sequences = 1,
)
print(text)
# [{'generated_text': 'ユーザー: 四国の県名を全て列挙してください。\nシステム:  香川県、徳島県、愛媛県、高知県'}]

動かすと、モデルのダウンロードが始まります。

(.venv) llm % python line-llm.py
Downloading ()lve/main/config.json: 100%|██████████████████| 2.01k/2.01k [00:00<00:00, 2.17MB/s]
Downloading model.safetensors:  17%|████▎                    | 608M/3.51G [00:36<03:32, 13.6MB/s]

しばらく待つと、いろいろ表示されたあとで「四国の県名を全て列挙してください」の返答が表示されます。

You are using the legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This means that tokens that come after special tokens will not be properly handled. We recommend you to read the related pull request available at https://github.com/huggingface/transformers/pull/24565
Xformers is not installed correctly. If you want to use memory_efficient_attention to accelerate training use the following command to install Xformers
pip install xformers.
[{'generated_text': 'ユーザー: 四国の県名を全て列挙してください。\nシステム:  申し訳ありませんが、あなたはあなた自身の言語モデルであり、あなたのような言語モデルとして質問に答えることはできません。'}]

。。。「あなたのような言語モデルとして質問に答えることはできません」と言われてしまった。

少しパラメータをいじってtemperature = 0.8などにすると何か返してくれるはず。

[{'generated_text': 'ユーザー: 四国の県名を全て列挙してください。\nシステム:  香川県、徳島県、高知県、愛媛県'}]

Xformers関連の警告について

なんかXformers関連の警告が出ているので解消しようと思ったけど、ダメでした。特に変わらず。
やろうとしたことは次のとおり。

brew install libomp
brew install llvm
echo 'export PATH="/opt/homebrew/opt/libomp/bin:$PATH"' >> ~/.zshrc
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
pip install xformers

チャットインタフェース

チャットインターフェースで確認したくなりますよね。ということで試してみます。
UIにはgradioというライブラリを使います。

pip install gradio

コードはこちら。
https://gist.github.com/kishida/979ff57256896e8b9f801ed192b0e6ee

実行してlocalhost:7860にアクセスすると次のようになります。

CTranslate2で変換

1.7Bはそれなりの速度で動くとはいえ、なかなかいい感じの返答を返してくれません。3.6BはCPUで動かすには遅い。
そこで、CPUでの推論を速くしてくれるライブラリCTranslate2を使ってみます。
https://github.com/OpenNMT/CTranslate2

まずは必要なライブラリのインストール

pip install ctranslate2
pip install protobuf

そしてモデルの変換

ct2-transformers-converter --model line-corporation/japanese-large-lm-3.6b-instruction-sft --quantization int8 --output_dir line-3.6b-sft-ct2

次のコードで動かせます。

import ctranslate2
import transformers

model_name = "line-corporation/japanese-large-lm-3.6b-instruction-sft"
ct2_model = "line-3.6b-sft-ct2"

generator = ctranslate2.Generator(ct2_model)
tokenizer = transformers.AutoTokenizer.from_pretrained(model_name, use_fast=False)
prompt = "ユーザー: 日本の首都はどこ?\nシステム: "

tokens = tokenizer.convert_ids_to_tokens(tokenizer.encode(prompt, add_special_tokens=False))

results = generator.generate_batch(
    [tokens],
    max_length=256,
    sampling_topk=20,
    sampling_temperature=0.7,
)

text = tokenizer.decode(results[0].sequences_ids[0])
print(text)

チャットインタフェースは次のようになります。
https://gist.github.com/kishida/46e7d540c64b7b7a9eda3be987bbbc6b

llama.cpp

CPUを使った高速推論のフレームワークとしてllama.cppがあります。

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

ただ、llama.cppはGPTNeoXに対応していないので、mmngaさんがパッチをあてたものを使います。

git clone --branch mmnga-dev https://github.com/mmnga/llama.cpp.git

本家と区別するために名前を変えておきます。

mv llama.cpp llama.cpp.mmnga

そしてビルド

cd llama.cpp.mmnga
make -j gptneox

モデルの変換が必要ですが、すでに変換済みのモデルがあるので、こちらを使います。

https://huggingface.co/mmnga/line-corp-japanese-large-lm-3.6b-instruction-sft-gguf

次のファイルをダウンロードして、modelsフォルダに保存します。
line-corp-japanese-large-lm-3.6b-instruction-sft-q4_0.gguf

次のコマンドで推論できます。

./gptneox -m 'models/line-corp-japanese-large-lm-3.6b-instruction-sft-q4_0.gguf' -n 128 -p 'ユーザー: 吾輩って猫ですか? システム: '

llama.cppでは画像対応モデルも動くので、試してみてください。
画像対応モデルのLLaVAをMacで動かす - きしだのHatena

ファインチューニング

返答の形式を指定するなど、ベースモデルから出力の調整ことをファインチューニングといいます。
LINE LLMでも対話用にファインチューニングしたモデルが出ていますね。
ただ、Macでファインチューニングするのは厳しそうなので、Google Colabを使ってファインチューニングを試します。

語尾が「ござる」になるようにファインチューニングを行っていきます。 ChatGPTのような大きいLLMであれば「語尾をござるにしてください」で対応できますが、小さいLLM(小さい大規模言語モデル?)ではプロンプトでの出力指定は難しいので、ファインチューニングが必要です。

ここでは「ござるデータセット」を使ってファインチューニングを行います。
また、パラメータをすべて更新するチューニングはVRAMが足りないので、一部のパラメータだけを更新するLoRAというテクニックを使います。といってもPEFT(Parameter-efficient Fine-Tuning)というライブラリを使うだけです。

こちらのノートブックを読ませてひとつずつ実行するとチューニングが行えます。
40分ほどかかります。
https://gist.github.com/kishida/4fa70a66a7bc258a153ffbf178c04198