NLP(言語処理学会)2018で面白かった論文メモ

あれこれ雑にメモっていて、きちんとしてから外に出そうと思っていましたが、下書きのままお蔵入りしそうだった。出せるところで出す...!すでに開催から一ヶ月も経ってしまった。

A1-1: ニューラルヘッドライン生成における誤生成問題の改善

  • ○清野舜 (東北大), 高瀬翔, 鈴木潤 (NTT), 岡崎直観 (東工大), 乾健太郎 (東北大/理研AIP), 永田昌明 (NTT)
  • 論文PDF: http://anlp.jp/proceedings/annual_meeting/2018/pdf_dir/A1-1.pdf
  • ニューラルヘッドライン生成はAttention付きのEncoder-Decoderモデルが使われることが多いが、問題点もある
    • 繰り返し同じ単語を生成する、無関係な単語を生成してしまう、固有表現など重要な単語が欠損するなど
  • それぞれ個別の問題に対応するような既存研究はあるが、本研究ではこれらを統一的に扱える枠組みを提案
  • 出力側の単語に対応する入力側の単語を予測する
    • 正解のアライメント(入力単語と出力単語の対応)は必要ない
  • 全体最適化は文単位でのロスを最適化
    • 通常は単語単位でのクロスエントロピーを最適化していく
    • 文単位のロス: 入力系列と出力系列のMSEの和
  • はてなのトピック機能でもヘッドライン生成に近いことを行なっているので、興味深く発表を聞かせてもらいました

B2-4: ニューラルネットを用いた多方言の翻訳と類型分析

  • ○阿部香央莉, 松林優一郎 (東北大), 岡崎直観 (東工大), 乾健太郎 (東北大/理研AIP)
  • 論文PDF: http://anlp.jp/proceedings/annual_meeting/2018/pdf_dir/B2-4.pdf
  • 対話アプリケーションの盛り上がりとともに、方言で入力されるケースも増えてきているが、方言で入力された場合には解析精度がどうしても落ちてしまう
  • 方言から共通語への翻訳ができればよいが、いくつか問題点がある
    • 方言から共通語への十分な対訳コーパスを用意するコストが高い
    • 小規模なコーパスではSMTのほうが精度がよいと言われているが、比較検証が行なわれていない
  • 方言毎にモデルを作るとデータ数が足りないため、複数方言のコーパスをまとめて学習する
  • 国語研が発行している全国の方言の対話の書き起しデータベースを使用
  • 翻訳している地域を区別するために、先頭のトークンに地域トークンを付加する
    • 地域トークンに対応する地域ベクトルができる
  • 方言から共通語への翻訳の性能向上だけでなく、地域ベクトルの可視化(図4)が方言研究としても面白かったです
    • 都市部から同心円状に広がっている?

D4-3: サンプリング戦略に基づく単語ベクトルの意味成分とスタイル成分の分離

  • ○赤間怜奈 (東北大), 横井祥 (東北大/理研AIP), 渡邉研斗 (東北大), 小林颯介 (PFN), 田然 (東北大), 乾健太郎 (東北大/理研AIP)
  • 論文PDF: http://anlp.jp/proceedings/annual_meeting/2018/pdf_dir/D4-3.pdf
  • 対話などの文生成タスクでは、何を(意味内容)どう表現するか(スタイル)が重要である
  • スタイルは多様な特徴があり、教師あり学習では網羅することが難しい
  • 分布仮説と発話内はスタイルが一貫しているという仮説を一つのモデルで表現することで、単語埋め込みベクトルを意味成分とスタイル成分に明示的に分けて学習する
  • 意味とスタイルで近傍の定義を距離を変える
    • ウィンドウ幅が一定の距離以内では、意味ベクトルとスタイルベクトルを更新
    • 一定距離以上ではスタイルベクトルを更新
  • 仮定はとてもシンプルですが、教師なしでうまく意味成分とスタイル成分を分けられているように見えました
  • 質疑では「意味ベクトルとスタイルベクトルの相互情報量が0になるような制約を目的関数に入れるとよいのでは?」という指摘も面白いなと感じました

社内でKaggleの布教活動をやっている話

最近、社内勉強会で機械学習についてエンジニアに説明する機会があり、その際にKaggleについても説明しました。一方で

という話もあり、(特にデータサイエンティスト以外の職種の人が)Kaggleをやる意義/メリットについてまとめてみました。ガッと勢いで書いたので、項目に結構被りがあります。なお、書いている本人はKaggleほぼ初心者であまり説得力がないです。Kaggle Masterの人がもっといいエントリを書いてくれるのを期待しています、議論の叩き台エントリです!!

Kaggleをやる意義/メリット

様々なデータセットを触ることができる

Kaggleには開催中/終了したものを含め様々なコンペがあり、データセットも多種多様です。データサイズがかなり大きいものもあります。

