본문 바로가기

R-PAGO 노트

Entropy Based Discretization: 어떻게 연속변수를 구분 짓는가?

Discretization: 연속변수를 구분 짓는 방법

인사팀 직원이 찾아와 현재 회사의 기준인 Toeic Specking 7단계 점수를 EPC 사업수행능력을 가장 잘 반영할 수 있는 우리 회사만의 영어 등급으로 재구분할 수 있는지를 문의해 왔다. 질문을 듣는 순간 떠오른 것이 Discretization!!!, 변수를 어떻게 효율적으로 구분하는 가이다.

현재 진행 중인 “아파트 청약 경쟁률 예측”에서 지하철역과 아파트간의 거리를 구분하는 경우가 있었는데 이 글의 주제인 “엔트로피”를 사용해 구분하지 않고 “부동산 상식”에 근거해 구분하였다. 구분하는 기준이 아무리 옳더라도 범용성과 간편성이 떨어지면 의미가 없기 때문이다. (아파트와 지하철역 거리를 351m를 기준으로 역세권과 비역세권을 나눈다 생각해보면 쉽다.) 비록 상식에 기반한 구분법이 비록 주먹구구처럼 보일지라도 실제 주요 책에서도 Discretization시 이 구분법(전문가의 경험(Expert’s experience))을 가장 우선 추천시 하는 것도 같은 이유에서다. 실제 작동이 잘 된다는 것은 덤 이상이다. 물론 우리 예의 맹점은 나와 내 친구가 전문가(Expert)인가 여부에 있겠지만…

아파트 청약 경쟁률 예측 결과가 만족스럽지 않게 나왔다. 역시 나와 내 친구는 전문가가 아니었던 것인가? 앞서 언급한 지하철 역 거리의 최적점을 찾아야한다. 같은 방법으로 구분한 주요 브랜드 아파트 구분도 같은 맥락으로 개선해야한다. 그래 나도 안다. 불길한 예감은 틀리지 않듯이 해결하지 않고 넘어간 문제는 항상 발목을 잡는다는 것을. 이제 잊고 싶었던 엔트로피를 이용한 Discretization을 배울 시점이다. 다행히 Discretization과 관련해 너무나도 자세하고 명확한 글을 이 날을 위해 저장해 두었다.하하하. 이 명쾌한 글은 번역만으로도 엔트로피 기반 Discretization을 이해하는데 충분해 보인다.

엔트로피(Entropy)란?

아..이런 젠장. 시작과 함께 발목이 걸리다니. 엔트로피 기반의 Discretization은 반응변수가 명목형인 분류(classification)일 경우에만 적용이 가능하다. 만약 우리의 데이터와 같은 연속형 반응변수(회귀(regression))일 경우에는 명목형으로 먼저 변환 후에만 적용이 가능하다. 또 다른 테크닉이 필요하다. 모든 반응 변수 데이터를 표준화 후 일정 간격으로 구분하는 것이 그것이다. 고진감래, 전화위복이랄까? 이 방법은 또하나의 묻어둔 아이템인 Stratified Sampling에 대한 해법을 제공한다. 샘플링시 표준화된 값 기준의 구분을을 사용하는 것이다. 2개의 주요 문제가 이렇게 풀리는가 싶다.

자! 엔트로피를 먼저 살펴 보자. 엔트로피란 기대 정보값(Expected Information)이다. 문자가 의미하는 바와 같이 현재의 구분이 얼마나 잠재된 정보를 유추하게 끔 하는가?란 뜻이다. 엔트로피 1은 “어떤 정보도 얻을 수 없는 것”이고 0은 “명확히 정보를 알 수 있는 것” 이다. 단순히 말해 클 수록 나쁜 것, 작을 수록 좋은 것이다. 예를 들어 보자. .

나이 Income<=50,000 Income>$50,000
Age<25 4 6

상기 Table을 나이와 수입간의 관계(Information)을 유추하기 어렵다. 그렇다면 다음 예는 어떨까?

나이 Income<=50,000 Income>$50,000
Age<25 9 1

이번 예는 정보가 명확하다. 나이가 어릴 수록 수입이 5만불 이상인 사람은 극히 적다는 관계를 알 수 있기 때문이다. 두 예를 종합해 보면 엔트로피는 첫번째 예에서는 1에 가까워야하고 두번째 예에서는 0에 가까워야 한다. 이것을 식으로 표현하면 다음과 같다.

