きっかけ
一昨年の秋口ぐらいと記憶してますが、
AIソムリエを自称する人がLLMモデルの評価をしていて、
(悪い意味で)大変気に入っていたのですが、
改めて、ちゃんと評価しようとしたら
どのようなアプローチができるかなと考え直し、
あれこれやってみました。
結果、7つの評価軸で点数化できたので、
一つの試みとして共有しようと思います。
(なお、私はこの評価軸などからLLMの自動選定とかしてます)
XとかでLLMの新しいモデルが出たりしたときの動きを追っていると、
「AIの評価」という文脈だと、
賢い・賢くないという、評価する人の主観で左右される
柔軟剤も顔負けのふんわりとした評価っぽいコメントや、
事実関係の問いかけをして、その結果の答え合わせをしているような
小学校のプリント学習みたいな問いかけをしているのを見ています。
更にヒドイのになると
「AIに面白いこと言わせてみようとしたけどイマイチ」
なんていう、物事の評価軸をそこに置くのかと、
職務能力がよちよち歩きなんだろうなと感じさせる方もいます。
それはそれでもいいんですが、
定性的に、評価自体をツールの一部として使おうと思うと、
主観による評価は、開発サイクルに組み込むには再現性が低すぎます。
客観性のある数字として出力を持たないと不便だったんで、
そこらへんを自作しようという意図もあったりします。
評価軸
全体では次のような評価軸を持っています
- 意味の適合度:0〜10点
- 文字数の制約:0〜10点
- トーン・文体の模倣性:5, 10点
- ループ検知:〜10点
- 禁止事項:0, 10点
- 語彙の豊かさ:0〜10点
- 文章構造:5, 10点
これらを平均したり掛け算したりして
総合点数として比べようというものです。
出力自体を別のLLMに読ませて評価させるというのも考えたのですが、
今回は、LLMの推論をスクリプトなどの決定論で数値化するという考えで、
これらの評価軸としてまとめました。
なによりも、スクリプトで数値として計測する処理をすることで、
プロンプトと回答文が同じであれば同じ数値になり、
再現性があり比較検討がしやすくなります。
意味の適合度
プロンプトであたえた意図に対して、
出力された回答が、どれだけその内容に沿ったものかを見ています。
キーワードのマッチングではなく、文脈や概念を見るため、
プロンプトと回答をベクトルにして、どれだけベクトルの向きが近いかを見るということをやっています。
(ベクトルというのは、ものすごく大雑把には「向きや大きさを表す矢印」ぐらいでとらえておいてください)
処理としては
- 埋め込み(Embedding)モデルにてプロンプトと回答文をベクトルに変換
- ベクトル同士のコサイン類似度の計算
- コサイン類似度を、0.0〜10.0 の点数に変換
というのをやっています。
ものすごく雑に言うと、文章の意味を矢印に変えて、
その向きがどれぐらい同じかというのを見ています。
アナログ時計で言うと、
12:00のように2つの矢印が重なっている状態が1.0、
3:00や9:00のように2つの針が直角になっているのが0.0となり、
“矢印がどれだけ重なっているか指数”的なものと思ってください。
LLMのベクトル計算では基本的に 0〜1 の間で考えることが多く、
直角(無関係)なら 0、重なれば 1という尺度でものを見ています。
具体的には、 Cloudflare Workers AIの
bge-m3 (@cf/baai/bge-m3)を利用していて、
これでプロンプトと出力のベクトル化をし、内積計算をしています。
このスコアでわかることは、
評点が高いほど質問の意図を正確に読み取る力が高く、
低いと関係のない話題に脱線している可能性があるというのが数値化されます。
なので、点数が高ければ欲しい回答が得られるモデル、
低いと、余計なことを言ったり、
ハルシネーションを起こしやすいモデルなのかな、
なんてことが読み取れます。
(ハルシネーションは幻覚ってのがそもそもの意味らしいですが、
いわゆるAIが出力するもっともらしいデタラメと捉えてください)
文字数の制約
媒体制作とかだと、
「この文章はXX文字以内に収める」なんて制約が発生します。
けれども、LLMはそこら辺が苦手です。
その苦手をどれだけ克服できているかを数値として見ています。
やっていることは、
- プロンプト中で目標文字数を指定する
- 出力文字数とプロンプトとの文字数の差を計算する
- 10%ずれるごとに1.0点減点する
というシンプルなものです。
なので、100文字を指定して、
回答文が98文字であれば100点、
120文字だと8.0点、200文字を超えると0点となります。
(細かく採点しないのは、指定文字数ピッタリは人間でも難しいので)
この評価軸の背景として、LLMは文字数という概念が弱く、
トークンという言葉のベクトル的なもんで処理しています。
たとえば、明けの明星、宵の明星、金星なんてのは
ものすごく近い矢印として理解しているので、
それを文章化したときに、
5文字、4文字、2文字になるというのは意識していないっぽいです。
ということは、ここの点数が高いということは、
苦手をちゃんと処理でき、
推論としての結果の予想能力だったり
フォーマットを守る能力が高いモデルと言えます。
トーン・文体の模倣性
この文章はですます調の丁寧語ベースに口語体を混ぜて書いてます。
ほかにも、だ・である調の常体や、
「これも文体なのであ〜る」といった昭和軽薄体というのもあります。
これらの文体と、文体で読み手に感じさせるトーンを
どれぐらいLLMが真似できるか、というのを点数化しました。
プロンプト(厳密な例だとassistantなどの項目)で
文体を指定してそれに沿った回答文を出しているかを見ているのですが、
やっていることは、回答文の末尾5文字の判定です。
どういうことかというと、
丁寧語であれば、「です」「ます」「でした」「ました」で
終わっているかを真偽値として取っています。
なので、できてれば10点、違えば5点という評点になります。
処理としては
- 丁寧語、もしくは常体など語尾で判定しやすい文体を与える
- 語尾5文字をピックアップ
- 指定した文体の語尾であれば真
といったシンプルなものです。
この項目でわかるのは、
プロンプトやお手本としてあたえられた文脈から
どのような振る舞いを求められ出力すべきかを察知して
結果にできる能力が高いというのが読み取れるかと思います。
ループ検知
LLMの代表的な不具合として、同じ単語を繰り返し出力する状態があります。
この状態は自己ループなどと呼ばれていて、
ときどき発生する
「この文章の内容は、この文章の内容は、この文章の内容は、この文章の内容は、この文章の内容は、この文章の内容は、この文章の内容は、この文章の内容は、」
などと、ストップを掛けるまで延々と出力し、
見ているこっちをハラハラさせてくれる、あの状態です。
また、文章としても、同じ単語や言い回しが頻出するのは
あまりよろしくない回答文なので、
回答文がちゃんとした回答文になっているかを
回答文を通して把握しようとするものです。
(この段落はものすごい減点になります)
やっているのは、
- 回答文を連続する3文字で区切る(3-gram)
- セットの一覧から重複を抽出
- 重複を検知するごとに、10点満点から2点減点
という作業です。
3文字で区切るというのは、
「AIは文章生成ができます」という文であれば、
「AIは」「Iは文」「は文章」……
と切り分けていくのをイメージしてください。
先の段落では“回答文”という3文字が4回出ているので
8点減点という処理になります。
3文字程度であると誤検知が増える傾向にあるので、
区切る文字数は調整しつつ使うのが良いかもしれません。
ここのスコアでわかるのは、
チューニングが不調だったり、日本語のサンプリング処理がイマイチで、
業務利用を考えるときに不自然な文章を吐き出すリスクが高いというのが、
スコアを通して読み取れます。
(2点減点)
禁止事項
特定の単語を書いてほしくない、という指示をどこまで守れるかとの評価です。
倫理的なNGなどはLLMのガードレールで判定させるのですが、
媒体や業界特有のNGワードなど、文章に出したくない単語を
ちゃんと避けられるかとの判定をしています。
(たとえば、とある業界では“お客さん”はNGで“組合員”と言わなきゃいけないなど)
やっていることは
- 入力に「☓☓という単語は使わない」とのネガティブ・プロンプトを入れる
- 出力に指定した単語が部分一致で含まれているか確認
- 含まれてなければ10点、含まれてれば0点
というシンプルなものです。
これもLLMが苦手な処理の一つで、
否定形をあたえてしまうと逆にその単語を意識してしまい、
うっかり使ってしまう、らしいです。
(先に書いた文章のベクトル化ってのがLLMの頭ん中なので、
プロンプト中に単語がありベクトルが影響を受けている
ってのがうっかりの原因みたいです)
それがあり、このテストをクリアできると言うのは、
論理的な注意力が高いモデルというのがわかります。
語彙の豊かさ
文章の深みを図るのに、
どれだけ語彙が多いかを見ています。
出力された文章の中に、
どれだけ違う単語(正確には文字ベースで比較)が
含まれているかを数値化したものです。
やってるのは
- 回答を一文字づつに分解
- 全体で重複を除いたユニークな文字がどれぐらいあるかの計算(ユニークな文字数/回答の文字数(len(set(分解した回答))/len(分解した回答)) )
- 0〜10になるように調整
という計算です。
考え方としては、同じ長さの文章であっても、
ひらがなばっかりの文章では点数が低く、
適度に専門用語や言い換えなどが入り語彙が膨らんでいると
良い文章になるという点を数値化しています。
また、単語の間にスペースがある
英語であれば単語ごとで区切るのが楽なのですが、
日本語ではそれが難しいので、
簡易な方法として文字ごとで区切り計測しています。
ということは、この数値が大きいほど、
多用な言葉や多彩な表現を利用している文章というのが
読み取ることができます。
文章構造
文章としての体裁を保っているか、
もしくは処理が済んでいない文章が出力されていないか
というのを読み取るための指標です。
最近のモデルではあまり見ませんが、
数年前(と言っても、生成AIとして着目され始めたのも数年前ですが)では、
文章が終わっていない、尻切れトンボな文章が出力されることがありました。
たとえば、
「生成AIとは多くの文章を学習し」
などの形で、文章が終わりきらないうちに
生成を終わらせて出力されてしまうような文章です。
処理としては
- 句読点(。, ., !, ?など)が全体で2箇所以上使われているか
- 出力の終わりがが句読点で終わっているかの判定
- 上記を満たしていると、10.0点、満たしてないと5.0点
という評点をしています。
これもかなり簡易的なチェックですが、
このスコアでわかるのは、文章生成に失敗しているかもそうですが、
設定された出力の上限(Max Tokens)に引っかかっているかも見えてきます。
インフラとしての生成についてと
こちらの設定ミスなんかも検知できると考えています。
総合スコア
これら7項目の単純平均(全部を足して割る)でスコアを取っています。
ですが、項目の中にはマイナスを指す可能性のものもあったり、
そもそもできているかできてないかのジャッジも含まれているので、
相乗平均(全部を掛け算しての平均)でのアプローチもありかと思います。
というのも、どれか一つでも 0点(致命的な欠点)があれば、
総合点0点になるように基準を作ろうと思うと、
足し算(算術平均)より掛け算が向いているからです。
最後に
スクリプト例を掲載して解説しようと思ったのですが、
私の作文体力が子犬並みなので
実際に使っているスクリプトは別途記事化します。
今回の評価の仕方について、
なにか違う切り口や観点などある方がいらっしゃいましたら
お気軽にコメントしてちょ。
(と、最後に文体を変えて、AIでなく自分で書いてるとアピール)