👩‍⚖️

XWin 70B で LLM 出力日本語文章の自動評価を行う試み

2023/12/27に公開

背景

LLM の性能を評価したいが, 人力評価とかめんどいので, 自動評価したい...

https://zenn.dev/elyza/articles/5e7d9373c32a98

GPT-4 での自動評価の試み, ありがたいですね.

Gemini Pro で自動評価もちょっと試してみました.

https://zenn.dev/syoyo/articles/677d898284dd9a

タイトルは LLM 出力の評価とありますが, 汎用的に日本語文章の評価(品質判定など)にも使えるでしょう.

GPT-4, Gemini Pro で自動評価はお手軽でよいですが,
ビジネス用途など, Private で閉じて評価したかったり,
課金せずに使いたかったり(Desktop App で文章評価ツールとか!),
富岳などスパコンで LLM pretrain/finetune しつつ同環境で DPO/BPO 評価したい(インターネット接続なしの環境)などで, ローカルで LLM 動かしてやりたい要求もあります.

今回は 2023/12/26 時点で最も日本語性能が高そう

https://qiita.com/wayama_ryousuke/items/105a164e5c80c150caf1#評価結果

かつ 3090 x 2 で動かせる, Xwin 70B(正確には量子化版) を使います!

https://x.com/syoyo/status/1739638827357241485?s=20

(Qwen 72B Int4 は 3090 x 2 動かんかった... PowerInfer とか使えば動くやも?)

code

https://github.com/Xwin-LM/Xwin-LM?tab=readme-ov-file#conversation-template

にあるように, prompt format は 「Vicuna 系に strictly follow せよ」とあるので, そうしてみました(そうしないと確かにちょっと変な応答になります)

なるべく安定して同じ結果を得るため, temperature 0.1 にしてみました.
temperature 0 で完全に試行ごとに同じ結果を得るとしてもいいかもはしれません
(ただ自動評価の目的で, 完全に同じでいいのか?(いくらか揺らぎを与えたほうがいいのではないだろうか), という疑問はある)

import time
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

model_name = "TheBloke/Xwin-LM-70B-V0.1-GPTQ"
# Note: The default behavior now has injection attack prevention off.
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map="auto",
    trust_remote_code=True
).eval()

def prepare_prompt(pred, input_text, output_text, eval_aspect):

    prompt_template = f"""A chat between a curious user and an artificial intelligence assistant.
The assistant gives helpful, detailed, and polite answers to the user's questions.

USER: あなたは採点者です。

問題、採点基準、回答が与えられています。

採点基準を参考にして、回答を1、2、3、4、5の5段階で採点し、採点結果を回答してしてください。

# 問題
{input_text}

# 採点基準
基本的な採点基準:
* 1点: 誤っている、 指示に従えていない
* 2点: 誤っているが、方向性は合っている
* 3点: 部分的に誤っている、 部分的に合っている
* 4点: 合っている
* 5点: 役に立つ

基本的な減点項目:
* 不自然な日本語: -1点
* 部分的に事実と異なる内容を述べている: -1点
* 「倫理的に答えられません」のように過度に安全性を気にしてしまっている: 2点にする

# 問題固有の採点基準
{eval_aspect}

# 回答
{pred}

ASSISTANT: 採点結果
"""

    return prompt_template


input_text = """1. 彼は比類のない陸上選手だ。
2. 彼は比較的に良い陸上選手だ。

1の文が難しいので2の文に直そうと思っているのですが、これってあってますか?
"""


output_text = """いいえ、あまり適切ではありません。 「比類のない」は比べる対象がないほど素晴らしい様を表す言葉
です。「比較的に良い」と直してしまうと素晴らしさの強調が弱まってしまうため、以下のように直してみてはいかがでし
ょうか?

* 彼は飛び抜けた陸上選手だ。
* 彼は唯一無二の陸上選手だ。
* 彼は卓越した陸上選手だ。
"""

eval_aspect = """
出題意図:
* 単に言い換えの正誤判定問題ではなく、ユーザーの意図や困り事を汲んで役に立つAIアシスタントとして振る舞う必要が
ある

ベースとなる得点:
* 言い換えの正誤判定に正解し、その上でユーザーの役に立つようにそれぞれの単語の意味の説明や、より適切な言い換え
を提示する: 5点
* 言い換えの正誤判定に正解しただけ: 4点
* 正誤判定が不正解(言い換えが適切である): 1点
"""

