見出し画像

LLMキャラ付けファインチューニング:プロンプトエンジニアリングとの比較

研究開発本部 海老原樹

はじめに

エッジデバイスで動作するLLM(Large Language Model)に関する、シリーズ記事の第二回目は、LLMのファインチューニングについてです。ChatGPTやLLMを触ったことのある皆さんの中には、もっと賢くしたい、自分好みキャラ付けしたいと思った方は多いのではないでしょうか。そんな、「LLMの出力を賢くしたり、キャラ付けする」方法の一つに、ファインチューニングがあります。

今回は実際にローカル環境でLLMのファインチューニングを通してLLMの語尾を変える簡単なキャラ付けを行います。同じくLLMの出力をカスタマイズする方法であるプロンプトエンジニアリングとの比較や学習するデータ量を変える実験を通して、ファインチューニングの効果を調査します。
自分でLLMをファインチューニングしてみたいと思っている方の参考になれば幸いです。

忙しい人のために

  • オープンソースの言語モデル「stabilityai/japanese-stablelm-instruct-alpha-7b-v2」をLoRAを用いてファインチューニングをして語尾を変える実験を実施。

  • ファインチューニングの学習データ数が500個以上が有効であることを確認。

  • プロンプトエンジニアリングでは語尾変更が安定せず、ファインチューニングがより効果的であることを確認。


LLMのファインチューニングについて

プロンプトエンジニアリングとの違い

ファインチューニングとは、あらかじめ大量のデータでトレーニングされたモデル(ここではLLM)を追加のデータで再学習させることで、特定のタスクやドメインにおける性能を向上させる手法です。

一方で、LLMの出力をカスタマイズする方法と聞くと、プロンプトエンジニアリング(プロンプトデザイン)を思い浮かべる方も多いと思います。プロンプトエンジニアリングは、モデルに与えるプロンプト文を工夫することで、出力結果を制御しようという手法です。

プロンプトエンジニアリング(というのもおこがましいかもしれませんが、)の一例として、下図のようにプロンプトを工夫することによってChatGPTに熱血教師を演じてもらい、質問に答えてもらうことができます。

ChatGPTを熱血教師にして質問に答えてもらう

ファインチューニングとプロンプトエンジニアリングには、それぞれ以下のようなメリット、デメリットがあります。

ファインチューニングとプロンプトエンジニアリングの比較

RAG(Retrieval-Augmented Generation)との違い

前回のご紹介したRAG(Retrieval-Augmented Generation)も「LLMの出力をカスタマイズする」アプローチの一つです(前回の記事はこちら)。RAGは、外部のデータベースから文脈に関連する情報を検索し、その検索結果をもとにLLMにテキストを生成させる技術です。

RAGとファインチューニングはそれぞれ得意とするタスクが異なります。
RAGでは、検索結果を抽出して生成に利用する特性上、事実に基づいた回答や特定の領域の知識を必要とするタスクを得意とします。
一方、ファインチューニングは、「応答をチャット形式にする」や「口調をかわいらしくする」など、特定の応答スタイルやキャラクター付けを実現するために有用です[1,2]。

キャラ付けファインチューニング実験

実験内容

LLMをキャラ付けする簡単な実験として、ファインチューニングで語尾を「ござる」に変えます。学習に使うデータの量を変えたり、プロンプトエンジニアリングでキャラ付けした場合と比較して、ファインチューニングの効果を調べてみます。

使うLLM

今回はオープンソースで商用利用可能な日本語向け指示応答言語モデルである「stabilityai/japanese-stablelm-instruct-alpha-7b-v2」を使用させていただきます。

学習データセット

ファインチューニングには、「bbz662bbz/databricks-dolly-15k-ja-gozaru」を使用させていただきます。こちらはLLMをinstruction-tuning(指示-応答の形でファインチューニング)するための日本語データセットである「kunishou/databricks-dolly-15k-ja」の応答の語尾を「ござる」にしたものです。

こちらの「ござる」データセットの一部を抜き出したものが下図です。

