ch0nny_log

[빅데이터분석] R _ 57. 의사 결정 트리 본문

빅데이터 분석(with 아이티윌)/R

[빅데이터분석] R _ 57. 의사 결정 트리

chonny 2024. 7. 16. 09:39
★ 점심시간 문제:독버섯을 분류하는데 있어 가장 중요한 컬럼이 무엇인가? 정보획득량이 가장 높은게 무엇인가?
mush <- read.csv("c:\\data\\mushrooms.csv", stringsAsFactors=T)

install.packages("FSelector")
library(FSelector)         # for 정보획득량 
library(doBy)              # for 정렬

mush<- information.gain( type ~  . , mush,  unit='log2')
orderBy (~attr_importance, mush)

= odor 가 제일 정보획득량이 높음


■ 의사결정트리란?

의사결정트리는 데이터의 속성을 기준으로 분할하여 트리 형태로 모델링하는 분류 예측 모델입니다.

이는 주어진 데이터에서 특정 속성을 기준으로 데이터를 분할하고,
그 분할된 데이터에 대해 반복적으로 동일한 과정을 적용하여 최종적으로 예측을 수행합니다.

의사결정트리는 각 노드에서 데이터를 분할하는 기준을 설정하고, 그 기준에 따라 데이터를 분류합니다.
이를 통해 데이터의 패턴을 발견하고, 새로운 데이터를 분류하거나 예측할 수 있는 모델을 만듭니다.


회귀분석과 의사결정트리는 현업에서 선호하는 머신러닝 알고리즘입니다.
신경망이 정확도는 뛰어나지만, 의사결정트리를 더 선호하는 경우가 있습니다. 그 이유는 ?

 

 

 

※ 불순도를 줄이는 질문을 계속 던지면서 순도가 좋아지게 분류를 해나가는 것

- 순도가 높다는 것 -> 같은 데이터들이 대부분이라는 것 

- 불순도가 높다는 것 -> 여러 종류의 데이터들이 섞여 있다는 것

 


 

■ 지니계수란?

 

 

 

지니 계수(Gini Index)를 결정 트리에 사용하는 이유는 다음과 같습니다:

  1. 분류 정확도 향상: 지니 계수는 노드의 불순도를 측정하는 데 사용됩니다. 불순도가 낮을수록 같은 클래스의 샘플들이 많이 포함되어 있다는 의미입니다. 지니 계수를 최소화함으로써 분류의 정확도를 높일 수 있습니다.
  2. 계산의 효율성: 지니 계수는 계산이 비교적 간단하며, 빠르게 수행될 수 있습니다. 이는 대규모 데이터셋을 처리할 때 중요한 장점입니다.
  3. 분할 기준 제공: 지니 계수는 각 노드를 어떻게 분할할지 결정하는 기준이 됩니다. 즉, 특정 속성의 특정 값을 기준으로 데이터를 분할할 때 지니 계수를 사용하여 불순도를 최소화하는 방향으로 분할합니다.

 

 

1) a  = 2/5 = 0.4 / (1-a) = 3/5 = 0.6 

2) 1-  (0.4^2 +0.6^2)  = 0.48   <- JINI 계수 

3) 0보다 높으므로 불순도가 높음 

■ 정보획득량?

정보 이득(Information Gain)의 공식이 소개됩니다. 정보 이득은 부모 노드의 지니 계수에서 자식 노드의 지니 계수를 뺀 값으로 계산됩니다.

 

 

 


■ 불순도 평가방법 

의사결정트리 모델의 불순도 평가 방법은 다음과 같습니다:

  1. 엔트로피
  2. 지니계수
  3. 카이제곱 검정

의사결정트리 패키지로는 C50과 party가 있습니다. C50은 엔트로피 지수로 정보획득량을 구하며, party는 카이제곱 검정으로 정보획득량을 구합니다.

 

문제 1. (컴퓨터로 정보 획득량 구하기)  구매여부에 가장 영향이 큰 변수는 무엇인가? (즉 정보획득량이 가장 높은 컬럼이 무엇인가 ?)
buy <- data.frame(
    cust_name=c('SCOTT','SMITH','ALLEN','JONES','WARD'),
    card_yn=c('Y','Y','N','Y','Y'),
    intro_yn=c('Y','Y','N','N','Y'),
    before_buy_yn=c('Y','Y','Y','N','Y'),
    buy_yn=c('Y','Y','N','Y','Y') )​


컬럼소개:  card_yn : 카드 소유유무
              intro_yn : 지인소개 
              before_buy_yn :  이전구매여부
              buy_yn  : 구매여부 ( 종속변수) 

