glm predict 시 발생하는 에러 사례

Statistics

“이진분류에 lasso regression을 이용하기 위해서 glmnet 패키지를 이용하여 모델을 적합한 후 predict할 경우에 발생할 수 있는 오류와 해결 방법을 제시한다.”

유충현
2021-10-17

프롤로그

분석가가 주관적으로 이진분류 모델을 적합하고, 적합한 모델로 예측(predict)할 경우에는 분석가의 의도가 실려, 데이터 분석 시에 문제가 발생하는 경우는 적다.

그러나, autoML의 경우에는 문제가 발생할 수도 있다. 물론 여러 사례에 대해서 미리 로직으로 문제를 회피하면 이런 이슈를 사전에 방지할 수 있다. alookr 패키지를 개발하면서 lasso 모델을 이용한 이진분류를 개발하고, 몇 가지 사례로 검증하면서 놓쳤던 에러 발생 사례를 최근에 발견하고 이를 해결하는 방법을 제시한다.

alookr과 이진분류 모델

alookr은 이진분류 모델의 auto-ML을 위해서 만들어진 패키지다. 아직은 개발해야 할 기능이 많지만, 느리게 목표를 향해 나가고 있다. logistic regression부터 시각해서 하나하나 모델을 추가해 나가고 있는 중, 몇 개월 전에 추가한 lasso regression 사례에서 오류가 발생한 것이다. 물론 특정 사례에서 발생하였다.

오류 사례의 재현

데이터 분할

원시 데이터에서 결측치를 보정하고 training set과 test set을 분할한다.

library(dlookr)
library(alookr)
library(dplyr)
library(mlbench)

data(BreastCancer)

# 결측치를 포함한 변수
diagnose(BreastCancer) %>%
  filter(missing_count > 0)
# A tibble: 1 x 6
  variables   types  missing_count missing_percent unique_count
  <chr>       <chr>          <int>           <dbl>        <int>
1 Bare.nuclei factor            16            2.29           11
# … with 1 more variable: unique_rate <dbl>
# 결측치의 대체 수행
breastCancer <- BreastCancer %>%
  mutate(Bare.nuclei = imputate_na(BreastCancer, Bare.nuclei, Class,
                         method = "mice", no_attrs = TRUE, print_flag = FALSE))

# 기본 인수로 training set과 test set로 분할
sb <- breastCancer %>%
  split_by(target = Class)

불균형 클래스 핸들링

# training set 돗수분포표 - 불균형 클래스 데이터
table(sb$Class)

   benign malignant 
      458       241 
# training set 상대돗수분포표 - 불균형 클래스 데이터
prop.table(table(sb$Class))

   benign malignant 
0.6552217 0.3447783 
# 집계 - 불균형 클래스 데이터
summary(sb)
** Split train/test set information **
 + random seed        :  29556 
 + split data            
    - train set count :  489 
    - test set count  :  210 
 + target variable    :  Class 
    - minority class  :  malignant (0.344778)
    - majority class  :  benign (0.655222)
# SMOTE 샘플링
train_smote <- sb %>%
  sampling_target(seed = 1234L, method = "ubSMOTE")

# 돗수분포표
table(train_smote$Class)

   benign malignant 
      692       519 

데이터 정제

# training set의 정제
train <- train_smote %>%
  cleanse
── Checking unique value ─────────────────────────── unique value is one ──
No variables that unique value is one.

── Checking unique rate ─────────────────────────────── high unique rate ──
• Id = 416(0.343517753922378)

── Checking character variables ─────────────────────── categorical data ──
No character variables.

모델 평가를 위한 test set 추출

# test set 추출
test <- sb %>%
  extract_set(set = "test")

이진분류 모델 적합

lasso 모델링을 위해서 alookr 패키지가 아닌, R 스크립트로 모델을 적합한다.

library(glmnet)
library(dplyr)

train_X <- train %>% 
  select(-Class) %>% 
  data.matrix 
  
