《R語言實戰》第四部分第十七章-分類學習筆記
前面一章的內容主要是對數據進行聚類,可能是兩類,也可能是三類,甚至更多,而分類一般是指一分為二。舉幾個例子:
- 根據個人信息和財務歷史記錄預測其是否會還貸;
- 根據重症病人的癥狀和生命體征判斷其是否為心臟病發作;
- 根據各種關鍵詞、主題、來源等判別一封郵件是否為病毒郵件。
其共同的基本特徵是:根據一組預測變數(或特徵)來預測相對應的二分類結果(無信用風險/有信用風險,心臟病發作/心臟病未發作,是病毒郵件/不是病毒郵件),目的是通過某種方法實現對新出現單元的準確分類。顯然,前面關於Titanic的生存預測問題就是典型的分類問題。
在有監督機器學習領域,有很多種方法可用於分類,比如邏輯回歸、決策樹、隨機森林、支持向量機、神經網路等,本章將著重討論前面四種方法。以來源於UCI機器學習資料庫中的威斯康星州乳腺癌數據為基礎,通過rpart 、rpart.plot 和party 包來實現決策樹模型及其可視化, 通過randomForest包擬合隨機森林,通過e1071包構造支持向量機,通過R中的基本函數glm()實現邏輯回歸,分別說明各種方法的具體如何使用。
在開始之前,請確認已經安裝相關包(rpart、rpart.plot和party)並載入到工作空間。
1 數據準備
威斯康星州乳腺癌數據集可在UCI 機器學習資料庫(http://archive.ics.uci.edu/ml)中找到。數據準備流程如下:
#設定數據下載地址> loc <- "http://archive.ics.uci.edu/ml/machine-learning-databases/"> ds <- "breast-cancer-wisconsin/breast-cancer-wisconsin.data"> url <- paste(loc,ds,sep = "")#讀取數據> breast <- read.table(url,sep = ",",header = FALSE,na.strings = "?")#修改數據名稱> names(breast) <- c("ID","clumpThickness","sizeUniformity","shapeUniformity","maginalAdhesion","singleEpithelialCellSize", "bareNuclei",+ "blandChromatin", "normalNucleoli", "mitosis", "class")#刪除首列數據序號> df <- breast[-1]#將類別數據因子化> df$class <- factor(df$class,levels = c(2,4),labels = c("benign","malignant"))#設定隨機數> set.seed(1234)#設定訓練數據比例> train <- sample(nrow(df), 0.7*nrow(df))#設定訓練數據集> df.train <- df[train,]#設定驗證數據集> df.validate <- df[-train,]#顯示訓練數據> table(df.train$class) benign malignant 329 160 #顯示驗證數據> table(df.validate$class) benign malignant 129 81 >
本數據集包含699個樣本單元,數據集中共有11個變數,原數據未標變數名,共有16個樣本單元中有缺失數據並用問號(?)表示。
數據集中的包含的變數如下:
其中第一個變數已經刪除,隨機分出的訓練集和驗證集分別包含499個樣本單元和210個樣本單元。訓練集將用於建立邏輯回歸、決策樹、條件推斷樹、隨機森林、支持向量機等分類模型,測試集用於評估各個模型的有效性。由於本章採用了相同的數據集,因此可以直接比較各個方法的結果。
2 邏輯回歸
邏輯回歸(Logistic regression)是廣義線性模型的一種,在13.2節中有詳細介紹。R中可用glm()函數來擬合邏輯回歸模型。代碼如下:
#模型擬合> fit.logit <- glm(class~.,data = df.train,family = binomial())#查閱模型> summary(fit.logit)Call:glm(formula = class ~ ., family = binomial(), data = df.train)Deviance Residuals: Min 1Q Median 3Q Max -2.75813 -0.10602 -0.05679 0.01237 2.64317 Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -10.42758 1.47602 -7.065 1.61e-12 ***clumpThickness 0.52434 0.15950 3.287 0.00101 ** sizeUniformity -0.04805 0.25706 -0.187 0.85171 shapeUniformity 0.42309 0.26775 1.580 0.11407 maginalAdhesion 0.29245 0.14690 1.991 0.04650 * singleEpithelialCellSize 0.11053 0.17980 0.615 0.53871 bareNuclei 0.33570 0.10715 3.133 0.00173 ** blandChromatin 0.42353 0.20673 2.049 0.04049 * normalNucleoli 0.28888 0.13995 2.064 0.03900 * mitosis 0.69057 0.39829 1.734 0.08295 . ---Signif. codes: 0 『***』 0.001 『**』 0.01 『*』 0.05 『.』 0.1 『 』 1(Dispersion parameter for binomial family taken to be 1) Null deviance: 612.063 on 482 degrees of freedomResidual deviance: 71.346 on 473 degrees of freedom (6 observations deleted due to missingness)AIC: 91.346Number of Fisher Scoring iterations: 8#用模型預測驗證數據集> prob <- predict(fit.logit,df.validate,type = "response")#對驗證數據集預測結果進行分類> logit.pred <- factor(prob > .5,levels = c(FALSE,TRUE),labels = c("begign","malignant"))#評估預測準確性> logit.perf <- table(df.validate$class,logit.pred,dnn = c("Actual","Predicted"))> logit.perf PredictedActual begign malignant benign 118 2 malignant 4 76>
在預測當中,以類別為響應變數,其餘變數為預測變數。並且基於df.train訓練集數據建立模型,並以此模型對df.validate數據集中的樣本進行分類。predict()函數加參數type="response"即可得到預測腫瘤為噁心的概率。
最後,給出預測與實際情況對比的交叉表(即混淆矩陣,confusion matrix)。
需要注意的是,模型中有三個預測變數(sizeUniformity、shapeUniformity和
singleEpithelialCellSize)的係數未通過顯著性檢驗(即p值大於0.1),後續從預測的角度,一般不會將這些變數納入最終模型。精簡模型可以通過以下代碼實現:
logit.fit.reduced <- step(fit.logit)
3 決策樹
決策樹是數據挖掘領域中的常用模型。其基本思想是對預測變數進行二元分離,從而構造一棵可用於預測新樣本單元所屬類別的樹。本節將介紹兩類決策樹:經典樹和條件推斷樹。
3.1 經典決策樹
經典決策樹的演算法如下:
- 選定一個最佳預測變數將全部樣本單元分為兩類,實現兩類中的純度最大化。如果預測變數連續,則選定一個分割點進行分類;如果預測變數為分類變數,則對各類別進行合併再分類;
- 對每一個子類別繼續執行步驟1;
- 重複步驟1~2,直到子類別中所含的樣本單元數過少,或者沒有分類法能將不純度下降到一個給定閾值以下。最終集中的子類別即為終端節點(terminal node);
- 對任一樣本單元執行決策樹,得到其終端節點,即可根據步驟3得到模型預測的所屬類別。
理論上,上述演算法通常會得到一顆過大的樹,將類別分得過細,從而出現過擬合現象。所導致的結果就是對於訓練集外單元的分類性能較差。可採用k-折交叉驗證(k-fold crossValidation)然後選擇預測誤差最小的樹來解決過擬合問題。
R中的rpart包支持rpart()函數構造決策樹,prune()函數對決策樹進行剪枝,決策樹演算法的R語言實現代碼如下:
#導入包> library(rpart)#設置隨機數> set.seed(1234)#用rpart生成決策樹> dtree <- rpart(class~.,data = df.train,method = "class",+ parms = list(split="information"))#顯示rpart返回的決策樹模型cptable參數> dtree$cptable CP nsplit rel error xerror xstd1 0.800000 0 1.00000 1.00000 0.064846052 0.046875 1 0.20000 0.30625 0.041500183 0.012500 3 0.10625 0.20625 0.034670894 0.010000 4 0.09375 0.18125 0.03264401#繪製複雜度參數vs交叉驗證誤差圖形> plotcp(dtree)#剪枝> dtree.pruned <- prune(dtree,cp=.0125)> library(rpart.plot)> prp(dtree.pruned,type = 2, extra = 104,+ fallen.leaves = TRUE,main = "Decision Tree")#對訓練集外樣本單元分類> dtree.pred <- predict(dtree.pruned, df.validate, type="class")> dtree.perf <- table(df.validate$class, dtree.pred,+ dnn=c("Actual", "Predicted"))> dtree.perf PredictedActual benign malignant benign 122 7 malignant 2 79>
rpart()函數用於生成決策樹,返回的模型的cptable值包含不同大小的樹所對應的預測誤差,其中:
- 複雜度參數(cp)用於懲罰過大的樹;
- 樹的大小即分支數(nsplit),有n個分支的樹將有n+1個終端節點;
- rel error欄即訓練集中各種樹對應的誤差;
- 交叉驗證誤差(xerror)即基於訓練樣本所得的10折交叉驗證誤差;
- xstd欄為交叉驗證誤差的標準差。
下圖為藉助plotcp()函數繪製的交叉驗證誤差與複雜度參數的關係圖,對於所有交叉驗證誤差在最小交叉驗證誤差一個標準差範圍內的樹,最小的樹即最優的樹。
本例中,最小的交叉驗證誤差為0.18,標準誤差為0.0326,則最優的樹為交叉驗證誤差在
0.18±0.0326(0.15和0.21)之間的樹。根據上圖可以選得最優樹,即三次分割的樹。
prune()函數根據複雜度參數在完整樹的基礎上剪掉最不重要的枝,從而將樹的大小控制
在理想範圍內。從cptable中的數據可以看出,三次分割對應的複雜度參數為0.0125,從而prune(dtree, cp=0.0125)可得到一個理想大小的樹。
rpart.plot包中的prp()函數可以畫出最終的決策樹,如下圖所示。
3.2 條件推斷樹
與傳統決策樹相比,條件推斷樹變數和分割的選取是基於顯著性檢驗的,而不是純凈度或者同質性一類的度量,主要演算法如下:
- 對輸出變數和每個預測變數間的關係計算p值;
- 選取p值最小的變數;
- 在因變數與被選中的變數間嘗試所有可能的二元分割(通過排列檢驗),並選取最顯著的分割;
- 將數據集分成兩群,並對每個子群重複上述步驟;
- 重複直至所有分割都不顯著或已到達最小節點為止。
R語言中的party包中的ctree()函數,以下代碼對乳腺癌數據生成條件推斷樹。
> library(party)載入需要的程輯包:grid載入需要的程輯包:mvtnorm載入需要的程輯包:modeltools載入需要的程輯包:stats4載入需要的程輯包:strucchange載入需要的程輯包:zoo載入程輯包:『zoo』The following objects are masked from 『package:base』: as.Date, as.Date.numeric載入需要的程輯包:sandwich> fit.ctree <- ctree(class~.,data = df.train)> plot(fit.ctree,main = "Conditional Inference Tree")> ctree.pred <- predict(fit.ctree,df.validate,type = "response")> ctree.perf <- table(df.validate$class,ctree.pred,dnn = c("Actual","Predicted"))> ctree.perf PredictedActual benign malignant benign 122 7 malignant 3 78>
對於條件推斷樹,剪枝不是必需的,並且party包所提供的圖像函數可以對條件推斷樹進行展示。
4 隨機森林
之前在泰坦尼克號存活率預測當中就用到了隨機森林(random forest),鏈接如下:隨機森林--二元分類的利器之Kaggle初體驗Titanic: Machine Learning from Disaster
隨機森林是一種組合式的有監督學習方法,我們同時對樣本單元和變數進行抽樣生成大量決策樹預測模型,並將模型的結果匯總提升分類準確率。加入訓練集中共有N個樣本單元,M個變數,則隨機森林的演算法如下:
- 從訓練集中隨機有放回地抽取N個樣本單元,生成大量決策樹;
- 在每一個節點隨機抽取m<M個變數,將其作為分割該節點的候選變數。每一個節點處的變數數應一致;
- 完整生成所有決策樹,最小節點為1;
- 終端節點的所屬類別由節點對應的眾數類別決定;
- 對於新的觀測點,用所有的樹對其進行分類,其類別由多數決定原則生成。
由於樣本點所對應的類別可由生成的樹估計,然後與其真實類別比較即可得到袋外預測誤差,在無法獲得驗證集時,這是隨機森林的一大優勢。
randomForest包中的randomFores()函數可用於生成隨機森林。以下代碼給出了隨機森林對乳腺癌數據的預測分析結果。
#導入包> library(randomForest)randomForest 4.6-12Type rfNews() to see new features/changes/bug fixes.#設置隨機數種子> set.seed(1234)#構建模型> fit.forest <- randomForest(class~.,data = df.train,+ na.action = na.roughfix,+ importance = TRUE)##查看模型> fit.forestCall: randomForest(formula = class ~ ., data = df.train, importance = TRUE, na.action = na.roughfix) Type of random forest: classification Number of trees: 500No. of variables tried at each split: 3 OOB estimate of error rate: 3.68%Confusion matrix: benign malignant class.errorbenign 319 10 0.03039514malignant 8 152 0.05000000#給出變數重要性> importance(fit.forest, type = 2) MeanDecreaseGiniclumpThickness 12.504484sizeUniformity 54.770143shapeUniformity 48.662325maginalAdhesion 5.969580singleEpithelialCellSize 14.297239bareNuclei 34.017599blandChromatin 16.243253normalNucleoli 26.337646mitosis 1.814502#對訓練集外樣本點分類> forest.pred <- predict(fit.forest,df.validate)> foresst.perf <- table(df.validate$class, forest.pred,+ dnn = c("Actual", "Predicted"))> forest.perf <- table(df.validate$class, forest.pred,+ dnn = c("Actual", "Predicted"))> forest.perf PredictedActual benign malignant benign 117 3 malignant 1 79>
randomForest()函數從訓練集中有放回地隨機抽取489個觀測點,在每棵樹的每個
節點隨機抽取3個變數,生成了500棵傳統決策樹。na.action=na.roughfix參數可將數
值變數中的缺失值替換成對應列的中位數,類別變數中的缺失值替換成對應列的眾數類(若有多個眾數則隨機選一個)。
隨機森林可度量變數重要性,通過設置information=TRUE參數得到,並可以通過importance()
函數輸出。
最後,再通過隨機森林演算法對驗證集中的樣本單元進行分類,並計算預測準確率。分類時
剔除驗證集中有缺失值的單元。總體來看,對驗證集的預測準確率高達98%。
之前已經介紹過兩種決策樹,而隨機森林也是不斷生成決策樹的過程,在R中有不同隨機森林的的包和函數分別採用不同的決策樹模型,其中randomForest包根據傳統決策樹生成隨機森林,而party包中的cforest()函數則可基於條件推斷樹生成隨機森林。當預測變數間高度相關時,基於條件推斷樹的隨機森林可能效果更好。
相較於其他分類方法,除了難以理解和表達外,隨機森林有眾多的優點,分類準確率通常更高,可處理大規模問題(即多樣本單元、多變數),可處理訓練集中有大量缺失值的數據,也可應對變數遠多於樣本單元的數據。可計算袋外預測誤差(OOB error)、度量變數重要性等。
5 支持向量機
支持向量機(SVM)是一類用於分類和回歸的有監督機器學習模型,基於優雅的數學理論,並可以輸出比較準確的預測結果,近年來在機器學習領域較為流行。
SVM旨在在多維空間中找到一個能將全部樣本單元分成兩類的最優平面,這一平面應使兩類
中距離最近的點的間距(margin)儘可能大,在間距邊界上的點被稱為支持向量(support vector,它們決定間距),分割的超平面位於間距的中間。
對於一個N維空間(即N個變數)來說,最優超平面(即線性決策面,linear decision surface)為N–1維。當變數數為2時,曲面是一條直線;當變數數為3時,曲面是一個平面;當變數數為10時,曲面就是一個9維的超平面。
對於線性可分的問題,對應的超平面就是一條直線,如下圖所示:
對於線性不可分的數據,如下圖所示。
普遍的方法是將N維的數據投射到N+1維空間,使其在高維線性可分。以上圖為例,不存在完全分開圓圈和三角形的線,此時SVM通過核函數將數據從二維投射到三維,通過下面的方式:
這時,我們就可以用一張硬紙片將三角形與圓圈分開。當然,SVM的數學解釋比較複雜,有興趣可以參考:https://zh.wikipedia.org/wiki/%E6%94%AF%E6%8C%81%E5%90%91%E9%87%8F%E6%9C%BA
SVM可以通過R中kernlab包的ksvm()函數和e1071包中的svm()函數實現,前者功能更強大。以下代碼就是通過svm()函數對乳腺癌數據建模的一個示例。
> library(e1071)> set.seed(1234)> fit.svm <- svm(class~.,data = df.train)> fit.svmCall:svm(formula = class ~ ., data = df.train)Parameters: SVM-Type: C-classification SVM-Kernel: radial cost: 1 gamma: 0.1111111 Number of Support Vectors: 76> svm.pred <- predict(fit.svm, na.omit(df.validate))> svm.perf <- table(na.omit(df.validate)$class,+ svm.pred, dnn=c("Actual", "Predicted"))> svm.perf PredictedActual benign malignant benign 116 4 malignant 3 77>
svm()函數默認在生成模型前對變數進行標準化,且不允許有缺失值出現,準確率不錯,不過比如隨機森林方法。
為了進一步提高svm()函數的準確度,可以探求一下其內部機理。默認是通過徑向基函數(Radial Basis Function,RBF)將樣本單元投射到高維空間,此時有兩個參數可能影響最終結果:gamma和成本(cost)。其中gamma是核函數的參數,控制分割超平面的形狀。這樣講比較抽象,直觀一些,我們也可將gamma看作控制訓練樣本「到達範圍」的參數,即gamma越大意味著訓練樣本到達範圍越廣,而越小則意味著到達範圍越窄。gamma必須大於,通常gamma越大會導致支持向量越多。
成本參數代表犯錯的成本。當成本較大時,則模型對誤差的懲罰更大,從而將生成一個更複雜的分類邊界,對應的訓練集中的誤差也會更小,但也意味著可能存在過擬合問題,即對新樣本單元的預測誤差可能很大。較小的成本意味著分類邊界更平滑,但可能會導致欠擬合。與gamma一樣,成本參數也恆為正。
在R中,svm()函數默認設置gamma為預測變數個數的倒數,成本參數為1。但是為了生成更有效的模型,我們可以嘗試不同的參數組合,這樣的過程我們稱之為調參。
下面的代碼我們採用格點搜索法尋找最為有效的參數組合。
#設置隨機數> set.seed(1234)#設置每個參數的變數範圍> tuned <- tune.svm(class~.,data = df.train,+ gamma = 10^(-6:1),+ cost = 10^(-10:10))#查看格點搜索法結果> tunedParameter tuning of 『svm』:- sampling method: 10-fold cross validation - best parameters: gamma cost 0.01 1- best performance: 0.02904092 #用新參數擬合模型> fit.svm <- svm(class~., data = df.train,gamma = 0.01,cost = 1)#評估模型交叉驗證集上的表現> svm.pred <- predict(fit.svm, na.omit(df.validate))#結果以聯列表形式呈現> svm.perf <- table(na.omit(df.validate)$class,+ svm.pred, dnn=c("Actual", "Predicted"))> svm.perf PredictedActual benign malignant benign 117 3 malignant 3 77>
上面首先用tuned.svm()函數選擇合適的參數,然後用新的參數來代替原模型所採用的默認參數,重新擬合模型,然後進行交叉集的驗證和輸出工作。從結果對比上看,調參後的模型輕微減少了錯分個數(從7減少到6)。通常,SVM結果調參後會得到更好的結果。
6 選擇預測效果最好的解
對於二分類問題我們一般由以下幾個指標來判斷預測的效果優劣:
以下代碼給出了計算以上相關統計量的函數。
performance <- function(table, n=2){ if(!all(dim(table) == c(2,2))) stop("Must be a 2 x 2 table") #得到頻數 tn = table[1,1] fp = table[1,2] fn = table[2,1] tp = table[2,2] #計算統計量 sensitivity = tp/(tp+fn) specificity = tn/(tn+fp) ppp = tp/(tp+fp) npp = tn/(tn+fn) hitrate = (tp+tn)/(tp+tn+fp+fn) #輸出結果 result <- paste("Sensitivity = ", round(sensitivity, n) , "
Specificity = ", round(specificity, n), "
Positive Predictive Value = ", round(ppp, n), "
Negative Predictive Value = ", round(npp, n), "
Accuracy = ", round(hitrate, n), "
", sep="") cat(result)}
下面用performance()函數計算前面五種分類工具的分類效果。
> performance(logit.perf)Sensitivity = 0.95Specificity = 0.98Positive Predictive Value = 0.97Negative Predictive Value = 0.97Accuracy = 0.97> performance(dtree.perf)Sensitivity = 0.98Specificity = 0.95Positive Predictive Power = 0.92Negative Predictive Power = 0.98Accuracy = 0.96> performance(ctree.perf)Sensitivity = 0.96Specificity = 0.95Positive Predictive Value = 0.92Negative Predictive Value = 0.98Accuracy = 0.95> performance(forest.perf)Sensitivity = 0.99Specificity = 0.98Positive Predictive Value = 0.96Negative Predictive Value = 0.99Accuracy = 0.98> performance(svm.perf)Sensitivity = 0.96Specificity = 0.98Positive Predictive Value = 0.96Negative Predictive Value = 0.98Accuracy = 0.97>
在本案例中,各種分類器的表現都還不錯,其中隨機森林的表現相對更好,不過各個分類器之間的差距較小。
7 用rattle包進行數據挖掘
Rattle(R Analytic Tool to Learn Easily)包向R語言用戶提供了一個可做數據分析的圖像式交互
界面(GUI)。
安裝rattle包略微有些複雜,主要是需要GTK+工具。
安裝好以後可以通過以下代碼進入:
library(rattle)rattle()
GUI界面如下:
由於機上安裝GTK+未成功,本節具體內容略去。
8 小結
本章介紹了一系列用於二分類的機器學習方法,包括邏輯回歸分類方法、傳統決策樹、條件推斷樹、集成性的隨機森林以及越來越流行的支持向量機。最後介紹了Rattle,它為數據挖掘提供了一個圖形用戶界面,使用戶可以通過滑鼠點擊的方式調用相關的函數。Rattle在比較多個分類模型時格外有用。由於它可在日誌文件中生成可重用的R代碼,也為我們學習R中的許多預測分析函數的語法提供了機會。
本章介紹的方法複雜度各異。數據挖掘者一般會嘗試一些相對簡單的方法(如邏輯回歸、決策樹)和一些複雜的、黑箱式的方法(如隨機森林、支持向量機)。如果與簡單的方法相比,複雜方法在預測效果方面並沒有顯著提升,則我們一般會選擇較簡單的方法。
推薦閱讀:
※第4講:複雜數據處理和分析聽課及實踐筆記
※LightGBM調參指南(帶貝葉斯優化代碼)
※R|ggplot2(一)|一個完整的繪圖流程
※【譯文】R語言不平衡數據分類指南
※【機器學習】確定最佳聚類數目的10種方法