如何入門(mén)Python與機器學(xué)習
編者按:本書(shū)節選自圖書(shū)《Python與機器學(xué)習實(shí)戰》,Python本身帶有許多機器學(xué)習的第三方庫,但本書(shū)在絕大多數情況下只會(huì )用到Numpy這個(gè)基礎的科學(xué)計算庫來(lái)進(jìn)行算法代碼的實(shí)現。這樣做的目的是希望讀者能夠從實(shí)現的過(guò)程中更好地理解機器學(xué)習算法的細節,以及了解Numpy的各種應用。不過(guò)作為補充,本書(shū)會(huì )在適當的時(shí)候應用scikit-learn這個(gè)成熟的第三方庫中的模型。
本文引用地址:http://dyxdggzs.com/article/201710/365207.htm“機器學(xué)習”在最近雖可能不至于到人盡皆知的程度,卻也是非?;馃岬脑~匯。機器學(xué)習是英文單詞“Machine Learning”(簡(jiǎn)稱(chēng)ML)的直譯,從字面上便說(shuō)明了這門(mén)技術(shù)是讓機器進(jìn)行“學(xué)習”的技術(shù)。然而我們知道機器終究是死的,所謂的“學(xué)習”歸根結底亦只是人類(lèi)“賦予”機器的一系列運算。這個(gè)“賦予”的過(guò)程可以有很多種實(shí)現,而Python正是其中相對容易上手、同時(shí)性能又相當不錯的一門(mén)語(yǔ)言。本文打算先談?wù)剻C器學(xué)習相關(guān)的一些比較寬泛的知識,再介紹并說(shuō)明為何要使用Python來(lái)作為機器學(xué)習的工具。最后,我們會(huì )提供一個(gè)簡(jiǎn)短易懂的、具有實(shí)際意義的例子來(lái)給大家提供一個(gè)直觀(guān)的感受。
具體而言,本章主要涉及的知識點(diǎn)有:
機器學(xué)習的定義及重要性;
Python在機器學(xué)習領(lǐng)域的優(yōu)異性;
如何在電腦上配置Python機器學(xué)習的環(huán)境;
機器學(xué)習一般性的步驟。
機器學(xué)習緒論
正如前言所說(shuō),由于近期的各種最新成果,使得“機器學(xué)習”成為了非常熱門(mén)的詞匯。機器學(xué)習在各種領(lǐng)域的優(yōu)異表現(圍棋界的Master是其中最具代表性的存在),使得各行各業(yè)的人們都或多或少地對機器學(xué)習產(chǎn)生了興趣與敬畏。然而與此同時(shí),對機器學(xué)習有所誤解的群體也日益壯大;他們或將機器學(xué)習想得過(guò)于神秘,或將它想得過(guò)于萬(wàn)能。本節擬對機器學(xué)習進(jìn)行一般性的介紹,同時(shí)會(huì )說(shuō)明機器學(xué)習中一些常見(jiàn)的術(shù)語(yǔ)以方便之后章節的敘述。
什么是機器學(xué)習
清晨的一句“今天天氣真好”、朋友之間的寒暄“你剛剛是去吃飯了吧”、考試過(guò)后的感嘆“復習了那么久終有收獲”……這些日常生活中隨處可見(jiàn)的話(huà)語(yǔ),其背后卻已蘊含了“學(xué)習”的思想—它們都是利用以往的經(jīng)驗、對未知的新情況作出的有效的決策。而把這個(gè)決策的過(guò)程交給計算機來(lái)做,可以說(shuō)就是“機器學(xué)習”的一個(gè)最淺白的定義。
我們或許可以先說(shuō)說(shuō)機器學(xué)習與以往的計算機工作樣式有什么不同。傳統的計算機如果想要得到某個(gè)結果,需要人類(lèi)賦予它一串實(shí)打實(shí)的指令,然后計算機就根據這串指令一步步地執行下去。這個(gè)過(guò)程中的因果關(guān)系非常明確,只要人類(lèi)的理解不出偏差,運行結果是可以準確預測的。但是在機器學(xué)習中,這一傳統樣式被打破了:計算機確實(shí)仍然需要人類(lèi)賦予它一串指令,但這串指令往往不能直接得到結果;相反,它是一串賦予了機器“學(xué)習能力”的指令。在此基礎上,計算機需要進(jìn)一步地接受“數據”,并根據之前人類(lèi)賦予它的“學(xué)習能力”,從中“學(xué)習”出最終的結果。這個(gè)結果往往是無(wú)法僅僅通過(guò)直接編程得出的。因此這里就導出了稍微深一點(diǎn)的機器學(xué)習的定義:它是一種讓計算機利用數據而非指令來(lái)進(jìn)行各種工作的方法。在這背后,最關(guān)鍵的就是“統計”的思想,它所推崇的“相關(guān)而非因果”的概念是機器學(xué)習的理論根基。在此基礎上,機器學(xué)習可以說(shuō)是計算機使用輸入給它的數據,利用人類(lèi)賦予它的算法得到某種模型的過(guò)程,其最終的目的則是使用該模型,預測未來(lái)未知數據的信息。
既然提到了統計,那么一定的數學(xué)理論就不可或缺。相關(guān)的、比較簡(jiǎn)短的定義會(huì )在第4章給出(PAC框架),這里我們就先只敘述機器學(xué)習在統計理論下的、比較深刻的本質(zhì):它追求的是合理的假設空間(Hypothesis Space)的選取和模型的泛化(Generalization)能力。該句中出現了一些專(zhuān)用術(shù)語(yǔ),詳細的定義會(huì )在介紹術(shù)語(yǔ)時(shí)提及,這里我們提供一個(gè)直觀(guān)的理解:
所謂的假設空間,就是我們的模型在數學(xué)上的“適用場(chǎng)合”。
所謂的泛化能力,就是我們的模型在未知數據上的表現。
注意:上述本質(zhì)上嚴格來(lái)說(shuō),應該是PAC Learning的本質(zhì);在其余的理論框架下,機器學(xué)習是可以具有不同的內核的。
從上面的討論可以看出,機器學(xué)習和人類(lèi)思考的過(guò)程有或多或少的類(lèi)似。事實(shí)上,我們在第6、第7章講的神經(jīng)網(wǎng)絡(luò )(Neural Network,NN)和卷積神經(jīng)網(wǎng)絡(luò )(Convolutional Neural Network,CNN)背后確實(shí)有著(zhù)相應的神經(jīng)科學(xué)的理論背景。然而與此同時(shí)需要知道的是,機器學(xué)習并非是一個(gè)“會(huì )學(xué)習的機器人”和“具有學(xué)習能力的人造人”之類(lèi)的,這一點(diǎn)從上面諸多討論也可以明晰(慚愧的是,筆者在第一次聽(tīng)到“機器學(xué)習”四個(gè)字時(shí),腦海中浮現的正是一個(gè)“聰明的機器人”的圖像,甚至還幻想過(guò)它和人類(lèi)一起生活的場(chǎng)景)。相反的,它是被人類(lèi)利用的、用于發(fā)掘數據背后信息的工具。
當然,現在也不乏“危險的人工智能”的說(shuō)法,霍金大概是其中的“標桿”,這位偉大的英國理論物理學(xué)家甚至警告說(shuō)“人工智能的發(fā)展可能意味著(zhù)人類(lèi)的滅亡”。孰好孰壞果然還是見(jiàn)仁見(jiàn)智,但可以肯定的是:本書(shū)所介紹的內容絕不至于導致世界的毀滅,大家大可輕松愉快地進(jìn)行接下來(lái)的閱讀!
機器學(xué)習常用術(shù)語(yǔ)
機器學(xué)習領(lǐng)域有著(zhù)許多非?;镜男g(shù)語(yǔ),這些術(shù)語(yǔ)在外人聽(tīng)來(lái)可能相當高深莫測。它們事實(shí)上也可能擁有非常復雜的數學(xué)背景,但需要知道:它們往往也擁有著(zhù)相對淺顯平凡的直觀(guān)理解(上一小節的假設空間和泛化能力就是兩個(gè)例子)。本小節會(huì )對這些常用的基本術(shù)語(yǔ)進(jìn)行說(shuō)明與解釋?zhuān)鼈儽澈蟮臄祵W(xué)理論會(huì )有所闡述,但不會(huì )涉及過(guò)于本質(zhì)的東西。
正如前文反復強調的,數據在機器學(xué)習中發(fā)揮著(zhù)不可或缺的作用;而用于描述數據的術(shù)語(yǔ)有好幾個(gè),需要被牢牢記住的如下。
“數據集”(Data Set),就是數據的集合的意思。其中,每一條單獨的數據被稱(chēng)為“樣本”(Sample)。若沒(méi)有進(jìn)行特殊說(shuō)明,本書(shū)都會(huì )假設數據集中樣本之間在各種意義下相互獨立。事實(shí)上,除了某些特殊的模型(如隱馬爾可夫模型和條件隨機場(chǎng)),該假設在大多數場(chǎng)景下都是相當合理的。
對于每個(gè)樣本,它通常具有一些“屬性”(Attribute)或者說(shuō)“特征”(Feature),特征所具體取的值就被稱(chēng)為“特征值”(Feature Value)。
特征和樣本所張成的空間被稱(chēng)為“特征空間”(Feature Space)和“樣本空間”(Sample Space),可以把它們簡(jiǎn)單地理解為特征和樣本“可能存在的空間”。
相對應的,我們有“標簽空間”(Label Space),它描述了模型的輸出“可能存在的空間”;當模型是分類(lèi)器時(shí),我們通常會(huì )稱(chēng)之為“類(lèi)別空間”。
其中、數據集又可以分為以下三類(lèi):
訓練集(Training Set);顧名思義,它是總的數據集中用來(lái)訓練我們模型的部分。雖說(shuō)將所有數據集都拿來(lái)當作訓練集也無(wú)不可,不過(guò)為了提高及合理評估模型的泛化能力,我們通常只會(huì )取數據集中的一部分來(lái)當訓練集。
測試集(Test Set);顧名思義,它是用來(lái)測試、評估模型泛化能力的部分。測試集不會(huì )用在模型的訓練部分,換句話(huà)說(shuō),測試集相對于模型而言是“未知”的,所以拿它來(lái)評估模型的泛化能力是相當合理的。
交叉驗證集(Cross-Validation Set,CV Set);這是比較特殊的一部分數據,它是用來(lái)調整模型具體參數的。
注意:需要指出的是,獲取數據集這個(gè)過(guò)程是不平凡的;尤其是當今“大數據”如日中天的情景下,諸如“得數據者得天下”的說(shuō)法也不算誑語(yǔ)。在此筆者推薦一個(gè)非常著(zhù)名的含有大量真實(shí)數據集的網(wǎng)站: http://archive.ics.uci.edu/ml/datasets.html ,本書(shū)常常會(huì )用到其中一些合適的數據集來(lái)評估我們自己實(shí)現的模型。
可以通過(guò)具體的例子來(lái)理解上述概念。比如,我們假設小明是一個(gè)在北京讀了一年書(shū)的學(xué)生,某天他想通過(guò)宿舍窗外的風(fēng)景(能見(jiàn)度、溫度、濕度、路人戴口罩的情況等)來(lái)判斷當天的霧霾情況并據此決定是否戴口罩。此時(shí),他過(guò)去一年的經(jīng)驗就是他擁有的數據集,過(guò)去一年中每一天的情況就是一個(gè)樣本?!澳芤?jiàn)度”、“溫度”、“濕度”、“路人戴口罩的情況”就是四個(gè)特征,而(能見(jiàn)度)“低”、(溫度)“低”、(濕度)“高”、(路人戴口罩的)“多”就是相對應的特征值?,F在小明想了想,決定在腦中建立一個(gè)模型來(lái)幫自己做決策,該模型將利用過(guò)去一年的數據集來(lái)對如今的情況做出“是否戴口罩”的決策。此時(shí)小明可以用過(guò)去一年中8個(gè)月的數據量來(lái)做訓練集、2個(gè)月的量來(lái)做測試集、2個(gè)月的量來(lái)做交叉驗證集,那么小明就需要不斷地思考(訓練模型)下列問(wèn)題:
用訓練集訓練出的模型是怎樣的?
該模型在交叉驗證集上的表現怎么樣?
. 如果足夠好了,那么思考結束(得到最終模型)。
. 如果不夠好,那么根據模型在交叉驗證集上的表現,重新思考(調整模型參數)
最后,小明可能會(huì )在測試集上評估自己剛剛思考后得到的模型的性能,然后根據這個(gè)性能和模型做出的“是否戴口罩”的決策來(lái)綜合考慮自己到底戴不戴口罩。
接下來(lái)說(shuō)明上一小節中提到過(guò)的重要概念:假設空間與泛化能力。泛化能力的含義在上文也有說(shuō)明,為強調起見(jiàn),這里再敘述一遍:
泛化能力針對的其實(shí)是學(xué)習方法,它用于衡量該學(xué)習方法學(xué)習到的模型在整個(gè)樣本空間上的表現。
這一點(diǎn)當然是十分重要的,因為我們拿來(lái)訓練模型的數據終究只是樣本空間的一個(gè)很小的采樣,如果只是過(guò)分專(zhuān)注于它們,就會(huì )出現所謂的“過(guò)擬合”(Over Fitting)的情況。當然,如果過(guò)分罔顧訓練數據,又會(huì )出現“欠擬合”(Under Fitting)??梢杂靡粡垐D來(lái)直觀(guān)地感受過(guò)擬合和欠擬合(如圖1所示,左為欠擬合,右為過(guò)擬合)。

