1.17.0
User Documentation for Apache MADlib
 All Files Functions Variables Groups
Warning
This MADlib method is still in early stage development. Interface and implementation are subject to change.

This module allows you to use SQL to call deep learning models designed in Keras [1], which is a high-level neural network API written in Python. Keras was developed for fast experimentation. It can run on top of different backends and the one that is currently supported by MADlib is TensorFlow [2]. The implementation in MADlib is distributed and designed to train a single model across multiple segments (workers) in Greenplum database. (PostgreSQL is also supported.) Alternatively, to train multiple models at the same time for model architecture search or hyperparameter tuning, you can use Model Selection.

The main use case is image classification using sequential models, which are made up of a linear stack of layers. This includes multilayer perceptrons (MLPs) and convolutional neural networks (CNNs). Regression is not currently supported.

Before using Keras in MADlib you will need to mini-batch your training and evaluation datasets by calling the Preprocessor for Images which is a utility that prepares image data for use by models that support mini-batch as an optimization option. This is a one-time operation and you would only need to re-run the preprocessor if your input data has changed. The advantage of using mini-batching is that it can perform better than stochastic gradient descent because it uses more than one training example at a time, typically resulting faster and smoother convergence [3].

You can also do inference on models that have not been trained in MADlib, but rather imported from an external source. This is in the section called "Predict BYOM" below, where "BYOM" stands for "Bring Your Own Model."

Note that the following MADlib functions are targeting a specific Keras version (2.2.4) with a specific TensorFlow kernel version (1.14). Using a newer or older version may or may not work as intended.

Note
CUDA GPU memory cannot be released until the process holding it is terminated. When a MADlib deep learning function is called with GPUs, Greenplum internally creates a process (called a slice) which calls TensorFlow to do the computation. This process holds the GPU memory until one of the following two things happen: query finishes and user logs out of the Postgres client/session; or, query finishes and user waits for the timeout set by gp_vmem_idle_resource_timeout. The default value for this timeout is 18 sec [8]. So the recommendation is: log out/reconnect to the session after every GPU query; or wait for gp_vmem_idle_resource_timeout before you run another GPU query (you can also set it to a lower value).

Fit
The fit (training) function has the following format:
madlib_keras_fit(
    source_table,
    model,
    model_arch_table,
    model_id,
    compile_params,
    fit_params,
    num_iterations,
    use_gpus,
    validation_table,
    metrics_compute_frequency,
    warm_start,
    name,
    description
    )

Arguments

source_table

TEXT. Name of the table containing the training data. This is the name of the output table from the image preprocessor. Independent and dependent variables are specified in the preprocessor step which is why you do not need to explictly state them here as part of the fit function.

model

TEXT. Name of the output table containing the model. Details of the output table are shown below.

model_arch_table

TEXT. Name of the table containing the model architecture and (optionally) initial weights to use for training.

model_id

INTEGER. This is the id in 'model_arch_table' containing the model architecture and (optionally) initial weights to use for training.

compile_params

TEXT. Parameters passed to the compile method of the Keras model class [4]. These parameters will be passed through as is so they must conform to the Keras API definition. As an example, you might use something like: loss='categorical_crossentropy', optimizer='adam', metrics=['acc']. The mandatory parameters that must be specified are 'optimizer' and 'loss'. Others are optional and will use the default values as per Keras if not specified here. Also, when specifying 'loss' and 'metrics' do not include the module and submodule prefixes like loss='losses.categorical_crossentropy' or optimizer='keras.optmizers.adam'.

Note
The following loss function is not supported: sparse_categorical_crossentropy. The following metrics are not supported: sparse_categorical_accuracy, top_k_categorical_accuracy, sparse_top_k_categorical_accuracy and custom metrics.

fit_params

TEXT. Parameters passed to the fit method of the Keras model class [4]. These will be passed through as is so they must conform to the Keras API definition. As an example, you might use something like: batch_size=128, epochs=4. There are no mandatory parameters so if you specify NULL, it will use all default values as per Keras.

num_iterations

INTEGER. Number of iterations to train.

use_gpus (optional)

BOOLEAN, default: FALSE (i.e., CPU). Determines whether GPUs are to be used for training the neural network. Set to TRUE to use GPUs.

Note
This parameter must not conflict with how the distribution rules are set in the preprocessor function. For example, if you set a distribution rule to use certain segments on hosts that do not have GPUs attached, you will get an error if you set ‘use_gpus’ to TRUE. Also, we have seen some memory related issues when segments share GPU resources. For example, if you have 1 GPU per segment host and your cluster has 4 segments per segment host, it means that all 4 segments will share the same GPU on each host. The current recommended configuration is 1 GPU per segment.
validation_table (optional)

TEXT, default: none. Name of the table containing the validation dataset. Note that the validation dataset must be preprocessed in the same way as the training dataset, so this is the name of the output table from running the image preprocessor on the validation dataset. Using a validation dataset can mean a longer training time, depending on its size. This can be controlled using the 'metrics_compute_frequency' paremeter described below.

metrics_compute_frequency (optional)

INTEGER, default: once at the end of training after 'num_iterations'. Frequency to compute per-iteration metrics for the training dataset and validation dataset (if specified). There can be considerable cost to computing metrics every iteration, especially if the training dataset is large. This parameter is a way of controlling the frequency of those computations. For example, if you specify 5, then metrics will be computed every 5 iterations as well as at the end of training after 'num_iterations'. If you use the default, metrics will be computed only once after 'num_iterations' have completed.

warm_start (optional)

BOOLEAN, default: FALSE. Initalize weights with the coefficients from the last call of the fit function. If set to TRUE, weights will be initialized from the model table generated by the previous training run.

Note
The warm start feature works based on the name of the model output table from a previous training run. When using warm start, do not drop the model output table or the model output summary table before calling the fit function, since these are needed to obtain the weights from the previous run. If you are not using warm start, the model output table and the model output table summary must be dropped in the usual way before calling the training function.
name (optional)

TEXT, default: NULL. Free text string to identify a name, if desired.

description (optional)
TEXT, default: NULL. Free text string to provide a description, if desired.

Output tables
The model table produced by fit contains the following columns:

model_weights BYTEA8. Byte array containing the weights of the neural net.
model_arch TEXT. A JSON representation of the model architecture used in training.

A summary table named <model>_summary is also created, which has the following columns:

