如何用Python和機器學習訓練中文文本情感分類模型?

利用Python機器學習框架scikit-learn谨履,我們自己做一個分類模型欢摄,對中文評論信息做情感分析。其中還會介紹中文停用詞的處理方法笋粟。

疑惑

前些日子怀挠,我在微信后臺收到了一則讀者的留言。

我一下子有些懵——這怎么還帶點播了呢害捕?

但是旋即我醒悟過來绿淋,好像是我自己之前挖了個坑。

之前我寫過《 如何用Python從海量文本抽取主題尝盼? 》一文吞滞,其中有這么一段:

為了演示的流暢,我們這里忽略了許多細節(jié)盾沫。很多內(nèi)容使用的是預置默認參數(shù)裁赠,而且完全忽略了中文停用詞設置環(huán)節(jié),因此“這個”疮跑、“如果”融蹂、“可能”典格、“就是”這樣的停用詞才會大搖大擺地出現(xiàn)在結(jié)果中。不過沒有關(guān)系妹窖,完成比完美重要得多啊奄。知道了問題所在渐苏,后面改進起來很容易。有機會我會寫文章介紹如何加入中文停用詞的去除環(huán)節(jié)菇夸。

根據(jù)“自己挖坑自己填”的法則琼富,我決定把這一部分寫出來。

我可以使用偷懶的辦法庄新。

例如在原先的教程里鞠眉,更新中文停用詞處理部分薯鼠,打個補丁。

但是械蹋,最近我發(fā)現(xiàn)出皇,好像至今為止,我們的教程從來沒有介紹過如何用機器學習做情感分析哗戈。

你可能說郊艘,不對吧?

情感分析不是講過了嗎唯咬?老師你好像講過《 如何用Python做情感分析纱注? 》,《 如何用Python做輿情時間序列可視化胆胰? 》和《 如何用Python和R對《權(quán)力的游戲》故事情節(jié)做情緒分析狞贱? 》。

你記得真清楚煮剧,提出表揚斥滤。

但是請注意,之前這幾篇文章中勉盅,并沒有使用機器學習方法佑颇。我們只不過調(diào)用了第三方提供的文本情感分析工具而已。

但是問題來了草娜,這些第三方工具是在別的數(shù)據(jù)集上面訓練出來的挑胸,未必適合你的應用場景。

例如有些情感分析工具更適合分析新聞宰闰,有的更善于處理微博數(shù)據(jù)……你拿過來茬贵,卻是要對店鋪評論信息做分析。

這就如同你自己筆記本電腦里的網(wǎng)頁瀏覽器移袍,和圖書館電子閱覽室的網(wǎng)頁瀏覽器解藻,可能類型、版本完全一樣葡盗。但是你用起自己的瀏覽器螟左,就是比公用電腦上的舒服、高效——因為你已經(jīng)根據(jù)偏好觅够,對自己瀏覽器上的“書簽”胶背、“密碼存儲”、“稍后閱讀”都做了個性化設置喘先。

咱們這篇文章钳吟,就給你講講如何利用Python和機器學習,自己訓練模型窘拯,對中文評論數(shù)據(jù)做情感分類红且。

# 數(shù)據(jù)

我的一個學生坝茎,利用爬蟲抓取了大眾點評網(wǎng)站上的數(shù)萬條餐廳評論數(shù)據(jù)。

這些數(shù)據(jù)在爬取時暇番,包含了豐富的元數(shù)據(jù)類型景东。

我從中抽取了評論文本和評星(1-5星),用于本文的演示奔誓。

從這些數(shù)據(jù)里斤吐,我們隨機篩選評星為1,2厨喂,4和措,5的,各500條評論數(shù)據(jù)蜕煌。一共2000條派阱。

為什么只甩下評星數(shù)量為3的沒有選擇?

你先思考10秒鐘斜纪,然后往下看贫母,核對答案。

答案是這樣的:

因為我們只希望對情感做出(正和負)二元分類盒刚,4和5星可以看作正向情感腺劣,1和2是負向情感……3怎么算?

所以因块,為了避免這種邊界不清晰造成的混淆橘原,咱們只好把標為3星的內(nèi)容丟棄掉了。

整理好之后的評論數(shù)據(jù)涡上,如下圖所示趾断。

