├── .gitignore ├── README.md ├── inception ├── __init__.py └── slim │ ├── BUILD │ ├── README.md │ ├── __init__.py │ ├── collections_test.py │ ├── inception_model.py │ ├── inception_test.py │ ├── losses.py │ ├── losses_test.py │ ├── ops.py │ ├── ops_test.py │ ├── scopes.py │ ├── scopes_test.py │ ├── slim.py │ ├── variables.py │ └── variables_test.py └── inception_score.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StackGAN Inception Evaluation Model 2 | - [StackGAN-pytorch](https://github.com/hanzhanggit/StackGAN-Pytorch) 3 | - [StackGAN-tensorflow](https://github.com/hanzhanggit/StackGAN) 4 | - [StackGAN-v2-pytorch](https://github.com/hanzhanggit/StackGAN-v2) 5 | 6 | Inception evaluation model for reproducing main results in the paper [StackGAN: Text to Photo-realistic Image Synthesis with Stacked Generative Adversarial Networks](https://arxiv.org/pdf/1612.03242v2.pdf) by Han Zhang, Tao Xu, Hongsheng Li, Shaoting Zhang, Xiaogang Wang, Xiaolei Huang, Dimitris Metaxas. 7 | 8 | 9 | ### Dependencies 10 | - python 2.7 11 | - TensorFlow 12 | 13 | ### Inception Model 14 | 15 | - [StackGAN inception model for birds and flowers evaluation](https://drive.google.com/open?id=0B3y_msrWZaXLMzNMNWhWdW0zVWs). Download and save it in the current folder 16 | - StackGAN for COCO. We use the model pretrained on the ImageNet. Same as the one used in [Improved GAN](https://github.com/openai/improved-gan/tree/master/inception_score). 17 | 18 | 19 | 20 | ### To get the inception score 21 | python inception_score.py --image_folder IMAGE_FOLDER_PATH 22 | 23 | 24 | 25 | 26 | ### Citing StackGAN 27 | If you find StackGAN useful in your research, please consider citing: 28 | 29 | ``` 30 | @inproceedings{han2017stackgan, 31 | Author = {Han Zhang and Tao Xu and Hongsheng Li and Shaoting Zhang and Xiaogang Wang and Xiaolei Huang and Dimitris Metaxas}, 32 | Title = {StackGAN: Text to Photo-realistic Image Synthesis with Stacked Generative Adversarial Networks}, 33 | Year = {2017}, 34 | booktitle = {{ICCV}}, 35 | } 36 | ``` 37 | 38 | **References** 39 | 40 | - Generative Adversarial Text-to-Image Synthesis [Paper](https://arxiv.org/abs/1605.05396) [Code](https://github.com/reedscot/icml2016) 41 | - Learning Deep Representations of Fine-grained Visual Descriptions [Paper](https://arxiv.org/abs/1605.05395) [Code](https://github.com/reedscot/cvpr2016) 42 | - Improved Techniques for Training GANs[ Paper](https://arxiv.org/abs/1606.03498) [Code](https://github.com/openai/improved-gan) 43 | 44 | -------------------------------------------------------------------------------- /inception/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | -------------------------------------------------------------------------------- /inception/slim/BUILD: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Contains the operations and nets for building TensorFlow-Slim models. 3 | 4 | package(default_visibility = ["//inception:internal"]) 5 | 6 | licenses(["notice"]) # Apache 2.0 7 | 8 | exports_files(["LICENSE"]) 9 | 10 | py_library( 11 | name = "scopes", 12 | srcs = ["scopes.py"], 13 | ) 14 | 15 | py_test( 16 | name = "scopes_test", 17 | size = "small", 18 | srcs = ["scopes_test.py"], 19 | deps = [ 20 | ":scopes", 21 | ], 22 | ) 23 | 24 | py_library( 25 | name = "variables", 26 | srcs = ["variables.py"], 27 | deps = [ 28 | ":scopes", 29 | ], 30 | ) 31 | 32 | py_test( 33 | name = "variables_test", 34 | size = "small", 35 | srcs = ["variables_test.py"], 36 | deps = [ 37 | ":variables", 38 | ], 39 | ) 40 | 41 | py_library( 42 | name = "losses", 43 | srcs = ["losses.py"], 44 | ) 45 | 46 | py_test( 47 | name = "losses_test", 48 | size = "small", 49 | srcs = ["losses_test.py"], 50 | deps = [ 51 | ":losses", 52 | ], 53 | ) 54 | 55 | py_library( 56 | name = "ops", 57 | srcs = ["ops.py"], 58 | deps = [ 59 | ":losses", 60 | ":scopes", 61 | ":variables", 62 | ], 63 | ) 64 | 65 | py_test( 66 | name = "ops_test", 67 | size = "small", 68 | srcs = ["ops_test.py"], 69 | deps = [ 70 | ":ops", 71 | ":variables", 72 | ], 73 | ) 74 | 75 | py_library( 76 | name = "inception", 77 | srcs = ["inception_model.py"], 78 | deps = [ 79 | ":ops", 80 | ":scopes", 81 | ], 82 | ) 83 | 84 | py_test( 85 | name = "inception_test", 86 | size = "medium", 87 | srcs = ["inception_test.py"], 88 | deps = [ 89 | ":inception", 90 | ], 91 | ) 92 | 93 | py_library( 94 | name = "slim", 95 | srcs = ["slim.py"], 96 | deps = [ 97 | ":inception", 98 | ":losses", 99 | ":ops", 100 | ":scopes", 101 | ":variables", 102 | ], 103 | ) 104 | 105 | py_test( 106 | name = "collections_test", 107 | size = "small", 108 | srcs = ["collections_test.py"], 109 | deps = [ 110 | ":slim", 111 | ], 112 | ) 113 | -------------------------------------------------------------------------------- /inception/slim/README.md: -------------------------------------------------------------------------------- 1 | # TensorFlow-Slim 2 | 3 | TF-Slim is a lightweight library for defining, training and evaluating models in 4 | TensorFlow. It enables defining complex networks quickly and concisely while 5 | keeping a model's architecture transparent and its hyperparameters explicit. 6 | 7 | [TOC] 8 | 9 | ## Teaser 10 | 11 | As a demonstration of the simplicity of using TF-Slim, compare the simplicity of 12 | the code necessary for defining the entire [VGG] 13 | (http://www.robots.ox.ac.uk/~vgg/research/very_deep/) network using TF-Slim to 14 | the lengthy and verbose nature of defining just the first three layers (out of 15 | 16) using native tensorflow: 16 | 17 | ```python{.good} 18 | # VGG16 in TF-Slim. 19 | def vgg16(inputs): 20 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], stddev=0.01, weight_decay=0.0005): 21 | net = slim.ops.repeat_op(2, inputs, slim.ops.conv2d, 64, [3, 3], scope='conv1') 22 | net = slim.ops.max_pool(net, [2, 2], scope='pool1') 23 | net = slim.ops.repeat_op(2, net, slim.ops.conv2d, 128, [3, 3], scope='conv2') 24 | net = slim.ops.max_pool(net, [2, 2], scope='pool2') 25 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 256, [3, 3], scope='conv3') 26 | net = slim.ops.max_pool(net, [2, 2], scope='pool3') 27 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 512, [3, 3], scope='conv4') 28 | net = slim.ops.max_pool(net, [2, 2], scope='pool4') 29 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 512, [3, 3], scope='conv5') 30 | net = slim.ops.max_pool(net, [2, 2], scope='pool5') 31 | net = slim.ops.flatten(net, scope='flatten5') 32 | net = slim.ops.fc(net, 4096, scope='fc6') 33 | net = slim.ops.dropout(net, 0.5, scope='dropout6') 34 | net = slim.ops.fc(net, 4096, scope='fc7') 35 | net = slim.ops.dropout(net, 0.5, scope='dropout7') 36 | net = slim.ops.fc(net, 1000, activation=None, scope='fc8') 37 | return net 38 | ``` 39 | 40 | ```python{.bad} 41 | # Layers 1-3 (out of 16) of VGG16 in native tensorflow. 42 | def vgg16(inputs): 43 | with tf.name_scope('conv1_1') as scope: 44 | kernel = tf.Variable(tf.truncated_normal([3, 3, 3, 64], dtype=tf.float32, stddev=1e-1), name='weights') 45 | conv = tf.nn.conv2d(inputs, kernel, [1, 1, 1, 1], padding='SAME') 46 | biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32), trainable=True, name='biases') 47 | bias = tf.nn.bias_add(conv, biases) 48 | conv1 = tf.nn.relu(bias, name=scope) 49 | with tf.name_scope('conv1_2') as scope: 50 | kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 64], dtype=tf.float32, stddev=1e-1), name='weights') 51 | conv = tf.nn.conv2d(images, kernel, [1, 1, 1, 1], padding='SAME') 52 | biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32), trainable=True, name='biases') 53 | bias = tf.nn.bias_add(conv, biases) 54 | conv1 = tf.nn.relu(bias, name=scope) 55 | with tf.name_scope('pool1') 56 | pool1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1') 57 | ``` 58 | 59 | ## Why TF-Slim? 60 | 61 | TF-Slim offers several advantages over just the built-in tensorflow libraries: 62 | 63 | * Allows one to define models much more compactly by eliminating boilerplate 64 | code. This is accomplished through the use of [argument scoping](scopes.py) 65 | and numerous high level [operations](ops.py). These tools increase 66 | readability and maintainability, reduce the likelihood of an error from 67 | copy-and-pasting hyperparameter values and simplifies hyperparameter tuning. 68 | * Makes developing models simple by providing commonly used [loss functions] 69 | (losses.py) 70 | * Provides a concise [definition](inception_model.py) of [Inception v3] 71 | (http://arxiv.org/abs/1512.00567) network architecture ready to be used 72 | out-of-the-box or subsumed into new models. 73 | 74 | Additionally TF-Slim was designed with several principles in mind: 75 | 76 | * The various modules of TF-Slim (scopes, variables, ops, losses) are 77 | independent. This flexibility allows users to pick and choose components of 78 | TF-Slim completely à la carte. 79 | * TF-Slim is written using a Functional Programming style. That means it's 80 | super-lightweight and can be used right alongside any of TensorFlow's native 81 | operations. 82 | * Makes re-using network architectures easy. This allows users to build new 83 | networks on top of existing ones as well as fine-tuning pre-trained models 84 | on new tasks. 85 | 86 | ## What are the various components of TF-Slim? 87 | 88 | TF-Slim is composed of several parts which were designed to exist independently. 89 | These include: 90 | 91 | * [scopes.py](./scopes.py): provides a new scope named `arg_scope` that allows 92 | a user to define default arguments for specific operations within that 93 | scope. 94 | * [variables.py](./variables.py): provides convenience wrappers for variable 95 | creation and manipulation. 96 | * [ops.py](./ops.py): provides high level operations for building models using 97 | tensorflow. 98 | * [losses.py](./losses.py): contains commonly used loss functions. 99 | 100 | ## Defining Models 101 | 102 | Models can be succinctly defined using TF-Slim by combining its variables, 103 | operations and scopes. Each of these elements are defined below. 104 | 105 | ### Variables 106 | 107 | Creating [`Variables`](https://www.tensorflow.org/how_tos/variables/index.html) 108 | in native tensorflow requires either a predefined value or an initialization 109 | mechanism (random, normally distributed). Furthermore, if a variable needs to be 110 | created on a specific device, such as a GPU, the specification must be [made 111 | explicit](https://www.tensorflow.org/how_tos/using_gpu/index.html). To alleviate 112 | the code required for variable creation, TF-Slim provides a set of thin wrapper 113 | functions in [variables.py](./variables.py) which allow callers to easily define 114 | variables. 115 | 116 | For example, to create a `weight` variable, initialize it using a truncated 117 | normal distribution, regularize it with an `l2_loss` and place it on the `CPU`, 118 | one need only declare the following: 119 | 120 | ```python 121 | weights = variables.variable('weights', 122 | shape=[10, 10, 3 , 3], 123 | initializer=tf.truncated_normal_initializer(stddev=0.1), 124 | regularizer=lambda t: losses.l2_loss(t, weight=0.05), 125 | device='/cpu:0') 126 | ``` 127 | 128 | In addition to the functionality provided by `tf.Variable`, `slim.variables` 129 | keeps track of the variables created by `slim.ops` to define a model, which 130 | allows one to distinguish variables that belong to the model versus other 131 | variables. 132 | 133 | ```python 134 | # Get all the variables defined by the model. 135 | model_variables = slim.variables.get_variables() 136 | 137 | # Get all the variables with the same given name, i.e. 'weights', 'biases'. 138 | weights = slim.variables.get_variables_by_name('weights') 139 | biases = slim.variables.get_variables_by_name('biases') 140 | 141 | # Get all the variables in VARIABLES_TO_RESTORE collection. 142 | variables_to_restore = tf.get_collection(slim.variables.VARIABLES_TO_RESTORE) 143 | 144 | 145 | weights = variables.variable('weights', 146 | shape=[10, 10, 3 , 3], 147 | initializer=tf.truncated_normal_initializer(stddev=0.1), 148 | regularizer=lambda t: losses.l2_loss(t, weight=0.05), 149 | device='/cpu:0') 150 | ``` 151 | 152 | ### Operations (Layers) 153 | 154 | While the set of TensorFlow operations is quite extensive, builders of neural 155 | networks typically think of models in terms of "layers". A layer, such as a 156 | Convolutional Layer, a Fully Connected Layer or a BatchNorm Layer are more 157 | abstract than a single TensorFlow operation and typically involve many such 158 | operations. For example, a Convolutional Layer in a neural network is built 159 | using several steps: 160 | 161 | 1. Creating the weight variables 162 | 2. Creating the bias variables 163 | 3. Convolving the weights with the input from the previous layer 164 | 4. Adding the biases to the result of the convolution. 165 | 166 | In python code this can be rather laborious: 167 | 168 | ```python 169 | input = ... 170 | with tf.name_scope('conv1_1') as scope: 171 | kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32, 172 | stddev=1e-1), name='weights') 173 | conv = tf.nn.conv2d(input, kernel, [1, 1, 1, 1], padding='SAME') 174 | biases = tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32), 175 | trainable=True, name='biases') 176 | bias = tf.nn.bias_add(conv, biases) 177 | conv1 = tf.nn.relu(bias, name=scope) 178 | ``` 179 | 180 | To alleviate the need to duplicate this code repeatedly, TF-Slim provides a 181 | number of convenient operations defined at the (more abstract) level of neural 182 | network layers. For example, compare the code above to an invocation of the 183 | TF-Slim code: 184 | 185 | ```python 186 | input = ... 187 | net = slim.ops.conv2d(input, [3, 3], 128, scope='conv1_1') 188 | ``` 189 | 190 | TF-Slim provides numerous operations used in building neural networks which 191 | roughly correspond to such layers. These include: 192 | 193 | Layer | TF-Slim Op 194 | --------------------- | ------------------------ 195 | Convolutional Layer | [ops.conv2d](ops.py) 196 | Fully Connected Layer | [ops.fc](ops.py) 197 | BatchNorm layer | [ops.batch_norm](ops.py) 198 | Max Pooling Layer | [ops.max_pool](ops.py) 199 | Avg Pooling Layer | [ops.avg_pool](ops.py) 200 | Dropout Layer | [ops.dropout](ops.py) 201 | 202 | [ops.py](./ops.py) also includes operations that are not really "layers" per se, 203 | but are often used to manipulate hidden unit representations during inference: 204 | 205 | Operation | TF-Slim Op 206 | --------- | --------------------- 207 | Flatten | [ops.flatten](ops.py) 208 | 209 | TF-Slim also provides a meta-operation called `repeat_op` that allows one to 210 | repeatedly perform the same operation. Consider the following snippet from the 211 | [VGG](https://www.robots.ox.ac.uk/~vgg/research/very_deep/) network whose layers 212 | perform several convolutions in a row between pooling layers: 213 | 214 | ```python 215 | net = ... 216 | net = slim.ops.conv2d(net, 256, [3, 3], scope='conv3_1') 217 | net = slim.ops.conv2d(net, 256, [3, 3], scope='conv3_2') 218 | net = slim.ops.conv2d(net, 256, [3, 3], scope='conv3_3') 219 | net = slim.ops.max_pool(net, [2, 2], scope='pool3') 220 | ``` 221 | 222 | This clear duplication of code can be removed via a standard loop: 223 | 224 | ```python 225 | net = ... 226 | for i in range(3): 227 | net = slim.ops.conv2d(net, 256, [3, 3], scope='conv3_' % (i+1)) 228 | net = slim.ops.max_pool(net, [2, 2], scope='pool3') 229 | ``` 230 | 231 | While this does reduce the amount of duplication, it can be made even cleaner by 232 | using the `RepeatOp`: 233 | 234 | ```python 235 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 256, [3, 3], scope='conv3') 236 | net = slim.ops.max_pool(net, [2, 2], scope='pool2') 237 | ``` 238 | 239 | Notice that the RepeatOp not only applies the same argument in-line, it also is 240 | smart enough to unroll the scopes such that the scopes assigned to each 241 | subsequent call of `ops.conv2d` is appended with an underscore and iteration 242 | number. More concretely, the scopes in the example above would be 'conv3_1', 243 | 'conv3_2' and 'conv3_3'. 244 | 245 | ### Scopes 246 | 247 | In addition to the types of scope mechanisms in TensorFlow ([name_scope] 248 | (https://www.tensorflow.org/api_docs/python/framework.html#name_scope), 249 | [variable_scope] 250 | (https://www.tensorflow.org/api_docs/python/state_ops.html#variable_scope), 251 | TF-Slim adds a new scoping mechanism called "argument scope" or [arg_scope] 252 | (scopes.py). This new scope allows a user to specify one or more operations and 253 | a set of arguments which will be passed to each of the operations defined in the 254 | `arg_scope`. This functionality is best illustrated by example. Consider the 255 | following code snippet: 256 | 257 | ```python 258 | net = slim.ops.conv2d(inputs, 64, [11, 11], 4, padding='SAME', stddev=0.01, weight_decay=0.0005, scope='conv1') 259 | net = slim.ops.conv2d(net, 128, [11, 11], padding='VALID', stddev=0.01, weight_decay=0.0005, scope='conv2') 260 | net = slim.ops.conv2d(net, 256, [11, 11], padding='SAME', stddev=0.01, weight_decay=0.0005, scope='conv3') 261 | ``` 262 | 263 | It should be clear that these three Convolution layers share many of the same 264 | hyperparameters. Two have the same padding, all three have the same weight_decay 265 | and standard deviation of its weights. Not only do the duplicated values make 266 | the code more difficult to read, it also adds the addition burder to the writer 267 | of needing to doublecheck that all of the values are identical in each step. One 268 | solution would be to specify default values using variables: 269 | 270 | ```python 271 | padding='SAME' 272 | stddev=0.01 273 | weight_decay=0.0005 274 | net = slim.ops.conv2d(inputs, 64, [11, 11], 4, padding=padding, stddev=stddev, weight_decay=weight_decay, scope='conv1') 275 | net = slim.ops.conv2d(net, 128, [11, 11], padding='VALID', stddev=stddev, weight_decay=weight_decay, scope='conv2') 276 | net = slim.ops.conv2d(net, 256, [11, 11], padding=padding, stddev=stddev, weight_decay=weight_decay, scope='conv3') 277 | 278 | ``` 279 | 280 | This solution ensures that all three convolutions share the exact same variable 281 | values but doesn't reduce the code clutter. By using an `arg_scope`, we can both 282 | ensure that each layer uses the same values and simplify the code: 283 | 284 | ```python 285 | with slim.arg_scope([slim.ops.conv2d], padding='SAME', stddev=0.01, weight_decay=0.0005): 286 | net = slim.ops.conv2d(inputs, 64, [11, 11], scope='conv1') 287 | net = slim.ops.conv2d(net, 128, [11, 11], padding='VALID', scope='conv2') 288 | net = slim.ops.conv2d(net, 256, [11, 11], scope='conv3') 289 | ``` 290 | 291 | As the example illustrates, the use of arg_scope makes the code cleaner, simpler 292 | and easier to maintain. Notice that while argument values are specifed in the 293 | arg_scope, they can be overwritten locally. In particular, while the padding 294 | argument has been set to 'SAME', the second convolution overrides it with the 295 | value of 'VALID'. 296 | 297 | One can also nest `arg_scope`s and use multiple operations in the same scope. 298 | For example: 299 | 300 | ```python 301 | with arg_scope([slim.ops.conv2d, slim.ops.fc], stddev=0.01, weight_decay=0.0005): 302 | with arg_scope([slim.ops.conv2d], padding='SAME'), slim.arg_scope([slim.ops.fc], bias=1.0): 303 | net = slim.ops.conv2d(inputs, 64, [11, 11], 4, padding='VALID', scope='conv1') 304 | net = slim.ops.conv2d(net, 256, [5, 5], stddev=0.03, scope='conv2') 305 | net = slim.ops.flatten(net) 306 | net = slim.ops.fc(net, 1000, activation=None, scope='fc') 307 | ``` 308 | 309 | In this example, the first `arg_scope` applies the same `stddev` and 310 | `weight_decay` arguments to the `conv2d` and `fc` ops in its scope. In the 311 | second `arg_scope`, additional default arguments to `conv2d` only are specified. 312 | 313 | In addition to `arg_scope`, TF-Slim provides several decorators that wrap the 314 | use of tensorflow arg scopes. These include `@AddArgScope`, `@AddNameScope`, 315 | `@AddVariableScope`, `@AddOpScope` and `@AddVariableOpScope`. To illustrate 316 | their use, consider the following example. 317 | 318 | ```python 319 | def MyNewOp(inputs): 320 | varA = ... 321 | varB = ... 322 | outputs = tf.mul(varA, inputs) + varB 323 | return outputs 324 | 325 | ``` 326 | 327 | In this example, the user has created a new op which creates two variables. To 328 | ensure that these variables exist within a certain variable scope (to avoid 329 | collisions with variables with the same name), in standard TF, the op must be 330 | called within a variable scope: 331 | 332 | ```python 333 | inputs = ... 334 | with tf.variable_scope('layer1'): 335 | outputs = MyNewOp(inputs) 336 | ``` 337 | 338 | As an alternative, one can use TF-Slim's decorators to decorate the function and 339 | simplify the call: 340 | 341 | ```python 342 | @AddVariableScope 343 | def MyNewOp(inputs): 344 | ... 345 | return outputs 346 | 347 | 348 | inputs = ... 349 | outputs = MyNewOp('layer1') 350 | ``` 351 | 352 | The `@AddVariableScope` decorater simply applies the `tf.variable_scope` scoping 353 | to the called function taking "layer1" as its argument. This allows the code to 354 | be written more concisely. 355 | 356 | ### Losses 357 | 358 | The loss function defines a quantity that we want to minimize. For 359 | classification problems, this is typically the cross entropy between the true 360 | (one-hot) distribution and the predicted probability distribution across 361 | classes. For regression problems, this is often the sum-of-squares differences 362 | between the predicted and true values. 363 | 364 | Certain models, such as multi-task learning models, require the use of multiple 365 | loss functions simultaneously. In other words, the loss function ultimatey being 366 | minimized is the sum of various other loss functions. For example, consider a 367 | model that predicts both the type of scene in an image as well as the depth from 368 | the camera of each pixel. This model's loss function would be the sum of the 369 | classification loss and depth prediction loss. 370 | 371 | TF-Slim provides an easy-to-use mechanism for defining and keeping track of loss 372 | functions via the [losses.py](./losses.py) module. Consider the simple case 373 | where we want to train the VGG network: 374 | 375 | ```python 376 | # Load the images and labels. 377 | images, labels = ... 378 | 379 | # Create the model. 380 | predictions = ... 381 | 382 | # Define the loss functions and get the total loss. 383 | loss = losses.cross_entropy_loss(predictions, labels) 384 | ``` 385 | 386 | In this example, we start by creating the model (using TF-Slim's VGG 387 | implementation), and add the standard classification loss. Now, lets turn to the 388 | case where we have a multi-task model that produces multiple outputs: 389 | 390 | ```python 391 | # Load the images and labels. 392 | images, scene_labels, depth_labels = ... 393 | 394 | # Create the model. 395 | scene_predictions, depth_predictions = CreateMultiTaskModel(images) 396 | 397 | # Define the loss functions and get the total loss. 398 | classification_loss = slim.losses.cross_entropy_loss(scene_predictions, scene_labels) 399 | sum_of_squares_loss = slim.losses.l2loss(depth_predictions - depth_labels) 400 | 401 | # The following two lines have the same effect: 402 | total_loss1 = classification_loss + sum_of_squares_loss 403 | total_loss2 = tf.get_collection(slim.losses.LOSSES_COLLECTION) 404 | ``` 405 | 406 | In this example, we have two losses which we add by calling 407 | `losses.cross_entropy_loss` and `losses.l2loss`. We can obtain the 408 | total loss by adding them together (`total_loss1`) or by calling 409 | `losses.GetTotalLoss()`. How did this work? When you create a loss function via 410 | TF-Slim, TF-Slim adds the loss to a special TensorFlow collection of loss 411 | functions. This enables you to either manage the total loss manually, or allow 412 | TF-Slim to manage them for you. 413 | 414 | What if you want to let TF-Slim manage the losses for you but have a custom loss 415 | function? [losses.py](./losses.py) also has a function that adds this loss to 416 | TF-Slims collection. For example: 417 | 418 | ```python 419 | # Load the images and labels. 420 | images, scene_labels, depth_labels, pose_labels = ... 421 | 422 | # Create the model. 423 | scene_predictions, depth_predictions, pose_predictions = CreateMultiTaskModel(images) 424 | 425 | # Define the loss functions and get the total loss. 426 | classification_loss = slim.losses.cross_entropy_loss(scene_predictions, scene_labels) 427 | sum_of_squares_loss = slim.losses.l2loss(depth_predictions - depth_labels) 428 | pose_loss = MyCustomLossFunction(pose_predictions, pose_labels) 429 | tf.add_to_collection(slim.losses.LOSSES_COLLECTION, pose_loss) # Letting TF-Slim know about the additional loss. 430 | 431 | # The following two lines have the same effect: 432 | total_loss1 = classification_loss + sum_of_squares_loss + pose_loss 433 | total_loss2 = losses.GetTotalLoss() 434 | ``` 435 | 436 | In this example, we can again either produce the total loss function manually or 437 | let TF-Slim know about the additional loss and let TF-Slim handle the losses. 438 | 439 | ## Putting the Pieces Together 440 | 441 | By combining TF-Slim Variables, Operations and scopes, we can write a normally 442 | very complex network with very few lines of code. For example, the entire [VGG] 443 | (https://www.robots.ox.ac.uk/~vgg/research/very_deep/) architecture can be 444 | defined with just the following snippet: 445 | 446 | ```python 447 | with arg_scope([slim.ops.conv2d, slim.ops.fc], stddev=0.01, weight_decay=0.0005): 448 | net = slim.ops.repeat_op(1, inputs, slim.ops.conv2d, 64, [3, 3], scope='conv1') 449 | net = slim.ops.max_pool(net, [2, 2], scope='pool1') 450 | net = slim.ops.repeat_op(1, net, slim.ops.conv2d, 128, [3, 3], scope='conv2') 451 | net = slim.ops.max_pool(net, [2, 2], scope='pool2') 452 | net = slim.ops.repeat_op(2, net, slim.ops.conv2d, 256, [3, 3], scope='conv3') 453 | net = slim.ops.max_pool(net, [2, 2], scope='pool3') 454 | net = slim.ops.repeat_op(2, net, slim.ops.conv2d, 512, [3, 3], scope='conv4') 455 | net = slim.ops.max_pool(net, [2, 2], scope='pool4') 456 | net = slim.ops.repeat_op(2, net, slim.ops.conv2d, 512, [3, 3], scope='conv5') 457 | net = slim.ops.max_pool(net, [2, 2], scope='pool5') 458 | net = slim.ops.flatten(net, scope='flatten5') 459 | net = slim.ops.fc(net, 4096, scope='fc6') 460 | net = slim.ops.dropout(net, 0.5, scope='dropout6') 461 | net = slim.ops.fc(net, 4096, scope='fc7') 462 | net = slim.ops.dropout(net, 0.5, scope='dropout7') 463 | net = slim.ops.fc(net, 1000, activation=None, scope='fc8') 464 | return net 465 | ``` 466 | 467 | ## Re-using previously defined network architectures and pre-trained models. 468 | 469 | ### Brief Recap on Restoring Variables from a Checkpoint 470 | 471 | After a model has been trained, it can be restored using `tf.train.Saver()` 472 | which restores `Variables` from a given checkpoint. For many cases, 473 | `tf.train.Saver()` provides a simple mechanism to restore all or just a few 474 | variables. 475 | 476 | ```python 477 | # Create some variables. 478 | v1 = tf.Variable(..., name="v1") 479 | v2 = tf.Variable(..., name="v2") 480 | ... 481 | # Add ops to restore all the variables. 482 | restorer = tf.train.Saver() 483 | 484 | # Add ops to restore some variables. 485 | restorer = tf.train.Saver([v1, v2]) 486 | 487 | # Later, launch the model, use the saver to restore variables from disk, and 488 | # do some work with the model. 489 | with tf.Session() as sess: 490 | # Restore variables from disk. 491 | restorer.restore(sess, "/tmp/model.ckpt") 492 | print("Model restored.") 493 | # Do some work with the model 494 | ... 495 | ``` 496 | 497 | See [Restoring Variables] 498 | (https://www.tensorflow.org/versions/r0.7/how_tos/variables/index.html#restoring-variables) 499 | and [Choosing which Variables to Save and Restore] 500 | (https://www.tensorflow.org/versions/r0.7/how_tos/variables/index.html#choosing-which-variables-to-save-and-restore) 501 | sections of the [Variables] 502 | (https://www.tensorflow.org/versions/r0.7/how_tos/variables/index.html) page for 503 | more details. 504 | 505 | ### Using slim.variables to Track which Variables need to be Restored 506 | 507 | It is often desirable to fine-tune a pre-trained model on an entirely new 508 | dataset or even a new task. In these situations, one must specify which layers 509 | of the model should be reused (and consequently loaded from a checkpoint) and 510 | which layers are new. Indicating which variables or layers should be restored is 511 | a process that quickly becomes cumbersome when done manually. 512 | 513 | To help keep track of which variables to restore, `slim.variables` provides a 514 | `restore` argument when creating each Variable. By default, all variables are 515 | marked as `restore=True`, which results in all variables defined by the model 516 | being restored. 517 | 518 | ```python 519 | # Create some variables. 520 | v1 = slim.variables.variable(name="v1", ..., restore=False) 521 | v2 = slim.variables.variable(name="v2", ...) # By default restore=True 522 | ... 523 | # Get list of variables to restore (which contains only 'v2') 524 | variables_to_restore = tf.get_collection(slim.variables.VARIABLES_TO_RESTORE) 525 | restorer = tf.train.Saver(variables_to_restore) 526 | with tf.Session() as sess: 527 | # Restore variables from disk. 528 | restorer.restore(sess, "/tmp/model.ckpt") 529 | print("Model restored.") 530 | # Do some work with the model 531 | ... 532 | ``` 533 | 534 | Additionally, every layer in `slim.ops` that creates slim.variables (such as 535 | `slim.ops.conv2d`, `slim.ops.fc`, `slim.ops.batch_norm`) also has a `restore` 536 | argument which controls whether the variables created by that layer should be 537 | restored or not. 538 | 539 | ```python 540 | # Create a small network. 541 | net = slim.ops.conv2d(images, 32, [7, 7], stride=2, scope='conv1') 542 | net = slim.ops.conv2d(net, 64, [3, 3], scope='conv2') 543 | net = slim.ops.conv2d(net, 128, [3, 3], scope='conv3') 544 | net = slim.ops.max_pool(net, [3, 3], stride=2, scope='pool3') 545 | net = slim.ops.flatten(net) 546 | net = slim.ops.fc(net, 10, scope='logits', restore=False) 547 | ... 548 | 549 | # VARIABLES_TO_RESTORE would contain the 'weights' and 'bias' defined by 'conv1' 550 | # 'conv2' and 'conv3' but not the ones defined by 'logits' 551 | variables_to_restore = tf.get_collection(slim.variables.VARIABLES_TO_RESTORE) 552 | 553 | # Create a restorer that would restore only the needed variables. 554 | restorer = tf.train.Saver(variables_to_restore) 555 | 556 | # Create a saver that would save all the variables (including 'logits'). 557 | saver = tf.train.Saver() 558 | with tf.Session() as sess: 559 | # Restore variables from disk. 560 | restorer.restore(sess, "/tmp/model.ckpt") 561 | print("Model restored.") 562 | 563 | # Do some work with the model 564 | ... 565 | saver.save(sess, "/tmp/new_model.ckpt") 566 | ``` 567 | 568 | Note: When restoring variables from a checkpoint, the `Saver` locates the 569 | variable names in a checkpoint file and maps them to variables in the current 570 | graph. Above, we created a saver by passing to it a list of variables. In this 571 | case, the names of the variables to locate in the checkpoint file were 572 | implicitly obtained from each provided variable's `var.op.name`. 573 | 574 | This works well when the variable names in the checkpoint file match those in 575 | the graph. However, sometimes, we want to restore a model from a checkpoint 576 | whose variables have different names those in the current graph. In this case, 577 | we must provide the `Saver` a dictionary that maps from each checkpoint variable 578 | name to each graph variable. Consider the following example where the checkpoint 579 | variables names are obtained via a simple function: 580 | 581 | ```python 582 | # Assuming that 'conv1/weights' should be restored from 'vgg16/conv1/weights' 583 | def name_in_checkpoint(var): 584 | return 'vgg16/' + var.op.name 585 | 586 | # Assuming that 'conv1/weights' and 'conv1/bias' should be restored from 'conv1/params1' and 'conv1/params2' 587 | def name_in_checkpoint(var): 588 | if "weights" in var.op.name: 589 | return var.op.name.replace("weights", "params1") 590 | if "bias" in var.op.name: 591 | return var.op.name.replace("bias", "params2") 592 | 593 | variables_to_restore = tf.get_collection(slim.variables.VARIABLES_TO_RESTORE) 594 | variables_to_restore = {name_in_checkpoint(var):var for var in variables_to_restore} 595 | restorer = tf.train.Saver(variables_to_restore) 596 | with tf.Session() as sess: 597 | # Restore variables from disk. 598 | restorer.restore(sess, "/tmp/model.ckpt") 599 | ``` 600 | 601 | ### Reusing the VGG16 network defined in TF-Slim on a different task, i.e. PASCAL-VOC. 602 | 603 | Assuming one have already a pre-trained VGG16 model, one just need to replace 604 | the last layer `fc8` with a new layer `fc8_pascal` and use `restore=False`. 605 | 606 | ```python 607 | def vgg16_pascal(inputs): 608 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], stddev=0.01, weight_decay=0.0005): 609 | net = slim.ops.repeat_op(2, inputs, slim.ops.conv2d, 64, [3, 3], scope='conv1') 610 | net = slim.ops.max_pool(net, [2, 2], scope='pool1') 611 | net = slim.ops.repeat_op(2, net, slim.ops.conv2d, 128, [3, 3], scope='conv2') 612 | net = slim.ops.max_pool(net, [2, 2], scope='pool2') 613 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 256, [3, 3], scope='conv3') 614 | net = slim.ops.max_pool(net, [2, 2], scope='pool3') 615 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 512, [3, 3], scope='conv4') 616 | net = slim.ops.max_pool(net, [2, 2], scope='pool4') 617 | net = slim.ops.repeat_op(3, net, slim.ops.conv2d, 512, [3, 3], scope='conv5') 618 | net = slim.ops.max_pool(net, [2, 2], scope='pool5') 619 | net = slim.ops.flatten(net, scope='flatten5') 620 | net = slim.ops.fc(net, 4096, scope='fc6') 621 | net = slim.ops.dropout(net, 0.5, scope='dropout6') 622 | net = slim.ops.fc(net, 4096, scope='fc7') 623 | net = slim.ops.dropout(net, 0.5, scope='dropout7') 624 | # To reuse vgg16 on PASCAL-VOC, just change the last layer. 625 | net = slim.ops.fc(net, 21, activation=None, scope='fc8_pascal', restore=False) 626 | return net 627 | ``` 628 | 629 | ## Authors 630 | 631 | Sergio Guadarrama and Nathan Silberman 632 | -------------------------------------------------------------------------------- /inception/slim/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | -------------------------------------------------------------------------------- /inception/slim/collections_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Tests for inception.""" 16 | from __future__ import absolute_import 17 | from __future__ import division 18 | from __future__ import print_function 19 | 20 | import tensorflow as tf 21 | 22 | from inception.slim import slim 23 | 24 | 25 | def get_variables(scope=None): 26 | return slim.variables.get_variables(scope) 27 | 28 | 29 | def get_variables_by_name(name): 30 | return slim.variables.get_variables_by_name(name) 31 | 32 | 33 | class CollectionsTest(tf.test.TestCase): 34 | 35 | def testVariables(self): 36 | batch_size = 5 37 | height, width = 299, 299 38 | with self.test_session(): 39 | inputs = tf.random_uniform((batch_size, height, width, 3)) 40 | with slim.arg_scope([slim.ops.conv2d], 41 | batch_norm_params={'decay': 0.9997}): 42 | slim.inception.inception_v3(inputs) 43 | self.assertEqual(len(get_variables()), 388) 44 | self.assertEqual(len(get_variables_by_name('weights')), 98) 45 | self.assertEqual(len(get_variables_by_name('biases')), 2) 46 | self.assertEqual(len(get_variables_by_name('beta')), 96) 47 | self.assertEqual(len(get_variables_by_name('gamma')), 0) 48 | self.assertEqual(len(get_variables_by_name('moving_mean')), 96) 49 | self.assertEqual(len(get_variables_by_name('moving_variance')), 96) 50 | 51 | def testVariablesWithoutBatchNorm(self): 52 | batch_size = 5 53 | height, width = 299, 299 54 | with self.test_session(): 55 | inputs = tf.random_uniform((batch_size, height, width, 3)) 56 | with slim.arg_scope([slim.ops.conv2d], 57 | batch_norm_params=None): 58 | slim.inception.inception_v3(inputs) 59 | self.assertEqual(len(get_variables()), 196) 60 | self.assertEqual(len(get_variables_by_name('weights')), 98) 61 | self.assertEqual(len(get_variables_by_name('biases')), 98) 62 | self.assertEqual(len(get_variables_by_name('beta')), 0) 63 | self.assertEqual(len(get_variables_by_name('gamma')), 0) 64 | self.assertEqual(len(get_variables_by_name('moving_mean')), 0) 65 | self.assertEqual(len(get_variables_by_name('moving_variance')), 0) 66 | 67 | def testVariablesByLayer(self): 68 | batch_size = 5 69 | height, width = 299, 299 70 | with self.test_session(): 71 | inputs = tf.random_uniform((batch_size, height, width, 3)) 72 | with slim.arg_scope([slim.ops.conv2d], 73 | batch_norm_params={'decay': 0.9997}): 74 | slim.inception.inception_v3(inputs) 75 | self.assertEqual(len(get_variables()), 388) 76 | self.assertEqual(len(get_variables('conv0')), 4) 77 | self.assertEqual(len(get_variables('conv1')), 4) 78 | self.assertEqual(len(get_variables('conv2')), 4) 79 | self.assertEqual(len(get_variables('conv3')), 4) 80 | self.assertEqual(len(get_variables('conv4')), 4) 81 | self.assertEqual(len(get_variables('mixed_35x35x256a')), 28) 82 | self.assertEqual(len(get_variables('mixed_35x35x288a')), 28) 83 | self.assertEqual(len(get_variables('mixed_35x35x288b')), 28) 84 | self.assertEqual(len(get_variables('mixed_17x17x768a')), 16) 85 | self.assertEqual(len(get_variables('mixed_17x17x768b')), 40) 86 | self.assertEqual(len(get_variables('mixed_17x17x768c')), 40) 87 | self.assertEqual(len(get_variables('mixed_17x17x768d')), 40) 88 | self.assertEqual(len(get_variables('mixed_17x17x768e')), 40) 89 | self.assertEqual(len(get_variables('mixed_8x8x2048a')), 36) 90 | self.assertEqual(len(get_variables('mixed_8x8x2048b')), 36) 91 | self.assertEqual(len(get_variables('logits')), 2) 92 | self.assertEqual(len(get_variables('aux_logits')), 10) 93 | 94 | def testVariablesToRestore(self): 95 | batch_size = 5 96 | height, width = 299, 299 97 | with self.test_session(): 98 | inputs = tf.random_uniform((batch_size, height, width, 3)) 99 | with slim.arg_scope([slim.ops.conv2d], 100 | batch_norm_params={'decay': 0.9997}): 101 | slim.inception.inception_v3(inputs) 102 | variables_to_restore = tf.get_collection( 103 | slim.variables.VARIABLES_TO_RESTORE) 104 | self.assertEqual(len(variables_to_restore), 388) 105 | self.assertListEqual(variables_to_restore, get_variables()) 106 | 107 | def testVariablesToRestoreWithoutLogits(self): 108 | batch_size = 5 109 | height, width = 299, 299 110 | with self.test_session(): 111 | inputs = tf.random_uniform((batch_size, height, width, 3)) 112 | with slim.arg_scope([slim.ops.conv2d], 113 | batch_norm_params={'decay': 0.9997}): 114 | slim.inception.inception_v3(inputs, restore_logits=False) 115 | variables_to_restore = tf.get_collection( 116 | slim.variables.VARIABLES_TO_RESTORE) 117 | self.assertEqual(len(variables_to_restore), 384) 118 | 119 | def testRegularizationLosses(self): 120 | batch_size = 5 121 | height, width = 299, 299 122 | with self.test_session(): 123 | inputs = tf.random_uniform((batch_size, height, width, 3)) 124 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], weight_decay=0.00004): 125 | slim.inception.inception_v3(inputs) 126 | losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) 127 | self.assertEqual(len(losses), len(get_variables_by_name('weights'))) 128 | 129 | def testTotalLossWithoutRegularization(self): 130 | batch_size = 5 131 | height, width = 299, 299 132 | num_classes = 1001 133 | with self.test_session(): 134 | inputs = tf.random_uniform((batch_size, height, width, 3)) 135 | dense_labels = tf.random_uniform((batch_size, num_classes)) 136 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], weight_decay=0): 137 | logits, end_points = slim.inception.inception_v3( 138 | inputs, 139 | num_classes=num_classes) 140 | # Cross entropy loss for the main softmax prediction. 141 | slim.losses.cross_entropy_loss(logits, 142 | dense_labels, 143 | label_smoothing=0.1, 144 | weight=1.0) 145 | # Cross entropy loss for the auxiliary softmax head. 146 | slim.losses.cross_entropy_loss(end_points['aux_logits'], 147 | dense_labels, 148 | label_smoothing=0.1, 149 | weight=0.4, 150 | scope='aux_loss') 151 | losses = tf.get_collection(slim.losses.LOSSES_COLLECTION) 152 | self.assertEqual(len(losses), 2) 153 | 154 | def testTotalLossWithRegularization(self): 155 | batch_size = 5 156 | height, width = 299, 299 157 | num_classes = 1000 158 | with self.test_session(): 159 | inputs = tf.random_uniform((batch_size, height, width, 3)) 160 | dense_labels = tf.random_uniform((batch_size, num_classes)) 161 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], weight_decay=0.00004): 162 | logits, end_points = slim.inception.inception_v3(inputs, num_classes) 163 | # Cross entropy loss for the main softmax prediction. 164 | slim.losses.cross_entropy_loss(logits, 165 | dense_labels, 166 | label_smoothing=0.1, 167 | weight=1.0) 168 | # Cross entropy loss for the auxiliary softmax head. 169 | slim.losses.cross_entropy_loss(end_points['aux_logits'], 170 | dense_labels, 171 | label_smoothing=0.1, 172 | weight=0.4, 173 | scope='aux_loss') 174 | losses = tf.get_collection(slim.losses.LOSSES_COLLECTION) 175 | self.assertEqual(len(losses), 2) 176 | reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) 177 | self.assertEqual(len(reg_losses), 98) 178 | 179 | 180 | if __name__ == '__main__': 181 | tf.test.main() 182 | -------------------------------------------------------------------------------- /inception/slim/inception_model.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Inception-v3 expressed in TensorFlow-Slim. 16 | 17 | Usage: 18 | 19 | # Parameters for BatchNorm. 20 | batch_norm_params = { 21 | # Decay for the batch_norm moving averages. 22 | 'decay': BATCHNORM_MOVING_AVERAGE_DECAY, 23 | # epsilon to prevent 0s in variance. 24 | 'epsilon': 0.001, 25 | } 26 | # Set weight_decay for weights in Conv and FC layers. 27 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], weight_decay=0.00004): 28 | with slim.arg_scope([slim.ops.conv2d], 29 | stddev=0.1, 30 | activation=tf.nn.relu, 31 | batch_norm_params=batch_norm_params): 32 | # Force all Variables to reside on the CPU. 33 | with slim.arg_scope([slim.variables.variable], device='/cpu:0'): 34 | logits, endpoints = slim.inception.inception_v3( 35 | images, 36 | dropout_keep_prob=0.8, 37 | num_classes=num_classes, 38 | is_training=for_training, 39 | restore_logits=restore_logits, 40 | scope=scope) 41 | """ 42 | from __future__ import absolute_import 43 | from __future__ import division 44 | from __future__ import print_function 45 | 46 | import tensorflow as tf 47 | 48 | from inception.slim import ops 49 | from inception.slim import scopes 50 | 51 | 52 | def inception_v3(inputs, 53 | dropout_keep_prob=0.8, 54 | num_classes=1000, 55 | is_training=True, 56 | restore_logits=True, 57 | scope=''): 58 | """Latest Inception from http://arxiv.org/abs/1512.00567. 59 | 60 | "Rethinking the Inception Architecture for Computer Vision" 61 | 62 | Christian Szegedy, Vincent Vanhoucke, Sergey Ioffe, Jonathon Shlens, 63 | Zbigniew Wojna 64 | 65 | Args: 66 | inputs: a tensor of size [batch_size, height, width, channels]. 67 | dropout_keep_prob: dropout keep_prob. 68 | num_classes: number of predicted classes. 69 | is_training: whether is training or not. 70 | restore_logits: whether or not the logits layers should be restored. 71 | Useful for fine-tuning a model with different num_classes. 72 | scope: Optional scope for name_scope. 73 | 74 | Returns: 75 | a list containing 'logits', 'aux_logits' Tensors. 76 | """ 77 | # end_points will collect relevant activations for external use, for example 78 | # summaries or losses. 79 | end_points = {} 80 | with tf.name_scope(scope, 'inception_v3', [inputs]): 81 | with scopes.arg_scope([ops.conv2d, ops.fc, ops.batch_norm, ops.dropout], 82 | is_training=is_training): 83 | with scopes.arg_scope([ops.conv2d, ops.max_pool, ops.avg_pool], 84 | stride=1, padding='VALID'): 85 | # 299 x 299 x 3 86 | end_points['conv0'] = ops.conv2d(inputs, 32, [3, 3], stride=2, 87 | scope='conv0') 88 | # 149 x 149 x 32 89 | end_points['conv1'] = ops.conv2d(end_points['conv0'], 32, [3, 3], 90 | scope='conv1') 91 | # 147 x 147 x 32 92 | end_points['conv2'] = ops.conv2d(end_points['conv1'], 64, [3, 3], 93 | padding='SAME', scope='conv2') 94 | # 147 x 147 x 64 95 | end_points['pool1'] = ops.max_pool(end_points['conv2'], [3, 3], 96 | stride=2, scope='pool1') 97 | # 73 x 73 x 64 98 | end_points['conv3'] = ops.conv2d(end_points['pool1'], 80, [1, 1], 99 | scope='conv3') 100 | # 73 x 73 x 80. 101 | end_points['conv4'] = ops.conv2d(end_points['conv3'], 192, [3, 3], 102 | scope='conv4') 103 | # 71 x 71 x 192. 104 | end_points['pool2'] = ops.max_pool(end_points['conv4'], [3, 3], 105 | stride=2, scope='pool2') 106 | # 35 x 35 x 192. 107 | net = end_points['pool2'] 108 | # Inception blocks 109 | with scopes.arg_scope([ops.conv2d, ops.max_pool, ops.avg_pool], 110 | stride=1, padding='SAME'): 111 | # mixed: 35 x 35 x 256. 112 | with tf.variable_scope('mixed_35x35x256a'): 113 | with tf.variable_scope('branch1x1'): 114 | branch1x1 = ops.conv2d(net, 64, [1, 1]) 115 | with tf.variable_scope('branch5x5'): 116 | branch5x5 = ops.conv2d(net, 48, [1, 1]) 117 | branch5x5 = ops.conv2d(branch5x5, 64, [5, 5]) 118 | with tf.variable_scope('branch3x3dbl'): 119 | branch3x3dbl = ops.conv2d(net, 64, [1, 1]) 120 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 121 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 122 | with tf.variable_scope('branch_pool'): 123 | branch_pool = ops.avg_pool(net, [3, 3]) 124 | branch_pool = ops.conv2d(branch_pool, 32, [1, 1]) 125 | net = tf.concat([branch1x1, branch5x5, branch3x3dbl, branch_pool], 3) 126 | end_points['mixed_35x35x256a'] = net 127 | # mixed_1: 35 x 35 x 288. 128 | with tf.variable_scope('mixed_35x35x288a'): 129 | with tf.variable_scope('branch1x1'): 130 | branch1x1 = ops.conv2d(net, 64, [1, 1]) 131 | with tf.variable_scope('branch5x5'): 132 | branch5x5 = ops.conv2d(net, 48, [1, 1]) 133 | branch5x5 = ops.conv2d(branch5x5, 64, [5, 5]) 134 | with tf.variable_scope('branch3x3dbl'): 135 | branch3x3dbl = ops.conv2d(net, 64, [1, 1]) 136 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 137 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 138 | with tf.variable_scope('branch_pool'): 139 | branch_pool = ops.avg_pool(net, [3, 3]) 140 | branch_pool = ops.conv2d(branch_pool, 64, [1, 1]) 141 | net = tf.concat([branch1x1, branch5x5, branch3x3dbl, branch_pool], 3) 142 | end_points['mixed_35x35x288a'] = net 143 | # mixed_2: 35 x 35 x 288. 144 | with tf.variable_scope('mixed_35x35x288b'): 145 | with tf.variable_scope('branch1x1'): 146 | branch1x1 = ops.conv2d(net, 64, [1, 1]) 147 | with tf.variable_scope('branch5x5'): 148 | branch5x5 = ops.conv2d(net, 48, [1, 1]) 149 | branch5x5 = ops.conv2d(branch5x5, 64, [5, 5]) 150 | with tf.variable_scope('branch3x3dbl'): 151 | branch3x3dbl = ops.conv2d(net, 64, [1, 1]) 152 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 153 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 154 | with tf.variable_scope('branch_pool'): 155 | branch_pool = ops.avg_pool(net, [3, 3]) 156 | branch_pool = ops.conv2d(branch_pool, 64, [1, 1]) 157 | net = tf.concat([branch1x1, branch5x5, branch3x3dbl, branch_pool], 3) 158 | end_points['mixed_35x35x288b'] = net 159 | # mixed_3: 17 x 17 x 768. 160 | with tf.variable_scope('mixed_17x17x768a'): 161 | with tf.variable_scope('branch3x3'): 162 | branch3x3 = ops.conv2d(net, 384, [3, 3], stride=2, padding='VALID') 163 | with tf.variable_scope('branch3x3dbl'): 164 | branch3x3dbl = ops.conv2d(net, 64, [1, 1]) 165 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3]) 166 | branch3x3dbl = ops.conv2d(branch3x3dbl, 96, [3, 3], 167 | stride=2, padding='VALID') 168 | with tf.variable_scope('branch_pool'): 169 | branch_pool = ops.max_pool(net, [3, 3], stride=2, padding='VALID') 170 | net = tf.concat([branch3x3, branch3x3dbl, branch_pool], 3) 171 | end_points['mixed_17x17x768a'] = net 172 | # mixed4: 17 x 17 x 768. 173 | with tf.variable_scope('mixed_17x17x768b'): 174 | with tf.variable_scope('branch1x1'): 175 | branch1x1 = ops.conv2d(net, 192, [1, 1]) 176 | with tf.variable_scope('branch7x7'): 177 | branch7x7 = ops.conv2d(net, 128, [1, 1]) 178 | branch7x7 = ops.conv2d(branch7x7, 128, [1, 7]) 179 | branch7x7 = ops.conv2d(branch7x7, 192, [7, 1]) 180 | with tf.variable_scope('branch7x7dbl'): 181 | branch7x7dbl = ops.conv2d(net, 128, [1, 1]) 182 | branch7x7dbl = ops.conv2d(branch7x7dbl, 128, [7, 1]) 183 | branch7x7dbl = ops.conv2d(branch7x7dbl, 128, [1, 7]) 184 | branch7x7dbl = ops.conv2d(branch7x7dbl, 128, [7, 1]) 185 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [1, 7]) 186 | with tf.variable_scope('branch_pool'): 187 | branch_pool = ops.avg_pool(net, [3, 3]) 188 | branch_pool = ops.conv2d(branch_pool, 192, [1, 1]) 189 | net = tf.concat([branch1x1, branch7x7, branch7x7dbl, branch_pool], 3) 190 | end_points['mixed_17x17x768b'] = net 191 | # mixed_5: 17 x 17 x 768. 192 | with tf.variable_scope('mixed_17x17x768c'): 193 | with tf.variable_scope('branch1x1'): 194 | branch1x1 = ops.conv2d(net, 192, [1, 1]) 195 | with tf.variable_scope('branch7x7'): 196 | branch7x7 = ops.conv2d(net, 160, [1, 1]) 197 | branch7x7 = ops.conv2d(branch7x7, 160, [1, 7]) 198 | branch7x7 = ops.conv2d(branch7x7, 192, [7, 1]) 199 | with tf.variable_scope('branch7x7dbl'): 200 | branch7x7dbl = ops.conv2d(net, 160, [1, 1]) 201 | branch7x7dbl = ops.conv2d(branch7x7dbl, 160, [7, 1]) 202 | branch7x7dbl = ops.conv2d(branch7x7dbl, 160, [1, 7]) 203 | branch7x7dbl = ops.conv2d(branch7x7dbl, 160, [7, 1]) 204 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [1, 7]) 205 | with tf.variable_scope('branch_pool'): 206 | branch_pool = ops.avg_pool(net, [3, 3]) 207 | branch_pool = ops.conv2d(branch_pool, 192, [1, 1]) 208 | net = tf.concat([branch1x1, branch7x7, branch7x7dbl, branch_pool], 3) 209 | end_points['mixed_17x17x768c'] = net 210 | # mixed_6: 17 x 17 x 768. 211 | with tf.variable_scope('mixed_17x17x768d'): 212 | with tf.variable_scope('branch1x1'): 213 | branch1x1 = ops.conv2d(net, 192, [1, 1]) 214 | with tf.variable_scope('branch7x7'): 215 | branch7x7 = ops.conv2d(net, 160, [1, 1]) 216 | branch7x7 = ops.conv2d(branch7x7, 160, [1, 7]) 217 | branch7x7 = ops.conv2d(branch7x7, 192, [7, 1]) 218 | with tf.variable_scope('branch7x7dbl'): 219 | branch7x7dbl = ops.conv2d(net, 160, [1, 1]) 220 | branch7x7dbl = ops.conv2d(branch7x7dbl, 160, [7, 1]) 221 | branch7x7dbl = ops.conv2d(branch7x7dbl, 160, [1, 7]) 222 | branch7x7dbl = ops.conv2d(branch7x7dbl, 160, [7, 1]) 223 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [1, 7]) 224 | with tf.variable_scope('branch_pool'): 225 | branch_pool = ops.avg_pool(net, [3, 3]) 226 | branch_pool = ops.conv2d(branch_pool, 192, [1, 1]) 227 | net = tf.concat([branch1x1, branch7x7, branch7x7dbl, branch_pool], 3) 228 | end_points['mixed_17x17x768d'] = net 229 | # mixed_7: 17 x 17 x 768. 230 | with tf.variable_scope('mixed_17x17x768e'): 231 | with tf.variable_scope('branch1x1'): 232 | branch1x1 = ops.conv2d(net, 192, [1, 1]) 233 | with tf.variable_scope('branch7x7'): 234 | branch7x7 = ops.conv2d(net, 192, [1, 1]) 235 | branch7x7 = ops.conv2d(branch7x7, 192, [1, 7]) 236 | branch7x7 = ops.conv2d(branch7x7, 192, [7, 1]) 237 | with tf.variable_scope('branch7x7dbl'): 238 | branch7x7dbl = ops.conv2d(net, 192, [1, 1]) 239 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [7, 1]) 240 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [1, 7]) 241 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [7, 1]) 242 | branch7x7dbl = ops.conv2d(branch7x7dbl, 192, [1, 7]) 243 | with tf.variable_scope('branch_pool'): 244 | branch_pool = ops.avg_pool(net, [3, 3]) 245 | branch_pool = ops.conv2d(branch_pool, 192, [1, 1]) 246 | net = tf.concat([branch1x1, branch7x7, branch7x7dbl, branch_pool], 3) 247 | end_points['mixed_17x17x768e'] = net 248 | # Auxiliary Head logits 249 | aux_logits = tf.identity(end_points['mixed_17x17x768e']) 250 | with tf.variable_scope('aux_logits'): 251 | aux_logits = ops.avg_pool(aux_logits, [5, 5], stride=3, 252 | padding='VALID') 253 | aux_logits = ops.conv2d(aux_logits, 128, [1, 1], scope='proj') 254 | # Shape of feature map before the final layer. 255 | shape = aux_logits.get_shape() 256 | aux_logits = ops.conv2d(aux_logits, 768, shape[1:3], stddev=0.01, 257 | padding='VALID') 258 | aux_logits = ops.flatten(aux_logits) 259 | aux_logits = ops.fc(aux_logits, num_classes, activation=None, 260 | stddev=0.001, restore=restore_logits) 261 | end_points['aux_logits'] = aux_logits 262 | # mixed_8: 8 x 8 x 1280. 263 | # Note that the scope below is not changed to not void previous 264 | # checkpoints. 265 | # (TODO) Fix the scope when appropriate. 266 | with tf.variable_scope('mixed_17x17x1280a'): 267 | with tf.variable_scope('branch3x3'): 268 | branch3x3 = ops.conv2d(net, 192, [1, 1]) 269 | branch3x3 = ops.conv2d(branch3x3, 320, [3, 3], stride=2, 270 | padding='VALID') 271 | with tf.variable_scope('branch7x7x3'): 272 | branch7x7x3 = ops.conv2d(net, 192, [1, 1]) 273 | branch7x7x3 = ops.conv2d(branch7x7x3, 192, [1, 7]) 274 | branch7x7x3 = ops.conv2d(branch7x7x3, 192, [7, 1]) 275 | branch7x7x3 = ops.conv2d(branch7x7x3, 192, [3, 3], 276 | stride=2, padding='VALID') 277 | with tf.variable_scope('branch_pool'): 278 | branch_pool = ops.max_pool(net, [3, 3], stride=2, padding='VALID') 279 | net = tf.concat([branch3x3, branch7x7x3, branch_pool], 3) 280 | end_points['mixed_17x17x1280a'] = net 281 | # mixed_9: 8 x 8 x 2048. 282 | with tf.variable_scope('mixed_8x8x2048a'): 283 | with tf.variable_scope('branch1x1'): 284 | branch1x1 = ops.conv2d(net, 320, [1, 1]) 285 | with tf.variable_scope('branch3x3'): 286 | branch3x3 = ops.conv2d(net, 384, [1, 1]) 287 | branch3x3 = tf.concat([ops.conv2d(branch3x3, 384, [1, 3]), 288 | ops.conv2d(branch3x3, 384, [3, 1])], 3) 289 | with tf.variable_scope('branch3x3dbl'): 290 | branch3x3dbl = ops.conv2d(net, 448, [1, 1]) 291 | branch3x3dbl = ops.conv2d(branch3x3dbl, 384, [3, 3]) 292 | branch3x3dbl = tf.concat([ops.conv2d(branch3x3dbl, 384, [1, 3]), 293 | ops.conv2d(branch3x3dbl, 384, [3, 1])], 3) 294 | with tf.variable_scope('branch_pool'): 295 | branch_pool = ops.avg_pool(net, [3, 3]) 296 | branch_pool = ops.conv2d(branch_pool, 192, [1, 1]) 297 | net = tf.concat([branch1x1, branch3x3, branch3x3dbl, branch_pool], 3) 298 | end_points['mixed_8x8x2048a'] = net 299 | # mixed_10: 8 x 8 x 2048. 300 | with tf.variable_scope('mixed_8x8x2048b'): 301 | with tf.variable_scope('branch1x1'): 302 | branch1x1 = ops.conv2d(net, 320, [1, 1]) 303 | with tf.variable_scope('branch3x3'): 304 | branch3x3 = ops.conv2d(net, 384, [1, 1]) 305 | branch3x3 = tf.concat([ops.conv2d(branch3x3, 384, [1, 3]), 306 | ops.conv2d(branch3x3, 384, [3, 1])], 3) 307 | with tf.variable_scope('branch3x3dbl'): 308 | branch3x3dbl = ops.conv2d(net, 448, [1, 1]) 309 | branch3x3dbl = ops.conv2d(branch3x3dbl, 384, [3, 3]) 310 | branch3x3dbl = tf.concat([ops.conv2d(branch3x3dbl, 384, [1, 3]), 311 | ops.conv2d(branch3x3dbl, 384, [3, 1])], 3) 312 | with tf.variable_scope('branch_pool'): 313 | branch_pool = ops.avg_pool(net, [3, 3]) 314 | branch_pool = ops.conv2d(branch_pool, 192, [1, 1]) 315 | net = tf.concat([branch1x1, branch3x3, branch3x3dbl, branch_pool], 3) 316 | end_points['mixed_8x8x2048b'] = net 317 | # Final pooling and prediction 318 | with tf.variable_scope('logits'): 319 | shape = net.get_shape() 320 | net = ops.avg_pool(net, shape[1:3], padding='VALID', scope='pool') 321 | # 1 x 1 x 2048 322 | net = ops.dropout(net, dropout_keep_prob, scope='dropout') 323 | net = ops.flatten(net, scope='flatten') 324 | # 2048 325 | logits = ops.fc(net, num_classes, activation=None, scope='logits', 326 | restore=restore_logits) 327 | # 1000 328 | end_points['logits'] = logits 329 | end_points['predictions'] = tf.nn.softmax(logits, name='predictions') 330 | return logits, end_points 331 | 332 | 333 | def inception_v3_parameters(weight_decay=0.00004, stddev=0.1, 334 | batch_norm_decay=0.9997, batch_norm_epsilon=0.001): 335 | """Yields the scope with the default parameters for inception_v3. 336 | 337 | Args: 338 | weight_decay: the weight decay for weights variables. 339 | stddev: standard deviation of the truncated guassian weight distribution. 340 | batch_norm_decay: decay for the moving average of batch_norm momentums. 341 | batch_norm_epsilon: small float added to variance to avoid dividing by zero. 342 | 343 | Yields: 344 | a arg_scope with the parameters needed for inception_v3. 345 | """ 346 | # Set weight_decay for weights in Conv and FC layers. 347 | with scopes.arg_scope([ops.conv2d, ops.fc], 348 | weight_decay=weight_decay): 349 | # Set stddev, activation and parameters for batch_norm. 350 | with scopes.arg_scope([ops.conv2d], 351 | stddev=stddev, 352 | activation=tf.nn.relu, 353 | batch_norm_params={ 354 | 'decay': batch_norm_decay, 355 | 'epsilon': batch_norm_epsilon}) as arg_scope: 356 | yield arg_scope 357 | -------------------------------------------------------------------------------- /inception/slim/inception_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Tests for slim.inception.""" 16 | from __future__ import absolute_import 17 | from __future__ import division 18 | from __future__ import print_function 19 | 20 | import tensorflow as tf 21 | 22 | from inception.slim import inception_model as inception 23 | 24 | 25 | class InceptionTest(tf.test.TestCase): 26 | 27 | def testBuildLogits(self): 28 | batch_size = 5 29 | height, width = 299, 299 30 | num_classes = 1000 31 | with self.test_session(): 32 | inputs = tf.random_uniform((batch_size, height, width, 3)) 33 | logits, _ = inception.inception_v3(inputs, num_classes) 34 | self.assertTrue(logits.op.name.startswith('logits')) 35 | self.assertListEqual(logits.get_shape().as_list(), 36 | [batch_size, num_classes]) 37 | 38 | def testBuildEndPoints(self): 39 | batch_size = 5 40 | height, width = 299, 299 41 | num_classes = 1000 42 | with self.test_session(): 43 | inputs = tf.random_uniform((batch_size, height, width, 3)) 44 | _, end_points = inception.inception_v3(inputs, num_classes) 45 | self.assertTrue('logits' in end_points) 46 | logits = end_points['logits'] 47 | self.assertListEqual(logits.get_shape().as_list(), 48 | [batch_size, num_classes]) 49 | self.assertTrue('aux_logits' in end_points) 50 | aux_logits = end_points['aux_logits'] 51 | self.assertListEqual(aux_logits.get_shape().as_list(), 52 | [batch_size, num_classes]) 53 | pre_pool = end_points['mixed_8x8x2048b'] 54 | self.assertListEqual(pre_pool.get_shape().as_list(), 55 | [batch_size, 8, 8, 2048]) 56 | 57 | def testVariablesSetDevice(self): 58 | batch_size = 5 59 | height, width = 299, 299 60 | num_classes = 1000 61 | with self.test_session(): 62 | inputs = tf.random_uniform((batch_size, height, width, 3)) 63 | # Force all Variables to reside on the device. 64 | with tf.variable_scope('on_cpu'), tf.device('/cpu:0'): 65 | inception.inception_v3(inputs, num_classes) 66 | with tf.variable_scope('on_gpu'), tf.device('/gpu:0'): 67 | inception.inception_v3(inputs, num_classes) 68 | for v in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='on_cpu'): 69 | self.assertDeviceEqual(v.device, '/cpu:0') 70 | for v in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='on_gpu'): 71 | self.assertDeviceEqual(v.device, '/gpu:0') 72 | 73 | def testHalfSizeImages(self): 74 | batch_size = 5 75 | height, width = 150, 150 76 | num_classes = 1000 77 | with self.test_session(): 78 | inputs = tf.random_uniform((batch_size, height, width, 3)) 79 | logits, end_points = inception.inception_v3(inputs, num_classes) 80 | self.assertTrue(logits.op.name.startswith('logits')) 81 | self.assertListEqual(logits.get_shape().as_list(), 82 | [batch_size, num_classes]) 83 | pre_pool = end_points['mixed_8x8x2048b'] 84 | self.assertListEqual(pre_pool.get_shape().as_list(), 85 | [batch_size, 3, 3, 2048]) 86 | 87 | def testUnknowBatchSize(self): 88 | batch_size = 1 89 | height, width = 299, 299 90 | num_classes = 1000 91 | with self.test_session() as sess: 92 | inputs = tf.placeholder(tf.float32, (None, height, width, 3)) 93 | logits, _ = inception.inception_v3(inputs, num_classes) 94 | self.assertTrue(logits.op.name.startswith('logits')) 95 | self.assertListEqual(logits.get_shape().as_list(), 96 | [None, num_classes]) 97 | images = tf.random_uniform((batch_size, height, width, 3)) 98 | sess.run(tf.global_variables_initializer()) 99 | output = sess.run(logits, {inputs: images.eval()}) 100 | self.assertEquals(output.shape, (batch_size, num_classes)) 101 | 102 | def testEvaluation(self): 103 | batch_size = 2 104 | height, width = 299, 299 105 | num_classes = 1000 106 | with self.test_session() as sess: 107 | eval_inputs = tf.random_uniform((batch_size, height, width, 3)) 108 | logits, _ = inception.inception_v3(eval_inputs, num_classes, 109 | is_training=False) 110 | predictions = tf.argmax(logits, 1) 111 | sess.run(tf.global_variables_initializer()) 112 | output = sess.run(predictions) 113 | self.assertEquals(output.shape, (batch_size,)) 114 | 115 | def testTrainEvalWithReuse(self): 116 | train_batch_size = 5 117 | eval_batch_size = 2 118 | height, width = 150, 150 119 | num_classes = 1000 120 | with self.test_session() as sess: 121 | train_inputs = tf.random_uniform((train_batch_size, height, width, 3)) 122 | inception.inception_v3(train_inputs, num_classes) 123 | tf.get_variable_scope().reuse_variables() 124 | eval_inputs = tf.random_uniform((eval_batch_size, height, width, 3)) 125 | logits, _ = inception.inception_v3(eval_inputs, num_classes, 126 | is_training=False) 127 | predictions = tf.argmax(logits, 1) 128 | sess.run(tf.global_variables_initializer()) 129 | output = sess.run(predictions) 130 | self.assertEquals(output.shape, (eval_batch_size,)) 131 | 132 | 133 | if __name__ == '__main__': 134 | tf.test.main() 135 | -------------------------------------------------------------------------------- /inception/slim/losses.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Contains convenience wrappers for various Neural Network TensorFlow losses. 16 | 17 | All the losses defined here add themselves to the LOSSES_COLLECTION 18 | collection. 19 | 20 | l1_loss: Define a L1 Loss, useful for regularization, i.e. lasso. 21 | l2_loss: Define a L2 Loss, useful for regularization, i.e. weight decay. 22 | cross_entropy_loss: Define a cross entropy loss using 23 | softmax_cross_entropy_with_logits. Useful for classification. 24 | """ 25 | from __future__ import absolute_import 26 | from __future__ import division 27 | from __future__ import print_function 28 | 29 | import tensorflow as tf 30 | 31 | # In order to gather all losses in a network, the user should use this 32 | # key for get_collection, i.e: 33 | # losses = tf.get_collection(slim.losses.LOSSES_COLLECTION) 34 | LOSSES_COLLECTION = '_losses' 35 | 36 | 37 | def l1_regularizer(weight=1.0, scope=None): 38 | """Define a L1 regularizer. 39 | 40 | Args: 41 | weight: scale the loss by this factor. 42 | scope: Optional scope for name_scope. 43 | 44 | Returns: 45 | a regularizer function. 46 | """ 47 | def regularizer(tensor): 48 | with tf.name_scope(scope, 'L1Regularizer', [tensor]): 49 | l1_weight = tf.convert_to_tensor(weight, 50 | dtype=tensor.dtype.base_dtype, 51 | name='weight') 52 | return tf.multiply(l1_weight, tf.reduce_sum(tf.abs(tensor)), name='value') 53 | return regularizer 54 | 55 | 56 | def l2_regularizer(weight=1.0, scope=None): 57 | """Define a L2 regularizer. 58 | 59 | Args: 60 | weight: scale the loss by this factor. 61 | scope: Optional scope for name_scope. 62 | 63 | Returns: 64 | a regularizer function. 65 | """ 66 | def regularizer(tensor): 67 | with tf.name_scope(scope, 'L2Regularizer', [tensor]): 68 | l2_weight = tf.convert_to_tensor(weight, 69 | dtype=tensor.dtype.base_dtype, 70 | name='weight') 71 | return tf.multiply(l2_weight, tf.nn.l2_loss(tensor), name='value') 72 | return regularizer 73 | 74 | 75 | def l1_l2_regularizer(weight_l1=1.0, weight_l2=1.0, scope=None): 76 | """Define a L1L2 regularizer. 77 | 78 | Args: 79 | weight_l1: scale the L1 loss by this factor. 80 | weight_l2: scale the L2 loss by this factor. 81 | scope: Optional scope for name_scope. 82 | 83 | Returns: 84 | a regularizer function. 85 | """ 86 | def regularizer(tensor): 87 | with tf.name_scope(scope, 'L1L2Regularizer', [tensor]): 88 | weight_l1_t = tf.convert_to_tensor(weight_l1, 89 | dtype=tensor.dtype.base_dtype, 90 | name='weight_l1') 91 | weight_l2_t = tf.convert_to_tensor(weight_l2, 92 | dtype=tensor.dtype.base_dtype, 93 | name='weight_l2') 94 | reg_l1 = tf.multiply(weight_l1_t, tf.reduce_sum(tf.abs(tensor)), 95 | name='value_l1') 96 | reg_l2 = tf.multiply(weight_l2_t, tf.nn.l2_loss(tensor), 97 | name='value_l2') 98 | return tf.add(reg_l1, reg_l2, name='value') 99 | return regularizer 100 | 101 | 102 | def l1_loss(tensor, weight=1.0, scope=None): 103 | """Define a L1Loss, useful for regularize, i.e. lasso. 104 | 105 | Args: 106 | tensor: tensor to regularize. 107 | weight: scale the loss by this factor. 108 | scope: Optional scope for name_scope. 109 | 110 | Returns: 111 | the L1 loss op. 112 | """ 113 | with tf.name_scope(scope, 'L1Loss', [tensor]): 114 | weight = tf.convert_to_tensor(weight, 115 | dtype=tensor.dtype.base_dtype, 116 | name='loss_weight') 117 | loss = tf.multiply(weight, tf.reduce_sum(tf.abs(tensor)), name='value') 118 | tf.add_to_collection(LOSSES_COLLECTION, loss) 119 | return loss 120 | 121 | 122 | def l2_loss(tensor, weight=1.0, scope=None): 123 | """Define a L2Loss, useful for regularize, i.e. weight decay. 124 | 125 | Args: 126 | tensor: tensor to regularize. 127 | weight: an optional weight to modulate the loss. 128 | scope: Optional scope for name_scope. 129 | 130 | Returns: 131 | the L2 loss op. 132 | """ 133 | with tf.name_scope(scope, 'L2Loss', [tensor]): 134 | weight = tf.convert_to_tensor(weight, 135 | dtype=tensor.dtype.base_dtype, 136 | name='loss_weight') 137 | loss = tf.multiply(weight, tf.nn.l2_loss(tensor), name='value') 138 | tf.add_to_collection(LOSSES_COLLECTION, loss) 139 | return loss 140 | 141 | 142 | def cross_entropy_loss(logits, one_hot_labels, label_smoothing=0, 143 | weight=1.0, scope=None): 144 | """Define a Cross Entropy loss using softmax_cross_entropy_with_logits. 145 | 146 | It can scale the loss by weight factor, and smooth the labels. 147 | 148 | Args: 149 | logits: [batch_size, num_classes] logits outputs of the network . 150 | one_hot_labels: [batch_size, num_classes] target one_hot_encoded labels. 151 | label_smoothing: if greater than 0 then smooth the labels. 152 | weight: scale the loss by this factor. 153 | scope: Optional scope for name_scope. 154 | 155 | Returns: 156 | A tensor with the softmax_cross_entropy loss. 157 | """ 158 | logits.get_shape().assert_is_compatible_with(one_hot_labels.get_shape()) 159 | with tf.name_scope(scope, 'CrossEntropyLoss', [logits, one_hot_labels]): 160 | num_classes = one_hot_labels.get_shape()[-1].value 161 | one_hot_labels = tf.cast(one_hot_labels, logits.dtype) 162 | if label_smoothing > 0: 163 | smooth_positives = 1.0 - label_smoothing 164 | smooth_negatives = label_smoothing / num_classes 165 | one_hot_labels = one_hot_labels * smooth_positives + smooth_negatives 166 | cross_entropy = tf.contrib.nn.deprecated_flipped_softmax_cross_entropy_with_logits( 167 | logits, one_hot_labels, name='xentropy') 168 | 169 | weight = tf.convert_to_tensor(weight, 170 | dtype=logits.dtype.base_dtype, 171 | name='loss_weight') 172 | loss = tf.multiply(weight, tf.reduce_mean(cross_entropy), name='value') 173 | tf.add_to_collection(LOSSES_COLLECTION, loss) 174 | return loss 175 | -------------------------------------------------------------------------------- /inception/slim/losses_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Tests for slim.losses.""" 16 | from __future__ import absolute_import 17 | from __future__ import division 18 | from __future__ import print_function 19 | 20 | 21 | import tensorflow as tf 22 | 23 | from inception.slim import losses 24 | 25 | 26 | class LossesTest(tf.test.TestCase): 27 | 28 | def testL1Loss(self): 29 | with self.test_session(): 30 | shape = [5, 5, 5] 31 | num_elem = 5 * 5 * 5 32 | weights = tf.constant(1.0, shape=shape) 33 | wd = 0.01 34 | loss = losses.l1_loss(weights, wd) 35 | self.assertEquals(loss.op.name, 'L1Loss/value') 36 | self.assertAlmostEqual(loss.eval(), num_elem * wd, 5) 37 | 38 | def testL2Loss(self): 39 | with self.test_session(): 40 | shape = [5, 5, 5] 41 | num_elem = 5 * 5 * 5 42 | weights = tf.constant(1.0, shape=shape) 43 | wd = 0.01 44 | loss = losses.l2_loss(weights, wd) 45 | self.assertEquals(loss.op.name, 'L2Loss/value') 46 | self.assertAlmostEqual(loss.eval(), num_elem * wd / 2, 5) 47 | 48 | 49 | class RegularizersTest(tf.test.TestCase): 50 | 51 | def testL1Regularizer(self): 52 | with self.test_session(): 53 | shape = [5, 5, 5] 54 | num_elem = 5 * 5 * 5 55 | tensor = tf.constant(1.0, shape=shape) 56 | loss = losses.l1_regularizer()(tensor) 57 | self.assertEquals(loss.op.name, 'L1Regularizer/value') 58 | self.assertAlmostEqual(loss.eval(), num_elem, 5) 59 | 60 | def testL1RegularizerWithScope(self): 61 | with self.test_session(): 62 | shape = [5, 5, 5] 63 | num_elem = 5 * 5 * 5 64 | tensor = tf.constant(1.0, shape=shape) 65 | loss = losses.l1_regularizer(scope='L1')(tensor) 66 | self.assertEquals(loss.op.name, 'L1/value') 67 | self.assertAlmostEqual(loss.eval(), num_elem, 5) 68 | 69 | def testL1RegularizerWithWeight(self): 70 | with self.test_session(): 71 | shape = [5, 5, 5] 72 | num_elem = 5 * 5 * 5 73 | tensor = tf.constant(1.0, shape=shape) 74 | weight = 0.01 75 | loss = losses.l1_regularizer(weight)(tensor) 76 | self.assertEquals(loss.op.name, 'L1Regularizer/value') 77 | self.assertAlmostEqual(loss.eval(), num_elem * weight, 5) 78 | 79 | def testL2Regularizer(self): 80 | with self.test_session(): 81 | shape = [5, 5, 5] 82 | num_elem = 5 * 5 * 5 83 | tensor = tf.constant(1.0, shape=shape) 84 | loss = losses.l2_regularizer()(tensor) 85 | self.assertEquals(loss.op.name, 'L2Regularizer/value') 86 | self.assertAlmostEqual(loss.eval(), num_elem / 2, 5) 87 | 88 | def testL2RegularizerWithScope(self): 89 | with self.test_session(): 90 | shape = [5, 5, 5] 91 | num_elem = 5 * 5 * 5 92 | tensor = tf.constant(1.0, shape=shape) 93 | loss = losses.l2_regularizer(scope='L2')(tensor) 94 | self.assertEquals(loss.op.name, 'L2/value') 95 | self.assertAlmostEqual(loss.eval(), num_elem / 2, 5) 96 | 97 | def testL2RegularizerWithWeight(self): 98 | with self.test_session(): 99 | shape = [5, 5, 5] 100 | num_elem = 5 * 5 * 5 101 | tensor = tf.constant(1.0, shape=shape) 102 | weight = 0.01 103 | loss = losses.l2_regularizer(weight)(tensor) 104 | self.assertEquals(loss.op.name, 'L2Regularizer/value') 105 | self.assertAlmostEqual(loss.eval(), num_elem * weight / 2, 5) 106 | 107 | def testL1L2Regularizer(self): 108 | with self.test_session(): 109 | shape = [5, 5, 5] 110 | num_elem = 5 * 5 * 5 111 | tensor = tf.constant(1.0, shape=shape) 112 | loss = losses.l1_l2_regularizer()(tensor) 113 | self.assertEquals(loss.op.name, 'L1L2Regularizer/value') 114 | self.assertAlmostEqual(loss.eval(), num_elem + num_elem / 2, 5) 115 | 116 | def testL1L2RegularizerWithScope(self): 117 | with self.test_session(): 118 | shape = [5, 5, 5] 119 | num_elem = 5 * 5 * 5 120 | tensor = tf.constant(1.0, shape=shape) 121 | loss = losses.l1_l2_regularizer(scope='L1L2')(tensor) 122 | self.assertEquals(loss.op.name, 'L1L2/value') 123 | self.assertAlmostEqual(loss.eval(), num_elem + num_elem / 2, 5) 124 | 125 | def testL1L2RegularizerWithWeights(self): 126 | with self.test_session(): 127 | shape = [5, 5, 5] 128 | num_elem = 5 * 5 * 5 129 | tensor = tf.constant(1.0, shape=shape) 130 | weight_l1 = 0.01 131 | weight_l2 = 0.05 132 | loss = losses.l1_l2_regularizer(weight_l1, weight_l2)(tensor) 133 | self.assertEquals(loss.op.name, 'L1L2Regularizer/value') 134 | self.assertAlmostEqual(loss.eval(), 135 | num_elem * weight_l1 + num_elem * weight_l2 / 2, 5) 136 | 137 | 138 | class CrossEntropyLossTest(tf.test.TestCase): 139 | 140 | def testCrossEntropyLossAllCorrect(self): 141 | with self.test_session(): 142 | logits = tf.constant([[10.0, 0.0, 0.0], 143 | [0.0, 10.0, 0.0], 144 | [0.0, 0.0, 10.0]]) 145 | labels = tf.constant([[1, 0, 0], 146 | [0, 1, 0], 147 | [0, 0, 1]]) 148 | loss = losses.cross_entropy_loss(logits, labels) 149 | self.assertEquals(loss.op.name, 'CrossEntropyLoss/value') 150 | self.assertAlmostEqual(loss.eval(), 0.0, 3) 151 | 152 | def testCrossEntropyLossAllWrong(self): 153 | with self.test_session(): 154 | logits = tf.constant([[10.0, 0.0, 0.0], 155 | [0.0, 10.0, 0.0], 156 | [0.0, 0.0, 10.0]]) 157 | labels = tf.constant([[0, 0, 1], 158 | [1, 0, 0], 159 | [0, 1, 0]]) 160 | loss = losses.cross_entropy_loss(logits, labels) 161 | self.assertEquals(loss.op.name, 'CrossEntropyLoss/value') 162 | self.assertAlmostEqual(loss.eval(), 10.0, 3) 163 | 164 | def testCrossEntropyLossAllWrongWithWeight(self): 165 | with self.test_session(): 166 | logits = tf.constant([[10.0, 0.0, 0.0], 167 | [0.0, 10.0, 0.0], 168 | [0.0, 0.0, 10.0]]) 169 | labels = tf.constant([[0, 0, 1], 170 | [1, 0, 0], 171 | [0, 1, 0]]) 172 | loss = losses.cross_entropy_loss(logits, labels, weight=0.5) 173 | self.assertEquals(loss.op.name, 'CrossEntropyLoss/value') 174 | self.assertAlmostEqual(loss.eval(), 5.0, 3) 175 | 176 | if __name__ == '__main__': 177 | tf.test.main() 178 | -------------------------------------------------------------------------------- /inception/slim/ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Contains convenience wrappers for typical Neural Network TensorFlow layers. 16 | 17 | Additionally it maintains a collection with update_ops that need to be 18 | updated after the ops have been computed, for exmaple to update moving means 19 | and moving variances of batch_norm. 20 | 21 | Ops that have different behavior during training or eval have an is_training 22 | parameter. Additionally Ops that contain variables.variable have a trainable 23 | parameter, which control if the ops variables are trainable or not. 24 | """ 25 | from __future__ import absolute_import 26 | from __future__ import division 27 | from __future__ import print_function 28 | 29 | 30 | import tensorflow as tf 31 | 32 | from tensorflow.python.training import moving_averages 33 | 34 | from inception.slim import losses 35 | from inception.slim import scopes 36 | from inception.slim import variables 37 | 38 | # Used to keep the update ops done by batch_norm. 39 | UPDATE_OPS_COLLECTION = '_update_ops_' 40 | 41 | 42 | @scopes.add_arg_scope 43 | def batch_norm(inputs, 44 | decay=0.999, 45 | center=True, 46 | scale=False, 47 | epsilon=0.001, 48 | moving_vars='moving_vars', 49 | activation=None, 50 | is_training=True, 51 | trainable=True, 52 | restore=True, 53 | scope=None, 54 | reuse=None): 55 | """Adds a Batch Normalization layer. 56 | 57 | Args: 58 | inputs: a tensor of size [batch_size, height, width, channels] 59 | or [batch_size, channels]. 60 | decay: decay for the moving average. 61 | center: If True, subtract beta. If False, beta is not created and ignored. 62 | scale: If True, multiply by gamma. If False, gamma is 63 | not used. When the next layer is linear (also e.g. ReLU), this can be 64 | disabled since the scaling can be done by the next layer. 65 | epsilon: small float added to variance to avoid dividing by zero. 66 | moving_vars: collection to store the moving_mean and moving_variance. 67 | activation: activation function. 68 | is_training: whether or not the model is in training mode. 69 | trainable: whether or not the variables should be trainable or not. 70 | restore: whether or not the variables should be marked for restore. 71 | scope: Optional scope for variable_scope. 72 | reuse: whether or not the layer and its variables should be reused. To be 73 | able to reuse the layer scope must be given. 74 | 75 | Returns: 76 | a tensor representing the output of the operation. 77 | 78 | """ 79 | inputs_shape = inputs.get_shape() 80 | with tf.variable_scope(scope, 'BatchNorm', [inputs], reuse=reuse): 81 | axis = list(range(len(inputs_shape) - 1)) 82 | params_shape = inputs_shape[-1:] 83 | # Allocate parameters for the beta and gamma of the normalization. 84 | beta, gamma = None, None 85 | if center: 86 | beta = variables.variable('beta', 87 | params_shape, 88 | initializer=tf.zeros_initializer(), 89 | trainable=trainable, 90 | restore=restore) 91 | if scale: 92 | gamma = variables.variable('gamma', 93 | params_shape, 94 | initializer=tf.ones_initializer(), 95 | trainable=trainable, 96 | restore=restore) 97 | # Create moving_mean and moving_variance add them to 98 | # GraphKeys.MOVING_AVERAGE_VARIABLES collections. 99 | moving_collections = [moving_vars, tf.GraphKeys.MOVING_AVERAGE_VARIABLES] 100 | moving_mean = variables.variable('moving_mean', 101 | params_shape, 102 | initializer=tf.zeros_initializer(), 103 | trainable=False, 104 | restore=restore, 105 | collections=moving_collections) 106 | moving_variance = variables.variable('moving_variance', 107 | params_shape, 108 | initializer=tf.ones_initializer(), 109 | trainable=False, 110 | restore=restore, 111 | collections=moving_collections) 112 | if is_training: 113 | # Calculate the moments based on the individual batch. 114 | mean, variance = tf.nn.moments(inputs, axis) 115 | 116 | update_moving_mean = moving_averages.assign_moving_average( 117 | moving_mean, mean, decay) 118 | tf.add_to_collection(UPDATE_OPS_COLLECTION, update_moving_mean) 119 | update_moving_variance = moving_averages.assign_moving_average( 120 | moving_variance, variance, decay) 121 | tf.add_to_collection(UPDATE_OPS_COLLECTION, update_moving_variance) 122 | else: 123 | # Just use the moving_mean and moving_variance. 124 | mean = moving_mean 125 | variance = moving_variance 126 | # Normalize the activations. 127 | outputs = tf.nn.batch_normalization( 128 | inputs, mean, variance, beta, gamma, epsilon) 129 | outputs.set_shape(inputs.get_shape()) 130 | if activation: 131 | outputs = activation(outputs) 132 | return outputs 133 | 134 | 135 | def _two_element_tuple(int_or_tuple): 136 | """Converts `int_or_tuple` to height, width. 137 | 138 | Several of the functions that follow accept arguments as either 139 | a tuple of 2 integers or a single integer. A single integer 140 | indicates that the 2 values of the tuple are the same. 141 | 142 | This functions normalizes the input value by always returning a tuple. 143 | 144 | Args: 145 | int_or_tuple: A list of 2 ints, a single int or a tf.TensorShape. 146 | 147 | Returns: 148 | A tuple with 2 values. 149 | 150 | Raises: 151 | ValueError: If `int_or_tuple` it not well formed. 152 | """ 153 | if isinstance(int_or_tuple, (list, tuple)): 154 | if len(int_or_tuple) != 2: 155 | raise ValueError('Must be a list with 2 elements: %s' % int_or_tuple) 156 | return int(int_or_tuple[0]), int(int_or_tuple[1]) 157 | if isinstance(int_or_tuple, int): 158 | return int(int_or_tuple), int(int_or_tuple) 159 | if isinstance(int_or_tuple, tf.TensorShape): 160 | if len(int_or_tuple) == 2: 161 | return int_or_tuple[0], int_or_tuple[1] 162 | raise ValueError('Must be an int, a list with 2 elements or a TensorShape of ' 163 | 'length 2') 164 | 165 | 166 | @scopes.add_arg_scope 167 | def conv2d(inputs, 168 | num_filters_out, 169 | kernel_size, 170 | stride=1, 171 | padding='SAME', 172 | activation=tf.nn.relu, 173 | stddev=0.01, 174 | bias=0.0, 175 | weight_decay=0, 176 | batch_norm_params=None, 177 | is_training=True, 178 | trainable=True, 179 | restore=True, 180 | scope=None, 181 | reuse=None): 182 | """Adds a 2D convolution followed by an optional batch_norm layer. 183 | 184 | conv2d creates a variable called 'weights', representing the convolutional 185 | kernel, that is convolved with the input. If `batch_norm_params` is None, a 186 | second variable called 'biases' is added to the result of the convolution 187 | operation. 188 | 189 | Args: 190 | inputs: a tensor of size [batch_size, height, width, channels]. 191 | num_filters_out: the number of output filters. 192 | kernel_size: a list of length 2: [kernel_height, kernel_width] of 193 | of the filters. Can be an int if both values are the same. 194 | stride: a list of length 2: [stride_height, stride_width]. 195 | Can be an int if both strides are the same. Note that presently 196 | both strides must have the same value. 197 | padding: one of 'VALID' or 'SAME'. 198 | activation: activation function. 199 | stddev: standard deviation of the truncated guassian weight distribution. 200 | bias: the initial value of the biases. 201 | weight_decay: the weight decay. 202 | batch_norm_params: parameters for the batch_norm. If is None don't use it. 203 | is_training: whether or not the model is in training mode. 204 | trainable: whether or not the variables should be trainable or not. 205 | restore: whether or not the variables should be marked for restore. 206 | scope: Optional scope for variable_scope. 207 | reuse: whether or not the layer and its variables should be reused. To be 208 | able to reuse the layer scope must be given. 209 | Returns: 210 | a tensor representing the output of the operation. 211 | 212 | """ 213 | with tf.variable_scope(scope, 'Conv', [inputs], reuse=reuse): 214 | kernel_h, kernel_w = _two_element_tuple(kernel_size) 215 | stride_h, stride_w = _two_element_tuple(stride) 216 | num_filters_in = inputs.get_shape()[-1] 217 | weights_shape = [kernel_h, kernel_w, 218 | num_filters_in, num_filters_out] 219 | weights_initializer = tf.truncated_normal_initializer(stddev=stddev) 220 | l2_regularizer = None 221 | if weight_decay and weight_decay > 0: 222 | l2_regularizer = losses.l2_regularizer(weight_decay) 223 | weights = variables.variable('weights', 224 | shape=weights_shape, 225 | initializer=weights_initializer, 226 | regularizer=l2_regularizer, 227 | trainable=trainable, 228 | restore=restore) 229 | conv = tf.nn.conv2d(inputs, weights, [1, stride_h, stride_w, 1], 230 | padding=padding) 231 | if batch_norm_params is not None: 232 | with scopes.arg_scope([batch_norm], is_training=is_training, 233 | trainable=trainable, restore=restore): 234 | outputs = batch_norm(conv, **batch_norm_params) 235 | else: 236 | bias_shape = [num_filters_out,] 237 | bias_initializer = tf.constant_initializer(bias) 238 | biases = variables.variable('biases', 239 | shape=bias_shape, 240 | initializer=bias_initializer, 241 | trainable=trainable, 242 | restore=restore) 243 | outputs = tf.nn.bias_add(conv, biases) 244 | if activation: 245 | outputs = activation(outputs) 246 | return outputs 247 | 248 | 249 | @scopes.add_arg_scope 250 | def fc(inputs, 251 | num_units_out, 252 | activation=tf.nn.relu, 253 | stddev=0.01, 254 | bias=0.0, 255 | weight_decay=0, 256 | batch_norm_params=None, 257 | is_training=True, 258 | trainable=True, 259 | restore=True, 260 | scope=None, 261 | reuse=None): 262 | """Adds a fully connected layer followed by an optional batch_norm layer. 263 | 264 | FC creates a variable called 'weights', representing the fully connected 265 | weight matrix, that is multiplied by the input. If `batch_norm` is None, a 266 | second variable called 'biases' is added to the result of the initial 267 | vector-matrix multiplication. 268 | 269 | Args: 270 | inputs: a [B x N] tensor where B is the batch size and N is the number of 271 | input units in the layer. 272 | num_units_out: the number of output units in the layer. 273 | activation: activation function. 274 | stddev: the standard deviation for the weights. 275 | bias: the initial value of the biases. 276 | weight_decay: the weight decay. 277 | batch_norm_params: parameters for the batch_norm. If is None don't use it. 278 | is_training: whether or not the model is in training mode. 279 | trainable: whether or not the variables should be trainable or not. 280 | restore: whether or not the variables should be marked for restore. 281 | scope: Optional scope for variable_scope. 282 | reuse: whether or not the layer and its variables should be reused. To be 283 | able to reuse the layer scope must be given. 284 | 285 | Returns: 286 | the tensor variable representing the result of the series of operations. 287 | """ 288 | with tf.variable_scope(scope, 'FC', [inputs], reuse=reuse): 289 | num_units_in = inputs.get_shape()[1] 290 | weights_shape = [num_units_in, num_units_out] 291 | weights_initializer = tf.truncated_normal_initializer(stddev=stddev) 292 | l2_regularizer = None 293 | if weight_decay and weight_decay > 0: 294 | l2_regularizer = losses.l2_regularizer(weight_decay) 295 | weights = variables.variable('weights', 296 | shape=weights_shape, 297 | initializer=weights_initializer, 298 | regularizer=l2_regularizer, 299 | trainable=trainable, 300 | restore=restore) 301 | if batch_norm_params is not None: 302 | outputs = tf.matmul(inputs, weights) 303 | with scopes.arg_scope([batch_norm], is_training=is_training, 304 | trainable=trainable, restore=restore): 305 | outputs = batch_norm(outputs, **batch_norm_params) 306 | else: 307 | bias_shape = [num_units_out,] 308 | bias_initializer = tf.constant_initializer(bias) 309 | biases = variables.variable('biases', 310 | shape=bias_shape, 311 | initializer=bias_initializer, 312 | trainable=trainable, 313 | restore=restore) 314 | outputs = tf.nn.xw_plus_b(inputs, weights, biases) 315 | if activation: 316 | outputs = activation(outputs) 317 | return outputs 318 | 319 | 320 | def one_hot_encoding(labels, num_classes, scope=None): 321 | """Transform numeric labels into onehot_labels. 322 | 323 | Args: 324 | labels: [batch_size] target labels. 325 | num_classes: total number of classes. 326 | scope: Optional scope for name_scope. 327 | Returns: 328 | one hot encoding of the labels. 329 | """ 330 | with tf.name_scope(scope, 'OneHotEncoding', [labels]): 331 | batch_size = labels.get_shape()[0] 332 | indices = tf.expand_dims(tf.range(0, batch_size), 1) 333 | labels = tf.cast(tf.expand_dims(labels, 1), indices.dtype) 334 | concated = tf.concat([indices, labels], 1) 335 | onehot_labels = tf.sparse_to_dense( 336 | concated, tf.pack([batch_size, num_classes]), 1.0, 0.0) 337 | onehot_labels.set_shape([batch_size, num_classes]) 338 | return onehot_labels 339 | 340 | 341 | @scopes.add_arg_scope 342 | def max_pool(inputs, kernel_size, stride=2, padding='VALID', scope=None): 343 | """Adds a Max Pooling layer. 344 | 345 | It is assumed by the wrapper that the pooling is only done per image and not 346 | in depth or batch. 347 | 348 | Args: 349 | inputs: a tensor of size [batch_size, height, width, depth]. 350 | kernel_size: a list of length 2: [kernel_height, kernel_width] of the 351 | pooling kernel over which the op is computed. Can be an int if both 352 | values are the same. 353 | stride: a list of length 2: [stride_height, stride_width]. 354 | Can be an int if both strides are the same. Note that presently 355 | both strides must have the same value. 356 | padding: the padding method, either 'VALID' or 'SAME'. 357 | scope: Optional scope for name_scope. 358 | 359 | Returns: 360 | a tensor representing the results of the pooling operation. 361 | Raises: 362 | ValueError: if 'kernel_size' is not a 2-D list 363 | """ 364 | with tf.name_scope(scope, 'MaxPool', [inputs]): 365 | kernel_h, kernel_w = _two_element_tuple(kernel_size) 366 | stride_h, stride_w = _two_element_tuple(stride) 367 | return tf.nn.max_pool(inputs, 368 | ksize=[1, kernel_h, kernel_w, 1], 369 | strides=[1, stride_h, stride_w, 1], 370 | padding=padding) 371 | 372 | 373 | @scopes.add_arg_scope 374 | def avg_pool(inputs, kernel_size, stride=2, padding='VALID', scope=None): 375 | """Adds a Avg Pooling layer. 376 | 377 | It is assumed by the wrapper that the pooling is only done per image and not 378 | in depth or batch. 379 | 380 | Args: 381 | inputs: a tensor of size [batch_size, height, width, depth]. 382 | kernel_size: a list of length 2: [kernel_height, kernel_width] of the 383 | pooling kernel over which the op is computed. Can be an int if both 384 | values are the same. 385 | stride: a list of length 2: [stride_height, stride_width]. 386 | Can be an int if both strides are the same. Note that presently 387 | both strides must have the same value. 388 | padding: the padding method, either 'VALID' or 'SAME'. 389 | scope: Optional scope for name_scope. 390 | 391 | Returns: 392 | a tensor representing the results of the pooling operation. 393 | """ 394 | with tf.name_scope(scope, 'AvgPool', [inputs]): 395 | kernel_h, kernel_w = _two_element_tuple(kernel_size) 396 | stride_h, stride_w = _two_element_tuple(stride) 397 | return tf.nn.avg_pool(inputs, 398 | ksize=[1, kernel_h, kernel_w, 1], 399 | strides=[1, stride_h, stride_w, 1], 400 | padding=padding) 401 | 402 | 403 | @scopes.add_arg_scope 404 | def dropout(inputs, keep_prob=0.5, is_training=True, scope=None): 405 | """Returns a dropout layer applied to the input. 406 | 407 | Args: 408 | inputs: the tensor to pass to the Dropout layer. 409 | keep_prob: the probability of keeping each input unit. 410 | is_training: whether or not the model is in training mode. If so, dropout is 411 | applied and values scaled. Otherwise, inputs is returned. 412 | scope: Optional scope for name_scope. 413 | 414 | Returns: 415 | a tensor representing the output of the operation. 416 | """ 417 | if is_training and keep_prob > 0: 418 | with tf.name_scope(scope, 'Dropout', [inputs]): 419 | return tf.nn.dropout(inputs, keep_prob) 420 | else: 421 | return inputs 422 | 423 | 424 | def flatten(inputs, scope=None): 425 | """Flattens the input while maintaining the batch_size. 426 | 427 | Assumes that the first dimension represents the batch. 428 | 429 | Args: 430 | inputs: a tensor of size [batch_size, ...]. 431 | scope: Optional scope for name_scope. 432 | 433 | Returns: 434 | a flattened tensor with shape [batch_size, k]. 435 | Raises: 436 | ValueError: if inputs.shape is wrong. 437 | """ 438 | if len(inputs.get_shape()) < 2: 439 | raise ValueError('Inputs must be have a least 2 dimensions') 440 | dims = inputs.get_shape()[1:] 441 | k = dims.num_elements() 442 | with tf.name_scope(scope, 'Flatten', [inputs]): 443 | return tf.reshape(inputs, [-1, k]) 444 | 445 | 446 | def repeat_op(repetitions, inputs, op, *args, **kwargs): 447 | """Build a sequential Tower starting from inputs by using an op repeatedly. 448 | 449 | It creates new scopes for each operation by increasing the counter. 450 | Example: given repeat_op(3, _, ops.conv2d, 64, [3, 3], scope='conv1') 451 | it will repeat the given op under the following variable_scopes: 452 | conv1/Conv 453 | conv1/Conv_1 454 | conv1/Conv_2 455 | 456 | Args: 457 | repetitions: number or repetitions. 458 | inputs: a tensor of size [batch_size, height, width, channels]. 459 | op: an operation. 460 | *args: args for the op. 461 | **kwargs: kwargs for the op. 462 | 463 | Returns: 464 | a tensor result of applying the operation op, num times. 465 | Raises: 466 | ValueError: if the op is unknown or wrong. 467 | """ 468 | scope = kwargs.pop('scope', None) 469 | with tf.variable_scope(scope, 'RepeatOp', [inputs]): 470 | tower = inputs 471 | for _ in range(repetitions): 472 | tower = op(tower, *args, **kwargs) 473 | return tower 474 | -------------------------------------------------------------------------------- /inception/slim/ops_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Tests for slim.ops.""" 16 | from __future__ import absolute_import 17 | from __future__ import division 18 | from __future__ import print_function 19 | 20 | 21 | import numpy as np 22 | import tensorflow as tf 23 | 24 | from tensorflow.python.ops import control_flow_ops 25 | 26 | from inception.slim import ops 27 | from inception.slim import scopes 28 | from inception.slim import variables 29 | 30 | 31 | class ConvTest(tf.test.TestCase): 32 | 33 | def testCreateConv(self): 34 | height, width = 3, 3 35 | with self.test_session(): 36 | images = tf.random_uniform((5, height, width, 3), seed=1) 37 | output = ops.conv2d(images, 32, [3, 3]) 38 | self.assertEquals(output.op.name, 'Conv/Relu') 39 | self.assertListEqual(output.get_shape().as_list(), [5, height, width, 32]) 40 | 41 | def testCreateSquareConv(self): 42 | height, width = 3, 3 43 | with self.test_session(): 44 | images = tf.random_uniform((5, height, width, 3), seed=1) 45 | output = ops.conv2d(images, 32, 3) 46 | self.assertEquals(output.op.name, 'Conv/Relu') 47 | self.assertListEqual(output.get_shape().as_list(), [5, height, width, 32]) 48 | 49 | def testCreateConvWithTensorShape(self): 50 | height, width = 3, 3 51 | with self.test_session(): 52 | images = tf.random_uniform((5, height, width, 3), seed=1) 53 | output = ops.conv2d(images, 32, images.get_shape()[1:3]) 54 | self.assertEquals(output.op.name, 'Conv/Relu') 55 | self.assertListEqual(output.get_shape().as_list(), [5, height, width, 32]) 56 | 57 | def testCreateFullyConv(self): 58 | height, width = 6, 6 59 | with self.test_session(): 60 | images = tf.random_uniform((5, height, width, 32), seed=1) 61 | output = ops.conv2d(images, 64, images.get_shape()[1:3], padding='VALID') 62 | self.assertEquals(output.op.name, 'Conv/Relu') 63 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 64]) 64 | 65 | def testCreateVerticalConv(self): 66 | height, width = 3, 3 67 | with self.test_session(): 68 | images = tf.random_uniform((5, height, width, 3), seed=1) 69 | output = ops.conv2d(images, 32, [3, 1]) 70 | self.assertEquals(output.op.name, 'Conv/Relu') 71 | self.assertListEqual(output.get_shape().as_list(), 72 | [5, height, width, 32]) 73 | 74 | def testCreateHorizontalConv(self): 75 | height, width = 3, 3 76 | with self.test_session(): 77 | images = tf.random_uniform((5, height, width, 3), seed=1) 78 | output = ops.conv2d(images, 32, [1, 3]) 79 | self.assertEquals(output.op.name, 'Conv/Relu') 80 | self.assertListEqual(output.get_shape().as_list(), 81 | [5, height, width, 32]) 82 | 83 | def testCreateConvWithStride(self): 84 | height, width = 6, 6 85 | with self.test_session(): 86 | images = tf.random_uniform((5, height, width, 3), seed=1) 87 | output = ops.conv2d(images, 32, [3, 3], stride=2) 88 | self.assertEquals(output.op.name, 'Conv/Relu') 89 | self.assertListEqual(output.get_shape().as_list(), 90 | [5, height/2, width/2, 32]) 91 | 92 | def testCreateConvCreatesWeightsAndBiasesVars(self): 93 | height, width = 3, 3 94 | images = tf.random_uniform((5, height, width, 3), seed=1) 95 | with self.test_session(): 96 | self.assertFalse(variables.get_variables('conv1/weights')) 97 | self.assertFalse(variables.get_variables('conv1/biases')) 98 | ops.conv2d(images, 32, [3, 3], scope='conv1') 99 | self.assertTrue(variables.get_variables('conv1/weights')) 100 | self.assertTrue(variables.get_variables('conv1/biases')) 101 | 102 | def testCreateConvWithScope(self): 103 | height, width = 3, 3 104 | with self.test_session(): 105 | images = tf.random_uniform((5, height, width, 3), seed=1) 106 | output = ops.conv2d(images, 32, [3, 3], scope='conv1') 107 | self.assertEquals(output.op.name, 'conv1/Relu') 108 | 109 | def testCreateConvWithoutActivation(self): 110 | height, width = 3, 3 111 | with self.test_session(): 112 | images = tf.random_uniform((5, height, width, 3), seed=1) 113 | output = ops.conv2d(images, 32, [3, 3], activation=None) 114 | self.assertEquals(output.op.name, 'Conv/BiasAdd') 115 | 116 | def testCreateConvValid(self): 117 | height, width = 3, 3 118 | with self.test_session(): 119 | images = tf.random_uniform((5, height, width, 3), seed=1) 120 | output = ops.conv2d(images, 32, [3, 3], padding='VALID') 121 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 32]) 122 | 123 | def testCreateConvWithWD(self): 124 | height, width = 3, 3 125 | with self.test_session() as sess: 126 | images = tf.random_uniform((5, height, width, 3), seed=1) 127 | ops.conv2d(images, 32, [3, 3], weight_decay=0.01) 128 | wd = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)[0] 129 | self.assertEquals(wd.op.name, 130 | 'Conv/weights/Regularizer/L2Regularizer/value') 131 | sess.run(tf.global_variables_initializer()) 132 | self.assertTrue(sess.run(wd) <= 0.01) 133 | 134 | def testCreateConvWithoutWD(self): 135 | height, width = 3, 3 136 | with self.test_session(): 137 | images = tf.random_uniform((5, height, width, 3), seed=1) 138 | ops.conv2d(images, 32, [3, 3], weight_decay=0) 139 | self.assertEquals( 140 | tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES), []) 141 | 142 | def testReuseVars(self): 143 | height, width = 3, 3 144 | with self.test_session(): 145 | images = tf.random_uniform((5, height, width, 3), seed=1) 146 | ops.conv2d(images, 32, [3, 3], scope='conv1') 147 | self.assertEquals(len(variables.get_variables()), 2) 148 | ops.conv2d(images, 32, [3, 3], scope='conv1', reuse=True) 149 | self.assertEquals(len(variables.get_variables()), 2) 150 | 151 | def testNonReuseVars(self): 152 | height, width = 3, 3 153 | with self.test_session(): 154 | images = tf.random_uniform((5, height, width, 3), seed=1) 155 | ops.conv2d(images, 32, [3, 3]) 156 | self.assertEquals(len(variables.get_variables()), 2) 157 | ops.conv2d(images, 32, [3, 3]) 158 | self.assertEquals(len(variables.get_variables()), 4) 159 | 160 | def testReuseConvWithWD(self): 161 | height, width = 3, 3 162 | with self.test_session(): 163 | images = tf.random_uniform((5, height, width, 3), seed=1) 164 | ops.conv2d(images, 32, [3, 3], weight_decay=0.01, scope='conv1') 165 | self.assertEquals(len(variables.get_variables()), 2) 166 | self.assertEquals( 167 | len(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)), 1) 168 | ops.conv2d(images, 32, [3, 3], weight_decay=0.01, scope='conv1', 169 | reuse=True) 170 | self.assertEquals(len(variables.get_variables()), 2) 171 | self.assertEquals( 172 | len(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)), 1) 173 | 174 | def testConvWithBatchNorm(self): 175 | height, width = 3, 3 176 | with self.test_session(): 177 | images = tf.random_uniform((5, height, width, 32), seed=1) 178 | with scopes.arg_scope([ops.conv2d], batch_norm_params={'decay': 0.9}): 179 | net = ops.conv2d(images, 32, [3, 3]) 180 | net = ops.conv2d(net, 32, [3, 3]) 181 | self.assertEquals(len(variables.get_variables()), 8) 182 | self.assertEquals(len(variables.get_variables('Conv/BatchNorm')), 3) 183 | self.assertEquals(len(variables.get_variables('Conv_1/BatchNorm')), 3) 184 | 185 | def testReuseConvWithBatchNorm(self): 186 | height, width = 3, 3 187 | with self.test_session(): 188 | images = tf.random_uniform((5, height, width, 32), seed=1) 189 | with scopes.arg_scope([ops.conv2d], batch_norm_params={'decay': 0.9}): 190 | net = ops.conv2d(images, 32, [3, 3], scope='Conv') 191 | net = ops.conv2d(net, 32, [3, 3], scope='Conv', reuse=True) 192 | self.assertEquals(len(variables.get_variables()), 4) 193 | self.assertEquals(len(variables.get_variables('Conv/BatchNorm')), 3) 194 | self.assertEquals(len(variables.get_variables('Conv_1/BatchNorm')), 0) 195 | 196 | 197 | class FCTest(tf.test.TestCase): 198 | 199 | def testCreateFC(self): 200 | height, width = 3, 3 201 | with self.test_session(): 202 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 203 | output = ops.fc(inputs, 32) 204 | self.assertEquals(output.op.name, 'FC/Relu') 205 | self.assertListEqual(output.get_shape().as_list(), [5, 32]) 206 | 207 | def testCreateFCWithScope(self): 208 | height, width = 3, 3 209 | with self.test_session(): 210 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 211 | output = ops.fc(inputs, 32, scope='fc1') 212 | self.assertEquals(output.op.name, 'fc1/Relu') 213 | 214 | def testCreateFcCreatesWeightsAndBiasesVars(self): 215 | height, width = 3, 3 216 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 217 | with self.test_session(): 218 | self.assertFalse(variables.get_variables('fc1/weights')) 219 | self.assertFalse(variables.get_variables('fc1/biases')) 220 | ops.fc(inputs, 32, scope='fc1') 221 | self.assertTrue(variables.get_variables('fc1/weights')) 222 | self.assertTrue(variables.get_variables('fc1/biases')) 223 | 224 | def testReuseVars(self): 225 | height, width = 3, 3 226 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 227 | with self.test_session(): 228 | ops.fc(inputs, 32, scope='fc1') 229 | self.assertEquals(len(variables.get_variables('fc1')), 2) 230 | ops.fc(inputs, 32, scope='fc1', reuse=True) 231 | self.assertEquals(len(variables.get_variables('fc1')), 2) 232 | 233 | def testNonReuseVars(self): 234 | height, width = 3, 3 235 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 236 | with self.test_session(): 237 | ops.fc(inputs, 32) 238 | self.assertEquals(len(variables.get_variables('FC')), 2) 239 | ops.fc(inputs, 32) 240 | self.assertEquals(len(variables.get_variables('FC')), 4) 241 | 242 | def testCreateFCWithoutActivation(self): 243 | height, width = 3, 3 244 | with self.test_session(): 245 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 246 | output = ops.fc(inputs, 32, activation=None) 247 | self.assertEquals(output.op.name, 'FC/xw_plus_b') 248 | 249 | def testCreateFCWithWD(self): 250 | height, width = 3, 3 251 | with self.test_session() as sess: 252 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 253 | ops.fc(inputs, 32, weight_decay=0.01) 254 | wd = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)[0] 255 | self.assertEquals(wd.op.name, 256 | 'FC/weights/Regularizer/L2Regularizer/value') 257 | sess.run(tf.global_variables_initializer()) 258 | self.assertTrue(sess.run(wd) <= 0.01) 259 | 260 | def testCreateFCWithoutWD(self): 261 | height, width = 3, 3 262 | with self.test_session(): 263 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 264 | ops.fc(inputs, 32, weight_decay=0) 265 | self.assertEquals( 266 | tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES), []) 267 | 268 | def testReuseFCWithWD(self): 269 | height, width = 3, 3 270 | with self.test_session(): 271 | inputs = tf.random_uniform((5, height * width * 3), seed=1) 272 | ops.fc(inputs, 32, weight_decay=0.01, scope='fc') 273 | self.assertEquals(len(variables.get_variables()), 2) 274 | self.assertEquals( 275 | len(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)), 1) 276 | ops.fc(inputs, 32, weight_decay=0.01, scope='fc', reuse=True) 277 | self.assertEquals(len(variables.get_variables()), 2) 278 | self.assertEquals( 279 | len(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)), 1) 280 | 281 | def testFCWithBatchNorm(self): 282 | height, width = 3, 3 283 | with self.test_session(): 284 | images = tf.random_uniform((5, height * width * 3), seed=1) 285 | with scopes.arg_scope([ops.fc], batch_norm_params={}): 286 | net = ops.fc(images, 27) 287 | net = ops.fc(net, 27) 288 | self.assertEquals(len(variables.get_variables()), 8) 289 | self.assertEquals(len(variables.get_variables('FC/BatchNorm')), 3) 290 | self.assertEquals(len(variables.get_variables('FC_1/BatchNorm')), 3) 291 | 292 | def testReuseFCWithBatchNorm(self): 293 | height, width = 3, 3 294 | with self.test_session(): 295 | images = tf.random_uniform((5, height * width * 3), seed=1) 296 | with scopes.arg_scope([ops.fc], batch_norm_params={'decay': 0.9}): 297 | net = ops.fc(images, 27, scope='fc1') 298 | net = ops.fc(net, 27, scope='fc1', reuse=True) 299 | self.assertEquals(len(variables.get_variables()), 4) 300 | self.assertEquals(len(variables.get_variables('fc1/BatchNorm')), 3) 301 | 302 | 303 | class MaxPoolTest(tf.test.TestCase): 304 | 305 | def testCreateMaxPool(self): 306 | height, width = 3, 3 307 | with self.test_session(): 308 | images = tf.random_uniform((5, height, width, 3), seed=1) 309 | output = ops.max_pool(images, [3, 3]) 310 | self.assertEquals(output.op.name, 'MaxPool/MaxPool') 311 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 3]) 312 | 313 | def testCreateSquareMaxPool(self): 314 | height, width = 3, 3 315 | with self.test_session(): 316 | images = tf.random_uniform((5, height, width, 3), seed=1) 317 | output = ops.max_pool(images, 3) 318 | self.assertEquals(output.op.name, 'MaxPool/MaxPool') 319 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 3]) 320 | 321 | def testCreateMaxPoolWithScope(self): 322 | height, width = 3, 3 323 | with self.test_session(): 324 | images = tf.random_uniform((5, height, width, 3), seed=1) 325 | output = ops.max_pool(images, [3, 3], scope='pool1') 326 | self.assertEquals(output.op.name, 'pool1/MaxPool') 327 | 328 | def testCreateMaxPoolSAME(self): 329 | height, width = 3, 3 330 | with self.test_session(): 331 | images = tf.random_uniform((5, height, width, 3), seed=1) 332 | output = ops.max_pool(images, [3, 3], padding='SAME') 333 | self.assertListEqual(output.get_shape().as_list(), [5, 2, 2, 3]) 334 | 335 | def testCreateMaxPoolStrideSAME(self): 336 | height, width = 3, 3 337 | with self.test_session(): 338 | images = tf.random_uniform((5, height, width, 3), seed=1) 339 | output = ops.max_pool(images, [3, 3], stride=1, padding='SAME') 340 | self.assertListEqual(output.get_shape().as_list(), [5, height, width, 3]) 341 | 342 | def testGlobalMaxPool(self): 343 | height, width = 3, 3 344 | with self.test_session(): 345 | images = tf.random_uniform((5, height, width, 3), seed=1) 346 | output = ops.max_pool(images, images.get_shape()[1:3], stride=1) 347 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 3]) 348 | 349 | 350 | class AvgPoolTest(tf.test.TestCase): 351 | 352 | def testCreateAvgPool(self): 353 | height, width = 3, 3 354 | with self.test_session(): 355 | images = tf.random_uniform((5, height, width, 3), seed=1) 356 | output = ops.avg_pool(images, [3, 3]) 357 | self.assertEquals(output.op.name, 'AvgPool/AvgPool') 358 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 3]) 359 | 360 | def testCreateSquareAvgPool(self): 361 | height, width = 3, 3 362 | with self.test_session(): 363 | images = tf.random_uniform((5, height, width, 3), seed=1) 364 | output = ops.avg_pool(images, 3) 365 | self.assertEquals(output.op.name, 'AvgPool/AvgPool') 366 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 3]) 367 | 368 | def testCreateAvgPoolWithScope(self): 369 | height, width = 3, 3 370 | with self.test_session(): 371 | images = tf.random_uniform((5, height, width, 3), seed=1) 372 | output = ops.avg_pool(images, [3, 3], scope='pool1') 373 | self.assertEquals(output.op.name, 'pool1/AvgPool') 374 | 375 | def testCreateAvgPoolSAME(self): 376 | height, width = 3, 3 377 | with self.test_session(): 378 | images = tf.random_uniform((5, height, width, 3), seed=1) 379 | output = ops.avg_pool(images, [3, 3], padding='SAME') 380 | self.assertListEqual(output.get_shape().as_list(), [5, 2, 2, 3]) 381 | 382 | def testCreateAvgPoolStrideSAME(self): 383 | height, width = 3, 3 384 | with self.test_session(): 385 | images = tf.random_uniform((5, height, width, 3), seed=1) 386 | output = ops.avg_pool(images, [3, 3], stride=1, padding='SAME') 387 | self.assertListEqual(output.get_shape().as_list(), [5, height, width, 3]) 388 | 389 | def testGlobalAvgPool(self): 390 | height, width = 3, 3 391 | with self.test_session(): 392 | images = tf.random_uniform((5, height, width, 3), seed=1) 393 | output = ops.avg_pool(images, images.get_shape()[1:3], stride=1) 394 | self.assertListEqual(output.get_shape().as_list(), [5, 1, 1, 3]) 395 | 396 | 397 | class OneHotEncodingTest(tf.test.TestCase): 398 | 399 | def testOneHotEncodingCreate(self): 400 | with self.test_session(): 401 | labels = tf.constant([0, 1, 2]) 402 | output = ops.one_hot_encoding(labels, num_classes=3) 403 | self.assertEquals(output.op.name, 'OneHotEncoding/SparseToDense') 404 | self.assertListEqual(output.get_shape().as_list(), [3, 3]) 405 | 406 | def testOneHotEncoding(self): 407 | with self.test_session(): 408 | labels = tf.constant([0, 1, 2]) 409 | one_hot_labels = tf.constant([[1, 0, 0], 410 | [0, 1, 0], 411 | [0, 0, 1]]) 412 | output = ops.one_hot_encoding(labels, num_classes=3) 413 | self.assertAllClose(output.eval(), one_hot_labels.eval()) 414 | 415 | 416 | class DropoutTest(tf.test.TestCase): 417 | 418 | def testCreateDropout(self): 419 | height, width = 3, 3 420 | with self.test_session(): 421 | images = tf.random_uniform((5, height, width, 3), seed=1) 422 | output = ops.dropout(images) 423 | self.assertEquals(output.op.name, 'Dropout/dropout/mul_1') 424 | output.get_shape().assert_is_compatible_with(images.get_shape()) 425 | 426 | def testCreateDropoutNoTraining(self): 427 | height, width = 3, 3 428 | with self.test_session(): 429 | images = tf.random_uniform((5, height, width, 3), seed=1, name='images') 430 | output = ops.dropout(images, is_training=False) 431 | self.assertEquals(output, images) 432 | 433 | 434 | class FlattenTest(tf.test.TestCase): 435 | 436 | def testFlatten4D(self): 437 | height, width = 3, 3 438 | with self.test_session(): 439 | images = tf.random_uniform((5, height, width, 3), seed=1, name='images') 440 | output = ops.flatten(images) 441 | self.assertEquals(output.get_shape().num_elements(), 442 | images.get_shape().num_elements()) 443 | self.assertEqual(output.get_shape()[0], images.get_shape()[0]) 444 | 445 | def testFlatten3D(self): 446 | height, width = 3, 3 447 | with self.test_session(): 448 | images = tf.random_uniform((5, height, width), seed=1, name='images') 449 | output = ops.flatten(images) 450 | self.assertEquals(output.get_shape().num_elements(), 451 | images.get_shape().num_elements()) 452 | self.assertEqual(output.get_shape()[0], images.get_shape()[0]) 453 | 454 | def testFlattenBatchSize(self): 455 | height, width = 3, 3 456 | with self.test_session() as sess: 457 | images = tf.random_uniform((5, height, width, 3), seed=1, name='images') 458 | inputs = tf.placeholder(tf.int32, (None, height, width, 3)) 459 | output = ops.flatten(inputs) 460 | self.assertEquals(output.get_shape().as_list(), 461 | [None, height * width * 3]) 462 | output = sess.run(output, {inputs: images.eval()}) 463 | self.assertEquals(output.size, 464 | images.get_shape().num_elements()) 465 | self.assertEqual(output.shape[0], images.get_shape()[0]) 466 | 467 | 468 | class BatchNormTest(tf.test.TestCase): 469 | 470 | def testCreateOp(self): 471 | height, width = 3, 3 472 | with self.test_session(): 473 | images = tf.random_uniform((5, height, width, 3), seed=1) 474 | output = ops.batch_norm(images) 475 | self.assertTrue(output.op.name.startswith('BatchNorm/batchnorm')) 476 | self.assertListEqual(output.get_shape().as_list(), [5, height, width, 3]) 477 | 478 | def testCreateVariables(self): 479 | height, width = 3, 3 480 | with self.test_session(): 481 | images = tf.random_uniform((5, height, width, 3), seed=1) 482 | ops.batch_norm(images) 483 | beta = variables.get_variables_by_name('beta')[0] 484 | self.assertEquals(beta.op.name, 'BatchNorm/beta') 485 | gamma = variables.get_variables_by_name('gamma') 486 | self.assertEquals(gamma, []) 487 | moving_mean = tf.moving_average_variables()[0] 488 | moving_variance = tf.moving_average_variables()[1] 489 | self.assertEquals(moving_mean.op.name, 'BatchNorm/moving_mean') 490 | self.assertEquals(moving_variance.op.name, 'BatchNorm/moving_variance') 491 | 492 | def testCreateVariablesWithScale(self): 493 | height, width = 3, 3 494 | with self.test_session(): 495 | images = tf.random_uniform((5, height, width, 3), seed=1) 496 | ops.batch_norm(images, scale=True) 497 | beta = variables.get_variables_by_name('beta')[0] 498 | gamma = variables.get_variables_by_name('gamma')[0] 499 | self.assertEquals(beta.op.name, 'BatchNorm/beta') 500 | self.assertEquals(gamma.op.name, 'BatchNorm/gamma') 501 | moving_mean = tf.moving_average_variables()[0] 502 | moving_variance = tf.moving_average_variables()[1] 503 | self.assertEquals(moving_mean.op.name, 'BatchNorm/moving_mean') 504 | self.assertEquals(moving_variance.op.name, 'BatchNorm/moving_variance') 505 | 506 | def testCreateVariablesWithoutCenterWithScale(self): 507 | height, width = 3, 3 508 | with self.test_session(): 509 | images = tf.random_uniform((5, height, width, 3), seed=1) 510 | ops.batch_norm(images, center=False, scale=True) 511 | beta = variables.get_variables_by_name('beta') 512 | self.assertEquals(beta, []) 513 | gamma = variables.get_variables_by_name('gamma')[0] 514 | self.assertEquals(gamma.op.name, 'BatchNorm/gamma') 515 | moving_mean = tf.moving_average_variables()[0] 516 | moving_variance = tf.moving_average_variables()[1] 517 | self.assertEquals(moving_mean.op.name, 'BatchNorm/moving_mean') 518 | self.assertEquals(moving_variance.op.name, 'BatchNorm/moving_variance') 519 | 520 | def testCreateVariablesWithoutCenterWithoutScale(self): 521 | height, width = 3, 3 522 | with self.test_session(): 523 | images = tf.random_uniform((5, height, width, 3), seed=1) 524 | ops.batch_norm(images, center=False, scale=False) 525 | beta = variables.get_variables_by_name('beta') 526 | self.assertEquals(beta, []) 527 | gamma = variables.get_variables_by_name('gamma') 528 | self.assertEquals(gamma, []) 529 | moving_mean = tf.moving_average_variables()[0] 530 | moving_variance = tf.moving_average_variables()[1] 531 | self.assertEquals(moving_mean.op.name, 'BatchNorm/moving_mean') 532 | self.assertEquals(moving_variance.op.name, 'BatchNorm/moving_variance') 533 | 534 | def testMovingAverageVariables(self): 535 | height, width = 3, 3 536 | with self.test_session(): 537 | images = tf.random_uniform((5, height, width, 3), seed=1) 538 | ops.batch_norm(images, scale=True) 539 | moving_mean = tf.moving_average_variables()[0] 540 | moving_variance = tf.moving_average_variables()[1] 541 | self.assertEquals(moving_mean.op.name, 'BatchNorm/moving_mean') 542 | self.assertEquals(moving_variance.op.name, 'BatchNorm/moving_variance') 543 | 544 | def testUpdateOps(self): 545 | height, width = 3, 3 546 | with self.test_session(): 547 | images = tf.random_uniform((5, height, width, 3), seed=1) 548 | ops.batch_norm(images) 549 | update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) 550 | update_moving_mean = update_ops[0] 551 | update_moving_variance = update_ops[1] 552 | self.assertEquals(update_moving_mean.op.name, 553 | 'BatchNorm/AssignMovingAvg') 554 | self.assertEquals(update_moving_variance.op.name, 555 | 'BatchNorm/AssignMovingAvg_1') 556 | 557 | def testReuseVariables(self): 558 | height, width = 3, 3 559 | with self.test_session(): 560 | images = tf.random_uniform((5, height, width, 3), seed=1) 561 | ops.batch_norm(images, scale=True, scope='bn') 562 | ops.batch_norm(images, scale=True, scope='bn', reuse=True) 563 | beta = variables.get_variables_by_name('beta') 564 | gamma = variables.get_variables_by_name('gamma') 565 | self.assertEquals(len(beta), 1) 566 | self.assertEquals(len(gamma), 1) 567 | moving_vars = tf.get_collection('moving_vars') 568 | self.assertEquals(len(moving_vars), 2) 569 | 570 | def testReuseUpdateOps(self): 571 | height, width = 3, 3 572 | with self.test_session(): 573 | images = tf.random_uniform((5, height, width, 3), seed=1) 574 | ops.batch_norm(images, scope='bn') 575 | self.assertEquals(len(tf.get_collection(ops.UPDATE_OPS_COLLECTION)), 2) 576 | ops.batch_norm(images, scope='bn', reuse=True) 577 | self.assertEquals(len(tf.get_collection(ops.UPDATE_OPS_COLLECTION)), 4) 578 | 579 | def testCreateMovingVars(self): 580 | height, width = 3, 3 581 | with self.test_session(): 582 | images = tf.random_uniform((5, height, width, 3), seed=1) 583 | _ = ops.batch_norm(images, moving_vars='moving_vars') 584 | moving_mean = tf.get_collection('moving_vars', 585 | 'BatchNorm/moving_mean') 586 | self.assertEquals(len(moving_mean), 1) 587 | self.assertEquals(moving_mean[0].op.name, 'BatchNorm/moving_mean') 588 | moving_variance = tf.get_collection('moving_vars', 589 | 'BatchNorm/moving_variance') 590 | self.assertEquals(len(moving_variance), 1) 591 | self.assertEquals(moving_variance[0].op.name, 'BatchNorm/moving_variance') 592 | 593 | def testComputeMovingVars(self): 594 | height, width = 3, 3 595 | with self.test_session() as sess: 596 | image_shape = (10, height, width, 3) 597 | image_values = np.random.rand(*image_shape) 598 | expected_mean = np.mean(image_values, axis=(0, 1, 2)) 599 | expected_var = np.var(image_values, axis=(0, 1, 2)) 600 | images = tf.constant(image_values, shape=image_shape, dtype=tf.float32) 601 | output = ops.batch_norm(images, decay=0.1) 602 | update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) 603 | with tf.control_dependencies(update_ops): 604 | barrier = tf.no_op(name='gradient_barrier') 605 | output = control_flow_ops.with_dependencies([barrier], output) 606 | # Initialize all variables 607 | sess.run(tf.global_variables_initializer()) 608 | moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] 609 | moving_variance = variables.get_variables('BatchNorm/moving_variance')[0] 610 | mean, variance = sess.run([moving_mean, moving_variance]) 611 | # After initialization moving_mean == 0 and moving_variance == 1. 612 | self.assertAllClose(mean, [0] * 3) 613 | self.assertAllClose(variance, [1] * 3) 614 | for _ in range(10): 615 | sess.run([output]) 616 | mean = moving_mean.eval() 617 | variance = moving_variance.eval() 618 | # After 10 updates with decay 0.1 moving_mean == expected_mean and 619 | # moving_variance == expected_var. 620 | self.assertAllClose(mean, expected_mean) 621 | self.assertAllClose(variance, expected_var) 622 | 623 | def testEvalMovingVars(self): 624 | height, width = 3, 3 625 | with self.test_session() as sess: 626 | image_shape = (10, height, width, 3) 627 | image_values = np.random.rand(*image_shape) 628 | expected_mean = np.mean(image_values, axis=(0, 1, 2)) 629 | expected_var = np.var(image_values, axis=(0, 1, 2)) 630 | images = tf.constant(image_values, shape=image_shape, dtype=tf.float32) 631 | output = ops.batch_norm(images, decay=0.1, is_training=False) 632 | update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) 633 | with tf.control_dependencies(update_ops): 634 | barrier = tf.no_op(name='gradient_barrier') 635 | output = control_flow_ops.with_dependencies([barrier], output) 636 | # Initialize all variables 637 | sess.run(tf.global_variables_initializer()) 638 | moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] 639 | moving_variance = variables.get_variables('BatchNorm/moving_variance')[0] 640 | mean, variance = sess.run([moving_mean, moving_variance]) 641 | # After initialization moving_mean == 0 and moving_variance == 1. 642 | self.assertAllClose(mean, [0] * 3) 643 | self.assertAllClose(variance, [1] * 3) 644 | # Simulate assigment from saver restore. 645 | init_assigns = [tf.assign(moving_mean, expected_mean), 646 | tf.assign(moving_variance, expected_var)] 647 | sess.run(init_assigns) 648 | for _ in range(10): 649 | sess.run([output], {images: np.random.rand(*image_shape)}) 650 | mean = moving_mean.eval() 651 | variance = moving_variance.eval() 652 | # Although we feed different images, the moving_mean and moving_variance 653 | # shouldn't change. 654 | self.assertAllClose(mean, expected_mean) 655 | self.assertAllClose(variance, expected_var) 656 | 657 | def testReuseVars(self): 658 | height, width = 3, 3 659 | with self.test_session() as sess: 660 | image_shape = (10, height, width, 3) 661 | image_values = np.random.rand(*image_shape) 662 | expected_mean = np.mean(image_values, axis=(0, 1, 2)) 663 | expected_var = np.var(image_values, axis=(0, 1, 2)) 664 | images = tf.constant(image_values, shape=image_shape, dtype=tf.float32) 665 | output = ops.batch_norm(images, decay=0.1, is_training=False) 666 | update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) 667 | with tf.control_dependencies(update_ops): 668 | barrier = tf.no_op(name='gradient_barrier') 669 | output = control_flow_ops.with_dependencies([barrier], output) 670 | # Initialize all variables 671 | sess.run(tf.global_variables_initializer()) 672 | moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] 673 | moving_variance = variables.get_variables('BatchNorm/moving_variance')[0] 674 | mean, variance = sess.run([moving_mean, moving_variance]) 675 | # After initialization moving_mean == 0 and moving_variance == 1. 676 | self.assertAllClose(mean, [0] * 3) 677 | self.assertAllClose(variance, [1] * 3) 678 | # Simulate assigment from saver restore. 679 | init_assigns = [tf.assign(moving_mean, expected_mean), 680 | tf.assign(moving_variance, expected_var)] 681 | sess.run(init_assigns) 682 | for _ in range(10): 683 | sess.run([output], {images: np.random.rand(*image_shape)}) 684 | mean = moving_mean.eval() 685 | variance = moving_variance.eval() 686 | # Although we feed different images, the moving_mean and moving_variance 687 | # shouldn't change. 688 | self.assertAllClose(mean, expected_mean) 689 | self.assertAllClose(variance, expected_var) 690 | 691 | if __name__ == '__main__': 692 | tf.test.main() 693 | -------------------------------------------------------------------------------- /inception/slim/scopes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Contains the new arg_scope used for TF-Slim ops. 16 | 17 | Allows one to define models much more compactly by eliminating boilerplate 18 | code. This is accomplished through the use of argument scoping (arg_scope). 19 | 20 | Example of how to use scopes.arg_scope: 21 | 22 | with scopes.arg_scope(ops.conv2d, padding='SAME', 23 | stddev=0.01, weight_decay=0.0005): 24 | net = ops.conv2d(inputs, 64, [11, 11], 4, padding='VALID', scope='conv1') 25 | net = ops.conv2d(net, 256, [5, 5], scope='conv2') 26 | 27 | The first call to conv2d will overwrite padding: 28 | ops.conv2d(inputs, 64, [11, 11], 4, padding='VALID', 29 | stddev=0.01, weight_decay=0.0005, scope='conv1') 30 | 31 | The second call to Conv will use predefined args: 32 | ops.conv2d(inputs, 256, [5, 5], padding='SAME', 33 | stddev=0.01, weight_decay=0.0005, scope='conv2') 34 | 35 | Example of how to reuse an arg_scope: 36 | with scopes.arg_scope(ops.conv2d, padding='SAME', 37 | stddev=0.01, weight_decay=0.0005) as conv2d_arg_scope: 38 | net = ops.conv2d(net, 256, [5, 5], scope='conv1') 39 | .... 40 | 41 | with scopes.arg_scope(conv2d_arg_scope): 42 | net = ops.conv2d(net, 256, [5, 5], scope='conv2') 43 | 44 | Example of how to use scopes.add_arg_scope: 45 | 46 | @scopes.add_arg_scope 47 | def conv2d(*args, **kwargs) 48 | """ 49 | from __future__ import absolute_import 50 | from __future__ import division 51 | from __future__ import print_function 52 | 53 | import contextlib 54 | import functools 55 | 56 | from tensorflow.python.framework import ops 57 | 58 | _ARGSTACK_KEY = ("__arg_stack",) 59 | 60 | _DECORATED_OPS = set() 61 | 62 | 63 | def _get_arg_stack(): 64 | stack = ops.get_collection(_ARGSTACK_KEY) 65 | if stack: 66 | return stack[0] 67 | else: 68 | stack = [{}] 69 | ops.add_to_collection(_ARGSTACK_KEY, stack) 70 | return stack 71 | 72 | 73 | def _current_arg_scope(): 74 | stack = _get_arg_stack() 75 | return stack[-1] 76 | 77 | 78 | def _add_op(op): 79 | key_op = (op.__module__, op.__name__) 80 | if key_op not in _DECORATED_OPS: 81 | _DECORATED_OPS.add(key_op) 82 | 83 | 84 | @contextlib.contextmanager 85 | def arg_scope(list_ops_or_scope, **kwargs): 86 | """Stores the default arguments for the given set of list_ops. 87 | 88 | For usage, please see examples at top of the file. 89 | 90 | Args: 91 | list_ops_or_scope: List or tuple of operations to set argument scope for or 92 | a dictionary containg the current scope. When list_ops_or_scope is a dict, 93 | kwargs must be empty. When list_ops_or_scope is a list or tuple, then 94 | every op in it need to be decorated with @add_arg_scope to work. 95 | **kwargs: keyword=value that will define the defaults for each op in 96 | list_ops. All the ops need to accept the given set of arguments. 97 | 98 | Yields: 99 | the current_scope, which is a dictionary of {op: {arg: value}} 100 | Raises: 101 | TypeError: if list_ops is not a list or a tuple. 102 | ValueError: if any op in list_ops has not be decorated with @add_arg_scope. 103 | """ 104 | if isinstance(list_ops_or_scope, dict): 105 | # Assumes that list_ops_or_scope is a scope that is being reused. 106 | if kwargs: 107 | raise ValueError("When attempting to re-use a scope by suppling a" 108 | "dictionary, kwargs must be empty.") 109 | current_scope = list_ops_or_scope.copy() 110 | try: 111 | _get_arg_stack().append(current_scope) 112 | yield current_scope 113 | finally: 114 | _get_arg_stack().pop() 115 | else: 116 | # Assumes that list_ops_or_scope is a list/tuple of ops with kwargs. 117 | if not isinstance(list_ops_or_scope, (list, tuple)): 118 | raise TypeError("list_ops_or_scope must either be a list/tuple or reused" 119 | "scope (i.e. dict)") 120 | try: 121 | current_scope = _current_arg_scope().copy() 122 | for op in list_ops_or_scope: 123 | key_op = (op.__module__, op.__name__) 124 | if not has_arg_scope(op): 125 | raise ValueError("%s is not decorated with @add_arg_scope", key_op) 126 | if key_op in current_scope: 127 | current_kwargs = current_scope[key_op].copy() 128 | current_kwargs.update(kwargs) 129 | current_scope[key_op] = current_kwargs 130 | else: 131 | current_scope[key_op] = kwargs.copy() 132 | _get_arg_stack().append(current_scope) 133 | yield current_scope 134 | finally: 135 | _get_arg_stack().pop() 136 | 137 | 138 | def add_arg_scope(func): 139 | """Decorates a function with args so it can be used within an arg_scope. 140 | 141 | Args: 142 | func: function to decorate. 143 | 144 | Returns: 145 | A tuple with the decorated function func_with_args(). 146 | """ 147 | @functools.wraps(func) 148 | def func_with_args(*args, **kwargs): 149 | current_scope = _current_arg_scope() 150 | current_args = kwargs 151 | key_func = (func.__module__, func.__name__) 152 | if key_func in current_scope: 153 | current_args = current_scope[key_func].copy() 154 | current_args.update(kwargs) 155 | return func(*args, **current_args) 156 | _add_op(func) 157 | return func_with_args 158 | 159 | 160 | def has_arg_scope(func): 161 | """Checks whether a func has been decorated with @add_arg_scope or not. 162 | 163 | Args: 164 | func: function to check. 165 | 166 | Returns: 167 | a boolean. 168 | """ 169 | key_op = (func.__module__, func.__name__) 170 | return key_op in _DECORATED_OPS 171 | -------------------------------------------------------------------------------- /inception/slim/scopes_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Tests slim.scopes.""" 16 | from __future__ import absolute_import 17 | from __future__ import division 18 | from __future__ import print_function 19 | 20 | 21 | import tensorflow as tf 22 | from inception.slim import scopes 23 | 24 | 25 | @scopes.add_arg_scope 26 | def func1(*args, **kwargs): 27 | return (args, kwargs) 28 | 29 | 30 | @scopes.add_arg_scope 31 | def func2(*args, **kwargs): 32 | return (args, kwargs) 33 | 34 | 35 | class ArgScopeTest(tf.test.TestCase): 36 | 37 | def testEmptyArgScope(self): 38 | with self.test_session(): 39 | self.assertEqual(scopes._current_arg_scope(), {}) 40 | 41 | def testCurrentArgScope(self): 42 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 43 | key_op = (func1.__module__, func1.__name__) 44 | current_scope = {key_op: func1_kwargs.copy()} 45 | with self.test_session(): 46 | with scopes.arg_scope([func1], a=1, b=None, c=[1]) as scope: 47 | self.assertDictEqual(scope, current_scope) 48 | 49 | def testCurrentArgScopeNested(self): 50 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 51 | func2_kwargs = {'b': 2, 'd': [2]} 52 | key = lambda f: (f.__module__, f.__name__) 53 | current_scope = {key(func1): func1_kwargs.copy(), 54 | key(func2): func2_kwargs.copy()} 55 | with self.test_session(): 56 | with scopes.arg_scope([func1], a=1, b=None, c=[1]): 57 | with scopes.arg_scope([func2], b=2, d=[2]) as scope: 58 | self.assertDictEqual(scope, current_scope) 59 | 60 | def testReuseArgScope(self): 61 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 62 | key_op = (func1.__module__, func1.__name__) 63 | current_scope = {key_op: func1_kwargs.copy()} 64 | with self.test_session(): 65 | with scopes.arg_scope([func1], a=1, b=None, c=[1]) as scope1: 66 | pass 67 | with scopes.arg_scope(scope1) as scope: 68 | self.assertDictEqual(scope, current_scope) 69 | 70 | def testReuseArgScopeNested(self): 71 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 72 | func2_kwargs = {'b': 2, 'd': [2]} 73 | key = lambda f: (f.__module__, f.__name__) 74 | current_scope1 = {key(func1): func1_kwargs.copy()} 75 | current_scope2 = {key(func1): func1_kwargs.copy(), 76 | key(func2): func2_kwargs.copy()} 77 | with self.test_session(): 78 | with scopes.arg_scope([func1], a=1, b=None, c=[1]) as scope1: 79 | with scopes.arg_scope([func2], b=2, d=[2]) as scope2: 80 | pass 81 | with scopes.arg_scope(scope1): 82 | self.assertDictEqual(scopes._current_arg_scope(), current_scope1) 83 | with scopes.arg_scope(scope2): 84 | self.assertDictEqual(scopes._current_arg_scope(), current_scope2) 85 | 86 | def testSimpleArgScope(self): 87 | func1_args = (0,) 88 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 89 | with self.test_session(): 90 | with scopes.arg_scope([func1], a=1, b=None, c=[1]): 91 | args, kwargs = func1(0) 92 | self.assertTupleEqual(args, func1_args) 93 | self.assertDictEqual(kwargs, func1_kwargs) 94 | 95 | def testSimpleArgScopeWithTuple(self): 96 | func1_args = (0,) 97 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 98 | with self.test_session(): 99 | with scopes.arg_scope((func1,), a=1, b=None, c=[1]): 100 | args, kwargs = func1(0) 101 | self.assertTupleEqual(args, func1_args) 102 | self.assertDictEqual(kwargs, func1_kwargs) 103 | 104 | def testOverwriteArgScope(self): 105 | func1_args = (0,) 106 | func1_kwargs = {'a': 1, 'b': 2, 'c': [1]} 107 | with scopes.arg_scope([func1], a=1, b=None, c=[1]): 108 | args, kwargs = func1(0, b=2) 109 | self.assertTupleEqual(args, func1_args) 110 | self.assertDictEqual(kwargs, func1_kwargs) 111 | 112 | def testNestedArgScope(self): 113 | func1_args = (0,) 114 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 115 | with scopes.arg_scope([func1], a=1, b=None, c=[1]): 116 | args, kwargs = func1(0) 117 | self.assertTupleEqual(args, func1_args) 118 | self.assertDictEqual(kwargs, func1_kwargs) 119 | func1_kwargs['b'] = 2 120 | with scopes.arg_scope([func1], b=2): 121 | args, kwargs = func1(0) 122 | self.assertTupleEqual(args, func1_args) 123 | self.assertDictEqual(kwargs, func1_kwargs) 124 | 125 | def testSharedArgScope(self): 126 | func1_args = (0,) 127 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 128 | with scopes.arg_scope([func1, func2], a=1, b=None, c=[1]): 129 | args, kwargs = func1(0) 130 | self.assertTupleEqual(args, func1_args) 131 | self.assertDictEqual(kwargs, func1_kwargs) 132 | args, kwargs = func2(0) 133 | self.assertTupleEqual(args, func1_args) 134 | self.assertDictEqual(kwargs, func1_kwargs) 135 | 136 | def testSharedArgScopeTuple(self): 137 | func1_args = (0,) 138 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 139 | with scopes.arg_scope((func1, func2), a=1, b=None, c=[1]): 140 | args, kwargs = func1(0) 141 | self.assertTupleEqual(args, func1_args) 142 | self.assertDictEqual(kwargs, func1_kwargs) 143 | args, kwargs = func2(0) 144 | self.assertTupleEqual(args, func1_args) 145 | self.assertDictEqual(kwargs, func1_kwargs) 146 | 147 | def testPartiallySharedArgScope(self): 148 | func1_args = (0,) 149 | func1_kwargs = {'a': 1, 'b': None, 'c': [1]} 150 | func2_args = (1,) 151 | func2_kwargs = {'a': 1, 'b': None, 'd': [2]} 152 | with scopes.arg_scope([func1, func2], a=1, b=None): 153 | with scopes.arg_scope([func1], c=[1]), scopes.arg_scope([func2], d=[2]): 154 | args, kwargs = func1(0) 155 | self.assertTupleEqual(args, func1_args) 156 | self.assertDictEqual(kwargs, func1_kwargs) 157 | args, kwargs = func2(1) 158 | self.assertTupleEqual(args, func2_args) 159 | self.assertDictEqual(kwargs, func2_kwargs) 160 | 161 | if __name__ == '__main__': 162 | tf.test.main() 163 | -------------------------------------------------------------------------------- /inception/slim/slim.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """TF-Slim grouped API. Please see README.md for details and usage.""" 16 | # pylint: disable=unused-import 17 | 18 | # Collapse tf-slim into a single namespace. 19 | from inception.slim import inception_model as inception 20 | from inception.slim import losses 21 | from inception.slim import ops 22 | from inception.slim import scopes 23 | from inception.slim import variables 24 | from inception.slim.scopes import arg_scope 25 | -------------------------------------------------------------------------------- /inception/slim/variables.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Contains convenience wrappers for creating variables in TF-Slim. 16 | 17 | The variables module is typically used for defining model variables from the 18 | ops routines (see slim.ops). Such variables are used for training, evaluation 19 | and inference of models. 20 | 21 | All the variables created through this module would be added to the 22 | MODEL_VARIABLES collection, if you create a model variable outside slim, it can 23 | be added with slim.variables.add_variable(external_variable, reuse). 24 | 25 | Usage: 26 | weights_initializer = tf.truncated_normal_initializer(stddev=0.01) 27 | l2_regularizer = lambda t: losses.l2_loss(t, weight=0.0005) 28 | weights = variables.variable('weights', 29 | shape=[100, 100], 30 | initializer=weights_initializer, 31 | regularizer=l2_regularizer, 32 | device='/cpu:0') 33 | 34 | biases = variables.variable('biases', 35 | shape=[100], 36 | initializer=tf.zeros_initializer(), 37 | device='/cpu:0') 38 | 39 | # More complex example. 40 | 41 | net = slim.ops.conv2d(input, 32, [3, 3], scope='conv1') 42 | net = slim.ops.conv2d(net, 64, [3, 3], scope='conv2') 43 | with slim.arg_scope([variables.variable], restore=False): 44 | net = slim.ops.conv2d(net, 64, [3, 3], scope='conv3') 45 | 46 | # Get all model variables from all the layers. 47 | model_variables = slim.variables.get_variables() 48 | 49 | # Get all model variables from a specific the layer, i.e 'conv1'. 50 | conv1_variables = slim.variables.get_variables('conv1') 51 | 52 | # Get all weights from all the layers. 53 | weights = slim.variables.get_variables_by_name('weights') 54 | 55 | # Get all bias from all the layers. 56 | biases = slim.variables.get_variables_by_name('biases') 57 | 58 | # Get all variables to restore. 59 | # (i.e. only those created by 'conv1' and 'conv2') 60 | variables_to_restore = slim.variables.get_variables_to_restore() 61 | 62 | ************************************************ 63 | * Initializing model variables from a checkpoint 64 | ************************************************ 65 | 66 | # Create some variables. 67 | v1 = slim.variables.variable(name="v1", ..., restore=False) 68 | v2 = slim.variables.variable(name="v2", ...) # By default restore=True 69 | ... 70 | # The list of variables to restore should only contain 'v2'. 71 | variables_to_restore = slim.variables.get_variables_to_restore() 72 | restorer = tf.train.Saver(variables_to_restore) 73 | with tf.Session() as sess: 74 | # Restore variables from disk. 75 | restorer.restore(sess, "/tmp/model.ckpt") 76 | print("Model restored.") 77 | # Do some work with the model 78 | ... 79 | 80 | """ 81 | from __future__ import absolute_import 82 | from __future__ import division 83 | from __future__ import print_function 84 | 85 | import tensorflow as tf 86 | 87 | from inception.slim import scopes 88 | 89 | # Collection containing all the variables created using slim.variables 90 | MODEL_VARIABLES = '_model_variables_' 91 | 92 | # Collection containing the slim.variables that are created with restore=True. 93 | VARIABLES_TO_RESTORE = '_variables_to_restore_' 94 | 95 | 96 | def add_variable(var, restore=True): 97 | """Adds a variable to the MODEL_VARIABLES collection. 98 | 99 | Optionally it will add the variable to the VARIABLES_TO_RESTORE collection. 100 | Args: 101 | var: a variable. 102 | restore: whether the variable should be added to the 103 | VARIABLES_TO_RESTORE collection. 104 | 105 | """ 106 | collections = [MODEL_VARIABLES] 107 | if restore: 108 | collections.append(VARIABLES_TO_RESTORE) 109 | for collection in collections: 110 | if var not in tf.get_collection(collection): 111 | tf.add_to_collection(collection, var) 112 | 113 | 114 | def get_variables(scope=None, suffix=None): 115 | """Gets the list of variables, filtered by scope and/or suffix. 116 | 117 | Args: 118 | scope: an optional scope for filtering the variables to return. 119 | suffix: an optional suffix for filtering the variables to return. 120 | 121 | Returns: 122 | a copied list of variables with scope and suffix. 123 | """ 124 | candidates = tf.get_collection(MODEL_VARIABLES, scope)[:] 125 | if suffix is not None: 126 | candidates = [var for var in candidates if var.op.name.endswith(suffix)] 127 | return candidates 128 | 129 | 130 | def get_variables_to_restore(): 131 | """Gets the list of variables to restore. 132 | 133 | Returns: 134 | a copied list of variables. 135 | """ 136 | return tf.get_collection(VARIABLES_TO_RESTORE)[:] 137 | 138 | 139 | def get_variables_by_name(given_name, scope=None): 140 | """Gets the list of variables that were given that name. 141 | 142 | Args: 143 | given_name: name given to the variable without scope. 144 | scope: an optional scope for filtering the variables to return. 145 | 146 | Returns: 147 | a copied list of variables with the given name and prefix. 148 | """ 149 | return get_variables(scope=scope, suffix=given_name) 150 | 151 | 152 | def get_unique_variable(name): 153 | """Gets the variable uniquely identified by that name. 154 | 155 | Args: 156 | name: a name that uniquely identifies the variable. 157 | 158 | Returns: 159 | a tensorflow variable. 160 | 161 | Raises: 162 | ValueError: if no variable uniquely identified by the name exists. 163 | """ 164 | candidates = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, name) 165 | if not candidates: 166 | raise ValueError('Couldnt find variable %s' % name) 167 | 168 | for candidate in candidates: 169 | if candidate.op.name == name: 170 | return candidate 171 | raise ValueError('Variable %s does not uniquely identify a variable', name) 172 | 173 | 174 | class VariableDeviceChooser(object): 175 | """Slim device chooser for variables. 176 | 177 | When using a parameter server it will assign them in a round-robin fashion. 178 | When not using a parameter server it allows GPU:0 placement otherwise CPU:0. 179 | """ 180 | 181 | def __init__(self, 182 | num_parameter_servers=0, 183 | ps_device='/job:ps', 184 | placement='CPU:0'): 185 | """Initialize VariableDeviceChooser. 186 | 187 | Args: 188 | num_parameter_servers: number of parameter servers. 189 | ps_device: string representing the parameter server device. 190 | placement: string representing the placement of the variable either CPU:0 191 | or GPU:0. When using parameter servers forced to CPU:0. 192 | """ 193 | self._num_ps = num_parameter_servers 194 | self._ps_device = ps_device 195 | self._placement = placement if num_parameter_servers == 0 else 'CPU:0' 196 | self._next_task_id = 0 197 | 198 | def __call__(self, op): 199 | device_string = '' 200 | if self._num_ps > 0: 201 | task_id = self._next_task_id 202 | self._next_task_id = (self._next_task_id + 1) % self._num_ps 203 | device_string = '%s/task:%d' % (self._ps_device, task_id) 204 | device_string += '/%s' % self._placement 205 | return device_string 206 | 207 | 208 | # TODO(sguada) Remove once get_variable is able to colocate op.devices. 209 | def variable_device(device, name): 210 | """Fix the variable device to colocate its ops.""" 211 | if callable(device): 212 | var_name = tf.get_variable_scope().name + '/' + name 213 | var_def = tf.NodeDef(name=var_name, op='Variable') 214 | device = device(var_def) 215 | if device is None: 216 | device = '' 217 | return device 218 | 219 | 220 | @scopes.add_arg_scope 221 | def global_step(device=''): 222 | """Returns the global step variable. 223 | 224 | Args: 225 | device: Optional device to place the variable. It can be an string or a 226 | function that is called to get the device for the variable. 227 | 228 | Returns: 229 | the tensor representing the global step variable. 230 | """ 231 | global_step_ref = tf.get_collection(tf.GraphKeys.GLOBAL_STEP) 232 | if global_step_ref: 233 | return global_step_ref[0] 234 | else: 235 | collections = [ 236 | VARIABLES_TO_RESTORE, 237 | tf.GraphKeys.GLOBAL_VARIABLES, 238 | tf.GraphKeys.GLOBAL_STEP, 239 | ] 240 | # Get the device for the variable. 241 | with tf.device(variable_device(device, 'global_step')): 242 | return tf.get_variable('global_step', shape=[], dtype=tf.int64, 243 | initializer=tf.zeros_initializer, 244 | trainable=False, collections=collections) 245 | 246 | 247 | @scopes.add_arg_scope 248 | def variable(name, shape=None, dtype=tf.float32, initializer=None, 249 | regularizer=None, trainable=True, collections=None, device='', 250 | restore=True): 251 | """Gets an existing variable with these parameters or creates a new one. 252 | 253 | It also add itself to a group with its name. 254 | 255 | Args: 256 | name: the name of the new or existing variable. 257 | shape: shape of the new or existing variable. 258 | dtype: type of the new or existing variable (defaults to `DT_FLOAT`). 259 | initializer: initializer for the variable if one is created. 260 | regularizer: a (Tensor -> Tensor or None) function; the result of 261 | applying it on a newly created variable will be added to the collection 262 | GraphKeys.REGULARIZATION_LOSSES and can be used for regularization. 263 | trainable: If `True` also add the variable to the graph collection 264 | `GraphKeys.TRAINABLE_VARIABLES` (see tf.Variable). 265 | collections: A list of collection names to which the Variable will be added. 266 | Note that the variable is always also added to the tf.GraphKeys.GLOBAL_VARIABLES 267 | and MODEL_VARIABLES collections. 268 | device: Optional device to place the variable. It can be an string or a 269 | function that is called to get the device for the variable. 270 | restore: whether the variable should be added to the 271 | VARIABLES_TO_RESTORE collection. 272 | 273 | Returns: 274 | The created or existing variable. 275 | """ 276 | collections = list(collections or []) 277 | 278 | # Make sure variables are added to tf.GraphKeys.GLOBAL_VARIABLES and MODEL_VARIABLES 279 | collections += [tf.GraphKeys.GLOBAL_VARIABLES, MODEL_VARIABLES] 280 | # Add to VARIABLES_TO_RESTORE if necessary 281 | if restore: 282 | collections.append(VARIABLES_TO_RESTORE) 283 | # Remove duplicates 284 | collections = set(collections) 285 | # Get the device for the variable. 286 | with tf.device(variable_device(device, name)): 287 | return tf.get_variable(name, shape=shape, dtype=dtype, 288 | initializer=initializer, regularizer=regularizer, 289 | trainable=trainable, collections=collections) 290 | -------------------------------------------------------------------------------- /inception/slim/variables_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """Tests for slim.variables.""" 16 | from __future__ import absolute_import 17 | from __future__ import division 18 | from __future__ import print_function 19 | 20 | import tensorflow as tf 21 | 22 | from inception.slim import scopes 23 | from inception.slim import variables 24 | 25 | 26 | class VariablesTest(tf.test.TestCase): 27 | 28 | def testCreateVariable(self): 29 | with self.test_session(): 30 | with tf.variable_scope('A'): 31 | a = variables.variable('a', [5]) 32 | self.assertEquals(a.op.name, 'A/a') 33 | self.assertListEqual(a.get_shape().as_list(), [5]) 34 | 35 | def testGetVariables(self): 36 | with self.test_session(): 37 | with tf.variable_scope('A'): 38 | a = variables.variable('a', [5]) 39 | with tf.variable_scope('B'): 40 | b = variables.variable('a', [5]) 41 | self.assertEquals([a, b], variables.get_variables()) 42 | self.assertEquals([a], variables.get_variables('A')) 43 | self.assertEquals([b], variables.get_variables('B')) 44 | 45 | def testGetVariablesSuffix(self): 46 | with self.test_session(): 47 | with tf.variable_scope('A'): 48 | a = variables.variable('a', [5]) 49 | with tf.variable_scope('A'): 50 | b = variables.variable('b', [5]) 51 | self.assertEquals([a], variables.get_variables(suffix='a')) 52 | self.assertEquals([b], variables.get_variables(suffix='b')) 53 | 54 | def testGetVariableWithSingleVar(self): 55 | with self.test_session(): 56 | with tf.variable_scope('parent'): 57 | a = variables.variable('child', [5]) 58 | self.assertEquals(a, variables.get_unique_variable('parent/child')) 59 | 60 | def testGetVariableWithDistractors(self): 61 | with self.test_session(): 62 | with tf.variable_scope('parent'): 63 | a = variables.variable('child', [5]) 64 | with tf.variable_scope('child'): 65 | variables.variable('grandchild1', [7]) 66 | variables.variable('grandchild2', [9]) 67 | self.assertEquals(a, variables.get_unique_variable('parent/child')) 68 | 69 | def testGetVariableThrowsExceptionWithNoMatch(self): 70 | var_name = 'cant_find_me' 71 | with self.test_session(): 72 | with self.assertRaises(ValueError): 73 | variables.get_unique_variable(var_name) 74 | 75 | def testGetThrowsExceptionWithChildrenButNoMatch(self): 76 | var_name = 'parent/child' 77 | with self.test_session(): 78 | with tf.variable_scope(var_name): 79 | variables.variable('grandchild1', [7]) 80 | variables.variable('grandchild2', [9]) 81 | with self.assertRaises(ValueError): 82 | variables.get_unique_variable(var_name) 83 | 84 | def testGetVariablesToRestore(self): 85 | with self.test_session(): 86 | with tf.variable_scope('A'): 87 | a = variables.variable('a', [5]) 88 | with tf.variable_scope('B'): 89 | b = variables.variable('a', [5]) 90 | self.assertEquals([a, b], variables.get_variables_to_restore()) 91 | 92 | def testNoneGetVariablesToRestore(self): 93 | with self.test_session(): 94 | with tf.variable_scope('A'): 95 | a = variables.variable('a', [5], restore=False) 96 | with tf.variable_scope('B'): 97 | b = variables.variable('a', [5], restore=False) 98 | self.assertEquals([], variables.get_variables_to_restore()) 99 | self.assertEquals([a, b], variables.get_variables()) 100 | 101 | def testGetMixedVariablesToRestore(self): 102 | with self.test_session(): 103 | with tf.variable_scope('A'): 104 | a = variables.variable('a', [5]) 105 | b = variables.variable('b', [5], restore=False) 106 | with tf.variable_scope('B'): 107 | c = variables.variable('c', [5]) 108 | d = variables.variable('d', [5], restore=False) 109 | self.assertEquals([a, b, c, d], variables.get_variables()) 110 | self.assertEquals([a, c], variables.get_variables_to_restore()) 111 | 112 | def testReuseVariable(self): 113 | with self.test_session(): 114 | with tf.variable_scope('A'): 115 | a = variables.variable('a', []) 116 | with tf.variable_scope('A', reuse=True): 117 | b = variables.variable('a', []) 118 | self.assertEquals(a, b) 119 | self.assertListEqual([a], variables.get_variables()) 120 | 121 | def testVariableWithDevice(self): 122 | with self.test_session(): 123 | with tf.variable_scope('A'): 124 | a = variables.variable('a', [], device='cpu:0') 125 | b = variables.variable('b', [], device='cpu:1') 126 | self.assertDeviceEqual(a.device, 'cpu:0') 127 | self.assertDeviceEqual(b.device, 'cpu:1') 128 | 129 | def testVariableWithDeviceFromScope(self): 130 | with self.test_session(): 131 | with tf.device('/cpu:0'): 132 | a = variables.variable('a', []) 133 | b = variables.variable('b', [], device='cpu:1') 134 | self.assertDeviceEqual(a.device, 'cpu:0') 135 | self.assertDeviceEqual(b.device, 'cpu:1') 136 | 137 | def testVariableWithDeviceFunction(self): 138 | class DevFn(object): 139 | 140 | def __init__(self): 141 | self.counter = -1 142 | 143 | def __call__(self, op): 144 | self.counter += 1 145 | return 'cpu:%d' % self.counter 146 | 147 | with self.test_session(): 148 | with scopes.arg_scope([variables.variable], device=DevFn()): 149 | a = variables.variable('a', []) 150 | b = variables.variable('b', []) 151 | c = variables.variable('c', [], device='cpu:12') 152 | d = variables.variable('d', []) 153 | with tf.device('cpu:99'): 154 | e_init = tf.constant(12) 155 | e = variables.variable('e', initializer=e_init) 156 | self.assertDeviceEqual(a.device, 'cpu:0') 157 | self.assertDeviceEqual(a.initial_value.device, 'cpu:0') 158 | self.assertDeviceEqual(b.device, 'cpu:1') 159 | self.assertDeviceEqual(b.initial_value.device, 'cpu:1') 160 | self.assertDeviceEqual(c.device, 'cpu:12') 161 | self.assertDeviceEqual(c.initial_value.device, 'cpu:12') 162 | self.assertDeviceEqual(d.device, 'cpu:2') 163 | self.assertDeviceEqual(d.initial_value.device, 'cpu:2') 164 | self.assertDeviceEqual(e.device, 'cpu:3') 165 | self.assertDeviceEqual(e.initial_value.device, 'cpu:99') 166 | 167 | def testVariableWithReplicaDeviceSetter(self): 168 | with self.test_session(): 169 | with tf.device(tf.train.replica_device_setter(ps_tasks=2)): 170 | a = variables.variable('a', []) 171 | b = variables.variable('b', []) 172 | c = variables.variable('c', [], device='cpu:12') 173 | d = variables.variable('d', []) 174 | with tf.device('cpu:99'): 175 | e_init = tf.constant(12) 176 | e = variables.variable('e', initializer=e_init) 177 | # The values below highlight how the replica_device_setter puts initial 178 | # values on the worker job, and how it merges explicit devices. 179 | self.assertDeviceEqual(a.device, '/job:ps/task:0/cpu:0') 180 | self.assertDeviceEqual(a.initial_value.device, '/job:worker/cpu:0') 181 | self.assertDeviceEqual(b.device, '/job:ps/task:1/cpu:0') 182 | self.assertDeviceEqual(b.initial_value.device, '/job:worker/cpu:0') 183 | self.assertDeviceEqual(c.device, '/job:ps/task:0/cpu:12') 184 | self.assertDeviceEqual(c.initial_value.device, '/job:worker/cpu:12') 185 | self.assertDeviceEqual(d.device, '/job:ps/task:1/cpu:0') 186 | self.assertDeviceEqual(d.initial_value.device, '/job:worker/cpu:0') 187 | self.assertDeviceEqual(e.device, '/job:ps/task:0/cpu:0') 188 | self.assertDeviceEqual(e.initial_value.device, '/job:worker/cpu:99') 189 | 190 | def testVariableWithVariableDeviceChooser(self): 191 | 192 | with tf.Graph().as_default(): 193 | device_fn = variables.VariableDeviceChooser(num_parameter_servers=2) 194 | with scopes.arg_scope([variables.variable], device=device_fn): 195 | a = variables.variable('a', []) 196 | b = variables.variable('b', []) 197 | c = variables.variable('c', [], device='cpu:12') 198 | d = variables.variable('d', []) 199 | with tf.device('cpu:99'): 200 | e_init = tf.constant(12) 201 | e = variables.variable('e', initializer=e_init) 202 | # The values below highlight how the VariableDeviceChooser puts initial 203 | # values on the same device as the variable job. 204 | self.assertDeviceEqual(a.device, '/job:ps/task:0/cpu:0') 205 | self.assertDeviceEqual(a.initial_value.device, a.device) 206 | self.assertDeviceEqual(b.device, '/job:ps/task:1/cpu:0') 207 | self.assertDeviceEqual(b.initial_value.device, b.device) 208 | self.assertDeviceEqual(c.device, '/cpu:12') 209 | self.assertDeviceEqual(c.initial_value.device, c.device) 210 | self.assertDeviceEqual(d.device, '/job:ps/task:0/cpu:0') 211 | self.assertDeviceEqual(d.initial_value.device, d.device) 212 | self.assertDeviceEqual(e.device, '/job:ps/task:1/cpu:0') 213 | self.assertDeviceEqual(e.initial_value.device, '/cpu:99') 214 | 215 | def testVariableGPUPlacement(self): 216 | 217 | with tf.Graph().as_default(): 218 | device_fn = variables.VariableDeviceChooser(placement='gpu:0') 219 | with scopes.arg_scope([variables.variable], device=device_fn): 220 | a = variables.variable('a', []) 221 | b = variables.variable('b', []) 222 | c = variables.variable('c', [], device='cpu:12') 223 | d = variables.variable('d', []) 224 | with tf.device('cpu:99'): 225 | e_init = tf.constant(12) 226 | e = variables.variable('e', initializer=e_init) 227 | # The values below highlight how the VariableDeviceChooser puts initial 228 | # values on the same device as the variable job. 229 | self.assertDeviceEqual(a.device, '/gpu:0') 230 | self.assertDeviceEqual(a.initial_value.device, a.device) 231 | self.assertDeviceEqual(b.device, '/gpu:0') 232 | self.assertDeviceEqual(b.initial_value.device, b.device) 233 | self.assertDeviceEqual(c.device, '/cpu:12') 234 | self.assertDeviceEqual(c.initial_value.device, c.device) 235 | self.assertDeviceEqual(d.device, '/gpu:0') 236 | self.assertDeviceEqual(d.initial_value.device, d.device) 237 | self.assertDeviceEqual(e.device, '/gpu:0') 238 | self.assertDeviceEqual(e.initial_value.device, '/cpu:99') 239 | 240 | def testVariableCollection(self): 241 | with self.test_session(): 242 | a = variables.variable('a', [], collections='A') 243 | b = variables.variable('b', [], collections='B') 244 | self.assertEquals(a, tf.get_collection('A')[0]) 245 | self.assertEquals(b, tf.get_collection('B')[0]) 246 | 247 | def testVariableCollections(self): 248 | with self.test_session(): 249 | a = variables.variable('a', [], collections=['A', 'C']) 250 | b = variables.variable('b', [], collections=['B', 'C']) 251 | self.assertEquals(a, tf.get_collection('A')[0]) 252 | self.assertEquals(b, tf.get_collection('B')[0]) 253 | 254 | def testVariableCollectionsWithArgScope(self): 255 | with self.test_session(): 256 | with scopes.arg_scope([variables.variable], collections='A'): 257 | a = variables.variable('a', []) 258 | b = variables.variable('b', []) 259 | self.assertListEqual([a, b], tf.get_collection('A')) 260 | 261 | def testVariableCollectionsWithArgScopeNested(self): 262 | with self.test_session(): 263 | with scopes.arg_scope([variables.variable], collections='A'): 264 | a = variables.variable('a', []) 265 | with scopes.arg_scope([variables.variable], collections='B'): 266 | b = variables.variable('b', []) 267 | self.assertEquals(a, tf.get_collection('A')[0]) 268 | self.assertEquals(b, tf.get_collection('B')[0]) 269 | 270 | def testVariableCollectionsWithArgScopeNonNested(self): 271 | with self.test_session(): 272 | with scopes.arg_scope([variables.variable], collections='A'): 273 | a = variables.variable('a', []) 274 | with scopes.arg_scope([variables.variable], collections='B'): 275 | b = variables.variable('b', []) 276 | variables.variable('c', []) 277 | self.assertListEqual([a], tf.get_collection('A')) 278 | self.assertListEqual([b], tf.get_collection('B')) 279 | 280 | def testVariableRestoreWithArgScopeNested(self): 281 | with self.test_session(): 282 | with scopes.arg_scope([variables.variable], restore=True): 283 | a = variables.variable('a', []) 284 | with scopes.arg_scope([variables.variable], 285 | trainable=False, 286 | collections=['A', 'B']): 287 | b = variables.variable('b', []) 288 | c = variables.variable('c', []) 289 | self.assertListEqual([a, b, c], variables.get_variables_to_restore()) 290 | self.assertListEqual([a, c], tf.trainable_variables()) 291 | self.assertListEqual([b], tf.get_collection('A')) 292 | self.assertListEqual([b], tf.get_collection('B')) 293 | 294 | 295 | class GetVariablesByNameTest(tf.test.TestCase): 296 | 297 | def testGetVariableGivenNameScoped(self): 298 | with self.test_session(): 299 | with tf.variable_scope('A'): 300 | a = variables.variable('a', [5]) 301 | b = variables.variable('b', [5]) 302 | self.assertEquals([a], variables.get_variables_by_name('a')) 303 | self.assertEquals([b], variables.get_variables_by_name('b')) 304 | 305 | def testGetVariablesByNameReturnsByValueWithScope(self): 306 | with self.test_session(): 307 | with tf.variable_scope('A'): 308 | a = variables.variable('a', [5]) 309 | matched_variables = variables.get_variables_by_name('a') 310 | 311 | # If variables.get_variables_by_name returns the list by reference, the 312 | # following append should persist, and be returned, in subsequent calls 313 | # to variables.get_variables_by_name('a'). 314 | matched_variables.append(4) 315 | 316 | matched_variables = variables.get_variables_by_name('a') 317 | self.assertEquals([a], matched_variables) 318 | 319 | def testGetVariablesByNameReturnsByValueWithoutScope(self): 320 | with self.test_session(): 321 | a = variables.variable('a', [5]) 322 | matched_variables = variables.get_variables_by_name('a') 323 | 324 | # If variables.get_variables_by_name returns the list by reference, the 325 | # following append should persist, and be returned, in subsequent calls 326 | # to variables.get_variables_by_name('a'). 327 | matched_variables.append(4) 328 | 329 | matched_variables = variables.get_variables_by_name('a') 330 | self.assertEquals([a], matched_variables) 331 | 332 | 333 | class GlobalStepTest(tf.test.TestCase): 334 | 335 | def testStable(self): 336 | with tf.Graph().as_default(): 337 | gs = variables.global_step() 338 | gs2 = variables.global_step() 339 | self.assertTrue(gs is gs2) 340 | 341 | def testDevice(self): 342 | with tf.Graph().as_default(): 343 | with scopes.arg_scope([variables.global_step], device='/gpu:0'): 344 | gs = variables.global_step() 345 | self.assertDeviceEqual(gs.device, '/gpu:0') 346 | 347 | def testDeviceFn(self): 348 | class DevFn(object): 349 | 350 | def __init__(self): 351 | self.counter = -1 352 | 353 | def __call__(self, op): 354 | self.counter += 1 355 | return '/cpu:%d' % self.counter 356 | 357 | with tf.Graph().as_default(): 358 | with scopes.arg_scope([variables.global_step], device=DevFn()): 359 | gs = variables.global_step() 360 | gs2 = variables.global_step() 361 | self.assertDeviceEqual(gs.device, '/cpu:0') 362 | self.assertEquals(gs, gs2) 363 | self.assertDeviceEqual(gs2.device, '/cpu:0') 364 | 365 | def testReplicaDeviceSetter(self): 366 | device_fn = tf.train.replica_device_setter(2) 367 | with tf.Graph().as_default(): 368 | with scopes.arg_scope([variables.global_step], device=device_fn): 369 | gs = variables.global_step() 370 | gs2 = variables.global_step() 371 | self.assertEquals(gs, gs2) 372 | self.assertDeviceEqual(gs.device, '/job:ps/task:0') 373 | self.assertDeviceEqual(gs.initial_value.device, '/job:ps/task:0') 374 | self.assertDeviceEqual(gs2.device, '/job:ps/task:0') 375 | self.assertDeviceEqual(gs2.initial_value.device, '/job:ps/task:0') 376 | 377 | def testVariableWithVariableDeviceChooser(self): 378 | 379 | with tf.Graph().as_default(): 380 | device_fn = variables.VariableDeviceChooser() 381 | with scopes.arg_scope([variables.global_step], device=device_fn): 382 | gs = variables.global_step() 383 | gs2 = variables.global_step() 384 | self.assertEquals(gs, gs2) 385 | self.assertDeviceEqual(gs.device, 'cpu:0') 386 | self.assertDeviceEqual(gs.initial_value.device, gs.device) 387 | self.assertDeviceEqual(gs2.device, 'cpu:0') 388 | self.assertDeviceEqual(gs2.initial_value.device, gs2.device) 389 | 390 | 391 | if __name__ == '__main__': 392 | tf.test.main() 393 | -------------------------------------------------------------------------------- /inception_score.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 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 | # ============================================================================== 15 | """A library to evaluate Inception on a single GPU. 16 | """ 17 | from __future__ import absolute_import 18 | from __future__ import division 19 | from __future__ import print_function 20 | 21 | from PIL import Image 22 | 23 | from inception.slim import slim 24 | import numpy as np 25 | import tensorflow as tf 26 | 27 | 28 | import math 29 | import os.path 30 | import scipy.misc 31 | # import time 32 | # import scipy.io as sio 33 | # from datetime import datetime 34 | import sys 35 | if sys.version_info[0] == 2: 36 | import cPickle as pickle 37 | else: 38 | import pickle 39 | 40 | 41 | FLAGS = tf.app.flags.FLAGS 42 | 43 | tf.app.flags.DEFINE_string('checkpoint_dir', 44 | './inception_finetuned_models/birds_valid299/model.ckpt-5000', 45 | """Path where to read model checkpoints.""") 46 | 47 | tf.app.flags.DEFINE_string('image_folder', 48 | '/Users/han/Documents/CUB_200_2011/CUB_200_2011/images', 49 | """Path where to load the images """) 50 | 51 | tf.app.flags.DEFINE_integer('num_classes', 50, # 20 for flowers 52 | """Number of classes """) 53 | tf.app.flags.DEFINE_integer('splits', 10, 54 | """Number of splits """) 55 | tf.app.flags.DEFINE_integer('batch_size', 64, "batch size") 56 | tf.app.flags.DEFINE_integer('gpu', 1, "The ID of GPU to use") 57 | # Batch normalization. Constant governing the exponential moving average of 58 | # the 'global' mean and variance for all activations. 59 | BATCHNORM_MOVING_AVERAGE_DECAY = 0.9997 60 | 61 | # The decay to use for the moving average. 62 | MOVING_AVERAGE_DECAY = 0.9999 63 | 64 | 65 | fullpath = FLAGS.image_folder 66 | print(fullpath) 67 | 68 | 69 | def preprocess(img): 70 | # print('img', img.shape, img.max(), img.min()) 71 | # img = Image.fromarray(img, 'RGB') 72 | if len(img.shape) == 2: 73 | img = np.resize(img, (img.shape[0], img.shape[1], 3)) 74 | img = scipy.misc.imresize(img, (299, 299, 3), 75 | interp='bilinear') 76 | img = img.astype(np.float32) 77 | # [0, 255] --> [0, 1] --> [-1, 1] 78 | img = img / 127.5 - 1. 79 | # print('img', img.shape, img.max(), img.min()) 80 | return np.expand_dims(img, 0) 81 | 82 | 83 | def get_inception_score(sess, images, pred_op): 84 | splits = FLAGS.splits 85 | # assert(type(images) == list) 86 | assert(type(images[0]) == np.ndarray) 87 | assert(len(images[0].shape) == 3) 88 | assert(np.max(images[0]) > 10) 89 | assert(np.min(images[0]) >= 0.0) 90 | bs = FLAGS.batch_size 91 | preds = [] 92 | num_examples = len(images) 93 | n_batches = int(math.floor(float(num_examples) / float(bs))) 94 | indices = list(np.arange(num_examples)) 95 | np.random.shuffle(indices) 96 | for i in range(n_batches): 97 | inp = [] 98 | # print('i*bs', i*bs) 99 | for j in range(bs): 100 | if (i*bs + j) == num_examples: 101 | break 102 | img = images[indices[i*bs + j]] 103 | # print('*****', img.shape) 104 | img = preprocess(img) 105 | inp.append(img) 106 | # print("%d of %d batches" % (i, n_batches)) 107 | # inp = inps[(i * bs):min((i + 1) * bs, len(inps))] 108 | inp = np.concatenate(inp, 0) 109 | # print('inp', inp.shape) 110 | pred = sess.run(pred_op, {'inputs:0': inp}) 111 | preds.append(pred) 112 | # if i % 100 == 0: 113 | # print('Batch ', i) 114 | # print('inp', inp.shape, inp.max(), inp.min()) 115 | preds = np.concatenate(preds, 0) 116 | scores = [] 117 | for i in range(splits): 118 | istart = i * preds.shape[0] // splits 119 | iend = (i + 1) * preds.shape[0] // splits 120 | part = preds[istart:iend, :] 121 | kl = (part * (np.log(part) - 122 | np.log(np.expand_dims(np.mean(part, 0), 0)))) 123 | kl = np.mean(np.sum(kl, 1)) 124 | scores.append(np.exp(kl)) 125 | print('mean:', "%.2f" % np.mean(scores), 'std:', "%.2f" % np.std(scores)) 126 | return np.mean(scores), np.std(scores) 127 | 128 | 129 | def load_data(fullpath): 130 | print(fullpath) 131 | images = [] 132 | for path, subdirs, files in os.walk(fullpath): 133 | for name in files: 134 | if name.rfind('jpg') != -1 or name.rfind('png') != -1: 135 | filename = os.path.join(path, name) 136 | # print('filename', filename) 137 | # print('path', path, '\nname', name) 138 | # print('filename', filename) 139 | if os.path.isfile(filename): 140 | img = scipy.misc.imread(filename) 141 | images.append(img) 142 | print('images', len(images), images[0].shape) 143 | return images 144 | 145 | 146 | def inference(images, num_classes, for_training=False, restore_logits=True, 147 | scope=None): 148 | """Build Inception v3 model architecture. 149 | 150 | See here for reference: http://arxiv.org/abs/1512.00567 151 | 152 | Args: 153 | images: Images returned from inputs() or distorted_inputs(). 154 | num_classes: number of classes 155 | for_training: If set to `True`, build the inference model for training. 156 | Kernels that operate differently for inference during training 157 | e.g. dropout, are appropriately configured. 158 | restore_logits: whether or not the logits layers should be restored. 159 | Useful for fine-tuning a model with different num_classes. 160 | scope: optional prefix string identifying the ImageNet tower. 161 | 162 | Returns: 163 | Logits. 2-D float Tensor. 164 | Auxiliary Logits. 2-D float Tensor of side-head. Used for training only. 165 | """ 166 | # Parameters for BatchNorm. 167 | batch_norm_params = { 168 | # Decay for the moving averages. 169 | 'decay': BATCHNORM_MOVING_AVERAGE_DECAY, 170 | # epsilon to prevent 0s in variance. 171 | 'epsilon': 0.001, 172 | } 173 | # Set weight_decay for weights in Conv and FC layers. 174 | with slim.arg_scope([slim.ops.conv2d, slim.ops.fc], weight_decay=0.00004): 175 | with slim.arg_scope([slim.ops.conv2d], 176 | stddev=0.1, 177 | activation=tf.nn.relu, 178 | batch_norm_params=batch_norm_params): 179 | logits, endpoints = slim.inception.inception_v3( 180 | images, 181 | dropout_keep_prob=0.8, 182 | num_classes=num_classes, 183 | is_training=for_training, 184 | restore_logits=restore_logits, 185 | scope=scope) 186 | 187 | # Grab the logits associated with the side head. Employed during training. 188 | auxiliary_logits = endpoints['aux_logits'] 189 | 190 | return logits, auxiliary_logits 191 | 192 | 193 | def main(unused_argv=None): 194 | """Evaluate model on Dataset for a number of steps.""" 195 | with tf.Graph().as_default(): 196 | config = tf.ConfigProto(allow_soft_placement=True) 197 | config.gpu_options.allow_growth = True 198 | with tf.Session(config=config) as sess: 199 | with tf.device("/gpu:%d" % FLAGS.gpu): 200 | # Number of classes in the Dataset label set plus 1. 201 | # Label 0 is reserved for an (unused) background class. 202 | num_classes = FLAGS.num_classes + 1 203 | 204 | # Build a Graph that computes the logits predictions from the 205 | # inference model. 206 | inputs = tf.placeholder( 207 | tf.float32, [FLAGS.batch_size, 299, 299, 3], 208 | name='inputs') 209 | # print(inputs) 210 | 211 | logits, _ = inference(inputs, num_classes) 212 | # calculate softmax after remove 0 which reserve for BG 213 | known_logits = \ 214 | tf.slice(logits, [0, 1], 215 | [FLAGS.batch_size, num_classes - 1]) 216 | pred_op = tf.nn.softmax(known_logits) 217 | 218 | # Restore the moving average version of the 219 | # learned variables for eval. 220 | variable_averages = \ 221 | tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY) 222 | variables_to_restore = variable_averages.variables_to_restore() 223 | saver = tf.train.Saver(variables_to_restore) 224 | saver.restore(sess, FLAGS.checkpoint_dir) 225 | print('Restore the model from %s).' % FLAGS.checkpoint_dir) 226 | images = load_data(fullpath) 227 | get_inception_score(sess, images, pred_op) 228 | 229 | 230 | 231 | 232 | if __name__ == '__main__': 233 | tf.app.run() 234 | --------------------------------------------------------------------------------