【白话机器学习】算法理论+实战之K近邻算法
作者1. 寫在前面
如果想從事數據挖掘或者機器學習的工作,掌握常用的機器學習算法是非常有必要的,在這簡單的先捋一捋, 常見的機器學習算法:
監督學習算法:邏輯回歸,線性回歸,決策樹,樸素貝葉斯,K近鄰,支持向量機,集成算法Adaboost等
無監督算法:聚類,降維,關聯規則, PageRank等
為了詳細的理解這些原理,曾經看過西瓜書,統計學習方法,機器學習實戰等書,也聽過一些機器學習的課程,但總感覺話語里比較深奧,讀起來沒有耐心,并且理論到處有,而實戰最重要, 所以在這里想用最淺顯易懂的語言寫一個白話機器學習算法理論+實戰系列。
個人認為,理解算法背后的idea和使用,要比看懂它的數學推導更加重要。idea會讓你有一個直觀的感受,從而明白算法的合理性,數學推導只是將這種合理性用更加嚴謹的語言表達出來而已,打個比方,一個梨很甜,用數學的語言可以表述為糖分含量90%,但只有親自咬一口,你才能真正感覺到這個梨有多甜,也才能真正理解數學上的90%的糖分究竟是怎么樣的。如果算法是個梨,本文的首要目的就是先帶領大家咬一口。另外還有下面幾個目的:
檢驗自己對算法的理解程度,對算法理論做一個小總結
能開心的學習這些算法的核心思想, 找到學習這些算法的興趣,為深入的學習這些算法打一個基礎。
每一節課的理論都會放一個實戰案例,能夠真正的做到學以致用,既可以鍛煉編程能力,又可以加深算法理論的把握程度。
也想把之前所有的筆記和參考放在一塊,方便以后查看時的方便。
學習算法的過程,獲得的不應該只有算法理論,還應該有樂趣和解決實際問題的能力!
今天是白話機器學習算法理論+實戰的第七篇 之K近鄰算法,這應該是諸多機器學習算法中最容易理解或者操作的一個算法了吧,但是別看它簡單,但是很實用的。因為它既可以用來做分類,也可以用來做回歸,易于實現,無需估計參數,無需訓練。當然也有一些缺點,比如計算量比較大,分析速度較慢,樣本不均衡問題不太好用,也無法給出數據的內在含義,是一種懶散學習法。?當然,KNN也可以用于推薦算法,雖然現在很多推薦系統的算法會使用 TD-IDF、協同過濾、Apriori 算法,不過針對數據量不大的情況下,采用 KNN 作為推薦算法也是可行的。
這么簡單,并且作用還很大的算法我們應該立刻把握住,所以這節課,我們就來學習KNN的計算原理,并且用KNN來實現一個實戰案例,我們開始吧。
大綱如下:
如果根據打斗和接吻的次數來劃分電影的類型?(我們引出KNN)
KNN的工作原理(近朱者赤,近墨者黑)
如何用KNN做回歸?(簡單介紹一下)
用KNN實現書寫數字的識別(手寫數字識別可是深度學習界的Hellow world,在這里先帶你認識一下)
OK, let's go!
2. K近鄰算法?我們依然從一個電影的分類開始
KNN 的英文叫 K-Nearest Neighbor,應該算是數據挖掘算法中最簡單的一種。雖然簡單,但是很實用, 老規矩,既然白話,還是先從例子開始,讓你再次體會算法來源生活。
假設有下面這幾部電影,我給你統計了電影中的打斗次數、接吻次數, 當然其他的也可以統計,但是沒顧得上,如下表所示:我們很容易理解《戰狼》《紅海行動》《碟中諜 6》是動作片,《前任 3》《春嬌救志明》《泰坦尼克號》是愛情片。(如果不理解,自己去看電影吧,還能找個放松的接口)
但是假設我目前有一部新電影, 叫做《速度與激情9》,我也統計了一下打斗次數和接吻次數, 這應該劃分到愛情還是動作呢??你會說,這還不簡單,我直接就知道是范·迪塞爾男神主演的動作片。但是機器不知道啊, 機器可不管你男神還是女神,你得讓機器明白這種分類規則,讓機器去分類啊, 那你應該用什么方法呢?
我們可以把打斗次數看成 X 軸,接吻次數看成 Y 軸,然后在二維的坐標軸上,對這幾部電影進行標記,如下圖所示。對于《速度與激情》,坐標為 (x,y),我們需要看下離電影 A 最近的都有哪些電影,這些電影中的大多數屬于哪個分類,那么電影 A 就屬于哪個分類。
嗯嗯,這才是你教機器做的東西呢,其實這就是K近鄰了啊,好理解吧,就是新的一部電影過來了,看他和哪個鄰居挨得近,鄰居是哪一類,它就是哪一類啦。
但是,具體還有些細節需要我們知道,下面就真的看看原理吧。
3. K近鄰的工作原理
簡單的一句話就可以說明KNN的工作原理“近朱者赤,近墨者黑”。
K近鄰算法的過程是給定一個訓練數據集,對新輸入的實例,在訓練數據集中找到與該實例最鄰近的K個實例, 這K個實例的多數屬于某個類, 就把該輸入實例分為這個類。看上面這個圖:有兩類不同的樣本數據, 分別用藍色的正方形和紅色的三角形表示, 而正中間的綠色圓表示待分類數據 下面我們根據K近鄰思想給綠色圓點分類:
如果k=3, 綠色圓點的最近鄰的3個點是2個紅色三角和1個藍色小正方形, 少數服從多數, 基于統計的方法,判定綠色的這個待分類點屬于紅色三角形一類
如果k=5, 綠色圓點的最近鄰的5個點是2個紅色三角和3個藍色小正方形,還是少數服從多數, 基于統計的方法,判定這個待分類點屬于藍色的正方形一類
從上面例子可以看出, k近鄰算法的思想非常簡單, 對新來的點如何歸類?【只要找到離它最近的k個實例, 哪個類別最多即可】
所以,KNN的工作原理大致分為三步:
計算待分類物體與其他物體之間的距離;
統計距離最近的 K 個鄰居;
對于 K 個最近的鄰居,它們屬于哪個分類最多,待分類物體就屬于哪一類。
但是下面就引出了2個問題:
怎么度量距離,鎖定最近的K個鄰居?(不同的度量方法可能導致K個鄰居不同)
怎么確定這個K值(K值不同,最后的結果可能不同,像上面那樣)
下面就來破了這倆問題。
首先先說第二個問題, K值如何選擇?
你能看出整個 KNN 的分類過程,K 值的選擇還是很重要的
如果 K 值比較小,就相當于未分類物體與它的鄰居非常接近才行。這樣產生的一個問題就是,如果鄰居點是個噪聲點,那么未分類物體的分類也會產生誤差,這樣 KNN 分類就會產生過擬合。就比如下面這個情況(K=1, 綠色點為分類點):上面這種情況K=1,直接把綠色點分到橙色那一類了,但是橙色點是個噪聲點啊。
如果 K 值比較大,相當于距離過遠的點也會對未知物體的分類產生影響,雖然這種情況的好處是魯棒性強,但是不足也很明顯,會產生欠擬合情況,也就是沒有把未分類物體真正分類出來。就比如下面這個情況:上面這種情況K=N, 那么無論輸入實例是什么,都將簡單的預測它屬于在訓練實例中最多的類,這時相當于壓根沒有訓練模型, 完全忽略訓練數據實例中大量的有用信息,是不可取的。
那么K值如何確定呢??呵呵,不好意思,這個值沒法事先而定,需要不斷的實踐,一次次的嘗試。工程上,我們一般采用交叉驗證的方式選取K值。
★交叉驗證的思路就是,把樣本集中的大部分樣本作為訓練集,剩余的小部分樣本用于預測,來驗證分類模型的準確性。所以在 KNN 算法中,我們一般會把 K 值選取在較小的范圍內,同時在驗證集上準確率最高的那一個最終確定作為 K 值。
”再說第二個問題, 距離如何計算?
在 KNN 算法中,還有一個重要的計算就是關于距離的度量。兩個樣本點之間的距離代表了這兩個樣本之間的相似度。距離越大,差異性越大;距離越小,相似度越大。
度量距離有下面的五種方式:
歐式距離歐氏距離是我們最常用的距離公式,也叫做歐幾里得距離。在二維空間中,兩點的歐式距離就是:同理,我們也可以求得兩點在n維空間中的距離:
曼哈頓距離曼哈頓距離在幾何空間中用的比較多。曼哈頓距離等于兩個點在坐標系上絕對軸距總和。用公式表示就是:以下圖為例,綠色的直線代表兩點之間的歐式距離,而紅色和黃色的線為兩點的曼哈頓距離。感受一下兩者的不同:
閔可夫斯基距離閔可夫斯基距離不是一個距離,而是一組距離的定義。對于 n 維空間中的兩個點 x(x1,x2,…,xn) 和 y(y1,y2,…,yn) , x 和 y 兩點之間的閔可夫斯基距離為:其中 p 代表空間的維數,當 p=1 時,就是曼哈頓距離;當 p=2 時,就是歐氏距離;當 p→∞時,就是切比雪夫距離。下面給出了不同的p值情況下,與原來Lp距離為1的點的圖形,直觀感受一下p值的不同,距離的變化:
切比雪夫距離那么切比雪夫距離怎么計算呢?二個點之間的切比雪夫距離就是這兩個點坐標數值差的絕對值的最大值,用數學表示就是:max(|x1-y1|,|x2-y2|)。
余弦距離余弦距離實際上計算的是兩個向量的夾角,是在方向上計算兩者之間的差異,對絕對數值不敏感。在興趣相關性比較上,角度關系比距離的絕對值更重要,因此余弦距離可以用于衡量用戶對內容興趣的區分度。比如我們用搜索引擎搜索某個關鍵詞,它還會給你推薦其他的相關搜索,這些推薦的關鍵詞就是采用余弦距離計算得出的。所以搜索推薦中,余弦距離會常用。
其中前三種距離是 KNN 中最常用的距離。
好了,解答了這個問題,也就弄明白了KNN的原理了吧。是不是想迫不及待的實戰了?先別慌,還有點東西得介紹一下。
4. KD樹
關于KD樹的知識,我在這不會介紹太多,涉及的數學公式有點多,但得介紹一下這個東西,我們不需要對 KD 樹的數學原理了解太多,你只需要知道它是一個二叉樹的數據結構,方便存儲 K 維空間的數據就可以了。而且在 sklearn 中,我們直接可以調用 KD 樹,很方便。
其實從上文你也能看出來,KNN 的計算過程是大量計算樣本點之間的距離。為了減少計算距離次數,提升 KNN 的搜索效率,人們提出了 KD 樹(K-Dimensional 的縮寫)。KD 樹是對數據點在 K 維空間中劃分的一種數據結構。在 KD 樹的構造中,每個節點都是 k 維數值點的二叉樹。既然是二叉樹,就可以采用二叉樹的增刪改查操作,這樣就大大提升了搜索效率。
嗯,關于KD樹,就說這么多,在實戰之前,再說一下,KNN如何做回歸吧,畢竟后面的實戰是個分類任務,之前既然說了KNN能夠做回歸,你可別說我撒謊。
5. KNN做回歸
KNN 不僅可以做分類,還可以做回歸。首先講下什么是回歸。在開頭電影這個案例中,如果想要對未知電影進行類型劃分,這是一個分類問題。首先看一下要分類的未知電影,離它最近的 K 部電影大多數屬于哪個分類,這部電影就屬于哪個分類。
如果是一部新電影,已知它是愛情片,想要知道它的打斗次數、接吻次數可能是多少,這就是一個回歸問題。
那KNN如何做回歸呢?
對于一個新電影 X(就假設《速度與激情》,我男神演的動作片吧)。我們要預測它的某個屬性值,比如打斗次數,具體特征屬性和數值如下所示。此時,我們會先計算待測點(新電影 X)到已知點的距離,選擇距離最近的 K 個點。假設 K=3,此時最近的 3 個點(電影)分別是《戰狼》,《紅海行動》和《碟中諜 6》,那么它的打斗次數就是這 3 個點的該屬性值的平均值,即 (100+95+105)/3=100 次。
好了,這就是KNN了,沒有什么特別要交代的了,下面實戰吧。
6. KNN:如何對手寫數字進行識別?
還是老規矩,先看看通過sklearn用這個工具。
6.1 如何在sklearn中使用KNN?
在 Python 的 sklearn 工具包中有 KNN 算法。KNN 既可以做分類器,也可以做回歸。
如果是做分類,你需要引用:from sklearn.neihbors import KNeighborsClassifier
如果是回歸, 需要引用:from sklearn.neighbors import KNeighborsRegressor
如何在sklearn中創建KNN分類器呢?
★KNeighborsClassifier(n_neighbors=5, weights='uniform', algorithm='auto', leaf_size=30), 看一下這幾個參數:
n_neighbors:即 KNN 中的 K 值,代表的是鄰居的數量。K 值如果比較小,會造成過擬合。如果 K 值比較大,無法將未知物體分類出來。一般我們使用默認值 5。
weights:是用來確定鄰居的權重,有三種方式:
weights=uniform,代表所有鄰居的權重相同;
weights=distance,代表權重是距離的倒數,即與距離成反比;
自定義函數,你可以自定義不同距離所對應的權重。大部分情況下不需要自己定義函數。
algorithm:用來規定計算鄰居的方法,它有四種方式:
algorithm=auto,根據數據的情況自動選擇適合的算法,默認情況選擇 auto;
algorithm=kd_tree,也叫作 KD 樹,是多維空間的數據結構,方便對關鍵數據進行檢索,不過 KD 樹適用于維度少的情況,一般維數不超過 20,如果維數大于 20 之后,效率反而會下降;
algorithm=ball_tree,也叫作球樹,它和 KD 樹一樣都是多維空間的數據結果,不同于 KD 樹,球樹更適用于維度大的情況;
algorithm=brute,也叫作暴力搜索,它和 KD 樹不同的地方是在于采用的是線性掃描,而不是通過構造樹結構進行快速檢索。當訓練集大的時候,效率很低。
4.leaf_size:代表構造 KD 樹或球樹時的葉子數,默認是 30,調整 leaf_size 會影響到樹的構造和搜索速度。
創建完 KNN 分類器之后,我們就可以輸入訓練集對它進行訓練,這里我們使用 fit() 函數,傳入訓練集中的樣本特征矩陣和分類標識,會自動得到訓練好的 KNN 分類器。然后可以使用 predict() 函數來對結果進行預測,這里傳入測試集的特征矩陣,可以得到測試集的預測分類結果。
6.2 如何在sklearn中使用KNN?
手寫數字數據集是個非常有名的用于圖像識別的數據集。數字識別的過程就是將這些圖片與分類結果 0-9 一一對應起來。完整的手寫數字數據集 MNIST 里面包括了 60000 個訓練樣本,以及 10000 個測試樣本。如果你學習深度學習的話,MNIST 基本上是你接觸的第一個數據集。
我們用 sklearn 自帶的手寫數字數據集做 KNN 分類,你可以把這個數據集理解成一個簡版的 MNIST 數據集,它只包括了 1797 幅數字圖像,每幅圖像大小是 8*8 像素。
還是先劃分一下流程:整個訓練過程基本上都會包括三個階段:
數據加載:我們可以直接從 sklearn 中加載自帶的手寫數字數據集;
準備階段:在這個階段中,我們需要對數據集有個初步的了解,比如樣本的個數、圖像長什么樣、識別結果是怎樣的。你可以通過可視化的方式來查看圖像的呈現。通過數據規范化可以讓數據都在同一個數量級的維度。另外,因為訓練集是圖像,每幅圖像是個 8*8 的矩陣,我們不需要對它進行特征選擇,將全部的圖像數據作為特征值矩陣即可;
分類階段:通過訓練可以得到分類器,然后用測試集進行準確率的計算。
好了,下面我們開始吧:
首先,導入包,這里導入了很多算法包,是因為既然這次項目比較簡單,都那么我們就對比一下之前的幾種方法,看看效果。
加載數據集并探索
我們對原始數據集中的第一幅進行數據可視化,可以看到圖像是個 8*8 的像素矩陣,上面這幅圖像是一個“0”,從訓練集的分類標注中我們也可以看到分類標注為“0”。
分割數據集并規范化 klearn 自帶的手寫數字數據集一共包括了 1797 個樣本,每幅圖像都是 8*8 像素的矩陣。因為并沒有專門的測試集,所以我們需要對數據集做劃分,劃分成訓練集和測試集。因為 KNN 算法和距離定義相關,我們需要對數據進行規范化處理,采用 Z-Score 規范化,代碼如下:
這里之所以用了0-1歸一化,是因為多項式樸素貝葉斯分類這個模型,傳入的數據不能有負數。因為 Z-Score 會將數值規范化為一個標準的正態分布,即均值為 0,方差為 1,數值會包含負數。因此我們需要采用 Min-Max 規范化,將數據規范化到[0,1]范圍內。
建立模型,并進行比較:這里構造五個分類器, 分別是K近鄰,SVM, 多項式樸素貝葉斯, 決策樹模型, AdaBoost模型。并分別看看他們的效果
PS: 注意, 在做多項式樸素貝葉斯的時候,傳入的數據不能有負數,由于Z-Score會將值歸一化一個標準的正態分布,會包含負數,所以不能有這個,采用MinMax歸一化到[0-1]范圍即可。
輸出結果:你能看出來 KNN 的準確率還是不錯的,和 SVM 不相上下。并且竟然比AdaBoost效果都要好,而讓我納悶的是決策樹和AdaBoost怎么效果這么差,不可思議。后來我發現了,原來是樣本數量的問題,我們最多數據集才1000多照片,數量太少了,AdaBoost的作用發揮不出來,所以我記性了數據的擴增,復制了三遍原來的數據:
data1 = np.vstack((data, data, data)) target1 = np.hstack((target, target, target))變成了5000多張數據,然后再進行測試,結果就是AdaBoost和tree的效果提升了,甚至可以和SVM效果媲美了:
7. 總結
好了,到這里就基本結束了,KNN不算太難,所以篇幅可能少了一些,但KNN還是挺好用的一個算法, 下面簡單的總結一下:
首先,就是通過電影分類的例子,體會了一下什么是KNN,然后寫了一下KNN的工作原理,主要問題就是K值怎么選取?距離怎么衡量?
然后,兩個小插曲KD樹和KNN做回歸。
最后就是手寫數字識別的實戰,這個當然也比較簡單,然后就是對比了這幾個算法的效果。KNN在里面表現不錯,SVM算法處理這種問題還是不錯,樣本較多的時候,集成的方式還是占據一定的優勢。在這個過程中你應該對數據探索、數據可視化、數據規范化、模型訓練和結果評估的使用過程有了一定的體會。在數據量不大的情況下,使用 sklearn 還是方便的。
參考:
http://note.youdao.com/noteshare?id=e753340eab85bb24b34ee45812b47ea1&sub=FE026DF879B7400F8DB196C8DE931238
http://note.youdao.com/noteshare?id=7ac08cc7ebdde0854ac3281923aeb5c0&sub=8072C6A29A3D4B00BB5316FFF676C1E2
http://note.youdao.com/noteshare?id=3e2f7f6d1147058dd4ca93bf03d0c338&sub=1686C728DD194617BDA9DFFF60049421
總結
以上是生活随笔為你收集整理的【白话机器学习】算法理论+实战之K近邻算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从 Google 的一道面试题谈谈数学基
- 下一篇: WSDM Cup 2020 引用意图识别