内容ベース協調フィルタリング

概要

この記事では、内容ベース協調フィルタリングについて解説する。内容ベース協調フィルタリングの定義や性質、応用例について数式とPythonのコードを用いて具体例を示す。

また、メリットとデメリットについても言及する。

さらに、具体例として「movielens-100k」データセットを利用した実装例を示す。

あくまでも個人的な備忘録であるので注意。

ソースコード

github

  • jupyter notebook形式のファイルはこちら

google colaboratory

  • google colaboratory で実行する場合はこちら

実行環境

OSはmacOSである。LinuxやUnixのコマンドとはオプションが異なりますので注意していただきたい。

!sw_vers
ProductName:		macOS
ProductVersion:		13.5.1
BuildVersion:		22G90
!python -V
Python 3.9.17

基本的なライブラリをインポートし watermark を利用してそのバージョンを確認しておきます。 ついでに乱数のseedの設定をします。

import random

import numpy as np
import pandas as pd

from pprint import pprint

seed = 123
random_state = 123

random.seed(seed)
np.random.seed(seed)


from watermark import watermark

print(watermark(python=True, watermark=True, iversions=True, globals_=globals()))
Python implementation: CPython
Python version       : 3.9.17
IPython version      : 8.17.2

numpy : 1.25.2
pandas: 2.0.3

Watermark: 2.4.3

内容ベース協調フィルタリングの定義

内容ベース協調フィルタリング(Content-Based Collaborative Filtering)は、アイテムやユーザーの特徴情報に基づいて推薦を行う手法である。従来の協調フィルタリングとは異なり、アイテムやユーザーのメタデータを活用し、ユーザーの過去の行動や嗜好を分析して類似するアイテムを推薦する。

数式と具体例

内容ベース協調フィルタリングでは、アイテムの特徴ベクトルを用いる。例えば、映画の推薦システムでは、映画のジャンル、出演者、監督などの情報が特徴ベクトルとなる。ユーザーの嗜好ベクトルとアイテムの特徴ベクトルの類似度を計算し、高い類似度のアイテムを推薦する。

特徴ベクトルの表現

アイテム $i$ の特徴ベクトルを $\mathbf{x}_i$、ユーザー $u$ の嗜好ベクトルを $\mathbf{y}_u$ とする。類似度計算にはコサイン類似度を用いる。

$$ \text{sim}(\mathbf{x}_i, \mathbf{y}_u) = \frac{\mathbf{x}_i \cdot \mathbf{y}_u}{|\mathbf{x}_i| |\mathbf{y}_u|} $$

ここで、$\mathbf{x}_i \cdot \mathbf{y}_u$ は内積、$|\mathbf{x}_i|$ と $|\mathbf{y}_u|$ はそれぞれのベクトルのノルムを表す。

Pythonコードによる実装例

以下に、映画推薦システムの簡単な実装例を示す。ここでは、映画の特徴ベクトルとユーザーの嗜好ベクトルを使ってコサイン類似度を計算する。

import numpy as np

from sklearn.metrics.pairwise import cosine_similarity

from pprint import pprint

# 仮の映画の特徴ベクトル
# 適当なベクトルを設定
movies = {
    "movie_1": np.array([1, 0, 1]),
    "movie_2": np.array([0, 1, 0]),
    "movie_3": np.array([1, 1, 0]),
}

# ユーザーの特徴ベクトル(嗜好ベクトル)
user_preference = np.array([1, 0, 1])

# cos類似度を計算
similarity_dict = {}
for movie, features in movies.items():
    similarity = cosine_similarity([user_preference], [features])[0][0]
    similarity_dict[movie] = round(similarity, 2)

pprint(similarity_dict)
{'movie_1': 1.0, 'movie_2': 0.0, 'movie_3': 0.5}

このコードでは、ユーザーの嗜好ベクトルと各映画の特徴ベクトルのcos類似度を計算し、類似度が高い映画を推薦する。

応用例

内容ベース協調フィルタリングは、以下のような領域で応用される。

  • 映画や音楽の推薦システム: ユーザーの視聴履歴や評価に基づいて、新しい映画や音楽を推薦する。
  • ECサイト: ユーザーの購入履歴や閲覧履歴を分析し、関連商品を推薦する。
  • ニュース記事の推薦: ユーザーの過去の閲覧履歴に基づいて、興味のあるニュース記事を推薦する。

メリットとデメリット

