How can I create a multihot embedding layer in Keras?

Viewed 928

I have sequential data where each element is a vector as follows:

x_i = [ 0.        ,  0.        ,  0.        ,  0.03666667,  0.        ,
        0.        ,  0.95666667,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        ,  0.        ,  0.00666667,  0.        ,
        0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
        0.        ]

The vector represents the distribution of time (over a 5-minute block, for example) a user spent on a set of activities. The task is to predict the distribution of the tasks over the next time step t+1 given the previous N steps (t-N : t). Consequently, my input shape is:

X.shape = (batch_size, timesteps, input_length), and an example would be (32, 10, 41) where we have a batch size of 32, 10 timesteps in the past and the each element has a dimenionsality of 41.

To do this I'm using an LSTM built using Keras. Before passing this input to the LSTM though, I would like to create something similar to an Embedding layer that converts this representation into a dense high-dimensional vector similar to what's done in NLP and embedding one-hot vectors of words into an embedding space using the embedding layer. However, the Embedding layer in Keras only accepts integer inputs (or one-hot representations), and in my case what I would like to achieve is a matrix product between the input vector X (which is composed of several x_i as it represents time-series data) and an Embedding Matrix V. To illustrate:

X.shape = (10, 41) Embedding matrix shape = (41, 100)

The role is to convert every element in X from it's 41 dimenional sparse representation into 100 dimensions via the matrix multiplication, and this should be done for all elements in the batch input.

To do that I've done the following

class EmbeddingMatrix(Layer):

def __init__(self, output_dim, **kwargs):
    self.output_dim = output_dim
    super(EmbeddingMatrix, self).__init__(**kwargs)

def build(self, input_shape):
    # Create a trainable weight variable for this layer.
    self.kernel = self.add_weight(name='kernel', 
                                  shape=(input_shape[2], self.output_dim),
                                  initializer='uniform',
                                  trainable=True)
    super(EmbeddingMatrix, self).build(input_shape)  # Be sure to call this somewhere!


def call(self, x, mask=None):
    return K.dot(x, self.kernel)

def compute_output_shape(self, input_shape):
    return (input_shape[0], input_shape[1], self.output_dim)

And the LSTM network I'm using is as follows:

inputs = Input(shape=(FLAGS.look_back, FLAGS.inputlength))
inputs_embedded = EmbeddingMatrix(N_EMBEDDING)(inputs)

encoded = LSTM(N_HIDDEN, dropout=0.2, recurrent_dropout=0.2)(inputs_embedded)

dense = TimeDistributed(Dense(N_DENSE, activation='sigmoid'))(dropout)

dense_output = TimeDistributed(Dense(FLAGS.inputlength, activation='softmax'))(dense)

embedder = Model(inputs, inputs_embedded)
model = Model(inputs, dense_output)

model.compile(loss='mean_squared_error', optimizer = RMSprop(lr=LEARNING_RATE, clipnorm=5))

However, when running I get the following error:

       ---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-24-5a28b4f3b6b9> in <module>()
      5 inputs_embedded = EmbeddingMatrix(N_EMBEDDING)(inputs)
      6 
----> 7 encoded = LSTM(N_HIDDEN, dropout=0.2, recurrent_dropout=0.2)(inputs_embedded)
      8 
      9 dense = TimeDistributed(Dense(N_DENSE, activation='sigmoid'))(dropout)

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/layers/recurrent.py in __call__(self, inputs, initial_state, **kwargs)
    260         # modify the input spec to include the state.
    261         if initial_state is None:
--> 262             return super(Recurrent, self).__call__(inputs, **kwargs)
    263 
    264         if not isinstance(initial_state, (list, tuple)):

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py in __call__(self, inputs, **kwargs)
    567                                          '`layer.build(batch_input_shape)`')
    568                 if len(input_shapes) == 1:
--> 569                     self.build(input_shapes[0])
    570                 else:
    571                     self.build(input_shapes)

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/layers/recurrent.py in build(self, input_shape)
   1041                                         initializer=bias_initializer,
   1042                                         regularizer=self.bias_regularizer,
-> 1043                                         constraint=self.bias_constraint)
   1044         else:
   1045             self.bias = None

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/legacy/interfaces.py in wrapper(*args, **kwargs)
     85                 warnings.warn('Update your `' + object_name +
     86                               '` call to the Keras 2 API: ' + signature, stacklevel=2)
---> 87             return func(*args, **kwargs)
     88         wrapper._original_function = func
     89         return wrapper

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py in add_weight(self, name, shape, dtype, initializer, regularizer, trainable, constraint)
    389         if dtype is None:
    390             dtype = K.floatx()
--> 391         weight = K.variable(initializer(shape), dtype=dtype, name=name)
    392         if regularizer is not None:
    393             self.add_loss(regularizer(weight))

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/layers/recurrent.py in bias_initializer(shape, *args, **kwargs)
   1033                         self.bias_initializer((self.units,), *args, **kwargs),
   1034                         initializers.Ones()((self.units,), *args, **kwargs),
-> 1035                         self.bias_initializer((self.units * 2,), *args, **kwargs),
   1036                     ])
   1037             else:

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/keras/backend/tensorflow_backend.py in concatenate(tensors, axis)
   1721         return tf.sparse_concat(axis, tensors)
   1722     else:
-> 1723         return tf.concat([to_dense(x) for x in tensors], axis)
   1724 
   1725 

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/tensorflow/python/ops/array_ops.py in concat(concat_dim, values, name)
   1073       ops.convert_to_tensor(concat_dim,
   1074                             name="concat_dim",
-> 1075                             dtype=dtypes.int32).get_shape(
   1076                             ).assert_is_compatible_with(tensor_shape.scalar())
   1077       return identity(values[0], name=scope)

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/tensorflow/python/framework/ops.py in convert_to_tensor(value, dtype, name, as_ref, preferred_dtype)
    667 
    668         if ret is None:
--> 669           ret = conversion_func(value, dtype=dtype, name=name, as_ref=as_ref)
    670 
    671         if ret is NotImplemented:

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/tensorflow/python/framework/constant_op.py in _constant_tensor_conversion_function(v, dtype, name, as_ref)
    174                                          as_ref=False):
    175   _ = as_ref
--> 176   return constant(v, dtype=dtype, name=name)
    177 
    178 

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/tensorflow/python/framework/constant_op.py in constant(value, dtype, shape, name, verify_shape)
    163   tensor_value = attr_value_pb2.AttrValue()
    164   tensor_value.tensor.CopyFrom(
--> 165       tensor_util.make_tensor_proto(value, dtype=dtype, shape=shape, verify_shape=verify_shape))
    166   dtype_value = attr_value_pb2.AttrValue(type=tensor_value.tensor.dtype)
    167   const_tensor = g.create_op(

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/tensorflow/python/framework/tensor_util.py in make_tensor_proto(values, dtype, shape, verify_shape)
    365       nparray = np.empty(shape, dtype=np_dt)
    366     else:
--> 367       _AssertCompatible(values, dtype)
    368       nparray = np.array(values, dtype=np_dt)
    369       # check to them.

/Users/asturkmani/anaconda3/lib/python3.5/site-packages/tensorflow/python/framework/tensor_util.py in _AssertCompatible(values, dtype)
    300     else:
    301       raise TypeError("Expected %s, got %s of type '%s' instead." %
--> 302                       (dtype.name, repr(mismatch), type(mismatch).__name__))
    303 
    304 

TypeError: Expected int32, got list containing Tensors of type '_Message' instead.

What could be causing this and what would be the best way to implement such a weighted embedding matrix?

0 Answers
Related