見出し画像

【ローカルLLM】Gradioとllama-cpp-pythonで日本語チャットボットを作る

  • 先日の記事に続き、ウェブUI用のPythonライブラリ「gradio」を使って、簡単なチャットボットを作ってみた記録。

  • 今回はLlama系の言語モデルを使いたいので、モデルとgradioUIをつなぐPythonバインディングに「llama-cpp-python」を使用。これにより軽量な量子化モデル(GGUF)を扱える。

ひな形を探す

  • Hugging Face Spaces用のウェブUIサンプルを公開しているRepoがあり、ここにgradio+llama-cpp-pythonの例もあると教えてもらった。

  • ここでは、モデルに「Llama-2-7B-Chat-GGML」を使っている。

  • このサンプルを少し加工して使えばいいな、と思ったが、日本語で使おうとするとちょっと問題あり。

llama-cpp-pythonで漢字が欠落

  • どうやら、llama-cpp-pythonでは出力時に漢字が部分的に欠ける問題があるらしい。例えば以下のテキストでは、私、音声、意味、多数といった語彙に欠落が起きている。

「llama-2-Chat-13B」+「llama-cpp-python」の出力
  • この事象はStreamオプション(ウェブのChatGPTのように、テキストを1トークンずつ流れるように出力させる)を指定したときだけ起きる模様。Stream無効に修正すると、漢字は欠けなかった。

  • ちょっと調べてみると、こちらのツイートなどで既に同じことが指摘されていた。llama.cpp本体の問題ではなさそう。

OpenBuddy-Llama2を使う

  • 「Llama-2-Chat」以外のLlama系モデルを使った場合も、llama-cpp-python経由で出力すると軒並みこの現象が起きる。

  • ただ自分が試した範囲だと、昨日紹介した「OpenBuddy-Llama2-13B」では漢字が欠けずに出力できる様子。

  • このモデルはCJK文字のサポートをうたっているので、トークナイザー?とかが漢字用に調整されてるのかもしれない。

  • とりあえず今回はこのモデルをllama.cppでGGUFフォーマット(.gguf)に変換して使用することに。

コード

  • あとは、先ほどのサンプルをローカルで使うようにエイヤーで修正。

  • ついでに、プロンプトフォーマットもシンプルなVicunaスタイルに変更。system_messageも英語から日本語に変更した。

  • Google Colabで試す用のコードは下記。ランタイムをGPUに変更のこと。

# ライブラリのインストール
!CMAKE_ARGS="-DLLAMA_CUBLAS=on" FORCE_CMAKE=1 pip install llama-cpp-python
#(CPUで実行する場合)!pip install llama-cpp-python
!pip install gradio

# モデルのダウンロード
!wget https://huggingface.co/TFMC/openbuddy-llama2-13b-v11.1-bf16-GGUF/resolve/main/ggml-model-q4_m.gguf

# ウェブUIの起動
import os
import gradio as gr
import copy
import time
from llama_cpp import Llama

llm = Llama(
    model_path="ggml-model-q4_m.gguf",
    n_ctx=2048,
    n_gpu_layers=100, #CPUで実行する場合は削除
)

history = []

system_message = """
あなたはAIアシスタントです。
"""

def generate_text(message, history):
    temp = ""
    input_prompt = f"{system_message}"
    for interaction in history:
        input_prompt = input_prompt + "\nUSER: " + str(interaction[0]) + "\nASSISTANT: " + str(interaction[1])
    input_prompt = input_prompt + "\nUSER: " + str(message) + "\nASSISTANT: "

    output = llm.create_completion(
        input_prompt,
        temperature=0.7,
        top_p=0.3,
        top_k=40,
        repeat_penalty=1.1,
        max_tokens=1024,
        stop=[
            "ASSISTANT:",
            "USER:",
            "SYSTEM:",
        ],
        stream=True,
    )
    for out in output:
        stream = copy.deepcopy(out)
        temp += stream["choices"][0]["text"]
        yield temp

    history = ["init", input_prompt]


demo = gr.ChatInterface(
    generate_text,
    title="Japanese chatbot using llama-cpp-python",
    description="",
    examples=["日本の四国にある県名を挙げてください。"],
    cache_examples=True,
    retry_btn=None,
    undo_btn="Remove last",
    clear_btn="Clear all",
)
demo.queue(concurrency_count=1, max_size=5)
demo.launch(debug=True, share=True)
愛媛の県庁所在地が分からなかったらしい。ちょっとかわいい。


参考

「llama-cpp-python」の基本的な使い方は以下の記事が分かりやすかった。※ ちょうど数日前に、llama.cppのファイルフォーマットがGGML(.bin)からGGUF(.gguf)に切り替わったので留意。

なお「Rinna」などGPT-NeoX系の日本語LLMは、現時点では「llama-cpp-python」だと使えない(llama.cpp本体はGPT-NeoX対応済み)。ただ「CTranslate2」という別のPythonバインディングが利用できる。