mmings_pring_day

[통계 데이터분석] 패널티회귀분석 본문

통계

[통계 데이터분석] 패널티회귀분석

mming_10 2024. 12. 1. 23:11

0.  페널티회귀분석 개요

- 표준적인 선형회귀모델은 독립변수의 개수가 표본크기에 비해 지나치게 많은 경우 제대로 된 성능을 발휘하기 어려움.  
   (불필요한 회귀계수는 모델의 예측성능을 저하시키기 때문임)
- 또한 많은 독립변수가 포함되어 있으면 다중공선성으로 인해 일부 회귀계수의 영향력이 과다 추정될 수 있음

=> 통계데이터분석) 모델의 단순화는 매우 중요함
 (최소한의 독립변수로 이루어진 간명한 모델은 유사한 다른 문제로의 일반화 가능성을 크게 해주기 때문)
=> 예측정확도가 비슷한 두 대안모델이 있으면 일반적으로 독립변수의 개수가 더 적은 단순한 모델을 선택하는 것이 과적합의 위험을 피할 수 있다는 점에서 보다 바람직히다

 

- 지나치게 너무 많은 독립변수를 갖는 모델, 즉 복잡한 모델에 페널티를 부과하는 방식으로 보다 간명한 회귀모델을 생성함

   -> 이를 통해, 모델의 성능에 크게 기여하지 못하는 변수의 영향력을 축소하거나 아예 모델을 제거

       (모델 내에 지나치게 많은 독립변수가 포함되는 것을 방지함)

    -> 페널티회귀모형은 여기에 패널티항을 추가하여,  최소자승법에 의한 잔차의 제곱합과 페널티항의 합이 최소가 되는 회귀계수를 추정함

       ( vs 표준적인 선형회귀모델이 최소자승법에 의해 잔차 (관측값- 예측값)의 제곱합을 최소화하는 회귀계수를 추정함)

-  릿지회귀분석, 라소회귀분석, 일래스틱넷회귀분석 등 가장 널리 사용되는 세 가지 페널티회귀분석 기법을 살펴본다.

 

0-1.  릿지회귀분석

'모델의 설명력에 기여하지 못하는 독립변수의 회귀계수 크기를 0에 근접하도록 축소함'

 - 'L2-norm' 이라고 불리는 페널티항으로 회귀모델에 페널티를 부과함으로써 회귀계수를 축소.

  (L2- norm 은 각 회귀계수를 제곱하고 이를 합산하여 계산한다.)

릿지회귀모델은 다음과 같이 잔차의 제곱합과 L2- norm 의 합을 최소화하는 회귀계수를 추정한다.
릿지회귀분석 = 최소자승법에 의한 잔차의 제곱합 (회귀계수 B 추정)_표준 선형회귀모델 + 페널티항의 합 

 

- L2- norm 을 통해 페널티를 줌으로써 회귀계수가 과다 추정되는 것을 방지한다.

페널티의 크기는 '람다'를 이용하여 조정할 수 있다.

('람다' 가 0이면 페널티항은 0이 되고 릿지회귀모델은 표준적인 선형회귀모델과 동일한 회귀계수를 추정한다. 그러나 '람다'가 증가함에 따라 페널티의 영향은 커지고 릿지회귀모델의 회귀계수는 축소된다. '람다'가 매우 큰 값을 가지면 회귀계수는 0에 근접하게 된다.)

 

- 표준적인 선형회귀분석과 달리 릿지회귀분석은 독립변수의 척도에 크게 영향을 받는다.

 -> 따라서 릿지회귀분석을 적용하기 전에 독립변수들이 모두 동일한 척도를 갖도록 표준화하는 것이 바람직하다.

 관측값 개수보다 독립변수의 개수가 상대적으로 많은 대규모 다변량 데이터의 경우, 릿지회귀분석은 표준적인 선형회귀분석에 비해 더 우수한 성능을 보이는 것으로 알려져 있다. 릿지회귀분석은 회귀계수를 0에 가깝도록 축소하지만 어떠한 회귀계수도 0으로 만들지는 않는다.

(= 원래 이 데이터셋에 포함되어 있는 모든 독립변수가 릿지회귀모델에도 그대로 포함된다는 것을 의미함)