圖1 欠擬合與過(guò)擬合
所以需要“張弛有度”,找到最好的那個(gè)平衡點(diǎn)。統計學(xué)習中的結構風(fēng)險最小化(Structural Risk Minimization,SRM)就是研究這個(gè)的,它和傳統的經(jīng)驗風(fēng)險最小化(Empirical Risk Minimization,ERM)相比,注重于對風(fēng)險上界的最小化,而不是單純地使經(jīng)驗風(fēng)險最小化。它有一個(gè)原則:在使風(fēng)險上界最小的函數子集中挑選出使經(jīng)驗風(fēng)險最小的函數。而這個(gè)函數子集,正是我們之前提到過(guò)的假設空間。
注意:所謂經(jīng)驗風(fēng)險,可以理解為訓練數據集上的風(fēng)險。相對應的,ERM則可以理解為只注重訓練數據集的學(xué)習方法,它的理論基礎是經(jīng)驗風(fēng)險在某種足夠合理的數學(xué)意義上一致收斂于期望風(fēng)險,亦即所謂的“真正的”風(fēng)險。
關(guān)于SRM和ERM的詳細討論會(huì )涉及諸如VC維和正則化的概念,這里不進(jìn)行詳細展開(kāi),但需要有這么一個(gè)直觀(guān)的認識:為了使我們學(xué)習方法訓練出的模型泛化能力足夠好,需要對模型做出一定的“限制”,而這個(gè)“限制”就表現在假設空間的選取上。一個(gè)非常普遍的做法是對模型的復雜度做出一定的懲罰,從而使模型趨于精簡(jiǎn)。這與所謂的“奧卡姆剃刀原理”不謀而合:“如無(wú)必要,勿增實(shí)體”“切勿浪費較多的東西去做,用較少的東西、同樣可以做好事情”。
相比起通過(guò)選取合適的假設空間來(lái)規避過(guò)擬合,進(jìn)行交叉驗證(Cross Validation)則可以讓我們知道過(guò)擬合的程度,從而幫助我們選擇合適的模型。常見(jiàn)的交叉驗證有以下三種。
S-fold Cross Validation:中文可翻譯成S折交叉驗證,它是應用最多的一種方法,其方法大致如下。
. 將數據分成S份:D={D_1,D_2,…,D_S},一共做S次試驗。
. 在第i次試驗中,使用D-D_i作為訓練集,D_i作為測試集對模型進(jìn)行訓練和評測。
. 最終選擇平均測試誤差最小的模型。
留一交叉驗證(Leave-one-out Cross Validation):這是S折交叉驗證的特殊情況,此時(shí)S=N。
簡(jiǎn)易交叉驗證:這種實(shí)現起來(lái)最簡(jiǎn)單,也是本書(shū)(在進(jìn)行交叉驗證時(shí))所采用的方法。它簡(jiǎn)單地將數據進(jìn)行隨機分組,最后達到訓練集約占原數據70%的程度(這個(gè)比例可以視情況改變),選擇模型時(shí)使用測試誤差作為標準。
機器學(xué)習的重要性
道理說(shuō)了不少,但到底為什么要學(xué)機器學(xué)習,機器學(xué)習的重要性又在哪里呢?事實(shí)上,回顧歷史可以發(fā)現,人類(lèi)的發(fā)展通常伴隨著(zhù)簡(jiǎn)單體力勞動(dòng)向復雜腦力勞動(dòng)的過(guò)渡。過(guò)去的工作基本上都有著(zhù)明確的定義,告訴你這一步怎么做、下一步再怎么做。而如今這一類(lèi)的工作已經(jīng)越來(lái)越少,取而代之的是更為寬泛模糊的、概念性的東西,比如說(shuō)“將本季度的產(chǎn)品推向最合適的市場(chǎng),在最大化期望利潤的同時(shí),盡量做到風(fēng)險最小化”這種需求。想要完成好這樣的任務(wù),需要獲取相應的數據;雖說(shuō)網(wǎng)絡(luò )的存在讓我們能夠得到數之不盡的數據,然而從這些數據中獲得信息與知識卻不是一項簡(jiǎn)單的工作。我們當然可以人工地、仔細地逐項甄選,但這樣顯然就又回到了最初的原點(diǎn)。機器學(xué)習這門(mén)技術(shù),可以說(shuō)正因此應運而生。
單單抽象地說(shuō)一大堆空話(huà)可能會(huì )讓人頭昏腦漲,我們就舉一舉機器學(xué)習具體的應用范圍,從中大概能夠比較直觀(guān)地看出機器學(xué)習的強大與重要。
發(fā)展到如今,機器學(xué)習的“爪牙”可謂已經(jīng)伸展到了各個(gè)角落、包括但不限于:
機器視覺(jué),也就是最近機器學(xué)習里很火熱的深度學(xué)習的一種應用;
語(yǔ)音識別,也就是微軟Cortana背后的核心技術(shù);
數據挖掘,也就是耳熟能詳的大數據相關(guān)的領(lǐng)域;
統計學(xué)習,也就是本書(shū)講解的主要范圍之一,有許許多多著(zhù)名的算法(比如支持向量機SVM)都源于統計學(xué)習(但是統計學(xué)習還是和機器學(xué)習有區別的;簡(jiǎn)單地說(shuō),統計學(xué)習偏數學(xué)而機器學(xué)習偏實(shí)踐)。
機器學(xué)習還能夠進(jìn)行模式識別、自然語(yǔ)言處理,等等,之前提到過(guò)的圍棋界的Master和最新人工智能在德州撲克上的表現亦無(wú)不呈現著(zhù)機器學(xué)習強大的潛力。一言以蔽之,機器學(xué)習是當今的熱點(diǎn),雖說(shuō)不能保證它的熱度能100%地一直延續下去,至少筆者認為、它能在相當長(cháng)的一段時(shí)間內保持強大的生命力。
人生苦短,我用Python
上一節大概地介紹了機器學(xué)習的各種概念,這一節我們主要講講腳本語(yǔ)言Python相關(guān)的一些東西。題目是在Python界流傳甚廣的“諺語(yǔ)”,它講述了Python強大的功能與易于上手的特性。
為何選擇Python
援引開(kāi)源運動(dòng)的領(lǐng)袖人物Eric Raymond的說(shuō)法:“Python語(yǔ)言非常干凈,設計優(yōu)雅,具有出色的模塊化特性。其最出色的地方在于,鼓勵清晰易讀的代碼,特別適合以漸進(jìn)開(kāi)發(fā)的方式構造項目”。Python的可讀性使得即使是剛學(xué)不久的人也能看懂大部分的代碼,Python龐大的社區和大量的開(kāi)發(fā)文檔更是使得初學(xué)者能夠快速地實(shí)現許許多多令人驚嘆的功能。對于Python的程序,人們甚至有時(shí)會(huì )戲稱(chēng)其為“可執行的偽代碼(executable pseudo-code)”,以突顯它的清晰性和可讀性。
Python的強大是毋庸置疑的,上文提到的Eric Raymond甚至稱(chēng)其“過(guò)于強大了”。與之相對應的,就是Python的速度比較慢。然而比起Python開(kāi)發(fā)環(huán)境提供的海量高級數據結構(如列表、元組、字典、集合等)和數之不盡的第三方庫,再加上高速的CPU和近代發(fā)展起來(lái)的GPU編程,速度的問(wèn)題就顯得沒(méi)那么尖銳了。況且Python還能通過(guò)各種途徑使用C / C++代碼來(lái)編寫(xiě)核心代碼,其強大的“膠水”功能使其速度(在程序員能力允許的情況下)和純粹的C / C++相比已經(jīng)相去不遠。一個(gè)典型的例子,也是我們會(huì )在本書(shū)常常運用到的Python中Numpy這個(gè)第三方庫。編寫(xiě)它的語(yǔ)言正是底層語(yǔ)言(C和Fortran),其支持向量、矩陣操作的特性和優(yōu)異的速度,使得Python在科學(xué)計算這一領(lǐng)域大放異彩。
注意:Python及本書(shū)用到的兩個(gè)非常優(yōu)異的第三方庫—Numpy和TensorFlow的簡(jiǎn)要教程我們會(huì )作為附錄章節放在本書(shū)的最后,建議有需要的讀者先閱讀相應部分。
Python 在機器學(xué)習領(lǐng)域的優(yōu)勢
雖然在上一小節敘述了Python的種種好處,但不可否認的是,確實(shí)存在諸如MATLAB和Mathematica這樣的高級程序語(yǔ)言。它們對機器學(xué)習的支持也不錯,MATLAB甚至還自帶許多機器學(xué)習的應用。但是作為一個(gè)問(wèn)心無(wú)愧的程序員,我們還是需要提倡支持正版,而MATLAB的正版軟件需要花費數千美元。與之相對,由于Python是開(kāi)源項目,幾乎所有必要的組件都是完全免費的。
之前也提到過(guò)Python的速度問(wèn)題,但是更快更底層的語(yǔ)言,比如C和C++,若使用它們來(lái)學(xué)習機器學(xué)習,會(huì )不可避免地引發(fā)這么一個(gè)問(wèn)題:即使是實(shí)現一個(gè)非常簡(jiǎn)單的功能,也需要進(jìn)行大量的編寫(xiě)和調試的過(guò)程;在這期間,程序員很有可能忘掉學(xué)習機器學(xué)習的初衷而迷失在代碼的海洋中。筆者曾經(jīng)嘗試過(guò)將Python上的神經(jīng)網(wǎng)絡(luò )框架移植到C++上,這之間的折騰至今難忘。
此外,筆者認為、使用Python來(lái)學(xué)習機器學(xué)習是和“不要過(guò)早優(yōu)化”這句編程界的金句有著(zhù)異曲同工之妙的。Python(幾乎)唯一的缺陷—速度,在初期進(jìn)行快速檢驗算法、思想正誤及開(kāi)發(fā)工作時(shí),其實(shí)基本上不是重要問(wèn)題。其中的道理是顯而易見(jiàn)的:如果解決問(wèn)題的思想存在問(wèn)題,那么即使拼命去提高程序的運行效率,也只能使問(wèn)題越來(lái)越大而已。這種時(shí)候,先使用Python進(jìn)行快速實(shí)現,有必要時(shí)再用底層代碼重寫(xiě)核心代碼,從各方面來(lái)說(shuō)都是一個(gè)更好的選擇。
關(guān)于A(yíng)naconda
Python的強大有相當大一部分體現在它那浩如煙海的第三方庫。在使用Python實(shí)現一個(gè)復雜功能時(shí),如果沒(méi)有特殊的需求,我們通常會(huì )先搜索Google有沒(méi)有現成的第三方庫,然后會(huì )搜索是否有相關(guān)聯(lián)的第三方庫,最后才會(huì )考慮自己重頭實(shí)現。
第三方庫是如此之多,從中挑選出心儀而合適的并非易事。幸運的是,就連這一點(diǎn)也有第三方軟件進(jìn)行了支持,那就是在Python科學(xué)計算領(lǐng)域非常出名的Anaconda。這是一個(gè)完全免費的軟件,經(jīng)常會(huì )進(jìn)行各種更新;最重要的是,它把幾乎所有常用且優(yōu)異的科學(xué)計算庫都集成在了一起。換句話(huà)說(shuō),只要你安裝了Anaconda,就意味著(zhù)擁有了一個(gè)完善精致的機器學(xué)習環(huán)境,基本上無(wú)須自己把要用到的庫一個(gè)一個(gè)通過(guò)命令行來(lái)安裝。
第一個(gè)機器學(xué)習樣例
作為本章的總結,我們來(lái)運用Python解決一個(gè)實(shí)際問(wèn)題,以便對機器學(xué)習有一個(gè)具體的感受。由于該樣例只是為了提供直觀(guān)感受,我們就拿比較有名的一個(gè)小問(wèn)題來(lái)進(jìn)行闡述。俗話(huà)說(shuō):“麻雀雖小,五臟俱全”,我們完全可以通過(guò)這個(gè)樣例來(lái)對機器學(xué)習的一般性步驟進(jìn)行一個(gè)大致的認知。
該問(wèn)題來(lái)自Coursera上的斯坦福大學(xué)機器學(xué)習課程,其敘述如下:現有47個(gè)房子的面積和價(jià)格,需要建立一個(gè)模型對新的房?jì)r(jià)進(jìn)行預測。稍微翻譯問(wèn)題,可以得知:
輸入數據只有一維,亦即房子的面積。
目標數據也只有一維,亦即房子的價(jià)格。
需要做的,就是根據已知的房子的面積和價(jià)格的關(guān)系進(jìn)行機器學(xué)習。
下面我們就來(lái)一步步地進(jìn)行操作。
獲取與處理數據
原始數據集的前10個(gè)樣本如表1.1所示,這里房子面積和房子價(jià)格的單位可以隨意定奪,因為它們不會(huì )對結果造成影響。
表1.1 房?jì)r(jià)數據集

