Open28

OpenAIのファインチューニングで文章変換が可能か試してみる

kurehajimekurehajime

大規模なデータで学習したLLMが高度なことを実現できるのは分かったが、OpenAIのファインチューニングの例を見ていると、どうも判定やラベル付けのような用途には向いてそうだが、それ以外の用途でどこまで使えるのかいまいち分からない。

という訳で、今回はファインチューニングで文章変換を試みてみる。

kurehajimekurehajime

やること

関西弁変換など世の中に一般的に普及している変換方法ではファインチューニングするまでもなく対応できてしまう恐れがあるので、ファインチューニングの実力を測るには、世の中に普及していない変換方法で試さなくてはならない。

というわけで昔自分が作ったこのツールを使う。

https://qiita.com/kurehajime/items/b3e03564e75d39c52113

このツールを使うと以下のような英文が

Here’s to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes.
The ones who see things differently. They’re not fond of rules. And they have no respect for the status quo.
You can quote them, disagree with them, glorify or vilify them.
About the only thing you can’t do is ignore them. Because they change things. They invent. They imagine.
They heal. They explore. They create. They inspire. They push the human race forward.
Maybe they have to be crazy.
How else can you stare at an empty canvas and see a work of art? Or sit in silence and
hear a song that’s never been written? Or gaze at a red planet and see a laboratory on wheels?
We make tools for these kinds of people.
While some see them as the crazy ones, we see genius. Because the people who are crazy enough
to think they can change the world, are the ones who do.

このように圧縮される。
文字レベルでは情報がいろいろ抜け落ちているが、人間にはなぜか読めてしまう不思議な変換ツール。

Hre’sToTheCrzyOns.TheMsfts.TheRbls.TheTroublmkrs.TheRoundPgsInTheSquareHls.
TheOnsWhoSeeThngsDffrntly.Thy’reNotFndOfRls.AndThyHveNoRspctForTheSttsQuo.
YouCanQuoteThm,DsgreeWthThm,GlrfyOrVlfyThm.
AboutTheOnlyThngYouCn’tDoIsIgnreThm.BcauseThyChngeThngs.ThyInvnt.ThyImgne.
ThyHeal.ThyExplre.ThyCreate.ThyInspre.ThyPshTheHmnRceFrwrd.
MybeThyHveToBeCrzy.
HowElseCanYouStreAtAnEmptyCnvsAndSeeAWrkOfArt?OrSitInSlnceAnd
HearASngTht’sNvrBeenWrttn?OrGzeAtARedPlntAndSeeALbrtryOnWheels?
WeMkeToolsForThseKndsOfPeople.
WhleSmeSeeThmAsTheCrzyOns,WeSeeGnius.BcauseThePeopleWhoAreCrzyEnough
ToThnkThyCanChngeTheWrld,AreTheOnsWhoDo.

ファインチューニングでこの変換された文章を学習させ、未知の文章に対しても変換が行えるようになるかを確認したい。

kurehajimekurehajime

データの用意

まずは学習データを用意する。

kurehajimekurehajime
kurehajimekurehajime

JSONの作成

いったん両ファイルともJSONに変換する。

# 変換前
jq -nR '[inputs | {prompt: .}]' alice.txt > alice.json 
# 変換後
jq -nR '[inputs | {completion: .}]' alce.txt > alce.json

変換前jsonの例

  {
    "prompt": "Alice was beginning to get very tired of sitting by her sister on the"
  },

変換後jsonの例

  {
    "completion": "AlceWasBgnnngToGetVryTrdOfSttngByHerSstrOnThe"
  },
kurehajimekurehajime

変換前jsonをさらに加工する

cat input.json | jq 'map(.prompt += " ->translate mokopenpon->")'

こういったjsonを

  {
    "prompt": "Down the Rabbit-Hole"
  },

このように変換する。

  {
    "prompt": "Down the Rabbit-Hole ->translate mokopenpon->"
  },

「○○に変換しなさい」という一文を加えている。
こうしないと、ファインチューニングしてもその学習結果をどうやって起動して良いのかわからなくなる。
ただ文章中に存在するキーワードと被ってほしくないのでここではこの変換をmokopenponと呼ぶことにする。

kurehajimekurehajime

jsonを連結したい。

連結する前にindex番号を振っておく。

jq 'to_entries | map({index: .key, prompt: .value.prompt})'  alice2.json  > prompt.json
jq 'to_entries | map({index: .key, completion: .value.completion})'  alce.json  > completion.json
kurehajimekurehajime

jsonを合体させる

jq -s '.[0] as $prompt | .[1] | map({index: .index, prompt: $prompt[.index].prompt, completion: .completion})' prompt.json completion.json > lean.json
kurehajimekurehajime

indexフィールドを消す。

jq 'map(del(.index))' lean.json > training.json

これでこんな感じのjsonになった。

