分词
常用的分词工具
Jiaba, SnowNLP, LTP, HanNLP
import jieba
seg_list = jieba.cut('张三学院专注于人工智能教育', cut_all=False)
print('Default Mode: ' + ' / '.join(seg_list))
jieba.add_word('张三学院')
seg_list = jieba.cut('张三学院专注于人工智能教育', cut_all=False)
print('Default Mode: ' + ' / '.join(seg_list))
分词方法
最大匹配算法
希望单词的长度最长, 则分成的词语最少. 贪心算法.
最大匹配算法的前向算法
sentence = '我们经常有意见分歧'
dictionary = ['我们', '经常', '有', '有意见', '意见', '分歧']
max_len = 5 # 单词的最大长度
words = []
left = 0
while left < len(sentence):
right = left + 5 if left + 5 <= len(sentence) else len(sentence)
while right > left:
if sentence[left:right] in dictionary:
words.append(sentence[left:right])
left = right
break
right -= 1
考虑语义的分词方法
上图所说的工具就是语言模型.
最简单的语言模型是Uni-gram, 各个单词独立.
维特比算法
例子 “经常有意见分歧”
词典 | 经常 | 经 | 有 | 有意见 | 意见 | 分歧 | 见 | 意 | 见分歧 | 分 | 其它 |
---|---|---|---|---|---|---|---|---|---|---|---|
概率 p p p | 0.1 0.1 0.1 | 0.05 0.05 0.05 | 0.1 0.1 0.1 | 0.1 0.1 0.1 | 0.2 0.2 0.2 | 0.2 0.2 0.2 | 0.2 0.2 0.2 | 0.05 0.05 0.05 | 0.05 0.05 0.05 | 0.1 0.1 0.1 | 1 0 − 8 10^{-8} 10−8 |
− log p -\log p −logp | 2.3 2.3 2.3 | 3 3 3 | 2.3 2.3 2.3 | 2.3 2.3 2.3 | 1.6 1.6 1.6 | 1.6 1.6 1.6 | 3 3 3 | 3 3 3 | 3 3 3 | 2.3 2.3 2.3 | 18.4 18.4 18.4 |
找概率 p p p之积最大的 = = = 找 − log p -\log p −logp之和最小的路径
定义 f ( n ) \mathop{f}(n) f(n)是从节点 1 1 1到节点 n n n的最短路径, 则 f ( 8 ) = min { f ( 5 ) + 3 , f ( 6 ) + 1.6 , f ( 7 ) + 18.4 } f(8)=\min \{f(5)+3, f(6)+1.6, f(7)+18.4\} f(8)=min{f(5)+3,f(6)+1.6,f(7)+18.4}
建立一个数组, 保存 f ( 1 ) … f ( 8 ) f(1)\dots f(8) f(1)…f(8): f ( 1 ) = 0 f(1)=0 f(1)=0, f ( 2 ) = f ( 1 ) + 3 = 3 f(2)=f(1)+3=3 f(2)=f(1)+3=3
拼写错误纠正
Spell Correction
编辑距离
1. 旧方法: 计算编辑距离
用动态规划解决
str1 = "their"
str2 = "thsi"
distances = [[0 for j in range(len(str2) + 1)] for i in range(len(str1) + 1)]
用 distances[i,j]
表示 str1[:i]
和 str2[:j]
的(最短)编辑距离
设替换的距离是2, 插入和删除的距离都是1
for i in range(len(str1)):
for j in range(len(str2)):
if str1[i] == str2[j]:
distances[i + 1][j + 1] = distances[i][j]
else:
distances[i + 1][j + 1] = min([
distances[i + 1][j] + 1, # 字符串1删除一个字符
distances[i][j + 1] + 1, # 字符串1插入一个字符
distances[i][j] + 2 # 字符串1的最后1个字符被替换
])
print(distances[len(str1)][len(str2)])
缺点是, 需要遍历词典中所有的词, 才能找到距离最短的词.
2. 新方法: 生成单词并过滤
距离为1和2字符串的生成:
- 通过插入/删除/替换1个字符生成编辑距离为1字符串
- 通过嵌套循环生成距离为2的字符串
字符串过滤:
假设后台知道用户把单词写错成各种形式的概率
设 p ( s ∣ c ) p(\bold{s}|\bold{c}) p(s∣c)是将正确单词 c \bold{c} c写成错误单词 s \bold{s} s的概率, 设 p ( c ) p(\bold{c}) p(c)是正确单词 c \bold{c} c在词典中出现的概率,
则 p ( s ∣ c ) p ( c ) = p ( c , s ) ∝ p ( c ∣ s ) p(\bold{s}|\bold{c})p(\bold{c})=p(\bold{c},\bold{s})\propto p(\bold{c}|\bold{s}) p(s∣c)p(c)=p(c,s)∝p(c∣s)
p ( c ∣ s ) p(\bold{c}|\bold{s}) p(c∣s)是错误单词 s \bold{s} s是正确单词 c \bold{c} c的概率
停用词过滤
删除停用词和出现频率很低的词
删除停用词要考虑应用场景.
NLTK停用词库
词的标准化操作
概念
Stemming
went, go, going → go
fly, flies → fli
deny, denied → deni
类似于都变成原型, 但是原型不一定是词典中的词
Lemmazation
比Stemming要求更严, 必须是词典中的词
Porter Stemmer
定义了许多规则, 比如
- step 1a
sess -> ss
ies -> i
ss -> ss
s -> $\Phi$
- step 1b
(*v*)ing -> $\Phi$
(*v*)ed -> $\Phi$
- step 2 (for long stems)
ational -> ate
izer -> ize
ator -> ate
- step 3 (for longer stems)
al -> $\Phi$
able -> $\Phi$
ate -> $\Phi$