따라서 단계선택법을 적용하여 통계적으로 유의한 독립변수만을 유지하는 선형회귀분석과 달리 릿지회귀분석은 회귀모델에 모든 독립변수를 그대로 포함하게 되므로 변수선택을 통해 모델을 단순화하는 효과는 제공하지 못한다.


0-2.  라소회귀분석

- 모델의 설명력에 기여하지 못하는 독립변수의 회귀계수 크기를 0으로 만듦

  -> 이를 통해 라소회귀분석은 릿지회귀분석과 달리 변수선택을 통해 간명한 모델을 만들 수 있음

 라소회귀모델은 다음과 같이 '잔차의 제곱합' (최소자승법을 통해 최소화) 과 L1-norm 의 합을 최소화하는 회귀계수를 추정한다.

- 'L1-norm' 페널티항으로 회귀모델에 페널티를 부과함으로써 회귀계수를 축소함

   (L1-norm 은 각 회귀계수의 절대값을 합산하여 계산함)

 

'람다'는 릿지회귀분석에서와 마찬가지로 페널티의 크기를 조정한다. 라소회귀분석에서 페널티는 모델에 대한 기여도가 낮은 회귀계수를 0으로 만든다.
-> 이는 해당 변수는 '모델에서 제거된다는 것'을 의미함
-> 따라서 라소회귀분석을 이용하면 변수선택을 통해 설명력이 우수한 독립변수만을 모델에 포함할 수 있고, 이를 통해 모델의 복잡성을 축소하는 효과를 기대할 수 있다.
- 라소회귀분석은 전체 독립변수가 아니라 보다 작은 규모의 독립변수만을 모델이 포함하기 때문에 릿지회귀분석에 비해 좀 더 간명하고 해석 가능한 모델을 생성한다는 장점을 갖는다.
(그러나 이것이 라소회귀분석이 릿지회귀분석보다 더 우수하다는 것을 의미하지는 않음!)

- 라소회귀분석은 일부 독립변수의 회귀계수는 크고 나머지 독립변수의 회귀계수는 매우 작을 때 일반적으로 우수한 성능을 발휘한다.
(반면에 릿지회귀분석은 많은 독립변수의 선형결합으로 결과변수를 예측할 수 있고 이 독립변수들의 회귀계수 크기가 서로 비슷할 때 더 나은 성능을 보여줌)
-> 따라서 두 기법 가운데 어느 한 기법이 더 우수하다고 말할 수는 없으며, 일반적으로 특정 데이터셋에 대한 교차검증을 바탕으로 어느 기법이 우수한지 평가한다

 

- 교차검증: 하나의 데이터셋을 바탕으로 해서 여러 개의 훈련 데이터와 테스트 데이터셋을 생성하고 그로부터 여러 차례 성능을 평가한 다음, 얻어진 성능 평가 값들을 평균해서 해당 모델의 최종적인 성능을 측정하는 방식을 얘기함  


0-3.  일래스틱넷 회귀분석

-  'L1-norm' 과 'L2- norm' 모두를 이용하여 회귀모델에 페널티를 부과함

- 페널티의 크기는 릿지회귀분석과 라소회귀분석에서와 마찬가지로 '람다'를 이용하여 조정할 수 있다.

- 일래스틱넷회귀분석은 릿지회귀분석과 라소회귀분석을 결합한 형태로서

   1) 릿지회귀분석에서처럼 모델을 충분히 설명하지 못하는 일부 독립변수의 회귀계수 크기를 축소하거나

   2) 라소회귀분석에서처럼 회귀계수를 강제로 0으로 할당해서 특정 독립변수를 모델에서 제거할 수도 있음

('a' 는 릿지회귀모델과 라소회귀모델의 혼합 정도를 통제함.

 -> 'a= 0'이면 순수한 릿지회귀분석을 수행하고, 'a=1'이면 순수한 라소회귀분석을 수행한다)


[ MASS 패키지에 포함된 Boston 데이터셋을 이용하여 패널티회귀분석의 세가지 기법을 수행하고 이들의 성능을 비교]

  • Boston 데이터셋: 미국 보스턴 지역의 주택가격과 그 주변 환경에 대한 정보가 저장되어 있는 데이터셋 
  • 13개의 예측변수를 통해 '결과변수인 주태가격' 예측! 
library(MASS)
str(Boston)

 

