1. 程式人生 > >深度學習----NLP結巴分詞基礎

深度學習----NLP結巴分詞基礎

文章目錄



1 jieba中文分詞簡介

中文分詞是中文NLP的第一步,一個優秀的分詞系統取決於足夠的語料和完善的模型,很多機構和公司也都會開發和維護自己的分詞系統。

這裡推薦的是一款完全開源、簡單易用的分詞工具,jieba中文分詞。官網在這裡,https://github.com/fxsjy/jieba 裡面提供了詳細的說明文件。雖然jieba分詞的效能並不是最優秀的,但它開源免費、使用簡單、功能豐富,並且支援多種程式語言實現。

2 中文分詞的原理

中文分詞的模型實現主要分類兩大類:基於規則和基於統計。

2.1 基於規則

基於規則是指根據一個已有的詞典,採用前向最大匹配、後向最大匹配、雙向最大匹配等人工設定的規則來進行分詞。

例如對於“上海自來水來自海上”這句話,使用前向最大匹配,即從前向後掃描,使分出來的詞存在於詞典中並且儘可能長,則可以得到“上海/自來水/來自/海上”。這類方法思想簡單且易於實現,對資料量的要求也不高。

當然,分詞所使用的規則可以設計得更復雜,從而使分詞效果更理想。但是由於中文博大精深、語法千變萬化,很難設計足夠全面而通用的規則,並且具體的上下文語境、詞語之間的搭配組合也都會影響到最終的分詞結果,這些挑戰都使得基於規則的分詞模型愈發力不從心。

2.2 基於統計

基於統計是從大量人工標註語料中總結詞的概率分佈以及詞之間的常用搭配,使用有監督學習訓練分詞模型。

對於“上海自來水來自海上”這句話,一個最簡單的統計分詞想法是,嘗試所有可能的分詞方案,因為任何兩個字之間,要麼需要切分,要麼無需切分。

對於全部可能的分詞方案,根據語料統計每種方案出現的概率,然後保留概率最大的一種。很顯然,“上海/自來水/來自/海上”的出現概率比“上海自/來水/來自/海上”更高,因為“上海”和“自來水”在標註語料中出現的次數比“上海自”和“來水”更多。

2.3 jieba的原理

jieba分詞結合了基於規則和基於統計兩類方法。

首先基於字首詞典進行詞圖掃描,字首詞典是指詞典中的詞按照字首包含的順序排列,例如詞典中出現了“上”,之後以“上”開頭的詞都會出現在這一塊,例如“上海”,進而會出現“上海市”,從而形成一種層級包含結構。

如果將詞看作節點,詞和詞之間的分詞符看作邊,那麼一種分詞方案則對應著從第一個字到最後一個字的一條分詞路徑。因此,基於字首詞典可以快速構建包含全部可能分詞結果的有向無環圖,這個圖中包含多條分詞路徑,有向是指全部的路徑都始於第一個字、止於最後一個字,無環是指節點之間不構成閉環。

基於標註語料,使用動態規劃的方法可以找出最大概率路徑,並將其作為最終的分詞結果。

3 安裝結巴jieba

以下我們使用Python中的jieba分詞完成一些基礎NLP任務,如果對jieba分詞感興趣,希望瞭解更多內容,可以參考官方使用文件。首先沒有jieba分詞的話需要安裝,使用pip即可。

pip install jieba

4 jieba三種分詞模式以及其應用

jieba提供了三種分詞模式:

  • 精確模式:試圖將句子最精確地切開,適合文字分析;cut_all=True
  • 全模式:把句子中所有可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;cut_all=False
  • 搜尋引擎模式:在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜尋引擎分詞。jieba.cut_for_search()

       ~~~~~~ 以下程式碼使用jieba實現中文分詞,使用jieba.cut()函式並傳入待分詞的文字字串即可,使用cut_all引數控制選擇使用全模式還是精確模式,預設為精確模式。如果需要使用搜索引擎模式,使用jieba.cut_for_search()函式即可。執行以下程式碼之後,jieba首先會載入自帶的字首詞典,然後完成相應的分詞任務。

import jieba
seg_list = jieba.cut("我來到北京清華大學", cut_all=True)
# join是split的逆操作
# 即使用一個拼接符將一個列表拼成字串
print("/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我來到北京清華大學", cut_all=False)
print("/ ".join(seg_list))  # 精確模式

