基于LSTM+CRF的中文命名实体识别

  • 任务描述:命名实体识别是指识别文本中具有特定意义的实体,主要包括人名、地名、机构名、专有名词等。
    • 实现一个简单的命名实体识别方法,该方法通过BiLSTM+CRF模型预测出文本中文字所对应的标签,再根据标签提取出文本中的实体。
    • 从数据文件中加载数据并进行预处理、构建模型、训练模型、评估模型和测试模型。
      • 说明:目前本文档仅作为示例,为了加快训练速度模型较为简单,词向量维度也比较低,因此导致模型准确率较低。
import tensorflow as tf
import numpy as np
import pandas as pd
import tensorflow_addons as tfa
  • tf 2.0 的新特性:eager execution

    • 可以用tf.executing_eargerly()查看Eager Execution当前的启动状态,返回True则是开启,False是关闭。
    • 可以用tf.compat.v1.enable_eager_execution()启动eager模式。
    • 关闭eager模式的函数是 tf.compat.v1.disable_eager_execution()
tf.compat.v1.disable_eager_execution()

文本数准备

  • 数据说明
  • 数据整理
  • 加载数据

数据说明

  • 数据集共包含约2.7万中文文本,其中包括约2.08万训练集,0.23万验证集和0.46万测试集。

  • 数据集分别命名为example.train,example.dev,example.test,保存在datasets目录下。

    • 1.训练集:包含文本和对应的标签,用于模型训练。
    • 2.验证集:包含文本和对应的标签,用于模型训练和参数调试。
    • 3.测试集:包含文本和对应的标签,用于预测结果、验证效果。
  • 数据集中标注有三种实体,分别为人名、地名、机构名,标注集合为{'O','B-PER','I-PER','B-ORG','I-ORG','B-LOC','I-LOC'}。

    • 其中'O'表示非实体,
    • 'B-'表示实体的首字,
    • 'I-'表示实体的其他位置的字,
    • 'PER'表示人名,
    • 'ORG'表示机构名,
    • 'LOC'表示地名。IOB(Inside–outside–beginning)是用于标记标志的通用标记格式。

数据整理

根据数据集中的字符建立字典,并保存在datasets/vocab.txt中, 标签根据数据集中字符建立字典,并保存在datasets/.txt中

def get_vocab_list(path_list):
    vocab_set = set()
    vocab_list = list()
    for path in path_list:
        with open(path,'r',encoding='utf-8') as f:
            for line in f:
                if len(line.strip()) == 0:
                    continue
                if line[0] not in vocab_set:
                    vocab_set.add(line[0])
                    vocab_list.append(line[0])
    return vocab_list

def save_vocab(path,vocab_list):
    output = ''.join([vocab + '\n' for vocab in vocab_list])
    with open(path,'w', encoding='utf-8') as f:
        f.write(output)
        