상기 예를 본 식으로 풀어보자 m은 반응변수의 구분 갯수이므로 이 경우는 2(5만이상 또는 이하)이다. 첫번째 예에서는 p1= 4/10, p2는 6/10이다. Entropy값은 다음과 같다.

D1=-(4/10*log2(4/10)+6/10*log2(6/10))
D1
## [1] 0.9709506

두번째 예는 p1= 9/10, p2는 1/10이다. Entropy값은 다음과 같다.

D2=-(9/10*log2(9/10)+1/10*log2(1/10))
D2
## [1] 0.4689956

만약 p1=10/10이라면 좌우변 모두 0이 된다. 즉, 의미하는 정보가 완벽하다는 뜻이된다.

Entropy Gain

Gain(Enew) = Einitial - Enew 상기 식과 같이 **Entropy Gain은 “데이터를 새롭게 구분시 기존 엔트로피대비 얼마나 엔트로피를 줄일 수 있는가**를 말한다. 목적은 Entropy Gain을 최대화일 때의 구분(Discretization)을 찾는 것이 된다. Discretization은 Entropy Gain이 더이상 증가하지 않거나 구분된 하나의 구간(bin)이 데이터 수가 특정 숫자 이하일 때 멈추게 된다.

상기 2개번째 예에서 하나의 구분을 추가하여 Entrop Gain을 구해 보자. 나이 |Income<=50,000 |Income>$50,000 ——–|—————|————–| Age<25 | 9 | 1 | Age>=25 | 4 | 6 |

전체 엔트로피를 구할 때는 각 구분시 사용되는 데이터의 숫자만큼 엔트로피에 가중치를 부여하면 된다. 이를 타나타낸 식은 다음과 같다.

Net.Entropy<-10/20*D1+10/20*D2
Information.Gain<-D2-Net.Entropy
Information.Gain
## [1] -0.2509775

새로 추가된 구분으로 인해 유추가 더 어려워 진 것이다. 그 밖에 사용 되는 용어로 Gain Ratio 와 Gini Index가 사용되는데 전자는 Information Gain을 기존의 엔트로피로 나눈 것(Information.Gain/D2)을 의미한다. Gini Index는 엔트로피 값과 상당히 유사하다. 이 값은 익숙하다. 바로 분류트리를 나누는 기준이기 때문이다.

변수 선택, 엔트로피의 또 다른 응용

엔트로피 방법을 이용하면 변수별로 반응변수에 영향을 주는 것을 정량화 할 수 있다. 분류 데이터인 Iris에서 반응변수에 가장 많이 영향을 주는 변수를 찾아보자.

library(CORElearn)
data(iris)
attrEval(Species~.,iris,estimator="InfGain")
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##    0.5572327    0.2831260    0.9182958    0.9182958

Petal이 Sepal보다 중요한 변수임을 알 수 있다.

다음을 분류트리로 확인해 보자. 한 node에 최소 5개의 데이터가 있게 한다.

library(rpart)
library(rpart.plot)
## Warning: package 'rpart.plot' was built under R version 3.3.3
ct<-rpart(Species ~.,iris,control=rpart.control(minsplit=5))
prp(ct,extra=101, box.col="orange", split.box.col="grey")

실제 트리 분류도 Petal에 의해서 이루어졌으며 Sepal은 영향을 주지 않았다. 다른 패키지를 사용해 보자.

library(tree)
## Warning: package 'tree' was built under R version 3.3.3
tr<-tree(Species~.,iris)
plot(tr)
text(tr,pretty=0)

tr
## node), split, n, deviance, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 150 329.600 setosa ( 0.33333 0.33333 0.33333 )  
##    2) Petal.Length < 2.45 50   0.000 setosa ( 1.00000 0.00000 0.00000 ) *
##    3) Petal.Length > 2.45 100 138.600 versicolor ( 0.00000 0.50000 0.50000 )  
##      6) Petal.Width < 1.75 54  33.320 versicolor ( 0.00000 0.90741 0.09259 )  
##       12) Petal.Length < 4.95 48   9.721 versicolor ( 0.00000 0.97917 0.02083 )  
##         24) Sepal.Length < 5.15 5   5.004 versicolor ( 0.00000 0.80000 0.20000 ) *
##         25) Sepal.Length > 5.15 43   0.000 versicolor ( 0.00000 1.00000 0.00000 ) *
##       13) Petal.Length > 4.95 6   7.638 virginica ( 0.00000 0.33333 0.66667 ) *
##      7) Petal.Width > 1.75 46   9.635 virginica ( 0.00000 0.02174 0.97826 )  
##       14) Petal.Length < 4.95 6   5.407 virginica ( 0.00000 0.16667 0.83333 ) *
##       15) Petal.Length > 4.95 40   0.000 virginica ( 0.00000 0.00000 1.00000 ) *

