<머신러닝. 딥러닝 문제해결 전략> 3부 11장 실습하고 해당 내용을 정리한 내용입니다.
https://www.kaggle.com/c/aerial-cactus-identification
Aerial Cactus Identification | Kaggle
www.kaggle.com
1. 베이스라인 모델 설계
파이토치를 활용해 딥러닝 모델을 만들어 보겠습니다. 베이스라인은 간단한 CNN 모델을 활용하겠습니다.
파이토치를 활용한 딥러닝 모델링 절차는 다음과 같습니다.
시드값 고정 및 GPU 장비 설정 | |
데이터 준비 | 1. 훈련/검증 데이터 분리 2. 데이터셋 클래스 정의 3. 데이터셋 생성 4. 데이터 로더 생성 |
모델 생성 (CNN) | |
모델 훈련 | 1. 손실 함수와 옵티마이저 설정 2. 모델 훈련 |
성능 검증 | |
예측 및 제출 |
1. 시드값 고정 및 GPU 장비 설정
- 시드값 고정 : 결과 재현을 위한 작업
- GPU 장비 설정 훈련 속도를 높이기 위해 데이터를 GPU가 처리하도록 변경
2. 데이터 준비
- 훈련/검증 데이터 분리
- 데이터셋 클래스 정의 : 이미지 데이터를 모델링에 적합한 형태로 불러오도록 해줌
- 데이터 셋 생성
- 데이터 로드(데이터셋으로부터 데이터를 배치 단위로 불러와주는 객체)생성
3. 모델 생성 : 신경망 모델 클래스를 직접 설계한 후 인스턴트 생성
4. 모델 훈련
- 손실 함수와 옵티마이저 설정 : 훈련에 앞서 손실 함수(예측값과 실젯값의 차이를 구하는 함수)와 옵티마이저(최적 가중치를 찾아주는 알고리즘)설정
- 모델 훈련 : 신경망의 가중치(파라미터)를 갱신하며 모델 훈련
5. 성능 검증 : 검증 데이터로 모델 성능 검증
6. 예측 및 제출 : 테스트 데이터로 예측 후 결과 제출
2. 시드값 고정 및 GPU 장비 설정
- 시드값 고정
먼저 파이토치를 임포트하고 시드값을 고정해줍니다. 시드값을 고정하는 이유는 다시 실행해도 같은 결과를 얻기 위해서입니다. 이는 머신러닝에서 모델 훈련시에 설정한 random_state 파라미터와 같은 역할을 수행합니다.
import torch # 파이토치
import random
import numpy as np
import os
# 시드값 고정
seed = 50
os.environ['PYTHONHASHSEED'] = str(seed)
random.seed(seed) # 파이썬 난수 생성기 시드 고정
np.random.seed(seed) # 넘파이 난수 생성기 시드 고정
torch.manual_seed(seed) # 파이토치 난수 생성기 시드 고정 (CPU 사용 시)
torch.cuda.manual_seed(seed) # 파이토치 난수 생성기 시드 고정 (GPU 사용 시)
torch.cuda.manual_seed_all(seed) # 파이토치 난수 생성기 시드 고정 (멀티GPU 사용 시)
torch.backends.cudnn.deterministic = True # 확정적 연산 사용
torch.backends.cudnn.benchmark = False # 벤치마크 기능 해제
torch.backends.cudnn.enabled = False # cudnn 사용 해제
하지만 이 모든 시드값 고정 코드가 캐글에서 GPU로 파이토치 사용 시 반드시 필요한 건 아니며, 파이토치를 사용할 때 시드 값을 고정하면 속도도 느려지고 예측 성능도 떨어질 우려가 있습니다. 따라 결과를 재현할 필요가 없다면 시드값 고정은 생략하는 것이 더 나을 수도 있습니다.
- GPU 장비 설정
정형 데이터(csv 파일 등)를 다루는 머신러닝 경진대회와 달리 딥러닝 경진대회는 주로 비정형 데이터(이미지, 음성, 텍스트 등)를 다룹니다. 비정형 데이터를 모델링하려면 연산량이 많아집니다. CPU로는 감당하기 벅찰 정도라서 훈련 시간이 너무 길어집니다. 그래서 GPU를 사용합니다. GPU는 단순 연산 수백~수만 개 이상을 병렬로 처리할 수 있어서 딥러닝 모델을 CPU보다 훨씬 빠르게 훈련시켜줍니다.
다음 코드로 연산에 이용할 장비를 할당합니다.
if torch.cuda.is_available():
device = torch.device('cuda')
else:
device = torch.device('cpu')
CUDA는 엔비디아에서 개발한 병렬 처리 플랫폼입니다. 만일 CUDA에서 GPU를 사용할 수 있다면 장비를 'cuda'로, 그렇지 않다면 'cpu'로 설정하도록 했습니다.
캐글 환경에서 아무 설정도 하지 않으면 기본적으로 CPU를 제공합니다. 앞 코드를 실행한 뒤, device를 출력해보겠습니다.
GPU를 사용하려면 설정을 바꿔줘야 합니다. 오른쪽 [Settings] 탭에서 Accelerator를 None을 클릭하여 GPU로 바꿔주면 됩니다.
Accelerator를 변경하면 코드 환경 전체가 초기화됩니다. 그렇기 때문에 코드를 처음부터 다시 실행해야 합니다. 지금까지 설명한 코드를 재실행하면 device 변수에 CUDA가 할당될 것입니다.
결과는 아래와 같습니다.
3. 데이터 준비
딥러닝 경진대회는 머신러닝 경진대회 때와 비교하여 데이터 준비 과정이 훨씬 복잡합니다. 다음과 같은 과정을 거칩니다.
1. 훈련/검증 데이터 분리
2. 데이터셋 클래스 정의
3. 데이터셋 생성
4. 데이터 로더 생성
새롭게 등장한 데이터셋 클래스와 데이터 로더는 딥러닝 모델의 훈련에 필요한 데이터를 미니배치 단위로 공급하는 역할을 수행합니다. 이때 데이터셋 클래스에 정의된 변환기가 원본 데이터를 다양한 형태로 변환해줍니다.
모델링을 위해 데이터를 불러옵니다.
import pandas as pd
# 데이터 경로
data_path = '/kaggle/input/aerial-cactus-identification/'
labels = pd.read_csv(data_path + 'train.csv')
submission = pd.read_csv(data_path + 'sample_submission.csv')
이어서 압축을 풀어주면 데이터가 준비됩니다.
from zipfile import ZipFile
# 훈련 이미지 데이터 압축 풀기
with ZipFile(data_path + 'train.zip') as zipper:
zipper.extractall()
# 테스트 이미지 데이터 압축 풀기
with ZipFile(data_path + 'test.zip') as zipper:
zipper.extractall()
- 훈련데이터, 검증 데이터 분리
현재 labels에는 대회 주최 측에서 제공한 train.csv 파일의 내용이 담겨 있습니다. trian_test_split() 함수를 사용해 이 데이터를 훈련 데이터와 검증 데이터로 나눠주겠습니다..
from sklearn.model_selection import train_test_split
# 훈련 데이터, 검증 데이터 분리
train, valid = train_test_split(labels,
test_size=0.1, # ⓐ
stratify=labels['has_cactus'], # ⓑ
random_state=50)
ⓐ test_size가 검증 데이터의 비율을 의미합니다. 0.1을 전달했으니 훈련 데이터와 검증 데이터 비율이 9:1임을 뜻합니다.
ⓑ 앞서 본 바와 같이 타깃값 비율은 약 3:1입니다. 따라서 stratify 파라미터에 타깃값의 열 이름을 전달하면 훈련 데이터와 검증 데이터 비율도 약 3:1이 됩니다.
결과를 확인해보면 훈련 데이터와 검증 데이터 비율이 정확히 9:1 임을 알 수 있습니다.
- 데이터셋 클래스 정의
파이토치로 신경망 모델을 구축하려면 데이터셋도 일정한 형식에 맞게 정의해줘야 합니다.
먼저 데이터셋 생성에 필요한 라이브러리를 임포트합니다.
import cv2 # OpenCV 라이브러리
from torch.utils.data import Dataset # 데이터 생성을 위한 클래스
파이토치에서 제공하는 Dataset 클래스를 활용해 데이터셋 객체를 만들 수 있습니다. Dataset은 추상 클래스이며, 우리는 Dataset을 상속받은 다음 특수 메서드인 __len__()과 __getitem__()을 재정의(오버라이딩)해야 합니다.
● __len__() : 데이터셋 크기를 반환합니다.
● __getitem__() : 인덱스를 전달받아 인덱스에 해당하는 데이터를 반환합니다.
class ImageDataset(Dataset):
# ⓐ 초기화 메서드(생성자)
def __init__(self, df, img_dir='./', transform=None):
super().__init__() # ⓑ 상속받은 Dataset의 생성자 호출
# ⓒ 전달받은 인수들 저장
self.df = df
self.img_dir = img_dir
self.transform = transform
# ⓓ 데이터셋 크기 반환 메서드
def __len__(self):
return len(self.df)
# ⓔ 인덱스(idx)에 해당하는 데이터 반환 메서드
def __getitem__(self, idx):
img_id = self.df.iloc[idx, 0] # 이미지 ID
img_path = self.img_dir + img_id # ⓕ 이미지 파일 경로
image = cv2.imread(img_path) # 이미지 파일 읽기
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 이미지 색상 보정
label = self.df.iloc[idx, 1] # 이미지 레이블(타깃값)
if self.transform is not None:
image = self.transform(image) # ⓖ 변환기가 있다면 이미지 변환
return image, label # ⓗ
ⓐ __init__()는 ImageDataset 클래스의 초기화 메서드입니다. ⓑ 상속받은 Dataset의 초기화 메서드를 호출한 후 ⓒ 파라미터로 받은 인수들을 저장합니다. 파라미터들의 역할은 다음과 같습니다.
● df : DataFrame 객체.
● img_dir : 이미지 데이터를 포함하는 경로
● trianform : 이미지 변환기
ⓓ __len__()은 데이터셋의 크기를 반환하는 메서드입니다. Dataset 클래스에 이미 정의되어 있는 메서드를 재정의하는 겁니다.
ⓔ __getitem__()은 지정한 인덱스에 해당하는 데이터를 반환하는 메서드입니다. ⓕ idx번째 이미지와 레이블(타깃값)을 반환합니다. ⓖ 초기화 메서드에서 받은 경로에 이미지 ID를 합쳐 이미지 위치를 알아내고, ⓗ 초기화 메서드에서 이미지 변환기(transform)를 받아뒀다면 변환 작업까지 수행한 후 반환합니다.
이상으로 데이터셋을 생성해줄 ImageDataset 클래스를 정의했습니다.
- 데이터셋 생성
파이토치 모델로 이미지를 다루려면 이미지 데이터를 텐서 타입으로 바꿔야 합니다. 따라 앞서 정의한 ImageDataset 클래스를 이용해서 데이터셋을 만들어주겠습니다.
from torchvision import transforms # 이미지 변환을 위한 모듈
transform = transforms.ToTensor()
torchvision은 파이토치용 컴퓨터 비전 라이브러리이며, transforms는 다양한 이미지 변환기를 제공하는 모듈입니다.
ToTensor() 메서드로 이미지를 텐서로 바꿨습니다. 이때 (가로 픽셀 수, 세로 픽셀 수, 채널 수) 형상이 (채널 수, 가로 픽셀 수, 세로 픽셀 수) 형상으로 바뀝니다. 파이토치로 이미지를 처리할 때는 형상이 (채널 수, 가로 픽셀 수, 세로 픽셀 수) 순서여야 합니다. 이번 대회의 이미지 데이터는 32 x 32 x 3 형상 이므로 3 x 32 x 32로 바뀐다는 의미입니다.
여기에 배치가 추가된다면 (배치 크기, 채널 수, 가로 픽셀 수, 세로 픽셀 수)가 됩니다.
이어서 훈련 데이터셋과 검증 데이터셋을 만들어주겠습니다. 앞에서 정의한 ImageDataset() 클래스를 사용하면 됩니다.
dataset_train = ImageDataset(df=train, img_dir='train/', transform=transform)
dataset_valid = ImageDataset(df=valid, img_dir='train/', transform=transform)
df 파라미터에는 훈련 데이터(train)나 검증 데이터(valid)를, img_dir에는 이미지 데이터가 저장되어 있는 경로를, transform에는 transform 변환기를 전달합니다.
이로써 훈련용 이미지 데이터셋과 검증용 이미지 데이터셋을 만들었습니다.
- 데이터 로더 생성
데이터셋 다음으로는 데이터 로더를 생성해야 합니다. 데이터 로더는 지정한 배치 크기만큼씩 데이터를 불러오는 객체입니다. 묶음 단위로 훈련하는게 훨씬 빠르기 때문에, 딥러닝 모델을 훈련할 때는 주로 배치 단위로 데이터를 가져와 훈련합니다.
torch.utils.data의 DataLoader 클래스로 데이터 로더를 만들 수 있습니다.
from torch.utils.data import DataLoader # 데이터 로더 클래스
loader_train = DataLoader(dataset=dataset_train, batch_size=32, shuffle=True)
loader_valid = DataLoader(dataset=dataset_valid, batch_size=32, shuffle=False)
각 파라미터의 의미는 다음과 같습니다.
● dataset : 앞서 만든 이미지 데이터셋을 전달합니다.
● batch_size : 배치 크기입니다. 훈련 데이터와 검증 데이터 모두 한 번에 32개씩 불러오게 설정했습니다.
● shuffle : 데이터를 섞을지 여부를 결정합니다. 특정 데이터가 몰려 있을 경우에 대비해 훈련 데이터에서는 True를 전달해 데이터를 섞었습니다.
이상으로 훈련 시 사용할 데이터 로더와 검증 시 사용할 데이터 로더를 만들었습니다.
4. 모델 생성
기본적인 합성곱 신경망(CNN) 모델을 만들어보겠습니다.
(3, 32, 32) 형상의 이미지 데이터를 두 번의 합성곱과 풀링, 평탄화, 전결합 등을 거쳐 최종적으로 값이 0일 확률과 1일 확률을 구할 것입니다.
먼저 CNN 모델을 만드는 데 필요한 두 모듈을 불러옵니다.
import torch.nn as nn # 신경망 모듈
import torch.nn.functional as F # 신경망 모듈에서 자주 사용되는 함수
torch.nn은 파이토치에서 제공하는 신경망 모듈이고 torch.nn.funcrional은 신경망 모듈에서 자주 사용하는 함수들을 모아둔 모듈입니다.
CNN 모델은 nn.Module을 상속해 정의합니다. 그리고 순전파 후 결과를 반환하는 메서드인 forward()를 재정의합니다. 참고로 파이토치에서 nn.Module은 모든 신경망 모듈의 기반 클래스입니다.
class Model(nn.Module):
# ⓐ 신경망 계층 정의
def __init__(self):
super().__init__() # 상속받은 nn.Module의 __init__() 메서드 호출
# ⓑ 첫 번째 합성곱 계층
self.conv1 = nn.Conv2d(in_channels=3, out_channels=32,
kernel_size=3, padding=2)
# ⓒ 두 번째 합성곱 계층
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64,
kernel_size=3, padding=2)
# ⓓ 최대 풀링 계층
self.max_pool = nn.MaxPool2d(kernel_size=2)
# ⓔ 평균 풀링 계층
self.avg_pool = nn.AvgPool2d(kernel_size=2)
# ⓕ 전결합 계층
self.fc = nn.Linear(in_features=64 * 4 * 4, out_features=2)
# ⓖ 순전파 출력 정의
def forward(self, x):
x = self.max_pool(F.relu(self.conv1(x))) # ⓗ
x = self.max_pool(F.relu(self.conv2(x))) # ⓘ
x = self.avg_pool(x) # ⓙ
x = x.view(-1, 64 * 4 * 4) # ⓚ 평탄화
x = self.fc(x) # ⓛ
return x
ⓐ 초기화 메서드인 __init__()에서 모델에서 쓸 신경망 계층들을 정의합니다. 여기서 정의한 계층들을 ⓖ forward() 메서드에서 조합해 모델을 완성시키는데 사용됩니다.
합성곱 신경망 계층인 ⓑ와 ⓒ은 nn.Conv2d()로 정의할 수 있습니다. nn.Conv2d() 파라미터는 다음과 같습니다.
● torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0)
○ in_channels : 입력 데이터의 채널수
○ out_channels : 출력 데이터의 채널 수
○ kernel_size : 필터(커널) 크기
○ stride : 스트라이드 크기
○ padding : 패딩 크기
합성곱 계층 ⓑ를 예시로 설명하겠습니다. 앞서 ToTensor() 메서드로 이미지 형상을 (3,32,32)로 변환했으니 in_channels 파라미터에는 3을 전달합니다. 출력 데이터의 채널 수(out_channels)에는 원하는 값을 전달하면 됩니다. 여기서는 32를 전달했습니다. 나머지 값들은 원하는 대로 설정하면 됩니다. 스트라이드는 명시하지 않아 기본값인 1이 사용됩니다.
풀링 계층 ⓓ와 ⓔ의 크기는 모두 2로 설정했습니다. 이때, 스트라이드 크기를 설정하는 stride 파라미터를 명시하지 않으면 기본적으로 스트라이드 크기는 풀링 크기와 동일하게 설정됩니다.
ⓕ은 전결합 계층을 정의합니다. nn.Linear()로 구현할 수 있는데, in_features 파라미터에는 입력값 개수를, out_features 파라미터에는 최종 출력값 개수를 전달합니다. 최종 출력값은 0일 확률과 1일 확률이므로 그 개수는 2개입니다.
ⓖ 마지막으로 forward()는 순전파 출력을 계산하는 메서드입니다. 앞의 코드를 다시 살펴보겠습니다.
# ⓖ 순전파 출력 정의
def forward(self, x):
x = self.max_pool(F.relu(self.conv1(x))) # ⓗ
x = self.max_pool(F.relu(self.conv2(x))) # ⓘ
x = self.avg_pool(x) # ⓙ
x = x.view(-1, 64 * 4 * 4) # ⓚ 평탄화
x = self.fc(x) # ⓛ
return x
__init__()에서 정의한 합성곱 계층, 풀링 계층, 전결합 계층을 활용해 순전파 출력을 구합니다. ⓗ 먼저 첫 번째 합성곱 연상 -> ReLU 활성화 함수 -> 최대 풀링을 차례로 수행합니다. 이어서 ⓘ 는 두 번째 합성곱 연산 -> ReLU 활성화 함수 -> 최대 풀링을 수행합니다. 그다음 ⓙ 평균 풀링 -> ⓚ 평탄화 -> ⓛ 전결합을 거쳐 순전파가 마무리됩니다.
마지막으로, 이렇게 정의한 Model 클래스로 CNN 모델을 생성하여 device 장비에 할당하겠습니다. 현재 device는 GPU를 사용하도록 설정되어 있습니다.
model = Model().to(device)
GPU에 할당한 모델을 model 변수에 저장했습니다. model을 출력하면 모델의 전체 구조를 보여줍니다.
5. 모델 훈련
이제 손실 함수와 옵티마이저를 정의하고, 모델을 훈련시키겠습니다.
- 손실 함수 설정
손실 함수부터 정의하겠습니다. 신경망 모델 훈련은 가중치를 갱신하는 작업인데, 가중치 갱신은 예측값과 실젯값의 손실이 작아지는 방향으로 이루어집니다. 이때 손실값을 구하는 함수가 손실 함수입니다.
# 손실함수
criterion = nn.CrossEntropyLoss()
여기서는 손실 함수로 딥러닝 분류 문제에서 자주 사용하는 교차 엔트로피를 사용했습니다.
- 옵티마이저 설정
다음으로 옵티마이저를 설정합니다. 옵티마이저는 최적 가중치를 찾아주는 알고리즘입니다. 확률적 경사 하강법을 이용한 옵티마이저인 SGD로 설정했습니다.
# 옵티마이저
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
첫 번째 파라미터로 모델이 가진 파라미터들(model.parameters())을 전달했습니다. 모델의 파라미터들을 최적화하겠다는 의미입니다. lr은 학습률을 뜻합니다.
- 모델 훈련
본격적으로 모델을 훈련하겠습니다. 이미지용 딥러닝 모델 훈련 절차는 다음과 같습니다.
1. 데이터 로더에서 배치 크기만큼 데이터를 불러옵니다.
2. 불러온 이미지 데이터와 레이블(타깃값) 데이터를 장비(GPU 혹은 CPU)에 할당합니다.
3. 옵티마이저 내 기울기를 초기화합니다.
4. 신경망 모델에 입력 데이터(이미지)를 전달해 순전파하여 출력값(예측값)을 구합니다.
5. 예측값과 실제 레이블(타깃값)을 비교해 손실을 계산합니다.
6. 손실을 기반으로 역전파를 수행합니다.
7. 역전파로 구한 기울기를 활용해 가중치를 갱신합니다.
8. 1~7 절차를 반복 횟수만큼 되풀이합니다.
9. 1~8 절차를 에폭만큼 반복합니다.
이제 모델을 훈련시키겠습니다. 에폭은 10으로 지정하고, 하나의 에폭이 끝날 때마다 손실값을 출력하도록 했습니다.
epochs = 10 # 총 에폭
# ⓐ 총 에폭만큼 반복
for epoch in range(epochs):
epoch_loss = 0 # 에폭별 손실값 초기화
# ⓑ '반복 횟수'만큼 반복
for images, labels in loader_train:
# ⓒ 이미지, 레이블 데이터 미니배치를 장비에 할당
images = images.to(device)
labels = labels.to(device)
# ⓓ 옵티마이저 내 기울기 초기화
optimizer.zero_grad()
# ⓔ 순전파 : 이미지 데이터를 신경망 모델의 입력값으로 사용해 출력값 계산
outputs = model(images)
# ⓕ 손실 함수를 활용해 outputs와 labels의 손실값 계산
loss = criterion(outputs, labels)
# 현재 배치에서의 손실 추가
epoch_loss += loss.item() # ⓖ 역전파 수행
loss.backward()
# ⓗ 가중치 갱신
optimizer.step()
# ⓘ 훈련 데이터 손실값 출력
print(f'에폭 [{epoch+1}/{epochs}] - 손실값: {epoch_loss/len(loader_train):.4f}')
에폭을 거듭하면서 손실값이 점차 줄어드는 걸 보아 훈련이 제대로 이루어지고 있습니다.
이제 코드를 살펴보겠습니다
ⓐ 바깥 for문은 에폭 수(10)만큼 반복하며 ⓘ 에폭이 하나 끝날 때마다 손실값을 출력합니다.
ⓑ 안쪽 for문은 반복 횟수(= 데이터 로더의 길이 = 493번)만큼 반복되며, 매번 데이터 로더(loader_trian)로부터 배치 크기(32)만큼의 이미지와 레이블을 추출해 images와 labels 변수에 할당해놓습니다.
그러면 ⓒ에서 이미지와 레이블 데이터를 장비에 할당하여 훈련 시 GPU를 활용하도록 해둡니다.
ⓓ 는 옵티마이저에 저장된 기울기를 초기화하는 코드입니다.
ⓔ 그다음으로 모델에 이미지 데이터를 입력하여 순전파를 진행한 다음, ⓕ 그 결과(outputs)와 실제 레이블(labels)을 비교해 손실을 구합니다. model과 criterion은 앞에서 정의한 CNN 모델과 교차 엔트로피 오차 손실 함수입니다.
ⓖ 이어서 역전파를 수행합니다. 바꿔 말하면, 손실값을 바탕으로 신경망의 가중치(파라미터)에 기울기를 할당합니다.
ⓗ 마지막으로 기중치를 갱신합니다. 새로운 가중치는 아래의 공식으로 구할 수 있었습니다.
● 새로운 가중치 = 기존 가중치 - (학습률 x 기울기)
기존 가중치와 학습률은 이미 알고 있고, 기울기는 ⓖ에서 구했으므로 새로운 가중치를 구할 수 있습니다.
다시 ⓓ를 보겠습니다. optimizer.zero_grad()가 없다면 기존 기울기가 계속 누적됩니다. 기존 기울기를 초기화하는 작업이 필요한데, optimizer.zero_grad()가 이를 수행합니다.
이상으로 모델 훈련을 마쳤습니다.
6. 성능 검증
훈련이 끝났으니 검증 데이터를 이용해 평가지표인 ROC AUC 값을 구해보겠습니다. 먼저 사이킷런에서 제공하는 ROC AUC 계산 함수를 임포트하고 실젯값과 예측 확률 담을 리스트를 초기화합니다.
from sklearn.metrics import roc_auc_score # ROC AUC 점수 계산 함수 임포트
# 실제값과 예측 확률값을 담을 리스트 초기화
true_list = []
preds_list = []
두 값을 알아야 ROC AUC를 구할 수 있기 때문에 true_list에는 실젯값을, preds_list에는 예측 확률을 담을 것입니다.
이어서 검증 데이터로 모델 성능을 평가해보겠습니다.
ROC AUC의 최솟값은 0, 최댓값은 1입니다. 이번 베이스라인 모델의 결과는 0.9902입니다. 코드를 살펴보겠습니다.
먼저 ⓐ 모델을 평가 상태로 설정하고, ⓑ 성능 검정 시 기울기를 계산하지 않도록 합니다.
ⓒ 데이터 로더(loader_valid)에서 검증 데이터를 배치 단위로 뽑아 반복하면서, ⓓ 순전파하여 ⓔ 타깃 예측값을 구합니다. 이때 배치 크기는 32, 타깃값은 2개이므로 순전파 출력값인 outputs는 형상(32,2)인 텐서입니다.
첫 번째 열 은 타깃값 0에 대한 순전파 출력값이고, 두 번째 열은 타깃값 1에 대한 순전파 출력값입니다.
ROC AUC를 구하려면 각 타깃값의 확률을 먼저 구해야 합니다. 그래서 순전파 출력값인 outputs를 torch.softmax()에 넘겨 타깃값이 0일 확률과 1일 확률을 얻었습니다. 우리가 원하는 값은 타깃값이 1일 확률, 즉 이미지가 선인장을 포함할 확률입니다. 그래서 마지막 코드 [:,1]로 호출 결과 중 (0열이 아닌) 1열의 값을 불러왔습니다.
ⓔ의 outputs.cpu()와 ⓕ의 labels.cpu()는 각각 이전에 GPU에 할당했던 outputs와 labels의 데이터를 다시 CPU에 할당합니다. 그래야 roc_auc_score() 함수로 ROC AUC 값을 계산할 수 있습니다. roc_auc_score()는 파이토치가 아니라 사이킷런 함수이므로 GPU에 있는 데이터를 직접 사용하지 못합니다.
ⓖ 마지막으로 에폭별 예측 확률값(preds_list)과 실젯값(true_list)으로부터 검증 데이터의 ROC AUC 값을 계산해 출력합니다.
7. 예측 및 결과 제출
드디어 예측 후 제출하는 단계입니다. 먼저 테스트 데이터를 담은 데이터셋과 데이터 로더를 만들겠습니다. 이 데이터 로더의 배치 크기도 32로 하겠습니다.
dataset_test = ImageDataset(df=submission, img_dir='test/', transform=transform)
loader_test = DataLoader(dataset=dataset_test, batch_size=32, shuffle=False)
- 예측
테스트 데이터에서 타깃값이 1일 확률을 예측해보겠습니다. 검증 데이터로 모델 성능을 평가하는 코드와 비슷합니다. 다만 테스트 데이터에는 타깃값이 없어서 for문에 labels 변수는 할당하지 않았습니다.
model.eval() # 모델을 평가 상태로 설정
preds = [] # ⓐ 타깃 예측값 저장용 리스트 초기화
with torch.no_grad(): # 기울기 계산 비활성화
for images, _ in loader_test:
# 이미지 데이터 미니배치를 장비에 할당
images = images.to(device)
# 순전파 : 이미지 데이터를 신경망 모델의 입력값으로 사용해 출력값 계산
outputs = model(images)
# ⓑ 타깃값이 1일 확률(예측값)
preds_part = torch.softmax(outputs.cpu(), dim=1)[:, 1].tolist()
# ⓒ preds에 preds_part 이어붙이기
preds.extend(preds_part)
ⓐ의 preds는 타깃 예측 값을 담을 리스트입니다. 여기에 최종 제출할 값, 즉 타깃값이 1일 확률 (선인장을 포함할 확률)을 저장할 것입니다.
ⓑ에서 해당 배치 크기만큼 타깃값이 1일 확률을 구한 후 ⓒ에서 preds에서 이어붙입니다. 이렇게 해서 for문이 끝나면 preds 변수에 최종 예측 확률이 모두 저장되어 있습니다.
- 결과 제출
방금 구한 최종 예측 확률로 제출 파일을 만들겠습니다.
submission['has_cactus'] = preds
submission.to_csv('submission.csv', index=False
제출 전에 더는 필요가 없으니 훈련 이미지 데이터와 테스트 이미지 데이터를 모두 삭제해줍니다.
import shutil
shutil.rmtree('./train')
shutil.rmtree('./test')
shutill은 파일을 제어하는 파이썬의 고수준 모듈이며 rmtree()는 전달받은 디렉터리 전체를 삭제하는 메서드입니다.
이제 커밋 후 제출하면 0.9811의 점수가 나오는 것을 확인 할 수 있습니다.
'IT > 머신러닝&딥러닝' 카테고리의 다른 글
[머신러닝/딥러닝] 항공 사진 내 선인장 식별 경진대회 (3)_모델 성능 개선 (0) | 2023.01.30 |
---|---|
[머신러닝/딥러닝] 항공 사진 내 선인장 식별 경진대회 (1)_탐색적 데이터 분석 (0) | 2023.01.30 |
[머신러닝/딥러닝] 향후 판매량 예측 (3)_모델 성능 개선 (0) | 2022.11.28 |
[머신러닝/딥러닝] 향후 판매량 예측 (2)_베이스라인 모델 (0) | 2022.11.28 |
[머신러닝/딥러닝] 안전 운전자 예측 (1) (0) | 2022.11.07 |