[nlp] 小傻学数据预处理
1. 分词
1.1 分词方法
1.1.1 基于规格(词典、词库)
基于字符串匹配分词。将待分的字符串与一个充分大的机器词典中的词条进行匹配。
正向最大匹配: 对输入的句子从左至右,以贪心的方式切分出当前位置上长度最大的词,组不了词的字单独划开。其分词原理是:词的颗粒度越大,所能表示的含义越精确。
逆向最大匹配: 原理与正向最大匹配相同,但顺序不是从首字开始,而是从末字开始,而且它使用的分词词典是逆序词典,其中每个词条都按逆序方式存放。在实际处理时,先将句子进行倒排处理,生成逆序句子,然后根据逆序词典,对逆序句子用正向最大匹配。
双向最大匹配: 将正向最大匹配与逆向最大匹配组合起来,对句子使用这两种方式进行扫描切分,如果两种分词方法得到的匹配结果相同,则认为分词正确,否则,选取分词结果中单个汉字数目较少的那一组。
小结: 基于词典的分词方法简单、速度快,效果也还可以,但对歧义和新词的处理不是很好,对词典中未登录的词没法进行处理。
1.1.2 基于知识理解
该方法主要**基于句法、语法分析,并结合语义分析,通过对上下文内容所提供信息的分析对词进行定界,**它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断。这类方法试图让机器具有人类的理解能力,需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式。因此目前基于知识的分词系统还处在试验阶段。
1.1.3 基于(词频)统计
从大量已经分词的文本中,利用统计学习方法来学习词的切分规律,从而实现对未知文本的切分。随着大规模语料库的建立,基于统计的分词方法不断受到研究和发展,渐渐成为了主流。
常用的统计学习方法有:隐马尔可夫模型(HMM)、条件随机场(CRF)和基于深度学习的方法。
jieba是基于统计的分词方法,jieba分词采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合,对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法。
**小结:**基于统计的分词方法能很好地处理歧义和新词问题,效果比基于词典的要好,但该方法需要有大量人工标注分好词的语料作为支撑,训练开销大,就分词速度而言不如前一种。
1.2 分词工具
Jieba分词 https://github.com/fxsjy/jieba
SnowNLP https://github.com/isnowfy/snownlp
LTP http://www.ltp-cloud.com/
HanNLP https://github.com/hankcs/HanLP/
1.3 jieba分词原理
jieba分词项目地址
**原理:**基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (Directed Acyclic Graph,DAG);采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合;
对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法。
1.3.1 前缀词典
结巴分词首先会依照结巴默认的统计词典dict.txt构造前缀词典。
dict.txt含有近35万的词条,每个词条占用一行,其中每一行有3列,第一列为词条,第二列为对应的词频,第三列为词性,构造前缀词典需要用到前两列。
具体做法为:首先定义一个空的python字典,然后遍历dict.txt的每一行,取词条作为字典的键,词频作为对应的键值,然后遍历该词条的前缀。如果前缀对应的键不在字典里,就把该前缀设为字典新的键,对应的键值设为0,如果前缀在字典里,则什么都不做。
这样等遍历完dict.txt后,前缀词典就构造好了。在构造前缀词典时,会对统计词典里所有词条的词频做一下累加,累加值等计算最大概率路径时会用到。
1.3.2 有向无环图生成
基于前缀词典,对输入文本进行切分;e.g.:
对于‘去’:没有前缀,只有一种划分方式;
对于‘北’:有‘北’,‘北京’,‘北京大学’三种划分方式;
对于‘大’:有‘大’,‘大学’两种划分方式……
依次类推,可以得到每个字开始大前缀词的划分方式。
对于0: [0],表示位置0对应的词,就是0 ~ 0,就是“去”;对于1: [1,2,4],表示位置1开始,在1,2,4位置都是词,就是1 ~ 1,1 ~ 2,1 ~ 4,即“北”,“北京”,“北京大学”这三个词。
对于每一种划分,都将相应的首尾位置相连,例如,对于位置1,可以将它与位置1、位置2、位置4相连接,最终构成一个有向无环图。
1.3.3 计算概率最大路径
在得到所有坑的切分方式构成的有向无环图后,其从起点到终点有多条路径(即多个分词结果);我们需要计算出最大概率路径,即按照这种方式切分后的分词结果的概率最大。
采用维特比算法进行计算。
对未登录词采用HMM模型进行分词
当出现没有在前缀词典里收录的词时,会采用HMM模型进行分词
结巴分词已经内置了训练好的状态初始概率、状态转移概率和状态发射概率。
句子会作为观测序列,当有新句子进来时,具体做法为:先通过Viterbi算法求出概率最大的状态序列,然后基于状态序列输出分词结果(每个字的状态为B、M、E、S之一)。(B, M, E, S): {B:begin, M:middle, E:end, S:single}
如果切出了词典中没有的词语,效果不理想,结巴里也可以关闭新词发现。
2 文本纠错
纠错是一个重要又不重要的领域。 不纠错下游工作其实也能进行,只是会影响效果、体验(例如word2vec之类大样本训练任务)。但有时候用户体验直接影响收入(如电商搜索,输入法,校对等,此时纠错又显得比较重要。)
纠错相对更偏系统工程(经常是其它nlp任务的上游,对响应速度要求较高)
2.1 why?
2.1.1
纠错场景:
输入法(拼音、英文)
通用搜索(百度、搜狗)
垂类搜索(电商、社区、图片)
智能问答
训练语料清洗
基本上凡是涉及用户输入的场景(nlp)都需要纠错
why:
文本输入(输入法): 直接影响输入体验
语义理解(搜索、对话): 直接影响(商品、候选回答等的)召回效果
数据预处理(训练样本,数仓):过脏的数据无法用来做分析和训练,虽然可以假设正确样本占多数,
校对(出版社):直接影响用户阅读理解
纠错常与分词一起,具体谁在前不一定;有分词纠错同时进行的
英文NER可以在纠错之前,中文NER一般在纠错之后
中文较少因为纠错让实体词变为非实体词
2.2 英文纠错
2.2.1 non-word, 不再词典中
编辑距离
2.2.2 real-word,用词错误
处理方法与中文相同
2.2.3 编辑距离(edit distance)
一个字符串需经过多少次变化后变为另外一个字符串,工业上一般编辑距离不超过2。
出错场景:
- 少输:hello->hllo(ed=1), hlo(ed=2)
- 多输:hello->helloo, (ed=1)
- 错输:hello->hallo, (ed=1)
- 顺序不对:hello->ehllo (ed=1or2)
处理方法:
输入字符串=s
触发条件:oov or unigram词频太低
候选词(方法一)
先尝试ed=1,
少字符(s的每个位置加入a-z)
多字符(删除s的每个字符)
错字符(把s的每个字符用a-z代替,可以基于字符位置(26键)做过滤)
顺序错(把s相临的每两个字符交换)
再试ed=2
把ed=1的每个字符串再遍历一遍ed=1的处理
不在词表的去掉
基于unigram词频去掉值太低的
BK-Tree
成立条件:
d(x,y)=0 当且仅当x=y
d(x,y)=d(y,x)
d(x,y)+d(y,z)>=d(x,z)
d(x,z)=a, d(x,y)=b, 求f=d(y,z)的范围
d(x,z)+d(z,y) >= d(x,y) => f>=b-a
d(y,x)+d(x,z) >= d(y,z) => f<=b+a
a=搜索范围、b=query与节点的距离、f=节点与子节点的距离
构造
以任一单词作为根
1. 插入时先计算与该子树节点的距离
2. 插入到该子树对应子节点下
3. 循环1、2直到成为叶子节点
查询
求与query编辑距离<=a的单词
计算query与子树根节点的距离
然后往下找出距离=[b-a,b+a]之间的子树
b=query与节点的距离
逐层往下最终到叶子节点
当a较小时有价值,较大时与遍历区别不大
候选词选择
尝试纠错
合并召回统一基于unigram排序
ed=1扣积分,ed=2扣更多分
输入为句子
计算整句但概率(ppl or lm(2gram or lstm))
选择概率最大的那个词,同样需要针对ed做降权
排序权重学习
词频、纠错排序等
用户有显式返回场景(输入法,补全框)
样本稀疏,一般针对人群
2.3 噪声通道模型
p(I)就是语言模型,类似hmm的发射概率
𝑝(𝑤𝑜𝑟𝑑│𝑒𝑟𝑟)=(𝑝(𝑒𝑟𝑟│𝑤𝑜𝑟𝑑)𝑝(𝑤𝑜𝑟𝑑))/𝑝(𝑒𝑟𝑟)
直接统计p(wrd|err)也是可以的
朴素贝叶斯𝑝(𝑐│𝑋)的X是多个样本(词),没法直接统计结果
err过于稀疏
p(wrd)本来已有,所以统计p(err|wrd)并不比p(wrd|err)费力,p(x)计算相对值不需要
常见用法
召回,基于先验代替p(err|word)
编辑距离(拼音混淆、方言等)
排序
用LM做排序
因为每种错误的频率过低,概率不好统计
可以混合使用
对高频错误计算p(wrd|err)
先要正常搜索候选,再根据ed+p(wrd|err)排序