Mahout:サンプル1:はじめてのレコメンド
Mahout:サンプル1:はじめてのレコメンド †
Mahoutを使った初めてのレコメンドです。
まあ、Mahout本に記載されているサンプルを実行してるだけですがw
※Mahout本にはimportするクラスとか書いてないけど、きっとサンプルソースはコーディングせずにDLして使ってね!ってことなんだよねw それとも、それぐらい解決できない人はそもそも対象外ですよ、ってことなのかなw
サンプルコード †
うだうだ長いですが、端的には
- DataModel?にデータを食わせて
- Simirarityを計算させて
- Neighborhoodを計算させて
- そいつらを元にRecommenderを生成して
- RecommenderにレコメンドするユーザID(1)と提示(レコメンデーション)させるアイテム数(1)を渡しているってことです。
■RecommenderIntro?.java
package test1; import java.io.File; import java.util.List; import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; public class RecommenderIntro { /** * @param args */ public static void main(String[] args) throws Exception{ DataModel model = new FileDataModel(new File("data/data1/intro.csv")); UserSimilarity similarity = new PearsonCorrelationSimilarity(model); UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, model); Recommender recommender = new GenericUserBasedRecommender( model, neighborhood, similarity); List<RecommendedItem> recommendations = recommender.recommend(1, 1); for (RecommendedItem recommendation : recommendations){ System.out.println(recommendation); } } }
サンプルで使用するデータは「data/data1/intro.csv」としてます。
■data/data1/intro.csv
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,5 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
して、サンプルの実行結果は以下のとおり。
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-jcl-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. 13/04/12 22:49:17 INFO file.FileDataModel: Creating FileDataModel for file data/data1/intro.csv 13/04/12 22:49:17 INFO file.FileDataModel: Reading file info... 13/04/12 22:49:17 INFO file.FileDataModel: Read lines: 21 13/04/12 22:49:17 INFO model.GenericDataModel: Processed 5 users RecommendedItem[item:104, value:4.257081]
ほっほー、とりあえず、結果が出力されましたw
結果としては、「ユーザ1に1つアイテムをレコメンドせい」という要求に対して「104ってやつがスコア4.26でおすすめです」と言うことですな。
じゃあ、レコメンドさせるアイテムを2にすると以下のようになります。
ソース上のここ
List<RecommendedItem> recommendations = recommender.recommend(1, 1);
を
List<RecommendedItem> recommendations = recommender.recommend(1, 2);
に修正して実行!っと。するとこんな感じです。
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-jcl-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. 13/04/12 23:21:42 INFO file.FileDataModel: Creating FileDataModel for file data/data1/intro.csv 13/04/12 23:21:43 INFO file.FileDataModel: Reading file info... 13/04/12 23:21:43 INFO file.FileDataModel: Read lines: 21 13/04/12 23:21:43 INFO model.GenericDataModel: Processed 5 users RecommendedItem[item:104, value:4.257081] RecommendedItem[item:106, value:4.0]
なるほど、2つのアイテムがレコメンドされた!そりゃあそうかw
レコメンダの評価 †
Mahout本では、いきなり次に、レコメンダの評価の話に移ります。
「評価ってなに?」って感じだけど、3回くらい読んで何となくわかった気になってます。
まあ、評価って「存在するデータの一部を使用してレコメンダを作成し、残りの一部(全部)のデータを使って、レコメンダで計算させた結果と、実際の値を比較し、評価する」ってこと見たいです。
上のサンプルを、評価を実行するように改変したコードは以下のとおり。なお、データは変わりません。
■RecommenderIntroAndEvaluation?.java
package test1; import java.io.File; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.eval.RecommenderBuilder; import org.apache.mahout.cf.taste.eval.RecommenderEvaluator; import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator; import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; import org.apache.mahout.common.RandomUtils; public class RecommenderIntroAndEvaluation { /** * @param args */ public static void main(String[] args) throws Exception{ RandomUtils.useTestSeed(); DataModel model = new FileDataModel(new File("data/data1/intro.csv")); RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator(); RecommenderBuilder builder = new RecommenderBuilder(){ @Override public Recommender buildRecommender(DataModel model) throws TasteException { UserSimilarity similarity = new PearsonCorrelationSimilarity(model); UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, model); return new GenericUserBasedRecommender( model, neighborhood, similarity); } }; // 70%のデータを使用してRecommenderを作成し、30%のデータを使用して評価を実行 double score = evaluator.evaluate(builder,null,model,0.7,1.0); System.out.println(score); } }
実行結果は以下のとおり。
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-jcl-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. 13/04/12 23:38:18 INFO file.FileDataModel: Creating FileDataModel for file data/data1/intro.csv 13/04/12 23:38:18 INFO file.FileDataModel: Reading file info... 13/04/12 23:38:18 INFO file.FileDataModel: Read lines: 21 13/04/12 23:38:18 INFO model.GenericDataModel: Processed 5 users 13/04/12 23:38:18 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation using 0.7 of FileDataModel[dataFile:/opt/extend/mahout/Mahout1/data/data1/intro.csv] 13/04/12 23:38:18 INFO model.GenericDataModel: Processed 5 users 13/04/12 23:38:18 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation of 3 users 13/04/12 23:38:18 INFO eval.AbstractDifferenceRecommenderEvaluator: Starting timing of 3 tasks in 1 threads 13/04/12 23:38:18 INFO eval.StatsCallable: Average time per recommendation: 7ms 13/04/12 23:38:18 INFO eval.StatsCallable: Approximate memory used: 6MB / 31MB 13/04/12 23:38:18 INFO eval.StatsCallable: Unable to recommend in 2 cases 13/04/12 23:38:18 INFO eval.AbstractDifferenceRecommenderEvaluator: Evaluation result: 1.0 1.0
はっきり言って、何の解説も無しにこの結果だけ見てもさっぱりわかりませんw
Mahout本によると、まあ、今回使用したevaluatorは「AverageAbsoluteDifferenceRecommenderEvaluator?」で、「レコメンダが推薦するアイテムの推奨値と、実際にユーザがつけた推奨値の差の絶対値の平均を出力する」評価器らしいw
ということは、今回の評価では、レコメンダが計算し推奨値と実際にユーザがつけた推奨値は平均で1.0の誤差があるということになるらしい。
なるほどー。
また、Mahout本では「単純な平均ではなく、二乗平均平方根でもスコアづけできるよ」ってかいてあったのでやってみましたw
「RMSRecommenderEvaluator?」っていうEvaluatorを使用するらしい。
して、以下のコードをmainメソッドの最後に追加。
RecommenderEvaluator evaluator2 = new RMSRecommenderEvaluator(); double score2 = evaluator2.evaluate(builder,null,model,0.7,1.0); System.out.println(score2);
結果はこんな感じです。
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-jcl-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/extend/mahout/Mahout1/lib-mahout/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. 13/04/13 00:05:12 INFO file.FileDataModel: Creating FileDataModel for file data/data1/intro.csv 13/04/13 00:05:12 INFO file.FileDataModel: Reading file info... 13/04/13 00:05:12 INFO file.FileDataModel: Read lines: 21 13/04/13 00:05:12 INFO model.GenericDataModel: Processed 5 users 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation using 0.7 of FileDataModel[dataFile:/opt/extend/mahout/Mahout1/data/data1/intro.csv] 13/04/13 00:05:12 INFO model.GenericDataModel: Processed 5 users 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation of 3 users 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Starting timing of 3 tasks in 1 threads 13/04/13 00:05:12 INFO eval.StatsCallable: Average time per recommendation: 6ms 13/04/13 00:05:12 INFO eval.StatsCallable: Approximate memory used: 6MB / 31MB 13/04/13 00:05:12 INFO eval.StatsCallable: Unable to recommend in 2 cases 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Evaluation result: 1.0 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation using 0.7 of FileDataModel[dataFile:/opt/extend/mahout/Mahout1/data/data1/intro.csv] 1.0 13/04/13 00:05:12 INFO model.GenericDataModel: Processed 5 users 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Beginning evaluation of 3 users 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Starting timing of 3 tasks in 1 threads 13/04/13 00:05:12 INFO eval.StatsCallable: Average time per recommendation: 1ms 13/04/13 00:05:12 INFO eval.StatsCallable: Approximate memory used: 6MB / 31MB 13/04/13 00:05:12 INFO eval.StatsCallable: Unable to recommend in 2 cases 13/04/13 00:05:12 INFO eval.AbstractDifferenceRecommenderEvaluator: Evaluation result: 1.0 1.0
うーむ、結局、今回のケースではどっちでやっても「1.0」なんだ、、、