《深度學(xué)習(xí)精要(基于R語言)》學(xué)習(xí)筆記
1、過擬合問題概述
機(jī)器學(xué)習(xí)的一個(gè)陷阱是黄橘,越復(fù)雜的數(shù)據(jù)越有可能過擬合訓(xùn)練數(shù)據(jù),因此屈溉,對(duì)相同數(shù)據(jù)訓(xùn)練模型的性能評(píng)價(jià)會(huì)導(dǎo)致有偏的塞关、過度樂觀的模型性能的估計(jì),甚至?xí)绊懙綄?duì)模型的選擇子巾。
上一篇的實(shí)例也表明:盡管一個(gè)更復(fù)雜的模型幾乎總是會(huì)把訓(xùn)練它的數(shù)據(jù)擬合得更好帆赢,但它未必能把新數(shù)據(jù)預(yù)測得更好。
本文主要介紹為了提升泛化能力而用于防止數(shù)據(jù)過擬合的不同的方法线梗,稱為無監(jiān)督數(shù)據(jù)上的正則化(regularization on unsupervised data)匿醒。更具體地說,與按照減少訓(xùn)練(training)誤差的方式來優(yōu)化參數(shù)訓(xùn)練模型不同缠导,正則化關(guān)注于減少測試(testing)或驗(yàn)證(validation)誤差廉羔,這樣模型在新數(shù)據(jù)上的性能會(huì)和在訓(xùn)練數(shù)據(jù)上的一樣好。包括以下內(nèi)容:
? L1 罰函數(shù)
? L2 罰函數(shù)
? 集成方法與模型平均
2僻造、L1罰函數(shù)
L1 罰函數(shù)憋他,也稱為最小絕對(duì)值收縮和選擇算子,它的基本思想是將權(quán)重向零的方向縮減的懲罰髓削。懲罰項(xiàng)使用的是權(quán)重絕對(duì)值的和竹挡,所以懲罰的程度不會(huì)更小或者更大,結(jié)果是小的權(quán)重會(huì)縮減到零立膛,作為一種方便的效果揪罕,除了防止過擬合之外梯码,它還可以作為一種變量選擇的方法。懲罰的力度是由一個(gè)超參數(shù)λ所控制的好啰,它乘以權(quán)重絕對(duì)值的和轩娶,可以被預(yù)先設(shè)定,或者就像其他超參數(shù)那樣框往,使用交叉驗(yàn)證或者一些類似的方法來優(yōu)化鳄抒。
把 L1 罰函數(shù)作為約束優(yōu)化時(shí),我們更容易看出它如何有效地限制了模型的復(fù)雜性椰弊。即使包括了許多預(yù)測變量许溅,權(quán)重絕對(duì)值的和也不能超過定義的閾值。這樣做的一個(gè)結(jié)果是秉版,使用 L1 罰函數(shù)贤重,只要有足夠強(qiáng)的懲罰項(xiàng),真的有可能包括比樣例或觀測還要多的預(yù)測變量清焕。
下面通過一個(gè)模擬廣義線性回歸問題來看 L1 罰函數(shù)是如何工作的:
> library(pacman)
> p_load(magrittr, glmnet)
>
> set.seed(123)
> # 模擬一組多元正態(tài)分布數(shù)據(jù)
> x <- MASS::mvrnorm(n = 200, mu = c(0, 0, 0, 0, 0),
Sigma = matrix(c(1, 0.9999, 0.99, 0.99, 0.1,
+ 0.9999, 1, 0.99, 0.99, 0.1,
+ 0.99, 0.99, 1, 0.99, 0.1,
+ 0.99, 0.99, 0.99, 1, 0.1,
+ 0.1, 0.1, 0.1, 0.1, 1), ncol = 5))
>
> # 模擬一組正態(tài)分布數(shù)據(jù)
> y <- rnorm(200, 3 + x %*% matrix(c(1, 1, 1, 1, 0)), 0.5)
> head(x)
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.63043 0.62539 0.55882 0.7107 -2.10443
## [2,] 0.19737 0.20667 0.34580 0.3419 -1.27003
## [3,] -1.59643 -1.60853 -1.51857 -1.5233 0.05489
## [4,] -0.04584 -0.06214 0.05673 -0.1577 -0.54775
## [5,] -0.09989 -0.10379 -0.23926 -0.1275 0.39342
## [6,] -1.82648 -1.82852 -1.60641 -1.6362 0.24322
> head(y)
## [1] 5.027 3.572 -3.256 2.725 1.155 -3.377
glmnet包的glmnet()函數(shù)能擬合L1罰函數(shù)或L2罰函數(shù)并蝗,取哪一個(gè)函數(shù)是由參數(shù)alpha來決定的。當(dāng)alpha=1時(shí)耐朴,它是L1罰函數(shù)(也就是lasso)借卧;當(dāng)alpha=2時(shí),它是L2罰函數(shù)(也就是嶺回歸)筛峭。
使用前100行數(shù)據(jù)作為訓(xùn)練集铐刘,交叉驗(yàn)證自動(dòng)調(diào)優(yōu)超參數(shù)lambda:
> x.train <- x[1:100, ]
> y.train <- y[1:100]
>
> lasso.cv <- cv.glmnet(x.train, y.train, alpha = 1)
>
> # 畫圖查看lasso回歸對(duì)象各個(gè)lambda值的均方誤差
> plot(lasso.cv)
從圖中可以看出,當(dāng)懲罰變得太大(lambda增大)時(shí)影晓,交叉驗(yàn)證模型的誤差增加镰吵。
最后,使用線性回歸OLS的系數(shù)來和lasso的系數(shù)對(duì)比:
> ols <- lm(y.train ~ x.train)
>
> cbind(OLS = coef(ols), Lasso = coef(lasso.cv)[, 1]) %>% print
## OLS Lasso
## (Intercept) 3.07450 3.0389
## x.train1 -1.79670 1.1957
## x.train2 3.94472 0.9800
## x.train3 0.62136 0.4413
## x.train4 1.18204 1.1050
## x.train5 0.08503 0.0000
OLS估計(jì)對(duì)于第一個(gè)預(yù)測變量的值是太低了挂签,對(duì)于第二個(gè)預(yù)測變量的值又太高了疤祭。反之lasso中變量5的系數(shù)直接被懲罰為0,跟真實(shí)系數(shù)3饵婆、1勺馆、1、1侨核、1草穆、0對(duì)比,Lasso更準(zhǔn)確搓译。
3悲柱、L2罰函數(shù)
L2罰函數(shù),也叫作嶺回歸(ridge regression)些己,除了懲罰是基于權(quán)重平方而不是基于權(quán)重絕對(duì)值的和之外豌鸡,在許多方面和L1罰函數(shù)很相似嘿般。
同樣,我們還是使用上面的方式看看L2罰函數(shù)是如何工作的:
> ridge.cv <- cv.glmnet(x.train, y.train, alpha = 0)
> par(mfrow = c(1, 2))
> plot(lasso.cv)
> plot(ridge.cv)
曲線跟之前的略有不同涯冠,ridge回歸中誤差對(duì)lambda值的增加是漸進(jìn)的炉奴。但是,總體上和lasso一樣功偿,嶺回歸往往在非常小的lambda值上表現(xiàn)很好盆佣,這可能表明lasso對(duì)于提高樣本外性能/泛化能力并不是很有幫助往堡。
還是使用線性回歸系數(shù)進(jìn)行對(duì)比:
> cbind(OLS = coef(ols), Lasso = coef(lasso.cv)[, 1], Ridge = coef(ridge.cv)[, 1]) %>%
+ print
## OLS Lasso Ridge
## (Intercept) 3.07450 3.0389 3.04427
## x.train1 -1.79670 1.1957 0.92560
## x.train2 3.94472 0.9800 0.93024
## x.train3 0.62136 0.4413 0.92107
## x.train4 1.18204 1.1050 0.94465
## x.train5 0.08503 0.0000 0.06053
盡管嶺回歸沒有把第五個(gè)預(yù)測因子的系數(shù)縮減到零械荷,但還是要比OLS的系數(shù)小,而且其余的參數(shù)都有輕微縮減虑灰,和真實(shí)值3吨瞎、1、1穆咐、1颤诀、1、0非常接近对湃。
接下來我們看看權(quán)重衰減(L2罰函數(shù))在神經(jīng)網(wǎng)絡(luò)模型中的應(yīng)用崖叫,前面我們使用caret包和nnet包訓(xùn)練的神經(jīng)網(wǎng)絡(luò)使用了0.01的權(quán)重衰減,為了探索權(quán)重衰減的使用拍柒,我們可以使用交叉驗(yàn)證來調(diào)整它的取值心傀。
> # 還是使用手寫數(shù)字識(shí)別數(shù)據(jù)集
> digits.train <- read.csv("./data_set/digit-recognizer/train.csv")
> digits.train$label <- factor(digits.train$label, levels = 0:9)
> ind <- 1:5000
> digits.x <- digits.train[ind, -1]
> digits.y <- digits.train[ind, 1]
創(chuàng)建并使用多線程:
> p_load(parallel, foreach, doSNOW)
> cl <- makeCluster(4)
>
> # 加載R包
> clusterEvalQ(cl, {
+ library(RSNNS)
+ })
>
> # 注冊(cè)集群
> registerDoSNOW(cl)
在數(shù)字分類問題上建立一個(gè)神經(jīng)網(wǎng)絡(luò),權(quán)重衰減在0(沒有懲罰)和0.10之間變化拆讯。循環(huán)迭代次數(shù)為100和150:
> p_load(caret)
> set.seed(123)
> decay.ml1 <- lapply(c(100, 150), function(its) {
+ train(digits.x, digits.y, method = "nnet",
+ tuneGrid = expand.grid(.size = 10, .decay = c(0, 0.1)),
+ trControl = trainControl(method = "cv", number = 5, repeats = 1),
+ MaxNWts = 10000, maxit = its)
+ })
查看迭代次數(shù)限制在100次時(shí)的結(jié)果:
> decay.ml1[[1]]
## Neural Network
##
## 5000 samples
## 784 predictor
## 10 classes: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 3999, 4001, 3999, 4000, 4001
## Resampling results across tuning parameters:
##
## decay Accuracy Kappa
## 0.0 0.6030 0.5585
## 0.1 0.5808 0.5340
##
## Tuning parameter 'size' was held constant at a value of 10
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were size = 10 and decay = 0.
選擇的參數(shù)為size = 10 and decay = 0脂男,此時(shí)Accuracy=0.603,即非正則化模型(準(zhǔn)確度=0.603)比正則化模型(準(zhǔn)確度=0.5808)表現(xiàn)要好种呐,盡管兩個(gè)模型的表現(xiàn)都不怎么好宰翅。
查看迭代次數(shù)限制在150次時(shí)的結(jié)果:
> decay.ml1[[2]]
## Neural Network
##
## 5000 samples
## 784 predictor
## 10 classes: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 3998, 4000, 4001, 4001, 4000
## Resampling results across tuning parameters:
##
## decay Accuracy Kappa
## 0.0 0.5765 0.5291
## 0.1 0.6084 0.5648
##
## Tuning parameter 'size' was held constant at a value of 10
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were size = 10 and decay = 0.1.
150次時(shí)選擇的參數(shù)為size = 10 and decay = 0.1,此時(shí)Accuracy=0.6084爽室,即非正則化模型(準(zhǔn)確度=0.5765)比正則化模型(準(zhǔn)確度=0.6084)表現(xiàn)要差汁讼。
這些結(jié)果強(qiáng)調(diào)的重點(diǎn)是,正則化通常對(duì)更復(fù)雜的阔墩、有更大的靈活性擬合(以及過擬合)數(shù)據(jù)的模型最有用嘿架,同時(shí)(在那些對(duì)于數(shù)據(jù)合適的或者過于簡單的模型)正則化實(shí)際上可能降低了性能。
4戈擒、集成和模型平均
另一種正則化的方法是創(chuàng)建模型的集成并且把它們組合起來眶明,如果我們有不同的模型,每一個(gè)生成一組預(yù)測筐高,每個(gè)模型也許會(huì)在預(yù)測中造成誤差搜囱。一個(gè)模型可能把某個(gè)值預(yù)測得太高丑瞧,另一個(gè)可能會(huì)把它預(yù)測得太低,這樣平均起來蜀肘,一些誤差相互抵消绊汹,產(chǎn)生比通過其他方法更為準(zhǔn)確的預(yù)測。
> # 創(chuàng)建模擬數(shù)據(jù)集
> set.seed(123)
> df <- data.frame(x = rnorm(400))
>
> df$y = with(df, rnorm(400, 2 + ifelse(x < 0, x + x^2, x + x^2.5), 1))
>
> # 拆分訓(xùn)練集和測試集
> train.df <- df[1:200, ]
> test.df <- df[200:400, ]
>
> # 創(chuàng)建三個(gè)模型
> model1 <- lm(y ~ x, data = train.df)
> model2 <- lm(y ~ I(x^2), data = train.df)
> # pmax()和pmin()將一個(gè)或多個(gè)向量作為參數(shù)扮宠,將它們循環(huán)到公共長度西乖,
> # 并返回一個(gè)向量,作為parallel函數(shù)最大值(或最小值)的參數(shù)向量
> model3 <- lm(y ~ pmax(x, 0) + pmin(x, 0), data = train.df)
>
> # 合并輸出模型的均方誤差
> cbind(M1 = summary(model1)$r.squared,
+ M2 = summary(model2)$r.squared,
+ M3 = summary(model3)$r.squared)
## M1 M2 M3
## [1,] 0.3549 0.6496 0.7024
可以看到坛增,三個(gè)模型在訓(xùn)練集上的均方誤差差別還是很大的获雕。
查看三個(gè)模型擬合值之間的相關(guān)性:
> cbind(M1 = fitted(model1),
+ M2 = fitted(model2),
+ M3 = fitted(model3)) %>% cor %>%
+ corrplot::corrplot.mixed()
M3的擬合值與M1、M2的擬合值有較強(qiáng)的相關(guān)性收捣。
接下來看看模型在測試集上的表現(xiàn):
> pred <- data.frame(M1.pred = predict(model1, newdata = test.df), M2.pred = predict(model2,
+ newdata = test.df), M3.pred = predict(model3, newdata = test.df))
> pred$Mean <- rowMeans(pred)
>
> # 查看相關(guān)性
> cbind(test.df, pred) %>% cor %>% corrplot::corrplot.mixed()
Mean與y的相關(guān)性高于其他任何一個(gè)模型届案,說明三個(gè)模型預(yù)測的平均確實(shí)比任何一個(gè)模型的個(gè)別表現(xiàn)都要好。當(dāng)然罢艾,這只是在每個(gè)模型的表現(xiàn)差不多好的時(shí)候楣颠,才可以保證是真的,所以理想的情況是在模型的預(yù)測值之間有較低的相關(guān)性咐蚯,因?yàn)檫@會(huì)產(chǎn)生最佳的平均性能童漩。
隨機(jī)森林模型使用自助聚集(bootstrap aggregating)的決策樹,其中數(shù)據(jù)采用替換抽樣來形成相等規(guī)模的數(shù)據(jù)集春锋,模型在每一個(gè)數(shù)據(jù)上訓(xùn)練矫膨,然后將這些結(jié)果平均。因?yàn)槟P褪窃诿總€(gè)數(shù)據(jù)集上訓(xùn)練的看疙,如果某個(gè)特殊的變化對(duì)少數(shù)樣例或數(shù)據(jù)的一個(gè)罕見巧合是獨(dú)特的豆拨,它可能只在一個(gè)模型中出現(xiàn)。當(dāng)預(yù)測在由每個(gè)重抽樣的數(shù)據(jù)集所訓(xùn)練的許多模型上平均時(shí)能庆,這種過擬合往往會(huì)減少施禾。另外,隨機(jī)森林在每個(gè)分劃的節(jié)點(diǎn)上隨機(jī)地選擇了一個(gè)特征子集搁胆,目的是減少模型與模型的相關(guān)性并由此提高整體的平均性能弥搞。
5、實(shí)例:使用丟棄提升樣本外模型性能
自助聚集和模型平均在深度神經(jīng)網(wǎng)絡(luò)中并不常用渠旁,因?yàn)橛?xùn)練每個(gè)模型的成本會(huì)相當(dāng)高攀例,所以就時(shí)間和計(jì)算資源來說,多次重復(fù)這個(gè)過程會(huì)變得相當(dāng)昂貴顾腊。
丟棄是一種相對(duì)較新的正則化方法粤铭,對(duì)于大型和復(fù)雜的深度神經(jīng)網(wǎng)絡(luò)特別有價(jià)值。在模型訓(xùn)練的過程中杂靶,單元(例如輸入梆惯、隱藏神經(jīng)元等)連同所有從它們出發(fā)的和到達(dá)它們的聯(lián)系一起按照概率在某一步驟/更新中被丟棄酱鸭。考慮丟棄會(huì)迫使模型對(duì)于擾動(dòng)更加穩(wěn)健垛吗。
> # 還是使用手寫數(shù)字識(shí)別數(shù)據(jù)集
> # 使用deepnet包的nn.train()函數(shù)
> clusterEvalQ(cl,{
+ library(deepnet)
+ })
## [[1]]
## [1] "deepnet" "RSNNS" "Rcpp" "snow" "stats" "graphics"
## [7] "grDevices" "utils" "datasets" "methods" "base"
##
## [[2]]
## [1] "deepnet" "RSNNS" "Rcpp" "snow" "stats" "graphics"
## [7] "grDevices" "utils" "datasets" "methods" "base"
##
## [[3]]
## [1] "deepnet" "RSNNS" "Rcpp" "snow" "stats" "graphics"
## [7] "grDevices" "utils" "datasets" "methods" "base"
##
## [[4]]
## [1] "deepnet" "RSNNS" "Rcpp" "snow" "stats" "graphics"
## [7] "grDevices" "utils" "datasets" "methods" "base"
> # 并行計(jì)算4個(gè)模型
> nn.models <- foreach(i=1:4,.combine = "c") %dopar% {
+ set.seed(123)
+ list(nn.train(
+ x = as.matrix(digits.x),
+ y = model.matrix( ~ 0 + digits.y),
+ # 40個(gè)或者80個(gè)神經(jīng)元
+ hidden = c(40,80,40,80)[i],
+ # 激活函數(shù)tanh
+ activationfun = "tanh",
+ # 學(xué)習(xí)率0.8
+ learningrate = 0.8,
+ momentum = 0.5,
+ numepochs = 150,
+ output = "softmax",
+ # 隱藏單位兩個(gè)丟棄凹髓,丟棄比例為0.5
+ hidden_dropout = c(0,0,0.5,0.5)[i],
+ # 可見單位兩個(gè)丟棄,丟棄比例為0.2
+ visible_dropout = c(0,0,0.2,0.2)[i]
+ ))
+ }
循環(huán)模型怯屉,獲得預(yù)測值并得到模型的整體性能:
> p_load(deepnet)
> nn.pred <- lapply(nn.models, function(obj) {
+ encodeClassLabels(nn.predict(obj, as.matrix(digits.x)))
+ })
>
> perf.train <- do.call(cbind, lapply(nn.pred, function(yhat) {
+ caret::confusionMatrix(xtabs(~I(yhat - 1) + digits.y))$overall
+ }))
>
> colnames(perf.train) <- c("N40", "N80", "N40_REG", "N80_REG")
>
> options(digits = 4)
> perf.train
## N40 N80 N40_REG N80_REG
## Accuracy 0.9060 0.9662 0.9234 0.9396
## Kappa 0.8955 0.9624 0.9149 0.9329
## AccuracyLower 0.8976 0.9608 0.9157 0.9326
## AccuracyUpper 0.9140 0.9710 0.9306 0.9460
## AccuracyNull 0.1116 0.1116 0.1116 0.1116
## AccuracyPValue 0.0000 0.0000 0.0000 0.0000
## McnemarPValue NaN NaN NaN NaN
40個(gè)神經(jīng)元的有正則化的模型比沒有正則化的模型表現(xiàn)要好蔚舀,但是80個(gè)神經(jīng)元沒有正則化的模型比有正則化的模型表現(xiàn)得要好。
看看在測試集上的表現(xiàn):
> ind2 <- 5001:10000
> test.x <- digits.train[ind2, -1]
> test.y <- digits.train[ind2, 1]
>
> nn.pred.test <- lapply(nn.models, function(obj) {
+ encodeClassLabels(nn.predict(obj, as.matrix(test.x)))
+ })
>
> perf.test <- do.call(cbind, lapply(nn.pred.test, function(yhat) {
+ caret::confusionMatrix(xtabs(~I(yhat - 1) + test.y))$overall
+ }))
>
> colnames(perf.test) <- c("N40", "N80", "N40_REG", "N80_REG")
>
> # 對(duì)比模型在訓(xùn)練集和測試集上的準(zhǔn)確度
> p_load(dplyr)
> perf.train %>% as.data.frame() %>%
+ bind_rows(as.data.frame(perf.test)) %>%
+ filter(rownames(.) == 1 | rownames(.) == 8) %>%
+ mutate(Dateset = c("train", "test")) %>%
+ as.data.frame() %>% print()
## N40 N80 N40_REG N80_REG Dateset
## 1 0.9060 0.9662 0.9234 0.9396 train
## 2 0.8656 0.8768 0.8896 0.9014 test
通過測試集的性能可知锨络,正則化的模型比對(duì)應(yīng)的非正則化的模型性能要好赌躺,盡管它們?cè)跍y試數(shù)據(jù)中都比在訓(xùn)練數(shù)據(jù)中的性能要差,但正則化后的性能下降得更少足删。這也提現(xiàn)了正則化的價(jià)值寿谴。