-
Notifications
You must be signed in to change notification settings - Fork 116
Description
Question:
Do we need to support the two types of keras model (Graph network from Keras Functional API, or subclass model), or just one?
There are two types of keras model:
- Graph network from Keras Functional and Sequential APIs.
- Subclassed networks are used when a user subclasses the
Modelclass.
More Keras features are supported with graph networks, such as serialization, whole model saving, etc.
Example of functional API:
inputs = tf.keras.Input(shape=(10,))
x = keras.layers.Dense(1)(inputs)
outputs = tf.nn.relu(x)
network = tf.keras.Model(inputs, outputs)
inputs and outputs can be a tensor, or a list of tensors.
Example of subclass model:
class MyModel(keras.Model):
def __init__(self):
super(MyModel, self).__init__(name='my_model', dynamic=False)
self.layer1 = keras.layers.Dense(10, activation='relu')
def call(self, inputs):
return self.layer1(inputs)
Functional API knows the input shape, while subclass model does not. In order to use model.call() for subclass model, model.build(input_shapes) need to be called in advance to let keras model knows the input shape. `input_shapes' is a shape or a list of shapes.
ElasticDL user-defined class for keras model definition
Functional API
We used to use functional API for keras model, such as in worker_test.py
class TestModel(object):
def __init__(self):
input1 = tf.keras.layers.Input(shape=(1,))
x1 = tf.keras.layers.Dense(1)(input1)
self._model = tf.keras.models.Model(input1, x1)
def get_keras_model(self):
return self._model
def output(self, data):
return self._model.call(data['x'])
@staticmethod
def loss(output, data):
return tf.reduce_mean(tf.square(output - data['y']))
@staticmethod
def input_fn(records):
x_list = []
y_list = []
# deserialize
for r in records:
parsed = np.frombuffer(r, dtype='float32')
x_list.append([parsed[0]])
y_list.append([parsed[1]])
# batching
batch_size = len(x_list)
xs = np.concatenate(x_list, axis=0)
xs = np.reshape(xs, (batch_size, 1))
ys = np.concatenate(y_list, axis=0)
ys = np.reshape(xs, (batch_size, 1))
return {'x': xs, 'y': ys}
@staticmethod
def optimizer(lr=0.1):
return tf.train.GradientDescentOptimizer(lr)
Subclass model
Later, we changed to subclass model worker_test.py:
class TestModel(tf.keras.Model):
def __init__(self):
super(TestModel, self).__init__(name='test_model')
self.dense = tf.keras.layers.Dense(1)
def call(self, inputs):
return self.dense(inputs)
@staticmethod
def input_shapes():
return (1, 1)
@staticmethod
def input_names():
return ['x']
@staticmethod
def loss(outputs, labels):
return tf.reduce_mean(tf.square(outputs - labels['y']))
@staticmethod
def input_fn(records):
x_list = []
y_list = []
# deserialize
for r in records:
parsed = np.frombuffer(r, dtype='float32')
x_list.append([parsed[0]])
y_list.append([parsed[1]])
# batching
batch_size = len(x_list)
xs = np.concatenate(x_list, axis=0)
xs = np.reshape(xs, (batch_size, 1))
ys = np.concatenate(y_list, axis=0)
ys = np.reshape(xs, (batch_size, 1))
return {'x': xs, 'y': ys}
@staticmethod
def optimizer(lr=0.1):
return tf.train.GradientDescentOptimizer(lr)