ぺーぱーの日々

上機嫌でいること、夢中でいることを目標に、今日も色んなことに手を出します。

MENU

機械学習で好みのアニメをおすすめ

目的

機械学習・レコメンデーション

やりたいこと

対象のユーザーが好きそうなアニメを予測して、おすすめする

最終的に欲しいアウトプットイメージ

  • userAは、●●(アニメ名)がおすすめ
  • この予測モデルの精度は、▲▲(評価指標)

実装手順

  1. ライブラリのインストール、インポート
  2. データのフィルター分け
  3. 予測モデルの作成
    1. Content filtering
    2. Collaborative filtering

実装コード

# google colaboratoryで実装するため、googleDriveとマウントする
from google.colab import drive
drive.mount('/content/drive')

# レコメンデーションに必要なライブラリをインストールする
!pip install surprise

# 必要なライブラリをインポート
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.sparse import csr_matrix
from sklearn.feature_extraction.text import TfidfVectorizer,
CountVectorizer
from sklearn.metrics.pairwise import linear_kernel, cosine_similarity
from surprise import Reader, Dataset, SVD
from surprise.model_selection import cross_validate

import warnings; warnings.simplefilter('ignore')

# データをインポート
anime_info_df = pd.read_csv
('/content/drive/MyDrive/kaggle/Anime-Recommender/anime.csv')
anime_desc_df = pd.read_csv
('/content/drive/MyDrive/kaggle/Anime-Recommender/anime_with_synopsis.csv')
rating_df = pd.read_csv
('/content/drive/MyDrive/kaggle/Anime-Recommender/rating.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_avg = anime_lst[anime_lst['Score'].notnull()]
['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の作品を表示
content_recommendations('Naruto').head(10)

# 評価Dataframeの中身を確認
rating_df.head(10)

# 評価列の値の数を確認
rating_df['rating'].value_counts()

# 評価が-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

Narutoと類似度の高い作品TOP10

Death Noteと類似度の高い作品TOP10
Collaborative filtering

Prediction(uid=1, iid=356, r_ui=5, est=5, details={'was_impossible': False})

まとめ

おすすめのアニメを抽出する方法としては、

  • 作品の
  • ユーザーの評価データから

類似度を計算して、予測するものに分かれる。

作品同士の類似度は変わらないので、実装難易度として低い。

が、どのユーザーにも同じ作品をおすすめすることになるので、おすすめの精度としては低い。

実装の難易度と学習精度は必ずしも比例しないが、多くの場合手を加えれば加えるだけ精度が向上すると言えるような気がする。