NLP(十四)自制序列标注平台
背景介绍
在平时的NLP任务中,我们经常用到命名实体识别(NER),常用的识别实体类型为人名、地名、组织机构名,但是我们往往也会有识别其它实体的需求,比如时间、品牌名等。在利用算法做实体识别的时候,我们一般采用序列标注算法,这就对标注的文本格式有一定的要求,因此,一个好的序列标注的平台必不可少,将会大大减少我们标注的工作量,有效提升算法的更新迭代速度。
本文将介绍笔者的一个工作:自制的序列标注平台。我们以时间识别为例。比如,在下面的文章中:
按计划,2019年8月10日,荣耀智慧屏将在华为开发者大会上正式亮相,在8月6日,荣耀官微表示该产品的预约量已破十万台,8月7日下午,荣耀总裁赵明又在微博上造势率先打出差异化牌,智慧屏没有开关机广告,并表态以后也不会有,消费者体验至上,营销一波接一波,可谓来势汹汹。
我们需要从该文章中标注出三个时间:2019年8月10日
,8月6日
,8月7日下午
,并形成标注序列。
下面将详细介绍笔者的工作。
序列标注平台
由于开发时间仓促以及笔者能力有限,因此,序列标注平台的功能还没有很完善,希望笔者的工作能抛砖引玉。
项目的结构图如下:
templates中存放静态资源,time_index.html为平台的操作界面,time_output为平台标注完实体后的文件保存路径,time_server.py是用tornado写的服务端路径控制代码,utils.py中是获取某个路径下的txt文件的最大数值的函数。
其中,utils.py的完整代码如下:
# -*- coding: utf-8 -*-
# time: 2019-03-14
# place: Xinbeiqiao, Beijing
import os
# 获取当前所在目录的txt文本的最大数值
def get_max_num(path):
files = os.listdir(path)
if files:
numbers = list(map(lambda x: int(x.replace('.txt', '')), files))
return max(numbers)
else:
return 0
time_server.py的完整代码如下:
# -*- coding: utf-8 -*-
# time: 2019-08-08
# place: Xinbeiqiao, Beijing
import os.path
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
from utils import get_max_num
#定义端口为9005
define("port", default=9005, help="run on the given port", type=int)
# GET请求
class QueryHandler(tornado.web.RequestHandler):
# get函数
def get(self):
self.render('time_index.html', data = ['', []])
# POST请求
class PostHandler(tornado.web.RequestHandler):
# post函数
def post(self):
# 获取前端参数, event, time, index
event = self.get_argument('event')
times = self.get_arguments('time')
indices = self.get_arguments('index')
print(event)
print(times)
print(indices)
# 前端显示序列标注信息
tags = ['O'] * len(event)
for time, index in zip(times, indices):
index = int(index)
tags[index] = 'B-TIME'
for i in range(1, len(time)):
tags[index+i] = 'I-TIME'
data = [event, tags]
self.render('time_index.html', data=data)
# 保存为txt文件
dir_path = './time_output'
with open('./%s/%s.txt' % (dir_path, get_max_num(dir_path)+1), 'w', encoding='utf-8') as f:
for char, tag in zip(event, tags):
f.write(char+'\t'+tag+'\n')
# 主函数
def main():
# 开启tornado服务
tornado.options.parse_command_line()
# 定义app
app = tornado.web.Application(
handlers=[(r'/query', QueryHandler),
(r'/result', PostHandler)
], #网页路径控制
template_path=os.path.join(os.path.dirname(__file__), "templates") # 模板路径
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
main()
time_index.html文件如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>时间抽取标注平台</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<style>
mark {
background-color:#00ff90; font-weight:bold;
}
p{text-indent:2em;}
</style>
<script>
var click_cnt = 0;
// 双击第i个select, 添加文字的index
function select_click(i){
var content = document.getElementById('event').value;
var time = document.getElementById('time_'+i.toString()).value;
for(var j=0; j<=content.length-time.length; j++){
if(content.substr(j, time.length) == time){
var select = document.getElementById('index_'+i.toString());
var option = document.createElement("option");
option.value = j;
option.innerHTML = j;
select.appendChild(option);
}
}
}
// 添加输入框和select框
$(document).ready(function(){
$("#add_time").click(function(){
click_cnt = click_cnt + 1;
var input_id = new String('time_'+click_cnt.toString());
var index_id = new String('index_'+click_cnt.toString());
var content = "<input type='text' id=" + input_id + " class='form-control' style='width:306px;' name='time' /> \
    <select class='form-control' name='index' id="+ index_id + " style='width:120px;' \
ondblclick='select_click("+click_cnt.toString()+")'></select>";
$(content).appendTo($("#time_column"));
});
});
</script>
</head>
<body>
<center>
<br><br><br>
<form class="form-horizontal" role="form" method="post" action="/result" style="width:600px">
<div class="form-group">
<label for="event" class="col-sm-2 control-label">输入语料</label>
<div class="col-sm-10">
<textarea type="text" class="form-control" id="event" style="width:490px; height:200px" name="event"></textarea>
</div>
</div>
<div class="form-inline" style="text-align:left;">
<label for="time_0" class="col-sm-2 control-label">时间</label>
<div class="col-sm-10" id="time_column">
<input type="text" class="form-control" id="time_0" style="width:306px;" name="time" />
   
<select class="form-control" id="index_0" name="index" style="width:120px;" ondblclick="select_click(0)"></select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<br>
<button type="button" class="btn btn-default" id="add_time">添加时间</button>
<button type="submit" class="btn btn-success">显示标签</button>
<a href="/query"><button type="button" class="btn btn-danger">返回</button></a>
<button type="reset" class="btn btn-warning">重置</button>
</div>
</div>
</form>
<br>
<div style="width:600px">
<p> 原文:{{data[0]}} </p>
<table class="table table-striped">
{% for char, tag in zip(data[0], data[1]) %}
<tr>
<td>{{char}} </td>
<td>{{tag}} </td>
</tr>
{%end%}
</table>
</div>
</center>
</body>
</html>
平台使用
运行上述time_server.py后,在浏览器端输入网址: http://localhost:9005/query , 则会显示如下界面:
在输入语料框
中,我们输入语料:
8月8日是“全民健身日”,推出重磅微视频《我们要赢的,是自己》。
在时间这个输入框中,可以标注语料中的时间,同时双击同一行中的下拉列表,就能显示该标注时间在语料中的起始位置,有时候同样的标注时间会在语料中出现多次,那么我们在下拉列表中选择我们需要的标注的起始位置即可。
点击添加时间
按钮,它会增加一行标注,允许我们在同一份预料中标注多个时间。我们的一个简单的标注例子如下:
点击显示标注
,则会显示我们标注完后形成的序列标注信息,同时将该序列信息保存为txt文件,该txt文件位于time_output目录下。在网页上的序列标注信息如下:
同时,我们也可以查看保存的txt文档信息,如下:
点击返回
按钮,它会允许我们进行下一次的标注。刚才展示的只是一个简单例子,稍微复杂的标注如下图:
它形成的标注序列(部分)如下:
按 O
计 O
划 O
, O
2 B-TIME
0 I-TIME
1 I-TIME
9 I-TIME
年 I-TIME
8 I-TIME
月 I-TIME
1 I-TIME
0 I-TIME
日 I-TIME
, O
荣 O
耀 O
智 O
慧 O
屏 O
将 O
在 O
华 O
为 O
开 O
发 O
者 O
大 O
会 O
上 O
正 O
式 O
亮 O
相 O
, O
在 O
8 B-TIME
月 I-TIME
6 I-TIME
日 I-TIME
, O
荣 O
耀 O
官 O
微 O
表 O
示 O
该 O
产 O
品 O
......
总结
本平台仅作为序列标注算法的前期标注工具使用,并不涉及具体的算法。另外,后续该平台也会陆续开放出来,如果大家有好的建议,也可以留言~
本项目已上传只Github, 网址为: https://github.com/percent4/entity_tagging_platform
注意:不妨了解下笔者的微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注~
NLP(十四)自制序列标注平台的更多相关文章
- 测开之路六十四:UI测试平台之前端页面
{% extends "base.html" %} {% block script %} <!-- 引入js文件,需要在base.html留入口,不然渲染会出问题. --&g ...
- 转:使用RNN解决NLP中序列标注问题的通用优化思路
http://blog.csdn.net/malefactor/article/details/50725480 /* 版权声明:可以任意转载,转载时请标明文章原始出处和作者信息 .*/ author ...
- NLP(二十四)利用ALBERT实现命名实体识别
本文将会介绍如何利用ALBERT来实现命名实体识别.如果有对命名实体识别不清楚的读者,请参考笔者的文章NLP入门(四)命名实体识别(NER) . 本文的项目结构如下: 其中,albert_ ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十四):请求消息去重
为了确保信息请求消息的到达率,微信服务器在没有及时收到响应消息(ResponseMessage)的情况下,会多次发送同一条请求消息(RequestMessage),包括MsgId等在内的所有文本内容都 ...
- SNF开发平台WinForm之十四-站内发送系统信息-SNF快速开发平台3.3-Spring.Net.Framework
1运行效果: 2开发实现: .组装站内信息发送实体对象. SNFService SNFService = new SNFService(); if (this.ucUser.SelectedIds ! ...
- 开发指南专题十四:JEECG微云高速开发平台MiniDao 介绍
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhangdaiscott/article/details/27068645 开发指南专题十四:J ...
- 第三百八十四节,Django+Xadmin打造上线标准的在线教育平台—路由映射与静态文件配置以及会员注册
第三百八十四节,Django+Xadmin打造上线标准的在线教育平台—路由映射与静态文件配置以及会员注册 基于类的路由映射 from django.conf.urls import url, incl ...
- 第三百七十四节,Django+Xadmin打造上线标准的在线教育平台—创建课程app,在models.py文件生成4张表,课程表、课程章节表、课程视频表、课程资源表
第三百七十四节,Django+Xadmin打造上线标准的在线教育平台—创建课程app,在models.py文件生成4张表,课程表.课程章节表.课程视频表.课程资源表 创建名称为app_courses的 ...
- HDU 6464.免费送气球-动态开点-权值线段树(序列中第first小至第second小的数值之和)(感觉就是只有一个状态的主席树) (“字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛)
免费送气球 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submi ...
随机推荐
- GIT \ SVN 版本管理 git + gitHub
场景1 想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件"另存为--"一个新的Word文件,再接着改,改到一定程度,再"另存为--"一个新 ...
- POI自动调整列宽支持中文
/** * @Description:表格自适应宽度(中文支持) * @Author: * @param sheet sheet * @param columnLength 列数 */ private ...
- Maven发布项目到Nexus私服中 (发布jar包)
目录 1 需求说明 2 实现步骤 2.1 Maven服务的setting.xml文件 2.2 项目的pom.xml文件 2.3 发布项目 1 需求说明 开发完项目后, 将项目版本发布到Nexus私服中 ...
- Django前端HTML通过JS实现表格可编辑,动态添加行,回车完成新建文件夹
功能描述: 点击“新建文件夹”按钮,在table的末尾增加一行:单击页面的新增行,使单元格td变成可编辑状态:输入内容后,当单元格失去焦点时,保存输入的内容:回车后通过AJAX提交后台完成新建文件夹. ...
- BeanUtils.copyProperties的用法
实现原理 原理 target.set + source的属性名(source.get + source的属性名):所有source必须有get方法,target必须有set方法 一. springfr ...
- CDQZ集训DAY6 日记
又炸了. 早上起来其他竞赛生也走了,食堂做饭做的挺潦草,但为什么四川烧麦的馅是米啊??!! 起来看题总觉得都似曾相识.第一题打完40分暴力后想拿莫队搞到70分,但发现能想到的莫队维护都是nsqrt(n ...
- JSP之BBS论坛网站的创建
游戏论坛 工具:myeclipse10.0版本 Tomacat是在外部导入的apache-tomcat-7.0.93 JDK版本为1.6.0_13 字符编码统一为utf-8 一.建立数据库(chat) ...
- kuangbin专题专题四 Heavy Transportation POJ - 1797
题目链接:https://vjudge.net/problem/POJ-1797 思路:请参考我列出的另一个题目,和这个题目要求的值相反,另一个清楚后,这个写的解释就明白了. 另一个类似题目的博客:h ...
- py+appium微信公众号自动化(已搞定多个坑)
最近需要做微信公众号的自动化测试,遇到了不少坑. 微信公众号自动化与app自动化还是有区别的,因为多了不少坑.打开微信x5内核调试的就不说了,百度有. 1.首先,微信公众号有webview,uiaut ...
- py+selenium+unittest遇到HTMLTestRunner_cn文件报错:ZeroDivisionError: float division by zero【已解决】
问题:遇到HTMLTestRunner_cn文件报错:ZeroDivisionError: float division by zero HTMLTestrunner_cn.py是从网上下的,照理说应 ...