J'ai pensé mask_zero=True
affichera des 0 lorsque la valeur d'entrée est 0, donc les couches suivantes pourraient ignorer le calcul ou quelque chose.
Comment mask_zero
travaux?
Exemple:
data_in = np.array([
[1, 2, 0, 0]
])
data_in.shape
>>> (1, 4)
# model
x = Input(shape=(4,))
e = Embedding(5, 5, mask_zero=True)(x)
m = Model(inputs=x, outputs=e)
p = m.predict(data_in)
print(p.shape)
print(p)
La sortie réelle est: (les nombres sont aléatoires)
(1, 4, 5)
[[[ 0.02499047 0.04617121 0.01586803 0.0338897 0.009652 ]
[ 0.04782704 -0.04035913 -0.0341589 0.03020919 -0.01157228]
[ 0.00451764 -0.01433611 0.02606953 0.00328832 0.02650392]
[ 0.00451764 -0.01433611 0.02606953 0.00328832 0.02650392]]]
Cependant, je pensais que la sortie serait:
[[[ 0.02499047 0.04617121 0.01586803 0.0338897 0.009652 ]
[ 0.04782704 -0.04035913 -0.0341589 0.03020919 -0.01157228]
[ 0 0 0 0 0]
[ 0 0 0 0 0]]]
En fait, la définition de mask_zero=True
Pour la couche d'intégration n'entraîne pas le retour d'un vecteur nul. Au contraire, le comportement de la couche d’incorporation ne changerait pas et renverrait le vecteur d’incorporation avec un index zéro. Vous pouvez le confirmer en vérifiant les poids de la couche d'intégration (c'est-à-dire que dans l'exemple que vous avez mentionné, ce serait m.layers[0].get_weights()
). Au lieu de cela, cela affecterait le comportement des couches suivantes telles que les couches RNN.
Si vous inspectez le code source de la couche Embedding, vous verrez une méthode appelée compute_mask
:
def compute_mask(self, inputs, mask=None):
if not self.mask_zero:
return None
output_mask = K.not_equal(inputs, 0)
return output_mask
Ce masque de sortie sera transmis, en tant qu'argument mask
, aux couches suivantes qui prennent en charge le masquage. Ceci a été implémenté dans la méthode __call__
de la couche de base, Layer
:
# Handle mask propagation.
previous_mask = _collect_previous_mask(inputs)
user_kwargs = copy.copy(kwargs)
if not is_all_none(previous_mask):
# The previous layer generated a mask.
if has_arg(self.call, 'mask'):
if 'mask' not in kwargs:
# If mask is explicitly passed to __call__,
# we should override the default mask.
kwargs['mask'] = previous_mask
Et cela fait que les couches suivantes ignorent (c'est-à-dire ne prennent pas en compte dans leurs calculs) ces étapes d'entrée. Voici un exemple minimal:
data_in = np.array([
[1, 0, 2, 0]
])
x = Input(shape=(4,))
e = Embedding(5, 5, mask_zero=True)(x)
rnn = LSTM(3, return_sequences=True)(e)
m = Model(inputs=x, outputs=rnn)
m.predict(data_in)
array([[[-0.00084503, -0.00413611, 0.00049972],
[-0.00084503, -0.00413611, 0.00049972],
[-0.00144554, -0.00115775, -0.00293898],
[-0.00144554, -0.00115775, -0.00293898]]], dtype=float32)
Comme vous pouvez le voir, les sorties de la couche LSTM pour les deuxième et quatrième pas de temps sont identiques à la sortie des premier et troisième pas de temps, respectivement. Cela signifie que ces pas de temps ont été masqués.
Mise à jour: Le masque sera également pris en compte lors du calcul de la perte car les fonctions de perte sont augmentées en interne pour prendre en charge le masquage à l'aide de weighted_masked_objective
:
def weighted_masked_objective(fn):
"""Adds support for masking and sample-weighting to an objective function.
It transforms an objective function `fn(y_true, y_pred)`
into a sample-weighted, cost-masked objective function
`fn(y_true, y_pred, weights, mask)`.
# Arguments
fn: The objective function to wrap,
with signature `fn(y_true, y_pred)`.
# Returns
A function with signature `fn(y_true, y_pred, weights, mask)`.
"""
lors de la compilation du modèle :
weighted_losses = [weighted_masked_objective(fn) for fn in loss_functions]
Vous pouvez le vérifier en utilisant l'exemple suivant:
data_in = np.array([[1, 2, 0, 0]])
data_out = np.arange(12).reshape(1,4,3)
x = Input(shape=(4,))
e = Embedding(5, 5, mask_zero=True)(x)
d = Dense(3)(e)
m = Model(inputs=x, outputs=d)
m.compile(loss='mse', optimizer='adam')
preds = m.predict(data_in)
loss = m.evaluate(data_in, data_out, verbose=0)
print(preds)
print('Computed Loss:', loss)
[[[ 0.009682 0.02505393 -0.00632722]
[ 0.01756451 0.05928303 0.0153951 ]
[-0.00146054 -0.02064196 -0.04356086]
[-0.00146054 -0.02064196 -0.04356086]]]
Computed Loss: 9.041069030761719
# verify that only the first two outputs
# have been considered in the computation of loss
print(np.square(preds[0,0:2] - data_out[0,0:2]).mean())
9.041070036475277
Le processus d'information du modèle qu'une partie des données est en fait un remplissage et doit être ignoré est appelé Masquage .
Il existe trois façons d'introduire input masks
dans les modèles Keras:
keras.layers.Masking
calque.keras.layers.Embedding
calque avec mask_zero=True
.Ci-dessous, le code à introduire Input Masks
en utilisant keras.layers.Embedding
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
raw_inputs = [[83, 91, 1, 645, 1253, 927],[73, 8, 3215, 55, 927],[711, 632, 71]]
padded_inputs = tf.keras.preprocessing.sequence.pad_sequences(raw_inputs,
padding='post')
print(padded_inputs)
embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
masked_output = embedding(padded_inputs)
print(masked_output._keras_mask)
La sortie du code ci-dessus est illustrée ci-dessous:
[[ 83 91 1 645 1253 927]
[ 73 8 3215 55 927 0]
[ 711 632 71 0 0 0]]
tf.Tensor(
[[ True True True True True True]
[ True True True True True False]
[ True True True False False False]], shape=(3, 6), dtype=bool)
Pour plus d'informations, reportez-vous à cette Tensorflow Tutorial .