小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

python 中文文本分類

 qqcat5 2019-07-15

一,中文文本分類流程:

1,預處理

2,中文分詞

3,結構化表示--構建詞向量空間

4,權重策略--TF-IDF

5,分類器

6,評價

二,具體細節(jié)

1,預處理。希望得到這樣的目標:

1.1得到訓練集語料庫

即已經(jīng)分好類的文本資料(例如:語料庫里是一系列txt文章,這些文章按照主題歸入到不同分類的目錄中,如 .\art\21.txt)

推薦語料庫:復旦中文文本分類語料庫,下載鏈接:http://download.csdn.net/detail/github_36326955/9747927

將下載的語料庫解壓后,請自己修改文件名和路徑,例如路徑可以設置為 ./train_corpus/,

其下則是各個類別目錄如:./train_corpus/C3-Art,……,\train_corpus\C39-Sports

1.2得到測試語料庫

也是已經(jīng)分好類的文本資料,與1.1類型相同,只是里面的文檔不同,用于檢測算法的實際效果。測試預料可以從1.1中的訓練預料中隨機抽取,也可以下載獨立的測試語料庫,復旦中文文本分類語料庫測試集鏈接:http://download.csdn.net/detail/github_36326955/9747929

路徑修改參考1.1,例如可以設置為 ./test_corpus/

1.3其他

