반응형

연속형 목표 변수.

목표변수가 연속형인 경우 모형의 예측력 측도로는 주로 예측평균제곱오차(PMSE: Prediction Mean Squared Error) 사용한다.

 

PMSE = sigma(yi-yhat i)2/n

 

관측치(yi) 예측치(yhat i) 사이에 차이가 적을수록 예측을 잘한 것으로므로 예측평균제곱오차가 작을수록 모형의 예측력이 높다고 있다. 또한 관측치와 예측치를 가로축 세로축으로 놓고 그린 산점도(scatter plot) 45 대각선을 중심으로 모여 있으면 예측력이 좋다고 있다.

 

보스턴 하우징 데이터 Housing Values in Suburbs of Boston

(출처: http://127.0.0.1:31866/library/MASS/html/Boston.html)

 

변수명

속성

변수 설명

crim

수치형

(numeric)

per capita crime rate by town
타운별 1인당 범죄율

zn

수치형

(numeric)

proportion of residential land zoned for lots over 25,000 sq.ft
25,000
평방피트를 초과하는 거주지역 비율

indus

수치형

(numeric)

proportion of non-retail business acres per town.
비소매 사업지역의 토지 비율

chas

범주형

(integer)

Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
찰스강 더비 변수 (강의 경계에 위치 = 1, 아니면 = 0)

nox

수치형

(numeric)

nitrogen oxides concentration (parts per 10 million)
10ppm
농축 일산화질소

rm

수치형

(numeric)

average number of rooms per dwelling.
주택 1가구등 방의 평균 개수

age

수치형

(numeric)

proportion of owner-occupied units built prior to 1940.
1940
이전에 건축된 소유자 주택 비율

dis

수치형

(numeric)

weighted mean of distances to five Boston employment centres.
5
개의 보스턴 고용센터까지의 접근성 지수

rad

범주형

(integer)

index of accessibility to radial highways.
방사형 도로까지의 접근성 지수

tax

수치형

(numeric)

full-value property-tax rate per \$10,000.
10,000
달러당 재산세율

ptratio

수치형

(numeric)

pupil-teacher ratio by town.
타운별 학생/교사 비율

black

수치형

(numeric)

1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town.
타운별 흑인의 비율

lstat

수치형

(numeric)

lower status of the population (percent).
모집단의 하위계층의 비율

medv
(
목표변수)

수치형

(numeric)

median value of owner-occupied homes in \$1000s.
본인 소유의 주택가격(중앙값)

 

 

 

 

<데이터 준비>

> library(MASS)

> str(Boston)

'data.frame':   506 obs. of  15 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 ...

 $ medv.hat: num  23.7 23.7 32.4 32.4 32.4 ...

> dim(Boston)

[1] 506  15

> 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 ...

 



1. 선형회귀모형

> 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.reg = lm(medv~.,data = Boston.train) #선형회귀모형

> summary(fit.reg) #선형회귀모형 요약

 

Call:

lm(formula = medv ~ ., data = Boston.train)

 

Residuals:

    Min      1Q  Median      3Q     Max

-9.9375 -2.7260 -0.4588  1.7937 24.3220

 

Coefficients:

              Estimate Std. Error t value Pr(>|t|)   

(Intercept)  41.027525   6.644569   6.175 1.93e-09 ***

crim         -0.098730   0.052682  -1.874 0.061794 . 

zn            0.059161   0.016208   3.650 0.000304 ***

indus         0.073377   0.075763   0.969 0.333492   

chas1         2.314962   1.023899   2.261 0.024409 * 

nox         -18.307923   4.638939  -3.947 9.67e-05 ***

rm            3.014437   0.498806   6.043 4.04e-09 ***

age          -0.002066   0.016414  -0.126 0.899922   

dis          -1.564686   0.235506  -6.644 1.25e-10 ***

rad2          1.485691   1.707949   0.870 0.384999   

rad3          5.371463   1.513283   3.550 0.000441 ***

rad4          2.763349   1.365438   2.024 0.043791 * 

rad5          3.407697   1.380288   2.469 0.014057 * 

rad6          1.840557   1.687536   1.091 0.276204   

rad7          3.790169   1.832307   2.069 0.039362 * 

rad8          4.691472   1.677890   2.796 0.005473 **

rad24         8.224882   2.114575   3.890 0.000121 ***

tax          -0.009025   0.004555  -1.981 0.048382 * 

ptratio      -1.021842   0.175757  -5.814 1.43e-08 ***

black         0.008734   0.003633   2.404 0.016749 * 

lstat        -0.596699   0.059397 -10.046  < 2e-16 ***

---

Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 

Residual standard error: 4.658 on 333 degrees of freedom

Multiple R-squared:  0.7468,    Adjusted R-squared:  0.7316

F-statistic: 49.11 on 20 and 333 DF,  p-value: < 2.2e-16

 

> fit.step.reg = step(fit.reg,direction = 'both', trace = FALSE) #단계적 선택법, trace=T 경우 AIC 있다.

> summary(fit.step.reg) #조정된 모델 요약

 

Call:

lm(formula = medv ~ crim + zn + chas + nox + rm + dis + rad +

    tax + ptratio + black + lstat, data = Boston.train)

 

Residuals:

    Min      1Q  Median      3Q     Max

-9.8479 -2.6319 -0.4838  1.7225 24.3723

 

Coefficients:

              Estimate Std. Error t value Pr(>|t|)   

(Intercept)  40.432105   6.597006   6.129 2.49e-09 ***

crim         -0.102050   0.052455  -1.945 0.052555 . 

zn            0.058012   0.015903   3.648 0.000307 ***

chas1         2.428119   1.014994   2.392 0.017296 * 

nox         -17.025847   4.237976  -4.017 7.27e-05 ***

rm            2.969435   0.489146   6.071 3.45e-09 ***

dis          -1.604642   0.221607  -7.241 3.08e-12 ***

rad2          1.763045   1.681098   1.049 0.295051   

rad3          5.319042   1.507369   3.529 0.000476 ***

rad4          2.871626   1.358651   2.114 0.035290 * 

rad5          3.377174   1.377514   2.452 0.014731 * 

rad6          1.669858   1.672013   0.999 0.318656   

rad7          3.831541   1.823028   2.102 0.036322 * 

rad8          4.551858   1.667065   2.730 0.006659 **

rad24         7.906985   2.076704   3.807 0.000167 ***

tax          -0.007384   0.004213  -1.753 0.080567 . 

ptratio      -0.996843   0.173266  -5.753 1.98e-08 ***

black         0.008481   0.003612   2.348 0.019458 *  

lstat        -0.593839   0.055467 -10.706  < 2e-16 ***

---

Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 

Residual standard error: 4.65 on 335 degrees of freedom

Multiple R-squared:  0.7461,    Adjusted R-squared:  0.7324

F-statistic: 54.69 on 18 and 335 DF,  p-value: < 2.2e-16

 

> fit.step.reg$anova #제거된 변수 보기. age indus 제거

     Step Df   Deviance Resid. Df Resid. Dev      AIC

1         NA         NA       333   7223.934 1109.614

2   - age  1  0.3436196       334   7224.278 1107.631

3 - indus  1 20.2559598       335   7244.534 1106.622

 

> yhat.reg = predict(fit.step.reg,newdata=Boston.test,type='response') #데이터 예측, 목표값 예측 type=’response’

> mean((Boston.test$medv-yhat.reg)^2) #예측 모형의 평균 제곱 오차 PMSE

[1] 23.68968

 

 

 

 

 

2. 회귀 나무모형

> library(rpart)

> set.seed(1234)

> i=sample(1:nrow(Boston),round(nrow(Boston)*0.7))

> Boston.train = Boston[i,]

> Boston.test = Boston[-i,]

 

> my.control = rpart.control(xval=10, cp=0, minsplit=nrow(Boston.train)*0.05)

> fit.tree = rpart(medv~.,data=Boston.train, method='anova',control=my.control)#method=anova 선택 회귀나무, class 선택 분류나무

 

> which.min(fit.tree$cp[,4])

17

17

> fit.tree$cp[17]

[1] 0.00193237

> fit.tree$cp[17,]

         CP      nsplit   rel error      xerror        xstd

 0.00193237 17.00000000  0.12723520  0.23200911  0.04362543

> fit.tree$cp[17,1]

[1] 0.00193237

> ii = which.min(fit.tree$cp[,4])

> fit.prune.tree = prune(fit.tree,cp=fit.tree$cp[ii,1])

 

> plot(fit.prune.tree,uniform=T,margin=0.1)

> text(fit.prune.tree,col='blue',cex=0.7)





> yhat.tree = predict(fit.prune.tree,newdata=Boston.test,type='vector')

> mean((Boston.test$medv - yhat.tree)^2)

[1] 24.17478

 

 

 

 

 

 3. 신경망모형

> library(neuralnet)

 

> Boston2 = Boston

> for(i in 1:ncol(Boston2))if(!is.numeric(Boston2[,i]))Boston2[,i]=as.numeric(Boston[,i])

 

> max1 = apply(Boston2,2,max)

> min1 = apply(Boston2,2,min)

 

> sdat = scale(Boston2,center=min1,scale=max1-min1)

> sdat=as.data.frame(sdat)

> View(sdat)

 

> set.seed(1234)

> i=sample(1:nrow(Boston2),round(nrow(Boston2)*0.7))

> Boston2.train = sdat[i,]

> Boston2.test = sdat[-i,]

 

> vname = names(Boston2.train)

> form = as.formula(paste('medv~',paste(vname[!vname %in% "medv"],collapse='+')))

> form

medv ~ crim + zn + indus + chas + nox + rm + age + dis + rad +

    tax + ptratio + black + lstat

> fit.nn = neuralnet(form,data=Boston2.train,hidden=c(3,2),linear.output=T)#linear.output=T 회귀

> plot(fit.nn) 

 

> str(Boston.test)

'data.frame':   152 obs. of  14 variables:

 $ crim   : num  0.0273 0.0299 0.1446 0.6274 1.0539 ...

 $ zn     : num  0 0 12.5 0 0 0 0 0 0 0 ...

 $ indus  : num  7.07 2.18 7.87 8.14 8.14 8.14 8.14 8.14 8.14 8.14 ...

 $ chas   : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...

 $ nox    : num  0.469 0.458 0.524 0.538 0.538 0.538 0.538 0.538 0.538 0.538 ...

 $ rm     : num  7.18 6.43 6.17 5.83 5.93 ...

 $ age    : num  61.1 58.7 96.1 56.5 29.3 98.1 89.2 91.7 85.7 90.3 ...

 $ dis    : num  4.97 6.06 5.95 4.5 4.5 ...

 $ rad    : Factor w/ 9 levels "1","2","3","4",..: 2 3 5 4 4 4 4 4 4 4 ...

 $ tax    : num  242 222 311 307 307 307 307 307 307 307 ...

 $ ptratio: num  17.8 18.7 15.2 21 21 21 21 21 21 21 ...

 $ black  : num  393 394 397 396 387 ...

 $ lstat  : num  4.03 5.21 19.15 8.47 6.58 ...

 $ medv   : num  34.7 28.7 27.1 19.9 23.1 13.6 19.6 15.2 13.9 16.6 ...

> pred=compute(fit.nn,Boston2.test[,1:13])#14번째 변수가 medv 목표변수.compute 작성된 신경망모형을 이용하여 새로운 예에 적용하여 결과를 도출.

> summary(pred)

           Length Class  Mode  

neurons      3    -none- list  

net.result 152    -none- numeric

> yhat.nn = pred$net.result*(max(Boston2$medv)-min(Boston2$medv))+min(Boston2$medv)#back transformation, 목표변수를 조정전 변수값으로 환원하는 과정. 원래 가지고 있는 값으로 환원. 역변환 과정

> head(cbind(Boston2[i,14],yhat.nn),50)

    [,1]        [,2]

2   31.6 21.17115946

3   23.8 30.30760801

10  28.2 20.06686203

13  21.6 21.29808480

14  16.1 20.28145030

18  23.8 18.30584489

24  36.2 16.10587573

26  21.2 16.16710531

27  17.1 17.71479369

28  20.9 16.71171622

33  31.2 12.87970542

35  20.7 14.84095463

36  17.8 22.05195863

42  14.1 33.16705465

43  15.6 26.86733582

46  17.2 21.39641163

47  14.0 20.62187769

49  19.2 13.55437741

51  22.0 21.63602446

57  18.7 23.04343250

59  19.4 23.33368068

72  15.6 21.68452217

75  20.0 22.95006783

76  18.2 21.37727858

81  19.5 27.88596126

90  11.5 31.97882170

94  29.6 24.56223312

98   8.4 41.94592034

102  8.5 23.20282812

103 19.6 27.89450422

104 28.7 19.47439349

105 21.4 19.90631048

111 11.8 20.49063003

120 23.3 20.15009581

123 26.6 19.19281571

128 21.7 16.99190292

135 20.6 14.54799220

148 20.3 13.09572781

150 21.4 15.35938684

151 13.3 18.92860416

161 50.0 31.83885454

165 24.8 20.14597079

167 29.8 44.60929619

169 23.2 20.70938882

170 15.3 20.60174521

171 31.7 19.12045877

172 22.1 19.63836241

173 27.5 20.36004622

176 22.8 32.60724125

177 26.6 24.24612266

Warning message:

In cbind(Boston2[i, 14], yhat.nn) :

  number of rows of result is not a multiple of vector length (arg 1)

 

> Boston2.test$medv=Boston2.test$medv*(max(Boston2$medv)-min(Boston2$medv))+min(Boston2$medv)#back transformation

> mean((Boston2.test$medv-yhat.nn)^2)

[1] 19.34365205

 

 

 


4. 랜덤 포레스트

> library(randomForest)

> library(MASS)

> 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 ...

 

> set.seed(1234)

> i=sample(1:nrow(Boston),round(nrow(Boston)*0.7))

> Boston.train = Boston[i,]

> Boston.test = Boston[-i,]

 

> fit.rf = randomForest(medv~., data=Boston.train, ntree=100, mtry=5, importance = T, na.action=na.omit)

> fit.rf

 

Call:

 randomForest(formula = medv ~ ., data = Boston.train, ntree = 100,      mtry = 5, importance = T, na.action = na.omit)

               Type of random forest: regression

                     Number of trees: 100

No. of variables tried at each split: 5

 

          Mean of squared residuals: 11.60448807

                    % Var explained: 85.6

> summary(fit.rf)

                Length Class  Mode    

call              7    -none- call    

type              1    -none- character

predicted       354    -none- numeric 

mse             100    -none- numeric 

rsq             100    -none- numeric 

oob.times       354    -none- numeric 

importance       26    -none- numeric 

importanceSD     13    -none- numeric 

localImportance   0    -none- NULL    

proximity         0    -none- NULL    

ntree             1    -none- numeric 

mtry              1    -none- numeric 

forest           11    -none- list    

coefs             0    -none- NULL    

y               354    -none- numeric 

test              0    -none- NULL    

inbag             0    -none- NULL    

terms             3    terms  call    

> head(fit.rf$predicted,30)

         58         315         308         314         433         321

31.38358586 22.70753968 28.77652778 21.11594595 20.61043860 23.92682540

          5         117         332         256         345         270

32.56341667 20.64287500 20.64851351 20.71283333 30.29218391 21.12653153

        140         456         144         412         141         131

15.02797980 15.33454167 14.36800926 14.37462963 15.01537037 19.89743590

         92         114         154         147          77          20

23.40651042 19.04477778 15.95588235 16.05761261 21.11859649 19.79196970

        106         390         253         439         398          22

19.35319820 10.55704563 31.93869369  9.22796875 12.18531532 18.03844203

> importance(fit.rf,type=1)

              %IncMSE

crim     6.3818661727

zn       1.5083317623

indus    4.9357684617

chas     0.6538995939

nox      6.8146688870

rm      15.9443905699

age      3.6616801321

dis      7.0604314940

rad      6.1703029034

tax      3.9762473150

ptratio  7.1788230837

black    3.4813158641

lstat   13.5182724091

> yhat.rf=predict(fit.rf,newdata=Boston.test,type='response')

> mean((Boston.test$medv-yhat.rf)^2)

[1] 12.41384092

 

 


모형 에측평균제곱오차

 

모델

예측평균제곱오차

선형회귀모형

23.68968

회귀나무모형

24.17478

신경망모형

19.34365205

랜덤포레스트

12.41384092

 


관측치와 예측치의 산점도 생성

> par(mfrow=c(2,2))

 

>plot(Boston.test$medv,yhat.reg,xlab='Observed',ylab='Predicted',main='Regression',xlim=c(0,55),ylim=c(0,55))

> abline(0,1,lty=2)

 

> plot(Boston.test$medv,yhat.tree,xlab='Observed',ylab='Predicted',main='Decision tree',xlim=c(0,55),ylim=c(0,55))

> abline(0,1,lty=2)

 

> plot(Boston.test$medv,yhat.nn,xlab='Observed',ylab='Predicted',main='Neural Network',xlim=c(0,55),ylim=c(0,55))

> abline(0,1,lty=2)

 

> plot(Boston.test$medv,yhat.rf,xlab='Observed',ylab='Predicted',main='Random Forest',xlim=c(0,55),ylim=c(0,55))

> abline(0,1,lty=2) 

 

 -> 랜덤포레스트의 그래프가 가장 조밀하게 형성되어, 가장 좋은 예측 결과를 가진다고 할 수 있다.



출처: 데이터마이닝(장영재, 김현중, 조형준 공저)

 

반응형
Posted by 마르띤
,
반응형

1. 연관분석의 의의

연관분석(association analysis)은 연관 규칙을 통해 하나의 거래나 사건에 포함되어 있는 둘 이상의 품목 간 상호 연관성을 발견해 내는 것. 예를 들어 와인을 구매한 고객 중 15%가 치즈를 구매한다, 기저귀를 산 남성 고객은 맥주를 산다 등. 이러한 분석을 통하여 효율적인 매장 진열, 패키지 품목의 개발, 교차판매전략 등에 응용할 수 있다.

 

2. 연관 분석의 측도

(1) 지지율:  A -> B의 지지율은 전체 거래 중 A B가 동시에 포함된 거래의 수. 전체 거래 수 5건 중 빵과 버터를 동시에 구매한 거래수가 3건이라면 지지율은 3/5 = 60%. 지지율이 1에 가까울수록 연관도가 높다고 할 수 있다.

지지율의 단점은 표본수가 적은 경우 통계적 유의성을 증명하기 어렵고, A->B 연관규칙과 B->A의 연관규칙 차이를 알 수 없으므로 다른 평가지표가 필요하다.

(2) 신뢰도: 지지율의 경우 기준이 되는 사건(구매)이 전체 집합인 데 비하여 신뢰도는 기준이 되는 사건을 특정 품목을 구매한 것에 한정한다. A->B 신뢰도는 A를 구매하였을 때, 품목 B를 추가로 구매할 확률. 예를 들어 장바구니 수가 150개였고, 빵과 우유가 함께 들어가 있는 장바구니가 30개였다면, -> 우유의 지지율은 30/150 = 20%. 그런데 빵이 들어가 있는 장바구니만 추렸더니 100개였고, 이중 우유가 들어있는 것이 30개였다면, -> 우유의 신뢰도는 30/100 = 30%. 신뢰도가 1에 가까울수록 연관도가 높다고 할 수 있다.

하지만 빵->우유의 신뢰도가 30%였으나, 빵이 없는 장바구니 50개 중 우유가 있는 경우가 15개라면 이 경우에도 신뢰도가 30%, 또 다른 지표가 필요하다.

(3)향상도: 향상도는 A->B의 신뢰도를 독립 가정하에서의 신뢰도로 나눈 것을 의미한다. 우유를 살 확률 p(A) 0.6, 콜라를 살 확률 p(B) 0.5, 동시 구매할 지지율이 0.4, 신뢰도가 0.67일 경우, 향샹도는 신뢰도 / p(B) 0.67/0.5 = 1.34가 된다. 이는 우유 구매 데이터를 안 상태에서 콜라 마케팅을 진행 할 경우, 우유 구매 데이터를 모를 때 보다 그 효과가 34% 증가한다고 알 수 있다.

 

l  A->B 지지율 : 품목 A를 구매한 후 B를 구매한 거래 수 / 전체 거래수

l  A->B 신뢰도 : 품목 A를 구매한 후 B를 구매한 거래 수 / A를 포함한 전체 거래 수

l  A->B 향상도 : A->B 신뢰도 / B를 포함한 거래의 비중

 

3. 연관 분석의 장단점

l  장점: 쉽게 이해할 수 있고, 적용하기도 편하다. 목적변수 없는 직관적인 분석(자율학습 unsupervised learning에 해당)이라는 점도 쉽게 활용할 수 있는 장점. 데이터마이닝에 앞서 탐색 도구로도 유용

l  단점: 품목수의 증가에 따라 계산량이 증가. 유사한 품목은 한 범주로 일반화해야 함. 연속형 변수를 사용해서는 규칙을 찾기 힘들고, 거래가 드문 품목에 대한 정보를 찾기 어렵다.


4. R 실습

1) 데이터 입력

