执行语义搜索之前的聚类演示 | 图片来自作者
嵌入是自然语言处理的基石。嵌入可以做很多事情,但其中最流行的用途之一是在检索应用程序中使用的语义搜索。
尽管整个技术社区都在热衷于了解知识图谱检索流程的工作原理,但使用标准向量检索并不过时。
您会发现多篇文章向您展示如何从语义搜索中过滤掉不相关的结果,我们还将在这里使用诸如聚类和重新排名等技术来关注这一点。
不过,本文的重点是 比较各种规模的开源和闭源嵌入模型。
重点关注的模型 — — 还有更多可供选择 | 图片来自作者
我们将比较多达 9 种在 MTEB排行榜上名列前茅的不同嵌入模型。这将让您了解大型模型和小型模型的性能以及扩展时的成本。
如果您曾经使用过 OpenAI 的模型来生成嵌入,那么您可能很好奇它们是否具有足够的竞争力。
快速回顾一下嵌入,如果它们对你来说是新事物:当我们为每个文本创建嵌入(向量数组)时,这会转化为计算机可以理解的东西。
文本到嵌入 | 图片来自作者
具体来说,对于语义搜索,我们会比较不同文本的嵌入,以查看它们之间的语义相似度。这使我们能够使用一种带有查询的模糊搜索(即搜索关系),而不是精确的关键字匹配。
查询嵌入与其他嵌入的语义相似性 | 图片来自作者
我将在介绍部分介绍嵌入及其工作原理,特别关注如何计算语义相似度。
使用自定义案例,这个案例来自一位咨询公司老板,他问他是否可以创建一个将职位描述与 LinkedIn 个人资料相匹配的应用程序。
如果我们真的要这样做,我们将使用数百万个用户资料,但为了完成这篇文章,我创建了 6,900 个合成的 LinkedIn 资料。
这通常是一个简单的用例,因为我们不会将文档分成一个大文件;每个配置文件都适合一个块。这个领域并不困难,因为我们很容易理解模型是否找到了正确的关系。
但它会让你了解如何思考解决类似的问题。
介绍
对于生产案例,您理想情况下希望在执行语义搜索之前使用某种关键字搜索或分类,以确保过滤掉大部分配置文件,但这不是本文的重点。
对于本文,由于我们的配置文件很少,我们可以使用聚类作为整个数据集的一种无监督分类方法。
请参见下面的图示来了解聚类的样子。
LinkedIn 个人资料的 4 个集群的简化图示 | 图片来自作者
聚类还能让我们了解不同的模型如何感知连接关系。
根据模型,我们可以在集群内执行语义搜索之前隔离正确的组。
将我们的查询与正确的集群匹配的简化图像 | 图片来自作者
这应该可以让我们过滤掉不相关的结果,比如将产品经理与产品营销经理混淆的模型。
您可以使用 LLM 重新排名作为最后一步,以确保最佳结果位于顶部。
为了让事情变得非常简单和更便宜,我已经为我们将在这个数据集中评估的每个模型添加了嵌入。
我还为我们的查询(即匿名职位描述)创建了嵌入,因此您不必启动开源模型来自行嵌入查询。
嵌入
我提到,嵌入是文本的数字表示,可以捕捉文本的含义,从而让计算机能够处理和理解自然语言。
文本到嵌入 | 图片来自作者
借助更现代的变换器模型,这些模型可以理解整个上下文,从而理解单词和句子的多种含义——这在几年前还是不可能的。
我们实际上可以通过将嵌入表示为几何空间中的点来可视化图形上的嵌入。因此,嵌入之间的语义关系转化为几何接近度。
图上的嵌入作为几何空间中的点 | 图片来自作者
不同的模型针对不同的任务而构建,但大多数较大的模型足够通用,可以执行各种任务,例如检索、聚类和分类。
用于检索的语义搜索使用图上的这种接近度来确定查询与其他嵌入的匹配位置,即,它计算图上嵌入的距离。
为了计算语义搜索中嵌入之间的相似度,使用了几种方法,但余弦相似度是最受欢迎的。
通过语义相似性找到最佳匹配 | 图片来自作者
我们使用的模型将直接影响您执行语义搜索所获得的结果,因为它如何连接不同的嵌入很重要,而这是它如何训练的结果。
使用什么数据集、目标和架构模型进行训练很重要,因为这将影响模型对各种概念的理解和链接程度。
另一方面,聚类将数据组织成组(或集群),其中项目彼此之间的相似性要高于其他组中的项目。它更善于识别和匹配嵌入之间的相似性,使我们能够有效地隔离组。
通过聚类嵌入对相似的配置文件进行分组并可视化查询 | 图片来自作者
此过程使我们能够在执行语义搜索之前首先过滤掉任何不相关的匹配,从而起到降噪策略的作用。
至少,这是个想法。
并非所有模型都能按照我们需要的方式使用聚类;根据模型的构建方式,有些模型会表现更好,有些则表现更差。
那么,您如何知道该选择哪种模型?MTEB排行榜根据嵌入模型在各种任务中的表现对其进行排名。
我从 OpenAI 中挑选了一些模型进行测试,这些模型比较受欢迎,与经过微调的 Mistral-7B 和Mixedbread AI 的Mxbai等较小的较新模型进行比较。
它们或多或少都是在过去两年内被释放的。
本文将使用我们选定的模型——这个列表不是确定的 | 图片来自作者
如果您是开源模型的新手,您可能会惊讶地发现许多开源模型的排名相当高。相对于所有其他模型, Ada-002在我们的列表中排名垫底。
如果您对这些模型并不是第一次尝试,那么看看哪一个模型最适合这项任务可能仍然很有趣。
查看下表,了解每个模型的大小、最大标记数以及检索和聚类的排名。
我们将使用的各种模型的 MTEB 排行榜指标 | 图片来自作者
因此,您可能会问自己,既然可以使用排行榜上名列前茅的开源模型,为什么还要使用商业模型呢?
开源模型的经济学
使用开源模型当然听起来不错,而且是隐私方面的首选。许多开源模型都名列前茅,但你确实需要考虑托管模型与使用 API 的经济性。
我研究了在 GPU 上托管较小(约 350M)和较大(7B)开源模型的成本,以及为一些流行的商业模型按代币付费的成本。
计算成本——不包括存储 | 图片来自作者
这里的假设是每个文本有 400 个标记,因此 334M 模型将能够在单个 L4 GPU 上每秒处理多达 103 个文本,而 7B 模型将使用单个 A100 每秒处理大约 40 个文本。
正如您所看到的,一旦您开始嵌入数百万条文本,使用像 Ada-002 这样的模型最终将真正产生效益。这还不包括存储。
如果您是企业客户,并且正在研究 Nvidia 的嵌入模型(例如 nv-embed-v1),它们提供了相当不错的API供您使用。我已使用它测试了其中几个模型。
不过,使用小于 500 个参数的小型模型无疑是最明智的选择。如果你可以使用较小的开源模型,那么你应该这样做,因为这样可以节省高达 95% 的成本。
如果要在单个 GPU 上托管较小和较大模型,我还继续计算了它们的处理时间。
使用一个 GPU 的预估处理时间 | 图片来自作者
调用 API 也需要时间,并且它们具有推理限制,因此无论您做出什么选择,都必须考虑完全嵌入整个数据集所需的时间。
对于开源模型,您总是可以使用更多的 GPU 来处理,但它会让您了解使用更小的东西可能更节能。
测试几个模型来看看哪种模型适合您的任务总是好的,这也是我们稍后要做的。
量化
正如您在上面看到的,使用更大的模型(例如 7B 左右的模型)仍然非常昂贵且耗能。我们仅计算了 250 万个嵌入的成本,但一旦您开始进一步扩展,可能就值得研究量化。
量化通过使用更少的位数来表示数据来压缩模型,从而减小模型的大小。其理念是,使用 4 位和 8 位量化等量化技术将有助于在通常无法处理如此大模型的硬件上运行更大的模型。
有一些人试图测量量化模型上各种指标的性能下降;我记得我看到的最后一个指标认为整体性能下降了 12%。
用例
我不知道你是怎么想的,但我喜欢测试不同的模型,而不是只看指标。这让我了解了较小和较大的模型如何解释文本之间的关系。
您可以在此处找到包含合成 LinkedIn 个人资料的数据集,以及应匹配的包含我们职位描述的数据集。
您可以在这里找到我们将要使用的 Colab 笔记本。
导入数据
您需要打开笔记本才能继续操作,但是一旦完成,您就会看到我们正在从 Hugging Face 导入两个数据集。
# Synthetic LinkedIn profiles with the embeddings
dataset = load_dataset("ilsilfverskiold/linkedin_profiles_synthetic")
profiles = dataset['train']
# Anonymous job descriptions with embeddings
dataset = load_dataset("ilsilfverskiold/linkedin_recruitment_questions_embedded")
applications = dataset['train']
这两个数据集将使我们能够比较已生成的 6,900 个 LinkedIn 个人资料的不同嵌入模型。
合成数据确实是合成的,所以请谨慎对待。它是使用 Llama 3.1 创建的,并且确实存在一些严重的对齐问题,它使用诸如“结果驱动”、“经验丰富”和“专注”等词语来描述个人资料。
嵌入已经添加,如果您查看“配置文件”,您就会看到它。
# profiles dataset
Dataset({
features: [...,'embeddings_nv-embed-v1', 'embeddings_nv-embedqa-e5-v5', 'embeddings_bge-m3', 'embeddings_arctic-embed-l', 'embeddings_mistral-7b-v2', 'embeddings_gte-large-en-v1.5', 'embeddings_text-embedding-ada-002', 'embeddings_text-embedding-3-small', 'embeddings_voyage-3', 'embeddings_mxbai-embed-large-v1 '],
num_rows: 6904
})
附言:
embeddings_gte-large-en-v1.5 不起作用。我尝试托管它,但未能为其设置所有嵌入,因此不要使用它。
从这里,您需要决定您感兴趣的与个人资料相匹配的职位描述。
看看下面的代码;我选择了第二个应用程序,但您可以设置另一个数字。
application = applications[1] # deciding on the second application - a product marketing manager position
application_text = application['natural_language']
print("application we're looking for: ",application_text)
如果这样更简单的话,请直接在 Hugging Face 上检查数据集。
HuggingFace 数据集查看器,用于查看求职申请数据集 | 图片来自作者
从这里,您可以决定要使用哪种嵌入模型。我已经测试了其中的大部分,因此我将
embeddings_mxbai-embed-large-v1在这次运行中使用这些模型。
这是 334M 开源模型,如果您向上滚动到我之前使用的表格,它在检索和聚类的排行榜上排名都相当高。
如果您想尝试不同的模型,只需设置另一个模型即可。查看上面提到的数据集,看看您可以访问哪些数据集。
# Get the query embeddings for an embedding model - in here we're picking mxbai-embed-large-v1
query_embedding_vector = np.array(application['embeddings_mxbai-embed-large-v1'])
embeddings_list = [np.array(emb) for emb in profiles['embeddings_mxbai-embed-large-v1 ']] # note the extra space
texts = profiles['text']
语义搜索
我们可以在尝试聚类之前尝试执行语义搜索;这使我们在添加其他任何内容之前了解它的性能。
为了计算个人资料和我们的查询(求职申请)之间的语义相似度,我们运行下面的代码。
# Let's first try to calculate the cosine similarity (without clustering)
def cosine_similarity(a, b):
a = np.array(a)
b = np.array(b)
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
similarities = []
for idx, emb in enumerate(embeddings_list):
sim = cosine_similarity(query_embedding_vector, emb)
similarities.append(sim)
然后,我们可以通过将相似度得分最高的排序在最上面来显示,将显示限制为前 30 个结果。
results = list(zip(range(1, len(texts) + 1), similarities, texts))
sorted_results = sorted(results, key=lambda x: x[1], reverse=True)
# Let's display the results as well
print("\nSimilarity Results (sorted from highest to lowest):")
for idx, sim, text in sorted_results[:30]: # adjust if you want to show more
percentage = (sim + 1) / 2 * 100
text_preview = ' '.join(text.split()[:10])
print(f"Text {idx} similarity: {percentage:.2f}% - Preview: {text_preview}...")
结果看起来如下所示,但这取决于您选择的应用程序。
Similarity Results (sorted from highest to lowest):
Text 3615 similarity: 89.59% - Preview: Product Marketing Manager | Building Go-to-Market Strategies for Growth Results-driven...
Text 6299 similarity: 89.56% - Preview: Product Marketing Manager | Driving Growth & Customer Engagement Results-driven...
Text 3232 similarity: 89.09% - Preview: Product Marketing Manager | Driving Product Growth through Data-Driven Strategies...
Text 5959 similarity: 88.90% - Preview: Product Marketing Manager | Data-Driven Growth Expert Results-driven Product Marketing...
Text 5635 similarity: 88.84% - Preview: Product Marketing Manager | Driving Growth through Data-Driven Marketing Strategies...
Text 5835 similarity: 88.74% - Preview: Product Marketing Manager | Cloud-Based SaaS Results-driven Product Marketing Manager...
Text 139 similarity: 88.66% - Preview: Product Marketing Manager | Scaling Growth through Data-Driven Strategies Experienced...
Text 6688 similarity: 88.48% - Preview: Product Marketing Manager | Driving Business Growth through Data-Driven Insights...
Text 6405 similarity: 88.27% - Preview: Product Marketing Manager | Scaling SaaS Products for Global Markets...
Text 3439 similarity: 88.11% - Preview: Product Manager | Focused on delivering innovative products that drive...
Text 5958 similarity: 88.00% - Preview: Product Manager Office | Growth Driven by Customer Centricity Highly...
Text 5183 similarity: 87.86% - Preview: Product Marketing Manager | B2B SaaS Experienced Product Marketing Manager...
Text 1329 similarity: 87.81% - Preview: Product Marketing Manager | Scaling Growth for Emerging Tech Startups...
Text 130 similarity: 87.81% - Preview: Product Marketing Manager | Growth Strategies & Launches Results-driven Product...
Text 3423 similarity: 87.78% - Preview: Product Marketing Manager | Scaling B2B SaaS Solutions Experienced Product...
Text 4234 similarity: 87.72% - Preview: Product Manager | Leading Cross-Functional Teams to Drive Business Growth...
使用 mxbai 嵌入模型以及许多其他模型,我们可以清楚地看到结果将返回产品营销经理和产品经理,这是我们不想要的。
请看上面的 88.11% — 预览:产品经理和 88.00% — 预览:产品经理办公室。
让我们引入聚类来看看它是否有帮助。
聚类
首先,我们从配置文件嵌入中设置聚类;在这里,我们需要确定聚类的数量。
我选了 10。
embeddings_array = np.array(embeddings_list)
num_clusters = 10 # you can pick another number here
kmeans = KMeans(n_clusters=num_clusters, random_state=42)
kmeans.fit(embeddings_array)
cluster_labels = kmeans.labels_
pca = PCA(n_components=2)
reduced_embeddings = pca.fit_transform(embeddings_array)
然后,我们需要了解该查询(或求职申请)适合哪个集群。
# Let's now see how query fits into the clustering
query_embedding_array = np.array(query_embedding_vector).reshape(1, -1)
reduced_query_embedding = pca.transform(query_embedding_array)
# Let's also predict which cluster the query would belong to
query_cluster_label = kmeans.predict(query_embedding_array)[0]
print(f"The query belongs to cluster {query_cluster_label}")
在此之后,我们可以在二维图上可视化这些簇——请记住,这些簇已经被压平,因此它们可能位于彼此的顶部。
对 LinkedIn 个人资料进行聚类 — 图片来自 Colab 笔记本
您可以将鼠标悬停在不同的嵌入上来查看配置文件。
查看第五个簇的嵌入——图片来自 Colab 笔记本
我们还可以在图表上隔离查询(X),以查看模型认为它所属的集群。
使用查询 X 隔离第 5 个群集 — 图片来自 Colab 笔记本
我们可以清楚地看到,该模型正确地将营销人员解释为一个集群,其中包括同一集群中的 SEO 专家和增长黑客,但不包括 Office 产品经理和产品经理。
这太棒了。
从这里开始,我们现在可以结合聚类和语义搜索来获得更好的结果。
记得检查不同的模型并尝试看看哪个做得更好;你会发现,较大的模型自然更擅长对相似的配置文件进行分组,但一些较小的模型也做得很好。
聚类与语义搜索
现在我们看到它能够将查询分组到正确的集群中,我们可以结合我们的方法。
# Let's now do semantic search but only in the correct cluster
cluster_indices = np.where(cluster_labels == query_cluster_label)[0]
cluster_embeddings = embeddings_array[cluster_indices]
cluster_texts = [texts[i] for i in cluster_indices]
similarities_in_cluster = []
for idx, emb in zip(cluster_indices, cluster_embeddings):
sim = cosine_similarity(query_embedding_vector, emb)
similarities_in_cluster.append((idx, sim))
similarities_in_cluster.sort(key=lambda x: x[1], reverse=True)
top_n = 40 # adjust this number if you want to display more matches
top_matches = similarities_in_cluster[:top_n]
print(f"\nTop {top_n} similar texts in the same cluster as the query:")
for idx, sim in top_matches:
percentage = (sim + 1) / 2 * 100
text_preview = ' '.join(texts[idx].split()[:10])
print(f"Text {idx+1} similarity: {percentage:.2f}% - Preview: {text_preview}...")
我们可以看到,如果我们运行上面的代码,结果现在返回的结果中没有产品经理,而是返回营销经理,这通常更合适。
Top 40 similar texts in the same cluster as the query:
Text 3615 similarity: 89.59% - Preview: Product Marketing Manager | Building Go-to-Market Strategies for Growth Results-driven...
Text 3232 similarity: 89.09% - Preview: Product Marketing Manager | Driving Product Growth through Data-Driven Strategies...
Text 5959 similarity: 88.90% - Preview: Product Marketing Manager | Data-Driven Growth Expert Results-driven Product Marketing...
Text 5635 similarity: 88.84% - Preview: Product Marketing Manager | Driving Growth through Data-Driven Marketing Strategies...
Text 5835 similarity: 88.74% - Preview: Product Marketing Manager | Cloud-Based SaaS Results-driven Product Marketing Manager...
Text 139 similarity: 88.66% - Preview: Product Marketing Manager | Scaling Growth through Data-Driven Strategies Experienced...
Text 6688 similarity: 88.48% - Preview: Product Marketing Manager | Driving Business Growth through Data-Driven Insights...
Text 6405 similarity: 88.27% - Preview: Product Marketing Manager | Scaling SaaS Products for Global Markets...
Text 5183 similarity: 87.86% - Preview: Product Marketing Manager | B2B SaaS Experienced Product Marketing Manager...
Text 1329 similarity: 87.81% - Preview: Product Marketing Manager | Scaling Growth for Emerging Tech Startups...
Text 130 similarity: 87.81% - Preview: Product Marketing Manager | Growth Strategies & Launches Results-driven Product...
Text 3423 similarity: 87.78% - Preview: Product Marketing Manager | Scaling B2B SaaS Solutions Experienced Product...
Text 5945 similarity: 87.63% - Preview: Marketing Manager | Driving Growth through Data-Driven Strategies Results-driven marketing...
Text 2664 similarity: 87.59% - Preview: Product Marketing Manager | Driving Growth & Innovation Results-driven Product...
Text 3368 similarity: 87.54% - Preview: Product Marketing Manager | Scaling Growth through Data-Driven Strategies Highly...
Text 5794 similarity: 87.48% - Preview: Product Marketing Manager | Driving Growth through Data-Driven Insights Results-driven...
Text 5685 similarity: 86.71% - Preview: Performance Marketing Manager | Driving Business Growth through Data-Driven Strategies...
Text 5818 similarity: 86.37% - Preview: Digital Marketing Manager | Driving Business Growth through Data-Driven Strategies...
对于真实情况,您理想情况下希望在执行语义搜索之前对该数据集进行过滤和分类。
这里的想法是让你比较不同的模型,特别是较小的模型和较大的模型,看看你愿意为了更快、更便宜的推理而牺牲多少质量。
不要仅仅因为某个原因就选择更大的型号,除非你真的需要它。
如果您想继续评估模型,您可以使用RAGA来评估检索应用程序基于不同模型的表现。
模型性能说明
我需要在这里选择一些东西来评估性能,所以我选择看看模型在区分产品经理和产品营销经理方面的表现如何。
更大的模型更有能力在聚类之前获得正确的结果,但它们最初都存在将两者分开的问题。
Ada-002 可能规模更大,在聚类之前表现良好,而 OpenAI 较小且较新的模型 text-embed-3-small 表现较差。
为职位简介设定我们自己的绩效指标 | 图片来自作者
然而,一些模型也难以正确地聚类轮廓。具体来说,经过微调的 7B Mistral 模型和 E5 在这里表现不佳。这可能是它们构建方式的自然结果。
对于这个特定的职位,其余人的做法也大致相同。
我对mxbai 的出色表现感到惊讶,它的大小只有 335M;这表明,对于比较简单的任务来说,较大的模型可能有些过度。
这只是对这件小事的评估;我建议您看看其他事情来评估您的任务的绩效。
尽管如此,我们可以从这里继续,并添加诸如重新排名之类的策略,以便为 LLM 评估提供最佳结果。
重新排序
有很多策略可以纠正 RAG 管道中不相关的结果;重新排序就是其中之一。
重新排序基本上意味着对结果进行重新排序,以便更相关的结果位于顶部。实现此目的的策略可以是使用成对排序。
为此,您需要将一对提供给模型(可以是 LLM),并要求它根据职位描述对两个配置文件的有用性进行排名。
法学硕士 (LLM) 的成对排名 — 简化版 | 图片来自作者
您必须结合您的用例的方法来使其表现良好。
如果您对嵌入还不熟悉,我希望您能学到一些东西,如果不是新的,那么我希望您能对使用较小与较大嵌入模型的经济性有所了解,无论它们是开源的还是商业的。
对于较大的 LLM,许多闭源模型占据主导地位;但对于嵌入模型而言,情况并非如此。
你需要做的是给更小、计算效率更高的模型一个机会。
参考:
https://towardsdatascience.com/working-with-embeddings-closed-versus-open-source-39491f0b95c2