
语言模型评测的重要性可以用一个简单的比喻来理解:就像我们不能仅凭汽车的外观和参数来判断其性能一样,我们也不能仅凭语言模型的参数数量和训练数据量来评估其实际能力。我们需要通过系统的"路试",即各种评测方法来全面了解模型的真实表现。
同样的,在我们工作中,如果我们需要为公司购买一台新的服务器,我们首先不会仅仅因为销售员说“它很快”就下单,而是要求看性能测试报告,比如CPU跑分、硬盘读写速度等。基于此,评测语言模型也是出于完全相同的原因,我们需要用客观、系统的方法来了解它的真实能力,而不是仅仅依赖厂商的宣传或我们的主观感受。

在人工智能快速发展的今天,语言模型已经成为推动技术进步的重要力量。从简单的聊天机器人到复杂的决策辅助系统,语言模型的应用场景日益广泛。然而,如何科学地评估这些模型的性能,确保它们在实际应用中能够可靠地工作,成为了一个关键问题。
得到一个语言模型后,我们需要对其生成能力进行评测,以判断其优劣,通俗的讲,就像我们不能仅凭一本教科书的前言来判断它是否是一本好书一样,我们也不能只听厂商宣传就说一个语言模型强大。我们需要一套系统的“考试方法”来全面评估它的智商和情商。传统的评测方法往往局限于单一的维度,要么只关注模型的基础语言能力,要么只关注在特定任务上的表现。这种片面的评估方式往往无法全面反映模型的真实能力。一个困惑度很低的模型可能在实际对话中表现糟糕,而一个在特定任务上表现优异的模型可能缺乏泛化能力。
1. 衡量能力水平,了解它能力有多强
2. 发现缺陷与局限,知道它哪里不行
发现这些问题对于规避应用风险至关重要。
3. 指导研发与优化,明白我们该如何改进它
通过评测发现模型的短板,不擅长数学计算或容易胡说八道,为开发者优化方向提供依据。对模型开发者而言,评测是指引方向的罗盘。通过分析模型在哪些题目上得分低,研发团队可以有针对性地:增加特定类型的数据进行训练、调整模型架构或算法以及进行后续的微调(Fine-tuning)和强化学习(RLHF)。
4. 确保安全与责任,确定它是否安全可靠
语言模型的影响力巨大,一旦被滥用或出错,后果严重。评测尤其是安全和伦理评测就像安全质检,确保模型在出厂前尽可能排除重大风险,符合伦理和法律规范,从而更负责任地推向社会。
总而言之,评测是连接模型研发与实际应用的桥梁。没有科学系统的评测,使用语言模型就像盲人摸象,我们既无法真正了解它的能力,也无法放心地将其应用于关键场景。
评测语言模型是一个系统工程,主要有两大流派:内在评测和外在评测。
内在评测是语言模型评估的基础,它专注于评估模型的核心语言能力,而不考虑任何特定的应用任务。这种方法类似于测试学生的基础知识掌握程度,而不是测试他们解决具体问题的能力。
核心思想: 在隔离、纯净的环境下,检验模型最核心的语言建模能力,即“预测下一个词”的准确度。
主要指标:困惑度(Perplexity, PPL)。可以通俗地理解为“模型在预测时会感到有多困惑”。PPL值越低,说明模型对文本越熟悉、预测越准确。如给模型一句“中国的首都是__”,一个好的模型会非常确定地预测“北京”,此时它的“困惑度”很低。
优点: 标准、客观、计算快速。
缺点: 像个书呆子考试,分数高不一定代表解决实际问题的能力强。
困惑度(PPL)是内在评测中最核心的指标。它可以理解为模型在预测下一个词时的"不确定程度"。一个较低的困惑度值意味着模型能够更准确地预测文本序列,表明它具有更好的语言理解能力。
从数学角度来看,困惑度是交叉熵损失的指数形式。具体计算公式为:PPL(W) = exp(-1/N * ΣlogP(w_i|w_1,...,w_{i-1})),其中W是文本序列,N是序列长度,P是模型预测的概率。
为了更好地理解困惑度,我们可以考虑几个具体的例子。对于一个训练良好的模型,在预测"今天天气很___"这样的常见句式时,困惑度会很低(可能小于20),因为模型很"确定"下一个词可能是"好"、"热"等常见词语。而对于"量子纠缠是___"这样的专业句式,困惑度会相对较高(可能在50-100之间),因为下一个词的可能性更多样。对于完全随机的文本,困惑度会非常高(可能超过1000),因为模型无法做出准确的预测。
困惑度的数学表达式为:
PPL(W) = exp(-1/N * ΣlogP(w_i | w_1, w_2, ..., w_{i-1}))
其中:
假设有一个简单文本:"今天 天气 很好",计算过程如下:
第一步. 计算每个位置的条件概率:
第二步. 计算对数概率和:
log(0.4) + log(0.6) + log(0.5) ≈ -0.92 -0.51 -0.69 = -2.12
第三步. 计算平均负对数概率:
-(-2.12)/3 ≈ 0.71
第四步. 计算困惑度:
exp(0.71) ≈ 2.03
这个较低的困惑度值表明模型能够很好地预测这个简单文本。
+-------------------+ +-------------------+ +-------------------+ +-------------------+
| 输入文本序列 | | 计算每个位置 | | 计算平均负 | | 取指数得 |
| "今天天气很好" | --> | 条件概率 | --> | 对数概率 | --> | 困惑度值 |
| | | P(w_i|w_<i) | | -1/N Σ log P(...) | | exp(平均值) |
+-------------------+ +-------------------+ +-------------------+ +-------------------+
| | | |
v v v v
+-------------------+ +-------------------+ +-------------------+ +-------------------+
| 分词: | | P("今天"|开始)=0.4 | | -1/3 [log(0.4) + | | PPL = exp(0.706) |
| ["今天", "天气", | | P("天气"|"今天")=0.6| | log(0.6) + | | = 2.027 |
| "很好"] | | P("很好"|"今天 天气")=0.5| | log(0.5) + | | |
| N=3 | | | | = -1/3 [-0.9163 + | | |
+-------------------+ +-------------------+ | -0.5108 + -0.6931] | +-------------------+
| = -1/3 × -2.1202 |
| = 0.7067 |
+-------------------+1. 作为"有效分支因子"的解释
困惑度可以理解为模型在每个词元位置面临的"平均选择困难度"。例如:
2. 与概率的关系
困惑度与概率呈反比关系:
3. 不同文本类型的典型困惑度范围
文本类型 | 典型困惑度范围 | 说明 |
|---|---|---|
简单常见文本 | 5-30 | 日常对话、简单句子 |
一般专业文本 | 30-100 | 新闻文章、技术文档 |
复杂专业文本 | 100-300 | 学术论文、专业术语多的文本 |
随机/无意义文本 | 300+ | 随机字符、无意义组合 |
使用Qwen API模型模拟验证以上示例中的困惑度计算过程:
import requests
import json
import math
import os
class QwenPerplexityCalculator:
def __init__(self, api_key):
self.api_key = api_key
self.api_url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
def calculate_perplexity_process(self, text, probabilities):
"""
使用Qwen API模拟困惑度计算过程
参数:
text: 要计算困惑度的文本
probabilities: 各位置的条件概率值列表
"""
# 构建详细的prompt,要求模型按照指定流程计算困惑度
prompt = f"""请按照以下步骤计算文本的困惑度(PPL),使用我提供的概率值:
文本: "{text}"
条件概率值:
P("今天" | <开始>) = {probabilities[0]}
P("天气" | "今天") = {probabilities[1]}
P("很好" | "今天 天气") = {probabilities[2]}
请逐步计算并解释:
1. 计算对数概率和:log({probabilities[0]}) + log({probabilities[1]}) + log({probabilities[2]})
2. 计算平均负对数概率:- (对数概率和) / N (N=3)
3. 计算困惑度:exp(平均负对数概率)
请详细展示每一步的计算过程和结果,最后给出完整的困惑度值。"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
payload = {
"model": "qwen-max",
"input": {
"messages": [{"role": "user", "content": prompt}]
},
"parameters": {
"result_format": "text",
"max_tokens": 800,
"temperature": 0.1
}
}
try:
response = requests.post(self.api_url, headers=headers, data=json.dumps(payload))
result = response.json()
if "output" in result and "text" in result["output"]:
return result["output"]["text"].strip()
return None
except Exception as e:
print(f"API调用失败: {e}")
return None
def manual_calculation(self, text, probabilities):
"""
手动计算困惑度过程,用于验证
"""
print(f"文本: '{text}'")
print("\n1. 计算每个位置的条件概率:")
for i, prob in enumerate(probabilities):
if i == 0:
print(f" P('{text.split()[i]}' | <开始>) = {prob}")
else:
context = " ".join(text.split()[:i])
print(f" P('{text.split()[i]}' | '{context}') = {prob}")
print("\n2. 计算对数概率和:")
log_probs = [math.log(p) for p in probabilities]
log_sum = sum(log_probs)
log_str = " + ".join([f"log({p})" for p in probabilities])
print(f" {log_str} = {log_str.replace('log', str(round(math.log(probabilities[0]), 2))).replace('log', str(round(math.log(probabilities[1]), 2))).replace('log', str(round(math.log(probabilities[2]), 2)))} = {round(log_sum, 2)}")
print("\n3. 计算平均负对数概率:")
n = len(probabilities)
avg_neg_log_prob = -log_sum / n
print(f" -({round(log_sum, 2)}) / {n} = {round(avg_neg_log_prob, 2)}")
print("\n4. 计算困惑度:")
perplexity = math.exp(avg_neg_log_prob)
print(f" exp({round(avg_neg_log_prob, 2)}) = {round(perplexity, 2)}")
return perplexity
# 使用示例
if __name__ == "__main__":
# 初始化计算器
calculator = QwenPerplexityCalculator(os.environ.get("DASHSCOPE_API_KEY"))
# 定义文本和概率值
text = "今天 天气 很好"
probabilities = [0.4, 0.6, 0.5]
print("使用Qwen API模拟困惑度计算过程:")
print("=" * 50)
# 使用Qwen API获取计算过程
process = calculator.calculate_perplexity_process(text, probabilities)
if process:
print(process)
else:
print("API调用失败,使用手动计算")
print("\n" + "=" * 50)
print("手动计算验证:")
print("=" * 50)
# 手动计算验证
final_ppl = calculator.manual_calculation(text, probabilities)
print(f"\n最终困惑度: {final_ppl:.2f}")输出计算过程:
使用Qwen API模拟困惑度计算过程:
================================================== 要计算给定文本 "今天 天气 很好" 的困惑度(PPL),我们将按照您提供的步骤进行。首先,我们需要明确几个概念:对数通常指的是自然对数(以e为底 ),在计算困惑度时我们使用的是这个定义;PPL是衡量语言模型预测下一个词的好坏的一个指标,值越小表示模型越好。
第一步: 计算对数概率和
根据题目给出的概率值,我们可以直接代入公式来计算对数概率之和。 - (P("今天" | <开始>) = 0.4) - (P("天气" | "今天") = 0.6) - (P("很好" | "今天 天气") = 0.5)
因此,对数概率和为: log(0.4) + log(0.6) + log(0.5)
使用自然对数(ln)计算每个项: - (log(0.4) ≈ -0.9163 - (log(0.6) ≈ -0.5108 - (log(0.5) = -0.6931
将这些值相加得到总和: -0.9163 - 0.5108 - 0.6931 = -2.1202
第二步: 计算平均负对数概率
接下来,我们需要计算平均负对数概率。这里N=3,因为句子中有三个词。 - (对数概率和/N) = -(-2.1202)/3 = 0.7067
第三步: 计算困惑度
最后一步是通过指数函数e^x来计算困惑度。 PPL = e^{0.7067} ≈ 2.028
结论 给定文本 "今天 天气 很好" 的困惑度大约为2.028。这意味着,在理想情况下,如果我们的模型能够完美地预测每一个词,那么对于这段文本来说,它平 均每猜测一个词需要考虑约2个选项。较低的PPL值表明模型具有更好的性能。
================================================== 手动计算验证: ================================================== 文本: '今天 天气 很好'
1. 计算每个位置的条件概率: P('今天' | <开始>) = 0.4 P('天气' | '今天') = 0.6 P('很好' | '今天 天气') = 0.5
2. 计算对数概率和: log(0.4) + log(0.6) + log(0.5) = -0.92(0.4) + -0.92(0.6) + -0.92(0.5) = -2.12
3. 计算平均负对数概率: -(-2.12) / 3 = 0.71
4. 计算困惑度: exp(0.71) = 2.03
最终困惑度: 2.03
结果解释:
得到的困惑度值约为2.03,这是一个相对较低的值,表明模型能够很好地预测这个文本序列。具体来说:
在这个例子中,较低的困惑度值反映了文本"今天 天气 很好"是一个常见且简单的序列,模型能够轻松预测。
在实际应用中,困惑度计算有几点需要注意:
这种方法虽然不能精确计算模型内部的概率,但可以很好地展示困惑度的计算原理和过程。
实施内在评测通常需要以下几个步骤:

虽然内在评测提供了模型基础能力的重要信息,但它也有局限性。最重要的局限是:低困惑度并不总是意味着好的实际表现。模型可能会过拟合训练数据,在测试文本上表现出色,但在实际应用中却表现不佳。因此,内在评测应该与其他评测方法结合使用。
这个示例展示了如何使用Qwen API估算文本的困惑度 并分步输出分析评估估算的具体细节。首先要设计好合适的prompt,让Qwen模型扮演语言模型专家的角色,直接返回困惑度数值。这种方法虽然不如直接计算精确,但提供了快速的估算方案,比较适用于初步评估和原型开发。
import requests
import json
import re
import os
class QwenIntrinsicEvaluator:
def __init__(self, api_key):
self.api_key = api_key
self.api_url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
def estimate_perplexity_with_details(self, text):
"""
估算文本困惑度并返回详细计算过程
参数:
text: 要计算困惑度的文本
返回:
包含困惑度值和计算细节的字典
"""
# 构建详细的prompt,要求模型解释计算过程
prompt = f"""作为语言模型专家,请分析以下文本的语言建模难度,并估算其困惑度(PPL)值。
请详细解释你的计算过程,包括以下内容:
1. 文本的语言复杂性分析
2. 词汇的常见程度评估
3. 句法结构的复杂性
4. 最终估算的困惑度值及其解释
文本: "{text}"
请按以下格式回复:
【文本分析】
[对文本复杂性的分析]
【词汇评估】
[对词汇常见程度的评估]
【句法分析】
[对句法复杂性的分析]
【困惑度估算】
估算值: [数值]
解释: [对估算值的解释]
"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
payload = {
"model": "qwen-max",
"input": {
"messages": [{"role": "user", "content": prompt}]
},
"parameters": {
"result_format": "text",
"max_tokens": 800,
"temperature": 0.1
}
}
try:
response = requests.post(self.api_url, headers=headers, data=json.dumps(payload))
result = response.json()
if "output" in result and "text" in result["output"]:
response_text = result["output"]["text"].strip()
# 解析响应内容
details = self._parse_response_details(response_text)
# 提取困惑度值
match = re.search(r"估算值:\s*([\d\.]+)", response_text)
if match:
details['perplexity'] = float(match.group(1))
else:
# 备用方法:尝试从文本中提取任何数字
num_match = re.search(r"(\d+\.\d+|\d+)", response_text)
if num_match:
details['perplexity'] = float(num_match.group())
else:
details['perplexity'] = None
details['full_response'] = response_text
return details
return None
except Exception as e:
print(f"API调用失败: {e}")
return None
def _parse_response_details(self, response_text):
"""解析模型响应中的详细信息"""
details = {
'text_analysis': '',
'vocabulary_assessment': '',
'syntax_analysis': '',
'explanation': ''
}
# 使用正则表达式提取各个部分
text_analysis_match = re.search(r"【文本分析】\s*(.*?)(?=【|$)", response_text, re.DOTALL)
vocab_match = re.search(r"【词汇评估】\s*(.*?)(?=【|$)", response_text, re.DOTALL)
syntax_match = re.search(r"【句法分析】\s*(.*?)(?=【|$)", response_text, re.DOTALL)
explanation_match = re.search(r"解释:\s*(.*?)(?=【|$)", response_text, re.DOTALL)
if text_analysis_match:
details['text_analysis'] = text_analysis_match.group(1).strip()
if vocab_match:
details['vocabulary_assessment'] = vocab_match.group(1).strip()
if syntax_match:
details['syntax_analysis'] = syntax_match.group(1).strip()
if explanation_match:
details['explanation'] = explanation_match.group(1).strip()
return details
# 使用示例
if __name__ == "__main__":
evaluator = QwenIntrinsicEvaluator(os.environ.get("DASHSCOPE_API_KEY"))
text = "语言模型的评测是一个重要的研究领域"
# 获取详细的困惑度分析
result = evaluator.estimate_perplexity_with_details(text)
if result and 'perplexity' in result:
print(f"文本: '{text}'")
print(f"估算困惑度值: {result['perplexity']}")
print("\n=== 详细分析 ===")
print(f"文本分析: {result.get('text_analysis', '无')}")
print(f"词汇评估: {result.get('vocabulary_assessment', '无')}")
print(f"句法分析: {result.get('syntax_analysis', '无')}")
print(f"解释: {result.get('explanation', '无')}")
print("\n=== 完整响应 ===")
print(result.get('full_response', '无'))
else:
print("无法计算困惑度")文本: '语言模型的评测是一个重要的研究领域'
估算困惑度值: 1.5
=== 详细分析 ===
文本分析: 该句"语言模型的评测是一个重要的研究领域"属于中文,句子结构相对简单直接,没有使用复杂的修辞手法或生僻词汇。从语义上看,这句话
表达了一个明确的观点,即“语言模型的评测”在学术界占据着重要地位。整体而言,对于具有一定中文基础的人来说理解起来并不困难。
词汇评估: - “语言模型”:这是一个专业术语,在自然语言处理(NLP)领域内较为常见。
- “评测”:普通词汇,但在此处特指对某种技术或方法进行评价和测试的过程。
- “是”、“一个”、“的”等词为常用连接词或助词。
- “重要”、“研究”、“领域”均为日常交流中频繁出现的词语。
综上所述,除了“语言模型”可能需要特定背景知识才能完全理解外,其他词汇都比较普遍易懂。
句法分析: 本句采用了典型的主谓宾结构:“[语言模型的评测] [是] [一个重要的研究领域]”。其中,“语言模型的评测”作为整个句子的主题;“是”起到
连接作用;而“一个重要的研究领域”则构成了句子的核心信息部分。这种构造方式符合汉语的基本语法规范,逻辑清晰,易于解析。
解释: 困惑度(Perplexity, PPL)是用来衡量语言模型预测下一个单词能力的一个指标,数值越低表示模型对该文本的理解越好。考虑到上述分析结果——即文本内容简洁明了、大部分词汇通俗易懂且句式结构简单——可以推测出对于训练良好的现代中文语言模型来说,这段话应该很容易被准确地理解和生成
。因此,我估计其PPL值大约位于1.5到2.0之间,表明模型能够以较高概率正确预测出每个词。当然,实际数值还需通过具体实验来确定。【文本分析】 该句"语言模型的评测是一个重要的研究领域"属于中文,句子结构相对简单直接,没有使用复杂的修辞手法或生僻词汇。从语义上看,这句话表达了一个 明确的观点,即“语言模型的评测”在学术界占据着重要地位。整体而言,对于具有一定中文基础的人来说理解起来并不困难。
【词汇评估】 - “语言模型”:这是一个专业术语,在自然语言处理(NLP)领域内较为常见。 - “评测”:普通词汇,但在此处特指对某种技术或方法进行评价和测试的过程。 - “是”、“一个”、“的”等词为常用连接词或助词。 - “重要”、“研究”、“领域”均为日常交流中频繁出现的词语。 综上所述,除了“语言模型”可能需要特定背景知识才能完全理解外,其他词汇都比较普遍易懂。
【句法分析】 本句采用了典型的主谓宾结构:“[语言模型的评测] [是] [一个重要的研究领域]”。其中,“语言模型的评测”作为整个句子的主题;“是”起到连接作用; 而“一个重要的研究领域”则构成了句子的核心信息部分。这种构造方式符合汉语的基本语法规范,逻辑清晰,易于解析。
【困惑度估算】 估算值: 1.5 - 2.0 解释: 困惑度(Perplexity, PPL)是用来衡量语言模型预测下一个单词能力的一个指标,数值越低表示模型对该文本的理解越好。考虑到上述分析结果——即文本内容简洁明了、大部分词汇通俗易懂且句式结构简单——可以推测出对于训练良好的现代中文语言模型来说,这段话应该很容易被准确地理解和生成 。因此,我估计其PPL值大约位于1.5到2.0之间,表明模型能够以较高概率正确预测出每个词。当然,实际数值还需通过具体实验来确定。
1. 代码使用正则表达式解析模型响应,提取以下信息:
2. 需要明确的是,通过API获取的困惑度值不是精确计算的结果,而是基于以下因素的估算:
核心思想: 将模型嵌入到一个具体的下游任务中,通过它在任务中的最终表现来评价它。这类似于学生的“毕业设计”或“入职实战”。
任务类型:任何具体的NLP任务,例如:
评价指标:
优点: 直接反映模型的实用价值,结果易于理解。
缺点: 评测成本高(需要构建测试数据集和流水线),结果受任务设计影响大。
流程图:

import requests
import json
import numpy as np
import pandas as pd
from sentence_transformers import SentenceTransformer
import faiss
import re
from typing import List, Dict, Any
import time
import os
class QwenAccuracyEvaluator:
def __init__(self, api_key: str, knowledge_base: List[str] = None):
"""
基于Qwen API的外在评测准确率计算系统
参数:
api_key: Qwen API密钥
knowledge_base: 知识库文本列表(可选,用于检索增强)
"""
self.api_key = api_key
self.api_url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
# 如果有知识库,构建Faiss索引
if knowledge_base:
self.knowledge_base = knowledge_base
self.embedder = SentenceTransformer('D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2')
self._build_faiss_index()
self.use_rag = True
else:
self.use_rag = False
# 测试数据集 - 问答对
self.qa_test_set = [
{
"id": 1,
"question": "Python中如何创建一个空列表?",
"expected_answer": "可以使用方括号 [] 或 list() 函数创建空列表",
"acceptable_variations": [
"使用 [] 创建空列表",
"用 list() 创建空列表",
"空列表可以通过 [] 或 list() 创建"
]
},
{
"id": 2,
"question": "什么是机器学习?",
"expected_answer": "机器学习是人工智能的一个子领域,使计算机能够从数据中学习而不需要明确编程",
"acceptable_variations": [
"机器学习是AI的一个分支,让计算机从数据中学习",
"ML是计算机通过数据自动学习和改进的技术"
]
},
{
"id": 3,
"question": "如何在Python中打印'Hello, World!'?",
"expected_answer": "使用 print('Hello, World!')",
"acceptable_variations": [
"print('Hello, World!')",
"使用print函数打印Hello, World!"
]
}
]
def _build_faiss_index(self):
"""构建Faiss向量索引(如果使用知识库)"""
if not hasattr(self, 'knowledge_base'):
return
# 生成知识库文本的向量
knowledge_vectors = self.embedder.encode(self.knowledge_base)
self.dimension = knowledge_vectors.shape[1]
# 创建Faiss索引
self.index = faiss.IndexFlatL2(self.dimension)
self.index.add(knowledge_vectors.astype('float32'))
print(f"知识库索引构建完成,包含 {len(self.knowledge_base)} 条知识")
def _retrieve_relevant_texts(self, query: str, top_k: int = 3) -> List[str]:
"""检索相关文本(如果使用知识库)"""
if not hasattr(self, 'index'):
return []
# 生成查询向量
query_vector = self.embedder.encode([query])
# 检索相似文本
D, I = self.index.search(query_vector.astype('float32'), top_k)
retrieved_texts = []
for i in range(top_k):
if I[0][i] < len(self.knowledge_base):
retrieved_texts.append(self.knowledge_base[I[0][i]])
return retrieved_texts
def call_qwen_api(self, prompt: str, max_tokens: int = 500) -> str:
"""调用Qwen API生成文本"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
payload = {
"model": "qwen-max", # 可以根据需要选择不同模型
"input": {
"messages": [
{
"role": "user",
"content": prompt
}
]
},
"parameters": {
"result_format": "text",
"max_tokens": max_tokens,
"temperature": 0.3 # 降低随机性,提高确定性
}
}
try:
response = requests.post(self.api_url, headers=headers, data=json.dumps(payload))
response.raise_for_status()
result = response.json()
if "output" in result and "text" in result["output"]:
return result["output"]["text"].strip()
return None
except Exception as e:
print(f"API调用失败: {e}")
return None
def generate_answer(self, question: str) -> str:
"""生成答案"""
if self.use_rag:
# 使用检索增强生成(RAG)
relevant_texts = self._retrieve_relevant_texts(question)
context = "\n".join(relevant_texts)
prompt = f"""基于以下上下文信息回答问题。如果信息不足,请明确说明。
上下文:
{context}
问题:
{question}
请提供准确、简洁的答案:"""
else:
# 直接回答问题
prompt = f"""请回答以下问题。提供准确、完整的回答。
问题:{question}
回答:"""
return self.call_qwen_api(prompt)
def evaluate_accuracy(self, generated_answer: str, expected_answer: str,
acceptable_variations: List[str] = None) -> Dict[str, Any]:
"""
评估生成答案的准确率
参数:
generated_answer: 模型生成的答案
expected_answer: 期望的标准答案
acceptable_variations: 可接受的答案变体列表
返回:
包含准确率评估结果的字典
"""
if not generated_answer:
return {
"is_correct": False,
"confidence": 0.0,
"match_type": "no_answer",
"explanation": "模型未生成答案"
}
# 标准化答案(小写、去除标点)
def normalize_text(text):
text = text.lower()
text = re.sub(r'[^\w\s]', '', text) # 移除标点符号
return text.strip()
gen_norm = normalize_text(generated_answer)
exp_norm = normalize_text(expected_answer)
# 1. 精确匹配检查
if gen_norm == exp_norm:
return {
"is_correct": True,
"confidence": 1.0,
"match_type": "exact",
"explanation": "生成答案与标准答案完全匹配"
}
# 2. 检查可接受的变体
if acceptable_variations:
for variation in acceptable_variations:
if gen_norm == normalize_text(variation):
return {
"is_correct": True,
"confidence": 0.9,
"match_type": "acceptable_variation",
"explanation": "生成答案与可接受变体匹配"
}
# 3. 关键词匹配检查
expected_keywords = set(exp_norm.split())
generated_keywords = set(gen_norm.split())
common_keywords = expected_keywords & generated_keywords
if len(common_keywords) >= len(expected_keywords) * 0.7: # 70%关键词匹配
keyword_coverage = len(common_keywords) / len(expected_keywords)
return {
"is_correct": True,
"confidence": keyword_coverage * 0.8, # 最高0.8置信度
"match_type": "keyword_based",
"explanation": f"关键词匹配度: {keyword_coverage:.2f}"
}
# 4. 使用Qwen API进行语义评估
evaluation_prompt = f"""请判断以下两个答案是否在语义上等价:
答案1: {expected_answer}
答案2: {generated_answer}
请只回复"是"或"否",不要包含其他内容。"""
semantic_eval = self.call_qwen_api(evaluation_prompt, max_tokens=10)
if semantic_eval and "是" in semantic_eval:
return {
"is_correct": True,
"confidence": 0.7,
"match_type": "semantic",
"explanation": "语义评估认为答案等价"
}
# 5. 所有检查都失败,答案不正确
return {
"is_correct": False,
"confidence": 0.0,
"match_type": "incorrect",
"explanation": "答案不正确"
}
def run_accuracy_evaluation(self) -> List[Dict[str, Any]]:
"""运行准确率评测"""
results = []
correct_count = 0
for test_case in self.qa_test_set:
print(f"\n处理测试用例 {test_case['id']}: {test_case['question']}")
# 生成答案
generated_answer = self.generate_answer(test_case['question'])
if not generated_answer:
print("生成答案失败")
results.append({
"test_case_id": test_case["id"],
"question": test_case["question"],
"generated_answer": None,
"expected_answer": test_case["expected_answer"],
"is_correct": False,
"confidence": 0.0,
"match_type": "no_answer"
})
continue
print(f"生成答案: {generated_answer}")
# 评估准确率
accuracy_result = self.evaluate_accuracy(
generated_answer,
test_case["expected_answer"],
test_case.get("acceptable_variations", [])
)
# 更新正确计数
if accuracy_result["is_correct"]:
correct_count += 1
# 保存结果
result = {
"test_case_id": test_case["id"],
"question": test_case["question"],
"generated_answer": generated_answer,
"expected_answer": test_case["expected_answer"],
"is_correct": accuracy_result["is_correct"],
"confidence": accuracy_result["confidence"],
"match_type": accuracy_result["match_type"],
"explanation": accuracy_result["explanation"]
}
results.append(result)
# 打印当前结果
status = "正确" if accuracy_result["is_correct"] else "错误"
print(f"评估结果: {status} (置信度: {accuracy_result['confidence']:.2f})")
print(f"匹配类型: {accuracy_result['match_type']}")
# 添加延迟避免API限制
time.sleep(1)
# 计算总体准确率
total_cases = len(results)
accuracy = correct_count / total_cases if total_cases > 0 else 0
return results, accuracy
def generate_accuracy_report(self, results: List[Dict[str, Any]], accuracy: float) -> str:
"""生成准确率评测报告"""
if not results:
return "无评测结果"
# 创建报告
report_lines = [
"Qwen API外在评测准确率报告",
"=" * 60,
f"评测时间: {time.strftime('%Y-%m-%d %H:%M:%S')}",
f"测试用例数量: {len(results)}",
f"正确回答数量: {sum(1 for r in results if r['is_correct'])}",
f"总体准确率: {accuracy:.2%}",
"\n详细结果:",
"=" * 60
]
for result in results:
status = "✓" if result["is_correct"] else "✗"
report_lines.extend([
f"\n{status} 用例 {result['test_case_id']}:",
f"问题: {result['question']}",
f"生成答案: {result['generated_answer']}",
f"期望答案: {result['expected_answer']}",
f"匹配类型: {result['match_type']}",
f"置信度: {result['confidence']:.2f}",
f"解释: {result['explanation']}"
])
# 添加匹配类型统计
match_types = {}
for result in results:
match_type = result["match_type"]
match_types[match_type] = match_types.get(match_type, 0) + 1
report_lines.extend([
"\n匹配类型统计:",
"=" * 60
])
for match_type, count in match_types.items():
report_lines.append(f"{match_type}: {count}次")
return "\n".join(report_lines)
# 使用示例
if __name__ == "__main__":
# 配置参数
API_KEY = os.environ.get("DASHSCOPE_API_KEY")
# 可选的知识库数据
knowledge_base = [
"Python中可以使用方括号[]或list()函数创建空列表",
"机器学习是人工智能的一个子领域,使计算机能够从数据中学习",
"在Python中使用print()函数可以输出文本到控制台"
]
# 初始化评测器(可以选择是否使用知识库)
evaluator = QwenAccuracyEvaluator(API_KEY, knowledge_base)
# 运行准确率评测
print("开始准确率评测...")
results, accuracy = evaluator.run_accuracy_evaluation()
# 生成报告
if results:
report = evaluator.generate_accuracy_report(results, accuracy)
print(report)
# 保存报告到文件
with open('qwen_accuracy_report.txt', 'w', encoding='utf-8') as f:
f.write(report)
print("\n准确率报告已保存至 qwen_accuracy_report.txt")
else:
print("评测失败,无结果生成")知识库索引构建完成,包含 3 条知识
开始准确率评测...
处理测试用例 1: Python中如何创建一个空列表?
生成答案: 在Python中,可以通过以下两种方式创建一个空列表:
1. 使用方括号 `[]`,例如:`my_list = []`
2. 使用 `list()` 函数,例如:`my_list = list()`
评估结果: 正确 (置信度: 0.70)
匹配类型: semantic
处理测试用例 2: 什么是机器学习?
生成答案: 机器学习是人工智能的一个子领域,它使计算机能够通过从数据中学习来改进其性能和决策能力,而无需进行明确的编
程。
评估结果: 正确 (置信度: 0.70)
匹配类型: semantic
处理测试用例 3: 如何在Python中打印'Hello, World!'?
生成答案: 在Python中打印'Hello, World!',你可以使用以下代码:
```python
print('Hello, World!')
```
评估结果: 正确 (置信度: 0.70)
匹配类型: semantic
Qwen API外在评测准确率报告
============================================================
评测时间: 2025-09-08 19:56:45
测试用例数量: 3
正确回答数量: 3
总体准确率: 100.00%
详细结果:
============================================================
✓ 用例 1:
问题: Python中如何创建一个空列表?
生成答案: 在Python中,可以通过以下两种方式创建一个空列表:
1. 使用方括号 `[]`,例如:`my_list = []`
2. 使用 `list()` 函数,例如:`my_list = list()`
期望答案: 可以使用方括号 [] 或 list() 函数创建空列表
匹配类型: semantic
置信度: 0.70
解释: 语义评估认为答案等价
✓ 用例 2:
问题: 什么是机器学习?
生成答案: 机器学习是人工智能的一个子领域,它使计算机能够通过从数据中学习来改进其性能和决策能力,而无需进行明确的编
程。
期望答案: 机器学习是人工智能的一个子领域,使计算机能够从数据中学习而不需要明确编程
匹配类型: semantic
置信度: 0.70
解释: 语义评估认为答案等价
✓ 用例 3:
问题: 如何在Python中打印'Hello, World!'?
生成答案: 在Python中打印'Hello, World!',你可以使用以下代码:
```python
print('Hello, World!')
```
期望答案: 使用 print('Hello, World!')
匹配类型: semantic
置信度: 0.70
解释: 语义评估认为答案等价
匹配类型统计:
============================================================
semantic: 3次
准确率报告已保存至 qwen_accuracy_report.txt这个准确率评测系统包含以下核心组件:
系统采用多层次的准确率评估策略:
第一层:精确匹配
if gen_norm == exp_norm:
return {
"is_correct": True,
"confidence": 1.0,
"match_type": "exact",
"explanation": "生成答案与标准答案完全匹配"
}第二层:可接受变体匹配
for variation in acceptable_variations:
if gen_norm == normalize_text(variation):
return {
"is_correct": True,
"confidence": 0.9,
"match_type": "acceptable_variation",
"explanation": "生成答案与可接受变体匹配"
}第三层:关键词匹配
if len(common_keywords) >= len(expected_keywords) * 0.7:
keyword_coverage = len(common_keywords) / len(expected_keywords)
return {
"is_correct": True,
"confidence": keyword_coverage * 0.8,
"match_type": "keyword_based",
"explanation": f"关键词匹配度: {keyword_coverage:.2f}"
}第四层:语义评估
evaluation_prompt = f"""请判断以下两个答案是否在语义上等价:
答案1: {expected_answer}
答案2: {generated_answer}
请只回复"是"或"否",不要包含其他内容。"""
semantic_eval = self.call_qwen_api(evaluation_prompt, max_tokens=10)准确率的计算基于以下公式:
准确率 = 正确回答数量 / 总测试用例数量
其中,正确回答的判断标准是上述多层级评估中的任何一层返回is_correct: True。
维度 | 内在评测 | 外在评测 |
|---|---|---|
定义 | 评估模型基础语言能力 | 评估模型在具体任务中的表现 |
关注点 | 模型的语言建模基本功 | 模型的实际应用效果 |
评测环境 | 孤立、受控的实验室环境 | 真实或模拟的应用场景 |
主要指标 | 困惑度(PPL)、交叉熵损失 | 任务相关指标(准确率、F1值等) |
复杂度 | 相对简单、标准化 | 复杂、需要构建任务管道 |
成本 | 计算成本低 | 需要标注数据、人工评估 |
一个完整的评测流程通常包括以下步骤:
在选择模型时,不要只看宣传参数,更要关注它在与你任务相关的基准测试和外在实际任务中的表现,内在评测看基本功,外在评测看实战能力,两者相辅相成,结合使用才能全面评估一个模型。最重要的是,语言模型评估应该是一个持续的过程,而不是一次性的活动。而应该是持续的过程。这包括:定期重新评估以跟踪模型性能的变化;监控生产环境中的模型表现;建立反馈机制收集用户评价;持续更新测试数据集以反映新的使用场景和需求。
随着模型的发展和应用的扩展,评估也需要不断演进和完善。特别是在模型更新或微调后,需要进行全面的重新评估,确保性能提升不会带来新的问题。同时,也需要关注模型在不同用户群体中的表现差异,确保公平性和包容性。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。