Kashgari概述
Kashgari 是一个极简且强大的 NLP 框架,可用于文本分类和标注的学习,研究及部署上线
- 方便易用 Kashgari 提供了简洁统一的 API 和完善的文档,使其非常方便易用。
- 内置迁移学习模块 Kashgari 通过提供
BertEmbedding
,GPT2Embedding
,WordEmbedding
等特征提取类, 方便利用预训练语言模型实现迁移学习。 - 易扩展 Kashgari 提供简便的接口和继承关系,自行扩展新的模型结构非常方便。
- 可用于生产 通过把 Kashgari 模型导出为
SavedModel
格式,可以使用 TensorFlow Serving 模块提供服务,直接在线上环境使用。 - Kashgari 的Github 网址
- Kashgari 本质上是基于keras的高层封装,所以使用格式,性能方面是相差无多的。
Kashgari 主要用来文本分类与标注,目前本文主要介绍了文本标注(NER)的使用 以及填坑,以后有时间会把分类的坑填上
NER 命名实体识别
安装
Kashgari 项目基于 Tensorflow 1.14.0
和 Python 3.6+
目前也有 tf2.0 的版本支持,但目前没有 tf1.4 稳定
pip install kashgari
# CPU
pip install tensorflow==1.14.0
# GPU
pip install tensorflow-gpu==1.14.0
载入数据
这里model的数据格式是固定的,如果要使用自己的数据,需要认真效验自己的数据格式。
我遇到的几个问题:
- 空格 \t 与 ‘ ‘区别
- entity实体值缺省的,在对应的标注位置上
- 标注规范支持BIO(推荐)、BIEO、SIEO、SIO
# 加载内置数据集,此处可以替换成自己的数据集,保证格式一致即可
# load internal data
from kashgari.corpus import ChineseDailyNerCorpus
train_x, train_y = ChineseDailyNerCorpus.load_data('train')
valid_x, valid_y = ChineseDailyNerCorpus.load_data('validate')
test_x, test_y = ChineseDailyNerCorpus.load_data('test')
# load own data
from kashgari.corpus import DataReader
train_x, train_y = DataReader().read_conll_format_file('./data/surround/train.txt')
valid_x, valid_y = DataReader().read_conll_format_file('data/surround/dev.txt')
test_x, test_y = DataReader().read_conll_format_file('./data/surround/test.txt')
print(f"train data count: {len(train_x)}")
print(f"validate data count: {len(valid_x)}")
print(f"test data count: {len(test_x)}")
创建 embedding
create Bare embedding
在 BareEmbedding 中参数sequence_length
有三种模式:
- ‘auto’: 这里默认的是 ‘auto’,即将语料库长度的 95% 作为序列长度
- ‘variable’: 模型输入形状将设置为 None ,可以处理各种长度的输入,它将使用每批中最大序列的长度作为序列长度
- ‘int值’: 如果使用整数,假设为 50,则输入输出序列长度将设置为 50
这里我选择的是 ‘variable’ 模式,因为在下面的model推理(tensorflow serving)的时候,能够自适应语句的长度值, 不会有多余的 ‘PAD’值。
from kashgari.embeddings import BareEmbedding
bare_embed = BareEmbedding(task=kashgari.LABELING,
sequence_length='variable',
embedding_size=300)
create BERT embedding
如果使用 BERTEmbedding
编码的时候,sequence_length
模式默认设置的只接受’int值’ 🙉
BERT_PATH 中的 '<bert-model-folder>'
是我们下载的 bert 预训练模型(pre−training)的文件路径,
比如 BERT_PATH = './models/bert_base_models/chinese_L-12_H-768_A-12/'
google 官方提供的中文bert模型的下载地址google-research-bert
from kashgari.embeddings import BERTEmbedding
BERT_PATH = '<bert-model-folder>'
bert_embed = BERTEmbedding(BERT_PATH,
task=kashgari.LABELING,
sequence_length=50)
对于使用 GPT-2 embedding 的方法与 BERT embedding 是类似的。
openAI GPT-2 官方提供的两种预训练模型:small(117M),medium(345M),large 版本并没有发布,据说是【他们认为如此强力的模型有遭到恶意滥用的风险】🙉 模型的下载地址为openai-gpt-2
但对于中文的支持并不良好,有兴趣研究中文版的 GPT-2 模型的小伙伴请移步GPT2-Chinese
选择 model
Kashgari 提供的 model有:
CNN_LSTM_Model
, BiLSTM_Model
, BiGRU_Model
or BiGRU_CRF_Model
, BiLSTM_CRF_Model
from kashgari.tasks.labeling import BiLSTM_CRF_Model
model = BiLSTM_CRF_Model(bare_embed)
# or model = BiLSTM_CRF_Model(bert_embed)
# or model = BiLSTM_CRF_Model(gpt2_embed)
可视化选择
这是 Kashgari 内置回调函数,会在训练过程计算精确度,召回率和 F1
from kashgari.callbacks import EvalCallBack
# check $ tensorboard --logdir logs
tf_board_callback = keras.callbacks.TensorBoard(log_dir='./logs', update_freq=1000)
eval_callback = EvalCallBack(kash_model=model,
valid_x=valid_x,
valid_y=valid_y,
step=5)
训练模型
选择可视化
model.fit(train_x,
train_y,
x_validate=valid_x,
y_validate=valid_y,
epochs=5,
batch_size=32,
callbacks=[eval_callback, tf_board_callback])
不选择可视化
model.fit(train_x,
train_y,
x_validate=valid_x,
y_validate=valid_y,
epochs=1,
batch_size=32)
评估模型
评估模型的具体 metrics ,请查看相关的 api 文档
model.evaluate(test_x, test_y)
保存模型
可以选择其他的库函数来保存模型,这里选择的函数是方便下面 tensorflow sering
的推理;
注意 model_path
的路径格式。
from kashgari import utils
utils.convert_to_saved_model(model,
model_path='./save_models/ner',
version='1')
results test
为了快速验证 model 的结果:
数据使用的是内置的人名日报的数据集(ChineseDailyNerCorpus)
使用的编码是 BareEmbedding
,epoch
只设了1轮的结果。
train data count: 20864
validate data count: 2318
test data count: 4636
Model: "model_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input (InputLayer) [(None, None)] 0
_________________________________________________________________
layer_embedding (Embedding) (None, None, 300) 1050000
_________________________________________________________________
layer_blstm (Bidirectional) (None, None, 256) 439296
_________________________________________________________________
layer_dense (Dense) (None, None, 64) 16448
_________________________________________________________________
layer_crf_dense (Dense) (None, None, 8) 520
_________________________________________________________________
layer_crf (CRF) (None, None, 8) 64
=================================================================
Total params: 1,506,328
Trainable params: 1,506,328
Non-trainable params: 0
_________________________________________________________________
653/653 [==============================] - 350s 536ms/step - loss: 15.2126 - accuracy: 0.9662 - val_loss: 157.9817 - val_accuracy: 0.8512
precision recall f1-score support
ORG 0.4330 0.4050 0.4185 2185
LOC 0.6217 0.5831 0.6018 3658
PER 0.6569 0.6604 0.6586 1864
micro avg 0.5778 0.5513 0.5642 7707
macro avg 0.5767 0.5513 0.5636 7707
同样的人名日报的数据集,同样的超参数设置,也跑 1 个epoch
,使用的编码换成 BERTEmbedding
的结果。
653/653 [==============================] - 124s 190ms/step - loss: 2.5958 - accuracy: 0.9820 - val_loss: 51.5484 - val_accuracy: 0.9898
precision recall f1-score support
LOC 0.8402 0.9115 0.8744 3016
ORG 0.7987 0.8339 0.8159 1932
PER 0.9248 0.9486 0.9365 1594
micro avg 0.8476 0.8976 0.8719 6542
macro avg 0.8485 0.8976 0.8722 6542
模型推理
docker 镜像挂载模型
通过 docker 安装tensorflow/serving
镜像,然后将服务挂载到本地的 8501 端口,
其中"/Users/shf/PycharmProjects/Kashgari/save_models:/models"
是本地生成模型的路径格式
如果是远程服务器上进行模型推理,过程是相似的
docker run -t --rm -p 8501:8501 -v "/Users/shf/PycharmProjects/Kashgari/save_models:/models" -e MODEL_NAME=ner tensorflow/serving
载入模型
from kashgari import utils
processor = utils.load_processor(model_path='./save_models/ner/1')
预处理数据
test_x = ['中', '国', '队', '将', '于', '北', '京', '对', '阵', '来', '着', '中', '东', '的', '叙', '利', '亚', '队', '。']
tensor = processor.process_x_dataset(data=[test_x])
如果使用 BERTEmbedding
,需要多一步经过下面的预处理
# if you using BERT, you need to reformat tensor first
# ------ Only for BERT Embedding Start --------
import numpy as np
tensor = [{
"Input-Token:0": i.tolist(),
"Input-Segment:0": np.zeros(i.shape).tolist()
} for i in tensor]
# ------ Only for BERT Embedding End ----------
进行预测
注意 Bare embedding 与 BERT embedding使用区别
import requests
r = requests.post("http://localhost:8501/v1/models/ner:predict", json={"instances": tensor.tolist()}) # Bare embedding
# or r = requests.post("http://localhost:8501/v1/models/ner:predict", json={"instances": tensor}) # BERT embedding
preds = r.json()['predictions']
# Convert result back to labels
pred = np.array(preds).argmax(-1)
labels = processor.reverse_numerize_label_sequences(pred)
print(test_x)
print(labels)
results predict
['中', '国', '队', '将', '于', '北', '京', '对', '阵', '来', '着', '中', '东', '的', '叙', '利', '亚', '队', '。']
[['B-ORG', 'I-ORG', 'I-ORG', 'O', 'O', 'B-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'B-LOC', 'I-LOC', 'O', 'B-ORG', 'I-ORG', 'I-ORG', 'I-ORG', 'O']]