2nd Place Solution
1.はじめに
運営の皆様、参加者の皆様ともにコンペを盛り上げてくださりありがとうございます。
今回、チームマージ+StakingによってPublic3位、Private2位になることが出来ました。
チームメンバーの皆様にも感謝申し上げます。
特徴量やモデル作成について簡単にまとめます。
2.モデル全体図
チームメンバーと協力しながらモデル作成を実施し、最後2段階Stackingしました。
![ac77041a-f762-4515-bca2-ef060c6ffa8b.png](https://probspace-stg.s3-ap-northeast-1.amazonaws.com/uploads/user/6c1711ad4415b5b6ceaf8b2ec35b030c/images/UserImage_164/ac77041a-f762-4515-bca2-ef060c6ffa8b.png =300x)
citesなしの全データ+test(910,608行)を使って集計を行うパターンとcitesがあるデータ+test(74,201行)を使った集計を行うパターンで主に分けていました。
3.目的変数
コンペ初期ではcitesをそのまま予測していましたが、citesを使った予測ではOut of Foldにおける予測最大値が1,000前後で実際の正解値との乖離が大きいことを解消できませんでした。
そこで、cites - doicites の差分を目的変数に変更して、予測値にdoicitesを加えるようにしたところ、Out of Foldにおける予測最大値が5,000~7000程度になったのでコンペ中盤以降はcites - doicitesの差分を目的変数としていました。
4.特徴量作成
最終的には全体で9,773列まで特徴生成をして、削減や選択などは実施せずそのまま使用していました。
作成した特徴を全て載せますので、長くなっております。
他に効いた特徴あれば教えてください。最後の方は特徴生成のアイデアは枯渇していました。
submitter
- textheroで大文字->小文字変換、stopwords削除
- CountRankEncoder
- CountEncoder
- OrdinalEncoder
authors
- authors_parsedの方が使いやすかったので使用せず
title
- textheroで大文字->小文字変換
- 記号などは科学的に意味あるかもと思い、削除せずそのまま使用
- word2vec(size:30, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- fasttext('cc.en.300.bin')で埋め込み
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- tfidf(max_features=4000)
- PCA, TrancatedSVDで64次元に圧縮
- scibertで埋め込み
- 論文のタイトルを眺めていると物理系の論文も多かったので、扱う波長帯のラベルを付与
- exp. Xray, Milli, UV, Gamma, cosmicなど
- 扱う物質の大きさのラベルを付与
- exp. quantum, atom, dust, gas, star, galaxyなど
- titleに含まれる数字・記号をカウント
- titleの単語数に対する数字、記号の数の割合
- titleの単語数
- titleの単号数の統計量との差分
- quantumという単語が含まれた論文が全体の10%弱存在していたため、quantumが含まれる論文のdoi_citesを年ごとに塁和
- titleに含まれる単語で単語間のネットワーク表現
- 媒介中心性、近接中心性を算出
- textheroで大文字->小文字に変換
- word2vec(size:30, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- fasttext('cc.en.300.bin')で埋め込み
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- scibertで埋め込み
- pages, figures, tablesを抽出
journal-ref
doi
- トピックを参考に出版元、掲載誌などを判定
- 掲載誌に対してword2vec,fasttextで埋め込み
- PCA, TruncatedSVDで次元圧縮
- 掲載誌に対してtfidfで埋め込み
report-no
categories
- textheroで大文字->小文字変換
- 記号などは科学的に意味あるかもと思い、削除せずそのまま使用
- word2vec(size:20, window:5)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- fasttext('cc.en.300.bin')で埋め込み
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- tfidf(max_features=1000)
- PCA, TrancatedSVDで64次元に圧縮
- scibertで埋め込み
- categoriesの1st、2nd、Lastだけ抽出しそれぞれ別の列に格納
- 1st, 2nd, Lastで共通したEncoding
- CountRankEncoder
- CountEncoder
- OrdinalEncoder
- categoriesに含まれる単語で単語間のネットワーク表現
- 媒介中心性、近接中心性を算出
license
abstract
- textheroで大文字->小文字変換
- 記号などは科学的に意味あるかもと思い、削除せずそのまま使用
- word2vec(size:30, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- fasttext('cc.en.300.bin')で埋め込み
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- scibertで埋め込み
- title + abstractでword2vec, fasttextで埋め込み
- abstractに含まれる数字・記号をカウント
- abstractの単語数に対する数字、記号の数の割合
- abstractに含まれる$の数
- abstractの単語数
versions
- 1st created year, month, unixtime
- 2nd created year, month, unixtime
- Last created year, month, unixtime
- 細かな時間を捨てて、年月表記に変更
- ( year-min(year) )*12 + month
- 年月の差分、unixtimeの差分を算出
update_date
- datetime変換
- 経過年月表記に変更
- versionsで求めた年月からの差分
authors_parsed
- submitterと結合
- word2vec(size:20, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- categoriesと結合
- word2vec(size:20, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- titleと結合
- word2vec(size:20, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- categories, titleと結合
- word2vec(size:20, window:10)
- PCA, TruncatedSVDで2次元に圧縮
- 圧縮前の生値もそのまま使用
- 1st author, 2nd author, Last authorを抽出
- 共通してEncoding
- CountRankEncoding
- CountEncoding
- OridinalEncoding
- authorsで単語間のネットワーク表現
- h-index, g-indexを導出
- 研究を定量化する指標を調べる過程でググったら見つけたので実装
submitter, 1st author, last authorが前回投稿した論文の情報を抽出
- 前回からの経過時間(unixtime)
- doi_cites
- pages
前年、前々年でaggregation
- 年ごとにaggregationで集約した情報をpd.shiftで前年の情報を抽出
- 集約した情報はPCA, TrunscatedSVDで次元圧縮
aggregation
- categories系 x created_year系 x カテゴリカル変数でpivottable
5.試したがうまくいかなかったこと
- LightGBMでlossをQuantile lossに変更して、予測信頼区間65%以内に入っているデータでPsuedo
- NNモデルでDense -> Conv1d(参考:Kaggle MoA2nd Solution)
- LSTM -> self attentionモデル(最後実装しようとしたが時間が足りず断念...)
- doi_citesを予測して、citesを予測する2段階学習(citesがないデータをうまく扱いたかったが、断念)
6.Cross Validation
- citesが存在するtrainデータの数が少なかったので、fold数は10で固定
- CVの切り方はいくつか試した中で最もCVとLBが良かった「doi_citesの整数部でStratified KFold」
- 他に試したもの
- citesの整数部でStatifiedKFold
- first created yearでStratifiedKFold
- 1st_categoriesでStratifiedKFold
- KFold
- 最後までCrossValidationの切り方はこれでいいのか悩んでいました。
7.Feature Importance
色々特徴を作った割には、埋め込み表現系が強く残っていて学習済みモデル(SciBERT, fasttext)やword2vecの優秀さが身に染みました。
![3613916a-6415-45ba-9cb0-96da59ddbe8a.png](https://probspace-stg.s3-ap-northeast-1.amazonaws.com/uploads/user/6c1711ad4415b5b6ceaf8b2ec35b030c/images/UserImage_163/3613916a-6415-45ba-9cb0-96da59ddbe8a.png =300x)
8.Stacking
seed(0, 1, 2, 3, 4)ごとにoof, subを出力して、62ファイルを2段階でStackingしました。
3段階、4段階と階層を増やしてみましたが、3階層くらいから過学習気味になりLB悪化したため2段階までで終盤は固定していました。
9.後処理
Stackingした後の予測値で1を切るものに対して、3パターン試してCVとLBの変化を確認
- そのまま使用
- 1以下は全て0
- 1以下は全て1
この中だと全て1にした時がLB向上したため、trainデータと同様にtestも引用数が1以上のものになっている、もしくは0が含まれていても数は少ないという予想で後処理を実施しています。
10.まとめ
今回は同じ会社のメンバーでチームを組んで早い段階から分担作業が出来ていたので、効率よく特徴生成をすることが出来ました。
(初期はバラバラに取り組んで、誰か一人でもtop10に入ればマージしてその人のやり方を踏襲する方針でした)
seedごとにoof, sub保存 -> 階層的なStackingによって劇的にスコア向上したので回帰予測だと微妙な値の変化のバリエーションを持つことが重要かなと思いました。(LB:0.487456 -> LB:0.484932)
私も特徴作成方法などはatmacup#10を参考にしていました。(https://www.guruguru.science/competitions/16/)
probspaceをはじめ、atmacupなど国内の分析コンペで知見を共有してくださった皆様のおかげで今回は2ndにたまたまなることが出来ましたので、私も作成した特徴は全て紹介しました。
少しでも参考になれば幸いです。
最後に、面白いテーマで開催してくださった運営の皆様誠にありがとうございます。次回の開催を楽しみにしております。