機(jī)器學(xué)習(xí)(八)——集成學(xué)習(xí)

對于訓(xùn)練集數(shù)據(jù),我們通過訓(xùn)練若干個個體學(xué)習(xí)器,通過一定的結(jié)合策略,就可以最終形成一個強(qiáng)學(xué)習(xí)器,以達(dá)到博采眾長的目的。也就是說,集成學(xué)習(xí)有兩個主要的問題需要解決,第一是如何得到若干個個體學(xué)習(xí)器,第二是如何選擇一種結(jié)合策略,將這些個體學(xué)習(xí)器集合成一個強(qiáng)學(xué)習(xí)器。
集成學(xué)習(xí)潛在的思想是即便某一個弱分類器得到了錯誤的預(yù)測,其他的弱分類器也可以將錯誤糾正回來。集成學(xué)習(xí)通過構(gòu)建并結(jié)合多個學(xué)習(xí)器來完成學(xué)習(xí)任務(wù),有時也被稱為多分類器系統(tǒng)、基于委員會的學(xué)習(xí)等。

對于個體學(xué)習(xí)器的選擇,我們有兩種選擇:第一種就是所有的個體學(xué)習(xí)器都是一個種類的,或者說是同質(zhì)的。比如都是決策樹個體學(xué)習(xí)器,或者都是神經(jīng)網(wǎng)絡(luò)個體學(xué)習(xí)器。第二種是所有的個體學(xué)習(xí)器不全是一個種類的,或者說是異質(zhì)的。比如我們有一個分類問題,對訓(xùn)練集采用支持向量機(jī)個體學(xué)習(xí)器,邏輯回歸個體學(xué)習(xí)器和樸素貝葉斯個體學(xué)習(xí)器來學(xué)習(xí),再通過某種結(jié)合策略來確定最終的分類強(qiáng)學(xué)習(xí)器。

同質(zhì)個體學(xué)習(xí)器按照個體學(xué)習(xí)器之間是否存在依賴關(guān)系可以分為兩類:
第一個是個體學(xué)習(xí)器之間存在強(qiáng)依賴關(guān)系,一系列個體學(xué)習(xí)器基本都需要串行生成,即:除了訓(xùn)練第一個之外,其他的學(xué)習(xí)器學(xué)習(xí)都需要依賴于前面生成的學(xué)習(xí)的結(jié)果。代表算法是boosting系列算法,第二個是個體學(xué)習(xí)器之間不存在強(qiáng)依賴關(guān)系,一系列個體學(xué)習(xí)器可以并行生成,代表算法是bagging和隨機(jī)森林(Random Forest)系列算法。

一、bootstrap 自助法統(tǒng)計簡介

bootstrap 的核心思想就是對樣本數(shù)據(jù)再進(jìn)行有放回抽樣,得到新的樣本,通過不斷重復(fù),得到若干個新的樣本,然后統(tǒng)計這些樣本數(shù)據(jù)分布情況,來估計總體的分布情況。一言以蔽之,子樣本之于樣本,可以類比樣本之于總體。舉個例子:
假設(shè)我們有一個樣本數(shù)據(jù):30,37,36,43,42,43,43,46,41,42,通過這個樣本,我們知道樣本的均值為:40.3。但是我們能說總體的均值也是40.3么?或者近似40.3么? 顯然不能。為什么? 因?yàn)槲覀冎挥幸粋€樣本。一個樣本并不能去說明總體。但是現(xiàn)在我們就只有這一個樣本數(shù)據(jù),那我們要想推斷總體的均值,要這么辦呢? 一個辦法就是對這個樣本再進(jìn)行抽樣,我們每次抽1個數(shù),然后返回,然后再抽,總共抽10次,得到一個新的樣本:
43,43,42,37,42,36,43,41,46,42,這個樣本的均值為:35.7。
重復(fù)上述過程,做20次,我們就可以得到20個樣本的均值,例如:第二次抽樣為:
36,41,43,42,36,36,37,42,43,43, 這個樣本的均值為:37.4
第三次抽樣結(jié)果為:
46,37,37,43,43,42,41,30,42,43,樣本均值為:38.0
……
因?yàn)槲覀兠看味际怯蟹呕爻闃?,因此得到的均值都是不同的。重?fù)20次后,我們得到20個不同的均值。我們計算出這20個均值相對于原始樣本均值之間的差,并且按從小到大排序。而對于樣本的抽樣得到的新樣本均值,與原始樣本均值之間的偏離程度,與原始樣本與總體樣本之間的偏離程度近視相等。于是我們就可以大致知道總體樣本的置信區(qū)間。