「新しい機械学習機能ができるか社内で検討したいが、手元には手頃なデータがまだない(あっても少量)」といったときに、Kaggleのデータセットを使ってあれこれ試してみることができます。例えば、はてなであれば画像関連のタスクはまだ経験がほとんどなくアノテーションデータも特に存在しませんが、画像を使うとどういう機能ができそうか/どのくらいの精度でできるのか、といったことを試せるといった具合です。どのくらいできるかも分からない状態でいきなり仕事でやるのはなかなかリスクが高いので、Kaggleで練習できることは仕事でのリスクを下げることにも役に立つと思います。最近見かけた参考になりそうなタスク、面白そうなタスクを少し紹介してみます。

  • Toxic Comment Classification Challenge | Kaggle
    • 嫌がらせのようなコメントを識別する典型的なNLPの練習になりそうなタスク
    • CGMを運用しているサービスではどこでも悩むことだと思うので、Kaggleの人がどういったアプローチで取り組んでいるのか参考になることが多そう
    • 単純に2値分類ではなく、toxic, severe_toxic, obscene, threat, insult, identity_hateといった複数のラベルを予測する問題になっていて、タスク設計という観点でも参考になる
  • Mercari Price Suggestion Challenge | Kaggle
    • メルカリ主催の価格推定タスク。商品名、カテゴリ、descriptionなどが与えられた状態で価格を推定する典型的な回帰のタスク
    • 特徴としては学習と予測もサーバー側でやる制約。サーバーのスペックや計算時間に制約があるというのは製品開発ではよくある話なので、そういった制約がある状況でどういうアプローチを取っているかは参考になりそうですね
    • 一位の人のコード: pjankiewicz/mercari-solution: Mercari 1st place solution (kaggle)
  • Text Normalization Challenge - English Language | Kaggle
    • 口語表現などを含むテキストが与えられたときに正規化するNLP系のタスク
    • CGMのテキストを入力として何かをやろうとするとき、各社正規化を頑張ることが多いと思う。その必要が大きい場合、こういったコンペを覗いてみると発見があるかも
  • Planet: Understanding the Amazon from Space | Kaggle
    • 航空写真が与えられ、どの地域が違法に密林を伐採されている区域かなどを当てるタスク
    • 素人目から見ると違いが全然分からんな...という気持ちになるし、画像タスクでどう取り組んでいけばよいかを考えるよい機会になりそう
    • データも圧縮済みで数十GBあるので、(半強制的に)データハンドリングをどうするかなどの勉強にもなる
    • Kaggle まとめ: Planet, Understanding the Amazon from Space - Qiita

自分もCTR予測のタスクの練習がしたかったので、Kaggleで練習した話を先日書きました。

こういった練習を繰り返すことで

  • どういった特徴量をDBやログに吐いておくと機械学習的観点でよいか
  • 有効な特徴量を継続的に保存していくためのデータ基盤はどうなっているとよいか

といったことも少し検討が付いてきたというのはよい発見でした。

kernelでデータ分析のやり方を知れる

Kaggleにはkernelというページがあり、他の人がどういうモデルや特徴量を作ったか、その特徴量を作るに至るにはどういった分析を行なったかを見れることができます。こういった試行錯誤は探索的データ分析(EDA: Exploratory data analysis)と呼ばれ、参加者は大体やっているのではないかと思います。

機械学習をproductionで使う際には「この特徴量や前処理はこういった側面から必要で入れている」といった説明をチームメイトに行なう必要がある場面が度々あると思います*1。こういった際にどういう分析や可視化を行なうと効果的なのか、事例をたくさん見ることができるというのは大きなメリットであると言えると思います。

場数を踏める

データ分析/機械学習案件を受けるとしましょう。案件に対して正確に早く動けるかどうかは機械学習に関する教科書的/理論的な知識も重要ですが、やはり踏んだ場数は成功に欠かせない要素だと思います。場数を踏まないとなかなか分からない具体例としては、例えば時系列データの学習データ(train)と検証用データ(dev)の分割の話があると思います。経験が少ないと意図せずleakしてしまい、テストデータでうまくいかないといったことがよく起きます。自分もついこないだやってしまいました...。

社内でたくさん機械学習プロジェクトが走っているならばそういった経験が積めるのでよいですが、そうでない場合にはなかなか経験を積むことが難しいです。Kaggleをやると、ある程度リアルな(多少ノイズを含むデータだったり、巨大なデータだったり)データをいじることになるので、場数を踏むことができます。仕事でぽんぽん失敗するのはなかなか辛いですが、Kaggleだと失敗してもあまり痛手ではありません。そのため、どんどん場数を踏むことができ、結果として仕事の案件でのスピードアップや正確性の向上に寄与することができるのではないかと思います。

様々な評価指標について知ることができる

一つの分野のタスクばかりやっていると、評価指標も同じものを使ってしまいがちです。Kaggleのコンペでは評価指標も様々なものがあります。これまで自分が知らなかった評価指標を使ってみると

  • どのタスクにそもそもどういった指標が使われうるのか
  • どういうときにどういう指標が向いているのか

