Javaで協調フィルタリングベースのリコメンドエンジンを実装してみた
Apache CommonsのCommons Mathライブラリを用いて、
Javaで協調フィルタリングベースのリコメンドエンジンを簡単に実装できたので、
実装方法をまとめておきます。
# 勉強のためのお試しという意味での「簡単」です。。
# 実務ベースでは(データサイズが大きいとかとか)いろいろあると思います。
協調フィルタリングベースのリコメンドエンジンはさまざまな計算方法があるので、
本エントリでの実装方法は、あくまで一例ということで参考にして下さい。
# ここでは、協調フィルタリング自体の説明はしません。
本エントリでは、以下の点について記載します。
・協調フィルタリングベースのリコメンドエンジンとは
・協調フィルタリングベースのリコメンドの計算方法
・Java/CommonsMathでのリコメンドエンジンの実装
協調フィルタリングベースのリコメンドエンジンとは
協調フィルタリングとは、
『多くのユーザの嗜好情報を蓄積し、
あるユーザと嗜好の類似した他のユーザの情報を用いて自動的に推論を行う方法論』
です。(日本語版Wikipediaより)
具体的に言うと、ネットショップでおすすめ商品を選ぶために使われている手法です。
# AmazonECサイトの「この商品を買った人はこんな商品も買っています」など。
もちろんネットショップのおすすめ商品以外にも応用は可能ですが、
身近でわかりやすい例として。
英語版WikipediaのGIFアニメが
何をしているのかイメージしやすいと思ったので、リンクを記載しておきます。
参考: Collaborative filtering - Wikipedia
http://en.wikipedia.org/wiki/Collaborative_filtering
協調フィルタリングベースのリコメンドの計算方法
本エントリでは、
過去に海外旅行に行ったことがある旅行者の旅先情報を使用して、
これから旅行しようとしている旅行者に、次の旅先をおすすめしたいと思います。
以下のデータを想定します。
# 台北とバンコク、ホノルルとグアムの相関が強くなるように、私が勝手においた値です。
◆表①:過去の旅行者の旅先情報(旅行回数)
台北 | バンコク | ホノルル | グアム | |
旅行者① | 2 | 1 | 0 | 0 |
旅行者② | 1 | 0 | 0 | 0 |
旅行者③ | 0 | 0 | 3 | 2 |
旅行者④ | 2 | 3 | 1 | 0 |
旅行者⑤ | 1 | 0 | 2 | 5 |
◆表②:これから旅行しようとしている旅行者の過去の旅先情報
台北 | バンコク | ホノルル | グアム | |
旅行者⑥ | 0 | 1 | 0 | 0 |
計算の流れは、次の通りです。
- 表①のデータから旅先同士の相関係数行列を算出
- 表②と相関係数行列の積を求める
表①のデータから旅先同士の相関係数行列を算出
まず、「過去の旅行者の旅先情報」を元に、
相関がある旅先(=「この旅先に行った人はこの旅先にも行っています」)
の対応情報を作ります。
ここでは、ピアソンの偏差積率相関係数を使って相関係数行列を算出します。
ピアソンの積率相関係数は、
{% mathjax %}( {s_{xy}} ){% endmathjax %}はxとyの共分散、{% mathjax %}(s^2_x, s^2_y){% endmathjax %}はx, yの分散、{% mathjax %}(\bar x, \bar y){% endmathjax %}はx, yの平均として
以下の式で定義されます。
上記の式から、例えば台北とバンコクの相関係数は以下のように計算できます。
{% mathjax %} \begin{eqnarray} \bar x &= &\frac{2 + 1 + 0 + 2 + 1}{5} = \frac{6}{5} \\ \bar y &= &\frac{1 + 0 + 0 + 3 + 0}{5} = \frac{4}{5} \\ s_x &= &\sqrt{\frac{1}{5} ((2-\frac{6}{5})^2 + (1-\frac{6}{5})^2 + (0-\frac{6}{5})^2 + (2-\frac{6}{5})^2 + (1-\frac{6}{5})^2)} = \sqrt{0.56} \\ s_y &= &\sqrt{\frac{1}{5} ((1-\frac{4}{5})^2 + (0-\frac{4}{5})^2 + (0-\frac{4}{5})^2 + (3-\frac{4}{5})^2 + (0-\frac{4}{5})^2)} = \sqrt{1.36} \\ s_{xy} &= &\frac{1}{5} ((2-\frac{6}{5})(1-\frac{4}{5}) + (1-\frac{6}{5})(0-\frac{4}{5}) \\ & &+ (0-\frac{6}{5})(0-\frac{4}{5}) + (2-\frac{6}{5})(3-\frac{4}{5}) + (1-\frac{6}{5})(0-\frac{4}{5}))) = 0.64 \\ r &= &\frac{0.64}{\sqrt{0.54} * \sqrt{1.36}} = 0.733358798 \end{eqnarray} {% endmathjax %}他の組み合わせも同様に計算すると表③のような相関係数行列が求められます。
◆表③:過去の旅行者の旅先先から求めた相関係数行列
台北 | バンコク | ホノルル | グアム | |
台北 | 1.0 | 0.7333587976 | -0.7333587976 | -0.4637130168 |
バンコク | 0.7333587976 | 1.0 | -0.2647058824 | -0.4900980294 |
ホノルル | -0.7333587976 | -0.2647058824 | 1.0 | 0.6651330399 |
グアム | -0.4637130168 | -0.4900980294 | 0.6651330399 | 1.0 |
2. 表②と相関係数行列の積を求める
これから旅行しようとしている旅行者⑥に、
次の旅先をおすすめするために、
旅行者⑥の過去の渡航情報(表②)と相関係数行列の積を求めます。
この計算結果がおすすめのスコアになります。
台北: 0.7333587976225692
バンコク: 1.0
ホノルル: -0.26470588235294124
グアム: -0.49009802940980335
この結果から、旅行者⑥に対しては、
バンコクや台北がおすすめ(=「この旅先に行った人はこの旅先にも行っています」)で、
ホノルルやグアムはあまりおすすめではないことがわかります。
Java/CommonsMathでのリコメンドエンジンの実装
次に、
「協調フィルタリングベースのリコメンドの計算方法」で説明したリコメンドを、
Javaを用いて実装してみます。
先ほど行った計算と同じ内容をJavaで書き直しただけなので、
説明は割愛してプログラムと実行結果だけを掲載しておきます。
行列計算を簡単にするため、
Commons Mathを以下からダウンロードしてCLASSPATHを通しておきます。
Commons Math: Apache Commons Mathmatics Library
http://commons.apache.org/proper/commons-math/
ファイル名(今回利用したもの): commons-math3-3.2.jar
RecommendEngine.java
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.stat.correlation.PearsonsCorrelation;
public class RecommendEngine {
public static void main(String[] args) throws Exception {
// 過去の渡航者データ
String[] prodname = {"TPE", "BKK", "HNL", "GUM"};
double[][] salesData = {
{2, 1, 0, 0},
{1, 0, 0, 0},
{0, 0, 3, 2},
{2, 3, 1, 0},
{1, 0, 2, 5},
};
// 相関係数行列の計算
RealMatrix corrMatrix = (new PearsonsCorrelation()).computeCorrelationMatrix(salesData);
System.out.println("相関係数行列: " + corrMatrix);
// リコメンドの実施
double[][] userSales = {{0, 1, 0, 0}};
RealMatrix userSalesMatrix = MatrixUtils.createRealMatrix(userSales);
RealMatrix scores = userSalesMatrix.multiply(corrMatrix);
for(int i = 0; i < prodname.length; i++) {
System.out.println(prodname[i] + &amp;quot;:&amp;quot; + scores.getEntry(0, i));
}
}
}
このプログラムの実行結果は以下の通りになります。
相関係数行列: BlockRealMatrix{{1.0,0.7333587976,-0.7333587976,-0.4637130168},{0.7333587976,1.0,-0.2647058824,-0.4900980294},{-0.7333587976,-0.2647058824,1.0,0.6651330399},{-0.4637130168,-0.4900980294,0.6651330399,1.0}}
TPE:0.7333587976225692
BKK:1.0
HNL:-0.26470588235294124
GUM:-0.49009802940980335
このようにして、
簡単な協調フィルタリングベースのリコメンドを実装することができました。
実運用ではmahoutなどを利用するかも知れませんが、
このような実装をして、基本的な仕組みを知っておくと、
チューニングなどを考える上で役に立つかも知れません。
参考文献:
Peter Harrington著「Machine Leaning in Action」2012年 Manning Pubns
稲垣宣生・山根芳知・吉田光雄著『統計学入門』1996年 裳華房