700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 自然语言处理——词性标注实战

自然语言处理——词性标注实战

时间:2022-10-16 18:49:09

相关推荐

自然语言处理——词性标注实战

引言

词性标注即在给定的句子中判定每个单词最合适的词性标记。是自然语言处理的基础。这里用的词性标注模型是ngram模型。

词性标注原理

假设句子S=(w1,w2,w3,⋯,wn)S=(w_1,w_2,w_3,\cdots ,w_n)S=(w1​,w2​,w3​,⋯,wn​),对应每个词的词性标注序列T=(t1,t2,t3,⋯,tn)T=(t_1,t_2,t_3,\cdots ,t_n)T=(t1​,t2​,t3​,⋯,tn​)。

我们要求给定句子SSS得出词性序列TTT的概率p(T∣S)p(T|S)p(T∣S)。

p(T∣S)=p(S∣T)⋅p(T)=p(w1w2⋯wn∣t1t2⋯tn)⋅p(t1t2⋯tn)=∏i=1np(wi∣ti)⋅p(t1)p(t2∣t1)p(t3∣t2)⋯p(tn∣tn−1)p(T|S) = p(S|T) \cdot p(T) \\ = p(w_1w_2\cdots w_n|t_1t_2\cdots t_n) \cdot p(t_1t_2\cdots t_n) \\ = \prod_{i=1}^n p(w_i|t_i) \cdot p(t_1)p(t_2|t_1)p(t_3|t_2)\cdots p(t_n|t_{n-1}) p(T∣S)=p(S∣T)⋅p(T)=p(w1​w2​⋯wn​∣t1​t2​⋯tn​)⋅p(t1​t2​⋯tn​)=i=1∏n​p(wi​∣ti​)⋅p(t1​)p(t2​∣t1​)p(t3​∣t2​)⋯p(tn​∣tn−1​)

其中我们假定p(w1w2⋯wn∣t1t2⋯tn)p(w_1w_2\cdots w_n|t_1t_2\cdots t_n)p(w1​w2​⋯wn​∣t1​t2​⋯tn​)是相互独立的,p(T)p(T)p(T)用到了bigram模型。

我们要求出最好的序列T^\hat TT^。

T^=arg⁡max⁡p(T∣S)=arg⁡max⁡∏i=1np(wi∣ti)⋅p(zi)⋅∏k=2np(tk∣zk−1)=arg⁡max⁡log⁡(∏i=1np(wi∣ti)⋅p(zi)⋅∏k=2np(tk∣zk−1))=arg⁡max⁡∑i=1nlog⁡p(wi∣ti)+log⁡p(ti)+∑k=2np(tk∣tk−1)\hat T = \arg \, \max p(T|S) \\ = \arg \, \max \prod_{i=1}^n p(w_i|t_i) \cdot p(z_i) \cdot \prod_{k=2}^n p(t_k|z_{k-1}) \\ = \arg \, \max \log \left( \prod_{i=1}^n p(w_i|t_i) \cdot p(z_i) \cdot \prod_{k=2}^n p(t_k|z_{k-1}) \right) \\ = \arg \, \max \sum_{i=1}^n \log p(w_i|t_i) + \log p(t_i) + \sum_{k=2}^n p(t_k|t_{k-1}) T^=argmaxp(T∣S)=argmaxi=1∏n​p(wi​∣ti​)⋅p(zi​)⋅k=2∏n​p(tk​∣zk−1​)=argmaxlog(i=1∏n​p(wi​∣ti​)⋅p(zi​)⋅k=2∏n​p(tk​∣zk−1​))=argmaxi=1∑n​logp(wi​∣ti​)+logp(ti​)+k=2∑n​p(tk​∣tk−1​)

我们要计算出三个概率:p(wi∣ti),p(ti),p(tk∣tk−1)p(w_i|t_i),p(t_i),p(t_k|t_{k-1})p(wi​∣ti​),p(ti​),p(tk​∣tk−1​),分别记为A,π,BA,\pi,BA,π,B。

我们可以看成有三个参数A,π,BA,\pi,BA,π,B。当我们求出这个三个参数后,就可以使用维特比算法来求出最大的T^\hat TT^。

我们先来看下AAA,它可以表示为一个矩阵。行是词性数量,列是单词数。

假设上面有一行向量代表动词(vb),表示每个单词是动词的概率。上面这个矩阵我们可以通过语料库计算出来。

下面来看下π\piπ,即p(ti)p(t_i)p(ti​),它表示每个词性出现在句首的概率。显示用一个向量就可以表示,向量的大小就是词性数量。

剩下的BBB可以用状态转移矩阵来表示,所谓状态转移,这里指的是一个词性后面接另一个词性。

BBB的大小是N×NN \times NN×N。

计算这个BBB和计算语言模型的Bigram是一样的。

Newsweek/NNP,/,trying/VBGto/TOkeep/VBpace/NNwith/INrival/JJTime/NNPmagazine/NN

上面是我们语料库中一句话,左边是单词,右边是词性。

训练数据下载地址: /download/yjw123456/12776403

把上面这句话转换成:

NNP,VBG,TO,VB,NN,IN,JJ,NNP,NN

就可以计算这个词性的Bigram。

下面就通过我们的语料库来统计下π,A,B\pi,A,Bπ,A,B。