1) 전체 데이터를 예측모델 생성을 위한 훈련 데이터와 검증을 위한 테스트 데이터로 분할함

  • caret 패키지의 creatDataPartition( ) 함수 이용:  훈련 데이터와 테스트 데이터를 7: 3 의 비율로 분할하는 함수 
  • -> creatDataPartition( ) 함수: p 인수에 지정된 비율만큼의 데이터를 훈련 데이터로 분할함
  • -> 훈련데이터를 행의 인덱스로 출력해 줌 (이 인덱스를 이용해서 훈련 데이터와 테스트 데이터를 생성할 수가 있음)
library(caret)
set.seed(123) #동일한 결과를 재현하기 위햇 seed 넘버를 부여함
train <- createDataPartition(y=Boston$medv, p=0.7, list=FALSE)
# createDataPartition() 함수의 1. 인수(y 인수): 결과 변수 지정 (주택 가격)
# createDataPartition() 함수의 2. 인수(p 인수): 결과 변수 지정 (훈련과 테스트 데이터셋을 분리할 때, 훈련 데이터의 비율 지정)
# createDataPartition() 함수의 3. 인수(list 인수): 기본적으로 TRUE 값이 지정되어 있음 
#  -> TRUE 일 땐, 리스트 형식으로 데이터의 인덱스가 출력됨
#  -> FALSE 일 땐, 행렬 형식으로 출력됨
head(train) # createDataPartition() 함수의 실행 결과를 train 변수에 저장

Boston.train <- Boston[train,]
# 훈련 데이터 인덱스를 행의 인덱스로 지정하여, 보스턴 데이터셋으로부터 훈련 데이터셋을 생성함
Boston.test <- Boston[-train,]
# 나머지 데이터는 test 데이터셋으로 사용 (Boston 데이터셋의 인덱스에 - 값을 부여하여 나머지 데이터셋 생성)

nrow(Boston.train)
nrow(Boston.test)

-> 이 데이터셋을 이용해서 모델을 생성하고, 생성된 모델의 성능을 평가해 보겠음

 

  • glmnet 패키지의 glmnet( ) 함수 이용: 패널티회귀분석 수행 함수
library(glmnet)
set.seed(123)
diabetes.cv <- cv.glmnet(x=x, y=y, family="gussian", alpha=1)
# 지정해야 하는 인수: x, y, family, alpha, lambda 인수
# x 인수 (예측 변수)/ y 인수 (결과 변수)
# family 인수: 결과 변수의 확률 분포를 지정함 (지금처럼, 연속형 변수가 결과변수일 경우엔, gussian 지정)
# alpha 인수: 릿지회귀분석, 라소회귀분석, 그리고 일래스틱넷회귀분석 선택하여 지정 가능

 

  •  * glmnet( ) 함수는 '포뮬러 형식'의 모델 설정을 지원하지는 않음. 따라서 예측변수 (x) 와 결과변수 (y) 를 각각 별도로 지정할 필요가 있다.
  • glmnet( ) 함수의 1. x 인수, 2. y 인수:  예측변수 (x)는 행렬 형식으로 지정되어야 하며 결과변수 (y)는 벡터 형식으로 제공되어야함
  • -> glmnet( )함수는 예측변수로 숫자만을 취할 수 있으므로 범주형 변수는 사전에 더미변수로 변환되어야 함 
  • glmnet( ) 함수의 family 인수: 결과 변수의 확률 분포를 지정함 (지금처럼, 연속형 변수가 결과변수일 경우엔, gussian 지정)
  • glmnet( ) 함수의 alpha 인수 지정: 릿지회귀분석, 라소회귀분석, 일래스틱넷회귀분석을 수행할 수 있음
  •  (alpha= 0 을 지정: 릿지회귀분석을 수행함/ alpha= 1 을 지정: 라소회귀분석을 생성/  alpha= 0과 1 사이의 값을 지정하면 L1-norm 페널티와 L2-norm 페널티가 결합된 일래스틱넷회귀분석을 수행함) 
  • 페널티의 크기를 조정하기 위해 lambda 지정 : lambda 에 따라서 회귀계수의 크기에 축소 정도가 결정됨
  •  (예측 오차를 최소화하는 lambda 를 '최적 lambda' 로 일반적으로 결정하게 되고, 교차검증을 통해서 이러한 lambda 값을 확인하게 됨)

 

