I have a dataset of dictionary of tensors, and the following model defined using the subclassing API:
class Model(tf.keras.Model):
def __init__(self):
super().__init__()
self._movie_full_dense = tf.keras.layers.Dense(
units=40, activation=tf.keras.layers.Activation("relu"))
self._user_full_dense = tf.keras.layers.Dense(
units=40, activation=tf.keras.layers.Activation("relu"))
self._full_dense = tf.keras.layers.Dense(
units=1, activation=tf.keras.layers.Activation("sigmoid"))
def call(self, features):
movie_embedding = self._movie_full_dense(features['movie'])
user_embedding = self._user_full_dense(features['user'])
combined = tf.concat([movie_embedding, user_embedding], 1)
output = self._full_dense(combined)
return output
I want to implement it using the functional API. but I do not know how to define the inputs? Namely, what is the functional equivalent for the following?
self._movie_full_dense(features['movie'])
import tensorflow as tf
print(tf.version.VERSION)
toy_data = {'movie': [[0], [1], [0], [1]], 'user': [[10], [12], [12], [10]]}
dataset = tf.data.Dataset.from_tensor_slices(toy_data).batch(2)
for x in dataset:
print(x)
def make_model():
inp_movie = tf.keras.Input(shape=(1,))
inp_user = tf.keras.Input(shape=(1,))
movie_embedding = tf.keras.layers.Dense(
units=40, activation=tf.keras.layers.Activation("relu"))(inp_movie)
user_embedding = tf.keras.layers.Dense(
units=40, activation=tf.keras.layers.Activation("relu"))(inp_user)
combined = tf.concat([movie_embedding, user_embedding], 1)
output = tf.keras.layers.Dense(
units=1, activation=tf.keras.layers.Activation("sigmoid"))(combined)
model = tf.keras.Model(inputs=[inp_movie, inp_user], outputs=output)
return model
model = make_model()
for x in dataset:
print(model(x))
This works. Beware that the iterable you pass to the inputs
argument of the tf.keras.Model
call has to be sorted in the same order as the dictionary you will use, which is sorted by its keys, movie
then user
. So using inputs={'a': inp_movie, 'b': inp_user}
or inputs={'movie': inp_movie, 'user': inp_user}
also works, while inputs=[inp_user, inp_movie]
won't.
You can use this code to test this kind of interaction:
def make_test_model():
inp_movie = tf.keras.Input(shape=(1,))
inp_user = tf.keras.Input(shape=(1,))
model = tf.keras.Model(inputs={'a': inp_movie, 'b': inp_user}, outputs=inp_movie)
return model
def make_test_model_2():
inp_movie = tf.keras.Input(shape=(1,))
inp_user = tf.keras.Input(shape=(1,))
model = tf.keras.Model(inputs=[inp_user, inp_movie], outputs=inp_movie)
return model
model_test = make_test_model()
model_test_2 = make_test_model_2()
for x in dataset:
print(model_test(x))
for x in dataset:
print(model_test_2(x))
You can also name the Input
layers using the keys of your dictonary, and give as the inputs
argument a list of Input
layers sorted by the layers names. This allows you to add or remove inputs in your model without having to worry about rewriting your inputs
argument each time. So this is what I would do:
def make_model_2():
input_list = []
inp_movie = tf.keras.Input(shape=(1,), name='movie')
input_list.append(inp_movie)
inp_user = tf.keras.Input(shape=(1,), name='user')
input_list.append(inp_user)
movie_embedding = tf.keras.layers.Dense(
units=40, activation=tf.keras.layers.Activation("relu"))(inp_movie)
user_embedding = tf.keras.layers.Dense(
units=40, activation=tf.keras.layers.Activation("relu"))(inp_user)
combined = tf.concat([movie_embedding, user_embedding], 1)
output = tf.keras.layers.Dense(
units=1, activation=tf.keras.layers.Activation("sigmoid"))(combined)
input_list.sort(key=lambda inp: inp._keras_history.layer.name)
model = tf.keras.Model(inputs=input_list, outputs=output)
return model
Here is a way to test that it works:
def make_test_model_3(boolean):
input_list = []
inp_movie = tf.keras.Input(shape=(1,), name='movie')
inp_user = tf.keras.Input(shape=(1,), name='user')
if boolean:
input_list.append(inp_movie)
input_list.append(inp_user)
else:
input_list.append(inp_user)
input_list.append(inp_movie)
input_list.sort(key=lambda inp: inp._keras_history.layer.name)
model = tf.keras.Model(inputs=input_list, outputs=inp_movie)
return model
model_test_3_0= make_test_model_3(True)
model_test_3_1= make_test_model_3(False)
for x in dataset:
print(model_test_3_0(x))
for x in dataset:
print(model_test_3_1(x))
Edit 2020-02-20:
make_model
does not work with tf2.1.0, but make_model_2
still does. I have oppened an issue on GitHub about this backward incompatibility. Here is the link if you are interested. Recall that both functions work if you plan to stay on tf2.0.0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With