📘

多様性の観点から検索/推薦モデルをオフライン評価する

2022/07/22に公開

多様性指向評価とは

背景

検索/推薦の精度評価には、accuracy-baseなもの(精度、正解率、ランキングなど)が評価としてよく使われます。NetflixやSpotifyなどは、ユーザの好みを開拓するレコメンドを評価するため、多様性、新規性、意外性などの観点からも評価すべきと述べています。本記事では、多様性、新規性、意外性の3つの指標を紹介します。

前提条件

prefs(i):コンテンツiを消費した人数

prefs(i,j):コンテンツij両方を消費した人数

U:ユーザーの集合

R_{u}:ユーザーuに推薦するアイテムの集合

H_{u}:ユーザーuが消費したアイテムの集合

多様性:Diversity

内容の似ていないアイテムどうしがレコメンドされている状態を、多様性と定義します。

diversity = \frac{1}{|U|}\sum_{u\subseteq U}\sum_{i,j\subseteq R_{u}, i<j}\frac{\sqrt{prefs(i)}\times \sqrt{prefs(j)}}{prefs(i,j)}

新規性:Novelty

そのアイテムを消費したユーザーの少なさを、新規性として定義します。

novelty = \frac{1}{|U|}\sum_{u\subseteq U}\sum_{i\subseteq R_{u}}\frac{\log_2 \frac{|U|}{prefs(i)}}{|R_{u}|}

意外性:Serendipity

過去にユーザーが消費したアイテムと、レコメンドアイテムが似通っていない状態を、意外性と定義します。

serendipity = \frac{1}{|U|}\sum_{u\subseteq U}\frac{1}{|H_{u}|}\sum_{h\subseteq H_{u}} \sum_{i\subseteq R_{u}} \frac{\sqrt{prefs(i)}\times \sqrt{prefs(h)}}{prefs(i,h)}

注意点

多様性指向評価については、ネットで調べると複数の定義が出てきますが、ユーザの好み(prefs)を考慮するものが現在は優勢であると考えられています。本記事でもユーザの好みを考慮した定義を採用しています。

多様性指向評価の実装

prefs関数の定義

def prefs(item_id):
  return len(dataset[dataset['item_id']==item_id])

def prefs_both(item_id1, item_id2):
  df_both = dataset[
    (dataset['item_id']==item_id1) | 
    (dataset['item_id']==item_id2)
  ]
  series_user = df_both['user_id'].value_counts()
  return series_user[series_user==2].count()

# 高速化のため、Dictionaryにする
prefs_dict = {}
for item_id in uq_items:
  prefs_dict[item_id] = prefs(item_id)

ここで、消費データ(H)と推薦データ(R)については

dataset:過去の消費データ[dataframe:"user_id","item_id"]

df_recommend_list:推薦データ[dataframe:"user_id","item_id"]

とします。

ユーザーごとの多様性の計算

def diversity_user(user_id):
  diversity_score = 0.0
  rec_list_user = \
    df_recommend_list[
      df_recommend_list['user_id']==user_id
    ]['item_id']
  for x1, x2 in list(
    itertools.combinations(rec_list_user, 2)
  ):
    pref_both = prefs_both(x1, x2)
    if pref_both != 0:
      diversity_score += \
        np.sqrt(prefs_dict[x1]) * \
        np.sqrt(prefs_dict[x2]) / pref_both
  return diversity_score

ユーザーごとの新規性の計算

def novelty_user(user_id):
  novelty_score = 0.0
  rec_list_user = df_recommend_list[
    df_recommend_list['user_id']==user_id
  ]['item_id']
  for rec_item in rec_list_user:
    pref = prefs(rec_item)
    if pref != 0:
      novelty_score += np.log2(n_users/pref) / topk
  return novelty_score

ユーザーごとの意外性の計算

def serendipity_user(user_id):
  serendipity_score = 0.0
  rec_list_user = df_recommend_list[
    df_recommend_list['user_id']==user_id
  ]['item_id']
  con_list_user = dataset[
    dataset['user_id']==user_id
  ]['item_id']
  for rec_item, con_item in list(
    itertools.product(rec_list_user, con_list_user)
  ):
    pref_both = prefs_both(rec_item, con_item)
    if pref_both != 0:
      serendipity_score += \
        np.sqrt(prefs_dict[rec_item]) * \
        np.sqrt(prefs_dict[con_item]) / pref_both
  return serendipity_score / len(con_list_user)

多様性指向評価の計算

def calc_diversity_oriented_score():
  #初期化
  diversity = 0.0
  novelty = 0.0
  serendipity = 0.0
  
  # ユーザーごとのスコアを算出し加算する
  for user_id in uq_users:
    diversity += diversity_user(user_id)
    novelty += novelty_user(user_id)
    serendipity += serendipity_user(user_id)
  
  # 平均を取る
  diversity /= n_users
  novelty /= n_users
  serendipity /= n_users

  return diversity, novelty, serendipity

これらの多様性指向の評価を取り入れることにより、新規作成した検索/推薦モデルについて、実サービスに出さずともバイアスの影響を受けずにオフライン評価することが可能です。

フィシルコム

Discussion