> setwd('c:/Rwork')

> read.table('trains3.txt')

V1

1 癤퓅ilk,bread,butter

2     milk,butter,coke

3    bread,butter,coke

4      milk,coke,ramen

5   bread,butter,ramen

> tr1=read.transactions('trains3.txt',format='basket',sep=',')

> tr1

transactions in sparse format with

6 transactions (rows) and

6 items (columns)

> as(tr1,'data.frame')

items

1 {bread,butter,癤퓅ilk}

2     {butter,coke,milk}

3    {bread,butter,coke}

4      {coke,milk,ramen}

5   {bread,butter,ramen}

6                     {}

as 함수를 통해 tr1이라는 이름으로 저장되어 있는 거래 object를 출력해 볼 수 있다.

 

2) apriori 함수로 연관규칙을 산출.

> rules1=apriori(tr1,parameter=list(supp=0.4,conf=0.4))

Apriori

 

Parameter specification:

confidence minval smax arem  aval originalSupport maxtime support minlen maxlen target   ext

0.4    0.1    1 none FALSE        TRUE      5     0.4     1    10  rules FALSE

 

Algorithmic control:

  filter tree heap memopt load sort verbose

0.1 TRUE TRUE  FALSE TRUE    2    TRUE

 

Absolute minimum support count: 2

 

set item appearances ...[0 item(s)] done [0.00s].

set transactions ...[6 item(s), 6 transaction(s)] done [0.00s].

sorting and recoding items ... [3 item(s)] done [0.00s].

creating transaction tree ... done [0.00s].

checking subsets of size 1 2 done [0.00s].

writing ... [5 rule(s)] done [0.00s].

creating S4 object  ... done [0.00s].

> inspect(rules1)

lhs         rhs      support   confidence lift

[1] {}       => {bread}  0.5000000 0.5000000  1.0

[2] {}       => {coke}   0.5000000 0.5000000  1.0

[3] {}       => {butter} 0.6666667 0.6666667  1.0

[4] {bread}  => {butter} 0.5000000 1.0000000  1.5

[5] {butter} => {bread}  0.5000000 0.7500000  1.5

함수 설명: rules1=apriori(tr1,parameter=list(supp=0.4,conf=0.4)) default값으로는 지지율support 0.1, 신뢰도confidence 0.8. bread

