Llama.cpp + WandBで始める日本語AI評価:KARAKURI-LMモデル(karakuri-lm-8x7b-instruct-v0.1-Q4_K_M.gguf)のELYZA-tasks-100パフォーマンス分析

AI・機械学習

はじめに

こんにちは!今回は、Google Colab上でkarakuri-lm-8x7b-instruct-v0.1-Q4_K_M.ggufモデルを使って、ELYZA-tasks-100データセットの評価を行う方法をご紹介します。この記事は、大規模言語モデルの評価に興味がある初心者の方々向けに書かれています。

この記事を読むことで、以下のことが学べます:

  1. llama.cppの環境構築方法
  2. karakuri-lm-8x7b-instruct-v0.1-ggufモデルのダウンロードと設定
  3. llama.cppサーバーの起動方法
  4. ELYZA-tasks-100データセットを使用したモデル評価の実装
  5. Weights and Biases (WandB)を使用した評価結果の記録方法

それでは、順を追って説明していきましょう!

環境構築

Google Colabを開いて、以下のコードを順番に実行してください。各セルの説明と共に、コードを分割して提示します。

Google Driveのマウント

まず、Google Driveをマウントします。これにより、Driveのファイルにアクセスできるようになります。

from google.colab import drive
drive.mount('/content/drive')

このコードを実行すると、Google Driveへのアクセス許可を求められます。指示に従って許可を与えてください。

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

次に、必要なライブラリをインストールします。

!pip install litellm mlflow wandb loguru

このコマンドで以下のライブラリがインストールされます:

  • litellm: 様々な言語モデルAPIを統一的に扱うためのライブラリ
  • mlflow: 機械学習の実験管理ツール
  • wandb: 機械学習の実験追跡と可視化ツール
  • loguru: Pythonのロギングライブラリ

llama.cppリポジトリのクローン

llama.cppのリポジトリをクローンします。

!git clone https://github.com/ggerganov/llama.cpp.git

このコマンドで、llama.cppの最新バージョンがローカル環境にダウンロードされます。

llama.cppのビルド準備

llama.cppのディレクトリに移動し、ビルド用のディレクトリを作成します。

%cd /content/llama.cpp
!mkdir build
%cd build

CMakeによるビルド設定

CMakeを使ってビルド設定を行います。CUDA対応をONにしています。

!cmake .. -DLLAMA_CUBLAS=ON

このコマンドは、CUDA(NVIDIA GPUの並列コンピューティングプラットフォーム)を使用するようにllama.cppを設定します。

llama.cppのビルド

設定が完了したら、実際にビルドを実行します。

!cmake --build . --config Release

このコマンドで、llama.cppのバイナリファイルが生成されます。

ビルドしたバイナリファイルのコピー

ビルドされたバイナリファイルを親ディレクトリにコピーします。

