機械学習
記事内に商品プロモーションを含む場合があります

LLM Prompt Recoveryに挑戦| Kaggleチャレンジ記録

Aru

Kaggleのコンテスト「LLM Prompt Recovery」へのチャレンジした記録。今回は、コンテスト終了2週間前からの参加となり、キャッチアップに追われる展開でした。最終的な順位は696/2175位と、残念な結果に終わりました。限られた時間の中での取り組みでしたが、コンペの内容や自分の取り組みを記録として残しておきます。

LLM Prompt Recoveryの概要

簡単にまとめると、コンペの目的は、与えられたプロンプトに従ってリライトされた文章が、どのようなプロンプト指示に基づいて書き直されたのかを予測するものでした。

具体的には、元の文章(①)を提示され、次にプロンプト指示文(②)に基づき、Gemmaモデルを使ってリライトされた文章(③)が生成されます。この①と③のペアが与えられ、②のプロンプト指示を推測する必要があります。

たとえば、「SFチックに書き直してください」といったプロンプトが与えられた場合、元の文書①と指示に従って書き直された③が与えられます。ここから、「SFチックに書き直してください」を予測するのがコンペの目標です。

評価指標はsentence-t5-baseモデルを使って計算したシャープコサイン類似度です(後ほど書きますが、このsentence-t5-baseモデルが最終結果に大きく影響を与えました)。

端的にいうと、このコンペの目標は「どのようなプロンプト指示で、入力が出力結果に変化したのかを当てる」ことです。

特徴的なのは、トレーニングデータが1つしかなく、テストデータも同様に1つだけという点で、学習用のデータも、サンプルも全く無いことです。そのため、「指示プロンプトにどのようなものあるのか、元の文章はどんな感じなのか」全くわからない手探り状態で進める必要があります。

例えば、プロンプトとしては「この文章を歌に変えて」のようなトーンを変える指示もあるかもしれないし、「主人公を〇〇に変えて」といったものをもあるかもしれないし、「文章をフォーマルに」といった指示もあるかもしれません。本当に多種多様なプロンプトを想像する必要がありました。個人的には、ここが一番の難関でした。

英文をターゲットにしているため英語の変化を理解しなければならないのですが、英語の細かな違いやニュアンスの差がわからなくて苦戦しました。

取り組み内容

とりあえず、公開コードとディスカッションを眺めてみる

公開コードをみると、全部に対して平均プロンプト(なんとなく無難なプロンプト)を使うだけでもメダル圏内のスコアになっていました。

逆に、LLMモデルを訓練データを使って学習させるもののLBはイマイチでした。

ディスカッションでも、どのようなプロントの場合もスコアが高くなる平均プロンプト」の話題が出ていました。

今回はノートブックを日本語に翻訳できるchromeの拡張機能が役に立ちました

Kaggleの公開ノートブックを日本語に翻訳するChrome拡張機能
Kaggleの公開ノートブックを日本語に翻訳するChrome拡張機能

学習コードを作成

公開コードを参考に、学習コードをGoogle Colab上に作成しました。学習コードはLoRAを使ったやつでそこまでコードが難しくないので、いろいろカスタムすることができました。

最終的には、Mistral 7Bを学習させるコードに修正し、学習したモデルをkaggleにアップするように変更しました。

また、対となるinferenceコードも自作して結果を確認しつつ学習を繰り返しました。

ここから、試行錯誤が始まります。いろいろハイパーパラメータを変更して試したけど、あまりスコアが上がらない・・・という日々が続きます。

この時点でちょっと途方にくれました

データセットを自作

最初は、他の方が作成したデータセットを利用して学習していましたが、データセットを自作してみることにしました。具体的には、データセットは、GPTやローカルで動かしているgemmaで作成しました。

とりあえず、1500データくらいの自作データセットを作成して学習させてみましたが、結果はスコアダウン・・・

ネイティブじゃないので、英語の細かい表現の差がわからず、LLMの出力した書き換え後がちゃんと指示どうりか確認できなかったのもスコアが上がらなかった原因かもとか考えていますが、単に指示のパターンがコンテストの指示と違うという可能性も。

ここまででの取り組みで、残り1週間に期限が迫ってきました。

方向性を変更

ディスカッションを眺めていると、ちょっと気になるノートブックの情報を発見。プロンプトに擬似的な入力→LLMの回答を入れる(?)というものです。

