如何在不使用机器学习的情况下构建基本的推荐引擎

推荐系统已经成为我们生活中不可或缺的一部分。这些智能算法对于塑造我们的在线体验、影响我们消费的内容、我们购买的产品和我们探索的服务至关重要。无论我们是在Netflix等平台上播放内容,在Spotify上发现新音乐,还是在线购物,推荐系统都在幕后默默地工作,以个性化和增强我们的互动。

这些推荐系统的独特之处在于它们能够根据历史行为和用户模式理解和预测我们的偏好。通过分析我们过去的选择,这些系统会提出量身定制的建议,节省我们的时间和精力,同时向我们介绍符合我们兴趣的内容/产品。这提高了用户满意度并促进发现,向我们介绍我们可能不会遇到的新的相关产品。

在较高层面上,开发人员了解这些算法是由机器学习和深度学习系统(可互换地称为神经网络)提供支持的, 但是如果我告诉您有一种方法可以构建推荐引擎,而无需经历部署神经网络的痛苦网络或机器学习模型?

这个问题在早期和中期初创公司的背景下特别相关,因为他们没有大量的结构化数据来训练他们的模型。正如我们所知,如果没有适当的训练数据,大多数机器学习模型将无法给出准确的预测。

我最近为一个项目构建并部署了一个基本的推荐引擎 语音优先的社交网络,这导致我们的关键指标跃升了 40%。在撰写本博客时,该系统每月生成超过 3000 万条推荐。尽管这个推荐系统是为社交网络构建的,但您可以将基本架构应用于任何用例,例如产品推荐、音乐推荐、文本和视频平台上的内容推荐或其他任何内容。让我首先描述问题陈述。

从工程角度陈述问题

我有一个广泛的 产品需求文件 和 后续工程需求文件 因为我们正在为每天已经有数千名用户使用的产品构建推荐系统。但为了使这篇博客简短而切题,我将只写高级需求,然后讨论相同的解决方案。如果您正在为您的产品构建推荐系统(简单的或基于神经网络的)并且遇到困难,请随时与我联系 我将非常乐意回答您的问题。

从较高的层面来看,从工程角度来看,我们有以下要求:

  1. 系统应该能够以关键字的形式捕获用户的兴趣。系统还应该能够对用户对特定关键字的兴趣程度进行分类。
  2. 系统应该能够捕获用户对其他用户的兴趣。它应该能够对用户对另一个用户创建的内容的兴趣程度进行分类。
  3. 系统应该能够根据用户的兴趣生成高质量的推荐。
  4. 系统应该能够确保用户已经查看/拒绝的推荐在 X 天之内不会再次出现。
  5. 系统 应该具有逻辑 来确保来自同一创建者的帖子不会分组在同一页面上。系统应该尽力确保如果用户消费十个帖子(我们的页面大小),所有这些都应该来自不同的创建者。
  6. 系统应该很快。P99 延迟小于 150 毫秒。
  7. 所有其他非功能性需求,例如高可用性、可扩展性、安全性、可靠性、可维护性等,都应该得到满足。

同样,这是一个高度过于简单化的问题陈述列表。事实上,这些文档有 3000 多个字长,因为它们还涵盖了将此推荐引擎集成到我们现有系统时可能出现的许多边缘情况和极端情况。让我们继续讨论解决方案。

解决方案 – 推荐引擎的高级工作

我将一一讨论问题的解决方案,然后描述整个系统的整体工作原理。

我们的第一个问题是捕获用户的兴趣并用特定兴趣定义他们的兴趣水平

为此,我们创建了一个名为 社交图谱。简而言之,社交图存储社交网络中不同实体之间的关系和联系。这些实体可以是不同的用户或具有特定兴趣的用户关系。社交图是理解和构建特定系统内关系的有效方法。为了简洁起见,我不会详细解释社交图谱,但我会建议您谷歌它并了解更多信息。以下是我为推荐引擎构建的社交图的简化版本。

社交图示例

从上图中可以看出,我们存储了大量信息,例如交互次数(点赞、评论、分享)和这些交互的新近度(最后一次发生的时间)作为两个用户之间以及两个用户之间的关系数据用户和兴趣。我们甚至存储两个不同兴趣关键字之间的关系。我用了 亚马逊海王星,AWS 托管的图形数据库,用于存储此社交图。您可以使用任何其他图形数据库,例如 Neo4j、JanusGraph、ArrangoDB等。


