728x90
합성곱 신경망2¶
이미지 분류를 위한 신경망¶
input data로 이미지를 사용한 분류는 특정 대상이 영상 내에 존재하는지 여부를 판단하는 것
6.1.1 LeNet¶
In [13]:
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
In [64]:
import torch
import torchvision
from torch.utils.data import DataLoader, Dataset
from torchvision import transforms
from torch.autograd import Variable
from torch import optim
import torch.nn as nn
import torch.nn.functional as F
import os
import cv2
from PIL import Image
from tqdm import tqdm_notebook as tqdm
import random
from matplotlib import pyplot as plt
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
device
Out[64]:
device(type='cuda', index=0)
In [65]:
class ImageTransform():
def __init__(self, resize, mean, std):
self.data_transform = {
'train': transforms.Compose([
transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),
# 주어진 resize 크기로 resize하고 이미지를 scale만큼(0.5 ~ 1.0 사이로)
# random하게 crop한다.
transforms.RandomHorizontalFlip(),
# 주어진 확률로 수평반전을 시킨다. default p는 0.5
transforms.ToTensor(),
# image를 tensor로 change
# 보통 PIL은 H, W, C 순으로 이미지를 표현
# tensor는 C, H, W 순으로 이미지를 표현. 그래서 change해줘야한다.
transforms.Normalize(mean, std)
# tensor를 일반화하는데 주어진 mean, std에 맞춰 평균, 표준편차를 설정
# OpenCV는 RGB가 아니라 BGR이다. 이 부분은 나중에 조심해야 한다.
]),
'val': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(resize),
# resize 크기만큼 centercrop을 진행
transforms.ToTensor(),
transforms.Normalize(mean, std)
]) # train, valid로 두 전처리 과정을 나눠서 설정
}
def __call__(self, img, phase):
return self.data_transform[phase](img)
In [66]:
cat_directory = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Cat'
dog_directory = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Dog'
# 해당 경로에 각각 251장의 데이터들이 존재.
cat_images_filepaths = sorted([os.path.join(cat_directory, f) for f in os.listdir(cat_directory)])
# 고양이 이미지 사진들의 경로를 하나하나 붙여서 cat_images_filepath에 저장하고 정렬까지 진행
dog_images_filepaths = sorted([os.path.join(dog_directory, f) for f in os.listdir(dog_directory)])
images_filepaths = [*cat_images_filepaths, *dog_images_filepaths]
# cat_images_filepath, dog_images_filepaths를 unpack하고 하나의 list로 붙여서 images_fliepahts에 저장
correct_images_filepaths = [i for i in images_filepaths if cv2.imread(i) is not None]
# 유효한 이미지 데이터들의 경로만 유지한다.
print(len(correct_images_filepaths))
random.seed(42)
random.shuffle(correct_images_filepaths) # 이미지 데이터들을 랜덤하게 shuffle
#train_images_filepaths = correct_images_filepaths[:20000] #성능을 향상시키고 싶다면 훈련 데이터셋을 늘려서 테스트해보세요
#val_images_filepaths = correct_images_filepaths[20000:-10] #훈련과 함께 검증도 늘려줘야 합니다
train_images_filepaths = correct_images_filepaths[:400] # 400장의 train dataset
val_images_filepaths = correct_images_filepaths[400:-10] # 92장의 valid dataset
test_images_filepaths = correct_images_filepaths[-10:] # 10장의 test dataset
print(len(train_images_filepaths), len(val_images_filepaths), len(test_images_filepaths))
502 400 92 10
In [67]:
def display_image_grid(images_filepaths, predicted_labels=(), cols=5):
rows = len(images_filepaths) // cols
# 총 행의 개수를 구하는 부분
figure, ax = plt.subplots(nrows=rows, ncols=cols, figsize=(12, 6))
# subplot은 figure와 ax를 return하는데 figure은 전체를 의미하고 ax는
# subplot들 하나하나를 의미하는듯
for i, image_filepath in enumerate(images_filepaths):
image = cv2.imread(image_filepath)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # opencv는 bgr로 읽기 때문에
# plt 로 표현하기 위해서 rgb로 convert해줘야한다.
# import pdb;pdb.set_trace()
true_label = os.path.normpath(image_filepath).split(os.sep)[-2]
# normpath는 해당 경로의 절대경로를 불러다준다.
# 절대경로로 바꾸면 /가 \가 되고 이를 이제 os.sep를 하면 \를 기준으로 나눠준다
# 마지막에서 두번째 값을 true_label로 저장하는데 이는 폴더명 위치가 될 것이다
# Cat, Dog 이런식으로 폴더안에 파일이 존재하니까
predicted_label = predicted_labels[i] if predicted_labels else true_label
# predicted_labels을 인자로 받는데 받은 값이 존재한다면 그 값을 그대로 사용하고
# 없으면 true_label을 사용한다는 코드
color = "green" if true_label == predicted_label else "red"
ax.ravel()[i].imshow(image)
ax.ravel()[i].set_title(predicted_label, color=color)
ax.ravel()[i].set_axis_off()
plt.tight_layout()
plt.show()
In [68]:
print(test_images_filepaths[0])
print(os.path.normpath(test_images_filepaths[0]))
print(os.path.normpath(test_images_filepaths[0]).split(os.sep))
/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Cat/cat.145.jpg /content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Cat/cat.145.jpg ['', 'content', 'drive', 'MyDrive', 'deel_learning_pytorch_book', 'deep learning pytorch book', '6장', 'data', 'dogs-vs-cats', 'Cat', 'cat.145.jpg']
In [69]:
display_image_grid(test_images_filepaths)
In [70]:
class DogvsCatDataset(Dataset):
def __init__(self, file_list, transform=None, phase='train'):
self.file_list = file_list
self.transform = transform
self.phase = phase
def __len__(self):
return len(self.file_list)
def __getitem__(self, idx):
img_path = self.file_list[idx]
img = Image.open(img_path)
img_transformed = self.transform(img, self.phase)
label = img_path.split('/')[-1].split('.')[0]
if label == 'dog':
label = 1
elif label == 'cat':
label = 0
return img_transformed, label
In [71]:
size = 224
mean = (0.485, 0.456, 0.406)
std = (0.229, 0.224, 0.225)
batch_size = 16
In [72]:
train_dataset = DogvsCatDataset(train_images_filepaths, transform=ImageTransform(size, mean, std), phase='train')
val_dataset = DogvsCatDataset(val_images_filepaths, transform=ImageTransform(size, mean, std), phase='val')
# train, val에 맞춰 전처리를 진행한다.
index = 0
print(train_dataset.__getitem__(index)[0].size())
print(train_dataset.__getitem__(index)[1])
torch.Size([3, 224, 224]) 0
In [73]:
for j in iter(lambda: np.random.randint(0, 10), 2):
print(j)
# 이렇게 iter를 사용하면 2가 나오기 전까지 실행하다가 2가 나오면 멈춘다.
8
In [74]:
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
dataloader_dict = {'train': train_dataloader, 'val': val_dataloader}
# dataloader를 dictionary로 만들어 준다.
batch_iterator = iter(train_dataloader)
# iter함수는 반복가능한 train_dataloader를 반복해서 불러준다.
# next를 사용해 그다음 값을 불러준다.
# 근데 이런 방식이 아니라 종료 값을 뒤에다 넣어줄 수 있는데 들어가면 그 종료값을 만나는 경우 멈춘다.
# train_dataloader의 return 값은 input, label이기 때문에 아래처럼 출력가능
inputs, label = next(batch_iterator)
print(inputs.size())
print(label)
torch.Size([16, 3, 224, 224]) tensor([1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0])
In [75]:
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.cnn1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=0)
self.relu1 = nn.ReLU()
self.maxpool1 = nn.MaxPool2d(kernel_size=2)
# maxpool의 kernel_size만큼 input을 나눈 값이 그 다음 feature map 크기가 된다.
self.cnn2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, stride=1, padding=0)
self.relu2 = nn.ReLU() # activation
self.maxpool2 = nn.MaxPool2d(kernel_size=2)
self.fc1 = nn.Linear(32*53*53, 128)
self.relu5 = nn.ReLU()
self.fc2 = nn.Linear(128, 2)
self.output = nn.Softmax(dim=1)
def forward(self, x):
out = self.cnn1(x)
out = self.relu1(out)
out = self.maxpool1(out)
out = self.cnn2(out)
out = self.relu2(out)
out = self.maxpool2(out)
out = out.view(out.size(0), -1)
out = self.fc1(out)
out = self.fc2(out)
out = self.output(out)
return out
In [76]:
model = LeNet()
print(model)
LeNet( (cnn1): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1)) (relu1): ReLU() (maxpool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (cnn2): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1)) (relu2): ReLU() (maxpool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (fc1): Linear(in_features=89888, out_features=128, bias=True) (relu5): ReLU() (fc2): Linear(in_features=128, out_features=2, bias=True) (output): Softmax(dim=1) )
In [77]:
# !pip install torchsummary
import torch, gc
gc.collect()
torch.cuda.empty_cache()
from torchsummary import summary
model.to(device)
summary(model, input_size=(3, 224, 224))
---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Conv2d-1 [-1, 16, 220, 220] 1,216 ReLU-2 [-1, 16, 220, 220] 0 MaxPool2d-3 [-1, 16, 110, 110] 0 Conv2d-4 [-1, 32, 106, 106] 12,832 ReLU-5 [-1, 32, 106, 106] 0 MaxPool2d-6 [-1, 32, 53, 53] 0 Linear-7 [-1, 128] 11,505,792 Linear-8 [-1, 2] 258 Softmax-9 [-1, 2] 0 ================================================================ Total params: 11,520,098 Trainable params: 11,520,098 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.57 Forward/backward pass size (MB): 19.47 Params size (MB): 43.95 Estimated Total Size (MB): 63.99 ----------------------------------------------------------------
In [78]:
def count_parameters(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f'The model has {count_parameters(model):,} trainable parameters')
The model has 11,520,098 trainable parameters
In [79]:
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
criterion = nn.CrossEntropyLoss()
In [80]:
model = model.to(device)
criterion = criterion.to(device)
In [81]:
from tqdm.auto import tqdm
def train_model(model, dataloader_dict, criterion, optimizer, num_epoch):
since = time.time()
best_acc = 0.0
for epoch in range(num_epoch):
print('Epoch {}/{}'.format(epoch + 1, num_epoch))
print('-'*20)
for phase in ['train', 'val']:
if phase == 'train':
model.train()
else:
model.eval() # val인 경우 model.eval을 사용
# 이 경우 자동으로 gradient update되는걸 끊어준다.
epoch_loss = 0.0
epoch_corrects = 0
for inputs, labels in tqdm(dataloader_dict[phase]):
inputs = inputs.to(device)
labels = labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'):
# torch.set_grad_enabled를 하면 기울기를 구할 수 있다.
# requires_grad가 true가 된다.
# val의 경우 기울기를 못구하게 막아야 하니까 False가 들어갈 것!!
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
# softmax를 하면서 고양이, 개의 확률 중 더 큰 값을 뽑아낸다.
# 옆에 1은 dimension을 의미
# 즉 batch_size에 맞춰 이미지가 들어올텐데 각 이미지의 예측 값을 구해준다.
loss = criterion(outputs, labels)
# label과 outputs의 crossentropyloss를 구한다.
if phase == 'train':
loss.backward()
optimizer.step()
epoch_loss += loss.item() * inputs.size(0)
# inputs.size는 batch_size를 의미한다.
# 우리는 crossentropy를 정의할 때 매개변수로 reduction을 선언하지 않았다.
# 이 reduction의 default는 "mean"인데 즉 batch_size만큼의 loss의 평균을
# return해준다. 그래서 이를 수정하기 위해서 input.size(0)를 곱해
# 오차의 총합을 더해준다.
epoch_corrects += torch.sum(preds == labels.data)
epoch_loss = epoch_loss / len(dataloader_dict[phase].dataset)
# 여기서 한 epoch의 평균 loss를 구한다.
epoch_acc = epoch_corrects.double() / len(dataloader_dict[phase].dataset)
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = model.state_dict()
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(
time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))
return model
In [82]:
import time
num_epoch = 10
model = train_model(model, dataloader_dict, criterion, optimizer, num_epoch)
Epoch 1/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6871 Acc: 0.5525
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.7093 Acc: 0.5109 Epoch 2/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6885 Acc: 0.5500
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6993 Acc: 0.5435 Epoch 3/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6738 Acc: 0.5700
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6823 Acc: 0.5761 Epoch 4/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6566 Acc: 0.6175
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6818 Acc: 0.5652 Epoch 5/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6702 Acc: 0.5750
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6766 Acc: 0.5870 Epoch 6/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6618 Acc: 0.6000
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6708 Acc: 0.6087 Epoch 7/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6585 Acc: 0.6200
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6573 Acc: 0.6304 Epoch 8/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6413 Acc: 0.6450
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6737 Acc: 0.5761 Epoch 9/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6526 Acc: 0.6350
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6525 Acc: 0.6522 Epoch 10/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6404 Acc: 0.6500
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6999 Acc: 0.5870 Training complete in 0m 46s Best val Acc: 0.652174
In [87]:
import pandas as pd
id_list = []
pred_list = []
_id=0
with torch.no_grad():
for test_path in tqdm(test_images_filepaths):
img = Image.open(test_path)
_id =test_path.split('/')[-1].split('.')[1]
transform = ImageTransform(size, mean, std)
img = transform(img, phase='val')
img = img.unsqueeze(0)
img = img.to(device)
print(img.shape)
model.eval()
img = img.to(device)
outputs = model(img)
preds = F.softmax(outputs, dim=1)[:, 1].tolist()
# 이 preds는 진짜 그냥 예측한 값을 Softmax를 적용해 고양이, 개일 확률을 구한 코드다.
# 이 값중 1 인덱스 값만 list로 만들어서 preds에 저장한 것
# 그래서 이 값이 0.5보다 크다면 개, 작다면 고양이라고 볼 수 있는 것!!
id_list.append(_id)
pred_list.append(preds[0])
res = pd.DataFrame({
'id': id_list,
'label': pred_list
})
res.sort_values(by='id', inplace=True)
res.reset_index(drop=True, inplace=True)
res.to_csv('/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/LesNet.csv', index=False)
0%| | 0/10 [00:00<?, ?it/s]
torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224]) torch.Size([1, 3, 224, 224])
In [88]:
res.head(10)
Out[88]:
id | label | |
---|---|---|
0 | 109 | 0.639914 |
1 | 145 | 0.561803 |
2 | 15 | 0.648557 |
3 | 162 | 0.552288 |
4 | 167 | 0.532938 |
5 | 200 | 0.652502 |
6 | 210 | 0.678951 |
7 | 211 | 0.714540 |
8 | 213 | 0.465073 |
9 | 224 | 0.685055 |
In [89]:
class_ = classes = {0:'cat', 1:'dog'}
def display_image_grid(images_filepaths, predicted_labels=(), cols=5):
rows = len(images_filepaths) // cols
figure, ax = plt.subplots(nrows=rows, ncols=cols, figsize=(12, 6))
for i, image_filepath in enumerate(images_filepaths):
image = cv2.imread(image_filepath)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
a = random.choice(res['id'].values)
label = res.loc[res['id'] == a, 'label'].values[0]
if label > 0.5:
label = 1
else:
label = 0
ax.ravel()[i].imshow(image)
ax.ravel()[i].set_title(class_[label])
ax.ravel()[i].set_axis_off()
plt.tight_layout()
plt.show()
In [90]:
display_image_grid(test_images_filepaths)
6.1.2 AlexNet¶
In [91]:
import torch
import torchvision
from torch.utils.data import DataLoader, Dataset
from torchvision import transforms
from torch.autograd import Variable
from torch import optim
import torch.nn as nn
import torch.nn.functional as F
import os
import cv2
from PIL import Image
from tqdm import tqdm_notebook as tqdm
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
device
Out[91]:
device(type='cuda', index=0)
In [92]:
class ImageTransform():
def __init__(self, resize, mean, std):
self.data_transform = {
'train': transforms.Compose([
transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean, std)
]),
'val': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(resize),
transforms.ToTensor(),
transforms.Normalize(mean, std)
])
}
def __call__(self, img, phase):
return self.data_transform[phase](img)
In [94]:
cat_directory = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Cat'
dog_directory = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Dog'
cat_images_filepaths = sorted([os.path.join(cat_directory, f) for f in os.listdir(cat_directory)])
dog_images_filepaths = sorted([os.path.join(dog_directory, f) for f in os.listdir(dog_directory)])
images_filepaths = [*cat_images_filepaths, *dog_images_filepaths]
correct_images_filepaths = [i for i in images_filepaths if cv2.imread(i) is not None]
random.seed(42)
random.shuffle(correct_images_filepaths)
#train_images_filepaths = correct_images_filepaths[:20000] #성능을 향상시키고 싶다면 훈련 데이터셋을 늘려서 테스트해보세요
#val_images_filepaths = correct_images_filepaths[20000:-10] #훈련과 함께 검증도 늘려줘야 합니다
train_images_filepaths = correct_images_filepaths[:400]
val_images_filepaths = correct_images_filepaths[400:-10]
test_images_filepaths = correct_images_filepaths[-10:]
print(len(train_images_filepaths), len(val_images_filepaths), len(test_images_filepaths))
400 92 10
In [95]:
class DogvsCatDataset(Dataset):
def __init__(self, file_list, transform=None, phase='train'):
self.file_list = file_list
self.transform = transform
self.phase = phase
def __len__(self):
return len(self.file_list)
def __getitem__(self, idx):
img_path = self.file_list[idx]
img = Image.open(img_path)
img_transformed = self.transform(img, self.phase)
label = img_path.split('/')[-1].split('.')[0]
if label == 'dog':
label = 1
elif label == 'cat':
label = 0
return img_transformed, label
In [96]:
size = 256
mean = (0.485, 0.456, 0.406)
std = (0.229, 0.224, 0.225)
batch_size = 16
In [97]:
train_dataset = DogvsCatDataset(train_images_filepaths, transform=ImageTransform(size, mean, std), phase='train')
val_dataset = DogvsCatDataset(val_images_filepaths, transform=ImageTransform(size, mean, std), phase='val')
test_dataset = DogvsCatDataset(val_images_filepaths, transform=ImageTransform(size, mean, std), phase='val')
index = 0
print(train_dataset.__getitem__(index)[0].size())
print(train_dataset.__getitem__(index)[1])
torch.Size([3, 256, 256]) 0
In [98]:
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
dataloader_dict = {'train': train_dataloader, 'val': val_dataloader}
batch_iterator = iter(train_dataloader)
inputs, label = next(batch_iterator)
print(inputs.size())
print(label)
torch.Size([16, 3, 256, 256]) tensor([0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0])
In [99]:
class AlexNet(nn.Module):
def __init__(self) -> None:
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 128, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(128, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((3, 3)) # AdaptiveAvgPool2d()는
# AvgPool2d와는 다르게 output 크기를 지정해준다. 원래 AvgPool2d()는 kernel_size, strides,
# padding 값을 넣어주는데 AdaptiveAvgPool2d는 output_size를 넣어주는게 포인트
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(64 * 3 * 3, 256),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(256, 32),
nn.ReLU(inplace=True),
nn.Linear(32, 2),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
In [100]:
model = AlexNet()
model.to(device)
Out[100]:
AlexNet( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2)) (1): ReLU(inplace=True) (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) (3): Conv2d(64, 128, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (4): ReLU(inplace=True) (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) (6): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (7): ReLU(inplace=True) (8): Conv2d(128, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (9): ReLU(inplace=True) (10): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): ReLU(inplace=True) (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(3, 3)) (classifier): Sequential( (0): Dropout(p=0.5, inplace=False) (1): Linear(in_features=576, out_features=256, bias=True) (2): ReLU(inplace=True) (3): Dropout(p=0.5, inplace=False) (4): Linear(in_features=256, out_features=32, bias=True) (5): ReLU(inplace=True) (6): Linear(in_features=32, out_features=2, bias=True) ) )
In [101]:
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
criterion = nn.CrossEntropyLoss()
In [102]:
from torchsummary import summary
summary(model, input_size=(3, 256, 256))
---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Conv2d-1 [-1, 64, 63, 63] 23,296 ReLU-2 [-1, 64, 63, 63] 0 MaxPool2d-3 [-1, 64, 31, 31] 0 Conv2d-4 [-1, 128, 31, 31] 204,928 ReLU-5 [-1, 128, 31, 31] 0 MaxPool2d-6 [-1, 128, 15, 15] 0 Conv2d-7 [-1, 128, 15, 15] 147,584 ReLU-8 [-1, 128, 15, 15] 0 Conv2d-9 [-1, 64, 15, 15] 73,792 ReLU-10 [-1, 64, 15, 15] 0 Conv2d-11 [-1, 64, 15, 15] 36,928 ReLU-12 [-1, 64, 15, 15] 0 MaxPool2d-13 [-1, 64, 7, 7] 0 AdaptiveAvgPool2d-14 [-1, 64, 3, 3] 0 Dropout-15 [-1, 576] 0 Linear-16 [-1, 256] 147,712 ReLU-17 [-1, 256] 0 Dropout-18 [-1, 256] 0 Linear-19 [-1, 32] 8,224 ReLU-20 [-1, 32] 0 Linear-21 [-1, 2] 66 ================================================================ Total params: 642,530 Trainable params: 642,530 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.75 Forward/backward pass size (MB): 7.36 Params size (MB): 2.45 Estimated Total Size (MB): 10.56 ----------------------------------------------------------------
In [103]:
def train_model(model, dataloader_dict, criterion, optimizer, num_epoch):
since = time.time()
best_acc = 0.0
for epoch in range(num_epoch):
print('Epoch {}/{}'.format(epoch + 1, num_epoch))
print('-'*20)
for phase in ['train', 'val']:
if phase == 'train':
model.train()
else:
model.eval()
epoch_loss = 0.0
epoch_corrects = 0
for inputs, labels in tqdm(dataloader_dict[phase]):
inputs = inputs.to(device)
labels = labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
if phase == 'train':
loss.backward()
optimizer.step()
epoch_loss += loss.item() * inputs.size(0)
epoch_corrects += torch.sum(preds == labels.data)
epoch_loss = epoch_loss / len(dataloader_dict[phase].dataset)
epoch_acc = epoch_corrects.double() / len(dataloader_dict[phase].dataset)
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(
time_elapsed // 60, time_elapsed % 60))
return model
In [104]:
num_epoch = 10
model = train_model(model, dataloader_dict, criterion, optimizer, num_epoch)
Epoch 1/10 --------------------
<ipython-input-103-8b28962edeb7>:18: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0 Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook` for inputs, labels in tqdm(dataloader_dict[phase]):
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6971 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6981 Acc: 0.4891 Epoch 2/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6964 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6968 Acc: 0.4891 Epoch 3/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6959 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6962 Acc: 0.4891 Epoch 4/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6948 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6953 Acc: 0.4891 Epoch 5/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6946 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6946 Acc: 0.4891 Epoch 6/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6941 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6944 Acc: 0.4891 Epoch 7/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6940 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6940 Acc: 0.4891 Epoch 8/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6935 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6938 Acc: 0.4891 Epoch 9/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6934 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6936 Acc: 0.4891 Epoch 10/10 --------------------
0%| | 0/25 [00:00<?, ?it/s]
train Loss: 0.6932 Acc: 0.4975
0%| | 0/6 [00:00<?, ?it/s]
val Loss: 0.6935 Acc: 0.4891 Training complete in 0m 41s
In [106]:
import pandas as pd
id_list = []
pred_list = []
_id=0
with torch.no_grad():
for test_path in tqdm(test_images_filepaths):
img = Image.open(test_path)
_id =test_path.split('/')[-1].split('.')[1]
transform = ImageTransform(size, mean, std)
img = transform(img, phase='val')
img = img.unsqueeze(0)
img = img.to(device)
model.eval()
outputs = model(img)
preds = F.softmax(outputs, dim=1)[:, 1].tolist()
id_list.append(_id)
pred_list.append(preds[0])
res = pd.DataFrame({
'id': id_list,
'label': pred_list
})
res.to_csv('/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/alexnet.csv', index=False)
<ipython-input-106-1ad9f4e2ee8d>:6: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0 Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook` for test_path in tqdm(test_images_filepaths):
0%| | 0/10 [00:00<?, ?it/s]
In [107]:
res.head(10)
Out[107]:
id | label | |
---|---|---|
0 | 145 | 0.492431 |
1 | 211 | 0.492654 |
2 | 162 | 0.492408 |
3 | 200 | 0.492827 |
4 | 210 | 0.492508 |
5 | 224 | 0.492607 |
6 | 213 | 0.492099 |
7 | 109 | 0.492625 |
8 | 15 | 0.492350 |
9 | 167 | 0.492118 |
In [108]:
class_ = classes = {0:'cat', 1:'dog'}
def display_image_grid(images_filepaths, predicted_labels=(), cols=5):
rows = len(images_filepaths) // cols
figure, ax = plt.subplots(nrows=rows, ncols=cols, figsize=(12, 6))
for i, image_filepath in enumerate(images_filepaths):
image = cv2.imread(image_filepath)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
a = random.choice(res['id'].values)
label = res.loc[res['id'] == a, 'label'].values[0]
if label > 0.5:
label = 1
else:
label = 0
ax.ravel()[i].imshow(image)
ax.ravel()[i].set_title(class_[label])
ax.ravel()[i].set_axis_off()
plt.tight_layout()
plt.show()
In [109]:
display_image_grid(test_images_filepaths)
6.1.3 VGGNet¶
In [1]:
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
In [2]:
import copy
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data as data
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as Datasets
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device
Out[2]:
device(type='cuda')
In [3]:
class VGG(nn.Module):
def __init__(self, features, output_dim):
super().__init__()
self.features = features
self.avgpool = nn.AdaptiveAvgPool2d(7)
self.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(inplace = True),
nn.Dropout(0.5),
nn.Linear(4096, 4096),
nn.ReLU(inplace = True),
nn.Dropout(0.5),
nn.Linear(4096, output_dim),
)
def forward(self, x):
x = self.features(x)
x = self.avgpool(x)
h = x.view(x.shape[0], -1)
x = self.classifier(h)
return x, h
In [4]:
vgg11_config = [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M']
vgg13_config = [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M']
vgg16_config = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512,
512, 'M']
vgg19_config = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M',
512, 512, 512, 512, 'M']
In [5]:
def get_vgg_layers(config, batch_norm):
layers = []
in_channels = 3
for c in config:
assert c == 'M' or isinstance(c, int)
# assert는 뒤에 조건이 False이면 프로그램이 멈춘다.
# isinstance(c, int)는 c갸 int형인지 확인하는 함수
if c == 'M':
layers += [nn.MaxPool2d(kernel_size = 2)]
else:
conv2d = nn.Conv2d(in_channels, c, kernel_size = 3, padding = 1)
if batch_norm:
layers += [conv2d, nn.BatchNorm2d(c), nn.ReLU(inplace = True)]
else:
layers += [conv2d, nn.ReLU(inplace = True)]
in_channels = c
return nn.Sequential(*layers)
In [6]:
vgg11_layers = get_vgg_layers(vgg11_config, batch_norm = True)
In [7]:
print(vgg11_layers)
Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU(inplace=True) (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (4): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (5): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (6): ReLU(inplace=True) (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (8): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (9): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (10): ReLU(inplace=True) (11): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (12): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (13): ReLU(inplace=True) (14): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (15): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (16): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (17): ReLU(inplace=True) (18): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (19): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (20): ReLU(inplace=True) (21): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (22): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (23): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (24): ReLU(inplace=True) (25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (26): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (27): ReLU(inplace=True) (28): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) )
In [8]:
OUTPUT_DIM = 2
model = VGG(vgg11_layers, OUTPUT_DIM)
print(model)
VGG( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU(inplace=True) (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (4): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (5): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (6): ReLU(inplace=True) (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (8): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (9): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (10): ReLU(inplace=True) (11): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (12): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (13): ReLU(inplace=True) (14): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (15): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (16): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (17): ReLU(inplace=True) (18): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (19): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (20): ReLU(inplace=True) (21): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (22): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (23): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (24): ReLU(inplace=True) (25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (26): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (27): ReLU(inplace=True) (28): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=7) (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=2, bias=True) ) )
In [9]:
import torchvision.models as models
pretrained_model = models.vgg11_bn(pretrained = True)
print(pretrained_model)
/usr/local/lib/python3.8/dist-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead. warnings.warn( /usr/local/lib/python3.8/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=VGG11_BN_Weights.IMAGENET1K_V1`. You can also use `weights=VGG11_BN_Weights.DEFAULT` to get the most up-to-date weights. warnings.warn(msg) Downloading: "https://download.pytorch.org/models/vgg11_bn-6002323d.pth" to /root/.cache/torch/hub/checkpoints/vgg11_bn-6002323d.pth
0%| | 0.00/507M [00:00<?, ?B/s]
VGG( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU(inplace=True) (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (4): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (5): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (6): ReLU(inplace=True) (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (8): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (9): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (10): ReLU(inplace=True) (11): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (12): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (13): ReLU(inplace=True) (14): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (15): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (16): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (17): ReLU(inplace=True) (18): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (19): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (20): ReLU(inplace=True) (21): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (22): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (23): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (24): ReLU(inplace=True) (25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (26): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (27): ReLU(inplace=True) (28): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(7, 7)) (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) ) )
In [10]:
train_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.RandomRotation(5),
transforms.RandomHorizontalFlip(0.5),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])
test_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])
In [11]:
train_path = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/catanddog/train'
test_path = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/catanddog/test'
train_dataset = torchvision.datasets.ImageFolder(
train_path,
transform=train_transforms
)
# 여기서 아까랑 다르게 ImageFolder라는 주어진 함수를 쓰는데
# 그 이유는 train, test dataset이 나눠져있기 때문이다.
test_dataset = torchvision.datasets.ImageFolder(
test_path,
transform=test_transforms
)
print(len(train_dataset)), print(len(test_dataset))
529 12
Out[11]:
(None, None)
In [12]:
VALID_RATIO = 0.9
n_train_examples = int(len(train_dataset) * VALID_RATIO)
n_valid_examples = len(train_dataset) - n_train_examples
train_data, valid_data = data.random_split(train_dataset,
[n_train_examples, n_valid_examples])
# random_split 함수는 train_dataset을 주어진 비율로 맞춰 train, valid로 data를 나눠준다.
In [13]:
valid_data = copy.deepcopy(valid_data)
valid_data.dataset.transform = test_transforms
# 여기서 deepcopy를 사용
In [14]:
print(f'Number of training examples: {len(train_data)}')
print(f'Number of validation examples: {len(valid_data)}')
print(f'Number of testing examples: {len(test_dataset)}')
Number of training examples: 476 Number of validation examples: 53 Number of testing examples: 12
In [15]:
BATCH_SIZE = 128
train_iterator = data.DataLoader(train_data,
shuffle = True,
batch_size = BATCH_SIZE)
valid_iterator = data.DataLoader(valid_data,
batch_size = BATCH_SIZE)
test_iterator = data.DataLoader(test_dataset,
batch_size = BATCH_SIZE)
# test, valid는 shuffle할 필요가 없다.
In [16]:
optimizer = optim.Adam(model.parameters(), lr = 1e-7)
criterion = nn.CrossEntropyLoss()
model = model.to(device)
criterion = criterion.to(device)
In [17]:
def calculate_accuracy(y_pred, y):
top_pred = y_pred.argmax(1, keepdim = True)
# batch_size에 맞춰서 y_pred가 나온다. 그래서 128, 2 크기의 y_pred가 생길텐데
# dim = 1로 지정해 argmax를 한다. 즉 제일 큰 값의 index를 return 한다.
correct = top_pred.eq(y.view_as(top_pred)).sum()
acc = correct.float() / y.shape[0]
return acc
In [18]:
def train(model, iterator, optimizer, criterion, device):
epoch_loss = 0
epoch_acc = 0
model.train()
for (x, y) in iterator:
x = x.to(device)
y = y.to(device)
optimizer.zero_grad()
y_pred, _ = model(x)
loss = criterion(y_pred, y)
acc = calculate_accuracy(y_pred, y)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / len(iterator), epoch_acc / len(iterator)
In [19]:
def evaluate(model, iterator, criterion, device):
epoch_loss = 0
epoch_acc = 0
model.eval()
with torch.no_grad():
for (x, y) in iterator:
x = x.to(device)
y = y.to(device)
y_pred, _ = model(x)
loss = criterion(y_pred, y)
acc = calculate_accuracy(y_pred, y)
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / len(iterator), epoch_acc / len(iterator)
In [20]:
def epoch_time(start_time, end_time):
elapsed_time = end_time - start_time
elapsed_mins = int(elapsed_time / 60)
elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
return elapsed_mins, elapsed_secs
In [ ]:
import time
EPOCHS = 5
best_valid_loss = float('inf')
for epoch in range(EPOCHS):
start_time = time.monotonic()
train_loss, train_acc = train(model, train_iterator, optimizer, criterion, device)
valid_loss, valid_acc = evaluate(model, valid_iterator, criterion, device)
if valid_loss < best_valid_loss:
best_valid_loss = valid_loss
torch.save(model.state_dict(), '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/VGG-model.pt')
end_time = time.monotonic()
epoch_mins, epoch_secs = epoch_time(start_time, end_time)
print(f'Epoch: {epoch+1:02} | Epoch Time: {epoch_mins}m {epoch_secs}s')
print(f'\tTrain Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%')
print(f'\t Valid. Loss: {valid_loss:.3f} | Valid. Acc: {valid_acc*100:.2f}%')
In [ ]:
import torch
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)
model.load_state_dict(torch.load('/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/VGG-model.pt'))
test_loss, test_acc = evaluate(model, test_iterator, criterion, device)
print(f'Test Loss: {test_loss:.3f} | Test Acc: {test_acc*100:.2f}%')
In [26]:
def get_predictions(model, iterator):
model.eval()
images = []
labels = []
probs = []
with torch.no_grad():
for (x, y) in iterator:
x = x.to(device)
y_pred, _ = model(x)
y_prob = F.softmax(y_pred, dim = -1)
top_pred = y_prob.argmax(1, keepdim = True)
images.append(x.cpu())
labels.append(y.cpu())
probs.append(y_prob.cpu())
images = torch.cat(images, dim = 0)
labels = torch.cat(labels, dim = 0)
probs = torch.cat(probs, dim = 0)
return images, labels, probs
In [27]:
images, labels, probs = get_predictions(model, test_iterator)
pred_labels = torch.argmax(probs, 1)
corrects = torch.eq(labels, pred_labels)
correct_examples = []
for image, label, prob, correct in zip(images, labels, probs, corrects):
if correct:
correct_examples.append((image, label, prob))
correct_examples.sort(reverse = True, key = lambda x: torch.max(x[2], dim = 0).values)
# reverse로 sort한다. 그리고 key에 있는 lambda는 함수로 볼 수 있고 x가 함수의 매개변수, 뒤에 있는 값이 return value가 된다.
#torch.max(x[2], dim = 0)은 dim = 0으로 x[2]의 max를 진행하는데 이 함수의 return value는 max value, max index다.
In [45]:
x = torch.tensor([1, 2])
y = x.add(10)
print(x, y)
print(x is y)
y = x.add_(10)
print(x, y)
print(x is y)
tensor([1, 2]) tensor([11, 12]) False tensor([11, 12]) tensor([11, 12]) True
In [28]:
def normalize_image(image):
image_min = image.min()
image_max = image.max()
image.clamp_(min = image_min, max = image_max) # clamp_의 경우도 해당 데이터를 clamp한 값으로 대체한다는 뜻
image.add_(-image_min).div_(image_max - image_min + 1e-5) # add_는 새로운 공간 할당없이 기존의 메모리에 위치한 값을 대체한다. 그냥 add는 새로운 공간을 할당. 즉 add_는 원본을 수정
return image
In [51]:
import torch
x = torch.tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3], [4, 5, 6], [7, 8, 9]]])
print(x, x.shape)
y = x.permute(1, 2, 0)
print(y, y.shape)
z = x.T
print(z, z.shape)
tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3], [4, 5, 6], [7, 8, 9]]]) torch.Size([2, 3, 3]) tensor([[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]], [[7, 7], [8, 8], [9, 9]]]) torch.Size([3, 3, 2]) tensor([[[1, 1], [4, 4], [7, 7]], [[2, 2], [5, 5], [8, 8]], [[3, 3], [6, 6], [9, 9]]]) torch.Size([3, 3, 2])
<ipython-input-51-834de974627d>:6: UserWarning: The use of `x.T` on tensors of dimension other than 2 to reverse their shape is deprecated and it will throw an error in a future release. Consider `x.mT` to transpose batches of matrices or `x.permute(*torch.arange(x.ndim - 1, -1, -1))` to reverse the dimensions of a tensor. (Triggered internally at ../aten/src/ATen/native/TensorShape.cpp:3277.) z = x.T
In [29]:
def plot_most_correct(correct, classes, n_images, normalize = True):
rows = int(np.sqrt(n_images)) # n_images를 루트씌우기
cols = int(np.sqrt(n_images)) # n_images를 루트씌우기
fig = plt.figure(figsize = (25, 20))
for i in range(rows*cols):
ax = fig.add_subplot(rows, cols, i+1)
image, true_label, probs = correct[i]
image = image.permute(1, 2, 0) # permute는 축을 변경할 때 사용
true_prob = probs[true_label]
correct_prob, correct_label = torch.max(probs, dim = 0)
true_class = classes[true_label]
correct_class = classes[correct_label]
if normalize:
image = normalize_image(image)
ax.imshow(image.cpu().numpy())
ax.set_title(f'true label: {true_class} ({true_prob:.3f})\n' \
f'pred label: {correct_class} ({correct_prob:.3f})')
ax.axis('off')
fig.subplots_adjust(hspace = 0.4)
In [32]:
import matplotlib.pyplot as plt
classes = test_dataset.classes
N_IMAGES = 5
plot_most_correct(correct_examples, classes, N_IMAGES)
6.1.4 ResNset¶
In [1]:
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
In [2]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data as data
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torchvision.models as models
import matplotlib.pyplot as plt
import numpy as np
import copy
from collections import namedtuple # namedtuple은 python의 자료형 중 하나. index, key로 접근 가능
import os
import random
import time
import cv2
from torch.utils.data import DataLoader, Dataset
from PIL import Image
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device
Out[2]:
device(type='cuda')
In [3]:
class ImageTransform():
def __init__(self, resize, mean, std):
self.data_transform = {
'train': transforms.Compose([
transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean, std)
]),
'val': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(resize),
transforms.ToTensor(),
transforms.Normalize(mean, std)
])
}
def __call__(self, img, phase):
return self.data_transform[phase](img)
In [4]:
size = 224
mean = (0.485, 0.456, 0.406)
std = (0.229, 0.224, 0.225)
batch_size = 32
In [5]:
cat_directory = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Cat'
dog_directory = '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/dogs-vs-cats/Dog'
cat_images_filepaths = sorted([os.path.join(cat_directory, f) for f in os.listdir(cat_directory)])
dog_images_filepaths = sorted([os.path.join(dog_directory, f) for f in os.listdir(dog_directory)])
images_filepaths = [*cat_images_filepaths, *dog_images_filepaths]
correct_images_filepaths = [i for i in images_filepaths if cv2.imread(i) is not None]
In [6]:
random.seed(42)
random.shuffle(correct_images_filepaths)
#train_images_filepaths = correct_images_filepaths[:20000] #성능을 향상시키고 싶다면 훈련 데이터셋을 늘려서 테스트해보세요
#val_images_filepaths = correct_images_filepaths[20000:-10] #훈련과 함께 검증도 늘려줘야 합니다
train_images_filepaths = correct_images_filepaths[:400]
val_images_filepaths = correct_images_filepaths[400:-10]
test_images_filepaths = correct_images_filepaths[-10:]
print(len(train_images_filepaths), len(val_images_filepaths), len(test_images_filepaths)) # train, valid, test dataset 분리
400 92 10
In [7]:
class DogvsCatDataset(Dataset):
def __init__(self, file_list, transform=None, phase='train'):
self.file_list = file_list
self.transform = transform
self.phase = phase
def __len__(self):
return len(self.file_list)
def __getitem__(self, idx):
img_path = self.file_list[idx]
img = Image.open(img_path)
img_transformed = self.transform(img, self.phase)
label = img_path.split('/')[-1].split('.')[0]
if label == 'dog':
label = 1
elif label == 'cat':
label = 0
return img_transformed, label
In [8]:
train_dataset = DogvsCatDataset(train_images_filepaths, transform=ImageTransform(size, mean, std), phase='train')
val_dataset = DogvsCatDataset(val_images_filepaths, transform=ImageTransform(size, mean, std), phase='val')
index = 0
print(train_dataset.__getitem__(index)[0].size())
print(train_dataset.__getitem__(index)[1])
torch.Size([3, 224, 224]) 0
In [9]:
train_iterator = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) # batch size에 맞춰 data 호출
valid_iterator = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
dataloader_dict = {'train': train_iterator, 'val': valid_iterator}
batch_iterator = iter(train_iterator)
inputs, label = next(batch_iterator)
print(inputs.size())
print(label)
torch.Size([32, 3, 224, 224]) tensor([0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1])
In [10]:
class BasicBlock(nn.Module): # resnet에서 layer가 깊지 않을 때 사용하는 basic block에 대한 정의
expansion = 1
def __init__(self, in_channels, out_channels, stride = 1, downsample = False):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size = 3,
stride = stride, padding = 1, bias = False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size = 3,
stride = 1, padding = 1, bias = False)
self.bn2 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace = True)
if downsample:
conv = nn.Conv2d(in_channels, out_channels, kernel_size = 1,
stride = stride, bias = False)
bn = nn.BatchNorm2d(out_channels)
downsample = nn.Sequential(conv, bn)
else:
downsample = None
self.downsample = downsample
def forward(self, x):
i = x
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.conv2(x)
x = self.bn2(x)
if self.downsample is not None:
i = self.downsample(i)
x += i # skip connection
x = self.relu(x)
return x
In [11]:
class Bottleneck(nn.Module): # resnet에서 layer가 깊어지면서 사용하는 bottleneck에 대한 정의
expansion = 4
def __init__(self, in_channels, out_channels, stride = 1, downsample = False):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size = 1, stride = 1, bias = False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size = 3, stride = stride, padding = 1, bias = False)
self.bn2 = nn.BatchNorm2d(out_channels)
self.conv3 = nn.Conv2d(out_channels, self.expansion * out_channels, kernel_size = 1,
stride = 1, bias = False)
self.bn3 = nn.BatchNorm2d(self.expansion * out_channels)
self.relu = nn.ReLU(inplace = True)
if downsample:
conv = nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size = 1,
stride = stride, bias = False)
bn = nn.BatchNorm2d(self.expansion * out_channels)
downsample = nn.Sequential(conv, bn)
else:
downsample = None
self.downsample = downsample
def forward(self, x):
i = x
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.conv2(x)
x = self.bn2(x)
x = self.relu(x)
x = self.conv3(x)
x = self.bn3(x)
if self.downsample is not None:
i = self.downsample(i)
x += i # skip connection
x = self.relu(x)
return x
In [12]:
class ResNet(nn.Module):
def __init__(self, config, output_dim, zero_init_residual=False): # config에 resnet 종류에 따른 namedtuple 값이 전달된다.
super().__init__()
# 아래 주석은 resnet50을 기준으로 작성
block, n_blocks, channels = config # block = Bottleneck, n_blocks = [3, 4, 6, 3], channels = [64, 128, 256, 512]
self.in_channels = channels[0] # 64
assert len(n_blocks) == len(channels) == 4 # n_blocks이랑 channels의 길이가 4가 아니면 stop
self.conv1 = nn.Conv2d(3, self.in_channels, kernel_size = 7, stride = 2, padding = 3, bias = False) # Conv2d(3, 64, kernel_size = 7, stride = 2, padding = 3, bias = False)
self.bn1 = nn.BatchNorm2d(self.in_channels)
self.relu = nn.ReLU(inplace = True)
self.maxpool = nn.MaxPool2d(kernel_size = 3, stride = 2, padding = 1)
# conv1 or maxpool 을 거치면 해당 layer의 input 크기의 절반으로 줄어든다
self.layer1 = self.get_resnet_layer(block, n_blocks[0], channels[0]) # bottleneck, 3, 64
self.layer2 = self.get_resnet_layer(block, n_blocks[1], channels[1], stride = 2) # bottleneck, 4, 128, stride = 2
self.layer3 = self.get_resnet_layer(block, n_blocks[2], channels[2], stride = 2) # bottleneck, 6, 256, stride = 2
self.layer4 = self.get_resnet_layer(block, n_blocks[3], channels[3], stride = 2) # bottleneck, 3, 512, stride = 2
self.avgpool = nn.AdaptiveAvgPool2d((1,1)) # 1, 1 크기로 avgpool2d 진행
self.fc = nn.Linear(self.in_channels, output_dim) # nn.Linear(64, 2)
if zero_init_residual: # residual branch에 있는 마지막 Batch Normalization을 0으로 초기화해서 다음 residual branch를 0에서 시작하게 한다
for m in self.modules():
if isinstance(m, Bottleneck):
nn.init.constant_(m.bn3.weight, 0)
elif isinstance(m, BasicBlock):
nn.init.constant_(m.bn2.weight, 0)
def get_resnet_layer(self, block, n_blocks, channels, stride = 1):
layers = []
if self.in_channels != block.expansion * channels:
downsample = True
else:
downsample = False
layers.append(block(self.in_channels, channels, stride, downsample)) # stride = 1 or 2로 적용되면서 feature map의 크기가 그대로 가거나 줄어들거나 한다.
for i in range(1, n_blocks): # n_blocks만큼 반복해서 layers에 append 한다
layers.append(block(block.expansion * channels, channels)) # 여긴 stride = 1
self.in_channels = block.expansion * channels
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
h = x.view(x.shape[0], -1)
x = self.fc(h)
return x, h
In [13]:
ResNetConfig = namedtuple('ResNetConfig', ['block', 'n_blocks', 'channels'])
In [14]:
resnet18_config = ResNetConfig(block = BasicBlock,
n_blocks = [2,2,2,2],
channels = [64, 128, 256, 512])
resnet34_config = ResNetConfig(block = BasicBlock,
n_blocks = [3,4,6,3],
channels = [64, 128, 256, 512])
In [15]:
resnet50_config = ResNetConfig(block = Bottleneck,
n_blocks = [3, 4, 6, 3],
channels = [64, 128, 256, 512])
resnet101_config = ResNetConfig(block = Bottleneck,
n_blocks = [3, 4, 23, 3],
channels = [64, 128, 256, 512])
resnet152_config = ResNetConfig(block = Bottleneck,
n_blocks = [3, 8, 36, 3],
channels = [64, 128, 256, 512])
In [16]:
pretrained_model = models.resnet50(pretrained = True) # pretrain된 model을 호출할 수도 있다
/usr/local/lib/python3.8/dist-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead. warnings.warn( /usr/local/lib/python3.8/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet50_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet50_Weights.DEFAULT` to get the most up-to-date weights. warnings.warn(msg) Downloading: "https://download.pytorch.org/models/resnet50-0676ba61.pth" to /root/.cache/torch/hub/checkpoints/resnet50-0676ba61.pth
0%| | 0.00/97.8M [00:00<?, ?B/s]
In [17]:
print(pretrained_model)
ResNet( (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False) (layer1): Sequential( (0): Bottleneck( (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (layer2): Sequential( (0): Bottleneck( (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (3): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (layer3): Sequential( (0): Bottleneck( (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (3): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (4): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (5): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (layer4): Sequential( (0): Bottleneck( (conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (avgpool): AdaptiveAvgPool2d(output_size=(1, 1)) (fc): Linear(in_features=2048, out_features=1000, bias=True) )
In [18]:
OUTPUT_DIM = 2 # 개와 고양이 분류문제니까
model = ResNet(resnet50_config, OUTPUT_DIM) # resnet50 사용
print(model)
ResNet( (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False) (layer1): Sequential( (0): Bottleneck( (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (layer2): Sequential( (0): Bottleneck( (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (3): Bottleneck( (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (layer3): Sequential( (0): Bottleneck( (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (3): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (4): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (5): Bottleneck( (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (layer4): Sequential( (0): Bottleneck( (conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) (downsample): Sequential( (0): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): Bottleneck( (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) (2): Bottleneck( (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False) (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace=True) ) ) (avgpool): AdaptiveAvgPool2d(output_size=(1, 1)) (fc): Linear(in_features=2048, out_features=2, bias=True) )
In [26]:
from torchsummary import summary
model = model.to(device)
summary(model, input_size = (3, 256, 256))
---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Conv2d-1 [-1, 64, 128, 128] 9,408 BatchNorm2d-2 [-1, 64, 128, 128] 128 ReLU-3 [-1, 64, 128, 128] 0 MaxPool2d-4 [-1, 64, 64, 64] 0 Conv2d-5 [-1, 64, 64, 64] 4,096 BatchNorm2d-6 [-1, 64, 64, 64] 128 ReLU-7 [-1, 64, 64, 64] 0 Conv2d-8 [-1, 64, 64, 64] 36,864 BatchNorm2d-9 [-1, 64, 64, 64] 128 ReLU-10 [-1, 64, 64, 64] 0 Conv2d-11 [-1, 256, 64, 64] 16,384 BatchNorm2d-12 [-1, 256, 64, 64] 512 Conv2d-13 [-1, 256, 64, 64] 16,384 BatchNorm2d-14 [-1, 256, 64, 64] 512 ReLU-15 [-1, 256, 64, 64] 0 Bottleneck-16 [-1, 256, 64, 64] 0 Conv2d-17 [-1, 64, 64, 64] 16,384 BatchNorm2d-18 [-1, 64, 64, 64] 128 ReLU-19 [-1, 64, 64, 64] 0 Conv2d-20 [-1, 64, 64, 64] 36,864 BatchNorm2d-21 [-1, 64, 64, 64] 128 ReLU-22 [-1, 64, 64, 64] 0 Conv2d-23 [-1, 256, 64, 64] 16,384 BatchNorm2d-24 [-1, 256, 64, 64] 512 ReLU-25 [-1, 256, 64, 64] 0 Bottleneck-26 [-1, 256, 64, 64] 0 Conv2d-27 [-1, 64, 64, 64] 16,384 BatchNorm2d-28 [-1, 64, 64, 64] 128 ReLU-29 [-1, 64, 64, 64] 0 Conv2d-30 [-1, 64, 64, 64] 36,864 BatchNorm2d-31 [-1, 64, 64, 64] 128 ReLU-32 [-1, 64, 64, 64] 0 Conv2d-33 [-1, 256, 64, 64] 16,384 BatchNorm2d-34 [-1, 256, 64, 64] 512 ReLU-35 [-1, 256, 64, 64] 0 Bottleneck-36 [-1, 256, 64, 64] 0 Conv2d-37 [-1, 128, 64, 64] 32,768 BatchNorm2d-38 [-1, 128, 64, 64] 256 ReLU-39 [-1, 128, 64, 64] 0 Conv2d-40 [-1, 128, 32, 32] 147,456 BatchNorm2d-41 [-1, 128, 32, 32] 256 ReLU-42 [-1, 128, 32, 32] 0 Conv2d-43 [-1, 512, 32, 32] 65,536 BatchNorm2d-44 [-1, 512, 32, 32] 1,024 Conv2d-45 [-1, 512, 32, 32] 131,072 BatchNorm2d-46 [-1, 512, 32, 32] 1,024 ReLU-47 [-1, 512, 32, 32] 0 Bottleneck-48 [-1, 512, 32, 32] 0 Conv2d-49 [-1, 128, 32, 32] 65,536 BatchNorm2d-50 [-1, 128, 32, 32] 256 ReLU-51 [-1, 128, 32, 32] 0 Conv2d-52 [-1, 128, 32, 32] 147,456 BatchNorm2d-53 [-1, 128, 32, 32] 256 ReLU-54 [-1, 128, 32, 32] 0 Conv2d-55 [-1, 512, 32, 32] 65,536 BatchNorm2d-56 [-1, 512, 32, 32] 1,024 ReLU-57 [-1, 512, 32, 32] 0 Bottleneck-58 [-1, 512, 32, 32] 0 Conv2d-59 [-1, 128, 32, 32] 65,536 BatchNorm2d-60 [-1, 128, 32, 32] 256 ReLU-61 [-1, 128, 32, 32] 0 Conv2d-62 [-1, 128, 32, 32] 147,456 BatchNorm2d-63 [-1, 128, 32, 32] 256 ReLU-64 [-1, 128, 32, 32] 0 Conv2d-65 [-1, 512, 32, 32] 65,536 BatchNorm2d-66 [-1, 512, 32, 32] 1,024 ReLU-67 [-1, 512, 32, 32] 0 Bottleneck-68 [-1, 512, 32, 32] 0 Conv2d-69 [-1, 128, 32, 32] 65,536 BatchNorm2d-70 [-1, 128, 32, 32] 256 ReLU-71 [-1, 128, 32, 32] 0 Conv2d-72 [-1, 128, 32, 32] 147,456 BatchNorm2d-73 [-1, 128, 32, 32] 256 ReLU-74 [-1, 128, 32, 32] 0 Conv2d-75 [-1, 512, 32, 32] 65,536 BatchNorm2d-76 [-1, 512, 32, 32] 1,024 ReLU-77 [-1, 512, 32, 32] 0 Bottleneck-78 [-1, 512, 32, 32] 0 Conv2d-79 [-1, 256, 32, 32] 131,072 BatchNorm2d-80 [-1, 256, 32, 32] 512 ReLU-81 [-1, 256, 32, 32] 0 Conv2d-82 [-1, 256, 16, 16] 589,824 BatchNorm2d-83 [-1, 256, 16, 16] 512 ReLU-84 [-1, 256, 16, 16] 0 Conv2d-85 [-1, 1024, 16, 16] 262,144 BatchNorm2d-86 [-1, 1024, 16, 16] 2,048 Conv2d-87 [-1, 1024, 16, 16] 524,288 BatchNorm2d-88 [-1, 1024, 16, 16] 2,048 ReLU-89 [-1, 1024, 16, 16] 0 Bottleneck-90 [-1, 1024, 16, 16] 0 Conv2d-91 [-1, 256, 16, 16] 262,144 BatchNorm2d-92 [-1, 256, 16, 16] 512 ReLU-93 [-1, 256, 16, 16] 0 Conv2d-94 [-1, 256, 16, 16] 589,824 BatchNorm2d-95 [-1, 256, 16, 16] 512 ReLU-96 [-1, 256, 16, 16] 0 Conv2d-97 [-1, 1024, 16, 16] 262,144 BatchNorm2d-98 [-1, 1024, 16, 16] 2,048 ReLU-99 [-1, 1024, 16, 16] 0 Bottleneck-100 [-1, 1024, 16, 16] 0 Conv2d-101 [-1, 256, 16, 16] 262,144 BatchNorm2d-102 [-1, 256, 16, 16] 512 ReLU-103 [-1, 256, 16, 16] 0 Conv2d-104 [-1, 256, 16, 16] 589,824 BatchNorm2d-105 [-1, 256, 16, 16] 512 ReLU-106 [-1, 256, 16, 16] 0 Conv2d-107 [-1, 1024, 16, 16] 262,144 BatchNorm2d-108 [-1, 1024, 16, 16] 2,048 ReLU-109 [-1, 1024, 16, 16] 0 Bottleneck-110 [-1, 1024, 16, 16] 0 Conv2d-111 [-1, 256, 16, 16] 262,144 BatchNorm2d-112 [-1, 256, 16, 16] 512 ReLU-113 [-1, 256, 16, 16] 0 Conv2d-114 [-1, 256, 16, 16] 589,824 BatchNorm2d-115 [-1, 256, 16, 16] 512 ReLU-116 [-1, 256, 16, 16] 0 Conv2d-117 [-1, 1024, 16, 16] 262,144 BatchNorm2d-118 [-1, 1024, 16, 16] 2,048 ReLU-119 [-1, 1024, 16, 16] 0 Bottleneck-120 [-1, 1024, 16, 16] 0 Conv2d-121 [-1, 256, 16, 16] 262,144 BatchNorm2d-122 [-1, 256, 16, 16] 512 ReLU-123 [-1, 256, 16, 16] 0 Conv2d-124 [-1, 256, 16, 16] 589,824 BatchNorm2d-125 [-1, 256, 16, 16] 512 ReLU-126 [-1, 256, 16, 16] 0 Conv2d-127 [-1, 1024, 16, 16] 262,144 BatchNorm2d-128 [-1, 1024, 16, 16] 2,048 ReLU-129 [-1, 1024, 16, 16] 0 Bottleneck-130 [-1, 1024, 16, 16] 0 Conv2d-131 [-1, 256, 16, 16] 262,144 BatchNorm2d-132 [-1, 256, 16, 16] 512 ReLU-133 [-1, 256, 16, 16] 0 Conv2d-134 [-1, 256, 16, 16] 589,824 BatchNorm2d-135 [-1, 256, 16, 16] 512 ReLU-136 [-1, 256, 16, 16] 0 Conv2d-137 [-1, 1024, 16, 16] 262,144 BatchNorm2d-138 [-1, 1024, 16, 16] 2,048 ReLU-139 [-1, 1024, 16, 16] 0 Bottleneck-140 [-1, 1024, 16, 16] 0 Conv2d-141 [-1, 512, 16, 16] 524,288 BatchNorm2d-142 [-1, 512, 16, 16] 1,024 ReLU-143 [-1, 512, 16, 16] 0 Conv2d-144 [-1, 512, 8, 8] 2,359,296 BatchNorm2d-145 [-1, 512, 8, 8] 1,024 ReLU-146 [-1, 512, 8, 8] 0 Conv2d-147 [-1, 2048, 8, 8] 1,048,576 BatchNorm2d-148 [-1, 2048, 8, 8] 4,096 Conv2d-149 [-1, 2048, 8, 8] 2,097,152 BatchNorm2d-150 [-1, 2048, 8, 8] 4,096 ReLU-151 [-1, 2048, 8, 8] 0 Bottleneck-152 [-1, 2048, 8, 8] 0 Conv2d-153 [-1, 512, 8, 8] 1,048,576 BatchNorm2d-154 [-1, 512, 8, 8] 1,024 ReLU-155 [-1, 512, 8, 8] 0 Conv2d-156 [-1, 512, 8, 8] 2,359,296 BatchNorm2d-157 [-1, 512, 8, 8] 1,024 ReLU-158 [-1, 512, 8, 8] 0 Conv2d-159 [-1, 2048, 8, 8] 1,048,576 BatchNorm2d-160 [-1, 2048, 8, 8] 4,096 ReLU-161 [-1, 2048, 8, 8] 0 Bottleneck-162 [-1, 2048, 8, 8] 0 Conv2d-163 [-1, 512, 8, 8] 1,048,576 BatchNorm2d-164 [-1, 512, 8, 8] 1,024 ReLU-165 [-1, 512, 8, 8] 0 Conv2d-166 [-1, 512, 8, 8] 2,359,296 BatchNorm2d-167 [-1, 512, 8, 8] 1,024 ReLU-168 [-1, 512, 8, 8] 0 Conv2d-169 [-1, 2048, 8, 8] 1,048,576 BatchNorm2d-170 [-1, 2048, 8, 8] 4,096 ReLU-171 [-1, 2048, 8, 8] 0 Bottleneck-172 [-1, 2048, 8, 8] 0 AdaptiveAvgPool2d-173 [-1, 2048, 1, 1] 0 Linear-174 [-1, 2] 4,098 ================================================================ Total params: 23,512,130 Trainable params: 23,512,130 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.75 Forward/backward pass size (MB): 374.27 Params size (MB): 89.69 Estimated Total Size (MB): 464.71 ----------------------------------------------------------------
In [27]:
optimizer = optim.Adam(model.parameters(), lr=1e-7)
criterion = nn.CrossEntropyLoss()
model = model.to(device)
criterion = criterion.to(device)
In [28]:
def calculate_topk_accuracy(y_pred, y, k = 2):
with torch.no_grad():
batch_size = y.shape[0]
_, top_pred = y_pred.topk(k, 1) # 가장 큰 index를 얻기 위해 사용
top_pred = top_pred.t() # 전치행렬
correct = top_pred.eq(y.view(1, -1).expand_as(top_pred))
correct_1 = correct[:1].reshape(-1).float().sum(0, keepdim = True)
correct_k = correct[:k].reshape(-1).float().sum(0, keepdim = True) # 이미지의 정확한 레이블 부여를 위해 사용
acc_1 = correct_1 / batch_size
acc_k = correct_k / batch_size
return acc_1, acc_k
In [29]:
def train(model, iterator, optimizer, criterion, device):
epoch_loss = 0
epoch_acc_1 = 0
epoch_acc_5 = 0
model.train()
for (x, y) in iterator:
x = x.to(device)
y = y.to(device)
optimizer.zero_grad()
y_pred = model(x)
loss = criterion(y_pred[0], y)
acc_1, acc_5 = calculate_topk_accuracy(y_pred[0], y)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
epoch_acc_1 += acc_1.item()
epoch_acc_5 += acc_5.item()
epoch_loss /= len(iterator)
epoch_acc_1 /= len(iterator)
epoch_acc_5 /= len(iterator)
return epoch_loss, epoch_acc_1, epoch_acc_5
In [30]:
def evaluate(model, iterator, criterion, device):
epoch_loss = 0
epoch_acc_1 = 0
epoch_acc_5 = 0
model.eval()
with torch.no_grad():
for (x, y) in iterator:
x = x.to(device)
y = y.to(device)
y_pred = model(x)
loss = criterion(y_pred[0], y)
acc_1, acc_5 = calculate_topk_accuracy(y_pred[0], y)
epoch_loss += loss.item()
epoch_acc_1 += acc_1.item()
epoch_acc_5 += acc_5.item()
epoch_loss /= len(iterator)
epoch_acc_1 /= len(iterator)
epoch_acc_5 /= len(iterator)
return epoch_loss, epoch_acc_1, epoch_acc_5
In [31]:
def epoch_time(start_time, end_time):
elapsed_time = end_time - start_time
elapsed_mins = int(elapsed_time / 60)
elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
return elapsed_mins, elapsed_secs
In [34]:
best_valid_loss = float('inf')
EPOCHS = 10
for epoch in range(EPOCHS):
start_time = time.monotonic()
train_loss, train_acc_1, train_acc_5 = train(model, train_iterator, optimizer, criterion, device)
valid_loss, valid_acc_1, valid_acc_5 = evaluate(model, valid_iterator, criterion, device)
if valid_loss < best_valid_loss:
best_valid_loss = valid_loss
torch.save(model.state_dict(), '/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/ResNet-model.pt')
end_time = time.monotonic()
epoch_mins, epoch_secs = epoch_time(start_time, end_time)
print(f'Epoch: {epoch+1:02} | Epoch Time: {epoch_mins}m {epoch_secs}s')
print(f'\tTrain Loss: {train_loss:.3f} | Train Acc @1: {train_acc_1*100:6.2f}% | ' \
f'Train Acc @5: {train_acc_5*100:6.2f}%')
print(f'\tValid Loss: {valid_loss:.3f} | Valid Acc @1: {valid_acc_1*100:6.2f}% | ' \
f'Valid Acc @5: {valid_acc_5*100:6.2f}%')
Epoch: 01 | Epoch Time: 0m 7s Train Loss: 0.942 | Train Acc @1: 49.76% | Train Acc @5: 100.00% Valid Loss: 0.852 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 02 | Epoch Time: 0m 7s Train Loss: 0.953 | Train Acc @1: 49.04% | Train Acc @5: 100.00% Valid Loss: 0.907 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 03 | Epoch Time: 0m 7s Train Loss: 0.933 | Train Acc @1: 49.52% | Train Acc @5: 100.00% Valid Loss: 0.932 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 04 | Epoch Time: 0m 7s Train Loss: 0.945 | Train Acc @1: 49.52% | Train Acc @5: 100.00% Valid Loss: 0.941 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 05 | Epoch Time: 0m 7s Train Loss: 0.915 | Train Acc @1: 50.48% | Train Acc @5: 100.00% Valid Loss: 0.932 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 06 | Epoch Time: 0m 7s Train Loss: 0.910 | Train Acc @1: 50.24% | Train Acc @5: 100.00% Valid Loss: 0.924 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 07 | Epoch Time: 0m 7s Train Loss: 0.919 | Train Acc @1: 49.28% | Train Acc @5: 100.00% Valid Loss: 0.921 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 08 | Epoch Time: 0m 7s Train Loss: 0.916 | Train Acc @1: 49.52% | Train Acc @5: 100.00% Valid Loss: 0.913 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 09 | Epoch Time: 0m 7s Train Loss: 0.924 | Train Acc @1: 48.56% | Train Acc @5: 100.00% Valid Loss: 0.923 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00% Epoch: 10 | Epoch Time: 0m 7s Train Loss: 0.906 | Train Acc @1: 49.52% | Train Acc @5: 100.00% Valid Loss: 0.904 | Valid Acc @1: 48.81% | Valid Acc @5: 100.00%
In [35]:
import pandas as pd
id_list = []
pred_list = []
_id=0
with torch.no_grad():
for test_path in test_images_filepaths:
img = Image.open(test_path)
_id =test_path.split('/')[-1].split('.')[1]
transform = ImageTransform(size, mean, std)
img = transform(img, phase='val')
img = img.unsqueeze(0)
img = img.to(device)
model.eval()
outputs = model(img)
preds = F.softmax(outputs[0], dim=1)[:, 1].tolist()
id_list.append(_id)
pred_list.append(preds[0])
res = pd.DataFrame({
'id': id_list,
'label': pred_list
})
res.sort_values(by='id', inplace=True)
res.reset_index(drop=True, inplace=True)
res.to_csv('/content/drive/MyDrive/deel_learning_pytorch_book/deep learning pytorch book/6장/data/ReNet.csv', index=False)
res.head(10)
Out[35]:
id | label | |
---|---|---|
0 | 109 | 0.208907 |
1 | 145 | 0.229707 |
2 | 15 | 0.194531 |
3 | 162 | 0.229875 |
4 | 167 | 0.214128 |
5 | 200 | 0.243111 |
6 | 210 | 0.226248 |
7 | 211 | 0.228028 |
8 | 213 | 0.204386 |
9 | 224 | 0.220855 |
In [36]:
class_ = classes = {0:'cat', 1:'dog'}
def display_image_grid(images_filepaths, predicted_labels=(), cols=5):
rows = len(images_filepaths) // cols
figure, ax = plt.subplots(nrows=rows, ncols=cols, figsize=(12, 6))
for i, image_filepath in enumerate(images_filepaths):
image = cv2.imread(image_filepath)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
a = random.choice(res['id'].values)
label = res.loc[res['id'] == a, 'label'].values[0]
if label > 0.5:
label = 1
else:
label = 0
ax.ravel()[i].imshow(image)
ax.ravel()[i].set_title(class_[label])
ax.ravel()[i].set_axis_off()
plt.tight_layout()
plt.show()
display_image_grid(test_images_filepaths)
6.2 객체 인식을 위한 신경망¶
- 객체 인식(object detection)은 이미지나 영상 내에 있는 객체를 식별하는 컴퓨터 비전 기술
- 객체 인식이란 이미지나 영상 내에 있는 여러 객체에 대해 각 객체가 무엇인지 분류하는 문제와 그 객체 위치가 어디인지 박스(bounding box)로 나타내는 위치 검출(localization) 문제를 다루는 분야
- 객체 인식 = 여러 가지 객체에 대한 분류 + 객체의 위치 정보를 파악하는 위치 검출
- 1단계 객체 인식
- 분류와 위치 검출을 동시에 행하는 방법
- 빠르지만 정확도는 낮다
- 대표적으로 CNN을 처음 적용시킨 R-CNN 계열이 존재
- 2단계 객체 인식
- 분류와 위치 검출을 순차적으로 행하는 방법
- 느리지만 정확도는 높다
- 대표적을 YOLO(You Only Look Once)계열과 SSD 계열 등 존재
6.2.1 R-CNN¶
과거에 객체 인식 알고리즘들은 슬라이딩 윈도우 방식으로 모든 영역을 탐색하면서 객체를 검출했다. 하지만 비효율적이기 때문에 현재는 선택적 탐색 알고리즘을 적용한 후보 영역을 많이 사용
- R-CNN(Region-based CNN)은 이미지 분류를 수행하는 CNN과 이미지에서 객체가 있을 만한 영역을 제안해 주는 후보 영역 알고리즘을 결합한 알고리즘
- 실행 순서
- input image를 받는다
- 2000개의 bounding box를 선택적 탐색 알고리즘으로 추출한 후 잘라내고(crop), CNN 모델에 넣기 위해 같은 크기(227 x 227)로 통일
- 크기가 동일한 이미지 200개에 각각 CNN 모델을 적용
- 각각 분류를 진행해 결과 도출
- 단점
- 복잡한 학습 과정
- 긴 학습 시간과 대용량 저장 공간
- 객체 검출 속도 문제
※ 선택적 탐색
- 객체 인식이나 검출을 위한 가능한 후보 영역(객체가 있을 만한 위치, 영역)을 알아내는 방법
- 분할 방식을 이용해 seed를 선정하고 seed에 대한 완전 탐색을 적용
- 초기 영역 생성, 작은 영역의 통합, 후보 영역 생성의 순서로 진행
6.2.3 Fast R-CNN¶
- Fast R-CNN(Fast Region-based CNN)은 R-CNN의 속도 문제를 개선하려고 RoI 풀링을 도입
- 선택적 탐색에서 찾은 bounding box에 대한 정보가 CNN을 통과하면서 유지되도록 하고 최종 CNN feature map은 pooling을 적용해 fully connected layer에 전달되도록 크기를 조정
- R-CNN의 경우 crop/warping을 한 후 CNN을 거쳐 fully connected layer로 전달되는 반면 Fast R-CNN은 CNN을 거친 후 RoI pooling을 한 결과가 fully connected layer로 전달하므로 고정된 크기로 crop하거나 warp할 필요가 없어 이미지의 특징을 잘 유지 가능
- 이를 통해 CNN을 돌리는 시간을 단축시킬 수 있음
※ RoI pooling
- 크기가 다른 feature map의 영역마다 stride를 다르게 max pool을 적용해 결과값의 크기를 동일하게 맞추는 방법
6.2.4 Faster R-CNN¶
- Fast R-CNN은 후보 영역 생성을 하면서 속도에 한계를 보였으며 Faster R-CNN은 CNN 내부 네트워크에서 후보 영역을 생성하도록 설계
- 외부의 느린 선택적 탐색(CPU로 계산) 대신 내부의 빠른 RPN(GPU로 계산)를 사용
- RPN은 마지막 CNN층 다음에 위치하고, 그 뒤에는 Fast R-CNN과 동일하게 RoI pooling 등이 존재
- 후보 영역 추출 network는 feature map에서 N x N 크기의 작은 윈도우 영역을 input으로 받고, 해당 영역에 객체의 존재 유무 판단을 위해 이진 분류를 수행하는 작은 네트워크를 생성 & 슬라이딩 윈도우 방식으로 객체를 탐색
- 단 이미지에 존재하는 객체들의 크기와 비율이 다양해 고정된 N x N 크기의 input 만으로 다양한 크기와 비율의 이미지를 수용하기엔 어려움이 존재
- 이를 해결하기 위해 여러 크기와 비율의 reference box k개를 미리 정의하고 각각의 슬라이딩 윈도우 위치마다 박스 k개를 출력하도록 설계. 이러한 방식을 앵커(anchor)라 함
- 후보 영역 추출 network의 output 값은 모든 anchor 위치에 대해 각각 객체와 배경을 판단하는 2k개의 분류에 대한 출력 & x, y, w, h 위치 보정 값을 위한 4k 개의 회귀 출력
728x90
'Deep Learning(강의 및 책) > Pytorch' 카테고리의 다른 글
[Pytorch] Deep Learning Pytorch 11. 클러스터링(Clustering) (0) | 2023.01.16 |
---|---|
[Pytorch] Deep Learning Pytorch 8. 성능 최적화 (0) | 2023.01.15 |
[Pytorch] Deep Learning Pytorch 5. 합성곱 신경망Ⅰ (0) | 2022.09.25 |
[Pytorch] Deep Learning Pytorch 3. 머신러닝 핵심 알고리즘 (0) | 2022.09.10 |
[Pytorch]Deep Learning Pytorch 2. Pytorch 기초 (0) | 2022.09.09 |