-  예측변수 (x)는 행렬 형식으로 지정되어야 하고, 숫자만을 취할 수가 있기 때문에 사전 처리 작업을 수행해야 함

  • model.matrix( ) 함수 이용: 데이터 전처리 작업 쉽게 수행할 수 있는 함수 
  • model.matrix( ) 함수: 모델에 투입할 예측변수의 행렬을 생성하고 범주형 변수를 더미변수로 자동 변환함
  • model.matrix( ) 함수: 예측변수 행렬을 생성하는 함수
  • * model.matrix( ) 함수로부터 생성된 행렬의 첫 번째 열은 불필요하므로 제거함
  • * 또한 glmet( ) 함수의 적용을 위해 결과변수 벡터를 별도로 생성함
x <- model.matrix(diabetes ~ ., Boston.train)[,-1]
# model.matrix() 함수의 1. 인수 (formula 인수): 결과변수(종속) ~ 예측변수(독립) 간의 관계 지정
# -> 결과 변수를 제외한 모든 변수를 예측변수로 사용 (에측변수를 '.' (점)으로 지정)
# model.matrix() 함수의 2. 인수: 훈련 데이터셋 지정
head(x) 
# -> 첫번째 행은 불필요하기 때문에 삭제: [,-1]

y <- Boston.train$medv

-> 이렇게 생성된 예측변수 행렬과 결과변수 벡터를 이용해서 페널티회귀분석 수행 가능

  • * 또한 glmet( ) 함수의 적용을 위해 결과변수 벡터를 별도로 생성함
  • glmnet( ) 함수의 x 인수: 예측변수/ y 인수: 결과 변수 벡터를 지정함/ family 인수: 결과 변수의 확률 분포 지정/ 
  • -> family 인수의 기본값: guassian ) 결과 변수가 연속형 변수인 표준적인 선형 회구분석 수행 
  • alpha 인수: 0 을 지정하여 '릿지회귀분석' 수행
  • lambda 인수: 페널티 크기를 조정하는 람다 값을 지정함 (람다 값에 따라서 회귀계수 크기의 축소 정도가 결정됨) 
  • -> 예측오차를 최소화하는 '람다'를 최적의 '람다'로 결정하며, 이는 일반적으로 교차검증을 통해 결정된다
glmnet(x=x, y=y, family='guassian', alpha=0, lambda=  )
# -> alpha=0, 릿지회귀분석 수행

 

  • glmnet 패키지의 cv.glmnet( ) 함수 이용:k- 묶음 교차검증을 (k-fold cross-validation) 을 수행하는 함수
  • (기본적으로 10- 묶음 교차검증을 수행함: k= 10 일 떼, k- 묶음 교차검증 수행)
[k- 묶음 교차검증 (k-fold cross- validation)]
-> k- 묶음 교차검증은 k 들을 서로 중복되지 않는 k 개의 묶음으로 분할
=  전체 데이터셋을 k개의 서브셋 (묶음, fold)으로 분할하여 각 서비스셋과 나머지 <k-1> 개의 서브셋을 각각 테스트 데이터와 훈련 데이터로 사용하여 성능평가와 모델생성 작업을 수행
- k번의 성능평가 결과를 평균하여 모델의 최종성능을 산출함

(1) 전체 데이터셋을 10개의 서브셋으로 분할함
(2) 10개의 서브셋 각각 한 번씩 테스트 데이터로 사용되고 나머지 서브셋들이 훈련 데이터로 사용되는 방식으로 모델을 생성하고 모델의 성능을 평가함
(3) (k-1) 나머지 훈련 데이터로 모델을 생성하고 테스트 데이터 (1) 를 이용해서 성능을 평가함 
(4) k 개의 성능 지표값이 나옴 -> 이 값을 모두 더한 다음에 k 로 나누게 되면 '평균적인 모델의 성능을 구할 수 있음'

 

  • cv.glmnet( ) 함수: glmnet( ) 함수와 대부분의 인수를 공유함 (* glmnet( ) 함수= 페널티회귀분석에 사용)
  • -> glmnet 의 인수 중 'lambda' 인수를 제외한 모든 이수를 그대로 사용할 수 있음 

[alpha= 0 을 지정하여 '릿지회귀 모델에서의 최적의 람다'를 탐색해 보겠음]

1.  릿지회귀분석

릿지회귀분석을 이용하여 보스턴 지역의 주택가격 예측 

