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を見るのもいいかもしれない。

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

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

AWS Lambda上で鯖(Mackerel)の曖昧性問題を機械学習で解決しよう

この記事は、はてなエンジニア Advent Calendar 2017の1日目の記事です。

サービスに関連する言及のみを観測したい

こんにちは。Mackerelチームでアプリケーションエンジニアをやっているid:syou6162です。サービスを運営していると、サービスに関するtweetをslackに流して定期的に観測しているといった方は多いと思います。観測するモチベーションは様々ですが

  • サービスの不具合に一早く気が付ける
    • もちろんテストや動作確認はやっているのが前提だと思いますが、それでも気づけないものも出てきます
  • 新しい機能を出した際にユーザーの反応が直に見れるため、開発者としてはモチベーションが上がる
    • 問い合わせまではないが、どういう機能要望などがあるか知ることができる

などが挙げられると思います。

問題点

Mackerelチームでもサービスに関するtweetを定期的に観測しています。余談ですが、Mackerelはサーバー(鯖)の監視/管理のサービスで、鯖の英語からMackerelというサービス名になっています。この名前ゆえに「サービスに関連するtweetのみ」を観測するというのが難しくなっています。

  • 魚の鯖に関するtweetが混じる
    • slackのチャンネルが鯖の飯テロ状態になるときがある
    • 最近だとローソンの燻製さばサンドが人気で困ります
      • 当然ながら燻製さばサンドに罪はありません
  • mackerelは多様な連語がある
    • mackerel skyやholy mackerelなど

少し脱線しますが、このエントリの内容を社内の技術勉強会で紹介したところ、イカリングにもいくつかの曖昧性があることを教えてもらいました。こういった曖昧性はサービスに特有の問題ですが、それ以外にも以下のような問題があります。こちらは他のサービス運営者で困っている方も多いのではないでしょうか。

  • ユーザー名にサービス名が入っている方のtweetが混じる
  • meetupなどのイベントが盛り上がりトレンドに入ると、公式のハッシュタグに関係ない内容を混ぜてくるspammerに遭遇する場合がある

以上のようなサービスに関連しないtweetを除外するために、これまではfluentdのfilterに正規表現を頑張って書いていました。しかし、ルールの整備は大変で段々と古びていきます。また、fluentdの障害とサービスの障害の時間が被ると、障害時のユーザーの反応が分からないといった問題点もあります。

解決方法

こういった背景から、サーバーレス(AWS Lambda)で機械学習を使ってMackerelのサービスに関連するtweetのみをSlackに定期的に流すコードを書きました。

sabaとついていますが、検索クエリと教師データ(自分の場合は秋の夜長に600件ほどアノテーションしました)を変えてあげれば他のものでも動くと思います。AWS Lambda上で動作するため、サーバーを用意する必要なく簡単に始められますし、fluentdが万が一障害の場合も問題なく動きます。

工夫点

tweetのjsonから特徴量を作り、平均化パーセプトロンで分類しているだけなので、機械学習的な面白みはほとんどありません。しかし、AWS Lambdaで動作させるための工夫が多少あるので、紹介します。

軽量な形態素解析器を使う

自然言語処理といえば最近ではPython一色になりつつあります。はてなでも機械学習関連のプロジェクトではPythonを使うことが増えてきています。日本語を扱う自然言語処理ではMeCabを扱うことが多いですが、Lambda上でMeCabを動かすのは一手間必要なようです。

サイドプロジェクトでやろうと思ったため、もっと手間をかけずお手軽にやろうということもあり、PythonではなくGo言語を使うことにしました。Go言語での形態素解析器ではkagomeがあり、apexを使うとkagomeも含んだシングルバイナリでAWS Lambdaまで送り込めるので便利です。これで一件落着かと思いきや、AWS Lambdaにはアップロードできるパッケージのサイズ制限(50Mb)があり、kagomeを含めてビルドしたバイナリは56Mbで少し足が出てしましました。どうしたものか困っていたところ、kagomeの作者の@ikawahaさんにIPADicのみのコンパクトなバージョンがあることを教えていただきました(kagomeのAWS Lambdaでの実績解除:tada:)。

