network.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. """Functions for building the face recognition network.
  2. """
  3. # MIT License
  4. #
  5. # Copyright (c) 2016 David Sandberg
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy
  8. # of this software and associated documentation files (the "Software"), to deal
  9. # in the Software without restriction, including without limitation the rights
  10. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. # copies of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be included in all
  15. # copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. # SOFTWARE.
  24. # pylint: disable=missing-docstring
  25. from __future__ import absolute_import
  26. from __future__ import division
  27. from __future__ import print_function
  28. import tensorflow as tf
  29. from tensorflow.python.ops import array_ops
  30. from tensorflow.python.ops import control_flow_ops
  31. def conv(inpOp, nIn, nOut, kH, kW, dH, dW, padType, name, phase_train=True, use_batch_norm=True, weight_decay=0.0):
  32. with tf.variable_scope(name):
  33. l2_regularizer = lambda t: l2_loss(t, weight=weight_decay)
  34. kernel = tf.get_variable("weights", [kH, kW, nIn, nOut],
  35. initializer=tf.truncated_normal_initializer(stddev=1e-1),
  36. regularizer=l2_regularizer, dtype=inpOp.dtype)
  37. cnv = tf.nn.conv2d(inpOp, kernel, [1, dH, dW, 1], padding=padType)
  38. if use_batch_norm:
  39. conv_bn = batch_norm(cnv, phase_train)
  40. else:
  41. conv_bn = cnv
  42. biases = tf.get_variable("biases", [nOut], initializer=tf.constant_initializer(), dtype=inpOp.dtype)
  43. bias = tf.nn.bias_add(conv_bn, biases)
  44. conv1 = tf.nn.relu(bias)
  45. return conv1
  46. def affine(inpOp, nIn, nOut, name, weight_decay=0.0):
  47. with tf.variable_scope(name):
  48. l2_regularizer = lambda t: l2_loss(t, weight=weight_decay)
  49. weights = tf.get_variable("weights", [nIn, nOut],
  50. initializer=tf.truncated_normal_initializer(stddev=1e-1),
  51. regularizer=l2_regularizer, dtype=inpOp.dtype)
  52. biases = tf.get_variable("biases", [nOut], initializer=tf.constant_initializer(), dtype=inpOp.dtype)
  53. affine1 = tf.nn.relu_layer(inpOp, weights, biases)
  54. return affine1
  55. def l2_loss(tensor, weight=1.0, scope=None):
  56. """Define a L2Loss, useful for regularize, i.e. weight decay.
  57. Args:
  58. tensor: tensor to regularize.
  59. weight: an optional weight to modulate the loss.
  60. scope: Optional scope for op_scope.
  61. Returns:
  62. the L2 loss op.
  63. """
  64. with tf.name_scope(scope):
  65. weight = tf.convert_to_tensor(weight,
  66. dtype=tensor.dtype.base_dtype,
  67. name='loss_weight')
  68. loss = tf.multiply(weight, tf.nn.l2_loss(tensor), name='value')
  69. return loss
  70. def lppool(inpOp, pnorm, kH, kW, dH, dW, padding, name):
  71. with tf.variable_scope(name):
  72. if pnorm == 2:
  73. pwr = tf.square(inpOp)
  74. else:
  75. pwr = tf.pow(inpOp, pnorm)
  76. subsamp = tf.nn.avg_pool(pwr,
  77. ksize=[1, kH, kW, 1],
  78. strides=[1, dH, dW, 1],
  79. padding=padding)
  80. subsamp_sum = tf.multiply(subsamp, kH*kW)
  81. if pnorm == 2:
  82. out = tf.sqrt(subsamp_sum)
  83. else:
  84. out = tf.pow(subsamp_sum, 1/pnorm)
  85. return out
  86. def mpool(inpOp, kH, kW, dH, dW, padding, name):
  87. with tf.variable_scope(name):
  88. maxpool = tf.nn.max_pool(inpOp,
  89. ksize=[1, kH, kW, 1],
  90. strides=[1, dH, dW, 1],
  91. padding=padding)
  92. return maxpool
  93. def apool(inpOp, kH, kW, dH, dW, padding, name):
  94. with tf.variable_scope(name):
  95. avgpool = tf.nn.avg_pool(inpOp,
  96. ksize=[1, kH, kW, 1],
  97. strides=[1, dH, dW, 1],
  98. padding=padding)
  99. return avgpool
  100. def batch_norm(x, phase_train):
  101. """
  102. Batch normalization on convolutional maps.
  103. Args:
  104. x: Tensor, 4D BHWD input maps
  105. n_out: integer, depth of input maps
  106. phase_train: boolean tf.Variable, true indicates training phase
  107. scope: string, variable scope
  108. affn: whether to affn-transform outputs
  109. Return:
  110. normed: batch-normalized maps
  111. Ref: http://stackoverflow.com/questions/33949786/how-could-i-use-batch-normalization-in-tensorflow/33950177
  112. """
  113. name = 'batch_norm'
  114. with tf.variable_scope(name):
  115. phase_train = tf.convert_to_tensor(phase_train, dtype=tf.bool)
  116. n_out = int(x.get_shape()[3])
  117. beta = tf.Variable(tf.constant(0.0, shape=[n_out], dtype=x.dtype),
  118. name=name+'/beta', trainable=True, dtype=x.dtype)
  119. gamma = tf.Variable(tf.constant(1.0, shape=[n_out], dtype=x.dtype),
  120. name=name+'/gamma', trainable=True, dtype=x.dtype)
  121. batch_mean, batch_var = tf.nn.moments(x, [0,1,2], name='moments')
  122. ema = tf.train.ExponentialMovingAverage(decay=0.9)
  123. def mean_var_with_update():
  124. ema_apply_op = ema.apply([batch_mean, batch_var])
  125. with tf.control_dependencies([ema_apply_op]):
  126. return tf.identity(batch_mean), tf.identity(batch_var)
  127. mean, var = control_flow_ops.cond(phase_train,
  128. mean_var_with_update,
  129. lambda: (ema.average(batch_mean), ema.average(batch_var)))
  130. normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
  131. return normed
  132. def inception(inp, inSize, ks, o1s, o2s1, o2s2, o3s1, o3s2, o4s1, o4s2, o4s3, poolType, name,
  133. phase_train=True, use_batch_norm=True, weight_decay=0.0):
  134. print('name = ', name)
  135. print('inputSize = ', inSize)
  136. print('kernelSize = {3,5}')
  137. print('kernelStride = {%d,%d}' % (ks,ks))
  138. print('outputSize = {%d,%d}' % (o2s2,o3s2))
  139. print('reduceSize = {%d,%d,%d,%d}' % (o2s1,o3s1,o4s2,o1s))
  140. print('pooling = {%s, %d, %d, %d, %d}' % (poolType, o4s1, o4s1, o4s3, o4s3))
  141. if (o4s2>0):
  142. o4 = o4s2
  143. else:
  144. o4 = inSize
  145. print('outputSize = ', o1s+o2s2+o3s2+o4)
  146. print()
  147. net = []
  148. with tf.variable_scope(name):
  149. with tf.variable_scope('branch1_1x1'):
  150. if o1s>0:
  151. conv1 = conv(inp, inSize, o1s, 1, 1, 1, 1, 'SAME', 'conv1x1', phase_train=phase_train, use_batch_norm=use_batch_norm, weight_decay=weight_decay)
  152. net.append(conv1)
  153. with tf.variable_scope('branch2_3x3'):
  154. if o2s1>0:
  155. conv3a = conv(inp, inSize, o2s1, 1, 1, 1, 1, 'SAME', 'conv1x1', phase_train=phase_train, use_batch_norm=use_batch_norm, weight_decay=weight_decay)
  156. conv3 = conv(conv3a, o2s1, o2s2, 3, 3, ks, ks, 'SAME', 'conv3x3', phase_train=phase_train, use_batch_norm=use_batch_norm, weight_decay=weight_decay)
  157. net.append(conv3)
  158. with tf.variable_scope('branch3_5x5'):
  159. if o3s1>0:
  160. conv5a = conv(inp, inSize, o3s1, 1, 1, 1, 1, 'SAME', 'conv1x1', phase_train=phase_train, use_batch_norm=use_batch_norm, weight_decay=weight_decay)
  161. conv5 = conv(conv5a, o3s1, o3s2, 5, 5, ks, ks, 'SAME', 'conv5x5', phase_train=phase_train, use_batch_norm=use_batch_norm, weight_decay=weight_decay)
  162. net.append(conv5)
  163. with tf.variable_scope('branch4_pool'):
  164. if poolType=='MAX':
  165. pool = mpool(inp, o4s1, o4s1, o4s3, o4s3, 'SAME', 'pool')
  166. elif poolType=='L2':
  167. pool = lppool(inp, 2, o4s1, o4s1, o4s3, o4s3, 'SAME', 'pool')
  168. else:
  169. raise ValueError('Invalid pooling type "%s"' % poolType)
  170. if o4s2>0:
  171. pool_conv = conv(pool, inSize, o4s2, 1, 1, 1, 1, 'SAME', 'conv1x1', phase_train=phase_train, use_batch_norm=use_batch_norm, weight_decay=weight_decay)
  172. else:
  173. pool_conv = pool
  174. net.append(pool_conv)
  175. incept = array_ops.concat(net, 3, name=name)
  176. return incept