1

https://github.com/davidsandberg/facenet/blob/master/src/align/detect_face.py

Please refer to this python code above.

I found the prototype of class Network function conv can NOT match with its calling part as

@layer
def conv(self,
         inp,
         k_h,
         k_w,
         c_o,
         s_h,
         s_w,
         name,
         relu=True,
         padding='SAME',
         group=1,
         biased=True):

& calling conv by

class PNet(Network):
def setup(self):
    (self.feed('data') #pylint: disable=no-value-for-parameter, no-member
         .conv(3, 3, 10, 1, 1, padding='VALID', relu=False, name='conv1')
         .prelu(name='PReLU1')
         .max_pool(2, 2, 2, 2, name='pool1')
         .conv(3, 3, 16, 1, 1, padding='VALID', relu=False, name='conv2')
         .prelu(name='PReLU2')
         .conv(3, 3, 32, 1, 1, padding='VALID', relu=False, name='conv3')
         .prelu(name='PReLU3')
         .conv(1, 1, 2, 1, 1, relu=False, name='conv4-1')
         .softmax(3,name='prob1'))

    (self.feed('PReLU3') #pylint: disable=no-value-for-parameter
         .conv(1, 1, 4, 1, 1, relu=False, name='conv4-2'))

Notice that

  1. self
  2. inp --> where it comes from?
  3. ....

I know that self can be ignored; inp, k_h, k_w, c_o, s_h, s_w, can match with position by, ex: 3, 3, 10, 1, 1 and the other parameters are assigned by name.

However, I cannot figure out the way inp comes from?

It contradicts with my familiar programming language C & C++ a a lot..

Does any one can help on explaining it?

Thanks in advance.

Sreeragh A R
  • 2,871
  • 3
  • 27
  • 54
chang jc
  • 489
  • 8
  • 16

2 Answers2

1

Pnet is a network which performs many convolutions. You can pass an image to the input layer and it performs many convolutions on it. Name of input layer is 'data'. Input layer is a tensor which accepts an image.

data = tf.placeholder(tf.float32, (None,None,None,3), 'input')
pnet = PNet({'data':data})

Consider

out = pnet(img_y)

img_y is placed on 'data' layer of Pnet,

which is fed to a convolution layer.

.conv(3, 3, 10, 1, 1, padding='VALID', relu=False, name='conv1')

k_h (kernel height) = 3

k_w (kernel width) = 3

c_o (number of filters) = 10

s_h (stride height) = 1

s_w (stride width) = 1

inp is output of previous layer i.e data layer which is our image.

prelu and max pool are applied, then the output is fed as input of next convolution layer.

.conv(3, 3, 16, 1, 1, padding='VALID', relu=False, name='conv2')

and so on.

To understand convolutional neural networks better, refer CNN

Sreeragh A R
  • 2,871
  • 3
  • 27
  • 54
1

You are indeed right to note that although the function signature takes as its first argument an input layer inp, it is not passed when the function is called.

This trick is achieved with the function decorator @layer that is placed right before the function definition. Here is the definition of the layer decorator:

def layer(op):
    """Decorator for composable network layers."""

    def layer_decorated(self, *args, **kwargs):
        # Automatically set a name if not provided.
        name = kwargs.setdefault('name', self.get_unique_name(op.__name__))
        # Figure out the layer inputs.
        if len(self.terminals) == 0:
            raise RuntimeError('No input variables found for layer %s.' % name)
        elif len(self.terminals) == 1:
            layer_input = self.terminals[0]
        else:
            layer_input = list(self.terminals)
        # Perform the operation and get the output.
        # [!] Here it passes the `inp` parameter, and all the other ones
        layer_output = op(self, layer_input, *args, **kwargs)
        # Add to layer LUT.
        self.layers[name] = layer_output
        # This output is now the input for the next layer.
        self.feed(layer_output)
        # Return self for chained calls.
        return self

return layer_decorated

It takes as input a function/method via the op parameter and it returns another one, layer_decorated which will replace the original definition of op. So PNet.conv = layer(Pnet.conv). If you look at the definition of layer_decorated, you see that it essentially sets up the first argument of the op function, namely layer_input (line with [!]). It also does some book-keeping to know which layer to use as input based on its name.

To simplify things, this allows the programmer to use chained method calls without repeating themselves. It transforms this:

 x = self.feed('data') #pylint: disable=no-value-for-parameter, no-member
 x = self.conv(x, 3, 3, 10, 1, 1, padding='VALID', relu=False, name='conv1')
 x = self.prelu(x, name='PReLU1')
 x = self.max_pool(x, 2, 2, 2, 2, name='pool1')

into this:

x = (self.feed('data') #pylint: disable=no-value-for-parameter, no-member
     .conv(3, 3, 10, 1, 1, padding='VALID', relu=False, name='conv1')
     .prelu(name='PReLU1')
     .max_pool(2, 2, 2, 2, name='pool1')
 )
Ciprian Tomoiagă
  • 3,773
  • 4
  • 41
  • 65
  • @changjc On stackOverflow questions are considered solved when they have an accepted answer. This shows other people all is well with the question and allows them to move to unaswered ones. So consider marking this as an accepted answer if it solved your problem (tick the green mark). Also consider doing this for your previous questions – Ciprian Tomoiagă Jun 28 '18 at 07:00