HanLP Demo(學習筆記)

需求,

實習需要學習這個。感覺蠻好玩的.....

我是這樣做的:

根據網上的資料,自己整理,因爲是開源的,所以配合Demo理解,不是算法層次的,嗯,更新中....

data包沒下載下來,家裏這邊網不支持下載那個data包.有些Demo沒測....嗯,下載下來在測

官網: http://www.hanlp.com/

文檔及源碼: https://github.com/hankcs/HanLP/blob/1.x/README.md

學習網站:https://www.hankcs.com/nlp/hanlp.html

HanLP自然語言處理包

在學習HanLp之前,我們先了解一下自然語言處理(NLP)。

自然語言處理:

自然語言處理的目標是使計算機能夠像人類一樣理解語言。

自然語言處理是一門交叉學科,屬於人工智能的一個分支,涉及計算機科學、語言學、數 學等多個領域的專業知識。

自然語言處理 :是一門合了計算機科學、人工智能以 及語言學的交叉學科,它們的關係如圖 1!1 所示。這門學科研究的是如何通過機器學習等技術, 讓計算機學會處理人類語言,乃至實現終極目標——理解人類語言或人工智能。

 

 

關鍵名詞:

  • 分詞:指的是將一個漢字序列切分成一個一個單獨的詞。分詞就是將連續的字序列按照一定的規範重新組合成 詞序列的過程。
  • 詞性:是指以詞的特點作爲劃分詞類的根據,詞性劃分也可稱之爲詞類的劃分,詞類是一個語言學術語,是一 種語言中詞的語法分類,是以語法特徵(包括句法功能和形態變化)爲主要依據、兼顧詞彙意義對詞進行劃分 的結果,現代漢語的詞可以分爲14種詞類。如名詞、動詞、動名詞等等。
  • 聚類聚類類似於分類,但是和分類不同的是,聚類要求劃分的類是未知的。聚類分析是一種探索性的分析, 在分類的過程中,人們不必事先給出一個分類的標準,聚類分析能夠從樣本數據出發,自動進行分類。聚類分 析所使用方法的不同,常常會得到不同的結論。不同研究者對於同一組數據進行聚類分析,所得到的聚類數未 必一致。
  • 相似度:從字面上理解就是兩個字符串的相似程度。常用的計算相似度的算法有:編輯距離計算、傑卡德係數 計算、TFIDF計算等等。
  • 詞向量:顧名思義,詞向量是用來表示詞的向量,通常也被認爲是詞的特徵向量,是自然語言中的詞在計算機 中的表現形式。詞向量技術是將詞轉化成爲稠密向量,並且對於相似的詞,其對應的詞向量也相近。
  • 依存關係:一個句子中有一箇中心詞,其他部分依存於該詞,比如“北京是中國的首都在一句話中,動詞是句子的中心,它支配其他成分,而不受其他成分支配。在上例中,動詞“是” 是句子的中心,其他成分依存於它。

關於相似度計算的幾種常用算法:

  1. (詞距)編輯距離計算:編輯距離,英文叫做 Edit Distance,又稱 Levenshtein 距離,是指兩個字串之間,由一個轉成另一個所需的最少編輯操作次數,如果它們的距離越大,說明它們越是不同。許可的編輯操作包括將一個字 符替換成另一個字符,插入一個字符,刪除一個字符。我們可以限制這個距離來得到相似的字符串。
  2. 傑卡德係數計算:這種算法比較簡單,就是分詞後的詞向量的交集除以並集,它的計算方式非常簡單,就是兩 個樣本的交集除以並集得到的數值,當兩個樣本完全一致時,結果爲 1,當兩個樣本完全不同時,結果爲 0。我們可以在[0,1]之間衡量這個相似度,約接近1相似度越高。
  3. TF-IDF計算:首先計算一個詞的詞頻TF,再計算一個詞的逆文檔頻率(權重),兩者之積就是TF-IDF的值。 找出兩個字符串中的關鍵詞,每個字符串各取出若干個關鍵詞(比如20個),合併成一個集合,計算每個字 符串對於這個集合中的詞的詞頻(爲了避免字符串長度的差異,可以使用相對詞頻);生成兩個字符串各自的 詞頻向量;計算兩個向量的餘弦相似度,值越大就表示越相似。

HanLP是由一系列模型與算法組成的Java工具包,目標是促進自然語言處理在生產環境中的應用。HanLP具備功能完善、性能高效、架構清晰、語料時新、可自定義的特點。

HanLP提供下列功能:

調用方法 

HanLP幾乎所有的功能都可以通過工具類HanLP快捷調用

詞典說明:

基本格式

詞典分爲詞頻詞性詞典詞頻詞典

  • 詞頻詞性詞典

    • 每一行代表一個單詞,格式遵從[單詞] [詞性A] [A的頻次] [詞性B] [B的頻次] ...

  • 詞頻詞典

    • 每一行代表一個單詞,格式遵從[單詞] [單詞的頻次]

    • 每一行的分隔符爲空格符或製表符

DEMO:

Demo

簡單分詞

  public static void demoAtFirstSight() {
        System.out.println("首次編譯運行時,HanLP會自動構建詞典緩存,請稍候……");
    //    HanLP.Config.enableDebug();         // 爲了避免你等得無聊,開啓調試模式說點什麼:-)
        System.out.println(HanLP.segment("你好,歡迎使用HanLP漢語處理包!接下來請從其他Demo中體驗HanLP豐富的功能~"));
    }

輸出

首次編譯運行時,HanLP會自動構建詞典緩存,請稍候……
[你好/l, ,/w, 歡迎/v, 使用/v, HanLP/nx, 漢語/nz, 處理/v, 包/v, !/w, 接下來/l, 請/v, 從/p, 其他/r, Demo/nx, 中/f, 體驗/vn, HanLP/nx, 豐富/a, 的/uj, 功能/n, ~/nx]

測試速度:

public static void demoBasicTokenizer(){
        String text = "舉辦紀念活動銘記二戰歷史,不忘戰爭帶給人類的深重災難,是爲了防止悲劇重演,確保和平永駐;" +
                "銘記二戰歷史,更是爲了提醒國際社會,需要共同捍衛二戰勝利成果和國際公平正義," +
                "必須警惕和抵制在歷史認知和維護戰後國際秩序問題上的倒行逆施。";
        System.out.println(BasicTokenizer.segment(text));
        // 測試分詞速度,讓大家對HanLP的性能有一個直觀的認識
        long start = System.currentTimeMillis();
        int pressure = 100000;
        for (int i = 0; i < pressure; ++i)
        {
            BasicTokenizer.segment(text);
        }
        double costTime = (System.currentTimeMillis() - start) / (double) 1000;
        System.out.printf("BasicTokenizer分詞速度:%.2f字每秒\n", text.length() * pressure / costTime);
    }

輸出:

[舉辦/v, 紀念/v, 活動/vn, 銘記/v, 二戰/j, 歷史/n, ,/w, 不/d, 忘/v, 戰爭/n, 帶/v, 給/p, 人類/n, 的/uj, 深重/a, 災難/n, ,/w, 是/v, 爲了/p, 防止/v, 悲劇/n, 重演/v, ,/w, 確保/v, 和平/n, 永/d, 駐/v, ;/w, 銘記/v, 二戰/j, 歷史/n, ,/w, 更/d, 是/v, 爲了/p, 提醒/v, 國際/n, 社會/n, ,/w, 需要/v, 共同/d, 捍衛/v, 二戰/j, 勝利/vn, 成果/n, 和/c, 國際/n, 公平/a, 正義/n, ,/w, 必須/d, 警惕/v, 和/c, 抵制/v, 在/p, 歷史/n, 認知/vn, 和/c, 維護/v, 戰後/t, 國際/n, 秩序/n, 問題/n, 上/f, 的/uj, 倒行逆施/i, 。/w]
BasicTokenizer分詞速度:2152604.27字每秒

 中人名識別

Segment segment = HanLP.newSegment().enableNameRecognize(true);

目前分詞器基本上都默認開啓了中國人名識別,比如HanLP.segment()接口中使用的分詞器等等,用戶不必手動開啓;上面的代碼只是爲了強調。

 public static void demoChineseNameRecognition() {
        String[] testCase = new String[]{
              
                "武大靖創世界紀錄奪冠,中國代表團平昌首金"
               
        };
        Segment segment = HanLP.newSegment().enableNameRecognize(true);
        for (String sentence : testCase)
        {
            List<Term> termList = segment.seg(sentence);
            System.out.println(termList);
        }
    }

輸出:


[武大靖/nr, 創/vg, 世界/n, 紀錄/n, 奪冠/v, ,/w, 中國/ns, 代表團/n, 平昌/ns, 首/m, 金/ng]

用戶詞典的動態增刪

這個我理解就是自己定義一些識別詞

  • CustomDictionary是一份全局的用戶自定義詞典,可以隨時增刪,影響全部分詞器。

  • 另外可以在任何分詞器中關閉它。通過代碼動態增刪不會保存到詞典文件。

  • CustomDictionary主詞典文本路徑是data/dictionary/custom/CustomDictionary.txt,用戶可以在此增加自己的詞語(不推薦);也可以單獨新建一個文本文件,通過配置文件CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 我的詞典.txt;來追加詞典(推薦)。

  • 始終建議將相同詞性的詞語放到同一個詞典文件裏,便於維護和分享。

  • 每一行代表一個單詞,格式遵從[單詞] [詞性A] [A的頻次] [詞性B] [B的頻次] ... 如果不填詞性則表示採用詞典的默認詞性。

  • 詞典的默認詞性默認是名詞n,可以通過配置文件修改:全國地名大全.txt ns;如果詞典路徑後面空格緊接着詞性,則該詞典默認是該詞性。


    public static void demoCustomDictionary() {
        // 動態增加
        CustomDictionary.add("攻城獅");
        // 強行插入
        CustomDictionary.insert("白富美", "nz 1024");
        // 刪除詞語(註釋掉試試)
//        CustomDictionary.remove("攻城獅");
        System.out.println(CustomDictionary.add("單身狗", "nz 1024 n 1"));
        System.out.println(CustomDictionary.get("單身狗"));

        String text = "攻城獅逆襲單身狗,迎娶白富美,走上人生巔峯";  // 怎麼可能噗哈哈!

        // DoubleArrayTrie分詞
        final char[] charArray = text.toCharArray();
       CustomDictionary.parseText(charArray, new AhoCorasickDoubleArrayTrie.IHit<CoreDictionary.Attribute>()
        {
            @Override
            public void hit(int begin, int end, CoreDictionary.Attribute value)
            {
                System.out.printf("[%d:%d]=%s %s\n", begin, end, new String(charArray, begin, end - begin), value);
            }
        });
        // 首字哈希之後二分的trie樹分詞
        BaseSearcher searcher = CustomDictionary.getSearcher(text);
        Map.Entry entry;
        System.out.println("首字哈希之後二分的trie樹分詞");
        while ((entry = searcher.next()) != null)
        {
            System.out.println(entry);
        }

        // 標準分詞
        System.out.println("標準分詞:"+HanLP.segment(text));

        // Note:動態增刪不會影響詞典文件
        // 目前CustomDictionary使用DAT儲存詞典文件中的詞語,用BinTrie儲存動態加入的詞語,前者性能高,後者性能低
        // 之所以保留動態增刪功能,一方面是歷史遺留特性,另一方面是調試用;未來可能會去掉動態增刪特性。
    }
