dot Redis 8 来了 — 而且是开源的

了解更多

什么是基于内容的过滤?构建推荐系统指南

用户喜欢选项,但过多的选项可能导致选择困难症。 

这就是推荐系统的用武之地。这些工具已经取得了长足的进步,使企业更容易提供丰富的选项而不会让用户感到不知所措。这是两全其美:既有多样性,又不会造成决策疲劳。

让我们来详细了解基于内容的过滤(它是许多类型推荐系统的核心部分),解释其工作原理的数据科学技术,深入探讨其优点和缺点,并通过一个教程向您展示如何使用 Redis 构建个性化推荐。 您可以在此处克隆此仓库

什么是基于内容的过滤?

基于内容的过滤是一种推荐技术,它利用机器学习根据项目的特征(即内容)向用户推荐项目。使用基于内容的过滤的推荐系统会分析项目特征和用户偏好,以构建用户画像,系统可以将用户画像与适合该画像的新项目进行匹配。

基于内容的过滤方法将用户和项目分解为元数据。例如,IMDB 的推荐系统可以按类型标签(如喜剧、恐怖或爱情)对电影进行分类。它还会捕获用户行为信息,例如您点击的电影或您当前使用的搜索词,从而构建用户画像,以保持推荐的相关性并支持持续推荐。

元数据是基于内容的过滤的基础,但推荐算法才是其神奇之处。 

许多推荐系统依赖于 k 近邻 (k-NN) 模型。这种机器学习模型会找到给定输入的最近数据点(即邻居),并根据这些邻居的属性进行预测。在 IMDB 的例子中,k-NN 模型会知道某个用户点击了一部带有“快节奏”、“群星出演”和“PG 级”标签的电影,然后推荐一部具有类似属性的新电影。  

当然,这种基本的数据科学方法不仅适用于电影

  • 电商服装零售商可能会因为用户之前购买了运动裤而推荐慢跑裤。
  • 社交媒体网络可能会因为用户点击并参与了类似帖子而推荐某个主题的帖子。
  • 数据分析仪表板可能会因为业务用户过去下载过类似的模板而推荐新模板。 

基于内容的过滤并非没有局限性,因此它经常与协同过滤结合使用。

基于内容的过滤 vs. 协同过滤

完全使用基于内容的过滤技术的推荐系统往往受限于可用元数据的范围和质量。 

当元数据有限时,即使最好的算法也可能表现不佳。推荐可能不准确,让人感觉没有说服力或不相关。如果元数据缺乏深度,系统很可能会提供过于相似的建议,让用户感到无聊和缺乏灵感。

由于这些原因(以及其他原因,我们将在下一节介绍),公司转向协同过滤——无论是为了取代基于内容的过滤还是作为补充。 

协同过滤方法依赖于用户互动,例如用户评分、点赞或购买,来进行推荐。推荐系统允许用户通过隐式反馈相互“协作”。然后,它利用其他用户的反馈来做出明智的推荐。

例如,如果某人给某部电影很高的评分,或者持续购买并评论某些服装品牌,协同过滤系统就会将这些电影和品牌推荐给类似的用户。 

基于内容的过滤为何有效——以及其不足之处

基于内容的过滤有其自身的权衡,但通过将其与添加了协同过滤的混合推荐系统相结合,其中许多权衡可以得到平衡。无论如何,如果您正在构建推荐系统,您需要了解如何利用其优点并避免其缺点。 

优点

基于内容的过滤具有众多优点,使其成为许多推荐系统的核心部分

  • 根据个人用户偏好(包括喜好和厌恶)量身定制的个性化推荐可以帮助公司创造更好的用户体验,从而提高转化率。
  • 根据项目内容推荐项目解决了所谓的冷启动问题,即使是新用户也能获得个性化推荐。
  • 这些推荐仍然可以在独立于任何特定用户数据或用户行为的情况下进行。

