都元ダイスケ IT-PRESS このページをアンテナに追加 RSSフィード

最近は会社ブログしか書いてません。

2011-03-09

[][]Apache Mahoutで機械学習してみるべ

Mahoutシリーズ目次(随時更新)

非分散レコメンデーション
  1. Apache Mahoutで機械学習してみるべ - 都元ダイスケ IT-PRESS (これ)
  2. レコメンデーションの簡単な原理を視覚的に把握してから実際に計算してみる - 都元ダイスケ IT-PRESS
  3. 機械学習における重大な"仮定"と、アルゴリズムの評価 - 都元ダイスケ IT-PRESS
分散レコメンデーション
  1. Mahoutで分散レコメンド(1) - 都元ダイスケ IT-PRESS
  2. Mahoutで分散レコメンド(2) - 都元ダイスケ IT-PRESS
  3. Mahoutで分散レコメンド(3) - 都元ダイスケ IT-PRESS
クラスタリング
  1. 今度はMahoutでクラスタリング - 都元ダイスケ IT-PRESS
  2. 今度はMahoutでクラスタリング(ソース編) - 都元ダイスケ IT-PRESS

では、本文いきます。

Apache Mahoutっていう機械学習ライブラリがあります。詳しくはno title辺りを参考にしてください。

まぁ、要するにクラスタリング*1とか、レコメンデーション*2なんかをするクラスライブラリです。

例えばAmazonの「おすすめ」は、大勢のユーザの購買履歴に基づいて、各ユーザが興味を持ちそうな製品を算出しています。また、Facebookの「もしかして、この人と知り合いではありませんか?」というのも、機械学習によるものです。

さらに、Google Newsには次々とニュース記事が入ってきますが、「あるニュースとその続報」など、関連の深いニュースをひとまとめのスレッドにして表示したりしています。この「ニュースの分類」も手動ではなく、本文中に現れる単語の頻度などを分析した結果、自動でまとめています。また、同じ技術を使って、「このメールはスパムか否か」ということも計算で判定できるのです。これはgmail等で利用されています。

そんな中、レコメンデーションエンジンなんかは、様々な言語で色々な実装があり、OSSとして公開されているものも多くあります。そんな中でMahoutが一押しであるのは、スケーラビリティの確保に重点が置かれていることです。

機械学習というのは、当然、計算に基づいて結果を出すわけですが、その基礎となるデータが多ければ多いほど、確からしい結果を出してくれます。が、しかし、データが多ければ多いほど、指数的に計算量が増加する傾向があります。近年、このような大量データ処理のニーズは高まっています。その辺りの考察はHadoopとかに入門してみる 〜 分散技術が出てきた背景 - 都元ダイスケ IT-PRESSを参照。

じゃあHadoopの上で動く機械学習ライブラリを実装すりゃいいじゃん、ていうのがMahoutです。

えぇい、そろそろコード見せろ、って思ってますね? とりあえずレコメンデーションでいきましょう。ひとまずHadoopとかは出て来ません。簡単な感じでいきます。

データを用意する

まず、「基礎となるデータ」を用意します。誰が何をどのくらい好きか、のデータですね。

1,101,5.0
1,102,3.0
1,103,2.5

2,101,2.0
2,102,2.5
2,103,5.0
2,104,2.0

3,101,2.5
3,104,4.0
3,105,4.5
3,107,5.0

4,101,5.0
4,103,3.0
4,104,4.5
4,106,4.0

5,101,4.0
5,102,3.0
5,103,2.0
5,104,4.0
5,105,3.5
5,106,4.0

こんなんですね。CSVフォーマットで、user,item,preference という順に並んでます。userとitemはlong型のID、preferenceはfloat型です。ここでは1.0〜5.0の範囲で適当に。5人のユーザと7つのアイテムに登場してもらいました。1番の人は101番のアイテムに5.0点つけました。…(中略)…5番の人は106のアイテムに4.0点をつけました。ってデータですね。

この状況で、1番の人に「新たなアイテム*3」を1つだけお薦めするとしたらどれ? というのがレコメンドです。レコメンデーションエンジンは101〜107のアイテムしか知らないので、104〜107の4択ですが。データ量が多くなれば、もっと幅広く、予想のつかない結果が出て面白いです。

Javaプロジェクトを用意する

Mahoutを使うために、Mavendependencyにコレを入れておきましょう。

<dependency>
  <groupId>org.apache.mahout</groupId>
  <artifactId>mahout-core</artifactId>
  <version>0.4</version>
</dependency>

さっき用意したcsvは、src/main/resources/intro.csv あたりに保存しておきましょう。

コードを書く

DataModel model = new FileDataModel(new File("src/main/resources/intro.csv"));
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);

// 第一引数の意味は明日解説
UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, model);

Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);