といった知見が溜まってきます。その結果、数ある評価指標の中から適切な指標を選べるようになり、仕事にも役に立ってくるのではないかと思います。不適切な指標に対してチューニングを頑張っても、productionに出したときに泣くことになります...。

実験のログや再現性について気を使えるようになる

Kaggleのコンペは2-3ヶ月の長期戦です。あるとき自分がsubmitした2週間前の結果がよかったので、手元で再現したいということはよくあります。実験設定について何も記録していないと、よかった結果がどういう条件で出てきたのかがあっという間に分からなくなります。Kaggleをやっていると、どういう設定で実験を行なったのか(ハイパラ/データ数やデータ分割方法/特徴量の種類など)ログを取る重要性を身を持って学習できます。また、実験設定だけあっても結果が再現できないと意味がないので、実験の再現性についても気を使えるようになります。

実験設定の記録や再現性は機械学習のサービス開発でも難しい部分であるというのは以前エントリに書きましたが、こういった点について自然と対応できるようになってくるというのはKaggleのよいところであると思います。

自分の実力が客観的に分かる

Kaggleのコンペはよくも悪くも客観的な数値で自分の成績が分かります。社内で一人で機械学習を進めている状況だと「自分のやっているアプローチは正しいのだろうか。この方法で出ている性能は客観的に見ると高いんだろうか/低いんだろうか」と悩むことも少なくないと思います。Kaggleでは、自分の提出したもののスコアがよければ自信になりますし、思ったよりも悪ければKernelやディスカッションを見てどういう改善ができそうか探ることができます。

楽しい

精度が上がった下がったで一喜一憂するのは、まあ結構楽しいです。ISUCONなどで地道にスコアを改善するコンペが好きな人は趣味としても楽しめるし、仕事に役に立つ要素もあるので一石二鳥では?!と思います。

賞金がもらえる

まだ賞金がもらえるようなランクに行ったことがないので、省略します!!!もらったことがある人が書いてくれ!!!

まとめ

Kaggleの上位に入るためには、最近だとアンサンブルやハイパラチューニングが必須になってきています。しかし、そういったインクリメンタルな改善の要素以外にも、これまで書いたようにKaggleには自分の実力を正しく計測したり、力を向上させられる要素がたくさんあります。データサイエンティストに限らず、色んな人がKaggleに挑戦してもらえると自分もうれしいです。

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

*1:不必要な特徴量はメモリや計算時間も無駄になるだけですし、その特徴量を取るために複雑性も増してしまいます。なるべく取り除きたい

KaggleのCTR予測コンペで上位10%に入るまでの試行錯誤

週末KagglerとしてavazuのCTR予測コンペに参加しました。Kaggleは機械学習版のISUCONだと思ってもらえばよいです。コンペ自体は終わっているので、late submiteであまり意味はないかもしれません、練習です。leaderboard上で上位10%以内に行けたので、そこまでの試行錯誤をメモしておきます。謎ノウハウ(?)を持っているガチ勢じゃないと上位に行けないものかと思っていましたが、基本に忠実にやればこれくらいの順位(上位7.6%)に行けましたし、他の人の工夫を垣間見えるという意味でも現場の機械学習やり始めたエンジニアにお薦めできそうでした。

参加の動機

そもそもなぜ今のタイミングでkaggleに取り組むのか、参加への動機を書いておきます。

  • 手掛けたことのあるタスクの幅を広げたい
    • nlpだと土地勘があって、うまくいかなくても何となく対処法が分かることが多いけど*1、そうでないドメインでもある程度できるように
    • 今回はCTR予測だけど、他のコンペもぼちぼちやっていきたいですね
  • 大きめのデータセットでの学習に慣れる
    • nlpだと教師データが数万文でやることが多いし*2、社内タスクでも人手でアノテーションするとそのくらいのオーダーのデータしか扱わないことが多い
    • 数千万規模のデータで試行錯誤(特徴量チューニング/ハイパラチューニング)の肌感覚を掴む
  • 使ったことのない手法やツールを試してみる
    • 仕事だと期日もあるし、使い慣れてる手法やツールを選びがち
  • 対外的な自分の実力を客観的に知る
    • 社内だと同じタスクをやる人が多くて数人、一人ということもありがちなので、自分の作った分類器がどれくらいいいのか客観的に知る機会が実はあまりない
    • 同じタスクに1000人規模でチャレンジして、その過程や結果がディスカッションやコードの形として残っているので、それらを自分の血肉としたい
    • もちろん、プロダクションで入れるときは目視して十分な精度あるかは確認しますが…

目標感: 頑張りすぎずに上位10%以内に入る

