こんにちは。
2021年11月8日から2022年2月7日まで開催されていたJigsaw Rate Severity of Toxic Commentsに参加してきました。
前回のPetコンペが終わって、コードの提出とリフレッシュをしてから始めたので2週間位の取り組みでした。
結果は2,329チーム中の41位で銀メダルを獲得することができました。
またこの結果よりcompetitions master に昇格することが出来ました!また別でポエム書くと思います。
それでは振り返りに入ります。
コンペ概要/データ・評価方法
wikipediaのコメントの有害コメントの深刻度の評価をする。
関連コンペ
タイトルに4とつけたのですが、過去に同様なコメントの毒性に関するコンペが行われていました。
- 【2018年】https://www.kaggle.com/c/jigsaw-toxic-comment-classification-challenge
- 【2019年】https://www.kaggle.com/c/jigsaw-unintended-bias-in-toxicity-classification
- 【2020年】https://www.kaggle.com/c/jigsaw-multilingual-toxic-comment-classification
私はいずれにも参加したことがなかったの上記コンペのデータやタスクについて理解しておく必要があり少し苦労しました。JigSaw1,2は振り返り記事が殆どなかった。。。
データと評価方法
本コンペのデータとしては下記の3つが与えられていました。
comments_to_scoreにはcomment_idと対応するtextが含まれます。validation_dataにはworker,less_toxic,more_toxicが含まれています。複数人のアノテーターに2つのtextのうちどちらがより毒性が高いかを判断してもらったデータの集合になります。
毒性順位や正解ラベルはなく、validation_dataと過去コンペのデータも使ってうまく学習してくれという感じでした。
評価方法としては、textに対して毒性の強さを数値でラベリング(大きいほうが強い)して、2つのtextをpickしてアノテーターと同じような評価をどれだけ再現できるかというものです。
同じテキストペアを複数人のアノテーターに渡して、それぞれ別の判断になる場合がありCVの理論値は0.824ということがディスカッションで言われていました。
予測するデータと評価する方法のイメージは図のようになると思います。
validation_dataが過去コンペデータと重複している
validation_dataには約14000種のtextがあり、その中の5-6割は2016年コンペのデータと重複していることが指摘されていました。CV計算や、追加データの作成で少し気をつける必要があったり、5年前の文章とtestの文章は同じ時期のものなのか?ということが気になるところです。
解法
上図のような感じです。
Data
2種類の追加データを作成。
- 本コンペで渡されていた validation_data.csv と同じ形式のもの
- 毒性があるかないかのバイナリラベルのデータ
いずれのデータも毒性のないデータが多すぎるので毒性があるデータ数に合わせて追加データを作成しました。
CV
MarginRankingLossでの学習時はJigsaw CV strategyを使用
BCEで学習するときはKFold
model選択
公開ノートとPublicLBはTF-IDF系のものが暴れまわってたのですが、publicが5%な点と、CVはBERT系のほうが高いことから、今後のためにもBERT系で攻めることにしました。discussionでは小さめのアーキテクチャのほうがいいようなことが書かれていましたが、手元では大きいほど良さそうなので学習時間が現実的な中でなるべく大きめのものを学習しました。
hugging face 便利だけど色々取り回しが違っていてtimm以上に苦労したのですがこちらのrepositoryで勉強しながら進めました。感謝しかありません。
アンサンブル
weightの調整方法がよくわからなかったので、optunaを使って最適化させました。
cols = ['less_toxic_pred', 'more_toxic_pred'] oof = [] oof.append(pd.read_csv('../input/jigsaw4-model2/bi003_deberta-large/oof.csv',usecols=cols).values) oof.append(pd.read_csv('../input/jigsaw4-model1/exp008_roberta-base/oof.csv',usecols=cols).values) def calc_score(weight): pred = np.zeros([n_data,2]) for p, w in zip(oof,weight): pred += p*w score = (pred[:,0] < pred[:,1]).sum()/pred.shape[0] return score class Objective: def __init__(self, n_models): self.n_models = n_models def __call__(self, trial): weight = [trial.suggest_uniform('weight' + str(n), 0, 1) for n in range(self.n_models)] return calc_score(weight) max_iter = 1800 SEED = 29 objective = Objective(n_models) sampler = optuna.samplers.TPESampler(seed=SEED) study = optuna.create_study(sampler = sampler,direction='maximize') study.optimize(objective, n_trials = max_iter, n_jobs = -1)
参考にしたサイト https://materialsinformaticsbeginner.blogspot.com/2021/01/blending.html
その他
- delta TF-IDF は普通のTF-IDFよりもCVは良かったですが、Bertに匹敵するところまでは行かなく、fitにラベルの入力が必要でその辺の調節も必要なため諦めました。
- 出来ればバイナリのモデルは回帰として解きたかったが、3種類の外部データでラベルに違いがあり正規化することが手間でできませんでした。
- バイナリデータ作成に関してもtoxicかnontoxicかの閾値を調整することでよりよく出来たのではないかという課題事項が残っています
- 公開ノートではやっていたRidgeでなくLGBMも試してみましたがそこまで良さそうではありませんでした。
最後に
コンペ終了後にコレするだけで上位に行けるという情報や、手元に金圏のスコアのサブがあったりとなんともいえない状態ではありますが、それでもデータを増やしてBERT系モデルをたくさんアンサンブルすればある程度いいところにはいけるだろうという狙いはいい結果につながったので良かったです。学びは多かったと思います!