完整的數據集可以參見(jiàn) https://github.com/carefree0910/MachineLearning/blob/master/ _Data/prices.txt 。雖然該數據集比較簡(jiǎn)單,但可以看到其中的數字都相當大。保留它原始形式確實(shí)有可能是有必要的,但一般而言,我們應該對它做簡(jiǎn)單的處理以期望降低問(wèn)題的復雜度。在這個(gè)例子里,采取常用的將輸入數據標準化的做法,其數學(xué)公式為:

代碼1-1 第一個(gè)機器學(xué)習樣例:a_FirstExampleRegression.py
01 # 導入需要用到的庫
02 import numpy as np
03 import matplotlib.pyplot as plt
04
05 # 定義存儲輸入數據(x)和目標數據(y)的數組
06 x, y = [], []
07 # 遍歷數據集,變量sample對應的正是一個(gè)個(gè)樣本
08 for sample in open("../_Data/prices.txt", "r"):
09 # 由于數據是用逗號隔開(kāi)的,所以調用Python中的split方法并將逗號作為參數傳入
10 _x, _y = sample.split(",")
11 # 將字符串數據轉化為浮點(diǎn)數
12 x.append(float(_x))
13 y.append(float(_y))
14 # 讀取完數據后,將它們轉化為Numpy數組以方便進(jìn)一步的處理
15 x, y = np.array(x), np.array(y)
16 # 標準化
17 x = (x - x.mean()) / x.std()
18 # 將原始數據以散點(diǎn)圖的形式畫(huà)出
19 plt.figure()
20 plt.scatter(x, y, c="g", s=6)
21 plt.show()
上面這段代碼的運行結果如圖2所示。

