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

This utility function sets up a model selection table for use by the multiple model Keras fit feature of MADlib. By model selection we mean both hyperparameter tuning and model architecture search. The table defines the unique combinations of model architectures, compile and fit parameters to run on a massively parallel processing database cluster.

Load Model Selection Table
load_model_selection_table(
    model_arch_table,
    model_selection_table,
    model_id_list,
    compile_params_list,
    fit_params_list
    )

Arguments

model_arch_table

VARCHAR. Table containing model architectures and weights. For more information on this table refer to Load Model.

model_selection_table

VARCHAR. Model selection table created by this utility. A summary table named <model_selection_table>_summary is also created. Contents of both output tables are described below.

model_id_list

INTEGER[]. Array of model IDs from the 'model_arch_table' to be included in the run combinations. For hyperparameter search, this will typically be one model ID. For model architecture search, this will be the different model IDs that you want to test.

compile_params_list

VARCHAR[]. Array of compile parameters to be tested. Each element of the array should consist of a string of compile parameters exactly as it is to be passed to Keras.

fit_params_list

VARCHAR[]. Array of fit parameters to be tested. Each element of the array should consist of a string of fit parameters exactly as it is to be passed to Keras.

Output table
The model selection output table contains the following columns:

mst_key INTEGER. ID that defines a unique tuple for model architecture-compile parameters-fit parameters.
model_id VARCHAR. Model architecture ID from the 'model_arch_table'.
compile_params VARCHAR. Keras compile parameters.
fit_params VARCHAR. Keras fit parameters.

A summary table named <model_selection_table>_summary is also created, which contains the following column:

model_arch_table VARCHAR. Name of the model architecture table containing the model architecture IDs.

