文 | DeepAuto、Yann-Aël Le Borgne
Sora的爆火给OpenAI注入了一针强心剂,也让自动驾驶行业从业者、创业者、技术达人心跳加速。DeepAuto提出一个假设:假如OpenAI是智驾行业终局的通吃者,那它每一次发布的新模型都可能在下一次自动驾驶新产业革命爆发前夜带来深远影响。
最近,Sora的爆火给OpenAI注入了一针强心剂,也让自动驾驶行业从业者、创业者、技术达人心跳加速。DeepAuto提出一个假设:假如OpenAI是智驾行业终局的通吃者,那它每一次发布的新开源模型都可能在下一次自动驾驶新产业革命爆发前夜带来深远影响。
最近,OpenAI 发布了新一代嵌入模型,名为 embedding v3,他们将其描述为性能最强的嵌入模型,具有更高的多语言性能。这些模型分为两类:一类较小,称为 text-embedding-3-small;另一类较大,功能更强大,称为 text-embedding-3-large。
关于这些模型的设计和训练方法,披露的信息很少。与之前发布的嵌入模型(2022 年 12 月发布的 ada-002 模型类)一样,OpenAI 再次选择了闭源方法,即只能通过付费 API 访问模型。
但是,模型的性能是否好到值得付费呢?
此文的目的是通过实证比较这些新模型与开源模型的性能。我们将以数据检索工作流程为基础,根据用户查询,找到语料库中最相关的文档。
语料库选取的是《欧洲人工智能法案》,目前正处于最后的验证阶段。该语料库除了是全球首个人工智能法律框架外,还有一个有趣的特点,那就是它有 24 种语言版本。这使得比较不同语系数据检索的准确性成为可能。
本帖将介绍以下两个主要步骤:
用于重现本篇文章中介绍的结果的代码和数据可在此 Github 存储库中获取。请注意,本文以《欧盟人工智能法案》为例,所采用的方法可适用于其他数据语料库。
首先,让我们在自定义数据上生成一个问答(Q/A)数据集,用于评估不同嵌入模型的性能。生成自定义 Q/A 数据集有两个好处。首先,它可以确保数据集没有参与嵌入模型的训练,从而避免出现偏差,而在 MTEB 等参考基准上可能会出现这种情况。其次,它允许根据特定的数据语料库进行评估,例如在检索增强应用(RAG)的情况下,这可能是相关的。
我们将遵循 Llama Index 在其文档中建议的简单流程。首先将语料库分割成一组块。然后,通过大语言模型(LLM)为每个语块生成一组合成问题,使答案位于相应的语块中。具体过程如下:
使用 LLM 数据框架(如 Llama Index)可以直接实施这一策略。语料库的加载和文本的分割可以通过高级函数方便地完成,如下代码所示。
从llama_index.readers.web导入SimpleWebPageReader
从
llama_index.core.node_parser导入SentenceSplitter
语言 = "EN"
url_doc = "
https://eur-lex.europa.eu/legal-content/" +language+ "/TXT/HTML/? uri=CELEX:52021PC0206"
文档 = SimpleWebPageReader(html_to_text= True ).load_data([url_doc])
解析器 = SentenceSplitter(chunk_size= 1000 )
节点 =
parser.get_nodes_from_documents(documents, show_progress= True )
在本示例中,语料库是欧盟人工智能法的英文版,使用的是 2021 年 4 月的草案版本,因为最终版本尚未适用于所有欧洲语言。在该版本中,可以用欧盟其他 23 种官方语言中的任何一种替换 URL 中的英语,以检索不同语言的文本(BG 表示保加利亚语、ES 表示西班牙语、CS 表示捷克语等)。
我们使用 SentenceSplitter 对象将文档分割为 1000 个标记的块。对于英语,这会产生大约 100 个块。
然后将每个块作为以下提示的上下文提供
提示={}
提示["EN"] = """\"EN" ] = """\
上下文信息如下。
---------------------
{context_str}
------------ ---------
给定上下文信息而不是先验知识,仅根据以下查询生成问题。
您是教师/教授。您的任务是为即将到来的测验/考试设置 {num_questions_per_chunk} 个问题。
整个文档中的问题本质上应该是多样化的。将问题限制在所提供的上下文信息范围内。”
该提示的目的是生成与文档分块相关的问题,就像老师在准备即将到来的测验一样。我们将参数 "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 上下文窗口,并针对对话进行了优化
生成的对象 "qa_dataset "包含问题和答案(块)对。以生成的问题为例,下面是前两个问题("答案 "是第一个文本块)的结果:
1) 根据解释性备忘录,制定人工智能统一规则的条例提案(人工智能法)的主要目标是什么?
2) 如背景信息中所述,关于人工智能的条例提案旨在如何解决与使用人工智能相关的风险,同时促进人工智能在欧盟的应用?
大块内容和问题的数量取决于语言,从英语的约 100 个大块内容和 200 个问题,到匈牙利语的 200 个大块内容和 400 个问题不等。
我们的评估功能遵循 Llama Index 文档,包括两个主要步骤。首先,将所有答案(文档块)的嵌入存储在 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 模型来说,"embed_model "参数是一个用模型名称和模型维度初始化的 OpenAIEmbedding 对象。
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding(model=model_spec['model_name'],
dimensions=model_spec['dimensions'])
在四种不同的 OpenAI 嵌入模型上运行了评估功能:
每个模型都在四种不同的语言上进行了评估:英语 (EN)、法语 (FR)、捷克语 (CS) 和匈牙利语 (HU),分别涵盖日耳曼语、罗曼语、斯拉夫语和乌拉尔语的例子。
embeddings_model_spec = {
}
embeddings_model_spec['OAI-Large-256']={'model_name':'text-embedding-3-large','dimensions':256}'OAI-Large-256' ]={ 'model_name':'text-embedding-3-large' , 'dimensions':256 }
embeddings_model_spec[ 'OAI-Large-3072' ]={ 'model_name':'文本嵌入-3-large','尺寸':3072 }
embeddings_model_spec[ 'OAI-Small' ]={ 'model_name':'text-embedding-3-small','尺寸':1536 }
embeddings_model_spec[ 'OAI-ada-002 ' ]={ 'model_name' : 'text-embedding-ada-002' , 'dimensions' : None }
results =
languages = [ "EN" , "FR" , "CS" , "HU" ]
# 循环all languages
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' ],
dimensions=model_spec[ 'dimensions' ])
# 评估嵌入分数(以 MRR 为单位)
Score =valuate(qa_dataset, embed_model)
results.append([language, model_name, Score])
df_results = pd .DataFrame(结果, columns = [ "语言" , "嵌入模型" , "MRR" ])
由此得出的 MRR 精确度报告如下:
不出所料,对于大型模型来说,3072 的较大嵌入尺寸会带来更好的性能。不过,与小型模型和传统 Ada 模型相比,大型模型的嵌入尺寸要小于我们的预期。为了进行比较,我们还在下文中报告了 OpenAI 模型在 MTEB 基准测试中的表现。
值得注意的是,在我们的评估中,大型模型、小型模型和 Ada 模型之间的性能差异远没有 MTEB 基准那么明显,这反映了一个事实,即在大型基准中观察到的平均性能并不一定反映在定制数据集上获得的性能。
为了在本文中进行比较,我们选择了一组最近(2024 年)发布的四个嵌入模型。选择的标准是它们在 MTEB 排行榜上的平均得分及其处理多语言数据的能力。所选模型的主要特点概述如下。
embeddings_model_spec = {
}
embeddings_model_spec['E5-mistral-7b']={'model_name':'intfloat/e5-mistral-7b-instruct','max_length':32768, 'pooling_type':'last_token', '标准化''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 }}
结果 =
languages = [ "EN"、“FR”、“CS”、"HU" ]
# 循环遍历所有模型
for model_name, model_spec in embeddings_model_spec.items:
print ( "处理模型 : " + 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 =valu(qa_dataset, tokenizer, embed_model, model_spec[ 'normalize' ], model_spec[ 'max_length' ], model_spec[ 'pooling_type' ])
# 获取评分评估时长
uration_assessment = time.time-start_time_assessment
results.append([语言、模型名称、分数、持续时间评估])
由此得出的 MRR 精确度报告如下。
结果表明,BGE-M3 的性能最好,其次是 ML-E5-Large、E5-mistral-7b 和 Nomic-Embed。BGE-M3 模型尚未在 MTEB 排行榜上进行基准测试,我们的结果表明,它的排名可能会高于其他模型。值得注意的是,虽然 BGE-M3 针对多语言数据进行了优化,但它在英语方面的表现也优于其他模型。
我们还在下面报告了每种嵌入模型的处理时间。
E5-mistral-7b 比其他型号大 10 多倍,毫无疑问是迄今为止速度最慢的型号。
让我们将八个测试模型的性能并排放在一个图中。
从这些结果中可以看出:
因此,选择付费订阅 OpenAI 还是托管开源嵌入模型,相信你已经有答案。
此外,OpenAI 最近的价格调整使访问其 API 的费用也在大大降低,目前为每百万代币 0.13 美元。因此,每月处理 100 万次查询(假设每次查询涉及约 1K 个代币)的成本约为 130 美元。因此,根据每个人不同的使用情况,租用和维护自己的嵌入式服务器可能并不划算。
不过,成本效益并不是唯一的考虑因素。还需要考虑其他因素,如延迟、隐私和对数据处理工作流程的控制。开源模式具有完全数据控制、提高隐私性和定制化的优势。另一方面,OpenAI 的应用程序接口也存在延迟问题,有时会导致响应时间延长。
总之,在开源模型和 OpenAI 等专有解决方案之间做出选择,并不是一个简单的答案。开源嵌入式提供了一个令人信服的选择,它将性能与对数据的更大控制相结合。相反,OpenAI 的产品可能仍然会吸引那些优先考虑便利性的用户,尤其是在隐私问题处于次要地位的情况下。
(Reference:https://towardsdatascience.com/openai-vs-open-source-multilingual-embedding-models-e5ccb7c90f05)