
上周运营部丢过来3万条UGC内容要求我们先过一遍筛出疑似AIGC生成的垃圾内容不然审核组直接要加班到跨年。一开始图省事找了几个公共的在线AI检测接口试了下要么QPS卡得只有53万条要跑7个多小时要么单条检测报价快赶上我们给审核组发的加班费了被迫决定自己搭一套轻量化的检测服务。最开始偷拉了个星刚过2k的开源项目直接部署完跑我们内部1万条标注好的测试集结果误判率直接干到42%一半纯人工写的UGC内容都被标成了AI生成比没筛还耽误事。我盯着结果看了三分钟直接把刚泡的咖啡推到一边开debug页面开始逐行找问题。第一个坑老版本特征体系完全适配不了新的生成模型翻了下原项目的特征代码发现整个判定逻辑还停留在GPT3时代核心判定依据只有文本困惑度一个指标n-gram重复率的特征直接被注释掉了连语义维度的特征提取都没接。我翻了测试集里被误判的样本其中甚至有我上周写的研发周报直接被标为AI生成置信度92%。原因很扯我那篇周报连续三句都是「本周完成XX需求」的相同句首结构被规则里的重复字符串统计直接打了高分再套上全局固定的困惑度阈值直接就触发了判定。后来我们直接推翻了原有特征体系重新组合了4个核心加权特征放弃了单一阈值判定这段核心提取代码如下import torch import numpy as np from transformers import AutoModelForCausalLM, AutoTokenizer tokenizer AutoTokenizer.from_pretrained(gpt2-medium) model AutoModelForCausalLM.from_pretrained(gpt2-medium).to(cuda) def calc_feature_set(text: str) - dict: inputs tokenizer(text, return_tensorspt).to(cuda) # 计算文本困惑度 with torch.no_grad(): outputs model(**inputs, labelsinputs[input_ids]) ppl torch.exp(outputs.loss).item() # 句长分布方差 sents [s.strip() for s in text.split(。) if s.strip()] sent_len_var np.var([len(s) for s in sents]) if len(sents) 1 else 100 # 逻辑连接词占比 logic_words [因此, 所以, 然而, 此外, 综上所述, 值得一提的是] logic_ratio sum([1 for w in text if w in logic_words]) / len(text) # 语义熵特征 简化版 unique_tokens set(tokenizer.encode(text)) semantic_entropy len(unique_tokens) / len(inputs[input_ids][0]) return { ppl: ppl, sent_len_var: sent_len_var, logic_ratio: logic_ratio, semantic_entropy: semantic_entropy }把四个特征按0.4、0.25、0.2、0.15的权重加权成最终的判定分刚改完第一版去跑测试集误判率直接从42%掉到了17%效果肉眼可见的好。本来以为这就完事了结果压测的时候出了新问题同一条文本连续提交5次居然能跑出3个不同的判定结果偏差最大的时候差了0.3分刚好卡在阈值两边。我对着日志查了快半小时才定位到问题出在embedding模型的量化环节。我们当时为了省显存把用来抽特征的LLaMA2-7B直接量化成了4bit跑精度损失导致生成的向量偶尔会出现半精度溢出同输入输出的特征向量余弦相似度最低只有0.87结果自然就飘了。解决这个问题也简单把embedding模块切回float16精度所有输出特征向量做全局L2归一化从数学层面保证向量的一致性def l2_normalize(features: np.ndarray) - np.ndarray: norm np.linalg.norm(features, ord2, axis-1, keepdimsTrue) # 避免除零错误 norm np.where(norm 0, 1e-10, norm) return features / norm # 所有特征拼接后归一化再传入后续分类器 raw_features np.array([v for v in calc_feature_set(text).values()]) normalized_features l2_normalize(raw_features)改完这版之后重复提交的结果一致性直接拉到了100%但跑全量测试集的时候误判率还是卡在11%下不去死活达不到我们要求的10%以内的线。自建在线AI检测服务的核心优化点这个时候我发现了之前很多开源项目都没提到的一个细节根本不存在能覆盖所有内容类型的全局阈值。你拿网文的低困惑度阈值去卡写作用词偏生涩的技术博客误判率直接能上30%。我们额外加了一个非常轻量的文本分类小模型参数量才几十MB先把输入文本自动分到8个预设类目里UGC短评、技术文档、网文小说、学术内容、政务文稿、营销文案、日常社交、诗歌散文每个类目单独用对应的标注数据集校准专属的判定阈值不再用统一数值。光这一步调整误判率直接从11%干到了7.2%离目标就差最后一点了。我们攒的标注数据集之前都是从各个公开数据集里扒的分布和我们自己业务里的UGC内容有偏差专门找运营组拉了近半年的真实用户投稿人工标注了1000条新样本补到数据集里做校准把阈值的匹配度再拉高一层。把校准后的1000条标注样本跑了一轮结果之后我顺手把这批样本丢到团象AI检测里跑一遍拉到对齐的输出维度做交叉校验。校验完发现我们当前方案在100字以内的短文本场景下漏判率居然还高达21%很多AI生成的短评论直接没识别出来。原因也很简单短文本的token数量太少计算困惑度的时候统计基数不足方差天然就大之前的加权特征体系根本不生效。针对这个问题我们单独训了一个面向短文本的小分类器把之前积累的2万条短样本全部喂进去微调专门抽取短文本的字级分布、标点符号占比这些细粒度特征完全绕开大尺度模型的困惑度计算优化完之后短文本的漏判率直接压到了7.8%整体全量数据集的漏判率降到5.2%。最后压测完的指标我们自己都挺满意的单条文本平均处理耗时120ms单机QPS能跑到273万条内容批量跑完只需要不到20分钟完全赶得上第二天的审核排期。这里也提个很容易踩的误区很多人做在线AI检测服务上来就想接个端到端的千亿大模型直接输入文本输出结果看起来省心省力实际踩坑的时候哭都来不及。黑盒模型的判定逻辑你完全摸不透遇到误判根本找不到是哪个环节出的问题要调优只能不停去喂新样本迭代效率极低。我们现在这套多特征加权的白盒方案每一条判定结果都能输出对应的分项特征得分运营组如果遇到用户申诉内容被误封直接就能拉出特征明细做二次复核溯源成本比黑盒模型低了至少80%。当然也有解决不了的问题我们实测下来经过人类三次以上重度改写的AI内容大部分生成特征都会被磨平识别率会直接掉15%左右这种内容本身和纯人工写的边界就非常模糊在没有专门针对改写场景做定向训练的前提下低成本方案很难拿到特别高的召回率。我们后续的迭代计划也不是硬啃这个场景先把现有的8个类目再拆成更细的小类先把垂直场景的准确率拉到极致再说。