天色グラフィティ

機械学習やプログラミングでいろいろ作って遊ぶブログ

Kaggle Eloコンペの振り返り・上位解法まとめ

f:id:ejinote:20190302203900p:plain

KaggleのElo Merchant Category Recommendationコンペに参加しました。

僕は@kasuminkoさん、@hirokasさんとチームを組んで、ラスト2週間だけ参加しました。結果から書くと、Public 221位からのPrivate 2220位という乱高下で儚く散りました。

手元に銀メダル相当のスコアを持っていたにもかかわらず、間違ったサブミットを選んでしまっており、とても悔しいです。

  • コンペの概要
  • 上位解法
    • 1st place solution (30CrMnSiA)
      • 特徴量
      • モデル
    • 5th place solution (Evgeny Patekha)
      • 特徴量
      • モデル
    • 7th place solution (senkin)
      • 特徴量
      • モデル
      • その他
    • 11th place solution (Zakaria EL Mesaoudi)
      • 特徴量
      • モデル
      • その他
    • 16th place solution (nlgn)
      • 特徴量
      • モデル
    • 18th place solution (pocket)
      • 特徴量
      • モデル
    • 上位解法のうち、今後も使えそうなアイディアのメモ
  • 僕たちの解法

コンペの概要

Eloというブラジルのカード会社が主催となり、各ユーザーの購買行動に対応したRoyalty Scoreという値を予測するコンペです。

データとしては各カードの属性を書いたtrain.csv/test.csvと、それぞれのカードに紐付いた購買履歴(transaction)を表すhistorical_transactions.csv/new_merchant_transactions.csvの4ファイルが与えられていました。

タスクの種類は値を当てる回帰で、targetの分布は0を中心とした分布に加え、-33付近に外れ値が存在するというものでした。

f:id:ejinote:20190302203644p:plain

評価指標はRMSEで、外れ値の影響を受けやすい指標です。特に今回は-33.22付近とかなり外れた位置にサンプルが存在するため、外れ値をどう捌くかが大きな論点のひとつでした。

続きを読む

Quoraコンペの振り返りと上位解法まとめ

f:id:ejinote:20190215163908p:plain

KaggleのQuora Insincere Questions Classificationコンペに参加しました。

f:id:ejinote:20190215162126p:plain

結果は121位で、銀メダルでした。これで銀メダルが3枚目です。わーい。

Public Leaderboardで692位と振るわず、コンペのdeadlineが修論発表の当日だったので直前ほとんど何も出来ず、「Quora何もわからなかった」という絶望感でいっぱいでした。

蓋を開けてみたら多くのTeamが消え、あれよあれよという間に銀メダルになりました。

  • タスクの概要
  • 上位陣の手法
    • 1st place solution (Psi)
      • モデル
      • Embedding
      • 閾値
      • バリデーション
    • 2nd place solution (takapt)
    • 3rd place solution (Guanshuo Xu)
    • 7th place solution (yufuin)
    • 10th place solution (tks)
    • 13th place solution (Canming Wang)
    • 15th place solution (Bai)
  • 覚えておきたいテクニック
  • 【おまけ】151th place solution (僕)
  • 終わりに

タスクの概要

QuoraというQ&Aサイト(日本で言うYahoo!知恵袋)に投稿された質問の中で、有害なもの(攻撃的、差別的、卑猥など)を分類するコンペです。

タスクの種類は0 or 1の2値分類で、正例がかなり少ない不均衡なデータセットになっています。

f:id:ejinote:20190215172356p:plain

評価指標はF1 scoreです。確率として出力した予測値をどこから1として、どこから0とするかの閾値決めが大きく最終スコアに影響を与えました

コンペの形式としてはKernel経由の提出のみを受け入れるKernel Onlyコンペかつ、締切後にテストデータを差し替えて順位を決定する2-stage制コンペでした。 1st stageではテストデータが56,000件程度であるのに対し、2nd stageでは376,000件と6倍程度に増えることが予告されていました。

制限時間内に実行出来なかった場合は順位がつかないという厳しいペナルティがあるため、素早く前処理をしたり、上手くモデルを訓練する必要のあるコンペだったと言えるでしょう。

続きを読む

決定木は本当に変換に依存しないのか?

f:id:ejinote:20190110011802j:plain

決定木をベースとしたモデル(RandomForestやXGBoost、LightGBMなど)は正規化などの前処理が必要ないと言われています。

理由として挙げられるのは「決定木は特徴量の大小関係のみに着目しており、値自体には意味がないから」というものです。