true
nz 1024 n 1 
[0:3]=攻城獅 nz 1 
[5:8]=單身狗 nz 1024 n 1 
[11:14]=白富美 nz 1024 
[0:2]=攻城 vi 15 
[3:5]=逆襲 nz 199 
首字哈希之後二分的trie樹分詞
攻城獅=nz 1 
單身狗=nz 1024 n 1 
白富美=nz 1024 
標準分詞:[攻城獅/nz, 逆襲/nz, 單身狗/nz, ,/w, 迎娶/v, 白富美/nz, ,/w, 走/v, 上/f, 人生/n, 巔峯/n]

自定義詞性,

以及往詞典中插入自定義詞性的詞語

 public static void demoCustomNature() {
        // 對於系統中已有的詞性,可以直接獲取
        Nature pcNature = Nature.fromString("n");
        System.out.println(pcNature);
        // 此時系統中沒有"電腦品牌"這個詞性
        pcNature = Nature.fromString("電腦品牌");
        System.out.println("此時系統中沒有是否有:\"電腦品牌\"這個詞性" + pcNature);
        // 我們可以動態添加一個
        pcNature = Nature.create("電腦品牌");
        System.out.println(pcNature);
        // 可以將它賦予到某個詞語
        LexiconUtility.setAttribute("蘋果電腦", pcNature);
        // 或者
        LexiconUtility.setAttribute("蘋果電腦", "電腦品牌 1000");
        // 它們將在分詞結果中生效
        List<Term> termList = HanLP.segment("蘋果電腦可以運行開源阿爾法狗代碼嗎");
        System.out.println(termList);
        for (Term term : termList) {
            if (term.nature == pcNature)
                System.out.printf("找到了 [%s] : %s\n", pcNature, term.word);
        }
        // 還可以直接插入到用戶詞典
        CustomDictionary.insert("阿爾法狗", "科技名詞 1024");
        StandardTokenizer.SEGMENT.enablePartOfSpeechTagging(true);  // 依然支持隱馬詞性標註
        termList = HanLP.segment("蘋果電腦可以運行開源阿爾法狗代碼嗎");
        System.out.println(termList);
        // 1.6.5之後Nature不再是枚舉類型,無法switch。但終於不再涉及反射了,在各種JRE環境下都更穩定。
        for (Term term : termList) {
            if (term.nature == n) {
                System.out.printf("找到了 [%s] : %s\n", "名詞", term.word);
            }
        }

    }
n
此時系統中沒有是否有:"電腦品牌"這個詞性null
電腦品牌
[蘋果電腦/電腦品牌, 可以/v, 運行/vn, 開源/vn, 阿爾法/nz, 狗/n, 代碼/n, 嗎/y]
找到了 [電腦品牌] : 蘋果電腦
[蘋果電腦/電腦品牌, 可以/v, 運行/vn, 開源/vn, 阿爾法狗/科技名詞, 代碼/n, 嗎/y]
找到了 [名詞] : 代碼

極速分詞,

基於DoubleArrayTrie實現的詞典正向最長分詞,適用於“高吞吐量”“精度一般”的場合\

SpeedTokenizer.segment(text)

HanLP.Config.ShowTermNature = false;// 用於設置詞性是否顯示。

public static void demoHighSpeedSegment() {
        String text = "江西鄱陽湖乾枯,中國最大淡水湖變成大草原";
       // 不顯示詞性
        HanLP.Config.ShowTermNature = false;
        System.out.println(SpeedTokenizer.segment(text));
        long start = System.currentTimeMillis();
        int pressure = 1000000;
        for (int i = 0; i < pressure; ++i)
        {
            SpeedTokenizer.segment(text);
        }
        double costTime = (System.currentTimeMillis() - start) / (double)1000;
        System.out.printf("SpeedTokenizer分詞速度:%.2f字每秒\n", text.length() * pressure / costTime);

    }
[江西, 鄱陽湖, 乾枯, ,, 中國, 最, 大, 淡水湖, 變成, 大, 草原]
SpeedTokenizer分詞速度:16877637.13字每秒

索引分詞

索引分詞IndexTokenizer是面向搜索引擎的分詞器,能夠對長詞全切分,另外通過term.offset可以獲取單詞在文本中的偏移量

List<Term> termList = IndexTokenizer.segment("主副食品");

  public static void emoIndexSegment() {
        List<Term> termList = IndexTokenizer.segment("主副食品");
        for (Term term : termList)
        {
            System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
        }

        System.out.println("\n最細顆粒度切分:");
        IndexTokenizer.SEGMENT.enableIndexMode(1);
        termList = IndexTokenizer.segment("主副食品");
        for (Term term : termList)
        {
            System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
        }
    }
主副食品/n [0:4]
主副食/j [0:3]
副食品/n [1:4]
副食/n [1:3]
食品/n [2:4]

最細顆粒度切分:
主副食品/n [0:4]
主副食/j [0:3]
主/n [0:1]
副食品/n [1:4]
副食/n [1:3]
副/b [1:2]
食品/n [2:4]
食/v [2:3]
品/v [3:4]

日本人名識別

Segment segment = HanLP.newSegment().enableJapaneseNameRecognize(true);

目前標準分詞器默認關閉了日本人名識別,用戶需要手動開啓;這是因爲日本人名的出現頻率較低,但是又消耗性能。

 public static void demoJapaneseNameRecognition()
    {
        String[] testCase = new String[]{
                "北川景子參演了林詣彬導演的《速度與激情3》",
                "林志玲亮相網友:確定不是波多野結衣?",
                "龜山千廣和近藤公園在龜山公園裏喝酒賞花",
        };
        Segment segment = HanLP.newSegment().enableJapaneseNameRecognize(true);
        for (String sentence : testCase)
        {
            List<Term> termList = segment.seg(sentence);
            System.out.println(termList);
        }
    }
[北川景子/nrj, 參演/v, 了/ul, 林詣彬/nr, 導演/n, 的/uj, 《/w, 速度/n, 與/p, 激情/n, 3/m, 》/w]
[林/ng, 志玲/nz, 亮/v, 相/d, 網友/n, :/w, 確定/v, 不是/c, 波多/nrf, 野/b, 結衣/nz, ?/w]
[龜山千廣/nrj, 和/c, 近藤公園/nrj, 在/p, 龜山/nz, 公園/n, 裏/f, 喝酒/v, 賞花/nz]

關鍵詞提取

內部採用TextRankKeyword實現,用戶可以直接調用TextRankKeyword.getKeywordList(document, size)

  List<String> keywordList = HanLP.extractKeyword(content, 5);

  public static void demoKeyword()
    {
        String content = "程序員(英文Programmer)是從事程序開發、維護的專業人員。" +
                "一般將程序員分爲程序設計人員和程序編碼人員," +
                "但兩者的界限並不非常清楚,特別是在中國。" +
                "軟件從業人員分爲初級程序員、高級程序員、系統" +
                "分析員和項目經理四大類。";
        List<String> keywordList = HanLP.extractKeyword(content, 5);
        System.out.println(keywordList);
    }
[程序員, 人員, 程序, 分爲, 開發]

多線程並行分詞

* 由於HanLP的任何分詞器都是線程安全的,所以用戶只需調用一個配置接口就可以啓用任何分詞器的並行化

  • segment.enableMultithreading(true); // 或者 segment.enableMultithreading(4); //多線程

  • segment.enableMultithreading(false); // 單線程

public static void emoMultithreadingSegment() throws IOException
    {
        Segment segment = new CRFLexicalAnalyzer(HanLP.Config.CRFCWSModelPath).enableCustomDictionary(false); // CRF分詞器效果好,速度慢,並行化之後可以提高一些速度

        String text = "舉辦紀念活動銘記二戰歷史,不忘戰爭帶給人類的深重災難,是爲了防止悲劇重演,確保和平永駐;" +
                "銘記二戰歷史,更是爲了提醒國際社會,需要共同捍衛二戰勝利成果和國際公平正義," +
                "必須警惕和抵制在歷史認知和維護戰後國際秩序問題上的倒行逆施。";
        HanLP.Config.ShowTermNature = false;
        System.out.println(segment.seg(text));
        int pressure = 10000;
        StringBuilder sbBigText = new StringBuilder(text.length() * pressure);
        for (int i = 0; i < pressure; i++)
        {
            sbBigText.append(text);
        }
        text = sbBigText.toString();
        System.gc();

        long start;
        double costTime;
        // 測個速度

        segment.enableMultithreading(false);
        start = System.currentTimeMillis();
        segment.seg(text);
        costTime = (System.currentTimeMillis() - start) / (double) 1000;
        System.out.printf("單線程分詞速度:%.2f字每秒\n", text.length() / costTime);
        System.gc();

        segment.enableMultithreading(true); // 或者 segment.enableMultithreading(4);
        start = System.currentTimeMillis();
        segment.seg(text);
        costTime = (System.currentTimeMillis() - start) / (double) 1000;
        System.out.printf("多線程分詞速度:%.2f字每秒\n", text.length() / costTime);
        System.gc();

        // Note:
        // 內部的並行化機制可以對1萬字以上的大文本開啓多線程分詞
        // 另一方面,HanLP中的任何Segment本身都是線程安全的。
        // 你可以開10個線程用同一個CRFSegment對象切分任意文本,不需要任何線程同步的措施,每個線程都可以得到正確的結果。
    }
[舉辦, 紀念, 活動, 銘記, 二戰, 歷史, ,, 不, 忘, 戰爭, 帶, 給, 人類, 的, 深重, 災難, ,, 是, 爲了, 防止, 悲劇, 重演, ,, 確保, 和平, 永駐, ;, 銘記, 二戰, 歷史, ,, 更是, 爲了, 提醒, 國際, 社會, ,, 需要, 共同, 捍衛, 二戰, 勝利, 成果, 和, 國際, 公平, 正義, ,, 必須, 警惕, 和, 抵制, 在, 歷史, 認知, 和, 維護, 戰後, 國際, 秩序, 問題, 上, 的, 倒行逆施, 。]
單線程分詞速度:234948.60字每秒
多線程分詞速度:994671.40字每秒

詞語提取、新詞發現

