本文最后更新于: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_responsefrom flask import Response, render_templateimport loaderfrom model import BertNER,BertBilstmCrfNERfrom 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) 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 >