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

概要

この記事では、内容ベースフィルタリングについて解説する。内容ベースフィルタリングの定義や性質、応用例について数式と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 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 TfidfTransformer
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"]
)

# ジャンルの二値特徴量をTF-IDFで重み付け
movie_genres = movies_df.iloc[:, 6:]
tfidf = TfidfTransformer()
tfidf_matrix = tfidf.fit_transform(movie_genres)
feature_names = movie_genres.columns.to_numpy()

print("TFIDF Matrix Shape:", tfidf_matrix.shape)
print("Feature Names:", feature_names)
TFIDF Matrix Shape: (1682, 18)
Feature Names: ['Action' 'Adventure' 'Animation' "Children's" 'Comedy' 'Crime'
 'Documentary' 'Drama' 'Fantasy' 'Film-Noir' 'Horror' 'Musical' 'Mystery'
 'Romance' 'Sci-Fi' 'Thriller' 'War' 'Western']
# user_1が高く評価した映画の行番号を取得
user_1_ratings = ratings_df[ratings_df["user_id"] == 1]
liked_movie_indices = user_1_ratings.loc[user_1_ratings["rating"] >= 4, "movie_id"].to_numpy() - 1


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


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

# 評価済み映画を推薦対象から除外
seen_movie_indices = user_1_ratings["movie_id"].to_numpy() - 1
similarities[0, seen_movie_indices] = -np.inf

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

print(recommended_movies[["movie_id", "title"]])

結果の解釈

類似度が高い映画をユーザーに推薦する。ここでは、ユーザー1に対して類似度の高い映画を10件表示する。

結論

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

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

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