非線形データに普通のKMeansを使う

1,2,2,2,3,3,4,10,10,11,11,11,12,12,12,12,13,13,25,25,26,26,26,27,28,28,29,29,29,29,30,30,30,31

例えば上記のような1次元のデータがある場合を考える。ある小学校のクラスで行われた簡易テストの点数のようなイメージだ。「1点の生徒が1人、2点の生徒が3人・・・31点を取った生徒が1人」という感じである。

nonlinear2

ヒストグラムは上記のようになる。ここで色分けは意図的に行っている。

  • 赤は落ちこぼれ
  • 緑は普通の生徒
  • 黒はよくできるグループ

のように、いわゆるラベル分けを行うことができる。繰り返しになってしつこいが、これは意図的なラベル分けだ。

ここでは3つに分類すると決めており、データは人間から見ると自然に3つに(ヒストグラム上で)別れている。各グループ間は線形分離が可能だ。

このような場合、KMeansクラスタリングでk=3とすると完璧にクラスタリングしてくれる(ことが多い)。「ラベルが3種類だから、k=3とする」というごくありがちなアプローチである。

a <-c(1,2,2,2,3,3,4,10,10,11,11,11,12,12,12,12,13,13,25,25,26,26,26,27,28,28,29,29,29,29,30,30,30,31) 
> kmeans(a,3)
K-means clustering with 3 clusters of sizes 16, 11, 7

Cluster means:
[,1]
1 28.000000
2 11.545455
3 2.428571

Clustering vector:
[1] 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Within cluster sum of squares by cluster:
[1] 56.000000 10.727273 5.714286
(between_SS / total_SS = 98.1 %)

Available components:

[1] "cluster" "centers" "totss" "withinss" "tot.withinss"
[6] "betweenss" "size" "iter" "ifault"

仮に今回のケースが、ラベル有りで上記データが提供されていて、KMeansでモデルを作り、新たなデータを「落ちこぼれ」「普通」「よくできる」のいずれかに分類したいという状況だとする。この場合、データが上記kmeansの実行で得られたクラスタ中心(28, 11.5, 2.4)のどれに一番近いか?を判断するだけで良い。

しかし、ラベルが3種類ではなく、下記の2種類だったらどうなるだろうか。

nonlinear3

ここでラベルが以下の2つであるとする。

  • 緑は「普通でない生徒」
  • 赤は「普通の生徒」

突如、緑に属するデータは非線形な集まりとなってしまう。先ほどと同じようにKMeansでモデルを作り、新たなデータを分類する問題として考える場合、先ほどと同じアプローチで「ラベルが2種類だからk=2でクラスタリングしよう」と思っても当然うまくいかない。

ここで「くそっ、KMeansは非線形データに弱いんだぜ…」と考えてカーネル関数とかそっちに走るのもひとつの手だが、もっと超簡単な方法がある。というかこのブログエントリを先頭から読めば自然に気づくと思うが、「ラベルが2種類だからk=2」というのを「ラベルは2種類だけどk=3でやってみよう」とするだけである。

そして、実際の分類の際には

  • 1つめのクラスタ中心に近ければ「普通でない生徒」
  • 2つめのクラスタ中心に近ければ「普通の生徒」
  • 3つめのクラスタ中心に近ければ「普通でない生徒」

というロジックに従えばOKである。

クラスタリング結果をそのまま分類に使うのではなく、クラスタリング結果にもうひとつだけ処理を追加するだけでよい。クラスタリングはあくまでも数値処理で、人間がそれをうまく解釈してやるだけの話だ。

あまりにも簡単な話なのだが、意外と目にしない(私の探し方が悪いだけかもしれないが)ので、ブログに書いてみた。


自宅で働くJavaプログラマになって13年経過したのでメモ

当時は「SOHO」というキーワードで知られている、いわゆる在宅勤務。私は2002年頃からずっとやっていて…
まぁ誰かの参考になる可能性もあるかと思ってメモします。

まず、私の在宅勤務がどんなものかを簡単に。

私は2002年頃に「株式会社ビットフォレスト」というスタートアップを立ち上げた、いわゆるファウンダーです。そのため、世の中の人が「在宅勤務」で想像するであろう「雇われている」形態ではありません。ここは非常に大きなポイントになると思います(要するに自分がさぼると、きっちりツケが廻ってくるという意味で)。

場所は横浜市の南の方で、「下永谷」という名前の、駅前に何もない場所でやっています。最近、ついに駅前にコンビニが出来ました。どうでもいい。
在宅勤務を始めるよりも前に結婚しており、一人暮らしではありません。

会社のオフィスは、立ち上げた当時は存在せず、全メンバーが在宅勤務でしたが、数年後に四谷三丁目にオフィスを構えました。現在は在宅勤務しているメンバーは私を含め数名いますが、多くはオフィスで仕事をしています。

