矢量嵌入是一种将真实世界数据(如文本、图像或音频)以数学方式表示为多维地图中的点的方法。这听起来非常枯燥,但有了足够的维度,矢量嵌入就可以让计算机(进而包括我们)发现和理解数据中的关系。
例如,您可能还记得“word2vec”。这是谷歌在 2013 年开发的一项革命性技术,它将单词转化为数字矢量,让机器获得了理解语义的重要能力。这一突破为自然语言处理的无数进步铺平了道路,从机器翻译到情感分析都涵盖其中。
此后,我们在此基础上发布了一个强大的文本嵌入模型 text-gecko
,使开发人员能够探索文本中丰富的语义关系。
Vertex Multimodal Embeddings API 更进一步,允许您将文本、图像和视频表达到相同的共享矢量空间中,从而在不同的模态中保留上下文和语义含义。
在这篇文章中,我们将探讨这项技术的两个实际应用案例:搜索我们团队在过去 10 年中制作的所有幻灯片,以及为艺术家设计的直观的可视化搜索工具。我们将深入探讨代码,并分享关于如何充分发挥 Multimodal Embeddings 潜力的实用建议。
最近,我们的团队正在探索如何运用最近发布的 Multimodal Embeddings API。我们认识到该工具在大型企业数据集方面的潜力,同时也想探索更多个性化和创意层面的应用。
Khyati 是我们团队的设计师,也是一位多产的插画家,她对应用这项技术更好地管理和理解她的作品特别感兴趣。用她的话说:
“艺术家通常很难根据视觉相似性或概念关键词来定位过去的作品。传统的文件组织方法根本无法胜任这项任务,尤其是在按非常见词语或抽象概念进行搜索时。”
因此,我们的开源 Multimodal Embeddings 演示
应运而生!
虽然 Khyati 的数据集比 Multimodal Embeddings API 文档中引用的百万文档规模小得多,但它为 4 月份在 Google Cloud Next 上新发布的 Cloud Firestore Vector Search 提供了理想的测试用例。
因此,我们建立了一个 Firebase 项目,并将大约 250 张 Khyati 的插图发送到 Multimodal Embeddings API。此过程生成了 1408 维浮点数组嵌入向量(最大程度提供上下文),然后我们将其存储在 Firestore 数据库中:
mm_embedding_model = MultiModalEmbeddingModel.from_pretrained("multimodalembedding")
#为每个图像创建嵌入矢量:
embedding = mm_embedding_model.get_embeddings(
image=image,
dimension=1408,
)
#创建 Firestore 用来储存,并添加到一个集合
doc = {
"name": "Illustration 1",
"imageEmbedding": Vector(embedding.image_embedding),
... # 其他元数据
}
khyati_collection.add(doc)
请务必使用 Firestore CLI 为 imageEmbedding
字段编制索引。
为简洁起见,此代码块已缩短,请查看 此 notebook 以获取完整的示例。要获取嵌入向量模块,请访问 vertexai.vision_models
package
使用 Firestore 的 K 最近邻 (KNN) 矢量搜索非常简单。只需嵌入查询(就像嵌入图像一样)并将其发送到 API:
// 我们的前端是类型脚本,但我们可以访问相同的嵌入 API:
const myQuery = 'fuzzy'; // 也可为图像
const myQueryVector = await getEmbeddingsForQuery(myQuery); // MM API 调用
const vectorQuery: VectorQuery = await khyati_collection.findNearest({
vectorField: 'imageEmbedding', // 索引字段的名称
queryVector: myQueryVector,
limit: 10, // 要检索多少个文档
distanceMeasure: 'DOT_PRODUCT' // 获取距离的三种算法之一
});
就是这样!findNearest
方法会返回与查询嵌入矢量最接近的文档以及所有相关元数据,就像标准 Firestore 查询一样。
您可以在此处
找到我们的演示或 搜索实现请注意我们采用的是@ google-cloud/firestore
NPM 库,这是这项技术当前所在位置,它不同于常规的Firebase
NPM 软件包。
如果看到这里,您还未能真正理解这些嵌入矢量是什么样子,这也情有可原:因为在这个项目开始时,我们也无法理解。我们生活在一个三维世界,所以 1408 维空间听起来相当科幻。
幸运的是,有很多工具可以降低这些矢量的维度,包括 Google PAIR 团队名为 UMAP 的精彩实现。与 t-SNE 类似,您可以使用 UMAP 轻松获取多模态嵌入矢量并将其进行三维可视化。我们在 GitHub 上提供了处理此问题的代码,包括天气图像及其“即插即用”式嵌入矢量的开源数据集。
在构建 Khyati 的演示时,我们还在探索如何以预期的规模展示 Multimodal Embeddings API 的真正实力。谷歌在嵌入领域表现出色是有原因的:毕竟,我们的许多核心搜索产品都采用了类似的技术。
但是我们怎样才能大规模地测试这一工具呢?事实是,我们团队在幻灯片创作上同样多产,在过去十年积累了数千份 Google 幻灯片演示文稿,这提供了一个很好的试验场。不妨将这次测试视为对我们团队创意历史记录的数字考古挖掘。
问题变成了:Multimodal Embeddings API 能否在这个庞大的资料库中挖掘出隐藏的宝藏?我们的团队主管能否最终找到那个丢失已久、“在某个项目设计冲刺阶段被谁写在一张便签上”的灵光乍现?我们的设计师能否轻松重新发现大家都赞不绝口的那张神奇海报?提前揭晓答案:能!
我们的大部分开发时间都花在使用 Drive 和 Slides API 来处理成千上万的演示文稿和提取每张幻灯片的缩略图上。嵌入过程本身与前述艺术家的演示几乎相同,可以总结如下:
for preso in all_decks:\ r
for slide in preso.slides:\ r
thumbnail = slides_api.getThumbnail (slide.id, preso.id)\ r
slide_embedding = mm_embedding_model.get_embeddings (\ r
image = thumbnail,\ r
dimension = 1408,\ r
)\ r
#在文档中存储 slide_embedding.image_embedding
此过程为超过 16,000 个演示文稿中总计 775,000 多张幻灯片生成了嵌入矢量。为了有效地存储和搜索这个庞大的数据集,我们转向了 Vertex AI 的矢量搜索,该工具专门为此类大规模应用而设计。
Vertex AI 的矢量搜索与 Google 搜索、YouTube 和 Play 采用同一种技术支持,可以在几毫秒内搜索数十亿个文档。它的运作原则与我们在艺术家演示中使用的 Firestore 方法相似,但规模要大得多,性能也更优越。
为了利用这项令人难以置信的强大技术,您需要在搜索之前完成一些额外的步骤:
# 矢量搜索依赖于通过代码或 UI 创建的索引,因此首先需确保上一步的嵌入矢量存储在云存储桶中,然后:
my_index = aiplatform.MatchingEngineIndex.create_tree_ah_index(
display_name = 'my_index_name',
contents_delta_uri = BUCKET_URI,
dimensions = 1408, #使用与创建索引时相同的编号
approximate_neighbors_count = 10, #
)
# 创建并将此索引部署到端点
my_index_endpoint = aiplatform.MatchingEngineIndexEndpoint.create(
display_name = "my_endpoint_name",
public_endpoint_enabled = True
)
my_index_endpoint.deploy_index(
index = my_index, deployed_index_id = "my_deployed_index_id"
)
# 一旦联机并准备就绪,您就可以像之前一样在应用程序中查询了!
response = my_index_endpoint.find_neighbors(
deployed_index_id = "my_deployed_index_id",
queries = [some_query_embedding],
num_neighbors = 10
)
这个过程类似于 Khyati 的演示,但有一个关键区别:我们创建了一个专用的矢量搜索索引,以充分发挥谷歌高效的矢量相似性搜索算法 ScaNN 的强大功能。
既然您已经大致了解了这两个选项,让我们来深入探索它们的差异。
您可能已经注意到,每种矢量搜索服务都有两种类型的算法:Firestore 的 K 最近邻算法和 Vertex AI 实现的 ScaNN 算法。我们的两个演示例子最初都使用 Firestore 进行,因为我们在团队的日常工作中通常不会用到企业级解决方案。
但 Firestore 的 KNN 搜索是一种暴力破解 O(n) 算法,这意味着它会随着您添加到索引中的文档数量增加而线性扩展。因此,一旦我们突破 1 万、1.5 甚至 2 万个文档嵌入,工作进程就开始急剧放缓。
不过,通过使用在“预过滤”步骤中采用的 Firestore 标准查询谓词,可以缓解这种降速问题。因此,您可以执行 where
查询,将集合限制在相关文档范围,而不是搜索已编入索引的每个嵌入矢量。但这需要在要用于筛选的字段上使用另一个复合索引。
#创建额外索引很简单,但仍需要考虑\ r
gcloud alpha firestore indexes composite create\ r --collection-group
= all_slides\ r --query-scope
= COLLECTION\ r --field-config
= order = ASCENDING, field-path = "project" # 额外字段\ r
--field-config field-path = slide_embedding, vector-config = '{"dimension":"1408", "flat": "{}"}'
ScaNN 是 Google Research 的一项突破成果,于 2020 年公开发布。该技术与 KNN 类似,但依赖于基于“近似”位置的智能索引(如“可扩展近似最近邻”)。
这种技术可以在毫秒内查询数十亿个文档,但这种能力是有代价的,特别是与 Firestore 读/写相比。此外,默认情况下索引很小,为简单的键值对;所以需要在查询到最近邻结果返回后,对其他集合或表进行二次查询。但是,对于我们的 775,000 张幻灯片来说,大约 100 毫秒的查找 + 大约 50 毫秒的 Firestore 元数据读取速度仍然比 Cloud Firestore Vector Search 原生提供的速度快几个数量级。
还有一些关于如何通过混合搜索方法将矢量搜索与传统关键词搜索相结合的精彩文档。欢迎点击此处阅读更多相关信息。
除快速格式化以外
为 Vertex AI 创建索引还需要单独的jsonl
key/value 文件格式;从我们最初的 Firestore 实现转换成这种格式费了一些功夫。如果您不确定要使用哪种,则可能需要将嵌入矢量编写为任一系统都可以轻松接收的不受限格式,以免去 LevelDB Firestore 导出带来的巨大麻烦。
如果完全采用云托管的解决方案不适合您,您仍然可以通过本地解决方案利用 Multimodal Embeddings API 的强大功能。
我们还测试了一个名为 sqlite-vec
的新库,这是一个非常快速、零依赖的 SQLite 实现,几乎可以在任何地方运行,并可轻松处理 Multimodal Embeddings API 返回的 1408 维矢量。移植我们的 20,000 多张幻灯片进行测试,结果显示查找用时的范围为约 200 毫秒。您仍然需要在线创建文档和查询嵌入矢量,但创建和存储完成后,您就可以随时随地进行搜索。
从 Word2Vec 的基础到今天的 Multimodal Embeddings API,构建自己的多模态人工智能系统来搜索信息有无数激动人心的全新可能。
如何选择合适的矢量搜索解决方案取决于您的需求。Firebase 为小型项目提供了易用且经济高效的选项,而 Vertex AI 则提供了处理大型数据集和实现毫秒级搜索时间所需的可扩展性和性能。对于本地开发,像 sqlite-vec 这样的工具可以让您在离线状态下充分利用嵌入矢量的大部分强大功能。
准备好探索多模态搜索的未来了吗?欢迎深入了解我们在 GitHub 上的开源 Multimodal Embeddings 演示、试验代码,并分享您自己的作品。期待看到您用这些工具建构的成果!。