电影推荐系统

时间: 120 分钟级别: 高级输出: GitHub

在本教程中,您将构建一个基于定义偏好推荐电影的机制。像Qdrant这样的向量数据库非常适合存储高维数据,例如用户和物品的嵌入。它们可以通过基于高级索引技术快速检索相似条目来实现个性化推荐。在这个特定的案例中,我们将使用稀疏向量来创建一个高效且准确的推荐系统。

隐私与主权:由于偏好数据是专有的,应存储在安全且受控的环境中。我们的向量数据库可以轻松托管在OVHcloud上,这是我们值得信赖的Qdrant 混合云合作伙伴。这意味着Qdrant可以从您的OVHcloud区域运行,但数据库本身仍可以通过Qdrant Cloud的界面进行管理。这两种产品已经过兼容性和可扩展性测试,我们推荐他们的托管Kubernetes服务。

要查看完整输出,请使用我们的带有完整说明的笔记本

组件

方法论: 我们采用协同过滤的方法,从提供的数据集构建推荐系统。协同过滤的前提是,如果两个用户的品味相似,他们可能会喜欢相似的电影。利用这一概念,我们将识别出评分与我们非常接近的用户,并探索他们喜欢但我们尚未观看的电影。为此,我们将每个用户的评分表示为高维稀疏空间中的向量。使用Qdrant,我们将对这些向量进行索引,并搜索评分向量与我们非常匹配的用户。最终,我们将看到与我们相似的用户喜欢哪些电影。

在OVHcloud上部署Qdrant混合云

服务托管的 Kubernetes,由欧洲领先的云服务提供商OVH Public Cloud Instances提供支持。内置OVHcloud负载均衡器和磁盘。OVHcloud Managed Kubernetes提供高可用性、合规性和CNCF一致性,让您能够专注于容器化的软件层,并具有完全的可逆性。

  1. To start using managed Kubernetes on OVHcloud, follow the 平台特定文档.
  2. Once your Kubernetes clusters are up, 您可以开始部署 Qdrant 混合云.

先决条件

下载并解压MovieLens数据集:

mkdir -p data
wget https://files.grouplens.org/datasets/movielens/ml-1m.zip
unzip ml-1m.zip -d data

必要的 * 库使用 pip 安装,包括用于数据操作的 pandas,用于与 Qdrant 接口的 qdrant-client,以及用于管理环境变量的 *-dotenv

!pip install -U  \
    pandas  \
    qdrant-client \
    *-dotenv

.env 文件用于安全地存储敏感信息,如 Qdrant 主机 URL 和 API 密钥。

QDRANT_HOST
QDRANT_API_KEY

将所有环境变量加载到设置中:

import os
from dotenv import load_dotenv
load_dotenv('./.env')

实现

将MovieLens数据集中的数据加载到pandas DataFrame中,以便于数据操作和分析。

from qdrant_client import QdrantClient, models
import pandas as pd

加载用户数据:

users = pd.read_csv(
    'data/ml-1m/users.dat', 
    sep='::', 
    names=['user_id', 'gender', 'age', 'occupation', 'zip'],
    engine='*'
)
users.head()

添加电影:

movies = pd.read_csv(
    'data/ml-1m/movies.dat',
    sep='::', 
    names=['movie_id', 'title', 'genres'], 
    engine='*', 
    encoding='latin-1'
)
movies.head()

最后,添加评分:

ratings = pd.read_csv( 
    'data/ml-1m/ratings.dat', 
    sep='::', 
    names=['user_id', 'movie_id', 'rating', 'timestamp'], 
    engine='*'
)
ratings.head()

标准化评分

稀疏向量可以利用负值的优势,因此我们可以将评分归一化,使其均值为0,标准差为1。这种归一化确保了评分的一致性并围绕零中心化,从而实现准确的相似性计算。在这种情况下,我们可以考虑我们不喜欢的电影。

ratings.rating = (ratings.rating - ratings.rating.mean()) / ratings.rating.std()

获取结果:

ratings.head()

数据准备

现在你将把用户评分转换为稀疏向量,其中每个向量代表不同电影的评分。这一步为在Qdrant中索引数据做准备。

