近日开始着手反抄袭工作,做了一些思考和尝试。
现状与动机
目前看,比较明显,容易判定的抄袭行为,主要包括:
-
直接完整复制,虽然容易被识破,但是因为成本低,甚至有些抄袭者使用 爬虫进行大量搬运。 -
洗稿,主要是打乱句子和段落顺序,使其看起来是另一篇正常的文章,但 是其实是比较简单的改头换面,句子仍然都是抄袭而来。 -
洗稿,但是更为深入,对句子也做了大量修改,这种洗稿较难识别,甚至 与一些正常的讨论、笔记等文章有一定的模糊地带,但是这种方式工作量 大,多?于社会新闻、时评热稿等高价值文章的抄袭行为。 -
剪刀党,可能剪裁自多篇文章,并非简单抄袭和复制某一篇文章。 -
部分抄袭,对于技术文章,有部分抄袭行为是围绕源头的核心内容,例如 代码进行搬运,并重写文字部分。这种行为的判定可以参考科技论文对抄袭的判定,大量不正规引用,或者未做清晰标识的引用,仍然是抄袭。 -
在CSDN,抄袭的主要动机是建立自己的个人影响力,故抄袭的产出主要仍然是可以 阅读的文章。反抄袭是一个与抄袭者?期对抗的过程,随着检查手段的发展,抄袭行 为也会演化。反抄袭是一个复杂工程,需要多方面的努力。
思考
反抄袭,必然从查重入手,那么第一个反应是利用既有的搜索技术,实现匹配查找。 但是搜索与查重的目标不同,其着重点也不同,直接使用效果并不好。假阳性比例会 非常高。
数据库专家周正中老师介绍过关于相似 HASH 的运用。这种策略可以有效对抗简 单复制的抄袭着,但是对于洗稿行为就不太有效。抄袭者只需要简单的增加一些无关 紧要的文字,就可以使相似Hash失效。
相对来说,通常的抄袭行为仍然会保持句子的原貌,因此构造一个对句子高度敏 感的查重算法,作为整个反抄袭工作的起点,是一个可行的思路。
参考自然语言处理工作中的词袋概念,我们可以构造一个“句袋”系统,将每一篇文 章按句子切分,然后在整个句库中查找匹配的句子。
目前梳理的主要步骤包含
- - 将既有文章按句切分,在数据库中保存所有句子的指纹和相关特征
-
建立指纹主要是为了引入行之有效的数据库索引,所以很多种散列 算法都可以用在这里,为了简单,我们可以先选用md5,虽然在密 码管理等领域,md5已经不是一个非常好的选择,但是在这种句袋模型里,它仍然够用 -
在指纹列上建立一个高度压缩,有利于快速匹配的索引,目前使用的是 PostgreSQL 的 bloom 索引。 -
保存每个句子的文章 id,便于查询时找到被抄袭的文章,这个字段要允许同一个句子出现在多篇文章,PostgreSQL JSONB 非常适用。 -
记录每个句子出现在多少篇文章中,这种计数经常在NLP中被称作频率,但是我们在写入数据库,和查询计算的时候仅需 计数。PostgreSQL 的 upsert 支持可以很好的完成这个工作
- 因为具体的业务原因,同一篇文章可能出现在我们的数据集中多 次,因此写入时要有去重机制,最终我通过 insert on conflicit do update where 形式的写入查询,实现了这个幂 等写入逻辑
- 搜索时,我们同样要构造文章句袋,然后生成句子的指纹集合,再从数据 库中做一个 where fingerprint = ANY(:fingerprints) 查询。
- 理想状态下正常的文章应该不会有重用的句子,但是叙述性或抒情文字, 甚至一些科技内容,例如数学定理,确实可能出现在多篇文章,因此要设 定重复句的阈值,包括最低计数和在句袋中的占比,超过阈值才会触发判 定。
- 与词频类似,频率越低的句子,越能表现文章特征,在查重时如果匹配多 篇文章,应按照频率由低向高排列。
- 基于关系型数据库构造句库,可以有效的满足句库本身的持续增?需要。
挑战
在开发过程中,我也发现了一些问题。
-
目前粗糙的开发测试来看,统计上句库表的行数大概与文章数目有两个数 量级的差距,故要有分库分表的预案 -
PostgreSQL 要求表的主键必须是 Btree索引,否则我可以直接将 fingerprint 作为主键 -
大规模数据集的查询效果,仍有待验证,目前的一个挑战是开发尽可能快 速的写入脚本——为了与整个NLP组的业务规则保持一致,我们尽可能重用 既有的 Python代码,那么如何提速成为了一个不大不小的挑战,在将来反 抄袭系统可能会引入一些更快的技术工具 -
- ?目前看分句仍然是一个问题,简单的基于标点符号的断句算法,对技术文 章仍有一些不够用的情况。这可能是一个需要持续改进的工作,并且反抄 袭对文章的句子提取要求,与推荐等 NLP 工作存在一些不同。 -
- ?单一的句库模型并不能处理所有的抄袭情况,反抄袭工具开发是一个?期 的持续过程。 -
- ?反抄袭永远需要热情的用户和有责任心的团队密切合作
|