译者 | 朱先忠
审校 | 重楼
出品 | 51CTO技术栈(微信号:blog51cto)
本文对OpenAI的多个嵌入模型与多家开源的多语言嵌入模型进行了综合比较,帮助你最终选择最适合自己的大数据开发模型。
1.引言
OpenAI最近发布了他们的新一代嵌入模型,称为嵌入v3,他们将其描述为性能最高的嵌入模型,而且具有更高的多语言性能。这些模型分为两类:一类较小,称为text-embedding-3-mall;另一类较大,功能也更强大,称为text-embedding-3-large。
关于这些模型的设计和训练方式,披露的信息很少。作为他们之前发布的嵌入模型(2022年12月,通过模型类ada-002的方式呈现),OpenAI公司再次选择了一种闭源市场策略,即这些模型只能通过付费的API方式进行访问。
但是,这些模型的性能是否如此优秀以至于值得人们以付费方式来使用呢?
这篇文章的目的是将这些新模型的性能与开源模型的性能进行实证比较。我们将使用数据检索工作流进行比较;在该工作流中,必须在给定用户查询的情况下找到语料库中最相关的文档。
我们的语料库将是《欧盟人工智能法案》(https://artificialintelligenceact.eu/),该法案目前正处于最后的验证阶段。这个语料库的一个有趣的特点是,除了是世界上第一个人工智能的法律框架外,它还有24种语言版本。这使得可以比较不同语言族的数据检索准确性。
本文将采取以下两个主要步骤:
- 从多语言文本语料库生成自定义合成问答数据集;
- 在这个自定义数据集上比较OpenAI和当今最先进的开源嵌入模型的准确性。
2.生成自定义问答数据集
让我们首先从生成自定义数据的问答数据集(Q/a)开始,该数据集将用于评估不同嵌入模型的性能。生成自定义问答数据集的好处有两个。
首先,它通过确保数据集没有成为嵌入模型训练的一部分来避免偏差,这可能发生在参考基准,如MTEB:https://huggingface.co/spaces/mteb/leaderboard)上。
其次,它允许根据特定的数据语料库进行评估,例如在检索增强应用程序(RAG)的情况下,这可能是相关的。
我们将遵循Llama Index在其文档(https://blog.llamaindex.ai/fine-tuning-embeddings-for-rag-with-synthetic-data-e534409a3971)中建议的简单流程。语料库首先被分成一组组的块。然后,对于每个块,通过大型语言模型(LLM)生成一组合成问题,使得答案位于相应的块中。该过程如下所示:
图片
通过Llama Index方法为你的数据生成问答数据集
使用LLM的数据框架(如Llama Index)实现上述策略非常简单。使用高级函数可以方便地加载语料库和分割文本,如以下代码所示:
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(https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A52021PC0206)可直接从网络上获取。
我们使用2021年4月的草案版本,因为最终版本尚未适用于所有欧洲语言。在这个版本中,URL中的英语可以替换为其他23种欧盟官方语言中的任何一种,以检索不同语言的文本(保加利亚语为BG,西班牙语为ES,捷克语为CS,等等)。
图片
下载24种欧盟官方语言的《欧盟人工智能法案》链接(来自欧盟官方网站)
我们使用PensioneSplitter对象将文档拆分为1000个标记的块。对于英语来说,这会产生大约100个块。
然后,将每个块作为上下文提供给以下提示(Llama索引库中建议的默认提示:
https://github.com/run-llama/llama_index/blob/c058f2531ea86ee74822cb1421ceaeee7098a99f/llama_index/finetuning/embeddings/common.py#L51):
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”传递,我们将其值设置为2。然后,可以通过调用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”中包含问题和答案(组块)对。作为生成问题的示例,以下是前两个问题的结果(“answer(答案)”是第一块文本):
1)What are the main objectives of the proposal for a Regulation laying down harmonised rules on artificial intelligence (Artificial Intelligence Act) according to the explanatory memorandum?
大致中文意思是:根据解释性备忘录,制定人工智能统一规则的法规(《人工智能法》)提案的主要目标是什么?
2)How does the proposal for a Regulation on artificial intelligence aim to address the risks associated with the use of AI while promoting the uptake of AI in the European Union, as outlined in the context information?
大致中文意思是:如上下文信息所述,人工智能条例的提案如何旨在解决与人工智能使用相关的风险,同时促进欧盟对人工智能的吸收?
语块和问题的数量取决于语言,从英语的大约100个语块和200个问题到匈牙利语的200个语块或400个问题。
3.OpenAI嵌入模型的评估
我们的评估功能遵循Llama Index文档,如下:
(https://docs.llamaindex.ai/en/stable/examples/finetuning/embeddings/finetune_embedding.html),
包括两个主要步骤——
首先,所有答案(文档块)的嵌入都存储在VectorStoreIndex中,以实现高效检索。然后,评估函数在所有查询上循环,检索前k个最相似的文档,并根据MRR(平均倒数排名)评估检索的准确性。
def evaluate(dataset, embed_model, insert_batch_size=1000, top_k=5):
# 从qa_dataset对象获取语料库、查询和相关文档
corpus = dataset.corpus
queries = dataset.queries
relevant_docs = dataset.relevant_docs
# 为语料库中的每个文档创建TextNode对象,并创建VectorStoreIndex以有效地存储和检索嵌入
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)
#准备收集评估结果
eval_results = []
# 对数据集中的每个查询进行迭代,以评估检索性能
for query_id, query in tqdm(queries.items()):
# 检索当前查询的前_k个最相似的文档,并提取检索到的文档的ID
retrieved_nodes = retriever.retrieve(query)
retrieved_ids = [node.node.node_id for node in retrieved_nodes]
#检查所需文档是否在检索到的文档中
expected_id = relevant_docs[query_id][0]
is_hit = expected_id in retrieved_ids # 假设每个查询有1个相关文档
# 计算平均倒数排名(MRR)并添加到结果集中
if is_hit:
rank = retrieved_ids.index(expected_id) + 1
mrr = 1 / rank
else:
mrr = 0
eval_results.append(mrr)
#返回所有查询的平均MRR作为最终评估度量
return np.average(eval_results)
嵌入模型通过“embed_mode”参数传递给评估函数。对于OpenAI模型,该参数是用模型名称和模型维度初始化的OpenAIEmbedding对象。
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding(model=model_spec['model_name'],
dimensinotallow=model_spec['dimensions'])
API参数dimensions可以缩短嵌入(即从序列的末尾移除一些数字),而不会丢失嵌入的概念表示属性。例如,OpenAI在其公告中建议,在MTEB基准上,嵌入可以缩短到256的大小,同时仍然优于1536大小的未缩短的text-embedding-ada-002嵌入。
我们在四个不同的OpenAI嵌入模型上运行了评估函数:
ltext-embedding-3-large的两个版本:一个具有尽可能低的维度(256),
另一个具有最高的维度(3072)。这些被称为“OAI-large256”和“OAI-Large3072”。
lOAI-small:即text-embedding-3-small嵌入模型,尺寸为1536。
lOAI-ada-002:传统的text-embedding-ada-002模型,尺寸为1536。
每个模型在四种不同的语言上进行了评估:英语(EN)、法语(FR)、捷克语(CS)和匈牙利语(HU),分别涵盖了日耳曼语、罗曼斯语、斯拉夫语和乌拉尔语。
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"]
# 循环遍历所有语言
for language in languages:
#加载数据集
file_name=language+"_dataset.json"
qa_dataset = EmbeddingQAFinetuneDataset.from_json(file_name)
# 循环遍历所有模型
for model_name, model_spec in embeddings_model_spec.items():
#取得模型
embed_model = OpenAIEmbedding(model=model_spec['model_name'],
dimensinotallow=model_spec['dimensions'])
# 评估嵌入分数(根据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基准中明显。这反映了一个事实,即在大型基准中观察到的平均性能并不一定反映在自定义数据集上获得的性能。
4.开源嵌入模型的评估
当前,围绕嵌入的开源研究相当活跃,并且定期发布新的模型。关于最新发布的模型,一个保持更新的好地方是Hugging Face MTEB排行榜(https://huggingface.co/spaces/mteb/leaderboard)。
为了在本文中进行比较,我们选择了最近发表的一组四个嵌入模型(2024)。选择的标准是它们在MTEB排行榜上的平均得分以及它们处理多语言数据的能力。所选模型的主要特征概述如下:
选定的开源嵌入模型
lE5-Mistral-7B-instruct(E5-Mistral-7B):微软的这个E5嵌入模型(https://huggingface.co/intfloat/e5-mistral-7b-instruct)是从Mistral-7B-v0.1初始化的,并在多语言数据集的混合上进行了微调。该模型在MTEB排行榜上表现最好,但也是迄今为止最大的模型(14GB)。
lmultilingual-e5-large-instruct(ML-e5-larg):微软的另一个e5模型(https://huggingface.co/intfloat/multilingual-e5-large-instruct),旨在更好地处理多语言数据。它是从xlm-roberta-large初始化的,并在多语言数据集的混合上进行训练。它比E5-Mistral小得多(10倍),但上下文大小也低得多(514)。
lBGE-M3:该模型(https://huggingface.co/BAAI/bge-m3)由北京人工智能研究院设计,是他们最先进的多语言数据嵌入模型,支持100多种工作语言。截至2024年2月22日,尚未在MTEB排行榜上进行基准测试。
lnomic-embed-text-v1(nomic-embed):该模型由Nomic公司(https://home.nomic.ai/)设计,声称比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"]
# 循环遍历所有模型
for model_name, model_spec in embeddings_model_spec.items():
print("Processing model : "+str(model_spec))
# 获取模型
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')
# 循环遍历所有语言
for language in languages:
# 加载数据集
file_name=language+"_dataset.json"
qa_dataset = EmbeddingQAFinetuneDataset.from_json(file_name)
start_time_assessment=time.time()
# 评估嵌入得分(根据k=5时的命中率)
score = evaluate(qa_dataset, tokenizer, embed_model, model_spec['normalize'], model_spec['max_length'], model_spec['pooling_type'])
#计算分数评估的持续时间
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多倍,是迄今为止速度最慢的模型。
5.结论
现在,让我们把八个测试模型的性能放在同一个图中进行比较。
八款测试模型的性能比对
从上述这些结果中,我们得到如下的主要分析结论:
- 开源模型获得了最佳性能。其中,北京人工智能研究院开发的BGE-M3模型脱颖而出。该模型的上下文长度与OpenAI模型(8K)相同,大小为2.2GB。
- OpenAI范围内的一致性。大型(3072)、小型和传统OpenAI模型的性能非常相似。然而,减小大模型(256)的嵌入尺寸导致了性能的下降。
- 语言敏感性。几乎所有模型(ML-E5-large除外)的英语表现都最好。捷克语和匈牙利语等语言的表现有显著差异。
根据此结论,你应该订阅付费的OpenAI还是选择使用托管的开源嵌入模型呢?
OpenAI最近的价格修订(https://openai.com/pricing)使其API的访问变得更加实惠,目前成本为每百万代币0.13美元。因此,每月处理一百万个查询(假设每个查询涉及大约1K个代币)的成本约为130美元。因此,根据你的使用情况,租用和维护自己的嵌入服务器可能不划算。
然而,成本效益并不是唯一的考虑因素。可能还需要考虑其他因素,如延迟、隐私和对数据处理工作流的控制。相对来说,开源模型提供了完全的数据控制、增强隐私和自定义的优势。另一方面,OpenAI的API还存在延迟问题,有时会导致响应时间延长。
总之,在开源模型和OpenAI等专有解决方案之间的选择还不能马上给出一个简单的答案。显然,开源嵌入提供了一种引人注目的选择,其优势主要表现在能够将性能与对数据的更大控制相结合。相反,OpenAI的产品可能仍然会吸引那些优先考虑便利性的人,尤其是在隐私问题是次要的情况下。
关键参考资料
Companion Github存储库:
https://github.com/Yannael/multilingual-embeddings
你想知道的关于句子嵌入的一切(也许还有更多):
https://osanseviero.github.io/hackerllama/blog/posts
OpenAI博客发布:新的嵌入模型和API更新
https://openai.com/blog/new-embedding-models-and-api-updates
嵌入:OpenAI指南
ttps://platform.openai.com/docs/guides/embeddings/embedding-models
MTEB:海量文本嵌入基准与Hugging Face MTEB排行榜
https://huggingface.co/spaces/mteb/leaderboard
文本嵌入:综合指南
https://towardsdatascience.com/text-embeddings-comprehensive-guide-afd97fce8fb5
检索增强生成(RAG)从业者指南
https://cameronrwolfe.substack.com/p/a-practitioners-guide-to-retrieval。
如何为RAG找到最佳的多语言嵌入模型
https://towardsdatascience.com/how-to-find-the-best-multilingual-embedding-model-for-your-rag-40325c308ebb
译者介绍
朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。
原文链接:https://towardsdatascience.com/openai-vs-open-source-multilingual-embedding-models-e5ccb7c90f05