AI回答采集数据清洗:Python实现空回答、拒绝与无关样本识别

问题:采集的AI回答JSON数据中混有空回答、拒绝回答、无关内容及重复样本,如何自动识别并清洗?环境:Python 3.9.7 + pandas 1.3.3,Ubuntu 20.04。读者:AI数据工程师。本文提供可复用的校验函数和验证示例,不涉及多轮对话场景。

环境说明

  • 操作系统:Ubuntu 20.04 LTS

  • Python版本:3.9.7

  • 依赖安装:

    bash 复制代码
    pip install pandas==1.3.3
  • 输入格式:每行一个JSON对象的文本文件,字段包括 query, response, timestamp, model。示例:

    json 复制代码
    {"query": "什么是AI", "response": "人工智能是...", "timestamp": "2025-01-01", "model": "gpt-4"}

校验流程

采集数据中常见的无效样本包括空回答、拒绝回答、无关内容以及重复记录。下面分别处理,最后整合为一个函数。

1. 无效样本识别

无效样本分三类:

  • 空回答response 字段为空或仅含空白字符。
  • 拒绝回答:回答以"抱歉"、"对不起"、"无法回答"等开头,或包含"作为AI"、"我无法"等模式。
  • 无关内容:回答与查询在语义上不相关。这里使用基于词重叠的简单规则,实际项目中可替换为嵌入向量相似度。
python 复制代码
import re

def is_empty_response(response: str) -> bool:
    return not response or response.strip() == ""

def is_refusal_response(response: str) -> bool:
    refusal_patterns = [
        r"^抱歉", r"^对不起", r"^无法回答",
        r"作为AI", r"我无法", r"我不能",
        r"这个问题我无法"
    ]
    for pattern in refusal_patterns:
        if re.search(pattern, response, re.IGNORECASE):
            return True
    return False

def is_irrelevant_response(query: str, response: str, threshold: float = 0.3) -> bool:
    # 简单基于词重叠的相似度,实际可用嵌入模型
    query_words = set(re.findall(r'\w+', query.lower()))
    response_words = set(re.findall(r'\w+', response.lower()))
    if not query_words or not response_words:
        return True
    overlap = len(query_words & response_words) / len(query_words)
    return overlap < threshold

2. 重复样本去重

重复样本指完全相同的 queryresponse 组合。使用MD5哈希快速去重:

python 复制代码
import hashlib

def compute_hash(query: str, response: str) -> str:
    content = f"{query}||{response}"
    return hashlib.md5(content.encode('utf-8')).hexdigest()

def deduplicate(records: list) -> list:
    seen = set()
    unique = []
    for rec in records:
        h = compute_hash(rec['query'], rec['response'])
        if h not in seen:
            seen.add(h)
            unique.append(rec)
    return unique

3. 异常值检测

异常值包括回答长度异常(过短或过长)。使用IQR方法检测,但注意该方法假设数据正态分布,对于偏态分布可能误判。

python 复制代码
import pandas as pd

def detect_outliers_by_length(df: pd.DataFrame, column: str = 'response_length') -> pd.Series:
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    return (df[column] < lower_bound) | (df[column] > upper_bound)

完整校验函数

将上述步骤整合,输出清洗后的数据及异常报告:

python 复制代码
def quality_check(records: list) -> (list, dict):
    report = {
        'total': len(records),
        'empty': 0,
        'refusal': 0,
        'irrelevant': 0,
        'duplicate': 0,
        'length_outlier': 0,
        'valid': 0
    }
    
    # Step 1: 无效样本识别
    valid_records = []
    for rec in records:
        if is_empty_response(rec['response']):
            report['empty'] += 1
            continue
        if is_refusal_response(rec['response']):
            report['refusal'] += 1
            continue
        if is_irrelevant_response(rec['query'], rec['response']):
            report['irrelevant'] += 1
            continue
        valid_records.append(rec)
    
    # Step 2: 重复去重
    unique_records = deduplicate(valid_records)
    report['duplicate'] = len(valid_records) - len(unique_records)
    
    # Step 3: 异常值检测
    df = pd.DataFrame(unique_records)
    df['response_length'] = df['response'].apply(len)
    outliers = detect_outliers_by_length(df)
    report['length_outlier'] = outliers.sum()
    
    # 标记异常但保留
    df['is_length_outlier'] = outliers
    final_records = df.to_dict('records')
    report['valid'] = len(final_records) - report['length_outlier']
    
    return final_records, report