代码实现

这里我们认为英文句号"."代表一个句子结束。它后面第一个单词/词性代表句首。

首先我们用4个字典来保存词性、单词与索引相关信息

# 词性到索引 以及 索引到词性的字典tag2id, id2tag = {},{} # 单词到索引,索引到单词word2id,id2word = {}, {}# 读取训练数据for line ine open(./data/traindata.txt):items = line.split('/')# 单词/词性word, tag = items[0],items[1].strip() #去掉换行符 if word not in word2id:word2id[word] = len(word2id)id2word[len(id2word)] = wordif tag not in tag2id:tag2id[tag] = len(tag2id)tag2id[len(tag2id)] = tagM = len(word2id) #单词数量N = len(tag2id) #词性数量

下面我们要计算π,A,B\pi,A,Bπ,A,B。

import numpy as nppi =np.zeros(N)#每个词性出现在句首的概率A = np.zeros((N,M))#A[i][j]表示 给定词性 i,出现单词j的概率B = np.zeros((N,N)) #B[i][j],词性i后面接j的概率# 构建 π,A,Bpre_tag = ''#前一个词性for line in open('./data/traindata.txt'):items = line.split('/')# 单词索引和词性索引wordId,tagId = word2id[items[0]],tag2id[items[1].rstrip()]A[tagId][wordId] += 1#tag下面 单词出现的次数 加1if pre_tag == '': #表示句首# 需要计算 πpi[tagId] += 1 #先统计次数,后面再除以总数else: #不是句首B[tag2id[pre_tag]][tagId] += 1pre_tag = items[1].rstrip()if items[0] == '.': #表示后面就是句首了,要清空pre_tagpre_tag = ''# 把次数转换为概率pi = pi / sum(pi)for i in range(N):A[i] /= sum(A[i])B[i] /= sum(B[i])

参数都计算出来了。下面就可以计算给定一句话的词性了。

“Newsweek(NNP) said(VBD) it(PRP) will(MD) introduce(VB) the(DT)”。

假定我们的句子是这样的,下面我们要求出每个单词的词性。

我们列出每个单词和每一种词性:

我们要从中找出一个路径,假定上图标出的绿色路径为r1r_1r1​,我们要如何计算这条路径的得分呢。

用到下面的公式就可以

T^=arg⁡max⁡∑i=1nlog⁡p(wi∣ti)+log⁡p(ti)+∑k=2np(tk∣tk−1)\hat T = \arg \, \max \sum_{i=1}^n \log p(w_i|t_i) + \log p(t_i) + \sum_{k=2}^n p(t_k|t_{k-1}) T^=argmaxi=1∑n​logp(wi​∣ti​)+logp(ti​)+k=2∑n​p(tk​∣tk−1​)

下面我们用动态规划(维特比算法)的方式来求出最好的路径,定义dp[i,j](i,j分别代表列和行)表示到从起点w1w_1w1​到这个矩阵中dp[i,j]位置最短的路径。

那如何计算dp[i,j]呢,假设它是从词性nnp过来的:

dp[i,j] = dp[i-1,0] + log p(dt|nnp) + log p(w_i|dt)

可以这样表示出从每个词性过来的公式,我们最终选择的就是整个式子得分最大的那个。

好,下面用代码实现上面的过程。

import numpy as npdef log(v):if v == 0:return np.log(v + 0.000001)return np.log(v)def viterbi(x,pi,A,B):'''x : 句子pi : tag出现在句首的概率A: A[i][j]表示 给定词性 i,出现单词j的概率B: B[i][j],词性i后面接j的概率'''# 简单的对应英文进行分词,并转换为词典中的索引x = [word2id[word] for word in x.split(" ")]T = len(x) #句子长度dp = np.zeros((T,N)) # dp[i][j] 代表w1 到wi,并假设wi的tag是第j个# 采用动态规划的方式,由左到右填充dp矩阵,先填充第一列,就是某个词性作为句首的情况,因此用到的是pifor j in range(N):# j 作为tag的概率 加上 j是tag的情况下,看到单词x[0]的概率(A)dp[0][j] = log(pi[j]) + log(A[j][x[0]])# 记录路径 保存到当前词性的最佳词性path = np.array([[0 for x in range(N)] for y in range(T)]) # T x Nfor i in range(1,T): #从第2个单词开始for j in range(N): #遍历每个词性# 目前词性为jdp[i][j] = -float('inf')for k in range(N): #从词性k到达词性jscore = dp[i-1][k] + log(B[k][j]) + log(A[j][x[i]])if score > dp[i][j]:dp[i][j] = scorepath[i][j] = k# 把最好的路径打印出来best_tag_seq = [0] * T# 从后往前求# 找出最后一个单词的词性best_tag_seq[T-1] = np.argmax(dp[T-1]) #最后一列,值最大的对应的索引# 通过从后到前的循环来求出依次每个单词的词性for i in range(T-2,-1,-1):# 第i个单词最好的词性 保存到了i+1个单词上best_tag_seq[i] = path[i+1][best_tag_seq[i+1]]for i in range(len(best_tag_seq)):print(id2tag[best_tag_seq[i]])

然后我们用训练数据中的一个句子测试一下:

x = 'Social Security number , passport number and details about the services provided for the payment'viterbi(x,pi,A,B)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。