コンペなので順位が出ますが、あまり凝った方法を使わないで上位10%以内に入ることを目標としました。理由としては以下の通りです。

  • 週末くらいしかまとまった時間をそもそも取りにくい
  • リッチな計算機環境はない
    • 手元のmacbookのみ(メモリは使えて10GB程度)で戦うので、あまり凝ったチューニングはできない
    • 一時的にEC2を借りる手もあるけど、それは開催中のコンペに参加する時でいいかな…
  • 上位勢はそもそもプロダクションでは使えなさそうな(kaggleでしか使えなさそうな)特徴量を使っているので、あまり深追いしすぎても仕事に生かせる部分は減っていく
    • 例: ある日の午前3時のCTR予測をしたいのに、なぜかその日全体の閲覧数/クリック数が特徴量に入ってる(3時台のおすすめアイテムを計算したい時には使えない特徴量)
  • 終わってるコンペなので、賞金は手に入らない

残りは取り組んだ方法について時系列で書いていきます。以下、スコアはLeaderboardのPrivate Scoreのことを意味しています。

試行錯誤

AthenaとRedashによる探索的データ解析

やみくもに機械学習やっていくのは筋悪だなと思い、まず探索的データ解析(EDA: Exploratory Data Analysis)をすることにしました。コンペによってはカーネルでEDAした結果を置いてくれている人もいるので、それを参考にするのもよいと思いますが、練習なので今回は自分でやります。時系列要素があるので、なるべくシャッフルやサンプリングしないで解析したいなと思うものの、学習データが4000万件以上あるので手元で解析するのはなかなか大変です。そこで、今回はAthenaとRedashを使ってデータ解析をすることにしました。

学習用データを解凍してcsvにして、S3に置きます。圧縮済みのものを置いてもAthenaは動きますが、SELECTが返ってくるのは解凍済みのほうが早かったので、Kaggle用途には直接csvを置いておけばよさそうです(圧縮済み1GB、解凍後5GBくらいなので、スキャン量は多少多くなる)。スキーマはこんな感じで適当に作ります。

Athena用のテーブルのスキーマ定義

DROP TABLE ctr_practice.ctr_practice;
CREATE EXTERNAL TABLE ctr_practice.ctr_practice (
  `id` string,
  `click` int,
  `hour` string,
  `C1` string,
  `banner_pos` int,
  `site_id` string,
  `site_domain` string,
  `site_category` string,
  `app_id` string,
  `app_domain` string,
  `app_category` string,
  `device_id` string,
  `device_ip` string,
  `device_model` string,
  `device_type` string,
  `device_conn_type` string,
  `C14` string,
  `C15` string,
  `C16` string,
  `C17` string,
  `C18` string,
  `C19` string,
  `C20` string,
  `C21` string
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = ',',
  'field.delim' = ','
) LOCATION 's3://ctr-practice/'
TBLPROPERTIES (
  'has_encrypted_data'='false',
  'skip.header.line.count'='1'
);

こんな感じで日付毎のクリック率の推移を可視化したり、カテゴリ変数毎のクリック数をテーブルに出してデータ分析を進めていきました。

手元で解析するのが億劫になるようなデータサイズでも、Athenaでは簡単なSELECTなら5秒程度、cardinatityの高いGROUP BYなどが入っても3分程度で返ってくるので、かなりお手軽です。5GB程度のデータをかなりフルスキャンしてデータ分析しましたが、200円程度で週末Kagglerのお財布にも優しくて安心。

見所としてはAthenaはウィンドウ関数にも対応している(Presto互換)ので、SQLだけで比較的強力な特徴量が作れるといったことでしょうか。例えば同じdevice_idが前回に登場した時刻を出して、その差分を計算し、それを特徴量として追加するといったことも簡単にできます(手元でpandasでやるのは若干面倒)。ウィンドウ関数は10年戦えるデータ分析入門で勉強しました。普通に実務のデータ分析で便利。

SELECT id,
       device_id,
       hour,
       prev
FROM
  (SELECT id,
          device_id,
          hour,
          lag(hour, 1) over(partition BY device_id
                            ORDER BY hour) AS prev,
   FROM ctr_practice.ctr_practice
   ORDER BY id LIMIT 1000000)
WHERE (NOT prev IS NULL)
  AND (hour != prev);

BigQueryで同じように探索的データ解析をYouTubeで紹介されている方もいらっしゃるので、こちらも参考になりました。


BigQueryでKaggle入門 Part 1(コンペ解説 + EDA紹介)

ベンチマークをまず超える

全部0.5を返すのがベンチマークだったので、学習データでのCTRの平均値をひたすら返す君を最初のsubmitにしました。評価指標はlogloss(クロスエントロピー)で、0/1の結果だけではなく結果の確信度も考慮した指標で評価されます。この時のスコアが0.4405302でした。後段の学習ベースの方法がきちんとうまく行っているかの判断材料になるので、初期段階でやっておきたいですね。

線形分類器でシンプルな特徴量

