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あって、データ作りに力を入れているなという印象を持った
    • アノテーション用のアプリケーションも作り込んでいそう(?)
    • アルゴリズムはシンプルだけど、最終的にはやはりデータセットがものを言う

2018年の目標1: 自分の砂場を作る

2018年の個人目標の一つ。もう一月が終わろうとしている...。

やりたいこと/目的意識

ミドルウェア/インフラで見れる範囲を少しずつ増やしていく/理解を深めていく。仕事で実際使っているっていうのもあるし、インフラ方面への理解を深めたいって気持ちが強い。苦手意識を少しでも減らしていきたいし、この辺含めてもう少しチームの戦力になりたい(今はあまりにも無力...)。

お題

題材としてはgo-active-learning。今はcliアプリになっていて、機械学習っぽいエントリを推薦してmarkdownに吐き出すという手抜き構成。自分が継続的に使っていたり、愛着がないとなかなかやる気にならない性格だけど、去年それに該当するようなものを書いていた自分good job。データ収集のクローラや格納先のElasticsearchなども去年から稼動している。

これをあれこれミドルウェアを使いつつ、ひとまずwebアプリ化。できてきたらmaster/slave構成にしたり、webサーバー/アプリサーバー/dbサーバーの同居をやめるとか色々やってみる。自分の用途的にはそうなっている必要は全くないが、ミドルウェア/インフラ回りで自分で構築してぶっ壊せる砂場を作りたい。同僚が似たような活動をやっていて、それの真似とも言える。

詳細

具体的にはこの辺が遊び要素かなって思ってる。大して難しいことはやるわけではない。

  • キャッシュにredis
    • 今はバイナリのファイルをローカルに持ってる。書き込みとか毎回やってると大変なので、適当にバッファリングしながらやってる
  • 学習データの管理
    • ぽすぐれ。テーブル構造はシンプル
    • やればいいだけ
    • master/slave構成やレプリケーション回りって実際どんな感じになっているかを知る
  • nginx
    • 自分のお手頃webアプリを作ると、ローカルで済ませるかherokuくらいしかやってなかった
    • バックエンド複数立てて割り振るとか、設定周りとかログ周りを一通りやってみれるようにする
    • 複数台構成。リクエストの割り振りとかあれこれ実験してみる

あとはこれらのprovisioningもできるようにする。前期に少しansible入門していたので、これを使う。cloudformationとかも使って楽をしたい。複数台立てたり、Amazon Elasticsearch Searviceへの通信とかもあるので、ネットワーク周りを含めて考えたい。

Clojureの思い出

ふと思い立ったので、記憶から忘却される前に書いてみようと思います。もう2年くらい書いてないけど、4年ほど仕事で使っていました。その期間はコード書くときはほとんどClojureだったので、まあまあヘビーに使っていたと思います。

前提

メリット/デメリットはコンテキストによるだろうと思うので、どんな感じで使ってたかを書いておきます。

  • 機械学習/自然言語処理の研究開発用途
    • 最近だとPython一択になりつつありますが、数年前はまだそうでもなかった
  • プロダクションに使われるわけではなく、論文を書くための実験や同僚に見てもらうためのデモを時々作ったりする用途
  • 最終成果物はコードではなく論文で評価されるので、メンテナンス性よりも素早くアイディアを実現させること優先
  • チームではなく基本的に一人でコードを書く文化だったので、自分のパフォーマンスがmaxになる言語だったら何でもいい
    • チームのメンバー数 = チームで使われる言語数という感じで、C++、 Python、Java、Matlab、Perl、Ruby、Allegro Common Lispなど統一感は全くない

Clojureを使うようになったきっかけ

  • 大学院時代、機械学習/自然言語処理の研究をやるのにC++で書いていた
    • ベイズを中心にやっていてMCMCなどでパフォーマンスが必要なことが多かった
    • 人類(少なくとも私)にはC++は早かったのじゃ...という気持ちになった
  • 他の言語に乗り換えたい
  • PythonやScalaが選択肢にあったが、普通の言語っぽいし(??)、もっと刺激が欲しいという気持ちが勝って、ClojureやHaskellが選択肢にあった
    • 修論前に現実逃避できるおもちゃが欲しかったのかもしれない...
    • とはいえ、既存の構文解析器もシームレスに使えないと研究に支障が出るということでClojureを選択した
    • 関数型、マクロなど自分がこれまで経験したことがないパラダイムだったので、面白そうだったり、実際面白くて役に立った
    • Haskellの本も読んだけど、Clojureを書くうえでかなり参考になりました

ここがよかった、Clojure

Clojureに限らない話が多いですが、書いていて気持ちよかった点をいくつか。

Immutableな世界

Clojureをやる前はC++をやっていて、思わぬ副作用で苦しんだ時もありました。Clojureはimmutableなデータ構造が前提なので、コードを書くときに考えればよいスコープがすごく狭く済むことが多く、脳内のメモリも少なく済み、テストも書きやすかったです。

リスト系関数の充実

Lisp系の言語なので、リストに対して何らかの処理を数珠つなぎのようにやることが多かったです。その際にthreadingマクロが役に立ちました。

