今回はKaggleの“Mercari Price Suggestion Challenge”で上位10%入り(Bronze入賞)した分析過程を記載いたします。
※kaggleに関する他の記事はこちら
Kaggleから学ぶ最新の機械学習実践Tips2018
Kaggleのtitanic問題で上位10%に入るまでのデータ解析と所感
KaggleのHousePrices問題を決定木系のアンサンブルで解く

 

データサイエンティストは四六時中、前処理とスプラトゥーン2をしている」と
揶揄されるほど、データ分析の過程において、前処理(とスプラトゥーン2による息抜き)は重要なものとなります。

しかし、一定レベル以上のデータサイエンティストにおいて、
既知の課題、あるいはKaggleのようなカーネル公開型のデータ分析コンペにおいては、
データ分析における前処理過程は、ある程度共通してきます。


必要な前処理を終えた後に、さらにデータ分析の精度を高めるためには、
機械学習の実装を工夫すること、あるいは最新の機械学習手法にトライすること、が求められます。


そこで今回はあえて、前処理パートの記載は、ある程度割愛して、
分析過程の後半である機械学習パートを重点的に記載いたします。
特に、deeplearningの実装の工夫、および決定木系の最先端手法であるdeep forestについて
解説いたします。



| (1) Mercari Price Suggestion Challengeとは

Kaggleとは、世界中のデータサイエンティストが参加する
データ解析のオンラインコンペサイトとなります。
分析結果を投稿すると、即座に精度が判定され、参加者の中でのランキングがつけられます。
賞金が出る企業からのChallengeあることや、ジョブオファーにつながることもあるため、
解析精度上位に与えられる”メダル”は、データサイエンティストにおける一種の称号となります。

今回は、Mercari提供のコンペとなります。
Mercariは、言わずと知れた、日本最大級のフリマアプリとなります。
特に若年女性の利用者が多いことが特徴でしょうか。
今回は、出品された商品ごとに、
各商品の、名前や、コンディション、カテゴリ、ブランド、などのデータから、
落札価格を予想する、という問題となります。

なぜ、Mercariが、落札価格を予測することが必要なのでしょうか。
落札価格の予想から売上を予測したい、ということもあると思いますが、
それ以上に、売上をより伸ばすため、であると思われます。

つまり、あくまで推測ですが、Mercariの課題は、
“ユーザに適切な出品価格を知らせること”、なのではないかと思われます。
なぜなら、取引額の10%がMercariの取り分となるためです。

現状、落札価格を予測できないために起きていると思われることを整理すると下記と想定されます。


上記のように、取引成立、不成立のケース、それぞれで、Mercariにとって、
もし適切な落札価格を予想して、ユーザに教えることができたら、
Mercariもユーザも、もっと儲かることができたケースが存在します。


さて、落札価格予想がMercariにとって重要であることが理解できたところで、
コンペで実際に提供されたデータの項目、およびデータ量を見ていきましょう。


train_data(学習用の正解付きデータ:1,482,535データ)

id 商品ID
price 商品の落札価格(今回、test_dataで予想すべき値)
name 商品名
item_condition_id 商品の状態(5段階評価)
category_name 商品カテゴリ
brand_name 商品ブランド名
shipping 送料を出品者が払うか落札者が払うか
item_description 商品詳細


test_data(価格予想するデータ:693,359データ)

id 商品ID
name 商品名
item_condition_id 商品の状態(5段階評価)
category_name 商品カテゴリ
brand_name 商品ブランド名
shipping 送料を出品者が払うか落札者が払うか
item_description 商品詳細


これらの値は、出品するユーザが記載するものであることに留意です。(つまりブランクなどもあり得る)
実際のレコードは下記のようなものとなります。


予測モデルの精度は、RMSLE(平均二乗誤差:各商品の実際の価格と、予想価格の差の合計が小さいほどよい)
によって評価されます。


また、今回は、分析において下記の条件がありました。
・データ解析(プログラムの実行)は、オンライン上のカーネルで行うこと
・解析処理時間は60分以内であること
・ステージ1では、train data/test dataが公開され、そこで作成したモデル(プログラム)に対して、
 ステージ2では、別のtest data(非公開)で適用され、最終的な順位を決定する

これはテストデータにおける過学習を避けること、および現実的な時間内での処理を終えること、を求めたためと思われます。
「Kaggleは、あなたのプログラムの処理を12時間も待つことも、コンペの終了を先延ばすこともしたくない。」とコンペの説明ページに記載されておりました。
※後述しますが、この時間制限は、非常にタイトでして、ここに機械学習における実践的でテクニカルなスキルが求められます。



| (2) データ探索・前処理

まずは、test dataで予想するべき「商品の価格」が、
train dataでは、どのように分布しているのか確認してみます。