我已經(jīng)把數(shù)據(jù)放到了演示文件夾壓縮包里面。后文會給你提供下載路徑吩愧。

模型

使用機器學習的時候芋酌,你會遇到模型的選擇問題。

例如雁佳,許多模型都可以用來處理分類問題脐帝。邏輯回歸、決策樹甘穿、SVM腮恩、樸素貝葉斯……具體到咱們的評論信息情感分類問題梢杭,該用哪一種呢温兼?

幸好,Python上的機器學習工具包 scikit-learn 不僅給我們提供了方便的接口武契,供我們調(diào)用募判,而且還非常貼心地幫我們做了小抄(cheat-sheet)荡含。

這張圖看似密密麻麻,非辰斓妫混亂释液,實際上是一個非常好的迷宮指南。其中綠色的方框装处,是各種機器學習模型误债。而藍色的圓圈,是你做判斷的地方妄迁。

你看寝蹈,咱們要處理類別問題,對吧登淘?

順著往下看箫老,會要求你判斷數(shù)據(jù)是否有標記。我們有啊黔州。

繼續(xù)往下走耍鬓,數(shù)據(jù)小于100K嗎?

考慮一下流妻,我們的數(shù)據(jù)有2000條牲蜀,小于這個閾值。

接下來問是不是文本數(shù)據(jù)绅这?是啊各薇。

于是路徑到了終點。

Scikit-learn告訴我們:用樸素貝葉斯模型好了君躺。

小抄都做得如此照顧用戶需求峭判,你對scikit-learn的品質(zhì)應該有個預期了吧?如果你需要使用經(jīng)典機器學習模型(你可以理解成深度學習之外的所有模型)棕叫,我推薦你先嘗試scikit-learn 林螃。

向量化

如何用Python從海量文本抽取主題? 》一文里俺泣,我們講過自然語言處理時的向量化疗认。

忘了?

沒關(guān)系伏钠。

子曰:

學而時習之横漏,不亦樂乎?

這里咱們復習一下熟掂。

對自然語言文本做向量化(vectorization)的主要原因缎浇,是計算機看不懂自然語言。

計算機赴肚,顧名思義素跺,就是用來算數(shù)的二蓝。文本對于它(至少到今天)沒有真正的意義。

但是自然語言的處理指厌,是一個重要問題刊愚,也需要自動化的支持。因此人就得想辦法踩验,讓機器能盡量理解和表示人類的語言鸥诽。

假如這里有兩句話:

I love the game.

I hate the game.

那么我們就可以簡單粗暴地抽取出以下特征(其實就是把所有的單詞都羅列一遍):

  • I
  • love
  • hate
  • the
  • game

對每一句話,都分別計算特征出現(xiàn)個數(shù)箕憾。于是上面兩句話就轉(zhuǎn)換為以下表格:

按照句子為單位衙传,從左到右讀數(shù)字,第一句表示為[1, 1, 0, 1, 1]厕九,第二句就成了[1, 0, 1, 1, 1]蓖捶。

這就叫向量化。

這個例子里面扁远,特征的數(shù)量叫做維度俊鱼。于是向量化之后的這兩句話,都有5個維度畅买。

你一定要記住并闲,此時機器依然不能理解兩句話的具體含義。但是它已經(jīng)盡量在用一種有意義的方式來表達它們谷羞。

注意這里我們使用的帝火,叫做“一袋子詞”(bag of words)模型。