label <- train %>% 
  transmute(Class = ifelse(Class == "malignant", 1, 0)) %>% 
  pull 
  
model_lasso <- glmnet::glmnet(x = train_X, y = label, family = "binomial")

모델의 평가

모델의 평가를 위해서 앞에서 만들어 놓은 test 데이터셋을 이용한다. 그런데, 에러가 발생하였다.

물론 alookr에서도 다음과 유사한 스크립트로 예측 로직을 구현하였다.

pred <- predict(model_lasso, 
                test %>% 
                  select(-Class) %>% 
                  data.matrix)

Error in h(simpleError(msg, call)) : error in evaluating the argument 'x' in selecting 
a method for function 'as.matrix': Cholmod error 'X and/or Y have wrong dimensions' 
at file ../MatrixOps/cholmod_sdmult.c, line 90

에러의 문제 탐색

“wrong dimensions”이라는 에러 메시지에 착안하여, train셋과 test 셋의 차원을 비교해 본다. 변수의 개수가 다르다.

dim(train_X)
[1] 1211    9
dim(test %>% 
      select(-Class))
[1] 210  10

변수 이름을 살펴보니, test 셋에서는 train 셋에 없는 “Id” 변수가 포함되어 있다. 앞에서 cleanse() 함수로 train 셋을 정제할 때 식별자인 “Id”가 제거되었던 것이다.

colnames(train_X)
[1] "Cl.thickness"    "Cell.size"       "Cell.shape"     
[4] "Marg.adhesion"   "Epith.c.size"    "Bare.nuclei"    
[7] "Bl.cromatin"     "Normal.nucleoli" "Mitoses"        
names(test %>% 
      select(-Class))
 [1] "Id"              "Cl.thickness"    "Cell.size"      
 [4] "Cell.shape"      "Marg.adhesion"   "Epith.c.size"   
 [7] "Bare.nuclei"     "Bl.cromatin"     "Normal.nucleoli"
[10] "Mitoses"        

에러의 문제 해결

alookr에서는 lasso외에 6개의 이진분류 모델이 있으며, 값을 예측할 때 에러가 발생하지 않았다. 아마도 함수 내부에서 필요한 변수를 취했을 것이다. 그러나 glmnet 패키지는 사용자가 기술한 데이터셋의 변수를 모두 사용하는 것 같다. 그래서 train 셋에 있는 변수만 취하는 로직으로 수정해서 문제를 해결하였다.

pred <- predict(model_lasso, 
                test %>% 
                  select(matches(model_lasso$beta %>% row.names())) %>% 
                  data.matrix)

cleanse() 함수로 train 셋을 정제하는 것처럼 test 셋도 정제하면 문제가 없었을 것이다. 그러나 auto-ML을 위해서는 사용자의 주관적인 개입이 적게 때문에 이 부분의 로직은 포함되어 있지 않았다.

아무튼 glmnet 패키지는 예측할 때(predict 함수를 호출할 때) train 셋과 test 셋의 변수 개수가 같아야 한다는 것을 잊지 말자.

test <- sb %>%
  extract_set(set = "test") %>% 
  cleanse
── Checking unique value ─────────────────────────── unique value is one ──
No variables that unique value is one.

── Checking unique rate ─────────────────────────────── high unique rate ──
• Id = 205(0.976190476190476)

── Checking character variables ─────────────────────── categorical data ──
No character variables.

Citation

For attribution, please cite this work as

유충현 (2021, Oct. 17). Dataholic: glm predict 시 발생하는 에러 사례. Retrieved from https://choonghyunryu.github.io/posts/2021-10-17-glmnet/

BibTeX citation

@misc{유충현2021glm,
  author = {유충현, },
  title = {Dataholic: glm predict 시 발생하는 에러 사례},
  url = {https://choonghyunryu.github.io/posts/2021-10-17-glmnet/},
  year = {2021}
}