二、裝袋(Bagging)

bagging 是一種個體學(xué)習(xí)器之間不存在強(qiáng)依賴關(guān)系、可同時生成的并行式集成學(xué)習(xí)方法。在Bagging算法中,模型集合中的每個成員學(xué)習(xí)器均從不同的訓(xùn)練集得到,而每個訓(xùn)練集通過bootstrapping方法得到。即給定包含n個樣本的數(shù)據(jù)集,先隨機(jī)從樣本中取出一個樣本放入采樣集中,再把該樣本返回初始數(shù)據(jù)集,使得下次采樣時該樣本仍可以被選中,這樣,經(jīng)過m次隨機(jī)采樣操作,就可以得到包含m個樣本的采樣集,初始數(shù)據(jù)集中有的樣本多次出現(xiàn),有的則未出現(xiàn)。每個樣本不被選中的概率為:p = (1-\frac{1}{n})^m。 當(dāng)n和m都非常大時,比如n=m=10000,一個樣本不被選中的概率p = 36.8%。因此一個bootstrap約包含原樣本63.2%,約36.8%的樣本未被選中。
照上面的方式進(jìn)行T次操作,采樣出T個含有m個訓(xùn)練集的采樣集,然后基于每個采樣集訓(xùn)練出T個基學(xué)習(xí)器,再將這些基學(xué)習(xí)器進(jìn)行結(jié)合,即可得到集成學(xué)習(xí)器。在對輸出進(jìn)行預(yù)測時,Bagging通常對分類進(jìn)行簡單投票法,對回歸使用簡單平均法。若出現(xiàn)形同,則任選其一。

在R中手工實(shí)現(xiàn)裝袋的例子如下:

heart <- read.table("heart.dat", quote="\"")
names(heart) <- c("AGE", "SEX", "CHESTPAIN", "RESTBP", "CHOL", "SUGAR", "ECG",
                  "MAXHR", "ANGINA", "DEP", "EXERCISE", "FLUOR", "THAL", "OUTPUT")
#將一些特征因子化
heart$CHESTPAIN = factor(heart$CHESTPAIN)
heart$ECG = factor(heart$ECG)
heart$THAL = factor(heart$THAL)
heart$EXERCISE = factor(heart$EXERCISE)

#將輸出范圍限制在0和1之間(之前是1和2)
heart$OUTPUT = heart$OUTPUT-1

#劃分測試集和訓(xùn)練集
library(caret)
set.seed(987954)
heart_sampling_vector <- createDataPartition(heart$OUTPUT, p = 0.85, list = FALSE)
heart_train <- heart[heart_sampling_vector,]
heart_train_labels <- heart$OUTPUT[heart_sampling_vector]
heart_test <- heart[-heart_sampling_vector,]
heart_test_labels <- heart$OUTPUT[-heart_sampling_vector]