基于内容的过滤的成功或失败往往取决于您的元数据的质量和广度。元数据越丰富,实现基于内容的过滤就越容易,结果也越好。

缺点

基于内容的过滤有一些局限性,其中一些比另一些更难克服。

  • 基于内容的过滤有时会产生“过滤气泡”(在社交媒体上如此称呼),导致推荐感觉重复或过于狭窄,因为系统过于关注用户互动过的少数项目。
  • 稀疏或低质量的元数据会限制基于内容的过滤的有效性,因为推荐只能像元数据那样好或多样。 
  • 与协同过滤不同,基于内容的过滤无法从网络效应或社会证明中受益。

基于内容的过滤在处理复杂或非结构化内容(如图像、音乐或视频)的领域也可能遇到困难。但如果您有特征提取方法,例如计算机视觉或基于视觉的 LLM 模型,您可以使用 RedisVL 等工具将这一缺点转化为优势。

基于内容的过滤的应用场景

基于内容的过滤广泛应用于各个行业和应用场景,因为如果没有个性化的过滤和推荐,近乎无限的选项可能会成为一种负担。这就是为什么,仔细观察,您几乎可以在网上随处找到基于内容的过滤。让我们探讨一些应用场景

  • 电商平台:在线零售商(无论是在自有平台还是 Amazon 或 Shopify 等平台)经常使用基于内容的过滤,根据用户过去购买的产品向他们推荐新产品。 
  • 媒体流媒体服务:Netflix 等流媒体服务受益于尽可能长时间地吸引用户在其平台上的参与,因此这些服务经常使用基于内容的过滤,根据用户已经喜欢的类型和艺术家来推荐电影、电视剧或音乐。
  • 教育平台:随着大规模开放在线课程 (MOOC) 的兴起以及编码训练营等行业特定训练营的普及,基于内容的过滤已成为教育平台根据用户的学习历史和偏好推荐课程和资源的有用方式。
  • 医疗健康应用:在线有种类繁多的治疗、饮食和锻炼方法,但并非所有都适用于所有人,医疗健康应用会利用其拥有的用户偏好信息来推荐新的锻炼方案或食谱。 

在实体店中,企业主展示新产品的空间有限。在线上,公司可以展示更多选项,前提是他们使用正确的推荐系统来帮助用户找到最适合他们的选择。

如何使用 Redis 构建基于内容的过滤推荐引擎

使用 Redis,构建基于内容的过滤系统非常简单。在这里,我们将逐步介绍如何使用 RedisVL 和 IMDB 电影数据集,构建一个由基于内容的过滤支持的电影推荐系统。 您可以在此处克隆此仓库

从高层面来看,我们将使用 RedisVL 从每部电影的标题、描述和关键词生成语义嵌入向量,然后使用向量相似性搜索存储和查询向量,以找到语义相似的电影。然后,我们将使用额外的字段,例如类型和上映年份,来增强结果。

设置您的环境

首先导入所需的库并定义您的 Redis URL。

Python

import pandas as pd
import ast
import os
import pickle
import requests

# Replace values below with your own if using Redis Cloud instance
REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT = os.getenv("REDIS_PORT", "6379")
REDIS_PASSWORD = os.getenv("REDIS_PASSWORD", "")

# If SSL is enabled on the endpoint, use rediss:// as the URL prefix
REDIS_URL = f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}"

加载和预处理数据集

我们使用的是来自 IMDB 的约 25,000 部电影的数据集。与任何数据任务一样,第一步是清理我们的数据。这包括填充缺失值、将某些字段转换为列表以及删除不必要的列。

Python

try:
   df = pd.read_csv("datasets/content_filtering/25k_imdb_movie_dataset.csv")