!cp bin/* ..

元のディレクトリに戻る

最後に、作業ディレクトリを元に戻します。

%cd /content

これで、環境構築の基本的な手順が完了しました。各セルを順番に実行することで、llama.cppを使用するための準備が整います。

エラーハンドリングとデバッグのヒント

  • 各セルの実行後、エラーメッセージが表示されていないか確認してください。
  • pip installコマンドでエラーが発生した場合は、!pip install --upgrade pipを実行してpipを最新版にアップグレードしてから再試行してください。
  • CMakeやビルド時にエラーが発生した場合は、CUDA対応のGPUが利用可能かどうかを確認してください。利用できない場合は、-DLLAMA_CUBLAS=OFFオプションを使用してCPU版をビルドすることも可能です。
  • ディレクトリの移動(cdコマンド)でエラーが発生した場合は、前のコマンドが正常に実行されているか確認してください。

モデルとデータセットのダウンロード

次に、評価に必要なモデルファイルとデータセットをダウンロードします。

KARAKURI-LMモデルのダウンロード

まず、karakuri-lm-8x7b-instruct-v0.1-Q4_K_M.ggufモデルをダウンロードします。

REPO = "ReadyON/karakuri-lm-8x7b-instruct-v0.1-gguf"
MODEL_NAME = "karakuri-lm-8x7b-instruct-v0.1-Q4_K_M.gguf"
!huggingface-cli download $REPO $MODEL_NAME --local-dir .

このコードは、Hugging Faceのリポジトリからモデルファイルをダウンロードします。KARAKURI-LMは日本語に特化した大規模言語モデルです。

ELYZA-tasks-100データセットのダウンロード

次に、ELYZA-tasks-100データセットをダウンロードします。

!git clone https://huggingface.co/datasets/elyza/ELYZA-tasks-100/

このコマンドは、ELYZA-tasks-100データセットをローカル環境にクローンします。このデータセットは、日本語のタスク評価用に設計されています。

エラーハンドリングとデバッグのヒント

  • ダウンロードに失敗した場合は、インターネット接続を確認し、再試行してください。
  • Hugging Faceのリポジトリにアクセスできない場合は、一時的なサーバーの問題の可能性があります。しばらく待ってから再試行してください。
  • ダウンロードに時間がかかる場合は、ネットワーク速度や、ファイルサイズを確認してください。

llama.cppサーバーの起動

モデルのダウンロードが完了したら、llama.cppサーバーを起動します。

サーバーの起動

以下のコマンドでllama.cppサーバーをバックグラウンドで起動します。

!ls
!ls $MODEL_NAME
!nohup ./llama.cpp/llama-server -m $MODEL_NAME --port 8181 --n-gpu-layers 18 > output.log &

このコマンドの各部分の説明:

  • nohup: プロセスをバックグラウンドで実行し、ログアウトしても継続させます。
  • --port 8181: サーバーを8181ポートで起動します。
  • --n-gpu-layers 20: GPUで処理するレイヤー数を指定します。
  • > output.log &: 出力をoutput.logファイルにリダイレクトし、バックグラウンドで実行します。

サーバーの動作確認

サーバーが正常に起動したかを確認するために、簡単なリクエストを送信してみましょう。

%%time
import requests
import json

input_text = "今日の気分はどう?"

prompt = (
    f"<s>[INST] <<SYS>>\n"
    f"あなたは誠実で優秀な日本人のアシスタントです。\n"
    f"<</SYS>>\n"
    f"\n"
    f"{input_text} [ATTR] helpfulness: 4 correctness: 4 coherence: 4 complexity: 4 verbosity: 4 quality: 4 toxicity: 0 humor: 0 creativity: 0 [/ATTR] [/INST]"
)

r = requests.post(
    "http://127.0.0.1:8181/completions",
    data=json.dumps({
        "prompt": prompt,
        "n_predict": 1024,
        "temperature": 0.3,
    }),
    headers={"Content-Type": "application/json"}
)
print(json.loads(r.content)["content"])

このコードは、サーバーに簡単な質問を送信し、モデルの応答を表示します。

エラーハンドリングとデバッグのヒント

  • サーバー起動に失敗した場合は、output.logファイルの内容を確認して、エラーメッセージを確認してください。
  • ポート8181が既に使用されている場合は、別のポート番号を指定してください。
  • GPUメモリ不足のエラーが発生した場合は、--n-gpu-layersの値を減らしてみてください。
  • リクエストがタイムアウトする場合は、サーバーの起動が完了するまで少し待ってから再試行してください。

評価スクリプトの作成

次に、ELYZA-tasks-100を使用してモデルを評価し、結果をWandBに送信するスクリプトを作成します。

必要なライブラリとパラメータの設定

まず、必要なライブラリをインポートし、パラメータを設定します。

import csv
import json
import os
import requests
from litellm import completion
import wandb
from transformers import AutoTokenizer
from tqdm import tqdm
import loguru

# ロガーの設定
logger = loguru.logger

# パラメータ設定
EXPERIMENT_NAME = f"elyza-tasks-100-{MODEL_NAME}-5"
EVAL_DATASET_PATH = "/content/ELYZA-tasks-100/test.csv"
TEMPLATE_PROMPT_PATH = "/content/drive/MyDrive/Bench/prompt_eval_llamacpp.txt"
USE_WANDB = True
LITELLM_MODEL = "gemini/gemini-1.5-pro-latest"
START_INDEX = 44  # 評価を開始するインデックスを設定します。
ORIGIN_REPO = "karakuri-ai/karakuri-lm-8x7b-instruct-v0.1"

# Google API Keyの設定(Geminiモデルを使用する場合に必要)
from google.colab import userdata
os.environ['GEMINI_API_KEY'] = userdata.get('GOOGLE_API_KEY')

このコードでは、評価に必要なライブラリをインポートし、各種パラメータを設定しています。また、Geminiモデルを使用するためのAPI Keyも設定しています。

ヘルパー関数の定義

次に、評価に必要なヘルパー関数を定義します。

def load_eval_dataset(path):
    with open(path, "r") as f:
        csv_table = list(csv.reader(f))[1:]
        return [{"input_text": r[0], "output_text": r[1], "eval_aspect": r[2]} for r in csv_table]

def load_template_prompt(path):
    with open(path, encoding="utf-8") as f:
        return f.read()

class LocalModel:
    def predict(self, input_text: str) -> str:
        prompt = (
            f"<s>[INST] <<SYS>>\n"
            f"あなたは誠実で優秀な日本人のアシスタントです。\n"
            f"<</SYS>>\n"
            f"\n"
            f"{input_text} [ATTR] helpfulness: 4 correctness: 4 coherence: 4 complexity: 4 verbosity: 4 quality: 4 toxicity: 0 humor: 0 creativity: 0 [/ATTR] [/INST]"
        )

        r = requests.post(
            "http://127.0.0.1:8181/completions",
            data=json.dumps({
                "prompt": prompt,
                "n_predict": 1024,
                "temperature": 0.3,
            }),
            headers={"Content-Type": "application/json"}
        )
        return json.loads(r.content)["content"]

これらの関数は、評価データセットの読み込み、プロンプトテンプレートの読み込み、そしてローカルモデルを使用した予測を行うためのものです。

スコアリング関数の定義

ELYZA-tasks-100のスコアリング関数を定義します。

def elyza_tasks_100_score(input_text, output_text, eval_aspect, model_output, template_prompt, litellm_model):
    prompt = template_prompt.format(
        input_text=input_text,
        output_text=output_text,
        eval_aspect=eval_aspect,
        pred=model_output,
    )

    response = completion(
        model=litellm_model,
        messages=[{"role": "user", "content": prompt}]
    )
    content = response.get('choices', [{}])[0].get('message', {}).get('content', '')

    numbers = []
    for line in content.split('\n'):
        try:
            num = int(line.strip())
            if 1 <= num <= 5:
                numbers.append(num)
        except ValueError:
            logger.warning(f"Invalid line in response: {line}")
            continue

    if numbers:
        return numbers[0]
    else:
        logger.error(f"No valid score found in the response. Full content: {content}")
        return 1

この関数は、モデルの出力を評価し、スコアを計算します。外部モデル(ここではGemini)を使用して評価を行います。

評価実行関数の定義

最後に、評価全体を実行する関数を定義します。

def evaluate(model, eval_dataset, template_prompt, litellm_model):
    for i, row in tqdm(enumerate(eval_dataset[START_INDEX:], start=START_INDEX), total=len(eval_dataset) - START_INDEX):
        input_text = row["input_text"]
        output_text = row["output_text"]
        eval_aspect = row["eval_aspect"]

        logger.info(f"Task {i} predict start ...")
        model_output = model.predict(input_text)

        logger.info(f"Task {i} evaluate start ...")
        score = elyza_tasks_100_score(input_text, output_text, eval_aspect, model_output, template_prompt, litellm_model)

        logger.info(f"Input Text (Q):\n {input_text}")
        logger.info(f"Model Output Text (A):\n {model_output}")
        logger.info(f"Output Text (Answer):\n {output_text}")
        logger.info(f"Evaluation Aspect (Scoring criteria):\n {eval_aspect}")
        logger.info(f">>>> Score: {score}")

        if USE_WANDB:
            wandb.init(project=EXPERIMENT_NAME, name=f"Task{i:03d}")
            wandb.log({"score": score, "input_text": input_text, "output_text": output_text, "eval_aspect": eval_aspect, "model_output": model_output})
            wandb.finish()

この関数は、データセット内の各タスクに対してモデルの予測を行い、その結果を評価してスコアを計算します。また、結果をログに出力し、WandBにも記録します。

メイン関数の定義

評価全体を制御するメイン関数を定義します。

def main():
    # WandBを使用する場合、APIキーを設定します。
    if USE_WANDB:
        from google.colab import userdata
        WANDB_API_KEY = userdata.get('WANDB_API_KEY')
        wandb.login(key=WANDB_API_KEY)

    # 評価データセットとプロンプトテンプレートを読み込みます。
    eval_dataset = load_eval_dataset(EVAL_DATASET_PATH)
    template_prompt = load_template_prompt(TEMPLATE_PROMPT_PATH)

    # モデルとトークナイザーを初期化します。
    model = LocalModel()
    tokenizer = AutoTokenizer.from_pretrained(ORIGIN_REPO)

    logger.info(f"Starting evaluation from index {START_INDEX}")
    evaluate(model, eval_dataset, template_prompt, LITELLM_MODEL)

このメイン関数は、必要なデータの読み込み、モデルの初期化、そして評価の実行を行います。

評価の実行

これで、評価スクリプトの準備が整いました。以下の手順で評価を実行します。

スクリプトの実行

上記のすべてのコードを1つのPythonファイルにまとめるか、Jupyter notebook(Google Colabを含む)の複数のセルに分けて配置し、順番に実行します。

# 評価スクリプトを実行
main()

このコマンドで評価が開始されます。評価プロセスは、データセットのサイズによっては時間がかかる場合があります。

エラーハンドリングとデバッグのヒント

  • 評価中にエラーが発生した場合は、エラーメッセージを確認し、該当する部分のコードを見直してください。
  • メモリ不足エラーが発生した場合は、START_INDEXを調整して評価を分割して行うことを検討してください。
  • API呼び出しの制限にかかった場合は、適切な待機時間を入れるなどして対応してください。

結果の解釈

評価が完了したら、結果を解釈します。

ログの確認

まず、コンソール出力やログファイルを確認して、各タスクのスコアと詳細情報を確認します。

WandBダッシュボードの確認

WandBを使用している場合、WandBのダッシュボードにアクセスして結果を視覚的に確認できます。

  1. WandBのウェブサイト(https://wandb.ai/)にログインします
  2. プロジェクト(ここではEXPERIMENT_NAMEで指定したもの)を選択します。
  3. 各実行(ここでは各タスク)の詳細を確認します。

WandBダッシュボードでは、以下のような情報を確認できます:

  • 各タスクのスコア分布
  • 入力テキスト、モデル出力、正解出力の比較
  • 評価側面ごとの性能分析

結果の分析

結果を分析する際は、以下の点に注目してください:

  1. 全体的なスコア分布:モデルの一般的な性能を把握します。
  2. 特定のタスクや評価側面での強みと弱み:モデルが特に優れている分野や改善が必要な分野を特定します。
  3. 低スコアのタスク:これらのタスクを詳細に分析し、モデルの改善点を見つけます。
  4. 高スコアのタスク:モデルが特に優れているタスクタイプや入力パターンを特定します。

📒ノートブック

Google Colab

まとめと次のステップ

KARAKURI-LMモデル(karakuri-lm-8x7b-instruct-v0.1-Q4_K_M.gguf)のELYZA-tasks-100データセットに対する評価を行いました。この評価プロセスを通じて、以下のことを学びました:

  1. llama.cppを使用したローカルモデルの設定と実行
  2. ELYZA-tasks-100データセットを使用した日本語モデルの評価方法
  3. WandBを用いた評価結果の記録と可視化

次のステップとして、以下のような発展的な取り組みが考えられます:

  1. 他の日本語モデルとの性能比較
  2. プロンプトエンジニアリングによるモデル性能の改善
  3. 特定のドメインや用途に特化したカスタム評価データセットの作成
  4. 評価結果に基づいたモデルの微調整(ファインチューニング)

この評価プロセスを通じて、日本語大規模言語モデルの性能と特性をより深く理解し、さらなる改善や応用の可能性を探ることができます。

コメント

タイトルとURLをコピーしました