目录
基于 CRF 和 Bi-LSTM 的命名实体识别 1
摘要 1
Abstract 1
0 引言 2
1 相关工作 3
2 基于 CRF 的命名实体识别 4
2.1 CRF 4
1 2 n 4
(2) 4
2.2 特征提取 5
2.3 CRF 模型框架 8
3 基于 CRF 和 LSTM 的命名实体识别 9
3.1 Bi-LSTM 9
3.2 CRF+Bi-LSTM 模型框架 10
4 实验 11
4.1 实验环境 11
@ 3.40GHz(2 核) 内存:13G 12
4.2 实验数据 12
4.3 评价指标 13
正确识别的命名实体个数 13
(5) 13
4.4 实验结果及分析 13
4.4.1 CRF 模型 13
4.4.2 CRF+Bi-LSTM 模型 17
4.4.3 模型对比 18
5 结束语 20
参考文献 21
1相关工作
命名实体识别研究至今已经有近二十年的发展历史,已经成为了自然语言处理领域的一 项重要技术,根据模型和算法的不同,先已陆续推出了成效可观的各类技术成果。
规则和词典相结合的方法最早应用于命名实体识别中,其多采用语言学专家手工构造规 则模板,选用特征包括标点符号、关键字等方法,以模式和字符串匹配为主要手段。采取这 种方法的代表性系统包括 GATE 项目中的 ANN E 系统以及参加 MUC 评测的 FACLE 系统,这类系统大多依赖于知识库和词典的建立,缺点是代价太大,存在系统建设周期长、移 植性差等问题。如王宁 [4] 等利用规则的方法进行金融领域的公司名识别,该系统对知识库的依赖性强,同时开放和封闭测试的结果也显示了规则方法的局限性。
传统的命名实体识别模型往往是基于统计的方法,在 CoNLL-2003 会议上,所参赛的 16 个系统全部采用基于统计的方法。基于统计的方法利用人工标注的语料进行训练,标注语料时不需要广博的语言学知识,并且可以在较短时间内完成。基于统计机器学习的方法主要有:

隐马尔可夫模型(HMM)、最大熵模型(ME)、最大熵马尔可夫模型(MEMM)、支持向量机(SVM)以及条件随机场(CRF)等。Zhao 等通过最大熵模型对 4 类名词进行实体识别, 获得 77.87% 的准确率 [5] 。另外有陈霄 [6] 采用 SVM 模型提出了中文组织机构名的实体识别, 准确率达到了 81.68%。其中 CRF 自 2001 年由 Lafferty 等人 [7] 提出后,就广泛应用于命名实体识别领域,如 Settles [8] 等使用 CRF 对生物医学命名实体进行识别;姜文至等 [9] 提出的基于 CRF 和规则相结合的军事命名实体识别模型。

# -*- coding: utf-8 -*-
'''
模型预测
Requirements:
    需要将输入的句子转为词向量和字符级别向量, 需要单词表word2index、字符表char2index
    模型参数文件 BiLSTM_CRF.h5
    模型输入需要相关参数 max_len_seq, max_len_word
    预测结果进行解码需要标签表, index2tag
'''
from CRF_BiLSTM import *
import time

