多年来,我们发现了如何从数字、原始文本、图像和标签等不同方式中检索信息。
随着定制用户界面的日益普及,标签搜索系统已成为一种方便的方式,可以轻松、准确地筛选信息。标签搜索常用于检索社交媒体帖子、文章、游戏、电影甚至简历。
然而,传统的标签搜索缺乏灵活性。如果我们要过滤完全包含给定标签的样本,那么在某些情况下,特别是对于只有几千个样本的数据库,可能没有任何(或只有几个)样本与我们的查询相匹配。
传统标签搜索是如何工作的?
传统系统采用一种称为 Jaccard 相似性的算法(通常通过 minhash 算法执行),该算法能够计算两组元素(在我们的例子中,这些元素就是标签)之间的相似性。如前所述,这种搜索方式并不灵活(集合要么包含查询的标记,要么不包含)。
我们能做得更好吗?
如果我们不只是从匹配的标签中筛选样本,而是将样本中与我们所选标签不完全相同但又相似的所有其他标签都考虑在内呢?我们可以让算法变得更加灵活,将结果扩展到非完全匹配但仍然很好的匹配上。我们将把语义相似性直接应用于标签,而不是文本。
引入语义标签搜索
正如简要说明的那样,这种新方法试图将语义搜索的功能与标签过滤系统结合起来。要建立这种算法,我们只需要一样东西:
我将使用的参考数据是 Steam 游戏库的开源集合(可从 Kaggle 下载 - MIT 许可)--大约有 40,000 个样本,这对于测试我们的算法来说是一个很好的样本量。从显示的数据帧中我们可以看到,每个游戏都有多个指定标签,我们的数据库中有 400 多个独特的标签。
有了起始数据,我们就可以继续:算法将按以下步骤阐述:
在本文中,我将只探讨这种新方法背后的数学原理。
1. 提取标签关系
我们想到的第一个问题是如何找到标签之间的关系。请注意,有几种算法可以获得相同的结果:
我们可以使用的最简单的提取标签关系的方法叫做共现矩阵,这也是我将在本文中使用的格式(既有效又简单)。
最先进的方法都是基于嵌入式神经网络(如过去的 Word2Vec,现在常用的是转换器,如 LLM),可以提取样本之间的语义关系。创建一个神经网络来提取标签关系(以自动编码器的形式)是一种可能性,而且在某些情况下通常是可取的。
由于标签是使用人类语言定义的,因此可以使用现有的预训练模型来计算已有的相似性。这可能会更快,麻烦也更少。但是,每个数据集都有其独特性。使用预先训练好的模型会忽略客户的行为。
例如 我们稍后将看到 2D 与梦幻之间的密切关系:使用预先训练的模型将永远无法发现这对关系。
算法的选择可能取决于很多因素,尤其是当我们必须处理一个庞大的数据池或我们有可扩展性方面的顾虑时(例如,# 标签将等于我们的向量长度:如果我们有太多标签,我们需要使用机器学习来解决这个问题。
a. 使用米开朗基罗相似性建立共生矩阵
如前所述,我将使用共现矩阵来提取这些关系。我的目标是找到每一对标签之间的关系,为此,我将使用 IoU(Intersection over Union)对所有样本集(S)进行以下计数:
这种算法与 Jaccard 相似度非常相似。它的操作对象是样本,而我介绍的算法的操作对象是元素,但由于(据我所知)这一特定应用尚未编入法典,因此我们可以将其命名为米开朗基罗相似性。(公平地说,之前在 StackOverflow 的一个问题中提到过这种算法的使用,但从未编入法典)。
对于 40,000 个样本,提取相似性矩阵大约需要一个小时,这就是结果:
让我们手动检查前 10 个样本中一些非常常见的标签,看看结果是否合理:
结果看起来很有希望!我们从普通的分类数据(只能转换为 0 和 1)开始,但我们已经提取出了标签之间的语义关系(甚至没有使用神经网络)。
b. 使用预先训练好的神经网络
同样,我们也可以使用预先训练好的编码器来提取样本之间的现有关系。然而,这种解决方案忽略了只能从我们的数据中提取的关系,只关注人类语言的现有语义关系。这可能不是一个适合在零售数据基础上工作的解决方案。
另一方面,通过使用神经网络,我们不需要建立关系矩阵:因此,这是一个可扩展的适当解决方案。例如,如果我们要分析一大批 Twitter 数据,我们需要分析 53.300 个标签。如果从这么多的标签中计算共现矩阵,将会得到一个大小为 2,500,000,000 的稀疏矩阵(相当不实用)。而使用标准编码器,输出的向量长度为 384,得到的矩阵总大小为 19,200,200 个。
2. 编码查询和样本
我们的目标是建立一个能够支持语义标签搜索的搜索引擎:就我们建立的格式而言,唯一能够支持这种搜索的技术就是矢量搜索。因此,我们需要找到一种合适的编码算法,将样本和查询都转换成向量。
在大多数编码算法中,我们使用相同的算法对查询和样本进行编码。但是,每个样本都包含不止一个标签,每个标签都代表一组不同的关系,我们需要在单个向量中捕捉这些关系。
此外,我们还需要解决前面提到的可扩展性问题,为此我们将使用 PCA 模块(当我们使用共现矩阵时,由于无需压缩向量,因此可以跳过 PCA)。
当标签数量过多时,我们就需要放弃计算共现矩阵的可能性,因为它是按平方率缩放的。因此,我们可以使用预先训练好的神经网络(PCA 模块的第一步)来提取每个现有标签的向量。例如,all-MiniLM-L6-v2 会将每个标签转换成长度为 384 的向量。
然后,我们可以对得到的矩阵进行转置,并对其进行压缩:最初,我们将使用 1 和 0 对可用的标签索引对查询/样本进行编码,从而得到一个与初始矩阵(53,300)长度相同的初始向量。此时,我们可以使用预先计算好的 PCA 实例,将同一稀疏向量压缩为 384 维。
编码样本
就我们的样本而言,这一过程在 PCA 压缩(激活时)后就结束了。
编码查询: 变量编码
不过,我们的查询需要采用不同的编码方式:我们需要考虑到与每个现有标签相关的关系。执行这一过程时,首先要将压缩向量与压缩矩阵(所有现有关系的总和)相加。现在我们已经得到了一个矩阵(384x384),我们需要将其平均,从而得到我们的查询向量。
由于我们将使用欧几里得搜索,它将首先优先搜索得分最高的特征(理想情况下,我们使用数字 1 激活的特征),但它也会考虑其他次要得分。
加权搜索
因为我们是将向量平均到一起,所以我们甚至可以在计算中应用权重,向量将受到不同于查询标签的影响。
3. 使用向量检索进行语义标签搜索
你可能会问:为什么我们要经历这个复杂的编码过程,而不是直接将一对标签输入一个函数,然后得到一个分数--f(查询,样本)?
如果你熟悉基于向量的搜索引擎,就会知道答案。通过成对计算,在仅有 40,000 个样本的情况下,所需的计算能力是巨大的(单次查询可能需要 10 秒):这不是一种可扩展的做法。然而,如果我们选择对 40,000 个样本进行矢量检索,搜索将在 0.1 秒内完成:这是一种高度可扩展的做法,在我们的案例中是完美的。
4. 验证
算法要想有效,就必须经过验证。目前,我们还缺乏适当的数学验证(乍看之下,M 的平均相似性得分已经显示出非常有前景的结果,但还需要进一步的研究来证明一个客观的指标)。
不过,如果使用对比示例来直观地显示现有结果,则会非常直观。以下是两种搜索方法的最高搜索结果(你看到的是为该游戏分配的标签)。
我们可以看到,传统搜索(没有附加规则,样本根据是否存在所有标签进行筛选,而不是排序)可能会返回标签数量较多的样本,但其中许多标签可能并不相关。
语义标签搜索根据所有标签的相关性对所有样本进行排序,简单地说,就是取消包含不相关标签的样本的资格。
这种新系统的真正优势在于,当传统搜索无法返回足够的样本时,我们可以使用语义标签搜索来选择任意数量的样本。
在上面的示例中,使用传统标签过滤法不会返回 Steam 库中的任何游戏。不过,通过使用语义标签过滤,我们仍然可以得到不完美的结果,但却是与我们的查询相匹配的最佳结果。你看到的就是与我们的搜索匹配的前 5 款游戏的标签。
结论
在此之前,如果不采用聚类、深度学习或多重 knn 搜索等复杂方法,就无法在过滤标签的同时考虑其语义关系。
这种算法所提供的灵活度可以让我们脱离传统的手动标记方法,这种方法会迫使用户在一组预定义的标记中做出选择,并为使用 VLM 的 LLMs 为文本或图像自由分配标记提供了可能性,而不必局限于一个预先存在的结构,从而为可扩展和改进的搜索方法开辟了新的选择。