自然语言处理 (分词
Assignment #1自我介绍问题重述实验结果声明分词原理隐式马尔可夫模型HMM + Viterbi结构化感知器SP实验实验环境实验数据背景描述数据说明与来源自我介绍我叫姓名,本人性格开朗具亲和力,乐观耿直,诚实守信,有良好的心理素质,环境适应性强,有吃苫的精神,做事有毅力,喜欢挑战,行事积极认真富有职责心;能够注意统筹安排。进行有效的自我管理;注重团队协作,善于沟通协调;学习本事强,并喜欢学
自我介绍
我叫姓名,本人性格开朗具亲和力,乐观耿直,诚实守信,有良好的心理素质,环境适应性强,有吃苫的精神,做事有毅力,喜欢挑战,行事积极认真富有职责心;能够注意统筹安排。进行有效的自我管理;注重团队协作,善于沟通协调;学习本事强,并喜欢学习和理解新事物,深信有耕耘就会有收获,但在某些方面存在一些自负的情绪。凭着年轻,我会不懈努力,让自我做得更好。给我一个舞台,我会给你满意的表现。
相信年轻,相信自我!您的信任与我的实力将为我们带来共同的成功!诚实守信,待人处事热情大方,性格开朗,能很快理解新事物,富有创新和开拓意识,勇于挑战自我,有较强的时间观念和职责心,善于思考,虚心向学,并始终坚特严于律己,宽以待人,懂得“若要人敬己,先要己敬人”的道理,良好的人际关系和丰富的专业知识为我的成功之路作了铺垫。
问题重述
本实验主要完成的任务是将一段自我介绍,做自然语言处理中第一步分词的工作。
实验结果
在这一部分中采用了两种方法,第一种是结构化感知器SP,第二种是隐式马尔可夫模型HMM + Viterbi。结果如下表:
way | Precision | Recall | F1 | OOV Recall | IV Recall |
---|---|---|---|---|---|
thulac(SP) | 0.806 | 0.657 | 0.724 | 0.222 | 0.922 |
jieba(HMM + Viterbi) | 0.909 | 0.855 | 0.88 | 0.5 | 0.842 |
结果不能作为评判两个模型的优劣,数据存在偏差行,结果仅供参考
声明
本文参考jieba、thulac 官方github内容
相关论文
- Zhongguo Li, Maosong Sun. Punctuation as Implicit Annotations for Chinese Word Segmentation. Computational Linguistics, vol. 35, no. 4, pp. 505-512, 2009.
相关知乎
- https://zhuanlan.zhihu.com/p/66904318
分词原理
隐式马尔可夫模型HMM + Viterbi
本文中采用jieba工具包来实现该模型。
官方介绍如下:
- 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)
- 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
- 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法
在提供知乎中有一个例子这里没有进行搬迁。
该工具包中,Viterbi假设每个词相互独立,则分词组合的联合概率:
p ( c 1 n ) = p ( w 1 m ) = ∏ i p ( w i ) p(c_1^n) = p(w_1^m)=\prod_ip(w_i) p(c1n)=p(w1m)=i∏p(wi)
在Viterbi分词后用HMM做未登录词识别,以修正分词结果。
在进行实际运算的时候,
下面展示一些包内源代码
。
def cut_for_search(sentence, HMM=True):
"""
Finer segmentation for search engines.
"""
words = cut(sentence, HMM=HMM)
for w in words:
if len(w) > 2:
for i in xrange(len(w) - 1):
gram2 = w[i:i + 2]
if FREQ.get(gram2):
yield gram2
if len(w) > 3:
for i in xrange(len(w) - 2):
gram3 = w[i:i + 3]
if FREQ.get(gram3):
yield gram3
yield w
在拿到句子之后一次进行循环滚动读取出出现在前缀词典中的二元子词;对于长度大于3的词,依次循环滚动取出在前缀词典中的三元子词。在建立检索词典采用的是建立树的方法进行实现,决定词在树中的位置采用的是通过词频的方式。包内源代码
也有展现
def gen_trie(f_name):
lfreq = {}
trie = {}
ltotal = 0.0
with open(f_name, 'rb') as f:
lineno = 0
for line in f.read().rstrip().decode('utf-8').split('\n'):
lineno += 1
try:
word,freq,_ = line.split(' ')
freq = float(freq)
lfreq[word] = freq
ltotal+=freq
p = trie
for c in word:
if c not in p:
p[c] ={}
p = p[c]
p['']='' #ending flag
except ValueError, e:
logger.debug('%s at line %s %s' % (f_name, lineno, line))
raise ValueError, e
return trie, lfreq, ltotal
一个句子所有的分词组合构成了有向无环图(Directed Acyclic Graph, DAG)𝐺=(𝑉,𝐸),一个词对应与DAG中的的一条边𝑒∈𝐸,边的起点为词的初始字符,边的结点为词的结束字符。
a
r
g
m
a
x
∏
i
p
(
w
i
)
=
a
r
g
m
a
x
l
o
g
∏
i
p
(
w
i
)
=
a
r
g
m
a
x
∑
i
l
o
g
p
(
w
i
)
arg max\prod_ip(w_i) = arg maxlog\prod_ip(w_i)=argmax\sum_ilogp(w_i)
argmaxi∏p(wi)=argmaxlogi∏p(wi)=argmaxi∑logp(wi)
对于Viterbi模型下的联合概率求对数,将词频的log值作为图G的权重,从图论的角度看,这就是将最大概率模型变成最大路径问题,Jieba用动态规划(DP)来求解最大路径问题。
结构化感知器SP
本文采用的c实现来结构化感知机SP。
SP与条件随机场不同的是以最大熵准则建模score函数,𝑋为输入序列,Y为标记序列
S
(
X
,
Y
)
=
∑
s
α
s
Φ
s
(
Y
,
X
)
S(X,Y)=\sum_s\alpha_s Φ_s(Y,X)
S(X,Y)=s∑αsΦs(Y,X)
其中 Φ𝑠(𝑌,𝑋)为本地特征函数𝜙𝑠(ℎ𝑖,𝑦𝑖)的全局化表示:
Φ
s
(
Y
,
X
)
=
∑
i
ϕ
s
(
h
i
,
y
i
)
Φ_s(Y,X) = \sum_i\phi_s(h_i,y_i)
Φs(Y,X)=i∑ϕs(hi,yi)
即问题求S(Y,X)的最大值。为避免模型过拟合,每一次更新心权重,都将会求其平均值,算法流程如下图:
在将SP应用于中文分词时,除了事先定义的特征模板外,还用用到一个状态转移特征(𝑦𝑡−1,𝑦𝑡)。记在时刻𝑡的状态为𝑦的路径𝑦𝑡所对应的score函数最大值为:
δ
t
(
y
)
=
m
a
x
S
(
y
1
t
−
1
,
X
,
y
t
=
y
)
\delta_t(y) =maxS(y_1^t-1,X,y_t=y)
δt(y)=maxS(y1t−1,X,yt=y)
那么t+1时刻
δ
t
+
1
(
y
)
=
m
a
x
y
′
[
δ
t
+
w
y
′
,
y
+
F
(
y
t
+
1
=
y
,
X
)
]
\delta_{t+1}(y) =max_{y'}{[\delta_t+w_{y',y}+F(y_{t+1}=y,X)]}
δt+1(y)=maxy′[δt+wy′,y+F(yt+1=y,X)]
其中,𝑤𝑦′,𝑦为转移特征(𝑦′,𝑦)所对应的权值,𝐹(𝑦𝑡+1=𝑦,𝑋)为𝑦𝑡+1=𝑦对应的特征模板的特征值的加权之和。
在原论文中提供了一个相关的例子。
相关数据切分源代码
def __cutWithOutMethod(self, oiraw, cut_method, text = True):
'''分词,先将原始句子split为一个数组,之后遍历每一行,调用对单行分词的函数(有两种)。
text=True会返回分词好的字符串,为False则会返回一个二位数组方便用户做后续处理。
函数中有一些细节处理主要是用于规范输出格式'''
oiraw = oiraw.split('\n')
txt = ""
array = []
if(text):
for line in oiraw:
if(self.__seg_only):
temp_txt = reduce(lambda x, y: x + ' ' + y if y != " " else x, cut_method(line), '') + '\n'
else:
temp_txt = reduce(lambda x, y: x + ' ' + "".join(y), cut_method(line), '') + '\n'
txt += temp_txt[1:]
return txt[:-1]
else:
for line in oiraw:
if(line):
if(self.__seg_only):
array += (reduce(lambda x, y: x + [[y, '']], cut_method(line), []))
else:
array += (reduce(lambda x, y: x + [[y[0], y[2]]], cut_method(line), []))
array += [['\n', '']]
return array[:-1]
实验
实验环境
在Mac os 下 pycharm + anaconda python = 3.7
实验数据
实验数据采用的是2014人民日报标注语料库,数据将会上传
背景描述
PFR人民日报标注语料库(版本1.0,下面简称PFR语料库)是在得到人民日报社新闻信息中心许可的条件下,以1998年人民日报语料为对象,由北京大学计算语言学研究所和富士通研究开发中心有限公司共同制作的标注语料库。该语料库对600多万字节的中文文章进行了分词及词性标注,其被作为原始数据应用于大量的研究和论文中。
数据说明与来源
2014语料库规模比1998年人民日报语料库大,98版本为人工标注,14版本在精度上相比要差一些。数据来自北京大学计算语言学研究所。
更多推荐
所有评论(0)