LLMをサービスに導入するための精度改善：「Fine-Tuningって結局どうなの？」編

# はじめに
:::note info
🎄 本記事は [ZOZO Advent Calendar 2024](https://qiita.com/advent-calendar/2024/zozo) シリーズ８の４日目です
:::

皆さんも実務で利用する機会が増えてきたであろうLLMですが、最初から意図した回答が返ってくることは、意外にも少ないのではないでしょうか？

ChatGPTであれば会話を通して適切な回答に誘導していくということも可能ですが、APIやローカルLLMを利用したサービスを検討している場合は、1回で理想の回答が返ってこないと困る場合も多いです。

本記事では、LLMをサービスに導入するための精度改善手法として「Fine-Tuning（ファインチューニング）」を使うべき状況についてご紹介します！✨

```
・ 想定読者　　　　：LLMを使ったサービスを企画・開発しようとしている方
・ 読んで分かること：Fine-Tuningを使うべき状況
```

###### 📕 LLMをサービスに導入するための精度改善シリーズ
1. [「Fine-Tuningって結局どうなの？」](https://qiita.com/ShionSakurai/items/9d84b49d6889a57eefa5)（本記事）
2. [「Prompt-Tuningって結局どうなの？」](https://qiita.com/ShionSakurai/items/bc2fc33eed69a3dcb3a0)


# 定義
本記事では以下の2つの観点を精度の定義とします。
```
1. 知識の精度：求めている文意となっているか？
2. 表現の精度：求めている文型となっているか？
```
また、本シリーズでは以下の2つを精度改善手法として扱います。
```
1. Fine-Tuning：ユーザーからの想定される入力とLLMからの理想の出力をペアとして学習させる手法
2. Prompt-Tuning：ユーザーからの入力を工夫して、LLMの出力を制御する手法
   ※ RAGはPromptに情報を追加する技術なので、本シリーズでは2番に内包する技術として扱います
```

# 一般的なFine-Tuningへの理解と疑問
LLMでよく知られている精度改善手法として、Fine-TuningやPrompt-Tuningと呼ばれる手法がありますが、これらの手法の使い分けについて以下のような認識の方が多いと思います。

|               | Fine-Tuning | Prompt-Tuning |
|:--------------|:-----------:|:-------------:|
| 知識の精度改善  | △           | ○             |
| 表現の精度改善  | ○           | △             |
| 改善コスト     | △           | ○             |
| 改善工数       | ×           | ◎             |

この表を見ると、ほとんどの人が以下の感想を持つかもしれません。

###### 🤔 💭 「Fine-Tuningってどこで使うの？」

この疑問に関する回答として、OpenAIの[公式ドキュメント](https://platform.openai.com/docs/guides/fine-tuning)に以下の記述があります。

> Some common use cases where fine-tuning can improve results:
> - Setting the style, tone, format, or other qualitative aspects
> - Improving reliability at producing a desired output
> - Correcting failures to follow complex prompts
> - Handling many edge cases in specific ways
> - Performing a new skill or task that’s hard to articulate in a prompt
>
> One high-level way to think about these cases is when it’s easier to "show, not tell".

「説明するよりも示す方が簡単なケース」で使用することを推奨されており、表現の精度を改善するだけでなく、プロンプトで表現するのが難しいスキルを実行するという知識の精度改善についても言及されています。

ただ、この説明を読んだだけでは以下のように感じる方も多いと思います。

###### 🤔 💭 「説明するよりも示す簡単なケースって何なの？」

それこそ説明するよりも示す方が簡単なので、Fine-Tuningのやり方から確認していきましょう！


# 説明するよりも示す方が簡単なケースとは？

LLMに新しい統計手法である「バランスシフト法」を学習させるケースを考えてみましょう。

> バランスシフト法 (Balance Shift Method)：
バランスシフト法は、2つの要素の関係性を評価し、片方の要素がどの程度影響を与えているかを定量的に示す手法です。これにより、最適なバランスのポイントを特定します。

バランスシフト法はChatGPTに考えてもらった新しい概念です。上記は実際にChatGPTが出力した回答となっており、2つの要素の関係性を示すための手法となっています。

###### 📐 定義：
1. 各要素の値を比較
2. シフト値 (Shift Value) を以下の式で計算:
    ```math
    Shift Value = 
    \frac{A-B}{A+B}
    ```
3. シフト値がプラスの場合、要素1の優位性を示し、マイナスの場合、要素2の優位性を示す

:::note warn
実在しない手法なので、他の集計では利用しないでください！
:::

それでは「この概念を理解したLLMと会話する場合」と「普通のLLMと会話する場合」はどんな会話になるのでしょうか？

###### \:a: バランスシフト法を理解したLLMの場合：
```
・ ユーザー　：バランスシフト法を用いて、以下のデータセットのバランスを解析してください。
　　　　　　　 要素1: 30、要素2: 20

・ LLM　　　：バランス解析結果:
            ・ シフト値計算: (30 - 20) / (30 + 20) = 0.2000
            ・ 解釈: 要素1が要素2よりも20.0%優位です。
```

###### \:b: 普通のLLMの場合：
```
・ ユーザー　：# 指示
　　　　　　　 バランスシフト法を用いて、以下のデータセットのバランスを解析してください。
　　　　　　　 バランスシフト法とは、要素の関係性を評価し、片方の要素がどの程度影響を与えているかを定量的に示す手法です。
　　　　　　　 2つの要素の差分を、2つの要素の和で割った値をシフト値と呼び、シフト値がプラスなのか-なのかで優位な要素を判定します。
　　　　　　　 以下の計算対象に対して計算を行い、以下の出力フォーマットで回答してください。

　　　　　　　 # 計算対象
　　　　　　　 要素1: 30、要素2: 20
       
　　　　　　　 # 出力フォーマット
　　　　　　　 バランス解析結果:
　　　　　　　 ・ シフト値計算: (A - B) / (A + B) = 0.X
　　　　　　　 ・ 解釈: 要素1が要素2よりもX.0%優位です。

・ LLM　　　：バランス解析結果:
　　　　　　　 ・ シフト値計算: (30 - 20) / (30 + 20) = 0.2000
　　　　　　　 ・ 解釈: 要素1が要素2よりも20.0%優位です。
```
Aの場合はバランスシフト法で計算して欲しいという指示と、要素を指定するだけで正しい結果が返ってきましたが、Bの場合は概念自体の説明と出力フォーマットを指定しないと求めている結果が返ってきませんでした。
このように、知識を与えるだけでなくその知識を使って何かの処理を行いたい場合や、出力フォーマットを一定の形式に揃えたい場合などに、Fine-Tuningは有用な技術です。

###### 🤔 💭 「でも、プロンプトの指示で制御できるならFine-Tuningを使う必要はないのでは？」

上記の具体例を見てこのように感じる方もいるかもしれません。確かに「予算が無限にあり、毎回丁寧な記述をしてくれるユーザーだけが対象」のサービスであれば、プロンプトの調整だけでも問題ありません。しかし、実際のサービスでは「予算と入力条件に大きく左右される」ことが多いため、簡単な記述で正解を導くことができるLLMは、とても重要な技術となります。

※ LLMのAPIはトークン数に対する従量課金制の場合が多いため、文字数を減らすことがコスト削減に直結します！💰


# 学習データの準備とトレーニング
それでは、OpenAIのAPIを使ったFine-Tuningを方法を確認していきましょう。
まずは、バランスシフト法を学習させるためのデータを作成します。

###### 📁 学習データの準備：
Fine-Tuningで利用する学習データは以下のようなフォーマットとなっており、「system、user、assistant」の3つの役割に行って欲しい1つの会話を正解データとして作成します。

```json
{
    "messages": [
        {"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, 
        {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 30、要素2: 20"}, 
        {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (30 - 20) / (30 + 20) = 0.2000\n- 解釈: 要素1が要素2よりも20.0%優位です。"}
    ]
}
```
次に、このようなJSONを最低10個以上作成し、それぞれを行毎に格納したJSONL形式のデータを作成します。

```json
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 30、要素2: 20"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (30 - 20) / (30 + 20) = 0.2000\n- 解釈: 要素1が要素2よりも20.0%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 32、要素2: 21"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (32 - 21) / (32 + 21) = 0.2075\n- 解釈: 要素1が要素2よりも20.8%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 34、要素2: 22"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (34 - 22) / (34 + 22) = 0.2143\n- 解釈: 要素1が要素2よりも21.4%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 36、要素2: 23"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (36 - 23) / (36 + 23) = 0.2203\n- 解釈: 要素1が要素2よりも22.0%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 38、要素2: 24"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (38 - 24) / (38 + 24) = 0.2258\n- 解釈: 要素1が要素2よりも22.6%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 40、要素2: 25"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (40 - 25) / (40 + 25) = 0.2308\n- 解釈: 要素1が要素2よりも23.1%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 42、要素2: 26"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (42 - 26) / (42 + 26) = 0.2353\n- 解釈: 要素1が要素2よりも23.5%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 44、要素2: 27"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (44 - 27) / (44 + 27) = 0.2394\n- 解釈: 要素1が要素2よりも23.9%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 46、要素2: 28"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (46 - 28) / (46 + 28) = 0.2432\n- 解釈: 要素1が要素2よりも24.3%優位です。"}]}
{"messages": [{"role": "system", "content": "This assistant provides factual outputs with precise calculations."}, {"role": "user", "content": "バランスシフト法を用いて、以下のデータセットのバランスを解析してください。要素1: 48、要素2: 29"}, {"role": "assistant", "content": "バランス解析結果:\n- シフト値計算: (48 - 29) / (48 + 29) = 0.2468\n- 解釈: 要素1が要素2よりも24.7%優位です。"}]}
```

これで学習データの準備は完了です！

###### 📚 トレーニング：
コードを使ったトレーニング方法もありますが、今回は[OpenAI Platform](https://platform.openai.com/finetune)を使ったトレーニング方法をご紹介します。
※ コードで行いたい場合は[こちらのドキュメント](https://platform.openai.com/docs/guides/fine-tuning)を参考にしてください。

![home.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539242/424bbab7-d2e2-7123-1ff5-85318bf2de58.png)

[OpenAI Platform](https://platform.openai.com/finetune)を開くとサインアップが要求されます。ログインを完了し、画像のようにFine-Tuningのトップ画面を開きましょう。

![create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539242/9ab97f9a-6620-8f09-2a64-b8e5ad393a9e.png)

「Create」のボタンをクリックすると、こちらの画像のような画面になるため、先ほど用意した学習データと任意の名前をつけてください。特にこだわりがなければ右下の「Create」ボタンを押すことでトレーニングが開始されます。

あとは待つだけでトレーニングが完了です！
※ 「$1 = ¥150」で計算した場合のトレーニングコスト：¥0.492075


# 精度検証
それでは先ほどトレーニングしたモデルを利用した精度検証を行っていきましょう。[OpenAI Platform](https://platform.openai.com/playground/chat)では、簡単な動作テストを行うこともできます。

![test1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539242/7609e6aa-a6f2-48ba-201c-21fd5e74a956.png)

これは通常のgpt-4o-miniとFine-Tuninigを行ったgpt-4o-miniに、全く同じプロンプトを入力した結果です。

###### 💡 精度評価１
|           | gpt-4o-mini      | gpt-4o-mini（Fine-Tuninig） |
|:----------|:----------------:|:--------------------------:|
| 知識の精度  | ×                | ○                          |
| 表現の精度  | ×                | ○                          |
| その他     | ハルシネーション発生 |                            |

Fine-Tuningしたモデルは、知識と表現の両面で完璧な結果となりました。一方で通常のgpt-4o-miniは、求めている回答とは異なる出力を返しており、ハルシネーションも発生しています。（知らないはずのバランスシフト法を解説している）

もちろん、通常のgpt-4o-miniはバランスシフト法を知らないはずなので、プロンプトを充実させた場合の出力を確認してみましょう。

![test2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539242/cc272ad2-a9d4-31a0-cfbc-c5c748ef2d25.png)

どちらのプロンプトも充実させた場合の結果がこちらです。

###### 💡 精度評価２
|           | gpt-4o-mini      | gpt-4o-mini（Fine-Tuninig）     |
|:----------|:----------------:|:------------------------------:|
| 知識の精度  | ○                | ○                              |
| 表現の精度  | ○                | ○                              |
| その他     |                  | 入力tokenが190増加（=246-56）    |

Fine-Tuningしていない通常のgpt-4o-miniでも、求めていた回答が返ってきました。一方で、Fine-Tuningしているモデルの入力tokenが190増加しています。

```
この増加分を「$1 = ¥150」で計算すると、¥0.000855となります。
今回のトレーニングコストが¥0.492075なので、575.5回で元を取れる計算です。
```

今回は比較的短いプロンプトで済んだため、あまり大きなコスト増加にはなりませんでしたが、条件が複雑になるほどプロンプトの長さも増加するため、Fine-Tuningを使うことによるコスト削減効果も高まります！💰

# おわりに
本記事では、Fine-Tuningの概要と方法についてご紹介しました。
先ほどの表では記載していなかった視点として、長期的なコストというメリットがFine-Tuningにはあるということが分かったと思います！

|               | Fine-Tuning | Prompt-Tuning |
|:--------------|:-----------:|:-------------:|
| 知識の精度改善  | △           | ○             |
| 表現の精度改善  | ○           | △             |
| 改善コスト     | △           | ○             |
| 改善工数       | ×           | ◎             |
| 長期的なコスト  | ◎           | -             |

それでは、次回の「Prompt-Tuningって結局どうなの？」編でもお会いしましょう！💫