为了账号安全,请及时绑定邮箱和手机立即绑定

研究到生产:相对答案质量(RAQ)和NVIDIA NIM

通过相对答案质量(RAQ)和NVIDIA NIM进行大型语言模型评估和部署的分步方法

本文与Rafael Guedes共同撰写。

介绍

2022年ChatGPT的成功发布让人们意识到,生成式AI不仅对那些希望自动化手动且耗时任务的个人有许多优势,也对那些希望提升客户体验和优化运营的公司有很多好处。

随着对生成式AI解决方案需求的增加,多家公司开始投资于开源解决方案的研究和开发,例如Mistral AI的Mixtral或Meta的LLaMA 3。对GenAI的大量投资使得这些模型广泛向公众开放,这使得大多数公司的重点从内部开发大型语言模型(LLMs)转向部署这些开源版本。

关于这些模型的产品化,有两个问题值得思考:应该使用哪个模型,以及如何能够大规模且安全地部署这些模型?本文旨在回答这些问题。我们提出了一种名为RAQ(相对答案质量)的方法,通过使用独立的LLM来比较和排名不同LLM的答案,从而评估这些模型。我们方法的关键优势在于它可以轻松地集成到任何工作流程中,并且可以评估任何一组LLM在任何领域/主题/用例上的表现。RAQ可以用于拥有特定主题的问题和正确答案的数据集的组织,但也可以在没有此类数据集的情况下应用。我们提出的方法的另一个优点是可以量化数据集的大小。我们使用统计测试来确定排名差异是否显著,从而让机器学习从业者能够自信地判断一个LLM是否优于另一个。排名本质上是基于它们与真实答案的接近程度,无论评估的数据属于哪个领域/主题/用例。

为了回答第二个问题,我们展示了使用 NVIDIA 的新 NIM 微服务从研究过渡到生产级解决方案是多么容易。NIM 允许在大规模部署 LLM,首先使用 NVIDIA 提供的服务进行快速原型设计,而在生产环境中,则可以在任何私有云或物理硬件上进行自托管部署。加速硬件设置和领先开源模型的推理引擎已经预先优化,因此我们可以轻松地设置基础设施。此外,它还提供了使用 LoRA 对模型进行微调的灵活性。

图1:从研究到生产的相对答案质量(RAQ)(使用NVIDIA NIM,图片由作者使用DALL-E生成)

一如既往,代码可在我们的 GitHub 上获取。

RAQ:相对答案质量

如前所述,在ChatGPT发布之后,对开源大语言模型(LLM)的研究和开发进行了巨大的投资。事实上,几乎每周都会有一个新的模型或模型的变种被发布。面对如此多的选择,如何选择最适合自己的模型呢?

定量指标如推理时间很重要,在选择您的大语言模型时应予以考虑。然而,推理速度快但响应质量低的大语言模型可能不是一个好选择。

在特定用例上定性评估大型语言模型(LLM)是一项手动且耗时的任务,因为需要根据数百个问题来评估模型。这具有高度主观性,并且需要阅读数百个答案并与真实答案进行比较。因此,为了克服这个问题并减少评估响应质量所需的手动工作,我们提出了相对答案质量(RAQ)。

RAQ 框架依赖于一个独立的大语言模型(LLM),该模型接收问题、真实答案以及一组 LLM 的答案。它使用独立的 LLM 根据与真实答案的比较来对它们进行排名。当不存在包含问题和答案的数据集时,可以开发 RAQ 的扩展。在这种情况下,我们可以使用另一个独立的 LLM 来为我们生成这些问题和答案。还有一种混合设置,即存在一个小数据集但不足以运行 RAQ。在这种情况下,我们可以利用这个小数据集作为种子,使用独立的 LLM 生成类似的示例,采用半合成的方法。

