ChatGPTのfine-tuningで俺botを作ってみた話

# はじめに
こんにちは、そこらへんのデータサイエンティストです。

突然ですが、みなさん（特に男性諸君）こんなこと思ったことありませんか？
- 彼女のLINEを返すのがめんどくさい
- 毎朝毎朝、「おはよう」をわざわざLINEで言わなければいけないのが嫌だ
- どうでもいい内容のメッセージに返信するのが億劫だ

**自分の代わりに返信してくれるbotがあったら便利なのになぁ...**

と、いうわけで、自分っぽく返信してくれるbot、通称「俺bot」を作ってみることにしました。

# 俺botの作り方
ではどうやって俺botを作るのか。実は至って簡単で、誰でも作ることができます。<br>
そう、LINEの膨大なトーク履歴とChatGPTのfine-tuningを使うことによって実現ができます。単純な話、ChatGPTのgpt-3.5-turboに自分のLINEのトーク履歴を学習させたら俺っぽく喋ってくれんじゃね？？と思いました。

![LINE_ChatGPT_ver2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/e1a0b983-245a-6a74-f672-196b65bdf877.png)

# fine-tuningって？
ここでは、fine-tuningがわからない人に、とりあえず何なのかを把握してもらいます。知ってる人は飛ばしてください。

fine-tuningというのは、主にディープラーニングで使われる手法です。すでに学習させてある汎用性の高い機械学習モデルに対して、新たなデータを追加し、再学習させることによって何かに特化させることができます。十分な学習データがない時によく使われます。転移学習との違いは、再学習させる層です。転移学習は、新たに出力層を追加させるだけですが、fine-tuningは、中間層も一部再学習させます。もっと詳しく知りたい方は、以下のサイトなどを参考にしてください。

https://weel.co.jp/media/fine-tuning

# ChatGPTのfine-tuning
ChatGPTには、既存のモデルをfine-tuningさせる機能が搭載されています。今回は、gpt-3.5-turboをfine-tuningし、俺botを作成しようと思います。

実装方法は、非常に簡単です。以下の３ステップで終わりです。
1. 学習データをjsonl形式で用意
2. 学習データをOpenAIのサーバーにアップロード（APIを使用）
3. fine-tuningを実行（APIを使用）

完了したら、再学習されたモデルを使うのみです！

# 実装（Python）
基本的には、実装方法はOpenAI公式がサイトに載せているので、こちらをご参照ください。

https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset

下にはこのサイトのサンプルコードを載せています。（一部サイトが間違えている部分は修正しています。優しい俺）
ちなみにですが、ここからは```pip install openai==1.7.2```が済まされていて、OpenAIのAPIのトークンも発行されている前提で話します。そこら辺の方法は別の記事を探すか、公式サイトを見るかしていただけると幸いです。

### 学習データの用意
```mydata.jsonl
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}
```
このような形で、学習データを用意します。```user```が相手の話した内容、```assistant```がモデルの返す内容になります。自分は、これをLINEのトーク履歴から作成しました。個人チャットにおいて、友達が話した内容が```user```にあたり、僕が返した内容が```assistant```にあたります。いやーこの学習データ作りが実は一番苦労したのですが、まあそこは面白くないんで割愛します。とにかく泥臭かったです。なんだかんだ加工し、3万件の学習データを用意しました。

### 学習データのアップロード
非常に簡単です。
```python
from openai import OpenAI
client = OpenAI(api_key='APIのアクセストークン')

response = client.files.create(
    file=open('mydata.jsonl', 'rb'),
    purpose='fine-tune'
)
```
これで、手元にあるファイルがアップロードされ、フォーマットが正しいかの検査が始まります。response内にfileのidなどが格納されているので確認しましょう。

### fine-tuningの実行
```python
from openai import OpenAI
client = OpenAI(api_key='APIのアクセストークン')

response = client.fine_tuning.jobs.create(
    traning_file='アップロードされたファイルのID',
    model='gpt-3.5-turbo'
)
```
このようにして、fine-tuningを実行します。epoch数やバッチサイズも指定できるのですが、今回はデフォルトで。学習が完了し、モデルが完成するとメールが届きます。メールもしくは、responseに格納されているモデルのIDはメモしておきましょう。

