使用Bert进行NER命名实体识别feat.fastNLP(下:使用flask部署模型)

本文最后更新于:2023年3月25日 晚上

前言

下篇主要介绍使用flask搭建简单的网页和响应,并实现调用模型推理,完成NER任务可视化。

增加bilstm+crf的模型确实在预测上更为准确,例子中的“崔永元真面”中可以准确分辨出"崔永元"才是人名,而mlp模型会将“崔永元真”作为一个人名🤣

项目工程的Github仓库地址为:https://github.com/Ash-one/ChineseBert-finetuned-NER

结果展示

服务器使用flask响应get请求,可视化结果部分使用python的displacy库生成HTML直接插入原始HTML。

Flask服务端

flask工程的目录结构如下,程序的入口在run.py中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── app
│ ├── __init__.py
│ ├── loader.py
│ ├── model.py
│ ├── run.py
│ ├── static
│ │ ├── bg.jpg
│ │ ├── js
│ │ │ └── mystyle.css
│ │ └── models
│ │ ├── rbt3-bilstm-crf-ner.pth
│ │ ├── rbt3-bilstm-crf-ner.txt
│ │ ├── rbt3-mlp-ner.pth
│ │ └── rbt3-mlp-ner.txt
│ └── templates
│ └── index.html
├── test.py

基础路由

只要在templates文件夹下放置好html文件,就可以直接通过render_template方法渲染出页面。

1
2
3
4
5
6
7
8
9
10
11
from flask import Flask, request, make_response
from flask import Response, render_template
import loader
from model import BertNER,BertBilstmCrfNER
from spacy import displacy

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

响应get请求

下面这个装饰器专门响应/predict路径的get请求。

这里由于只需要根据输入参数返回预测结果,故选择使用get请求,检查参数合法后加载对应模型预测,并对预测结果进行后处理,由displacy库渲染为HTML,最后返回响应结果。

displacy官方表示最好不要在服务器端渲染得到HTML,而是使用他们提供的js文件在客户端渲染,而那个js文件的仓库已经被archived了,不得不用这种方法......

由于在这个文件中需要获取到模型,因此需要在import阶段加载定义的pytorch模型。加载模型推理的部分比较简单,上篇有所提及,这里不做展示,需要注意的是displacy库的输入格式是形如{'text': '我就要在中国传媒大学吃上崔永元真面', 'ents': [{'label': 'GPE.NAM', 'start': 4, 'end': 6}, {'label': 'ORG.NAM', 'start': 6, 'end': 10}, {'label': 'PER.NAM', 'start': 12, 'end': 15}], 'title': None}的字典,在得到模型预测序列后需要多一步转换为字典。

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
@app.get('/predict')
def predict__text():
text = request.args.get('text')
model_name = request.args.get('model')
print('text:',text,'model_name:',model_name)
# 下面对不合法或无法读取的模型进行处理
try:
model,label_idx_list = loader.load_model_from_file(model_name)
except IOError:
return 'IOError'
if model == None or label_idx_list == None:
return 'Model not found'

result = {}
result['text'] = text
numbers = [int(x) for x in loader.predict(text,model)]

labels = loader.convert_result2label(numbers,label_idx_list)
entities = loader.convert_label2entity(labels)

result['ents'] = entities
result['title'] = None
print(result)

# 配置需要展示的实体,从loader中加载颜色配置
options = {"ents": ["ORG.NAM",
"GPE.NAM",
"PER.NAM",
"LOC.NAM",
"LOC.NOM",
"PER.NOM",
"ORG.NOM"
], "colors": loader.load_colors_preset()}
html = displacy.render(result, style="ent", manual=True, options=options)

response = make_response(html,200)
return response

Web客户端

预测过程在个人服务器上会花费较多时间,因此需要在页面中使用js异步控制发送get请求和获取响应,所以不能使用表单form提交get请求,不然会一直卡住不动而实际上是在等待响应,类似于客户端多线程请求,这里使用js的fetch发送get请求,并修改页面内容表示正在等待响应,等收到响应后修改html文件。

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
<script>
function submitForm() {
var text = document.getElementById("my-text").value;
var model = document.querySelector('input[name="model"]:checked').value;
var waiting = document.getElementById("waiting-text");
waiting.innerHTML = '正在预测...';
var displacy = document.getElementById("visual");
var url = "/predict?text=" + text + "&model=" + model;
const response = fetch(url, {
method: 'GET',
}).then(function (response) {
return response.text();
})
.then(function (data) {
console.log(data);
waiting.innerHTML = '';
displacy.innerHTML = data;
console.log("ok!!!");
});
}
</script>
......
<div id="input">
<h3>输入待预测文本:</h3>
<textarea type="text" id="my-text" name="text" rows="3" cols="20">我就要在中国传媒大学吃上崔永元真面</textarea><br>
<h3>选择一个模型:</h3>
<input type="radio" name="model" value="rbt3-mlp-ner" checked="true">rbt3-mlp-ner<br>
<input type="radio" name="model" value="rbt3-bilstm-crf-ner">rbt3-bilstm-crf-ner<br>
<input type="submit" id="my-button" value="提交开始预测" onclick="submitForm()">
</div>

使用Bert进行NER命名实体识别feat.fastNLP(下:使用flask部署模型)
https://ash-one.github.io/2023/03/25/shi-yong-bert-jin-xing-ner-ming-ming-shi-ti-shi-bie-feat-fastnlp-xia-shi-yong-flask-bu-shu-mo-xing/
作者
灰一
发布于
2023年3月25日
许可协议