Tengo una entrada que es una serie temporal de 5 dimensiones:
a = [[8,3],[2] , [4,5],[1], [9,1],[2]...] #total 100 timestamps. For each element, dims 0,1 are numerical data and dim 2 is a numerical encoding of a category. This is per sample, 3200 samples
La categoría tiene 3 valores posibles (0,1,2)
Quiero construir un NN de modo que la última dimensión (la categoría) pase por una capa de incrustación con tamaño de salida 8 y luego se concatene de nuevo a las dos primeras dimensiones (los datos numéricos).
Entonces, esto será algo como:
input1 = keras.layers.Input(shape=(2,)) #the numerical features input2 = keras.layers.Input(shape=(1,)) #the encoding of the categories. this part will be embedded to 5 dims x2 = Embedding(input_dim=1, output_dim = 8)(input2) #apply it to every timestamp and take only dim 3, so [2],[1], [2] x = concatenate([input1,x2]) #will get 10 dims at each timepoint, still 100 timepoints x = LSTM(units=24)(x) #the input has 10 dims/features at each timepoint, total 100 timepoints per sample x = Dense(1, activation='sigmoid')(x) model = Model(inputs=[input1, input2] , outputs=[x]) #input1 is 1D vec of the width 2 , input2 is 1D vec with the width 1 and it is going through the embedding model.compile( loss='binary_crossentropy', optimizer='adam', metrics=['acc'] )¿Cómo puedo hacerlo? (preferiblemente en keras)? Mi problema es cómo aplicar la incrustación a cada punto de tiempo. Es decir, si tengo 1000 puntos de tiempo con 3 dims cada uno, necesito convertirlos a 1000 puntos de tiempo con 8 dims cada uno (la capa de embebido debería transformar input2 de (1000X1) a (1000X8)
Hay un par de problemas que está teniendo aquí. Primero déjame darte un ejemplo de trabajo y explicarte cómo resolver tus problemas.
import tensorflow as tf import numpy as np from tensorflow.keras import layers from tensorflow.keras.models import Model num_timesteps = 100 max_features_values = [100, 100, 3] num_observations = 2 input_list = [[[np.random.randint(0, v) for _ in range(num_timesteps)] for v in max_features_values] for _ in range(num_observations)] input_arr = np.array(input_list) # shape (2, 3, 100)Para usar una incrustación, necesitamos voc_size como input_dimension, como se indica en la documentación de LSTM .
voc_size = len(np.unique(input_arr[:, 2, :])) + 1 # 4 Ahora necesitamos crear las entradas. Las entradas deben tener un tamaño [None, 2, num_timesteps] y [None, 1, num_timesteps] donde la primera dimensión es la flexible y se completará con el número de observaciones que estamos pasando. Usemos la incrustación justo después de eso usando el voc_size previamente calculado.
inp1 = layers.Input(shape=(2, num_timesteps)) # TensorShape([None, 2, 100]) inp2 = layers.Input(shape=(1, num_timesteps)) # TensorShape([None, 1, 100]) x2 = layers.Embedding(input_dim=voc_size, output_dim=8)(inp2) # TensorShape([None, 1, 100, 8]) x2_reshaped = tf.transpose(tf.squeeze(x2, axis=1), [0, 2, 1]) # TensorShape([None, 8, 100]) Esto no se puede concatenar fácilmente ya que todas las dimensiones deben coincidir, excepto la que se encuentra a lo largo del eje de concatenación. Pero las formas no coinciden desafortunadamente. Por lo tanto, remodelamos x2 . Lo hacemos eliminando la primera dimensión y luego transponiendo.
Ahora podemos concatenar sin ningún problema y todo funciona de forma sencilla:
x = layers.concatenate([inp1, x2_reshaped], axis=1) x = layers.LSTM(32)(x) x = layers.Dense(1, activation='sigmoid')(x) model = Model(inputs=[inp1, inp2], outputs=[x]) inp1_np = input_arr[:, :2, :] inp2_np = input_arr[:, 2:, :] model.predict([inp1_np, inp2_np]) # Output # array([[0.544262 ], # [0.6157502]], dtype=float32)#Esto genera valores entre 0 y 1 tal como se esperaba.
En caso de que no esté buscando Embedding s de la forma en que generalmente se usa en Keras (mapa de enteros positivos a vectores densos). Es posible que esté buscando algún tipo de expansión de base o no proyección, en la que 3 dimensiones se asignan (incrustan) a 8 y concatenan el resultado. Esto se puede hacer usando el truco del kernel u otros métodos, pero también ocurre implícitamente en redes neuronales con aplicaciones no lineales.
Como tal, puede hacer algo como esto, siguiendo un formato similar a pythonic833 porque era bueno (pero con marcas de tiempo en el medio según la documentación deKeras LSTM que solicita [batch, timesteps, feature] ):
import tensorflow as tf import numpy as np from tensorflow.keras import layers from tensorflow.keras.models import Model num_timesteps = 100 num_features = 5 num_observations = 2 input_list = [[[np.random.randint(1, 100) for _ in range(num_features)] for _ in range(num_timesteps)] for _ in range(num_observations)] input_arr = np.array(input_list) # shape (2, 100, 5)Luego puede procesar las entradas:
input1 = layers.Input(shape=(num_timesteps, 2,)) input2 = layers.Input(shape=(num_timesteps, 3)) x2 = layers.Dense(8, activation='relu')(input2) x = layers.concatenate([input1,x2], axis=2) # This produces tensors of shape (None, 100, 10) x = layers.LSTM(units=24)(x) x = layers.Dense(1, activation='sigmoid')(x) model = Model(inputs=[input1, input2] , outputs=[x]) model.compile( loss='binary_crossentropy', optimizer='adam', metrics=['acc'] ) inp1_np = input_arr[:, :, :2] inp2_np = input_arr[:, :, 2:] model.predict([inp1_np, inp2_np])que produce
array([[0.44117224], [0.23611131]], dtype=float32)Otras explicaciones sobre la expansión de la base para consultar: