引言

我之前参加了一个中文文本智能校对大赛,拿了17名,虽然没什么奖金但好歹也是自己solo拿的第一个比较好的名次吧,期间也学到了一些BERT应用的新视角和新的预训练方法,感觉还挺有趣的,所以在这里记录一下这期间学到的知识,分享一下自己的比赛过程。这个赛题任务大概就是,选择网络文本作为输入,从中检测并纠正错误,实现中文文本校对系统。即给定一段文本,校对系统从中检测出错误字词、错误类型,并进行纠正。

任务定义

系统/模型的输入为原始序列\(X=(x1,x2,..,xn)\),输出为纠错后的序列 \(Y=(y1,y2,..,ym)\)X可能已经是完全正确的序列,所以X可能与Y相同。系统/模型需要支持多种粒度的序列,包括:字词、短语、句子、短文。

中文错误类型

一般包含三种,从字词到语义错误,难度依次递增

Soft-Masked BERT (ACL2020,字节跳动)

论文:Spelling Error Correction with Soft-Masked BERT

注意该模型只能处理输入序列和输出序列等长度的纠错场景!

模型简介:整个模型包括检错网络和改错网路:

  • 检错网络是一个简单的Bi-GRU+MLP的网络,输出每个token是错字的概率
  • 改错网络是BERT模型,创新点在于,BERT的输入是原始Token的embbeding和 [MASK]的embbeding的加权平均值,权重就是检错网络的概率,这也就是所谓的Soft-MASK,即 \(ei=pi∗e_{mask}+(1−p_i)∗e_i\) 。极端情况下,如果检错网络输出的错误概率是1,那么BERT的输入就是MASK的embedding,如果输出的错误概率是0,那么BERT的输入就是原始Token的embedding。

在训练方式上采用Multi-Task Learning的方式进行,\(L=λ·L_c+(1−λ)·L_d\),这里λ取值为0.8最佳,即更侧重于改错网络(Lc means correction)的学习。

模型结果:

该结果是句子级别的评价结果,Soft-MASK BERT在两个数据集上均达到了新的SOTA,相比仅使用BERT在F1上有2-3%的提升。

该模型处理错误的情况,主要有以下缺点,模型没有推理能力不能处理逻辑错误(语义错误),模型缺乏世界知识不能处理知识错误(地名等)

用MLM-phonetics纠错

2021ACL中文文本纠错论文:Correcting Chinese Spelling Errors with Phonetic Pre-training 论文笔记 - 知乎 (zhihu.com)

论文地址: paper

作者在论文中对比了MLM-baseMLM-phonetics的差异:

  1. MLM-base 遮盖了15%的词进行预测, MLM-phonetics 遮盖了20%的词进行预测。
  2. MLM-base 的遮盖策略基于以下3种:[MASK]标记替换(和BERT一致)、随机字符替换(Random Hanzi)、原词不变(Same)。且3种遮盖策略占比分别为: 80% 、10%、10%。MLM-phonetics的Mask策略基于以下3种:[MASK]标记替换(和BERT一致)、字音混淆词替换(Confused-Hanzi)、混淆字符的拼音替换(Noisy-pinyin)。且这3种遮盖策略分别占比为: 40%、30%、30%。

端到端文本纠错包括Detection Module和Correction Module2个部分,具体如下图所示,但官方没有发布预训练模型,paddle中提供了使用ernie1.0为backbone的模型:

GECToR

GECToR -- Grammatical Error Correction: Tag, Not Rewrite | Papers With Code

Seq2Edit模型简介:本文属于seq2edit模型,Seq2Edit模型只有Encoder,将GEC任务看作是一个序列标注任务,在每个Time-Step预测生成一个编辑动作。通过使用预测得到的编辑动作对源文本进行转化,我们便可以得到目标文本。属于一种序列标注模型,通过预先定义一些编辑动作,采用神经网络为句子的token打上编辑标签,从而进行语法纠错。

目前较为常用的Seq2Edit模型有PIE、GECToR等。以2019年Awasthi等人的并行迭代编辑(Parallel Iterative Edit, PIE)模型为例,它们使用的编辑动作有:复制、删除、增加、替换、变形等。其中,由于增加操作和替换操作需要在候选集中指定单词,所以实际上包含多种编辑操作。总体而言,Seq2Edit模型的编辑空间远远小于Seq2Seq模型的词汇空间,所以解码空间小了很多。此外,非自回归模型能够并行解码,速度优势巨大,比如GECToR 5次迭代比NMT beam-size为1还快接近一倍,并且是当前的sota。

Token级别的变换

原理

比较两个错误和正确句子的diff可以找到一系列编辑操作,从而把语法错误的句子变成语法正确的句子。为了给序列打标签,可以把编辑映射到某个token上认为是对这个token的操作。如果同一个token需要进行多个编辑操作,则需要采用迭代的方法给序列打标签。

比如上图的例子,红色的句子是语法错误的句子:”A ten years old boy go school”。

  1. 先经过一次序列打标签,找到了需要对ten和go进行操作,也就是把ten和years合并成ten-years,把go变成goes。注意:这里的用连字符”-“把两个词合并的操作定义在前面的Token上。
  2. 接着再进行一次序列打标签,发现需要对ten-years和goes进行操作,把ten-years变成ten-year然后与old合并,在goes后面增加to。
  3. 最后一次序列打标签在school后面增加句号”.”。

变换

上述的编辑操作被定义为对某个Token的变换(Transform),如果词典是5000的话,则总共包含4971个基本变换(Basic Transform)和29个g-变换。

基本变换

基本变化包括两类:与Token无关的和与Token相关的变换。与Token无关的包括\(KEEP(不做修改)、\)DELETE(删除当前token)。与token相关的有1167个\(APPEND_t1变换,也就是在当前Token后面可以插1167个常见词t1(5000个词并不是所以的词都可以被插入,因为有些词很少会遗漏);另外还有3802个\)REPLACE_t2,也就是把当前Token替换成t2。

g-变换

前面的替换只是把当前词换成另一个词,但是英语有很多时态和单复数的变化,如果把不同的形态的词都当成一个新的词,则词的数量会暴增,而且也不利于模型学习到这是一种时态的变化。所以这里定义了g-变换,也就是对当前Token进行特殊的变换。完整的g-变换包括:

  • CASE类的变化包括字母大小写的纠错,比如$CASE_CAPITAL_1就是把第2(下标0开始)个字母变成对象,因此它会把iphone纠正为iPhone。
  • MERGE把当前Token和下一个合并,包括MERGESPACE和MERGESPACE和MERGE_HYPHEN,分别是用空格和连字符”-“合并两个Token。
  • SPLIT $SPLIT-HYPHEN把包含连字符的当前Token分开成两个
  • NOUN_NUMBER把单数变成复数或者复数变成单数。
  • VERB_FORM动词的时态变化,这是最复杂的,我们只看一个例子。比如VERB_FORM_VB_VBZ可以把go纠正成goes。时态变换使用了word forms提供的词典

预处理获得训练数据

我们的训练数据只是错误-正确的句对,没有我们要的VERB_FORM_VB_VBZ标签,因此需要有一个预处理的过程把句对变成Token上的变换标签。

1 token映射

把源句子(语法错误句子)的每一个Token映射为目标句子(语法正确句子)的零个(删除)、一个或者多个Token。比如”A ten years old boy go school”->”A ten-year-old boy goes to school.”会得到如下的映射:

A → A
ten → ten, -
years → year, -
old → old
boy → boy
go → goes, to
school → school, .

这是一种对齐算法,但是不能直接用基于连续块(Span)的对齐,因为这可能会把源句子的多个Token映射为目标句子的一个Token。我们要求每个Token有且仅有一个标签,所以这里使用了修改过的编辑距离的对齐算法。这个问题的形式化描述为:假设源句子为\(x_1,…,x_N\),目标句子为\(y_1,…,y_M\),对于源句子的每一个Token \(x_i(1≤i≤N)\),我们需要找到与之对齐的子序列\(y_{j_1},…,y_{j_2}\),其中\(1≤j_1≤j_2≤M\),使得修改后的编辑距离最小。这里的编辑距离的cost函数经过了修改,使得g-变换的代价为零。

2 找出token变换

通过前面的对齐,我们可以找到每个Token的变换,因为是一对多的,所以可能一个Token会有多个变换。比如上面的例子,会得到如下的变换:

[A → A] : $KEEP
[ten → ten, -]: $KEEP, $MERGE_HYPHEN
[years → year, -]: $NOUN_NUMBER_SINGULAR, $MERGE_HYPHEN
[old → old]: $KEEP
[boy → boy]: $KEEP
[go → goes, to]: $VERB_FORM_VB_VBZ, $APPEND_to
[school → school, .]: $KEEP, $APPEND_{.}

3 保留一个变换

