Python　機械学習を用いてアニメのレコメンド機能の作成

![ソードアートオンライン.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/385eda87-fb8d-86ae-930e-58820be153ea.jpeg)

# はじめに
「あなたにはこちらの動画もおすすめです」、「こちらの商品をご購入の方はこのような商品にも興味を持っています」といったおすすめを受けたことはありませんか？
これらには**レコメンド機能**というものが使われております。

私はアニメが好きで毎クール10本以上は見ています。その中でクール中に見ているアニメの類似作品は無いかと検索することが度々あります。プログラミングにて検索エンジンを自作できないかと思い、今回機械学習の部類の中の**K近傍法（K-nearest neighbor）というアルゴリズムを用いてアニメのレコメンド機能をPythonで実装・構築**していきたいと思います。



# 目次
[1.実行環境](#1-実行環境)
[2.データセットについて（アニメレコメンドデータベース）](#2-データセットについて（アニメレコメンドデータベース）)
[3.Pythonにて実際にレコメンド機能を実装](#3-Pythonにて実際にレコメンド機能を実装)
[4.結果の考察](#4-結果の考察)
[5.今後の展望](#5-今後の展望)
[6.終わりに](#6-終わりに)
[7.参考文献](#7-参考文献)


# 1.実行環境
Python3
ノートPC
Chrome
Google Colaboratory

# 2.データセット（アニメレコメンドデータベース）
まずは、本チュートリアルで利用するデータセットについて説明します。今回ですが、「Anime Recommendations Database　vol2（アニメお勧めデータベースvol2）」という、108,000ユーザーのアニメのレビューデータを利用します。

このデータセットですが「anime.csv」と「ratings.csv」の2つのCSVファイルにより構成されています。2つのCSVファイルの概要は下記の通りです。

>anime.csv 概要
 + anime_id  各アニメのユニークID
 + title  アニメタイトル
 + genres  アニメの属するカテゴリ
 + media  メディアタイプ（例：映画、テレビetc）
 + episodes  アニメのエピソード数
 + rating  最大10の平均レーティング
 + members  当該アニメのグループに参加するユーザー数
 + start_date　放送開始日
 + season　放送時期
 + source　原作

>ratings.csv 概要
 + user_id  ユニークユーザーID
 + anime_id  当該ユーザーがレートしたアニメID
 + rating  当該ユーザーのレーティング
 
この2つのCSVファイルを利用してレコメンド機能を構築しましょう。

データの入手方法ですが、下記のKaggleページより**会員登録後にダウロードが可能**です。下記のリンクから、「rating.csv」と「anime.csv」のダウロードを行いましょう。

[kaggle:Anime Recommendations Database vol.2](https://www.kaggle.com/datasets/noiruuuu/anime-recommendations-database-vol2/code)


# 3.Pythonにて実際にレコメンド機能を実装
ここからは実際にPythonを使って、k近傍方（k-nearest neighbor)のアルゴリズムを使用して、基本的な「協調フィルタリング」のレコメンドエンジンを構築しましょう！

必要なライブラリのインポート
まずは使うライブラリののインポートから行いましょう。ご自身の環境にこれらのライブラリがインストールされていない場合は、インストールが必要です。

```
# 使用するライブラリのインポート
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.sparse import csr_matrix
from sklearn.neighbors import NearestNeighbors
```
次に、CSVファイルをPandasのデータフレーム形式で読み込んであげましょう。CSVファイルですが[Kaggleのこちらのページ](https://www.kaggle.com/datasets/noiruuuu/anime-recommendations-database-vol2/code)からダウロードが可能です。また、ファイルを保存した先が異なる場合は、下記のコードでファイルパスも指定してあげましょう。
```
# CSVファイルをデータフレーム形式として読み込み
# 保存先が異なる場合はパスも指定してあげましょう
ratings = pd.read_csv('/content/drive/MyDrive/ratings2.csv')
anime = pd.read_csv('/content/drive/MyDrive/animes2.csv')
```

>データを確認して見ましょう

データの読み込みが完了したので、次はデータの探索をしてみましょう。
まずは、 <font color='red'>head()</font>でデータの最初の5行を表示してみましょう。
```
# ratingのデータフレームの最初の5行を表示
ratings.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/fb042be8-97de-7d59-a247-6af1654bc194.png)

```
anime.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/123c8bda-c91f-a907-531d-92057eb628e6.png)

次に、このanimeのデータフレームの members の値で並び替えをしてみましょう。この members の値ですが、各アニメの購読メンバー数となていますので、並び替えをすることにより、このデータセット内にあるアニメの中の人気順が把握できるかと思います。
```
# animeのデータフレームをmemberの値で並び替え
anime.sort_values('members', ascending= False)[:10]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/21e3ee37-6f4e-0a45-a411-a9396a95404f.png)

「デス・ノート」、「進撃の巨人」、「ソードアート・オンライン」といった作品が上位にあがってくるのが読み取れますね。

次に<font color='red'>describe()</font>の関数を使って、基本統計量を確認してみましょう。

```
# animeの基本統計量の確認
round(anime.describe(),2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/9b697030-12cc-aa73-58dc-7b378ef14922.png)

上の図の基本統計量のテーブルを見てみると、このデータの特性が見えてくるかと思います。例えば、 members の列を見て頂きたいのですが、こちらはmin（最小値）が16.0、max（最大値）が2780502となっています。最小値と最大値のレンジとしては、かなり大きく離れていますね。

また、このデータセット内のアニメの25%が525名以下の購読者数に対して、上位25%は18,000名以上の購読者数がいます。人気のアニメとそうでないアニメの差が激しいのも確認できます。


次は同様にratingsのデータフレームの基本統計量も確認してみましょう。

```
# ratingsの基本統計量の確認
round(ratings.describe(),2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/55751baa-c3c2-8692-6a26-3994aec0d0e8.png)


次は、このユーザーレーティング（アニメの評価値）のデータ分散を可視化して確認してみましょう。下記コードで、ratingsのヒストグラムの生成を行います。
```
# ratingsのヒストグラムを作成
ratings['rating'].hist(bins=11, figsize=(10,10), color = 'red')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/81cbc23b-3bcb-d23b-c77e-78b31d02b6f3.png)

>データの前処理をしよう

さて、データの一通りの確認ができたら、次は**データの前処理**を行います。データの前処理とは、機械学習で利用する前にデータの処理を行うことで、機械が学習をスムーズに行えるようにする為の作業です。

まずはじめに、これから構築するレコメンドエンジンの質を向上する為、 members （当該のアニメのグループに所属しているユーザー数）が15,000以下のデータを足切りをしましょう。足切りすることにより、これから構築するレコメンド機能が、メンバー数が少ないアニメ（つまり比較的人気の低いアニメ）をオススメしないようになります。

＊参考までにですが、今回は15,000という値を閾値（いきち）として選びましたが、この値は適当に決めた値であり、本来であれば、より詳細を確認しながら設定するものです。

```
# membersの値が15,000より大きいデータのみに変更
anime = anime[anime['members'] > 15000]
```

次は、**欠損値の処理**を行いましょう。実際のデータセットは、殆どの場合でデータが「欠損（つまり欠けている）」います。完璧に全てのデータが揃っている方が珍しいくらいです。実際に機械学習のアルゴリズムを使ってモデルを構築する前に、この欠損したデータは何かしらの処理をしなくてはいけません。
```
# 欠損データの確認
anime.isnull().sum()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/74c5745e-400d-6252-142f-a9e740b94920.png)


今回のアニメのデータセットでも、非常に少ない割合ではありますが欠損が確認できます。欠損データの扱いですが、こちらも様々な処理方法がありますが、今回は一番単純な方法・・・削除をしましょう。

```
# 欠損データをdropna()でデータセットから取り除く
anime = anime.dropna()
```

次にanimeとratingsのデータを「 **anime_id** 」を軸にして、「user_id」毎に切り分けてマージさせましょう。

```
# animeとratingsの2つのデータフレームをマージさせる
mergeddf = ratings.merge(anime, left_on = 'anime_id', right_on = 'anime_id', suffixes= ['_user', ''])
 
# 合体したデータフレームの最初の5行を表示
mergeddf.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/cd4c43c6-aae9-eb9a-1bd6-306dda9aaba6.png)

これで、2つの異なるデータフレームが、ユーザーIDごとに切り分けられた一つのデータフレームとしてマージ（合体）しました。
参考までに、このマージさせたデータフレームの基本統計量も確認しておきましょう。
```
# mergeddfの基本統計量の確認
round(mergeddf.describe(),2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/690b48d8-0028-185a-5a18-0d24f71606b5.png)

count （データの個数）も揃ってますし、特に問題なさそうですね！

さて、ここまでいじってきたデータセットですが、今回のレコメンド機能に使うデータは「 user_id 」「 title 」「 rating_user 」の3項目のみです。データの前処理の最後の項目として、使わないデータ項目の削除と重複データのカットを行いましょう。

```
#不必要な項目と重複項目を削除
mergeddf = mergeddf[['user_id','title','rating_user']]
mergeddf = mergeddf.drop_duplicates(['user_id','title'])
 
# head()で最初の5行を表示
mergeddf.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/e25e6079-6f12-96ff-83e2-fc6e277b9258.png)

さて、次の処理ですが、 title （アニメのタイトル名）がデータフレームのインデックスに、 user_id をカラムとして、ピボットさせましょう。文字で見ると、いまいち分かりにくいかも知れませんが、実際にデータフレームを処理して見てみると分かるかと思います。

またピボットの処理と併せて、ユーザーが評価していないアニメに関しては  fillna(0) で「0」の値を入れてあげましょう。

```
# データフレームのピボット
anime_pivot = mergeddf.pivot(index= 'title',columns='user_id',values='rating_user').fillna(0)
anime_pivot_sparse = csr_matrix(anime_pivot.values)
```

では、実際にピボット処理を行ったこの疎行列の最初の10行を表示させて見ましょう。
```
# anime_pivotの最初の10行を表示
anime_pivot.head(10)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/8eb6c85c-d52f-9c80-a298-6025598a5e3a.png)

先ほど説明した通り、データフレームのindex（上記表でいう一番左の部分）がアニメのタイトルになっており、各列がユーザーのIDを表しているのが分かります。

>k近傍法（k-nearest neighbors）でレコメンド機能を構築

さて、いよいよ本題のレコメンド機能の構築にうつります。今回構築するレコメンド機能ですが、**k近傍法**（ケイ・きんぼうほう）というアルゴリズムを利用してモデルを構築します。k近傍法ですが英語の「k-nearest neighbor（ケイ・ニアレスト・ネイバー）」またはその頭文字を取って「KNN」と呼ばれることも多いので、覚えておくと便利かと思います。

では、このk近傍法とは一体どのようなアルゴリズムなんでしょうか？

k近傍法ですが最も単純なアルゴリズムと呼ばれており、クラスタリングのアルゴリズムの一種で、一般的に分類問題などで利用されます。今回のチュートリアルの例で考えると、とあるアニメのタイトルを入力した際に、そのアニメのneighbors（ご近所さん）を探して、距離が近い（類似性が高い）アニメのタイトルをオススメとして返してくれます。

では、実際にこのk近傍法のアルゴリズムと事前に処理したデータセットを使ってモデルを構築してみましょう！今回はPythonの機械学習ライブラリ「Scikit-learn（またはSklearnとも呼ばれる）」を利用しましょう。

```
# Scikit-learnのライブラリを利用します
# n_neiborsやalgorithm、metricなど重要なアーギュメントを設定しています
knn = NearestNeighbors(n_neighbors=9,algorithm= 'brute', metric= 'cosine')
 
# 前処理したデータセットでモデルを訓練
model_knn = knn.fit(anime_pivot_sparse)
```
これで、モデルの構築と前処理したデータセットでの訓練が完了です！ライブラリを使うと、想像以上に簡単ですよね？では、いよいよ、この構築したアニメのレコメンド機能の実力を試してみましょう！

>構築したレコメンド機能を試してみる

まず最初に、今回のデータセットですが全てアニメタイトルが英名で記載されており、探すのに一苦労しますので、簡単な検索機能を作ってあげましょう。試しに「Deai」で検索してみます。

```
# データセットのタイトルをキーワードで検索
def searchanime(string):
    print(anime_pivot[anime_pivot.index.str.contains(string)].index[0:])
    
searchanime('Deai')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/00cc48e6-b84d-ad56-b4a4-6aa9e6c523b0.png)

タイトルに「Deai」を含むアニメの一覧が検索できました！ searchanime(”) にキーワードを入力すると、そのワードを含むアニメタイトルが戻ってきます。ご自身で色々なアニメで確認される際は、是非こちらの機能を使ってみてください。

では、いよいよ構築したレコメンド機能の実力を試してみましょう！アニメタイトルを入力すると、モデルは10個のオススメのアニメタイトルを返してくれるはずです。

まず私が個人的に好きな「ダンジョンに出会いを求めるのは間違っているだろうか（Dungeon ni Deai wo Motomeru no wa Machigatteiru Darou ka）」を見てみます。

```
# 「ダンジョンに出会いを求めるのは間違っているだろうか」に対してのオススメのアニメ10個
Anime = 'Dungeon ni Deai wo Motomeru no wa Machigatteiru Darou ka'
 
distance, indice = model_knn.kneighbors(anime_pivot.iloc[anime_pivot.index== Anime].values.reshape(1,-1),n_neighbors=11)
for i in range(0, len(distance.flatten())):
    if  i == 0:
        print('Recommendations if you like the anime {0}:\n'.format(anime_pivot[anime_pivot.index== Anime].index[0]))
    else:
        print('{0}: {1} with distance: {2}'.format(i,anime_pivot.index[indice.flatten()[i]],distance.flatten()[i]))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288878/ccd1d1df-d5cf-9af7-3efd-a93751273c87.png)


# 4.結果の考察

1位「この素晴らしい世界に祝福を」
2位「ノーゲーム・ノーライフ」
3位「オーバーロード」
4位「アカメが斬る！」
5位「Re:ゼロから始める異世界生活」
6位「ゲート 自衛隊 彼の地にて、斯く戦えり」
7位「Charlotte」
8位「ソードアートオンラインⅡ」
9位「七つの大罪」
10位「落第騎士の英雄譚」

以上の結果となりました！6作品が異世界作品が返ってきましたね。且つ異世界作品の中でも自分が大好きな「ノーゲーム・ノーライフ」「ソードアートオンラインⅡ」が返ってきました！個人的には満足のいく結果となりました。

# 5.今後の展望

今回のレコメンド機能はアニメのタイトルでおすすめのアニメを返すものでした。
他にもジャンルや年代といった様々なデータがあるのでいろいろな方法でアニメを探す検索エンジンを作成していきたいと思います。また今回使用したデータセットは[Kaggle](https://www.kaggle.com/datasets/noiruuuu/anime-recommendations-database-vol2/code)から引用してきたものですが、引用先のkaggleにはコードの投稿がほとんど無かったため、そちらにも投稿し、より良い検索エンジンが出てきたらいいなと思います。


# 6.終わりに
今回アニメの類似作品を検索するレコメンドの構築を、ネット上のコードを参考に作成しました。
初めて自分で一から触ってみて問題なく機能するコードを作ることができ、感動しております。
これを機にアニメ以外にもアーティストであったり歌であったり様々な類似検索エンジンを作成してみたいなと思いました。
またこの先AI技術がどんどん進歩していく中で、時代に置いて行かれないようプログラミングの勉強に励んで行きたいと思います。

最後までブログを読んでいただき本当にありがとうございました！

# 7.参考文献

データセット
[kaggle:Anime Recommendations Database vol.2](https://www.kaggle.com/datasets/noiruuuu/anime-recommendations-database-vol2/code)

見出し画像
https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.pinterest.jp%2Fpin%2F689543392927523844%2F&psig=AOvVaw09romR05EbV_iYNSS4huAg&ust=1679408241182000&source=images&cd=vfe&ved=0CBAQjRxqFwoTCPiAnq_Z6v0CFQAAAAAdAAAAABAE

参考記事
[機械学習を使って630万件のレビューに基づいたアニメのレコメンド機能を作ってみよう（機械学習 k近傍法 初心者向け）](https://www.codexa.net/collaborative-filtering-k-nearest-neighbor/)