페널티 로지스틱 회귀분석(Penalized Logistic Regression Analysis)

순동·2022년 3월 5일
0

📌 페널티 로지스틱 회귀분석(Penalized Logistic Regression Analysis)

  • 지나치게 많은 예측변수를 갖는 로지스틱 회귀모델에 페널티를 부과한다.
  • 모델의 설명력에 덜 기여하는 예측변수의 회귀계수를 0으로 만들거나(Lasso) 0에 가깝게 축소하여(Rigde) 모델의 복잡성을 줄인다.
  • 예측 정확도가 비슷할 경우 예측변수 개수가 작은(과적합 위험이 작은) 간명한 모델이 바람직히다.

📌 Lasso 회귀분석을 이용한 페널티 로지스틱 회귀분석

  • Lasso 회귀분석은 기여도가 낮은 예측변수의 회귀계수를 0으로 만들어 예측변수에서 제거한다.

환자의 당뇨병 여부와 관련된 임상 데이터

  • diabetes (결과변수) : 당뇨병 여부
> library(mlbench)
> data("PimaIndiansDiabetes2")
> str(PimaIndiansDiabetes2)
'data.frame':	768 obs. of  9 variables:
 $ pregnant: num  6 1 8 1 0 5 3 10 2 8 ...
 $ glucose : num  148 85 183 89 137 116 78 115 197 125 ...
 $ pressure: num  72 66 64 66 40 74 50 NA 70 96 ...
 $ triceps : num  35 29 NA 23 35 NA 32 NA 45 NA ...
 $ insulin : num  NA NA NA 94 168 NA 88 NA 543 NA ...
 $ mass    : num  33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 NA ...
 $ pedigree: num  0.627 0.351 0.672 0.167 2.288 ...
 $ age     : num  50 31 32 21 33 30 26 29 53 54 ...
 $ diabetes: Factor w/ 2 levels "neg","pos": 2 1 2 1 2 1 2 1 2 2 ...
  1. 데이터 전처리
    결측값이 포함되어 있으므로 결측값을 제외시킨다.
> PimaIndiansDiabetes3 <- na.omit(PimaIndiansDiabetes2)
  1. 훈련 데이터와 테스트 데이터로 7:3 분할
> library(caret)
> set.seed(123)

> PimaIndiansDiabetes3 <- na.omit(PimaIndiansDiabetes2)
> train <- createDataPartition(y=PimaIndiansDiabetes3$diabetes,
+                             p=0.7, list=FALSE)

> # 훈련 데이터
> diabetes.train <- PimaIndiansDiabetes3[train, ]

> # 테스트 데이터
> diabetes.test <- PimaIndiansDiabetes3[-train, ]

훈련 데이터와 테스트 데이터가 적절히 분할되었는지 확인하기 위해 당뇨병 환자의 비율 계산한다.

> table(diabetes.train$diabetes)  # 당뇨병 환자 91명
neg pos 
184  91 
> prop.table(table(diabetes.train$diabetes))  # 당뇨병 환자 33%
      neg       pos 
0.6690909 0.3309091

> table(diabetes.test$diabetes)  # 당뇨병 환자 39명 
neg pos 
 78  39 
> prop.table(table(diabetes.test$diabetes))  # 33%
      neg       pos 
0.6666667 0.3333333

훈련 데이터와 테스트 데이터가 적절히 분할되었다.

  1. 교차검증 ⭐
    예측오차 최소화하는 최적의 lambda를 찾는다.
  • cv.glmnet은 포뮬러 형식을 지원하지 않는다.
  • 예측변수 : 행렬 형식
  • 결과변수 : 벡터 형식
  • 예측변수는 숫자만 취할 수 있으므로 범주형 변수는 더미변수로 변환이 필요하다.(cv.glmnet 사용 시 자동 변환 됨)
> library(glmnet)
> set.seed(123)

> x <- model.matrix(diabetes ~ ., data=diabetes.train)[, -1]
> head(x)
   pregnant glucose pressure triceps insulin mass pedigree age
7         3      78       50      32      88 31.0    0.248  26
9         2     197       70      45     543 30.5    0.158  53
14        1     189       60      23     846 30.1    0.398  59
17        0     118       84      47     230 45.8    0.551  31
20        1     115       70      30      96 34.6    0.529  32
21        3     126       88      41     235 39.3    0.704  2

> y <- ifelse(diabetes.train$diabetes=='pos', 1, 0)
> head(y)
[1] 1 1 1 1 1 0
> diabetes.cv <- cv.glmnet(x=x, y=y, family='binomial', alpha=1)

> diabetes.cv$lambda.min  
[1] 0.01087884
> diabetes.cv$lambda.1se  
[1] 0.05805712
  • 일정 수준 예측정확도를 보장하면서 가능한 간명한 모델이 목표이다.
  • lambda.1se에 또 다른 lambda를 제공한다.
  • 최소 예측 오차의 한계 표준편차 범위 내의 정확도를 보이는 모델 중 가장 간명한 모델을 생성한다.(과적합 위험 낮음)
  1. lambda.min vs lambda.1se 회귀계수
