This is an automated email from the ASF dual-hosted git repository.

njayaram pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/madlib.git

commit 920175f4825694628db27a66bcf2a384c2d53322
Author: Nandish Jayaram <[email protected]>
AuthorDate: Thu Apr 25 12:13:59 2019 -0700

    DL: Add GPU support for predict
    
    JIRA: MADLIB-1330
    Add an optional parameter named use_gpu in predict interface which is
    set to TRUE by default. If use_gpu is true, then GPU support on predict
    is enabled.
    Closes #377
---
 .../modules/deep_learning/madlib_keras.sql_in      | 36 ++++++++---
 .../deep_learning/madlib_keras_predict.py_in       | 16 +++--
 .../deep_learning/madlib_keras_wrapper.py_in       |  3 +-
 .../modules/deep_learning/test/madlib_keras.sql_in | 74 ++++++++++++++++++----
 4 files changed, 102 insertions(+), 27 deletions(-)

diff --git a/src/ports/postgres/modules/deep_learning/madlib_keras.sql_in 
b/src/ports/postgres/modules/deep_learning/madlib_keras.sql_in
index 37b1068..5f53488 100644
--- a/src/ports/postgres/modules/deep_learning/madlib_keras.sql_in
+++ b/src/ports/postgres/modules/deep_learning/madlib_keras.sql_in
@@ -173,7 +173,8 @@ CREATE OR REPLACE FUNCTION 
MADLIB_SCHEMA.madlib_keras_predict(
     id_col                  VARCHAR,
     independent_varname     VARCHAR,
     output_table            VARCHAR,
-    pred_type               VARCHAR
+    pred_type               VARCHAR,
+    use_gpu                 BOOLEAN
 ) RETURNS VOID AS $$
     PythonFunctionBodyOnly(`deep_learning', `madlib_keras_predict')
     with AOControl(False):
@@ -183,7 +184,8 @@ CREATE OR REPLACE FUNCTION 
MADLIB_SCHEMA.madlib_keras_predict(
                id_col,
                independent_varname,
                output_table,
-               pred_type)
+               pred_type,
+               use_gpu)
 $$ LANGUAGE plpythonu VOLATILE
 m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `MODIFIES SQL DATA', `');
 
@@ -192,19 +194,33 @@ CREATE OR REPLACE FUNCTION 
MADLIB_SCHEMA.madlib_keras_predict(
     test_table              VARCHAR,
     id_col                  VARCHAR,
     independent_varname     VARCHAR,
+    output_table            VARCHAR,
+    pred_type               VARCHAR
+) RETURNS VOID AS $$
+    SELECT MADLIB_SCHEMA.madlib_keras_predict($1, $2, $3, $4, $5, $6, TRUE);
+$$ LANGUAGE sql VOLATILE
+m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `MODIFIES SQL DATA');
+
+CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.madlib_keras_predict(
+    model_table             VARCHAR,
+    test_table              VARCHAR,
+    id_col                  VARCHAR,
+    independent_varname     VARCHAR,
     output_table            VARCHAR
 ) RETURNS VOID AS $$
-    SELECT MADLIB_SCHEMA.madlib_keras_predict($1, $2, $3, $4, $5, 'response');
+    SELECT MADLIB_SCHEMA.madlib_keras_predict($1, $2, $3, $4, $5, 'response', 
TRUE);
 $$ LANGUAGE sql VOLATILE
 m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `MODIFIES SQL DATA');
 
 CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.internal_keras_predict(
-   independent_var double precision [],
+   independent_var    DOUBLE PRECISION [],
    model_architecture TEXT,
-   model_data bytea,
-   input_shape integer[],
-   is_response BOOLEAN,
-   normalizing_const DOUBLE PRECISION
+   model_data         BYTEA,
+   input_shape        INTEGER[],
+   is_response        BOOLEAN,
+   normalizing_const  DOUBLE PRECISION,
+   use_gpu            BOOLEAN,
+   seg                INTEGER
 ) RETURNS DOUBLE PRECISION[] AS $$
     PythonFunctionBodyOnly(`deep_learning', `madlib_keras_predict')
     with AOControl(False):
@@ -214,7 +230,9 @@ CREATE OR REPLACE FUNCTION 
MADLIB_SCHEMA.internal_keras_predict(
                model_data,
                input_shape,
                is_response,
-               normalizing_const)
+               normalizing_const,
+               use_gpu,
+               seg)
 $$ LANGUAGE plpythonu VOLATILE
 m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `MODIFIES SQL DATA', `');
 
