NLP (Natural Language Processing) 是人工智慧(AI)的一個子領域。自然語言是人類智慧的結晶,自然語言處理是人工智慧中最為困難的問題之一(來自百度百科)。
其中中文更是不好處理。下面將分析中文語句中的時間的識別:time NLP 輸入一句話,能識別出話裡的時間。下面2種簡單的實現方法。
1.單詞的識別
/** * 常用時間列舉 * * @author xkzhangsan */ public enum CommonTimeEnum { TODAY("today", "今天"), TOMORROW("tomorrow", "明天"), NEXTWEEK("nextWeek", "下週"), NEXTMONTH("nextMonth", "下月"), NEXTYEAR("nextYear", "明年"), YESTERDAY("yesterday", "昨天"), LASTWEEK("lastWeek", "上週"), LASTMONTH("lastMonth", "上月"), LASTYEAR("lastYear", "去年"), ; private String code; private String name; public String getCode() { return code; } public String getName() { return name; } CommonTimeEnum(String code, String name) { this.code = code; this.name = name; } public static Map<String, String> convertToMap(){ Map<String, String> commonTimeMap = new HashMap<String, String>(); for (CommonTimeEnum commonTimeEnum : CommonTimeEnum.values()) { commonTimeMap.put(commonTimeEnum.getCode(), commonTimeEnum.getCode()); commonTimeMap.put(commonTimeEnum.getName(), commonTimeEnum.getCode()); } return commonTimeMap; } public static CommonTimeEnum getCommonTimeEnumByCode(String code){ for (CommonTimeEnum commonTimeEnum : CommonTimeEnum.values()) { if(commonTimeEnum.getCode().equals(code)){ return commonTimeEnum; } } return null; } } /** * 解析自然語言時間,今天,明天,下週,下月,明年,昨天,上週,上月,去年等。 * @param text 自然語言時間,待解析字串 * @param naturalLanguageMap 自定義自然語言時間map,其中key自定義,value需為 com.xkzhangsan.time.enums.CommonTimeEnum中的code; * 可以為空,預設使用com.xkzhangsan.time.enums.CommonTimeEnum解析。 * @return Date */ public static Date parseNaturalLanguageToDate(String text, Map<String, String> naturalLanguageMap){ if(StringUtil.isEmpty(text)){ return null; } text = text.trim(); boolean isCommonTimeMap = false; if(CollectionUtil.isEmpty(naturalLanguageMap)){ naturalLanguageMap = CommonTimeEnum.convertToMap(); isCommonTimeMap = true; } if(! naturalLanguageMap.containsKey(text) || StringUtil.isEmpty(naturalLanguageMap.get(text))){ return null; } String targetMethod = null; if(isCommonTimeMap){ targetMethod = naturalLanguageMap.get(text); }else{ String code = naturalLanguageMap.get(text); Map<String, String> commonTimeMap = CommonTimeEnum.convertToMap(); if(commonTimeMap.containsKey(code)){ targetMethod = commonTimeMap.get(code); } } if(targetMethod == null){ return null; } //執行結果 CommonTimeEnum targetCommonTime = CommonTimeEnum.getCommonTimeEnumByCode(targetMethod); if(targetCommonTime == null){ return null; } switch (targetCommonTime){ case TODAY : return DateTimeCalculatorUtil.today(); case TOMORROW: return DateTimeCalculatorUtil.tomorrow(); case NEXTWEEK: return DateTimeCalculatorUtil.nextWeek(); case NEXTMONTH: return DateTimeCalculatorUtil.nextMonth(); case NEXTYEAR: return DateTimeCalculatorUtil.nextYear(); case YESTERDAY: return DateTimeCalculatorUtil.yesterday(); case LASTWEEK: return DateTimeCalculatorUtil.lastWeek(); case LASTMONTH: return DateTimeCalculatorUtil.lastMonth(); case LASTYEAR: return DateTimeCalculatorUtil.lastYear(); default: return null; } } /** * 明天 * @return Date */ public static Date tomorrow(){ return plusDays(today(), 1); } /** * 今天 * @return Date */ public static Date today(){ return new Date(); }
2.中文語句中的時間的識別
這個是真實語境下的時間識別,比如 Hi,all.下週一下午三點開會,如果今天是2021-06-10 那麼 返回結果為:2021-06-14 15:00:00 。
2.1 原理和圖解
原理和第一種類似,也是識別時間詞語,根據基準時間推斷結果,但更強大一些。
基本分為三步:
(1)載入正則檔案
(2)解析中文語句中的所有時間詞語
(3)根據基準時間,迴圈解析(2)中的時間詞語
程式碼比較多這裡就不貼了,詳細可以看github上的專案程式碼,下面是簡單流程圖:
2.2 相關原始碼及說明
2.2.1 Time-NLP
github: https://github.com/shinyke/Time-NLP
author:shinyke
由復旦NLP中的時間分析功能修改而來,做了很多細節和功能的優化。
- 泛指時間的支援,如:早上、晚上、中午、傍晚等。
- 時間未來傾向。 如:在週五輸入“週一早上開會”,則識別到下週一早上的時間;在下午17點輸入:“9點送牛奶給隔壁的漢子”則識別到第二天上午9點。
- 多個時間的識別,及多個時間之間上下文關係處理。如:"下月1號下午3點至5點到圖書館還書",識別到開始時間為下月1號下午三點。同時,結束時間也繼承上文時間,識別到下月1號下午5點。
- 可自定義基準時間:指定基準時間為“2016-05-20-09-00-00-00”,則一切分析以此時間為基準。
- 修復了各種各樣的BUG。
簡而言之,這是一個輸入一句話,能識別出話裡的時間的工具。
2.2.2 xk-time TimeNLPUtil
https://github.com/xkzhangsan/xk-time TimeNLPUtil
在Time-NLP基礎上做了很多優化:
(1)封裝屬性,重新命名使符合駝峰命名標準。
(2)將載入正則資原始檔改為單例載入。
(3)將類按照功能重新劃分為單獨的多個類。
(4)使用Java8日期API重寫。
(5)增加註釋說明,優化程式碼。
(6)修復原專案中的issue:標準時間yyyy-MM-dd、yyyy-MM-dd HH:mm:ss和yyyy-MM-dd HH:mm解析問題。
(7)修復原專案中的issue:1小時後,1個半小時後,1小時50分鐘等解析問題;並且支援到秒,比如50秒後,10分鐘30秒後等。
(8)修復原專案中的issue:修復當前時間是上午10點,那麼下午三點 會識別為明天下午三點問題。
(9)修復原專案中的issue:修復小數解析異常問題。
(10)效能優化,將使用到的正則預編譯後放到快取中,下次直接使用,提高效能。
3 實現方法的侷限性
第一種只能識別單詞;
第二種也只能識別正則檔案中的詞語,比第一種識別能力更強,但如果有新的或不常用的時間詞語無法處理,比如星期一的同義詞禮拜一等,如果要不斷支援新的詞語,需要不斷的修改,不如機器學習好;
對於常用的時間詞語識別,第二種已經達到很高的識別率。
4.開發這個功能的原因
第一種實現,因為有網友需要識別中文時間詞語,我寫了第一種的實現;
第二種實現,另一個網友有需要識別語句中的中文時間詞語,他嚮往推薦了Time-NLP這個專案,說這個專案很好,不維護了,有一些小問題,希望我能參考實現,我研究了原專案程式碼,在我的專案中重寫,優化,並修復了一些問題。
感謝shinyke,這個專案很好,學習到很多正則解析的知識。
原始碼地址: https://github.com/xkzhangsan/xk-time