class Predict:
    def __init__(self, filename, includeBound=True):
        self.filename = filename

        self.load_testdata(filename, includeBound)

        self.load_params()

        self.predict()

    '''
    读取文件, 按句保存
    :@param includeBound 是否包含句子边界
    '''

    def load_testdata(self, filepath, includeBound):
        # 读取
        with open(filepath, 'r') as f:
            lines = f.readlines()
        # 保存所有句子
        seqs = []
        # 保存每个句子
        seq = []
        # 不包含句子边界
        if not includeBound:
            for line in lines:
                # \n换行, 读取时不会自动替换
                line = line.replace('\n', '')
                # 属于同一个句子
                if line != '':
                    seq.append(line)
                else:
                    # 标志一个句子的完结
                    seqs.append(seq)
                    seq = []
        # 有句子边界
        else:
            # 上一个是句子边界
            lastB = False
            for line in lines:
                line = line.replace('\n', '')
                # 是句子边界则直接保存
                if '###' in line:
                    seqs.append(line)
                    lastB = True
                elif line != '':
                    seq.append(line)
                    lastB = False
                # 句子边界紧接着的空行
                elif line == '' and lastB:
                    pass
                # 句子完结
                else:
                    seqs.append(seq)
                    seq = []
        self.seqs = seqs

    def load_params(self):
        # 读取单词表、字符表、标签表以及标签反表
        self.word2index = self.load_json('files/word2index.json')
        self.char2index = self.load_json('files/char2index.json')
        self.tag2index = self.load_json('files/tag2index.json')
        self.index2tag = self.load_json('files/index2tag.json')

        # 读取搭建网络所需参数
        params = self.load_json('files/params.json')
        self.max_len_word = params.get('max_len_word')
        self.max_len_seq = params.get('max_len_seq')
        self.n_words = params.get('n_words')
        self.n_tags = params.get('n_tags')
        self.n_chars = params.get('n_chars')

    '''
    对输入句子集合进行测试
    
    构建模型 => 读取参数 => 对每个句子进行预测 => 对预测结果进行解码
    
    Requirements:
        model, model_params
        params: max_len_seq, max_len_word, n_words, n_chars, n_tags
                word2index, char2index, tag2index, index2tag(标签解码)
        xseq => X_word, X_char
    
    '''

    def predict(self):
        # 搭建神经网络
        model = create_model(self.max_len_seq,
                             self.max_len_word,
                             self.n_words,
                             self.n_tags,
                             self.n_chars)

        # 读取模型并重建模型
        model.load_weights('files/model.txt')

        # 由于边界情况的问题, 因此对句子一个一个进行预测
        seq_labels = []

        count = 0
        start = time.time()
        for seq in self.seqs:
            if '###' in seq:
                seq_labels.append(seq)
            else:
                # 对句子集合操作, 便于后面的测试
                # 向量化
                X_word, X_char = self.vetorization_padding([seq])
                # 进行标签预测
                y_pred = model.predict([X_word,
                                        np.asarray(X_char).reshape(len(X_char),
                                                                   self.max_len_seq, self.max_len_word)])

                # 对预测结果进行解码
                yseq = self.decode_tags(y_pred)

                seq_labels.append(yseq[:len(seq)])
            count += 1
            if not count % 100:
                print('{:.0f}s... {}/{}'.format(time.time() - start, count, len(self.seqs)))

        # 保存预测结果
        self.save_predict(seq_labels)

    '''
    y_pred的形式为:
    [[[],[],[]],
    [[],[],[]],
    [[],[],[]]]
    每个词有tags+2种概率预测
    取每个词最大概率的预测标签下标, 并转为标签

    需要注意去除填充
    '''

    def decode_tags(self, y_pred):
        # 取最后一个维度的最大值, 即取每个词的最大概率标记的下标
        y_idx = np.argmax(y_pred, axis=-1)
        # 将每个词的标签下标转为标签
        # 保存的key值是字符串类型
        yseq = [[self.index2tag[str(w)] for w in s] for s in y_idx]
        return yseq[0]

    def vetorization_padding(self, xseqs):
        # # 注意未登录词情况
        X_word = [[self.word2index.get(word, self.word2index['UNK']) for word in xseq] for xseq in xseqs]
        # 填充(从后补齐或者从后面截断)
        X_word = pad_sequences(maxlen=self.max_len_seq,
                               sequences=X_word,
                               value=self.word2index['PAD'],
                               padding='post')


        # 将每个句子中每个单词的字符转换为编号
        # 此时每个单词由一个构成字符编号列表组成
        X_char = []
        for xseq in xseqs:
            xseq_pad = []
            # 填充每个句子为max_len_seq长度
            for i in range(self.max_len_seq):
                word = []
                # 填充每个单词为max_len_word长度
                for j in range(self.max_len_word):
                    try:
                        # 获取字符的编号
                        # 注意未登录词情况
                        word.append(self.char2index.get(xseq[i][j], self.char2index['UNK']))
                    # 产生异常则需要填充
                    except:
                        word.append(self.char2index['PAD'])

                xseq_pad.append(word)
            X_char.append(xseq_pad)

        return X_word, X_char

    def load_json(self, filename):
        with open(filename, 'r') as f:
            json_data = json.load(f)
        return json_data

    def save_predict(self, seqs_labels):
        with open('result.txt', 'w') as f:
            # 遍历得到每句及该句标记
            for seq, seq_labels in zip(self.seqs, seqs_labels):
                if '###' in seq:
                    f.write(seq + '\n')
                else:
                    # 遍历每句
                    for word, label in zip(seq, seq_labels):
                        f.write(word + '\t' + label + '\n')
                # 每句用空行分割
                f.write('\n')

if __name__ == '__main__':
    # 测试包含句子边界
    filename = 'files/Genia4EReval2.raw'
    Predict(filename, True)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