下面這張圖(來自 https://goo.gl/2jJ9Kp )湃缎,形象化表示出這個模型的含義犀填。


一袋子詞模型不考慮詞語的出現(xiàn)順序,也不考慮詞語和前后詞語之間的連接嗓违。每個詞都被當作一個獨立的特征來看待九巡。

你可能會問:“這樣不是很不精確嗎?充分考慮順序和上下文聯(lián)系蹂季,不是更好嗎冕广?”

沒錯,你對文本的順序偿洁、結(jié)構(gòu)考慮得越周全撒汉,模型可以獲得的信息就越多。

但是涕滋,凡事都有成本睬辐。只需要用基礎的排列組合知識,你就能計算出獨立考慮單詞,和考慮連續(xù)n個詞語(稱作 n-gram)溉委,造成的模型維度差異了。

為了簡單起見爱榕,咱們這里還是先用一袋子詞吧瓣喊。有空我再給你講講……

打住,不能再挖坑了黔酥。

中文

上一節(jié)咱們介紹的藻三,是自然語言向量化處理的通則。

處理中文的時候跪者,要更加麻煩一些棵帽。

因為不同于英文、法文等拉丁語系文字渣玲,中文天然沒有空格作為詞語之間的分割符號逗概。

我們要先將中文分割成空格連接的詞語。

例如把:

“我喜歡這個游戲”

變成:

“我 喜歡 這個 游戲”

這樣一來忘衍,就可以仿照英文句子的向量化逾苫,來做中文的向量化了。

你可能擔心計算機處理起中文的詞語枚钓,跟處理英文詞語有所不同铅搓。

這種擔心沒必要。

因為咱們前面講過搀捷,計算機其實連英文單詞也看不懂星掰。

在它眼里,不論什么自然語言的詞匯嫩舟,都只是某種特定組合的字符串而已氢烘。
不論處理中文還是英文,都需要處理的一種詞匯家厌,叫做停用詞威始。

中文維基百科里,是這么定義停用詞的:

在信息檢索中像街,為節(jié)省存儲空間和提高搜索效率黎棠,在處理自然語言數(shù)據(jù)(或文本)之前或之后會自動過濾掉某些字或詞,這些字或詞即被稱為Stop Words(停用詞)镰绎。

咱們做的脓斩,不是信息檢索,而已文本分類畴栖。

對咱們來說随静,你不打算拿它做特征的單詞,就可以當作停用詞。

還是舉剛才英文的例子燎猛,下面兩句話:

I love the game.

I hate the game.

告訴我恋捆,哪些是停用詞?

直覺會告訴你重绷,定冠詞 the 應該是沸停。

沒錯,它是虛詞昭卓,沒有什么特殊意義愤钾。

它在哪兒出現(xiàn),都是一個意思候醒。

一段文字里能颁,出現(xiàn)很多次定冠詞都很正常。把它和那些包含信息更豐富的詞匯(例如love, hate)放在一起統(tǒng)計倒淫,就容易干擾我們把握文本的特征伙菊。

所以,咱們把它當作停用詞敌土,從特征里面剔除出去占业。

舉一反三,你會發(fā)現(xiàn)分詞后的中文語句:

“我 喜歡 這個 游戲”

其中的“這個”應該也是停用詞吧纯赎?

答對了谦疾!

要處理停用詞,怎么辦呢犬金?當然你可以一個個手工來尋找念恍,但是那顯然效率太低。

有的機構(gòu)或者團隊處理過許多停用詞晚顷。他們會發(fā)現(xiàn)峰伙,某種語言里,停用詞是有規(guī)律的该默。

他們把常見的停用詞總結(jié)出來瞳氓,匯集成表格。以后只需要查表格栓袖,做處理匣摘,就可以利用先前的經(jīng)驗和知識,提升效率裹刮,節(jié)約時間音榜。

在scikit-learn中,英語停用詞是自帶的捧弃。只需要指定語言為英文赠叼,機器會幫助你自動處理它們擦囊。

但是中文……

scikit-learn開發(fā)團隊里,大概缺少足夠多的中文使用者吧嘴办。

好消息是瞬场,你可以使用第三方共享的停用詞表。

這種停用詞表到哪里下載呢涧郊?

我已經(jīng)幫你找到了 一個 github 項目 贯被,里面包含了4種停用詞表,來自哈工大底燎、四川大學和百度等自然語言處理方面的權(quán)威單位弹砚。

這幾個停用詞表文件長度不同双仍,內(nèi)容也差異很大。為了演示的方便與一致性桌吃,咱們統(tǒng)一先用哈工大這個停用詞表吧朱沃。

我已經(jīng)將其一并存儲到了演示目錄壓縮包中,供你下載茅诱。
# 環(huán)境

請你先到 這個網(wǎng)址 下載本教程配套的壓縮包逗物。

下載后解壓,你會在生成的目錄里面看到以下4個文件瑟俭。

下文中翎卓,我們會把這個目錄稱為“演示目錄”。

請一定注意記好它的位置哦摆寄。

要裝Python失暴,最簡便辦法是安裝Anaconda套裝。

請到 這個網(wǎng)址 下載Anaconda的最新版本微饥。

請選擇左側(cè)的 Python 3.6 版本下載安裝逗扒。

如果你需要具體的步驟指導,或者想知道Windows平臺如何安裝并運行Anaconda命令欠橘,請參考我為你準備的 視頻教程 矩肩。

打開終端,用cd命令進入演示目錄肃续。如果你不了解具體使用方法黍檩,也可以參考 視頻教程
我們需要使用許多軟件包始锚。如果每一個都手動安裝建炫,會非常麻煩。

我?guī)湍阕隽藗€虛擬環(huán)境的配置文件疼蛾,叫做environment.yaml 肛跌,也放在演示目錄中。