- cv. glmnet( ) 함수: family= 'guassian' 일 경우, 기본적으로 MSE (mean squared error) 를 최소로 만든느 '람다'를 탐색함 

[성능평가지표] 
- MSE (mean squared error): (관측값 -예측값) ^2 들의 합 / 표본의 크기  [잔차 제곱의 평균값] 

- RMSE (root mean squared error): MSE 에 루트 를 씌운 값

- MAE (mean absolute error) 관측값 - 예측값  값에 절댓값을 취한 값의 합 / 표본의 크기 [절댓값으로 표현된 편차의 평균]
set.seed(123)
Boston.cv <- cv.glmnet(x=x, y=y, family="gaussian", alpha=0)
windows(width=7.0, height=5.5)
plot(Boston.cv)

람다에 로그를 취한 로그를 람다에 따른 mse 의 변화 추이를 그래포로 확인할 수 있음

- 왼쪽의 점선은 최적 '람다'의 로그값이 대략 -0.4 라는 것을 나타냄 ( 이 값이  예측오차를 최소화하는 로그 람다) 
- 그래프 상단의 숫자 13 은 예측변수의 개수
(람다 값이 변화함에 따라서 mse 의 값이 변화하고 그때 사용되는 예측 변수의 개수가 그래프 상단에 표현되어 있음)

- 릿지 회귀분석은 어떠한 회귀계수도 0으로 만들지는 않으므로, 람다 탐색 과정에서도 원래의 예측변수 개수는 그대로 유지됨

 

str(Boston.cv)

->  lambda.min 원소 : MSE 를 최소화하는 실제의 람다 값을 확인할 수 있음

(최적의 람다를 확인했으므로 이를 glmnet( ) 함수의 lamba 인수에 지정하여 예측모델을 생성함)

Boston.cv$lambda.min
log(Boston.cv$lambda.min) #로그 람다 값의 실제 값 확인

 

  • coef( ) 함수 이용: 생성된 릿지회귀모델의 회귀계수 추출 가능 함수 
Boston.gnet <- glmnet(x, y, family="gaussian", alpha=0, lambda=Boston.cv$lambda.min)

coef(Boston.gnet)
# coef() 함수의 인수: 생성한 릿지회귀모델 객체를 지정

- 릿지회귀모델을 테스트 데이터에 적용하여 예측 모델의 성능을 평가해보자.

테스트 데이터의 예측변수 행렬을 생성한 후 predict( ) 함수 이용: 주택가격을 예측

Boston.test.x <- model.matrix(medv ~ ., Boston.test)[,-1]
# 테스트 데이터의 예측변수 행렬을 생성하기 위해서 model.matrix() 함수 다시 사용

Boston.pred <- predict(Boston.gnet, newx=Boston.test.x)
# predict()함수의 1. 인수: 릿지회귀모델 지정
# predict()함수의 2. 인수: (newx 인수) 예측변수 행렬 지정 
head(Boston.pred)

-> 테스트 데이터의 예측 변수 값에 따른 예측 모델에서 예측된 주택가격 확인 가능 

  • caret 패키지의 postResample( ) 함수 이용: 예측모델의 성능 평가 가능 함수
  •  ->  기본적으로 RMSE, R^2, MAE 등 세가지 성능평가 지표로 출력됨
  • -> RMSE 와 MAE= 오차를 나타내기 때문에 '값이 작을수록 우수한 성능을 나타냄'
  • -> R^2 (예측 변수에 대한 결과 변수의 설명력을 나타내는 지표이기에 -> 값이 클수록 우수한 성능을 나타냄)
postResample(pred=Boston.pred, obs=Boston.test$medv)
# postResample()함수의 1.인수 = (pred 인수) 예측 모델에 의해서 예측된 주택가격 지정
# postResample()함수의 2.인수 = (obs 인수) 실제 관측값을 지정 (테스트 데이터에 포함되어 있는 결과 변수인 주택가격 지정)


2.  라소회귀분석

라소회귀분석을 이용하여 보스턴 지역의 주택가격 예측

- cv.glmnet( ) 함수나 glmnet( ) 함수를 적용할 때 릿지회귀분석과 라소회귀분석 간의 유일한 차이는, alpha 인수에 지정된 값임

  -> alpha= 0 대신에 alpha=1 을 지정하면 라소회귀분석 수행 

 

(1) 예측 오차를 최소화하는 람다를 산출

