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

AI・機械学習

はじめに

こんにちは!今回は、Google Colab上でkarakuri-lm-8x7b-instruct-v0.1-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をマウントします。これにより、Driveのファイルにアクセスできるようになります。
from google.colab import drive
drive.mount('/content/drive')

# 必要なライブラリをインストールします。
# litellm: 様々な言語モデルAPIを統一的に扱うためのライブラリ
# mlflow: 機械学習の実験管理ツール
# wandb: 機械学習の実験追跡と可視化ツール
# loguru: Pythonのロギングライブラリ
!pip install litellm mlflow wandb loguru

# llama.cppリポジトリをクローンします。
!git clone https://github.com/ggerganov/llama.cpp.git

# llama.cppのディレクトリに移動します。
%cd /content/llama.cpp

# buildディレクトリを作成し、そこに移動します。
!mkdir build
%cd build

# CMakeを使ってビルド設定を行います。CUDA対応をONにしています。
!cmake .. -DLLAMA_CUBLAS=ON

# ビルドを実行します。
!cmake --build . --config Release

# ビルドされたバイナリファイルを親ディレクトリにコピーします。
!cp bin/* ..

# 元のディレクトリに戻ります。
%cd /content

このコードブロックでは、環境構築に必要な作業を行っています。Google Driveのマウント、必要なライブラリのインストール、llama.cppのクローンとビルドを行っています。

モデルのダウンロード

次に、karakuri-lm-8x7b-instruct-v0.1-ggufモデルとELYZA-tasks-100データセットをダウンロードします。

# karakuri-lm-8x7b-instruct-v0.1-IQ4_XS.ggufモデルをダウンロードします。
# このモデルは、Hugging Faceのリポジトリからダウンロードされます。
!huggingface-cli download ReadyON/karakuri-lm-8x7b-instruct-v0.1-gguf karakuri-lm-8x7b-instruct-v0.1-IQ4_XS.gguf --local-dir .

# ELYZA-tasks-100データセットをクローンします。
# このデータセットは、日本語のタスク評価用のデータセットです。
!git clone https://huggingface.co/datasets/elyza/ELYZA-tasks-100/

このコードブロックでは、評価に必要なモデルファイルとデータセットをダウンロードしています。karakuri-lm-8x7b-instruct-v0.1-ggufモデルは、日本語に特化した大規模言語モデルです。

llama.cppサーバーの起動

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

# llama.cppサーバーをバックグラウンドで起動します。
# nohup: プロセスをバックグラウンドで実行し、ログアウトしても継続させます。
# --port 8181: サーバーを8181ポートで起動します。
# --n-gpu-layers 20: GPUで処理するレイヤー数を指定します。
# > output.log &: 出力をoutput.logファイルにリダイレクトし、バックグラウンドで実行します。
!nohup ./llama.cpp/llama-server -m karakuri-lm-8x7b-instruct-v0.1-IQ4_XS.gguf --port 8181 --n-gpu-layers 20 > output.log &

このコマンドは、ダウンロードしたモデルファイルを使ってllama.cppサーバーをバックグラウンドで起動します。ポート8181でサーバーが起動し、出力はoutput.logファイルに記録されます。

評価スクリプトの作成

次に、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

# パラメータ設定
MODEL_NAME = "karakuri-ai/karakuri-lm-8x7b-instruct-v0.1"
EXPERIMENT_NAME = "elyza-tasks-100-KARAKURI-3"
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 = 0  # 評価を開始するインデックスを設定します。

# 評価データセットを読み込む関数
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]"
        )

        # llama.cppサーバーにリクエストを送信します。
        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,
    )

    # LiteLLMを使用して外部モデル(ここではGemini)にリクエストを送信します。
    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

# 評価実行関数
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}")

        # WandBを使用している場合、結果をWandBに記録します。
        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()

# メイン関数
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(MODEL_NAME)

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

# Google Colab用のユーザーデータからWandB APIキーを取得し、環境変数に設定します。
from google.colab import userdata
os.environ['WANDB_API_KEY'] = userdata.get('WANDB_API_KEY')

# メイン関数を実行します。
main()

このスクリプトは以下の機能を持っています:

  1. 必要なライブラリとモジュールのインポート
  2. 評価データセットとプロンプトテンプレートの読み込み
  3. ローカルで起動したllama.cppサーバーへのリクエスト送信
  4. ELYZA-tasks-100の評価基準に基づくスコアリング
  5. 評価結果のWandBへの送信

各関数や処理にはコメントを付けて、初心者の方でも理解しやすいようにしています。

評価の実行とWandBへの結果送信

最後に、作成したスクリプトを実行して評価を行い、結果をWandBに送信します。上記のコードブロックの最後の部分で、main()関数を呼び出すことで評価が実行されます。

wandb結果

file

file

集計結果

file

ELYZA-tasks-100 評価結果シート
2024-03-12 評価結果 input(入力),output(正解例),eval_aspect(評価観点),elyza/ELYZA-japanese-Llama-2-70b,eval_elyza/ELYZA-japanese-Llama-2-70b,eval_elyza/ELYZA-japanese-Llama-2...

まとめ

以上で、Google Colab上でkarakuri-lm-8x7b-instruct-v0.1-ggufモデルを使って、ELYZA-tasks-100データセットの評価を行う方法をご紹介しました。この記事では、以下の内容を学びました:

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

これらの手順を踏むことで、大規模言語モデルの評価と結果の可視化が可能になります。モデルの性能向上や比較分析に役立てることができるでしょう。

初心者の方々も、この記事を参考に実際に試してみることで、大規模言語モデルの評価プロセスについて理解を深めることができるでしょう。それでは、記事の続きをお届けします。

注意点とTips

評価プロセスをスムーズに進めるために、いくつかの注意点とTipsをご紹介します。

  1. Google Colab環境について

    • Google Colabのセッションは一定時間経過すると切断されることがあります。長時間の評価を行う場合は、定期的に操作を行うか、有料版のColab Proの使用を検討してください。
  2. メモリ管理

    • 大規模なモデルを扱うため、メモリ使用量に注意が必要です。評価中にメモリ不足エラーが発生した場合は、torch.cuda.empty_cache()を実行してGPUメモリをクリアしてみてください。
  3. エラーハンドリング

    • 評価中に予期せぬエラーが発生する可能性があります。try-except文を使用して適切にエラーをキャッチし、ログに記録することをお勧めします。
  4. 中断と再開

    • 評価が途中で中断された場合に備えて、定期的に進捗状況を保存しておくと良いでしょう。START_INDEX変数を使用して、中断したポイントから再開することができます。
  5. WandBの活用

    • WandBのダッシュボードを活用して、評価結果をリアルタイムで可視化することができます。グラフや表を使って結果を分析することで、モデルの性能を直感的に理解できます。

発展的な取り組み

本記事で紹介した評価方法をマスターしたら、以下のような発展的な取り組みにチャレンジしてみるのも良いでしょう。

  1. 複数モデルの比較

    • karakuri-lm-8x7b-instruct-v0.1-ggufモデル以外の日本語モデルでも同様の評価を行い、性能を比較してみましょう。
  2. プロンプトエンジニアリング

    • モデルへの入力プロンプトを工夫することで、性能がどのように変化するか実験してみてください。
  3. カスタムデータセットの作成

    • ELYZA-tasks-100以外にも、自分で作成したカスタムデータセットで評価を行ってみましょう。特定のドメインや用途に特化したデータセットを使用することで、より実践的な評価ができます。
  4. 評価指標の拡張

    • 現在のスクリプトでは単一のスコアのみを使用していますが、複数の評価指標を組み合わせてより総合的な評価を行うことも可能です。
  5. 自動化とパイプライン化

    • 評価プロセス全体を自動化し、継続的にモデルの性能をモニタリングするパイプラインを構築してみましょう。

おわりに

本記事では、Google Colab上でkarakuri-lm-8x7b-instruct-v0.1-ggufモデルを使ってELYZA-tasks-100データセットの評価を行う方法を詳しく解説しました。大規模言語モデルの評価は、モデルの性能を客観的に把握し、改善点を見出すための重要なプロセスです。

初心者の方々にとっては、ここで紹介した手順を1つずつ丁寧に実践していくことで、モデル評価の基礎を身につけることができるでしょう。また、経験豊富な方々にとっても、この評価フレームワークをベースにさらに高度な評価手法を開発するきっかけになるかもしれません。

評価結果を分析し、モデルの強みや弱みを理解することで、より効果的なモデルの選択や改善が可能になります。ぜひ、この記事を参考に実際に評価を行い、日本語大規模言語モデルの可能性を探ってみてください。

最後に、本記事で紹介した手法はあくまで一例です。常に新しい評価手法や指標が提案されていますので、最新の研究動向にも注目しながら、自身のニーズに合った評価方法を探求し続けることをお勧めします。

モデル評価の世界は奥深く、まだまだ発見や改善の余地がたくさんあります。皆さんの挑戦が、日本語自然言語処理の発展に貢献することを願っています。頑張ってください!

📒ノートブック

Google Colab

コメント

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