seg_list = jieba.cut("他來到了網易杭研大廈")  # 預設是精確模式
print("/ ".join(seg_list))

seg_list = jieba.cut_for_search("小明碩士畢業於中國科學院計算所,後在日本京都大學深造")  # 搜尋引擎模式
print("/ ".join(seg_list))

結果:

我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學
我/ 來到/ 北京/ 清華大學
他/ 來到/ 了/ 網易/ 杭研/ 大廈
小明/ 碩士/ 畢業/ 於/ 中國/ 科學/ 學院/ 科學院/ 中國科學院/ 計算/ 計算所/ ,/ 後/ 在/ 日本/ 京都/ 大學/ 日本京都大學/ 深造

5 jieba增強功能-載入自定義詞典

5.1 載入新自定義詞典

開發者可以指定自己自定義的詞典,以便包含 jieba 詞庫裡沒有的詞。雖然 jieba 有新詞識別能力,但是自行新增新詞可以保證更高的正確率 用法: jieba.load_userdict(file_name) # file_name 為檔案類物件或自定義詞典的路徑 詞典格式和 dict.txt 一樣,一個詞佔一行;每一行分三部分:詞語、詞頻(可省略)、詞性(可省略),用空格隔開,順序不可顛倒。file_name 若為路徑或二進位制方式開啟的檔案,則檔案必須為 UTF-8 編碼。 詞頻省略時使用自動計算的能保證分出該詞的詞頻。 例如

創新辦 3 i
雲端計算 5
凱特琳 nz
臺中

用法示例:https://github.com/fxsjy/jieba/blob/master/test/test_userdict.py

之前: 李小福 / 是 / 創新 / 辦 / 主任 / 也 / 是 / 雲 / 計算 / 方面 / 的 / 專家 /

載入自定義詞庫後: 李小福 / 是 / 創新辦 / 主任 / 也 / 是 / 雲端計算 / 方面 / 的 / 專家 /



5.2 載入停用詞表

主要思想是分詞過後,遍歷一下停用詞表,去掉停用詞。

import jieba  


# jieba.load_userdict('userdict.txt')  
# 建立停用詞list  
def stopwordslist(filepath):  
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]  
    return stopwords  


# 對句子進行分詞  
def seg_sentence(sentence):  
    sentence_seged = jieba.cut(sentence.strip())  
    stopwords = stopwordslist('./test/stopwords.txt')  # 這裡載入停用詞的路徑  
    outstr = ''  
    for word in sentence_seged:  
        if word not in stopwords:  
            if word != '\t':  
                outstr += word  
                outstr += " "  
    return outstr  


inputs = open('./test/input.txt', 'r', encoding='utf-8')  
outputs = open('./test/output.txt', 'w')  
for line in inputs:  
    line_seg = seg_sentence(line)  # 這裡的返回值是字串  
    outputs.write(line_seg + '\n')  
outputs.close()  
inputs.close() 

6 jieba分詞的其他應用

6.1 關鍵詞提取

jieba還實現了TF-IDF和TextRank這兩種關鍵詞提取演算法,直接呼叫即可。

當然,提取關鍵詞的前提是中文分詞,所以這裡也會使用到jieba自帶的字首詞典和IDF權重詞典。

import jieba.analyse # 字串前面加u表示使用unicode編碼 content = u'中國特色社會主義是我們黨領導的偉大事業,全面推進黨的建設新的偉大工程,是這一偉大事業取得勝利的關鍵所在。黨堅強有力,事業才能興旺發達,國家才能繁榮穩定,人民才能幸福安康。黨的十八大以來,我們黨堅持黨要管黨、從嚴治黨,凝心聚力、直擊積弊、扶正祛邪,黨的建設開創新局面,黨風政風呈現新氣象。習近平總書記圍繞從嚴管黨治黨提出一系列新的重要思想,為全面推進黨的建設新的偉大工程進一步指明瞭方向。' # 第一個引數:待提取關鍵詞的文字 # 第二個引數:返回關鍵詞的數量,重要性從高到低排序 # 第三個引數:是否同時返回每個關鍵詞的權重 # 第四個引數:詞性過濾,為空表示不過濾,若提供則僅返回符合詞性要求的關鍵詞 keywords = jieba.analyse.extract_tags(content, topK=20, withWeight=True, allowPOS=()) # 訪問提取結果 for item in keywords: # 分別為關鍵詞和相應的權重 print item[0], item[1] # 同樣是四個引數,但allowPOS預設為('ns', 'n', 'vn', 'v')

