🦾

llama.cppを使ってMacのローカルPC内にLLMサーバを立てる

2023/12/07に公開

はじめに

この記事はllama.cppを使ってMacのローカル環境にLLMサーバを立てるための手順を紹介しています。

llama.cppのビルドが終わっている環境を前提としています。
ビルド方法がわからない場合は、まずはこちらの記事を見てみてください。

https://zenn.dev/michy/articles/d13d24e5f19c56

LLMサーバを立てると何が良いのか?

先の記事の通り、サーバを立てなくてもLLMの推論は可能ですが何度も繰り返し実行する場合はサーバを立てた方が圧倒的に処理が早くなります。

LLMの推論は、モデルを一度メモリに読み込んでから推論を実行するのですが、
サーバを建てずに実行すると1回推論する度にモデルを読み込み直してしまうため効率が悪いです。

サーバを立てることで、サーバが動いている間は読み込んだモデルが開放されないため連続で使う場合に効率が良いです。

実行環境

AppleSiliconのMac(私はM1-pro 32GBを使っています)

この記事ではpythonのnotebook環境(ipynb)を前提としています。

動作させるだけならpythonを使う必要はありませんが、LLMに色々なタスクをこなしてもらう場合ファイルの加工や前処理などもセットで必要になるためpython環境の構築をおすすめします。

動作方法

サーバの起動

まずはサーバを起動します。次のコードをllama.cppのディレクトリ内のターミナル内で実行してください。
このコードではcalm2を使って、自作のloraを使うことを前提にしています。
loraは必要ないので外してもOKです。-cはプロンプト長です、私は短くしていますが適宜長くしても問題ないです。

./server -m ./models/calm2-7b-chat.Q8_0.gguf --lora ./lora/lora.bin  -c 128

サーバにリクエストを投げる準備

以降の作業はnotebook環境で行ってください。

まずは諸々処理がしやすいように、2つ関数を定義します。
notebook内の適当なセルで実行してください。

文字列の中から指定部分だけ取り出す関数

def extract_between(text, start_str, end_str):
    start_index = text.find(start_str)
    end_index = text.find(end_str)

    if start_index == -1 or end_index == -1 or start_index + len(start_str) >= end_index:
        return ""

    return text[start_index + len(start_str):end_index]

リクエストを投げて結果を文字列で受け取る関数

リクエストする際にリクエスト用のjsonを作って文字列化しています。
先に作った関数を使い、結果だけを取り出します。

import json
import subprocess

def getResult(pmp: str):

    # JSONオブジェクトを作成
    request_data = {
        "prompt": "USER :" + pmp + "\nASSISTANT :",
        "n_predict": 128
    }

    # JSONオブジェクトを文字列に変換
    request_json = json.dumps(request_data)

    # curl コマンドを構築
    curl_command = [
        "curl",
        "--request", "POST",
        "--url", "http://localhost:8080/completion",
        "--data", request_json
    ]

    # コマンドを実行
    result = subprocess.run(curl_command, capture_output=True, text=True)

    return extract_between(result.stdout,"content\":\"" , ",\"generation_settings")

リクエストを投げる

次のコードを実行します。

prompt = "日本の首都は?"
result = getResult(prompt)

print(result)
# 東京でござる。

おまけ

ゲームのキャラクターのセリフを作ったり、翻訳タスクを行うときなどは結果をデータとして保存しておくと管理が楽です。
参考のコードとして、pandasのdataframeに保存する方法を紹介します。

ローカルLLMも賢くなってきたものの、やはりまだアウトプットをそのまま用いることは難しいです。
活用するには予め大量の推論をさせておいて、良かったアウトプットをピックアップして活用する方が現実的だとおもうのでこういった処理をおすすめします。
※商用利用する際はモデルの利用ルールに注意ください。

df = pd.DataFrame(columns=['prompt','result'])

for i in range(0,100):
	result = getResult(prompt)
        df.loc[len(df)] = [prompt,result]

df.to_csv("output.csv",index=False)

まとめ

いかがだったでしょうか?
今回は話題のLLMの使い方をまとめました。

Macのスペック持て余している方は是非今回の手順で使ってみてください!
私のTwitterではLLMに限らず、AIを活用した業務改善情報の発信をしておりますのでご興味のある方は是非フォローをお願いします。
https://twitter.com/Linus_lab

Discussion