A211-基于改进创新的IDCNN和bilstm两种主干网络结合crf实现ner中文实体识别
【购买前必看】
1、关于我们
深度学习乐园是由python哥全职技术团队组建运营【团队成员为:复旦大学博士、华东理工爱丁堡博士、格拉斯哥博士、纽约大学硕士、浙江大学硕士】。
我们只做python业务,精通sklearn机器学习/torch深度学习/django/flask/vue全栈开发。
2、关于项目
我们从2018年开始,就专注于深度学习sci、ei、ccf、kaggle等,至今已有7年,共发表过10多篇顶刊顶会。
官网累积了数百个项目,已有3000多学员付费购买,圈子内有口皆碑:www.zzgcz.com (更多高级私密项目无法对外,联系微信定制:zzgcz_com)
3、售后承诺
包远程安装调试,所有项目均在本地运行通过,大部分都有截图和录屏。
支持二次修改,所有项目都是我们自己写的,改起来也非常容易。
加急定制1-2天可完成,这就是实力证明,远程验收满意后再付全款!
所有客户终身售后。兼职的人家都有主业,谁愿意持续服务你?
实体识别和关系抽取是例如构建知识图谱等上层自然语言处理应用的基础。实体识别可以简单理解为一个序列标注问题:给定一个句子,为句子序列中的每一个字做标注。因为同是序列标注问题,除去实体识别之外,相同的技术也可以去解决诸如分词、词性标注等不同的自然语言处理问题。
说到序列标注直觉是会想到RNN的结构。现在大部分表现最好的实体识别或者词性标注算法基本都是biLSTM的套路。就算是上篇提到的关系抽取这种本来应该很适合CNN来做的文本分类的问题,我们也用了biGRU加字级别与句子级别的双重Attention结构解决掉了。
IDCNN加CRF模型
对于序列标注来讲,普通CNN有一个劣势,就是卷积之后,末层神经元可能只是得到了原始输入数据中一小块的信息。而对NER来讲,整个句子的每个字都有可能都会对当前需要标注的字做出影响。为了覆盖到输入的全部信息就需要加入更多的卷积层, 导致层数越来越深,参数越来越多,而为了防止过拟合又要加入更多的Dropout之类的正则化,带来更多的超参数,整个模型变得庞大和难以训练。因为CNN这样的劣势,大部分序列标注问题人们还是使用biLSTM之类的网络结构,尽可能使用网络的记忆力记住全句的信息来对单个字做标注。
但这带来的问题是,biLSTM毕竟是一个序列模型,在对GPU并行计算的优化上面不如CNN那么强大。如何能够像CNN那样给GPU提供一个火力全开的战场,而又像LSTM这样用简单的结构记住尽可能多的输入信息呢?
Fisher Yu and Vladlen Koltun 2015 提出了一个dilated CNN的模型,意思是“膨胀的”CNN。想法其实很简单:正常CNN的filter,都是作用在输入矩阵一片连续的位置上,不断sliding做卷积。dilated CNN为这片filter增加了一个dilation width,作用在输入矩阵的时候,会skip掉所有dilation width中间的输入数据;而filter矩阵本身的大小仍然不变,这样filter获取到了更广阔的输入矩阵上的数据,看上去就像是“膨胀”了一般。
具体使用时,dilated width会随着层数的增加而指数增加。这样随着层数的增加,参数数量是线性增加的,而receptive field却是指数增加的,可以很快覆盖到全部的输入数据。

对应在文本上,输入是一个一维的向量,每个元素是一个character embedding:
图片来自Emma Strubell, Patrick Verga, David Belanger, Andrew McCallum 2017