library(FSelector)  # 정보 획득량 구하기 
library(doBy)   # 자동 정렬 처리

information.gain( buy_yn ~  . , buy,  unit='log2')


->  카드 소유 유무가 가장 영향을 미침
-> cust_name 은 이름이기 때문에 상관 x

information.gain( buy_yn ~  . , buy[  ,c(2:5)],  unit='log2')
문제 2. 위 결과를 시각화 하시오.
# 데이터 불러오기
buy <- data.frame(
  cust_name=c('SCOTT','SMITH','ALLEN','JONES','WARD'),
  card_yn=c('Y','Y','N','Y','Y'),
  intro_yn=c('Y','Y','N','N','Y'),
  before_buy_yn=c('Y','Y','Y','N','Y'),
  buy_yn=c('Y','Y','N','Y','Y') )

buy



library(FSelector)  # 정보 획득량 구하기 
library(doBy)   # 자동 정렬 처리
library(plotly)
weights <- information.gain( buy_yn ~ . ,  buy[ , c(2:5)],   unit='log2')  
weights
orderBy( ~ attr_importance,  weights )


# 1. 데이터 불러오기
a <- orderBy( ~ attr_importance,  weights )  
info_gain_df <- data.frame(  col1=rownames(a), col2=a$attr_importance)
info_gain_df


# 색깔 지정하기
colors <- c('#FFCDD2','#E1BEE7','#BBDEFB')

# 4. plotly 를 사용한 막대 그래프 생성하기
fig <- plot_ly( info_gain_df,
                x = ~col1,
                y = ~ col2,
                type = 'bar',
                marker = list(color = colors))


# 5. 그래프 출력
fig​



■ R을 이용해서 의사결정 트리모델 만들기

실습 2
데이터: 화장품 고객 데이터 (skin.csv)
1) 순서:
#1. 데이터 불러오기
#2. 데이터 살펴보기
#3. 훈련 데이터와 테스트 데이터 분리하기
#4. 의사결정트리 모델 만들기
#5. 테스트 데이터 예측하기
#6. 모델 성능 확인하기
#7. 모델 성능 평가하기

2) 코드구현:

summary(model) 결과

 

설명: 결혼유무를 가장 중요한 결정 요인으로 골랐습니다.  그리고 나이와 직업 순서로 정보획득량이 높아서 위와 같이 질문 나무를 생성함

#1.  데이터 불러오기
setwd("c:\\data")
skin <- read.csv("skin.csv" , stringsAsFactors=T,  fileEncoding = "euc-kr")
head(skin)

#2.  데이터 살펴보기
colSums(is.na(skin))

#3.  훈련 데이터와 테스트 데이터 분리하기
library(caret)
set.seed(1)
train_num <- createDataPartition( skin$cupon_react, p=0.8, list=F)
length(train_num) # 25
train_data <- skin[ train_num,    ]
test_data  <- skin[  -train_num,  ]
nrow(train_data)   # 25
nrow(test_data)    # 5

#4.  의사결정트리 모델 만들기
install.packages("C50")
library(C50)   # 질문 나무 만드는 패키지

model <- C5.0( train_data[   ,  c(-1, -7) ], train_data[   , 7] ) 
## 고객번호와 정답을 제외    정답
model

## Classification Tree
## Number of samples: 25    ->
## Number of predictors: 5  -> 가지를 5개 만들었다.

summary(model)
plot (model)


#5.  테스트 데이터 예측하기
# 1) 훈련 데이터의 정확도
train_result <- predict( model,  train_data[   , c(-1, -7) ] )
train_result

sum(train_result ==train_data[ ,7])/25*100 #정확도 88%

# 2) 테스트 데이터의 정확도
test_result <- predict( model,  test_data[   , c(-1, -7) ] )
test_result

sum(test_result ==test_data[ ,7])/5*100 #정확도 80%

#6.  모델 성능 확인하기
library(C50)
model2 <- C5.0( train_data[   ,  c(-1, -7) ], train_data[   , 7] ,trials = 5 )
model2
## trials = 5 의 의미 : 의사결정트리 모델 5개를 생성해서 5개의 모델이 
## 예측한 결과를 가지고 다수결을 붙여서 다수결에 의해서 예측하겠다는 뜻 


#7.  모델 성능 평가하기
test_result2 <- predict( model2,  test_data[   , c(-1, -7) ] )
test_result2

sum(test_result2 ==test_data[ ,7])/5*100 #정확도 60%​

