
torch에서 제공하는 models.resnet34(pretrained=False)로 먼저 성능을 평가한 후에,
내가 만든 resnet34인 MyResNet34()로 성능을 평가하여
둘이 비슷한 성능을 보이는지 관찰할 계획..
그러기 위해 PaperResNet34부터 training 시작
pytorch 첫 실습이기 때문에
논문에서 dataset을 detail하게 전처리한 것을 신경쓰지 않고,
우선 models.resnet34()가 제대로 training되는지 확인하고자 함.
논문에서 learning rate를 plateau일 때 10씩 divide했다고 하여,
lr_scheduler를 다음과 같이 설정함.
하지만 epoch 40이 될 무렵, val loss와 val accuracy가 saturation되었는데도 lr decay가 되지 않았음.
threshold argument의 default값이 0.0001이어서
lr_scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5, verbose=True)




실험 0-1, 0-2를 거쳐 torch에서 제공하는 models.resnet34(pretrained=False)은 제대로 학습되는 것을 알 수 있었음.
이제 논문의 val loss, accuracy와 똑같게 training시키기 위해
data preprocessing, hyper parameter를 수정할 것임.
이제 논문의 val loss, acc와 똑같게 training시키기 위해
dataset preprocessing, hyper parameter를 수정할 것임
논문에서 언급한 training data preprocessing 방법
THe image is resized with its shorter side randomly sampled in [256, 480] for scale augmentation[41]

224 x 224 crop is randomly sampled from an image or it's horizontal flip, with the per-pixel mean substracted.