set.seed(123)
Boston.cv <- cv.glmnet(x=x, y=y, family="gaussian", alpha=1)
windows(width=7.0, height=5.5)
plot(Boston.cv)

[람다 값과 MSE 간의 관계 살펴보기]

- 릿지회귀분석에서와 마찬가지로 왼쪽의 점선이 예측오차를 최소화하는 (즉, 예측 정확도를 가장 크게 하는 ) 람다를 나타냄

라소회귀분석에선, '중요하지 않은 예측변수의 회귀계수는 0으로 만들기 때문에 릿지회귀분석에서와 달리 로그 람다 에 따라 상단의 예측변수 개수가 변화하는 것을 볼 수 있음

 

(2) 예측 오차를 최소화하는 람다를 산출 (릿지회귀분석에서와 마찬가지로, 교차검증 객체 'lambda.min'원소에서 확인 가능

Boston.cv$lambda.min
log(Boston.cv$lambda.min)

-> 로그 람다의 값이 '-4.5' 일 때 mse 의 값이 최소가 된다는 것을 그래프에서 확인 가능

 

- 패널티회귀분석은 일반적으로, 정확도를 가능한 크게 하면서 동시에 과적합의 위험을 최소화하는 것을 목표로 한다.

-> 즉, 모델의 예측정확도와 간명도 간의 균현을 찾는 것이 목적이다.

=> 이는 다시 말해, 최소한의 예측변수로 적정 수준의 정확도를 제공하는 모델을 탐색하는 것을 의미한다.

 

  • cv.glmnet( ) 함수: 이러한 목적을 위해 예측오차를 최소화하는 최적의 람다 뿐만 아니라, 최소 예측오차의 한 개 표준편차 이내에 있으면서 예측변수의 개수를 최소화하는 (즉, 가장 단순한 모델을 생성하는 ) 람다를 탐색하는 함수

-> 이러한 람다 값을 다음과 같이 교차검증 객체의 lambda.lse 에서 확인할 수 있음

Boston.cv$lambda.1se
log(Boston.cv$lambda.1se)

 

- lambda.lse 에 로그 값을 취한 '로그 람다 값'은 그래프 상의 오른쪽 점선으로 표현됨 

(오른쪽 점선은 '예측 정확도와 간명도 간의 균형점'을 제공하는 로그 감다 값을 나타냄) 

 

coef(Boston.cv, Boston.cv$lambda.min)
coef(Boston.cv, Boston.cv$lambda.1se)
# -> 교차 검증 모델과 사용할 람다를 지정함
  • lambda.min 을 사용할 경우의 회귀계수는 다음과 같다.

(라소모회귀모델에선 한 개의 회귀계수 (age)가 제거되어 12개의 예측변수가 포함되었음)

  • lambda.lse 를 사용할 경우의 회귀계수는 다음과 같다.

(라소회귀모델은 10개의 예측변수의 회귀계수만을 추정함 (age, rad, zn 제거)) => lambda.min 을 사용한 예측 모델에 비해 간명한 모델이 생성되었음

[lambda.min 을 사용한 경우와 lambda.lse 를 사용한 경우의 성능을 비교해보자]
- 10개의 변수가 포함된 lambda.lse 를 사용한 모델이 간명하긴 하지만, 12개 변수가 포함된 lambda.min 을 사용한 모델에 비해 예측성능은 다소 떨어진 것을 알 수 있음
(즉, 예측정확도와 간명도 간의 트레이드오프로 인해 lambda.lse 는 과적합의 위험은 줄이면서 정확도는 다소 낮은 예측모델을 생성함)
Boston.gnet1 <- glmnet(x, y, family="gaussian", 
                       alpha=1, lambda=Boston.cv$lambda.min)
Boston.pred1 <- predict(Boston.gnet1, newx=Boston.test.x)
postResample(pred=Boston.pred1, obs=Boston.test$medv)

Boston.gnet2 <- glmnet(x, y, family="gaussian", 
                       alpha=1, lambda=Boston.cv$lambda.1se)
Boston.pred2 <- predict(Boston.gnet2, newx=Boston.test.x)
postResample(pred=Boston.pred2, obs=Boston.test$medv)

 

3.  일레스틱넷회귀분석

일래스틱넷회귀분석을 이용하여 보스턴 지역의 주택가격 예측

(일래스틱넷회귀분석은 '릿지회귀분석'과 '라소회귀분석'을 결합한 형태 -> 람다 뿐만 아니라 L1-norm 과 L2-norm 페널티의 비중을 결정하는 a 도 선택해야 함.)

  • caret 패키지의 train( ) 함수 이용: 인수튜닝을 통해 비교적 쉽게 최적의 a 와 람다를 탐색할 수 있음
  • train( ) 함수의 첫 번째 인수 (form 인수): 결과변수와 예측변수를 포뮬러 형식으로 지정함
  • train( ) 함수의 data 인수: 훈련 데이터를 지정함
  • train( ) 함수의 method 인수: 모델의 함수 이름을 지정하는 것 만으로도 간단히 인수튜닝을 수행할 수 있음
  •  -> 모델의 함수 이름 (페널티회귀 분석 수행을 위한 glment()함수 이름 지정
library(caret)
set.seed(123)
Boston.cv <- train(form=medv ~ ., data=Boston.train, method="glmnet",
                   trControl=trainControl(method="cv", number=10),
                   tuneLength=10)
  • trainControl( ) 함수 이용: 다양한 인수 옵션을 저장한 객체를 생성한 후 이를 train( ) 함수의 trControl 인수에 지정함으로써 기본 설정을 이용하는 것보다 더 나은 성능의 모델을 생성하는 것이 가능함
  • trainControl( ) 함수의 method 인수 : 표본추출 방법을 설정하는 데 이용됨
  • -> method= 'cv' 지정 ) k- 묶음 교차검증을 위한 표본 추출
  • number 인수에 서브셋의 개수 (k) 를 지정함
  • -> number = 10 지정 ) 10 -묶음 교차검증을 수행함
  • train( ) 함수의 tuneLength 인수: 튜닝할 파라미터값의 탐색 개수를 지정함 
  • -> tuneLength = 10 지정) a 와 람다 각각 10개의 서로 다른 값을 이용하여 이들의 조합을 바탕으로 교차검증을 수행함