public static void demoNewWordDiscover() throws IOException
        {
            // 文本長度越大越好,試試紅樓夢?
            List<WordInfo> wordInfoList = HanLP.extractWords(IOUtil.newBufferedReader("C:\\Users\\12249\\Desktop\\新建文本文檔 (2).txt"), 100);
            System.out.println(wordInfoList);
        }

嗯,沒測試,獲取不到文本。

NLP分詞,更精準的中文分詞、詞性標註與命名實體識別。

NLP分詞NLPTokenizer會執行全部命名實體識別和詞性標註。

* 語料庫規模決定實際效果,面向生產環境的語料庫應當在千萬字量級。歡迎用戶在自己的語料上訓練新模型以適應新領域、識別新的命名實體。
* 標註集請查閱 https://github.com/hankcs/HanLP/blob/master/data/dictionary/other/TagPKU98.csv
* 或者乾脆調用 Sentence#translateLabels() 轉爲中文

  public static void demoNLPSegment ()
    {
        NLPTokenizer.ANALYZER.enableCustomDictionary(false); // 中文分詞≠詞典,不用詞典照樣分詞。
        System.out.println(NLPTokenizer.segment("我新造一個詞叫幻想鄉你能識別並正確標註詞性嗎?")); // “正確”是副形詞。
        // 注意觀察下面兩個“希望”的詞性、兩個“晚霞”的詞性
        System.out.println(NLPTokenizer.analyze("我的希望是希望張晚霞的背影被晚霞映紅").translateLabels());
        System.out.println(NLPTokenizer.analyze("支援臺灣正體香港繁體:微軟公司於1975年由比爾·蓋茲和保羅·艾倫創立。"));
    }
[我/r, 新/d, 造/v, 一個/m, 詞/n, 叫/v, 幻想鄉/ns, 你/r, 能/v, 識別/v, 並/c, 正確/ad, 標註/v, 詞性/n, 嗎/y, ?/w]
我/代詞 的/助詞 希望/名動詞 是/動詞 希望/動詞 張/量詞 晚霞/名詞 的/助詞 背影/名詞 被/介詞 晚霞/名詞 映紅/動詞
支援/v [臺灣/ns 正體/n 香港/ns 繁體/n]/nt :/w [微軟/nt 公司/n]/nt 於/p 1975年/t 由/p 比爾·蓋茲/nr 和/c 保羅·艾倫/nr 創立/v 。/w

正規化字符配置項的效果(繁體->簡體,全角->半角,大寫->小寫)。

* 該配置項位於hanlp.properties中,通過Normalization=true來開啓
* 切換配置後必須刪除CustomDictionary.txt.bin緩存,否則隻影響動態插入的新詞。

 

 public static void demoNormalization()
    {
        HanLP.Config.Normalization = true;
        CustomDictionary.insert("愛聽4G", "nz 1000");
        System.out.println(HanLP.segment("愛聽4g"));
        System.out.println(HanLP.segment("愛聽4G"));
        System.out.println(HanLP.segment("愛聽4G"));
        System.out.println(HanLP.segment("愛聽4G"));
        System.out.println(HanLP.segment("愛聽4G"));
    }
[愛聽4g/nz]
[愛聽4g/nz]
[愛聽4g/nz]
[愛聽4g/nz]
[愛聽4g/nz]

自動去除停用詞、自動斷句的分詞器

public static void demoNotionalTokenizer()
    {
        String text = "小區居民有的反對餵養流浪貓,而有的居民卻贊成餵養這些小寶貝";
        // 自動去除停用詞
        System.out.println(NotionalTokenizer.segment(text));
        // 停用詞典位於data/dictionary/stopwords.txt,可以自行修改
        // 自動斷句+去除停用詞
        for (List<Term> sentence : NotionalTokenizer.seg2sentence(text))
        {
            System.out.println(sentence);
        }
    }
[小區/n, 居民/n, 反對/v, 餵養/vn, 流浪貓/nz, 居民/n, 贊成/v, 餵養/vn, 小寶貝/nz]
[小區/n, 居民/n, 反對/v, 餵養/vn, 流浪貓/nz]
[居民/n, 贊成/v, 餵養/vn, 小寶貝/nz]

N最短路徑分詞,

  • N最短路分詞器NShortSegment比最短路分詞器慢,但是效果稍微好一些,對命名實體識別能力更強。

  • 一般場景下最短路分詞的精度已經足夠,而且速度比N最短路分詞器快幾倍,請酌情選擇。

該分詞器比最短路分詞器慢,但是效果稍微好一些,對命名實體識別能力更強

        public static void DemoNShortSegment()
        {
            Segment nShortSegment = new NShortSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
            Segment shortestSegment = new ViterbiSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
            String[] testCase = new String[]{
              
                    "與中國太平洋財產保險股份有限公司南昌中心支公司保險合同糾紛案",
                    "新北商貿有限公司",
            };
            for (String sentence : testCase)
            {
                System.out.println("N-最短分詞:" + nShortSegment.seg(sentence) + "\n最短路分詞:" + shortestSegment.seg(sentence));
            }
        }

N-最短分詞:[ 與/cc, 中國/ns, 太平洋/ns, 財產保險股份有限公司南昌中心支公司/nt, 保險/n, 合同/n, 糾紛案/nz]
最短路分詞:[與/cc, 中國/ns, 太平洋/ns, 財產保險股份有限公司南昌中心支公司/nt, 保險/n, 合同/n, 糾紛案/nz]
N-最短分詞:[新北商貿有限公司/nt]
最短路分詞:[新北商貿有限公司/nt]

CRF分詞

CRF對新詞有很好的識別能力,但是無法利用自定義詞典。

    public static void DemoCRFSegment() throws IOException {
                HanLP.Config.ShowTermNature = false;    // 關閉詞性顯示
                Segment segment = new CRFLexicalAnalyzer();
                String[] sentenceArray = new String[]
                        {
                                "HanLP是由一系列模型與算法組成的Java工具包,目標是普及自然語言處理在生產環境中的應用。",
                                "鐵桿部隊憤怒情緒集結 馬英九腹背受敵",           // 繁體無壓力
                                "馬英九回應連勝文“丐幫說”:稱黨內同志談話應謹慎",
                                "高錳酸鉀,強氧化劑,紫紅色晶體,可溶於水,遇乙醇即被還原。常用作劑、水淨化劑、氧化劑、漂白劑、毒氣吸收劑、二氧化碳精製劑等。", // 專業名詞有一定辨識能力
                                "《夜晚的骰子》通過描述淺草的舞女在暗夜中扔骰子的情景,寄託了作者對庶民生活區的情感",    // 非新聞語料
                                "這個像是真的[委屈]前面那個打扮太江戶了,一點不上品...@hankcs",                       // 微博
                                "鼎泰豐的小籠一點味道也沒有...每樣都淡淡的...淡淡的,哪有食堂2A的好次",
                                "克里斯蒂娜·克羅爾說:不,我不是虎媽。我全家都熱愛音樂,我也鼓勵他們這麼做。",
                                "今日APPS:Sago Mini Toolbox培養孩子動手能力",
                                "財政部副部長王保安調任國家統計局黨組書記",
                                "2.34米男子娶1.53米女粉絲 稱夫妻生活沒問題",
                                "你看過穆赫蘭道嗎",
                                "樂視超級手機能否承載賈布斯的生態夢"
                        };
                for (String sentence : sentenceArray)
                {
                    List<Term> termList = segment.seg(sentence);
                    System.out.println(termList);
                }
            }
HanLP/nx 是/v 由/p 一/m 系列/q 模型/n 與/c 算法/n 組成/v 的/u Java/vn 工具/n 包/v ,/w 目標/n 是/v 普及/v 自然語言處理/nz 在/p 生產/vn 環境/n 中的/v 應用/vn 。/w
高錳/nr 酸鉀/n ,/w 強氧化劑/n ,/w 紫紅色/n 晶體/n ,/w 可/v 溶於/v 水/n ,/w 遇乙醇/v 即/v 被/p 還原/v 。/w 常用/b 作劑/n 、/w 水/n 淨化劑/n 、/w 氧化劑/n 、/w 漂白劑/n 、/w 毒氣/n 吸收劑/n 、/w 二氧化碳/n 精製劑/n 等/u 。/w
《/w 夜晚/t 的/u 骰子/n 》/w 通過/p 描述/v 淺草/n 的/u 舞女/n 在/p 暗夜/n 中/f 扔/v 骰子/n 的/u 情景/n ,/w 寄託/v 了/u 作者/n 對/p 庶民/n 生活區/n 的/u 情感/n
這個/r 像是/v 真的/d [/w 委屈/n ]/w 前面/f 那個/r 打扮/v 太江戶/n 了/y ,/w 一點/m 不/d 上/v 品...@hankcs/n
鼎泰豐/nr 的/u 小籠/nf 一點/m 味道/n 也/d 沒有/d .../w 每樣/r 都/d 淡淡/a 的/u .../w 淡淡/a 的/u ,/w 哪/r 有/v 食堂/n 2A/m 的/u 好/a 次/Bg
克里斯蒂娜·克羅爾/nr 說/v :/w 不/d ,/w 我/r 不/d 是/v 虎媽/n 。/w 我/r 全家/n 都/d 熱愛/v 音樂/n ,/w 我/r 也/d 鼓勵/v 他們/r 這麼/r 做/v 。/w
今日/t A/nx PPS/n :/w Sago Mini Toolbox/nx 培養/v 孩子/n 動手/v 能力/n
2.34/m 米/q 男子/n 娶/v 1.53/m 米/q 女/b 粉絲 /n 稱/v 夫妻生活/nz 沒/v 問題/n
你/r 看/v 過/u 穆赫蘭道/n 嗎/y
樂視/v 超級/b 手機/n 能否/v 承載/v 賈布斯/nr 的/u 生態夢/n

 

數詞和數量詞識別

 public static void demoNumberAndQuantifierRecognition()
        {
            StandardTokenizer.SEGMENT.enableNumberQuantifierRecognize(true);
            String[] testCase = new String[]
                    {
                            "十九元套餐包括什麼",
                            "九千九百九十九朵玫瑰",
                            "壹佰塊都不給我",
                            "9012345678只螞蟻",
                            "牛奶三〇〇克*2",
                            "ChinaJoy“掃黃”細則露胸超2釐米罰款",
                    };
            for (String sentence : testCase)
            {
                System.out.println(StandardTokenizer.segment(sentence));
            }
        }
[十九元/mq, 套餐/n, 包括/v, 什麼/r]
[九千九百九十九朵/mq, 玫瑰/n]
[壹佰塊/mq, 都/d, 不/d, 給/p, 我/r]
[9012345678只/mq, 螞蟻/n]
[牛奶/n, 三〇〇克/mq, */w, 2/m]
[ChinaJoy/nx, “/w, 掃黃/v, ”/w, 細則/n, 露/v, 胸/ng, 超/v, 2釐米/mq, 罰款/v]