이 패키지가 더 추가적인 트리를 분할 하는 이유는 지니계수의 정의의 차이 때문으로 보인다. 위는 (1-p2) 아래는 (p-p2사용) 한 Node의 데이터수를 볼 때 두번째 Package가 더 신뢰가 있다.

회귀 반응변수의 Discretizaton

이 글의 주제인 엔트로피를 사용한 회귀데이터의 Discretization을 해보자. MASS Package의 Boston 집 값을 예로 들자.

library(MASS)
## Warning: package 'MASS' was built under R version 3.3.3
data<-Boston
set.seed(1)
train=sample(1:nrow(data),nrow(data)*0.7)
Boston.tree<-tree(medv~.,data,subset=train)
plot(Boston.tree)
text(Boston.tree,pretty=0)

pred<-predict(Boston.tree,newdata=data[-train,])
boston.test<-data[-train,"medv"]
sqrt(mean((pred-boston.test)^2))
## [1] 4.915494

반응변수가 평균4.9의 차이를 보인다.(RMSE) 반응 변수를 0과 1사이의 값으로 표준화하여 구간을 나눠보자. 데이터의 분포는 다음과 같다.

boxplot(data[,"medv"])

summary(data[,"medv"])
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    5.00   17.02   21.20   22.53   25.00   50.00
library(dplyr)
## Warning: package 'dplyr' was built under R version 3.3.3
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:MASS':
## 
##     select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
mxs<-apply(select(data,medv),2,max)
mns<-apply(select(data,medv),2,min)
medv.norm<-scale((data$medv), center=mns, scale=mxs-mns)
hist(medv.norm)

같은 간격으로 나눈다. 어느 기준이 맞을지 모르니 하나는 4개 하나는 5개로 구분한다.

medv.norm.dis4<-cut(medv.norm,4,labels=c("LL","L","M","H"))
medv.norm.dis5<-cut(medv.norm,5,labels=c("LL","L","M","H","HH"))
table(medv.norm.dis4)
## medv.norm.dis4
##  LL   L   M   H 
## 116 284  74  32
table(medv.norm.dis5)
## medv.norm.dis5
##  LL   L   M   H  HH 
##  77 239 123  36  31

(개인적으로 1.Discretization, 2.Stratified Sampling,3.ReliefF를 통한 회귀의 변수 선택은 우리만의 경쟁우위가 될 수 있는 아주 중요한 요소라 생각한다.)

추가 사항: 회귀에서의 변수 선택

엔트로피와 관계는 없지만 회귀 데이터의 경우 반응변수에 영향을 주는 변수를 찾아 보자. 이것은 앞서 언급한 ReliefF를 통해 결정된다.. ReliefF는 각각의 변수간 독립성을 전제하지 않기 때문에 상당히 중요한 기준인데 아직 스터디가 부족해 이해가 되지 않는다. 응용 예는 다음과 같다.

data(algae, package="DMwR2")
attrEval(a1~.,algae[,1:12],estimator="MSEofMean")
##    season      size     speed      mxPH      mnO2        Cl       NO3 
## -453.2142 -395.9696 -413.5873 -413.3519 -395.2823 -252.7300 -380.6412 
##       NH4      oPO4       PO4      Chla 
## -291.0525 -283.3738 -272.9903 -303.5737
attrEval(a1~.,algae[,1:12],estimator="RReliefFexpRank")
##       season         size        speed         mxPH         mnO2 
## -0.031203465 -0.028139035 -0.035271926  0.080825823 -0.072103230 
##           Cl          NO3          NH4         oPO4          PO4 
## -0.152077352 -0.011462467 -0.009879109 -0.134034483 -0.076488066 
##         Chla 
## -0.142442935

이 패키지에서 추가로 사용 가능한 용어는 다음과 같다.

infoCore(what="attrEvalReg")
## [1] "RReliefFequalK"      "RReliefFexpRank"     "RReliefFbestK"      
## [4] "RReliefFwithMSE"     "MSEofMean"           "MSEofModel"         
## [7] "MAEofModel"          "RReliefFdistance"    "RReliefFsqrDistance"