Python scikit-learn 機械学習

協調フィルタリングによるレコメンドシステムを実装する

この記事は約7分で読めます。

 

今回は業務で協調フィルタリング(ユーザーベース)を用いたレコメンドロジックをPythonで実装した際のメモになります。(ユーザーベースとアイテムベースの違いは以下参照)

 

関連記事:【Python】レコメンドでよく使われる機械学習アルゴリズムの一覧とコード実装

 

使用するデータセットはKaggleに用意されているレコメンド用のアニメ視聴データです。簡単なロジックの仕組みとしてはメモリベースでユーザーの視聴履歴からコサイン類似度を計算し、似たようなユーザーを抽出してその評価数値から上位のものをレコメンドするというユーザーベースのレコメンドロジックになります。

 

データセット→ Anime Recommendations Database | Kaggle

 

前処理

 

データの読み込み

 

まずはデータを読み込みます。

 

import numpy as np

import pandas as pd

df_rating = pd.read_csv(r"C:\Users\~~\archive\rating.csv")

df_label = pd.read_csv(r"C:\Users\~~\archive\anime.csv")

# 量が多いので500000件以上レビューがある人気アニメに絞る

df_label =df_label[df_label['members']>500000]

 

今回は実装するだけなので雑に足切りしましたが、本来は検証しつつ足切りのラインは決めた方がいいです。

 

anime_idをキーにしてデータセットを内部結合する

 

# anime_idをキーにして内部結合する
df = pd.merge(df_rating, df_label, on='anime_id', how='inner')[['user_id','anime_id','name','rating_x']]

 

↓こんな感じになっています。

 

f:id:oruka199665:20220325141912p:plain

 

ダミー変数のマートを作る

 

次に協調フィルタリングで用いる各ユーザーのコサイン類似度やユークリッド距離の計算するために必要なマートを作成します。具体的にはユーザーID×アニメタイトルの行列データを作成します。

 

# ユーザーID×アニメタイトル
users_movies = df.pivot_table(index="user_id",columns="name",values="rating_x")

 

変数 users_moviesの中身は↓のようになっています。

 

f:id:oruka199665:20220325142457p:plain

 

ですが、このままだと欠損値があり計算ができないので、0埋めします。また数値の大きさを統一するために正規化を行います。

 

#欠損値を0埋めする
users_movies = users_movies.fillna(0)

#正規化
users_movies_norm = users_movies.apply(lambda x:(x-np.mean(x))/(np.max(x)-np.min(x)),axis=1)
users_movies_norm = users_movies_norm.fillna(0)

 

これでusers_movies_norm の中身は↓のようになっています。

 

f:id:oruka199665:20220325152547p:plain

 

コサイン類似度を計算する

 

# コサイン類似度を計算
from scipy.sparse import csr_matrix
from sklearn.metrics.pairwise import cosine_similarity

users_movies_sparse = csr_matrix(users_movies_norm.values)
user_sim = cosine_similarity(users_movies_sparse)
user_sim_df = pd.DataFrame(user_sim,index=users_movies_norm.index,columns=users_movies_norm.index)



(予備:#スペックが足りない場合はインデックスで読み込むデータを一部のみにする)
from scipy.sparse import csr_matrix
from sklearn.metrics.pairwise import cosine_similarity
users_movies_sparse = csr_matrix(users_movies_norm.iloc[:20000].values)
user_sim = cosine_similarity(users_movies_sparse)
user_sim_df = pd.DataFrame(user_sim,index=users_movies_norm.iloc[:20000].index,columns=users_movies_norm.iloc[:20000].index)

 

 

1列目のユーザーに対する類似ユーザー上位100人を抽出する

 

# ユーザーID1と類似度の高いユーザー上位100人を抽出する
sim_users = user_sim_df.iloc[0,:].sort_values(ascending=False)[0:100]
print(sim_users)

# ユーザーのインデックスをリストに変換
sim_users_list = sim_users.index.tolist()
print(sim_users_list)

 

類似ユーザーの視聴データのみのmartを作る

 

sim_df = pd.DataFrame()
count = 0
# 類似度の高い上位ユーザーのスコア情報を集めてデータフレームに格納
for i in users_movies_norm.iloc[:,0].index:
    if i in sim_users_list:
        sim_df = pd.concat([sim_df,pd.DataFrame(users_movies_norm.iloc[count]).T])
    count += 1

 

本来データフレームをfor文で回すのはあまり良い手法ではないので、where系の関数を用いて処理した方がいいと思います。

 

対象ユーザーの視聴済みアニメを抽出して計算から除外する

 

# ユーザーID1の視聴済みアニメを取得する
user_list = users_movies[users_movies.index==1].T
user_list = user_list[user_list[1]!=0].T.columns.tolist()

# 未視聴のアニメリストを作る
result = list(set(users_movies_norm.columns) - set(user_list))

# 未視聴のアニメのスコアデータ
users_movies_norm = users_movies_norm[result]

#各アニメ評点の平均をとる
score = []
for i in range(len(users_movies_norm.columns)):
    mean_score = users_movies_norm.iloc[:,i].mean()
    name = users_movies_norm.iloc[:,i].name
    score.append([name, mean_score])

# 集計結果からスコアの高い順にソートする
score_data = pd.DataFrame(score).sort_values(1)

 

 

 

ソードアートオンライン2、ブリーチ、フェアリーテイル辺りをオススメすればよいという結果になります。

 

 

・googlecolabのリンク

Google Colaboratory

 

 

 


プログラミング・スクレイピングツール作成の相談を受け付けています!

クラウドワークス・ココナラ・MENTAなどでPython・SQL・GASなどのプログラミングに関する相談やツール作成などを承っております!

過去の案件事例:

  • Twitter・インスタグラムの自動化ツール作成
  • ウェブサイトのスクレイピングサポート
  • ダッシュボード・サイト作成
  • データエンジニア転職相談

これまでの案件例を見る

キャリア相談もお気軽に!文系学部卒からエンジニア・データサイエンティストへの転職経験をもとに、未経験者がどう進むべきかのアドバイスを提供します。


スポンサーリンク
/* プログラミング速報関連記事一覧表示 */
ミナピピンの研究室

コメント

  1. […] 前回記事:【Python】協調フィルタリングでアニメのレコメンドシステムを実装する(ユーザーベース) […]

タイトルとURLをコピーしました