次に学習データに与えられているカテゴリカルな特徴量をonehot encodingして、線形分類器に食わせるだけのものを作りました。学習にはvwを使いました。

学習が高速で、省メモリでラップトップ参加者にも非常に優しいツールでした。また、コマンドラインから組み合わせ特徴量を作ることができるというのも嬉しいポイントでした。学習データが4000万行あるので、テキストファイルに組み合わせ特徴量を吐いていると30分程度かかってしまい、試行錯誤が非常にやりにくくなってしまいます。コマンドラインから組み合わせ特徴量を作れると、テキストファイルに組み合わせ特徴量を吐き出す手間が省けて助かりました。

awkでvw形式に吐き出します。文字列から特徴量idへの変換もvwが中でやってくれるので本当にお手軽ですね。

% awk -F, 'NR > 1 {print ($2*2)-1, "|a", "a_"$4, "|b", "b_"$5, "|c", "c_"$6, "|d", "d_"$7, "|e", "e_"$8, "|f", "f_"$9, "|g", "g_"$10, "|h", "h_"$11, "|i", "i_"$12, "|j", "j_"$13, "|k", "k_"$14, "|l", "l_"$15, "|m", "m_"$16, "|n", "n_"$17, "|o", "o_"$18, "|p", "p_"$19, "|q", "q_"$20, "|r", "r_"$21, "|s", "s_"$22, "|t", "t_"$23, "|u", "u_"$24, "|v", "v_"substr($3, 7, 2)}'

シンプルな特徴量のみで学習。スコアは0.4153354程度でした。正則化項は結構小さめ(1e-7くらいのオーダー)でないと正則化が強すぎるようで、学習がうまくいきませんでした。

時系列要素を忘れていて過学習発生

vwにハイパラチューニング用のスクリプトが付属しているので、これを使うと便利です。(その1/その2)。これを使ってハイパラチューニングをしていると、手元の開発用データではよかった設定が、submitしてみるとだいぶ悪化しているという現象に遭遇しました。データを眺めてみたところ、時系列的な要素を考慮し忘れているのが原因でした。学習データをシャッフルして8:2に分割して開発用データを作っていましたが、新しい広告やユーザーがどんどん入ってくるので、シャッフルしてしまうと時系列要素が壊れてしまいます。学習データの最終日(10月30日)を開発用データとしたところ、leaderboardの傾向と一致しました。時系列要素のあるチューニングは難しいことが多いので、Kaggleはちょうどよい練習になりますね。

特徴量エンジニアリングに苦しむ

ここからスコアを上げるのに難儀しました。vwでは組み合わせ特徴量にワイルドカードを使うことができます。発火している特徴量の全ての組み合わせを考えてくれるってやつです(例えば-q ::-q a:など。後者はaと他の全ての特徴量の組み合わせを考慮)。何も考えずに実行するもスコアはあまり変わらず…。ディスカッションの様子を見ていると、組み合わせ特徴量が重要であることは疑いようがなかったので、ワイルドカードにしたことにより不必要な特徴量も入ってしまったのでしょう。匿名化されている特徴量もありますが、意味を考えながら有効そうな組み合わせを人手で追加していきました。スコアは0.3945532まで上がりました(とはいえまだまだ750位くらい…)。

かなりスパースなデータなので、登場頻度の少ない特徴量は足切り(頻度10回未満のものはUNKNOWNにする)すれば汎化されて良いのではと思って試しましたが、自分の手元ではあまり効果がありませんでした。なんでだ…。

Factrization Machine(その1)

組み合わせ特徴量といえばFactorization Machine(FM)だよねと思ったので試してみます。vwがFMをサポートしていたので、そのままやってみました(::8のようなワイルドカードを使った)。スコアは0.3936758で650位前後で思ったよりは上がらず。1位の人もFMだし、きっと何か勘違いしているかチューニングの仕方が悪かったんだろう…。libfmなど本家のものを使ってみるといい結果が得られるかもしれません。

追記: やり方を変えたFactrization Machine(その2)ではもう少し精度が上がりました。

重みの更新方法を変える

sparseなデータで登場頻度も結構異なるので、重みの更新方法をsgdのように特徴量全部同じ幅でやるのではなくadagrad的な更新方法にすればよくなるのではと思って試しました。vwでのオプションは--adaptive --invariant--ftrl --ftrl_alpha 0.01 --ftrl_beta 1といったやつです。これはまあまあうまくいってスコアが少し改善(0.3938853)しました。関連論文は恐らくこの辺。

しかし、これでもまだ700位前後。

GBTMで特徴量の組み合わせを考慮

人間による特徴量チューニングに疲れてきたので、決定木ベースの方法で組み合わせを見ることにしました。決定木ベースの方法を使うのは初めてだったので、色々分からないことがだらけでした。