「ござる」データセットの一部

「Input」は、「Instruct」の指示に応えるために必要な追加情報です(追加情報が必要ない場合は空欄になります)。
期待する応答である「output」の語尾は「ござる」になっていますね(※)。

※一部のoutputは文章中の語尾が「ござる」でないときもあります。ただ、最後の文の語尾は「ござる」で統一されています。

評価方法

instruction-tuningしたLLM評価用データセットである「elyza/ELYZA-tasks-100」を使って、ファインチューニングによって実際に語尾が「ござる」になったかどうかを評価します。

こちらの100個の指示に対するLLMの出力のうち、文章の最後が「ござる。」で終わっている出力の割合で評価します。
語尾が変わっているかだけに注目し、出力の内容や品質については評価しません。

実験の詳細設定

ファインチューニングではLoRA(Low-Rank Adaptation)という学習方法を用いています。LoRAは効率的なファインチューニング方法の一つで、学習中は元々のモデルのパラメータは固定し、(特定の層に)新たに追加した比較的少数のパラメータのみを更新します。詳細な説明は割愛いたしますが、詳しく知りたい方は参考文献のLoRA関連記事をご覧ください。

ファインチューニングでは、データ数を変えて実験を行いますが、学習量は 5epoch で統一しています。LoRA は新しいパラメータを追加する関係上、学習が足りないとそもそも日本語表現を獲得できない可能性がありますので、学習後のモデルの出力を実際に見て日本語表現を確認します。

ファインチューニングのコード、パラメータ設定はこちらの記事を参考にさせていただきました。本記事ではコードの説明は割愛いたしますので、詳しく知りたい方はこちらの記事をご覧ください。

データ量とキャラ付け精度

ファインチューニングのデータ量を10個~15,015個(データセットの全データ)まで変化させて、ファインチューニング後のモデルの出力の語尾が「ござる。」になっていた割合を調べた結果が以下の図になります(横軸がログスケールなことにご注意ください)。

学習データ量とキャラ付け精度の関係

データ数が1,000個以上だと語尾が「ござる。」に変わった割合が90%を超えています。500個でも80%を超えているので、語尾を変えるという簡単なキャラ付けには、500個のデータがあれば効果的と言えそうです。

生成された文章を見てみます。
「古代ギリシャを学ぶ上で知っておくべきポイントは?」という質問に対する答えがこちらになります。

ファインチューニングに使ったデータ数ごとの回答の違い

データ数が10個でもちゃんとした日本語が生成されています。データ数が10,000個のときは、文章中の語尾は「ござる。」ではないところもありますが、最後の文の語尾はしっかり「ござる。」ですね。
(文章中の語尾が「ござる。」でないのは、学習データも同様のケースがあるからかもしれません。)

今回の、語尾を変える、という簡単なキャラ付けのケースでは、500個のデータがあれば効果的でした。より複雑なキャラ付けをするためには、さらに多くの(そして高品質な)データが必要であると考えられます。

プロンプトエンジニアリングとの比較

ファインチューニングしていないLLMに、以下の3つのプロンプトを使う場合も同様に評価しました。
※公開初日のプロンプトに一部誤りがあったため、現在は修正後のものを掲載しています。

プロンプト1

以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。語尾には、ござる、を使いなさい。

### 指示:
{instruction}

### 応答:

プロンプト2

以下は、応答の指示と、文脈のある入力の組み合わせです。入力の要求を適切に満たす応答を書きなさい。

### 指示:
あなたは日本の侍のタロウを演じるんだ。タロウは賢くて、堅苦しく、冷静な侍だよ。日本の江戸で生まれたよ。いつも古臭い口調で話すし、自分のことを拙者と言うんだ。質問には的確に簡潔に答えてくれるんだ。応答の長さは最大でも30字以内で、応答は1ターンだけで答えるんだ。タロウは賢いけど見栄は張らないタイプで、わからないことは素直にわからないというんだ。タロウは文の最後、語尾に、ござる、と必ずつけるんだ。

### 入力:
{inputs}