先日もkaggler-jaというSlackチャンネルでこの話が出ました(細かく読まなくて大丈夫です)。

 

自分で質問に答えておいて、「本当か?」という疑問が湧いてきたので実験してみることにしました。

結論から言うと、StandardScalerやMinMaxScalerなどの各特徴量について線形な変換に対しては結果は変化しないけど、logをかけたりするとちょっと変わるということが分かりました。

決定木のアルゴリズム(ざっくり)

決定木を作成する際のおおまかな流れは以下のとおりです。

  1. 目的変数(target)をうまく分離できる特徴量と、分割境界を決める
  2. サンプルを基準よりも小さい群と大きい群に分ける
  3. それぞれの群を繰り返し分割していく
続きを読む

2018年、たいへんお世話になりました。

f:id:ejinote:20181231235259j:plain

晦日なので実家に帰っています。あまり酒を嗜んだりはしない両親とシャンパンを飲み、話しているうちにこんな時間になってしまいました。

帰省する前は「親と話すことなんてそんなに多くはないよなぁ」と思っていましたが、24歳になって昔(中高時代など)を振り返っているうちに「あの頃自分はこう思っていたんだ。親のことをこう見ていたんだ」と伝えたくなりました。

父方、母方の祖母がともに今年亡くなったこともあり、残った人生を謳歌しようとしている両親を見ていると考えさせられるものがあります。

さて。本年を振り返ってみますと、良くも悪くも技術漬けの毎日であったように思います。

プログラミング言語としてはPythonC++Golang、Rustあたりをちょいちょいと触っていましたし、要素技術としては機械学習、特にKaggleに多く触れた一年でした(研究をしろ)。

今年のKaggle戦績は銀2枚銅1枚で、それなりにコミットしたコンペでそれなりの結果を残したということに終止するかと思います。来年は金ピカのメダルとお金を狙っていきたいですね。DeNAは強い人ばかりなので、入社前にMasterになりたいという欲求が強いです。

f:id:ejinote:20181231234914p:plain

競技プログラミングは結局水色でフィニッシュしてしまいました。特に終盤でRatedのコンテストに全然出られなかったのが足を引っ張ったかと思います。来年は予定の調整を頑張っていきたいです。

f:id:ejinote:20181231235009p:plain

プログラミングまわりで少し頑張ったのは、9/21から毎日草を生やしたということでしょうか。もちろん途中にはすごくしょうもないコミットが挟まっていて、「手段の目的化」という言葉が頭をよぎらないわけではないですが、どんな日でも毎日PCを開いてコードを書くという習慣付けが行えたことは価値があることだと思っています。

f:id:ejinote:20181231234929p:plain

ブログは自分としてはがんばって書きました。このブログは今年の4月に開設しましたが、現在読者数が47人になり、ちらほらと読んでいただけているようです。切りの良い数字を目指したいので、年末やることがなくてこの記事を読んでいる方は読者登録ボタンをぽちりと押して頂けると幸いです。来年は月1回以上書くという超低い目標を設定してやっていきたいと思います。金メダル取りました!って記事が書けるといいですね。

年末っぽいコンテンツとしては、Amazonの購入履歴などを見ながら今年買って(契約して)よかったものを挙げておきましょう。順番は思いついた順です。

  • Google Home
    • 声で指示するのが予想以上にストレスフリーだった。主に使用している機能はNature Remoと組み合わせた家電の制御と、目覚まし。あとは天気予報。今はAmazonで売ってないっぽいです。
  • Netflix
    • 月額1000円以下で無限にコンテンツがあってしまうので学生にはおすすめできない。
  • アイリスオーヤマのふとん乾燥機
    • 睡眠時のQOLが爆上がりする。知らない間に汗を吸って重たくなっていた布団が軽くなる。最高。
  • ラムダッシュの5枚刃のやつ
    • 今まで3枚刃を使っていたけど完全に時間の無駄だった。仕上がりのクオリティとかかる時間が格段に違う。クリスマスプレゼントでもらった。
  • Dashlane
    • パスワードを覚えたり、手入力したりという手間から開放された。1年契約した。
  • Happy Hacking KeyBoard BT
    • タイプ感がすごく良い。MBPと組み合わせた際の優雅さを犠牲に満足感を得る感じ。彼女にもプレゼントした。
  • ダンボールをお手軽にゴミに出すやつ
    • Amazonを多用することもあって家にダンボールがあふれかえる。紐で縛るのはめちゃめちゃめんどくさいけど、これを使えばちょっとめんどくさいくらいで済む。300円くらいなのでめっちゃおすすめ。
  • Kyash
    • VISAカードとして使えるウォレットアプリ。手軽に送金が行えてユーザー体験が良いです。ユーザーが増えてくれると嬉しいです。

