對(duì)于訓(xùn)練集數(shù)據(jù),我們通過訓(xùn)練若干個(gè)個(gè)體學(xué)習(xí)器叫潦,通過一定的結(jié)合策略,就可以最終形成一個(gè)強(qiáng)學(xué)習(xí)器矗蕊,以達(dá)到博采眾長(zhǎng)的目的短蜕。也就是說傻咖,集成學(xué)習(xí)有兩個(gè)主要的問題需要解決朋魔,第一是如何得到若干個(gè)個(gè)體學(xué)習(xí)器卿操,第二是如何選擇一種結(jié)合策略警检,將這些個(gè)體學(xué)習(xí)器集合成一個(gè)強(qiáng)學(xué)習(xí)器害淤。
集成學(xué)習(xí)潛在的思想是即便某一個(gè)弱分類器得到了錯(cuò)誤的預(yù)測(cè)扇雕,其他的弱分類器也可以將錯(cuò)誤糾正回來窥摄。集成學(xué)習(xí)通過構(gòu)建并結(jié)合多個(gè)學(xué)習(xí)器來完成學(xué)習(xí)任務(wù)镶奉,有時(shí)也被稱為多分類器系統(tǒng)、基于委員會(huì)的學(xué)習(xí)等哨苛。
對(duì)于個(gè)體學(xué)習(xí)器的選擇鸽凶,我們有兩種選擇:第一種就是所有的個(gè)體學(xué)習(xí)器都是一個(gè)種類的建峭,或者說是同質(zhì)的玻侥。比如都是決策樹個(gè)體學(xué)習(xí)器亿蒸,或者都是神經(jīng)網(wǎng)絡(luò)個(gè)體學(xué)習(xí)器凑兰。第二種是所有的個(gè)體學(xué)習(xí)器不全是一個(gè)種類的,或者說是異質(zhì)的票摇。比如我們有一個(gè)分類問題,對(duì)訓(xùn)練集采用支持向量機(jī)個(gè)體學(xué)習(xí)器砚蓬,邏輯回歸個(gè)體學(xué)習(xí)器和樸素貝葉斯個(gè)體學(xué)習(xí)器來學(xué)習(xí),再通過某種結(jié)合策略來確定最終的分類強(qiáng)學(xué)習(xí)器灰蛙。
同質(zhì)個(gè)體學(xué)習(xí)器按照個(gè)體學(xué)習(xí)器之間是否存在依賴關(guān)系可以分為兩類:
第一個(gè)是個(gè)體學(xué)習(xí)器之間存在強(qiáng)依賴關(guān)系,一系列個(gè)體學(xué)習(xí)器基本都需要串行生成摩梧,即:除了訓(xùn)練第一個(gè)之外物延,其他的學(xué)習(xí)器學(xué)習(xí)都需要依賴于前面生成的學(xué)習(xí)的結(jié)果仅父。代表算法是boosting系列算法叛薯,第二個(gè)是個(gè)體學(xué)習(xí)器之間不存在強(qiáng)依賴關(guān)系笙纤,一系列個(gè)體學(xué)習(xí)器可以并行生成耗溜,代表算法是bagging和隨機(jī)森林(Random Forest)系列算法。
一抖拴、bootstrap 自助法統(tǒng)計(jì)簡(jiǎn)介
bootstrap 的核心思想就是對(duì)樣本數(shù)據(jù)再進(jìn)行有放回抽樣,得到新的樣本腥椒,通過不斷重復(fù),得到若干個(gè)新的樣本笼蛛,然后統(tǒng)計(jì)這些樣本數(shù)據(jù)分布情況,來估計(jì)總體的分布情況滨砍。一言以蔽之拉馋,子樣本之于樣本,可以類比樣本之于總體煌茴。舉個(gè)例子:
假設(shè)我們有一個(gè)樣本數(shù)據(jù):30,37蔓腐,36,43龄句,42,43分歇,43傀蓉,46职抡,41葬燎,42缚甩,通過這個(gè)樣本谱净,我們知道樣本的均值為:40.3擅威。但是我們能說總體的均值也是40.3么壕探?或者近似40.3么郊丛? 顯然不能李请。為什么? 因?yàn)槲覀冎挥幸粋€(gè)樣本导盅。一個(gè)樣本并不能去說明總體。但是現(xiàn)在我們就只有這一個(gè)樣本數(shù)據(jù)庆猫,那我們要想推斷總體的均值,要這么辦呢月培? 一個(gè)辦法就是對(duì)這個(gè)樣本再進(jìn)行抽樣,我們每次抽1個(gè)數(shù)杉畜,然后返回纪蜒,然后再抽此叠,總共抽10次纯续,得到一個(gè)新的樣本:
43,43猬错,42窗看,37倦炒,42显沈,36逢唤,43拉讯,41鳖藕,46魔慷,42,這個(gè)樣本的均值為:35.7院尔。
重復(fù)上述過程,做20次页滚,我們就可以得到20個(gè)樣本的均值,例如:第二次抽樣為:
36裹驰,41隧熙,43幻林,42,36躏敢,36件余,37遭居,42啼器,43俱萍,43枪蘑, 這個(gè)樣本的均值為:37.4
第三次抽樣結(jié)果為:
46损谦,37,37颅湘,43栗精,43,42级历,41叭披,30,42嚼贡,43,樣本均值為:38.0
……
因?yàn)槲覀兠看味际怯蟹呕爻闃游蠼眩虼说玫降木刀际遣煌呐场V貜?fù)20次后丙唧,我們得到20個(gè)不同的均值想际。我們計(jì)算出這20個(gè)均值相對(duì)于原始樣本均值之間的差沼琉,并且按從小到大排序。而對(duì)于樣本的抽樣得到的新樣本均值打瘪,與原始樣本均值之間的偏離程度彩扔,與原始樣本與總體樣本之間的偏離程度近視相等虫碉。于是我們就可以大致知道總體樣本的置信區(qū)間敦捧。
二兢卵、裝袋(Bagging)
bagging 是一種個(gè)體學(xué)習(xí)器之間不存在強(qiáng)依賴關(guān)系秽荤、可同時(shí)生成的并行式集成學(xué)習(xí)方法窃款。在Bagging算法中烟阐,模型集合中的每個(gè)成員學(xué)習(xí)器均從不同的訓(xùn)練集得到踱稍,而每個(gè)訓(xùn)練集通過bootstrapping方法得到珠月。即給定包含n個(gè)樣本的數(shù)據(jù)集啤挎,先隨機(jī)從樣本中取出一個(gè)樣本放入采樣集中庆聘,再把該樣本返回初始數(shù)據(jù)集象对,使得下次采樣時(shí)該樣本仍可以被選中宴抚,這樣冠绢,經(jīng)過m次隨機(jī)采樣操作弟胀,就可以得到包含m個(gè)樣本的采樣集孵户,初始數(shù)據(jù)集中有的樣本多次出現(xiàn)夏哭,有的則未出現(xiàn)。每個(gè)樣本不被選中的概率為:酱固。 當(dāng)n和m都非常大時(shí)运悲,比如n=m=10000希停,一個(gè)樣本不被選中的概率p = 36.8%署隘。因此一個(gè)bootstrap約包含原樣本63.2%违崇,約36.8%的樣本未被選中羞延。
照上面的方式進(jìn)行T次操作伴箩,采樣出T個(gè)含有m個(gè)訓(xùn)練集的采樣集鄙漏,然后基于每個(gè)采樣集訓(xùn)練出T個(gè)基學(xué)習(xí)器,再將這些基學(xué)習(xí)器進(jìn)行結(jié)合鞠值,即可得到集成學(xué)習(xí)器彤恶。在對(duì)輸出進(jìn)行預(yù)測(cè)時(shí)声离,Bagging通常對(duì)分類進(jìn)行簡(jiǎn)單投票法术徊,對(duì)回歸使用簡(jiǎ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
#劃分測(cè)試集和訓(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個(gè)小型的訓(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ù)序號(hào),找到對(duì)應(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個(gè)邏輯回歸的結(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) ######################################
#對(duì)每個(gè)數(shù)據(jù),計(jì)算11個(gè)模型的預(yù)測(cè)結(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類似省艳,只是是一個(gè)多變量版本跋炕。simplify=True表示返回一個(gè)矩陣辐烂,否則返回一個(gè)list
#由于glm_predictions需要傳遞多個(gè)參數(shù)纠修,因此需要用mapply扣草。
training_predictions <- mapply(glm_predictions,models,bags,1:M,SIMPLIFY=F)
training_predictions
#Reduce函數(shù)是將每次計(jì)算后的結(jié)果保留辰妙,并與下一個(gè)數(shù)字進(jìn)行計(jì)算甫窟,這是和 apply 函數(shù)不同的
#這里是利用reduce函數(shù)將多個(gè)數(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é)果投票,對(duì)于每一行敬锐,去掉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
#計(jì)算精度(準(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ù)測(cè)(類似于測(cè)試集)
get_1oo_bag <- function(sample_indices) {
#注意椿疗,這里的n是個(gè)全局變量届榄,上面定義過铝条,是心臟病數(shù)據(jù)的行數(shù)。
#使用setdiff班挖,將統(tǒng)計(jì)從1到n的數(shù)中與sample_indices不同的數(shù)给梅,得到袋外數(shù)據(jù)的行號(hào)
unique_sample <- setdiff(1:n,unique(sample_indices));
df <- heart_train[unique_sample,]; #根據(jù)行號(hào)取出這些數(shù)據(jù)
df$ID <- unique_sample; #賦予一個(gè)ID
#如果ECG的數(shù)量小于3动羽,則將ECG=1的數(shù)據(jù)的全部變?yōu)镹A运吓。
#因?yàn)镋CG=1的數(shù)量很少疯趟,很有可能訓(xùn)練集的時(shí)候沒有倦青。導(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)
#對(duì)每個(gè)數(shù)據(jù)逃糟,計(jì)算11個(gè)模型的預(yù)測(cè)結(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))
#再來使用測(cè)試集進(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))
#可以看到菇肃,測(cè)試集上的精度為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)練出一個(gè)弱學(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個(gè)弱學(xué)習(xí)器通過集合策略進(jìn)行整合串述,得到最終的強(qiáng)學(xué)習(xí)器纲酗。
即:原始數(shù)據(jù)集 -> 某種算法擬合觅赊,會(huì)產(chǎn)生錯(cuò)誤 -> 根據(jù)上個(gè)模型預(yù)測(cè)結(jié)果吮螺,更新樣本點(diǎn)權(quán)重萝风。預(yù)測(cè)錯(cuò)誤的結(jié)果權(quán)重增大 -> 再次使用更新了權(quán)重后的數(shù)據(jù)進(jìn)行模型的擬合规惰,得到一個(gè)新的模型 -> 重復(fù)上述過程泉蝌,繼續(xù)重點(diǎn)訓(xùn)練錯(cuò)誤的預(yù)測(cè)樣本點(diǎn)勋陪。每一次生成的子模型,都是在生成擬合結(jié)果更好的模型(用的數(shù)據(jù)點(diǎn)都是相同的寒锚,但是樣本點(diǎn)具有不同的權(quán)重值)。
注意:增強(qiáng)每次都會(huì)用上所有的觀測(cè)數(shù)據(jù)腮郊,而裝袋只是抽樣了部分?jǐn)?shù)據(jù)轧飞;
AdaBoost:
AdaBoost 是Boosting中的經(jīng)典算法过咬,其主要應(yīng)用于二分類問題(打當(dāng)然也可以用于回歸問題掸绞。但是主要是用于二分類問題)。Adaboost 算法采用調(diào)整樣本權(quán)重的方式來對(duì)樣本分布進(jìn)行調(diào)整俺抽,即提高前一輪個(gè)體學(xué)習(xí)器錯(cuò)誤分類的樣本的權(quán)重,而降低那些正確分類的樣本的權(quán)重捷犹,這樣就能使得錯(cuò)誤分類的樣本可以受到更多的關(guān)注萍歉,從而在下一輪中可以正確分類,使得分類問題被一系列的弱分類器“分而治之”销凑。對(duì)于組合方式仅炊,AdaBoost采用加權(quán)多數(shù)表決的方法蜕窿,具體地桐经,加大分類誤差率小的若分類器的權(quán)值阴挣,減小分類誤差率大的若分類器的權(quán)值纺腊,從而調(diào)整他們?cè)诒頉Q中的作用揖膜。
我們從上面的表述可以看到拜隧,這里有兩個(gè)權(quán)重:
一個(gè)是樣本的權(quán)重(每一個(gè)樣本的權(quán)重我們記為:洪添,整體樣本的權(quán)重集合記為:D): 樣本權(quán)重越大薇组,代表這個(gè)樣本被正確分類的要求越高,可能性越大貌矿。
一個(gè)是基分類器的權(quán)重(我們記為:):一個(gè)分類器逛漫,分類的正確率越高,其權(quán)重越大,代表其在最后投票時(shí)所占的比重越大菩暗。
下面這張圖對(duì)Ada-boost做了恰當(dāng)?shù)慕忉專?/p>
- Box 1: 你可以看到我們假設(shè)所有的數(shù)據(jù)點(diǎn)有相同的權(quán)重(正號(hào)停团、負(fù)號(hào)的大小都一樣),并用一個(gè)決策樹樁D1將它們分為兩部分舌胶。我們可以看到這個(gè)決策樹樁將其中的三個(gè)正號(hào)標(biāo)記的數(shù)據(jù)點(diǎn)分類錯(cuò)誤岗屏,因此我們將這三個(gè)點(diǎn)賦予更大的權(quán)重交由下一個(gè)預(yù)測(cè)樹樁進(jìn)行分類婉烟。
- Box 2: 在這里你可以看到三個(gè)未被正確分類的(+)號(hào)的點(diǎn)的權(quán)重變大暇屋。在這種情況下咐刨,第二個(gè)決策樹樁D2試圖將這三個(gè)錯(cuò)誤的點(diǎn)準(zhǔn)確的分類定鸟,但是這又引起新的分類錯(cuò)誤啼县,將三個(gè)(-)號(hào)標(biāo)記的點(diǎn)識(shí)別錯(cuò)誤季眷,因此在下一次分類中子刮,這三個(gè)(-)號(hào)標(biāo)記的點(diǎn)被賦予更大的權(quán)重葵孤。
- Box 3: 在這里三個(gè)被錯(cuò)誤分類的(-)號(hào)標(biāo)記的點(diǎn)被賦予更大的權(quán)重橱赠,利用決策樹樁D3進(jìn)行新的分類病线,這時(shí)候又產(chǎn)生了新的分類錯(cuò)誤送挑,圖中用小圓圈圈起來的一個(gè)負(fù)號(hào)點(diǎn)和兩個(gè)正號(hào)點(diǎn)
- Box 4: 在這里惕耕,我們將D1司澎、D2和D3三個(gè)決策器組合起來形成一個(gè)復(fù)雜的規(guī)則挤安,你可以看到這個(gè)組合后的決策器比它們?nèi)魏我粋€(gè)弱分類器表現(xiàn)的都足夠好蛤铜。
那么我們?nèi)绾蝸泶_定樣本的權(quán)重 和分類器的權(quán)重呢围肥?AdaBoost采用如下算法:
1)初始時(shí)穆刻,假設(shè)所有樣本的權(quán)重都相等,假設(shè)都為:氢伟,N代表樣本的數(shù)量;
2)使用初始權(quán)重和初始數(shù)據(jù)訓(xùn)練第一個(gè)基學(xué)習(xí)器;
3)根據(jù)樣本數(shù)據(jù)设褐,計(jì)算的誤差率助析。對(duì)于一個(gè)二元分類問題來說外冀,誤差率的計(jì)算公式為分類錯(cuò)誤數(shù)量除以樣本總數(shù)雪隧,即:脑沿。如果帶上樣本權(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ù)公式: 得到分類器的權(quán)重韭邓。可以看到瞭郑,誤差率越大, 越小苇本,也越小瓣窄。
5)更新樣本的權(quán)值:
若分類正確笛厦,則新的權(quán)值為:,以減少權(quán)重俺夕;
若分類錯(cuò)誤裳凸,則新的權(quán)值為:贱鄙,以增大權(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)最終輸出模型:
在R中實(shí)現(xiàn)AdaBoost算法如下:
#預(yù)測(cè)大氣中伽馬射線的輻射(根據(jù)一系列特征來判斷是放射性泄漏污染還是常規(guī)背景輻射)
#輸出(class)是二元的哼拔,g代表是伽馬射線,b代表背景輻射瓣颅【胫穑總共有19020條數(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)練集和測(cè)試集
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]
#對(duì)數(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個(gè)神經(jīng)元的神經(jīng)網(wǎng)絡(luò)進(jìn)行擬合
library(nnet)
n_model <- nnet(CLASS~., data = magic_train_df_pp, size = 1)
#在測(cè)試集上進(jìn)行預(yù)測(cè)
n_test_predictions = predict(n_model, magic_test_pp, type = "class")
#計(jì)算測(cè)試集上的精度(精度為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ū)別。
#在一個(gè)函數(shù)中斋荞,如果一個(gè)包不存在荞雏,執(zhí)行到library將會(huì)停止執(zhí)行,require則會(huì)繼續(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個(gè)隱藏單元的神經(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ù)測(cè)蜈彼,預(yù)測(cè)后將結(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,分類錯(cuò)誤返回true
#as.numeric(errors)幸逆,會(huì)是的分類正確變?yōu)?棍辕,分類錯(cuò)誤變?yōu)?。然后再乘以權(quán)重还绘,相當(dāng)于是把所有分類錯(cuò)誤的權(quán)重求和楚昭,再除以全部的權(quán)重之和,得到錯(cuò)誤率拍顷。
error_rate<-sum(w*as.numeric(errors))/sum(w)
alpha<-0.5*log((1-error_rate)/error_rate) #計(jì)算分類器的權(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)重抚太,對(duì)測(cè)試集上的所有數(shù)據(jù),應(yīng)用所有模型,根據(jù)模型的權(quán)重尿贫,進(jìn)行綜合評(píng)分电媳。
AdaBoostNN.predict<-function(ada_model,test_data) {
models<-ada_model$models #獲得模型列表
alphas<-ada_model$alphas #獲得模型權(quán)重列表
#針對(duì)每一個(gè)模型,使用測(cè)試集數(shù)據(jù)進(jìn)行預(yù)測(cè)匾乓,結(jié)果是一個(gè)矩陣。每一行是一個(gè)模型預(yù)測(cè)的結(jié)果又谋。每一列是測(cè)試集的每一條觀測(cè)數(shù)據(jù)的預(yù)測(cè)值
prediction_matrix<-sapply(models,function (x) as.numeric(predict(x,test_data,type = "class")))
#對(duì)每一行數(shù)據(jù)拼缝,乘以其對(duì)應(yīng)的權(quán)重值
weighted_predictions<-t(apply(prediction_matrix,1,function (x) mapply(function(y,z) y*z,x,alphas)))
#對(duì)權(quán)值化后的預(yù)測(cè)值求和后,判斷正負(fù)號(hào)
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ì)算仁卷,而是隨機(jī)取全部特征的一些子集來參與運(yùn)算。這樣的抽樣步驟可以強(qiáng)制讓裝袋的樹結(jié)構(gòu)互不同犬第,可以有效避免因?yàn)闃颖颈旧碛衅钏鶐淼哪P驮跍y(cè)試集上效果不理想的問題锦积。
森林很好理解。一樹成木歉嗓,百樹成林丰介。當(dāng)我們使用了多棵決策樹時(shí),自然也就了森林鉴分。
我們用R來比較幾種算法的優(yōu)劣(決策樹哮幢、bagging、隨機(jī)森林志珍、回歸決策樹):
#計(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),] #去除空行
#劃分測(cè)試集和訓(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)
#在測(cè)試集上進(jìn)行預(yù)測(cè)
regtree_predictions = predict(regtree, skillcraft_test)
(regtree_SSE <- compute_SSE(regtree_predictions, skillcraft_test$LeagueIndex))#評(píng)判測(cè)試集上的預(yù)測(cè)效果
#baggin函數(shù)默認(rèn)使用rpart包的決策樹方法來進(jìn)行分類
#coob=T表示用袋外樣本來評(píng)估誤差
#nbagg表示用100個(gè)袋子橙垢,也就是生成100個(gè)小的模型
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ù)測(cè)
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ù)測(cè)
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原理