推薦システムについて調べてみた

<副題>KDD Cup2012への道

KDD Track1の訳はこちらから。
http://satomacoto.blogspot.jp/2012/03/kdd-cup-2012-track-1.html

世の中の推薦システムには,協調フィルタリング
コンテンツベースフィルタリングの2種類が主にあるようです.

こちらの修士論文に先行研究がまとめられています.
http://inet-lab.naist.jp/publication/m-gu3.pdf

絵で見るとわかりやすいです.

(1)コンテンツベースフィルタリング


(図の出展:評価付けの重みを考慮した協調フィルタリング手法の提案と評価 - 奈良先端科学技術大学院大学 修士論文
http://inet-lab.naist.jp/publication/m-gu3.pdf )

まずユーザの嗜好を表すプロファイルベクトルを用意します.
プロファイルベクトルの中身は嗜好を表す内容です.
(上の図だと.席.ジャンル数.予算,休日)
そして,ユーザプロファイルベクトルと店が有するプロファイルベクトルとの類似度を計算します.

ではpythonで遊んでみます.

contentbased.py

#! /usr/bin/env python                                                          
# -*- coding: utf-8 -*-                                                         

from numpy import *

pv = array([[1,2,1,0]])
#print pv                                                                       

cv = matrix([[0,1,1,0,1],
           [0,0,4,0,2],
           [1,0,3,0,1],
           [3,1,0,0,1]] )
ip = dot(pv,cv)
shop = ["shop1","shop2","shop3","shop4","shop5"]
z = zip(shop,ip.tolist()[0])
result =  sorted(z,key=lambda x:x[1],reverse=True)
print "あなたへのおすすめの店は..."
for i in range(len(result)):
    print "第",str(i+1),"位",result[i][0],":","コサインによる類似度",result[i][1]


(2)協調フィルタリング


(図の出展:評価付けの重みを考慮した協調フィルタリング手法の提案と評価 - 奈良先端科学技術大学院大学 修士論文
http://inet-lab.naist.jp/publication/m-gu3.pdf )

こちらはコンテンツベースと打って変わって,意味内容を取り扱いません.
代わりに用いる判断指標は他のユーザの挙動です.
(例えば好き,嫌いを5点評価でアイテムに与える)
そして評価の付け方が類似しているユーザを見つけて,
次も彼と同じ挙動を示すだろうという信念に基づいて推薦を行います.
協調フィルタリングにはメモリベースとモデルベースの2種類があり,
適応度,計算速度においてトレードオフがあります.

いずれの方法もメタデータを数値で表現するのですが,
協調フィルタリングは意味内容をダイレクトに扱っていない点で
スマートな印象を個人的に受けます.

協調フィルタリングも同じく実装を行います,
今回はメモリベースのGroupLensアルゴリズムを利用しました.
GroupLensのアルゴリズムは以下です.

 r_{AB}=\frac{\sum_i (A_i - \bar A) (B_i - \bar B)}   {\sqrt{\sum_i (A_i - \bar A)^2} \sqrt{\sum_i (B_i - \bar B)^2}}
 r_{AB} : ユーザ A とユーザ B の類似度
 A_i: ユーザ A の対象 i に対する評価値
 \bar A : ユーザ A の対象に対する評価の平均

collaborative.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from numpy import *
import math

#二重丸を2点,丸を1点,バツを-1点,評価なしは0点
user = matrix([[2,-1,0,1,-1,0],
               [-1,-1,-1,2,1,0],
               [2,0,2,1,-1,-1],
               [0,0,0,-1,1,1]  ]) 
#print user.T.tolist()[0]

#平均を格納した縦ベクトル
ave =array([average(array(user.tolist()[x])) for x in range(len(user.tolist()))])
#print ave

R = []

for x in range(len(user.tolist())):
    Rx = []
    for y in range(len(user.tolist())):
        #ローカル変数の初期化
        ab = 0
        a = 0
        b = 0
        a_sq = 0
        b_sq = 0
        #ユーザxとyの類似度
        for i in range(len(user.tolist()[0])):
            ab += (user.tolist()[x][i]-ave[x])*(user.tolist()[y][i]-ave[y])
            a_sq += (user.tolist()[x][i]-ave[x])*(user.tolist()[x][i]-ave[x])
            b_sq += (user.tolist()[y][i]-ave[y])*(user.tolist()[y][i]-ave[y])
        r_ab = ab / ( pow(a_sq,0.5) * pow(b_sq,0.5) )
        Rx.append(r_ab)
    R.append(Rx) 
R = matrix(R)
#print R.tolist()

for i in range(len(R.tolist())):
    rlist = R.tolist()[i]
    sortR =  sorted(rlist,reverse=True)
    related_user = rlist.index(sortR[1])+1
    product = sorted(user.tolist()[related_user - 1],reverse=True)[0]
    product_index = user.tolist()[related_user -1].index(product) + 1
    print "%dさんに似たユーザーは%dさんです" % ( i+1, related_user)
    print "☆☆☆ %dさんは%dの商品もいいね!と言っています ☆☆☆" % (related_user,product_index )