你可能希望從自己爬取到的網(wǎng)頁等內容中獲取新文本,用本節(jié)內容進行實際的文本分類,這時候,你可能需要將html標簽去除來獲取文本格式的文檔,這里提供一個基于python 和lxml的樣例代碼:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: html_demo.py
@time: 2017/2/6 12:25
@software: PyCharm
"""
import sys
from lxml import html
# 設置utf-8 unicode環(huán)境
reload(sys)
sys.setdefaultencoding('utf-8')

def html2txt(path):
    with open(path,"rb") as f:
        content=f.read() 
    r'''
    上面兩行是python2.6以上版本增加的語法,省略了繁瑣的文件close和try操作
    2.5版本需要from __future__ import with_statement
    新手可以參考這個鏈接來學習http:///archives/325
    '''
    page = html.document_fromstring(content) # 解析文件
    text = page.text_content() # 去除所有標簽
    return text

if __name__  =="__main__":
    # htm文件路徑,以及讀取文件
    path = "1.htm"
    text=html2txt(path)
    print text	 # 輸出去除標簽后解析結果




2,中文分詞。

本小節(jié)的目標是:

1,告訴你中文分詞的實際操作。

2,告訴你中文分詞的算法。





2.1概述

第1小節(jié)預處理中的語料庫都是沒有分詞的原始語料(即連續(xù)的句子,而后面的工作需要我們把文本分為一個個單詞),現(xiàn)在需要對這些文本進行分詞,只有這樣,才能在 基于單詞的基礎上,對文檔進行結構化表示。

中文分詞有其特有的難點(相對于英文而言),最終完全解決中文分詞的算法是基于概率圖模型的條件隨機場(CRF)。(可以參考博主的另一篇博文)

當然,在實際操作中,即使你對于相關算法不甚了解,也不影響你的操作,中文分詞的工具有很多。但是比較著名的幾個都是基于java的,這里推薦python的第三方庫jieba(所采用的算法就是條件隨機場)。對于非專業(yè)文檔綽綽有余。如果你有強迫癥,希望得到更高精度的分詞工具,可以使用開源項目Anjs(基于java),你可以將這個開源項目與python整合。

關于分詞庫的更多討論可以參考這篇文章:https://www.zhihu.com/question/19651613

你可以通過pip安裝jieba:打開cmd,切換到目錄  .../python/scripts/,執(zhí)行命令:pip install jieba

或者你也可以在集成開發(fā)平臺上安裝jieba,例如,如果你用的是pycharm,可以點擊file-settings-project:xxx-Projuect Interpreter.其他平臺也都有類似的安裝方法。

2.2分詞操作

不要擔心下面的代碼你看不懂,我會非常詳細的進行講解,確保python入門級別水平的人都可以看懂:

2.2.1

首先講解jieba分詞使用方法(詳細的和更進一步的,可以參考這個鏈接):

jieba.cut 方法接受三個輸入?yún)?shù): 需要分詞的字符串;cut_all 參數(shù)用來控制是否采用全模式;HMM 參數(shù)用來控制是否使用 HMM 模型
jieba.cut_for_search 方法接受兩個參數(shù):需要分詞的字符串;是否使用 HMM 模型。該方法適合用于搜索引擎構建倒排索引的分詞,粒度比較細
待分詞的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建議直接輸入 GBK 字符串,可能無法預料地錯誤解碼成 UTF-8
jieba.cut 以及 jieba.cut_for_search 返回的結構都是一個可迭代的 generator,可以使用 for 循環(huán)來獲得分詞后得到的每一個詞語(unicode),或者用
jieba.lcut 以及 jieba.lcut_for_search 直接返回 list
jieba.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

接下來,我們將要通過python編程,來將1.1節(jié)中的 ./train_corpus/原始訓練語料庫和1.2節(jié)中的./test_corpus/原始測試語料庫進行分詞,分詞后保存的路徑可以設置為

./train_corpus_seg/和./test_corpus_seg/

代碼如下,思路很簡單,就是遍歷所有的txt文本,然后將每個文本依次進行分詞。你唯一需要注意的就是寫好自己的路徑,不要出錯。下面的代碼已經(jīng)給出了非常詳盡的解釋,初學者也可以看懂。如果你還沒有明白,或者在運行中出現(xiàn)問題(其實根本不可能出現(xiàn)問題,我寫的代碼,質量很高的。。。),都可以發(fā)郵件給我,郵件地址在代碼中,或者在博文下方評論中給出。

#!/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ù)集(訓練集)有哪些信息:

1,類別,也就是所有分類類別的集合,即我們./train_corpus_seg/和./test_corpus_seg/下的所有子目錄的名字。我們在這里不妨把它叫做target_name(這是一個列表)

2,文本文件名。例如./train_corpus_seg/art/21.txt,我們可以把所有文件名集合在一起做一個列表,叫做filenames

3,文本標簽(就是文本的類別),不妨叫做label(與2中的filenames一一對應)

例如2中的文本“21.txt”在./train_corpus_seg/art/目錄下,則它的標簽就是art。

文本標簽與1中的類別區(qū)別在于:文本標簽集合里面的元素就是1中類別,而文本標簽集合的元素是可以重復的,因為./train_corpus_seg/art/目錄下有好多文本,不是嗎?相應的,1中的類別集合元素顯然都是獨一無二的類別。

4,文本內容(contens)。

上一步代碼我們已經(jīng)成功的把文本內容進行了分詞,并且去除掉了所有的換行,得到的其實就是一行詞袋(詞向量),每個文本文件都是一個詞向量。這里的文本內容指的就是這些詞向量。



那么,用Bunch表示,就是:

from sklearn.datasets.base import Bunch

bunch = Bunch(target_name=[],label=[],filenames=[],contents=[]) 



我們在Bunch對象里面創(chuàng)建了有4個成員:

target_name:是一個list,存放的是整個數(shù)據(jù)集的類別集合。

label:是一個list,存放的是所有文本的標簽。

filenames:是一個list,存放的是所有文本文件的名字。

contents:是一個list,分詞后文本文件詞向量形式

如果你還沒有明白,看一下下面這個圖,你總該明白了:

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的博文不同的是:
  1. 我們會全方位,足夠深入的為你講解CNN的知識。包括很多,你之前在網(wǎng)上找了很多資料也沒搞清楚的東西。
  2. 我們會利用CNN做文本分類的實踐。
  3. 我們會繪制大量精美的示意圖。保證博文的高質量和美觀。




8,At last





welcome!

Xiangguo Sun 

sunxiangguodut@qq.com 

http://blog.csdn.net/github_36326955 



Welcome to my blog column: Dive into ML/DL!

(click here to blog column Dive into ML/DL)

這里寫圖片描述


I devote myself to dive into typical algorithms on machine learning and deep learning, especially the application in the area of computational personality.

My research interests include computational personality, user portrait, online social network, computational society, and ML/DL. In fact you can find the internal connection between these concepts: 

這里寫圖片描述

In this blog column, I will introduce some typical algorithms about machine learning and deep learning used in OSNs(Online Social Networks), which means we will include NLP, networks community, information diffusion,and individual recommendation systemApparently, our ultimate target is to dive into user portrait , especially the issues on your personality analysis.


All essays are created by myself, and copyright will be reserved by me. You can use them for non-commercical intention and if you are so kind to donate me, you can scan the QR code below. All donation will be used to the library of charity for children in Lhasa.



    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多