더북(TheBook)

성능 평가

가족 정보를 사용한 모델의 성능을 알아보자. 이 모델을 위해 설명한 내용이 길어 이해를 도우려고 전체 코드를 보였다.

family_result <- foreach(f=folds) %do% {
  f$train$type <- "T"
  f$validation$type <- "V"
  all <- rbind(f$train, f$validation)
  ctree_model <- ctree(
    survived ~ pclass + sex + age + sibsp + parch + fare + embarked,
    data=f$train)
  all$prob <- sapply(predict(ctree_model, type="prob", newdata=all),
                     function(result) { result[1] })

  # 티켓 번호를 사용한 family_id
  family_idx <- 0
  ticket_based_family_id <- ddply(all, .(ticket), function(rows) {
    family_idx <<- family_idx + 1
    return(data.frame(family_id=paste0("TICKET_", family_idx)))
  })
  all <- adply(all, 1,
    function(row) {
      family_id <- NA
      if (!is.na(row$ticket)) {
        family_id <- subset(ticket_based_family_id,
                            ticket == row$ticket)$family_id
      }
        return(data.frame(family_id=family_id))
      })

  # avg_prob
  all <- ddply(all,
               .(family_id),
               function(rows) {
                 rows$avg_prob <- mean(rows$prob)
                 return(rows)
               })

  # maybe_parent, may_be_child
  all <- ddply(all, .(family_id), function(rows) {
    rows$maybe_parent <- FALSE
    rows$maybe_child <- FALSE
    if (NROW(rows) == 1 ||
        sum(rows$parch) == 0 ||
        NROW(rows) == sum(is.na(rows$age))) {
      return(rows)
    }
    max_age <- max(rows$age, na.rm=TRUE)
    min_age <- min(rows$age, na.rm=TRUE)
    return(adply(rows, 1, function(row) {
      if (!is.na(row$age) && !is.na(row$sex)) {
        row$maybe_parent <- (max_age - row$age) < 10
        row$maybe_child <- (row$age - min_age) < 10
      }
      return(row)
    }))
  })

  # avg_parent_prob, avg_child_prob
  all <- ddply(all, .(family_id), function(rows) {
    rows$avg_parent_prob <- rows$avg_prob
    rows$avg_child_prob <- rows$avg_prob
    if (NROW(rows) == 1 || sum(rows$parch) == 0) {
      return(rows)
    }
    parent_prob <- subset(rows, maybe_parent == TRUE)[, "prob"]
    if (NROW(parent_prob) > 0) {
      rows$avg_parent_prob <- mean(parent_prob)
    }
    child_prob <- c(subset(rows, maybe_child == TRUE)[, "prob"])
    if (NROW(child_prob) > 0) {
      rows$avg_child_prob <- mean(child_prob)
    }
    return(rows)
  })

  # ctree 모델
  f$train <- subset(all, type == "T")
  f$validation <- subset(all, type == "V")
  (m <- ctree(survived ~ pclass + sex + age + sibsp + parch + fare + embarked + maybe_parent
              + maybe_child + age + sex + avg_prob + avg_parent_prob + avg_child_prob,
              data=f$train))
  print(m)
  predicted <- predict(m, newdata=f$validation)
  return(list(actual=f$validation$survived, predicted=predicted))
}

성능 평가 결과는 다음과 같다.

> family_accuracy <- evaluation(family_result)
[1] "MEAN +/- SD: 0.815 +/- 0.028"

ctree( )만 사용한 이전 모델의 경우 성능이 0.812 +/- 0.030이었으므로, 가족 정보를 사용해 정확도의 평균이 개선되었고 표준 편차가 줄어들어 서로 다른 데이터에 대해서도 성능이 좀 더 일관적으로 나옴을 알 수 있다.

이제 최종적으로 11.2.2절에서 분리해둔 테스트 데이터에 세 가지 알고리즘(rpart, ctree, family_id를 사용한 모델)을 적용해서 결과를 비교해볼 차례다. 이 부분은 독자의 연습 문제로 남겨둔다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.