【Llama x Python】ローカルLLMを動かしてみた
まえがき
みなさんこんにちは、こんばんは、tsucciです。昨今のAIブームはすごいですよね。各クラウドベンダーから様々なサービスがリリース・アップデートされていて、世はまさに大AI時代という感じです。そんな中、若干逆行するようですが今回はローカルでLLMを動かしてみたいと思います。
動作環境
申し訳程度にメモリを増設してます。
ライブラリ
- Python 11.x
- llama.cpp
- llama-cpp-python
- huggingface-hub
Llama
Meta社が開発した大規模言語モデル(LLM)です。無料で商用利用可ということから今回採用しています。
llama.cpp
CPUだけで(GPU不要で)LLMを動かすためのプラットフォームです。今回検証機がMacなので導入します。
llama-cpp-python
C++で書かれた「llama.cpp」をPythonで動かすためのバインディングを行うライブラリです。
huggingface-hub
Hugging FaceからモデルをダウンロードするためのPythonライブラリです。
構成
tree -L 2
.
├── README.md
├── download.py
├── llama.cpp
├── main.py
└── models
└── gguf
量子化
多少の予備知識が必要なのでざっくりと説明します。
LLMは「ニューラルネットワーク」から構成されており、多層の「ニューロン」から成り立ち、各ニューロンには「重み」と呼ばれる係数が与えられています。多くの場合、重みは32bitの浮動小数点数であり、それが何億、何十億と集まっているため、一般的にLLMのモデルサイズは非常に大きくなり、大量のメモリやGPUを必要とします。
量子化とは、この重みを32bit→4bitなどと少ないbit数で表現することを言います。これによりモデルサイズやメモリ使用量を減らすことができますが、反面精度も落ちます。もちろん極力精度を落とさずサイズを減らすための工夫は考えられており、最近ではスマートフォンに搭載可能なレベルまで開発が進んでいるようです。
GGML/GGUF
ローカルLLMの量子化フォーマットの1つです。llama.cppでは元々GGMLというC言語で記述された「.bin」というフォーマットでしたが、より拡張性の高い「.gguf」というフォーマットに変更になりました。これによりLlama以外の言語モデルにも対応するようになりました。
動かしてみる
モデル
まずはHugging Faceからモデルをダウンロードします。今回はSwallow-7B-GGUFを採用します。「7B」というのは7億個のパラメータを学習させたモデルになります。最新では70Bとかのモデルもありますがスペック的に厳しいので(7Bでも10GB強のストレージ、10GB程度のメモリが必要になります)、精度は一旦度外視です。それでもダウンロードにはかなり時間がかかります。量子化モデルにはswallow-7b.Q6_K.gguf
を使用します。「Q6」というのが6bit量子化であることを表し、「K」は量子化手法を表しています。
# download.py
from huggingface_hub import snapshot_download
repo_id = "TheBloke/Swallow-7B-GGUF"
snapshot_download(repo_id=repo_id, revision="main", local_dir="./models/gguf")
llama.cpp
リポジトリをクローンしてビルドします。cmakeを使います。
brew install cmake
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
mkdir build && cd build
cmake ..
llama-cpp-python
実際にPythonで動かしてみます。簡単なチャットを作ります。
# main.py
from llama_cpp import Llama
model_path = "./models/gguf/swallow-7b.Q6_K.gguf"
llm = Llama(model_path=model_path, n_ctx=2048, chat_format="llama-2")
# Simple Chat
output = llm.create_chat_completion(
messages=[
{
"role": "system",
"content": "You are an assistant to answer questions about Japan.",
},
{"role": "user", "content": "Q: What is highest mountain in Japan?"},
],
stop=["[/INST]", "<</SYS>>"],
max_tokens=32,
)
print(output["choices"][0]["message"]["content"]) # type: ignore
仮想環境を作成して実行します(グローバル汚染、ダメ、ゼッタイ)
$ python -m venv .venv
$ source .venv/bin/activate
$ python main.py
...
A: Mount Fuji is the highest mountain in Japan.
おお、GPU不使用、Python環境でLLMが動作しました。やはりかなりのリソースを消費します。
ps u
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
user 21051 95.2 23.7 418419024 5969392 s000 R+ 5:11PM 1:46.62 python main.py
日本語も試してみます。
output = llm.create_chat_completion(
messages=[
{
"role": "system",
"content": "あなたは日本に関する質問に答えるアシスタントです。",
},
{"role": "user", "content": "Q: 日本で一番高い山は?"},
],
...
$ python main.py
...
A: 富士山です
おお、日本語で回答してくれますね。いい所ばかりお見せして申し訳ないですが、少し複雑な質問をするとあまり精度がいいとは言えませんでした。改善の余地はまだまだあると思います。
# {"role": "user", "content": "Q: 日本で初めて内閣総理大臣になった人物は?"},
A: 伊藤博文です。
# {"role": "user", "content": "Q: 日本の天皇は今何代目?"},
A: 125代目です。 <- !?
少し情報が古いのかもしれません。(2024年4月24時点で、126代目)
あとがき
ほんとは量子化も試してみたかったのですが、作業時間が確保できなかったり、ストレージが大変なことになったりで、断念してしまったことが悔やまれます。しかしながら、LLMの内部構造や内部事情を知るいい機会になったと思います。同時に無限のリソース(無償ではないですが)を持つクラウドサービスはやはり偉大だなと、改めて思う今日この頃です。