仕事の内容はソフトウェア(あるいはウェブサービス)の開発で、2002年頃からずっとJavaを使っています。なので、勤務時間中、殆どの時間はコードを書いていることになります。

なぜ在宅勤務にしたか?

大きく4つ理由があり、

  • オフィスでなくても仕事ができる(オフィスのコストの削減が可能)
  • 満員電車が苦痛
  • 通勤時間がもったいない
  • 集中できる

というものです。特に説明しなくてもよいですね。(1つめについては、その後結局オフィスを構えてしまいましたが・・・)

さて実際にやってみた感じは次の通りです。

むちゃくちゃ集中できる

仕事をしている時間は、同居している家族はそれぞれ仕事や学校で出払っていることが多いため、自宅にはひとりという状態になります。
場所が住宅地で、外も静かなため、数時間続けて集中してコードを書くことができます。

途中誰かから話しかけることがなく、また電話が鳴ることも殆どないため、プログラマにとっては理想的な作業環境です。非常に生産性が高く、また基本的に毎日そのような環境で仕事を継続的に行うことができます。

数年間でのトータルのアウトプット量で考えた場合、かつてサラリーマンとしてオフィスに通勤していた頃よりもはるかによい結果が出せていることは間違いないと思います。この点については今振り返ってみて、本当によい選択(時間を無駄にしなかったという意味で)だった思います。

ストレスが少ない

部屋に連続して数時間以上一人でいるという状態は、非常にストレスが少なく済みます。他の人の音やしゃべり声、ニオイなどがないので、心が乱されることがありません。また、例えばウ○コしたくなったらすぐトイレですればよいだけです。例えば下痢をしている日には、15分おきにトイレに入っても何も問題ありません。オフィスの場合、15分おきにトイレにいくのって結構周りの目が気になってつらいですよね。そういう生理的な欲求などを我慢する必要がない点についても、在宅勤務は優れていると思います。

自己管理が要求される

誰もいないので、誰も注意してくれません。私は太ってはいませんが、例えば異常な量のお菓子をバリバリ食べていても、誰も「食べ過ぎじゃない?」と言ってくれません。そのため食欲をはじめ、色々な面で自らの堕落を自ら節する必要があります。自己管理が苦手な人は在宅勤務は向いていません。

さみしいか?

在宅勤務のデメリットとして「寂しさ」が挙げられるケースがありますが、私は幸い家族がいることもあり、その点を感じたことはありません。在宅勤務の後は呑みに行く仲間はいないので、頻繁に仲間と呑みに行きたい人には辛いかもしれません。

どんな職業に向いているか

プログラマーのように、コンピュータだけあれば完結するような仕事が向いていることは言わずもがなです。他には、その人が雇われているのか、自営なのかが大きなポイントだと思います。自営の場合にはピーク時には頑張って稼ぎ、それを過ぎたら休みを増やしてのんびりすることもやりやすいですし、さぼると自分の収入が少なくなるため自制もやりやすい面があるでしょう。

また、在宅勤務には「面白い」と思える仕事が向いていると思います。

働きすぎる

37signalsの本にありましたが、在宅勤務で注意が必要なのは「さぼること」ではなく「働きすぎること」ですw。

朝食を済ませたらコーヒーを淹れて即仕事が開始できますし、また夜も仕事を終えたら10分で眠れてしまうため、どうしても仕事をしすぎになります。私の場合、コードを書くことは仕事でもあり趣味でもあるため、この点については我ながらダメだなと思います。

以上、誰かの参考になれば。


コモディティサーバ戦略のパラドックス

servers

今さら確認するまでもないが、サービスの成長に合わせてこまめにサーバリソースを追加し、負荷をうまく分散して処理していく戦略がスケールアウトで、図の上がこれを示す。
主に安価なコモディティサーバを使うイメージである。

一方、図の下は、最初から高価で高性能のサーバを買ってしまい、それをぎりぎりまで使い切って、あるタイミングでまた高性能のサーバを追加するイメージである。まぁ、一昔前まで当たり前だと考えられていたアプローチである。

上だと最終的に6台のサーバが、下だと最終的に2台のサーバがある感じである。

図中の青い部分が「買ってしまったけど使っていない」リソースであって、無駄なコストである。

上の方が、青い部分の面積は小さく、無駄なコストを省くことに成功している。スケールアウト戦略を選択する理由にはいくつかあるが、そのうちでポピュラーなものがこの「無駄なコストの削減」である。

しかしこのときサーバ台数は増えてしまっているので、DatadogやMackerelのような「サーバ毎に課金される」サービスを利用する場合には、料金もきれいにスケールしてしまう。

貧者の戦略としてコモディティサーバを選んだのに、価格が高くなってしまうのだ。