こちらを使うとバイナリのサイズが20Mb程度まで小さくなったため、AWS Lambdaへのデプロイも問題なくできました。高精度に動くリッチなモデルに注意が行きがちですが、高速に動く/消費リソースが少ないモデルも実世界では重要ですね。

軽量かつシンプルな分類器を使う

はてな社内でも深層学習の手法であるCNNやLSTMをベースにした分類手法の実験を行なっています。今回の用途の場合、深層学習を用いると学習後のモデルサイズが大きくなりがちという問題点があります(もちろん、パラメータをスパースにしてモデルをコンパクトにするという研究も多数行なわれています)。今回は古典的な教師あり学習の方法の一つである平均化パーセプトロンを用いました。自前でtweetを600件ほどアノテーションを行ない交差検定で実験した結果、十分な精度が出ていると判断したためこれ以上複雑な方法を取るのはやめました(関連エントリ)。

学習した重みベクトルはgo-bindataを使ってGoのコードに変換します。Goのコードに変換できればシングルバイナリにしやすいので、deployが簡単です。deployはapexを使ってapex deployで簡単にdeployできます。

AWS Lambda上での動作の監視を行なう

Mackerelチームに所属しているので完全に宣伝ですが、MackerelではAWSインテグレーションによりLambdaのメトリックを取得したり、それを元に監視/アラートの通知といったことが簡単にできます。是非使ってみてください!

おわりに

SNS上での自社サービスの言及を必要なものだけにフィルタリングして継続的に観察する仕組みについて簡単に紹介しました。細かいところではありますが、ユーザーの声を取り込みつつサービスをよりいいものにしていきたいですね。

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

Multi-task Multi-modal Models for Collective Anomaly Detectionを読んだ

勤労に感謝しながら読みました(論文, スライド)。いい論文をありがとうございます。

概要

基本的にはSparse Gaussian Markov Random Field Mixtures for Anomaly Detection(ICDM 2016)の素直な拡張だと思います。式は少しゴツいですが、拡張を順番に追っていくと大丈夫。拡張のされ方は以下のようになっています。

  • 次元数がある程度高いような場合、どの事例が外れ値か分かるだけでは不十分。どの次元がいつもと異なる動きをしているか教えて欲しい
    • Gaussian Markov Random Field(GMRF)
  • GMRFだけではシステムが状態を一つしか取らないような状況にしか対応できない。昼/夜、停止中/運転中/高速を運転中などいくつかの状態を通常状態として取り得る場合にも対応したい
    • Gaussian Markov Random Field Mixtures
    • ICDM 2016
  • システムの状態がいくつかある状態を仮定しつつ、それぞれの情報源(この論文ではタスクと読んでいます)の特性をモデルに反映させたい
    • ICDM 2017。この論文です
    • 例: 車の状態といっても運転手によってdriving conditionはかなり変わる
    • 例: 学生でも学校によって特性が異なる、その特性をモデルに反映したい
    • 例: 同じはてなのWebサービス(昼と夜でアクセスパターンが異なるなどの特徴がある)といっても、はてなブックマーク、はてなブログ、カクヨムではそれぞれ異なった特性がある。それらを反映したい
      • 自分で勝手に想定した例です

それぞれのタスクの特性をモデルに反映させたい場合、各タスク毎にGaussian Markov Random Field Mixturesのモデルパラメータを推定するというのは一つの手です。しかし、モデル一個当たりに使用できる学習データの量は減ってしまします。学習データの量を増やすために特性を反映させず一個のモデルを作る方法もありますが、全体としてぼやけた推定になってしまいます。この論文ではいわゆるマルチタスク学習を用いて、様々な学習データを使えるうまみを利用しつつ、それぞれのタスクの特徴を反映させる手法を提案しています。

