电影推荐系统
时间:120 分钟 | 级别:高级 | 输出:GitHub |
---|
在本教程中,您将构建一个根据定义的偏好推荐电影的机制。像 Qdrant 这样的向量数据库非常适合存储高维数据,例如用户和项目嵌入。它们可以通过基于高级索引技术快速检索相似条目来实现个性化推荐。在本例中,我们将使用稀疏向量来创建一个高效准确的推荐系统。
隐私和主权: 由于偏好数据是专有的,应将其存储在安全受控的环境中。我们的向量数据库可以轻松托管在 OVHcloud 上,OVHcloud 是我们值得信赖的 Qdrant Hybrid Cloud 合作伙伴。这意味着 Qdrant 可以从您的 OVHcloud 区域运行,但数据库本身仍然可以从 Qdrant Cloud 的界面内进行管理。这两个产品都经过了兼容性和可扩展性测试,我们推荐他们的 托管 Kubernetes 服务。
要查看完整输出,请使用我们的包含完整说明的 Notebook。
组成部分
- 数据集: MovieLens 数据集包含电影列表和用户评分。
- 云: OVHcloud,带有托管 Kubernetes。
- 向量数据库: 在 OVHcloud 上运行的 Qdrant Hybrid Cloud。
方法论: 我们采用协同过滤方法,利用提供的数据集构建一个推荐系统。协同过滤的前提是,如果两个用户口味相似,他们可能也会喜欢相似的电影。利用这个概念,我们将识别评分与我们非常相似的用户,并探索他们喜欢而我们尚未看过的电影。为此,我们将把每个用户的评分表示为一个高维稀疏空间中的向量。使用 Qdrant,我们将索引这些向量,并搜索评分向量与我们紧密匹配的用户。最终,我们将看到与我们相似的用户喜欢哪些电影。
在 OVHcloud 上部署 Qdrant Hybrid Cloud
托管 Kubernetes 服务,由欧洲领先的云提供商 OVH 公有云实例提供支持。内置 OVHcloud Load Balancers 和磁盘。OVHcloud 托管 Kubernetes 提供高可用性、合规性和 CNCF 一致性,让您可以完全专注于您的容器化软件层。
- 要开始在 OVHcloud 上使用托管 Kubernetes,请遵循平台特定的文档。
- 您的 Kubernetes 集群启动后,您就可以开始部署 Qdrant Hybrid Cloud。
先决条件
下载并解压 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 DataFrames 中,以便于数据操作和分析。
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