独立的大规模语言模型(LLM)可能包含偏见和其他潜在问题。为了解决这些问题,我们提出了两个选项:i) 选择最佳的开源或闭源模型,或者 ii) 选择一组最佳模型,并对每个独立的LLM运行相对答案质量(RAQ)评估。后者需要在应用通用RAQ框架之前增加一个额外步骤。结果需要在计算排名的中位数和标准差之前进行汇总。对于RAQ的一个关键点是确保独立的LLM或LLM集合不在我们评估的LLM集合中。否则,我们将对该特定模型或模型集合引入强烈偏见。

最后,RAQ 还可以包含其他指标以提供对一系列大语言模型的完整比较。例如,我们可以计算每秒单词数和平均回答长度。这些指标可以提供更多关于模型性能和详细程度的数据点。

RAQ 实际应用

我们首先创建以下提示:我们要求独立的大规模语言模型(LLM)根据答案质量对LLM ID进行比较和排名。

    根据正确答案:{Ground Truth Answer},将以下答案的ID从最正确的到最不正确的进行排序:  
    ID: 1 答案:{LLM 1 Answer}  
    ID: 2 答案:{LLM 2 Answer}  
    ID: 3 答案:{LLM 3 Answer}  
    ...

我们多次运行此过程并记录所有排名。在收集了所有问题的排名后,我们执行Dunn的多重比较检验[2]。这有助于我们了解所有大型语言模型(LLM)的排名是否存在显著差异,或者它们的表现是否相似。该检验在预定义的显著性水平(通常为5%)下进行成对比较,并告诉我们哪些组在统计上存在显著差异。

图2:RAQ处理机制(作者供图)

NVIDIA NIM 是什么?

NVIDIA NIMs [1] 是提供现成推理 API 的容器,用于 AI 模型。它是一种云原生微服务解决方案,旨在使部署过程更加简单且节省时间。它消除了将 AI 模型连接到现有企业基础设施的复杂性。

公司通常以三种方式部署大型语言模型(LLMs):在自己的物理硬件上、在云端、或通过第三方托管的API。前两种选项提供了诸如数据隐私、安全性和模型灵活性等优势。然而,它们需要非常专业的资源以避免效率低下,例如硬件利用率低或应用程序性能问题。另一方面,后一种方式解决了性能问题,但不能确保数据隐私、安全性和模型灵活性。它也会成为核心依赖项,给部署带来额外的风险。

NIM 通过两种方式填补了这些空白。首先,它们提供了自己的第三方API,你可以使用这些API。主要的区别在于,它们提供了社区支持的模型,并确保数据隐私和安全。它们只提供运行这些模型的微服务,因此没有使用任何数据进行训练的动机,这与OpenAI、Anthropic、Google等公司的情况不同。第二种选择是使用NIM在你的专用基础设施上部署模型。在这种情况下,NIM为每个模型和硬件配置提供了优化的推理引擎。这意味着NVIDIA团队已经在基础设施方面完成了关键工作,以确保低延迟和高吞吐量。此外,它还提供了灵活性,可以自由选择其目录中的任何模型并进行最小更改部署。最后,这些模型也可以使用LoRA进行微调。

