web-dev-qa-db-fra.com

TimeDistributed (Dense) vs Dense in Keras - Même nombre de paramètres

Je construis un modèle qui convertit une chaîne en une autre chaîne en utilisant des couches récurrentes (GRU). J'ai essayé à la fois les couches Dense et TimeDistributed (Dense) comme dernière couche, mais je ne comprends pas la différence entre les deux lors de l'utilisation de return_sequences = True, notamment parce qu'elles semblent avoir le même nombre de paramètres. .

Mon modèle simplifié est le suivant:

InputSize = 15
MaxLen = 64
HiddenSize = 16

inputs = keras.layers.Input(shape=(MaxLen, InputSize))
x = keras.layers.recurrent.GRU(HiddenSize, return_sequences=True)(inputs)
x = keras.layers.TimeDistributed(keras.layers.Dense(InputSize))(x)
predictions = keras.layers.Activation('softmax')(x)

Le résumé du réseau est:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 64, 15)            0         
_________________________________________________________________
gru_1 (GRU)                  (None, 64, 16)            1536      
_________________________________________________________________
time_distributed_1 (TimeDist (None, 64, 15)            255       
_________________________________________________________________
activation_1 (Activation)    (None, 64, 15)            0         
=================================================================

Cela a du sens pour moi car ma compréhension de TimeDistributed est qu’elle applique le même calque à tous les moments, de sorte que le calque dense comporte 16 * 15 + 15 = 255 paramètres (poids + biais).

Cependant, si je passe à un simple calque dense:

inputs = keras.layers.Input(shape=(MaxLen, InputSize))
x = keras.layers.recurrent.GRU(HiddenSize, return_sequences=True)(inputs)
x = keras.layers.Dense(InputSize)(x)
predictions = keras.layers.Activation('softmax')(x)

Je n'ai toujours que 255 paramètres:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 64, 15)            0         
_________________________________________________________________
gru_1 (GRU)                  (None, 64, 16)            1536      
_________________________________________________________________
dense_1 (Dense)              (None, 64, 15)            255       
_________________________________________________________________
activation_1 (Activation)    (None, 64, 15)            0         
=================================================================

Je me demande si c'est parce que Dense () n'utilisera que la dernière dimension de la forme et traitera efficacement tout le reste comme une dimension de type lot. Mais alors je ne suis plus sûr de la différence entre Dense et TimeDistributed (Dense).

Mise à jour En regardant https://github.com/fchollet/keras/blob/master/keras/layers/core.py il semble que Dense utilise la dernière dimension uniquement pour se redimensionner elle-même:

def build(self, input_shape):
    assert len(input_shape) >= 2
    input_dim = input_shape[-1]

    self.kernel = self.add_weight(shape=(input_dim, self.units),

Il utilise également keras.dot pour appliquer les poids:

def call(self, inputs):
    output = K.dot(inputs, self.kernel)

Les docs de keras.dot impliquent que cela fonctionne très bien sur les tenseurs n dimensions. Je me demande si son comportement exact signifie que Dense () sera appelé à chaque pas de temps. Si tel est le cas, il reste à savoir ce que TimeDistributed () accomplit dans ce cas.

14
thon

TimeDistributedDense applique une même densité à chaque pas de temps pendant le déroulement de la cellule GRU/LSTM. La fonction d'erreur sera donc entre la séquence d'étiquettes prédite et la séquence d'étiquettes réelle. (Ce qui est normalement l'obligation pour la séquence de séquencer les problèmes d'étiquetage).

Cependant, avec return_sequences = False, le calque dense n’est appliqué qu’une fois à la dernière cellule. C'est normalement le cas lorsque les RNN sont utilisés pour des problèmes de classification. Si return_sequences = True, la couche dense est appliquée à chaque pas de temps, tout comme TimeDistributedDense.

Donc, selon vos modèles, les deux sont identiques, mais si vous changez votre deuxième modèle en "return_sequences = False", la densité sera appliquée uniquement à la dernière cellule. Essayez de le changer et le modèle émettra une erreur car alors le Y sera de taille [Batch_size, InputSize], il ne s’agit plus d’une séquence à séquencer, mais d’une séquence complète à étiqueter. 

from keras.models import Sequential
from keras.layers import Dense, Activation, TimeDistributed
from keras.layers.recurrent import GRU
import numpy as np

InputSize = 15
MaxLen = 64
HiddenSize = 16

OutputSize = 8
n_samples = 1000

model1 = Sequential()
model1.add(GRU(HiddenSize, return_sequences=True, input_shape=(MaxLen, InputSize)))
model1.add(TimeDistributed(Dense(OutputSize)))
model1.add(Activation('softmax'))
model1.compile(loss='categorical_crossentropy', optimizer='rmsprop')


model2 = Sequential()
model2.add(GRU(HiddenSize, return_sequences=True, input_shape=(MaxLen, InputSize)))
model2.add(Dense(OutputSize))
model2.add(Activation('softmax'))
model2.compile(loss='categorical_crossentropy', optimizer='rmsprop')

model3 = Sequential()
model3.add(GRU(HiddenSize, return_sequences=False, input_shape=(MaxLen, InputSize)))
model3.add(Dense(OutputSize))
model3.add(Activation('softmax'))
model3.compile(loss='categorical_crossentropy', optimizer='rmsprop')

X = np.random.random([n_samples,MaxLen,InputSize])
Y1 = np.random.random([n_samples,MaxLen,OutputSize])
Y2 = np.random.random([n_samples, OutputSize])

model1.fit(X, Y1, batch_size=128, nb_Epoch=1)
model2.fit(X, Y1, batch_size=128, nb_Epoch=1)
model3.fit(X, Y2, batch_size=128, nb_Epoch=1)

print(model1.summary())
print(model2.summary())
print(model3.summary())

Dans l'exemple ci-dessus, l'architecture de modèle1 et modèle2 sont des exemples (modèles séquence à séquence) et modèle3 est une séquence complète à modèle d'étiquette.

8
mujjiga

Voici un morceau de code qui vérifie que TimeDistirbuted(Dense(X)) est identique à Dense(X):

import numpy as np 
from keras.layers import Dense, TimeDistributed
import tensorflow as tf

X = np.array([ [[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9],
                [10, 11, 12]
               ],
               [[3, 1, 7],
                [8, 2, 5],
                [11, 10, 4],
                [9, 6, 12]
               ]
              ]).astype(np.float32)
print(X.shape)

(2, 4, 3)

dense_weights = np.array([[0.1, 0.2, 0.3, 0.4, 0.5],
                          [0.2, 0.7, 0.9, 0.1, 0.2],
                          [0.1, 0.8, 0.6, 0.2, 0.4]])
bias = np.array([0.1, 0.3, 0.7, 0.8, 0.4])
print(dense_weights.shape)

(3, 5)

dense = Dense(input_dim=3, units=5, weights=[dense_weights, bias])
input_tensor = tf.Variable(X, name='inputX')
output_tensor1 = dense(input_tensor)
output_tensor2 = TimeDistributed(dense)(input_tensor)
print(output_tensor1.shape)
print(output_tensor2.shape)

(2, 4, 5)

(2,?, 5)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    output1 = sess.run(output_tensor1)
    output2 = sess.run(output_tensor2)

print(output1 - output2)

Et la différence est:

[[[0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]]]
0
user263387