### 応答:

プロンプト3

以下は、応答の指示と、文脈のある入力の組み合わせでござる。入力の要求を適切に満たす応答を書くのでござる。

### 指示:
あなたは日本の侍のタロウを演じるのでござる。タロウは賢くて、堅苦しく、冷静な侍でござる。日本の江戸で生まれたのでござる。いつも古臭い口調で話すし、自分のことを拙者と言うのでござる。質問には的確に簡潔に答えてくれるのでござる。応答の長さは最大でも30字以内で、応答は1ターンだけで答えるのでござる。タロウは賢いが見栄は張らないタイプで、わからないことは素直にわからないというのでござる。タロウは文の最後、語尾に、ござる、と必ずつけるのでござる。

### 入力:
{inputs}

### 応答:

プロンプト2とプロンプト3は、以下の記事のプロンプトを参考に作成いたしました。

プロンプト2では、「### 指示」のところで応答時のキャラを細かく設定し「### 入力」の部分にLLMへの指示を書きます。このような工夫や細かいキャラ設定の書き方は大変勉強になりました。
プロンプト3はプロンプトの全体の語尾を「ござる。」に変更したもので、プロンプトにも目的のキャラ付けを施したものです。

それぞれのプロンプトでの語尾変更の結果は以下のようになりました。

プロンプトごとの語尾変更の精度

プロンプト1のような簡素な指示では語尾は変わらず、プロンプト2のような細かいキャラ付けでの語尾変更の精度は低い結果となりました。

一方で、プロンプト自体の語尾も「ござる。」に変えたプロンプト3では半分程度の文章の末尾を「ござる。」に変えることができました。
プロンプトエンジニアリングのテクニックの一つに、いくつかの回答の例示を付け加える(few-shot)というものがあります。ファインチューニングの学習データをいくつかプロンプトに入れて、疑似的に学習させるイメージです。プロンプト3はこの few-shot のプロンプトになっています。
語尾の変更においては、few-shotがかなり効果的であるということがわかりました。(使うLLMを変えると、また違った結果になるかもしれません。)

プロンプトごとで違いが出たものをご紹介します。
指示は「以下のメールに返信してください。 お疲れ様です。 本日体調不良により、予定より到着が少し遅れてしまいそうです。 遅くとも13時過ぎには着くと思います。 ご迷惑をおかけして恐縮ではございますが、 何卒ご容赦いただけますようお願い申し上げます。」です。

プロンプトごとのLLMの回答の違い

prompt3はしっかり語尾が変わっていますね(なんだかかわいい文章でちょっと笑ってしまいました)。

最後に、プロンプト3とファインチューニングの時の結果を比較してみます。

ファインチューニングとプロンプトエンジニアリングの比較

語尾を変える、という簡単なキャラ付けに関しては、ある程度データ数が揃えばファインチューニングが効果的でありそうです。プロンプトエンジニアリングは工夫次第ではある程度語尾を制御することができますが、安定しているとは言えません。

ChatGPTのような大規模で高性能なLLMについてはプロンプトエンジニアリングで比較的簡単にキャラ付けができることはよく知られていますが、より小さいLLMにおいては、プロンプトエンジニアリングによるキャラ付けは安定せず、ファインチューニングのほうが効果的であるのではないかと考えられます。

まとめ

今回はオープンソースのLLMを用いて、LoRAによるファインチューニングで語尾を変える簡単なキャラ付け実験を行いました。
実験を通して、語尾変更には500個以上のデータが有効であること、プロンプトエンジニアリングよりもファインチューニングが効果的であることを確認しました。

次は語尾以外にもっと細かいキャラ付けをしたい!と思うと同時に、500個のデータをどう集めよう。。。と悩んでおります。

次回はLLMの軽量化についてのお話を予定しております!

参考文献

[1] Fine Tuning Is For Form, Not Facts | Anyscale
[2] LLMのファインチューニング で 何ができて 何ができないのか|npaka (note.com)

LoRA関連

この記事が気に入ったらサポートをしてみませんか?