この手法が効きそうなMotivating exampleが挙げられていて、これを見るのが分かりやすかったです。図は論文のFigure 2から引用しました。1次元でタスクが2つあるようなケースです。タスク1、タスク2ともに大きな山が二つあるようなmulti-modalityがある分布になっていますが、それぞれの山の尖り方は異なっています。

f:id:syou6162:20171123013356p:plain

単純なマルチタスク学習(MTL)ではmulti-modalityを反映できず、ぼやけた分布になってしまいます。混合ガウス分布(GMM)では二つの山こそ表現できていますが、それぞれのタスクの山の尖り方は反映できていません。今回の提案手法(MTL-MM)ではmulti-dalityを反映しつつ、それぞれの山の尖り方もかなり反映できています。事例x毎にタスクのラベルsが付いているという状況は割とあるので、現実的な問題設定でもいけそうです。

モデル化: Multi-task sparse GGM mixture

マルチタスク間の共通点や相違点を反映しつつ、どの変数が異常か見て分かりやすく(=結果がスパースになっている)なるようなモデルをベイズ流の生成モデルで記述していきます。モデル化はICDM 2016の論文とそこまで違いはありません。クラスタ毎に考えていた潜在変数がクラスタ/タスク毎に考えるようになったくらいが変更点でしょうか。結果がスパースになるように事前分布にはラプラス分布を組み込んでいきます。

モデルパラメータの推定

式は結構ゴツいですが、ICDM 2016のときと同様に変分ベイズを使い、近似しつつ変分下限を最大化していきます。やり方は大体同じですが、潜在変数z^sに対するパラメータ\pi^sの(スパースになる)解き方が二通り紹介されています。

一つ目は従来からあるような変分ベイズの流儀で進めていく方法。関連度自動決定の枠組みで結果がスパースになってくれるというやつです。しかし、今回の定式化の場合、目的関数に\log \pi^s_kという項が入っており、スパースになる(\pi^s_k = 0)とこの項がマイナス無限大になってしまい都合がよくありません。論文ではヒューリステックを使って解決する方法が述べられています。

二つ目は\pi^sにl0ノルムに相当するような事前分布(これも結果はスパースになる)を考えて、目的関数を書き換えるという方法です。この目的関数はconvex mixed-integer programming (MIP)の形式になっています。基本的には汎用ソルバーに投げて解くのかなと思っていたのですが、論文では頑張ってアルゴリズムを導出していました(ごつかったので詳細は追ってない...)。ここまで頑張ってconvex mixed-integer programmingを解く必要はあるのか(関連度自動決定+ヒューリステックでもよさそう?)と思って論文を読み進めていたところ、6.1章の実験で頑張ったほうがよい解が得られたと書いてあったので御利益はあるようでした。

モデルパラメータが推定できれば、あとは簡単です。モデルは基本的にはGaussian Markov Random Field Mixturesなので、ある事例が異常かの判定は負の対数尤度を計算すればよいだけですし、ある事例のある次元が異常かどうかはGaussian Markov Random Field Mixturesならば簡単に計算できます(条件付き確率が簡単な形になるため)。詳しくは異常検知本を読みましょう。

実験

ここではLondon SchoolのデータセットとAnuran Calls(カエルの鳴き声?)のデータセットで提案手法と既存手法の精度比較が行なわれていました。どちらのデータセットでも提案手法が結構なマージンで勝っていましたが、データセットはどちらも元々異常検知でよく使われるデータセットというわけではなく、実験の状況設定がいいものなのかは自分には判断しかねるという感想でした(しかし、マルチタスクの設定になっている異常検知用のデータセットは今のところないと思うので、仕方なさそう)。

2017年の振り返り技術編

ふと思いたったので書く。2016年は自然言語研究者からWeb系エンジニアになったということで、今振り返ってもキャッチアップで精一杯だったなーと思うが、2017年は去年よりは慣れたこともあり、もう少し自分にとって新しいことに取り組めたかなーと思う。といっても、XXXやり始めたという内容のほとんどが仕事で必要だったという理由なので、Mackerelチームで働くための基礎体力が本当になかったんだなと改めて痛感している(なぜはてなに入社できたのか謎)。飽きる暇もなく勉強の毎日です。来年はもう少し狭く深く掘り進めていきたいかな。