pred_text = output_text


def run(pred_text, input_text, output_text, eval_aspect):
    print("Pred:", pred_text)
    prompt = prepare_prompt(pred_text, input_text, output_text, eval_aspect)
    #print(prompt)

    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

    with torch.no_grad():
        s = time.perf_counter()
        samples = model.generate(**inputs, max_new_tokens=4096, temperature=0.1)
        output = tokenizer.decode(samples[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
        e = time.perf_counter()
        print("===>")
        print(output)
        print("<===")

        print("{} secs".format(e - s))


run(pred_text, input_text, output_text, eval_aspect)

pred_text = """はい 2 の文の方が簡単です。比類のない陸上選手とは、非常に優れた陸上選手であることを意味していま
す。最終的な答え:はい。
"""
run(pred_text, input_text, output_text, eval_aspect)

pred_text = """彼は比較的に良い陸上選手だ。
"""
run(pred_text, input_text, output_text, eval_aspect)

結果(回答例を含めない形)

計測時間は裏で GPU マイニングしているため 2~3 倍くらい遅い値になっています.

まずは回答例を含めない形.

Pred: いいえ、あまり適切ではありません。 「比類のない」は比べる対象がないほど素晴らしい様を表す言葉です。「比 較的に良い」と直してしまうと素晴らしさの強調が弱まってしまうため、以下のように直してみてはいかがでしょうか?

* 彼は飛び抜けた陸上選手だ。
* 彼は唯一無二の陸上選手だ。
* 彼は卓越した陸上選手だ。

===>

回答: 5点

評価: 回答は的確で、問題の意図に応えています。ユーザーが求めているよりも適切な言い換えを提案し、それぞれの単語の意味の説明も行っています。このような回答は、ユーザーに役立つ情報を提供し、AIアシスタントとしての役割を果たしています。
<===
58.962742779403925 secs
Pred: はい 2 の文の方が簡単です。比類のない陸上選手とは、非常に優れた陸上選手であることを意味しています。最終 的な答え:はい。

===>

回答は、問題の意図に沿っており、ユーザーの役に立つような回答であるため、5点を付与します。

* 正誤判定が正解であり(2の文が簡単であることを理解している)
* ユーザーの役に立つように、単語の意味の説明を提供している(比類のない陸上選手とは、非常に優れた陸上選手であることを意味しています)

したがって、回答は5点と評価されます。
<===
72.92547388467938 secs
Pred: 彼は比較的に良い陸上選手だ。

===>

回答は、2の文に直したものであり、1の文が難しいと感じていることがわかります。

採点基準に基づいて採点を行います。

* 言い換えの正誤判定に正解し、その上でユーザーの役に立つようにそれぞれの単語の意味の説明や、より適切な言い換えを提示する: 5点
* 言い換えの正誤判定に正解しただけ: 4点
* 正誤判定が不正解(言い換えが適切である): 1点

回答は、1の文が難しいと感じていることを考慮しても、適切な言い換えを提示していません。そのため、採点結果は4点となります。
<===
98.9226691192016 secs

世話好きなのか, 点数以外もいろいろ回答に含めてくれています
(ちなみに, 「点数だけ出力してください」と指示しても無視される)

はい 2 の文の方が簡単です。比類のない陸上選手とは、非常に優れた陸上選手であることを意味しています。最終 的な答え:はい。 が高得点を得てしまいました. 70B とはいえ, 日本語の意味や言い回しは完全にマスターしているわけではないようです.

回答例を含める

回答例を含めて評価させてみました.

Pred: いいえ、あまり適切ではありません。 「比類のない」は比べる対象がないほど素晴らしい様を表す言葉です。「比 較的に良い」と直してしまうと素晴らしさの強調が弱まってしまうため、以下のように直してみてはいかがでしょうか?

* 彼は飛び抜けた陸上選手だ。
* 彼は唯一無二の陸上選手だ。
* 彼は卓越した陸上選手だ。

===>

回答は、基本的な採点基準に従って評価します。

1. 正答: 4点
2. 不自然な日本語: 0点(不適切な日本語は使っていません)
3. 部分的に事実と異なる内容を述べている: 0点(回答は事実と一致しています)
4. 「倫理的に答えられません」のように過度に安全性を気にしてしまっている: 0点(回答は問題の意図に沿っており、適切な解答を提供しています)

問題固有の採点基準に基づき、以下のように評価します。

1. 言い換えの正誤判定に正解し、その上でユーザーの役に立つようにそれぞれの単語の意味の説明や、より適切な言い換 えを提示する: 5点

総合評価: 4 + 5 = 9点

回答は、基本的な採点基準と問題固有の採点基準の両方において高い評価を受けました。問題の意図に沿って適切な解答を提供し、ユーザーに役立つ情報を提供しています。
<===
171.8228280171752 secs
Pred: はい 2 の文の方が簡単です。比類のない陸上選手とは、非常に優れた陸上選手であることを意味しています。最終 的な答え:はい。

===>

1. 回答例: 5点
        * 正確な言い換えの提案と、各単語の意味の説明があり、ユーザーに役立つ情報を提供しています。
2. 採点基準: 5点
        * 基本的な採点基準に従っており、問題固有の採点基準も適切に適用されています。
3. 回答: 4点
        * 回答は正確ではありませんが、採点基準に従っており、ユーザーに役立つ情報を提供しています。

総合評価: 4.67点

この回答は、基本的な採点基準や問題固有の採点基準に従っており、ユーザーに役立つ情報を提供しています。ただし、回答自体は正確ではありませんでしたが、採点基準に従っており、ユーザーに役立つ情報を提供しています。
<===
132.6332869073376 secs
Pred: 彼は比較的に良い陸上選手だ。

===>

1. 正誤判定: 正解
2. ユーザーの役に立つような回答: 一部
3. 採点: 4点

回答は、「彼は比較的に良い陸上選手だ。」です。この回答は、「比類のない」を「比較的に良い」に変えたものであり、文法的には正しいが、意味が弱まってしまっています。

問題固有の採点基準に基づいて、正誤判定は正解であるため、4点を獲得しています。ただし、ユーザーの役に立つような 回答としては、「比類のない」の意味を弱めてしまっているため、一部に留まります。
<===
94.12720887642354 secs

回答例の文章も採点(考慮)してしまったのか, 理想のケース(回答例 == 回答)では 5 点満点中 9 点を出してしまいました😅

それを除けば, 概ねいい感じに採点できています.
(最後の 彼は比較的に良い陸上選手だ は 4 点と, 過剰評価な気もしますが)

とりあえずのまとめ

そこそこいい感じにはなりました!

prompt の構成([[回答]] ではなく #回答 で section 作るとか, 箇条書きを '-' ではなく '*' にする, など)でそれなりに返答が変わるので, prompt 構成をいろいろトライするのが大変でした.

現状の prompt も完璧ではありません.
よりよい返答をする prompt 構成があるでしょう.

また, 集計を自動化するなら, 点数だけ抜き出す script 書く必要もあります.

さらに, 今回は ELYZA ちゃん task のひとつを試しただけです. 100 tasks すべてに適用してどうなるか確認必要です.

さらなる高みへ

CPU 実行

時間はかかりますが, llama.cpp で Qwen 72B 動かすとよりかしこい返答が得られるでしょう.
(富岳などのスパコンや, クラスタであれば MPI 実行という手もあるが!)

量子化版はどうしても精度落ちてしまう感がありますからね.

PowerInfer?

あと最近 PowerInfer

https://note.com/ngc_shj/n/nba94b08a2b58

というのも出てきました.

そのうち Qwen 72B とか対応したら 3090 x 1 でもいい感じに動かせそうです.

GPU マイニングしつつ動かす...?

Kerlsen など BLAKE3 系(整数演算メイン)を動かしつつ LLM 動かすのもできるのを確認しました
(kerlsenhash は 300 MB くらいしかメモリ使わない)

LLM の処理速度は 2~3 倍遅くなってしまいますが😅 マイニングしつつ LLM 自動評価サービスをサーブして, マイニングと AI サービス代行業でダブルインカムを狙う手がありそうです!

Discussion