結果展示:

才能 1.0 管黨 0.7999933934163805 全面 0.7325692441985737 社會主義 0.6327916888315029 圍繞 0.60594603358887 總書記 0.5945625023471114 凝心 0.5840883789052874 政風 0.5792034335473362 新氣象 0.5772168490112909 黨風 0.5728262292165519 呈現 0.5700456186486299 推進 0.5548361394986431 方向 0.5150324602730256 指明 0.5113586590717408 治黨 0.5062232626208965 局面 0.4744549207999055 聚力 0.46596165707522896 積弊 0.4646149902996275 直擊 0.46314922535402286 國家 0.46179235227324805

6.2 詞性標註

jieba在進行中文分詞的同時,還可以完成詞性標註任務。根據分詞結果中每個詞的詞性,可以初步實現命名實體識別,即將標註為nr的詞視為人名,將標註為ns的詞視為地名等。所有標點符號都會被標註為x,所以可以根據這個去除分詞結果中的標點符號。
# 載入jieba.posseg並取個別名,方便呼叫
import jieba.posseg as pseg
words = pseg.cut("我愛北京天安門")
for word, flag in words:
    # 格式化模版並傳入引數
    print('%s, %s' % (word, flag))

結果展示:
>我, r
愛, v
北京, ns
天安門, ns

參考視訊:全棧資料工程師養成攻略-18-結巴分詞

7 用jieba分詞實戰(含檔案的讀取與儲存)

# -*- coding: utf-8 -*-
#本程式主要用於jieba分詞,以及去除停用詞

import os
import jieba


# 儲存檔案的函式
def savefile(savepath,content):
    fp = open(savepath,'w',encoding='utf8',errors='ignore')
    fp.write(content)
    fp.close()

# 讀取檔案的函式
def readfile(path):
    fp = open(path, "r", encoding='utf8', errors='ignore')
    content = fp.read()
    fp.close()
    return content

## 去除停用詞的2個函式
# 建立停用詞list
def stopwordslist(filepath):
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
    return stopwords

# 對句子去除停用詞
def movestopwords(sentence):
    stopwords = stopwordslist('語料/hlt_stop_words.txt')  # 這裡載入停用詞的路徑
    outstr = ''
    for word in sentence:
        if word not in stopwords:
            if word != '\t'and'\n':
                outstr += word
                # outstr += " "
    return outstr


corpus_path = "語料/train/"  # 未分詞分類預料庫路徑
seg_path = "語料/train_seg/"  # 分詞後分類語料庫路徑

catelist = os.listdir(corpus_path)  # 獲取未分詞目錄下所有子目錄
for mydir in catelist:
    class_path = corpus_path + mydir + "/"  # 拼出分類子目錄的路徑
    seg_dir = seg_path + mydir + "/"  # 拼出分詞後預料分類目錄
    if not os.path.exists(seg_dir):  # 是否存在,不存在則建立
        os.makedirs(seg_dir)

    file_list = os.listdir(class_path) # 列舉當前目錄所有檔案
    for file_path in file_list:
        fullname = class_path + file_path # 路徑+檔名
        print("當前處理的檔案是: ",fullname)  # 語料/train/pos/pos1.txt
                        #  語料/train/neg/neg1.txt

        content = readfile(fullname).strip()  # 讀取檔案內容
        content = content.replace("\n", "").strip()  # 刪除換行和多餘的空格
        content_seg = jieba.cut(content)    # jieba分詞
        print("jieba分詞後:",content_seg)
        listcontent = ''
        for i in content_seg:
            listcontent += i
            listcontent += " "
        print(listcontent[0:10])
        listcontent = movestopwords(listcontent)    # 去除停用詞
        print("去除停用詞後:", listcontent[0:10])
        listcontent = listcontent.replace("   ", " ").replace("  ", " ")
        savefile(seg_dir + file_path, "".join(listcontent)) # 儲存