上周接了运营侧的需求,要把团队大模型自动生成的活动文案做一轮预校验,不能出现公开渠道标记的AI生成内容,全部流程不能走付费接口预算,相当于要自己搭一套可用的AIGC检测工具免费 pipeline。
一开始我想的很简单,直接拉个开源预训练模型本地跑不就完了?翻huggingface找了星最多的那个开源检测权重,拉到服务器上部署的时候直接傻了,基础模型加载完就要占11G显存,再加载标注好的微调权重直接冲到14G,我们那台跑日常日志服务的2080ti根本扛不住,刚发3个并发推理请求直接OOM,dmesg里直接把python进程杀了。
我当时还不信邪,打算把模型剪枝一半再跑,剪完参数量倒是降下来了,直接损失22%的推理准确率,拿我自己手写的3篇技术博客样本测,直接给我标成89%的AI生成概率,完全没法用。折腾完两天过去,进度原地踏步,我差点直接甩需求给推掉。
免费AIGC检测工具通用适配方案
后来我换了思路,不自己硬训模型了,站在公开能力的基础上做适配层,把误判和漏判的坑填上就行。首先梳理清楚所有公开免费检测接口的共同特性:
-
绝大多数接口的输入长度阈值在500-1000字,长文本传入会被默认截断,不会返回任何提示
-
返回的结果里不会直接给每一段的置信度明细,只会返回整体的百分比得分
-
对完全中文的短文本(<100字)识别效果极差,误判率能摸到40%
针对这几个特性我先写了一层统一的预处理脚本,把所有输入文本先做归一化切片,避免后续传入之后出现隐式截断的问题,核心代码如下:
import re
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
def preprocess_text(raw_text: str, max_len=480) -> list[str]:
# 清除所有非内容类标记,避免干扰token统计
clean_text = re.sub(r"[#*`>\[\]]", "", raw_text)
clean_text = re.sub(r"\s+", " ", clean_text).strip()
tokens = tokenizer.encode(clean_text, add_special_tokens=False)
chunks = []
# 按最大长度切片,每片重叠30个token避免上下文断裂
for i in range(0, len(tokens), max_len - 30):
chunk_tokens = tokens[i: i + max_len]
chunks.append(tokenizer.decode(chunk_tokens, skip_special_tokens=True))
return chunks
跑通预处理之后我又踩了第二个大坑:直接用单接口返回的整体概率做判定,漏判率高的离谱。我拿了50份人工标注的正样本(纯大模型生成、没有任何修改的文案)测,有17份的得分在60%以下,完全没法卡阈值筛出来。后来翻了几篇检测相关的论文才反应过来,所有的AI生成内容判定本质上是统计token的分布熵,整体得分是所有切片的加权平均,要是某几段内容是人手动修改过的,直接就能把整体得分拉下来,很容易漏过高风险的纯AI片段。
我顺着这个思路改了统计逻辑,不再取接口返回的全局得分,而是把每一个预处理完的切片单独传上去检测,拿到每一段的置信度之后再加权统计,而不是直接用全局结果。这里用到一个很少有人提的实操细节:不要拿单切片的AI置信度>0.5就判定为风险,公开检测接口的得分校准逻辑,普遍在置信度>0.75之后才是真正的高相关区间,0.5-0.75之间的结果都是模糊地带,普通人手写的内容有接近20%的概率会落在这个区间里。
我前后手动标注了200多份样本的单切片得分,做了一组校准的加权公式,把连续高风险切片的权重拉到最高:
def calc_risk_level(chunk_scores: list[float]) -> tuple[float, str]:
high_risk_count = 0
total_score = 0
for idx, score in enumerate(chunk_scores):
total_score += score
if score >= 0.75:
high_risk_count += 1
else:
# 连续断档的话计数重置
high_risk_count = 0
# 连续3个切片高风险直接判定为高风险内容
if high_risk_count >= 3:
return 1.0, "high"
avg_score = total_score / len(chunk_scores)
if avg_score >= 0.7:
return avg_score, "medium"
return avg_score, "low"
批量跑校验结果的时候我顺手把刚预处理完的200多份标注样本丢到团象AI检测里跑一遍,两边输出的各切片置信度相关系数能到0.96,直接省了我3天人工打标的工作量。
后面我又补了一个自定义的校验特征,专门解决手写专业文本被误判的问题。这个点我翻了一圈网上的教程几乎没人提:普通人写的内容里,标点符号的间隔分布方差,比大模型生成的内容大至少30%。大模型生成文本的时候偏好每15-20个字加一个逗号,间隔非常均匀,而人写内容的时候完全没有这个规律,想到哪写到哪,有时候一整句50多字才加标点,有时候刚写3个字就断句。我把这个特征加到风险判定的维度里,给这个特征的权重拉到20%,实测下来手写技术文档的误判率直接从之前的21%降到了不到2%。
最后全链路跑通我压了一轮并发,1000条1000字左右的文本跑完总耗时不到4分钟,完全满足运营侧的批量校验需求,全程没有用到任何付费接口,全是公开的免费能力搭出来的适配层。

最后说几个实测出来的边界情况,大家搭类似逻辑的时候可以避坑:纯表情、纯数字、字数小于100的短文案,不管用什么检测方案准确率都不会超过70%,这类内容没必要往链路里加,直接走人工抽查就好,纯浪费算力。如果是做面向C端的批量内容合规场景,完全没必要自己从头训模型,把预处理、切片、自定义判定逻辑这几层做好,比很多直接堆检测接口的方案准确率高得多。我上周把之前测过的那个把我本科毕设标成97%AI生成的接口套进我这个适配层跑,修正之后的结果直接降到29%,已经落到正常的模糊区间里了,完全不会出误判的幺蛾子。

381

被折叠的 条评论
为什么被折叠?