vocab_list = get_vocab_list(['./datasets/example.train','./datasets/example.dev','./datasets/example.test'])
save_vocab('./datasets/vocab.txt',vocab_list)
vocab_list
['海',
 '钓',
 '比',
 '赛',
 '地',
 '点',
 '在',
 '厦',
 '门',
 '与',
 '金',
 '之',
 '间',
 '的',
 '域',
 '。',
 '这',
 '座',
 '依',
 '山',
 '傍',
 '水',
 '博',
 '物',
 '馆',
 '由',
 '国',
 '内',
 '一',
 '流',
 '设',
 '计',
 '师',
 '主',
 '持',
 ',',
 '整',
 '个',
 '建',
 '筑',
 '群',
 '精',
 '美',
 '而',
 '恢',
 '宏',
 '但',
 '作',
 '为',
 '共',
 '产',
 '党',
 '员',
 '、',
 '人',
 '民',
 '公',
 '仆',
 '应',
 '当',
 '胸',
 '怀',
 '宽',
 '阔',
 '真',
 '正',
 '做',
 '到',
 '“',
 '先',
 '天',
 '下',
 '忧',
 '后',
 '乐',
 '”',
 '淡',
 '化',
 '名',
 '利',
 '得',
 '失',
 '和',
 '宠',
 '辱',
 '悲',
 '喜',
 '把',
 '改',
 '革',
 '大',
 '业',
 '摆',
 '首',
 '位',
 '样',
 '才',
 '能',
 '超',
 '越',
 '自',
 '我',
 '脱',
 '世',
 '俗',
 '有',
 '所',
 '发',
 '达',
 '家',
 '急',
 '救',
 '保',
 '险',
 '十',
 '分',
 '普',
 '及',
 '已',
 '成',
 '社',
 '会',
 '障',
 '体',
 '系',
 '重',
 '要',
 '组',
 '部',
 '日',
 '俄',
 '两',
 '政',
 '局',
 '都',
 '充',
 '满',
 '变',
 '数',
 '尽',
 '管',
 '关',
 '目',
 '前',
 '是',
 '历',
 '史',
 '最',
 '佳',
 '时',
 '期',
 '其',
 '脆',
 '弱',
 '性',
 '不',
 '言',
 '明',
 '克',
 '马',
 '尔',
 '女',
 '儿',
 '让',
 '娜',
 '今',
 '年',
 '读',
 '五',
 '级',
 '她',
 '班',
 '上',
 '3',
 '0',
 '多',
 '同',
 '学',
 '该',
 '委',
 '1',
 '长',
 '参',
 '加',
 '步',
 '行',
 '男',
 '轻',
 '也',
 '中',
 '沙',
 '特',
 '队',
 '教',
 '练',
 '佩',
 '雷',
 '拉',
 ':',
 '支',
 '想',
 '胜',
 '因',
 '此',
 '出',
 '了',
 '努',
 '力',
 '种',
 '混',
 '乱',
 '面',
 '导',
 '致',
 '些',
 '使',
 '用',
 '者',
 '合',
 '法',
 '权',
 '益',
 '难',
 '以',
 '维',
 '护',
 '鲁',
 '宾',
 '确',
 '指',
 '对',
 '府',
 '控',
 '完',
 '全',
 '没',
 '事',
 '实',
 '根',
 '据',
 '向',
 '转',
 '敏',
 '感',
 '技',
 '术',
 '相',
 '总',
 '白',
 '于',
 ';',
 '众',
 '议',
 '院',
 '令',
 '非',
 '常',
 '望',
 '将',
 '商',
 '卫',
 '星',
 '受',
 '威',
 '胁',
 '竞',
 '争',
 '损',
 '害',
 '育',
 '场',
 '每',
 '早',
 '6',
 '至',
 '8',
 '免',
 '费',
 '开',
 '放',
 '游',
 '泳',
 '等',
 '则',
 '增',
 '综',
 '服',
 '务',
 '延',
 '采',
 '取',
 '灵',
 '活',
 '收',
 '再',
 '看',
 '容',
 '图',
 '文',
 '并',
 '茂',
 '简',
 '短',
 '字',
 '准',
 '反',
 '映',
 '六',
 '族',
 '风',
 '土',
 '情',
 '传',
 '统',
 '各',
 '讲',
 '很',
 '哥',
 '伦',
 '亚',
 '号',
 '航',
 '飞',
 '机',
 '宇',
 '边',
 '进',
 '验',
 '继',
 '续',
 '抢',
 '修',
 '故',
 '二',
 '氧',
 '碳',
 '清',
 '除',
 '装',
 '置',
 '从',
 '剥',
 '削',
 '阶',
 '劳',
 '动',
 '生',
 '率',
 '低',
 '况',
 '占',
 '限',
 '剩',
 '余',
 '品',
 '足',
 '豪',
 '华',
 '需',
 '量',
 '扩',
 '现',
 '资',
 '条',
 '件',
 '榨',
 '价',
 '值',
 '供',
 '身',
 '求',
 '外',
 '又',
 '本',
 '新',
 '雇',
 '佣',
 '私',
 '来',
 '料',
 '集',
 '代',
 '表',
 '澳',
 '别',
 '区',
 '筹',
 '备',
 '第',
 '次',
 '午',
 '北',
 '京',
 '堂',
 '幕',
 '副',
 '理',
 '任',
 '钱',
 '琛',
 '词',
 '工',
 '经',
 '启',
 '临',
 '紧',
 '迫',
 '们',
 '道',
 '远',
 '希',
 '齐',
 '心',
 '协',
 '平',
 '稳',
 '过',
 '渡',
 '顺',
 '交',
 '接',
 '贡',
 '献',
 '济',
 '程',
 '界',
 '深',
 '信',
 '观',
 '必',
 '须',
 '微',
 '基',
 '础',
 '病',
 '好',
 '颈',
 '背',
 '躯',
 '干',
 '殖',
 '器',
 '可',
 '见',
 '龄',
 '市',
 '柔',
 '县',
 '试',
 '遍',
 '觉',
 '四',
 '节',
 '课',
 '饥',
 '饿',
 '消',
 '川',
 '省',
 '江',
 '油',
 '丰',
 '选',
 '豆',
 '奶',
 '复',
 '营',
 '养',
 '素',
 '贫',
 '血',
 '降',
 '百',
 '照',
 '只',
 '.',
 '4',
 '宗',
 '旨',
 '决',
 '定',
 '奉',
 '甘',
 '愿',
 '吃',
 '亏',
 '苦',
 '享',
 '困',
 '危',
 '留',
 '给',
 '己',
 '方',
 '便',
 '安',
 '初',
 '央',
 '形',
 '势',
 '快',
 '速',
 '策',
 '立',
 '研',
 '究',
 '派',
 '就',
 '提',
 '许',
 '富',
 '意',
 '味',
 '论',
 '话',
 '题',
 '书',
 '记',
 '粟',
 '光',
 '断',
 '他',
 '亲',
 '兄',
 '弟',
 '签',
 '!',
 '熟',
 '悉',
 '运',
 '士',
 '说',
 '贷',
 '款',
 '旦',
 '被',
 '推',
 '迟',
 '几',
 '月',
 '甚',
 '更',
 '考',
 '虑',
 '伍',
 '义',
 '气',
 '原',
 '惠',
 '象',
 '估',
 '像',
 '疤',
 '痕',
 '笨',
 '拙',
 '手',
 '针',
 '线',
 '周',
 '恩',
 '那',
 '送',
 '株',
 '万',
 '古',
 '青',
 '友',
 '谊',
 '红',
 '杉',
 '吧',
 '巴',
 '谈',
 '问',
 '印',
 '声',
 '宣',
 '称',
 '何',
 '三',
 '介',
 '入',
 '久',
 '赴',
 '西',
 '老',
 '访',
 '广',
 '职',
 '积',
 '极',
 '企',
 '监',
 '督',
 '如',
 '居',
 '子',
 '倘',
 '若',
 '客',
 '环',
 '境',
 '钝',
 '迅',
 '判',
 '陷',
 '处',
 '碰',
 '壁',
 '然',
 '热',
 '解',
 '寻',
 '找',
 '联',
 '邦',
 '州',
 '予',
 '适',
 '具',
 '骤',
 '吸',
 '七',
 '团',
 '另',
 '龙',
 '口',
 '科',
 '投',
 '视',
 '息',
 '创',
 '跳',
 '跃',
 '式',
 '展',
 '强',
 '调',
 '高',
 '培',
 '训',
 '认',
 '识',
 '密',
 '结',
 '小',
 '九',
 '升',
 '或',
 '段',
 '少',
 '懂',
 '习',
 '互',
 '贵',
 '贱',
 '请',
 '注',
 '语',
 '构',
 '偏',
 '孝',
 '父',
 '母',
 '始',
 '票',
 '否',
 '制',
 '还',
 '缺',
 '乏',
 '际',
 '电',
 '影',
 '摄',
 '录',
 '音',
 '剪',
 '辑',
 '诸',
 '趋',
 '激',
 '烈',
 '造',
 '奇',
 '夺',
 '起',
 '直',
 '追',
 '医',
 '药',
 '李',
 '珍',
 '闻',
 '屡',
 '仕',
 '寄',
 '托',
 '八',
 '股',
 '兴',
 '趣',
 '酷',
 '爱',
 '召',
 '吴',
 '英',
 '司',
 '董',
 '德',
 '·',
 '葛',
 '夫',
 '邀',
 '席',
 '车',
 '闲',
 '防',
 '施',
 '配',
 '套',
 '存',
 '鹅',
 '洲',
 '源',
 '终',
 '连',
 '串',
 '井',
 '序',
 '顾',
 '犯',
 '罪',
 '讳',
 '审',
 '死',
 '刑',
 '笔',
 '融',
 '农',
 '假',
 '币',
 '知',
 '巧',
 '律',
 '规',
 '村',
 '秩',
 '『',
 '范',
 '茅',
 '迹',
 '览',
 '』',
 '磁',
 '球',
 '孩',
 '格',
 '散',
 '着',
 '独',
 '魅',
 '莱',
 '坞',
 '千',
 '模',
 '翻',
 '版',
 '?',
 '燃',
 '汽',
 '仅',
 '守',
 '—',
 '路',
 '易',
 '斯',
 '切',
 '迪',
 '阿',
 '塞',
 '瓦',
 '萨',
 '卡',
 '里',
 '罗',
 '弗',
 '哈',
 '丹',
 '尼',
 '扎',
 '胡',
 '埃',
 '索',
 '库',
 '纳',
 '吉',
 '莫',
 '帕',
 '锋',
 '米',
 '贝',
 '耶',
 '冈',
 '玻',
 '乌',
 '布',
 '苏',
 '念',
 '案',
 '罚',
 '较',
 '战',
 '略',
 '均',
 '贸',
 '拓',
 '5',
 '份',
 '去',
 '阅',
 '室',
 '刊',
 '晚',
 '智',
 '藏',
 '突',
 '破',
 '借',
 '架',
 '办',
 '沃',
 '盟',
 '昨',
 '约',
 '南',
 '举',
 '空',
 '军',
 '演',
 '示',
 '欢',
 '迎',
 '帮',
 '助',
 '7',
 '叫',
 '迈',
 '菲',
 '查',
 '奋',
 '剂',
 '通',
 '未',
 '勇',
 '曲',
 '绕',
 '桥',
 '绝',
 '《',
 '嫂',
 '》',
 '警',
 '魂',
 '片',
 '获',
 '房',
 '石',
 '香',
 '港',
 '朝',
 '蓬',
 '勃',
 '拼',
 '搏',
 '斗',
 '神',
 '写',
 '2',
 '春',
 '季',
 '招',
 '兵',
 '募',
 '既',
 '食',
 '预',
 '止',
 '癌',
 '症',
 '城',
 '引',
 '项',
 '懈',
 '液',
 '尾',
 '9',
 '%',
 '氢',
 '减',
 '污',
 '染',
 '双',
 '网',
 '抽',
 '封',
 '差',
 '亟',
 '待',
 '单',
 '麦',
 '果',
 '∶',
 '松',
 '赢',
 '测',
 '析',
 '迷',
 '叶',
 '钦',
 '岁',
 '刻',
 '哲',
 '思',
 '虽',
 '担',
 '负',
 '领',
 '责',
 '专',
 '却',
 '爸',
 '劲',
 '抗',
 '艰',
 '聘',
 '仍',
 '治',
 '洋',
 '尊',
 '庄',
 '志',
 '回',
 '忆',
 '淳',
 '朴',
 '报',
 '届',
 '张',
 '君',
 '秋',
 '剧',
 '角',
 '艺',
 '校',
 '坚',
 '质',
 '围',
 '侧',
 '缓',
 '财',
 '状',
 '聚',
 '渠',
 '逐',
 '靠',
 '优',
 '征',
 '稿',
 '截',
 '句',
 '晤',
 '纠',
 '纷',
 '朋',
 '坏',
 '它',
 '促',
 '岗',
 '标',
 '织',
 '践',
 '邓',
 '按',
 '署',
 '挥',
 '核',
 '●',
 '承',
 '键',
 '街',
 '巷',
 '横',
 '幅',
 '悬',
 '画',
 '头',
 '欣',
 '慰',
 ...]
def get_label_list(path_list):
    label_set = set()
    label_list = list()
    for path in path_list:
        with open(path,'r',encoding='utf-8') as f:
            for line in f:
                if len(line.strip()) == 0:
                    continue
                if line[2:].strip() not in label_set:
                    label_set.add(line[2:].strip())
                    label_list.append(line[2:].strip())
    return label_list

def save_label(path,label_list):
    output = ''.join([label + '\n' for label in label_list])
    with open(path,'w', encoding='utf-8') as f:
        f.write(output)
        