請你首先執(zhí)行以下命令:

conda env create -f environment.yaml

這樣,所需的軟件包就一次性安裝完畢了衍慎。

之后執(zhí)行转唉,

source activate datapy3

進入這個虛擬環(huán)境。

注意一定要執(zhí)行下面這句:

python -m ipykernel install --user --name=datapy3

只有這樣稳捆,當前的Python環(huán)境才會作為核心(kernel)在系統(tǒng)中注冊赠法。
確認你的電腦上已經(jīng)安裝了 Google Chrome 瀏覽器。如果沒有安裝請到這里 下載 安裝乔夯。

之后砖织,在演示目錄中,我們執(zhí)行:

jupyter notebook

Google Chrome會開啟末荐,并啟動 Jupyter 筆記本界面:

你可以直接點擊文件列表中的demo.ipynb文件侧纯,可以看到本教程的全部示例代碼。

你可以一邊看教程的講解甲脏,一邊依次執(zhí)行這些代碼眶熬。

但是,我建議的方法块请,是回到主界面下娜氏,新建一個新的空白 Python 3 (顯示名稱為datapy3的那個)筆記本。

請跟著教程墩新,一個個字符輸入相應的內(nèi)容贸弥。這可以幫助你更為深刻地理解代碼的含義,更高效地把技能內(nèi)化海渊。

準備工作結(jié)束绵疲,下面我們開始正式輸入代碼。

代碼

我們讀入數(shù)據(jù)框處理工具pandas切省。

import pandas as pd

利用pandas的csv讀取功能最岗,把數(shù)據(jù)讀入。

注意為了與Excel和系統(tǒng)環(huán)境設置的兼容性朝捆,該csv數(shù)據(jù)文件采用的編碼為GB18030般渡。這里需要顯式指定,否則會報錯芙盘。

df = pd.read_csv('data.csv', encoding='gb18030')

我們看看讀入是否正確驯用。

df.head()

前5行內(nèi)容如下:

看看數(shù)據(jù)框整體的形狀是怎么樣的:

df.shape
(2000, 2)

我們的數(shù)據(jù)一共2000行,2列儒老。完整讀入蝴乔。

我們并不準備把情感分析的結(jié)果分成4個類別。我們只打算分成正向和負向驮樊。

這里我們用一個無名函數(shù)來把評星數(shù)量>3的薇正,當成正向情感片酝,取值為1挖腰;反之視作負向情感雕沿,取值為0辽俗。

def make_label(df):
    df["sentiment"] = df["star"].apply(lambda x: 1 if x>3 else 0)

編制好函數(shù)之后薄疚,我們實際運行在數(shù)據(jù)框上面。

make_label(df)

看看結(jié)果:

df.head()

從前5行看來酥泞,情感取值就是根據(jù)我們設定的規(guī)則,從評星數(shù)量轉(zhuǎn)化而來悯许。

下面我們把特征和標簽拆開先壕。

X = df[['comment']]
y = df.sentiment

X 是我們的全部特征瘩扼。因為我們只用文本判斷情感,所以X實際上只有1列垃僚。

X.shape
(2000, 1)

而y是對應的標記數(shù)據(jù)邢隧。它也是只有1列。

y.shape
(2000,)

我們來看看 X 的前幾行數(shù)據(jù)冈在。

X.head()

注意這里評論數(shù)據(jù)還是原始信息倒慧。詞語沒有進行拆分。

為了做特征向量化包券,下面我們利用結(jié)巴分詞工具來拆分句子為詞語纫谅。

import jieba

我們建立一個輔助函數(shù),把結(jié)巴分詞的結(jié)果用空格連接溅固。

這樣分詞后的結(jié)果就如同一個英文句子一樣付秕,單次之間依靠空格分割。