#準(zhǔn)備訓(xùn)練11個小型的訓(xùn)練集
M <- 11
seeds <- 70000 : (70000 + M - 1)
n <- nrow(heart_train) #心臟病數(shù)據(jù)的行數(shù)
#每次抽n行,有放回的抽樣。抽M次。(抽出來的是行數(shù))
sample_vectors<-sapply(seeds,function(x) { set.seed(x); return(sample(n,n,replace=T)) })
#根據(jù)序號,找到對應(yīng)的數(shù)據(jù),放到glm函數(shù)中進(jìn)行邏輯回歸
train_1glm <- function(sample_indices) { 
    data <- heart_train[sample_indices,]; 
    model <- glm(OUTPUT~., data=data, family=binomial("logit")); 
    return(model)
}
#生成11個邏輯回歸的結(jié)果
models <- apply(sample_vectors, 2, train_1glm)##################################

#取出抽樣數(shù)據(jù)中的非重復(fù)數(shù)據(jù)
get_1bag <- function(sample_indices) {
    unique_sample <- unique(sample_indices); 
    df <- heart_train[unique_sample, ]; 
    df$ID <- unique_sample; 
    return(df)
}
bags<-apply(sample_vectors, 2, get_1bag) ######################################

#對每個數(shù)據(jù),計算11個模型的預(yù)測結(jié)果
glm_predictions <- function(model, data, model_index) {
    colname <- paste("PREDICTIONS",model_index);
    data[colname] <- as.numeric(predict(model,data,type="response") > 0.5); 
    return(data[,c("ID",colname), drop=FALSE])
}
#mapply跟sapply類似,只是是一個多變量版本。simplify=True表示返回一個矩陣,否則返回一個list
#由于glm_predictions需要傳遞多個參數(shù),因此需要用mapply。
training_predictions <- mapply(glm_predictions,models,bags,1:M,SIMPLIFY=F)
training_predictions
#Reduce函數(shù)是將每次計算后的結(jié)果保留,并與下一個數(shù)字進(jìn)行計算,這是和 apply 函數(shù)不同的
#這里是利用reduce函數(shù)將多個數(shù)據(jù)框按照同一列merge
train_pred_df <- Reduce(function(x, y) merge(x, y, by = "ID", all = T), training_predictions)
train_pred_df
#進(jìn)行結(jié)果投票,對于每一行,去掉ID列后,求這一行的均值。若均值大于0.5,則返回1,否則返回0
train_pred_vote<-apply(train_pred_df[,-1],1,function(x) as.numeric(mean(x,na.rm=TRUE)>0.5))
train_pred_vote
#計算精度(準(zhǔn)確度),準(zhǔn)確度從原來的0.86提高到0.88
(training_accuracy<-mean(train_pred_vote==heart_train$OUTPUT[as.numeric(train_pred_df$ID)]))


# 用袋外數(shù)據(jù)來進(jìn)行預(yù)測(類似于測試集)
get_1oo_bag <- function(sample_indices) {
  #注意,這里的n是個全局變量,上面定義過,是心臟病數(shù)據(jù)的行數(shù)。
  #使用setdiff,將統(tǒng)計從1到n的數(shù)中與sample_indices不同的數(shù),得到袋外數(shù)據(jù)的行號
    unique_sample <- setdiff(1:n,unique(sample_indices)); 
    df <- heart_train[unique_sample,];  #根據(jù)行號取出這些數(shù)據(jù)
    df$ID <- unique_sample;  #賦予一個ID
    #如果ECG的數(shù)量小于3,則將ECG=1的數(shù)據(jù)的全部變?yōu)镹A。
    #因?yàn)镋CG=1的數(shù)量很少,很有可能訓(xùn)練集的時候沒有。導(dǎo)致模型沒有見過ECG的特征,所以將ECG判定為NA,忽略此特征
    if (length(unique(heart_train[sample_indices,]$ECG)) < 3) df[df$ECG == 1,"ECG"] = NA; 
    return(df)
}
#獲得帶外數(shù)據(jù)
oo_bags <- apply(sample_vectors,2, get_1oo_bag)
#對每個數(shù)據(jù),計算11個模型的預(yù)測結(jié)果
oob_predictions<-mapply(glm_predictions, models, oo_bags, 1:M,SIMPLIFY=F)
#合并數(shù)據(jù)
oob_pred_df <- Reduce(function(x, y) merge(x, y, by="ID", all=T), oob_predictions)
#投票
oob_pred_vote<-apply(oob_pred_df[,-1], 1, function(x) as.numeric(mean(x,na.rm=TRUE)>0.5))
#查看投票后的精度(袋外數(shù)據(jù)的精度為0.8)
(oob_accuracy<-mean(oob_pred_vote==heart_train$OUTPUT[as.numeric(oob_pred_df$ID)],na.rm=TRUE))