Go言語やり始めた

仕事でGo言語をやる必要があって勉強し始めたのが今年の初めだった。A Tour of Goを最初にやったけど、実際に手を動かしてレビューで見てもらったほうが早いなと思って、MackerelのグラフアノテーションをmkrでできるようにPRを出したのが初めだったと思う。

書き始めた最初はsortやるにもあれこれやんないといけないし、かっこよく書けないなーと思っていた。しかし、慣れてくるととにかく素朴に書くのがいい文化であることが分かってきて、慣れるとそれはそれでよいもんだということが分かってきた。最近では一番好きになった言語かもしれない。mattn/memoのソースコードを読んで小さいPRを出したり、中規模なものではSlackで能動学習をできるようなアプリケーションを書いたり、係り受け解析器、エントリ推薦を書いたりした。このくらいの規模をいくつか書くと大分慣れることができた。go routineはまだ使いこなせているとは言えない。

Goはcliアプリをさっと書けて、シングルバイナリで簡単に使ってもらえるのが最高だということが分かって以降、Mackerelのプラグインを作りまくったりしていた。Goの練習に丁度いいと思うので、どんどん書いて欲しい。

勢い余って他の人のプラグインのmkr plugin install対応活動もやっていた(しゅっとmerge & releaseしてもらってありがとうございました)。

Pythonやり始めた

こちらも今さら感がある。去年は仕事の機械学習のコードはPerlで書いていたけれど、今年はPythonで書く仕事がきた。Python自体は遠い昔書いていた時代があったので、大きな戸惑いは特になかった。scikit-learnやjupyterがあって、便利な世の中になっていた(激しく今さら感)。githubやgithub enterprizeがipynb対応していて、プロトタイプや調査結果の共有に便利。はてなのインターンの機械学習講義も今年からついにPythonにしました、講師を担当していた。

一方で機械学習以外ではGoがよかったこともあって、趣味ではあまりPythonのコードを書く機会がなかった。色んな環境でさっと使ってもらえることを考えるとpyenvでpython3.6をインストールしてもらって、pipでライブラリをインストールしてもらって...というを考えると面倒で、この辺はGoがいいなと思う。

異常検知やり始めた

書籍での勉強は昨年末に始めたけど、今年は手を動かして社内の多次元の時系列データでの異常検知のプロトタイプやproductionのコードを書いたりしていた。異常検知、教師なしがdefaultなので難しいだろうなと思っていたけど、予想通り簡単ではなかった。現場的には誤検知をどうやって抑えればいいか、どういうデータにはどういう手法ならばいいかのノウハウを少しずつ貯めていってという感じだった。中間アウトプットとしてMackerelのチェック監視で異常検知を行なうものを出したりしていた(これはさすがにGoじゃなくてPython)。

論文もちまちまと読んでいた。異常検知回りは問題設定でまだまだやられていないことも多いので、論文読んでて楽しいですね。

一般の機械学習や深層学習はそこかしこで勉強会やコミュニティが存在するけど、異常検知peopleはどこに行くと会えるんでしょうか。

深層学習やり始めた

仕事で現状必要になっているわけではないが、これからどう向きあっていくかを含めて色々試している。深層学習の本は山ほど出ているので、それで勉強しつつkerasで手を動かして感覚を掴んだりしていた。LSTMやCNNを使って社内の既存タスクを解き直してみたが、多少精度は上がるものの劇的というほどではなかった。これまで社内で取り扱っていなかった生成系のタスクや画像系の問題について面白い(かつお金にもなるような)問題を探っていく試行錯誤がもう少し必要そう。画像系は教師データと大量の計算機リソースが必要だと思っていたが、転移学習をベースにすれば全く手が出ないなんてことはなかった。とはいえ、深層学習に関係なく、社内のコーパスを含めたリソースは今後も定期的に整備していきたい。

