クラウドエンジニアのノート

情報技術系全般,自分用メモを公開してます。

Kaggle Coleridge 52nd solution

はじめに

今回Kaggleに参加して初めてメダルを取ることができました。 Public scoreでは全然メダルに届いていなかったので、半ば諦めていましたが 大幅shakeがあり、たまたま銀メダルを取ることができました。

簡単にですが、その解法を公開します。

Coleridgeコンペについて

論文内で示されているデータセット名を当てるコンペです。 渡されたデータは論文のテキストのみです。

validationの分け方

今回のコンペでは、学習セットに130ほどのデータセット名(ターゲット)がありますが、テストセットには学習に出てこないデータセット名が含まれています。

そのため、validationをちゃんと分けるには、それぞれデータセット名の重複なしで分けなければいけません。 なので、幅優先探索を実装して、データセット名が重複しないように8:2で分けました。

本当はk-foldに分けたかったですが、組み合わせの数的に無理でした。

しかし、違う文字列で同じデータセット名を指している場合があり、完全に切り離すのは難しく、実際はいくつか重複があったと思われます。

Pipeline

まず推論のパイプラインの図を示します。

f:id:tontainoti:20210707135804j:plain
coleridge 52nd solution pipeline

文章を短く区切って、dataset名が存在するか文章を2値分類して、カーネルにもあったMLMモデルでそれがデータセット名なのかを予測します

そして、1つの論文に対して予測されたデータセット名のリストに対してjaccard係数を計算し、0.75以上の文章をフィルタリングします。(文章が短い方を残す)

最後に、これもカーネルにあったexternal datasetsと予測を結合します。 いろいろ試したのですが、シンプルにcsvに存在したらそれを使用し、無ければBERTの予測を使うというやり方が一番スコアが良かったです。

Shorten sentence

これはカーネルにあったものをそのまま流用しています。

Classifier

この分類器を使うアイディアは、一緒に参加した研究室の先輩のアイディアです。 きちんと検証してないのであれですが、この分類器が思ったより効いており、チームの上位subの殆どがこの分類器を入れたものでした。

入力文書に、データセット名が含まれているがどうかを予測します。 シンプルに2値分類です。 また、追加情報として、BERTのfc層へ、BERTからの特徴量と、単語数・大文字の単語数・単語の大文字率をconcatしてます。

MLM

以下のカーネルのほぼ丸パクリです。

[Coleridge] Predict with Masked Dataset Modeling | Kaggle

Jaccard filter

これもカーネルにあったやつです。 全部の予測が終わってから、[set(データセット名候補1, ...), set(),...,set()]の状態になったリストを渡すと、フィルタリングしてくれます。

def jaccard_filter(org_labels, threthold=0.75):
    assert isinstance(org_labels, list)

    filtered_labels = []
    for labels in org_labels:
        filtered = []

        for label in sorted(labels, key=len):
            label = clean_text(label)
            if len(filtered) == 0 or all(jaccard(label, got_label)
                                         < threthold for got_label in filtered):
                filtered.append(label)

        filtered_labels.append('|'.join(filtered))

    return filtered_labels

試したこと

  • DiceLoss, FocalLoss等の不均衡データに強いロス: スコア下がった
  • NER: 有効じゃなさそうだった
  • SciBERT: 変わらなかった
  • external datasets csvを増やす: 余計な文字列がヒットしてスコア下がった
  • BERT→Electra: スコア下がった
  • CONNECTION_TOKENの変更: 対象の文書が増えてスコア下がった
  • ビームサーチでk-fold: 計算時間的に厳しかった

感想

取り組むのが遅かったというのもあり、第4位の解法と同じアイディアを思いついたのですが、結局ローカルのCVが悪くsubしませんでした。これをもう少しちゃんと取り組んでれば賞金圏行けたと思うと非常に悔しいです。

あとはもう一つメダルを取って、早くKaggle expertになりたいです。

第4位の解法

当時のアイディア

以下は当時Githubのissueに挙げてた原文(悔しいので載せちゃいます)

単純にtitle_caseだけの単語列は多い

しかし、title_case + (略)みたいなパターン(例: Alzheimer's Disease Neuroimaging Initiative (ADNI))はデータセットを指している場合が多い印象

これをルールベースで抜きたい
正規表現できた

[A-Z]{1}['a-z]+\s([a-z]{1,3}\s|[A-Z]{1}['a-z]+\s)+\([A-Z]+\)