3 분 소요

데이콘(생육 환경 최적화 경진대회) : 사진을 통해 이상치를 찾아보자.

최종결과 : 다른 청경채 샘플 같은 잎 넓이(무게)

무게 변화를 통한 이상치 탐색

#### 1. 분석 준비 ####
pacman::p_load(magick, tidyverse)                  # 데이터 전처리 관련 패키지

#### 2. 기본 데이터 로딩 ####
train_labels <- list.files(path="label", full.names = TRUE) %>% 
  lapply(read_csv, show_col_types = F) %>% bind_rows()


#### 3. 이상치 점검 ####
#### 나. 이미지 점검 ####
images1 <- function(case="CASE45", num=c("15", "16", "17")){
  ggsave(filename = "data/temp.png",      # 임시로 그래프를 이미지로 저장
         train_labels %>% 
           mutate(num=as.integer(str_sub(img_name, 8, 9))) %>% 
           filter(grepl(case, img_name)) %>% 
           ggplot(aes(num, leaf_weight)) + 
           geom_line(size=0.4) + theme_bw()+ 
           theme(text = element_text(size=3.5),
                 plot.margin = margin(0, 0, 0.1, 0.1, "cm"), 
                 # top, right, bottom, left (사진과의 적당한 여백 유지)
                 panel.border = element_rect(size = 0.3),
                 panel.grid = element_line(size=0.2),
                 axis.title.x = element_blank(),
                 axis.title.y = element_blank(),
                 axis.ticks = element_line(size = 0.2),
                 axis.ticks.length = unit(.03, "cm")) +
           labs(x=""),
         width = 200, height = 150, dpi = 300, units = "px", device='png')
  
  img1_name <- train_labels %>% mutate(img_name=paste0("train/", img_name)) %>% 
    filter(grepl(paste0(case, "_", num[1]), img_name))
  img1 <- image_read(img1_name$img_name) %>% image_scale(200) %>% 
    image_annotate(paste0(str_sub(img1_name, 7, -1), ", ", round(img1_name$leaf_weight)), 
                   size=12, color="blue", boxcolor="white", location="+40+135")
  
  img2 <- image_read("data/temp.png")
  
  img12 <- image_append(c(img1, img2))
  
  img3_name <- train_labels %>% mutate(img_name=paste0("train/", img_name)) %>%
    filter(grepl(paste0(case, "_", num[2]), img_name))
  img3 <- image_read(img3_name$img_name) %>% image_scale(200) %>%
    image_annotate(paste0(str_sub(img3_name, 7, -1), ", ", round(img3_name$leaf_weight)),
                   size=12, color="blue", boxcolor="white", location="+40+135")
  
  img4_name <- train_labels %>% mutate(img_name=paste0("train/", img_name)) %>%
    filter(grepl(paste0(case, "_", num[3]), img_name))
  img4 <- image_read(img4_name$img_name) %>% image_scale(200) %>%
    image_annotate(paste0(str_sub(img4_name, 7, -1), ", ", round(img4_name$leaf_weight)),
                   size=12, color="blue", boxcolor="white", location="+40+135")
  
  img34 <- image_append(c(img3, img4))
  
  image_append(c(img12, img34), stack=T)
}
images1()

오른쪽 위에 있는 그래프를 보면 16 부분에서 갑자기 무게가 감소한 것을 알 수 있습니다. 사진 밑에 해당 사진의 파일명과 소수 첫째자리에서 반올림한 무게를 표시해 두었습니다. 그래프와 비교하면서 보시면 됩니다. CASE45_06의 경우 24시간 후 무게가 감소한 이유를 다음과 같이 추론할 수 있습니다. CASE45_16은 CASE45_15의 24시간 후 모습입니다. 같은 맥락에서 CASE45_17은 CASE_16의 24시간 후의 모습이죠. 사진에서도 확인할 수 있듯이 CASE45_17을 보면 손이 같이 찍혔습니다. 무게를 측정(?)하는데 오류가 발생해서 CASE45_16의 경우 24시간 후 무게가 감소한 것으로 기록된 것입니다. 많은 분들이 CASE45_17의 경우 손이 찍혀서 자연스럽게 분석에서 제외했을텐데 CASE45_16도 제외하는 것이 타당합니다.

떨어져 나간 잎

images1(case="CASE72", num=c("05","06","07"))

CASE72의 경우도 06에서 무게가 감소한 것으로 나타납니다. 그 이유는 CASE72_06의 24시간 후 모습인 CASE72_07을 보면 일부 잎이 떨어져 나간 것을 확인할 수 있습니다. CASE72_06의 시진을 분석에 활용한다는 것은 이 사진을 보고 24시간 후 무게가 감소할 것을 학습시키는 것과 같습니다.

