├── .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 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
10 |
11 |
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 |
--------------------------------------------------------------------------------