Java
Maven
mahout

mahout をとりあえず動かしてレコメンドしてみる(動作確認程度)

More than 3 years have passed since last update.


シンプルなレコメンドの考え方

特にMahoutの仕組や分散による高速化には触れない。

ごく単純にmahoutをJavaで試すだけ。

但し、前提として単純に言うと


好きなもの
好きなもの
好きなもの

Aさん
林檎
蜜柑

Bさん

葡萄
バナナ

Cさん
林檎
蜜柑

Dさん
林檎
蜜柑

さて、Cさんが次に欲しいものは何?

感覚的にそれは「おそらく苺」だろう、と。

ここでは4件しか無い上に好きなもの丸かぶりだから直ぐ分かるけど

Cさんに似た嗜好を持つ人はだれかを探して(AさんとDさん)、

そこからオススメの果物(苺)をレコメンドする。

image

今回はユークリッド距離を利用し、

より距離が近い=好みが類似している人を特定する。

(映画の評価など厳しめや甘めの人が混在するような場合は

本当はユークリッド距離は向かない)


実装


maven


pom.xml

    <dependency>

<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<scope>provided</scope>
<version>0.9</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-math</artifactId>
<scope>provided</scope>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-examples</artifactId>
<scope>provided</scope>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-collections</artifactId>
<scope>provided</scope>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-utils</artifactId>
<scope>provided</scope>
<version>0.5</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
</exclusion>
</exclusions>
</dependency>


Javaコード


sample.java

import java.util.ArrayList;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
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.EuclideanDistanceSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
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 AdHocTestSpace {
public static void main(String argv[]){

try {
/*
* 数値型に置き換えて値を渡す
* 第1引数
* 100 Aさん
* 200 Bさん
* 300 Cさん
* 400 Cさん
*
* 第2引数
* 1 林檎
* 2 蜜柑
* 3 苺
* 4 梨
* 5 葡萄
* 6 バナナ
*
* 第3引数
* ここは好みを表すが今回は5.0固定(値に特に意味は無い)
* やろうと思えば
* 1.0 あんまり・・・
* 2.0 ふつー
* 3.0 好き
* みたいに好みの重さを指定できる。
*/

List<Preference> list_1 = new ArrayList<>();
List<Preference> list_2 = new ArrayList<>();
List<Preference> list_3 = new ArrayList<>();
List<Preference> list_4 = new ArrayList<>();

// Aさんは、林檎と蜜柑と苺が好き
list_1.add(new GenericPreference(100, 1, 5.0f));
list_1.add(new GenericPreference(100, 2, 5.0f));
list_1.add(new GenericPreference(100, 3, 5.0f));

// Bさんは、梨と葡萄とバナナが好き
list_2.add(new GenericPreference(200, 4, 5.0f));
list_2.add(new GenericPreference(200, 5, 5.0f));
list_2.add(new GenericPreference(200, 6, 5.0f));

// Cさんは、林檎と蜜柑が好き
list_3.add(new GenericPreference(300, 1, 5.0f));
list_3.add(new GenericPreference(300, 2, 5.0f));

// Dさんは、林檎と蜜柑と苺が好き
list_4.add(new GenericPreference(400, 1, 5.0f));
list_4.add(new GenericPreference(400, 2, 5.0f));
list_4.add(new GenericPreference(400, 3, 5.0f));

/*
* これらの情報をモデルにセット
*/

FastByIDMap<PreferenceArray> input_info = new FastByIDMap<>();
input_info.put(100, new GenericUserPreferenceArray(list_1));
input_info.put(200, new GenericUserPreferenceArray(list_2));
input_info.put(300, new GenericUserPreferenceArray(list_3));
input_info.put(400, new GenericUserPreferenceArray(list_4));

DataModel model = new GenericDataModel(input_info);

// ユークリッド距離を利用
UserSimilarity similarity = new EuclideanDistanceSimilarity(model);

/**
* 類似(距離の近い)ユーザをn件迄を検索
* とりあえず、3くらいで
*/

UserNeighborhood neighborhood = new NearestNUserNeighborhood(3, similarity , model);

/**
* Cさんの好みをレコメンド
* 結果は 3:苺 になる
*/

Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
List<RecommendedItem> rcmmList = recommender.recommend(300, 5);
System.out.println("Cさんにオススメしたい果物のIDは以下のとおり");
rcmmList.stream().forEach(System.out::println);

} catch (TasteException ex) {
Logger.getLogger(AdHocTestSpace.class.getName()).log(Level.SEVERE, null, ex);
}
}
}