任务077-81:词性标注-实战
进行词性标注,这里用了Noise Channel Model
将式子进行化简
问题便转换成2个步骤
- 计算A、B、 π \pi π
- 利用维特比算法找出最优解
A是一个N乘以M的的矩阵,每一列是词库里出现的单词,每一行是单词的词性
π
\pi
π是每一种词性的词作为句子开头词的概率
B是前一个单词词性已知(列index),后一个单词是某种词性的概率
traindata.txt的样子:
tag2id, id2tag = {}, {} # maps tag to id . tag2id: {"VB": 0, "NNP":1,..} , id2tag: {0: "VB", 1: "NNP"....}
word2id, id2word = {}, {} # maps word to id
for line in open('traindata.txt'):
items = line.split('/')
word, tag = items[0], items[1].rstrip() # 抽取每一行里的单词和词性
if word not in word2id:
word2id[word] = len(word2id)
id2word[len(id2word)] = word
if tag not in tag2id:
tag2id[tag] = len(tag2id)
id2tag[len(id2tag)] = tag
M = len(word2id) # M: 词典的大小、# of words in dictionary
#18978
N = len(tag2id) # N: 词性的种类个数 # of tags in tag set
#54
# 构建 pi, A, B
import numpy as np
pi = np.zeros(N) # 每个词性出现在句子中第一个位置的概率, N: # of tags pi[i]: tag i出现在句子中第一个位置的概率
A = np.zeros((N,M)) # A[i][j]: 给定tag i, 出现单词j的概率。 N: # of tags M: # of words in dictionary
B = np.zeros((N,N)) # B[i][j]: 之前的状态是i, 之后转换成转态j的概率 N: # of tags
prev_tag = ""
for line in open('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()
# normalize
pi = pi/sum(pi)
for i in range(N):
A[i] /= sum(A[i])
B[i] /= sum(B[i])
# 到此为止计算完了模型的所有的参数: pi, A, B
给定有6个单词的一句话,对于词性而言,一共有54^6种组合,每一种组合我们都可以计算第一行的概率,目的是找出概率最大的词性组合,但是复杂度太高了
先从
w
1
w_{1}
w1到
w
T
w_{T}
wT计算dp每个格子最短路径,计算
w
1
w_{1}
w1时只考虑句子只有
w
1
w_{1}
w1这个单词,计算
w
2
w_{2}
w2时考虑句子有
w
1
w_{1}
w1和
w
2
w_{2}
w2两个单词,以此类推。。。
到最后 w T w_{T} wT那一列填满了,找出dp[T,0-N]最大的那个位置,作为最后一个单词的tag,反向推倒前面 w T − 1 w_{T-1} wT−1单词的tag,以此类推。。。
def viterbi(x, pi, A, B):
"""
x: user input string/sentence: x: "I like playing soccer"
pi: initial probability of tags
A: 给定tag, 每个单词出现的概率
B: tag之间的转移概率
"""
x = [word2id[word] for word in x.split(" ")] # x: [4521, 412, 542 ..]
T = len(x)
dp = np.zeros((T,N)) # dp[i][j]: w1...wi, 假设wi的tag是第j个tag
ptr = np.array([[0 for x in range(N)] for y in range(T)] ) # T*N
# TODO: ptr = np.zeros((T,N), dtype=int)
for j in range(N): # basecase for DP算法
dp[0][j] = log(pi[j]) + log(A[j][x[0]])
for i in range(1,T): # 每个单词
for j in range(N): # 每个词性
# TODO: 以下几行代码可以写成一行(vectorize的操作, 会使得效率变高)
dp[i][j] = -9999999
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
ptr[i][j] = k
# decoding: 把最好的tag sequence 打印出来
best_seq = [0]*T # best_seq = [1,5,2,23,4,...]
# step1: 找出对应于最后一个单词的词性
best_seq[T-1] = np.argmax(dp[T-1])
# step2: 通过从后到前的循环来依次求出每个单词的词性
for i in range(T-2, -1, -1): # T-2, T-1,... 1, 0
best_seq[i] = ptr[i+1][best_seq[i+1]]
# 到目前为止, best_seq存放了对应于x的 词性序列
for i in range(len(best_seq)):
print (id2tag[best_seq[i]])
x = "Social Security number , passport number and details about the services provided for the payment"
viterbi(x, pi, A, B)
# NNP
# NNP
# NN
# ,
# NN
# NN
# CC
# NNS
# IN
# DT
# NNS
# VBN
# IN
# DT
# NN
下一个状态依赖于上一个状态,
z
2
z_{2}
z2依赖于
z
1
z_{1}
z1,
z
3
z_{3}
z3依赖于
z
2
z_{2}
z2,以此类推,这种情况下适合维特比算法。
如果不是这样的不适合维特比算法
任务138: MLE VS MAP介绍
正则可以理解成先验
先验概率:没有看到任何数据,就对模型的参数有一个判断,例如
θ
\theta
θ可能服从高斯分布、拉普拉斯分布
后验概率:随着观察更多的数据对模型的参数有一个判断有一个改变
任务144: MLE与MAP
MLE仅仅通过样本来估计模型参数
MAP通过样本+先验来估计模型参数,这种先验在数据量较小的情况下很重要,但是随着数据量增大就变得不再重要。
正则可以理解成先验
当我们假设咱们的参数服从高斯分布,最后MAP的solution相当于我们加了一个正则(l2-norm)
当我们假设咱们的参数服从拉普拉斯分布,最后MAP的solution相当于我们加了一个正则(l1-norm)
add prior == regularzation,对于任何模型都成立
样本无穷大,MAP的solution逼近于MLE的solution