| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- """Functions for building the face recognition network.
- """
- # MIT License
- #
- # Copyright (c) 2016 David Sandberg
- #
- # Permission is hereby granted, free of charge, to any person obtaining a copy
- # of this software and associated documentation files (the "Software"), to deal
- # in the Software without restriction, including without limitation the rights
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- # copies of the Software, and to permit persons to whom the Software is
- # furnished to do so, subject to the following conditions:
- #
- # The above copyright notice and this permission notice shall be included in all
- # copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- # SOFTWARE.
- # pylint: disable=missing-docstring
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- import tensorflow as tf
- from tensorflow.python.ops import array_ops
- from tensorflow.python.ops import control_flow_ops
- def conv(inpOp, nIn, nOut, kH, kW, dH, dW, padType, name, phase_train=True, use_batch_norm=True, weight_decay=0.0):
- with tf.variable_scope(name):
- l2_regularizer = lambda t: l2_loss(t, weight=weight_decay)
- kernel = tf.get_variable("weights", [kH, kW, nIn, nOut],
- initializer=tf.truncated_normal_initializer(stddev=1e-1),
- regularizer=l2_regularizer, dtype=inpOp.dtype)
- cnv = tf.nn.conv2d(inpOp, kernel, [1, dH, dW, 1], padding=padType)
-
- if use_batch_norm:
- conv_bn = batch_norm(cnv, phase_train)
- else:
- conv_bn = cnv
- biases = tf.get_variable("biases", [nOut], initializer=tf.constant_initializer(), dtype=inpOp.dtype)
- bias = tf.nn.bias_add(conv_bn, biases)
- conv1 = tf.nn.relu(bias)
- return conv1
- def affine(inpOp, nIn, nOut, name, weight_decay=0.0):
- with tf.variable_scope(name):
- l2_regularizer = lambda t: l2_loss(t, weight=weight_decay)
- weights = tf.get_variable("weights", [nIn, nOut],
- initializer=tf.truncated_normal_initializer(stddev=1e-1),
- regularizer=l2_regularizer, dtype=inpOp.dtype)
- biases = tf.get_variable("biases", [nOut], initializer=tf.constant_initializer(), dtype=inpOp.dtype)
- affine1 = tf.nn.relu_layer(inpOp, weights, biases)
- return affine1
- def l2_loss(tensor, weight=1.0, scope=None):
- """Define a L2Loss, useful for regularize, i.e. weight decay.
- Args:
- tensor: tensor to regularize.
- weight: an optional weight to modulate the loss.
- scope: Optional scope for op_scope.
- Returns:
- the L2 loss op.
- """
- with tf.name_scope(scope):
- weight = tf.convert_to_tensor(weight,
- dtype=tensor.dtype.base_dtype,
- name='loss_weight')
- loss = tf.multiply(weight, tf.nn.l2_loss(tensor), name='value')
- return loss
- def lppool(inpOp, pnorm, kH, kW, dH, dW, padding, name):
- with tf.variable_scope(name):
- if pnorm == 2:
- pwr = tf.square(inpOp)
- else:
- pwr = tf.pow(inpOp, pnorm)
-
- subsamp = tf.nn.avg_pool(pwr,
- ksize=[1, kH, kW, 1],
- strides=[1, dH, dW, 1],
- padding=padding)
- subsamp_sum = tf.multiply(subsamp, kH*kW)
-
- if pnorm == 2:
- out = tf.sqrt(subsamp_sum)
- else:
- out = tf.pow(subsamp_sum, 1/pnorm)
-
- return out
- def mpool(inpOp, kH, kW, dH, dW, padding, name):
- with tf.variable_scope(name):
- maxpool = tf.nn.max_pool(inpOp,
- ksize=[1, kH, kW, 1],
- strides=[1, dH, dW, 1],
- padding=padding)
- return maxpool
- def apool(inpOp, kH, kW, dH, dW, padding, name):
- with tf.variable_scope(name):
- avgpool = tf.nn.avg_pool(inpOp,
- ksize=[1, kH, kW, 1],
- strides=[1, dH, dW, 1],
- padding=padding)
- return avgpool
- def batch_norm(x, phase_train):
- """
- Batch normalization on convolutional maps.
- Args:
- x: Tensor, 4D BHWD input maps
- n_out: integer, depth of input maps
- phase_train: boolean tf.Variable, true indicates training phase
- scope: string, variable scope
- affn: whether to affn-transform outputs
- Return:
- normed: batch-normalized maps
- Ref: http://stackoverflow.com/questions/33949786/how-could-i-use-batch-normalization-in-tensorflow/33950177
- """
- name = 'batch_norm'
- with tf.variable_scope(name):
- phase_train = tf.convert_to_tensor(phase_train, dtype=tf.bool)
- n_out = int(x.get_shape()[3])
- beta = tf.Variable(tf.constant(0.0, shape=[n_out], dtype=x.dtype),
- name=name+'/beta', trainable=True, dtype=x.dtype)
- gamma = tf.Variable(tf.constant(1.0, shape=[n_out], dtype=x.dtype),
- name=name+'/gamma', trainable=True, dtype=x.dtype)
-
- batch_mean, batch_var = tf.nn.moments(x, [0,1,2], name='moments')
- ema = tf.train.ExponentialMovingAverage(decay=0.9)
- def mean_var_with_update():
- ema_apply_op = ema.apply([batch_mean, batch_var])
- with tf.control_dependencies([ema_apply_op]):
- return tf.identity(batch_mean), tf.identity(batch_var)
- mean, var = control_flow_ops.cond(phase_train,
- mean_var_with_update,
- lambda: (ema.average(batch_mean), ema.average(batch_var)))
- normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
- return normed
- def inception(inp, inSize, ks, o1s, o2s1, o2s2, o3s1, o3s2, o4s1, o4s2, o4s3, poolType, name,
- phase_train=True, use_batch_norm=True, weight_decay=0.0):
-
- print('name = ', name)
- print('inputSize = ', inSize)
- print('kernelSize = {3,5}')
- print('kernelStride = {%d,%d}' % (ks,ks))
- print('outputSize = {%d,%d}' % (o2s2,o3s2))
- print('reduceSize = {%d,%d,%d,%d}' % (o2s1,o3s1,o4s2,o1s))
- print('pooling = {%s, %d, %d, %d, %d}' % (poolType, o4s1, o4s1, o4s3, o4s3))
- if (o4s2>0):
- o4 = o4s2
- else:
- o4 = inSize
- print('outputSize = ', o1s+o2s2+o3s2+o4)
- print()
-
- net = []
-
- with tf.variable_scope(name):
- with tf.variable_scope('branch1_1x1'):
- if o1s>0:
- 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)
- net.append(conv1)
-
- with tf.variable_scope('branch2_3x3'):
- if o2s1>0:
- 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)
- 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)
- net.append(conv3)
-
- with tf.variable_scope('branch3_5x5'):
- if o3s1>0:
- 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)
- 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)
- net.append(conv5)
-
- with tf.variable_scope('branch4_pool'):
- if poolType=='MAX':
- pool = mpool(inp, o4s1, o4s1, o4s3, o4s3, 'SAME', 'pool')
- elif poolType=='L2':
- pool = lppool(inp, 2, o4s1, o4s1, o4s3, o4s3, 'SAME', 'pool')
- else:
- raise ValueError('Invalid pooling type "%s"' % poolType)
-
- if o4s2>0:
- 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)
- else:
- pool_conv = pool
- net.append(pool_conv)
-
- incept = array_ops.concat(net, 3, name=name)
- return incept
|