NLP 多任务评测巡检:按任务拆指标,别用一个均值
NLP 多任务评测巡检:按任务拆指标,别用一个均值
多任务评测最容易被一个总分掩盖问题。分类、序列标注和生成任务的误差性质不同,不能用同一平均值代替各自的失败样本。
1. 按任务冻结数据与度量
NLP 评测要先定义任务边界、样本来源、分词或编码版本及度量计算方式。样本仅使用公开、合成或已脱敏内容,并保留可复核的数据版本标识。
每个任务记录数据版本、标签映射、分词器、清洗规则和度量实现。共享样本需要排查跨切分重复;无法公开的数据只保留统计摘要和不可逆标识。
2. 巡检先找结构变化
多任务比较应分别检查各任务的错误类型与覆盖范围,不用一个汇总分数替代细节。新增样本先经复核,再进入固定的回归集。
检查缺失字段、标签集合变化、长度分布漂移和未知 Token 比例,再运行模型指标。告警给出受影响任务和数据分片,不直接把“指标下降”解释成模型退化。
3. 数据契约与分任务报告
[INFO] Loading evaluation dataset: <eval-dataset>.jsonl [WARN] Line <n>: Missing required key in task '<task>'. [CRITICAL] Data Leakage Detected! [CHECK] Matching content hashes found between evaluation and training sets. [RESULT] Metric output is invalid until the dataset is corrected.import hashlib import json from pathlib import Path from typing import List, Dict, Set, Any class NLPEvalDataInspector: """ NLP 多任务评测数据自动化巡检器。 用于在模型评测启动前,执行 SHA256 签名、Schema 校验与数据泄漏去重。 """ def __init__(self, eval_file_path: str, expected_sha256: str): self.eval_path = Path(eval_file_path) self.expected_sha256 = expected_sha256 self.train_hashes: Set[str] = set() def verify_file_integrity(self) -> bool: """检查文件物理 Hash 是否一致,防止文件被篡改""" if not self.eval_path.exists(): print(f"[FATAL] 评测文件不存在: {self.eval_path}") return False hasher = hashlib.sha256() with open(self.eval_path, "rb") as f: while chunk := f.read(8192): hasher.update(chunk) actual_hash = hasher.hexdigest() if actual_hash != self.expected_sha256: print(f"[CRITICAL] 数据集签名校验失败!\n 期望值: {self.expected_sha256}\n 实际值: {actual_hash}") return False print(f"[SUCCESS] 数据集文件 SHA256 完整性校验通过: {actual_hash[:8]}...") return True def load_train_signatures(self, train_file_path: str): """加载训练集的样本 Hash 签名表(用于泄漏查重)""" train_path = Path(train_file_path) if not train_path.exists(): return with open(train_path, "r", encoding="utf-8") as f: for line in f: if line.strip(): # 按照文本内容的 MD5 判定唯一性 item_hash = hashlib.md5(line.strip().encode("utf-8")).hexdigest() self.train_hashes.add(item_hash) def inspect_dataset_schema(self) -> Dict[str, Any]: """执行 Schema 结构合规性与泄漏检测""" passed_count = 0 leaked_count = 0 corrupted_count = 0 required_keys = {"task_name", "input_text", "target_output"} with open(self.eval_path, "r", encoding="utf-8") as f: for line_no, line in enumerate(f, 1): line_str = line.strip() if not line_str: continue # 1. 尝试 JSON 解析 try: data = json.loads(line_str) except Exception: print(f"[ERROR] 第 {line_no} 行 JSON 格式破损,无法解析!") corrupted_count += 1 continue # 2. 检查 Schema 字段缺失 if not required_keys.issubset(data.keys()): missing = required_keys - set(data.keys()) print(f"[ERROR] 第 {line_no} 行缺少关键 Schema 字段: {missing}") corrupted_count += 1 continue # 3. 查重是否在训练集中出现(数据泄漏) item_hash = hashlib.md5(line_str.encode("utf-8")).hexdigest() if item_hash in self.train_hashes: print(f"[WARN] 第 {line_no} 行数据在训练集中已被找到,判定为泄漏样本!") leaked_count += 1 else: passed_count += 1 summary = { "total_passed": passed_count, "total_leaked": leaked_count, "total_corrupted": corrupted_count, "is_valid": (leaked_count == 0 and corrupted_count == 0) } print(f"[INSPECTION REPORT] 校验完成: 合格={passed_count}, 泄漏={leaked_count}, 破损={corrupted_count}") return summary[AUDIT] 启动评测前数据质量巡检... [SUCCESS] 数据集文件 SHA256 完整性校验通过: a8f91c32... [ERROR] 第 104 行缺少关键 Schema 字段: {'target_output'} [WARN] 第 215 行数据在训练集中已被找到,判定为泄漏样本! [INSPECTION REPORT] 校验完成: 合格=965, 泄漏=20, 破损=15 [FATAL] 发现数据集污染,评分结果不可信! 评测流程已终止退出。4. 复核清单
- 各任务的数据版本、标签和度量是否独立记录。
- 汇总分数之外是否保留分任务错误类型。
- 数据结构变化是否先于模型指标检查。
- 新增回归样本是否经过人工复核。
总结
“日常巡检怎样少走弯路”应以清晰的条件和脚本复核。先记录边界,再解释结果。
