我们将使用欧盟 AI 行为作为嵌入模型比较的数据语料库。图片由 Dall-E 3 提供。
OpenAI 最近发布了他们的新一代嵌入模型,称为嵌入 v3,他们将其描述为性能最高的嵌入模型,具有更高的多语言性能。这些模型分为两类:一类较小,称为 ,另一类更大、功能更强大,称为 text-embedding-3-small
text-embedding-3-large
。
关于这些模型的设计和训练方式的信息很少。作为他们之前的嵌入模型版本(2022 年 12 月的 ada-002 模型类),OpenAI 再次选择闭源方法,其中模型只能通过付费 API 访问。
但是,这些表演是否如此出色,以至于值得付出代价?
这篇文章的动机是实证比较这些新模型与开源模型的性能。我们将依赖于数据检索工作流,其中必须根据用户查询找到语料库中最相关的文档。
我们的语料库将是《欧洲人工智能法案》,该法案目前正处于验证的最后阶段。该语料库的一个有趣特点是,除了是全球首个关于人工智能的法律框架外,它还支持24种语言。这样就可以比较不同语言系列的数据检索的准确性。
这篇文章将经历以下两个主要步骤:
- 从多语言文本语料库生成自定义合成问答数据集
- 在此自定义数据集上比较 OpenAI 和最先进的开源嵌入模型的准确性。
重现本文中介绍的结果的代码和数据在此 Github 存储库中提供。请注意,以欧盟人工智能法案为例,本文中遵循的方法可以适用于其他数据语料库。
生成自定义 Q/A 数据集
首先,我们首先在自定义数据上生成一个问题和答案 (Q/A) 数据集,该数据集将用于评估不同嵌入模型的性能。生成自定义 Q/A 数据集的好处是双重的。首先,它通过确保数据集没有成为嵌入模型训练的一部分来避免偏差,这可能发生在参考基准(如 MTEB)上。其次,它允许根据特定的数据语料库定制评估,例如,这可能与检索增强应用程序 (RAG) 相关。
我们将遵循 Llama Index 在其文档中建议的简单过程。语料库首先被拆分为一组块。然后,对于每个块,通过大型语言模型 ()LLM 生成一组合成问题,使得答案位于相应的块中。该过程如下图所示:
为您的数据生成问题/答案数据集,来自 Llama Index 的方法
使用Llama Index等数据框架LLM,实施此策略非常简单。可以使用高级函数方便地执行语料库的加载和文本的拆分,如以下代码所示。
from llama_index.readers.web import SimpleWebPageReader
from llama_index.core.node_parser import SentenceSplitter
language = "EN"
url_doc = "https://eur-lex.europa.eu/legal-content/"+language+"/TXT/HTML/?uri=CELEX:52021PC0206"
documents = SimpleWebPageReader(html_to_text=True).load_data([url_doc])
parser = SentenceSplitter(chunk_size=1000)
nodes = parser.get_nodes_from_documents(documents, show_progress=True)
在此示例中,语料库是英文版的《欧盟人工智能法案》,使用此官方 URL 直接从 Web 获取。我们使用 2021 年 4 月的草稿版本,因为最终版本尚未适用于所有欧洲语言。在此版本中,可以在 URL 中将英语替换为其他 23 种欧盟官方语言中的任何一种,以检索其他语言的文本(BG 表示保加利亚语,ES 表示西班牙语,CS 表示捷克语,依此类推)。
下载欧盟 24 种官方语言的欧盟人工智能法案链接(来自欧盟官方网站)
我们使用 SentenceSplitter 对象将文档拆分为 1000 个标记的块。对于英语,这会产生大约 100 个块。
然后,每个块都作为上下文提供给以下提示(Llama 索引库中建议的默认提示):
prompts={}
prompts["EN"] = """\
Context information is below.
---------------------
{context_str}
---------------------
Given the context information and not prior knowledge, generate only questions based on the below query.
You are a Teacher/ Professor. Your task is to setup {num_questions_per_chunk} questions for an upcoming quiz/examination.
The questions should be diverse in nature across the document. Restrict the questions to the context information provided."
该提示旨在生成有关文档块的问题,就好像老师正在准备即将到来的测验一样。为每个块生成的问题数作为参数“num_questions_per_chunk”传递,我们将其设置为两个。然后,可以通过调用 Llama 索引库中的generate_qa_embedding_pairs来生成问题:
from llama_index.llms import OpenAI
from llama_index.legacy.finetuning import generate_qa_embedding_pairs
qa_dataset = generate_qa_embedding_pairs(
llm=OpenAI(model="gpt-3.5-turbo-0125",additional_kwargs={'seed':42}),
nodes=nodes,
qa_generate_prompt_tmpl = prompts[language],
num_questions_per_chunk=2
)
我们依靠 OpenAI 的 GPT-3.5-turbo-0125 模式来完成这项任务,根据 OpenAI 的说法,该模式是该系列的旗舰型号,支持 16K 上下文窗口并针对对话进行了优化(https://platform.openai.com/docs/models/gpt-3-5-turbo)。
生成的对象“qa_dataset”包含问题和答案(块)对。作为生成问题的示例,以下是前两个问题的结果(其中“答案”是文本的第一块):
1) 根据解释性备忘录,制定人工智能统一规则(《人工智能法》)的法规提案的主要目标是什么?2) 如上下文信息所述,关于人工智能法规的提案如何旨在解决与使用人工智能相关的风险,同时促进人工智能在欧盟的采用?
块和问题的数量取决于语言,从英语的大约 100 个块和 200 个问题到匈牙利语的 200 个块和 400 个问题不等。
OpenAI 嵌入模型评估
我们的评估功能遵循骆驼指数文档,包括两个主要步骤。首先,所有答案(文档块)的嵌入都存储在 VectorStoreIndex 中,以便进行高效检索。然后,评估函数遍历所有查询,检索前 k 个最相似的文档,并根据 MRR(平均倒数排名)评估检索的准确性。
def evaluate(dataset, embed_model, insert_batch_size=1000, top_k=5):
# Get corpus, queries, and relevant documents from the qa_dataset object
corpus = dataset.corpus
queries = dataset.queries
relevant_docs = dataset.relevant_docs
# Create TextNode objects for each document in the corpus and create a VectorStoreIndex to efficiently store and retrieve embeddings
nodes = [TextNode(id_=id_, text=text) for id_, text in corpus.items()]
index = VectorStoreIndex(
nodes, embed_model=embed_model, insert_batch_size=insert_batch_size
)
retriever = index.as_retriever(similarity_top_k=top_k)
# Prepare to collect evaluation results
eval_results = []
# Iterate over each query in the dataset to evaluate retrieval performance
for query_id, query in tqdm(queries.items()):
# Retrieve the top_k most similar documents for the current query and extract the IDs of the retrieved documents
retrieved_nodes = retriever.retrieve(query)
retrieved_ids = [node.node.node_id for node in retrieved_nodes]
# Check if the expected document was among the retrieved documents
expected_id = relevant_docs[query_id][0]
is_hit = expected_id in retrieved_ids # assume 1 relevant doc per query
# Calculate the Mean Reciprocal Rank (MRR) and append to results
if is_hit:
rank = retrieved_ids.index(expected_id) + 1
mrr = 1 / rank
else:
mrr = 0
eval_results.append(mrr)
# Return the average MRR across all queries as the final evaluation metric
return np.average(eval_results)
嵌入模型通过“embed_model”参数传递给评估函数,对于 OpenAI 模型,该参数是使用模型名称和模型维度初始化的 OpenAIEmbedding 对象。
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding(model=model_spec['model_name'],
dimensions=model_spec['dimensions'])
dimensions
API 参数可以缩短嵌入(即从序列末尾删除一些数字),而不会使嵌入失去其概念表示属性。例如,OpenAI 在他们的公告中建议,在 MTEB 基准测试中,嵌入可以缩短到 256 的大小,同时仍然优于未缩短的 text-embedding-ada-002
嵌入大小为 1536。
我们在四个不同的 OpenAI 嵌入模型上运行了评估功能:
- 的两个版本
text-embedding-3-large
:一个具有最低可能的维度 (256),另一个具有最高可能的维度 (3072)。它们被称为“OAI-large-256”和“OAI-large-3072”。 - OAI-small:
text-embedding-3-small
嵌入模型,维度为 1536。 - OAI-ada-002:旧
text-embedding-ada-002
模型,维度为 1536。
每个模型都用四种不同的语言进行评估:英语(EN),法语(FR),捷克语(CS)和匈牙利语(胡),分别涵盖日耳曼语,罗曼语,斯拉夫语和乌拉尔语的例子。
embeddings_model_spec = {
}
embeddings_model_spec['OAI-Large-256']={'model_name':'text-embedding-3-large','dimensions':256}
embeddings_model_spec['OAI-Large-3072']={'model_name':'text-embedding-3-large','dimensions':3072}
embeddings_model_spec['OAI-Small']={'model_name':'text-embedding-3-small','dimensions':1536}
embeddings_model_spec['OAI-ada-002']={'model_name':'text-embedding-ada-002','dimensions':None}
results = []
languages = ["EN", "FR", "CS", "HU"]
# Loop through all languages
for language in languages:
# Load dataset
file_name=language+"_dataset.json"
qa_dataset = EmbeddingQAFinetuneDataset.from_json(file_name)
# Loop through all models
for model_name, model_spec in embeddings_model_spec.items():
# Get model
embed_model = OpenAIEmbedding(model=model_spec['model_name'],
dimensions=model_spec['dimensions'])
# Assess embedding score (in terms of MRR)
score = evaluate(qa_dataset, embed_model)
results.append([language, model_name, score])
df_results = pd.DataFrame(results, columns = ["Language" ,"Embedding model", "MRR"])
MRR的最终准确性报告如下:
OpenAI 模型的性能摘要
正如预期的那样,对于大型模型,在更大的嵌入尺寸 3072 下观察到更好的性能。然而,与小型和传统的 Ada 型号相比,大型型号比我们预期的要小。为了进行比较,我们还在下面报告了 OpenAI 模型在 MTEB 基准测试中获得的性能。
OpenAI 嵌入模型的性能,如其官方公告中所报道的那样
有趣的是,在我们的评估中,大型、小型和 Ada 模型之间的性能差异远不如 MTEB 基准测试那么明显,这反映了这样一个事实,即在大型基准测试中观察到的平均性能并不一定反映在自定义数据集上获得的平均性能。
开源嵌入模型的评估
围绕嵌入的开源研究非常活跃,并且定期发布新模型。了解最新发布型号的好地方是 Hugging Face 😊 MTEB 排行榜。
为了进行比较,我们选择了一组最近(2024 年)发布的四种嵌入模型。选择的标准是他们在 MTEB 排行榜上的平均分数以及他们处理多语言数据的能力。下面总结了所选型号的主要特征。
- E5-Mistral-7B-instruct(E5-mistral-7b):Microsoft 的这个 E5 嵌入模型是从 Mistral-7B-v0.1 初始化的,并在多语言数据集的混合上进行了微调。该模型在 MTEB 排行榜上表现最好,但也是迄今为止最大的模型 (14GB)。
- multilingual-e5-large-instruct(ML-E5-large):Microsoft的另一个E5模型,旨在更好地处理多语言数据。它是从 xlm-roberta-large 初始化的,并在多语言数据集的混合上进行训练。它比 E5-Mistral 小得多(10 倍),但上下文大小也小得多 (514)。
- BGE-M3:该模型由北京人工智能研究院设计,是他们最先进的多语言数据嵌入模型,支持 100 多种工作语言。截至 2024 年 2 月 22 日,它尚未在 MTEB 排行榜上进行基准测试。
- nomic-embed-text-v1(Nomic-Embed):该模型由 Nomic 设计,声称性能优于 OpenAI Ada-002 和 text-embedding-3-small,但大小仅为 0.55GB。有趣的是,该模型是第一个完全可重复和可审计的模型(开放数据和开源训练代码)。
用于评估这些开源模型的代码类似于用于 OpenAI 模型的代码。主要变化在于模型规范,其中必须指定其他详细信息,例如最大上下文长度和池化类型。然后,我们针对四种语言中的每一种评估每个模型:
embeddings_model_spec = {
}
embeddings_model_spec['E5-mistral-7b']={'model_name':'intfloat/e5-mistral-7b-instruct','max_length':32768, 'pooling_type':'last_token',
'normalize': True, 'batch_size':1, 'kwargs': {'load_in_4bit':True, 'bnb_4bit_compute_dtype':torch.float16}}
embeddings_model_spec['ML-E5-large']={'model_name':'intfloat/multilingual-e5-large','max_length':512, 'pooling_type':'mean',
'normalize': True, 'batch_size':1, 'kwargs': {'device_map': 'cuda', 'torch_dtype':torch.float16}}
embeddings_model_spec['BGE-M3']={'model_name':'BAAI/bge-m3','max_length':8192, 'pooling_type':'cls',
'normalize': True, 'batch_size':1, 'kwargs': {'device_map': 'cuda', 'torch_dtype':torch.float16}}
embeddings_model_spec['Nomic-Embed']={'model_name':'nomic-ai/nomic-embed-text-v1','max_length':8192, 'pooling_type':'mean',
'normalize': True, 'batch_size':1, 'kwargs': {'device_map': 'cuda', 'trust_remote_code' : True}}
results = []
languages = ["EN", "FR", "CS", "HU"]
# Loop through all models
for model_name, model_spec in embeddings_model_spec.items():
print("Processing model : "+str(model_spec))
# Get model
tokenizer = AutoTokenizer.from_pretrained(model_spec['model_name'])
embed_model = AutoModel.from_pretrained(model_spec['model_name'], **model_spec['kwargs'])
if model_name=="Nomic-Embed":
embed_model.to('cuda')
# Loop through all languages
for language in languages:
# Load dataset
file_name=language+"_dataset.json"
qa_dataset = EmbeddingQAFinetuneDataset.from_json(file_name)
start_time_assessment=time.time()
# Assess embedding score (in terms of hit rate at k=5)
score = evaluate(qa_dataset, tokenizer, embed_model, model_spec['normalize'], model_spec['max_length'], model_spec['pooling_type'])
# Get duration of score assessment
duration_assessment = time.time()-start_time_assessment
results.append([language, model_name, score, duration_assessment])
df_results = pd.DataFrame(results, columns = ["Language" ,"Embedding model", "MRR", "Duration"])
MRR的准确性报告如下。
开源模型的性能摘要
事实证明,BGE-M3 提供了最好的性能,其次是 ML-E5-Large、E5-mistral-7b 和 Nomic-Embed。BGE-M3 模型尚未在 MTEB 排行榜上进行基准测试,我们的结果表明它的排名可能高于其他模型。同样有趣的是,虽然 BGE-M3 针对多语言数据进行了优化,但它在英语方面的表现也优于其他型号。
我们还在下面报告了每个嵌入模型的处理时间。
浏览英语问答数据集的处理时间(以秒为单位)
E5-mistral-7b 比其他型号大 10 倍以上,毫不奇怪,它是迄今为止最慢的型号。
结论
让我们将八个测试模型的性能并排放在一个数字中。
八款测试样本的性能摘要
从这些结果中得出的主要结论是:
- 最佳性能是通过开源模型获得的。由北京人工智能研究院开发的BGE-M3模型成为表现最好的模型。该模型的上下文长度与 OpenAI 模型 (8K) 相同,大小为 2.2GB。
- OpenAI 范围内的一致性。大型 (3072)、小型和传统 OpenAI 模型的性能非常相似。然而,减小大型模型(256)的嵌入尺寸会导致性能下降。
- 语言敏感性。几乎所有型号(ML-E5-large除外)在英语方面表现最好。在捷克语和匈牙利语等语言中观察到表演的显着差异。
因此,您应该订阅付费的 OpenAI 还是托管开源嵌入模型?
OpenAI 最近的价格调整使其 API 的访问变得更加实惠,现在的成本为每百万个代币 0.13 美元。因此,每月处理 100 万个查询(假设每个查询涉及大约 1K 个令牌)的成本约为 130 美元。因此,根据您的用例,租用和维护自己的嵌入服务器可能不划算。
然而,成本效益并不是唯一的考虑因素。可能还需要考虑其他因素,例如延迟、隐私和对数据处理工作流程的控制。开源模型具有完全数据控制的优势,增强了隐私和定制。另一方面,OpenAI 的 API 也出现了延迟问题,有时会导致响应时间延长。
总之,在开源模型和 OpenAI 等专有解决方案之间进行选择并不能给出直接的答案。开源嵌入提供了一个引人注目的选择,它将性能与对数据的更大控制相结合。相反,OpenAI 的产品可能仍然会吸引那些优先考虑便利性的人,尤其是在隐私问题是次要的情况下。
相关链接
- 配套 Github 存储库:https://github.com/Yannael/multilingual-embeddings
- 你想知道的关于句子嵌入的一切(也许还有更多)
- OpenAI 博客公告:新的嵌入模型和 API 更新
- 嵌入:OpenAI 指南
- MTEB:海量文本嵌入基准测试和拥抱脸部 MTEB 排行榜
- 文本嵌入:综合指南
- 检索增强生成 (RAG) 的从业者指南
- 如何为您的 RAG 找到最佳的多语言嵌入模型
笔记:
- 除非另有说明,否则所有图片均由作者提供
- 欧盟人工智能法案草案根据欧盟委员会基于第2011/833/EU号决定的文件重用政策发布,可以重新用于商业或非商业目的。