결과 해석: bread -> butter의 지지율은 0.5, 신뢰도는 1.0(100%), 향상율은 1.5(150%). 이는 빵을 구매한 고객은 반드시 버터를 사고 이 사실을 알고 버터에 대한 마케팅을 할 경우 모를 때 보다 그 효과가 25% 증가한다는 것을 의미한다.

 

3) 연관규칙의 시각화

> library(arulesViz)

> plot(rules1)

 

가로축은 지지율, 세로축은 신뢰도, 우측 막대기는 향상도

 


> plot(rules1,'grouped')


 

원의 크기는 지지율, 색상의 진하기는 향상도. LHS는 조건으로 이름 앞의 숫자는 각 규칙의 수를 의미


 

> plot(rules1,'graph')

 

원의 크기는 각 규칙의 지지율을, 색상의 진하기는 향상도를 의미. Butterbread가 상대적으로 중심에 위치하고 있고, 콜라의 경우 떨어져 있다.

 

 

> plot(rules1,'paracoord')

  

품목 간 연간관계를 병렬적으로 확인할 수 있다. 화살표의 두께는 지지율을, 화살표의 색상의 진하기는 향상도를 나타낸다.

 

출처: 데이터마이닝(장영재, 김현중, 조형준 공저)


소감: 데이터 양이 많을 경우 입력이 조금 힘들 것 같지만, A를 사는 고객은 반드시 B를 사니 A를 사지 않은 고객 대상 타켓 마케팅이 가능해보여 적용할 수 있는 분야가 많아 보인다. 그래프도 조금 더 공부는 해야겠지만, 시각화된 내용이 매우 직관적이라 활용도가 많아 보인다.

반응형
Posted by 마르띤
,
반응형

독일 신용평가 데이터를 활용한 신경망 모형. 목표변수 y는 good / bad의 범주형 데이터로 모든 변수를 수치화 한 후 신경망 모형을 


1) 
데이터 입력

> set.seed(1000)

> library(neuralnet)

> library(dummy)

> setwd('c:/Rwork')

> german = read.table('germandata.txt',header = T)

 

추가 공부: dummy화란?

 

2) 데이터 및 타입 변경

> dvar=c(4,9,10,15,17) #명목변수 지정 purpose(a43,a40..), personal,  debtors, housing, job

> german2 = dummy(x=german[,dvar]) #명목변수를 더미변수화

> head(german2,1)

purpose_A40 purpose_A41 purpose_A410 purpose_A42 purpose_A43 purpose_A44 purpose_A45

1           0           0            0           0           1           0           0

purpose_A46 purpose_A48 purpose_A49 personal_A91 personal_A92 personal_A93 personal_A94

1           0           0           0            0            0            1            0

debtors_A101 debtors_A102 debtors_A103 housing_A151 housing_A152 housing_A153 job_A171

1            1            0            0            0            1            0        0

job_A172 job_A173 job_A174

1        0        1        0

> german2 = german2[,-c(10,14,17,20,24)] #더미변수생성

> head(german,1)

check  duration  history  purpose  credit  savings  employment  installment  personal  debtors

1   A11        6     A34      A43    1169      A65        A75           4      A93    A101

Residence  property  age  others  housing  numcredits  job  residpeople  telephone  foreign    y

1        4     A121  67   A143    A152          2 A173           1      A192    A201   good

> german2 = cbind(german[,-dvar],german2) #변수 결함

> str(german2)
'data.frame':   1000 obs. of  40 variables:
 $ check       : Factor w/ 4 levels "A11","A12","A13",..: 1 2 4 1 1 4 4 2 4 2 ...
 $ duration    : int  6 48 12 42 24 36 24 36 12 30 ...
 $ history     : Factor w/ 5 levels "A30","A31","A32",..: 5 3 5 3 4 3 3 3 3 5 ...
 $ credit      : int  1169 5951 2096 7882 4870 9055 2835 6948 3059 5234 ...
 $ savings     : Factor w/ 5 levels "A61","A62","A63",..: 5 1 1 1 1 5 3 1 4 1 ...
 $ employment  : Factor w/ 5 levels "A71","A72","A73",..: 5 3 4 4 3 3 5 3 4 1 ...
 $ installment : int  4 2 2 2 3 2 3 2 2 4 ...
 $ residence   : int  4 2 3 4 4 4 4 2 4 2 ...
 $ property    : Factor w/ 4 levels "A121","A122",..: 1 1 1 2 4 4 2 3 1 3 ...
 $ age         : int  67 22 49 45 53 35 53 35 61 28 ...
 $ others      : Factor w/ 3 levels "A141","A142",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ numcredits  : int  2 1 1 1 2 1 1 1 1 2 ...
 $ residpeople : int  1 1 2 2 2 2 1 1 1 1 ...
 $ telephone   : Factor w/ 2 levels "A191","A192": 2 1 1 1 1 2 1 2 1 1 ...
 $ foreign     : Factor w/ 2 levels "A201","A202": 1 1 1 1 1 1 1 1 1 1 ...
 $ y           : Factor w/ 2 levels "bad","good": 2 1 2 2 1 2 2 2 2 1 ...
 $ purpose_A40 : Factor w/ 2 levels "0","1": 1 1 1 1 2 1 1 1 1 2 ...
 $ purpose_A41 : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 2 1 1 ...
 $ purpose_A410: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ purpose_A42 : Factor w/ 2 levels "0","1": 1 1 1 2 1 1 2 1 1 1 ...
 $ purpose_A43 : Factor w/ 2 levels "0","1": 2 2 1 1 1 1 1 1 2 1 ...
 $ purpose_A44 : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ purpose_A45 : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ purpose_A46 : Factor w/ 2 levels "0","1": 1 1 2 1 1 2 1 1 1 1 ...
 $ purpose_A48 : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ purpose_A49 : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ personal_A91: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 2 1 ...
 $ personal_A92: Factor w/ 2 levels "0","1": 1 2 1 1 1 1 1 1 1 1 ...
 $ personal_A93: Factor w/ 2 levels "0","1": 2 1 2 2 2 2 2 2 1 1 ...
 $ personal_A94: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 2 ...
 $ debtors_A101: Factor w/ 2 levels "0","1": 2 2 2 1 2 2 2 2 2 2 ...
 $ debtors_A102: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ debtors_A103: Factor w/ 2 levels "0","1": 1 1 1 2 1 1 1 1 1 1 ...
 $ housing_A151: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 2 1 1 ...
 $ housing_A152: Factor w/ 2 levels "0","1": 2 2 2 1 1 1 2 1 2 2 ...
 $ housing_A153: Factor w/ 2 levels "0","1": 1 1 1 2 2 2 1 1 1 1 ...
 $ job_A171    : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ job_A172    : Factor w/ 2 levels "0","1": 1 1 2 1 1 2 1 1 2 1 ...
 $ job_A173    : Factor w/ 2 levels "0","1": 2 2 1 2 2 1 2 1 1 1 ...
 $ job_A174    : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 2 1 2 ...

> nrow(german2);ncol(german2)
[1] 1000
[1] 40

> for(i in 1:ncol(german2)) if(!is.numeric(german2[,i])) german2[,i] = as.numeric(german2[,i])#여타 순서가 있는 범주형 변수의 수치형 변수화

> german2$y = ifelse(german$y == 'good',1,0) #목표변수 변환

> head(german$y)

[1] good bad  good good bad  good

Levels: bad good

> head(german2$y)

[1] 1 0 1 1 0 1

 ## 중요 : 신경망에서는 범주형 데이터를 수치화하여 적용한다. ##

 

3) 75% 랜덤 추출

> i = sample(1:nrow(german2),round(0.75*nrow(german2))) #75%랜덤 추출

> length(i)
[1] 750

 

4) 변수의 표준화 과정

> max2 = apply(german2, 2, max)

> min2 = apply(german2, 2, min)

> gdat = scale(german2, center = min2, scale = max2 - min2) # 변수조정(0,1,dummy variable은 변화 없음)

> gdat = as.data.frame(gdat) #데이터 프레임 형태로 변경, 데이터 준비 끝!

> str(gdat)

'data.frame':     1000 obs. of  35 variables:

$ check       : num  0 0.333 1 0 0 ...

$ duration    : num  0.0294 0.6471 0.1176 0.5588 0.2941 ...

$ history     : num  1 0.5 1 0.5 0.75 0.5 0.5 0.5 0.5 1 ...

$ credit      : num  0.0506 0.3137 0.1016 0.4199 0.2542 ...

$ savings     : num  1 0 0 0 0 1 0.5 0 0.75 0 ...

$ employment  : num  1 0.5 0.75 0.75 0.5 0.5 1 0.5 0.75 0 ...

$ installment : num  1 0.333 0.333 0.333 0.667 ...

$ residence   : num  1 0.333 0.667 1 1 ...

$ property    : num  0 0 0 0.333 1 ...

$ age         : num  0.8571 0.0536 0.5357 0.4643 0.6071 ...

$ others      : num  1 1 1 1 1 1 1 1 1 1 ...

$ numcredits  : num  0.333 0 0 0 0.333 ...

$ residpeople : num  0 0 1 1 1 1 0 0 0 0 ...

$ telephone   : num  1 0 0 0 0 1 0 1 0 0 ...

$ foreign     : num  0 0 0 0 0 0 0 0 0 0 ...

$ y           : num  1 0 1 1 0 1 1 1 1 0 ...

$ purpose_A40 : num  0 0 0 0 1 0 0 0 0 1 ...

$ purpose_A41 : num  0 0 0 0 0 0 0 1 0 0 ...

$ purpose_A410: num  0 0 0 0 0 0 0 0 0 0 ...

$ purpose_A42 : num  0 0 0 1 0 0 1 0 0 0 ...

$ purpose_A43 : num  1 1 0 0 0 0 0 0 1 0 ...

$ purpose_A44 : num  0 0 0 0 0 0 0 0 0 0 ...

$ purpose_A45 : num  0 0 0 0 0 0 0 0 0 0 ...

$ purpose_A46 : num  0 0 1 0 0 1 0 0 0 0 ...

$ purpose_A48 : num  0 0 0 0 0 0 0 0 0 0 ...

$ personal_A91: num  0 0 0 0 0 0 0 0 1 0 ...

$ personal_A92: num  0 1 0 0 0 0 0 0 0 0 ...

$ personal_A93: num  1 0 1 1 1 1 1 1 0 0 ...

$ debtors_A101: num  1 1 1 0 1 1 1 1 1 1 ...

$ debtors_A102: num  0 0 0 0 0 0 0 0 0 0 ...

$ housing_A151: num  0 0 0 0 0 0 0 1 0 0 ...

$ housing_A152: num  1 1 1 0 0 0 1 0 1 1 ...

$ job_A171    : num  0 0 0 0 0 0 0 0 0 0 ...

$ job_A172    : num  0 0 1 0 0 1 0 0 1 0 ...

$ job_A173    : num  1 1 0 1 1 0 1 0 0 0 ...


5)
신경망 모델 구축 및 신경망 그래프 그리기

> train = gdat[i,] #학습샘플과 테스트 샘플 추출

> test = gdat[-i,]

> gn = names(german2)

> gn

[1] "check"        "duration"     "history"      "credit"       "savings"      "employment" 

[7] "installment"  "residence"    "property"     "age"          "others"       "numcredits" 

[13] "residpeople"  "telephone"    "foreign"      "y"            "purpose_A40"  "purpose_A41"

[19] "purpose_A410" "purpose_A42"  "purpose_A43"  "purpose_A44"  "purpose_A45"  "purpose_A46"

[25] "purpose_A48"  "personal_A91" "personal_A92" "personal_A93" "debtors_A101" "debtors_A102"

[31] "housing_A151" "housing_A152" "job_A171"     "job_A172"     "job_A173"   

> f = as.formula(paste('y~',paste(gn[!gn %in% 'y'],collapse = '+')))

> f

y ~ check + duration + history + credit + savings + employment +

 installment + residence + property + age + others + numcredits +

   residpeople + telephone + foreign + purpose_A40 + purpose_A41 +

   purpose_A410 + purpose_A42 + purpose_A43 + purpose_A44 +

 purpose_A45 + purpose_A46 + purpose_A48 + personal_A91 +

   personal_A92 + personal_A93 + debtors_A101 + debtors_A102 +

   housing_A151 + housing_A152 + job_A171 + job_A172 + job_A173

> nn1 = neuralnet(f,data=train,hidden=c(3,2),linear.output=F) #은닉층은 2, 첫번째 노드는 3, 두번째 노드는 2. 분류의 경우 linear.output = F

> summary(nn1)

Length Class      Mode   

call                   5  -none-     call   

response            750  -none-     numeric

covariate          25500  -none-     numeric

model.list              2  -none-     list   

err.fct                 1  -none-     function

act.fct                 1  -none-     function

linear.output           1  -none-     logical

data                  35  data.frame list   

net.result              1  -none-     list   

weights                1  -none-     list   

startweights            1  -none-     list   

generalized.weights     1  -none-     list   

result.matrix         119  -none-     numeric

> plot(nn1)


은닉층이 2개인 신경망 모형의 그래프가 완성된다. 이 그래프에 대한 해석을 좀 더 공부하고 싶은데 아직은 잘 모르겠음.

 

6) 모형 추정:

> dim(german2)[1]
[1] 1000

> dim(german2)[2]
[1] 35

> colnames(test)[16]
[1] "y"

> pred.nn0 = compute(nn1,train[,c(1:15,17:dim(german2)[2])]) #학습데이터의 실제값과 예측값 비교, 16번째 열의 값은 y

 

함수 설명:

> pred.nn0 = compute(nn1,train[,c(1:15,17:dim(german2)[2])]) 16번째 변수가 y 목표변수. compute 작성된 신경망모형을 이용하여 새로운 예에 적용하여 결과를 도출. nn1 새롭게 예측에 적용할 자료, train[,c(1:15,17:dim(german2)[2])]는 신경망모형으로적합한 결과 오브젝트

 

7) 학습샘플의 실제값과 예측값을 비교해보자.

> head(cbind(german2[1,16],round(pred.nn0$net.result,10)))

[,1]         [,2]

931    1 0.7786470581

546    1 0.0000005387

56     1 0.8208161458

883    1 0.9999999722

11     1 0.0000004232

354    1 0.0000046419

#왼쪽이 실제값, 오른쪽이 예측값. 분류의 문제이므로 값은 0 1사이. 0.5 cut off값을 둘 수 있다. 또는 0.3미만 폐기, 0.7이하 보류, 0.7 초과만 사용한느 cutoff도 가능. 왼쪽이 실제 값, 오른쪽이 학습된 데이터. 4번째 행은 실제 1의 값을 1에 가깝게 예측하였고, 5번째 행은 실제 1이지만 0에 가깝게 예측한 사례. german2[,16] 16번째 열 즉 y값임을 알겠는데 german2[1,16]은 뭘까궁금

 

8) 예측 정확도 평가

> pred.nn1 = compute(nn1,test[,c(1:15,17:dim(german2)[2])]) #test data를 바탕으로 판단해보자

> pred.nn2 = ifelse(pred.nn1$net.result>0.5,1,0) #0.5를 경계로 1 0 분류

> head(cbind(german2[-i,16],pred.nn2)) #테스트 샘플의 실제값과 예측값

[,1]  [,2]

3     1    1

12    1    0

13    1    1

14    1    0

15    0    1

26    1    1

> sum(german2[-i,16]!=pred.nn2) / length(german2[-i,16])

[1] 0.404

> sum(german2[-i,16]!=pred.nn2)

[1] 101

> length(german2[-i,16])

[1] 250

#테스트 샘플의 오분류율, 16번째 값은 목표변수, sum(german2[-i,16]!=pred.nn2) pred.nn2와 같지 않은  값을 전체길이 length(german2[-i,16])로 나눔. i를 빼고 16번째 컬럼을 사용

 

> library(nnet)

> nnet1 =nnet(f,data=train,size = 3, linout = F)

# weights:  109

initial  value 181.570914

iter  10 value 113.679457

iter  20 value 96.943318

iter  30 value 82.803121

iter  40 value 73.239858

iter  50 value 70.807278

iter  60 value 69.865795

iter  70 value 69.476434

iter  80 value 69.158350

iter  90 value 69.039026

iter 100 value 68.929899

final  value 68.929899

stopped after 100 iterations

> pred.nnet1 = predict(nnet1,test[,c(1:15,17:dim(german2)[2])])

> pred.nnet2 = ifelse(pred.nnet1>0.5,1,0)

> head(cbind(german2[-i,16],pred.nnet2)) #테스트 샘플의 실제값과 예측값

[,1] [,2]

3     1    1

12    1    0

13    1    1

14    1    1

15    0    0

26    1    1

> sum(german2[-i,16]!=pred.nnet2) / length(german2[-i,16]) #테스트 샘플 예측의 오분류율

[1] 0.408

이 부분은 교재에 별도 설명이 없어서 추가 공부가 필요함.

 

소감: 알파고 딥마이닝으로 인해 관심을 가지게 된 신경망 모형. 이론 공부도 해보고 R도 따라해보니 약 50%정도 이해된 상태. 궁금한 점은 아래와 같음.

1. 명복 변수 중 더미화 하지 않은 것들도 있음.

-> 교수님 답변: 해당 신경망 모형에서는 숫자로 입력되어 있는 범주형 변수들은 수치변수로 그대로 사용하고 표준화만 하였습니다. 순서가 있는 범주형 변수라고 판단한 변수였습니다. 해당 변수들을 제외하고 나머지 변수들은 변환이 필요하여 dummy 함수를 사용하였습니다.

 

2. 위 신경망 plot 그래프가 무엇을 의미하는지 더 자세히 해석할 능력이 필요함.

-> 교수님 답변: 신경망 모형의 해석은 그림을 보고 해석하기가 상당히 힘듭니다. 워낙 복잡한 함수의 결합이기 때문입니다. 다만, 화살표 위의 수치(절대값)를 보고 연결강도가 강한지 여부를 판단할 수 있습니다. 신경망 모형의 태생적인 한계점인 것 같습니다.

참고로 교재에서 사용하였던 neuralnet 패키지는 plot을 제공합니다. 과거 강의에서 사용했던 다른 패키지는 직접적으로 plot을 산출할 수는 없었습니다.

 

3. 위 신경망 모형을 통해서 오분류율은 40.4%로 나왔는데 너무 높은건 아닌지 생각됨.

 -> 교수님 답변: 오분율은 상대적인 것이긴 하지만 높은 수준으로 보입니다. 교재에서는 예측 판별 기준으로 0.5라는 값을 사용했는데 실제 실무에서는 이 값을 적절하게 변경시키면서 오분류율을 낮추어주는 것이 좋을 것입니다. 예를 들어 원 데이터가 1 0에 비해 많이 분포되어 있다면, 0.5보다 큰 값을 기준치로 삼는 편이 좋습니다.

 

4. 은닉층의 개수는 어떻게 설정하면 좋을지?

 

5. 가장 궁금한 것은 이 모델을 어떻게 실무에 적용할지 잘 모르겠음. 위 독일 신용평가 데이터로 신경망 모형을 만들고 오분류율도 체크하고, 실제값과 예측값도 비교하였는데, 이것이 의미하는 것들. 가령, 변수의 중요성, 그래서 신용도가 좋은 경우는 어느 경우이고, 또 다른 고객 데이터 셋이 있는데 이 새로운 셋에서 어떤 고객, 변수, 값들을 추출해야 우리가 원하는 우수한 고객을 알아낼 수 있는지, 실제 비즈니스 적용 포인트가 가장 궁금함.

 -> 교수님 답변: 실무에서 신경망 모형을 잘 해석하여 활용하기는 어려운 것이 사실입니다. 다만, 상대적으로 예측력이 높다는 점을 살려 새로운 데이터의 모든 변수들을 활용하여 1이나 0값을 예측하거나 목표변수의 값 자체를 산출하는 목적으로는 유용성이 높다고 봅니다. 변수의 해석이나 변수의 선택보다는 예측이나 분류 자체의 목적으로 사용하기에 적합하다고 보시면 좋습니다.

 

6. 전반적으로 의사결정나무, 앙상블 모형에 이어 데이터 마이닝 분야에 깊은 관심을 가지게 되는 좋은 계기.

 

출처: 데이터마이닝(장영재, 김현중, 조형준 공저)

반응형
Posted by 마르띤
,
반응형

보스턴 하우징을 이용한 신경망 모형. 목표변수를 주택가격의 중간값인 medv(연속형 변수)로 하고, 나머지 변수를 입력변수로 하는 신경망 모형



1) 데이터 입력

> set.seed(100)

> library(MASS)

> library(neuralnet)

> bdat = Boston

> str(bdat)

'data.frame':     506 obs. of  15 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 ...

$ medv.hat: num  23.7 23.7 32.4 32.4 32.4 ...

 

주의: chas, rad를 수치형 변수로 바꾸고, 앙상블 모형에서 사용하였던 medv.hat 변수를 삭제해야 한다.

신경망에서는 범주형 데이터를 수치화하여 적용한다. 대체로 순서가 의미 있는 범부형 데이터는 수치 전환을 한 뒤 표준화하여 사용하게 된다.

 



2) 데이터 및 타입 변경

> bdat<-bdat[,-15]

> bdat$chas = as.numeric(bdat$chas)

> bdat$rad = as.numeric(bdat$rad)

> class(bdat$chas);class(bdat$rad)

[1] "numeric"

[1] "numeric"

 



3) 50% 랜덤 추출

> i = sample(1:nrow(bdat), round(0.5*nrow(bdat)))

> max1 = apply(bdat, 2, max)

> min1 = apply(bdat, 2, min)

결과 해석 : 50% 랜덤 추출하여 훈련데이터(train) 저장하고 나머지는 검증 데이터(test) 저장

 


 

4) 변수의 표준화 과정

> sdat = scale(bdat,center=min1,scale = max1 - min1) 

> sdat = as.data.frame(sdat)

> head(sdat,3)

crim          zn               indus  chas                nox                   rm                 age                   dis   rad

1 0.0000000000000 0.18 0.06781524927    0 0.3148148148 0.5775052692 0.6416065911 0.2692031391 0.000

2 0.0002359225392 0.00 0.24230205279    0 0.1728395062 0.5479977007 0.7826982492 0.3489619802 0.125

3 0.0002356977440 0.00 0.24230205279    0 0.1728395062 0.6943858977 0.5993820803 0.3489619802 0.125

tax               ptratio              black                     lstat         medv

1 0.2080152672 0.2872340426 1.0000000000 0.08967991170 0.4222222222

2 0.1049618321 0.5531914894 1.0000000000 0.20447019868 0.3688888889

3 0.1049618321 0.5531914894 0.9897372535 0.06346578366 0.6600000000

 

함수 설명:

> sdat = scale(bdat,center=min1,scale = max1 - min1) 신경망에서 사용하는 수치형 변수를 0과 1사이의 값으로 바꾸어 주기 위한 단계. 위의 [0-1] 변환은 (x-min(x) /(max(x)-min(x)) 수식으로도 계산 가능. 

 

 



5) 신경망 모델 구축 및 신경망 그래프 그리기

> train = sdat[i,] #학습, 훈련샘플 training

> test = sdat[-i,] #테스트샘플 test


> n = names(train) ;n

[1] "crim"    "zn"      "indus"   "chas"    "nox"     "rm"      "age"     "dis"     "rad"     "tax"   