詞共現統計

 public static void DemoOccurrence()
        {
            Occurrence occurrence = new Occurrence();
            occurrence.addAll("在計算機音視頻和圖形圖像技術等二維信息算法處理方面目前比較先進的視頻處理算法");
            occurrence.compute();

            Set<Map.Entry<String, TermFrequency>> uniGram = occurrence.getUniGram();
            for (Map.Entry<String, TermFrequency> entry : uniGram)
            {
                TermFrequency termFrequency = entry.getValue();
                System.out.println(termFrequency);
            }

            Set<Map.Entry<String, PairFrequency>> biGram = occurrence.getBiGram();
            for (Map.Entry<String, PairFrequency> entry : biGram)
            {
                PairFrequency pairFrequency = entry.getValue();
                if (pairFrequency.isRight())
                    System.out.println(pairFrequency);
            }

            Set<Map.Entry<String, TriaFrequency>> triGram = occurrence.getTriGram();
            for (Map.Entry<String, TriaFrequency> entry : triGram)
            {
                TriaFrequency triaFrequency = entry.getValue();
                if (triaFrequency.isRight())
                    System.out.println(triaFrequency);
            }
        }
信息=1
先進=1
圖像=1
圖形=1
處理=2
技術=1
方面=1
比較=1
目前=1
算法=2
視頻=2
計算機=1
音=1
信息→算法= tf=1 mi=15.974323732511902 le=0.0 re=0.0 score=1.1201926307907633
先進→視頻= tf=1 mi=16.02853754910605 le=0.0 re=0.0 score=1.1239943515304154
圖像→技術= tf=1 mi=11.849234163719903 le=0.0 re=0.0 score=0.8309224861706236
圖形→圖像= tf=1 mi=16.495759535389908 le=0.0 re=0.0 score=1.156758093817257
處理→方面= tf=1 mi=9.784949554110385 le=0.0 re=0.0 score=0.6861654093603521
處理→算法= tf=1 mi=16.486909920112925 le=0.0 re=0.0 score=1.1561375183246974
技術→信息= tf=1 mi=9.130573783505678 le=0.0 re=0.0 score=0.6402775878616854
方面→目前= tf=1 mi=8.658819236548384 le=0.0 re=0.0 score=0.6071960016929984
比較→先進= tf=1 mi=10.77626412105942 le=0.0 re=0.0 score=0.755680920081583
目前→比較= tf=1 mi=10.108506174504294 le=0.0 re=0.0 score=0.7088546792085115
算法→處理= tf=1 mi=16.486909920112925 le=0.0 re=0.0 score=1.1561375183246974
視頻→圖形= tf=1 mi=19.71463536025811 le=0.0 re=0.0 score=1.3824803865932096
視頻→處理= tf=1 mi=16.486909920112925 le=0.0 re=0.0 score=1.1561375183246974
計算機→音= tf=1 mi=15.802612354829963 le=0.0 re=0.0 score=1.108151443750666
音→視頻= tf=1 mi=20.120100468366275 le=0.0 re=0.0 score=1.4109134541678408
信息→算法→處理= tf=1 mi=0.0 le=0.0 re=0.0
先進→視頻→處理= tf=1 mi=0.0 le=0.0 re=0.0
圖像→技術→信息= tf=1 mi=0.0 le=0.0 re=0.0
圖形→圖像→技術= tf=1 mi=0.0 le=0.0 re=0.0
處理→方面→目前= tf=1 mi=0.0 le=0.0 re=0.0
技術→信息→算法= tf=1 mi=0.0 le=0.0 re=0.0
方面→目前→比較= tf=1 mi=0.0 le=0.0 re=0.0
比較→先進→視頻= tf=1 mi=0.0 le=0.0 re=0.0
目前→比較→先進= tf=1 mi=0.0 le=0.0 re=0.0
算法→處理→方面= tf=1 mi=0.0 le=0.0 re=0.0
視頻→圖形→圖像= tf=1 mi=0.0 le=0.0 re=0.0
視頻→處理→算法= tf=1 mi=0.0 le=0.0 re=0.0
計算機→音→視頻= tf=1 mi=0.0 le=0.0 re=0.0
音→視頻→圖形= tf=1 mi=0.0 le=0.0 re=0.0

機構名識別

  • 目前分詞器默認關閉了機構名識別,用戶需要手動開啓;這是因爲消耗性能,其實常用機構名都收錄在核心詞典和用戶自定義詞典中。

  • HanLP的目的不是演示動態識別,在生產環境中,能靠詞典解決的問題就靠詞典解決,這是最高效穩定的方法

    public static void DemoOrganizationRecognition()
    {
        String[] testCase = new String[]{
                "我在上海林原科技有限公司兼職工作,",
                "我經常在臺川喜宴餐廳喫飯,",
                "偶爾去開元地中海影城看電影。",
                "不用詞典,福哈生態工程有限公司是動態識別的結果。",
        };
        Segment segment = HanLP.newSegment().enableCustomDictionary(false).enableOrganizationRecognize(true);
        for (String sentence : testCase)
        {
            List<Term> termList = segment.seg(sentence);
            System.out.println(termList);
        }
    }
[我/rr, 在/p, 上海/ns, 林原科技有限公司/nt, 兼職/vn, 工作/vn, ,/w]
[我/rr, 經常/d, 在/p, 臺川喜宴餐廳/nt, 喫飯/vi, ,/w]
[偶爾/d, 去/vf, 開元地中海影城/nt, 看/v, 電影/n, 。/w]
[不用/d, 詞典/n, ,/w, 福哈生態工程有限公司/nt, 是/vshi, 動態/n, 識別/vn, 的/ude1, 結果/n, 。/w]

基於感知機序列標註的詞法分析器,可選多個模型。


* - large訓練自一億字的大型綜合語料庫,是已知範圍內全世界最大的中文分詞語料庫。
* - pku199801訓練自個人修訂版1998人民日報語料1月份,僅有183萬字。
* 語料庫規模決定實際效果,面向生產環境的語料庫應當在千萬字量級。歡迎用戶在自己的語料上訓練新模型以適應新領域、識別新的命名實體。
* 無論在何種語料上訓練,都完全支持簡繁全半角和大小寫。

public class DemoPerceptronLexicalAnalyzer extends TestUtility
{
    public static void main(String[] args) throws IOException
    {
        PerceptronLexicalAnalyzer analyzer = new PerceptronLexicalAnalyzer("data/model/perceptron/pku199801/cws.bin",
                                                                           HanLP.Config.PerceptronPOSModelPath,
                                                                           HanLP.Config.PerceptronNERModelPath);
        System.out.println(analyzer.analyze("上海華安工業(集團)公司董事長譚旭光和祕書胡花蕊來到美國紐約現代藝術博物館參觀"));
        System.out.println(analyzer.analyze("微軟公司於1975年由比爾·蓋茲和保羅·艾倫創立,18年啟動以智慧雲端、前端為導向的大改組。"));

        // 任何模型總會有失誤,特別是98年這種陳舊的語料庫
        System.out.println(analyzer.analyze("通電話討論太空探索技術公司"));
        // 支持在線學習
        analyzer.learn("與/c 特朗普/nr 通/v 電話/n 討論/v [太空/s 探索/vn 技術/n 公司/n]/nt");
        // 學習到新知識
        System.out.println(analyzer.analyze("通電話討論太空探索技術公司"));
        // 還可以舉一反三
        System.out.println(analyzer.analyze("通電話"));

        // 知識的泛化不是死板的規則,而是比較靈活的統計信息
        System.out.println(analyzer.analyze("我在浙江金華出生"));
        analyzer.learn("在/p 浙江/ns 金華/ns 出生/v");
        System.out.println(analyzer.analyze("我在四川金華出生,我的名字叫金華"));

        // 在線學習後的模型支持序列化,以分詞模型爲例:
//        analyzer.getPerceptronSegmenter().getModel().save(HanLP.Config.PerceptronCWSModelPath);

        // 請用戶按需執行對空格製表符等的預處理,只有你最清楚自己的文本中都有些什麼奇怪的東西
        System.out.println(analyzer.analyze("空格 \t\n\r\f&nbsp;統統都不要"
                                                .replaceAll("\\s+", "")    // 去除所有空白符
                                                .replaceAll("&nbsp;", "")  // 如果一些文本中含有html控制符
        ));
    }
}
[上海/ns 華安/nz 工業/n (/w 集團/n )/w 公司/n]/nt 董事長/n 譚旭光/nr 和/c 祕書/n 胡花蕊/nr 來到/v [美國紐約/ns 現代/ntc 藝術/n 博物館/n]/ns 參觀/v
微軟公司/ntc 於/p 1975年/t 由/p 比爾·蓋茲/nr 和/c 保羅·艾倫/nr 創立/v ,/w 18年/t 啟動/v 以/p 智慧/n 雲端/n 、/w 前端/n 為/v 導向/n 的/u 大/a 改組/vn 。/w
通/v 電話/n 討論/v [太空/s 探索/vn 技術/n]/nt 公司/n
通/v 電話/n 討論/v [太空/s 探索/vn 技術/n 公司/n]/nt
通/v 電話/n
我/r 在/p [浙江/ns 金華/ns]/ns 出生/v
我/r 在/p [四川/ns 金華/ns]/ns 出生/v ,/w 我/r 的/u 名字/n 叫/v 金華/nr
空格/n 統統/d 都/d 不要/d

短語提取

**
 * 短語提取
 * @author hankcs
 */