这些兴趣关键词主要是名词。有一个系统可以将帖子的内容分解为这些关键字(名词)。它由AWS comprehend 提供支持; 自然语言处理 (NLP) 服务,使用机器学习将文本分解为实体、关键短语等。同样,您可以使用任何托管 NLP 服务(有多个可用)来完成相同的任务。您不需要学习或部署机器学习模型!如果你已经了解机器学习,那么你可以去查看 Huggingface 上也有开源 NLP 模型

我们的第二个问题是根据用户的兴趣生成高质量的推荐

下图是系统工作原理的简化高级表示。

推荐系统如何工作的简化高级表示。

虽然上面的内容看起来很简单,但每一步都有很多事情要做,必须仔细考虑这些事情,然后进行编程,以确保系统以最佳状态运行。

让我一步步解释:

第 1 步 – 将帖子内容转换为矢量嵌入

要生成这些推荐,首先,我们必须将帖子的内容转换为名为 – 向量嵌入。随着最近法学硕士、OpenAI(ChatGPT 的制造商)和 矢量数据库,向量嵌入正在成为一个日常术语。我不会详细介绍它们是什么以及它们如何工作,但我强烈建议您阅读更多有关它们的信息。但是,为提要生成可行的候选者还必须考虑内容隐私和审核等问题(删除亵渎的词语、辱骂、色情内容、骚扰、过滤被阻止的用户等)。

为了生成矢量嵌入,您可以使用任何著名的嵌入模型,例如 OpenAI 嵌入模型、  Amazon titan 或任何 开源文本嵌入模型,具体取决于您的用例。我们选择了 Amazon Titan  ,因为它的价格实惠、性能好且操作简便。

步骤2-查询用户的兴趣

现在,事情变得有趣了。您可能希望根据您的特定业务需求来设计查询。例如,在查询兴趣时,我们对参与度的新近度给予比特定关键字或用户的参与度更多的权重。我们还运行多个并行查询来查找用户的不同兴趣类型 – 关键字或其他用户。由于我们为单个用户生成多个提要,因此我们还会根据趋势运行一些宣传特定主题的查询(例如,在圣诞节附近您会看到许多与圣诞节相关的帖子,或者如果发生了地震,您会看到许多与地震相关的帖子)。不用说,只有当用户在旅程中表达了对这些主题的兴趣时,该主题才会出现在查询结果中。

因此,选择适合您的业务用例的逻辑以及您想要驱动和运行多个查询的行为,以获得所有用户兴趣的足够大的列表。

第 3 步 – 根据找到的兴趣进行 ANN 搜索

矢量数据库主要用于执行特定类型的搜索,称为 近似最近邻搜索(安)。同样,对各种兴趣进行分类的方式以及是否进行大型 ANN 搜索或并行差异搜索应完全基于您的用例和业务需求。我建议不仅仅进行基于群组的搜索,然后对结果进行排序(我们将在本博客后面讨论这一点)以获得最佳的最终用户体验。在这种情况下,ANN 搜索的作用是找到平台上与用户兴趣相似(更接近)的其他帖子。

步骤 4 – 将结果按顺序存储在缓存数据库中。

缓存数据库,因为我们需要解决的问题之一就是速度。我们使用 Redis排序集 来存储特定用户的帖子的唯一 ID。我们使用 Redis 排序集,因为用户提要中的帖子顺序至关重要。另外,您必须解决的另一个问题是“ 系统应该具有逻辑 来确保来自同一创建者的帖子不会分组在同一页面上”。为了避免来自同一创建者的内容重复,我们编写了一个简单的算法,确保如果将特定创建者的帖子插入到特定用户的提要(排序集)中的任何位置,我们不会插入来自同一创建者的其他帖子连续十个位置(在向最终用户提供提要时,我们的页面大小为 10,因此我们保持静态以避免复杂性)。

为了决定用户特定推荐的顺序,我们考虑了以下因素 –

  1. 该用户与特定兴趣(或其他用户)的关系强度:它由从社交图中获取各种数据点的算术公式确定。所有这些都是参与度数据,例如最后创建的点赞的时间戳、创建的点赞数量、最后的评论等。用户参与行为是他们对某事物感兴趣的指标。
  2. 帖子在平台上的受欢迎程度: 为了确定这一点,我们创建了一种算法,该算法考虑了各种因素,例如参与度、参与度与印象数比率、参与的唯一用户数量等,以生成该帖子的参与度分数在平台级别发布。