只保留一个变换,因为一个Token只能有一个Tag。但是有读者可能会问,这样岂不是纠错没完全纠对?是的,所以这种算法需要多次的迭代纠错。最后的一个问题就是,多个变换保留哪个呢?论文说优先保留KEEP之外的,因为这个Tag太多了,训练数据足够。如果去掉KEEP还有多个,则保留第一个。所以最终得到的标签为:

[A → A] : $KEEP
[ten → ten, -]: $MERGE_HYPHEN
[years → year, -]: $NOUN_NUMBER_SINGULAR
[old → old]: $KEEP
[boy → boy]: $KEEP
[go → goes, to]: $VERB_FORM_VB_VBZ
[school → school, .]: $APPEND_{.}

模型结构

类似BERT的Transformer模型,加两个全连接层和一个softmax。根据不同的Pretraining模型选择不同的subword切分算法:RoBERTa使用BPE;BERT使用WordPiece;XLNet使用SentencePiece。因为我们需要在Token上而不是在subword进行Tag,因此我们只把每个Token的第一个subword的输出传给全连接层。

迭代纠错

前面介绍过,有的时候需要对一个Token进行多次纠错。比如前面的go先要变成goes,然后在后面增加to。因此我们的纠错算法需要进行多次,理论上会一直迭代直到没有发现新的错误。但是最后设置一个上限,因此论文做了如下统计:

基本上两次迭代就能达到比较好的效果,如果不在意纠错速度,可以到三次或者四次。

实验

3-stage training

本文中,训练分为三个阶段:在合成数据上的Pretraining;在错误-正确的句对上的fine-tuning;在同时包含错误-正确和正确-正确句对数据上的fine-tuning。

有第三步让模型看懂一些没有语法错误的句子是很重要的,实验也说明第三步使得结果好了很多;最后一行表示加上一些推理的trick,具体如下

推理的trick

  • 给$KEEP增加一个bias

    • 因为大部分的句子错误较少,而训练时错误的却居多,所以要给它加一个bias
  • 增加最小的错误概率阈值
    • 因为模型会尽量纠错,即使概率很少。这里增加一个句子基本的概率值,如果小于它则不纠错。

这两个值是使用验证集找到的。从上图的结果可以看出,使用了推理trick后效果提升不少。

预训练模型

RoBERTa和XLNet比较好,GPT-2和ALBERT较差,文章认为因为是生成模型

性能提升技术

重排序 TODO

与其它集成在模型内部的性能提升手段不同,重排序(Reranking)更像是模型预测完成之后的一个独立的阶段,所以它被称为一种后处理方法(post-training)。它的目的主要是为了解决:模型预测得分最高的结果,往往并不是最好的结果。

它的主要做法是:将GEC模型输出的N个最好的结果作为候选集,使用一些在GEC模型中无法被很好地覆盖但却又较为重要的特征,对这N个最好结果进行重新排序,选取得分最高的结果作为最终的预测结果。

通过使用重排序方法,我们可以引入丰富的语言学知识,考虑更多全局的特征,还能集成多个GEC模型的输出一起重排序。

常用的重排序特征有:1)语言模型得分;2)编辑距离特征;3)句法特征

模型集成 TODO

模型集成(Model Ensemble)也是当下最为常用的性能提升手段之一,它的做法主要有:1)在Beam-Search解码阶段,将多个模型的输出取平均;2)在输出预测结果阶段,采用多模型投票的方式确定编辑操作等。

迭代纠正

同人类一样,机器对一个句子进行语法纠错往往也无法一次就找到所有的错误,所以,迭代纠正(Iterative Correction)的思想应运而生。这一方法的主要思想是:对一个含有语病的句子进行多轮纠错,直到评判句子正确程度的某种指标达到指定的阈值。比较典型的一个例子是微软亚洲研究院在2018年提出的Fluency Boosting模型。

修改损失函数

一种更直接的性能提升方式,是修改模型的损失函数

例如:GEC任务中,输出结果的大多数Token与输入文本是相同的,并不重要,而那些产生了差异的Token理应受到更多的关注,所以我们应该提升这些产生差异的Token在损失函数中所占的权重,才能让模型更好地捕捉信息。

数据增强

人工生成的平行语料主要有两种使用方式:1)直接与真实数据集相合并,一起进行训练;2)先使用人工平行语料对模型进行预训练,再将预训练的模型使用真实数据集进行微调。由于人工数据的分布往往与真实数据不一致,所以将人工数据用于预训练阶段能够收获更好的性能,当下绝大多数基于神经网络的GEC模型都采用这一方式。