public class DemoPhraseExtractor
{
    public static void main(String[] args)
    {
        String text = "算法工程師\n" +
                "算法(Algorithm)是一系列解決問題的清晰指令,也就是說,能夠對一定規範的輸入,在有限時間內獲得所要求的輸出。" +
                "如果一個算法有缺陷,或不適合於某個問題,執行這個算法將不會解決這個問題。不同的算法可能用不同的時間、" +
                "空間或效率來完成同樣的任務。一個算法的優劣可以用空間複雜度與時間複雜度來衡量。算法工程師就是利用算法處理事物的人。\n" +
                "\n" +
                "1職位簡介\n" +
                "算法工程師是一個非常高端的職位;\n" +
                "專業要求:計算機、電子、通信、數學等相關專業;\n" +
                "學歷要求:本科及其以上的學歷,大多數是碩士學歷及其以上;\n" +
                "語言要求:英語要求是熟練,基本上能閱讀國外專業書刊;\n" +
                "必須掌握計算機相關知識,熟練使用仿真工具MATLAB等,必須會一門編程語言。\n" +
                "\n" +
                "2研究方向\n" +
                "視頻算法工程師、圖像處理算法工程師、音頻算法工程師 通信基帶算法工程師\n" +
                "\n" +
                "3目前國內外狀況\n" +
                "目前國內從事算法研究的工程師不少,但是高級算法工程師卻很少,是一個非常緊缺的專業工程師。" +
                "算法工程師根據研究領域來分主要有音頻/視頻算法處理、圖像技術方面的二維信息算法處理和通信物理層、" +
                "雷達信號處理、生物醫學信號處理等領域的一維信息算法處理。\n" +
                "在計算機音視頻和圖形圖像技術等二維信息算法處理方面目前比較先進的視頻處理算法:機器視覺成爲此類算法研究的核心;" +
                "另外還有2D轉3D算法(2D-to-3D conversion),去隔行算法(de-interlacing),運動估計運動補償算法" +
                "(Motion estimation/Motion Compensation),去噪算法(Noise Reduction),縮放算法(scaling)," +
                "銳化處理算法(Sharpness),超分辨率算法(Super Resolution),手勢識別(gesture recognition),人臉識別(face recognition)。\n" +
                "在通信物理層等一維信息領域目前常用的算法:無線領域的RRM、RTT,傳送領域的調製解調、信道均衡、信號檢測、網絡優化、信號分解等。\n" +
                "另外數據挖掘、互聯網搜索算法也成爲當今的熱門方向。\n" +
                "算法工程師逐漸往人工智能方向發展。";
        List<String> phraseList = HanLP.extractPhrase(text, 5);
        System.out.println(phraseList);
    }
}
[算法工程師, 算法處理, 一維信息, 算法研究, 圖像技術]

漢字轉拼音

/**
 * 漢字轉拼音
 * @author hankcs
 */
public class DemoPinyin
{
    public static void main(String[] args)
    {
        String text = "重載不是重任!";
        List<Pinyin> pinyinList = HanLP.convertToPinyinList(text);
        System.out.print("原文,");
        for (char c : text.toCharArray())
        {
            System.out.printf("%c,", c);
        }
        System.out.println();

        System.out.print("拼音(數字音調),");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin);
        }
        System.out.println();

        System.out.print("拼音(符號音調),");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getPinyinWithToneMark());
        }
        System.out.println();

        System.out.print("拼音(無音調),");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getPinyinWithoutTone());
        }
        System.out.println();

        System.out.print("聲調,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getTone());
        }
        System.out.println();

        System.out.print("聲母,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getShengmu());
        }
        System.out.println();

        System.out.print("韻母,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getYunmu());
        }
        System.out.println();

        System.out.print("輸入法頭,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getHead());
        }
        System.out.println();

        // 拼音轉換可選保留無拼音的原字符
        System.out.println(HanLP.convertToPinyinString("截至2012年,", " ", true));
        System.out.println(HanLP.convertToPinyinString("截至2012年,", " ", false));
    }
}
原文,重,載,不,是,重,任,!,
拼音(數字音調),chong2,zai3,bu2,shi4,zhong4,ren4,none5,
拼音(符號音調),chóng,zǎi,bú,shì,zhòng,rèn,none,
拼音(無音調),chong,zai,bu,shi,zhong,ren,none,
聲調,2,3,2,4,4,4,5,
聲母,ch,z,b,sh,zh,r,none,
韻母,ong,ai,u,i,ong,en,none,
輸入法頭,ch,z,b,sh,zh,r,none,
jie zhi none none none none nian none
jie zhi 2 0 1 2 nian ,

流水線模式

/**
 * 演示流水線模式,幾個概念:
 * - pipe:流水線的一節管道,執行統計分詞或規則邏輯
 * - flow:管道的數據流,在同名方法中執行本節管道的業務
 * - pipeline:流水線,由至少一節管道(統計分詞管道)構成,可自由調整管道的拼裝方式
 *
 * @author hankcs
 */
public class DemoPipeline
{
    private static final Pattern WEB_URL = Pattern.compile("((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?(?:(((([a-zA-Z0-9][a-zA-Z0-9\\-]*)*[a-zA-Z0-9]\\.)+((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(δοκιμή|испытание|рф|срб|טעסט|آزمایشی|إختبار|الاردن|الجزائر|السعودية|المغرب|امارات|بھارت|تونس|سورية|فلسطين|قطر|مصر|परीक्षा|भारत|ভারত|ਭਾਰਤ|ભારત|இந்தியா|இலங்கை|சிங்கப்பூர்|பரிட்சை|భారత్|ලංකා|ไทย|テスト|中國|中國|臺灣|臺灣|新加坡|測試|測試|香港|테스트|한국|xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-3e0b707e|xn\\-\\-45brj9c|xn\\-\\-80akhbyknj4f|xn\\-\\-90a3ac|xn\\-\\-9t4b11yi5a|xn\\-\\-clchc0ea0b2g2a9gcd|xn\\-\\-deba0ad|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-g6w251d|xn\\-\\-gecrj9c|xn\\-\\-h2brj9c|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-j6w193g|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-s9brj9c|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-yfro4i67o|xn\\-\\-ygbi2ammx|xn\\-\\-zckzah|xxx)|y[et]|z[amw]))|((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9]))))(?:\\:\\d{1,5})?)(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?");
    private static final Pattern EMAIL = Pattern.compile("(\\w+(?:[-+.]\\w+)*)@(\\w+(?:[-.]\\w+)*\\.\\w+(?:[-.]\\w+)*)");