在某些提要中,我们优先考虑受欢迎程度;在其他情况下,我们会优先考虑社交图谱。但大多数情况下,它们都是两者的健康组合。

系统如何运作

从上图中可以看出,系统有意保持非常简单。以下是系统的工作原理 –

  1. 当用户 A 创建帖子时,帖子服务在保存该帖子后会向队列触发发布/订阅事件,该事件由用于候选生成的后台服务接收。我们用 谷歌发布/订阅 用于发布/订阅功能。
  2. 该后台服务异步接收此信息并执行前面讨论的功能 – 隐私检查、审核检查和关键字生成,然后生成向量嵌入并将其存储在向量数据库中。我们正在使用 AstraDB 作为我们的矢量数据库 (稍后讨论)。
  3. 每当用户在更新我们的主 NoSQL 数据库后参与(点赞/评论/分享等)时,后期服务就会触发推荐引擎服务的发布/订阅事件。
  4. 该推荐引擎服务更新图形数据库,然后通过执行 ANN 搜索和更新 Redis 数据库来近乎实时地更新用户的推荐源。因此,用户互动越多,提要就越好。 有一些检查可以确保建议不会偏向于特定的关键字列表。这些检查是在我们查询图形数据库时执行的。该服务还异步更新参与度分数。参与度分数也会根据查看帖子的用户重新计算。
  5. 由于上述所有步骤都是在后台异步执行的,因此这些计算不会影响最终用户体验。
  6. feed最终通过feed服务提供给最终用户。由于该服务仅在 Redis 和我们的主 NoSQL 数据库上执行查找(DynamoDB),其P99延迟小于110毫秒。无论规模如何,这两个数据库都以个位数毫秒的延迟返回查询结果。

使用的工具和技术

  1. 一些服务已经写在 Go编程语言,而其他人已经写在 NodeJS(带有打字稿)。
  2. 我们正在使用 Datastax 的 AstraDB 作为我们的矢量数据库。我们在评估了多个其他数据库(例如 pinecone、milvus 和 weaviate)后做出了这一决定。除了对矢量和其他数据类型具有出色的查询和索引功能之外,它还提供了袖珍友好的无服务器定价计划。它运行在 Cassandra 引擎之上,我们在平台上的其他几个功能中将其用作数据库,并且它提供了 CQL 查询接口,这对开发人员非常友好。我强烈建议您尝试将其用于矢量用例。
  3. 我们用 谷歌发布/订阅 对于我们的异步通信来说,因为就我们目前的规模(几十万的总用户,几千个每日活跃用户)而言,它具有很高的成本效益。我已经在数十万用户的规模上运行它,每秒处理数千个事件。它运行良好,并且使用和扩展起来毫不费力。
  4. 雷迪斯 – 速度、简单和强大的数据结构。我认为2024年我不需要讨论为什么要使用redis。
  5. 动态数据库 – 同样,它具有高度可扩展性且易于使用,并且我们在无服务器模式下运行它,尽管每分钟有数十万个查询,但我们的总费用相当低。它还提供非常强大的索引功能和读取和写入的个位数毫秒延迟。

未来需要解决的问题

正如您可以想象的那样,可以调整相同的设置来为任何用例构建基本的推荐引擎。但是,由于我们的网络是一个社交网络,因此我们需要进行一些调整以使该系统更加高效。

  1. 社交图层面需要机器学习/深度学习算法来预测与用户最相关的关键词和用户。目前,数据集太小,无法准确预测任何内容,因为它是一个非常新的产品。然而,随着数据的增长,我们将需要用机器学习算法的输出来替换当前的简单查询和公式。
  2. 各种关键词和用户之间的关系必须进行微调,变得更细化。他们现在处于非常高的水平。但他们需要更深入。我们需要探索图中的二度和三度关系,以首先完善建议。
  3. 我们目前没有对嵌入模型进行任何微调。我们将需要在不久的将来这样做。

尾注

我希望您觉得这个博客有帮助。如果您有任何问题、疑问或建议,请随时通过以下方式与我联系 。请与您的朋友和同事分享这篇文章。