利用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ù)處理流程是這樣的:
- 特征向量化邀桑;
- 樸素貝葉斯分類。
如果每次修改一個參數(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é)
回顧一下盗扇,本文介紹了以下知識點:
- 如何用一袋子詞(bag of words)模型將自然語言語句向量化祷肯,形成特征矩陣;
- 如何利用停用詞表疗隶、詞頻閾值和標記模式(token pattern)移除不想干的偽特征詞匯佑笋,降低模型復雜度。
- 如何選用合適的機器學習分類模型斑鼻,對詞語特征矩陣做出分類蒋纬;
- 如何用管道模式,歸并和簡化機器學習步驟流程坚弱;
- 如何選擇合適的性能測度工具蜀备,對模型的效能進行評估和對比。
希望這些內(nèi)容能夠幫助你更高效地處理中文文本情感分類工作荒叶。
討論
你之前用機器學習做過中文情感分類項目嗎碾阁?你是如何去除停用詞的?你使用的分類模型是哪個停撞?獲得的準確率怎么樣瓷蛙?歡迎留言,把你的經(jīng)驗和思考分享給大家戈毒,我們一起交流討論艰猬。
喜歡請點贊。還可以微信關(guān)注和置頂我的公眾號“玉樹芝蘭”(nkwangshuyi)埋市。
如果你對數(shù)據(jù)科學感興趣冠桃,不妨閱讀我的系列教程索引貼《如何高效入門數(shù)據(jù)科學?》道宅,里面還有更多的有趣問題及解法食听。