> coef(diabetes.cv, diabetes.cv$lambda.min)  # 1개의 예측변수 제거
9 x 1 sparse Matrix of class "dgCMatrix"
                     s1
(Intercept) -9.23133474
pregnant     0.01006916
glucose      0.03446189
pressure     .         
triceps      0.02678380
insulin      0.00147175
mass         0.04309304
pedigree     1.14386810
age          0.03039017

> coef(diabetes.cv, diabetes.cv$lambda.1se)  # 2개의 예측변수 제거
9 x 1 sparse Matrix of class "dgCMatrix"
                       s1
(Intercept) -5.6548823980
pregnant     .           
glucose      0.0276723502
pressure     .           
triceps      0.0137868665
insulin      0.0001604243
mass         0.0120567459
pedigree     0.3234881001
age          0.0137023589
  1. 혼동행렬
    lambda.mse를 사용한 모델을 먼저 평가한다.
> # 예측 정확도 평가
> diabetes.gnet1 <- glmnet(x=x, y=y, family='binomial',
+                          alpha=1, lambda=diabetes.cv$lambda.min)

> # 예측변수 행렬
> diabetes.test.x <- model.matrix(diabetes ~ ., data=diabetes.test)[, -1]

> # 예측 확률
> diabetes.pred1 <- predict(diabetes.gnet1,
+                           newx=diabetes.test.x,
+                           type='response')

> # 예측 확률 0.5를 기준으로 당뇨병 여부 판정
> diabetes.pred1 <- ifelse(diabetes.pred1 > 0.5,
+                          'pos', 'neg')

> # 혼동행렬
> table(diabetes.test$diabetes, diabetes.pred1,
+       dnn=c('Actual', 'Predicted'))
      Predicted
Actual neg pos
   neg  69   9
   pos  20  19

대각선 : 실제 판정 결과와 예측 모델의 예측 결과가 일치하는 개수

  1. 예측 정확도
> mean(diabetes.test$diabetes==diabetes.pred1)  # 75.2%
[1] 0.7521368

간명도를 높인 모델 lambda.1se를 평가한다.

> # 예측 정확도 평가
> diabetes.gnet2 <- glmnet(x=x, y=y, family='binomial',
+                          alpha=1, lambda=diabetes.cv$lambda.1se)

> # 예측변수 행렬
> diabetes.test.x <- model.matrix(diabetes ~ ., data=diabetes.test)[, -1]

> # 예측 확률
> diabetes.pred2 <- predict(diabetes.gnet2,
+                           newx=diabetes.test.x,
+                           type='response')

> # 예측 확률 0.5를 기준으로 당뇨병 여부 판정
> diabetes.pred2 <- ifelse(diabetes.pred2 > 0.5,
+                          'pos', 'neg')
# 혼동행렬
> table(diabetes.test$diabetes, diabetes.pred2,
+       dnn=c('Actual', 'Predicted'))
      Predicted
Actual neg pos
   neg  71   7
   pos  22  17
   
> # 예측 정확도
> mean(diabetes.test$diabetes==diabetes.pred2)  # 75.2%
[1] 0.7521368

lambda.mse와 lambda.1se와 같은 예측 정확도가 나타났다.


📌 이항 로지스틱 회귀분석

일반적인 이항 로지스틱 회귀분석을 수행해 페널티 회귀분석과의 비교해보자.

  1. 모델 생성
> diabetes.logit <- glm(diabetes ~ ., data=diabetes.train,
+                       family=binomial(link='logit'))
  1. 예측 확률
> diabetes.logit.pred <- predict(diabetes.logit,
+                                newdata=diabetes.test,
+                                type='response')

> # 예측 확률 0.5를 기준으로 당뇨병 여부 판정
> diabetes.logit.pred <- ifelse(diabetes.logit.pred > 0.5,
+                               'pos', 'neg')
  1. 혼동행렬
> table(diabetes.test$diabetes, diabetes.logit.pred,
+       dnn=c('Actual', 'Predicted'))
      Predicted
Actual neg pos
   neg  66  12
   pos  20  19
  1. 예측 정확도
> mean(diabetes.test$diabetes==diabetes.logit.pred)  # 72.6%
[1] 0.7264957

📝 결론

  • 일반적인 로지스틱 회귀모델보다 페널티 회귀모델이 더 우수하다.
  • 페널티 회귀분석에서 lambda.min을 사용한 것과 lambda.1se를 사용한 예측 정확도가 같게 나타났다.
  • 따라서 예측변수의 개수를 고려하여 lambda.1se를 사용한 페널티 로지스틱 회귀모델이 가장 우수하다.

0개의 댓글