LoginSignup
5
6

More than 1 year has passed since last update.

Pythonでアキネイター方式のレコメンドシステムを作る

Posted at

リコメンドシステムについて思うこと

僕はよくYouTube・アマゾンプライムビデオ・Netflixなどの動画配信サービスで動画を見るのですが、
トップページには僕の視聴履歴からAIが作ったおすすめの動画一覧があって、
大体その中から選んで視聴します。
このおすすめに従って動画視聴し続けていると
似たような動画ばっか見てきて飽きたなと感じた時には、
もうおすすめには似たような動画しかなくなってしまっています。

僕の願い

今までの視聴履歴とか関係なしにもっと今の気分を汲んでおすすめしてほしい

ランプのおじさんならこの願いを叶えてくれる

みなさんはアキネイターというサービスを知っていますか。

アキネイターのTOP

アキネイターはユーザーがある人物を思い浮かべてから、
アキネイターさんからの「男ですか?」など質問に、
「はい」「いいえ」「部分的に」などを選択して回答を繰り返していくと
思い浮かべている人物を当ててくれるというものです。

これをリコメンドに使って「アクションの気分ですか?」とかの質問に答えていったら、
今の気分にピッタリの動画に辿りつけるのではないだろうか

アキネイターを作るための決定木

実際のアキネイターのアルゴリズムはかなり複雑ですが、
今回は簡単に質問にYes・Noで答えていって一つの動画に辿り着く感じにします。
例えば選択肢の動画が

  • VTuberのゲーム実況
  • VTuberの料理動画
  • VTuberによる解説動画
  • YouTuberによる料理動画
    の4つだとすると

こんな感じの決定木が見えてきます。
選択肢の動画が4つの時は
$$a^2 \leqq 4 $$
となるaが4つの選択肢から選ぶ時に質問する回数として最も少ない数になります。
なので $$ a = log_24 = 2 $$
となって上の決定木のように2回の質問で動画をおすすめできるのが今回の例での最適な決定木になります。

質問の順番を変えると

このように動画をおすすめするまでに最長で3回の質問が必要になってしまいます。
これではリコメンドシステムとしての効率が悪いので、
最小の質問回数ですむ深さが最も少ない決定木を作る必要があります。

アルゴリズムを考える

深さが最も少ない決定木を作るには純度が低い質問からしていく必要があります。
純度は高いほど選択肢の割合が偏った状態になっていることを示します。
ある質問の回答がYesときに残る選択肢が、
YesとNoで綺麗に半分づつ分かれるとそれは純度が0の質問となります。
(一般的には決定木は不純度を交差エントロピーやジニ係数で求めてその値で条件分岐を作ります。)

recommend.py
import pandas as pd
import numpy as np

def recommend(data):
  # 純度 = abs(回答がYesになる選択肢の数 - 選択肢数/2)
  purity = (data.iloc[:, 1:].sum() - np.full(data.shape[1]-1, data.shape[0]/2)).abs()
  
  selected_column = purity.idxmin()
  print(f'見たいのは{selected_column}ですか?')
  choice = input()
  if choice == 'y':
    selected_data = data[data[selected_column]]
  else:
    selected_data = data[~data[selected_column]]
  selected_data = selected_data.reset_index(drop=True)
  if selected_data.shape[0] == 1:
    print(f'あなたにおすすめの動画は {selected_data.iloc[0, 0]} です')
  else:
    recommend(selected_data)

data = [{'タイトル': 'VTuberのゲーム実況', 'ライブ配信': True, 'VTuber': True, 'ゲーム実況': True},
        {'タイトル': 'VTuberの料理配信', 'ライブ配信': True, 'VTuber': True, 'ゲーム実況': False},
        {'タイトル': 'VTuberによる解説動画', 'ライブ配信': False, 'VTuber': True, 'ゲーム実況': False},
        {'タイトル': 'YouTuberによる料理配信', 'ライブ配信': False, 'VTuber': False, 'ゲーム実況': False}]
df = pd.DataFrame(data)
recommend(df)
# 見たいのはライブ配信ですか?
# y
# 見たいのはゲーム実況ですか?
# y
# あなたにおすすめの動画は VTuberのゲーム実況 です

さいごに

シンプルに実装するだけなら意外と簡単に実装できました。
たぶん本家のアキネイターは頻度が高い人物が当てられる質問を最初の方に持ってきたり、
いろいろな工夫がなされているので、そういうのも参考にしてみたいですね。

参考文献

前に書いた記事

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6