面白かったので以下の記事にまとめています。

あわせて読みたい
擬似会話でLLMの回答をコントロールする方法|Few-shot Promptingの応用テクニック
擬似会話でLLMの回答をコントロールする方法|Few-shot Promptingの応用テクニック

学習より、こちらのやり方の方が出力がコントロールしやすいことがわかったので、学習したモデル+このやり方にトライしてみることにしました。

最終的に、この取り組みのコードがLBベストになりました。

その他取り組み

sentence-t5-baseの評価を知りたかったので、平均プロンプトをいくつか作成してみて変化を見ていました。が、変化の法則を見つけられず。途中で諦めました。

上位解放は、評価モデルに対して敵対的攻撃をする手法が紹介されています。sentence-t5に対する取り組みのレベルの差を感じました。

結果と感想

結果は、696/2175位でした(終了してしばらくして順位が変化しました。不正アカウントが削除されたと思われます)。

LLMは少しいじってますが、ここまで思考錯誤したことがなかったのでLLMの学習・プロンプトエンジニアリング等の学習として、参加してかなりよかったと感じています。

正攻法(?)で、学習させても結果が出にくいコンペということで、敵対的攻撃(平均プロンプト発見)に上位手法が集中してました。キーワードで出てくる「lucrareaってなんだよ」と思ってしまいます。

コンテスト終了後にlucrareaをsentence-t5のvocabで調べてみると、存在していました。ブルートフォースアタックしていたら見つけたのかも。ただ、個人の嗜好として、結局、そちら方向にはエネルギーを費やさなかった気がします。

コンテスト後に以下のようなコードで実験してみるとlucrareaが登場しました。以下のコードは、sentence-t5のvocabから単語を拾って、コサイン類似度が一番上がる単語をつなげていくというものです。

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('sentence-transformers/sentence-t5-base')


# スペシャルトークンを除去した、トークンリストを作成
tokenizer = model.tokenizer
vocab = tokenizer.vocab
vocab_list = []
for e in vocab :
  if "<" not in e : vocab_list.append(e)

# ターゲットとする指示をエンコード
target = ["Please improve this sentence more formally.", 
          "Convert this into a sea shanty", 
          "Improve this text by transforming the tone into poetic lyrics.",
          "Rewrite this text by infusing it with the charm and whimsy of a romantic comedy.",
          "Rewrite this text to evoke mystery."]
target_enc = model.encode(target)

s = ""
cur_sim = -1

for i in range(20):
  enc = model.encode([s + e for e  in vocab_list])
  sim = cosine_similarity(target_enc, enc).sum(axis=0)  
  sel = np.argmax(sim)
  if sim[sel] > cur_sim :
    s += vocab_list[sel]
    cur_sim = sim[sel]
  else: break
  print(s, (cur_sim/len(target))**3)

print(s)
あわせて読みたい
Sentence Transformersで文章をベクトル化し、類似度を調べる方法
Sentence Transformersで文章をベクトル化し、類似度を調べる方法

下記のようにlucrareaが登場します。ちなみに、これを計算するのにT4で約3分でした。いくつかのプロンプトを用意してスコアがあがるように総当たりすると確かに登場するようです。

ちなみに、この出力をそのまま使ってサブすると0.63のスコアが達成できました。これだけでとりあえず、結果をみると0.67ですが、少し下がるのはターゲットとするプロンプトが適当すぎるからだと思います。とはいえ、簡単に0.63です・・・

結果
▁modificări 0.5116659052147181
▁modificări▁this 0.5895178193225697
▁modificări▁this▁writing 0.6195698084142054
▁modificări▁this▁writingaesthetically 0.6442036323090553
▁modificări▁this▁writingaestheticallyley 0.6517300797073368
▁modificări▁this▁writingaestheticallyley▁pitched 0.656351273539928
▁modificări▁this▁writingaestheticallyley▁pitchedlucrarea 0.6671248219217195
▁modificări▁this▁writingaestheticallyley▁pitchedlucrarea. 0.6706090414279353
▁modificări▁this▁writingaestheticallyley▁pitchedlucrarea.

まとめ

今回は、LLMの学習方法などを理解することが目的だったので、2週間という短い期間でしたが参加してよかったです。これまでよりLLMについて理解が深まりました。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました