文章目录
-
- datasets
-
-
- datasets和自建考题哪个好?
- 常见的数据集有哪些?
-
- [数据集-1. 数学与逻辑推理类 (你的主战场)](#数据集-1. 数学与逻辑推理类 (你的主战场))
- [数据集-2. 综合知识与学术能力类 (全能学霸)](#数据集-2. 综合知识与学术能力类 (全能学霸))
- [数据集-3. 编程与代码能力类 (程序员助手)](#数据集-3. 编程与代码能力类 (程序员助手))
- [数据集-4. 语言理解与指令遵循类 (听话程度)](#数据集-4. 语言理解与指令遵循类 (听话程度))
- self-refine和self-consistence的区别?
-
又一课题,必须要有套路,以及能举例。
思路理解:
1、定义一个测评类ModelEvaluator。
2、在该配里面配置基座模型、分词器、量化配置、run_dataset()方法是自定义的评测方法
3、运行时,传入数据集名称gsm8k,任务类型math,表示测评这个维度的这类题目
原生模型-示例代码
python
import torch
import re
from tqdm import tqdm
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from peft import PeftModel
class ModelEvaluator:
def __init__(self, model_path, adapter_path=None, use_4bit=True):
self.device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"🚀 正在加载模型: {model_path} ...")
# 1. 量化配置
bnb_config = None
if use_4bit and self.device == "cuda":
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4"
)
# 2. 加载 Tokenizer
self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
# 【关键修复 1】设置 pad_token,防止 Attention Mask 报错
self.tokenizer.pad_token = self.tokenizer.eos_token
self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
# 3. 加载基座模型
self.base_model = AutoModelForCausalLM.from_pretrained(
model_path,
quantization_config=bnb_config,
device_map="auto",
torch_dtype=torch.float16,
trust_remote_code=True,
# 【关键修复 2】显式传入 pad_token_id,彻底解决 mask 问题
pad_token_id=self.tokenizer.pad_token_id
)
# 4. 加载 LoRA 适配器
if adapter_path:
print(f"🔧 正在加载 LoRA 适配器: {adapter_path} ...")
self.model = PeftModel.from_pretrained(self.base_model, adapter_path)
else:
self.model = self.base_model
self.model.eval()
def generate(self, prompt: str, max_new_tokens=512) -> str:
# 【关键修复 3】消除警告:强制覆盖模型配置中的采样参数
self.model.generation_config.do_sample = False
self.model.generation_config.top_p = None
self.model.generation_config.top_k = 0
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
with torch.no_grad():
generate_ids = self.model.generate(
inputs.input_ids,
max_new_tokens=max_new_tokens,
do_sample=False,
pad_token_id=self.tokenizer.eos_token_id
)
# 解码并去除输入部分
output = self.tokenizer.batch_decode(generate_ids, skip_special_tokens=True)[0]
return output.replace(prompt, "").strip()
def run_dataset(self, dataset_name, task_type="math"):
print(f"📚 正在加载数据集: {dataset_name} ...")
# 加载数据集,指定 'main' 配置
dataset = load_dataset(dataset_name, 'main', split="test")
results = []
score = 0
# 限制数量用于测试,实际评测可去掉 .select(range(10))
for item in tqdm(dataset.select(range(10)), desc="评测中"):
# 构造 Prompt:强制要求模型输出特定格式
prompt = f"请回答下面的数学问题,并在最后一行输出"答案是 [数字]"。\n\n问题:{item['question']}\n"
# 推理
raw_output = self.generate(prompt)
# 阅卷
is_correct = False
ground_truth = item['answer'] # GSM8K 的正确答案在 'answer' 字段
if task_type == "math":
# 提取模型回答中的数字
pred = self.parse_math_answer(raw_output)
# 提取标准答案中的数字
label = self.parse_math_answer(ground_truth)
# 比对数字
if pred and label and pred == label:
is_correct = True
results.append({
"question": item['question'],
"prediction": raw_output,
"ground_truth": ground_truth,
"correct": is_correct
})
if is_correct:
score += 1
# 统计结果
accuracy = score / len(results)
print(f"✅ 评测完成。准确率: {accuracy:.2%}")
return results, accuracy
def parse_math_answer(self, text):
# 针对数学题的正则:提取 "答案是 " 后面的数字
# 支持整数和小数
match = re.search(r"答案是\s*([-+]?\d+\.?\d*)", text)
if match:
return match.group(1)
# 兜底策略:尝试提取最后一个出现的数字
numbers = re.findall(r"[-+]?\d+\.?\d*", text)
if numbers:
return numbers[-1]
return None
# --- 使用示例 ---
if __name__ == "__main__":
# 配置模型路径
MODEL_PATH = "Qwen/Qwen2.5-0.5B-Instruct"
ADAPTER_PATH = None
# 初始化评测器
evaluator = ModelEvaluator(MODEL_PATH, adapter_path=ADAPTER_PATH, use_4bit=True)
# 运行评测
try:
results, acc = evaluator.run_dataset("gsm8k", task_type="math")
except Exception as e:
print(f"评测出错: {e}")
import traceback
traceback.print_exc()
输出结果:
python
🚀 正在加载模型: Qwen/Qwen2.5-0.5B-Instruct ...
📚 正在加载数据集: gsm8k ...
评测中: 0%| | 0/10 [00:00<?, ?it/s]D:\PycharmProjects\transformer_demo\.venv\Lib\site-packages\transformers\generation\configuration_utils.py:590: UserWarning: `do_sample` is set to `False`. However, `temperature` is set to `0.7` -- this flag is only used in sample-based generation modes. You should set `do_sample=True` or unset `temperature`.
warnings.warn(
D:\PycharmProjects\transformer_demo\.venv\Lib\site-packages\transformers\generation\configuration_utils.py:612: UserWarning: `do_sample` is set to `False`. However, `top_k` is set to `0` -- this flag is only used in sample-based generation modes. You should set `do_sample=True` or unset `top_k`.
warnings.warn(
The attention mask is not set and cannot be inferred from input because pad token is same as eos token. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.
评测中: 100%|██████████| 10/10 [04:16<00:00, 25.65s/it]
✅ 评测完成。准确率: 0.00%
解读:
准确率是0.00%,这大概率和模型是千问0.5b有关,先不管它了,这里主要看思路。
LmEval-示例代码
LmEval是一款评测框架,之前原生为了说明机制。如果要快速上手,用框架肯定高效方便。
1、安装依赖
python
pip install lm_eval # 注:是lm_eval,不是lmeval,中间有中划线
2、新建python文件lmEval.py,代码:
python
import lm_eval
import torch
import os
def run_lm_eval_ultimate():
task_name = "boolq"
# 【双重保险 1/2】设置环境变量加速下载
# 这是解决 ReadTimeout 的关键
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
# 【双重保险 2/2】关于 dtype 的警告处理
# 既然 lm_eval 频繁报 dtype 错误,我们直接不传 dtype
# 并且忽略 HalfTensor 的弃用警告,改用 set_default_dtype (如果环境支持)
try:
torch.set_default_dtype(torch.float16)
print("已设置默认精度为 float16 (兼容新版本 PyTorch)")
except:
# 如果 set_default_dtype 失败(比如版本太低),尝试旧方法或直接跳过
print("当前 PyTorch 版本可能较低,依赖模型自动处理精度...")
model_args = {
"pretrained": "Qwen/Qwen2.5-0.5B",
# 🔥 核心策略:绝对不要写 "dtype": ...
# 让 HuggingFace 模型加载器自己决定用什么精度
# lm_eval 有时候会强制注入参数,不写反而最安全
"device": "cuda:1", # 优先使用你的第二张显卡
"trust_remote_code": True # Qwen 必须
}
print(f"正在加载模型 (不指定 dtype 以避免报错) ...")
try:
results = lm_eval.simple_evaluate(
model="hf",
model_args=model_args,
tasks=[task_name],
num_fewshot=0,
batch_size=1,
limit=10
)
print(f"--- {task_name} 评测结果 ---")
# print(f"准确率 (acc): {results['results'][task_name]['acc']}")
# 【临时调试代码】:打印完整结果
print("\n=== 完整的 Results 字典结构如下 ===")
import pprint
pprint.pprint(results) # 这会把所有结果原封不动打出来
except Exception as e:
print(f"发生错误: {e}")
print("💡 如果依然报 dtype 错误,请尝试手动下载模型(见下方注释)")
if __name__ == "__main__":
run_lm_eval_ultimate()
输出结果:
内容太多了,900多行,重点看这里:
bash
'results': {'boolq': {'acc,none': 0.6,
'acc_stderr,none': 0.16329931618554522,
'alias': 'boolq'}},
acc是accuracy(准确度)的意思,寿命准确度是60%。
LmEval投喂自定义数据
说明:
(1)创建jsonl文件,存放多行json数据。
(2)lm_eval的task目录下创建yaml配置文件(必须放在这里)
(3)创建python文件运行代码
python中的tasks的任务名要和yaml中的task任务名一致。
1、创建jsonl文件my_custom_boolq.jsonl,内容:
json
{"text": "Passage: 苹果是一种水果。 Question: 苹果是水果吗?", "answer": "yes"}
{"text": "Passage: 太阳从西边升起。 Question: 太阳从东边升起吗?", "answer": "no"}
2、必须在lm_eval的task目录下创建配置文件my_boolq.yaml ,这样会自动识别,不支持通过路径的方式配置yaml地址。
符合规范的路径地址如:
D:/PycharmProjects/transformer_demo/.venv/Lib/site-packages/lm_eval/tasks/my_boolq.yaml,内容:
yml
task: my_custom_boolq # 任务名称,运行命令时用这个
dataset_path: json # 使用 json 加载器
dataset_kwargs:
data_files:
test: "D:/PycharmProjects/transformer_demo/other_demo/my_custom_boolq.jsonl" # 这里填你上面那个 jsonl 文件的绝对路径
output_type: multiple_choice # 输出类型:多项选择
test_split: test # 指定测试集切片的名称(对应上面的 test)
doc_to_text: "{{text}}" # 输入文本的字段名
doc_to_target: answer # 答案所在的字段名 (对应 jsonl 里的 "answer")
# 定义选项列表,顺序必须对应。lm_eval 会计算 "yes" 和 "no" 的概率
doc_to_choice: ["yes", "no"]
metric_list:
- metric: acc # 评估指标:准确率
aggregation: mean
higher_is_better: true
3、创建python文件custom_lmEval_demo.py,代码:
tasks里面的名称要和yaml里面的名称对应,见代码。
python
import lm_eval
import os
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
def run_custom_eval():
model_args = {
"pretrained": "Qwen/Qwen2.5-0.5B",
"device": "cuda:1",
"trust_remote_code": True
}
# 核心修改在这里
results = lm_eval.simple_evaluate(
model="hf",
model_args=model_args,
tasks=["my_custom_boolq"], # 这里填 yaml 里的 task 名称
# include_path=".", # 填 yaml 文件所在的文件夹路径
num_fewshot=0,
batch_size=1
)
print(results['results']['my_custom_boolq']['acc,none'])
if __name__ == "__main__":
run_custom_eval()
输出结果:
python
Generating test split: 2 examples [00:00, 37.59 examples/s]
Overwriting default num_fewshot of my_custom_boolq from None to 0
100%|██████████| 2/2 [00:00<00:00, 558.38it/s]
Running loglikelihood requests: 100%|██████████| 4/4 [00:05<00:00, 1.39s/it]
fatal: Not a git repository (or any of the parent directories): .git
0.5
进阶玩法
示例太基础,想往深里走可以研究下。
如何喂自定义数据?
大量数据如何统计?
如何和任意模型接入?
lmEval支持的扩展有哪些?
这些都是提前预定义的,只有这几个:
bash
m_eval[api] # 调用在线大模型
lm_eval[multilingual] # 多语言(中文必须)
lm_eval[vllm] # 加速推理
lm_eval[hf] # HuggingFace 模型
lm_eval[all] # 一次性装全部
LmEval文档
LmEval官网git地址:
https://github.com/EleutherAI/lm-evaluation-harness
LmEval官网git文档地址:
https://github.com/EleutherAI/lm-evaluation-harness/blob/main/docs/config_files.md
datasets
datasets和自建考题哪个好?
一个天上一个地下,datasets完胜。
除非极个别情况,而且要基于你对场景,边界极其了解的情况下,自建考题可能会在某方面有优势。
所以,一般来说datasets是不二之选。
常见的数据集有哪些?
需要先按照大类分一下。然后每个大类下有几种不同的产品。
数据集-1. 数学与逻辑推理类 (你的主战场)
这类数据集主要考察模型的"智商",看它能不能像人一样进行多步思考(Chain-of-Thought)。
| 数据集 | 难度/特点 | 适用场景 |
|---|---|---|
| GSM8K | 入门级 (小学水平) | 必测基准。考察多步推理能力。如果模型连这个都做不好,说明逻辑能力很差。 |
| MATH | 进阶级 (竞赛级) | 高难度挑战。包含代数、几何、微积分等。用于测试顶尖模型(如 GPT-4, Qwen-72B)的极限推理能力。 |
| BBH | 逻辑类 | 包含逻辑谜题、常识推理等。考察模型在复杂语境下的逻辑一致性。 |
数据集-2. 综合知识与学术能力类 (全能学霸)
这类数据集考察模型的"知识储备"和"全科能力",通常包含历史、物理、法律、医学等题目。
| 数据集 | 难度/特点 | 适用场景 |
|---|---|---|
| MMLU | 黄金标准 (全学科) | 最权威榜单。涵盖57个学科(从小学到研究生水平)。如果你想知道一个模型"聪不聪明"、"懂不懂行",看 MMLU 分数最准。 |
| CMMLU | 中文全能 | 中文语境必测。类似于 MMLU,但专门针对中国文化和教育体系设计。评测中文大模型(如 Qwen, ChatGLM)时必跑。 |
| ARC | 科学问答 | 包含小学科学题。分为"挑战版"和"简单版",用于测试基础科学常识。 |
数据集-3. 编程与代码能力类 (程序员助手)
| 数据集 | 难度/特点 | 适用场景 |
|---|---|---|
| HumanEval | 代码入门 | 行业标准。包含164道编程题。主要看模型能不能根据注释写出正确的 Python 函数。 |
| MBPP | 工程实战 | 题目更贴近实际工程场景,由程序员编写。用于测试模型解决实际代码问题的能力。 |
数据集-4. 语言理解与指令遵循类 (听话程度)
| 数据集 | 难度/特点 | 适用场景 |
|---|---|---|
| IFEval | 指令遵循 | 测试"听话"程度。比如"请写一段话,且不能用字母 e"。专门测试模型能不能严格遵守格式限制。 |
| GLUE / SuperGLUE | 传统NLP | 老牌的阅读理解、情感分析测试。现在主要用于测试基座模型的语言基本功。 |
self-refine和self-consistence的区别?
最大的区别对象的数量。
self-refine(自我优化)
对当前的结果打分,看是否通过,如果不通过重新生成。这是对一个结果来说的。
self-consistence(自我一致性)
从多个结果中选出最合适的记录。这是对多个结果来说的。