[11] "ptratio" "black"   "lstat"   "medv"  

> form = as.formula(paste('medv~',paste(n[!n %in% 'medv'],collapse = '+'))) 

> form

medv ~ crim + zn + indus + chas + nox + rm + age + dis + rad + tax + ptratio + black + lstat


> nn1 = neuralnet(form,data=train,hidden = c(5,3),linear.output = T)

> summary(nn1) #작성된 신경망모형의 오브젝트의 구조를 정리

                       Length    Class     Mode   

call                          5   -none-     call   

response              253   -none-     numeric

covariate            3289   -none-     numeric

model.list                 2   -none-     list   

err.fct                      1   -none-     function

act.fct                      1   -none-     function

linear.output             1   -none-     logical

data                       14   data.frame list   

net.result                 1   -none-     list   

weights                    1   -none-     list   

startweights              1   -none-     list   

generalized.weights   1   -none-     list   

result.matrix            95   -none-     numeric

함수 설명

> form = as.formula(paste('medv~',paste(n[!n %in% 'medv'],collapse = '+'))) #종속변수는 mdev, 나머지는 독립변수이다 틸다 ~. 와같은 개념. 종속변수를 제외한 n 안에 있는 모든 변수명을 집어넣고 더하여라. 변수명을 직접 입력해도 된다.

실제 form라고 입력하면 medv ~ crim + zn + indus + chas + nox + rm + age + dis + rad + tax + ptratio + black + lstat

 

라는 공식을 있다.

 

> nn1 = neuralnet(form,data=train,hidden = c(5,3),linear.output = T) #은닉층은 2개이고 처음은 5 나머지는 3, 회귀의 문제이므로 linear.output = T

 

 

질문: 은닉층의 수는 어떻게 결정할까? 통상 은닉층의 마디수는 입력층의 마디수 두배를 넘지 않도록 해야한다. 위에서는 c(5,3)으로 결정하였는데 수치가 최적의 수치인지는 어떻게 알까? 공부가 필요하다.


 

<참고> 딥러닝(deep learning)

기계학습 기법 하나로 신경망모형으로부터 비롯된 딥러닝(deep learning) 기본적으로 은닉층이 많이 쌓여 가면서 복잡하고 깊은 구조로 발전하면서 deep 이라는 이름이 붙여졌다. 입력변수와 출력변수 복잡한 관계를 가중치를 통해 조정할 있는 구조와 매커니즘을 갖고 있다.

 

 

> plot(nn1)

 

 

로 표시된 것이 상수항에 해당하고 가중치는 각각의 화살 표 위에 출력된다. 해석 공부도 더 필요하다.

처음 은닉층은 5개, 두번째 은닉층은 3개를 가지고 있는 신경망 모형이 완성된다. 




6) 모형 추정: 위의 그래프는 50%의 training data만을 사용하였으니 나머지 50% test data를 쓰자.

> pred.nn0 = compute(nn1,test[,1:13])

> summary(pred.nn0)
               Length Class  Mode  
    neurons      3    -none- list  
    net.result 253    -none- numeric

> names(pred.nn0)

[1] "neurons"    "net.result"

> pred0 = pred.nn0$net.result*(max(bdat$medv)-min(bdat$medv))+min(bdat$medv)

> head(pred0)
                    [,1]    
    1 26.55282080
    2 25.15912380
    3 34.48852794
    4 31.56317828
    5 32.47594868
    6 25.32302689

 

함수 설명:

> pred.nn0 = compute(nn1,test[,1:13])

14번째 변수가 medv 목표변수.compute 작성된 신경망모형을 이용하여 새로운 예에 적용하여 결과를 도출. nn1 새롭게 예측에 적용할 자료, test[,1:13] 신경망모형으로 적합한 결과 오브젝트

> pred0 = pred.nn0$net.result*(max(bdat$medv)-min(bdat$medv))+min(bdat$medv)

표변수를 조정전 변수값으로 환원하는 과정. 원래 가지고 있는 값으로 환원. 역변환 과정

 

 


7) 학습샘플의 실제값과 예측값 (적합값) 비교해보자. 


아래 함수 head(cbind(bdat[i,14],pred0),50)에서 cbind(bdat[i,14], 실제값(training data)과 예측값(neural network)으로 구성된 행렬을 구성하는 함수

> head(cbind(bdat[i,14],pred0),50)

[,1]        [,2]

1  15.6 28.12482177

2  19.2 21.36405177

3  29.1 34.24035993

4  18.4 35.88436410

6  24.0 25.77273199

9  22.2 14.86779840

12 11.9 20.82595913

14 26.4 20.03825755

18 24.4 17.19077183

19 23.9 18.69934412

21 20.3 14.85391889

23  9.6 15.77921679

24 13.3 14.95074253

25 33.3 16.19467498

26 15.0 15.53094329

27 19.3 16.50978025

28 27.5 15.65043905

30 22.6 19.71434020

33 29.4 13.39051466

34 19.5 15.45310394

36 33.8 22.75202201

41 31.2 37.96410157

42 21.2 36.32787047

43 19.9 27.29495234

44 42.3 26.74797523

46 24.8 20.55343697

47 50.0 19.83522653

48 20.8 16.66204238

50 48.8 16.90901569

51 23.0 19.54979162

54 41.7 22.43724314

55 17.1 16.69040814

57 25.0 22.91956778

58 15.2 29.22726384

62  8.1 18.82030494

63  8.8 24.13500281

66 19.7 25.15181243

67 28.6 20.16650282

68 20.2 20.27218139

69 18.7 17.48036500

70 17.0 19.87776814

71 12.1 25.21432307

73 25.0 23.16314867

75 12.3 23.82067236

77 23.9 20.42068536

79 37.6 20.09872166

80 22.7 20.89416546

81  5.0 28.30078783

84 28.4 22.10685814

87 14.0 21.30514814

결과 해석: 26번째 행의 값처럼 유사한 결과값도 있지만 81 결과 값처럼 서로 차이가 나는 경우도 있다.

 


 

8) 예측 정확도 평가: 모형 추정에 사용되지 않은 test data를 가지고 예측을 해보자. 

> pred.nn1 = compute(nn1,test[,1:13]) #목표 변수 제외, 새로운 변수로 간주하고 nn1에 적용

> pred1 = pred.nn1$net.result*(max(bdat$medv)-min(bdat$medv))+min(bdat$medv) #목표변수를 조정전 변수값으로 환원. 역변환


> head(cbind(bdat[-i,14],pred1),50) #좌측은 test 변수의 실제값, 우측은 예측값(적합값), 14번째 값은 목표변수, pred1 예측값(적합값)

[,1]        [,2]

1  24.0 28.12482177

2  21.6 21.36405177

3  34.7 34.24035993

4  33.4 35.88436410

6  28.7 25.77273199

9  16.5 14.86779840

12 18.9 20.82595913

14 20.4 20.03825755

18 17.5 17.19077183

19 20.2 18.69934412

21 13.6 14.85391889

23 15.2 15.77921679

24 14.5 14.95074253

25 15.6 16.19467498

26 13.9 15.53094329

27 16.6 16.50978025

28 14.8 15.65043905

30 21.0 19.71434020

33 13.2 13.39051466

34 13.1 15.45310394

36 18.9 22.75202201

41 34.9 37.96410157

42 26.6 36.32787047

43 25.3 27.29495234

44 24.7 26.74797523

46 19.3 20.55343697

47 20.0 19.83522653

48 16.6 16.66204238

50 19.4 16.90901569

51 19.7 19.54979162

54 23.4 22.43724314

55 18.9 16.69040814

57 24.7 22.91956778

58 31.6 29.22726384

62 16.0 18.82030494

63 22.2 24.13500281

66 23.5 25.15181243

67 19.4 20.16650282

68 22.0 20.27218139

69 17.4 17.48036500

70 20.9 19.87776814

71 24.2 25.21432307

73 22.8 23.16314867

75 24.1 23.82067236

77 20.0 20.42068536

79 21.2 20.09872166

80 20.3 20.89416546

81 28.0 28.30078783

84 22.9 22.10685814

87 22.5 21.30514814

 

결과해석: 전반적으로 실제값과 예측값이 서로 유사함을 알 수 있다.


 

> head(cbind(bdat[-i,14],pred1)[,1],10)
   1    2    3    4    5    6    7    8    9   10
24.0 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9
> head(cbind(bdat[-i,14],pred1),10)
   [,1]        [,2]
1  24.0 26.55282080
2  21.6 25.15912380
3  34.7 34.48852794
4  33.4 31.56317828
5  36.2 32.47594868
6  28.7 25.32302689
7  22.9 19.92609580
8  27.1 20.01203029
9  16.5 19.68915018
10 18.9 19.33552651
> obs <-cbind(bdat[-i,14],pred1)[,1]
> pdt <-cbind(bdat[-i,14],pred1)[,2]

> out = cbind(obs,pdt)
> head(out)
   obs         pdt
1 24.0 26.55282080
2 21.6 25.15912380
3 34.7 34.48852794
4 33.4 31.56317828
5 36.2 32.47594868
6 28.7 25.32302689
> which.min(abs(out$obs - out$pdt))
Error in out$obs : $ operator is invalid for atomic vectors
> out = as.data.frame(out)
> which.min(abs(out$obs - out$pdt))
[1] 43
> out[43,]
    obs         pdt
43 25.3 25.31048382
> which.max(abs(out$obs - out$pdt))
[1] 197
> out[197,]
    obs         pdt
372  50 18.13253008

 

 



9) PMSE: 예측된 값이 얼마나 잘 예측되었을까? 

> PMSE = sum((bdat[-i,14] - pred1)^2) / nrow(test)

> PMSE

[1] 18.70948041

함수 설명:

> PMSE = sum((bdat[-i,14] - pred1)^2) / nrow(test) #예측 평균 제곱 오차, 

 

 결과 해석: 예측평균제곱오차는 18.7



출처: 데이터마이닝(장영재, 김현중, 조형준 공저)

반응형
Posted by 마르띤
,
반응형

회귀앙상블 - 랜덤포레스트(링크) 

분류앙상블 – 배깅, 부스팅, 랜덤 포레스트



 1) 데이터 입력

> setwd('c:/Rwork')

> german=read.table('germandata.txt',header=T)

> german$numcredits = factor(german$numcredits)

> german$residence = factor(german$residence)

> german$residpeople = factor(german$residpeople)

 

 

2) 랜덤 포레스트 방법의 실행

> library(randomForest)

> rf.german<-randomForest(y~.,data=german,ntree=100,mytr=5,importance=T,na.action = na.omit)

> summary(rf.german)

Length Class  Mode    

call               7   -none- call    

type               1   -none- character

predicted       1000   factor numeric 

err.rate         300   -none- numeric 

confusion          6   -none- numeric 

votes           2000   matrix numeric 

oob.times       1000   -none- numeric 

classes            2   -none- character

importance        80   -none- numeric 

importanceSD      60   -none- numeric 

localImportance    0   -none- NULL    

proximity          0   -none- NULL    

ntree              1   -none- numeric 

mtry               1   -none- numeric 

forest            14   -none- list    

y               1000   factor numeric 

test               0   -none- NULL    

inbag              0   -none- NULL    

terms              3   terms  call

 

함수 설명:

randomForest(y~.,data=german,ntree=100,mytr=5,importance=T,na.action = na.omit ntree는 랜덤포레스트 방법에서 생성하게 될 분류기의 개수 B이다. 100개의 분류기는 분류나무를 이용하여 생성되도록 하였다. mtry는 중간노드마다 랜덤하게 선택되는 변수의 개수를 의미한다. 5개의 입력변수를 중간노드마다 모두 선택하고 이 중에서 분할점이 탐색되도록 하였다. importance는 변수의 중요도를 계산하는 옵션이고, na.action은 결측치를 처리하는 방법에 대한 사항이다. 변수의 중요도를 계산하게 하고, 결측치는 필요한 경우에만 삭제되도락 하였다.

 

 

2) 랜덤 포레스트 방법의 실행 변수 중요도 보기