mapの中にあるような関数が充実していて、coreにある関数で大体どうにかなる安心感はよかったです。Linux的(パイプをつないで小さい関数で加工していく)というか、小さい関数をthreadingでつないでいけばいいんだ、というのは他の言語を書くときにも役に立っているなーと思います。小さくimmutable前提の関数をたくさん書いていくので、テストもしやすい作りに自然となっていきました。

困ったときにはJavaに逃げられる

Clojureはパッケージが結構充実してるので、特にデモ環境とか試してもらうためのcli/apiを作るための道具は探すと結構あってそこまで困りませんでした(compojureとか使っていた)。機械学習の最適化などでマニアックなことをしようとすると流石にClojureだけでは厳しくなりますが、JavaのライブラリもClojureの中でそこまで違和感なく使えます。つまりmavenにあればなんとか出来る安心感。英文からの特徴量抽出にStanford ParserBerkeley Parserを使いましたが、両方ともJavaで書かれていてかなり恩恵を受けました。liblinearなどの大抵の分類器もJavaにならあるので、自分に必要なところだけ書けばいい。

また、パフォーマンスで困ったらJavaの配列に逃げらるので、これまた助かりました。JavaっぽいところはClojure likeに使えるようなマクロを用意してラップして使ってました。

マクロ

数年前はまだ人間が特徴量抽出を行なっていた時代だったので(冗談です)、構文木から様々な特徴量を人間がテンプレートとして書いていました。特徴量抽出は似たようなコードが並びがちですが、そういったときにマクロを使って似てるけど微妙に違う関数を自動で大量に生成する、といった用途で使っていました。係り受け解析器での例をいくつか挙げておきます。

乱用は厳禁ですが、便利だし、コード生成は脳汁が出ますね...。チーム開発で乱用されたら殴りたくなるとは思う。

Deployが容易

手元のMacで書いて、計算サーバーで実行というときに環境の差異があるとイライラしますね。今ならDockerやpyenvなどあるので事情が大分違いそうですが、当時はこの辺りをほとんど知りませんでした。Clojureなら成果物をjarファイルにまとめてサーバーにscpすれば動いてくれるので、deploy回りで困ることが少なかったのはよかったです。

並列処理が容易

機械学習をやる上で大量のデータを早く処理したいことは多々あったので、Clojureが並列処理が得意というのには助けられました。(map hoge list)のように書いていた箇所を(pmap hoge list)に変えればいいだけということも多く、とにかくお手軽でした。研究所時代は24コア/メモリが1TBのマシンも普通に使えたので、とにかく富豪的に解決すればいいやろという環境であったことも書き添えておきます。

ClojureScript面白い

研究やっているとjsを書く機会はほぼ皆無ですが、デモ環境を作っているときにjsっぽいことが書けるとUIがちょっとリッチになります。とはいえ、デモ環境のためだけにjsの学習コストを払いたくない(それをやるなら論文もう一本書きたい)...というときにClojureScriptを見かけました。いわゆるAltJsでフロントエンドまで含めてClojureで完結するのは結構面白かったです。サーバーサイドの関数を使い回せたりするのは用途よってはよいかもしれません。

ここが辛いよ、Clojure

書いていた当時に辛かったこと、今チーム開発でClojureを書くとしたら辛いだろうなと思うことが混在しています。

コード上で型が分からない

最近はScalaやGolangを書くことが多いですが、変数や引数に型があることでレビュー/コードリーディングでとても助かっています。Typed Clojureはありましたが、ディファクトになりきれていないように見えました。最近だとcoreに入っているようなので、事情が違うのかもしれません。

とはいえ、型の記述が必須だと当時はプロトタイプ書くのに邪魔だと思って選ばなかったかなと思うので、難しいところですね。

パフォーマンスチューニング

パフォーマンスでつらくなったらJavaに逃げられるとは言え、なるべくJavaは書かずにClojureで済ませたいものです。ボトルネックの箇所のみJavaで書くことにしたいですが、Clojureでのパフォーマンスチューニングは結構難しかったです。プロファイラをかけて、どの関数が重くなっているか目星を付けたいときにコンパイル元のどのClojureの関数に対応しているかがよく分からないことがあり難儀した記憶があります。マイナーな言語なので、パフォーマンスチューニングの事例が調べてもあまり出てこなかったというのもあるかもしれません。

ちょっとしたcliアプリが書きにくい

起動してしばらく使うようなアプリはよいのですが、jqのようにちょっとしたcliアプリを書きたくなることはよくありますが、Clojureは起動に時間がかかるためこういった用途では積極的には採用しにくかったです。この用途は今だったら完全にGolangで書いてしまうなぁ。

再帰をよく使う

慣れれば特に難しくはないけれど、慣れるまではちょっと困る人も多そう(再帰で書ききれないので、transientで逃げるとか)。チーム開発をやる場合には他の言語と比べると導入が大変だったり、慣れている人が少ないといったことがあるかもしれません。自分の場合はThe Little Schemerを読んで練習したりしていました。

プログラミングClojure 第2版

プログラミングClojure 第2版

