任务目标:通过已有的训练数据,将每个单词的词性标记出来。
知识储备:
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))