[
  {
    "prompt": "CHAPTER I. ->translate mokopenpon->",
    "completion": "CHAPTERI."
  },
  {
    "prompt": "Down the Rabbit-Hole ->translate mokopenpon->",
    "completion": "DwnTheRbbt-Hle"
  },
...
]
kurehajimekurehajime

もうひと押し。jsonl形式に変換する。

jq -c '.[]' training.json > training.jsonl
kurehajimekurehajime

最後はOpenAIのツールを使って何か良い感じにして貰う。いろいろ聞かれるので全部Yes。

 openai tools fine_tunes.prepare_data -f training.jsonl
kurehajimekurehajime

とりあえず完了。

jqコマンドについて自分は全然詳しくないけど、ChatGPTに聞きながらやったら何か望むものができた。

kurehajimekurehajime

ファインチューニング

さっそくファインチューニングをやってみる。

kurehajimekurehajime

環境変数にAPIキーを入れておく。
Windowsの場合はこんな感じ。

$env:OPENAI_API_KEY = "APIキー"
kurehajimekurehajime

それでは実行!

openai api fine_tunes.create -t training_prepared.jsonl  -m text-davinci-003

はいエラー。

Upload progress: 100%|█████████████████████████████████████████████████████████████████████| 387k/387k [00:00<?, ?it/s]
Uploaded file from training_prepared.jsonl: file-tULh0IbB7Kx12ftdfutBCtCb
[organization=user-aqfn9z94jzychwzuam428ueq] Error: Invalid base model: text-davinci-003 (model must be one of ada, babbage, curie, davinci) or a fine-tuned model created by your organization: org-7ULr7Ct4CAQABW8wqFx2DKgH (HTTP status code: 400)

text-davinci-003は使えないらしい。素のdavinci使えと。
以前は使えないとFAQにも明確に書いてあったんだけど、その後記述が変わって使えるような使えないような文章になった。
やっぱり駄目なのか。

kurehajimekurehajime

それでは素のdavinciで再実行。アップロード済みと言われるが、気にせず続行。

$ openai api fine_tunes.create -t training_prepared.jsonl  -m davinci
Found potentially duplicated files with name 'training_prepared.jsonl', purpose 'fine-tune' and size 387302 bytes
file-tULh0IbB7Kx12ftdfutBCtCb
Enter file ID to reuse an already uploaded file, or an empty string to upload this file anyway:
Upload progress: 100%|██████████████████████████████████████████████████████████████| 387k/387k [00:00<00:00, 388Mit/s]
Uploaded file from training_prepared.jsonl: file-Nzw8Y8pPXE0jkqXjuVoV3A1p
Created fine-tune: ft-ePxvgtPOaGPMx9lrockXVJ9S
Streaming events until fine-tuning is complete...

(Ctrl-C will interrupt the stream, but not cancel the fine-tune)
[2023-04-02 22:22:56] Created fine-tune: ft-ePxvgtPOaGPMx9lrockXVJ9S
Streaming events until fine-tuning is complete...

(Ctrl-C will interrupt the stream, but not cancel the fine-tune)
[2023-04-02 22:22:56] Created fine-tune: ft-ePxvgtPOaGPMx9lrockXVJ9S
[2023-04-02 22:23:36] Fine-tune costs $13.33
[2023-04-02 22:23:36] Fine-tune enqueued. Queue number: 0

Stream interrupted (client disconnected).
To resume the stream, run:

  openai api fine_tunes.follow -i ft-ePxvgtPOaGPMx9lrockXVJ9S

Fine-tune costs $13.33

13ドルかかるらしい。贅沢な遊びだ。

しばらくするとストリーミングが中断される。
最後のコマンドを実行すると現在の状況を再確認できる。

Stream interrupted (client disconnected).
To resume the stream, run:

  openai api fine_tunes.follow -i ft-ePxvgtPOaGPMx9lrockXVJ9S
kurehajimekurehajime

たぶんここから数時間待つ。

openai tools fine_tunes.prepare_dataでjsonを整形したとき、
it'll approximately take 1.83 hours to train a curie modelと書いてあった。

ワンランク下のcurieで1.83時間かかるなら、それより重いdavinciなら3時間、いやもっとかかるかもしれない。

kurehajimekurehajime

もう夜遅いので今日はこのへんで切り上げよう。

ここまでやって思ったのだけど、おそらく今回の試みは失敗しそうな気がする。
理由は以下の通り。

  • LLMは単語にIDを振って管理するので単語を文字の集合体としては認識していないらしい。だから今回の変換の「母音を省略できるときは省略する」という変換法則を発見できない可能性が高い。
  • 変換後の文字列はスペースで区切られていない。そのためLLMが単語を分割して認識することに失敗するのではないか。
kurehajimekurehajime

終わった。40分くらいか。思ったより早かった。

