词性标注实战——Viterbi算法

第一步 初始化

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) # 学习这里的id命名字典的过程!!!这种方法可以准确不重复计算种类!!巧妙!
        id2word[len(id2word)] = word
    if tag not in tag2id:
        tag2id[tag] = len(tag2id)    # 学习这里的id命名字典的过程!!!这种方法可以准确不重复计算种类!!巧妙!
        id2tag[len(id2tag)] = tag
        
M = len(word2id) # M:词典的大小 # of words in dictionary
N = len(tag2id)  # N: 词性的种类个数  # of tags in tag set
    

第二步 构建 pi,A,B

import numpy as np
pi = np.zeros(N) # 每个词性出现在句子中第一个位置的顺序,N:词性的种数 pi[i]:tag i出现在句子中第一个位置的概率
A = np.zeros((N, M)) # A[i][j]: 给定tag i, 出现单词j的概率。N:词性的种数,M:字典中词的个数
B = np.zeros((N, N)) # B[i][j]: 之前的状态是i,之后转换成状态j的概率 N : 词性的种数

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()
        
        
# nomolize
pi = pi / sum(pi)
print(pi, sum(pi)) # 得出的是概率,总和为1
print('-' * 50)
for i in range(N):
    A[i] /= sum(A[i])
    B[i] /= sum(B[i])


到此为止计算完了模型所有的参数:pi, A, B

避免矩阵中的一些0,使得不能log

def log(v):
    if v == 0:
        return np.log(v + 0.000001)
    return np.log(v)

第三步 维特比算法

def viterbi(x, pi, A, B): # HMM算法的最简单的case
    """
    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: [5452,452,55,...] 字符串分词操作!!
    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)
    #     print(ptr.dtype) # 没问题。。。。。是int32
    
    # 初始化第一列,填充第一列
    for j in range(N): # base case for DP算法
        
        # 注意这里矩阵里面好多0,不可以直接log    !!
        dp[0][j] = log(pi[j]) + log(A[j][x[0]]) # 每个词性出现的概率   x[0] 单词下,各个词性的概率
        
    for i in range(1, T): # 每个单词
        for j in range(N): # 每个词性
            
            # TODO: 以下几行代码可以写成一行代码(vectorize的操作,会使得效率变高)
            dp[i][j] = -999999
            for k in range(N): # 从每一个k可以到达j
                
                # 这句是核心!! 前面的积累 + B(第k个词性后面是第j个词性的概率)+ A(x[i]单词是第j个词性的概率)     
                score = dp[i-1][k] + log(B[k][j]) + log(A[j][x[i]]) # 之老师不是说最小的吗o(╯□╰)o????
                
                if score > dp[i][j]:
                    dp[i][j] = score
                    ptr[i][j] = k
                    
    # decoding: 把最好的tag sequence 打印出来
    best_seq = [0] * T # best_seq = [1, 5, 14, 4, ...] # 词性下标
    # step1: 找出对应于最后一个单词的词性
    best_seq[T-1] = np.argmax(dp[T-1]) # 求出最后一列值最大的下标
    
    # step2:通过 从后到前的循环 来以此求出 每个单词的词性
    for i in range(T-2, -1, -1): #T-1, T-2,... 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]], sep=', ')
    

最后 测试

x = 'you will find it .'
viterbi(x, pi, A, B)
        

输出:

PRP
MD
VB
PRP
.

最后的最后 词性对照表

英文词性对照表

CC Coordinating conjunction
CD Cardinal number
DT Determiner
EX Existential there
FW Foreign word
IN Preposition or subordinating conjunction
JJ Adjective
JJR Adjective, comparative
JJS Adjective, superlative
LS List item marker
MD Modal
NN Noun, singular or mass
NNS Noun, plural
NNP Proper noun, singular
NNPS Proper noun, plural
PDT Predeterminer
POS Possessive ending
PRP Personal pronoun
PRP$ Possessive pronoun
RB Adverb
RBR Adverb, comparative
RBS Adverb, superlative
RP Particle
SYM Symbol
TO to
UH Interjection
VB Verb, base form
VBD Verb, past tense
VBG Verb, gerund or present participle
VBN Verb, past participle
VBP Verb, non-3rd person singular present
VBZ Verb, 3rd person singular present
WDT Wh-determiner
WP Wh-pronoun
WP$ Possessive wh-pronoun
WRB Wh-adverb

中文词性对照表

Ag 形语素 形容词性语素。形容词代码为a,语素代码g前面置以A。
a 形容词 取英语形容词adjective的第1个字母。
ad 副形词 直接作状语的形容词。形容词代码a和副词代码d并在一起。
an 名形词 具有名词功能的形容词。形容词代码a和名词代码n并在一起。
b 区别词 取汉字“别”的声母。
c 连词 取英语连词conjunction的第1个字母。
Dg 副语素 副词性语素。副词代码为d,语素代码g前面置以D。
d 副词 取adverb的第2个字母,因其第1个字母已用于形容词。
e 叹词 取英语叹词exclamation的第1个字母。
f 方位词 取汉字“方” 的声母。
g 语素 绝大多数语素都能作为合成词的“词根”,取汉字“根”的声母。
h 前接成分 取英语head的第1个字母。
i 成语 取英语成语idiom的第1个字母。
j 简称略语 取汉字“简”的声母。
k 后接成分
l 习用语 习用语尚未成为成语,有点“临时性”,取“临”的声母。
m 数词 取英语numeral的第3个字母,n,u已有他用。
Ng 名语素 名词性语素。名词代码为n,语素代码g前面置以N。
n 名词 取英语名词noun的第1个字母。
nr 人名 名词代码n和“人(ren)”的声母并在一起。
ns 地名 名词代码n和处所词代码s并在一起。
nt 机构团体 “团”的声母为t,名词代码n和t并在一起。
nz 其他专名 “专”的声母的第1个字母为z,名词代码n和z并在一起。
o 拟声词 取英语拟声词onomatopoeia的第1个字母。
p 介词 取英语介词prepositional的第1个字母。
q 量词 取英语quantity的第1个字母。
r 代词 取英语代词pronoun的第2个字母,因p已用于介词。
s 处所词 取英语space的第1个字母。
Tg 时语素 时间词性语素。时间词代码为t,在语素的代码g前面置以T。
t 时间词 取英语time的第1个字母。
u 助词 取英语助词auxiliary 的第2个字母,因a已用于形容词。
Vg 动语素 动词性语素。动词代码为v。在语素的代码g前面置以V。
v 动词 取英语动词verb的第一个字母。
vd 副动词 直接作状语的动词。动词和副词的代码并在一起。
vn 名动词 指具有名词功能的动词。动词和名词的代码并在一起。
w 标点符号
x 非语素字 非语素字只是一个符号,字母x通常用于代表未知数、符号。
y 语气词 取汉字“语”的声母。
z 状态词 取汉字“状”的声母的前一个字母。
nx 字符串

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值