でもまぁ、台数が増えたらサーバ管理の手間は増えるから、「サーバ管理を助けるサービス」の使用料が高くなるのは当たり前だよ、って言われると納得するかも…


Deep Learningで人工知能が実現するとかいってる人たち、あるいはそういう見出しの記事を書きたい記者が正座して読むべき記事

Facebookでケンタロさんに教えてもらった記事。
http://spectrum.ieee.org/automaton/robotics/artificial-intelligence/facebook-ai-director-yann-lecun-on-deep-learning

僕も一時期(2014年の12月から2015年の1月)にDeep Learningについてかなり時間をかけて調べてみたけど、結局明らかに達成されていることは「精度の高い教師あり学習ができる」ということであって、人のように抽象化思考を行う何かがそこから発現するとは思えないと考えていた。(フリーソフトではじめる機械学習の、WEKAでのニューラルネットの章で、オートエンコーダーによってコンピュータが自動的に二進数の概念を獲得するところは興奮したけど。)

We had quite a bit of success with this, but in the end, what ended up actually working in practice was good old supervised learning

また、下記の部分などは僕が好きな苫米地氏がずっと前から表明しているのと同一の見解だと思う。

The bottom line is that the brain is much better than our model at doing unsupervised learning. That means that our artificial learning systems are missing some very basic principles of biological learning.

個人的には、人工知能(強いAI)を作るには、「痛み」「快楽」「生」「死」のような生命の根源に直結する概念、欲求を教えないとダメだと思う。痛みや死から逃げるための判定を最優先させる。このときは低い抽象度で。死から遠いところでの活動には、より抽象度の高い学習や判定を行わせていく。


Continuous Deliveryの時代とJava

かつてバージョン管理やCIなどが存在しなかった頃
人々は手作業で(FTPなどを使い)PHPファイルをアップロードすることで、本番環境へデプロイを行っていた

筆者はJavaプログラマだったので
「本番環境でバグが見つかっても、問題のファイルを修正してアップロードすることで、一瞬で修正できる」
というPHPの利点が非常にうらやましかった
Javaだとjarに固めてアプリケーションサーバにFTPで上げて再起動する、みたいな感じで非常に面倒くさかったのだ

しかし時代は経過し、「ユニットテスト等が行われた後でしか本番環境のファイルが変更されるべきではない」という考え方が常識となってきた
こうなると、「手軽にちょちょいと問題を修正する」というPHPの良さは失われることになる

ウェブアプリケーションを構成するファイル群が全体としてバージョン管理され、テストをパスしたファイル群のみが本番環境に到達できるのだ
まぁそれが正解なのだが、ぶっちゃけ、いざというときの俊敏さは失われることになる
PHPを手作業でアップロードしていた時代、ひどい場合には電話で「なんか問題でてるんだけど」という報告を受けてから数十秒で修正が完了していたケースもあるだろう。

そんなわけで、相対的にJavaでもいいかな、という感じになってきた。


Log4j 2にも採用されたLMAX Disruptorはなぜ狂ったように速いのか?

dis

LMAXという会社はおそらくFX業者で、筆者はLMAXの開発者の講演を、InfoQの動画で何度か見たことがあった。
彼らは非常に特異な集団で、さしずめ「Javaのスピード狂」という感じだ。
印象的なのは、シングルスレッドで仕事を片付けることを強調している点だ。
「Javaならマルチスレッドで並列処理すれば性能が出ると広く思われているが、我々の仕事においてはシングルスレッドが最速だ」というような主張を何度も見た。
ゴールドマンサックスといいLMAXといい、やはり多額の金が動く会社でガチでJavaをやっている連中はカリカリにチューニングするため、技術的には非常に面白い。
彼らがコアのライブラリをOSS化してくれるというのは、金融業界を否定的な目で見る筆者からすると複雑だが、悔しいことに参考になる。

LMAX DisruptorはJavaのライブラリだ。Producer/Consumerパターンであるスレッドから別のスレッドへメッセージを渡す際のパフォーマンスを追求したものである。Log4j2のAsync実装に採用されているようだ。

Disruptorの速さの秘密はいくつかのブログで詳細に解説されているので、じっくり知りたい人はソースコードを読むとともにそちらを参照するとよいだろう。

  • ロックを極力排除し、Producerからのアクセスの一ヶ所でしか使用していない
  • データ構造としてリングバッファを使い、メッセージが格納されている領域のインデックスを各Consumer等がシーケンス番号として管理することで、ロックを減らしている

何とも、非常に頭が良い解決方法である。

シーケンス番号はどんどん上がっていくので、配列を用意すると無限の大きさの配列が必要となってしまう。しかしリングバッファをぐるぐる廻して、全Consumerが処理済みの領域を上書きして使っていくことでこの問題を解決している。

