diff --git a/README.md b/README.md index 2e4d2f8c..4541204b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# dkube-examples +## dkube-examples This repository contains the DL examples ported to run on Dkube and showcase the features of Dkube platform. @@ -35,3 +35,4 @@ Following examples are provided, ## Pytorch Examples **Will be added soon** + diff --git a/tensorflow/classification/mnist/digits/classifier/program/model.py b/tensorflow/classification/mnist/digits/classifier/program/model.py index 4026b3c2..92a81877 100644 --- a/tensorflow/classification/mnist/digits/classifier/program/model.py +++ b/tensorflow/classification/mnist/digits/classifier/program/model.py @@ -27,10 +27,8 @@ FLAGS = None TF_TRAIN_STEPS = int(os.getenv('STEPS',1000)) -MODEL_DIR = os.getenv('DKUBE_JOB_OUTPUT_S3', None) -DATA_DIR = os.getenv('DKUBE_INPUT_DATASETS', None) -if DATA_DIR is not None: - DATA_DIR = DATA_DIR.split(",")[0] +MODEL_DIR = "/opt/dkube/output" +DATA_DIR = "/opt/dkube/input" BATCH_SIZE = int(os.getenv('BATCHSIZE', 10)) EPOCHS = int(os.getenv('EPOCHS', 1)) TF_MODEL_DIR = MODEL_DIR @@ -40,13 +38,13 @@ print ("TF_CONFIG: {}".format(os.getenv("TF_CONFIG", '{}'))) def count_epochs(iterator): - cluster_spec = json.loads(os.getenv('TF_CONFIG',None)) - role = cluster_spec['task'] - host = cluster_spec['cluster'][role['type']][role['index']] - if len(cluster_spec['cluster'].keys()) > 1: - sess = tf.Session('grpc://'+ host) - else: - sess = tf.Session() + sess = tf.Session() + if os.getenv('TF_CONFIG') is not None: + cluster_spec = json.loads(os.getenv('TF_CONFIG',None)) + role = cluster_spec['task'] + host = cluster_spec['cluster'][role['type']][role['index']] + if len(cluster_spec['cluster'].keys()) > 1: + sess = tf.Session('grpc://'+ host) global steps_epoch if not steps_epoch: while True: @@ -153,20 +151,21 @@ def model_fn(features, labels, mode, params): logging_hook = logger_hook({"loss": loss, "accuracy":accuracy[1] , "step" : tf.train.get_or_create_global_step(), "steps_epoch": steps_epoch, "mode":"eval"}, every_n_iter=summary_interval) return tf.estimator.EstimatorSpec( - mode=tf.estimator.ModeKeys.EVAL, - loss=loss, - eval_metric_ops={ - 'accuracy': - tf.metrics.accuracy( - labels=tf.argmax(labels, axis=1), - predictions=tf.argmax(logits, axis=1)), - }, + mode=tf.estimator.ModeKeys.EVAL, + loss=loss, + eval_metric_ops={ + 'accuracy': + tf.metrics.accuracy( + labels=tf.argmax(labels, axis=1), + predictions=tf.argmax(logits, axis=1)), + }, evaluation_hooks = [logging_hook]) def main(unused_argv): try: fp = open(os.getenv('DKUBE_JOB_HP_TUNING_INFO_FILE', 'None'),'r') hyperparams = json.loads(fp.read()) + hyperparams['num_epochs'] = EPOCHS except: hyperparams = { "learning_rate":1e-4, "batch_size":BATCH_SIZE, "num_epochs":EPOCHS } pass @@ -233,7 +232,7 @@ def eval_input_fn(): throttle_secs=1, start_delay_secs=1) tf.estimator.train_and_evaluate(mnist_classifier, train_spec, eval_spec) - if os.getenv('TF_CONFIG') != '': + if os.getenv('TF_CONFIG', '') != '': config = json.loads(os.getenv('TF_CONFIG')) if config['task']['type'] == 'master': mnist_classifier.export_savedmodel(MODEL_DIR, export_fn) @@ -263,4 +262,6 @@ def run(): tf.app.run(main=main) if __name__ == '__main__': + if os.getenv("STEPS") is None: + os.environ['STEPS'] = str(TF_TRAIN_STEPS) run() diff --git a/tensorflow/classification/mnist/digits/hptuning/tuning.json b/tensorflow/classification/mnist/digits/hptuning/tuning.json index 2bf38cd8..94ab0bf3 100644 --- a/tensorflow/classification/mnist/digits/hptuning/tuning.json +++ b/tensorflow/classification/mnist/digits/hptuning/tuning.json @@ -1,40 +1,31 @@ { - "RequestCount": 1, - "OptimizationType": "maximize", - "OptimizationGoal": 0.99, - "ObjectiveValueName": "train_accuracy_1", - "MetricsNames": [ - "train_accuracy_1" - ], - "ParameterConfigs": [ - { - "name": "--learning_rate", - "parametertype": "double", - "feasible": { - "max": "0.05", - "min": "0.01" + "parallelTrialCount": 3, + "maxTrialCount": 6, + "maxFailedTrialCount": 3, + "objective": { + "type": "maximize", + "goal": 0.99, + "objectiveMetricName": "accuracy" + }, + "algorithm": { + "algorithmName": "random" + }, + "parameters": [ + { + "name": "--learning_rate", + "parameterType": "double", + "feasibleSpace": { + "min": "0.01", + "max": "0.05" + } + }, + { + "name": "--batch_size", + "parameterType": "int", + "feasibleSpace": { + "min": "100", + "max": "200" + } } - }, - { - "name": "--batch_size", - "parametertype": "int", - "feasible": { - "max": "200", - "min": "100" - } - }, - { - "name": "--num_epochs", - "parametertype": "int", - "feasible": { - "max": "10", - "min": "1" - } - } - ], - "SuggestionSpec": { - "requestNumber": 3, - "suggestionAlgorithm": "random" - } + ] } - diff --git a/tensorflow/classification/mnist/digits/hptuning/tuning.yaml b/tensorflow/classification/mnist/digits/hptuning/tuning.yaml index 89095b20..da5fd7b1 100644 --- a/tensorflow/classification/mnist/digits/hptuning/tuning.yaml +++ b/tensorflow/classification/mnist/digits/hptuning/tuning.yaml @@ -1,25 +1,21 @@ -optimizationtype: maximize -objectivevaluename: train_accuracy_1 -optimizationgoal: 0.99 -requestcount: 1 -metricsnames: - - train_accuracy_1 -parameterconfigs: - - name: --learning_rate - parametertype: double - feasible: - min: "0.01" - max: "0.05" - - name: --batch_size - parametertype: int - feasible: - min: "100" - max: "200" - - name: --num_epochs - parametertype: int - feasible: - min: "1" - max: "10" -suggestionSpec: - suggestionAlgorithm: "random" - requestNumber: 3 +parallelTrialCount: 3 +maxTrialCount: 6 +maxFailedTrialCount: 3 +objective: + type: maximize + goal: 0.99 + objectiveMetricName: accuracy +algorithm: + algorithmName: random +parameters: + - name: --learning_rate + parameterType: double + feasibleSpace: + min: "0.01" + max: "0.05" + - name: --batch_size + parameterType: int + feasibleSpace: + min: "100" + max: "200" + diff --git a/tensorflow/classification/mnist/digits/pipeline/digits.ipynb b/tensorflow/classification/mnist/digits/pipeline/digits.ipynb index ad413a95..1fc9fed6 100644 --- a/tensorflow/classification/mnist/digits/pipeline/digits.ipynb +++ b/tensorflow/classification/mnist/digits/pipeline/digits.ipynb @@ -108,30 +108,42 @@ "def d3pipeline(\n", " #In notebook DKUBE_USER_ACCESS_TOKEN is automatically picked up from env variable\n", " auth_token = os.getenv(\"DKUBE_USER_ACCESS_TOKEN\"),\n", - " #By default tf v1.12 image is used here, v1.10, v1.11 or v1.13 can be used. \n", + " #By default tf v1.14 image is used here, v1.13 or v1.14 can be used. \n", " #Or any other custom image name can be supplied.\n", " #For custom private images, please input username/password\n", - " training_container=json.dumps({'image':'docker.io/ocdr/dkube-datascience-tf-gpu:v1.12', 'username':'', 'password': ''}),\n", + " training_container=json.dumps({'image':'docker.io/ocdr/d3-datascience-tf-cpu:v1.14', 'username':'', 'password': ''}),\n", " #Name of the workspace in dkube. Update accordingly if different name is used while creating a workspace in dkube.\n", " training_program=\"mnist\",\n", " #Script to run inside the training container \n", " training_script=\"python model.py\",\n", + " tuning = '{\"parallelTrialCount\":2,\"maxTrialCount\":4,\"maxFailedTrialCount\":2,\"objective\":{\"type\":\"maximize\",\"goal\":0.99,\"objectiveMetricName\":\"accuracy\"},\"algorithm\":{\"algorithmName\":\"random\"},\"parameters\":[{\"name\":\"--learning_rate\",\"parameterType\":\"double\",\"feasibleSpace\":{\"min\":\"0.01\",\"max\":\"0.05\"}},{\"name\":\"--batch_size\",\"parameterType\":\"int\",\"feasibleSpace\":{\"min\":\"100\",\"max\":\"200\"}}]}',\n", " #Input datasets for training. Update accordingly if different name is used while creating dataset in dkube. \n", " training_datasets=json.dumps([\"mnist\"]),\n", + " #Input dataset mount paths\n", + " training_input_dataset_mounts=json.dumps([\"/opt/dkube/input\"]),\n", + " #Output models for training.\n", + " training_outputs=json.dumps([\"mnist\"]),\n", + " #Output dataset mount paths\n", + " training_output_mounts=json.dumps([\"/opt/dkube/output\"]),\n", " #Request gpus as needed. Val 0 means no gpu, then training_container=docker.io/ocdr/dkube-datascience-tf-cpu:v1.12 \n", - " training_gpus=1,\n", + " training_gpus=0,\n", " #Any envs to be passed to the training program \n", " training_envs=json.dumps([{\"steps\": 100}]),\n", " #Device to be used for serving - dkube mnist example trained on gpu needs gpu for serving else set this param to 'cpu'\n", - " serving_device='gpu'):\n", + " serving_device='cpu',\n", + " serving_container=json.dumps({'image':'docker.io/ocdr/mnist-example-preprocess:2.0.4', 'username':'', 'password': ''})\n", + " ):\n", "\n", " train = dkube_training_op(auth_token, training_container,\n", " program=training_program, run_script=training_script,\n", - " datasets=training_datasets, ngpus=training_gpus,\n", - " envs=training_envs)\n", - " serving = dkube_serving_op(auth_token, train.outputs['artifact'], device=serving_device).after(train)\n", - " inference = dkube_viewer_op(auth_token, serving.outputs['servingurl'],\n", - " 'digits', viewtype='inference').after(serving)" + " datasets=training_datasets, outputs=training_outputs,\n", + " input_dataset_mounts=training_input_dataset_mounts,\n", + " output_mounts=training_output_mounts,\n", + " ngpus=training_gpus,\n", + " envs=training_envs, tuning=tuning)\n", + " serving = dkube_serving_op(auth_token, train.outputs['artifact'], device=serving_device, serving_container=serving_container).after(train)\n", + " #inference = dkube_viewer_op(auth_token, serving.outputs['servingurl'],\n", + " #'digits', viewtype='inference').after(serving)" ] }, { @@ -173,6 +185,13 @@ "source": [ "run = client.run_pipeline(mnist_experiment.id, 'mnist_classifier_pipeline', 'dkube_mnist_pl.tar.gz', params={})" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -191,7 +210,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tensorflow/classification/mnist/digits/pipeline/dkube_mnist_pl.tar.gz b/tensorflow/classification/mnist/digits/pipeline/dkube_mnist_pl.tar.gz index a93fdfb1..64de4be6 100644 Binary files a/tensorflow/classification/mnist/digits/pipeline/dkube_mnist_pl.tar.gz and b/tensorflow/classification/mnist/digits/pipeline/dkube_mnist_pl.tar.gz differ diff --git a/tensorflow/classification/resnet/catsdogs/classifier/data/tfhub_module.tar.gz b/tensorflow/classification/resnet/catsdogs/classifier/data/tfhub_module.tar.gz new file mode 100644 index 00000000..f0d5e8df Binary files /dev/null and b/tensorflow/classification/resnet/catsdogs/classifier/data/tfhub_module.tar.gz differ diff --git a/tensorflow/classification/resnet/catsdogs/classifier/program/model.py b/tensorflow/classification/resnet/catsdogs/classifier/program/model.py index c8cda0e0..80d1df0a 100644 --- a/tensorflow/classification/resnet/catsdogs/classifier/program/model.py +++ b/tensorflow/classification/resnet/catsdogs/classifier/program/model.py @@ -16,11 +16,9 @@ tf.logging.info('TF_CONFIG: {}'.format(os.environ["TF_CONFIG"])) FLAGS = None -DATA_DIR = os.getenv('DKUBE_INPUT_DATASETS', None) -if DATA_DIR is not None: - DATA_DIR = DATA_DIR.split(",")[0] -MODEL_DIR = os.getenv('DKUBE_JOB_OUTPUT_S3', None) -TFHUB_CACHE_DIR = os.getenv('TFHUB_CACHE_DIR',None) +DATA_DIR = "/opt/dkube/input" +MODEL_DIR = "/opt/dkube/output" +TFHUB_CACHE_DIR = os.getenv('TFHUB_CACHE_DIR', "/opt/dkube/input") BATCH_SIZE = int(os.getenv('BATCHSIZE', 10)) EPOCHS = int(os.getenv('EPOCHS', 1)) TF_TRAIN_STEPS = int(os.getenv('STEPS',1000)) @@ -32,6 +30,8 @@ # os.makedirs(MODEL_DIR) def count_epochs(iterator): + if os.getenv('TF_CONFIG', None) == None: + return cluster_spec = json.loads(os.getenv('TF_CONFIG',None)) role = cluster_spec['task'] host = cluster_spec['cluster'][role['type']][role['index']] @@ -127,6 +127,7 @@ def train(_): try: fp = open(os.getenv('DKUBE_JOB_HP_TUNING_INFO_FILE', 'None'),'r') hyperparams = json.loads(fp.read()) + hyperparams['num_epochs'] = EPOCHS except: hyperparams = { "learning_rate":1e-3, "batch_size":BATCH_SIZE, "num_epochs":EPOCHS } pass @@ -158,11 +159,13 @@ def train(_): } global TFHUB_CACHE_DIR if TFHUB_CACHE_DIR != None: + EXTRACT_PATH = "/tmp/tfhub-cache-dir" files = [os.path.join(TFHUB_CACHE_DIR, f) for f in tf.gfile.ListDirectory(TFHUB_CACHE_DIR) if f.endswith('tar.gz')] for fname in files: tar = tarfile.open(fname, "r:gz") - tar.extractall(TFHUB_CACHE_DIR) + tar.extractall(EXTRACT_PATH) tar.close() + TFHUB_CACHE_DIR = EXTRACT_PATH else: TFHUB_CACHE_DIR = params['module_spec'] @@ -195,7 +198,7 @@ def serving_input_receiver_fn(): fn = lambda image: _img_string_to_tensor(image, input_img_size) features['inputs'] = tf.map_fn(fn, features['inputs'], dtype=tf.float32) return tf.estimator.export.ServingInputReceiver(features, received_tensors) - if os.getenv('TF_CONFIG') != '': + if os.getenv('TF_CONFIG', '') != '': config = json.loads(os.getenv('TF_CONFIG')) if config['task']['type'] == 'master': classifier.export_savedmodel(MODEL_DIR, serving_input_receiver_fn) @@ -214,4 +217,6 @@ def run(): tf.app.run(main=train) if __name__ == '__main__': + if os.getenv("STEPS") is None: + os.environ['STEPS'] = str(TF_TRAIN_STEPS) run() diff --git a/tensorflow/classification/resnet/catsdogs/hptuning/tuning.json b/tensorflow/classification/resnet/catsdogs/hptuning/tuning.json index 3e855bf4..94ab0bf3 100644 --- a/tensorflow/classification/resnet/catsdogs/hptuning/tuning.json +++ b/tensorflow/classification/resnet/catsdogs/hptuning/tuning.json @@ -1,40 +1,31 @@ { - "RequestCount": 1, - "OptimizationType": "minimize", - "OptimizationGoal": 0.01, - "ObjectiveValueName": "loss", - "MetricsNames": [ - "loss" - ], - "ParameterConfigs": [ - { - "name": "--learning_rate", - "parametertype": "double", - "feasible": { - "max": "0.05", - "min": "0.01" + "parallelTrialCount": 3, + "maxTrialCount": 6, + "maxFailedTrialCount": 3, + "objective": { + "type": "maximize", + "goal": 0.99, + "objectiveMetricName": "accuracy" + }, + "algorithm": { + "algorithmName": "random" + }, + "parameters": [ + { + "name": "--learning_rate", + "parameterType": "double", + "feasibleSpace": { + "min": "0.01", + "max": "0.05" + } + }, + { + "name": "--batch_size", + "parameterType": "int", + "feasibleSpace": { + "min": "100", + "max": "200" + } } - }, - { - "name": "--batch_size", - "parametertype": "int", - "feasible": { - "max": "200", - "min": "100" - } - }, - { - "name": "--num_epochs", - "parametertype": "int", - "feasible": { - "max": "10", - "min": "1" - } - } - ], - "SuggestionSpec": { - "requestNumber": 3, - "suggestionAlgorithm": "random" - } + ] } - diff --git a/tensorflow/classification/resnet/catsdogs/hptuning/tuning.yaml b/tensorflow/classification/resnet/catsdogs/hptuning/tuning.yaml index bc0d7cb8..da5fd7b1 100644 --- a/tensorflow/classification/resnet/catsdogs/hptuning/tuning.yaml +++ b/tensorflow/classification/resnet/catsdogs/hptuning/tuning.yaml @@ -1,25 +1,21 @@ -optimizationtype: minimize -objectivevaluename: loss -optimizationgoal: 0.01 -requestcount: 1 -metricsnames: - - loss -parameterconfigs: - - name: --learning_rate - parametertype: double - feasible: - min: "0.01" - max: "0.05" - - name: --batch_size - parametertype: int - feasible: - min: "100" - max: "200" - - name: --num_epochs - parametertype: int - feasible: - min: "1" - max: "10" -suggestionSpec: - suggestionAlgorithm: "random" - requestNumber: 3 +parallelTrialCount: 3 +maxTrialCount: 6 +maxFailedTrialCount: 3 +objective: + type: maximize + goal: 0.99 + objectiveMetricName: accuracy +algorithm: + algorithmName: random +parameters: + - name: --learning_rate + parameterType: double + feasibleSpace: + min: "0.01" + max: "0.05" + - name: --batch_size + parameterType: int + feasibleSpace: + min: "100" + max: "200" + diff --git a/tensorflow/classification/resnet/catsdogs/pipeline/catsdogs.ipynb b/tensorflow/classification/resnet/catsdogs/pipeline/catsdogs.ipynb index 8a1299e0..c23860da 100644 --- a/tensorflow/classification/resnet/catsdogs/pipeline/catsdogs.ipynb +++ b/tensorflow/classification/resnet/catsdogs/pipeline/catsdogs.ipynb @@ -108,28 +108,39 @@ "def d3pipeline(\n", " #In notebook DKUBE_USER_ACCESS_TOKEN is automatically picked up from env variable\n", " auth_token = os.getenv(\"DKUBE_USER_ACCESS_TOKEN\"),\n", - " #By default tf v1.12 image is used here, v1.10, v1.11 or v1.13 can be used. \n", + " #By default tf v1.14 image is used here, v1.13 or v1.14 can be used. \n", " #Or any other custom image name can be supplied.\n", " #For custom private images, please input username/password\n", - " training_container=json.dumps({'image':'docker.io/ocdr/dkube-datascience-tf-gpu:v1.12', 'username':'', 'password': ''}),\n", + " training_container=json.dumps({'image':'docker.io/ocdr/d3-datascience-tf-cpu:v1.14', 'username':'', 'password': ''}),\n", " #Name of the workspace in dkube. Update accordingly if different name is used while creating a workspace in dkube.\n", " training_program=\"catsdogs\",\n", " #Script to run inside the training container\n", " training_script=\"python model.py\",\n", " #Input datasets for training. Update accordingly if different name is used while creating dataset in dkube. \n", " training_datasets=json.dumps([\"catsdogs\"]),\n", + " #Input dataset mount paths\n", + " training_input_dataset_mounts=json.dumps([\"/opt/dkube/input\"]),\n", + " #Output models for training.\n", + " training_outputs=json.dumps([\"catsdogs\"]),\n", + " #Output dataset mount paths\n", + " training_output_mounts=json.dumps([\"/opt/dkube/output\"]),\n", " #Request gpus as needed. Val 0 means no gpu, then training_container=docker.io/ocdr/dkube-datascience-tf-cpu:v1.12.\n", - " training_gpus=1,\n", + " training_gpus=0,\n", " #Any envs to be passed to the training program\n", - " training_envs=json.dumps([{\"steps\": 100}])):\n", + " training_envs=json.dumps([{\"steps\": 100}]),\n", + " serving_container=json.dumps({'image':'docker.io/ocdr/catdogs-example-preprocess:2.0.4', 'username':'', 'password': ''})\n", + "):\n", "\n", " train = dkube_training_op(auth_token, training_container,\n", " program=training_program, run_script=training_script,\n", - " datasets=training_datasets, ngpus=training_gpus,\n", + " datasets=training_datasets, outputs=training_outputs,\n", + " input_dataset_mounts=training_input_dataset_mounts,\n", + " output_mounts=training_output_mounts,\n", + " ngpus=training_gpus,\n", " envs=training_envs)\n", - " serving = dkube_serving_op(auth_token, train.outputs['artifact']).after(train)\n", - " inference = dkube_viewer_op(auth_token, serving.outputs['servingurl'],\n", - " 'catsdogs', viewtype='inference').after(serving)" + " serving = dkube_serving_op(auth_token, train.outputs['artifact'], serving_container=serving_container).after(train)\n", + " #inference = dkube_viewer_op(auth_token, serving.outputs['servingurl'],\n", + " #'catsdogs', viewtype='inference').after(serving)" ] }, { @@ -189,7 +200,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tensorflow/classification/resnet/catsdogs/pipeline/dkube_resnetv2_pl.tar.gz b/tensorflow/classification/resnet/catsdogs/pipeline/dkube_resnetv2_pl.tar.gz index 8ab5868e..7bb0b0a9 100644 Binary files a/tensorflow/classification/resnet/catsdogs/pipeline/dkube_resnetv2_pl.tar.gz and b/tensorflow/classification/resnet/catsdogs/pipeline/dkube_resnetv2_pl.tar.gz differ diff --git a/tensorflow/classification/resnet/catsdogs/serving/Dockerfile b/tensorflow/classification/resnet/catsdogs/serving/Dockerfile new file mode 100644 index 00000000..2adedfba --- /dev/null +++ b/tensorflow/classification/resnet/catsdogs/serving/Dockerfile @@ -0,0 +1,3 @@ +FROM ocdr/dkube-tf-serving:2.0.7 +RUN pip3 install -r requirements.txt +COPY transformer.py /usermodule/ diff --git a/tensorflow/classification/resnet/catsdogs/serving/README.md b/tensorflow/classification/resnet/catsdogs/serving/README.md new file mode 100644 index 00000000..be85eb7c --- /dev/null +++ b/tensorflow/classification/resnet/catsdogs/serving/README.md @@ -0,0 +1,44 @@ +README explains how to write a code & build image for transformer stage of serving of model trained by the below DKube example. + +*https://github.com/oneconvergence/dkube-examples/tree/2.0.7/tensorflow/classification/resnet/catsdogs/classifier* + +Why is this required? +==================== +Transfomer stage is necessary when there is certain preprocessing involved on the inputs before calling the predict method which will pass the processed input to model for inference. + +How to write & build transformer +================================ +Implement a transformer.py and place in this project directory. +The below function is mandatory to be implemented. + +*def preprocess(inputs: Dict) -> Dict:* + +Please see the sample *transformer.py* + +Update the python package requirements in *requirements.txt* + +Build the container using - docker build -t . + +Push in the container registry and make the image **public** + +NOTE - Its very important to return *token* in the returned response. See below line in the example. + +*res = {"signature_name":in_signature,"examples":rqst_list,"token":inputs['token']}* + +Use in DKube +============ +Use the transformer image in *Test Inference* functionality of DKube. +Supply the image built as input while deploying the model for inference. + + +How to test +=========== +This package shows the *transformer.py* for DKube cats-dogs example which is built on resnetv2 model. +This package has an example image file which can be used with the below *curl* command. + +*example/cat.png* - Actual cat image to be used for inference testing +*example/image.json* - JSON input expected by *transformer.py* - Image is sent as B64 encoded bytes (standard way) + - Please update the *serving_url* and *token* fields in *image.json* + + +curl -v --insecure -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc0YmNkZjBmZWJmNDRiOGRhZGQxZWIyOGM2MjhkYWYxIn0.eyJ1c2VybmFtZSI6Im9jZGt1YmUiLCJyb2xlIjoib3BlcmF0b3IiLCJleHAiOjQ4MzI1NTA3MTAsImlhdCI6MTU5MjU1MDcxMCwiaXNzIjoiREt1YmUifQ.IADw32O4Y5eX_PxYB7vXIU583U0XsEMovETOvI3_xrg26JACKr7CpquUem-OxZWqcB-aplfyJmiKKcQkw4llX_gheBM6LFkj0IDKNmoG7BhYg2g7rko90b8D2-O8aLkZw9XoiRHoSFW5ZJ5HzVSWaqFeRe-vZpNUYbHJpNX3C8eLWkAJlUAJ-H-jFmnqw5vMyUJedwXFcZgHM_XiCNUNOR5hOkcbRq16lH10uwifLFHFp9fQT9y9kKaTcZYEkpAwtXX272tIk3JegcsKFCS58zC1jsOWH6UF7t-4DX-sOLyG83tXTywWAzeq9hwLQZUFjhCa2J2KKZSEcmLS7TO35A" https://34.72.97.235:32222/dkube/inf/v1/models/d3-inf-job1-ocdkube:predict -d @image.json diff --git a/tensorflow/classification/resnet/catsdogs/serving/example/cat.png b/tensorflow/classification/resnet/catsdogs/serving/example/cat.png new file mode 100644 index 00000000..b180076d Binary files /dev/null and b/tensorflow/classification/resnet/catsdogs/serving/example/cat.png differ diff --git a/tensorflow/classification/resnet/catsdogs/serving/example/image.json b/tensorflow/classification/resnet/catsdogs/serving/example/image.json new file mode 100644 index 00000000..470d4237 --- /dev/null +++ b/tensorflow/classification/resnet/catsdogs/serving/example/image.json @@ -0,0 +1,20 @@ +{ + "signatures": { + "inputs": [ + [{ + "key": "inputs", + "formatter": { + "label": "file2b64", + "custom": "false", + "script": "", + "command": "" + }, + "data": "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" + }] + ], + "name": "serving_default" + }, + "instances": [], + "serving_url": "https://dkube-proxy.dkube.svc.cluster.local:443/dkube/inf/v1/models/d3-inf-ffd4ac7d-f083-4a87-9ffd-818f2a458313:classify", + "token": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc0YmNkZjBmZWJmNDRiOGRhZGQxZWIyOGM2MjhkYWYxIn0.eyJ1c2VybmFtZSI6Im9jZGt1YmUiLCJyb2xlIjoib3BlcmF0b3IiLCJleHAiOjQ4MzI4NDEwNDcsImlhdCI6MTU5Mjg0MTA0NywiaXNzIjoiREt1YmUifQ.bLWamNYkBPwRGjZuXKAWSLefqAw9Y36fDWfRYnNpBk2lcRmtnvXtifoXjw2OEruWV1Lq5aAkgKlM4ABPDsB7ub7nokmxhbcy8OR2eBE26BG8jfsAogI9gxY9YiHoTPikNlrfn_O04pCBQFmb-R7hRNk9KKSESsg34Fr3OtVpViW_tx1KRSEqbxXUuLHIUE6jSJ0Xj0RXdyN1SADxeXEbQq2aaeS8jxjpHAIZU6o0wiasK0Fr1Iz0yh309jqYPj4OqirVg-4zRh5d7RgoSSdyovPsLROhYdh399DmgdnczAHzEGKEuia0QYt_fR9dKyw7wy6A3YYGB7MTm6iH2hcYgA" +} diff --git a/tensorflow/classification/resnet/catsdogs/serving/requirements.txt b/tensorflow/classification/resnet/catsdogs/serving/requirements.txt new file mode 100644 index 00000000..3868fb16 --- /dev/null +++ b/tensorflow/classification/resnet/catsdogs/serving/requirements.txt @@ -0,0 +1 @@ +pillow diff --git a/tensorflow/classification/resnet/catsdogs/serving/transformer.py b/tensorflow/classification/resnet/catsdogs/serving/transformer.py new file mode 100644 index 00000000..92ef76e2 --- /dev/null +++ b/tensorflow/classification/resnet/catsdogs/serving/transformer.py @@ -0,0 +1,82 @@ +from typing import List, Dict +from PIL import Image +import logging +import io +import numpy as np +import base64 + +import sys,json +import requests +import os +import logging + +def convert(input_file): + try: + f = open(input_file, "rb").read() + data = base64.encodestring(f) + data = data.decode('utf-8') + except Exception as err: + msg = "Failed to convert input image. " + str(err) + logging.error(msg) + return "", msg + return data, "" + + +def cleanup(input_file, convertor_file): + if input_file != None and os.path.exists(input_file): + os.remove(input_file) + if convertor_file != None and os.path.exists(base_dir + convertor_file + ".py"): + os.remove(base_dir + convertor_file + ".py") + + +def b64_filewriter(filename, content): + string = content.encode('utf8') + b64_decode = base64.decodebytes(string) + fp = open(filename, "wb") + fp.write(b64_decode) + fp.close() + + +def preprocess(inputs: Dict) -> Dict: + #return {'instances': [image_transform(instance) for instance in inputs['instances']]} + del inputs['instances'] + try: + json_data = inputs + except ValueError: + return json.dumps({ "error": "Recieved invalid json" }) + try: + model_inputs = model_method = script = None + input_file = "" + input_type_mapping = {} + in_signature = json_data["signatures"]["name"] + in_data = json_data["signatures"]["inputs"] + rqst_list = [] + batch_list = {} + for batch in in_data: + for element in batch: + batch_list.clear() + key = "inputs" + data = element["data"] + if data: + input_file = "input_" +key + b64_filewriter(input_file, data) + output, err = convert(input_file) + if err != "": + return json.dumps({ "error": err }) + if output == '' or output == b'': + cleanup(input_file, script) + return json.dumps({ "error": "Script did not execute successfuly" }) + if isinstance(output, (bytes, bytearray)): + output = output.decode('utf-8') + batch_list[key] = { "b64": output} + cleanup(input_file, script) + rqst_list.append(batch_list) + res = {"signature_name":in_signature,"examples":rqst_list,"token":inputs['token']} + logging.info("req: %s", str(res)) + return res + except Exception as e: + logging.info("exception: %s",str(e)) + return "error" + +def postprocess(inputs: List) -> List: + return inputs diff --git a/tensorflow/object-detection/pets/README.md b/tensorflow/object-detection/pets/README.md index 3e1e8f3a..163e277d 100644 --- a/tensorflow/object-detection/pets/README.md +++ b/tensorflow/object-detection/pets/README.md @@ -13,130 +13,158 @@ This example is derived from [tensorflow object detection example](https://githu # How to Preprocess Data Tensorflow object detection API expects the input dataset to be in TFRecord format. But the pet dataset available is in .jpg and .xml format. We need some preprocessing to convert this into TFRecord format. -## Step1: Create a workspace - 1. Click *Workspaces* side menu option. - 2. Click *+Workspace* button. - 3. Select *Github* option. - 4. Enter a unique name say *pets-detector-preprocessing* +## Step 1: Create a Project + 1. Click *Repos* in side menu under *WORKFLOW* section. + 2. Click *+Project* button under *Projects* section. + 3. Enter a unique name say *pets-detector-preprocessing* . + 4. Select *Project Source* as *Git*. 5. Paste link *[https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/preprocessing ](https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/preprocessing)* in the URL text box. - 6. Click *Add Workspace* button. - 7. Workspace will be created and imported in Dkube. Progress of import can be seen. - 8. Please wait till status turns to *ready*. -## Step2: Download the Dataset + 6. Click *Add Project* button. + 7. Enter branch name in *Branch* text-box. + 8. Project will be created and imported in Dkube. Progress of import can be seen. + 9. Please wait till status turns to *ready*. +## Step 2. Create Download Dataset DVS +This step is to create a dvs dataset which will hold the downloaded dataset. This will act as the output dataset for download preprocess run. + 1. Click *Repos* in side menu under *WORKFLOW* section. + 2. Click *+Dataset* button under *Datasets*. + 3. Enter a unique name say *pets-download-dataset* . + 4. Select *Dataset Source* as *None*. + 5. Click *Add Dataset* button. + 6. Dataset will be created in Dkube. + 7. Please wait till status turns to *ready*. +## Step 3: Download the Dataset This step will download the images.tar.gz and annotations.tar.gz for Oxford IIT Pets dataset and create a dataset named "pets" in dkube. -1. Click *Jobs* side menu option. - 2. Click *+Data Preprocessing* button. - 3. Fill the fields in Job form and click *Submit* button. Toggle *Expand All* button to auto expand the form. See below for sample values to be given in the form, for advanced usage please refer to **Dkube User Guide**. - - Enter a unique name say *download-pets-dataset* - - Enter target dataset name for data being downloaded by the job say *pets*. - - **Container** section - - - Docker Image URL : docker.io/ocdr/dkube-datascience-preprocess:1.1 - - Private : If image is private, select private and provide dockerhub username and password - - Start-up script : `python download.py` - - **Parameters** section - Leave it to default. - - **Workspace** section - Please select the workspace *pet-detector-preprocessing* created in *Step1*. + 1. Click *Runs* side menu under *WORKFLOW* section. + 2. Click *+Run* and select *Preprocessing* button. + 3. Fill the fields in Job form and click *Submit* button. See below for sample values to be given in the form, for advanced usage please refer to **Dkube User Guide**. + - **Basic** tab + - Enter a unique name say *download-pets-dataset* + - Docker Image URL : docker.io/ocdr/dkube-datascience-preprocess:1.2 + - Private : If image is private, select private and provide dockerhub username and password + - Start-up script : `python download.py` + - Click *Next*. + - **Repos** tab + - *Inputs* section + - Project: Click on **+** button and select *pets-detector-preprocessing* project. + - *Outputs* section + - Dataset: Click on **+** button and select *pets-download-dataset*. + - Mount path: Enter path say */opt/dkube/output*. + - Click *Next*. 4. Click *Submit* button. -5. A new entry with name *download-pets-dataset* will be created in *Data Preprocessing* table. -6. Check the *Status* field for lifecycle of job, wait till it shows *complete*. +5. Check the *Status* field for lifecycle of Preprocessing run under *All Runs* section, wait till it shows *complete*. -## Step3: Preprocess Data (Conversion to TFRecord format) +## Step 4. Create TF-record Dataset DVS +This step is to prepare a *DVS* dataset for storing output of preprocess job which will convert downloaded dataset into *tf-record*. + 1. Click *Repos* in side menu under *WORKFLOW* section. + 2. Click *+Dataset* button under *Datasets*. + 3. Enter a unique name say *preprocess-pets-dataset* . + 4. Select *Dataset Source* as *None*. + 5. Click *Add Dataset* button. + 6. Dataset will be created in Dkube. + 7. Please wait till status turns to *ready*. + +## Step 5: Preprocess Data (Conversion to TFRecord format) This step converts the downloaded dataset to TFRecords, the format expected by tensorflow object detection API. -1. Click *Jobs* side menu option. - 2. Click *+Data Preprocessing* button. - 3. Fill the fields in Job form and click *Submit* button. Toggle *Expand All* button to auto expand the form. See below for sample values to be given in the form, for advanced usage please refer to **Dkube User Guide**. - - Enter a unique name say *preprocess-pets-dataset* - - Enter target dataset name for the preprocessed data say *tf-records*. - - **Container** section - - - Docker Image URL : docker.io/ocdr/dkube-datascience-preprocess:1.1 - - Private : If image is private, select private and provide dockerhub username and password - - Start-up script : `python extract.py; python create_pet_tf_record.py --data_dir=/tmp/dataset/ --output_dir=$DKUBE_JOB_OUTPUT_SHAREDDIR --label_map_path=pet_label_map.pbtxt` - - **Parameters** section - Leave it to default. - - **Workspace** section - Please select the workspace *pet-detector-preprocessing* created in *Step1*. - - **Dataset** section - Please select the dataset *pets* created in *Step2*. + 1. Click *Runs* side menu under *WORKFLOW* section. + 2. Click *+Run* and select *Preprocessing* button. + 3. Fill the fields in Job form and click *Submit* button. See below for sample values to be given in the form, for advanced usage please refer to **Dkube User Guide**. + - **Basic** tab + - Enter a unique name say *preprocess-pets-dataset* + - Docker Image URL : docker.io/ocdr/dkube-datascience-preprocess:1.2 + - Private : If image is private, select private and provide dockerhub username and password + - Start-up script : `python extract.py; python create_pet_tf_record.py --label_map_path=pet_label_map.pbtxt` + - Click *Next*. + - **Repos** tab + - *Inputs* section + - Project: Click on **+** button and select *pets-detector-preprocessing* project. + - Dataset: Click on **+** button and select *pets-download-dataset*. + - Mount path: Enter mount path say */opt/dkube/input* + - *Outputs* section + - Dataset: Click on **+** button and select *preprocess-pets-dataset*. + - Enter mount path: Enter path say */opt/dkube/output*. + - Click *Next*. 4. Click *Submit* button. -5. A new entry with name *preprocess-pets-dataset* will be created in *Data Preprocessing* table. -6. Check the *Status* field for lifecycle of job, wait till it shows *complete*. +5. Check the *Status* field for lifecycle of Preprocessing run under *All Runs* section, wait till it shows *complete*. + # How to Train -## Step1: Create a workspace +## Step 1: Create a Project - 1. Click *Workspaces* side menu option. - 2. Click *+Workspace* button. - 3. Select *Github* option. - 4. Enter a unique name say *pet-detector* - 5. Paste link *[https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training - ](https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training )* in the URL text box. - 6. Click *Add Workspace* button. - 7. Workspace will be created and imported in Dkube. Progress of import can be seen. - 8. Please wait till status turns to *ready*. +1. Click *Repos* in side menu under *WORKFLOW* section. + 2. Click *+Project* button under *Projects* section. + 3. Enter a unique name say *pets-detector-training* . + 4. Select *Project Source* as *Git*. + 5. Paste link *[https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training](https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training)* in the URL text box. + 6. Click *Add Project* button. + 7. Enter branch name in *Branch* text-box. + 8. Project will be created and imported in Dkube. Progress of import can be seen. + 9. Please wait till status turns to *ready*. -## Step2: Add model for transfer learning - 1. Click *Models* side menu option. - 2. Click *+Model* button. - 3. Select *Other* option. - 4. Enter a unique name say *faster-rcnn* +## Step 2: Add model for transfer learning +This step will download *faster-rcnn* object detection model which we will use to perform transfer learning. + 1. Click *Repos* in side menu under *WORKFLOW* section. + 2. Click *+Model* button under *Models*. + 3. Enter a unique name say *faster-rcnn* . + 4. Select *Model Source* as *Other*. 5. Paste link *[http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz](http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz)* in the URL text box. - 6. Click *Add Model* button. - 7. Model will be created and imported in Dkube. Progress of import can be seen. - 8. Please wait till status turns to *ready*. -## Step3: Start a training job - 1. Click *Jobs* side menu option. - 2. Click *+Training Job* button. - 3. Fill the fields in Job form and click *Submit* button. Toggle *Expand All* button to auto expand the form. See below for sample values to be given in the form, for advanced usage please refer to **Dkube User Guide**. - - Enter a unique name say *pet-detector* - - **Container** section - - Tensorflow Version - Leave with default options selected. - - Startup script - `bash process.sh; python model_main.py --model_dir=$DKUBE_JOB_OUTPUT_S3` - - **GPUs** section - Provide the required number of GPUs. This field is optional, if not provided network will train on CPU. - - **Parameters** section - - Select the pipeline.config file which is stored locally(Download from https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training/pipeline.config) - - Set the number of steps - - **Workspace** section - Please select the workspace *pet-detector* created in *Step1(How to train)*. - - **Model** section - Please select the workspace *faster-rcnn* created in *Step2(How to train)*. - - **Dataset** section - Please select the dataset *tf-records* created in *Step3(How to Preprocess Data)*. -4. Click *Submit* button. -5. A new entry with name *pet-detector* will be created in *Jobs* table. -6. Check the *Status* field for lifecycle of job, wait till it shows *complete*. + 6. Select extract uploaded file checkbox. + 7. Click *Add Model* button. + 8. Model will be created in Dkube. + 9. Please wait till status turns to *ready*. -# How to Serve +## Step 3. Create Output Model DVS +This step is to create a dvs model which will hold the trained output model. + 1. Click *Repos* in side menu under *WORKFLOW* section. + 2. Click *+Model* button under *Models*. + 3. Enter a unique name say *pets-detector* . + 4. Select *Model Source* as *None*. + 5. Click *Add Model* button. + 6. Model will be created in Dkube. + 7. Please wait till status turns to *ready*. - 1. After the job is *complete* from above step. The trained model will get generated inside *Dkube*. Link to which is reflected in the *Model* field of a job in *Job* table. - 2. Click the link to see the trained model details. - 3. Click the *Deploy* button to deploy the trained model for serving. A form will display. - 4. Input the unique name say *pet-detector-serving* - 5. Select *CPU* or *GPU* to deploy model on specific device. Unless specifically required, model can be served on CPU. - 6. Click *Deploy* button. - 7. Click *Inferences* side menu and check that a serving job is created with the name given i.e, *digits-serving*. - 8. Wait till *status* field shows *running*. - 9. Copy the *URL* shown in *Endpoint* field of the serving job. +## Step 4: Start a training job + 1. Click *Runs* side menu under *WORKFLOW* section. + 2. Click *+Run* and select *Training* button. + 3. Fill the fields in Job form and click *Submit* button. See below for sample values to be given in the form, for advanced usage please refer to **Dkube User Guide**. + - **Basic** tab + - Enter a unique name say *training-pets-detector* + - Start-up script : `bash process.sh; python model_main.py` + - Click *Next*. + - **Repos** tab + - *Inputs* section + - Project: Click on **+** button and select *pets-detector-training* project. + - Dataset: Click on **+** button and select *preprocess-pets-dataset*. + - Mount path: Enter mount path say */opt/dkube/input/dataset* + - Models: Click on **+** button and select *faster-rcnn*. + - Mount path: Enter mount path say */opt/dkube/input/model* + - *Outputs* section + - Models: Click on **+** button and select *pets-detector*. + - Enter mount path: Enter path say */opt/dkube/output*. + - Click *Next*. + - *Configuration* section + - Parameters upload configuration: Select the pipeline.config file which is stored locally(Download from [https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training/pipeline.config](https://github.com/oneconvergence/dkube-examples/tree/master/tensorflow/object-detection/pets/program/training/pipeline.config)) +4. Click *Submit* button. +5. Check the *Status* field for lifecycle of Training run under *All Runs* section, wait till it shows *complete*. +# How to Serve +After the job is *complete* from above step. The trained model will get generated inside *Dkube*. + 1. Click on the training run named *training-pets-detector*. + 2. Select *Lineage* tab beside the *Summary* details page. + 3. Click on *OUTPUTS* model *pets-detector*. + 4. Click on *Test Inference* button. + 5. Enter a meaningful name for inference job say *pets-inference*. + 6. Select CPU/GPU. A button named *Test Inference* appear. + 7. Click on *Test inference* button. + 8. Click *Test Inferences* in side menu under *WORKFLOW* section. + 9. Wait till *status* field shows *running*. + 10. Copy the *URL* shown in *Endpoint* field of the serving job. + # How to test Inference -1. To test inference **dkubectl** binary is needed. -2. Please use *dkube-notebook* for testing inference. -3. Create a file *pet-detector.ini* with below contents, Only field to be filled in is *modelurl*. Paste the *URL* copied in previous step. - - ``` - [INFAPP] - #Name of the program to run, choices are - digits,catsdogs,cifar,objdetect,bolts - program="objdetect" - #Serving URL of the model in dkube - modelurl="" - #Container image to be used for inference app - #image="" - #IP to make inference app available on - #accessip="" - - ################################################################################################################ - # Following fields need not be filled in when launching inference application from inside dkube notebook # - ################################################################################################################ - #Name of the dkube user - #user="" - #Path to the kubeconfig of the cluster ex: "~/.dkube/kubeconfig" - #kubeconfig="" - ``` - 4. Execute the command `dkubectl infapp launch --config pet-detector.ini -n pet-detector` - 5. The above command will output a URL, please click the URL to see the UI which can be used for testing inference. - 6. Upload an image for inference, images in **inference** folder can be used. - 7. Upload the labe map file in the file upload section. The pet_label_map.pbtxt file in **inference** cab be used. - 8. Set the Number of classes to 37 - 7. Click *Detect* button and the image is displayed with detection boxes returned by the model. +1. To test inference open a new tab with link < DKUBE_URL:port/inference > +2. Paste the *URL* shown in *Endpoint* field of the serving job. +3. Copy and Paste *Dkube OAuth token* from *Developer Settings* present in menu on top right to *Authorization Token* present in Inference page. +4. Select Model type as *objdetect*. +5. Set the Number of classes to 37. +6. Upload an image for inference, images in **inference** folder can be used. +7. Upload the labe map file in the file upload section. The pet_label_map.pbtxt file in **inference** cab be used. +8. Click _Predict_ button and the image is displayed with detection boxes returned by the model. diff --git a/tensorflow/object-detection/pets/program/preprocessing/create_pet_tf_record.py b/tensorflow/object-detection/pets/program/preprocessing/create_pet_tf_record.py index 9b3b55c6..bc0a3738 100644 --- a/tensorflow/object-detection/pets/program/preprocessing/create_pet_tf_record.py +++ b/tensorflow/object-detection/pets/program/preprocessing/create_pet_tf_record.py @@ -44,8 +44,8 @@ from object_detection.utils import label_map_util flags = tf.app.flags -flags.DEFINE_string('data_dir', '', 'Root directory to raw pet dataset.') -flags.DEFINE_string('output_dir', '', 'Path to directory to output TFRecords.') +flags.DEFINE_string('data_dir', '/tmp/dataset/', 'Root directory to raw pet dataset.') +flags.DEFINE_string('output_dir', '/opt/dkube/output', 'Path to directory to output TFRecords.') flags.DEFINE_string('label_map_path', 'data/pet_label_map.pbtxt', 'Path to label map proto') flags.DEFINE_boolean('faces_only', True, 'If True, generates bounding boxes ' diff --git a/tensorflow/object-detection/pets/program/preprocessing/download.py b/tensorflow/object-detection/pets/program/preprocessing/download.py index 0ed4bfe8..ee3bf612 100644 --- a/tensorflow/object-detection/pets/program/preprocessing/download.py +++ b/tensorflow/object-detection/pets/program/preprocessing/download.py @@ -3,7 +3,7 @@ import tarfile import shutil -OUTPUT_DIR = os.getenv('DKUBE_JOB_OUTPUT_SHAREDDIR', None) +OUTPUT_DIR = "/opt/dkube/output" def download(): opener = urllib.request.URLopener() diff --git a/tensorflow/object-detection/pets/program/preprocessing/extract.py b/tensorflow/object-detection/pets/program/preprocessing/extract.py index 88ec34af..324dd84a 100644 --- a/tensorflow/object-detection/pets/program/preprocessing/extract.py +++ b/tensorflow/object-detection/pets/program/preprocessing/extract.py @@ -1,9 +1,7 @@ import os import tarfile -DATA_DIR = os.getenv('DKUBE_INPUT_DATASETS', None) -if DATA_DIR is not None: - DATA_DIR = DATA_DIR.split(",")[0] +DATA_DIR = "/opt/dkube/input" target_dir = '/tmp/dataset/' def extract(): diff --git a/tensorflow/object-detection/pets/program/training/model_main.py b/tensorflow/object-detection/pets/program/training/model_main.py index 5abdd3fc..9c0cbe5f 100644 --- a/tensorflow/object-detection/pets/program/training/model_main.py +++ b/tensorflow/object-detection/pets/program/training/model_main.py @@ -31,7 +31,7 @@ from google.protobuf import text_format flags.DEFINE_string( - 'model_dir', None, 'Path to output model directory ' + 'model_dir', '/opt/dkube/output', 'Path to output model directory ' 'where event and checkpoint files will be written.') flags.DEFINE_string('pipeline_config_path', '/tmp/config_file/pipeline.config', 'Path to pipeline config ' 'file.') diff --git a/tensorflow/object-detection/pets/program/training/process.sh b/tensorflow/object-detection/pets/program/training/process.sh index a5675e07..fb179e6c 100644 --- a/tensorflow/object-detection/pets/program/training/process.sh +++ b/tensorflow/object-detection/pets/program/training/process.sh @@ -17,11 +17,9 @@ fi echo "Config file path : $CONFIG_FILE" -IFS=',' read -ra DATASETS <<< "$DKUBE_INPUT_DATASETS" -DATA_DIR="${DATASETS[0]}" +DATA_DIR="/opt/dkube/input/dataset" -IFS=',' read -ra MODELS <<< "$DKUBE_INPUT_MODELS" -MODEL_DIR="${MODELS[0]}" +MODEL_DIR="/opt/dkube/input/model" #Set datset path in pipeline config file sed -i "s|DATA_PATH|"${DATA_DIR}"|g" $CONFIG_FILE @@ -49,4 +47,3 @@ done #Set the model path in pipeline.config file to the extracted path sed -i "s|MODEL_PATH|"${EXTRACT_PATH}"|g" $CONFIG_FILE -sed -i '/num_steps/c\ num_steps : '"${STEPS}"'' $CONFIG_FILE