source_table Source table used for training.
model Model output table produced by training.
independent_varname Independent variables column from the original source table in the image preprocessing step.
dependent_varname Dependent variable column from the original source table in the image preprocessing step.
model_arch_table Name of the table containing the model architecture and (optionally) the initial model weights.
model_id The id of the model in the model architecture table used for training.
compile_params Compile parameters passed to Keras.
fit_params Fit parameters passed to Keras.
num_iterations Number of iterations of training completed.
validation_table Name of the table containing the validation dataset (if specified).
metrics_compute_frequency Frequency that per-iteration metrics are computed for the training dataset and validation dataset.
name Name of the training run (free text).
description Description of the training run (free text).
model_type General identifier for type of model trained. Currently says 'madlib_keras'.
model_size Size of the model in KB. Models are stored in 'bytea' data format which is used for binary strings in PostgreSQL type databases.
start_training_time Timestamp for start of training.
end_training_time Timestamp for end of training.
metrics_elapsed_time Array of elapsed time for metric computations as per the 'metrics_compute_frequency' parameter. Useful for drawing a curve showing loss, accuracy or other metrics as a function of time. For example, if 'metrics_compute_frequency=5' this would be an array of elapsed time for every 5th iteration, plus the last iteration.
madlib_version Version of MADlib used.
num_classes Count of distinct classes values used.
class_values Array of actual class values used.
dependent_vartype Data type of the dependent variable.
normalizing_constant Normalizing constant used from the image preprocessing step.
metrics_type Metric specified in the 'compile_params'.
training_metrics_final Final value of the training metric after all iterations have completed. The metric reported is the one specified in the 'metrics_type' parameter.
training_loss_final Final value of the training loss after all iterations have completed.
training_metrics Array of training metrics as per the 'metrics_compute_frequency' parameter. For example, if 'metrics_compute_frequency=5' this would be an array of metrics for every 5th iteration, plus the last iteration.
training_loss Array of training losses as per the 'metrics_compute_frequency' parameter. For example, if 'metrics_compute_frequency=5' this would be an array of losses for every 5th iteration, plus the last iteration.
validation_metrics_final Final value of the validation metric after all iterations have completed. The metric reported is the one specified in the 'metrics_type' parameter.
validation_loss_final Final value of the validation loss after all iterations have completed.
validation_metrics Array of validation metrics as per the 'metrics_compute_frequency' parameter. For example, if 'metrics_compute_frequency=5' this would be an array of metrics for every 5th iteration, plus the last iteration.
validation_loss Array of validation losses as per the 'metrics_compute_frequency' parameter. For example, if 'metrics_compute_frequency=5' this would be an array of losses for every 5th iteration, plus the last iteration.
metrics_iters Array indicating the iterations for which metrics are calculated, as derived from the parameters 'num_iterations' and 'metrics_compute_frequency'. For example, if 'num_iterations=5' and 'metrics_compute_frequency=2', then 'metrics_iters' value would be {2,4,5} indicating that metrics were computed at iterations 2, 4 and 5 (at the end). If 'num_iterations=5' and 'metrics_compute_frequency=1', then 'metrics_iters' value would be {1,2,3,4,5} indicating that metrics were computed at every iteration.

Evaluate
The evaluation function has the following format:
madlib_keras_evaluate(
    model_table,
    test_table,
    output_table,
    use_gpus,
    mst_key
    )

Arguments

model_table

TEXT. Name of the table containing the model to use for validation.

test_table

TEXT. Name of the table containing the evaluation dataset. Note that test/validation data must be preprocessed in the same way as the training dataset, so this is the name of the output table from the image preprocessor. Independent and dependent variables are specified in the preprocessor step which is why you do not need to explictly state them here as part of the fit function.

output_table
TEXT. Name of table that validation output will be written to. Table contains:
loss Loss value on evaluation dataset.
metric Metric value on evaluation dataset, where 'metrics_type' below identifies the type of metric.
metrics_type

Type of metric used that was used in the training step.

use_gpus (optional)

BOOLEAN, default: FALSE (i.e., CPU). Determines whether GPUs are to be used for training the neural network. Set to TRUE to use GPUs.

Note
This parameter must not conflict with how the distribution rules are set in the preprocessor function. For example, if you set a distribution rule to use certain segments on hosts that do not have GPUs attached, you will get an error if you set ‘use_gpus’ to TRUE. Also, we have seen some memory related issues when segments share GPU resources. For example, if you have 1 GPU per segment host and your cluster has 4 segments per segment host, it means that all 4 segments will share the same GPU on each host. The current recommended configuration is 1 GPU per segment.
mst_key (optional)
INTEGER, default: NULL. ID that defines a unique tuple for model architecture-compile parameters-fit parameters in a model selection table. Do not use this if training one model at a time using madlib_keras_fit(). See the Model Selection section for more details on model selection by training multiple models at a time.

Predict
The prediction function has the following format:
madlib_keras_predict(
    model_table,
    test_table,
    id_col,
    independent_varname,
    output_table,
    pred_type,
    use_gpus,
    mst_key
    )

Arguments

model_table

TEXT. Name of the table containing the model to use for prediction.

test_table

TEXT. Name of the table containing the dataset to predict on. Note that test data is not preprocessed (unlike fit and evaluate) so put one test image per row for prediction. Also see the comment below for the 'independent_varname' parameter regarding normalization.

id_col

TEXT. Name of the id column in the test data table.

independent_varname

TEXT. Column with independent variables in the test table. If a 'normalizing_const' is specified when preprocessing the training dataset, this same normalization will be applied to the independent variables used in predict.

output_table
TEXT. Name of the table that prediction output will be written to. Table contains:
id Gives the 'id' for each prediction, corresponding to each row from the test_table.
estimated_COL_NAME (For pred_type='response') The estimated class for classification, where COL_NAME is the name of the column to be predicted from test data.
prob_CLASS

(For pred_type='prob' for classification) The probability of a given class. There will be one column for each class in the training data.

pred_type (optional)

TEXT, default: 'response'. The type of output desired, where 'response' gives the actual prediction and 'prob' gives the probability value for each class.

use_gpus (optional)

BOOLEAN, default: FALSE (i.e., CPU). Flag to enable GPU support for training neural network. The number of GPUs to use is determined by the parameters passed to the preprocessor.

Note
We have seen some memory related issues when segments share GPU resources. For example, if you provide 1 GPU and your database cluster is set up to have 4 segments per segment host, it means that all 4 segments on a segment host will share the same GPU. The current recommended configuration is 1 GPU per segment.
mst_key (optional)
INTEGER, default: NULL. ID that defines a unique tuple for model architecture-compile parameters-fit parameters in a model selection table. Do not use this if training one model at a time using madlib_keras_fit(). See the Model Selection section for more details on model selection by training multiple models at a time.

Predict BYOM (bring your own model)
The predict BYOM function allows you to do inference on models that have not been trained on MADlib, but rather imported from elsewhere. It has the following format:
madlib_keras_predict_byom(
    model_arch_table,
    model_id,
    test_table,
    id_col,
    independent_varname,
    output_table,
    pred_type,
    use_gpus,
    class_values,
    normalizing_const
    )

Arguments

model_arch_table

TEXT. Name of the architecture table containing the model to use for prediction. The model weights and architecture can be loaded to this table by using the load_keras_model function.

model_id

INTEGER. This is the id in 'model_arch_table' containing the model architecture and model weights to use for prediction.

test_table

TEXT. Name of the table containing the dataset to predict on. Note that test data is not preprocessed (unlike fit and evaluate) so put one test image per row for prediction. Set the 'normalizing_const' below for the independent variable if necessary.

id_col

TEXT. Name of the id column in the test data table.

independent_varname

TEXT. Column with independent variables in the test table. Set the 'normalizing_const' below if necessary.

output_table
TEXT. Name of the table that prediction output will be written to. Table contains:
id Gives the 'id' for each prediction, corresponding to each row from the 'test_table'.
estimated_dependent_var (For pred_type='response') Estimated class for classification. If the 'class_values' parameter is passed in as NULL, then we assume that the class labels are [0,1,2...,n-1] where n-1 is the number of classes in the model architecture.
prob_CLASS

(For pred_type='prob' for classification) Probability of a given class. If 'class_values' is passed in as NULL, we create one column called 'prob' which is an array of probabilities for each class. If 'class_values' is not NULL, then there will be one column for each class.

pred_type (optional)

TEXT, default: 'response'. The type of output desired, where 'response' gives the actual prediction and 'prob' gives the probability value for each class.

use_gpus (optional)

BOOLEAN, default: FALSE (i.e., CPU). Flag to enable GPU support for training neural network. The number of GPUs to use is determined by the parameters passed to the preprocessor.

Note
We have seen some memory related issues when segments share GPU resources. For example, if you provide 1 GPU and your database cluster is set up to have 4 segments per segment host, it means that all 4 segments on a segment host will share the same GPU. The current recommended configuration is 1 GPU per segment.
class_values (optional)

TEXT[], default: NULL. List of class labels that were used while training the model. See the 'output_table' column above for more details.

Note
If you specify the class values parameter, it must reflect how the dependent variable was 1-hot encoded for training. If you accidently pick another order that does not match the 1-hot encoding, the predictions would be wrong.
normalizing_const (optional)
DOUBLE PRECISION, default: 1.0. The normalizing constant to divide each value in the 'independent_varname' array by. For example, you would use 255 for this value if the image data is in the form 0-255.

Examples
Note
Deep learning works best on very large datasets, but that is not convenient for a quick introduction to the syntax. So in this example we use an MLP on the well known iris data set from https://archive.ics.uci.edu/ml/datasets/iris. For more realistic examples with images please refer to the deep learning notebooks at https://github.com/apache/madlib-site/tree/asf-site/community-artifacts.