#再來使用測試集進(jìn)行驗(yàn)證
get_1test_bag <- function(sample_indices) {
     df <- heart_test; 
     df$ID <- row.names(df); 
     if (length(unique(heart_train[sample_indices,]$ECG)) < 3) 
         df[df$ECG == 1,"ECG"] = NA; 
     return(df)
}

test_bags <- apply(sample_vectors,2, get_1test_bag)
test_predictions<-mapply(glm_predictions, models, test_bags, 1 : M, SIMPLIFY = F)
test_pred_df <- Reduce(function(x, y) merge(x, y, by="ID",all=T), test_predictions)
test_pred_vote<-apply(test_pred_df[,-1],1,function(x) as.numeric(mean(x,na.rm=TRUE)>0.5))
#可以看到,測試集上的精度為0.925
(test_accuracy<-mean(test_pred_vote==heart_test[test_pred_df$ID,"OUTPUT"],na.rm=TRUE))

三、增強(qiáng)(Boosting)

Boosting算法的工作機(jī)制是首先從訓(xùn)練集用初始權(quán)重訓(xùn)練出一個弱學(xué)習(xí)器1,根據(jù)弱學(xué)習(xí)的學(xué)習(xí)誤差率表現(xiàn)來更新訓(xùn)練樣本的權(quán)重,使得之前弱學(xué)習(xí)器1學(xué)習(xí)誤差率高的訓(xùn)練樣本點(diǎn)的權(quán)重變高,使得這些誤差率高的點(diǎn)在后面的弱學(xué)習(xí)器2中得到更多的重視。然后基于調(diào)整權(quán)重后的訓(xùn)練集來訓(xùn)練弱學(xué)習(xí)器2.,如此重復(fù)進(jìn)行,直到弱學(xué)習(xí)器數(shù)達(dá)到事先指定的數(shù)目T,最終將這T個弱學(xué)習(xí)器通過集合策略進(jìn)行整合,得到最終的強(qiáng)學(xué)習(xí)器。


即:原始數(shù)據(jù)集 -> 某種算法擬合,會產(chǎn)生錯誤 -> 根據(jù)上個模型預(yù)測結(jié)果,更新樣本點(diǎn)權(quán)重。預(yù)測錯誤的結(jié)果權(quán)重增大 -> 再次使用更新了權(quán)重后的數(shù)據(jù)進(jìn)行模型的擬合,得到一個新的模型 -> 重復(fù)上述過程,繼續(xù)重點(diǎn)訓(xùn)練錯誤的預(yù)測樣本點(diǎn)。每一次生成的子模型,都是在生成擬合結(jié)果更好的模型(用的數(shù)據(jù)點(diǎn)都是相同的,但是樣本點(diǎn)具有不同的權(quán)重值)。

注意:增強(qiáng)每次都會用上所有的觀測數(shù)據(jù),而裝袋只是抽樣了部分?jǐn)?shù)據(jù);

