Python ローカルPCにて、Llama-3モデルによる質疑応答の操作を実現する「llama-cpp-python, huggingface」

はじめに

 Llama-3(ラマ)モデルはMeta社のオープンソースのLLM(大規模言語モデル)です。これを元に日本語での精度を向上させたモデルがいくつか公開されています。無料で利用できます。

 本記事では、このLlama-3モデル(派生モデル)をローカルPCでChatGPTのように質問に対して応答するようなpythonの雛形コードを紹介します。使用するライブラリは「llama-cpp-python」「huggingface」です。LLMモデルの拡張子は「.gguf」です。

 例題として使用させて頂いたLLMモデルは、https://huggingface.co/elyza/Llama-3-ELYZA-JP-8B-GGUF です。CPUでも動作するほど、軽くて速いです。但し、精度はそれなりです。逆に、高精度と思われるモデルはこちらです。https://huggingface.co/mmnga/Llama-3.1-70B-Japanese-Instruct-2407-gguf/tree/main 但し、動作は激重です(ビデオメモリが多いグラフィックボードが必要と思います。私が使用しているのはGeForece3060(VRAM12G)でも実用に耐えない)。

使用するPythonライブラリのインストール

Llama-3モデルを使用するために、以下の2つのPythonライブラリをインストールします。llama-cpp-pythonはLlamaモデルの実行に使用し、huggingfaceはモデルのダウンロードにて必要となります。

pip install llama-cpp-python
pip install huggingface

 

モデルの準備と実行

次に、Llama-3モデルを使って実際にチャットボットを動作させるPythonコードを紹介します。このコードは、指定したフォルダにモデルが存在しない場合、Hugging Faceのリポジトリから自動的にダウンロードします。すでに存在する場合はダウンロードで上書きするようなことはなく、それを読み込みます。
ちなみに、本コードには他のLLMモデルについても、リポジトリとモデルファイル名をコメントアウトで載せていますので、検証したい人は試してみてください。

from llama_cpp import Llama

# モデルファイルが置かれているディレクトリのパス
local_dir = r"保存先のフォルダパス"

# モデルファイル名
repo_id = "elyza/Llama-3-ELYZA-JP-8B-GGUF"
filename = "Llama-3-ELYZA-JP-8B-q4_k_m.gguf"
#repo_id = "mmnga/rinna-llama-3-youko-8b-gguf"
#filename = "rinna-llama-3-youko-8b-IQ4_XS.gguf"
#repo_id = "mmnga/Fugaku-LLM-13B-instruct-gguf"
#filename = "Fugaku-LLM-13B-instruct-Q4_0.gguf"
#repo_id = "mmnga/HODACHI-EZO-Common-9B-gemma-2-it-gguf"
#filename = "HODACHI-EZO-Common-9B-gemma-2-it-Q5_K_M.gguf"

# モデルをロード
llm = Llama.from_pretrained(
    repo_id=repo_id,
    filename=filename,
    local_dir=local_dir,  # モデルファイルのディレクトリを指定
    verbose=False,
    use_gpu=True  # GPUを使用するオプションを追加
)

# チャット生成
def chat_bot_func(sentence):
    response = llm.create_chat_completion(
        messages=[
            {"role": "system", "content": "あなたは日本語で回答するアシスタントです"},
            {"role": "user", "content": sentence}
        ],
        temperature=0.7,
        top_p=0.9,
        #max_tokens=150,
        #stop=["\n"],
        stream=True,
    )

"""
def chat_bot_func(sentence):
    system_message = "あなたは日本語で回答するアシスタントです"
    user_message = f"{system_message}\n\n{sentence}"

    response = llm.create_chat_completion(
        messages=[
            {"role": "user", "content": user_message}
        ],
        temperature=0.7,
        top_p=0.9,
        stream=True,
    )
"""
    
    # 応答を逐次的に表示
    for chunk in response:
        if "choices" in chunk:
            content = chunk["choices"][0].get("delta", {}).get("content", "")
            print(content, end="")

    print('\nfinished')

# チャットボットへの質問
SENTENCE = """
pandasにて、A列の要素グループ別に、B列とC列のそれぞれの平均値と合計値を算出して、新規データフレームを作成したいです。
そのようなpythonコードをお願いします。
"""
chat_bot_func(SENTENCE)