验证结果

以下为模拟数据示例,实际使用时请替换为真实采集数据。

python 复制代码
test_records = [
    {'query': '什么是AI', 'response': '人工智能是...', 'timestamp': '2025-01-01', 'model': 'gpt-4'},
    {'query': '天气', 'response': '', 'timestamp': '2025-01-01', 'model': 'gpt-4'},
    {'query': '你好', 'response': '抱歉,我无法回答。', 'timestamp': '2025-01-01', 'model': 'gpt-4'},
    {'query': '什么是AI', 'response': '人工智能是...', 'timestamp': '2025-01-01', 'model': 'gpt-4'},  # 重复
    {'query': '股票', 'response': '今天天气很好。', 'timestamp': '2025-01-01', 'model': 'gpt-4'},  # 无关
    {'query': '写一首诗', 'response': '春眠不觉晓...' * 1000, 'timestamp': '2025-01-01', 'model': 'gpt-4'},  # 长度异常
]

cleaned, report = quality_check(test_records)
print(report)
# 预期输出:{'total': 6, 'empty': 1, 'refusal': 1, 'irrelevant': 1, 'duplicate': 1, 'length_outlier': 1, 'valid': 1}

正常情况下,最终 cleaned 列表包含1条正常记录和1条长度异常记录(被保留但标记)。

常见问题与避坑

  • 无关内容检测的阈值:基于词重叠的方法对短文本有效,但对长文本或同义词不敏感。建议在实际项目中替换为嵌入向量余弦相似度,阈值需根据业务数据调整。
  • 重复去重的粒度 :如果只对 response 去重,可能误删不同查询的相同回答。建议组合 queryresponse 哈希。
  • 异常值边界:IQR方法假设数据正态分布,对于偏态分布可能误判。可改用分位数或业务规则(如回答长度<10字符视为异常)。

总结

本文实现了一套轻量级AI回答数据校验流程,覆盖空回答、拒绝回答、无关内容识别、重复去重和长度异常检测。该方案可直接集成到采集管道中,输出清洗后的数据和质量报告。适用范围:单轮问答场景;对于多轮对话,需调整重复检测逻辑。当前限制:无关检测依赖简单词重叠,高精度场景需引入语义模型。方案整体偏规则,适合快速过滤,但无法处理语义层面的复杂异常。

相关推荐
wifi___9 小时前
全局异常处理的原理
java·开发语言
火山引擎开发者社区10 小时前
七夕漫谈|向量检索界的超强 CP:DiskANN 铺路,RaBitQ 加速,又准又快还能省
人工智能
平常心的技术小牛12 小时前
Qt-快速上手-QMenuBar
开发语言·qt
孙启超13 小时前
【大模型应用开发】LLM 到底是什么,以及它是怎么训练的
人工智能·lora·llm·微调·sft·token·rlhf
新知图书13 小时前
7.1 需求分析与规划 《AI Agent智能体开发实践》
人工智能·agent·ai agent·智能体
青 春 记 忆13 小时前
零基础入门Python11|Git实战:为任务管理器建立版本历史
开发语言·git·vscode·python·python3.11
clorinda13 小时前
机器学习文本分类入门:从数据清洗到中文评论词向量转换
人工智能
小宋102113 小时前
Dify 知识库实战:从 PDF 导入到带引用回答,完整搭建企业问答助手
人工智能·ai编程
Python私教13 小时前
多个项目怎么安全合并?先适配,再切换
后端·python·架构
用户9385156350714 小时前
从Vibe Coding到SDD:规范驱动开发如何拯救AI编程失控
人工智能