Must Have - 머신러닝, 딥러닝 문제해결 전략__(신백균 저)
11장 항공 사진 내 선인장 식별 경진대회에 대한 내용
시드값 고정하는 이유: 여러번 실행해도 계속 같은 결과를 얻기 위함
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)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.enabled = False
if torch.cuda.is_available():
device = torch.device('cuda')
else:
device = torch.device('cpu')
내 컴퓨터에서는 cuda 할당
google colab에서 작업하였다. 경로 설정해준다. 이미 EDA 과정에서 압축은 해제했다.
import pandas as pd
data_path = '/content/drive/MyDrive/cactus/'
labels = pd.read_csv(data_path + 'train.csv')
submission = pd.read_csv(data_path+'sample_submission.csv')
훈련데이터와 검증데이터도 분리해 줘야 한다. sklearn의 train_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)
train과 valid set의 데이터 개수를 확인해주면
print(len(train))
print(len(valid))
15750 1750
이 출력된다.
데이터셋 클래스 정의/생성
import cv2
from torch.utils.data import Dataset
class ImageDataset(Dataset):
#생성자
def __init__(self, df, img_dir = './', transform = None):
super().__init__()
self.df = df
self.img_dir = img_dir
self.transform = transform
#데이터셋 크기 반환하는 메서드
def __len__(self):
return len(self.df)
#해당 인덱스 데이터 반환 메서드
def __getitem__(self,idx):
img_id = self.df.iloc[idx,0]
img_path = self.img_dir + img_id
image= cv2.imread(img_path)
image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB) #RGB로 바꿔줘야 한다.
label = self.df.iloc[idx,1]
if self.transform is not None:
image = self.transfr(image)
return image,label
데이터셋 클래스 정의가 완료되었으니, 데이터셋을 생성해준다.
from torchvision import transforms
transform = transforms.ToTensor()
dataset_train = ImageDataset(df=train, img_dir = '/content/drive/MyDrive/cactus/train/', transform = transform)
dataset_valid = ImageDataset(df=valid, img_dir = '/content/drive/MyDrive/cactus/valid/', transform = transform)
클래스에 df에는 데이터를 전달, img_dir는 이미지 디렉토리, transform에는 방금 만든 transform을 전달해서 인스턴스 생성
데이터로더 생성
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는 2의 제곱수로 설정하면 좋고, shuffle은 데이터 섞는 건데 훈련 데이터는 섞으면 좋지만 검증 데이터는 섞을 필요가 없다.
import torch.nn as nn
import torch.nn.functional as F
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
forward() 메서드: 순전파 출력을 계산한다.
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)
: 평탄화 , view가 데이터 모양을 바꿈
x = self.fc(x)
: 전결합
#손실함수
criterion = nn.CrossEntropyLoss() #교차엔트로피
#옵티마이저
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01)
epochs = 10
for epoch in range(epochs):
epoch_loss = 0
for images, lables in loader_train:
image = images.to(device)
labels = labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
epoch_loss += loss.item()
loss.backward()
optimizer.step()