LibRec というJava製の推薦システム用ライブラリがある。
Treasure DataインターンのときやPython, Juliaで推薦アルゴリズムの実装をライブラリ化したとき (FluRS, Recommendation.jl) にはこの実装を大いに参考にした。
しかし思い返すとこのライブラリ自体を実際に動かしたことがなかったので、documentationに従ってサンプルコードを読みつつ動かしてみる。
雰囲気
CLIもあるらしいけど、今回は普通にJavaでコードを書くタイプの例を試す。
Mavenプロジェクトなら dependency
に net.librec
を入れればすぐに使える:
<dependency>
<groupId>net.librec</groupId>
<artifactId>librec-core</artifactId>
<version>2.0.0</version>
</dependency>
1. Build data model
コードはデータモデルを生成するところから始まる:
// build data model
Configuration conf = new Configuration();
conf.set("dfs.data.dir", "/Users/takuti/src/github.com/guoguibing/librec/data");
TextDataModel dataModel = new TextDataModel(conf);
dataModel.buildDataModel();
読み込むデータは user-id item-id rating
(スペース区切り)からなるテキストファイルとWekaのARFFをサポートしている。
ファイルのパスやアルゴリズムのハイパーパラメータ(e.g., kNNの近傍数)はすべて Configuration
経由で設定する。その実体は librec.properties で、この形式で記述した独自の設定をガッとまとめて読み込むことも可能:
Resource resource = new Resource("rec/cf/itemknn-test.properties");
conf.addResource(resource);
dataModel.buildDataModel()
では指定されたパス内のファイルをすべて読んで、train/testデータへの分割までやってくれる。このあたりの挙動もすべて librec.properties に従っている。
2. Build recommender context
さっき定義した Configuration
と dataModel
を使って、これから行うタスクのためのコンテキストを生成する:
// build recommender context
RecommenderContext context = new RecommenderContext(conf, dataModel);
3. Build similarity
求めたい類似度を設定して、計算して、それをコンテキストにセットする:
// build similarity
conf.set("rec.recommender.similarity.key" ,"item");
RecommenderSimilarity similarity = new PCCSimilarity();
similarity.buildSimilarityMatrix(dataModel);
context.setSimilarity(similarity);
ここでは アイテムに対して (item-based)、ピアソン相関係数 (PCC) に基づく類似度を計算させている。
4. Build & run recommender
Recommender
を生成してコンテキストを渡してあげる。ここでは近傍数 (rec.neighbors.knn.number
) が5の ItemKNNRecommender()
を生成:
// build recommender
conf.set("rec.neighbors.knn.number", "5");
Recommender recommender = new ItemKNNRecommender();
recommender.setContext(context);
つまり、事前にコンテキストにセットしていた類似度の設定と合わせると、今回は『ピアソン相関係数に基づく $k=5$ の item-based collaborative filtering』を実行することになる。
ここまで来れば、実行はワンライン:
// run recommender algorithm
recommender.recommend(context);
5. Evaluation
RMSEEvaluator
を生成して、それを recommend()
実行済の recommender
の evaluate()
に渡してあげることで評価が行われる:
// evaluate the recommended result
RecommenderEvaluator evaluator = new RMSEEvaluator();
System.out.println("RMSE:" + recommender.evaluate(evaluator)); // => RMSE:0.8352805769243591
6. Get results
recommender.recommend()
で得られた推薦(予測)結果を RecommendedItem
というオブジェクトのリストとして取得することができる。
取得対象の RecommendedItem
にフィルターをかけることもできる。たとえば『ユーザ#1またはアイテム#70を対象とする予測結果』が見たければ、次のようにフィルターをかける:
// set id list of filter
List<String> userIdList = new ArrayList<String>();
List<String> itemIdList = new ArrayList<String>();
userIdList.add("1");
itemIdList.add("70");
// filter the recommended result
List<RecommendedItem> recommendedItemList = recommender.getRecommendedList();
GenericRecommendedFilter filter = new GenericRecommendedFilter();
filter.setUserIdList(userIdList);
filter.setItemIdList(itemIdList);
recommendedItemList = filter.filter(recommendedItemList);
あとは煮るなり焼くなり。表示してみる:
// print filter result
for (RecommendedItem recommendedItem : recommendedItemList) {
System.out.println(
"user:" + recommendedItem.getUserId() + " " +
"item:" + recommendedItem.getItemId() + " " +
"value:" + recommendedItem.getValue()
);
}
/*
user:1 item:1 value:3.72186572013805
user:1 item:9 value:3.6660434401297843
user:659 item:70 value:2.381236774589509
user:1065 item:70 value:2.9759100334538178
user:1 item:4 value:3.6802197608115867
user:918 item:70 value:2.660444061555275
*/
確かにユーザ#1またはアイテム#70に対する結果のみが得られている。
以上、チュートリアルでした。
ここから先は、たとえばクエリを組み立ててDBに問い合わせる処理が来たりするのかな。
良さ
LibRecの強みは何と言っても豊富なアルゴリズム。
Most Popular(ひたすら一番人気のアイテムを推薦する)のような Non-personalized Recommenders から、古典的な協調フィルタリング、そして新しいところでは Factorization Machines や Restricted Boltzmann Machine を使った手法まで、実に70種類以上の推薦手法をサポートしている (cf. Algorithm List)。
そして実装がシンプルで、とてもうまく設計されていると思う。
先の例だけ見ても、推薦システム構築の際の "チェックポイント" となりうるパーツが綺麗に分離されていることがわかる:
- Recommender
- Context
- Configuration
- Data Model
- Similarity
- Evaluator
- Context
- Filter
「アルゴリズムを切り替えたいな」と思えば Recommender recommender = new ItemKNNRecommender();
を別の Recommender にすれば良いし、類似度や評価指標の変更も同様にできる。
もちろん手法に応じて細かい設定項目は変わるけど、それらはすべて Configuration
でラップしていて、手法にほぼ依存しないインタフェースを提供している。
これによって「さっきコンテキストをセットしたのにまた渡すの?」と思ってしまうような実装にはなるが、それはもはや些細な問題だと思う:
Recommender recommender = new ItemKNNRecommender();
recommender.setContext(context);
recommender.recommend(context);
推薦アルゴリズムを自分で実装したことがある人なら分かると思うけど、複数の手法に対して同一のインタフェースを提供するのは結構難しい。評価値 (rating) 情報とランキング情報に対するデータ構造の違い(e.g., 行列 or リスト)や、アルゴリズムごとに異なる大量のハイパーパラメータ…考えただけで頭が痛い。それにもかかわらず、推薦というタスクは様々な設定(アルゴリズム、評価指標、類似度、データ、etc...)の組み合わせの試行錯誤が当たり前という現実もある。
その点、LibRecの実装から学ぶべきことは多い。
というわけで、突然中国語のdocumentが出てきたりしてたまに不安になるけど、これからも応援していきたいプロジェクト LibRec の話でした。
シェアする
カテゴリ
あわせて読みたい
- 2017-11-23
- 筋トレ、登山、昨今の推薦システムのトレンドなどについて話しました
- 2017-05-14
- 推薦システムのためのOSSたち
- 2017-01-27
- Courseraの推薦システムのコースを修了した
最終更新日: 2022-01-18
書いた人: たくち
Takuya Kitazawa(たくち)です。長野県出身、カナダ・バンクーバー在住のソフトウェアエンジニア。これまでB2B/B2Cの各領域で、Web技術・データサイエンス・機械学習のプロダクト化および顧客への導入支援・コンサルティング、そして関連分野の啓蒙活動に携わってきました。現在は主に北米(カナダ)、アジア(日本)、アフリカ(マラウイ)の個人および企業を対象にフリーランスとして活動中。詳しい経歴はレジュメ を参照ください。いろいろなまちを走って、時に自然と戯れながら、その時間その場所の「日常」を生きています。ご意見・ご感想およびお仕事のご相談は [email protected] まで。
近況 一杯のコーヒーを贈る免責事項
- Amazonのアソシエイトとして、当サイトは amazon.co.jp 上の適格販売により収入を得ています。
- 当サイトおよび関連するメディア上での発言はすべて私個人の見解であり、所属する(あるいは過去に所属した)組織のいかなる見解を代表するものでもありません。
- 当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、個人ブログという性質上、誤情報や客観性を欠いた意見が入り込んでいることもございます。いかなる場合でも、当サイトおよびリンク先に掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
- その他、記事の内容や掲載画像などに問題がございましたら、直接メールでご連絡ください。確認の後、対応させていただきます。