それRedisでできるよ、期でしょうか。 最近Redisでレコメンド機能をつくってみたのでご紹介です。
ここで”レコメンド機能”というのは、 Amazonでいう”この商品を見たお客様はこれも見ています”や、ブログの関連記事を出す機能のこと。
user:1がproduct:Aをみたときに、product:Aに似ているproduct:Bをレコメンドしたい。 product:Aとproduct:Bがどれくらい似ているか:類似度 を算出した後は、 Redis得意のSorted Setを使って類似度のランキングをつくれば 似ているproductを出すことができます。
類似度の算出にはいろいろ方法があるようですが、 Redisのデータ構造と相性のよい Jaccard [wikipedia]という方法を使いました。
この例に適用すれば、 product:Aを見たユーザー群(RedisのSet)と、product:Bを見たユーザー群があった時に、 類似度 = (product:Aとproduct:Bを両方見た人数) / (product:Aまたはproduct:Bのいずれかを見た人数) となります。
productページを見た時に、ユーザーIDをproductのSetに追加しておけば、
1
|
|
類似度は
1
|
|
となります。
これをSorted Setに入れておきます。 AとBが似ているならばBとAも似ているので逆方向も。
1 2 |
|
1
|
|
には、product:Aに似たproductのIDが入っているので 似ている順に10件とるのはお得意の
1
|
|
さらに、ただいまなんでもluaスクリプト期でもあるので、Redisのluaスクリプティングを使いたくなる理由を考えます。
SINTERとSUNIONは、Set同士のそれぞれANDとORをとって、その結果得られるSetの要素を返してくれますが、 ここで欲しいのは要素ではなく、要素数。 残念ながら、SINTER,SUNIONした結果の要素数だけとる方法はありません。 しかも要素数は単純に割り算した後すぐにSorted SetにZADDします。 これはアプリケーション側でやらずにRedis側でやった方が速そうです。
下につけたluaスクリプトを書けばこう書けます。
1
|
|
product:A Setとproduct:B Setの類似度を計算し similarity:product:A Sorted Setにその類似度をスコアとしてproduct:Bを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
RedisのCPUがあまってるならluaスクリプティングもいいんじゃないでしょうか。 wallclock timeで10倍くらい高速ですし。
1 2 3 |
|
ベンチマークスクリプトはこちら