レコメンデーションシステム: User-based Collaborative Filtering using N Nearest Neighbors

Ashay Pathak
Ashay Pathak

Follow

Feb 25, 2019 – 9 min read

Ashay Pathak, Chatana Mandava, Ritesh Patel

協調フィルタリングは推薦システムで広く使われている技術で、急速に進歩している研究領域である. 4087>

この記事では、メモリベースの手法である (User-Based Collaborative Filtering) UB-CF にのみ焦点を当てることにします。 UB-CFの主な考え方は、同じような特徴を持つ人々は同じような嗜好を共有しているということです。 例えば、友人のボブに映画を薦めたいと思ったとき、ボブと私は一緒に多くの映画を見たことがあり、ほとんど同じように評価したとします。

それでは、UB-CF を実装して、アクティブ ユーザーである友人 Bob が観たいと思うような映画のリストを生成してみましょう。 この記事を書く動機は、アルゴリズムに深く潜り込み、UB-CFが実際にどのように動作するかを理解することです。 4087>

ユーザー-ユーザー協調フィルタリング

この手法は、問い合わせたユーザーに類似したユーザーを識別し、これらの類似したユーザーの評価の加重平均が望ましい評価になるよう推定します。

Collaborative Filtering : source here

MovieLens Datasetで推薦を行うことになります。 プログラミング言語はpythonで、データ解析は主にpandasライブラリを使って行います。 IDE は jupyter notebook を使用しています。

始める前に、使用したライブラリのリストを提供したいと思います:

  1. Pandas
  2. Numpy
  3. sklearn

それで、前に進み、推薦の概念を理解してください。 理解を深めるために、いくつかのコードスニペットとアウトプットをブログに添付しました。 ipynb ファイル全体はブログの最後に添付されています。

スコア関数

非個人化協調フィルタリング(つまり、アクティブユーザーの過去の好き嫌いや評価を考慮しない)のために、ユーザー u とアイテム i を入力パラメータとしてスコアを返す関数を思いつくのは簡単なことです。 この関数は、ユーザー u がアイテム i をどれだけ強く好きか、または好んでいるかを定量化したスコアを出力します。

つまり、これは通常、ユーザーに似た他の人々の評価を用いて行われるのです。 これについては、後で詳しく説明します。 今のところ、私が使用した式は、

score function

ここで ‘s’ は予測スコア、 ‘u’ はユーザー、 ‘i’ はアイテム、 ‘r’ はユーザーによって与えられた評価、 ‘w’ は重み付けを表しています。

この場合、スコアは、各ユーザーがそのアイテムに付けた評価の合計から、そのユーザーの平均評価を引いたものに、このユーザーが他のユーザーの予測にどの程度似ているか、貢献していると思われるかという重みを掛けたものに等しいと言えます。 スコアは0から1の間で、0が低く、1が高い。 すべてが完璧に見えますが、なぜ各ユーザーの評価から平均評価を引いたのか、なぜ単純平均ではなく加重平均を使用したのか、問題は我々が扱っているユーザーのタイプにあります。 これは、人々がしばしば非常に異なる尺度で評価するという事実から始まります。 私はポジティブで楽観的なユーザーで、好きな映画を5点満点中4点と評価するかもしれませんが、それほど楽観的でない、あるいは高い基準を持っている他のユーザーは、彼の好きな映画を5点満点中2点と評価するかもしれません。 これを改善するために、ユーザーの評価を正規化することで、このアルゴリズムの効率を上げることができます。 その方法の 1 つは、ユーザーが各項目につける平均評価にいくつかの偏差を加えたスコアとして s(u,i) を計算し、その偏差がこの項目が平均よりどれだけ良いか悪いかを表すと言うことです。

上記の方法でデータを正規化するために、pandasでいくつかのデータ分析が必要です。 あなたは最後に全体のコードを取得することができます。

import pandas as pdmovies = pd.read_csv("movies.csv",encoding="Latin1")
Ratings = pd.read_csv("ratings.csv")
Tags = pd.read_csv("tags.csv",encoding="Latin1")Mean = Ratings.groupby(by="userId",as_index=False).mean()
Rating_avg = pd.merge(Ratings,Mean,on='userId')
Rating_avg=Rating_avg-Rating_avg
Rating_avg.head()

Normalized ratings

さて、これでユーザーの正規化された評価を計算することが終わりました。 4087>

Cosine Similarity

上記の式では、似たような考えを持っているユーザーを見つける必要があります。 好き嫌いが似ているユーザーを見つけるというのは、とても面白そうですね。

この問題に答えるために、コサイン類似度を使用して、ユーザーがどのくらい似ているかを確認します。 これは通常、両方のユーザーが過去に評価した評価に基づいて計算されます。

この例では、類似度を計算するのに sklearn の cosine_similarity 関数を使用しました。