The color augmentation in [21] is used.
논문에서 한 preprocessing 방법을 기반으로 작성한 코드 :
class Lighting(object):
"""Lighting noise(AlexNet - style PCA - based noise)"""
def __init__(self, alphastd, eigval, eigvec):
self.alphastd = alphastd
self.eigval = eigval
self.eigvec = eigvec
def __call__(self, img):
if self.alphastd == 0:
return img
alpha = img.new().resize_(3).normal_(0, self.alphastd)
rgb = self.eigvec.type_as(img).clone()\
.mul(alpha.view(1, 3).expand(3, 3))\
.mul(self.eigval.view(1, 3).expand(3, 3))\
.sum(1).squeeze()
return img.add(rgb.view(3, 1, 1).expand_as(img))
__imagenet_pca = {
'eigval': torch.Tensor([0.2175, 0.0188, 0.0045]),
'eigvec': torch.Tensor([
[-0.5675, 0.7192, 0.4009],
[-0.5808, -0.0045, -0.8140],
[-0.5836, -0.6948, 0.4203],
])
}
trainset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/train',
transform = transforms.Compose([
transforms.RandomResizedCrop(size=(256, 480)),
transforms.RandomCrop(size=224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
Lighting(0.1, __imagenet_pca['eigval'], __imagenet_pca['eigvec']),
])
)
valset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/val',
transform=transforms.Compose([
transforms.Resize(size=256),
transforms.CenterCrop(size=224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
)
lr_scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=3, verbose=True, threshold=0.01)



exp0-3에서는 왜 성능이 더 나빠졌는지 이유를 몰랐어서(RandomResizedCrop() 때문임을 몰랐어서)
논문에서의 preprocessing 방법 말고,
pytorch community 또는 pytorch resnet sample code에서 사용했던 preprocessing을 그대로 사용하였다.
(https://github.com/pytorch/examples/blob/42e5b996718797e45c46a25c55b031e6768f8440/imagenet/main.py#L89-L101)

trainset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/train',
transform = transforms.Compose([
# transforms.RandomResizedCrop(size=(256, 480)),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
# Lighting(0.1, __imagenet_pca['eigval'], __imagenet_pca['eigvec']),
])
)valset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/val',
transform=transforms.Compose([
transforms.Resize(size=256),
transforms.CenterCrop(size=224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
)exp [0-1] ~ [0-3] + 추가 실험을 진행하면서 얻은
lr_schduler에 대한 heuristic한 직관으로 다음과 같이 설정.
lr_scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=3, verbose=True, threshold=0.01)


Top-1 val acc : 70.72%
Paper :

Top-1 val acc : 73.02%

Top-5 val acc : 91.32%

torch ResNet34는 training하는 데에 성공했지만,
논문과 image preprocessing, hyper parameter가 미세하게 달라서
완전히 똑같은 validation accuracy를 달성하지는 못했다..
[Paper]
top-1 val acc (10-crop) : 75.48%
top-5 val acc (10-crop) : 92.54%
[Torch]
top-1 val acc (10-crop) : 73.02%
top-5 val acc (10-crop) : 91.32%
앞에서 했던 실험들 중 바로 위에서 했던
Paper_ResNet34_exp0-4.ipynb 가장 성공적인 성능을 보였으니
data preprocessing을 똑같이 하여 내가 만든 resnet34() 가 제대로 training이 진행되는지 확인해보고자 함
preprocessing, hyper parameter 모두 동일.
model만 models.resnet34()가 아닌 내가 직접 구현한 class인 resnet34()로 대체하여
training 진행.

Paper :

Top-1 val acc : 72.914% (torch : 73.02%)

Top-5 val acc : 91.094% (torch : 91.32%)

10-crop testing을 하기 전에 centercrop()만 했을 때,
논문과 val acc 차이가 약 7~9% 차이가 났기 때문에
architecture 문제인지, image preprocessing 문제인지, hyper parameter문제인지 파악하기 어려웠다.
일단 torch에서 제공하는 models.resnet34()와 내가 구현한 myresnet34()의 val acc가 똑같이 나왔기 때문에
architecture 상의 문제는 없다고 판단했지만,
혹시 몰라서 ResNet32 on CIFAR-10을 구현하여 architecture의 문제가 없는지 또 한 번 판단하기로 했다.
MyResNet34의 architecture 문제가 있는지 없는지 판단하기 위해
똑같은 residual block을 이용하여 만든 resnet32를 이용하여
ResNet32 on CIFAR-10이 논문에서의 val acc와 똑같이 나오는지 판단하기로 했다.
resnet paper에 소개한 대로 CIFAR-10을 위한 ResNet32를 구성하였고,
option (A), (B) 2개로 각각 training 시켜보았다.
참고로 논문에서 ResNet32 on CIFAR-10에는 option (A)를 사용함.
option (B)를 사용했다.



Top-1 test acc : 91.84%(Paper : 92.49%)
option (A)를 사용했다.

myresnet32 : 0.46M

paper resnet32 : 0.46M

Top-1 test acc : 91.92%(Paper : 92.49%)
resnet32 on CIFAR-10을 진행하면서,
논문에 있는 figure 4.를 유심히 봤었는데
논문에서 글로는 plateau일 때마다 10씩 divide해줬다고 되어있었지만
figure 4에서는 30, 60, 90 epoch마다 10씩 divide해준 것을 추측할 수 있었다.
(figure 4의 오른쪽 graph)

그래서 exp2에서는
lr_scheduler로 ReduceLROnPlateau()를 사용하지 않고,
lr_scheduler = lr_scheduler.MultiStepLR(optimizer, milestones=[30, 60, 90], gamma=0.1, verbose=True)
로 수정하였다.
def shorter_side_resize(img):
w, h = img.size
if w < h:
new_w = torch.randint(256, 480, (1,)).item()
new_h = int(new_w)
else:
new_h = torch.randint(256, 480, (1,)).item()
new_w = int(new_h)
return transforms.Resize((new_h, new_w))(img)
trainset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/train',
transform = transforms.Compose([
shorter_side_resize,
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
normalize,
])
)
valset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/val',
transform=transforms.Compose([
# 10-crop
transforms.Resize(256),
# transforms.Resize((256 + 480) // 2),
transforms.TenCrop(224),
transforms.Lambda(lambda crops: torch.stack([transforms.ToTensor()(crop) for crop in crops])),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
)
Paper :

exp1 :

top-1 val acc : 75.266%
top-5 val acc : 91.678%
exp2 :

top-1 val acc : 73.006%
top-5 val acc : 91.678%

그런데 document를 자세히 참고하여 봤더니, (https://pytorch.org/vision/main/generated/torchvision.transforms.RandomResizedCrop.html)
RandomResizedCrop(224)은 scale을 default (0.08, 1.0)으로 조정한 뒤,
random area를 224로 crop하는 함수였다.
하지만 scale augmentation을 위해서 앞서 shorter_side_resize 함수를 통해
scale을 [256, 480]으로 random하게 맞춰준 다음에 random하게 224만 crop하면 되는 것이기 때문에
RandomResizedCrop()이 아닌, RandomCrop()을 사용하는 것이 더 알맞은 것이라고 생각이 들었다.
그래서 trainset transform에 transforms.RandomResizedCrop(224) 대신에
transforms.RandomCrop(224)으로 변경하여 다시 처음부터 학습을 돌려보았다.
trainset = torchvision.datasets.ImageFolder(
root='/home/hslee/Desktop/Datasets/ILSVRC2012_ImageNet/train',
transform = transforms.Compose([
shorter_side_resize,
# transforms.RandomResizedCrop(224),
transforms.RandomCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
normalize,
])
)


Paper :

exp1 :
top-1 val acc : 75.114%top-5 val acc : 92.362%
exp2 :
top-1 val acc : 73.86%top-5 val acc : 91.99%

bottleneck architecture를 사용.

Paper :
exp1 : top-1 val acc : 76.45%top-5 val acc : 93.248%
exp2 : top-1 val acc : 75.52%top-5 val acc : 92.762%
논문에서 제시한 architecutre를 직접 구현해본 결과,
accuracy가 약 0.05~0.1%가 떨어지는데, 거의 동일하다고 볼 수 있을 것 같다.
구현에는 성공했지만
아직실험은 시도해보지 못했다.
구현보다는실험이 중요하다고 생각하여 이제실험에 대해 고민해봐야겠다...