热门搜索:和平精英 原神 街篮2 

您的位置:首页 > > 教程攻略 > ai资讯 >从研究到生产:相对答案质量 (RAQ) 和 NVIDIA NIM

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

来源:互联网 更新时间:2026-08-25 13:47

引言

2022年ChatGPT的成功发布,让整个行业第一次真切地意识到:生成式AI不仅能让个人从繁琐的手动任务中解脱出来,对企业而言,更是提升客户体验、优化运营效率的一把利器。

随着对生成式AI解决方案需求的激增,大量公司开始押注开源模型的研究与开发,比如Mistral AI的Mixtral系列,或者Meta的LLaMA 3。巨额的资本注入使得这些模型迅速走向公众,也因此,多数企业从“自研大模型”转向了“部署开源模型”这条更务实的路径。

可问题也随之而来:面对琳琅满目的开源模型,到底该选哪一个?选好了之后,又如何安全、高效地将其规模化部署?本文要聊的,就是围绕这两个核心问题展开的。我们提出了一套名为

RAQ(相对答案质量)

的方法,核心思路很简单——用一个独立的LLM去比较和排序其他LLM给出的答案。这套方法的关键优势在于,它可以无缝嵌入任何工作流程,并且适用于任何领域、任何主题或用例下任意一组LLM的评估。不管你的组织是否有现成的“问题-正确答案”数据集,RAQ都能派上用场。更贴心的是,我们还能量化数据集的大小,并通过统计检验来判断排名差异是否显著,让机器学习从业者能自信地判断出哪个LLM更胜一筹。排名本身是基于答案与真实答案的接近程度,完全不受数据领域、主题或用例的限制。

回答“选谁”之后,下一个问题就是“怎么部署”。为此,我们展示了如何借助NVIDIA的新一代NIM微服务,轻松地将研究成果转化为生产级解决方案。NIM支持LLM的大规模部署,它允许你先用NVIDIA的托管服务快速搭建原型,之后再将模型自托管到任何私有云或物理硬件上,完成生产环境的落地。由于针对加速硬件的配置组合和主流开源模型的推理引擎都已预先优化,整个基础设施的搭建过程几乎不需要额外投入。此外,它还支持通过LoRA对模型进行微调,灵活性相当不错。

RAQ:相对答案质量

ChatGPT发布之后,开源大语言模型的研发投入有多大?夸张点说,几乎每周都有新模型或变体问世。面对如此密集的更新,怎么才能为自己手头的应用场景挑出最合适的那个?

当然,推理时间这类定量指标很重要,在选择LLM时肯定得考虑。但一个推理飞快、回答却一塌糊涂的模型,显然不是我们想要的。

在特定场景下对LLM做定性评估,过去一直是个耗时又费力的活儿。你得基于成百上千个问题去逐一验证,不仅要读一堆答案,还得跟标准答案反复比对,主观性极强。为了把这部分手动工作大幅降下来,我们构思了RAQ框架。

RAQ框架的核心,是依赖一个独立的LLM。它会拿到问题、标准答案以及一组候选LLM给出的答案,然后根据这些答案与标准答案的匹配程度,把它们排个序。万一有的场景下,手头压根儿没有现成的问题和答案数据集,那也没关系——RAQ有一个扩展版本,可以让另一个独立的LLM来帮我们生成这些数据。还有一种混合场景:数据集是有的,但规模太小,不足以支撑RAQ跑出可靠结果。这时候,我们可以拿这个小数据集当种子,让独立的LLM用半合成的方式生成更多类似的样本。

独立LLM本身也可能带有偏见或其他潜在问题。怎么克服?两个选择:要么,挑一个当前能拿到的最好的开源或闭源模型;要么,选一批顶级的模型,分别用它们作为独立LLM来跑RAQ,然后在应用通用RAQ之前,先汇总结果,再计算中位数和标准差。这里有一个必须警惕的关键点:独立LLM,或者你选的那组LLM集合,绝不能出现在被评估的LLM集合中,否则就会对那个特定模型或模型集合产生强烈的偏见。

最后,RAQ还可以加入额外的定量指标,为LLM集合提供更全面的比较。比如,我们可以算一下每秒字数、平均回答长度,这些数据点能帮助我们更立体地评估模型的性能和“话多话少”。

RAQ 实战

实战部分,我们先设计一个提示,让独立的LLM根据答案质量对LLM的ID进行排序:

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

我们多次运行这个过程,记录下每次输出的排名。收集完所有问题下的排名后,再执行邓恩多重比较检验。这个检验可以帮助我们了解所有LLM的排名之间是否存在显著差异,还是说它们表现得差不多。它会针对每一组进行成对比较,然后告诉我们,在预定义的显著性水平(通常是5%)上,哪些组是统计上显著不同的。

