Je voudrais utiliser la couche 1D-Conv suivie de la couche LSTM pour classer un signal à 400 canaux en 16 temps.
La forme d'entrée est composée de:
X = (n_samples, n_timesteps, n_features)
, où n_samples=476
, n_timesteps=400
, n_features=16
sont le nombre d'échantillons, d'horodatages et de caractéristiques (ou canaux) du signal.
y = (n_samples, n_timesteps, 1)
. Chaque pas de temps est étiqueté par 0 ou 1 (classification binaire).
J'utilise le 1D-Conv pour extraire les informations temporelles, comme indiqué dans la figure ci-dessous. F=32
Et K=8
Sont les filtres et kernel_size. 1D-MaxPooling est utilisé après 1D-Conv. LSTM de 32 unités est utilisé pour la classification du signal. Le modèle doit renvoyer une y_pred = (n_samples, n_timesteps, 1)
.
L'extrait de code est présenté comme suit:
input_layer = Input(shape=(dataset.n_timestep, dataset.n_feature))
conv1 = Conv1D(filters=32,
kernel_size=8,
strides=1,
activation='relu')(input_layer)
pool1 = MaxPooling1D(pool_size=4)(conv1)
lstm1 = LSTM(32)(pool1)
output_layer = Dense(1, activation='sigmoid')(lstm1)
model = Model(inputs=input_layer, outputs=output_layer)
Le résumé du modèle est illustré ci-dessous:
Cependant, j'ai eu l'erreur suivante:
ValueError: Error when checking target: expected dense_15 to have 2 dimensions, but got array with shape (476, 400, 1).
Je suppose que le problème était la forme incorrecte. Veuillez me faire savoir comment y remédier.
Une autre question est le nombre de pas de temps. Puisque le input_shape
Est assigné dans le 1D-Conv, comment pouvons-nous faire savoir à LSTM que le pas de temps doit être 400?
Je voudrais ajouter le graphique du modèle basé sur la suggestion de @today. Dans ce cas, le pas de temps de LSTM sera de 98. Avons-nous besoin d'utiliser TimeDistributed dans ce cas? Je n'ai pas appliqué le TimeDistributed dans le Conv1D.
Existe-t-il de toute façon la convolution entre les canaux, au lieu des pas de temps? Par exemple, un filtre (2, 1) parcourt chaque pas de temps, comme illustré dans la figure ci-dessous.
Merci.
Si vous souhaitez prédire une valeur pour chaque pas de temps, deux solutions légèrement différentes me viennent à l'esprit:
1) Retirez le MaxPooling1D
calque, ajoutez le padding='same'
argument à Conv1D
calque et ajoutez return_sequence=True
argument à LSTM pour que le LSTM renvoie la sortie de chaque pas de temps:
from keras.layers import Input, Dense, LSTM, MaxPooling1D, Conv1D
from keras.models import Model
input_layer = Input(shape=(400, 16))
conv1 = Conv1D(filters=32,
kernel_size=8,
strides=1,
activation='relu',
padding='same')(input_layer)
lstm1 = LSTM(32, return_sequences=True)(conv1)
output_layer = Dense(1, activation='sigmoid')(lstm1)
model = Model(inputs=input_layer, outputs=output_layer)
model.summary()
Le résumé du modèle serait:
Layer (type) Output Shape Param #
=================================================================
input_4 (InputLayer) (None, 400, 16) 0
_________________________________________________________________
conv1d_4 (Conv1D) (None, 400, 32) 4128
_________________________________________________________________
lstm_4 (LSTM) (None, 400, 32) 8320
_________________________________________________________________
dense_4 (Dense) (None, 400, 1) 33
=================================================================
Total params: 12,481
Trainable params: 12,481
Non-trainable params: 0
_________________________________________________________________
2) Modifiez simplement le nombre d'unités dans la couche dense à 400 et remodelez y
à (n_samples, n_timesteps)
:
from keras.layers import Input, Dense, LSTM, MaxPooling1D, Conv1D
from keras.models import Model
input_layer = Input(shape=(400, 16))
conv1 = Conv1D(filters=32,
kernel_size=8,
strides=1,
activation='relu')(input_layer)
pool1 = MaxPooling1D(pool_size=4)(conv1)
lstm1 = LSTM(32)(pool1)
output_layer = Dense(400, activation='sigmoid')(lstm1)
model = Model(inputs=input_layer, outputs=output_layer)
model.summary()
Le résumé du modèle serait:
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_6 (InputLayer) (None, 400, 16) 0
_________________________________________________________________
conv1d_6 (Conv1D) (None, 393, 32) 4128
_________________________________________________________________
max_pooling1d_5 (MaxPooling1 (None, 98, 32) 0
_________________________________________________________________
lstm_6 (LSTM) (None, 32) 8320
_________________________________________________________________
dense_6 (Dense) (None, 400) 13200
=================================================================
Total params: 25,648
Trainable params: 25,648
Non-trainable params: 0
_________________________________________________________________
N'oubliez pas que dans les deux cas, vous devez utiliser 'binary_crossentropy'
(ne pas 'categorical_crossentropy'
) comme fonction de perte. Je m'attends à ce que cette solution ait une précision inférieure à la solution # 1; mais vous devez essayer les deux et essayer de modifier les paramètres car cela dépend entièrement du problème spécifique que vous essayez de résoudre et de la nature des données que vous avez.
Mise à jour:
Vous avez demandé une couche de convolution qui ne couvre qu'un seul pas de temps et k entités adjacentes. Oui, vous pouvez le faire en utilisant une couche Conv2D:
# first add an axis to your data
X = np.expand_dims(X) # now X has a shape of (n_samples, n_timesteps, n_feats, 1)
# adjust input layer shape ...
conv2 = Conv2D(n_filters, (1, k), ...) # covers one timestep and k features
# adjust other layers according to the output of convolution layer...
Bien que je ne sache pas pourquoi vous faites cela, pour utiliser la sortie de la couche de convolution (qui est (?, n_timesteps, n_features, n_filters)
, une solution consiste à utiliser une couche LSTM qui est enveloppée dans une couche TimeDistributed. Ou une autre solution consiste à aplatir les deux derniers axes.
Les formes d'entrée et de sortie sont (476, 400, 16) et (476, 1) - ce qui signifie qu'il ne produit qu'une seule valeur par séquence complète.
Votre LSTM ne retient pas les séquences (return_sequences = False). Mais même si vous effectuez le Conv1D et MaxPooling avant que le LSTM ne comprime l'entrée. Ainsi, LSTM lui-même va obtenir un échantillon de (98,32).
Je suppose que vous voulez une sortie pour chaque étape d'entrée.
En supposant que Conv1D et MaxPooling sont pertinents pour les données d'entrée, vous pouvez essayer une approche seq à seq où vous donnez la sortie du premier N/w à un autre réseau pour récupérer 400 sorties.
Je vous recommande de regarder certains modèles comme les réseaux seq2seq de décodeur d'encodeur comme ci-dessous
https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html