R/ML & DL 공부

[R] 독버섯 분류 - 분류 규칙 (OneR, Ripper)

dori_0 2022. 4. 19. 15:31

독버섯 분류

OneR, Ripper 알고리즘

 

https://archive.ics.uci.edu/ml/datasets/mushroom

 

UCI Machine Learning Repository: Mushroom Data Set

Mushroom Data Set Download: Data Folder, Data Set Description Abstract: From Audobon Society Field Guide; mushrooms described in terms of physical characteristics; classification: poisonous or edible Data Set Characteristics:   Multivariate Number of Ins

archive.ics.uci.edu

8,124개의 버섯 표본과 23개의 주름 있는 버섯의 속성으로 되어 있음

버섯 머리 모양, 색, 냄새, 주름의 크기, 색, 모양, 서식지 등 22개의 속성에 대한 데이터

type - 독성 분류 poisonous, edible


1. 데이터 준비 

> mr = read.csv("C:/R/mushrooms.csv", stringsAsFactors = T)
> str(mr)
'data.frame':	8124 obs. of  23 variables:
 $ type                    : Factor w/ 2 levels "edible","poisonous": 2 1 1 2 1 1 1 1 2 1 ...
 $ cap_shape               : Factor w/ 6 levels "bell","conical",..: 3 3 1 3 3 3 1 1 3 1 ...
 $ cap_surface             : Factor w/ 4 levels "fibrous","grooves",..: 4 4 4 3 4 3 4 3 3 4 ...
 $ cap_color               : Factor w/ 10 levels "brown","buff",..: 1 10 9 9 4 10 9 9 9 10 ...
 $ bruises                 : Factor w/ 2 levels "no","yes": 2 2 2 2 1 2 2 2 2 2 ...
 $ odor                    : Factor w/ 9 levels "almond","anise",..: 8 1 2 8 7 1 1 2 8 1 ...
 $ gill_attachment         : Factor w/ 2 levels "attached","free": 2 2 2 2 2 2 2 2 2 2 ...
 $ gill_spacing            : Factor w/ 2 levels "close","crowded": 1 1 1 1 2 1 1 1 1 1 ...
 $ gill_size               : Factor w/ 2 levels "broad","narrow": 2 1 1 2 1 1 1 1 2 1 ...
 $ gill_color              : Factor w/ 12 levels "black","brown",..: 1 1 2 2 1 2 5 2 8 5 ...
 $ stalk_shape             : Factor w/ 2 levels "enlarging","tapering": 1 1 1 1 2 1 1 1 1 1 ...
 $ stalk_root              : Factor w/ 5 levels "bulbous","club",..: 3 2 2 3 3 2 2 2 3 2 ...
 $ stalk_surface_above_ring: Factor w/ 4 levels "fibrous","scaly",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ stalk_surface_below_ring: Factor w/ 4 levels "fibrous","scaly",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ stalk_color_above_ring  : Factor w/ 9 levels "brown","buff",..: 8 8 8 8 8 8 8 8 8 8 ...
 $ stalk_color_below_ring  : Factor w/ 9 levels "brown","buff",..: 8 8 8 8 8 8 8 8 8 8 ...
 $ veil_type               : Factor w/ 1 level "partial": 1 1 1 1 1 1 1 1 1 1 ...
 $ veil_color              : Factor w/ 4 levels "brown","orange",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ ring_number             : Factor w/ 3 levels "none","one","two": 2 2 2 2 2 2 2 2 2 2 ...
 $ ring_type               : Factor w/ 5 levels "evanescent","flaring",..: 5 5 5 5 1 5 5 5 5 5 ...
 $ spore_print_color       : Factor w/ 9 levels "black","brown",..: 1 2 2 1 2 1 1 2 1 1 ...
 $ population              : Factor w/ 6 levels "abundant","clustered",..: 4 3 3 4 1 3 3 4 5 4 ...
 $ habitat                 : Factor w/ 7 levels "grasses","leaves",..: 5 1 3 5 1 1 3 3 1 3 ...

> table(mr$veil_type)  # 1개이므로 제외
partial 
   8124 
   
> mr$veil_type = NULL
  • veil_type의 변수가 1개의 라벨을 가지고 있으므로 제외시켜주었다.

 

목적변수인 type을 확인해보니

> # 목적변수 확인
> table(mr$type)
   edible poisonous 
     4208      3916 
     
> 4208/(4208+3916)*100  # edible 51.8%
[1] 51.79714
> 3916/(4208+3916)*100  # poisonous 48.2%
[1] 48.20286

51.8%는 먹을 수 있는 버섯, 48.2%는 독성이 있는 버섯으로 이루어져 있다.

 

 

2. 훈련 데이터와 테스트 데이터 생성 (8:2)

> # 훈련 데이터와 테스트 데이터 생성 
> set.seed(12345)
> mr_rand = mr[order(runif(8124)), ]
> 
> 8124*0.8  #6500
[1] 6499.2
> 
> # 8:2로 나누기
> mr_train = mr_rand[1:6500, ]
> mr_test = mr_rand[6501:8124, ]
> 
> table(mr_train$type)
   edible poisonous 
     3339      3161 
     
> table(mr_test$type)
   edible poisonous 
      869       755

8:2의 비율로 train data, test data를 나눠주었다.

table을 확인해보면 골고루 잘 나뉜 것을 확인할 수 있다.

 

 

3. 분류 규칙 모델

① OneR 규칙기반 알고리즘

 

먼저 모델을 만들고 훈련한 후 확인해보자

> library(OneR)
> mr_1R = OneR(type~., data=mr_train)

> summary(mr_1R)

Call:
OneR.formula(formula = type ~ ., data = mr_train)

Rules:
If odor = almond   then type = edible
If odor = anise    then type = edible
If odor = creosote then type = poisonous
If odor = fishy    then type = poisonous
If odor = foul     then type = poisonous
If odor = musty    then type = poisonous
If odor = none     then type = edible
If odor = pungent  then type = poisonous
If odor = spicy    then type = poisonous

Accuracy:
6403 of 6500 instances classified correctly (98.51%)

Contingency table:
           odor
type        almond anise creosote fishy   foul musty   none pungent spicy  Sum
  edible     * 318 * 305        0     0      0     0 * 2716       0     0 3339
  poisonous      0     0    * 160 * 462 * 1746  * 26     97   * 203 * 467 3161
  Sum          318   305      160   462   1746    26   2813     203   467 6500
---
Maximum in each column: '*'

Pearson's Chi-squared test:
X-squared = 6125.1, df = 8, p-value < 2.2e-16
  • odor이 almond, anise, none이면 edible / creosote, fishy, foul 등이면 poisonous이라는 규칙이 생겼다.

 

 

이 모델의 성능을 평가해보면

> # 모델 성능 평가
> mr_pred = predict(mr_1R, mr_test)
> 
> library(gmodels)
> 
> CrossTable(mr_test$type, mr_pred,
+            prop.chisq = F, prop.c = F,
+            dnn = c('edible', 'poisonous'))

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  1624 

 
             | poisonous 
      edible |    edible | poisonous | Row Total | 
-------------|-----------|-----------|-----------|
      edible |       869 |         0 |       869 | 
             |     1.000 |     0.000 |     0.535 | 
             |     0.535 |     0.000 |           | 
-------------|-----------|-----------|-----------|
   poisonous |        23 |       732 |       755 | 
             |     0.030 |     0.970 |     0.465 | 
             |     0.014 |     0.451 |           | 
-------------|-----------|-----------|-----------|
Column Total |       892 |       732 |      1624 | 
-------------|-----------|-----------|-----------|

 
> 
> (869+732) / 1624 * 100 # 정확도 98.6%
[1] 98.58374
  • 이 모델의 정확도는 98.6%로 높은 성능을 보인다.

 

 

② Ripper 알고리즘

 

이번에는 조금 더 향상된 모델을 만들기 위해 Ripper 알고리즘으로 분류해보려고 한다.

> library(RWeka)
> mr_JRip = JRip(type~., data=mr_train)
> mr_JRip
JRIP rules:
===========

(odor = foul) => type=poisonous (1746.0/0.0)
(gill_size = narrow) and (gill_color = buff) => type=poisonous (929.0/0.0)
(gill_size = narrow) and (odor = pungent) => type=poisonous (203.0/0.0)
(odor = creosote) => type=poisonous (160.0/0.0)
(spore_print_color = green) => type=poisonous (60.0/0.0)
(stalk_surface_above_ring = silky) and (gill_spacing = close) => type=poisonous (52.0/0.0)
(stalk_color_above_ring = yellow) => type=poisonous (8.0/0.0)
(habitat = leaves) and (cap_color = white) => type=poisonous (3.0/0.0)
 => type=edible (3339.0/0.0)

Number of Rules : 9


> summary(mr_JRip)

=== Summary ===

Correctly Classified Instances        6500              100      %
Incorrectly Classified Instances         0                0      %
Kappa statistic                          1     
Mean absolute error                      0     
Root mean squared error                  0     
Relative absolute error                  0      %
Root relative squared error              0      %
Total Number of Instances             6500     

=== Confusion Matrix ===

    a    b   <-- classified as
 3339    0 |    a = edible
    0 3161 |    b = poisonous
  • 총 9개의 규칙이 만들어진 것을 확인할 수 있다.
  • odor=foul이면 poisonous와 같이 poisonous로 분류하는 8개의 규칙이 생겼다.
  • 이 8개의 규칙에 해당하지 않으면 edible로 분류한다는 규칙이 9번째 규칙이다.

 

 

이 모델의 성능을 평가해보면

> # 모델 성능 평가
> mr_pred2 = predict(mr_JRip, mr_test)
> 
> library(gmodels)
> 
> CrossTable(mr_test$type, mr_pred2,
+            prop.chisq = F, prop.c = F,
+            dnn = c('edible', 'poisonous'))

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  1624 

 
             | poisonous 
      edible |    edible | poisonous | Row Total | 
-------------|-----------|-----------|-----------|
      edible |       869 |         0 |       869 | 
             |     1.000 |     0.000 |     0.535 | 
             |     0.535 |     0.000 |           | 
-------------|-----------|-----------|-----------|
   poisonous |         0 |       755 |       755 | 
             |     0.000 |     1.000 |     0.465 | 
             |     0.000 |     0.465 |           | 
-------------|-----------|-----------|-----------|
Column Total |       869 |       755 |      1624 | 
-------------|-----------|-----------|-----------|

 
> 
> (869+755) / 1624 * 100  # 정확도 100%
[1] 100
  • 정확도 100%로 완벽한 성능을 보인다.

 

 

4. 두 알고리즘의 장단점

① OneR 규칙기반 알고리즘

장점 단점
쉽게 이해할 수 있다.
최상의 규칙 한 개를 생성한다.
좀 더 복잡한 알고리즘에 대해 벤치마크로서 실행할 수 있다.
하나의 속성을 사용한다.
단순하다.

 

 

② Ripper 알고리즘

단점
쉽게 이해할 수 있다.
결정 트리보다 단순한 모델을 만든다.
크고 노이즈한 데이터에 대해 효과적이다.
수치 데이터에 적합하지 않다.
좀 더 복잡한 모델은 성능 향상이 되지 않을 수도 있다.
전문 지식이나 일반 상식에 반대인 듯한 규칙을 만든다.