当前位置:   article > 正文

自然语言处理——词性标注实战_nlp词性标注实战traindata.txt

nlp词性标注实战traindata.txt

引言

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

词性标注原理

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

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

p ( T ∣ S ) = p ( S ∣ T ) ⋅ p ( T ) = p ( w 1 w 2 ⋯ w n ∣ t 1 t 2 ⋯ t n ) ⋅ p ( t 1 t 2 ⋯ t n ) = ∏ i = 1 n p ( w i ∣ t i ) ⋅ p ( t 1 ) p ( t 2 ∣ t 1 ) p ( t 3 ∣ t 2 ) ⋯ p ( t n ∣ t n − 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(TS)=p(ST)p(T)=p(w1w2wnt1t2tn)p(t1t2tn)=i=1np(witi)p(t1)p(t2t1)p(t3t2)p(tntn1)

其中我们假定 p ( w 1 w 2 ⋯ w n ∣ t 1 t 2 ⋯ t n ) p(w_1w_2\cdots w_n|t_1t_2\cdots t_n) p(w1w2wnt1t2tn)是相互独立的, p ( T ) p(T) p(T)用到了bigram模型。

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

T ^ = arg ⁡   max ⁡ p ( T ∣ S ) = arg ⁡   max ⁡ ∏ i = 1 n p ( w i ∣ t i ) ⋅ p ( z i ) ⋅ ∏ k = 2 n p ( t k ∣ z k − 1 ) = arg ⁡   max ⁡ log ⁡ ( ∏ i = 1 n p ( w i ∣ t i ) ⋅ p ( z i ) ⋅ ∏ k = 2 n p ( t k ∣ z k − 1 ) ) = arg ⁡   max ⁡ ∑ i = 1 n log ⁡ p ( w i ∣ t i ) + log ⁡ p ( t i ) + ∑ k = 2 n p ( t k ∣ t k − 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(TS)=argmaxi=1np(witi)p(zi)k=2np(tkzk1)=argmaxlog(i=1np(witi)p(zi)k=2np(tkzk1))=argmaxi=1nlogp(witi)+logp(ti)+k=2np(tktk1)

我们要计算出三个概率: p ( w i ∣ t i ) , p ( t i ) , p ( t k ∣ t k − 1 ) p(w_i|t_i),p(t_i),p(t_k|t_{k-1}) p(witi),p(ti),p(tktk1),分别记为 A , π , B A,\pi,B A,π,B

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

我们先来看下 A A A,它可以表示为一个矩阵。行是词性数量,列是单词数。
在这里插入图片描述
假设上面有一行向量代表动词(vb),表示每个单词是动词的概率。上面这个矩阵我们可以通过语料库计算出来。

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

在这里插入图片描述

剩下的 B B B可以用状态转移矩阵来表示,所谓状态转移,这里指的是一个词性后面接另一个词性。
B B B的大小是 N × N N \times N N×N

在这里插入图片描述
计算这个 B B B和计算语言模型的Bigram是一样的。

Newsweek/NNP
,/,
trying/VBG
to/TO
keep/VB
pace/NN
with/IN
rival/JJ
Time/NNP
magazine/NN
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

训练数据下载地址: https://download.csdn.net/download/yjw123456/12776403

把上面这句话转换成:

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

就可以计算这个词性的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)] = word
    if tag not in tag2id:
        tag2id[tag] = len(tag2id)
        tag2id[len(tag2id)] = tag
        
M = len(word2id) #单词数量
N = len(tag2id) #词性数量
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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

import numpy as np

pi =np.zeros(N)#每个词性出现在句首的概率
A = np.zeros((N,M))#A[i][j]表示 给定词性 i,出现单词j的概率
B = np.zeros((N,N)) #B[i][j],词性i后面接j的概率

# 构建 π,A,B

pre_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下面 单词出现的次数 加1

    if pre_tag == '': #表示句首
        # 需要计算 π
        pi[tagId] += 1 #先统计次数,后面再除以总数
    else: #不是句首
        B[tag2id[pre_tag]][tagId] += 1
    
    pre_tag = items[1].rstrip()
    if items[0] == '.': #表示后面就是句首了,要清空pre_tag
        pre_tag = ''

# 把次数转换为概率
pi = pi / sum(pi)

for i in range(N):
    A[i] /= sum(A[i])
    B[i] /= sum(B[i])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

参数都计算出来了。下面就可以计算给定一句话的词性了。
在这里插入图片描述
“Newsweek(NNP) said(VBD) it(PRP) will(MD) introduce(VB) the(DT)”。
假定我们的句子是这样的,下面我们要求出每个单词的词性。

我们列出每个单词和每一种词性:
在这里插入图片描述

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

用到下面的公式就可以

T ^ = arg ⁡   max ⁡ ∑ i = 1 n log ⁡ p ( w i ∣ t i ) + log ⁡ p ( t i ) + ∑ k = 2 n p ( t k ∣ t k − 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=1nlogp(witi)+logp(ti)+k=2np(tktk1)

在这里插入图片描述
下面我们用动态规划(维特比算法)的方式来求出最好的路径,定义dp[i,j](i,j分别代表列和行)表示到从起点 w 1 w_1 w1到这个矩阵中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 np

def 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矩阵,先填充第一列,就是某个词性作为句首的情况,因此用到的是pi
    for 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 N
    
    
    for i in range(1,T): #从第2个单词开始
        for j in range(N): #遍历每个词性
            # 目前词性为j
            dp[i][j] = -float('inf')
            for k in range(N): #从词性k到达词性j
                score = dp[i-1][k] + log(B[k][j]) + log(A[j][x[i]])
                if score > dp[i][j]:
                    dp[i][j] = score
                    path[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]])
        
        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

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

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

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/599258
推荐阅读
相关标签
  

闽ICP备14008679号