> names(rf.german)

[1] "call"            "type"            "predicted"       "err.rate"        "confusion"     

[6] "votes"           "oob.times"       "classes"         "importance"      "importanceSD"  

[11] "localImportance" "proximity"       "ntree"           "mtry"            "forest"        

[16] "y"               "test"            "inbag"           "terms"         


> head(rf.german$predicted,10) #데이터의 예측집단을 출력

1    2    3    4    5    6    7    8    9   10

good  bad good  bad  bad good good good good  bad

Levels: bad good


> importance(rf.german)

bad        good MeanDecreaseAccuracy MeanDecreaseGini

check       13.14268724  9.63208341         15.1106884       44.838547

duration     3.33563217  8.10482760          8.6281030        41.273512

history      3.87863720  4.50449203            5.8685013        25.731461

purpose      2.67025503  3.09871408            4.1078354        35.651165

credit       2.44312087  4.16670498            4.8800356        52.641745

savings      7.48182326  2.84190645            6.1879918        21.705909

employment   1.91049595  2.70568977            3.2416484        23.910509

installment  0.02100147  3.49542966            3.0522029        15.714499

personal     2.10802207  1.83819513            3.0602966        15.365475

debtors     -0.17277289  4.39384503            3.8963219         6.718969

residence    0.74571096  0.90284661            1.1155901        17.932937

property     2.16016716  3.85454658            4.7010080        18.803008

age          2.69637542  4.35170316            5.4753585        41.451103

others       0.31569112  3.60499256            3.3679530        11.399935

housing      2.70314243  2.06074416            3.0737900         9.596539

numcredits  -0.24996827  0.95259106            0.6502566         7.944861

job          1.53070574  1.18486660            2.1420488        12.054190

residpeople  0.88657814 -0.43166449            0.1370845         4.234831

telephone    1.46824003 -0.24200291            0.6110284         6.427048

foreign      1.26297478 -0.05431566            0.5733125         1.519832


> importance(rf.german,type=1)

MeanDecreaseAccuracy

check                 15.1106884

duration               8.6281030

history                5.8685013

purpose                4.1078354

credit                 4.8800356

savings                6.1879918

employment             3.2416484

installment            3.0522029

personal               3.0602966

debtors                3.8963219

residence              1.1155901

property               4.7010080

age                    5.4753585

others                 3.3679530

housing                3.0737900

numcredits             0.6502566

job                    2.1420488

residpeople            0.1370845

telephone              0.6110284

foreign                0.5733125

 

결과 해석: check 가장 중요하고 duration 두번째로 중요

 


> order(importance(rf.german)[,'MeanDecreaseAccuracy'],decreasing = T)

[1]  1  2  6  3 13  5 12  4 10 14  7 15  9  8 17 11 16 19 20 18


> which.max(importance(rf.german)[,'MeanDecreaseAccuracy'])

check

1


> importance(rf.german)[which.max(importance(rf.german)[,'MeanDecreaseAccuracy']),]

bad                 good     MeanDecreaseAccuracy     MeanDecreaseGini

13.142687             9.632083                15.110688            44.838547


> rf.german$importance

bad          good MeanDecreaseAccuracy MeanDecreaseGini

check        5.728619e-02  2.488337e-02         3.461467e-02        44.838547

duration     1.262784e-02  2.059686e-02         1.820397e-02        41.273512

history      1.165482e-02  8.234252e-03         9.100115e-03        25.731461

purpose      1.001589e-02  5.877403e-03         7.125436e-03        35.651165

credit       8.957835e-03  9.469900e-03         9.342178e-03        52.641745

savings      1.873739e-02  4.572813e-03         8.795598e-03        21.705909

employment   5.377379e-03  4.391391e-03         4.647218e-03        23.910509

installment  5.083834e-05  4.492258e-03         3.109720e-03        15.714499

personal     4.903039e-03  2.481005e-03         3.188303e-03        15.365475

debtors     -1.380717e-04  3.198633e-03         2.202015e-03         6.718969

residence    1.833243e-03  1.342976e-03         1.400876e-03        17.932937

property     6.357328e-03  5.871030e-03         6.069060e-03        18.803008

age          1.026633e-02  8.969565e-03         9.239623e-03        41.451103

others       6.321219e-04  4.940365e-03         3.562374e-03        11.399935

housing      5.086080e-03  2.740150e-03         3.358222e-03         9.596539

numcredits  -3.793953e-04  7.653351e-04         4.256327e-04         7.944861

job          2.803065e-03  1.422938e-03         1.912685e-03        12.054190

residpeople  9.171770e-04 -2.801992e-04         7.286163e-05         4.234831

telephone    2.173474e-03 -2.108124e-04         4.743466e-04         6.427048

foreign      5.935867e-04 -1.538455e-05         1.547421e-04         1.519832

 

추가:

rf.german$importance #이건 importance(rf.german) 결과와 어떻게 다른지 공부하자

 

 

3) 분류예측치를 구하고 정확도 평가

> pred.rf.german<-predict(rf.german,newdata=german)

> head(pred.rf.german,10)

  1    2    3    4    5    6    7    8    9   10

  good  bad good good  bad good good good good  bad

  Levels: bad good

> tab=table(german$y,pred.rf.german,dnn=c("Actual","Predicted"))

> tab

        Predicted

  Actual  bad   good

    bad    300    0

 good   0      700

> addmargins(tab)

  Predicted

  Actual  bad  good  Sum

  bad   300   0      300

  good  0      700  700

  Sum   300  700  1000

> 1-sum(diag(tab)/sum(tab))

  [1] 0

결과 해석기존 cart 분류나무 모형 오분류율은 19.6%, 배깅은 3.7%, 부스팅은 22.6%인 반면

랜덤 포레스트는 오분류율이 0%로 기존 그 어떠한 모형보다 대단히 우수한 결과를 보임을 알 수 있다


(분류나무 cart 모형 바로가기) / (분류앙상블 모형 – 배깅 바로가기) / (분류앙상블 모형 – 부스팅 바로가기)

 

4) 몇개의 분류기가 적당할까?

> plot(rf.german,'l')


 

결과 해석: x축은 분류기, y축은 OOB 오분류율을 의미 out of bag 약자로 부트스트랩에 포함되지 못한 데이터를 의미. 결국 oob데이터는 검증데이터의 역할과 비슷. 가장 아래선은 목표변수 good 대한 오분율, 가장 위쪽 선은 bad 대한 오분율. 가운데는 전체 오분류율. 그림에 따르면 분류기 개수가 80개이상이면 안정적인 형태로 보인다

 

 

5) 훈련 데이터와 검증데이터로 분할하여 랜덤포레스트 방법을 평가해 보자.

> set.seed(1234)

> i=sample(1:nrow(german),round(nrow(german)*0.7)) #70% for training 훈련 data, 30% test 검증 데이

> german.train = german[i,]

> german.test = german[-i,]

> rf.train.german<-randomForest(y~.,data=german.train,ntree=100,mtry=5,importance=T,na.action = na.omit)

> pred.rf.train.german<-predict(rf.train.german,newdata=german.test)

> tab.train = table(german.test$y,pred.rf.train.german,dnn=c('Actual','Predicted'))

> tab.train

Predicted

Actual bad good

bad   39   60

good  15  186


addmargins(tab.train)

Predicted

Actual   bad  good Sum

bad    39   60     99

good  15   186    201

Sum   54   246    300


> 1-sum(diag(tab.train) / sum(tab.train)) #오분류율이 25% 어느정도 향상

[1] 0.25

 

결과 해석: 검증데이터에 대한 오분류율은 기존 cart 분류 나무는 26.3%, 배깅 역시 26.3%, 부스팅은 25.3%. 랜덤포레스트는 25%, 조금 향상 되었음.


(분류나무 cart 모형 바로가기) / (분류앙상블 모형 – 배깅 바로가기) / (분류앙상블 모형 부스팅 바로가기)


출처: 데이터마이닝(장영재, 김현중, 조형준 공저) 

반응형
Posted by 마르띤
,
반응형

회귀앙상블 - 랜덤포레스트(링크)

분류앙상블 배깅, 부스팅, 랜덤 포레스트


 


부스팅 방법 실행


1) 데이터 입력

> setwd('c:/Rwork')

> german=read.table('germandata.txt',header=T)

> german$numcredits = factor(german$numcredits)

> german$residence = factor(german$residence)

> german$residpeople = factor(german$residpeople)

 


2) 부스팅 방법의 실행

> library(rpart)

> library(adabag)

> my.control<-rpart.control(xval=0,cp=0,maxdepth=1)

> boo.german<-boosting(y~.,data=german,boos=T,mfianl=100,control=my.control)

> names(boo.german)

[1] "formula"    "trees"      "weights"    "votes"      "prob"       "class"      "importance"

[8] "terms"      "call"     

> summary(boo.german)

Length Class   Mode     

formula       3   formula call    

trees       100   -none-  list    

weights     100   -none-  numeric 

votes      2000   -none-  numeric 

prob       2000   -none-  numeric 

class      1000   -none-  character

importance   20   -none-  numeric 

terms         3   terms   call    

call          6   -none-  call

함수 설명: boos=T, 표본추출에 의한 분류기 생성방식, boos=F 가중치를 반영한 분류기 생성방식 사용, mfinal 부스팅 방법에서 생성하게 분류기의 개수 B, 100개의 분류기는 rpart 분류나무를 이용하여 생성되고, 분류나무는 최대분할의 깊이는 1이라는 옵션, 100개의 스텀프나무를 생성

 


2) 부스팅 방법의 실행 - 중요도 보기 

> names(boo.german)

[1] "formula"    "trees"      "weights"    "votes"      "prob"       "class"      "importance"

[8] "terms"      "call"

> head(boo.german$trees) #B=100개의 분류나무를 출력할 있다

[[1]]

n= 1000

 

node), split, n, loss, yval, (yprob)

* denotes terminal node

 

1) root 1000 294 good (0.2940000 0.7060000) 

2) check=A11 275 119 bad (0.5672727 0.4327273) *

3) check=A12,A13,A14 725 138 good (0.1903448 0.8096552) *

 

중간 생략

 

> boo.german$importance

age        check       credit      debtors     duration   employment      foreign

5.757338101 40.631489260  5.445313887  0.008302238 15.889422076  2.918286176  0.133462457

history      housing  installment          job   numcredits       others     personal

12.835057694  0.312497120  1.276787836  0.000000000  0.000000000  1.941629429  1.282855587

property      purpose    residence  residpeople      savings    telephone

1.402513542  6.311856846  0.537511661  0.000000000  3.315676089  0.000000000

> order(boo.german$importance,decreasing=T) #bagging 방법과 중요도 변수가 조금은 다르네.

[1]  2  5  8 16  1  3 19  6 13 15 14 10 17  9  7  4 11 12 18 20

> boo.german$importance[2]

check

40.63149

> which.max(boo.german$importance)

check

2

> boo.german$importance[which.max(boo.german$importance)]

check

40.63149

> boo.german$importance[5]

duration

15.88942

> boo.german$importance[8]

history

12.83506

> importanceplot(boo.german)

 

 


3) 목표변수의 분류예측치를 구하고 정확도에 대해 평가해보자.

> pred.boo.german<-predict.boosting(boo.german,newdata=german)

> names(pred.boo.german)

  [1] "formula"   "votes"     "prob"      "class"     "confusion" "error"   

