【NLP实战】Task2:特征处理
一、基本文本處理技能
目前有三大主流分詞方法:基于字符串匹配的分詞方法、基于理解的分詞方法和基于統計的分詞方法。
實際應用中,常常將字符串匹配分詞和統計分詞結合使用,這樣既體現了匹配分詞速度快、效率高的優點,同時又能運用統計分詞識別生詞、自動消除歧義等方面的特點。
0、分詞
分詞算法設計的基本原則:
- 顆粒度越大越好。用于進行語義分析的文本分詞,單詞的字數越多,所能表示的含義越確切。eg: “公安局長”可以分為“公安 局長”、“公安局 長”、“公安局長”都算對,但是要用于語義分析,則“公安局長”的分詞結果最好.
- 切分結果中非詞典詞越少越好,單字字典詞數越少越好。“非詞典詞”就是不包含在詞典中的單字,而“單字字典詞”指的是可以獨立運用的單字。eg:“技術和服務”可切分為“技術 和服 務”,但“務”字無法獨立成詞,有1個非詞典詞;“技術 和 服務”其中“和”字可以單獨成詞(詞典中要包含)有0個非詞典詞,因此選用后者。
- 總體詞數越少越好,在相同字數的情況下,總詞數越少。
1、分詞匹配方法
最大匹配法:最大匹配是指以詞典為依據,取詞典中最長單詞為第一個次取字數量的掃描串,在詞典中進行掃描(為提升掃描效率,還可以跟據字數多少設計多個字典,然后根據字數分別從不同字典中進行掃描。下面以“我們在野生動物園玩”詳細說明一下這幾種匹配方法:
(1)正向最大匹配法
正向即從前往后取詞,從7->1,每次減一個字,直到詞典命中或剩下1個單字。
第1輪掃描:
第1次:“我們在野生動物”,掃描7字詞典,無
第2次:“我們在野生動”,掃描6字詞典,無
。。。。
第6次:“我們”,掃描2字詞典,有
掃描中止,輸出第1個詞為“我們”,去除第1個詞后開始第2輪掃描,即:
第2輪掃描:
第1次:“在野生動物園玩”,掃描7字詞典,無
第2次:“在野生動物園”,掃描6字詞典,無
。。。。
第6次:“在野”,掃描2字詞典,有
掃描中止,輸出第2個詞為“在野”,去除第2個詞后開始第3輪掃描,即:
第3輪掃描:
第1次:“生動物園玩”,掃描5字詞典,無
第2次:“生動物園”,掃描4字詞典,無
第3次:“生動物”,掃描3字詞典,無
第4次:“生動”,掃描2字詞典,有
掃描中止,輸出第3個詞為“生動”,第4輪掃描,即:
第4輪掃描:
第1次:“物園玩”,掃描3字詞典,無
第2次:“物園”,掃描2字詞典,無
第3次:“物”,掃描1字詞典,無
掃描中止,輸出第4個詞為“物”,非字典詞數加1,開始第5輪掃描,即:
第5輪掃描:
第1次:“園玩”,掃描2字詞典,無
第2次:“園”,掃描1字詞典,有
掃描中止,輸出第5個詞為“園”,單字字典詞數加1,開始第6輪掃描,即:
第6輪掃描:
第1次:“玩”,掃描1字字典詞,有
掃描中止,輸出第6個詞為“玩”,單字字典詞數加1,整體掃描結束。
結果:正向最大匹配法,最終切分結果為:“我們/在野/生動/物/園/玩”,其中,單字字典詞為2,非詞典詞為1。
(2)反向最大匹配法
逆向即從后往前取詞,其他邏輯和正向相同。即:
第1輪掃描:“在野生動物園玩”
第1次:“在野生動物園玩”,掃描7字詞典,無
第2次:“野生動物園玩”,掃描6字詞典,無
。。。。
第7次:“玩”,掃描1字詞典,有
掃描中止,輸出“玩”,單字字典詞加1,開始第2輪掃描
第2輪掃描:“們在野生動物園”
第1次:“們在野生動物園”,掃描7字詞典,無
第2次:“在野生動物園”,掃描6字詞典,無
第3次:“野生動物園”,掃描5字詞典,有
掃描中止,輸出“野生動物園”,開始第3輪掃描
第3輪掃描:“我們在”
第1次:“我們在”,掃描3字詞典,無
第2次:“們在”,掃描2字詞典,無
第3次:“在”,掃描1字詞典,有
掃描中止,輸出“在”,單字字典詞加1,開始第4輪掃描
第4輪掃描:“我們”
第1次:“我們”,掃描2字詞典,有
掃描中止,輸出“我們”,整體掃描結束。
結果:逆向最大匹配法,最終切分結果為:“我們/在/野生動物園/玩”,其中,單字字典詞為2,非詞典詞為0。
(3)雙向最大匹配法
正向最大匹配法和逆向最大匹配法,都有其局限性 ==》雙向最大匹配法,雙向最大匹配法。
即,兩種算法都切一遍,然后根據大顆粒度詞越多越好,非詞典詞和單字詞越少越好的原則,選取其中一種分詞結果輸出。
如:“我們在野生動物園玩”
正向最大匹配法,最終切分結果為:“我們/在野/生動/物/園/玩”,其中,兩字詞3個,單字字典詞為2,非詞典詞為1。
逆向最大匹配法,最終切分結果為:“我們/在/野生動物園/玩”,其中,五字詞1個,兩字詞1個,單字字典詞為2,非詞典詞為0。
非字典詞:正向(1)>逆向(0)(越少越好)
單字字典詞:正向(2)=逆向(2)(越少越好)
總詞數:正向(6)>逆向(4)(越少越好)
因此最終輸出為逆向結果。
2、詞、字符頻率統計
python中 collections.Counter 模塊
新建兩個txt文件,其內容分別為:
word1.txt
hello python goodbye pythonword2.txt
i like python import os from collections import Countersumsdata = []for fname in os.listdir(os.getcwd()):if os.path.isfile(fname) and fname.endswith('.txt'):with open(fname, "r") as fp:data = fp.readlines()fp.close()sumsdata += [line.strip().lower() for line in data]cnt = Counter() for word in sumsdata:cnt[word] += 1cnt=dict(cnt) for key, value in cnt.items():print(key + ":" + str(value))輸出結果
i:1 like:1 python:3 hello:1 goodbye:1二、語言模型
統計語言模型是一個單詞序列上的概率分布,對于一個給定長度為m的序列,它可以為整個序列產生一個概率 P(w_1,w_2,…,w_m) 。其實就是想辦法找到一個概率分布,它可以表示任意一個句子或序列出現的概率。
目前在自然語言處理相關應用非常廣泛,如語音識別(speech recognition) , 機器翻譯(machine translation), 詞性標注(part-of-speech tagging), 句法分析(parsing)等。傳統方法主要是基于統計學模型,最近幾年基于神經網絡的語言模型也越來越成熟。
常見的方法有n-gram模型方法、決策樹方法、最大熵模型方法、最大熵馬爾科夫模型方法、條件隨機域方法、神經網絡方法,等等。
1、n-gram模型:unigram、bigram、trigram
為了解決自由參數數目過多的問題,引入了馬爾科夫假設:隨意一個詞出現的概率只與它前面出現的有限的n個詞有關。基于上述假設的統計語言模型被稱為N-gram語言模型。
當n取1、2、3時,n-gram模型分別稱為unigram、bigram、trigram語言模型
- unigram 一元分詞,把句子分成一個一個的漢字
- bigram 二元分詞,把句子從頭到尾每兩個字組成一個詞語,也叫一階馬爾科夫鏈
- trigram 三元分詞,把句子從頭到尾每三個字組成一個詞語,也叫二階馬爾科夫鏈
2、文本矩陣化
過程:
加載數據集->分詞->生成詞匯表->生成word_index->加載預訓練詞向量模型->生成詞向量矩陣
(1)分詞——jieba
jieba:https://github.com/fxsjy/jieba
三種分詞模式:
- 精確模式,試圖將句子最精確地切開,適合文本分析;
- 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;
- 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用于搜索引擎分詞。
輸出結果
[全模式]: 我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學 [精確模式]: 我/ 來到/ 北京/ 清華大學 [默認模式]: 我/ 來到/ 北京/ 清華大學 [搜索引擎模式]: 我/ 來到/ 北京/ 清華/ 華大/ 大學/ 清華大學(2)新詞識別
新詞識別: “杭研”并沒有在詞典中,但是也被 Viterbi 算法識別出來了
import jiebaseg_list = jieba.cut("他來到了網易杭研大廈") print(u"[新詞識別]: ", "/ ".join(seg_list))(3)自定義詞典
自定義詞典,以便包含 jieba 詞庫中沒有的詞語(尤其是專有名稱),以提升正確率。
import jiebatext = "故宮的著名景點包括乾清宮、太和殿和黃琉璃瓦等"# 全模式 seg_list = jieba.cut(text, cut_all=True) print(u"[全模式: ]", "/ ".join(seg_list)) # 精確模式 seg_list = jieba.cut(text, cut_all=False) print(u"[精確模式: ]", "/".join(seg_list)) # 搜索引擎模式 seg_list = jieba.cut_for_search(text) print(u"[搜索引擎模式: ]", "/".join(seg_list))輸出結果
[全模式: ] 故宮/ 的/ 著名/ 著名景點/ 景點/ 包括/ 乾/ 清宮/ / / 太和/ 太和殿/ 和/ 黃/ 琉璃/ 琉璃瓦/ 等 [精確模式: ] 故宮/的/著名景點/包括/乾/清宮/、/太和殿/和/黃/琉璃瓦/等 [搜索引擎模式: ] 故宮/的/著名/景點/著名景點/包括/乾/清宮/、/太和/太和殿/和/黃/琉璃/琉璃瓦/等缺陷:jieba認出了專有名詞”太和殿”,但沒有認出”乾清宮”和”黃琉璃瓦”。
設置自定義字典:
每一行分三部分,第一部分為詞語,中間部分為詞頻,最后部分為詞性(可省略,ns為地點名詞),用空格隔開。如下所示。
改進
import jieba# 設置并加載自定義字典 filename = './mydict.txt' jieba.load_userdict(filename)text = "故宮的著名景點包括乾清宮、太和殿和黃琉璃瓦等"# 全模式 seg_list = jieba.cut(text, cut_all=True) print(u"[全模式: ]", "/ ".join(seg_list))# 精確模式 seg_list = jieba.cut(text, cut_all=False) print(u"[精確模式: ]", "/".join(seg_list))# 搜索引擎模式 seg_list = jieba.cut_for_search(text) print(u"[搜索引擎模式: ]", "/".join(seg_list))輸出結果:新添加的兩個專有名詞已經被結巴分詞工具辨別出來了。
[全模式: ] 故宮/ 的/ 著名/ 著名景點/ 景點/ 包括/ 乾清宮/ 清宮/ / / 太和/ 太和殿/ 和/ 黃琉璃瓦/ 琉璃/ 琉璃瓦/ 等 [精確模式: ] 故宮/的/著名景點/包括/乾清宮/、/太和殿/和/黃琉璃瓦/等 [搜索引擎模式: ] 故宮/的/著名/景點/著名景點/包括/清宮/乾清宮/、/太和/太和殿/和/琉璃/琉璃瓦/黃琉璃瓦/等(4)關鍵詞
jieba.analyse.extract_tags(text, topK=)
import jieba import jieba.analysefilename = './mydict.txt' jieba.load_userdict(filename)text = "故宮的著名景點包括乾清宮、太和殿和午門等。其中乾清宮非常精美,午門是紫禁城的正門,午門居中向陽。"seg_list = jieba.cut(text, cut_all=False) print (u"分詞結果:") print ("/ ".join(seg_list))# 獲取關鍵詞 keywords = jieba.analyse.extract_tags(text, topK=5) print(u"關鍵詞: ") print(" ".join(keywords))輸出結果
分詞結果: 故宮/ 的/ 著名景點/ 包括/ 乾清宮/ 、/ 太和殿/ 和/ 午門/ 等/ 。/ 其中/ 乾清宮/ 非常/ 精美/ ,/ 午門/ 是/ 紫禁城/ 的/ 正門/ ,/ 午門/ 居中/ 向陽/ 。 關鍵詞: 午門 乾清宮 著名景點 太和殿 向陽分析:輸出結果按出現詞頻降序,在詞頻一樣是,根據TF/IDF的升序輸出。
(5)去除停用詞
為節省存儲空間和提高搜索效率,常會自動過濾某些字或詞,eg:“的”、“是”、“而且”、“但是”、”非常“等。這些字或詞被稱為 stop words (停用詞)
在分詞之前去除停用詞,再進行分詞。
import jieba import jieba.analysefilename = './mydict.txt' jieba.load_userdict(filename)text = "故宮的著名景點包括乾清宮、太和殿和午門等。其中乾清宮非常精美,午門是紫禁城的正門,午門居中向陽。" stopwords = {}.fromkeys(['的', '包括', '等', '是'])seg_list = jieba.cut(text, cut_all=False) final = '' for seg in seg_list:if seg not in stopwords:final += seg print(final, '\n')seg_list1 = jieba.cut(final, cut_all=False) print("/ ".join(seg_list1))輸出結果
故宮著名景點乾清宮、太和殿和午門。其中乾清宮非常精美,午門紫禁城正門,午門居中向陽。故宮/ 著名景點/ 乾清宮/ 、/ 太和殿/ 和/ 午門/ 。/ 其中/ 乾清宮/ 非常/ 精美/ ,/ 午門/ 紫禁城/ 正門/ ,/ 午門/ 居中/ 向陽/ 。(6)構建詞表
def build_vocab(train_dir, vocab_dir, vocab_size=5000):"""根據訓練集構建詞匯表,存儲"""data_train, _ = read_file(train_dir)all_data = []for content in data_train:all_data.extend(content)counter = Counter(all_data)count_pairs = counter.most_common(vocab_size - 1)words, _ = list(zip(*count_pairs))# 添加一個 <PAD> 來將所有文本pad為同一長度words = ['<PAD>'] + list(words)open_file(vocab_dir, mode='w').write('\n'.join(words) + '\n')(7)文檔向量化
import jieba import pandas as pd from sklearn.feature_extraction.text import CountVectorizer# 讀取停用詞 def read_stopword(filename):stopword = []fp = open(filename, 'r')for line in fp.readlines():stopword.append(line.replace('\n', ''))fp.close()return stopword# 切分數據,并刪除停用詞 def cut_data(data, stopword):words = []for content in data['content']:word = list(jieba.cut(content))for w in list(set(word) & set(stopword)):while w in word:word.remove(w)words.append(' '.join(word))data['content'] = wordsreturn data# 獲取單詞列表 def word_list(data):all_word = []for word in data['content']:all_word.extend(word)all_word = list(set(all_word))return all_word# 計算文本向量 def text_vec(data):count_vec = CountVectorizer(max_features=300, min_df=2)count_vec.fit_transform(data['content'])fea_vec = count_vec.transform(data['content']).toarray()return fea_vecif __name__ == '__main__':data = pd.read_csv('./cnews/cnews.test.txt', names=['title', 'content'], sep='\t') # (10000, 2)data = data.head(50)stopword = read_stopword('stopword.txt')data = cut_data(data, stopword)fea_vec = text_vec(data)print(fea_vec)總結
以上是生活随笔為你收集整理的【NLP实战】Task2:特征处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LeetCode】462. 最少移动次
- 下一篇: 【LeetCode】617. 合并二叉树