AdaBoost:
AdaBoost 是Boosting中的經(jīng)典算法,其主要應(yīng)用于二分類問題(打當(dāng)然也可以用于回歸問題。但是主要是用于二分類問題)。Adaboost 算法采用調(diào)整樣本權(quán)重的方式來對樣本分布進(jìn)行調(diào)整,即提高前一輪個體學(xué)習(xí)器錯誤分類的樣本的權(quán)重,而降低那些正確分類的樣本的權(quán)重,這樣就能使得錯誤分類的樣本可以受到更多的關(guān)注,從而在下一輪中可以正確分類,使得分類問題被一系列的弱分類器“分而治之”。對于組合方式,AdaBoost采用加權(quán)多數(shù)表決的方法,具體地,加大分類誤差率小的若分類器的權(quán)值,減小分類誤差率大的若分類器的權(quán)值,從而調(diào)整他們在表決中的作用。

我們從上面的表述可以看到,這里有兩個權(quán)重:
一個是樣本的權(quán)重(每一個樣本的權(quán)重我們記為:w_i,整體樣本的權(quán)重集合記為:D): 樣本權(quán)重越大,代表這個樣本被正確分類的要求越高,可能性越大。
一個是基分類器的權(quán)重(我們記為:\alpha):一個分類器,分類的正確率越高,其權(quán)重越大,代表其在最后投票時所占的比重越大。

下面這張圖對Ada-boost做了恰當(dāng)?shù)慕忉專?/p>

  • Box 1: 你可以看到我們假設(shè)所有的數(shù)據(jù)點(diǎn)有相同的權(quán)重(正號、負(fù)號的大小都一樣),并用一個決策樹樁D1將它們分為兩部分。我們可以看到這個決策樹樁將其中的三個正號標(biāo)記的數(shù)據(jù)點(diǎn)分類錯誤,因此我們將這三個點(diǎn)賦予更大的權(quán)重交由下一個預(yù)測樹樁進(jìn)行分類。
  • Box 2: 在這里你可以看到三個未被正確分類的(+)號的點(diǎn)的權(quán)重變大。在這種情況下,第二個決策樹樁D2試圖將這三個錯誤的點(diǎn)準(zhǔn)確的分類,但是這又引起新的分類錯誤,將三個(-)號標(biāo)記的點(diǎn)識別錯誤,因此在下一次分類中,這三個(-)號標(biāo)記的點(diǎn)被賦予更大的權(quán)重。
  • Box 3: 在這里三個被錯誤分類的(-)號標(biāo)記的點(diǎn)被賦予更大的權(quán)重,利用決策樹樁D3進(jìn)行新的分類,這時候又產(chǎn)生了新的分類錯誤,圖中用小圓圈圈起來的一個負(fù)號點(diǎn)和兩個正號點(diǎn)
  • Box 4: 在這里,我們將D1、D2和D3三個決策器組合起來形成一個復(fù)雜的規(guī)則,你可以看到這個組合后的決策器比它們?nèi)魏我粋€弱分類器表現(xiàn)的都足夠好。

