├── .idea ├── .gitignore ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── v1.iml └── vcs.xml ├── README.md ├── ReID_preprocessing ├── __init__.py ├── __init__.pyc ├── img_process_utils.py ├── img_process_utils.pyc ├── preprocessing_factory.py ├── preprocessing_factory.pyc ├── reid_preprocessing_fix.py └── reid_preprocessing_fix.pyc ├── dataset_factory.py ├── dataset_utils.py ├── eval_market_multi.py ├── model_deploy.py ├── nets ├── __init__.py ├── __init__.pyc ├── nets_factory.py ├── nets_factory.pyc ├── resnet_utils.py ├── resnet_utils.pyc ├── resnet_v1.py └── resnet_v1.pyc ├── pipeline.sh ├── train_ReID_classifier.py ├── train_ReID_classifier_con.py ├── train_ReID_classifier_from_resnet50.py └── utils.py /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/v1.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Robust Person Re-identification by Modelling Feature Uncertainty 2 | 3 | This repo contains the reference source code for the paper [Robust Person Re-identification by Modelling Feature Uncertainty](http://openaccess.thecvf.com/content_ICCV_2019/papers/Yu_Robust_Person_Re-Identification_by_Modelling_Feature_Uncertainty_ICCV_2019_paper.pdf) in ICCV 2019. In this project, we provide Resnet-Baseline and DistributionNet architectures and results with 10% random noise on Market dataset. 4 | 5 | 6 | ## Enviroment 7 | - Python 2.7 8 | - Tensorflow 1.3.0 9 | 10 | ## Getting started 11 | 12 | Download dataset from [here](https://drive.google.com/drive/folders/1VUpNKRjaxOh3A_sbgsWdKuhq7BOHOOC9?usp=sharing) 13 | 14 | Folder 'Market' includes original Market dataset (training, query, and gallery) and 10% random noise Market training dataset file. 15 | 16 | Folder 'result' includes trained models of Resnet-Baseline and DistributionNet 17 | 18 | Folder 'pretrained_model' includes Resnet-50 pretrained in ImageNet 19 | 20 | ## Train and Test 21 | Run 22 | ```bash pipeline.sh``` 23 | 24 | ## Results 25 | The test results will be recorded in `./result/model_name/rank.txt` 26 | -------------------------------------------------------------------------------- /ReID_preprocessing/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ReID_preprocessing/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/ReID_preprocessing/__init__.pyc -------------------------------------------------------------------------------- /ReID_preprocessing/img_process_utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | import tensorflow.contrib.slim as slim 7 | 8 | from tensorflow.python.ops import control_flow_ops 9 | 10 | def _crop(image, offset_height, offset_width, crop_height, crop_width): 11 | """Crops the given image using the provided offsets and sizes. 12 | 13 | Note that the method doesn't assume we know the input image size but it does 14 | assume we know the input image rank. 15 | 16 | Args: 17 | image: an image of shape [height, width, channels]. 18 | offset_height: a scalar tensor indicating the height offset. 19 | offset_width: a scalar tensor indicating the width offset. 20 | crop_height: the height of the cropped image. 21 | crop_width: the width of the cropped image. 22 | 23 | Returns: 24 | the cropped (and resized) image. 25 | 26 | Raises: 27 | InvalidArgumentError: if the rank is not 3 or if the image dimensions are 28 | less than the crop size. 29 | """ 30 | original_shape = tf.shape(image) 31 | 32 | rank_assertion = tf.Assert( 33 | tf.equal(tf.rank(image), 3), 34 | ['Rank of image must be equal to 3.']) 35 | cropped_shape = control_flow_ops.with_dependencies( 36 | [rank_assertion], 37 | tf.stack([crop_height, crop_width, original_shape[2]])) 38 | 39 | size_assertion = tf.Assert( 40 | tf.logical_and( 41 | tf.greater_equal(original_shape[0], crop_height), 42 | tf.greater_equal(original_shape[1], crop_width)), 43 | ['Crop size greater than the image size.']) 44 | 45 | offsets = tf.to_int32(tf.stack([offset_height, offset_width, 0])) 46 | 47 | # Use tf.slice instead of crop_to_bounding box as it accepts tensors to 48 | # define the crop size. 49 | image = control_flow_ops.with_dependencies( 50 | [size_assertion], 51 | tf.slice(image, offsets, cropped_shape)) 52 | return tf.reshape(image, cropped_shape) 53 | 54 | 55 | def _random_crop(image_list, crop_height, crop_width): 56 | """Crops the given list of images. 57 | 58 | The function applies the same crop to each image in the list. This can be 59 | effectively applied when there are multiple image inputs of the same 60 | dimension such as: 61 | 62 | image, depths, normals = _random_crop([image, depths, normals], 120, 150) 63 | 64 | Args: 65 | image_list: a list of image tensors of the same dimension but possibly 66 | varying channel. 67 | crop_height: the new height. 68 | crop_width: the new width. 69 | 70 | Returns: 71 | the image_list with cropped images. 72 | 73 | Raises: 74 | ValueError: if there are multiple image inputs provided with different size 75 | or the images are smaller than the crop dimensions. 76 | """ 77 | if not image_list: 78 | raise ValueError('Empty image_list.') 79 | 80 | # Compute the rank assertions. 81 | rank_assertions = [] 82 | for i in range(len(image_list)): 83 | image_rank = tf.rank(image_list[i]) 84 | rank_assert = tf.Assert( 85 | tf.equal(image_rank, 3), 86 | ['Wrong rank for tensor %s [expected] [actual]', 87 | image_list[i].name, 3, image_rank]) 88 | rank_assertions.append(rank_assert) 89 | 90 | image_shape = control_flow_ops.with_dependencies( 91 | [rank_assertions[0]], 92 | tf.shape(image_list[0])) 93 | image_height = image_shape[0] 94 | image_width = image_shape[1] 95 | crop_size_assert = tf.Assert( 96 | tf.logical_and( 97 | tf.greater_equal(image_height, crop_height), 98 | tf.greater_equal(image_width, crop_width)), 99 | ['Crop size greater than the image size.']) 100 | 101 | asserts = [rank_assertions[0], crop_size_assert] 102 | 103 | for i in range(1, len(image_list)): 104 | image = image_list[i] 105 | asserts.append(rank_assertions[i]) 106 | shape = control_flow_ops.with_dependencies([rank_assertions[i]], 107 | tf.shape(image)) 108 | height = shape[0] 109 | width = shape[1] 110 | 111 | height_assert = tf.Assert( 112 | tf.equal(height, image_height), 113 | ['Wrong height for tensor %s [expected][actual]', 114 | image.name, height, image_height]) 115 | width_assert = tf.Assert( 116 | tf.equal(width, image_width), 117 | ['Wrong width for tensor %s [expected][actual]', 118 | image.name, width, image_width]) 119 | asserts.extend([height_assert, width_assert]) 120 | 121 | # Create a random bounding box. 122 | # 123 | # Use tf.random_uniform and not numpy.random.rand as doing the former would 124 | # generate random numbers at graph eval time, unlike the latter which 125 | # generates random numbers at graph definition time. 126 | max_offset_height = control_flow_ops.with_dependencies( 127 | asserts, tf.reshape(image_height - crop_height + 1, [])) 128 | max_offset_width = control_flow_ops.with_dependencies( 129 | asserts, tf.reshape(image_width - crop_width + 1, [])) 130 | offset_height = tf.random_uniform( 131 | [], maxval=max_offset_height, dtype=tf.int32) 132 | offset_width = tf.random_uniform( 133 | [], maxval=max_offset_width, dtype=tf.int32) 134 | 135 | return [_crop(image, offset_height, offset_width, 136 | crop_height, crop_width) for image in image_list] 137 | 138 | 139 | def _upl_crop(image_list, crop_height, crop_width): 140 | # Upper Left 141 | outputs = [] 142 | for image in image_list: 143 | image_height = tf.shape(image)[0] 144 | image_width = tf.shape(image)[1] 145 | 146 | offset_height = 0 147 | offset_width = 0 148 | 149 | outputs.append(_crop(image, offset_height, offset_width, 150 | crop_height, crop_width)) 151 | return outputs 152 | 153 | def _upr_crop(image_list, crop_height, crop_width): 154 | # Upper Right 155 | outputs = [] 156 | for image in image_list: 157 | image_height = tf.shape(image)[0] 158 | image_width = tf.shape(image)[1] 159 | 160 | offset_height = 0 161 | offset_width = image_width - crop_width 162 | 163 | outputs.append(_crop(image, offset_height, offset_width, 164 | crop_height, crop_width)) 165 | return outputs 166 | 167 | def _dl_crop(image_list, crop_height, crop_width): 168 | # Down Left 169 | outputs = [] 170 | for image in image_list: 171 | image_height = tf.shape(image)[0] 172 | image_width = tf.shape(image)[1] 173 | 174 | offset_height = image_height - crop_height 175 | offset_width = 0 176 | 177 | outputs.append(_crop(image, offset_height, offset_width, 178 | crop_height, crop_width)) 179 | return outputs 180 | 181 | def _dr_crop(image_list, crop_height, crop_width): 182 | # Down Right 183 | outputs = [] 184 | for image in image_list: 185 | image_height = tf.shape(image)[0] 186 | image_width = tf.shape(image)[1] 187 | 188 | offset_height = image_height - crop_height 189 | offset_width = image_width - crop_width 190 | 191 | outputs.append(_crop(image, offset_height, offset_width, 192 | crop_height, crop_width)) 193 | return outputs 194 | 195 | 196 | def _central_crop(image_list, crop_height, crop_width): 197 | """Performs central crops of the given image list. 198 | 199 | Args: 200 | image_list: a list of image tensors of the same dimension but possibly 201 | varying channel. 202 | crop_height: the height of the image following the crop. 203 | crop_width: the width of the image following the crop. 204 | 205 | Returns: 206 | the list of cropped images. 207 | """ 208 | outputs = [] 209 | for image in image_list: 210 | image_height = tf.shape(image)[0] 211 | image_width = tf.shape(image)[1] 212 | 213 | offset_height = (image_height - crop_height) / 2 214 | offset_width = (image_width - crop_width) / 2 215 | 216 | outputs.append(_crop(image, offset_height, offset_width, 217 | crop_height, crop_width)) 218 | return outputs 219 | 220 | 221 | def _mean_image_subtraction(image, means): 222 | """Subtracts the given means from each image channel. 223 | 224 | For example: 225 | means = [123.68, 116.779, 103.939] 226 | image = _mean_image_subtraction(image, means) 227 | 228 | Note that the rank of `image` must be known. 229 | 230 | Args: 231 | image: a tensor of size [height, width, C]. 232 | means: a C-vector of values to subtract from each channel. 233 | 234 | Returns: 235 | the centered image. 236 | 237 | Raises: 238 | ValueError: If the rank of `image` is unknown, if `image` has a rank other 239 | than three or if the number of channels in `image` doesn't match the 240 | number of values in `means`. 241 | """ 242 | if image.get_shape().ndims != 3: 243 | raise ValueError('Input must be of size [height, width, C>0]') 244 | num_channels = image.get_shape().as_list()[-1] 245 | if len(means) != num_channels: 246 | raise ValueError('len(means) must match the number of channels') 247 | 248 | channels = tf.split(axis=2, num_or_size_splits=num_channels, value=image) 249 | for i in range(num_channels): 250 | channels[i] -= means[i] 251 | return tf.concat(axis=2, values=channels) 252 | 253 | 254 | def _smallest_size_at_least(height, width, smallest_side): 255 | """Computes new shape with the smallest side equal to `smallest_side`. 256 | 257 | Computes new shape with the smallest side equal to `smallest_side` while 258 | preserving the original aspect ratio. 259 | 260 | Args: 261 | height: an int32 scalar tensor indicating the current height. 262 | width: an int32 scalar tensor indicating the current width. 263 | smallest_side: A python integer or scalar `Tensor` indicating the size of 264 | the smallest side after resize. 265 | 266 | Returns: 267 | new_height: an int32 scalar tensor indicating the new height. 268 | new_width: and int32 scalar tensor indicating the new width. 269 | """ 270 | smallest_side = tf.convert_to_tensor(smallest_side, dtype=tf.int32) 271 | 272 | height = tf.to_float(height) 273 | width = tf.to_float(width) 274 | smallest_side = tf.to_float(smallest_side) 275 | 276 | scale = tf.cond(tf.greater(height, width), 277 | lambda: smallest_side / width, 278 | lambda: smallest_side / height) 279 | new_height = tf.to_int32(height * scale) 280 | new_width = tf.to_int32(width * scale) 281 | return new_height, new_width 282 | 283 | 284 | def _aspect_preserving_resize(image, smallest_side): 285 | """Resize images preserving the original aspect ratio. 286 | 287 | Args: 288 | image: A 3-D image `Tensor`. 289 | smallest_side: A python integer or scalar `Tensor` indicating the size of 290 | the smallest side after resize. 291 | 292 | Returns: 293 | resized_image: A 3-D tensor containing the resized image. 294 | """ 295 | smallest_side = tf.convert_to_tensor(smallest_side, dtype=tf.int32) 296 | 297 | shape = tf.shape(image) 298 | height = shape[0] 299 | width = shape[1] 300 | new_height, new_width = _smallest_size_at_least(height, width, smallest_side) 301 | image = tf.expand_dims(image, 0) 302 | resized_image = tf.image.resize_bilinear(image, [new_height, new_width], 303 | align_corners=False) 304 | resized_image = tf.squeeze(resized_image) 305 | resized_image.set_shape([None, None, 3]) 306 | return resized_image 307 | 308 | def _resize(image, new_height, new_width): 309 | shape = tf.shape(image) 310 | height = shape[0] 311 | width = shape[1] 312 | if (height == new_height) and (width == new_width): 313 | image = tf.expand_dims(image, 0) 314 | resized_image = tf.squeeze(image) 315 | resized_image.set_shape([None, None, 3]) 316 | else: 317 | image = tf.expand_dims(image, 0) 318 | resized_image = tf.image.resize_bilinear(image, [new_height, new_width], 319 | align_corners=False) 320 | resized_image = tf.squeeze(resized_image) 321 | resized_image.set_shape([None, None, 3]) 322 | return resized_image 323 | 324 | 325 | def augment_images_vgg(images): 326 | processed_images = tf.map_fn(lambda inputs: call_distort_image_vgg(inputs), elems=images, dtype=tf.float32) 327 | return processed_images 328 | 329 | 330 | def call_distort_image_vgg(image): 331 | image = tf.to_float(image) 332 | image = tf.image.random_flip_left_right(image) 333 | image = tf.subtract(image, [123.68, 116.78, 103.94]) 334 | return image 335 | 336 | 337 | def augment_images_inception(images, rand_erase=False): 338 | if rand_erase: 339 | processed_images = tf.map_fn(lambda inputs: call_distort_image_inception_with_erase(inputs), elems=images, dtype=tf.float32) 340 | else: 341 | processed_images = tf.map_fn(lambda inputs: call_distort_image_inception(inputs), elems=images, dtype=tf.float32) 342 | return processed_images 343 | 344 | 345 | def call_distort_image_inception(image): 346 | image = tf.to_float(image) 347 | image = tf.image.random_flip_left_right(image) 348 | image = tf.divide(image, 255) 349 | image = tf.subtract(image, 0.5) 350 | image = tf.multiply(image, 2.0) 351 | return image 352 | 353 | 354 | def call_distort_image_inception_with_erase(image): 355 | image = tf.to_float(image) 356 | image = tf.image.random_flip_left_right(image) 357 | image = rand_erase_images(image) 358 | image = tf.divide(image, 255) 359 | image = tf.subtract(image, 0.5) 360 | image = tf.multiply(image, 2.0) 361 | return image 362 | 363 | 364 | def call_distort_image(image): 365 | crop_size = FLAGS.crop_size 366 | if FLAGS.basenet in ['inceptionv1', 'inceptionv3']: 367 | image = tf.subtract(image, 0.5) 368 | image = tf.mul(image, 2.0) 369 | elif FLAGS.basenet in ['alexnet', 'resnet']: 370 | image = tf.subtract(image, [104.0069879317889, 116.66876761696767, 122.6789143406786]) 371 | else: 372 | image = tf.subtract(image, 250.42) 373 | return distort_image(image, crop_size, crop_size) 374 | 375 | 376 | def rand_erase_images(image, sl = 0.02, sh = 0.4, r1 = 0.3): 377 | 378 | image_height, image_width = int(image.shape[0]), int(image.shape[1]) 379 | area = image_height * image_width 380 | target_area = tf.random_uniform([], sl, sh, name='area') * area 381 | aspect_ratio = tf.random_uniform([], r1, 1/r1, name='aspect_ratio') 382 | target_h = tf.minimum(tf.cast(tf.sqrt(target_area*aspect_ratio), tf.int32), image_height-2) 383 | target_w = tf.minimum(tf.cast(tf.sqrt(target_area/aspect_ratio), tf.int32), image_width-2) 384 | offset_x = tf.random_uniform([], 1, image_height-target_h, tf.int32, name='offset_x') 385 | offset_y = tf.random_uniform([], 1, image_width-target_w, tf.int32, name='offset_y') 386 | erase_cond = tf.less(tf.random_uniform([], 0, 1.0), .5) 387 | result_image = tf.cond( 388 | erase_cond, 389 | lambda: fix_erase_images(image, offset_x, offset_y, target_h, target_w), 390 | lambda: image) 391 | 392 | return result_image 393 | 394 | 395 | def fix_erase_images(image, x, y, h, w): 396 | image_height, image_width, dim_size = int(image.shape[0]), int(image.shape[1]), int(image.shape[2]) 397 | image_top = tf.slice(image, [0, 0, 0], [x, image_width, dim_size]) 398 | image_left = tf.slice(image, [x, 0, 0], [h, y, dim_size]) 399 | image_right = tf.slice(image, [x, y+w, 0], [h, image_width-y-w, dim_size]) 400 | image_bottom = tf.slice(image, [x+h, 0, 0], [image_height-x-h, image_width, dim_size]) 401 | random_image_part = tf.random_uniform([h, w, 3], 0, 1.0) 402 | image_between = tf.concat([image_left, random_image_part, image_right], 1) 403 | erased_image = tf.concat([image_top, image_between, image_bottom], 0) 404 | return tf.reshape(erased_image, [image_height, image_width, dim_size]) 405 | 406 | 407 | def process_images_vgg_for_eval(images): 408 | processed_images = tf.map_fn(lambda inputs: call_process_image_vgg_for_eval(inputs), elems=images, dtype=tf.float32) 409 | return processed_images 410 | 411 | 412 | def call_process_image_vgg_for_eval(image): 413 | image = tf.to_float(image) 414 | image = tf.subtract(image, [123.68, 116.78, 103.94]) 415 | return image 416 | 417 | 418 | def process_images_inception_for_eval(images): 419 | processed_images = tf.map_fn(lambda inputs: call_process_image_inception_for_eval(inputs), elems=images, dtype=tf.float32) 420 | return processed_images 421 | 422 | 423 | def call_process_image_inception_for_eval(image): 424 | image = tf.to_float(image) 425 | image = tf.divide(image, 255) 426 | image = tf.subtract(image, 0.5) 427 | image = tf.multiply(image, 2.0) 428 | return image -------------------------------------------------------------------------------- /ReID_preprocessing/img_process_utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/ReID_preprocessing/img_process_utils.pyc -------------------------------------------------------------------------------- /ReID_preprocessing/preprocessing_factory.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Contains a factory for building various models.""" 16 | 17 | from __future__ import absolute_import 18 | from __future__ import division 19 | from __future__ import print_function 20 | 21 | from ReID_preprocessing import reid_preprocessing_fix 22 | 23 | def get_preprocessing(name, aug_mode=1, test_mode=1, is_training=False, rand_erase=False): 24 | """Returns preprocessing_fn(image, height, width, **kwargs). 25 | 26 | Args: 27 | name: The name of the preprocessing function. 28 | is_training: `True` if the model is being used for training and `False` 29 | otherwise. 30 | 31 | Returns: 32 | preprocessing_fn: A function that preprocessing a single image (pre-batch). 33 | It has the following signature: 34 | image = preprocessing_fn(image, output_height, output_width, ...). 35 | 36 | Raises: 37 | ValueError: If Preprocessing `name` is not recognized. 38 | """ 39 | 40 | assert test_mode in [1, 2] 41 | 42 | if aug_mode == 3: 43 | # directly resize to fix shape 44 | # train: resize to 256 x 128, then random flip 45 | # testing: resize to 256 x 128 46 | # test_mode 1: just resize 47 | # test_mode 2: flip = 2 samples 48 | preprocessing_fn_map = { 49 | 'resnet_v1_50': reid_preprocessing_fix, 50 | 'resnet_v1_distributions_50': reid_preprocessing_fix, 51 | 'resnet_v1_distributions_baseline_50': reid_preprocessing_fix, 52 | } 53 | else: 54 | raise ValueError('aug_mode should be set to 3.') 55 | 56 | if name not in preprocessing_fn_map: 57 | raise ValueError('Preprocessing name [%s] was not recognized' % name) 58 | 59 | def preprocessing_fn(image, output_height, output_width, **kwargs): 60 | if rand_erase: 61 | return preprocessing_fn_map[name].preprocess_image( 62 | image, output_height, output_width, test_mode, is_training=is_training, rand_erase=True, **kwargs) 63 | else: 64 | return preprocessing_fn_map[name].preprocess_image( 65 | image, output_height, output_width, test_mode, is_training=is_training, **kwargs) 66 | 67 | return preprocessing_fn 68 | -------------------------------------------------------------------------------- /ReID_preprocessing/preprocessing_factory.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/ReID_preprocessing/preprocessing_factory.pyc -------------------------------------------------------------------------------- /ReID_preprocessing/reid_preprocessing_fix.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Provides utilities to preprocess images. 16 | 17 | The preprocessing steps for VGG were introduced in the following technical 18 | report: 19 | 20 | Very Deep Convolutional Networks For Large-Scale Image Recognition 21 | Karen Simonyan and Andrew Zisserman 22 | arXiv technical report, 2015 23 | PDF: http://arxiv.org/pdf/1409.1556.pdf 24 | ILSVRC 2014 Slides: http://www.robots.ox.ac.uk/~karen/pdf/ILSVRC_2014.pdf 25 | CC-BY-4.0 26 | 27 | More information can be obtained from the VGG website: 28 | www.robots.ox.ac.uk/~vgg/research/very_deep/ 29 | """ 30 | 31 | from __future__ import absolute_import 32 | from __future__ import division 33 | from __future__ import print_function 34 | 35 | import tensorflow as tf 36 | import tensorflow.contrib.slim as slim 37 | 38 | import ReID_preprocessing.img_process_utils as img_proc_utils 39 | import pdb 40 | 41 | # pdb.set_trace() 42 | 43 | 44 | # slim = tf.contrib.slim 45 | 46 | _R_MEAN = 123.68 47 | _G_MEAN = 116.78 48 | _B_MEAN = 103.94 49 | 50 | _RESIZE_H = 256 51 | _RESIZE_W = 128 52 | 53 | 54 | # _RESIZE_SIDE_MIN = 256 55 | # _RESIZE_SIDE_MAX = 512 56 | 57 | 58 | def preprocess_for_train(image, 59 | output_height, 60 | output_width): 61 | """Preprocesses the given image for training. 62 | 63 | Note that the actual resizing scale is sampled from 64 | [`resize_size_min`, `resize_size_max`]. 65 | 66 | Args: 67 | image: A `Tensor` representing an image of arbitrary size. 68 | output_height: The height of the image after preprocessing. 69 | output_width: The width of the image after preprocessing. 70 | 71 | Returns: 72 | A preprocessed image. 73 | """ 74 | # image = img_proc_utils._resize(image, _RESIZE_H, _RESIZE_W) 75 | # image = img_proc_utils._random_crop([image], output_height, output_width)[0] 76 | # image.set_shape([output_height, output_width, 3]) 77 | # image = tf.to_float(image) 78 | # image = tf.image.random_flip_left_right(image) 79 | # return img_proc_utils._mean_image_subtraction(image, [_R_MEAN, _G_MEAN, _B_MEAN]) 80 | return img_proc_utils.augment_images_vgg(image) 81 | 82 | 83 | def _post_img_list(crop_img_list): 84 | tmp_list = [] 85 | for image in crop_img_list: 86 | # image.set_shape([output_height, output_width, 3]) 87 | image = tf.to_float(image) 88 | # Remove Mean Here 89 | tmp_list.append(img_proc_utils._mean_image_subtraction(image, [_R_MEAN, _G_MEAN, _B_MEAN])) 90 | return tmp_list 91 | 92 | 93 | def preprocess_for_eval(image, output_height, output_width, test_mode): 94 | """Preprocesses the given image for evaluation. 95 | 96 | Args: 97 | image: A `Tensor` representing an image of arbitrary size. 98 | output_height: The height of the image after preprocessing. 99 | output_width: The width of the image after preprocessing. 100 | test_mode: 101 | 1: crop central 102 | 2: crop 4 corner + central and flip = 10 samples 103 | 104 | Returns: 105 | preprocessed image / images (testing) list!!!. 106 | """ 107 | 108 | # remove the image resizing 109 | # image = tf.image.resize_images(image, [output_height, output_width]) 110 | image = tf.image.resize_bilinear(image, [output_height, output_width]) 111 | 112 | if test_mode == 1: 113 | 114 | # return a list 115 | return img_proc_utils.process_images_vgg_for_eval(image) 116 | elif test_mode == 2: 117 | # crop 4 corner + central and flip = 10 samples 118 | raise Exception('Not implemented') 119 | 120 | # return a list 121 | 122 | 123 | 124 | def preprocess_image(image, output_height, output_width, test_mode, is_training=False): 125 | """Preprocesses the given image. 126 | 127 | Args: 128 | image: A `Tensor` representing an image of arbitrary size. 129 | output_height: The height of the image after preprocessing. 130 | output_width: The width of the image after preprocessing. 131 | is_training: `True` if we're preprocessing the image for training and 132 | `False` otherwise. 133 | 134 | Returns: 135 | preprocessed image / images (testing). 136 | """ 137 | if is_training: 138 | return preprocess_for_train(image, output_height, output_width) 139 | else: 140 | return preprocess_for_eval(image, output_height, output_width, test_mode) 141 | -------------------------------------------------------------------------------- /ReID_preprocessing/reid_preprocessing_fix.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/ReID_preprocessing/reid_preprocessing_fix.pyc -------------------------------------------------------------------------------- /dataset_factory.py: -------------------------------------------------------------------------------- 1 | import os 2 | import h5py 3 | import itertools 4 | import numpy as np 5 | import tensorflow as tf 6 | import tensorflow.contrib.slim as slim 7 | 8 | def load_hdf5(fname): 9 | hf = h5py.File(fname, 'r') 10 | d = {key: np.array(hf.get(key)) for key in hf.keys()} 11 | hf.close() 12 | return d 13 | 14 | 15 | class DataLoader(object): 16 | """Class for loading data.""" 17 | 18 | def __init__(self, model, name, dataset_dir, _set, hd, img_func, batch_size, batch_k, num_iters, pair_type='single', openset=False, target_number=0): 19 | self.model = model 20 | self.dataset = name 21 | self.dataset_dir = dataset_dir 22 | self.subset = _set 23 | self.img_func = img_func 24 | self.batch_size = batch_size 25 | self.batch_k = batch_k 26 | self.batch_p = batch_size/batch_k 27 | self.pair_type = pair_type 28 | self.num_iters = num_iters 29 | self.hdf5_dir = get_dataset_dir(name, dataset_dir) 30 | self.hd_flag = hd 31 | self.target_number = target_number 32 | if openset: 33 | self.load_data(openset) 34 | else: 35 | self.load_data() 36 | if 'clean' in model: 37 | self.load_ref() 38 | self.get_pairs() 39 | self.set_epochs() 40 | self.creat_tf_data() 41 | self.process_tf_data() 42 | self.config_get_batch() 43 | 44 | def load_ref(self): 45 | file_path = '/import/vision-ephemeral/ty303/clean-net-master/util/'+self.dataset+'/ref.npy' 46 | self.ref_data = np.load(file_path) 47 | 48 | file_path = '/import/vision-ephemeral/ty303/clean-net-master/util/'+self.dataset+'/0.1_0_train.npy' 49 | temp = np.load(file_path) 50 | 51 | a = 1 52 | 53 | 54 | def load_data(self, openset=False): 55 | 56 | if self.hd_flag: 57 | self.hdf5_file = os.path.join(self.hdf5_dir, '%s_hd.h5' % self.subset) 58 | else: 59 | self.hdf5_file = os.path.join(self.hdf5_dir, '%s.h5' % self.subset) 60 | 61 | data_info = load_hdf5(self.hdf5_file) 62 | self.label = data_info['label'] 63 | if openset: 64 | import random 65 | if self.target_number == 15: 66 | self.label = np.array(self.label) 67 | label_tmp = np.unique(self.label) 68 | rand_start_label = np.random.choice(label_tmp[:-16],1) 69 | #rand_start_label = np.random.choice(label_tmp[:-(int(0.01*len(self.label))+2)],1) 70 | #rand_start_label = random.randint(0, max(self.label)) 71 | start = self.label.tolist().index(rand_start_label) 72 | #rand_end_label = label_tmp[label_tmp.tolist().index(rand_start_label)+int(0.01*len(self.label))+1] 73 | rand_end_label = label_tmp[label_tmp.tolist().index(rand_start_label)+15+1] 74 | end = self.label.tolist().index(rand_end_label) 75 | 76 | if self.target_number == 100: 77 | start = self.label.tolist().index(100) 78 | end = self.label.tolist().index(200) 79 | 80 | self.label = self.label[start:end] 81 | self.data = data_info['image_data'][start:end] 82 | 83 | self.pid = data_info['pid'][start:end] 84 | self.cid = data_info['cid'][start:end] 85 | self.image_id = data_info['id'][start:end] 86 | self.image_name = data_info['image_name'][start:end] 87 | else: 88 | self.data = data_info['image_data'] 89 | self.pid = data_info['pid'] 90 | self.cid = data_info['cid'] 91 | self.image_id = data_info['id'] 92 | self.image_name = data_info['image_name'] 93 | 94 | self.num_classes = max(self.label) - min(self.label) + 1 95 | tmp = set(self.label) 96 | 97 | self.data_size, self.image_height, self.image_width, self.num_channel = self.data.shape 98 | self.num_samples = self.data_size # notice that num_samples is equal to number of training images 99 | 100 | def creat_tf_data(self): 101 | batch_size, height, width, num_channel = self.batch_size, self.image_height, self.image_width, self.num_channel 102 | self.tf_data = tf.placeholder(tf.float32, shape=(batch_size, height, width, num_channel), name="data") 103 | if 'clean' in self.model: 104 | self.tf_ref_data = tf.placeholder(tf.float32, shape=(batch_size, 2, 2048), name="ref_data") 105 | self.tf_label = tf.placeholder(tf.int32, shape=batch_size, name="label") 106 | self.tf_vlabel = tf.placeholder(tf.int32, shape=batch_size, name="vlabel") 107 | self.tf_vflag = tf.placeholder(tf.int32, shape=batch_size, name="vflag") 108 | if self.pair_type == 'pair': 109 | full_product_size = batch_size*batch_size/2 110 | self.tf_pair = tf.placeholder(tf.int32, shape=(batch_size, full_product_size/batch_size, 3), name="pair") 111 | if self.pair_type == 'trip': 112 | product_size = batch_size*batch_size/4 113 | self.tf_trip = tf.placeholder(tf.int32, shape=(batch_size, product_size/batch_size, 3), name="trip") 114 | if self.pair_type == 'hard': 115 | product_size = self.batch_p*self.batch_k*self.batch_k*(self.batch_p-1) 116 | self.tf_trip = tf.placeholder(tf.int32, shape=(batch_size, product_size/batch_size, 4), name="trip") 117 | 118 | def process_tf_data(self): 119 | self.tf_images = self.img_func(self.tf_data) 120 | self.tf_labels = slim.one_hot_encoding(self.tf_label, self.num_classes) 121 | if 'clean' in self.model: 122 | self.tf_batch_queue = [self.tf_images, self.tf_labels, self.tf_ref_data, self.tf_vlabel, self.tf_vflag] # input for network 123 | self.tf_batch_tuple = (self.tf_data, self.tf_label, self.tf_ref_data, self.tf_vlabel, self.tf_vflag) # input for feed dict 124 | elif self.pair_type in ['single', 'eval']: 125 | self.tf_batch_queue = [self.tf_images, self.tf_labels] # input for network 126 | self.tf_batch_tuple = (self.tf_data, self.tf_label) # input for feed dict 127 | elif self.pair_type == 'pair': 128 | self.tf_batch_queue = [self.tf_images, self.tf_labels, self.tf_pair] # input for network 129 | self.tf_batch_tuple = (self.tf_data, self.tf_label, self.tf_pair) # input for feed dict 130 | elif self.pair_type in ['trip', 'hard']: 131 | self.tf_batch_queue = [self.tf_images, self.tf_labels, self.tf_trip] # input for network 132 | self.tf_batch_tuple = (self.tf_data, self.tf_label, self.tf_trip) # input for feed dict 133 | else: 134 | raise Exception('pair type error') 135 | 136 | def get_pairs(self): 137 | if self.pair_type in ['single', 'eval']: 138 | print "no need to load pairs" 139 | 140 | elif self.pair_type in ['pair', 'trip', 'hard']: 141 | min_width = self.batch_size/2 142 | if self.pair_type == 'hard': 143 | min_width = self.batch_p 144 | self.pairs, self.data_size = gen_data_pairs(self.label, min_width, self.batch_k, self.pair_type) 145 | print "set data size to the number of classes: %d" % self.data_size 146 | else: 147 | raise Exception('pair type error') 148 | 149 | def set_epochs(self): 150 | self.num_batches_per_epoch = int(np.ceil(self.data_size*1.0/self.batch_size)) 151 | if self.pair_type in ['pair', 'trip']: 152 | self.num_epochs = int(np.ceil(self.num_iters*1.0/self.data_size)) 153 | else: 154 | self.num_epochs = int(np.ceil(self.num_iters*1.0/self.num_batches_per_epoch)) 155 | 156 | def config_get_batch(self): 157 | if self.pair_type == 'single': 158 | self.gen_batches = self.batch_iter() 159 | elif self.pair_type == 'pair': 160 | self.gen_batches = self.pair_batch_iter() 161 | elif self.pair_type == 'trip': 162 | self.gen_batches = self.trip_batch_iter() 163 | elif self.pair_type == 'hard': 164 | self.gen_batches = self.hard_trip_batch_iter() 165 | elif self.pair_type == 'eval': 166 | self.gen_batches = self.batch_iter_eval() 167 | else: 168 | raise Exception('Pair type error') 169 | 170 | def get_feed_dict(self): 171 | 172 | batch_tuple = zip(*(self.gen_batches.next())) 173 | feed_dict = {tfdata_elem: batch_elem for tfdata_elem, batch_elem in zip(self.tf_batch_tuple, batch_tuple)} 174 | # import pdb 175 | # pdb.set_trace() 176 | # print feed_dict[feed_dict.keys()[0]].shape 177 | return feed_dict 178 | 179 | def batch_iter(self): 180 | # Generates a batch iterator for a dataset with naive data and label format. 181 | batch_size = self.batch_size 182 | for epoch in range(self.num_epochs): 183 | # Shuffle the data at each epoch 184 | shuffle_indices = np.random.permutation(np.arange(self.data_size)) 185 | for batch_id in range(self.num_batches_per_epoch): 186 | start_index = batch_id * batch_size 187 | indices = np.remainder(np.arange(start_index, start_index + batch_size), self.data_size) 188 | indices = shuffle_indices[indices] 189 | 190 | data_batch = self.data[indices, ::] 191 | label_batch = self.label[indices] 192 | if 'clean' in self.model: 193 | ref_batch = self.ref_data[label_batch] 194 | yield zip(data_batch, label_batch, ref_batch) 195 | else: 196 | yield zip(data_batch, label_batch) 197 | 198 | 199 | def batch_iter_eval(self): 200 | # Generates a batch iterator for a dataset with naive data and label format. 201 | batch_size = self.batch_size 202 | for epoch in range(self.num_epochs): 203 | for batch_id in range(self.num_batches_per_epoch): 204 | start_index = batch_id * batch_size 205 | indices = np.remainder(np.arange(start_index, start_index + batch_size), self.data_size) 206 | 207 | data_batch = self.data[indices, ::] 208 | label_batch = self.label[indices] 209 | id_batch = self.image_id[indices] 210 | name_batch = self.image_name[indices, 0] 211 | yield zip(data_batch, label_batch, id_batch, name_batch) 212 | 213 | def pair_batch_iter(self): 214 | # generate a batch iterator with paired data 215 | batch_size = self.batch_size 216 | half_batch_size = batch_size/2 217 | product_size = half_batch_size*half_batch_size 218 | full_product_size = product_size + product_size 219 | pair_data = np.ones((full_product_size, 3), dtype=np.int) 220 | # pair_data[:product_size, 2] = 1 221 | pair_data[product_size:, 2] = 0 222 | for epoch in range(self.num_epochs): 223 | # shuffle trip array, equals to shuffle the data, but can keep the relation between data and hard trip list 224 | for batch_id in range(self.data_size): 225 | pos_inds = np.random.permutation(self.pairs['pos'][batch_id])[:half_batch_size] 226 | neg_class_ids = np.random.choice(np.arange(self.data_size), batch_size-half_batch_size) 227 | neg_inds = [np.random.choice(self.pairs['pos'][neg_class_id]) for neg_class_id in neg_class_ids] 228 | batch_inds = list(itertools.chain(pos_inds, neg_inds)) 229 | 230 | pair_data[:product_size, 0] = self.pairs['p2p'][0] 231 | pair_data[:product_size, 1] = self.pairs['p2p'][1] 232 | pair_data[product_size:, 0] = self.pairs['p2p'][0] 233 | pair_data[product_size:, 1] = self.pairs['p2n'][1] 234 | 235 | indices = np.random.permutation(np.arange(full_product_size)) 236 | # pair_indices = np.random.permutation(np.arange(batch_size)) 237 | 238 | data_batch = self.data[batch_inds, ::] 239 | label_batch = self.label[batch_inds] 240 | pair_data_batch = np.reshape(pair_data[indices, ::], [batch_size, -1, 3]) 241 | # pair_data_batch = np.reshape(pair_data, [batch_size, -1, 3])[pair_indices, ::] 242 | 243 | yield zip(data_batch, label_batch, pair_data_batch) 244 | 245 | def trip_batch_iter(self): 246 | # generate a batch iterator with paired data 247 | batch_size = self.batch_size 248 | half_batch_size = batch_size / 2 249 | product_size = half_batch_size * half_batch_size 250 | trip_data = np.ones((product_size, 3), dtype=np.int) 251 | for epoch in range(self.num_epochs): 252 | # shuffle trip array, equals to shuffle the data, but can keep the relation between data and hard trip list 253 | for batch_id in range(self.data_size): 254 | pos_inds = np.random.permutation(self.pairs['pos'][batch_id])[:half_batch_size] 255 | # neg_inds = np.random.choice(self.pairs[batch_id]['neg'], batch_size-num_pos_inds) 256 | neg_class_ids = np.random.choice(np.arange(self.data_size), batch_size - half_batch_size) 257 | neg_inds = [np.random.choice(self.pairs['pos'][neg_class_id]) for neg_class_id in neg_class_ids] 258 | batch_inds = list(itertools.chain(pos_inds, neg_inds)) 259 | 260 | trip_data[:, 0] = self.pairs['p2p'][0] 261 | trip_data[:, 1] = self.pairs['p2p'][1] 262 | trip_data[:, 2] = self.pairs['p2n'][1] 263 | 264 | indices = np.random.permutation(np.arange(product_size)) 265 | 266 | data_batch = self.data[batch_inds, ::] 267 | label_batch = self.label[batch_inds] 268 | trip_data_batch = np.reshape(trip_data[indices, ::], [batch_size, -1, 3]) 269 | 270 | yield zip(data_batch, label_batch, trip_data_batch) 271 | 272 | def hard_trip_batch_iter(self): 273 | # generate a batch iterator with paired data 274 | batch_size = self.batch_size 275 | pos_pair_size = self.batch_p*self.batch_k*(self.batch_k-1) 276 | product_size = self.batch_p*self.batch_k*self.batch_k*(self.batch_p-1) 277 | trip_data = np.ones((product_size, 4), dtype=np.int) 278 | temp_trip_data = np.ones((batch_size, product_size/batch_size, 2), dtype=np.int) 279 | valid_dim = pos_pair_size/batch_size 280 | for epoch in range(self.num_epochs): 281 | for batch_id in range(self.data_size): 282 | batch_inds = get_batch_inds(self.pairs, self.batch_p, self.batch_k) 283 | 284 | # trip_data[:pos_pair_size, 0] = self.pairs['p2p'][:, 0] 285 | # trip_data[:pos_pair_size, 1] = self.pairs['p2p'][:, 1] 286 | 287 | temp_trip_data[:, :valid_dim, 0] = np.reshape(self.pairs['p2p'][:, 0], (batch_size, -1)) 288 | temp_trip_data[:, :valid_dim, 1] = np.reshape(self.pairs['p2p'][:, 1], (batch_size, -1)) 289 | trip_data[:, :2] = np.reshape(temp_trip_data, (-1, 2)) 290 | trip_data[:, 2] = self.pairs['p2n'][:, 0] 291 | trip_data[:, 3] = self.pairs['p2n'][:, 1] 292 | 293 | # indices = np.random.permutation(np.arange(product_size)) 294 | trip_indices = np.random.permutation(np.arange(batch_size)) 295 | 296 | data_batch = self.data[batch_inds, ::] 297 | label_batch = self.label[batch_inds] 298 | # trip_data_batch = np.reshape(trip_data[indices, ::], [batch_size, -1, 4]) 299 | trip_data_batch = np.reshape(trip_data, [batch_size, -1, 4])[trip_indices, ::] 300 | 301 | yield zip(data_batch, label_batch, trip_data_batch) 302 | 303 | 304 | def get_dataset_dir(name, dataset_dir): 305 | if name in ['CUHK03_New_ZL_D', 'CUHK03_New_ZL_L']: 306 | dataset_dir = os.path.join(dataset_dir, 'CUHK03_New_ZL') 307 | # elif name in ['CUHK01_AB', 'VIPeR', 'PRID', '3DPeS', 'i-LIDS_p80', 'i-LIDS_p50', 'i-LIDS_p30', 'GRID']: 308 | # dataset_dir = os.path.join(dataset_dir, '{}_TT'.format(name)) 309 | elif name in ['CUHK01_AB', '3DPeS', 'i-LIDS_p80', 'i-LIDS_p50', 'i-LIDS_p30']: 310 | dataset_dir = os.path.join(dataset_dir, '{}_TT'.format(name)) 311 | elif name in ['VIPeR_Data']: 312 | dataset_dir = os.path.join(dataset_dir, '{}'.format(name[:-5])) 313 | elif name in ['Viper']: 314 | dataset_dir = 'VIPeR'.join(dataset_dir.split('Viper')) 315 | elif name in ['i-LIDS']: 316 | dataset_dir = 'iLIDS'.join(dataset_dir.split('i-LIDS')) 317 | # pdb.set_trace() 318 | print("dataset_dir: %s" % dataset_dir) 319 | #assert os.path.isdir(dataset_dir) 320 | 321 | return dataset_dir 322 | 323 | 324 | def gen_data_pairs(label_data, min_width, depth, pair_type): 325 | num_class = max(label_data) + 1 326 | unique_ids = np.sort(np.unique(label_data)).tolist() 327 | 328 | pair_info = {'pos': []} 329 | 330 | assert num_class == len(unique_ids), "the labels are not consistent" 331 | 332 | for index in xrange(num_class): 333 | pos_ids = np.where(label_data==index)[0].tolist() 334 | pos_width = len(pos_ids) 335 | if pos_width < min_width: 336 | comp_width = min_width - pos_width 337 | pos_ids.extend(np.array(pos_ids)[np.remainder(np.arange(comp_width), pos_width)].tolist()) 338 | # pair_info['neg'] = [x for x in range_list if x not in pair_info['pos']] # not apply to hard triplet pair 339 | pair_info['pos'].append(pos_ids) 340 | 341 | if pair_type in ['pair', 'trip']: 342 | 343 | pos_rel_inds = range(min_width) 344 | neg_rel_inds = range(min_width, min_width+min_width) 345 | pos_to_pos_pairs = zip(*list(itertools.product(pos_rel_inds, pos_rel_inds))) 346 | pos_to_neg_pairs = zip(*list(itertools.product(pos_rel_inds, neg_rel_inds))) 347 | 348 | pair_info['p2p'] = pos_to_pos_pairs 349 | pair_info['p2n'] = pos_to_neg_pairs 350 | 351 | elif pair_type == 'hard': 352 | 353 | pos_to_pos_pairs = np.zeros((min_width, depth, depth-1, 2)) 354 | pos_to_neg_pairs = np.zeros((min_width, depth, depth*(min_width-1), 2)) 355 | all_indices = np.arange(min_width * depth).tolist() 356 | 357 | for ind_p in range(min_width): 358 | depth_offset = ind_p * depth 359 | pos_to_pos_pair_for_ind_p = zip(*list(itertools.permutations(range(depth_offset, depth_offset + depth), 2))) 360 | pos_to_pos_pairs[ind_p, :, :, 0] = np.reshape(pos_to_pos_pair_for_ind_p[0], (depth, -1)) 361 | pos_to_pos_pairs[ind_p, :, :, 1] = np.reshape(pos_to_pos_pair_for_ind_p[1], (depth, -1)) 362 | for ind_k in range(depth): 363 | pos_to_neg_pairs[ind_p, ind_k, :, 0] = depth_offset + ind_k 364 | pos_to_neg_pairs[ind_p, ind_k, :, 1] = all_indices[:depth_offset] + all_indices[(depth_offset + depth):] 365 | 366 | pair_info['p2p'] = np.reshape(pos_to_pos_pairs, (-1, 2)) 367 | pair_info['p2n'] = np.reshape(pos_to_neg_pairs, (-1, 2)) 368 | 369 | else: 370 | raise Exception('Pair type error') 371 | 372 | return pair_info, num_class 373 | 374 | 375 | def get_batch_inds(pairs, batch_p, batch_k): 376 | num_class = len(pairs['pos']) 377 | batch_inds = [] 378 | batch_ps = np.random.choice(num_class, batch_p, replace=False) 379 | # import pdb 380 | # pdb.set_trace() 381 | for ind_p in range(batch_p): 382 | batch_inds.extend(np.random.choice(pairs['pos'][batch_ps[ind_p]], batch_k, replace=True)) 383 | return batch_inds 384 | -------------------------------------------------------------------------------- /dataset_utils.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | from PIL import Image 4 | import numpy as np 5 | import skimage.io as io 6 | 7 | import scipy.misc 8 | # import matplotlib.pyplot as plt # some error here 9 | 10 | import os 11 | import pdb 12 | 13 | """ 14 | TFRecord tutorial style generation 15 | 16 | class ImageReader: retrieval the spatial info from string encoded object (required by .tfrecord files); 17 | it is provied us a way to decode string encoded object back to image arrays; 18 | the internal decoding function (graph) need a tf session to run it. 19 | Still not sure how excatly this can be done??? 20 | 21 | decode the image string data to image array 22 | decode_img_string_tf(img_string): no need external height and width info, 23 | but the decode graph (as a ImageReader object) and a session is needed to run it. 24 | """ 25 | 26 | 27 | class ImageReader(object): 28 | """Helper class that provides TF image coding utilities. 29 | since input image as a string, so the height and width is not direct 30 | this class is focus on retrieval spatial indormation from string data. 31 | """ 32 | 33 | def __init__(self): 34 | # Initializes function that decodes RGB JPEG data. 35 | # here a graph is defined? 36 | self._decode_jpeg_data = tf.placeholder(dtype=tf.string) 37 | self._decode_jpeg = tf.image.decode_jpeg(self._decode_jpeg_data, channels=3) 38 | 39 | def read_image_dims(self, sess, image_data): 40 | image = self.decode_jpeg(sess, image_data) 41 | return image.shape[0], image.shape[1] 42 | 43 | # here provides the decode methods!!!!! from string encoded to array data 44 | def decode_jpeg(self, sess, image_data): 45 | image = sess.run(self._decode_jpeg, 46 | feed_dict={self._decode_jpeg_data: image_data}) 47 | # image_data is string 48 | assert len(image.shape) == 3 49 | assert image.shape[2] == 3 50 | return image 51 | 52 | 53 | def decode_img_string_tf(img_string, decoder, sess): 54 | # external run session, 55 | # external graph context, 56 | # graph in decoder.decode_jpeg. 57 | 58 | image = decoder.decode_jpeg(sess, img_string) 59 | # pdb.set_trace() 60 | return image 61 | 62 | 63 | """ 64 | Alternative to TFRecord tutorial style generation: 65 | 66 | Mainly based on Daniil's BLOG: Tfrecords Guide 67 | 68 | image2string: 69 | read the image file directly as array, the spatial info cam be get directly, 70 | no need complicated methods (define graph and session run in 71 | height, width = image_reader.read_image_dims(sess, image_data)) 72 | array to string also have simple API 73 | 74 | decode the image string data to image array 75 | decode_img_string_np: parsing the .tfrecord files in alternative way (no graph and session are needed) 76 | """ 77 | 78 | 79 | def image2string(img_file_name): 80 | img = np.array(Image.open(img_file_name)) 81 | height = img.shape[0] 82 | width = img.shape[1] 83 | image_data = img.tostring() 84 | return image_data, height, width 85 | 86 | 87 | def decode_img_string_np(img_string, height, width): 88 | # recover as a 1d array 89 | # dtype=np.uint8 is important 90 | img_1d = np.fromstring(img_string, dtype=np.uint8) 91 | # reshape to image 92 | return img_1d.reshape((height, width, -1)) 93 | 94 | 95 | """ 96 | Transform the bunch of data to tfexample style 97 | """ 98 | 99 | 100 | def int64_feature(values): 101 | """Returns a TF-Feature of int64s. 102 | 103 | Args: 104 | values: A scalar or list of values. 105 | 106 | Returns: 107 | a TF-Feature. 108 | """ 109 | if not isinstance(values, (tuple, list)): 110 | values = [values] 111 | return tf.train.Feature(int64_list=tf.train.Int64List(value=values)) 112 | 113 | 114 | def bytes_feature(values): 115 | """Returns a TF-Feature of bytes. 116 | 117 | Args: 118 | values: A string. 119 | 120 | Returns: 121 | a TF-Feature. 122 | """ 123 | return tf.train.Feature(bytes_list=tf.train.BytesList(value=[values])) 124 | 125 | 126 | def ReID_image_to_tfexample(image_data, image_file_name, image_format, height, width, 127 | PID, PL, CID, CP, LCV, LPI, DID, ImUID): 128 | # tf.train.Example is a very important data utility for 129 | # generating and parsing .tfrecord file 130 | return tf.train.Example(features=tf.train.Features(feature={ 131 | 'image/encoded': bytes_feature(image_data), 132 | 'image/filename': bytes_feature(image_file_name), 133 | 'image/format': bytes_feature(image_format), 134 | 'image/height': int64_feature(height), 135 | 'image/width': int64_feature(width), 136 | 'image/PID': int64_feature(PID), 137 | 'image/PL': int64_feature(PL), 138 | 'image/CID': int64_feature(CID), 139 | 'image/CP': int64_feature(CP), 140 | 'image/LCV': int64_feature(LCV), 141 | 'image/LPI': int64_feature(LPI), 142 | 'image/DID': int64_feature(DID), 143 | 'image/ImUID': int64_feature(ImUID), 144 | })) 145 | 146 | def Market_ReID_image_to_tfexample(image_data, image_file_name, image_format, height, width, 147 | PID, PL, CID, Seq, frame, BB, DID, ImUID): 148 | # tf.train.Example is a very important data utility for 149 | # generating and parsing .tfrecord file 150 | return tf.train.Example(features=tf.train.Features(feature={ 151 | 'image/encoded': bytes_feature(image_data), 152 | 'image/filename': bytes_feature(image_file_name), 153 | 'image/format': bytes_feature(image_format), 154 | 'image/height': int64_feature(height), 155 | 'image/width': int64_feature(width), 156 | 'image/PID': int64_feature(PID), 157 | 'image/PL': int64_feature(PL), 158 | 'image/CID': int64_feature(CID), 159 | 'image/Seq': int64_feature(Seq), 160 | 'image/frame': int64_feature(frame), 161 | 'image/BB': int64_feature(BB), 162 | 'image/DID': int64_feature(DID), 163 | 'image/ImUID': int64_feature(ImUID), 164 | })) 165 | 166 | 167 | def SmallSet_ReID_image_to_tfexample(image_data, img_full_path, image_format, height, width, 168 | PID, PL, CID, DID, ImUID): 169 | # tf.train.Example is a very important data utility for 170 | # generating and parsing .tfrecord file 171 | return tf.train.Example(features=tf.train.Features(feature={ 172 | 'image/encoded': bytes_feature(image_data), 173 | 'image/filename': bytes_feature(img_full_path), 174 | 'image/format': bytes_feature(image_format), 175 | 'image/height': int64_feature(height), 176 | 'image/width': int64_feature(width), 177 | 'image/PID': int64_feature(PID), 178 | 'image/PL': int64_feature(PL), 179 | 'image/CID': int64_feature(CID), 180 | 'image/DID': int64_feature(DID), 181 | 'image/ImUID': int64_feature(ImUID), 182 | })) 183 | 184 | 185 | def MergeSets_ReID_image_to_tfexample(image_data, img_full_path, image_format, height, width, 186 | PID_str, PL, CID, DID, ImUID): 187 | # tf.train.Example is a very important data utility for 188 | # generating and parsing .tfrecord file 189 | return tf.train.Example(features=tf.train.Features(feature={ 190 | 'image/encoded': bytes_feature(image_data), 191 | 'image/filename': bytes_feature(img_full_path), 192 | 'image/format': bytes_feature(image_format), 193 | 'image/height': int64_feature(height), 194 | 'image/width': int64_feature(width), 195 | 'image/PID': bytes_feature(PID_str), 196 | 'image/PL': int64_feature(PL), 197 | 'image/CID': int64_feature(CID), 198 | 'image/DID': int64_feature(DID), 199 | 'image/ImUID': int64_feature(ImUID), 200 | })) 201 | 202 | # def show_image(img_array): 203 | 204 | 205 | 206 | def parsing_compare_tfrecord(tfrecord_files, img_folder, _STRING_ENCODE_STYLE): 207 | """ 208 | recover the information in tfrecord_file and compare with the original images 209 | 210 | :param tfrecord_files: the list of .tfrecord files 211 | :param img_folder: original images folder 212 | :param _STRING_ENCODE_STYLE: different ways to decode the string encoded image data 213 | :return: 214 | """ 215 | 216 | for tfrecord_file in tfrecord_files: 217 | # record_iterator for iterating example string objects in tfrecord file, one by one 218 | record_iterator = tf.python_io.tf_record_iterator(path=tfrecord_file) 219 | 220 | for example_str_obj in record_iterator: 221 | # define an example object for parsing example string object 222 | example = tf.train.Example() 223 | # parsing the string and load the data 224 | example.ParseFromString(example_str_obj) 225 | 226 | # read the data out from example using 227 | # example.features.feature['name'].[type: int64_list, bytes_list, float_list].value 228 | # it correspinding to the tf.train.Example(features=tf.train.Features(feature={'name': format_data})) structure 229 | # 'name' need to be corespondented. 230 | # returned a list 231 | 232 | height = int(example.features.feature['image/height'].int64_list.value[0]) 233 | width = int(example.features.feature['image/width'].int64_list.value[0]) 234 | PID = int(example.features.feature['image/PID'].int64_list.value[0]) 235 | CID = int(example.features.feature['image/CID'].int64_list.value[0]) 236 | CP = int(example.features.feature['image/CP'].int64_list.value[0]) 237 | LCV = int(example.features.feature['image/LCV'].int64_list.value[0]) 238 | LPI = int(example.features.feature['image/LPI'].int64_list.value[0]) 239 | 240 | ori_file_name_str = example.features.feature['image/filename'].bytes_list.value[0] 241 | print 'Ori file name: {}'.format(ori_file_name_str) 242 | img_full_name = os.path.join(img_folder, ori_file_name_str) 243 | assert os.path.isfile(img_full_name) 244 | img_ori_array = np.array(Image.open(img_full_name)) 245 | print img_ori_array.dtype 246 | assert img_ori_array.shape[0] == height 247 | assert img_ori_array.shape[1] == width 248 | 249 | print 'Height: {}, Width: {}, PID: {}, CID: {},' \ 250 | ' CP: {}, LCV: {}, LPI: {}'.format(height, width, PID, CID, CP, LCV, LPI) 251 | 252 | # Discuss later: string data encoding and decoding 253 | # different ways to decode the string data according to different encode ways 254 | img_string = example.features.feature['image/encoded'].bytes_list.value[0] 255 | 256 | if _STRING_ENCODE_STYLE is 'tf': 257 | with tf.Graph().as_default(): 258 | # ImageReader: provide string data decoding function 259 | image_reader = ImageReader() 260 | 261 | # sess for run the graph 262 | # (using ImageReader object) 263 | with tf.Session('') as sess: 264 | # Even though the image recovered from tf style encoding string is not totally (erery pixel) the same as 265 | # original image, perceptionally no much difference (looks the same). 266 | img_recover_array = decode_img_string_tf(img_string, image_reader, sess) 267 | 268 | elif _STRING_ENCODE_STYLE is 'np': 269 | # totally the same as original images 270 | img_recover_array = decode_img_string_np(img_string, height, width) 271 | else: 272 | raise ValueError('No such string encode style: {}'.format(_STRING_ENCODE_STYLE)) 273 | 274 | # spatial info comparison 275 | assert img_recover_array.shape == img_ori_array.shape 276 | assert img_ori_array.shape[0] == height 277 | assert img_ori_array.shape[1] == width 278 | 279 | # pdb.set_trace() 280 | 281 | # can not plot, do not know why????? 282 | # show_image(img_ori_array) 283 | # show_image(img_recover_array) 284 | 285 | abs_diff = np.abs(img_ori_array - img_recover_array) 286 | print np.sum(np.asarray(abs_diff == 1, dtype=np.int64)) 287 | print np.sum(np.asarray(abs_diff == 2, dtype=np.int64)) 288 | print np.sum(np.asarray(abs_diff == 255, dtype=np.int64)) 289 | 290 | # SAVE AND COMPARE 291 | # Even though the image recovered from tf style encoding string is not totally (erery pixel) the same as 292 | # original image, perceptionally no much difference (looks the same). 293 | img_cmp_path = '/import/vision-datasets001/xc302/ReID_Datasets/TFRecords/img_cmp' 294 | scipy.misc.imsave('{}/ori.jpg'.format(img_cmp_path), img_ori_array) 295 | scipy.misc.imsave('{}/recover.jpg'.format(img_cmp_path), img_recover_array) 296 | 297 | 298 | # pdb.set_trace() 299 | if np.allclose(img_ori_array, img_recover_array): 300 | print 'recover image is close to original image' 301 | else: 302 | print 'recover image is NOT close to original image' 303 | 304 | raw_input() 305 | -------------------------------------------------------------------------------- /eval_market_multi.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Generic evaluation script that evaluates a model using a given dataset.""" 16 | 17 | from __future__ import absolute_import 18 | from __future__ import division 19 | from __future__ import print_function 20 | 21 | import math 22 | import os 23 | import numpy as np 24 | 25 | import tensorflow as tf 26 | 27 | import dataset_factory 28 | from nets import nets_factory 29 | 30 | import tensorflow.contrib.slim as slim 31 | 32 | from utils import config_eval_ckpt_path, get_img_func, get_pair_type 33 | 34 | model = 'resnet_v1_50' 35 | 36 | tf.app.flags.DEFINE_string('sub_dir', '', 'Subdirectory to identify the sv dir') 37 | 38 | tf.app.flags.DEFINE_string('dataset_dir', '', 39 | 'The directory where the dataset files are stored.') 40 | 41 | 42 | tf.app.flags.DEFINE_string('dataset_name', '', 'The name of the Person ReID dataset to load.') 43 | 44 | tf.app.flags.DEFINE_string('feat_layer', 'PreLogits', 'The name of the feature layer.') 45 | 46 | tf.app.flags.DEFINE_integer('batch_size', 100, 'The number of samples in each batch.') 47 | tf.app.flags.DEFINE_integer('batch_k', 4, 'The number of samples for each class (identity) in each batch.') 48 | tf.app.flags.DEFINE_integer('max_num_batches', None, 'Max number of batches to evaluate by default use all.') 49 | tf.app.flags.DEFINE_string('master', '', 'The address of the TensorFlow master to use.') 50 | tf.app.flags.DEFINE_string('checkpoint_path', '/import/vision-ephemeral/ty303/result', 51 | 'The directory where the model was written to or an absolute path to a checkpoint file.') 52 | #tf.app.flags.DEFINE_string('checkpoint_path', '/homes/ty303/Downloads/slim_reid_h5/result', 53 | # 'The directory where the model was written to or an absolute path to a checkpoint file.') 54 | 55 | tf.app.flags.DEFINE_string('eval_dir', '', 56 | 'Directory where the results are saved to.') 57 | #tf.app.flags.DEFINE_string('eval_dir', '/homes/ty303/Downloads/slim_reid_h5/result', 58 | # 'Directory where the results are saved to.') 59 | 60 | tf.app.flags.DEFINE_integer('num_preprocessing_threads', 4, 'The number of threads used to create the batches.') 61 | tf.app.flags.DEFINE_integer('GAN_Pose_v', None, 'The GAN Pose Version.') 62 | tf.app.flags.DEFINE_string('set', None, "train, all_test_prb, all_test_gal, randOne_test_prb, randOne_test_gal") 63 | tf.app.flags.DEFINE_integer('pose_n', None, 'The Pose to be extracted') 64 | tf.app.flags.DEFINE_string('source', None, 'detected, labeled, mixed') 65 | tf.app.flags.DEFINE_integer('split_num', None, '0-19') 66 | tf.app.flags.DEFINE_integer('cam_num', None, '6 cams or 10 cams.') 67 | tf.app.flags.DEFINE_integer('test_step', None, 'The step of the tested model.') 68 | tf.app.flags.DEFINE_integer('max_number_of_steps', 60000, 'The maximum number of training steps.') 69 | 70 | # tf.app.flags.DEFINE_string( 71 | # 'dataset_split_name', 'test', 'The name of the train/test split.') 72 | 73 | tf.app.flags.DEFINE_boolean('hd_data', False, 'using high resolution image data for training.') 74 | tf.app.flags.DEFINE_integer('labels_offset', 0, 'An offset for the labels in the dataset. This flag is primarily used ' 75 | 'to evaluate the VGG and ResNet architectures which do not use a ' 76 | 'background class for the ImageNet dataset.') 77 | tf.app.flags.DEFINE_string('model_name', model, 'The name of the architecture to evaluate.') 78 | tf.app.flags.DEFINE_string('model_scope', '', 'The name scope of given model.') 79 | # tf.app.flags.DEFINE_string('feat_layer', 'layer_19', 'The name of the feature layer.') 80 | tf.app.flags.DEFINE_string('preprocessing_name', None, 'The name of the preprocessing to use. If left as `None`, then ' 81 | 'the model_name flag is used.') 82 | tf.app.flags.DEFINE_float('moving_average_decay', None, 'The decay to use for the moving average.If left as None, then ' 83 | 'moving averages are not used.') 84 | 85 | # tf.app.flags.DEFINE_integer( 86 | # 'eval_image_size', None, 'Eval image size') 87 | 88 | tf.app.flags.DEFINE_integer('aug_mode', 3, 'data augumentation(1,2,3)') 89 | tf.app.flags.DEFINE_boolean('rand_erase', False, 'random erasing the image to augment the data') 90 | tf.app.flags.DEFINE_integer('test_mode', 1, 'testing 1: central crop 2: (coner crops + central crop +) flips') 91 | tf.app.flags.DEFINE_integer('train_image_height', 256, 'Crop Height') 92 | tf.app.flags.DEFINE_integer('train_image_width', 128, 'Crop Width') 93 | 94 | 95 | ############## 96 | # Loss FLags # 97 | ############## 98 | 99 | tf.app.flags.DEFINE_boolean('use_clf', True, 'Add classification (identification) loss to the network.') 100 | 101 | ############### 102 | # Other Flags # 103 | ############### 104 | tf.app.flags.DEFINE_boolean('log_device_placement', False,"Whether to log device placement.") 105 | tf.app.flags.DEFINE_boolean('imagenet_pretrain', True, 'Using imagenet pretrained model to initialise.') 106 | tf.app.flags.DEFINE_string('checkpoint_exclude_scopes', '', 'Comma-separated list of scopes of variables to exclude ' 107 | 'when restoring from a checkpoint.') 108 | 109 | FLAGS = tf.app.flags.FLAGS 110 | 111 | 112 | def feat_aggregate(tmp_feats, image_list_len): 113 | if FLAGS.test_mode == 1: 114 | assert FLAGS.batch_size == tmp_feats.shape[0] 115 | return tmp_feats 116 | elif FLAGS.test_mode == 2: 117 | assert FLAGS.batch_size * image_list_len == tmp_feats.shape[0] 118 | assert len(tmp_feats.shape) == 2 119 | tmp_list = [] 120 | for i in range(FLAGS.batch_size): 121 | same_p_feat_idx_list = range(i, FLAGS.batch_size * image_list_len, FLAGS.batch_size) 122 | assert len(same_p_feat_idx_list) == image_list_len 123 | same_p_feat_idx_arr = np.asarray(same_p_feat_idx_list) 124 | same_p_feats = tmp_feats[same_p_feat_idx_arr, :] 125 | assert same_p_feats.shape[0] == image_list_len 126 | tmp_list.append(np.mean(same_p_feats, axis=0)) 127 | agged_feats = np.vstack(tmp_list) 128 | assert agged_feats.shape[0] == FLAGS.batch_size 129 | assert agged_feats.shape[1] == tmp_feats.shape[1] 130 | return agged_feats 131 | else: 132 | raise ValueError('No such test_mode') 133 | 134 | 135 | def _extract_feats(Restorer, endpoints, feat_layer_name, num_samples, images, labels, dataset): 136 | # ImUIDs: guarantees all the testing examples are extracted only once. 137 | # f_names: file names 138 | # TODO(sguada) use num_epochs=1 139 | if FLAGS.max_num_batches: 140 | num_batches = FLAGS.max_num_batches 141 | else: 142 | # This ensures that we make a single pass over all of the data. 143 | num_batches = int(math.ceil(num_samples / float(FLAGS.batch_size))) 144 | 145 | tf.logging.info('Features from Layer: %s' % feat_layer_name) 146 | 147 | if feat_layer_name == 'sample_dist': 148 | activations_op = endpoints[feat_layer_name] 149 | elif ',' in feat_layer_name: 150 | activations_op = [] 151 | tmp = feat_layer_name.split(',') 152 | for tmps in tmp: 153 | activations_op.append(endpoints[tmps]) 154 | else: 155 | activations_op = tf.squeeze(endpoints[feat_layer_name]) 156 | 157 | # Build an initialization operation to run below. 158 | init = tf.global_variables_initializer() 159 | 160 | # Start running operations on the Graph. allow_soft_placement must be set to 161 | # True to build towers on GPU, as some of the ops do not have GPU 162 | # implementations. 163 | #gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.2) 164 | sess = tf.Session(config=tf.ConfigProto( 165 | allow_soft_placement=True, 166 | log_device_placement=FLAGS.log_device_placement, 167 | # gpu_options=gpu_options 168 | )) 169 | sess.run(init) 170 | 171 | # config the directory to load the ckpt 172 | full_ckpt_path = config_eval_ckpt_path(FLAGS,flag=0) 173 | print(full_ckpt_path) 174 | 175 | if tf.gfile.IsDirectory(full_ckpt_path): 176 | checkpoint_path = tf.train.latest_checkpoint(full_ckpt_path) 177 | else: 178 | checkpoint_path = full_ckpt_path 179 | 180 | if checkpoint_path: 181 | global_step = checkpoint_path.split('/')[-1].split('-')[-1] 182 | print('Successfully loaded model from %s at step=%s.' % (checkpoint_path, global_step)) 183 | FLAGS.test_step = global_step 184 | else: 185 | raise ValueError('No checkpoint file found at %s' % FLAGS.checkpoint_path) 186 | 187 | # restoring the weights 188 | Restorer.restore(sess, checkpoint_path) 189 | 190 | # Start the queue runners. 191 | tf.train.start_queue_runners(sess=sess) 192 | 193 | feats_list = [] 194 | feats_sig_list = [] 195 | labels_list = [] 196 | f_names_list = [] 197 | appeared_img_ids = [] 198 | cameras_list = [] 199 | 200 | tf_data, tf_label = dataset.tf_data, dataset.tf_label 201 | 202 | for step in range(num_batches * 2): # since there is no shuffle, we need at most 2 round to get all the samples 203 | 204 | data_batch, label_batch, id_batch, name_batch = zip(*(dataset.gen_batches.next())) 205 | camera_batch = [] 206 | for name in name_batch: 207 | #camera_batch.append((name[name.find('c') + 1])) 208 | if 'CUHK' not in FLAGS.dataset_dir: 209 | tmp = name.split('/')[-1] 210 | camera_batch.append(float(tmp[tmp.find('c')+1])) 211 | elif 'CUHK03' in FLAGS.dataset_dir: 212 | tmp = name.split('/')[-1].split('_')[2] 213 | camera_batch.append(float(tmp)) 214 | else: 215 | tmp = name 216 | camera_batch.append((tmp[tmp.find('c') + 1])) 217 | camera_batch = tuple(camera_batch) 218 | #tmp_feats, sig_feats = sess.run(activations_op, feed_dict={tf_data: data_batch, tf_label: label_batch}) 219 | tmp_feats = sess.run(activations_op, feed_dict={tf_data: data_batch, tf_label: label_batch}) 220 | # check size 221 | data_batch, label_batch, id_batch = np.array(data_batch), np.array(label_batch), np.array(id_batch) 222 | assert id_batch.shape[0] == FLAGS.batch_size 223 | assert label_batch.shape[0] == FLAGS.batch_size 224 | sample_n, height, width, channel = data_batch.shape 225 | assert tmp_feats.shape[0] == sample_n 226 | # assert height == FLAGS.train_image_height 227 | # assert width == FLAGS.train_image_width 228 | assert channel == 3 229 | if FLAGS.test_mode == 1: 230 | assert sample_n == FLAGS.batch_size 231 | elif FLAGS.test_mode == 2: 232 | raise ValueError('Not implemented') 233 | else: 234 | raise ValueError('No such test_mode') 235 | # pdb.set_trace() 236 | 237 | # check the ImUIDs to be unique 238 | assert len(np.unique(id_batch)) == id_batch.shape[0] 239 | 240 | ImageUniIDs_list = id_batch.tolist() 241 | agg_feats = feat_aggregate(tmp_feats, 1) 242 | assert agg_feats.shape[0] == len(ImageUniIDs_list) 243 | for _idx in range(len(ImageUniIDs_list)): 244 | img_uid = ImageUniIDs_list[_idx] 245 | if img_uid not in appeared_img_ids: 246 | feats_list.append(agg_feats[_idx]) 247 | labels_list.append(label_batch[_idx]) 248 | f_names_list.append(name_batch[_idx]) 249 | cameras_list.append(camera_batch[_idx]) 250 | appeared_img_ids.append(img_uid) 251 | assert len(feats_list) == len(labels_list) == len(appeared_img_ids) == np.unique(appeared_img_ids).shape[0] 252 | if len(appeared_img_ids) == num_samples: 253 | break 254 | # check whether all unique samples are extracted. 255 | assert len(appeared_img_ids) == num_samples 256 | 257 | feats_ = np.array(feats_list)[0:num_samples, ::] 258 | labels_ = np.hstack(labels_list)[0:num_samples] 259 | cameras_ = np.hstack(cameras_list)[0:num_samples] 260 | names = f_names_list[0:num_samples] 261 | assert len(labels_) == num_samples 262 | assert feats_.shape[0] == num_samples 263 | 264 | # pdb.set_trace() 265 | # save the corresponding mat 266 | if FLAGS.dataset_name == 'Market': 267 | 268 | tmp = list(labels_).index(2) 269 | feats_ = feats_[tmp:] 270 | #feats_sig = feats_sig[tmp:] 271 | labels_ = labels_[tmp:] 272 | cameras_ = cameras_[tmp:] 273 | appeared_img_ids = appeared_img_ids[tmp:] 274 | names = names[tmp:] 275 | 276 | else: 277 | raise ValueError('Unrecognize dataset {}'.format(FLAGS.dataset_name)) 278 | 279 | mat_dict = {'feats': feats_, 'labels': labels_, 'cameras': cameras_, 280 | 'ImUIDs': np.asarray(appeared_img_ids, dtype=np.int), 'names': names} 281 | 282 | 283 | return mat_dict 284 | 285 | 286 | def main(_): 287 | folder_path = './result' 288 | files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if 'distri' in f]# and (len(os.listdir(folder_path+'/'+f)) == 4)]# or len(os.listdir(folder_path+'/'+f)) == 7)] 289 | 290 | 291 | for file in files: 292 | 293 | FLAGS.sub_dir = file 294 | 295 | FLAGS.dataset_dir = './Market/' 296 | FLAGS.dataset_name = 'Market' 297 | 298 | sub_sets = ['bounding_box_test', 'query'] 299 | 300 | #FLAGS.eval_dir += FLAGS.eval_dir + '/' + FLAGS.sub_dir + '_eval' 301 | 302 | feat_list = [] 303 | 304 | for sub_set in sub_sets: 305 | FLAGS.set = sub_set 306 | print("Extracting features for %s" % sub_set) 307 | feat_list.append(extract_features()) 308 | 309 | query = feat_list[-1] 310 | bounding_box_test = feat_list[0] 311 | 312 | q_feat_mean = query['feats'] 313 | test_feat_mean = bounding_box_test['feats'] 314 | 315 | distmat = np.zeros([q_feat_mean.shape[0], test_feat_mean.shape[0]]) 316 | 317 | print(distmat.shape[0]) 318 | 319 | print(distmat.shape[1]) 320 | 321 | q_feat = q_feat_mean / np.sqrt(np.sum(np.power(q_feat_mean, 2), axis=-1))[:, None] 322 | test_feat = test_feat_mean / np.sqrt(np.sum(np.power(test_feat_mean, 2), axis=-1))[:, None] 323 | 324 | distmat += np.repeat(np.expand_dims(np.sum(np.power(q_feat,2),axis=-1), axis=1), test_feat.shape[0], axis=1)+ \ 325 | np.repeat(np.expand_dims(np.sum(np.power(test_feat, 2), axis=-1), axis=0), q_feat.shape[0], axis=0) 326 | 327 | distmat -= 2*np.matmul(q_feat,test_feat.transpose()) 328 | 329 | print('average_mean: ' + str(distmat.mean())) 330 | 331 | cmc, map = eval_market(distmat, q_label = query['labels'], g_label = bounding_box_test['labels'], q_camera = query['cameras'], g_camera = bounding_box_test['cameras'], q_name=query['names'], g_name=bounding_box_test['names']) 332 | 333 | ranks = [1, 5, 10, 20] 334 | 335 | print("Results ----------") 336 | print("mAP: {:.8%}".format(map)) 337 | print("CMC curve") 338 | for r in ranks: 339 | print("Rank-{:<3}: {:.8%}".format(r, cmc[r - 1])) 340 | print("------------------") 341 | 342 | dict = {} 343 | dict['map'] = str(map) 344 | dict['rank1'] = str(cmc[0]) 345 | dict['rank5'] = str(cmc[4]) 346 | dict['rank10'] = str(cmc[9]) 347 | dict['rank20'] = str(cmc[19]) 348 | 349 | import json 350 | with open(file+'/rank.txt', 'w') as f: 351 | json.dump(dict, f) 352 | 353 | 354 | 355 | 356 | def eval_market(distmat, q_label, g_label, q_camera, g_camera, q_name, g_name, max_rank=20): 357 | num_q, num_g = distmat.shape 358 | indices = np.argsort(distmat,axis=1) 359 | matches = (g_label[indices] == q_label[:, np.newaxis]).astype(np.int32) 360 | 361 | all_cmc = [] 362 | all_AP = [] 363 | num_valid_q = 0. # number of valid query 364 | for q_idx in range(num_q): 365 | #print(q_idx) 366 | # get query pid and camid 367 | if 'Market' in FLAGS.dataset_dir or 'Duke' in FLAGS.dataset_dir or 'CUHK03' in FLAGS.dataset_dir: 368 | q_pid = q_label[q_idx] 369 | q_camid = q_camera[q_idx] 370 | 371 | order = indices[q_idx] 372 | remove = (g_label[order] == q_pid) & (g_camera[order] == q_camid) 373 | keep = np.invert(remove) 374 | 375 | # compute cmc curve 376 | orig_cmc = matches[q_idx][keep] # binary vector, positions with value 1 are correct matches 377 | else: 378 | orig_cmc = matches[q_idx] 379 | if not np.any(orig_cmc): 380 | # this condition is true when query identity does not appear in gallery 381 | continue 382 | 383 | cmc = orig_cmc.cumsum() 384 | cmc[cmc > 1] = 1 385 | 386 | all_cmc.append(cmc[:max_rank]) 387 | num_valid_q += 1. 388 | 389 | # compute average precision 390 | # reference: https://en.wikipedia.org/wiki/Evaluation_measures_(information_retrieval)#Average_precision 391 | num_rel = orig_cmc.sum() 392 | tmp_cmc = orig_cmc.cumsum() 393 | tmp_cmc = [x / (i+1.) for i, x in enumerate(tmp_cmc)] 394 | tmp_cmc = np.asarray(tmp_cmc) * orig_cmc 395 | AP = tmp_cmc.sum() / num_rel 396 | all_AP.append(AP) 397 | 398 | assert num_valid_q > 0, "Error: all query identities do not appear in gallery" 399 | 400 | all_cmc = np.asarray(all_cmc).astype(np.float32) 401 | all_cmc = all_cmc.sum(0) / num_valid_q 402 | mAP = np.mean(all_AP) 403 | 404 | return all_cmc, mAP 405 | 406 | 407 | 408 | def extract_features(): 409 | #if not os.path.isdir(FLAGS.eval_dir): 410 | # os.makedirs(FLAGS.eval_dir) 411 | 412 | if not FLAGS.dataset_dir: 413 | raise ValueError('You must supply the dataset directory with --dataset_dir') 414 | 415 | if not FLAGS.aug_mode: 416 | raise ValueError('aug_mode need to be speficied.') 417 | 418 | if (not FLAGS.train_image_height) or (not FLAGS.train_image_width): 419 | raise ValueError('The image height and width must be define explicitly.') 420 | 421 | tf.logging.set_verbosity(tf.logging.INFO) 422 | with tf.Graph().as_default(): 423 | tf_global_step = slim.get_or_create_global_step() 424 | 425 | ##################################### 426 | # Select the preprocessing function # 427 | ##################################### 428 | img_func = get_img_func(is_training=False) 429 | 430 | ###################### 431 | # Select the dataset # 432 | ###################### 433 | # testing pose extraction 434 | 435 | 436 | dataset = dataset_factory.DataLoader(FLAGS.model_name, FLAGS.dataset_name, FLAGS.dataset_dir, FLAGS.set, FLAGS.hd_data, img_func, 437 | FLAGS.batch_size, FLAGS.batch_k, FLAGS.max_number_of_steps, 438 | get_pair_type(is_training=False)) 439 | 440 | #################### 441 | # Select the model # 442 | #################### 443 | # testing mode 444 | # class num is None, is_training=False 445 | network_fn = nets_factory.get_network_fn( 446 | FLAGS.model_name, 447 | num_classes=None, 448 | is_training=False) # num_classes=None, should return pool5 features 449 | 450 | 451 | #################### 452 | # Define the model # 453 | #################### 454 | 455 | images, labels = dataset.tf_batch_queue[:2] 456 | 457 | logits, endpoints = network_fn(images) 458 | 459 | endpoints['pool5'] = logits 460 | 461 | ############################# 462 | # code segment from _get_init_fn 463 | # get the variables_to_restore considering FLAGS.checkpoint_exclude_scopes 464 | exclusions = [] 465 | if FLAGS.checkpoint_exclude_scopes: 466 | exclusions = [scope.strip() 467 | for scope in FLAGS.checkpoint_exclude_scopes.split(',')] 468 | 469 | # TODO(sguada) variables.filter_variables() 470 | variables_to_restore = [] 471 | tmp = slim.get_model_variables() 472 | for var in slim.get_model_variables(): 473 | excluded = False 474 | for exclusion in exclusions: 475 | if var.op.name.startswith(exclusion): 476 | excluded = True 477 | break 478 | if not excluded: 479 | variables_to_restore.append(var) 480 | 481 | labels = tf.squeeze(labels) 482 | 483 | Restorer = tf.train.Saver(variables_to_restore) 484 | 485 | feature_dict = _extract_feats(Restorer, endpoints, FLAGS.feat_layer, dataset.num_samples, images, labels, dataset) 486 | 487 | return feature_dict 488 | 489 | 490 | if __name__ == '__main__': 491 | tf.app.run() 492 | 493 | -------------------------------------------------------------------------------- /model_deploy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Deploy Slim models across multiple clones and replicas. 16 | 17 | # TODO(sguada) docstring paragraph by (a) motivating the need for the file and 18 | # (b) defining clones. 19 | 20 | # TODO(sguada) describe the high-level components of model deployment. 21 | # E.g. "each model deployment is composed of several parts: a DeploymentConfig, 22 | # which captures A, B and C, an input_fn which loads data.. etc 23 | 24 | To easily train a model on multiple GPUs or across multiple machines this 25 | module provides a set of helper functions: `create_clones`, 26 | `optimize_clones` and `deploy`. 27 | 28 | Usage: 29 | 30 | g = tf.Graph() 31 | 32 | # Set up DeploymentConfig 33 | config = model_deploy.DeploymentConfig(num_clones=2, clone_on_cpu=True) 34 | 35 | # Create the global step on the device storing the variables. 36 | with tf.device(config.variables_device()): 37 | global_step = slim.create_global_step() 38 | 39 | # Define the inputs 40 | with tf.device(config.inputs_device()): 41 | images, labels = LoadData(...) 42 | inputs_queue = slim.data.prefetch_queue((images, labels)) 43 | 44 | # Define the optimizer. 45 | with tf.device(config.optimizer_device()): 46 | optimizer = tf.train.MomentumOptimizer(FLAGS.learning_rate, FLAGS.momentum) 47 | 48 | # Define the model including the loss. 49 | def model_fn(inputs_queue): 50 | images, labels = inputs_queue.dequeue() 51 | predictions = CreateNetwork(images) 52 | slim.losses.log_loss(predictions, labels) 53 | 54 | model_dp = model_deploy.deploy(config, model_fn, [inputs_queue], 55 | optimizer=optimizer) 56 | 57 | # Run training. 58 | slim.learning.train(model_dp.train_op, my_log_dir, 59 | summary_op=model_dp.summary_op) 60 | 61 | The Clone namedtuple holds together the values associated with each call to 62 | model_fn: 63 | * outputs: The return values of the calls to `model_fn()`. 64 | * scope: The scope used to create the clone. 65 | * device: The device used to create the clone. 66 | 67 | DeployedModel namedtuple, holds together the values needed to train multiple 68 | clones: 69 | * train_op: An operation that run the optimizer training op and include 70 | all the update ops created by `model_fn`. Present only if an optimizer 71 | was specified. 72 | * summary_op: An operation that run the summaries created by `model_fn` 73 | and process_gradients. 74 | * total_loss: A `Tensor` that contains the sum of all losses created by 75 | `model_fn` plus the regularization losses. 76 | * clones: List of `Clone` tuples returned by `create_clones()`. 77 | 78 | DeploymentConfig parameters: 79 | * num_clones: Number of model clones to deploy in each replica. 80 | * clone_on_cpu: True if clones should be placed on CPU. 81 | * replica_id: Integer. Index of the replica for which the model is 82 | deployed. Usually 0 for the chief replica. 83 | * num_replicas: Number of replicas to use. 84 | * num_ps_tasks: Number of tasks for the `ps` job. 0 to not use replicas. 85 | * worker_job_name: A name for the worker job. 86 | * ps_job_name: A name for the parameter server job. 87 | 88 | TODO(sguada): 89 | - describe side effect to the graph. 90 | - what happens to summaries and update_ops. 91 | - which graph collections are altered. 92 | - write a tutorial on how to use this. 93 | - analyze the possibility of calling deploy more than once. 94 | 95 | 96 | """ 97 | 98 | from __future__ import absolute_import 99 | from __future__ import division 100 | from __future__ import print_function 101 | 102 | import collections 103 | 104 | import tensorflow as tf 105 | 106 | from tensorflow.python.ops import control_flow_ops 107 | 108 | slim = tf.contrib.slim 109 | 110 | 111 | __all__ = ['create_clones', 112 | 'deploy', 113 | 'optimize_clones', 114 | 'DeployedModel', 115 | 'DeploymentConfig', 116 | 'Clone', 117 | ] 118 | 119 | 120 | # Namedtuple used to represent a clone during deployment. 121 | Clone = collections.namedtuple('Clone', 122 | ['outputs', # Whatever model_fn() returned. 123 | 'scope', # The scope used to create it. 124 | 'device', # The device used to create. 125 | ]) 126 | 127 | # Namedtuple used to represent a DeployedModel, returned by deploy(). 128 | DeployedModel = collections.namedtuple('DeployedModel', 129 | ['train_op', # The `train_op` 130 | 'summary_op', # The `summary_op` 131 | 'total_loss', # The loss `Tensor` 132 | 'clones', # A list of `Clones` tuples. 133 | ]) 134 | 135 | # Default parameters for DeploymentConfig 136 | _deployment_params = {'num_clones': 1, 137 | 'clone_on_cpu': False, 138 | 'replica_id': 0, 139 | 'num_replicas': 1, 140 | 'num_ps_tasks': 0, 141 | 'worker_job_name': 'worker', 142 | 'ps_job_name': 'ps'} 143 | 144 | 145 | def create_clones(config, model_fn, args=None, kwargs=None): 146 | """Creates multiple clones according to config using a `model_fn`. 147 | 148 | The returned values of `model_fn(*args, **kwargs)` are collected along with 149 | the scope and device used to created it in a namedtuple 150 | `Clone(outputs, scope, device)` 151 | 152 | Note: it is assumed that any loss created by `model_fn` is collected at 153 | the tf.GraphKeys.LOSSES collection. 154 | 155 | To recover the losses, summaries or update_ops created by the clone use: 156 | ```python 157 | losses = tf.get_collection(tf.GraphKeys.LOSSES, clone.scope) 158 | summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, clone.scope) 159 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, clone.scope) 160 | ``` 161 | 162 | The deployment options are specified by the config object and support 163 | deploying one or several clones on different GPUs and one or several replicas 164 | of such clones. 165 | 166 | The argument `model_fn` is called `config.num_clones` times to create the 167 | model clones as `model_fn(*args, **kwargs)`. 168 | 169 | If `config` specifies deployment on multiple replicas then the default 170 | tensorflow device is set appropriatly for each call to `model_fn` and for the 171 | slim variable creation functions: model and global variables will be created 172 | on the `ps` device, the clone operations will be on the `worker` device. 173 | 174 | Args: 175 | config: A DeploymentConfig object. 176 | model_fn: A callable. Called as `model_fn(*args, **kwargs)` 177 | args: Optional list of arguments to pass to `model_fn`. 178 | kwargs: Optional list of keyword arguments to pass to `model_fn`. 179 | 180 | Returns: 181 | A list of namedtuples `Clone`. 182 | """ 183 | clones = [] 184 | args = args or [] 185 | kwargs = kwargs or {} 186 | with slim.arg_scope([slim.model_variable, slim.variable], 187 | device=config.variables_device()): 188 | # Create clones. 189 | for i in range(0, config.num_clones): 190 | with tf.name_scope(config.clone_scope(i)) as clone_scope: 191 | clone_device = config.clone_device(i) 192 | with tf.device(clone_device): 193 | with tf.variable_scope(tf.get_variable_scope(), 194 | reuse=True if i > 0 else None): 195 | outputs = model_fn(*args, **kwargs) 196 | clones.append(Clone(outputs, clone_scope, clone_device)) 197 | return clones 198 | 199 | 200 | def _gather_clone_loss(clone, num_clones, regularization_losses): 201 | """Gather the loss for a single clone. 202 | 203 | Args: 204 | clone: A Clone namedtuple. 205 | num_clones: The number of clones being deployed. 206 | regularization_losses: Possibly empty list of regularization_losses 207 | to add to the clone losses. 208 | 209 | Returns: 210 | A tensor for the total loss for the clone. Can be None. 211 | """ 212 | # The return value. 213 | sum_loss = None 214 | # Individual components of the loss that will need summaries. 215 | clone_loss = None 216 | regularization_loss = None 217 | # Compute and aggregate losses on the clone device. 218 | with tf.device(clone.device): 219 | all_losses = [] 220 | clone_losses = tf.get_collection(tf.GraphKeys.LOSSES, clone.scope) 221 | if clone_losses: 222 | clone_loss = tf.add_n(clone_losses, name='clone_loss') 223 | if num_clones > 1: 224 | clone_loss = tf.div(clone_loss, 1.0 * num_clones, 225 | name='scaled_clone_loss') 226 | all_losses.append(clone_loss) 227 | if regularization_losses: 228 | regularization_loss = tf.add_n(regularization_losses, 229 | name='regularization_loss') 230 | all_losses.append(regularization_loss) 231 | if all_losses: 232 | sum_loss = tf.add_n(all_losses) 233 | # Add the summaries out of the clone device block. 234 | if clone_loss is not None: 235 | tf.summary.scalar(clone.scope + '/clone_loss', clone_loss) 236 | if regularization_loss is not None: 237 | tf.summary.scalar('regularization_loss', regularization_loss) 238 | return sum_loss 239 | 240 | 241 | def _optimize_clone(optimizer, clone, num_clones, regularization_losses, 242 | **kwargs): 243 | """Compute losses and gradients for a single clone. 244 | 245 | Args: 246 | optimizer: A tf.Optimizer object. 247 | clone: A Clone namedtuple. 248 | num_clones: The number of clones being deployed. 249 | regularization_losses: Possibly empty list of regularization_losses 250 | to add to the clone losses. 251 | **kwargs: Dict of kwarg to pass to compute_gradients(). 252 | 253 | Returns: 254 | A tuple (clone_loss, clone_grads_and_vars). 255 | - clone_loss: A tensor for the total loss for the clone. Can be None. 256 | - clone_grads_and_vars: List of (gradient, variable) for the clone. 257 | Can be empty. 258 | """ 259 | sum_loss = _gather_clone_loss(clone, num_clones, regularization_losses) 260 | clone_grad = None 261 | if sum_loss is not None: 262 | with tf.device(clone.device): 263 | clone_grad = optimizer.compute_gradients(sum_loss, **kwargs) 264 | return sum_loss, clone_grad 265 | 266 | 267 | def optimize_clones(clones, optimizer, 268 | regularization_losses=None, 269 | **kwargs): 270 | """Compute clone losses and gradients for the given list of `Clones`. 271 | 272 | Note: The regularization_losses are added to the first clone losses. 273 | 274 | Args: 275 | clones: List of `Clones` created by `create_clones()`. 276 | optimizer: An `Optimizer` object. 277 | regularization_losses: Optional list of regularization losses. If None it 278 | will gather them from tf.GraphKeys.REGULARIZATION_LOSSES. Pass `[]` to 279 | exclude them. 280 | **kwargs: Optional list of keyword arguments to pass to `compute_gradients`. 281 | 282 | Returns: 283 | A tuple (total_loss, grads_and_vars). 284 | - total_loss: A Tensor containing the average of the clone losses including 285 | the regularization loss. 286 | - grads_and_vars: A List of tuples (gradient, variable) containing the sum 287 | of the gradients for each variable. 288 | 289 | """ 290 | grads_and_vars = [] 291 | clones_losses = [] 292 | num_clones = len(clones) 293 | if regularization_losses is None: 294 | regularization_losses = tf.get_collection( 295 | tf.GraphKeys.REGULARIZATION_LOSSES) 296 | for clone in clones: 297 | with tf.name_scope(clone.scope): 298 | clone_loss, clone_grad = _optimize_clone( 299 | optimizer, clone, num_clones, regularization_losses, **kwargs) 300 | if clone_loss is not None: 301 | clones_losses.append(clone_loss) 302 | grads_and_vars.append(clone_grad) 303 | # Only use regularization_losses for the first clone 304 | regularization_losses = None 305 | # Compute the total_loss summing all the clones_losses. 306 | total_loss = tf.add_n(clones_losses, name='total_loss') 307 | # Sum the gradients across clones. 308 | grads_and_vars = _sum_clones_gradients(grads_and_vars) 309 | return total_loss, grads_and_vars 310 | 311 | 312 | def deploy(config, 313 | model_fn, 314 | args=None, 315 | kwargs=None, 316 | optimizer=None, 317 | summarize_gradients=False): 318 | """Deploys a Slim-constructed model across multiple clones. 319 | 320 | The deployment options are specified by the config object and support 321 | deploying one or several clones on different GPUs and one or several replicas 322 | of such clones. 323 | 324 | The argument `model_fn` is called `config.num_clones` times to create the 325 | model clones as `model_fn(*args, **kwargs)`. 326 | 327 | The optional argument `optimizer` is an `Optimizer` object. If not `None`, 328 | the deployed model is configured for training with that optimizer. 329 | 330 | If `config` specifies deployment on multiple replicas then the default 331 | tensorflow device is set appropriatly for each call to `model_fn` and for the 332 | slim variable creation functions: model and global variables will be created 333 | on the `ps` device, the clone operations will be on the `worker` device. 334 | 335 | Args: 336 | config: A `DeploymentConfig` object. 337 | model_fn: A callable. Called as `model_fn(*args, **kwargs)` 338 | args: Optional list of arguments to pass to `model_fn`. 339 | kwargs: Optional list of keyword arguments to pass to `model_fn`. 340 | optimizer: Optional `Optimizer` object. If passed the model is deployed 341 | for training with that optimizer. 342 | summarize_gradients: Whether or not add summaries to the gradients. 343 | 344 | Returns: 345 | A `DeployedModel` namedtuple. 346 | 347 | """ 348 | # Gather initial summaries. 349 | summaries = set(tf.get_collection(tf.GraphKeys.SUMMARIES)) 350 | 351 | # Create Clones. 352 | clones = create_clones(config, model_fn, args, kwargs) 353 | first_clone = clones[0] 354 | 355 | # Gather update_ops from the first clone. These contain, for example, 356 | # the updates for the batch_norm variables created by model_fn. 357 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, first_clone.scope) 358 | 359 | train_op = None 360 | total_loss = None 361 | with tf.device(config.optimizer_device()): 362 | if optimizer: 363 | # Place the global step on the device storing the variables. 364 | with tf.device(config.variables_device()): 365 | global_step = slim.get_or_create_global_step() 366 | 367 | # Compute the gradients for the clones. 368 | total_loss, clones_gradients = optimize_clones(clones, optimizer) 369 | 370 | if clones_gradients: 371 | if summarize_gradients: 372 | # Add summaries to the gradients. 373 | summaries |= set(_add_gradients_summaries(clones_gradients)) 374 | 375 | # Create gradient updates. 376 | grad_updates = optimizer.apply_gradients(clones_gradients, 377 | global_step=global_step) 378 | update_ops.append(grad_updates) 379 | 380 | update_op = tf.group(*update_ops) 381 | train_op = control_flow_ops.with_dependencies([update_op], total_loss, 382 | name='train_op') 383 | else: 384 | clones_losses = [] 385 | regularization_losses = tf.get_collection( 386 | tf.GraphKeys.REGULARIZATION_LOSSES) 387 | for clone in clones: 388 | with tf.name_scope(clone.scope): 389 | clone_loss = _gather_clone_loss(clone, len(clones), 390 | regularization_losses) 391 | if clone_loss is not None: 392 | clones_losses.append(clone_loss) 393 | # Only use regularization_losses for the first clone 394 | regularization_losses = None 395 | if clones_losses: 396 | total_loss = tf.add_n(clones_losses, name='total_loss') 397 | 398 | # Add the summaries from the first clone. These contain the summaries 399 | # created by model_fn and either optimize_clones() or _gather_clone_loss(). 400 | summaries |= set(tf.get_collection(tf.GraphKeys.SUMMARIES, 401 | first_clone.scope)) 402 | 403 | if total_loss is not None: 404 | # Add total_loss to summary. 405 | summaries.add(tf.summary.scalar('total_loss', total_loss)) 406 | 407 | if summaries: 408 | # Merge all summaries together. 409 | summary_op = tf.summary.merge(list(summaries), name='summary_op') 410 | else: 411 | summary_op = None 412 | 413 | return DeployedModel(train_op, summary_op, total_loss, clones) 414 | 415 | 416 | def _sum_clones_gradients(clone_grads): 417 | """Calculate the sum gradient for each shared variable across all clones. 418 | 419 | This function assumes that the clone_grads has been scaled appropriately by 420 | 1 / num_clones. 421 | 422 | Args: 423 | clone_grads: A List of List of tuples (gradient, variable), one list per 424 | `Clone`. 425 | 426 | Returns: 427 | List of tuples of (gradient, variable) where the gradient has been summed 428 | across all clones. 429 | """ 430 | sum_grads = [] 431 | for grad_and_vars in zip(*clone_grads): 432 | # Note that each grad_and_vars looks like the following: 433 | # ((grad_var0_clone0, var0), ... (grad_varN_cloneN, varN)) 434 | grads = [] 435 | var = grad_and_vars[0][1] 436 | for g, v in grad_and_vars: 437 | assert v == var 438 | if g is not None: 439 | grads.append(g) 440 | if grads: 441 | if len(grads) > 1: 442 | sum_grad = tf.add_n(grads, name=var.op.name + '/sum_grads') 443 | else: 444 | sum_grad = grads[0] 445 | sum_grads.append((sum_grad, var)) 446 | return sum_grads 447 | 448 | 449 | def _add_gradients_summaries(grads_and_vars): 450 | """Add histogram summaries to gradients. 451 | 452 | Note: The summaries are also added to the SUMMARIES collection. 453 | 454 | Args: 455 | grads_and_vars: A list of gradient to variable pairs (tuples). 456 | 457 | Returns: 458 | The _list_ of the added summaries for grads_and_vars. 459 | """ 460 | summaries = [] 461 | for grad, var in grads_and_vars: 462 | if grad is not None: 463 | if isinstance(grad, tf.IndexedSlices): 464 | grad_values = grad.values 465 | else: 466 | grad_values = grad 467 | summaries.append(tf.summary.histogram(var.op.name + ':gradient', 468 | grad_values)) 469 | summaries.append(tf.summary.histogram(var.op.name + ':gradient_norm', 470 | tf.global_norm([grad_values]))) 471 | else: 472 | tf.logging.info('Var %s has no gradient', var.op.name) 473 | return summaries 474 | 475 | 476 | class DeploymentConfig(object): 477 | """Configuration for deploying a model with `deploy()`. 478 | 479 | You can pass an instance of this class to `deploy()` to specify exactly 480 | how to deploy the model to build. If you do not pass one, an instance built 481 | from the default deployment_hparams will be used. 482 | """ 483 | 484 | def __init__(self, 485 | num_clones=1, 486 | clone_on_cpu=False, 487 | replica_id=0, 488 | num_replicas=1, 489 | num_ps_tasks=0, 490 | worker_job_name='worker', 491 | ps_job_name='ps'): 492 | """Create a DeploymentConfig. 493 | 494 | The config describes how to deploy a model across multiple clones and 495 | replicas. The model will be replicated `num_clones` times in each replica. 496 | If `clone_on_cpu` is True, each clone will placed on CPU. 497 | 498 | If `num_replicas` is 1, the model is deployed via a single process. In that 499 | case `worker_device`, `num_ps_tasks`, and `ps_device` are ignored. 500 | 501 | If `num_replicas` is greater than 1, then `worker_device` and `ps_device` 502 | must specify TensorFlow devices for the `worker` and `ps` jobs and 503 | `num_ps_tasks` must be positive. 504 | 505 | Args: 506 | num_clones: Number of model clones to deploy in each replica. 507 | clone_on_cpu: If True clones would be placed on CPU. 508 | replica_id: Integer. Index of the replica for which the model is 509 | deployed. Usually 0 for the chief replica. 510 | num_replicas: Number of replicas to use. 511 | num_ps_tasks: Number of tasks for the `ps` job. 0 to not use replicas. 512 | worker_job_name: A name for the worker job. 513 | ps_job_name: A name for the parameter server job. 514 | 515 | Raises: 516 | ValueError: If the arguments are invalid. 517 | """ 518 | if num_replicas > 1: 519 | if num_ps_tasks < 1: 520 | raise ValueError('When using replicas num_ps_tasks must be positive') 521 | if num_replicas > 1 or num_ps_tasks > 0: 522 | if not worker_job_name: 523 | raise ValueError('Must specify worker_job_name when using replicas') 524 | if not ps_job_name: 525 | raise ValueError('Must specify ps_job_name when using parameter server') 526 | if replica_id >= num_replicas: 527 | raise ValueError('replica_id must be less than num_replicas') 528 | self._num_clones = num_clones 529 | self._clone_on_cpu = clone_on_cpu 530 | self._replica_id = replica_id 531 | self._num_replicas = num_replicas 532 | self._num_ps_tasks = num_ps_tasks 533 | self._ps_device = '/job:' + ps_job_name if num_ps_tasks > 0 else '' 534 | self._worker_device = '/job:' + worker_job_name if num_ps_tasks > 0 else '' 535 | 536 | @property 537 | def num_clones(self): 538 | return self._num_clones 539 | 540 | @property 541 | def clone_on_cpu(self): 542 | return self._clone_on_cpu 543 | 544 | @property 545 | def replica_id(self): 546 | return self._replica_id 547 | 548 | @property 549 | def num_replicas(self): 550 | return self._num_replicas 551 | 552 | @property 553 | def num_ps_tasks(self): 554 | return self._num_ps_tasks 555 | 556 | @property 557 | def ps_device(self): 558 | return self._ps_device 559 | 560 | @property 561 | def worker_device(self): 562 | return self._worker_device 563 | 564 | def caching_device(self): 565 | """Returns the device to use for caching variables. 566 | 567 | Variables are cached on the worker CPU when using replicas. 568 | 569 | Returns: 570 | A device string or None if the variables do not need to be cached. 571 | """ 572 | if self._num_ps_tasks > 0: 573 | return lambda op: op.device 574 | else: 575 | return None 576 | 577 | def clone_device(self, clone_index): 578 | """Device used to create the clone and all the ops inside the clone. 579 | 580 | Args: 581 | clone_index: Int, representing the clone_index. 582 | 583 | Returns: 584 | A value suitable for `tf.device()`. 585 | 586 | Raises: 587 | ValueError: if `clone_index` is greater or equal to the number of clones". 588 | """ 589 | if clone_index >= self._num_clones: 590 | raise ValueError('clone_index must be less than num_clones') 591 | device = '' 592 | if self._num_ps_tasks > 0: 593 | device += self._worker_device 594 | if self._clone_on_cpu: 595 | device += '/device:CPU:0' 596 | else: 597 | if self._num_clones > 1: 598 | device += '/device:GPU:%d' % clone_index 599 | return device 600 | 601 | def clone_scope(self, clone_index): 602 | """Name scope to create the clone. 603 | 604 | Args: 605 | clone_index: Int, representing the clone_index. 606 | 607 | Returns: 608 | A name_scope suitable for `tf.name_scope()`. 609 | 610 | Raises: 611 | ValueError: if `clone_index` is greater or equal to the number of clones". 612 | """ 613 | if clone_index >= self._num_clones: 614 | raise ValueError('clone_index must be less than num_clones') 615 | scope = '' 616 | if self._num_clones > 1: 617 | scope = 'clone_%d' % clone_index 618 | return scope 619 | 620 | def optimizer_device(self): 621 | """Device to use with the optimizer. 622 | 623 | Returns: 624 | A value suitable for `tf.device()`. 625 | """ 626 | if self._num_ps_tasks > 0 or self._num_clones > 0: 627 | return self._worker_device + '/device:CPU:0' 628 | else: 629 | return '' 630 | 631 | def inputs_device(self): 632 | """Device to use to build the inputs. 633 | 634 | Returns: 635 | A value suitable for `tf.device()`. 636 | """ 637 | device = '' 638 | if self._num_ps_tasks > 0: 639 | device += self._worker_device 640 | device += '/device:CPU:0' 641 | return device 642 | 643 | def variables_device(self): 644 | """Returns the device to use for variables created inside the clone. 645 | 646 | Returns: 647 | A value suitable for `tf.device()`. 648 | """ 649 | device = '' 650 | if self._num_ps_tasks > 0: 651 | device += self._ps_device 652 | device += '/device:CPU:0' 653 | 654 | class _PSDeviceChooser(object): 655 | """Slim device chooser for variables when using PS.""" 656 | 657 | def __init__(self, device, tasks): 658 | self._device = device 659 | self._tasks = tasks 660 | self._task = 0 661 | 662 | def choose(self, op): 663 | if op.device: 664 | return op.device 665 | node_def = op if isinstance(op, tf.NodeDef) else op.node_def 666 | if node_def.op == 'Variable': 667 | t = self._task 668 | self._task = (self._task + 1) % self._tasks 669 | d = '%s/task:%d' % (self._device, t) 670 | return d 671 | else: 672 | return op.device 673 | 674 | if not self._num_ps_tasks: 675 | return device 676 | else: 677 | chooser = _PSDeviceChooser(device, self._num_ps_tasks) 678 | return chooser.choose 679 | -------------------------------------------------------------------------------- /nets/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nets/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/nets/__init__.pyc -------------------------------------------------------------------------------- /nets/nets_factory.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Contains a factory for building various models.""" 16 | 17 | from __future__ import absolute_import 18 | from __future__ import division 19 | from __future__ import print_function 20 | import functools 21 | 22 | import tensorflow as tf 23 | 24 | from nets import resnet_v1 25 | 26 | 27 | slim = tf.contrib.slim 28 | 29 | networks_map = { 30 | 'resnet_v1_50': resnet_v1.resnet_v1_50, 31 | 'resnet_v1_distributions_50': resnet_v1.resnet_v1_distributions_50, 32 | 'resnet_v1_distributions_baseline_50': resnet_v1.resnet_v1_distributions_baseline_50, 33 | } 34 | 35 | arg_scopes_map = { 36 | 'resnet_v1_50': resnet_v1.resnet_arg_scope, 37 | 'resnet_v1_distributions_50': resnet_v1.resnet_arg_scope, 38 | 'resnet_v1_distributions_baseline_50': resnet_v1.resnet_arg_scope, 39 | } 40 | 41 | 42 | def get_network_fn(name, num_classes, weight_decay=0.0, is_training=False, sample_number=1): 43 | """Returns a network_fn such as `logits, end_points = network_fn(images)`. 44 | 45 | Args: 46 | name: The name of the network. 47 | num_classes: The number of classes to use for classification. 48 | weight_decay: The l2 coefficient for the model weights. 49 | is_training: `True` if the model is being used for training and `False` 50 | otherwise. 51 | 52 | Returns: 53 | network_fn: A function that applies the model to a batch of images. It has 54 | the following signature: 55 | logits, end_points = network_fn(images) 56 | Raises: 57 | ValueError: If network `name` is not recognized. 58 | """ 59 | if name not in networks_map: 60 | raise ValueError('Name of network unknown %s' % name) 61 | 62 | if name in arg_scopes_map: 63 | arg_scope = arg_scopes_map[name](weight_decay=weight_decay) 64 | else: 65 | raise ValueError('Name of network unknown %s' % name) 66 | 67 | func = networks_map[name] 68 | @functools.wraps(func) 69 | def network_fn1(images): 70 | with slim.arg_scope(arg_scope): 71 | return func(images, num_classes, is_training=is_training, sample_number=sample_number) 72 | if hasattr(func, 'default_image_size'): 73 | network_fn1.default_image_size = func.default_image_size 74 | def network_fn2(images, refs): 75 | with slim.arg_scope(arg_scope): 76 | return func(images, refs, num_classes, is_training=is_training, sample_number=sample_number) 77 | if hasattr(func, 'default_image_size'): 78 | network_fn2.default_image_size = func.default_image_size 79 | if 'clean' in name: 80 | return network_fn2 81 | else: 82 | return network_fn1 83 | -------------------------------------------------------------------------------- /nets/nets_factory.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/nets/nets_factory.pyc -------------------------------------------------------------------------------- /nets/resnet_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | """Contains building blocks for various versions of Residual Networks. 16 | 17 | Residual networks (ResNets) were proposed in: 18 | Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun 19 | Deep Residual Learning for Image Recognition. arXiv:1512.03385, 2015 20 | 21 | More variants were introduced in: 22 | Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun 23 | Identity Mappings in Deep Residual Networks. arXiv: 1603.05027, 2016 24 | 25 | We can obtain different ResNet variants by changing the network depth, width, 26 | and form of residual unit. This module implements the infrastructure for 27 | building them. Concrete ResNet units and full ResNet networks are implemented in 28 | the accompanying resnet_v1.py and resnet_v2.py modules. 29 | 30 | Compared to https://github.com/KaimingHe/deep-residual-networks, in the current 31 | implementation we subsample the output activations in the last residual unit of 32 | each block, instead of subsampling the input activations in the first residual 33 | unit of each block. The two implementations give identical results but our 34 | implementation is more memory efficient. 35 | """ 36 | from __future__ import absolute_import 37 | from __future__ import division 38 | from __future__ import print_function 39 | 40 | import re 41 | import collections 42 | import tensorflow as tf 43 | # import tensorflow.contrib.slim as slim 44 | slim = tf.contrib.slim 45 | 46 | import pdb 47 | 48 | 49 | class Block(collections.namedtuple('Block', ['scope', 'unit_fn', 'args'])): 50 | """A named tuple describing a ResNet block. 51 | 52 | Its parts are: 53 | scope: The scope of the `Block`. 54 | unit_fn: The ResNet unit function which takes as input a `Tensor` and 55 | returns another `Tensor` with the output of the ResNet unit. 56 | args: A list of length equal to the number of units in the `Block`. The list 57 | contains one (depth, depth_bottleneck, stride) tuple for each unit in the 58 | block to serve as argument to unit_fn. 59 | """ 60 | 61 | 62 | def subsample(inputs, factor, scope=None): 63 | """Subsamples the input along the spatial dimensions. 64 | 65 | Args: 66 | inputs: A `Tensor` of size [batch, height_in, width_in, channels]. 67 | factor: The subsampling factor. 68 | scope: Optional variable_scope. 69 | 70 | Returns: 71 | output: A `Tensor` of size [batch, height_out, width_out, channels] with the 72 | input, either intact (if factor == 1) or subsampled (if factor > 1). 73 | """ 74 | if factor == 1: 75 | return inputs 76 | else: 77 | return slim.max_pool2d(inputs, [1, 1], stride=factor, scope=scope) 78 | 79 | 80 | def conv2d_same(inputs, num_outputs, kernel_size, stride, rate=1, scope=None): 81 | """Strided 2-D convolution with 'SAME' padding. 82 | 83 | When stride > 1, then we do explicit zero-padding, followed by conv2d with 84 | 'VALID' padding. 85 | 86 | Note that 87 | 88 | net = conv2d_same(inputs, num_outputs, 3, stride=stride) 89 | 90 | is equivalent to 91 | 92 | net = slim.conv2d(inputs, num_outputs, 3, stride=1, padding='SAME') 93 | net = subsample(net, factor=stride) 94 | 95 | whereas 96 | 97 | net = slim.conv2d(inputs, num_outputs, 3, stride=stride, padding='SAME') 98 | 99 | is different when the input's height or width is even, which is why we add the 100 | current function. For more details, see ResnetUtilsTest.testConv2DSameEven(). 101 | 102 | Args: 103 | inputs: A 4-D tensor of size [batch, height_in, width_in, channels]. 104 | num_outputs: An integer, the number of output filters. 105 | kernel_size: An int with the kernel_size of the filters. 106 | stride: An integer, the output stride. 107 | rate: An integer, rate for atrous convolution. 108 | scope: Scope. 109 | 110 | Returns: 111 | output: A 4-D tensor of size [batch, height_out, width_out, channels] with 112 | the convolution output. 113 | """ 114 | if stride == 1: 115 | return slim.conv2d(inputs, num_outputs, kernel_size, stride=1, rate=rate, 116 | padding='SAME', scope=scope) 117 | else: 118 | kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1) 119 | pad_total = kernel_size_effective - 1 120 | pad_beg = pad_total // 2 121 | pad_end = pad_total - pad_beg 122 | inputs = tf.pad(inputs, 123 | [[0, 0], [pad_beg, pad_end], [pad_beg, pad_end], [0, 0]]) 124 | return slim.conv2d(inputs, num_outputs, kernel_size, stride=stride, 125 | rate=rate, padding='VALID', scope=scope) 126 | 127 | 128 | @slim.add_arg_scope 129 | def extra_fc(in_net, extra_fc_out_dim, extra_fc_W_decay, extra_fc_type, f_decorr_fr, f_decorr_decay, outputs_collections=None, 130 | loss_collection=tf.GraphKeys.REGULARIZATION_LOSSES): 131 | """ 132 | input 4 dimension, output 4 dimension 133 | fc using 2 dimension, weights only no bias 134 | :param in_net: 4 dim? squeeze 135 | :param extra_fc_out_dim: >0 136 | :param extra_fc_W_decay: >0 137 | :return: 138 | 4 dim, extending it at last 139 | """ 140 | assert extra_fc_out_dim > 0 141 | assert extra_fc_W_decay > 0 142 | assert extra_fc_type >= 0 143 | assert len(in_net.get_shape().as_list()) == 4 144 | 145 | # Global average pooling as input; dimension is kept, input as pre_pool5 146 | net = tf.reduce_mean(in_net, [1, 2], name='pre_pool5', keep_dims=True) 147 | pre_pool5 = net 148 | net = tf.squeeze(net, [1, 2]) 149 | assert len(net.get_shape().as_list()) == 2 150 | 151 | # extra fc layer 152 | net = fc_layer(net, extra_fc_out_dim, extra_fc_W_decay, extra_fc_type, f_decorr_fr, f_decorr_decay, 153 | outputs_collections, loss_collection, name='final_extra_fc') 154 | assert len(net.get_shape().as_list()) == 2 155 | 156 | # output as pool5 157 | net = tf.expand_dims(tf.expand_dims(net, axis=1), axis=1, name='pool5') 158 | assert len(net.get_shape().as_list()) == 4 159 | return net, pre_pool5 160 | 161 | 162 | def fc_layer(in_net, extra_fc_out_dim, extra_fc_W_decay, extra_fc_type, f_decorr_fr, f_decorr_decay, 163 | outputs_collections, loss_collection, name='extra_fc'): 164 | """ 165 | no bias 166 | :param in_net: 2 dim 167 | :param extra_fc_out_dim: 168 | :param extra_fc_W_decay: 169 | :param extra_fc_type: 170 | 0. normal extra fc layer 171 | 1. soft whitening reg ||W'W-I||_{F}^{2} 172 | 2. soft W decorrelation (L1 loss) 173 | 3. std W then soft decorrelation (L1 loss) 174 | :param outputs_collections: 175 | :param loss_collection: 176 | :param name: 177 | :return: 178 | """ 179 | in_dim = in_net.get_shape().as_list()[-1] 180 | with tf.variable_scope(name) as sc: 181 | if extra_fc_type == 0: 182 | # normal extra fc layer 183 | net = slim.fully_connected(in_net, 184 | extra_fc_out_dim, 185 | activation_fn=tf.nn.relu, 186 | normalizer_fn=slim.batch_norm, 187 | weights_regularizer = slim.l2_regularizer(extra_fc_W_decay), 188 | weights_initializer = slim.xavier_initializer(), 189 | biases_initializer=None) 190 | else: 191 | # no need weights_regularizer 192 | net = slim.fully_connected(in_net, 193 | extra_fc_out_dim, 194 | activation_fn=tf.nn.relu, 195 | normalizer_fn=slim.batch_norm, 196 | weights_regularizer=None, 197 | weights_initializer=slim.xavier_initializer(), 198 | biases_initializer=None) 199 | 200 | # scope = sc.original_name_scope 201 | # another regularisation decay needed??? for feature reg 202 | net_decorr_reg(net, f_decorr_fr, f_decorr_decay, loss_collection) 203 | weights_list = get_weights(sc.original_name_scope) 204 | assert len(weights_list) == 1 205 | biases_list = get_biases(sc.original_name_scope) 206 | assert len(biases_list) == 0 207 | weight_decorr_whiten_reg(extra_fc_type, weights_list, in_dim, extra_fc_out_dim, extra_fc_W_decay, loss_collection) 208 | 209 | 210 | # checking 211 | weights_list = get_weights(sc.original_name_scope) 212 | assert len(weights_list) == 1 213 | biases_list = get_biases(sc.original_name_scope) 214 | assert len(biases_list) == 0 215 | # pdb.set_trace() 216 | assert net.get_shape().as_list()[-1] == extra_fc_out_dim 217 | return slim.utils.collect_named_outputs(outputs_collections, 218 | sc.original_name_scope, 219 | net) 220 | 221 | def net_decorr_reg(in_net, f_decorr_fr, f_decorr_decay, loss_collection): 222 | # f_decorr_fr: for the accumulated covariance term. 223 | # in_net: dim 2 224 | # averaging the sample num (not yet) 225 | assert len(in_net.get_shape().as_list()) == 2 226 | in_dim = in_net.get_shape().as_list()[-1] 227 | 228 | if f_decorr_fr < 0.: 229 | print('no need feat decorrelation.') 230 | elif f_decorr_fr > 1: 231 | raise ValueError('f_decorr_fr should not > 1.') 232 | else: # 0= 0. 234 | accum_cov_mat = tf.Variable(initial_value=tf.zeros([in_dim, in_dim]), name="accum_cov_mat") 235 | accum_aver_num = tf.Variable(initial_value=0.0, name="accum_aver_num") 236 | # averaged 237 | tmp_loss = moving_average_cov_mat(in_net, accum_cov_mat, accum_aver_num, f_decorr_fr) 238 | loss = tf.multiply(tf.constant(f_decorr_decay, dtype=tf.float32), tmp_loss) 239 | tf.add_to_collection(loss_collection, loss) 240 | 241 | def moving_average_cov_mat(in_net, accum_cov_mat, accum_aver_num, f_decorr_fr): 242 | # moving averaging feature covariance mat 243 | # std the input first 244 | assert len(in_net.get_shape().as_list()) == 2 245 | in_dim = in_net.get_shape().as_list()[-1] 246 | N = in_net.get_shape().as_list()[0] 247 | assert N > 1 248 | mean, var = tf.nn.moments(in_net, [0], keep_dims=True) 249 | assert len(mean.get_shape().as_list()) == len(var.get_shape().as_list()) == 2 250 | assert mean.get_shape().as_list()[1] == var.get_shape().as_list()[1] == in_dim 251 | in_std_tensor = tf.div(tf.subtract(in_net, mean), tf.add(tf.sqrt(var), tf.constant(0.000001, dtype=tf.float32))) 252 | 253 | # accumulate cov mat 254 | fr_tensor = tf.constant(f_decorr_fr, dtype=tf.float32) 255 | #averaging the sample num 256 | cov_mat = tf.div( tf.matmul(in_std_tensor, in_std_tensor, transpose_a=True), tf.constant(N-1, dtype=tf.float32) ) 257 | tmp_accum_cov_mat = tf.add(tf.multiply(fr_tensor, accum_cov_mat), cov_mat) 258 | # accumulate num 259 | tmp_accum_aver_num = tf.add(tf.multiply(fr_tensor, accum_aver_num), tf.constant(1.0, dtype=tf.float32)) 260 | # averageing 261 | # aver_accum_cov_mat = tf.div(accum_cov_mat, accum_aver_num) 262 | # absolute for loss computation 263 | abs_aver_accum_cov_mat = tf.abs(tf.div(tmp_accum_cov_mat, tmp_accum_aver_num)) 264 | assert len(abs_aver_accum_cov_mat.get_shape().as_list()) == 2 265 | assert abs_aver_accum_cov_mat.get_shape().as_list()[0] == abs_aver_accum_cov_mat.get_shape().as_list()[1] == in_dim 266 | # update through assignment 267 | accum_cov_mat.assign(tmp_accum_cov_mat) 268 | accum_aver_num.assign(tmp_accum_aver_num) 269 | 270 | 271 | tmp_loss = tf.subtract(tf.reduce_sum(abs_aver_accum_cov_mat, [0, 1]), tf.trace(abs_aver_accum_cov_mat)) 272 | # averaging the dim num: in_dim*(in_dim-1) 273 | loss = tf.div(tmp_loss, tf.constant(in_dim*(in_dim-1), dtype=tf.float32)) 274 | return loss 275 | 276 | 277 | def get_weights(name): 278 | # pdb.set_trace() 279 | return [v for v in tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, name) if v.name.endswith('weights:0')] 280 | 281 | def get_biases(name): 282 | # pdb.set_trace() 283 | return [v for v in tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, name) if v.name.endswith('bias:0')] 284 | 285 | def weight_decorr_whiten_reg(extra_fc_type, weights_list, in_dim, out_dim, extra_fc_W_decay, loss_collection): 286 | """ 287 | :param extra_fc_type: 288 | 1. soft whitening reg ||W'W-I||_{F}^{2} 289 | 2. soft W decorrelation (L1 loss) 290 | 3. std W then soft decorrelation (L1 loss) 291 | :param weights_list: 292 | :param in_dim: 293 | :param extra_fc_out_dim: 294 | :param extra_fc_W_decay: 295 | :param loss_collection: 296 | :return: None 297 | """ 298 | 299 | #get the weight and check size 300 | W_tensor = weights_list[0] 301 | # pdb.set_trace() 302 | assert len(W_tensor.get_shape().as_list()) == 2 303 | assert W_tensor.get_shape().as_list()[0] == in_dim 304 | assert W_tensor.get_shape().as_list()[1] == out_dim 305 | assert in_dim >= out_dim 306 | assert in_dim > 1 307 | 308 | if extra_fc_type == 1: 309 | # soft whitening reg ||W'W-I||_{F}^{2} 310 | I_tensor = tf.eye(out_dim) 311 | # averging sample num 312 | Cov_tensor = tf.div(tf.matmul(W_tensor, W_tensor, transpose_a=True), tf.constant(in_dim-1, dtype=tf.float32)) 313 | diff_tensor = tf.subtract(Cov_tensor, I_tensor) 314 | assert len(diff_tensor.get_shape().as_list()) == 2 315 | assert diff_tensor.get_shape().as_list()[0] == diff_tensor.get_shape().as_list()[1] == out_dim 316 | # reduce_mean: averging the elements 317 | tmp_loss = tf.multiply(tf.constant(0.5, dtype=tf.float32), tf.reduce_mean(tf.multiply(diff_tensor, diff_tensor))) 318 | elif extra_fc_type == 2: 319 | # soft W decorrelation (L1 loss) 320 | # averging sample num 321 | Cov_tensor = tf.div(tf.matmul(W_tensor, W_tensor, transpose_a=True), tf.constant(in_dim - 1, dtype=tf.float32)) 322 | Corr_abs_tensor = tf.abs(Cov_tensor) 323 | assert len(Corr_abs_tensor.get_shape().as_list()) == 2 324 | assert Corr_abs_tensor.get_shape().as_list()[0] == Corr_abs_tensor.get_shape().as_list()[1] == out_dim 325 | # averging the elements 326 | tmp_loss = tf.div( tf.subtract(tf.reduce_sum(Corr_abs_tensor), tf.trace(Corr_abs_tensor)), 327 | tf.constant(out_dim * (out_dim-1), dtype=tf.float32) ) 328 | elif extra_fc_type == 3: 329 | # std W then soft decorrelation (L1 loss) 330 | mean, var = tf.nn.moments(W_tensor, [0], keep_dims=True) 331 | assert len(mean.get_shape().as_list()) == len(var.get_shape().as_list()) == 2 332 | assert mean.get_shape().as_list()[1] == var.get_shape().as_list()[1] == out_dim 333 | W_std_tensor = tf.div(tf.subtract(W_tensor, mean), tf.add(tf.sqrt(var), tf.constant(0.000001, dtype=tf.float32))) 334 | # averging sample num 335 | Cov_tensor = tf.div(tf.matmul(W_std_tensor, W_std_tensor, transpose_a=True), tf.constant(in_dim - 1, dtype=tf.float32)) 336 | Corr_abs_tensor = tf.abs(Cov_tensor) 337 | assert len(Corr_abs_tensor.get_shape().as_list()) == 2 338 | assert Corr_abs_tensor.get_shape().as_list()[0] == Corr_abs_tensor.get_shape().as_list()[1] == out_dim 339 | # averging the elements 340 | tmp_loss = tf.div(tf.subtract(tf.reduce_sum(Corr_abs_tensor), tf.trace(Corr_abs_tensor)), 341 | tf.constant(out_dim * (out_dim - 1), dtype=tf.float32)) 342 | else: 343 | raise ValueError('Unrecognized extra_fc_type: {}'.format(extra_fc_type)) 344 | 345 | loss = tf.multiply(tf.constant(extra_fc_W_decay, dtype=tf.float32), tmp_loss) 346 | tf.add_to_collection(loss_collection, loss) 347 | 348 | @slim.add_arg_scope 349 | def stack_blocks_dense(net, blocks, output_stride=None, 350 | outputs_collections=None): 351 | """Stacks ResNet `Blocks` and controls output feature density. 352 | 353 | First, this function creates scopes for the ResNet in the form of 354 | 'block_name/unit_1', 'block_name/unit_2', etc. 355 | 356 | Second, this function allows the user to explicitly control the ResNet 357 | output_stride, which is the ratio of the input to output spatial resolution. 358 | This is useful for dense prediction tasks such as semantic segmentation or 359 | object detection. 360 | 361 | Most ResNets consist of 4 ResNet blocks and subsample the activations by a 362 | factor of 2 when transitioning between consecutive ResNet blocks. This results 363 | to a nominal ResNet output_stride equal to 8. If we set the output_stride to 364 | half the nominal network stride (e.g., output_stride=4), then we compute 365 | responses twice. 366 | 367 | Control of the output feature density is implemented by atrous convolution. 368 | 369 | Args: 370 | net: A `Tensor` of size [batch, height, width, channels]. 371 | blocks: A list of length equal to the number of ResNet `Blocks`. Each 372 | element is a ResNet `Block` object describing the units in the `Block`. 373 | output_stride: If `None`, then the output will be computed at the nominal 374 | network stride. If output_stride is not `None`, it specifies the requested 375 | ratio of input to output spatial resolution, which needs to be equal to 376 | the product of unit strides from the start up to some level of the ResNet. 377 | For example, if the ResNet employs units with strides 1, 2, 1, 3, 4, 1, 378 | then valid values for the output_stride are 1, 2, 6, 24 or None (which 379 | is equivalent to output_stride=24). 380 | outputs_collections: Collection to add the ResNet block outputs. 381 | 382 | Returns: 383 | net: Output tensor with stride equal to the specified output_stride. 384 | 385 | Raises: 386 | ValueError: If the target output_stride is not valid. 387 | """ 388 | # The current_stride variable keeps track of the effective stride of the 389 | # activations. This allows us to invoke atrous convolution whenever applying 390 | # the next residual unit would result in the activations having stride larger 391 | # than the target output_stride. 392 | current_stride = 1 393 | 394 | # The atrous convolution rate parameter. 395 | rate = 1 396 | 397 | for block in blocks: 398 | with tf.variable_scope(block.scope, 'block', [net]) as sc: 399 | for i, unit in enumerate(block.args): 400 | if output_stride is not None and current_stride > output_stride: 401 | raise ValueError('The target output_stride cannot be reached.') 402 | 403 | with tf.variable_scope('unit_%d' % (i + 1), values=[net]): 404 | unit_depth, unit_depth_bottleneck, unit_stride = unit 405 | 406 | # If we have reached the target output_stride, then we need to employ 407 | # atrous convolution with stride=1 and multiply the atrous rate by the 408 | # current unit's stride for use in subsequent layers. 409 | if output_stride is not None and current_stride == output_stride: 410 | net = block.unit_fn(net, 411 | depth=unit_depth, 412 | depth_bottleneck=unit_depth_bottleneck, 413 | stride=1, 414 | rate=rate) 415 | rate *= unit_stride 416 | 417 | else: 418 | net = block.unit_fn(net, 419 | depth=unit_depth, 420 | depth_bottleneck=unit_depth_bottleneck, 421 | stride=unit_stride, 422 | rate=1) 423 | current_stride *= unit_stride 424 | net = slim.utils.collect_named_outputs(outputs_collections, sc.name, net) 425 | 426 | if output_stride is not None and current_stride != output_stride: 427 | raise ValueError('The target output_stride cannot be reached.') 428 | 429 | return net 430 | 431 | 432 | # ex-final feats project and sum with final feat 433 | @slim.add_arg_scope 434 | def projecting_feats(net, gate_proj_type, proj_dim, gate_prop_down, is_training, avr_cc=0, outputs_collections=None): 435 | """ 436 | projecting feats and/or deep (fc) features to the same dimension and add them up 437 | :param net: [batch_size, 1, 1, channel_num] 438 | :param gate_proj_type: 439 | 0: projecting gates to dim of deep feats 440 | 1: Projecting gates and deep feats to same dimension (proj_dim) 441 | :param proj_dim: joint dimension 442 | :param gate_prop_down: whether propagate down on gating activations 443 | :param outputs_collections: 444 | :return: 445 | averaged features 446 | """ 447 | 448 | local_sc_name = 'pool5_feats_proj_add' 449 | 450 | # retrievaling gates using endpoints 451 | tmp_end_points = slim.utils.convert_collection_to_dict(outputs_collections) 452 | # pdb.set_trace() 453 | with tf.variable_scope(local_sc_name) as sc: 454 | net_add_feats = proj_feats_fn(net, gate_proj_type, proj_dim, gate_prop_down, tmp_end_points, avr_cc, is_training) 455 | return slim.utils.collect_named_outputs(outputs_collections, 456 | sc.original_name_scope, 457 | net_add_feats) 458 | 459 | 460 | def proj_feats_fn(net, gate_proj_type, proj_dim, gate_prop_down, tmp_end_points, avr_cc, is_training): 461 | # all output feats from different blocks are used 462 | print('Using gates from all blocks.') 463 | gate_feat_len = 32 464 | block_unit_pat_str = 'block[0-9]+?/unit_[0-9]+?' 465 | # sigmoid gates 466 | select_pat_str = r'resnet_v[1-2]_[0-9]+?/{}/bottleneck_v[1-2]$'.format(block_unit_pat_str) 467 | 468 | # check size: [batch_size, 1, 1, channel_num] 469 | assert len(net.get_shape().as_list()) == 4 470 | assert net.get_shape().as_list()[1] == 1 471 | assert net.get_shape().as_list()[2] == 1 472 | feat_channle_num = net.get_shape().as_list()[3] 473 | if gate_proj_type == 0: 474 | # deep feature not project, gate project to deep 475 | proj_dim = feat_channle_num 476 | elif gate_proj_type == 1: 477 | # both deep feats and gates feats project to proj_dim 478 | print('projecting deep feats to {}.'.format(proj_dim)) 479 | net = proj_2Din4_bn_relu(net, proj_dim, sc_name='df_proj_layer') 480 | else: 481 | raise ValueError('Unrecognised projection type: {}'.format(gate_proj_type)) 482 | assert len(net.get_shape().as_list()) == 4 483 | assert net.get_shape().as_list()[1] == 1 484 | assert net.get_shape().as_list()[2] == 1 485 | assert net.get_shape().as_list()[3] == proj_dim 486 | 487 | print('fetching the mid-level feats.') 488 | end_point_key_list = tmp_end_points.keys() 489 | selected_keys = [] 490 | for _key in end_point_key_list: 491 | selected_list = re.findall(select_pat_str, _key) 492 | if len(selected_list) == 1: 493 | selected_keys.append(selected_list[0]) 494 | 495 | feat_tensor_list = [] 496 | # pdb.set_trace() 497 | ex_final_selected_keys = sorted(selected_keys)[:-1] 498 | assert len(selected_keys)-1 == len(ex_final_selected_keys) 499 | concate_len_check = 0 500 | for gate_key in sorted(ex_final_selected_keys): 501 | # reduce mean on spatial dimensions: axis = [1,2] 502 | feat_WH_tensor = tmp_end_points[gate_key] 503 | assert len(feat_WH_tensor.get_shape().as_list()) == 4 504 | tmp_f_dim = feat_WH_tensor.get_shape().as_list()[3] 505 | # mean over spatial 506 | feat_mean_tensor = tf.reduce_mean(feat_WH_tensor, [1, 2]) 507 | assert len(feat_mean_tensor.get_shape().as_list()) == 2 508 | assert feat_mean_tensor.get_shape().as_list()[1] == tmp_f_dim 509 | concate_len_check += tmp_f_dim 510 | # pdb.set_trace() 511 | feat_tensor_list.append(feat_mean_tensor) 512 | 513 | tmp_mid_feats_tensor = tf.concat(feat_tensor_list, axis=1) 514 | # pdb.set_trace() 515 | # if concat_gate_sreg and is_training: 516 | # gate_reg_loss(concat_gate_reg_type, tmp_gate_feat_tensor, 1.0) 517 | if gate_prop_down: 518 | mid_feats_tensor = tf.expand_dims(tf.expand_dims(tmp_mid_feats_tensor, axis=1), axis=1) 519 | else: # no propagate down for gate tensor 520 | mid_feats_tensor = tf.stop_gradient(tf.expand_dims(tf.expand_dims(tmp_mid_feats_tensor, axis=1), axis=1)) 521 | # check size 522 | assert len(mid_feats_tensor.get_shape().as_list()) == 4 523 | mid_feats_len = mid_feats_tensor.get_shape().as_list()[3] 524 | assert mid_feats_len == concate_len_check 525 | # assert float(gates_feat_len)/gate_feat_len == int(gates_feat_len/gate_feat_len) 526 | 527 | print('projecting ex-final concate feats') 528 | 529 | ex_final_feat_tensor = proj_2Din4_bn_relu(mid_feats_tensor, proj_dim, sc_name='gate_proj_layer') 530 | assert len(ex_final_feat_tensor.get_shape().as_list()) == 4 531 | assert ex_final_feat_tensor.get_shape().as_list()[1] == 1 532 | assert ex_final_feat_tensor.get_shape().as_list()[2] == 1 533 | assert ex_final_feat_tensor.get_shape().as_list()[3] == proj_dim 534 | 535 | if avr_cc == 0: 536 | print('adding up') 537 | out_feat_tensor = tf.multiply(tf.add(net, ex_final_feat_tensor), tf.constant(0.5)) 538 | assert len(out_feat_tensor.get_shape().as_list()) == 4 539 | assert out_feat_tensor.get_shape().as_list()[1] == 1 540 | assert out_feat_tensor.get_shape().as_list()[2] == 1 541 | assert out_feat_tensor.get_shape().as_list()[3] == proj_dim 542 | elif avr_cc == 1: 543 | print('Concatenating') 544 | out_feat_tensor = tf.concat([net, ex_final_feat_tensor], 3) 545 | assert len(out_feat_tensor.get_shape().as_list()) == 4 546 | assert out_feat_tensor.get_shape().as_list()[1] == 1 547 | assert out_feat_tensor.get_shape().as_list()[2] == 1 548 | assert out_feat_tensor.get_shape().as_list()[3] == proj_dim * 2 549 | else: 550 | raise ValueError('Undefined fusion method: {}'.format(avr_cc)) 551 | return out_feat_tensor 552 | 553 | 554 | 555 | def proj_2Din4_bn_relu(in_2Din4_tensor, proj_dim, sc_name=None, gate_net_weight_decay=0.0001): 556 | # 4D to 2D tensor 557 | in_2Din4_tensor = tf.squeeze(in_2Din4_tensor) 558 | assert len(in_2Din4_tensor.get_shape().as_list()) == 2 559 | 560 | with tf.variable_scope(sc_name, 'proj_layer') as sc: 561 | with slim.arg_scope([slim.fully_connected], 562 | weights_regularizer=slim.l2_regularizer(gate_net_weight_decay), 563 | weights_initializer=slim.xavier_initializer(), 564 | activation_fn=tf.nn.relu, 565 | normalizer_fn=slim.batch_norm): 566 | 567 | out_tensor = slim.fully_connected(in_2Din4_tensor, proj_dim) 568 | 569 | assert len(out_tensor.get_shape().as_list()) == 2 570 | assert out_tensor.get_shape().as_list()[1] == proj_dim 571 | 572 | # return 4D tensor 573 | return tf.expand_dims(tf.expand_dims(out_tensor, axis=1), axis=1) 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | def resnet_arg_scope(weight_decay=0.0001, 582 | batch_norm_decay=0.997, 583 | batch_norm_epsilon=1e-5, 584 | batch_norm_scale=True): 585 | """Defines the default ResNet arg scope. 586 | 587 | TODO(gpapan): The batch-normalization related default values above are 588 | appropriate for use in conjunction with the reference ResNet models 589 | released at https://github.com/KaimingHe/deep-residual-networks. When 590 | training ResNets from scratch, they might need to be tuned. 591 | 592 | Args: 593 | weight_decay: The weight decay to use for regularizing the model. 594 | batch_norm_decay: The moving average decay when estimating layer activation 595 | statistics in batch normalization. 596 | batch_norm_epsilon: Small constant to prevent division by zero when 597 | normalizing activations by their variance in batch normalization. 598 | batch_norm_scale: If True, uses an explicit `gamma` multiplier to scale the 599 | activations in the batch normalization layer. 600 | 601 | Returns: 602 | An `arg_scope` to use for the resnet models. 603 | """ 604 | batch_norm_params = { 605 | 'decay': batch_norm_decay, 606 | 'epsilon': batch_norm_epsilon, 607 | 'scale': batch_norm_scale, 608 | 'updates_collections': tf.GraphKeys.UPDATE_OPS, 609 | } 610 | 611 | with slim.arg_scope( 612 | [slim.conv2d], 613 | weights_regularizer=slim.l2_regularizer(weight_decay), 614 | weights_initializer=slim.variance_scaling_initializer(), 615 | activation_fn=tf.nn.relu, 616 | normalizer_fn=slim.batch_norm, 617 | normalizer_params=batch_norm_params): 618 | with slim.arg_scope([slim.batch_norm], **batch_norm_params): 619 | # The following implies padding='SAME' for pool1, which makes feature 620 | # alignment easier for dense prediction tasks. This is also used in 621 | # https://github.com/facebook/fb.resnet.torch. However the accompanying 622 | # code of 'Deep Residual Learning for Image Recognition' uses 623 | # padding='VALID' for pool1. You can switch to that choice by setting 624 | # slim.arg_scope([slim.max_pool2d], padding='VALID'). 625 | with slim.arg_scope([slim.max_pool2d], padding='SAME') as arg_sc: 626 | return arg_sc 627 | -------------------------------------------------------------------------------- /nets/resnet_utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/nets/resnet_utils.pyc -------------------------------------------------------------------------------- /nets/resnet_v1.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyuanYu/DistributionNet/bce7161345cbe9420452ff7d2a37bda3063fce59/nets/resnet_v1.pyc -------------------------------------------------------------------------------- /pipeline.sh: -------------------------------------------------------------------------------- 1 | python train_ReID_classifier_from_resnet50.py --set bounding_box_train_0.1_0 --standard True 2 | 3 | python train_ReID_classifier_con.py --max_number_of_steps 20200 --target market --dataset_name Market --set bounding_box_train_0.1_0 --checkpoint_path2 ./result/resnet_v1_50_targetmarket_noise_0.1_0_standard 4 | 5 | python train_ReID_classifier.py --entropy_loss True --max_number_of_steps 20200 --target market --dataset_name Market --set bounding_box_train_0.1_0 --checkpoint_path2 ./result/resnet_v1_50_targetmarket_noise_0.1_0_standard 6 | 7 | python eval_market_multi.py 8 | -------------------------------------------------------------------------------- /train_ReID_classifier.py: -------------------------------------------------------------------------------- 1 | # this is a modification based on the train_image_classifier.py 2 | # ============================================================================== 3 | """Generic training script that trains a ReID model using a given dataset.""" 4 | 5 | ####################################### 6 | ## This file is to train DistributionNet based on Resnet-50 pretrained by Market-1501 7 | # Note: This is step 2 of our method. The model is Resnet-distribution-50, and loss is Xent of mean and samples drawn from distributions. The optional losses include entropy loss and two versions of across instance losses 8 | ######################################## 9 | 10 | 11 | from __future__ import absolute_import 12 | from __future__ import division 13 | from __future__ import print_function 14 | 15 | import tensorflow as tf 16 | 17 | import os 18 | import time 19 | 20 | from tensorflow.python.ops import control_flow_ops 21 | import dataset_factory 22 | import model_deploy 23 | from nets import nets_factory 24 | 25 | import tensorflow.contrib.slim as slim 26 | 27 | from utils import config_and_print_log, _configure_learning_rate, _configure_optimizer, _get_variables_to_train, _get_init_fn, get_img_func, build_graph, get_pair_type 28 | 29 | 30 | ############## 31 | # FLags may be usually changed # 32 | ############## 33 | 34 | tf.app.flags.DEFINE_string('model_name', 'resnet_v1_distributions_50', 'The name of the architecture to train.') 35 | 36 | tf.app.flags.DEFINE_boolean('entropy_loss', False, 'uncertainty loss') 37 | tf.app.flags.DEFINE_integer('max_number_of_steps', 60200, 'The maximum number of training steps.') 38 | tf.app.flags.DEFINE_string('target', 'market', 'For name of model') 39 | tf.app.flags.DEFINE_boolean('standard', False, 'For name of model') 40 | 41 | tf.app.flags.DEFINE_string('set', 'bounding_box_train', "subset under current dataset") 42 | tf.app.flags.DEFINE_integer('boot_weight', 1.0, 'cross entropy loss weight') 43 | 44 | tf.app.flags.DEFINE_float('sampled_ce_loss_weight', 0.1, 'loss weight for xent of drawn samples.') 45 | tf.app.flags.DEFINE_integer('sample_number', 1, 'the number of samples drawn from distribution.') 46 | 47 | tf.app.flags.DEFINE_boolean('resume_train', True, 'when set to true, resume training from current train dir or load dir.') 48 | 49 | tf.app.flags.DEFINE_string('dataset_name', 'Market', 'The name of the Person ReID dataset to load.') 50 | 51 | tf.app.flags.DEFINE_string('dataset_dir', './Market/', 52 | 'The directory where the dataset files are stored.') 53 | 54 | tf.app.flags.DEFINE_string('checkpoint_path2', '/import/vision-ephemeral/ty303/result/resnet_v1_50_emdNone_targetmarket_standard', 55 | 'The path to a checkpoint from which to fine-tune.') 56 | 57 | 58 | tf.app.flags.DEFINE_float('learning_rate', 0.00005, 'Initial learning rate.') 59 | tf.app.flags.DEFINE_float('end_learning_rate', 0.00001, 'The minimal end learning rate used by a polynomial decay learning rate.') 60 | 61 | tf.app.flags.DEFINE_string('trainable_scopes', 'resnet_v1_50/Distributions, resnet_v1_50/logits, resnet_v1_50/block4/', 'Comma-separated list of scopes to filter the set of variables to train. By default, None would train all the variables.') 62 | 63 | tf.app.flags.DEFINE_string('checkpoint_exclude_scopes', ['Distributions'], 'Comma-separated list of scopes of variables to exclude when restoring from a checkpoint.') 64 | 65 | 66 | ##################### 67 | ###The following flags are fixed all the time 68 | ##################### 69 | 70 | tf.app.flags.DEFINE_boolean('use_clf', True, 'Add classification (identification) loss to the network.') 71 | tf.app.flags.DEFINE_string('master', '', 'The address of the TensorFlow master to use.') 72 | tf.app.flags.DEFINE_string('train_dir', './result', 73 | 'Directory where checkpoints and event logs are written to.') 74 | tf.app.flags.DEFINE_string('sub_dir', '', 'Subdirectory to identify the sv dir') 75 | tf.app.flags.DEFINE_integer('num_clones', 1, 'Number of model clones to deploy.') 76 | tf.app.flags.DEFINE_boolean('clone_on_cpu', False, 'Use CPUs to deploy clones.') 77 | tf.app.flags.DEFINE_integer('worker_replicas', 1, 'Number of worker replicas.') 78 | tf.app.flags.DEFINE_integer('num_ps_tasks', 0, 'The number of parameter servers. If the value is 0, then the ' 79 | 'parameters are handled locally by the worker.') 80 | 81 | # readers number 82 | tf.app.flags.DEFINE_integer('num_readers', 4, 'The number of parallel readers that read data from the dataset.') 83 | tf.app.flags.DEFINE_integer('num_preprocessing_threads', 4, 'The number of threads used to create the batches.') 84 | tf.app.flags.DEFINE_integer('log_every_n_steps', 100, 'The frequency with which logs are print.') 85 | tf.app.flags.DEFINE_integer('task', 0, 'Task id of the replica running the training.') 86 | 87 | ###################### 88 | # Optimization Flags # 89 | ###################### 90 | 91 | tf.app.flags.DEFINE_float('weight_decay', 0.0001, 'The weight decay on the model weights.') 92 | tf.app.flags.DEFINE_string('optimizer', 'adam', 'The name of the optimizer, one of "adadelta", "adagrad", "adam", ' 93 | '"ftrl", "momentum", "sgd" or "rmsprop".') 94 | tf.app.flags.DEFINE_float('adam_beta1', 0.9, 'The exponential decay rate for the 1st moment estimates.') 95 | tf.app.flags.DEFINE_float('adam_beta2', 0.999, 'The exponential decay rate for the 2nd moment estimates.') 96 | tf.app.flags.DEFINE_float('opt_epsilon', 1e-5, 'Epsilon term for the optimizer.') 97 | 98 | 99 | ####################### 100 | # Learning Rate Flags # 101 | ####################### 102 | 103 | tf.app.flags.DEFINE_string('learning_rate_decay_type', 'exponential', 'Specifies how the learning rate is decayed. One' 104 | ' of "fixed", "exponential", or "polynomial"') 105 | 106 | tf.app.flags.DEFINE_float('label_smoothing', 0.1, 'The amount of label smoothing.') 107 | tf.app.flags.DEFINE_float('learning_rate_decay_factor', 0.95, 'Learning rate decay factor.') 108 | tf.app.flags.DEFINE_float('num_epochs_per_decay', 2.0, 'Number of epochs after which learning rate decays.') 109 | tf.app.flags.DEFINE_bool('sync_replicas', False, 'Whether or not to synchronize the replicas during training.') 110 | tf.app.flags.DEFINE_integer('replicas_to_aggregate', 1, 'The Number of gradients to collect before updating params.') 111 | tf.app.flags.DEFINE_float('moving_average_decay', None, 'The decay to use for the moving average. If left as None, ' 112 | 'then moving averages are not used.') 113 | 114 | ####################### 115 | # Dataset Flags # 116 | ####################### 117 | tf.app.flags.DEFINE_string('source', None, 'detected, labeled, mixed') 118 | tf.app.flags.DEFINE_integer('split_num', None, '0-19') 119 | tf.app.flags.DEFINE_integer('cam_num', None, '6 cams or 10 cams.') 120 | tf.app.flags.DEFINE_boolean('hd_data', False, 'using high resolution image data for training.') 121 | tf.app.flags.DEFINE_integer('labels_offset', 0, 'An offset for the labels in the dataset. This flag is primarily used ' 122 | 'to evaluate the VGG and ResNet architectures which do not use a ' 123 | 'background class for the ImageNet dataset.') 124 | tf.app.flags.DEFINE_string('model_scope', '', 'The name scope of given model.') 125 | tf.app.flags.DEFINE_string('preprocessing_name', None, 'The name of the preprocessing to use. If left as `None`, then ' 126 | 'the model_name flag is used.') 127 | tf.app.flags.DEFINE_integer('batch_size', 32, 'The number of samples in each batch.') 128 | tf.app.flags.DEFINE_integer('batch_k', 4, 'The number of samples for each class (identity) in each batch.') 129 | tf.app.flags.DEFINE_string('shuffle_order', 'T', 'whether shuffle the batch order. T for Ture; F for False') 130 | tf.app.flags.DEFINE_integer('aug_mode', 3, 'data augumentation(1,2,3)') 131 | tf.app.flags.DEFINE_boolean('rand_erase', False, 'random erasing the image to augment the data') 132 | tf.app.flags.DEFINE_integer('test_mode', 1, 'testing 1: central crop 2: (coner crops + central crop +) flips') 133 | tf.app.flags.DEFINE_integer('train_image_height', 256, 'Crop Height') 134 | tf.app.flags.DEFINE_integer('train_image_width', 128, 'Crop Width') 135 | tf.app.flags.DEFINE_integer('summary_snapshot_steps', 20000, 'Summary save steps.') 136 | tf.app.flags.DEFINE_integer('model_snapshot_steps', 10000, 'Model save steps.') 137 | 138 | ##################### 139 | # Fine-Tuning Flags # 140 | ##################### 141 | 142 | tf.app.flags.DEFINE_string('checkpoint_path', None, 'The path to a checkpoint from which to fine-tune.') 143 | tf.app.flags.DEFINE_boolean('imagenet_pretrain', True, 'Using imagenet pretrained model to initialise.') 144 | tf.app.flags.DEFINE_boolean('ignore_missing_vars', False, 'When restoring a checkpoint would ignore missing variables.') 145 | 146 | ############### 147 | # Other Flags # 148 | ############### 149 | tf.app.flags.DEFINE_boolean('log_device_placement', False, "Whether to log device placement.") 150 | 151 | FLAGS = tf.app.flags.FLAGS 152 | 153 | ############################################## 154 | # Main Training Fuction # 155 | ############################################## 156 | 157 | def main(_): 158 | 159 | if not os.path.isdir(FLAGS.train_dir): 160 | os.makedirs(FLAGS.train_dir) 161 | 162 | if not FLAGS.dataset_dir: 163 | raise ValueError('You must supply the dataset directory with --dataset_dir') 164 | 165 | if not FLAGS.aug_mode: 166 | raise ValueError('aug_mode need to be speficied.') 167 | 168 | if (not FLAGS.train_image_height) or (not FLAGS.train_image_width): 169 | raise ValueError('The image height and width must be define explicitly.') 170 | 171 | if FLAGS.hd_data: 172 | if FLAGS.train_image_height != 400 or FLAGS.train_image_width != 200: 173 | FLAGS.train_image_height, FLAGS.train_image_width = 400, 200 174 | print("set the image size to (%d, %d)" % (400, 200)) 175 | 176 | # config and print log 177 | config_and_print_log(FLAGS) 178 | 179 | tf.logging.set_verbosity(tf.logging.INFO) 180 | with tf.Graph().as_default(): 181 | ####################### 182 | # Config model_deploy # 183 | ####################### 184 | deploy_config = model_deploy.DeploymentConfig( 185 | num_clones=FLAGS.num_clones, 186 | clone_on_cpu=FLAGS.clone_on_cpu, 187 | replica_id=FLAGS.task, 188 | num_replicas=FLAGS.worker_replicas, 189 | num_ps_tasks=FLAGS.num_ps_tasks) 190 | 191 | # Create global_step 192 | with tf.device(deploy_config.variables_device()): 193 | global_step = slim.create_global_step() 194 | 195 | ##################################### 196 | # Select the preprocessing function # 197 | ##################################### 198 | img_func = get_img_func() 199 | 200 | ###################### 201 | # Select the dataset # 202 | ###################### 203 | dataset = dataset_factory.DataLoader(FLAGS.model_name, FLAGS.dataset_name, FLAGS.dataset_dir, FLAGS.set, FLAGS.hd_data, img_func, 204 | FLAGS.batch_size, FLAGS.batch_k, FLAGS.max_number_of_steps, get_pair_type()) 205 | 206 | ###################### 207 | # Select the network # 208 | ###################### 209 | network_fn = nets_factory.get_network_fn( 210 | FLAGS.model_name, 211 | num_classes=(dataset.num_classes - FLAGS.labels_offset), 212 | weight_decay=FLAGS.weight_decay, 213 | is_training=True, 214 | sample_number= FLAGS.sample_number 215 | ) 216 | 217 | #################### 218 | # Define the model # 219 | #################### 220 | def clone_fn(tf_batch_queue): 221 | return build_graph(tf_batch_queue, network_fn) 222 | 223 | clones = model_deploy.create_clones(deploy_config, clone_fn, [dataset.tf_batch_queue]) 224 | first_clone_scope = deploy_config.clone_scope(0) 225 | # Gather update_ops from the first clone. These contain, for example, 226 | # the updates for the batch_norm variables created by network_fn. 227 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, first_clone_scope) 228 | 229 | # Add summaries for end_points. 230 | end_points = clones[0].outputs 231 | 232 | # Add summaries for losses. 233 | loss_dict = {} 234 | for loss in tf.get_collection(tf.GraphKeys.LOSSES, first_clone_scope): 235 | if loss.name == 'softmax_cross_entropy_loss/value:0': 236 | loss_dict['clf'] = loss 237 | elif 'softmax_cross_entropy_loss' in loss.name: 238 | loss_dict['sample_clf_'+str(loss.name.split('/')[0].split('_')[-1])] = loss 239 | elif 'entropy' in loss.name: 240 | loss_dict['entropy'] = loss 241 | else: 242 | raise Exception('Loss type error') 243 | 244 | ################################# 245 | # Configure the moving averages # 246 | ################################# 247 | if FLAGS.moving_average_decay: 248 | moving_average_variables = slim.get_model_variables() 249 | variable_averages = tf.train.ExponentialMovingAverage( 250 | FLAGS.moving_average_decay, global_step) 251 | else: 252 | moving_average_variables, variable_averages = None, None 253 | 254 | ######################################### 255 | # Configure the optimization procedure. # 256 | ######################################### 257 | with tf.device(deploy_config.optimizer_device()): 258 | learning_rate = _configure_learning_rate(dataset.num_samples, global_step, FLAGS) 259 | optimizer = _configure_optimizer(learning_rate) 260 | 261 | if FLAGS.sync_replicas: 262 | # If sync_replicas is enabled, the averaging will be done in the chief 263 | # queue runner. 264 | optimizer = tf.train.SyncReplicasOptimizer( 265 | opt=optimizer, 266 | replicas_to_aggregate=FLAGS.replicas_to_aggregate, 267 | variable_averages=variable_averages, 268 | variables_to_average=moving_average_variables, 269 | replica_id=tf.constant(FLAGS.task, tf.int32, shape=()), 270 | total_num_replicas=FLAGS.worker_replicas) 271 | elif FLAGS.moving_average_decay: 272 | # Update ops executed locally by trainer. 273 | update_ops.append(variable_averages.apply(moving_average_variables)) 274 | 275 | # Variables to train. 276 | variables_to_train = _get_variables_to_train() 277 | 278 | # and returns a train_tensor and summary_op 279 | # total_loss is the sum of all LOSSES and REGULARIZATION_LOSSES in tf.GraphKeys 280 | total_loss, clones_gradients = model_deploy.optimize_clones( 281 | clones, 282 | optimizer, 283 | var_list=variables_to_train) 284 | 285 | # Create gradient updates. 286 | grad_updates = optimizer.apply_gradients(clones_gradients, 287 | global_step=global_step) 288 | update_ops.append(grad_updates) 289 | 290 | update_op = tf.group(*update_ops) 291 | train_tensor = control_flow_ops.with_dependencies([update_op], total_loss, 292 | name='train_op') 293 | 294 | train_tensor_list = [train_tensor] 295 | format_str = 'step %d, loss = %.2f' 296 | 297 | for loss_key in sorted(loss_dict.keys()): 298 | train_tensor_list.append(loss_dict[loss_key]) 299 | format_str += (', %s_loss = ' % loss_key + '%.8f') 300 | 301 | format_str += ' (%.1f examples/sec; %.3f sec/batch)' 302 | 303 | # Create a saver. 304 | saver = tf.train.Saver(tf.global_variables(),max_to_keep=1) 305 | checkpoint_path = os.path.join(FLAGS.train_dir, 'model.ckpt') 306 | 307 | ########################### 308 | # Kicks off the training. # 309 | ########################### 310 | # Build an initialization operation to run below. 311 | init = tf.global_variables_initializer() 312 | 313 | # Start running operations on the Graph. allow_soft_placement must be set to 314 | # True to build towers on GPU, as some of the ops do not have GPU 315 | # implementations. 316 | sess = tf.Session(config=tf.ConfigProto( 317 | allow_soft_placement=True, 318 | log_device_placement=FLAGS.log_device_placement)) 319 | sess.run(init) 320 | 321 | # load pretrained weights 322 | if FLAGS.checkpoint_path is not None: 323 | print("Load the pretrained weights") 324 | weight_ini_fn = _get_init_fn() 325 | weight_ini_fn(sess) 326 | else: 327 | print("Train from the scratch") 328 | 329 | # Start the queue runners. 330 | tf.train.start_queue_runners(sess=sess) 331 | 332 | # for step in xrange(FLAGS.max_number_of_steps): 333 | for step in xrange(FLAGS.max_number_of_steps + 1): 334 | start_time = time.time() 335 | 336 | loss_value_list = sess.run(train_tensor_list, feed_dict=dataset.get_feed_dict()) 337 | 338 | duration = time.time() - start_time 339 | 340 | # assert not np.isnan(loss_value), 'Model diverged with loss = NaN' 341 | 342 | if step % FLAGS.log_every_n_steps == 0: 343 | # num_examples_per_step = FLAGS.batch_size * FLAGS.num_gpus 344 | num_examples_per_step = FLAGS.batch_size 345 | examples_per_sec = num_examples_per_step / duration 346 | # sec_per_batch = duration / FLAGS.num_gpus 347 | sec_per_batch = duration 348 | 349 | print(format_str % tuple([step] + loss_value_list + [examples_per_sec, sec_per_batch])) 350 | 351 | # Save the model checkpoint periodically. 352 | # if step % FLAGS.model_snapshot_steps == 0 or (step + 1) == FLAGS.max_number_of_steps: 353 | if step % FLAGS.model_snapshot_steps == 0: 354 | saver.save(sess, checkpoint_path, global_step=step) 355 | 356 | print('OK...') 357 | 358 | 359 | if __name__ == '__main__': 360 | tf.app.run() 361 | -------------------------------------------------------------------------------- /train_ReID_classifier_con.py: -------------------------------------------------------------------------------- 1 | # this is a modification based on the train_image_classifier.py 2 | # ============================================================================== 3 | """Generic training script that trains a ReID model using a given dataset.""" 4 | 5 | from __future__ import absolute_import 6 | from __future__ import division 7 | from __future__ import print_function 8 | 9 | import tensorflow as tf 10 | 11 | import os 12 | import time 13 | 14 | from tensorflow.python.ops import control_flow_ops 15 | import dataset_factory 16 | import model_deploy 17 | from nets import nets_factory 18 | 19 | import tensorflow.contrib.slim as slim 20 | 21 | from utils import config_and_print_log, _configure_learning_rate, _configure_optimizer, _get_variables_to_train, _get_init_fn, get_img_func, build_graph, get_pair_type 22 | 23 | 24 | 25 | ############## 26 | # FLags may be usually changed # 27 | ############## 28 | 29 | tf.app.flags.DEFINE_string('model_name', 'resnet_v1_distributions_baseline_50', 'The name of the architecture to train.') 30 | 31 | tf.app.flags.DEFINE_boolean('entropy_loss', False, 'uncertainty loss') 32 | tf.app.flags.DEFINE_integer('max_number_of_steps', 20200, 'The maximum number of training steps.') 33 | tf.app.flags.DEFINE_string('target', 'market', 'For name of model') 34 | tf.app.flags.DEFINE_boolean('standard', False, 'For name of model') 35 | 36 | tf.app.flags.DEFINE_string('set', 'bounding_box_train', "subset under current dataset") 37 | tf.app.flags.DEFINE_integer('boot_weight', 1.0, 'cross entropy loss weight') 38 | 39 | tf.app.flags.DEFINE_float('sampled_ce_loss_weight', None, 'loss weight for xent of drawn samples.') 40 | tf.app.flags.DEFINE_integer('sample_number', None, 'the number of samples drawn from distribution.') 41 | 42 | tf.app.flags.DEFINE_boolean('resume_train', True, 'when set to true, resume training from current train dir or load dir.') 43 | 44 | tf.app.flags.DEFINE_string('dataset_name', 'Market', 'The name of the Person ReID dataset to load.') 45 | 46 | tf.app.flags.DEFINE_string('dataset_dir', './Market/', 47 | 'The directory where the dataset files are stored.') 48 | 49 | tf.app.flags.DEFINE_string('checkpoint_path2', '', 50 | 'The path to a checkpoint from which to fine-tune.') 51 | 52 | 53 | tf.app.flags.DEFINE_float('learning_rate', 0.00005, 'Initial learning rate.') 54 | tf.app.flags.DEFINE_float('end_learning_rate', 0.00001, 'The minimal end learning rate used by a polynomial decay learning rate.') 55 | 56 | tf.app.flags.DEFINE_string('trainable_scopes', 'resnet_v1_50/Distributions, resnet_v1_50/logits, resnet_v1_50/block4/', 'Comma-separated list of scopes to filter the set of variables to train. By default, None would train all the variables.') 57 | 58 | tf.app.flags.DEFINE_string('checkpoint_exclude_scopes', ['Distributions'], 'Comma-separated list of scopes of variables to exclude when restoring from a checkpoint.') 59 | 60 | 61 | ##################### 62 | ###The following flags are fixed all the time 63 | ##################### 64 | 65 | tf.app.flags.DEFINE_boolean('use_clf', True, 'Add classification (identification) loss to the network.') 66 | tf.app.flags.DEFINE_string('master', '', 'The address of the TensorFlow master to use.') 67 | tf.app.flags.DEFINE_string('train_dir', './result', 68 | 'Directory where checkpoints and event logs are written to.') 69 | tf.app.flags.DEFINE_string('sub_dir', '', 'Subdirectory to identify the sv dir') 70 | tf.app.flags.DEFINE_integer('num_clones', 1, 'Number of model clones to deploy.') 71 | tf.app.flags.DEFINE_boolean('clone_on_cpu', False, 'Use CPUs to deploy clones.') 72 | tf.app.flags.DEFINE_integer('worker_replicas', 1, 'Number of worker replicas.') 73 | tf.app.flags.DEFINE_integer('num_ps_tasks', 0, 'The number of parameter servers. If the value is 0, then the ' 74 | 'parameters are handled locally by the worker.') 75 | 76 | # readers number 77 | tf.app.flags.DEFINE_integer('num_readers', 4, 'The number of parallel readers that read data from the dataset.') 78 | tf.app.flags.DEFINE_integer('num_preprocessing_threads', 4, 'The number of threads used to create the batches.') 79 | tf.app.flags.DEFINE_integer('log_every_n_steps', 100, 'The frequency with which logs are print.') 80 | tf.app.flags.DEFINE_integer('task', 0, 'Task id of the replica running the training.') 81 | 82 | ###################### 83 | # Optimization Flags # 84 | ###################### 85 | 86 | tf.app.flags.DEFINE_float('weight_decay', 0.0001, 'The weight decay on the model weights.') 87 | tf.app.flags.DEFINE_string('optimizer', 'adam', 'The name of the optimizer, one of "adadelta", "adagrad", "adam", ' 88 | '"ftrl", "momentum", "sgd" or "rmsprop".') 89 | tf.app.flags.DEFINE_float('adam_beta1', 0.9, 'The exponential decay rate for the 1st moment estimates.') 90 | tf.app.flags.DEFINE_float('adam_beta2', 0.999, 'The exponential decay rate for the 2nd moment estimates.') 91 | tf.app.flags.DEFINE_float('opt_epsilon', 1e-5, 'Epsilon term for the optimizer.') 92 | 93 | 94 | ####################### 95 | # Learning Rate Flags # 96 | ####################### 97 | 98 | tf.app.flags.DEFINE_string('learning_rate_decay_type', 'exponential', 'Specifies how the learning rate is decayed. One' 99 | ' of "fixed", "exponential", or "polynomial"') 100 | 101 | tf.app.flags.DEFINE_float('label_smoothing', 0.1, 'The amount of label smoothing.') 102 | tf.app.flags.DEFINE_float('learning_rate_decay_factor', 0.95, 'Learning rate decay factor.') 103 | tf.app.flags.DEFINE_float('num_epochs_per_decay', 2.0, 'Number of epochs after which learning rate decays.') 104 | tf.app.flags.DEFINE_bool('sync_replicas', False, 'Whether or not to synchronize the replicas during training.') 105 | tf.app.flags.DEFINE_integer('replicas_to_aggregate', 1, 'The Number of gradients to collect before updating params.') 106 | tf.app.flags.DEFINE_float('moving_average_decay', None, 'The decay to use for the moving average. If left as None, ' 107 | 'then moving averages are not used.') 108 | 109 | ####################### 110 | # Dataset Flags # 111 | ####################### 112 | tf.app.flags.DEFINE_string('source', None, 'detected, labeled, mixed') 113 | tf.app.flags.DEFINE_integer('split_num', None, '0-19') 114 | tf.app.flags.DEFINE_integer('cam_num', None, '6 cams or 10 cams.') 115 | tf.app.flags.DEFINE_boolean('hd_data', False, 'using high resolution image data for training.') 116 | tf.app.flags.DEFINE_integer('labels_offset', 0, 'An offset for the labels in the dataset. This flag is primarily used ' 117 | 'to evaluate the VGG and ResNet architectures which do not use a ' 118 | 'background class for the ImageNet dataset.') 119 | tf.app.flags.DEFINE_string('model_scope', '', 'The name scope of given model.') 120 | tf.app.flags.DEFINE_string('preprocessing_name', None, 'The name of the preprocessing to use. If left as `None`, then ' 121 | 'the model_name flag is used.') 122 | tf.app.flags.DEFINE_integer('batch_size', 32, 'The number of samples in each batch.') 123 | tf.app.flags.DEFINE_integer('batch_k', 4, 'The number of samples for each class (identity) in each batch.') 124 | tf.app.flags.DEFINE_string('shuffle_order', 'T', 'whether shuffle the batch order. T for Ture; F for False') 125 | tf.app.flags.DEFINE_integer('aug_mode', 3, 'data augumentation(1,2,3)') 126 | tf.app.flags.DEFINE_boolean('rand_erase', False, 'random erasing the image to augment the data') 127 | tf.app.flags.DEFINE_integer('test_mode', 1, 'testing 1: central crop 2: (coner crops + central crop +) flips') 128 | tf.app.flags.DEFINE_integer('train_image_height', 256, 'Crop Height') 129 | tf.app.flags.DEFINE_integer('train_image_width', 128, 'Crop Width') 130 | tf.app.flags.DEFINE_integer('summary_snapshot_steps', 20000, 'Summary save steps.') 131 | tf.app.flags.DEFINE_integer('model_snapshot_steps', 10000, 'Model save steps.') 132 | 133 | ##################### 134 | # Fine-Tuning Flags # 135 | ##################### 136 | 137 | tf.app.flags.DEFINE_string('checkpoint_path', None, 'The path to a checkpoint from which to fine-tune.') 138 | tf.app.flags.DEFINE_boolean('imagenet_pretrain', True, 'Using imagenet pretrained model to initialise.') 139 | tf.app.flags.DEFINE_boolean('ignore_missing_vars', False, 'When restoring a checkpoint would ignore missing variables.') 140 | 141 | ############### 142 | # Other Flags # 143 | ############### 144 | tf.app.flags.DEFINE_boolean('log_device_placement', False, "Whether to log device placement.") 145 | 146 | FLAGS = tf.app.flags.FLAGS 147 | 148 | 149 | 150 | ############################################## 151 | # Main Training Fuction # 152 | ############################################## 153 | 154 | def main(_): 155 | if not os.path.isdir(FLAGS.train_dir): 156 | os.makedirs(FLAGS.train_dir) 157 | 158 | if not FLAGS.dataset_dir: 159 | raise ValueError('You must supply the dataset directory with --dataset_dir') 160 | 161 | if not FLAGS.aug_mode: 162 | raise ValueError('aug_mode need to be speficied.') 163 | 164 | if (not FLAGS.train_image_height) or (not FLAGS.train_image_width): 165 | raise ValueError('The image height and width must be define explicitly.') 166 | 167 | if FLAGS.hd_data: 168 | if FLAGS.train_image_height != 400 or FLAGS.train_image_width != 200: 169 | FLAGS.train_image_height, FLAGS.train_image_width = 400, 200 170 | print("set the image size to (%d, %d)" % (400, 200)) 171 | 172 | # config and print log 173 | config_and_print_log(FLAGS) 174 | 175 | tf.logging.set_verbosity(tf.logging.INFO) 176 | with tf.Graph().as_default(): 177 | ####################### 178 | # Config model_deploy # 179 | ####################### 180 | deploy_config = model_deploy.DeploymentConfig( 181 | num_clones=FLAGS.num_clones, 182 | clone_on_cpu=FLAGS.clone_on_cpu, 183 | replica_id=FLAGS.task, 184 | num_replicas=FLAGS.worker_replicas, 185 | num_ps_tasks=FLAGS.num_ps_tasks) 186 | 187 | # Create global_step 188 | with tf.device(deploy_config.variables_device()): 189 | global_step = slim.create_global_step() 190 | 191 | ##################################### 192 | # Select the preprocessing function # 193 | ##################################### 194 | img_func = get_img_func() 195 | 196 | ###################### 197 | # Select the dataset # 198 | ###################### 199 | dataset = dataset_factory.DataLoader(FLAGS.model_name, FLAGS.dataset_name, FLAGS.dataset_dir, FLAGS.set, FLAGS.hd_data, img_func, 200 | FLAGS.batch_size, FLAGS.batch_k, FLAGS.max_number_of_steps, get_pair_type()) 201 | 202 | ###################### 203 | # Select the network # 204 | ###################### 205 | network_fn = nets_factory.get_network_fn( 206 | FLAGS.model_name, 207 | num_classes=(dataset.num_classes - FLAGS.labels_offset), 208 | weight_decay=FLAGS.weight_decay, 209 | is_training=True, 210 | sample_number= FLAGS.sample_number 211 | ) 212 | 213 | #################### 214 | # Define the model # 215 | #################### 216 | def clone_fn(tf_batch_queue): 217 | return build_graph(tf_batch_queue, network_fn) 218 | 219 | clones = model_deploy.create_clones(deploy_config, clone_fn, [dataset.tf_batch_queue]) 220 | first_clone_scope = deploy_config.clone_scope(0) 221 | # Gather update_ops from the first clone. These contain, for example, 222 | # the updates for the batch_norm variables created by network_fn. 223 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, first_clone_scope) 224 | 225 | loss_dict = {} 226 | for loss in tf.get_collection(tf.GraphKeys.LOSSES, first_clone_scope): 227 | if loss.name == 'softmax_cross_entropy_loss/value:0': 228 | loss_dict['clf'] = loss 229 | elif 'softmax_cross_entropy_loss' in loss.name: 230 | loss_dict['sample_clf_'+str(loss.name.split('/')[0].split('_')[-1])] = loss 231 | elif 'entropy' in loss.name: 232 | loss_dict['entropy'] = loss 233 | else: 234 | raise Exception('Loss type error') 235 | 236 | 237 | ################################# 238 | # Configure the moving averages # 239 | ################################# 240 | if FLAGS.moving_average_decay: 241 | moving_average_variables = slim.get_model_variables() 242 | variable_averages = tf.train.ExponentialMovingAverage( 243 | FLAGS.moving_average_decay, global_step) 244 | else: 245 | moving_average_variables, variable_averages = None, None 246 | 247 | ######################################### 248 | # Configure the optimization procedure. # 249 | ######################################### 250 | with tf.device(deploy_config.optimizer_device()): 251 | learning_rate = _configure_learning_rate(dataset.num_samples, global_step, FLAGS) 252 | optimizer = _configure_optimizer(learning_rate) 253 | 254 | if FLAGS.sync_replicas: 255 | # If sync_replicas is enabled, the averaging will be done in the chief 256 | # queue runner. 257 | optimizer = tf.train.SyncReplicasOptimizer( 258 | opt=optimizer, 259 | replicas_to_aggregate=FLAGS.replicas_to_aggregate, 260 | variable_averages=variable_averages, 261 | variables_to_average=moving_average_variables, 262 | replica_id=tf.constant(FLAGS.task, tf.int32, shape=()), 263 | total_num_replicas=FLAGS.worker_replicas) 264 | elif FLAGS.moving_average_decay: 265 | # Update ops executed locally by trainer. 266 | update_ops.append(variable_averages.apply(moving_average_variables)) 267 | 268 | # Variables to train. 269 | variables_to_train = _get_variables_to_train() 270 | 271 | # and returns a train_tensor and summary_op 272 | # total_loss is the sum of all LOSSES and REGULARIZATION_LOSSES in tf.GraphKeys 273 | total_loss, clones_gradients = model_deploy.optimize_clones( 274 | clones, 275 | optimizer, 276 | var_list=variables_to_train) 277 | 278 | # Create gradient updates. 279 | grad_updates = optimizer.apply_gradients(clones_gradients, 280 | global_step=global_step) 281 | update_ops.append(grad_updates) 282 | 283 | update_op = tf.group(*update_ops) 284 | train_tensor = control_flow_ops.with_dependencies([update_op], total_loss, 285 | name='train_op') 286 | 287 | train_tensor_list = [train_tensor] 288 | format_str = 'step %d, loss = %.2f' 289 | 290 | for loss_key in sorted(loss_dict.keys()): 291 | train_tensor_list.append(loss_dict[loss_key]) 292 | format_str += (', %s_loss = ' % loss_key + '%.8f') 293 | 294 | format_str += ' (%.1f examples/sec; %.3f sec/batch)' 295 | 296 | # Create a saver. 297 | saver = tf.train.Saver(tf.global_variables(),max_to_keep=1) 298 | checkpoint_path = os.path.join(FLAGS.train_dir, 'model.ckpt') 299 | 300 | ########################### 301 | # Kicks off the training. # 302 | ########################### 303 | # Build an initialization operation to run below. 304 | init = tf.global_variables_initializer() 305 | 306 | # Start running operations on the Graph. allow_soft_placement must be set to 307 | # True to build towers on GPU, as some of the ops do not have GPU 308 | # implementations. 309 | sess = tf.Session(config=tf.ConfigProto( 310 | allow_soft_placement=True, 311 | log_device_placement=FLAGS.log_device_placement)) 312 | sess.run(init) 313 | 314 | # load pretrained weights 315 | if FLAGS.checkpoint_path is not None: 316 | print("Load the pretrained weights") 317 | weight_ini_fn = _get_init_fn() 318 | weight_ini_fn(sess) 319 | else: 320 | print("Train from the scratch") 321 | 322 | # Start the queue runners. 323 | tf.train.start_queue_runners(sess=sess) 324 | 325 | # for step in xrange(FLAGS.max_number_of_steps): 326 | for step in xrange(FLAGS.max_number_of_steps + 1): 327 | start_time = time.time() 328 | loss_value_list = sess.run(train_tensor_list, feed_dict=dataset.get_feed_dict()) 329 | 330 | duration = time.time() - start_time 331 | 332 | if step % FLAGS.log_every_n_steps == 0: 333 | num_examples_per_step = FLAGS.batch_size 334 | examples_per_sec = num_examples_per_step / duration 335 | sec_per_batch = duration 336 | 337 | print(format_str % tuple([step] + loss_value_list + [examples_per_sec, sec_per_batch])) 338 | 339 | # Save the model checkpoint periodically. 340 | # if step % FLAGS.model_snapshot_steps == 0 or (step + 1) == FLAGS.max_number_of_steps: 341 | if step % FLAGS.model_snapshot_steps == 0: 342 | saver.save(sess, checkpoint_path, global_step=step) 343 | 344 | print('OK...') 345 | 346 | 347 | if __name__ == '__main__': 348 | tf.app.run() 349 | -------------------------------------------------------------------------------- /train_ReID_classifier_from_resnet50.py: -------------------------------------------------------------------------------- 1 | # this is a modification based on the train_image_classifier.py 2 | # ============================================================================== 3 | """Generic training script that trains a ReID model using a given dataset.""" 4 | 5 | from __future__ import absolute_import 6 | from __future__ import division 7 | from __future__ import print_function 8 | 9 | import tensorflow as tf 10 | 11 | import os 12 | import time 13 | 14 | from tensorflow.python.ops import control_flow_ops 15 | import dataset_factory 16 | import model_deploy 17 | from nets import nets_factory 18 | 19 | import tensorflow.contrib.slim as slim 20 | 21 | from utils import config_and_print_log, _configure_learning_rate, _configure_optimizer, _get_variables_to_train, _get_init_fn, get_img_func, build_graph, get_pair_type 22 | 23 | 24 | 25 | ############## 26 | # FLags may be usually changed # 27 | ############## 28 | 29 | tf.app.flags.DEFINE_string('model_name', 'resnet_v1_50', 'The name of the architecture to train.') 30 | 31 | tf.app.flags.DEFINE_integer('max_number_of_steps', 60200, 'The maximum number of training steps.') 32 | tf.app.flags.DEFINE_string('target', 'market', 'For name of model') 33 | tf.app.flags.DEFINE_boolean('standard', False, 'For name of model') 34 | tf.app.flags.DEFINE_boolean('entropy_loss', False, 'uncertainty loss') 35 | tf.app.flags.DEFINE_string('set', 'bounding_box_train', "subset under current dataset") 36 | tf.app.flags.DEFINE_integer('boot_weight', 1.0, 'cross entropy loss weight') 37 | 38 | tf.app.flags.DEFINE_float('sampled_ce_loss_weight', None, 'loss weight for xent of drawn samples.') 39 | tf.app.flags.DEFINE_integer('sample_number', None, 'the number of samples drawn from distribution.') 40 | 41 | tf.app.flags.DEFINE_boolean('resume_train', False, 'when set to true, resume training from current train dir or load dir.') 42 | 43 | tf.app.flags.DEFINE_string('dataset_name', 'Market', 'The name of the Person ReID dataset to load.') 44 | 45 | tf.app.flags.DEFINE_string('dataset_dir', './Market/', 46 | 'The directory where the dataset files are stored.') 47 | 48 | tf.app.flags.DEFINE_string('checkpoint_path2', '', 49 | 'The path to a checkpoint from which to fine-tune.') 50 | 51 | 52 | tf.app.flags.DEFINE_float('learning_rate', 0.00035, 'Initial learning rate.') 53 | tf.app.flags.DEFINE_float('end_learning_rate', 0.0001, 'The minimal end learning rate used by a polynomial decay learning rate.') 54 | 55 | tf.app.flags.DEFINE_string('trainable_scopes', None, 'Comma-separated list of scopes to filter the set of variables to train. By default, None would train all the variables.') 56 | 57 | tf.app.flags.DEFINE_string('checkpoint_exclude_scopes', ['logits', 'concat_comb', 'fusion', 'Distributions'], 'Comma-separated list of scopes of variables to exclude when restoring from a checkpoint.') 58 | 59 | 60 | ##################### 61 | ###The following flags are fixed all the time 62 | ##################### 63 | 64 | tf.app.flags.DEFINE_boolean('use_clf', True, 'Add classification (identification) loss to the network.') 65 | tf.app.flags.DEFINE_string('master', '', 'The address of the TensorFlow master to use.') 66 | tf.app.flags.DEFINE_string('train_dir', './result', 67 | 'Directory where checkpoints and event logs are written to.') 68 | tf.app.flags.DEFINE_string('sub_dir', '', 'Subdirectory to identify the sv dir') 69 | tf.app.flags.DEFINE_integer('num_clones', 1, 'Number of model clones to deploy.') 70 | tf.app.flags.DEFINE_boolean('clone_on_cpu', False, 'Use CPUs to deploy clones.') 71 | tf.app.flags.DEFINE_integer('worker_replicas', 1, 'Number of worker replicas.') 72 | tf.app.flags.DEFINE_integer('num_ps_tasks', 0, 'The number of parameter servers. If the value is 0, then the ' 73 | 'parameters are handled locally by the worker.') 74 | 75 | # readers number 76 | tf.app.flags.DEFINE_integer('num_readers', 4, 'The number of parallel readers that read data from the dataset.') 77 | tf.app.flags.DEFINE_integer('num_preprocessing_threads', 4, 'The number of threads used to create the batches.') 78 | tf.app.flags.DEFINE_integer('log_every_n_steps', 100, 'The frequency with which logs are print.') 79 | tf.app.flags.DEFINE_integer('task', 0, 'Task id of the replica running the training.') 80 | 81 | ###################### 82 | # Optimization Flags # 83 | ###################### 84 | 85 | tf.app.flags.DEFINE_float('weight_decay', 0.0001, 'The weight decay on the model weights.') 86 | tf.app.flags.DEFINE_string('optimizer', 'adam', 'The name of the optimizer, one of "adadelta", "adagrad", "adam", ' 87 | '"ftrl", "momentum", "sgd" or "rmsprop".') 88 | tf.app.flags.DEFINE_float('adam_beta1', 0.9, 'The exponential decay rate for the 1st moment estimates.') 89 | tf.app.flags.DEFINE_float('adam_beta2', 0.999, 'The exponential decay rate for the 2nd moment estimates.') 90 | tf.app.flags.DEFINE_float('opt_epsilon', 1e-5, 'Epsilon term for the optimizer.') 91 | 92 | 93 | ####################### 94 | # Learning Rate Flags # 95 | ####################### 96 | 97 | tf.app.flags.DEFINE_string('learning_rate_decay_type', 'exponential', 'Specifies how the learning rate is decayed. One' 98 | ' of "fixed", "exponential", or "polynomial"') 99 | 100 | tf.app.flags.DEFINE_float('label_smoothing', 0.1, 'The amount of label smoothing.') 101 | tf.app.flags.DEFINE_float('learning_rate_decay_factor', 0.95, 'Learning rate decay factor.') 102 | tf.app.flags.DEFINE_float('num_epochs_per_decay', 2.0, 'Number of epochs after which learning rate decays.') 103 | tf.app.flags.DEFINE_bool('sync_replicas', False, 'Whether or not to synchronize the replicas during training.') 104 | tf.app.flags.DEFINE_integer('replicas_to_aggregate', 1, 'The Number of gradients to collect before updating params.') 105 | tf.app.flags.DEFINE_float('moving_average_decay', None, 'The decay to use for the moving average. If left as None, ' 106 | 'then moving averages are not used.') 107 | 108 | ####################### 109 | # Dataset Flags # 110 | ####################### 111 | tf.app.flags.DEFINE_string('source', None, 'detected, labeled, mixed') 112 | tf.app.flags.DEFINE_integer('split_num', None, '0-19') 113 | tf.app.flags.DEFINE_integer('cam_num', None, '6 cams or 10 cams.') 114 | tf.app.flags.DEFINE_boolean('hd_data', False, 'using high resolution image data for training.') 115 | tf.app.flags.DEFINE_integer('labels_offset', 0, 'An offset for the labels in the dataset. This flag is primarily used ' 116 | 'to evaluate the VGG and ResNet architectures which do not use a ' 117 | 'background class for the ImageNet dataset.') 118 | tf.app.flags.DEFINE_string('model_scope', '', 'The name scope of given model.') 119 | tf.app.flags.DEFINE_string('preprocessing_name', None, 'The name of the preprocessing to use. If left as `None`, then ' 120 | 'the model_name flag is used.') 121 | tf.app.flags.DEFINE_integer('batch_size', 32, 'The number of samples in each batch.') 122 | tf.app.flags.DEFINE_integer('batch_k', 4, 'The number of samples for each class (identity) in each batch.') 123 | tf.app.flags.DEFINE_string('shuffle_order', 'T', 'whether shuffle the batch order. T for Ture; F for False') 124 | tf.app.flags.DEFINE_integer('aug_mode', 3, 'data augumentation(1,2,3)') 125 | tf.app.flags.DEFINE_boolean('rand_erase', False, 'random erasing the image to augment the data') 126 | tf.app.flags.DEFINE_integer('test_mode', 1, 'testing 1: central crop 2: (coner crops + central crop +) flips') 127 | tf.app.flags.DEFINE_integer('train_image_height', 256, 'Crop Height') 128 | tf.app.flags.DEFINE_integer('train_image_width', 128, 'Crop Width') 129 | tf.app.flags.DEFINE_integer('summary_snapshot_steps', 20000, 'Summary save steps.') 130 | tf.app.flags.DEFINE_integer('model_snapshot_steps', 10000, 'Model save steps.') 131 | 132 | ##################### 133 | # Fine-Tuning Flags # 134 | ##################### 135 | 136 | tf.app.flags.DEFINE_string('checkpoint_path', None, 'The path to a checkpoint from which to fine-tune.') 137 | tf.app.flags.DEFINE_boolean('imagenet_pretrain', True, 'Using imagenet pretrained model to initialise.') 138 | tf.app.flags.DEFINE_boolean('ignore_missing_vars', False, 'When restoring a checkpoint would ignore missing variables.') 139 | 140 | ############### 141 | # Other Flags # 142 | ############### 143 | tf.app.flags.DEFINE_boolean('log_device_placement', False, "Whether to log device placement.") 144 | 145 | FLAGS = tf.app.flags.FLAGS 146 | 147 | 148 | ############################################## 149 | # Main Training Fuction # 150 | ############################################## 151 | 152 | def main(_): 153 | if not os.path.isdir(FLAGS.train_dir): 154 | os.makedirs(FLAGS.train_dir) 155 | 156 | if not FLAGS.dataset_dir: 157 | raise ValueError('You must supply the dataset directory with --dataset_dir') 158 | 159 | if not FLAGS.aug_mode: 160 | raise ValueError('aug_mode need to be speficied.') 161 | 162 | if (not FLAGS.train_image_height) or (not FLAGS.train_image_width): 163 | raise ValueError('The image height and width must be define explicitly.') 164 | 165 | if FLAGS.hd_data: 166 | if FLAGS.train_image_height != 400 or FLAGS.train_image_width != 200: 167 | FLAGS.train_image_height, FLAGS.train_image_width = 400, 200 168 | print("set the image size to (%d, %d)" % (400, 200)) 169 | 170 | # config and print log 171 | config_and_print_log(FLAGS) 172 | 173 | tf.logging.set_verbosity(tf.logging.INFO) 174 | with tf.Graph().as_default(): 175 | ####################### 176 | # Config model_deploy # 177 | ####################### 178 | deploy_config = model_deploy.DeploymentConfig( 179 | num_clones=FLAGS.num_clones, 180 | clone_on_cpu=FLAGS.clone_on_cpu, 181 | replica_id=FLAGS.task, 182 | num_replicas=FLAGS.worker_replicas, 183 | num_ps_tasks=FLAGS.num_ps_tasks) 184 | 185 | # Create global_step 186 | with tf.device(deploy_config.variables_device()): 187 | global_step = slim.create_global_step() 188 | 189 | ##################################### 190 | # Select the preprocessing function # 191 | ##################################### 192 | img_func = get_img_func() 193 | 194 | ###################### 195 | # Select the dataset # 196 | ###################### 197 | dataset = dataset_factory.DataLoader(FLAGS.model_name, FLAGS.dataset_name, FLAGS.dataset_dir, FLAGS.set, FLAGS.hd_data, img_func, 198 | FLAGS.batch_size, FLAGS.batch_k, FLAGS.max_number_of_steps, get_pair_type()) 199 | 200 | ###################### 201 | # Select the network # 202 | ###################### 203 | network_fn = nets_factory.get_network_fn( 204 | FLAGS.model_name, 205 | num_classes=(dataset.num_classes - FLAGS.labels_offset), 206 | weight_decay=FLAGS.weight_decay, 207 | is_training=True, 208 | ) 209 | 210 | #################### 211 | # Define the model # 212 | #################### 213 | def clone_fn(tf_batch_queue): 214 | return build_graph(tf_batch_queue, network_fn) 215 | 216 | # Gather initial summaries. 217 | summaries = set(tf.get_collection(tf.GraphKeys.SUMMARIES)) 218 | 219 | clones = model_deploy.create_clones(deploy_config, clone_fn, [dataset.tf_batch_queue]) 220 | first_clone_scope = deploy_config.clone_scope(0) 221 | # Gather update_ops from the first clone. These contain, for example, 222 | # the updates for the batch_norm variables created by network_fn. 223 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, first_clone_scope) 224 | 225 | # Add summaries for end_points. 226 | end_points = clones[0].outputs 227 | for end_point in end_points: 228 | if end_point == 'sample_dist': 229 | continue 230 | 231 | # Add summaries for losses. 232 | loss_dict = {} 233 | for loss in tf.get_collection(tf.GraphKeys.LOSSES, first_clone_scope): 234 | summaries.add(tf.summary.scalar('losses/%s' % loss.op.name, loss)) 235 | if 'ver' in loss.name: 236 | loss_dict['ver'] = loss 237 | elif 'distribution' in loss.name: 238 | loss_dict['trip_distribution'] = loss 239 | elif 'instance' in loss.name: 240 | loss_dict['entropy_across_instance'] = loss 241 | elif 'entropy' in loss.name: 242 | loss_dict['entropy'] = loss 243 | else: 244 | raise Exception('Loss type error') 245 | 246 | 247 | ################################# 248 | # Configure the moving averages # 249 | ################################# 250 | if FLAGS.moving_average_decay: 251 | moving_average_variables = slim.get_model_variables() 252 | variable_averages = tf.train.ExponentialMovingAverage( 253 | FLAGS.moving_average_decay, global_step) 254 | else: 255 | moving_average_variables, variable_averages = None, None 256 | 257 | ######################################### 258 | # Configure the optimization procedure. # 259 | ######################################### 260 | with tf.device(deploy_config.optimizer_device()): 261 | learning_rate = _configure_learning_rate(dataset.num_samples, global_step, FLAGS) 262 | optimizer = _configure_optimizer(learning_rate) 263 | summaries.add(tf.summary.scalar('learning_rate', learning_rate)) 264 | 265 | if FLAGS.sync_replicas: 266 | # If sync_replicas is enabled, the averaging will be done in the chief 267 | # queue runner. 268 | optimizer = tf.train.SyncReplicasOptimizer( 269 | opt=optimizer, 270 | replicas_to_aggregate=FLAGS.replicas_to_aggregate, 271 | variable_averages=variable_averages, 272 | variables_to_average=moving_average_variables, 273 | replica_id=tf.constant(FLAGS.task, tf.int32, shape=()), 274 | total_num_replicas=FLAGS.worker_replicas) 275 | elif FLAGS.moving_average_decay: 276 | # Update ops executed locally by trainer. 277 | update_ops.append(variable_averages.apply(moving_average_variables)) 278 | 279 | # Variables to train. 280 | variables_to_train = _get_variables_to_train() 281 | 282 | # and returns a train_tensor and summary_op 283 | # total_loss is the sum of all LOSSES and REGULARIZATION_LOSSES in tf.GraphKeys 284 | total_loss, clones_gradients = model_deploy.optimize_clones( 285 | clones, 286 | optimizer, 287 | var_list=variables_to_train) 288 | # Add total_loss to summary. 289 | summaries.add(tf.summary.scalar('total_loss', total_loss)) 290 | 291 | # Create gradient updates. 292 | grad_updates = optimizer.apply_gradients(clones_gradients, 293 | global_step=global_step) 294 | update_ops.append(grad_updates) 295 | 296 | update_op = tf.group(*update_ops) 297 | train_tensor = control_flow_ops.with_dependencies([update_op], total_loss, 298 | name='train_op') 299 | 300 | train_tensor_list = [train_tensor] 301 | format_str = 'step %d, loss = %.2f' 302 | 303 | for loss_key in sorted(loss_dict.keys()): 304 | train_tensor_list.append(loss_dict[loss_key]) 305 | format_str += (', %s_loss = ' % loss_key + '%.8f') 306 | 307 | format_str += ' (%.1f examples/sec; %.3f sec/batch)' 308 | 309 | # Create a saver. 310 | saver = tf.train.Saver(tf.global_variables(),max_to_keep=1) 311 | checkpoint_path = os.path.join(FLAGS.train_dir, 'model.ckpt') 312 | 313 | ########################### 314 | # Kicks off the training. # 315 | ########################### 316 | # Build an initialization operation to run below. 317 | init = tf.global_variables_initializer() 318 | 319 | # Start running operations on the Graph. allow_soft_placement must be set to 320 | # True to build towers on GPU, as some of the ops do not have GPU 321 | # implementations. 322 | sess = tf.Session(config=tf.ConfigProto( 323 | allow_soft_placement=True, 324 | log_device_placement=FLAGS.log_device_placement)) 325 | sess.run(init) 326 | 327 | # load pretrained weights 328 | if FLAGS.checkpoint_path is not None: 329 | print("Load the pretrained weights") 330 | weight_ini_fn = _get_init_fn() 331 | weight_ini_fn(sess) 332 | else: 333 | print("Train from the scratch") 334 | 335 | # Start the queue runners. 336 | tf.train.start_queue_runners(sess=sess) 337 | 338 | 339 | # for step in xrange(FLAGS.max_number_of_steps): 340 | for step in xrange(FLAGS.max_number_of_steps + 1): 341 | start_time = time.time() 342 | 343 | loss_value_list = sess.run(train_tensor_list, feed_dict=dataset.get_feed_dict()) 344 | 345 | duration = time.time() - start_time 346 | 347 | if step % FLAGS.log_every_n_steps == 0: 348 | num_examples_per_step = FLAGS.batch_size 349 | examples_per_sec = num_examples_per_step / duration 350 | sec_per_batch = duration 351 | 352 | print(format_str % tuple([step] + loss_value_list + [examples_per_sec, sec_per_batch])) 353 | 354 | if step % FLAGS.model_snapshot_steps == 0: 355 | saver.save(sess, checkpoint_path, global_step=step) 356 | 357 | print('OK...') 358 | 359 | 360 | if __name__ == '__main__': 361 | tf.app.run() 362 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """SketchRNN data loading and image manipulation utilities.""" 15 | 16 | import os 17 | import h5py 18 | import tensorflow as tf 19 | 20 | from tensorflow.python.ops.losses.losses_impl import Reduction, compute_weighted_loss 21 | from tensorflow.python.framework import ops 22 | import numpy as np 23 | 24 | tfd = tf.contrib.distributions 25 | slim = tf.contrib.slim 26 | FLAGS = tf.app.flags.FLAGS 27 | 28 | try: 29 | from tabulate import tabulate 30 | except: 31 | print "tabulate lib not installed" 32 | 33 | 34 | class Tee(object): 35 | def __init__(self, *files): 36 | self.files = files 37 | 38 | def write(self, obj): 39 | for f in self.files: 40 | f.write(obj) 41 | f.flush() # If you want the output to be visible immediately 42 | 43 | def flush(self) : 44 | for f in self.files: 45 | f.flush() 46 | 47 | 48 | def config_and_print_log(FLAGS): 49 | _config_pretrain_model(FLAGS) 50 | log_dir = FLAGS.train_dir 51 | sub_dir_list = [FLAGS.sub_dir] 52 | if not FLAGS.use_clf: 53 | sub_dir_list.append('nclf') 54 | if FLAGS.checkpoint_path is None: 55 | sub_dir_list.append('ts') 56 | if FLAGS.rand_erase: 57 | sub_dir_list.append('re') 58 | if FLAGS.hd_data: 59 | sub_dir_list.append('hd') 60 | FLAGS.sub_dir = '_'.join(sub_dir_list) 61 | if FLAGS.dataset_name.lower() != 'market': 62 | FLAGS.sub_dir += '_' + FLAGS.dataset_name.lower() 63 | if FLAGS.sub_dir: 64 | FLAGS.train_dir = os.path.join(FLAGS.train_dir, FLAGS.sub_dir) 65 | if FLAGS.resume_train: 66 | FLAGS.train_dir = FLAGS.train_dir+'_con' 67 | if FLAGS.sampled_ce_loss_weight: 68 | FLAGS.train_dir = FLAGS.train_dir+'_scelw'+str(FLAGS.sampled_ce_loss_weight) 69 | if FLAGS.sample_number: 70 | FLAGS.train_dir = FLAGS.train_dir+'_sample'+str(FLAGS.sample_number) 71 | if FLAGS.target: 72 | FLAGS.train_dir = FLAGS.train_dir + '_target'+FLAGS.target 73 | if '_0.' in FLAGS.set: 74 | FLAGS.train_dir = FLAGS.train_dir + '_noise_'+FLAGS.set.split('train_')[1] 75 | if FLAGS.standard: 76 | FLAGS.train_dir = FLAGS.train_dir+'_standard' 77 | if FLAGS.entropy_loss: 78 | FLAGS.train_dir = FLAGS.train_dir+'_entropy' 79 | 80 | log_prefix = 'logs/' + FLAGS.dataset_name.lower() + '_%s_' % FLAGS.sub_dir 81 | log_prefix = os.path.join(log_dir, log_prefix) 82 | print_log(log_prefix, FLAGS) 83 | 84 | 85 | def config_eval_ckpt_path(FLAGS, flag=1): 86 | _config_pretrain_model(FLAGS, is_train=False) 87 | full_ckpt_path = FLAGS.sub_dir 88 | return full_ckpt_path 89 | 90 | 91 | def _config_pretrain_model(FLAGS, is_train=True): 92 | pretrian_dir = './pretrained_model' 93 | # pretrained_model = {'resnet':'resnet_v1_50.ckpt', 'inceptionv1':'inception_v1.ckpt', 'mobilenet':'mobile_net.ckpt'} 94 | pretrained_model = {'resnet_v1_50':'resnet_v1_50.ckpt', 'resnet_v2':'resnet_v2_50.ckpt', 'resnet_v1_distributions_50':'resnet_v1_50.ckpt', 'resnet_v1_distributions_baseline_50':'resnet_v1_50.ckpt',} 95 | model_scopes = {'resnet_v1_50': 'resnet_v1_50', 'resnet_v2': 'resnet_v2_50', 'resnet_v1_distributions_50': 'resnet_v1_50', 'resnet_v1_distributions_baseline_50': 'resnet_v1_50'} 96 | checkpoint_exclude_scopes = {'resnet_v1': ['logits', 'concat_comb', 'fusion'], 'resnet_v1_distributions_50': [], 'resnet_v1_distributions_baseline_50': [],} 97 | shared_checkpoint_exclude_scopes = ['verifier'] 98 | for model_key in pretrained_model.keys(): 99 | if model_key == FLAGS.model_name: 100 | if FLAGS.imagenet_pretrain and is_train: 101 | FLAGS.checkpoint_path = os.path.join(pretrian_dir, pretrained_model[model_key]) 102 | if len(FLAGS.sub_dir) == 0: 103 | FLAGS.sub_dir = model_key 104 | print "Set sub_dir to %s" % model_key 105 | if len(FLAGS.model_scope) == 0: 106 | FLAGS.model_scope = model_scopes[model_key] 107 | print "Set model scope to %s" % model_scopes[model_key] 108 | if len(FLAGS.checkpoint_exclude_scopes) == 0 and is_train: 109 | FLAGS.checkpoint_exclude_scopes = checkpoint_exclude_scopes[model_key] 110 | FLAGS.checkpoint_exclude_scopes.extend(shared_checkpoint_exclude_scopes) 111 | print "Set checkpoint exclude scopes to :", checkpoint_exclude_scopes[model_key] 112 | 113 | 114 | def _configure_learning_rate(num_samples_per_epoch, global_step, FLAGS): 115 | """Configures the learning rate. 116 | 117 | Args: 118 | num_samples_per_epoch: The number of samples in each epoch of training. 119 | global_step: The global_step tensor. 120 | 121 | Returns: 122 | A `Tensor` representing the learning rate. 123 | 124 | Raises: 125 | ValueError: if 126 | """ 127 | decay_steps = int(num_samples_per_epoch / FLAGS.batch_size * 128 | FLAGS.num_epochs_per_decay) 129 | if FLAGS.sync_replicas: 130 | decay_steps /= FLAGS.replicas_to_aggregate 131 | 132 | if FLAGS.learning_rate_decay_type == 'exponential': 133 | return tf.train.exponential_decay(FLAGS.learning_rate, 134 | global_step, 135 | decay_steps, 136 | FLAGS.learning_rate_decay_factor, 137 | staircase=True, 138 | name='exponential_decay_learning_rate') 139 | else: 140 | raise ValueError('learning_rate_decay_type [%s] was not recognized', 141 | FLAGS.learning_rate_decay_type) 142 | 143 | 144 | def _configure_optimizer(learning_rate): 145 | """Configures the optimizer used for training. 146 | 147 | Args: 148 | learning_rate: A scalar or `Tensor` learning rate. 149 | 150 | Returns: 151 | An instance of an optimizer. 152 | 153 | Raises: 154 | ValueError: if FLAGS.optimizer is not recognized. 155 | """ 156 | 157 | if FLAGS.optimizer == 'adam': 158 | optimizer = tf.train.AdamOptimizer( 159 | learning_rate, 160 | beta1=FLAGS.adam_beta1, 161 | beta2=FLAGS.adam_beta2, 162 | epsilon=FLAGS.opt_epsilon) 163 | else: 164 | raise ValueError('Optimizer [%s] was not recognized', FLAGS.optimizer) 165 | return optimizer 166 | 167 | 168 | def _get_init_fn(): 169 | """Returns a function run by the chief worker to warm-start the training. 170 | 171 | Note that the init_fn is only run when initializing the model during the very 172 | first global step. 173 | 174 | Returns: 175 | An init function run by the supervisor. 176 | """ 177 | if FLAGS.checkpoint_path is None: 178 | return None 179 | 180 | # Warn the user if a checkpoint exists in the train_dir. Then we'll be 181 | # ignoring the checkpoint anyway. 182 | #if tf.train.latest_checkpoint(FLAGS.train_dir) and FLAGS.resume_train: 183 | # print('Ignoring --checkpoint_path because a checkpoint already exists in %s'% FLAGS.train_dir) 184 | # return None 185 | 186 | exclusions = [] 187 | model_scope = FLAGS.model_scope 188 | if FLAGS.checkpoint_exclude_scopes: 189 | exclusions = [model_scope + '/' + scope.strip() for scope in FLAGS.checkpoint_exclude_scopes] 190 | exclusions.append('instance/') 191 | 192 | if FLAGS.resume_train: 193 | FLAGS.checkpoint_path = FLAGS.checkpoint_path2 194 | 195 | # TODO(sguada) variables.filter_variables() 196 | 197 | 198 | variables_to_restore = [] 199 | for var in slim.get_model_variables(): 200 | excluded = False 201 | for exclusion in exclusions: 202 | if var.op.name.startswith(exclusion): 203 | excluded = True 204 | break 205 | if not excluded: 206 | variables_to_restore.append(var) 207 | #print var 208 | 209 | if tf.gfile.IsDirectory(FLAGS.checkpoint_path): 210 | checkpoint_path = tf.train.latest_checkpoint(FLAGS.checkpoint_path) 211 | else: 212 | checkpoint_path = FLAGS.checkpoint_path 213 | 214 | print('Fine-tuning from %s' % checkpoint_path) 215 | 216 | return slim.assign_from_checkpoint_fn( 217 | checkpoint_path, 218 | variables_to_restore, 219 | ignore_missing_vars=FLAGS.ignore_missing_vars) 220 | 221 | 222 | def _get_variables_to_train(): 223 | """Returns a list of variables to train. 224 | 225 | Returns: 226 | A list of variables to train by the optimizer. 227 | """ 228 | tmp = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES) 229 | if FLAGS.trainable_scopes is None: 230 | return tf.trainable_variables() 231 | else: 232 | scopes = [scope.strip() for scope in FLAGS.trainable_scopes.split(',')] 233 | 234 | variables_to_train = [] 235 | for scope in scopes: 236 | variables = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope) 237 | variables_to_train.extend(variables) 238 | return variables_to_train 239 | 240 | 241 | def print_log(log_prefix, FLAGS): 242 | # print header 243 | print "===============================================" 244 | print "Trainning ", FLAGS.model_name, " in this framework" 245 | print "===============================================" 246 | 247 | print "Tensorflow flags:" 248 | 249 | flags_list = [] 250 | for attr, value in sorted(FLAGS.__flags.items()): 251 | flags_list.append(attr) 252 | FLAGS.saved_flags = " ".join(flags_list) 253 | 254 | flag_table = {} 255 | flag_table['FLAG_NAME'] = [] 256 | flag_table['Value'] = [] 257 | flag_lists = FLAGS.saved_flags.split() 258 | # print self.FLAGS.__flags 259 | for attr in flag_lists: 260 | if attr not in ['saved_flags', 'net_name', 'log_root']: 261 | flag_table['FLAG_NAME'].append(attr.upper()) 262 | flag_table['Value'].append(getattr(FLAGS, attr)) 263 | flag_table['FLAG_NAME'].append('HOST_NAME') 264 | flag_table['Value'].append(os.uname()[1].split('.')[0]) 265 | try: 266 | print tabulate(flag_table, headers="keys", tablefmt="fancy_grid").encode('utf-8') 267 | except: 268 | for attr in flag_lists: 269 | print "attr name, ", attr.upper() 270 | print "attr value, ", getattr(FLAGS, attr) 271 | 272 | 273 | def get_img_func(is_training=True): 274 | from ReID_preprocessing import preprocessing_factory 275 | preprocessing_name = FLAGS.preprocessing_name or FLAGS.model_name 276 | image_preprocessing_fn = preprocessing_factory.get_preprocessing( 277 | preprocessing_name, 278 | aug_mode=FLAGS.aug_mode, 279 | test_mode=FLAGS.test_mode, 280 | is_training=is_training, 281 | rand_erase=FLAGS.rand_erase, 282 | ) 283 | 284 | def callback(images): 285 | return image_preprocessing_fn(images, FLAGS.train_image_height, FLAGS.train_image_width) 286 | 287 | return callback 288 | 289 | 290 | def loss_entropy( 291 | mu, sig, weights=0.001, label_smoothing=0, scope=None, 292 | loss_collection=ops.GraphKeys.LOSSES, 293 | reduction=Reduction.SUM_BY_NONZERO_WEIGHTS): 294 | 295 | with ops.name_scope(scope, "entropy_loss", 296 | (mu, sig, weights)) as scope: 297 | 298 | sigma_avg = 5 299 | threshold = np.log(sigma_avg) + (1 + np.log(2 * np.pi)) / 2 300 | losses = tf.reduce_mean(tf.nn.relu(threshold-tfd.MultivariateNormalDiagWithSoftplusScale(loc=mu, scale_diag=sig).entropy()/2048)) 301 | 302 | return compute_weighted_loss( 303 | losses, weights, scope, loss_collection, reduction=reduction) 304 | 305 | 306 | def build_graph(tf_batch_queue, network_fn): 307 | 308 | images, labels = tf_batch_queue[:2] 309 | 310 | if 'distribution' in FLAGS.model_name and 'baseline' not in FLAGS.model_name: 311 | logits, logits2, end_points = network_fn(images) 312 | else: 313 | logits, end_points = network_fn(images) 314 | 315 | ############################# 316 | # Specify the loss function # 317 | ############################# 318 | if FLAGS.use_clf: 319 | if 'AuxLogits' in end_points: 320 | tf.losses.softmax_cross_entropy( 321 | logits=end_points['AuxLogits'], onehot_labels=labels, 322 | label_smoothing=FLAGS.label_smoothing, weights=0.4, scope='aux_loss') 323 | tf.losses.softmax_cross_entropy( 324 | logits=logits, onehot_labels=labels, 325 | label_smoothing=FLAGS.label_smoothing, weights=FLAGS.boot_weight) 326 | if 'distribution' in FLAGS.model_name and 'baseline' not in FLAGS.model_name: 327 | for logits_ in logits2: 328 | tf.losses.softmax_cross_entropy( 329 | logits=logits_, onehot_labels=labels, 330 | label_smoothing=FLAGS.label_smoothing, weights=FLAGS.sampled_ce_loss_weight) 331 | 332 | if FLAGS.entropy_loss: 333 | mu = end_points['PreLogits_mean'] 334 | sig = end_points['PreLogits_sig'] 335 | loss_entropy(mu, sig) 336 | 337 | return end_points 338 | 339 | 340 | def get_pair_type(is_training=True): 341 | if is_training: 342 | pair_type = 'single' 343 | else: 344 | pair_type = 'eval' 345 | return pair_type 346 | 347 | --------------------------------------------------------------------------------