본문 바로가기

Study/class note

머신러닝 / R로 의사결정트리 모델 구현하기

# python
import pandas as pd

skin = pd.read_csv("c:\\data\\skin.csv")

x = skin['cupon_react']

ct = pd.crosstab(x, skin['marry'], margins = True)

before = []
for i in ct.columns[:-1]:
    before.append(ct.loc[i,ct.columns[-1]]/ct.loc[ct.columns[-1],ct.columns[-1]])


after = []
for i in ct.columns[:-1]:
    for k in ct.columns[:-1]:
        after.append(ct.loc[k,i]/ct.loc[ct.columns[-1],i])
        
before
after

문제266. 화장품 고객 데이터에서 결혼 여부에 대한 분할 전 엔트로피를 구하시오.

import numpy as np  # log는 math모듈에도 있음

before  # [0.5666666666666667, 0.43333333333333335] 'No','YES'

bf_cnt = 0
for i in before:
    bf_cnt += (- i* np.log2(i))
    
bf_cnt  # 0.9871377743721863

 문제267. 화장품 고객 데이터에서 결혼 여부에 대한 분할 후 엔트로피를 구하시오.

after # [0.9, 0.1, 0.4, 0.6]

af_cnt = []
a = ct.loc['All','NO']/ct.loc['All','All']
b = ct.loc['All','YES']/ct.loc['All','All']

for i in after:
    af_cnt.append(- i*np.log2(i))
    
af_entropy = a * (af_cnt[0]+af_cnt[1]) + b * (af_cnt[2]+af_cnt[3])
af_entropy  # 0.8036322608328728

 

정보획득량 = 분할 전 엔트로피 - 분할 후 엔트로피

info_gain = bf_entropy - af_entropy
info_gain  # 0.18350551353931355

R의 FSelector 패키지 infomation.gain() 메소드로 구한 정보획득량 결과와 같은지 확인해보자.

> 출력결과

        attr_importance
cust_no      0.00000000
gender       0.06798089
age          0.00000000
job          0.03600662
marry        0.18350551
car          0.02487770

> 결혼유무에 대해 파이썬에서 구한 정보획득량과 R에서 구한 정보획득량이 같음을 확인할 수 있음.

 

+) 정보획득량 구하는 전체코드

# 정보획득량 전체코드
import pandas as pd
import numpy as np

skin = pd.read_csv("c:\\data\\skin.csv")
x = skin['cupon_react']
ct = pd.crosstab(x, skin['job'], margins = True)
col_all = ct.columns[-1]

# 분할 전 엔트로피
before = [] 
for i in ct.columns[:-1]:  # 해원님 코드 참고
    before.append(ct.loc[i,col_all]/ct.loc[col_all,col_all])

bf_entropy = 0
for i in before:
    bf_entropy += (- i* np.log2(i))

# 분할 후 엔트로피
after = []
for i in ct.columns[:-1]:
    for k in ct.columns[:-1]:
        after.append(ct.loc[k,i]/ct.loc[col_all,i])    
        
af_cnt = []
for i in after:
    af_cnt.append(- i*np.log2(i))

part = []
for i in ct.columns[:-1]:
    part.append(ct.loc[col_all,i]/ct.loc[col_all,col_all])
        
af_entropy = part[0]*np.sum(af_cnt[:2]) + part[1]*np.sum(af_cnt[2:])    

# 정보획득량 구하기
info_gain = bf_entropy - af_entropy
info_gain

문제268. 직업에 대한 정보획득량을 파이썬으로 구하시오.

# 정보획득량 전체코드
import pandas as pd
import numpy as np

skin = pd.read_csv("c:\\data\\skin.csv")

x = skin['cupon_react']

ct = pd.crosstab(x, skin['job'], margins = True)

# 분할 전 엔트로피
before = [] 
for i in ct.columns[:-1]:  # 해원님 코드 참고
    before.append(ct.loc[i,ct.columns[-1]]/ct.loc[ct.columns[-1],ct.columns[-1]])