baggingとboostingの違いについても今回初めて知りました。baggingは学習データや使用する特徴量をサンプリングすることで少し異なる識別器を多数学習するような方法です。各学習器は独立に学習することができます(random forestはこっちっぽい)。一方、boostingは最初に学習器を作ったらその学習器の出力と学習データの損失を元に次の学習器を作るような逐次的な学習を行なっていきます(gradient boosting tree modelはこちら)。Gradient Boosting from scratchなどを参考にしました。

使うならrandom forestかxgboostかなと思っていましたが、最近はlightgbmというのが省メモリで高速っぽかったのでこれを試しました。vwは1GBくらいでも動いてくれましたが、lightgbmは省メモリになるような設定をしても8GBくらい持って行くので、ラップトップではこの辺が限界っぽい…。xgboostはOOMでshellに殺されてしまいました。

vwとは違って特徴量idへの変換は自分で行う必要があります。MurmurHash3を使ってfeature hashingすることで対応しました。最初はpython組み込みのhash関数を使っていましたが、セキュリティ要件で実行の度に結果が変わってしまうということに気付かず、精度が全然出なくてしばらく悩んだというのは笑い話です。hashのbinのサイズは大きいほうがcollisionしなくなりますが、大きすぎると学習の際にメモリを食うようになります。vwを使っているときに24程度(次元数が224で大体800万程度)でも十分精度は出ていたので、ここでも同じものを使いました。

お手軽feature hashingスクリプト

import csv
import sys
import mmh3

input_file = sys.argv[1]
hash_bin = 2**24

with open(input_file, "r") as i:
    reader = csv.reader(i, delimiter=",")
    header = next(reader)

    for words in reader:
        result = []
        click = None
        for idx, word in enumerate(words):
            h = header[idx]
            feature = word
            if h == "click":
                click = feature
            else:
                feature = mmh3.hash("%s_%s" % (h, word)) % hash_bin
                result.append(feature)
        result.sort()
        if click is not None:
            print("%s " % click, end="")
        print(" ".join(map(lambda x: "%s:1" % x, result)))

木の数を増やしまくると学習時間がどんどん増えていくので、leafの数を増やす方向でやっていきました。サンプリングレートなど色々チューニング要素がありますが、手元であれこれ試すのが厳しかったので、他はディフォルトパラメータでやりました。スコアは0.3930893で500位程度。チューニングを頑張ればこれはもう少し上がると思います。

% lightgbm data=xgboost_train_shuf.txt objective=binary metric=binary_logloss max_bin=127 num_trees=300 num_leaves=127

余談ですが、決定木ベースの手法はデコード(予測パート)が遅いですね…。全部の木をleafまで舐めないといけないからそりゃそうかって感じですが、こんだけ遅いとnlpのようなデコード速度もそれなりに求められるアプリケーションにはなかなか難しいという発見が今さらながらありました。

カリブレーション

これも初めて知りました。学習器の出力する確率の平均とテストデータの平均が一致するかはやってみないと分かりません。一致して欲しい気はするけど、一致する保証など特にない。平均がズレている場合にそこの補正をしましょうというのがprobability calibrationという方法です。難しそうな響きがするけど、やることは簡単。少しですが、精度が上がりました。テストデータの平均は分からないので、学習データの平均はと一致するようにカリブレーションしました。

カリブレーションするための簡単なPythonスクリプト

import sys
import math

def inv_logit(x):
    return math.log(x / (1.0 - x))

def logit(x):
    return 1.0 / (1.0 + math.exp(- x))

train_avg_ctr = 0.169806
pred_sum_ctr = 0.0
cnt = 0.0

result = []
for line in sys.stdin:
    ctr = float(line)
    result.append(ctr)
    pred_sum_ctr += ctr
    cnt += 1.0

pred_avg_ctr = pred_sum_ctr / cnt

for item in result:
    intercept = inv_logit(pred_avg_ctr) - inv_logit(train_avg_ctr)
    # print("%s\t%s" %(item, logit(inv_logit(item) - intercept)))
    print(logit(inv_logit(item) - intercept))

Factrization Machine(その2)

ワイルドカードで全ての組み合わせを考慮しているからFactrization Machineの精度が思ったほど出ていないのではと思ったので、多少組み合わせ方を考えてみることにしました。データのフィールド名に

  • siteに関するもの
  • appに関するもの
  • deviceに関するもの
  • categoryに関するもの

といった種類が分かれていたので、それら毎に組み合わせてみることにしました。上から仮にa/b/c/dと名前だとすると、ab/bc/cdといった感じでフィールドの組み合わせ方を指定してFactrization Machineの学習を行ないました。スコアは0.3916204で230位まで上がりました。

アンサンブル

性質の異なる3つのモデル(人手による組み合わせ特徴量/LightGBM/Factorization Machine)ができたので、アンサンブルをやってみることにしました。データからそれぞれの学習器の重みを決定してもよかったのですが、今回はお手軽にやるのが目的なので、単純にmodel averagingします。これが意外と効いてスコアは0.3895500(122位で上位7.6%)まで伸ばすことができました。

