환경
code
import numpy as np
import matplotlib.pyplot as plt
import sys
import os
import time
import random
import pandas as pd
import torch
from torch import nn, cuda, optim
from torchvision import models,transforms,datasets
from torch.utils.data import DataLoader,random_split
from PIL import Image
import seaborn as sns
import torch.nn.functional as F
import shutil
import torchvision
import torchvision.transforms as transforms
import torch
import torch.nn as nn
import torch.optim as optim
device = 'cuda' if cuda.is_available() else 'cpu'
data_dir = '../input/birddata/train'
classes = []
img_per_class = []
for folder in os.listdir(data_dir):
classes.append(folder)
img_per_class.append(len(os.listdir(f'{data_dir}/{folder}')))
num_classes = len(classes)
df = pd.DataFrame({'Classes':classes, 'Examples':img_per_class})
train_transform = transforms.Compose([transforms.RandomRotation(15),transforms.RandomHorizontalFlip(),
transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
val_transform = transforms.Compose([transforms.RandomRotation(15),transforms.RandomHorizontalFlip(),
transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
class AverageMeter(object):
r"""Computes and stores the average and current value
"""
def __init__(self, name, fmt=':f'):
self.name = name
self.fmt = fmt
self.reset()
def reset(self):
self.val = 0
self.avg = 0
self.sum = 0
self.count = 0
def update(self, val, n=1):
self.val = val
self.sum += val * n
self.count += n
self.avg = self.sum / self.count
def __str__(self):
fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
return fmtstr.format(**self.__dict__)
class ProgressMeter(object):
def __init__(self, num_batches, *meters, prefix=""):
self.batch_fmtstr = self._get_batch_fmtstr(num_batches)
self.meters = meters
self.prefix = prefix
def print(self, batch):
entries = [self.prefix + self.batch_fmtstr.format(batch)]
entries += [str(meter) for meter in self.meters]
print('\t'.join(entries))
def _get_batch_fmtstr(self, num_batches):
num_digits = len(str(num_batches // 1))
fmt = '{:' + str(num_digits) + 'd}'
return '[' + fmt + '/' + fmt.format(num_batches) + ']'
def accuracy(output, target, topk=(1,)):
r"""Computes the accuracy over the $k$ top predictions for the specified values of k
"""
with torch.no_grad():
maxk = max(topk)
batch_size = target.size(0)
# _, pred = output.topk(maxk, 1, True, True)
# pred = pred.t()
# correct = pred.eq(target.view(1, -1).expand_as(pred))
# faster topk (ref: https://github.com/pytorch/pytorch/issues/22812)
_, idx = output.sort(descending=True)
pred = idx[:,:maxk]
pred = pred.t()
correct = pred.eq(target.view(1, -1).expand_as(pred))
res = []
for k in topk:
correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True)
res.append(correct_k.mul_(100.0 / batch_size))
return res
data = datasets.ImageFolder(data_dir)
train_size = int(len(data)*0.95)
val_size = int((len(data)-train_size))
train_data,val_data = random_split(data,[train_size,val_size])
torch.manual_seed(3334)
print(f'train size: {len(train_data)}\nval size: {len(val_data)}')
train_data.dataset.transform = train_transform
val_data.dataset.transform = val_transform
batch_size = 16
train_loader = DataLoader(train_data,batch_size=batch_size,shuffle=True, num_workers = 2)
val_loader = DataLoader(val_data,batch_size=batch_size,shuffle=False, num_workers = 2)
- kaggle notebook workdir 수정 코드
%cd '../input/efficientnet/EfficientNets-PyTorch-master'
import math
from models.layers import conv_bn_act
from models.layers import SamePadConv2d
from models.layers import Flatten
from models.layers import SEModule
from models.layers import DropConnect
%cd '/kaggle/working'
class MBConv(nn.Module):
def __init__(self, in_, out_, expand,
kernel_size, stride, skip,
se_ratio, dc_ratio=0.2):
super().__init__()
mid_ = in_ * expand
self.expand_conv = conv_bn_act(in_, mid_, kernel_size=1, bias=False) if expand != 1 else nn.Identity()
self.depth_wise_conv = conv_bn_act(mid_, mid_,
kernel_size=kernel_size, stride=stride,
groups=mid_, bias=False)
self.se = SEModule(mid_, int(in_ * se_ratio)) if se_ratio > 0 else nn.Identity()
self.project_conv = nn.Sequential(
SamePadConv2d(mid_, out_, kernel_size=1, stride=1, bias=False),
nn.BatchNorm2d(out_, 1e-3, 0.01)
)
# if _block_args.id_skip:
# and all(s == 1 for s in self._block_args.strides)
# and self._block_args.input_filters == self._block_args.output_filters:
self.skip = skip and (stride == 1) and (in_ == out_)
# DropConnect
# self.dropconnect = DropConnect(dc_ratio) if dc_ratio > 0 else nn.Identity()
# Original TF Repo not using drop_rate
# https://github.com/tensorflow/tpu/blob/05f7b15cdf0ae36bac84beb4aef0a09983ce8f66/models/official/efficientnet/efficientnet_model.py#L408
self.dropconnect = nn.Identity()
def forward(self, inputs):
expand = self.expand_conv(inputs)
x = self.depth_wise_conv(expand)
x = self.se(x)
x = self.project_conv(x)
if self.skip:
x = self.dropconnect(x)
x = x + inputs
return x
class MBBlock(nn.Module):
def __init__(self, in_, out_, expand, kernel, stride, num_repeat, skip, se_ratio, drop_connect_ratio=0.2):
super().__init__()
layers = [MBConv(in_, out_, expand, kernel, stride, skip, se_ratio, drop_connect_ratio)]
for i in range(1, num_repeat):
layers.append(MBConv(out_, out_, expand, kernel, 1, skip, se_ratio, drop_connect_ratio))
self.layers = nn.Sequential(*layers)
def forward(self, x):
return self.layers(x)
class EfficientNet(nn.Module):
def __init__(self, width_coeff, depth_coeff,
depth_div=8, min_depth=None,
dropout_rate=0.2, drop_connect_rate=0.2,
num_classes=400):
super().__init__()
min_depth = min_depth or depth_div
def renew_ch(x):
if not width_coeff:
return x
x *= width_coeff
new_x = max(min_depth, int(x + depth_div / 2) // depth_div * depth_div)
if new_x < 0.9 * x:
new_x += depth_div
return int(new_x)
def renew_repeat(x):
return int(math.ceil(x * depth_coeff))
self.stem = conv_bn_act(3, renew_ch(32), kernel_size=3, stride=2, bias=False)
self.blocks = nn.Sequential(
# input channel output expand k s skip se
MBBlock(renew_ch(32), renew_ch(16), 1, 3, 1, renew_repeat(1), True, 0.25, drop_connect_rate),
MBBlock(renew_ch(16), renew_ch(24), 6, 3, 2, renew_repeat(2), True, 0.25, drop_connect_rate),
MBBlock(renew_ch(24), renew_ch(40), 6, 5, 2, renew_repeat(2), True, 0.25, drop_connect_rate),
MBBlock(renew_ch(40), renew_ch(80), 6, 3, 2, renew_repeat(3), True, 0.25, drop_connect_rate),
MBBlock(renew_ch(80), renew_ch(112), 6, 5, 1, renew_repeat(3), True, 0.25, drop_connect_rate),
MBBlock(renew_ch(112), renew_ch(192), 6, 5, 2, renew_repeat(4), True, 0.25, drop_connect_rate),
MBBlock(renew_ch(192), renew_ch(320), 6, 3, 1, renew_repeat(1), True, 0.25, drop_connect_rate)
)
self.head = nn.Sequential(
*conv_bn_act(renew_ch(320), renew_ch(1280), kernel_size=1, bias=False),
nn.AdaptiveAvgPool2d(1),
nn.Dropout2d(dropout_rate, True) if dropout_rate > 0 else nn.Identity(),
Flatten(),
nn.Linear(renew_ch(1280), num_classes)
)
self.init_weights()
def init_weights(self):
for m in self.modules():
if isinstance(m, SamePadConv2d):
nn.init.kaiming_normal_(m.weight, mode="fan_out")
elif isinstance(m, nn.Linear):
init_range = 1.0 / math.sqrt(m.weight.shape[1])
nn.init.uniform_(m.weight, -init_range, init_range)
def forward(self, inputs):
stem = self.stem(inputs)
x = self.blocks(stem)
head = self.head(x)
return head
model = EfficientNet(1.06, 1, dropout_rate = 0.5, drop_connect_rate=0.5, num_classes=400)
pytorch_total_params = sum(p.numel() for p in model.parameters())
print(f"Number of parameters: {pytorch_total_params}")
if int(pytorch_total_params) > 5000000:
print('Your model has the number of parameters more than 5 millions..')
sys.exit()
device = torch.device('cuda:0' if cuda.is_available() else 'cpu')
model.to(device)
print(device)
# optimizer = optim.Adam(model.parameters(), lr=0.0002, weight_decay=0.0005)
# optimizer = optim.NAdam(params, lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.005, momentum_decay=0.004)
optimizer = torch.optim.SGD(model.parameters(), lr=0.02, momentum=0.9, nesterov=True, weight_decay=0.0005)
scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones = [20, 30, 35, 40, 43, 47], gamma = 0.5)
# scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, 5, eta_min=0, last_epoch=- 1, verbose=False)
def fit(model,criterion,optimizer,num_epochs=10):
print_freq = 200
start = time.time()
best_model = model.state_dict()
best_acc = 0
train_loss_over_time = []
val_loss_over_time = []
train_acc_over_time = []
val_acc_over_time = []
# each epoch has a training and validation phase
for epoch in range(num_epochs):
print("\n----- epoch: {}, lr: {} -----".format(epoch, optimizer.param_groups[0]["lr"]))
batch_time = AverageMeter('Time', ':6.3f')
acc = AverageMeter('Accuracy', ':.4e')
progress = ProgressMeter(len(train_loader), batch_time, acc, prefix="Epoch: [{}]".format(epoch))
for phase in ['train','val']:
if phase == 'train':
data_loader = train_loader
model.train() # set the model to train mode
end = time.time()
else:
data_loader = val_loader
model.eval() # set the model to evaluate mode
end = time.time()
running_loss = 0.0
running_corrects = 0.0
# iterate over the data
for i,(inputs,labels) in enumerate(data_loader):
inputs = inputs.to(device)
labels = labels.to(device)
# zero the parameter gradients
optimizer.zero_grad()
# forward
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_,pred = torch.max(outputs,dim=1)
loss = criterion(outputs,labels)
# backward + optimize only if in training phase
if phase == 'train':
loss.backward()
optimizer.step()
# calculating the loss and accuracy
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(pred == labels.data)
epoch_acc = (running_corrects.double()/len(train_data)).cpu().numpy()
acc.update(epoch_acc.item(), inputs.size(0))
if phase == 'train':
batch_time.update(time.time() - end)
end = time.time()
if i % print_freq == 0:
progress.print(i)
if phase == 'train':
epoch_loss = running_loss/len(train_data)
train_loss_over_time.append(epoch_loss)
epoch_acc = (running_corrects.double()/len(train_data)).cpu().numpy()
train_acc_over_time.append(epoch_acc)
else:
epoch_loss = running_loss/len(val_data)
val_loss_over_time.append(epoch_loss)
epoch_acc = (running_corrects.double()/len(val_data)).cpu().numpy()
val_acc_over_time.append(epoch_acc)
print(f'{phase} loss: {epoch_loss:.3f}, acc: {epoch_acc:.3f}')
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
torch.save(model.state_dict(), 'model_best.pt')
torch.save(model.state_dict(),'model_latest.pt')
scheduler.step()
print('-'*60)
print('\n')
elapsed_time = time.time() - start
print('==> {:.2f} seconds to train this epoch\n'.format(elapsed_time))
print(f'best accuracy: {best_acc:.3f}')
# load best model weights
model.load_state_dict(best_model)
loss = {'train':train_loss_over_time, 'val':val_loss_over_time}
acc = {'train':train_acc_over_time, 'val':val_acc_over_time}
return model,loss, acc
criterion = nn.CrossEntropyLoss()
epochs = 50
history, loss, acc = fit(model, criterion, optimizer, num_epochs = epochs)
train_loss = loss['train']
val_loss = loss['val']
train_acc = acc['train']
val_acc = acc['val']
epochs_range = range(epochs)
plt.figure(figsize=(20,10))
plt.subplot(1,2,1)
plt.ylim(0,10)
plt.xlim(0,50)
plt.plot(epochs_range, train_loss, label='train_loss')
plt.plot(epochs_range, val_loss, label='val_loss')
plt.legend(loc=0)
plt.title('Loss')
plt.subplot(1,2,2)
plt.plot(epochs_range, train_acc ,label='train_acc')
plt.plot(epochs_range, val_acc, label='val_acc')
plt.legend(loc=0)
plt.ylim(0,1)
plt.xlim(0,50)
plt.title('Accuracy')
seed = 0
torch.manual_seed(seed)
if cuda:
torch.cuda.manual_seed(seed)
torch.manual_seed(3334)
test_transform = transforms.Compose([transforms.RandomRotation(15),transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
# splitting the data into train/validation/test sets
test_data_dir = '../input/birddata/test'
_data = datasets.ImageFolder(test_data_dir)
test1_size = int(len(_data)*1)
test2_size = int((len(_data)-test1_size))
test_data, test2_data = torch.utils.data.random_split(_data,[test1_size, test2_size])
torch.manual_seed(3334)
print(f'test size: {len(test_data)}')
test_data.dataset.transform = test_transform
batch_size = 64
test_loader = DataLoader(test_data, batch_size = batch_size, shuffle = False)
print(test_loader)
import itertools
# testing how good the model is
def evaluate(model,criterion):
model.eval() # setting the model to evaluate mode
preds = []
Category = []
test_model = EfficientNet(1.06, 1, dropout_rate = 0.18, drop_connect_rate=0.24, num_classes=400).cuda()
test_model.load_state_dict(torch.load('./model_best.pt'))
for inputs, label_ in test_loader:
inputs = inputs.to(device)
labels = label_.to(device)
# predicting
with torch.no_grad():
outputs = test_model(inputs)
_,pred = torch.max(outputs,dim=1)
preds.append(pred)
category = [t.cpu().numpy() for t in preds]
t_category = list(itertools.chain(*category))
Id = list(range(0, len(t_category)))
prediction = {
'Id': Id,
'Category': t_category
}
prediction_df = pd.DataFrame(prediction, columns=['Id','Category'])
prediction_df.to_csv('prediction.csv', index=False)
print('Done!!')
return preds
# testing the model
predictions = evaluate(model, criterion)