except:
   # download the file
   url = 'https://redis-ai-resources.s3.us-east-2.amazonaws.com/recommenders/datasets/content-filtering/25k_imdb_movie_dataset.csv'
   r = requests.get(url)

   #save the file as a csv
   if not os.path.exists('./datasets/content_filtering'):
       os.makedirs('./datasets/content_filtering')
   with open('./datasets/content_filtering/25k_imdb_movie_dataset.csv', 'wb') as f:
       f.write(r.content)
   df = pd.read_csv("datasets/content_filtering/25k_imdb_movie_dataset.csv")

Python

roman_numerals = ['(I)','(II)','(III)','(IV)', '(V)', '(VI)', '(VII)', '(VIII)', '(IX)', '(XI)', '(XII)', '(XVI)', '(XIV)', '(XXXIII)', '(XVIII)', '(XIX)', '(XXVII)']

def replace_year(x):
   if x in roman_numerals:
       return 1998 # the average year of the dataset
   else:
       return x

df.drop(columns=['runtime', 'writer', 'path'], inplace=True)

Python

df['year'] = df['year'].apply(replace_year)             # replace roman numerals with average year
df['genres'] = df['genres'].apply(ast.literal_eval)     # convert string representation of list to list
df['keywords'] = df['keywords'].apply(ast.literal_eval) # convert string representation of list to list
df['cast'] = df['cast'].apply(ast.literal_eval)         # convert string representation of list to list
df = df[~df['overview'].isnull()]                       # drop rows with missing overviews
df = df[~df['overview'].isin(['none'])]                 # drop rows with 'none' as the overview

生成向量嵌入以推荐相似内容

我们推荐系统的核心是根据电影的描述确定它们之间的相似性。为此,我们使用 HuggingFace 的预训练语言模型 为每部电影的概述和关键词生成向量嵌入。此步骤将花费一些时间,但只需为整个数据集执行一次。 

如果您不想等待,可以跳过此单元格,加载我们已经为您预先生成到文件中的向量。

Python

# add a column to the dataframe with all the text we want to embed
df["full_text"] = df["title"] + ". " + df["overview"] + " " + df['keywords'].apply(lambda x: ', '.join(x))

from redisvl.utils.vectorize import HFTextVectorizer

vectorizer = HFTextVectorizer(model = 'sentence-transformers/paraphrase-MiniLM-L6-v2')

df['embedding'] = df['full_text'].apply(lambda x: vectorizer.embed(x, as_buffer=False))
pickle.dump(df['embedding'], open('datasets/content_filtering/text_embeddings.pkl', 'wb'))

Python

try:
    with open('datasets/content_filtering/text_embeddings.pkl', 'rb') as vector_file:
        df['embedding'] = pickle.load(vector_file)
except:
    embeddings_url = 'https://redis-ai-resources.s3.us-east-2.amazonaws.com/recommenders/datasets/content-filtering/text_embeddings.pkl'
    r = requests.get(embeddings_url)
    with open('./datasets/content_filtering/text_embeddings.pkl', 'wb') as f:
        f.write(r.content)
    with open('datasets/content_filtering/text_embeddings.pkl', 'rb') as vector_file:
        df['embedding'] = pickle.load(vector_file)

定义我们的 Redis Search 模式

接下来,我们为 RedisVL 定义一个模式,以指定每部电影将拥有的字段,包括向量维度、距离度量以及任何额外字段,如年份、类型或评分。我们将从一个 yaml 文件 content_filtering_schema.yaml 加载此模式。

取消设置

index:
    name: movies_recommendation
    prefix: movie
    storage_type: json

fields:
    - name: title
      type: text
    - name: rating
      type: numeric
    - name: rating_count
      type: numeric
    - name: genres
      type: tag
    - name: overview
      type: text
    - name: keywords
      type: tag
    - name: cast
      type: tag
    - name: writer
      type: text
    - name: year
      type: numeric
    - name: full_text
      type: text

    - name: embedding
      type: vector
      attrs:
          dims: 384
          distance_metric: cosine
          algorithm: flat
          dtype: float32

Python