AWSやり始めた

これも仕事で必要だった。MackerelのAWS/AzureのIntegrationをやっているにも関わらずcloudほとんど自分では使ったことがなく、仕事でもLambdaやDynamodb、EC2を使っていなかった。これはどう考えてもまずい、と思って趣味で動かしていたElasticsearchをAWS Elasticsearch Serviceに引っ越ししたり、Mac Miniで動かしていたクローラーをEC2に引っ越ししたりすることから始めた(これはraspberry piを買ったので、今はやめた)。Jenkinsで動かしていたいくつかのJobをAWS Lambdaで動かすようにした。

仕事でやるような規模のものは趣味の範囲では無理なので、この辺で得た知見をベースに仕事でも生かしていきたいなという感じ。

Docker&Ansible始めた

これも仕事で。最初はすでに存在するdocker imageをdocker runしかやっていなかったけど、Dockerfile書いたり、composeが何者か分かってきた。簡単なやつではあるけど、自分でよく使いそうなものはDocker Hubに置いたりしていた。

Ansibleは自分のクローラーのコードや環境設定をEC2でバチっとやりたかったので、少し勉強した。sshしてあれこれやらなくていいのは便利ですね。EC2だと環境作っては壊してというのがお手軽にできる。mackerel agentのcheck監視の設定で細かいところに手が届かなかったのでPull Request送ったりしていた。

IDEに魂を売った

これは仕事じゃなくて趣味。去年までは頑張ってScalaをEmacs(ensime)で書いていたんだけど、いよいよ辛くなってきた。これはEmacsに固執している場合じゃないと思って、IntelliJ IDEAを使い始めた。変数の型が分かったり、関数の定義元にさくさく飛べるので、大分効率が上がった。メモリがっつり持っていかれるのが痛いけど、これは諦めた。IntelliJ IDEAが便利だったので、Python(PyCharm)やGo(Goland)を書くのもIDEを使うようになってしまった。

Emacsで書くものがmarkdownだけになってしまったので、気分転換がてらvimに乗り換えをした。今のところそんなに困っていない。左手が楽になった。なお、SKKはまだ捨てれていない。多分捨てられない。

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

AWS Batchのメトリックを収集するMackerelプラグインを作りました

AWS Batchを仕事で使うことになりそうなので、Mackerelのプラグインを作りました。Queueの状態をカスタムメトリックとして収集します。mkr plugin install mackerelio/mackerel-plugin-aws-batchでインストールしてくれ!

AWS Batchの困りどころ

全然使い込んでいるわけではないので、他にもありそうですが。AWS Batchの裏側で起動するEC2インスタンスのCPUとMemoryの組み合わせでいいものがないと、BatchがfailするわけではなくRUNNABLEという状態に行って滞留するようです。

知らないうちにQueueが溢れていると困るから監視したいなーと思ったので、プラグインを作りました。Mackerelのプラグインではないですが、同じようなことをしている先人がいらっしゃいました。

cloudwatchから直接メトリクスを取れるわけではないので、同じようにstatus毎に集計をしています。

特定のDockerコンテナが起動しているか確認するチェックプラグインを作りました

Dockerコンテナをデーモンとして起動(docker run -d ...)させていたけど、いつの間にか死んでいたのを後から知った...ということがあって悲しかったので、特定のDockerコンテナが起動しているかを確認してくれるMackerelのチェックプラグインを作りました。mkr plugin install syou6162/check-docker-psでインストールしてくれ!

check-procsプラグインのdocker版のイメージで作りましたが、コンテナ名はexact matchのみ、個数はまだ指定できないといった感じです。使ってくれそうな人がいたら対応していこうかなという気持ち。

Mackerel サーバ監視[実践]入門

Mackerel サーバ監視[実践]入門

  • 作者: 井上大輔,粕谷大輔,杉山広通,田中慎司,坪内佑樹,松木雅幸
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/08/26
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る