설명: 훈련 데이터의 정확도는 100% 인데 트스트가 60% 입니다. 오버피팅이 발생했습니다. 오버피팅이 적게 일어나면서 정확도는 좋은 적절한 trials 값을 알아내야합니다.
실습 2. 아이리스 꽃의 품종을 분류하는 의사결정트리 모델을 생성하시오.
데이터: 아이리스 데이터
#1.  데이터 불러오기
setwd("c:\\data")
iris <- read.csv("iris2.csv" , stringsAsFactors=T,  fileEncoding = "euc-kr")
head(iris)

#2.  데이터 살펴보기
colSums(is.na(iris)) # 결측치 0개 

#3.  훈련 데이터와 테스트 데이터 분리하기
library(caret)
set.seed(1)
train_num <- createDataPartition( iris$Species, p=0.8, list=F)
length(train_num) # 120
train_data <- iris[ train_num,    ]
test_data  <- iris[  -train_num,  ]
nrow(train_data)   # 120
nrow(test_data)    # 30

#4.  의사결정트리 모델 만들기
install.packages("C50")
library(C50)   # 질문 나무 만드는 패키지

model <- C5.0( train_data[   ,-5], train_data[   , 5] ) 
##  정답을 제외 정답
model #가지를 3개 만들음음


summary(model)
plot (model)


#5.  테스트 데이터 예측하기
# 1) 훈련 데이터의 정확도
train_result <- predict( model,  train_data[   ,-5 ] )
train_result

sum(train_result ==train_data[ ,5])/120 *100 #정확도 95.83%

# 2) 테스트 데이터의 정확도
test_result <- predict( model,  test_data[   , -5 ] )
test_result

sum(test_result ==test_data[ ,5])/30*100 #정확도 96.66%

#6.  모델 성능 확인하기
y <- 0
jumpby <-1

options(scipen=999)  
for  ( i  in  1:10 ) {
  model<-C5.0(train_data[ ,-5],train_data[ ,5], trials=y) 
  test_result2 <- predict(model, test_data[ ,-5])
  a<- sum(test_result2 == test_data[ ,5])/30 *100 #100%
  y <- y + jumpby
  print(paste(i,'일때',a))
  
}

library(C50)
model2 <- C5.0( train_data[   ,  -5], train_data[   , 5] ,trials = 5 )
model2
## trials = 5 의 의미 : 의사결정트리 모델 5개를 생성해서 5개의 모델이 
## 예측한 결과를 가지고 다수결을 붙여서 다수결에 의해서 예측하겠다는 뜻 




#7.  모델 성능 평가하기
test_result2 <- predict( model2,  test_data[   , -5 ] )
test_result2

sum(test_result2 ==test_data[ ,5])/30*100 #정확도 96.66%


train_result <- predict( model2,  train_data[   ,-5 ] )
train_result

sum(train_result ==train_data[ ,5])/120 *100 #정확도 100%
실습3. 은행 대출 불이행자 예측 모델
#1. 데이터를 불러옵니다.

credit <- read.csv("credit.csv", stringsAsFactors=T)
head(credit)

#2. 데이터를 살펴봅니다.
str(credit)

# 데이터 설명
checking_balance : 예금계좌
saving_balance : 적금계좌
amount :  대출금액
default :  채무불이행 여부(종속변수)

# amount을 히스토그램 그래프로 그려보시오!

hist(credit$amount)
summary(credit$amount)
Min.     1st Qu.  Median    Mean 3rd Qu.    Max. 
    250    1366    2320    3271    3972   18424 

최소250마르크(천칠백 오십만원) ~ 18,424(12억 8천만원)

정답컬럼:   default ---> yes : 대출금 상환안함
                                 no : 대출금 상환함

prop.table( table(credit$default) )

30% 에 해당하는 사람들이 대출금을 상환하지 않고있어서
머신러닝 모델을 만들어서 대출금을 상환하지 않을것으로 예측되는 고객은
아예 대출을 안해주게 해서 대출금 상환안하는 고객의 비율을 최대한 낮춰보는것이
머신러닝 모델을 만드는 목적입니다.

# 결측치 확인
colSums( is.na(credit))

#3. 훈련 데이터와 테스트 데이터 분리
library(caret)
set.seed(1)

train_num <- createDataPartition( credit$default, p=0.9, list=F)

train_data <-  credit[  train_num,    ]
test_data <- credit[ -train_num,    ]

nrow(train_data)   #900
nrow(test_data)    #100

