(Je suis désolé pour le contenu de niche.)
Lorsque je portais un certain code GAN (Generative Adversarial Networks) écrit en ** Theano ** + ** Lasagne ** en ** TensorFlow **, je suis tombé sur quelque chose qui s'appelait "lasagne.layers.NINLayer". C'était. Si vous regardez la documentation Lasagne,
Network-in-network layer. Like DenseLayer, but broadcasting across all trailing dimensions beyond the 2nd. This results in a convolution operation with filter size 1 on all trailing dimensions. Any number of trailing dimensions is supported, so NINLayer can be used to implement 1D, 2D, 3D, ... convolutions.
"Une couche comme une couche étroitement couplée qui" diffuse "aux commandes au-delà de la deuxième dimension." À partir du nom de couche réseau dans le réseau, je me suis préparé au début, mais je voudrais le présenter car il était très facile d'écrire dans TensorFlow.
(Environnement de programmation: - Python: 3.5.2 - Theano: 0.9.0 - Lasagne: 0.2.dev1 - TensorFlow: 1.2.0 )
Comme l'opération n'était pas claire dans l'explication du document, je l'ai testé avec de petites données.
import numpy as np
import theano
import theano.tensor as T
import lasagne
from lasagne.layers import InputLayer, NINLayer
# variables
x = T.tensor4('x')
y = T.matrix('y')
# shared variable
w_np= np.array([[1., 2., 3], [2., 2., 2.]], dtype=np.float32)
ws = theano.shared(w_np, name='w')
# layers
l_in = InputLayer((1, 2, 5, 5)) # image size = 3
l1 = NINLayer(l_in, num_units=3, W=ws, b=None,
nonlinearity=None)
l_in.input_var = x
y = lasagne.layers.get_output(l1)
# theano function
mylayer = theano.function(
inputs=[x],
outputs=y,
allow_input_downcast=True
)
x_np = np.ones([1, 2, 5, 5]) * 0.1
y_np = mylayer(x_np)
y_np = np.asarray(y_np)
print('x_np.shape = ', x_np.shape)
print('x_np = \n', x_np)
print(' ')
print('y_np.shape = ', y_np.shape)
print('y_np = \n', y_np)
Ce qui suit est la sortie du résultat du calcul.
x_np.shape = (1, 2, 5, 5)
x_np =
[[[[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]]
[[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]
[ 0.1 0.1 0.1 0.1 0.1]]]]
y_np.shape = (1, 3, 5, 5)
y_np =
[[[[ 0.30000001 0.30000001 0.30000001 0.30000001 0.30000001]
[ 0.30000001 0.30000001 0.30000001 0.30000001 0.30000001]
[ 0.30000001 0.30000001 0.30000001 0.30000001 0.30000001]
[ 0.30000001 0.30000001 0.30000001 0.30000001 0.30000001]
[ 0.30000001 0.30000001 0.30000001 0.30000001 0.30000001]]
[[ 0.40000001 0.40000001 0.40000001 0.40000001 0.40000001]
[ 0.40000001 0.40000001 0.40000001 0.40000001 0.40000001]
[ 0.40000001 0.40000001 0.40000001 0.40000001 0.40000001]
[ 0.40000001 0.40000001 0.40000001 0.40000001 0.40000001]
[ 0.40000001 0.40000001 0.40000001 0.40000001 0.40000001]]
[[ 0.5 0.5 0.5 0.5 0.5 ]
[ 0.5 0.5 0.5 0.5 0.5 ]
[ 0.5 0.5 0.5 0.5 0.5 ]
[ 0.5 0.5 0.5 0.5 0.5 ]
[ 0.5 0.5 0.5 0.5 0.5 ]]]]
Si vous regardez les données d'entrée / sortie ci-dessus, vous pouvez comprendre la fonction de la couche. Saisissez les données de chaque pixel développées en deux dimensions et le poids dans la direction du canal (en profondeur). Dans l'exemple ci-dessus, w = [[1., 2., 3], [2., 2., 2] C'est un calcul à multiplier par.]]. Puisque l'entrée est sous la forme de 2 canaux et que le poids est sous la forme de [2 x 3], la sortie est de 3 canaux. De plus, dans le code ci-dessus, les données d'entrée sont toutes ... 0,1, mais comme il s'agit en fait de données d'image, n'importe quelle valeur peut être entrée.
Notez que dans "Theano", les données d'image sont traitées comme "canal-1er" (en fait, le canal vient en second pour la commodité du traitement par lots), et dans "TensorFlow" c'est "canal-dernier". Ensuite, j'ai essayé de le transplanter.
# NIN (network in network) like function
def lasagne_nin_like(x, w):
'''
args.:
input shape: [None, pixel, pixel, input_channel]
output shape: [None, pixel, pixel, output_channel]
'''
input_ch = tf.shape(x)[-1] # eq. 2
output_ch = tf.shape(w)[-1] # eq. 3
net = tf.reshape(x, [-1, input_ch])
net = tf.matmul(net, w)
y = tf.reshape(net, [-1, 5, 5, output_ch])
return y
y = lasagne_nin_like(x, W)
Tout ce que vous avez à faire est de laisser la valeur d'entrée du nombre de canaux et d'en faire une forme plate tf.reshape ()
, et après la multiplication de la matrice, de la remettre à sa forme d'origine. C'était plus facile que prévu de mettre en communication.
L'ensemble du code TensorFlow est le suivant.
import numpy as np
import tensorflow as tf
# tensorflow placeholders
x = tf.placeholder(tf.float32, [None, 5, 5, 2])
# shared variable
w_np= np.array([[1., 2., 3], [2., 2., 2.]])
W = tf.Variable(w_np, dtype=tf.float32)
# NIN (network in network) like function
def lasagne_nin_like(x, w):
'''
args.:
input shape: [None, pixel, pixel, input_channel]
output shape: [None, pixel, pixel, output_channel]
'''
input_ch = tf.shape(x)[-1] # eq. 2
output_ch = tf.shape(w)[-1] # eq. 3
net = tf.reshape(x, [-1, input_ch])
net = tf.matmul(net, w)
y = tf.reshape(net, [-1, 5, 5, output_ch])
return y
y = lasagne_nin_like(x, W)
# tensorflow session
init = tf.global_variables_initializer()
sess = tf.InteractiveSession()
sess.run(init)
x_np = np.ones([1, 5, 5, 2], dtype=np.float32) * 0.1
y_np = sess.run(y, feed_dict={x: x_np})
print('x_np.shape = ', x_np.shape)
# output should be transposed to compare theano's result.
#Translocation du tenseur 4D pour comparaison des résultats de calcul(ch-last -> ch-1st)je fais
y_np = np.transpose(y_np, (0, 3, 1, 2))
print('y_np.shape = ', y_np.shape)
print('y_np = ', y_np)
La situation est telle qu'elle est, "c'est plus facile à produire que vous ne le pensez". À titre de considération, la raison pour laquelle le portage était facile est que TensorFlow suit la règle du «dernier canal». En revanche, Theano + Lasagne avait l'inconvénient de ne pas pouvoir aplatir les données sur une seule ligne (en fonction du canal), il se peut donc que lasagne.layers.NINLayer ait été préparé.
(Le titre "Porting NIN Layer" était un titre assez exagéré ...)