├── .gitignore
├── README.md
├── baseline
├── __init__.py
├── dataset
│ ├── Dataset.py
│ ├── __init__.py
│ └── add_transforms.py
├── model
│ ├── DeepMAR.py
│ ├── __init__.py
│ └── resnet.py
└── utils
│ ├── __init__.py
│ ├── evaluate.py
│ └── utils.py
├── dataset
├── demo
│ ├── .gitsave
│ └── demo_image.png
├── pa100k
│ └── .gitsave
├── peta
│ └── .gitsave
├── rap
│ └── .gitsave
└── rap2
│ └── .gitsave
└── script
├── dataset
├── transform_pa100k.py
├── transform_peta.py
├── transform_rap.py
└── transform_rap2.py
└── experiment
├── demo.py
├── test.sh
├── train.sh
└── train_deepmar_resnet50.py
/.gitignore:
--------------------------------------------------------------------------------
1 | ## General
2 | exp
3 | dataset/peta/*
4 | dataset/pa100k/*
5 | dataset/rap/*
6 | dataset/rap2/*
7 | !dataset/peta/.gitsave
8 | !dataset/rap/.gitsave
9 | !dataset/pa100k/.gitsave
10 | !dataset/rap2/.gitsave
11 |
12 | # Compiled Object files
13 | *.slo
14 | *.lo
15 | *.o
16 | *.cuo
17 |
18 | # Compiled Dynamic libraries
19 | *.so
20 | *.dylib
21 |
22 | # Compiled Static libraries
23 | *.lai
24 | *.la
25 | *.a
26 |
27 | # Compiled protocol buffers
28 | *.pb.h
29 | *.pb.cc
30 | *_pb2.py
31 |
32 | # Compiled python
33 | *.pyc
34 |
35 | # Compiled MATLAB
36 | *.mex*
37 |
38 | # IPython notebook checkpoints
39 | .ipynb_checkpoints
40 |
41 | # Editor temporaries
42 | *.swp
43 | *~
44 |
45 | # Sublime Text settings
46 | *.sublime-workspace
47 | *.sublime-project
48 |
49 | # Eclipse Project settings
50 | *.*project
51 | .settings
52 |
53 | # QtCreator files
54 | *.user
55 |
56 | # PyCharm files
57 | .idea
58 |
59 | # Visual Studio Code files
60 | .vscode
61 |
62 | # OSX dir files
63 | .DS_Store
64 |
65 | ## Caffe
66 |
67 | # User's build configuration
68 | Makefile.config
69 |
70 | # Data and models are either
71 | # 1. reference, and not casually committed
72 | # 2. custom, and live on their own unless they're deliberated contributed
73 | data/*
74 | models/*
75 | *.caffemodel
76 | *.caffemodel.h5
77 | *.solverstate
78 | *.solverstate.h5
79 | *.binaryproto
80 | *leveldb
81 | *lmdb
82 |
83 | # build, distribute, and bins (+ python proto bindings)
84 | build
85 | .build_debug/*
86 | .build_release/*
87 | distribute/*
88 | *.testbin
89 | *.bin
90 | python/caffe/proto/
91 | cmake_build
92 | .cmake_build
93 |
94 | # Generated documentation
95 | docs/_site
96 | docs/_includes
97 | docs/gathered
98 | _site
99 | doxygen
100 | docs/dev
101 |
102 | # LevelDB files
103 | *.sst
104 | *.ldb
105 | LOCK
106 | LOG*
107 | CURRENT
108 | MANIFEST-*
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Implement of Deep Multi-attribute Recognition model under ResNet50 backbone network
2 |
3 | ## Preparation
4 |
5 |
6 | **Prerequisite: Python 2.7 and Pytorch 0.3.1**
7 |
8 | 1. Install [Pytorch](https://pytorch.org/)
9 |
10 | 2. Download and prepare the dataset as follow:
11 |
12 | a. PETA [Baidu Yun](https://pan.baidu.com/s/1q8nsydT7xkDjZJOxvPcoEw), passwd: 5vep, or [Google Drive](https://drive.google.com/open?id=1q4cux17K3zNBgIrDV4FtcHJPLzXNKfYG).
13 |
14 | ```
15 | ./dataset/peta/images/*.png
16 | ./dataset/peta/PETA.mat
17 | ./dataset/peta/README
18 | ```
19 | ```
20 | python script/dataset/transform_peta.py
21 | ```
22 |
23 | b. RAP [Google Drive](https://drive.google.com/open?id=1FkXlpbk3R-M_vkvM8ByeAZVAMzN6vUOr).
24 | ```
25 | ./dataset/rap/RAP_dataset/*.png
26 | ./dataset/rap/RAP_annotation/RAP_annotation.mat
27 | ```
28 | ```
29 | python script/dataset/transform_rap.py
30 | ```
31 |
32 | c. PA100K [Links](https://drive.google.com/drive/folders/0B5_Ra3JsEOyOUlhKM0VPZ1ZWR2M)
33 | ```
34 | ./dataset/pa100k/data/*.png
35 | ./dataset/pa100k/annotation.mat
36 | ```
37 | ```
38 | python script/dataset/transform_pa100k.py
39 | ```
40 |
41 | d. RAP(v2) [Links](https://drive.google.com/open?id=1hoPIB5NJKf3YGMvLFZnIYG5JDcZTxHph).
42 | ```
43 | ./dataset/rap2/RAP_dataset/*.png
44 | ./dataset/rap2/RAP_annotation/RAP_annotation.mat
45 | ```
46 | ```
47 | python script/dataset/transform_rap2.py
48 | ```
49 |
50 |
51 | ## Train the model
52 |
53 |
54 | ```
55 | sh script/experiment/train.sh
56 | ```
57 |
58 |
59 | ## Test the model
60 |
61 |
62 | ```
63 | sh script/experiment/test.sh
64 | ```
65 |
66 |
67 |
68 | ## Demo
69 |
70 |
71 | ```
72 | python script/experiment/demo.py
73 | ```
74 |
75 |
76 |
77 | ## Citation
78 |
79 | Please cite this paper in your publications if it helps your research:
80 |
81 |
82 | ```
83 | @inproceedings{li2015deepmar,
84 | author = {Dangwei Li and Xiaotang Chen and Kaiqi Huang},
85 | title = {Multi-attribute Learning for Pedestrian Attribute Recognition in Surveillance Scenarios},
86 | booktitle = {ACPR},
87 | pages={111--115},
88 | year = {2015}
89 | }
90 | ```
91 |
92 | ## Thanks
93 |
94 |
95 | Partial codes are based on the repository from [Houjing Huang](https://github.com/huanghoujing).
96 |
97 | The code should only be used for academic research.
98 |
99 |
100 |
--------------------------------------------------------------------------------
/baseline/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/baseline/__init__.py
--------------------------------------------------------------------------------
/baseline/dataset/Dataset.py:
--------------------------------------------------------------------------------
1 | import torch.utils.data as data
2 | import os
3 | from PIL import Image
4 | import numpy as np
5 | import cPickle as pickle
6 | import copy
7 |
8 | class AttDataset(data.Dataset):
9 | """
10 | person attribute dataset interface
11 | """
12 | def __init__(
13 | self,
14 | dataset,
15 | partition,
16 | split='train',
17 | partition_idx=0,
18 | transform=None,
19 | target_transform=None,
20 | **kwargs):
21 | if os.path.exists( dataset ):
22 | self.dataset = pickle.load(open(dataset))
23 | else:
24 | print dataset + ' does not exist in dataset.'
25 | raise ValueError
26 | if os.path.exists( partition ):
27 | self.partition = pickle.load(open(partition))
28 | else:
29 | print partition + ' does not exist in dataset.'
30 | raise ValueError
31 | if not self.partition.has_key(split):
32 | print split + ' does not exist in dataset.'
33 | raise ValueError
34 |
35 | if partition_idx > len(self.partition[split])-1:
36 | print 'partition_idx is out of range in partition.'
37 | raise ValueError
38 |
39 | self.transform = transform
40 | self.target_transform = target_transform
41 |
42 | # create image, label based on the selected partition and dataset split
43 | self.root_path = self.dataset['root']
44 | self.att_name = [self.dataset['att_name'][i] for i in self.dataset['selected_attribute']]
45 | self.image = []
46 | self.label = []
47 | for idx in self.partition[split][partition_idx]:
48 | self.image.append(self.dataset['image'][idx])
49 | label_tmp = np.array(self.dataset['att'][idx])[self.dataset['selected_attribute']].tolist()
50 | self.label.append(label_tmp)
51 |
52 | def __getitem__(self, index):
53 | """
54 | Args:
55 | index (int): Index
56 | Returns:
57 | tuple: (image, target) where target is the index of the target class
58 | """
59 | imgname, target = self.image[index], self.label[index]
60 | # load image and labels
61 | imgname = os.path.join(self.dataset['root'], imgname)
62 | img = Image.open(imgname)
63 | if self.transform is not None:
64 | img = self.transform( img )
65 |
66 | # default no transform
67 | target = np.array(target).astype(np.float32)
68 | target[target == 0] = -1
69 | target[target == 2] = 0
70 | if self.target_transform is not None:
71 | target = self.transform( target )
72 |
73 | return img, target
74 |
75 | # useless for personal batch sampler
76 | def __len__(self):
77 | return len(self.image)
78 |
79 |
80 |
--------------------------------------------------------------------------------
/baseline/dataset/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/baseline/dataset/__init__.py
--------------------------------------------------------------------------------
/baseline/dataset/add_transforms.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import numbers
4 | __all__ = ["AddPad", "AddCrop"]
5 |
6 | class AddCrop(object):
7 | def __init__(self, size):
8 | self.size = size # two
9 | assert len(self.size) == 2
10 | def __repr__(self):
11 | return self.__class__.__name__ + '(size={0})'.format(self.size)
12 | def __call__(self, img):
13 | shape = img.shape # 3*H*W
14 | h_high = shape[1] - self.size[0]
15 | w_high = shape[2] - self.size[1]
16 | h_start = np.random.randint(low=0, high=h_high)
17 | w_start = np.random.randint(low=0, high=w_high)
18 | return img[:, h_start: h_start+self.size[0], w_start: w_start+self.size[1]]
19 |
20 | class AddPad(object):
21 | def __init__(self, padding, fill=0):
22 | self.padding = padding
23 | self.fill = fill
24 | if isinstance(self.padding, numbers.Number):
25 | self.pad_l = int(self.padding)
26 | self.pad_r = int(self.padding)
27 | self.pad_u = int(self.padding)
28 | self.pad_d = int(self.padding)
29 | elif isinstance(self.padding, (list, tuple)) and len(self.padding) == 4:
30 | self.pad_l = int(self.padding[0])
31 | self.pad_r = int(self.padding[1])
32 | self.pad_u = int(self.padding[2])
33 | self.pad_d = int(self.padding[3])
34 | else:
35 | print "The type of padding is not right."
36 | raise ValueError
37 | if self.pad_l <0 or self.pad_r < 0 or self.pad_u < 0 or self.pad_d < 0:
38 | raise ValueError
39 | if isinstance(self.fill, numbers.Number):
40 | self.fill_value = [self.fill]
41 | elif isinstance(self.fill, list):
42 | self.fill_value = self.fill
43 |
44 | def __repr__(self):
45 | return self.__class__.__name__ + '(padding={0})'.format(self.padding)
46 |
47 | def __call__(self, img):
48 | """
49 | Args:
50 | img: a 3-dimensional torch tensor with shape [R,G,B]*H*W
51 | Returns:
52 | img: a 3-dimensional padded tensor with shape [R,G,B]*H'*W'
53 | """
54 | if not (self.pad_l or self.pad_r or self.pad_u or self.pad_d):
55 | return img
56 | shape = img.shape
57 | img_ = torch.rand(shape[0], shape[1]+self.pad_u+self.pad_d, \
58 | shape[2]+self.pad_l+self.pad_r)
59 | for i in range(shape[0]):
60 | img_[i, 0:self.pad_u, :] = self.fill_value[i%len(self.fill_value)]
61 | img_[i, -(self.pad_d+1):-1, :] = self.fill_value[i%len(self.fill_value)]
62 | img_[i, :, 0:self.pad_l] = self.fill_value[i%len(self.fill_value)]
63 | img_[i, :, -(self.pad_r+1):-1] = self.fill_value[i%len(self.fill_value)]
64 | img_[i, self.pad_u:self.pad_u+shape[1], self.pad_l:self.pad_l+shape[2]] = img[i, :, :]
65 | return img_
66 |
--------------------------------------------------------------------------------
/baseline/model/DeepMAR.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.init as init
4 | import torch.nn.functional as F
5 | from torch.autograd import Variable
6 | import numpy as np
7 | from .resnet import resnet50
8 |
9 |
10 | class DeepMAR_ResNet50(nn.Module):
11 | def __init__(
12 | self,
13 | **kwargs
14 | ):
15 | super(DeepMAR_ResNet50, self).__init__()
16 |
17 | # init the necessary parameter for netwokr structure
18 | if kwargs.has_key('num_att'):
19 | self.num_att = kwargs['num_att']
20 | else:
21 | self.num_att = 35
22 | if kwargs.has_key('last_conv_stride'):
23 | self.last_conv_stride = kwargs['last_conv_stride']
24 | else:
25 | self.last_conv_stride = 2
26 | if kwargs.has_key('drop_pool5'):
27 | self.drop_pool5 = kwargs['drop_pool5']
28 | else:
29 | self.drop_pool5 = True
30 | if kwargs.has_key('drop_pool5_rate'):
31 | self.drop_pool5_rate = kwargs['drop_pool5_rate']
32 | else:
33 | self.drop_pool5_rate = 0.5
34 | if kwargs.has_key('pretrained'):
35 | self.pretrained = kwargs['pretrained']
36 | else:
37 | self.pretrained = True
38 |
39 | self.base = resnet50(pretrained=self.pretrained, last_conv_stride=self.last_conv_stride)
40 |
41 | self.classifier = nn.Linear(2048, self.num_att)
42 | init.normal(self.classifier.weight, std=0.001)
43 | init.constant(self.classifier.bias, 0)
44 |
45 | def forward(self, x):
46 | x = self.base(x)
47 | x = F.avg_pool2d(x, x.shape[2:])
48 | x = x.view(x.size(0), -1)
49 | if self.drop_pool5:
50 | x = F.dropout(x, p=self.drop_pool5_rate, training=self.training)
51 | x = self.classifier(x)
52 | return x
53 |
54 | class DeepMAR_ResNet50_ExtractFeature(object):
55 | """
56 | A feature extraction function
57 | """
58 | def __init__(self, model, **kwargs):
59 | self.model = model
60 |
61 | def __call__(self, imgs):
62 | old_train_eval_model = self.model.training
63 |
64 | # set the model to be eval
65 | self.model.eval()
66 |
67 | # imgs should be Variable
68 | if not isinstance(imgs, Variable):
69 | print 'imgs should be type: Variable'
70 | raise ValueError
71 | score = self.model(imgs)
72 | score = score.data.cpu().numpy()
73 |
74 | self.model.train(old_train_eval_model)
75 |
76 | return score
77 |
--------------------------------------------------------------------------------
/baseline/model/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/baseline/model/__init__.py
--------------------------------------------------------------------------------
/baseline/model/resnet.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import math
3 | import torch.utils.model_zoo as model_zoo
4 |
5 |
6 | __all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',
7 | 'resnet152']
8 |
9 |
10 | model_urls = {
11 | 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
12 | 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
13 | 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
14 | 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
15 | 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
16 | }
17 |
18 |
19 | def conv3x3(in_planes, out_planes, stride=1):
20 | """3x3 convolution with padding"""
21 | return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
22 | padding=1, bias=False)
23 |
24 |
25 | class BasicBlock(nn.Module):
26 | expansion = 1
27 |
28 | def __init__(self, inplanes, planes, stride=1, downsample=None):
29 | super(BasicBlock, self).__init__()
30 | self.conv1 = conv3x3(inplanes, planes, stride)
31 | self.bn1 = nn.BatchNorm2d(planes)
32 | self.relu = nn.ReLU(inplace=True)
33 | self.conv2 = conv3x3(planes, planes)
34 | self.bn2 = nn.BatchNorm2d(planes)
35 | self.downsample = downsample
36 | self.stride = stride
37 |
38 | def forward(self, x):
39 | residual = x
40 |
41 | out = self.conv1(x)
42 | out = self.bn1(out)
43 | out = self.relu(out)
44 |
45 | out = self.conv2(out)
46 | out = self.bn2(out)
47 |
48 | if self.downsample is not None:
49 | residual = self.downsample(x)
50 |
51 | out += residual
52 | out = self.relu(out)
53 |
54 | return out
55 |
56 |
57 | class Bottleneck(nn.Module):
58 | expansion = 4
59 |
60 | def __init__(self, inplanes, planes, stride=1, downsample=None):
61 | super(Bottleneck, self).__init__()
62 | self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
63 | self.bn1 = nn.BatchNorm2d(planes)
64 | self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
65 | padding=1, bias=False)
66 | self.bn2 = nn.BatchNorm2d(planes)
67 | self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
68 | self.bn3 = nn.BatchNorm2d(planes * 4)
69 | self.relu = nn.ReLU(inplace=True)
70 | self.downsample = downsample
71 | self.stride = stride
72 |
73 | def forward(self, x):
74 | residual = x
75 |
76 | out = self.conv1(x)
77 | out = self.bn1(out)
78 | out = self.relu(out)
79 |
80 | out = self.conv2(out)
81 | out = self.bn2(out)
82 | out = self.relu(out)
83 |
84 | out = self.conv3(out)
85 | out = self.bn3(out)
86 |
87 | if self.downsample is not None:
88 | residual = self.downsample(x)
89 |
90 | out += residual
91 | out = self.relu(out)
92 |
93 | return out
94 |
95 |
96 | class ResNet(nn.Module):
97 |
98 | def __init__(self, block, layers, last_conv_stride=2):
99 | self.inplanes = 64
100 | super(ResNet, self).__init__()
101 | self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
102 | bias=False)
103 | self.bn1 = nn.BatchNorm2d(64)
104 | self.relu = nn.ReLU(inplace=True)
105 | self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
106 | self.layer1 = self._make_layer(block, 64, layers[0])
107 | self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
108 | self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
109 | self.layer4 = self._make_layer(block, 512, layers[3], stride=last_conv_stride)
110 |
111 | for m in self.modules():
112 | if isinstance(m, nn.Conv2d):
113 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
114 | m.weight.data.normal_(0, math.sqrt(2. / n))
115 | elif isinstance(m, nn.BatchNorm2d):
116 | m.weight.data.fill_(1)
117 | m.bias.data.zero_()
118 |
119 | def _make_layer(self, block, planes, blocks, stride=1):
120 | downsample = None
121 | if stride != 1 or self.inplanes != planes * block.expansion:
122 | downsample = nn.Sequential(
123 | nn.Conv2d(self.inplanes, planes * block.expansion,
124 | kernel_size=1, stride=stride, bias=False),
125 | nn.BatchNorm2d(planes * block.expansion),
126 | )
127 |
128 | layers = []
129 | layers.append(block(self.inplanes, planes, stride, downsample))
130 | self.inplanes = planes * block.expansion
131 | for i in range(1, blocks):
132 | layers.append(block(self.inplanes, planes))
133 |
134 | return nn.Sequential(*layers)
135 |
136 | def forward(self, x):
137 | x = self.conv1(x)
138 | x = self.bn1(x)
139 | x = self.relu(x)
140 | x = self.maxpool(x)
141 |
142 | x = self.layer1(x)
143 | x = self.layer2(x)
144 | x = self.layer3(x)
145 | x = self.layer4(x)
146 |
147 | return x
148 |
149 | def remove_fc(state_dict):
150 | """ Remove the fc layer parameter from state_dict. """
151 | for key, value in state_dict.items():
152 | if key.startswith('fc.'):
153 | del state_dict[key]
154 | return state_dict
155 |
156 |
157 | def resnet18(pretrained=False, **kwargs):
158 | """Constructs a ResNet-18 model.
159 |
160 | Args:
161 | pretrained (bool): If True, returns a model pre-trained on ImageNet
162 | """
163 | model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
164 | if pretrained:
165 | model.load_state_dict(remove_fc(model_zoo.load_url(model_urls['resnet18'])))
166 | return model
167 |
168 |
169 | def resnet34(pretrained=False, **kwargs):
170 | """Constructs a ResNet-34 model.
171 |
172 | Args:
173 | pretrained (bool): If True, returns a model pre-trained on ImageNet
174 | """
175 | model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs)
176 | if pretrained:
177 | model.load_state_dict(remove_fc(model_zoo.load_url(model_urls['resnet34'])))
178 | return model
179 |
180 |
181 | def resnet50(pretrained=False, **kwargs):
182 | """Constructs a ResNet-50 model.
183 |
184 | Args:
185 | pretrained (bool): If True, returns a model pre-trained on ImageNet
186 | """
187 | model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
188 | if pretrained:
189 | model.load_state_dict(remove_fc(model_zoo.load_url(model_urls['resnet50'])))
190 | return model
191 |
192 |
193 | def resnet101(pretrained=False, **kwargs):
194 | """Constructs a ResNet-101 model.
195 |
196 | Args:
197 | pretrained (bool): If True, returns a model pre-trained on ImageNet
198 | """
199 | model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs)
200 | if pretrained:
201 | model.load_state_dict(remove_fc(model_zoo.load_url(model_urls['resnet101'])))
202 | return model
203 |
204 |
205 | def resnet152(pretrained=False, **kwargs):
206 | """Constructs a ResNet-152 model.
207 |
208 | Args:
209 | pretrained (bool): If True, returns a model pre-trained on ImageNet
210 | """
211 | model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs)
212 | if pretrained:
213 | model.load_state_dict(remove_fc(model_zoo.load_url(model_urls['resnet152'])))
214 | return model
215 |
--------------------------------------------------------------------------------
/baseline/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/baseline/utils/__init__.py
--------------------------------------------------------------------------------
/baseline/utils/evaluate.py:
--------------------------------------------------------------------------------
1 | import os
2 | import torch
3 | from torch.autograd import Variable
4 | import numpy as np
5 | import copy
6 | import time
7 | import sys
8 |
9 | def extract_feat(feat_func, dataset, **kwargs):
10 | """
11 | extract feature for images
12 | """
13 | test_loader = torch.utils.data.DataLoader(
14 | dataset = dataset, batch_size = 32,
15 | num_workers = 2, pin_memory = True)
16 | # extract feature for all the images of test/val identities
17 | start_time = time.time()
18 | total_eps = len(test_loader)
19 | N = len(dataset.image)
20 | start = 0
21 | for ep, (imgs, labels) in enumerate(test_loader):
22 | imgs_var = Variable(imgs, volatile=True).cuda()
23 | feat_tmp = feat_func( imgs_var )
24 | batch_size = feat_tmp.shape[0]
25 | if ep == 0:
26 | feat = np.zeros((N, feat_tmp.size/batch_size))
27 | feat[start:start+batch_size, :] = feat_tmp.reshape((batch_size, -1))
28 | start += batch_size
29 | end_time = time.time()
30 | print('{} batches done, total {:.2f}s'.format(total_eps, end_time-start_time))
31 | return feat
32 |
33 | # attribute recognition evaluation
34 | def attribute_evaluate(feat_func, dataset, **kwargs):
35 | print "extracting features for attribute recognition"
36 | pt_result = extract_feat(feat_func, dataset)
37 | # obain the attributes from the attribute dictionary
38 | print "computing attribute recognition result"
39 | N = pt_result.shape[0]
40 | L = pt_result.shape[1]
41 | gt_result = np.zeros(pt_result.shape)
42 | # get the groundtruth attributes
43 | for idx, label in enumerate(dataset.label):
44 | gt_result[idx, :] = label
45 | pt_result[pt_result>=0] = 1
46 | pt_result[pt_result<0] = 0
47 | return attribute_evaluate_lidw(gt_result, pt_result)
48 |
49 | def attribute_evaluate_lidw(gt_result, pt_result):
50 | """
51 | Input:
52 | gt_result, pt_result, N*L, with 0/1
53 | Output:
54 | result
55 | a dictionary, including label-based and instance-based evaluation
56 | label-based: label_pos_acc, label_neg_acc, label_acc
57 | instance-based: instance_acc, instance_precision, instance_recall, instance_F1
58 | """
59 | # obtain the label-based and instance-based accuracy
60 | # compute the label-based accuracy
61 | if gt_result.shape != pt_result.shape:
62 | print 'Shape beteen groundtruth and predicted results are different'
63 | # compute the label-based accuracy
64 | result = {}
65 | gt_pos = np.sum((gt_result == 1).astype(float), axis=0)
66 | gt_neg = np.sum((gt_result == 0).astype(float), axis=0)
67 | pt_pos = np.sum((gt_result == 1).astype(float) * (pt_result == 1).astype(float), axis=0)
68 | pt_neg = np.sum((gt_result == 0).astype(float) * (pt_result == 0).astype(float), axis=0)
69 | label_pos_acc = 1.0*pt_pos/gt_pos
70 | label_neg_acc = 1.0*pt_neg/gt_neg
71 | label_acc = (label_pos_acc + label_neg_acc)/2
72 | result['label_pos_acc'] = label_pos_acc
73 | result['label_neg_acc'] = label_neg_acc
74 | result['label_acc'] = label_acc
75 | # compute the instance-based accuracy
76 | # precision
77 | gt_pos = np.sum((gt_result == 1).astype(float), axis=1)
78 | pt_pos = np.sum((pt_result == 1).astype(float), axis=1)
79 | floatersect_pos = np.sum((gt_result == 1).astype(float)*(pt_result == 1).astype(float), axis=1)
80 | union_pos = np.sum(((gt_result == 1)+(pt_result == 1)).astype(float),axis=1)
81 | # avoid empty label in predicted results
82 | cnt_eff = float(gt_result.shape[0])
83 | for iter, key in enumerate(gt_pos):
84 | if key == 0:
85 | union_pos[iter] = 1
86 | pt_pos[iter] = 1
87 | gt_pos[iter] = 1
88 | cnt_eff = cnt_eff - 1
89 | continue
90 | if pt_pos[iter] == 0:
91 | pt_pos[iter] = 1
92 | instance_acc = np.sum(floatersect_pos/union_pos)/cnt_eff
93 | instance_precision = np.sum(floatersect_pos/pt_pos)/cnt_eff
94 | instance_recall = np.sum(floatersect_pos/gt_pos)/cnt_eff
95 | floatance_F1 = 2*instance_precision*instance_recall/(instance_precision+instance_recall)
96 | result['instance_acc'] = instance_acc
97 | result['instance_precision'] = instance_precision
98 | result['instance_recall'] = instance_recall
99 | result['instance_F1'] = floatance_F1
100 | return result
101 |
--------------------------------------------------------------------------------
/baseline/utils/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import cPickle as pickle
3 | import datetime
4 | import time
5 | # from contextlib import contextmanger
6 | import torch
7 | from torch.autograd import Variable
8 | import random
9 | import numpy as np
10 |
11 | def time_str(fmt=None):
12 | if fmt is None:
13 | fmt = '%Y-%m-%d_%H:%M:%S'
14 | return datetime.datetime.today().strftime(fmt)
15 |
16 | def str2bool(v):
17 | return v.lower() in ("yes", "true", "1")
18 |
19 | def is_iterable(obj):
20 | return hasattr(obj, '__len__')
21 |
22 | def to_scalar(vt):
23 | """
24 | transform a 1-length pytorch Variable or Tensor to scalar
25 | """
26 | if isinstance(vt, Variable):
27 | return vt.data.cpu().numpy().flatten()[0]
28 | if torch.is_tensor(vt):
29 | return vt.cpu().numpy().flatten()[0]
30 | raise TypeError('Input should be a variable or tensor')
31 |
32 | def set_seed(rand_seed):
33 | np.random.seed( rand_seed )
34 | random.seed( rand_seed )
35 | torch.backends.cudnn.enabled = True
36 | torch.manual_seed( rand_seed )
37 | torch.cuda.manual_seed( rand_seed )
38 |
39 | def may_mkdir(fname):
40 | if not os.path.exists(os.path.dirname(os.path.abspath(fname))):
41 | os.makedirs(os.path.dirname(os.path.abspath(fname)))
42 |
43 | class AverageMeter(object):
44 | """
45 | Computes and stores the average and current value
46 | """
47 | def __init__(self):
48 | self.val = 0
49 | self.avg = 0
50 | self.sum = 0
51 | self.count = 0
52 | def reset(self):
53 | self.val = 0
54 | self.avg = 0
55 | self.sum = 0
56 | self.count = 0
57 | def update(self, val, n=1):
58 | self.val = val
59 | self.sum += val * n
60 | self.count += n
61 | self.avg = float(self.sum) / (self.count + 1e-10)
62 |
63 | class RunningAverageMeter(object):
64 | """
65 | Computes and stores the running average and current value
66 | """
67 | def __init__(self, hist=0.99):
68 | self.val = None
69 | self.avg = None
70 | self.hist = hist
71 |
72 | def reset(self):
73 | self.val = None
74 | self.avg = None
75 |
76 | def update(self, val):
77 | if self.avg is None:
78 | self.avg = val
79 | else:
80 | self.avg = self.avg * self.hist + val * (1 - self.hist)
81 | self.val = val
82 |
83 | class RecentAverageMeter(object):
84 | """
85 | Stores and computes the average of recent values
86 | """
87 | def __init__(self, hist_size=100):
88 | self.hist_size = hist_size
89 | self.fifo = []
90 | self.val = 0
91 |
92 | def reset(self):
93 | self.fifo = []
94 | self.val = 0
95 |
96 | def update(self, value):
97 | self.val = val
98 | self.fifo.append(val)
99 | if len(self.fifo) > self.hist_size:
100 | del self.fifo[0]
101 | @property
102 | def avg(self):
103 | assert len(self.fifo) > 0
104 | return float(sum(self.fifo)) / len(self.fifo)
105 |
106 | class ReDirectSTD(object):
107 | """
108 | overwrites the sys.stdout or sys.stderr
109 | Args:
110 | fpath: file path
111 | console: one of ['stdout', 'stderr']
112 | immediately_visiable: False
113 | Usage example:
114 | ReDirectSTD('stdout.txt', 'stdout', False)
115 | ReDirectSTD('stderr.txt', 'stderr', False)
116 | """
117 | def __init__(self, fpath=None, console='stdout', immediately_visiable=False):
118 | import sys
119 | import os
120 | assert console in ['stdout', 'stderr']
121 | self.console = sys.stdout if console == "stdout" else sys.stderr
122 | self.file = fpath
123 | self.f = None
124 | self.immediately_visiable = immediately_visiable
125 | if fpath is not None:
126 | # Remove existing log file
127 | if os.path.exists(fpath):
128 | os.remove(fpath)
129 | if console == 'stdout':
130 | sys.stdout = self
131 | else:
132 | sys.stderr = self
133 |
134 | def __del__(self):
135 | self.close()
136 |
137 | def __enter__(self):
138 | pass
139 |
140 | def __exit__(self, **args):
141 | self.close()
142 |
143 | def write(self, msg):
144 | self.console.write(msg)
145 | if self.file is not None:
146 | if not os.path.exists(os.path.dirname(os.path.abspath(self.file))):
147 | os.mkdir(os.path.dirname(os.path.abspath(self.file)))
148 | if self.immediately_visiable:
149 | with open(self.file, 'a') as f:
150 | f.write(msg)
151 | else:
152 | if self.f is None:
153 | self.f = open(self.file, 'w')
154 | self.f.write(msg)
155 |
156 | def flush(self):
157 | self.console.flush()
158 | if self.f is not None:
159 | self.f.flush()
160 | import os
161 | os.fsync(self.f.fileno())
162 |
163 | def close(self):
164 | self.console.close()
165 | if self.f is not None:
166 | self.f.close()
167 |
168 | def find_index(seq, item):
169 | for i, x in enumerate(seq):
170 | if item == x:
171 | return i
172 | return -1
173 |
174 | def set_devices(sys_device_ids):
175 | """
176 | Args:
177 | sys_device_ids: a tuple; which GPUs to use
178 | e.g. sys_device_ids = (), only use cpu
179 | sys_device_ids = (3,), use the 4-th gpu
180 | sys_device_ids = (0, 1, 2, 3,), use the first 4 gpus
181 | sys_device_ids = (0, 2, 4,), use the 1, 3 and 5 gpus
182 | """
183 | import os
184 | visiable_devices = ''
185 | for i in sys_device_ids:
186 | visiable_devices += '{}, '.format(i)
187 | os.environ['CUDA_VISIBLE_DEVICES'] = visiable_devices
188 | # Return wrappers
189 | # Models and user defined Variables/Tensors would be transferred to
190 | # the first device
191 | device_id = 0 if len(sys_device_ids) > 0 else -1
192 |
193 | def transfer_optims(optims, device_id=-1):
194 | for optim in optims:
195 | if isinstance(optim, torch.optim.Optimizer):
196 | transfer_optim_state(optim.state, device_id=device_id)
197 |
198 | def transfer_optim_state(state, device_id=-1):
199 | for key, val in state.items():
200 | if isinstance(val, dict):
201 | transfer_optim_state(val, device_id=device_id)
202 | elif isinstance(val, Variable):
203 | raise RuntimeError("Oops, state[{}] is a Variable!".format(key))
204 | elif isinstance(val, torch.nn.Parameter):
205 | raise RuntimeError("Oops, state[{}] is a Parameter!".format(key))
206 | else:
207 | try:
208 | if device_id == -1:
209 | state[key] = val.cpu()
210 | else:
211 | state[key] = val.cuda(device=device_id)
212 | except:
213 | pass
214 |
215 |
216 | def load_state_dict(model, src_state_dict):
217 | """
218 | copy parameter from src_state_dict to model
219 | Arguments:
220 | model: A torch.nn.Module object
221 | src_state_dict: a dict containing parameters and persistent buffers
222 | """
223 | from torch.nn import Parameter
224 | dest_state_dict = model.state_dict()
225 | for name, param in src_state_dict.items():
226 | if name not in dest_state_dict:
227 | continue
228 | if isinstance(param, Parameter):
229 | param = param.data
230 | try:
231 | dest_state_dict[name].copy_(param)
232 | except Exception, msg:
233 | print("Warning: Error occurs when copying '{}': {}"
234 | .format(name, str(msg)))
235 |
236 | src_missing = set(dest_state_dict.keys()) - set(src_state_dict.keys())
237 | if len(src_missing) > 0:
238 | print ("Keys not found in source state_dict: ")
239 | for n in src_missing:
240 | print('\t', n)
241 |
242 | dest_missint = set(src_state_dict.keys()) - set(dest_state_dict.keys())
243 | if len(dest_missint):
244 | print ("Keys not found in destination state_dict: ")
245 | for n in dest_missint:
246 | print('\t', n)
247 |
248 | def load_ckpt(modules_optims, ckpt_file, load_to_cpu=True, verbose=True):
249 | """
250 | load state_dict of module & optimizer from file
251 | Args:
252 | modules_optims: A two-element list which contains module and optimizer
253 | ckpt_file: the check point file
254 | load_to_cpu: Boolean, whether to transform tensors in model & optimizer to cpu type
255 | """
256 | map_location = (lambda storage, loc: storage) if load_to_cpu else None
257 | ckpt = torch.load(ckpt_file, map_location=map_location)
258 | for m, sd in zip(modules_optims, ckpt['state_dicts']):
259 | m.load_state_dict(sd)
260 | if verbose:
261 | print("Resume from ckpt {}, \nepoch: {}, scores: {}".format(
262 | ckpt_file, ckpt['ep'], ckpt['scores']))
263 | return ckpt['ep'], ckpt['scores']
264 |
265 | def save_ckpt(modules_optims, ep, scores, ckpt_file):
266 | """
267 | save state_dict of modules/optimizers to file
268 | Args:
269 | modules_optims: a two-element list which contains a module and a optimizer
270 | ep: the current epoch number
271 | scores: the performance of current module
272 | ckpt_file: the check point file path
273 | Note:
274 | torch.save() reserves device type and id of tensors to save.
275 | So when loading ckpt, you have to inform torch.load() to load these tensors
276 | to cpu or your desired gpu, if you change devices.
277 | """
278 | state_dicts = [m.state_dict() for m in modules_optims]
279 | ckpt = dict(state_dicts = state_dicts,
280 | ep = ep,
281 | scores = scores)
282 | if not os.path.exists(os.path.dirname(os.path.abspath(ckpt_file))):
283 | os.mkdir(os.path.dirname(os.path.abspath(ckpt_file)))
284 | torch.save(ckpt, ckpt_file)
285 |
286 | def adjust_lr_staircase(param_groups, base_lrs, ep, decay_at_epochs, factor):
287 | """ Multiplied by a factor at the beging of specified epochs. Different
288 | params groups specify thier own base learning rates.
289 | Args:
290 | param_groups: a list of params
291 | base_lrs: starting learning rate, len(base_lrs) = len(params_groups)
292 | ep: current epoch, ep >= 1
293 | decay_at_epochs: a list or tuple; learning rates are multiplied by a factor
294 | at the begining of these epochs
295 | factor: a number in range (0, 1)
296 | Example:
297 | base_lrs = [0.1, 0.01]
298 | decay_at_epochs = [51, 101]
299 | factor = 0.1
300 | Note:
301 | It is meant to be called at the begining of an epoch
302 | """
303 | assert len(base_lrs) == len(param_groups), \
304 | 'You should specify base lr for each param group.'
305 | assert ep >= 1, "Current epoch number should be >= 1"
306 |
307 | if ep not in decay_at_epochs:
308 | return
309 |
310 | ind = find_index(decay_at_epochs, ep)
311 | for i, (g, base_lr) in enumerate(zip(param_groups, base_lrs)):
312 | g['lr'] = base_lr * factor ** (ind + 1)
313 | print('=====> Param group {}: lr adjusted to {:.10f}'
314 | .format(i, g['lr']).rstrip('0'))
315 |
316 | def may_set_mode(maybe_modules, mode):
317 | """
318 | maybe_modules, an object or a list of objects.
319 | """
320 | assert mode in ['train', 'eval']
321 | if not is_iterable(maybe_modules):
322 | maybe_modules = [maybe_modules]
323 | for m in maybe_modules:
324 | if isinstance(m, torch.nn.Module):
325 | if mode == 'train':
326 | m.train()
327 | else:
328 | m.eval()
329 |
--------------------------------------------------------------------------------
/dataset/demo/.gitsave:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/dataset/demo/.gitsave
--------------------------------------------------------------------------------
/dataset/demo/demo_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/dataset/demo/demo_image.png
--------------------------------------------------------------------------------
/dataset/pa100k/.gitsave:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/dataset/pa100k/.gitsave
--------------------------------------------------------------------------------
/dataset/peta/.gitsave:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/dataset/peta/.gitsave
--------------------------------------------------------------------------------
/dataset/rap/.gitsave:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/dataset/rap/.gitsave
--------------------------------------------------------------------------------
/dataset/rap2/.gitsave:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangweili/pedestrian-attribute-recognition-pytorch/468ae58cf49d09931788f378e4b3d4cc2f171c22/dataset/rap2/.gitsave
--------------------------------------------------------------------------------
/script/dataset/transform_pa100k.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import random
4 | import cPickle as pickle
5 | from scipy.io import loadmat
6 |
7 | np.random.seed(0)
8 | random.seed(0)
9 |
10 | def make_dir(path):
11 | if os.path.exists(path):
12 | pass
13 | else:
14 | os.mkdir(path)
15 |
16 | def generate_data_description(save_dir):
17 | """
18 | create a dataset description file, which consists of images, labels
19 | """
20 | dataset = dict()
21 | dataset['description'] = 'pa100k'
22 | dataset['root'] = './dataset/pa100k/data/'
23 | dataset['image'] = []
24 | dataset['att'] = []
25 | dataset['att_name'] = []
26 | dataset['selected_attribute'] = range(26)
27 | # load ANNOTATION.MAT
28 | data = loadmat(open('./dataset/pa100k/annotation.mat', 'r'))
29 | for idx in range(26):
30 | dataset['att_name'].append(data['attributes'][idx][0][0])
31 |
32 | for idx in range(80000):
33 | dataset['image'].append(data['train_images_name'][idx][0][0])
34 | dataset['att'].append(data['train_label'][idx, :].tolist())
35 |
36 | for idx in range(10000):
37 | dataset['image'].append(data['val_images_name'][idx][0][0])
38 | dataset['att'].append(data['val_label'][idx, :].tolist())
39 |
40 | for idx in range(10000):
41 | dataset['image'].append(data['test_images_name'][idx][0][0])
42 | dataset['att'].append(data['test_label'][idx, :].tolist())
43 |
44 | with open(os.path.join(save_dir, 'pa100k_dataset.pkl'), 'w+') as f:
45 | pickle.dump(dataset, f)
46 |
47 | def create_trainvaltest_split(traintest_split_file):
48 | """
49 | create a dataset split file, which consists of index of the train/val/test splits
50 | """
51 | partition = dict()
52 | partition['trainval'] = []
53 | partition['train'] = []
54 | partition['val'] = []
55 | partition['test'] = []
56 | partition['weight_trainval'] = []
57 | partition['weight_train'] = []
58 | # load ANNOTATION.MAT
59 | data = loadmat(open('./dataset/pa100k/annotation.mat', 'r'))
60 | train = range(80000)
61 | val = [i+80000 for i in range(10000)]
62 | test = [i+90000 for i in range(10000)]
63 | trainval = train + val
64 | partition['train'].append(train)
65 | partition['val'].append(val)
66 | partition['trainval'].append(trainval)
67 | partition['test'].append(test)
68 | # weight
69 | train_label = data['train_label'].astype('float32')
70 | trainval_label = np.concatenate((data['train_label'], data['val_label']), axis=0).astype('float32')
71 | weight_train = np.mean(train_label==1, axis=0).tolist()
72 | weight_trainval = np.mean(trainval_label==1, axis=0).tolist()
73 |
74 | partition['weight_trainval'].append(weight_trainval)
75 | partition['weight_train'].append(weight_train)
76 |
77 | with open(traintest_split_file, 'w+') as f:
78 | pickle.dump(partition, f)
79 |
80 | if __name__ == "__main__":
81 | import argparse
82 | parser = argparse.ArgumentParser(description="pa100k dataset")
83 | parser.add_argument(
84 | '--save_dir',
85 | type=str,
86 | default='./dataset/pa100k/')
87 | parser.add_argument(
88 | '--traintest_split_file',
89 | type=str,
90 | default="./dataset/pa100k/pa100k_partition.pkl")
91 | args = parser.parse_args()
92 | save_dir = args.save_dir
93 | traintest_split_file = args.traintest_split_file
94 |
95 | generate_data_description(save_dir)
96 | create_trainvaltest_split(traintest_split_file)
97 |
--------------------------------------------------------------------------------
/script/dataset/transform_peta.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import random
4 | import cPickle as pickle
5 | from scipy.io import loadmat
6 |
7 | np.random.seed(0)
8 | random.seed(0)
9 |
10 | def make_dir(path):
11 | if os.path.exists(path):
12 | pass
13 | else:
14 | os.mkdir(path)
15 |
16 | def generate_data_description(save_dir):
17 | """
18 | create a dataset description file, which consists of images, labels
19 | """
20 | dataset = dict()
21 | dataset['description'] = 'peta'
22 | dataset['root'] = './dataset/peta/images/'
23 | dataset['image'] = []
24 | dataset['att'] = []
25 | dataset['att_name'] = []
26 | dataset['selected_attribute'] = range(35)
27 | # load PETA.MAT
28 | data = loadmat(open('./dataset/peta/PETA.mat', 'r'))
29 | for idx in range(105):
30 | dataset['att_name'].append(data['peta'][0][0][1][idx,0][0])
31 |
32 | for idx in range(19000):
33 | dataset['image'].append('%05d.png'%(idx+1))
34 | dataset['att'].append(data['peta'][0][0][0][idx, 4:].tolist())
35 | with open(os.path.join(save_dir, 'peta_dataset.pkl'), 'w+') as f:
36 | pickle.dump(dataset, f)
37 |
38 | def create_trainvaltest_split(traintest_split_file):
39 | """
40 | create a dataset split file, which consists of index of the train/val/test splits
41 | """
42 | partition = dict()
43 | partition['trainval'] = []
44 | partition['train'] = []
45 | partition['val'] = []
46 | partition['test'] = []
47 | partition['weight_trainval'] = []
48 | partition['weight_train'] = []
49 | # load PETA.MAT
50 | data = loadmat(open('./dataset/peta/PETA.mat', 'r'))
51 | for idx in range(5):
52 | train = (data['peta'][0][0][3][idx][0][0][0][0][:,0]-1).tolist()
53 | val = (data['peta'][0][0][3][idx][0][0][0][1][:,0]-1).tolist()
54 | test = (data['peta'][0][0][3][idx][0][0][0][2][:,0]-1).tolist()
55 | trainval = train + val
56 | partition['train'].append(train)
57 | partition['val'].append(val)
58 | partition['trainval'].append(trainval)
59 | partition['test'].append(test)
60 | # weight
61 | weight_trainval = np.mean(data['peta'][0][0][0][trainval, 4:].astype('float32')==1, axis=0).tolist()
62 | weight_train = np.mean(data['peta'][0][0][0][train, 4:].astype('float32')==1, axis=0).tolist()
63 | partition['weight_trainval'].append(weight_trainval)
64 | partition['weight_train'].append(weight_train)
65 | with open(traintest_split_file, 'w+') as f:
66 | pickle.dump(partition, f)
67 |
68 | if __name__ == "__main__":
69 | import argparse
70 | parser = argparse.ArgumentParser(description="peta dataset")
71 | parser.add_argument(
72 | '--save_dir',
73 | type=str,
74 | default='./dataset/peta/')
75 | parser.add_argument(
76 | '--traintest_split_file',
77 | type=str,
78 | default="./dataset/peta/peta_partition.pkl")
79 | args = parser.parse_args()
80 | save_dir = args.save_dir
81 | traintest_split_file = args.traintest_split_file
82 |
83 | generate_data_description(save_dir)
84 | create_trainvaltest_split(traintest_split_file)
85 |
--------------------------------------------------------------------------------
/script/dataset/transform_rap.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import random
4 | import cPickle as pickle
5 | from scipy.io import loadmat
6 |
7 | np.random.seed(0)
8 | random.seed(0)
9 |
10 | def make_dir(path):
11 | if os.path.exists(path):
12 | pass
13 | else:
14 | os.mkdir(path)
15 |
16 | def generate_data_description(save_dir):
17 | """
18 | create a dataset description file, which consists of images, labels
19 | """
20 | dataset = dict()
21 | dataset['description'] = 'rap'
22 | dataset['root'] = './dataset/rap/RAP_dataset/'
23 | dataset['image'] = []
24 | dataset['att'] = []
25 | dataset['att_name'] = []
26 | dataset['selected_attribute'] = range(51)
27 | # load Rap_annotation.mat
28 | data = loadmat(open('./dataset/rap/RAP_annotation/RAP_annotation.mat', 'r'))
29 | for idx in range(51):
30 | dataset['att_name'].append(data['RAP_annotation'][0][0][6][idx][0][0])
31 |
32 | for idx in range(41585):
33 | dataset['image'].append(data['RAP_annotation'][0][0][5][idx][0][0])
34 | dataset['att'].append(data['RAP_annotation'][0][0][1][idx, :].tolist())
35 |
36 | with open(os.path.join(save_dir, 'rap_dataset.pkl'), 'w+') as f:
37 | pickle.dump(dataset, f)
38 |
39 | def create_trainvaltest_split(traintest_split_file):
40 | """
41 | create a dataset split file, which consists of index of the train/val/test splits
42 | """
43 | partition = dict()
44 | partition['trainval'] = []
45 | partition['test'] = []
46 | partition['weight_trainval'] = []
47 | # load RAP_annotation.mat
48 | data = loadmat(open('./dataset/rap/RAP_annotation/RAP_annotation.mat', 'r'))
49 | for idx in range(5):
50 | trainval = (data['RAP_annotation'][0][0][0][idx][0][0][0][0][0,:]-1).tolist()
51 | test = (data['RAP_annotation'][0][0][0][idx][0][0][0][1][0,:]-1).tolist()
52 | partition['trainval'].append(trainval)
53 | partition['test'].append(test)
54 | # weight
55 | weight_trainval = np.mean(data['RAP_annotation'][0][0][1][trainval, :].astype('float32')==1, axis=0).tolist()
56 | partition['weight_trainval'].append(weight_trainval)
57 | with open(traintest_split_file, 'w+') as f:
58 | pickle.dump(partition, f)
59 |
60 | if __name__ == "__main__":
61 | import argparse
62 | parser = argparse.ArgumentParser(description="rap dataset")
63 | parser.add_argument(
64 | '--save_dir',
65 | type=str,
66 | default='./dataset/rap/')
67 | parser.add_argument(
68 | '--traintest_split_file',
69 | type=str,
70 | default="./dataset/rap/rap_partition.pkl")
71 | args = parser.parse_args()
72 | save_dir = args.save_dir
73 | traintest_split_file = args.traintest_split_file
74 |
75 | generate_data_description(save_dir)
76 | create_trainvaltest_split(traintest_split_file)
77 |
--------------------------------------------------------------------------------
/script/dataset/transform_rap2.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import random
4 | import cPickle as pickle
5 | from scipy.io import loadmat
6 |
7 | np.random.seed(0)
8 | random.seed(0)
9 |
10 | def make_dir(path):
11 | if os.path.exists(path):
12 | pass
13 | else:
14 | os.mkdir(path)
15 |
16 | def generate_data_description(save_dir):
17 | """
18 | create a dataset description file, which consists of images, labels
19 | """
20 | dataset = dict()
21 | dataset['description'] = 'rap2'
22 | dataset['root'] = './dataset/rap2/RAP_dataset/'
23 | dataset['image'] = []
24 | dataset['att'] = []
25 | dataset['att_name'] = []
26 | # load RAP_annotation.mat
27 | data = loadmat(open('./dataset/rap2/RAP_annotation/RAP_annotation.mat', 'r'))
28 | dataset['selected_attribute'] = (data['RAP_annotation'][0][0][3][0,:]-1).tolist()
29 | for idx in range(152):
30 | dataset['att_name'].append(data['RAP_annotation'][0][0][2][idx][0][0])
31 |
32 | for idx in range(84928):
33 | dataset['image'].append(data['RAP_annotation'][0][0][0][idx][0][0])
34 | dataset['att'].append(data['RAP_annotation'][0][0][1][idx, :].tolist())
35 |
36 | with open(os.path.join(save_dir, 'rap2_dataset.pkl'), 'w+') as f:
37 | pickle.dump(dataset, f)
38 |
39 | def create_trainvaltest_split(traintest_split_file):
40 | """
41 | create a dataset split file, which consists of index of the train/val/test splits
42 | """
43 | partition = dict()
44 | partition['train'] = []
45 | partition['val'] = []
46 | partition['trainval'] = []
47 | partition['test'] = []
48 | partition['weight_train'] = []
49 | partition['weight_trainval'] = []
50 | # load RAP_annotation.mat
51 | data = loadmat(open('./dataset/rap2/RAP_annotation/RAP_annotation.mat', 'r'))
52 | for idx in range(5):
53 | train = (data['RAP_annotation'][0][0][4][0, idx][0][0][0][0,:]-1).tolist()
54 | val = (data['RAP_annotation'][0][0][4][0, idx][0][0][1][0,:]-1).tolist()
55 | test = (data['RAP_annotation'][0][0][4][0, idx][0][0][2][0,:]-1).tolist()
56 | trainval = train + val
57 | partition['trainval'].append(trainval)
58 | partition['train'].append(train)
59 | partition['val'].append(val)
60 | partition['test'].append(test)
61 | # weight
62 | weight_train = np.mean(data['RAP_annotation'][0][0][1][train, :].astype('float32')==1, axis=0).tolist()
63 | weight_trainval = np.mean(data['RAP_annotation'][0][0][1][trainval, :].astype('float32')==1, axis=0).tolist()
64 | partition['weight_train'].append(weight_train)
65 | partition['weight_trainval'].append(weight_trainval)
66 |
67 | with open(traintest_split_file, 'w+') as f:
68 | pickle.dump(partition, f)
69 |
70 | if __name__ == "__main__":
71 | import argparse
72 | parser = argparse.ArgumentParser(description="rap2 dataset")
73 | parser.add_argument(
74 | '--save_dir',
75 | type=str,
76 | default='./dataset/rap2/')
77 | parser.add_argument(
78 | '--traintest_split_file',
79 | type=str,
80 | default="./dataset/rap2/rap2_partition.pkl")
81 | args = parser.parse_args()
82 | save_dir = args.save_dir
83 | traintest_split_file = args.traintest_split_file
84 |
85 | generate_data_description(save_dir)
86 | create_trainvaltest_split(traintest_split_file)
87 |
--------------------------------------------------------------------------------
/script/experiment/demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import numpy as np
4 | import random
5 | import math
6 |
7 | import torch
8 | import torchvision.transforms as transforms
9 | from torch.autograd import Variable
10 | from torch.nn.parallel import DataParallel
11 | import cPickle as pickle
12 | import time
13 | import argparse
14 | from PIL import Image, ImageFont, ImageDraw
15 |
16 | from baseline.model.DeepMAR import DeepMAR_ResNet50
17 | from baseline.utils.utils import str2bool
18 | from baseline.utils.utils import save_ckpt, load_ckpt
19 | from baseline.utils.utils import load_state_dict
20 | from baseline.utils.utils import set_devices
21 | from baseline.utils.utils import set_seed
22 |
23 |
24 | class Config(object):
25 | def __init__(self):
26 |
27 | parser = argparse.ArgumentParser()
28 | parser.add_argument('-d', '--sys_device_ids', type=eval, default=(0,))
29 | parser.add_argument('--set_seed', type=str2bool, default=False)
30 | # model
31 | parser.add_argument('--resize', type=eval, default=(224, 224))
32 | parser.add_argument('--last_conv_stride', type=int, default=2, choices=[1,2])
33 | # demo image
34 | parser.add_argument('--demo_image', type=str, default='./dataset/demo/demo_image.png')
35 | ## dataset parameter
36 | parser.add_argument('--dataset', type=str, default='peta',
37 | choices=['peta','rap', 'pa100k'])
38 | # utils
39 | parser.add_argument('--load_model_weight', type=str2bool, default=True)
40 | parser.add_argument('--model_weight_file', type=str, default='./exp/deepmar_resnet50/peta/partition0/run1/model/ckpt_epoch150.pth')
41 | args = parser.parse_args()
42 |
43 | # gpu ids
44 | self.sys_device_ids = args.sys_device_ids
45 |
46 | # random
47 | self.set_seed = args.set_seed
48 | if self.set_seed:
49 | self.rand_seed = 0
50 | else:
51 | self.rand_seed = None
52 | self.resize = args.resize
53 | self.mean = [0.485, 0.456, 0.406]
54 | self.std = [0.229, 0.224, 0.225]
55 |
56 | # utils
57 | self.load_model_weight = args.load_model_weight
58 | self.model_weight_file = args.model_weight_file
59 | if self.load_model_weight:
60 | if self.model_weight_file == '':
61 | print 'Please input the model_weight_file if you want to load model weight'
62 | raise ValueError
63 | # dataset
64 | datasets = dict()
65 | datasets['peta'] = './dataset/peta/peta_dataset.pkl'
66 | datasets['rap'] = './dataset/rap/rap_dataset.pkl'
67 | datasets['pa100k'] = './dataset/pa100k/pa100k_dataset.pkl'
68 |
69 | if args.dataset in datasets:
70 | dataset = pickle.load(open(datasets[args.dataset]))
71 | else:
72 | print '%s does not exist.'%(args.dataset)
73 | raise ValueError
74 | self.att_list = [dataset['att_name'][i] for i in dataset['selected_attribute']]
75 |
76 | # demo image
77 | self.demo_image = args.demo_image
78 |
79 | # model
80 | model_kwargs = dict()
81 | model_kwargs['num_att'] = len(self.att_list)
82 | model_kwargs['last_conv_stride'] = args.last_conv_stride
83 | self.model_kwargs = model_kwargs
84 |
85 | ### main function ###
86 | cfg = Config()
87 |
88 | # dump the configuration to log.
89 | import pprint
90 | print('-' * 60)
91 | print('cfg.__dict__')
92 | pprint.pprint(cfg.__dict__)
93 | print('-' * 60)
94 |
95 | # set the random seed
96 | if cfg.set_seed:
97 | set_seed( cfg.rand_seed )
98 | # init the gpu ids
99 | set_devices(cfg.sys_device_ids)
100 |
101 | # dataset
102 | normalize = transforms.Normalize(mean=cfg.mean, std=cfg.std)
103 | test_transform = transforms.Compose([
104 | transforms.Resize(cfg.resize),
105 | transforms.ToTensor(),
106 | normalize,])
107 |
108 | ### Att model ###
109 | model = DeepMAR_ResNet50(**cfg.model_kwargs)
110 |
111 | # load model weight if necessary
112 | if cfg.load_model_weight:
113 | map_location = (lambda storage, loc:storage)
114 | ckpt = torch.load(cfg.model_weight_file, map_location=map_location)
115 | model.load_state_dict(ckpt['state_dicts'][0])
116 |
117 | model.cuda()
118 | model.eval()
119 |
120 | # load one image
121 | img = Image.open(cfg.demo_image)
122 | img_trans = test_transform( img )
123 | img_trans = torch.unsqueeze(img_trans, dim=0)
124 | img_var = Variable(img_trans).cuda()
125 | score = model(img_var).data.cpu().numpy()
126 |
127 | # show the score in command line
128 | for idx in range(len(cfg.att_list)):
129 | if score[0, idx] >= 0:
130 | print '%s: %.2f'%(cfg.att_list[idx], score[0, idx])
131 |
132 | # show the score in the image
133 | img = img.resize(size=(256, 512), resample=Image.BILINEAR)
134 | draw = ImageDraw.Draw(img)
135 | positive_cnt = 0
136 | for idx in range(len(cfg.att_list)):
137 | if score[0, idx] >= 0:
138 | txt = '%s: %.2f'%(cfg.att_list[idx], score[0, idx])
139 | draw.text((10, 10 + 10*positive_cnt), txt, (255, 0, 0))
140 | positive_cnt += 1
141 | img.save('./dataset/demo/demo_image_result.png')
142 |
--------------------------------------------------------------------------------
/script/experiment/test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | python ./script/experiment/train_deepmar_resnet50.py \
4 | --sys_device_ids="(0,)" \
5 | --dataset=peta \
6 | --partition_idx=0 \
7 | --test_split=test \
8 | --resize="(224,224)" \
9 | --exp_subpath=deepmar_resnet50 \
10 | --run=1 \
11 | --test_only=True \
12 | --load_model_weight=True \
13 | --model_weight_file='./exp/deepmar_resnet50/peta/partition0/run1/model/ckpt_epoch150.pth'
14 |
--------------------------------------------------------------------------------
/script/experiment/train.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | python ./script/experiment/train_deepmar_resnet50.py \
4 | --sys_device_ids="(0,)" \
5 | --dataset=peta \
6 | --partition_idx=0 \
7 | --split=trainval \
8 | --test_split=test \
9 | --batch_size=32 \
10 | --resize="(224,224)" \
11 | --exp_subpath=deepmar_resnet50 \
12 | --new_params_lr=0.001 \
13 | --finetuned_params_lr=0.001 \
14 | --staircase_decay_at_epochs="(50,100)" \
15 | --total_epochs=150 \
16 | --epochs_per_val=10\
17 | --epochs_per_save=50 \
18 | --drop_pool5=True \
19 | --drop_pool5_rate=0.5 \
20 | --run=1 \
21 | --resume=False \
22 | --ckpt_file= \
23 | --test_only=False \
24 | --model_weight_file= \
25 |
--------------------------------------------------------------------------------
/script/experiment/train_deepmar_resnet50.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import numpy as np
4 | import random
5 | import math
6 |
7 | import torch
8 | import torch.optim as optim
9 | import torchvision.transforms as transforms
10 | import torch.nn.functional as F
11 | import torch.backends.cudnn as cudnn
12 | from torch.autograd import Variable
13 | from torch.nn.parallel import DataParallel
14 | import cPickle as pickle
15 | import time
16 | import argparse
17 |
18 | from baseline.dataset import add_transforms
19 | from baseline.dataset.Dataset import AttDataset
20 | from baseline.model.DeepMAR import DeepMAR_ResNet50
21 | from baseline.model.DeepMAR import DeepMAR_ResNet50_ExtractFeature
22 | from baseline.utils.evaluate import attribute_evaluate
23 | from baseline.utils.utils import str2bool
24 | from baseline.utils.utils import transfer_optim_state
25 | from baseline.utils.utils import time_str
26 | from baseline.utils.utils import save_ckpt, load_ckpt
27 | from baseline.utils.utils import load_state_dict
28 | from baseline.utils.utils import ReDirectSTD
29 | from baseline.utils.utils import adjust_lr_staircase
30 | from baseline.utils.utils import set_devices
31 | from baseline.utils.utils import AverageMeter
32 | from baseline.utils.utils import to_scalar
33 | from baseline.utils.utils import may_set_mode
34 | from baseline.utils.utils import may_mkdir
35 | from baseline.utils.utils import set_seed
36 |
37 | class Config(object):
38 | def __init__(self):
39 |
40 | parser = argparse.ArgumentParser()
41 | parser.add_argument('-d', '--sys_device_ids', type=eval, default=(0,))
42 | parser.add_argument('--set_seed', type=str2bool, default=False)
43 | ## dataset parameter
44 | parser.add_argument('--dataset', type=str, default='peta',
45 | choices=['peta','rap', 'pa100k', 'rap2'])
46 | parser.add_argument('--split', type=str, default='trainval',
47 | choices=['trainval', 'train'])
48 | parser.add_argument('--test_split', type=str, default='test')
49 | parser.add_argument('--partition_idx', type=int, default=0)
50 | parser.add_argument('--resize', type=eval, default=(224, 224))
51 | parser.add_argument('--mirror', type=str2bool, default=True)
52 | parser.add_argument('--batch_size', type=int, default=32)
53 | parser.add_argument('--workers', type=int, default=2)
54 | # model
55 | parser.add_argument('--num_att', type=int, default=35)
56 | parser.add_argument('--pretrained', type=str2bool, default=True)
57 | parser.add_argument('--last_conv_stride', type=int, default=2, choices=[1,2])
58 | parser.add_argument('--drop_pool5', type=str2bool, default=True)
59 | parser.add_argument('--drop_pool5_rate', type=float, default=0.5)
60 |
61 | parser.add_argument('--sgd_weight_decay', type=float, default=0.0005)
62 | parser.add_argument('--sgd_momentum', type=float, default=0.9)
63 | parser.add_argument('--new_params_lr', type=float, default=0.001)
64 | parser.add_argument('--finetuned_params_lr', type=float, default=0.001)
65 | parser.add_argument('--staircase_decay_at_epochs', type=eval,
66 | default=(51, ))
67 | parser.add_argument('--staircase_decay_multiple_factor', type=float,
68 | default=0.1)
69 | parser.add_argument('--total_epochs', type=int, default=150)
70 | parser.add_argument('--weighted_entropy', type=str2bool, default=True)
71 | # utils
72 | parser.add_argument('--resume', type=str2bool, default=False)
73 | parser.add_argument('--ckpt_file', type=str, default='')
74 | parser.add_argument('--load_model_weight', type=str2bool, default=False)
75 | parser.add_argument('--model_weight_file', type=str, default='')
76 | parser.add_argument('--test_only', type=str2bool, default=False)
77 | parser.add_argument('--exp_dir', type=str, default='')
78 | parser.add_argument('--exp_subpath', type=str, default='deepmar_resnet50')
79 | parser.add_argument('--log_to_file', type=str2bool, default=True)
80 | parser.add_argument('--steps_per_log', type=int, default=20)
81 | parser.add_argument('--epochs_per_val', type=int, default=10)
82 | parser.add_argument('--epochs_per_save', type=int, default=50)
83 | parser.add_argument('--run', type=int, default=1)
84 | args = parser.parse_args()
85 |
86 | # gpu ids
87 | self.sys_device_ids = args.sys_device_ids
88 | # random
89 | self.set_seed = args.set_seed
90 | if self.set_seed:
91 | self.rand_seed = 0
92 | else:
93 | self.rand_seed = None
94 | # run time index
95 | self.run = args.run
96 | # Dataset #
97 | datasets = dict()
98 | datasets['peta'] = './dataset/peta/peta_dataset.pkl'
99 | datasets['rap'] = './dataset/rap/rap_dataset.pkl'
100 | datasets['pa100k'] = './dataset/pa100k/pa100k_dataset.pkl'
101 | datasets['rap2'] = './dataset/rap2/rap2_dataset.pkl'
102 | partitions = dict()
103 | partitions['peta'] = './dataset/peta/peta_partition.pkl'
104 | partitions['rap'] = './dataset/rap/rap_partition.pkl'
105 | partitions['pa100k'] = './dataset/pa100k/pa100k_partition.pkl'
106 | partitions['rap2'] = './dataset/rap2/rap2_partition.pkl'
107 |
108 | self.dataset_name = args.dataset
109 | if not datasets.has_key(args.dataset) or not partitions.has_key(args.dataset):
110 | print "Please select the right dataset name."
111 | raise ValueError
112 | else:
113 | self.dataset = datasets[args.dataset]
114 | self.partition = partitions[args.dataset]
115 | self.partition_idx = args.partition_idx
116 | self.split = args.split
117 | self.test_split = args.test_split
118 | self.resize = args.resize
119 | self.mirror = args.mirror
120 | self.mean = [0.485, 0.456, 0.406]
121 | self.std = [0.229, 0.224, 0.225]
122 | self.batch_size = args.batch_size
123 | self.workers = args.workers
124 | # optimization
125 | self.sgd_momentum = args.sgd_momentum
126 | self.sgd_weight_decay = args.sgd_weight_decay
127 | self.new_params_lr = args.new_params_lr
128 | self.finetuned_params_lr = args.finetuned_params_lr
129 | self.staircase_decay_at_epochs = args.staircase_decay_at_epochs
130 | self.staircase_decay_multiple_factor = args.staircase_decay_multiple_factor
131 | self.total_epochs = args.total_epochs
132 | self.weighted_entropy = args.weighted_entropy
133 |
134 | # utils
135 | self.resume = args.resume
136 | self.ckpt_file = args.ckpt_file
137 | if self.resume:
138 | if self.ckpt_file == '':
139 | print 'Please input the ckpt_file if you want to resume training'
140 | raise ValueError
141 | self.load_model_weight = args.load_model_weight
142 | self.model_weight_file = args.model_weight_file
143 | if self.load_model_weight:
144 | if self.model_weight_file == '':
145 | print 'Please input the model_weight_file if you want to load model weight'
146 | raise ValueError
147 | self.test_only = args.test_only
148 | self.exp_dir = args.exp_dir
149 | self.exp_subpath = args.exp_subpath
150 | self.log_to_file = args.log_to_file
151 | self.steps_per_log = args.steps_per_log
152 | self.epochs_per_val = args.epochs_per_val
153 | self.epochs_per_save = args.epochs_per_save
154 | self.run = args.run
155 |
156 | # for model
157 | model_kwargs = dict()
158 | model_kwargs['num_att'] = args.num_att
159 | model_kwargs['last_conv_stride'] = args.last_conv_stride
160 | model_kwargs['drop_pool5'] = args.drop_pool5
161 | model_kwargs['drop_pool5_rate'] = args.drop_pool5_rate
162 | self.model_kwargs = model_kwargs
163 | # for evaluation
164 | self.test_kwargs = dict()
165 |
166 | if self.exp_dir == '':
167 | self.exp_dir = os.path.join('exp',
168 | '{}'.format(self.exp_subpath),
169 | '{}'.format(self.dataset_name),
170 | 'partition{}'.format(self.partition_idx),
171 | 'run{}'.format(self.run))
172 | self.stdout_file = os.path.join(self.exp_dir, \
173 | 'log', 'stdout_{}.txt'.format(time_str()))
174 | self.stderr_file = os.path.join(self.exp_dir, \
175 | 'log', 'stderr_{}.txt'.format(time_str()))
176 | may_mkdir(self.stdout_file)
177 |
178 | ### main function ###
179 | cfg = Config()
180 |
181 | # log
182 | if cfg.log_to_file:
183 | ReDirectSTD(cfg.stdout_file, 'stdout', False)
184 | ReDirectSTD(cfg.stderr_file, 'stderr', False)
185 |
186 | # dump the configuration to log.
187 | import pprint
188 | print('-' * 60)
189 | print('cfg.__dict__')
190 | pprint.pprint(cfg.__dict__)
191 | print('-' * 60)
192 |
193 | # set the random seed
194 | if cfg.set_seed:
195 | set_seed( cfg.rand_seed )
196 | # init the gpu ids
197 | set_devices(cfg.sys_device_ids)
198 |
199 | # dataset
200 | normalize = transforms.Normalize(mean=cfg.mean, std=cfg.std)
201 | transform = transforms.Compose([
202 | transforms.Resize(cfg.resize),
203 | transforms.RandomHorizontalFlip(),
204 | transforms.ToTensor(), # 3*H*W, [0, 1]
205 | normalize,]) # normalize with mean/std
206 | # by a subset of attributes
207 | train_set = AttDataset(
208 | dataset = cfg.dataset,
209 | partition = cfg.partition,
210 | split = cfg.split,
211 | partition_idx= cfg.partition_idx,
212 | transform = transform)
213 |
214 | num_att = len(train_set.dataset['selected_attribute'])
215 | cfg.model_kwargs['num_att'] = num_att
216 |
217 | train_loader = torch.utils.data.DataLoader(
218 | dataset = train_set,
219 | batch_size = cfg.batch_size,
220 | shuffle = True,
221 | num_workers = cfg.workers,
222 | pin_memory = True,
223 | drop_last = False)
224 |
225 | test_transform = transforms.Compose([
226 | transforms.Resize(cfg.resize),
227 | transforms.ToTensor(),
228 | normalize,])
229 | test_set = AttDataset(
230 | dataset = cfg.dataset,
231 | partition = cfg.partition,
232 | split = cfg.test_split,
233 | partition_idx = cfg.partition_idx,
234 | transform = test_transform)
235 | ### Att model ###
236 | model = DeepMAR_ResNet50(**cfg.model_kwargs)
237 |
238 | # Wrap the model after set_devices, data parallel
239 | model_w = torch.nn.DataParallel(model)
240 |
241 | # using the weighted cross entropy loss
242 | if cfg.weighted_entropy:
243 | rate = np.array(train_set.partition['weight_' + cfg.split][cfg.partition_idx])
244 | rate = rate[train_set.dataset['selected_attribute']].tolist()
245 | else:
246 | rate = None
247 | # compute the weight of positive and negative
248 | if rate is None:
249 | weight_pos = [1 for i in range(num_att)]
250 | weight_neg = [1 for i in range(num_att)]
251 | else:
252 | if len(rate) != num_att:
253 | print "the length of rate should be equal to %d" % (num_att)
254 | raise ValueError
255 | weight_pos = []
256 | weight_neg = []
257 | for idx, v in enumerate(rate):
258 | weight_pos.append(math.exp(1.0 - v))
259 | weight_neg.append(math.exp(v))
260 | criterion = F.binary_cross_entropy_with_logits
261 |
262 | # Optimizer
263 | finetuned_params = []
264 | new_params = []
265 | for n, p in model.named_parameters():
266 | if n.find('classifier') >=0:
267 | new_params.append(p)
268 | else:
269 | finetuned_params.append(p)
270 | param_groups = [{'params': finetuned_params, 'lr': cfg.finetuned_params_lr},
271 | {'params': new_params, 'lr': cfg.new_params_lr}]
272 |
273 | optimizer = optim.SGD(
274 | param_groups,
275 | momentum = cfg.sgd_momentum,
276 | weight_decay = cfg.sgd_weight_decay)
277 | # bind the model and optimizer
278 | modules_optims = [model, optimizer]
279 |
280 | # load model weight if necessary
281 | if cfg.load_model_weight:
282 | map_location = (lambda storage, loc:storage)
283 | ckpt = torch.load(cfg.model_weight_file, map_location=map_location)
284 | model.load_state_dict(ckpt['state_dicts'][0], strict=False)
285 |
286 | ### Resume or not ###
287 | if cfg.resume:
288 | # store the model, optimizer, epoch
289 | start_epoch, scores = load_ckpt(modules_optims, cfg.ckpt_file)
290 | else:
291 | start_epoch = 0
292 |
293 | model_w = torch.nn.DataParallel(model)
294 | model_w.cuda()
295 | transfer_optim_state(state=optimizer.state, device_id=0)
296 |
297 | # cudnn.benchmark = True
298 | # for evaluation
299 | feat_func_att = DeepMAR_ResNet50_ExtractFeature(model=model_w)
300 |
301 | def attribute_evaluate_subfunc(feat_func, test_set, **test_kwargs):
302 | """ evaluate the attribute recognition precision """
303 | result = attribute_evaluate(feat_func, test_set, **test_kwargs)
304 | print '-' * 60
305 | print 'Evaluation on %s set:' % (cfg.test_split)
306 | print 'Label-based evaluation: \n mA: %.4f'%(np.mean(result['label_acc']))
307 | print 'Instance-based evaluation: \n Acc: %.4f, Prec: %.4f, Rec: %.4f, F1: %.4f' \
308 | %(result['instance_acc'], result['instance_precision'], result['instance_recall'], result['instance_F1'])
309 | print '-' * 60
310 |
311 | # print the model into log
312 | print model
313 | # test only
314 | if cfg.test_only:
315 | print 'test with feat_func_att'
316 | attribute_evaluate_subfunc(feat_func_att, test_set, **cfg.test_kwargs)
317 | sys.exit(0)
318 |
319 | # training
320 | for epoch in range(start_epoch, cfg.total_epochs):
321 | # adjust the learning rate
322 | adjust_lr_staircase(
323 | optimizer.param_groups,
324 | [cfg.finetuned_params_lr, cfg.new_params_lr],
325 | epoch + 1,
326 | cfg.staircase_decay_at_epochs,
327 | cfg.staircase_decay_multiple_factor)
328 |
329 | may_set_mode(modules_optims, 'train')
330 | # recording loss
331 | loss_meter = AverageMeter()
332 | dataset_L = len(train_loader)
333 | ep_st = time.time()
334 |
335 | for step, (imgs, targets) in enumerate(train_loader):
336 |
337 | step_st = time.time()
338 | imgs_var = Variable(imgs).cuda()
339 | targets_var = Variable(targets).cuda()
340 |
341 | score = model_w(imgs_var)
342 |
343 | # compute the weight
344 | weights = torch.zeros(targets_var.shape)
345 | for i in range(targets_var.shape[0]):
346 | for j in range(targets_var.shape[1]):
347 | if targets_var.data.cpu()[i, j] == -1:
348 | weights[i, j] = weight_neg[j]
349 | elif targets_var.data.cpu()[i, j] == 1:
350 | weights[i, j] = weight_pos[j]
351 | else:
352 | weights[i, j] = 0
353 |
354 | # loss for the attribute classification, average over the batch size
355 | targets_var[targets_var == -1] = 0
356 | loss = criterion(score, targets_var, weight=Variable(weights.cuda()))*num_att
357 |
358 | optimizer.zero_grad()
359 | loss.backward()
360 | optimizer.step()
361 |
362 | ############
363 | # step log #
364 | ############
365 | loss_meter.update(to_scalar(loss))
366 |
367 | if (step+1) % cfg.steps_per_log == 0 or (step+1)%len(train_loader) == 0:
368 | log = '{}, Step {}/{} in Ep {}, {:.2f}s, loss:{:.4f}'.format( \
369 | time_str(), step+1, dataset_L, epoch+1, time.time()-step_st, loss_meter.val)
370 | print(log)
371 |
372 | ##############
373 | # epoch log #
374 | ##############
375 | log = 'Ep{}, {:.2f}s, loss {:.4f}'.format(
376 | epoch+1, time.time() - ep_st, loss_meter.avg)
377 | print(log)
378 |
379 | # model ckpt
380 | if (epoch + 1) % cfg.epochs_per_save == 0 or epoch+1 == cfg.total_epochs:
381 | ckpt_file = os.path.join(cfg.exp_dir, 'model', 'ckpt_epoch%d.pth'%(epoch+1))
382 | save_ckpt(modules_optims, epoch+1, 0, ckpt_file)
383 |
384 | ##########################
385 | # test on validation set #
386 | ##########################
387 | if (epoch + 1) % cfg.epochs_per_val == 0 or epoch+1 == cfg.total_epochs:
388 | print 'att test with feat_func_att'
389 | attribute_evaluate_subfunc(feat_func_att, test_set, **cfg.test_kwargs)
390 |
--------------------------------------------------------------------------------