J'ai du mal à construire un modèle simple qui traite des valeurs d'entrée masquées. Mes données d’entraînement sont constituées de listes de traces GPS de longueur variable, c’est-à-dire des listes où chaque élément contient la latitude et la longitude.
Il y a 70 exemples de formation
Comme ils ont des longueurs variables, je les remplis de zéros, dans le but de dire à Keras d'ignorer ces valeurs nulles.
train_data = keras.preprocessing.sequence.pad_sequences(train_data, maxlen=max_sequence_len, dtype='float32',
padding='pre', truncating='pre', value=0)
Je construis ensuite un modèle très basique comme
model = Sequential()
model.add(Dense(16, activation='relu',input_shape=(max_sequence_len, 2)))
model.add(Flatten())
model.add(Dense(2, activation='sigmoid'))
Après quelques essais et erreurs, j'ai réalisé que j'avais besoin de la couche Flatten
ou que l'ajustement du modèle jetterait l'erreur.
ValueError: Error when checking target: expected dense_87 to have 3 dimensions, but got array with shape (70, 2)
En incluant cette couche Flatten
, cependant, je ne peux pas utiliser une couche Masking
(pour ignorer les zéros remplis) ou Keras lève cette erreur
TypeError: Layer flatten_31 does not support masking, but was passed an input_mask: Tensor("masking_9/Any_1:0", shape=(?, 48278), dtype=bool)
J'ai longuement cherché, en lisant des publications sur GitHub et de nombreuses Q/A ici, mais je n'arrive pas à comprendre.
Le masquage semble déréglé. Mais ne vous inquiétez pas: les 0 ne vont pas aggraver votre modèle; tout au plus moins efficace.
Je recommanderais d'utiliser une approche convolutionnelle au lieu de pure Dense ou peut-être RNN. Je pense que cela fonctionnera très bien pour les données GPS.
S'il vous plaît essayez le code suivant:
from keras.preprocessing.sequence import pad_sequences
from keras import Sequential
from keras.layers import Dense, Flatten, Masking, LSTM, GRU, Conv1D, Dropout, MaxPooling1D
import numpy as np
import random
max_sequence_len = 70
n_samples = 100
num_coordinates = 2 # lat/long
data = [[[random.random() for _ in range(num_coordinates)]
for y in range(min(x, max_sequence_len))]
for x in range(n_samples)]
train_y = np.random.random((n_samples, 2))
train_data = pad_sequences(data, maxlen=max_sequence_len, dtype='float32',
padding='pre', truncating='pre', value=0)
model = Sequential()
model.add(Conv1D(32, (5, ), input_shape=(max_sequence_len, num_coordinates)))
model.add(Dropout(0.5))
model.add(MaxPooling1D())
model.add(Flatten())
model.add(Dense(2, activation='relu'))
model.compile(loss='mean_squared_error', optimizer="adam")
model.fit(train_data, train_y)
Au lieu d'utiliser une couche Flatten
, vous pouvez utiliser une couche Global Pooling .
Celles-ci sont adaptées pour réduire la dimension longueur/temps sans perdre la possibilité d'utiliser des longueurs variables.
Ainsi, au lieu de Flatten()
, vous pouvez essayer un GlobalAveragePooling1D
ou GlobalMaxPooling1D
.
Aucun d'entre eux n'utilise supports_masking
dans leur code, ils doivent donc être utilisés avec précaution.
Le moyen considérera plus d'entrées que le maximum (donc les valeurs qui doivent être masquées).
Le max prendra un seul de la longueur. Avec un peu de chance, si toutes vos valeurs utiles sont supérieures à celles de la position masquée, le masque sera indirectement conservé. Il faudra probablement encore plus de neurones d'entrée que les autres.
Cela dit, oui, essayez les méthodes Conv1D
ou RNN (LSTM
) suggérées.
Vous pouvez également créer votre propre couche de mise en pool (nécessite un modèle d'API fonctionnel dans lequel vous transmettez les entrées du modèle et le tenseur que vous souhaitez mettre en pool).
Ci-dessous, un exemple de travail avec la mise en commun moyenne appliquant un masque basé sur les entrées:
def customPooling(maskVal):
def innerFunc(x):
inputs = x[0]
target = x[1]
#getting the mask by observing the model's inputs
mask = K.equal(inputs, maskVal)
mask = K.all(mask, axis=-1, keepdims=True)
#inverting the mask for getting the valid steps for each sample
mask = 1 - K.cast(mask, K.floatx())
#summing the valid steps for each sample
stepsPerSample = K.sum(mask, axis=1, keepdims=False)
#applying the mask to the target (to make sure you are summing zeros below)
target = target * mask
#calculating the mean of the steps (using our sum of valid steps as averager)
means = K.sum(target, axis=1, keepdims=False) / stepsPerSample
return means
return innerFunc
x = np.ones((2,5,3))
x[0,3:] = 0.
x[1,1:] = 0.
print(x)
inputs = Input((5,3))
out = Lambda(lambda x: x*4)(inputs)
out = Lambda(customPooling(0))([inputs,out])
model = Model(inputs,out)
model.predict(x)