低価格帯に、商品が集中しているようです。(ポワソン分布的)

カテゴリごとに見てみるとどうでしょうか。


カテゴリごとでもやはり、低価格帯に商品が集中しているようです。
よって、価格は、対数をとって、分析することにします。


次に、各カテゴリごとの、価格の平均値・中央値・最小値・最大値を確認いたします。

カテゴリ名 平均($) 中央値($) 最小値($) 最大値($)
Beauty $20 $15 $0 $2,000
Electronics $35 $15 $0 $1,909
Handmade $18 $12 $0 $1,750
Home $25 $18 $0 $848
Kids $21 $14 $0 $809
Men $35 $21 $0 $1,309
Nan $25 $16 $0 $1,000
Other $21 $14 $0 $1,400
Sports & Outdoors $26 $16 $0 $915
Vintage & Collectibles $27 $16 $0 $1,709
Women $29 $19 $0 $2,009


上記を見る限り、カテゴリ平均と最大値は比例していないようです。
もしかすると、各カテゴリ内の、特定ブランド(ハイブランド)が異常値として最大値を引き上げているのでしょうか。


もしその場合、ブランド名が価格予想において重要な要素となりますが、
ブランド名の列は、全体の42.7%がブランク(NaN)となります。
つまりブランド名をいかに補完するか、がキモなのかもしれません。


そこで、もう少し、ブランド名について深堀してみます。
train dataにおいてブランド名の数は5,290個、ただし1度しか現れないブランド名は、1,280個となります。
また、”brand”の欄がブランクでも、タイトルや説明文にブランド名が含まれているケースがあります。
ただし、注意点として、ブランド名には「All」や「Complete」などの一般用語も含まれます。

そこで、まずやってみた前処理は、ざっくり下記となります。
・train dataにおいて、商品価格を対数をとったものに変換
・ブランド名をタイトル、説明文から補完。
 ※ただし、”All”や”Complete”などの一般用語ブランド名は、
  補完対象から除外。(あらかじめblack_listを作成)
・説明文の長さ、単語数を変数に
 ※説明がしっかりしているものは、同じ商品でも高い落札価格になっているかもしれない。
・説明文を形態素解析し、各単語出現頻度を変数化

冒頭に述べた通り、上記は、データサイエンティスト間で差がでない、基礎的な処理となります。
※ただし、処理時間の問題から、あえて行わないことは考えられる。


このあたりで、一度モデリングしてみることにします。



| (3) モデリング

まずは手始めに、決定木系のいくつかの手法を適用してみます。
Xgboost、lightGBM、XgboostとlightGBMのアンサンブル、の3つを試してみましょう。
それぞれの手法(決定木系アルゴリズムの進化)の超ざっくりした概要は下記となります。


各手法の精度は下記の通りとなりました。

手法 rmsle
Xgboost 0.46545
lightGBM 0.47876
XgboostとlightGBMのアンサンブル 0.46059


アンサンブル手法によって、0.46059の精度を得ることができました。
しかし、投稿時点で順位は、上位54%となりまして、まだまだ上がいる状況となります。


そこで、さらに独自の変数を検討することにしました。
例えば、説明文にリンクを含むか、とか、
説明文に含まれる!マークの数、とか、
移動中の電車の中など、とにかく暇さえあれば、候補となり得る変数をいくつも考える時間を過ごしました。


しかし、ここで大きな問題に直面します。
立ちふさがる処理時間制限60分以内の壁、です。


つまり、前処理以上に、全体の処理時間の管理がキモとなるのです。
絶対必要なタスクの処理時間などから、モデリングにどの程度の時間を用いてよいのか整理してみます。

タスク カーネル上での処理時間(秒)
パッケージインポート 1.7
データインポート 11.5
機械学習するための最低限の前処理 500.0
自由に使える時間 3062.7
複数処理のアンサンブルから提出ファイル作成 24.1


そこで、大きく方針変更することにしました。
・前処理は最低限に。変数作成は抑える。
・頻度の低い変数は削除。ブランド名なども、頻度が低いものは削除し、できるだけ処理を早くする。
・XgboostやlightGBMをやめて、
 前処理がなくても、ある程度の精度までいけるdeep learning中心に実装(個人的な肌感)


上記をもって、とりいそぎ、ざっくりdeep learningを用いてみたときの、
精度および処理時間は下記となりました。

精度(rmsle):0.46000
処理残り時間:692.9秒

タスク カーネル上での処理時間(秒)
パッケージインポート 1.7
データインポート 11.5
機械学習するための最低限の前処理 500.0
deep learning 2369.8
複数処理のアンサンブルから提出ファイル作成 24.1


