R caretでCost-Sensitive 決定木をやってみよう

# 最初に

機械学習などでモデルを作成した際、一般的にはそのモデルを評価するためにAccuracyという指標を使う。

```math
\text{Accuracy} = \frac{\text{True Positives} + \text{True Negatives}}{\text{True Positives} + \text{True Negatives} + \text{False Positives} + \text{False Negatives}}
```

これを見るたび、False PositiveとFalse Negativeを単純に足している事に違和感があった。例えば、製造業における出荷前検査を想定して、不良品を不良品だと検査できることをTrue Positiveとすると、
- False Positive: 検査にて不良品と判断されるが実際は良品 -> 製品の破棄コストのみ
- False Negative: 検査にて良品と判断されるが実際は不良品 -> 不良品が市場に流れるリスク

となるため、まったく等価ではない。

そこで調べていたところ、Cost-Sensitiveなモデルの立て方があると学んだのでメモを残す。

## R code

視覚化して理解したいので、x=-1, y=-1を中心としたAと、x=1, y=1を中心としたBという二つの乱数を発生させる。それをAをBと判断する際のコストをBをAと判断するコストの10倍に設定し、決定木のモデルを作成した。

```R
library(caret)
library(rpart)
library(tidyverse)
library(rpart.plot)

# テーマ設定
theme_set(theme_bw(base_size = 11))

# サンプルデータの作成
set.seed(1)

x <- rnorm(1000, -1, 1)
y <- rnorm(1000, -1, 1)
res <- rep("A", 1000) %>% as.factor()

data_A <- tibble(x, y, res)

x <- rnorm(1000, 1, 1)
y <- rnorm(1000, 1, 1)
res <- rep("B", 1000) %>% as.factor()

data_B <- tibble(x, y, res)

data <- full_join(data_A, data_B)

# コスト行列の作成
# 0を1と判断する際のコストを1を0と判断するコストの100倍に設定
cost_matrix <- matrix(c(0, 1, 10, 0), ncol = 2)
rownames(cost_matrix) <- colnames(cost_matrix) <- levels(data$res)

# コスト感受性の決定木モデルを作成
model_10 <- train(res ~ x + y, data=data, method="rpart", 
               parms = list(loss=cost_matrix))

# 決定木の境界をプロットする関数
plot_decision_boundary <- function(model, data) {
  # 予測用のデータを作成
  grid_data <- expand.grid(x = seq(min(data$x), max(data$x), length.out = 100),
                           y = seq(min(data$y), max(data$y), length.out = 100))
  grid_data$res <- predict(model, grid_data)
  
  # グラフをプロット
  ggplot(data, aes(x = x, y = y)) +
    geom_tile(data = grid_data, aes(fill = res), alpha = 0.6) +
    geom_point(aes(color = res)) +
    scale_fill_manual(values = c("#FFDDDD", "#DDFFDD")) +
    scale_color_manual(values = c("red", "green")) +
    labs(title = "Decision Boundary of Decision Tree")
}

# グラフを表示
plot_decision_boundary(model_10, data)

# 決定木を表示
prp(model_10$finalModel, type=2, extra=104, main="Decision Tree")
```
以下のようなplotが作成出来る。AとBの境界線がかなり右上に来ている事がわかる。

![Rplot02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3526485/e96c16d2-8ea7-794f-d01c-1358cdd7b4ae.png)

![Rplot03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3526485/62598a28-89d7-45fd-408c-e4ef81e8a5c3.png)

このとき、
```R
cost_matrix <- matrix(c(0, 1, 10, 0), ncol = 2)
```
がコストを設定するmatrixとなる。これをcaretでモデルを作成する際にparamとして与える。
matrixはつまり下のような形になるのだが、対角線は基本的には0を設定する。これはAをAと判断する、またはBをBと判断するコストを0としている。
```
     [,A] [,B]
[A,]    0   10
[B,]    1    0
```
まとめると以下のような形になる。

- [A,A] の値 0 は、AをAと正しく分類した際のコスト。正しく分類されたのでコストは0。
- [A,B] の値 1 は、AをBと誤分類した際のコスト。10とする。
- [B,A] の値 1 は、BをAと誤分類した際のコスト。こっちを1とする。
- [B,B] の値 0 は、BをBと正しく分類した際のコスト。正しく分類されたのでコストは0。

このときのCofusion Matrixは以下のようになっている。Sensitivity が高く出ているので期待通りではある。ただ、cost_matrixと比較して、referenceとpredictionが逆になっているのがちょっと気持ち悪い気がする。

```
Confusion Matrix and Statistics

          Reference
Prediction   A   B
         A 980 280
         B  20 720
                                          
               Accuracy : 0.85            
                 95% CI : (0.8336, 0.8654)
    No Information Rate : 0.5             
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.7             
                                          
 Mcnemar's Test P-Value : < 2.2e-16       
                                          
            Sensitivity : 0.9800          
            Specificity : 0.7200          
         Pos Pred Value : 0.7778          
         Neg Pred Value : 0.9730          
             Prevalence : 0.5000          
         Detection Rate : 0.4900          
   Detection Prevalence : 0.6300          
      Balanced Accuracy : 0.8500          
                                          
       'Positive' Class : A               
```
## まとめと課題
- cost-sensitive決定木のやり方をしらべて、だいたい動かす方法がわかった。
- 数学的な意味合いを調べること。（10倍とは？）

## 参考
https://stats.stackexchange.com/questions/46963/how-to-control-the-cost-of-misclassification-in-random-forests

https://api.rpubs.com/JoPaPi/983017