噪音生成

噪音生成的思想来自于预训练阶段常用的降噪自编码器(DAE)。例如:猿辅导研究院的Wei Zhao等人提出采用随机制造错误数据的方法来构建伪数据,具体流程如下:按照10%的概率随机删除一个词;按照10%的比例随机增加一个词;按照10%的比例随机替换一个词;对所有的词语序号增加一个正态分布,然后对增加正态分布后的词语序号进行重新排序后得到的句子作为错误语句。

噪音生成的具体做法有很多,目前比较好的方法是预先统计真实数据里各类型错误的分布及概率,再根据这一分布生成噪音,从而使人造数据尽可能地接近真实数据地情况。

通过将加入噪音的句子纠正回原本的句子,我们可以以一种无监督的方式对模型进行预训练,这种做法即为降噪自编码器,能有效提升模型性能。(这种方式有些类似PERT的做法,即打乱正常语序的句子而非【MASK】,让语言模型学会重新生成正确的句子)

比赛思路分享

模型

GECToR作为baseline模型,我的方案主要是在赛题的baseline上进行更改,可参考GECToR论文GECToR源代码

backbone则替换为了hfl/chinese-macbert-base

训练说明

该模型训练按GECToR的论文所述,尝试两个stage和三个stage的训练方法,由于验证下来两个stage显著优于只用伪数据训练,而三个stage相对两个stage提升不大,所以选择了两个stage的训练方式。

Stage1

第一个stage先在100w条样本的伪数据上进行训练,将训练得到的在preliminary_val.json上效果最优的权重作为stage2的预训练权重。这里直接将第一个stage训练得到的权重等文件保存在pretrained_model/ch_macbert_base_epoch5,step1,testf1_39_41%,devf1_67_26%,方便stage2的调用。

Stage2

第二个stage使用pretrained_model/ch_macbert_base_epoch5,step1,testf1_39_41%,devf1_67_26%作为预训练权重,使用合并的初赛和决赛数据合并的data/final_train_fusion_stage2_3.json数据集,分为十折来进行训练和验证,最后选取的是验证集表现最好的两组权重平均考虑其预测,生成最后得分Fscore=51.89的提交文件。

调优和trick搜索

trick

在a榜b榜的提交过程中尝试了不同的trick均未有明显提升所以最后没有使用其他trick(尝试过的trick有迭代纠错、使用detect输出判断整句话是否有错,如果最大检错概率小于一定的阈值则认为该句没有出错直接跳过,测试记录可见提交结果记录文档)

backbone

在stage1尝试过roberta-base、macbert-base、pert-base、macbert-large,调优后发现macbert-base效果较好,个人觉得应该是因为macbert预训练就是使用了错字或者span替换等策略和gec中出现最多的错误类似,pert则是使用的语序打乱复原的预训练方式,可能对于乱序的错误的错误更有效果,也有考虑融合不同模型的优势,但由于时间问题没有尝试,但不清楚为什么large大模型反而效果更差,也许是因为没有足够的计算资源尝试lr调优

参考:

ERNIE for CSC:【的、地、得】傻傻分不清?救星来了! - 飞桨AI Studio (baidu.com)

(4 封私信 / 8 条消息) 目前NLP中文文本纠错(错别字检索,修改)有什么研究? - 知乎 (zhihu.com)

中文文本纠错调研 - nghuyong

文本纠错的论文看这一篇就够了 - 知乎 (zhihu.com)

竞赛大神易显维:带你深度认知校对问题_哔哩哔哩_bilibili

语法纠错进展综述 | HillZhang的博客 (gitee.io)

http://fancyerii.github.io/2020/06/15/gector/

