继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

OpenAI 与开源多语言嵌入模型的对比

繁花不似锦
关注TA
已关注
手记 348
粉丝 50
获赞 241
选择最适合您数据的模型

我们将使用欧盟《人工智能法》作为我们的嵌入模型比较的数据语料库。图片由 Dall-E 3 提供。

OpenAI 最近发布了他们的新一代嵌入模型,称为 embedding v3 ,他们 描述 这些模型为他们性能最强的嵌入模型,并且在多语言性能方面表现更佳。这些模型分为两类:较小的一个称为 text-embedding-3-small,较大的一个称为 text-embedding-3-large

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

但是性能真的那么好,值得付费吗?

本文的动机是通过实证比较这些新模型与其开源版本的性能。我们将依赖于一个数据检索工作流,在该工作流中,需要根据用户的查询在语料库中找到最相关的文档。

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

这篇文章将依次经历以下两个主要步骤:

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

本文中展示的结果的代码和数据可以在 这个 Github 仓库 中获取。请注意,本文使用欧盟 AI 法案作为示例,但本文中采用的方法也可以应用于其他数据集。

生成自定义的问答数据集

让我们首先从生成一个基于自定义数据的问题和答案(Q/A)数据集开始,该数据集将用于评估不同嵌入模型的性能。生成自定义Q/A数据集的好处有两方面。首先,它通过确保数据集未参与任何嵌入模型的训练来避免偏差,这种情况可能出现在参考基准如MTEB中。其次,它允许根据特定的数据集进行定制评估,这在检索增强应用(RAG)等情况下可能非常相关。

我们将按照Llama Index 在其文档中建议的简单流程进行。首先将语料库拆分成一系列片段。然后,对于每个片段,通过大型语言模型(LLM)生成一组合成问题,使得答案位于相应的片段中。该过程如下图所示:

生成适用于您的数据的问答数据集,方法来自Llama Index

实现这一策略使用像 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)

在本示例中,语料库是直接从 Web 获取的欧盟《人工智能法》的英文版本,使用此 官方 URL。我们使用 2021 年 4 月的草案版本,因为最终版本尚未提供所有欧洲官方语言的版本。在这个版本中,URL 中的英文可以替换为其他 23 种欧盟官方语言中的任意一种,以获取不同语言的文本(例如,BG 代表保加利亚语,ES 代表西班牙语,CS 代表捷克语,等等)。

下载链接到欧盟AI法案的24种官方欧盟语言版本(来自EU官方网站

我们使用 SentenceSplitter 对象将文档拆分成每块 1000 个 token。对于英文文档,这将产生大约 100 块。

每个块随后作为上下文提供给以下提示(在Llama Index库中建议的默认提示):

    prompts={}  
    prompts["EN"] = """\  
    下面是上下文信息。  

    ---------------------  
    {context_str}  
    ---------------------  

    根据以下查询和提供的上下文信息(不使用先验知识),仅生成基于查询的问题。  

    你是一位教师/教授。你的任务是为即将到来的测验/考试设置{num_questions_per_chunk}个问题。这些问题应该在文档中具有多样性的性质。问题应仅基于提供的上下文信息。  
    """

该提示旨在生成关于文档片段的问题,就像老师正在准备即将到来的测验一样。每个片段需要生成的问题数量通过参数‘num_questions_per_chunk’传递,我们将其设置为两个。然后可以通过调用Llama Index库中的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的嵌入模型评估

我们的评估函数遵循Llama Index 文档,并分为两个主要步骤。首先,将所有答案(文档片段)的嵌入存储在 VectorStoreIndex 中,以实现高效检索。然后,评估函数遍历所有查询,检索 top 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()):  
            # 检索当前查询的 top_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  # 假设每个查询只有一个相关文档  

            # 计算平均倒数排名 (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_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 的大小,仍然优于未缩短的 1536 大小的 text-embedding-ada-002 嵌入。

我们在四个不同的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}  
    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'],  
                                          dimensions=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基准测试中那么明显,这反映了在大型基准测试中观察到的平均性能并不一定能反映在自定义数据集上获得的性能。

开源嵌入模型的评估

围绕嵌入的开源研究非常活跃,新的模型会定期发布。了解最新发布的模型的好地方是Hugging Face 😊 MTEB排行榜

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

选定的开源嵌入模型

  • 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小得多(仅为E5-Mistral的十分之一),但上下文大小也小得多(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"]  

    # 遍历所有模型  
    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 = 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 模型比其他模型大十倍以上,不出所料,它是运行速度最慢的模型。

结论

让我们将八个测试模型的性能放在同一张图中进行对比。

八种测试模型的性能摘要

这些结果的关键观察是:

  • 开源模型表现最佳。由北京人工智能研究院开发的BGE-M3模型表现最为出色。该模型与OpenAI模型具有相同的上下文长度(8K),大小为2.2GB。
  • OpenAI模型系列的一致性。大型(3072)、小型和旧版OpenAI模型的表现非常相似。然而,减小大型模型的嵌入大小(256)会导致性能下降。
  • 语言敏感性。几乎所有模型(除ML-E5-large外)在英语上的表现最佳。在捷克语和匈牙利语等语言上观察到了显著的性能差异。

你应该选择付费的 OpenAI 订阅,还是托管一个开源的嵌入式模型?

OpenAI 最近的价格调整(详情)使得访问他们的 API 更加经济实惠,现在的费用是每百万个 token 收取 0.13 美元。如果每月处理一千万个查询(假设每个查询涉及大约 1K 个 token),那么费用大约是 130 美元。因此,根据你的使用情况,租用和维护自己的嵌入式服务器可能并不划算。

成本效益并不是唯一的考虑因素。其他因素,如延迟、隐私和对数据处理工作流的控制,也可能需要考虑。开源模型提供了完全控制数据的优势,增强了隐私和定制化。另一方面,使用OpenAI的API时,有时会遇到延迟问题,导致响应时间延长。

总之,在开源模型和像 OpenAI 这样的专有解决方案之间进行选择,并没有一个简单的答案。开源嵌入提供了一个有吸引力的选择,结合了性能和对数据的更大控制。相反,如果隐私问题不是首要考虑的话,OpenAI 的解决方案仍然会吸引那些优先考虑便利性的人。

有意义的链接

注释:

喜欢这篇文章吗?分享你的想法,点赞,或者在LinkedIn上与我联系

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP