'conv2d run separately per example, to help compute per-example gradients.
Args:
input_: tensor containing a minibatch of images / feature maps.
Shape [batch_size, rows, columns, channels]
w: convolution kernels. Shape
[kernel rows, kernel columns, input channels, output channels]
strides: passed through to regular conv_2d
padding: passed through to regular conv_2d
Returns:
conv: the output of the convolution.
single tensor, same as what regular conv_2d does
w_px: a list of batch_size copies of w. each copy was used
for the corresponding example in the minibatch.
calling tf.gradients on the copy gives the gradient for just
that example.'

def _PxConv2DBuilder(self, input_, w, strides, padding):

    input_shape = [int(e) for e in input_.get_shape()]
    batch_size = input_shape[0]
    input_px = [tf.slice(input_, ([example] + ([0] * 3)), ([1] + input_shape[1:])) for example in xrange(batch_size)]
    for input_x in input_px:
        assert (int(input_x.get_shape()[0]) == 1)
    w_px = [tf.identity(w) for example in xrange(batch_size)]
    conv_px = [tf.nn.conv2d(input_x, w_x, strides=strides, padding=padding) for (input_x, w_x) in zip(input_px, w_px)]
    for conv_x in conv_px:
        num_x = int(conv_x.get_shape()[0])
        assert (num_x == 1), num_x
    assert (len(conv_px) == batch_size)
    conv = tf.concat(axis=0, values=conv_px)
    assert (int(conv.get_shape()[0]) == batch_size)
    return (conv, w_px)