from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')

pivot table

これは、すべてのユーザーがすべての映画を見ていないため NaN 値がいくつか多く含まれており、それがこのタイプの行列が疎行列と呼ばれる理由です。 この疎な行列を処理するために、行列分解などの方法が用いられますが、このブログでは取り上げません。

  1. Use the user average over the row.
  2. User the movie average over the column.

私は両方の方法を使用しましたので、以下のコードでそれを取得することができます。 しかし、説明のために私は映画平均法を使用します。

# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))

Replace NaN values by movie average

さて、次はユーザー間の類似度を計算することです。

# user similarity on replacing NAN by item(movie) avg
cosine = cosine_similarity(final_movie)
np.fill_diagonal(cosine, 0 )
similarity_with_movie =pd.DataFrame(cosine,index=final_movie.index)
similarity_with_movie.columns=final_user.index
similarity_with_movie.head()

User User cosine similarity

ここで、自分たちが求めたものが本当に意味があるか自己チェックしてみることにしましょう!

a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()

movies similar

上の画像から、与えられたユーザー (370,86309) がほぼ同じ評価と好感を持っているので、我々が計算した類似性が正しいことが分かります。

これでユーザー間の類似性の計算は終わりですが、私はまだ満足していません。 その理由は次のステップで説明します。

ユーザー (K) の近傍領域

以上から、すべてのユーザーについて類似度を計算したことがわかります。 しかし、ビッグデータの研究者である私は、問題の複雑さにいつも悩まされています。 4087>

映画推薦システムの例で説明すると、データには 862 人のユニークなユーザーがいるので、上で得た行列は (862*862) になります。 この数は、本来のシステムが扱うデータと比較するとまだ小さい。 Amazonについて考えてみましょう。 Amazonのデータベースには数百万人以上のユーザーが登録されているため、アイテムのスコアを計算する際に、常に他のすべてのユーザーを参照することは良い解決策や方法とは言えません。 そこで、この問題を解決するために、私たちは近傍という概念を作りました。 4087>

では、このアイデアを実装するためのさらなるステップを説明します。 この例では、k の値を 30 としました。

類似性マトリックスと n の値を入力として受け取り、すべてのユーザーの最近傍 n 人を返す、カスタム関数 find_n_neighbours を使用しました。 コードはブログの最後にあるノートブックにあります。

# top 30 neighbours for each user
sim_user_30_m = find_n_neighbours(similarity_with_movie,30)
sim_user_30_m.head()

top 30 neighbors for users

これで、不要な計算の数を大幅に削減できたと思いますか? 4087>

最終的なスコア S(u,i)

うわー!これでプロセスは終了です。 4087>

ここで、与えられたユーザーが見ていない映画のスコアを予測しようとします。

score = User_item_score(320,7371)
print(score)

predicted score

したがって、システムはスコアを 4.25 と予測し、非常に良い結果となりました。 ユーザー (370) は ID (7371) の映画を好きだと思います。

さて、私たちのシステムに最終的なタッチを与えてみましょう。 ほとんどの人がNetflixやHotstarを利用したことがあると思います。

このように、アプリを開くと、自分が気に入ったものが表示されるのですが、それが自分の好きな映画であることがわかると、ついつい見入ってしまいます。 これはすべて、レコメンドシステムそのものによるものです。

このロジックはそれほど難しくはありません。 私たちはすでに1つの項目に対してスコアを生成しました。

しかし、今再びビッグデータを考慮に入れて、他のすべての項目のスコアを生成することは意味があるのでしょうか?

私はNOOOOOだと思います!

ユーザー(U)とユーザー(K)を考えてみましょう。 ここで、K が U の近所にいないとすると、K が見て評価した映画をユーザー U が好きになることは可能でしょうか。 ほとんどNoです。

トリックがわかったような気がします。 はい、私たちはただ、その近隣のユーザーが見たことのあるアイテムのスコアを計算することに興味があるだけです。 計算を 2500 から N (N は近所の人が好きな映画のセット) に減らし、2500 よりも非常に少なくすることができました。

User_item_score1 は私のカスタム関数で、上記の議論を使用して予測を計算します

user = int(input("Enter the user id to whom you want to recommend : "))
predicted_movies = User_item_score1(user)
print(" ")
print("The Recommendations for User Id : ",user)
print(" ")
for i in predicted_movies:
print(i)

ついに !!!!!! やりましたね。

以下のコードの表現はあまり読みやすくないかもしれませんので、私の GitHub リポジトリにアクセスしてください。

必要なデータセットはここから取得できます。 何か質問があれば、私にメールを送ることができます。 これは私の興味のある分野の 1 つであり、必ず新しいものを投稿しますので、さらなるブログにご期待ください。

ありがとうございました…!

コメントを残す

メールアドレスが公開されることはありません。