Boston.cv$bestTune

  • bestTune 원소를 통해 최적의 a 와 람다 값 산출 가능
  • -> 최적의 a 와 람다를 이용하여 '예측모델' 을 생성함
Boston.gnet <- glmnet(x, y, family="gaussian", 
                      alpha=Boston.cv$bestTune$alpha, 
                      lambda=Boston.cv$bestTune$lambda)
# glmnet() 함수의 alpha 인수: 조금 전 산출된 알파 값 지정
# glmnet() 함수의 lambda 인수: 조금 전 산출된 lambda 값 지정

coef(Boston.gnet)

  • (한 개의 변수_age 가 모델에서 제외되었음)

-> 테스트 데이터를 이용해서 이렇게 생성한 일래스틱넷회귀모델 의 성능 평가: 예측모델에 의해 예측된 주택가격 확인 가능 

Boston.pred <- predict(Boston.gnet, newx=Boston.test.x)
postResample(pred=Boston.pred, obs=Boston.test$medv)

[ 패널티회귀분석 모델 비교 ]

- 페널티회귀분석의 릿지회귀모델, 라소회귀모델, 일래스틱넷회귀모델 가운데 '어느 모델을 선택하느냐'는 결국, 예측정확도와 간명도 간 선택의 문제일 수 있음

-> 일반적으로, 정확도에 있어서 큰 차이가 없다면, 좀 더 간명한, 즉 예측변수의 개수가 더 작은 모델을 선택하는 것이 바람직함

(과적합의 위험을 줄여 일반화 가능성을 크게 해주기 때문에)

 

  • caret 패키지의 train( ) 함수 이용:  릿지회귀모델과 라소회귀모델에 대해서도 인수튜닝을 통해 최적의 모델을 생성하고 이들 3가지의 패널티회귀 모델의 성능을 비교 평가!

(1) 탐색할 lambda 의 범위 지정: 10의 마이너스 5승 ~ 10의 5승까지 보면서 100 개의 lambda 값을 탐색 

library(caret)
lambda <- 10^seq(-5, 5, length=100)

 

(2) caret 패키지의 train( ) 함수 이용: 이들 100개의 lambda 에 대한 교차검증을 수행하고 이를 바탕으로 최적의 릿지회귀모델을 생성 

