Browse Source

ResNet changes for Office dataset

master
Fazil Altinel 4 years ago
parent
commit
eb1daa18bf
  1. 6
      README.md
  2. 30
      core/train.py
  3. 2
      datasets/office.py
  4. 30
      experiments/office.py
  5. 3
      models/alexnet.py
  6. 44
      models/model.py
  7. 213
      models/resnet.py
  8. 15
      utils/utils.py

6
README.md

@ -9,8 +9,8 @@ A PyTorch implementation for paper *[Unsupervised Domain Adaptation by Backpropa
## Environment ## Environment
- Python 3.6
- PyTorch 1.0
- Python 3.8.5
- PyTorch 1.6.0
## Note ## Note
@ -26,6 +26,8 @@ Before running the training code, make sure that `DATASETDIR` environment variab
- `GTSRBmodel()` - `GTSRBmodel()`
- `AlexModel` - `AlexModel`
- not successful, mainly due to the pretrained model difference - not successful, mainly due to the pretrained model difference
- `ResNet50`
- Better and more stable results than AlexNet.
## Result ## Result

30
core/train.py

@ -126,20 +126,30 @@ def train_dann(model, params, src_data_loader, tgt_data_loader, tgt_data_loader_
optimizer = optim.SGD(model.parameters(), lr=params.lr, momentum=params.momentum, weight_decay=params.weight_decay) optimizer = optim.SGD(model.parameters(), lr=params.lr, momentum=params.momentum, weight_decay=params.weight_decay)
else: else:
print("training office task") print("training office task")
# parameter_list = [{
# "params": model.features.parameters(),
# "lr": 0.001
# }, {
# "params": model.fc.parameters(),
# "lr": 0.001
# }, {
# "params": model.bottleneck.parameters()
# }, {
# "params": model.classifier.parameters()
# }, {
# "params": model.discriminator.parameters()
# }]
parameter_list = [{ parameter_list = [{
"params": model.features.parameters(),
"lr": 0.001
"params": model.feature_extractor.parameters(),
"lr": 0.0001
}, { }, {
"params": model.fc.parameters(),
"lr": 0.001
"params": model.classifier.parameters(),
"lr": 0.0001
}, { }, {
"params": model.bottleneck.parameters()
}, {
"params": model.classifier.parameters()
}, {
"params": model.discriminator.parameters()
"params": model.discriminator.parameters(),
"lr": 0.0001
}] }]
optimizer = optim.SGD(parameter_list, lr=0.001, momentum=0.9)
optimizer = optim.SGD(parameter_list, momentum=0.9, weight_decay=1e-4)
criterion = nn.CrossEntropyLoss() criterion = nn.CrossEntropyLoss()

2
datasets/office.py

@ -17,7 +17,7 @@ def get_office(dataset_root, batch_size, category):
# datasets and data_loader # datasets and data_loader
office_dataset = datasets.ImageFolder( office_dataset = datasets.ImageFolder(
os.path.join(dataset_root, 'office', category, 'images'), transform=pre_process)
os.path.join(dataset_root, 'office31', category, 'images'), transform=pre_process)
office_dataloader = torch.utils.data.DataLoader( office_dataloader = torch.utils.data.DataLoader(
dataset=office_dataset, batch_size=batch_size, shuffle=True, num_workers=0) dataset=office_dataset, batch_size=batch_size, shuffle=True, num_workers=0)

30
experiments/office.py

@ -1,19 +1,28 @@
import os import os
import sys import sys
import torch import torch
sys.path.append('../')
sys.path.append(os.path.abspath('.'))
from core.train import train_dann from core.train import train_dann
from core.test import test from core.test import test
from models.model import AlexModel from models.model import AlexModel
from models.model import ResNet50
from utils.utils import get_data_loader, init_model, init_random_seed from utils.utils import get_data_loader, init_model, init_random_seed
from utils.altutils import setLogger
# To avoid proxy issues while downloading pretrained model
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
class Config(object): class Config(object):
# params for path # params for path
dataset_root = os.path.expanduser(os.path.join('~', 'Datasets'))
model_root = os.path.expanduser(os.path.join('~', 'Models', 'pytorch-dann'))
currentDir = os.path.dirname(os.path.realpath(__file__))
dataset_root = os.environ["DATASETDIR"]
model_root = os.path.join(currentDir, 'checkpoints')
finetune_flag = True
lr_adjust_flag = 'non-simple'
src_only_flag = False
# params for datasets and data loader # params for datasets and data loader
batch_size = 32 batch_size = 32
@ -52,6 +61,10 @@ class Config(object):
params = Config() params = Config()
currentDir = os.path.dirname(os.path.realpath(__file__))
logFile = os.path.join(currentDir+'/../', 'dann-{}-{}.log'.format(params.src_dataset, params.tgt_dataset))
loggi = setLogger(logFile)
# init random seed # init random seed
init_random_seed(params.manual_seed) init_random_seed(params.manual_seed)
@ -63,12 +76,13 @@ src_data_loader = get_data_loader(params.src_dataset, params.dataset_root, param
tgt_data_loader = get_data_loader(params.tgt_dataset, params.dataset_root, params.batch_size) tgt_data_loader = get_data_loader(params.tgt_dataset, params.dataset_root, params.batch_size)
# load dann model # load dann model
dann = init_model(net=AlexModel(), restore=None)
# dann = init_model(net=AlexModel(), restore=None)
dann = init_model(net=ResNet50(), restore=None)
# train dann model # train dann model
print("Start training dann model.") print("Start training dann model.")
if not (dann.restored and params.dann_restore):
dann = train_dann(dann, params, src_data_loader, tgt_data_loader, tgt_data_loader, device)
# if not (dann.restored and params.dann_restore):
dann = train_dann(dann, params, src_data_loader, tgt_data_loader, tgt_data_loader, device, loggi)
print('done') print('done')

3
models/alexnet.py

@ -80,9 +80,10 @@ def alexnet(pretrained=False, **kwargs):
Args: Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet pretrained (bool): If True, returns a model pre-trained on ImageNet
""" """
model_root = os.environ["MODELDIR"]
model = AlexNet(**kwargs) model = AlexNet(**kwargs)
if pretrained: if pretrained:
model_path = '/home/wogong/Models/alexnet.pth.tar'
model_path = os.path.join(model_root, 'alexnet.pth.tar')
pretrained_model = torch.load(model_path) pretrained_model = torch.load(model_path)
model.load_state_dict(pretrained_model['state_dict']) model.load_state_dict(pretrained_model['state_dict'])
return model return model

44
models/model.py

@ -1,9 +1,12 @@
"""DANN model.""" """DANN model."""
import os
import torch
import torch.nn as nn import torch.nn as nn
from .functions import ReverseLayerF from .functions import ReverseLayerF
from torchvision import models from torchvision import models
from .alexnet import alexnet from .alexnet import alexnet
from .resnet import resnet50
from utils.utils import weights_init
class Classifier(nn.Module): class Classifier(nn.Module):
@ -260,7 +263,8 @@ class AlexModel(nn.Module):
def __init__(self): def __init__(self):
super(AlexModel, self).__init__() super(AlexModel, self).__init__()
self.restored = False self.restored = False
model_alexnet = models.alexnet(pretrained=True)
# model_alexnet = models.alexnet(pretrained=True)
model_alexnet = alexnet(pretrained=True)
self.features = model_alexnet.features self.features = model_alexnet.features
@ -302,3 +306,39 @@ class AlexModel(nn.Module):
domain_output = self.discriminator(reverse_bottleneck) domain_output = self.discriminator(reverse_bottleneck)
return class_output, domain_output return class_output, domain_output
class ResNet50(nn.Module):
def __init__(self):
super(ResNet50, self).__init__()
resnetModel = models.resnet50(pretrained=True)
feature_map = list(resnetModel.children())
feature_map.pop()
self.feature_extractor = nn.Sequential(*feature_map)
self.classifier = nn.Sequential(
nn.Linear(2048, 31),
)
self.discriminator = nn.Sequential(
nn.Linear(2048, 1024),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(1024, 1024),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(1024, 2),
)
def forward(self, input_data, alpha=-1, sln='dann'):
input_data = input_data.expand(input_data.data.shape[0], 3, 227, 227)
feature = self.feature_extractor(input_data)
feature = feature.view(-1, 2048)
reverse_bottleneck = ReverseLayerF.apply(feature, alpha)
class_output = self.classifier(feature)
domain_output = self.discriminator(reverse_bottleneck)
return class_output, domain_output

213
models/resnet.py

@ -0,0 +1,213 @@
import torch.nn as nn
import math
import torch.utils.model_zoo as model_zoo
__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',
'resnet152']
model_urls = {
'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
}
def conv3x3(in_planes, out_planes, stride=1):
"3x3 convolution with padding"
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
padding=1, bias=False)
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(Bottleneck, self).__init__()
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
if self.downsample is not None:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=1000):
self.inplanes = 64
super(ResNet, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, layers[0])
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
self.avgpool = nn.AvgPool2d(7, stride=1)
# self.fc = nn.Linear(512 * block.expansion, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
def _make_layer(self, block, planes, blocks, stride=1):
downsample = None
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
nn.Conv2d(self.inplanes, planes * block.expansion,
kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(planes * block.expansion),
)
layers = []
layers.append(block(self.inplanes, planes, stride, downsample))
self.inplanes = planes * block.expansion
for i in range(1, blocks):
layers.append(block(self.inplanes, planes))
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)
# x = x.view(x.size(0), -1)
# x = self.fc(x)
return x
def resnet18(pretrained=False, **kwargs):
"""Constructs a ResNet-18 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))
return model
def resnet34(pretrained=False, **kwargs):
"""Constructs a ResNet-34 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['resnet34']))
return model
def resnet50(pretrained=False, **kwargs):
"""Constructs a ResNet-50 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['resnet50']))
return model
def resnet101(pretrained=False, **kwargs):
"""Constructs a ResNet-101 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['resnet101']))
return model
def resnet152(pretrained=False, **kwargs):
"""Constructs a ResNet-152 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['resnet152']))
return model

15
utils/utils.py

@ -35,6 +35,19 @@ def init_weights(layer):
layer.bias.data.fill_(0) layer.bias.data.fill_(0)
def weights_init(m):
classname = m.__class__.__name__
if classname.find('Conv') != -1:
m.weight.data.normal_(0.0, 0.02)
elif classname.find('BatchNorm') != -1:
m.weight.data.normal_(1.0, 0.02)
m.bias.data.fill_(0)
elif classname.find('Linear') != -1:
size = m.weight.size()
m.weight.data.normal_(0.0, 0.1)
m.bias.data.fill_(0)
def init_random_seed(manual_seed): def init_random_seed(manual_seed):
"""Init random seed.""" """Init random seed."""
seed = None seed = None
@ -61,6 +74,8 @@ def get_data_loader(name, dataset_root, batch_size, train=True):
return get_office(dataset_root, batch_size, 'amazon') return get_office(dataset_root, batch_size, 'amazon')
elif name == "webcam31": elif name == "webcam31":
return get_office(dataset_root, batch_size, 'webcam') return get_office(dataset_root, batch_size, 'webcam')
elif name == "dslr31":
return get_office(dataset_root, batch_size, 'dslr')
elif name == "webcam10": elif name == "webcam10":
return get_officecaltech(dataset_root, batch_size, 'webcam') return get_officecaltech(dataset_root, batch_size, 'webcam')
elif name == "syndigits": elif name == "syndigits":

Loading…
Cancel
Save