def chinese_word_cut(mytext):
    return " ".join(jieba.cut(mytext))

有了這個函數(shù)侍郭,我們就可以使用 apply 命令询吴,把每一行的評論數(shù)據(jù)都進行分詞。

X['cutted_comment'] = X.comment.apply(chinese_word_cut)

我們看看分詞后的效果:

X.cutted_comment[:5]

單詞和標點之間都用空格分割亮元,符合我們的要求猛计。

下面就是機器學習的常規(guī)步驟了:我們需要把數(shù)據(jù)分成訓練集和測試集。

為什么要拆分數(shù)據(jù)集合爆捞?

在《貸還是不貸:如何用Python和機器學習幫你決策奉瘤?》一文中,我已解釋過煮甥,這里復習一下:

如果期末考試之前盗温,老師給你一套試題和答案,你把它背了下來成肘。然后考試的時候卖局,只是從那套試題里面抽取一部分考。你憑借超人的記憶力獲得了100分双霍。請問你學會了這門課的知識了嗎砚偶?不知道如果給你新的題目孕索,你會不會做呢捂敌?答案還是不知道。所以考試題目需要和復習題目有區(qū)別英妓。

同樣的道理顷蟀,假設咱們的模型只在某個數(shù)據(jù)集上訓練酒请,準確度非常高,但是從來沒有見過其他新數(shù)據(jù)鸣个,那么它面對新數(shù)據(jù)表現(xiàn)如何呢羞反?

你心里也沒底吧布朦?

所以我們需要把數(shù)據(jù)集拆開,只在訓練集上訓練昼窗。保留測試集先不用是趴,作為考試題,看模型經(jīng)過訓練后的分類效果澄惊。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

這里唆途,我們設定了 random_state 取值,這是為了在不同環(huán)境中掸驱,保證隨機數(shù)取值一致肛搬,以便驗證咱們模型的實際效果。

我們看看此時的 X_train 數(shù)據(jù)集形狀毕贼。

X_train.shape
(1500, 2)

可見温赔,在默認模式下,train_test_split函數(shù)對訓練集和測試集的劃分比例為 3:1鬼癣。

我們檢驗一下其他3個集合看看:

y_train.shape
(1500,)
X_test.shape
(500, 2)
y_test.shape
(500,)

同樣都正確無誤陶贼。

下面我們就要處理中文停用詞了。

我們編寫一個函數(shù)待秃,從中文停用詞表里面拜秧,把停用詞作為列表格式保存并返回:

def get_custom_stopwords(stop_words_file):
    with open(stop_words_file) as f:
        stopwords = f.read()
    stopwords_list = stopwords.split('\n')
    custom_stopwords_list = [i for i in stopwords_list]
    return custom_stopwords_list

我們指定使用的停用詞表,為我們已經(jīng)下載保存好的哈工大停用詞表文件锥余。

stop_words_file = "stopwordsHIT.txt"
stopwords = get_custom_stopwords(stop_words_file)

看看我們的停用詞列表的后10項:

stopwords[-10:]

這些大部分都是語氣助詞腹纳,作為停用詞去除掉痢掠,不會影響到語句的實質(zhì)含義驱犹。

下面我們就要嘗試對分詞后的中文語句做向量化了。

我們讀入CountVectorizer向量化工具足画,它依據(jù)詞語出現(xiàn)頻率轉(zhuǎn)化向量雄驹。

from sklearn.feature_extraction.text import CountVectorizer

我們建立一個CountVectorizer()的實例,起名叫做vect淹辞。

注意這里為了說明停用詞的作用医舆。我們先使用默認參數(shù)建立vect。

vect = CountVectorizer()

然后我們用向量化工具轉(zhuǎn)換已經(jīng)分詞的訓練集語句象缀,并且將其轉(zhuǎn)化為一個數(shù)據(jù)框蔬将,起名為term_matrix

term_matrix = pd.DataFrame(vect.fit_transform(X_train.cutted_comment).toarray(), columns=vect.get_feature_names())

我們看看term_matrix的前5行:

term_matrix.head()

我們注意到央星,特征詞語五花八門霞怀,特別是很多數(shù)字都被當作特征放在了這里。

term_matrix的形狀如下:

term_matrix.shape
(1500, 7305)

行數(shù)沒錯莉给,列數(shù)就是特征個數(shù)毙石,有7305個廉沮。