以下我们介绍如何在您自己的基础设施上从 NIM 目录中获取并运行大型语言模型 (LLM) 的过程:

  1. 安装 Docker (https://docs.docker.com/engine/install/)
  2. 安装 NVIDIA Container Toolkit (https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
  3. 登录 NVIDIA docker login [nvcr.io](https://medium.com/<http:/nvcr.io/>)
  4. 设置你的 NVIDIA NGC API 密钥 export NGC_API_KEY=<你的 api 密钥>
  5. 定义容器名称、镜像名称和下载模型的本地路径。如果你想部署另一个模型,例如 LLaMA 3 70B,你只需要将 IMG_NAME 更改为 nvcr.io/nim/meta/meta-llama3-70b-instruct:<版本>
    # 选择一个容器名称用于记录  
    export CONTAINER_NAME=meta-llama3-8b-instruct  

    # 从NGC选择一个LLM NIM镜像  
    export IMG_NAME="nvcr.io/nim/meta/${CONTAINER_NAME}:1.0.0"  

    # 选择系统上的一个路径来缓存下载的模型  
    export LOCAL_NIM_CACHE=~/.cache/nim  
    mkdir -p "$LOCAL_NIM_CACHE"

6. 运行以下 docker 命令。

    # 启动 LLM NIM  
    docker run -it --rm --name=$CONTAINER_NAME \\  
      --runtime=nvidia \\  
      --gpus all \\  
      --shm-size=16GB \\  
      -e NGC_API_KEY \\  
      -v "$LOCAL_NIM_CACHE:/opt/nim/.cache" \\  
      -u $(id -u) \\  
      -p 8000:8000 \\  
      $IMG_NAME

表1:启动NIM容器的docker命令解释。

我们已经准备好了。我们现在可以向刚刚部署的 LLaMA 3 8B 发送请求了。

    import requests  
    from pprint import pp  

    endpoint = '<http://0.0.0.0:8000/v1/chat/completions>'  
    headers = {  
        'accept': 'application/json',  
        'Content-Type': 'application/json'  
    }  
    messages = [  
        {"role": "user",  
         "content": "写一篇简短的文章,解释为什么AI很重要。"}  
    ]  
    data = {  
        'model': 'meta/llama3-8b-instruct',  
        'messages': messages,  
        'max_tokens': 100,  
        'temperature': 1,  
        'n': 1,  
        'stream': False,  
        'stop': 'string',  
        'frequency_penalty': 0.0  
    }  
    response = requests.post(endpoint, headers=headers, json=data)  
    pp(response.json())

人工智能(AI)正在改变世界,并有可能显著影响我们的日常生活。通过处理大量数据,AI 可以自动化重复性任务,改进决策过程,并提供个性化的体验。它还可以通过分析模式和识别解决方案来帮助我们应对气候变化、医疗保健和教育等复杂挑战。此外,AI 可以在从客户服务到交通运输的各种行业中提高生产率、效率和准确性。

行业标准APIs

另一个有趣的方面是 NIM 与流行的 LLM 包(如 LangChain 和 LlamaIndex)的集成。此外,任何与 OpenAI 的 API 兼容的包都可以通过更改基础 URL 轻松地与 NIM 集成。

以下示例使用 LangChain 和 OpenAI 来询问与之前相同的问题。请注意,我们提供了本地主机 URL,因为我们正在查询刚刚部署的 LLaMA 3 8B 模型。在这种情况下,我们创建了一个 LLMChain,它使用一个简单的模板接收一个问题并将其传递给 LLaMA。

    from langchain import PromptTemplate  
    from langchain.chains import LLMChain  
    from langchain_openai import ChatOpenAI  

    template = """  
        问题: {question}  
        回答:  
    """  
    prompt = PromptTemplate(  
        template=template, input_variables=["context", "question"]  
    )  
    llm = ChatOpenAI(base_url="<http://localhost:8000/v1>",  
            model="meta/llama3-8b-instruct",  
            api_key="not-used",  
            temperature=0.1,  
            max_tokens=100,  
            top_p=1.0)  
    query_llm = LLMChain(  
                llm=llm,  
                prompt=prompt,  
            )  
    answer = query_llm.invoke(  
        {"question": "写一段简短的信息,解释为什么AI很重要。"}  
    )

人工智能(AI)在当今世界中至关重要,因为它有可能彻底改变我们生活、工作和相互交流的方式。AI 可以自动化重复性和繁琐的任务,释放人力资源,使其能够专注于更具创意和战略性的任务。它还可以改善医疗保健成果,增强客户服务,并优化企业运营。此外,AI 可以通过提供数据驱动的见解和解决方案来帮助我们应对气候变化、贫困和不平等这些复杂的全球性挑战。

领域特定模型

像 HuggingFace 一样,NVIDIA API 目录 非常全面,包含了解决不同领域问题的模型,例如语言、语音、视觉和游戏。

在本文中,我们重点关注四种可用的语言模型:

  • mistralai /mistral-7b-instruct-v0.3
  • mistralai /mixtral-8x22b-instruct-v0.1
  • meta/llama3–70b-instruct
  • meta/llama3–8b-instruct
Mistral 与 Meta 对比:Mistral 7B、Mixtral 8x22B、Llama 3 8B 和 70B 的比较

本节测试四个模型在许可证为 CC BY-SA 4.0 的问题回答数据集 SQuAD 中的表现。该阅读理解数据集包含关于一组维基百科文章的问题。根据上下文,模型应该能够回答正确的问题。对我们用例来说,三个更重要的字段是:

  • question - 模型需要回答的问题。

  • context - 模型需要从中提取答案的背景信息。

  • answers - 问题的文字答案。

为了进行评估,我们使用上述描述的相对答案质量(RAQ)。在这个实例中,我们使用GPT-3.5作为独立的大规模语言模型(LLM),对感兴趣的LLM集合进行排名。它根据真实答案从最好(rank=1)到最差(rank=4)对它们的答案进行排序。

RAQ 使用一种名为 Dunn’s 多重比较检验的统计检验来评估这些大型语言模型(LLMs)的排名之间是否存在统计学上的显著差异。

最后,RAQ 还比较了每秒单词数和平均答案长度,这为模型性能和冗长程度提供了额外的数据点。

我们首先在 env/ 目录下设置一个 env 文件,包含 OpenAI 和 NCG API 密钥:

  • var.env 文件
    OPENAI_KEY=<YOUR_OPENAI_KEY>  
    NGC_API_KEY=YOUR_NGC_API_KEY

然后,我们导入所有库,加载API密钥,并定义要从NVIDIA目录中使用的模型。

    import os  

    import matplotlib.pyplot as plt  
    import pandas as pd  
    import scikit_posthocs as sp  
    import seaborn as sns  
    import utils  
    from datasets import load_dataset  
    from dotenv import load_dotenv  
    from generator import Generator  
    load_dotenv('env/var.env')  
    # 模型  
    llama8b = Generator(model='meta/llama3-8b-instruct', ngc_key=os.getenv("NGC_API_KEY"))  
    mistral7b = Generator(model="mistralai/mistral-7b-instruct-v0.3", ngc_key=os.getenv("NGC_API_KEY"))  
    llama70b = Generator(model="meta/llama3-70b-instruct", ngc_key=os.getenv("NGC_API_KEY"))  
    mixtral = Generator(model="mistralai/mixtral-8x22b-instruct-v0.1", ngc_key=os.getenv("NGC_API_KEY"))

Generator 类负责加载模型并创建由 LangChain 提供支持的 Prompt 模板。它在将查询和上下文传递给大语言模型以获取响应之前进行格式化。

    from langchain import PromptTemplate  
    from langchain_openai import ChatOpenAI  
    from langchain.chains import LLMChain  

    class Generator:  
        """生成器,即LLM,根据问题和上下文提供答案"""  
        def __init__(self, model: str, ngc_key: str) -> None:  
            # 模板  
            self.template = """  
                使用以下上下文来简洁清晰地回答问题:  
                {context}  
                问题:{question}  
                回答:  
            """  
            # LLM  
            self.llm = ChatOpenAI(  
                base_url="<https://integrate.api.nvidia.com/v1>",  
                api_key=ngc_key,  
                model=model,  
                temperature=0.1)  
            # 创建提示模板  
            self.prompt = PromptTemplate(  
                template=self.template, input_variables=["context", "question"]  
            )  
        def generate_answer(self, context: str, question: str) -> str:  
            """  
            根据上下文和用户的问题从LLM获取答案  
            Args:  
                context (str): 最相似的文档  
                question (str): 用户的问题  
            Returns:  
                str: LLM的回答  
            """  
            query_llm = LLMChain(  
                llm=self.llm,  
                prompt=self.prompt,  
                llm_kwargs={"max_tokens": 2000},  
            )  
            answer = query_llm.invoke(  
                {"context": context, "question": question}  
            )  

            return answer['text']

加载了大语言模型后,我们从HuggingFace获取SQuAD数据集并打乱顺序,以确保问题主题的多样性。

    squad = load_dataset("squad", split="train")  
    squad = squad.shuffle()

现在,我们可以将 RAQ 循环应用于 100 个问题和上下文中,并记录上述指标。

    for i in range(100):  
        context = squad[i]['context']  
        query = squad[i]['question']  
        answer = squad[i]['answers']['text'][0]  

        # Llama 8B  
        answer_llama, words_per_second, words = utils.get_llm_response(llama8b, context, query)  
        llama8b_metrics["words_per_second"].append(words_per_second)  
        llama8b_metrics["words"].append(words)  
        # Mistral 7B  
        answer_mistral, words_per_second, words = utils.get_llm_response(mistral7b, context, query)  
        mistral7b_metrics["words_per_second"].append(words_per_second)  
        mistral7b_metrics["words"].append(words)  
        # Llama 70B  
        answer_llama70b, words_per_second, words = utils.get_llm_response(llama70b, context, query)  
        llama70b_metrics["words_per_second"].append(words_per_second)  
        llama70b_metrics["words"].append(words)  
        # Mixtral  
        answer_mixtral, words_per_second, words = utils.get_llm_response(mixtral, context, query)  
        mixtral_metrics["words_per_second"].append(words_per_second)  
        mixtral_metrics["words"].append(words)  
        # GPT-3.5 排序  
        llm_answers_dict = {'llama8b': answer_llama, 'mistral7b': answer_mistral, 'llama70b': answer_llama70b, 'mixtral': answer_mixtral}  
        rank = utils.get_gpt_rank(answer, llm_answers_dict, os.getenv("OPENAI_API_KEY"))  
        llama8b_metrics["rank"].append(rank.index('1')+1)  
        mistral7b_metrics["rank"].append(rank.index('2')+1)  
        llama70b_metrics["rank"].append(rank.index('3')+1)  
        mixtral_metrics["rank"].append(rank.index('4')+1)

函数 get_llm_response 接收加载的 LLM、上下文和问题,并返回 LLM 的回答以及量化指标。

    def 获取_llm_response(model: Generator, context: str, query: str) -> Tuple[str, int, int]:  
        """  
        根据上下文和查询从给定的LLM生成答案  
        返回答案、每秒单词数和总单词数  
        Args:  
            model (Generator): LLM  
            context (str): 上下文数据  
            query (str): 查询  
        Returns:  
            Tuple[str, int, int]: 答案, 每秒单词数, 单词总数  
        """  

        初始化时间 = time.time()  
        llm答案 = model.get_answer(context, query)  
        总时间 = time.time()-初始化时间  
        每秒单词数 = len(re.sub("[^a-zA-Z']+", ' ', llm答案).split())/总时间  
        单词总数 = len(re.sub("[^a-zA-Z']+", ' ', llm答案).split())  
        return llm答案, 每秒单词数, 单词总数

另一方面,get_gpt_rank 函数实现了 RAQ 核心逻辑。它负责接收真实答案和每个大语言模型(LLM)的答案,并向 GPT-3.5 发送请求,根据正确性对它们进行排名。

    def get_gpt_rank(true_answer: str, llm_answers: dict, openai_key: str) -> list:  
        """  
        实现RAQ核心:根据正确答案,使用GPT-3.5对LLM的答案进行排名  
        Args:  
            true_answer (str): 正确答案  
            llm_answers (dict): LLM答案  
            openai_key (str): OpenAI密钥  
        Returns:  
            list: LLM ID的排名  
        """  

        # 从OpenAI获取格式化的输出  
        functions = define_open_ai_function()  
        gpt_query = f"""基于正确答案:{true_answer},对以下四个答案的ID从最正确到最不正确的顺序进行排名:  
            ID: 1 答案:{re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['llama8b'])}  
            ID: 2 答案:{re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['mistral7b'])}  
            ID: 3 答案:{re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['llama70b'])}  
            ID: 4 答案:{re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['mixtral'])}"""  
        completion = OpenAI(api_key=openai_key).chat.completions.create(  
            model="gpt-3.5-turbo",  
            messages=[{"role": "user", "content": gpt_query}],  
            functions=functions,  
            function_call={"name": "return_rank"},  
        )  
        response_message = completion.choices[0].message.function_call.arguments  
        rank = ast.literal_eval(response_message)["rank"].split(",")  
        if len(rank) == 1:  
            rank = list(rank[0])  
        return rank

从图3可以看出,LLaMA 3 8B是最快速的大型语言模型,平均每秒生成约43个单词。在回答长度方面,Mistral 7B生成的回答更长,平均长度为24个单词,远超过LLaMA 70B的8个单词。最后,根据独立的大型语言模型排名,Mistral 7B的平均排名约为2.25,而LLaMA 3 8B的平均排名约为2.8,表现最差。

图3:所有大语言模型(LLM)之间的指标比较(作者提供)

表2展示了Dunn事后检验的结果,比较了不同语言模型的性能。每个单元格表示相应模型之间在5%显著性水平下的性能差异是否具有统计学意义。“显著”表示具有统计学意义的差异(p值≤0.05),而“不显著”表示没有统计学意义的差异(p值>0.05)。

对于选定的显著性水平,邓恩检验的结果表明,Mistral 7B 的表现与 LLaMA 3 8B 显著不同,但与其他大语言模型(LLMs)没有显著差异。为了增加在剩余比较中检测到显著差异的可能性,可以增加测试的样本量(用于排名模型的示例数量)。样本量增大后,如果确实存在排名差异,我们可能会获得更小的 p 值。

    p_values = sp.posthoc_dunn([mistral7b_metrics['rank'], llama8b_metrics['rank'], llama70b_metrics['rank'], mixtral_metrics['rank']], p_adjust='holm')  
    p_values < 0.05

表2:各组大型语言模型(LLMs)排名差异的显著性

如前所述,RAQ 的好处在于它可以用于评估任何一组大语言模型(LLM)在任何领域/主题/使用案例上,而无需依赖传统的基准测试。这意味着,根据使用的数据集不同,在这种评估中不同的模型会脱颖而出。

结论

在开发更强大的大型语言模型(LLMs)并使其易于每个人访问方面取得的快速进展带来了新的采用挑战。公司正在寻找将这些模型集成到内部和外部工具中的新方法。然而,目前选择和部署这些模型的方法在控制、隐私、灵活性和安全性之间存在很大的权衡。

在本文中,我们介绍了RAQ,这是一个用于评估和比较一组大型语言模型(LLMs)答案质量的新框架。它使得为新的应用场景选择一个LLM变得客观、可扩展和灵活。灵活性来源于这样一个事实:当一个组织有以前的私人示例并希望用这些示例测试这组LLMs时,可以应用该框架。即使在没有这些数据集的情况下,该框架仍然可以正常工作。

选择了大语言模型后,我们探索了NIM作为部署模型的解决方案。NIM确保了隐私、安全性和可扩展性,无论模型是部署在物理硬件、私有云还是作为托管服务。

我们应用了相对答案质量(RAQ)并使用NVIDIA NIM来比较Mistral和Meta模型(小版本和大版本)的性能。我们使用了一个阅读理解数据集来展示如何使用RAQ。在我们的设置中,Mistral 7B表现最佳,生成的答案明显优于LLaMA 3 8B。而LLaMA 3 8B实际上是最快的模型,每秒生成的单词更多。这表明选择模型通常需要在质量和速度之间进行权衡。

关于我

连续的创业者,也是人工智能领域的领导者。我为企业开发人工智能产品,并投资专注于人工智能的初创公司。

创始人 @ ZAAI | 领英 | X/Twitter

参考资料

[1] https://developer.nvidia.com/blog/nvidia-nim-offers-optimized-inference-microservices-for-deploying-ai-models-at-scale/ NVIDIA NIM 提供了优化的推理微服务,用于大规模部署 AI 模型。

[2] Dunn, O. J. (1964) 使用秩和进行多重比较. 技术计量学. 6, 241–252. doi:10.1080/00401706.1964.10490181.

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消