ちなみにvwでパラメータだけ変えたものをアンサンブルしても、同じような出力を出すのでアンサンブルしても精度向上はほとんどありませんでした。アンサンブルで性能が上がるかどうかは性質の異なる学習器を複数作れるかどうにかかっていそうです。

他のチームで参考になった方法

GBMの特徴量をvwに入れる

各データをGBMの各木に食わせて、それぞれどのleafにたどり着いたかというカテゴリカルな変数を特徴量にしてvwに食わせるという特徴量エンジニアリングです。1位の人もやっているようで、GBDT featureと呼ばれている。

後段でGBMの結果とアンサンブルする方法と精度がどれくらい違うのかは自分でも試してみたいですね。

カテゴリカル変数を実数の特徴量に置き換えていく

こちらを参考に。集計が必要な分ちょっと面倒ですが、one-hotとは違った特徴量になるので、アンサンブルすると効きそうですね。自分の試行錯誤では出てこないアイディアだったので、なるほどという感じでした。id:ultraistさんも書かれていますが、集計期間は少し気を使う必要がありそうですね。

追記: 識者に教えてもらいましたが、この手の特徴量エンジニアリングはCount Encoding/LabelCount Encoding/Target Encodingなどと呼ばれているそうです。以下に詳しく書いてありました。一つコンペに参加すると色々情報が集まってきてお得!

まとめ

  • 手元のMacbookだけ&基本的なテクニックで上位10%以内に入ることができた
    • 今回はログデータでしたが、画像データの場合はさすがに無理かも...
  • 他者の回答や技術を参考にできるので、本に載っていないような生きた技術を学ぶことができる
  • 自分が使ったことがないツールや初めてのタスクを練習するのにはうってつけ
    • 今回はFactorization MachineやGradient Boosting Decision Treeの様子が分かってよかったです

というわけでKaggleオススメです。

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

*1:CoNLL2015のShared Taskだけはチームで一度出たことがあった

*2:機械翻訳は例外的

S3のログをAmazon Athenaを経由してRedashで分析/可視化

DWHやログ基盤を色々試してみたいけど、Amazon Redshiftよりお手軽に始められるツールとしてAmazon Athenaを教えてもらいました。S3にtsv/csv/json...を置いておくと、SQLで分析できるってやつです。BigQueryと同じくクエリ単位で課金だけど、ログはS3に上がっていることが多いので、お手軽に試せそうということで試しました。N番煎じ感しかないやつです。やったこと。

  • S3に分析したい対象のデータを置く
    • 大きいデータなら日毎などに分けておくとあとのパーティションで役に立つ
  • Athenaでtsv/csvのカラムの型を定義しておく
    • インデックスの指定などは特にない
    • パーティション分けてなければ基本的にフルスキャン
  • 数万件くらいの簡単なSQLで手元だと一瞬で返ってくるようなクエリでも3秒くらい時間かかるときもある
    • S3のバケットなめるからそういうものっぽい
  • 参考

可視化

データの傾向を見たいときはSELECTした結果だけじゃなくて、それをグラフにしたいことも多いです。可視化ツールへの繋ぎ込みを調べてみたところ、AWSスタックだとQuickSightというのがいい感じのBIツールのようです。

が、個人で試したいのでもっとお手軽なものはないかなと思って調べたところ、RedashのデータソースでAthenaにも対応しているということが分かりました。docker-composeで手元のMacからAthenaに接続。

% docker-compose -f docker-compose.production.yml run --rm server create_db
% docker-compose -f docker-compose.production.yml up
  • AWSのコンソールからAWSQuicksightAthenaAccessAmazonS3ReadOnlyAccessの権限を持つユーザーを作る
  • 作ったユーザーのaws_access_key_idaws_secret_access_keyをRedashのデータソースの設定に入力

社内だとkibanaで可視化することも多いけど、様々なデータソースをダッシュボードで一元的に可視化したい場合はRedashもなかなかよいですね。

まとめ

Athenaのことを特に意識せずにS3にログを置いておけば分析できて気持ちとしてはとっかかりやすいですね。色々感覚が掴めてきたらRedshiftとかも試してみたい(RedashのデータソースとしてRedshiftも選択できるようだ)。

10年戦えるデータ分析入門 SQLを武器にデータ活用時代を生き抜く (Informatics &IDEA)

10年戦えるデータ分析入門 SQLを武器にデータ活用時代を生き抜く (Informatics &IDEA)

Hacker Tackleで「はてなにおける機械学習の取り組み」について登壇しました

LINE福岡で行なわれたHacker Tackleにて登壇してきました。