    public static void main(String[] args) throws IOException
    {
        LexicalAnalyzerPipeline analyzer = new LexicalAnalyzerPipeline(new PerceptronLexicalAnalyzer());
        // 管道順序=優先級,自行調整管道順序以控制優先級
        analyzer.addFirst(new RegexRecognizePipe(WEB_URL, "【網址】"));
        analyzer.addFirst(new RegexRecognizePipe(EMAIL, "【郵件】"));
        analyzer.addLast(new Pipe<List<IWord>, List<IWord>>() // 自己寫個管道也並非難事
        {
            @Override
            public List<IWord> flow(List<IWord> input)
            {
                for (IWord word : input)
                {
                    if ("nx".equals(word.getLabel()))
                        word.setLabel("字母");
                }
                return input;
            }
        });
        String text = "HanLP的項目地址是https://github.com/hankcs/HanLP,聯繫郵箱[email protected]";
        System.out.println(analyzer.analyze(text));
    }
}
        analyzer.addLast(new Pipe<List<IWord>, List<IWord>>() // 自己寫個管道也並非難事

 

HanLP中的數據結構和接口是靈活的,組合這些接口,可以自己創造新功能

HanLP中的數據結構和接口是靈活的,組合這些接口,可以自己創造新功能
 *
 * @author hankcs
 */
public class DemoPinyinToChinese
{
    public static void main(String[] args)
    {
        StringDictionary dictionary = new StringDictionary("=");
        dictionary.load(HanLP.Config.PinyinDictionaryPath);
        TreeMap<String, Set<String>> map = new TreeMap<String, Set<String>>();
        for (Map.Entry<String, String> entry : dictionary.entrySet())
        {
            String pinyins = entry.getValue().replaceAll("[\\d,]", "");
            Set<String> words = map.get(pinyins);
            if (words == null)
            {
                words = new TreeSet<String>();
                map.put(pinyins, words);
            }
            words.add(entry.getKey());
        }
        Set<String> words = new TreeSet<String>();
        words.add("綠色");
        words.add("濾色");
        map.put("lvse", words);

        // 1.5.2及以下版本
        AhoCorasickDoubleArrayTrie<Set<String>> trie = new AhoCorasickDoubleArrayTrie<Set<String>>();
        trie.build(map);
        System.out.println(CommonAhoCorasickSegmentUtil.segment("renmenrenweiyalujiangbujianlvse", trie));

        // 1.5.3及以上版本
        CommonAhoCorasickDoubleArrayTrieSegment<Set<String>> segment = new CommonAhoCorasickDoubleArrayTrieSegment<Set<String>>(map);
        System.out.println(segment.segment("renmenrenweiyalujiangbujianlvse"));

    }
}
[renmen/[人們], renwei/[人爲, 認爲], yalujiang/[鴨綠江], bujian/[不見], lvse/[濾色, 綠色]]
[renmen/[人們], renwei/[人爲, 認爲], yalujiang/[鴨綠江], bujian/[不見], lvse/[濾色, 綠色]]

 地名識別

Segment segment = HanLP.newSegment().enablePlaceRecognize(true);

  • 目前標準分詞器都默認關閉了地名識別,用戶需要手動開啓;這是因爲消耗性能,其實多數地名都收錄在核心詞典和用戶自定義詞典中。

  • 在生產環境中,能靠詞典解決的問題就靠詞典解決,這是最高效穩定的方法。

/**
 * 地名識別
 * @author hankcs
 */
public class DemoPlaceRecognition
{
    public static void main(String[] args)
    {
        String[] testCase = new String[]{
                "藍翔給寧夏固原市彭陽縣紅河鎮黑牛溝村捐贈了挖掘機",
        };
        Segment segment = HanLP.newSegment().enablePlaceRecognize(true);
        for (String sentence : testCase)
        {
            List<Term> termList = segment.seg(sentence);
            System.out.println(termList);
        }
    }
}
[藍翔/nr, 給/p, 寧夏/ns, 固原市/ns, 彭陽縣/ns, 紅河鎮/ns, 黑牛溝村/ns, 捐贈/v, 了/ule, 挖掘機/n]

詞性標註 

詞性標註(Part-of-Speech tagging 或POS tagging),又稱詞類標註或者簡稱標註,是指爲分詞結果中的每個單詞標註一個正確的詞性的程序,也即確定每個詞是名詞、動詞、形容詞或其他詞性的過程。在漢語中,詞性標註比較簡單,因爲漢語詞彙詞性多變的情況比較少見,大多詞語只有一個詞性,或者出現頻次最高的詞性遠遠高於第二位的詞性。據說,只需選取最高頻詞性,即可實現80%準確率的中文詞性標註程序。

利用HMM即可實現更高準確率的詞性標註,

/**
 * 詞性標註
 * @author hankcs
 */
public class DemoPosTagging
{
    public static void main(String[] args)
    {
        String text = "教授正在教授自然語言處理課程";
        Segment segment = HanLP.newSegment();

        System.out.println("未標註:" + segment.seg(text));
        segment.enablePartOfSpeechTagging(true);
        System.out.println("標註後:" + segment.seg(text));
    }
}
未標註:[教授/nnt, 正在/d, 教授/nnt, 自然語言處理/nz, 課程/n]
標註後:[教授/nnt, 正在/d, 教授/v, 自然語言處理/nz, 課程/n]

改寫句子

public class DemoRewriteText
{
    public static void main(String[] args)
    {
        String text = "這個方法可以利用同義詞詞典將一段文本改寫成意思相似的另一段文本,而且差不多符合語法";
        System.out.println(CoreSynonymDictionary.rewrite(text));
    }
}
這措施可以使用同義詞詞典將同截文本改寫成意思相似的別樣一樣段文本,而且多符合語法

 

標準分詞

HanLP中有一系列“開箱即用”的靜態分詞器,以Tokenizer結尾,HanLP.segment其實是對StandardTokenizer.segment的包裝

/**
 * 標準分詞
 *
 * @author hankcs
 */
public class DemoSegment
{
    public static void main(String[] args)
    {
        String[] testCase = new String[]{
                "商品和服務",
                "當下雨天地面積水分外嚴重",
                "結婚的和尚未結婚的確實在干擾分詞啊",
                "買水果然後來世博園最後去世博會",
                "中國的首都是北京",
                "歡迎新老師生前來就餐",
                "工信處女幹事每月經過下屬科室都要親口交代24口交換機等技術性器件的安裝工作",
                "隨着頁遊興起到現在的頁遊繁盛,依賴於存檔進行邏輯判斷的設計減少了,但這塊也不能完全忽略掉。",
        };
        for (String sentence : testCase)
        {
            List<Term> termList = HanLP.segment(sentence);
            System.out.println(termList);
        }
    }
}
[商品/n, 和/cc, 服務/vn]
[當/p, 下雨天/n, 地面/n, 積水/n, 分外/d, 嚴重/a]
[結婚/vi, 的/ude1, 和/cc, 尚未/d, 結婚/vi, 的/ude1, 確實/ad, 在/p, 干擾/vn, 分詞/n, 啊/y]
[買/v, 水果/n, 然後/c, 來/vf, 世博園/n, 最後/f, 去/vf, 世博會/n]
[中國/ns, 的/ude1, 首都/n, 是/vshi, 北京/ns]
[歡迎/v, 新/a, 老/a, 師生/n, 前來/vi, 就餐/vi]
[工信處/n, 女/b, 幹事/nnt, 每月/t, 經過/p, 下屬/v, 科室/n, 都/d, 要/v, 親口/d, 交代/v, 24/m, 口/n, 交換機/n, 等/udeng, 技術性/n, 器件/n, 的/ude1, 安裝/v, 工作/vn]
[隨着/p, 頁遊/nz, 興起/v, 到/v, 現在/t, 的/ude1, 頁遊/nz, 繁盛/a, ,/w, 依賴於/v, 存檔/vi, 進行/vn, 邏輯/n, 判斷/v, 的/ude1, 設計/vn, 減少/v, 了/ule, ,/w, 但/c, 這/rzv, 塊/q, 也/d, 不能/v, 完全/ad, 忽略/v, 掉/v, 。/w]

第一個demo,演示文本分類最基本的調用方式

/**
 * 第一個demo,演示文本分類最基本的調用方式
 *
 * @author hankcs
 */
public class DemoSentimentAnalysis
{
    /**
     * 中文情感挖掘語料-ChnSentiCorp 譚松波
     */
    public static final String CORPUS_FOLDER = TestUtility.ensureTestData("ChnSentiCorp情感分析酒店評論", "http://file.hankcs.com/corpus/ChnSentiCorp.zip");

    public static void main(String[] args) throws IOException
    {
        IClassifier classifier = new NaiveBayesClassifier(); // 創建分類器,更高級的功能請參考IClassifier的接口定義
        classifier.train(CORPUS_FOLDER);                     // 訓練後的模型支持持久化,下次就不必訓練了
        predict(classifier, "前臺客房服務態度非常好!早餐很豐富,房價很乾淨。再接再厲!");
        predict(classifier, "結果大失所望,燈光昏暗,空間極其狹小,牀墊質量惡劣,房間還伴着一股黴味。");
        predict(classifier, "可利用文本分類實現情感分析,效果還行");
    }

    private static void predict(IClassifier classifier, String text)
    {
        System.out.printf("《%s》 情感極性是 【%s】\n", text, classifier.classify(text));
    }

    static
    {
        File corpusFolder = new File(CORPUS_FOLDER);
        if (!corpusFolder.exists() || !corpusFolder.isDirectory())
        {
            System.err.println("沒有文本分類語料,請閱讀IClassifier.train(java.lang.String)中定義的語料格式、準備語料");
            System.exit(1);
        }
    }
}
模式:訓練集
文本編碼:UTF-8
根目錄:C:\Users\12249\Desktop\HanLP-1.x\data\test\ChnSentiCorp情感分析酒店評論
加載中...
[正面]...100.00% 2000 篇文檔
[負面]...100.00% 226 篇文檔
耗時 6973 ms 加載了 2 個類目,共 2226 篇文檔
原始數據集大小:2226
使用卡方檢測選擇特徵中...耗時 25 ms,選中特徵數:511 / 9049 = 5.65%
貝葉斯統計結束
《前臺客房服務態度非常好!早餐很豐富,房價很乾淨。再接再厲!》 情感極性是 【正面】
《結果大失所望,燈光昏暗,空間極其狹小,牀墊質量惡劣,房間還伴着一股黴味。》 情感極性是 【負面】
《可利用文本分類實現情感分析,效果還行》 情感極性是 【正面】

如何去除停用詞

  停用詞是指在信息檢索中,爲節省存儲空間和提高搜索效率,在處理自然語言數據(或文本)之前或之後會自動過濾掉某些字或詞,這些字或詞即被稱爲Stop Words(停用詞)。這些停用詞都是人工輸入、非自動化生成的,生成後的停用詞會形成一個停用詞表。

**
 * 演示如何去除停用詞
 *
 * @author hankcs
 */
public class DemoStopWord
{
    public static void main(String[] args)
    {
        String text = "小區居民有的反對餵養流浪貓,而有的居民卻贊成餵養這些小寶貝";
        // 可以動態修改停用詞詞典
        CoreStopWordDictionary.add("居民");
        System.out.println(NotionalTokenizer.segment(text));
        CoreStopWordDictionary.remove("居民");
        System.out.println(NotionalTokenizer.segment(text));
        // 可以對任意分詞器的結果執行過濾
        List<Term> termList = BasicTokenizer.segment(text);
        System.out.println(termList);
        CoreStopWordDictionary.apply(termList);
        System.out.println(termList);
        // 還可以自定義過濾邏輯
        CoreStopWordDictionary.FILTER = new Filter()
        {
            @Override
            public boolean shouldInclude(Term term)
            {
                if (term.nature == nz)
                {
                    return !CoreStopWordDictionary.contains(term.word);
                }
                return false;
            }
        };
        System.out.println(NotionalTokenizer.segment(text));
    }
}
[小區/n, 反對/v, 餵養/v, 流浪貓/nz, 贊成/v, 餵養/v, 小寶貝/nz]
[小區/n, 居民/n, 反對/v, 餵養/v, 流浪貓/nz, 居民/n, 贊成/v, 餵養/v, 小寶貝/nz]
[小區/n, 居民/n, 有/vyou, 的/ude1, 反對/v, 餵養/v, 流浪貓/nz, ,/w, 而/cc, 有的/rz, 居民/n, 卻/d, 贊成/v, 餵養/v, 這些/rz, 小寶貝/nz]
[小區/n, 居民/n, 反對/v, 餵養/v, 流浪貓/nz, 居民/n, 贊成/v, 餵養/v, 小寶貝/nz]
[流浪貓/nz, 小寶貝/nz]

文本推薦

(句子級別,從一系列句子中挑出與輸入句子最相似的那一個)

  • 在搜索引擎的輸入框中,用戶輸入一個詞,搜索引擎會聯想出最合適的搜索詞,HanLP實現了類似的功能。

  • 可以動態調節每種識別器的權重

**
 * 文本推薦(句子級別,從一系列句子中挑出與輸入句子最相似的那一個)
 * @author hankcs
 */
public class DemoSuggester
{
    public static void main(String[] args)
    {
        Suggester suggester = new Suggester();
        String[] titleArray =
        (
                "威廉王子發表演說 呼籲保護野生動物\n" +
                "魅惑天后許佳慧不愛“預謀” 獨唱《許某某》\n" +
              
                "“黑格比”橫掃菲:菲吸取“海燕”經驗及早疏散\n" +
                "日本保密法將正式生效 日媒指其損害國民知情權\n" +
                "英報告說空氣污染帶來“公共健康危機”"
        ).split("\\n");
        for (String title : titleArray)
        {
            suggester.addSentence(title);
        }

        System.out.println(suggester.suggest("陳述", 2));       // 語義
        System.out.println(suggester.suggest("危機", 1));   // 字符
        System.out.println(suggester.suggest("mayun", 1));      // 拼音
        System.out.println(suggester.suggest("徐家彙", 1));      // 拼音
    }
}
[威廉王子發表演說 呼籲保護野生動物, 英報告說空氣污染帶來“公共健康危機”]
[英報告說空氣污染帶來“公共健康危機”]

[魅惑天后許佳慧不愛“預謀” 獨唱《許某某》]

自動摘要

內部採用TextRankSentence實現,用戶可以直接調用TextRankSentence.getTopSentenceList(document, size)

**
 * 自動摘要
 * @author hankcs
 */
public class DemoSummary
{
    public static void main(String[] args)
    {
        String document = "水利部水資源司司長陳明忠9月29日在國務院新聞辦舉行的新聞發佈會上透露," +
                "根據剛剛完成了水資源管理制度的考覈,有部分省接近了紅線的指標," +
                "有部分省超過紅線的指標。對一些超過紅線的地方,陳明忠表示,對一些取用水項目進行區域的限批," +
                "嚴格地進行水資源論證和取水許可的批准。";
        List<String> sentenceList = HanLP.extractSummary(document, 3);
        System.out.println(sentenceList);
    }
}
[嚴格地進行水資源論證和取水許可的批准, 有部分省超過紅線的指標, 水利部水資源司司長陳明忠9月29日在國務院新聞辦舉行的新聞發佈會上透露]

文本分類最基本的調用方式 

**
 * 第一個demo,演示文本分類最基本的調用方式
 *
 * @author hankcs
 */
public class DemoTextClassification
{
    /**
     * 搜狗文本分類語料庫5個類目,每個類目下1000篇文章,共計5000篇文章
     */
    public static final String CORPUS_FOLDER = TestUtility.ensureTestData("搜狗文本分類語料庫迷你版", "http://file.hankcs.com/corpus/sogou-text-classification-corpus-mini.zip");
    /**
     * 模型保存路徑
     */
    public static final String MODEL_PATH = "data/test/classification-model.ser";


    public static void main(String[] args) throws IOException
    {
        IClassifier classifier = new NaiveBayesClassifier(trainOrLoadModel());
        predict(classifier, "C羅獲2018環球足球獎最佳球員 德尚榮膺最佳教練");
        predict(classifier, "英國造航母耗時8年仍未服役 被中國速度遠遠甩在身後");
        predict(classifier, "研究生考錄模式亟待進一步專業化");
        predict(classifier, "如果真想用食物解壓,建議可以食用燕麥");
        predict(classifier, "通用及其部分競爭對手目前正在考慮解決庫存問題");
    }

    private static void predict(IClassifier classifier, String text)
    {
        System.out.printf("《%s》 屬於分類 【%s】\n", text, classifier.classify(text));
    }

    private static NaiveBayesModel trainOrLoadModel() throws IOException
    {
        NaiveBayesModel model = (NaiveBayesModel) IOUtil.readObjectFrom(MODEL_PATH);
        if (model != null) return model;

        File corpusFolder = new File(CORPUS_FOLDER);
        if (!corpusFolder.exists() || !corpusFolder.isDirectory())
        {
            System.err.println("沒有文本分類語料,請閱讀IClassifier.train(java.lang.String)中定義的語料格式與語料下載:" +
                                   "https://github.com/hankcs/HanLP/wiki/%E6%96%87%E6%9C%AC%E5%88%86%E7%B1%BB%E4%B8%8E%E6%83%85%E6%84%9F%E5%88%86%E6%9E%90");
            System.exit(1);
        }

        IClassifier classifier = new NaiveBayesClassifier(); // 創建分類器,更高級的功能請參考IClassifier的接口定義
        classifier.train(CORPUS_FOLDER);                     // 訓練後的模型支持持久化,下次就不必訓練了
        model = (NaiveBayesModel) classifier.getModel();
        IOUtil.saveObjectTo(model, MODEL_PATH);
        return model;
    }
}

沒測:

聚類數量匹配

public class DemoTextClustering
{
    public static void main(String[] args)
    {
        ClusterAnalyzer<String> analyzer = new ClusterAnalyzer<String>();
        analyzer.addDocument("趙一", "流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 藍調, 藍調, 藍調, 藍調, 藍調, 藍調, 搖滾, 搖滾, 搖滾, 搖滾");
        analyzer.addDocument("錢二", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲");
        analyzer.addDocument("張三", "古典, 古典, 古典, 古典, 民謠, 民謠, 民謠, 民謠");
        analyzer.addDocument("李四", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 金屬, 金屬, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲");
        analyzer.addDocument("王五", "流行, 流行, 流行, 流行, 搖滾, 搖滾, 搖滾, 嘻哈, 嘻哈, 嘻哈");
        analyzer.addDocument("馬六", "古典, 古典, 古典, 古典, 古典, 古典, 古典, 古典, 搖滾");
        System.out.println(analyzer.kmeans(3));
        System.out.println(analyzer.repeatedBisection(3));
        System.out.println(analyzer.repeatedBisection(1.0)); // 自動判斷聚類數量k
    }
}
[[張三, 馬六], [李四, 錢二], [王五, 趙一]]
[[李四, 錢二], [王五, 趙一], [馬六, 張三]]
[[李四, 錢二], [王五, 趙一], [張三, 馬六]]

 基於AhoCorasickDoubleArrayTrie的分詞器

/**
 * 基於AhoCorasickDoubleArrayTrie的分詞器,該分詞器允許用戶跳過核心詞典,直接使用自己的詞典。
 * 需要注意的是,自己的詞典必須遵守HanLP詞典格式。
 *
 * @author hankcs
 */
public class DemoUseAhoCorasickDoubleArrayTrieSegment
{
    public static void main(String[] args) throws IOException
    {
        // AhoCorasickDoubleArrayTrieSegment要求用戶必須提供自己的詞典路徑
        AhoCorasickDoubleArrayTrieSegment segment = new AhoCorasickDoubleArrayTrieSegment(HanLP.Config.CustomDictionaryPath[0]);
        System.out.println(segment.seg("微觀經濟學繼續教育循環經濟"));
    }
}
[微觀經濟學/null, 繼續教育/null, 循環經濟/null]

URL識別

/**
 * 演示URL識別
 *
 * @author hankcs
 */
public class DemoURLRecognition
{
    public static void main(String[] args)
    {
        String text =
                "HanLP的項目地址是https://github.com/hankcs/HanLP," +
                        "發佈地址是https://github.com/hankcs/HanLP/releases," +
                        "我有時候會在www.hankcs.com上面發佈一些消息," +
                        "我的微博是http://weibo.com/hankcs/,會同步推送hankcs.com的新聞。" 
                      ;
        List<Term> termList = URLTokenizer.segment(text);
        System.out.println(termList);
        for (Term term : termList)
        {
            if (term.nature == Nature.xu)
                System.out.println(term.word);
        }
    }
}
[HanLP/nx, 的/ude1, 項目/n, 地址/n, 是/vshi, https://github.com/hankcs/HanLP/xu, ,/w, 發佈/v, 地址/n, 是/vshi, https://github.com/hankcs/HanLP/releases/xu, ,/w, 我/rr, 有時候/d, 會/v, 在/p, www/nx, ./w, hankcs/nrf, ./w, com/nx, 上面/f, 發佈/v, 一些/m, 消息/n, ,/w, 我/rr, 的/ude1, 微博/n, 是/vshi, http://weibo.com/hankcs/xu, //w, ,/w, 會/v, 同步/vd, 推送/nz, hankcs/nrf, ./w, com/nx, 的/ude1, 新聞/n, 。/w, 聽說/v, ./w, 中國/ns, 域名/n, 開放/v, 申請/v, 了/ule, ,/w, 但/c, 我/rr, 並/cc, 沒有/v, 申請/v, hankcs/nrf, ./w, 中國/ns, ,/w, 因爲/c, 窮/a, ……/w]
https://github.com/hankcs/HanLP
https://github.com/hankcs/HanLP/releases
http://weibo.com/hankcs

音譯人名識別

目前分詞器基本上都默認開啓了音譯人名識別,用戶不必手動開啓;上面的代碼只是爲了強調。

**
 * 音譯人名識別
 * @author hankcs
 */
public class DemoTranslatedNameRecognition
{
    public static void main(String[] args)
    {
        String[] testCase = new String[]{
                "一桶冰水當頭倒下,微軟的比爾蓋茨、Facebook的扎克伯格跟桑德博格、亞馬遜的貝索斯、蘋果的庫克全都不惜溼身入鏡,這些硅谷的科技人,飛蛾撲火似地犧牲演出,其實全爲了慈善。",
                "世界上最長的姓名是簡森·喬伊·亞歷山大·比基·卡利斯勒·達夫·埃利奧特·福克斯·伊維魯莫·馬爾尼·梅爾斯·帕特森·湯普森·華萊士·普雷斯頓。",
        };
        Segment segment = HanLP.newSegment().enableTranslatedNameRecognize(true);
        for (String sentence : testCase)
        {
            List<Term> termList = segment.seg(sentence);
            System.out.println(termList);
        }
    }
}

 

[一桶/nz, 冰水/n, 當頭/vi, 倒下/v, ,/w, 微軟/ntc, 的/ude1, 比爾蓋茨/nrf, 、/w, Facebook/nx, 的/ude1, 扎克伯格/nrf, 跟/p, 桑德博格/nrf, 、/w, 亞馬遜/nrf, 的/ude1, 貝索斯/nrf, 、/w, 蘋果/nf, 的/ude1, 庫克/nrf, 全都/d, 不惜/v, 溼身/nz, 入鏡/nz, ,/w, 這些/rz, 硅谷/ns, 的/ude1, 科技/n, 人/n, ,/w, 飛蛾/n, 撲火/vn, 似/vg, 地/ude2, 犧牲/v, 演出/vn, ,/w, 其實/d, 全/a, 爲了/p, 慈善/a, 。/w]
[世界/n, 上/f, 最長/d, 的/ude1, 姓名/n, 是/vshi, 簡森/nr, ·/w, 喬伊/nr, ·/w, 亞歷山大/ns, ·/w, 比/p, 基/ng, ·/w, 卡利/nz, 斯/b, 勒/v, ·/w, 達夫·埃利奧特·福克斯·伊維魯莫·馬爾尼·梅爾斯·帕特森·湯普森·華萊士·普雷斯頓/nrf, 。/w]

繁體中文分詞 

**
 * 繁體中文分詞
 *
 * @author hankcs
 */
public class DemoTraditionalChineseSegment
{
    public static void main(String[] args)
    {
        List<Term> termList = TraditionalChineseTokenizer.segment("大衛貝克漢不僅僅是名著名球員,球場以外,其妻為前" +
                                                                          "辣妹合唱團成員維多利亞·碧鹹,亦由於他擁有" +
                                                                          "突出外表、百變髮型及正面的形象,以至自己" +
                                                                          "品牌的男士香水等商品,及長期擔任運動品牌" +
                                                                          "Adidas的代言人,因此對大眾傳播媒介和時尚界" +
                                                                          "等方面都具很大的影響力,在足球圈外所獲得的" +
                                                                          "認受程度可謂前所未見。");
        System.out.println(termList);

        termList = TraditionalChineseTokenizer.segment("(中央社記者黃巧雯臺北20日電)外需不振,影響接單動能,經濟部今天公佈7月外銷訂單金額362.9億美元,年減5%," +
                                                               "連續4個月衰退,減幅較6月縮小。1040820\n");
        System.out.println(termList);

        termList = TraditionalChineseTokenizer.segment("中央社記者黃巧雯臺北20日電");
        System.out.println(termList);
    }
}
[大衛貝克漢/nrf, 不僅僅/d, 是/vshi, 名/q, 著名/a, 球員/nnt, ,/w, 球場/n, 以外/f, ,/w, 其/rz, 妻/ng, 為/p, 前/f, 辣妹/nz, 合唱團/nis, 成員/nnt, 維多利亞/nsf, ·/w, 碧/ag, 鹹/a, ,/w, 亦/d, 由於/p, 他/rr, 擁有/v, 突出/a, 外表/n, 、/w, 百變/nz, 髮型/n, 及/cc, 正面/b, 的/ude1, 形象/n, ,/w, 以至/c, 自己/rr, 品牌/n, 的/ude1, 男士/n, 香水/n, 等/udeng, 商品/n, ,/w, 及/cc, 長期/d, 擔任/v, 運動/vn, 品牌/n, Adidas/nx, 的/ude1, 代言人/nnt, ,/w, 因此/c, 對/p, 大眾/ntc, 傳播/vn, 媒介/n, 和/cc, 時尚界/nz, 等/udeng, 方面/n, 都/d, 具/vg, 很大/d, 的/ude1, 影響力/n, ,/w, 在/p, 足球/n, 圈外/nz, 所/usuo, 獲得/v, 的/ude1, 認/v, 受/v, 程度/n, 可謂/v, 前所未見/n, 。/w]
[(/w, 中央社/nz, 記者/nnt, 黃巧雯/nr, 臺北/ns, 20/m, 日/b, 電/n, )/w, 外需/n, 不/d, 振/vg, ,/w, 影響/vn, 接單/nz, 動能/n, ,/w, 經濟部/nis, 今天/t, 公佈/v, 7月/t, 外銷/v, 訂單/n, 金額/n, 362.9/m, 億/m, 美元/q, ,/w, 年/qt, 減/v, 5/m, %/nx, ,/w, 連續/a, 4/m, 個/q, 月/n, 衰退/vi, ,/w, 減幅/n, 較/d, 6月/t, 縮小/v, 。/w, 1040820/m]
[中央社/nz, 記者/nnt, 黃巧雯/nr, 臺北/ns, 20/m, 日/b, 電/n]

 

「以後等你當上皇后,就能買草莓慶祝了」。發現一根白頭髮
憑藉筆記本電腦寫程序HanLP
hankcs在臺灣寫程式碼
hankcs在臺灣寫代碼
hankcs在香港寫代碼
hankcs在香港寫代碼
hankcs在臺灣寫程式碼
hankcs在香港寫代碼
hankcs在臺灣寫程式碼
hankcs在臺灣寫代碼
hankcs在臺灣寫代碼
hankcs在臺灣寫代碼

動態設置預置分詞器,這裏的設置是全局的

/**
 * 演示動態設置預置分詞器,這裏的設置是全局的
 * @author hankcs
 */
public class DemoTokenizerConfig
{
    public static void main(String[] args)
    {
        String text = "澤田依子是上外日本文化經濟學院的外教";
        System.out.println(StandardTokenizer.segment(text));
        StandardTokenizer.SEGMENT.enableAllNamedEntityRecognize(true);
        System.out.println(StandardTokenizer.segment(text));
    }
}
[澤田依/nr, 子/ng, 是/vshi, 上外/nit, 日本/ns, 文化/n, 經濟學院/nit, 的/ude1, 外教/n]
[澤田依子/nrj, 是/vshi, 上外日本文化經濟學院/nt, 的/ude1, 外教/n]

語義距離

設想的應用場景是搜索引擎對詞義的理解,詞與詞並不只存在“同義詞”與“非同義詞”的關係,就算是同義詞,它們之間的意義也是有微妙的差別的。

/**
 * 語義距離
 * @author hankcs
 */
public class DemoWordDistance
{
    public static void main(String[] args)
    {
        String[] wordArray = new String[]
                {
                        "香蕉",
                        "蘋果",
                        "白菜",
                        "水果",
                        "蔬菜",
                        "自行車",
                        "公交車",
                        "飛機",
                        "買",
                        "賣",
                        "購入",
                        "新年",
                        "春節",
                        "丟失",
                        "補辦",
                        "辦理",
                        "送給",
                        "尋找",
                        "孩子",
                        "教室",
                        "教師",
                        "會計",
                };
        for (String a : wordArray)
        {
            for (String b : wordArray)
            {
                System.out.println(a + "\t" + b + "\t之間的距離是\t" + CoreSynonymDictionary.distance(a, b));
            }
        }
    }
}
香蕉	香蕉	之間的距離是	0
香蕉	蘋果	之間的距離是	19980
香蕉	白菜	之間的距離是	2628369
香蕉	水果	之間的距離是	32967
香蕉	蔬菜	之間的距離是	2630367
香蕉	自行車	之間的距離是	1854515628
香蕉	公交車	之間的距離是	1854535619
香蕉	飛機	之間的距離是	1857307833
香蕉	買	之間的距離是	39729797433
香蕉	賣	之間的距離是	39729897333
香蕉	購入	之間的距離是	39729797433
香蕉	新年	之間的距離是	4981789224
。。。。。。。。。。。。。。

依存句法解析

  • 內部採用NeuralNetworkDependencyParser實現,用戶可以直接調用NeuralNetworkDependencyParser.compute(sentence)

  • 也可以調用基於MaxEnt的依存句法分析器MaxEntDependencyParser.compute(sentence)

/**
 * 依存句法分析(神經網絡句法模型需要-Xms1g -Xmx1g -Xmn512m)
 * @author hankcs
 */
public class DemoDependencyParser
{
    public static void main(String[] args)
    {
        CoNLLSentence sentence = HanLP.parseDependency("徐先生還具體幫助他確定了把畫雄鷹、松鼠和麻雀作爲主攻目標。");
        System.out.println(sentence);
        // 可以方便地遍歷它
        for (CoNLLWord word : sentence)
        {
            System.out.printf("%s --(%s)--> %s\n", word.LEMMA, word.DEPREL, word.HEAD.LEMMA);
        }
        // 也可以直接拿到數組,任意順序或逆序遍歷
        CoNLLWord[] wordArray = sentence.getWordArray();
        for (int i = wordArray.length - 1; i >= 0; i--)
        {
            CoNLLWord word = wordArray[i];
            System.out.printf("%s --(%s)--> %s\n", word.LEMMA, word.DEPREL, word.HEAD.LEMMA);
        }
        // 還可以直接遍歷子樹,從某棵子樹的某個節點一路遍歷到虛根
        CoNLLWord head = wordArray[12];
        while ((head = head.HEAD) != null)
        {
            if (head == CoNLLWord.ROOT) System.out.println(head.LEMMA);
            else System.out.printf("%s --(%s)--> ", head.LEMMA, head.DEPREL);
        }
    }
}

實戰:

對一個字符串進行格式化輸出,把一份目錄以指定的格式輸出。

思路,結合分詞工具,利用詞性的組成,控制格式。

package testdemo;

import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.suggest.Suggester;

import java.util.Arrays;
import java.util.List;

/**
 * @Description : 對一個字符串進行格式化輸出
 * @Author: Liruilong
 * @Date: 2020/3/25 16:10
 */

public class TwoTopic {

    static String sentence = "第一節 概覽\n" +
            "一、發行人基本情況\n" +
            "1)基本情況\n" +
            "2)業務說明\n" +
            "二、發行人股東、董監高情況\n" +
            "(1)、股東簡歷\n" +
            "(2)、監事簡歷\n" +
            "(3)、高管簡歷\n" +
            "第二節 業務與技術\n" +
            "1、公司基本業務\n" +
            "(1)、業務介紹\n" +
            "(2)、競爭情況\n" +
            "2、公司技術實力\n" +
            "(1)、業務介紹";

    public static void main(String[] args) {
        twoTopic(sentence);
    }



    public static void twoTopic(String sentence ) {
        Suggester suggester = new Suggester();
        String[] strings = sentence.split("\n");
        StringBuilder stringBuilder = new StringBuilder();
        Arrays.stream(strings).forEach(o -> {
                    List<Term> stermList = HanLP.segment(o);
                    String s = stermList.stream()
                            .map(o1 -> o1.nature != null ? o1.nature.toString() : "空")
                            .reduce("", (a, b) -> a + b);
                    if (s.indexOf("mqnwn") != -1) {
                        stringBuilder.append(o.trim() + "\n");
                    } else if (s.indexOf("mwn") != -1 && o.indexOf(")") == -1) {
                        stringBuilder.append("       " + o.trim() + "\n");
                    } else {
                        stringBuilder.append("           " + o.trim() + "\n");
                    }
                }

        );
        System.out.println(stringBuilder);

    }
}

抽取文本中的連續日期

也是利用詞性分析,獲取分詞(年份和標點符號),之後根據需求格式化

package testdemo;

import com.hankcs.hanlp.seg.NShort.NShortSegment;
import com.hankcs.hanlp.seg.Segment;
import com.hankcs.hanlp.seg.common.Term;

import java.util.List;


/**
 * @Author Liruilong
 * @Description 抽取文本中的連續日期
 * @Date 0:59 2020/3/26
 * @Param
 * @return
 **/

public class ThreeTopic {

    static String sentence = "報告期內,即2014年、2015年、2016年公司淨利潤爲2000.00萬元、3000.00萬元、5000.00萬元。";

    public static void main(String[] args) {
        threeTopic(sentence);
    }



    public static void threeTopic(String sentence) {
        Segment nShortSegment = new NShortSegment().enableCustomDictionary(false)
                .enablePlaceRecognize(true).enableOrganizationRecognize(true);
        List<Term> stermList = nShortSegment.seg(sentence);
        String s = stermList.stream().filter(o -> {
                    String nature = o.nature != null ? o.nature.toString() : "空";
                    String word = o.word != null ? o.word.toString() : "空";
                    switch (nature) {
                        case "t":
                            return true;
                        case "w":
                            return "、".equals(word) ? true : false;
                        default:
                            return false;
                    }
                }
        ).map(o -> o.word).reduce("", (a, b) -> a + b);
        System.out.println(s.substring(0, s.lastIndexOf("年") + 1));
    }
}

將日期區間轉換成連續日期

根據詞性獲取時間差和逗號後面的文本,將時間差加工格式化爲指定格式,與逗號後面文本拼接 

package testdemo;

import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.seg.common.Term;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description :將日期區間轉換成連續日期
 * @Author: Liruilong
 * @Date: 2020/3/25 21:35
 */
public class FourTopic {

    static  String[] strings = new String[]{"2014年至2016年,公司基本情況","2014至2016年,公司基本情況"
            ,"2014年-2016年,公司基本情況","2014-2016年,公司基本情況"};

    public static void main(String[] args) {
        for (String sentence : strings) {
            fourTopic(sentence);
        }
    }


    public static void fourTopic(String sentence) {

        List<Term> stermList = HanLP.segment(sentence);
        StringBuffer stringBuffer = new StringBuffer();
        int index = 0;
        for (int i = 0; i < stermList.size(); i++) {
            if ((stermList.get(i).nature != null ? stermList.get(i).nature.toString() : "空").equals("w")){
                 index = i;
                    break;
            }
        }
        for (int i = index; i < stermList.size(); i++) {
            stringBuffer.append((stermList.get(i).word != null ? stermList.get(i).word.toString() : "空"));
        }
        List<LocalDate> localDate = stermList.stream()
                .filter(o ->
                        (o.nature != null ? o.nature.toString() : "空").equals("m"))
                .map(o -> {
                    return LocalDate.of(Integer.parseInt(o.word.toString()), 1, 1);
                }).collect(Collectors.toList());

        StringBuilder yer = new StringBuilder();
        for (int i = (localDate.get(0)).getYear(); i <= localDate.get(1).getYear()
                ; localDate.set(0, localDate.get(0).withYear(i + 1)), i++) {
            yer.append(DateTimeFormatter.ofPattern("yyyy年、").format(localDate.get(0)).toString());
        }
        System.out.println(yer.substring(0, yer.lastIndexOf("、"))+ stringBuffer.toString());

    }
}

HanLP詞性標註集

—————————

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章