首先,创建一个配置了稀疏向量的集合。对于稀疏向量,您不需要指定维度,因为它会自动从数据中提取。

from collections import defaultdict

user_sparse_vectors = defaultdict(lambda: {"values": [], "indices": []})

for row in ratings.itertuples():
    user_sparse_vectors[row.user_id]["values"].append(row.rating)
    user_sparse_vectors[row.user_id]["indices"].append(row.movie_id)

连接到Qdrant并创建一个名为movielens的集合:

client = QdrantClient(
    url = os.getenv("QDRANT_HOST"),
    api_key = os.getenv("QDRANT_API_KEY")
)

client.create_collection(
    "movielens",
    vectors_config={},
    sparse_vectors_config={
        "ratings": models.SparseVectorParams()
    }
)

将用户评分上传到Qdrant中的movielens集合作为稀疏向量,同时上传用户元数据。此步骤为生成推荐填充数据库所需的数据。

def data_generator():
    for user in users.itertuples():
        yield models.PointStruct(
            id=user.user_id,
            vector={
                "ratings": user_sparse_vectors[user.user_id]
            },
            payload=user._asdict()
        )

client.upload_points(
    "movielens",
    data_generator()
)

推荐

个人电影评分被指定,其中正面评分表示喜欢,负面评分表示不喜欢。这些评分作为寻找具有相似品味的用户的基础。

个人评分被转换为适合查询Qdrant的稀疏向量表示。该向量表示用户对不同电影的偏好。

让我们尝试为自己推荐一些东西:

1 = Like
-1 = dislike
# Search with movies[movies.title.str.contains("Matrix", case=False)].

my_ratings = { 
    2571: 1,  # Matrix
    329: 1,   # Star Trek
    260: 1,   # Star Wars
    2288: -1, # The Thing
    1: 1,     # Toy Story
    1721: -1, # Titanic
    296: -1,  # Pulp Fiction
    356: 1,   # Forrest Gump
    2116: 1,  # Lord of the Rings
    1291: -1, # Indiana Jones
    1036: -1  # Die Hard
}

inverse_ratings = {k: -v for k, v in my_ratings.items()}

def to_vector(ratings):
    vector = models.SparseVector(
        values=[],
        indices=[]
    )
    for movie_id, rating in ratings.items():
        vector.values.append(rating)
        vector.indices.append(movie_id)
    return vector

查询Qdrant以根据提供的个人评分找到具有相似品味的用户。搜索返回一个相似用户列表及其评分,便于协同过滤。

results = client.query_points(
    "movielens",
    query=to_vector(my_ratings),
    using="ratings",
    with_vectors=True, # We will use those to find new movies
    limit=20
).points

电影评分是根据每部电影在相似用户评分中出现的频率计算的,并按其评分加权。此步骤识别出在具有相似品味的用户中受欢迎的电影。计算每部电影在相似用户评分中出现的频率

def results_to_scores(results):
    movie_scores = defaultdict(lambda: 0)

    for user in results:
        user_scores = user.vector['ratings']
        for idx, rating in zip(user_scores.indices, user_scores.values):
            if idx in my_ratings:
                continue
            movie_scores[idx] += rating

    return movie_scores

评分最高的电影根据其评分进行排序,并作为推荐打印给用户。这些推荐是根据用户的偏好量身定制的,并与他们的口味相符。按评分排序电影并打印前五名:

movie_scores = results_to_scores(results)
top_movies = sorted(movie_scores.items(), key=lambda x: x[1], reverse=True)

for movie_id, score in top_movies[:5]:
    print(movies[movies.movie_id == movie_id].title.values[0], score)

结果:

Star Wars: Episode V - The Empire Strikes Back (1980) 20.02387858
Star Wars: Episode VI - Return of the Jedi (1983) 16.443184379999998
Princess Bride, The (1987) 15.840068229999996
Raiders of the Lost Ark (1981) 14.94489462
Sixth Sense, The (1999) 14.570322149999999
这个页面有用吗?

感谢您的反馈!🙏

我们很抱歉听到这个消息。😔 你可以在GitHub上编辑这个页面,或者创建一个GitHub问题。