那么我們?nèi)绾蝸泶_定樣本的權(quán)重w_i 和分類器的權(quán)重\alpha呢?AdaBoost采用如下算法:
1)初始時,假設(shè)所有樣本的權(quán)重都相等,假設(shè)都為:w_i = 1/N,N代表樣本的數(shù)量;
2)使用初始權(quán)重和初始數(shù)據(jù)訓(xùn)練第一個基學(xué)習(xí)器G(x)_1;
3)根據(jù)樣本數(shù)據(jù),計算G(x)_1的誤差率。對于一個二元分類問題來說,誤差率的計算公式為分類錯誤數(shù)量除以樣本總數(shù),即:err = \frac{1}{N}\sum_{i=1}^NI(G(x_i) \ne y_i)。如果帶上樣本權(quán)重,則變?yōu)椋?img class="math-inline" src="https://math.jianshu.com/math?formula=e%5E%7B(m)%7D%20%3D%20%5Cfrac%7B%5Csum_%7Bi%3D1%7D%5ENw_i%5E%7B(m)%7DI(G(x_i)%20%5Cne%20y_i)%7D%7B%5Csum_%7Bi%3D1%7D%5ENw_i%5E%7B(m)%7D%7D" alt="e^{(m)} = \frac{\sum_{i=1}^Nw_i^{(m)}I(G(x_i) \ne y_i)}{\sum_{i=1}^Nw_i^{(m)}}" mathimg="1">
4)根據(jù)公式:\alpha^{(m)}=\frac{1}{2}\ln\frac{1-e^{(m)}}{e^{(m)}} 得到分類器的權(quán)重??梢钥吹?,誤差率e^{(m)}越大,\frac{1-e^{(m)}}{e^{(m)}} 越小,\alpha也越小。
5)更新樣本的權(quán)值:
若分類正確,則新的權(quán)值為:w_i * e^{-\alpha^{(m)}},以減少權(quán)重;
若分類錯誤,則新的權(quán)值為:w_i * e^{\alpha^{(m)}},以增大權(quán)重;
6)將更新后的權(quán)重進(jìn)行歸一化處理,確保所有權(quán)重之和等于1。(即用當(dāng)前權(quán)值除以所有權(quán)值之和);
7)重復(fù)上述步驟2~6,直到基分類器數(shù)量達(dá)到規(guī)定的數(shù)量T為止。
8)最終輸出模型:G(x)=sign(\sum_{m=1}^T\alpha^{(m)}G^{(m)}(x))

在R中實(shí)現(xiàn)AdaBoost算法如下:

#預(yù)測大氣中伽馬射線的輻射(根據(jù)一系列特征來判斷是放射性泄漏污染還是常規(guī)背景輻射)
#輸出(class)是二元的,g代表是伽馬射線,b代表背景輻射??偣灿?9020條數(shù)據(jù)
magic <- read.csv("study/graduate/機(jī)器學(xué)習(xí)/magic04.data", header=FALSE)
names(magic)=c("FLENGTH", "FWIDTH", "FSIZE", "FCONC", "FCONC1", "FASYM", "FM3LONG", 
               "FM3TRANS", "FALPHA", "FDIST", "CLASS")
#把輸出變量進(jìn)行因子化,g轉(zhuǎn)化為1,b變?yōu)?1;
magic$CLASS = as.factor(ifelse(magic$CLASS=='g',1,-1))

#劃分訓(xùn)練集和測試集
set.seed(33711209)
magic_sampling_vector <- createDataPartition(magic$CLASS, p = 0.80, list = FALSE)
magic_train <- magic[magic_sampling_vector,1:10]
magic_train_output <- magic[magic_sampling_vector,11]
magic_test <- magic[-magic_sampling_vector,1:10]
magic_test_output <- magic[-magic_sampling_vector,11]

#對數(shù)據(jù)進(jìn)行歸一化、中心化處理;
magic_pp <- preProcess(magic_train, method = c("center", "scale"))
magic_train_pp <- predict(magic_pp, magic_train)
magic_train_df_pp <- cbind(magic_train_pp, CLASS = magic_train_output)
magic_test_pp <- predict(magic_pp, magic_test)

#使用只有1個神經(jīng)元的神經(jīng)網(wǎng)絡(luò)進(jìn)行擬合
library(nnet)
n_model <- nnet(CLASS~., data = magic_train_df_pp, size = 1)
#在測試集上進(jìn)行預(yù)測
n_test_predictions = predict(n_model, magic_test_pp, type = "class")
#計算測試集上的精度(精度為78.67%)
(n_test_accuracy <- mean(n_test_predictions == magic_test_output))