下面我們測試一下,加上停用詞去除功能徐矩,特征向量的轉(zhuǎn)化結(jié)果會有什么變化滞时。

vect = CountVectorizer(stop_words=frozenset(stopwords))

下面的語句跟剛才一樣:

term_matrix = pd.DataFrame(vect.fit_transform(X_train.cutted_comment).toarray(), columns=vect.get_feature_names())
term_matrix.head()

可以看到,此時特征個數(shù)從剛才的7305個滤灯,降低為7144個坪稽。我們沒有調(diào)整任何其他的參數(shù),因此減少的161個特征鳞骤,就是出現(xiàn)在停用詞表中的單詞刽漂。

但是,這種停用詞表的寫法弟孟,依然會漏掉不少漏網(wǎng)之魚贝咙。

首先就是前面那一堆顯眼的數(shù)字。它們在此處作為特征毫無道理拂募。如果沒有單位庭猩,沒有上下文,數(shù)字都是沒有意義的陈症。

因此我們需要設定蔼水,數(shù)字不能作為特征。

在Python里面录肯,我們可以設定token_pattern來完成這個目標趴腋。

這一部分需要用到正則表達式的知識,我們這里無法詳細展開了论咏。

但如果你只是需要去掉數(shù)字作為特征的話优炬,按照我這樣寫,就可以了厅贪。

另一個問題在于蠢护,我們看到這個矩陣,實際上是個非常稀疏的矩陣养涮,其中大部分的取值都是0.

這沒有關(guān)系葵硕,也很正常。

畢竟大部分評論語句當中只有幾個到幾十個詞語而已贯吓。7000多的特征懈凹,單個語句顯然是覆蓋不過來的。

然而悄谐,有些詞匯作為特征介评,就值得注意了。

首先是那些過于普遍的詞匯尊沸。盡管我們用了停用詞表威沫,但是難免有些詞匯幾乎出現(xiàn)在每一句評論里贤惯。什么叫做特征?特征就是可以把一個事物與其他事物區(qū)別開的屬性棒掠。

假設讓你描述今天見到的印象最深刻的人孵构。你怎么描述?

我看見他穿著小丑的衣服烟很,在繁華的商業(yè)街踩高蹺颈墅,一邊走還一邊拋球,和路人打招呼雾袱。

還是……

我看見他有兩只眼睛恤筛,一只鼻子。

后者絕對不算是好的特征描述芹橡,因為難以把你要描述的個體區(qū)分出來毒坛。

物極必反,那些過于特殊的詞匯林说,其實也不應該保留煎殷。因為你了解了這個特征之后,對你的模型處理新的語句情感判斷腿箩,幾乎都用不上豪直。

這就如同你跟著神仙學了屠龍之術(shù),然而之后一輩子也沒有見過龍……

所以珠移,如下面兩個代碼段所示弓乙,我們一共多設置了3層特征詞匯過濾。

max_df = 0.8 # 在超過這一比例的文檔中出現(xiàn)的關(guān)鍵詞(過于平凡)钧惧,去除掉暇韧。
min_df = 3 # 在低于這一數(shù)量的文檔中出現(xiàn)的關(guān)鍵詞(過于獨特),去除掉垢乙。
vect = CountVectorizer(max_df = max_df,
                       min_df = min_df,
                       token_pattern=u'(?u)\\b[^\\d\\W]\\w+\\b',
                       stop_words=frozenset(stopwords))

這時候锨咙,再運行我們之前的語句,看看效果追逮。

term_matrix = pd.DataFrame(vect.fit_transform(X_train.cutted_comment).toarray(), columns=vect.get_feature_names())
term_matrix.head()

可以看到,那些數(shù)字全都不見了粹舵。特征數(shù)量從單一詞表法去除停用詞之后的7144個钮孵,變成了1864個。

你可能會覺得眼滤,太可惜了吧巴席?好容易分出來的詞,就這么扔了诅需?

要知道漾唉,特征多荧库,絕不一定是好事兒。

尤其是噪聲大量混入時赵刑,會顯著影響你模型的效能分衫。

好了,評論數(shù)據(jù)訓練集已經(jīng)特征向量化了般此。下面我們要利用生成的特征矩陣來訓練模型了蚪战。

我們的分類模型,采用樸素貝葉斯(Multinomial naive bayes)铐懊。

