#freeze [[FrontPage]] #contents 2010/01/19からのアクセス回数 &counter; ** Tasteについて [#q4fdb91a] Tasteは、mahoutに含まれるレコメンドシステム用パッケージです。 mahout ver. 0.20のTasteは、まだMapReduce(hadoop)に対応して いないので、hadoupをインストールしなくても使用することができます。 ** 準備 [#i2a788fa] mahoutでの開発には、JDK 1.6が必要です。 *** Mac OS X (Leopard以降)でJDK 1.6を使う方法 [#kd7548af] ネット検索するとOS XでJDK 1.6を使うには、アプリケーションのユーティリティフォルダにある Java Preferencesで切り替えるとありますが、これを実施してもターミナルでjavac -versionが1.6に 切り替わりません。 そこで、以下の設定を.profileに追加します。 #pre{{ export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home export PATH=$JAVA_HOME/bin:$PATH }} 新しいターミナルを起動して、設定が正しくできたか確認します。 #pre{{ $ javac -version javac 1.6.0_15 }} ** レコメンド・アプリケーションの作成 [#jf267c14] *** プロジェクトの作成 [#pd53abc7] mavenを使ってレコメンド・アプリケーションを作成します。 最初に、プロジェクトを作成します。 #pre{{ $ mvn archetype:create -DgroupId=sample.recommendApp -DartifactId=recommendApp -Dversion=0.0.1 }} *** pomファイルの変更 [#o6606cd4] pom.xmlのdependecyに以下の項目を追加します。 #pre{{ <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-core</artifactId> <version>0.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jcl</artifactId> <version>1.5.8</version> <scope>test</scope> </dependency> }} また、JDKを1.6にするために以下のbuildを追加します。 #pre{{ <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> }} これで、&ref(pom.xml); の設定は完了です。 *** Eclipseのプロジェクトとしてインポート [#s711b2fc] Eclipseを使って編集ができるように、以下のコマンドでEclipseのプロジェクトにします。 #pre{{ mvn eclipse:eclipse -DdownloadSources=true }} これで、必要なライブラリがすべてセットされ、ソースの参照も可能なEclipseプロジェクト のできあがりです。 Eclipseで作成したrecommendAppをインポートしてください。 *** 最初のレコメンドアプリケーション [#wa964c85] [[集合知>図書/集合知]]の2章のデータを使って、ユークリッド距離によるレコメンドアプリケーション を作成してみましょう。 集合知のデータは、 #pre{{ # A dictionary of movie critics and their ratings of a small # set of movies critics={'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5, 'The Night Listener': 3.0}, 'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5, 'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0, 'You, Me and Dupree': 3.5}, 'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0, 'Superman Returns': 3.5, 'The Night Listener': 4.0}, 'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'The Night Listener': 4.5, 'Superman Returns': 4.0, 'You, Me and Dupree': 2.5}, 'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0, 'You, Me and Dupree': 2.0}, 'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5}, 'Toby': {'Snakes on a Plane':4.5,'You, Me and Dupree':1.0,'Superman Returns':4.0} } }} ですが、これを各評価を #pre{{ ユーザID,アイテムID,評価値 }} の並びに変えたものが、入力データのcritics.csvです。 ユーザIDは、 #pre{{ 1 Lisa Rose 2 Gen Seymour 3 Michael Phillips 4 Claudia Puig 5 Mick LaSalle 6 Jack Matthews 7 Toby }} アイテムIDは、 #pre{{ 1 Lisa Rose 2 Gen Seymour 3 Michael Phillips 4 Claudia Puig 5 Mick LaSalle 6 Jack Matthews 7 Toby }} です。 プログラムのメインは、以下のようになります。 #pre{{ // モデル構築用のデータをファイルから読み込む DataModel model = new FileDataModel(new File("data/critics.csv")); // ユーザ類似性と類似ユーザ抽出のメソッドを決定 UserSimilarity similarity = new EuclideanDistanceSimilarity(model); UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, model); // ユーザベースの推薦を作成 Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity); // Toby(ユーザID=7)への推薦項目を3個検索 List<RecommendedItem> recommendations = recommender.recommend(7, 3); // 推薦結果を出力 for (RecommendedItem recommendation : recommendations) { System.out.println(recommendation); } }} 以下のテストデータとjavaのプログラムがあります。 - &ref(App.java); - &ref(critics.csv); *** 実行結果 [#v2c64f3a] プログラムを実行すると以下のように出力されます。 #pre{{ 10/01/19 19:24:39 INFO file.FileDataModel: Creating FileDataModel for file data/critics.csv 10/01/19 19:24:39 INFO file.FileDataModel: Reading file info... 10/01/19 19:24:39 INFO file.FileDataModel: Read lines: 35 10/01/19 19:24:39 INFO model.GenericDataModel: Processed 7 users RecommendedItem[item:6, value:3.741911] RecommendedItem[item:1, value:3.0] RecommendedItem[item:3, value:2.4946072] }} 推薦の結果は、最後の2行です。ItemIdを映画の名前にすると - Item:6→ The Night Listener - Item:1→ Lady in the Water - Item:3→ Just My Luck となり、Tobyがまだ見ていない映画のなかから、Tobyと似た嗜好をもつユーザの 好きな映画がリストアップされます。 この結果は、「集合知」2.4の結果とスコアは異なりますが順序は一致します。 #pre{{ # 集合知の出力 >>>recommendations.getRecommendations(recommendations.critics,'Toby', ... similarity=recommendations.sim_distance) [(3.5002478401415877, 'The Night Listener'), (2.7561242939959363, 'Lady in the Water'), (2.4619884860743739, 'Just My Luck')] }} ** 実データを使った例 [#y2fb3aa7] 集合知のデータは、小さいので実用に耐えうるかどうかをみるために、実際のデータで 試してみます。 *** 実データの準備 [#y6c40abf] 実データとして、GroupLensのサイト((Mahout in Actionの4.2.3を参考)) から - [[MovieLens Data Set>http://www.grouplens.org/node/73]] から10 million ratings をダウンロードし、解凍したフォルダからua.baseを取り出します。 このファイルは、10 million ratingsよりさらに小さな評価データ90570個を含むデータファイルです。 以下のawkコマンドを使ってこのファイルをUserId, ItemId, Ratingのカンマ区切りのファイルに変換します。 #pre{{ $ awk '{printf("%s,%s,%s\n",$1,$2,$3);}' data/ua.base > movielens.csv }} *** 推薦手法の選択 [#w092e42f] 実データのように大きな規模になると推薦に要する計算時間が大きな要因になってきます。 今回は、ユーザアイテムマトリックスをすくない特性からなるユーザ特性マトリックスに変換して推薦する SVDRecommenderを使うことにします。 SVDRecommenderの引数は、 #pre{{ SVDRecommender(model, numFeatures, initialSetps) model モデル numFeatures 特性数 initialSetps イテレーション回数 }} です。 SVDRecommenderのメインは以下のようになります。 #pre{{ // モデル構築用のデータをファイルから読み込む DataModel model = new FileDataModel(new File("data/movielens.csv")); // ユーザベースのSVD推薦を作成 Recommender recommender = new SVDRecommender(model, 3, 50); // ユーザID=1への推薦項目を3個検索 List<RecommendedItem> recommendations = recommender.recommend(1, 3); // 推薦結果を出力 for (RecommendedItem recommendation : recommendations) { System.out.println(recommendation); } }} 実行結果は、以下のようになります。 #pre{{ 10/01/19 21:00:43 INFO file.FileDataModel: Creating FileDataModel for file data/movielens.csv 10/01/19 21:00:44 INFO file.FileDataModel: Reading file info... 10/01/19 21:00:44 INFO file.FileDataModel: Read lines: 90570 10/01/19 21:00:44 INFO model.GenericDataModel: Processed 943 users RecommendedItem[item:1449, value:5.2471733] RecommendedItem[item:483, value:5.05086] RecommendedItem[item:511, value:4.951155] }} しかし、これでは本当にうまく推薦できているのか分かりませんね。そこで推薦の精度を調べてみましょう。 *** 推薦の精度 [#k8cf8636] 推薦の評価方法のひとつに実際の評価値と推定値の誤差の自乗を足し合わせ平方根を取ったRMS(root mean squared)があります。 RMSRecommenderEvaluatorは、RMSで推薦を評価するクラスです。 evaluatorのevaluateメソッドは、最後の2つの引数が - 学習用データの割合 - 検証用データの割合 を指定します。 評価のメインクラスは、以下のようになります。 #pre{{ // モデル構築用のデータをファイルから読み込む DataModel model = new FileDataModel(new File("data/movielens.csv")); // RMSを使った評価オブジェクトを作成 RecommenderEvaluator evaluator = new RMSRecommenderEvaluator(); // 推薦ビルダーを定義 RecommenderBuilder builder = new RecommenderBuilder() { public Recommender buildRecommender(DataModel model) throws TasteException { return new SVDRecommender(model, 3, 50); } }; // 学習用データとして、全体の0.75を使い、検証用データとして0.25を使って評価する double score = evaluator.evaluate(builder, null, model, 0.75, 0.25); System.out.println(score); }} 実行結果は、 #pre{{ 10/01/19 21:25:16 INFO file.FileDataModel: Creating FileDataModel for file data/movielens.csv 10/01/19 21:25:16 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation using 0.75 of FileDataModel[dataFile:/Users/take/tmp/recommendApp/data/movielens.csv] 10/01/19 21:25:16 INFO file.FileDataModel: Reading file info... 10/01/19 21:25:16 INFO file.FileDataModel: Read lines: 90570 10/01/19 21:25:16 INFO model.GenericDataModel: Processed 943 users 10/01/19 21:25:16 INFO model.GenericDataModel: Processed 235 users 10/01/19 21:25:17 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation of 231 users 10/01/19 21:25:17 INFO eval.AbstractDifferenceRecommenderEvaluator: Item exists in test data but not training data: 360 10/01/19 21:25:17 INFO eval.AbstractDifferenceRecommenderEvaluator: Item exists in test data but not 途中省略 10/01/19 21:25:17 INFO eval.AbstractDifferenceRecommenderEvaluator: Item exists in test data but not training data: 1394 10/01/19 21:25:17 INFO eval.AbstractDifferenceRecommenderEvaluator: Finished 1 10/01/19 21:25:17 INFO eval.AbstractDifferenceRecommenderEvaluator: Evaluation result: 0.9598115822159887 0.9598115822159887 }} 0.9598は、まあまあの値です。どの推薦手法もそうですが、パラメータを調整しないとよい値はでません。 ** コメント [#kc9b000c] #vote(おもしろかった[46],そうでもない[0],わかりずらい[1]) #vote(おもしろかった[47],そうでもない[0],わかりずらい[1]) 皆様のご意見、ご希望をお待ちしております。 #comment_kcaptcha