diff --git 
a/src/ports/postgres/modules/deep_learning/madlib_keras_predict.py_in 
b/src/ports/postgres/modules/deep_learning/madlib_keras_predict.py_in
index e726f57..d47f53a 100644
--- a/src/ports/postgres/modules/deep_learning/madlib_keras_predict.py_in
+++ b/src/ports/postgres/modules/deep_learning/madlib_keras_predict.py_in
@@ -30,11 +30,13 @@ import numpy as np
 from madlib_keras_helper import expand_input_dims
 from madlib_keras_helper import MODEL_DATA_COLNAME
 from madlib_keras_validator import PredictInputValidator
+from madlib_keras_wrapper import get_device_name_and_set_cuda_env
 from madlib_keras_wrapper import set_model_weights
 from predict_input_params import PredictParamsProcessor
 from utilities.model_arch_info import get_input_shape
 from utilities.utilities import add_postfix
 from utilities.utilities import create_cols_from_array_sql_string
+from utilities.utilities import is_platform_pg
 from utilities.utilities import unique_string
 from utilities.validate_args import input_tbl_valid
 from utilities.validate_args import output_tbl_valid
@@ -79,7 +81,7 @@ def _strip_trailing_nulls_from_class_values(class_values):
     return class_values
 
 def predict(schema_madlib, model_table, test_table, id_col,
-            independent_varname, output_table, pred_type, **kwargs):
+            independent_varname, output_table, pred_type, use_gpu, **kwargs):
     input_validator = PredictInputValidator(
         test_table, model_table, id_col, independent_varname,
         output_table, pred_type, MODULE_NAME)
@@ -110,6 +112,9 @@ def predict(schema_madlib, model_table, test_table, id_col,
         class_values, intermediate_col, pred_col_name,
         pred_col_type, is_response, MODULE_NAME)
 
+    segment_id = -1 if is_platform_pg() else '{0}.gp_segment_id'.format(
+        test_table)
+
     plpy.execute("""
         CREATE TABLE {output_table} AS
         SELECT {id_col}, {prediction_select_clause}
@@ -121,17 +126,18 @@ def predict(schema_madlib, model_table, test_table, 
id_col,
                         {0},
                         ARRAY{input_shape},
                         {is_response},
-                        {normalizing_const})
+                        {normalizing_const},
+                        {use_gpu},
+                        {segment_id})
                    ) AS {intermediate_col}
         FROM {test_table}, {model_table}
         ) q
         """.format(MODEL_DATA_COLNAME, **locals()))
 
 def internal_keras_predict(x_test, model_arch, model_data, input_shape,
-                           is_response, normalizing_const):
+                           is_response, normalizing_const, use_gpu, seg):
     model = model_from_json(model_arch)
-    device_name = '/cpu:0'
-    os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
+    device_name = get_device_name_and_set_cuda_env(use_gpu, seg)
     model_shapes = madlib_keras_serializer.get_model_shapes(model)
     set_model_weights(model, device_name, model_data, model_shapes)
     # Since the test data isn't mini-batched,
diff --git 
a/src/ports/postgres/modules/deep_learning/madlib_keras_wrapper.py_in 
b/src/ports/postgres/modules/deep_learning/madlib_keras_wrapper.py_in
index e0fd8f7..6149411 100644
--- a/src/ports/postgres/modules/deep_learning/madlib_keras_wrapper.py_in
+++ b/src/ports/postgres/modules/deep_learning/madlib_keras_wrapper.py_in
@@ -33,6 +33,7 @@ import keras.losses as losses
 
 import madlib_keras_serializer
 from utilities.utilities import _assert
+from utilities.utilities import is_platform_pg
 
 #######################################################################
 ########### Keras specific functions #####
@@ -41,7 +42,7 @@ def get_device_name_and_set_cuda_env(use_gpu, seg):
     gpus_per_host = 4
     if use_gpu:
         device_name = '/gpu:0'
-        if seg == -1:
+        if is_platform_pg():
             cuda_visible_dev = ','.join([i for i in range(gpus_per_host)])
         else:
             cuda_visible_dev = str(seg % gpus_per_host)
diff --git a/src/ports/postgres/modules/deep_learning/test/madlib_keras.sql_in 
b/src/ports/postgres/modules/deep_learning/test/madlib_keras.sql_in
index 3d1f8d7..81a088e 100644
--- a/src/ports/postgres/modules/deep_learning/test/madlib_keras.sql_in
+++ b/src/ports/postgres/modules/deep_learning/test/madlib_keras.sql_in
@@ -144,6 +144,42 @@ FROM (SELECT * FROM keras_saved_out_summary) summary;
 
 SELECT assert(model_data IS NOT NULL , 'Keras model output validation failed') 
FROM (SELECT * FROM keras_saved_out) k;
 