[인수 튜닝: 다양한 인수 조합에 따른 여러 대안 모델을 탐색하는 과정임]

 -> 탐색하고자 하는 인수 조합을 'grid'라는 팩 형태로 구성하여 각 인소 조합에 대응되는 대안 모델을 탐색 

 -> 탐색해야 하는 인수값의 조합이 많을 경우) 하나하나 입력해서 그리드를 생성하는 것은 매우 비효율적

  • expand.grid( ) 함수 이용: 이러한 그리드 생성 작업을 간단히 수행 가능
  • expand.grid( ) 함수 이용: 인수로 주어진 값의 모든 가능한 조합을 그리드 형태로 변환함
  • -> 생생한 그리드를 train( ) 함수의 turnGrid 인수에 지정함 

[인수 튜닝 결과로부터 생성된 최종 모델의 회귀계수 추출)

  • coef( )함수의 1. 인수: 최종 모델/ 2. 인수: 그때의 lambda 값을 지정함 
set.seed(123)
ridge <- train(medv ~ ., data=Boston.train, method="glmnet",
               trControl=trainControl(method="cv", number=10),
               tuneGrid=expand.grid(alpha=0, lambda=lambda))
coef(ridge$finalModel, ridge$bestTune$lambda)

[테스트 데이터에 대한 릿지회귀모델의 성능은 다음과 같음- 예측 모델을 통해서 생성된 예측값을 이용하여 모델의 성능 평가]

  • predict( ) 함수의 1. 인수: train( ) 함수로부터 생성된 모델 객체 지정
  • predict( ) 함수의 2. 인수: 테스트 데이터 지정
ridge.pred <- predict(ridge, Boston.test)
postResample(pred=ridge.pred, obs=Boston.test$medv)

 

 

- 동일한 100개 lambda 에 대한 교차검증을 바탕으로 최적의 라소회귀모델 (alpha=1) 을 생성하고 성능을 평가한다. 

expand.grid( ) 함수의 alpha 인수: alpha = 1 을 지정 

set.seed(123)
lasso <- train(medv ~ ., data=Boston.train, method="glmnet",
               trControl=trainControl(method="cv", number=10),
               tuneGrid=expand.grid(alpha=1, lambda=lambda))
coef(lasso$finalModel, lasso$bestTune$lambda)

lasso.pred <- predict(lasso, Boston.test)
postResample(pred=lasso.pred, obs=Boston.test$medv)

 

- 일래스틱넷회귀모델은앞과 동일하게 a 와 lamnda 에 대해 10개의 서로 다른 값의 조합을 이용하여 교차검증을 수행하여 생성함

-> train 인수에 tune.Grid 인수 대신, 'tuneLength 인수 이용'

set.seed(123)
elastic <- train(form=medv ~ ., data=Boston.train, method="glmnet",
                 trControl=trainControl(method="cv", number=10),
                 tuneLength=10)
coef(elastic$finalModel, elastic$bestTune$lambda)

 

- Resample( ) 함수 이용: 각 모델별로 성능 지표를 표본추출하여 이들의 표준분포를 살펴보겠음

-> 이를 위해, 세 모델을 리스트 데이터 구조의 형태로 저장 (10개 씩의 표본 추출) 

elastic.pred <- predict(elastic, Boston.test)
postResample(pred=elastic.pred, obs=Boston.test$medv)

models <- list(ridge=ridge, lasso=lasso, elastic=elastic)
summary(resamples(models), metric="RMSE")

summary(diff(resamples(models), metric="RMSE"))

 

-> 릿지회귀모델, 라소회귀모델, 일래스틱넷회귀모델은 모두 RMSE 에 있어서 큰 차이가 없어서 비슷한 예측성능을 갖고 있는 것으로 보인다. 통계적으로 유의한 차이가 있는지는 다음과 같이 검정할 수 있다. 

 

-> 출력된 RMSE 행렬에서 대각선 위는 세 회귀모델 간 RMSE 의 차이를 나타내며, 대각선 아래는 RMSE 차이에 대한 p- 값이다.

세 회귀모델 간에는 예측성능에 있어서 통계적으로 유의한 차이가 없는 것으로 나타났다 (유의확률이 모두 1).

그러나 라소회귀모델과 일래스틱넷 회귀모델의 예측변수 개수가 릿지회귀모델보다 작아서 상대적으로 간명하므로 여기에서는 예측모델의 일반화 관점에서 이 두 회귀모델을 선택하는 것이 바람직할 수 있다. 

-