来年の目標の話をしましょう。

すでに上のほうでだいぶ書いてしまっていますが、まずは金メダルがほしいですね。修論をサクッと片付けてコミットしていきたいです。長期的な視野で行くと、キャリアについての考え方を固めていきたいです。自分の好みはユーザーの顔が想像できる所で施策を考えたり分析をしたりするところにあるのですが、それがどういう仕事になり、どういう場所で生きていくのかについて考えていきたいです。

4月から生活が大きく変わるので、早いうちに順応したいものです。いろいろな兼ね合いもあって修論時期と引っ越し先を探す時期と諸々がかぶってなかなか大変になりそうです。

いつもの通り、Amazon欲しいものリストがこちらです。新生活を控えた3月ごろになると生々しいリストになっているかと思いますが、今はかわいいもんです。

ついでにKyashのバーコードがこちらです。

手軽に投銭できるので、ぜひ僕で試してみてください。

それではみなさま、2018年は大変お世話になりました。2019年もどうぞよろしくお願いいたします。

間に合った!

コピペで使える。Kaggleでの実験を効率化する小技まとめ

f:id:ejinote:20181220184224j:plain

この記事はKaggle Advent Calendar 201820日目の記事です。当初の予定ではPLAsTiCCコンペの振り返りをするはずだったのですが、時空の狭間に吸い込まれた結果0サブミットでフィニッシュしてしまいました。何ででしょうね。 そこで、代わりにKaggleで使える便利なスニペットまとめを書くことにします。

ちなみにもうひとネタあったのでいつか書きたいですが、修論があるのでいったん見送り……

  • LINEに通知を送る
  • 処理にかかる時間を計測する
  • LightGBMの学習結果をログに出す
  • Google Spreadsheetに結果を記録する
    • Google Spreadsheet側の設定
    • Pythonからアクセスする
  • Notebook上でライブラリを毎回再読込する
  • DataFrameのメモリを節約する
  • まとめ

LINEに通知を送る

f:id:ejinote:20181220181419p:plain
Home Creditコンペで実際に送っていたログ

「思考は止めてもサーバーは止めるな」とはkaggler-jaの管理者であるtkmさんの名言です。サーバーを止めないためには正常・異常問わず計算終了時に通知を送ることが重要です。

import requests

def send_line_notification(message):
    line_token = 'YOUR_LINE_TOKEN'  # 終わったら無効化する
    endpoint = 'https://notify-api.line.me/api/notify'
    message = "\n{}".format(message)
    payload = {'message': message}
    headers = {'Authorization': 'Bearer {}'.format(line_token)}
    requests.post(endpoint, data=payload, headers=headers)
  1. https://notify-bot.line.me/my/ からパーソナルアクセストークンを発行する
  2. 発行したアクセストークンをYOUR_ACCESS_TOKENに入れる

これでLINE通知を送ることができます。アクセストークンはコンペが終わったら無効化すれば、GitHubにコードをそのままpushしても大丈夫です。

副作用として、計算が終了したことがわかってしまうというものがあります。

続きを読む

2018年11月を振り返ってみる

先月に引き続き11月の振り返りをやっていきます。

やったこと

毎日GitHubに草を生やした

先月に引き続き毎日草を生やせました。

授業のTAの一環で簡単なコンペを開催するツールを作った(公開準備中)

KaggleのInClassコンペのように、限られた人だけが参加するデータ分析コンペを簡単に作れるソフトウェアを書きました。

取り回しに若干バギーなところ(サブミットがAPIを直に叩かないとできないとか)が残っているので、それを直して公開に持っていきたいです。

ロジックはFlaskで書いていて、フロントエンドはBulmaを使っています。

HHKBデビューした

HHKB BTのUS配列無刻印バージョンを買いました。超かっこいいです。

研究室で使っているキーボードはRealforceで、あのぬるぬるした打鍵感も嫌いじゃないですが、HHKBのシュコシュコ感はなかなか癖になります。けっこうおすすめです。

JIS→USの移行は1週間ぐらい大変な苦痛を伴いましたが、今では慣れました。無刻印はそんなに違和感なく使うことができています。 そもそもキーボード見ながらタイピングしているわけじゃないし、他の人にキーボード貸すわけでもないので、違和感なく使えるのは当たり前といえば当たり前です。

