Mahoutで分散レコメンド(3)
前回は 5ユーザ, 7アイテム, 21評価 という非常に小さいデータでした。さて、今回は大きめのデータを使ってみましょう。6040ユーザ, 3900アイテム, 100万評価です。
データの準備
GroupLensというラボが、評価データを公開してくれています。研究開発目的に限り無償で利用できるので、今回はこちらを利用させてもらいましょう。
このデータはMovieLensと言い、映画の評価情報の塊です。データ規模別に3つに分かれています。
- 943ユーザ, 1682アイテム, 10万評価
- 6040ユーザ, 3900アイテム, 100万評価
- 71567ユーザ, 10681アイテム, 1000万評価(+10万タグ)
ここでは、(何の根拠もないですが)100万評価を選びました。「1M Ratings Data Set (.tar.gz)」ってのをDLしましょう。このデータはREADMEを除いて3つのファイルから構成されています。
- movies.dat : 要するにアイテムマスタです。"::"区切りで、ID::タイトル::ジャンルですね。ジャンルは"|"区切りで複数指定です。
1::Toy Story (1995)::Animation|Children's|Comedy 2::Jumanji (1995)::Adventure|Children's|Fantasy 3::Grumpier Old Men (1995)::Comedy|Romance ...(略)...
- users.dat : 要するにユーザマスタ。やはり"::"区切りで、ID::性別::年齢層::職業::郵便番号です。今回はあまり使いません。
1::F::1::10::48067 2::M::56::16::70072 3::M::25::15::55117 ...(略)...
- ratings.dat : これが評価データです。ユーザID::アイテムID::評点(1〜5)::タイムスタンプ(epoch)
1::1193::5::978300760 1::661::3::978302109 1::914::3::978301968 ...(略)...
で、rating.datは、このままの形でMahoutのRecommenderJobに食わせることができません。フォーマットが違います。ユーザID,アイテムID,評点(float)って感じなので、まぁコードでも書いて上手く変換してください。
BufferedReader in = null; BufferedWriter out = null; try { in = new BufferedReader(new InputStreamReader(new FileInputStream(INPUT))); out = new BufferedWriter(new FileWriter(OUTPUT)); String buf; while ((buf = in.readLine()) != null) { // UserID::MovieID::Rating::Timestamp String[] split = buf.split("::"); out.write(String.format("%s,%s,%s%n", split[0], split[1], split[2])); } } finally { Closeables.closeQuietly(in); Closeables.closeQuietly(out); }
で、この変換後のファイルをratings.txtと呼びましょう。
データの追加
まぁ、ratings.txtを使って、ユーザ1〜6040の誰かに対してレコメンドを計算させてもいいんですけど、それだとなんだかつまらない。ということで、自分の見た事ある映画の評価情報も混ぜてみました。
9999,47,2 9999,50,5 9999,126,3 ...(略)...
意外とこのデータ作るのは大変だと思いますが、実際回してみる際は是非、やってみると面白いと思います。こいつをmyratings.txtとしましょう。ユーザIDの9999は自分です。
そしてusers.txt(前回参照)も用意しましょう。
9999
いざ計算
まずは必要なデータをHDFSに転送。
前回のレコメンドを回した人は、inputディレクトリに既に既に色々ファイルがあると思いますが、一回全部消しましょう。outputディレクトリやtempディレクトリなども出来ていると思いますが、これも存在すると勝手に上書きはしてくれないので、消しておきましょう。
$ hadoop fs -put /path/to/ratings.txt input/ratings.txt $ hadoop fs -put /path/to/myratings.txt input/myratings.txt $ hadoop fs -put /path/to/users.txt users.txt
ちなみに、前回はHadoopの起動コマンドラインに入力ファイルを指定しましたが、今回はファイルが複数なので、入力ディレクトリとしてinputを指定します。このinputディレクトリ内にある全ファイルを読み込むため、inputディレクトリにこれ以外のファイルが入らないようにしましょう。上記の例でもusers.txtはinputディレクトリに入れてません。
$ hadoop jar /path/to/mahout-core-0.5-job.jar \ org.apache.mahout.cf.taste.hadoop.item.RecommenderJob \ -Dmapred.output.dir=output \ -Dmapred.input.dir=input \ --usersFile users.txt \ --similarityClassname SIMILARITY_PEARSON_CORRELATION
今回はこんな感じ。前回は5分で済みましたが、今回は77分かかりました。結果はこんなかんじ。
$ hadoop fs -cat output/part-r-00000 9999 [1038:5.0,1565:5.0,2638:5.0,3184:5.0,3106:5.0,2063:5.0,3092:5.0,2049:5.0,630:5.0,37:5.0]
元の評価データが1〜5の整数だったのでバリエーションが少ないのかな…。アイテムベースだからか…? なぜか全部5.0という予想評点でした。まだまだ5予想のアイテムが数多くある中の10個なんでしょう。100個でやってみても全部5.0でした。なんか微妙に納得いきませんが…。とりあえず挙げられた映画をリストアップ。
1038::Unhook the Stars (1996)::Drama 1565::Head Above Water (1996)::Comedy|Thriller 2638::Mummy's Tomb, The (1942)::Horror 3184::Montana (1998)::Action|Comedy|Crime|Drama 3106::Come See the Paradise (1990)::Drama|Romance 2063::Seventh Heaven (Le Septième ciel) (1997)::Drama|Romance 3092::Chushingura (1962)::Drama 2049::Happiest Millionaire, The (1967)::Comedy|Musical 630::Carried Away (1996)::Drama|Romance 37::Across the Sea of Time (1995)::Documentary
ふーん…。まぁ、いまいち映画に興味がない都元でありました。
さて、次回は
次回は疑似分散じゃなくて本格的にこいつを分散させてみます。Amazon Elastic MapReduce っていうサービス(有料)を使いますので、実際に試してみたい方はAmazon Web Servicesの以下のサービスを申し込んでおきましょう。
- S3
- EC2
- SimpleDB
- Elastic MapReduce
追記
と、思ってたら、ボヤっとしているうちにsuz-labさんに先を越されてしまいましたwww 続きはあちらで(ぉ