목표변수가 집단을 의미하는 범주형 의사결정나무 -> 분류나무모형
목표변수가 연속형 변수인 의사결정나무 -> 회귀나무모형
목표 변수가 연속형 변수인 경우, 의사결정나무는 회귀나무라고 한다. 통계학에서 목표변수가 연속형일 때 흔히 수행하는 분석이 선형 회귀분석인 것을 연상해 본다면 명칭이 이해가 될 것이다. 일반적으로 범주형 변수들은 가변수화하여 0,1의 형태로 변형하여 회귀분석에 활용하지만, 범주형 변수의 수가 매우 많고, 각 범주형 변수의 개수도 많은 경우 해석은 어려워지고 분석도 복잡해 진다. 이러한 경우 회귀나무 모형을 사용하게 되면, 가변수를 생성할 필요 없이 범주형 입력 변수와 연속형 입력변수를 그대로 활용할 수 있게 되어 분석 및 그 해석이 용이해질 수 있다.
예제) 목표변수가 숫자인 보스턴하우징 데이터를 이용하여 cart 방법을 이용한 회귀나무 모형 구축
1) 데이터 읽기
> library(MASS)
> Boston$chas=factor(Boston$chas)
> Boston$rad=factor(Boston$rad)
> summary(Boston)
> Boston<-Boston[,-15]
> str(Boston)
'data.frame': 506 obs. of 14 variables:
$ crim : num 0.00632 0.02731 0.02729 0.03237 0.06905 ...
$ zn : num 18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
$ indus : num 2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
$ chas : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
$ nox : num 0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
$ rm : num 6.58 6.42 7.18 7 7.15 ...
$ age : num 65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
$ dis : num 4.09 4.97 4.97 6.06 6.06 ...
$ rad : Factor w/ 9 levels "1","2","3","4",..: 1 2 2 3 3 3 5 5 5 5 ...
$ tax : num 296 242 242 222 222 222 311 311 311 311 ...
$ ptratio: num 15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
$ black : num 397 397 393 395 397 ...
$ lstat : num 4.98 9.14 4.03 2.94 5.33 ...
$ medv : num 24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...
2) cart 회귀나무모형 실행
> library(rpart)
> my.control<-rpart.control(xval=10,cp=0,minsplit=nrow(Boston)*0.05)
> fit.Boston=rpart(medv~.,data=Boston,method='anova',control=my.control)
> fit.Boston
n= 506
node), split, n, deviance, yval
* denotes terminal node
1) root 506 42716.30000 22.532810
… 중간 생략…
30) rad=2,7,8 10 140.04100 43.670000 *
31) rad=1,3,4,5,24 17 135.72120 48.158820 *
함수 설명:
rpart.control(xval=10,cp=0,minsplit=nrow(Boston)*0.05) : 10-fold교차타당성 오분류율을 계산하고, 가지치기 방법에서 a값은 0이될때까지 순차적인 나무구조를 저장하도록 한다. 그리고 노드를 나누는 최소 자료이 수인 minsplit은 전체 데이터 수의 5% 수준에서 정의한다
3) 가지치기 수행 : ①최소 cp 값 찾기
> printcp(fit.Boston)
Regression tree:
rpart(formula = medv ~ ., data = Boston, method = "anova", control = my.control)
Variables actually used in tree construction:
[1] age crim indus lstat nox ptratio rad rm
Root node error: 42716/506 = 84.42
n= 506
CP nsplit rel error xerror xstd
1 0.45274420 0 1.00000 1.00298 0.083114
2 0.17117244 1 0.54726 0.62762 0.058631
3 0.07165784 2 0.37608 0.43188 0.047879
4 0.03428819 3 0.30443 0.35161 0.043496
5 0.02661300 4 0.27014 0.32766 0.042850
6 0.01802372 5 0.24352 0.29534 0.041781
7 0.01348721 6 0.22550 0.27799 0.039217
8 0.01285085 7 0.21201 0.28299 0.039277
9 0.00844925 8 0.19916 0.26415 0.032335
10 0.00833821 9 0.19071 0.25667 0.031357
11 0.00726539 10 0.18238 0.25624 0.031407
12 0.00612633 11 0.17511 0.25343 0.031377
13 0.00480532 12 0.16898 0.24000 0.028645
14 0.00410785 13 0.16418 0.23897 0.027439
15 0.00394102 14 0.16007 0.23177 0.027282
16 0.00385379 15 0.15613 0.23177 0.027282
17 0.00223540 16 0.15228 0.22527 0.027268
18 0.00171691 17 0.15004 0.22441 0.027196
19 0.00153485 18 0.14832 0.22453 0.026980
20 0.00140981 19 0.14679 0.22585 0.026952
21 0.00135401 20 0.14538 0.22646 0.026946
22 0.00113725 22 0.14267 0.22674 0.026936
23 0.00098921 23 0.14153 0.22490 0.026948
24 0.00081924 24 0.14054 0.22674 0.027098
25 0.00074570 25 0.13972 0.22624 0.027097
26 0.00074096 27 0.13823 0.22671 0.027092
27 0.00040763 28 0.13749 0.22753 0.027088
28 0.00031464 29 0.13708 0.22749 0.027081
29 0.00019358 30 0.13677 0.22798 0.027098
30 0.00000000 31 0.13658 0.22816 0.027099
> names(fit.Boston)
[1] "frame" "where" "call" "terms"
[5] "cptable" "method" "parms" "control"
[9] "functions" "numresp" "splits" "csplit"
[13] "variable.importance" "y" "ordered"
> which.min(fit.Boston$cp[,4])
18
18
> which.min(fit.Boston$cptable[,'xerror'])
18
18
> fit.Boston$cp[18,]
CP nsplit rel error xerror xstd
0.001716908 17.000000000 0.150039975 0.224411843 0.027196436
> fit.Boston$cp[18]
[1] 0.001716908
> fit.Boston$cptable[which.min(fit.Boston$cptable[,'xerror']),]
CP nsplit rel error xerror xstd
0.001716908 17.000000000 0.150039975 0.224411843 0.027196436
> fit.Boston$cptable[which.min(fit.Boston$cptable[,'xerror'])]
[1] 0.001716908
> 0.22441 +0.027196
[1] 0.251606
결과 해석: 가장 작은 cp값은 18번째 0.001716908이나 가장 작은 xerror의 xstd 범위 내 즉, 0.22441 + 0.027196 = 0.251606 내 작은 cp 값 적용도 가능하다.
3) 가지치기 수행 : ②가장 적은 cp값을 찾았으니 prune 함수를 이용하여 가지기치를 수행하자.
> fit.prune.Boston<-prune(fit.Boston,cp=0.00304027)
> print(fit.prune.Boston)
> names(fit.prune.Boston)
[1] "frame" "where" "call" "terms"
[5] "cptable" "method" "parms" "control"
[9] "functions" "numresp" "splits" "csplit"
[13] "variable.importance" "y" "ordered"
> fit.prune.Boston$variable.importance
rm lstat indus nox age ptratio dis
23124.34862 16998.05183 5871.44959 5492.56562 5042.88012 4757.36213 4742.25575
tax crim zn rad black chas
4155.77974 2258.47799 1760.50488 1390.73661 772.28149 11.40364
node), split, n, deviance, yval 는 각각 노드번호, 분할규칙, 해등 노드의 총 관칙치 수, 해당 노드의 분산, 해당 노드의 목표변수 예측치(회귀나무일 때는 평균값)의 순서로 정보를 제공하고 있고, 줄 마지막에 있는 * denotes terminal node는 * 표시가 최종노드임을 알려주고 있다.
fit.prune.Boston$variable.importance로 중요 변수도 알 수 있다. rm이 가장 중요하고 상대적으로 chas가 덜 중요한 변수이다.
4) cart 회귀나무 모형 그리기
> plot(fit.prune.Boston)
> plot(fit.prune.Boston,uniform=T)
> plot(fit.prune.Boston,uniform=T,margin=0.1)
> text(fit.prune.Boston,use.n=T,col='blue',cex=0.7)
5) 목표변수의 적합값을 구하고 평가하자. 먼저 MSE 구하기
> Boston$medv.hat = predict(fit.prune.Boston,newdata=Boston,type='vector')
> mean((Boston$medv - Boston$medv.hat)^2)
[1] 10.8643
> plot(Boston$medv,Boston$medv.hat,xlab='Observed values',ylab='Fitted values',xlim=c(0,50),ylim=c(0,50))
> abline(0,1)
결과 해석: MSE(Mean Squared Error, 평균오차제곱합)은 10.8643 이고, plot 그래프를 통해 cart 회귀나무모형의 적합값과 실제값의 일치도를 보이고 있음을 알 수 있다. 회귀나무 모형에서는 최종노드에는 적합값으로 관찰치들의 평균값을 사용하기 때문에 동일한 값을 가진 적합값이 많음을 확인할 수 있다.
6) 보스턴하우징 데이터를 훈련 데이터와 검증 데이터로 분할하여 회귀나무를 평가해 보자.
> set.seed(1234)
> Boston<-Boston[,-15]
> str(Boston)
'data.frame': 506 obs. of 14 variables:
$ crim : num 0.00632 0.02731 0.02729 0.03237 0.06905 ...
$ zn : num 18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
$ indus : num 2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
$ chas : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
$ nox : num 0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
$ rm : num 6.58 6.42 7.18 7 7.15 ...
$ age : num 65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
$ dis : num 4.09 4.97 4.97 6.06 6.06 ...
$ rad : Factor w/ 9 levels "1","2","3","4",..: 1 2 2 3 3 3 5 5 5 5 ...
$ tax : num 296 242 242 222 222 222 311 311 311 311 ...
$ ptratio: num 15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
$ black : num 397 397 393 395 397 ...
$ lstat : num 4.98 9.14 4.03 2.94 5.33 ...
$ medv : num 24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...
> i=sample(1:nrow(Boston),round(nrow(Boston)*0.7))
> Boston.train= Boston[i,] #70% for training data 훈련 데이터
> Boston.test = Boston[-i,] #30% for test data 검증 데이터
#Fit a CART model by training data
> fit.train.Boston<-rpart(medv~.,data=Boston.train,method='anova',control=my.control)
> printcp(fit.train.Boston)
Regression tree:
rpart(formula = medv ~ ., data = Boston.train, method = "anova",
control = my.control)
Variables actually used in tree construction:
[1] age black crim dis indus lstat nox rad rm tax
Root node error: 28893/354 = 81.618
n= 354
CP nsplit rel error xerror xstd
1 0.45225287 0 1.00000 1.00649 0.099965
2 0.17404032 1 0.54775 0.64452 0.071140
3 0.06762461 2 0.37371 0.46448 0.058349
4 0.04776357 3 0.30608 0.41314 0.057818
5 0.02547541 4 0.25832 0.34693 0.051952
6 0.02433056 5 0.23284 0.32946 0.051911
7 0.01063840 6 0.20851 0.31645 0.051778
8 0.00718287 7 0.19787 0.31064 0.051954
9 0.00584084 8 0.19069 0.30989 0.052698
10 0.00558293 10 0.17901 0.30937 0.052600
11 0.00327437 11 0.17343 0.30188 0.052057
12 0.00323971 12 0.17015 0.29575 0.052015
13 0.00266789 13 0.16691 0.29202 0.051794
14 0.00242096 14 0.16424 0.29298 0.051791
15 0.00217440 15 0.16182 0.29230 0.051816
16 0.00164045 17 0.15748 0.28260 0.049406
17 0.00090517 18 0.15583 0.28542 0.049919
18 0.00081553 19 0.15493 0.28338 0.049884
19 0.00079750 20 0.15411 0.28347 0.049883
20 0.00067797 21 0.15332 0.28384 0.049878
21 0.00000000 22 0.15264 0.28332 0.049290
> which.min(fit.train.Boston$cptable[,'xerror'])
16
16
> fit.train.Boston$cptable[16]
[1] 0.001640451
> fit.train.Boston$cptable[16,]
CP nsplit rel error xerror xstd
0.001640451 17.000000000 0.157475033 0.282599587 0.049406453
> 0.28260 + 0.049406
[1] 0.332006
> fit.prune.train.Boston<-prune(fit.train.Boston,cp=0.00558293)
> fit.prune.train.Boston
> summary(fit.prune.train.Boston)
Call:
rpart(formula = medv ~ ., data = Boston.train, method = "anova",
control = my.control)
n= 354
CP nsplit rel error xerror xstd
1 0.452252869 0 1.0000000 1.0064946 0.09996494
2 0.174040318 1 0.5477471 0.6445168 0.07114014
3 0.067624608 2 0.3737068 0.4644789 0.05834934
4 0.047763574 3 0.3060822 0.4131392 0.05781821
5 0.025475412 4 0.2583186 0.3469320 0.05195198
6 0.024330558 5 0.2328432 0.3294558 0.05191119
7 0.010638405 6 0.2085127 0.3164491 0.05177769
8 0.007182873 7 0.1978743 0.3106449 0.05195410
9 0.005840842 8 0.1906914 0.3098942 0.05269778
10 0.005582930 10 0.1790097 0.3093730 0.05259987
Variable importance
lstat rm indus crim nox age dis tax rad ptratio
26 19 14 11 11 11 3 2 1 1
Node number 1: 354 observations, complexity param=0.4522529
mean=22.15763, MSE=81.61758
left son=2 (215 obs) right son=3 (139 obs)
Primary splits: #improve는 개선도를 뜻한다.
lstat < 9.63 to the right, improve=0.4522529, (0 missing)
rm < 6.9715 to the left, improve=0.4283989, (0 missing)
indus < 6.66 to the right, improve=0.2809072, (0 missing)
ptratio < 19.9 to the right, improve=0.2685387, (0 missing)
nox < 0.6635 to the right, improve=0.2328252, (0 missing)
Surrogate splits:
indus < 7.625 to the right, agree=0.825, adj=0.554, (0 split)
nox < 0.519 to the right, agree=0.802, adj=0.496, (0 split)
rm < 6.478 to the left, agree=0.794, adj=0.475, (0 split)
crim < 0.08547 to the right, agree=0.785, adj=0.453, (0 split)
age < 64.8 to the right, agree=0.782, adj=0.446, (0 split)
> plot(fit.prune.train.Boston,uniform=T,margin=0.1)
> text(fit.prune.train.Boston,use.n=T,col='blue',cex=0.8)
7) 예측치:
이제 이 회귀나무를 활용하여 30% 검증 데이터에 적용시켜서 예측치를 구해보자. 그리고 그 예측치의 오차를 계산하기 위해 예측평균오차제곱합인 PMSE(Predicted Mean Square Error)를 구해보자
> medv.hat.test<-predict(fit.prune.train.Boston,newdata=Boston.test,type='vector')
> mean((Boston.test$medv-medv.hat.test)^2) #PSME,
[1] 13.95258
결과 해석: 예측평균오차제곱합은 13.95258로 계산되었다.
PMSE 계산시, Boston.train$medv가 아닌 Boston.test$medv임을 유의하자.
위에서 구한 관찰값과 적합값의 MSE(Mean Squared Error, 평균오차제곱합) 10.8643 과 비교하면 다소 높음을 알 수 있다.
복습과 무한반복 연습이 필요해 보인다
출처: 데이터마이닝(장영재, 김현중, 조형준 공저)
'KNOU > 2 데이터마이닝' 카테고리의 다른 글
제4장 앙상블 모형 - 회귀앙상블모형 - 랜덤포레스트 (1) | 2016.11.02 |
---|---|
제4장 앙상블 모형 (0) | 2016.11.02 |
제3장 나무모형 - 분류나무모형 (4) | 2016.10.18 |
제2장 회귀모형 - 로지스틱 회귀모형 연습 (0) | 2016.09.14 |
제2장 회귀모형 - 선형회귀 연습 (0) | 2016.09.14 |