こんにちは、インターンの玉木です。
昨年9月まで電気通信大学修士課程で対話システムの研究をしながら、Studio Ousiaのインターンプログラムに参加していました。大学院の修士課程はすでに修了しましたが、現在もインターンを続けています。
先日までKaggleで開催されていたWSDM KKBox's Music Recommendation Challengeに参加していました。
結果は1081チーム中47位でした。本記事ではこのコンペティションと参加した感想を紹介したいと思います。
KKBox's Music Recommendation Challenge
今回のコンペティションではKKBOXという音楽配信サービスのデータを使い、各ユーザーが一度聴いた曲を再度聴くかどうかの2値分類の精度を競いました。評価指標はAUCです。
与えられたデータの説明は記事の最後にあります。
実際の流れ
モデル作成は以下のようなプロセスで行います。
- 特徴量抽出
- モデル作成
- モデル評価
Kaggleではkernelという他の参加者の分析結果や上記1~3を行った結果などが共有されている場があります。
自分はupvoteが多いkernelを参考にして始めることが多いです。今回はこのkernelに特徴量を追加していく形で作業を進めていました。Kaggleではほとんど1の特徴量抽出で差がでます。2, 3のプロセスはほとんどやることが変わらないためです。
検証データの準備
今回のコンペティションのデータは時系列データで、訓練データとテストデータは期間で分割されています。時系列データの予測モデルを評価する場合、同様に期間で分割して作られた検証データを作る必要があります*1 。
しかし、今回使用するデータには明示的にtimestampが与えられておらず、訓練データから期間で分割された検証データを作ることができませんでした。timestampが与えられていれば良い検証データを作成できるだけでなくそれを用いて新たな特徴量も作れるのですが、リークに繋がる可能性を考慮して削除されたようです*2。
多くの参加者が良い検証データを作るのに苦戦している中、訓練データとテストデータがシャッフルされておらず、時系列順に並んでいることを発見した参加者がいました*3。
曲が録音された年度の代わりにユーザーがサービスに登録した日付(registration_init_time)をplotすると、時系列順に並んでいることだけでなく、細かい日付まで知ることができます。横軸にデータのインデックス、縦軸にユーザーがサービスに登録した日付をplotした図が以下の図です。青色の部分が訓練データ、緑色の部分がテストデータです。

