R-CNN 논문 리뷰는 지난 포스팅을 참고하길 바란다: https://hyelimkungkung.tistory.com/25
프로젝트 개요
(0) 서론, 데이터 셋 소개: https://hyelimkungkung.tistory.com/27
(1) selective serarch: https://hyelimkungkung.tistory.com/28?category=935193
(2) fine tuning: https://hyelimkungkung.tistory.com/29
(3) linear svm, (4) 결론: https://hyelimkungkung.tistory.com/30
(2) Fine Tuning
이번의 개요는 다음과 같다.
(1) positive sample, negative sample 을 32:96의 비율로 배치 만들기
(2) pre train 한 알렉스 넷을 통해 훈련시키기
(3) 훈련한 모델을 저장하기
(1) positive sample, negative sample 을 32:96의 비율로 배치 만들기
custom.py
pytorch에서 커스텀 데이터 셋을 만들고, 이용하기 위해서는
torch.utils.data.Dataset과 torch.utils.data.DataLoader를 주로 사용한다.
dataset은 말 그대로 데이터 셋 클래스를 만드는 것이고, dataloader는 데이터셋을 배치별로 로드하는 역할이다
본 프로젝트에서 사용한 내용은 아래와 같다.
필요한 라이브러리
from random import random
import torch
from torch.utils.data import Sampler
from torch.utils.data import Dataset
class CustomDataset
class CustomDataset(Dataset):
def __init__(self):
self.x_data = train_images
self.y_data =[ [i] for i in train_labels]
def __len__(self):
return len(self.x_data)
def __getitem__(self, idx):
x = torch.FloatTensor(self.x_data[idx])
y = torch.LongTensor(self.y_data[idx])
return x,y
만약 본인이 custom dataset을 만들고 싶다면 3 함수만 정의해주면 된다
__init__ , __len__, __getitem__
하나씩 보도록 하자
우선 torch.utils.data.Dataset 클래스를 상속받는다
파이썬에서 상속은 class 상속받을클래스명 (상속할 클래스명) 으로 이루어진다
class CustomDataset(Dataset):
파이썬에서 상속은 class 상속받을클래스명 (상속할 클래스명) 으로 이루어진다
먼저 __init__은
def __init__(self):
self.x_data = train_images
self.y_data =[ [i] for i in train_labels]
이렇게 생긴 놈이다. class 에서 __init__은 객체가 생성되면 바로 실행하는 함수를 의미한다.
train_images나 train_labels는 글로벌 변수다.
이 init을 통해서 우리가 이용할 데이터를 이케 등록한다? 정도로 생각하면 될 것 같다.
다음으로 __len__은 전체 데이터의 수다.
self.x_data나 self.y_data 수는 당근 똑같아야 하니, x_data의 길이가 아니라 y_data를 사용해도 무방할 것이다
def __len__(self):
return len(self.x_data)
마지막으로 __iter__은 매 iter마다 데이터를 가져오는 애다.
아까 __init__에서 전체 데이터를 등록했으니 거기서 하나씩 가져오게 하는 녀석인거다
이 함수 덕분에 우리가 for 문을 통해서 데이터 하나씩 읽어올 수 있는 것이다
def __getitem__(self, idx):
x = torch.FloatTensor(self.x_data[idx])
y = torch.LongTensor(self.y_data[idx])
return x, y
필요에 따라 더 많은 값을 return 할 수 있을 것이다. 만약 그랬다면 변수 생성에 주의하자.
class customsampler
## custom batch 만들기 32개의 positive와 96개의 negative
import random
import numpy as np
from torch.utils.data import Sampler
class customsampler(Sampler):
def __init__(self, data, positive_num, negative_num):
self.data = data
self.total = len(self.data)
self.positive_num = positive_num
self.negative_num = negative_num
self.batch_size = self.positive_num + self.negative_num
self.iter = self.total // self.batch_size
self.positive_list = len([i for i, predict in enumerate(self.data) if predict != 0 ])
self.idx = list(range(self.total))
def __iter__(self):
batch = list()
for i in range(self.iter):
temp = np.concatenate((random.sample(self.idx[:self.positive_list], self.positive_num),
random.sample(self.idx[self.positive_list:], self.negative_num)))
random.shuffle(temp)
batch.extend(temp)
return iter(batch)
def __len__(self) -> int:
return self.iter * self.batch_size
def get_num_batch(self) -> int:
return self.iter
fine tuning에서 우리는 32 개의 positive sample과 96개의 negative sample로 하나의 배치를 구성해야한다.
그러기 위해서 sampler 클래스를 상속하고 있는 것을 알 수 있다.
sampler는 이후 dataload의 파라미터로 들어가게 된다.
애초부터 positive_data 리스트와 negative_data 리스트를 만들어뒀던 게 아니기 때문에 나는 이 클래스를 통해서 구분을 만들었다.
-> 은 python 에서 function annotation 이란 것이다. 실은 이번에 처음 알게 됐다.
다른 언어를 한 번 다뤄본 사람이라면 파이썬이 가진 특징 중 하나가 바로 변수를 선언할 때 변수 타입을 지정하지 않는 것이다. C 할때는 항상 int i 이런 식으로 선언했었고... javascript에서도 var, const 이렇게 선언을 했었는데 파이썬은 그런게 아무 필요가 없다. 그래서 솔직히 C하다가 파이썬으로 돌아왔을 때는 이 부분이 제일 불편했다. 뭔지 명시적으로 해놓고 시작하지 않으니까 내가 하나씩 다 나중에 확인해야 하는 불편쓰...
하여튼 그런 점에서 -> 은 def를 통해서 만든 사용자 지정 함수의 return 값의 타입을 명시해놓는 기능인 것이다.
위에서 __len__에 -> int 라고 쓰인 것은 결과값은 int라는 것을 말해준 것이다.
실제로 코드에는 영향을 주지 않고, 주석같으 느낌이라고 한다.
참고로 여기서 헷갈릴 만한 건 iter, epoch, 그리고 batch인데.
전체 dataset을 batch 사이즈로 나눈만큼 iter 해서 학습고, iter 한 번에는 batch 사이즈만큼만 훈련한다.
이 과정을 epoch만큼 반복하는 거다. 그러면 배치의 수가 iter이 되는 것을 이해할 수 있을 것이다.
(2) pre train 한 알렉스 넷을 통해 훈련시키기
fineTuning.py
import torch
import torchvision.models as models
import torch.nn as nn
from torch.utils.data import DataLoader
from selectiveSearch import region_proposal
from customdata import CustomSampler,CustomDataset
positive_num = 32
negative_num = 96
batch_size = positive_num + negative_num
train_images, train_labels = region_proposal('finetune')
print('region_proposal ended')
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = models.alexnet(pretrained=True)
num_features = model.classifier[6].in_features
model.classifier[6] = nn.Linear(num_features, 4)
model = model.to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.0001)
dataset = CustomDataset(train_images, train_labels)
sampler = CustomSampler(train_labels, positive_num, negative_num)
dataloader = DataLoader(dataset, batch_size=batch_size, sampler=sampler)
total_batch = len(dataloader)
epochs = 15
for epoch in range(epochs):
avg_cost = 0.0
for num, data in enumerate(dataloader):
imgs, labels = data
imgs = imgs.to(device)
labels = labels.to(device)
model.train()
optimizer.zero_grad()
print(imgs.shape)
out = model(imgs.permute(0, 3, 1,2)) ## pytorch의 경우 인자의 순서가 (batchsize, channel, height, width) 우리는 보통 (batchsize,height,width,channel)이라고 생각하는데
labels = labels.squeeze() ## crossentropy input size 가 (N,C) 배치사이즈, 클래스 수 target size는 (N) 이 돼야해서 squeeze로 차원 변경
loss = criterion(out, labels)
loss.backward()
optimizer.step()
avg_cost += loss / total_batch
print(f'[Epoch:{epoch + 1}] cost = {avg_cost}')
print('Learning Finished!')
보면 pre trained 한 알렉스 넷 classifer의 마지막 layer output node수만 바꾼 걸 알 수 있다.
우리가 classify하고 싶은 3가지의 class 에 background 하나까지 더해서 총 4개다.
fine tuning에선 softmax로 학습하고, linear SVM에서는 class 별로 linear svm을 시행한다.
그런데 참고한 구현에서 2개의 output node가 필요한 경우만 있어서 씁 어렵다 class 별로 하는게..
근데 사실 그때는 gpu의 한계 때문에 class specific linear SVM을 한 거니까 그냥 softmax를 써도 되지 않을까? 하는 마음...
아 그리고 보면 model.train()이 있는데, model.eval()과 함께 알아두자.
model.eval()은 evaluate 모드? 정도로 생각하면 될 것 같다. 그래서 저때는 dropout 등을 시행하지 않는다.
model.eval()과 with torch.no_grad 를 함께 쓴다고 한다.
(3) 훈련한 모델을 저장하기
torch.save(model.state_dict(), 'data/content/BCCD/fine_tuned.pth') ## 가중치들 저장
'딥러닝 > 프로젝트' 카테고리의 다른 글
[Kaggle] CT Medical Image - (0) 서론, 액션 플랜 (0) | 2021.09.25 |
---|---|
[개인]R-CNN 을 이용한 BCCD type 분류 -(3)linear svm, (4)결론 (0) | 2021.06.29 |
[개인]R-CNN 을 이용한 BCCD type 구분 -(1) selective search (0) | 2021.06.23 |
[개인]R-CNN 을 이용한 BCCD type 구분 -(0) 서론, 데이터 셋 소개 (0) | 2021.06.22 |
[개인]합성곱 신경망을 통한 이미지 분류 (0) | 2021.04.26 |
댓글