最大匹配算法的介绍可以看一下上一篇文章,这个算法是有缺陷的,在词库里面没有收录到一个词的时候,是难以被正确划分的,也就是没有新词发现,并且在一个特别长的词中往往还包含多个词,例如‘自然语言处理’,其实是包括多个词“自然,语言,自然语言,处理,自然语言处理”;但是使用最大匹配算法的时候是不能够划分的,只会被划分为‘自然语言处理’一个词。
viterbi算法则是动态规划的一种算法,所求出来的路径是最优路径。在求得最优路径时,只需要回溯到开始位置,便可以实现解码的步骤。
以下为具体代码实现,其中语料使用的是‘人名日报分词词性标注’语料库,并且只是拿其中一部分做的demo。
import os
import numpy as np
import re
import time
vocab = []
def get_dataset(dir_path):
'''
:param dir_path: 文件夹路径
:return: 所有文件的数据集合
'''
file_list = os.listdir(dir_path)
dataset = []
tag2id, id2tag = {}, {}
word2id, id2word = {}, {}
for file_name in file_list:
file_path = dir_path + '/' + file_name
f = open(file_path, 'r', encoding='utf-8')
file_data = f.read().replace('\n', '')
line_data = file_data.split("。/w")
dataset.append(line_data)
for line in line_data:
line = line + ' 。/w'
# long_word_list = re.sub()
items = line.split(' ')
for item in items:
word_tag = item.split('/')
if len(word_tag) == 2:
word, tag = word_tag[0].rstrip(), word_tag[1].rstrip()
else:
continue
if word not in word2id:
vocab.append(word)
word2id[word] = len(word2id)
id2word[len(id2word)] = word
if tag not in tag2id:
tag2id[tag] = len(tag2id)
id2tag[len(id2tag)] = tag
# print(tag2id)
return word2id, id2word, tag2id, id2tag, dataset
def get_Pi_A_B(dataset, word2id, tag2id):
'''
:param dataset: 训练数据集
:param word2id:
:param tag2id:
:return:
'''
M = len(word2id)
N = len(tag2id)
pi = np.zeros(N)
A = np.zeros((N, M))
B = np.zeros((N, N))
for file in dataset:
for sentence in file:
sentence = sentence + ' 。/w'
prev_tag = ""
if len(sentence) <= 2:
continue
else:
word_tag_list = sentence.split(' ')
for word_tag in word_tag_list:
items = word_tag.split("/")
if len(items) != 2:
continue
# print(items)
word_id = word2id[items[0].rstrip()]
tag_id = tag2id[items[1].rstrip()]
if prev_tag == '':
pi[tag_id] += 1
A[tag_id][word_id] += 1
else:
A[tag_id][word_id] += 1
B[tag2id[prev_tag]][tag_id] += 1
if items[0] == '。':
prev_tag = ''
else:
prev_tag = items[1].rstrip()
pi = pi / sum(pi)
for i in range(N):
A[i] = A[i] / sum(A[i])
B[i] = B[i] / sum(B[i])
return pi, A, B
def log(x):
'''
:param x:
:return: 避免输入为0的情况
'''
if x == 0:
return np.log(0.00000001)
else:
return np.log(x)
def max_match_forword(input_sentence, vocab, max_len: int):
'''
:param input_sentence:
:param vocab:
:param max_len:
:return: 最大匹配分词算法
'''
result = []
tmp = 0
while tmp < len(input_sentence):
for i in range(max_len):
index = tmp + max_len - i
if input_sentence[tmp: index] in vocab:
result.append(input_sentence[tmp:index])
tmp = index
break
elif index == 1 + tmp:
result.append(input_sentence[tmp])
tmp = index
return result
def viterbi(input, word2id, tag2id, id2tag, pi, A, B):
'''
:param input: 输入句子
:param pi:
:param A:
:param B:
:return: 维特比算法
'''
# print(vocab)
wordlist = max_match_forword(input, vocab, 8)
x = []
for word in wordlist:
if word in word2id:
x.append(word2id[word])
else:
x.append(0)
T = len(x)
N = len(tag2id)
temp_matrix = np.zeros((T, N))
ptr = np.zeros((T, N))
for j in range(N):
temp_matrix[0][j] = log(pi[j]) + log(A[j][x[0]])
for i in range(1, T):
for j in range(N):
temp_matrix[i][j] = -111111
for k in range(N):
score = temp_matrix[i - 1][k] + log(B[k][j]) + log(A[j][x[i]])
if score > temp_matrix[i][j]:
temp_matrix[i][j] = score
ptr[i][j] = k
best_seq = [0] * T
best_seq[T - 1] = np.argmax(temp_matrix[T - 1])
result = []
for i in range(T - 2, -1, -1):
best_seq[i] = ptr[i + 1][int(best_seq[i + 1])]
for i in range(len(best_seq)):
result.append([wordlist[i], id2tag[best_seq[i]]])
return result
if __name__ == "__main__":
file_path = '/NLP/dataset/people2014/2014/0101'
word2id, id2word, tag2id, id2tag, dataset = get_dataset(file_path)
pi, A, B = get_Pi_A_B(dataset, word2id, tag2id)
input = ''
truedata = []
time1 = time.time()
with open('/NLP/dataset/people2014/2014/c1002-23995935.txt', 'r', encoding='utf-8') as f:
data = f.read()
sentencelist = data.split('。/w')
for sentence in sentencelist:
sentence = sentence + ' 。/w'
word_tag_list = sentence.split(' ')
for word_tag in word_tag_list:
items = word_tag.split("/")
input += items[0]
truedata.append(items)
result = viterbi(input, word2id, tag2id, id2tag, pi, A, B)
count = 0
for item in result:
if item in truedata:
count += 1
print(len(truedata), truedata)
print(len(result), result)
print(count / len(truedata))
time2 = time.time()
print('time:', time2-time1)
运行结果:
input: 人民网1月1日讯据《[纽约时报]》报道,美国华尔街股市在2013年的最后一天继续上涨,和[全球股市一样,都以[最高纪录或接近[最高纪录结束本年的交易。
《[纽约时报》报道说,标普500指数今年上升29.6%,为1997年以来的最大涨幅;[道琼斯工业平均指数上升26.5%,为1996年以来的最大涨幅;[纳斯达克上涨38.3%。
就12月31日来说,由于就业前景看好和[经济增长明年可能加速,消费者信心上升。工商协进会(ConferenceBoard)报告,12月消费者信心上升到78.1,明显高于11月的72。
另据《[华尔街日报》报道,2013年是1995年以来[美国股市表现最好的一年。这一年里,投资[美国股市的明智做法是追着“傻钱”跑。所谓的“傻钱”策略,其实就是买入并持有美国股票这样的普通组合。这个策略要比[对冲基金和其它专业投资者使用的更为复杂的投资方法效果好得多。(老任)
。
true: 220 [['人民网', 'nz'], ['1月1日', 't'], ['讯', 'ng'], ['据', 'p'], ['《', 'w'], ['[纽约', 'nsf'], ['时报', 'n'], [']', 'nz'], ['》', 'w'], ['报道', 'v'], [',', 'w'], ['美国', 'nsf'], ['华尔街', 'nsf'], ['股市', 'n'], ['在', 'p'], ['2013年', 't'], ['的', 'ude1'], ['最后', 'f'], ['一天', 'mq'], ['继续', 'v'], ['上涨', 'vn'], [',', 'w'], ['和', 'cc'], ['[全球', 'n'], ['股市', 'n]', 'nz'], ['一样', 'uyy'], [',', 'w'], ['都', 'd'], ['以', 'p'], ['[最高', 'a'], ['纪录', 'n]', 'nz'], ['或', 'c'], ['接近', 'v'], ['[最高', 'a'], ['纪录', 'n]', 'nz'], ['结束', 'v'], ['本', 'rz'], ['年', 'qt'], ['的', 'ude1'], ['交易', 'vn'], [''], ['。', 'w'], [''], ['\n《', 'w'], ['[纽约', 'nsf'], ['时报', 'n]', 'nz'], ['》', 'w'], ['报道', 'v'], ['说', 'v'], [',', 'w'], ['标普', 'nz'], ['500', 'm'], ['指数', 'n'], ['今年', 't'], ['上升', 'vi'], ['29.6%', 'm'], [',', 'w'], ['为', 'p'], ['1997年', 't'], ['以来', 'f'], ['的', 'ude1'], ['最大', 'gm'], ['涨幅', 'n'], [';', 'w'], ['[道琼斯', 'ntc'], ['工业', 'n'], ['平均', 'a'], ['指数', 'n]', 'nz'], ['上升', 'vi'], ['26.5%', 'm'], [',', 'w'], ['为', 'p'], ['1996年', 't'], ['以来', 'f'], ['的', 'ude1'], ['最大', 'gm'], ['涨幅', 'n'], [';', 'w'], ['[纳斯', 'nrf'], ['达', 'v'], ['克', 'q]', 'nz'], ['上涨', 'vi'], ['38.3%', 'm'], [''], ['。', 'w'], [''], ['\n就', 'd'], ['12月31日', 't'], ['来说', 'uls'], [',', 'w'], ['由于', 'p'], ['就业', 'vn'], ['前景', 'n'], ['看好', 'v'], ['和', 'cc'], ['[经济', 'n'], ['增长', 'v]', 'nz'], ['明年', 't'], ['可能', 'v'], ['加速', 'vn'], [',', 'w'], ['消费者', 'n'], ['信心', 'n'], ['上升', 'vi'], [''], ['。', 'w'], [''], ['工商', 'n'], ['协进会', 'nis'], ['(', 'w'], ['ConferenceBoard', 'x'], [')', 'w'], ['报告', 'n'], [',', 'w'], ['12月', 't'], ['消费者', 'n'], ['信心', 'n'], ['上升', 'vi'], ['到', 'v'], ['78.1', 'm'], [',', 'w'], ['明显', 'a'], ['高于', 'v'], ['11月', 't'], ['的', 'ude1'], ['72', 'm'], [''], ['。', 'w'], [''], ['\n另据', 'nz'], ['《', 'w'], ['[华尔街', 'nsf'], ['日报', 'n]', 'nz'], ['》', 'w'], ['报道', 'v'], [',', 'w'], ['2013年', 't'], ['是', 'vshi'], ['1995年', 't'], ['以来', 'f'], ['[美国', 'nsf'], ['股市', 'n]', 'nz'], ['表现', 'v'], ['最好', 'd'], ['的', 'ude1'], ['一年', 'mq'], [''], ['。', 'w'], [''], ['这', 'rzv'], ['一年', 'mq'], ['里', 'f'], [',', 'w'], ['投资', 'v'], ['[美国', 'nsf'], ['股市', 'n]', 'nz'], ['的', 'ude1'], ['明智', 'a'], ['做法', 'n'], ['是', 'vshi'], ['追', 'v'], ['着', 'uzhe'], ['“', 'w'], ['傻钱', 'nz'], ['”', 'w'], ['跑', 'v'], [''], ['。', 'w'], [''], ['所谓', 'v'], ['的', 'ude1'], ['“', 'w'], ['傻钱', 'nz'], ['”', 'w'], ['策略', 'n'], [',', 'w'], ['其实', 'd'], ['就是', 'v'], ['买入', 'vn'], ['并', 'cc'], ['持有', 'v'], ['美国', 'nsf'], ['股票', 'n'], ['这样', 'rzv'], ['的', 'ude1'], ['普通', 'a'], ['组合', 'vn'], [''], ['。', 'w'], [''], ['这个', 'rz'], ['策略', 'n'], ['要', 'v'], ['比', 'p'], ['[对冲', 'vn'], ['基金', 'n]', 'nz'], ['和', 'cc'], ['其它', 'rz'], ['专业', 'n'], ['投资者', 'nnd'], ['使用', 'v'], ['的', 'ude1'], ['更为', 'd'], ['复杂', 'a'], ['的', 'ude1'], ['投资', 'vn'], ['方法', 'n'], ['效果', 'n'], ['好', 'a'], ['得', 'ude3'], ['多', 'a'], [''], ['。', 'w'], [''], ['(', 'w'], ['老', 'a'], ['任', 'v'], [')', 'w'], ['\n'], ['。', 'w']]
predict: 247 [['人民网', 'nz'], ['1月1日', 't'], ['讯', 'ng'], ['据', 'p'], ['《', 'w'], ['[纽约', 'nsf'], ['时报', 'n'], [']', 'w'], ['》', 'w'], ['报道', 'v'], [',', 'w'], ['美国', 'nsf'], ['华', 'b'], ['尔', 'n'], ['街', 'n'], ['股', 'q'], ['市', 'n'], ['在', 'p'], ['2013年', 't'], ['的', 'ude1'], ['最后', 'f'], ['一天', 'mq'], ['继续', 'v'], ['上涨', 'vi'], [',', 'w'], ['和', 'cc'], ['[全球', 'n'], ['股', 'q'], ['市', 'n'], ['一样', 'uyy'], [',', 'w'], ['都', 'd'], ['以', 'p'], ['[最高', 'a'], ['纪录', 'n'], ['或', 'c'], ['接近', 'v'], ['[最高', 'a'], ['纪录', 'n'], ['结束', 'v'], ['本', 'rz'], ['年', 'qt'], ['的', 'ude1'], ['交易', 'vn'], ['。', 'w'], ['\n', 'w'], ['《', 'w'], ['[纽约', 'nsf'], ['时报', 'n'], ['》', 'w'], ['报道', 'v'], ['说', 'v'], [',', 'w'], ['标', 'v'], ['普', 'ag'], ['500', 'm'], ['指数', 'n'], ['今年', 't'], ['上升', 'vi'], ['29', 'm'], ['.', 'w'], ['6%', 'm'], [',', 'w'], ['为', 'p'], ['1997年', 't'], ['以来', 'f'], ['的', 'ude1'], ['最大', 'gm'], ['涨幅', 'n'], [';', 'w'], ['[', 'w'], ['道', 'qv'], ['琼斯', 'w'], ['工业', 'n'], ['平均', 'a'], ['指数', 'n'], ['上升', 'vi'], ['26', 'm'], ['.', 'w'], ['5%', 'm'], [',', 'w'], ['为', 'p'], ['1996年', 't'], ['以来', 'f'], ['的', 'ude1'], ['最大', 'gm'], ['涨幅', 'n'], [';', 'w'], ['[', 'w'], ['纳', 'w'], ['斯', 'b'], ['达', 'v'], ['克', 'q'], ['上涨', 'vi'], ['38', 'm'], ['.', 'w'], ['3%', 'm'], ['。', 'w'], ['\n', 'w'], ['就', 'd'], ['12月31日', 't'], ['来说', 'uls'], [',', 'w'], ['由于', 'p'], ['就业', 'vn'], ['前景', 'n'], ['看好', 'v'], ['和', 'cc'], ['[经济', 'n'], ['增长', 'v'], ['明年', 't'], ['可能', 'v'], ['加速', 'vn'], [',', 'w'], ['消费者', 'n'], ['信心', 'n'], ['上升', 'vi'], ['。', 'w'], ['工商', 'n'], ['协', 'vg'], ['进', 'vf'], ['会', 'v'], ['(', 'w'], ['C', 'x'], ['o', 'w'], ['n', 'w'], ['f', 'w'], ['e', 'w'], ['r', 'w'], ['e', 'w'], ['n', 'w'], ['c', 'x'], ['e', 'w'], ['B', 'x'], ['o', 'w'], ['a', 'w'], ['r', 'w'], ['d', 'w'], [')', 'w'], ['报告', 'n'], [',', 'w'], ['12月', 't'], ['消费者', 'n'], ['信心', 'n'], ['上升', 'vi'], ['到', 'v'], ['7', 'm'], ['8', 'm'], ['.', 'w'], ['1', 'm'], [',', 'w'], ['明显', 'a'], ['高于', 'v'], ['11月', 't'], ['的', 'ude1'], ['7', 'm'], ['2', 'm'], ['。', 'w'], ['\n', 'w'], ['另据', 'nz'], ['《', 'w'], ['[华尔街', 'nsf'], ['日报', 'n'], ['》', 'w'], ['报道', 'v'], [',', 'w'], ['2013年', 't'], ['是', 'vshi'], ['1995年', 't'], ['以来', 'f'], ['[美国', 'nsf'], ['股', 'q'], ['市', 'n'], ['表现', 'v'], ['最好', 'd'], ['的', 'ude1'], ['一年', 'mq'], ['。', 'w'], ['这', 'rzv'], ['一年', 'mq'], ['里', 'f'], [',', 'w'], ['投资', 'v'], ['[美国', 'nsf'], ['股', 'q'], ['市', 'n'], ['的', 'ude1'], ['明智', 'a'], ['做法', 'n'], ['是', 'vshi'], ['追', 'v'], ['着', 'uzhe'], ['“', 'w'], ['傻', 'a'], ['钱', 'n'], ['”', 'w'], ['跑', 'v'], ['。', 'w'], ['所谓', 'v'], ['的', 'ude1'], ['“', 'w'], ['傻', 'a'], ['钱', 'n'], ['”', 'w'], ['策略', 'n'], [',', 'w'], ['其实', 'd'], ['就是', 'v'], ['买入', 'v'], ['并', 'cc'], ['持有', 'v'], ['美国', 'nsf'], ['股票', 'n'], ['这样', 'rzv'], ['的', 'ude1'], ['普通', 'a'], ['组合', 'vn'], ['。', 'w'], ['这个', 'rz'], ['策略', 'n'], ['要', 'v'], ['比', 'p'], ['[对', 'p'], ['冲', 'v'], ['基金', 'n'], ['和', 'cc'], ['其它', 'rz'], ['专业', 'n'], ['投资者', 'nnd'], ['使用', 'v'], ['的', 'ude1'], ['更为', 'd'], ['复杂', 'a'], ['的', 'ude1'], ['投资方', 'n'], ['法', 'n'], ['效果', 'n'], ['好', 'a'], ['得', 'ude3'], ['多', 'a'], ['。', 'w'], ['(', 'w'], ['老', 'a'], ['任', 'v'], [')', 'w'], ['\n', 'w'], ['。', 'w']]
0.7863636363636364
预测时间: 6.640239715576172
从结果来看,效果不是很好的,在词库里出现的词基本上都能正确的划分,但是例如:‘[纽约时报]’,只是被划分了“纽约,时报”这个两个词,实则应该还包含‘纽约时报’,这是因为考虑到使用的最大匹配算法,若是把‘纽约时报’这个词收录进词库,则这个词会根据最大匹配的原则只会分词为‘纽约时报’。当然了,使用最大匹配算法也是能解决这个问题的,只是时间问题,本人没有去实现。