コードの解説

  • ライブラリのインポートとモデルの設定
    Llamaクラスを使用して、指定したディレクトリ内のモデルをロードします。repo_idにはモデルのリポジトリIDを指定し、filenameにはモデルのファイル名を指定します。

  • チャット生成関数
    chat_bot_func関数は、指定したメッセージに対してLlamaモデルを使用して応答を生成します。messagesリストには、システムとユーザーの両方のメッセージが含まれ、これに基づいて応答が生成されます。stream=Trueにより、逐次応答が得られます。

  • 応答の表示
    forループ内で応答をリアルタイムに表示するようになっています。最後にfinishedと表示して、応答の終了を明確にします。

 

上記のコードを実行して得られた回答の結果(正しくない)

以下は、pandasを使用して、A列の要素グループ別に、B列とC列のそれぞれの平均値と合計値を算出して、新規データフレームを作成する方法です。

```
import pandas as pd

# 新しいデータフレームを定義
data = {
    'A': ['A1', 'A2', 'A1', 'A3', 'A2'],
    'B': [1, 2, 3, 4, 5],
    'C': [10, 20, 30, 40, 50]
}

df = pd.DataFrame(data)

# A列の要素グループ別に、B列とC列のそれぞれの平均値と合計値を算出
grouped_df = df.groupby('A').agg({'B': 'mean', 'C': 'sum'}).reset_index()

print(grouped_df)
``` 上記のコードでは、A列の要素グループ別に、B列の平均値とC列の合計値を算出しています。`groupby`関数を使用して、A列の要素グループに分類し、`agg`関数を使用して、各グループのB列の平均値とC列の合計値を算出しています。`reset_index`関数を使用して、グループ化されたインデックスを復元しています。

 

以上の方法により、Llama-3モデルを使用して、ローカルPC上でChatGPTのような会話エージェントを簡単に実現できます。しかし、AIによって得られた内容が、本当に正しいかは注意してください。精度と速度はモデルによって全く異なります(それはトレードオフの関係にあります)。

以上

#################################

(参考)以下では、上記で生成されたコードの間違いの内容についてと、正しいコードについて記載しました(ChatGPT無料版による回答です)。

上記の回答コードの問題点

生成されたコードは一見正しく見えますが、いくつかの重要な問題点があります。

  1. 要求と結果の不一致:

    • 質問では「B列とC列のそれぞれの平均値と合計値」を算出したいとしていますが、生成されたコードでは、B列の平均値とC列の合計値しか計算していません(下図)。つまり、B列の合計値とC列の平均値が計算されておらず、要求された内容と一致していません。
  2. agg関数の誤用:

    • agg関数は複数の集約関数を適用するために使用できますが、コードではB列にmeanのみ、C列にsumのみが適用されています。複数の集約関数を適用するためには、以下のように辞書内にリスト形式で集約関数を指定する必要があります。
    • # A列の要素グループ別に、B列とC列のそれぞれの平均値と合計値を算出
      grouped_df = df.groupby('A').agg({
          'B': ['mean', 'sum'],
          'C': ['mean', 'sum']
      }).reset_index()
  3. 結果の解釈の難しさ:
    • 上記の修正後のコードで得られるデータフレームは、多重インデックスとなり、初心者にとっては解釈が難しい場合があります。例えば、reset_indexだけでは不十分で、列名を平坦化する処理が必要になることが多いです。

正しいアプローチ

import pandas as pd

# 新しいデータフレームを定義
data = {
    'A': ['A1', 'A2', 'A1', 'A3', 'A2'],
    'B': [1, 2, 3, 4, 5],
    'C': [10, 20, 30, 40, 50]
}

df = pd.DataFrame(data)

# A列の要素グループ別に、B列とC列のそれぞれの平均値と合計値を算出
grouped_df = df.groupby('A').agg({
    'B': ['mean', 'sum'],
    'C': ['mean', 'sum']
}).reset_index()

# 列名をフラット化
grouped_df.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in grouped_df.columns]

print(grouped_df)

 groupbyで指定された列に対し、複数の集約関数を適用したデータフレームを得ることができます。列名のフラット化により、結果の解釈が容易になります。下図が、上記コードによる結果であり、質問に対する正解です。

<広告>