from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()

注意我們的數(shù)據(jù)處理流程是這樣的:

  1. 特征向量化邀桑;
  2. 樸素貝葉斯分類。

如果每次修改一個參數(shù)科乎,或者換用測試集壁畸,我們都需要重新運行這么多的函數(shù),肯定是一件效率不高茅茂,且令人頭疼的事兒瓤摧。而且只要一復雜,出現(xiàn)錯誤的幾率就會增加玉吁。

幸好照弥,Scikit-learn給我們提供了一個功能,叫做管道(pipeline)进副,可以方便解決這個問題这揣。

它可以幫助我們,把這些順序工作連接起來影斑,隱藏其中的功能順序關(guān)聯(lián)给赞,從外部一次調(diào)用,就能完成順序定義的全部工作矫户。

使用很簡單片迅,我們就把 vect 和 nb 串聯(lián)起來,叫做pipe皆辽。

from sklearn.pipeline import make_pipeline
pipe = make_pipeline(vect, nb)

看看它都包含什么步驟:

pipe.steps

看柑蛇,我們剛才做的工作,都在管道里面了驱闷。我們可以把管道當成一個整體模型來調(diào)用耻台。

下面一行語句,就可以把未經(jīng)特征向量化的訓練集內(nèi)容輸入空另,做交叉驗證盆耽,算出模型分類準確率的均值。

from sklearn.cross_validation import cross_val_score
cross_val_score(pipe, X_train.cutted_comment, y_train, cv=5, scoring='accuracy').mean()

咱們的模型在訓練中的準確率如何呢?

0.820687244673089

這個結(jié)果摄杂,還是不錯的坝咐。

回憶一下,總體的正向和負向情感析恢,各占了數(shù)據(jù)集的一半墨坚。

如果我們建立一個“笨模型”(dummy model),即所有的評論氮昧,都當成正向(或者負向)情感框杜,準確率多少?

對袖肥,50%咪辱。

目前的模型準確率,遠遠超出這個數(shù)值椎组。超出的這30%多油狂,其實就是評論信息為模型帶來的確定性。

但是寸癌,不要忘了专筷,我們不能光拿訓練集來說事兒,對吧蒸苇?下面咱們給模型來個考試磷蛹。

我們用訓練集,把模型擬合出來溪烤。

pipe.fit(X_train.cutted_comment, y_train)

然后味咳,我們在測試集上,對情感分類標記進行預測檬嘀。

pipe.predict(X_test.cutted_comment)

這一大串0和1槽驶,你看得是否眼花繚亂?

沒關(guān)系鸳兽,scikit-learn給我們提供了非常多的模型性能測度工具掂铐。

我們先把預測結(jié)果保存到y_pred

y_pred = pipe.predict(X_test.cutted_comment)

讀入 scikit-learn 的測量工具集揍异。

from sklearn import metrics

我們先來看看測試準確率:

metrics.accuracy_score(y_test, y_pred)
0.86

這個結(jié)果是不是讓你很吃驚全陨?沒錯,模型面對沒有見到的數(shù)據(jù)蒿秦,居然有如此高的情感分類準確性烤镐。

對于分類問題,光看準確率有些不全面棍鳖,咱們來看看混淆矩陣。

metrics.confusion_matrix(y_test, y_pred)
array([[194,  43],
       [ 27, 236]])

混淆矩陣中的4個數(shù)字,分別代表:

  • TP: 本來是正向渡处,預測也是正向的镜悉;
  • FP: 本來是負向,預測卻是正向的医瘫;
  • FN: 本來是正向侣肄,預測卻是負向的;
  • TN: 本來是負向醇份,預測也是負向的稼锅。

