NLP(十四)自制序列標(biāo)注平臺

背景介紹

??在平時的NLP任務(wù)中,我們經(jīng)常用到命名實體識別(NER),常用的識別實體類型為人名、地名、組織機構(gòu)名,但是我們往往也會有識別其它實體的需求,比如時間、品牌名等。在利用算法做實體識別的時候,我們一般采用序列標(biāo)注算法,這就對標(biāo)注的文本格式有一定的要求,因此,一個好的序列標(biāo)注的平臺必不可少,將會大大減少我們標(biāo)注的工作量,有效提升算法的更新迭代速度。
??本文將介紹筆者的一個工作:自制的序列標(biāo)注平臺。我們以時間識別為例。比如,在下面的文章中:

按計劃,2019年8月10日,榮耀智慧屏將在華為開發(fā)者大會上正式亮相,在8月6日,榮耀官微表示該產(chǎn)品的預(yù)約量已破十萬臺,8月7日下午,榮耀總裁趙明又在微博上造勢率先打出差異化牌,智慧屏沒有開關(guān)機廣告,并表態(tài)以后也不會有,消費者體驗至上,營銷一波接一波,可謂來勢洶洶。

我們需要從該文章中標(biāo)注出三個時間:2019年8月10日8月6日,8月7日下午,并形成標(biāo)注序列。
??下面將詳細(xì)介紹筆者的工作。

序列標(biāo)注平臺

??由于開發(fā)時間倉促以及筆者能力有限,因此,序列標(biāo)注平臺的功能還沒有很完善,希望筆者的工作能拋磚引玉。
??項目的結(jié)構(gòu)圖如下:

項目結(jié)構(gòu)圖

templates中存放靜態(tài)資源,time_index.html為平臺的操作界面,time_output為平臺標(biāo)注完實體后的文件保存路徑,time_server.py是用tornado寫的服務(wù)端路徑控制代碼,utils.py中是獲取某個路徑下的txt文件的最大數(shù)值的函數(shù)。

??其中,utils.py的完整代碼如下:

# -*- coding: utf-8 -*-
# time: 2019-03-14
# place: Xinbeiqiao, Beijing

import os

# 獲取當(dāng)前所在目錄的txt文本的最大數(shù)值
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函數(shù)
    def get(self):
        self.render('time_index.html', data = ['', []])

# POST請求
class PostHandler(tornado.web.RequestHandler):
    # post函數(shù)
    def post(self):

        # 獲取前端參數(shù), event, time, index
        event = self.get_argument('event')
        times = self.get_arguments('time')
        indices = self.get_arguments('index')
        print(event)
        print(times)
        print(indices)

        # 前端顯示序列標(biāo)注信息
        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')


# 主函數(shù)
def main():
    # 開啟tornado服務(wù)
    tornado.options.parse_command_line()
    # 定義app
    app = tornado.web.Application(
            handlers=[(r'/query', QueryHandler),
                      (r'/result', PostHandler)
                      ], #網(wǎng)頁路徑控制
            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>時間抽取標(biāo)注平臺</title>
    <link rel="stylesheet" >
    <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' /> \
                                &emsp;&emsp;&emsp; <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" />
            &emsp;&emsp;&emsp;
            <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">顯示標(biāo)簽</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后,在瀏覽器端輸入網(wǎng)址: http://localhost:9005/query , 則會顯示如下界面:

序列標(biāo)注平臺界面

??在輸入語料框中,我們輸入語料:

8月8日是“全民健身日”,推出重磅微視頻《我們要贏的,是自己》。

在時間這個輸入框中,可以標(biāo)注語料中的時間,同時雙擊同一行中的下拉列表,就能顯示該標(biāo)注時間在語料中的起始位置,有時候同樣的標(biāo)注時間會在語料中出現(xiàn)多次,那么我們在下拉列表中選擇我們需要的標(biāo)注的起始位置即可。
??點擊添加時間按鈕,它會增加一行標(biāo)注,允許我們在同一份預(yù)料中標(biāo)注多個時間。我們的一個簡單的標(biāo)注例子如下:

標(biāo)注過程的例子

??點擊顯示標(biāo)注,則會顯示我們標(biāo)注完后形成的序列標(biāo)注信息,同時將該序列信息保存為txt文件,該txt文件位于time_output目錄下。在網(wǎng)頁上的序列標(biāo)注信息如下:

形成的標(biāo)注序列

同時,我們也可以查看保存的txt文檔信息,如下:

保存的txt文件

??點擊返回按鈕,它會允許我們進行下一次的標(biāo)注。剛才展示的只是一個簡單例子,稍微復(fù)雜的標(biāo)注如下圖:

稍微復(fù)雜些的標(biāo)注

它形成的標(biāo)注序列(部分)如下:

按   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
發(fā)   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
產(chǎn)   O
品   O
......

總結(jié)

??本平臺僅作為序列標(biāo)注算法的前期標(biāo)注工具使用,并不涉及具體的算法。另外,后續(xù)該平臺也會陸續(xù)開放出來,如果大家有好的建議,也可以留言~
??本項目已上傳只Github, 網(wǎng)址為: https://github.com/percent4/entity_tagging_platform

注意:不妨了解下筆者的微信公眾號: Python爬蟲與算法(微信號為:easy_web_scrape), 歡迎大家關(guān)注~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容