圖2 預處理后的數據散點(diǎn)圖
這里橫軸是標準化后的房子面積,縱軸是房子價(jià)格。以上我們已經(jīng)比較好地完成了機器學(xué)習任務(wù)的第一步:數據預處理。
選擇與訓練模型
在弄好數據之后,下一步就要開(kāi)始選擇相應的學(xué)習方法和模型了。幸運的是,通過(guò)可視化原始數據,可以非常直觀(guān)地感受到:很有可能通過(guò)線(xiàn)性回歸(Linear Regression)中的多項式擬合來(lái)得到一個(gè)不錯的結果。其模型的數學(xué)表達式如下。
注意:用多項式擬合散點(diǎn)只是線(xiàn)性回歸的很小的一部分,但是它的直觀(guān)意義比較明顯??紤]到問(wèn)題比較簡(jiǎn)單,我們才選用了多項式擬合。線(xiàn)性回歸的詳細討論超出了本書(shū)的范圍,這里不再贅述。

其中f(x|p;n)就是我們的模型,p、n都是模型的參數,其中p是多項式f的各個(gè)系數,n是多項式的次數。L(p;n)則是模型的損失函數,這里我們采用了常見(jiàn)的平方損失函數,也就是所謂的歐氏距離(或說(shuō)向量的二范數)。x、y則分別是輸入向量和目標向量;在我們這個(gè)樣例中,x、y這兩個(gè)向量都是47維的向量,分別由47個(gè)不同的房子面積、房子價(jià)格所構成。
在確定好模型后,就可以開(kāi)始編寫(xiě)代碼來(lái)進(jìn)行訓練了。對于大多數機器學(xué)習算法,所謂的訓練正是最小化某個(gè)損失函數的過(guò)程,這個(gè)多項式擬合的模型也不例外:我們的目的就是讓上面定義的L(p;n)最小。在數理統計領(lǐng)域里有專(zhuān)門(mén)的理論研究這種回歸問(wèn)題,其中比較有名的正規方程更是直接給出了一個(gè)簡(jiǎn)單的解的通式。不過(guò)由于有Numpy的存在,這個(gè)訓練過(guò)程甚至變得還要更加簡(jiǎn)單一些。
22 # 在(-2,4)這個(gè)區間上取100個(gè)點(diǎn)作為畫(huà)圖的基礎
23 x0 = np.linspace(-2, 4, 100)
24 # 利用Numpy的函數定義訓練并返回多項式回歸模型的函數
25 # deg參數代表著(zhù)模型參數中的n,亦即模型中多項式的次數
26 # 返回的模型能夠根據輸入的x(默認是x0),返回相對應的預測的y
27 def get_model(deg):
28 return lambda input_x=x0: np.polyval(np.polyfit(x, y, deg), input_x)
這里需要解釋Numpy里面帶的兩個(gè)函數:polyfit和polyval的用法。
polyfit(x, y, deg):該函數會(huì )返回使得上述

