LLM评估
目前,大语言模型(LLM)已经变得非常复杂。在万亿(Trillion, T)量级的token上进行了预训练,并且进行了SFT、RLHF等精密的后训练,LLM表现出了很强的指令遵循能力。
然而,在生产环境中,可能仍然有一些bad case需要修复。另外,出于成本考虑,在保证性能可接受的前提下会用尽可能小的模型来降低推理成本。
这使得业务场景下LLM微调的需求很旺盛。构造一份目标任务的数据集,微调让LLM对其中的示例进行拟合可以很直接地修复bad case,并使得参数量较小的模型快速提升目标任务的性能。然而,微调一方面拟合了目标任务数据集中的示例,另一方面又存在过拟合、灾难性遗忘等风险,可能影响模型在目标任务上的综合表现。为了趋利避害,建立完善的评估机制来最大化微调后模型的收益,最小化风险非常重要。
目前有一些框架已经能够处理LLM的评估了: 1. Evaluate 2. RAGAs 3. evals 4. lm-evaluation-harness(推荐) 5. Argilla
这些框架基本都能处理一些简单的LLM评估,但侧重于不同方面,也基本都有着一些独特的优势和局限性。
Evaluate
Evaluate
是huggingface官方推出的评估库,用来轻松评估机器学习模型和数据集。优点是:
huggingface官方评估库,和huggingface的训练框架集成度高,指标评估流程可以直接集成进行训练流程。
指标多,考虑的方面更全。
Evaluate
提供了Metric
、Comparison
、Measurement
三个概念。其中Metric用于评估模型性能,并且对常用的评估指标提供了现成的脚本去计算。Comparison用于比较两个模型,有些指标并无绝对好坏,但通过比较可以区分模型的性能。比如常用的困惑度就是一个表达相对好坏的指标,并无一个确切地阈值来表征对或不对,但对于正确答案困惑度相对较低的模型一般性能更好。Measurement用于评价数据集的质量。比如文本重复度、标签分布等。从模型和数据集两个方面来进行评估考虑,并且模型评估时考虑了绝对指标和相对指标。
支持任务广泛。不仅支持传统的分类任务,还支持NLP、CV等领域的多种分类任务。
因为这里讨论的是关于LLM的评估,当然其缺点也很明显:
- 因为其针对的是更宽泛的机器学习评估,所以对LLM评估没有过多优化。对于数据集加载、LLM调用、结果提取、超时重试处理等都需要自己编写代码,会比较复杂。
- 加载指标需要连接huggingface官网,可能由于网络原因连接失败。
总的来说,Evaluate
实现了评估的一些基本功能,但除非是想开发一套完全透明的LLM评估框架,否则一般有更好的选择。
RAGAs
RAGAS(Retrieval Augmented Generation Assessment)是专用于自动化评估检索增强(RAG)系统的评估框架,算是一个特化的LLM开源评估工具。
论文地址:RAGAS: Automated Evaluation of Retrieval Augmented Generation
项目地址:RAGAs
RAGAs专注于RAG系统的评估,并且和需要GT的评估不同,它甚至可以在没有人工标注的情况下完成RAG系统的评估。其核心为3个性能指标:忠实性、答案相关性和上下文相关性。
忠实性
忠实性指的是答案应基于给定的上下文。这对于避免幻觉并确保检索到的上下文可以作为生成答案的正当性依据非常重要。通俗地讲,就是不管检索出来什么事实,在RAG系统中LLM就应该根据这个检索出来的事实回答问题,而不是自作主张地变更。比如检索出来0的0次方是0
的事实,那在回答问题0的0次方的值是什么?
时,LLM就不应该回答无意义,而应该根据检索出来的事实回答是0。
它的评估依赖gpt-3.5模型的提示学习能力。如果答案\(a_s(q)\)中的陈述可以从上下文\(c(q)\)中推断出来,我们就说该答案忠实于上下文。为了估计忠实性,我们首先使用 LLM提取一组陈述\(S(a_s(q))\)。作者使用以下提示:
1 | 给定一个问题和一个答案,从给定答案的每个句子中创建一个或多个陈述。 |
其中 [question] 和 [answer] 指的是给定的问题和答案。对于\(S\)中的每个陈述\(s_i\),LLM 使用验证函数\(v(s_i, c(q))\)确定\(s_i\)是否可以从\(c(q)\)中推断出来。这个所谓的验证函数其实也是通过提示驱动gpt-3.5完成的:
1 | 考虑给定的上下文和以下陈述,然后确定它们是否被上下文中的信息所支持。在得出结论(是/否)之前,为每个陈述提供简要解释。最后按照指定 |
最终的忠实度分数\(F\)计算为: \[ F=\frac{|V|}{|S|} \] 其中,\(|V|\)是LLM认为被上下文信息支持的陈述的数量,\(|S|\)是陈述的总数。
答案相关性
如果答案\(a_s(q)\)以适当的方式直接回答了问题,我们就说答案是相关的。这里评估答案相关性的方式不是直接问LLM答案和问题是否相关,而是转了一手,先让LLM根据答案生成可能的问题,然后根据这些生成的问题与原问题的相关性分数来衡量答案相关性。这里可能是想剥离问题本身和答案的对应关系,即答案是不是正确的这里是不考虑的。如果直接问答案和问题是否相关,则可能受到答案本身是否正确的特性影响,如果生成对应的问题后再计算相关性就能规避。
首先,利用提示学习对答案\(a_s(q)\)生成\(n\)个可能的问题\(q_i\):
1 | 为给定的答案生成一个问题。 |
然后使用openai的嵌入模型(原论文中使用的是text-embedding-ada-002,现在有更新的了)为所有问题生成嵌入。对所有生成的问题\(q_i\)与原始问题\(q\)的嵌入计算余弦相似度,作为两个问题之间的相似度\(sim(q_i,q)\)。最后问题问题\(q\)与答案\(a_s(q)\)的答案相关性\(AR\): \[ AR=\frac{1}{n}sim(q_i,q) \]
上下文相关性
如果上下文中包含了回答问题所需要的信息,则上下文被认为是相关的,并且冗余信息越少,相关性越高。上下文相关性目的是惩罚上下文中包含的冗余信息。为了估计上下文相关性,给定一个问题\(q\)及其上下文\(c(q)\),LLM 从\(c(q)\)中提取一个句子子集\(S_{ext}\),这些句子对回答\(q\)至关重要,使用以下提示:
1 | 请从提供的上下文中提取可能有助于回答以下问题的相关句子。如果未找到相关句子,或者您认为无法从给定上下文中回答问题,请返回“信息不足”。在提取候选句子时,您不得对给定上下文中的句子进行任何更改。 |
之后根据候选句子的数量计算上下文相关性\(CR\): \[ CR = \frac{候选句子数量}{c(q)中句子的总数量} \]
方法效果
Faith. | Ans. Rel. | Cont. Rel. | |
---|---|---|---|
RAGAs | 0.95 | 0.78 | 0.70 |
GPT Score | 0.72 | 0.52 | 0.63 |
GPT Ranking | 0.54 | 0.40 | 0.52 |
Table 1: 在使用 WikEval 数据集进行成对比较时,与人工标注者在忠实性、答案相关性和上下文相关性方面的一致性(准确率)。
上表展示了RAGAs在作者提出的WikEval数据集上与人工评估的相关性。可以看到相比GPT Score和GPT Ranking这两种基线方法,RAGAs的效果都要好。尤其在忠实性方面已经做到开箱可用的程度了。
但答案相关性和上下文相关性仍然不那么好。对于答案相关性,作者解释是:
对于答案相关性,一致性较低,但这主要是由于两个候选答案之间的差异通常非常微妙。
但我感觉问题在使用语义嵌入余弦相似度来判断生成问题\(q_i\)和原问题\(q\)之间的语义相似度,因为语义嵌入本来就是一个相对粗粒度的指标,对于从一个大集合中快速召回一个小集合,这种方式很高效。但更细粒度的操作一般不会直接操作语义嵌入的,因为精度有限。计算\(AR\)时如果也使用提示学习进行打分会不会更好?
对上下文相关性,作者解释是:
我们发现上下文相关性是最难评估的质量维度。特别是,我们观察到 ChatGPT 在选择上下文中关键句子的任务上常常遇到困难,尤其是对于较长的上下文。
这是在说gpt-3.5在指令遵循方面有问题吗?也许原来的方法不变,人为地在每个句子前标个序号,让LLM挑出与问题相关的句子序号集合会更简单一些。(这个可以参考一下这篇文章:Set-of-Mark Prompting Unleashes Extraordinary Visual Grounding in GPT-4V,同样的视觉任务,如果能有预先分割的物体标号效果就会更好)
因为token很多,直接复述上下文LLM确实会出现一些偏移导致匹配失败。现在有4o这样更好的模型,指令遵循能力应该更好,选择关键句子的困难应该会缓解一些。
总的来说,RAGAs的几个指标非常有参考意义,而且因为不需要参考答案,实战价值也很高。其实这个RAGAs就很多地用了LLM-as-a-Judge的概念,并且结合了RAG的流程进行了充分的设计,因此相比基线得到更好的结果也是可以预见的。
对我的启发
这一节其实和LLM评估没太大关系,主要是想起来之前面试时有问到“你觉得RAG系统里面最重要的部分是什么?”。我当时想这个问题真有点宽泛,一个RAG系统,有数据收集处理、分块、索引、召回、排序、增强生成这么多步骤,里面每一步都可能会影响到最后结果,我也很难说最重要的是什么。我当时硬答的是“分块最重要“,主要是当时觉得按字符数量/token数量+重叠的分块虽然有效,但没怎么考虑语义连贯性。加上当时又有进行语义chunking的一些尝试,所以也就这么讲了。但现在回想起来也挺片面的,因为分块的问题毕竟属于边界问题,能妥善解决当然最好。不能就按指定大小硬切其实也未必RAG的效果就不好,因为文档它就不一定有那么强的语义连贯性。属于是捡芝麻丢西瓜了。
回顾一下整个RAG系统,其实是一个流程(这样的流程有个比较潮的词叫工作流,workflow)固定的Agent系统。首先,数据收集处理里面,一般可能会出现多种数据源提取信息,比如从pdf、word等里面提取信息的需求。这里面就涉及到文本结构化信息的保留、表格怎么无损提取、图表(这里指柱状图、饼状图这样的chart)的信息怎么进行转换的问题。其次,入库前需要把长文档分成小片进行索引。分片的时候即使按照最简单的字符数量+重叠字符数来进行分片,也至少有2个超参数,而且实践中这俩超参数对RAG的效果影响还挺显著的。索引大概分为稀疏索引(如TF-IDF、BM25等)和密集索引(也就是各种嵌入模型生成的嵌入)两种,前者可以实现精确搜索,后者能召回语义相似的片段,注重效果的系统里一般是两者一起上。再后面就是召回,刚才说了一般就是多路召回,稀疏索引和密集索引一起用上,尽量保证召回率。再之后就是排序,排序一种是根据问题和内容精排,嵌入模型的嵌入计算余弦相似度的粒度还是太粗,要那种同时能看到问题和上下文的模型打分就会准很多,比如ColBERT。另外一种就是大模型对于不同位置的上下文关注度不一样,最重要的信息需要放到开头和结尾,可以参考:Lost in the Middle: How Language Models Use Long Contexts。最后还有增强生成,模型友好的prompt是一方面,如果prompt实在解决不了,还得微调LLM。
这里面每一步都有不少可选项,如果都实施一遍可能黄花菜都凉了系统还没搭起来。而且如提取表格、转换图表甚至转换图像信息是非常复杂的,成本可能很高。字符数量+重叠字符数来进行分片需要实验验证。在这么一套复杂系统里,请问什么最重要?
我现在认为是评估。RAG系统说到底还是讲究收益的系统,如果对于评估的大部分case能通过,那么他即使有弱点有corner case,但仍然是很有用的系统。所以如何建立和真实业务表现强相关的评估才是RAG里最重要的事情,根据现有的bad case,确定解决方案,定向地优化RAG系统是提升投入产出比的重要途径。所以让我现在回答,我会说是评估,通过评估确定基线系统的性能,之后分析bad case进行定向优化。
evals
evals
是openai官方的LLM评估库。evals
提供了一个框架,用于评估大型语言模型(LLMs)或使用LLMs构建的系统。其提供了一个现有的评估注册表,以测试OpenAI模型的不同维度,并允许使用自有用例编写自定义评估。还可以使用自己的数据构建私有评估,这些评估代表了您工作流程中常见的LLMs模式,而无需公开任何数据。
如果您正在使用LLMs进行构建,创建高质量的评估是您可以做的最有影响力的事情之一。没有evals
,理解不同模型版本可能如何影响您的用例可能会非常困难和耗时。正如openai的ceo所说:
evals are surprisingly often all you need. ——openai ceo Greg Brockman
本文一开始就是为evals
这个碟醋包的这盘饺子,但深入了解之后感觉这个库还是不如后面要谈的lm-evaluation-harness
通用,因此后面的实操还是使用的lm-evaluation-harness
库。
当然,这个库还是有一些亮点值得借鉴:
- 对于支持OpenAI的库调用的大模型来说,需要自己编写的代码很少,一般只需要按格式准备好数据的jsonl即可。
- 提供了3个由模型进行评分的评估(model-graded evals)模板,对使用LLM进行评估打分很有启发。
缺点就是只支持自家的OpenAI模型,对于本地跑的模型可能需要添加额外的转换层。然后对评估数据管理比较粗放,jsonl对文本数据可能还没啥,如果之后要进行多模态评估可能就比较麻烦。
接下来将从评估数据准备、评估指标说明和运行评估3个维度说明这个库的使用。
评估数据准备
基本是需要在评估之前将数据格式化为每行为包含input
和ideal
的字典的jsonl
格式。input
字段包含输入给LLM的必要信息,包含对任务的解释说明、任务示例(可选)和待处理的数据,并且格式化为system
,
user
以及assistant
三者的组合。在openai官方的示例中,基本只需要发送带system
角色的消息即可。ideal
字段则根据所要评估的指标不同而有所区别,可以输入单个参考答案,也可以以列表形式输入多个参考答案。以下是示例:
1 | {"input":[{"role":"system","content":"You are on an island populated by two tribes. Members of one tribe consistently lie. Members of the other tribe always tell the truth. Tribe members can recognize one another, but you can't tell them apart. You meet two people, C and D on the island. C says, 'Exactly one of us is from the liars tribe.' Which tribe is D from?"}],"ideal":"D is from the Liars tribe."} |
准备好这样格式的jsonl文件后,则需要将该任务注册到evals
库中。所谓注册就是编写指定格式的yaml文件到evals/registry/evals/<eval_name>.yaml
路径下。示例如下:
1 | <eval_name>: |
evals
的命名约定采用以下形式<eval_name>.<split>.<version>
。
<eval_name>
是评估名称,用于对分数可比较的评估进行分组。<split>
是数据分割,用于进一步对同一下的评估进行分组<base_eval>
。例如,用于测试的“val”、“test”或“dev”。<version>
是 eval 的版本,可以是任何您想要使用的描述性文本(但最好不包含.
)。
一般而言,针对同一模型运行相同的评估名称应始终产生类似的结果,以便其他人可以重现它。因此,当您更改评估时,应该更改版本。
评估指标
评估指标这部分基础评估指标(Basic eval)和由模型进行评分的评估(model-graded evals)指标。
基础评估指标主要用来评估多项选择题或者判断题这种答案相对固定的题目。在所需模型响应变化很小的情况下,例如回答多项选择题或具有直接答案的简单问题,我们发现以下模板很有用。
对于模型完成a
和正确答案的参考列表B
,以下评估实现:
basic/match.py:Match
:any([a.startswith(b) for b in B])
basic/includes.py:Includes
:any([(b in a) for b in B])
basic/fuzzy_match.py:FuzzyMatch
:any([(a in b or b in a) for b in B])
也即Match
、Include
和FuzzyMatch
三种基础评估实现。
对于由模型进行评分的评估,evals
库给了几种评估模板,在evals/registry/modelgraded
目录下。其中有3种非常常用,我觉得有必要单独拿出来讲一下,对LLM-as-a-Judge还是比较有启发的。
fact
事实一致性评估,给定完成a
和参考答案b
,返回:
"A"
如果a
⊆b
,即提交的答案是专家答案的子集,并且与专家答案完全一致。"B"
如果a
⊇b
,即提交的答案是专家答案的超集,并与其完全一致。"C"
如果a
=b
,即提交的答案包含与专家答案相同的所有细节。"D"
如果a
≠b
,即提交的答案与专家的答案存在分歧。"E"
如果a
≈b
,即答案有所不同,但从事实性的角度来看,这些差异并不重要。
这样的评估是如何完成的呢?其实是通过提示工程驱动LLM完成打分的,我们可以看一下fact指标的yaml配置文件,将prompt翻译为中文以便理解,为了便于说明也将默认参数补上了:
1 | fact: |
其实可以从prompt看出这就是让LLM继续做一个选择题,从而评估提交的答案和GT的关系。但LLM只是个语言模型,是如何做选择题的呢?实际上这也就是在prompt后面补一段话,然后利用LLM的指令遵循能力做选择题的。通过指定eval_type
这个参数来决定在原prompt后面添加什么指令:
1 | ANSWER_PROMPTS = { |
大概就这三种参数,官方推荐的是cot_classify
,因为毕竟是自回归模型,只有先输出原因后面做决策的时候才能进行参考,一般来说也是cot_classify
效果最好。然后后面和get_choice
函数一配合就能选出答案了:
1 | def get_choice( |
这里面还有一个比较有意思的参数choice_scores
,这个可以不填,不填的效果就类似于我们做多项选择题,如果做对了就得分,做错了不管选啥都得0分。但是对模型评估来讲,如果是非常离谱的答案可能需要更重的惩罚,比如这里的事实检验任务,如果是C
应该得最高分,E
和C
相同,而A
则是回答存在冗余,但都比D
得分要高,因此在计分时可以考虑对D
分配低分数,来达到差异化评估的效果。
1 | def get_choice_score( |
对fact指标可以观察其各选项占比来生成最后报告:
高 A, C 类占比:
- 系统生成的答案整体准确性高。
高 B 类占比:
- 系统倾向于生成超集答案,可能是冗余信息较多。
高 D 类占比:
- 系统在事实性一致性方面有显著问题,需要重点优化。
高 E 类占比:
- 系统生成的答案差异较小,但可能需要更严格的评估标准。
closeqa
closeqa是一个问答评估模板,其中在提供包含问题和回答问题所需信息的提示的情况下,检查模型的回答是否:
- 相关(relevant):即从提示中提供的信息中提取;
- 简洁(concise):即不包含不必要的细节或信息;
- 正确(correct):即使用提取的信息得出正确的结论。
同样也是通过提示工程实现的,也贴出对应的yaml文件:
1 | closedqa: |
看prompt可以很容易发现这是一个回答Yes或者No的问题,那么怎么评估相关性、简洁性和正确性呢?实际上这几个性质应该通过criteria
字段进行描述。但我们可以发现,配置文件里并不包含关于criteria
的具体信息。实际上,这是在准备的数据集里面定义的。我们在之前的评估数据准备章节里说到需要准备input
字段和ideal
字段,这里是特殊情况,我们额外需要准备criteria
字段以告诉LLM我们要评估哪个标准。比如,我们要专门评估相关性,就可以建一个criteria
字段描述相关性的jsonl文件:
1
{"input": "北极熊可以生活在南极吗?", "ideal": "不能", "criteria": "以提交的答案和任务的相关性进行打分,如果强相关则符合标准,否则不符合。"}
通过很多个这样的示例,可以构造一个专门用于评估相关性的数据集。同样,也可以改变该字段用来评估简洁性、正确性等,甚至如果这几个指标同等重要也可以进行混合。最后这个prompt用到了choice_scores
,就是如果是Y那模型就在该项上得1分,否则不得分。
battle
一对一评估,比较两个可能不同的提示的两个模型完成情况。choice_scores
这里用于记录判断第一个完成情况优于第二个完成情况的频率。
1 | battle: |
用于比较两个模型输出的相对好坏,因为有时候没有对照很难说一个回答有多好,但如果要比较两个回答则可能相对容易。在实际操作中,我们一般会使用更具体的指令告诉模型去比较哪些方面。比如我们训练一个讲笑话模型,可能就会问第一个模型的输出是否比第二个模型输出更幽默。
以上是三个基于模型的评估指标,基本涵盖了使用LLM评估答案效果的大部分情况,可以根据具体情况调整以获得更好效果。
运行评估
准备好数据、配置好指标、注册好任务之后,就可以开始运行评估了:
1 | oaieval gpt-3.5-turbo <eval_name> |
这样就可以使用gpt-3.5完成整个评估流程。
lm-evaluation-harness
lm-evaluation-harness
是EleutherAI开源的语言模型评估套件,被用做Open
LLM
Leaderboard的后端实现,可以说lm-evaluation-harness
是目前最in最流行的大模型评估框架了。其原生支持了MMLU、GMS8K、GPTQ等多种流行大模型评测任务的开箱即用评测,并且也支持从VLLM评测本地模型、调用API评测远端模型。同时,它还支持使用Zeno、Weights
and
Biases等可视化工具对评测结果来对评测结果进行可视化展示。基本上一站式搞定了LLM评估的全流程,因此对这个框架会进行实操以加深熟悉度。
框架安装
1 | git clone --depth 1 https://github.com/EleutherAI/lm-evaluation-harness |
与evals
相似,官方推荐使用"-e"就地安装,这样代码的更改可以直接生效,方便我们注册新任务。
当然,一般需要根据需要安装一些额外的依赖项,一般会使用api
以调用openai形式LLM的api。另外,也会使用wandb
来可视化输出。因此,可以这样安装:
1 | pip install -e ".[api]" |
任务注册
和evals
类似,lm-evaluation-harness
也是将新任务放置于特定目录,然后自动进行注册的。
lm-evaluation-harness
的任务放置于lm_eval/tasks
目录下,任务通过yaml
文件进行注册,其模板如下:
1 | task: <任务名> |
task
参数类似于任务的ID,需要唯一化。
而评估的数据集方面,lm-evaluation-harness
选择托管给huggingface的datasets库进行处理。
output_type
方面则具有几种选择:
generate_until
:这是当下LLM最常用的评估手段,即给定源输入(source),以及生成参数,之后让模型生成该问题的输出(output)。loglikelihood
:这其实是之前语言模型尚没有很好的指令遵循能力时常用的一种评估方法。会直接给定源输入(source)和目标输出(target),最后返回基于此输入,语言模型产生目标输出的对数似然。对数似然可以用于衡量语言模型在给定输入条件下对于目标输出的倾向性,因此可以用于做多项选择题。loglikelihood_rolling
:计算输入字符串在语言模型上的对数似然,它是loglikelihood
的一种source为空字符串的特殊形式,指标的意义实际上和困惑度比较接近。主要是用于评估模型在该数据分布上的预测能力。
\[ \text{PPL} = \exp\left(-\frac{1}{N} \sum_{i=1}^N \log P(w_i | w_1, w_2, \ldots, w_{i-1})\right) \]
\[ \text{loglikelihood\_rolling} = \sum_{i=1}^N \log P(w_i | w_1, w_2, \ldots, w_{i-1}) \]
\[ \text{PPL} = \exp\left(-\frac{\text{loglikelihood\_rolling}}{N}\right) \]
目前评估LLM大多数使用第一个参数,直接评估模型输出和用户使用场景也是最贴近的。
generation_kwargs
主要用于指定LLM生成时的一些参数。
doc_to_text
就是编写prompt的地方,可以利用两个花括号将doc
数据中的字段传入prompt模板中。同理,doc_to_target
也是类似的。
process_docs: !function utils.process_docs
这一行可以用于指定处理Dataset
的函数,该函数可以写在注册任务同级的utils.py
中。可以通过Dataset
的filter和map方法方便地对每一行数据进行适当的处理。比如:
1 | def process_docs(dataset: datasets.Dataset) -> datasets.Dataset: |
filter_list
则是指定从模型输出中寻找答案的方式,一般使用正则表达式。比如示例中就通过“####
答案: ([ABCDE])”的正则表达式,取第一个匹配的项目作为答案。
metric_list
指定评估的指标,这部分也可以参考evals
的评估指标进行设置。这里因为只是选择题,可以直接使用exact_match
精确匹配来进行衡量。如果有用LLM做评估的,似乎也在进行中,参考:TODOs
for Implementing LLM-as-a-Judge in Eval-Harness (Work in Progress)
#2233。
最后,metadata
里可以用来记录评估任务的版本信息。
运行评估
命令行评估
lm-evaluation-harness
的直接使用途径是通过命令行,即:
1 | lm_eval --model <模型类型> \ |
如果仅使用预设任务评估预设模型(如openai的模型或huggingface上托管的模型),这样是相对简单直观的。如果需要更加个性化的设置,可以进一步参考参数说明。
脚本评估
有时候我们自己编写的评估任务可能希望其他用户使用命令行如调用预设任务一般执行评估,但在开发过程中可能有一些边界条件需要调试。这时候如果直接通过命令行调试可能就得用pdb调试器调试了。
但有时候我们使用了现代化的IDE(如VSCode),我们希望直接在现代化IDE的可视化界面而不是在相对简陋的命令行上执行调试。这时候我们需要将命令行调用转为脚本调用。源代码的lm_eval/__main__.py
中处理命令行评估的相关问题:
1 | # lm_eval/__main__.py |
那我们可以使用一个脚本模拟其调用,如:
1 | import argparse |
这样我们可以使用VSCode调试器如调试其他python程序一般调试新任务或者新模型代码。
有时候一些由yaml配置文件转过来的调用,如:
1 | process_docs: !function utils.process_docs |
这里面utils.py
中的process_docs
函数可能会遇到打断点打不上的问题。原因可能是yaml文件中的
!function
是通过反序列化机制动态解析的。这种方式通常通过反射或 import
动态加载函数,而不是直接导入python模块。这可能使调试器无法正确关联源代码位置。
这时候可以使用python
3.7(笔者写这个的时候python版本基本都比3.7高了,因此可以直接当作标准库内置函数用了)引入的breakpoint
函数来手动打断点。有些资料说这个breakpoint
函数相当于pdb.set_trace()
,这个理解在默认情况下是对的,直接调用该函数确实会进入pdb调试。但在VSCode调试环境下,breakpoint
函数会调用VSCode关联调试器的断点函数,具体可以参考python文档:
breakpoint
函数会在调用位置进入调试器。 具体来说,它将调用sys.breakpointhook()
,直接传递args
和kws
。 在默认情况下,sys.breakpointhook()
将不带参数地调用pdb.set_trace()
。 在此情况下,它纯粹是一个便捷函数让你不必显式地导入pdb
或键入过多代码即可进入调试器。 不过,sys.breakpointhook()
也可被设置为某些其他函数并被breakpoint()
自动调用,允许你进入选定的调试器。 如果sys.breakpointhook()
不可用,此函数将引发RuntimeError
。在默认情况下,
breakpoint()
的行为可使用PYTHONBREAKPOINT
环境变量来改变。 请参阅sys.breakpointhook()
了解详细用法。
因此,可以在需要调试的代码上方加入breakpoint()
函数来手动打断点,防止yaml反序列的函数无法正确关联源代码位置,并且可以正常使用VSCode调试器。
外部库使用
lm-evaluation-harness
甚至还支持集成到外部库中,这样可以在训练时及时对模型能力进行评估。集成代码如下:
1 | import lm_eval |
这里的Your_LM
需要进一步实现:
1 | class MyCustomLM(LM): |
这样灵活性很强,可以说既有Evaluate
库应用范围广的优点,也有evals
为评估语言模型设计、灵活易用的优点。
Argilla
Argilla 是人工智能工程师和领域专家构建高质量数据集的协作工具。Argilla实际并不严格是一个LLM评估工具,它更多的是一个数据集管理工具。但在LLM可以作为Teacher模型,甚至可以作为Judge的当下,管理LLM的输出,提升LLM输出质量以构建高质量数据集也成为模型性能迭代的重要议题。构建高效的数据飞轮以提升数据集规模和质量、缩短数据提质周期对LLM业务落地至关重要。
Argilla 的编程方法让您可以构建工作流程以进行持续评估和模型改进。Argilla 的目标是通过快速迭代正确的数据和模型来确保您的数据工作获得回报。计算成本高昂,输出质量也很重要。我们帮助您专注于数据,从而同时解决这两个问题的根本原因。Argilla 可帮助您实现并保持数据的高质量标准。这意味着您可以提高 AI 输出的质量。
这个工具之后可能也需要学习一下,这里给个文档地址标记一下:Argilla文档