從今天開始給大家寫機器學習算法,這個東西并不是大多數(shù)人想象的那么高深恢口,也不是說編程的人,搞計算機的人才能學習使用穷躁,醫(yī)學領(lǐng)域耕肩、社會科學領(lǐng)域的研究越來越多運用機器學習的,在我的理解中每個人都應(yīng)該掌握基本的機器學習思想和基本的編程能力问潭。
這個系列的第一篇文章從簡單的分類算法KNN開始:這個算法真的非常的簡單猿诸,簡單到初中生都可以掌握,所以大家一定要有信心:
kNN is arguably the simplest machine learning algorithm. In spite of its simplicity, kNN can provide surprisingly good classification performance, and its simplicity makes it easy to interpret.
KNN算法思想解釋
K最近鄰算法是一種分類算法狡忙,算法思想是在數(shù)據(jù)集中找到與樣本最相似的K個樣本梳虽,如果這K個樣本中的大多數(shù)屬于某一類別,則該樣本也屬于某一類別灾茁。
用例子說明:現(xiàn)在有3個爬行動物窜觉,其中兩個是毒蛇分別叫做:grass snake and the adder是复,一個是蟲子叫做slow worm。這個蟲子和蛇長得挺像竖螃,很多人都會把它們搞混。
現(xiàn)在你參與了一個項目:去調(diào)查一片草地上到底這三種動物到底有多少個逗余,在實地調(diào)查的時候你只能通過動物的身長Body length和攻擊性aggression這兩個指標來對其的類型做出判斷特咆,你和一個動物專家已經(jīng)收集了一些動物數(shù)據(jù)(已經(jīng)有帶標簽的數(shù)據(jù)集了),現(xiàn)在你自己想訓練一個KNN模型對動物進行自動分類录粱。
你收集的數(shù)據(jù)可視化如下圖:
圖中有每個爬行動物在身長和攻擊性兩個維度的值腻格,XX就是待分類的動物的身長和攻擊性,我們現(xiàn)在需要用KNN模型去判斷這三個X分別是哪一類啥繁。
KNN算法是如何做的呢?
對于每一個X菜职,算法會去計算這個X到周圍已知點的距離,比如對最上面的X我們計算距離可以如下(所有的線就代表歐幾里得距離):
距離有了之后我們對距離進行排序:
然后我們?nèi)∏癒個最近距離的點(這個K是自己設(shè)定的旗闽,叫做超參數(shù))酬核,然后這個K個點中類別占比最多的那一類就是我們的X的所屬類別。
我們還是把上面的步驟在例子中給大家走一遍:
我把K分別設(shè)定為1适室,3嫡意,5,就是說我希望我新收集的X的類別和它最鄰近的1捣辆,3蔬螟,5個爬行動物的類別一樣。
那么整個分類過程就如下圖:
從圖中可以看出:當K為1時3個X都被KNN認為是grass snake汽畴,當K為3時3個X被KNN模型分為了3個不同的類別旧巾,當K為5時我們的3個X被分為了2個不同的類別。所以忍些,K會影響KNN算法的表現(xiàn)鲁猩,K不同算法結(jié)果不同。
有沒有人好奇如果我把K設(shè)置為4然后就找了前4個最近的已知樣本坐昙,2個是A類绳匀,兩個是B類,A和B打了個平局炸客,這個時候X到底是哪一類呢疾棵?
這個時候,如果我們的分類問題本身是一個兩分類問題我們一定記住把K設(shè)置為奇數(shù)痹仙,就可以避免平局的出現(xiàn)是尔,如果為多分類問題,算法會直接進行隨機分开仰,因為實際操作中K個鄰居中有類別數(shù)量等同的情形還是比較少的拟枚,對整體結(jié)果不會產(chǎn)生實際影響薪铜。
最后給大家回顧一下KNN的實現(xiàn)過程:
- 構(gòu)建一個已經(jīng)分類好的數(shù)據(jù)集。
- 計算一個新樣本與數(shù)據(jù)集中所有數(shù)據(jù)的距離恩溅。
- 按照距離大小進行遞增排序隔箍。
- 選取距離最小的K個樣本。
- 確定前K個樣本所在類別出現(xiàn)的頻率脚乡,并輸出出現(xiàn)頻率最高的類別蜒滩。
KNN建模實操
寫完原理,我們繼續(xù)用一個例子給大家寫實際操作奶稠,假如你是一個醫(yī)生俯艰,手上有很多糖尿病病人的診斷數(shù)據(jù),你現(xiàn)在想建立一個KNN模型實現(xiàn)對新病人的診斷锌订,診斷結(jié)果有3類:healthy, chemically diabetic, overtly diabetic竹握。
一個很簡單的3分類問題哈。
我們的數(shù)據(jù)長這樣:
4個變量辆飘,第一個變量就是分類結(jié)局啦辐,取值可以為non-diabetic (Normal),chemically
diabetic (Chemical), overtly diabetic (Overt)
訓練模型之前我們可以先畫圖看看數(shù)據(jù)分布:
ggplot(diabetesTib, aes(glucose, insulin, col = class)) +
geom_point() +
theme_bw()
ggplot(diabetesTib, aes(sspg, insulin, col = class)) +
geom_point() +
theme_bw()
ggplot(diabetesTib, aes(sspg, glucose, col = class)) +
geom_point()+
theme_bw()
上面的圖是不同的自變量組合下的病人類別分布劈猪,其實可以看出來僅僅使用glucose和insulin兩個變量就可較好地區(qū)分三種類別了昧甘,不過在本例中我們依然使用所有的預(yù)測變量進行模型的訓練,我們用到的包是非常經(jīng)典的mlr包战得,第一步是定義學習任務(wù)makeClassifTask充边,第二部是定義學習器makeLearner,第三步就是訓練模型了常侦,具體代碼如下:
library(mlr)
diabetesTask <- makeClassifTask(data = diabetesTib, target = "class")
knn <- makeLearner("classif.knn", par.vals = list("k" = 2))
listLearners()$class#看究竟有多少學習器
knnModel <- train(knn, diabetesTask)
knnPred <- predict(knnModel, newdata = diabetesTib)
解釋一下上面的代碼過程:我們是用diabetesTib數(shù)據(jù)集訓練的模型浇冰,然后我們做預(yù)測的時候也是用的同樣的數(shù)據(jù)集。
同樣的數(shù)據(jù)集本來是有標簽的(糖尿病類型)聋亡,然后我們訓練的KNN模型會給它在預(yù)測一個標簽肘习,這個時候我們就可以進行真標簽和預(yù)測標簽的比較,就可以形成一個performance metrics坡倔,這個中文叫做性能指標漂佩。
性能指標可以用如下代碼得到:
performance(knnPred, measures = list(mmce, acc))
代碼后面的list就是你想要的指標,這兒我想要的是mean misclassification error和accuracy罪塔,其余的性能指標見下圖:
運行上面的代碼就可以得到指標的值:
可以看到我們的模型表現(xiàn)很好投蝉,畢竟是預(yù)測的本身的數(shù)據(jù)集嘛。
偏差-方差的權(quán)衡bias-variance trade-off
接下來給大家介紹偏方權(quán)衡這個概念征堪,這個在機器學習中很重要瘩缆,在所有的算法中我們都會存在矛盾的兩面,一個是欠擬合underfitted佃蚜,一個是過擬合overfitted庸娱,所帶來的后果就是算法學習不好或者外推行太差着绊,欠擬合造成的問題就是學習不足,會造成偏差熟尉,過擬合造成的問題是模型對新數(shù)據(jù)預(yù)測不穩(wěn)定归露,會造成方程過大,所以我們訓練模型的時候一定要注意兩者的權(quán)衡斤儿,這個就叫偏差-方差的權(quán)衡靶擦。
bias-variance are also opposed to each other: somewhere between a model that underfits and has bias, and a model that overfits and has variance, is an optimal model that balances the biasvariance trade-off
用上圖來說明就是,隨著模型復(fù)雜度的提高雇毫,方差越大,偏差越小踩蔚,我們要找的最優(yōu)模型一定是平衡可方差和偏差之后的模型棚放。
那么具體到我們上面例子,我們的K越小就越可能最近的K個中噪聲所占的比例越大馅闽,越容易過擬合飘蚯,K越大則越容易欠擬合。
寫到這問題就又來了福也,這個方差和偏差的度怎么把握呢局骤?
這就引出來了交叉驗證這個方法。
交叉驗證
在我們的例子中暴凑,我用我的數(shù)據(jù)訓練好一個KNN峦甩,然后我又在我原來的數(shù)據(jù)中來看我模型的表現(xiàn),這不是扯淡嘛现喳,結(jié)果肯定不會差啊凯傲。
甚至說結(jié)果你不滿意,你可以使你的模型更為復(fù)雜以至于最終達到能正確分類所有的數(shù)據(jù)嗦篱,完全做得到的冰单,但是這個時候你過擬合了呀,來一批新數(shù)據(jù)肯定就慘不忍睹灸促。
所以說我們評估機器學習的模型表現(xiàn)一定要在未知數(shù)據(jù)中進行诫欠。
未知數(shù)據(jù)怎么來?
再去搜集浴栽?
不用那么麻煩荒叼,常規(guī)的操作就是把原來的數(shù)據(jù)集進行劃分,一部分來訓練吃度,一部分來測試甩挫。然后根據(jù)模型在測試集中的表現(xiàn)我們就可以對模型進行評價了。
上面的過程就叫做交叉驗證cross-validation (CV),這個是每一個有監(jiān)督的學習過程中必須的過程椿每,無監(jiān)督的學習沒法做伊者,因為它沒有l(wèi)abeled data.
通過交叉驗證我們就知道某一個模型表現(xiàn)的具體情況英遭,如果表現(xiàn)的令人滿意,那么我們就用完整數(shù)據(jù)集再訓練一遍亦渗,這樣就得到了最終的模型挖诸,一個機器學習過程也就完成了。
Once we have cross-validated our model and are happy with its performance, we then use all the data we have(including the data in the test set) to train the final model (because typically, the more data we train our model with, the less bias it will have).
交叉驗證具體怎么做呢法精?繼續(xù)往下看多律。
為了加深大家的理解,我們對剛剛訓練的模型進行交叉驗證搂蜓,我們就用簡單交叉驗證(還有留一驗證和K折驗證狼荞,之后給大家寫)
簡單驗證很好理解,就是把原始數(shù)據(jù)集隨機劃分為一個訓練集一個測試集帮碰,比例可以自己設(shè)相味,一般是7:3
簡單驗證的代碼如下:
holdout <- makeResampleDesc(method = "Holdout", split = 7/10,stratify = TRUE)
holdoutCV <- resample(learner = knn, task = diabetesTask,
resampling = holdout, measures = list(mmce, acc))
上面的代碼中第一個holdout是生存交叉驗證的數(shù)據(jù)集,按照7:3劃分殉挽,其中令stratify為T保證了類別之間的平衡丰涉,第二個對象就是我們在劃分出來的訓練集中跑的KNN模型。
同樣的我們得到驗證后模型的mean misclassification error和accuracy
可以看到模型的mmce變大了斯碌,而acc變小了一死,很好理解,因為多了一個驗證的過程傻唾,實現(xiàn)了偏差和方差的權(quán)衡投慈,這個模型比剛剛的模型肯定外推性更好。
混淆矩陣
掌握了驗證的方法之后冠骄,我其實還想具體看看我這個KNN究竟錯在哪里逛裤。
我想知道訓練的這個KNN究竟幫助我把具體的類別分類對了多少,錯了多少猴抹,我們把對的錯的列成矩陣带族,就叫做混淆矩陣confusion matrix
對剛剛的例子,我們可以通過如下代碼形成混淆矩陣:
calculateConfusionMatrix(holdoutCV$pred, relative = TRUE)
可以看到蟀给,我們的混淆矩陣中既有絕對數(shù)又有占比蝙砌,很棒,我剛剛訓練的KNN分類錯了共5+1+6=11個病人跋理,其中這個KNN模型對overt這一類病人分類效果似乎并不太好择克。
超參數(shù)
剛剛寫的所有的東西都是在K為2的基礎(chǔ)上進行的,我也在開篇就說了K會影響模型的表現(xiàn)前普,但是K到底取幾好呢肚邢?
這兒接著引入超參數(shù)的概念,很多機器學習算法需要我們提前指定某些參數(shù),比如KNN的K骡湖,聚類算法中的類別數(shù)等等贱纠,指定好了之后,其他的參數(shù)就是算法自己在數(shù)據(jù)中去學習了响蕴,像這種算法根據(jù)數(shù)據(jù)自己學習的參數(shù)就是我們常常說的參數(shù)谆焊,而我們?nèi)藶橹付ǖ膮?shù)叫做超參數(shù):
k is what’s known as a hyperparameter: a variable or option that controls how a model makes predictions but is not estimated from the data.
超參數(shù)可以根據(jù)常識選,或者一個一個試浦夷,今天給大家寫hyperparameter tuning辖试,中文翻譯為超參調(diào)試,對于我們的例子劈狐,如果我們設(shè)定K很小罐孝,那么類別很容易受到單個噪聲的影響,如果K很大肥缔,就體現(xiàn)不出來數(shù)據(jù)的特征肾档,所以我們需要進行自動化的調(diào)試,調(diào)試的目標就是找到那個交叉驗證表現(xiàn)最好的模型對應(yīng)的K辫继。
首先,我們需要設(shè)定超參取值空間和取值方式:
knnParamSpace <- makeParamSet(makeDiscreteParam("k", values = 1:10))
gridSearch <- makeTuneControlGrid()
然后我們用K折驗證進行不同超參模型的評價:
cvForTuning <- makeResampleDesc("RepCV", folds = 10, reps = 20)
最后就是進行整個調(diào)試過程:
tunedK <- tuneParams("classif.knn", task = diabetesTask,
resampling = cvForTuning,
par.set = knnParamSpace, control = gridSearch)
結(jié)果顯示K為7的時候模型表現(xiàn)最好俗慈,當然了姑宽,這個超參調(diào)試的過程我們也可以進行可視化
knnTuningData <- generateHyperParsEffectData(tunedK)
plotHyperParsEffect(knnTuningData, x = "k", y = "mmce.test.mean",
plot.type = "line") +
theme_bw()
那么,我們現(xiàn)在就知道我們的K應(yīng)該是7闺阱,我們就用k=7重新訓練我們的KNN模型:
tunedKnn <- setHyperPars(makeLearner("classif.knn"),
par.vals = tunedK$x)
tunedKnnModel <- train(tunedKnn, diabetesTask)
到這兒炮车,我們的整個KNN的機器學習過程就算完成了,一個好的模型肯定要應(yīng)用嘛酣溃,接下來我們繼續(xù)看使用模型進行未知數(shù)據(jù)的預(yù)測
用訓練好的模型做預(yù)測
現(xiàn)在我們來模擬一些新病人瘦穆,比如3個病人吧,他們的glucose赊豌,insulin扛或,sspg分別如下:
newDiabetesPatients <- tibble(glucose = c(82, 108, 300),
insulin = c(361, 288, 1052),
sspg = c(200, 186, 135))
我們將這些新病人喂給我們的模型,得到預(yù)測這些病人患糖尿病的結(jié)果:
newPatientsPred <- predict(tunedKnnModel, newdata = newDiabetesPatients)
getPredictionResponse(newPatientsPred)
運行上面的代碼我們便可以得到模型給出的預(yù)測結(jié)果:
看到?jīng)]碘饼,3個病人KNN告訴我們熙兔,兩個沒糖尿病,1個是Overt類型的糖尿病艾恼。
好了住涉,寫到這兒,相信同學們都會KNN了钠绍,也許這是你接觸的第一個機器學習模型舆声,很簡單吧,關(guān)注我柳爽,之后的文章中我會一一給大家拆解各種各樣的機器學習算法媳握。
小結(jié)
今天給大寫了KNN的原理和和實現(xiàn)碱屁,以及一些常見通用的機器學習知識,感謝大家耐心看完毙芜,自己的文章都寫的很細忽媒,代碼都在原文中,希望大家都可以自己做一做腋粥,請關(guān)注后私信回復(fù)“數(shù)據(jù)鏈接”獲取所有數(shù)據(jù)和本人收集的學習資料晦雨。如果對您有用請先收藏,再點贊轉(zhuǎn)發(fā)隘冲。
也歡迎大家的意見和建議闹瞧,大家想了解什么統(tǒng)計方法都可以在文章下留言,說不定我看見了就會給你寫教程哦展辞。