OpenAI 和开源多语言嵌入的对比测试

2024年02月27日 由 alex 发表 115 0

OpenAI 最近发布了新一代嵌入模型,名为 embedding v3,他们将其描述为性能最强的嵌入模型,具有更高的多语言性能。这些模型分为两类:一类较小,称为 text-embedding-3-small;另一类较大,功能更强大,称为 text-embedding-3-large。


关于这些模型的设计和训练方法,披露的信息很少。与之前发布的嵌入模型(2022 年 12 月发布的 ada-002 模型类)一样,OpenAI 再次选择了闭源方法,即只能通过付费 API 访问模型。


但是,模型的性能是否好到值得付费呢?


本文的目的是通过实证比较这些新模型与开源模型的性能。我们将以数据检索工作流程为基础,根据用户查询,找到语料库中最相关的文档。


我们的语料库将是《欧洲人工智能法案》,目前正处于最后的验证阶段。该语料库除了是全球首个人工智能法律框架外,还有一个有趣的特点,那就是它有 24 种语言版本。这使得比较不同语系数据检索的准确性成为可能。


本文将介绍以下两个主要步骤:


  • 从多语言文本语料库中生成自定义合成问题/答案数据集
  • 比较 OpenAI 和最先进的开源嵌入模型在该自定义数据集上的准确性。


生成自定义 Q/A 数据集

首先,让我们在自定义数据上生成一个问答(Q/A)数据集,用于评估不同嵌入模型的性能。生成自定义 Q/A 数据集有两个好处。首先,它可以确保数据集没有参与嵌入模型的训练,从而避免出现偏差,而在 MTEB 等参考基准上可能会出现这种情况。其次,它允许根据特定的数据语料库进行评估,例如在检索增强应用(RAG)的情况下,这可能是相关的。


我们将遵循 Llama Index 在其文档中建议的简单流程。首先将语料库分割成一组块。然后,通过大语言模型(LLM)为每个语块生成一组合成问题,使答案位于相应的语块中。整个过程如下图所示:


12


使用 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 直接从网上获取。我们使用的是 2021 年 4 月的草案版本,因为最终版本尚未适用于所有欧洲语言。在该版本中,可以用欧盟其他 23 种官方语言中的任何一种替换 URL 中的英语,以检索不同语言的文本(BG 表示保加利亚语、ES 表示西班牙语、CS 表示捷克语等)。


13


我们使用 SentenceSplitter 对象将文档分割成 1000 个词块。对于英语而言,这将产生约 100 个语块。


然后将每个块作为以下提示的上下文提供:


prompts={}
prompts["EN"] = """\"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 上下文窗口,并针对对话进行了优化 。


生成的对象 "qa_dataset "包含问题和答案(块)对。以生成的问题为例,下面是前两个问题("答案 "是第一个文本块)的结果:


1) 根据解释性备忘录,制定人工智能统一规则的条例(人工智能法)提案的主要目标是什么?
2) 如背景信息所述,关于人工智能的条例提案如何旨在解决与使用人工智能相关的风险,同时促进人工智能在欧盟的应用?


语块和问题的数量取决于语言,英语约为 100 个语块和 200 个问题,匈牙利语为 200 个语块和 400 个问题。


评估 OpenAI 嵌入模型

我们的评估功能遵循 Llama Index 文档,包括两个主要步骤。首先,将所有答案(文档块)的嵌入模型存储在 VectorStoreIndex 中,以便高效检索。然后,评估函数在所有查询中循环,检索前 k 个最相似的文档,并以 MRR(平均互易等级)评估检索的准确性。


def evaluate(dataset, embed_model, insert_batch_size=1000, top_k=5):(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 OpenAIEmbeddingimport OpenAIEmbedding
embed_model = OpenAIEmbedding(model=model_spec['model_name'],
                              dimensions=model_spec['dimensions'])


维度 API 参数可以缩短嵌入(即从序列末尾移除一些数字),而不会使嵌入失去其概念表示特性。例如,OpenAI 在其公告中指出,在 MTEB 基准测试中,嵌入式可以缩短到 256 大小,但仍然优于未缩短的text-embedding-ada-0021536 的嵌入。


我们在四种不同的 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) 和匈牙利语 (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':'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 精确度报告如下:


14


不出所料,对于大型模型来说,3072 的较大嵌入尺寸会带来更好的性能。不过,与小型模型和传统 Ada 模型相比,大型模型的嵌入尺寸要小于我们的预期。为了进行比较,我们还在下文中报告了 OpenAI 模型在 MTEB 基准测试中的表现。


15


值得注意的是,在我们的评估中,大型模型、小型模型和 Ada 模型之间的性能差异远没有 MTEB 基准那么明显,这反映了一个事实,即在大型基准中观察到的平均性能并不一定反映在定制数据集上获得的性能。


开源嵌入模型评估

为了在本文中进行比较,我们选择了一组最近(2024 年)发布的四个嵌入模型。选择的标准是它们在 MTEB 排行榜上的平均得分及其处理多语言数据的能力。所选模型的主要特点概述如下。


16


  • E5-Mistral-7B-instruct (E5-mistral-7b): 微软的这一 E5 嵌入模型由 Mistral-7B-v0.1 初始化,并在混合多语言数据集上进行了微调。该模型在 MTEB 排行榜上表现最佳,但也是迄今为止最大的模型(14GB)。
  • multilingual-E5-large-instruct(ML-E5-large): 微软的另一个 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', '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 精确度报告如下。


16


结果表明,BGE-M3 的性能最好,其次是 ML-E5-Large、E5-mistral-7b 和 Nomic-Embed。BGE-M3 模型尚未在 MTEB 排行榜上进行基准测试,我们的结果表明,它的排名可能会高于其他模型。值得注意的是,虽然 BGE-M3 针对多语言数据进行了优化,但它在英语方面的表现也优于其他模型。


我们还在下面报告了每种嵌入模型的处理时间。


17


E5-mistral-7b 比其他型号大 10 多倍,毫无疑问是迄今为止速度最慢的型号。


结论

让我们把八个测试型号的性能并列在一张图中。


18


从这些结果中可以看出:


  • 开源模型表现最佳。北京人工智能学会开发的 BGE-M3 模型表现最佳。该模型的上下文长度与 OpenAI 模型相同(8K),大小为 2.2GB。
  • OpenAI 范围内的一致性。大型(3072)、小型和传统 OpenAI 模型的性能非常相似。然而,减少大型模型的嵌入大小(256)会导致性能下降。
  • 语言敏感性。几乎所有模型(ML-E5-large 模型除外)在英语中的表现都最好。在捷克语和匈牙利语等语言中的表现则有显著差异。


因此,你应该选择付费订阅 OpenAI 还是托管开源嵌入模型?


文章来源:https://medium.com/towards-data-science/openai-vs-open-source-multilingual-embedding-models-e5ccb7c90f05
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消