ncol(train_data)  # 17
head(train_data)   # 종속변수가 17번째 컬럼입니다.

#4. 모델 생성 
library(C50)

credit_model <- C5.0( train_data[   , -17],  train_data[   , 17] )

#5. 모델 확인
summary(credit_model)
Decision tree:

checking_balance = unknown: no (356/42) # 예금통장이 unknown 인 사람들 356이
                                                        # 돈을 갚았고 42명은 예외입니다.
checking_balance in {< 0 DM,> 200 DM,1 - 200 DM}:
:...amount > 8648: yes (31/6)  대출금액이 8648 마르크보다 더 큰 사람들중 31명이
    amount <= 8648:            대출금을 상환하지 않았습니다.  대출금액이 8448 이하
    :...credit_history in {perfect,very good}: 인 사람들중에 신용이력이 perfect,very good
        :...housing in {other,rent}: yes (26/3)  인 사람들중에서 집이 랜트이거나 기타유형
                                                      이면 돈 안갚았습다. 
        :   housing = own:           집이 자가 소유인데 
        :   :...savings_balance in {> 1000 DM,500 - 1000 DM,  적금이 있으면 
        :       :                   unknown}: no (8/2)  8명이 대출금 갚았습니다.(2명 예외)


#6. 모델 예측
train_result <- predict(  credit_model,  train_data[  , -17] )  
test_result <-  predict(  credit_model, test_data[   , -17] )

#7. 모델 평가

#훈련 정확도
sum( train_result == train_data[  , 17] ) / 900 * 100  # 83.33 %

#테스트 정확도
sum( test_result == test_data[  , 17] ) / 100 * 100   # 67%

#8. 모델 개선

문제172.  trials 파라미터를 주고 모델의 성능을 올리시오 !  

y <- 1
jumpby <-1

options(scipen=999)  
for  ( i  in  1:20 ) {
  credit_model2 <- C5.0( train_data[   , -17],  train_data[   , 17], trials=y )
  test_result2 <-  predict(  credit_model2, test_data[   , -17] )
  a<- sum(test_result2 == test_data[ ,17])/100 *100 #100%
  y <- y + jumpby
  print(paste(i,'일때',a))
  
}

 [1] "6 일때 78" 이 나왔습니다.  정확도만 봐서는 안되고 FN (거짓부정) 을 같이 
 봐야합니다. 

 library(gmodels)    # 의사결정 나무의 갯수는 1 ~100까지만 지정할 수 있습니다.

 credit_model2 <- C5.0( train_data[   , -17],  train_data[   , 17], trials=6 )
 test_result2 <-  predict(  credit_model2, test_data[   , -17] )

 x <-  CrossTable( test_data[   , 17],  test_result2 )

 x$t
     y
x     no yes
  no  65   5
  yes 17  13​
★ 마지막 문제: 와인데이터를 이용하여 의사결정 트리모델을 올리고 정확도를 올리시오.

#1. 데이터를 불러옵니다.

wine <- read.csv("wine2.csv", stringsAsFactors=T)
head(wine)

#2. 데이터를 살펴봅니다.
str(wine)


#3. 결측치 확인
colSums(is.na(wine)) # 결측치 없음음

#3. 훈련 데이터와 테스트 데이터 분리
library(caret)
set.seed(1)

train_num <- createDataPartition( wine$Type, p=0.8, list=F)

train_data <-  wine[  train_num,    ]
test_data <- wine[ -train_num,    ]

nrow(train_data)   #143
nrow(test_data)    #34

#4. 모델 생성 
library(C50)

model <- C5.0( train_data[   , -1],  train_data[   , 1] )

#5. 모델 확인
summary(model)

#6. 모델 예측
train_result <- predict(  model,  train_data[  , -1] )  
test_result <-  predict(  model, test_data[   , -1] )

#7. 모델 평가

#훈련 정확도
sum( train_result == train_data[  , 1] ) / 143 * 100  # 99.3%

#테스트 정확도
sum( test_result == test_data[  , 1] ) / 34 * 100   # 94.12% 

#8. 모델 개선

  y <- 1
jumpby <-1

options(scipen=999)  
for  ( i  in  1:20 ) {
  model2 <- C5.0( train_data[   , -1],  train_data[   , 1], trials=y )
  test_result2 <-  predict(  model2, test_data[   , -1] )
  a<- sum(test_result2 == test_data[ ,1])/34 *100 #100%
  y <- y + jumpby
  print(paste(i,'일때',a))
  
}

[1] "4 일때 100" 으로 가장 높음​