我们的模型是4个大的相同结构的Dilated CNN block拼在一起,每个block里面是dilation width为1, 1, 2的三层Dilated卷积层,所以叫做 Iterated Dilated CNN。
IDCNN对输入句子的每一个字生成一个logits,这里就和biLSTM模型输出logits之后完全一样,放入CRF Layer,用Viterbi算法解码出标注结果。
在biLSTM或者IDCNN这样的深度网络模型后面接上CRF层是一个序列标注很常见的方法。biLSTM或者IDCNN计算出的是每个词分类的概率,而CRF层引入序列的转移概率,最终计算出loss反馈回网络。
项目结构:
main.py
-
入口脚本:包括
train()函数和evaluate_line()等逻辑。 -
负责加载数据、调用
Model进行训练与评估、保存模型等。
model.py(或同等文件名)
-
定义
Model类,封装了 BiLSTM/IDCNN + CRF 的核心实现: -
Embedding 层
-
BiLSTM / IDCNN 层
-
CRF 层
-
Forward + Loss + Decode + Evaluate
data_utils.py
-
提供数据相关的辅助函数:
-
读取/处理文本 (
load_sentences,prepare_dataset等) -
字典映射/统计 (
create_dico,create_mapping) -
IOB/IOBES 格式转换 (
iob2,iob_iobes) -
分词特征 (
get_seg_features,调用jieba) -
Batch 管理 (
BatchManager)
rnncell.py
-
自定义或封装的 RNN/LSTM 单元,例如 CoupledInputForgetGateLSTMCell。
-
主要给
Model中的 BiLSTM 部分使用。
utils.py
- 一些通用工具函数,如日志输出、结果序列转换 (
result_to_json)、保存或加载模型等。
数据准备与预处理:
语料获取
中文实体识别的公开语料也是非常之少。
现在有很多带有中文实体识别功能的软件包,可以参考awesome chinese nlp。理论上是可以爬下来大量未标注中文语料,然后用这些软件包生成实体识别的标注数据。但是我试了几个发现这些软件包在实际情况中应用的准确率也不是非常高,生成的标注数据质量并不是很好。
另一个想法依然是像distant supervision一样,从已知的不同实体的词库出发,爬取含有相关词库的语料作为标注数据。然而精力有限并没有时间做这件事情。。
数据格式
-
一般每行对应一个词或字符 + 其标注标签,如:
-
mathematica
-
复制编辑
-
我 O爱 O北 B-LOC京 I-LOC... -
每个句子之间用空行分隔;对于 CoNLL 格式,可能包含特殊的
DOCSTART标记行。
主要预处理步骤
-
load_sentences(path, lower, zeros): 逐行读取文件,把句子加载为 [[[word, tag], [word, tag], ...], ...] 结构;可选将数字替换为0(zeros)、英文转小写(lower)。 -
update_tag_scheme(sentences, tag_schema): 将所有标签转换为 IOB2 或 IOBES 格式;如果检测到旧格式 IO(B1),则自动升级成 IOB2。 -
char_mapping+tag_mapping: 扫描训练集的所有字符、标签,统计出现频次并生成映射表(char_to_id、id_to_tag 等)。 预训练向量时,可能会用augment_with_pretrained把额外字符合并进字典。 -
prepare_dataset(sentences, char_to_id, tag_to_id, lower, train=True): 将原始 [[word, tag], ...] 转成数值索引 [chars_id, segs_id, tags_id] 方便模型使用;同时会生成分词特征segs。 -
BatchManager: -
在训练或评估时,将数据按
batch_size分割并做 padding; -
提供
iter_batch(shuffle=True/False)来一批一批吐出数据。
模型主要模块说明
-
Embedding 层
-
包含 字符嵌入 (char_embedding)
-
以及 分词特征嵌入 (seg_embedding),根据配置可选。
-
网络结构
-
BiLSTM:利用双向 LSTM 提取上下文特征。
-
IDCNN:多层空洞卷积结构 (dilated convolution) 并行捕获不同感受野。
-
投影层 (Project Layer)
-
BiLSTM 输出先经过一层全连接 hidden,再投影到
num_tags。 -
IDCNN 输出直接投影到
num_tags。 -
CRF 层与损失
-
在 logits 基础上使用 CRF(log-likelihood) 计算 loss。
-
通过
viterbi_decode解码得到最优标签路径。 -
训练与推断
-
loss_layer中定义了 CRF loss。 -
run_step中根据is_train决定是否执行self.train_op,并返回相应结果。 -
evaluate与evaluate_line使用 decode 后的标签结果做后处理 (如 iobes->iob),并输出可视化或 JSON 格式。
训练、评估与推断
训练主函数(train() in main.py)
-
准备数据:
-
调用
load_sentences+update_tag_scheme+char_mapping/tag_mapping+prepare_dataset获取训练集、验证集、测试集。 -
创建 BatchManager:
-
例如
train_manager = BatchManager(train_data, batch_size)。 -
构建或加载模型:
-
通过
model = create_model(sess, Model, ...)(此函数通常封装模型初始化/恢复操作)。 -
训练循环:
-
for 循环若干 epoch;
-
对
train_manager.iter_batch(shuffle=True)的每个batch执行model.run_step(sess, True, batch); -
每隔
steps_check步打印一次 loss; -
每个 epoch 后调用
evaluate(sess, model, "dev", ...)等在dev上验证,若效果提升则save_model。 -
测试集评估:
-
同样
evaluate(sess, model, "test", ...)。
评估(批量)
model.evaluate:对批次数据 decode 得到预测标签,再与真实标签一起输出或保存;可计算 F1、Precision、Recall 等指标。
单句推断(在线预测)
-
evaluate_line:对输入的文本(行)做预处理(全角转半角、HTML符号替换),将字符映射为 ID,获取seg_features; -
调用
run_step(sess, False, inputs)拿到 scores,再decode; -
最终调用
result_to_json返回可读格式结果。
系统要求配置:
-
Python (>=3.5)
-
TensorFlow (>=r1.0)
-
jieba (>=0.37) 正在试图在不用jieba分词的情况下提高模型表现
训练:
1.所有数据在 data/ , 包括了训练数据(example.train)、验证数据(example.dev)和测试数据(example.test),以及在wikipedia中文语料上预训练好的中文的字向量(vec.txt)。
2.进行训练,验证集上表现最好的模型会存储在 ckpt/ 下面。
要使用IDCNN训练,运行:
要使用biLSTM训练,运行:
预测:
方式是用户直接从命令行输入句子,然后进行实体识别。
如果你已经训练好了自己的模型在ckpt/ 下面,那么运行:
我们也提供了分别预训练好的IDCNN和biLSTM的命名实体识别模型。
要测试IDCNN模型,运行:
要测试biLSTM模型,运行:
运行结果:
