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


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())
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
SYMBOL INDEX (41 symbols across 3 files)
FILE: CUB-200-2011.py
class VGG (line 65) | class VGG(nn.Module):
method __init__ (line 66) | def __init__(self, vgg_name):
method forward (line 71) | def forward(self, x):
method _make_layers (line 77) | def _make_layers(self, cfg):
function Mask (line 98) | def Mask(nb_batch, channels):
function supervisor (line 113) | def supervisor(x,targets,height,cnum):
class model_bn (line 133) | class model_bn(nn.Module):
method __init__ (line 134) | def __init__(self, feature_size=512,classes_num=200):
method forward (line 154) | def forward(self, x, targets):
function train (line 193) | def train(epoch,net, args, trainloader,optimizer):
function test (line 229) | def test(epoch,net,testloader,optimizer):
function cosine_anneal_schedule (line 256) | def cosine_anneal_schedule(t):
function get_params (line 272) | def get_params():
FILE: CUB-200-2011_ResNet18.py
function conv3x3 (line 79) | def conv3x3(in_planes, out_planes, stride=1):
class BasicBlock (line 85) | class BasicBlock(nn.Module):
method __init__ (line 88) | def __init__(self, inplanes, planes, stride=1, downsample=None):
method forward (line 98) | def forward(self, x):
class Bottleneck (line 117) | class Bottleneck(nn.Module):
method __init__ (line 120) | def __init__(self, inplanes, planes, stride=1, downsample=None):
method forward (line 133) | def forward(self, x):
class ResNet (line 156) | class ResNet(nn.Module):
method __init__ (line 158) | def __init__(self, block, layers, num_classes=1000):
method _make_layer (line 181) | def _make_layer(self, block, planes, blocks, stride=1):
function resnet18 (line 200) | def resnet18(pretrained=False, **kwargs):
function Mask (line 216) | def Mask(nb_batch, channels):
function supervisor (line 231) | def supervisor(x,targets,height,cnum):
class model_bn (line 251) | class model_bn(nn.Module):
method __init__ (line 252) | def __init__(self, feature_size=512,classes_num=200):
method forward (line 271) | def forward(self, x, targets):
function train (line 309) | def train(epoch,net, args, trainloader,optimizer):
function test (line 345) | def test(epoch,net,testloader,optimizer):
function cosine_anneal_schedule (line 372) | def cosine_anneal_schedule(t):
function get_params (line 387) | def get_params():
FILE: my_pooling.py
class my_MaxPool2d (line 12) | class my_MaxPool2d(Module):
method __init__ (line 15) | def __init__(self, kernel_size, stride=None, padding=0, dilation=1,
method forward (line 25) | def forward(self, input):
method __repr__ (line 36) | def __repr__(self):
class my_AvgPool2d (line 52) | class my_AvgPool2d(Module):
method __init__ (line 53) | def __init__(self, kernel_size, stride=None, padding=0, ceil_mode=False,
method forward (line 62) | def forward(self, input):
method __repr__ (line 71) | def __repr__(self):
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (31K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/custom.md",
"chars": 126,
"preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": "CUB-200-2011.py",
"chars": 9217,
"preview": "'''PyTorch CUB-200-2011 Training with VGG16 (TRAINED FROM SCRATCH).'''\nfrom __future__ import print_function\nimport os\n#"
},
{
"path": "CUB-200-2011_ResNet18.py",
"chars": 12623,
"preview": "'''PyTorch CUB-200-2011 Training with ResNet18 (TRAINED FROM SCRATCH).\n NOTICE: for baseline, the channel of the final"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "MIT License\n\nCopyright (c) 2020 Dongliang Chang\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README.md",
"chars": 2336,
"preview": "# The Devil is in the Channels: Mutual-Channel Loss for Fine-Grained Image Classification\n\nCode release for The Devil is"
},
{
"path": "_config.yml",
"chars": 29,
"preview": "theme: jekyll-theme-modernist"
},
{
"path": "my_pooling.py",
"chars": 2913,
"preview": "import torch\nimport numpy as np\nimport random\nfrom torch.autograd import Variable\nfrom torch.nn.modules.module import Mo"
}
]
About this extraction
This page contains the full source code of the PRIS-CV/Mutual-Channel-Loss GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (29.0 KB), approximately 8.1k tokens, and a symbol index with 41 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.