이번 캐글 경진 대회는 병든 사과나무 잎사귀를 식별하는 다중분류 문제를 풀어보았다.
<대회 정보와 데이터>
Plant Pathology 2020 - FGVC7
https://www.kaggle.com/competitions/plant-pathology-2020-fgvc7
Plant Pathology 2020 - FGVC7 | Kaggle
www.kaggle.com
본 경진대회에서 주어지는 여러 잎사귀 사진 중에서는 건강한 잎사귀도 있고 병든 잎사귀도 있다.
해충으로 인해 피해를 입지 않으려면 질병을 조기에 발견해야 할 것이다. 하지만 사람이 일일이 확인하려면 시간도 비용도 많이 든다. 이 문제를 딥러닝 모델을 활용해서 해결해 보려고 한다.
이번에는 다중분류 문제로 타깃값이 다음과 같이 총 4개로 잎사귀가 각 타깃값일 확률을 예측해야한다.
- healthy - 건강한 잎사귀
- multiple_diseases - 여러 질병에 걸린 잎사귀
- rust - 녹병에 걸린 잎사귀
- scab - 붉은곰팡이병에 걸린 잎사귀
주어진 데이터는 다음과 같다. 각 데이터의 의미는 경진대회 페이지에서 더 자세히 볼 수 있다.
- images - 훈련/테스트 이미지(jpg) 데이터가 들어있는 디렉터리
- sample_submission.csv - 샘플 제출 양식
- test.csv - 테스트 이미지 데이터 ID(파일명)
- train.csv - 훈련 이미지 데이터 ID(파일명)와 타깃값
csv 파일에 담긴 이미지 데이터 ID는 images 디렉터리에 담긴 해당 jpg 파일의 이름에 해당한다.
그러므로 csv 파일은 이미지 파일명과 타깃값을 위해서 존재하는 것이다.
데이터 둘러보기
이번에는 분석할 요소가 그렇게 많지 않으므로 csv 데이터를 간단하게 둘러보고, 타깃값 분포를 알아본 후 이미지를 출력해본다.
import pandas as pd
# 데이터 경로
data_path = '/kaggle/input/plant-pathology-2020-fgvc7/'
train = pd.read_csv(data_path + 'train.csv')
test = pd.read_csv(data_path + 'test.csv')
submission = pd.read_csv(data_path + 'sample_submission.csv')
train.shape, test.shape
csv 데이터를 불러와 개수를 확인해보니 훈련 데이터와 테스트 데이터 모두 1821개로 같다.
타깃값이 4개이므로 훈련 데이터의 열은 4개로 테스트 데이터보다 4개 더 많다.
train.head()
훈련 데이터의 다섯 행을 출력해보면
image_id에 확장자가 미포함된 훈련 이미지 데이터의 파일명이 들어있다. 실제 이미지는 위에서 본 것처럼 images 디렉터리에 있다.
healty, multiple_diseases, rust, scab 열은 타깃값으로 0과 1로 원-핫 인코딩 형식으로 기록되어있다. 각 잎사귀의 상태에 속하는 열에 1이 입력되어 있는 것이다. 예를 들어 Train_0 이미지의 잎사귀 상태는 scab로 붉은곰팡이병에 걸렸다는 의미다.
테스트 데이터도 살펴보자.
test.head()
테스트 데이터는 타깃값이 없으므로 테스트 이미지 데이터 파일명인 image_id만 있다. 위에서 데이터가 총 1821개였으므로 데이터 파일명은 Test_0 ~ Test_1820까지 있을 것이다.
마지막으로 제출 샘플 파일을 확인해보면
submission.head()
각 잎사귀 이미지가 어떤 상태인지 4개의 타깃값에 확률이 기재되어있다. 샘플 데이터에는 각 확률을 25%로 일괄 기재해두었다.
데이터 시각화
위에서 살펴본 csv 파일의 데이터를 시각해보자. 타깃값 분포와 실제 이미지를 출력해볼 것이다.
타깃값 분포
타깃값 분포를 보기 위해서 먼저 데이터를 타깃값 별로 나누어야 한다. 각 타깃값에 해당하는 데이터가 몇 개인지 알아보기 위해서이다.
# 데이터를 타깃값 별로 추출
healthy = train.loc[train['healthy'] == 1]
multiple_diseases = train.loc[train['multiple_diseases'] == 1]
rust = train.loc[train['rust'] == 1]
scab = train.loc[train['scab'] == 1]
타깃값에 해당하는 데이터를 각 타깃값 이름 변수에 담았다. 어떻게 들어갔는지 확인해 보기 위해 healty를 불러와 보자.
healty에 값이 1이 입력되어 있는 데이터가 잘 담겨있는 것을 확인했다.
각 타깃값에 해당하는 데이터가 할당되었다. 이 변수들을 사용해 타깃값 분포를 파이 그래프로 그려보자
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
mpl.rc('font', size = 15)
plt.figure(figsize = (7, 7))
label = ['healthy', 'multiple diseases', 'rust', 'scab'] # 타깃값 레이블
# 타깃값 분포 파이 그래프
plt.pie([len(healthy), len(multiple_diseases), len(rust), len(scab)],
labels = label,
autopct = '%.1f%%');
파이 그래프 출력 결과 rust > scab > healthy > multiple diseases 순으로 많다. multiple diseases의 비율은 나머지 타깃값에 비해 상대적으로 적다. 비율 차이가 크기 때문에 훈련 데이터와 검증 데이터로 나눌 때 타깃값 비율에 맞게 나누는 것이 좋을 것이다.
이미지 출력
이제 각 타깃값에 해당하는 이미지를 출력해보자. 이미지 ID를 전달 받아서 화면에 이미지를 출력하기 위해 show_image() 함수를 사용할 것이다.
import matplotlib.gridspec as gridspec
import cv2 # OpenCV 라이브러리
def show_image(img_ids, rows = 2, cols = 3):
assert len(img_ids) <= rows*cols # 이미지가 행/열 개수보다 많으면 오류 발생함
plt.figure(figsize = (15, 8)) # 전체 Figure 크기 설정
grid = gridspec.GridSpec(rows, cols) # 서브플롯 배치
# 이미지 출력
for idx, img_id in enumerate(img_ids):
img_path = f'{data_path}/images/{img_id}.jpg' # 이미지 파일 경로
image = cv2.imread(img_path) # 이미지 파일 읽기
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 이미지 색상 보정
ax = plt.subplot(grid[idx])
ax.imshow(image) # 이미지 출력
2행 3열로 6개의 이미지를 출력하는 함수를 만들었다. assert 함수는 조건을 만족하는지 확인하는 함수로 이미지가 정해둔 행/열 개수보다 많으면 오류가 발생하게 된다.
이제 이 함수에 전달할 타깃값별 이미지 ID를 구해야 한다. 6개의 이미지를 출력하는 함수이므로 타깃값별로 마지막 6개 이미지 데이터의 image_id를 가져온다.
# 각 타깃값별 image_id(마지막 6개)
num_of_imgs = 6
last_healthy_img_ids = healthy['image_id'][-num_of_imgs:]
last_multiple_diseases_img_ids = multiple_diseases['image_id'][-num_of_imgs:]
last_rust_img_ids = rust['image_id'][-num_of_imgs:]
last_scab_img_ids = scab['image_id'][-num_of_imgs:]
이미지 ID를 구했으니 이제 각 타깃값별 잎사귀 이미지를 출력해보자.
show_image(last_healthy_img_ids) # 건강한 잎사귀 출력
타깃값이 healthy인 건강한 잎사귀 이미지다. 질병 없이 우리가 아는 깔끔한 잎사귀 모습이다.
아래로 나머지 병든 잎사귀 이미지도 확인해보았다.
show_image(last_multiple_diseases_img_ids) # 여러 질병에 걸린 잎사귀 출력
show_image(last_rust_img_ids) # 녹병에 걸린 잎사귀 출력
show_image(last_scab_img_ids) # 붉은곰팡이병에 걸린 잎사귀
질병이 있는 잎사귀들은 건강한 잎사귀와 다르게 곳곳에 반점 같은 것이 있는 모습을 확인할 수 있었다.
만약에 더 많은 이미지를 보고 싶다면 num_of_imgs 값과 figsize 크기를 수정하고, rows와 cols 파라미터를 지정해주면 된다.
예를 들어 4행 3열로 총 12장을 출력해보면 각 파라미터를 조정 후 다음과 같이 입력하면 된다.
show_image(last_healthy_img_ids2, 4, 3)
4행 3열로 총 12장의 건강한 잎사귀 이미지가 출력되었다.
분석 정리
1. csv 파일의 id 피처는 이미지 파일명이다. 파일의 경로명과 확장자만 추가하면 파일의 위치를 바로 얻을 수 있다.
2. 훈련 데이터는 잎사귀 상태(타깃값)을 4개의 열로 원-핫 인코딩 되어있다.
3. 타깃값들의 비율 차이가 커서 (multiple diseases 비율이 적음) 훈련 데이터와 검증 데이터를 나눌 때 타깃값 비율에 맞게 나누어야 한다.
다음 글에서는 딥러닝 모델을 직접 설계하지 않고 성능이 우수하다고 알려진 사전 훈련 모델을 활용해 전이 학습을 수행해볼 것이다.
참고 교재
머신러닝·딥러닝 문제해결 전략 | 신백균
[머신러닝·딥러닝 문제해결 전략 - chapter12]
'ML,DL' 카테고리의 다른 글
[머신러닝/딥러닝] 흉부 엑스선 기반 폐렴 진단: 분석정리 및 시각화 (0) | 2023.02.12 |
---|---|
[머신러닝/딥러닝] 병든 잎사귀 식별 경진대회: 베이스라인 모델 및 성능개선 (0) | 2023.02.05 |
[머신러닝/딥러닝] 향후 판매량 예측 경진대회: 분석정리 및 시각화 (0) | 2022.11.19 |
[머신러닝/딥러닝] 안전 운전자 예측 경진대회: 분석정리 및 시각화 (0) | 2022.11.07 |
[머신러닝/딥러닝] 범주형 데이터 이진분류 경진대회: 분석정리 및 시각화 (1) | 2022.10.04 |