(注:該公式中的x和y就是輸入的x和y)最小的參數p,亦即多項式的各項系數。換句話(huà)說(shuō),該函數就是模型的訓練函數。
polyval(p, x):根據多項式的各項系數p和多項式中x的值,返回多項式的值y。
29 # 根據參數n、輸入的x、y返回相對應的損失
30 def get_cost(deg, input_x, input_y):
31 return 0.5 * ((get_model(deg)(input_x) - input_y) ** 2).sum()
32 # 定義測試參數集并根據它進(jìn)行各種實(shí)驗
33 test_set = (1, 4, 10)
34 for d in test_set:
35 # 輸出相應的損失
36 print(get_cost(d, x, y))
所得的結果是:當n=1,4,10時(shí),損失的頭兩位數字分別為96、94和75。這么看來(lái)似乎是n=10優(yōu)于n=4,而n=1最差,但從圖3可以看出,似乎直接選擇n=1作為模型的參數才是最好的選擇。這里矛盾的來(lái)源正是前文所提到過(guò)的過(guò)擬合情況。

圖3 線(xiàn)性回歸的可視化
那么,怎么最直觀(guān)地了解是否出現過(guò)擬合了呢?當然還是畫(huà)圖了。
37 # 畫(huà)出相應的圖像
38 plt.scatter(x, y, c="g", s=20)
39 for d in test_set:
40 plt.plot(x0, get_model(d)(), label="degree = {}".format(d))
41 # 將橫軸、縱軸的范圍分別限制在(-2,4)、(〖10〗^5,8×〖10〗^5)
42 plt.xlim(-2, 4)
43 plt.ylim(1e5, 8e5)
44 # 調用legend方法使曲線(xiàn)對應的label正確顯示
45 plt.legend()
46 plt.show()
上面這段代碼的運行結果如圖3所示。
其中,三條線(xiàn)分別代表n=1、n=4、n=10的情況(圖1.10的右上角亦有說(shuō)明)??梢钥闯?,從n=4開(kāi)始模型就已經(jīng)開(kāi)始出現過(guò)擬合現象了,到n=10時(shí)模型已經(jīng)變得非常不合理。
至此,可以說(shuō)這個(gè)問(wèn)題就已經(jīng)基本上解決了。在這個(gè)樣例里面,除了交叉驗證,我們涵蓋了機器學(xué)習中的大部分主要步驟(之所以沒(méi)有進(jìn)行交叉驗證是因為數據太少了……)。代碼部分加起來(lái)總共40~50行,應該算是一個(gè)比較合適的長(cháng)度。希望大家能夠通過(guò)這個(gè)樣例對機器學(xué)習有個(gè)大概的了解,也希望它能引起大家對機器學(xué)習的興趣。
本章小結
與傳統的計算機程序不同,機器學(xué)習是面向數據的算法,能夠從數據中獲得信息。它符合新時(shí)代腦力勞動(dòng)代替體力勞動(dòng)的趨勢,是富有生命力的領(lǐng)域。
Python是一門(mén)優(yōu)異的語(yǔ)言,代碼清晰可讀、功能廣泛強大。其最大弱點(diǎn)—速度問(wèn)題也可以通過(guò)很多不太困難的方法彌補。
Anaconda是Python的一個(gè)很好的集成環(huán)境,它能讓我們免于人工地安裝大量科學(xué)計算所需要的第三方庫。
雖說(shuō)機器學(xué)習算法很多,但通常而言,進(jìn)行機器學(xué)習的過(guò)程會(huì )包含以下三步:
. 獲取與處理數據;
. 選擇與訓練模型;
. 評估與可視化結果。
評論