movie_schema = IndexSchema.from_yaml("content_filtering_schema.yaml")
index = SearchIndex(movie_schema, redis_client=client)
index.create(overwrite=True, drop=True)

data = df.to_dict(orient='records')
keys = index.load(data)

使用向量相似性搜索查找电影推荐

现在我们的数据已存储在 Redis 中,我们可以使用向量相似性搜索来查找彼此相似的电影。例如,要查找与经典影片《海底两万里》相似的电影,我们检索其向量嵌入并用它来搜索相似电影。

Python

from redisvl.query import RangeQuery

query_vector = df[df['title'] == '20,000 Leagues Under the Sea']['embedding'].values[0]

query = RangeQuery(vector=query_vector,
                    vector_field_name='embedding',
                    num_results=3,
                    distance_threshold=0.7,
                    return_fields = ['title', 'overview', 'vector_distance'])

results = index.query(query)

查询结果如下所示。


Python

[{'id': 'movie:b64fc099d6af440a891e1dd8314e5af7',
 'vector_distance': '0.584870040417',
 'title': 'The Odyssey',
 'overview': 'The aquatic adventure of the highly influential and fearlessly ambitious pioneer, innovator, filmmaker, researcher, and conservationist, Jacques-Yves Cousteau, covers roughly thirty years of an inarguably rich in achievements life.'},

{'id': 'movie:2fbd7803b51a4bf9a8fb1aa79244ad64',
 'vector_distance': '0.63329231739',
 'title': 'The Inventor',
 'overview': 'Inventing flying contraptions, war machines and studying cadavers, Leonardo da Vinci tackles the meaning of life itself with the help of French princess Marguerite de Nevarre.'},

{'id': 'movie:224a785ca7ea4006bbcdac8aad5bf1bc',
 'vector_distance': '0.658123672009',
 'title': 'Ruin',
 'overview': 'The film follows a nameless ex-Nazi captain who navigates the ruins of post-WWII Germany determined to atone for his crimes during the war by hunting down the surviving members of his former SS Death Squad.'}]

添加过滤器以改进推荐

在现实世界的推荐系统中,用户通常喜欢应用自己的过滤器——例如按类型缩小范围或使用特定关键词搜索。我们可以轻松扩展我们的系统,将模式中设置的这些(或任何其他字段)过滤器包含在内。

添加这些过滤器没有一刀切的方法,因为每个内容推荐应用都会有不同的字段,这就是 Redis 支持多种过滤器类型的原因,包括标签文本模糊匹配数字范围地理半径。尝试为模式中定义的其他字段添加过滤器,看看结果如何变化。

Python

from redisvl.query.filter import Tag, Num, Text

def make_filter(genres=None, release_year=None, keywords=None):
    flexible_filter = (
        (Num("year") > release_year) &  # only show movies released after this year
        (Tag("genres") == genres) &     # only show movies that match at least one in list of genres
        (Text("full_text") % keywords)  # only show movies that contain at least one of the keywords
    )
    return flexible_filter

def get_recommendations(movie_vector, num_results=5, distance=0.6, filter=None):
    query = RangeQuery(vector=movie_vector,
                        vector_field_name='embedding',
                        num_results=num_results,
                        distance_threshold=distance,
                        return_fields = ['title', 'overview', 'genres'],
                        filter_expression=filter,
                        )

    recommendations = index.query(query)
    return recommendations

开始使用 RedisVL

现在,您已经了解了基于内容的过滤的基础知识、该技术的优点和缺点、其广泛的应用场景,以及如何使用 RedisVL 自行构建基于内容的推荐系统。 

借助 Redis 作为向量数据库的强大功能,您可以生成相关的推荐,从而改善用户体验并提高转化率(以及许多其他好处)。无论您是推荐产品、音乐、电影还是图书,RedisVL 的灵活性和性能使其成为构建可扩展推荐系统的绝佳选择。

免费试用 Redis。