キーボード買ったことによる副次的な効果として、他人のキーボードを観察するようになりました。HHKBユーザーを見つけると勝手に親近感を感じています。

(ちなみに彼女へのクリスマスプレゼントもこれになりました)

統計検定

終わった

AppBrewでバイト

コスメの口コミアプリLIPSを開発している株式会社AppBrewで週2〜3くらいのペースで働いています。 データ分析周りのこまごました処理を主に行っています。

AppBrewはデータの民主化をがんばっていて、エンジニアじゃない人もSQLが叩けるのが好印象です。

面白かった記事

買った本

今月はあまり本を買っていないことにいま気がついた。

イノベーション・オブ・ライフ ハーバード・ビジネススクールを巣立つ君たちへ

イノベーション・オブ・ライフ ハーバード・ビジネススクールを巣立つ君たちへ

ちはやふる(40) (BE LOVE KC)

ちはやふる(40) (BE LOVE KC)

12月に向けて

修論をそろそろ書いていかないとまずいような気がしています。

LightGBMのcallbackを利用して学習履歴をロガー経由で出力する

KaggleなどでLightGBMを使っていて学習履歴を見たとき、ログファイルにも残してほしいと思うことがあります。

公式にはそのような機能は実装されていないようなので、LightGBMのコールバックで対応したいと思います。 LightGBMではfitメソッドの引数としてコールバック関数を渡すことができ、内部的にはEarlyStoppingや学習履歴を標準出力に吐くのに使われています。

LightGBMのコールバックを実装

lightgbm/callback.pyを見ると、学習時に標準出力に履歴を表示する関数があります。

def print_evaluation(period=1, show_stdv=True):
    """Create a callback that prints the evaluation results.
    Parameters
    ----------
    period : int, optional (default=1)
        The period to print the evaluation results.
    show_stdv : bool, optional (default=True)
        Whether to show stdv (if provided).
    Returns
    -------
    callback : function
        The callback that prints the evaluation results every ``period`` iteration(s).
    """
    def _callback(env):
        if period > 0 and env.evaluation_result_list and (env.iteration + 1) % period == 0:
            result = '\t'.join([_format_eval_result(x, show_stdv) for x in env.evaluation_result_list])
            print('[%d]\t%s' % (env.iteration + 1, result))
    _callback.order = 10
    return _callback

これをベースにしてカスタマイズしてあげればよさそうです。

実装例は以下のとおりです。基本的にはprint_evaluationと一緒で、logging.Loggerとログレベルを新たに受け取れるようにしてあります。

import logging

from lightgbm.callback import _format_eval_result


def log_evaluation(logger, period=1, show_stdv=True, level=logging.DEBUG):
    def _callback(env):
        if period > 0 and env.evaluation_result_list and (env.iteration + 1) % period == 0:
            result = '\t'.join([_format_eval_result(x, show_stdv) for x in env.evaluation_result_list])
            logger.log(level, '[{}]\t{}'.format(env.iteration+1, result))
    _callback.order = 10
    return _callback

使い方

ロガーを予め作成しておいて log_evaluation の引数に渡してあげれば動くはずです。

# ロガーの作成
logger = logging.getLogger('main')
logger.setLevel(logging.DEBUG)
sc = logging.StreamHandler()
logger.addHandler(sc)
fh = logging.FileHandler('hoge.log')
logger.addHandler(fh)

# データのロードなどは省略

# 訓練時にコールバックのリストを渡す
clf = lgb.LGBMClassifier()
callbacks = [log_evaluation(logger, period=10)]
clf.fit(X_train, y_train, eval_set=[(X_val, y_val)], callbacks=callbacks)

LightGBMのコールバックの実装方法

コールバック関数のリストをfit()時のcallbacks引数に渡してあげるとコールバックが呼ばれます。

コールバック関数は引数をひとつ取る必要があります。その引数には以下のように学習状況などが入ったnamedtupleが渡されます。

namedtuple(
    "LightGBMCallbackEnv",
    ["model",
     "params",
     "iteration",
     "begin_iteration",
     "end_iteration",
     "evaluation_result_list"])

例えば env.iteration のようにすれば現在のイテレーション数が取れます。(0始まりなので+1するのが良いと思います)

コールバック関数にパラメータを設定したい場合は今回のようにクロージャ(関数を返す関数)にしてあげると良いです。

まとめ

  • LightGBMのコールバックを作成すると学習履歴をロガーに渡すことができる
  • lightgbm/callback.pyはシンプルなので読んでみるとよい

お役立ちリンク