NVIDIA NIM:是什么?

NVIDIA NIM本质上是一组提供现成AI模型推理API的容器。它是一种云原生微服务解决方案,目标直指简化部署过程、压缩部署时间。它解决了将AI模型接入现有企业基础设施时的种种复杂问题。

通常,企业部署LLM有三种方式:放在自己的物理硬件上、扔到云上、或者通过第三方托管的API。前两个选项好处显而易见——数据隐私、安全性、模型灵活性全都有保障。但坏处也很真实:需要非常专业的资源,否则很容易出现硬件利用不足、应用性能拉胯等问题。第三个选项虽然解决了性能问题,但数据隐私、安全性和模型灵活性就没法保证了,而且它会成为系统的核心依赖,引入额外的风险。

NIM有意思的地方在于,它试图弥合这些差距。首先,它自己就提供第三方API给你用。关键区别在于,它只提供运行这些模型的微服务,所以不像OpenAI、Anthropic、Google那样,会有动机拿你的数据去做训练。数据隐私和安全是能保证的。第二个选择,就是你可以用NIM在你自己的专用基础设施上部署模型。这种情况下,NIM为每一个模型和硬件配置提供了优化的推理引擎。换句话说,NVIDIA的团队已经帮你把确保低延迟和高吞吐量的关键工作,在底层基础设施层面做完了。而且,你可以在它的目录里挑任何模型,做最小改动就能部署。最后,这些模型还能通过LoRA做微调。

下面是在你自己的基础设施上,从NIM目录里启动一个LLM的操作流程:

  1. 安装Docker
  2. 安装NVIDIA Container Toolkit
  3. 登录NVIDIA docker login
  4. 设置你的NVIDIA NGC API密钥 export NGC_API_KEY=
  5. 定义容器名称、镜像名称和下载模型的本地路径。如果你想部署另一个模型,比如LLaMA 3 70B,只要把IMG_NAME改成对应的镜像名就行。
# 选择一个容器名称用于记录
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"
  1. 运行以下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

搞定。现在我们可以向部署好的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 可以提高各行业(从客户服务到交通运输)的生产力、效率和准确性。

行业标准API

NIM还有一个很讨巧的地方——它可以很好地集成到流行的LLM框架里,比如LangChain和LlamaIndex。实际上,任何兼容OpenAI API的包,只需要改一下base_url,就能轻松接上NIM。

下面这个例子展示了用LangChain和OpenAI的风格来提问。注意这里用的是localhost的URL,因为我们在查询自己刚部署好的LLaMA 3 8B。我们创建了一个简单的模板,接收问题然后丢给LLaMA的LLMChain去处理。

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

template = """
Question: {question}
Answer:
"""
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": "Write a short message explaining why AI is important."}
)