> head(pred.boo.german$prob,10) 

  [,1]      [,2]

  [1,] 0.28278825 0.7172117

  [2,] 0.56170615 0.4382938

  [3,] 0.17385176 0.8261482

  [4,] 0.50498723 0.4950128

  [5,] 0.52209125 0.4779088

  [6,] 0.35035420 0.6496458

  [7,] 0.18447524 0.8155248

  [8,] 0.44024939 0.5597506

  [9,] 0.08690876 0.9130912

  [10,] 0.48660566 0.5133943

> pred.boo.german$confusion

  Observed Class

  Predicted Class bad good

           bad  135   61

           good 165  639

  > addmargins(pred.boo.german$confusion)

                 Observed Class

  Predicted Class  bad good  Sum

           bad   135   61  196

           good  165  639  804

  Sum  300  700 1000

  > 1-sum(diag(pred.boo.german$confusion)) / sum(pred.boo.german$confusion)

  [1] 0.226

 

 

함수 설명: > head(pred.boo.german$prob,10) #prob벡터는 부스팅 방법에서 집단별 투표비율을 출력


결과 해석: 기존 cart 분류나무 모형 오분류율은 19.6%, 배깅은 3.7%, 

부스팅의 오분류율은 22.6%로 기존 cart 분류나무 모형 대비 다소 결과가 떨어짐을 알 수 있다.

(분류나무 cart 모형 바로가기) / (분류앙상블 모형배깅 바로가기)


 


4) 분류 앙상블에서 몇 개의 분류기가 적당한 것인지를 알 아 볼 수 있다.


> evol.german <- errorevol(boo.german,newdata=german) 

> plot.errorevol(evol.german)

  

결과 해석: x축은 분류기의 개수, y축은 오분류율을 의미, 그림에 따르면 분류기의 개수가 30개가 넘어가면 비교적 안정적인 결과를 보인다. 하지만 90개 이상인 경우 약간 오분류율이 감소하는 경향도 있는 듯하다.따라서 독일신용평가데이터에는 배깅 앙상블의 크기를 최소 90개 이상으로 정하면 된다. 




5) 데이터의 70%를 랜덤하게 훈련 데이터로 할당하고, 나머지 30%는 검증 데이터로 할당

#데이터를 입력

> set.seed(1234)

> i=sample(1:nrow(german),round(nrow(german)*0.7))#70% for training data, 훈련 데이터, 30% test data 검증 데이터

> german.train = german[i,]

> german.test = german[-i,]


#부스팅 앙상블 분류 모형 만들기

> my.control<-rpart.control(xval=0,cp=0,maxdepth=1)

> boo.train.german<-boosting(y~.,data=german.train,boos=T,mfianl=100,control=my.control)


#중요한 변수 분석

> names(boo.train.german)

[1] "formula"    "trees"      "weights"    "votes"      "prob"       "class"      "importance"

[8] "terms"      "call"     

> boo.train.german$importance

age       check      credit     debtors    duration  employment     foreign     history

3.9363991  32.5300152   4.3733165   0.0000000  16.0942441   6.2892310   0.0000000  13.8030374

housing installment         job  numcredits      others    personal    property     purpose

0.3568900   2.0703206   0.0000000   0.6247335   1.1360575   0.7039699   0.0000000  12.6654775

residence residpeople     savings   telephone

0.0000000   0.0000000   5.4163077   0.0000000

> order(boo.train.german$importance,decreasing = T)

[1]  2  5  8 16  6 19  3  1 10 13 14 12  9  4  7 11 15 17 18 20

> boo.train.german$importance[2]

check

32.53002

> boo.train.german$importance[5]

duration

16.09424

> boo.train.german$importance[8]

history

13.80304

> importanceplot(boo.train.german)

 

#예측하기

> pred.boo.train.german<-predict.boosting(boo.train.german,newdata=german.test)

> pred.boo.train.german$confusion

Observed Class

Predicted Class bad good

bad   42   19

good  57  182

> addmargins(pred.boo.train.german$confusion)

Observed Class

Predicted Class bad good Sum

bad   42   19  61

good  57  182 239

Sum   99  201 300


#오분류율 계산

> 1-sum(diag(pred.boo.train.german$confusion)) / sum(pred.boo.train.german$confusion)

[1] 0.2533333

 

결과 해석: 검증데이터에 대한 오분류율은 기존 cart 분류 나무는 26.33%, 배깅 역시 26.3%, 

              반면 부스팅은 25.3%으로 계산되어, 조금 향상되었음.


(분류나무 cart 모형 바로가기) / (분류앙상블 모형 – 배깅 바로가기)



출처: 데이터마이닝(장영재, 김현중, 조형준 공저) 



반응형
Posted by 마르띤
,
반응형

회귀앙상블모형 - 랜덤포레스트(링크)

분류앙상블모형 - 배깅, 부스팅, 랜덤포레스트


 

이번에는 배깅 방법을 이용하여 분류 앙상블 모형을 진행해보자

 

1) 데이터 입력

> setwd('c:/Rwork')

> german=read.table('germandata.txt',header=T)

> german$numcredits = factor(german$numcredits)

> german$residence = factor(german$residence)

> german$residpeople = factor(german$residpeople)

> class(german$numcredits);class(german$residpeople);class(german$residence)

[1] "factor"

[1] "factor"

[1] "factor"

 


2) 배깅 방법의 실행

> library(rpart)

> library(adabag)

> my.control <- rpart.control(xval = 0, cp = 0, minsplit = 5, maxdepth = 10)

> bag.german <- bagging(y~.,data = german, mfinal = 50, control = my.control)

> summary(bag.german)

Length Class   Mode    

formula        3  formula call    

trees         50  -none-  list    

votes       2000  -none-  numeric 

prob        2000  -none-  numeric 

class       1000  -none-  character

samples    50000  -none-  numeric 

importance    20  -none-  numeric 

terms          3  terms   call    

call           5  -none-  call

 

함수 설명

 rpart.control(xval = 0, cp = 0, minsplit = 5, maxdepth = 10) : mfinal 배깅 방법에서 생성하게 분류기의 개수  B, 50개의 분류기는 rpart 분류나무를 이용하여 생성되고, 각각의 분류나무는 노드의 최소 데이터 수는 5이고 최대 분할의 깊이는 10이라는 옵션으로 생성된다.


 

3) 가장 중요한 변수가 뭔지 알아보자.

> names(bag.german)

[1] "formula"    "trees"      "votes"      "prob"       "class"      "samples"    "importance" "terms"    

[9] "call"     

> bag.german$importance

age        check       credit        debtors     duration      employment  foreign     history      housing

7.1242723 16.4701060  13.2520285  2.0382461  10.3813110   6.4053643   0.1100654   6.7854018   0.9387335

installment   job         numcredits   others      personal    property     purpose      residence    residpeople

1.7740456   1.8354714   0.9098175   2.6632713   3.0909168   3.8218936  11.4089057   3.5700495   0.3570470

savings      telephone

6.5166935   0.5463590

결과 해석: check 변수가 가장 중요한 입력 변수이고, credit 번째로 중요한 역할을 하는 입력변수이다.

 

> order(bag.german$importance)

[1]  7 18 20 12  9 10 11  4 13 14 17 15  6 19  8  1  5 16  3  2

> order(bag.german$importance,decreasing=T)

[1]  2  3 16  5  1  8 19  6 15 17 14 13  4 11 10  9 12 20 18  7

> bag.german$importance[2]

check

16.47011

> bag.german$importance[3]

credit

13.25203

> which.max(bag.german$importance)

check

2

> which.min(bag.german$importance)

foreign

7

> bag.german$importance[which.max(bag.german$importance)]

check

16.47011

> bag.german$importance[which.min(bag.german$importance)]

foreign

0.1100654

> importanceplot(bag.german)



 

 

4) 목표변수의 분류예측치를 구하고 정확도에 대해 평가하는 방법에 대해 알아보자

> pred.bag.german <- predict.bagging(bag.german,newdata=german)

> names(pred.bag.german)

  [1] "formula"   "votes"     "prob"      "class"     "confusion" "error"   

> head(pred.bag.german$prob,10) # 집단 투표비율

      [,1] [,2]

  [1,] 0.04 0.96

  [2,] 0.92 0.08

  [3,] 0.02 0.98

  [4,] 0.30 0.70

  [5,] 0.72 0.28

  [6,] 0.16 0.84

  [7,] 0.00 1.00

  [8,] 0.16 0.84

  [9,] 0.00 1.00

  [10,] 0.86 0.14

  > head(pred.bag.german$class,10) #관측치마다 최종 예측 집단 출력

  [1] "good" "bad"  "good" "good" "bad"  "good" "good" "good" "good" "bad"

  > pred.bag.german$confusion #실제 목표변수의 값과 예측 목표변수의 값이 어느정도 유사한지 행렬의 형태로 보이고 있다.

                  Observed Class

  Predicted Class bad good

               bad  268    5

              good  32  695

 

 

> addmargins(pred.bag.german$confusion)

Observed Class

Predicted Class  bad good  Sum

bad   268    5  273

good   32  695  727

Sum   300  700 1000

> sum(pred.bag.german$confusion)

[1] 1000

> diag(pred.bag.german$confusion)

bad good

268  695

> 1-sum(diag(pred.bag.german$confusion)) /sum(pred.bag.german$confusion)

[1] 0.037

결과 해석: 오분류율이 3.7% 기존 19.6% 보다 배깅은 대단히 우수한 결과를 보임을 있다

(분류나무 cart 모형 바로가기)

 

5) 분류 앙상블에서 개의 분류기가 적당한 것인지를 있다.

> evol.german <- errorevol(bag.german,newdata=german)

> plot.errorevol(evol.german)


 

결과 해석: x축은 분류기의 개수, y축은 오분류율을 의미, 그림에 따르면 분류기의 개수가 40개가 넘어가면 비교적 안정적인 결과를 보인다. 따라서 독일신용평가데이터에는 배깅 앙상블의 크기를 40 이상으로 정하면 된다.

 

  

6) 훈련데이터와 검증 데이터로 분할하여 배깅 방법으로 평가

> set.seed(1234)

> i = sample(1:nrow(german),round(nrow(german)*0.7)) #70% for training data, 훈련 데이터, 30% test data 검증 데이터

> german.train = german[i,]

> german.test = german[-i,]

> bag.train.german <- bagging(y~., data = german.train, mfinal = 50, control = my.control)

> bag.train.german$importance

age        check        credit       debtors      duration    employment     foreign     history

5.9281898  14.8985632  12.2570321   2.1746067  13.6936647   7.2300214   0.0000000   6.3576993

Housing  installment        job    numcredits      others     personal    property     purpose

1.0506904   2.3547243   1.4585356   0.4620406   1.9891509   3.0157372   3.8454948  12.3522852

Residence  residpeople     savings   telephone

3.7829112   0.4905634   5.8943300   0.7637592

> order(bag.train.german$importance,decreasing=T)

[1]  2  5 16  3  6  8  1 19 15 17 14 10  4 13 11  9 20 18 12  7

> bag.train.german$importance[2]

check

14.89856

> bag.train.german$importance[5]

duration

13.69366

 

> which.max(bag.train.german$importance)

check

2

> which.min(bag.train.german$importance)

foreign

7

> importanceplot(bag.train.german)


> pred.bag.train.german <- predict.bagging(bag.train.german,newdata=german.test)

> pred.bag.train.german$confusion

Observed Class

Predicted Class bad good

bad   44   24

good  55  177

> addmargins(pred.bag.train.german$confusion)

Observed Class

Predicted Class bad  good Sum

bad    44   24   68

good   55  177  232

Sum    99  201  300

> 1-sum(diag(pred.bag.train.german$confusion)) / sum(pred.bag.train.german$confusion)

[1] 0.2633333

결과 해석: 검증데이터에 대한 오분류율은 26.33% 계산되어, 기존 분류 나무의 검증데이터 오분류율 26.33%과 동일하네 왜 동일하지..조금이라도 향상되었어야 하는데.. 더 공부하자.