メリット

  • コールドスタート問題への対応: 新規アイテムでも特徴ベクトルが利用できるため、推薦が可能である。
  • ユーザーの嗜好に基づいた推薦: ユーザーの個別の嗜好を反映した推薦が可能である。

デメリット

  • 過学習のリスク: ユーザーの過去の嗜好に過度に依存すると、新しいアイテムが推薦されにくくなる。
  • 計算コスト: 特徴ベクトルの計算や類似度計算に時間がかかる場合がある。

具体例の計算

ここでは、「movielens-100k」データセットを使用して映画推薦システムを実装する。

データセットの準備

まず、「movielens-100k」データセットをロードし、映画の特徴ベクトルとユーザーの嗜好ベクトルを準備する。

import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# データセットの読み込み
movies_df = pd.read_csv(
    "./ml-100k/u.item",
    sep="|",
    encoding="latin-1",
    header=None,
    names=[
        "movie_id",
        "title",
        "release_date",
        "video_release_date",
        "IMDb_URL",
        "unknown",
        "Action",
        "Adventure",
        "Animation",
        "Children's",
        "Comedy",
        "Crime",
        "Documentary",
        "Drama",
        "Fantasy",
        "Film-Noir",
        "Horror",
        "Musical",
        "Mystery",
        "Romance",
        "Sci-Fi",
        "Thriller",
        "War",
        "Western",
    ],
)
ratings_df = pd.read_csv(
    "./ml-100k/u.data", sep="\t", encoding="latin-1", header=None, names=["user_id", "movie_id", "rating", "timestamp"]
)

# ジャンル情報を文字列として結合
movie_genres = movies_df.iloc[:, 6:]
movie_genres_str = movie_genres.apply(lambda x: " ".join(movie_genres.columns[x == 1]), axis=1)

# TFIDF vectoriaer 初期化
tfidf = TfidfVectorizer()

# TF-IDFベクトルを作成
try:
    tfidf_matrix = tfidf.fit_transform(movie_genres_str)
    print("TFIDF Matrix Shape:", tfidf_matrix.shape)

    # 特徴量の名前を表示
    feature_names = tfidf.get_feature_names_out()
    print("Feature Names:", feature_names)
except ValueError as e:
    print(e)
TFIDF Matrix Shape: (1682, 20)
Feature Names: ['action' 'adventure' 'animation' 'children' 'comedy' 'crime'
 'documentary' 'drama' 'fantasy' 'fi' 'film' 'horror' 'musical' 'mystery'
 'noir' 'romance' 'sci' 'thriller' 'war' 'western']
# ユーザーの嗜好ベクトルを作成
user_preferences = ratings_df.groupby("user_id")["movie_id"].apply(list)


# cos類似度を計算する関数
def calculate_similarity(user_pref, tfidf_matrix):
    user_vector = np.asarray(np.mean(tfidf_matrix[user_pref], axis=0))
    similarities = cosine_similarity(user_vector, tfidf_matrix)
    return similarities


# user_1 の嗜好ベクトルと映画の類似度を計算
user_1_pref = user_preferences[1]
print("user_1 preferences length:", len(user_1_pref))

# 類似度を計算
similarities = calculate_similarity(user_1_pref, tfidf_matrix)

# 類似度が高い映画を表示
similar_movies = np.argsort(similarities[0])[::-1][:10]
recommended_movies = movies_df.iloc[similar_movies]

print(recommended_movies[["movie_id", "title"]])
user_1 preferences length: 272
      movie_id                                title
3            4                    Get Shorty (1995)
73          74  Faster Pussycat! Kill! Kill! (1965)
1236      1237                       Twisted (1996)
521        522                   Down by Law (1986)
1456      1457          Love Is All There Is (1996)
1011      1012                 Private Parts (1997)
92          93      Welcome to the Dollhouse (1995)
1459      1460                     Sleepover (1995)
1271      1272             Talking About Sex (1994)
346        347                   Wag the Dog (1997)

結果の解釈

類似度が高い映画をユーザーに推薦する。例えば、ユーザー1に対して最も類似度が高い映画を表示する。

結論

この記事では、内容ベース協調フィルタリングについて詳述した。具体的な定義や数式、Pythonコードを用いた具体例を示し、メリットとデメリットを論じた。

この手法は、映画や音楽の推薦、電子商取引、ニュース記事の推薦など、さまざまな分野で応用されている。

評価手法やハイパラ調整など他にやることはたくさんあるが、あくまでも個人的なメモとして残しておく。