【NLP | 词性标注】使用最大匹配与viterbi算法代码实现中文词性标注

        最大匹配算法的介绍可以看一下上一篇文章,这个算法是有缺陷的,在词库里面没有收录到一个词的时候,是难以被正确划分的,也就是没有新词发现,并且在一个特别长的词中往往还包含多个词,例如‘自然语言处理’,其实是包括多个词“自然,语言,自然语言,处理,自然语言处理”;但是使用最大匹配算法的时候是不能够划分的,只会被划分为‘自然语言处理’一个词。

        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

        从结果来看,效果不是很好的,在词库里出现的词基本上都能正确的划分,但是例如:‘[纽约时报]’,只是被划分了“纽约,时报”这个两个词,实则应该还包含‘纽约时报’,这是因为考虑到使用的最大匹配算法,若是把‘纽约时报’这个词收录进词库,则这个词会根据最大匹配的原则只会分词为‘纽约时报’。当然了,使用最大匹配算法也是能解决这个问题的,只是时间问题,本人没有去实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值