mackerel-agentでChromeのタブが吹き飛ぶ悲劇を防ごう

この記事は、Mackerel Advent Calendar 2017の6日目の記事です。昨日はid:myfinderさんによるMackerel思い出話でした。今日は小ネタです!!!

ブラウザのタブを閉じない人間の習慣

全国のブラウザのタブを閉じない皆さん、こんにちは。私もブラウザ(Chrome)のタブを閉じれない人間です。以下のようにコンテキスト毎にウィンドウを新しく作って調べものをするので、タブの数も軽く50個を越えることがよくあります。

  • コードを書くための調べものウィンドウAでタブを開きまくる
  • 調査タスクを頼まれたので、ウィンドウBでタブを開きまくる
  • 少し休憩ではてブを見るために(??)ウィンドウCでタブを開きまくる

悲劇は突然やってきます。Chromeの調子悪いなと思ったら固まり強制終了、フォームに長い文章などを書いていた日にはもう今日の営業は終了してビールでも飲みたい気分になります。お疲れ様でした。

mackerel-agentで悲劇を防ごう

悲劇を防ぐには今いくつタブを開いているか数えればよいですが、こういうことは裏側で勝手にやっておいて欲しいものです。そんなことをお手軽にやってくれるプラグインがmackerelにはあります。

起動しているプロセスの数が閾値を上回ったら通知を飛ばすことができます。補足しておくと、通常の用途としてはデーモンのようなプロセスが起動しているかを監視したい場合によく使われます。以下の設定の例では、開いているタブが50個を上回ったらwarning、70個を上回ったらcriticalのアラートが飛びます(slack等への通知は別途行ないましょう)。

[plugin.checks.chrome]
command = "/path/to/check-procs -p Chrome -w 50 -c 70"

余談ですが、Chromeはタブ毎にプロセスになっているので、check-procsでの監視が可能です。他のブラウザでもいい感じにできる方法があったら教えてください。

以下は「タブを開きすぎだぞ」という通知がslackにきたので、しぶしぶタブを閉じていく様子です。

f:id:syou6162:20171127194140p:plain

mackerel-agentのactionオプションを使うと、一定個数以上タブを開いていたら強制的にChromeのプロセスを殺すといったこともできますが、それはそれで悲劇が起きそうなので今回はやめておきます。

明日のアドベントカレンダーはid:astjさんです!

マージン最大化近傍法の読書メモ

社内で異常検知本の輪講を進めています。今回は近傍法による異常検知。一年くらいに一人で読んだときのメモはこちら。慣れない人は途中で出てくる劣モジュラの概念や勾配の導出が大変かなと思ったので、メモ書きしておきます。

劣勾配/劣勾配法

目的関数が連続で微分可能な関数の場合、SGDなどによって最適化が可能な場合がある。しかし、微分可能でない点を含む場合(機械学習の文脈ではヒンジ損失やL1正則化項など、様々なところで目にする)、単純な勾配法は使えない。しかし、勾配の概念を拡張した劣勾配法で最適化を行なえる場合がある。

連続で凸な関数fがあったときに、任意の\mathbf{x} \in \mathbf{R}^Mに対して

f(\mathbf{x}) \geq f(\mathbf{a}) + \mathbf{g}(\mathbf{x} - \mathbf{a})

を満たすような\mathbf{g} \in \mathbf{R}^M\mathbf{x} = \mathbf{a}におけるfの劣勾配という。勾配は一つの値であるが、劣勾配は条件を満たすものが複数存在することがあるので、値ではなく集合として与えられる。1次元で書くと分かりやすいが、右辺が傾きgで、点(a, f(a))を通る直線であり、左辺はそれより等しいか上にいるという条件を表わしている(fが微分可能で等号が成立している場合、いわゆる接線の方程式ってやつになってますね)。

図は劣勾配と劣微分の定義と意味 - 具体例で学ぶ数学より引用。f(x)よりも下にくるような直線は何本でも引けるため、劣勾配は集合というわけである。この劣勾配を使ってSGDのように最適化を行なうことを劣勾配法と呼ぶ。劣勾配を使って勾配法を行なった場合にも勾配法と似たような収束の議論ができる(参考リンク)。

目的関数の勾配

発表資料をWebに上げてくださっている方がすでにいらっしゃるので、そこを参考にしながら輪講を進めている。

42ページから勾配の導出の説明がされているが、本に書いてあるように行列のTraceの微分を使うともっとすっきりと書けるので、その方法についてメモしておく。\frac{\partial \text{Tr}(AB)}{\partial A} = B^Tというよく知られた関係を使う。これ自体は丁寧に書きくだすと簡単に導出できます。z = x_i - x_jとおいて、z^T M z = \text{Tr}(M z z^T)が分かると勾配の式が大体出てくる。一般の式で示すのは若干面倒だけど、2x2くらいのサイズの行列で示してみると雰囲気が分かる。手書きが汚ないけど、こんな感じじゃ。元論文のAppendixを見るのもいいかもしれない。

異常検知と変化検知 (機械学習プロフェッショナルシリーズ)

異常検知と変化検知 (機械学習プロフェッショナルシリーズ)