圖像是貓還是狗?情感是正還是負?貸還是不貸?這些問題,該如何使用合適的機器學習模型來解決呢?
問題
暑假后,又有一批研究生要開題了。這幾天陸續收到他們發來的研究計劃大綱。
其中好幾個,打算使用機器學習做分類。
但是,從他們的文字描述來看,不少人對機器學習進行分類的方法,還是一知半解。
考慮到之前分享機器學習處理分類問題的文章,往往針對具體的任務案例。似乎對分類問題的整體步驟與注意事項,還沒有詳細論述過。于是我決定寫這篇文章,幫他們梳理一下。
他們和你一樣,也是我專欄的讀者。
如果你對機器學習感興趣,并且實際遇到了分類任務,那我解答他們遇到的一些疑問,可能對于你同樣有用。
所以,我把這篇文章也分享給你。希望能有一些幫助。
監督
監督式機器學習任務很常見。主要模型,是分類與回歸。
就分類問題而言,二元分類是典型應用。
例如決策輔助,你利用結構化數據,判定可否貸款給某個客戶;
例如情感分析,你需要通過一段文字,來區分情感的正負極性;
例如圖像識別,你得識別出圖片是貓,還是狗。
今天咱們就先介紹一下,二元分類,這個最為簡單和常見的機器學習應用場景。
注意要做分類,你首先得有合適的數據。
什么是合適的數據呢?
這得回到我們對機器學習的大類劃分。
分類任務,屬于監督式學習。
監督式學習的特點,是要有標記。
例如給你1000張貓的圖片,1000張狗的圖片,扔在一起,沒有打標記。這樣你是做不了分類的。
雖然你可以讓機器學習不同圖片的特征,讓它把圖片區分開。
但是這叫做聚類,屬于非監督學習。
天知道,機器是根據什么特征把圖片分開的。
你想得到的結果,是貓放在一類,狗放在另一類。
但是機器抓取特征的時候,也許更喜歡按照顏色區分。
結果白貓白狗放在了一個類別,黃貓黃狗放在了另一個類別。跟你想要的結果大相徑庭。
如果你對非監督學習感興趣,可以參考《如何用Python從海量文本抽取主題?》一文。
所以,要做分類,就必須有標記才行。
但是標記不是天上掉下來的。
大部分情況下,都是人打上去的。
標記
打標記(Labeling),是個專業化而繁復的勞動。
你可以自己打,可以找人幫忙,也可以利用眾包的力量。
例如亞馬遜的“土耳其機器人”(Amazon Mechanical Turk)項目。
別被名字唬住,這不是什么人工智能項目,而是普通人可以利用業余時間賺外快的機會。
你可以幫別人做任務拿傭金。任務中很重要的一部分,就是人工分類,打標記。
因此如果你有原始數據,但是沒有標記,就可以把數據扔上去。
說明需求,花錢找人幫你標記。
類似的服務,國內也有很多。
建議找知名度比較高的平臺來做,這樣標記的質量會比較靠譜。
如果你還是在校學生,可能會覺得這樣的服務價格太貴,個人難以負擔。
沒關系。假如你的研究是有基金資助項目的一部分,可以正大光明地找導師申請數據采集費用。
但若你的研究屬于個人行為,就得另想辦法了。
不少學生選擇的辦法,是依靠團隊支持。
例如找低年級的研究生幫忙標記。
人家幫了忙,讓你發表了論文,順利畢業。你總得請大家吃一頓好吃的,是吧?
學習
有了標記以后,你就能夠實施監督學習,做分類了。
這里我們順帶說一下,什么叫做“機器學習”。
這個名字很時髦。
其實它做的事情,叫做“基于統計的信息表征”。
先說信息表征(representation)。
你的輸入,可能是結構化的數據,例如某個人的各項生理指標;可能是非結構化數據,例如文本、聲音甚至是圖像,但是最終機器學習模型看到的東西,都是一系列的數字。
這些數字,以某種形式排布。
可能是零維的,叫做標量(scalar);
可能是一維的,叫做向量(vector);
可能是二維的,叫做矩陣(Matrice);
可能是高維的,叫做張量(Tensor)。
但是,不論輸入的數據,究竟有多少維度,如果你的目標是做二元分類,那么經過一個或簡單、或復雜的模型,最后的輸出,一定是個標量數字。
你的模型,會設置一個閾值。例如0.5。
超出這個數字的,被分類到一處。
反之,被分類到另一處。
任務完成。
那么模型究竟在做什么呢?
它的任務,就是把輸入的數據,表征成最終的這個標量。
打個不恰當的比方,就如同高考。
每一個考生,其實都是獨特的。
每個人都有自己的家庭,自己的夢想,自己的經歷,自己的故事。
但是高考這個模型,不可能完整準確表征上述全部細節。
它簡單地以考生們的試卷答題紙作為輸入,以一個最終的總成績作為輸出。
然后,劃定一個叫做錄取分數線的東西,作為閾值(判定標準)。
達到或超出了,錄取。
否則,不錄取。
這個分數,就是高考模型對每個考生的信息表征。
所謂分類模型的優劣,其實就是看模型是否真的達到了預期的分類效果。
什么是好的分類效果?
大學想招收的人,錄取了(True Positive, TP);
大學不想招收的人,沒被錄取(True Negative, TN)。
什么是不好的分類效果?
大學想招收的人,沒能被錄取(False Negative, FN);
大學不想招收的人,被錄取了(False Positive, FP)。
好的模型,需要盡力增大 TP 和 TN 的比例,降低 FN 和 FP 的比例。
評判的標準,視你的類別數據平衡而定。
數據平衡,例如1000張貓照片,1000張狗照片,可以使用 ROC AUC。
數據不平衡,例如有1000張貓的照片,卻只有100張狗的照片,可以使用 Precision 和 Recall ,或者二者相結合的 F1 score。
因為有這樣明確的評估標準,所以二元分類模型不能滿足于“分了類”,而需要向著“更好的分類結果”前進。
辦法就是利用統計結果,不斷改進模型的表征方法。
所以,模型的參數需要不斷迭代。
恢復高考后的40年,高考的形式、科目、分值、大綱……包括加分政策等,一直都在變化。這也可以看作是一種模型的迭代。
“表征”+“統計”+“迭代”,這基本上就是所謂的“學習”。
結構化
看到這里,希望你的頭腦里已經有了機器學習做二元分類問題的技術路線概貌。
下面咱們針對不同的數據類型,說說具體的操作形式和注意事項。
先說最簡單的結構化數據。
例如《貸還是不貸:如何用Python和機器學習幫你決策?》一文中,我們見到過的客戶信息。
處理這樣的數據,你首先需要關注數據的規模。
如果數據量大,你可以使用復雜的模型。
如果數據量小,你就得使用簡單的模型。
為什么呢?
因為越復雜的模型,表征的信息就越多。
表征的信息多,未必是好事。
因為你抓住的,既有可能是信號,也有可能是噪聲。
如果表征信息多,可是學習過的數據不多,它可能就會對不該記住的信息,形成記憶。
在機器學習領域,這是最恐怖的結果——過擬合(overfitting)。
翻譯成人話,就是見過的數據,分類效果極好;沒見過的數據,表現很糟糕。
舉一個我自己的例子。
我上學前班后沒多久,我媽就被請了家長。
因為我漢語拼音默寫,得了0分。
老師嘴上說,是懷疑我不認真完成功課;心里想的,八成是這孩子智商余額不足。
其實我挺努力的。
每天老師讓回家默寫的內容,都默了。
但是我默寫的時候,是嚴格按照“a o e i u ……”的順序默的。
因為每天都這樣默寫,所以我記住的東西,不是每個讀音對應的符號,而是它們出現的順序。
結果上課的時候,老師是這樣念的“a b c d e ……”
我毫無懸念沒跟下來。
我的悲劇,源于自己的心智模型,實際上只反復學習了一條數據“a o e i u ……”。每天重復,導致過擬合,符號出現在順序中,才能辨識和記憶。
因此,見到了新的組合方式,就無能為力了。
看,過擬合很糟糕吧。
確定了模型的復雜度以后,你依然需要根據特征多少,選擇合適的分類模型。
上圖來自于 Scikit-learn ,我截取了其中“分類”模型部分,你可以做參考。
注意模型的效果,實際上是有等級劃分的。
例如根據 Kaggle 數據科學競賽多年的實踐結果來看,Gradient Boosting Machine 優于隨機森林,隨機森林優于決策樹。
這么比有些不厚道,因為三者的出現,也是有時間順序的。
讓爺爺跟孫子一起賽跑,公平性有待商榷。
因此,你不宜在論文中,將不同的分類模型,分別調包跑一遍,然后來個橫向對比大測評。
許多情況下,這是沒有意義的。
雖然顯得工作量很大。
但假如你發現在你自己的數據集上面,決策樹的效果就是明顯優于 Gradient Boosting Machine ,那你倒是很有必要通過論文做出匯報。
盡管大部分審稿人都會認為,一定是你算錯了。
另一個需要注意的事項,是特征工程(feature engineering)。
什么叫特征工程呢?
就是手動挑選特征,或者對特征(組合)進行轉化。
例如《如何用Python和深度神經網絡鎖定即將流失的客戶?》一文中,我們就對特征進行了甄別。其中三列數據,我們直接剔除掉了:
- RowNumber:行號,這個肯定沒用,刪除
- CustomerID:用戶編號,這個是順序發放的,刪除
- Surname:用戶姓名,對流失沒有影響,刪除
正式學習之前,你需要把手頭掌握的全部數據分成3類:
- 訓練集
- 驗證集
- 測試集
我在給期刊審稿的時候,發現許多使用機器學習模型的作者,無論中外,都似乎不能精確理解這些集合的用途。
訓練集讓你的模型學習,如何利用當前的超參數(例如神經網絡的層數、每一層的神經元個數等)組合,盡可能把表征擬合標記結果。
就像那個笑話說的一樣:
Machine Learning in a Nutshell:
Interviewer: what's you biggest strength?
Me: I'm a quick learner.
Interviewer: What's 11*11?
Me: 65.
Interviewer: Not even close. It's 121.
Me: It's 121.
而驗證集的存在,就是為了讓你對比不同的超參數選擇,哪一組更適合當前任務。它必須用訓練集沒有用過的數據。
驗證集幫你選擇了合適的超參數后,它的歷史任務也就結束了。
這時候,你可以把訓練集、驗證集合并在一起,用最終確定的超參數組合進行訓練,獲得最優模型。
這個模型表現怎么樣?
你當然需要其他的數據來評判。這就是為什么你還要劃分出另外的測試集。
圖像
Franc?ois Chollet 在自己的書中舉過一個例子,我覺得很有啟發,一并分享給你。
假如你看到了這樣的原始數據:
你該怎么做分類?
有的同學一看是圖像,立刻決定,上卷積神經網絡!
別忙,想想看,真的需要“直接上大錘”嗎?
別的不說,那一圈的刻度,就對我們的模型毫無意義。
你可以利用特征工程,將其表達為這樣的坐標點:
你看,這樣處理之后,你立刻就擁有了結構化數據。
注意這個轉換過程,并不需要人工完成,完全可以自動化。
但是舉一反三的你,估計已經想到了“更好的”解決方案:
對,這樣一來,表達鐘表時間的數據,就從原先的4個數字,變成了只需要2個。
一個本來需要用復雜模型解決的問題,就是因為簡單的特征工程轉化,復雜度和難度顯著下降。
其實,曾經人們進行圖片分類,全都得用特征工程的方法。
那個時候,圖片分類問題極其繁瑣、成本很高,而且效果還不理想。
手動提取的特征,也往往不具備良好的可擴展性和可遷移性。
于是,深度卷積神經網絡就登場了。
如果你的圖片數據量足夠多的話,你就可以采用“端到端”的學習方式。
所謂“端到端”,是指不進行任何的特征工程,構造一個規模合適的神經網絡模型,扔圖片進去就可以了。
但是,現實往往是殘酷的。
你最需要了解的,是圖片不夠多的時候,怎么辦。
這時候,很容易出現過擬合。
因為深度神經網絡,屬于典型的復雜模型。
這個時候,可以嘗試以下幾個不同的方法:
首先,如果有可能,搜集更多的帶標注圖片。這是最簡單的辦法,如果成本可以接受,你應該優先采用。
其次,使用數據增強(Data Augmentation)。名字聽起來很強大,其實無非是把原始的數據進行鏡像、剪裁、旋轉、扭曲等處理。這樣“新的”圖片與老圖片的標注肯定還是一樣的。但是圖片內容發生的變化,可以有效防止模型記住過多噪聲。
第三,使用遷移學習。
所謂遷移學習,就是利用別人訓練好的模型,保留其中從輸入開始的大多數層次(凍結保留其層次數量、神經元數量等網絡結構,以及權重數值),只把最后的幾層敲掉,換上自己的幾層神經網絡,對小規模數據做訓練。
上圖同樣來自于 Franc?ois Chollet 的著作。
這種做法,用時少,成本低,效果還特別好。如果重新訓練,圖片數少,就很容易過擬合。但是用了遷移學習,過擬合的可能性就大大降低。
其原理其實很容易理解。
卷積神經網絡的層次,越是靠近輸入位置,表達的特征就越是細節;越到后面,就越宏觀。
識別貓和狗,要從形狀邊緣開始;識別哆啦a夢和瓦力,也一樣要從形狀邊緣開始。因此模型的底層,可以被拿來使用。
你訓練的,只是最后幾層表征方式。結構簡單,當然也就不需要這么多數據了。
第四,引入 Dropout, Regularization 和 Early Stopping 等常規方法。注意這些方法不僅適用于圖像數據。
以 Dropout 為例。假如一個模型因為復雜,所以記住了很多噪聲,那么訓練的時候,每次都隨機將一定比例的神經元“扔掉”(設置權重為0),那么模型的復雜度降低。而且因為隨機,又比降低層數與神經元個數的固化模型適用性更高。
文本
前面說過了,機器不認得文本,只認得數字。
所以,要對文本做二元分類,你需要把文本轉換成為數字。
這個過程,叫做向量化。
向量化的方式,有好幾種。大致上可以分成兩類:
第一類,是無意義轉換。也就是轉換的數字,只是個編號而已,本身并不攜帶其他語義信息。
這一類問題,我們在《如何用Python和機器學習訓練中文文本情感分類模型?》中,已經非常詳細地介紹過了。
你需要做的,包括分詞(如果是中文)、向量化、去除停用詞,然后丟進一個分類模型(例如樸素貝葉斯,或者神經網絡),直接獲取結果,加以評估。
但是,這個過程,顯然有大量的語義和順序信息被丟棄了。
第二類,是有意義轉換。這時候,每個語言單元(例如單詞)轉換出來的數字,往往是個高維向量。
這個向量,你可以自己通過訓練來產生。
但是這種訓練,需要對海量語料進行建模。
建模的過程,成本很高,占用龐大存儲空間,運算量極大。
因此更常見的做法,是使用別人通過大規模語料訓練后的結果。也就是我們曾經介紹過的詞嵌入預訓練模型。
具體內容,請參見《如何用Python處理自然語言?(Spacy與Word Embedding)》和《如何用 Python 和 gensim 調用中文詞嵌入預訓練模型?》。
注意如果你有多個預訓練模型可以選擇,那么盡量選擇與你要解決任務的文本更為接近的那種。
畢竟預訓練模型來自于統計結果。兩種差別很大的語料,詞語在上下文中的含義也會有顯著差異,導致語義的刻畫不夠準確。
如果你需要在分類的過程中,同時考慮語義和語言單元順序等信息,那么你可以這樣做:
第一步,利用詞嵌入預訓練模型,把你的輸入語句轉化為張量,這解決了詞語的語義問題;
第二步,采用一維卷積神經網絡(Conv1D)模型,或者循環神經網絡模型(例如 LSTM),構造分類器。
注意這一步中,雖然兩種不同的神經網絡結構,都可以應用。但是一般而言,處理二元分類問題,前者(卷積神經網絡)表現更好。
因為卷積神經網絡實際上已經充分考慮了詞語的順序問題;而循環神經網絡用在此處,有些“大炮轟蚊子”。很容易發生過擬合,導致模型效果下降。
實施
如果你了解二元分類問題的整體流程,并且做好了模型的選擇,那么實際的機器學習過程,是很簡單的。
對于大部分的普通機器學習問題,你都可以用 Scikit-learn 來調用模型。
注意其實每一個模型,都有參數設置的需要。但是對于很多問題來說,默認初始參數,就能帶來很不錯的運行結果。
Scikit-learn 雖好,可惜一直不能很好支持深度學習任務。
因而不論是圖像還是文本分類問題,你都需要挑選一個好用的深度學習框架。
注意,目前主流的深度學習框架,很難說有好壞之分。
畢竟,在深度學習領域如此動蕩激烈的競爭環境中,“壞”框架(例如功能不完善、性能低下)會很快被淘汰出局。
然而,從易用性上來說,框架之間確實有很大區別。
易用到了一種極致,便是蘋果的 Turi Create 。
從《如何用Python和深度神經網絡識別圖像?》和《如何用Python和深度神經網絡尋找近似圖片?》這兩篇文章中,你應該有體會,Turi Create 在圖像識別和相似度查詢問題上,已經易用到你自己都不知道究竟發生了什么,任務就解決了。
但是,如果你需要對于神經網絡的結構進行深度設置,那么 Turi Create 就顯得不大夠用了。
畢竟,其開發的目標,是給蘋果移動設備開發者賦能,讓他們更好地使用深度學習技術。
對于更通用的科研和實踐深度學習任務,我推薦你用 Keras 。
它已經可以把 Theano, Tensorflow 和 CNTK 作為后端。
對比上面那張深度學習框架全家福,你應該看到,Keras 覆蓋了 Google 和 微軟自家框架,幾乎占領了深度學習框架界的半壁江山。
照這勢頭發展下去,一統江湖也說不定哦。
為什么 Keras 那么厲害?
因為簡單易學。
簡單易學到,顯著拉低了深度學習的門檻。
就連 Tensorflow 的主力開發人員 Josh Gordon,也已經認為你根本沒必要去學習曾經的 Tensorflow 繁復語法了。
直接學 Keras ,用它完成任務,結束。
另外,使用深度學習,你可能需要 GPU 硬件設備的支持。這東西比較貴。建議你采用租用的方式。
《如何用云端 GPU 為你的 Python 深度學習加速?》提到的 FloydHub,租賃一個小時,大概需要1美元左右。注冊賬號就贈送你2個小時;
至于《如何免費云端運行Python深度學習框架?》中提到的 Google Colab ,就更慷慨了——到目前為止,一直是免費試用。
喜歡請點贊。還可以微信關注和置頂我的公眾號“玉樹芝蘭”(nkwangshuyi)。
如果你對數據科學感興趣,不妨閱讀我的系列教程索引貼《如何高效入門數據科學?》,里面還有更多的有趣問題及解法。