c++实现决策树分类汽车评估数据集_R有监督机器学习-分类方法
當我們說機器學習的的時候,我們在說什么?
來源于mlr3包的作者:https://mlr3book.mlr-org.com/basics.html上圖解釋了完整的機器學習流程,包括構建任務、準備訓練數據集及測試數據集、選擇學習方法(leaner)、構建模型、預測、評估、重復執行并可視化學習情況。
下面所提及到的有監督機器學習各個方法只是Learner的一種方法而已。
有監督機器學習領域中包含許多可用于分類的方法,如邏輯回歸、決策樹、隨機森林、支持向量機、神經網絡等。
有監督學習基于一組包含預測變量值和輸出變量值的樣本單元。將全部數據分為一個訓練集和一個驗證集,其中訓練集用于建立預測模型,驗證集用于測試模型的準確性。對訓練集和驗證集的劃分尤其重要,因為任何分類技術都會最大化給定數據的預測效果。用訓練集建立模型并測試模型會使得模型的有效性被過分夸大,而用單獨的驗證集來測試基于訓練集得到的模型則可使得估計更準確、更切合實際。得到一個有效的預測模型后,就可以預測那些只知道預測變量值的樣本單元對應的輸出值了。
通過 rpart 、 rpart.plot 和 party 包來實現決策樹模型及其可視化,通過randomForest包擬合隨機森林,通過 e1071包構造支持向量機,通過R中的基本函數 glm()實現邏輯回歸。
機器學習的數據準備
下面給出R中數據準備流程。數據從UCI數據庫中抽取,并隨機分出訓練集和驗證集,其中訓練集中包含499個樣本單元(占70%),其中良性樣本單元329個,惡性160個;驗證集中包含210個樣本單元(占30%),其中良性129個,惡性81個。
數據集中的ID變量不作考慮,類別變量為輸出變量用作判斷。 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) table(df.validate$class)predict函數對應一系列的函數用于預測模型,predict.glm, predict.lm, predict.loess, predict.nls, predict.poly, predict.princomp, predict.smooth.spline.SafePrediction for prediction from (univariable) polynomial and spline fits.
For time-series prediction, predict.ar, predict.Arima, predict.arima0, predict.HoltWinters, predict.StructTS.
logistic回歸模型分類
fit.logit <- glm(class~., data = df.train, family = binomial()) summary(fit.logit) prob <- predict.glm(fit.logit, newdata = df.validate, type = "response") # 注意type= 參數 # type # the type of prediction required. The default is on the scale of the linear # predictors; the alternative "response" is on the scale of the response variable. # Thus for a default binomial model the default predictions are of log-odds # (probabilities on logit scale) and type = "response" gives the predicted # probabilities. The "terms" option returns a matrix giving the fitted values of # each term in the model formula on the linear predictor scale.logit.prob <- factor(prob > 0.5, levels = c(FALSE, TRUE, labels = c("Benign", "Malignant"))) # 關注一下這里factor()進行差別并標注標簽的妙用 table(df.validate$class, logit.prob, dnn = c("Actual", "Predicted")) # table()的交叉驗證對象長度需要一致解釋:
1. 首先,以類別為響應變量,其余變量為預測變量。 2. 基于 df.train 數據框中的數據構造邏輯回歸模型, 接著給出了模型中的系數。 3. 接著,采用基于 df.train 建立的模型來對 df.validate 數據集中的樣本單元分類。predict() 函數默認輸出腫瘤為惡性的對數概率,指定參數 type="response" 即可得到預測腫瘤為惡性的概率。樣本單元中,概率大于0.5的被分為惡性腫瘤類,概率小于等于0.5的被分為良性腫瘤類。 4. 最后給出預測與實際情況對比的交叉表(即混淆矩陣,confusion matrix)。
同時要注意的是,模型中有三個預測變量( sizeUniformity 、 shapeUniformity 和singleEpithelialCellSize )的系數未通過顯著性檢驗(即p值大于0.1)。從預測的角度來說,我們一般不會將這些變量納入最終模型。當這類不包含相關信息的變量特別多時,可以直接將其認定為模型中的噪聲。在這種情況下,可用逐步邏輯回歸生成一個包含更少解釋變量的模型,其目的是通過增加或移除變量來得到一個更小的AIC值。精簡后的模型在驗證集上的誤差相對全變量模型更小。此處需要聯系到之間學習的logistic回歸模型如何減少納入考慮的無效變量,參廣義線性模型_高級回歸分析.md。
決策樹分類
基本思想是對預測變量進行二元分離,從而構造一棵可用于預測新樣本單元所屬類別的樹。此處將介紹兩類決策樹:經典樹和條件推斷樹。
經典決策樹
具體執行步驟如下:
1. 選定一個最佳預測變量將全部樣本單元分為兩類,實現兩類中的純度最大化(即一類中良性樣本單元盡可能多,另一類中惡性樣本單元盡可能多)。如果預測變量連續,則選定一個分割點進行分類,使得兩類純度最大化;如果預測變量為分類變量(本例中未體現),則對各類別進行合并再分類。 2. 對每一個子類別繼續執行步驟(1)。 3. 重復步驟(1)~(2),直到子類別中所含的樣本單元數過少,或者沒有分類法能將不純度下降到一個給定閾值以下。最終集中的子類別即終端節點(terminal node)。根據每一個終端節點中樣本單元的類別數眾數來判別這一終端節點的所屬類別。 4. 對任一樣本單元執行決策樹,得到其終端節點,即可根據步驟3得到模型預測的所屬類別。
R中的 rpart 包支持 rpart() 函數構造決策樹, prune()函數對決策樹進行剪枝。 # rpart包主要有兩個函數組成,分別介紹如下: rpart(formula, data, weight s, subset, na. action = na. rpart, method, model= FALSE, x= FALSE,y= TRUE, parms, control, cost, . . . )
- fomula 回歸方程形式: 例如 y~ x 1+ x2+ x3。
- data 數據: 包含前面方程中變量的數據框( data frame) 。
- na.action 缺失數據的處理辦法: 默認辦法是刪除因變量缺失的觀測而保留自變量缺失的觀測。
- method 根據樹末端的數據類型選擇相應變量分割方法,本參數有四種取值: 連續型>anova; 離散型>class; 計數型( 泊松過程)>poisson; 生存分析型>exp。程序會根據因變量的類型自動選擇方法, 但一般情況下最好還是指明本參數, 以便讓程序清楚做哪一種樹模型。
- 連續性method=“anova”,離散型method=“class”,計數型method=“poisson”,生存分布型method=“exp”
- parms 對應于method的選擇進行設置,是關于如何分割(splitting)數據的optional選項。在classification splitting中,可供選擇進行設置的三個參數包括: 向量的先驗概率(component prior)、損失矩陣(component loss)或者分割指數(splitting index)(component split)。
- component priors必須為正值且和為1,component loss必須對角為0且非對角為正數,component split可以是gini(基尼系數)或者information(信息增益)。
- 默認值如下,prior為數據計數的占比情況,loss為1,split為gini
- 如果method = "anova", 則沒有參數;
- 如果method = "poisson", 分割(split)有一個參數,先驗分布(prior)變異系數的比率,默認為1;
- 如果method = "exp", 其參數設置與method = "poisson"一致;
- 如果為離散型數據,則需要分別設置prior、loss、split;
- control 控制每個節點上的最小樣本量、交叉驗證的次數、復雜性參量: 即cp: complexity pamemeter, 這個參數意味著對每一步拆分, 模型的擬合優度必須提高的程度, 等等。在rpart的生成對象中即包含cptable
- tree 一個回歸樹對象, 常是rpart()的結果對象。
- cp 復雜性參量, 指定剪枝采用的閾值。
對于所有交叉驗證誤差在最小交叉驗證誤差一個標準差范圍內的樹,最小的樹即最優的樹
虛線是基于一個標準差準則得到的上限(0.1235295+1×0.02637116=0.149)。從圖像來看,應選擇虛線下最左側 cp 值對應的樹 # 根據本例中,最小的交叉驗證誤差為0.123,標準誤差為0.026 # 故最優的樹為交叉驗證誤差在0.123±0.026(0.097和0.149)之間的樹 # cp選擇0.01,即dtree$cptable中4次分割對應的復雜度符合 dtree.pruned <- prune(dtree, cp = 0.01) library(rpart.plot) rpart.plot::prp(dtree.pruned, type = 2, extra = 104, fallen.leaves = TRUE,main = "Decision Tree") # type=2 可畫出每個節點下分割的標簽 # extra=104 可畫出每一類的概率以及每個節點處的樣本占比 # fallen.leaves=TRUE 可在圖的底端顯示終端節點# 驗證模型 dtree.pred <- predict(dtree.pruned, newdata = df.validate, type = "class") # 這里與Logistic的type參數不同table(df.validate$class, dtree.pred, dnn = c("Actual", "Predicted")) ### Predicted ### Actual Benign Malignant ### Benign 129 10 ### Malignant 2 69predict() 函數用來對驗證集中的觀測點分類。
整體來看,驗證集中的準確率達到了96%。與邏輯回歸不同的是,驗證集中的210個樣本單元都可由最終樹來分類。值得注意的是,對于水平數很多或缺失值很多的預測變量,決策樹可能會有偏。
條件推斷樹
經典決策樹的一種重要變體,即條件推斷樹(conditional inference tree)。條件推斷樹與經典決策樹類似,但變量和分割的選取是基于顯著性檢驗的,而不是純凈度或同質性一類的度量。顯著性檢驗是置換檢驗。
條件推斷樹的步驟如下:
1. 對輸出變量與每個預測變量間的關系計算p值。 2. 選取p值最小的變量。 3. 在因變量與被選中的變量間嘗試所有可能的二元分割(通過排列檢驗),并選取最顯著的分割。 4. 將數據集分成兩群,并對每個子群重復上述步驟。 5. 重復直至所有分割都不顯著或已到達最小節點為止。
條件推斷樹可由party包中的ctree()函數獲得。
library(party) fit.party <- ctree(class~., data = df.train) plot(fit.party, main = "Conditinal Inference Tree")df.pred <- predict(fit.party, newdata = df.validate, type = "response") table(df.validate$class, df.pred, dnn = c("Actual", "Predicted")) # Predicted # Actual Benign Malignant # Benign 131 8 # Malignant 4 67值得注意的是,對于條件推斷樹來說,剪枝不是必需的, 其生成過程相對更自動化一些。另外, party 包也提供了許多圖像參數。每個節點中的陰影區域代表這個節點對應的惡性腫瘤比例。
如果想通過 rpart() 函數得到一棵經典決策樹,但想要以上圖的形式展示這棵決策樹,則可借助 partykit 包。安裝并載入這個包后,可通過 plot(as.party(an.rpart.tree))繪制想要的圖。隨機森林分類
- 隨機森林(random forest)是一種組成式的有監督學習方法。在隨機森林中,我們同時生成多個預測模型,并將模型的結果匯總以提升分類準確率。
- 隨機森林的算法涉及對樣本單元和變量進行抽樣,從而生成大量決策樹。對每個樣本單元來說,所有決策樹依次對其進行分類。所有決策樹預測類別中的眾數類別即為隨機森林所預測的這一樣本單元的類別。
假設訓練集中共有N個樣本單元,M個變量,則隨機森林步驟如下: 1. 從訓練集中隨機有放回地抽取N個樣本單元,生成大量決策樹。 2. 在每一個節點隨機抽取m<M個變量,將其作為分割該節點的候選變量。每一個節點處的變量數應一致。 3. 完整生成所有決策樹,無需剪枝(最小節點為1)。 4. 終端節點的所屬類別由節點對應的眾數類別決定。 5. 對于新的觀測點,用所有的樹對其進行分類,其類別由多數決定原則生成。
生成樹時沒有用到的樣本點所對應的類別可由生成的樹估計,與其真實類別比較即可得到袋外預測(out-of-bag,OOB)誤差。無法獲得驗證集時,這是隨機森林的一大優勢。隨機森林算法可計算變量的相對重要程度,randomForest 包中的 randomForest() 函數可用于生成隨機森林。函數默認生成500棵樹,并且默認在每個節點處抽取 sqrt(M) 個變量,最小節點為1。
library(randomForest) set.seed(1234) fit.rf <- randomForest(class~., data = df.train, importance = TRUE,na.action = na.roughfix)importance(fit.rf, type = 2)df.pred <- predict(fit.rf, newdata = df.validate) table(df.validate$class, df.pred)- 首先, randomForest() 函數從訓練集中有放回地隨機抽取489個觀測點,在每棵樹的每個節點隨機抽取3個變量,從而生成了500棵傳統決策樹
- na.action=na.roughfix 參數可將數值變量中的缺失值替換成對應列的中位數,類別變量中的缺失值替換成對應列的眾數類(若有多個眾數則隨機選一個)。
- 隨機森林可度量變量重要性,通過設置 information=TRUE 參數得到,并通過 importance()函數輸出。由 type=2 參數得到的變量相對重要性就是分割該變量時節點不純度(異質性)的下降總量對所有樹取平均。節點不純度由Gini系數定義。
- 最后,再通過隨機森林算法對驗證集中的樣本單元進行分類,并計算預測準確率。分類時剔除驗證集中有缺失值的單元。
相較于其他分類方法,隨機森林的分類準確率通常更高。
隨機森林算法可處理大規模問題(即多樣本單元、多變量),可處理訓練集中有大量缺失值的數據,也可應對變量遠多于樣本單元的數據。可計算袋外預測誤差(OOB error)、度量變量重要性也是隨機森林的兩個明顯優勢。
隨機森林的一個明顯缺點是分類方法(此例中相當于500棵決策樹)較難理解和表達。另外,我們需要存儲整個隨機森林以對新樣本單元分類。
支持向量機分類
支持向量機(SVM)是一類可用于分類和回歸的有監督機器學習模型。其流行歸功于兩個方面:一方面,他們可輸出較準確的預測結果;另一方面,模型基于較優雅的數學理論。
SVM旨在在多維空間中找到一個能將全部樣本單元分成兩類的最優平面,這一平面應使兩類中距離最近的點的間距(margin)盡可能大,在間距邊界上的點被稱為支持向量(support vector,它們決定間距),分割的超平面位于間距的中間。
SVM可以通過R中 kernlab 包的 ksvm() 函數和 e1071 包中的 svm() 函數實現。 ksvm() 功能更強大,但 svm() 相對更簡單。
library(e1071) fit.svm <- svm(class~., df.train) fit.svmsvm.pred <- predict(fit.svm, newdata = na.omit(df.validate)) # svm不支持缺失值 table(df.validate$calss, svm.pred, dnn = c("Actual", "Predicted")) ### Predicted ### Actual Benign Malignant ### Benign 136 8 ### Malignant 1 65由于方差較大的預測變量通常對SVM的生成影響更大, svm() 函數默認在生成模型前對每個變量標準化,使其均值為0、標準差為1。
選擇調和參數
svm() 函數默認通過徑向基函數(Radial Basis Function,RBF)將樣本單元投射到高維空間。
在用帶RBF核的SVM擬合樣本時,兩個參數可能影響最終結果:gamma和成本(cost)。
gamma是核函數的參數,控制分割超平面的形狀。gamma越大,通常導致支持向量越多。我們也可將gamma看作控制訓練樣本“到達范圍”的參數,即gamma越大意味著訓練樣本到達范圍越廣,而越小則意味著到達范圍越窄。gamma必須大于0。
成本參數代表犯錯的成本。一個較大的成本意味著模型對誤差的懲罰更大,從而將生成一個更復雜的分類邊界,對應的訓練集中的誤差也會更小,但也意味著可能存在過擬合問題,即對新樣本單元的預測誤差可能很大。較小的成本意味著分類邊界更平滑,但可能會導致欠擬合。與gamma一樣,成本參數也恒為正。
svm() 函數默認設置gamma為預測變量個數的倒數,成本參數為1。不過gamma與成本參數的不同組合可能生成更有效的模型。在建模時,我們可以嘗試變動參數值建立不同的模型,但利用格點搜索法可能更有效。可以通過tune.svm()對每個參數設置一個候選范圍, tune.svm()函數對每一個參數組合生成一個SVM模型,并輸出在每一個參數組合上的表現。
set.seed(1234) tuned <- tune.svm(class~., data = df.train, gamma = 10^(-6:1), cost = 10^(-10:10))set.seed(1234) tuned <- tune.svm(class~., data = df.train, gamma = 10^(-6:1), cost = 10^(-10:10)) tuned Parameter tuning of ‘svm’: # sampling method: 10-fold cross validation # best parameters: # gamma cost # 0.001 10 # best performance: 0.0327381 fit.svm2 <- svm(class~., data = df.train, gamma = 0.001, cost = 10) svm.pred2 <- predict(fit.svm2, newdata = na.omit(df.validate)) table(df.validate$class, svm.pred2) # Predicted # Actual Benign Malignant # Benign 138 6 # Malignant 1 65- 首先,對不同的gamma和成本擬合一個帶RBF核的SVM模型。我們一共將嘗試八個不同的gamma (從0.000 001到10)以及21個成本參數(從0.01到1010)。總體來說,我們共擬合了168 (8×21)個模型,并比較了其結果。訓練集中10折交叉驗證誤差最小的模型所對應的參數為gamm=0.1,成本參數為1。
- 基于這一參數值組合,我們對全部訓練樣本擬合出新的SVM模型,然后用這一模型對驗證集中的樣本單元進行預測,并給出錯分個數。在本例中,調和后的模型輕微減少了錯分個數(從7減少到6)。一般來說,為SVM模型選取調和參數通常可以得到更好的結果。
選擇預測效果最好的解
涉及一個分類器的敏感度(sensitivity)、特異性(sensitivity)、正例命中率(positive predictive power)和負例命中率(negative predictive power)。
通過比較上述值在不同模型中的表現,可以得出最佳模型。
predict() 函數可以估計一個樣本單元為惡性組織的概率。如果這一概率值大于0.5,則分類器會把這一樣本單元判為惡性。這個0.5即閾值(threshold)或門檻值(cutoff value)。通過變動這一閾值,我們可以通過犧牲分類器的特異性來增加其敏感度。這同樣適用于決策樹、隨機森林和支持向量機(盡管語句寫法上會有差別)。
變動閾值可能帶來的影響可以通過ROC(Receiver Operating Characteristic)曲線來進一步觀察。ROC曲線可對一個區間內的門檻值畫出特異性和敏感度之間的關系,然后我們就能針對特定問題選擇特異性和敏感度的最佳組合。許多R包都可以畫ROC曲線,如 ROCR 、 pROC 等。
ROC & AUC curve with pROC
The best threshold is of as high as possible true positive rate while as low as possible false positive rate. This decision made through the graph is the point the farest away from the green line.The better method decided by the AUC value, the higher the better. Through the graph, the red one is better than the green one.pROC的roc()函數的兩個主要參數response和predictor由原始數據或者待測數據的分類結果表示,默認為0為control而1為case。如果為多分類變量,則要用levels指明control和case。predictor則為probability分布值。在logistic regression中為fit$fitted.value,在randomForest中為fit$votes[,]的具體列。另外,需要指出的是這兩個參數必須長度相等。而回歸分析往往對NA值敏感,會自動剔除,導致長度不等,因此需要注意。
library(pROC) library(randomForest) fit.logit <- glm(class~., data = df.train, family = binomial()) # the variable values and fitted.value stored in fit.logit into the lines() functionfit.rf <- randomForest(class~., data = df.train, importance = TRUE,na.action = na.roughfix) roc(df.train$class, fit.logit$fitted.value,plot=TRUE, legacy.axes = TRUE, percent = TRUE,print.auc = TRUE, col = "red") # 這里理論上會報錯,因為進行glm()回歸時,含有NA值的那一行會自動忽略 # 因此最后response和predictor的length是不一樣的。 # 這種情況下應該用df.train[complete.cases(df.train),]$class # legacy.axes = TRUE表示1-specificity # percent = TRUE表示用百分數表示 # print.auc = TRUE顯示曲線下面積# 比較兩種方法的ROC曲線,繪制于同一圖像上 plot.roc(df.train$class, fit.rf$votes[,1], percent = TRUE, print.auc = TRUE, add = TRUE, levels = c(control = "Benign", case = "Malignant"), col = "blue") # randomForest對NA不敏感 # print.auc默認打印在中間,如果要調整,選用print.auc.y或者print.auc.x = 40(百分比位置) # levels = 設置class的類型便于取舍 # 多種方法時要區分col# legend legend("bottomright", legend = c("Logistic regression", "Random Forest"),col = c("red", "blue"), lwd =4) # 添加Legend時保持顏色一致對于更改兩邊margin,可以用par(pty = "s")。默認“m”更新于
2020-10-27
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的c++实现决策树分类汽车评估数据集_R有监督机器学习-分类方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP-PDO参数绑定问题
- 下一篇: 论文写作——texstudio+texl