전체 프로젝트 구조
data/
experiments/
model/
net.py
data_loader.py
train.py
evaluate.py
search_hyperparams.py
synthesize_results.py
evaluate.py
utils.py
- model / net.py: 뉴럴 네트워크, loss function, evaluation metrics를 지정
- model / data_loader.py: 네트워크에 데이터를 feeding
- train.py: 메인 training loop
- evaluate.py: 메인 evaluation loop
- utils.py: hyperparams / logging / storingmodel 하는 유틸리티 함수
Custom Model 구조
- nn.Module을 상속받아 오버라이딩
- . __init__(): 객체가 생성될 때 자동 호출, 속성 값을 초기화
- . super(CustomModel, self).__init__(): 부모 클래스에서 받아오기
- . forward : prediction 값을 반환
예시
# module import
import torch
import torch.nn as nn
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
## 생략하고 super().__init__() 로만 작성해도 됨.
## 현재 커스텀모델에서 정의한 인자 외의 인자들이 있다면, 부모클래스인 nn.Module에서(self)에서 받아오기
self.conv1 = nn.Conv2d(3, 16, 3, padding='same')
self.conv2 = nn.Conv2d(16, 32, 3, padding='same')
self.conv3 = nn.Conv2d(32, 64, 3, padding='same')
self.conv4 = nn.Conv2d(64, 128, 3, padding='same')
self.conv5 = nn.Conv2d(128, 256, 3, padding='same')
self.pool = nn.MaxPool2d(2, 2)
self.dropout = nn.Dropout(0.25)
self.fc1 = nn.Linear(7*7*256, 1024)
self.fc2 = nn.Linear(1024, 128)
self.fc3 = nn.Linear(128, 10)
def forward(self, x):
x = torch.relu(self.conv1(x))
x = self.pool(x)
x = torch.relu(self.conv2(x))
x = self.pool(x)
x = torch.relu(self.conv3(x))
x = self.pool(x)
x = torch.relu(self.conv4(x))
x = self.pool(x)
x = torch.relu(self.conv5(x))
x = self.pool(x)
x = x.view(-1, 7*7*256)
x = self.dropout(x)
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x
model = Net()
# Use GPU
model = model.cuda()
print(model)
모델 정보 확인
1. model.state_dict()
print("Model`s state_dict:")
for param_tensor in model.state_dict():
print(param_tensor, "\t", model.state_dict()[param_tensor].size())
2. torchsummary
from torchsummary import summary
summary(model, (input_size))
3. named_modules()
for name, layer in CustomMol.named_modules():
print(name, layer)
Custom Dataset 만들기
- torch.utils.data.Dataset 클래스를 상속받는 자식 클래스 생성
- 다음 세 가지 메소드가 필요함
- __init__(self, 인수들): 데이터셋을 처음 선언할 때 호출되는 함수 (path, transform등 인수로 받음)
- __len__(self): 데이터 로데에서 내부적으로 사용됨
- __getitem__(self, idx): 데이터 로더에서 내부적으로 사용됨
데이터셋 - 예시
import torch
from torch.utils.data import Dataset, DataLoader
class SimpleDataset(Dataset):
def __init__(self, t):
self.t = t
def __len__(self):
return self.t
def __getitem__(self, idx):
return torch.LongTensor([idx])
if __name__ == "__main__":
dataset = SimpleDataset(t=5)
print(len(dataset))
it = iter(dataset)
for i in range(10):
print(i, next(it))
데이터 로더 - 예시
if __name__ == "__main__":
dataset = SimpleDataset(t=5)
dataloader = DataLoader(dataset=dataset,
batch_size=2,
shuffle=True,
drop_last=False)
for epoch in range(2):
print(f"epoch : {epoch} ")
for batch in dataloader:
print(batch)
- 데이터 로더에는 배치 사이즈 및 셔플을 지정
- drop_last: 배치를 돌고 남은 샘플들
커스텀 데이터셋
import glob
import torch
from torchvision import transforms
from PIL import Image
from torch.utils.data import Dataset, DataLoader
class catdogDataset(Dataset):
def __init__(self, path, train=True, transform=None):
self.path = path
if train:
self.cat_path = path + '/cat/train'
self.dog_path = path + '/dog/train'
else:
self.cat_path = path + '/cat/test'
self.dog_path = path + '/dog/test'
self.cat_img_list = glob.glob(self.cat_path + '/*.png')
self.dog_img_list = glob.glob(self.dog_path + '/*.png')
self.transform = transform
self.img_list = self.cat_img_list + self.dog_img_list
self.class_list = [0] * len(self.cat_img_list) + [1] * len(self.dog_img_list)
def __len__(self):
return len(self.img_list)
def __getitem__(self, idx):
img_path = self.img_list[idx]
label = self.class_list[idx]
img = Image.open(img_path)
if self.transform is not None:
img = self.transform(img)
return img, label
- __init__: path / train 여부 / transform을 입력 받음
- __len__: 전체 이미지 개수를 return
- __getitem__: idx번째 이미지를 PIL.Image로 열고 transform 적용
-> torchvision.transforms은 PIL Image를 인풋으로 받기 때문에 Image.open(img_path) 사용
-> transform에서 ToTensor() 이전에 transforms.Resize, CenterCrop, RandomHorizontalFlip 등 적용 가능
if __name__ == "__main__":
transform = transforms.Compose(
[
transforms.ToTensor(),
]
)
dataset = catdogDataset(path='./cat_and_dog', train=True, transform=transform)
dataloader = DataLoader(dataset=dataset,
batch_size=1,
shuffle=True,
drop_last=False)
for epoch in range(2):
print(f"epoch : {epoch} ")
for batch in dataloader:
img, label = batch
print(img.size(), label)
정형데이터 예시
from torch.utils.data import Dataset
from sklearn.preprocessing import StandardScaler
class CustomDataset(Dataset):
def __init__(self, df, target='target', normalize=True):
super(CustomDataset, self).__init__()
self.x = df.drop(target, 1)
# 데이터 표준화
if normalize:
scaler = StandardScaler()
self.x = pd.DataFrame(scaler.fit_transform(self.x))
self.y = data['target']
# 텐서 변환
self.x = torch.tensor(self.x.values).float()
self.y = torch.tensor(self.y).float()
def __len__(self):
return len(self.x)
def __getitem__(self, idx):
x = self.x[idx]
y = self.y[idx]
return x, y
dataset = CustomDataset(df, 'target', True)
사전 학습된 모델 사용
from torch import nn
from torchvision import models
## Custom Model 정의
class CustomModel(nn.Module):
def __init__(self):
super(CustomModel, self).__init__()
self.기존모델 = models.사전학습모델(pretrained=True)
self.linear_layers = nn.Linear(output, num_classes)
def forward(self, x):
x = self.기존모델(x)
return self.linear_layers(x)
## cuda 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
## accuracy 함수 정의
def binary_acc(y_pred, y_test):
y_pred_tag = torch.round(torch.sigmoid(y_pred))
correct_results_sum = (y_pred_tag == y_test).sum().float()
acc = correct_results_sum/y_test.shape[0]
acc = torch.round(acc * 100)
return acc
## Model Training (Transfer Learning)
my_model = Custom_Model()
my_model = my_model.to(device)
# freeze
for param in my_model.parameters():
param.requires_grad = False ## 파라미터 freeze
for param in my_model.linear_layers.parameters():
param.requires_grad = True ## ## 마지막 Linear 층만 학습
## Loss 및 Optimizer 함수 정의
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(my_model.parameters(), lr=LEARNING_RATE)
## Training !!
for epoch in range(1, EPOCHS+1):
epoch_loss = 0
epoch_acc = 0
for X_batch, y_batch in DataLoader:
X_batch, y_batch = X_batch.to(device), y_batch.to(device).type(torch.cuda.FloatTensor)
optimizer.zero_grad()
y_pred = my_model(X_batch)
loss = criterion(y_pred, y_batch.unsqueeze(1))
acc = binary_acc(y_pred, y_batch.unsqueeze(1))
loss.backward()
optimizer.step()
epoch_loss += loss.item()
epoch_acc += acc.item()
## Checkpoints 및 Early Stopping 설정 추가 가능
print(f'Epoch {e+0:03}: | Loss: {epoch_loss/len(dataloader):.5f} | Acc: {epoch_acc/len(dataloader):.3f}')
훈련 루프
output_batch = model(train_batch) # 모델 output 계산
loss = loss_fn(output_batch, labels_batch) # loss 계산
optimizer.zero_grad() # gradients 초기화
loss.backward() # gradients 계산
optimizer.step() # 가중치 업데이트
손실 함수
# torch.nn 모듈에에서 loss function 가져오기
loss_fn = nn.CrossEntropyLoss()
loss = loss_fn(out, target)
커스텀 손실 함수
def myCrossEntropyLoss(outputs, labels):
batch_size = outputs.size()[0]
# 아웃풋의 배치 사이즈
outputs = F.log_softmax(outputs, dim=1)
# 로그 소프트맥스 계산
outputs = outputs[range(batch_size), labels]
return -torch.sum(outputs)/num_examples
Optimizer
# torch.optim
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
optimizer = torch.optim.Adam(model.parameters(), lr = 0.0001)
정확도 계산
def accuracy(out, labels):
outputs = np.argmax(out, axis=1)
return np.sum(outputs==labels)/float(labels.size)
metrics = { 'accuracy': accuracy,
##add your own custom metrics,
}
모델 저장 및 로드
state = {'epoch': epoch + 1,
'state_dict': model.state_dict(),
'optim_dict' : optimizer.state_dict()}
utils.save_checkpoint(state,
is_best=is_best,
# True if this is the model with best metrics
checkpoint=model_dir)
# path to folder
# 모델 로드
utils.load_checkpoint(restore_path, model, optimizer)
반응형
'DL > Pytorch' 카테고리의 다른 글
[PyTorch] Yelp 데이터로 커스텀 데이터셋 만들기 (0) | 2023.06.21 |
---|---|
[PyTorch] Tensorboard 사용하기 (colab) (0) | 2022.11.26 |
[PyTorch] Weight Initialization (기울기 초기화) (0) | 2022.11.25 |
[PyTorch] Transformer 코드로 이해하기 (0) | 2022.11.22 |
[PyTorch] Tensor 차원, 크기 다루기 (0) | 2022.11.21 |