下面這張圖(來自 https://goo.gl/5cYGZd )應該能讓你更為清晰理解混淆矩陣的含義:

寫到這兒,你大概能明白咱們模型的性能了僚纷。

但是總不能只把咱們訓練出的模型和無腦“笨模型”去對比吧矩距?這也太不公平了!

下面怖竭,我們把老朋友 SnowNLP 呼喚出來锥债,做個對比。

如果你把它給忘了痊臭,請復習《如何用Python做情感分析哮肚?

from snownlp import SnowNLP
def get_sentiment(text):
    return SnowNLP(text).sentiments

我們利用測試集評論原始數(shù)據(jù),讓 SnowNLP 跑一遍广匙,獲得結(jié)果允趟。

y_pred_snownlp = X_test.comment.apply(get_sentiment)

注意這里有個小問題。 SnowNLP 生成的結(jié)果鸦致,不是0和1潮剪,而是0到1之間的小數(shù)。所以我們需要做一步轉(zhuǎn)換蹋凝,把0.5以上的結(jié)果當作正向鲁纠,其余當作負向。

y_pred_snownlp_normalized = y_pred_snownlp.apply(lambda x: 1 if x>0.5 else 0)

看看轉(zhuǎn)換后的前5條 SnowNLP 預測結(jié)果:

y_pred_snownlp_normalized[:5]

好了鳍寂,符合我們的要求改含。

下面我們先看模型分類準確率:

metrics.accuracy_score(y_test, y_pred_snownlp_normalized)
0.77

與之對比,咱們的測試集分類準確率迄汛,可是0.86哦捍壤。

我們再來看看混淆矩陣。

metrics.confusion_matrix(y_test, y_pred_snownlp_normalized)
array([[189,  48],
       [ 67, 196]])

對比的結(jié)果鞍爱,是 TP 和 TN 兩項上鹃觉,咱們的模型判斷正確數(shù)量,都要超出 SnowNLP睹逃。

小結(jié)

回顧一下盗扇,本文介紹了以下知識點:

  1. 如何用一袋子詞(bag of words)模型將自然語言語句向量化祷肯,形成特征矩陣;
  2. 如何利用停用詞表疗隶、詞頻閾值和標記模式(token pattern)移除不想干的偽特征詞匯佑笋,降低模型復雜度。
  3. 如何選用合適的機器學習分類模型斑鼻,對詞語特征矩陣做出分類蒋纬;
  4. 如何用管道模式,歸并和簡化機器學習步驟流程坚弱;
  5. 如何選擇合適的性能測度工具蜀备,對模型的效能進行評估和對比。

希望這些內(nèi)容能夠幫助你更高效地處理中文文本情感分類工作荒叶。

討論

你之前用機器學習做過中文情感分類項目嗎碾阁?你是如何去除停用詞的?你使用的分類模型是哪個停撞?獲得的準確率怎么樣瓷蛙?歡迎留言,把你的經(jīng)驗和思考分享給大家戈毒,我們一起交流討論艰猬。

喜歡請點贊。還可以微信關(guān)注和置頂我的公眾號“玉樹芝蘭”(nkwangshuyi)埋市。

如果你對數(shù)據(jù)科學感興趣冠桃,不妨閱讀我的系列教程索引貼《如何高效入門數(shù)據(jù)科學?》道宅,里面還有更多的有趣問題及解法食听。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市污茵,隨后出現(xiàn)的幾起案子樱报,更是在濱河造成了極大的恐慌,老刑警劉巖泞当,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迹蛤,死亡現(xiàn)場離奇詭異,居然都是意外死亡襟士,警方通過查閱死者的電腦和手機盗飒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陋桂,“玉大人逆趣,你說我怎么就攤上這事∈壤” “怎么了宣渗?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵抖所,是天一觀的道長。 經(jīng)常有香客問我落包,道長部蛇,這世上最難降的妖魔是什么摊唇? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任咐蝇,我火速辦了婚禮,結(jié)果婚禮上巷查,老公的妹妹穿的比我還像新娘有序。我一直安慰自己,他們只是感情好岛请,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布旭寿。 她就那樣靜靜地躺著,像睡著了一般崇败。 火紅的嫁衣襯著肌膚如雪盅称。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天后室,我揣著相機與錄音缩膝,去河邊找鬼。 笑死岸霹,一個胖子當著我的面吹牛疾层,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贡避,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼痛黎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刮吧?” 一聲冷哼從身側(cè)響起湖饱,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杀捻,沒想到半個月后井厌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡水醋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年旗笔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拄踪。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝇恶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惶桐,到底是詐尸還是另有隱情撮弧,我是刑警寧澤潘懊,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站贿衍,受9級特大地震影響授舟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贸辈,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一释树、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擎淤,春花似錦奢啥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至席吴,卻和暖如春赌结,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孝冒。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工柬姚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迈倍。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓伤靠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親啼染。 傳聞我的和親對象是個殘疾皇子宴合,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容