(기존 cart 분류나무 모형 바로가기)

 


출처: 데이터마이닝(장영재, 김현중, 조형준 공저) 

반응형
Posted by 마르띤
,
반응형

보스턴 하우징 데이터 랜덤포레스트 방법의 회귀앙상블 모형

1) 데이터 읽기

> Boston$chas=factor(Boston$chas)

> Boston$rad=factor(Boston$rad)

> 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 ...

Factor 함수를 이용하여 범주형으로 변경하고, medv 변수를 목표변수로 다른 변수를 입력변수로 사용한다.

 

2) 랜덤포레스트 방법의 실행

>library(randomForest)

> rf.boston<-randomForest(medv~.,data=Boston,ntree=100,mtry=5,importance=T,na.action=na.omit)

> rf.boston

 

Call:

  randomForest(formula = medv ~ ., data = Boston, ntree = 100,      mtry = 5, importance = T, na.action = na.omit)

Type of random forest: regression

Number of trees: 100

No. of variables tried at each split: 5

 

Mean of squared residuals: 9.743395

% Var explained: 88.46

 

> summary(rf.boston)

Length Class  Mode    

call              7    -none- call    

type              1    -none- character

predicted       506    -none- numeric 

mse             100    -none- numeric 

rsq             100    -none- numeric 

oob.times       506    -none- numeric 

importance       26    -none- numeric 

importanceSD     13    -none- numeric 

localImportance   0    -none- NULL    

proximity         0    -none- NULL    

ntree             1    -none- numeric 

mtry              1    -none- numeric 

forest           11    -none- list    

coefs             0    -none- NULL    

y               506    -none- numeric 

test              0    -none- NULL    

inbag             0    -none- NULL    

terms             3    terms  call

 함수 설명

ntree=100, 분류기 개수. 디폴트는 500

mtry=5, 중간노드마다 랜덤하게 선택되는 변수들의 개수. 디폴트는 분류나무의 경우 sqrt(p), 회귀나무의 경우 p/3

importance=T, 변수의 중요도 계산, 디폴트는 F

na.action=na.omit, 결측치를 처리하는 방법, 변수의 중요도를 계산하게 하고, 결측치는 필요한 경우에만 삭제.

 

 

names()함수를 통해 rf.boston에 저장된 오브젝트의 리스트를 불러내어, $predicted를 이용하여 훈련 데이터의 예측 집단을 출력할 수 있다.

> names(rf.boston)

[1] "call"            "type"            "predicted"       "mse"             "rsq"           

[6] "oob.times"       "importance"      "importanceSD"    "localImportance" "proximity"     

[11] "ntree"           "mtry"            "forest"          "coefs"           "y"              

[16] "test"            "inbag"           "terms"         

> head(rf.boston$predicted,30)

1        2        3        4        5        6        7        8        9       10

28.25382 22.55963 34.14192 35.45333 34.06798 26.81151 21.01950 16.78839 17.80599 19.23591

11       12       13       14       15       16       17       18       19       20

21.02440 21.23466 21.80889 20.05162 19.30557 20.21721 21.61349 18.46000 18.14724 19.96174

21       22       23       24       25       26       27       28       29       30

14.10136 18.55984 16.05801 15.04825 16.70996 15.70548 17.84748 14.82048 18.88633 20.64939

 

importance()함수를 통해 계산된 입력변수의 중요도를 알 수 있다.

> importance(rf.boston,type=1)

%IncMSE

crim     8.325232

zn       2.061869

indus    5.130483

chas     1.030915

nox      9.211906

rm      17.090802

age      5.229782

dis      8.322716

rad      5.342500

tax      4.604745

ptratio  7.102056

black    5.292651

lstat   14.652271

결과를 보면 rm변수과 lstat 변수의 중요도가 가장 높음을 알 수 있다.

함수 설명

 type=1,은 정분류율의 평균감소값, 2는 불순도의 평균감소값을 이용하여 계산

 

목표변수의 적합값을 구하고 평가하기 위해 평균오차제곱합(mse)를 계산.

> names(Boston)

[1] "crim"    "zn"      "indus"   "chas"    "nox"     "rm"      "age"     "dis"     "rad"   

[10] "tax"     "ptratio" "black"   "lstat"   "medv"  

> Boston$medv.hat = predict(rf.boston,newdata=Boston)

> mean((Boston$medv-Boston$medv.hat)^2) #mean square error(mse)

[1] 1.915207

 

기존 선형회귀 한 회귀 분류 나무 모형 결과의 평균오차제곱합 mean((Boston$medv-Boston$medv.hat)^2) = 10.8643  대비 랜덤포레스트의 평균오차제곱합이 1.915207로 설명력이 매우 증가되었음을 알 수 있다. 랜덤포레스트의 경우 부트스트랩을 이용하기 때문에 확률임의추출에 의한 변동성이 있을 수 있다. 따라서 모델링을 할 때 마다 결과가 다르기 때문에, 랜덤포레스트를 수차례 반복 시행하고 예측결과의 평균값을 취하는 경우도 있다.

(기존 보스턴 하우징 데이터 회귀나무모 사례 분석 링크 로가기)

 

랜덤 포레스트 회귀앙상블의 적합값과 실제값의 일치도를 보자. 예측일치도가 우수함을 알 수 있다.

> plot(Boston$medv,Boston$medv.hat,xlab='Observed Values',ylab='Fitted Values')

> abline(0,1)


 

 

 

기존 분류 회귀의 나무모형의 적합값과 실제값의 일치도와 비교해봐도 매우 우수함을 알 수 있다.

 

 

 

 

이 의사결정 나무를 활용하여 30% 검증 데이터에 적용시켜서 분류예측치를 구해보자. 그리고 그 예측치를 구해보자.

> set.seed(1234)

> nrow(Boston)

[1] 506

> 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 검증 데이터

> rf.train.boston<-randomForest(medv~.,data=Boston.train,ntree=100,importance=T,na.action=na.omit)

#obtain the predicted values

> medv.hat.test<-predict(rf.train.boston,newdata=Boston.test)

> mean((Boston.test$medv-medv.hat.test)^2) #predicted mean square error

[1] 4.114596

검증 데이터에 대한 평균오차제곱합 mse 4.11로 계산되었다. 기존 회귀 분류 나무모형 의 검증 데이터 오분류율 13.95258와 비교해보면 상당히 향상된 결과임을 알 수 있다.

(기존 보스턴 하우징 데이터 회귀나무모 사례 분석 링크 로가기)


출처: 데이터마이닝(장영재, 김현중, 조형준 공저) 

반응형
Posted by 마르띤
,
반응형

앙상블(ensemble)모형이란 주어진 데이터를 이용하여 여러 개의 서로 다른 예측 모형을 생성한 후, 이러한 예측 모형의 예측 결과를 종합하여 하나의 최종 예측결과를 도출해 내는 방법을 말한다. 목표변수의 형태에 따라 분류분석에도 사용 가능하고, 회귀분석에도 사용 가능하다. 분류분석에 사용한다면 분류앙상블, 회귀분석에 사용한다면 회귀앙상블이라 부를 수 있다. 현실적으로 앙상블 모형은 대부분 분류모형에서 사용되고 있는 실정이다. 이유는 데이터마이닝의 영역에서 더 자주 필요로 하는 모형이 분류모형이기 때문이라고 추측된다.

 

데이터를 이용하여 생성해 낸 한 분류모형의 결과를 분류기(classifier)라 하자. 예측집단을 종합하는 방법으로는 주도 다수결 방식이 사용되고 있다. 다수결 방식에 따라 아래와 같이 구분할 수 있다.

단순 다수결 방식: 만약 예측치 중에서 6개의 분류기가 1이라고 예측하고, 5개의 분류기가 0이라고 예측했다면, 다수결 방식에 의해서 이 관찰치는 1이라고 최종 결론을 내린다. 배깅, 랜덤포레스트 방법이 단순 다수결 방식을 사용한다.

가중 다수결 방식: 각 분류기마다 가중치인 wi를 고려해야 한다. wi는 각 분류기 오류율의 역수 개념이다. 성능이 우수한 분류기에 가중치를 더 부여하는 것이다. 부스팅 방법이 가중다수결 방식을 사용한다.

 

앙상블 모형의 종류에 따른 구분은 다음과 같다.

배깅 방법: 배깅(bagging) 방법은 Breiman(1996)에 의해 개발된 분류 앙상블 방법이다. Bagging bootstrap aggregating의 약어로 훈련 데이터로부터 부트스트랩 데이터를 B번 생성하여 부트스트랩 데이터마다 분류기를 생성한 후 그 예측결과를 앙상블하는 방법이다. 배깅 방법은 불안정한 분류방법의 예측력을 획기적으로 향상시킨다고 알려져 있다. 분류나무 중에서 가지치기를 사용하지 않은 최대나무가 더 불안정한 불류방법이기 때문에 그 효과가 더욱 좋다.

부스팅 방법: 부스팅(boosting) 방법은 Freund and Schapire(1997)에 의해 개발된 분류 앙상블 방법이다. 부스팅에 사용되는 분류기는 오분류율을 랜덤하게 예측하는 것보다 조금이라도 좋은 예측모형이기만 하면 효과가 있다고 알려져 있다. 이는 예측력이 약한 분류 모형을 결합하여 강한 예측모형을 만드는 과정으로, 가장 많이 실행되는 알고리즘은 아다부스트(AdaBoost: adaptive boosing)방법이다. 그 방법으로는 두 가지 방식으로 수행할 수 있다.

 1) 가중치를 반영한 분류기 생성 방식

 2) 표본추출에 의한 분류기 생성방식

 

랜덤포레스트 방법: randor forest 방법은 부트스트랩을 이용한 데이터의 변화 및 나무모형 분할방법에 랜덤성을 가미하여 나무 모형이 배깅과 부스팅보다 훨씬 더 다양해지도록 유도하는 아이디어를 가지고 있고, 더 정확한 예측력을 가지고 있다고 알려져 있다. 이 방법은 Breiman(2001)이 고안한 방법으로, 입력변수의 개수가 많을 때 그 효과가 극대화된다.



아래 나무모형과 앙상블 모형을 비교한 table, 각 방법을 클릭하면 해당 글로 이동


 

분류나무모형

분류앙상블 모형

cart

배깅

부스팅

랜덤포레스트

오분류율

19.6%

3.7%

22.6%

0%

예측성능의 오분류율

26.3%

25.3%

25.3%

   25% 


 - 목표변수가 YES / NO 등 집단을 의미하는 범주형 의사결정나무 -> 분류나무모형 




 

회귀나무모형

회귀 앙상블 모형

cart

랜덤포레스트

평균오차제곱(MSE)

10.86

1.92

예측평균오차제곱합(PMSE)

13.95

4.11


 - 목표변수가 22.53, 50 등 연속형 변수인 의사결정나무 -> 회귀나무모형




출처: 데이터마이닝(장영재, 김현중, 조형준 공저)

반응형
Posted by 마르띤
,
반응형

목표변수가 집단을 의미하는 범주형 의사결정나무 -> 분류나무모형

목표변수가 연속형 변수인 의사결정나무 -> 회귀나무모형


 목표 변수가 연속형 변수인 경우, 의사결정나무는 회귀나무라고 한다. 통계학에서 목표변수가 연속형일 때 흔히 수행하는 분석이 선형 회귀분석인 것을 연상해 본다면 명칭이 이해가 될 것이다. 일반적으로 범주형 변수들은 가변수화하여 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 과 비교하면 다소 높음을 알 수 있다. 



복습과 무한반복 연습이 필요해 보인다


출처: 데이터마이닝(장영재, 김현중, 조형준 공저)

반응형
Posted by 마르띤
,