한치록(2022). 계량경제학강의, 제4판, 박영사, 19장 부록 “머신러닝 예제”, 버전 0.2.2.
이 문서에 제시된 R 명령들은 주로 James, Witten, Hastie and Tibshirani (2013, An Introduction to Statistical Learning: with Applications in R, Springer)을 참고하였으며, 그 책으로 충분하지 않은 부분은 패키지 문서에 기초하여 직접 코딩하였거나 인터넷 검색으로부터 얻은 정보를 확인하여 작성하였다.
회귀(regression) | 분류(classifiction) |
---|---|
데이터 준비 Subset Selection Splines Ridge와 Lasso PCR과 PLS Decision Tree Tree Ensemble Support Vector Regression Neural Networks Super Learner |
데이터 준비 Logit, LDA, QDA ROC와 Cutoff 값 Class Imbalance Ridge와 Lasso Decision Tree Tree Ensemble Support Vector Machines Neural Networks Super Leaner |
Lasso 부분의 마지막에 살펴본 바 있는 h2o
패키지(설치)를 사용한다. 설치 시 파일 다운로드 도중에
중단되면, options(timeout = 600)
으로 time-out 시간을 적절히
늘려 주면(600은 10분) 도움이 된다.
options(timeout=600)
install.packages("h2o", type="source", repos=(c("http://h2o-release.s3.amazonaws.com/h2o/latest_stable_R"))) # will take long
options(timeout=60) # default
구버전으로 만족하다면 install.packages("h2o")
로도
충분하다. 시스템에 Java가 설치되어 있어야 한다. 설치가 끝났으면
다음으로 시험해 본다.
library(h2o)
h2o.init()
h2o.shutdown(prompt = FALSE)
library(h2o)
부분에서 오류가 발생하는 것은 R
h2o
패키지가 제대로 설치되지 않았음을 의미하고,
h2o.init()
부분에서 오류가 발생하는 것은 Java와 관련되었을
가능성이 높다. 오류 메시지를 읽어 보고 문제를 해결하기 바란다.
H2O는 배경에 java 엔진을 돌리고 R의 h2o
패키지는 이
엔진에 접속하여 일처리를 한다. 뭔가 알 수 없는 오류가 발생할 수도
있는데, 그럴 때에는 h2o.shutdown(prompt = FALSE)
로 H2O를
shutdown한 후 다시 해 보거나, 아니면 시스템을 재부팅해야 할지도 모른다.
가급적 최신 버전을 사용하는 것이 좋겠다.
이 절의 내용은 H2O.ai의 Deep Learning 문서를 참고하였다. 우선
h2o
를 시작한다.
library(h2o) # install.packages("h2o") if necessary
h2o.init()
H2O에서 자료 집합은 특별한 형태로 관리된다. as.h2o()
함수를 사용하여 만들 수 있다. 매번 as.h2o(z14)
처럼 사용해도
좋으나, 자주 사용할 것이므로 미리 만들어 두고 시작하자.
<- as.h2o(z14)
z14h <- as.h2o(z15) z15h
다음으로 NN을 학습해 본다. 노드 1개짜리 은닉층이 1개 있는
모형을 학습해 보자(hidden = c(1)
).
<- 'ynext'
yvar <- setdiff(names(z14), yvar)
xvar <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 100, seed = 1, reproducible = TRUE)
nn h2o.performance(nn, train=TRUE)
# H2ORegressionMetrics: deeplearning
# ** Reported on training data. **
# ** Metrics reported on full training frame **
#
# MSE: 2812.667
# RMSE: 53.03459
# MAE: 40.0028
# RMSLE: 0.07964379
# Mean Residual Deviance : 2812.667
위에서 reproducible = TRUE
옵션은 매번 동일한 결과를
얻도록 하기 위함이다. Train set의 RMSE는 53.03459이다. 참고로, 이 RMSE는
다음과 같이 구할 수도 있다.
RMSE(z14h$ynext, h2o.predict(nn, z14h)) # RMSE() defined in index11.php
# [1] 53.03458
# RMSE(z14$ynext, as.vector(h2o.predict(nn, z14h)))
학습 없이 Random Walk로부터 구한 RMSE는 다음과 같다.
RMSE(z14$ynext, z14$deathrate) # RMSE() defined in index11.php
# [1] 57.12526
NN으로부터 얻은 train set의 RMSE (53.03459)는 단순 random walk를 적용한 결과(RMSE = 57.12526)보다는 더 낫다.
위 NN 학습 결과를 test set에 적용할 때 예측 결과는 다음과 같다.
h2o.performance(nn, newdata = z15h)
# H2ORegressionMetrics: deeplearning
#
# MSE: 2875.945
# RMSE: 53.62784
# MAE: 39.75183
# RMSLE: 0.07874747
# Mean Residual Deviance : 2875.945
결과는 평범하다(nn으로부터의 RMSE 53.62784를 random walk의 RMSE
53.24273과 비교). epochs = 500
으로 설정하면 결과는 다음과
같다.
<- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 500, seed = 1, reproducible = T)
nn500
nn500# Model Details:
# ==============
#
# H2ORegressionModel: deeplearning
# Model ID: DeepLearning_model_R_1632757675284_2
# Status of Neuron Layers: predicting ynext, regression, gaussian distribution, Quadratic loss, 22 weights/biases, 5.5 KB, 38,356 training samples, mini-batch size 1
# layer units type dropout l1 l2 mean_rate rate_rms momentum
# 1 1 19 Input 0.00 % NA NA NA NA NA
# 2 2 1 Rectifier 0.00 % 0.000000 0.000000 0.001999 0.000800 0.000000
# 3 3 1 Linear NA 0.000000 0.000000 0.000424 0.000000 0.000000
# mean_weight weight_rms mean_bias bias_rms
# 1 NA NA NA NA
# 2 0.078716 0.266824 1.728693 0.000000
# 3 0.639869 0.000000 -1.123834 0.000000
#
#
# H2ORegressionMetrics: deeplearning
# ** Reported on training data. **
# ** Metrics reported on full training frame **
#
# MSE: 2409.193
# RMSE: 49.08353
# MAE: 35.83647
# RMSLE: 0.06359786
# Mean Residual Deviance : 2409.193
h2o.performance(nn500, newdata = z15h)
# H2ORegressionMetrics: deeplearning
#
# MSE: 2503.499
# RMSE: 50.03498
# MAE: 35.34185
# RMSLE: 0.06333317
# Mean Residual Deviance : 2503.499
Test set에 대한 예측력이 약간 좋아졌다. 거듭 말하지만 test set 예측력이 좋아졌다는 것은 사후적으로 보니 그렇다는 것이며, test set에 대한 예측력을 바탕으로 모델을 고르는 것은 아니다.
위에서 epochs
는 boosting에서
boosting 횟수의 역할을 하며, 튜닝 대상이다. 아무 옵션도 주지 않으면
stopping_rounds = 5
옵션을 준 것과 같아서, 최근 5회
deviance의 단순 이동평균이 5회 이상 개선되지 않으면 자동으로 멈추도록
되어 있다(early stopping). 실제로,
epochs = 500
으로 주었지만 172회에서 멈추었다.
max(nn500@model$scoring_history$iterations)
# [1] 172
h2o.learning_curve_plot(nn500)
CV를 위해서는 h2o
에서
nfolds = 10
과 같은 옵션을 주면 CV를 한다. 그러면
stopping_rounds
epoch 이상 CV 성능개선이 없으면
멈춘다(early stopping). stopping_rounds
를 50으로 설정하면
결과는 다음과 같다.
<- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, variable_importances = TRUE, reproducible = TRUE)
nn.tuned h2o.learning_curve_plot(nn.tuned)
위 명령에서 epochs = 1000
으로 최대 횟수를 1,000으로
설정하였으나 early stopping 기준에 따라 약 300회 후 종료되었음을
알 수 있다(stopping_rounds
를 50으로 설정했는데, 더 작은
숫자로 줄이면 더 일찍 종료된다).
학습의 성능은 다음과 같다.
h2o.performance(nn.tuned, train = TRUE) # train
# RMSE: 47.38929
h2o.performance(nn.tuned, xval = TRUE) # cross validation
# RMSE: 51.43421
h2o.performance(nn.tuned, newdata = z15h) # test set
# RMSE: 50.23829
h2o.varimp_plot(nn.tuned)
위 결과에 의하면 stopping_rounds
값이 50일 때 train, CV,
test RMSE는 각각 47.38929, 51.43421, 50.23829이다.
참고로, CV 및 early stopping과 관련하여 H2O.ai FAQ에 다음과 같이 설명되어 있다.
Can H2O automatically feed back the implications of the cross-validation results to improve the algorithm during training, as well as tune some of the model’s hyperparamters?
Yes, H2O can use cross-validation for parameter tuning if early stopping is enabled (stopping_rounds>0). In that case, cross-validation is used to automatically tune the optimal number of epochs for Deep Learning or the number of trees for DRF/GBM. The main model will use the mean number of epochs across all cross-validation models.
추가로, 은닉층 node 개수를 1, 2, 3개로 하면서 CV RMSE를 비교해 보자.
<- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, reproducible = T)
nn1 h2o.performance(nn1, xval = TRUE) # CV
# RMSE: 51.43421
<- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(2), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, reproducible = T)
nn2 h2o.performance(nn2, xval = TRUE) # CV
# RMSE: 54.63187
<- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(3), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, reproducible = T)
nn3 h2o.performance(nn3, xval = TRUE) # CV
# RMSE: 61.67837
작은 NN의 CV 오차수준이 더 낮다. 그러므로 위에서 은닉층 노드 개수를 1개로 한 모형이 2개나 3개보다 선호된다. (은닉층을 아예 없애면 학습에 시간이 아주 오래 걸리고 성과가 더 나아지지도 않는다.)
본 실습에서는 overfitting보다는 underfitting이 문제인 것 같다.
일반적으로는 overfitting이 문제이다. 이 경우 NN에서 overfitting을 피하는
방법으로 early stopping 이외에도 L1 또는 L2 regularisation (lasso와
ridge와 유사, l1
과 l2
옵션 사용), Hinton et al
(2012)의 dropout 방법(input_dropout_ratio
와
hidden_dropout_ratios
옵션 사용)도 사용된다.
다음 명령으로 h2o
를 종료한다.
h2o.shutdown(prompt = FALSE)
## -------------------------------------------------------------------- library(h2o) h2o.init() z14h <- as.h2o(z14) z15h <- as.h2o(z15) yvar <- 'ynext' xvar <- setdiff(names(z14), yvar) nn <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 100, seed = 1, reproducible = TRUE) h2o.performance(nn, train=TRUE) RMSE(z14h$ynext, h2o.predict(nn, z14h)) RMSE(z14$ynext, z14$deathrate) h2o.performance(nn, newdata = z15h) nn500 <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 500, seed = 1, reproducible = T) nn500 h2o.performance(nn500, newdata = z15h) max(nn500@model$scoring_history$iterations) h2o.learning_curve_plot(nn500) nn.tuned <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, variable_importances = TRUE, reproducible = TRUE) h2o.learning_curve_plot(nn.tuned) h2o.performance(nn.tuned, train = TRUE) # train h2o.performance(nn.tuned, xval = TRUE) # cross validation h2o.performance(nn.tuned, newdata = z15h) # test set h2o.varimp_plot(nn.tuned) nn1 <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(1), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, reproducible = T) h2o.performance(nn1, xval = TRUE) # CV nn2 <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(2), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, reproducible = T) h2o.performance(nn2, xval = TRUE) # CV nn3 <- h2o.deeplearning(xvar, yvar, z14h, standardize = TRUE, hidden = c(3), epochs = 1000, seed = 1, nfolds = 10, stopping_rounds = 50, reproducible = T) h2o.performance(nn3, xval = TRUE) # CV h2o.shutdown(prompt = FALSE) ## --------------------------------------------------------------------