blob: 0081e58e4c5ce3f6ee0fe781ef45d5da51110e4d [file] [log] [blame]
# coding=utf-8
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
m4_changequote(`<!', `!>')
import sys
import json
import plpy
from keras_model_arch_table import ModelArchSchema
def _get_layers(model_arch):
d = json.loads(model_arch)
config = d['config']
if type(config) == list:
return config # In keras 2.1.x, all models are sequential
elif type(config) == dict and 'layers' in config:
layers = config['layers']
if type(layers) == list:
return config['layers'] # In keras 2.x, only sequential models are supported
plpy.error("Unable to read model architecture JSON.")
def get_input_shape(model_arch):
arch_layers = _get_layers(model_arch)
shapes = []
for i in arch_layers:
if 'batch_input_shape' in i['config']:
shapes.append(i['config']['batch_input_shape'][1:])
if shapes:
return shapes
plpy.error('Unable to get input shape from model architecture.'\
'Make sure that the first layer defines an input_shape.')
def get_num_classes(model_arch, multi_dep_count):
"""
We assume that the last dense layer in the model architecture contains the num_classes (units)
An example can be:
```
...
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))
```
where activation can be after the dense layer.
:param model_arch:
:return:
"""
arch_layers = _get_layers(model_arch)
num_classes = []
i = len(arch_layers) - 1
dep_counter = 0
while i >= 0 and dep_counter < multi_dep_count:
if 'units' in arch_layers[i]['config']:
num_classes.append(arch_layers[i]['config']['units'])
dep_counter +=1
i -= 1
if num_classes:
num_classes.reverse()
return num_classes
plpy.error('Unable to get number of classes from model architecture.')
def get_model_arch_layers_str(model_arch):
arch_layers = _get_layers(model_arch)
layers = "Model arch layers:\n"
first = True
for layer in arch_layers:
if first:
first = False
else:
layers += " |\n"
layers += " V\n"
class_name = layer['class_name']
config = layer['config']
if class_name == 'Dense':
layers += "{1}[{2}]\n".format(class_name, config['units'])
else:
layers += "{1}\n".format(class_name)
return layers
def get_model_arch(model_arch_table, model_id):
"""
For fit_multiple, we don't want to keep sending weights back and
forth between the main host and the segment hosts. weights can be
up to 1GB in size, whereas the model arch in JSON is usually very
small.
"""
s = ModelArchSchema
model_arch_query = """
SELECT {s.MODEL_ARCH} FROM {model_arch_table}
WHERE {s.MODEL_ID} = {model_id}
""".format(**locals())
model_arch_result = plpy.execute(model_arch_query)
if not model_arch_result or len(model_arch_result) != 1:
plpy.error("no model arch found in table {0} with id {1}".format(
model_arch_table, model_id))
model_arch = model_arch_result[0][ModelArchSchema.MODEL_ARCH]
return model_arch
def get_model_arch_weights(model_arch_table, model_id):
"""
For fit, we need both the model arch & model weights
"""
#assume validation is already called
model_arch_query = "SELECT {0}, {1} FROM {2} WHERE {3} = {4}".format(
ModelArchSchema.MODEL_ARCH, ModelArchSchema.MODEL_WEIGHTS,
model_arch_table, ModelArchSchema.MODEL_ID,
model_id)
model_arch_result = plpy.execute(model_arch_query)
if not model_arch_result:
plpy.error("no model arch found in table {0} with id {1}".format(
model_arch_table, model_id))
model_arch_result = model_arch_result[0]
model_arch = model_arch_result[ModelArchSchema.MODEL_ARCH]
model_weights = model_arch_result[ModelArchSchema.MODEL_WEIGHTS]
return model_arch, model_weights