[2023-04-02 22:22:56] Created fine-tune: ft-ePxvgtPOaGPMx9lrockXVJ9S
[2023-04-02 22:23:36] Fine-tune costs $13.33
[2023-04-02 22:23:36] Fine-tune enqueued. Queue number: 0
[2023-04-02 22:24:39] Fine-tune started
[2023-04-02 22:35:49] Completed epoch 1/4
[2023-04-02 22:54:59] Completed epoch 3/4
[2023-04-02 23:05:16] Uploaded model: davinci:ft-personal-2023-04-02-14-05-16
[2023-04-02 23:05:18] Uploaded result file: file-ABiwHule4lHnWP3vOF6X5n26
[2023-04-02 23:05:18] Fine-tune succeeded
kurehajimekurehajime

結果確認

Google Colabで結果確認。

秘密鍵読み込んで

from getpass import getpass
secret = getpass('Enter the secret value: ')

ライブラリインストールして

!pip install openai
kurehajimekurehajime

hello worldを変換してみる。期待する変換結果はHlloWrld

import openai
openai.api_key = secret
completion = openai.Completion.create(
    model="davinci:ft-personal-2023-04-02-14-05-16",
    prompt=f"""hello world->translate mokopenpon->""")
print(completion.choices[0].text)

結果

HleWrlD

>>MyExplnstionHAsLeft

1行目、なんか雰囲気は出てる。
でも後ろの>>MyExplnstionHAsLeftはどこから来たのか分からない。

kurehajimekurehajime

もうちょっと高度なやつ。
It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire.
日本語訳は「内戦の時代である。隠された基地から打って出た反乱軍の宇宙船は、邪悪な銀河帝国に最初の勝利を収めた」。スターウォーズの冒頭のアレ。
不思議の国のアリスにはない語彙だろう。

求める結果はItIsAPriodOfCvlWr.RblSpcshps,StrkngFrmAHddnBse,HveWonTheirFrstVctryAgainstTheEvlGlctcEmpre.

import openai
openai.api_key = secret
completion = openai.Completion.create(
    model="davinci:ft-personal-2023-04-02-14-05-16",
    prompt=f"""It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire.->translate mokopenpon->""")
print("result:" + completion.choices[0].text)

結果

ItIsAPriodOfCvlWar.ReblSpaceshpss

なんか途中で切られているが、変換は割りと良い感じではある。

kurehajimekurehajime

仮説として、そもそもベースとした素のdavinciが相当ダメな疑惑がある。

ファインチューニングしてない素のdavinciに文章を単純に全部大文字にしてもらう。

import openai
openai.api_key = secret
completion = openai.Completion.create(
    model="davinci",
    prompt=f"""It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. 
---
Make this sentence Upper-case.
""")
print(completion.choices[0].text)

結果

As seen in the 2014 film, it is an extremely large space station with

ダメだ。ぜんぜん会話が通じてない。

まったく同じプロンプトをChat GPTに聞いてみると、ちゃんと大文字に変換してくれる。
プロンプトはそんなに悪くないはず。

kurehajimekurehajime

素のdavinciに猫と犬の違いを聞いてみる。

import openai
openai.api_key = secret
completion = openai.Completion.create(
    model="davinci",
    prompt=f"""What is the difference between cats and dogs?""")
print(completion.choices[0].text)

結果

” and 65% of people correctly answered that cats are “independent

65%の人が猫は自立していると解答している…という文章のように見えるが、そもそも文章の構造がおかしい。andから文章が始まってるし、引用符の位置も壊れてる。
たしかに犬より猫のほうが自由なイメージはあるが、この回答は会話できているとは言い難い。

kurehajimekurehajime

同じ質問をファインチューニングしたモデルに聞いてみる。
文章変換は特に指定しない。ただ質問するだけ。

import openai
openai.api_key = secret
completion = openai.Completion.create(
    model="davinci:ft-personal-2023-04-02-14-05-16",
    prompt=f"""What is the difference between cats and dogs?""")
print(completion.choices[0].text)

結果

I mean of course: cats have whiskers, ->translate mokopen

あーーーー変な癖がついてる。
こっちは特に変換指定してないのに解答に->translate mokopenをつけている。

こういう副作用あるとなるとファインチューニングを使うの難しくなってくるなぁ…。

kurehajimekurehajime

結論

  • ファインチューニングで文章変換を学習させると何らかの法則性を会得してくれた。
  • しかしベースモデルとなるdavinci(ChatGPTより性能が劣るtext-davinci-003よりさらに性能が劣る素のモデル)は会話に適さない。
  • ファインチューニングすると悪いクセが付くことがある。

  • Open AIがtext-davinci-003gpt-3.5-turboのファインチューニングを開放しない限り、文章生成の用途に使うのはかなり難しい。ベースモデルがしょぼ過ぎるので結局ラベル付けくらいしか使い道がない。
kurehajimekurehajime

学習データを10倍に増やせばもう少しマシになるのかもしれないが、さすがに1万円払う気にはなれない。
マシになっても元が素のdavinciじゃ高度な会話は期待できないしなぁ。

kurehajimekurehajime

使い終わった学習済みモデルは消しとく。

openai api models.delete -i davinci:ft-personal-2023-04-02-14-05-16