// 1番の人に対するレコメンドが1つ欲しい、という意味で(1, 1)です。
List<RecommendedItem> recommendations = recommender.recommend(1, 1);
for (RecommendedItem recommendation : recommendations) {
     System.out.println(recommendation);
}

こんだけです! ホントにこんだけです。すげーー。出力結果はこれ。

RecommendedItem[item:104, value:4.257081]

1番の人にお勧めなのは、104のアイテムで、恐らくこの人にこのアイテムを評価させたら4.25点くらいをつけるでしょう。ということです。

*1:データの集合をクラスタと呼ぶグループに分ける。似たようなデータが同じクラスタに属するようになる。

*2:多くのユーザの好みに基づいて、特定ユーザーが興味を持つと思われる情報をお薦めとして提示する。

*3:101〜103は既に知ってるアイテムなのでレコメンドする意味はない。

2010-09-15

[]Hadoopとかに入門してみる 〜 分散技術が出てきた背景

調べたメモ。色々思い込みや想定に基づいた事も書いてるので、鵜呑みして騙され注意報発令さしとく。

最近分散技術系の話題をよく聞くようになりました。企業内グループ内で使うような業務システムであれば、そこまで無茶な数のアクセスも無いだろうから、数台〜数十台規模のサーバを立てればだいたい事足りたのだろう。例えば、サーバ構成を「Webサーバ - APサーバ - DBサーバ」という3レイヤにして、各サーバを冗長化していく、等の手法でどうにかなった。

ただ、処理リクエスト数の増大や、処理対象データの増大、そして処理ロジックの複雑化に伴って、大量のデータを逐次処理するだけでは処理が追いつかない世界が出てきた。業務システムではなく、サービスプロバイダの世界では、この現象は顕著。

また、Webサーバ層とAPサーバ層の冗長化は比較的簡単だけども、DBサーバ層は大量のステートを持っているレイヤだから冗長化がめんどくさい。1つのサーバに更新をかけても同時に他のサーバに更新がかかる訳じゃない。一瞬かもしれないけど整合性が崩れる。っていうか、負荷分散のための冗長化なのに、レプリケーションのコスト大きすぎて逆効果だったりしないのか心配になります。

等々。なんか、今までのパラダイムだと、このような大きな情報処理にはついていけない。ということで分散コンピューティングとかいう考え方が話題になってきた。ってところかな。(他にも理由は色々ありそうだけど)

例えば大量のアクセスログから、特定の条件にマッチしたものを抽出する場合。今までだったら、ログを頭から読んでいって、1エントリ毎に条件を検討して、マッチしていたら別に出力。というようなことをするだろう。そうではなくて、大量のログを例えば100個に分割して、100個のタスクを同時並行処理して、それぞれの結果をマージする、とすれば処理速度上がるよね? という話。

ただ、1台のマシン(ノード)に複数のスレッドなりプロセスを乗せて並行処理をしても、処理スケールには限度がある。前の例では、恐らく処理速度は数倍にはなったとしても100倍には及ばないだろう。元々1ノードの限界は決まってるのだから。従って、複数のノードにまたがって処理を分散させなければ、あまり大きな効果は望めない。ノードの数を100台にできれば、処理速度100倍は無理にしても、それに近いパフォーマンスは出せるのではないか。

また、並行処理(マルチスレッド等)プログラミングってのは異様に難しい。例えば「ここに大量(数TB規模)のasciiの英文テキストファイルがある。この中で使われている英単語の中で1番出現頻度の高い単語を抽出せよ。」というジョブがあったとする。この処理プログラムを単ノード単プロセススレッドで書くのは難しいことじゃない。しかし、条件として「結果は1分以内に返してください。ハードウェアの数はいくらでも用意しますので…」とか言われたらどうでしょう? 突然難しくなると思います。

従来のコーディングパラダイムロジックを実装すると、分散に対応できる実装にはならない。分散処理を目的としたロジックの書き方、というものがある。ならば分散向きのロジック記述の枠組み、すなわち、フレームワークが欲しい。というのがMapReduceだ。この枠組みの上でロジックを実装すれば、あとはフレームワークが上手いことジョブをタスクに分割して、タスクを各ノードに分散させて、結果をマージしてくれる。

MapReduceは、ジョブをこういう仕組みの上に実装すると、分散しやすくなるよ、という指針であり、そのように実装されたジョブをどのように分散処理すべきなのかを記した仕様。そして、この仕様をJavaで実装したのがHadoop

今までフリーダムに実装してきたロジックを、どのように MapReduce という枠組み上に乗せ、それを実装するのか?ってのがキモになりそうだ。手続き型からオブジェクト指向へのパラダイム転換と同じ様に、プログラム記述の考え方をひっくり返さなきゃいけないかもですね。