Repository: PRIS-CV/Mutual-Channel-Loss Branch: master Commit: befb3692cd0d Files: 9 Total size: 29.0 KB Directory structure: gitextract_1o87plqc/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── CUB-200-2011.py ├── CUB-200-2011_ResNet18.py ├── LICENSE ├── README.md ├── _config.yml └── my_pooling.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/custom.md ================================================ --- name: Custom issue template about: Describe this issue template's purpose here. title: '' labels: '' assignees: '' --- ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: CUB-200-2011.py ================================================ '''PyTorch CUB-200-2011 Training with VGG16 (TRAINED FROM SCRATCH).''' from __future__ import print_function import os # import nni import time import torch import logging import argparse import torchvision import random import torch.nn as nn import numpy as np import torch.optim as optim import torch.nn.functional as F from torch.autograd import Variable import torch.backends.cudnn as cudnn import torchvision from my_pooling import my_MaxPool2d,my_AvgPool2d import torchvision.transforms as transforms logger = logging.getLogger('MC_VGG_224') os.environ["CUDA_VISIBLE_DEVICES"] = "2,3" lr = 0.1 nb_epoch = 300 criterion = nn.CrossEntropyLoss() #Data print('==> Preparing data..') transform_train = transforms.Compose([ transforms.Scale((224,224)), transforms.RandomCrop(224, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) transform_test = transforms.Compose([ transforms.Scale((224,224)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) trainset = torchvision.datasets.ImageFolder(root='/home/data/Birds/train', transform=transform_train) trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=16, drop_last = True) testset = torchvision.datasets.ImageFolder(root='/home/data/Birds/test', transform=transform_test) testloader = torch.utils.data.DataLoader(testset, batch_size=32, shuffle=True, num_workers=16) print('==> Building model..') cfg = { 'VGG11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], 'VGG13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], 'VGG16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 600, 'M', 512, 512, 600], 'VGG19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'], } class VGG(nn.Module): def __init__(self, vgg_name): super(VGG, self).__init__() self.features = self._make_layers(cfg[vgg_name]) self.classifier = nn.Linear(512, 10) def forward(self, x): out = self.features(x) out = out.view(out.size(0), -1) out = self.classifier(out) return out def _make_layers(self, cfg): layers = [] in_channels = 3 for x in cfg: if x == 'M': layers += [nn.MaxPool2d(kernel_size=2, stride=2)] else: layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1), nn.BatchNorm2d(x), nn.ReLU(inplace=True)] in_channels = x layers += [nn.AvgPool2d(kernel_size=1, stride=1)] return nn.Sequential(*layers) def Mask(nb_batch, channels): foo = [1] * 2 + [0] * 1 bar = [] for i in range(200): random.shuffle(foo) bar += foo bar = [bar for i in range(nb_batch)] bar = np.array(bar).astype("float32") bar = bar.reshape(nb_batch,200*channels,1,1) bar = torch.from_numpy(bar) bar = bar.cuda() bar = Variable(bar) return bar def supervisor(x,targets,height,cnum): mask = Mask(x.size(0), cnum) branch = x branch = branch.reshape(branch.size(0),branch.size(1), branch.size(2) * branch.size(3)) branch = F.softmax(branch,2) branch = branch.reshape(branch.size(0),branch.size(1), x.size(2), x.size(2)) branch = my_MaxPool2d(kernel_size=(1,cnum), stride=(1,cnum))(branch) branch = branch.reshape(branch.size(0),branch.size(1), branch.size(2) * branch.size(3)) loss_2 = 1.0 - 1.0*torch.mean(torch.sum(branch,2))/cnum # set margin = 3.0 branch_1 = x * mask branch_1 = my_MaxPool2d(kernel_size=(1,cnum), stride=(1,cnum))(branch_1) branch_1 = nn.AvgPool2d(kernel_size=(height,height))(branch_1) branch_1 = branch_1.view(branch_1.size(0), -1) loss_1 = criterion(branch_1, targets) return [loss_1, loss_2] class model_bn(nn.Module): def __init__(self, feature_size=512,classes_num=200): super(model_bn, self).__init__() self.features_1 = nn.Sequential(*list(VGG('VGG16').features.children())[:34]) self.features_2 = nn.Sequential(*list(VGG('VGG16').features.children())[34:]) self.max = nn.MaxPool2d(kernel_size=2, stride=2) self.num_ftrs = 600*7*7 self.classifier = nn.Sequential( nn.BatchNorm1d(self.num_ftrs), #nn.Dropout(0.5), nn.Linear(self.num_ftrs, feature_size), nn.BatchNorm1d(feature_size), nn.ELU(inplace=True), #nn.Dropout(0.5), nn.Linear(feature_size, classes_num), ) def forward(self, x, targets): x = self.features_1(x) x = self.features_2(x) if self.training: MC_loss = supervisor(x,targets,height=14,cnum=3) x = self.max(x) x = x.view(x.size(0), -1) x = self.classifier(x) loss = criterion(x, targets) if self.training: return x, loss, MC_loss else: return x, loss use_cuda = torch.cuda.is_available() net =model_bn(512, 200) if use_cuda: net.classifier.cuda() net.features_1.cuda() net.features_2.cuda() net.classifier = torch.nn.DataParallel(net.classifier) net.features_1 = torch.nn.DataParallel(net.features_1) net.features_2 = torch.nn.DataParallel(net.features_2) cudnn.benchmark = True def train(epoch,net, args, trainloader,optimizer): print('\nEpoch: %d' % epoch) net.train() train_loss = 0 correct = 0 total = 0 idx = 0 for batch_idx, (inputs, targets) in enumerate(trainloader): idx = batch_idx inputs, targets = inputs.cuda(), targets.cuda() optimizer.zero_grad() inputs, targets = Variable(inputs), Variable(targets) out, ce_loss, MC_loss = net(inputs, targets) loss = ce_loss + args["alpha_1"] * MC_loss[0] + args["beta_1"] * MC_loss[1] loss.backward() optimizer.step() train_loss += loss.item() _, predicted = torch.max(out.data, 1) total += targets.size(0) correct += predicted.eq(targets.data).cpu().sum().item() train_acc = 100.*correct/total train_loss = train_loss/(idx+1) logging.info('Iteration %d, train_acc = %.5f,train_loss = %.6f' % (epoch, train_acc,train_loss)) return train_acc, train_loss def test(epoch,net,testloader,optimizer): net.eval() test_loss = 0 correct = 0 total = 0 idx = 0 for batch_idx, (inputs, targets) in enumerate(testloader): with torch.no_grad(): idx = batch_idx if use_cuda: inputs, targets = inputs.cuda(), targets.cuda() inputs, targets = Variable(inputs), Variable(targets) out, ce_loss = net(inputs,targets) test_loss += ce_loss.item() _, predicted = torch.max(out.data, 1) total += targets.size(0) correct += predicted.eq(targets.data).cpu().sum().item() test_acc = 100.*correct/total test_loss = test_loss/(idx+1) logging.info('test, test_acc = %.4f,test_loss = %.4f' % (test_acc,test_loss)) return test_acc def cosine_anneal_schedule(t): cos_inner = np.pi * (t % (nb_epoch )) # t - 1 is used when t has 1-based indexing. cos_inner /= (nb_epoch ) cos_out = np.cos(cos_inner) + 1 return float( 0.1 / 2 * cos_out) optimizer = optim.SGD([ {'params': net.classifier.parameters(), 'lr': 0.1}, {'params': net.features_1.parameters(), 'lr': 0.1}, {'params': net.features_2.parameters(), 'lr': 0.1}, ], momentum=0.9, weight_decay=5e-4) def get_params(): # Training settings parser = argparse.ArgumentParser(description='PyTorch MC2_AutoML Example') parser.add_argument('--alpha_1', type=float, default=1.5, metavar='ALPHA', help='alpha_1 value (default: 2.0)') parser.add_argument('--beta_1', type=float, default=20.0, metavar='BETA', help='beta_1 value (default: 20.0)') args, _ = parser.parse_known_args() return args if __name__ == '__main__': try: args = vars(get_params()) print(args) # main(params) max_val_acc = 0 for epoch in range(1, nb_epoch+1): if epoch ==150: lr = 0.01 if epoch ==225: lr = 0.001 optimizer.param_groups[0]['lr'] = lr optimizer.param_groups[1]['lr'] = lr optimizer.param_groups[2]['lr'] = lr train(epoch, net, args,trainloader,optimizer) test_acc = test(epoch, net,testloader,optimizer) if test_acc >max_val_acc: max_val_acc = test_acc print("max_val_acc", max_val_acc) except Exception as exception: logger.exception(exception) raise ================================================ FILE: CUB-200-2011_ResNet18.py ================================================ '''PyTorch CUB-200-2011 Training with ResNet18 (TRAINED FROM SCRATCH). NOTICE: for baseline, the channel of the final features should keep same with the Vanilla ResNet18''' from __future__ import print_function import os # import nni import time import torch import logging import argparse import torchvision import random import torch.nn as nn import numpy as np import torch.optim as optim import torch.nn.functional as F from torch.autograd import Variable import torch.backends.cudnn as cudnn import torchvision from my_pooling import my_MaxPool2d,my_AvgPool2d import torchvision.transforms as transforms logger = logging.getLogger('MC_ResNet18_224') os.environ["CUDA_VISIBLE_DEVICES"] = "0" lr = 0.1 nb_epoch = 300 criterion = nn.CrossEntropyLoss() #Data print('==> Preparing data..') transform_train = transforms.Compose([ transforms.Scale((224,224)), transforms.RandomCrop(224, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) transform_test = transforms.Compose([ transforms.Scale((224,224)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) trainset = torchvision.datasets.ImageFolder(root='/home/data/Birds/train', transform=transform_train) trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=16, drop_last = True) testset = torchvision.datasets.ImageFolder(root='/home/data/Birds/test', transform=transform_test) testloader = torch.utils.data.DataLoader(testset, batch_size=32, shuffle=True, num_workers=16) print('==> Building model..') # Model 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, 600, layers[3], stride=1) 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 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 net = resnet18(pretrained=False) def Mask(nb_batch, channels): foo = [1] * 2 + [0] * 1 bar = [] for i in range(200): random.shuffle(foo) bar += foo bar = [bar for i in range(nb_batch)] bar = np.array(bar).astype("float32") bar = bar.reshape(nb_batch,200*channels,1,1) bar = torch.from_numpy(bar) bar = bar.cuda() bar = Variable(bar) return bar def supervisor(x,targets,height,cnum): mask = Mask(x.size(0), cnum) branch = x branch = branch.reshape(branch.size(0),branch.size(1), branch.size(2) * branch.size(3)) branch = F.softmax(branch,2) branch = branch.reshape(branch.size(0),branch.size(1), x.size(2), x.size(2)) branch = my_MaxPool2d(kernel_size=(1,cnum), stride=(1,cnum))(branch) branch = branch.reshape(branch.size(0),branch.size(1), branch.size(2) * branch.size(3)) loss_2 = 1.0 - 1.0*torch.mean(torch.sum(branch,2))/cnum # set margin = 3.0 branch_1 = x * mask branch_1 = my_MaxPool2d(kernel_size=(1,cnum), stride=(1,cnum))(branch_1) branch_1 = nn.AvgPool2d(kernel_size=(height,height))(branch_1) branch_1 = branch_1.view(branch_1.size(0), -1) loss_1 = criterion(branch_1, targets) return [loss_1, loss_2] class model_bn(nn.Module): def __init__(self, feature_size=512,classes_num=200): super(model_bn, self).__init__() self.features = nn.Sequential(*list(net.children())[:-2]) self.max = nn.MaxPool2d(kernel_size=14, stride=14) self.num_ftrs = 600*1*1 self.classifier = nn.Sequential( nn.BatchNorm1d(self.num_ftrs), #nn.Dropout(0.5), nn.Linear(self.num_ftrs, feature_size), nn.BatchNorm1d(feature_size), nn.ELU(inplace=True), #nn.Dropout(0.5), nn.Linear(feature_size, classes_num), ) def forward(self, x, targets): x = self.features(x) if self.training: MC_loss = supervisor(x,targets,height=14,cnum=3) x = self.max(x) x = x.view(x.size(0), -1) x = self.classifier(x) loss = criterion(x, targets) if self.training: return x, loss, MC_loss else: return x, loss use_cuda = torch.cuda.is_available() net =model_bn(512, 200) if use_cuda: net.classifier.cuda() net.features.cuda() net.classifier = torch.nn.DataParallel(net.classifier) net.features = torch.nn.DataParallel(net.features) cudnn.benchmark = True def train(epoch,net, args, trainloader,optimizer): print('\nEpoch: %d' % epoch) net.train() train_loss = 0 correct = 0 total = 0 idx = 0 for batch_idx, (inputs, targets) in enumerate(trainloader): idx = batch_idx inputs, targets = inputs.cuda(), targets.cuda() optimizer.zero_grad() inputs, targets = Variable(inputs), Variable(targets) out, ce_loss, MC_loss = net(inputs, targets) loss = ce_loss + args["alpha_1"] * MC_loss[0] + args["beta_1"] * MC_loss[1] loss.backward() optimizer.step() train_loss += loss.item() _, predicted = torch.max(out.data, 1) total += targets.size(0) correct += predicted.eq(targets.data).cpu().sum().item() train_acc = 100.*correct/total train_loss = train_loss/(idx+1) logging.info('Iteration %d, train_acc = %.5f,train_loss = %.6f' % (epoch, train_acc,train_loss)) return train_acc, train_loss def test(epoch,net,testloader,optimizer): net.eval() test_loss = 0 correct = 0 total = 0 idx = 0 for batch_idx, (inputs, targets) in enumerate(testloader): with torch.no_grad(): idx = batch_idx if use_cuda: inputs, targets = inputs.cuda(), targets.cuda() inputs, targets = Variable(inputs), Variable(targets) out, ce_loss = net(inputs,targets) test_loss += ce_loss.item() _, predicted = torch.max(out.data, 1) total += targets.size(0) correct += predicted.eq(targets.data).cpu().sum().item() test_acc = 100.*correct/total test_loss = test_loss/(idx+1) logging.info('test, test_acc = %.4f,test_loss = %.4f' % (test_acc,test_loss)) return test_acc def cosine_anneal_schedule(t): cos_inner = np.pi * (t % (nb_epoch )) # t - 1 is used when t has 1-based indexing. cos_inner /= (nb_epoch ) cos_out = np.cos(cos_inner) + 1 return float( 0.1 / 2 * cos_out) optimizer = optim.SGD([ {'params': net.classifier.parameters(), 'lr': 0.1}, {'params': net.features.parameters(), 'lr': 0.1}, ], momentum=0.9, weight_decay=5e-4) def get_params(): # Training settings parser = argparse.ArgumentParser(description='PyTorch MC2_AutoML Example') parser.add_argument('--alpha_1', type=float, default=1.5, metavar='ALPHA', help='alpha_1 value (default: 2.0)') parser.add_argument('--beta_1', type=float, default=20.0, metavar='BETA', help='beta_1 value (default: 20.0)') args, _ = parser.parse_known_args() return args if __name__ == '__main__': try: args = vars(get_params()) print(args) # main(params) max_val_acc = 0 for epoch in range(1, nb_epoch+1): if epoch ==150: lr = 0.01 if epoch ==225: lr = 0.001 optimizer.param_groups[0]['lr'] = lr optimizer.param_groups[1]['lr'] = lr train(epoch, net, args,trainloader,optimizer) test_acc = test(epoch, net,testloader,optimizer) if test_acc >max_val_acc: max_val_acc = test_acc print("max_val_acc", max_val_acc) except Exception as exception: logger.exception(exception) raise ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Dongliang Chang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # The Devil is in the Channels: Mutual-Channel Loss for Fine-Grained Image Classification Code release for The Devil is in the Channels: Mutual-Channel Loss for Fine-Grained Image Classification (TIP 2020) [DOI](https://doi.org/10.1109/TIP.2020.2973812 "DOI") ## Changelog - 2020/09/14 update the code: CUB-200-2011_ResNet18.py Training with ResNet18 (TRAINED FROM SCRATCH). - 2020/04/19 add the hyper-parameter fine-tune results. - 2020/04/18 clean the code for better understanding. ## Dataset ### CUB-200-2011 ## Requirements - python 3.6 - PyTorch 1.2.0 - torchvision ## Training - Download datasets - Train: `python CUB-200-2011.py`, the alpha and beta are the hyper-parameters of the `MC-Loss` - Description : PyTorch CUB-200-2011 Training with VGG16 (TRAINED FROM SCRATCH). ## Hyper-parameter Loss = ce_loss + alpha_1 * L_dis + beta_1 * L_div ![Hyper-parameter_1](https://github.com/dongliangchang/Mutual-Channel-Loss/blob/master/Hyper-parameter_1.jpg) ![Hyper-parameter_2](https://github.com/dongliangchang/Mutual-Channel-Loss/blob/master/Hyper-parameter_2.jpg) The figure is plot by NNI. ## Other versions Other unofficial implements can be found in the following: - Kurumi233: This repo integrate the MC-Loss into a class. [code](https://github.com/Kurumi233/Mutual-Channel-Loss "code") - darcula1993: This repo implement the tf version of the MC-Loss. [code](https://github.com/darcula1993/Mutual-Channel-Loss "code") - Holocron: Implementations of recent Deep Learning tricks in Computer Vision, easily paired up with your favorite framework and model zoo. [code](https://github.com/frgfm/Holocron "code") ## Citation If you find this paper useful in your research, please consider citing: ``` @ARTICLE{9005389, author={D. {Chang} and Y. {Ding} and J. {Xie} and A. K. {Bhunia} and X. {Li} and Z. {Ma} and M. {Wu} and J. {Guo} and Y. {Song}}, journal={IEEE Transactions on Image Processing}, title={The Devil is in the Channels: Mutual-Channel Loss for Fine-Grained Image Classification}, year={2020}, volume={29}, number={}, pages={4683-4695}, doi={10.1109/TIP.2020.2973812}, ISSN={1941-0042}, month={},} ``` ## Contact Thanks for your attention! If you have any suggestion or question, you can leave a message here or contact us directly: - changdongliang@bupt.edu.cn - mazhanyu@bupt.edu.cn ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-modernist ================================================ FILE: my_pooling.py ================================================ import torch import numpy as np import random from torch.autograd import Variable from torch.nn.modules.module import Module from torch.nn.modules.utils import _single, _pair, _triple import torch.nn.functional as F from torch.nn.parameter import Parameter class my_MaxPool2d(Module): def __init__(self, kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False): super(my_MaxPool2d, self).__init__() self.kernel_size = kernel_size self.stride = stride or kernel_size self.padding = padding self.dilation = dilation self.return_indices = return_indices self.ceil_mode = ceil_mode def forward(self, input): input = input.transpose(3,1) input = F.max_pool2d(input, self.kernel_size, self.stride, self.padding, self.dilation, self.ceil_mode, self.return_indices) input = input.transpose(3,1).contiguous() return input def __repr__(self): kh, kw = _pair(self.kernel_size) dh, dw = _pair(self.stride) padh, padw = _pair(self.padding) dilh, dilw = _pair(self.dilation) padding_str = ', padding=(' + str(padh) + ', ' + str(padw) + ')' \ if padh != 0 or padw != 0 else '' dilation_str = (', dilation=(' + str(dilh) + ', ' + str(dilw) + ')' if dilh != 0 and dilw != 0 else '') ceil_str = ', ceil_mode=' + str(self.ceil_mode) return self.__class__.__name__ + '(' \ + 'kernel_size=(' + str(kh) + ', ' + str(kw) + ')' \ + ', stride=(' + str(dh) + ', ' + str(dw) + ')' \ + padding_str + dilation_str + ceil_str + ')' class my_AvgPool2d(Module): def __init__(self, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True): super(my_AvgPool2d, self).__init__() self.kernel_size = kernel_size self.stride = stride or kernel_size self.padding = padding self.ceil_mode = ceil_mode self.count_include_pad = count_include_pad def forward(self, input): input = input.transpose(3,1) input = F.avg_pool2d(input, self.kernel_size, self.stride, self.padding, self.ceil_mode, self.count_include_pad) input = input.transpose(3,1).contiguous() return input def __repr__(self): return self.__class__.__name__ + '(' \ + 'kernel_size=' + str(self.kernel_size) \ + ', stride=' + str(self.stride) \ + ', padding=' + str(self.padding) \ + ', ceil_mode=' + str(self.ceil_mode) \ + ', count_include_pad=' + str(self.count_include_pad) + ')' m = my_MaxPool2d((1, 32), stride=(1, 32)) input = Variable(torch.randn(3, 2208, 7, 7)) output = m(input) print(output.size())