bf_entropy = 0
for i in before:
    bf_entropy += (- i* np.log2(i))

# 분할 후 엔트로피
after = []
for i in ct.columns[:-1]:
    for k in ct.columns[:-1]:
        after.append(ct.loc[k,i]/ct.loc[ct.columns[-1],i])    
        
af_cnt = []
a = ct.loc['All','NO']/ct.loc['All','All']
b = ct.loc['All','YES']/ct.loc['All','All']

for i in after:
    af_cnt.append(- i*np.log2(i))
    
af_entropy = a * (af_cnt[0]+af_cnt[1]) + b * (af_cnt[2]+af_cnt[3])

# 정보획득량 구하기
info_gain = bf_entropy - af_entropy
info_gain

 0.03600662066790816  

 

 

48 R로 의사결정트리 모델 구현하기(화장품 고객 데이터의 구매여부 모델 생성)

 

# 1. 의사결정트리에 필요한 패키지 설치

# 2. 화장품 고객 데이터 로드

# 3. 결측치 확인

# 4. 데이터를 훈련(80%)와 테스트(20%)로 분리

# 5. 훈련 데이터로 의사결정트리 모델을 생성

# 6. 훈련된 모델에 테스트 데이터를 넣어서 결과를 예측

# 7. 모델의 성능(정확도)를 확인

# 8. 모델의 성능 높이기

 

# R
# 1. 의사결정트리에 필요한 패키지 설치
install.packages("C50")
library(C50)

# 2. 화장품 고객 데이터 로드
skin <- read.csv("c:\\data\\skin.csv", stringsAsFactors = T)  # 반드시 팩터형으로 가져올 것
head(skin)

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

# 4. 데이터를 훈련(80%)와 테스트(20%)로 분리
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

# 5. 훈련 데이터로 의사결정트리 모델을 생성
library(C50)
model <- C5.0(train_data[ ,c(-1,-7)], train_data[ , 7])
model

model <- C5.0(정답을 제외한 훈련데이터 , 정답 훈련데이터) 

트리사이즈가 5인 것을 확인할 수 있음.

 

summary(model)

# 출력결과
Call:
C5.0.default(x = train_data[, c(-1, -7)], y = train_data[, 7])


C5.0 [Release 2.07 GPL Edition]  	Tue Feb 08 11:24:54 2022
-------------------------------