どんなConsumerが存在するのか?が中央で管理されているのが面白い。オブジェクト指向的にはイケていないが、ロックを排除するための工夫ということだろう。

2015/01/06追記
最後の「オブジェクト指向的にはイケていないが…」の部分に対して「別にイケていなくてもいいのでは」的な反応を頂いたので追記しておく。

まさしくその通りで、Disruptorはパフォーマンスを極めるためのものなので、「オブジェクト指向的にイケて」いなくてもOKである。
しかし実は筆者ではなく、LMAXの中の人が、この点について(かなり強く)言及しており、ここは大きなポイントだ。
オブジェクト指向的に綺麗に設計する(関心を分離する)ことにこだわることがパフォーマンス劣化(正確にはcontention)につながっている
と、http://lmax-exchange.github.io/disruptor/files/Disruptor-1.0.pdfに記述されている。

Further investigation and a focus on the computer science made us realise that the conflation of concerns inherent in
conventional approaches, (e.g. queues and processing nodes) leads to contention in multi-threaded implementations,
suggesting that there may be a better approach.

同様にこちらのブログの以下の部分も同様。

The ConsumerTrackingProducerBarrier has a list of all the Consumers that are accessing the ring buffer. Now to me this seemed a bit odd – I wouldn’t expect the ProducerBarrier to know anything about the consuming side. But wait, there is a reason. Because we don’t want the “conflation of concerns” a queue has (it has to track the head and tail which are sometimes the same point), our consumers are responsible for knowing which sequence number they’re up to, not the ring buffer. So, if we want to make sure we don’t wrap the buffer, we need to check where the consumers have got to.

筆者の書き方が悪かったのだが、つまり「オブジェクト指向的にきれいな設計を捨てることで性能向上を実現している」という、トレードオフの部分が非常に面白いのだ。(そのため「オブジェクト指向的にはイケていない」と書いたことは文脈的におかしくないし、むしろもっと強調してもよい部分だと考えている。)

ちなみに上記の指摘をされた方からは「アルゴリズム的な話に、オブジェクト指向をもってくるのがナンセンスなんです。」とも言われたのだが、上記PDFの図を見てもらえばわかるようにDisruptorは典型的なオブジェクト指向で書かれたソフトウェアであるので、Disruptorについて言及している本エントリにおいて「オブジェクト指向をもってくるのがナンセンス」と言われても困る。おそらく指摘された方はDisruptorのドキュメントやソース等は読まずに、本エントリだけに脊髄反射されただけだろうと思う。

Disruptorはいわば「守破離」的に、全体としてはオブジェクト指向に従いながら、オブジェクト指向で当たり前とされる「関心の分離」の一部だけをわざと崩すことで最高のパフォーマンスを実現しているところが素晴らしい。


WekaのIBkはデフォルトで正規化してくれる

フリーソフトではじめる機械学習入門という本があります。

フリーソフトではじめる機械学習入門
荒木 雅弘
森北出版
売り上げランキング: 18,141

筆者的にはかなりツボな本で、既に2度ほど読み通しています。
今度は実際に手を動かしながら(Wekaを使ったりJavaのコードを書きながら)もう一度読んでみようかと思っています。

18ページあたりから、KnowledgeFlow Environment(KFE)を使ってWekaに格納されているirisのデータをK近傍法で分類する例が始まります。
28ページ真ん中あたりで正規化(1~10程度の値を取る属性と1~1000程度の属性などが混在する場合に、データ間の距離の計算において、後者の属性の影響が強くならないようにすること)が紹介され、下図のようにNormalize(左から3つめ)のが追加されます。

weka-nfe

筆者はまず、本書の通りにKFEを使ってみました。

次に、KFEのようなGUIのツールではなく、JavaのコードからIBkクラスを使ったirisデータの分類をしたかったので、Googleで調べながら独学で進めてみました。これは、特に難しいところはありませんでしたが、その時点で手元で書いてみたコードでは、データの正規化を行っていませんでした。そのためこのときの結果は不正確だろうと考えました。

影響を確認するために、わざと極端にレンジの広い属性と狭い属性を持つデータを用意して分類させてみたのですが、特におかしな結果になりません。仕方ないのでNormalizeクラスの使い方を学んできちんと正規化してみましたが、結果はNormalizeクラスを使っても使わなくても同じで、正常な結果となりました。

原因を調べてみると、どうやらIBkを利用したK近傍法による分類では、デフォルトで正規化を行ってくれるようです。デフォルトではインスタンス間の距離を計算するのにweka.core.EuclideanDistanceクラスが使われますが、こちらのクラスが計算の際に正規化をしてくれます。-Dオプションを渡すと、正規化を行わないという動作になるようです。

そのため、本書の28ページから29ページにかけて行われているNormalizeの追加は、省略しても同じ結果になります。