目的
機械学習・レコメンデーション
やりたいこと
対象のユーザーが好きそうなアニメを予測して、おすすめする
最終的に欲しいアウトプットイメージ
- userAは、●●(アニメ名)がおすすめ
- この予測モデルの精度は、▲▲(評価指標)
実装手順
- ライブラリのインストール、インポート
- データのフィルター分け
- 予測モデルの作成
- Content filtering
- Collaborative filtering
実装コード
# google colaboratoryで実装するため、googleDriveとマウントする
drive.mount('/content/drive')
# レコメンデーションに必要なライブラリをインストールする
!pip install surprise
# 必要なライブラリをインポート
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.feature_extraction.text import TfidfVectorizer,
CountVectorizer
from surprise import Reader, Dataset, SVD
from surprise.model_selection import cross_validate
import warnings; warnings.simplefilter('ignore')
# データをインポート
anime_info_df = pd.read_csv
anime_desc_df = pd.read_csv
rating_df = pd.read_csv
# 2つのDataframeを結合
anime_df = pd.merge(anime_desc_df,anime_info_df
[['MAL_ID','Type','Popularity','Members','Favorites']],on='MAL_ID')
anime_df.head(10)
# 各列の中身を整理
for column in anime_df.columns:
print(column, ':')
# MAL_ID : アニメ名ID
# Name : アニメ名
# Score : 評価
# Genres : ジャンル
# sypnopsis : あらすじ
# Type : 放送媒体
# Popularity : 人気順位
# Members : 評価した人数
# Favorites : 高評価の数
# dataframeの形状を確認
anime_df.info()
# 評価の統計情報を確認
anime_df['Score'].describe()
# 評価列が不明のものを除く
anime_df = anime_df[(anime_df["Score"] != "Unknown")]
anime_df.shape
# あらすじがないものは空白で埋める
anime_df['sypnopsis'] = anime_df['sypnopsis'].fillna('')
# 2つの単語の組で文章を分割
tfidf = TfidfVectorizer(analyzer='word',ngram_range=(1, 2),min_df=0,
stop_words='english')
# データを正規化
tfidf_matrix = tfidf.fit_transform(anime_df['sypnopsis'])
# 正規化したデータの形状を確認
tfidf_matrix.shape
# 線形カーネルを適用
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)
cosine_sim.shape
# アニメ名と番号だけのSeriesを生成
anime_df = anime_df.reset_index()
titles = anime_df['Name']
indices = pd.Series(anime_df.index, index=anime_df['Name'])
# コンテンツの類似度からレコメンデーションモデルを作成
def content_recommendations(title):
idx = indices[title]
sim_scores = list(enumerate(cosine_sim[idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores = sim_scores[1:31]
anime_indices = [i[0] for i in sim_scores]
anime_lst = anime_df.iloc[anime_indices][['Name', 'Members', 'Score']]
favorite_count = anime_lst[anime_lst['Members'].notnull()]
['Members'].astype('int')
['Score'].astype('float')
C = score_avg.mean()
m = favorite_count.quantile(0.60)
qualified = anime_lst[(anime_lst['Members'] >= m) &
(anime_lst['Members'].notnull()) & (anime_lst['Score'].notnull())]
qualified['Members'] = qualified['Members'].astype('int')
qualified['Score'] = qualified['Score'].astype('float')
def weighted_rating(x):
v = x['Members']
R = x['Score']
return (v/(v+m) * R) + (m/(m+v) * C)
qualified['wr'] = qualified.apply(weighted_rating, axis=1)
qualified = qualified.sort_values('wr', ascending=False).head(10)
return qualified
# 「ナルト」と類似度が高いTOP10の作品を表示
# 評価Dataframeの中身を確認
rating_df.head(10)
# 評価列の値の数を確認
# 評価が-1を除く
rating_df = rating_df[(rating_df["rating"] != -1)]
rating_df.head(5)
# レコメンデーションモデルを作成できるようにデータを変換
reader = Reader()
rating_data = Dataset.load_from_df(rating_df, reader)
svd = SVD()
trainset = rating_data.build_full_trainset()
svd.fit(trainset)
# モデルを使って予測
svd.predict(1, 356, 5)
実行結果
Content filtering
Collaborative filtering
Prediction(uid=1, iid=356, r_ui=5, est=5, details={'was_impossible': False})
まとめ
おすすめのアニメを抽出する方法としては、
- 作品の
- ユーザーの評価データから
類似度を計算して、予測するものに分かれる。
作品同士の類似度は変わらないので、実装難易度として低い。
が、どのユーザーにも同じ作品をおすすめすることになるので、おすすめの精度としては低い。
実装の難易度と学習精度は必ずしも比例しないが、多くの場合手を加えれば加えるだけ精度が向上すると言えるような気がする。