Creating input tensors with the right dimensions from data
I have 4 features, 2 continous ones taking the form of
[1,2,3,4,1,1,...]
And 2 categoric in the form of
[["A"],["A","B"],["A","B","C"], ["A", "C"]]
My labels take the same form as continous features, from 0 to 8 for multiclassification. My goal is to predict the label class based on the 4 features.
I extract my data from a json file into a Pandas dataframe that looks like this:
f1 f2 f3 f4 label
0 1 3 [R] [None] 1
1 2 2 [U, W] [Flying] 2
2 1 4 [None] [None] 2
3 1 2 [B] [Flying] 0
.. ... ... ... ... ...
From my understanding of the ragged tensor documentation I can directly feed all of these features into my model like so:
import tensorflow as tf
f1 = tf.keras.Input(shape=(1,),dtype=tf.dtypes.int32)
f2 = tf.keras.Input(shape=(1,),dtype=tf.dtypes.int32)
f3 = tf.keras.Input(shape=(None,),dtype=tf.dtypes.string, ragged=True)
f4 = tf.keras.Input(shape=(None,),dtype=tf.dtypes.string, ragged=True)
After that I normalize f1 and f2, and use a StringLookup, Embedding and Flatten layer for f3 and f4. Then I concatenate and feed them into a couple of Dense layers and then into a final dense layer using a softmax.
My model builds sucesfully.
However when I pass my dataframe to my training function like so:
features = ["f1","f2","f3","f4"]
model.fit(x=[training_set[feature] for feature in features],
y=training_set[label],
validation_split=0.1)
I get the following error
ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type list).
Next I tried manually turning my dataframe into NumPy arrays with the right type:
import numpy as np
f1 = np.asarray(training_set["f1"]).astype(np.int32)
f2 = np.asarray(training_set["f2"]).astype(np.int32)
f3 = np.asarray(training_set["f3"]).astype(object)
f4 = np.asarray(training_set["f4"]).astype(object)
l = np.asarray(training_set["label"]).astype(np.int32)
model.fit(x=[f1,f2,f3,f4],
y=l,
validation_split=0.1)
Which produces the same error:
ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type list).
It seems to me like instead of Numpy Arrays I should convert the data to tensors and feed that into the fit method. If I interpret the tf.Data documentation correctly this should be possible?
In trying to do so I got stuck at getting the dimensions to be right, e.g :
f1 = tf.stack([tf.convert_to_tensor(i) for i in training_set["f1"].values],axis=0)
# Is shape (622,) afaik I need shape(1,)?
f3 = tf.ragged.stack(data["colors"])
# Is shape (622, None) afaik needs to be shape (None,)
What am I doing wrong?
Solution 1:
It is quite complicated to flatten ragged tensors with a Keras
model, but what you can do is use a LSTM
layer or GlobalMaxPool1D
or GlobalAveragePooling1D
etc. to process and flatten the tensors. Just make sure the 2D tensor is somehow converted to a 1D tensor. Here is a working example with two GlobalMaxPool1D
layers:
Data preparation:
import tensorflow as tf
import pandas as pd
d = {'f1': [1, 2, 1, 1],
'f2': [3, 2, 4, 2],
'f3':[['R'] , ['U', 'W'], [None], ['B']],
'f4':[[None] , ['Flying'], [None], ['Flying']],
'label': [1, 2, 2, 0]}
df = pd.DataFrame(data=d)
df['f3'] = df['f3'].apply(lambda x: ['[UNK]' if i == None else i for i in x ])
df['f4'] = df['f4'].apply(lambda x: ['[UNK]' if i == None else i for i in x])
data_f1 = df[["f1"]].to_numpy()
data_f2 = df[["f2"]].to_numpy()
labels = df[["label"]].to_numpy()
data_f3 = tf.ragged.constant([df['f3'].to_list()])
data_f4 = tf.ragged.constant([df['f4'].to_list()])
Model:
look_up_layer = tf.keras.layers.StringLookup()
look_up_layer.adapt(tf.ragged.stack([data_f3, data_f4]))
data_f3 = tf.squeeze(data_f3, axis=0)
data_f4 = tf.squeeze(data_f4, axis=0)
f1 = tf.keras.Input(shape=(1,),dtype=tf.dtypes.int32)
f2 = tf.keras.Input(shape=(1,),dtype=tf.dtypes.int32)
f3 = tf.keras.Input(shape=(None,),dtype=tf.dtypes.string, ragged=True)
f4 = tf.keras.Input(shape=(None,),dtype=tf.dtypes.string, ragged=True)
f1_dense = tf.keras.layers.Dense(5, activation='relu')(f1)
f2_dense = tf.keras.layers.Dense(5, activation='relu')(f2)
f3_lookup = look_up_layer(f3)
f4_lookup = look_up_layer(f4)
embedding_layer = tf.keras.layers.Embedding(len(look_up_layer.get_vocabulary()), 10, input_length=1)
embedd_f3 = embedding_layer(f3_lookup)
embedd_f4 = embedding_layer(f4_lookup)
embedd_f3 = tf.keras.layers.GlobalMaxPool1D()(embedd_f3)
embedd_f4 = tf.keras.layers.GlobalMaxPool1D()(embedd_f4)
output = tf.keras.layers.Concatenate(axis=-1)([f1_dense, f2_dense, embedd_f3, embedd_f4])
output = tf.keras.layers.Dense(3, 'softmax')(output)
model = tf.keras.Model([f1, f2, f3, f4], output)
model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy())
model.fit([data_f1, data_f2, data_f3, data_f4], labels, batch_size=1, epochs=2)