Kaggle主催者がわざわざリークを考慮して含まなかった特徴量をこんな簡単に見つけられてしまっていいのかというツッコミもありますが、これで訓練データから良い検証データを作成できるようになり、より良いモデルの評価ができるようになりました。これがなかったらLeaderboard上のpublicデータでしか評価ができず、かなり厳しかったように思えます(publicデータにオーバーフィットしてしまう可能性もあります)。
自分がやったこと
最初に各カテゴリカル変数の頻度を計算しました。カテゴリカル変数とはグループで分類されるような変数のことを指します。今回与えられたデータでは、ユーザーID、 曲のID等ほとんどがカテゴリカル変数です。曲の長さ等はカテゴリカル変数に含まれません。実はこれらの特徴量を用いるだけで一気に上位に入ることができます。特にユーザーの頻度情報が効きました。
次に各カテゴリカル変数における目的変数の平均値(期待値)を計算しました。例えば、曲に対する目的変数の平均を取ることで、その曲がどのくらい再度聴かれているかを表現することができます。target based encodingなどと呼ばれることがあるのですが、目的変数の情報を用いるためそのまま使うとリークしてしまいます。自分自身の情報を含まないようにデータを4分割して、その訓練データのアイテムが含まれない他の3つを使って特徴量抽出をしたのですがAUCはむしろ下がってしまいました。そのときは極端に頻度が少ないユーザーや曲が存在するためなのかなーと考えていましたが、実は違う方法で特徴量を計算すれば非常に有効な特徴量になりました。後述します。
次にtimestampを用いて特徴量の作成を試みました。しかしこれも同様の後述する理由で失敗してしまいます。
最終的に各カテゴリカル変数の頻度に加えて、各カテゴリカル変数を組み合わせた頻度、その他細かい特徴量を追加してLightGBM を用いたGradient Boosted Decision Trees (GBDT) のシングルモデルの予測結果をsubmitしました。
その他ニューラルネットワークやFactorization Machinesも試したのですがGBDTに劣り、それらの結果をアンサンブルしても、むしろAUCが下がってしまいました。
答え合わせ
Kaggleではコンペティションが終わったあとにdiscussionという場でsolutionが公開されることがあります。
3位の方のsolutionが分かりやすかったので、ここで比較をしてみたいと思います。*4
この手法では、以下の特徴量を抽出したそうです。
1) mean of target from history by categorical features (pairs and triples)
2) count – the number of observations in all data by categorical features (pairs and triples)
3) regression – linear regression target by categorical features (msno, msno + genre_ids, msno+composer and so on)
4) time_from_prev_heard – time from last heard by categorical features (msno, msno + genre_ids, msno+composer and so on)
5) time_to_next_heard – time to next heard by categorical features (pairs and triples which include msno)
6) last_time_diff – time to the last heard by categorical features
7) part_of_unique_song – the share of unique song of artist that the user heard on all unique song of artist
8) matrix_factorization – LightFM. I use as a user msno in different context (source_type) and the msno was the feature for user.
特徴量の構成は私がやったことと似ているような気がします。では、私のやったこととの主な差分はどこにあるのでしょうか?
その差分は特徴量の作り方にあります。3位の方は学習には訓練データの直近のデータ35%を学習に使い、残りの65%のデータから特徴量を作成しています。テストデータの特徴量の抽出にはすべての訓練データから抽出しています。自分は各カテゴリカル変数の目的変数の平均値の計算をデータを4分割して行いましたが、それがよくなかったようです。また、上記4~6のような時系列の特徴量を作る際、私はデータを分割せずにそのまま特徴量抽出をおこなっていました。結果として直近の似たデータとの差分等をとることになってしまい、意味のある特徴量を抽出できなかったようです。
1位の方もsolutionを公開しています*5。1位の方はGBDTとニューラルネットワークのアンサンブルを用いています。GBDTだけではLeaderboardで3位のスコアだったそうですが、ニューラルネットワークと組み合わせることにより、より高いスコアを獲得しています。GBDTとニューラルネットワークでは違った特徴の捉え方をするためだと考えられます。
1位の方が作成したモデルの概略図がこちらです。かなり複雑です。
また、1位の方は次のようにも述べています。
Again I learn how powerful GBDT is. A simple GBDT can beat an ensemble of 30 carefully-designed NNs (of course GBDT is an ensemble model itself).
以前Kaggleであったinstacartのコンペの3位の方がニューラルネットワークのみを用いてモデルを作成し話題になりました。個人的な感想ですが、今回のようなタスクにおいてニューラルネットワークのモデルを作成するのはかなり難しそうです。
感想
いい勉強になりました。コンペ期間中にうまくいかなかった原因を突き止められなかったのは正直悔しいですが次に活かしたいと思います。
本気で上位を目指そうと思ったら熱意が足りませんでした。1位の方はsubmit数もダントツに多いです。
データ分析の知見を得るのにKaggleはとても良いと思います。今現在もおもしろそうなコンペがいくつもあるので興味のあるコンペに参加してみてはいかがでしょうか。
与えられたデータ
train/test.csv(targetはtrainのみ) |
説明 |
msno |
ユーザーのID |
song_id |
曲のID |
source_system_tab |
そのイベントが起こったときのタブ名。search, radio, mylibrary等 |
source_screen_name |
ユーザーが見ているレイアウト |
source_type |
ユーザーが最初にその曲を聴いたときのエントリーポイント。album, online-playlist等 |
target |
target=1がその曲をユーザーがもう一度聴いたことを示し、target=0が聴かなかったことを示す |
songs.csv |
説明 |
song_id |
曲のID |
song_length |
曲の長さ(ms) |
genre_ids |
曲のジャンルのID |
artist_name |
アーティストの名前 |
composer |
作曲家の名前 |
lyricist |
作詞家の名前 |
language |
曲の言語 |
members.csv |
説明 |
msno |
ユーザーのID |
city |
住んでいる場所 |
bd |
年齢 |
gender |
性別 |
registered_via |
サービスの登録方法 |
registration_init_time |
サービスを開始した日付 |
expiration_date |
サービスが使用できる期限 |