분무기 오류

images1(case="CASE05", num=c("21","22","23"))

CASE05에서도 21번째의 경우 24시간 후 무게가 감소한 것으로 나타났습니다. 앞 뒤 이미지를 살펴보면 CASE05_21과 CASE05_23은 정상적으로 보이는데 CASE05_22번 사진은 비정상적으로 말라 있는 것을 알 수 있습니다. 아마도 물 분무 기계 장치에 오류가 생겨서 물 분무가 안 된 것 같습니다. 그러다보니 CASE05_21 이미지의 경우 24시간 후 무게가 매우 많이 감소한 것으로 나타난 것입니다. 개인적으로 생각할 때 CASE05_21, 22, 23 모두 문제가 있는 이미지로 판단됩니다.

무게 측정 오류

images1(case="CASE07", num=c("06","07","08"))

CASE07_07과 CASE07_08의 경우 24시간 후 무게가 감소한 것으로 나타났습니다. 하지만 사진장으로 보면 멀쩡하게 잘 자라고 있는 모습을 확인할 수 있습니다. 이는 무게 측정 센서(?) 혹은 나름대로의 방법(?)에 문제가 생겨서 나타난 현상 같습니다. CASE07_07과 08 모두 빼야할 것 같습니다.

사진 비교를 통한 무게 추정

images2 <- function(files=c('train/CASE01_06.png', 'train/CASE04_05.png', 
                            'train/CASE23_03.jpg', 'train/CASE16_03.jpg'),
                   value=c(123, 130, 123, 130)){
  img1 <- image_read(files[1:2]) %>% image_scale(200) %>% image_append() %>% 
    image_annotate(str_sub(files[1], 7, -1) %>% paste0(", ", value[1]), 
                   size = 12, color = "blue", boxcolor = "white", location = "+40+135") %>% 
    image_annotate(str_sub(files[2], 7, -1) %>% paste0(", ", value[2]), 
                   size = 12, color = "blue", boxcolor = "white", location = "+240+135")
  img2 <- image_read(files[3:4]) %>% image_scale(200) %>% image_append() %>% 
    image_annotate(str_sub(files[3], 7, -1) %>% paste0(", ", value[3]), 
                   size = 12, color = "blue", boxcolor = "white", location = "+40+135") %>% 
    image_annotate(str_sub(files[4], 7, -1) %>% paste0(", ", value[4]), 
                   size = 12, color = "blue", boxcolor = "white", location = "+240+135")
  image_append(c(img1, img2), stack=T)
}
data <- train_labels %>% filter(leaf_weight>120 & leaf_weight<140) %>% 
  mutate(img_name=paste0("train/", img_name))
images2(file=c(data$img_name[c(1,2,5,6)]), value=c(round(data$leaf_weight[c(1,2,5,6)])))

잎 무게가 비슷한 데이터들을 선별해서 눈으로 잎 면적을 비교해 보았습니다. 다양한 형태의 잎이 보이는데 대략적으로 수긍이 갑니다.

무게 이상치

images2(file=c(data$img_name[c(10,1,3,4)]), value=c(round(data$leaf_weight[c(10,1,3,4)])))

많은 데이터가 수긍이 가지만 위와 같이 믿을 수 없는 경우도 있습니다. CASE41_20과 CASE16_03이 같다는 사실을 도저히 믿을 수 없습니다. 이 경우도 CASE16_03 무게를 측정할 때 오류가 발생했다고 판단하는 것이 타당합니다. 같은 맥락에서 CASE23_03도 마찬가지입니다. CASE01_06과 비교해보면 얼마나 터무니 없는 무게인지 알 수 있습니다.

위와 같은 사례들은 전체 이상치 중 일부에 해당됩니다. 꼼꼼하게 살펴보면서 이상치를 모두 제거해 주어야 25%의 public 데이터를 넘어 나머지 test 데이터들의 무게를 보다 정확하게 추론할 수 있습니다.

대회가 한참 진행중이었던 5월 6일 9위 달리고 있었으며 5월 7일 CNN 고수분과 팀을 이루면서 서로의 데이터를 합쳐서 5월 8일 현재 6위 중입니다. 100% 다 공개하지는 못해도 꾸준히 분석 과정 공유하고 1등을 향해 최선을 다할 예정입니다. 대회가 끝난 이후에는 성적에 관계없이 최소한 제가 분석한 결과는 모두 공개하도록 하겠습니다. ^^

댓글남기기