先ほどの決定木系アルゴリズムよりも、精度がよくなりました。


さて、まだ処理残り時間が692秒、余っておりますので、
他の手法とアンサンブルできそうです。


ここで、deep learningに、どのような手法を組み合わせるか、ですが、
他の手法と組み合わせるのではなく、
“性格の異なる3つのdeep learningを組み合わせる”
ことにしました。
名付けて、“3AI寄れば文殊の知恵作戦”です。
※結果的に、deep learningと他の手法のアンサンブルよりも精度がよかったためです。

3つの異なる性格のdeep learning、とは、どういうことでしょうか。
deep learningにおけるチューニングは、中間レイヤーを何層設定するか、および、
各層で、ノードをどれくらいに絞り込むか、がキモとなります。


今回、処理時間の問題で、レイヤーは3層にし、
中間レイヤーのノードを大きく変えた、3つのdeep learningモデルを実装し、アンサンブルすることにしました。
つまり、下記の3つのdeep learningとなります。
・「直感タイプ」:中間レイヤーで一気に絞り込む
・「バランスタイプ」:中間レイヤーである程度絞り込む
・「熟考タイプ」:中間レイヤーではあまり絞り込まない


上記によって、処理時間は限界ギリギリまで使用した上で、
精度(rmsle)は、0.42350、上位10%に入りました。


さて、ここまででコンペ締め切りの1週間前、という状態でした。
ここからの方針として、下記2つの方針が考えられます。

1) モデルの微調整を続け、少しでも上位を狙う。
2) 全く別の手法に取り組み、一発逆転を狙う。


今回は、せっかくですので、新しい手法にチャレンジしてみました。
2017年2月に論文が発表されました、「Deep Forest」となります。


これは、決定木系の(おそらく)最新手法でして、
「Deep Learningを超えた」という触れ込みで、
当時、そのネーミングも含めて、データサイエンティスト系の方々のTwitterでバズっておりました。


この手法は、「完全ランダムフォレスト」と「ランダムフォレスト」を組み合わせて、
その出力結果と入力値をDeep Learningのように、多層に重ねていくものとなります。


ちなみに、ランダムフォレストは、各決定木の分岐に使われる項目をgini係数などで決定するのに対し、
完全ランダムフォレストは完全にランダムに分岐に使われる項目を選択するものとなります。


2017年6月に、南京大学よりオフィシャルPythonコードが公開されております。
、、ですが、、周りのデータサイエンティストや、ウェブ上のコメントを見るに、下記のような声が聞かれました。
※英語のコメントを、超訳
「Deep Forestのコード、メモリエラーが頻発するんだけど俺だけ?」
「Deep Forestを実行した日から、俺のPCは何も返答しなくなった」


ご多分にもれず、私のPCもメモリエラーとなってしまいましたので、
オフィシャルコードを参考に、簡易高速版を自作いたしました。


結果は、
精度(rmsle)は、0.46374、となり、残念ながら、Deep Learningを3つアンサンブルしたものを超えることはできませんでした。
※ちなみに、Deep LearningとDeep Forestをアンサンブルするより、
 Deep Learningを3つアンサンブルしたもののほうが、精度は高かったです。
※これは、Deep Forestがよくないというよりも、簡易高速版のコードがよくなかった可能性もあります。


ということで、このままステージ2も
「性格の異なる3つのDeep Learningのアンサンブル」にて提出し、
最終的に、精度(rmsle):0.430005、上位10%以内でのフィニッシュとなりました。

さて、今回、あらためて感じたことは、
理論だけではなく実装ノウハウが重要だということです。


今回、60分以内の実行制限があったわけですが、
理論的にデータがある、データがIDでつながっているからできる、などではなく、
現実的な時間内に完結する処理なのかが重要だということです。

現実問題として、多くの課題には時間的な制約がありますし、
研究的な課題に関しても、スピードは重要となります。
青色発光ダイオードの中村さんは、実験器具を自作して、
他の人よりも超高速でPDCAまわしたことが世界的な発明につながったと言われています。


また、今回説明をはしょりました、tf-idfなどの自然言語処理は
できて当たり前という状態で、ここではデータサイエンティスト間で差がつきませんでした。
動画、静止画、音楽、テキストなど、非構造化データ処理経験が大事になってくると思いますので、
苦手な動画系の解析についても、鍛錬を積んでいきたいと思います。

その手始めに、まずはスプラトゥーン2をプレイし、そのプレイ動画を解析するところから始めようかな
という心の弱さで、今日もゲームをして1日が過ぎて行きました。
だからといって、この息抜きは私にとって不可欠であり、
nintendo switchもスプラトゥーン2もmercariに出品する日は(少なくとも次回作が出るまで)来ないでしょう。