Examples
  1. The model selection table works in conjunction with a model architecture table, so we first create a model architecture table with two different models. Use Keras to define a model architecture with 1 hidden layer:
    import keras
    from keras.models import Sequential
    from keras.layers import Dense
    model1 = Sequential()
    model1.add(Dense(10, activation='relu', input_shape=(4,)))
    model1.add(Dense(10, activation='relu'))
    model1.add(Dense(3, activation='softmax'))
    model1.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:
    model1.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"}'
    
    Now use Keras to define a model architecture with 2 hidden layers:
    model2 = Sequential()
    model2.add(Dense(10, activation='relu', input_shape=(4,)))
    model2.add(Dense(10, activation='relu'))
    model2.add(Dense(10, activation='relu'))
    model2.add(Dense(3, activation='softmax'))
    model2.summary()
    
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    dense_4 (Dense)              (None, 10)                50
    _________________________________________________________________
    dense_5 (Dense)              (None, 10)                110
    _________________________________________________________________
    dense_6 (Dense)              (None, 10)                110
    _________________________________________________________________
    dense_7 (Dense)              (None, 3)                 33
    =================================================================
    Total params: 303
    Trainable params: 303
    Non-trainable params: 0
    
    Export the model to JSON:
    model2.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_4", "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_5", "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_6", "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_7", "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"}'
    
  2. Load both models into the 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
                                   'MLP with 1 hidden layer'       -- Descr
    );
    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_4", "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_5", "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_6", "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_7", "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
                                   'MLP with 2 hidden layers'       -- Descr
    );
    SELECT model_id, name, description FROM model_arch_library ORDER BY model_id;
    
     model_id |  name  |       description
    ----------+--------+--------------------------
            1 | Sophie | MLP with 1 hidden layer
            2 | Maria  | MLP with 2 hidden layers
    (2 rows)
    
  3. Load model selection table. Select the model(s) from the model architecture table that you want to run, along with the compile and fit parameters. Unique combinations will be created:
    DROP TABLE IF EXISTS mst_table, mst_table_summary;
    SELECT madlib.load_model_selection_table('model_arch_library', -- model architecture table
                                             'mst_table',          -- model selection table output
                                              ARRAY[1,2],              -- model ids from model architecture table
                                              ARRAY[                   -- compile params
                                                  $$loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']$$,
                                                  $$loss='categorical_crossentropy', optimizer='Adam(lr=0.01)',metrics=['accuracy']$$,
                                                  $$loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']$$
                                              ],
                                              ARRAY[                    -- fit params
                                                  $$batch_size=4,epochs=1$$,
                                                  $$batch_size=8,epochs=1$$
                                              ]
                                             );
    SELECT * FROM mst_table ORDER BY mst_key;
    
     mst_key | model_id      |                                 compile_params                                  |      fit_params
    ---------+---------------+---------------------------------------------------------------------------------+-----------------------
           1 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']   | batch_size=4,epochs=1
           2 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']   | batch_size=8,epochs=1
           3 |             1 | loss='categorical_crossentropy', optimizer='Adam(lr=0.01)',metrics=['accuracy'] | batch_size=4,epochs=1
           4 |             1 | loss='categorical_crossentropy', optimizer='Adam(lr=0.01)',metrics=['accuracy'] | batch_size=8,epochs=1
           5 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy'] | batch_size=4,epochs=1
           6 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy'] | batch_size=8,epochs=1
           7 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']   | batch_size=4,epochs=1
           8 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']   | batch_size=8,epochs=1
           9 |             2 | loss='categorical_crossentropy', optimizer='Adam(lr=0.01)',metrics=['accuracy'] | batch_size=4,epochs=1
          10 |             2 | loss='categorical_crossentropy', optimizer='Adam(lr=0.01)',metrics=['accuracy'] | batch_size=8,epochs=1
          11 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy'] | batch_size=4,epochs=1
          12 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy'] | batch_size=8,epochs=1
    (12 rows)
    
    The name of the model architecture table is stored in the summary table:
    SELECT * FROM mst_table_summary;
    
      model_arch_table
    --------------------+
     model_arch_library
    (1 row)
    
  4. Create model selection table manually. If you would like to have more control over the set of model selection parameters to run, you can manually create the model selection table and the associated summary table. Both must be created since they are needed by the multiple model fit module. For example, let's say we don't want all combinations but only want batch_size=4 for model_id=1 and batch_size=8 for model_id=2:
    DROP TABLE IF EXISTS mst_table_manual;
    CREATE TABLE mst_table_manual(
        mst_key serial,
        model_id integer,
        compile_params varchar,
        fit_params varchar
    );
    INSERT INTO mst_table_manual(model_id, compile_params, fit_params) VALUES
    (1, $$loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']$$, 'batch_size=4,epochs=1'),
    (1, $$loss='categorical_crossentropy',optimizer='Adam(lr=0.01)',metrics=['accuracy']$$, 'batch_size=4,epochs=1'),
    (1, $$loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']$$, 'batch_size=4,epochs=1'),
    (2, $$loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']$$, 'batch_size=8,epochs=1'),
    (2, $$loss='categorical_crossentropy',optimizer='Adam(lr=0.01)',metrics=['accuracy']$$, 'batch_size=8,epochs=1'),
    (2, $$loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']$$, 'batch_size=8,epochs=1');
    SELECT * FROM mst_table_manual ORDER BY mst_key;
    
     mst_key | model_id      |                                 compile_params                                  |      fit_params
    ---------+---------------+---------------------------------------------------------------------------------+-----------------------
           1 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']   | batch_size=4,epochs=1
           2 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.01)',metrics=['accuracy']  | batch_size=4,epochs=1
           3 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy'] | batch_size=4,epochs=1
           4 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']   | batch_size=8,epochs=1
           5 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.01)',metrics=['accuracy']  | batch_size=8,epochs=1
           6 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy'] | batch_size=8,epochs=1
    (6 rows)
    
    Create the summary table which must be named with the model selection output table appended by "_summary":
    DROP TABLE IF EXISTS mst_table_manual_summary;
    CREATE TABLE mst_table_manual_summary (
        model_arch_table varchar
    );
    INSERT INTO mst_table_manual_summary(model_arch_table) VALUES
    ('model_arch_library');
    SELECT * FROM mst_table_manual_summary;
    
      model_arch_table
    --------------------+
     model_arch_library
    (1 row)
    
  5. Generate hyperparameters automatically. You can use other libraries or methods to generate hyperparameters according to the tests that you want to run. For example, let's randomly generate batch size from powers of 2 and learning rate on a log scale. We use psycopg which is a PostgreSQL database adapter for the Python programming language.
    import numpy as np
    import psycopg2 as p2
    conn = p2.connect('postgresql://gpadmin@35.239.240.26:5432/madlib')
    #conn = p2.connect('postgresql://fmcquillan:5432/madlib')
    cur = conn.cursor()
    #
    sql DROP TABLE IF EXISTS mst_table_auto, mst_table_auto_summary;
    #
    #compile params
    learning_rate = np.random.permutation([0.1,0.01,0.001,0.0001])[:3]
    compile_param1 = "loss='categorical_crossentropy',optimizer='Adam(lr=" + str(learning_rate[0]) + ")',metrics=['accuracy']"
    compile_param2 = "loss='categorical_crossentropy',optimizer='Adam(lr=" + str(learning_rate[1]) + ")',metrics=['accuracy']"
    compile_param3 = "loss='categorical_crossentropy',optimizer='Adam(lr=" + str(learning_rate[2]) + ")',metrics=['accuracy']"
    compile_params = [compile_param1,compile_param2,compile_param3]
    #
    #fit params
    batch_size = np.random.permutation([4,8,16,32,64])[:2]
    fit_param1 = "batch_size=" + str(batch_size[0]) + ",epochs=1"
    fit_param2 = "batch_size=" + str(batch_size[1]) + ",epochs=1"
    fit_params = [fit_param1,fit_param2]
    #
    query = "SELECT madlib.load_model_selection_table('model_arch_library', 'mst_table_auto', ARRAY[1,2], %s, %s);"
    #
    cur.execute(query,[compile_params, fit_params])
    conn.commit()
    #
    # review model selection table
    sql SELECT * FROM mst_table_auto ORDER BY mst_key;
    
     mst_key | model_id      |                                  compile_params                                  |      fit_params
    ---------+---------------+----------------------------------------------------------------------------------+-----------------------
           1 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.0001)',metrics=['accuracy'] | batch_size=4,epochs=1
           2 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.0001)',metrics=['accuracy'] | batch_size=8,epochs=1
           3 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']    | batch_size=4,epochs=1
           4 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']    | batch_size=8,epochs=1
           5 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']  | batch_size=4,epochs=1
           6 |             1 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']  | batch_size=8,epochs=1
           7 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.0001)',metrics=['accuracy'] | batch_size=4,epochs=1
           8 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.0001)',metrics=['accuracy'] | batch_size=8,epochs=1
           9 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']    | batch_size=4,epochs=1
          10 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.1)',metrics=['accuracy']    | batch_size=8,epochs=1
          11 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']  | batch_size=4,epochs=1
          12 |             2 | loss='categorical_crossentropy',optimizer='Adam(lr=0.001)',metrics=['accuracy']  | batch_size=8,epochs=1
    (12 rows)
    
    The name of the model architecture table is stored in the summary table:
    SELECT * FROM mst_table_auto_summary;
    
      model_arch_table
    --------------------+
     model_arch_library
    (1 row)
    

Notes
  1. In this method, the same compile and fit parameters are applied to all model architectures when generating combinations. However, you may wish to have different compile and fit parameters for each model. To do so, call 'load_model_selection_table' multiple times - once for each model. Then you can combine the resulting tables using UNION or other means. Note that the 'mst_key' must be unique so you will need to regenerate it in your final combined table.

Related Topics

See keras_model_arch_table.sql_in