label_list = get_label_list(['./datasets/example.train','./datasets/example.dev','./datasets/example.test'])
save_label('./datasets/label.txt',label_list)
label_list
['O', 'B-LOC', 'I-LOC', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG']

加载数据

分别获取vocab 和label与id的映射

def get_string2id(path):    string2id = {}    id2string = []    with open(path,'r',encoding='utf-8') as f:        for line in f:             string2id[line.strip()] = len(string2id)            id2string.append(line.strip())    return id2string,string2idid2label,label2id = get_string2id("./datasets/label.txt")id2vocab,vocab2id = get_string2id("./datasets/vocab.txt")
id2label
['O', 'B-LOC', 'I-LOC', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG']
label2id
{'O': 0,
 'B-LOC': 1,
 'I-LOC': 2,
 'B-PER': 3,
 'I-PER': 4,
 'B-ORG': 5,
 'I-ORG': 6}
id2vocab
['海',
 '钓',
 '比',
 '赛',
 '地',
 '点',
 '在',
 '厦',
 '门',
 '与',
 '金',
 '之',
 '间',
 '的',
 '域',
 '。',
 '这',
 '座',
 '依',
 '山',
 '傍',
 '水',
 '博',
 '物',
 '馆',
 '由',
 '国',
 '内',
 '一',
 '流',
 '设',
 '计',
 '师',
 '主',
 '持',
 ',',
 '整',
 '个',
 '建',
 '筑',
 '群',
 '精',
 '美',
 '而',
 '恢',
 '宏',
 '但',
 '作',
 '为',
 '共',
 '产',
 '党',
 '员',
 '、',
 '人',
 '民',
 '公',
 '仆',
 '应',
 '当',
 '胸',
 '怀',
 '宽',
 '阔',
 '真',
 '正',
 '做',
 '到',
 '“',
 '先',
 '天',
 '下',
 '忧',
 '后',
 '乐',
 '”',
 '淡',
 '化',
 '名',
 '利',
 '得',
 '失',
 '和',
 '宠',
 '辱',
 '悲',
 '喜',
 '把',
 '改',
 '革',
 '大',
 '业',
 '摆',
 '首',
 '位',
 '样',
 '才',
 '能',
 '超',
 '越',
 '自',
 '我',
 '脱',
 '世',
 '俗',
 '有',
 '所',
 '发',
 '达',
 '家',
 '急',
 '救',
 '保',
 '险',
 '十',
 '分',
 '普',
 '及',
 '已',
 '成',
 '社',
 '会',
 '障',
 '体',
 '系',
 '重',
 '要',
 '组',
 '部',
 '日',
 '俄',
 '两',
 '政',
 '局',
 '都',
 '充',
 '满',
 '变',
 '数',
 '尽',
 '管',
 '关',
 '目',
 '前',
 '是',
 '历',
 '史',
 '最',
 '佳',
 '时',
 '期',
 '其',
 '脆',
 '弱',
 '性',
 '不',
 '言',
 '明',
 '克',
 '马',
 '尔',
 '女',
 '儿',
 '让',
 '娜',
 '今',
 '年',
 '读',
 '五',
 '级',
 '她',
 '班',
 '上',
 '3',
 '0',
 '多',
 '同',
 '学',
 '该',
 '委',
 '1',
 '长',
 '参',
 '加',
 '步',
 '行',
 '男',
 '轻',
 '也',
 '中',
 '沙',
 '特',
 '队',
 '教',
 '练',
 '佩',
 '雷',
 '拉',
 ':',
 '支',
 '想',
 '胜',
 '因',
 '此',
 '出',
 '了',
 '努',
 '力',
 '种',
 '混',
 '乱',
 '面',
 '导',
 '致',
 '些',
 '使',
 '用',
 '者',
 '合',
 '法',
 '权',
 '益',
 '难',
 '以',
 '维',
 '护',
 '鲁',
 '宾',
 '确',
 '指',
 '对',
 '府',
 '控',
 '完',
 '全',
 '没',
 '事',
 '实',
 '根',
 '据',
 '向',
 '转',
 '敏',
 '感',
 '技',
 '术',
 '相',
 '总',
 '白',
 '于',
 ';',
 '众',
 '议',
 '院',
 '令',
 '非',
 '常',
 '望',
 '将',
 '商',
 '卫',
 '星',
 '受',
 '威',
 '胁',
 '竞',
 '争',
 '损',
 '害',
 '育',
 '场',
 '每',
 '早',
 '6',
 '至',
 '8',
 '免',
 '费',
 '开',
 '放',
 '游',
 '泳',
 '等',
 '则',
 '增',
 '综',
 '服',
 '务',
 '延',
 '采',
 '取',
 '灵',
 '活',
 '收',
 '再',
 '看',
 '容',
 '图',
 '文',
 '并',
 '茂',
 '简',
 '短',
 '字',
 '准',
 '反',
 '映',
 '六',
 '族',
 '风',
 '土',
 '情',
 '传',
 '统',
 '各',
 '讲',
 '很',
 '哥',
 '伦',
 '亚',
 '号',
 '航',
 '飞',
 '机',
 '宇',
 '边',
 '进',
 '验',
 '继',
 '续',
 '抢',
 '修',
 '故',
 '二',
 '氧',
 '碳',
 '清',
 '除',
 '装',
 '置',
 '从',
 '剥',
 '削',
 '阶',
 '劳',
 '动',
 '生',
 '率',
 '低',
 '况',
 '占',
 '限',
 '剩',
 '余',
 '品',
 '足',
 '豪',
 '华',
 '需',
 '量',
 '扩',
 '现',
 '资',
 '条',
 '件',
 '榨',
 '价',
 '值',
 '供',
 '身',
 '求',
 '外',
 '又',
 '本',
 '新',
 '雇',
 '佣',
 '私',
 '来',
 '料',
 '集',
 '代',
 '表',
 '澳',
 '别',
 '区',
 '筹',
 '备',
 '第',
 '次',
 '午',
 '北',
 '京',
 '堂',
 '幕',
 '副',
 '理',
 '任',
 '钱',
 '琛',
 '词',
 '工',
 '经',
 '启',
 '临',
 '紧',
 '迫',
 '们',
 '道',
 '远',
 '希',
 '齐',
 '心',
 '协',
 '平',
 '稳',
 '过',
 '渡',
 '顺',
 '交',
 '接',
 '贡',
 '献',
 '济',
 '程',
 '界',
 '深',
 '信',
 '观',
 '必',
 '须',
 '微',
 '基',
 '础',
 '病',
 '好',
 '颈',
 '背',
 '躯',
 '干',
 '殖',
 '器',
 '可',
 '见',
 '龄',
 '市',
 '柔',
 '县',
 '试',
 '遍',
 '觉',
 '四',
 '节',
 '课',
 '饥',
 '饿',
 '消',
 '川',
 '省',
 '江',
 '油',
 '丰',
 '选',
 '豆',
 '奶',
 '复',
 '营',
 '养',
 '素',
 '贫',
 '血',
 '降',
 '百',
 '照',
 '只',
 '.',
 '4',
 '宗',
 '旨',
 '决',
 '定',
 '奉',
 '甘',
 '愿',
 '吃',
 '亏',
 '苦',
 '享',
 '困',
 '危',
 '留',
 '给',
 '己',
 '方',
 '便',
 '安',
 '初',
 '央',
 '形',
 '势',
 '快',
 '速',
 '策',
 '立',
 '研',
 '究',
 '派',
 '就',
 '提',
 '许',
 '富',
 '意',
 '味',
 '论',
 '话',
 '题',
 '书',
 '记',
 '粟',
 '光',
 '断',
 '他',
 '亲',
 '兄',
 '弟',
 '签',
 '!',
 '熟',
 '悉',
 '运',
 '士',
 '说',
 '贷',
 '款',
 '旦',
 '被',
 '推',
 '迟',
 '几',
 '月',
 '甚',
 '更',
 '考',
 '虑',
 '伍',
 '义',
 '气',
 '原',
 '惠',
 '象',
 '估',
 '像',
 '疤',
 '痕',
 '笨',
 '拙',
 '手',
 '针',
 '线',
 '周',
 '恩',
 '那',
 '送',
 '株',
 '万',
 '古',
 '青',
 '友',
 '谊',
 '红',
 '杉',
 '吧',
 '巴',
 '谈',
 '问',
 '印',
 '声',
 '宣',
 '称',
 '何',
 '三',
 '介',
 '入',
 '久',
 '赴',
 '西',
 '老',
 '访',
 '广',
 '职',
 '积',
 '极',
 '企',
 '监',
 '督',
 '如',
 '居',
 '子',
 '倘',
 '若',
 '客',
 '环',
 '境',
 '钝',
 '迅',
 '判',
 '陷',
 '处',
 '碰',
 '壁',
 '然',
 '热',
 '解',
 '寻',
 '找',
 '联',
 '邦',
 '州',
 '予',
 '适',
 '具',
 '骤',
 '吸',
 '七',
 '团',
 '另',
 '龙',
 '口',
 '科',
 '投',
 '视',
 '息',
 '创',
 '跳',
 '跃',
 '式',
 '展',
 '强',
 '调',
 '高',
 '培',
 '训',
 '认',
 '识',
 '密',
 '结',
 '小',
 '九',
 '升',
 '或',
 '段',
 '少',
 '懂',
 '习',
 '互',
 '贵',
 '贱',
 '请',
 '注',
 '语',
 '构',
 '偏',
 '孝',
 '父',
 '母',
 '始',
 '票',
 '否',
 '制',
 '还',
 '缺',
 '乏',
 '际',
 '电',
 '影',
 '摄',
 '录',
 '音',
 '剪',
 '辑',
 '诸',
 '趋',
 '激',
 '烈',
 '造',
 '奇',
 '夺',
 '起',
 '直',
 '追',
 '医',
 '药',
 '李',
 '珍',
 '闻',
 '屡',
 '仕',
 '寄',
 '托',
 '八',
 '股',
 '兴',
 '趣',
 '酷',
 '爱',
 '召',
 '吴',
 '英',
 '司',
 '董',
 '德',
 '·',
 '葛',
 '夫',
 '邀',
 '席',
 '车',
 '闲',
 '防',
 '施',
 '配',
 '套',
 '存',
 '鹅',
 '洲',
 '源',
 '终',
 '连',
 '串',
 '井',
 '序',
 '顾',
 '犯',
 '罪',
 '讳',
 '审',
 '死',
 '刑',
 '笔',
 '融',
 '农',
 '假',
 '币',
 '知',
 '巧',
 '律',
 '规',
 '村',
 '秩',
 '『',
 '范',
 '茅',
 '迹',
 '览',
 '』',
 '磁',
 '球',
 '孩',
 '格',
 '散',
 '着',
 '独',
 '魅',
 '莱',
 '坞',
 '千',
 '模',
 '翻',
 '版',
 '?',
 '燃',
 '汽',
 '仅',
 '守',
 '—',
 '路',
 '易',
 '斯',
 '切',
 '迪',
 '阿',
 '塞',
 '瓦',
 '萨',
 '卡',
 '里',
 '罗',
 '弗',
 '哈',
 '丹',
 '尼',
 '扎',
 '胡',
 '埃',
 '索',
 '库',
 '纳',
 '吉',
 '莫',
 '帕',
 '锋',
 '米',
 '贝',
 '耶',
 '冈',
 '玻',
 '乌',
 '布',
 '苏',
 '念',
 '案',
 '罚',
 '较',
 '战',
 '略',
 '均',
 '贸',
 '拓',
 '5',
 '份',
 '去',
 '阅',
 '室',
 '刊',
 '晚',
 '智',
 '藏',
 '突',
 '破',
 '借',
 '架',
 '办',
 '沃',
 '盟',
 '昨',
 '约',
 '南',
 '举',
 '空',
 '军',
 '演',
 '示',
 '欢',
 '迎',
 '帮',
 '助',
 '7',
 '叫',
 '迈',
 '菲',
 '查',
 '奋',
 '剂',
 '通',
 '未',
 '勇',
 '曲',
 '绕',
 '桥',
 '绝',
 '《',
 '嫂',
 '》',
 '警',
 '魂',
 '片',
 '获',
 '房',
 '石',
 '香',
 '港',
 '朝',
 '蓬',
 '勃',
 '拼',
 '搏',
 '斗',
 '神',
 '写',
 '2',
 '春',
 '季',
 '招',
 '兵',
 '募',
 '既',
 '食',
 '预',
 '止',
 '癌',
 '症',
 '城',
 '引',
 '项',
 '懈',
 '液',
 '尾',
 '9',
 '%',
 '氢',
 '减',
 '污',
 '染',
 '双',
 '网',
 '抽',
 '封',
 '差',
 '亟',
 '待',
 '单',
 '麦',
 '果',
 '∶',
 '松',
 '赢',
 '测',
 '析',
 '迷',
 '叶',
 '钦',
 '岁',
 '刻',
 '哲',
 '思',
 '虽',
 '担',
 '负',
 '领',
 '责',
 '专',
 '却',
 '爸',
 '劲',
 '抗',
 '艰',
 '聘',
 '仍',
 '治',
 '洋',
 '尊',
 '庄',
 '志',
 '回',
 '忆',
 '淳',
 '朴',
 '报',
 '届',
 '张',
 '君',
 '秋',
 '剧',
 '角',
 '艺',
 '校',
 '坚',
 '质',
 '围',
 '侧',
 '缓',
 '财',
 '状',
 '聚',
 '渠',
 '逐',
 '靠',
 '优',
 '征',
 '稿',
 '截',
 '句',
 '晤',
 '纠',
 '纷',
 '朋',
 '坏',
 '它',
 '促',
 '岗',
 '标',
 '织',
 '践',
 '邓',
 '按',
 '署',
 '挥',
 '核',
 '●',
 '承',
 '键',
 '街',
 '巷',
 '横',
 '幅',
 '悬',
 '画',
 '头',
 '欣',
 '慰',
 ...]
vocab2id
{'海': 0,
 '钓': 1,
 '比': 2,
 '赛': 3,
 '地': 4,
 '点': 5,
 '在': 6,
 '厦': 7,
 '门': 8,
 '与': 9,
 '金': 10,
 '之': 11,
 '间': 12,
 '的': 13,
 '域': 14,
 '。': 15,
 '这': 16,
 '座': 17,
 '依': 18,
 '山': 19,
 '傍': 20,
 '水': 21,
 '博': 22,
 '物': 23,
 '馆': 24,
 '由': 25,
 '国': 26,
 '内': 27,
 '一': 28,
 '流': 29,
 '设': 30,
 '计': 31,
 '师': 32,
 '主': 33,
 '持': 34,
 ',': 35,
 '整': 36,
 '个': 37,
 '建': 38,
 '筑': 39,
 '群': 40,
 '精': 41,
 '美': 42,
 '而': 43,
 '恢': 44,
 '宏': 45,
 '但': 46,
 '作': 47,
 '为': 48,
 '共': 49,
 '产': 50,
 '党': 51,
 '员': 52,
 '、': 53,
 '人': 54,
 '民': 55,
 '公': 56,
 '仆': 57,
 '应': 58,
 '当': 59,
 '胸': 60,
 '怀': 61,
 '宽': 62,
 '阔': 63,
 '真': 64,
 '正': 65,
 '做': 66,
 '到': 67,
 '“': 68,
 '先': 69,
 '天': 70,
 '下': 71,
 '忧': 72,
 '后': 73,
 '乐': 74,
 '”': 75,
 '淡': 76,
 '化': 77,
 '名': 78,
 '利': 79,
 '得': 80,
 '失': 81,
 '和': 82,
 '宠': 83,
 '辱': 84,
 '悲': 85,
 '喜': 86,
 '把': 87,
 '改': 88,
 '革': 89,
 '大': 90,
 '业': 91,
 '摆': 92,
 '首': 93,
 '位': 94,
 '样': 95,
 '才': 96,
 '能': 97,
 '超': 98,
 '越': 99,
 '自': 100,
 '我': 101,
 '脱': 102,
 '世': 103,
 '俗': 104,
 '有': 105,
 '所': 106,
 '发': 107,
 '达': 108,
 '家': 109,
 '急': 110,
 '救': 111,
 '保': 112,
 '险': 113,
 '十': 114,
 '分': 115,
 '普': 116,
 '及': 117,
 '已': 118,
 '成': 119,
 '社': 120,
 '会': 121,
 '障': 122,
 '体': 123,
 '系': 124,
 '重': 125,
 '要': 126,
 '组': 127,
 '部': 128,
 '日': 129,
 '俄': 130,
 '两': 131,
 '政': 132,
 '局': 133,
 '都': 134,
 '充': 135,
 '满': 136,
 '变': 137,
 '数': 138,
 '尽': 139,
 '管': 140,
 '关': 141,
 '目': 142,
 '前': 143,
 '是': 144,
 '历': 145,
 '史': 146,
 '最': 147,
 '佳': 148,
 '时': 149,
 '期': 150,
 '其': 151,
 '脆': 152,
 '弱': 153,
 '性': 154,
 '不': 155,
 '言': 156,
 '明': 157,
 '克': 158,
 '马': 159,
 '尔': 160,
 '女': 161,
 '儿': 162,
 '让': 163,
 '娜': 164,
 '今': 165,
 '年': 166,
 '读': 167,
 '五': 168,
 '级': 169,
 '她': 170,
 '班': 171,
 '上': 172,
 '3': 173,
 '0': 174,
 '多': 175,
 '同': 176,
 '学': 177,
 '该': 178,
 '委': 179,
 '1': 180,
 '长': 181,
 '参': 182,
 '加': 183,
 '步': 184,
 '行': 185,
 '男': 186,
 '轻': 187,
 '也': 188,
 '中': 189,
 '沙': 190,
 '特': 191,
 '队': 192,
 '教': 193,
 '练': 194,
 '佩': 195,
 '雷': 196,
 '拉': 197,
 ':': 198,
 '支': 199,
 '想': 200,
 '胜': 201,
 '因': 202,
 '此': 203,
 '出': 204,
 '了': 205,
 '努': 206,
 '力': 207,
 '种': 208,
 '混': 209,
 '乱': 210,
 '面': 211,
 '导': 212,
 '致': 213,
 '些': 214,
 '使': 215,
 '用': 216,
 '者': 217,
 '合': 218,
 '法': 219,
 '权': 220,
 '益': 221,
 '难': 222,
 '以': 223,
 '维': 224,
 '护': 225,
 '鲁': 226,
 '宾': 227,
 '确': 228,
 '指': 229,
 '对': 230,
 '府': 231,
 '控': 232,
 '完': 233,
 '全': 234,
 '没': 235,
 '事': 236,
 '实': 237,
 '根': 238,
 '据': 239,
 '向': 240,
 '转': 241,
 '敏': 242,
 '感': 243,
 '技': 244,
 '术': 245,
 '相': 246,
 '总': 247,
 '白': 248,
 '于': 249,
 ';': 250,
 '众': 251,
 '议': 252,
 '院': 253,
 '令': 254,
 '非': 255,
 '常': 256,
 '望': 257,
 '将': 258,
 '商': 259,
 '卫': 260,
 '星': 261,
 '受': 262,
 '威': 263,
 '胁': 264,
 '竞': 265,
 '争': 266,
 '损': 267,
 '害': 268,
 '育': 269,
 '场': 270,
 '每': 271,
 '早': 272,
 '6': 273,
 '至': 274,
 '8': 275,
 '免': 276,
 '费': 277,
 '开': 278,
 '放': 279,
 '游': 280,
 '泳': 281,
 '等': 282,
 '则': 283,
 '增': 284,
 '综': 285,
 '服': 286,
 '务': 287,
 '延': 288,
 '采': 289,
 '取': 290,
 '灵': 291,
 '活': 292,
 '收': 293,
 '再': 294,
 '看': 295,
 '容': 296,
 '图': 297,
 '文': 298,
 '并': 299,
 '茂': 300,
 '简': 301,
 '短': 302,
 '字': 303,
 '准': 304,
 '反': 305,
 '映': 306,
 '六': 307,
 '族': 308,
 '风': 309,
 '土': 310,
 '情': 311,
 '传': 312,
 '统': 313,
 '各': 314,
 '讲': 315,
 '很': 316,
 '哥': 317,
 '伦': 318,
 '亚': 319,
 '号': 320,
 '航': 321,
 '飞': 322,
 '机': 323,
 '宇': 324,
 '边': 325,
 '进': 326,
 '验': 327,
 '继': 328,
 '续': 329,
 '抢': 330,
 '修': 331,
 '故': 332,
 '二': 333,
 '氧': 334,
 '碳': 335,
 '清': 336,
 '除': 337,
 '装': 338,
 '置': 339,
 '从': 340,
 '剥': 341,
 '削': 342,
 '阶': 343,
 '劳': 344,
 '动': 345,
 '生': 346,
 '率': 347,
 '低': 348,
 '况': 349,
 '占': 350,
 '限': 351,
 '剩': 352,
 '余': 353,
 '品': 354,
 '足': 355,
 '豪': 356,
 '华': 357,
 '需': 358,
 '量': 359,
 '扩': 360,
 '现': 361,
 '资': 362,
 '条': 363,
 '件': 364,
 '榨': 365,
 '价': 366,
 '值': 367,
 '供': 368,
 '身': 369,
 '求': 370,
 '外': 371,
 '又': 372,
 '本': 373,
 '新': 374,
 '雇': 375,
 '佣': 376,
 '私': 377,
 '来': 378,
 '料': 379,
 '集': 380,
 '代': 381,
 '表': 382,
 '澳': 383,
 '别': 384,
 '区': 385,
 '筹': 386,
 '备': 387,
 '第': 388,
 '次': 389,
 '午': 390,
 '北': 391,
 '京': 392,
 '堂': 393,
 '幕': 394,
 '副': 395,
 '理': 396,
 '任': 397,
 '钱': 398,
 '琛': 399,
 '词': 400,
 '工': 401,
 '经': 402,
 '启': 403,
 '临': 404,
 '紧': 405,
 '迫': 406,
 '们': 407,
 '道': 408,
 '远': 409,
 '希': 410,
 '齐': 411,
 '心': 412,
 '协': 413,
 '平': 414,
 '稳': 415,
 '过': 416,
 '渡': 417,
 '顺': 418,
 '交': 419,
 '接': 420,
 '贡': 421,
 '献': 422,
 '济': 423,
 '程': 424,
 '界': 425,
 '深': 426,
 '信': 427,
 '观': 428,
 '必': 429,
 '须': 430,
 '微': 431,
 '基': 432,
 '础': 433,
 '病': 434,
 '好': 435,
 '颈': 436,
 '背': 437,
 '躯': 438,
 '干': 439,
 '殖': 440,
 '器': 441,
 '可': 442,
 '见': 443,
 '龄': 444,
 '市': 445,
 '柔': 446,
 '县': 447,
 '试': 448,
 '遍': 449,
 '觉': 450,
 '四': 451,
 '节': 452,
 '课': 453,
 '饥': 454,
 '饿': 455,
 '消': 456,
 '川': 457,
 '省': 458,
 '江': 459,
 '油': 460,
 '丰': 461,
 '选': 462,
 '豆': 463,
 '奶': 464,
 '复': 465,
 '营': 466,
 '养': 467,
 '素': 468,
 '贫': 469,
 '血': 470,
 '降': 471,
 '百': 472,
 '照': 473,
 '只': 474,
 '.': 475,
 '4': 476,
 '宗': 477,
 '旨': 478,
 '决': 479,
 '定': 480,
 '奉': 481,
 '甘': 482,
 '愿': 483,
 '吃': 484,
 '亏': 485,
 '苦': 486,
 '享': 487,
 '困': 488,
 '危': 489,
 '留': 490,
 '给': 491,
 '己': 492,
 '方': 493,
 '便': 494,
 '安': 495,
 '初': 496,
 '央': 497,
 '形': 498,
 '势': 499,
 '快': 500,
 '速': 501,
 '策': 502,
 '立': 503,
 '研': 504,
 '究': 505,
 '派': 506,
 '就': 507,
 '提': 508,
 '许': 509,
 '富': 510,
 '意': 511,
 '味': 512,
 '论': 513,
 '话': 514,
 '题': 515,
 '书': 516,
 '记': 517,
 '粟': 518,
 '光': 519,
 '断': 520,
 '他': 521,
 '亲': 522,
 '兄': 523,
 '弟': 524,
 '签': 525,
 '!': 526,
 '熟': 527,
 '悉': 528,
 '运': 529,
 '士': 530,
 '说': 531,
 '贷': 532,
 '款': 533,
 '旦': 534,
 '被': 535,
 '推': 536,
 '迟': 537,
 '几': 538,
 '月': 539,
 '甚': 540,
 '更': 541,
 '考': 542,
 '虑': 543,
 '伍': 544,
 '义': 545,
 '气': 546,
 '原': 547,
 '惠': 548,
 '象': 549,
 '估': 550,
 '像': 551,
 '疤': 552,
 '痕': 553,
 '笨': 554,
 '拙': 555,
 '手': 556,
 '针': 557,
 '线': 558,
 '周': 559,
 '恩': 560,
 '那': 561,
 '送': 562,
 '株': 563,
 '万': 564,
 '古': 565,
 '青': 566,
 '友': 567,
 '谊': 568,
 '红': 569,
 '杉': 570,
 '吧': 571,
 '巴': 572,
 '谈': 573,
 '问': 574,
 '印': 575,
 '声': 576,
 '宣': 577,
 '称': 578,
 '何': 579,
 '三': 580,
 '介': 581,
 '入': 582,
 '久': 583,
 '赴': 584,
 '西': 585,
 '老': 586,
 '访': 587,
 '广': 588,
 '职': 589,
 '积': 590,
 '极': 591,
 '企': 592,
 '监': 593,
 '督': 594,
 '如': 595,
 '居': 596,
 '子': 597,
 '倘': 598,
 '若': 599,
 '客': 600,
 '环': 601,
 '境': 602,
 '钝': 603,
 '迅': 604,
 '判': 605,
 '陷': 606,
 '处': 607,
 '碰': 608,
 '壁': 609,
 '然': 610,
 '热': 611,
 '解': 612,
 '寻': 613,
 '找': 614,
 '联': 615,
 '邦': 616,
 '州': 617,
 '予': 618,
 '适': 619,
 '具': 620,
 '骤': 621,
 '吸': 622,
 '七': 623,
 '团': 624,
 '另': 625,
 '龙': 626,
 '口': 627,
 '科': 628,
 '投': 629,
 '视': 630,
 '息': 631,
 '创': 632,
 '跳': 633,
 '跃': 634,
 '式': 635,
 '展': 636,
 '强': 637,
 '调': 638,
 '高': 639,
 '培': 640,
 '训': 641,
 '认': 642,
 '识': 643,
 '密': 644,
 '结': 645,
 '小': 646,
 '九': 647,
 '升': 648,
 '或': 649,
 '段': 650,
 '少': 651,
 '懂': 652,
 '习': 653,
 '互': 654,
 '贵': 655,
 '贱': 656,
 '请': 657,
 '注': 658,
 '语': 659,
 '构': 660,
 '偏': 661,
 '孝': 662,
 '父': 663,
 '母': 664,
 '始': 665,
 '票': 666,
 '否': 667,
 '制': 668,
 '还': 669,
 '缺': 670,
 '乏': 671,
 '际': 672,
 '电': 673,
 '影': 674,
 '摄': 675,
 '录': 676,
 '音': 677,
 '剪': 678,
 '辑': 679,
 '诸': 680,
 '趋': 681,
 '激': 682,
 '烈': 683,
 '造': 684,
 '奇': 685,
 '夺': 686,
 '起': 687,
 '直': 688,
 '追': 689,
 '医': 690,
 '药': 691,
 '李': 692,
 '珍': 693,
 '闻': 694,
 '屡': 695,
 '仕': 696,
 '寄': 697,
 '托': 698,
 '八': 699,
 '股': 700,
 '兴': 701,
 '趣': 702,
 '酷': 703,
 '爱': 704,
 '召': 705,
 '吴': 706,
 '英': 707,
 '司': 708,
 '董': 709,
 '德': 710,
 '·': 711,
 '葛': 712,
 '夫': 713,
 '邀': 714,
 '席': 715,
 '车': 716,
 '闲': 717,
 '防': 718,
 '施': 719,
 '配': 720,
 '套': 721,
 '存': 722,
 '鹅': 723,
 '洲': 724,
 '源': 725,
 '终': 726,
 '连': 727,
 '串': 728,
 '井': 729,
 '序': 730,
 '顾': 731,
 '犯': 732,
 '罪': 733,
 '讳': 734,
 '审': 735,
 '死': 736,
 '刑': 737,
 '笔': 738,
 '融': 739,
 '农': 740,
 '假': 741,
 '币': 742,
 '知': 743,
 '巧': 744,
 '律': 745,
 '规': 746,
 '村': 747,
 '秩': 748,
 '『': 749,
 '范': 750,
 '茅': 751,
 '迹': 752,
 '览': 753,
 '』': 754,
 '磁': 755,
 '球': 756,
 '孩': 757,
 '格': 758,
 '散': 759,
 '着': 760,
 '独': 761,
 '魅': 762,
 '莱': 763,
 '坞': 764,
 '千': 765,
 '模': 766,
 '翻': 767,
 '版': 768,
 '?': 769,
 '燃': 770,
 '汽': 771,
 '仅': 772,
 '守': 773,
 '—': 774,
 '路': 775,
 '易': 776,
 '斯': 777,
 '切': 778,
 '迪': 779,
 '阿': 780,
 '塞': 781,
 '瓦': 782,
 '萨': 783,
 '卡': 784,
 '里': 785,
 '罗': 786,
 '弗': 787,
 '哈': 788,
 '丹': 789,
 '尼': 790,
 '扎': 791,
 '胡': 792,
 '埃': 793,
 '索': 794,
 '库': 795,
 '纳': 796,
 '吉': 797,
 '莫': 798,
 '帕': 799,
 '锋': 800,
 '米': 801,
 '贝': 802,
 '耶': 803,
 '冈': 804,
 '玻': 805,
 '乌': 806,
 '布': 807,
 '苏': 808,
 '念': 809,
 '案': 810,
 '罚': 811,
 '较': 812,
 '战': 813,
 '略': 814,
 '均': 815,
 '贸': 816,
 '拓': 817,
 '5': 818,
 '份': 819,
 '去': 820,
 '阅': 821,
 '室': 822,
 '刊': 823,
 '晚': 824,
 '智': 825,
 '藏': 826,
 '突': 827,
 '破': 828,
 '借': 829,
 '架': 830,
 '办': 831,
 '沃': 832,
 '盟': 833,
 '昨': 834,
 '约': 835,
 '南': 836,
 '举': 837,
 '空': 838,
 '军': 839,
 '演': 840,
 '示': 841,
 '欢': 842,
 '迎': 843,
 '帮': 844,
 '助': 845,
 '7': 846,
 '叫': 847,
 '迈': 848,
 '菲': 849,
 '查': 850,
 '奋': 851,
 '剂': 852,
 '通': 853,
 '未': 854,
 '勇': 855,
 '曲': 856,
 '绕': 857,
 '桥': 858,
 '绝': 859,
 '《': 860,
 '嫂': 861,
 '》': 862,
 '警': 863,
 '魂': 864,
 '片': 865,
 '获': 866,
 '房': 867,
 '石': 868,
 '香': 869,
 '港': 870,
 '朝': 871,
 '蓬': 872,
 '勃': 873,
 '拼': 874,
 '搏': 875,
 '斗': 876,
 '神': 877,
 '写': 878,
 '2': 879,
 '春': 880,
 '季': 881,
 '招': 882,
 '兵': 883,
 '募': 884,
 '既': 885,
 '食': 886,
 '预': 887,
 '止': 888,
 '癌': 889,
 '症': 890,
 '城': 891,
 '引': 892,
 '项': 893,
 '懈': 894,
 '液': 895,
 '尾': 896,
 '9': 897,
 '%': 898,
 '氢': 899,
 '减': 900,
 '污': 901,
 '染': 902,
 '双': 903,
 '网': 904,
 '抽': 905,
 '封': 906,
 '差': 907,
 '亟': 908,
 '待': 909,
 '单': 910,
 '麦': 911,
 '果': 912,
 '∶': 913,
 '松': 914,
 '赢': 915,
 '测': 916,
 '析': 917,
 '迷': 918,
 '叶': 919,
 '钦': 920,
 '岁': 921,
 '刻': 922,
 '哲': 923,
 '思': 924,
 '虽': 925,
 '担': 926,
 '负': 927,
 '领': 928,
 '责': 929,
 '专': 930,
 '却': 931,
 '爸': 932,
 '劲': 933,
 '抗': 934,
 '艰': 935,
 '聘': 936,
 '仍': 937,
 '治': 938,
 '洋': 939,
 '尊': 940,
 '庄': 941,
 '志': 942,
 '回': 943,
 '忆': 944,
 '淳': 945,
 '朴': 946,
 '报': 947,
 '届': 948,
 '张': 949,
 '君': 950,
 '秋': 951,
 '剧': 952,
 '角': 953,
 '艺': 954,
 '校': 955,
 '坚': 956,
 '质': 957,
 '围': 958,
 '侧': 959,
 '缓': 960,
 '财': 961,
 '状': 962,
 '聚': 963,
 '渠': 964,
 '逐': 965,
 '靠': 966,
 '优': 967,
 '征': 968,
 '稿': 969,
 '截': 970,
 '句': 971,
 '晤': 972,
 '纠': 973,
 '纷': 974,
 '朋': 975,
 '坏': 976,
 '它': 977,
 '促': 978,
 '岗': 979,
 '标': 980,
 '织': 981,
 '践': 982,
 '邓': 983,
 '按': 984,
 '署': 985,
 '挥': 986,
 '核': 987,
 '●': 988,
 '承': 989,
 '键': 990,
 '街': 991,
 '巷': 992,
 '横': 993,
 '幅': 994,
 '悬': 995,
 '画': 996,
 '头': 997,
 '欣': 998,
 '慰': 999,
 ...}

获取每个句子的长度,并得到最大长度

def get_sequence_len(path):
    sequence_len = list()
    tmp_len = 0
    with open(path,'r',encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if len(line) == 0 :
#                 print(tmp_len)
                sequence_len.append(tmp_len)
                tmp_len = 0
            else:
                tmp_len +=1
    return np.array(sequence_len)
train_sequence_len= get_sequence_len('./datasets/example.train')dev_sequence_len= get_sequence_len('./datasets/example.dev')test_sequence_len= get_sequence_len('./datasets/example.test')
train_sequence_len,dev_sequence_len,test_sequence_len
(array([18, 35, 88, ..., 53, 65, 41]), array([69, 35, 54, ..., 46, 38, 29]), array([40, 39, 43, ..., 42, 29, 52]))
max_length = max(max(train_sequence_len),max(dev_sequence_len),max(test_sequence_len))
max_length
577

读取数据,对不足最大长度的句子进行padding

def read_data(path,vocab2id,label2id,max_len):    data_x = list()    data_y = list()    tmp_text = list()    tmp_label = list()        with open(path,'r', encoding='utf-8') as f:        for line in f:            line = line.strip()            if len(line) ==0:                # 数据集中以空行作为句子分隔符,说明前一个句子已经结束,tmp_text为vocab(id),tmp_label为label(id)                # padding 至max_len                tmp_text += [len(vocab2id)] * (max_len - len(tmp_text))                tmp_label += [0] * (max_len - len(tmp_label))                                # 保存已经结束的句子并清空tmp                data_x.append(tmp_text)                data_y.append(tmp_label)                tmp_text = list()                tmp_label = list()            else:                # 将字符和标签转换为相应的id放入tmp                # 根据数据格式,line[0]为vocab,line[1]为空格                # line[2:]为label                tmp_text.append(vocab2id[line[0]])                tmp_label.append(label2id[line[2:]])    print('{} include sequences {}'.format(path,len(data_x)))    return np.array(data_x), np.array(data_y)
train_text,train_label = read_data("./datasets/example.train",vocab2id,label2id,max_length)dev_text,dev_label = read_data("./datasets/example.dev",vocab2id,label2id,max_length)test_text,test_label = read_data("./datasets/example.test",vocab2id,label2id,max_length)
./datasets/example.train include sequences 20864./datasets/example.dev include sequences 2318./datasets/example.test include sequences 4636
train_text[0],train_label[0]
(array([   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,
           8,   11,   12,   13,    0,   14,   15, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465, 4465,
        4465, 4465, 4465, 4465, 4465]),
 array([0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0]))

讲数据通过tf.data.Dataset加载

Dataset可以看作是相同类型“元素”的有序列表,可以通过Iterator对其中的元素进行读取,通过初始化不同的initializer实现读取不同的数据

train_dataset = tf.compat.v1.data.Dataset.from_tensor_slices((train_text,train_label,train_sequence_len))
train_dataset = train_dataset.batch(1)  #指定读取数据时的batch_size
dev_dataset = tf.compat.v1.data.Dataset.from_tensor_slices((dev_text,dev_label,dev_sequence_len))dev_dataset = dev_dataset.batch(1)  #指定读取数据时的batch_size
test_dataset = tf.compat.v1.data.Dataset.from_tensor_slices((test_text,test_label,test_sequence_len))test_dataset = test_dataset.batch(1)  #指定读取数据时的batch_size
iterator = tf.compat.v1.data.Iterator.from_structure(train_dataset.output_types,                                                     train_dataset.output_shapes)
WARNING:tensorflow:From <ipython-input-24-248b28ede763>:1: DatasetV1.output_types (from tensorflow.python.data.ops.dataset_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.compat.v1.data.get_output_types(dataset)`.
WARNING:tensorflow:From <ipython-input-24-248b28ede763>:2: DatasetV1.output_shapes (from tensorflow.python.data.ops.dataset_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.compat.v1.data.get_output_shapes(dataset)`.
#初始化不同的initializer,从X,Y,S读取的数据不同
train_initializer = iterator.make_initializer(train_dataset)
dev_initializer = iterator.make_initializer(dev_dataset)
test_initializer = iterator.make_initializer(test_dataset)
X,Y,S = iterator.get_next() #运行时每次读取一个batch_size的text,label和sequence_len
WARNING:tensorflow:From c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\tensorflow\python\data\ops\iterator_ops.py:336: Iterator.output_types (from tensorflow.python.data.ops.iterator_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.compat.v1.data.get_output_types(iterator)`.
WARNING:tensorflow:From c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\tensorflow\python\data\ops\iterator_ops.py:337: Iterator.output_shapes (from tensorflow.python.data.ops.iterator_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.compat.v1.data.get_output_shapes(iterator)`.
WARNING:tensorflow:From c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\tensorflow\python\data\ops\iterator_ops.py:339: Iterator.output_classes (from tensorflow.python.data.ops.iterator_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.compat.v1.data.get_output_classes(iterator)`.

命名实体识别模型构建

  • 输入文本经过预处理后得到字符的id,
  • 首先经过Embedding层得到字符向量,
  • 然后经过BiLSTM层得到句子表示,
  • 再经过CRF计算每个字符的标签得分,
  • 对于每个字符,选取得分最高的标签作为该字符的类别。
embedding_size = 5
hidden_dim = 4
# embedding层
with tf.compat.v1.variable_scope('embedding'): #随机生成字符向量
    embedding = tf.compat.v1.Variable(
        tf.compat.v1.random_normal([len(vocab2id)+1, embedding_size]),
        dtype= tf.compat.v1.float32
    )
inputs = tf.compat.v1.nn.embedding_lookup(embedding,X) #将X中vocab的id转换为对应的字符向量
# BiLSTM隐藏层
# 前向lstm
lstm_fw_cell = tf.compat.v1.nn.rnn_cell.LSTMCell(hidden_dim,reuse = tf.compat.v1.AUTO_REUSE)
# 后向lstm
lstm_bw_cell = tf.compat.v1.nn.rnn_cell.LSTMCell(hidden_dim,reuse = tf.compat.v1.AUTO_REUSE)
# 组合成双向lstm
(output,output_states) = tf.compat.v1.nn.bidirectional_dynamic_rnn(
    lstm_fw_cell,
    lstm_bw_cell,
    inputs,
    S,
    dtype=tf.compat.v1.float32
)
#list -> tensor [batch_size,max_len,hidden_dim * 2]
output = tf.stack(output,axis=1)
# -> [batch_size * max_len,hidden_dim * 2]
output = tf.reshape(output,[-1,hidden_dim *2])
WARNING:tensorflow:From <ipython-input-28-215bd7d8a5e9>:12: bidirectional_dynamic_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `keras.layers.Bidirectional(keras.layers.RNN(cell))`, which is equivalent to this API
WARNING:tensorflow:From c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\tensorflow\python\ops\rnn.py:447: dynamic_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `keras.layers.RNN(cell)`, which is equivalent to this API
WARNING:tensorflow:From c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\keras\layers\legacy_rnn\rnn_cell_impl.py:979: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor


c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\keras\layers\legacy_rnn\rnn_cell_impl.py:901: UserWarning: `tf.nn.rnn_cell.LSTMCell` is deprecated and will be removed in a future version. This class is equivalent as `tf.keras.layers.LSTMCell`, and will be replaced by that in Tensorflow 2.0.
  warnings.warn("`tf.nn.rnn_cell.LSTMCell` is deprecated and will be "
c:\users\ysilhouette\documents\pyenv\py_365_tf_2\lib\site-packages\keras\engine\base_layer_v1.py:1684: UserWarning: `layer.add_variable` is deprecated and will be removed in a future version. Please use `layer.add_weight` method instead.
  warnings.warn('`layer.add_variable` is deprecated and '
# BiLSTM输出层
with tf.compat.v1.variable_scope('output'):
    W = tf.compat.v1.Variable(
        tf.compat.v1.truncated_normal(
            shape=[hidden_dim *2 , len(label2id)],
            mean=0,
            stddev=0.1),
        dtype=tf.compat.v1.float32
    )
    B = tf.compat.v1.Variable(
        np.zeros(len(label2id)),
        dtype=tf.compat.v1.float32)
    #[batch_size * max_len,label_size]
    Y_out = tf.matmul(output,W) + B 
scores = tf.reshape(Y_out,[-1, max_length,len(label2id)])  #[batch_size,max_len,label_size]
scores
<tf.Tensor 'Reshape_1:0' shape=(None, 577, 7) dtype=float32>
Y
<tf.Tensor 'IteratorGetNext:1' shape=(None, 577) dtype=int32>
S
<tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=int32>
# CRF层
with tf.compat.v1.variable_scope('crf',reuse=tf.compat.v1.AUTO_REUSE):
    trans_matrix = tf.compat.v1.get_variable('transition', [len(label2id),len(label2id)])
log_likelihood,_ =tfa.text.crf_log_likelihood(scores,Y,S,trans_matrix)
loss=tf.reduce_mean(-log_likelihood)
# optimizer = tf.keras.optimizers.SGD(learning_rate=0.5)
# train_op =optimizer.minimize(loss)#梯度下降法,学习率0.1
train_op = tf.compat.v1.train.GradientDescentOptimizer(0.1).minimize(loss)#梯度下降法,学习率0.1
saver = tf.compat.v1.train.Saver() #用来保存和加载模型
min_Loss = int(1e8) #维护当前最小的Loss,初始化为一个较大的值

命名实体识别模型训练

  • 通过训练集训练,并保存当前loss值最小的模型。
  • 构建的模型抽象出来是一个包含计算和变量的图。定义变量时不会进行运算,也没有具体的值,变量在Session中进行初始化后才有值。
  • 在Session中通过run可以执行图,进行相应的计算或取出对应的值。通过训练改变变量的值,最终可以得到模型需要的参数。
  • 变量的值仅在一个Session中有效,通过saver保存变量后,可以在新的Session中通过saver加载变量,就无需再重新训练模型。
sess = tf.compat.v1.Session() # 创建一个Session
sess.run(tf.compat.v1.global_variables_initializer()) # 初始化所有变量
for epoch in range(100):
    sess.run(train_initializer) #初始化训练集和initializer
    for step in range(10): #使用了10个batch的数据
        tf_train_scores,tf_trans_matrix,Loss,_ = sess.run(
            [scores,trans_matrix,loss,train_op]
        ) # 执行训练过程
        
    if Loss <min_Loss:
        saver.save(sess,'model/my_model') # 保存当前loss最小的model
        min_Loss = Loss
    print("** epoch",epoch+1,"Loss",Loss)
** epoch 1 Loss 58.72455
** epoch 2 Loss 53.927124
** epoch 3 Loss 50.51535
** epoch 4 Loss 49.02472
** epoch 5 Loss 50.43283
** epoch 6 Loss 50.08667
** epoch 7 Loss 46.792816
** epoch 8 Loss 44.00418
** epoch 9 Loss 42.740723
** epoch 10 Loss 38.633057
** epoch 11 Loss 34.638123
** epoch 12 Loss 30.238342
** epoch 13 Loss 31.892944
** epoch 14 Loss 28.726074
** epoch 15 Loss 27.086182
** epoch 16 Loss 25.640747
** epoch 17 Loss 24.253967
** epoch 18 Loss 23.045715
** epoch 19 Loss 22.389526
** epoch 20 Loss 21.917236
** epoch 21 Loss 21.551575
** epoch 22 Loss 21.257202
** epoch 23 Loss 21.010925
** epoch 24 Loss 20.801147
** epoch 25 Loss 20.622559
** epoch 26 Loss 20.46753
** epoch 27 Loss 20.330994
** epoch 28 Loss 20.209717
** epoch 29 Loss 20.09906
** epoch 30 Loss 20.002686
** epoch 31 Loss 19.913574
** epoch 32 Loss 19.83197
** epoch 33 Loss 19.756409
** epoch 34 Loss 19.690308
** epoch 35 Loss 19.62616
** epoch 36 Loss 19.563904
** epoch 37 Loss 19.511597
** epoch 38 Loss 19.460754
** epoch 39 Loss 19.409851
** epoch 40 Loss 19.362366
** epoch 41 Loss 19.32257
** epoch 42 Loss 19.281921
** epoch 43 Loss 19.241943
** epoch 44 Loss 19.204895
** epoch 45 Loss 19.17163
** epoch 46 Loss 19.14154
** epoch 47 Loss 19.10846
** epoch 48 Loss 19.077393
** epoch 49 Loss 19.050842
** epoch 50 Loss 19.02295
** epoch 51 Loss 18.999817
** epoch 52 Loss 18.973328
** epoch 53 Loss 18.948425
** epoch 54 Loss 18.927612
** epoch 55 Loss 18.903809
** epoch 56 Loss 18.88678
** epoch 57 Loss 18.864624
** epoch 58 Loss 18.844116
** epoch 59 Loss 18.824219
** epoch 60 Loss 18.807495
** epoch 61 Loss 18.79132
** epoch 62 Loss 18.773804
** epoch 63 Loss 18.757812
** epoch 64 Loss 18.743103
** epoch 65 Loss 18.72815
** epoch 66 Loss 18.71283
** epoch 67 Loss 18.69812
** epoch 68 Loss 18.68335
** epoch 69 Loss 18.670227
** epoch 70 Loss 18.656555
** epoch 71 Loss 18.646484
** epoch 72 Loss 18.633667
** epoch 73 Loss 18.621033
** epoch 74 Loss 18.611145
** epoch 75 Loss 18.603027
** epoch 76 Loss 18.591614
** epoch 77 Loss 18.577148
** epoch 78 Loss 18.568176
** epoch 79 Loss 18.558899
** epoch 80 Loss 18.550049
** epoch 81 Loss 18.5401
** epoch 82 Loss 18.531128
** epoch 83 Loss 18.52301
** epoch 84 Loss 18.51294
** epoch 85 Loss 18.504944
** epoch 86 Loss 18.495544
** epoch 87 Loss 18.486633
** epoch 88 Loss 18.481384
** epoch 89 Loss 18.473328
** epoch 90 Loss 18.466125
** epoch 91 Loss 18.459839
** epoch 92 Loss 18.45227
** epoch 93 Loss 18.444885
** epoch 94 Loss 18.435303
** epoch 95 Loss 18.432373
** epoch 96 Loss 18.423462
** epoch 97 Loss 18.418274
** epoch 98 Loss 18.411194
** epoch 99 Loss 18.403381
** epoch 100 Loss 18.400818

命名实体识别模型评估

计算验证集f1_score并查看验证集预测结果。

  • 精确率pression、召回率recall和f1_score
  • 对于结果仅包括“正负”的二分类问题,
    • “预测为正,实际也为正”被称为真阳性,“预测为正,实际为负”被称为假阳性,“预测为负,实际为正”被称为假阴性,
    • 精确率定义为\(\frac {真阳性}{真阳性+假阳性}\),召回率定义为\(\frac{真阳性}{真阳性+假阴性}\),f1_score可以看作是精确率和召回率的一种加权平均,定义为$ 2*$。
  • 对于多分类问题
    • 既可以分别计算出每一类的精确率、召回率和f1_score
      • 计算某一类时可认为该类为“正”,其他所有类为“负”,以此得到每一类的真阳性、假阳性和假阴性数量),再计算出f1_score的平均值,这种f1_score被称为macro-f1。
    • 也可以计算出总的精确率和召回率
      • 得到每一类的真阳性、假阳性和假阴性数量,分别求和得到总的数量),再计算出f1_score,这种f1_score被称为micro-f1
    • 当预测结果未出现错误类别(即未包含在类别集合的类别)时,这种方式计算得到的精确率、召回率和f1_score都相等,值为$ $。
sess.run(dev_initializer) # 初始化验证集的 initializer
tf_dev_scores,tf_trans_matrix,tf_dev_text,tf_dev_label,tf_dev_sequence_len = sess.run(
    [scores,trans_matrix,X,Y,S]
)
# viterbi_decode每次处理一个句子,这里因为我们设定的batch_size为1,可以直接通过reshape将该维去掉
# 同时去掉句子的padding部分
tf_dev_sequence_len = int(tf_dev_sequence_len)
tf_dev_text = tf_dev_text.reshape(max_length)[:tf_dev_sequence_len]

tf_dev_scores = tf_dev_scores.reshape(max_length,len(label2id))[:tf_dev_sequence_len]
tf_dev_label = tf_dev_label.reshape(max_length)[:tf_dev_sequence_len]

# viterbi_decode传入的scores参数的shape应为[sequence_length,label_size],trans_matix应为[label_size,label_size]
viterbi_sequence,_ = tfa.text.viterbi_decode(tf_dev_scores,tf_trans_matrix)

correct_labels = np.sum(np.equal(viterbi_sequence,tf_dev_label)) # 预测正确的标签数
print("f1_score:{}".format(correct_labels / tf_dev_sequence_len))
print(viterbi_sequence)
f1_score:0.463768115942029
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0]

命名实体识别模型预测

加载模型
sess = tf.compat.v1.Session()
saver.restore(sess,"model/my_model") #加载模型参数
sess.run(test_initializer)  # 初始化测试集的initializer

tf_test_scores,tf_test_matrix,tf_test_text,tf_test_label,tf_test_sequence_len = sess.run(
    [scores,trans_matrix,X,Y,S]
)
tf_test_sequence_len = int(tf_test_sequence_len)
tf_test_text = tf_test_text.reshape(max_length)[:tf_test_sequence_len]

tf_test_scores = tf_test_scores.reshape(max_length,len(label2id))[:tf_test_sequence_len]
tf_test_label = tf_test_label.reshape(max_length)[:tf_test_sequence_len]

viterbi_sequence,_ = tfa.text.viterbi_decode(tf_test_scores,tf_trans_matrix)

example_test_text = [id2vocab[vocab] for vocab in tf_test_text] # id转换为vocab
example_prediction_label = [id2label[label] for label in viterbi_sequence] # id转换为label

print([id2vocab[tf_test_text[index]] + '' + id2label[viterbi_sequence[index]] for index in range(len(tf_test_text))])
sess.close()
INFO:tensorflow:Restoring parameters from model/my_model
['我I-ORG', '们I-ORG', '变I-ORG', '而O', '以O', '书O', '会O', '友O', ',O', '以O', '书O', '结O', '缘O', ',O', '把O', '欧O', '美O', '、O', '港O', '台O', '流B-ORG', '行I-ORG', '的I-ORG', '食I-ORG', '品I-ORG', '类I-ORG', '图I-ORG', '谱I-ORG', '、I-ORG', '画I-ORG', '册I-ORG', '、I-ORG', '工I-ORG', '具I-ORG', '书I-ORG', '汇I-ORG', '集I-ORG', '一I-ORG', '堂I-ORG', '。O']
example_test_text
['我',
 '们',
 '变',
 '而',
 '以',
 '书',
 '会',
 '友',
 ',',
 '以',
 '书',
 '结',
 '缘',
 ',',
 '把',
 '欧',
 '美',
 '、',
 '港',
 '台',
 '流',
 '行',
 '的',
 '食',
 '品',
 '类',
 '图',
 '谱',
 '、',
 '画',
 '册',
 '、',
 '工',
 '具',
 '书',
 '汇',
 '集',
 '一',
 '堂',
 '。']
example_prediction_label 
['I-ORG',
 'I-ORG',
 'I-ORG',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'B-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'I-ORG',
 'O']

结果展示

exam_test_text = ['我','们','变','而','以','书','会','友',',','以','书','结','缘',',',
                     '把','欧','美','、','港','台','流','行','的','食','品','类','图','谱','、',
                     '画','册','、','工','具','书','汇','集','一','堂','。']
exam_prediction_label = ['O','O','O','O','O','O','O','O','O','O','O','O','O','O',
                            'O','B-LOC','B-LOC','O','B-LOC','B-LOC','O','O','O','O','O','O','O','O','O',
                            'O','O','O','O','O','O','O','O','O','O','O']
def get_named_entity(text,labels):
    named_entity_set = set() #去重,同一个实体只记录一次
    named_entity_list = list()
    cur_type= ''
    is_entity = False
    tmp_named_entity= ''
    
    for index in range(len(text)):
        label = labels[index]
        if label =='O' or label[:2] == 'B-': # 可能是前一个实体的结束
            if is_entity == True: # 一个实体的结束
                if tmp_named_entity not in named_entity_set:
                    named_entity_set.add(tmp_named_entity)
                    named_entity_list.append({'text':tmp_named_entity,'type':cur_type})
                
                is_entity =False
                tmp_named_entity =''
            if label == 'O':
                continue
            cur_type = label[2:] # 若为'B-',说明此时是回一个实体的开头
            is_entity =True
            tmp_named_entity += text[index]
        else: # 'I-'
            tmp_named_entity += text[index]
    return named_entity_list
named_entity_list = get_named_entity(exam_test_text,exam_prediction_label)
print(named_entity_list)
[{'text': '欧', 'type': 'LOC'}, {'text': '美', 'type': 'LOC'}, {'text': '港', 'type': 'LOC'}, {'text': '台', 'type': 'LOC'}]

About ME

👋 读书城南,🤔 在未来面前,我们都是孩子~
  • 📙 一个热衷于探索学习新方向、新事物的智能产品经理,闲暇时间喜欢coding💻、画图🎨、音乐🎵、学习ing~
👋 Social Media
👋 加入小组~

👋 感谢打赏~