Class specified by attribute `outcome'

Read 25 cases (6 attributes) from undefined.data   # 훈련데이터 25개로 모델 생성

Decision tree:   # 정보획득량이 높은 컬럼부터
 
marry = NO: NO (7)   # 결혼 안했으면 다 구매 안함
marry = YES:    # 결혼 했다면
:...car = YES: YES (7/1)   # 결혼했는데 차가 있으면 구매 6명, 1명은 구매안함
    car = NO:    # 차가 없는 사람중에
    :...job = NO: NO (4)   # 직업이 없으면 다 구매 안했음(4명)
        job = YES:   # 직업이 있으면
        :...age <= 20: NO (2)   # 나이가 20이하면 구매 안했음(2명)
            age > 20: YES (5)   # 나이가 20보다 크면 구매했음(5명)


Evaluation on training data (25 cases):

	    Decision Tree   
	  ----------------  
	  Size      Errors  

	     5    1( 4.0%)   <<


	   (a)   (b)    <-classified as
	  ----  ----
	    13     1    (a): class NO
	          11    (b): class YES


	Attribute usage:

	100.00%	marry
	 72.00%	car
	 44.00%	job
	 28.00%	age

위의 결과를 시각화 하려면?

plot(model)

car가 불순도가 가장 높음. 하지만 건들지 않겠음. 오버피팅이 될 수 있기 때문.

 

# 훈련데이터 25개를 모델에 넣어서 훈련데이터는 몇개를 맞추는지 확인
train_result <- predict(model, train_data[ ,c(-1,-7)])
sum(train_result == train_data[,7]) / 25 * 100  # 96%

# 6. 훈련된 모델에 테스트 데이터를 넣어서 결과를 예측
test_result <- predict(model, test_data[,c(-1,-7)])
test_result

# 7. 모델의 성능(정확도)를 확인
sum(test_result == test_data[,7]) / 5 * 100  # 60%

훈련데이터는 96%의 정확도이고 테스트 데이터는 60%의 정확도를 보이는 모델이 생성되었음.

테스트 데이터는 5개중에 3개 맞춤.

 

# 8. 모델의 성능높이기
library(C50)
model <- C5.0(train_data[,c(-1,-7)], train_data[,7], trials = 5)

trials = 5의 의미는 의사결정트리 모델을 5개를 생성해서 그 5개의 모델 예측 결과를 가지고 다수결에 의해서 훈련 데이터를 분류함. 훈련 데이터에서 샘플을 추출해 5개의 의사결정트리 모델을 생성

# 8. 모델의 성능높이기
library(C50)
model2 <- C5.0(train_data[,c(-1,-7)], train_data[,7], trials = 5)

summary(model2) # 모델 확인

문제269. (점심시간 문제) 성능을 개선한 model2를 훈련데이터에 넣어 정확도가 어떻게 되는지 확인하시오.

# model2 성능 확인
train_result2 <- predict(model2, train_data[ ,c(-1,-7)])
sum(train_result2 == train_data[,7]) / 25 * 100  # 100% 정확도

문제270. model2 모형으로 테스트 데이터 5개의 정확도를 확인하시오.

test_result2 <- predict(model2, test_data[ ,c(-1,-7)])
sum(test_result2 == test_data[,7]) / 5 * 100  # 60% 정확도

개선한 모델도 테스트 데이터의 정확도가 60%밖에 되지 않음.

 

> 훈련 데이터의 정확도는 100%인데 테스트 데이터의 정확도가 60%이면 오버피팅되어 있다고 볼 수 있음.

의사결정트리의 단점 중 하나가 오버피팅이 다른 모델에 비해서 높은 것.

 

이런 경우에는 데이터가 30개밖에 안되서(데이터가 너무 작아서) 의사결정트리로 구현하기에는 적절하지 않음. 그래서 데이터를 늘려야 함.

 

 

문제271. Iris데이터로 꽃을 분류하는 의사결정트리 분류모델을 생성하시오.

(데이터 : iris2.csv, 정답컬럼 : Species)

Sepal.Length : 꽃받침의 길이

Sepal.Width : 꽃받침의 너비

Petal.Length : 꽃잎의 길이

Petal.Width : 꽃잎의 너비

Species : 아이리스 종류(정답컬럼)

# 1. 데이터 로드
iris <- read.csv("c:\\data\\iris2.csv", stringsAsFactors = T)
head(iris)

# 2. 결측치 확인
colSums(is.na(iris)) # 결측치 없음

# 3. 데이터를 훈련(80%)와 테스트(20%)로 분리
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

prop.table(table(train_data$Species))
prop.table(table(test_data$Species))

# 4. 훈련 데이터로 의사결정트리 모델을 생성
library(C50)
iris_model <- C5.0(train_data[ ,-5], train_data[ ,5]) 
summary(iris_model)
plot(iris_model)

> 꽃받침은 의사결정트리 모델에 중요한 컬럼이 아님. 꽃잎 컬럼들만 의사결정트리가 만들어짐

 

# 훈련데이터로 모델 테스트
train_result <- predict(iris_model, train_data[,-5])
sum(train_result == train_data[,5]) / length(train_data[,5]) * 100  #97.5%

# 5. 훈련된 모델에 테스트 데이터를 넣어서 결과를 예측
test_result <- predict(iris_model, test_data[,-5])
sum(test_result == test_data[,5]) / length(test_data[,5]) *  100  #93.33333%
# 6. 모델의 성능 높이기
library(C50)
iris_model2 <- C5.0(train_data[ ,-5], train_data[ ,5], trials = 10) 

train_result2 <- predict(iris_model2, train_data[,-5])
sum(train_result2 == train_data[,5]) / length(train_data[,5]) * 100  #100%

test_result2 <- predict(iris_model2, test_data[,-5])
sum(test_result2 == test_data[,5]) / length(test_data[,5]) *  100  #93.33333%

 

 

ㅇ (의사결정트리 실습3) 은행대출채무 불이행 데이터로 채무 불이행할 것 같은 고객이 누구인지 확인하기(C50패키지)

은행에서 의사결정트리 모델을 어떻게 활용하는가?

은행에 대출을 신청했는데 대출 거부를 당했으면 은행에서는 대출 거부된 이유를 당사자에게 설명해줘야하는데 이때 활용이 됩니다.

# 1. 의사결정트리에 필요한 패키지 설치
library(C50)

# 2. 데이터 로드
credit <- read.csv("c:\\data\\credit.csv", stringsAsFactors = T)
head(credit)
str(credit)
summary(credit$amount)

데이터설명:

checking_balance : 예금계좌

saving_balance : 적금계좌

amount : 대출금액(250마르크 ~ 18424마르크), 100마르크 = 6~7만원

default : 채무 ( yes : 대출금 상환안함 / no : 대출금 상환함 )

prop.table(table(credit$default))

 no yes 
0.7 0.3   > 30%에 해당하는 사람들이 대출금을 상환하지 않고 있어서 머신러닝 모델을 만들어서 대출금을 상환하지 않는 고객의 비율을 최대한 낮춰보는 것이 이 머신러닝 모델을 만드는 목적

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

# 4. 데이터를 훈련(90%)와 테스트(10%)로 분리
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
prop.table(table(train_data$default))
prop.table(table(test_data$default))

ncol(credit) # 17

# 5. 훈련 데이터로 의사결정트리 모델을 생성
library(C50)
set.seed(1)  # 모델에 대한 seed 설정
credit_model <- C5.0( train_data[,-17], train_data[ ,17])

table(credit$checking_balance)

 < 0 DM   > 200 DM 1 - 200 DM    unknown 
      274              63           269           394 

> 예금계좌에 돈이 하나도 없는 사람이 274명이고 200마르크보다 큰 사람이 63명, 1~200마르크 사이가 269명, 알 수 없는 사람들이 394명임.

summary(credit_model)

checking_balance = unknown: no (356/42)    
checking_balance in {< 0 DM,> 200 DM,1 - 200 DM}:
:...amount > 8648: yes (31/6)
    amount <= 8648:
    :...credit_history in {perfect,very good}:
        :...housing in {other,rent}: yes (26/3)
        :   housing = own:
        :   :...savings_balance in {> 1000 DM,500 - 1000 DM,
        :       :                   unknown}: no (8/2)

                  

> 예금계좌가 unknown인 사람중에 356명이 대출금을 상환(no)함. 예금계좌가 unknown이 아닌 사람들( {< 0 DM,> 200 DM,1 - 200 DM} ) 중에서 대출금액이 8648마르크보다 큰 사람들 중 31명이 대출금을 상환하지 않음(yes).

대출금이 8648마르크 이하인 사람들은 신용이력이  {perfect,very good}인 사람들 중에서 집이  {other,rent} 인 유형인 26명이 대출금을 상환하지 않음(yes). 집이 own인 사람인데 savings_balance가 {> 1000 DM,500 - 1000 DM,unknown}인 사람 중 8명이 대출금을 상환함(no)

 

# 6. 훈련된 모델에 데이터를 넣어서 결과 예측
train_result <- predict(credit_model, train_data[,-17])
table(train_result)

 no yes 
711 189  > 훈련데이터 900건 중 no(상환함) 711, yes(상환안함) 189으로 예측하고 있음.

sum(train_result == train_data[,17]) / length(train_data[,17]) * 100  #83.88889% 정확도

훈련데이터로 예측했을 때 정확도가 83.8%로 나옴.

 

test_result <- predict(credit_model, test_data[,-17])
table(test_result)

no yes 
 81  19 > 테스트 데이터 100건 중 no(상환함) 81, yes(상환안함) 19으로 예측하고 있음.

# 7. 모델의 성능(정확도)를 확인
sum(test_result == test_data[,17]) / length(test_data[,17]) * 100  #67% 정확도

테스트 데이터로 예측했을 때 정확도 67%

library(gmodels)
x <- CrossTable(test_data[,17], test_result)
x$t
# table(x = test_data[,17], y = test_result) # CrossTable안하고 이렇게 써도 됨

     y
x     no yes
  no  59  11
  yes 22   8

> FN이 22건이나 나옴. 상환할 줄 알았는데 상환하지 않은 사람의 수

 

# 8. 모델의 성능 높이기
# trials = 10
credit_model2 <- C5.0( train_data[,-17], train_data[ ,17], trials = 10) # 
train_result2 <- predict(credit_model2, train_data[,-17])

sum(train_result2 == train_data[,17]) / length(train_data[,17]) * 100  #91.88889% 정확도

test_result2 <- predict(credit_model2, test_data[,-17])

sum(test_result2 == test_data[,17]) / length(test_data[,17]) * 100  #73% 정확도

table(x = test_data[,17], y = test_result2)  #FN = 21

 

문제273. trials를 100으로 주면 FN값이 얼마나 줄어드는지 확인하시오.

# trials = 100
credit_model3 <- C5.0( train_data[,-17], train_data[ ,17], trials = 100) 
train_result3 <- predict(credit_model3, train_data[,-17])

sum(train_result3 == train_data[,17]) / length(train_data[,17]) * 100  #96.66667% 정확도

test_result3 <- predict(credit_model3, test_data[,-17])

sum(test_result3 == test_data[,17]) / length(test_data[,17]) * 100  #70% 정확도

a <- table(x = test_data[,17], y = test_result3)  #FN = 20
a[2,1]

 

의사결정 나무의 갯수는 1~100까지만 지정할 수 있음. = trials를 1~100까지 지정

 

+) trials 1~100까지 다 넣어보기

find_fn <-  c()
for (i in 1:100){
  credit_model <- C5.0( train_data[,-17], train_data[ ,17], trials = i) 
  train_result <- predict(credit_model, train_data[,-17])
  
  sum(train_result == train_data[,17]) / length(train_data[,17]) * 100  # 훈련데이터 정확도
  
  test_result <- predict(credit_model, test_data[,-17])
  
  sum(test_result == test_data[,17]) / length(test_data[,17]) * 100  # 테스트데이터 정확도
  
  a <- table(x = test_data[,17], y = test_result)  #FN
  find_fn <- append(find_fn, a[2,1])
}
find_fn
print(paste('i =',which.min(find_fn),'---> FN =',find_fn[which.min(find_fn)]))

 

ㅇ위 모델의 성능을 더 높이려면?
1. 머신러닝 알고리즘을 다른 알고리즘으로 변경

2. 기존 의사결정트리를 그대로 사용하는데 분류를 해나가는 기준을 변경

   - 엔트로피 지수 : 엔트로피지수로 정보획득량을 구해서 의사결정나무를 구성

   - 카이제곱값 : 카이제곱검정으로 정보획득량을 구해서 의사결정나무를 구성

   - 지니지수 : 지니지수로 정보획득량을 구해서 의사결정나무를 구성

 

ㅇ의사결정 패키지 2가지

1. C50 : 엔트로피 지수로 정보획득량을 구해서 의사결정나무를 구성

2. party : 카이제곱검정으로 정보획득량을 구해서 의사결정나무를 구성

> 결론을 이야기 하면 C50을 사용하거나 party를 사용하나 결과는 비슷한데 차이가 있다면 C50은 trials를 이용해서 바로 성능을 개선할 수 있는데 장점이고 party는 의사결정트리를 시각화할 수 있는 장점이 있음(party는 trials를 사용할 수 없음)

 

 

ㅇ독일은행 채무불이행자 예측을 party 패키지를 이용해서 의사결정트리 만들기

# 1. party 패키지 설치

# 2. 데이터 로드

# 3. 훈련데이터와 테스트 데이터 분리

# 4. 모델 생성

# 5. 예측

# 6. 모델 평가

# 7. 모델 시각화

# 1. party 패키지 설치
install.packages("party")
library(party)

# 2. 데이터 로드
credit <- read.csv("c:\\data\\credit.csv", stringsAsFactors = T)

# 3. 훈련데이터와 테스트 데이터 분리
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

# 4. 모델 생성
party_model <- ctree(default ~ ., data = train_data)
plot(party_model)

> C5.0 패키지 문법 : model <- C5.0( 정답 뺀 데이터(x = 으로 써도 가능), 정답 데이터(y = 으로 써도 가능))

> party 패키지 문법 : model <- ctree( 정답컬럼 ~ ., data = 데이터 프레임명)

 

# 5. 예측
test_result <- predict(party_model, test_data[,-17])

# 6. 모델 평가
sum(test_result == test_data[,17])/length(test_data[,17]) * 100  # 70% 정확도

# 7. 모델 시각화
plot(party_model)

 

ㅇ오늘 의사결정트리 모델 생성할 때 다뤘던 데이터

1. 화장품 고객 데이터

2. 독일은행 데이터

3. iris 데이터

 

 

문제273. (오늘의 마지막 문제) party패키지를 이용해서 iris데이터를 분류하는 의사결정트리 모델을 생성하시오.

# 1. 패키지 설치
library(party)

# 2. 데이터 로드
iris <- read.csv("c:\\data\\iris2.csv", stringsAsFactors = T)
head(iris)

# 3. 훈련데이터와 테스트 데이터 분리
set.seed(1)
train_num <- createDataPartition(iris$Species, p=0.9, list=F)
train_data <- iris[train_num, ]
test_data <- iris[-train_num, ]

nrow(train_data) #135
nrow(test_data) #15

# 4. 모델 생성
party_model <- ctree(Species ~ ., data = train_data)

# 5. 예측
# 훈련데이터로 연습 예측
train_result <- predict(party_model, train_data[ ,-5])
sum(train_result == train_data[,5]) / length(train_data[,5]) *100  #95.55556% 정확도

# 테스트데이터 예측
test_result <- predict(party_model, test_data[,-5])

# 6. 모델 평가
sum(test_result == test_data[,5])/length(test_data[,5]) * 100  # 100% 정확도
table(x = test_data[,5], y = test_result)  # CrossTable의 $t와 같은 기능

# 7. 모델 시각화
plot(party_model)

                 y
x                 Iris-setosa Iris-versicolor Iris-virginica
  Iris-setosa               5               0              0
  Iris-versicolor           0               5              0
  Iris-virginica            0               0               5    > 실제값과 예측값이 모두 맞은 것을 확인할 수 있음

 

 

 

 

 

+) 특정 컬럼을 추출하고 싶을 경우 subset() 메소드를 사용하면 됨

subset(train_data, select = -default)   # dafault컬럼만 제외하고 출력
subset(train_data, select = c(default,checking_balance)) # default,checking_balance만 출력
subset(train_data, select = - c(default,checking_balance)) # default,checking_balance만 제외하고 출력
반응형