1. 程式人生 > 實用技巧 >NLP學習筆記:詞性標註

NLP學習筆記:詞性標註

任務目標:通過已有的訓練資料,將每個單詞的詞性標記出來。

知識儲備:

  1.計算語言模型

    思路:假設每句話經過分詞表示為 (w1,w2,w3,...wi )對應的每個單詞的詞性記為(z1,z2,z3,...zi)

       求則語言模型z = P(w1,w2,w3,...wi | z1,z2,z3,...zi)*P(z1,z2,z3,...zi)

               = [ P(wi |zi)(i從0到n的連乘)] *P(z1)* [ P(zt | z t-1)(t從2到n的連乘)]

               = 取各個數的log值方便計算。

       假定 :訓練樣本的單詞數為M ,詞性種類為N

       記pi =log (P(z1)) 表示詞性tag出現在句子開頭的概率 長度為N的陣列

       A =[ P(wi |zi)(i從0到n的連乘)] 表示單詞wi為詞性zi的概率 表示N*M的矩陣

        B =[ P(zt | z t-1)(t從2到n的連乘)] 表示詞性 zt-1的下一個詞的詞性為zt的概率 N*N的矩陣

  2.維特比演算法

    第一步已經計算出了各個單詞與詞性的關係,下一步就需要根絕維特比演算法計算出最優解。 (動態規劃的思想)

    找規律,假定 s(n)=P(z1) +P (w1|z1)

              +P(z2|z1)+P(w2|z2)

              +P(z3|z2)+P(w3|z3)

              + ......

              +P(zn|zn-1)+P(wn|zn)

    根據上述關係式,可以定義陣列dp[i][j] 來表示第i 個單詞的詞性為第j個tag的概率

定義陣列dp[i][j] =dp[i-1][k] + A[][] + B[][]

    思路大概是這樣的接下來實現即可

已有的訓練資料的樣本格式為:

目前假定遇到 . 即判定為一句話的結束,這樣方便計算pi 值

tag2id,id2tag = {},{}   #兩個map ,用來記錄位置和詞性的關係  ,可以通過位置找到詞性,也可通過詞性定位置
word2id,id2word = {},{} #同上,記錄單詞與位置的關係。 for line in open("data/traindata.txt"): items = line.split("/") word , tag = items[0],items[1].rstrip() #如果單詞未在map中,則加入。 if word not in word2id: word2id[word] = len(id2word) id2word[len(id2word)] = word #tag不在,則加入 if tag not in tag2id: tag2id[tag] = len(id2tag) id2tag[len(id2tag)] = tag M = len(id2word) #詞典長度 N = len(id2tag) #詞性種類個數 print(M,N) #構建需要的矩陣 ,pi ,A , B A 是N*M 的矩陣 #計算使用的數學公式 Z = A + pi + B # A為N*M的矩陣,表示 A[i][j] 表示 tag i 出現單詞 j 的概率。 pi 表示 每種詞性出現在句子開頭的概率 B 表示 N*N B[i][j] 之前狀態是 i 轉換成 j 的概率 import numpy as np pi = np.zeros(N) #每種詞性,出現在句子開頭的概率 A = np.zeros((N,M)) B = np.zeros((N,N)) prev_tag = "" for line in open("data/traindata.txt"): items = line.split("/") wordId ,tagId = word2id[items[0]] , tag2id[items[1].rstrip()] if prev_tag == "": #表示句子開始 pi[tagId] +=1 A[tagId][wordId] +=1 else: #不是句子開始 A[tagId][wordId] += 1 B[tag2id[prev_tag]][tagId] +=1 if items[0] == ".": prev_tag = "" else: prev_tag = items[1].rstrip() pi = pi/sum(pi) for i in range(N): A[i] /= sum(A[i]) B[i] /= sum(B[i]) def log(v): if v ==0: return np.log(0.000001) else: return np.log(v) def vitebi(x,pi,A,B): """ :param x: :param pi: :param A: 單詞概率 :param B: tag狀態轉移概率 :return: """ X = [word2id[word] for word in x.split(" ")] T = len(X) dp = np.zeros((T,N)) #dp[i][j] 表示 wi 詞性是第j 個tag 的概率 ptr = np.array([[0 for x in range(N)] for y in range(T)]) for j in range(N): dp[0][j] = log(pi[j]) + log(A[j][X[0]]) for i in range(1,T): for j in range(N): dp[i][j] = -9999 for k in range(N): score =dp[i-1][k] + log(A[j][X[i]]) + log(B[k][j]) if score>dp[i][j]: dp[i][j] = score ptr[i][j] = k #輸出結果 best_seq = [0]*T #找最後一個單詞的詞性 best_seq[T-1] = np.argmax(dp[T-1]) #從後到前的迴圈,找每個單詞的詞性 for i in range(T-2,-1,-1): best_seq[i] = ptr[i + 1][best_seq[i + 1]] for i in range(len(best_seq)): print(id2tag[best_seq[i]]) mystr = "Social Security number , passport number and details about the services provided for the payment" print(vitebi(mystr,pi,A,B))