Classification

  1. Create an input data set.
    DROP TABLE IF EXISTS iris_data;
    CREATE TABLE iris_data(
        id serial,
        attributes numeric[],
        class_text varchar
    );
    INSERT INTO iris_data(id, attributes, class_text) VALUES
    (1,ARRAY[5.1,3.5,1.4,0.2],'Iris-setosa'),
    (2,ARRAY[4.9,3.0,1.4,0.2],'Iris-setosa'),
    (3,ARRAY[4.7,3.2,1.3,0.2],'Iris-setosa'),
    (4,ARRAY[4.6,3.1,1.5,0.2],'Iris-setosa'),
    (5,ARRAY[5.0,3.6,1.4,0.2],'Iris-setosa'),
    (6,ARRAY[5.4,3.9,1.7,0.4],'Iris-setosa'),
    (7,ARRAY[4.6,3.4,1.4,0.3],'Iris-setosa'),
    (8,ARRAY[5.0,3.4,1.5,0.2],'Iris-setosa'),
    (9,ARRAY[4.4,2.9,1.4,0.2],'Iris-setosa'),
    (10,ARRAY[4.9,3.1,1.5,0.1],'Iris-setosa'),
    (11,ARRAY[5.4,3.7,1.5,0.2],'Iris-setosa'),
    (12,ARRAY[4.8,3.4,1.6,0.2],'Iris-setosa'),
    (13,ARRAY[4.8,3.0,1.4,0.1],'Iris-setosa'),
    (14,ARRAY[4.3,3.0,1.1,0.1],'Iris-setosa'),
    (15,ARRAY[5.8,4.0,1.2,0.2],'Iris-setosa'),
    (16,ARRAY[5.7,4.4,1.5,0.4],'Iris-setosa'),
    (17,ARRAY[5.4,3.9,1.3,0.4],'Iris-setosa'),
    (18,ARRAY[5.1,3.5,1.4,0.3],'Iris-setosa'),
    (19,ARRAY[5.7,3.8,1.7,0.3],'Iris-setosa'),
    (20,ARRAY[5.1,3.8,1.5,0.3],'Iris-setosa'),
    (21,ARRAY[5.4,3.4,1.7,0.2],'Iris-setosa'),
    (22,ARRAY[5.1,3.7,1.5,0.4],'Iris-setosa'),
    (23,ARRAY[4.6,3.6,1.0,0.2],'Iris-setosa'),
    (24,ARRAY[5.1,3.3,1.7,0.5],'Iris-setosa'),
    (25,ARRAY[4.8,3.4,1.9,0.2],'Iris-setosa'),
    (26,ARRAY[5.0,3.0,1.6,0.2],'Iris-setosa'),
    (27,ARRAY[5.0,3.4,1.6,0.4],'Iris-setosa'),
    (28,ARRAY[5.2,3.5,1.5,0.2],'Iris-setosa'),
    (29,ARRAY[5.2,3.4,1.4,0.2],'Iris-setosa'),
    (30,ARRAY[4.7,3.2,1.6,0.2],'Iris-setosa'),
    (31,ARRAY[4.8,3.1,1.6,0.2],'Iris-setosa'),
    (32,ARRAY[5.4,3.4,1.5,0.4],'Iris-setosa'),
    (33,ARRAY[5.2,4.1,1.5,0.1],'Iris-setosa'),
    (34,ARRAY[5.5,4.2,1.4,0.2],'Iris-setosa'),
    (35,ARRAY[4.9,3.1,1.5,0.1],'Iris-setosa'),
    (36,ARRAY[5.0,3.2,1.2,0.2],'Iris-setosa'),
    (37,ARRAY[5.5,3.5,1.3,0.2],'Iris-setosa'),
    (38,ARRAY[4.9,3.1,1.5,0.1],'Iris-setosa'),
    (39,ARRAY[4.4,3.0,1.3,0.2],'Iris-setosa'),
    (40,ARRAY[5.1,3.4,1.5,0.2],'Iris-setosa'),
    (41,ARRAY[5.0,3.5,1.3,0.3],'Iris-setosa'),
    (42,ARRAY[4.5,2.3,1.3,0.3],'Iris-setosa'),
    (43,ARRAY[4.4,3.2,1.3,0.2],'Iris-setosa'),
    (44,ARRAY[5.0,3.5,1.6,0.6],'Iris-setosa'),
    (45,ARRAY[5.1,3.8,1.9,0.4],'Iris-setosa'),
    (46,ARRAY[4.8,3.0,1.4,0.3],'Iris-setosa'),
    (47,ARRAY[5.1,3.8,1.6,0.2],'Iris-setosa'),
    (48,ARRAY[4.6,3.2,1.4,0.2],'Iris-setosa'),
    (49,ARRAY[5.3,3.7,1.5,0.2],'Iris-setosa'),
    (50,ARRAY[5.0,3.3,1.4,0.2],'Iris-setosa'),
    (51,ARRAY[7.0,3.2,4.7,1.4],'Iris-versicolor'),
    (52,ARRAY[6.4,3.2,4.5,1.5],'Iris-versicolor'),
    (53,ARRAY[6.9,3.1,4.9,1.5],'Iris-versicolor'),
    (54,ARRAY[5.5,2.3,4.0,1.3],'Iris-versicolor'),
    (55,ARRAY[6.5,2.8,4.6,1.5],'Iris-versicolor'),
    (56,ARRAY[5.7,2.8,4.5,1.3],'Iris-versicolor'),
    (57,ARRAY[6.3,3.3,4.7,1.6],'Iris-versicolor'),
    (58,ARRAY[4.9,2.4,3.3,1.0],'Iris-versicolor'),
    (59,ARRAY[6.6,2.9,4.6,1.3],'Iris-versicolor'),
    (60,ARRAY[5.2,2.7,3.9,1.4],'Iris-versicolor'),
    (61,ARRAY[5.0,2.0,3.5,1.0],'Iris-versicolor'),
    (62,ARRAY[5.9,3.0,4.2,1.5],'Iris-versicolor'),
    (63,ARRAY[6.0,2.2,4.0,1.0],'Iris-versicolor'),
    (64,ARRAY[6.1,2.9,4.7,1.4],'Iris-versicolor'),
    (65,ARRAY[5.6,2.9,3.6,1.3],'Iris-versicolor'),
    (66,ARRAY[6.7,3.1,4.4,1.4],'Iris-versicolor'),
    (67,ARRAY[5.6,3.0,4.5,1.5],'Iris-versicolor'),
    (68,ARRAY[5.8,2.7,4.1,1.0],'Iris-versicolor'),
    (69,ARRAY[6.2,2.2,4.5,1.5],'Iris-versicolor'),
    (70,ARRAY[5.6,2.5,3.9,1.1],'Iris-versicolor'),
    (71,ARRAY[5.9,3.2,4.8,1.8],'Iris-versicolor'),
    (72,ARRAY[6.1,2.8,4.0,1.3],'Iris-versicolor'),
    (73,ARRAY[6.3,2.5,4.9,1.5],'Iris-versicolor'),
    (74,ARRAY[6.1,2.8,4.7,1.2],'Iris-versicolor'),
    (75,ARRAY[6.4,2.9,4.3,1.3],'Iris-versicolor'),
    (76,ARRAY[6.6,3.0,4.4,1.4],'Iris-versicolor'),
    (77,ARRAY[6.8,2.8,4.8,1.4],'Iris-versicolor'),
    (78,ARRAY[6.7,3.0,5.0,1.7],'Iris-versicolor'),
    (79,ARRAY[6.0,2.9,4.5,1.5],'Iris-versicolor'),
    (80,ARRAY[5.7,2.6,3.5,1.0],'Iris-versicolor'),
    (81,ARRAY[5.5,2.4,3.8,1.1],'Iris-versicolor'),
    (82,ARRAY[5.5,2.4,3.7,1.0],'Iris-versicolor'),
    (83,ARRAY[5.8,2.7,3.9,1.2],'Iris-versicolor'),
    (84,ARRAY[6.0,2.7,5.1,1.6],'Iris-versicolor'),
    (85,ARRAY[5.4,3.0,4.5,1.5],'Iris-versicolor'),
    (86,ARRAY[6.0,3.4,4.5,1.6],'Iris-versicolor'),
    (87,ARRAY[6.7,3.1,4.7,1.5],'Iris-versicolor'),
    (88,ARRAY[6.3,2.3,4.4,1.3],'Iris-versicolor'),
    (89,ARRAY[5.6,3.0,4.1,1.3],'Iris-versicolor'),
    (90,ARRAY[5.5,2.5,4.0,1.3],'Iris-versicolor'),
    (91,ARRAY[5.5,2.6,4.4,1.2],'Iris-versicolor'),
    (92,ARRAY[6.1,3.0,4.6,1.4],'Iris-versicolor'),
    (93,ARRAY[5.8,2.6,4.0,1.2],'Iris-versicolor'),
    (94,ARRAY[5.0,2.3,3.3,1.0],'Iris-versicolor'),
    (95,ARRAY[5.6,2.7,4.2,1.3],'Iris-versicolor'),
    (96,ARRAY[5.7,3.0,4.2,1.2],'Iris-versicolor'),
    (97,ARRAY[5.7,2.9,4.2,1.3],'Iris-versicolor'),
    (98,ARRAY[6.2,2.9,4.3,1.3],'Iris-versicolor'),
    (99,ARRAY[5.1,2.5,3.0,1.1],'Iris-versicolor'),
    (100,ARRAY[5.7,2.8,4.1,1.3],'Iris-versicolor'),
    (101,ARRAY[6.3,3.3,6.0,2.5],'Iris-virginica'),
    (102,ARRAY[5.8,2.7,5.1,1.9],'Iris-virginica'),
    (103,ARRAY[7.1,3.0,5.9,2.1],'Iris-virginica'),
    (104,ARRAY[6.3,2.9,5.6,1.8],'Iris-virginica'),
    (105,ARRAY[6.5,3.0,5.8,2.2],'Iris-virginica'),
    (106,ARRAY[7.6,3.0,6.6,2.1],'Iris-virginica'),
    (107,ARRAY[4.9,2.5,4.5,1.7],'Iris-virginica'),
    (108,ARRAY[7.3,2.9,6.3,1.8],'Iris-virginica'),
    (109,ARRAY[6.7,2.5,5.8,1.8],'Iris-virginica'),
    (110,ARRAY[7.2,3.6,6.1,2.5],'Iris-virginica'),
    (111,ARRAY[6.5,3.2,5.1,2.0],'Iris-virginica'),
    (112,ARRAY[6.4,2.7,5.3,1.9],'Iris-virginica'),
    (113,ARRAY[6.8,3.0,5.5,2.1],'Iris-virginica'),
    (114,ARRAY[5.7,2.5,5.0,2.0],'Iris-virginica'),
    (115,ARRAY[5.8,2.8,5.1,2.4],'Iris-virginica'),
    (116,ARRAY[6.4,3.2,5.3,2.3],'Iris-virginica'),
    (117,ARRAY[6.5,3.0,5.5,1.8],'Iris-virginica'),
    (118,ARRAY[7.7,3.8,6.7,2.2],'Iris-virginica'),
    (119,ARRAY[7.7,2.6,6.9,2.3],'Iris-virginica'),
    (120,ARRAY[6.0,2.2,5.0,1.5],'Iris-virginica'),
    (121,ARRAY[6.9,3.2,5.7,2.3],'Iris-virginica'),
    (122,ARRAY[5.6,2.8,4.9,2.0],'Iris-virginica'),
    (123,ARRAY[7.7,2.8,6.7,2.0],'Iris-virginica'),
    (124,ARRAY[6.3,2.7,4.9,1.8],'Iris-virginica'),
    (125,ARRAY[6.7,3.3,5.7,2.1],'Iris-virginica'),
    (126,ARRAY[7.2,3.2,6.0,1.8],'Iris-virginica'),
    (127,ARRAY[6.2,2.8,4.8,1.8],'Iris-virginica'),
    (128,ARRAY[6.1,3.0,4.9,1.8],'Iris-virginica'),
    (129,ARRAY[6.4,2.8,5.6,2.1],'Iris-virginica'),
    (130,ARRAY[7.2,3.0,5.8,1.6],'Iris-virginica'),
    (131,ARRAY[7.4,2.8,6.1,1.9],'Iris-virginica'),
    (132,ARRAY[7.9,3.8,6.4,2.0],'Iris-virginica'),
    (133,ARRAY[6.4,2.8,5.6,2.2],'Iris-virginica'),
    (134,ARRAY[6.3,2.8,5.1,1.5],'Iris-virginica'),
    (135,ARRAY[6.1,2.6,5.6,1.4],'Iris-virginica'),
    (136,ARRAY[7.7,3.0,6.1,2.3],'Iris-virginica'),
    (137,ARRAY[6.3,3.4,5.6,2.4],'Iris-virginica'),
    (138,ARRAY[6.4,3.1,5.5,1.8],'Iris-virginica'),
    (139,ARRAY[6.0,3.0,4.8,1.8],'Iris-virginica'),
    (140,ARRAY[6.9,3.1,5.4,2.1],'Iris-virginica'),
    (141,ARRAY[6.7,3.1,5.6,2.4],'Iris-virginica'),
    (142,ARRAY[6.9,3.1,5.1,2.3],'Iris-virginica'),
    (143,ARRAY[5.8,2.7,5.1,1.9],'Iris-virginica'),
    (144,ARRAY[6.8,3.2,5.9,2.3],'Iris-virginica'),
    (145,ARRAY[6.7,3.3,5.7,2.5],'Iris-virginica'),
    (146,ARRAY[6.7,3.0,5.2,2.3],'Iris-virginica'),
    (147,ARRAY[6.3,2.5,5.0,1.9],'Iris-virginica'),
    (148,ARRAY[6.5,3.0,5.2,2.0],'Iris-virginica'),
    (149,ARRAY[6.2,3.4,5.4,2.3],'Iris-virginica'),
    (150,ARRAY[5.9,3.0,5.1,1.8],'Iris-virginica');
    
    Create a test/validation dataset from the training data:
    DROP TABLE IF EXISTS iris_train, iris_test;
    -- Set seed so results are reproducible
    SELECT setseed(0);
    SELECT madlib.train_test_split('iris_data',     -- Source table
                                   'iris',          -- Output table root name
                                    0.8,            -- Train proportion
                                    NULL,           -- Test proportion (0.2)
                                    NULL,           -- Strata definition
                                    NULL,           -- Output all columns
                                    NULL,           -- Sample without replacement
                                    TRUE            -- Separate output tables
                                  );
    SELECT COUNT(*) FROM iris_train;
    
     count
    ------+
       120
    
  2. Call the preprocessor for deep learning. For the training dataset:
    \x off
    DROP TABLE IF EXISTS iris_train_packed, iris_train_packed_summary;
    SELECT madlib.training_preprocessor_dl('iris_train',         -- Source table
                                           'iris_train_packed',  -- Output table
                                           'class_text',         -- Dependent variable
                                           'attributes'          -- Independent variable
                                            );
    SELECT * FROM iris_train_packed_summary;
    
    -[ RECORD 1 ]-------+---------------------------------------------
    source_table        | iris_train
    output_table        | iris_train_packed
    dependent_varname   | class_text
    independent_varname | attributes
    dependent_vartype   | character varying
    class_values        | {Iris-setosa,Iris-versicolor,Iris-virginica}
    buffer_size         | 60
    normalizing_const   | 1.0
    num_classes         | 3
    
    For the validation dataset:
    DROP TABLE IF EXISTS iris_test_packed, iris_test_packed_summary;
    SELECT madlib.validation_preprocessor_dl('iris_test',          -- Source table
                                             'iris_test_packed',   -- Output table
                                             'class_text',         -- Dependent variable
                                             'attributes',         -- Independent variable
                                             'iris_train_packed'   -- From training preprocessor step
                                              );
    SELECT * FROM iris_test_packed_summary;
    
    -[ RECORD 1 ]-------+---------------------------------------------
    source_table        | iris_test
    output_table        | iris_test_packed
    dependent_varname   | class_text
    independent_varname | attributes
    dependent_vartype   | character varying
    class_values        | {Iris-setosa,Iris-versicolor,Iris-virginica}
    buffer_size         | 15
    normalizing_const   | 1.0
    num_classes         | 3
    
  3. Define and load model architecture. Use Keras to define the model architecture:
    import keras
    from keras.models import Sequential
    from keras.layers import Dense
    model_simple = Sequential()
    model_simple.add(Dense(10, activation='relu', input_shape=(4,)))
    model_simple.add(Dense(10, activation='relu'))
    model_simple.add(Dense(3, activation='softmax'))
    model_simple.summary()
    
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    dense_1 (Dense)              (None, 10)                50
    _________________________________________________________________
    dense_2 (Dense)              (None, 10)                110
    _________________________________________________________________
    dense_3 (Dense)              (None, 3)                 33
    =================================================================
    Total params: 193
    Trainable params: 193
    Non-trainable params: 0
    
    Export the model to JSON:
    model_simple.to_json()
    
    '{"class_name": "Sequential", "keras_version": "2.1.6", "config": [{"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_1", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "dtype": "float32", "activation": "relu", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "batch_input_shape": [null, 4], "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_2", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "relu", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_3", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "softmax", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 3, "use_bias": true, "activity_regularizer": null}}], "backend": "tensorflow"}'
    
    Load into model architecture table:
    DROP TABLE IF EXISTS model_arch_library;
    SELECT madlib.load_keras_model('model_arch_library',  -- Output table,
    $$
    {"class_name": "Sequential", "keras_version": "2.1.6", "config": [{"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_1", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "dtype": "float32", "activation": "relu", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "batch_input_shape": [null, 4], "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_2", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "relu", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_3", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "softmax", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 3, "use_bias": true, "activity_regularizer": null}}], "backend": "tensorflow"}
    $$
    ::json,  -- JSON blob
                                   NULL,                  -- Weights
                                   'Sophie',              -- Name
                                   'A simple model'       -- Descr
    );
    
  4. Train model and view summary table:
    DROP TABLE IF EXISTS iris_model, iris_model_summary;
    SELECT madlib.madlib_keras_fit('iris_train_packed',   -- source table
                                   'iris_model',          -- model output table
                                   'model_arch_library',  -- model arch table
                                    1,                    -- model arch id
                                    $$ loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] $$,  -- compile_params
                                    $$ batch_size=5, epochs=3 $$,  -- fit_params
                                    10                    -- num_iterations
                                  );
    SELECT * FROM iris_model_summary;
    
    -[ RECORD 1 ]-------------+--------------------------------------------------------------------------
    source_table              | iris_train_packed
    model                     | iris_model
    dependent_varname         | class_text
    independent_varname       | attributes
    model_arch_table          | model_arch_library
    model_id                  | 1
    compile_params            |  loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']
    fit_params                |  batch_size=5, epochs=3
    num_iterations            | 10
    validation_table          |
    metrics_compute_frequency | 10
    name                      |
    description               |
    model_type                | madlib_keras
    model_size                | 0.7900390625
    start_training_time       | 2019-06-05 20:55:15.785034
    end_training_time         | 2019-06-05 20:55:25.373035
    metrics_elapsed_time      | {9.58799290657043}
    madlib_version            | 1.17.0
    num_classes               | 3
    class_values              | {Iris-setosa,Iris-versicolor,Iris-virginica}
    dependent_vartype         | character varying
    normalizing_const         | 1
    metrics_type              | {accuracy}
    training_metrics_final    | 0.766666650772
    training_loss_final       | 0.721103310585
    training_metrics          | {0.766666650772095}
    training_loss             | {0.721103310585022}
    validation_metrics_final  |
    validation_loss_final     |
    validation_metrics        |
    validation_loss           |
    metrics_iters             | {10}
    
  5. Use the test dataset to evaluate the model we built above:
    DROP TABLE IF EXISTS iris_validate;
    SELECT madlib.madlib_keras_evaluate('iris_model',       -- model
                                       'iris_test_packed',  -- test table
                                       'iris_validate'      -- output table
                                       );
    SELECT * FROM iris_validate;
    
           loss        |      metric       | metrics_type
    -------------------+-------------------+--------------
     0.719491899013519 | 0.800000011920929 | {accuracy}
    (1 row)
    
  6. Predict. We will use the validation dataset for prediction as well, which is not usual but serves to show the syntax. The prediction is in the 'estimated_class_text' column:
    DROP TABLE IF EXISTS iris_predict;
    SELECT madlib.madlib_keras_predict('iris_model', -- model
                                       'iris_test',  -- test_table
                                       'id',  -- id column
                                       'attributes', -- independent var
                                       'iris_predict'  -- output table
                                       );
    SELECT * FROM iris_predict ORDER BY id;
    
     id  | estimated_class_text
    -----+----------------------
       4 | Iris-setosa
       6 | Iris-setosa
       8 | Iris-setosa
      12 | Iris-setosa
      13 | Iris-setosa
      15 | Iris-setosa
      24 | Iris-setosa
      30 | Iris-setosa
      38 | Iris-setosa
      49 | Iris-setosa
      60 | Iris-virginica
      68 | Iris-versicolor
      69 | Iris-versicolor
      76 | Iris-versicolor
      78 | Iris-versicolor
      81 | Iris-versicolor
      85 | Iris-virginica
      90 | Iris-versicolor
      91 | Iris-versicolor
      94 | Iris-virginica
     104 | Iris-virginica
     106 | Iris-versicolor
     107 | Iris-virginica
     110 | Iris-virginica
     119 | Iris-versicolor
     127 | Iris-virginica
     129 | Iris-virginica
     134 | Iris-versicolor
     139 | Iris-virginica
     144 | Iris-virginica
    (30 rows)
    
    Count missclassifications:
    SELECT COUNT(*) FROM iris_predict JOIN iris_test USING (id)
    WHERE iris_predict.estimated_class_text != iris_test.class_text;
    
     count
    -------+
         6
    (1 row)
    
    Accuracy:
    SELECT round(count(*)*100/(150*0.2),2) as test_accuracy_percent from
        (select iris_test.class_text as actual, iris_predict.estimated_class_text as estimated
         from iris_predict inner join iris_test
         on iris_test.id=iris_predict.id) q
    WHERE q.actual=q.estimated;
    
     test_accuracy_percent
    -----------------------+
                     80.00
    (1 row)
    
  7. Predict BYOM. We will use the validation dataset for prediction as well, which is not usual but serves to show the syntax. See load_keras_model for details on how to load the model architecture and weights. In this example we will use weights we already have:
    UPDATE model_arch_library
    SET model_weights = iris_model.model_weights
    FROM iris_model
    WHERE model_arch_library.model_id = 1;
    
    Now train using a model from the model architecture table directly without referencing the model table from the MADlib training. Note that if you specify the class values parameter as we do below, it must reflect how the dependent variable was 1-hot encoded for training. In this example the 'training_preprocessor_dl()' in Step 2 above encoded in the order {'Iris-setosa', 'Iris-versicolor', 'Iris-virginica'} so this is the order we pass in the parameter. If we accidently pick another order that does not match the 1-hot encoding, the predictions would be wrong.
    DROP TABLE IF EXISTS iris_predict_byom;
    SELECT madlib.madlib_keras_predict_byom('model_arch_library',  -- model arch table
                                             1,                    -- model arch id
                                            'iris_test',           -- test_table
                                            'id',                  -- id column
                                            'attributes',          -- independent var
                                            'iris_predict_byom',   -- output table
                                            'response',            -- prediction type
                                             FALSE,                -- use GPUs
                                             ARRAY['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'], -- class values
                                             1.0                   -- normalizing const
                                       );
    SELECT * FROM iris_predict_byom ORDER BY id;
    
    The prediction is in the 'estimated_dependent_var' column:
     id  | estimated_dependent_var
    -----+----------------------
       4 | Iris-setosa
       6 | Iris-setosa
       8 | Iris-setosa
      12 | Iris-setosa
      13 | Iris-setosa
      15 | Iris-setosa
      24 | Iris-setosa
      30 | Iris-setosa
      38 | Iris-setosa
      49 | Iris-setosa
      60 | Iris-virginica
      68 | Iris-versicolor
      69 | Iris-versicolor
      76 | Iris-versicolor
      78 | Iris-versicolor
      81 | Iris-versicolor
      85 | Iris-virginica
      90 | Iris-versicolor
      91 | Iris-versicolor
      94 | Iris-virginica
     104 | Iris-virginica
     106 | Iris-versicolor
     107 | Iris-virginica
     110 | Iris-virginica
     119 | Iris-versicolor
     127 | Iris-virginica
     129 | Iris-virginica
     134 | Iris-versicolor
     139 | Iris-virginica
     144 | Iris-virginica
    (30 rows)
     
    Count missclassifications:
    SELECT COUNT(*) FROM iris_predict_byom JOIN iris_test USING (id)
    WHERE iris_predict_byom.estimated_dependent_var != iris_test.class_text;
    
     count
    -------+
         6
    (1 row)
    
    Accuracy:
    SELECT round(count(*)*100/(150*0.2),2) as test_accuracy_percent from
        (select iris_test.class_text as actual, iris_predict_byom.estimated_dependent_var as estimated
         from iris_predict_byom inner join iris_test
         on iris_test.id=iris_predict_byom.id) q
    WHERE q.actual=q.estimated;
    
     test_accuracy_percent
    -----------------------+
                     80.00
    (1 row)
    

Classification with Other Parameters

  1. Validation dataset. Now use a validation dataset and compute metrics every 3rd iteration using the 'metrics_compute_frequency' parameter. This can help reduce run time if you do not need metrics computed at every iteration.
    DROP TABLE IF EXISTS iris_model, iris_model_summary;
    SELECT madlib.madlib_keras_fit('iris_train_packed',   -- source table
                                   'iris_model',          -- model output table
                                   'model_arch_library',  -- model arch table
                                    1,                    -- model arch id
                                    $$ loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] $$,  -- compile_params
                                    $$ batch_size=5, epochs=3 $$,  -- fit_params
                                    10,                   -- num_iterations
                                    FALSE,                -- use GPUs
                                    'iris_test_packed',   -- validation dataset
                                    3,                    -- metrics compute frequency
                                    FALSE,                -- warm start
                                   'Sophie L.',           -- name
                                   'Simple MLP for iris dataset'  -- description
                                  );
    SELECT * FROM iris_model_summary;
    
    -[ RECORD 1 ]-------------+--------------------------------------------------------------------------
    source_table              | iris_train_packed
    model                     | iris_model
    dependent_varname         | class_text
    independent_varname       | attributes
    model_arch_table          | model_arch_library
    model_id                  | 1
    compile_params            |  loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']
    fit_params                |  batch_size=5, epochs=3
    num_iterations            | 10
    validation_table          | iris_test_packed
    metrics_compute_frequency | 3
    name                      | Sophie L.
    description               | Simple MLP for iris dataset
    model_type                | madlib_keras
    model_size                | 0.7900390625
    start_training_time       | 2019-06-05 20:58:23.224629
    end_training_time         | 2019-06-05 20:58:35.477499
    metrics_elapsed_time      | {4.69859290122986,8.2062520980835,10.8104848861694,12.2528700828552}
    madlib_version            | 1.17.0
    num_classes               | 3
    class_values              | {Iris-setosa,Iris-versicolor,Iris-virginica}
    dependent_vartype         | character varying
    normalizing_const         | 1
    metrics_type              | {accuracy}
    training_metrics_final    | 0.941666662693
    training_loss_final       | 0.40586027503
    training_metrics          | {0.699999988079071,0.800000011920929,0.899999976158142,0.941666662693024}
    training_loss             | {0.825238645076752,0.534248650074005,0.427499741315842,0.405860275030136}
    validation_metrics_final  | 0.866666674614
    validation_loss_final     | 0.409001916647
    validation_metrics        | {0.733333349227905,0.733333349227905,0.866666674613953,0.866666674613953}
    validation_loss           | {0.827081918716431,0.536275088787079,0.431326270103455,0.409001916646957}
    metrics_iters             | {3,6,9,10}
    
  2. Predict probabilities for each class:
    DROP TABLE IF EXISTS iris_predict;
    SELECT madlib.madlib_keras_predict('iris_model',      -- model
                                       'iris_test',       -- test_table
                                       'id',              -- id column
                                       'attributes',      -- independent var
                                       'iris_predict',    -- output table
                                       'prob'             -- response type
                                       );
    SELECT * FROM iris_predict ORDER BY id;
    
     id  | prob_Iris-setosa | prob_Iris-versicolor | prob_Iris-virginica
    -----+------------------+----------------------+---------------------
       4 |        0.9241953 |          0.059390426 |          0.01641435
       6 |        0.9657151 |           0.02809224 |        0.0061926916
       8 |        0.9543316 |           0.03670931 |         0.008959154
      12 |       0.93851465 |          0.048681837 |         0.012803554
      13 |       0.93832576 |           0.04893658 |         0.012737647
      15 |       0.98717564 |           0.01091238 |        0.0019119986
      24 |        0.9240628 |          0.060805064 |         0.015132156
      30 |       0.92063266 |          0.062279057 |         0.017088294
      38 |        0.9353765 |          0.051353406 |         0.013270103
      49 |        0.9709265 |          0.023811856 |         0.005261566
      60 |      0.034395564 |            0.5260507 |          0.43955377
      68 |      0.031360663 |           0.53689945 |          0.43173987
      69 |     0.0098787155 |           0.46121457 |          0.52890676
      76 |      0.031186827 |            0.5644549 |          0.40435827
      78 |       0.00982633 |           0.48929632 |           0.5008774
      81 |       0.03658528 |           0.53248984 |           0.4309249
      85 |      0.015423619 |           0.48452598 |           0.5000504
      90 |      0.026857043 |            0.5155698 |          0.45757324
      91 |      0.013675574 |           0.47155368 |           0.5147708
      94 |      0.073440716 |            0.5418821 |           0.3846772
     104 |     0.0021637122 |            0.3680499 |          0.62978643
     106 |    0.00052832486 |           0.30891812 |           0.6905536
     107 |      0.007315576 |           0.40949163 |           0.5831927
     110 |     0.0022259138 |            0.4058138 |          0.59196025
     119 |    0.00018505375 |           0.24510723 |           0.7547077
     127 |      0.009542585 |           0.46958733 |          0.52087003
     129 |     0.0019719477 |           0.36288205 |            0.635146
     134 |     0.0056418083 |           0.43401477 |          0.56034344
     139 |       0.01067015 |            0.4755573 |          0.51377255
     144 |     0.0018909549 |           0.37689638 |           0.6212126
    (30 rows)
    
  3. Warm start. Next, use the warm_start parameter to continue learning, using the coefficients from the run above. Note that we don't drop the model table or model summary table:
    SELECT madlib.madlib_keras_fit('iris_train_packed',   -- source table
                                   'iris_model',          -- model output table
                                   'model_arch_library',  -- model arch table
                                    1,                    -- model arch id
                                    $$ loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] $$,  -- compile_params
                                    $$ batch_size=5, epochs=3 $$,  -- fit_params
                                    5,                   -- num_iterations
                                    FALSE,               -- use GPUs
                                    'iris_test_packed',   -- validation dataset
                                    1,                    -- metrics compute frequency
                                    TRUE,                 -- warm start
                                   'Sophie L.',           -- name
                                   'Simple MLP for iris dataset'  -- description
                                  );
    SELECT * FROM iris_model_summary;
    
    -[ RECORD 1 ]-------------+--------------------------------------------------------------------------------------------
    source_table              | iris_train_packed
    model                     | iris_model
    dependent_varname         | class_text
    independent_varname       | attributes
    model_arch_table          | model_arch_library
    model_id                  | 1
    compile_params            |  loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']
    fit_params                |  batch_size=5, epochs=3
    num_iterations            | 5
    validation_table          | iris_test_packed
    metrics_compute_frequency | 1
    name                      | Sophie L.
    description               | Simple MLP for iris dataset
    model_type                | madlib_keras
    model_size                | 0.7900390625
    start_training_time       | 2019-06-05 20:59:43.971792
    end_training_time         | 2019-06-05 20:59:51.654586
    metrics_elapsed_time      | {2.89326310157776,4.14273309707642,5.24781513214111,6.34498596191406,7.68279695510864}
    madlib_version            | 1.17.0
    num_classes               | 3
    class_values              | {Iris-setosa,Iris-versicolor,Iris-virginica}
    dependent_vartype         | character varying
    normalizing_const         | 1
    metrics_type              | {accuracy}
    training_metrics_final    | 0.933333337307
    training_loss_final       | 0.334455043077
    training_metrics          | {0.933333337306976,0.933333337306976,0.975000023841858,0.975000023841858,0.933333337306976}
    training_loss             | {0.386842548847198,0.370587915182114,0.357161343097687,0.344598710536957,0.334455043077469}
    validation_metrics_final  | 0.866666674614
    validation_loss_final     | 0.34414178133
    validation_metrics        | {0.866666674613953,0.866666674613953,0.933333337306976,0.866666674613953,0.866666674613953}
    validation_loss           | {0.391442179679871,0.376414686441422,0.362262904644012,0.351912915706635,0.344141781330109}
    metrics_iters             | {1,2,3,4,5}
    
    Note that the loss and accuracy values pick up from where the previous run left off.