#構(gòu)造AdaBoost
AdaBoostNN <- function(training_data, output_column, M, hidden_units) {
  #library和require都可以載入包,但二者存在區(qū)別。
  #在一個函數(shù)中,如果一個包不存在,執(zhí)行到library將會停止執(zhí)行,require則會繼續(xù)執(zhí)行。
  require("nnet")
  models<-list()
  alphas<-list()
  n <- nrow(training_data)
  model_formula <- as.formula(paste(output_column,'~.',sep=''))
  w <- rep((1/n),n) #初始化樣本權(quán)重
  for (m in 1:M) {
    #構(gòu)造hidden_units個隱藏單元的神經(jīng)網(wǎng)絡(luò)
    model<-nnet(model_formula,data=training_data,size=hidden_units, weights=w)
    models[[m]]<-model #將構(gòu)造后的模型存入模型列表中
    #先將training_data中,輸出變量去除,然后使用模型進(jìn)行預(yù)測,預(yù)測后將結(jié)果轉(zhuǎn)為數(shù)值型
    predictions<-as.numeric(predict(model,training_data[,-which(names(training_data) == output_column)],type = "class"))
    errors<-predictions!=training_data[,output_column] #如果分類正確,則返回false,分類錯誤返回true
    #as.numeric(errors),會是的分類正確變?yōu)?,分類錯誤變?yōu)?。然后再乘以權(quán)重,相當(dāng)于是把所有分類錯誤的權(quán)重求和,再除以全部的權(quán)重之和,得到錯誤率。
    error_rate<-sum(w*as.numeric(errors))/sum(w)
    alpha<-0.5*log((1-error_rate)/error_rate) #計算分類器的權(quán)重
    alphas[[m]]<-alpha #將分類器的權(quán)重存入列表中
    temp_w<-mapply(function(x,y) if (y) {x*exp(alpha)} else {x*exp(-alpha)},w,errors) #更新樣本權(quán)重
    w<-temp_w/sum(temp_w) #樣本權(quán)重歸一化處理
  }
  return(list(models=models, alphas=unlist(alphas)))  #返回模型列表和模型權(quán)重列表
}

#不同的模型有不同的權(quán)重,對測試集上的所有數(shù)據(jù),應(yīng)用所有模型,根據(jù)模型的權(quán)重,進(jìn)行綜合評分。
AdaBoostNN.predict<-function(ada_model,test_data) {
  models<-ada_model$models #獲得模型列表
  alphas<-ada_model$alphas #獲得模型權(quán)重列表
  #針對每一個模型,使用測試集數(shù)據(jù)進(jìn)行預(yù)測,結(jié)果是一個矩陣。每一行是一個模型預(yù)測的結(jié)果。每一列是測試集的每一條觀測數(shù)據(jù)的預(yù)測值
  prediction_matrix<-sapply(models,function (x) as.numeric(predict(x,test_data,type = "class")))
  #對每一行數(shù)據(jù),乘以其對應(yīng)的權(quán)重值
  weighted_predictions<-t(apply(prediction_matrix,1,function (x) mapply(function(y,z) y*z,x,alphas)))
  #對權(quán)值化后的預(yù)測值求和后,判斷正負(fù)號
  final_predictions<-apply(weighted_predictions,1,function(x) sign(sum(x)))
  return(final_predictions)
}

ada_model <- AdaBoostNN(magic_train_df_pp, 'CLASS', 10, 1)
predictions <- AdaBoostNN.predict(ada_model, magic_test_pp)
mean(predictions == magic_test_output)

四、隨機(jī)森林

隨機(jī)森林=集成方法(bagging)+決策樹
這里的隨機(jī),有兩層含義:1)樣本的隨機(jī)(參考上面的bagging介紹);2)特征的隨機(jī)。注意,隨機(jī)森林不是使用全部的特征來參與計算,而是隨機(jī)取全部特征的一些子集來參與運(yùn)算。這樣的抽樣步驟可以強(qiáng)制讓裝袋的樹結(jié)構(gòu)互不同,可以有效避免因?yàn)闃颖颈旧碛衅钏鶐淼哪P驮跍y試集上效果不理想的問題。
森林很好理解。一樹成木,百樹成林。當(dāng)我們使用了多棵決策樹時,自然也就了森林。

