如何获得正确的向量嵌入
本文最初发表于《The New Stack》,经授权在此转发。
全面介绍向量嵌入以及如何使用流行的开源模型生成向量嵌入。
图片作者:Денис Марчук 来自 Pixabay
在处理语义相似性时,向量嵌入至关重要。然而,向量只是一系列数字;向量嵌入则是代表输入数据的一系列数字。使用向量嵌入,我们可以通过将非结构化数据转换成一系列数字,来构建非结构化数据或处理任何类型的数据。这种方法允许我们对输入数据进行数学操作,而不是依赖定性比较。
向量嵌入对很多任务都有影响,尤其是语义搜索。不过,在使用之前,获得适当的向量嵌入至关重要。例如,如果使用图像模型对文本进行向量嵌入,或者反之亦然,很可能会得到很差的结果。
在本篇文章中,我们将了解矢量嵌入的含义,如何使用不同的模型为您的应用生成合适的矢量嵌入,以及如何通过Milvus和 ZillizCloud 等矢量数据库充分利用矢量嵌入。
如何创建向量嵌入?
既然了解了向量嵌入的重要性,我们就来了解一下向量嵌入是如何工作的。向量嵌入是深度学习模型中输入数据的内部表示,也称为嵌入模型或深度神经网络。那么,我们如何提取这些信息呢?
我们通过去掉最后一层,取倒数第二层的输出来获得向量。神经网络的最后一层通常会输出模型的预测结果,因此我们取倒数第二层的输出。向量嵌入是馈送给神经网络预测层的数据。
向量嵌入的维度相当于模型中倒数第二层的大小,因此,可以与向量的大小或长度互换。常见的向量维度包括 384(由 Sentence Transformers Mini-LM 生成)、768(由 Sentence Transformers MPNet 生成)、1536(由 OpenAI 生成)和 2048(由 ResNet-50 生成)。
向量嵌入是什么意思?
曾经有人问我向量嵌入中每个维度的含义。简而言之,没什么意义。向量嵌入中的单个维度没有任何意义,因为它太抽象,无法确定其含义。但是,当我们把所有维度放在一起时,它们就提供了输入数据的语义。
向量的维度是不同属性的高层次抽象表示。所代表的属性取决于训练数据和模型本身。文本模型和图像模型会生成不同的嵌入,因为它们是针对根本不同的数据类型进行训练的。即使是不同的文本模型也会生成不同的 Embeddings。有时,它们的大小不同;其他时候,它们所代表的属性也不同。例如,针对法律数据训练的模型与针对医疗数据训练的模型会学习到不同的东西。我曾在比较向量嵌入的文章中探讨过这个话题。
生成正确的向量嵌入
如何获得正确的向量嵌入?首先要确定要嵌入的数据类型。本节涵盖嵌入五种不同类型的数据:图像、文本、音频、视频和多模态数据。我们在此介绍的所有模型都是开源的,来自 Hugging Face 或 PyTorch。
图像嵌入
2012 年,AlexNet 问世后,图像识别技术迅猛发展。从那时起,计算机视觉领域取得了无数进步。最新的著名图像识别模型是 ResNet-50,它是基于前 ResNet-34 架构的 50 层深度残差网络。
残差神经网络(ResNet)利用快捷连接解决了深度卷积神经网络中的梯度消失问题。这些连接允许前层的输出直接进入后层,而不经过所有中间层,从而避免了梯度消失问题。这种设计使得 ResNet 的复杂度低于 VGGNet(视觉几何组),后者是以前性能最好的卷积神经网络。
我推荐两个 ResNet-50 的实现作为示例: Hugging Face 上的 ResNet 50和PyTorch Hub 上的 ResNet 50。虽然网络相同,但获取 Embeddings 的过程不同。
下面的代码示例演示了如何使用 PyTorch 获取向量嵌入。首先,我们从 PyTorch Hub 加载模型。接下来,我们移除最后一层,并调用.eval()
来指示模型的行为,就像它正在运行推理一样。然后,embed
函数生成向量嵌入。
# Load the embedding model with the last layer removed
model = torch.hub.load('pytorch/vision:v0.10.0', 'resnet50', pretrained=True) model = torch.nn.Sequential(*(list(model.children())[:-1]))
model.eval()
def embed(data):
with torch.no_grad():
output = model(torch.stack(data[0])).squeeze()
return output
HuggingFace 使用的设置略有不同。下面的代码演示了如何从 Hugging Face 获取向量嵌入。首先,我们需要transformers
库中的特征提取器和模型。我们将使用特征提取器为模型获取输入,并使用模型获取输出和提取最后的隐藏状态。
# Load model directly
from transformers import AutoFeatureExtractor, AutoModelForImageClassification
extractor = AutoFeatureExtractor.from_pretrained("microsoft/resnet-50")
model = AutoModelForImageClassification.from_pretrained("microsoft/resnet-50")
from PIL import Image
image = Image.open("<image path>")
# image = Resize(size=(256, 256))(image)
inputs = extractor(images=image, return_tensors="pt")
# print(inputs)
outputs = model(**inputs)
vector_embeddings = outputs[1][-1].squeeze()
文本嵌入
自人工智能发明以来,工程师和研究人员一直在进行自然语言和人工智能方面的实验。最早的一些实验包括
- 第一个人工智能治疗师聊天机器人 ELIZA。
- 约翰-塞尔的 "中文房间"(Chinese Room),这是一个思想实验,研究中英文之间的翻译能力是否需要对语言的理解。
- 英语和俄语之间基于规则的翻译。
人工智能对自然语言的操作已经从基于规则的 Embeddings 有了很大的发展。从初级神经网络开始,我们通过 RNN 增加了递归关系,以跟踪时间步骤。在此基础上,我们使用变换器来解决序列转换问题。
变换器由编码器、注意矩阵和解码器组成,编码器将输入编码为代表状态的矩阵。解码器对状态和注意力矩阵进行解码,以预测正确的下一个标记,从而完成输出序列。GPT-3 是迄今为止最流行的语言模型,由严格的解码器组成。它们对输入进行编码,并预测正确的下一个(多个)标记。
下面是 Hugging Face 从sentence-transformers
库中提供的两个模型,除了 OpenAI 的 Embeddings 之外,你还可以使用它们:
- MiniLM-L6-v2:一个 384 维模型
- MPNet-Base-V2:768 维模型
您可以以相同的方式访问这两种模型的嵌入式数据。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("<model-name>")
vector_embeddings = model.encode(“<input>”)
多模态嵌入模型
多模态模型不如图像或文本模型完善。它们通常将图像与文本联系起来。
最有用的开源模型是CLIP VIT,它是一种图像到文本模型。您可以像访问图像模型一样访问 CLIP VIT 的嵌入模型,如下代码所示。
# Load model directly
from transformers import AutoProcessor, AutoModelForZeroShotImageClassification
processor = AutoProcessor.from_pretrained("openai/clip-vit-large-patch14")
model = AutoModelForZeroShotImageClassification.from_pretrained("openai/clip-vit-large-patch14")
from PIL import Image
image = Image.open("<image path>")
# image = Resize(size=(256, 256))(image)
inputs = extractor(images=image, return_tensors="pt")
# print(inputs)
outputs = model(**inputs)
vector_embeddings = outputs[1][-1].squeeze()
音频嵌入
与文本或图像人工智能相比,音频人工智能受到的关注较少。音频最常见的使用案例是呼叫中心、医疗技术和无障碍等行业的语音转文本。一种流行的语音转文本开源模型是OpenAI 的 Whisper。下面的代码展示了如何从语音到文本模型中获取向量嵌入。
import torch
from transformers import AutoFeatureExtractor, WhisperModel
from datasets import load_dataset
model = WhisperModel.from_pretrained("openai/whisper-base")
feature_extractor = AutoFeatureExtractor.from_pretrained("openai/whisper-base")
ds = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")
inputs = feature_extractor(ds[0]["audio"]["array"], return_tensors="pt")
input_features = inputs.input_features
decoder_input_ids = torch.tensor([[1, 1]]) * model.config.decoder_start_token_id
vector_embedding = model(input_features, decoder_input_ids=decoder_input_ids).last_hidden_state
视频嵌入
视频嵌入比音频或图像嵌入更为复杂。在处理视频时,必须采用多模态方法,因为视频包括同步音频和图像。DeepMind 的多模态感知器就是一种流行的视频模型。本笔记本教程展示了如何使用该模型对视频进行分类。
要获取输入的 Embeddings,请使用outputs[1][-1].squeeze()
从笔记本中显示的代码中获取,而不是删除输出。我在autoencode
函数中突出显示了这一代码片段。
def autoencode_video(images, audio):
# only create entire video once as inputs
inputs = {'image': torch.from_numpy(np.moveaxis(images, -1, 2)).float().to(device),
'audio': torch.from_numpy(audio).to(device),
'label': torch.zeros((images.shape[0], 700)).to(device)}
nchunks = 128
reconstruction = {}
for chunk_idx in tqdm(range(nchunks)):
image_chunk_size = np.prod(images.shape[1:-1]) // nchunks
audio_chunk_size = audio.shape[1] // SAMPLES_PER_PATCH // nchunks
subsampling = {
'image': torch.arange(
image_chunk_size * chunk_idx, image_chunk_size * (chunk_idx + 1)),
'audio': torch.arange(
audio_chunk_size * chunk_idx, audio_chunk_size * (chunk_idx + 1)),
'label': None,
}
# forward pass
with torch.no_grad():
outputs = model(inputs=inputs, subsampled_output_points=subsampling)
output = {k:v.cpu() for k,v in outputs.logits.items()}
reconstruction['label'] = output['label']
if 'image' not in reconstruction:
reconstruction['image'] = output['image']
reconstruction['audio'] = output['audio']
else:
reconstruction['image'] = torch.cat(
[reconstruction['image'], output['image']], dim=1)
reconstruction['audio'] = torch.cat(
[reconstruction['audio'], output['audio']], dim=1)
vector_embeddings = outputs[1][-1].squeeze()
# finally, reshape image and audio modalities back to original shape
reconstruction['image'] = torch.reshape(reconstruction['image'], images.shape)
reconstruction['audio'] = torch.reshape(reconstruction['audio'], audio.shape)
return reconstruction
return None
使用向量数据库存储、索引和搜索向量 embeddings
既然我们已经了解了什么是向量嵌入,以及如何使用各种功能强大的嵌入模型生成向量嵌入,下一个问题就是如何存储并利用它们。向量数据库就是答案。
像Milvus和Zilliz Cloud这样的矢量数据库就是专门为通过向量嵌入在海量非结构化数据集上进行存储、索引和搜索而构建的。它们也是各种人工智能堆栈最关键的基础设施之一。
向量数据库通常使用近似近邻(ANN)算法来计算查询向量与数据库中存储的向量之间的空间距离。两个向量的位置越近,相关性就越高。然后,该算法会找出前 k 个近邻,并将它们提供给用户。
向量数据库在LLM 检索增强生成(RAG)、问答系统、推荐系统、语义搜索以及图像、视频和音频相似性搜索等用例中很受欢迎。
要了解有关向量嵌入、非结构化数据和向量数据库的更多信息,请从向量数据库 101系列开始。
总结
向量是处理非结构化数据的强大工具。使用向量,我们可以根据语义相似性对不同的非结构化数据进行数学比较。选择正确的向量嵌入模型对于为任何应用构建向量搜索引擎都至关重要。
在本篇文章中,我们了解到向量嵌入是神经网络中输入数据的内部表示。因此,它们高度依赖于网络架构和用于训练模型的数据。不同的数据类型(如图像、文本和音频)需要特定的模型。幸运的是,许多预训练的开源模型可供使用。在本篇文章中,我们介绍了五种最常见数据类型的模型:图像、文本、多模态、音频和视频。此外,如果你想充分利用向量 Embeddings,向量数据库是最受欢迎的工具。
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word