library(ISLR)
library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5 v purrr 0.3.4
## v tibble 3.1.4 v dplyr 1.0.7
## v tidyr 1.1.3 v stringr 1.4.0
## v readr 2.0.1 v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
data(Hitters)
names(Hitters)
## [1] "AtBat" "Hits" "HmRun" "Runs" "RBI" "Walks"
## [7] "Years" "CAtBat" "CHits" "CHmRun" "CRuns" "CRBI"
## [13] "CWalks" "League" "Division" "PutOuts" "Assists" "Errors"
## [19] "Salary" "NewLeague"
head(Hitters)
## AtBat Hits HmRun Runs RBI Walks Years CAtBat CHits CHmRun
## -Andy Allanson 293 66 1 30 29 14 1 293 66 1
## -Alan Ashby 315 81 7 24 38 39 14 3449 835 69
## -Alvin Davis 479 130 18 66 72 76 3 1624 457 63
## -Andre Dawson 496 141 20 65 78 37 11 5628 1575 225
## -Andres Galarraga 321 87 10 39 42 30 2 396 101 12
## -Alfredo Griffin 594 169 4 74 51 35 11 4408 1133 19
## CRuns CRBI CWalks League Division PutOuts Assists Errors
## -Andy Allanson 30 29 14 A E 446 33 20
## -Alan Ashby 321 414 375 N W 632 43 10
## -Alvin Davis 224 266 263 A W 880 82 14
## -Andre Dawson 828 838 354 N E 200 11 3
## -Andres Galarraga 48 46 33 N E 805 40 4
## -Alfredo Griffin 501 336 194 A W 282 421 25
## Salary NewLeague
## -Andy Allanson NA A
## -Alan Ashby 475.0 N
## -Alvin Davis 480.0 A
## -Andre Dawson 500.0 N
## -Andres Galarraga 91.5 N
## -Alfredo Griffin 750.0 A
Hitters %>%
is.na() %>%
sum()
## [1] 59
NA값의 갯수를 확인한다.
Hitters데이터 중에서 na를 가지고 있는 관측치를 TRUE, FALSE로 식별해준 후,
다 더해서 NA값을 확인한다.
Hitters %>%
na.omit() -> Hitters
Hitters데이터에서 NA값을 없애버린다.
그리고 그러한 데이터 셋을 Hitters에 다시 저장한다.
lamda가 크면, 축소를 많이 시킨 것이고, lamda가 작으면 축소를 안시킨 것이다.
#install.packages("glmnet")
library(glmnet)
## 필요한 패키지를 로딩중입니다: Matrix
##
## 다음의 패키지를 부착합니다: 'Matrix'
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
## Loaded glmnet 4.1-2
str(Hitters)
## 'data.frame': 263 obs. of 20 variables:
## $ AtBat : int 315 479 496 321 594 185 298 323 401 574 ...
## $ Hits : int 81 130 141 87 169 37 73 81 92 159 ...
## $ HmRun : int 7 18 20 10 4 1 0 6 17 21 ...
## $ Runs : int 24 66 65 39 74 23 24 26 49 107 ...
## $ RBI : int 38 72 78 42 51 8 24 32 66 75 ...
## $ Walks : int 39 76 37 30 35 21 7 8 65 59 ...
## $ Years : int 14 3 11 2 11 2 3 2 13 10 ...
## $ CAtBat : int 3449 1624 5628 396 4408 214 509 341 5206 4631 ...
## $ CHits : int 835 457 1575 101 1133 42 108 86 1332 1300 ...
## $ CHmRun : int 69 63 225 12 19 1 0 6 253 90 ...
## $ CRuns : int 321 224 828 48 501 30 41 32 784 702 ...
## $ CRBI : int 414 266 838 46 336 9 37 34 890 504 ...
## $ CWalks : int 375 263 354 33 194 24 12 8 866 488 ...
## $ League : Factor w/ 2 levels "A","N": 2 1 2 2 1 2 1 2 1 1 ...
## $ Division : Factor w/ 2 levels "E","W": 2 2 1 1 2 1 2 2 1 1 ...
## $ PutOuts : int 632 880 200 805 282 76 121 143 0 238 ...
## $ Assists : int 43 82 11 40 421 127 283 290 0 445 ...
## $ Errors : int 10 14 3 4 25 7 9 19 0 22 ...
## $ Salary : num 475 480 500 91.5 750 ...
## $ NewLeague: Factor w/ 2 levels "A","N": 2 1 2 2 1 1 1 2 1 1 ...
## - attr(*, "na.action")= 'omit' Named int [1:59] 1 16 19 23 31 33 37 39 40 42 ...
## ..- attr(*, "names")= chr [1:59] "-Andy Allanson" "-Billy Beane" "-Bruce Bochte" "-Bob Boone" ...
데이터를 보았을 때, League와 Division, NewLeague는 Factor형 변수를 가진다는 것을 볼 수 있다.
우리는 이러한 factor형 변수들을 수치형 데이터로 바꿔서 문제를 해결해야 한다.
이럴때 사용하는 함수가 바로 model.matrix()이다.
model.matirx(예측하고자 하는 변수~.,데이터셋)[,-1]
여기서 [,-1]은 intercept값은 제외시키겠다는 이야기이다.
x<-model.matrix(Salary~.,Hitters)[,-1]
x %>% head()
## AtBat Hits HmRun Runs RBI Walks Years CAtBat CHits CHmRun
## -Alan Ashby 315 81 7 24 38 39 14 3449 835 69
## -Alvin Davis 479 130 18 66 72 76 3 1624 457 63
## -Andre Dawson 496 141 20 65 78 37 11 5628 1575 225
## -Andres Galarraga 321 87 10 39 42 30 2 396 101 12
## -Alfredo Griffin 594 169 4 74 51 35 11 4408 1133 19
## -Al Newman 185 37 1 23 8 21 2 214 42 1
## CRuns CRBI CWalks LeagueN DivisionW PutOuts Assists Errors
## -Alan Ashby 321 414 375 1 1 632 43 10
## -Alvin Davis 224 266 263 0 1 880 82 14
## -Andre Dawson 828 838 354 1 0 200 11 3
## -Andres Galarraga 48 46 33 1 0 805 40 4
## -Alfredo Griffin 501 336 194 0 1 282 421 25
## -Al Newman 30 9 24 1 0 76 127 7
## NewLeagueN
## -Alan Ashby 1
## -Alvin Davis 0
## -Andre Dawson 1
## -Andres Galarraga 1
## -Alfredo Griffin 0
## -Al Newman 0
데이터가 수치형으로 잘 변환되었다는 것을 알 수 있다.
y에는 당연히 우리가 예측하려고 하는 변수인 y를 넣는다.
y<-Hitters$Salary
y %>% head()
## [1] 475.0 480.0 500.0 91.5 750.0 70.0
2.glmnet()함수
ridge와 Lasso를 fit해주는 함수이다.
glmnet(관측할 값, 예측할 값, alpha=, lamda=)
alpha=0 : ridge 적합
alpha=1 : Lasso 적합
lamda=람다 값
lamda에 아무것도 쓰지 않으면 그냥 알아서 lamda를 설정해준다.
grid<-10^seq(10,-2,length=100)
grid
## [1] 1.000000e+10 7.564633e+09 5.722368e+09 4.328761e+09 3.274549e+09
## [6] 2.477076e+09 1.873817e+09 1.417474e+09 1.072267e+09 8.111308e+08
## [11] 6.135907e+08 4.641589e+08 3.511192e+08 2.656088e+08 2.009233e+08
## [16] 1.519911e+08 1.149757e+08 8.697490e+07 6.579332e+07 4.977024e+07
## [21] 3.764936e+07 2.848036e+07 2.154435e+07 1.629751e+07 1.232847e+07
## [26] 9.326033e+06 7.054802e+06 5.336699e+06 4.037017e+06 3.053856e+06
## [31] 2.310130e+06 1.747528e+06 1.321941e+06 1.000000e+06 7.564633e+05
## [36] 5.722368e+05 4.328761e+05 3.274549e+05 2.477076e+05 1.873817e+05
## [41] 1.417474e+05 1.072267e+05 8.111308e+04 6.135907e+04 4.641589e+04
## [46] 3.511192e+04 2.656088e+04 2.009233e+04 1.519911e+04 1.149757e+04
## [51] 8.697490e+03 6.579332e+03 4.977024e+03 3.764936e+03 2.848036e+03
## [56] 2.154435e+03 1.629751e+03 1.232847e+03 9.326033e+02 7.054802e+02
## [61] 5.336699e+02 4.037017e+02 3.053856e+02 2.310130e+02 1.747528e+02
## [66] 1.321941e+02 1.000000e+02 7.564633e+01 5.722368e+01 4.328761e+01
## [71] 3.274549e+01 2.477076e+01 1.873817e+01 1.417474e+01 1.072267e+01
## [76] 8.111308e+00 6.135907e+00 4.641589e+00 3.511192e+00 2.656088e+00
## [81] 2.009233e+00 1.519911e+00 1.149757e+00 8.697490e-01 6.579332e-01
## [86] 4.977024e-01 3.764936e-01 2.848036e-01 2.154435e-01 1.629751e-01
## [91] 1.232847e-01 9.326033e-02 7.054802e-02 5.336699e-02 4.037017e-02
## [96] 3.053856e-02 2.310130e-02 1.747528e-02 1.321941e-02 1.000000e-02
우선 그리드를 자동으로 찾아보지 않고 내가 지정한다. 이 때, 그리드는 \(10^10\) 부터 \(10^-2\) 까지의 값을 100개의 간격으로 자른 수이다.
ridge.mod<-glmnet(x,y,alpha=0,lamda=grid)
그 후, 능형회귀에 lamda를 grid로 설정해주고 fitting해주었다. grid가 100개니까 100개의 모델이 fit되었을 것이다.
names(ridge.mod)
## [1] "a0" "beta" "df" "dim" "lambda" "dev.ratio"
## [7] "nulldev" "npasses" "jerr" "offset" "call" "nobs"
이 때, ridge.mod가 가진 names들을 보니까, 위와같이 있다는 것을 알았다.
ridge.mod %>%
coef() %>%
dim()
## [1] 20 100
이 때, ridge.mod의 회귀계수를 보고, dim을 봐서, 몇개의 model이 fitting 되었는지 보자.
값은 행이 20개고 열이 100개인 차원이 나온다.
즉, 값은 행에 각각의 변수들이 저장되고, 열에는 lamda의 값들이 저장되는 것을 확인할 수 있다.
100개의 lamda에 대한 각각의 20개의 회귀변수가 나왔다고 해석하면 될 것이다.
ridge.mod %>%
coef ->ridge.mod.coef
ridge.mod.coef[,50]
## (Intercept) AtBat Hits HmRun Runs
## 213.066444060 0.090095728 0.371252755 1.180126954 0.596298285
## RBI Walks Years CAtBat CHits
## 0.594502389 0.772525465 2.473494235 0.007597952 0.029272172
## CHmRun CRuns CRBI CWalks LeagueN
## 0.217335715 0.058705097 0.060722036 0.058698830 3.276567808
## DivisionW PutOuts Assists Errors NewLeagueN
## -21.889942546 0.052667119 0.007463678 -0.145121335 2.972759111
ridge.mod$lambda[50]
## [1] 2674.375
이 때, 50번째 lamda에 대한 회귀계수를 한번 보자.
ridge.mod.coef[,10]
## (Intercept) AtBat Hits HmRun Runs
## 5.193354e+02 4.787004e-03 1.742001e-02 6.974621e-02 2.941856e-02
## RBI Walks Years CAtBat CHits
## 3.102036e-02 3.661001e-02 1.486101e-01 4.102827e-04 1.511717e-03
## CHmRun CRuns CRBI CWalks LeagueN
## 1.139588e-02 3.032875e-03 3.130119e-03 3.304540e-03 -4.416363e-02
## DivisionW PutOuts Assists Errors NewLeagueN
## -6.992187e-01 1.940563e-03 3.162091e-04 -1.571121e-03 -4.618317e-03
ridge.mod$lambda[10]
## [1] 110505.5
이 때, 10번째 lamda에 대한 회귀계수도 같이 비교해보자.
보면 50번째 lamda가 더 lamda의 값이 작고, 회귀계수가 축소가 덜 된 것을 확인할 수 있다. 즉, 람다의 값이 작을수록 회귀계수의 축소가 덜 일어난다는 것을 알 수 있다.
반면, 10번째 람다에 대해서는 람다의 값이 훨씬 더 크게 형성되었다는 것을 알 수 있고, 이때 회귀계수돌은 매우 많이 축소되었다는 것을 알 수 있다.
L2-norm과 같이 비교해보자.
ridge.mod.coef[-1,50]^2 %>%
sum() %>%
sqrt->model_50
model_50
## [1] 22.53415
ridge.mod.coef[-1,10]^2 %>%
sum() %>%
sqrt->model_10
model_10
## [1] 0.7221433
이 때, model10이 L2-norm의 값이 더 작게 나오고 있다. 즉, 람다의 값이 더 크면, b가 축소되어, b와 관련된 L2-norm의 값이 작아진다. L2-norm의 값이 작을수록, 축소가 많이 일어났다는 것을 말해준다.
plot(ridge.mod, "lambda")
이 때, 람다에 대한 각 회귀계수의 값을 그래프로 그려보았는데, 람다가 작아질수록 회귀계수들이 수축이 안된다는 사실을 알 수 있다.
lamda가 크면, 축소를 많이 시킨 것이고, lamda가 작으면 축소를 안시킨 것이다.
lasso.mod<-glmnet(x,y,alpha=1)
alpha에 1을 주게 되면, lasso를 실시한다. 람다에 아무값도 주지 않는다면, 스스로 람다를 정해준다.
names(lasso.mod)
## [1] "a0" "beta" "df" "dim" "lambda" "dev.ratio"
## [7] "nulldev" "npasses" "jerr" "offset" "call" "nobs"
lasso.mod의 구성요소들을 볼 수 있다.
lasso.mod$lambda %>%
length()
## [1] 80
우선 임의로 람다를 80개를 뽑았다는 것을 알 수 있다.
lasso.mod$lambda
## [1] 255.2820965 232.6035387 211.9396814 193.1115442 175.9560469 160.3245967
## [7] 146.0818014 133.1042968 121.2796779 110.5055256 100.6885193 91.7436287
## [13] 83.5933776 76.1671724 69.4006907 63.2353246 57.6176727 52.4990774
## [19] 47.8352041 43.5856564 39.7136268 36.1855777 32.9709507 30.0419023
## [25] 27.3730625 24.9413151 22.7255974 20.7067180 18.8671902 17.1910810
## [31] 15.6638728 14.2723375 13.0044224 11.8491453 10.7964999 9.8373686
## [37] 8.9634439 8.1671562 7.4416086 6.7805166 6.1781542 5.6293040
## [43] 5.1292122 4.6735471 4.2583620 3.8800609 3.5353670 3.2212947
## [49] 2.9351238 2.6743755 2.4367913 2.2203135 2.0230670 1.8433433
## [55] 1.6795857 1.5303760 1.3944216 1.2705450 1.1576733 1.0548288
## [61] 0.9611207 0.8757374 0.7979393 0.7270526 0.6624632 0.6036118
## [67] 0.5499886 0.5011291 0.4566102 0.4160462 0.3790858 0.3454089
## [73] 0.3147237 0.2867645 0.2612891 0.2380769 0.2169268 0.1976557
## [79] 0.1800965 0.1640972
선택한 람다들을 볼 수 있다.
lasso.mod %>%
coef() %>%
dim()
## [1] 20 80
이 때, 람다 80개 각각에 대해서, \(b_0\)부터 \(b_19\)까지 총 20개의 회귀계수가 추정되었다는 사실을 알 수 있다.
lasso_lambda_df<-cbind(lasso.mod$lambda,lasso.mod$df)
colnames(lasso_lambda_df)<-c("lambda","df")
head(lasso_lambda_df,5)
## lambda df
## [1,] 255.2821 0
## [2,] 232.6035 1
## [3,] 211.9397 2
## [4,] 193.1115 2
## [5,] 175.9560 3
tail(lasso_lambda_df,5)
## lambda df
## [76,] 0.2380769 18
## [77,] 0.2169268 18
## [78,] 0.1976557 18
## [79,] 0.1800965 18
## [80,] 0.1640972 19
이 때, lambda값이 작아질수록 df의 값이 커진다는 사실을 알 수 있다. 즉, 람다의 값이 작으면 축소가 작게된 것이고, 람다의 값이 크면, 축소가 크게되었다는 사실을 알 수 있다.
dim(lasso.mod$beta)
## [1] 19 80
apply(lasso.mod$beta, 2, function(t) sum(t!=0))
## s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19
## 0 1 2 2 3 4 4 4 4 4 5 5 5 5 6 6 6 6 6 6
## s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 s32 s33 s34 s35 s36 s37 s38 s39
## 6 6 6 6 6 6 6 6 6 7 7 7 9 9 9 9 9 11 11 12
## s40 s41 s42 s43 s44 s45 s46 s47 s48 s49 s50 s51 s52 s53 s54 s55 s56 s57 s58 s59
## 12 13 13 13 13 13 13 13 13 13 13 14 15 15 17 17 17 17 17 17
## s60 s61 s62 s63 s64 s65 s66 s67 s68 s69 s70 s71 s72 s73 s74 s75 s76 s77 s78 s79
## 17 17 17 17 18 18 18 17 18 18 18 18 18 18 18 18 18 18 18 19
다음으로는, 0이 아닌 beta의 갯수를 살펴볼 것이다. beta의 dim을 보니까, 행이 19개고, 열이 80개라고 나와있다. 즉, 열은 lambda를 행은 beta를 나타낸다고 할 수 있다.
즉, 열 방향으로 0이 아닌것을 모두 더하면, 0이 아닌 beta의 갯수가 나올 것이다.
이를 apply함수를 사용하여 계산하였다.
이 때, t!=0는 논리연산자이기 때문에 TRUE, FALSE로 계산되어 갯수를 출력할 수 있다.
apply(데이터, 옵션, function(t) 함수(t))
옵션 = 1 : 행의 방향으로 모두 더한다
옵션 = 2 : 열의 방향으로 모두 더한다
apply(lasso.mod$beta, 2, function(t) sum(abs(t)))
## s0 s1 s2 s3 s4 s5
## 0.00000000 0.07026614 0.13476614 0.19402153 0.32607217 0.66902681
## s6 s7 s8 s9 s10 s11
## 1.16062643 1.60917353 2.01773750 2.38998186 2.72609874 2.97402205
## s12 s13 s14 s15 s16 s17
## 3.20123049 3.40846344 11.64817218 22.98691627 33.32443344 42.73991182
## s18 s19 s20 s21 s22 s23
## 51.32307688 59.13982854 66.26049080 72.75317515 78.66537137 84.05609839
## s24 s25 s26 s27 s28 s29
## 88.96411117 93.43957427 97.51365487 101.22906065 104.61046950 108.52993494
## s30 s31 s32 s33 s34 s35
## 114.71476794 120.36084436 125.89147670 131.25050408 136.15014891 140.62777705
## s36 s37 s38 s39 s40 s41
## 144.71425308 148.18864814 151.59045005 154.77018794 157.86174328 160.79016820
## s42 s43 s44 s45 s46 s47
## 163.63677945 166.20598165 168.54928183 170.69298424 172.63820733 174.40982707
## s48 s49 s50 s51 s52 s53
## 176.02382743 177.49419703 178.84082943 180.15787370 181.56430483 182.27355302
## s54 s55 s56 s57 s58 s59
## 183.66972594 189.17522968 192.91656387 196.32739300 199.43820868 202.27621917
## s60 s61 s62 s63 s64 s65
## 204.85652984 207.20743988 209.35199945 211.29939556 213.11247799 215.55494811
## s66 s67 s68 s69 s70 s71
## 217.47724806 219.02184399 220.60118640 222.07917527 223.51628146 224.81215797
## s72 s73 s74 s75 s76 s77
## 226.03163842 227.15990943 228.20005790 229.15914906 230.03587822 230.83954274
## s78 s79
## 231.57502970 231.92115738
다음은 L1-norm을 구한 것을 나타내었다. t를 절댓값으로 묶어준 다음에, apply함수를 사용하여 합을 계산하였다.
lasso.L1.norm<-apply(lasso.mod$beta, 2, function(t) sum(abs(t)))
names(lasso.L1.norm)<-log(lasso.mod$lambda)
lasso.L1.norm
## 5.54236919451432 5.44933545346989 5.35630171242546 5.26326797138104
## 0.00000000 0.07026614 0.13476614 0.19402153
## 5.17023423033661 5.07720048929218 4.98416674824775 4.89113300720332
## 0.32607217 0.66902681 1.16062643 1.60917353
## 4.7980992661589 4.70506552511447 4.61203178407004 4.51899804302561
## 2.01773750 2.38998186 2.72609874 2.97402205
## 4.42596430198118 4.33293056093676 4.23989681989233 4.1468630788479
## 3.20123049 3.40846344 11.64817218 22.98691627
## 4.05382933780347 3.96079559675904 3.86776185571462 3.77472811467019
## 33.32443344 42.73991182 51.32307688 59.13982854
## 3.68169437362576 3.58866063258133 3.4956268915369 3.40259315049247
## 66.26049080 72.75317515 78.66537137 84.05609839
## 3.30955940944805 3.21652566840362 3.12349192735919 3.03045818631476
## 88.96411117 93.43957427 97.51365487 101.22906065
## 2.93742444527033 2.84439070422591 2.75135696318148 2.65832322213705
## 104.61046950 108.52993494 114.71476794 120.36084436
## 2.56528948109262 2.47225574004819 2.37922199900377 2.28618825795934
## 125.89147670 131.25050408 136.15014891 140.62777705
## 2.19315451691491 2.10012077587048 2.00708703482605 1.91405329378162
## 144.71425308 148.18864814 151.59045005 154.77018794
## 1.8210195527372 1.72798581169277 1.63495207064834 1.54191832960391
## 157.86174328 160.79016820 163.63677945 166.20598165
## 1.44888458855948 1.35585084751506 1.26281710647063 1.1697833654262
## 168.54928183 170.69298424 172.63820733 174.40982707
## 1.07674962438177 0.983715883337344 0.890682142292916 0.797648401248488
## 176.02382743 177.49419703 178.84082943 180.15787370
## 0.704614660204059 0.611580919159631 0.518547178115203 0.425513437070775
## 181.56430483 182.27355302 183.66972594 189.17522968
## 0.332479696026347 0.239445954981919 0.146412213937491 0.0533784728930625
## 192.91656387 196.32739300 199.43820868 202.27621917
## -0.0396552681513655 -0.132689009195794 -0.225722750240222 -0.31875649128465
## 204.85652984 207.20743988 209.35199945 211.29939556
## -0.411790232329078 -0.504823973373506 -0.597857714417934 -0.690891455462362
## 213.11247799 215.55494811 217.47724806 219.02184399
## -0.78392519650679 -0.876958937551219 -0.969992678595647 -1.06302641964007
## 220.60118640 222.07917527 223.51628146 224.81215797
## -1.1560601606845 -1.24909390172893 -1.34212764277336 -1.43516138381779
## 226.03163842 227.15990943 228.20005790 229.15914906
## -1.52819512486222 -1.62122886590664 -1.71426260695107 -1.8072963479955
## 230.03587822 230.83954274 231.57502970 231.92115738
람다값과 비교해보면, 람다값이 클수록 L1 norm이 아주 크게 축소되었고, 람다가 작게 나올수록 축소가 별로 되지 않은 것을 확인할 수 있다.
plot(lasso.mod,"lambda", label= TRUE)
마찬가지로, lambda에 대한 회귀계수의 각각의 값들을 plot한 결과, 람다가 커질수록 회귀계수가 축소되고, 람다가 작을수록 회귀계수가 축소가 잘 안된다는 사실을 알 수 있다.
plot(lasso.mod, "norm", label=TRUE)
이번엔 L1 norm을 기준으로 본 결과, L1 norm이 커지면 커질수록 회귀모형이 더 커진다는 사실을 알 수 있는데 이는 당연하다. 왜냐하면, L1 norm은 회귀계수의 절댓값을 모두 더한 값이기 때문이다.
set.seed(123)
train<-sample(1:nrow(x), nrow(x)/2)
test<- (-train)
sigle split은 전체 데이터에 대해서, train set과 test set을 한번만 나누는 것을 말한다.
이 때, test set은 오직 검증만을 위해 사용되고, train set은 파라미터 튜닝을 위해 사용된다.
즉 test set안에 있는 x값은 전혀 사용되지 않고, 오직 y값만 사용된다
이 떄, train set과 test set은 각각 반반으로 지정하였다.
y.test<-y[test]
이 때, test set은 y값만 사용할 것이므로, test set안에있는 y값은 미리 추출하여 따로 저장해주자.
grid<-10^seq(10,-2,length=100)
fit.tran<-glmnet(x[train,],y[train],alpha=0,lambda=grid)
dim(coef(fit.tran))
## [1] 20 100
모델을 피팅한 결과, 100개에 람다에 대해서 20개의 회귀계수가 추출된 것을 알 수 있다.
이제 우리는 best lambda를 구하고 회귀계수를 추출해야 한다.
tran.model.name<-0:(length(fit.tran$lambda)-1)
tran.model.name
## [1] 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
## [26] 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
## [51] 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
## [76] 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
그 후, 모델의 이름을 설정해준다. 모델의 이름을 0부터 99까지 설정한 이유는, glmnet()의 고유한 특성 때문이다.
colnames(fit.tran$beta)
## [1] "s0" "s1" "s2" "s3" "s4" "s5" "s6" "s7" "s8" "s9" "s10" "s11"
## [13] "s12" "s13" "s14" "s15" "s16" "s17" "s18" "s19" "s20" "s21" "s22" "s23"
## [25] "s24" "s25" "s26" "s27" "s28" "s29" "s30" "s31" "s32" "s33" "s34" "s35"
## [37] "s36" "s37" "s38" "s39" "s40" "s41" "s42" "s43" "s44" "s45" "s46" "s47"
## [49] "s48" "s49" "s50" "s51" "s52" "s53" "s54" "s55" "s56" "s57" "s58" "s59"
## [61] "s60" "s61" "s62" "s63" "s64" "s65" "s66" "s67" "s68" "s69" "s70" "s71"
## [73] "s72" "s73" "s74" "s75" "s76" "s77" "s78" "s79" "s80" "s81" "s82" "s83"
## [85] "s84" "s85" "s86" "s87" "s88" "s89" "s90" "s91" "s92" "s93" "s94" "s95"
## [97] "s96" "s97" "s98" "s99"
glmnet으로 fit시킨 beta들의 값들은 s00의 값으로 0부터 저장되기 때문에, 이러한 과정을 거쳐야한다. 즉, 임의로 glmnet이 이름을 지은 것을 그대로 따라간다고 보면 된다.
Error<-NULL
for (i in 1:length(fit.tran$lambda)){
fit.tran.pred<-predict(fit.tran, tran.model.name=tran.model.name[i], newx=x[test,])
Error[i]<-mean((fit.tran.pred[,i] - y.test)^2)
}
Error
## [1] 214949.5 214949.5 214949.5 214949.5 214949.4 214949.4 214949.3 214949.2
## [9] 214949.1 214948.9 214948.7 214948.5 214948.1 214947.6 214947.0 214946.1
## [17] 214945.0 214943.5 214941.6 214939.0 214935.6 214931.0 214925.1 214917.2
## [25] 214906.7 214892.9 214874.7 214850.6 214818.8 214776.8 214721.2 214647.9
## [33] 214551.1 214423.5 214255.2 214033.5 213741.8 213358.5 212856.1 212203.8
## [41] 211350.9 210245.7 208822.6 207004.7 204706.9 201841.1 198327.6 194111.7
## [49] 189186.6 183616.8 177558.9 171262.5 165047.6 159250.3 154159.5 149960.1
## [57] 146709.7 144345.4 142745.1 141762.3 141247.9 141090.6 141205.5 141524.3
## [65] 141990.6 142559.1 143187.1 143837.9 144479.6 145086.8 145657.9 146153.9
## [73] 146571.1 146915.8 147189.8 147410.8 147600.0 147788.0 148007.6 148282.7
## [81] 148631.8 149052.7 149513.9 149992.4 150470.4 150929.1 151349.3 151721.0
## [89] 152037.5 152301.8 152515.5 152687.3 152822.9 152928.8 153009.4 153070.3
## [97] 153117.8 153152.2 153181.1 153200.9
각각 하나씩 설명하도록 하겠다
Error<-NULL #우선 오차값을 저장할 Error라는 공간을 만든다.
for(i in 1:length(fit.tran$lambda)){ #tran에 fit한 모델들의 람다의 갯수만큼 반복한다.
tran.fit.pred<-predict(fit.tran, tran.model.names=tran.model.names[i], xnew=x[test,])
tran으로 fit한 모델의 예측값을 구한다. 이 때,
predict
fit.tran <- fitting한 모델
tran.model.names=tran.model.name[i]
fitting한 모델이 여러가지 인데,(우리 모델로 치면 람다가 100개라서 모델이 100개), 이러한 모델들 중 어느 것을 fit 할 것인가?
newx<-예측을 하려고 하는 test set은 무엇인가? 즉, test set의 x관측값들을 넣으면 된다.
which.min(Error)->loc.lambda
which.min(Error)
## [1] 62
fit.tran$lambda[which.min(Error)]->opt.lambda
fit.tran$lambda[which.min(Error)]
## [1] 403.7017
이 때, tran의 fit한 모델에서 가장 작은 오차를 같는 lambda를 찾는다.
full.model<-glmnet(x,y,alpha=0,lambda=grid)
full.model$beta[,loc.lambda]
## AtBat Hits HmRun Runs RBI Walks
## 0.09238344 0.79744297 0.80505438 1.03411394 0.87728221 1.53862798
## Years CAtBat CHits CHmRun CRuns CRBI
## 1.83395501 0.01129748 0.05406625 0.38509943 0.10815217 0.11427651
## CWalks LeagueN DivisionW PutOuts Assists Errors
## 0.06137171 19.67455371 -72.32155123 0.15293830 0.02448004 -1.15688766
## NewLeagueN
## 9.45368575
predict(full.model,type="coefficients", s=opt.lambda)
## 20 x 1 sparse Matrix of class "dgCMatrix"
## s1
## (Intercept) 22.07163454
## AtBat 0.09238344
## Hits 0.79744297
## HmRun 0.80505438
## Runs 1.03411394
## RBI 0.87728221
## Walks 1.53862798
## Years 1.83395501
## CAtBat 0.01129748
## CHits 0.05406625
## CHmRun 0.38509943
## CRuns 0.10815217
## CRBI 0.11427651
## CWalks 0.06137171
## LeagueN 19.67455371
## DivisionW -72.32155123
## PutOuts 0.15293830
## Assists 0.02448004
## Errors -1.15688766
## NewLeagueN 9.45368575
이 때, 다시 모든 관측치들로 최상의 lambda를 넣어서 fitting을 시켜주면 된다.
1.glmnet는 cv.glmnet()으로 cross validation 옵션을 제공한다 2.cv.glmnet(x,y,alpha=옵션,nfolds=folding의 갯수)
set.seed(1)
cv.model<-cv.glmnet(x,y,alpha=0,nfolds=10)
names(cv.model)
## [1] "lambda" "cvm" "cvsd" "cvup" "cvlo"
## [6] "nzero" "call" "name" "glmnet.fit" "lambda.min"
## [11] "lambda.1se" "index"
우선, cv.glmnet로 cv된 모델을 만들었다. 자기 혼자서 cross validation을 진행 한 것이다.
이 때 names로 옵션을 살펴보자.
cvlo : cross validation lower bound : 로어바운드
cvm : cross validation median : 중앙값
cvup : cross validation upper bound : 어퍼바운드
이 세개는 one-standard-error-rule에 쓰인다
range<-cbind(cv.model$cvlo,cv.model$cvm,cv.model$cvup)
colnames(range)<-c("lower", "middle", "upper")
plot(cv.model)
이 때, 각각의 로어바운드, 어퍼바운드, 중앙을 나타내주었고, fit한 모델을 플랏에 표시해준 결과,
one-standard-error-Rule에 따라서 수축시킨 모델은 람다 값이 큰 쪽에서 그어져 있는 것을 볼 수 있고,
반대로, 에러가 제일 작은 것은 람다가 제일 작은 곳에 선이 그어져 있는 것을 확인할 수 있다.
log(cv.model$lambda.min)
## [1] 3.239784
log(cv.model$lambda.1se)
## [1] 7.519336
위와 같은 수식을 써서 구할수도 있다. lambda.min의 경우 오차가 가장 작을 때를 나타내고,
lambda.1se의 경우에는 최대한으로 축소시킬 수 있는 모델을 나타낸다.
which(cv.model$lambda==cv.model$lambda.min)
## [1] 100
which(cv.model$lambda==cv.model$lambda.1se)
## [1] 54
이 때, 100번째 람다에서는 오차를 가장 최소화하는 람다가 뽑한 것을 볼 수 있고, 54번째 람다에서는 축소를 최대로 시킨 모델임을 알 수 있다.
b.min=predict(cv.model, type="coefficients", s=cv.model$lambda.min)
b.1se=predict(cv.model, type="coefficients", s=cv.model$lambda.1se)
b.min
## 20 x 1 sparse Matrix of class "dgCMatrix"
## s1
## (Intercept) 8.112693e+01
## AtBat -6.815959e-01
## Hits 2.772312e+00
## HmRun -1.365680e+00
## Runs 1.014826e+00
## RBI 7.130225e-01
## Walks 3.378558e+00
## Years -9.066800e+00
## CAtBat -1.199478e-03
## CHits 1.361029e-01
## CHmRun 6.979958e-01
## CRuns 2.958896e-01
## CRBI 2.570711e-01
## CWalks -2.789666e-01
## LeagueN 5.321272e+01
## DivisionW -1.228345e+02
## PutOuts 2.638876e-01
## Assists 1.698796e-01
## Errors -3.685645e+00
## NewLeagueN -1.810510e+01
b.1se
## 20 x 1 sparse Matrix of class "dgCMatrix"
## s1
## (Intercept) 159.796625704
## AtBat 0.102483884
## Hits 0.446840518
## HmRun 1.289060569
## Runs 0.702915317
## RBI 0.686866068
## Walks 0.925962427
## Years 2.714623467
## CAtBat 0.008746278
## CHits 0.034359576
## CHmRun 0.253594870
## CRuns 0.068874010
## CRBI 0.071334608
## CWalks 0.066114944
## LeagueN 5.396487429
## DivisionW -29.096663728
## PutOuts 0.067805862
## Assists 0.009201998
## Errors -0.235989097
## NewLeagueN 4.457548059
이를 predict함수로 최종적인 모델을 추출할 수 있다.
predict(적합시킨 모델, type=“coefficient”, s=람다값)
(b.min[-1,])^2 %>% sum() ->L2.b.min
(b.1se[-1,])^2 %>% sum() ->L2.b.1se
cbind(L2.b.min,L2.b.1se)
## L2.b.min L2.b.1se
## [1,] 18367.29 906.8121
L2 norm을 보았을 때, 확실히 1se가 모델이 더욱 축소된 모습을 보여주고 있다.
set.seed(12345)
train<-sample(1:nrow(x), nrow(x)/2)
test<- (-train)
sigle split은 전체 데이터에 대해서, train set과 test set을 한번만 나누는 것을 말한다.
이 때, test set은 오직 검증만을 위해 사용되고, train set은 파라미터 튜닝을 위해 사용된다.
즉 test set안에 있는 x값은 전혀 사용되지 않고, 오직 y값만 사용된다
이 떄, train set과 test set은 각각 반반으로 지정하였다.
y.test<-y[test]
이 때, test set은 y값만 사용할 것이므로, test set안에있는 y값은 미리 추출하여 따로 저장해주자.
grid<-10^seq(10,-2,length=100)
fit.tran<-glmnet(x[train,],y[train],alpha=1,lambda=grid)
dim(coef(fit.tran))
## [1] 20 100
모델을 피팅한 결과, 100개에 람다에 대해서 20개의 회귀계수가 추출된 것을 알 수 있다.
이제 우리는 best lambda를 구하고 회귀계수를 추출해야 한다.
tran.model.name<-0:(length(fit.tran$lambda)-1)
tran.model.name
## [1] 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
## [26] 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
## [51] 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
## [76] 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
그 후, 모델의 이름을 설정해준다. 모델의 이름을 0부터 99까지 설정한 이유는, glmnet()의 고유한 특성 때문이다.
colnames(fit.tran$beta)
## [1] "s0" "s1" "s2" "s3" "s4" "s5" "s6" "s7" "s8" "s9" "s10" "s11"
## [13] "s12" "s13" "s14" "s15" "s16" "s17" "s18" "s19" "s20" "s21" "s22" "s23"
## [25] "s24" "s25" "s26" "s27" "s28" "s29" "s30" "s31" "s32" "s33" "s34" "s35"
## [37] "s36" "s37" "s38" "s39" "s40" "s41" "s42" "s43" "s44" "s45" "s46" "s47"
## [49] "s48" "s49" "s50" "s51" "s52" "s53" "s54" "s55" "s56" "s57" "s58" "s59"
## [61] "s60" "s61" "s62" "s63" "s64" "s65" "s66" "s67" "s68" "s69" "s70" "s71"
## [73] "s72" "s73" "s74" "s75" "s76" "s77" "s78" "s79" "s80" "s81" "s82" "s83"
## [85] "s84" "s85" "s86" "s87" "s88" "s89" "s90" "s91" "s92" "s93" "s94" "s95"
## [97] "s96" "s97" "s98" "s99"
glmnet으로 fit시킨 beta들의 값들은 s00의 값으로 0부터 저장되기 때문에, 이러한 과정을 거쳐야한다. 즉, 임의로 glmnet이 이름을 지은 것을 그대로 따라간다고 보면 된다.
Error<-NULL
for (i in 1:length(fit.tran$lambda)){
fit.tran.pred<-predict(fit.tran, tran.model.name=tran.model.name[i], newx=x[test,])
Error[i]<-mean((fit.tran.pred[,i] - y.test)^2)
}
Error
## [1] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [9] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [17] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [25] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [33] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [41] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [49] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5
## [57] 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 199378.5 182432.9
## [65] 156370.4 140265.6 130781.3 124290.5 118509.1 114790.8 112574.2 111248.3
## [73] 110445.9 110745.7 111025.7 110958.8 109744.6 108692.9 108219.6 108104.2
## [81] 108505.5 108893.4 108933.1 109221.9 110511.2 111767.3 112847.0 113766.4
## [89] 114462.8 115014.6 115452.3 115449.6 116117.6 116116.1 116115.7 116115.9
## [97] 116709.2 116707.6 116707.0 116706.6
각각 하나씩 설명하도록 하겠다
Error<-NULL #우선 오차값을 저장할 Error라는 공간을 만든다.
for(i in 1:length(fit.tran$lambda)){ #tran에 fit한 모델들의 람다의 갯수만큼 반복한다.
tran.fit.pred<-predict(fit.tran, tran.model.names=tran.model.names[i], xnew=x[test,])
tran으로 fit한 모델의 예측값을 구한다. 이 때,
predict
fit.tran <- fitting한 모델
tran.model.names=tran.model.name[i]
fitting한 모델이 여러가지 인데,(우리 모델로 치면 람다가 100개라서 모델이 100개), 이러한 모델들 중 어느 것을 fit 할 것인가?
newx<-예측을 하려고 하는 test set은 무엇인가? 즉, test set의 x관측값들을 넣으면 된다.
which.min(Error)->loc.lambda
which.min(Error)
## [1] 80
fit.tran$lambda[which.min(Error)]->opt.lambda
fit.tran$lambda[which.min(Error)]
## [1] 2.656088
이 때, tran의 fit한 모델에서 가장 작은 오차를 같는 lambda를 찾는다.
full.model<-glmnet(x,y,alpha=1,lambda=grid)
full.model$beta[,loc.lambda]
## AtBat Hits HmRun Runs RBI Walks
## -1.5600984 5.6931685 0.0000000 0.0000000 0.0000000 4.7505395
## Years CAtBat CHits CHmRun CRuns CRBI
## -9.5180241 0.0000000 0.0000000 0.5191611 0.6604074 0.3915415
## CWalks LeagueN DivisionW PutOuts Assists Errors
## -0.5326868 32.1125493 -119.2583540 0.2726207 0.1748164 -2.0567432
## NewLeagueN
## 0.0000000
predict(full.model,type="coefficients", s=opt.lambda)
## 20 x 1 sparse Matrix of class "dgCMatrix"
## s1
## (Intercept) 124.0894873
## AtBat -1.5600984
## Hits 5.6931685
## HmRun .
## Runs .
## RBI .
## Walks 4.7505395
## Years -9.5180241
## CAtBat .
## CHits .
## CHmRun 0.5191611
## CRuns 0.6604074
## CRBI 0.3915415
## CWalks -0.5326868
## LeagueN 32.1125493
## DivisionW -119.2583540
## PutOuts 0.2726207
## Assists 0.1748164
## Errors -2.0567432
## NewLeagueN .
이 때, 다시 모든 관측치들로 최상의 lambda를 넣어서 fitting을 시켜주면 된다.
1.glmnet는 cv.glmnet()으로 cross validation 옵션을 제공한다 2.cv.glmnet(x,y,alpha=옵션,nfolds=folding의 갯수)
set.seed(2)
cv.model<-cv.glmnet(x,y,alpha=1,nfolds=10)
names(cv.model)
## [1] "lambda" "cvm" "cvsd" "cvup" "cvlo"
## [6] "nzero" "call" "name" "glmnet.fit" "lambda.min"
## [11] "lambda.1se" "index"
우선, cv.glmnet로 cv된 모델을 만들었다. 자기 혼자서 cross validation을 진행 한 것이다.
이 때 names로 옵션을 살펴보자.
cvlo : cross validation lower bound : 로어바운드
cvm : cross validation median : 중앙값
cvup : cross validation upper bound : 어퍼바운드
이 세개는 one-standard-error-rule에 쓰인다
range<-cbind(cv.model$cvlo,cv.model$cvm,cv.model$cvup)
colnames(range)<-c("lower", "middle", "upper")
plot(cv.model)
이 때, 각각의 로어바운드, 어퍼바운드, 중앙을 나타내주었고, fit한 모델을 플랏에 표시해준 결과,
one-standard-error-Rule에 따라서 수축시킨 모델은 람다 값이 큰 쪽에서 그어져 있는 것을 볼 수 있고,
반대로, 에러가 제일 작은 것은 람다가 제일 작은 곳에 선이 그어져 있는 것을 확인할 수 있다.
log(cv.model$lambda.min)
## [1] 0.8906821
log(cv.model$lambda.1se)
## [1] 4.239897
위와 같은 수식을 써서 구할수도 있다. lambda.min의 경우 오차가 가장 작을 때를 나타내고,
lambda.1se의 경우에는 최대한으로 축소시킬 수 있는 모델을 나타낸다.
which(cv.model$lambda==cv.model$lambda.min)
## [1] 51
which(cv.model$lambda==cv.model$lambda.1se)
## [1] 15
이 때, 51번째 람다에서는 오차를 가장 최소화하는 람다가 뽑한 것을 볼 수 있고, 15번째 람다에서는 축소를 최대로 시킨 모델임을 알 수 있다.
b.min=predict(cv.model, type="coefficients", s=cv.model$lambda.min)
b.1se=predict(cv.model, type="coefficients", s=cv.model$lambda.1se)
cbind(b.min,b.1se)
## 20 x 2 sparse Matrix of class "dgCMatrix"
## s1 s1
## (Intercept) 129.4155569 127.95694771
## AtBat -1.6130155 .
## Hits 5.8058915 1.42342566
## HmRun . .
## Runs . .
## RBI . .
## Walks 4.8469340 1.58214110
## Years -9.9724045 .
## CAtBat . .
## CHits . .
## CHmRun 0.5374550 .
## CRuns 0.6811938 0.16027975
## CRBI 0.3903563 0.33667715
## CWalks -0.5560143 .
## LeagueN 32.4646094 .
## DivisionW -119.3480842 -8.06171247
## PutOuts 0.2741895 0.08393604
## Assists 0.1855978 .
## Errors -2.1650837 .
## NewLeagueN . .
이를 predict함수로 최종적인 모델을 추출할 수 있다.
predict(적합시킨 모델, type=“coefficient”, s=람다값)
(b.min[-1,])^2 %>% sum() ->L2.b.min
(b.1se[-1,])^2 %>% sum() ->L2.b.1se
cbind(L2.b.min,L2.b.1se)
## L2.b.min L2.b.1se
## [1,] 15463.18 69.66661
L2 norm을 보았을 때, 확실히 1se가 모델이 더욱 축소된 모습을 보여주고 있다.