Transfer Learning

Here we want to start with initial weights from a pre-trained model rather than training from scratch. We also want to use a model architecture with the earlier feature layer(s) frozen to save on training time. The example below is somewhat contrived but gives you the idea of the steps.

  1. Define and load a model architecture with the 1st hidden layer frozen:
model_transfer = Sequential()
model_transfer.add(Dense(10, activation='relu', input_shape=(4,), trainable=False))
model_transfer.add(Dense(10, activation='relu'))
model_transfer.add(Dense(3, activation='softmax'))
model_simple.summary()

_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
dense_1 (Dense)              (None, 10)                50
_________________________________________________________________
dense_2 (Dense)              (None, 10)                110
_________________________________________________________________
dense_3 (Dense)              (None, 3)                 33
=================================================================
Total params: 193
Trainable params: 143
Non-trainable params: 50

Export the model to JSON:

model_simple.to_json()
'{"class_name": "Sequential", "keras_version": "2.1.6", "config": [{"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_2", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "dtype": "float32", "activation": "relu", "trainable": false, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "batch_input_shape": [null, 4], "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_3", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "relu", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_4", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "softmax", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 3, "use_bias": true, "activity_regularizer": null}}], "backend": "tensorflow"}'

Load into model architecture table:

SELECT madlib.load_keras_model('model_arch_library',  -- Output table,
$$
{"class_name": "Sequential", "keras_version": "2.1.6", "config": [{"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_2", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "dtype": "float32", "activation": "relu", "trainable": false, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "batch_input_shape": [null, 4], "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_3", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "relu", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 10, "use_bias": true, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"kernel_initializer": {"class_name": "VarianceScaling", "config": {"distribution": "uniform", "scale": 1.0, "seed": null, "mode": "fan_avg"}}, "name": "dense_4", "kernel_constraint": null, "bias_regularizer": null, "bias_constraint": null, "activation": "softmax", "trainable": true, "kernel_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "units": 3, "use_bias": true, "activity_regularizer": null}}], "backend": "tensorflow"}
$$
::json,  -- JSON blob
                               NULL,                  -- Weights
                               'Maria',               -- Name
                               'A transfer model'     -- Descr
);

Fetch the weights from a previous MADlib run. (Normally these would be downloaded from a source that trained the same model architecture on a related dataset.)

UPDATE model_arch_library
SET model_weights = iris_model.model_weights
FROM iris_model
WHERE model_arch_library.model_id = 2;

Now train the model using the transfer model and the pre-trained weights:

DROP TABLE IF EXISTS iris_model, iris_model_summary;
SELECT madlib.madlib_keras_fit('iris_train_packed',   -- source table
                               'iris_model',          -- model output table
                               'model_arch_library',  -- model arch table
                                2,                    -- model arch id
                                $$ loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] $$,  -- compile_params
                                $$ batch_size=5, epochs=3 $$,  -- fit_params
                                10                    -- num_iterations
                              );
SELECT * FROM iris_model_summary;
-[ RECORD 1 ]-------------+--------------------------------------------------------------------------
source_table              | iris_train_packed
model                     | iris_model
dependent_varname         | class_text
independent_varname       | attributes
model_arch_table          | model_arch_library
model_id                  | 2
compile_params            |  loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']
fit_params                |  batch_size=5, epochs=3
num_iterations            | 10
validation_table          |
metrics_compute_frequency | 10
name                      |
description               |
model_type                | madlib_keras
model_size                | 0.7900390625
start_training_time       | 2019-06-05 21:01:03.998422
end_training_time         | 2019-06-05 21:01:13.525838
metrics_elapsed_time      | {9.52741599082947}
madlib_version            | 1.17.0
num_classes               | 3
class_values              | {Iris-setosa,Iris-versicolor,Iris-virginica}
dependent_vartype         | character varying
normalizing_const         | 1
metrics_type              | {accuracy}
training_metrics_final    | 0.975000023842
training_loss_final       | 0.245171800256
training_metrics          | {0.975000023841858}
training_loss             | {0.245171800255775}
validation_metrics_final  |
validation_loss_final     |
validation_metrics        |
validation_loss           |
metrics_iters             | {10}

Notes
  1. Refer to the deep learning section of the Apache MADlib wiki [5] for important information including supported libraries and versions.
  2. Classification is currently supported, not regression.
  3. Reminder about the distinction between warm start and transfer learning. Warm start uses model state (weights) from the model output table from a previous training run - set the 'warm_start' parameter to TRUE in the fit function. Transfer learning uses initial model state (weights) stored in the 'model_arch_table' - in this case set the 'warm_start' parameter to FALSE in the fit function.

Technical Background

For an introduction to deep learning foundations, including MLP and CNN, refer to [6].

This module trains a single large model across the database cluster using the bulk synchronous parallel (BSP) approach, with model averaging [7].

On the effect of database cluster size: as the database cluster size increases, the per iteration loss will be higher since the model only sees 1/n of the data, where n is the number of segments. However, each iteration runs faster than single node because it is only traversing 1/n of the data. For highly non-convex solution spaces, convergence behavior may diminish as cluster size increases. Ensure that each segment has sufficient volume of data and examples of each class value.

Alternatively, to train multiple models at the same time for model architecture search or hyperparameter tuning, you can use Model Selection, which does not do model averaging and hence may have better covergence efficiency.

Literature

[1] https://keras.io/

[2] https://www.tensorflow.org/

[3] "Neural Networks for Machine Learning", Lectures 6a and 6b on mini-batch gradient descent, Geoffrey Hinton with Nitish Srivastava and Kevin Swersky, http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf

[4] https://keras.io/models/model/

[5] Deep learning section of Apache MADlib wiki, https://cwiki.apache.org/confluence/display/MADLIB/Deep+Learning

[6] Deep Learning, Ian Goodfellow, Yoshua Bengio and Aaron Courville, MIT Press, 2016.

[7] "Resource-Efficient and Reproducible Model Selection on Deep Learning Systems," Supun Nakandala, Yuhao Zhang, and Arun Kumar, Technical Report, Computer Science and Engineering, University of California, San Diego https://adalabucsd.github.io/papers/TR_2019_Cerebro.pdf.

[8] Greenplum Database server configuration parameters https://gpdb.docs.pivotal.io/latest/ref_guide/config_params/guc-list.html

Related Topics

File madlib_keras.sql_in documenting the training, evaluate and predict functions.