我們用R來比較幾種算法的優(yōu)劣(決策樹、bagging、隨機(jī)森林、回歸決策樹):

#計算SSE
compute_SSE <- function(correct,predictions) {
  return(sum((correct-predictions)^2))
}

skillcraft <- read.csv("SkillCraft1_Dataset.csv")
#數(shù)據(jù)預(yù)處理
skillcraft<-skillcraft[-1]
skillcraft$TotalHours <- factor(skillcraft$TotalHours)
skillcraft$HoursPerWeek <- factor(skillcraft$HoursPerWeek)
skillcraft$Age <- factor(skillcraft$Age)
skillcraft$TotalHours=as.numeric(levels(skillcraft$TotalHours))[skillcraft$TotalHours]
skillcraft$HoursPerWeek=as.numeric(levels(skillcraft$HoursPerWeek))[skillcraft$HoursPerWeek]
skillcraft$Age=as.numeric(levels(skillcraft$Age))[skillcraft$Age]
skillcraft<-skillcraft[complete.cases(skillcraft),] #去除空行

#劃分測試集和訓(xùn)練集
library(caret)
set.seed(133)
skillcraft_sampling_vector <- createDataPartition(skillcraft$LeagueIndex, p = 0.80, list = FALSE)
skillcraft_train <- skillcraft[skillcraft_sampling_vector,]
skillcraft_test <- skillcraft[-skillcraft_sampling_vector,]

#使用rpart進(jìn)行決策樹分類
library(rpart)
regtree <- rpart(LeagueIndex~., data=skillcraft_train)
#在測試集上進(jìn)行預(yù)測
regtree_predictions = predict(regtree, skillcraft_test)
(regtree_SSE <- compute_SSE(regtree_predictions, skillcraft_test$LeagueIndex))#評判測試集上的預(yù)測效果

#baggin函數(shù)默認(rèn)使用rpart包的決策樹方法來進(jìn)行分類
#coob=T表示用袋外樣本來評估誤差
#nbagg表示用100個袋子,也就是生成100個小的模型
library("ipred")
baggedtree <- bagging(LeagueIndex~., data=skillcraft_train, nbagg=100, coob=T)
baggedtree_predictions <- predict(baggedtree, skillcraft_test)
(baggedtree_SSE <- compute_SSE(baggedtree_predictions, skillcraft_test$LeagueIndex))

#使用隨機(jī)森林來進(jìn)行預(yù)測
library("randomForest")
rf<-randomForest(LeagueIndex~., data=skillcraft_train)
rf_predictions = predict(rf,skillcraft_test)
(rf_SSE <- compute_SSE(rf_predictions, skillcraft_test$LeagueIndex))

#使用回歸決策樹來進(jìn)行預(yù)測
library("gbm")
boostedtree <- gbm(LeagueIndex~., data=skillcraft_train, distribution="gaussian", n.trees=10000, shrinkage=0.1)
best.iter <- gbm.perf(boostedtree,method="OOB")
boostedtree_predictions = predict(boostedtree, skillcraft_test,best.iter)
(boostedtree_SSE <- compute_SSE(boostedtree_predictions, skillcraft_test$LeagueIndex))

可以看到,我們使用決策樹,最后的SSE為799,使用bagging,SSE為677,使用隨機(jī)森林,SSE為575,使用GBDT,SSE為549。

【參考資料】
Bootstrap方法詳解——技術(shù)與實(shí)例
Bootstrap詳解
Bootstrap采樣
機(jī)器學(xué)習(xí)之集成學(xué)習(xí)(ensemble learning)
集成學(xué)習(xí)(Ensemble learning)
機(jī)器學(xué)習(xí)算法之Boosting
Boosting算法總結(jié)
AdaBoost算法
隨機(jī)森林算法及其實(shí)現(xiàn)
集成學(xué)習(xí)之Boosting —— Gradient Boosting原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容