KaggleのLLM系のコンペでは、コンペのホストから提供されるデータがほとんどないor全くない場合が多いです。
そんなとき、サブミッションした時のLB(リーダーボード)のスコアと、手元で検証した時のCVのスコアで相関のあるデータセットが手元に作れれば、よりスコアの改善に役立てることができます。(サブミットによってKaggle側のデータがどのようなものかを探る行為をProbeと呼ぶらしいです)
今回はそのようなデータセットの作り方を紹介しようと思います。
まずは品質をあまり気にせず大量のデータと集める
ChatGPTのAPIなどを利用して大量のデータを作ります。
ただしコンペの参加者の方々が、自分の作ったデータセットを公開してくれることが多いので、それらを拝借するのが手っ取り早いと思います。
なお、この時点でそこまで高い品質のデータは求めていません。
明らかに形式が間違っていたりするようなデータでなければとりあえずOKです。
玉石混交みたいな感じのデータセットになるイメージです。
いくつかのモデルでサブミットをした時のスコアを控えておく
ここでは例として、model_A
とmodel_B
とmodel_C
とmodel_D
とmodel_E
のような5種類のモデルを使ってそれぞれをサブミットして、そのそれぞれのLBでのスコアを得ることができたとします。
以下のようにして、モデルとそれをサブミットした時のLBのスコアを対にした辞書を作成します。
scores = {
# key→サブミットした時に使ったモデル
# value→サブミットした時のスコア
model_A: 0.71,
model_B: 0.68,
model_C: 0.70,
model_D: 0.68,
model_E: 0.65
}
集めたデータ群からLBとCVで相関のあるデータ群を抽出する
基本的なアイデアとしては、
「サブミットした時のスコア」と「ローカルデータでのスコア」
の差が小さくなるように、ローカルデータの抽出を繰り返して洗練していくイメージです。
実装例としては以下のようになります。
def fitness(features, targets):
"""
各モデルでサブミットを行った時のスコアと、
そのモデルでサンプルデータの推論を行った時のスコアの差の累積値を計算する。
"""
score_losses = np.array(list(scores.values()))
# 各モデルで部分集合の推論をした時の損失を計算する。・・・①
predicts = np.array([model.predict(features) for model in scores.keys()])
local_scores = metric(np.array(predicts - targets))
return np.abs(local_scores - score_losses).sum()
def find_best_sample(all_collected_data : pd.DataFrame, sample_size: int = 100, iterations: int = 500):
"""
集めた全てのデータセット(all_collected_data)の中から、Kaggle側のデータと似ている部分集合を抜き出す。
"""
best_sub_data = None
best_loss = float('inf')
for _ in range(iterations):
# all_collected_dataからランダムに部分集合(sub_data)を取り出す。
sample_indices = np.random.choice(len(all_collected_data), sample_size, replace=True)
sub_data = all_collected_data.loc[sample_indices]
features = sub_data["特徴量"]
targets = sub_data["正解"]
# 上記のfitness関数を使って、部分集合sampleの損失を計算する。
current_loss = fitness(features, targets)
# 現在の部分集合の損失が暫定で1番小さくなったら、現在の部分集合をベストなものとして更新する。
if current_loss < best_loss:
best_loss = current_loss
best_sub_data = sub_data
best_idx = sample_indices
return best_sub_data, best_loss, best_idx
上のコードについてはここを参考にさせていただきました。
fitness
関数の①ではscikit-learnのモデルのインターフェース風に推論を行って損失計算していますが、適宜目的に沿って書き換えれば所望のデータセットを作ることができます。
言うまでもないことですがall_collected_data
もデータフレームである必要はありません。