paddle&蜜度 文本智能较对大赛经验分享(17/685)的更多相关文章

  1. CHIMA网络安全攻防大赛经验分享

    比赛模式 第一轮:20分钟基础知识赛(50道题) 安全运维,法律法规,linux操作系统等 第二轮:50分钟CTF夺旗(5道题) 题目涵盖 密码学 运用多种工具,如ASCII对照,古典密码,凯撒密码, ...

  2. 使用 paddle来进行文本生成

    paddle 简单介绍 paddle 是百度在2016年9月份开源的深度学习框架. 就我最近体验的感受来说的它具有几大优点: 1. 本身内嵌了许多和实际业务非常贴近的模型比如个性化推荐,情感分析,词向 ...

  3. 微信小程序—智能小蜜(基于智能语义解析olami开放平台)

    概述 该程序支持功能有查天气.查诗词.查百科.算算术.查日历.看笑话.看故事.聊天等,通过用户输入语句智能解析用户意图输出相应答案. 详细 代码下载:http://www.demodashi.com/ ...

  4. 智能生活 “视”不可挡——首届TCL杯HTML5智能电视开发大赛等你来挑战

    http://www.csdn.net/article/2014-06-04/2820063-TCL-Smart-TV-Innovation-Competation

  5. 关于数字化工厂&智能工厂建设 IT 经验总结

    最近疫情闹得胆战心惊,前不久客户给我开了一个玩笑,当天我们同桌会议了一天,晚上客户回家之后就被隔离了,当他给我发这个消息的时候背都凉了一截,害怕之余在机场呆了一个晚上,捅乐鼻孔插了嗓子之后确认无事,后 ...

  6. 【★】Web精彩实战之<智能迷宫>

    JS精彩实战之<智能迷宫>      ---宝贵编程经验分享会--- hello大家好,这里是Web云课堂,之前的一年里我们经历了Html和CSS的系统攻城,此时的你们已经是做静态(动静结 ...

  7. 【★】Web精彩实战之

    JS精彩实战之<智能迷宫>      ---宝贵编程经验分享会--- hello大家好,这里是Web云课堂,之前的一年里我们经历了Html和CSS的系统攻城,此时的你们已经是做静态(动静结 ...

  8. 【ARTS】01_45_左耳听风-201900916~201900922

    ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...

  9. 文本相似度 余弦值相似度算法 VS L氏编辑距离(动态规划)

    设置n为字符串s的长度.("我是个小仙女") 设置m为字符串t的长度.("我不是个小仙女") 如果n等于0,返回m并退出.如果m等于0,返回n并退出.构造两个向 ...

随机推荐

  1. 【Java面试】生产环境服务器变慢,如何诊断处理?

    "生产环境服务器变慢?如何诊断处理" 这是最近一些工作5年以上的粉丝反馈给我的问题,他们去一线大厂面试,都被问到了这一类的问题. 今天给大家分享一下,面试过程中遇到这个问题,我们应 ...

  2. 什么是hive的静态分区和动态分区,它们又有什么区别呢?hive动态分区详解

    面试官问我,什么是hive的静态分区和动态分区,这题我会呀. 简述 分区是hive存放数据的一种方式,将列值作为目录来存放数据,就是一个分区,可以有多列. 这样查询时使用分区列进行过滤,只需根据列值直 ...

  3. Luogu1038 神经网络 (拓扑排序)

    拓扑排序,裸的,水的. 第一发:题读错,输出错,输入错,到处错 \(\longrightarrow\) 40pts (excuse me ?) 第二发:漏了输入层特判 \(\longrightarro ...

  4. Linux 02 基本命令

    参考源 https://www.bilibili.com/video/BV187411y7hF?spm_id_from=333.999.0.0 版本 本文章基于 CentOS 7.6 工具 清屏 cl ...

  5. 线程重用问题--ThreadLocal数据错乱

    前言 复现Java业务开发常见错误100例--1 项目完整代码:Github地址 知识点回顾: ThreadLocal的定义和使用: ThreadLocal概念以及使用场景 配置文件的读取: 获取配置 ...

  6. Excel 运算符(四):引用运算符

    引用运算符用于将单元格区域合并运算,包括:冒号.,逗号. 空格. : 运算 :运算符用于定义一个连续的数据区域,例如"A1:B3",表示从 A1 到 B3 的 6 个单元格. 并集 ...

  7. Python自动化之常用模块学习

    自动化常用模块 urllib和request模块学习笔记 '获取页面,UI自动化校验页面展示作用': #-*- coding : utf-8 -*-import urllib.requestimpor ...

  8. 闭包 与 js内存管理

    参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management            https://blog ...

  9. KingbaseES R6 集群修改物理IP和VIP案例

    在用户的实际环境里,可能有时需要修改主机的IP,这就涉及到集群的配置修改.以下以例子的方式,介绍下KingbaseES R6集群如何修改IP. 一.案例测试环境 操作系统: [KINGBASE@nod ...

  10. Sentinel 源码分析-限流原理

    1. git clone senetinel 源码到本地,切换到release1.8分支 2. 找到FlowQpsDemo.java, 根据sentinel自带的案例来学习sentinel的原理 3. ...