+-- Fit with use_gpu set to TRUE must error out on machines
+-- that don't have GPUs. Since Jenkins builds are run on docker containers
+-- that don't have GPUs, these queries must error out.
+DROP TABLE IF EXISTS keras_saved_out_gpu, keras_saved_out_gpu_summary;
+SELECT assert(trap_error($TRAP$madlib_keras_fit(
+    'cifar_10_sample_batched',
+    'keras_saved_out_gpu',
+    'dependent_var',
+    'independent_var',
+    'model_arch',
+    1,
+    $$ optimizer=SGD(lr=0.01, decay=1e-6, nesterov=True), 
loss='categorical_crossentropy', metrics=['accuracy']$$::text,
+    $$ batch_size=2, epochs=1, verbose=0 $$::text,
+    3,
+    TRUE,
+    'cifar_10_sample_val');$TRAP$) = 1,
+       'Fit with use_gpu=True must error out.');
+
+-- Prediction with use_gpu set to TRUE must error out on machines
+-- that don't have GPUs. Since Jenkins builds are run on docker containers
+-- that don't have GPUs, these queries must error out.
+
+-- IMPRORTANT: The following test must be run when we have a valid
+-- keras_saved_out model table. Otherwise, it will fail because of a
+-- non-existent model table, while we want to trap failure due to
+-- use_gpu=TRUE
+DROP TABLE IF EXISTS cifar10_predict_gpu;
+SELECT assert(trap_error($TRAP$madlib_keras_predict(
+    'keras_saved_out',
+    'cifar_10_sample',
+    'id',
+    'x',
+    'cifar10_predict_gpu',
+    NULL,
+    TRUE);$TRAP$) = 1,
+    'Prediction with use_gpu=TRUE must error out.');
 
 -- Test for
   -- Non null name and description columns
@@ -203,7 +239,9 @@ SELECT madlib_keras_predict(
     'cifar_10_sample',
     'id',
     'x',
-    'cifar10_predict');
+    'cifar10_predict',
+    NULL,
+    FALSE);
 
 -- Validate that prediction output table exists and has correct schema
 SELECT assert(UPPER(atttypid::regtype::TEXT) = 'INTEGER', 'id column should be 
INTEGER type')
@@ -223,12 +261,15 @@ SELECT assert(estimated_dependent_var IN (0,1),
               'Predicted value not in set of defined class values for model')
 FROM cifar10_predict;
 
-select assert(trap_error($TRAP$madlib_keras_predict(
+DROP TABLE IF EXISTS cifar10_predict;
+SELECT assert(trap_error($TRAP$madlib_keras_predict(
     'keras_saved_out',
     'cifar_10_sample_batched',
     'id',
     'x',
-    'cifar10_predict');$TRAP$) = 1,
+    'cifar10_predict',
+    NULL,
+    FALSE);$TRAP$) = 1,
     'Passing batched image table to predict should error out.');
 
 -- Compile and fit parameter tests
@@ -321,7 +362,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'prob');
+    'prob',
+    FALSE);
 
 SELECT assert(UPPER(atttypid::regtype::TEXT) =
     'DOUBLE PRECISION', 'column prob_0 should be double precision type')
@@ -405,7 +447,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'prob');
+    'prob',
+    FALSE);
 
 -- Validate the output datatype of newly created prediction columns
 -- for prediction type = 'prob' and class_values 'TEXT' with NULL as a valid
@@ -438,7 +481,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'response');
+    'response',
+    FALSE);
 
 -- Validate the output datatype of newly created prediction columns
 -- for prediction type = 'response' and class_values 'TEXT' with NULL
@@ -461,7 +505,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'prob');
+    'prob',
+    FALSE);
 
 -- Validate the output datatype of newly created prediction column
 -- for prediction type = 'response' and class_value = NULL
@@ -479,7 +524,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'response');
+    'response',
+    FALSE);
 
 -- Validate the output datatype of newly created prediction column
 -- for prediction type = 'response' and class_value = NULL
@@ -535,7 +581,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'prob');
+    'prob',
+    FALSE);
 
 -- Validate the output datatype of newly created prediction column
 -- for prediction type = 'prob' and class_values 'INT' with NULL
@@ -558,7 +605,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'response');
+    'response',
+    FALSE);
 
 -- Validate the output datatype of newly created prediction column
 -- for prediction type = 'response' and class_values 'TEXT' with NULL
@@ -625,7 +673,8 @@ SELECT madlib_keras_predict(
     'id',
     'x',
     'cifar10_predict',
-    'prob');
+    'prob',
+    FALSE);
 
 -- Prediction with incorrectly shaped data must error out.
 DROP TABLE IF EXISTS cifar10_predict;
@@ -635,5 +684,6 @@ SELECT assert(trap_error($TRAP$madlib_keras_predict(
         'id',
         'x',
         'cifar10_predict',
-        'prob');$TRAP$) = 1,
+        'prob',
+        FALSE);$TRAP$) = 1,
     'Input shape is (32, 32, 3) but model was trained with (3, 32, 32). Should 
have failed.');

Reply via email to