発表内容は(1)機械学習を使ったサービス開発の難しい点について整理し(2)その難しさを乗り越えていくためにはてながどのような取り組みを行なっているかについてでした。一口に機械学習を使ったサービス開発といっても、古典的な問題設定でどうやればいいか比較的クリアに見えているものと、R&D要素が強くどう取り組んでよいか分からないものではよい取り組み方も異なってきます。そこで、今回の発表では古典的な問題設定(テキスト分類)であるBrandSafe はてなのリニューアル、R&D要素の強いMackerelの異常検知、それぞれに対し技術的/組織的にどのような取り組みを行なったかについて話させてもらいました。

登壇時間は30分で割と話すことも多かったので、当初話す予定だった箇所は登壇ではばっさりカットしていましたが、このスライドではおまけスライドとして追加しています。

  • 比較的高度なNLPを使ったサービスの事例
  • 能動学習を使った効率的な教師データの作り方
  • 単語埋め込みを使ったタスクに依存しない特徴量抽出器

などの話題について触れています。

登壇の様子です。

こうすればうまく行く!というよりはこの一年間ではてながどのように試行錯誤をしてきたかをまとめた内容です。この資料をきっかけに「うちではこんな取り組みをするとうまくいった!」という事例がどんどん出てくると自分も発表したかいがあったかなと思います。各社の取り組みについてみんなどんどん書いてくれ!

仕事ではじめる機械学習

仕事ではじめる機械学習

異常検知ナイトでLT登壇しました

機械学習の中でもマイナーなテーマであろう異常検知がテーマの勉強会、異常検知ナイトというイベントでLTの登壇をしてきました。マイナーテーマなのに300人以上が集まる東京怖い。

3ページしかないですが、発表資料も置いておきます(LTのレギュレーションで3ページ5分)。

LTのテーマは、現在自分がどんなデータで異常検知をやっているか、どういう困り事があるかを発表してプロの方からアドバイスをもらおうというものです。Mackerelで今まさに異常検知機能の開発をしていて、時系列周りのモデルのハイパーパラメータを開発データでチューニンングしたいけれども、そもそも異常データを含む開発データって手に入らないことが多くてどう対応していくのがよいのか?という質問をさせてもらいました。プロからのアドバイスは動画で見れるので見てくれ!

自分の発表を含む全てのセッションの動画がすでに上がっています。自分の発表は1:47あたりからです。


異常検知ナイト(エンジニア向け)

所感

  • 異常検知でどんなことができるかどんな方法があるのか知りたい人が多いのかなーと漠然と思っていたけど、飛んでる質問がガチだった
  • 自分で手を動かして、それゆえ困ってという人がたくさんいて、話を聞いてて「めっちゃ分かる〜」という気持ちになった
  • どこか一箇所の組織で大人数でやっているというよりは、部署で一人だけやっているという方が多かった
    • 論文とかも結構読まれていて、話していてめっちゃ楽しかった
  • 電力や工場でのセンサー系など、Web系とは全然違う分野の方が多かった
  • 異常検知ナイトのためだけに東京出張にきたけど、余裕で元取れまくる素敵イベントでした。運営の方、ありがとうございました!

おまけ

はてなに転職してから外部発表をしていなかったので、かなり久しぶりの登壇でした。めちゃくちゃ緊張した結果、登壇前に「安静時なのに心拍数めっちゃ上がってるけど大丈夫??」と通知がきたのが個人的ハイライトです。最初の笑いが取れたのでよかった。Apple Watchの異常検知機能(?)なかなかやるのではないか。

f:id:syou6162:20180215232248j:plain

Rettyにおける推薦文からの見出し抽出の論文を読んだ

OR学会の自然言語処理と数理モデルの特集で、はてなと同じくC向けのサービスを運営しているRettyさんの論文が出ていたので、週末に論文を読みました。方法論としてはシンプルだけど、サービスへの貢献もなかなかありそうだなーと思いながら眺めました。

箇条書きスタイルでまとめておきます。

  • 推薦文からレストランの特徴を表わす見出しを自動で抽出したい
    • 全部で80万レストランあり、見出し文の定期的な更新もやりたいので、人手では難しい
  • 優先度学習を使って見出しを抽出する方法を提案
    • 一種のランキング学習のようなもの。特徴量は素朴な設計
    • 生成するわけではなく抽出型。一種の要約タスクと見ることもできる
    • 未知語処理やdata augumentationなどの工夫で汎化性能が高くなるように工夫
      • 訓練データで登場回数の低い単語は形式的な語(unk)に置き換える
  • 推薦文に対して部分文字列を全列挙、one-versus-oneで順序付けを行なった中から見出しらしいと判定された回数が最も多かったものを見出し文として採用する方法
    • 部分文字列について最短/最長の制約などは特に書いてないようだが、本当に全列挙してたらなかなか大変そう
  • 比較的小規模なデータと論文で書かれているが、人手で付与された正解の見出し文は252,498あって、データ作りに力を入れているなという印象を持った
    • アノテーション用のアプリケーションも作り込んでいそう(?)
    • アルゴリズムはシンプルだけど、最終的にはやはりデータセットがものを言う