1 # Copyright 2016 The TensorFlow Authors. All Rights Reserved.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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 definition for inception v3 classification network."""
17 from __future__ import absolute_import
18 from __future__ import division
19 from __future__ import print_function
21 import tensorflow as tf
23 from nets import inception_utils
25 slim = tf.contrib.slim
26 trunc_normal = lambda stddev: tf.truncated_normal_initializer(0.0, stddev)
29 def inception_v3_base(inputs,
30 final_endpoint='Mixed_7c',
34 """Inception model from http://arxiv.org/abs/1512.00567.
36 Constructs an Inception v3 network from inputs to the given final endpoint.
37 This method can construct the network up to the final inception block
40 Note that the names of the layers in the paper do not correspond to the names
41 of the endpoints registered by this function although they build the same
44 Here is a mapping from the old_names to the new names:
46 =======================================
50 pool1 | MaxPool_3a_3x3
53 pool2 | MaxPool_5a_3x3
54 mixed_35x35x256a | Mixed_5b
55 mixed_35x35x288a | Mixed_5c
56 mixed_35x35x288b | Mixed_5d
57 mixed_17x17x768a | Mixed_6a
58 mixed_17x17x768b | Mixed_6b
59 mixed_17x17x768c | Mixed_6c
60 mixed_17x17x768d | Mixed_6d
61 mixed_17x17x768e | Mixed_6e
62 mixed_8x8x1280a | Mixed_7a
63 mixed_8x8x2048a | Mixed_7b
64 mixed_8x8x2048b | Mixed_7c
67 inputs: a tensor of size [batch_size, height, width, channels].
68 final_endpoint: specifies the endpoint to construct the network up to. It
69 can be one of ['Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3',
70 'MaxPool_3a_3x3', 'Conv2d_3b_1x1', 'Conv2d_4a_3x3', 'MaxPool_5a_3x3',
71 'Mixed_5b', 'Mixed_5c', 'Mixed_5d', 'Mixed_6a', 'Mixed_6b', 'Mixed_6c',
72 'Mixed_6d', 'Mixed_6e', 'Mixed_7a', 'Mixed_7b', 'Mixed_7c'].
73 min_depth: Minimum depth value (number of channels) for all convolution ops.
74 Enforced when depth_multiplier < 1, and not an active constraint when
75 depth_multiplier >= 1.
76 depth_multiplier: Float multiplier for the depth (number of channels)
77 for all convolution ops. The value must be greater than zero. Typical
78 usage will be to set this value in (0, 1) to reduce the number of
79 parameters or computation cost of the model.
80 scope: Optional variable_scope.
83 tensor_out: output tensor corresponding to the final_endpoint.
84 end_points: a set of activations for external use, for example summaries or
88 ValueError: if final_endpoint is not set to one of the predefined values,
89 or depth_multiplier <= 0
91 # end_points will collect relevant activations for external use, for example
92 # summaries or losses.
95 if depth_multiplier <= 0:
96 raise ValueError('depth_multiplier is not greater than zero.')
97 depth = lambda d: max(int(d * depth_multiplier), min_depth)
99 with tf.variable_scope(scope, 'InceptionV3', [inputs]):
100 with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
101 stride=1, padding='VALID'):
103 end_point = 'Conv2d_1a_3x3'
104 net = slim.conv2d(inputs, depth(32), [3, 3], stride=2, scope=end_point)
105 end_points[end_point] = net
106 if end_point == final_endpoint: return net, end_points
108 end_point = 'Conv2d_2a_3x3'
109 net = slim.conv2d(net, depth(32), [3, 3], scope=end_point)
110 end_points[end_point] = net
111 if end_point == final_endpoint: return net, end_points
113 end_point = 'Conv2d_2b_3x3'
114 net = slim.conv2d(net, depth(64), [3, 3], padding='SAME', scope=end_point)
115 end_points[end_point] = net
116 if end_point == final_endpoint: return net, end_points
118 end_point = 'MaxPool_3a_3x3'
119 net = slim.max_pool2d(net, [3, 3], stride=2, scope=end_point)
120 end_points[end_point] = net
121 if end_point == final_endpoint: return net, end_points
123 end_point = 'Conv2d_3b_1x1'
124 net = slim.conv2d(net, depth(80), [1, 1], scope=end_point)
125 end_points[end_point] = net
126 if end_point == final_endpoint: return net, end_points
128 end_point = 'Conv2d_4a_3x3'
129 net = slim.conv2d(net, depth(192), [3, 3], scope=end_point)
130 end_points[end_point] = net
131 if end_point == final_endpoint: return net, end_points
133 end_point = 'MaxPool_5a_3x3'
134 net = slim.max_pool2d(net, [3, 3], stride=2, scope=end_point)
135 end_points[end_point] = net
136 if end_point == final_endpoint: return net, end_points
140 with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
141 stride=1, padding='SAME'):
142 # mixed: 35 x 35 x 256.
143 end_point = 'Mixed_5b'
144 with tf.variable_scope(end_point):
145 with tf.variable_scope('Branch_0'):
146 branch_0 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
147 with tf.variable_scope('Branch_1'):
148 branch_1 = slim.conv2d(net, depth(48), [1, 1], scope='Conv2d_0a_1x1')
149 branch_1 = slim.conv2d(branch_1, depth(64), [5, 5],
150 scope='Conv2d_0b_5x5')
151 with tf.variable_scope('Branch_2'):
152 branch_2 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
153 branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
154 scope='Conv2d_0b_3x3')
155 branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
156 scope='Conv2d_0c_3x3')
157 with tf.variable_scope('Branch_3'):
158 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
159 branch_3 = slim.conv2d(branch_3, depth(32), [1, 1],
160 scope='Conv2d_0b_1x1')
161 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
162 end_points[end_point] = net
163 if end_point == final_endpoint: return net, end_points
165 # mixed_1: 35 x 35 x 288.
166 end_point = 'Mixed_5c'
167 with tf.variable_scope(end_point):
168 with tf.variable_scope('Branch_0'):
169 branch_0 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
170 with tf.variable_scope('Branch_1'):
171 branch_1 = slim.conv2d(net, depth(48), [1, 1], scope='Conv2d_0b_1x1')
172 branch_1 = slim.conv2d(branch_1, depth(64), [5, 5],
173 scope='Conv_1_0c_5x5')
174 with tf.variable_scope('Branch_2'):
175 branch_2 = slim.conv2d(net, depth(64), [1, 1],
176 scope='Conv2d_0a_1x1')
177 branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
178 scope='Conv2d_0b_3x3')
179 branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
180 scope='Conv2d_0c_3x3')
181 with tf.variable_scope('Branch_3'):
182 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
183 branch_3 = slim.conv2d(branch_3, depth(64), [1, 1],
184 scope='Conv2d_0b_1x1')
185 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
186 end_points[end_point] = net
187 if end_point == final_endpoint: return net, end_points
189 # mixed_2: 35 x 35 x 288.
190 end_point = 'Mixed_5d'
191 with tf.variable_scope(end_point):
192 with tf.variable_scope('Branch_0'):
193 branch_0 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
194 with tf.variable_scope('Branch_1'):
195 branch_1 = slim.conv2d(net, depth(48), [1, 1], scope='Conv2d_0a_1x1')
196 branch_1 = slim.conv2d(branch_1, depth(64), [5, 5],
197 scope='Conv2d_0b_5x5')
198 with tf.variable_scope('Branch_2'):
199 branch_2 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
200 branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
201 scope='Conv2d_0b_3x3')
202 branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
203 scope='Conv2d_0c_3x3')
204 with tf.variable_scope('Branch_3'):
205 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
206 branch_3 = slim.conv2d(branch_3, depth(64), [1, 1],
207 scope='Conv2d_0b_1x1')
208 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
209 end_points[end_point] = net
210 if end_point == final_endpoint: return net, end_points
212 # mixed_3: 17 x 17 x 768.
213 end_point = 'Mixed_6a'
214 with tf.variable_scope(end_point):
215 with tf.variable_scope('Branch_0'):
216 branch_0 = slim.conv2d(net, depth(384), [3, 3], stride=2,
217 padding='VALID', scope='Conv2d_1a_1x1')
218 with tf.variable_scope('Branch_1'):
219 branch_1 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
220 branch_1 = slim.conv2d(branch_1, depth(96), [3, 3],
221 scope='Conv2d_0b_3x3')
222 branch_1 = slim.conv2d(branch_1, depth(96), [3, 3], stride=2,
223 padding='VALID', scope='Conv2d_1a_1x1')
224 with tf.variable_scope('Branch_2'):
225 branch_2 = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID',
226 scope='MaxPool_1a_3x3')
227 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2])
228 end_points[end_point] = net
229 if end_point == final_endpoint: return net, end_points
231 # mixed4: 17 x 17 x 768.
232 end_point = 'Mixed_6b'
233 with tf.variable_scope(end_point):
234 with tf.variable_scope('Branch_0'):
235 branch_0 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
236 with tf.variable_scope('Branch_1'):
237 branch_1 = slim.conv2d(net, depth(128), [1, 1], scope='Conv2d_0a_1x1')
238 branch_1 = slim.conv2d(branch_1, depth(128), [1, 7],
239 scope='Conv2d_0b_1x7')
240 branch_1 = slim.conv2d(branch_1, depth(192), [7, 1],
241 scope='Conv2d_0c_7x1')
242 with tf.variable_scope('Branch_2'):
243 branch_2 = slim.conv2d(net, depth(128), [1, 1], scope='Conv2d_0a_1x1')
244 branch_2 = slim.conv2d(branch_2, depth(128), [7, 1],
245 scope='Conv2d_0b_7x1')
246 branch_2 = slim.conv2d(branch_2, depth(128), [1, 7],
247 scope='Conv2d_0c_1x7')
248 branch_2 = slim.conv2d(branch_2, depth(128), [7, 1],
249 scope='Conv2d_0d_7x1')
250 branch_2 = slim.conv2d(branch_2, depth(192), [1, 7],
251 scope='Conv2d_0e_1x7')
252 with tf.variable_scope('Branch_3'):
253 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
254 branch_3 = slim.conv2d(branch_3, depth(192), [1, 1],
255 scope='Conv2d_0b_1x1')
256 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
257 end_points[end_point] = net
258 if end_point == final_endpoint: return net, end_points
260 # mixed_5: 17 x 17 x 768.
261 end_point = 'Mixed_6c'
262 with tf.variable_scope(end_point):
263 with tf.variable_scope('Branch_0'):
264 branch_0 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
265 with tf.variable_scope('Branch_1'):
266 branch_1 = slim.conv2d(net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
267 branch_1 = slim.conv2d(branch_1, depth(160), [1, 7],
268 scope='Conv2d_0b_1x7')
269 branch_1 = slim.conv2d(branch_1, depth(192), [7, 1],
270 scope='Conv2d_0c_7x1')
271 with tf.variable_scope('Branch_2'):
272 branch_2 = slim.conv2d(net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
273 branch_2 = slim.conv2d(branch_2, depth(160), [7, 1],
274 scope='Conv2d_0b_7x1')
275 branch_2 = slim.conv2d(branch_2, depth(160), [1, 7],
276 scope='Conv2d_0c_1x7')
277 branch_2 = slim.conv2d(branch_2, depth(160), [7, 1],
278 scope='Conv2d_0d_7x1')
279 branch_2 = slim.conv2d(branch_2, depth(192), [1, 7],
280 scope='Conv2d_0e_1x7')
281 with tf.variable_scope('Branch_3'):
282 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
283 branch_3 = slim.conv2d(branch_3, depth(192), [1, 1],
284 scope='Conv2d_0b_1x1')
285 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
286 end_points[end_point] = net
287 if end_point == final_endpoint: return net, end_points
288 # mixed_6: 17 x 17 x 768.
289 end_point = 'Mixed_6d'
290 with tf.variable_scope(end_point):
291 with tf.variable_scope('Branch_0'):
292 branch_0 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
293 with tf.variable_scope('Branch_1'):
294 branch_1 = slim.conv2d(net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
295 branch_1 = slim.conv2d(branch_1, depth(160), [1, 7],
296 scope='Conv2d_0b_1x7')
297 branch_1 = slim.conv2d(branch_1, depth(192), [7, 1],
298 scope='Conv2d_0c_7x1')
299 with tf.variable_scope('Branch_2'):
300 branch_2 = slim.conv2d(net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
301 branch_2 = slim.conv2d(branch_2, depth(160), [7, 1],
302 scope='Conv2d_0b_7x1')
303 branch_2 = slim.conv2d(branch_2, depth(160), [1, 7],
304 scope='Conv2d_0c_1x7')
305 branch_2 = slim.conv2d(branch_2, depth(160), [7, 1],
306 scope='Conv2d_0d_7x1')
307 branch_2 = slim.conv2d(branch_2, depth(192), [1, 7],
308 scope='Conv2d_0e_1x7')
309 with tf.variable_scope('Branch_3'):
310 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
311 branch_3 = slim.conv2d(branch_3, depth(192), [1, 1],
312 scope='Conv2d_0b_1x1')
313 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
314 end_points[end_point] = net
315 if end_point == final_endpoint: return net, end_points
317 # mixed_7: 17 x 17 x 768.
318 end_point = 'Mixed_6e'
319 with tf.variable_scope(end_point):
320 with tf.variable_scope('Branch_0'):
321 branch_0 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
322 with tf.variable_scope('Branch_1'):
323 branch_1 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
324 branch_1 = slim.conv2d(branch_1, depth(192), [1, 7],
325 scope='Conv2d_0b_1x7')
326 branch_1 = slim.conv2d(branch_1, depth(192), [7, 1],
327 scope='Conv2d_0c_7x1')
328 with tf.variable_scope('Branch_2'):
329 branch_2 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
330 branch_2 = slim.conv2d(branch_2, depth(192), [7, 1],
331 scope='Conv2d_0b_7x1')
332 branch_2 = slim.conv2d(branch_2, depth(192), [1, 7],
333 scope='Conv2d_0c_1x7')
334 branch_2 = slim.conv2d(branch_2, depth(192), [7, 1],
335 scope='Conv2d_0d_7x1')
336 branch_2 = slim.conv2d(branch_2, depth(192), [1, 7],
337 scope='Conv2d_0e_1x7')
338 with tf.variable_scope('Branch_3'):
339 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
340 branch_3 = slim.conv2d(branch_3, depth(192), [1, 1],
341 scope='Conv2d_0b_1x1')
342 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
343 end_points[end_point] = net
344 if end_point == final_endpoint: return net, end_points
346 # mixed_8: 8 x 8 x 1280.
347 end_point = 'Mixed_7a'
348 with tf.variable_scope(end_point):
349 with tf.variable_scope('Branch_0'):
350 branch_0 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
351 branch_0 = slim.conv2d(branch_0, depth(320), [3, 3], stride=2,
352 padding='VALID', scope='Conv2d_1a_3x3')
353 with tf.variable_scope('Branch_1'):
354 branch_1 = slim.conv2d(net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
355 branch_1 = slim.conv2d(branch_1, depth(192), [1, 7],
356 scope='Conv2d_0b_1x7')
357 branch_1 = slim.conv2d(branch_1, depth(192), [7, 1],
358 scope='Conv2d_0c_7x1')
359 branch_1 = slim.conv2d(branch_1, depth(192), [3, 3], stride=2,
360 padding='VALID', scope='Conv2d_1a_3x3')
361 with tf.variable_scope('Branch_2'):
362 branch_2 = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID',
363 scope='MaxPool_1a_3x3')
364 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2])
365 end_points[end_point] = net
366 if end_point == final_endpoint: return net, end_points
367 # mixed_9: 8 x 8 x 2048.
368 end_point = 'Mixed_7b'
369 with tf.variable_scope(end_point):
370 with tf.variable_scope('Branch_0'):
371 branch_0 = slim.conv2d(net, depth(320), [1, 1], scope='Conv2d_0a_1x1')
372 with tf.variable_scope('Branch_1'):
373 branch_1 = slim.conv2d(net, depth(384), [1, 1], scope='Conv2d_0a_1x1')
374 branch_1 = tf.concat(axis=3, values=[
375 slim.conv2d(branch_1, depth(384), [1, 3], scope='Conv2d_0b_1x3'),
376 slim.conv2d(branch_1, depth(384), [3, 1], scope='Conv2d_0b_3x1')])
377 with tf.variable_scope('Branch_2'):
378 branch_2 = slim.conv2d(net, depth(448), [1, 1], scope='Conv2d_0a_1x1')
379 branch_2 = slim.conv2d(
380 branch_2, depth(384), [3, 3], scope='Conv2d_0b_3x3')
381 branch_2 = tf.concat(axis=3, values=[
382 slim.conv2d(branch_2, depth(384), [1, 3], scope='Conv2d_0c_1x3'),
383 slim.conv2d(branch_2, depth(384), [3, 1], scope='Conv2d_0d_3x1')])
384 with tf.variable_scope('Branch_3'):
385 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
386 branch_3 = slim.conv2d(
387 branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
388 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
389 end_points[end_point] = net
390 if end_point == final_endpoint: return net, end_points
392 # mixed_10: 8 x 8 x 2048.
393 end_point = 'Mixed_7c'
394 with tf.variable_scope(end_point):
395 with tf.variable_scope('Branch_0'):
396 branch_0 = slim.conv2d(net, depth(320), [1, 1], scope='Conv2d_0a_1x1')
397 with tf.variable_scope('Branch_1'):
398 branch_1 = slim.conv2d(net, depth(384), [1, 1], scope='Conv2d_0a_1x1')
399 branch_1 = tf.concat(axis=3, values=[
400 slim.conv2d(branch_1, depth(384), [1, 3], scope='Conv2d_0b_1x3'),
401 slim.conv2d(branch_1, depth(384), [3, 1], scope='Conv2d_0c_3x1')])
402 with tf.variable_scope('Branch_2'):
403 branch_2 = slim.conv2d(net, depth(448), [1, 1], scope='Conv2d_0a_1x1')
404 branch_2 = slim.conv2d(
405 branch_2, depth(384), [3, 3], scope='Conv2d_0b_3x3')
406 branch_2 = tf.concat(axis=3, values=[
407 slim.conv2d(branch_2, depth(384), [1, 3], scope='Conv2d_0c_1x3'),
408 slim.conv2d(branch_2, depth(384), [3, 1], scope='Conv2d_0d_3x1')])
409 with tf.variable_scope('Branch_3'):
410 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
411 branch_3 = slim.conv2d(
412 branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
413 net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
414 end_points[end_point] = net
415 if end_point == final_endpoint: return net, end_points
416 raise ValueError('Unknown final endpoint %s' % final_endpoint)
419 def inception_v3(inputs,
422 dropout_keep_prob=0.8,
424 depth_multiplier=1.0,
425 prediction_fn=slim.softmax,
426 spatial_squeeze=True,
428 scope='InceptionV3'):
429 """Inception model from http://arxiv.org/abs/1512.00567.
431 "Rethinking the Inception Architecture for Computer Vision"
433 Christian Szegedy, Vincent Vanhoucke, Sergey Ioffe, Jonathon Shlens,
436 With the default arguments this method constructs the exact model defined in
437 the paper. However, one can experiment with variations of the inception_v3
438 network by changing arguments dropout_keep_prob, min_depth and
441 The default image size used to train this network is 299x299.
444 inputs: a tensor of size [batch_size, height, width, channels].
445 num_classes: number of predicted classes.
446 is_training: whether is training or not.
447 dropout_keep_prob: the percentage of activation values that are retained.
448 min_depth: Minimum depth value (number of channels) for all convolution ops.
449 Enforced when depth_multiplier < 1, and not an active constraint when
450 depth_multiplier >= 1.
451 depth_multiplier: Float multiplier for the depth (number of channels)
452 for all convolution ops. The value must be greater than zero. Typical
453 usage will be to set this value in (0, 1) to reduce the number of
454 parameters or computation cost of the model.
455 prediction_fn: a function to get predictions out of logits.
456 spatial_squeeze: if True, logits is of shape [B, C], if false logits is
457 of shape [B, 1, 1, C], where B is batch_size and C is number of classes.
458 reuse: whether or not the network and its variables should be reused. To be
459 able to reuse 'scope' must be given.
460 scope: Optional variable_scope.
463 logits: the pre-softmax activations, a tensor of size
464 [batch_size, num_classes]
465 end_points: a dictionary from components of the network to the corresponding
469 ValueError: if 'depth_multiplier' is less than or equal to zero.
471 if depth_multiplier <= 0:
472 raise ValueError('depth_multiplier is not greater than zero.')
473 depth = lambda d: max(int(d * depth_multiplier), min_depth)
475 with tf.variable_scope(scope, 'InceptionV3', [inputs, num_classes],
476 reuse=reuse) as scope:
477 with slim.arg_scope([slim.batch_norm, slim.dropout],
478 is_training=is_training):
479 net, end_points = inception_v3_base(
480 inputs, scope=scope, min_depth=min_depth,
481 depth_multiplier=depth_multiplier)
483 # Auxiliary Head logits
484 with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
485 stride=1, padding='SAME'):
486 aux_logits = end_points['Mixed_6e']
487 with tf.variable_scope('AuxLogits'):
488 aux_logits = slim.avg_pool2d(
489 aux_logits, [5, 5], stride=3, padding='VALID',
490 scope='AvgPool_1a_5x5')
491 aux_logits = slim.conv2d(aux_logits, depth(128), [1, 1],
492 scope='Conv2d_1b_1x1')
494 # Shape of feature map before the final layer.
495 kernel_size = _reduced_kernel_size_for_small_input(
497 aux_logits = slim.conv2d(
498 aux_logits, depth(768), kernel_size,
499 weights_initializer=trunc_normal(0.01),
500 padding='VALID', scope='Conv2d_2a_{}x{}'.format(*kernel_size))
501 aux_logits = slim.conv2d(
502 aux_logits, num_classes, [1, 1], activation_fn=None,
503 normalizer_fn=None, weights_initializer=trunc_normal(0.001),
504 scope='Conv2d_2b_1x1')
506 aux_logits = tf.squeeze(aux_logits, [1, 2], name='SpatialSqueeze')
507 end_points['AuxLogits'] = aux_logits
509 # Final pooling and prediction
510 with tf.variable_scope('Logits'):
511 kernel_size = _reduced_kernel_size_for_small_input(net, [8, 8])
512 net = slim.avg_pool2d(net, kernel_size, padding='VALID',
513 scope='AvgPool_1a_{}x{}'.format(*kernel_size))
515 net = slim.dropout(net, keep_prob=dropout_keep_prob, scope='Dropout_1b')
516 end_points['PreLogits'] = net
518 logits = slim.conv2d(net, num_classes, [1, 1], activation_fn=None,
519 normalizer_fn=None, scope='Conv2d_1c_1x1')
521 logits = tf.squeeze(logits, [1, 2], name='SpatialSqueeze')
523 end_points['Logits'] = logits
524 end_points['Predictions'] = prediction_fn(logits, scope='Predictions')
525 return logits, end_points
526 inception_v3.default_image_size = 299
529 def _reduced_kernel_size_for_small_input(input_tensor, kernel_size):
530 """Define kernel size which is automatically reduced for small input.
532 If the shape of the input images is unknown at graph construction time this
533 function assumes that the input images are is large enough.
536 input_tensor: input tensor of size [batch_size, height, width, channels].
537 kernel_size: desired kernel size of length 2: [kernel_height, kernel_width]
540 a tensor with the kernel size.
542 TODO(jrru): Make this function work with unknown shapes. Theoretically, this
543 can be done with the code below. Problems are two-fold: (1) If the shape was
544 known, it will be lost. (2) inception.slim.ops._two_element_tuple cannot
545 handle tensors that define the kernel size.
546 shape = tf.shape(input_tensor)
547 return = tf.pack([tf.minimum(shape[1], kernel_size[0]),
548 tf.minimum(shape[2], kernel_size[1])])
551 shape = input_tensor.get_shape().as_list()
552 if shape[1] is None or shape[2] is None:
553 kernel_size_out = kernel_size
555 kernel_size_out = [min(shape[1], kernel_size[0]),
556 min(shape[2], kernel_size[1])]
557 return kernel_size_out
560 inception_v3_arg_scope = inception_utils.inception_arg_scope