-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b29aa5c
Showing
224 changed files
with
45,831 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
## NaCL:Noise-Robust Cross-Domain Contrastive Learning for Unsupervised Domain Adaptation | ||
|
||
### Introduction | ||
This is a PyTorch implementation of ["NaCL:Noise-Robust Cross-Domain Contrastive Learning for Unsupervised Domain Adaptation"]. | ||
|
||
### Requirements | ||
* Python 3.7 | ||
* torchvision 0.9.0 | ||
* PyTorch 1.8.0 | ||
|
||
|
||
### Train: | ||
|
||
- Unsupervised DA on `Office31`,`OfficeHome`, and `VisDA2017` datasets: | ||
```bash | ||
sh runUDA.sh | ||
``` | ||
- Semi-supervised DA on `COVID-19` dataset: | ||
|
||
```bash | ||
sh runSSDA.sh | ||
``` | ||
### Log: | ||
|
||
- The training log will be generated in the folder with ``--log_dir``. We can visualize the training process through `tensorboard` as follows. | ||
|
||
```bash | ||
tensorboard --logdir=/log_dir/ --host= `host address` | ||
``` | ||
|
||
### Usage | ||
|
||
- We uploaded the file `PythonGraphPers_withCompInfo.so` for computing the `connected components`. If you need to generate it, you can compile the C++ code in folder `ref`, run `./compile_pers_lib.sh` (by default it requires Python 3.7. If you are using other Python versions, modify the command inside `compile_pers_lib.sh`). | ||
|
||
- `pybind11` is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code. Our code will be further improved to make it cleaner and easier to use. | ||
|
||
|
||
***Note***: Place the datasets in the corresponding data path. | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__all__ = ['modules', 'vision'] |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .classifier import * | ||
|
||
__all__ = ['classifier'] |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
from typing import Tuple, Optional, List, Dict | ||
import torch.nn as nn | ||
import torch | ||
import torch.nn.functional as F | ||
import math | ||
|
||
__all__ = ['Classifier'] | ||
def initialize_layer(layer): | ||
for m in layer.modules(): | ||
if isinstance(m, (nn.BatchNorm2d, nn.BatchNorm1d)): | ||
nn.init.constant_(m.weight, 1) | ||
nn.init.constant_(m.bias, 0) | ||
elif isinstance(m, nn.Linear): | ||
nn.init.kaiming_normal_(m.weight) | ||
nn.init.constant_(m.bias, 0) | ||
|
||
def initialize_layer2(layer): | ||
for m in layer.modules(): | ||
if isinstance(m, (nn.BatchNorm2d, nn.BatchNorm1d)): | ||
nn.init.constant_(m.weight, 1) | ||
# nn.init.constant_(m.bias, 0) | ||
elif isinstance(m, nn.Linear): | ||
nn.init.kaiming_normal_(m.weight) | ||
# nn.init.constant_(m.bias, 0) | ||
|
||
class Classifier(nn.Module): | ||
"""A generic Classifier class for domain adaptation. | ||
Args: | ||
backbone (torch.nn.Module): Any backbone to extract 2-d features from data | ||
num_classes (int): Number of classes | ||
bottleneck (torch.nn.Module, optional): Any bottleneck layer. Use no bottleneck by default | ||
bottleneck_dim (int, optional): Feature dimension of the bottleneck layer. Default: -1 | ||
head (torch.nn.Module, optional): Any classifier head. Use :class:`torch.nn.Linear` by default | ||
finetune (bool): Whether finetune the classifier or train from scratch. Default: True | ||
.. note:: | ||
Different classifiers are used in different domain adaptation algorithms to achieve better accuracy | ||
respectively, and we provide a suggested `Classifier` for different algorithms. | ||
Remember they are not the core of algorithms. You can implement your own `Classifier` and combine it with | ||
the domain adaptation algorithm in this algorithm library. | ||
.. note:: | ||
The learning rate of this classifier is set 10 times to that of the feature extractor for better accuracy | ||
by default. If you have other optimization strategies, please over-ride :meth:`~Classifier.get_parameters`. | ||
Inputs: | ||
- x (tensor): input data fed to `backbone` | ||
Outputs: | ||
- predictions: classifier's predictions | ||
- features: features after `bottleneck` layer and before `head` layer | ||
Shape: | ||
- Inputs: (minibatch, *) where * means, any number of additional dimensions | ||
- predictions: (minibatch, `num_classes`) | ||
- features: (minibatch, `features_dim`) | ||
""" | ||
|
||
def __init__(self, backbone: nn.Module, num_classes: int, | ||
bottleneck_dim: Optional[int] = -1, head: Optional[nn.Module] = None, finetune=True, pool_layer=None): | ||
super(Classifier, self).__init__() | ||
self.backbone = backbone | ||
self.bottleneck_dim = bottleneck_dim | ||
self.parameter_list = [{"params": self.backbone.parameters(), "lr": 1}] | ||
self.num_classes = num_classes | ||
if pool_layer is None: | ||
self.pool_layer = nn.Sequential( | ||
nn.AdaptiveAvgPool2d(output_size=(1, 1)), | ||
nn.Flatten() | ||
) | ||
else: | ||
self.pool_layer = pool_layer | ||
self.bottleneck = nn.Sequential( | ||
nn.Linear(self.backbone.out_features, 256), | ||
nn.BatchNorm1d(256), | ||
nn.ReLU() | ||
) | ||
initialize_layer(self.bottleneck) | ||
self.parameter_list += [{"params": self.bottleneck.parameters(), "lr": 10}] | ||
self.contrast_layer = nn.Sequential( | ||
nn.Dropout(0.5), | ||
nn.Linear(256, self.bottleneck_dim), | ||
) | ||
initialize_layer(self.contrast_layer) | ||
self.parameter_list += [{"params": self.contrast_layer.parameters(), "lr": 10}] | ||
|
||
self.classifier = nn.Sequential( | ||
nn.Dropout(0.5), | ||
nn.Linear(256, num_classes) | ||
) | ||
|
||
initialize_layer(self.classifier) | ||
self.parameter_list += [{"params": self.classifier.parameters(), "lr": 10}] | ||
|
||
@property | ||
def features_dim(self) -> int: | ||
"""The dimension of features before the final `head` layer""" | ||
return self._features_dim | ||
|
||
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: | ||
"""""" | ||
end_points = {} | ||
features = self.pool_layer(self.backbone(x)) | ||
end_points['features'] = features | ||
features=self.bottleneck(features) | ||
end_points['norm_features'] = features | ||
|
||
contrast_features = self.contrast_layer(features) | ||
contrast_features = F.normalize(contrast_features, p=2, dim=1) | ||
end_points['contrast_features'] = contrast_features | ||
logits = self.classifier(features) | ||
end_points['logits'] = logits | ||
|
||
return end_points | ||
|
||
def weight_norm(self): | ||
w = self.classifier[1].weight.data | ||
norm = w.norm(p=2, dim=1, keepdim=True) | ||
self.classifier[1].weight.data = w.div(norm.expand_as(w)) | ||
|
||
|
||
def get_parameter_list(self): | ||
return self.parameter_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__all__ = ['models', 'transforms'] |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .resnet import * | ||
|
||
__all__ = ['resnet'] |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
|
||
import torch.nn as nn | ||
from torchvision import models | ||
from torchvision.models.utils import load_state_dict_from_url | ||
from torchvision.models.resnet import BasicBlock, Bottleneck, model_urls | ||
import copy | ||
|
||
__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', | ||
'resnet152', 'resnext50_32x4d', 'resnext101_32x8d', | ||
'wide_resnet50_2', 'wide_resnet101_2'] | ||
|
||
|
||
class ResNet(models.ResNet): | ||
"""ResNets without fully connected layer""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(ResNet, self).__init__(*args, **kwargs) | ||
self._out_features = self.fc.in_features | ||
|
||
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 = torch.flatten(x, 1) | ||
# x = x.view(-1, self._out_features) | ||
return x | ||
|
||
@property | ||
def out_features(self) -> int: | ||
"""The dimension of output features""" | ||
return self._out_features | ||
|
||
def copy_head(self) -> nn.Module: | ||
"""Copy the origin fully connected layer""" | ||
return copy.deepcopy(self.fc) | ||
|
||
|
||
def _resnet(arch, block, layers, pretrained, progress, **kwargs): | ||
model = ResNet(block, layers, **kwargs) | ||
if pretrained: | ||
model_dict = model.state_dict() | ||
pretrained_dict = load_state_dict_from_url(model_urls[arch], | ||
progress=progress) | ||
# remove keys from pretrained dict that doesn't appear in model dict | ||
pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict} | ||
model.load_state_dict(pretrained_dict, strict=False) | ||
return model | ||
|
||
|
||
def resnet18(pretrained=False, progress=True, **kwargs): | ||
r"""ResNet-18 model from | ||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained, progress, | ||
**kwargs) | ||
|
||
|
||
def resnet34(pretrained=False, progress=True, **kwargs): | ||
r"""ResNet-34 model from | ||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained, progress, | ||
**kwargs) | ||
|
||
|
||
def resnet50(pretrained=False, progress=True, **kwargs): | ||
r"""ResNet-50 model from | ||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress, | ||
**kwargs) | ||
|
||
|
||
def resnet101(pretrained=False, progress=True, **kwargs): | ||
r"""ResNet-101 model from | ||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, progress, | ||
**kwargs) | ||
|
||
|
||
def resnet152(pretrained=False, progress=True, **kwargs): | ||
r"""ResNet-152 model from | ||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained, progress, | ||
**kwargs) | ||
|
||
|
||
def resnext50_32x4d(pretrained=False, progress=True, **kwargs): | ||
r"""ResNeXt-50 32x4d model from | ||
`"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
kwargs['groups'] = 32 | ||
kwargs['width_per_group'] = 4 | ||
return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3], | ||
pretrained, progress, **kwargs) | ||
|
||
|
||
def resnext101_32x8d(pretrained=False, progress=True, **kwargs): | ||
r"""ResNeXt-101 32x8d model from | ||
`"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_ | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
kwargs['groups'] = 32 | ||
kwargs['width_per_group'] = 8 | ||
return _resnet('resnext101_32x8d', Bottleneck, [3, 4, 23, 3], | ||
pretrained, progress, **kwargs) | ||
|
||
|
||
def wide_resnet50_2(pretrained=False, progress=True, **kwargs): | ||
r"""Wide ResNet-50-2 model from | ||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_ | ||
The model is the same as ResNet except for the bottleneck number of channels | ||
which is twice larger in every block. The number of channels in outer 1x1 | ||
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048 | ||
channels, and in Wide ResNet-50-2 has 2048-1024-2048. | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
kwargs['width_per_group'] = 64 * 2 | ||
return _resnet('wide_resnet50_2', Bottleneck, [3, 4, 6, 3], | ||
pretrained, progress, **kwargs) | ||
|
||
|
||
def wide_resnet101_2(pretrained=False, progress=True, **kwargs): | ||
r"""Wide ResNet-101-2 model from | ||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_ | ||
The model is the same as ResNet except for the bottleneck number of channels | ||
which is twice larger in every block. The number of channels in outer 1x1 | ||
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048 | ||
channels, and in Wide ResNet-50-2 has 2048-1024-2048. | ||
Args: | ||
pretrained (bool): If True, returns a model pre-trained on ImageNet | ||
progress (bool): If True, displays a progress bar of the download to stderr | ||
""" | ||
kwargs['width_per_group'] = 64 * 2 | ||
return _resnet('wide_resnet101_2', Bottleneck, [3, 4, 23, 3], | ||
pretrained, progress, **kwargs) |
Oops, something went wrong.