人工智能(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 vs Meta:Mistral 7B、Mixtral 8x22B、Llama 3 8B 与 70B 的对比

这一节,我们在遵循CC BY-SA 4.0许可的SQuAD问答数据集上测试了这四个模型。SQuAD是一个阅读理解数据集,包含关于一组维基百科文章的问题。模型需要根据给定的上下文,检索出问题的正确答案。对我们的场景来说,以下三个字段最为关键:

  • question - 模型需要回答的问题。
  • context - 模型需要从中提取答案的背景信息。
  • answers - 问题的文本答案。

评估方法就是前面提到的RAQ。我们使用GPT-3.5作为独立的LLM来给这四个模型排序。它会根据标准答案,把模型的回答从最好(rank=1)到最差(rank=4)排好序。

RAQ接着会运行Dunn多重比较检验这种统计测试,来判断这几个LLM的排名之间是否真的存在统计上显著的差异。

此外,RAQ还会比较每秒字数和平均答案长度,为模型性能和“爱不爱啰嗦”提供额外的数据点。

先来设置环境变量。在env/目录下创建一个包含OpenAI和NGC API密钥的env文件:

  • var.env 文件
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')
# models
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创建提示模板。它在把查询和上下文丢给LLM之前,会先做个格式化处理。

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

class Generator:
    """Generator, aka LLM, to provide an answer based on some question and context"""
    def __init__(self, model: str, ngc_key: str) -> None:
        # template
        self.template = """
        Use the following pieces of context to give a succinct and clear answer to the question at the end:
        {context}
        Question: {question}
        Answer:
        """
        # llm
        self.llm = ChatOpenAI(
            base_url="https://integrate.api.nvidia.com/v1",
            api_key=ngc_key,
            model=model,
            temperature=0.1)
        # create prompt template
        self.prompt = PromptTemplate(
            template=self.template, input_variables=["context", "question"]
        )
    def generate_answer(self, context: str, question: str) -> str:
        """
        Get the answer from llm based on context and user's question
        Args:
            context (str): most similar document retrieved
            question (str): user's question
        Returns:
            str: llm answer
        """
        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()

接下来,在100个问题和上下文上跑RAQ循环,并记录上述指标。

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 rank
    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 get_llm_response(model: Generator, context: str, query: str) -> Tuple[str, int, int]:
    """
    Generates an answer from a given LLM based on context and query
    returns the answer and the number of words per second and the total number of words
    Args:
        model (Generator): LLM
        context (str): context data
        query (str): question
    Returns:
        Tuple[str, int, int]: answer, words_per_second, words
    """

    init_time = time.time()
    answer_llm = model.get_answer(context, query)
    total_time = time.time()-init_time
    words_per_second = len(re.sub("[^a-zA-Z']+", ' ', answer_llm).split())/total_time
    words = len(re.sub("[^a-zA-Z']+", ' ', answer_llm).split())
    return answer_llm, words_per_second, words

另一头的函数get_gpt_rank则实现了RAQ的核心逻辑。它负责接收标准答案和每个LLM的答案,向GPT-3.5发个请求,让后者按正确性给你排好序。

def get_gpt_rank(true_answer: str, llm_answers: dict, openai_key: str) -> list:
    """
    Implements RAQ core: based on the true answer, it uses GPT-3.5 to rank the answers of the LLMs
    Args:
        true_answer (str): correct answer
        llm_answers (dict): LLM answers
        openai_key (str): open ai key
    Returns:
        list: rank of LLM IDs
    """

    # get a formatted output from OpenAI
    functions = define_open_ai_function()
    gpt_query = f"""Based on the correct answer: {true_answer}, rank the IDs of the following four answers from the most to the least correct one:
ID: 1 Answer: {re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['llama8b'])}
ID: 2 Answer: {re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['mistral7b'])}
ID: 3 Answer: {re.sub("[^a-zA-Z0-9']+", ' ', llm_answers['llama70b'])}
ID: 4 Answer: {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个单词。最后看独立LLM给出的排名,Mistral 7B的平均排名最好,大约2.25,而LLaMA 3 8B表现最差,平均排名大约在2.8。

表2展示的是Dunn事后检验的结果,用于比较不同语言模型的性能。表格里的每个单元格,表示对应模型之间的性能差异在5%显著性水平下是否具有统计意义。“显著”代表p值≤0.05,差异是统计显著的;“不显著”代表p值>0.05,差异没被检测出来。

在选定的显著性水平下,Dunn检验结果显示:Mistral 7B的性能跟LLaMA 3 8B有显著差异,但跟其他两个LLM之间没有看出明显的差异。如果想提高剩下几组对比中检测出显著差异的可能性,一个策略就是增加检验的样本量(也就是用于排名的示例数量)。样本量大了,如果差异真的存在,我们很可能拿到更小的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

前面也提到过,RAQ的一个核心优势在于,它可以用来评估任何领域、主题或用例下的任何一组LLM,而不是像传统方法那样只能依赖既定基准。这意味着,换一套数据集,评估结果里领先的模型可能就完全不同了。

结论

更强大的LLM层出不穷,而且越来越容易获取,这给实际落地带来了新的挑战。企业正在寻找各种方式,把它们集成到内部或者面向客户的工具里去。但当前选择和部署这些模型的方法,在控制权、隐私、灵活性和安全性之间,存在着巨大的权衡空间。

这篇文章介绍了RAQ——一个用于评估和比较一组LLM答案质量的新型框架。它让为新用例选择LLM这件事变得更加客观、可扩展和灵活。灵活性体现在,无论组织是否有现成的私有样本,甚至在没有这类数据集的情况下,它都能正常工作。

选好模型之后,我们又探讨了NIM作为大规模部署模型的解决方案。它保证了开箱即用的隐私、安全性和可扩展性,不管你的模型是部署在物理硬件上、私有云里,还是作为托管服务运行。

最后,我们实际应用RAQ,并用NIM比较了Mistral和Meta的几个模型(包括小参数版和大参数版)。我们拿一个阅读理解数据集作为例子,展示了RAQ的完整用法。在我们这次的设定中,Mistral 7B是最优的,生成的答案明显好过LLaMA 3 8B。但话说回来,LLaMA 3 8B又是跑得最快的那个,每秒能吐更多的词儿。这也说明了一个很现实的问题:选模型,通常就是在质量与速度之间做权衡。

关于宇宙的好的网名有哪些
关于宇宙的好的网名有哪些

类型:角色扮演

大小:1

语言:简体中文

平台:互联网

游戏下载

热门手游

手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc