命名实体识别——日期识别
一、命名實體識別簡介
其目的是識別語料中的人名、地名、組織結構名等命名實體,由于這些命名實體在不斷地更新,很難在詞典中全部列出,所以就對這些詞的識別在詞匯形態處理任務中單獨處理,也就是NER技術。
而命名實體識別效果的評判標準主要是看實體的邊界是否劃分正確,以及實體的類型是否標注正確,對于英文來說命名實體的邊界識別相對簡單,因為一般都有明顯的形式標志,而對于實體類型的確定相對較難。在中文中相較于實體類別標注,實體邊界的識別更加困難。
中文命名實體識別難點主要有以下幾點:
命名實體的數量眾多、命名實體構成規律復雜、嵌套情況復雜、長度不確定、等
命名實體的識別目前主要采用的是基于規則和統計的混合方法。因為單純的基于規則的話需要手工修改規則,難以覆蓋所有的語言現象,也就存在可移植性差,維護困難。而基于統計的命名實體識別,其對語料庫的依賴比較大,而用來建設和評估命名實體識別系統的大規模通用語料庫又比較少。因此目前多采用混合的方法來做。
這里舉一個簡單的日期識別的實例:
例如現有一個基于語音問答系統的酒店預訂系統根據用戶輸入的每句語音進行分析,識別出用戶的酒店預訂需求,然而由于語音轉換的文字大都不是嚴格的數字形式,這時就需要通過一定的規則來進行處理。
二、日期識別:
主要思想是:這里主要通過正則表達式和jieba分詞來完成任務。
首先將輸入的要識別的句子進行jieba分詞,提取出其帶有時間詞性的詞,比如詞性是“m”(數字),“t”(時間)的詞,然后再通過正則化處理,得到相應的時間實體。注意這里是沒有訓練語料,直接用的是jieba的分詞和詞性標注功能。
示例代碼:
# -*- coding: utf-8 -*-#日期識別 import re from datetime import datetime,timedelta from dateutil.parser import parse import jieba.posseg as psgUTIL_CN_NUM = {'零': 0, '一': 1, '二': 2, '兩': 2, '三': 3, '四': 4,'五': 5, '六': 6, '七': 7, '八': 8, '九': 9,'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,'5': 5, '6': 6, '7': 7, '8': 8, '9': 9 }UTIL_CN_UNIT = {'十': 10, '百': 100, '千': 1000, '萬': 10000}def cn2dig(src):if src == "":return Nonem = re.match("\d+", src)if m:return int(m.group(0))rsl = 0unit = 1for item in src[::-1]:if item in UTIL_CN_UNIT.keys():unit = UTIL_CN_UNIT[item]elif item in UTIL_CN_NUM.keys():num = UTIL_CN_NUM[item]rsl += num * unitelse:return Noneif rsl < unit:rsl += unitreturn rsldef year2dig(year):res = ''for item in year:if item in UTIL_CN_NUM.keys():res = res + str(UTIL_CN_NUM[item])else:res = res + itemm = re.match("\d+", res)if m:if len(m.group(0)) == 2:return int(datetime.datetime.today().year/100)*100 + int(m.group(0))else:return int(m.group(0))else:return Nonedef parse_datetime(msg):#print('msg:',msg)if msg is None or len(msg) == 0:return Nonem = re.match(r"([0-9零一二兩三四五六七八九十]+年)?([0-9一二兩三四五六七八九十]+月)?([0-9一二兩三四五六七八九十]+[號日])?([上中下午晚早]+)?([0-9零一二兩三四五六七八九十百]+[點:\.時])?([0-9零一二三四五六七八九十百]+分?)?([0-9零一二三四五六七八九十百]+秒)?",msg) #print('m.group:',m.group(0),m.group(1),m.group(2),m.group(3),m.group(4),m.group(5))if m.group(0) is not None:res = {"year": m.group(1),"month": m.group(2),"day": m.group(3),"noon":m.group(4), # 上中下午晚早"hour": m.group(5) if m.group(5) is not None else '00',"minute": m.group(6) if m.group(6) is not None else '00',"second": m.group(7) if m.group(7) is not None else '00',}params = {}for name in res:if res[name] is not None and len(res[name]) != 0:tmp = Noneif name == 'year':tmp = year2dig(res[name][:-1])else:tmp = cn2dig(res[name][:-1])if tmp is not None:params[name] = int(tmp)target_date = datetime.today().replace(**params)#print('target_date:',target_date)is_pm = m.group(4)if is_pm is not None:if is_pm == u'下午' or is_pm == u'晚上' or is_pm =='中午':hour = target_date.time().hourif hour < 12:target_date = target_date.replace(hour=hour + 12)return target_date.strftime('%Y-%m-%d %H:%M:%S')else:return None# 對提取出的拼接日期串進行進一步的處理,進行有效性判斷 def check_time_valid(word):#print('check:',word)m = re.match("\d+$", word)if m:if len(word) <= 6:return Noneword1 = re.sub('[號|日]\d+$', '日', word)#print('word1:',word1)if word1 != word:return check_time_valid(word1)else:return word1#時間提取 def time_extract(text):time_res = []word = ''keyDate = {'今天': 0, '明天':1, '后天': 2}for k, v in psg.cut(text):#print(k,v)if k in keyDate:if word != '':time_res.append(word) # 日期的轉換,timedelta提取任意延遲天數的信息word = (datetime.today() +timedelta(days=keyDate.get(k, 0))).\strftime('%Y{y}%m{m}%dze8trgl8bvbq').format(y='年',m='月',d='日') elif word != '':if v in ['m', 't']:word = word + kelse:time_res.append(word)word = ''elif v in ['m', 't']: # m:數字 t:時間word = k #print('word:',word)if word != '':time_res.append(word)#print('time_res:',time_res)# filter() 函數用于過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表result = list(filter(lambda x: x is not None, [check_time_valid(w) for w in time_res]))#print('result:',result)final_res = [parse_datetime(w) for w in result]#print('final_res:',final_res)return [x for x in final_res if x is not None]text1 = '我要住到明天下午三點' print(text1, time_extract(text1), sep=':')text2 = '預定28號的房間' print(text2, time_extract(text2), sep=':')text3 = '我要從26號下午4點住到11月2號' print(text3, time_extract(text3), sep=':')text5 = '今天30號呵呵' print(text5, time_extract(text5), sep=':')text4 = '我要預訂今天到30的房間' print(text4, time_extract(text4), sep=':')運行結果:
我要住到明天下午三點:['2018-07-27 15:00:00'] 預定28號的房間:['2018-07-28 00:00:00'] 我要從26號下午4點住到11月2號:['2018-07-26 16:00:00', '2018-11-02 00:00:00'] 今天30號呵呵:['2018-07-26 00:03:00'] 我要預訂今天到30的房間:['2018-07-26 00:00:00']從運行結果來看,前三句話都很多好的識別出日期了,而后面兩句則識別不出來,這也正是基于規則識別的限制所在,因為不可能覆蓋所有的規則場景,但好處就是不需要在系統建設初期為搜集數據標注訓練而煩惱。
三、筆記:
格式轉換成帶有漢字的形式可以通過如下方式:
timedelta(days=keyDate.get(k, 0))).strftime('%Y年%m月%d日')UnicodeEncodeError: 'locale' codec can't encode character '\u5e74' in position 2: Illegal byte sequence解決辦法:
time.strftime('%Y{y}%m{m}%dze8trgl8bvbq %H{h}%M{f}%S{s}').format(y='年',m='月',d='日',h='時',f='分',s='秒')參考:《pytho自然語言處理實戰 核心技術與算法》
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的命名实体识别——日期识别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jieba词性标注
- 下一篇: 中信银行贷款需要什么条件 需要满足四个基