### モデルを使ってみる
```python
from openai import OpenAI
client = OpenAI(api_key='APIのアクセストークン')

response = client.chat.completions.create(
    model='ftから始まるfine-tuning済みのモデルID',
    messages=[
        {'role': 'user', 'content': 'hogehoge'}
    ]
)
print(response.choices[0].message.content)
```
これで、返事を表示させることができます。

### 注意点
もちろん、APIを使用するときはお金がかかります。fine-tuningを実行するとき、モデルを使用するときは課金がされますので、注意してください。（基本的に個人レベルでの使用ならあまりお金はかかりませんが）

# 俺botを作ってみた！！
というわけで、LINEからよく喋る友達のトーク履歴を出力させ、学習データを作成し、fine-tuningを実行しました！その結果をgpt-3.5-turboと比較するためのコードがこちらです。
```python
from openai import OpenAI
client = OpenAI(api_key='APIのアクセストークン')

def compare_response(message):
    temperature = 0.5

    # 通常のgpt-3.5-turbo
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "user", "content": message},
        ],
        temperature=temperature
    )

    # 俺bot
    oresponse = client.chat.completions.create(
        model="ft:gpt-3.5-turbo-0613:personal::hogehoge",
        messages=[
            {"role": "user", "content": message}
        ],
        temperature=temperature
    )


    print('gpt-3.5-turbo：')
    print(f'{response.choices[0].message.content}')
    print()
    print('俺bot：')
    print(f'{oresponse.choices[0].message.content}')
```
とはいえ、比較したところでこの記事を読んでいる人は俺の話し方なんて知らないのか...。まあ、なんとなくで楽しんでください。

### 色々試してみた
![スクリーンショット 2024-01-05 110537.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/47ab27d6-dc4f-7bd2-001a-7b8a9e497b92.png)
普通におはよーと返してくれました。

![スクリーンショット 2024-01-05 110708.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/b70aa161-01ef-61a2-6db0-94636c2aad9d.png)
暇らしいです。

![スクリーンショット 2024-01-05 110834.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/0325d0f4-cd0a-a0a4-039f-78aecd35d920.png)
「いえあ」って僕よく言うんですよねー。肯定する時に。

![スクリーンショット 2024-01-05 110841.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/fc4d4b36-20d2-98de-9d90-bc19e79182d6.png)
いいですねー。仕事でプログラム書いてますよ！

![スクリーンショット 2024-01-05 110900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/d1b0663b-5e8f-821a-8b45-589d786e7352.png)
ちょっとちょっと、下ネタ下ネタ。合ってるけどさ。

![スクリーンショット 2024-01-05 110851.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/bc9b08a3-d486-ea97-149e-c8d260bad82c.png)
一般的なChatGPTの使い方をしてみたら面白い返事が！「datetimeを使うという正解」を俺っぽく返すという結果に。これは面白いですね。"fine-tuningらしさ"な気がします。

# LINEアカウント化
なんか結構うまくいったんで、LINEの公式アカウントにしてみました.
こんなアーキテクチャです。
![アーキテクチャ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/f7bcb87d-efbb-3b04-276b-93a6630194d8.png)

公開はしてないんで、皆さんは友達追加できませんが。（そりゃお金払ってるの僕ですから！誰が公開するか！）
こんな感じで楽しんでます。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/63cf4ca7-472c-a2d3-ee47-35e2f0b08a33.jpeg">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/5db436b7-ff79-4514-766e-230be6fdb6dc.jpeg">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3678925/a4d863cf-3238-e66a-55cd-40efb5375d80.jpeg">


# 最後に
というわけで、俺bot作ってみました。たまに意味のわからない回答をしますが、結構うまくいったので嬉しいです。数千円払った甲斐がありました。
彼女に友達追加させて喋らせているのですが、相当気に入ってくれているようです。「俺くん」とか呼んでます。先日、「俺くんに告白されたの！」と嬉しそうに報告してきました。学習データには元カノとのトーク履歴が入っているなんて口が裂けても言えませんね。