|
一,中文文本分類流程: 1,預處理 2,中文分詞 3,結構化表示--構建詞向量空間 4,權重策略--TF-IDF 5,分類器 6,評價 二,具體細節(jié)1,預處理。希望得到這樣的目標:
2,中文分詞。
實例代碼:jieba.cut 方法接受三個輸入?yún)?shù): 需要分詞的字符串;cut_all 參數(shù)用來控制是否采用全模式;HMM 參數(shù)用來控制是否使用 HMM 模型jieba.cut_for_search 方法接受兩個參數(shù):需要分詞的字符串;是否使用 HMM 模型。該方法適合用于搜索引擎構建倒排索引的分詞,粒度比較細待分詞的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建議直接輸入 GBK 字符串,可能無法預料地錯誤解碼成 UTF-8jieba.cut 以及 jieba.cut_for_search 返回的結構都是一個可迭代的 generator,可以使用 for 循環(huán)來獲得分詞后得到的每一個詞語(unicode),或者用jieba.lcut 以及 jieba.lcut_for_search 直接返回 listjieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定義分詞器,可用于同時使用不同詞典。jieba.dt 為默認分詞器,所有全局分詞相關函數(shù)都是該分詞器的映射。 import jieba
seg_list = jieba.cut("我來到北京清華大學", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
seg_list = jieba.cut("我來到北京清華大學", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精確模式
seg_list = jieba.cut("他來到了網(wǎng)易杭研大廈") # 默認是精確模式
print(", ".join(seg_list))
seg_list = jieba.cut_for_search("小明碩士畢業(yè)于中國科學院計算所,后在日本京都大學深造") # 搜索引擎模式
print(", ".join(seg_list))輸出: 【全模式】: 我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學 【精確模式】: 我/ 來到/ 北京/ 清華大學 【新詞識別】:他, 來到, 了, 網(wǎng)易, 杭研, 大廈 (此處,“杭研”并沒有在詞典中,但是也被Viterbi算法識別出來了) 【搜索引擎模式】: 小明, 碩士, 畢業(yè), 于, 中國, 科學, 學院, 科學院, 中國科學院, 計算, 計算所, 后, 在, 日本, 京都, 大學, 日本京都大 2.2.2 #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: corpus_segment.py
@time: 2017/2/5 15:28
@software: PyCharm
"""
import sys
import os
import jieba
# 配置utf-8輸出環(huán)境
reload(sys)
sys.setdefaultencoding('utf-8')
# 保存至文件
def savefile(savepath, content):
with open(savepath, "wb") as fp:
fp.write(content)
'''
上面兩行是python2.6以上版本增加的語法,省略了繁瑣的文件close和try操作
2.5版本需要from __future__ import with_statement
新手可以參考這個鏈接來學習http:///archives/325
'''
# 讀取文件
def readfile(path):
with open(path, "rb") as fp:
content = fp.read()
return content
def corpus_segment(corpus_path, seg_path):
'''
corpus_path是未分詞語料庫路徑
seg_path是分詞后語料庫存儲路徑
'''
catelist = os.listdir(corpus_path) # 獲取corpus_path下的所有子目錄
'''
其中子目錄的名字就是類別名,例如:
train_corpus/art/21.txt中,'train_corpus/'是corpus_path,'art'是catelist中的一個成員
'''
# 獲取每個目錄(類別)下所有的文件
for mydir in catelist:
'''
這里mydir就是train_corpus/art/21.txt中的art(即catelist中的一個類別)
'''
class_path = corpus_path + mydir + "/" # 拼出分類子目錄的路徑如:train_corpus/art/
seg_dir = seg_path + mydir + "/" # 拼出分詞后存貯的對應目錄路徑如:train_corpus_seg/art/
if not os.path.exists(seg_dir): # 是否存在分詞目錄,如果沒有則創(chuàng)建該目錄
os.makedirs(seg_dir)
file_list = os.listdir(class_path) # 獲取未分詞語料庫中某一類別中的所有文本
'''
train_corpus/art/中的
21.txt,
22.txt,
23.txt
...
file_list=['21.txt','22.txt',...]
'''
for file_path in file_list: # 遍歷類別目錄下的所有文件
fullname = class_path + file_path # 拼出文件名全路徑如:train_corpus/art/21.txt
content = readfile(fullname) # 讀取文件內容
'''此時,content里面存貯的是原文本的所有字符,例如多余的空格、空行、回車等等,
接下來,我們需要把這些無關痛癢的字符統(tǒng)統(tǒng)去掉,變成只有標點符號做間隔的緊湊的文本內容
'''
content = content.replace("\r\n", "") # 刪除換行
content = content.replace(" ", "")#刪除空行、多余的空格
content_seg = jieba.cut(content) # 為文件內容分詞
savefile(seg_dir + file_path, " ".join(content_seg)) # 將處理后的文件保存到分詞后語料目錄
print "中文語料分詞結束!??!"
'''
如果你對if __name__=="__main__":這句不懂,可以參考下面的文章
http://imoyao.lofter.com/post/3492bc_bd0c4ce
簡單來說如果其他python文件調用這個文件的函數(shù),或者把這個文件作為模塊
導入到你的工程中時,那么下面的代碼將不會被執(zhí)行,而如果單獨在命令行中
運行這個文件,或者在IDE(如pycharm)中運行這個文件時候,下面的代碼才會運行。
即,這部分代碼相當于一個功能測試。
如果你還沒懂,建議你放棄IT這個行業(yè)。
'''
if __name__=="__main__":
#對訓練集進行分詞
corpus_path = "./train_corpus/" # 未分詞分類語料庫路徑
seg_path = "./train_corpus_seg/" # 分詞后分類語料庫路徑
corpus_segment(corpus_path,seg_path)
#對測試集進行分詞
corpus_path = "./test_corpus/" # 未分詞分類語料庫路徑
seg_path = "./test_corpus_seg/" # 分詞后分類語料庫路徑
corpus_segment(corpus_path,seg_path)截止目前,我們已經(jīng)得到了分詞后的訓練集語料庫和測試集語料庫,下面我們要把這兩個數(shù)據(jù)集表示為變量,從而為下面程序調用提供服務。我們采用的是Scikit-Learn庫中的Bunch數(shù)據(jù)結構來表示這兩個數(shù)據(jù)集。你或許對于Scikit-Learn和Bunch并不是特別了解,而官方的技術文檔有兩千多頁你可能也沒耐心去看,好在你們有相國大人。下面我們 以這兩個數(shù)據(jù)集為背景,對Bunch做一個非常通俗的講解,肯定會讓你一下子就明白。 首先來看看Bunch: Bunch這玩意兒,其實就相當于python中的字典。你往里面?zhèn)魇裁矗痛媸裁础?/p> 好了,解釋完了。 是不是很簡單? 在本篇博文中,你對Bunch能夠有這種層次的理解,就足夠了。如果你想繼續(xù)詳細透徹的理解Bunch,請見博主的另一篇博文《暫時還沒寫,寫完在這里更新鏈接》 接下來,讓我們看看的我們的數(shù)據(jù)集(訓練集)有哪些信息:
那么,用Bunch表示,就是: from sklearn.datasets.base import Bunch 我們在Bunch對象里面創(chuàng)建了有4個成員: 如果你還沒有明白,看一下下面這個圖,你總該明白了: Bunch: 下面,我們將文本文件轉為Bunch類形: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: corpus2Bunch.py
@time: 2017/2/7 7:41
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import os#python內置的包,用于進行文件目錄操作,我們將會用到os.listdir函數(shù)
import cPickle as pickle#導入cPickle包并且取一個別名pickle
'''
事實上python中還有一個也叫作pickle的包,與這里的名字相同了,無所謂
關于cPickle與pickle,請參考博主另一篇博文:
python核心模塊之pickle和cPickle講解
http://blog.csdn.net/github_36326955/article/details/54882506
本文件代碼下面會用到cPickle中的函數(shù)cPickle.dump
'''
from sklearn.datasets.base import Bunch
#這個您無需做過多了解,您只需要記住以后導入Bunch數(shù)據(jù)結構就像這樣就可以了。
#今后的博文會對sklearn做更有針對性的講解
def _readfile(path):
'''讀取文件'''
#函數(shù)名前面帶一個_,是標識私有函數(shù)
# 僅僅用于標明而已,不起什么作用,
# 外面想調用還是可以調用,
# 只是增強了程序的可讀性
with open(path, "rb") as fp:#with as句法前面的代碼已經(jīng)多次介紹過,今后不再注釋
content = fp.read()
return content
def corpus2Bunch(wordbag_path,seg_path):
catelist = os.listdir(seg_path)# 獲取seg_path下的所有子目錄,也就是分類信息
#創(chuàng)建一個Bunch實例
bunch = Bunch(target_name=[], label=[], filenames=[], contents=[])
bunch.target_name.extend(catelist)
'''
extend(addlist)是python list中的函數(shù),意思是用新的list(addlist)去擴充
原來的list
'''
# 獲取每個目錄下所有的文件
for mydir in catelist:
class_path = seg_path + mydir + "/" # 拼出分類子目錄的路徑
file_list = os.listdir(class_path) # 獲取class_path下的所有文件
for file_path in file_list: # 遍歷類別目錄下文件
fullname = class_path + file_path # 拼出文件名全路徑
bunch.label.append(mydir)
bunch.filenames.append(fullname)
bunch.contents.append(_readfile(fullname)) # 讀取文件內容
'''append(element)是python list中的函數(shù),意思是向原來的list中添加element,注意與extend()函數(shù)的區(qū)別'''
# 將bunch存儲到wordbag_path路徑中
with open(wordbag_path, "wb") as file_obj:
pickle.dump(bunch, file_obj)
print "構建文本對象結束?。。?
if __name__ == "__main__":#這個語句前面的代碼已經(jīng)介紹過,今后不再注釋
#對訓練集進行Bunch化操作:
wordbag_path = "train_word_bag/train_set.dat" # Bunch存儲路徑
seg_path = "train_corpus_seg/" # 分詞后分類語料庫路徑
corpus2Bunch(wordbag_path, seg_path)
# 對測試集進行Bunch化操作:
wordbag_path = "test_word_bag/test_set.dat" # Bunch存儲路徑
seg_path = "test_corpus_seg/" # 分詞后分類語料庫路徑
corpus2Bunch(wordbag_path, seg_path)3,結構化表示--向量空間模型在第2節(jié)中,我們對原始數(shù)據(jù)集進行了分詞處理,并且通過綁定為Bunch數(shù)據(jù)類型,實現(xiàn)了數(shù)據(jù)集的變量表示。事實上在第2節(jié)中,我們通過分詞,已經(jīng)將每一個文本文件表示為了一個詞向量了。也許你對于什么是詞向量并沒有清晰的概念,這里有一篇非常棒的文章《Deep Learning in NLP (一)詞向量和語言模型》,簡單來講,詞向量就是詞向量空間里面的一個向量。 你可以類比為三維空間里面的一個向量,例如: 如果我們規(guī)定詞向量空間為:(我,喜歡,相國大人),這相當于三維空間里面的(x,y,z)只不過這里的x,y,z的名字變成了“我”,“喜歡”,“相國大人” 現(xiàn)在有一個詞向量是:我喜歡 喜歡相國大人 表示在詞向量空間中就變?yōu)椋海?,2,1),歸一化后可以表示為:(0.166666666667 0.333333333333 0.166666666667)表示在剛才的詞向量空間中就是這樣: 但是在我們第2節(jié)處理的這些文件中,詞向量之間的單詞個數(shù)并不相同,詞向量的涵蓋的單詞也不盡相同。他們并不在一個空間里,換句話說,就是他們之間沒有可比性,例如: 詞向量1:我喜歡相國大人,對應的詞向量空間是(我,喜歡,相國大人),可以表示為(1,1,1) 詞向量2:她不喜歡我,對應的詞向量空間是(她,不,喜歡,我),可以表示為(1,1,1,1) 兩個空間不一樣 因此,接下來我們要做的,就是把所有這些詞向量統(tǒng)一到同一個詞向量空間中,例如,在上面的例子中,我們可以設置詞向量空間為(我,喜歡,相國大人,她,不) 這樣,詞向量1和詞向量2分別可以表示為(1,1,1,0,0)和(1,1,0,1,1),這樣兩個向量就都在同一個空間里面了??梢赃M行比較和各種運算了。 也許你已經(jīng)發(fā)現(xiàn)了,這樣做的一個很糟糕的結果是,我們要把訓練集內所有出現(xiàn)過的單詞,都作為一個維度,構建統(tǒng)一的詞向量空間,即使是中等大小的文本集合,向量維度也很輕易就達到數(shù)十萬維。為了節(jié)省空間,我們首先將訓練集中每個文本中一些垃圾詞匯去掉。所謂的垃圾詞匯,就是指意義模糊的詞,或者一些語氣助詞,標點符號等等,通常他們對文本起不了分類特征的意義。這些垃圾詞匯我們稱之為停用詞。把所有停用詞集合起來構成一張停用詞表格,這樣,以后我們處理文本時,就可以從這個根據(jù)表格,過濾掉文本中的一些垃圾詞匯了。 你可以從這里下載停用詞表:hlt_stop_words.txt 存放在這里路徑中:train_word_bag/hlt_stop_words.txt 下面的程序,目的就是要將訓練集所有文本文件(詞向量)統(tǒng)一到同一個詞向量空間中。值得一提的是,在詞向量空間中,事實上不同的詞,它的權重是不同的,它對文本分類的影響力也不同,為此我們希望得到的詞向量空間不是等權重的空間,而是不同權重的詞向量空間。我們把帶有不同權重的詞向量空間叫做“加權詞向量空間”,也有的技術文檔將其稱為“加權向量詞袋”,一個意思。 現(xiàn)在的問題是,如何計算不同詞的權重呢? 4,權重策略--TF-IDF什么是TF-IDF?今后有精力我會在這里更新補充,現(xiàn)在,先給你推薦一篇非常棒的文章《使用scikit-learn工具計算文本TF-IDF值》 下面,我們假定你已經(jīng)對TF-IDF有了最基本的了解。請你動動你的小腦袋瓜想一想,我們把訓練集文本轉換成了一個TF-IDF詞向量空間,姑且叫它為A空間吧。那么我們還有測試集數(shù)據(jù),我們以后實際運用時,還會有新的數(shù)據(jù),這些數(shù)據(jù)顯然也要轉到詞向量空間,那么應該和A空間為同一個空間嗎? 是的。 即使測試集出現(xiàn)了新的詞匯(不是停用詞),即使新的文本數(shù)據(jù)有新的詞匯,只要它不是訓練集生成的TF-IDF詞向量空間中的詞,我們就都不予考慮。這就實現(xiàn)了所有文本詞向量空間“大一統(tǒng)”,也只有這樣,大家才在同一個世界里。才能進行下一步的研究。 下面的程序就是要將訓練集所有文本文件(詞向量)統(tǒng)一到同一個TF-IDF詞向量空間中(或者叫做用TF-IDF算法計算權重的有權詞向量空間)。這個詞向量空間最終存放在train_word_bag/tfdifspace.dat中。 這段代碼你可能有點看不懂,因為我估計你可能比較懶,還沒看過TF-IDF(盡管我剛才已經(jīng)給你推薦那篇文章了)。你只需要明白,它把一大坨訓練集數(shù)據(jù)成功的構建了一個TF-IDF詞向量空間,空間的各個詞都是出自這個訓練集(去掉了停用詞)中,各個詞的權值也都一并保存了下來,叫做權重矩陣。 需要注意的是,你要明白,權重矩陣是一個二維矩陣,a[i][j]表示,第i個詞在第j個類別中的IF-IDF值(看到這里,我估計你壓根就沒去看那篇文章,所以你可能到現(xiàn)在也不知道 這是個啥玩意兒。。。) 請記住權重矩陣這個詞,代碼解釋中我會用到。 #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: vector_space.py
@time: 2017/2/7 17:29
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 引入Bunch類
from sklearn.datasets.base import Bunch
import cPickle as pickle#之前已經(jīng)說過,不再贅述
from sklearn.feature_extraction.text import TfidfVectorizer#這個東西下面會講
# 讀取文件
def _readfile(path):
with open(path, "rb") as fp:
content = fp.read()
return content
# 讀取bunch對象
def _readbunchobj(path):
with open(path, "rb") as file_obj:
bunch = pickle.load(file_obj)
return bunch
# 寫入bunch對象
def _writebunchobj(path, bunchobj):
with open(path, "wb") as file_obj:
pickle.dump(bunchobj, file_obj)
#這個函數(shù)用于創(chuàng)建TF-IDF詞向量空間
def vector_space(stopword_path,bunch_path,space_path):
stpwrdlst = _readfile(stopword_path).splitlines()#讀取停用詞
bunch = _readbunchobj(bunch_path)#導入分詞后的詞向量bunch對象
#構建tf-idf詞向量空間對象
tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
'''
在前面幾節(jié)中,我們已經(jīng)介紹了Bunch。
target_name,label和filenames這幾個成員都是我們自己定義的玩意兒,前面已經(jīng)講過不再贅述。
下面我們講一下tdm和vocabulary(這倆玩意兒也都是我們自己創(chuàng)建的):
tdm存放的是計算后得到的TF-IDF權重矩陣。請記住,我們后面分類器需要的東西,其實就是訓練集的tdm和標簽label,因此這個成員是
很重要的。
vocabulary是詞向量空間的索引,例如,如果我們定義的詞向量空間是(我,喜歡,相國大人),那么vocabulary就是這樣一個索引字典
vocabulary={"我":0,"喜歡":1,"相國大人":2},你可以簡單的理解為:vocabulary就是詞向量空間的坐標軸,索引值相當于表明了第幾
個維度。
我們現(xiàn)在就是要構建一個詞向量空間,因此在初始時刻,這個tdm和vocabulary自然都是空的。如果你在這一步將vocabulary賦值了一個
自定義的內容,那么,你是傻逼。
'''
'''
與下面這2行代碼等價的代碼是:
vectorizer=CountVectorizer()#構建一個計算詞頻(TF)的玩意兒,當然這里面不只是可以做這些
transformer=TfidfTransformer()#構建一個計算TF-IDF的玩意兒
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))
#vectorizer.fit_transform(corpus)將文本corpus輸入,得到詞頻矩陣
#將這個矩陣作為輸入,用transformer.fit_transform(詞頻矩陣)得到TF-IDF權重矩陣
看名字你也應該知道:
Tfidf-Transformer + Count-Vectorizer = Tfidf-Vectorizer
下面的代碼一步到位,把上面的兩個步驟一次性全部完成
值得注意的是,CountVectorizer()和TfidfVectorizer()里面都有一個成員叫做vocabulary_(后面帶一個下劃線)
這個成員的意義,與我們之前在構建Bunch對象時提到的自己定義的那個vocabulary的意思是一樣的,相當于詞向量
空間的坐標軸。顯然,我們在第45行中創(chuàng)建tfidfspace中定義的vocabulary就應該被賦值為這個vocabulary_
他倆還有一個叫做vocabulary(后面沒有下劃線)的參數(shù),這個參數(shù)和我們第45中講到的意思是一樣的。
那么vocabulary_和vocabulary的區(qū)別是什么呢?
vocabulary_:是CountVectorizer()和TfidfVectorizer()的內部成員,表示最終得到的詞向量空間坐標
vocabulary:是創(chuàng)建CountVectorizer和TfidfVectorizer類對象時,傳入的參數(shù),它是我們外部輸入的空間坐標,不寫的話,函數(shù)就從
輸入文檔中自己構造。
一般情況它倆是相同的,不一般的情況沒遇到過。
'''
#構建一個快樂地一步到位的玩意兒,專業(yè)一點兒叫做:使用TfidfVectorizer初始化向量空間模型
#這里面有TF-IDF權重矩陣還有我們要的詞向量空間坐標軸信息vocabulary_
vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5)
'''
關于參數(shù),你只需要了解這么幾個就可以了:
stop_words:
傳入停用詞,以后我們獲得vocabulary_的時候,就會根據(jù)文本信息去掉停用詞得到
vocabulary:
之前說過,不再解釋。
sublinear_tf:
計算tf值采用亞線性策略。比如,我們以前算tf是詞頻,現(xiàn)在用1+log(tf)來充當詞頻。
smooth_idf:
計算idf的時候log(分子/分母)分母有可能是0,smooth_idf會采用log(分子/(1+分母))的方式解決。默認已經(jīng)開啟,無需關心。
norm:
歸一化,我們計算TF-IDF的時候,是用TF*IDF,TF可以是歸一化的,也可以是沒有歸一化的,一般都是采用歸一化的方法,默認開啟.
max_df:
有些詞,他們的文檔頻率太高了(一個詞如果每篇文檔都出現(xiàn),那還有必要用它來區(qū)分文本類別嗎?當然不用了呀),所以,我們可以
設定一個閾值,比如float類型0.5(取值范圍[0.0,1.0]),表示這個詞如果在整個數(shù)據(jù)集中超過50%的文本都出現(xiàn)了,那么我們也把它列
為臨時停用詞。當然你也可以設定為int型,例如max_df=10,表示這個詞如果在整個數(shù)據(jù)集中超過10的文本都出現(xiàn)了,那么我們也把它列
為臨時停用詞。
min_df:
與max_df相反,雖然文檔頻率越低,似乎越能區(qū)分文本,可是如果太低,例如10000篇文本中只有1篇文本出現(xiàn)過這個詞,僅僅因為這1篇
文本,就增加了詞向量空間的維度,太不劃算。
當然,max_df和min_df在給定vocabulary參數(shù)時,就失效了。
'''
#此時tdm里面存儲的就是if-idf權值矩陣
tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
tfidfspace.vocabulary = vectorizer.vocabulary_
_writebunchobj(space_path, tfidfspace)
print "if-idf詞向量空間實例創(chuàng)建成功?。?!"
if __name__ == '__main__':
stopword_path = "train_word_bag/hlt_stop_words.txt"#停用詞表的路徑
bunch_path = "train_word_bag/train_set.dat" #導入訓練集Bunch的路徑
space_path = "train_word_bag/tfdifspace.dat" # 詞向量空間保存路徑
vector_space(stopword_path,bunch_path,space_path)上面的代碼運行之后,會將訓練集數(shù)據(jù)轉換為TF-IDF詞向量空間中的實例,保存在train_word_bag/tfdifspace.dat中,具體來說,這個文件里面有兩個我們感興趣的東西,一個是vocabulary,即詞向量空間坐標,一個是tdm,即訓練集的TF-IDF權重矩陣。 接下來,我們要開始第5步的操作,設計分類器,用訓練集訓練,用測試集測試。在做這些工作之前,你一定要記住,首先要把測試數(shù)據(jù)也映射到上面這個TF-IDF詞向量空間中,也就是說,測試集和訓練集處在同一個詞向量空間(vocabulary相同),只不過測試集有自己的tdm,與訓練集(train_word_bag/tfdifspace.dat)中的tdm不同而已。 同一個世界,同一個夢想。 至于說怎么弄,請看下節(jié)。 5,分類器這里我們采用的是樸素貝葉斯分類器,今后我們會詳細講解它。 現(xiàn)在,你即便不知道這是個啥玩意兒,也一點不會影響你,這個分類器我們有封裝好了的函數(shù),MultinomialNB,這玩意兒獲取訓練集的權重矩陣和標簽,進行訓練,然后獲取測試集的權重矩陣,進行預測(給出預測標簽)。 下面我們開始動手實踐吧! 首先,我們要把測試數(shù)據(jù)也映射到第4節(jié)中的那個TF-IDF詞向量空間上: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: test.py
@time: 2017/2/8 11:39
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 引入Bunch類
from sklearn.datasets.base import Bunch
import cPickle as pickle
from sklearn.feature_extraction.text import TfidfVectorizer
def _readfile(path):
with open(path, "rb") as fp:
content = fp.read()
return content
def _readbunchobj(path):
with open(path, "rb") as file_obj:
bunch = pickle.load(file_obj)
return bunch
def _writebunchobj(path, bunchobj):
with open(path, "wb") as file_obj:
pickle.dump(bunchobj, file_obj)
def vector_space(stopword_path,bunch_path,space_path,train_tfidf_path):
stpwrdlst = _readfile(stopword_path).splitlines()
bunch = _readbunchobj(bunch_path)
tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
#導入訓練集的TF-IDF詞向量空間
trainbunch = _readbunchobj(train_tfidf_path)
tfidfspace.vocabulary = trainbunch.vocabulary
vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5,vocabulary=trainbunch.vocabulary)
tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
_writebunchobj(space_path, tfidfspace)
print "if-idf詞向量空間實例創(chuàng)建成功?。?!"
if __name__ == '__main__':
stopword_path = "train_word_bag/hlt_stop_words.txt"#停用詞表的路徑
bunch_path = "test_word_bag/test_set.dat" # 詞向量空間保存路徑
space_path = "test_word_bag/testspace.dat" # TF-IDF詞向量空間保存路徑
train_tfidf_path="train_word_bag/tfdifspace.dat"
vector_space(stopword_path,bunch_path,space_path,train_tfidf_path)你已經(jīng)發(fā)現(xiàn)了,這段代碼與第4節(jié)幾乎一模一樣,唯一不同的就是在第39~41行中,我們導入了第4節(jié)中訓練集的IF-IDF詞向量空間,并且第41行將訓練集的vocabulary賦值給測試集的vocabulary,第43行增加了入口參數(shù)vocabulary,原因在上一節(jié)中都已經(jīng)說明,不再贅述。 考慮到第4節(jié)和剛才的代碼幾乎完全一樣,因此我們可以將這兩個代碼文件統(tǒng)一為一個: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: TFIDF_space.py
@time: 2017/2/8 11:39
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
from sklearn.datasets.base import Bunch
import cPickle as pickle
from sklearn.feature_extraction.text import TfidfVectorizer
def _readfile(path):
with open(path, "rb") as fp:
content = fp.read()
return content
def _readbunchobj(path):
with open(path, "rb") as file_obj:
bunch = pickle.load(file_obj)
return bunch
def _writebunchobj(path, bunchobj):
with open(path, "wb") as file_obj:
pickle.dump(bunchobj, file_obj)
def vector_space(stopword_path,bunch_path,space_path,train_tfidf_path=None):
stpwrdlst = _readfile(stopword_path).splitlines()
bunch = _readbunchobj(bunch_path)
tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
if train_tfidf_path is not None:
trainbunch = _readbunchobj(train_tfidf_path)
tfidfspace.vocabulary = trainbunch.vocabulary
vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5,vocabulary=trainbunch.vocabulary)
tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
else:
vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5)
tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
tfidfspace.vocabulary = vectorizer.vocabulary_
_writebunchobj(space_path, tfidfspace)
print "if-idf詞向量空間實例創(chuàng)建成功?。?!"
if __name__ == '__main__':
stopword_path = "train_word_bag/hlt_stop_words.txt"
bunch_path = "train_word_bag/train_set.dat"
space_path = "train_word_bag/tfdifspace.dat"
vector_space(stopword_path,bunch_path,space_path)
bunch_path = "test_word_bag/test_set.dat"
space_path = "test_word_bag/testspace.dat"
train_tfidf_path="train_word_bag/tfdifspace.dat"
vector_space(stopword_path,bunch_path,space_path,train_tfidf_path)哇哦,你好棒!現(xiàn)在連注釋都不用,就可以看懂代碼了。。。 對測試集進行了上述處理后,接下來的步驟,變得如此輕盈和優(yōu)雅。 #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: NBayes_Predict.py
@time: 2017/2/8 12:21
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import cPickle as pickle
from sklearn.naive_bayes import MultinomialNB # 導入多項式貝葉斯算法
# 讀取bunch對象
def _readbunchobj(path):
with open(path, "rb") as file_obj:
bunch = pickle.load(file_obj)
return bunch
# 導入訓練集
trainpath = "train_word_bag/tfdifspace.dat"
train_set = _readbunchobj(trainpath)
# 導入測試集
testpath = "test_word_bag/testspace.dat"
test_set = _readbunchobj(testpath)
# 訓練分類器:輸入詞袋向量和分類標簽,alpha:0.001 alpha越小,迭代次數(shù)越多,精度越高
clf = MultinomialNB(alpha=0.001).fit(train_set.tdm, train_set.label)
# 預測分類結果
predicted = clf.predict(test_set.tdm)
for flabel,file_name,expct_cate in zip(test_set.label,test_set.filenames,predicted):
if flabel != expct_cate:
print file_name,": 實際類別:",flabel," -->預測類別:",expct_cate
print "預測完畢!!!"
# 計算分類精度:
from sklearn import metrics
def metrics_result(actual, predict):
print '精度:{0:.3f}'.format(metrics.precision_score(actual, predict,average='weighted'))
print '召回:{0:0.3f}'.format(metrics.recall_score(actual, predict,average='weighted'))
print 'f1-score:{0:.3f}'.format(metrics.f1_score(actual, predict,average='weighted'))
metrics_result(test_set.label, predicted)出錯的這個,是我故意制造的,(因為實際分類精度100%,不能很好的說明問題)效果圖:
當然,你也可以采用其他分類器,比如KNN 6,評價與小結評價部分的實際操作我們已經(jīng)在上一節(jié)的代碼中給出了。這里主要是要解釋一下代碼的含義,以及相關的一些概念。 截止目前,我們已經(jīng)完成了全部的實踐工作。接下來,你或許希望做的是: 1,分詞工具和分詞算法的研究 2,文本分類算法的研究 這些內容,博主會在今后的時間里,專門研究并寫出博文。 整個工程的完整源代碼到這里下載: https://github.com/sunxiangguo/chinese_text_classification 需要說明的是,在工程代碼和本篇博文中,細心的你已經(jīng)發(fā)現(xiàn)了,我們所有的路徑前面都有一個點“. /”,這主要是因為我們不知道您會將工程建在哪個路徑內,因此這個表示的是你所在項目的目錄,本篇博文所有路徑都是相對路徑。因此你需要自己注意一下。工程里面語料庫是空的,因為上傳資源受到容量的限制。你需要自己添加。 7,進一步的討論:我們的這些工作究竟實不實用?這是很多人關心的問題。事實上,本博文的做法,是最經(jīng)典的文本分類思想。也是你進一步深入研究文本分類的基礎。在實際工作中,用本文的方法,已經(jīng)足夠勝任各種情況了。 那么,我們也許想問,有沒有更好,更新的技術?答案是有的。未來,博主會集中介紹兩種技術: 1.利用LDA模型進行文本分類 2.利用深度學習進行文本分類 利用深度學習進行文本分類,要求你必須對深度學習的理論有足夠多的掌握。 為此,你可以參考博主的其他博文, 例如下面的這個系列博文《卷積神經(jīng)網(wǎng)絡CNN理論到實踐》。 這是一些列的博文。與網(wǎng)上其他介紹CNN的博文不同的是:
8,At last
|
|
|