From 129e85318bfab6fb324db3559474f2d6ceef2509 Mon Sep 17 00:00:00 2001 From: Oleksandra Pavlusieva Date: Thu, 5 Jan 2023 16:59:40 +0200 Subject: [PATCH 001/147] Create build_spec.yaml Add build_spec.yaml as preparation for Oracle's GitHub repository guideline checks --- build_spec.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 build_spec.yaml diff --git a/build_spec.yaml b/build_spec.yaml new file mode 100644 index 000000000..c267d2abe --- /dev/null +++ b/build_spec.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2023, 2022, Oracle and/or its affiliates. + +version: 0.1 +component: build +timeoutInSeconds: 1000 +shell: bash + +steps: + - type: Command + name: "compress the repo" + command: | + tar -cvzf ${OCI_WORKSPACE_DIR}/repo.tgz ./ +outputArtifacts: + - name: artifact + type: BINARY + location: ${OCI_WORKSPACE_DIR}/repo.tgz From f784c2f96165143b1c0c56ba238d104897b835b8 Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:04:35 -0800 Subject: [PATCH 002/147] Doc fixes for release (#73) --- docs/source/user_guide/pipeline/index.rst | 6 ++-- docs/source/user_guide/pipeline/overview.rst | 8 ++--- docs/source/user_guide/pipeline/pipeline.rst | 34 +++++++++---------- .../user_guide/pipeline/pipeline_run.rst | 22 ++++++------ .../user_guide/pipeline/quick_start.rst | 16 ++++----- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/source/user_guide/pipeline/index.rst b/docs/source/user_guide/pipeline/index.rst index 62aa0774c..8fc200f8d 100644 --- a/docs/source/user_guide/pipeline/index.rst +++ b/docs/source/user_guide/pipeline/index.rst @@ -1,8 +1,8 @@ .. _pipeline: -######## -Pipeline -######## +######### +Pipelines +######### .. versionadded:: 2.8.0 diff --git a/docs/source/user_guide/pipeline/overview.rst b/docs/source/user_guide/pipeline/overview.rst index 823a3999e..6c2474911 100644 --- a/docs/source/user_guide/pipeline/overview.rst +++ b/docs/source/user_guide/pipeline/overview.rst @@ -1,20 +1,20 @@ Overview ******** -Oracle Cloud Infrastructure (OCI) Data Science Machine Learning (ML) Pipeline enables you to define and run an end-to-end machine learning orchestration covering all the steps of machine learning lifecycle that can be executed in a repeatable, continuous ML pipeline. +Oracle Cloud Infrastructure (OCI) Data Science Machine Learning (ML) Pipelines lets you define and run an end-to-end machine learning orchestration covering all the steps of machine learning lifecycle that can be executed in a repeatable, continuous ML pipeline. -Machine learning lifecycle is composed of data acquisition and extraction, data preparation, featurization, training model including algorithm selection and hyper-parameter tuning, model evaluation, deployment, and then monitoring the deployed model and retraining. +The machine learning lifecycle is composed of several steps: data acquisition and extraction, data preparation, featurization, model training including algorithm selection and hyper-parameter tuning, model evaluation, deployment, and finally monitoring the deployed model and possible retraining. Pipeline Step ============= -Pipeline step is a task in a pipeline. A pipeline step can be either a Data Science Job step or a Custom Script step. +A pipeline step is a task in a pipeline. A pipeline step can be either a Data Science Job step or a Custom Script step. Pipeline ======== -A pipeline is a workflow of tasks, called steps. Steps can run in sequence or in parallel, creating a Directed Acyclic Graph (DAG) of the steps. +A pipeline is a workflow of tasks, called steps. Steps can run in sequence or in parallel resulting in a Directed Acyclic Graph (DAG) of the steps. In a machine learning context, ML Pipelines provide a workflow of data import, data transformation, model training, and model evaluation. diff --git a/docs/source/user_guide/pipeline/pipeline.rst b/docs/source/user_guide/pipeline/pipeline.rst index e6e998029..6794d6fe8 100644 --- a/docs/source/user_guide/pipeline/pipeline.rst +++ b/docs/source/user_guide/pipeline/pipeline.rst @@ -196,21 +196,21 @@ You can call the ``run()`` method of the ``Pipeline`` instance to launch a new P It returns a ``PipelineRun`` instance. -The ``.run()`` method gives you the option to override the configurations in a pipeline run. It takes the following optional parameters: +The ``run()`` method gives you the option to override the configurations in a pipeline run. It takes the following optional parameters: -- ``display_name: str, optional``. Defaults to ``None``. The display name of the run. -- ``project_id: str, optional``. Defaults to ``None``. The project id to override the one defined previously. -- ``compartment_id: str, optional``. Defaults to ``None``. The compartment id to override the one defined previously. -- ``configuration_override_details: dict, optional``. Defaults to ``None``. The configuration details dictionary to override the one defined previously. The ``configuration_override_details`` contains the following keys: + - ``display_name: str, optional``. Defaults to ``None``. The display name of the run. + - ``project_id: str, optional``. Defaults to ``None``. The project id to override the one defined previously. + - ``compartment_id: str, optional``. Defaults to ``None``. The compartment id to override the one defined previously. + - ``configuration_override_details: dict, optional``. Defaults to ``None``. The configuration details dictionary to override the one defined previously. The ``configuration_override_details`` contains the following keys: ``type``: str, only ``DEFAULT`` is allowed; ``environment_variables``: dict, the environment variables; ``command_line_arguments``: str, the command line arguments; ``maximum_runtime_in_minutes``: int, the maximum runtime allowed in minutes. -- ``log_configuration_override_details: dict, optional``. Defaults to ``None``. The log configuration details dictionary to override the one defined previously. -- ``step_override_details: list[PipelineStepOverrideDetails], optional``. Defaults to ``None``. The step details list to override the one defined previously. -- ``free_form_tags: dict(str, str), optional``. Defaults to ``None``. The free from tags dictionary to override the one defined previously. -- ``defined_tags: dict(str, dict(str, object)), optional``. Defaults to ``None``. The defined tags dictionary to override the one defined previously. -- ``system_tags: dict(str, dict(str, object)), optional``. Defaults to ``None``. The system tags dictionary to override the one defined previously. + - ``log_configuration_override_details: dict, optional``. Defaults to ``None``. The log configuration details dictionary to override the one defined previously. + - ``step_override_details: list[PipelineStepOverrideDetails], optional``. Defaults to ``None``. The step details list to override the one defined previously. + - ``free_form_tags: dict(str, str), optional``. Defaults to ``None``. The free from tags dictionary to override the one defined previously. + - ``defined_tags: dict(str, dict(str, object)), optional``. Defaults to ``None``. The defined tags dictionary to override the one defined previously. + - ``system_tags: dict(str, dict(str, object)), optional``. Defaults to ``None``. The system tags dictionary to override the one defined previously. .. code-block:: python3 @@ -233,11 +233,11 @@ Use the ``from_ocid()`` method from the ``Pipeline`` class to load an existing p Visualize ========= -Use the ``.show()`` method on the ``Pipeline`` instance to visualize the pipeline in a graph. +Use the ``show()`` method on the ``Pipeline`` instance to visualize the pipeline in a graph. -The ``.show()`` method takes the following optional parameter: +The ``show()`` method takes the following optional parameter: -- ``rankdir: (str, optional)``. Defaults to ``TB``. The allowed values are ``TB`` or ``LR``. This parameter is applicable only for ``graph`` mode and it renders the direction of the graph as either top to bottom (TB) or left to right (LR). + - ``rankdir: (str, optional)``. Defaults to ``TB``. The allowed values are ``TB`` or ``LR``. This parameter is applicable only for ``graph`` mode and it renders the direction of the graph as either top to bottom (TB) or left to right (LR). .. code-block:: python3 @@ -252,11 +252,11 @@ Below is an example of the output. Delete ====== -Use the ``.delete()`` method on the ``Pipeline`` instance to delete a pipeline. It takes the following optional parameters: +Use the ``delete()`` method on the ``Pipeline`` instance to delete a pipeline. It takes the following optional parameters: -- ``delete_related_pipeline_runs: (bool, optional)``. Specify whether to delete related PipelineRuns or not. Defaults to ``True``. -- ``delete_related_job_runs: (bool, optional)``. Specify whether to delete related JobRuns or not. Defaults to ``True``. -- ``max_wait_seconds: (int, optional)``. The maximum time to wait, in seconds. Defaults to ``1800``. + - ``delete_related_pipeline_runs: (bool, optional)``. Specify whether to delete related PipelineRuns or not. Defaults to ``True``. + - ``delete_related_job_runs: (bool, optional)``. Specify whether to delete related JobRuns or not. Defaults to ``True``. + - ``max_wait_seconds: (int, optional)``. The maximum time to wait, in seconds. Defaults to ``1800``. A pipeline can only be deleted when its associated pipeline runs are all deleted, or alternatively, set the parameter ``delete_related_pipeline_runs`` to delete all associated runs in the same operation. diff --git a/docs/source/user_guide/pipeline/pipeline_run.rst b/docs/source/user_guide/pipeline/pipeline_run.rst index 712369a60..c4291a6d5 100644 --- a/docs/source/user_guide/pipeline/pipeline_run.rst +++ b/docs/source/user_guide/pipeline/pipeline_run.rst @@ -11,13 +11,13 @@ With a ``PipelineRun`` instance, you can watch the status of the run and stream Watch status ============ -Use the ``.show()`` method of the ``PipelineRun`` instance to watch the status of pipeline run. +Use the ``show()`` method of the ``PipelineRun`` instance to watch the status of pipeline run. -The ``.show()`` method takes the following optional parameter: +The ``show()`` method takes the following optional parameter: -- ``mode: (str, optional)``. Defaults to ``graph``. The allowed values are ``text`` or ``graph``. This parameter renders the current status of pipeline run as either ``text`` or ``graph``. -- ``wait: (bool, optional)``. Defaults to ``False`` and it only renders the current status of each step run in graph. If set to ``True``, it renders the current status of each step run until the entire pipeline is complete. -- ``rankdir: (str, optional)``. Defaults to ``TB``. The allowed values are ``TB`` or ``LR``. This parameter is applicable only for ``graph`` mode and it renders the direction of the graph as either top to bottom (TB) or left to right (LR). + - ``mode: (str, optional)``. Defaults to ``graph``. The allowed values are ``text`` or ``graph``. This parameter renders the current status of pipeline run as either ``text`` or ``graph``. + - ``wait: (bool, optional)``. Defaults to ``False`` and it only renders the current status of each step run in graph. If set to ``True``, it renders the current status of each step run until the entire pipeline is complete. + - ``rankdir: (str, optional)``. Defaults to ``TB``. The allowed values are ``TB`` or ``LR``. This parameter is applicable only for ``graph`` mode and it renders the direction of the graph as either top to bottom (TB) or left to right (LR). To watch the live update of each step run status in text until the entire pipeline is complete @@ -49,8 +49,8 @@ Below is an example of the output. Monitor Logs ============ -Use the ``.watch()`` method on the ``PipelineRun`` instance to stream the service, custom, or consolidated log of the pipeline run. -The ``.watch()`` method takes the following optional parameters: +Use the ``watch()`` method on the ``PipelineRun`` instance to stream the service, custom, or consolidated log of the pipeline run. +The ``watch()`` method takes the following optional parameters: - ``steps: (list, optional)``. Defaults to ``None`` and streams the log of the pipeline run. If a list of the step names is provided, the method streams the log of the specified pipeline step runs. - ``log_type: (str, optional)``. Defaults to ``None``. The allowed values are ``custom_log``, ``service_log``, or ``None``. If ``None`` is provided, the method streams both service and custom logs. @@ -122,7 +122,7 @@ Use the ``from_ocid()`` method from the ``PipelineRun`` class to load an existin Cancel ====== -Use the ``.cancel()`` method on the ``PipelineRun`` instance to cancel a pipeline run. +Use the ``cancel()`` method on the ``PipelineRun`` instance to cancel a pipeline run. Pipeline Runs can only be canceled when they are in the ACCEPTED or IN_PROGRESS state. @@ -135,10 +135,10 @@ Pipeline Runs can only be canceled when they are in the ACCEPTED or IN_PROGRESS Delete ====== -Use the ``.delete()`` method on the ``PipelineRun`` instance to delete a pipeline run. It takes the following optional parameter: +Use the ``delete()`` method on the ``PipelineRun`` instance to delete a pipeline run. It takes the following optional parameter: -- ``delete_related_job_runs: (bool, optional)``. Specify whether to delete related JobRuns or not. Defaults to ``True``. -- ``max_wait_seconds: (int, optional)``. The maximum time to wait in seconds. Defaults to ``1800``. + * ``delete_related_job_runs: (bool, optional)``. Specify whether to delete related JobRuns or not. Defaults to ``True``. + * ``max_wait_seconds: (int, optional)``. The maximum time to wait in seconds. Defaults to ``1800``. Pipeline runs can only be deleted when they are already in a SUCCEEDED, FAILED, or CANCELED state. diff --git a/docs/source/user_guide/pipeline/quick_start.rst b/docs/source/user_guide/pipeline/quick_start.rst index 35662621e..05ce53de2 100644 --- a/docs/source/user_guide/pipeline/quick_start.rst +++ b/docs/source/user_guide/pipeline/quick_start.rst @@ -5,9 +5,9 @@ Quick Start ADS Python SDK ============== -The following sections provide sample code to define, create and run a pipeline, and watch the pipeline run status. +The following sections provide sample code to define, create, and run a pipeline, including a visualization to track the pipeline run status. -The following example shows creating and runnning a pipeline with multiple steps. The steps, ``step_1`` and ``step_2`` run in parallel and +The following example shows creating and runnning a pipeline with multiple steps. The steps ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs after ``step_1`` and ``step_2`` are complete. .. figure:: figures/quick_start_example.png @@ -353,7 +353,7 @@ Alternatively, to run an existing pipeline, provide the pipeline OCID with the ` Visualize --------- -To visualize a pipeline in a graph, provide the pipeline OCID +To visualize a pipeline in a graph, use the pipeline OCID .. code-block:: shell @@ -362,7 +362,7 @@ To visualize a pipeline in a graph, provide the pipeline OCID Watch status ------------ -To watch the status of pipeline run, provide the pipeline run OCID +To watch the status of pipeline run, use the pipeline run OCID .. admonition:: Tip @@ -383,7 +383,7 @@ Below is an example of watching the status of pipeline run in ``text`` mode Monitor logs ------------ -To monitor a pipeline run, provide the pipeline run OCID +To monitor a pipeline run, use the pipeline run OCID. .. admonition:: Tip @@ -404,7 +404,7 @@ Below is an example of viewing the last 10 consolidated logs with ``tail`` Cancel ------ -To cancel a pipeline run, provide the pipeline run OCID +To cancel a pipeline run, use the pipeline run OCID .. code-block:: shell @@ -419,7 +419,7 @@ Delete Get more information about deleting pipelines and pipeline runs by running ``%pipeline delete -h`` -To delete a pipeline run, provide the pipeline run OCID +To delete a pipeline run, use the pipeline run OCID .. code-block:: shell @@ -427,7 +427,7 @@ To delete a pipeline run, provide the pipeline run OCID Data Science Pipeline Runs can only be deleted when they are in the `SUCCEEDED`, `FAILED`, or `CANCELED` state. -To delete a pipeline, provide the pipeline OCID +To delete a pipeline, use the pipeline OCID .. code-block:: shell From 69093c637329e616f9859cf095059052e79b1f01 Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:45:45 -0800 Subject: [PATCH 003/147] Pipelines docs updates (#75) --- docs/source/user_guide/pipeline/examples.rst | 1146 ++++------------- docs/source/user_guide/pipeline/index.rst | 6 +- docs/source/user_guide/pipeline/pipeline.rst | 128 +- .../user_guide/pipeline/pipeline_step.rst | 203 ++- .../user_guide/pipeline/quick_start.rst | 208 +-- 5 files changed, 492 insertions(+), 1199 deletions(-) diff --git a/docs/source/user_guide/pipeline/examples.rst b/docs/source/user_guide/pipeline/examples.rst index 87e0d5516..688e679b0 100644 --- a/docs/source/user_guide/pipeline/examples.rst +++ b/docs/source/user_guide/pipeline/examples.rst @@ -6,87 +6,6 @@ Create a pipeline .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - pipeline_step = ( - PipelineStep("Python_Script_Step") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = ( - Pipeline("A single step pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step]) - ) - - pipeline.create() - .. code-tab:: Python3 :caption: YAML @@ -132,73 +51,19 @@ Create a pipeline pipeline = Pipeline.from_yaml(yaml_string) pipeline.create() - - -Run a job as a step -=================== - -.. tabs:: + .. code-tab:: Python3 :caption: Python - from ads.jobs import Job, DataScienceJob, ScriptRuntime - from ads.pipeline import PipelineStep, Pipeline - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = DataScienceJob( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - job = Job( - infrastructure=infrastructure, - runtime=runtime - ) - job.create() # create a job - - pipeline_step = PipelineStep( - name="Job_Step", - description="A step running a job", - job_id=job.id - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.jobs import Job, DataScienceJob, ScriptRuntime - from ads.pipeline import Pipeline, PipelineStep + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os with open("script.py", "w") as f: f.write("print('Hello World!')") infrastructure = ( - DataScienceJob() + CustomScriptStep() .with_block_storage_size(200) .with_shape_name("VM.Standard3.Flex") .with_shape_config_details(ocpus=4, memory_in_gbs=32) @@ -210,18 +75,12 @@ Run a job as a step .with_service_conda("generalml_p37_cpu_v1") ) - job = ( - Job() + pipeline_step = ( + PipelineStep("Python_Script_Step") + .with_description("A step running a python script") .with_infrastructure(infrastructure) .with_runtime(runtime) ) - job.create() # create a job - - pipeline_step = ( - PipelineStep("Job_Step") - .with_description("A step running a job") - .with_job_id(job.id) - ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] @@ -235,7 +94,13 @@ Run a job as a step pipeline.create() - pipeline_run = pipeline.run() + + + +Run a job as a step +=================== + +.. tabs:: .. code-tab:: Python3 :caption: YAML @@ -292,64 +157,18 @@ Run a job as a step pipeline_run = pipeline.run() -Run a python script as a step -============================= - -.. tabs:: - .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime + from ads.jobs import Job, DataScienceJob, ScriptRuntime + from ads.pipeline import Pipeline, PipelineStep import os with open("script.py", "w") as f: f.write("print('Hello World!')") infrastructure = ( - CustomScriptStep() + DataScienceJob() .with_block_storage_size(200) .with_shape_name("VM.Standard3.Flex") .with_shape_config_details(ocpus=4, memory_in_gbs=32) @@ -361,12 +180,18 @@ Run a python script as a step .with_service_conda("generalml_p37_cpu_v1") ) - pipeline_step = ( - PipelineStep("Python_Script_Step") - .with_description("A step running a python script") + job = ( + Job() .with_infrastructure(infrastructure) .with_runtime(runtime) ) + job.create() # create a job + + pipeline_step = ( + PipelineStep("Job_Step") + .with_description("A step running a job") + .with_job_id(job.id) + ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] @@ -382,6 +207,13 @@ Run a python script as a step pipeline_run = pipeline.run() + + +Run a python script as a step +============================= + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -430,59 +262,16 @@ Run a python script as a step pipeline_run = pipeline.run() - -Run a notebook as a step -======================== - -.. tabs:: .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime - import os - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os + with open("script.py", "w") as f: + f.write("print('Hello World!')") + infrastructure = ( CustomScriptStep() .with_block_storage_size(200) @@ -491,19 +280,14 @@ Run a notebook as a step ) runtime = ( - NotebookRuntime() - .with_notebook( - path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - encoding='utf-8' - ) - .with_service_conda("tensorflow26_p37_cpu_v2") - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_output("oci://@/") + ScriptRuntime() + .with_source("script.py") + .with_service_conda("generalml_p37_cpu_v1") ) pipeline_step = ( - PipelineStep("Notebook_Step") - .with_description("A step running a notebook") + PipelineStep("Python_Script_Step") + .with_description("A step running a python script") .with_infrastructure(infrastructure) .with_runtime(runtime) ) @@ -512,16 +296,23 @@ Run a notebook as a step project_id = os.environ["PROJECT_OCID"] pipeline = ( - Pipeline("A single step pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step]) - ) + Pipeline("A single step pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step]) + ) pipeline.create() pipeline_run = pipeline.run() + + +Run a notebook as a step +======================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -571,79 +362,13 @@ Run a notebook as a step pipeline.create() pipeline_run = pipeline.run() - - - -Run two steps with the same infrastructure -========================================== - -.. tabs:: - + .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - step_one_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "generalml_p37_cpu_v1"} - ) - - pipeline_step_one = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=step_one_runtime - ) - - step_two_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step_two = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=step_two_runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_one, pipeline_step_two], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime import os - with open("script.py", "w") as f: - f.write("print('Hello World!')") - infrastructure = ( CustomScriptStep() .with_block_storage_size(200) @@ -651,20 +376,7 @@ Run two steps with the same infrastructure .with_shape_config_details(ocpus=4, memory_in_gbs=32) ) - step_one_runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - pipeline_step_one = ( - PipelineStep("Python_Script_Step") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(step_one_runtime) - ) - - step_two_runtime = ( + runtime = ( NotebookRuntime() .with_notebook( path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", @@ -675,27 +387,34 @@ Run two steps with the same infrastructure .with_output("oci://@/") ) - pipeline_step_two = ( + pipeline_step = ( PipelineStep("Notebook_Step") .with_description("A step running a notebook") .with_infrastructure(infrastructure) - .with_runtime(step_two_runtime) + .with_runtime(runtime) ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] pipeline = ( - Pipeline("A single step pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step_one, pipeline_step_two]) - ) + Pipeline("A single step pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step]) + ) pipeline.create() pipeline_run = pipeline.run() + + +Run two steps with the same infrastructure +========================================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -751,83 +470,19 @@ Run two steps with the same infrastructure kind: runtime spec: conda: - slug: tensorflow26_p37_cpu_v2 - type: service - env: - - name: GREETINGS - value: Welcome to OCI Data Science - notebookEncoding: utf-8 - notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb - outputURI: oci://@/ - type: notebook - type: pipeline - """.format(compartment_id=compartment_id, project_id=project_id) - - pipeline = Pipeline.from_yaml(yaml_string) - - pipeline.create() - - pipeline_run = pipeline.run() - - - - -Run two steps in parallel -========================= - -In the example below, when DAG is not specified, the steps in the pipeline run in parallel. - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - step_one_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "generalml_p37_cpu_v1"} - ) - - pipeline_step_one = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=step_one_runtime - ) - - step_two_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step_two = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=step_two_runtime - ) - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] + slug: tensorflow26_p37_cpu_v2 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputURI: oci://@/ + type: notebook + type: pipeline + """.format(compartment_id=compartment_id, project_id=project_id) - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_one, pipeline_step_two], - ) + pipeline = Pipeline.from_yaml(yaml_string) pipeline.create() @@ -835,7 +490,7 @@ In the example below, when DAG is not specified, the steps in the pipeline run i .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime import os @@ -895,6 +550,16 @@ In the example below, when DAG is not specified, the steps in the pipeline run i pipeline_run = pipeline.run() + + + +Run two steps in parallel +========================= + +In the example below, when DAG is not specified, the steps in the pipeline run in parallel. + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -967,73 +632,11 @@ In the example below, when DAG is not specified, the steps in the pipeline run i pipeline.create() pipeline_run = pipeline.run() - - - -Run two steps sequentially -========================== - -.. tabs:: - + + .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - step_one_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "generalml_p37_cpu_v1"} - ) - - pipeline_step_one = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=step_one_runtime - ) - - step_two_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step_two = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=step_two_runtime - ) - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_one, pipeline_step_two], - dag=["Python_Script_Step >> Notebook_Step"], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime import os @@ -1086,13 +689,20 @@ Run two steps sequentially .with_compartment_id(compartment_id) .with_project_id(project_id) .with_step_details([pipeline_step_one, pipeline_step_two]) - .with_dag(["Python_Script_Step >> Notebook_Step"]) ) pipeline.create() pipeline_run = pipeline.run() + + + +Run two steps sequentially +========================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1168,83 +778,9 @@ Run two steps sequentially pipeline_run = pipeline.run() - -Run multiple steps with dependencies specified in DAG -===================================================== - -In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs after ``step_1`` and ``step_2`` are complete. - -.. tabs:: - .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - script_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - notebook_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step_1 = PipelineStep( - name="step_1", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - pipeline_step_2 = PipelineStep( - name="step_2", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=notebook_runtime - ) - - pipeline_step_3 = PipelineStep( - name="step_3", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="An example pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_1, pipeline_step_2, pipeline_step_3], - dag=["(step_1, step_2) >> step_3"], - ) - - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline - - pipeline_run = pipeline.run() # run the pipeline - - pipeline_run.show(wait=True) # watch the pipeline run status - - - - .. code-tab:: Python3 - :caption: Python (Alternative) - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime import os @@ -1258,61 +794,62 @@ In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs a .with_shape_config_details(ocpus=4, memory_in_gbs=32) ) - script_runtime = ( + step_one_runtime = ( ScriptRuntime() .with_source("script.py") .with_service_conda("generalml_p37_cpu_v1") ) - notebook_runtime = ( + pipeline_step_one = ( + PipelineStep("Python_Script_Step") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(step_one_runtime) + ) + + step_two_runtime = ( NotebookRuntime() .with_notebook( path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", encoding='utf-8' ) .with_service_conda("tensorflow26_p37_cpu_v2") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_output("oci://@/") ) - pipeline_step_1 = ( - PipelineStep("step_1") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) - ) - - pipeline_step_2 = ( - PipelineStep("step_2") + pipeline_step_two = ( + PipelineStep("Notebook_Step") .with_description("A step running a notebook") .with_infrastructure(infrastructure) - .with_runtime(notebook_runtime) - ) - - pipeline_step_3 = ( - PipelineStep("step_3") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) + .with_runtime(step_two_runtime) ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] pipeline = ( - Pipeline("An example pipeline") + Pipeline("A single step pipeline") .with_compartment_id(compartment_id) .with_project_id(project_id) - .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) - .with_dag(["(step_1, step_2) >> step_3"]) + .with_step_details([pipeline_step_one, pipeline_step_two]) + .with_dag(["Python_Script_Step >> Notebook_Step"]) ) - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline + pipeline.create() - pipeline_run = pipeline.run() # run the pipeline + pipeline_run = pipeline.run() - pipeline_run.show(wait=True) # watch the pipeline run status + +Run multiple steps with dependencies specified in DAG +===================================================== + +In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs after ``step_1`` and ``step_2`` are complete. + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1408,46 +945,128 @@ In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs a pipeline_run.show(wait=True) # watch the pipeline run status -Set environment variables in a step -=================================== - -.. tabs:: - .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime - import os + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime + import os + + with open("script.py", "w") as f: + f.write("print('Hello World!')") + + infrastructure = ( + CustomScriptStep() + .with_block_storage_size(200) + .with_shape_name("VM.Standard3.Flex") + .with_shape_config_details(ocpus=4, memory_in_gbs=32) + ) + + script_runtime = ( + ScriptRuntime() + .with_source("script.py") + .with_service_conda("generalml_p37_cpu_v1") + ) + + notebook_runtime = ( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow26_p37_cpu_v2") + ) - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, + pipeline_step_1 = ( + PipelineStep("step_1") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) ) - runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} + pipeline_step_2 = ( + PipelineStep("step_2") + .with_description("A step running a notebook") + .with_infrastructure(infrastructure) + .with_runtime(notebook_runtime) ) - pipeline_step = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=runtime + pipeline_step_3 = ( + PipelineStep("step_3") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) + pipeline = ( + Pipeline("An example pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) + .with_dag(["(step_1, step_2) >> step_3"]) + ) + + pipeline.create() # create the pipeline + pipeline.show() # visualize the pipeline + + pipeline_run = pipeline.run() # run the pipeline + + pipeline_run.show(wait=True) # watch the pipeline run status + + + +Set environment variables in a step +=================================== + +.. tabs:: + + .. code-tab:: Python3 + :caption: YAML + + from ads.pipeline import Pipeline + import os + + compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] + project_id = os.environ["PROJECT_OCID"] + + yaml_string = """ + kind: pipeline + spec: + compartmentId: {compartment_id} + displayName: A single step pipeline + projectId: {project_id} + stepDetails: + - kind: customScript + spec: + description: A step running a notebook + infrastructure: + kind: infrastructure + spec: + blockStorageSize: 200 + shapeConfigDetails: + memoryInGBs: 32 + ocpus: 4 + shapeName: VM.Standard3.Flex + name: Notebook_Step + runtime: + kind: runtime + spec: + conda: + slug: tensorflow26_p37_cpu_v2 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputURI: oci://@/ + type: notebook + type: pipeline + """.format(compartment_id=compartment_id, project_id=project_id) + + pipeline = Pipeline.from_yaml(yaml_string) pipeline.create() @@ -1455,7 +1074,7 @@ Set environment variables in a step .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime import os @@ -1499,6 +1118,14 @@ Set environment variables in a step pipeline_run = pipeline.run() + + + +Watch status update on a pipeline run +===================================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1508,6 +1135,9 @@ Set environment variables in a step compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] + with open("script.py", "w") as f: + f.write("print('Hello World!')") + yaml_string = """ kind: pipeline spec: @@ -1517,7 +1147,7 @@ Set environment variables in a step stepDetails: - kind: customScript spec: - description: A step running a notebook + description: A step running a python script infrastructure: kind: infrastructure spec: @@ -1526,82 +1156,28 @@ Set environment variables in a step memoryInGBs: 32 ocpus: 4 shapeName: VM.Standard3.Flex - name: Notebook_Step + name: Python_Script_Step runtime: kind: runtime spec: conda: - slug: tensorflow26_p37_cpu_v2 + slug: generalml_p37_cpu_v1 type: service - env: - - name: GREETINGS - value: Welcome to OCI Data Science - notebookEncoding: utf-8 - notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb - outputURI: oci://@/ - type: notebook + scriptPathURI: script.py + type: script type: pipeline """.format(compartment_id=compartment_id, project_id=project_id) pipeline = Pipeline.from_yaml(yaml_string) - pipeline.create() - - pipeline_run = pipeline.run() - - - -Watch status update on a pipeline run -===================================== - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - pipeline.create() pipeline_run = pipeline.run() - + # pipeline_run.show(mode="text") # watch pipeline run status in text - pipeline_run.show(wait=True) # watch pipeline run status in graph - + pipeline_run.show(wait=True) # watch pipeline run status in graph .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os @@ -1646,6 +1222,12 @@ Watch status update on a pipeline run pipeline_run.show(wait=True) # watch pipeline run status in graph + +Monitor logs of a pipeline run +============================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1692,67 +1274,13 @@ Watch status update on a pipeline run pipeline.create() pipeline_run = pipeline.run() - - # pipeline_run.show(mode="text") # watch pipeline run status in text - pipeline_run.show(wait=True) # watch pipeline run status in graph - - - - -Monitor logs of a pipeline run -============================== - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - pipeline_run = pipeline.run() # pipeline_run.watch() # stream the consolidated log of the pipeline run pipeline_run.watch(log_type="service_log") # stream service log of the pipeline run pipeline_run.watch("Python_Script_Step", log_type="custom_log") # stream custom log of the step run - .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os @@ -1797,6 +1325,14 @@ Monitor logs of a pipeline run pipeline_run.watch(log_type="service_log") # stream service log of the pipeline run pipeline_run.watch("Python_Script_Step", log_type="custom_log") # stream custom log of the step run + + + +Override configurations when creating a pipeline run +==================================================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1812,6 +1348,9 @@ Monitor logs of a pipeline run yaml_string = """ kind: pipeline spec: + commandLineArguments: argument --key value + environmentVariables: + env: value compartmentId: {compartment_id} displayName: A single step pipeline projectId: {project_id} @@ -1841,58 +1380,6 @@ Monitor logs of a pipeline run pipeline = Pipeline.from_yaml(yaml_string) - pipeline.create() - pipeline_run = pipeline.run() - - # pipeline_run.watch() # stream the consolidated log of the pipeline run - pipeline_run.watch(log_type="service_log") # stream service log of the pipeline run - pipeline_run.watch("Python_Script_Step", log_type="custom_log") # stream custom log of the step run - - -Override configurations when creating a pipeline run -==================================================== - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - command_line_arguments="argument --key value", - environment_variables={"env": "value"}, - ) - pipeline.create() # Override configurations when creating a pipeline run @@ -1919,12 +1406,10 @@ Override configurations when creating a pipeline run configuration_override_details=configuration_override_details, step_override_details=step_override_details, ) - - - + .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os @@ -1993,80 +1478,7 @@ Override configurations when creating a pipeline run - .. code-tab:: Python3 - :caption: YAML - - from ads.pipeline import Pipeline - import os - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - yaml_string = """ - kind: pipeline - spec: - commandLineArguments: argument --key value - environmentVariables: - env: value - compartmentId: {compartment_id} - displayName: A single step pipeline - projectId: {project_id} - stepDetails: - - kind: customScript - spec: - description: A step running a python script - infrastructure: - kind: infrastructure - spec: - blockStorageSize: 200 - shapeConfigDetails: - memoryInGBs: 32 - ocpus: 4 - shapeName: VM.Standard3.Flex - name: Python_Script_Step - runtime: - kind: runtime - spec: - conda: - slug: generalml_p37_cpu_v1 - type: service - scriptPathURI: script.py - type: script - type: pipeline - """.format(compartment_id=compartment_id, project_id=project_id) - - pipeline = Pipeline.from_yaml(yaml_string) - - pipeline.create() - - # Override configurations when creating a pipeline run - display_override_name = "RunOverrideName" - configuration_override_details = { - "maximum_runtime_in_minutes": 30, - "type": "DEFAULT", - "environment_variables": {"a": "b"}, - "command_line_arguments": "ARGUMENT --KEY VALUE", - } - step_override_details = [ - { - "step_name": "Python_Script_Step", - "step_configuration_details": { - "maximum_runtime_in_minutes": 200, - "environment_variables": {"1": "2"}, - "command_line_arguments": "argument --key value", - }, - } - ] - pipeline_run = pipeline.run( - display_name=display_override_name, - configuration_override_details=configuration_override_details, - step_override_details=step_override_details, - ) - diff --git a/docs/source/user_guide/pipeline/index.rst b/docs/source/user_guide/pipeline/index.rst index 8fc200f8d..43a8ecf72 100644 --- a/docs/source/user_guide/pipeline/index.rst +++ b/docs/source/user_guide/pipeline/index.rst @@ -1,8 +1,8 @@ .. _pipeline: -######### -Pipelines -######### +###################### +Data Science Pipelines +###################### .. versionadded:: 2.8.0 diff --git a/docs/source/user_guide/pipeline/pipeline.rst b/docs/source/user_guide/pipeline/pipeline.rst index 6794d6fe8..6abaf0e78 100644 --- a/docs/source/user_guide/pipeline/pipeline.rst +++ b/docs/source/user_guide/pipeline/pipeline.rst @@ -46,89 +46,6 @@ A ``Pipeline`` instance will be created. .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PipelineStep, Pipeline - - pipeline_step_one = PipelineStep( - name="", - description="", - job_id="" - ) - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri=oci://@//", - conda={"type": "service", "slug": ""} - ) - - pipeline_step_two = PipelineStep( - name="", - description="", - infrastructure=infrastructure, - runtime=runtime, - ) - - pipeline = Pipeline( - name="", - compartment_id="", - project_id="", - log_group_id="", - log_id="", - enable_service_log=True, # to stream service log in pipeline runs - step_details=[pipeline_step_one, pipeline_step_two], - dag=["pipeline_step_name_1 >> pipeline_step_name_2"], - ) - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import PipelineStep, Pipeline - - pipeline_step_one = ( - PipelineStep("") - .with_description("") - .with_job_id("") - ) - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - runtime = ( - ScriptRuntime() - .with_source("oci://@//") - .with_service_conda("") - ) - - pipeline_step_two = ( - PipelineStep("") - .with_description("") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - - pipeline = ( - Pipeline("") - .with_compartment_id("") - .with_project_id("") - .with_log_group_id("") - .with_log_id("") - .with_enable_service_log(True) # to stream service log in pipeline runs - .with_step_details([pipeline_step_one, pipeline_step_two]) - .with_dag(["pipeline_step_name_1 >> pipeline_step_name_2"]) - ) - - .. code-tab:: Python3 :caption: YAML @@ -177,6 +94,51 @@ A ``Pipeline`` instance will be created. """ pipeline = Pipeline.from_yaml(yaml_string) + + + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import PipelineStep, Pipeline + + pipeline_step_one = ( + PipelineStep("") + .with_description("") + .with_job_id("") + ) + + infrastructure = ( + CustomScriptStep() + .with_block_storage_size(200) + .with_shape_name("VM.Standard3.Flex") + .with_shape_config_details(ocpus=4, memory_in_gbs=32) + ) + + runtime = ( + ScriptRuntime() + .with_source("oci://@//") + .with_service_conda("") + ) + + pipeline_step_two = ( + PipelineStep("") + .with_description("") + .with_infrastructure(infrastructure) + .with_runtime(runtime) + ) + + pipeline = ( + Pipeline("") + .with_compartment_id("") + .with_project_id("") + .with_log_group_id("") + .with_log_id("") + .with_enable_service_log(True) # to stream service log in pipeline runs + .with_step_details([pipeline_step_one, pipeline_step_two]) + .with_dag(["pipeline_step_name_1 >> pipeline_step_name_2"]) + ) + + Create diff --git a/docs/source/user_guide/pipeline/pipeline_step.rst b/docs/source/user_guide/pipeline/pipeline_step.rst index 472d2332b..b44488b30 100644 --- a/docs/source/user_guide/pipeline/pipeline_step.rst +++ b/docs/source/user_guide/pipeline/pipeline_step.rst @@ -17,28 +17,6 @@ Create a Data Science Job step with the OCID of an existing Job. .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PipelineStep - - pipeline_step = PipelineStep( - name="", - description="", - job_id="" - ) - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import PipelineStep - - pipeline_step = ( - PipelineStep("") - .with_description("") - .with_job_id("") - ) - .. code-tab:: YAML kind: pipeline @@ -53,7 +31,17 @@ Create a Data Science Job step with the OCID of an existing Job. name: ... type: pipeline + + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import PipelineStep + pipeline_step = ( + PipelineStep("") + .with_description("") + .with_job_id("") + ) Custom Script Step @@ -67,20 +55,30 @@ When constructing a Custom Scrip step ``infrastructure``, you specify the Comput .. tabs:: - .. code-tab:: Python3 - :caption: Python + .. code-tab:: YAML - from ads.pipeline import CustomScriptStep - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) + kind: pipeline + spec: + ... + stepDetails: + - kind: customScript + spec: + infrastructure: + kind: infrastructure + spec: + blockStorageSize: 200 + shapeConfigDetails: + memoryInGBs: 32 + ocpus: 4 + shapeName: VM.Standard3.Flex + name: Python_Script_Step + ... + type: pipeline .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import CustomScriptStep @@ -104,25 +102,26 @@ To define a Custom Script step with ``GitPythonRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import GitPythonRuntime + .. code-tab:: YAML - runtime = GitPythonRuntime( - env={"GREETINGS": "Welcome to OCI Data Science"} - conda={"type": "service", "slug": "pytorch19_p37_gpu_v1"} - url="https://github.com/pytorch/tutorials.git", - entrypoint="beginner_source/examples_nn/polynomial_nn.py", - output_dir="~/Code/tutorials/beginner_source/examples_nn", - outputURI="oci://@/", - ) + kind: runtime + spec: + conda: + slug: pytorch19_p37_gpu_v1 + type: service + entrypoint: beginner_source/examples_nn/polynomial_nn.py + env: + - name: GREETINGS + value: Welcome to OCI Data Science + outputDir: ~/Code/tutorials/beginner_source/examples_nn + outputUri: oci://@/ + url: https://github.com/pytorch/tutorials.git + type: gitPython .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python - from ads.pipeline import GitPythonRuntime runtime = ( @@ -142,22 +141,23 @@ To define a Custom Script step with ``NotebookRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import NotebookRuntime - - runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - notebook_encoding="utf-8", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - env={"GREETINGS": "Welcome to OCI Data Science"} - outputURI="oci://@/", - ) + .. code-tab:: YAML + kind: runtime + spec: + conda: + slug: tensorflow26_p37_cpu_v2 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputURI: oci://bucket_name@namespace/path/to/dir + type: notebook .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import NotebookRuntime @@ -177,23 +177,24 @@ To define a Custom Script step with ``PythonRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PythonRuntime - - runtime = PythonRuntime( - script_path_uri="local/path/to/zip_or_dir", - entrypoint="zip_or_dir/my_package/entry.py", - working_dir="zip_or_dir", - python_path=["my_python_packages"], - output_uri="oci://@/", - conda={"type": "service", "slug": "pytorch19_p37_cpu_v1"} - ) + .. code-tab:: YAML + kind: runtime + spec: + conda: + slug: pytorch19_p37_cpu_v1 + type: service + entrypoint: zip_or_dir/my_package/entry.py + outputDir: output + outputUri: oci://bucket_name@namespace/path/to/dir + pythonPath: + - my_python_packages + scriptPathURI: local/path/to/zip_or_dir + workingDir: zip_or_dir + type: python .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import PythonRuntime @@ -218,19 +219,18 @@ To define a Custom Script step with ``ScriptRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import ScriptRuntime - - runtime = ScriptRuntime( - script_path_uri="oci://@//", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - + .. code-tab:: YAML + + kind: runtime + spec: + conda: + slug: tensorflow26_p37_cpu_v2 + type: service + scriptPathURI: oci://@// + type: script .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import ScriptRuntime @@ -244,32 +244,6 @@ With ``Infrastructure`` and ``runtime`` provided, create a pipeline step of the .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PipelineStep - - pipeline_step = PipelineStep( - name="", - description="", - infrastructure=infrastructure, - runtime=runtime, - ) - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import PipelineStep - - pipeline_step = ( - PipelineStep("") - .with_description("") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - - .. code-tab:: YAML kind: pipeline @@ -299,6 +273,17 @@ With ``Infrastructure`` and ``runtime`` provided, create a pipeline step of the type: script ... type: pipeline + + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import PipelineStep + pipeline_step = ( + PipelineStep("") + .with_description("") + .with_infrastructure(infrastructure) + .with_runtime(runtime) + ) diff --git a/docs/source/user_guide/pipeline/quick_start.rst b/docs/source/user_guide/pipeline/quick_start.rst index 05ce53de2..b335ff894 100644 --- a/docs/source/user_guide/pipeline/quick_start.rst +++ b/docs/source/user_guide/pipeline/quick_start.rst @@ -15,143 +15,6 @@ The following example shows creating and runnning a pipeline with multiple steps .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - script_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - notebook_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step_1 = PipelineStep( - name="step_1", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - pipeline_step_2 = PipelineStep( - name="step_2", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=notebook_runtime - ) - - pipeline_step_3 = PipelineStep( - name="step_3", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="An example pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_1, pipeline_step_2, pipeline_step_3], - dag=["(step_1, step_2) >> step_3"], - ) - - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline - - pipeline_run = pipeline.run() # run the pipeline - - pipeline_run.show() # watch the pipeline run status - - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - script_runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - notebook_runtime = ( - NotebookRuntime() - .with_notebook( - path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - encoding='utf-8' - ) - .with_service_conda("tensorflow26_p37_cpu_v2") - ) - - pipeline_step_1 = ( - PipelineStep("step_1") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) - ) - - pipeline_step_2 = ( - PipelineStep("step_2") - .with_description("A step running a notebook") - .with_infrastructure(infrastructure) - .with_runtime(notebook_runtime) - ) - - pipeline_step_3 = ( - PipelineStep("step_3") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = ( - Pipeline("An example pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) - .with_dag(["(step_1, step_2) >> step_3"]) - ) - - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline - - pipeline_run = pipeline.run() # run the pipeline - - pipeline_run.show() # watch the pipeline run status - - .. code-tab:: Python3 :caption: YAML @@ -247,6 +110,77 @@ The following example shows creating and runnning a pipeline with multiple steps pipeline_run.show() # watch the pipeline run status + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime + import os + + with open("script.py", "w") as f: + f.write("print('Hello World!')") + + infrastructure = ( + CustomScriptStep() + .with_block_storage_size(200) + .with_shape_name("VM.Standard3.Flex") + .with_shape_config_details(ocpus=4, memory_in_gbs=32) + ) + + script_runtime = ( + ScriptRuntime() + .with_source("script.py") + .with_service_conda("generalml_p37_cpu_v1") + ) + + notebook_runtime = ( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow26_p37_cpu_v2") + ) + + pipeline_step_1 = ( + PipelineStep("step_1") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) + ) + + pipeline_step_2 = ( + PipelineStep("step_2") + .with_description("A step running a notebook") + .with_infrastructure(infrastructure) + .with_runtime(notebook_runtime) + ) + + pipeline_step_3 = ( + PipelineStep("step_3") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) + ) + + compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] + project_id = os.environ["PROJECT_OCID"] + + pipeline = ( + Pipeline("An example pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) + .with_dag(["(step_1, step_2) >> step_3"]) + ) + + pipeline.create() # create the pipeline + pipeline.show() # visualize the pipeline + + pipeline_run = pipeline.run() # run the pipeline + + pipeline_run.show() # watch the pipeline run status + + ADS CLI ======= From f3fe3b12bbedc11c057cd82604a5f38251da72b3 Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:18:28 -0800 Subject: [PATCH 004/147] Release/2.8.x (#76) --- docs/source/user_guide/pipeline/examples.rst | 1146 ++++------------- docs/source/user_guide/pipeline/index.rst | 7 +- docs/source/user_guide/pipeline/pipeline.rst | 128 +- .../user_guide/pipeline/pipeline_step.rst | 203 ++- .../user_guide/pipeline/quick_start.rst | 208 +-- 5 files changed, 493 insertions(+), 1199 deletions(-) diff --git a/docs/source/user_guide/pipeline/examples.rst b/docs/source/user_guide/pipeline/examples.rst index 87e0d5516..688e679b0 100644 --- a/docs/source/user_guide/pipeline/examples.rst +++ b/docs/source/user_guide/pipeline/examples.rst @@ -6,87 +6,6 @@ Create a pipeline .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - pipeline_step = ( - PipelineStep("Python_Script_Step") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = ( - Pipeline("A single step pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step]) - ) - - pipeline.create() - .. code-tab:: Python3 :caption: YAML @@ -132,73 +51,19 @@ Create a pipeline pipeline = Pipeline.from_yaml(yaml_string) pipeline.create() - - -Run a job as a step -=================== - -.. tabs:: + .. code-tab:: Python3 :caption: Python - from ads.jobs import Job, DataScienceJob, ScriptRuntime - from ads.pipeline import PipelineStep, Pipeline - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = DataScienceJob( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - job = Job( - infrastructure=infrastructure, - runtime=runtime - ) - job.create() # create a job - - pipeline_step = PipelineStep( - name="Job_Step", - description="A step running a job", - job_id=job.id - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.jobs import Job, DataScienceJob, ScriptRuntime - from ads.pipeline import Pipeline, PipelineStep + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os with open("script.py", "w") as f: f.write("print('Hello World!')") infrastructure = ( - DataScienceJob() + CustomScriptStep() .with_block_storage_size(200) .with_shape_name("VM.Standard3.Flex") .with_shape_config_details(ocpus=4, memory_in_gbs=32) @@ -210,18 +75,12 @@ Run a job as a step .with_service_conda("generalml_p37_cpu_v1") ) - job = ( - Job() + pipeline_step = ( + PipelineStep("Python_Script_Step") + .with_description("A step running a python script") .with_infrastructure(infrastructure) .with_runtime(runtime) ) - job.create() # create a job - - pipeline_step = ( - PipelineStep("Job_Step") - .with_description("A step running a job") - .with_job_id(job.id) - ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] @@ -235,7 +94,13 @@ Run a job as a step pipeline.create() - pipeline_run = pipeline.run() + + + +Run a job as a step +=================== + +.. tabs:: .. code-tab:: Python3 :caption: YAML @@ -292,64 +157,18 @@ Run a job as a step pipeline_run = pipeline.run() -Run a python script as a step -============================= - -.. tabs:: - .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime + from ads.jobs import Job, DataScienceJob, ScriptRuntime + from ads.pipeline import Pipeline, PipelineStep import os with open("script.py", "w") as f: f.write("print('Hello World!')") infrastructure = ( - CustomScriptStep() + DataScienceJob() .with_block_storage_size(200) .with_shape_name("VM.Standard3.Flex") .with_shape_config_details(ocpus=4, memory_in_gbs=32) @@ -361,12 +180,18 @@ Run a python script as a step .with_service_conda("generalml_p37_cpu_v1") ) - pipeline_step = ( - PipelineStep("Python_Script_Step") - .with_description("A step running a python script") + job = ( + Job() .with_infrastructure(infrastructure) .with_runtime(runtime) ) + job.create() # create a job + + pipeline_step = ( + PipelineStep("Job_Step") + .with_description("A step running a job") + .with_job_id(job.id) + ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] @@ -382,6 +207,13 @@ Run a python script as a step pipeline_run = pipeline.run() + + +Run a python script as a step +============================= + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -430,59 +262,16 @@ Run a python script as a step pipeline_run = pipeline.run() - -Run a notebook as a step -======================== - -.. tabs:: .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime - import os - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os + with open("script.py", "w") as f: + f.write("print('Hello World!')") + infrastructure = ( CustomScriptStep() .with_block_storage_size(200) @@ -491,19 +280,14 @@ Run a notebook as a step ) runtime = ( - NotebookRuntime() - .with_notebook( - path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - encoding='utf-8' - ) - .with_service_conda("tensorflow26_p37_cpu_v2") - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_output("oci://@/") + ScriptRuntime() + .with_source("script.py") + .with_service_conda("generalml_p37_cpu_v1") ) pipeline_step = ( - PipelineStep("Notebook_Step") - .with_description("A step running a notebook") + PipelineStep("Python_Script_Step") + .with_description("A step running a python script") .with_infrastructure(infrastructure) .with_runtime(runtime) ) @@ -512,16 +296,23 @@ Run a notebook as a step project_id = os.environ["PROJECT_OCID"] pipeline = ( - Pipeline("A single step pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step]) - ) + Pipeline("A single step pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step]) + ) pipeline.create() pipeline_run = pipeline.run() + + +Run a notebook as a step +======================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -571,79 +362,13 @@ Run a notebook as a step pipeline.create() pipeline_run = pipeline.run() - - - -Run two steps with the same infrastructure -========================================== - -.. tabs:: - + .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - step_one_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "generalml_p37_cpu_v1"} - ) - - pipeline_step_one = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=step_one_runtime - ) - - step_two_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step_two = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=step_two_runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_one, pipeline_step_two], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime import os - with open("script.py", "w") as f: - f.write("print('Hello World!')") - infrastructure = ( CustomScriptStep() .with_block_storage_size(200) @@ -651,20 +376,7 @@ Run two steps with the same infrastructure .with_shape_config_details(ocpus=4, memory_in_gbs=32) ) - step_one_runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - pipeline_step_one = ( - PipelineStep("Python_Script_Step") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(step_one_runtime) - ) - - step_two_runtime = ( + runtime = ( NotebookRuntime() .with_notebook( path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", @@ -675,27 +387,34 @@ Run two steps with the same infrastructure .with_output("oci://@/") ) - pipeline_step_two = ( + pipeline_step = ( PipelineStep("Notebook_Step") .with_description("A step running a notebook") .with_infrastructure(infrastructure) - .with_runtime(step_two_runtime) + .with_runtime(runtime) ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] pipeline = ( - Pipeline("A single step pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step_one, pipeline_step_two]) - ) + Pipeline("A single step pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step]) + ) pipeline.create() pipeline_run = pipeline.run() + + +Run two steps with the same infrastructure +========================================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -751,83 +470,19 @@ Run two steps with the same infrastructure kind: runtime spec: conda: - slug: tensorflow26_p37_cpu_v2 - type: service - env: - - name: GREETINGS - value: Welcome to OCI Data Science - notebookEncoding: utf-8 - notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb - outputURI: oci://@/ - type: notebook - type: pipeline - """.format(compartment_id=compartment_id, project_id=project_id) - - pipeline = Pipeline.from_yaml(yaml_string) - - pipeline.create() - - pipeline_run = pipeline.run() - - - - -Run two steps in parallel -========================= - -In the example below, when DAG is not specified, the steps in the pipeline run in parallel. - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - step_one_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "generalml_p37_cpu_v1"} - ) - - pipeline_step_one = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=step_one_runtime - ) - - step_two_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step_two = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=step_two_runtime - ) - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] + slug: tensorflow26_p37_cpu_v2 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputURI: oci://@/ + type: notebook + type: pipeline + """.format(compartment_id=compartment_id, project_id=project_id) - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_one, pipeline_step_two], - ) + pipeline = Pipeline.from_yaml(yaml_string) pipeline.create() @@ -835,7 +490,7 @@ In the example below, when DAG is not specified, the steps in the pipeline run i .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime import os @@ -895,6 +550,16 @@ In the example below, when DAG is not specified, the steps in the pipeline run i pipeline_run = pipeline.run() + + + +Run two steps in parallel +========================= + +In the example below, when DAG is not specified, the steps in the pipeline run in parallel. + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -967,73 +632,11 @@ In the example below, when DAG is not specified, the steps in the pipeline run i pipeline.create() pipeline_run = pipeline.run() - - - -Run two steps sequentially -========================== - -.. tabs:: - + + .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - step_one_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "generalml_p37_cpu_v1"} - ) - - pipeline_step_one = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=step_one_runtime - ) - - step_two_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} - ) - - pipeline_step_two = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=step_two_runtime - ) - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_one, pipeline_step_two], - dag=["Python_Script_Step >> Notebook_Step"], - ) - - pipeline.create() - - pipeline_run = pipeline.run() - - - .. code-tab:: Python3 - :caption: Python (Alternative) - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime import os @@ -1086,13 +689,20 @@ Run two steps sequentially .with_compartment_id(compartment_id) .with_project_id(project_id) .with_step_details([pipeline_step_one, pipeline_step_two]) - .with_dag(["Python_Script_Step >> Notebook_Step"]) ) pipeline.create() pipeline_run = pipeline.run() + + + +Run two steps sequentially +========================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1168,83 +778,9 @@ Run two steps sequentially pipeline_run = pipeline.run() - -Run multiple steps with dependencies specified in DAG -===================================================== - -In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs after ``step_1`` and ``step_2`` are complete. - -.. tabs:: - .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - script_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - notebook_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step_1 = PipelineStep( - name="step_1", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - pipeline_step_2 = PipelineStep( - name="step_2", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=notebook_runtime - ) - - pipeline_step_3 = PipelineStep( - name="step_3", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="An example pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_1, pipeline_step_2, pipeline_step_3], - dag=["(step_1, step_2) >> step_3"], - ) - - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline - - pipeline_run = pipeline.run() # run the pipeline - - pipeline_run.show(wait=True) # watch the pipeline run status - - - - .. code-tab:: Python3 - :caption: Python (Alternative) - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime import os @@ -1258,61 +794,62 @@ In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs a .with_shape_config_details(ocpus=4, memory_in_gbs=32) ) - script_runtime = ( + step_one_runtime = ( ScriptRuntime() .with_source("script.py") .with_service_conda("generalml_p37_cpu_v1") ) - notebook_runtime = ( + pipeline_step_one = ( + PipelineStep("Python_Script_Step") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(step_one_runtime) + ) + + step_two_runtime = ( NotebookRuntime() .with_notebook( path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", encoding='utf-8' ) .with_service_conda("tensorflow26_p37_cpu_v2") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_output("oci://@/") ) - pipeline_step_1 = ( - PipelineStep("step_1") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) - ) - - pipeline_step_2 = ( - PipelineStep("step_2") + pipeline_step_two = ( + PipelineStep("Notebook_Step") .with_description("A step running a notebook") .with_infrastructure(infrastructure) - .with_runtime(notebook_runtime) - ) - - pipeline_step_3 = ( - PipelineStep("step_3") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) + .with_runtime(step_two_runtime) ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] pipeline = ( - Pipeline("An example pipeline") + Pipeline("A single step pipeline") .with_compartment_id(compartment_id) .with_project_id(project_id) - .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) - .with_dag(["(step_1, step_2) >> step_3"]) + .with_step_details([pipeline_step_one, pipeline_step_two]) + .with_dag(["Python_Script_Step >> Notebook_Step"]) ) - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline + pipeline.create() - pipeline_run = pipeline.run() # run the pipeline + pipeline_run = pipeline.run() - pipeline_run.show(wait=True) # watch the pipeline run status + +Run multiple steps with dependencies specified in DAG +===================================================== + +In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs after ``step_1`` and ``step_2`` are complete. + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1408,46 +945,128 @@ In this example, ``step_1`` and ``step_2`` run in parallel and ``step_3`` runs a pipeline_run.show(wait=True) # watch the pipeline run status -Set environment variables in a step -=================================== - -.. tabs:: - .. code-tab:: Python3 :caption: Python - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime - import os + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime + import os + + with open("script.py", "w") as f: + f.write("print('Hello World!')") + + infrastructure = ( + CustomScriptStep() + .with_block_storage_size(200) + .with_shape_name("VM.Standard3.Flex") + .with_shape_config_details(ocpus=4, memory_in_gbs=32) + ) + + script_runtime = ( + ScriptRuntime() + .with_source("script.py") + .with_service_conda("generalml_p37_cpu_v1") + ) + + notebook_runtime = ( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow26_p37_cpu_v2") + ) - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, + pipeline_step_1 = ( + PipelineStep("step_1") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) ) - runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"}, - output_uri="oci://@/", - env={"GREETINGS": "Welcome to OCI Data Science"} + pipeline_step_2 = ( + PipelineStep("step_2") + .with_description("A step running a notebook") + .with_infrastructure(infrastructure) + .with_runtime(notebook_runtime) ) - pipeline_step = PipelineStep( - name="Notebook_Step", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=runtime + pipeline_step_3 = ( + PipelineStep("step_3") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) ) compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) + pipeline = ( + Pipeline("An example pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) + .with_dag(["(step_1, step_2) >> step_3"]) + ) + + pipeline.create() # create the pipeline + pipeline.show() # visualize the pipeline + + pipeline_run = pipeline.run() # run the pipeline + + pipeline_run.show(wait=True) # watch the pipeline run status + + + +Set environment variables in a step +=================================== + +.. tabs:: + + .. code-tab:: Python3 + :caption: YAML + + from ads.pipeline import Pipeline + import os + + compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] + project_id = os.environ["PROJECT_OCID"] + + yaml_string = """ + kind: pipeline + spec: + compartmentId: {compartment_id} + displayName: A single step pipeline + projectId: {project_id} + stepDetails: + - kind: customScript + spec: + description: A step running a notebook + infrastructure: + kind: infrastructure + spec: + blockStorageSize: 200 + shapeConfigDetails: + memoryInGBs: 32 + ocpus: 4 + shapeName: VM.Standard3.Flex + name: Notebook_Step + runtime: + kind: runtime + spec: + conda: + slug: tensorflow26_p37_cpu_v2 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputURI: oci://@/ + type: notebook + type: pipeline + """.format(compartment_id=compartment_id, project_id=project_id) + + pipeline = Pipeline.from_yaml(yaml_string) pipeline.create() @@ -1455,7 +1074,7 @@ Set environment variables in a step .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, NotebookRuntime import os @@ -1499,6 +1118,14 @@ Set environment variables in a step pipeline_run = pipeline.run() + + + +Watch status update on a pipeline run +===================================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1508,6 +1135,9 @@ Set environment variables in a step compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] project_id = os.environ["PROJECT_OCID"] + with open("script.py", "w") as f: + f.write("print('Hello World!')") + yaml_string = """ kind: pipeline spec: @@ -1517,7 +1147,7 @@ Set environment variables in a step stepDetails: - kind: customScript spec: - description: A step running a notebook + description: A step running a python script infrastructure: kind: infrastructure spec: @@ -1526,82 +1156,28 @@ Set environment variables in a step memoryInGBs: 32 ocpus: 4 shapeName: VM.Standard3.Flex - name: Notebook_Step + name: Python_Script_Step runtime: kind: runtime spec: conda: - slug: tensorflow26_p37_cpu_v2 + slug: generalml_p37_cpu_v1 type: service - env: - - name: GREETINGS - value: Welcome to OCI Data Science - notebookEncoding: utf-8 - notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb - outputURI: oci://@/ - type: notebook + scriptPathURI: script.py + type: script type: pipeline """.format(compartment_id=compartment_id, project_id=project_id) pipeline = Pipeline.from_yaml(yaml_string) - pipeline.create() - - pipeline_run = pipeline.run() - - - -Watch status update on a pipeline run -===================================== - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - pipeline.create() pipeline_run = pipeline.run() - + # pipeline_run.show(mode="text") # watch pipeline run status in text - pipeline_run.show(wait=True) # watch pipeline run status in graph - + pipeline_run.show(wait=True) # watch pipeline run status in graph .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os @@ -1646,6 +1222,12 @@ Watch status update on a pipeline run pipeline_run.show(wait=True) # watch pipeline run status in graph + +Monitor logs of a pipeline run +============================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1692,67 +1274,13 @@ Watch status update on a pipeline run pipeline.create() pipeline_run = pipeline.run() - - # pipeline_run.show(mode="text") # watch pipeline run status in text - pipeline_run.show(wait=True) # watch pipeline run status in graph - - - - -Monitor logs of a pipeline run -============================== - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - ) - - pipeline.create() - pipeline_run = pipeline.run() # pipeline_run.watch() # stream the consolidated log of the pipeline run pipeline_run.watch(log_type="service_log") # stream service log of the pipeline run pipeline_run.watch("Python_Script_Step", log_type="custom_log") # stream custom log of the step run - .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os @@ -1797,6 +1325,14 @@ Monitor logs of a pipeline run pipeline_run.watch(log_type="service_log") # stream service log of the pipeline run pipeline_run.watch("Python_Script_Step", log_type="custom_log") # stream custom log of the step run + + + +Override configurations when creating a pipeline run +==================================================== + +.. tabs:: + .. code-tab:: Python3 :caption: YAML @@ -1812,6 +1348,9 @@ Monitor logs of a pipeline run yaml_string = """ kind: pipeline spec: + commandLineArguments: argument --key value + environmentVariables: + env: value compartmentId: {compartment_id} displayName: A single step pipeline projectId: {project_id} @@ -1841,58 +1380,6 @@ Monitor logs of a pipeline run pipeline = Pipeline.from_yaml(yaml_string) - pipeline.create() - pipeline_run = pipeline.run() - - # pipeline_run.watch() # stream the consolidated log of the pipeline run - pipeline_run.watch(log_type="service_log") # stream service log of the pipeline run - pipeline_run.watch("Python_Script_Step", log_type="custom_log") # stream custom log of the step run - - -Override configurations when creating a pipeline run -==================================================== - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step = PipelineStep( - name="Python_Script_Step", - description="A step running a python script", - infrastructure=infrastructure, - runtime=runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="A single step pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step], - command_line_arguments="argument --key value", - environment_variables={"env": "value"}, - ) - pipeline.create() # Override configurations when creating a pipeline run @@ -1919,12 +1406,10 @@ Override configurations when creating a pipeline run configuration_override_details=configuration_override_details, step_override_details=step_override_details, ) - - - + .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime import os @@ -1993,80 +1478,7 @@ Override configurations when creating a pipeline run - .. code-tab:: Python3 - :caption: YAML - - from ads.pipeline import Pipeline - import os - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - yaml_string = """ - kind: pipeline - spec: - commandLineArguments: argument --key value - environmentVariables: - env: value - compartmentId: {compartment_id} - displayName: A single step pipeline - projectId: {project_id} - stepDetails: - - kind: customScript - spec: - description: A step running a python script - infrastructure: - kind: infrastructure - spec: - blockStorageSize: 200 - shapeConfigDetails: - memoryInGBs: 32 - ocpus: 4 - shapeName: VM.Standard3.Flex - name: Python_Script_Step - runtime: - kind: runtime - spec: - conda: - slug: generalml_p37_cpu_v1 - type: service - scriptPathURI: script.py - type: script - type: pipeline - """.format(compartment_id=compartment_id, project_id=project_id) - - pipeline = Pipeline.from_yaml(yaml_string) - - pipeline.create() - - # Override configurations when creating a pipeline run - display_override_name = "RunOverrideName" - configuration_override_details = { - "maximum_runtime_in_minutes": 30, - "type": "DEFAULT", - "environment_variables": {"a": "b"}, - "command_line_arguments": "ARGUMENT --KEY VALUE", - } - step_override_details = [ - { - "step_name": "Python_Script_Step", - "step_configuration_details": { - "maximum_runtime_in_minutes": 200, - "environment_variables": {"1": "2"}, - "command_line_arguments": "argument --key value", - }, - } - ] - pipeline_run = pipeline.run( - display_name=display_override_name, - configuration_override_details=configuration_override_details, - step_override_details=step_override_details, - ) - diff --git a/docs/source/user_guide/pipeline/index.rst b/docs/source/user_guide/pipeline/index.rst index 8fc200f8d..dad01d7a1 100644 --- a/docs/source/user_guide/pipeline/index.rst +++ b/docs/source/user_guide/pipeline/index.rst @@ -1,8 +1,9 @@ .. _pipeline: -######### -Pipelines -######### + +###################### +Data Science Pipelines +###################### .. versionadded:: 2.8.0 diff --git a/docs/source/user_guide/pipeline/pipeline.rst b/docs/source/user_guide/pipeline/pipeline.rst index 6794d6fe8..6abaf0e78 100644 --- a/docs/source/user_guide/pipeline/pipeline.rst +++ b/docs/source/user_guide/pipeline/pipeline.rst @@ -46,89 +46,6 @@ A ``Pipeline`` instance will be created. .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PipelineStep, Pipeline - - pipeline_step_one = PipelineStep( - name="", - description="", - job_id="" - ) - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - runtime = ScriptRuntime( - script_path_uri=oci://@//", - conda={"type": "service", "slug": ""} - ) - - pipeline_step_two = PipelineStep( - name="", - description="", - infrastructure=infrastructure, - runtime=runtime, - ) - - pipeline = Pipeline( - name="", - compartment_id="", - project_id="", - log_group_id="", - log_id="", - enable_service_log=True, # to stream service log in pipeline runs - step_details=[pipeline_step_one, pipeline_step_two], - dag=["pipeline_step_name_1 >> pipeline_step_name_2"], - ) - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import PipelineStep, Pipeline - - pipeline_step_one = ( - PipelineStep("") - .with_description("") - .with_job_id("") - ) - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - runtime = ( - ScriptRuntime() - .with_source("oci://@//") - .with_service_conda("") - ) - - pipeline_step_two = ( - PipelineStep("") - .with_description("") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - - pipeline = ( - Pipeline("") - .with_compartment_id("") - .with_project_id("") - .with_log_group_id("") - .with_log_id("") - .with_enable_service_log(True) # to stream service log in pipeline runs - .with_step_details([pipeline_step_one, pipeline_step_two]) - .with_dag(["pipeline_step_name_1 >> pipeline_step_name_2"]) - ) - - .. code-tab:: Python3 :caption: YAML @@ -177,6 +94,51 @@ A ``Pipeline`` instance will be created. """ pipeline = Pipeline.from_yaml(yaml_string) + + + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import PipelineStep, Pipeline + + pipeline_step_one = ( + PipelineStep("") + .with_description("") + .with_job_id("") + ) + + infrastructure = ( + CustomScriptStep() + .with_block_storage_size(200) + .with_shape_name("VM.Standard3.Flex") + .with_shape_config_details(ocpus=4, memory_in_gbs=32) + ) + + runtime = ( + ScriptRuntime() + .with_source("oci://@//") + .with_service_conda("") + ) + + pipeline_step_two = ( + PipelineStep("") + .with_description("") + .with_infrastructure(infrastructure) + .with_runtime(runtime) + ) + + pipeline = ( + Pipeline("") + .with_compartment_id("") + .with_project_id("") + .with_log_group_id("") + .with_log_id("") + .with_enable_service_log(True) # to stream service log in pipeline runs + .with_step_details([pipeline_step_one, pipeline_step_two]) + .with_dag(["pipeline_step_name_1 >> pipeline_step_name_2"]) + ) + + Create diff --git a/docs/source/user_guide/pipeline/pipeline_step.rst b/docs/source/user_guide/pipeline/pipeline_step.rst index 472d2332b..b44488b30 100644 --- a/docs/source/user_guide/pipeline/pipeline_step.rst +++ b/docs/source/user_guide/pipeline/pipeline_step.rst @@ -17,28 +17,6 @@ Create a Data Science Job step with the OCID of an existing Job. .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PipelineStep - - pipeline_step = PipelineStep( - name="", - description="", - job_id="" - ) - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import PipelineStep - - pipeline_step = ( - PipelineStep("") - .with_description("") - .with_job_id("") - ) - .. code-tab:: YAML kind: pipeline @@ -53,7 +31,17 @@ Create a Data Science Job step with the OCID of an existing Job. name: ... type: pipeline + + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import PipelineStep + pipeline_step = ( + PipelineStep("") + .with_description("") + .with_job_id("") + ) Custom Script Step @@ -67,20 +55,30 @@ When constructing a Custom Scrip step ``infrastructure``, you specify the Comput .. tabs:: - .. code-tab:: Python3 - :caption: Python + .. code-tab:: YAML - from ads.pipeline import CustomScriptStep - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) + kind: pipeline + spec: + ... + stepDetails: + - kind: customScript + spec: + infrastructure: + kind: infrastructure + spec: + blockStorageSize: 200 + shapeConfigDetails: + memoryInGBs: 32 + ocpus: 4 + shapeName: VM.Standard3.Flex + name: Python_Script_Step + ... + type: pipeline .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import CustomScriptStep @@ -104,25 +102,26 @@ To define a Custom Script step with ``GitPythonRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import GitPythonRuntime + .. code-tab:: YAML - runtime = GitPythonRuntime( - env={"GREETINGS": "Welcome to OCI Data Science"} - conda={"type": "service", "slug": "pytorch19_p37_gpu_v1"} - url="https://github.com/pytorch/tutorials.git", - entrypoint="beginner_source/examples_nn/polynomial_nn.py", - output_dir="~/Code/tutorials/beginner_source/examples_nn", - outputURI="oci://@/", - ) + kind: runtime + spec: + conda: + slug: pytorch19_p37_gpu_v1 + type: service + entrypoint: beginner_source/examples_nn/polynomial_nn.py + env: + - name: GREETINGS + value: Welcome to OCI Data Science + outputDir: ~/Code/tutorials/beginner_source/examples_nn + outputUri: oci://@/ + url: https://github.com/pytorch/tutorials.git + type: gitPython .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python - from ads.pipeline import GitPythonRuntime runtime = ( @@ -142,22 +141,23 @@ To define a Custom Script step with ``NotebookRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import NotebookRuntime - - runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - notebook_encoding="utf-8", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - env={"GREETINGS": "Welcome to OCI Data Science"} - outputURI="oci://@/", - ) + .. code-tab:: YAML + kind: runtime + spec: + conda: + slug: tensorflow26_p37_cpu_v2 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputURI: oci://bucket_name@namespace/path/to/dir + type: notebook .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import NotebookRuntime @@ -177,23 +177,24 @@ To define a Custom Script step with ``PythonRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PythonRuntime - - runtime = PythonRuntime( - script_path_uri="local/path/to/zip_or_dir", - entrypoint="zip_or_dir/my_package/entry.py", - working_dir="zip_or_dir", - python_path=["my_python_packages"], - output_uri="oci://@/", - conda={"type": "service", "slug": "pytorch19_p37_cpu_v1"} - ) + .. code-tab:: YAML + kind: runtime + spec: + conda: + slug: pytorch19_p37_cpu_v1 + type: service + entrypoint: zip_or_dir/my_package/entry.py + outputDir: output + outputUri: oci://bucket_name@namespace/path/to/dir + pythonPath: + - my_python_packages + scriptPathURI: local/path/to/zip_or_dir + workingDir: zip_or_dir + type: python .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import PythonRuntime @@ -218,19 +219,18 @@ To define a Custom Script step with ``ScriptRuntime`` you can use: .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import ScriptRuntime - - runtime = ScriptRuntime( - script_path_uri="oci://@//", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - + .. code-tab:: YAML + + kind: runtime + spec: + conda: + slug: tensorflow26_p37_cpu_v2 + type: service + scriptPathURI: oci://@// + type: script .. code-tab:: Python3 - :caption: Python (Alternative) + :caption: Python from ads.pipeline import ScriptRuntime @@ -244,32 +244,6 @@ With ``Infrastructure`` and ``runtime`` provided, create a pipeline step of the .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import PipelineStep - - pipeline_step = PipelineStep( - name="", - description="", - infrastructure=infrastructure, - runtime=runtime, - ) - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import PipelineStep - - pipeline_step = ( - PipelineStep("") - .with_description("") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - - .. code-tab:: YAML kind: pipeline @@ -299,6 +273,17 @@ With ``Infrastructure`` and ``runtime`` provided, create a pipeline step of the type: script ... type: pipeline + + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import PipelineStep + pipeline_step = ( + PipelineStep("") + .with_description("") + .with_infrastructure(infrastructure) + .with_runtime(runtime) + ) diff --git a/docs/source/user_guide/pipeline/quick_start.rst b/docs/source/user_guide/pipeline/quick_start.rst index 05ce53de2..b335ff894 100644 --- a/docs/source/user_guide/pipeline/quick_start.rst +++ b/docs/source/user_guide/pipeline/quick_start.rst @@ -15,143 +15,6 @@ The following example shows creating and runnning a pipeline with multiple steps .. tabs:: - .. code-tab:: Python3 - :caption: Python - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = CustomScriptStep( - block_storage_size=200, - shape_name="VM.Standard3.Flex", - shape_config_details={"ocpus": 4, "memory_in_gbs": 32}, - ) - - script_runtime = ScriptRuntime( - script_path_uri="script.py", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - notebook_runtime = NotebookRuntime( - notebook_path_uri="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - conda={"type": "service", "slug": "tensorflow26_p37_cpu_v2"} - ) - - pipeline_step_1 = PipelineStep( - name="step_1", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - pipeline_step_2 = PipelineStep( - name="step_2", - description="A step running a notebook", - infrastructure=infrastructure, - runtime=notebook_runtime - ) - - pipeline_step_3 = PipelineStep( - name="step_3", - description="A step running a python script", - infrastructure=infrastructure, - runtime=script_runtime - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = Pipeline( - name="An example pipeline", - compartment_id=compartment_id, - project_id=project_id, - step_details=[pipeline_step_1, pipeline_step_2, pipeline_step_3], - dag=["(step_1, step_2) >> step_3"], - ) - - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline - - pipeline_run = pipeline.run() # run the pipeline - - pipeline_run.show() # watch the pipeline run status - - - - .. code-tab:: Python3 - :caption: Python (Alternative) - - from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime - import os - - with open("script.py", "w") as f: - f.write("print('Hello World!')") - - infrastructure = ( - CustomScriptStep() - .with_block_storage_size(200) - .with_shape_name("VM.Standard3.Flex") - .with_shape_config_details(ocpus=4, memory_in_gbs=32) - ) - - script_runtime = ( - ScriptRuntime() - .with_source("script.py") - .with_service_conda("generalml_p37_cpu_v1") - ) - - notebook_runtime = ( - NotebookRuntime() - .with_notebook( - path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - encoding='utf-8' - ) - .with_service_conda("tensorflow26_p37_cpu_v2") - ) - - pipeline_step_1 = ( - PipelineStep("step_1") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) - ) - - pipeline_step_2 = ( - PipelineStep("step_2") - .with_description("A step running a notebook") - .with_infrastructure(infrastructure) - .with_runtime(notebook_runtime) - ) - - pipeline_step_3 = ( - PipelineStep("step_3") - .with_description("A step running a python script") - .with_infrastructure(infrastructure) - .with_runtime(script_runtime) - ) - - compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] - project_id = os.environ["PROJECT_OCID"] - - pipeline = ( - Pipeline("An example pipeline") - .with_compartment_id(compartment_id) - .with_project_id(project_id) - .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) - .with_dag(["(step_1, step_2) >> step_3"]) - ) - - pipeline.create() # create the pipeline - pipeline.show() # visualize the pipeline - - pipeline_run = pipeline.run() # run the pipeline - - pipeline_run.show() # watch the pipeline run status - - .. code-tab:: Python3 :caption: YAML @@ -247,6 +110,77 @@ The following example shows creating and runnning a pipeline with multiple steps pipeline_run.show() # watch the pipeline run status + .. code-tab:: Python3 + :caption: Python + + from ads.pipeline import Pipeline, PipelineStep, CustomScriptStep, ScriptRuntime, NotebookRuntime + import os + + with open("script.py", "w") as f: + f.write("print('Hello World!')") + + infrastructure = ( + CustomScriptStep() + .with_block_storage_size(200) + .with_shape_name("VM.Standard3.Flex") + .with_shape_config_details(ocpus=4, memory_in_gbs=32) + ) + + script_runtime = ( + ScriptRuntime() + .with_source("script.py") + .with_service_conda("generalml_p37_cpu_v1") + ) + + notebook_runtime = ( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow26_p37_cpu_v2") + ) + + pipeline_step_1 = ( + PipelineStep("step_1") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) + ) + + pipeline_step_2 = ( + PipelineStep("step_2") + .with_description("A step running a notebook") + .with_infrastructure(infrastructure) + .with_runtime(notebook_runtime) + ) + + pipeline_step_3 = ( + PipelineStep("step_3") + .with_description("A step running a python script") + .with_infrastructure(infrastructure) + .with_runtime(script_runtime) + ) + + compartment_id = os.environ['NB_SESSION_COMPARTMENT_OCID'] + project_id = os.environ["PROJECT_OCID"] + + pipeline = ( + Pipeline("An example pipeline") + .with_compartment_id(compartment_id) + .with_project_id(project_id) + .with_step_details([pipeline_step_1, pipeline_step_2, pipeline_step_3]) + .with_dag(["(step_1, step_2) >> step_3"]) + ) + + pipeline.create() # create the pipeline + pipeline.show() # visualize the pipeline + + pipeline_run = pipeline.run() # run the pipeline + + pipeline_run.show() # watch the pipeline run status + + ADS CLI ======= From 70f10b06b56dec5a85deaf7bf15d2bd1d0cbbd2e Mon Sep 17 00:00:00 2001 From: Dmitrii Cherkasov Date: Sat, 11 Feb 2023 11:27:37 -0800 Subject: [PATCH 005/147] Improves DataFlow user guide with the new features. --- docs/source/user_guide/apachespark/dataflow.rst | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/apachespark/dataflow.rst b/docs/source/user_guide/apachespark/dataflow.rst index 9be187a7f..75a9a7bf7 100644 --- a/docs/source/user_guide/apachespark/dataflow.rst +++ b/docs/source/user_guide/apachespark/dataflow.rst @@ -36,6 +36,7 @@ Define config. If you have not yet configured your dataflow setting, or would li dataflow_config.logs_bucket_uri = "oci://@/" dataflow_config.spark_version = "3.2.1" dataflow_config.configuration = {"spark.driver.memory": "512m"} + dataflow_config.private_endpoint_id = "ocid1.dataflowprivateendpoint.oc1.iad." Use the config defined above to submit the cell. @@ -159,6 +160,7 @@ You could submit a notebook using ADS SDK APIs. Here is an example to submit a n .with_executor_shape("VM.Standard.E4.Flex") .with_executor_shape_config(ocpus=4, memory_in_gbs=64) .with_logs_bucket_uri("oci://mybucket@mytenancy/") + .with_private_endpoint_id("ocid1.dataflowprivateendpoint.oc1.iad.") ) rt = ( DataFlowNotebookRuntime() @@ -167,6 +169,7 @@ You could submit a notebook using ADS SDK APIs. Here is an example to submit a n ) # This could be local path or http path to notebook ipynb file .with_script_bucket("") .with_exclude_tag(["ignore", "remove"]) # Cells to Ignore + .with_environment_variable(env1="test", env2= "test2") # will be propagated to both driver and executor ) job = Job(infrastructure=df, runtime=rt).create(overwrite=True) df_run = job.run(wait=True) @@ -197,6 +200,7 @@ You can set them using the ``with_{property}`` functions: - ``with_num_executors`` - ``with_spark_version`` - ``with_warehouse_bucket_uri`` +- ``with_private_endpoint_id`` (`doc `__) For more details, see `DataFlow class documentation `__. @@ -209,6 +213,7 @@ The ``DataFlowRuntime`` properties are: - ``with_archive_uri`` (`doc `__) - ``with_archive_bucket`` - ``with_custom_conda`` +- ``with_environment_variable`` For more details, see the `runtime class documentation <../../ads.jobs.html#module-ads.jobs.builders.runtimes.python_runtime>`__. @@ -217,7 +222,7 @@ object can be reused and combined with various ``DataFlowRuntime`` parameters to create applications. In the following "hello-world" example, ``DataFlow`` is populated with ``compartment_id``, -``driver_shape``, ``driver_shape_config``, ``executor_shape``, ``executor_shape_config`` +``driver_shape``, ``driver_shape_config``, ``executor_shape``, ``executor_shape_config`` and ``spark_version``. ``DataFlowRuntime`` is populated with ``script_uri`` and ``script_bucket``. The ``script_uri`` specifies the path to the script. It can be local or remote (an Object Storage path). If the path is local, then @@ -267,6 +272,7 @@ accepted. In the next example, the prefix is given for ``script_bucket``. .with_script_uri(os.path.join(td, "script.py")) .with_script_bucket("oci://mybucket@namespace/prefix") .with_custom_conda("oci://@/") + .with_environment_variable(env1="test", env2= "test2") # will be propagated to both driver and executor ) df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) df.create() @@ -545,7 +551,8 @@ into the ``Job.from_yaml()`` function to build a Data Flow job: language: PYTHON logsBucketUri: numExecutors: 1 - sparkVersion: 2.4.4 + sparkVersion: 3.2.1 + privateEndpointId: type: dataFlow name: dataflow_app_name runtime: @@ -553,6 +560,9 @@ into the ``Job.from_yaml()`` function to build a Data Flow job: spec: scriptBucket: bucket_name scriptPathURI: oci://@/ + env: + - name: env1 + value: test1 type: dataFlow **Data Flow Infrastructure YAML Schema** @@ -618,6 +628,9 @@ into the ``Job.from_yaml()`` function to build a Data Flow job: sparkVersion: required: false type: string + privateEndpointId: + required: false + type: string type: allowed: - dataFlow From b82eeb783a13bedbeb506f354ec3c7bb5ab57fc7 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 14 Feb 2023 12:35:41 -0500 Subject: [PATCH 006/147] Update jobs index.rst and toc.rst --- docs/source/user_guide/jobs/index.rst | 47 ++++++++++++++++++--------- docs/source/user_guide/jobs/toc.rst | 12 +++++++ 2 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 docs/source/user_guide/jobs/toc.rst diff --git a/docs/source/user_guide/jobs/index.rst b/docs/source/user_guide/jobs/index.rst index 526643e6a..1077c65cf 100644 --- a/docs/source/user_guide/jobs/index.rst +++ b/docs/source/user_guide/jobs/index.rst @@ -1,21 +1,36 @@ .. _jobs-1: ################# -Data Science Jobs +Training with OCI ################# -Oracle Cloud Infrastructure (OCI) Data Science jobs enable you to define and run a repeatable machine learning task on a fully managed infrastructure, such as **data preparation, model training, hyperparameter optimization, batch inference, and so on**. - -.. toctree:: - :maxdepth: 1 - - overview - data_science_job - run_container - run_git - run_notebook - run_script - run_zip - ../cli/opctl/_template/jobs - ../cli/opctl/_template/monitoring - ../cli/opctl/localdev/local_jobs +Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `__ +enables you to define and run repeatable machine learning tasks on a fully managed infrastructure. +You can have Compute resource on demand and run applications that perform tasks such as +data preparation, model training, hyperparameter tuning, and batch inference. + +Training with OCI involves two types resources: **Job** and **Job Run**. + +A **Job** is a template that describes the training task. It contains configurations about the *infrastructure*, such as +`Compute Shape `__, +`Block Storage `__, +`Logging `__, +and information about the *runtime*, such as the source code of your training workload, environment variables, and CLI arguments. + +A **Job Run** is an instantiation of a job. In each job run, you can override some of the job configurations, +such as environment variables and CLI arguments. +You can use the same job as a template and launch multiple simultaneous job runs to parallelize a large task. +You can also sequence jobs and keep the state by writing state information to +`Object Storage `__ + +For example, you may want to experiment with how different model classes perform on the same training data +by using the ADSTuner to perform hyperparameter tuning on each model class. +You could do this in parallel by having a different job run for each class of models. +For a given job run, you could pass an environment variable that identifies the model class that you want to use. +Each model cab write its results to the Logging service or Object Storage. +Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. + +The following sections provides details on running training workloads with OCI Data Science Jobs using ADS Jobs APIs. +You can use similar APIs to `Run a OCI DataFlow Application `__. + +.. include:: ../jobs/toc.rst diff --git a/docs/source/user_guide/jobs/toc.rst b/docs/source/user_guide/jobs/toc.rst new file mode 100644 index 000000000..2f4d21705 --- /dev/null +++ b/docs/source/user_guide/jobs/toc.rst @@ -0,0 +1,12 @@ +.. toctree:: + :maxdepth: 1 + + ../jobs/data_science_job + ../jobs/run_container + ../jobs/run_git + ../jobs/run_notebook + ../jobs/run_script + ../jobs/run_zip + ../cli/opctl/_template/jobs + ../cli/opctl/_template/monitoring + ../cli/opctl/localdev/local_jobs From 5a615aae8170abf24b421bce63e523b193b95bb5 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 14 Feb 2023 12:36:13 -0500 Subject: [PATCH 007/147] Add jobs section to train models. --- docs/source/user_guide/model_training/index.rst | 1 + docs/source/user_guide/model_training/training_with_oci.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 docs/source/user_guide/model_training/training_with_oci.rst diff --git a/docs/source/user_guide/model_training/index.rst b/docs/source/user_guide/model_training/index.rst index 2f207b2de..168774710 100644 --- a/docs/source/user_guide/model_training/index.rst +++ b/docs/source/user_guide/model_training/index.rst @@ -18,6 +18,7 @@ TensorBoard provides the visualization and the tooling that is needed to watch a :maxdepth: 1 ads_tuner + training_with_oci distributed_training/overview tensorboard/tensorboard model_evaluation/index diff --git a/docs/source/user_guide/model_training/training_with_oci.rst b/docs/source/user_guide/model_training/training_with_oci.rst new file mode 100644 index 000000000..f9e99ed58 --- /dev/null +++ b/docs/source/user_guide/model_training/training_with_oci.rst @@ -0,0 +1 @@ +.. include:: ../jobs/index.rst From 89ac1e21dc791a1809e3e8ab75d7e4a5b6361dad Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 14 Feb 2023 13:07:25 -0500 Subject: [PATCH 008/147] Update jobs index.rst --- docs/source/user_guide/jobs/index.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/source/user_guide/jobs/index.rst b/docs/source/user_guide/jobs/index.rst index 1077c65cf..eb853cfc3 100644 --- a/docs/source/user_guide/jobs/index.rst +++ b/docs/source/user_guide/jobs/index.rst @@ -4,24 +4,26 @@ Training with OCI ################# -Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `__ +Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `_ enables you to define and run repeatable machine learning tasks on a fully managed infrastructure. You can have Compute resource on demand and run applications that perform tasks such as data preparation, model training, hyperparameter tuning, and batch inference. Training with OCI involves two types resources: **Job** and **Job Run**. -A **Job** is a template that describes the training task. It contains configurations about the *infrastructure*, such as -`Compute Shape `__, -`Block Storage `__, -`Logging `__, -and information about the *runtime*, such as the source code of your training workload, environment variables, and CLI arguments. +A **Job** is a template that describes the training task. +It contains configurations about the *infrastructure*, such as +`Compute Shape `_, +`Block Storage `_, +`Logging `_, +and information about the *runtime*, +such as the source code of your training workload, environment variables, and CLI arguments. -A **Job Run** is an instantiation of a job. In each job run, you can override some of the job configurations, -such as environment variables and CLI arguments. +A **Job Run** is an instantiation of a job. +In each job run, you can override some of the job configurations, such as environment variables and CLI arguments. You can use the same job as a template and launch multiple simultaneous job runs to parallelize a large task. You can also sequence jobs and keep the state by writing state information to -`Object Storage `__ +`Object Storage `_ For example, you may want to experiment with how different model classes perform on the same training data by using the ADSTuner to perform hyperparameter tuning on each model class. @@ -31,6 +33,6 @@ Each model cab write its results to the Logging service or Object Storage. Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. The following sections provides details on running training workloads with OCI Data Science Jobs using ADS Jobs APIs. -You can use similar APIs to `Run a OCI DataFlow Application `__. +You can use similar APIs to `Run a OCI DataFlow Application `_. .. include:: ../jobs/toc.rst From a47a918847bc10184296e55f51d84ce0dbba3d4b Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 14 Feb 2023 13:07:56 -0500 Subject: [PATCH 009/147] Add jobs policies docs --- docs/source/user_guide/jobs/policies.rst | 50 ++++++++++++++++++++++++ docs/source/user_guide/jobs/toc.rst | 1 + 2 files changed, 51 insertions(+) create mode 100644 docs/source/user_guide/jobs/policies.rst diff --git a/docs/source/user_guide/jobs/policies.rst b/docs/source/user_guide/jobs/policies.rst new file mode 100644 index 000000000..b5c388177 --- /dev/null +++ b/docs/source/user_guide/jobs/policies.rst @@ -0,0 +1,50 @@ +Data Science Jobs Policies +************************** + +.. admonition:: Policy subject + + In the following example, ``group `` is the subject of the policy + when using OCI API keys for authentication. For resource principal authentication, + the subject should be a ``dynamic-group``, for example, ``dynamic-group `` + +Here is an example defining a dynamic group for all job runs in a compartment: + +.. code-block:: + + all { resource.type='datasciencejobrun', resource.compartment.id='' } + +The following policies are for creating and managing Data Science Jobs and Job Runs: + +.. code-block:: + + Allow group to manage data-science-jobs in compartment + Allow group to manage data-science-job-runs in compartment + Allow group to use virtual-network-family in compartment + Allow group to manage log-groups in compartment + Allow group to use logging-family in compartment + Allow group to use read metrics in compartment + +The following policies are for job runs to access other OCI resources: + +.. code-block:: + + Allow dynamic-group to read repos in compartment + Allow dynamic-group to use data-science-family in compartment + Allow dynamic-group to use virtual-network-family in compartment + Allow dynamic-group to use log-groups in compartment + Allow dynamic-group to use logging-family in compartment + Allow dynamic-group to manage objects in compartment where all {target.bucket.name=} + Allow dynamic-group to use buckets in compartment where all {target.bucket.name=} + +The following policy is needed for running a container job: + +.. code-block:: + + Allow dynamic-group to read repos in compartment + +See also: + +* `Dynamic Group `_ +* `Data Science Policies `_ +* `Object Storage `_ +* `Container Registry `_ diff --git a/docs/source/user_guide/jobs/toc.rst b/docs/source/user_guide/jobs/toc.rst index 2f4d21705..400e9f64f 100644 --- a/docs/source/user_guide/jobs/toc.rst +++ b/docs/source/user_guide/jobs/toc.rst @@ -2,6 +2,7 @@ :maxdepth: 1 ../jobs/data_science_job + ../jobs/policies ../jobs/run_container ../jobs/run_git ../jobs/run_notebook From 3b2d2dffd968a2c901f842e59378cc992cf8d5a9 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 14 Feb 2023 14:10:35 -0800 Subject: [PATCH 010/147] ODSC-36246: replace generic model example with catboost model --- .../model_registration/quick_start.rst | 78 ++++++++++++++----- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 2c066cd44..617c2a769 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -13,6 +13,9 @@ Sklearn from sklearn.datasets import load_iris from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split + import ads + + ads.set_auth(auth="resource_principal") # Load dataset and Prepare train and test split iris = load_iris() @@ -55,6 +58,9 @@ Create a model, prepare it, verify that it works, save it to the model catalog, from sklearn.datasets import load_iris from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split + import ads + + ads.set_auth(auth="resource_principal") # Load dataset and Prepare train and test split iris = load_iris() @@ -93,6 +99,9 @@ Create a model, prepare it, verify that it works, save it to the model catalog, from ads.model.framework.lightgbm_model import LightGBMModel from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split + import ads + + ads.set_auth(auth="resource_principal") # Load dataset and Prepare train and test split iris = load_iris() @@ -135,6 +144,9 @@ Create a model, prepare it, verify that it works, save it to the model catalog, import torch import torchvision from ads.model.framework.pytorch_model import PyTorchModel + import ads + + ads.set_auth(auth="resource_principal") # Load a pre-trained resnet model torch_estimator = torchvision.models.resnet18(pretrained=True) @@ -172,6 +184,9 @@ Create a model, prepare it, verify that it works, save it to the model catalog, from pyspark.ml.classification import LogisticRegression from pyspark.ml.feature import HashingTF, Tokenizer from ads.model.framework.spark_model import SparkPipelineModel + import ads + + ads.set_auth(auth="resource_principal") spark = SparkSession \ .builder \ @@ -228,6 +243,9 @@ Create a model, prepare it, verify that it works, save it to the model catalog, from ads.model.framework.tensorflow_model import TensorFlowModel import tempfile import tensorflow as tf + import ads + + ads.set_auth(auth="resource_principal") mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() @@ -262,32 +280,50 @@ Other Frameworks .. code-block:: python3 - import tempfile from ads.model.generic_model import GenericModel + from catboost import CatBoostRegressor - # Create custom framework model - class Toy: - def predict(self, x): - return x ** 2 - model = Toy() + import tempfile + import ads + + ads.set_auth(auth="resource_principal") + + # Initialize data + + X_train = [[1, 4, 5, 6], + [4, 5, 6, 7], + [30, 40, 50, 60]] + + X_test = [[2, 4, 6, 8], + [1, 4, 50, 60]] + + y_train = [10, 20, 30] + # Initialize CatBoostRegressor + catboost_estimator = CatBoostRegressor(iterations=2, + learning_rate=1, + depth=2) + # Train a CatBoost Classifier model + catboost_estimator.fit(X_train, y_train) + # Get predictions + preds = catboost_estimator.predict(X_test) - # Instantite ads.model.generic_model.GenericModel using the trained Custom Model - generic_model = GenericModel(estimator=model, artifact_dir=tempfile.mkdtemp()) - generic_model.summary_status() + + # Instantite ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model + catboost_model = GenericModel(estimator=catboost_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json - generic_model.prepare( - inference_conda_env="dbexp_p38_cpu_v1", - model_file_name="toy_model.pkl", - force_overwrite=True - ) - - # Check if the artifacts are generated correctly. - # The verify method invokes the ``predict`` function defined inside ``score.py`` in the artifact_dir - generic_model.verify([2]) - - # Register the model - model_id = generic_model.save(display_name="Custom Framework Model") + catboost_model.prepare( + inference_conda_env="oci://bucket@namespace/path/to/your/conda/pack", + inference_python_version="your_python_version", + X_sample=X_train, + y_sample=y_train, + ) + + # Verify generated artifacts + catboost_model.verify(X_test, auto_serialize_data=True) + + # Register CatBoostRegressor model + model_id = catboost_model.save(display_name="CatBoost Model") With Model Version Set From e9e5474dc6879b48cf27afc8e75d3d1b0523358a Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 14 Feb 2023 14:22:37 -0800 Subject: [PATCH 011/147] ODSC-36246: fix the import order --- .../model_registration/quick_start.rst | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 617c2a769..1b8283dda 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -9,11 +9,13 @@ Sklearn .. code-block:: python3 import tempfile + + import ads from ads.model.framework.sklearn_model import SklearnModel from sklearn.datasets import load_iris from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split - import ads + ads.set_auth(auth="resource_principal") @@ -53,12 +55,14 @@ Create a model, prepare it, verify that it works, save it to the model catalog, .. code-block:: python3 import tempfile + + import ads import xgboost as xgb from ads.model.framework.xgboost_model import XGBoostModel from sklearn.datasets import load_iris from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split - import ads + ads.set_auth(auth="resource_principal") @@ -94,13 +98,15 @@ Create a model, prepare it, verify that it works, save it to the model catalog, .. code-block:: python3 - import lightgbm as lgb + import tempfile + + import ads + import lightgbm as lgb from ads.model.framework.lightgbm_model import LightGBMModel from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split - import ads - + ads.set_auth(auth="resource_principal") # Load dataset and Prepare train and test split @@ -141,10 +147,11 @@ Create a model, prepare it, verify that it works, save it to the model catalog, import tempfile + + import ads import torch import torchvision from ads.model.framework.pytorch_model import PyTorchModel - import ads ads.set_auth(auth="resource_principal") @@ -177,14 +184,15 @@ Create a model, prepare it, verify that it works, save it to the model catalog, .. code-block:: python3 - import tempfile import os - from pyspark.sql import SparkSession + import tempfile + + import ads + from ads.model.framework.spark_model import SparkPipelineModel from pyspark.ml import Pipeline from pyspark.ml.classification import LogisticRegression from pyspark.ml.feature import HashingTF, Tokenizer - from ads.model.framework.spark_model import SparkPipelineModel - import ads + from pyspark.sql import SparkSession ads.set_auth(auth="resource_principal") @@ -240,10 +248,12 @@ Create a model, prepare it, verify that it works, save it to the model catalog, .. code-block:: python3 - from ads.model.framework.tensorflow_model import TensorFlowModel import tempfile - import tensorflow as tf + import ads + import tensorflow as tf + from ads.model.framework.tensorflow_model import TensorFlowModel + ads.set_auth(auth="resource_principal") @@ -280,11 +290,12 @@ Other Frameworks .. code-block:: python3 + import tempfile + + import ads from ads.model.generic_model import GenericModel from catboost import CatBoostRegressor - import tempfile - import ads ads.set_auth(auth="resource_principal") @@ -298,16 +309,17 @@ Other Frameworks [1, 4, 50, 60]] y_train = [10, 20, 30] + # Initialize CatBoostRegressor catboost_estimator = CatBoostRegressor(iterations=2, learning_rate=1, depth=2) - # Train a CatBoost Classifier model + # Train a CatBoostRegressor model catboost_estimator.fit(X_train, y_train) + # Get predictions preds = catboost_estimator.predict(X_test) - # Instantite ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model catboost_model = GenericModel(estimator=catboost_estimator, artifact_dir=tempfile.mkdtemp()) From 57e91979e862346a309618ab78dfd8080c86eb0e Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 14 Feb 2023 14:39:18 -0800 Subject: [PATCH 012/147] fix previous example --- docs/source/user_guide/model_registration/quick_start.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 1b8283dda..47a831e93 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -368,7 +368,7 @@ With Model Version Set # Check if the artifacts are generated correctly. # The verify method invokes the ``predict`` function defined inside ``score.py`` in the artifact_dir - generic_model.verify([2]) + generic_model.verify(2) # Register the model model_id = generic_model.save(display_name="Custom Framework Model") From b79f0a1089c635827f951a85da1b358b6e8380eb Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 15 Feb 2023 14:32:53 -0800 Subject: [PATCH 013/147] ODSC-36246: fix based on the comments --- .../model_registration/model_deploy.rst | 2 +- .../model_registration/model_metadata.rst | 2 +- .../model_registration/model_schema.rst | 2 +- .../model_registration/quick_start.rst | 16 ++++++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/source/user_guide/model_registration/model_deploy.rst b/docs/source/user_guide/model_registration/model_deploy.rst index b48713690..2ab3d3fe7 100644 --- a/docs/source/user_guide/model_registration/model_deploy.rst +++ b/docs/source/user_guide/model_registration/model_deploy.rst @@ -23,7 +23,7 @@ Here is an example of deploying LightGBM model: } lightgbm_estimator = lgb.train(param, train) - # Instantite ads.model.LightGBMModel using the trained LGBM Model + # Instantiate ads.model.LightGBMModel using the trained LGBM Model lightgbm_model = LightGBMModel(estimator=lightgbm_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json diff --git a/docs/source/user_guide/model_registration/model_metadata.rst b/docs/source/user_guide/model_registration/model_metadata.rst index 43eb7a626..e23f833a5 100644 --- a/docs/source/user_guide/model_registration/model_metadata.rst +++ b/docs/source/user_guide/model_registration/model_metadata.rst @@ -82,7 +82,7 @@ You can populate the ``use_case_type`` by passing it in the ``.prepare()`` metho sklearn_estimator = LogisticRegression() sklearn_estimator.fit(X_train, y_train) - # Instantite ads.model.SklearnModel using the sklearn LogisticRegression model + # Instantiate ads.model.SklearnModel using the sklearn LogisticRegression model sklearn_model = SklearnModel(estimator=sklearn_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json diff --git a/docs/source/user_guide/model_registration/model_schema.rst b/docs/source/user_guide/model_registration/model_schema.rst index 24d434d46..108ea3c35 100644 --- a/docs/source/user_guide/model_registration/model_schema.rst +++ b/docs/source/user_guide/model_registration/model_schema.rst @@ -134,7 +134,7 @@ Eg. sklearn_estimator = LogisticRegression() sklearn_estimator.fit(X_train, y_train) - # Instantite ads.model.SklearnModel using the sklearn LogisticRegression model + # Instantiate ads.model.SklearnModel using the sklearn LogisticRegression model sklearn_model = SklearnModel(estimator=sklearn_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 47a831e93..5dc1f210e 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -28,7 +28,7 @@ Sklearn sklearn_estimator = LogisticRegression() sklearn_estimator.fit(X_train, y_train) - # Instantite ads.model.framework.sklearn_model.SklearnModel using the sklearn LogisticRegression model + # Instantiate ads.model.framework.sklearn_model.SklearnModel using the sklearn LogisticRegression model sklearn_model = SklearnModel( estimator=sklearn_estimator, artifact_dir=tempfile.mkdtemp() ) @@ -75,7 +75,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, xgboost_estimator = xgb.XGBClassifier() xgboost_estimator.fit(X_train, y_train) - # Instantite ads.model.framework.xgboost_model.XGBoostModel using the trained XGBoost Model + # Instantiate ads.model.framework.xgboost_model.XGBoostModel using the trained XGBoost Model xgboost_model = XGBoostModel(estimator=xgboost_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json @@ -121,7 +121,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, } lightgbm_estimator = lgb.train(param, train) - # Instantite ads.model.lightgbm_model.XGBoostModel using the trained LGBM Model + # Instantiate ads.model.lightgbm_model.XGBoostModel using the trained LGBM Model lightgbm_model = LightGBMModel(estimator=lightgbm_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json @@ -162,7 +162,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, # create random test data test_data = torch.randn(1, 3, 224, 224) - # Instantite ads.model.framework.pytorch_model.PyTorchModel using the pre-trained PyTorch Model + # Instantiate ads.model.framework.pytorch_model.PyTorchModel using the pre-trained PyTorch Model artifact_dir=tempfile.mkdtemp() torch_model = PyTorchModel(torch_estimator, artifact_dir=artifact_dir) @@ -228,7 +228,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, pipeline = Pipeline(stages=[tokenizer, hashingTF, lr]) model = pipeline.fit(training) - # Instantite ads.model.framework.spark_model.SparkPipelineModel using the pre-trained Spark Pipeline Model + # Instantiate ads.model.framework.spark_model.SparkPipelineModel using the pre-trained Spark Pipeline Model spark_model = SparkPipelineModel(estimator=model, artifact_dir=tempfile.mkdtemp()) spark_model.prepare(inference_conda_env="pyspark32_p38_cpu_v2", X_sample = training, @@ -273,7 +273,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, tf_estimator.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) tf_estimator.fit(x_train, y_train, epochs=1) - # Instantite ads.model.framework.tensorflow_model.TensorFlowModel using the pre-trained TensorFlow Model + # Instantiate ads.model.framework.tensorflow_model.TensorFlowModel using the pre-trained TensorFlow Model tf_model = TensorFlowModel(tf_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json @@ -320,7 +320,7 @@ Other Frameworks # Get predictions preds = catboost_estimator.predict(X_test) - # Instantite ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model + # Instantiate ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model catboost_model = GenericModel(estimator=catboost_estimator, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json @@ -351,7 +351,7 @@ With Model Version Set return x ** 2 model = Toy() - # Instantite ads.model.generic_model.GenericModel using the trained Custom Model + # Instantiate ads.model.generic_model.GenericModel using the trained Custom Model generic_model = GenericModel(estimator=model, artifact_dir=tempfile.mkdtemp()) generic_model.summary_status() From 49fef5560973b9504dd589c95565562ca7c9aa7d Mon Sep 17 00:00:00 2001 From: Lu Peng Date: Wed, 15 Feb 2023 23:03:18 -0800 Subject: [PATCH 014/147] Updated ModelDeployment docs. --- .../access_a_model_deployment.rst | 11 +- .../model_deployment/attributes.rst | 6 +- .../user_guide/model_deployment/delete.rst | 21 +- .../user_guide/model_deployment/deploy.rst | 469 +++++++++++++++--- .../user_guide/model_deployment/inventory.rst | 2 +- .../user_guide/model_deployment/logs.rst | 34 +- .../user_guide/model_deployment/overview.rst | 1 - .../model_deployment/properties.rst | 69 --- .../user_guide/model_deployment/state.rst | 4 +- .../user_guide/model_deployment/update.rst | 14 +- 10 files changed, 415 insertions(+), 216 deletions(-) delete mode 100644 docs/source/user_guide/model_deployment/properties.rst diff --git a/docs/source/user_guide/model_deployment/access_a_model_deployment.rst b/docs/source/user_guide/model_deployment/access_a_model_deployment.rst index 353c14f05..26ea32e6c 100644 --- a/docs/source/user_guide/model_deployment/access_a_model_deployment.rst +++ b/docs/source/user_guide/model_deployment/access_a_model_deployment.rst @@ -1,18 +1,13 @@ Access a Model Deployment ************************* -When a model is deployed the ``.deploy()`` method of the ``ModelDeployer`` class will return a ``ModelDeployment`` object. This object can be used to interact with the actual model deployment. However, if the model has already been deployed, it is possible to obtain a ``ModelDeployment`` object. Use the ``.get_model_deployment()`` method when the model deployment OCID is known. +When a model is deployed the ``.deploy()`` method of the ``ModelDeployment`` class will return an updated ``ModelDeployment`` object. This object can be used to interact with the actual model deployment. However, if the model has already been deployed, it is possible to obtain a ``ModelDeployment`` object. Use the ``.from_id()`` method when the model deployment OCID is known. The next code snippet creates a new ``ModelDeployment`` object that has access to the created model deployment. .. code-block:: python3 - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - existing_deployment = deployer.get_model_deployment(model_deployment_id="") - - - + from ads.model.deployment import ModelDeployment + existing_deployment = ModelDeployment.from_id(id="") diff --git a/docs/source/user_guide/model_deployment/attributes.rst b/docs/source/user_guide/model_deployment/attributes.rst index cca04654d..71e4e6276 100644 --- a/docs/source/user_guide/model_deployment/attributes.rst +++ b/docs/source/user_guide/model_deployment/attributes.rst @@ -3,7 +3,7 @@ Attributes The ``ModelDeployment`` class has a number of attributes that are assigned by the system. They provide a mechanism to determine the state of the model deployment, the URI to make predictions, the model deployment OCID, etc. -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.get_model_deployment()``. +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. OCID ==== @@ -18,9 +18,9 @@ the OCID of the model deployment. State ===== -You can determine the state of the model deployment using the ``.current_state`` enum attribute of a ``ModelDeployment`` object. This returns an enum object and the string value can be determined with ``.current_state.name``. It will have values like ‘ACTIVE’, ‘INACTIVE’, and ‘FAILED’. +You can determine the state of the model deployment using the ``.current_state`` enum attribute of a ``ModelDeployment`` object. This returns an enum object and the string value can be determined with ``.current_state.name``. It will have values like 'ACTIVE', 'INACTIVE', and 'FAILED'. -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.get_model_deployment()``. +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. .. code-block:: python3 diff --git a/docs/source/user_guide/model_deployment/delete.rst b/docs/source/user_guide/model_deployment/delete.rst index dd1bcc053..4d67cdd0f 100644 --- a/docs/source/user_guide/model_deployment/delete.rst +++ b/docs/source/user_guide/model_deployment/delete.rst @@ -1,30 +1,13 @@ Delete ****** -A model deployment can be deleted using a ``ModelDeployer`` or ``ModelDeployment`` objects. +A model deployment can be deleted using a ``ModelDeployment`` objects. When a model deployment is deleted, it deletes the load balancer instances associated with it. However, it doesn't delete other resources like log group, log, or model. - -``ModelDeployer`` -================= - -The ``ModelDeployer`` instance has a ``.delete()`` method for deleting a model deployment when give its OCID. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - deployer.delete(model_deployment_id=deployment_id) - - -``ModelDeployment`` -=================== - If you have a ``ModelDeployment`` object, you can use the ``.delete()`` method to delete the model that is associated with that object. The optional ``wait_for_completion`` parameter accepts a Boolean and determines if the process is blocking or not. -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.get_model_deployment()``. +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. .. code-block:: python3 diff --git a/docs/source/user_guide/model_deployment/deploy.rst b/docs/source/user_guide/model_deployment/deploy.rst index f3f436177..96b7042f2 100644 --- a/docs/source/user_guide/model_deployment/deploy.rst +++ b/docs/source/user_guide/model_deployment/deploy.rst @@ -1,88 +1,413 @@ Deploy ****** -The ``.deploy()`` method of the ``ModelDeployer`` class is used to create a model deployment. It has the following parameters: +The ``.deploy()`` method of the ``ModelDeployment`` class is used to create a model deployment. It has the following parameters: -* ``max_wait_time``: The timeout limit, in seconds, for the deployment process to wait until it is active. Defaults to 1200 seconds. -* ``poll_interval``: The interval between checks of the deployment status in seconds. Defaults to 30 seconds. -* ``wait_for_completion``: Blocked process until the deployment has been completed. Defaults to ``True``. + * ``max_wait_time``: The timeout limit, in seconds, for the deployment process to wait until it is active. Defaults to 1200 seconds. + * ``poll_interval``: The interval between checks of the deployment status in seconds. Defaults to 30 seconds. + * ``wait_for_completion``: Blocked process until the deployment has been completed. Defaults to ``True``. -There are two ways to use the ``.deploy()`` method. You can create a ``ModelDeploymentProperties`` object and pass that in, or you can define the model deployment properties using the ``.deploy()`` method. +To deploy a ``ModelDeployment``, you need to define a ``ModelDeployment`` object first and then call ``.deploy()``. There are two ways to define a ``ModelDeployment`` object. -With ``ModelDeploymentProperties`` -================================== +Builder Pattern +=============== -After a ``ModelDeploymentProperties`` object is created, then you use ``model_deployment_properties`` to deploy a model as in this example: +Infrastructure +-------------- + +You define the model deployment infrastructure by passing the following properties to ``ModelDeploymentInfrastructure``: + + * ``access_log``: Log group OCID and log OCID for the access logs. + * ``bandwidth_mbps``: The bandwidth limit on the load balancer in Mbps. + * ``compartment_id``: Compartment OCID that the model deployment belongs to. + * ``replica``: The number of instances to deploy. + * ``shape_name``: The instance shape name to use. For example, "VM.Standard.E4.Flex". + * ``shape_config_details``: The instance shape configure details to use if flexible shape is selected for ``shape_name``. + * ``predict_log``: Log group OCID and log OCID for the predict logs. + * ``project_id``: Project OCID that the model deployment will belong to. + * ``web_concurrency``: The web concurrency to use. + +Below is an example to define a ``ModelDeploymentInfrastructure`` object .. code-block:: python3 - from ads.model.deployment import ModelDeployer, ModelDeploymentProperties - - model_deployment_properties = ModelDeploymentProperties( - "" - ).with_prop( - 'display_name', "Model Deployment Demo using ADS" - ).with_prop( - "project_id", "" - ).with_prop( - "compartment_id", "" - ).with_logging_configuration( - "", "", "", "" - ).with_instance_configuration( - config={ - "INSTANCE_SHAPE":"VM.Standard.E4.Flex", - "INSTANCE_COUNT":"1", - "bandwidth_mbps":10, - "memory_in_gbs":10, - "ocpus":1 - } + from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure + + infrastructure = ( + ModelDeploymentInfrastructure() + .with_project_id("") + .with_compartment_id("") + .with_shape_name("VM.Standard.E4.Flex") + .with_shape_config_details( + ocpus=1, + memory_in_gbs=16 + ) + .with_replica(1) + .with_bandwidth_mbps(10) + .with_web_concurrency(10) + .with_access_log( + log_group_id="", + log_id="" + ) + .with_predict_log( + log_group_id="", + log_id="" + ) ) - deployer = ModelDeployer() - deployment = deployer.deploy(model_deployment_properties) - - -Without ``ModelDeploymentProperties`` -===================================== - -Depending on your use case, it might be more convenient to skip the creation of a ``ModelDeploymentProperties`` object and create the model deployment directly using the ``.deploy()`` method. You can do this by passing the using keyword arguments instead of ``ModelDeploymentProperties``. You specify the model deployment properties as parameters in the ``.deploy()`` method. - -You define the model deployment properties using the following parameters: - -* ``access_log_group_id``: Log group OCID for the access logs. Required when ``access_log_id`` is specified. -* ``access_log_id``: Custom logger OCID for the access logs. Required when ``access_log_group_id`` is specified. -* ``bandwidth_mbps``: The bandwidth limit on the load balancer in Mbps. Optional. -* ``compartment_id``: Compartment OCID that the model deployment belongs to. -* ``defined_tags``: A dictionary of defined tags to be attached to the model deployment. Optional. -* ``description``: A description of the model deployment. Optional. -* ``display_name``: A name that identifies the model deployment in the Console. -* ``freeform_tags``: A dictionary of freeform tags to be attached to the model deployment. Optional. -* ``instance_count``: The number of instances to deploy. -* ``instance_shape``: The instance compute shape to use. For example, “VM.Standard2.1”. -* ``memory_in_gbs``: The size of the memory of the model deployment instance in GBs. Applicable for the flexible shapes, for example, “VM.Standard.E4.Flex”. -* ``model_id``: Model OCID that is used in the model deployment. -* ``ocpus``: The ocpus count of the model deployment instance. Applicable for the flexible shapes, for example, “VM.Standard.E4.Flex”. -* ``predict_log_group_id``: Log group OCID for the predict logs. Required when ``predict_log_id`` is specified. -* ``predict_log_id``: Custom logger OCID for the predict logs. Required when ``predict_log_group_id`` is specified. -* ``project_id``: Project OCID that the model deployment will belong to. + + +Runtime +------- + +The Data Science Model Deployment supports service managed conda runtime and customized docker container runtime. + + * ``ModelDeploymentContainerRuntime`` allows you to deploy model deployment on customized docker container runtime. + * ``ModelDeploymentCondaRuntime`` allows you to deploy model deployment on service-managed conda runtime. + +ModelDeploymentContainerRuntime +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define the model deployment container runtime by passing the following properties to ``ModelDeploymentContainerRuntime`` object: + + * ``model_uri``: The model ocid or path to model artifacts directory that is used in the model deployment. + * ``deployment_mode``: The mode of model deployment. Allowed deployment modes are ``HTTPS_ONLY`` and ``STREAM_ONLY``. Optional. + * ``input_stream_ids``: The input stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. + * ``output_stream_ids``: The output stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. + * ``env``: The environment variables. Optional. + * ``image``: The full path of docker container image to the OCIR registry. The acceptable formats are: ``.ocir.io//:`` and ``.ocir.io//:@digest``. Required. + * ``image_digest``: The docker container image digest. Optional. + * ``entrypoint``: The entrypoint to docker container image. Optional. + * ``server_port``: The server port of docker container image. Optional. + * ``health_check_port``: The health check port of docker container image. Optional. + * ``cmd``: The additional commands to docker container image. The commands can be args to the entrypoint or the only command to execute in the absence of an entrypoint. Optional. + +Below is an example to define a ``ModelDeploymentContainerRuntime`` object + +.. code-block:: python3 + + from ads.model.deployment.model_deployment_runtime import ModelDeploymentContainerRuntime + + container_runtime = ( + ModelDeploymentContainerRuntime() + .with_image("") + .with_image_digest("") + .with_entrypoint(["python","/opt/ds/model/deployed_model/api.py"]) + .with_server_port(5000) + .with_health_check_port(5000) + .with_env({"key":"value"}) + .with_deployment_mode("HTTPS_ONLY") + .with_model_uri("") + ) + + +ModelDeploymentCondaRuntime +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define the model deployment conda runtime by passing the following properties to ``ModelDeploymentCondaRuntime`` object: + + * ``model_uri``: The model ocid or path to model artifacts that is used in the model deployment. + * ``deployment_mode``: The deployment mode. The allowed deployment modes are ``HTTPS_ONLY`` and ``STREAM_ONLY``. Optional. + * ``input_stream_ids``: The input stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. + * ``output_stream_ids``: The output stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. + * ``env``: The environment variables. Optional. + +Below is an example to define a ``ModelDeploymentCondaRuntime`` object + +.. code-block:: python3 + + from ads.model.deployment.model_deployment_runtime import ModelDeploymentCondaRuntime + + conda_runtime = ( + ModelDeploymentCondaRuntime() + .with_env({"key":"value"}) + .with_deployment_mode("HTTPS_ONLY") + .with_model_uri("") + ) + + +ModelDeployment +~~~~~~~~~~~~~~~ + +You can define the model deployment by passing the following properties to ``ModelDeployment`` object: + + * ``defined_tags``: A dictionary of defined tags to be attached to the model deployment. Optional. + * ``description``: A description of the model deployment. Optional. + * ``display_name``: A name that identifies the model deployment in the Console. + * ``freeform_tags``: A dictionary of freeform tags to be attached to the model deployment. Optional. + * ``runtime``: The runtime configuration to be attached to the model deployment. + * ``infrastructure``: The infrastructure configuration to be attached to the model deployment. + +Below is an example to define and deploy a ``ModelDeployment`` object with custom docker container runtime .. code-block:: python3 - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - deployment = deployer.deploy( - model_id="", - display_name="Model Deployment Demo using ADS", - instance_shape="VM.Standard.E4.Flex", - instance_count=1, - memory_in_gbs=6, # Applicable for the flexible shapes for example, “VM.Standard.E4.Flex”. - ocpus=2, # Applicable for the flexible shapes for example, “VM.Standard.E4.Flex”. - project_id="", - compartment_id="", - # The following are optional - access_log_group_id="", - access_log_id="", - predict_log_group_id="", - predict_log_id="" + from ads.model.deployment import ModelDeployment + + deployment = ( + ModelDeployment() + .with_display_name("Model Deployment Demo using ADS") + .with_description("The model deployment description") + .with_freeform_tags({"key1":"value1"}) + .with_infrastructure(infrastructure) + .with_runtime(container_runtime) ) + deployment.deploy(wait_for_completion=False) + + +YAML Serialization +================== + +A ``ModelDeployment`` object can be serialized to a YAML file by calling ``to_yaml()``, which returns the YAML as a string. You can easily share the YAML with others, and reload the configurations by calling ``from_yaml()``. The ``to_yaml()`` and ``from_yaml()`` methods also take an optional ``uri`` argument for saving and loading the YAML file. This argument can be any URI to the file location supported by `fsspec `__, including Object Storage. For example: + +.. code-block:: python3 + + # Save the model deployment configurations to YAML file + deployment.to_yaml(uri="oci://bucket_name@namespace/path/to/deployment.yaml") + + # Load the model deployment configurations from YAML file + deployment = ModelDeployment.from_yaml(uri="oci://bucket_name@namespace/path/to/deployment.yaml") + + # Save the model deployment configurations to YAML in a string + yaml_string = ModelDeployment.to_yaml() + + # Load the model deployment configurations from a YAML string + deployment = ModelDeployment.from_yaml(""" + kind: deployment + spec: + infrastructure: + kind: infrastructure + ... + """") + + deployment.deploy(wait_for_completion=False) + +Here is an example of a YAML file representing the ``ModelDeployment`` with docker container runtime defined in the preceding examples: + + +.. code-block:: yaml + + kind: deployment + spec: + displayName: Model Deployment Demo using ADS + description: The model deployment description + freeform_tags: + key1: value1 + infrastructure: + kind: infrastructure + type: datascienceModelDeployment + spec: + compartmentId: + projectId: + accessLog: + logGroupId: + logId: + predictLog: + logGroupId: + logId: + shapeName: VM.Standard.E4.Flex + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + replica: 1 + bandWidthMbps: 10 + runtime: + kind: runtime + type: container + spec: + modelUri: + image: + imageDigest: + entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] + serverPort: 5000 + healthCheckPort: 5000 + env: + WEB_CONCURRENCY: "10" + deploymentMode: HTTPS_ONLY + + +**ADS ModelDeployment YAML schema** + +.. code-block:: yaml + + kind: + required: true + type: string + allowed: + - deployment + spec: + required: true + type: dict + schema: + displayName: + type: string + required: false + description: + type: string + required: false + freeform_tags: + type: dict + required: false + defined_tags: + type: dict + required: false + infrastructure: + type: dict + required: true + runtime: + type: dict + required: true + +**ADS Model Deployment Infrastructure YAML Schema** + +.. code-block:: yaml + + kind: + required: true + type: string + allowed: + - infrastructure + type: + required: true + type: string + allowed: + - datascienceModelDeployment + spec: + compartmentId: + type: string + required: true + projectId: + type: string + required: true + bandWidthMbps: + type: integer + required: false + webConcurrency: + type: integer + required: false + logGroupId: + type: string + required: false + logId: + type: string + required: false + accessLog: + type: dict + nullable: true + required: false + schema: + logId: + required: false + type: string + logGroupId: + required: false + type: string + predictLog: + type: dict + nullable: true + required: false + schema: + logId: + required: false + type: string + logGroupId: + required: false + type: string + shapeName: + type: string + required: false + shapeConfigDetails: + type: dict + nullable: true + required: false + schema: + ocpus: + required: true + type: float + memoryInGBs: + required: true + type: float + replica: + type: integer + required: false + +**ADS Model Deployment Conda Runtime YAML Schema** + +.. code-block:: yaml + + kind: + required: true + type: string + allowed: + - runtime + type: + required: true + type: string + allowed: + - conda + spec: + modelUri: + type: string + required: true + env: + type: dict + required: false + inputStreamIds: + type: list + required: false + outputStreamIds: + type: list + required: false + deploymentMode: + type: string + required: false + +**ADS Model Deployment Container Runtime YAML Schema** + +.. code-block:: yaml + + kind: + required: true + type: string + allowed: + - runtime + type: + required: true + type: string + allowed: + - container + spec: + modelUri: + type: string + required: true + image: + type: string + required: true + imageDigest: + type: string + required: false + entrypoint: + type: list + required: false + cmd: + type: list + required: false + serverPort: + type: integer + required: false + healthCheckPort: + type: integer + required: false + env: + type: dict + required: false + inputStreamIds: + type: list + required: false + outputStreamIds: + type: list + required: false + deploymentMode: + type: string + required: false + diff --git a/docs/source/user_guide/model_deployment/inventory.rst b/docs/source/user_guide/model_deployment/inventory.rst index 86c138abe..f51866ab4 100644 --- a/docs/source/user_guide/model_deployment/inventory.rst +++ b/docs/source/user_guide/model_deployment/inventory.rst @@ -4,7 +4,7 @@ Inventory List ==== -The ``.list_deployments()`` method of the ``ModelDeployer`` class returns a list of ``ModelDeployment`` objects. The optional ``compartment_id`` parameter limits the search to a specific compartment. By default, it uses the same compartment that the notebook is in. The optional ``status`` parameter limits the returned ``ModelDeployment`` objects to those model deployments that have the specified status. Values for the ``status`` parameter would be ‘ACTIVE’, ‘INACTIVE’, or ‘FAILED’. +The ``.list_deployments()`` method of the ``ModelDeployer`` class returns a list of ``ModelDeployment`` objects. The optional ``compartment_id`` parameter limits the search to a specific compartment. By default, it uses the same compartment that the notebook is in. The optional ``status`` parameter limits the returned ``ModelDeployment`` objects to those model deployments that have the specified status. Values for the ``status`` parameter would be 'ACTIVE', 'INACTIVE', or 'FAILED'. The code snippet obtains a list of active deployments in the compartment specified by ``compartment_id``, and prints the display name. diff --git a/docs/source/user_guide/model_deployment/logs.rst b/docs/source/user_guide/model_deployment/logs.rst index 92beb676e..ec52149bd 100644 --- a/docs/source/user_guide/model_deployment/logs.rst +++ b/docs/source/user_guide/model_deployment/logs.rst @@ -3,7 +3,7 @@ Logs The model deployment process creates a set of workflow logs. Optionally, you can also configure the Logging service to capture access and predict logs. -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.get_model_deployment()``. +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. Access/Predict ============== @@ -30,36 +30,4 @@ This method returns a dataframe where each row represents a log entry. deployment.show_logs(log_type="access", limit=10) -Workflow -======== - -The ``.list_workflow_logs()`` provides a list of dictionaries that define the steps that were used to deploy the model. These are referred to as the workflow logs. - -.. code-block:: python3 - - deployment.list_workflow_logs() - -.. parsed-literal:: - - [{ - "message": "Creating compute resource configuration.", - "timestamp": "2021-04-21T20:45:27.609000+00:00" - }, - { - "message": "Creating compute resources.", - "timestamp": "2021-04-21T20:45:30.237000+00:00" - }, - { - "message": "Creating load balancer.", - "timestamp": "2021-04-21T20:45:33.076000+00:00" - }, - { - "message": "Compute resources are provisioned.", - "timestamp": "2021-04-21T20:46:46.876000+00:00" - }, - { - "message": "Load balancer is provisioned.", - "timestamp": "2021-04-21T20:53:54.764000+00:00" - }] - diff --git a/docs/source/user_guide/model_deployment/overview.rst b/docs/source/user_guide/model_deployment/overview.rst index 163f3df39..5520d86f3 100644 --- a/docs/source/user_guide/model_deployment/overview.rst +++ b/docs/source/user_guide/model_deployment/overview.rst @@ -16,5 +16,4 @@ The ``ads.model.deployment`` module provides the following classes, which are us * ``ModelDeployer``: It creates a new deployment. It is also used to delete, list, and update existing deployments. * ``ModelDeployment``: Encapsulates the information and actions for an existing deployment. -* ``ModelDeploymentProperties``: Stores the properties used to deploy a model. diff --git a/docs/source/user_guide/model_deployment/properties.rst b/docs/source/user_guide/model_deployment/properties.rst deleted file mode 100644 index a90484250..000000000 --- a/docs/source/user_guide/model_deployment/properties.rst +++ /dev/null @@ -1,69 +0,0 @@ -Properties -********** - -``ModelDeploymentProperties`` -============================= - -The ``ModelDeploymentProperties`` class is a container to store model deployment properties. String properties are set using the ``.with_prop()`` method. You use it to assemble properties such as the display name, project OCID, and compartment OCID. The ``.with_access_log()`` and ``.with_predict_log()`` methods define the logging properties. Alternatively, you could use the ``.with_logging_configuration()`` helper method to define the predict and access log properties using a single method. The ``.with_instance_configuration()`` method defines the instance shape, count, and bandwidth. Initializing ``ModelDeploymentProperties`` requires a ``model_id`` or ``model_uri``. The ``model_id`` is the model OCID from the model catalog. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeploymentProperties - - model_deployment_properties = ModelDeploymentProperties( - "" - ).with_prop( - 'display_name', "Model Deployment Demo using ADS" - ).with_prop( - "project_id", "" - ).with_prop( - "compartment_id", "" - ).with_logging_configuration( - "", "", "", "" - ).with_instance_configuration( - config={ - "INSTANCE_SHAPE":"VM.Standard.E4.Flex", - "INSTANCE_COUNT":"1", - "bandwidth_mbps":10, - "memory_in_gbs":10, - "ocpus":1 - } - ) - -Alternatively, you can specify a ``model_uri`` instead of a ``model_id``. The -``model_uri`` is the path to the directory containing the model artifact. This can be a local path or -the URI of Object Storage. For example, ``oci://your_bucket@your_namespace/path/to/dir``. - -.. code-block:: python3 - - model_deployment_properties = ModelDeploymentProperties( - "" - ) - - -``properties`` -============== - -The ``ModelDeployment`` class has a number of attributes that provide information about the deployment. The ``properties`` attribute contains information about the model deployment’s properties that are related to the information that is stored in the model's ``ModelDeploymentProperties`` object. This object has all of the attributes of the `Data Science model deployment model `__. The most commonly used properties are: - -* ``category_log_details``: A model object that contains the OCIDs for the access and predict logs. -* ``compartment_id``: Compartment ID of the model deployment. -* ``created_by``: OCID of the user that created the model deployment. -* ``defined_tags``: System defined tags. -* ``description``: Description of the model deployment. -* ``display_name``: Name of the model that is displayed in the Console. -* ``freeform_tags``: User-defined tags. -* ``instance_count``: The number of instances to deploy. -* ``instance_shape``: The instance compute shape to use. For example, “VM.Standard2.1”. -* ``memory_in_gbs``: The size of the memory of the model deployment instance in GBs. Applicable for the flexible shapes, for example, “VM.Standard.E4.Flex”. -* ``model_id``: OCID of the deployed model. -* ``ocpus``: The ocpus count of the model deployment instance. Applicable for the flexible shapes, for example, “VM.Standard.E4.Flex”. -* ``project_id``: OCID of the project the model deployment belongs to. - -To access these properties use the ``.properties`` accessor on a ``ModelDeployment`` object. For example, to determine the OCID of the project that a model deployment is associated with, use the command: - -.. code-block:: python3 - - deployment.properties.project_id - - diff --git a/docs/source/user_guide/model_deployment/state.rst b/docs/source/user_guide/model_deployment/state.rst index 13e37cf67..d99632b96 100644 --- a/docs/source/user_guide/model_deployment/state.rst +++ b/docs/source/user_guide/model_deployment/state.rst @@ -20,9 +20,9 @@ The ``.get_model_deployment_state()`` method of the ``ModelDeployer`` class acce ``ModelDeployment`` =================== -You can determine the state of the model deployment using the ``current_state.name`` attribute of a ``ModelDeployment`` object. This returns a string with values like ‘ACTIVE’, ‘INACTIVE’, and ‘FAILED’. +You can determine the state of the model deployment using the ``current_state.name`` attribute of a ``ModelDeployment`` object. This returns a string with values like 'ACTIVE', 'INACTIVE', and 'FAILED'. -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.get_model_deployment()``. +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. .. code-block:: python3 diff --git a/docs/source/user_guide/model_deployment/update.rst b/docs/source/user_guide/model_deployment/update.rst index 7db087b53..349e08fbc 100644 --- a/docs/source/user_guide/model_deployment/update.rst +++ b/docs/source/user_guide/model_deployment/update.rst @@ -1,22 +1,20 @@ Update ****** -The ``.update()`` method of the ``ModelDeployment`` class is used to make changes to a deployed model. This method accepts the same parameters as the ``.deploy()`` method. Check out the `Editing Model Deployments `__ for a +The ``.update()`` method of the ``ModelDeployment`` class is used to make changes to a deployed model. Check out the `Editing Model Deployments `__ for a list of what properties can be updated. -A common use case is to change the underlying model that is deployed. In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.get_model_deployment()``. +A common use case is to change the underlying model that is deployed. In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. .. code-block:: python3 - deployment.update(model_id="") + deployment.runtime.with_model_uri("") + deployment.update() Or, you could update the instance shape with: .. code-block:: python3 - deployment.update( - model_deployment_properties.with_instance_configuration( - dict(instance_shape="VM.Standard2.1") - ) - ) + deployment.infrastructure.with_shape_name("VM.Standard.E4.Flex") + deployment.update() From 55530ee77e8901f0d84879d09633f88e36765689 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 15 Feb 2023 23:10:43 -0800 Subject: [PATCH 015/147] ODSC-38627: adding doc --- .../frameworks/huggingface.rst | 250 ++++++++++++++++++ .../frameworks/pytorchmodel.rst | 2 +- 2 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 docs/source/user_guide/model_registration/frameworks/huggingface.rst diff --git a/docs/source/user_guide/model_registration/frameworks/huggingface.rst b/docs/source/user_guide/model_registration/frameworks/huggingface.rst new file mode 100644 index 000000000..cee4a68e2 --- /dev/null +++ b/docs/source/user_guide/model_registration/frameworks/huggingface.rst @@ -0,0 +1,250 @@ +.. HuggingFacePipelineModel: + +HuggingFacePipelineModel +************************ + +.. versionadded:: 2.8.2 + +See `API Documentation <../../../ads.model_framework.html#ads.model.framework.huggingface_model.HuggingFacePipelineModel>`__ + +Overview +======== + +The ``ads.model.framework.huggingface_model.HuggingFacePipelineModel`` class in ADS is designed to allow you to rapidly get a HuggingFace Pipeline into production. The ``.prepare()`` method creates the model artifacts that are needed to deploy a functioning pipeline without you having to configure it or write code. However, you can customize the required ``score.py`` file. + +.. include:: ../_template/overview.rst + +The following steps take your trained ``HuggingFacePipelineModel`` model and deploy it into production with a few lines of code. + +**Create a HuggingFace Pipeline** + +Load a `ImageSegmentationPipeline `_ pretrained model. + +.. code-block:: python3 + + from transformers import pipeline + + segmenter = pipeline(task="image-segmentation", model="facebook/detr-resnet-50-panoptic", revision="fc15262") + preds = segmenter( + "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" + ) + + preds + + [{'score': 0.987885, + 'label': 'LABEL_184', + 'mask': }, + {'score': 0.997345, + 'label': 'snow', + 'mask': }, + {'score': 0.997247, + 'label': 'cat', + 'mask': }] + +Prepare Model Artifact +====================== + +.. code-block:: python3 + + >>> from ads.common.model_metadata import UseCaseType + >>> from ads.model import HuggingFacePipelineModel + + >>> import tempfile + + >>> # Prepare the model + >>> artifact_dir = "huggingface_pipeline_model_artifact" + >>> huggingface_pipeline_model = HuggingFacePipelineModel(model, artifact_dir=artifact_dir) + >>> huggingface_pipeline_model.prepare( + ... inference_conda_env="", + ... inference_python_version="", + ... training_conda_env="", + ... use_case_type=UseCaseType.OTHER, + ... force_overwrite=True, + ...) + # You don't need to modify the score.py generated. The model can be loaded by the transformers.pipeline. + # More info here - https://huggingface.co/docs/transformers/main_classes/pipelines#transformers.pipeline + + +Instantiate a ``HuggingFacePipelineModel()`` object with a HuggingFace Pipelines model. Each instance accepts the following parameters: + +* ``artifact_dir: str``. Artifact directory to store the files needed for deployment. +* ``auth: (Dict, optional)``: Defaults to ``None``. The default authentication is set using the ``ads.set_auth`` API. To override the default, use ``ads.common.auth.api_keys()`` or ``ads.common.auth.resource_principal()`` and create the appropriate authentication signer and the ``**kwargs`` required to instantiate the ``IdentityClient`` object. +* ``estimator: Callable``. Any model object generated by the PyTorch framework. +* ``properties: (ModelProperties, optional)``. Defaults to ``None``. The ``ModelProperties`` object required to save and deploy model. + +For more detailed information on what parameters that ``HuggingFacePipelineModel`` takes, refer to the `API Documentation <../../../ads.model_framework.html#ads.model.framework.huggingface_model.HuggingFacePipelineModel>`__ +All the pipelines related files are saved under the ``artifact_dir``. + +.. include:: ../_template/initialize.rst + + +Summary Status +============== + +.. include:: ../_template/summary_status.rst + +.. figure:: ../figures/summary_status.png + :align: center + +Register Model +============== + +.. code-block:: python3 + + >>> # Register the model + >>> model_id = huggingface_pipeline_model.save() + + Model is successfully loaded. + ['.model-ignore', 'score.py', 'config.json', 'runtime.yaml', 'preprocessor_config.json', 'pytorch_model.bin'] + + 'ocid1.datasciencemodel.oc1.xxx.xxxxx' + +Deploy and Generate Endpoint +============================ + +.. code-block:: python3 + + >>> # Deploy and create an endpoint for the huggingface_pipeline_model + >>> huggingface_pipeline_model.deploy( + display_name="HuggingFace Pipeline Model For Image Segmentation", + deployment_log_group_id="ocid1.loggroup.oc1.xxx.xxxxx", + deployment_access_log_id="ocid1.log.oc1.xxx.xxxxx", + deployment_predict_log_id="ocid1.log.oc1.xxx.xxxxx", + ) + >>> print(f"Endpoint: {huggingface_pipeline_model.model_deployment.url}") +.. parsed-literal:: + https://modeldeployment.{region}.oci.customer-oci.com/ocid1.datasciencemodeldeployment.oc1.xxx.xxxxx + +Run Prediction against Endpoint +=============================== + +.. code-block:: python3 + + # Download an image + import PIL.Image + import requests + import cloudpickle + image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" + + image = PIL.Image.open(requests.get(image_url, stream=True).raw) + image_bytes = cloudpickle.dumps(image) + + # Generate prediction by invoking the deployed endpoint + preds = huggingface_pipeline_model.predict(image)["prediction"] + print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) + +.. parsed-literal:: + + [{'score': 0.9879, 'label': 'LABEL_184'}, + {'score': 0.9973, 'label': 'snow'}, + {'score': 0.9972, 'label': 'cat'}] + + +Predict with Image +------------------ + +Predict Image by passing a PIL.Image.Image object using the ``data`` argument in ``predict()`` to predict a single image. +The image will be converted to bytes using cloudpickle so it can be passed to the endpoint. +It will be loaded back to PIL.Image.Image in ``score.py`` before pass into the pipeline. + +.. note:: + - The payload size limit is 10 MB. Read more about invoking a model deployment `here `_. + - Model deployment currently does not support internet(coming soon), hence you cannot pass in a url. + +Predict with Multiple Arguments +------------------------------- + +If your model takes more than one argument, you can pass in through dictionary with the keys as the argument name and values as the value of the arguement. + +.. code-block:: python3 + >>> your_huggingface_pipeline_model.verify({"parameter_name_1": "parameter_value_1", ..., "parameter_name_n": "parameter_value_n"}) + >>> your_huggingface_pipeline_model.predict({"parameter_name_1": "parameter_value_1", ..., "parameter_name_n": "parameter_value_n"}) + + +Run Prediction with oci sdk +============================== + +Model deployment endpoints can be invoked with the oci sdk. This example invokes a model deployment with the oci sdk with a ``bytes`` payload: + +`bytes` payload example +------------------------------ + +.. code-block:: python3 + + >>> # The OCI SDK must be installed for this example to function properly. + >>> # Installation instructions can be found here: https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/pythonsdk.htm + + >>> import requests + >>> import oci + >>> import ads + >>> import cloudpickle + >>> headers = {"Content-Type": "application/octet-stream"} + >>> endpoint = huggingface_pipeline_model.model_deployment.url + "/predict" + + >>> preds = requests.post(endpoint, data=image_bytes, auth=ads.common.auth.default_signer()['signer'], headers=headers).json() + >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) +.. parsed-literal:: + [{'score': 0.9879, 'label': 'LABEL_184'}, + {'score': 0.9973, 'label': 'snow'}, + {'score': 0.9972, 'label': 'cat'}] + + +Example +======= + +.. code-block:: python3 + + from transformers import pipeline + import tempfile + import PIL.Image + import ads + import requests + import cloudpickle + + ## download the image + image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + image = PIL.Image.open(requests.get(image_link, stream=True).raw) + image_bytes = cloudpickle.dumps(image) + + ## download the pretrained model + classifier = pipeline(model="openai/clip-vit-large-patch14") + classifier( + images=image, + candidate_labels=["animals", "humans", "landscape"], + ) + + ## Initiate a HuggingFacePipelineModel instance + zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) + + ## Prepare a model artifact + conda = "oci://bucket@namespace/path/to/conda/pack" + python_version = "3.8" + zero_shot_image_classification_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + + ## Test data + data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} + body = cloudpickle.dumps(data) # convert image to bytes + + ## Verify + zero_shot_image_classification_model.verify(data=data) + zero_shot_image_classification_model.verify(data=body) + + ## Save + zero_shot_image_classification_model.save() + + ## Deploy + log_group_id = "" + log_id = "" + zero_shot_image_classification_model.deploy(deployment_bandwidth_mbps=100, + wait_for_completion=False, + deployment_log_group_id = log_group_id, + deployment_access_log_id = log_id, + deployment_predict_log_id = log_id) + zero_shot_image_classification_model.predict(image) + zero_shot_image_classification_model.predict(body) + + ### Invoke the model by sending bytes + auth = ads.common.auth.default_signer()['signer'] + endpoint = zero_shot_image_classification_model.model_deployment.url + "/predict" + headers = {"Content-Type": "application/octet-stream"} + requests.post(endpoint, data=body, auth=auth, headers=headers).json() diff --git a/docs/source/user_guide/model_registration/frameworks/pytorchmodel.rst b/docs/source/user_guide/model_registration/frameworks/pytorchmodel.rst index 0a4de6daa..b9a60a836 100644 --- a/docs/source/user_guide/model_registration/frameworks/pytorchmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/pytorchmodel.rst @@ -215,7 +215,7 @@ Deploy and Generate Endpoint .. code-block:: python3 - >>> # Deploy and create an endpoint for the TensorFlow model + >>> # Deploy and create an endpoint for the PyTorch model >>> pytorch_model.deploy( display_name="PyTorch Model For Classification", deployment_log_group_id="ocid1.loggroup.oc1.xxx.xxxxx", From a914e0754c80e1c098482e157dcc0cd3fa1cd0d0 Mon Sep 17 00:00:00 2001 From: Lu Peng <118394507+lu-ohai@users.noreply.github.com> Date: Wed, 15 Feb 2023 23:27:13 -0800 Subject: [PATCH 016/147] Update delete.rst --- docs/source/user_guide/model_deployment/delete.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_deployment/delete.rst b/docs/source/user_guide/model_deployment/delete.rst index 4d67cdd0f..ec418a087 100644 --- a/docs/source/user_guide/model_deployment/delete.rst +++ b/docs/source/user_guide/model_deployment/delete.rst @@ -1,7 +1,7 @@ Delete ****** -A model deployment can be deleted using a ``ModelDeployment`` objects. +A model deployment can be deleted using a ``ModelDeployment`` object. When a model deployment is deleted, it deletes the load balancer instances associated with it. However, it doesn't delete other resources like log group, log, or model. From 8df69e82fa7c747c147e4c98dfee8a4db64dbb85 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 16 Feb 2023 11:56:33 -0500 Subject: [PATCH 017/147] Update data_science_job.rst --- .../user_guide/jobs/data_science_job.rst | 378 +++++------------- 1 file changed, 95 insertions(+), 283 deletions(-) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 7314818be..5b0a59da1 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -1,326 +1,138 @@ -Data Science Job -**************** +Quick Start +*********** -This section shows how you can use the ADS jobs APIs to run OCI Data Science jobs. You can use similar APIs to `Run a OCI DataFlow Application `__. +.. admonition:: OCI Data Science Policies -Before creating a job, ensure that you have policies configured for Data Science resources, see `About Data Science Policies `__. + Before creating a job, ensure that you have policies configured for Data Science resources. + See also: :doc:`policies` and `About Data Science Policies `_. -Infrastructure -============== +In ADS, a job is defined by **infrastructure** and **runtime**. +The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance. +The runtime can be an instance of :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, +:py:class:`~ads.jobs.builders.runtimes.python_runtime.GitPythonRuntime`, +:py:class:`~ads.jobs.builders.runtimes.python_runtime.NotebookRuntime` or +:py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime`. -The Data Science job infrastructure is defined by a ``DataScienceJob`` instance. When creating a job, you specify the compartment ID, project ID, subnet ID, Compute shape, Block Storage size, log group ID, and log ID in the ``DataScienceJob`` instance. For example: -.. code-block:: python3 +Running a Python Job +==================== - from ads.jobs import DataScienceJob +Here is an example to define and run a Python job: - infrastructure = ( - DataScienceJob() - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) # Applicable only for the flexible shapes - .with_block_storage_size(50) - .with_log_group_id("") - .with_log_id("") - ) - -If you are using these API calls in a Data Science `Notebook Session `__, and you want to use the same infrastructure configurations as the notebook session, you can initialize the ``DataScienceJob`` with only the logging configurations: - -.. code-block:: python3 - - from ads.jobs import DataScienceJob +.. tabs:: - infrastructure = ( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - ) - -In some cases, you may want to override the shape and block storage size. For example, if you are testing your code in a CPU notebook session, but want to run the job in a GPU VM: - -.. code-block:: python3 + .. code-tab:: python + :caption: Python - from ads.jobs import DataScienceJob + from ads.jobs import Job, DataScienceJob, PythonRuntime - infrastructure = ( + job = ( + Job() + .with_infrastructure( DataScienceJob() - .with_shape_name("VM.GPU2.1") .with_log_group_id("") .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + PythonRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Set the working directory + # When using a directory as source, the default working dir is the parent of code_dir. + # Working dir should be a relative path beginning from the source directory (code_dir) + .with_working_dir("code_dir") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.py is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.py") + # Add an additional Python path, relative to the working dir (code_dir/other_packages). + .with_python_path("other_packages") + # Copy files in "code_dir/output" to object storage after job finishes. + .with_output("output", "oci://bucket_name@namespace/path/to/dir") + ) ) -Data Science jobs support the following shapes: - -==================== ========== =========== -Shape Name Core Count Memory (GB) -==================== ========== =========== -VM.Optimized3.Flex 18 256 -VM.Standard3.Flex 32 512 -VM.Standard.E4.Flex 16 1024 -VM.Standard2.1 1 15 -VM.Standard2.2 2 30 -VM.Standard2.4 4 60 -VM.Standard2.8 8 120 -VM.Standard2.16 16 240 -VM.Standard2.24 24 320 -VM.GPU2.1 12 72 -VM.GPU3.1 6 90 -VM.GPU3.2 12 180 -VM.GPU3.4 24 360 -==================== ========== =========== - -You can get a list of currently supported shapes by calling ``DataScienceJob.instance_shapes()``. - -Logs -==== - -In the preceding examples, both the log OCID and corresponding log group OCID are specified in the ``DataScienceJob`` instance. If your administrator configured the permission for you to search for logging resources, you can skip specifying the log group OCID because ADS automatically retrieves it. - -If you specify only the log group OCID and no log OCID, a new Log resource is automatically created within the log group to store the logs, see `ADS Logging <../logging/logging.html>`__. - -Runtime -======= - -A job can have different types of *runtime* depending on the source code you want to run: - -.. include:: _template/runtime_types.rst - -All of these runtime options allow you to configure a `Data Science Conda Environment `__ for running your code. For example, to define a python script as a job runtime with a TensorFlow conda environment you could use: - -.. code-block:: python3 - - from ads.jobs import ScriptRuntime - - runtime = ( - ScriptRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_service_conda("tensorflow28_p38_cpu_v1") - ) - -You can store your source code in a local file path or location supported by `fsspec `__, including OCI Object Storage. - -You can also use a custom conda environment published to OCI Object Storage by passing the ``uri`` to the ``with_custom_conda()`` method, for example: - -.. code-block:: python3 - - runtime = ( - ScriptRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_custom_conda("oci://bucket@namespace/conda_pack/pack_name") - ) - -For more details on custom conda environment, see `Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy `__. - -You can also configure the environment variables, command line arguments, and free form tags for runtime: - -.. code-block:: python3 - - runtime = ( - ScriptRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_service_conda("tensorflow28_p38_cpu_v1") - .with_environment_variable(ENV="value") - .with_argument("argument", key="value") - .with_freeform_tag(tag_name="tag_value") - ) - -With the preceding arguments, the script is started as ``python script.py argument --key value``. - -Define a Job -============ - -With ``runtime`` and ``infrastructure``, you can define a job and give it a name: - -.. code-block:: python3 - - from ads.jobs import Job - - job = ( - Job(name="") - .with_infrastructure(infrastructure) - .with_runtime(runtime) - ) - -If the job name is not specified, a name is generated automatically based on the name of the job artifact and a time stamp. - -Alternatively, a job can also be defined with keyword arguments: - -.. code-block:: python3 - - job = Job( - name="", - infrastructure=infrastructure, - runtime=runtime - ) - -Create and Run -============== - -You can call the ``create()`` method of a job instance to create a job. After the job is created, you can call the ``run()`` method to create and start a job run. The ``run()`` method returns a ``DataScienceJobRun``. You can monitor the job run output by calling the ``watch()`` method of the ``DataScienceJobRun`` instance: - -.. code-block:: python3 - - # Create a job + # Create the job on OCI Data Science job.create() - # Run a job, a job run will be created and started - job_run = job.run() + # Start a job run + run = job.run() # Stream the job run outputs - job_run.watch() - -.. code-block:: text - - 2021-10-28 17:17:58 - Job Run ACCEPTED - 2021-10-28 17:18:07 - Job Run ACCEPTED, Infrastructure provisioning. - 2021-10-28 17:19:19 - Job Run ACCEPTED, Infrastructure provisioned. - 2021-10-28 17:20:48 - Job Run ACCEPTED, Job run bootstrap starting. - 2021-10-28 17:23:41 - Job Run ACCEPTED, Job run bootstrap complete. Artifact execution starting. - 2021-10-28 17:23:50 - Job Run IN_PROGRESS, Job run artifact execution in progress. - 2021-10-28 17:23:50 - - 2021-10-28 17:23:50 - - 2021-10-28 17:23:50 - ... - -Override Configuration -====================== - -When you run ``job.run()``, the job is run with the default configuration. You may want to override this default configuration with custom variables. You can specify a custom job run display name, override command line argument, add additional environment variables, or free form tags as in this example: - -.. code-block:: python3 - - job_run = job.run( - name="", - args="new_arg --new_key new_val", - env_var={"new_env": "new_val"}, - freeform_tags={"new_tag": "new_tag_val"} - ) + run.watch() -YAML Serialization -================== - -A job instance can be serialized to a YAML file by calling ``to_yaml()``, which returns the YAML as a string. You can easily share the YAML with others, and reload the configurations by calling ``from_yaml()``. The ``to_yaml()`` and ``from_yaml()`` methods also take an optional ``uri`` argument for saving and loading the YAML file. This argument can be any URI to the file location supported by `fsspec `__, including Object Storage. For example: - -.. code-block:: python3 - - # Save the job configurations to YAML file - job.to_yaml(uri="oci://bucket_name@namespace/path/to/job.yaml") - - # Load the job configurations from YAML file - job = Job.from_yaml(uri="oci://bucket_name@namespace/path/to/job.yaml") - - # Save the job configurations to YAML in a string - yaml_string = job.to_yaml() - - # Load the job configurations from a YAML string - job = Job.from_yaml(""" - kind: job - spec: - infrastructure: - kind: infrastructure - ... - """") - -Here is an example of a YAML file representing the job defined in the preceding examples: - -.. code-block:: yaml + .. code-tab:: yaml + :caption: YAML kind: job spec: - name: + name: infrastructure: kind: infrastructure type: dataScienceJob spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: ME_STANDALONE + jobType: DEFAULT logGroupId: logId: - compartmentId: projectId: - subnetId: - shapeName: VM.Standard.E3.Flex shapeConfigDetails: memoryInGBs: 16 ocpus: 1 - blockStorageSize: 50 + shapeName: VM.Standard.E3.Flex + subnetId: runtime: kind: runtime - type: script + type: python spec: conda: - slug: tensorflow28_p38_cpu_v1 + slug: pytorch19_p37_cpu_v1 type: service - scriptPathURI: oci://bucket_name@namespace/path/to/script.py + entrypoint: my_package/my_script.py + outputDir: output + outputUri: oci://bucket_name@namespace/path/to/dir + pythonPath: + - other_packages + scriptPathURI: local/path/to/code_dir + workingDir: code_dir -**ADS Job YAML schema** -.. code-block:: yaml +YAML +==== - kind: - required: true - type: string - allowed: - - job - spec: - required: true - type: dict - schema: - id: - required: false - infrastructure: - required: false - runtime: - required: false - name: - required: false - type: string +A job can also be defined using YAML, as shown in the "YAML" tab. +Here are some examples to load/save the YAML job configurations: -**Data Science Job Infrastructure YAML Schema** +.. code-block:: python -.. code-block:: yaml + # Load a job from a YAML file + job = Job.from_yaml(uri="oci://bucket_name@namespace/path/to/job.yaml") + # Save a job to a YAML file + job.to_yaml(uri="oci://bucket_name@namespace/path/to/job.yaml") - kind: - required: true - type: "string" - allowed: - - "infrastructure" - type: - required: true - type: "string" - allowed: - - "dataScienceJob" - spec: - required: true - type: "dict" - schema: - blockStorageSize: - default: 50 - min: 50 - required: false - type: "integer" - compartmentId: - required: false - type: "string" - displayName: - required: false - type: "string" - id: - required: false - type: "string" - logGroupId: - required: false - type: "string" - logId: - required: false - type: "string" - projectId: - required: false - type: "string" - shapeName: - required: false - type: "string" - subnetId: - required: false - type: "string" - shapeConfigDetails: - required: false - type: "dict" + # Save a job to YAML in a string + yaml_string = job.to_yaml() + + # Load a job from a YAML string + job = Job.from_yaml(""" + kind: job + spec: + infrastructure: + kind: infrastructure + ... + """") +The ``uri`` can be a local file path or a remote location supported by +`fsspec `_, including OCI object storage. From e094c74d461f33bd077cc857cb1de86de536bc46 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 10:36:10 -0800 Subject: [PATCH 018/147] ODSC-38627: update docs --- .../framework_specific_instruction.rst | 3 +- .../{huggingface.rst => huggingfacemodel.rst} | 63 ++++++++----------- .../model_registration/quick_start.rst | 61 ++++++++++++++++++ .../user_guide/model_serialization/index.rst | 1 + 4 files changed, 89 insertions(+), 39 deletions(-) rename docs/source/user_guide/model_registration/frameworks/{huggingface.rst => huggingfacemodel.rst} (76%) diff --git a/docs/source/user_guide/model_registration/framework_specific_instruction.rst b/docs/source/user_guide/model_registration/framework_specific_instruction.rst index 776c15639..9150bf2fb 100644 --- a/docs/source/user_guide/model_registration/framework_specific_instruction.rst +++ b/docs/source/user_guide/model_registration/framework_specific_instruction.rst @@ -9,6 +9,7 @@ frameworks/sparkpipelinemodel frameworks/lightgbmmodel frameworks/xgboostmodel + frameworks/huggingfacemodel frameworks/automlmodel frameworks/genericmodel - + diff --git a/docs/source/user_guide/model_registration/frameworks/huggingface.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst similarity index 76% rename from docs/source/user_guide/model_registration/frameworks/huggingface.rst rename to docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index cee4a68e2..19c125db7 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingface.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -10,7 +10,7 @@ See `API Documentation <../../../ads.model_framework.html#ads.model.framework.hu Overview ======== -The ``ads.model.framework.huggingface_model.HuggingFacePipelineModel`` class in ADS is designed to allow you to rapidly get a HuggingFace Pipeline into production. The ``.prepare()`` method creates the model artifacts that are needed to deploy a functioning pipeline without you having to configure it or write code. However, you can customize the required ``score.py`` file. +The ``ads.model.framework.huggingface_model.HuggingFacePipelineModel`` class in ADS is designed to allow you to rapidly get a HuggingFace pipelines into production. The ``.prepare()`` method creates the model artifacts that are needed to deploy a functioning pipeline without you having to configure it or write code. However, you can customize the required ``score.py`` file. .. include:: ../_template/overview.rst @@ -22,15 +22,13 @@ Load a `ImageSegmentationPipeline >> from transformers import pipeline + >>> segmenter = pipeline(task="image-segmentation", model="facebook/detr-resnet-50-panoptic", revision="fc15262") + >>> preds = segmenter( + ... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" + ... ) + >>> preds [{'score': 0.987885, 'label': 'LABEL_184', 'mask': }, @@ -65,17 +63,10 @@ Prepare Model Artifact # More info here - https://huggingface.co/docs/transformers/main_classes/pipelines#transformers.pipeline -Instantiate a ``HuggingFacePipelineModel()`` object with a HuggingFace Pipelines model. Each instance accepts the following parameters: - -* ``artifact_dir: str``. Artifact directory to store the files needed for deployment. -* ``auth: (Dict, optional)``: Defaults to ``None``. The default authentication is set using the ``ads.set_auth`` API. To override the default, use ``ads.common.auth.api_keys()`` or ``ads.common.auth.resource_principal()`` and create the appropriate authentication signer and the ``**kwargs`` required to instantiate the ``IdentityClient`` object. -* ``estimator: Callable``. Any model object generated by the PyTorch framework. -* ``properties: (ModelProperties, optional)``. Defaults to ``None``. The ``ModelProperties`` object required to save and deploy model. +Instantiate a ``HuggingFacePipelineModel()`` object with HuggingFace pipelines. All the pipelines related files are saved under the ``artifact_dir``. For more detailed information on what parameters that ``HuggingFacePipelineModel`` takes, refer to the `API Documentation <../../../ads.model_framework.html#ads.model.framework.huggingface_model.HuggingFacePipelineModel>`__ -All the pipelines related files are saved under the ``artifact_dir``. -.. include:: ../_template/initialize.rst Summary Status @@ -106,13 +97,12 @@ Deploy and Generate Endpoint >>> # Deploy and create an endpoint for the huggingface_pipeline_model >>> huggingface_pipeline_model.deploy( - display_name="HuggingFace Pipeline Model For Image Segmentation", - deployment_log_group_id="ocid1.loggroup.oc1.xxx.xxxxx", - deployment_access_log_id="ocid1.log.oc1.xxx.xxxxx", - deployment_predict_log_id="ocid1.log.oc1.xxx.xxxxx", - ) + ... display_name="HuggingFace Pipeline Model For Image Segmentation", + ... deployment_log_group_id="ocid1.loggroup.oc1.xxx.xxxxx", + ... deployment_access_log_id="ocid1.log.oc1.xxx.xxxxx", + ... deployment_predict_log_id="ocid1.log.oc1.xxx.xxxxx", + ... ) >>> print(f"Endpoint: {huggingface_pipeline_model.model_deployment.url}") -.. parsed-literal:: https://modeldeployment.{region}.oci.customer-oci.com/ocid1.datasciencemodeldeployment.oc1.xxx.xxxxx Run Prediction against Endpoint @@ -120,21 +110,18 @@ Run Prediction against Endpoint .. code-block:: python3 - # Download an image - import PIL.Image - import requests - import cloudpickle - image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" - - image = PIL.Image.open(requests.get(image_url, stream=True).raw) - image_bytes = cloudpickle.dumps(image) - - # Generate prediction by invoking the deployed endpoint - preds = huggingface_pipeline_model.predict(image)["prediction"] - print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) + >>> # Download an image + >>> import PIL.Image + >>> import requests + >>> import cloudpickle + >>> image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" -.. parsed-literal:: + >>> image = PIL.Image.open(requests.get(image_url, stream=True).raw) + >>> image_bytes = cloudpickle.dumps(image) + >>> # Generate prediction by invoking the deployed endpoint + >>> preds = huggingface_pipeline_model.predict(image)["prediction"] + >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) [{'score': 0.9879, 'label': 'LABEL_184'}, {'score': 0.9973, 'label': 'snow'}, {'score': 0.9972, 'label': 'cat'}] @@ -157,6 +144,7 @@ Predict with Multiple Arguments If your model takes more than one argument, you can pass in through dictionary with the keys as the argument name and values as the value of the arguement. .. code-block:: python3 + >>> your_huggingface_pipeline_model.verify({"parameter_name_1": "parameter_value_1", ..., "parameter_name_n": "parameter_value_n"}) >>> your_huggingface_pipeline_model.predict({"parameter_name_1": "parameter_value_1", ..., "parameter_name_n": "parameter_value_n"}) @@ -166,7 +154,7 @@ Run Prediction with oci sdk Model deployment endpoints can be invoked with the oci sdk. This example invokes a model deployment with the oci sdk with a ``bytes`` payload: -`bytes` payload example +``bytes`` payload example ------------------------------ .. code-block:: python3 @@ -183,7 +171,6 @@ Model deployment endpoints can be invoked with the oci sdk. This example invokes >>> preds = requests.post(endpoint, data=image_bytes, auth=ads.common.auth.default_signer()['signer'], headers=headers).json() >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) -.. parsed-literal:: [{'score': 0.9879, 'label': 'LABEL_184'}, {'score': 0.9973, 'label': 'snow'}, {'score': 0.9972, 'label': 'cat'}] diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 2c066cd44..aea05078b 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -257,6 +257,67 @@ Create a model, prepare it, verify that it works, save it to the model catalog, #Register TensorFlow model model_id = tf_model.save(display_name="TensorFlow Model") +HuggingFace Pipelines +--------------------- + +.. code-block:: python3 + + from transformers import pipeline + import tempfile + import PIL.Image + import ads + import requests + import cloudpickle + + ## download the image + image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + image = PIL.Image.open(requests.get(image_link, stream=True).raw) + image_bytes = cloudpickle.dumps(image) + + ## download the pretrained model + classifier = pipeline(model="openai/clip-vit-large-patch14") + classifier( + images=image, + candidate_labels=["animals", "humans", "landscape"], + ) + + ## Initiate a HuggingFacePipelineModel instance + zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) + + ## Prepare a model artifact + conda = "oci://bucket@namespace/path/to/conda/pack" + python_version = "3.8" + zero_shot_image_classification_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + + ## Test data + data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} + body = cloudpickle.dumps(data) # convert image to bytes + + ## Verify + zero_shot_image_classification_model.verify(data=data) + zero_shot_image_classification_model.verify(data=body) + + ## Save + zero_shot_image_classification_model.save() + + ## Deploy + log_group_id = "" + log_id = "" + zero_shot_image_classification_model.deploy(deployment_bandwidth_mbps=100, + wait_for_completion=False, + deployment_log_group_id = log_group_id, + deployment_access_log_id = log_id, + deployment_predict_log_id = log_id) + zero_shot_image_classification_model.predict(image) + zero_shot_image_classification_model.predict(body) + + ### Invoke the model by sending bytes + auth = ads.common.auth.default_signer()['signer'] + endpoint = zero_shot_image_classification_model.model_deployment.url + "/predict" + headers = {"Content-Type": "application/octet-stream"} + requests.post(endpoint, data=body, auth=auth, headers=headers).json() + + Other Frameworks ---------------- diff --git a/docs/source/user_guide/model_serialization/index.rst b/docs/source/user_guide/model_serialization/index.rst index b828db7c9..c7a132ddb 100644 --- a/docs/source/user_guide/model_serialization/index.rst +++ b/docs/source/user_guide/model_serialization/index.rst @@ -13,6 +13,7 @@ Model Serialization quick_start automlmodel genericmodel + huggingfacemodel lightgbmmodel pytorchmodel sklearnmodel From fba684e45d912be3b6ed3b07626b8ba0f62a57f5 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 10:42:08 -0800 Subject: [PATCH 019/147] ODSC-38627: update docs --- .../model_registration/quick_start.rst | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index aea05078b..e36423d68 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -28,7 +28,7 @@ Sklearn estimator=sklearn_estimator, artifact_dir=tempfile.mkdtemp() ) - # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + # Autogenerate score.py, serialized model, runtime.yaml, input_schema.json and output_schema.json sklearn_model.prepare( inference_conda_env="dbexp_p38_cpu_v1", X_sample=X_train, @@ -68,7 +68,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, # Instantite ads.model.framework.xgboost_model.XGBoostModel using the trained XGBoost Model xgboost_model = XGBoostModel(estimator=xgboost_estimator, artifact_dir=tempfile.mkdtemp()) - # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + # Autogenerate score.py, serialized model, runtime.yaml, input_schema.json and output_schema.json xgboost_model.prepare( inference_conda_env="generalml_p38_cpu_v1", X_sample=X_train, @@ -109,7 +109,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, # Instantite ads.model.lightgbm_model.XGBoostModel using the trained LGBM Model lightgbm_model = LightGBMModel(estimator=lightgbm_estimator, artifact_dir=tempfile.mkdtemp()) - # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + # Autogenerate score.py, serialized model, runtime.yaml, input_schema.json and output_schema.json lightgbm_model.prepare( inference_conda_env="generalml_p38_cpu_v1", X_sample=X_train, @@ -154,7 +154,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, # Verify generated artifacts torch_model.verify(test_data) - #Register PyTorch model + # Register PyTorch model model_id = torch_model.save(display_name="PyTorch Model") @@ -214,7 +214,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, # Verify generated artifacts prediction = spark_model.verify(test) - #Register Spark model + # Register Spark model spark_model.save(display_name="Spark Pipeline Model") @@ -248,13 +248,13 @@ Create a model, prepare it, verify that it works, save it to the model catalog, # Instantite ads.model.framework.tensorflow_model.TensorFlowModel using the pre-trained TensorFlow Model tf_model = TensorFlowModel(tf_estimator, artifact_dir=tempfile.mkdtemp()) - # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + # Autogenerate score.py, serialized model, runtime.yaml, input_schema.json and output_schema.json tf_model.prepare(inference_conda_env="tensorflow28_p38_cpu_v1") # Verify generated artifacts tf_model.verify(x_test[:1]) - #Register TensorFlow model + # Register TensorFlow model model_id = tf_model.save(display_name="TensorFlow Model") HuggingFace Pipelines @@ -284,20 +284,20 @@ HuggingFace Pipelines ## Initiate a HuggingFacePipelineModel instance zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) - ## Prepare a model artifact - conda = "oci://bucket@namespace/path/to/conda/pack" - python_version = "3.8" - zero_shot_image_classification_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + # Autogenerate score.py, serialized model, runtime.yaml + conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" + python_version = "3.x" + zero_shot_image_classification_model.prepare(inference_conda_env=conda_pack_path, inference_python_version = python_version, force_overwrite=True) ## Test data data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} body = cloudpickle.dumps(data) # convert image to bytes - ## Verify + # Verify generated artifacts zero_shot_image_classification_model.verify(data=data) zero_shot_image_classification_model.verify(data=body) - ## Save + # Register HuggingFace Pipeline model zero_shot_image_classification_model.save() ## Deploy @@ -336,7 +336,7 @@ Other Frameworks generic_model = GenericModel(estimator=model, artifact_dir=tempfile.mkdtemp()) generic_model.summary_status() - # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + # Autogenerate score.py, serialized model, runtime.yaml, input_schema.json and output_schema.json generic_model.prepare( inference_conda_env="dbexp_p38_cpu_v1", model_file_name="toy_model.pkl", @@ -372,7 +372,7 @@ With Model Version Set # Within the context manager, you can save the :ref:`Model Serialization` model without specifying the ``model_version_set`` parameter because it's taken from the model context manager. If the model version set doesn't exist in the model catalog, the example creates a model version set named ``my_model_version_set``. If the model version set exists in the model catalog, the models are saved to that model version set. with ads.model.experiment(name="my_model_version_set", create_if_not_exists=True): - # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + # Autogenerate score.py, serialized model, runtime.yaml, input_schema.json and output_schema.json generic_model.prepare( inference_conda_env="dbexp_p38_cpu_v1", model_file_name="toy_model.pkl", From 805111faa5ad1900d48f89799a8cb4f686770d12 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 10:43:38 -0800 Subject: [PATCH 020/147] ODSC-38627: fix the comments --- .../frameworks/huggingfacemodel.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 19c125db7..1998b822f 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -203,20 +203,20 @@ Example ## Initiate a HuggingFacePipelineModel instance zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) - ## Prepare a model artifact - conda = "oci://bucket@namespace/path/to/conda/pack" - python_version = "3.8" - zero_shot_image_classification_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + # Autogenerate score.py, serialized model, runtime.yaml + conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" + python_version = "3.x" + zero_shot_image_classification_model.prepare(inference_conda_env=conda_pack_path, inference_python_version = python_version, force_overwrite=True) ## Test data data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} body = cloudpickle.dumps(data) # convert image to bytes - ## Verify + # Verify generated artifacts zero_shot_image_classification_model.verify(data=data) zero_shot_image_classification_model.verify(data=body) - ## Save + # Register HuggingFace Pipeline model zero_shot_image_classification_model.save() ## Deploy From 74eaffd4e61b9ad7f63d2b34e36af7266bda08d7 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 12:34:47 -0800 Subject: [PATCH 021/147] adding release notes --- docs/source/release_notes.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index dece7585a..bb40c4757 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -2,6 +2,13 @@ Release Notes ============= +2.8.1 +----- +Release date: February 16, 2023 + +* Fixed a bug for ads opctl run when --auth flag is passed and image is built by ads. +* Fixed a bug in OCIDataScienceModel._wait_for_work_request when the work requests are not successfully populated. +* Fixed a bug in DataScienceModel.create to when the provenance metadata is not provided. 2.8.0 ----- From 57e3c65c618c1ec9c4ef02a344ae01cd08aade35 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 13:21:17 -0800 Subject: [PATCH 022/147] format --- docs/source/release_notes.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index bb40c4757..fa453e2a0 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -6,9 +6,9 @@ Release Notes ----- Release date: February 16, 2023 -* Fixed a bug for ads opctl run when --auth flag is passed and image is built by ads. -* Fixed a bug in OCIDataScienceModel._wait_for_work_request when the work requests are not successfully populated. -* Fixed a bug in DataScienceModel.create to when the provenance metadata is not provided. +* Fixed a bug for ``ads opctl run`` when ``--auth`` flag is passed and image is built by ads. +* Fixed a bug in ``OCIDataScienceModel._wait_for_work_request`` when the work requests are not successfully populated. +* Fixed a bug in ``DataScienceModel.create`` to when the provenance metadata is not provided. 2.8.0 ----- From 9b2b451890cc4b3ffa2632f97561ab70c956ed06 Mon Sep 17 00:00:00 2001 From: Dmitrii Cherkasov Date: Thu, 16 Feb 2023 16:22:09 -0800 Subject: [PATCH 023/147] Adds information about the configuration. --- .../user_guide/apachespark/dataflow.rst | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/source/user_guide/apachespark/dataflow.rst b/docs/source/user_guide/apachespark/dataflow.rst index 75a9a7bf7..f7bc7232f 100644 --- a/docs/source/user_guide/apachespark/dataflow.rst +++ b/docs/source/user_guide/apachespark/dataflow.rst @@ -161,6 +161,10 @@ You could submit a notebook using ADS SDK APIs. Here is an example to submit a n .with_executor_shape_config(ocpus=4, memory_in_gbs=64) .with_logs_bucket_uri("oci://mybucket@mytenancy/") .with_private_endpoint_id("ocid1.dataflowprivateendpoint.oc1.iad.") + .with_configuration({ + "spark.driverEnv.myEnvVariable": "value1", + "spark.executorEnv.myEnvVariable": "value2", + }) ) rt = ( DataFlowNotebookRuntime() @@ -169,7 +173,6 @@ You could submit a notebook using ADS SDK APIs. Here is an example to submit a n ) # This could be local path or http path to notebook ipynb file .with_script_bucket("") .with_exclude_tag(["ignore", "remove"]) # Cells to Ignore - .with_environment_variable(env1="test", env2= "test2") # will be propagated to both driver and executor ) job = Job(infrastructure=df, runtime=rt).create(overwrite=True) df_run = job.run(wait=True) @@ -213,7 +216,7 @@ The ``DataFlowRuntime`` properties are: - ``with_archive_uri`` (`doc `__) - ``with_archive_bucket`` - ``with_custom_conda`` -- ``with_environment_variable`` +- ``with_configuration`` For more details, see the `runtime class documentation <../../ads.jobs.html#module-ads.jobs.builders.runtimes.python_runtime>`__. @@ -272,7 +275,10 @@ accepted. In the next example, the prefix is given for ``script_bucket``. .with_script_uri(os.path.join(td, "script.py")) .with_script_bucket("oci://mybucket@namespace/prefix") .with_custom_conda("oci://@/") - .with_environment_variable(env1="test", env2= "test2") # will be propagated to both driver and executor + .with_configuration({ + "spark.driverEnv.myEnvVariable": "value1", + "spark.executorEnv.myEnvVariable": "value2", + }) ) df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) df.create() @@ -380,6 +386,10 @@ In the next example, ``archive_uri`` is given as an Object Storage location. .with_executor_shape("VM.Standard.E4.Flex") .with_executor_shape_config(ocpus=4, memory_in_gbs=64) .with_spark_version("3.0.2") + .with_configuration({ + "spark.driverEnv.myEnvVariable": "value1", + "spark.executorEnv.myEnvVariable": "value2", + }) ) runtime_config = ( DataFlowRuntime() @@ -558,11 +568,11 @@ into the ``Job.from_yaml()`` function to build a Data Flow job: runtime: kind: runtime spec: + configuration: + spark.driverEnv.myEnvVariable: value1 + spark.executorEnv.myEnvVariable: value2 scriptBucket: bucket_name scriptPathURI: oci://@/ - env: - - name: env1 - value: test1 type: dataFlow **Data Flow Infrastructure YAML Schema** @@ -631,6 +641,9 @@ into the ``Job.from_yaml()`` function to build a Data Flow job: privateEndpointId: required: false type: string + configuration: + required: false + type: dict type: allowed: - dataFlow @@ -675,11 +688,9 @@ into the ``Job.from_yaml()`` function to build a Data Flow job: - service required: true type: string - env: - type: list + configuration: required: false - schema: - type: dict + type: dict freeform_tag: required: false type: dict From ead4daab6e6ad9a6ad90ea270cbb666ee431db2b Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 17:22:56 -0800 Subject: [PATCH 024/147] update based on the comments --- docs/source/release_notes.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index fa453e2a0..74b351ebb 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -6,9 +6,9 @@ Release Notes ----- Release date: February 16, 2023 -* Fixed a bug for ``ads opctl run`` when ``--auth`` flag is passed and image is built by ads. -* Fixed a bug in ``OCIDataScienceModel._wait_for_work_request`` when the work requests are not successfully populated. -* Fixed a bug in ``DataScienceModel.create`` to when the provenance metadata is not provided. +* Fixed a bug for ``ads opctl run`` when ``--auth`` flag is passed and image is built by ADS. +* Fixed a bug in ``GenericModel.save()`` when the work requests are not successfully populated. +* Fixed a bug in ``DataScienceModel.create()`` to when the provenance metadata is not provided. 2.8.0 ----- From d54b350552c6696dfc2f4c74d81a35b32fadf833 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 16 Feb 2023 18:30:07 -0800 Subject: [PATCH 025/147] update the ads folder --- ads/ads_version.json | 2 +- ads/catalog/model.py | 12 +++++++++--- ads/common/auth.py | 2 +- ads/model/datascience_model.py | 11 ++++++----- ads/model/service/oci_datascience_model.py | 12 +++++++++--- ads/opctl/cli.py | 8 +++++++- 6 files changed, 33 insertions(+), 14 deletions(-) diff --git a/ads/ads_version.json b/ads/ads_version.json index 4d01ef82b..a3343c314 100644 --- a/ads/ads_version.json +++ b/ads/ads_version.json @@ -1,3 +1,3 @@ { - "version": "2.8.0" + "version": "2.8.1" } diff --git a/ads/catalog/model.py b/ads/catalog/model.py index 28c9d79b7..d107a620a 100644 --- a/ads/catalog/model.py +++ b/ads/catalog/model.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import warnings @@ -1559,7 +1559,8 @@ def _wait_for_work_request( work_request_logs = self.ds_client.list_work_request_logs( work_request_id ).data - new_work_request_logs = work_request_logs[i:] + if work_request_logs: + new_work_request_logs = work_request_logs[i:] for wr_item in new_work_request_logs: progress.update(wr_item.message) @@ -1567,7 +1568,12 @@ def _wait_for_work_request( if work_request.data.status in STOP_STATE: if work_request.data.status != WorkRequest.STATUS_SUCCEEDED: - raise Exception(work_request_logs[-1].message) + if work_request_logs: + raise Exception(work_request_logs[-1].message) + else: + raise Exception( + "An error occurred in attempt to perform the operation. Check the service logs to get more details." + ) else: break return work_request diff --git a/ads/common/auth.py b/ads/common/auth.py index 91535d502..f3dbd7ab9 100644 --- a/ads/common/auth.py +++ b/ads/common/auth.py @@ -283,7 +283,7 @@ def create_signer( >>> auth = ads.auth.create_signer(config=config) # api_key type of authentication dictionary created based on provided config >>> singer = oci.auth.signers.get_resource_principals_signer() - >>> auth = ads.auth.create_signer(config={}, singer=signer) # resource principals authentication dictionary created + >>> auth = ads.auth.create_signer(config={}, signer=signer) # resource principals authentication dictionary created >>> auth = ads.auth.create_signer(auth_type='instance_principal') # instance principals authentication dictionary created diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 91ab8b25e..058725d15 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import cgi @@ -590,10 +590,11 @@ def create(self, **kwargs) -> "DataScienceModel": self.dsc_model = self._to_oci_dsc_model(**kwargs).create() # Create model provenance - logger.info("Saving model provenance metadata.") - self.dsc_model.create_model_provenance( - self.provenance_metadata._to_oci_metadata() - ) + if self.provenance_metadata: + logger.info("Saving model provenance metadata.") + self.dsc_model.create_model_provenance( + self.provenance_metadata._to_oci_metadata() + ) # Upload artifacts logger.info("Uploading model artifacts.") diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 0dff7e180..86dbf5bf9 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import logging @@ -573,7 +573,8 @@ def _wait_for_work_request(self, work_request_id: str, num_steps: int = 3) -> No work_request_logs = self.client.list_work_request_logs( work_request_id ).data - new_work_request_logs = work_request_logs[i:] + if work_request_logs: + new_work_request_logs = work_request_logs[i:] for wr_item in new_work_request_logs: progress.update(wr_item.message) @@ -581,6 +582,11 @@ def _wait_for_work_request(self, work_request_id: str, num_steps: int = 3) -> No if work_request.data.status in STOP_STATE: if work_request.data.status != WorkRequest.STATUS_SUCCEEDED: - raise Exception(work_request_logs[-1].message) + if work_request_logs: + raise Exception(work_request_logs[-1].message) + else: + raise Exception( + "An error occurred in attempt to perform the operation. Check the service logs to get more details." + ) else: break diff --git a/ads/opctl/cli.py b/ads/opctl/cli.py index 550540d44..194548c2d 100644 --- a/ads/opctl/cli.py +++ b/ads/opctl/cli.py @@ -12,6 +12,7 @@ import yaml from ads.common.auth import AuthType +from ads.common import auth as authutil from ads.opctl.cmds import cancel as cancel_cmd from ads.opctl.cmds import configure as configure_cmd from ads.opctl.cmds import delete as delete_cmd @@ -358,7 +359,12 @@ def run(file, **kwargs): debug = kwargs["debug"] if file: if os.path.exists(file): - auth = kwargs["auth"] or authutil.default_signer() + auth = {} + if kwargs["auth"]: + auth = authutil.create_signer(kwargs["auth"]) + else: + auth = authutil.default_signer() + with fsspec.open(file, "r", **auth) as f: config = suppress_traceback(debug)(yaml.safe_load)(f.read()) else: From 5a4ea9b6abe5d1955f847498abe2a8715f2a364f Mon Sep 17 00:00:00 2001 From: MING KANG Date: Fri, 17 Feb 2023 09:36:07 -0800 Subject: [PATCH 026/147] fixed job run_a_script example --- docs/source/user_guide/jobs/run_script.rst | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/source/user_guide/jobs/run_script.rst b/docs/source/user_guide/jobs/run_script.rst index b570db370..b19109bfa 100644 --- a/docs/source/user_guide/jobs/run_script.rst +++ b/docs/source/user_guide/jobs/run_script.rst @@ -201,11 +201,14 @@ Suppose you want to run the following python script named ``job_script_env.py``: import os import sys - print("Hello " + os.environ["KEY1"] + " and " + os.environ["KEY2"])""") + print("Hello " + os.environ["KEY1"] + " and " + os.environ["KEY2"]) This example runs a job with environment variables: .. code-block:: python3 + from ads.jobs import Job + from ads.jobs import DataScienceJob + from ads.jobs import ScriptRuntime job = Job() job.with_infrastructure( @@ -246,12 +249,12 @@ You could create the preceding example job with the following YAML file: .. code-block:: yaml - kind: job - spec: + kind: job + spec: infrastructure: - kind: infrastructure + kind: infrastructure type: dataScienceJob - spec: + spec: logGroupId: logId: compartmentId: @@ -263,11 +266,11 @@ You could create the preceding example job with the following YAML file: ocpus: 1 blockStorageSize: 50 runtime: - kind: runtime + kind: runtime type: python - spec: - env: - - name: KEY1 + spec: + env: + - name: KEY1 value: - name: KEY2 value: From 56ce081d9739af2d1fbf467b1aa1cb9719e5f8ec Mon Sep 17 00:00:00 2001 From: MING KANG Date: Fri, 17 Feb 2023 09:58:29 -0800 Subject: [PATCH 027/147] removed -e . in requirements.txt to make pip install -r requirements.txt work --- docs/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 4210339a3..f8e332b13 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,3 @@ --e . autodoc nbsphinx sphinx From f9bc491e49f3d949415b8688684de373bed3612b Mon Sep 17 00:00:00 2001 From: MING KANG Date: Fri, 17 Feb 2023 10:26:16 -0800 Subject: [PATCH 028/147] fixed job run_a_script example --- docs/source/user_guide/jobs/run_script.rst | 37 ++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/source/user_guide/jobs/run_script.rst b/docs/source/user_guide/jobs/run_script.rst index b19109bfa..1903cc994 100644 --- a/docs/source/user_guide/jobs/run_script.rst +++ b/docs/source/user_guide/jobs/run_script.rst @@ -128,6 +128,10 @@ This example runs a job with CLI arguments: .. code-block:: python3 + from ads.jobs import Job + from ads.jobs import DataScienceJob + from ads.jobs import ScriptRuntime + job = Job() job.with_infrastructure( DataScienceJob() @@ -161,9 +165,9 @@ You could create the preceding example job with the following YAML file: .. code-block:: yaml - kind: job - spec: - infrastructure: + kind: job + spec: + infrastructure: kind: infrastructure type: dataScienceJob spec: @@ -177,14 +181,14 @@ You could create the preceding example job with the following YAML file: memoryInGBs: 16 ocpus: 1 blockStorageSize: 50 - runtime: - kind: runtime + runtime: + kind: runtime type: python - spec: - args: - - - - - scriptPathURI: job_script_argument.py + spec: + args: + - + - + scriptPathURI: job_script_env.py Environment Variables @@ -206,6 +210,7 @@ Suppose you want to run the following python script named ``job_script_env.py``: This example runs a job with environment variables: .. code-block:: python3 + from ads.jobs import Job from ads.jobs import DataScienceJob from ads.jobs import ScriptRuntime @@ -251,7 +256,7 @@ You could create the preceding example job with the following YAML file: kind: job spec: - infrastructure: + infrastructure: kind: infrastructure type: dataScienceJob spec: @@ -265,16 +270,16 @@ You could create the preceding example job with the following YAML file: memoryInGBs: 16 ocpus: 1 blockStorageSize: 50 - runtime: + runtime: kind: runtime type: python spec: env: - name: KEY1 - value: - - name: KEY2 - value: - scriptPathURI: job_script_env.py + value: + - name: KEY2 + value: + scriptPathURI: job_script_env.py From 56f2042cce0c75f187a1dcb34e92244eeea6a78d Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Sun, 19 Feb 2023 15:57:08 -0800 Subject: [PATCH 029/147] Removed ads.* rst files (#89) --- docs/source/ads.automl.rst | 30 --- docs/source/ads.bds.rst | 21 -- docs/source/ads.catalog.rst | 46 ---- docs/source/ads.common.rst | 112 --------- docs/source/ads.data_labeling.rst | 157 ------------ docs/source/ads.database.rst | 24 -- docs/source/ads.dataflow.rst | 31 --- docs/source/ads.dataset.rst | 206 ---------------- docs/source/ads.evaluations.rst | 38 --- docs/source/ads.explanations.rst | 61 ----- docs/source/ads.feature_engineering.rst | 309 ------------------------ docs/source/ads.hpo.rst | 38 --- docs/source/ads.jobs.rst | 45 ---- docs/source/ads.model.rst | 125 ---------- docs/source/ads.model.runtime.rst | 53 ---- docs/source/ads.model_deployment.rst | 38 --- docs/source/ads.model_framework.rst | 61 ----- docs/source/ads.oracledb.rst | 13 - docs/source/ads.rst | 50 ---- docs/source/ads.secrets.rst | 62 ----- docs/source/ads.text_dataset.rst | 46 ---- docs/source/ads.vault.rst | 21 -- 22 files changed, 1587 deletions(-) delete mode 100644 docs/source/ads.automl.rst delete mode 100644 docs/source/ads.bds.rst delete mode 100644 docs/source/ads.catalog.rst delete mode 100644 docs/source/ads.common.rst delete mode 100644 docs/source/ads.data_labeling.rst delete mode 100644 docs/source/ads.database.rst delete mode 100644 docs/source/ads.dataflow.rst delete mode 100644 docs/source/ads.dataset.rst delete mode 100644 docs/source/ads.evaluations.rst delete mode 100644 docs/source/ads.explanations.rst delete mode 100644 docs/source/ads.feature_engineering.rst delete mode 100644 docs/source/ads.hpo.rst delete mode 100644 docs/source/ads.jobs.rst delete mode 100644 docs/source/ads.model.rst delete mode 100644 docs/source/ads.model.runtime.rst delete mode 100644 docs/source/ads.model_deployment.rst delete mode 100644 docs/source/ads.model_framework.rst delete mode 100644 docs/source/ads.oracledb.rst delete mode 100644 docs/source/ads.rst delete mode 100644 docs/source/ads.secrets.rst delete mode 100644 docs/source/ads.text_dataset.rst delete mode 100644 docs/source/ads.vault.rst diff --git a/docs/source/ads.automl.rst b/docs/source/ads.automl.rst deleted file mode 100644 index c45b7223b..000000000 --- a/docs/source/ads.automl.rst +++ /dev/null @@ -1,30 +0,0 @@ -ads.automl package -================== - -Submodules ----------- - -ads.automl.driver module ------------------------- - -.. automodule:: ads.automl.driver - :members: - :undoc-members: - :show-inheritance: - -ads.automl.provider module --------------------------- - -.. automodule:: ads.automl.provider - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.automl - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.bds.rst b/docs/source/ads.bds.rst deleted file mode 100644 index a9fd18880..000000000 --- a/docs/source/ads.bds.rst +++ /dev/null @@ -1,21 +0,0 @@ -ads.bds package -=============== - -Submodules ----------- - -ads.bds.auth module -------------------- - -.. automodule:: ads.bds.auth - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.bds - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.catalog.rst b/docs/source/ads.catalog.rst deleted file mode 100644 index 943629697..000000000 --- a/docs/source/ads.catalog.rst +++ /dev/null @@ -1,46 +0,0 @@ -ads.catalog package -=================== - -Submodules ----------- - -ads.catalog.model module ------------------------- - -.. automodule:: ads.catalog.model - :members: - :undoc-members: - :show-inheritance: - -ads.catalog.notebook module ---------------------------- - -.. automodule:: ads.catalog.notebook - :members: - :undoc-members: - :show-inheritance: - -ads.catalog.project module --------------------------- - -.. automodule:: ads.catalog.project - :members: - :undoc-members: - :show-inheritance: - -ads.catalog.summary module --------------------------- - -.. automodule:: ads.catalog.summary - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.catalog - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.common.rst b/docs/source/ads.common.rst deleted file mode 100644 index dd26d8ac0..000000000 --- a/docs/source/ads.common.rst +++ /dev/null @@ -1,112 +0,0 @@ -ads.common package -================== - -Submodules ----------- - -ads.common.card\_identifier module ----------------------------------- - -.. automodule:: ads.common.card_identifier - :members: - :undoc-members: - :show-inheritance: - -ads.common.auth module ----------------------- - -.. automodule:: ads.common.auth - :members: - :undoc-members: - :show-inheritance: - -ads.common.data module ----------------------- - -.. automodule:: ads.common.data - :members: - :undoc-members: - :show-inheritance: - -ads.common.model module ------------------------ - -.. automodule:: ads.common.model - :members: - :undoc-members: - :show-inheritance: - -ads.common.model\_metadata module ---------------------------------- - -.. automodule:: ads.common.model_metadata - :members: - :undoc-members: - :show-inheritance: - -ads.common.decorator.runtime\_dependency module ------------------------------------------------ - -.. automodule:: ads.common.decorator.runtime_dependency - :members: - :undoc-members: - :show-inheritance: - -ads.common.decorator.deprecate module -------------------------------------- - -.. automodule:: ads.common.decorator.deprecate - :members: - :undoc-members: - :show-inheritance: - -ads.common.model\_introspect module ------------------------------------ - -.. automodule:: ads.common.model_introspect - :members: - :undoc-members: - :show-inheritance: - -ads.common.model\_export\_util module -------------------------------------- - -.. automodule:: ads.common.model_export_util - :members: - :undoc-members: - :show-inheritance: - -ads.common.function.fn\_util module ------------------------------------ - -.. automodule:: ads.common.function.fn_util - :members: - :undoc-members: - :show-inheritance: - - -ads.common.utils module ------------------------ - -.. automodule:: ads.common.utils - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.common - :members: - :undoc-members: - :show-inheritance: - - -ads.common.model\_metadata\_mixin module ----------------------------------------- - -.. automodule:: ads.common.model_metadata_mixin - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.data_labeling.rst b/docs/source/ads.data_labeling.rst deleted file mode 100644 index c0502ebd2..000000000 --- a/docs/source/ads.data_labeling.rst +++ /dev/null @@ -1,157 +0,0 @@ -ads.data\_labeling package -========================== - -Submodules ----------- - -ads.data\_labeling.interface.loader module ------------------------------------------- - -.. automodule:: ads.data_labeling.interface.loader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.interface.parser module ------------------------------------------- - -.. automodule:: ads.data_labeling.interface.parser - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.interface.reader module ------------------------------------------- - -.. automodule:: ads.data_labeling.interface.reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.boundingbox module -------------------------------------- - -.. automodule:: ads.data_labeling.boundingbox - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.constants module ------------------------------------ - -.. automodule:: ads.data_labeling.constants - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.data\_labeling\_service module -------------------------------------------------- - -.. automodule:: ads.data_labeling.data_labeling_service - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.metadata module ----------------------------------- - -.. automodule:: ads.data_labeling.metadata - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.ner module ------------------------------ - -.. automodule:: ads.data_labeling.ner - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.record module --------------------------------- - -.. automodule:: ads.data_labeling.record - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.mixin.data\_labeling module ----------------------------------------------- - -.. automodule:: ads.data_labeling.mixin.data_labeling - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.parser.export\_metadata\_parser module ---------------------------------------------------------- - -.. automodule:: ads.data_labeling.parser.export_metadata_parser - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.parser.export\_record\_parser module -------------------------------------------------------- - -.. automodule:: ads.data_labeling.parser.export_record_parser - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.dataset\_reader module ------------------------------------------------- - -.. automodule:: ads.data_labeling.reader.dataset_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.jsonl\_reader module ----------------------------------------------- - -.. automodule:: ads.data_labeling.reader.jsonl_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.metadata\_reader module -------------------------------------------------- - -.. automodule:: ads.data_labeling.reader.metadata_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.record\_reader module ------------------------------------------------ - -.. automodule:: ads.data_labeling.reader.record_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.visualizer.image\_visualizer module ------------------------------------------------------- - -.. automodule:: ads.data_labeling.visualizer.image_visualizer - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.visualizer.text\_visualizer module ------------------------------------------------------ - -.. automodule:: ads.data_labeling.visualizer.text_visualizer - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.data_labeling - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.database.rst b/docs/source/ads.database.rst deleted file mode 100644 index a2df4002f..000000000 --- a/docs/source/ads.database.rst +++ /dev/null @@ -1,24 +0,0 @@ -ads.database package -==================== - -Subpackages ------------ - -Submodules ----------- - -ads.database.connection module ------------------------------- - -.. automodule:: ads.database.connection - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.database - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.dataflow.rst b/docs/source/ads.dataflow.rst deleted file mode 100644 index 83307c986..000000000 --- a/docs/source/ads.dataflow.rst +++ /dev/null @@ -1,31 +0,0 @@ -ads.dataflow package -==================== - -Submodules ----------- - -ads.dataflow.dataflow module ----------------------------- - -.. automodule:: ads.dataflow.dataflow - :members: - :undoc-members: - :show-inheritance: - -ads.dataflow.dataflowsummary module ------------------------------------ - -.. automodule:: ads.dataflow.dataflowsummary - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.dataflow - :members: - :undoc-members: - :show-inheritance: - - diff --git a/docs/source/ads.dataset.rst b/docs/source/ads.dataset.rst deleted file mode 100644 index 8c26e5fc6..000000000 --- a/docs/source/ads.dataset.rst +++ /dev/null @@ -1,206 +0,0 @@ -ads.dataset package -=================== - -Submodules ----------- - -ads.dataset.classification\_dataset module ------------------------------------------- - -.. automodule:: ads.dataset.classification_dataset - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.correlation module ------------------------------- - -.. automodule:: ads.dataset.correlation - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.correlation\_plot module ------------------------------------- - -.. automodule:: ads.dataset.correlation_plot - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.dask\_series module -------------------------------- - -.. automodule:: ads.dataset.dask_series - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.dataframe\_transformer module ------------------------------------------ - -.. automodule:: ads.dataset.dataframe_transformer - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.dataset module --------------------------- - -.. automodule:: ads.dataset.dataset - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.dataset\_browser module ------------------------------------ - -.. automodule:: ads.dataset.dataset_browser - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.dataset\_with\_target module ----------------------------------------- - -.. automodule:: ads.dataset.dataset_with_target - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.exception module ----------------------------- - -.. automodule:: ads.dataset.exception - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.factory module --------------------------- - -.. automodule:: ads.dataset.factory - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.feature\_engineering\_transformer module ----------------------------------------------------- - -.. automodule:: ads.dataset.feature_engineering_transformer - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.feature\_selection module -------------------------------------- - -.. automodule:: ads.dataset.feature_selection - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.forecasting\_dataset module ---------------------------------------- - -.. automodule:: ads.dataset.forecasting_dataset - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.helper module -------------------------- - -.. automodule:: ads.dataset.helper - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.label\_encoder module ---------------------------------- - -.. automodule:: ads.dataset.label_encoder - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.pipeline module ---------------------------- - -.. automodule:: ads.dataset.pipeline - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.plot module ------------------------ - -.. automodule:: ads.dataset.plot - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.progress module ---------------------------- - -.. automodule:: ads.dataset.progress - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.recommendation module ---------------------------------- - -.. automodule:: ads.dataset.recommendation - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.recommendation\_transformer module ----------------------------------------------- - -.. automodule:: ads.dataset.recommendation_transformer - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.regression\_dataset module --------------------------------------- - -.. automodule:: ads.dataset.regression_dataset - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.sampled\_dataset module ------------------------------------ - -.. automodule:: ads.dataset.sampled_dataset - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.target module -------------------------- - -.. automodule:: ads.dataset.target - :members: - :undoc-members: - :show-inheritance: - -ads.dataset.timeseries module ------------------------------ - -.. automodule:: ads.dataset.timeseries - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.dataset - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.evaluations.rst b/docs/source/ads.evaluations.rst deleted file mode 100644 index e4144d794..000000000 --- a/docs/source/ads.evaluations.rst +++ /dev/null @@ -1,38 +0,0 @@ -ads.evaluations package -======================= - -Submodules ----------- - -ads.evaluations.evaluation\_plot module ---------------------------------------- - -.. automodule:: ads.evaluations.evaluation_plot - :members: - :undoc-members: - :show-inheritance: - -ads.evaluations.evaluator module --------------------------------- - -.. automodule:: ads.evaluations.evaluator - :members: - :undoc-members: - :show-inheritance: - -ads.evaluations.statistical\_metrics module -------------------------------------------- - -.. automodule:: ads.evaluations.statistical_metrics - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.evaluations - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.explanations.rst b/docs/source/ads.explanations.rst deleted file mode 100644 index 8077b1a4e..000000000 --- a/docs/source/ads.explanations.rst +++ /dev/null @@ -1,61 +0,0 @@ -ads.explanations package -======================== - -Submodules ----------- - -ads.explanations.base\_explainer module ---------------------------------------- - -.. automodule:: ads.explanations.base_explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.explainer module ---------------------------------- - -.. automodule:: ads.explanations.explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_global\_explainer module ----------------------------------------------- - -.. automodule:: ads.explanations.mlx_global_explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_interface module --------------------------------------- - -.. automodule:: ads.explanations.mlx_interface - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_local\_explainer module ---------------------------------------------- - -.. automodule:: ads.explanations.mlx_local_explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_whatif\_explainer module ----------------------------------------------- - -.. automodule:: ads.explanations.mlx_whatif_explainer - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.explanations - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.feature_engineering.rst b/docs/source/ads.feature_engineering.rst deleted file mode 100644 index af547adf8..000000000 --- a/docs/source/ads.feature_engineering.rst +++ /dev/null @@ -1,309 +0,0 @@ -ads.feature\_engineering package -================================ - -Submodules ----------- - -ads.feature\_engineering.exceptions module ------------------------------------------- - -.. automodule:: ads.feature_engineering.exceptions - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type\_manager module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type_manager - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.dataframe\_accessor module -------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.dataframe_accessor - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.series\_accessor module ----------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.series_accessor - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.correlation module ------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.mixin.correlation - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.eda\_mixin module ------------------------------------------------------------ - -.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.eda\_mixin\_series module -------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin_series - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.feature\_types\_mixin module --------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.mixin.feature_types_mixin - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.adsstring.common\_regex\_mixin module --------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.adsstring.common_regex_mixin - :members: CommonRegexMixin - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.adsstring.oci\_language module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.adsstring.oci_language - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.adsstring.string module ------------------------------------------------- - -.. automodule:: ads.feature_engineering.adsstring.string - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.address module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.address - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.base module ---------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.base - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.boolean module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.boolean - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.category module ----------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.category - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.constant module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.constant - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.continuous module ---------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.continuous - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.creditcard module ---------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.creditcard - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.datetime module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.datetime - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.discrete module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.discrete - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.document module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.document - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.gis module --------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.gis - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.integer module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.integer - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ip_address module --------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ip_address - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ip_address\_v4 module ------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ip_address_v4 - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ip_address\_v6 module ------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ip_address_v6 - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.lat_long module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.lat_long - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.object module ------------------------------------------------------ - -.. automodule:: ads.feature_engineering.feature_type.object - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ordinal module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ordinal - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.phone_number module ----------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.phone_number - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.string module ------------------------------------------------------ - -.. automodule:: ads.feature_engineering.feature_type.string - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.text module ---------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.text - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.unknown module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.unknown - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.zip_code module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.zip_code - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.handler.feature\_validator module ------------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.handler.feature_validator - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.handler.feature\_warning module ------------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.handler.feature_warning - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.handler.warnings module ----------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.handler.warnings - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.feature_engineering - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.hpo.rst b/docs/source/ads.hpo.rst deleted file mode 100644 index e67d72f2f..000000000 --- a/docs/source/ads.hpo.rst +++ /dev/null @@ -1,38 +0,0 @@ -ads.hpo package -======================== - -Submodules ----------- - -ads.hpo.distributions module ---------------------------------------- - -.. automodule:: ads.hpo.distributions - :members: - :undoc-members: - :show-inheritance: - -ads.hpo.search\_cv module ---------------------------------------- - -.. automodule:: ads.hpo.search_cv - :members: - :undoc-members: - :show-inheritance: - -ads.hpo.stopping\_criterion ---------------------------------------- - -.. automodule:: ads.hpo.stopping_criterion - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.hpo - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/source/ads.jobs.rst b/docs/source/ads.jobs.rst deleted file mode 100644 index 7eb2d6482..000000000 --- a/docs/source/ads.jobs.rst +++ /dev/null @@ -1,45 +0,0 @@ -ads.jobs package -================ - -Submodules ----------- - -ads.jobs.ads\_job module ------------------------- - -.. automodule:: ads.jobs.ads_job - :members: - :undoc-members: - :show-inheritance: - -ads.jobs.builders.runtimes.python\_runtime module -------------------------------------------------- - -.. automodule:: ads.jobs.builders.runtimes.python_runtime - :members: - :undoc-members: - :show-inheritance: - -ads.jobs.builders.infrastructure.dataflow module ------------------------------------------------- - -.. automodule:: ads.jobs.builders.infrastructure.dataflow - :members: - :undoc-members: - :show-inheritance: - -ads.jobs.builders.infrastructure.dsc\_job module ------------------------------------------------- - -.. automodule:: ads.jobs.builders.infrastructure.dsc_job - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.jobs - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/docs/source/ads.model.rst b/docs/source/ads.model.rst deleted file mode 100644 index 87731a054..000000000 --- a/docs/source/ads.model.rst +++ /dev/null @@ -1,125 +0,0 @@ -ads.model.framework other package -================================= - -Submodules ----------- - -ads.model.artifact module -------------------------- - -.. automodule:: ads.model.artifact - :members: - :undoc-members: - :show-inheritance: - -ads.model.generic\_model module -------------------------------- - -.. automodule:: ads.model.generic_model - :members: - :undoc-members: - :show-inheritance: - -ads.model.model\_properties module ----------------------------------- - -.. automodule:: ads.model.model_properties - :members: - :undoc-members: - :show-inheritance: - -ads.model.runtime.runtime\_info module --------------------------------------- - -.. automodule:: ads.model.runtime.runtime_info - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.model\_info\_extractor\_factory module ----------------------------------------------------------- - -.. automodule:: ads.model.extractor.model_info_extractor_factory - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.model\_artifact module ------------------------------------------- - -.. automodule:: ads.model.extractor.model_artifact - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.automl\_extractor module --------------------------------------------- - -.. automodule:: ads.model.extractor.automl_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.xgboost\_extractor module ---------------------------------------------- - -.. automodule:: ads.model.extractor.xgboost_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.lightgbm\_extractor module ----------------------------------------------- - -.. automodule:: ads.model.extractor.lightgbm_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.model\_info\_extractor module -------------------------------------------------- - -.. automodule:: ads.model.extractor.model_info_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.sklearn\_extractor module ---------------------------------------------- - -.. automodule:: ads.model.extractor.sklearn_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.keras\_extractor module -------------------------------------------- - -.. automodule:: ads.model.extractor.keras_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.tensorflow\_extractor module ------------------------------------------------- - -.. automodule:: ads.model.extractor.tensorflow_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.pytorch\_extractor module ---------------------------------------------- - -.. automodule:: ads.model.extractor.pytorch_extractor - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.model - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.model.runtime.rst b/docs/source/ads.model.runtime.rst deleted file mode 100644 index 1d26f4c2c..000000000 --- a/docs/source/ads.model.runtime.rst +++ /dev/null @@ -1,53 +0,0 @@ -ads.model.runtime package -========================= - -Submodules ----------- - -ads.model.runtime.env\_info module ----------------------------------- - -.. automodule:: ads.model.runtime.env_info - :members: - :undoc-members: - :show-inheritance: - -ads.model.runtime.model\_deployment\_details module ---------------------------------------------------- - -.. automodule:: ads.model.runtime.model_deployment_details - :members: - :undoc-members: - :show-inheritance: - -ads.model.runtime.model\_provenance\_details module ---------------------------------------------------- - -.. automodule:: ads.model.runtime.model_provenance_details - :members: - :undoc-members: - :show-inheritance: - -ads.model.runtime.runtime\_info module --------------------------------------- - -.. automodule:: ads.model.runtime.runtime_info - :members: - :undoc-members: - :show-inheritance: - -ads.model.runtime.utils module ------------------------------- - -.. automodule:: ads.model.runtime.utils - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.model.runtime - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.model_deployment.rst b/docs/source/ads.model_deployment.rst deleted file mode 100644 index cb14e1d63..000000000 --- a/docs/source/ads.model_deployment.rst +++ /dev/null @@ -1,38 +0,0 @@ -ads.model.deployment package -============================ - -Submodules ----------- - -ads.model.deployment.model\_deployer module -------------------------------------------- - -.. automodule:: ads.model.deployment.model_deployer - :members: - :undoc-members: - :show-inheritance: - -ads.model.deployment.model\_deployment module ---------------------------------------------- - -.. automodule:: ads.model.deployment.model_deployment - :members: - :undoc-members: - :show-inheritance: - -ads.model.deployment.model\_deployment\_properties module ---------------------------------------------------------- - -.. automodule:: ads.model.deployment.model_deployment_properties - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.model.deployment - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/source/ads.model_framework.rst b/docs/source/ads.model_framework.rst deleted file mode 100644 index bb1923ab4..000000000 --- a/docs/source/ads.model_framework.rst +++ /dev/null @@ -1,61 +0,0 @@ -ads.model.framework package -================================ - -Submodules ----------- - -ads.model.framework.automl\_model module ----------------------------------------- - -.. automodule:: ads.model.framework.automl_model - :members: - :undoc-members: - :show-inheritance: - -ads.model.framework.lightgbm\_model module ------------------------------------------- - -.. automodule:: ads.model.framework.lightgbm_model - :members: - :undoc-members: - :show-inheritance: - -ads.model.framework.pytorch\_model module ------------------------------------------ - -.. automodule:: ads.model.framework.pytorch_model - :members: - :undoc-members: - :show-inheritance: - -ads.model.framework.sklearn\_model module ------------------------------------------ - -.. automodule:: ads.model.framework.sklearn_model - :members: - :undoc-members: - :show-inheritance: - -ads.model.framework.tensorflow\_model module --------------------------------------------- - -.. automodule:: ads.model.framework.tensorflow_model - :members: - :undoc-members: - :show-inheritance: - -ads.model.framework.xgboost\_model module ------------------------------------------ - -.. automodule:: ads.model.framework.xgboost_model - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.model.framework - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.oracledb.rst b/docs/source/ads.oracledb.rst deleted file mode 100644 index 9d1bd521c..000000000 --- a/docs/source/ads.oracledb.rst +++ /dev/null @@ -1,13 +0,0 @@ -ads.oracledb package -================================ - -Submodules ----------- - -ads.oracledb.oracle\_db module ------------------------------------------- - -.. automodule:: ads.oracledb.oracle_db - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/docs/source/ads.rst b/docs/source/ads.rst deleted file mode 100644 index 38af463d2..000000000 --- a/docs/source/ads.rst +++ /dev/null @@ -1,50 +0,0 @@ -ads package -=========== - -Subpackages ------------ - -.. toctree:: - - ads.automl - ads.catalog - ads.common - ads.bds - ads.data_labeling - ads.database - ads.dataflow - ads.dataset - ads.evaluations - ads.explanations - ads.feature_engineering - ads.hpo - ads.jobs - ads.model - ads.model_deployment - ads.model_framework - ads.model.runtime - ads.oracledb - ads.secrets - ads.text_dataset - ads.vault - - -Submodules ----------- - -ads.config module ------------------ - -.. automodule:: ads.config - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.secrets.rst b/docs/source/ads.secrets.rst deleted file mode 100644 index 5d9f9bb2e..000000000 --- a/docs/source/ads.secrets.rst +++ /dev/null @@ -1,62 +0,0 @@ -ads.secrets package -=================== - -Submodules ----------- - -ads.secrets.secrets module --------------------------- - -.. automodule:: ads.secrets.secrets - :members: - :undoc-members: - :show-inheritance: - -ads.secrets.adb module ----------------------- - -.. automodule:: ads.secrets.adb - :members: - :undoc-members: - :show-inheritance: - -ads.secrets.mysqldb module --------------------------- - -.. automodule:: ads.secrets.mysqldb - :members: - :undoc-members: - :show-inheritance: - -ads.secrets.oracledb module ---------------------------- - -.. automodule:: ads.secrets.oracledb - :members: - :undoc-members: - :show-inheritance: - -ads.secrets.big\_data\_service module -------------------------------------- - -.. automodule:: ads.secrets.big_data_service - :members: - :undoc-members: - :show-inheritance: - -ads.secrets.auth\_token module ------------------------------- - -.. automodule:: ads.secrets.auth_token - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.secrets - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/source/ads.text_dataset.rst b/docs/source/ads.text_dataset.rst deleted file mode 100644 index 8ba643ce6..000000000 --- a/docs/source/ads.text_dataset.rst +++ /dev/null @@ -1,46 +0,0 @@ -ads.text\_dataset package -================================ - -Submodules ----------- - -ads.text\_dataset.backends module ------------------------------------------- - -.. automodule:: ads.text_dataset.backends - :members: - :undoc-members: - :show-inheritance: - -ads.text\_dataset.dataset module ------------------------------------------------------- - -.. automodule:: ads.text_dataset.dataset - :members: - :undoc-members: - :show-inheritance: - -ads.text\_dataset.extractor module -------------------------------------------------------------- - -.. automodule:: ads.text_dataset.extractor - :members: - :undoc-members: - :show-inheritance: - -ads.text\_dataset.options module ----------------------------------------------------------- - -.. automodule:: ads.text_dataset.options - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.text_dataset - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/docs/source/ads.vault.rst b/docs/source/ads.vault.rst deleted file mode 100644 index de92820ca..000000000 --- a/docs/source/ads.vault.rst +++ /dev/null @@ -1,21 +0,0 @@ -ads.vault package -================= - -Submodules ----------- - -ads.vault module ----------------- - -.. automodule:: ads.vault.vault - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.vault - :members: - :undoc-members: - :show-inheritance: From 20b2baaaf303c027c2767cd2a572e90142c914de Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Sun, 19 Feb 2023 17:54:44 -0800 Subject: [PATCH 030/147] Manually added rst files to correct for RTD --- docs/source/ads.automl.rst | 29 +++ docs/source/ads.bds.rst | 29 +++ docs/source/ads.catalog.rst | 45 ++++ docs/source/ads.common.artifact.rst | 10 + docs/source/ads.common.decorator.rst | 45 ++++ docs/source/ads.common.function.rst | 21 ++ docs/source/ads.common.rst | 223 ++++++++++++++++++ docs/source/ads.data_labeling.interface.rst | 37 +++ docs/source/ads.data_labeling.loader.rst | 21 ++ docs/source/ads.data_labeling.mixin.rst | 21 ++ docs/source/ads.data_labeling.parser.rst | 37 +++ docs/source/ads.data_labeling.reader.rst | 61 +++++ docs/source/ads.data_labeling.rst | 74 ++++++ docs/source/ads.data_labeling.visualizer.rst | 29 +++ docs/source/ads.database.rst | 21 ++ docs/source/ads.dataflow.rst | 29 +++ docs/source/ads.dataset.rst | 205 ++++++++++++++++ docs/source/ads.dbmixin.rst | 21 ++ docs/source/ads.environment.rst | 21 ++ docs/source/ads.evaluations.rst | 37 +++ docs/source/ads.experiments.rst | 10 + docs/source/ads.explanations.rst | 61 +++++ ...ads.feature_engineering.accessor.mixin.rst | 53 +++++ .../ads.feature_engineering.accessor.rst | 37 +++ ...feature_engineering.adsimage.interface.rst | 21 ++ .../ads.feature_engineering.adsimage.rst | 37 +++ ...ure_engineering.adsstring.oci_language.rst | 10 + ....feature_engineering.adsstring.parsers.rst | 37 +++ .../ads.feature_engineering.adsstring.rst | 47 ++++ ...s.feature_engineering.adsstring.string.rst | 10 + .../ads.feature_engineering.dataset.rst | 21 ++ ...neering.feature_type.adsstring.parsers.rst | 37 +++ ...ure_engineering.feature_type.adsstring.rst | 45 ++++ ...ature_engineering.feature_type.handler.rst | 37 +++ .../ads.feature_engineering.feature_type.rst | 206 ++++++++++++++++ docs/source/ads.feature_engineering.rst | 57 +++++ docs/source/ads.hpo.rst | 85 +++++++ docs/source/ads.hpo.visualization.rst | 10 + .../ads.jobs.builders.infrastructure.rst | 53 +++++ docs/source/ads.jobs.builders.rst | 30 +++ docs/source/ads.jobs.builders.runtimes.rst | 45 ++++ docs/source/ads.jobs.rst | 71 ++++++ docs/source/ads.jobs.schema.rst | 21 ++ docs/source/ads.jobs.templates.rst | 45 ++++ docs/source/ads.model.common.rst | 21 ++ docs/source/ads.model.deployment.common.rst | 29 +++ docs/source/ads.model.deployment.rst | 45 ++++ docs/source/ads.model.extractor.rst | 93 ++++++++ docs/source/ads.model.framework.rst | 69 ++++++ ...oilerplate.artifact_introspection_test.rst | 21 ++ .../ads.model.model_artifact_boilerplate.rst | 29 +++ docs/source/ads.model.rst | 116 +++++++++ docs/source/ads.model.runtime.rst | 53 +++++ docs/source/ads.model.service.rst | 29 +++ docs/source/ads.model.transformer.rst | 21 ++ ...oilerplate.artifact_introspection_test.rst | 21 ++ .../source/ads.model_artifact_boilerplate.rst | 29 +++ docs/source/ads.mysqldb.rst | 21 ++ docs/source/ads.opctl.backend.rst | 53 +++++ docs/source/ads.opctl.conda.rst | 45 ++++ docs/source/ads.opctl.config.diagnostics.rst | 10 + docs/source/ads.opctl.config.rst | 70 ++++++ ....opctl.config.yaml_parsers.distributed.rst | 21 ++ docs/source/ads.opctl.config.yaml_parsers.rst | 29 +++ docs/source/ads.opctl.diagnostics.rst | 37 +++ docs/source/ads.opctl.distributed.common.rst | 61 +++++ docs/source/ads.opctl.distributed.rst | 45 ++++ docs/source/ads.opctl.rst | 59 +++++ docs/source/ads.opctl.spark.rst | 29 +++ docs/source/ads.opctl.spec.rst | 37 +++ docs/source/ads.oracledb.rst | 21 ++ .../ads.pipeline.builders.infrastructure.rst | 21 ++ docs/source/ads.pipeline.builders.rst | 18 ++ docs/source/ads.pipeline.rst | 63 +++++ docs/source/ads.pipeline.schema.rst | 10 + docs/source/ads.pipeline.visualizer.rst | 37 +++ docs/source/ads.rst | 64 +++++ docs/source/ads.secrets.rst | 61 +++++ docs/source/ads.telemetry.rst | 21 ++ docs/source/ads.tests.rst | 29 +++ docs/source/ads.text_dataset.rst | 61 +++++ docs/source/ads.type_discovery.rst | 125 ++++++++++ docs/source/ads.vault.rst | 21 ++ 83 files changed, 3747 insertions(+) create mode 100644 docs/source/ads.automl.rst create mode 100644 docs/source/ads.bds.rst create mode 100644 docs/source/ads.catalog.rst create mode 100644 docs/source/ads.common.artifact.rst create mode 100644 docs/source/ads.common.decorator.rst create mode 100644 docs/source/ads.common.function.rst create mode 100644 docs/source/ads.common.rst create mode 100644 docs/source/ads.data_labeling.interface.rst create mode 100644 docs/source/ads.data_labeling.loader.rst create mode 100644 docs/source/ads.data_labeling.mixin.rst create mode 100644 docs/source/ads.data_labeling.parser.rst create mode 100644 docs/source/ads.data_labeling.reader.rst create mode 100644 docs/source/ads.data_labeling.rst create mode 100644 docs/source/ads.data_labeling.visualizer.rst create mode 100644 docs/source/ads.database.rst create mode 100644 docs/source/ads.dataflow.rst create mode 100644 docs/source/ads.dataset.rst create mode 100644 docs/source/ads.dbmixin.rst create mode 100644 docs/source/ads.environment.rst create mode 100644 docs/source/ads.evaluations.rst create mode 100644 docs/source/ads.experiments.rst create mode 100644 docs/source/ads.explanations.rst create mode 100644 docs/source/ads.feature_engineering.accessor.mixin.rst create mode 100644 docs/source/ads.feature_engineering.accessor.rst create mode 100644 docs/source/ads.feature_engineering.adsimage.interface.rst create mode 100644 docs/source/ads.feature_engineering.adsimage.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.oci_language.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.parsers.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.string.rst create mode 100644 docs/source/ads.feature_engineering.dataset.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.adsstring.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.handler.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.rst create mode 100644 docs/source/ads.feature_engineering.rst create mode 100644 docs/source/ads.hpo.rst create mode 100644 docs/source/ads.hpo.visualization.rst create mode 100644 docs/source/ads.jobs.builders.infrastructure.rst create mode 100644 docs/source/ads.jobs.builders.rst create mode 100644 docs/source/ads.jobs.builders.runtimes.rst create mode 100644 docs/source/ads.jobs.rst create mode 100644 docs/source/ads.jobs.schema.rst create mode 100644 docs/source/ads.jobs.templates.rst create mode 100644 docs/source/ads.model.common.rst create mode 100644 docs/source/ads.model.deployment.common.rst create mode 100644 docs/source/ads.model.deployment.rst create mode 100644 docs/source/ads.model.extractor.rst create mode 100644 docs/source/ads.model.framework.rst create mode 100644 docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst create mode 100644 docs/source/ads.model.model_artifact_boilerplate.rst create mode 100644 docs/source/ads.model.rst create mode 100644 docs/source/ads.model.runtime.rst create mode 100644 docs/source/ads.model.service.rst create mode 100644 docs/source/ads.model.transformer.rst create mode 100644 docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst create mode 100644 docs/source/ads.model_artifact_boilerplate.rst create mode 100644 docs/source/ads.mysqldb.rst create mode 100644 docs/source/ads.opctl.backend.rst create mode 100644 docs/source/ads.opctl.conda.rst create mode 100644 docs/source/ads.opctl.config.diagnostics.rst create mode 100644 docs/source/ads.opctl.config.rst create mode 100644 docs/source/ads.opctl.config.yaml_parsers.distributed.rst create mode 100644 docs/source/ads.opctl.config.yaml_parsers.rst create mode 100644 docs/source/ads.opctl.diagnostics.rst create mode 100644 docs/source/ads.opctl.distributed.common.rst create mode 100644 docs/source/ads.opctl.distributed.rst create mode 100644 docs/source/ads.opctl.rst create mode 100644 docs/source/ads.opctl.spark.rst create mode 100644 docs/source/ads.opctl.spec.rst create mode 100644 docs/source/ads.oracledb.rst create mode 100644 docs/source/ads.pipeline.builders.infrastructure.rst create mode 100644 docs/source/ads.pipeline.builders.rst create mode 100644 docs/source/ads.pipeline.rst create mode 100644 docs/source/ads.pipeline.schema.rst create mode 100644 docs/source/ads.pipeline.visualizer.rst create mode 100644 docs/source/ads.rst create mode 100644 docs/source/ads.secrets.rst create mode 100644 docs/source/ads.telemetry.rst create mode 100644 docs/source/ads.tests.rst create mode 100644 docs/source/ads.text_dataset.rst create mode 100644 docs/source/ads.type_discovery.rst create mode 100644 docs/source/ads.vault.rst diff --git a/docs/source/ads.automl.rst b/docs/source/ads.automl.rst new file mode 100644 index 000000000..3dbd52e62 --- /dev/null +++ b/docs/source/ads.automl.rst @@ -0,0 +1,29 @@ +ads.automl package +================== + +Submodules +---------- + +ads.automl.driver module +------------------------ + +.. automodule:: ads.automl.driver + :members: + :undoc-members: + :show-inheritance: + +ads.automl.provider module +-------------------------- + +.. automodule:: ads.automl.provider + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.automl + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.bds.rst b/docs/source/ads.bds.rst new file mode 100644 index 000000000..d192c047f --- /dev/null +++ b/docs/source/ads.bds.rst @@ -0,0 +1,29 @@ +ads.bds package +=============== + +Submodules +---------- + +ads.bds.auth module +------------------- + +.. automodule:: ads.bds.auth + :members: + :undoc-members: + :show-inheritance: + +ads.bds.big\_data\_service module +--------------------------------- + +.. automodule:: ads.bds.big_data_service + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.bds + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.catalog.rst b/docs/source/ads.catalog.rst new file mode 100644 index 000000000..63e2bbfe3 --- /dev/null +++ b/docs/source/ads.catalog.rst @@ -0,0 +1,45 @@ +ads.catalog package +=================== + +Submodules +---------- + +ads.catalog.model module +------------------------ + +.. automodule:: ads.catalog.model + :members: + :undoc-members: + :show-inheritance: + +ads.catalog.notebook module +--------------------------- + +.. automodule:: ads.catalog.notebook + :members: + :undoc-members: + :show-inheritance: + +ads.catalog.project module +-------------------------- + +.. automodule:: ads.catalog.project + :members: + :undoc-members: + :show-inheritance: + +ads.catalog.summary module +-------------------------- + +.. automodule:: ads.catalog.summary + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.catalog + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.artifact.rst b/docs/source/ads.common.artifact.rst new file mode 100644 index 000000000..6b36311bc --- /dev/null +++ b/docs/source/ads.common.artifact.rst @@ -0,0 +1,10 @@ +ads.common.artifact package +=========================== + +Module contents +--------------- + +.. automodule:: ads.common.artifact + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.decorator.rst b/docs/source/ads.common.decorator.rst new file mode 100644 index 000000000..9912b256d --- /dev/null +++ b/docs/source/ads.common.decorator.rst @@ -0,0 +1,45 @@ +ads.common.decorator package +============================ + +Submodules +---------- + +ads.common.decorator.argument\_to\_case module +---------------------------------------------- + +.. automodule:: ads.common.decorator.argument_to_case + :members: + :undoc-members: + :show-inheritance: + +ads.common.decorator.deprecate module +------------------------------------- + +.. automodule:: ads.common.decorator.deprecate + :members: + :undoc-members: + :show-inheritance: + +ads.common.decorator.runtime\_dependency module +----------------------------------------------- + +.. automodule:: ads.common.decorator.runtime_dependency + :members: + :undoc-members: + :show-inheritance: + +ads.common.decorator.utils module +--------------------------------- + +.. automodule:: ads.common.decorator.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.common.decorator + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.function.rst b/docs/source/ads.common.function.rst new file mode 100644 index 000000000..378eda371 --- /dev/null +++ b/docs/source/ads.common.function.rst @@ -0,0 +1,21 @@ +ads.common.function package +=========================== + +Submodules +---------- + +ads.common.function.fn\_util module +----------------------------------- + +.. automodule:: ads.common.function.fn_util + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.common.function + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.rst b/docs/source/ads.common.rst new file mode 100644 index 000000000..e1ccc8259 --- /dev/null +++ b/docs/source/ads.common.rst @@ -0,0 +1,223 @@ +ads.common package +================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.common.artifact + ads.common.decorator + ads.common.function + +Submodules +---------- + +ads.common.analyzer module +-------------------------- + +.. automodule:: ads.common.analyzer + :members: + :undoc-members: + :show-inheritance: + +ads.common.auth module +---------------------- + +.. automodule:: ads.common.auth + :members: + :undoc-members: + :show-inheritance: + +ads.common.base\_properties module +---------------------------------- + +.. automodule:: ads.common.base_properties + :members: + :undoc-members: + :show-inheritance: + +ads.common.card\_identifier module +---------------------------------- + +.. automodule:: ads.common.card_identifier + :members: + :undoc-members: + :show-inheritance: + +ads.common.config module +------------------------ + +.. automodule:: ads.common.config + :members: + :undoc-members: + :show-inheritance: + +ads.common.data module +---------------------- + +.. automodule:: ads.common.data + :members: + :undoc-members: + :show-inheritance: + +ads.common.data\_serializer module +---------------------------------- + +.. automodule:: ads.common.data_serializer + :members: + :undoc-members: + :show-inheritance: + +ads.common.error module +----------------------- + +.. automodule:: ads.common.error + :members: + :undoc-members: + :show-inheritance: + +ads.common.extended\_enum module +-------------------------------- + +.. automodule:: ads.common.extended_enum + :members: + :undoc-members: + :show-inheritance: + +ads.common.ipython module +------------------------- + +.. automodule:: ads.common.ipython + :members: + :undoc-members: + :show-inheritance: + +ads.common.model module +----------------------- + +.. automodule:: ads.common.model + :members: + :undoc-members: + :show-inheritance: + +ads.common.model\_artifact module +--------------------------------- + +.. automodule:: ads.common.model_artifact + :members: + :undoc-members: + :show-inheritance: + +ads.common.model\_export\_util module +------------------------------------- + +.. automodule:: ads.common.model_export_util + :members: + :undoc-members: + :show-inheritance: + +ads.common.model\_introspect module +----------------------------------- + +.. automodule:: ads.common.model_introspect + :members: + :undoc-members: + :show-inheritance: + +ads.common.model\_metadata module +--------------------------------- + +.. automodule:: ads.common.model_metadata + :members: + :undoc-members: + :show-inheritance: + +ads.common.model\_metadata\_mixin module +---------------------------------------- + +.. automodule:: ads.common.model_metadata_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.common.object\_storage\_details module +------------------------------------------ + +.. automodule:: ads.common.object_storage_details + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_client module +----------------------------- + +.. automodule:: ads.common.oci_client + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_datascience module +---------------------------------- + +.. automodule:: ads.common.oci_datascience + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_logging module +------------------------------ + +.. automodule:: ads.common.oci_logging + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_mixin module +---------------------------- + +.. automodule:: ads.common.oci_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_resource module +------------------------------- + +.. automodule:: ads.common.oci_resource + :members: + :undoc-members: + :show-inheritance: + +ads.common.serializer module +---------------------------- + +.. automodule:: ads.common.serializer + :members: + :undoc-members: + :show-inheritance: + +ads.common.utils module +----------------------- + +.. automodule:: ads.common.utils + :members: + :undoc-members: + :show-inheritance: + +ads.common.word\_lists module +----------------------------- + +.. automodule:: ads.common.word_lists + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.interface.rst b/docs/source/ads.data_labeling.interface.rst new file mode 100644 index 000000000..d49a392c1 --- /dev/null +++ b/docs/source/ads.data_labeling.interface.rst @@ -0,0 +1,37 @@ +ads.data\_labeling.interface package +==================================== + +Submodules +---------- + +ads.data\_labeling.interface.loader module +------------------------------------------ + +.. automodule:: ads.data_labeling.interface.loader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.interface.parser module +------------------------------------------ + +.. automodule:: ads.data_labeling.interface.parser + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.interface.reader module +------------------------------------------ + +.. automodule:: ads.data_labeling.interface.reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.interface + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.loader.rst b/docs/source/ads.data_labeling.loader.rst new file mode 100644 index 000000000..e0193640b --- /dev/null +++ b/docs/source/ads.data_labeling.loader.rst @@ -0,0 +1,21 @@ +ads.data\_labeling.loader package +================================= + +Submodules +---------- + +ads.data\_labeling.loader.file\_loader module +--------------------------------------------- + +.. automodule:: ads.data_labeling.loader.file_loader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.loader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.mixin.rst b/docs/source/ads.data_labeling.mixin.rst new file mode 100644 index 000000000..9509083fa --- /dev/null +++ b/docs/source/ads.data_labeling.mixin.rst @@ -0,0 +1,21 @@ +ads.data\_labeling.mixin package +================================ + +Submodules +---------- + +ads.data\_labeling.mixin.data\_labeling module +---------------------------------------------- + +.. automodule:: ads.data_labeling.mixin.data_labeling + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.mixin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.parser.rst b/docs/source/ads.data_labeling.parser.rst new file mode 100644 index 000000000..df1d192a3 --- /dev/null +++ b/docs/source/ads.data_labeling.parser.rst @@ -0,0 +1,37 @@ +ads.data\_labeling.parser package +================================= + +Submodules +---------- + +ads.data\_labeling.parser.dls\_record\_parser module +---------------------------------------------------- + +.. automodule:: ads.data_labeling.parser.dls_record_parser + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.parser.export\_metadata\_parser module +--------------------------------------------------------- + +.. automodule:: ads.data_labeling.parser.export_metadata_parser + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.parser.export\_record\_parser module +------------------------------------------------------- + +.. automodule:: ads.data_labeling.parser.export_record_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.parser + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.reader.rst b/docs/source/ads.data_labeling.reader.rst new file mode 100644 index 000000000..fd6299b1a --- /dev/null +++ b/docs/source/ads.data_labeling.reader.rst @@ -0,0 +1,61 @@ +ads.data\_labeling.reader package +================================= + +Submodules +---------- + +ads.data\_labeling.reader.dataset\_reader module +------------------------------------------------ + +.. automodule:: ads.data_labeling.reader.dataset_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.dls\_record\_reader module +---------------------------------------------------- + +.. automodule:: ads.data_labeling.reader.dls_record_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.export\_record\_reader module +------------------------------------------------------- + +.. automodule:: ads.data_labeling.reader.export_record_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.jsonl\_reader module +---------------------------------------------- + +.. automodule:: ads.data_labeling.reader.jsonl_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.metadata\_reader module +------------------------------------------------- + +.. automodule:: ads.data_labeling.reader.metadata_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.record\_reader module +----------------------------------------------- + +.. automodule:: ads.data_labeling.reader.record_reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.rst b/docs/source/ads.data_labeling.rst new file mode 100644 index 000000000..7cd6c0763 --- /dev/null +++ b/docs/source/ads.data_labeling.rst @@ -0,0 +1,74 @@ +ads.data\_labeling package +========================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.data_labeling.interface + ads.data_labeling.loader + ads.data_labeling.mixin + ads.data_labeling.parser + ads.data_labeling.reader + ads.data_labeling.visualizer + +Submodules +---------- + +ads.data\_labeling.boundingbox module +------------------------------------- + +.. automodule:: ads.data_labeling.boundingbox + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.constants module +----------------------------------- + +.. automodule:: ads.data_labeling.constants + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.data\_labeling\_service module +------------------------------------------------- + +.. automodule:: ads.data_labeling.data_labeling_service + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.metadata module +---------------------------------- + +.. automodule:: ads.data_labeling.metadata + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.ner module +----------------------------- + +.. automodule:: ads.data_labeling.ner + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.record module +-------------------------------- + +.. automodule:: ads.data_labeling.record + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.visualizer.rst b/docs/source/ads.data_labeling.visualizer.rst new file mode 100644 index 000000000..ddef0f229 --- /dev/null +++ b/docs/source/ads.data_labeling.visualizer.rst @@ -0,0 +1,29 @@ +ads.data\_labeling.visualizer package +===================================== + +Submodules +---------- + +ads.data\_labeling.visualizer.image\_visualizer module +------------------------------------------------------ + +.. automodule:: ads.data_labeling.visualizer.image_visualizer + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.visualizer.text\_visualizer module +----------------------------------------------------- + +.. automodule:: ads.data_labeling.visualizer.text_visualizer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.visualizer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.database.rst b/docs/source/ads.database.rst new file mode 100644 index 000000000..5d15010a2 --- /dev/null +++ b/docs/source/ads.database.rst @@ -0,0 +1,21 @@ +ads.database package +==================== + +Submodules +---------- + +ads.database.connection module +------------------------------ + +.. automodule:: ads.database.connection + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.database + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.dataflow.rst b/docs/source/ads.dataflow.rst new file mode 100644 index 000000000..e4e9e68e5 --- /dev/null +++ b/docs/source/ads.dataflow.rst @@ -0,0 +1,29 @@ +ads.dataflow package +==================== + +Submodules +---------- + +ads.dataflow.dataflow module +---------------------------- + +.. automodule:: ads.dataflow.dataflow + :members: + :undoc-members: + :show-inheritance: + +ads.dataflow.dataflowsummary module +----------------------------------- + +.. automodule:: ads.dataflow.dataflowsummary + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.dataflow + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.dataset.rst b/docs/source/ads.dataset.rst new file mode 100644 index 000000000..b40c62861 --- /dev/null +++ b/docs/source/ads.dataset.rst @@ -0,0 +1,205 @@ +ads.dataset package +=================== + +Submodules +---------- + +ads.dataset.classification\_dataset module +------------------------------------------ + +.. automodule:: ads.dataset.classification_dataset + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.correlation module +------------------------------ + +.. automodule:: ads.dataset.correlation + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.correlation\_plot module +------------------------------------ + +.. automodule:: ads.dataset.correlation_plot + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.dask\_series module +------------------------------- + +.. automodule:: ads.dataset.dask_series + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.dataframe\_transformer module +----------------------------------------- + +.. automodule:: ads.dataset.dataframe_transformer + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.dataset module +-------------------------- + +.. automodule:: ads.dataset.dataset + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.dataset\_browser module +----------------------------------- + +.. automodule:: ads.dataset.dataset_browser + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.dataset\_with\_target module +---------------------------------------- + +.. automodule:: ads.dataset.dataset_with_target + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.exception module +---------------------------- + +.. automodule:: ads.dataset.exception + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.factory module +-------------------------- + +.. automodule:: ads.dataset.factory + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.feature\_engineering\_transformer module +---------------------------------------------------- + +.. automodule:: ads.dataset.feature_engineering_transformer + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.feature\_selection module +------------------------------------- + +.. automodule:: ads.dataset.feature_selection + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.forecasting\_dataset module +--------------------------------------- + +.. automodule:: ads.dataset.forecasting_dataset + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.helper module +------------------------- + +.. automodule:: ads.dataset.helper + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.label\_encoder module +--------------------------------- + +.. automodule:: ads.dataset.label_encoder + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.pipeline module +--------------------------- + +.. automodule:: ads.dataset.pipeline + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.plot module +----------------------- + +.. automodule:: ads.dataset.plot + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.progress module +--------------------------- + +.. automodule:: ads.dataset.progress + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.recommendation module +--------------------------------- + +.. automodule:: ads.dataset.recommendation + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.recommendation\_transformer module +---------------------------------------------- + +.. automodule:: ads.dataset.recommendation_transformer + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.regression\_dataset module +-------------------------------------- + +.. automodule:: ads.dataset.regression_dataset + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.sampled\_dataset module +----------------------------------- + +.. automodule:: ads.dataset.sampled_dataset + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.target module +------------------------- + +.. automodule:: ads.dataset.target + :members: + :undoc-members: + :show-inheritance: + +ads.dataset.timeseries module +----------------------------- + +.. automodule:: ads.dataset.timeseries + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.dataset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.dbmixin.rst b/docs/source/ads.dbmixin.rst new file mode 100644 index 000000000..3d825bcd4 --- /dev/null +++ b/docs/source/ads.dbmixin.rst @@ -0,0 +1,21 @@ +ads.dbmixin package +=================== + +Submodules +---------- + +ads.dbmixin.db\_pandas\_accessor module +--------------------------------------- + +.. automodule:: ads.dbmixin.db_pandas_accessor + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.dbmixin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.environment.rst b/docs/source/ads.environment.rst new file mode 100644 index 000000000..24b6fbd61 --- /dev/null +++ b/docs/source/ads.environment.rst @@ -0,0 +1,21 @@ +ads.environment package +======================= + +Submodules +---------- + +ads.environment.ml\_runtime module +---------------------------------- + +.. automodule:: ads.environment.ml_runtime + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.environment + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.evaluations.rst b/docs/source/ads.evaluations.rst new file mode 100644 index 000000000..681e494fd --- /dev/null +++ b/docs/source/ads.evaluations.rst @@ -0,0 +1,37 @@ +ads.evaluations package +======================= + +Submodules +---------- + +ads.evaluations.evaluation\_plot module +--------------------------------------- + +.. automodule:: ads.evaluations.evaluation_plot + :members: + :undoc-members: + :show-inheritance: + +ads.evaluations.evaluator module +-------------------------------- + +.. automodule:: ads.evaluations.evaluator + :members: + :undoc-members: + :show-inheritance: + +ads.evaluations.statistical\_metrics module +------------------------------------------- + +.. automodule:: ads.evaluations.statistical_metrics + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.evaluations + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.experiments.rst b/docs/source/ads.experiments.rst new file mode 100644 index 000000000..877db54a7 --- /dev/null +++ b/docs/source/ads.experiments.rst @@ -0,0 +1,10 @@ +ads.experiments package +======================= + +Module contents +--------------- + +.. automodule:: ads.experiments + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.explanations.rst b/docs/source/ads.explanations.rst new file mode 100644 index 000000000..f8b6770a0 --- /dev/null +++ b/docs/source/ads.explanations.rst @@ -0,0 +1,61 @@ +ads.explanations package +======================== + +Submodules +---------- + +ads.explanations.base\_explainer module +--------------------------------------- + +.. automodule:: ads.explanations.base_explainer + :members: + :undoc-members: + :show-inheritance: + +ads.explanations.explainer module +--------------------------------- + +.. automodule:: ads.explanations.explainer + :members: + :undoc-members: + :show-inheritance: + +ads.explanations.mlx\_global\_explainer module +---------------------------------------------- + +.. automodule:: ads.explanations.mlx_global_explainer + :members: + :undoc-members: + :show-inheritance: + +ads.explanations.mlx\_interface module +-------------------------------------- + +.. automodule:: ads.explanations.mlx_interface + :members: + :undoc-members: + :show-inheritance: + +ads.explanations.mlx\_local\_explainer module +--------------------------------------------- + +.. automodule:: ads.explanations.mlx_local_explainer + :members: + :undoc-members: + :show-inheritance: + +ads.explanations.mlx\_whatif\_explainer module +---------------------------------------------- + +.. automodule:: ads.explanations.mlx_whatif_explainer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.explanations + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.accessor.mixin.rst b/docs/source/ads.feature_engineering.accessor.mixin.rst new file mode 100644 index 000000000..4d742cf87 --- /dev/null +++ b/docs/source/ads.feature_engineering.accessor.mixin.rst @@ -0,0 +1,53 @@ +ads.feature\_engineering.accessor.mixin package +=============================================== + +Submodules +---------- + +ads.feature\_engineering.accessor.mixin.correlation module +---------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.correlation + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.eda\_mixin module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.eda\_mixin\_series module +----------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin_series + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.feature\_types\_mixin module +-------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.feature_types_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.utils module +---------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.accessor.mixin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.accessor.rst b/docs/source/ads.feature_engineering.accessor.rst new file mode 100644 index 000000000..430d7b3c0 --- /dev/null +++ b/docs/source/ads.feature_engineering.accessor.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.accessor package +========================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.accessor.mixin + +Submodules +---------- + +ads.feature\_engineering.accessor.dataframe\_accessor module +------------------------------------------------------------ + +.. automodule:: ads.feature_engineering.accessor.dataframe_accessor + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.series\_accessor module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.series_accessor + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.accessor + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsimage.interface.rst b/docs/source/ads.feature_engineering.adsimage.interface.rst new file mode 100644 index 000000000..768792d89 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsimage.interface.rst @@ -0,0 +1,21 @@ +ads.feature\_engineering.adsimage.interface package +=================================================== + +Submodules +---------- + +ads.feature\_engineering.adsimage.interface.reader module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsimage.interface.reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsimage.interface + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsimage.rst b/docs/source/ads.feature_engineering.adsimage.rst new file mode 100644 index 000000000..18b4b31c5 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsimage.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.adsimage package +========================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.adsimage.interface + +Submodules +---------- + +ads.feature\_engineering.adsimage.image module +---------------------------------------------- + +.. automodule:: ads.feature_engineering.adsimage.image + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsimage.image\_reader module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.adsimage.image_reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsimage + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.oci_language.rst b/docs/source/ads.feature_engineering.adsstring.oci_language.rst new file mode 100644 index 000000000..9f01d73d0 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.oci_language.rst @@ -0,0 +1,10 @@ +ads.feature\_engineering.adsstring.oci\_language package +======================================================== + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring.oci_language + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.parsers.rst b/docs/source/ads.feature_engineering.adsstring.parsers.rst new file mode 100644 index 000000000..e36481d03 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.parsers.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.adsstring.parsers package +================================================== + +Submodules +---------- + +ads.feature\_engineering.adsstring.parsers.base module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.adsstring.parsers.base + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.parsers.nltk\_parser module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.parsers.nltk_parser + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.parsers.spacy\_parser module +--------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.parsers.spacy_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring.parsers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.rst b/docs/source/ads.feature_engineering.adsstring.rst new file mode 100644 index 000000000..e7b369d13 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.rst @@ -0,0 +1,47 @@ +ads.feature\_engineering.adsstring package +========================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.adsstring.oci_language + ads.feature_engineering.adsstring.parsers + ads.feature_engineering.adsstring.string + +Submodules +---------- + +ads.feature\_engineering.adsstring.common\_regex\_mixin module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.common_regex_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.oci\_language module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.oci_language + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.string module +------------------------------------------------ + +.. automodule:: ads.feature_engineering.adsstring.string + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.string.rst b/docs/source/ads.feature_engineering.adsstring.string.rst new file mode 100644 index 000000000..33881bd27 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.string.rst @@ -0,0 +1,10 @@ +ads.feature\_engineering.adsstring.string package +================================================= + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring.string + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.dataset.rst b/docs/source/ads.feature_engineering.dataset.rst new file mode 100644 index 000000000..762272758 --- /dev/null +++ b/docs/source/ads.feature_engineering.dataset.rst @@ -0,0 +1,21 @@ +ads.feature\_engineering.dataset package +======================================== + +Submodules +---------- + +ads.feature\_engineering.dataset.zip\_code\_data module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.dataset.zip_code_data + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.dataset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst b/docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst new file mode 100644 index 000000000..6b88006f9 --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.feature\_type.adsstring.parsers package +================================================================ + +Submodules +---------- + +ads.feature\_engineering.feature\_type.adsstring.parsers.base module +-------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers.base + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.parsers.nltk\_parser module +---------------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers.nltk_parser + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.parsers.spacy\_parser module +----------------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers.spacy_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.adsstring.rst b/docs/source/ads.feature_engineering.feature_type.adsstring.rst new file mode 100644 index 000000000..2b979db3a --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.adsstring.rst @@ -0,0 +1,45 @@ +ads.feature\_engineering.feature\_type.adsstring package +======================================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.feature_type.adsstring.parsers + +Submodules +---------- + +ads.feature\_engineering.feature\_type.adsstring.common\_regex\_mixin module +---------------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.common_regex_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.oci\_language module +--------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.oci_language + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.string module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.string + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.handler.rst b/docs/source/ads.feature_engineering.feature_type.handler.rst new file mode 100644 index 000000000..bb258d4e0 --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.handler.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.feature\_type.handler package +====================================================== + +Submodules +---------- + +ads.feature\_engineering.feature\_type.handler.feature\_validator module +------------------------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.handler.feature_validator + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.handler.feature\_warning module +---------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.handler.feature_warning + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.handler.warnings module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.handler.warnings + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type.handler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.rst b/docs/source/ads.feature_engineering.feature_type.rst new file mode 100644 index 000000000..5e8ea8afe --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.rst @@ -0,0 +1,206 @@ +ads.feature\_engineering.feature\_type package +============================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.feature_type.adsstring + ads.feature_engineering.feature_type.handler + +Submodules +---------- + +ads.feature\_engineering.feature\_type.address module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.address + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.base module +-------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.base + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.boolean module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.boolean + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.category module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.category + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.constant module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.constant + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.continuous module +-------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.continuous + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.creditcard module +-------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.creditcard + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.datetime module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.datetime + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.discrete module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.discrete + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.document module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.document + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.gis module +------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.gis + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.integer module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.integer + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ip\_address module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ip_address + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ip\_address\_v4 module +------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ip_address_v4 + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ip\_address\_v6 module +------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ip_address_v6 + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.lat\_long module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.lat_long + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.object module +---------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.object + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ordinal module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ordinal + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.phone\_number module +----------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.phone_number + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.string module +---------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.string + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.text module +-------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.text + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.unknown module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.unknown + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.zip\_code module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.zip_code + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.rst b/docs/source/ads.feature_engineering.rst new file mode 100644 index 000000000..4f81cd6b3 --- /dev/null +++ b/docs/source/ads.feature_engineering.rst @@ -0,0 +1,57 @@ +ads.feature\_engineering package +================================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.accessor + ads.feature_engineering.adsimage + ads.feature_engineering.adsstring + ads.feature_engineering.dataset + ads.feature_engineering.feature_type + +Submodules +---------- + +ads.feature\_engineering.exceptions module +------------------------------------------ + +.. automodule:: ads.feature_engineering.exceptions + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type\_manager module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type_manager + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.schema module +-------------------------------------- + +.. automodule:: ads.feature_engineering.schema + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.utils module +------------------------------------- + +.. automodule:: ads.feature_engineering.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.hpo.rst b/docs/source/ads.hpo.rst new file mode 100644 index 000000000..313d93d66 --- /dev/null +++ b/docs/source/ads.hpo.rst @@ -0,0 +1,85 @@ +ads.hpo package +=============== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.hpo.visualization + +Submodules +---------- + +ads.hpo.ads\_search\_space module +--------------------------------- + +.. automodule:: ads.hpo.ads_search_space + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.distributions module +---------------------------- + +.. automodule:: ads.hpo.distributions + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.objective module +------------------------ + +.. automodule:: ads.hpo.objective + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.search\_cv module +------------------------- + +.. automodule:: ads.hpo.search_cv + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.stopping\_criterion module +---------------------------------- + +.. automodule:: ads.hpo.stopping_criterion + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.tuner\_artifact module +------------------------------ + +.. automodule:: ads.hpo.tuner_artifact + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.utils module +-------------------- + +.. automodule:: ads.hpo.utils + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.validation module +------------------------- + +.. automodule:: ads.hpo.validation + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.hpo + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.hpo.visualization.rst b/docs/source/ads.hpo.visualization.rst new file mode 100644 index 000000000..37426494f --- /dev/null +++ b/docs/source/ads.hpo.visualization.rst @@ -0,0 +1,10 @@ +ads.hpo.visualization package +============================= + +Module contents +--------------- + +.. automodule:: ads.hpo.visualization + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.builders.infrastructure.rst b/docs/source/ads.jobs.builders.infrastructure.rst new file mode 100644 index 000000000..614615674 --- /dev/null +++ b/docs/source/ads.jobs.builders.infrastructure.rst @@ -0,0 +1,53 @@ +ads.jobs.builders.infrastructure package +======================================== + +Submodules +---------- + +ads.jobs.builders.infrastructure.base module +-------------------------------------------- + +.. automodule:: ads.jobs.builders.infrastructure.base + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.dataflow module +------------------------------------------------ + +.. automodule:: ads.jobs.builders.infrastructure.dataflow + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.dsc\_job module +------------------------------------------------ + +.. automodule:: ads.jobs.builders.infrastructure.dsc_job + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.dsc\_job\_runtime module +--------------------------------------------------------- + +.. automodule:: ads.jobs.builders.infrastructure.dsc_job_runtime + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.utils module +--------------------------------------------- + +.. automodule:: ads.jobs.builders.infrastructure.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.builders.infrastructure + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.builders.rst b/docs/source/ads.jobs.builders.rst new file mode 100644 index 000000000..0ead1cae1 --- /dev/null +++ b/docs/source/ads.jobs.builders.rst @@ -0,0 +1,30 @@ +ads.jobs.builders package +========================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.jobs.builders.infrastructure + ads.jobs.builders.runtimes + +Submodules +---------- + +ads.jobs.builders.base module +----------------------------- + +.. automodule:: ads.jobs.builders.base + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.builders + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.builders.runtimes.rst b/docs/source/ads.jobs.builders.runtimes.rst new file mode 100644 index 000000000..c7d5ebd63 --- /dev/null +++ b/docs/source/ads.jobs.builders.runtimes.rst @@ -0,0 +1,45 @@ +ads.jobs.builders.runtimes package +================================== + +Submodules +---------- + +ads.jobs.builders.runtimes.artifact module +------------------------------------------ + +.. automodule:: ads.jobs.builders.runtimes.artifact + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.runtimes.base module +-------------------------------------- + +.. automodule:: ads.jobs.builders.runtimes.base + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.runtimes.container\_runtime module +---------------------------------------------------- + +.. automodule:: ads.jobs.builders.runtimes.container_runtime + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.runtimes.python\_runtime module +------------------------------------------------- + +.. automodule:: ads.jobs.builders.runtimes.python_runtime + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.builders.runtimes + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.rst b/docs/source/ads.jobs.rst new file mode 100644 index 000000000..2e7daa34e --- /dev/null +++ b/docs/source/ads.jobs.rst @@ -0,0 +1,71 @@ +ads.jobs package +================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.jobs.builders + ads.jobs.schema + ads.jobs.templates + +Submodules +---------- + +ads.jobs.ads\_job module +------------------------ + +.. automodule:: ads.jobs.ads_job + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.cli module +------------------- + +.. automodule:: ads.jobs.cli + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.env\_var\_parser module +-------------------------------- + +.. automodule:: ads.jobs.env_var_parser + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.extension module +------------------------- + +.. automodule:: ads.jobs.extension + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.serializer module +-------------------------- + +.. automodule:: ads.jobs.serializer + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.utils module +--------------------- + +.. automodule:: ads.jobs.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.schema.rst b/docs/source/ads.jobs.schema.rst new file mode 100644 index 000000000..0856ee19e --- /dev/null +++ b/docs/source/ads.jobs.schema.rst @@ -0,0 +1,21 @@ +ads.jobs.schema package +======================= + +Submodules +---------- + +ads.jobs.schema.validator module +-------------------------------- + +.. automodule:: ads.jobs.schema.validator + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.schema + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.templates.rst b/docs/source/ads.jobs.templates.rst new file mode 100644 index 000000000..ee6634c29 --- /dev/null +++ b/docs/source/ads.jobs.templates.rst @@ -0,0 +1,45 @@ +ads.jobs.templates package +========================== + +Submodules +---------- + +ads.jobs.templates.container module +----------------------------------- + +.. automodule:: ads.jobs.templates.container + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.templates.driver\_notebook module +------------------------------------------ + +.. automodule:: ads.jobs.templates.driver_notebook + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.templates.driver\_oci module +------------------------------------- + +.. automodule:: ads.jobs.templates.driver_oci + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.templates.driver\_python module +---------------------------------------- + +.. automodule:: ads.jobs.templates.driver_python + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.templates + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.common.rst b/docs/source/ads.model.common.rst new file mode 100644 index 000000000..0e8a811a6 --- /dev/null +++ b/docs/source/ads.model.common.rst @@ -0,0 +1,21 @@ +ads.model.common package +======================== + +Submodules +---------- + +ads.model.common.utils module +----------------------------- + +.. automodule:: ads.model.common.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.deployment.common.rst b/docs/source/ads.model.deployment.common.rst new file mode 100644 index 000000000..2fde6ebbc --- /dev/null +++ b/docs/source/ads.model.deployment.common.rst @@ -0,0 +1,29 @@ +ads.model.deployment.common package +=================================== + +Submodules +---------- + +ads.model.deployment.common.progress\_bar module +------------------------------------------------ + +.. automodule:: ads.model.deployment.common.progress_bar + :members: + :undoc-members: + :show-inheritance: + +ads.model.deployment.common.utils module +---------------------------------------- + +.. automodule:: ads.model.deployment.common.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.deployment.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.deployment.rst b/docs/source/ads.model.deployment.rst new file mode 100644 index 000000000..c15d28dd7 --- /dev/null +++ b/docs/source/ads.model.deployment.rst @@ -0,0 +1,45 @@ +ads.model.deployment package +============================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model.deployment.common + +Submodules +---------- + +ads.model.deployment.model\_deployer module +------------------------------------------- + +.. automodule:: ads.model.deployment.model_deployer + :members: + :undoc-members: + :show-inheritance: + +ads.model.deployment.model\_deployment module +--------------------------------------------- + +.. automodule:: ads.model.deployment.model_deployment + :members: + :undoc-members: + :show-inheritance: + +ads.model.deployment.model\_deployment\_properties module +--------------------------------------------------------- + +.. automodule:: ads.model.deployment.model_deployment_properties + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.deployment + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.extractor.rst b/docs/source/ads.model.extractor.rst new file mode 100644 index 000000000..e21802b46 --- /dev/null +++ b/docs/source/ads.model.extractor.rst @@ -0,0 +1,93 @@ +ads.model.extractor package +=========================== + +Submodules +---------- + +ads.model.extractor.automl\_extractor module +-------------------------------------------- + +.. automodule:: ads.model.extractor.automl_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.keras\_extractor module +------------------------------------------- + +.. automodule:: ads.model.extractor.keras_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.lightgbm\_extractor module +---------------------------------------------- + +.. automodule:: ads.model.extractor.lightgbm_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.model\_info\_extractor module +------------------------------------------------- + +.. automodule:: ads.model.extractor.model_info_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.model\_info\_extractor\_factory module +---------------------------------------------------------- + +.. automodule:: ads.model.extractor.model_info_extractor_factory + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.pytorch\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.pytorch_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.sklearn\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.sklearn_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.spark\_extractor module +------------------------------------------- + +.. automodule:: ads.model.extractor.spark_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.tensorflow\_extractor module +------------------------------------------------ + +.. automodule:: ads.model.extractor.tensorflow_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.xgboost\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.xgboost_extractor + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.extractor + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.framework.rst b/docs/source/ads.model.framework.rst new file mode 100644 index 000000000..80b6f9a99 --- /dev/null +++ b/docs/source/ads.model.framework.rst @@ -0,0 +1,69 @@ +ads.model.framework package +=========================== + +Submodules +---------- + +ads.model.framework.automl\_model module +---------------------------------------- + +.. automodule:: ads.model.framework.automl_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.framework.lightgbm\_model module +------------------------------------------ + +.. automodule:: ads.model.framework.lightgbm_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.framework.pytorch\_model module +----------------------------------------- + +.. automodule:: ads.model.framework.pytorch_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.framework.sklearn\_model module +----------------------------------------- + +.. automodule:: ads.model.framework.sklearn_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.framework.spark\_model module +--------------------------------------- + +.. automodule:: ads.model.framework.spark_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.framework.tensorflow\_model module +-------------------------------------------- + +.. automodule:: ads.model.framework.tensorflow_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.framework.xgboost\_model module +----------------------------------------- + +.. automodule:: ads.model.framework.xgboost_model + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.framework + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst b/docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst new file mode 100644 index 000000000..d5ca14e8e --- /dev/null +++ b/docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst @@ -0,0 +1,21 @@ +ads.model.model\_artifact\_boilerplate.artifact\_introspection\_test package +============================================================================ + +Submodules +---------- + +ads.model.model\_artifact\_boilerplate.artifact\_introspection\_test.model\_artifact\_validate module +----------------------------------------------------------------------------------------------------- + +.. automodule:: ads.model.model_artifact_boilerplate.artifact_introspection_test.model_artifact_validate + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.model_artifact_boilerplate.artifact_introspection_test + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.model_artifact_boilerplate.rst b/docs/source/ads.model.model_artifact_boilerplate.rst new file mode 100644 index 000000000..d4f626fb8 --- /dev/null +++ b/docs/source/ads.model.model_artifact_boilerplate.rst @@ -0,0 +1,29 @@ +ads.model.model\_artifact\_boilerplate package +============================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model.model_artifact_boilerplate.artifact_introspection_test + +Submodules +---------- + +ads.model.model\_artifact\_boilerplate.score module +--------------------------------------------------- + +.. automodule:: ads.model.model_artifact_boilerplate.score + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.model_artifact_boilerplate + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.rst b/docs/source/ads.model.rst new file mode 100644 index 000000000..8a34769bb --- /dev/null +++ b/docs/source/ads.model.rst @@ -0,0 +1,116 @@ +ads.model package +================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model.common + ads.model.deployment + ads.model.extractor + ads.model.framework + ads.model.model_artifact_boilerplate + ads.model.runtime + ads.model.service + ads.model.transformer + +Submodules +---------- + +ads.model.artifact module +------------------------- + +.. automodule:: ads.model.artifact + :members: + :undoc-members: + :show-inheritance: + +ads.model.artifact\_downloader module +------------------------------------- + +.. automodule:: ads.model.artifact_downloader + :members: + :undoc-members: + :show-inheritance: + +ads.model.artifact\_uploader module +----------------------------------- + +.. automodule:: ads.model.artifact_uploader + :members: + :undoc-members: + :show-inheritance: + +ads.model.base\_properties module +--------------------------------- + +.. automodule:: ads.model.base_properties + :members: + :undoc-members: + :show-inheritance: + +ads.model.datascience\_model module +----------------------------------- + +.. automodule:: ads.model.datascience_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.generic\_model module +------------------------------- + +.. automodule:: ads.model.generic_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_introspect module +---------------------------------- + +.. automodule:: ads.model.model_introspect + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_metadata module +-------------------------------- + +.. automodule:: ads.model.model_metadata + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_metadata\_mixin module +--------------------------------------- + +.. automodule:: ads.model.model_metadata_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_properties module +---------------------------------- + +.. automodule:: ads.model.model_properties + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_version\_set module +------------------------------------ + +.. automodule:: ads.model.model_version_set + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.runtime.rst b/docs/source/ads.model.runtime.rst new file mode 100644 index 000000000..1d26f4c2c --- /dev/null +++ b/docs/source/ads.model.runtime.rst @@ -0,0 +1,53 @@ +ads.model.runtime package +========================= + +Submodules +---------- + +ads.model.runtime.env\_info module +---------------------------------- + +.. automodule:: ads.model.runtime.env_info + :members: + :undoc-members: + :show-inheritance: + +ads.model.runtime.model\_deployment\_details module +--------------------------------------------------- + +.. automodule:: ads.model.runtime.model_deployment_details + :members: + :undoc-members: + :show-inheritance: + +ads.model.runtime.model\_provenance\_details module +--------------------------------------------------- + +.. automodule:: ads.model.runtime.model_provenance_details + :members: + :undoc-members: + :show-inheritance: + +ads.model.runtime.runtime\_info module +-------------------------------------- + +.. automodule:: ads.model.runtime.runtime_info + :members: + :undoc-members: + :show-inheritance: + +ads.model.runtime.utils module +------------------------------ + +.. automodule:: ads.model.runtime.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.runtime + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.service.rst b/docs/source/ads.model.service.rst new file mode 100644 index 000000000..caa9173c7 --- /dev/null +++ b/docs/source/ads.model.service.rst @@ -0,0 +1,29 @@ +ads.model.service package +========================= + +Submodules +---------- + +ads.model.service.oci\_datascience\_model module +------------------------------------------------ + +.. automodule:: ads.model.service.oci_datascience_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.service.oci\_datascience\_model\_version\_set module +-------------------------------------------------------------- + +.. automodule:: ads.model.service.oci_datascience_model_version_set + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.transformer.rst b/docs/source/ads.model.transformer.rst new file mode 100644 index 000000000..be953afec --- /dev/null +++ b/docs/source/ads.model.transformer.rst @@ -0,0 +1,21 @@ +ads.model.transformer package +============================= + +Submodules +---------- + +ads.model.transformer.onnx\_transformer module +---------------------------------------------- + +.. automodule:: ads.model.transformer.onnx_transformer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.transformer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst b/docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst new file mode 100644 index 000000000..cddd1fe75 --- /dev/null +++ b/docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst @@ -0,0 +1,21 @@ +ads.model\_artifact\_boilerplate.artifact\_introspection\_test package +====================================================================== + +Submodules +---------- + +ads.model\_artifact\_boilerplate.artifact\_introspection\_test.model\_artifact\_validate module +----------------------------------------------------------------------------------------------- + +.. automodule:: ads.model_artifact_boilerplate.artifact_introspection_test.model_artifact_validate + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model_artifact_boilerplate.artifact_introspection_test + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model_artifact_boilerplate.rst b/docs/source/ads.model_artifact_boilerplate.rst new file mode 100644 index 000000000..825fc42c9 --- /dev/null +++ b/docs/source/ads.model_artifact_boilerplate.rst @@ -0,0 +1,29 @@ +ads.model\_artifact\_boilerplate package +======================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model_artifact_boilerplate.artifact_introspection_test + +Submodules +---------- + +ads.model\_artifact\_boilerplate.score module +--------------------------------------------- + +.. automodule:: ads.model_artifact_boilerplate.score + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model_artifact_boilerplate + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.mysqldb.rst b/docs/source/ads.mysqldb.rst new file mode 100644 index 000000000..1092b6cb7 --- /dev/null +++ b/docs/source/ads.mysqldb.rst @@ -0,0 +1,21 @@ +ads.mysqldb package +=================== + +Submodules +---------- + +ads.mysqldb.mysql\_db module +---------------------------- + +.. automodule:: ads.mysqldb.mysql_db + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.mysqldb + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.backend.rst b/docs/source/ads.opctl.backend.rst new file mode 100644 index 000000000..df19c4a45 --- /dev/null +++ b/docs/source/ads.opctl.backend.rst @@ -0,0 +1,53 @@ +ads.opctl.backend package +========================= + +Submodules +---------- + +ads.opctl.backend.ads\_dataflow module +-------------------------------------- + +.. automodule:: ads.opctl.backend.ads_dataflow + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.ads\_ml\_job module +------------------------------------- + +.. automodule:: ads.opctl.backend.ads_ml_job + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.ads\_ml\_pipeline module +------------------------------------------ + +.. automodule:: ads.opctl.backend.ads_ml_pipeline + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.base module +----------------------------- + +.. automodule:: ads.opctl.backend.base + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.local module +------------------------------ + +.. automodule:: ads.opctl.backend.local + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.backend + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.conda.rst b/docs/source/ads.opctl.conda.rst new file mode 100644 index 000000000..716491f61 --- /dev/null +++ b/docs/source/ads.opctl.conda.rst @@ -0,0 +1,45 @@ +ads.opctl.conda package +======================= + +Submodules +---------- + +ads.opctl.conda.cli module +-------------------------- + +.. automodule:: ads.opctl.conda.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.conda.cmds module +--------------------------- + +.. automodule:: ads.opctl.conda.cmds + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.conda.multipart\_uploader module +------------------------------------------ + +.. automodule:: ads.opctl.conda.multipart_uploader + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.conda.pack module +--------------------------- + +.. automodule:: ads.opctl.conda.pack + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.conda + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.diagnostics.rst b/docs/source/ads.opctl.config.diagnostics.rst new file mode 100644 index 000000000..5f7b8b8be --- /dev/null +++ b/docs/source/ads.opctl.config.diagnostics.rst @@ -0,0 +1,10 @@ +ads.opctl.config.diagnostics package +==================================== + +Module contents +--------------- + +.. automodule:: ads.opctl.config.diagnostics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.rst b/docs/source/ads.opctl.config.rst new file mode 100644 index 000000000..bc58c70ce --- /dev/null +++ b/docs/source/ads.opctl.config.rst @@ -0,0 +1,70 @@ +ads.opctl.config package +======================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.config.diagnostics + ads.opctl.config.yaml_parsers + +Submodules +---------- + +ads.opctl.config.base module +---------------------------- + +.. automodule:: ads.opctl.config.base + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.merger module +------------------------------ + +.. automodule:: ads.opctl.config.merger + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.resolver module +-------------------------------- + +.. automodule:: ads.opctl.config.resolver + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.utils module +----------------------------- + +.. automodule:: ads.opctl.config.utils + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.validator module +--------------------------------- + +.. automodule:: ads.opctl.config.validator + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.versioner module +--------------------------------- + +.. automodule:: ads.opctl.config.versioner + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.config + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.yaml_parsers.distributed.rst b/docs/source/ads.opctl.config.yaml_parsers.distributed.rst new file mode 100644 index 000000000..a4ad70daf --- /dev/null +++ b/docs/source/ads.opctl.config.yaml_parsers.distributed.rst @@ -0,0 +1,21 @@ +ads.opctl.config.yaml\_parsers.distributed package +================================================== + +Submodules +---------- + +ads.opctl.config.yaml\_parsers.distributed.yaml\_parser module +-------------------------------------------------------------- + +.. automodule:: ads.opctl.config.yaml_parsers.distributed.yaml_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.config.yaml_parsers.distributed + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.yaml_parsers.rst b/docs/source/ads.opctl.config.yaml_parsers.rst new file mode 100644 index 000000000..5709e9851 --- /dev/null +++ b/docs/source/ads.opctl.config.yaml_parsers.rst @@ -0,0 +1,29 @@ +ads.opctl.config.yaml\_parsers package +====================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.config.yaml_parsers.distributed + +Submodules +---------- + +ads.opctl.config.yaml\_parsers.base module +------------------------------------------ + +.. automodule:: ads.opctl.config.yaml_parsers.base + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.config.yaml_parsers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.diagnostics.rst b/docs/source/ads.opctl.diagnostics.rst new file mode 100644 index 000000000..1cb8b85ce --- /dev/null +++ b/docs/source/ads.opctl.diagnostics.rst @@ -0,0 +1,37 @@ +ads.opctl.diagnostics package +============================= + +Submodules +---------- + +ads.opctl.diagnostics.check\_distributed\_job\_requirements module +------------------------------------------------------------------ + +.. automodule:: ads.opctl.diagnostics.check_distributed_job_requirements + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.diagnostics.check\_requirements module +------------------------------------------------ + +.. automodule:: ads.opctl.diagnostics.check_requirements + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.diagnostics.requirement\_exception module +--------------------------------------------------- + +.. automodule:: ads.opctl.diagnostics.requirement_exception + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.diagnostics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.distributed.common.rst b/docs/source/ads.opctl.distributed.common.rst new file mode 100644 index 000000000..ca95e15f3 --- /dev/null +++ b/docs/source/ads.opctl.distributed.common.rst @@ -0,0 +1,61 @@ +ads.opctl.distributed.common package +==================================== + +Submodules +---------- + +ads.opctl.distributed.common.abstract\_cluster\_provider module +--------------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.abstract_cluster_provider + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.abstract\_framework\_spec\_builder module +---------------------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.abstract_framework_spec_builder + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.cluster\_config\_helper module +----------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.cluster_config_helper + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.cluster\_provider\_factory module +-------------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.cluster_provider_factory + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.cluster\_runner module +--------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.cluster_runner + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.framework\_factory module +------------------------------------------------------ + +.. automodule:: ads.opctl.distributed.common.framework_factory + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.distributed.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.distributed.rst b/docs/source/ads.opctl.distributed.rst new file mode 100644 index 000000000..ef3d73b10 --- /dev/null +++ b/docs/source/ads.opctl.distributed.rst @@ -0,0 +1,45 @@ +ads.opctl.distributed package +============================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.distributed.common + +Submodules +---------- + +ads.opctl.distributed.certificates module +----------------------------------------- + +.. automodule:: ads.opctl.distributed.certificates + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.cli module +-------------------------------- + +.. automodule:: ads.opctl.distributed.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.cmds module +--------------------------------- + +.. automodule:: ads.opctl.distributed.cmds + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.distributed + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.rst b/docs/source/ads.opctl.rst new file mode 100644 index 000000000..d659af771 --- /dev/null +++ b/docs/source/ads.opctl.rst @@ -0,0 +1,59 @@ +ads.opctl package +================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.backend + ads.opctl.conda + ads.opctl.config + ads.opctl.diagnostics + ads.opctl.distributed + ads.opctl.spark + ads.opctl.spec + +Submodules +---------- + +ads.opctl.cli module +-------------------- + +.. automodule:: ads.opctl.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.cmds module +--------------------- + +.. automodule:: ads.opctl.cmds + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.constants module +-------------------------- + +.. automodule:: ads.opctl.constants + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.utils module +---------------------- + +.. automodule:: ads.opctl.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.spark.rst b/docs/source/ads.opctl.spark.rst new file mode 100644 index 000000000..d02fb1216 --- /dev/null +++ b/docs/source/ads.opctl.spark.rst @@ -0,0 +1,29 @@ +ads.opctl.spark package +======================= + +Submodules +---------- + +ads.opctl.spark.cli module +-------------------------- + +.. automodule:: ads.opctl.spark.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.spark.cmds module +--------------------------- + +.. automodule:: ads.opctl.spark.cmds + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.spark + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.spec.rst b/docs/source/ads.opctl.spec.rst new file mode 100644 index 000000000..71d7fda3b --- /dev/null +++ b/docs/source/ads.opctl.spec.rst @@ -0,0 +1,37 @@ +ads.opctl.spec package +====================== + +Submodules +---------- + +ads.opctl.spec.abstract\_operator\_spec module +---------------------------------------------- + +.. automodule:: ads.opctl.spec.abstract_operator_spec + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.spec.operator\_spec\_factory module +--------------------------------------------- + +.. automodule:: ads.opctl.spec.operator_spec_factory + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.spec.utils module +--------------------------- + +.. automodule:: ads.opctl.spec.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.spec + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.oracledb.rst b/docs/source/ads.oracledb.rst new file mode 100644 index 000000000..6227b5d28 --- /dev/null +++ b/docs/source/ads.oracledb.rst @@ -0,0 +1,21 @@ +ads.oracledb package +==================== + +Submodules +---------- + +ads.oracledb.oracle\_db module +------------------------------ + +.. automodule:: ads.oracledb.oracle_db + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.oracledb + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.builders.infrastructure.rst b/docs/source/ads.pipeline.builders.infrastructure.rst new file mode 100644 index 000000000..1359b3e8c --- /dev/null +++ b/docs/source/ads.pipeline.builders.infrastructure.rst @@ -0,0 +1,21 @@ +ads.pipeline.builders.infrastructure package +============================================ + +Submodules +---------- + +ads.pipeline.builders.infrastructure.custom\_script module +---------------------------------------------------------- + +.. automodule:: ads.pipeline.builders.infrastructure.custom_script + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.pipeline.builders.infrastructure + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.builders.rst b/docs/source/ads.pipeline.builders.rst new file mode 100644 index 000000000..c2886f393 --- /dev/null +++ b/docs/source/ads.pipeline.builders.rst @@ -0,0 +1,18 @@ +ads.pipeline.builders package +============================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.pipeline.builders.infrastructure + +Module contents +--------------- + +.. automodule:: ads.pipeline.builders + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.rst b/docs/source/ads.pipeline.rst new file mode 100644 index 000000000..a1f539b15 --- /dev/null +++ b/docs/source/ads.pipeline.rst @@ -0,0 +1,63 @@ +ads.pipeline package +==================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.pipeline.builders + ads.pipeline.schema + ads.pipeline.visualizer + +Submodules +---------- + +ads.pipeline.ads\_pipeline module +--------------------------------- + +.. automodule:: ads.pipeline.ads_pipeline + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.ads\_pipeline\_run module +-------------------------------------- + +.. automodule:: ads.pipeline.ads_pipeline_run + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.ads\_pipeline\_step module +--------------------------------------- + +.. automodule:: ads.pipeline.ads_pipeline_step + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.cli module +----------------------- + +.. automodule:: ads.pipeline.cli + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.extension module +----------------------------- + +.. automodule:: ads.pipeline.extension + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.pipeline + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.schema.rst b/docs/source/ads.pipeline.schema.rst new file mode 100644 index 000000000..52f18f458 --- /dev/null +++ b/docs/source/ads.pipeline.schema.rst @@ -0,0 +1,10 @@ +ads.pipeline.schema package +=========================== + +Module contents +--------------- + +.. automodule:: ads.pipeline.schema + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.visualizer.rst b/docs/source/ads.pipeline.visualizer.rst new file mode 100644 index 000000000..a1aea7807 --- /dev/null +++ b/docs/source/ads.pipeline.visualizer.rst @@ -0,0 +1,37 @@ +ads.pipeline.visualizer package +=============================== + +Submodules +---------- + +ads.pipeline.visualizer.base module +----------------------------------- + +.. automodule:: ads.pipeline.visualizer.base + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.visualizer.graph\_renderer module +---------------------------------------------- + +.. automodule:: ads.pipeline.visualizer.graph_renderer + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.visualizer.text\_renderer module +--------------------------------------------- + +.. automodule:: ads.pipeline.visualizer.text_renderer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.pipeline.visualizer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.rst b/docs/source/ads.rst new file mode 100644 index 000000000..753260e3d --- /dev/null +++ b/docs/source/ads.rst @@ -0,0 +1,64 @@ +ads package +=========== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.automl + ads.bds + ads.catalog + ads.common + ads.data_labeling + ads.database + ads.dataflow + ads.dataset + ads.dbmixin + ads.environment + ads.evaluations + ads.experiments + ads.explanations + ads.feature_engineering + ads.hpo + ads.jobs + ads.model + ads.model_artifact_boilerplate + ads.mysqldb + ads.opctl + ads.oracledb + ads.pipeline + ads.secrets + ads.telemetry + ads.tests + ads.text_dataset + ads.type_discovery + ads.vault + +Submodules +---------- + +ads.cli module +-------------- + +.. automodule:: ads.cli + :members: + :undoc-members: + :show-inheritance: + +ads.config module +----------------- + +.. automodule:: ads.config + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.secrets.rst b/docs/source/ads.secrets.rst new file mode 100644 index 000000000..45e860093 --- /dev/null +++ b/docs/source/ads.secrets.rst @@ -0,0 +1,61 @@ +ads.secrets package +=================== + +Submodules +---------- + +ads.secrets.adb module +---------------------- + +.. automodule:: ads.secrets.adb + :members: + :undoc-members: + :show-inheritance: + +ads.secrets.auth\_token module +------------------------------ + +.. automodule:: ads.secrets.auth_token + :members: + :undoc-members: + :show-inheritance: + +ads.secrets.big\_data\_service module +------------------------------------- + +.. automodule:: ads.secrets.big_data_service + :members: + :undoc-members: + :show-inheritance: + +ads.secrets.mysqldb module +-------------------------- + +.. automodule:: ads.secrets.mysqldb + :members: + :undoc-members: + :show-inheritance: + +ads.secrets.oracledb module +--------------------------- + +.. automodule:: ads.secrets.oracledb + :members: + :undoc-members: + :show-inheritance: + +ads.secrets.secrets module +-------------------------- + +.. automodule:: ads.secrets.secrets + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.secrets + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.telemetry.rst b/docs/source/ads.telemetry.rst new file mode 100644 index 000000000..f775d1b5d --- /dev/null +++ b/docs/source/ads.telemetry.rst @@ -0,0 +1,21 @@ +ads.telemetry package +===================== + +Submodules +---------- + +ads.telemetry.telemetry module +------------------------------ + +.. automodule:: ads.telemetry.telemetry + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.telemetry + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.tests.rst b/docs/source/ads.tests.rst new file mode 100644 index 000000000..61bec4b34 --- /dev/null +++ b/docs/source/ads.tests.rst @@ -0,0 +1,29 @@ +ads.tests package +================= + +Submodules +---------- + +ads.tests.test\_notebooks module +-------------------------------- + +.. automodule:: ads.tests.test_notebooks + :members: + :undoc-members: + :show-inheritance: + +ads.tests.utils module +---------------------- + +.. automodule:: ads.tests.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.text_dataset.rst b/docs/source/ads.text_dataset.rst new file mode 100644 index 000000000..2db1e9f1c --- /dev/null +++ b/docs/source/ads.text_dataset.rst @@ -0,0 +1,61 @@ +ads.text\_dataset package +========================= + +Submodules +---------- + +ads.text\_dataset.backends module +--------------------------------- + +.. automodule:: ads.text_dataset.backends + :members: + :undoc-members: + :show-inheritance: + +ads.text\_dataset.dataset module +-------------------------------- + +.. automodule:: ads.text_dataset.dataset + :members: + :undoc-members: + :show-inheritance: + +ads.text\_dataset.extractor module +---------------------------------- + +.. automodule:: ads.text_dataset.extractor + :members: + :undoc-members: + :show-inheritance: + +ads.text\_dataset.options module +-------------------------------- + +.. automodule:: ads.text_dataset.options + :members: + :undoc-members: + :show-inheritance: + +ads.text\_dataset.udfs module +----------------------------- + +.. automodule:: ads.text_dataset.udfs + :members: + :undoc-members: + :show-inheritance: + +ads.text\_dataset.utils module +------------------------------ + +.. automodule:: ads.text_dataset.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.text_dataset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.type_discovery.rst b/docs/source/ads.type_discovery.rst new file mode 100644 index 000000000..5a04610f5 --- /dev/null +++ b/docs/source/ads.type_discovery.rst @@ -0,0 +1,125 @@ +ads.type\_discovery package +=========================== + +Submodules +---------- + +ads.type\_discovery.abstract\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.abstract_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.constant\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.constant_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.continuous\_detector module +----------------------------------------------- + +.. automodule:: ads.type_discovery.continuous_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.credit\_card\_detector module +------------------------------------------------- + +.. automodule:: ads.type_discovery.credit_card_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.datetime\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.datetime_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.discrete\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.discrete_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.document\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.document_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.ip\_detector module +--------------------------------------- + +.. automodule:: ads.type_discovery.ip_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.latlon\_detector module +------------------------------------------- + +.. automodule:: ads.type_discovery.latlon_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.phone\_number\_detector module +-------------------------------------------------- + +.. automodule:: ads.type_discovery.phone_number_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.type\_discovery\_driver module +-------------------------------------------------- + +.. automodule:: ads.type_discovery.type_discovery_driver + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.typed\_feature module +----------------------------------------- + +.. automodule:: ads.type_discovery.typed_feature + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.unknown\_detector module +-------------------------------------------- + +.. automodule:: ads.type_discovery.unknown_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.zipcode\_detector module +-------------------------------------------- + +.. automodule:: ads.type_discovery.zipcode_detector + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.type_discovery + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.vault.rst b/docs/source/ads.vault.rst new file mode 100644 index 000000000..632cfc3fd --- /dev/null +++ b/docs/source/ads.vault.rst @@ -0,0 +1,21 @@ +ads.vault package +================= + +Submodules +---------- + +ads.vault.vault module +---------------------- + +.. automodule:: ads.vault.vault + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.vault + :members: + :undoc-members: + :show-inheritance: From ba09fb6e9f1c24bbdb5a1da8cb57730de5c6ae5a Mon Sep 17 00:00:00 2001 From: Oleksandra Pavlusieva Date: Mon, 20 Feb 2023 13:09:22 +0200 Subject: [PATCH 031/147] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05c0d1fc5..ca21db925 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ This example uses SQL injection safe binding variables. ## Contributing -This project welcomes contributions from the community. Before submitting a pull request, please review our contribution guide [CONTRIBUTING.md](https://github.com/oracle/accelerated-data-science/blob/main/CONTRIBUTING.md). +This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md) Find Getting Started instructions for developers in [README-development.md](https://github.com/oracle/accelerated-data-science/blob/main/README-development.md) From 02ba29baf5043b83ab9c56d47f755f880030a602 Mon Sep 17 00:00:00 2001 From: Oleksandra Pavlusieva Date: Mon, 20 Feb 2023 13:10:32 +0200 Subject: [PATCH 032/147] Update SECURITY.md --- SECURITY.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index ceb50544b..fb2384138 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,11 +6,10 @@ and privacy of all our users. Please do NOT raise a GitHub Issue to report a security vulnerability. If you believe you have found a security vulnerability, please submit a report to -[secalert_us@oracle.com](mailto:secalert_us@oracle.com) preferably with a proof of concept. -Please review some additional information on -[how to report security vulnerabilities to Oracle](https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html). +[secalert_us@oracle.com][1] preferably with a proof of concept. Please review +some additional information on [how to report security vulnerabilities to Oracle][2]. We encourage people who contact Oracle Security to use email encryption using -[our encryption key](https://www.oracle.com/security-alerts/encryptionkey.html). +[our encryption key][3]. We ask that you do not use other channels or contact the project maintainers directly. @@ -22,11 +21,9 @@ security features are welcome on GitHub Issues. Security updates will be released on a regular cadence. Many of our projects will typically release security fixes in conjunction with the -[Oracle Critical Patch Update](https://www.oracle.com/security-alerts/encryptionkey.html) program. -Security updates are released on the Tuesday closest to the 17th day of January, April, July and October. -A pre-release announcement will be published on the Thursday preceding each release. Additional -information, including past advisories, is available on our -[security alerts](https://www.oracle.com/security-alerts/) page. +[Oracle Critical Patch Update][3] program. Additional +information, including past advisories, is available on our [security alerts][4] +page. ## Security-related information @@ -34,3 +31,8 @@ We will provide security related information such as a threat model, considerati for secure use, or any known security issues in our documentation. Please note that labs and sample code are intended to demonstrate a concept and may not be sufficiently hardened for production use. + +[1]: mailto:secalert_us@oracle.com +[2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html +[3]: https://www.oracle.com/security-alerts/encryptionkey.html +[4]: https://www.oracle.com/security-alerts/ From e1358e5bbd434640e22c41d76ac1f1c409f156ec Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 20 Feb 2023 11:41:29 -0500 Subject: [PATCH 033/147] Update index.rst and training_with_oci.rst --- docs/source/user_guide/jobs/index.rst | 33 ++----------------- .../model_training/training_with_oci.rst | 8 ++++- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/docs/source/user_guide/jobs/index.rst b/docs/source/user_guide/jobs/index.rst index eb853cfc3..6f905c9ce 100644 --- a/docs/source/user_guide/jobs/index.rst +++ b/docs/source/user_guide/jobs/index.rst @@ -1,38 +1,9 @@ .. _jobs-1: ################# -Training with OCI +Data Science Jobs ################# -Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `_ -enables you to define and run repeatable machine learning tasks on a fully managed infrastructure. -You can have Compute resource on demand and run applications that perform tasks such as -data preparation, model training, hyperparameter tuning, and batch inference. - -Training with OCI involves two types resources: **Job** and **Job Run**. - -A **Job** is a template that describes the training task. -It contains configurations about the *infrastructure*, such as -`Compute Shape `_, -`Block Storage `_, -`Logging `_, -and information about the *runtime*, -such as the source code of your training workload, environment variables, and CLI arguments. - -A **Job Run** is an instantiation of a job. -In each job run, you can override some of the job configurations, such as environment variables and CLI arguments. -You can use the same job as a template and launch multiple simultaneous job runs to parallelize a large task. -You can also sequence jobs and keep the state by writing state information to -`Object Storage `_ - -For example, you may want to experiment with how different model classes perform on the same training data -by using the ADSTuner to perform hyperparameter tuning on each model class. -You could do this in parallel by having a different job run for each class of models. -For a given job run, you could pass an environment variable that identifies the model class that you want to use. -Each model cab write its results to the Logging service or Object Storage. -Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. - -The following sections provides details on running training workloads with OCI Data Science Jobs using ADS Jobs APIs. -You can use similar APIs to `Run a OCI DataFlow Application `_. +.. include:: ../jobs/overview.rst .. include:: ../jobs/toc.rst diff --git a/docs/source/user_guide/model_training/training_with_oci.rst b/docs/source/user_guide/model_training/training_with_oci.rst index f9e99ed58..7ddc05f0d 100644 --- a/docs/source/user_guide/model_training/training_with_oci.rst +++ b/docs/source/user_guide/model_training/training_with_oci.rst @@ -1 +1,7 @@ -.. include:: ../jobs/index.rst +################# +Training with OCI +################# + +.. include:: ../jobs/overview.rst + +.. include:: ../jobs/toc.rst From 66f0b652cb84d525cd4e50f7cce746bcb703782e Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 20 Feb 2023 11:41:50 -0500 Subject: [PATCH 034/147] Update CLI jobs.rst --- docs/source/user_guide/cli/opctl/_template/jobs.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/cli/opctl/_template/jobs.rst b/docs/source/user_guide/cli/opctl/_template/jobs.rst index 91218854b..457008bba 100644 --- a/docs/source/user_guide/cli/opctl/_template/jobs.rst +++ b/docs/source/user_guide/cli/opctl/_template/jobs.rst @@ -1,6 +1,6 @@ -++++++++++++++++++++++++++++++++++++++++++++ -Working with OCI Data Science Jobs Using CLI -++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++ +Working with CLI +++++++++++++++++ Prerequisite ------------ From fc4be7eeb4364c9514b4c8bd206487fb265e851a Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 20 Feb 2023 11:42:10 -0500 Subject: [PATCH 035/147] Update overview.rst --- docs/source/user_guide/jobs/overview.rst | 79 +++++++++--------------- 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/docs/source/user_guide/jobs/overview.rst b/docs/source/user_guide/jobs/overview.rst index 327659960..0800e67dc 100644 --- a/docs/source/user_guide/jobs/overview.rst +++ b/docs/source/user_guide/jobs/overview.rst @@ -1,49 +1,30 @@ -Overview -******** - -Data Science jobs allow you to run customized tasks outside of a notebook session. You can have Compute on demand and only pay for the Compute that you need. With jobs, you can run applications that perform tasks such as data preparation, model training, hyperparameter tuning, and batch inference. When the task is complete the compute automatically terminates. You can use the Logging service to capture output messages. - -Using jobs, you can: - -* Run machine learning (ML) or data science tasks outside of your JupyterLab notebook session. -* Operationalize discrete data science and machine learning tasks, such as reusable runnable operations. -* Automate your MLOps or CI/CD pipeline. -* Run batch or workloads triggered by events or actions. -* Batch, mini batch, or distributed batch job inference. -* In a JupyterLab notebook session, you can launch long running tasks or computation intensive tasks in a Data Science job to keep your notebook free for you to continue your work. - -Typically, an ML and data science project is a series of steps including: - -* Access -* Explore -* Prepare -* Model -* Train -* Validate -* Deploy -* Test - -.. image:: figures/ml_steps.png - :alt: Machine Learning Project Steps - -After the steps are completed, you can automate the process of data exploration, model training, deploying, and testing using jobs. A single change in the data preparation or model training to experiment with hyperparameter tunings can be run as a job and independently tested. - -Data Science jobs consist of two types of resources: job and job run. - -Job -=== - -A job is a template that describes the task. It contains elements like the job artifact, which is immutable. It can’t be modified after being registered as a Data Science job. A job contains information about the Compute shape, logging configuration, Block Storage, and other options. You can configure environment variables can be configured that are used at run-time by the job run. You can also pass in CLI arguments. This allows a job run to be customized while using the same job as a template. You can override the environment variable and CLI parameters in job runs. Only the job artifact is immutable though the settings can be changed. - -Job Run -======= - -A job run is an instantiation of a job. In each job run, you can override some of the job configuration. The most common configurations to change are the environment variables and CLI arguments. You can use the same job as a template and launch multiple simultaneous job runs to parallelize a large task. You can also sequence jobs and keep the state by writing state information to Object Storage. - -For example, you could experiment with how different model classes perform on the same training data by using the ADSTuner to perform hyperparameter tuning on each model class. You could do this in parallel by having a different job run for each class of models. For a given job run, you could pass an environment variable that identifies the model class that you want to use. Each model cab write its results to the Logging service or Object Storage. Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. - -ADS Jobs -======== - -ADS jobs API calls separate the job configurations into infrastructure and runtime. Infrastructure specifies the configurations of the OCI resources and service for running the job. Runtime specifies the source code and the software environments for running the job. These two types of infrastructure are supported: `Data Science job `__ and `Data Flow `__. - +Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `_ +enables you to define and run repeatable machine learning tasks on a fully managed infrastructure. +You can have Compute resource on demand and run applications that perform tasks such as +data preparation, model training, hyperparameter tuning, and batch inference. + +Training with OCI involves two types resources: **Job** and **Job Run**. + +A **Job** is a template that describes the training task. +It contains configurations about the *infrastructure*, such as +`Compute Shape `_, +`Block Storage `_, +`Logging `_, +and information about the *runtime*, +such as the source code of your training workload, environment variables, and CLI arguments. + +A **Job Run** is an instantiation of a job. +In each job run, you can override some of the job configurations, such as environment variables and CLI arguments. +You can use the same job as a template and launch multiple simultaneous job runs to parallelize a large task. +You can also sequence jobs and keep the state by writing state information to +`Object Storage `_ + +For example, you may want to experiment with how different model classes perform on the same training data +by using the ADSTuner to perform hyperparameter tuning on each model class. +You could do this in parallel by having a different job run for each class of models. +For a given job run, you could pass an environment variable that identifies the model class that you want to use. +Each model cab write its results to the Logging service or Object Storage. +Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. + +The following sections provides details on running training workloads with OCI Data Science Jobs using ADS Jobs APIs. +You can use similar APIs to `Run a OCI DataFlow Application `_. From 79f05aa13764c0d9d377b0534acc4cf64cff34dd Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 20 Feb 2023 11:42:22 -0500 Subject: [PATCH 036/147] Update toc.rst --- docs/source/user_guide/jobs/toc.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/jobs/toc.rst b/docs/source/user_guide/jobs/toc.rst index 400e9f64f..9b65b4b19 100644 --- a/docs/source/user_guide/jobs/toc.rst +++ b/docs/source/user_guide/jobs/toc.rst @@ -3,10 +3,11 @@ ../jobs/data_science_job ../jobs/policies - ../jobs/run_container - ../jobs/run_git + ../jobs/infrastructure + ../jobs/runtime ../jobs/run_notebook - ../jobs/run_script + ../jobs/run_git + ../jobs/run_container ../jobs/run_zip ../cli/opctl/_template/jobs ../cli/opctl/_template/monitoring From a96859263c73c39f3beb76403ce7a72b42877784 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 20 Feb 2023 11:42:33 -0500 Subject: [PATCH 037/147] Update data_science_job.rst --- .../user_guide/jobs/data_science_job.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 5b0a59da1..121aa1758 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -6,7 +6,7 @@ Quick Start Before creating a job, ensure that you have policies configured for Data Science resources. See also: :doc:`policies` and `About Data Science Policies `_. -In ADS, a job is defined by **infrastructure** and **runtime**. +In ADS, a job is defined by :doc:`infrastructure` and :doc:`runtime`. The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance. The runtime can be an instance of :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, :py:class:`~ads.jobs.builders.runtimes.python_runtime.GitPythonRuntime`, @@ -14,10 +14,10 @@ The runtime can be an instance of :py:class:`~ads.jobs.builders.runtimes.python_ :py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime`. -Running a Python Job +Create and Run a Job ==================== -Here is an example to define and run a Python job: +Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: .. tabs:: @@ -108,6 +108,9 @@ Here is an example to define and run a Python job: scriptPathURI: local/path/to/code_dir workingDir: code_dir +In :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, +the ``entrypoint`` can be a Python script, a Python function or a Jupyter notebook. +For more details, see :doc:`infrastructure` configurations and :doc:`runtime` configurations. YAML ==== @@ -136,3 +139,12 @@ Here are some examples to load/save the YAML job configurations: The ``uri`` can be a local file path or a remote location supported by `fsspec `_, including OCI object storage. + +With the YAML file, you can create and run the job with ADS CLI: + +.. code-block:: bash + + ads opctl run -f your_job.yaml + +For more details on ``ads opctl``, see :doc:`../cli/opctl/_template/jobs`. + From fc81a55aa507dd511286b7cc23e10da2e9bd6bfc Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 20 Feb 2023 11:42:40 -0500 Subject: [PATCH 038/147] Update policies.rst --- docs/source/user_guide/jobs/policies.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/jobs/policies.rst b/docs/source/user_guide/jobs/policies.rst index b5c388177..54f935583 100644 --- a/docs/source/user_guide/jobs/policies.rst +++ b/docs/source/user_guide/jobs/policies.rst @@ -1,5 +1,9 @@ -Data Science Jobs Policies -************************** +I AM Policies +************* + +Oracle Cloud Infrastructure Identity and Access Management (IAM) +lets you specify policies to control the access to your cloud resources. +This section contain the policies recommended for Data Science Jobs. .. admonition:: Policy subject From 7667c1902f896f786f40c2fdbba48825fa9c7099 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:14:56 -0500 Subject: [PATCH 039/147] Update data_science_job.rst --- .../user_guide/jobs/data_science_job.rst | 88 +++++++++++++++++-- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 121aa1758..c54e1a0ab 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -10,8 +10,9 @@ In ADS, a job is defined by :doc:`infrastructure` and :doc:`runtime`. The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance. The runtime can be an instance of :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, :py:class:`~ads.jobs.builders.runtimes.python_runtime.GitPythonRuntime`, -:py:class:`~ads.jobs.builders.runtimes.python_runtime.NotebookRuntime` or -:py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime`. +:py:class:`~ads.jobs.builders.runtimes.python_runtime.NotebookRuntime`, +:py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime`, or +:py:class:`~ads.jobs.builders.runtimes.python_runtime.ContainerRuntime` Create and Run a Job @@ -27,7 +28,7 @@ Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: from ads.jobs import Job, DataScienceJob, PythonRuntime job = ( - Job() + Job(name="My Job") .with_infrastructure( DataScienceJob() .with_log_group_id("") @@ -37,6 +38,7 @@ Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: # The configurations of the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") + # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. @@ -76,14 +78,14 @@ Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: kind: job spec: - name: + name: "My Job" infrastructure: kind: infrastructure type: dataScienceJob spec: blockStorageSize: 50 compartmentId: - jobInfrastructureType: ME_STANDALONE + jobInfrastructureType: STANDALONE jobType: DEFAULT logGroupId: logId: @@ -108,9 +110,30 @@ Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: scriptPathURI: local/path/to/code_dir workingDir: code_dir +For more details, see :doc:`infrastructure` configurations and see :doc:`runtime` configurations. + In :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, the ``entrypoint`` can be a Python script, a Python function or a Jupyter notebook. -For more details, see :doc:`infrastructure` configurations and :doc:`runtime` configurations. + +Once the job is created, the job OCID can be accessed through ``job.id``. +Once the job run is created, the job run OCID can be accessed through ``run.id``. + +The ``watch()`` method is useful to monitor the progress of the job run if logging is configured. +It will stream the logs to terminal and return once the job is finished. +Here is an example of the logs: + +.. code-block:: text + + 2021-10-28 17:17:58 - Job Run ACCEPTED + 2021-10-28 17:18:07 - Job Run ACCEPTED, Infrastructure provisioning. + 2021-10-28 17:19:19 - Job Run ACCEPTED, Infrastructure provisioned. + 2021-10-28 17:20:48 - Job Run ACCEPTED, Job run bootstrap starting. + 2021-10-28 17:23:41 - Job Run ACCEPTED, Job run bootstrap complete. Artifact execution starting. + 2021-10-28 17:23:50 - Job Run IN_PROGRESS, Job run artifact execution in progress. + 2021-10-28 17:23:50 - + 2021-10-28 17:23:50 - + 2021-10-28 17:23:50 - ... + YAML ==== @@ -148,3 +171,56 @@ With the YAML file, you can create and run the job with ADS CLI: For more details on ``ads opctl``, see :doc:`../cli/opctl/_template/jobs`. + +Loading Existing Job or Job Run +=============================== + +You can load an existing job or job run using the OCID from OCI: + +.. code-block:: python + + from ads.jobs import Job, DataScienceJobRun + + # Load a job + job = Job.from_datascience_job("") + + # Load a job run + job_run = DataScienceJobRun.from_ocid(""") + + +List Existing Jobs or Job Runs +============================== + +To get a list of existing jobs in a specific compartment: + +.. code-block:: python + + from ads.jobs import Job + + # Load a job + jobs = Job.datascience_job("") + +With a ``Job`` object, you can get a list of job runs: + +.. code-block:: python + + # Gets a list of job runs for a specific job. + runs = job.run_list() + +Deleting a Job or Job Run +========================= + +You can delete a job or job run by calling the ``delete()`` method. + +.. code-block:: python + + # Delete a job and the corresponding job runs. + job.delete() + # Delete a job run + run.delete() + +You can also cancel a job run: + +.. code-block:: python + + run.cancel() From 65f87731d9a07554d14b5fb53153003e86903bf3 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:15:15 -0500 Subject: [PATCH 040/147] Add runtime.rst --- docs/source/user_guide/jobs/runtime.rst | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/source/user_guide/jobs/runtime.rst diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst new file mode 100644 index 000000000..5d9903f22 --- /dev/null +++ b/docs/source/user_guide/jobs/runtime.rst @@ -0,0 +1,59 @@ +Runtime +******* + +The *runtime* of a job defines the source code of your workload, environment variables, CLI arguments +and other configurations for the environment to run the workload. + +Depending on the source code, ADS provides different types of *runtime* for defining a data science job, +including: + +* :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime` + for Python code stored locally, OCI object storage, or other remote location supported by + `fsspec `_ +* :py:class:`~ads.jobs.builders.runtimes.python_runtime.GitPythonRuntime` + for Python code from a Git repository. +* :py:class:`~ads.jobs.builders.runtimes.python_runtime.NotebookRuntime` + for a single Jupyter notebook stored locally, OCI object storage, or other remote location supported by + `fsspec `_ +* :py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime` + for bash or shell scripts stored locally, OCI object storage, or other remote location supported by + `fsspec `_ +* :py:class:`~ads.jobs.builders.runtimes.python_runtime.ContainerRuntime` for container images. + + +Conda Environment +================= + +Except for :py:class:`~ads.jobs.builders.runtimes.python_runtime.ContainerRuntime`, +all the other runtime options allow you to configure a +`Conda Environment `_ +for your workload. You can use the slug name to specify a +`conda environment provided by the data science service `_. +For example, to use the TensorFlow conda environment: + +.. code-block:: python3 + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + # Use slug name for conda environment provided by data science service + .with_service_conda("tensorflow28_p38_cpu_v1") + ) + +You can also use a custom conda environment published to OCI Object Storage +by passing the ``uri`` to the ``with_custom_conda()`` method, for example: + +.. code-block:: python3 + + runtime = ( + ScriptRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + .with_custom_conda("oci://bucket@namespace/conda_pack/pack_name") + ) + +For more details on custom conda environment, see +`Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy `__. + + From 21eb29bdf460abb37dfcf7d4be1e7f0a06e1b476 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:15:27 -0500 Subject: [PATCH 041/147] Update overview.rst --- docs/source/user_guide/jobs/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/jobs/overview.rst b/docs/source/user_guide/jobs/overview.rst index 0800e67dc..ceacecc1b 100644 --- a/docs/source/user_guide/jobs/overview.rst +++ b/docs/source/user_guide/jobs/overview.rst @@ -11,7 +11,7 @@ It contains configurations about the *infrastructure*, such as `Block Storage `_, `Logging `_, and information about the *runtime*, -such as the source code of your training workload, environment variables, and CLI arguments. +such as the source code of your workload, environment variables, and CLI arguments. A **Job Run** is an instantiation of a job. In each job run, you can override some of the job configurations, such as environment variables and CLI arguments. From c709d67328d905842cdeed0d024d92701fdfcc6d Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:15:37 -0500 Subject: [PATCH 042/147] Add infrastructure.rst --- .../source/user_guide/jobs/infrastructure.rst | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 docs/source/user_guide/jobs/infrastructure.rst diff --git a/docs/source/user_guide/jobs/infrastructure.rst b/docs/source/user_guide/jobs/infrastructure.rst new file mode 100644 index 000000000..d51a8b44b --- /dev/null +++ b/docs/source/user_guide/jobs/infrastructure.rst @@ -0,0 +1,95 @@ +Infrastructure +************** + +The Data Science Job infrastructure is defined by a +:py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance. For example: + + +.. code-block:: python3 + + from ads.jobs import DataScienceJob + + infrastructure = ( + DataScienceJob() + .with_compartment_id("") + .with_project_id("") + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum block storage size is 50 + .with_block_storage_size(50) + .with_log_group_id("") + .with_log_id("") + ) + +When creating a :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance, +the following configurations are required: + +* Compartment ID +* Project ID +* Compute Shape + +The following configurations are optional: +* Block Storage Size, defaults to 50 (GB) +* Log Group ID +* Log ID + +Using Configurations from Notebook +================================== + +If you are creating a job from an OCI Data Science +`Notebook Session `_, +the same infrastructure configurations from the notebook session will be used as defaults. +You can initialize the :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` +with the logging configurations and override the other options as needed. For example: + +.. code-block:: python3 + + from ads.jobs import DataScienceJob + + infrastructure = ( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # Use a GPU shape for the job, + # regardless of the shape used by the notebook session + .with_shape_name("VM.GPU2.1") + # compartment ID, project ID, subnet ID and block storage will be + # the same as the ones set in the notebook session + ) + +Compute Shapes +============== + +You can get a list of currently supported compute shapes by calling ``DataScienceJob.instance_shapes()``. +Additionally, you can get a list of shapes are available for fast launch by calling ``DataScienceJob.fast_launch_shapes()`` +Specifying a fast launch shape will allow your job to start as fast as possible. + +Networking +========== + +Data Science Job offers two types of networking: default networking (managed egress) and custom networking. +Default networking allows job runs to access public internet through a NAT gateway and OCI service through +a service gateway, both are configured automatically. Custom networking requires you to specify a subnet ID. +You can control the network access through the subnet and security lists. + +If you specified a subnet ID, your job will be configured to have custom networking. +Otherwise, default networking will be used. Note that when you are in a Data Science Notebook Session, +the same networking configuration is be used by default. +You can specify the networking manually by calling ``with_job_infrastructure_type()``. + +Logging +======= + +Logging is not required to create the job. +However, it is highly recommended to enable logging for debugging and monitoring purpose. + +In the preceding example, both the log OCID and corresponding log group OCID are specified +with the ``DataScienceJob`` instance. +If your administrator configured the permission for you to search for logging resources, +you can skip specifying the log group OCID because ADS can automatically retrieve it. + +If you specify only the log group OCID and no log OCID, +a new Log resource is automatically created within the log group to store the logs, +see also `ADS Logging <../logging/logging.html>`_. From 61c3dac3c82b0f47756e5b01a3cf3bffb3cb2b29 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:16:10 -0500 Subject: [PATCH 043/147] Rename run_zip.rst to run_python.rst --- docs/source/user_guide/jobs/{run_zip.rst => run_python.rst} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename docs/source/user_guide/jobs/{run_zip.rst => run_python.rst} (99%) diff --git a/docs/source/user_guide/jobs/run_zip.rst b/docs/source/user_guide/jobs/run_python.rst similarity index 99% rename from docs/source/user_guide/jobs/run_zip.rst rename to docs/source/user_guide/jobs/run_python.rst index 836c5df1d..12371eb80 100644 --- a/docs/source/user_guide/jobs/run_zip.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -1,7 +1,7 @@ .. _job_run_zip: -Run Code in ZIP or Folder -************************* +Run a Python Workload +********************* ScriptRuntime ============= From 86fab0d4b6f29a81dac798e9febdcd97a4880651 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:16:42 -0500 Subject: [PATCH 044/147] Update toc.rst --- docs/source/user_guide/jobs/toc.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/jobs/toc.rst b/docs/source/user_guide/jobs/toc.rst index 9b65b4b19..1e6ffbbde 100644 --- a/docs/source/user_guide/jobs/toc.rst +++ b/docs/source/user_guide/jobs/toc.rst @@ -5,10 +5,11 @@ ../jobs/policies ../jobs/infrastructure ../jobs/runtime + ../jobs/run_python ../jobs/run_notebook - ../jobs/run_git + ../jobs/run_script ../jobs/run_container - ../jobs/run_zip + ../jobs/run_git ../cli/opctl/_template/jobs ../cli/opctl/_template/monitoring ../cli/opctl/localdev/local_jobs From 0fd06eae9006abe204468cc190bba8abc81c68e2 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Feb 2023 11:18:20 -0500 Subject: [PATCH 045/147] Update runtime.rst --- docs/source/user_guide/jobs/runtime.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst index 5d9903f22..a2ccbcf26 100644 --- a/docs/source/user_guide/jobs/runtime.rst +++ b/docs/source/user_guide/jobs/runtime.rst @@ -56,4 +56,27 @@ by passing the ``uri`` to the ``with_custom_conda()`` method, for example: For more details on custom conda environment, see `Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy `__. +Environment Variables +===================== + +CLI Arguments +============= + + +Override Configuration +====================== + +When you call ``job.run()``, a new job run will be started with the configuration defined in the **job**. +You may want to override the configuration with custom variables. For example, +you can customize job run display name, override command line argument, specify additional environment variables, +and add free form tags: + +.. code-block:: python3 + + job_run = job.run( + name="", + args="new_arg --new_key new_val", + env_var={"new_env": "new_val"}, + freeform_tags={"new_tag": "new_tag_val"} + ) \ No newline at end of file From da02c4eadc9bf1b8515b05c9bdcfc4dfc54959f5 Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Tue, 21 Feb 2023 09:07:22 -0800 Subject: [PATCH 046/147] Manually built rst files (#92) --- docs/source/ads.automl.rst | 19 +- docs/source/ads.bds.rst | 20 +- docs/source/ads.catalog.rst | 31 +- docs/source/ads.common.artifact.rst | 10 + docs/source/ads.common.decorator.rst | 45 +++ docs/source/ads.common.function.rst | 21 ++ docs/source/ads.common.rst | 233 ++++++++++---- docs/source/ads.data_labeling.interface.rst | 37 +++ docs/source/ads.data_labeling.loader.rst | 21 ++ docs/source/ads.data_labeling.mixin.rst | 21 ++ docs/source/ads.data_labeling.parser.rst | 37 +++ docs/source/ads.data_labeling.reader.rst | 61 ++++ docs/source/ads.data_labeling.rst | 107 +------ docs/source/ads.data_labeling.visualizer.rst | 29 ++ docs/source/ads.database.rst | 15 +- docs/source/ads.dataflow.rst | 20 +- docs/source/ads.dataset.rst | 151 +++++---- docs/source/ads.dbmixin.rst | 21 ++ docs/source/ads.environment.rst | 21 ++ docs/source/ads.evaluations.rst | 25 +- docs/source/ads.experiments.rst | 10 + docs/source/ads.explanations.rst | 42 +-- ...ads.feature_engineering.accessor.mixin.rst | 53 ++++ .../ads.feature_engineering.accessor.rst | 37 +++ ...feature_engineering.adsimage.interface.rst | 21 ++ .../ads.feature_engineering.adsimage.rst | 37 +++ ...ure_engineering.adsstring.oci_language.rst | 10 + ....feature_engineering.adsstring.parsers.rst | 37 +++ .../ads.feature_engineering.adsstring.rst | 47 +++ ...s.feature_engineering.adsstring.string.rst | 10 + .../ads.feature_engineering.dataset.rst | 21 ++ ...neering.feature_type.adsstring.parsers.rst | 37 +++ ...ure_engineering.feature_type.adsstring.rst | 45 +++ ...ature_engineering.feature_type.handler.rst | 37 +++ .../ads.feature_engineering.feature_type.rst | 206 +++++++++++++ docs/source/ads.feature_engineering.rst | 288 ++---------------- docs/source/ads.hpo.rst | 83 +++-- docs/source/ads.hpo.visualization.rst | 10 + .../ads.jobs.builders.infrastructure.rst | 53 ++++ docs/source/ads.jobs.builders.rst | 30 ++ docs/source/ads.jobs.builders.runtimes.rst | 45 +++ docs/source/ads.jobs.rst | 46 ++- docs/source/ads.jobs.schema.rst | 21 ++ docs/source/ads.jobs.templates.rst | 45 +++ docs/source/ads.model.common.rst | 21 ++ docs/source/ads.model.deployment.common.rst | 29 ++ ...eployment.rst => ads.model.deployment.rst} | 33 +- docs/source/ads.model.extractor.rst | 93 ++++++ ..._framework.rst => ads.model.framework.rst} | 10 +- ...oilerplate.artifact_introspection_test.rst | 21 ++ .../ads.model.model_artifact_boilerplate.rst | 29 ++ docs/source/ads.model.rst | 161 +++++----- docs/source/ads.model.service.rst | 29 ++ docs/source/ads.model.transformer.rst | 21 ++ ...oilerplate.artifact_introspection_test.rst | 21 ++ .../source/ads.model_artifact_boilerplate.rst | 29 ++ docs/source/ads.mysqldb.rst | 21 ++ docs/source/ads.opctl.backend.rst | 53 ++++ docs/source/ads.opctl.conda.rst | 45 +++ docs/source/ads.opctl.config.diagnostics.rst | 10 + docs/source/ads.opctl.config.rst | 70 +++++ ....opctl.config.yaml_parsers.distributed.rst | 21 ++ docs/source/ads.opctl.config.yaml_parsers.rst | 29 ++ docs/source/ads.opctl.diagnostics.rst | 37 +++ docs/source/ads.opctl.distributed.common.rst | 61 ++++ docs/source/ads.opctl.distributed.rst | 45 +++ docs/source/ads.opctl.rst | 59 ++++ docs/source/ads.opctl.spark.rst | 29 ++ docs/source/ads.opctl.spec.rst | 37 +++ docs/source/ads.oracledb.rst | 14 +- .../ads.pipeline.builders.infrastructure.rst | 21 ++ docs/source/ads.pipeline.builders.rst | 18 ++ docs/source/ads.pipeline.rst | 63 ++++ docs/source/ads.pipeline.schema.rst | 10 + docs/source/ads.pipeline.visualizer.rst | 37 +++ docs/source/ads.rst | 72 +++-- docs/source/ads.secrets.rst | 41 ++- docs/source/ads.telemetry.rst | 21 ++ docs/source/ads.tests.rst | 29 ++ docs/source/ads.text_dataset.rst | 27 +- docs/source/ads.type_discovery.rst | 125 ++++++++ docs/source/ads.vault.rst | 16 +- 82 files changed, 2942 insertions(+), 782 deletions(-) create mode 100644 docs/source/ads.common.artifact.rst create mode 100644 docs/source/ads.common.decorator.rst create mode 100644 docs/source/ads.common.function.rst create mode 100644 docs/source/ads.data_labeling.interface.rst create mode 100644 docs/source/ads.data_labeling.loader.rst create mode 100644 docs/source/ads.data_labeling.mixin.rst create mode 100644 docs/source/ads.data_labeling.parser.rst create mode 100644 docs/source/ads.data_labeling.reader.rst create mode 100644 docs/source/ads.data_labeling.visualizer.rst create mode 100644 docs/source/ads.dbmixin.rst create mode 100644 docs/source/ads.environment.rst create mode 100644 docs/source/ads.experiments.rst create mode 100644 docs/source/ads.feature_engineering.accessor.mixin.rst create mode 100644 docs/source/ads.feature_engineering.accessor.rst create mode 100644 docs/source/ads.feature_engineering.adsimage.interface.rst create mode 100644 docs/source/ads.feature_engineering.adsimage.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.oci_language.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.parsers.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.rst create mode 100644 docs/source/ads.feature_engineering.adsstring.string.rst create mode 100644 docs/source/ads.feature_engineering.dataset.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.adsstring.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.handler.rst create mode 100644 docs/source/ads.feature_engineering.feature_type.rst create mode 100644 docs/source/ads.hpo.visualization.rst create mode 100644 docs/source/ads.jobs.builders.infrastructure.rst create mode 100644 docs/source/ads.jobs.builders.rst create mode 100644 docs/source/ads.jobs.builders.runtimes.rst create mode 100644 docs/source/ads.jobs.schema.rst create mode 100644 docs/source/ads.jobs.templates.rst create mode 100644 docs/source/ads.model.common.rst create mode 100644 docs/source/ads.model.deployment.common.rst rename docs/source/{ads.model_deployment.rst => ads.model.deployment.rst} (67%) create mode 100644 docs/source/ads.model.extractor.rst rename docs/source/{ads.model_framework.rst => ads.model.framework.rst} (86%) create mode 100644 docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst create mode 100644 docs/source/ads.model.model_artifact_boilerplate.rst create mode 100644 docs/source/ads.model.service.rst create mode 100644 docs/source/ads.model.transformer.rst create mode 100644 docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst create mode 100644 docs/source/ads.model_artifact_boilerplate.rst create mode 100644 docs/source/ads.mysqldb.rst create mode 100644 docs/source/ads.opctl.backend.rst create mode 100644 docs/source/ads.opctl.conda.rst create mode 100644 docs/source/ads.opctl.config.diagnostics.rst create mode 100644 docs/source/ads.opctl.config.rst create mode 100644 docs/source/ads.opctl.config.yaml_parsers.distributed.rst create mode 100644 docs/source/ads.opctl.config.yaml_parsers.rst create mode 100644 docs/source/ads.opctl.diagnostics.rst create mode 100644 docs/source/ads.opctl.distributed.common.rst create mode 100644 docs/source/ads.opctl.distributed.rst create mode 100644 docs/source/ads.opctl.rst create mode 100644 docs/source/ads.opctl.spark.rst create mode 100644 docs/source/ads.opctl.spec.rst create mode 100644 docs/source/ads.pipeline.builders.infrastructure.rst create mode 100644 docs/source/ads.pipeline.builders.rst create mode 100644 docs/source/ads.pipeline.rst create mode 100644 docs/source/ads.pipeline.schema.rst create mode 100644 docs/source/ads.pipeline.visualizer.rst create mode 100644 docs/source/ads.telemetry.rst create mode 100644 docs/source/ads.tests.rst create mode 100644 docs/source/ads.type_discovery.rst diff --git a/docs/source/ads.automl.rst b/docs/source/ads.automl.rst index c45b7223b..3dbd52e62 100644 --- a/docs/source/ads.automl.rst +++ b/docs/source/ads.automl.rst @@ -8,23 +8,22 @@ ads.automl.driver module ------------------------ .. automodule:: ads.automl.driver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.automl.provider module -------------------------- .. automodule:: ads.automl.provider - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.automl - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.bds.rst b/docs/source/ads.bds.rst index a9fd18880..d192c047f 100644 --- a/docs/source/ads.bds.rst +++ b/docs/source/ads.bds.rst @@ -8,14 +8,22 @@ ads.bds.auth module ------------------- .. automodule:: ads.bds.auth - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +ads.bds.big\_data\_service module +--------------------------------- + +.. automodule:: ads.bds.big_data_service + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.bds - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.catalog.rst b/docs/source/ads.catalog.rst index 943629697..63e2bbfe3 100644 --- a/docs/source/ads.catalog.rst +++ b/docs/source/ads.catalog.rst @@ -8,39 +8,38 @@ ads.catalog.model module ------------------------ .. automodule:: ads.catalog.model - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.catalog.notebook module --------------------------- .. automodule:: ads.catalog.notebook - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.catalog.project module -------------------------- .. automodule:: ads.catalog.project - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.catalog.summary module -------------------------- .. automodule:: ads.catalog.summary - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.catalog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.artifact.rst b/docs/source/ads.common.artifact.rst new file mode 100644 index 000000000..6b36311bc --- /dev/null +++ b/docs/source/ads.common.artifact.rst @@ -0,0 +1,10 @@ +ads.common.artifact package +=========================== + +Module contents +--------------- + +.. automodule:: ads.common.artifact + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.decorator.rst b/docs/source/ads.common.decorator.rst new file mode 100644 index 000000000..9912b256d --- /dev/null +++ b/docs/source/ads.common.decorator.rst @@ -0,0 +1,45 @@ +ads.common.decorator package +============================ + +Submodules +---------- + +ads.common.decorator.argument\_to\_case module +---------------------------------------------- + +.. automodule:: ads.common.decorator.argument_to_case + :members: + :undoc-members: + :show-inheritance: + +ads.common.decorator.deprecate module +------------------------------------- + +.. automodule:: ads.common.decorator.deprecate + :members: + :undoc-members: + :show-inheritance: + +ads.common.decorator.runtime\_dependency module +----------------------------------------------- + +.. automodule:: ads.common.decorator.runtime_dependency + :members: + :undoc-members: + :show-inheritance: + +ads.common.decorator.utils module +--------------------------------- + +.. automodule:: ads.common.decorator.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.common.decorator + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.function.rst b/docs/source/ads.common.function.rst new file mode 100644 index 000000000..378eda371 --- /dev/null +++ b/docs/source/ads.common.function.rst @@ -0,0 +1,21 @@ +ads.common.function package +=========================== + +Submodules +---------- + +ads.common.function.fn\_util module +----------------------------------- + +.. automodule:: ads.common.function.fn_util + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.common.function + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.common.rst b/docs/source/ads.common.rst index dd26d8ac0..e1ccc8259 100644 --- a/docs/source/ads.common.rst +++ b/docs/source/ads.common.rst @@ -1,112 +1,223 @@ ads.common package ================== +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.common.artifact + ads.common.decorator + ads.common.function + Submodules ---------- -ads.common.card\_identifier module ----------------------------------- +ads.common.analyzer module +-------------------------- -.. automodule:: ads.common.card_identifier - :members: - :undoc-members: - :show-inheritance: +.. automodule:: ads.common.analyzer + :members: + :undoc-members: + :show-inheritance: ads.common.auth module ---------------------- - + .. automodule:: ads.common.auth - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +ads.common.base\_properties module +---------------------------------- + +.. automodule:: ads.common.base_properties + :members: + :undoc-members: + :show-inheritance: + +ads.common.card\_identifier module +---------------------------------- + +.. automodule:: ads.common.card_identifier + :members: + :undoc-members: + :show-inheritance: + +ads.common.config module +------------------------ + +.. automodule:: ads.common.config + :members: + :undoc-members: + :show-inheritance: ads.common.data module ---------------------- .. automodule:: ads.common.data - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +ads.common.data\_serializer module +---------------------------------- + +.. automodule:: ads.common.data_serializer + :members: + :undoc-members: + :show-inheritance: + +ads.common.error module +----------------------- + +.. automodule:: ads.common.error + :members: + :undoc-members: + :show-inheritance: + +ads.common.extended\_enum module +-------------------------------- + +.. automodule:: ads.common.extended_enum + :members: + :undoc-members: + :show-inheritance: + +ads.common.ipython module +------------------------- + +.. automodule:: ads.common.ipython + :members: + :undoc-members: + :show-inheritance: ads.common.model module ----------------------- .. automodule:: ads.common.model - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -ads.common.model\_metadata module +ads.common.model\_artifact module --------------------------------- -.. automodule:: ads.common.model_metadata - :members: - :undoc-members: - :show-inheritance: - -ads.common.decorator.runtime\_dependency module ------------------------------------------------ - -.. automodule:: ads.common.decorator.runtime_dependency - :members: - :undoc-members: - :show-inheritance: +.. automodule:: ads.common.model_artifact + :members: + :undoc-members: + :show-inheritance: -ads.common.decorator.deprecate module +ads.common.model\_export\_util module ------------------------------------- -.. automodule:: ads.common.decorator.deprecate - :members: - :undoc-members: - :show-inheritance: +.. automodule:: ads.common.model_export_util + :members: + :undoc-members: + :show-inheritance: ads.common.model\_introspect module ----------------------------------- .. automodule:: ads.common.model_introspect - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -ads.common.model\_export\_util module -------------------------------------- +ads.common.model\_metadata module +--------------------------------- -.. automodule:: ads.common.model_export_util - :members: - :undoc-members: - :show-inheritance: +.. automodule:: ads.common.model_metadata + :members: + :undoc-members: + :show-inheritance: -ads.common.function.fn\_util module ------------------------------------ +ads.common.model\_metadata\_mixin module +---------------------------------------- -.. automodule:: ads.common.function.fn_util - :members: - :undoc-members: - :show-inheritance: +.. automodule:: ads.common.model_metadata_mixin + :members: + :undoc-members: + :show-inheritance: +ads.common.object\_storage\_details module +------------------------------------------ + +.. automodule:: ads.common.object_storage_details + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_client module +----------------------------- + +.. automodule:: ads.common.oci_client + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_datascience module +---------------------------------- + +.. automodule:: ads.common.oci_datascience + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_logging module +------------------------------ + +.. automodule:: ads.common.oci_logging + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_mixin module +---------------------------- + +.. automodule:: ads.common.oci_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.common.oci\_resource module +------------------------------- + +.. automodule:: ads.common.oci_resource + :members: + :undoc-members: + :show-inheritance: + +ads.common.serializer module +---------------------------- + +.. automodule:: ads.common.serializer + :members: + :undoc-members: + :show-inheritance: ads.common.utils module ----------------------- .. automodule:: ads.common.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: +ads.common.word\_lists module +----------------------------- + +.. automodule:: ads.common.word_lists + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.common - :members: - :undoc-members: - :show-inheritance: - - -ads.common.model\_metadata\_mixin module ----------------------------------------- - -.. automodule:: ads.common.model_metadata_mixin :members: :undoc-members: :show-inheritance: diff --git a/docs/source/ads.data_labeling.interface.rst b/docs/source/ads.data_labeling.interface.rst new file mode 100644 index 000000000..d49a392c1 --- /dev/null +++ b/docs/source/ads.data_labeling.interface.rst @@ -0,0 +1,37 @@ +ads.data\_labeling.interface package +==================================== + +Submodules +---------- + +ads.data\_labeling.interface.loader module +------------------------------------------ + +.. automodule:: ads.data_labeling.interface.loader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.interface.parser module +------------------------------------------ + +.. automodule:: ads.data_labeling.interface.parser + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.interface.reader module +------------------------------------------ + +.. automodule:: ads.data_labeling.interface.reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.interface + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.loader.rst b/docs/source/ads.data_labeling.loader.rst new file mode 100644 index 000000000..e0193640b --- /dev/null +++ b/docs/source/ads.data_labeling.loader.rst @@ -0,0 +1,21 @@ +ads.data\_labeling.loader package +================================= + +Submodules +---------- + +ads.data\_labeling.loader.file\_loader module +--------------------------------------------- + +.. automodule:: ads.data_labeling.loader.file_loader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.loader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.mixin.rst b/docs/source/ads.data_labeling.mixin.rst new file mode 100644 index 000000000..9509083fa --- /dev/null +++ b/docs/source/ads.data_labeling.mixin.rst @@ -0,0 +1,21 @@ +ads.data\_labeling.mixin package +================================ + +Submodules +---------- + +ads.data\_labeling.mixin.data\_labeling module +---------------------------------------------- + +.. automodule:: ads.data_labeling.mixin.data_labeling + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.mixin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.parser.rst b/docs/source/ads.data_labeling.parser.rst new file mode 100644 index 000000000..df1d192a3 --- /dev/null +++ b/docs/source/ads.data_labeling.parser.rst @@ -0,0 +1,37 @@ +ads.data\_labeling.parser package +================================= + +Submodules +---------- + +ads.data\_labeling.parser.dls\_record\_parser module +---------------------------------------------------- + +.. automodule:: ads.data_labeling.parser.dls_record_parser + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.parser.export\_metadata\_parser module +--------------------------------------------------------- + +.. automodule:: ads.data_labeling.parser.export_metadata_parser + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.parser.export\_record\_parser module +------------------------------------------------------- + +.. automodule:: ads.data_labeling.parser.export_record_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.parser + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.reader.rst b/docs/source/ads.data_labeling.reader.rst new file mode 100644 index 000000000..fd6299b1a --- /dev/null +++ b/docs/source/ads.data_labeling.reader.rst @@ -0,0 +1,61 @@ +ads.data\_labeling.reader package +================================= + +Submodules +---------- + +ads.data\_labeling.reader.dataset\_reader module +------------------------------------------------ + +.. automodule:: ads.data_labeling.reader.dataset_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.dls\_record\_reader module +---------------------------------------------------- + +.. automodule:: ads.data_labeling.reader.dls_record_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.export\_record\_reader module +------------------------------------------------------- + +.. automodule:: ads.data_labeling.reader.export_record_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.jsonl\_reader module +---------------------------------------------- + +.. automodule:: ads.data_labeling.reader.jsonl_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.metadata\_reader module +------------------------------------------------- + +.. automodule:: ads.data_labeling.reader.metadata_reader + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.reader.record\_reader module +----------------------------------------------- + +.. automodule:: ads.data_labeling.reader.record_reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.data_labeling.rst b/docs/source/ads.data_labeling.rst index c0502ebd2..7cd6c0763 100644 --- a/docs/source/ads.data_labeling.rst +++ b/docs/source/ads.data_labeling.rst @@ -1,32 +1,21 @@ ads.data\_labeling package ========================== -Submodules ----------- - -ads.data\_labeling.interface.loader module ------------------------------------------- - -.. automodule:: ads.data_labeling.interface.loader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.interface.parser module ------------------------------------------- +Subpackages +----------- -.. automodule:: ads.data_labeling.interface.parser - :members: - :undoc-members: - :show-inheritance: +.. toctree:: + :maxdepth: 4 -ads.data\_labeling.interface.reader module ------------------------------------------- + ads.data_labeling.interface + ads.data_labeling.loader + ads.data_labeling.mixin + ads.data_labeling.parser + ads.data_labeling.reader + ads.data_labeling.visualizer -.. automodule:: ads.data_labeling.interface.reader - :members: - :undoc-members: - :show-inheritance: +Submodules +---------- ads.data\_labeling.boundingbox module ------------------------------------- @@ -76,78 +65,6 @@ ads.data\_labeling.record module :undoc-members: :show-inheritance: -ads.data\_labeling.mixin.data\_labeling module ----------------------------------------------- - -.. automodule:: ads.data_labeling.mixin.data_labeling - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.parser.export\_metadata\_parser module ---------------------------------------------------------- - -.. automodule:: ads.data_labeling.parser.export_metadata_parser - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.parser.export\_record\_parser module -------------------------------------------------------- - -.. automodule:: ads.data_labeling.parser.export_record_parser - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.dataset\_reader module ------------------------------------------------- - -.. automodule:: ads.data_labeling.reader.dataset_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.jsonl\_reader module ----------------------------------------------- - -.. automodule:: ads.data_labeling.reader.jsonl_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.metadata\_reader module -------------------------------------------------- - -.. automodule:: ads.data_labeling.reader.metadata_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.reader.record\_reader module ------------------------------------------------ - -.. automodule:: ads.data_labeling.reader.record_reader - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.visualizer.image\_visualizer module ------------------------------------------------------- - -.. automodule:: ads.data_labeling.visualizer.image_visualizer - :members: - :undoc-members: - :show-inheritance: - -ads.data\_labeling.visualizer.text\_visualizer module ------------------------------------------------------ - -.. automodule:: ads.data_labeling.visualizer.text_visualizer - :members: - :undoc-members: - :show-inheritance: - Module contents --------------- diff --git a/docs/source/ads.data_labeling.visualizer.rst b/docs/source/ads.data_labeling.visualizer.rst new file mode 100644 index 000000000..ddef0f229 --- /dev/null +++ b/docs/source/ads.data_labeling.visualizer.rst @@ -0,0 +1,29 @@ +ads.data\_labeling.visualizer package +===================================== + +Submodules +---------- + +ads.data\_labeling.visualizer.image\_visualizer module +------------------------------------------------------ + +.. automodule:: ads.data_labeling.visualizer.image_visualizer + :members: + :undoc-members: + :show-inheritance: + +ads.data\_labeling.visualizer.text\_visualizer module +----------------------------------------------------- + +.. automodule:: ads.data_labeling.visualizer.text_visualizer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.data_labeling.visualizer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.database.rst b/docs/source/ads.database.rst index a2df4002f..5d15010a2 100644 --- a/docs/source/ads.database.rst +++ b/docs/source/ads.database.rst @@ -1,9 +1,6 @@ ads.database package ==================== -Subpackages ------------ - Submodules ---------- @@ -11,14 +8,14 @@ ads.database.connection module ------------------------------ .. automodule:: ads.database.connection - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.database - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.dataflow.rst b/docs/source/ads.dataflow.rst index 83307c986..e4e9e68e5 100644 --- a/docs/source/ads.dataflow.rst +++ b/docs/source/ads.dataflow.rst @@ -8,24 +8,22 @@ ads.dataflow.dataflow module ---------------------------- .. automodule:: ads.dataflow.dataflow - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataflow.dataflowsummary module ----------------------------------- .. automodule:: ads.dataflow.dataflowsummary - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.dataflow - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.dataset.rst b/docs/source/ads.dataset.rst index 8c26e5fc6..b40c62861 100644 --- a/docs/source/ads.dataset.rst +++ b/docs/source/ads.dataset.rst @@ -8,199 +8,198 @@ ads.dataset.classification\_dataset module ------------------------------------------ .. automodule:: ads.dataset.classification_dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.correlation module ------------------------------ .. automodule:: ads.dataset.correlation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.correlation\_plot module ------------------------------------ .. automodule:: ads.dataset.correlation_plot - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.dask\_series module ------------------------------- .. automodule:: ads.dataset.dask_series - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.dataframe\_transformer module ----------------------------------------- .. automodule:: ads.dataset.dataframe_transformer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.dataset module -------------------------- .. automodule:: ads.dataset.dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.dataset\_browser module ----------------------------------- .. automodule:: ads.dataset.dataset_browser - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.dataset\_with\_target module ---------------------------------------- .. automodule:: ads.dataset.dataset_with_target - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.exception module ---------------------------- .. automodule:: ads.dataset.exception - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.factory module -------------------------- .. automodule:: ads.dataset.factory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.feature\_engineering\_transformer module ---------------------------------------------------- .. automodule:: ads.dataset.feature_engineering_transformer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.feature\_selection module ------------------------------------- .. automodule:: ads.dataset.feature_selection - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.forecasting\_dataset module --------------------------------------- .. automodule:: ads.dataset.forecasting_dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.helper module ------------------------- .. automodule:: ads.dataset.helper - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.label\_encoder module --------------------------------- .. automodule:: ads.dataset.label_encoder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.pipeline module --------------------------- .. automodule:: ads.dataset.pipeline - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.plot module ----------------------- .. automodule:: ads.dataset.plot - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.progress module --------------------------- .. automodule:: ads.dataset.progress - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.recommendation module --------------------------------- .. automodule:: ads.dataset.recommendation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.recommendation\_transformer module ---------------------------------------------- .. automodule:: ads.dataset.recommendation_transformer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.regression\_dataset module -------------------------------------- .. automodule:: ads.dataset.regression_dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.sampled\_dataset module ----------------------------------- .. automodule:: ads.dataset.sampled_dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.target module ------------------------- .. automodule:: ads.dataset.target - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.dataset.timeseries module ----------------------------- .. automodule:: ads.dataset.timeseries - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.dbmixin.rst b/docs/source/ads.dbmixin.rst new file mode 100644 index 000000000..3d825bcd4 --- /dev/null +++ b/docs/source/ads.dbmixin.rst @@ -0,0 +1,21 @@ +ads.dbmixin package +=================== + +Submodules +---------- + +ads.dbmixin.db\_pandas\_accessor module +--------------------------------------- + +.. automodule:: ads.dbmixin.db_pandas_accessor + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.dbmixin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.environment.rst b/docs/source/ads.environment.rst new file mode 100644 index 000000000..24b6fbd61 --- /dev/null +++ b/docs/source/ads.environment.rst @@ -0,0 +1,21 @@ +ads.environment package +======================= + +Submodules +---------- + +ads.environment.ml\_runtime module +---------------------------------- + +.. automodule:: ads.environment.ml_runtime + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.environment + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.evaluations.rst b/docs/source/ads.evaluations.rst index e4144d794..681e494fd 100644 --- a/docs/source/ads.evaluations.rst +++ b/docs/source/ads.evaluations.rst @@ -8,31 +8,30 @@ ads.evaluations.evaluation\_plot module --------------------------------------- .. automodule:: ads.evaluations.evaluation_plot - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.evaluations.evaluator module -------------------------------- .. automodule:: ads.evaluations.evaluator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.evaluations.statistical\_metrics module ------------------------------------------- .. automodule:: ads.evaluations.statistical_metrics - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.evaluations - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.experiments.rst b/docs/source/ads.experiments.rst new file mode 100644 index 000000000..877db54a7 --- /dev/null +++ b/docs/source/ads.experiments.rst @@ -0,0 +1,10 @@ +ads.experiments package +======================= + +Module contents +--------------- + +.. automodule:: ads.experiments + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.explanations.rst b/docs/source/ads.explanations.rst index 8077b1a4e..f8b6770a0 100644 --- a/docs/source/ads.explanations.rst +++ b/docs/source/ads.explanations.rst @@ -8,54 +8,54 @@ ads.explanations.base\_explainer module --------------------------------------- .. automodule:: ads.explanations.base_explainer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.explanations.explainer module --------------------------------- .. automodule:: ads.explanations.explainer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.explanations.mlx\_global\_explainer module ---------------------------------------------- .. automodule:: ads.explanations.mlx_global_explainer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.explanations.mlx\_interface module -------------------------------------- .. automodule:: ads.explanations.mlx_interface - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.explanations.mlx\_local\_explainer module --------------------------------------------- .. automodule:: ads.explanations.mlx_local_explainer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.explanations.mlx\_whatif\_explainer module ---------------------------------------------- .. automodule:: ads.explanations.mlx_whatif_explainer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.explanations - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.accessor.mixin.rst b/docs/source/ads.feature_engineering.accessor.mixin.rst new file mode 100644 index 000000000..4d742cf87 --- /dev/null +++ b/docs/source/ads.feature_engineering.accessor.mixin.rst @@ -0,0 +1,53 @@ +ads.feature\_engineering.accessor.mixin package +=============================================== + +Submodules +---------- + +ads.feature\_engineering.accessor.mixin.correlation module +---------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.correlation + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.eda\_mixin module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.eda\_mixin\_series module +----------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin_series + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.feature\_types\_mixin module +-------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.feature_types_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.mixin.utils module +---------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.mixin.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.accessor.mixin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.accessor.rst b/docs/source/ads.feature_engineering.accessor.rst new file mode 100644 index 000000000..430d7b3c0 --- /dev/null +++ b/docs/source/ads.feature_engineering.accessor.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.accessor package +========================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.accessor.mixin + +Submodules +---------- + +ads.feature\_engineering.accessor.dataframe\_accessor module +------------------------------------------------------------ + +.. automodule:: ads.feature_engineering.accessor.dataframe_accessor + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.accessor.series\_accessor module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.accessor.series_accessor + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.accessor + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsimage.interface.rst b/docs/source/ads.feature_engineering.adsimage.interface.rst new file mode 100644 index 000000000..768792d89 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsimage.interface.rst @@ -0,0 +1,21 @@ +ads.feature\_engineering.adsimage.interface package +=================================================== + +Submodules +---------- + +ads.feature\_engineering.adsimage.interface.reader module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsimage.interface.reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsimage.interface + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsimage.rst b/docs/source/ads.feature_engineering.adsimage.rst new file mode 100644 index 000000000..18b4b31c5 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsimage.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.adsimage package +========================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.adsimage.interface + +Submodules +---------- + +ads.feature\_engineering.adsimage.image module +---------------------------------------------- + +.. automodule:: ads.feature_engineering.adsimage.image + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsimage.image\_reader module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.adsimage.image_reader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsimage + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.oci_language.rst b/docs/source/ads.feature_engineering.adsstring.oci_language.rst new file mode 100644 index 000000000..9f01d73d0 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.oci_language.rst @@ -0,0 +1,10 @@ +ads.feature\_engineering.adsstring.oci\_language package +======================================================== + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring.oci_language + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.parsers.rst b/docs/source/ads.feature_engineering.adsstring.parsers.rst new file mode 100644 index 000000000..e36481d03 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.parsers.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.adsstring.parsers package +================================================== + +Submodules +---------- + +ads.feature\_engineering.adsstring.parsers.base module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.adsstring.parsers.base + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.parsers.nltk\_parser module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.parsers.nltk_parser + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.parsers.spacy\_parser module +--------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.parsers.spacy_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring.parsers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.rst b/docs/source/ads.feature_engineering.adsstring.rst new file mode 100644 index 000000000..e7b369d13 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.rst @@ -0,0 +1,47 @@ +ads.feature\_engineering.adsstring package +========================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.adsstring.oci_language + ads.feature_engineering.adsstring.parsers + ads.feature_engineering.adsstring.string + +Submodules +---------- + +ads.feature\_engineering.adsstring.common\_regex\_mixin module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.common_regex_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.oci\_language module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.adsstring.oci_language + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.adsstring.string module +------------------------------------------------ + +.. automodule:: ads.feature_engineering.adsstring.string + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.adsstring.string.rst b/docs/source/ads.feature_engineering.adsstring.string.rst new file mode 100644 index 000000000..33881bd27 --- /dev/null +++ b/docs/source/ads.feature_engineering.adsstring.string.rst @@ -0,0 +1,10 @@ +ads.feature\_engineering.adsstring.string package +================================================= + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.adsstring.string + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.dataset.rst b/docs/source/ads.feature_engineering.dataset.rst new file mode 100644 index 000000000..762272758 --- /dev/null +++ b/docs/source/ads.feature_engineering.dataset.rst @@ -0,0 +1,21 @@ +ads.feature\_engineering.dataset package +======================================== + +Submodules +---------- + +ads.feature\_engineering.dataset.zip\_code\_data module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.dataset.zip_code_data + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.dataset + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst b/docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst new file mode 100644 index 000000000..6b88006f9 --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.adsstring.parsers.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.feature\_type.adsstring.parsers package +================================================================ + +Submodules +---------- + +ads.feature\_engineering.feature\_type.adsstring.parsers.base module +-------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers.base + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.parsers.nltk\_parser module +---------------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers.nltk_parser + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.parsers.spacy\_parser module +----------------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers.spacy_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.parsers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.adsstring.rst b/docs/source/ads.feature_engineering.feature_type.adsstring.rst new file mode 100644 index 000000000..2b979db3a --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.adsstring.rst @@ -0,0 +1,45 @@ +ads.feature\_engineering.feature\_type.adsstring package +======================================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.feature_type.adsstring.parsers + +Submodules +---------- + +ads.feature\_engineering.feature\_type.adsstring.common\_regex\_mixin module +---------------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.common_regex_mixin + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.oci\_language module +--------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.oci_language + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.adsstring.string module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring.string + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type.adsstring + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.handler.rst b/docs/source/ads.feature_engineering.feature_type.handler.rst new file mode 100644 index 000000000..bb258d4e0 --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.handler.rst @@ -0,0 +1,37 @@ +ads.feature\_engineering.feature\_type.handler package +====================================================== + +Submodules +---------- + +ads.feature\_engineering.feature\_type.handler.feature\_validator module +------------------------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.handler.feature_validator + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.handler.feature\_warning module +---------------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.handler.feature_warning + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.handler.warnings module +-------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.handler.warnings + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type.handler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.feature_type.rst b/docs/source/ads.feature_engineering.feature_type.rst new file mode 100644 index 000000000..5e8ea8afe --- /dev/null +++ b/docs/source/ads.feature_engineering.feature_type.rst @@ -0,0 +1,206 @@ +ads.feature\_engineering.feature\_type package +============================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.feature_type.adsstring + ads.feature_engineering.feature_type.handler + +Submodules +---------- + +ads.feature\_engineering.feature\_type.address module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.address + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.base module +-------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.base + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.boolean module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.boolean + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.category module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.category + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.constant module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.constant + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.continuous module +-------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.continuous + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.creditcard module +-------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.creditcard + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.datetime module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.datetime + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.discrete module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.discrete + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.document module +------------------------------------------------------ + +.. automodule:: ads.feature_engineering.feature_type.document + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.gis module +------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.gis + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.integer module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.integer + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ip\_address module +--------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ip_address + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ip\_address\_v4 module +------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ip_address_v4 + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ip\_address\_v6 module +------------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ip_address_v6 + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.lat\_long module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.lat_long + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.object module +---------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.object + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.ordinal module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.ordinal + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.phone\_number module +----------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.phone_number + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.string module +---------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.string + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.text module +-------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.text + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.unknown module +----------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.unknown + :members: + :undoc-members: + :show-inheritance: + +ads.feature\_engineering.feature\_type.zip\_code module +------------------------------------------------------- + +.. automodule:: ads.feature_engineering.feature_type.zip_code + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.feature_engineering.feature_type + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.feature_engineering.rst b/docs/source/ads.feature_engineering.rst index af547adf8..4f81cd6b3 100644 --- a/docs/source/ads.feature_engineering.rst +++ b/docs/source/ads.feature_engineering.rst @@ -1,6 +1,18 @@ ads.feature\_engineering package ================================ +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.feature_engineering.accessor + ads.feature_engineering.adsimage + ads.feature_engineering.adsstring + ads.feature_engineering.dataset + ads.feature_engineering.feature_type + Submodules ---------- @@ -20,282 +32,18 @@ ads.feature\_engineering.feature\_type\_manager module :undoc-members: :show-inheritance: -ads.feature\_engineering.accessor.dataframe\_accessor module -------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.dataframe_accessor - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.series\_accessor module ----------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.series_accessor - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.correlation module ------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.mixin.correlation - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.eda\_mixin module ------------------------------------------------------------ - -.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.eda\_mixin\_series module -------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.mixin.eda_mixin_series - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.accessor.mixin.feature\_types\_mixin module --------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.accessor.mixin.feature_types_mixin - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.adsstring.common\_regex\_mixin module --------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.adsstring.common_regex_mixin - :members: CommonRegexMixin - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.adsstring.oci\_language module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.adsstring.oci_language - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.adsstring.string module ------------------------------------------------- - -.. automodule:: ads.feature_engineering.adsstring.string - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.address module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.address - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.base module ---------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.base - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.boolean module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.boolean - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.category module ----------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.category - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.constant module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.constant - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.continuous module ---------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.continuous - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.creditcard module ---------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.creditcard - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.datetime module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.datetime - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.discrete module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.discrete - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.document module -------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.document - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.gis module --------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.gis - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.integer module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.integer - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ip_address module --------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ip_address - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ip_address\_v4 module ------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ip_address_v4 - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ip_address\_v6 module ------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ip_address_v6 - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.lat_long module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.lat_long - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.object module ------------------------------------------------------ - -.. automodule:: ads.feature_engineering.feature_type.object - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.ordinal module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.ordinal - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.phone_number module ----------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.phone_number - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.string module ------------------------------------------------------ - -.. automodule:: ads.feature_engineering.feature_type.string - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.text module ---------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.text - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.unknown module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.unknown - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.zip_code module ------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.zip_code - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.handler.feature\_validator module ------------------------------------------------------------------------- - -.. automodule:: ads.feature_engineering.feature_type.handler.feature_validator - :members: - :undoc-members: - :show-inheritance: - -ads.feature\_engineering.feature\_type.handler.feature\_warning module ------------------------------------------------------------------------- +ads.feature\_engineering.schema module +-------------------------------------- -.. automodule:: ads.feature_engineering.feature_type.handler.feature_warning +.. automodule:: ads.feature_engineering.schema :members: :undoc-members: :show-inheritance: -ads.feature\_engineering.feature\_type.handler.warnings module ----------------------------------------------------------------- +ads.feature\_engineering.utils module +------------------------------------- -.. automodule:: ads.feature_engineering.feature_type.handler.warnings +.. automodule:: ads.feature_engineering.utils :members: :undoc-members: :show-inheritance: diff --git a/docs/source/ads.hpo.rst b/docs/source/ads.hpo.rst index e67d72f2f..313d93d66 100644 --- a/docs/source/ads.hpo.rst +++ b/docs/source/ads.hpo.rst @@ -1,38 +1,85 @@ ads.hpo package -======================== +=============== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.hpo.visualization Submodules ---------- +ads.hpo.ads\_search\_space module +--------------------------------- + +.. automodule:: ads.hpo.ads_search_space + :members: + :undoc-members: + :show-inheritance: + ads.hpo.distributions module ---------------------------------------- +---------------------------- .. automodule:: ads.hpo.distributions - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.objective module +------------------------ + +.. automodule:: ads.hpo.objective + :members: + :undoc-members: + :show-inheritance: ads.hpo.search\_cv module ---------------------------------------- +------------------------- .. automodule:: ads.hpo.search_cv - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -ads.hpo.stopping\_criterion ---------------------------------------- +ads.hpo.stopping\_criterion module +---------------------------------- .. automodule:: ads.hpo.stopping_criterion - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.tuner\_artifact module +------------------------------ + +.. automodule:: ads.hpo.tuner_artifact + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.utils module +-------------------- + +.. automodule:: ads.hpo.utils + :members: + :undoc-members: + :show-inheritance: + +ads.hpo.validation module +------------------------- + +.. automodule:: ads.hpo.validation + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.hpo - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.hpo.visualization.rst b/docs/source/ads.hpo.visualization.rst new file mode 100644 index 000000000..37426494f --- /dev/null +++ b/docs/source/ads.hpo.visualization.rst @@ -0,0 +1,10 @@ +ads.hpo.visualization package +============================= + +Module contents +--------------- + +.. automodule:: ads.hpo.visualization + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.builders.infrastructure.rst b/docs/source/ads.jobs.builders.infrastructure.rst new file mode 100644 index 000000000..614615674 --- /dev/null +++ b/docs/source/ads.jobs.builders.infrastructure.rst @@ -0,0 +1,53 @@ +ads.jobs.builders.infrastructure package +======================================== + +Submodules +---------- + +ads.jobs.builders.infrastructure.base module +-------------------------------------------- + +.. automodule:: ads.jobs.builders.infrastructure.base + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.dataflow module +------------------------------------------------ + +.. automodule:: ads.jobs.builders.infrastructure.dataflow + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.dsc\_job module +------------------------------------------------ + +.. automodule:: ads.jobs.builders.infrastructure.dsc_job + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.dsc\_job\_runtime module +--------------------------------------------------------- + +.. automodule:: ads.jobs.builders.infrastructure.dsc_job_runtime + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.infrastructure.utils module +--------------------------------------------- + +.. automodule:: ads.jobs.builders.infrastructure.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.builders.infrastructure + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.builders.rst b/docs/source/ads.jobs.builders.rst new file mode 100644 index 000000000..0ead1cae1 --- /dev/null +++ b/docs/source/ads.jobs.builders.rst @@ -0,0 +1,30 @@ +ads.jobs.builders package +========================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.jobs.builders.infrastructure + ads.jobs.builders.runtimes + +Submodules +---------- + +ads.jobs.builders.base module +----------------------------- + +.. automodule:: ads.jobs.builders.base + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.builders + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.builders.runtimes.rst b/docs/source/ads.jobs.builders.runtimes.rst new file mode 100644 index 000000000..c7d5ebd63 --- /dev/null +++ b/docs/source/ads.jobs.builders.runtimes.rst @@ -0,0 +1,45 @@ +ads.jobs.builders.runtimes package +================================== + +Submodules +---------- + +ads.jobs.builders.runtimes.artifact module +------------------------------------------ + +.. automodule:: ads.jobs.builders.runtimes.artifact + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.runtimes.base module +-------------------------------------- + +.. automodule:: ads.jobs.builders.runtimes.base + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.runtimes.container\_runtime module +---------------------------------------------------- + +.. automodule:: ads.jobs.builders.runtimes.container_runtime + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.builders.runtimes.python\_runtime module +------------------------------------------------- + +.. automodule:: ads.jobs.builders.runtimes.python_runtime + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.builders.runtimes + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.rst b/docs/source/ads.jobs.rst index 7eb2d6482..2e7daa34e 100644 --- a/docs/source/ads.jobs.rst +++ b/docs/source/ads.jobs.rst @@ -1,6 +1,16 @@ ads.jobs package ================ +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.jobs.builders + ads.jobs.schema + ads.jobs.templates + Submodules ---------- @@ -12,26 +22,42 @@ ads.jobs.ads\_job module :undoc-members: :show-inheritance: -ads.jobs.builders.runtimes.python\_runtime module -------------------------------------------------- +ads.jobs.cli module +------------------- + +.. automodule:: ads.jobs.cli + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.env\_var\_parser module +-------------------------------- -.. automodule:: ads.jobs.builders.runtimes.python_runtime +.. automodule:: ads.jobs.env_var_parser :members: :undoc-members: :show-inheritance: -ads.jobs.builders.infrastructure.dataflow module ------------------------------------------------- +ads.jobs.extension module +------------------------- -.. automodule:: ads.jobs.builders.infrastructure.dataflow +.. automodule:: ads.jobs.extension :members: :undoc-members: :show-inheritance: -ads.jobs.builders.infrastructure.dsc\_job module ------------------------------------------------- +ads.jobs.serializer module +-------------------------- -.. automodule:: ads.jobs.builders.infrastructure.dsc_job +.. automodule:: ads.jobs.serializer + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.utils module +--------------------- + +.. automodule:: ads.jobs.utils :members: :undoc-members: :show-inheritance: @@ -42,4 +68,4 @@ Module contents .. automodule:: ads.jobs :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/source/ads.jobs.schema.rst b/docs/source/ads.jobs.schema.rst new file mode 100644 index 000000000..0856ee19e --- /dev/null +++ b/docs/source/ads.jobs.schema.rst @@ -0,0 +1,21 @@ +ads.jobs.schema package +======================= + +Submodules +---------- + +ads.jobs.schema.validator module +-------------------------------- + +.. automodule:: ads.jobs.schema.validator + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.schema + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.jobs.templates.rst b/docs/source/ads.jobs.templates.rst new file mode 100644 index 000000000..ee6634c29 --- /dev/null +++ b/docs/source/ads.jobs.templates.rst @@ -0,0 +1,45 @@ +ads.jobs.templates package +========================== + +Submodules +---------- + +ads.jobs.templates.container module +----------------------------------- + +.. automodule:: ads.jobs.templates.container + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.templates.driver\_notebook module +------------------------------------------ + +.. automodule:: ads.jobs.templates.driver_notebook + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.templates.driver\_oci module +------------------------------------- + +.. automodule:: ads.jobs.templates.driver_oci + :members: + :undoc-members: + :show-inheritance: + +ads.jobs.templates.driver\_python module +---------------------------------------- + +.. automodule:: ads.jobs.templates.driver_python + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.jobs.templates + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.common.rst b/docs/source/ads.model.common.rst new file mode 100644 index 000000000..0e8a811a6 --- /dev/null +++ b/docs/source/ads.model.common.rst @@ -0,0 +1,21 @@ +ads.model.common package +======================== + +Submodules +---------- + +ads.model.common.utils module +----------------------------- + +.. automodule:: ads.model.common.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.deployment.common.rst b/docs/source/ads.model.deployment.common.rst new file mode 100644 index 000000000..2fde6ebbc --- /dev/null +++ b/docs/source/ads.model.deployment.common.rst @@ -0,0 +1,29 @@ +ads.model.deployment.common package +=================================== + +Submodules +---------- + +ads.model.deployment.common.progress\_bar module +------------------------------------------------ + +.. automodule:: ads.model.deployment.common.progress_bar + :members: + :undoc-members: + :show-inheritance: + +ads.model.deployment.common.utils module +---------------------------------------- + +.. automodule:: ads.model.deployment.common.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.deployment.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model_deployment.rst b/docs/source/ads.model.deployment.rst similarity index 67% rename from docs/source/ads.model_deployment.rst rename to docs/source/ads.model.deployment.rst index cb14e1d63..c15d28dd7 100644 --- a/docs/source/ads.model_deployment.rst +++ b/docs/source/ads.model.deployment.rst @@ -1,6 +1,14 @@ ads.model.deployment package ============================ +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model.deployment.common + Submodules ---------- @@ -8,31 +16,30 @@ ads.model.deployment.model\_deployer module ------------------------------------------- .. automodule:: ads.model.deployment.model_deployer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.model.deployment.model\_deployment module --------------------------------------------- .. automodule:: ads.model.deployment.model_deployment - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: ads.model.deployment.model\_deployment\_properties module --------------------------------------------------------- .. automodule:: ads.model.deployment.model_deployment_properties - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.model.deployment - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.extractor.rst b/docs/source/ads.model.extractor.rst new file mode 100644 index 000000000..e21802b46 --- /dev/null +++ b/docs/source/ads.model.extractor.rst @@ -0,0 +1,93 @@ +ads.model.extractor package +=========================== + +Submodules +---------- + +ads.model.extractor.automl\_extractor module +-------------------------------------------- + +.. automodule:: ads.model.extractor.automl_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.keras\_extractor module +------------------------------------------- + +.. automodule:: ads.model.extractor.keras_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.lightgbm\_extractor module +---------------------------------------------- + +.. automodule:: ads.model.extractor.lightgbm_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.model\_info\_extractor module +------------------------------------------------- + +.. automodule:: ads.model.extractor.model_info_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.model\_info\_extractor\_factory module +---------------------------------------------------------- + +.. automodule:: ads.model.extractor.model_info_extractor_factory + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.pytorch\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.pytorch_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.sklearn\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.sklearn_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.spark\_extractor module +------------------------------------------- + +.. automodule:: ads.model.extractor.spark_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.tensorflow\_extractor module +------------------------------------------------ + +.. automodule:: ads.model.extractor.tensorflow_extractor + :members: + :undoc-members: + :show-inheritance: + +ads.model.extractor.xgboost\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.xgboost_extractor + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.extractor + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model_framework.rst b/docs/source/ads.model.framework.rst similarity index 86% rename from docs/source/ads.model_framework.rst rename to docs/source/ads.model.framework.rst index bb1923ab4..80b6f9a99 100644 --- a/docs/source/ads.model_framework.rst +++ b/docs/source/ads.model.framework.rst @@ -1,5 +1,5 @@ ads.model.framework package -================================ +=========================== Submodules ---------- @@ -36,6 +36,14 @@ ads.model.framework.sklearn\_model module :undoc-members: :show-inheritance: +ads.model.framework.spark\_model module +--------------------------------------- + +.. automodule:: ads.model.framework.spark_model + :members: + :undoc-members: + :show-inheritance: + ads.model.framework.tensorflow\_model module -------------------------------------------- diff --git a/docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst b/docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst new file mode 100644 index 000000000..d5ca14e8e --- /dev/null +++ b/docs/source/ads.model.model_artifact_boilerplate.artifact_introspection_test.rst @@ -0,0 +1,21 @@ +ads.model.model\_artifact\_boilerplate.artifact\_introspection\_test package +============================================================================ + +Submodules +---------- + +ads.model.model\_artifact\_boilerplate.artifact\_introspection\_test.model\_artifact\_validate module +----------------------------------------------------------------------------------------------------- + +.. automodule:: ads.model.model_artifact_boilerplate.artifact_introspection_test.model_artifact_validate + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.model_artifact_boilerplate.artifact_introspection_test + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.model_artifact_boilerplate.rst b/docs/source/ads.model.model_artifact_boilerplate.rst new file mode 100644 index 000000000..d4f626fb8 --- /dev/null +++ b/docs/source/ads.model.model_artifact_boilerplate.rst @@ -0,0 +1,29 @@ +ads.model.model\_artifact\_boilerplate package +============================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model.model_artifact_boilerplate.artifact_introspection_test + +Submodules +---------- + +ads.model.model\_artifact\_boilerplate.score module +--------------------------------------------------- + +.. automodule:: ads.model.model_artifact_boilerplate.score + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.model_artifact_boilerplate + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.rst b/docs/source/ads.model.rst index 87731a054..8a34769bb 100644 --- a/docs/source/ads.model.rst +++ b/docs/source/ads.model.rst @@ -1,5 +1,20 @@ -ads.model.framework other package -================================= +ads.model package +================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model.common + ads.model.deployment + ads.model.extractor + ads.model.framework + ads.model.model_artifact_boilerplate + ads.model.runtime + ads.model.service + ads.model.transformer Submodules ---------- @@ -12,6 +27,38 @@ ads.model.artifact module :undoc-members: :show-inheritance: +ads.model.artifact\_downloader module +------------------------------------- + +.. automodule:: ads.model.artifact_downloader + :members: + :undoc-members: + :show-inheritance: + +ads.model.artifact\_uploader module +----------------------------------- + +.. automodule:: ads.model.artifact_uploader + :members: + :undoc-members: + :show-inheritance: + +ads.model.base\_properties module +--------------------------------- + +.. automodule:: ads.model.base_properties + :members: + :undoc-members: + :show-inheritance: + +ads.model.datascience\_model module +----------------------------------- + +.. automodule:: ads.model.datascience_model + :members: + :undoc-members: + :show-inheritance: + ads.model.generic\_model module ------------------------------- @@ -20,6 +67,30 @@ ads.model.generic\_model module :undoc-members: :show-inheritance: +ads.model.model\_introspect module +---------------------------------- + +.. automodule:: ads.model.model_introspect + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_metadata module +-------------------------------- + +.. automodule:: ads.model.model_metadata + :members: + :undoc-members: + :show-inheritance: + +ads.model.model\_metadata\_mixin module +--------------------------------------- + +.. automodule:: ads.model.model_metadata_mixin + :members: + :undoc-members: + :show-inheritance: + ads.model.model\_properties module ---------------------------------- @@ -28,94 +99,14 @@ ads.model.model\_properties module :undoc-members: :show-inheritance: -ads.model.runtime.runtime\_info module --------------------------------------- +ads.model.model\_version\_set module +------------------------------------ -.. automodule:: ads.model.runtime.runtime_info +.. automodule:: ads.model.model_version_set :members: :undoc-members: :show-inheritance: -ads.model.extractor.model\_info\_extractor\_factory module ----------------------------------------------------------- - -.. automodule:: ads.model.extractor.model_info_extractor_factory - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.model\_artifact module ------------------------------------------- - -.. automodule:: ads.model.extractor.model_artifact - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.automl\_extractor module --------------------------------------------- - -.. automodule:: ads.model.extractor.automl_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.xgboost\_extractor module ---------------------------------------------- - -.. automodule:: ads.model.extractor.xgboost_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.lightgbm\_extractor module ----------------------------------------------- - -.. automodule:: ads.model.extractor.lightgbm_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.model\_info\_extractor module -------------------------------------------------- - -.. automodule:: ads.model.extractor.model_info_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.sklearn\_extractor module ---------------------------------------------- - -.. automodule:: ads.model.extractor.sklearn_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.keras\_extractor module -------------------------------------------- - -.. automodule:: ads.model.extractor.keras_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.tensorflow\_extractor module ------------------------------------------------- - -.. automodule:: ads.model.extractor.tensorflow_extractor - :members: - :undoc-members: - :show-inheritance: - -ads.model.extractor.pytorch\_extractor module ---------------------------------------------- - -.. automodule:: ads.model.extractor.pytorch_extractor - :members: - :undoc-members: - :show-inheritance: - Module contents --------------- diff --git a/docs/source/ads.model.service.rst b/docs/source/ads.model.service.rst new file mode 100644 index 000000000..caa9173c7 --- /dev/null +++ b/docs/source/ads.model.service.rst @@ -0,0 +1,29 @@ +ads.model.service package +========================= + +Submodules +---------- + +ads.model.service.oci\_datascience\_model module +------------------------------------------------ + +.. automodule:: ads.model.service.oci_datascience_model + :members: + :undoc-members: + :show-inheritance: + +ads.model.service.oci\_datascience\_model\_version\_set module +-------------------------------------------------------------- + +.. automodule:: ads.model.service.oci_datascience_model_version_set + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model.transformer.rst b/docs/source/ads.model.transformer.rst new file mode 100644 index 000000000..be953afec --- /dev/null +++ b/docs/source/ads.model.transformer.rst @@ -0,0 +1,21 @@ +ads.model.transformer package +============================= + +Submodules +---------- + +ads.model.transformer.onnx\_transformer module +---------------------------------------------- + +.. automodule:: ads.model.transformer.onnx_transformer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model.transformer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst b/docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst new file mode 100644 index 000000000..cddd1fe75 --- /dev/null +++ b/docs/source/ads.model_artifact_boilerplate.artifact_introspection_test.rst @@ -0,0 +1,21 @@ +ads.model\_artifact\_boilerplate.artifact\_introspection\_test package +====================================================================== + +Submodules +---------- + +ads.model\_artifact\_boilerplate.artifact\_introspection\_test.model\_artifact\_validate module +----------------------------------------------------------------------------------------------- + +.. automodule:: ads.model_artifact_boilerplate.artifact_introspection_test.model_artifact_validate + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model_artifact_boilerplate.artifact_introspection_test + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.model_artifact_boilerplate.rst b/docs/source/ads.model_artifact_boilerplate.rst new file mode 100644 index 000000000..825fc42c9 --- /dev/null +++ b/docs/source/ads.model_artifact_boilerplate.rst @@ -0,0 +1,29 @@ +ads.model\_artifact\_boilerplate package +======================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.model_artifact_boilerplate.artifact_introspection_test + +Submodules +---------- + +ads.model\_artifact\_boilerplate.score module +--------------------------------------------- + +.. automodule:: ads.model_artifact_boilerplate.score + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.model_artifact_boilerplate + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.mysqldb.rst b/docs/source/ads.mysqldb.rst new file mode 100644 index 000000000..1092b6cb7 --- /dev/null +++ b/docs/source/ads.mysqldb.rst @@ -0,0 +1,21 @@ +ads.mysqldb package +=================== + +Submodules +---------- + +ads.mysqldb.mysql\_db module +---------------------------- + +.. automodule:: ads.mysqldb.mysql_db + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.mysqldb + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.backend.rst b/docs/source/ads.opctl.backend.rst new file mode 100644 index 000000000..df19c4a45 --- /dev/null +++ b/docs/source/ads.opctl.backend.rst @@ -0,0 +1,53 @@ +ads.opctl.backend package +========================= + +Submodules +---------- + +ads.opctl.backend.ads\_dataflow module +-------------------------------------- + +.. automodule:: ads.opctl.backend.ads_dataflow + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.ads\_ml\_job module +------------------------------------- + +.. automodule:: ads.opctl.backend.ads_ml_job + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.ads\_ml\_pipeline module +------------------------------------------ + +.. automodule:: ads.opctl.backend.ads_ml_pipeline + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.base module +----------------------------- + +.. automodule:: ads.opctl.backend.base + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.backend.local module +------------------------------ + +.. automodule:: ads.opctl.backend.local + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.backend + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.conda.rst b/docs/source/ads.opctl.conda.rst new file mode 100644 index 000000000..716491f61 --- /dev/null +++ b/docs/source/ads.opctl.conda.rst @@ -0,0 +1,45 @@ +ads.opctl.conda package +======================= + +Submodules +---------- + +ads.opctl.conda.cli module +-------------------------- + +.. automodule:: ads.opctl.conda.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.conda.cmds module +--------------------------- + +.. automodule:: ads.opctl.conda.cmds + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.conda.multipart\_uploader module +------------------------------------------ + +.. automodule:: ads.opctl.conda.multipart_uploader + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.conda.pack module +--------------------------- + +.. automodule:: ads.opctl.conda.pack + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.conda + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.diagnostics.rst b/docs/source/ads.opctl.config.diagnostics.rst new file mode 100644 index 000000000..5f7b8b8be --- /dev/null +++ b/docs/source/ads.opctl.config.diagnostics.rst @@ -0,0 +1,10 @@ +ads.opctl.config.diagnostics package +==================================== + +Module contents +--------------- + +.. automodule:: ads.opctl.config.diagnostics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.rst b/docs/source/ads.opctl.config.rst new file mode 100644 index 000000000..bc58c70ce --- /dev/null +++ b/docs/source/ads.opctl.config.rst @@ -0,0 +1,70 @@ +ads.opctl.config package +======================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.config.diagnostics + ads.opctl.config.yaml_parsers + +Submodules +---------- + +ads.opctl.config.base module +---------------------------- + +.. automodule:: ads.opctl.config.base + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.merger module +------------------------------ + +.. automodule:: ads.opctl.config.merger + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.resolver module +-------------------------------- + +.. automodule:: ads.opctl.config.resolver + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.utils module +----------------------------- + +.. automodule:: ads.opctl.config.utils + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.validator module +--------------------------------- + +.. automodule:: ads.opctl.config.validator + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.config.versioner module +--------------------------------- + +.. automodule:: ads.opctl.config.versioner + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.config + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.yaml_parsers.distributed.rst b/docs/source/ads.opctl.config.yaml_parsers.distributed.rst new file mode 100644 index 000000000..a4ad70daf --- /dev/null +++ b/docs/source/ads.opctl.config.yaml_parsers.distributed.rst @@ -0,0 +1,21 @@ +ads.opctl.config.yaml\_parsers.distributed package +================================================== + +Submodules +---------- + +ads.opctl.config.yaml\_parsers.distributed.yaml\_parser module +-------------------------------------------------------------- + +.. automodule:: ads.opctl.config.yaml_parsers.distributed.yaml_parser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.config.yaml_parsers.distributed + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.config.yaml_parsers.rst b/docs/source/ads.opctl.config.yaml_parsers.rst new file mode 100644 index 000000000..5709e9851 --- /dev/null +++ b/docs/source/ads.opctl.config.yaml_parsers.rst @@ -0,0 +1,29 @@ +ads.opctl.config.yaml\_parsers package +====================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.config.yaml_parsers.distributed + +Submodules +---------- + +ads.opctl.config.yaml\_parsers.base module +------------------------------------------ + +.. automodule:: ads.opctl.config.yaml_parsers.base + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.config.yaml_parsers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.diagnostics.rst b/docs/source/ads.opctl.diagnostics.rst new file mode 100644 index 000000000..1cb8b85ce --- /dev/null +++ b/docs/source/ads.opctl.diagnostics.rst @@ -0,0 +1,37 @@ +ads.opctl.diagnostics package +============================= + +Submodules +---------- + +ads.opctl.diagnostics.check\_distributed\_job\_requirements module +------------------------------------------------------------------ + +.. automodule:: ads.opctl.diagnostics.check_distributed_job_requirements + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.diagnostics.check\_requirements module +------------------------------------------------ + +.. automodule:: ads.opctl.diagnostics.check_requirements + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.diagnostics.requirement\_exception module +--------------------------------------------------- + +.. automodule:: ads.opctl.diagnostics.requirement_exception + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.diagnostics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.distributed.common.rst b/docs/source/ads.opctl.distributed.common.rst new file mode 100644 index 000000000..ca95e15f3 --- /dev/null +++ b/docs/source/ads.opctl.distributed.common.rst @@ -0,0 +1,61 @@ +ads.opctl.distributed.common package +==================================== + +Submodules +---------- + +ads.opctl.distributed.common.abstract\_cluster\_provider module +--------------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.abstract_cluster_provider + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.abstract\_framework\_spec\_builder module +---------------------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.abstract_framework_spec_builder + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.cluster\_config\_helper module +----------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.cluster_config_helper + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.cluster\_provider\_factory module +-------------------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.cluster_provider_factory + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.cluster\_runner module +--------------------------------------------------- + +.. automodule:: ads.opctl.distributed.common.cluster_runner + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.common.framework\_factory module +------------------------------------------------------ + +.. automodule:: ads.opctl.distributed.common.framework_factory + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.distributed.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.distributed.rst b/docs/source/ads.opctl.distributed.rst new file mode 100644 index 000000000..ef3d73b10 --- /dev/null +++ b/docs/source/ads.opctl.distributed.rst @@ -0,0 +1,45 @@ +ads.opctl.distributed package +============================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.distributed.common + +Submodules +---------- + +ads.opctl.distributed.certificates module +----------------------------------------- + +.. automodule:: ads.opctl.distributed.certificates + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.cli module +-------------------------------- + +.. automodule:: ads.opctl.distributed.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.distributed.cmds module +--------------------------------- + +.. automodule:: ads.opctl.distributed.cmds + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.distributed + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.rst b/docs/source/ads.opctl.rst new file mode 100644 index 000000000..d659af771 --- /dev/null +++ b/docs/source/ads.opctl.rst @@ -0,0 +1,59 @@ +ads.opctl package +================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.opctl.backend + ads.opctl.conda + ads.opctl.config + ads.opctl.diagnostics + ads.opctl.distributed + ads.opctl.spark + ads.opctl.spec + +Submodules +---------- + +ads.opctl.cli module +-------------------- + +.. automodule:: ads.opctl.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.cmds module +--------------------- + +.. automodule:: ads.opctl.cmds + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.constants module +-------------------------- + +.. automodule:: ads.opctl.constants + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.utils module +---------------------- + +.. automodule:: ads.opctl.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.spark.rst b/docs/source/ads.opctl.spark.rst new file mode 100644 index 000000000..d02fb1216 --- /dev/null +++ b/docs/source/ads.opctl.spark.rst @@ -0,0 +1,29 @@ +ads.opctl.spark package +======================= + +Submodules +---------- + +ads.opctl.spark.cli module +-------------------------- + +.. automodule:: ads.opctl.spark.cli + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.spark.cmds module +--------------------------- + +.. automodule:: ads.opctl.spark.cmds + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.spark + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.opctl.spec.rst b/docs/source/ads.opctl.spec.rst new file mode 100644 index 000000000..71d7fda3b --- /dev/null +++ b/docs/source/ads.opctl.spec.rst @@ -0,0 +1,37 @@ +ads.opctl.spec package +====================== + +Submodules +---------- + +ads.opctl.spec.abstract\_operator\_spec module +---------------------------------------------- + +.. automodule:: ads.opctl.spec.abstract_operator_spec + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.spec.operator\_spec\_factory module +--------------------------------------------- + +.. automodule:: ads.opctl.spec.operator_spec_factory + :members: + :undoc-members: + :show-inheritance: + +ads.opctl.spec.utils module +--------------------------- + +.. automodule:: ads.opctl.spec.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.opctl.spec + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.oracledb.rst b/docs/source/ads.oracledb.rst index 9d1bd521c..6227b5d28 100644 --- a/docs/source/ads.oracledb.rst +++ b/docs/source/ads.oracledb.rst @@ -1,13 +1,21 @@ ads.oracledb package -================================ +==================== Submodules ---------- ads.oracledb.oracle\_db module ------------------------------------------- +------------------------------ .. automodule:: ads.oracledb.oracle_db :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.oracledb + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.builders.infrastructure.rst b/docs/source/ads.pipeline.builders.infrastructure.rst new file mode 100644 index 000000000..1359b3e8c --- /dev/null +++ b/docs/source/ads.pipeline.builders.infrastructure.rst @@ -0,0 +1,21 @@ +ads.pipeline.builders.infrastructure package +============================================ + +Submodules +---------- + +ads.pipeline.builders.infrastructure.custom\_script module +---------------------------------------------------------- + +.. automodule:: ads.pipeline.builders.infrastructure.custom_script + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.pipeline.builders.infrastructure + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.builders.rst b/docs/source/ads.pipeline.builders.rst new file mode 100644 index 000000000..c2886f393 --- /dev/null +++ b/docs/source/ads.pipeline.builders.rst @@ -0,0 +1,18 @@ +ads.pipeline.builders package +============================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.pipeline.builders.infrastructure + +Module contents +--------------- + +.. automodule:: ads.pipeline.builders + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.rst b/docs/source/ads.pipeline.rst new file mode 100644 index 000000000..a1f539b15 --- /dev/null +++ b/docs/source/ads.pipeline.rst @@ -0,0 +1,63 @@ +ads.pipeline package +==================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + ads.pipeline.builders + ads.pipeline.schema + ads.pipeline.visualizer + +Submodules +---------- + +ads.pipeline.ads\_pipeline module +--------------------------------- + +.. automodule:: ads.pipeline.ads_pipeline + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.ads\_pipeline\_run module +-------------------------------------- + +.. automodule:: ads.pipeline.ads_pipeline_run + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.ads\_pipeline\_step module +--------------------------------------- + +.. automodule:: ads.pipeline.ads_pipeline_step + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.cli module +----------------------- + +.. automodule:: ads.pipeline.cli + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.extension module +----------------------------- + +.. automodule:: ads.pipeline.extension + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.pipeline + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.schema.rst b/docs/source/ads.pipeline.schema.rst new file mode 100644 index 000000000..52f18f458 --- /dev/null +++ b/docs/source/ads.pipeline.schema.rst @@ -0,0 +1,10 @@ +ads.pipeline.schema package +=========================== + +Module contents +--------------- + +.. automodule:: ads.pipeline.schema + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.pipeline.visualizer.rst b/docs/source/ads.pipeline.visualizer.rst new file mode 100644 index 000000000..a1aea7807 --- /dev/null +++ b/docs/source/ads.pipeline.visualizer.rst @@ -0,0 +1,37 @@ +ads.pipeline.visualizer package +=============================== + +Submodules +---------- + +ads.pipeline.visualizer.base module +----------------------------------- + +.. automodule:: ads.pipeline.visualizer.base + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.visualizer.graph\_renderer module +---------------------------------------------- + +.. automodule:: ads.pipeline.visualizer.graph_renderer + :members: + :undoc-members: + :show-inheritance: + +ads.pipeline.visualizer.text\_renderer module +--------------------------------------------- + +.. automodule:: ads.pipeline.visualizer.text_renderer + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.pipeline.visualizer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.rst b/docs/source/ads.rst index 38af463d2..753260e3d 100644 --- a/docs/source/ads.rst +++ b/docs/source/ads.rst @@ -5,46 +5,60 @@ Subpackages ----------- .. toctree:: + :maxdepth: 4 - ads.automl - ads.catalog - ads.common - ads.bds - ads.data_labeling - ads.database - ads.dataflow - ads.dataset - ads.evaluations - ads.explanations - ads.feature_engineering - ads.hpo - ads.jobs - ads.model - ads.model_deployment - ads.model_framework - ads.model.runtime - ads.oracledb - ads.secrets - ads.text_dataset - ads.vault - + ads.automl + ads.bds + ads.catalog + ads.common + ads.data_labeling + ads.database + ads.dataflow + ads.dataset + ads.dbmixin + ads.environment + ads.evaluations + ads.experiments + ads.explanations + ads.feature_engineering + ads.hpo + ads.jobs + ads.model + ads.model_artifact_boilerplate + ads.mysqldb + ads.opctl + ads.oracledb + ads.pipeline + ads.secrets + ads.telemetry + ads.tests + ads.text_dataset + ads.type_discovery + ads.vault Submodules ---------- +ads.cli module +-------------- + +.. automodule:: ads.cli + :members: + :undoc-members: + :show-inheritance: + ads.config module ----------------- .. automodule:: ads.config - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.secrets.rst b/docs/source/ads.secrets.rst index 5d9f9bb2e..45e860093 100644 --- a/docs/source/ads.secrets.rst +++ b/docs/source/ads.secrets.rst @@ -4,18 +4,26 @@ ads.secrets package Submodules ---------- -ads.secrets.secrets module --------------------------- +ads.secrets.adb module +---------------------- -.. automodule:: ads.secrets.secrets +.. automodule:: ads.secrets.adb :members: :undoc-members: :show-inheritance: -ads.secrets.adb module ----------------------- +ads.secrets.auth\_token module +------------------------------ -.. automodule:: ads.secrets.adb +.. automodule:: ads.secrets.auth_token + :members: + :undoc-members: + :show-inheritance: + +ads.secrets.big\_data\_service module +------------------------------------- + +.. automodule:: ads.secrets.big_data_service :members: :undoc-members: :show-inheritance: @@ -36,18 +44,10 @@ ads.secrets.oracledb module :undoc-members: :show-inheritance: -ads.secrets.big\_data\_service module -------------------------------------- - -.. automodule:: ads.secrets.big_data_service - :members: - :undoc-members: - :show-inheritance: - -ads.secrets.auth\_token module ------------------------------- +ads.secrets.secrets module +-------------------------- -.. automodule:: ads.secrets.auth_token +.. automodule:: ads.secrets.secrets :members: :undoc-members: :show-inheritance: @@ -56,7 +56,6 @@ Module contents --------------- .. automodule:: ads.secrets - :members: - :undoc-members: - :show-inheritance: - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.telemetry.rst b/docs/source/ads.telemetry.rst new file mode 100644 index 000000000..f775d1b5d --- /dev/null +++ b/docs/source/ads.telemetry.rst @@ -0,0 +1,21 @@ +ads.telemetry package +===================== + +Submodules +---------- + +ads.telemetry.telemetry module +------------------------------ + +.. automodule:: ads.telemetry.telemetry + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.telemetry + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.tests.rst b/docs/source/ads.tests.rst new file mode 100644 index 000000000..61bec4b34 --- /dev/null +++ b/docs/source/ads.tests.rst @@ -0,0 +1,29 @@ +ads.tests package +================= + +Submodules +---------- + +ads.tests.test\_notebooks module +-------------------------------- + +.. automodule:: ads.tests.test_notebooks + :members: + :undoc-members: + :show-inheritance: + +ads.tests.utils module +---------------------- + +.. automodule:: ads.tests.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.text_dataset.rst b/docs/source/ads.text_dataset.rst index 8ba643ce6..2db1e9f1c 100644 --- a/docs/source/ads.text_dataset.rst +++ b/docs/source/ads.text_dataset.rst @@ -1,11 +1,11 @@ ads.text\_dataset package -================================ +========================= Submodules ---------- ads.text\_dataset.backends module ------------------------------------------- +--------------------------------- .. automodule:: ads.text_dataset.backends :members: @@ -13,7 +13,7 @@ ads.text\_dataset.backends module :show-inheritance: ads.text\_dataset.dataset module ------------------------------------------------------- +-------------------------------- .. automodule:: ads.text_dataset.dataset :members: @@ -21,7 +21,7 @@ ads.text\_dataset.dataset module :show-inheritance: ads.text\_dataset.extractor module -------------------------------------------------------------- +---------------------------------- .. automodule:: ads.text_dataset.extractor :members: @@ -29,13 +29,28 @@ ads.text\_dataset.extractor module :show-inheritance: ads.text\_dataset.options module ----------------------------------------------------------- +-------------------------------- .. automodule:: ads.text_dataset.options :members: :undoc-members: :show-inheritance: +ads.text\_dataset.udfs module +----------------------------- + +.. automodule:: ads.text_dataset.udfs + :members: + :undoc-members: + :show-inheritance: + +ads.text\_dataset.utils module +------------------------------ + +.. automodule:: ads.text_dataset.utils + :members: + :undoc-members: + :show-inheritance: Module contents --------------- @@ -43,4 +58,4 @@ Module contents .. automodule:: ads.text_dataset :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/source/ads.type_discovery.rst b/docs/source/ads.type_discovery.rst new file mode 100644 index 000000000..5a04610f5 --- /dev/null +++ b/docs/source/ads.type_discovery.rst @@ -0,0 +1,125 @@ +ads.type\_discovery package +=========================== + +Submodules +---------- + +ads.type\_discovery.abstract\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.abstract_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.constant\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.constant_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.continuous\_detector module +----------------------------------------------- + +.. automodule:: ads.type_discovery.continuous_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.credit\_card\_detector module +------------------------------------------------- + +.. automodule:: ads.type_discovery.credit_card_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.datetime\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.datetime_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.discrete\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.discrete_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.document\_detector module +--------------------------------------------- + +.. automodule:: ads.type_discovery.document_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.ip\_detector module +--------------------------------------- + +.. automodule:: ads.type_discovery.ip_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.latlon\_detector module +------------------------------------------- + +.. automodule:: ads.type_discovery.latlon_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.phone\_number\_detector module +-------------------------------------------------- + +.. automodule:: ads.type_discovery.phone_number_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.type\_discovery\_driver module +-------------------------------------------------- + +.. automodule:: ads.type_discovery.type_discovery_driver + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.typed\_feature module +----------------------------------------- + +.. automodule:: ads.type_discovery.typed_feature + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.unknown\_detector module +-------------------------------------------- + +.. automodule:: ads.type_discovery.unknown_detector + :members: + :undoc-members: + :show-inheritance: + +ads.type\_discovery.zipcode\_detector module +-------------------------------------------- + +.. automodule:: ads.type_discovery.zipcode_detector + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: ads.type_discovery + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/ads.vault.rst b/docs/source/ads.vault.rst index de92820ca..632cfc3fd 100644 --- a/docs/source/ads.vault.rst +++ b/docs/source/ads.vault.rst @@ -4,18 +4,18 @@ ads.vault package Submodules ---------- -ads.vault module ----------------- +ads.vault.vault module +---------------------- .. automodule:: ads.vault.vault - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: ads.vault - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: From 937e79dbd0ae8baf1c9dc4f16e4379c04d5e5691 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 22 Feb 2023 15:01:37 -0500 Subject: [PATCH 047/147] Update runtime.rst --- docs/source/user_guide/jobs/runtime.rst | 159 +++++++++++++++++++----- 1 file changed, 127 insertions(+), 32 deletions(-) diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst index a2ccbcf26..a311c1d9b 100644 --- a/docs/source/user_guide/jobs/runtime.rst +++ b/docs/source/user_guide/jobs/runtime.rst @@ -7,67 +7,162 @@ and other configurations for the environment to run the workload. Depending on the source code, ADS provides different types of *runtime* for defining a data science job, including: -* :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime` +* :py:class:`~ads.jobs.PythonRuntime` for Python code stored locally, OCI object storage, or other remote location supported by `fsspec `_ -* :py:class:`~ads.jobs.builders.runtimes.python_runtime.GitPythonRuntime` +* :py:class:`~ads.jobs.GitPythonRuntime` for Python code from a Git repository. -* :py:class:`~ads.jobs.builders.runtimes.python_runtime.NotebookRuntime` +* :py:class:`~ads.jobs.NotebookRuntime` for a single Jupyter notebook stored locally, OCI object storage, or other remote location supported by `fsspec `_ -* :py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime` +* :py:class:`~ads.jobs.ScriptRuntime` for bash or shell scripts stored locally, OCI object storage, or other remote location supported by `fsspec `_ -* :py:class:`~ads.jobs.builders.runtimes.python_runtime.ContainerRuntime` for container images. +* :py:class:`~ads.jobs.ContainerRuntime` for container images. +Environment Variables +===================== +You can set environment variables for a runtime by calling +:py:meth:`~ads.jobs.PythonRuntime.with_environment_variable()`. +Environment variables enclosed by ``${...}`` will be substituted. For example: + +.. code-block:: python + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_environment_variable( + HOST="10.0.0.1", + PORT="443", + URL="http://${HOST}:${PORT}/path/", + ESCAPED_URL="http://$${HOST}:$${PORT}/path/", + MISSING_VAR="This is ${UNDEFINED}", + VAR_WITH_DOLLAR="$10", + DOUBLE_DOLLAR="$$10" + ) + ) + + for k, v in runtime.environment_variables.items(): + print(f"{k}: {v}") + +will show the following environment variables for the runtime: + +.. code-block:: text + + HOST: 10.0.0.1 + PORT: 443 + URL: http://10.0.0.1:443/path/ + ESCAPED_URL: http://${HOST}:${PORT}/path/ + MISSING_VAR: This is This is ${UNDEFINED} + VAR_WITH_DOLLAR: $10 + DOUBLE_DOLLAR: $10 + +Note that: + +* You can use ``$$`` to escape the substitution. +* Undefined variable enclosed by ``${...}`` will be ignored. +* Double dollar signs ``$$`` will be substituted by a single one ``$``. + +Command Line Arguments +====================== + +The command line arguments for running your script or function can be configured by calling +:py:meth:`~ads.jobs.PythonRuntime.with_argument()`. For example: + +.. code-block:: python + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + .with_arguments( + "arg1", "arg2", + key1="val1", + key2="val2" + ) + ) + +will configured the job to call your script by: + +.. code-block:: bash + + python script.py arg1 arg2 --key1 val1 --key2 val2 + +You can call :py:meth:`~ads.jobs.PythonRuntime.with_argument()` multiple times to set the arguments +to your desired order. You can check ``runtime.args`` to see the added arguments. + +Here are a few more examples: + +.. code-block:: python + + runtime = PythonRuntime().with_argument(key1="val1", key2="val2").with_argument("pos1") + print(runtime.args) + # ["--key1", "val1", "--key2", "val2", "pos1"] + + runtime = PythonRuntime() + runtime.with_argument("pos1") + runtime.with_argument(key1="val1", key2="val2.1 val2.2") + runtime.with_argument("pos2") + print(runtime.args) + # ['pos1', '--key1', 'val1', '--key2', 'val2.1 val2.2', 'pos2'] + + runtime = PythonRuntime() + runtime.with_argument("pos1") + runtime.with_argument(key1=None, key2="val2") + runtime.with_argument("pos2") + print(runtime.args) + # ["pos1", "--key1", "--key2", "val2", "pos2"] + Conda Environment ================= -Except for :py:class:`~ads.jobs.builders.runtimes.python_runtime.ContainerRuntime`, +Except for :py:class:`~ads.jobs.ContainerRuntime`, all the other runtime options allow you to configure a `Conda Environment `_ for your workload. You can use the slug name to specify a -`conda environment provided by the data science service `_. +`conda environment provided by the data science service +`_. For example, to use the TensorFlow conda environment: -.. code-block:: python3 +.. code-block:: python - from ads.jobs import PythonRuntime + from ads.jobs import PythonRuntime - runtime = ( - PythonRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - # Use slug name for conda environment provided by data science service - .with_service_conda("tensorflow28_p38_cpu_v1") - ) + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + # Use slug name for conda environment provided by data science service + .with_service_conda("tensorflow28_p38_cpu_v1") + ) You can also use a custom conda environment published to OCI Object Storage -by passing the ``uri`` to the ``with_custom_conda()`` method, for example: +by passing the ``uri`` to :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`, +for example: -.. code-block:: python3 +.. code-block:: python runtime = ( - ScriptRuntime() + PythonRuntime() .with_source("oci://bucket_name@namespace/path/to/script.py") .with_custom_conda("oci://bucket@namespace/conda_pack/pack_name") ) -For more details on custom conda environment, see -`Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy `__. - -Environment Variables -===================== +By default, ADS will try to determine the region based on the authenticated API key or resource principal. +If your custom conda environment is stored in a different region, +you can specify the ``region`` when calling :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`. - -CLI Arguments -============= +For more details on custom conda environment, see +`Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy +`__. Override Configuration ====================== -When you call ``job.run()``, a new job run will be started with the configuration defined in the **job**. +When you call :py:meth:`ads.jobs.Job.run`, a new job run will be started with the configuration defined in the **job**. You may want to override the configuration with custom variables. For example, you can customize job run display name, override command line argument, specify additional environment variables, and add free form tags: @@ -75,8 +170,8 @@ and add free form tags: .. code-block:: python3 job_run = job.run( - name="", - args="new_arg --new_key new_val", - env_var={"new_env": "new_val"}, - freeform_tags={"new_tag": "new_tag_val"} - ) \ No newline at end of file + name="", + args="new_arg --new_key new_val", + env_var={"new_env": "new_val"}, + freeform_tags={"new_tag": "new_tag_val"} + ) From 5c26ba35f9d988b69962650a6fe4c2d21f531465 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 22 Feb 2023 15:01:42 -0500 Subject: [PATCH 048/147] Update infrastructure.rst --- .../source/user_guide/jobs/infrastructure.rst | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/source/user_guide/jobs/infrastructure.rst b/docs/source/user_guide/jobs/infrastructure.rst index d51a8b44b..b75763f9d 100644 --- a/docs/source/user_guide/jobs/infrastructure.rst +++ b/docs/source/user_guide/jobs/infrastructure.rst @@ -1,8 +1,8 @@ Infrastructure ************** -The Data Science Job infrastructure is defined by a -:py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance. For example: +The Data Science Job infrastructure is defined by a :py:class:`~ads.jobs.DataScienceJob` instance. +For example: .. code-block:: python3 @@ -23,14 +23,14 @@ The Data Science Job infrastructure is defined by a .with_log_id("") ) -When creating a :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance, -the following configurations are required: +When creating a :py:class:`~ads.jobs.DataScienceJob` instance, the following configurations are required: * Compartment ID * Project ID * Compute Shape The following configurations are optional: + * Block Storage Size, defaults to 50 (GB) * Log Group ID * Log ID @@ -41,7 +41,7 @@ Using Configurations from Notebook If you are creating a job from an OCI Data Science `Notebook Session `_, the same infrastructure configurations from the notebook session will be used as defaults. -You can initialize the :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` +You can initialize the :py:class:`~ads.jobs.DataScienceJob` with the logging configurations and override the other options as needed. For example: .. code-block:: python3 @@ -62,9 +62,13 @@ with the logging configurations and override the other options as needed. For ex Compute Shapes ============== -You can get a list of currently supported compute shapes by calling ``DataScienceJob.instance_shapes()``. -Additionally, you can get a list of shapes are available for fast launch by calling ``DataScienceJob.fast_launch_shapes()`` -Specifying a fast launch shape will allow your job to start as fast as possible. +The :py:class:`~ads.jobs.DataScienceJob` class provides two static methods to obtain the support compute shapes: + +* You can get a list of currently supported compute shapes by calling + :py:meth:`~ads.jobs.DataScienceJob.instance_shapes`. +* can get a list of shapes are available for fast launch by calling + :py:meth:`~ads.jobs.DataScienceJob.fast_launch_shapes`. + Specifying a fast launch shape will allow your job to start as fast as possible. Networking ========== @@ -77,7 +81,7 @@ You can control the network access through the subnet and security lists. If you specified a subnet ID, your job will be configured to have custom networking. Otherwise, default networking will be used. Note that when you are in a Data Science Notebook Session, the same networking configuration is be used by default. -You can specify the networking manually by calling ``with_job_infrastructure_type()``. +You can specify the networking manually by calling :py:meth:`~ads.jobs.DataScienceJob.with_job_infrastructure_type()`. Logging ======= @@ -86,7 +90,7 @@ Logging is not required to create the job. However, it is highly recommended to enable logging for debugging and monitoring purpose. In the preceding example, both the log OCID and corresponding log group OCID are specified -with the ``DataScienceJob`` instance. +with the :py:class:`~ads.jobs.DataScienceJob` instance. If your administrator configured the permission for you to search for logging resources, you can skip specifying the log group OCID because ADS can automatically retrieve it. From 9d94589bb9782de65b936dc0e07963ea82c10f7c Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 22 Feb 2023 15:02:00 -0500 Subject: [PATCH 049/147] Update data_science_job.rst --- .../user_guide/jobs/data_science_job.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index c54e1a0ab..3f6507845 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -7,12 +7,12 @@ Quick Start See also: :doc:`policies` and `About Data Science Policies `_. In ADS, a job is defined by :doc:`infrastructure` and :doc:`runtime`. -The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.builders.infrastructure.dsc_job.DataScienceJob` instance. -The runtime can be an instance of :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, -:py:class:`~ads.jobs.builders.runtimes.python_runtime.GitPythonRuntime`, -:py:class:`~ads.jobs.builders.runtimes.python_runtime.NotebookRuntime`, -:py:class:`~ads.jobs.builders.runtimes.python_runtime.ScriptRuntime`, or -:py:class:`~ads.jobs.builders.runtimes.python_runtime.ContainerRuntime` +The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.DataScienceJob` instance. +The runtime can be an instance of :py:class:`~ads.jobs.PythonRuntime`, +:py:class:`~ads.jobs.GitPythonRuntime`, +:py:class:`~ads.jobs.NotebookRuntime`, +:py:class:`~ads.jobs.ScriptRuntime`, or +:py:class:`~ads.jobs.ContainerRuntime` Create and Run a Job @@ -112,14 +112,16 @@ Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: For more details, see :doc:`infrastructure` configurations and see :doc:`runtime` configurations. -In :py:class:`~ads.jobs.builders.runtimes.python_runtime.PythonRuntime`, +In :py:class:`~ads.jobs.PythonRuntime`, the ``entrypoint`` can be a Python script, a Python function or a Jupyter notebook. Once the job is created, the job OCID can be accessed through ``job.id``. Once the job run is created, the job run OCID can be accessed through ``run.id``. -The ``watch()`` method is useful to monitor the progress of the job run if logging is configured. +The :py:meth:`~ads.jobs.DataScienceJobRun.watch` method is useful to monitor the progress of the job run. It will stream the logs to terminal and return once the job is finished. +Logging configurations are required for this method to show logs. + Here is an example of the logs: .. code-block:: text From d2280e386969a183a833cac2efb4320ed282cd65 Mon Sep 17 00:00:00 2001 From: Liuda Rudenka Date: Wed, 22 Feb 2023 17:32:50 -0600 Subject: [PATCH 050/147] ODSC-37150. Add workflow yml to main (#79) Co-authored-by: John DeSanto <202220+jdesanto@users.noreply.github.com> --- .github/workflows/run-unittests.yml | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/run-unittests.yml diff --git a/.github/workflows/run-unittests.yml b/.github/workflows/run-unittests.yml new file mode 100644 index 000000000..2ca6c6b94 --- /dev/null +++ b/.github/workflows/run-unittests.yml @@ -0,0 +1,69 @@ +name: Unit Tests + +on: + workflow_dispatch: + push: + branches: + - main + - 'release/**' + - develop + paths: + - '!docs/**' + + pull_request: + +# Cancel in progress workflows on pull_requests. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + name: ${{ matrix.tests-type }}, python ${{ matrix.python-version }} + runs-on: ubuntu-latest + timeout-minutes: 45 + + strategy: + fail-fast: false + matrix: + python-version: ["3.7","3.8","3.9","3.10"] + tests-type: ["DefaultSetup"] + + steps: + - uses: actions/checkout@v3 +# - uses: actions/cache@v3 +# with: +# path: ~/.cache/pip +# key: ${{ runner.os }}-pip-${{ hashFiles('**/test-requirements.txt') }} +# restore-keys: | +# ${{ runner.os }}-pip- + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: "Setup test env" + run: | + pip install coverage pytest-codecov tox==4.2.8 + + - name: "Run unit tests" + timeout-minutes: 45 + shell: bash + run: | + set -x # print commands that are executed +# coverage erase +# ./scripts/runtox.sh "${{ matrix.python-version }}-${{ matrix.tests-type }}" --cov --cov-report= +# coverage combine .coverage-* +# coverage html -i + + # Uploading test artifacts + # https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts#uploading-build-and-test-artifacts +# - name: "Upload artifact" +# uses: actions/upload-artifact@v3 +# with: +# name: code-coverage-report +# path: htmlcov/ +# retention-days: 10 From aad957e6e5e7199ffe8ff2ae66759bfcac3d12eb Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 23 Feb 2023 10:53:39 -0500 Subject: [PATCH 051/147] Re-organize page components for jobs docs. --- .../jobs/_template/runtime_types.rst | 4 ---- .../jobs/{ => components}/overview.rst | 0 .../jobs/components/runtime_non_byoc.rst | 11 ++++++++++ .../jobs/components/runtime_types.rst | 2 ++ .../user_guide/jobs/components/toc_local.rst | 4 ++++ docs/source/user_guide/jobs/index.rst | 4 +--- .../source/user_guide/jobs/infrastructure.rst | 1 - docs/source/user_guide/jobs/runtime.rst | 20 ++++++------------- .../model_training/training_with_oci.rst | 2 +- .../user_guide/pipeline/pipeline_step.rst | 2 +- 10 files changed, 26 insertions(+), 24 deletions(-) delete mode 100644 docs/source/user_guide/jobs/_template/runtime_types.rst rename docs/source/user_guide/jobs/{ => components}/overview.rst (100%) create mode 100644 docs/source/user_guide/jobs/components/runtime_non_byoc.rst create mode 100644 docs/source/user_guide/jobs/components/runtime_types.rst create mode 100644 docs/source/user_guide/jobs/components/toc_local.rst diff --git a/docs/source/user_guide/jobs/_template/runtime_types.rst b/docs/source/user_guide/jobs/_template/runtime_types.rst deleted file mode 100644 index e01f619bf..000000000 --- a/docs/source/user_guide/jobs/_template/runtime_types.rst +++ /dev/null @@ -1,4 +0,0 @@ -* ``GitPythonRuntime`` allows you to run source code from a Git repository, see :ref:`Run from Git `. -* ``NotebookRuntime`` allows you to run a JupyterLab Python notebook, see :ref:`Run a Notebook `. -* ``PythonRuntime`` allows you to run Python code with additional options, including setting a working directory, adding Python paths, and copying output files, see :ref:`Run a ZIP file or folder `. -* ``ScriptRuntime`` allows you to run Python, Bash, and Java scripts from a single source file (``.zip`` or ``.tar.gz``) or code directory, see :ref:`Run a Script ` and :ref:`Run a ZIP file or folder `. \ No newline at end of file diff --git a/docs/source/user_guide/jobs/overview.rst b/docs/source/user_guide/jobs/components/overview.rst similarity index 100% rename from docs/source/user_guide/jobs/overview.rst rename to docs/source/user_guide/jobs/components/overview.rst diff --git a/docs/source/user_guide/jobs/components/runtime_non_byoc.rst b/docs/source/user_guide/jobs/components/runtime_non_byoc.rst new file mode 100644 index 000000000..cb5e3f286 --- /dev/null +++ b/docs/source/user_guide/jobs/components/runtime_non_byoc.rst @@ -0,0 +1,11 @@ +* :py:class:`~ads.jobs.PythonRuntime` + for Python code stored locally, OCI object storage, or other remote location supported by + `fsspec `_. See :doc:`run_python`. +* :py:class:`~ads.jobs.GitPythonRuntime` + for Python code from a Git repository. See :doc:`run_git`. +* :py:class:`~ads.jobs.NotebookRuntime` + for a single Jupyter notebook stored locally, OCI object storage, or other remote location supported by + `fsspec `_. See :doc:`run_notebook`. +* :py:class:`~ads.jobs.ScriptRuntime` + for bash or shell scripts stored locally, OCI object storage, or other remote location supported by + `fsspec `_. See :doc:`run_script`. \ No newline at end of file diff --git a/docs/source/user_guide/jobs/components/runtime_types.rst b/docs/source/user_guide/jobs/components/runtime_types.rst new file mode 100644 index 000000000..aacbbdc06 --- /dev/null +++ b/docs/source/user_guide/jobs/components/runtime_types.rst @@ -0,0 +1,2 @@ +.. include:: ../jobs/components/runtime_non_byoc.rst +* :py:class:`~ads.jobs.ContainerRuntime` for container images. diff --git a/docs/source/user_guide/jobs/components/toc_local.rst b/docs/source/user_guide/jobs/components/toc_local.rst new file mode 100644 index 000000000..1a10fe7b0 --- /dev/null +++ b/docs/source/user_guide/jobs/components/toc_local.rst @@ -0,0 +1,4 @@ +.. contents:: In this Page + :depth: 2 + :local: + :backlinks: none \ No newline at end of file diff --git a/docs/source/user_guide/jobs/index.rst b/docs/source/user_guide/jobs/index.rst index 6f905c9ce..669caec27 100644 --- a/docs/source/user_guide/jobs/index.rst +++ b/docs/source/user_guide/jobs/index.rst @@ -1,9 +1,7 @@ -.. _jobs-1: - ################# Data Science Jobs ################# -.. include:: ../jobs/overview.rst +.. include:: ../jobs/components/overview.rst .. include:: ../jobs/toc.rst diff --git a/docs/source/user_guide/jobs/infrastructure.rst b/docs/source/user_guide/jobs/infrastructure.rst index b75763f9d..ddc8c9c8d 100644 --- a/docs/source/user_guide/jobs/infrastructure.rst +++ b/docs/source/user_guide/jobs/infrastructure.rst @@ -4,7 +4,6 @@ Infrastructure The Data Science Job infrastructure is defined by a :py:class:`~ads.jobs.DataScienceJob` instance. For example: - .. code-block:: python3 from ads.jobs import DataScienceJob diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst index a311c1d9b..475d62ffb 100644 --- a/docs/source/user_guide/jobs/runtime.rst +++ b/docs/source/user_guide/jobs/runtime.rst @@ -4,25 +4,17 @@ Runtime The *runtime* of a job defines the source code of your workload, environment variables, CLI arguments and other configurations for the environment to run the workload. +.. include:: ../jobs/components/toc_local.rst + Depending on the source code, ADS provides different types of *runtime* for defining a data science job, including: -* :py:class:`~ads.jobs.PythonRuntime` - for Python code stored locally, OCI object storage, or other remote location supported by - `fsspec `_ -* :py:class:`~ads.jobs.GitPythonRuntime` - for Python code from a Git repository. -* :py:class:`~ads.jobs.NotebookRuntime` - for a single Jupyter notebook stored locally, OCI object storage, or other remote location supported by - `fsspec `_ -* :py:class:`~ads.jobs.ScriptRuntime` - for bash or shell scripts stored locally, OCI object storage, or other remote location supported by - `fsspec `_ -* :py:class:`~ads.jobs.ContainerRuntime` for container images. +.. include:: ../jobs/components/runtime_types.rst Environment Variables ===================== + You can set environment variables for a runtime by calling :py:meth:`~ads.jobs.PythonRuntime.with_environment_variable()`. Environment variables enclosed by ``${...}`` will be substituted. For example: @@ -159,8 +151,8 @@ For more details on custom conda environment, see `__. -Override Configuration -====================== +Override Configurations +======================= When you call :py:meth:`ads.jobs.Job.run`, a new job run will be started with the configuration defined in the **job**. You may want to override the configuration with custom variables. For example, diff --git a/docs/source/user_guide/model_training/training_with_oci.rst b/docs/source/user_guide/model_training/training_with_oci.rst index 7ddc05f0d..9975adefb 100644 --- a/docs/source/user_guide/model_training/training_with_oci.rst +++ b/docs/source/user_guide/model_training/training_with_oci.rst @@ -2,6 +2,6 @@ Training with OCI ################# -.. include:: ../jobs/overview.rst +.. include:: ../jobs/components/overview.rst .. include:: ../jobs/toc.rst diff --git a/docs/source/user_guide/pipeline/pipeline_step.rst b/docs/source/user_guide/pipeline/pipeline_step.rst index b44488b30..4ef16c44a 100644 --- a/docs/source/user_guide/pipeline/pipeline_step.rst +++ b/docs/source/user_guide/pipeline/pipeline_step.rst @@ -93,7 +93,7 @@ When constructing a Custom Scrip step ``infrastructure``, you specify the Comput A Custom Script step can have different types of ``runtime`` depending on the source code you run: -.. include:: ../jobs/_template/runtime_types.rst +.. include:: ../jobs/components/runtime_non_byoc.rst All of these runtime options allow you to configure a `Data Science Conda Environment `__ for running your code. From 093c986c29c0e2ce860fedde25bb6c8ea7470fc5 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 23 Feb 2023 10:54:46 -0500 Subject: [PATCH 052/147] Update data_science_job.rst and run_python.rst --- .../user_guide/jobs/data_science_job.rst | 128 ++---- docs/source/user_guide/jobs/run_python.rst | 405 +++++++----------- .../jobs/tabs/name_substitution.rst | 59 +++ .../user_guide/jobs/tabs/python_runtime.rst | 89 ++++ 4 files changed, 340 insertions(+), 341 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/name_substitution.rst create mode 100644 docs/source/user_guide/jobs/tabs/python_runtime.rst diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 3f6507845..6c4bfe3af 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -6,109 +6,20 @@ Quick Start Before creating a job, ensure that you have policies configured for Data Science resources. See also: :doc:`policies` and `About Data Science Policies `_. -In ADS, a job is defined by :doc:`infrastructure` and :doc:`runtime`. -The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.DataScienceJob` instance. -The runtime can be an instance of :py:class:`~ads.jobs.PythonRuntime`, -:py:class:`~ads.jobs.GitPythonRuntime`, -:py:class:`~ads.jobs.NotebookRuntime`, -:py:class:`~ads.jobs.ScriptRuntime`, or -:py:class:`~ads.jobs.ContainerRuntime` - +.. include:: ../jobs/components/toc_local.rst Create and Run a Job ==================== +In ADS, a job is defined by :doc:`infrastructure` and :doc:`runtime`. +The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.DataScienceJob` instance. +The runtime can be an instance of: + +.. include:: ../jobs/components/runtime_types.rst + Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: -.. tabs:: - - .. code-tab:: python - :caption: Python - - from ads.jobs import Job, DataScienceJob, PythonRuntime - - job = ( - Job(name="My Job") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - # For default networking, no need to specify subnet ID - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - PythonRuntime() - # Specify the service conda environment by slug name. - .with_service_conda("pytorch19_p37_cpu_v1") - # The job artifact can be a single Python script, a directory or a zip file. - .with_source("local/path/to/code_dir") - # Set the working directory - # When using a directory as source, the default working dir is the parent of code_dir. - # Working dir should be a relative path beginning from the source directory (code_dir) - .with_working_dir("code_dir") - # The entrypoint is applicable only to directory or zip file as source - # The entrypoint should be a path relative to the working dir. - # Here my_script.py is a file in the code_dir/my_package directory - .with_entrypoint("my_package/my_script.py") - # Add an additional Python path, relative to the working dir (code_dir/other_packages). - .with_python_path("other_packages") - # Copy files in "code_dir/output" to object storage after job finishes. - .with_output("output", "oci://bucket_name@namespace/path/to/dir") - ) - ) - - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - - .. code-tab:: yaml - :caption: YAML - - kind: job - spec: - name: "My Job" - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - blockStorageSize: 50 - compartmentId: - jobInfrastructureType: STANDALONE - jobType: DEFAULT - logGroupId: - logId: - projectId: - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - shapeName: VM.Standard.E3.Flex - subnetId: - runtime: - kind: runtime - type: python - spec: - conda: - slug: pytorch19_p37_cpu_v1 - type: service - entrypoint: my_package/my_script.py - outputDir: output - outputUri: oci://bucket_name@namespace/path/to/dir - pythonPath: - - other_packages - scriptPathURI: local/path/to/code_dir - workingDir: code_dir +.. include:: ../jobs/tabs/python_runtime.rst For more details, see :doc:`infrastructure` configurations and see :doc:`runtime` configurations. @@ -140,7 +51,7 @@ Here is an example of the logs: YAML ==== -A job can also be defined using YAML, as shown in the "YAML" tab. +A job can be defined using YAML, as shown in the "YAML" tab. Here are some examples to load/save the YAML job configurations: .. code-block:: python @@ -160,7 +71,7 @@ Here are some examples to load/save the YAML job configurations: infrastructure: kind: infrastructure ... - """") + """) The ``uri`` can be a local file path or a remote location supported by `fsspec `_, including OCI object storage. @@ -173,6 +84,8 @@ With the YAML file, you can create and run the job with ADS CLI: For more details on ``ads opctl``, see :doc:`../cli/opctl/_template/jobs`. +The job infrastructure, runtime and job run also support YAML serialization/deserialization. + Loading Existing Job or Job Run =============================== @@ -226,3 +139,20 @@ You can also cancel a job run: .. code-block:: python run.cancel() + + +Variable Substitution +===================== + +When defining a job or starting a job run, +you can use environment variable substitution for the names and ``output_uri`` argument of +the :py:meth:`~ads.jobs.PythonRuntime.with_output` method. + +For example, the following job specifies the name based on the environment variable ``DATASET_NAME``, +and ``output_uri`` based on the environment variables ``JOB_RUN_OCID``: + +.. include:: ../jobs/tabs/name_substitution.rst + +Note that ``JOB_RUN_OCID`` is an environment variable provided by the service after the job run is created. +It is available for the ``output_uri`` but cannot be used in the job name. +See also :ref:`Saving Outputs <_runtime_outputs>` diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index 12371eb80..0c1d93703 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -1,250 +1,171 @@ -.. _job_run_zip: - Run a Python Workload ********************* -ScriptRuntime -============= - -The ``ScriptRuntime`` class is designed for you to define job artifacts and configurations supported by OCI Data Science jobs natively. It can be used with any script types that is supported by the OCI Data Science jobs, including a ZIP or compressed tar file or folder. See `Preparing Job Artifacts `__ for more details. In the job run, the working directory is the user's home directory. For example ``/home/datascience``. - -Python ------- - -If you are in a notebook session, ADS can automatically fetch the infrastructure configurations, and use them in the job. If you aren't in a notebook session or you want to customize the infrastructure, you can specify them using the methods in the ``DataScienceJob`` class. - -With the ``ScriptRuntime``, you can pass in a path to a ZIP file or directory. For a ZIP file, the path can be any URI supported by `fsspec `__, including OCI Object Storage. - -You must specify the ``entrypoint``, which is the relative path from the ZIP file or directory to the script starting your program. Note that the ``entrypoint`` contains the name of the directory, since the directory itself is also zipped as the job artifact. - -.. code-block:: python3 - - from ads.jobs import Job, DataScienceJob, ScriptRuntime - - job = ( - Job() - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - ScriptRuntime() - .with_source("path/to/zip_or_dir", entrypoint="zip_or_dir/main.py") - .with_service_conda("pytorch19_p37_cpu_v1") - ) +The :py:class:`~ads.jobs.PythonRuntime` is designed for running a Python workload. +You can configure the environment variables, command line arguments and conda environment +as described in :doc:`runtime`. This section shows the additional enhancements provided by +:py:class:`~ads.jobs.PythonRuntime`. + +.. include:: ../jobs/components/toc_local.rst + +Here is an example to define and run a job using :py:class:`~ads.jobs.PythonRuntime`: + +.. include:: ../jobs/tabs/python_runtime.rst + +The :py:class:`~ads.jobs.PythonRuntime` uses an driver script from ADS as the entry point for the job run. +It performs additional operations before and after invoking your code. +You can examine the driver script by downloading the job artifact from the OCI Console. + +Source Code +=========== + +In the :py:meth:`~ads.jobs.PythonRuntime.with_source` method, you can specify the location of your source code. +The location can be a local path or a remote URI supported by +`fsspec `_. +For example, you can specify files on OCI object storage using URI like +``oci://bucket@namespace/path/to/prefix``. ADS will use the authentication method configured by +:py:meth:`ads.set_auth()` to fetch the files and upload them as job artifact. + +The source code can be a single file, a compressed file/archive (zip/tar), or a folder. +When the source code is a compressed file/archive (zip/tar) or a folder, you need to also specify the entrypoint +using :py:meth:`~ads.jobs.PythonRuntime.with_entrypoint`. The path of the entrypoint should be a path relative to +the working directory. + +Working Directory +================= + +The working directory of your workload can be configured by :py:meth:`~ads.jobs.PythonRuntime.with_working_dir`. +By default, :py:class:`~ads.jobs.PythonRuntime` will create a ``code`` directory as the working directory +in the job run to store your source code (job artifact), +for example ``/home/datascience/decompressed_artifact/code``. + +File Source Code +---------------- + +If your source code is a single file, for example, ``my_script.py``, the file structure in the job run will look like: + +.. code-block:: text + + code <---This is the working directory + └── my_script.py + +You can refer your as ``./my_script.py`` + +Folder Source Code +------------------ + +If your source code is a folder, for example ``my_source_code``, ADS will compress the folder as job artifact. +In the job run, it will be decompressed under the working directory. The file structure in the job run will look like: + +.. code-block:: text + + code <---This is the working directory + └── my_source_code + ├── my_module.py + └── my_entrypoint.py + +In this case, the working directory is the parent of your source code folder. +You will need to specify the entrypoint as ``my_source_code/my_entrypoint.py``. + +.. code-block:: python + + runtime = ( + PythonRuntime() + .with_source("path/to/my_source_code") + .with_entrypoint("my_source_code/my_entrypoint.py") ) - # Create the job with OCI - job.create() - # Run the job and stream the outputs - job_run = job.run().watch() - - -YAML ----- - -You could use the following YAML example to create the same job with ``ScriptRuntime``: - -.. code-block:: yaml - - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - runtime: - kind: runtime - type: script - spec: - conda: - slug: pytorch19_p37_cpu_v1 - type: service - entrypoint: zip_or_dir/main.py - scriptPathURI: path/to/zip_or_dir - - - -PythonRuntime -============= - -The ``PythonRuntime`` class allows you to run Python code with ADS enhanced features like configuring the working directory and Python path. It also allows you to copy the output files to OCI Object Storage. This is especially useful for Python code involving multiple files and packages in the job artifact. - -The ``PythonRuntime`` uses an ADS generated driver script as the entry point for the job run. It performs additional operations before and after invoking your code. You can examine the driver script by downloading the job artifact from the OCI Console. - -Python ------- - -Relative to ``ScriptRunTime`` the ``PythonRuntime`` has 3 additional methods: - -* ``.with_working_dir()``: Specify the working directory to use when running a job. By default, the working directory is also added to the Python paths. This should be a relative path from the parent of the job artifact directory. -* ``.with_python_path()``: Add one or more Python paths to use when running a job. The paths should be relative paths from the working directory. -* ``.with_output()``: Specify the output directory and a remote URI (for example, an OCI Object Storage URI) in the job run. Files in the output directory are copied to the remote output URI after the job run finishes successfully. - -Following is an example of creating a job with ``PythonRuntime``: - -.. code-block:: python3 - - from ads.jobs import Job, DataScienceJOb, PythonRuntime - - job = ( - Job() - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) # Applicable only for the flexible shapes - .with_block_storage_size(50) - ) - .with_runtime( - PythonRuntime() - .with_service_conda("pytorch19_p37_cpu_v1") - # The job artifact directory is named "zip_or_dir" - .with_source("local/path/to/zip_or_dir", entrypoint="zip_or_dir/my_package/entry.py") - # Change the working directory to be inside the job artifact directory - # Working directory a relative path from the parent of the job artifact directory - # Working directory is also added to Python paths - .with_working_dir("zip_or_dir") - # Add an additional Python path - # The "my_python_packages" folder is under "zip_or_dir" (working directory) - .with_python_path("my_python_packages") - # Files in "output" directory will be copied to OCI object storage once the job finishes - # Here we assume "output" is a folder under "zip_or_dir" (working directory) - .with_output("output", "oci://bucket_name@namespace/path/to/dir") - ) +Alternatively, you can specify the working directory as ``my_source_code`` and the entrypoint as ``my_entrypoint.py``: + +.. code-block:: python + + runtime = ( + PythonRuntime() + .with_source("path/to/my_source_code") + .with_working_dir("my_source_code") + .with_entrypoint("my_entrypoint.py") ) -YAML ----- - -You could use the following YAML to create the same job with ``PythonRuntime``: - -.. code-block:: yaml - - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - runtime: - kind: runtime - type: python - spec: - conda: - slug: pytorch19_p37_cpu_v1 - type: service - entrypoint: zip_or_dir/my_package/entry.py - scriptPathURI: path/to/zip_or_dir - workingDir: zip_or_dir - outputDir: zip_or_dir/output - outputUri: oci://bucket_name@namespace/path/to/dir - pythonPath: - - "zip_or_dir/python_path" - -**PythonRuntime YAML Schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - script - spec: - required: true - type: dict - schema: - args: - nullable: true - required: false - type: list - schema: - type: string - conda: - nullable: false - required: false - type: dict - schema: - slug: - required: true - type: string - type: - allowed: - - service - required: true - type: string - env: - nullable: true - required: false - type: list - schema: - type: dict - schema: - name: - type: string - value: - type: - - number - - string - scriptPathURI: - required: true - type: string - entrypoint: - required: false - type: string - outputDir: - required: false - type: string - outputUri: - required: false - type: string - workingDir: - required: false - type: string - pythonPath: - required: false - type: list +Archive Source Code +------------------- + +If your source code is a zip/tar file, the files in the archive will be decompressed under the working directory. +The file structure in the job run depends on whether your archive has a top level directory. +For example, you can inspect the structure of your zip file by running the ``unzip -l`` command: + +.. code-block:: bash + + unzip -l my_source_code.zip + +This will give you outputs similar to the following: + +.. code-block:: text + + Archive: path/to/my_source_code.zip + Length Date Time Name + --------- ---------- ----- ---- + 0 02-22-2023 16:38 my_source_code/ + 1803 02-22-2023 16:38 my_source_code/my_module.py + 91 02-22-2023 16:38 my_source_code/my_entrypoint.py + --------- ------- + 1894 3 files + +In this case, a top level directory ``my_source_code/`` is presented in the archive. +The file structure in the job run will look like: + +.. code-block:: text + + code <---This is the working directory + └── my_source_code + ├── my_module.py + └── my_entrypoint.py + +which is the same as the case when you specified a local folder as source code. +You can configure the entrypoint and working directory similar to the examples above. + +If a top level directory is not presented, outputs for the archive will look like the following: + +.. code-block:: text + + Archive: path/to/my_source_code.zip + Length Date Time Name + --------- ---------- ----- ---- + 1803 02-22-2023 16:38 my_module.py + 91 02-22-2023 16:38 my_entrypoint.py + --------- ------- + 1894 2 files + +In this case, the file structure in the job run will look like: + +.. code-block:: text + + code <---This is the working directory + ├── my_module.py + └── my_entrypoint.py + +And, you can specify the entrypoint with the filename directly: + +.. code-block:: python + + runtime = ( + PythonRuntime() + .with_source("path/to/my_source_code.zip") + .with_entrypoint("my_entrypoint.py") + ) + +Python Paths +============ + +The working directory is added to the Python paths automatically. +You can call :py:meth:`~ads.jobs.PythonRuntime.with_python_path` to add additional python paths as needed. +The paths should be relative paths from the working directory. + +.. _runtime_outputs: + +Saving Outputs +============== +The :py:meth:`~ads.jobs.PythonRuntime.with_output` method allows you to specify the output directory ``output_dir`` +in the job run and a remote URI (for example, an OCI Object Storage URI). +Files in the ``output_dir`` are copied to the remote output URI after the job run finishes successfully. +Note that the ``output_dir`` should be a path relative to the working directory. diff --git a/docs/source/user_guide/jobs/tabs/name_substitution.rst b/docs/source/user_guide/jobs/tabs/name_substitution.rst new file mode 100644 index 000000000..f855185cd --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/name_substitution.rst @@ -0,0 +1,59 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, PythonRuntime + + job = ( + Job(name="Training on ${DATASET_NAME}") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + .with_compartment_id("") + .with_project_id("") + .with_shape_name("VM.Standard.E3.Flex") + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + ) + .with_runtime( + PythonRuntime() + .with_service_conda("pytorch19_p37_cpu_v1") + .with_environment_variable(DATASET_NAME="MyData") + .with_source("local/path/to/training_script.py") + .with_output("output", "oci://bucket_name@namespace/prefix/${JOB_RUN_OCID}") + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: Training on ${DATASET_NAME} + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + compartmentId: + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + runtime: + kind: runtime + type: python + spec: + conda: + slug: pytorch19_p37_cpu_v1 + type: service + env: + - name: DATASET_NAME + value: MyData + outputDir: output + outputUri: oci://bucket_name@namespace/prefix/${JOB_RUN_OCID} + scriptPathURI: local/path/to/training_script.py diff --git a/docs/source/user_guide/jobs/tabs/python_runtime.rst b/docs/source/user_guide/jobs/tabs/python_runtime.rst new file mode 100644 index 000000000..e7d5b74fd --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/python_runtime.rst @@ -0,0 +1,89 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, PythonRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + PythonRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Set the working directory + # When using a directory as source, the default working dir is the parent of code_dir. + # Working dir should be a relative path beginning from the source directory (code_dir) + .with_working_dir("code_dir") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.py is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.py") + # Add an additional Python path, relative to the working dir (code_dir/other_packages). + .with_python_path("other_packages") + # Copy files in "code_dir/output" to object storage after job finishes. + .with_output("output", "oci://bucket_name@namespace/path/to/dir") + ) + ) + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: python + spec: + conda: + slug: pytorch19_p37_cpu_v1 + type: service + entrypoint: my_package/my_script.py + outputDir: output + outputUri: oci://bucket_name@namespace/path/to/dir + pythonPath: + - other_packages + scriptPathURI: local/path/to/code_dir + workingDir: code_dir \ No newline at end of file From 626d1b5cb05fdce5e71ef541027451925ce9a8a7 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 23 Feb 2023 11:49:19 -0500 Subject: [PATCH 053/147] Update run_python.rst --- docs/source/user_guide/jobs/run_python.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index 0c1d93703..23f442fa4 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -29,7 +29,9 @@ For example, you can specify files on OCI object storage using URI like The source code can be a single file, a compressed file/archive (zip/tar), or a folder. When the source code is a compressed file/archive (zip/tar) or a folder, you need to also specify the entrypoint using :py:meth:`~ads.jobs.PythonRuntime.with_entrypoint`. The path of the entrypoint should be a path relative to -the working directory. +the working directory. + +The entrypoint can be a Python script or a Jupyter Notebook. Working Directory ================= @@ -105,10 +107,10 @@ This will give you outputs similar to the following: Length Date Time Name --------- ---------- ----- ---- 0 02-22-2023 16:38 my_source_code/ - 1803 02-22-2023 16:38 my_source_code/my_module.py - 91 02-22-2023 16:38 my_source_code/my_entrypoint.py + 1803 02-22-2023 16:38 my_source_code/my_module.py + 91 02-22-2023 16:38 my_source_code/my_entrypoint.py --------- ------- - 1894 3 files + 1894 3 files In this case, a top level directory ``my_source_code/`` is presented in the archive. The file structure in the job run will look like: @@ -130,10 +132,10 @@ If a top level directory is not presented, outputs for the archive will look lik Archive: path/to/my_source_code.zip Length Date Time Name --------- ---------- ----- ---- - 1803 02-22-2023 16:38 my_module.py - 91 02-22-2023 16:38 my_entrypoint.py + 1803 02-22-2023 16:38 my_module.py + 91 02-22-2023 16:38 my_entrypoint.py --------- ------- - 1894 2 files + 1894 2 files In this case, the file structure in the job run will look like: @@ -166,6 +168,6 @@ Saving Outputs ============== The :py:meth:`~ads.jobs.PythonRuntime.with_output` method allows you to specify the output directory ``output_dir`` -in the job run and a remote URI (for example, an OCI Object Storage URI). +in the job run and a remote URI (``output_uri``, for example, an OCI Object Storage URI). Files in the ``output_dir`` are copied to the remote output URI after the job run finishes successfully. Note that the ``output_dir`` should be a path relative to the working directory. From 413a3a95e10a481129481b73d5780fcd47ab4dbf Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 23 Feb 2023 11:49:34 -0500 Subject: [PATCH 054/147] Update run_notebook.rst --- docs/source/user_guide/jobs/run_notebook.rst | 173 ++++-------------- .../user_guide/jobs/tabs/notebook_runtime.rst | 85 +++++++++ 2 files changed, 121 insertions(+), 137 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/notebook_runtime.rst diff --git a/docs/source/user_guide/jobs/run_notebook.rst b/docs/source/user_guide/jobs/run_notebook.rst index 256e77e11..72dc40e71 100644 --- a/docs/source/user_guide/jobs/run_notebook.rst +++ b/docs/source/user_guide/jobs/run_notebook.rst @@ -1,154 +1,53 @@ -.. _job_run_a_notebook: - Run a Notebook ************** -In some cases, you may want to run an existing JupyterLab notebook as a job. You can do this using the ``NotebookRuntime()`` object. - -The next example shows you how to run an the `TensorFlow 2 quick start for beginner `__ notebook from the internet and save the results to OCI Object Storage. The notebook path points to the raw file link from GitHub. To run the following example, ensure that you have internet access to retrieve the notebook: - -Python -====== - -.. code-block:: python3 - - from ads.jobs import Job, DataScienceJob, NotebookRuntime - - job = ( - Job() - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) # Applicable only for the flexible shapes - .with_block_storage_size(50) - ) - .with_runtime( - NotebookRuntime() - .with_notebook( - path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - encoding='utf-8' - ) - .with_service_conda("tensorflow28_p38_cpu_v1") - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_output("oci://bucket_name@namespace/path/to/dir") - ) - ) - - job.create() - run = job.run().watch() - -After the notebook finishes running, the notebook with results are saved to ``oci://bucket_name@namespace/path/to/dir``. You can download the output by calling the ``download()`` method. +The :py:class:`~ads.jobs.NotebookRuntime` allows you to run a single Jupyter notebook as a job. -.. code-block:: python3 +If your notebook needs extra dependencies like custom module or data files, you can use +:py:class:`~ads.jobs.PythonRuntime` or :py:class:`~ads.jobs.GitPythonRuntime` and set your notebook as the entrypoint. - run.download("/path/to/local/dir") +See also: -The ``NotebookRuntime`` also allows you to use exclusion tags, which lets you exclude cells from a job run. For example, you could use these tags to do exploratory data analysis, and then train and evaluate your model in a notebook. Then you could use that same notebook to only build future models that are trained on a different dataset. So the job run only has to execute the cells that are related to training the model, and not the exploratory data analysis or model evaluation. +* :doc:`run_python` +* :doc:`run_git` -You tag the cells in the notebook, and then specify the tags using the ``.with_exclude_tag()`` method. Cells with any matching tags are excluded from the job run. For example, if you tagged cells with ``ignore`` and ``remove``, you can pass in a list of the two tags to the method and those cells are excluded from the code that is executed as part of the job run. To tag cells in a notebook, see `Adding tags using notebook interfaces `__. +TensorFlow Example +================== -.. code-block:: python3 +The following example shows you how to run an the +`TensorFlow 2 quick start for beginner +`_ +notebook from the internet and save the results to OCI Object Storage. +The notebook path points to the raw file link from GitHub. +To run the example, ensure that you have internet access to retrieve the notebook: - job.with_runtime( - NotebookRuntime() - .with_notebook("path/to/notebook") - .with_exclude_tag(["ignore", "remove"]) - ) +.. include:: ../jobs/tabs/notebook_runtime.rst -YAML -==== +Working Directory +================= -You could use the following YAML to create the job: +An empty directory in the job run will be created as the working directory for running the notebook. +All relative paths used in the notebook will be base on the working directory. -.. code-block:: yaml +Download the Outputs +==================== - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - jobInfrastructureType: STANDALONE - jobType: DEFAULT - logGroupId: - logId: - runtime: - kind: runtime - type: notebook - spec: - notebookPathURI: /path/to/notebook - conda: - slug: tensorflow28_p38_cpu_v1 - type: service +If you specify the output location using :py:meth:`~ads.jobs.NotebookRuntime.with_output`. +All files in the working directory, including the notebook with outputs, +will be saved to output location (``oci://bucket_name@namespace/path/to/dir``) after the job finishes running. +You can download the output by calling the :py:meth:`~ads.jobs.NotebookRuntime.download` method. -**NotebookRuntime Schema** +Exclude Cells +============= -.. code-block:: yaml +The :py:class:`~ads.jobs.NotebookRuntime` also allows you to specify tags to exclude cells from being processed +in a job run using :py:meth:`~ads.jobs.NotebookRuntime.with_exclude_tag` method. +For example, you could do exploratory data analysis and visualization in a notebook, +and you may want to exclude the visualization when running the notebook in a job. - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - notebook - spec: - required: true - type: dict - schema: - excludeTags: - required: false - type: list - notebookPathURI: - required: false - type: string - notebookEncoding: - required: false - type: string - outputUri: - required: false - type: string - args: - nullable: true - required: false - type: list - schema: - type: string - conda: - nullable: false - required: false - type: dict - schema: - slug: - required: true - type: string - type: - required: true - type: string - allowed: - - service - env: - nullable: true - required: false - type: list - schema: - type: dict - schema: - name: - type: string - value: - type: - - number - - string +To tag cells in a notebook, see +`Adding tags using notebook interfaces `__. +The :py:meth:`~ads.jobs.NotebookRuntime.with_exclude_tag` take a list of tags as argument +Cells with any matching tags are excluded from the job run. +In the above example, cells with ``ignore`` or ``remove`` are excluded. diff --git a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst new file mode 100644 index 000000000..ac47ff3dc --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst @@ -0,0 +1,85 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, NotebookRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow28_p38_cpu_v1") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_exclude_tag(["ignore", "remove"]) + .with_output("oci://bucket_name@namespace/path/to/dir") + ) + ) + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + # Download the notebook back to local + run.download("/path/to/local/dir") + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: notebook + spec: + conda: + slug: tensorflow28_p38_cpu_v1 + type: service + env: + - name: GREETINGS + value: Welcome to OCI Data Science + excludeTags: + - ignore + - remove + notebookEncoding: utf-8 + notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb + outputUri: oci://bucket_name@namespace/path/to/dir \ No newline at end of file From 72bf41305e4349d302bfda1f19dc322d0bc66f6a Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 23 Feb 2023 17:45:35 -0500 Subject: [PATCH 055/147] Update overview.rst, data_science_job.rst, and 11 more files... --- .../user_guide/jobs/components/overview.rst | 2 +- .../user_guide/jobs/data_science_job.rst | 6 +- docs/source/user_guide/jobs/run_container.rst | 135 +------ docs/source/user_guide/jobs/run_git.rst | 263 ++++---------- docs/source/user_guide/jobs/run_python.rst | 22 +- docs/source/user_guide/jobs/run_script.rst | 340 +----------------- docs/source/user_guide/jobs/runtime.rst | 6 + .../jobs/tabs/container_runtime.rst | 75 ++++ .../user_guide/jobs/tabs/git_runtime.rst | 86 +++++ .../user_guide/jobs/tabs/git_runtime_args.rst | 39 ++ .../user_guide/jobs/tabs/script_runtime.rst | 76 ++++ .../user_guide/jobs/tabs/training_mnist.rst | 78 ++++ .../model_training/training_with_oci.rst | 14 +- 13 files changed, 501 insertions(+), 641 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/container_runtime.rst create mode 100644 docs/source/user_guide/jobs/tabs/git_runtime.rst create mode 100644 docs/source/user_guide/jobs/tabs/git_runtime_args.rst create mode 100644 docs/source/user_guide/jobs/tabs/script_runtime.rst create mode 100644 docs/source/user_guide/jobs/tabs/training_mnist.rst diff --git a/docs/source/user_guide/jobs/components/overview.rst b/docs/source/user_guide/jobs/components/overview.rst index ceacecc1b..df69d5975 100644 --- a/docs/source/user_guide/jobs/components/overview.rst +++ b/docs/source/user_guide/jobs/components/overview.rst @@ -23,7 +23,7 @@ For example, you may want to experiment with how different model classes perform by using the ADSTuner to perform hyperparameter tuning on each model class. You could do this in parallel by having a different job run for each class of models. For a given job run, you could pass an environment variable that identifies the model class that you want to use. -Each model cab write its results to the Logging service or Object Storage. +Each model can write its results to the Logging service or Object Storage. Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. The following sections provides details on running training workloads with OCI Data Science Jobs using ADS Jobs APIs. diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 6c4bfe3af..24074a30f 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -155,4 +155,8 @@ and ``output_uri`` based on the environment variables ``JOB_RUN_OCID``: Note that ``JOB_RUN_OCID`` is an environment variable provided by the service after the job run is created. It is available for the ``output_uri`` but cannot be used in the job name. -See also :ref:`Saving Outputs <_runtime_outputs>` + +See also: + +* :ref:`Saving Outputs ` +* `Service Provided Environment Variables `_ diff --git a/docs/source/user_guide/jobs/run_container.rst b/docs/source/user_guide/jobs/run_container.rst index 8de7d1bb8..6bf15faab 100644 --- a/docs/source/user_guide/jobs/run_container.rst +++ b/docs/source/user_guide/jobs/run_container.rst @@ -1,129 +1,26 @@ Run a Container *************** -The ADS ``ContainerRuntime`` class allows you to run a container image using OCI data science jobs. +The :py:class:`~ads.jobs.ContainerRuntime` class allows you to run a container image using OCI data science jobs. -To use the ``ContainerRuntime``, you need to first push the image to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. +.. admonition:: OCI Container Registry -Python -====== + To use the :py:class:`~ads.jobs.ContainerRuntime`, you need to first push the image to + `OCI container registry `_. + For more details, see: + + * `Creating a Repository `_ + * `Pushing Images Using the Docker CLI `_. -To configure ``ContainerRuntime``, you must specify the container ``image``. Similar to other runtime, you can add environment variables. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). +Here is an example to create and run a container job: -.. code-block:: python3 +.. include:: ../jobs/tabs/container_runtime.rst - from ads.jobs import Job, DataScienceJob, ContainerRuntime +To configure ``ContainerRuntime``, you must specify the container ``image``. +Similar to other runtime, you can add environment variables. +You can optionally specify the `entrypoint` and `cmd` for running the container. - job = ( - Job() - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) # Applicable only for the flexible shapes - .with_block_storage_size(50) - ) - .with_runtime( - ContainerRuntime() - .with_image(".ocir.io//") - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_entrypoint(["/bin/sh", "-c"]) - .with_cmd("sleep 5 && echo $GREETINGS") - ) - ) - - # Create the job with OCI - job.create() - # Run the job and stream the outputs - job_run = job.run().watch() - -YAML -==== - -You could use the following YAML to create the same job: - -.. code-block:: yaml - - kind: job - spec: - name: container-job - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - runtime: - kind: runtime - type: container - spec: - image: iad.ocir.io// - cmd: - - sleep 5 && echo $GREETINGS - entrypoint: - - /bin/sh - - -c - env: - - name: GREETINGS - value: Welcome to OCI Data Science - -**ContainerRuntime Schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - container - spec: - type: dict - required: true - schema: - image: - required: true - type: string - entrypoint: - required: false - type: - - string - - list - cmd: - required: false - type: - - string - - list - env: - nullable: true - required: false - type: list - schema: - type: dict - schema: - name: - type: string - value: - type: - - number - - string +See also: +* `Understand how CMD and ENTRYPOINT interact `_ +* `Bring Your Own Container `_ diff --git a/docs/source/user_guide/jobs/run_git.rst b/docs/source/user_guide/jobs/run_git.rst index 80475f6dd..7be2f968a 100644 --- a/docs/source/user_guide/jobs/run_git.rst +++ b/docs/source/user_guide/jobs/run_git.rst @@ -1,78 +1,88 @@ -.. _job_run_git: +Run Code from Git Repo +********************** -Run a Git Repo -************** +The :py:class:`~ads.jobs.GitPythonRuntime` allows you to run source code from a Git repository as a job. -The ADS ``GitPythonRuntime`` class allows you to run source code from a Git repository as a Data Science job. The next example shows how to run a -`PyTorch Neural Network Example to train third order polynomial predicting y=sin(x) `__. +The following example shows how to run a +`PyTorch Neural Network Example to train third order polynomial predicting y=sin(x) +`_. -Python -====== +.. include:: ../jobs/tabs/git_runtime.rst -To configure the ``GitPythonRuntime``, you must specify the source code ``url`` and entrypoint ``path``. Similar to ``PythonRuntime``, you can specify a service conda environment, environment variables, and CLI arguments. In this example, the ``pytorch19_p37_gpu_v1`` service conda environment is used. Assuming you are running this example in an Data Science notebook session, only log ID and log group ID need to be configured for the ``DataScienceJob`` object, see `Data Science Jobs `__ for more details about configuring the infrastructure. +Git Repository +============== -.. code-block:: python3 +To configure the :py:class:`~ads.jobs.GitPythonRuntime`, you must specify the source code ``url`` and the entrypoint. +The default branch from the Git repository is used unless you specify a different ``branch`` or ``commit`` +in the :py:meth:`~ads.jobs.GitPythonRuntime.with_source` method. + +For a public repository, we recommend the "http://" or "https://" URL. +Authentication may be required for the SSH URL even if the repository is public. + +To use a private repository, you must first save an SSH key to +`OCI Vault `_ as a secret, +and provide the ``secret_ocid`` when calling :py:meth:`~ads.jobs.GitPythonRuntime.with_source`. +For more information about creating and using secrets, +see `Managing Secret with Vault `_. +For repository on GitHub, you could setup the +`GitHub Deploy Key `_ as secret. + +.. admonition:: Git Version for Private Repository + + Git version of 2.3+ is required to use a private repository. + + +Entrypoint +========== + +The entrypoint specifies how the source code is invoked. +The :py:meth:`~ads.jobs.GitPythonRuntime.with_entrypoint` supports the following arguments: - from ads.jobs import Job, DataScienceJob, GitPythonRuntime - - job = ( - Job() - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) # Applicable only for the flexible shapes - .with_block_storage_size(50) - ) - .with_runtime( - GitPythonRuntime() - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_service_conda("pytorch19_p37_gpu_v1") - .with_source("https://github.com/pytorch/tutorials.git") - .with_entrypoint("beginner_source/examples_nn/polynomial_nn.py") - .with_output( - output_dir="~/Code/tutorials/beginner_source/examples_nn", - output_uri="oci://BUCKET_NAME@BUCKET_NAMESPACE/PREFIX" - ) - ) - ) - - # Create the job with OCI - job.create() - # Run the job and stream the outputs - job_run = job.run().watch() - - -The default branch from the Git repository is used unless you specify a different ``branch`` or ``commit`` in the ``.with_source()`` method. - -For a public repository, we recommend the "http://" or "https://" URL. Authentication may be required for the SSH URL even if the repository is -public. - -To use a private repository, you must first save an SSH key to an `OCI Vault `__ as a secret, and provide the ``secret_ocid`` to the ``with_source()`` method, see `Managing Secret with Vault `__. For example, you could use `GitHub Deploy -Key `__. - -The entry point specifies how the source code is invoked. The ``.with_entrypiont()`` has the following arguments: - -* ``func``: Optional. The function in the script specified by ``path`` to call. If you don't specify it, then the script specified by ``path`` is run as a Python script in a subprocess. * ``path``: Required. The relative path for the script, module, or file to start the job. +* ``func``: Optional. The function in the script specified by ``path`` to call. + If you don't specify it, then the script specified by ``path`` is run as a Python script in a subprocess. + +The arguments for the entrypoint can be specified through :py:meth:`~ads.jobs.GitPythonRuntime.with_argument`. +For running a script, the arguments are passed in as command line arguments. +See :ref:`Runtime Command Line Arguments ` for more details. +For running a function, the arguments are passed into the function call. -With the ``GitPythonRuntime`` class, you can save the output files from the job run to Object Storage using ``with_output()``. By default, the source code is cloned to the ``~/Code`` directory. In the example, the files in the ``example_nn`` directory are copied to the Object Storage specified by the ``output_uri`` parameter. The ``output_uri`` parameter should have this format: +The following example shows how you can define a runtime using Python function from a git repository as an entrypoint. +Here ``my_function`` is a function in the ``my_source/my_module.py`` module. -``oci://BUCKET_NAME@BUCKET_NAMESPACE/PREFIX`` +.. include:: ../jobs/tabs/git_runtime_args.rst -The ``GitPythonRuntime`` also supports these additional configurations: +The function will be called as ``my_function("arg1", "arg2", key1="val1", key2="val2")``. -* The ``.with_python_path()`` method allows you to add additional Python paths to the runtime. By default, the code directory checked out from Git is added to ``sys.path``. Additional Python paths are appended before the code directory is appended. -* The ``.with_argument()`` method allows you to pass arguments to invoke the script or function. For running a script, the arguments are passed in as CLI arguments. For running a function, the ``list`` and ``dict`` JSON serializable objects are supported and are passed into the function. +The arguments can be strings, ``list`` of strings or ``dict`` containing only strings. -The ``GitPythonRuntime`` method updates metadata in the free form tags of the job run after the job run finishes. The following tags are added automatically: +:py:class:`~ads.jobs.GitPythonRuntime` also support Jupyter notebook as entrypoint. +Arguments are not used when the entrypoint is a notebook. + + +Python Paths +============ + +The working directory is the root of the git repository. +The working directory is added to the Python paths automatically. +You can call :py:meth:`~ads.jobs.GitPythonRuntime.with_python_path` to add additional python paths as needed. +The paths should be relative paths from the working directory. + +Outputs +======= + +The :py:meth:`~ads.jobs.GitPythonRuntime.with_output` method allows you to specify the output path ``output_path`` +in the job run and a remote URI (``output_uri``). +Files in the ``output_path`` are copied to the remote output URI after the job run finishes successfully. +Note that the ``output_path`` should be a path relative to the working directory. + +OCI object storage location can be specified in the format of ``oci://bucket_name@namespace/path/to/dir``. +Please make sure you configure the I AM policy to allow the job run dynamic group to use object storage. + +Metadata +======== +The :py:class:`~ads.jobs.GitPythonRuntime` updates metadata as free-form tags of the job run +after the job run finishes. The following tags are added automatically: * ``commit``: The Git commit ID. * ``method``: The entry function or method. @@ -80,132 +90,9 @@ The ``GitPythonRuntime`` method updates metadata in the free form tags of the jo * ``outputs``: The prefix of the output files in Object Storage. * ``repo``: The URL of the Git repository. -The new values overwrite any existing tags. If you want to skip the metadata update, set ``skip_metadata_update`` to ``True`` when initializing the runtime: +The new values overwrite any existing tags. +If you want to skip the metadata update, set ``skip_metadata_update`` to ``True`` when initializing the runtime: .. code-block:: python3 runtime = GitPythonRuntime(skip_metadata_update=True) - -YAML -==== - -You could create the preceding example job with the following YAML file: - -.. code-block:: yaml - - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - name: git_example - runtime: - kind: runtime - type: gitPython - spec: - entrypoint: beginner_source/examples_nn/polynomial_nn.py - outputDir: ~/Code/tutorials/beginner_source/examples_nn - outputUri: oci://BUCKET_NAME@BUCKET_NAMESPACE/PREFIX - url: https://github.com/pytorch/tutorials.git - conda: - slug: pytorch19_p37_gpu_v1 - type: service - env: - - name: GREETINGS - value: Welcome to OCI Data Science - -**GitPythonRuntime YAML Schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - gitPython - spec: - required: true - type: dict - schema: - args: - type: list - nullable: true - required: false - schema: - type: string - branch: - nullable: true - required: false - type: string - commit: - nullable: true - required: false - type: string - codeDir: - required: false - type: string - conda: - nullable: false - required: false - type: dict - schema: - slug: - required: true - type: string - type: - required: true - type: string - allowed: - - service - entryFunction: - nullable: true - required: false - type: string - entrypoint: - required: false - type: - - string - - list - env: - nullable: true - required: false - type: list - schema: - type: dict - schema: - name: - type: string - value: - type: - - number - - string - outputDir: - required: false - type: string - outputUri: - required: false - type: string - pythonPath: - nullable: true - required: false - type: list - url: - required: false - type: string - diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index 23f442fa4..7e6c4d050 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -33,6 +33,8 @@ the working directory. The entrypoint can be a Python script or a Jupyter Notebook. +.. _runtime_working_dir: + Working Directory ================= @@ -41,6 +43,11 @@ By default, :py:class:`~ads.jobs.PythonRuntime` will create a ``code`` directory in the job run to store your source code (job artifact), for example ``/home/datascience/decompressed_artifact/code``. +When the entrypoint is a Jupyter notebook, +the working directory for the code running in the notebook will be the directory containing the notebook. + +When the entrypoint is not a notebook, the working directory depends on the source code. + File Source Code ---------------- @@ -164,10 +171,13 @@ The paths should be relative paths from the working directory. .. _runtime_outputs: -Saving Outputs -============== +Outputs +======= + +The :py:meth:`~ads.jobs.PythonRuntime.with_output` method allows you to specify the output path ``output_path`` +in the job run and a remote URI (``output_uri``). +Files in the ``output_path`` are copied to the remote output URI after the job run finishes successfully. +Note that the ``output_path`` should be a path relative to the working directory. -The :py:meth:`~ads.jobs.PythonRuntime.with_output` method allows you to specify the output directory ``output_dir`` -in the job run and a remote URI (``output_uri``, for example, an OCI Object Storage URI). -Files in the ``output_dir`` are copied to the remote output URI after the job run finishes successfully. -Note that the ``output_dir`` should be a path relative to the working directory. +OCI object storage location can be specified in the format of ``oci://bucket_name@namespace/path/to/dir``. +Please make sure you configure the I AM policy to allow the job run dynamic group to use object storage. diff --git a/docs/source/user_guide/jobs/run_script.rst b/docs/source/user_guide/jobs/run_script.rst index b570db370..ee053f9a3 100644 --- a/docs/source/user_guide/jobs/run_script.rst +++ b/docs/source/user_guide/jobs/run_script.rst @@ -1,334 +1,26 @@ -.. _job_run_script: - Run a Script ************ -This example shows you how to create a job running "Hello World" Python scripts. Although Python scripts are used here, you could also run Bash or Shell scripts. The Logging service log and log group are defined in the infrastructure. The output of the script appear in the logs. - -Python -====== - -Suppose you would like to run the following "Hello World" python script named ``job_script.py``. - -.. code-block:: python3 - - print("Hello World") - -First, initiate a job with a job name: - -.. code-block:: python3 - - from ads.jobs import Job - job = Job(name="Job Name") - -Next, you specify the desired infrastructure to run the job. If you are in a notebook session, ADS can automatically fetch the infrastructure configurations and use them for the job. If you aren't in a notebook session or you want to customize the infrastructure, you can specify them using the methods from the ``DataScienceJob`` class: - -.. code-block:: python3 - - from ads.jobs import DataScienceJob - - job.with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) # Applicable only for the flexible shapes - .with_block_storage_size(50) - ) - -In this example, it is a Python script so the ``ScriptRuntime()`` class is used to define the name of the script using the ``.with_source()`` method: - -.. code-block:: python3 - - from ads.jobs import ScriptRuntime - job.with_runtime( - ScriptRuntime().with_source("job_script.py") - ) - -Finally, you create and run the job, which gives you access to the -``job_run.id``: - -.. code-block:: python3 - - job.create() - job_run = job.run() - -Additionally, you can acquire the job run using the OCID: - -.. code-block:: python3 - - from ads.jobs import DataScienceJobRun - job_run = DataScienceJobRun.from_ocid(job_run.id) - -The ``.watch()`` method is useful to monitor the progress of the job run: - -.. code-block:: python3 - - job_run.watch() - -After the job has been created and runs successfully, you can find -the output of the script in the logs if you configured logging. - -YAML -==== - -You could also initialize a job directly from a YAML string. For example, to create a job identical to the preceding example, you could simply run the following: - -.. code-block:: python3 - - job = Job.from_string(f""" - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - name: - runtime: - kind: runtime - type: python - spec: - scriptPathURI: job_script.py - """) - - -Command Line Arguments -====================== - -If the Python script that you want to run as a job requires CLI arguments, -use the ``.with_argument()`` method to pass the arguments to the job. - -Python ------- - -Suppose you want to run the following python script named ``job_script_argument.py``: - -.. code-block:: python3 - - import sys - print("Hello " + str(sys.argv[1]) + " and " + str(sys.argv[2])) - -This example runs a job with CLI arguments: - -.. code-block:: python3 - - job = Job() - job.with_infrastructure( - DataScienceJob() - .with_log_id("") - .with_log_group_id("") - ) - - # The CLI argument can be passed in using `with_argument` when defining the runtime - job.with_runtime( - ScriptRuntime() - .with_source("job_script_argument.py") - .with_argument("", "") - ) - - job.create() - job_run = job.run() - -After the job run is created and run, you can use the ``.watch()`` method to monitor -its progress: - -.. code-block:: python3 - - job_run.watch() - -This job run prints out ``Hello and ``. - -YAML ----- - -You could create the preceding example job with the following YAML file: - -.. code-block:: yaml - - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - runtime: - kind: runtime - type: python - spec: - args: - - - - - scriptPathURI: job_script_argument.py - - -Environment Variables -===================== - -Similarly, if the script you want to run requires environment variables, you also pass them in using the ``.with_environment_variable()`` method. The key-value pair of the environment variable are passed in using the ``.with_environment_variable()`` method, and are accessed in the Python script using the ``os.environ`` dictionary. - -Python ------- - -Suppose you want to run the following python script named ``job_script_env.py``: - -.. code-block:: python3 - - import os - import sys - print("Hello " + os.environ["KEY1"] + " and " + os.environ["KEY2"])""") - -This example runs a job with environment variables: - -.. code-block:: python3 - - job = Job() - job.with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - - job.with_runtime( - ScriptRuntime() - .with_source("job_script_env.py") - .with_environment_variable(KEY1="", KEY2="") - ) - job.create() - job_run = job.run() - -You can watch the progress of the job run using the ``.watch()`` method: - -.. code-block:: python3 - - job_run.watch() - -This job run prints out ``Hello and ``. - -YAML ----- - -You could create the preceding example job with the following YAML file: - -.. code-block:: yaml +This section shows how to create a job to run a script. - kind: job - spec: - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - logGroupId: - logId: - compartmentId: - projectId: - subnetId: - shapeName: VM.Standard.E3.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - blockStorageSize: 50 - runtime: - kind: runtime - type: python - spec: - env: - - name: KEY1 - value: - - name: KEY2 - value: - scriptPathURI: job_script_env.py +The :py:class:`~ads.jobs.ScriptRuntime` is designed for you to define job artifacts and configurations supported by OCI +Data Science jobs natively. It can be used with any script types that is supported by the OCI Data Science jobs, +including shell scripts and python scripts. +The source code can be a single script, files in a folder or a zip/tar file. +See also: `Preparing Job Artifacts `_. -**ScriptRuntime YAML Schema** +Here is an example: -.. code-block:: yaml +.. include:: ../jobs/tabs/script_runtime.rst - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - script - spec: - required: true - type: dict - schema: - args: - nullable: true - required: false - type: list - schema: - type: string - conda: - nullable: false - required: false - type: dict - schema: - slug: - required: true - type: string - type: - allowed: - - service - required: true - type: string - env: - nullable: true - required: false - type: list - schema: - type: dict - schema: - name: - type: string - value: - type: - - number - - string - scriptPathURI: - required: true - type: string - entrypoint: - required: false - type: string +Working Directory +================= +The working directory is the parent directory where the job artifacts are decompressed, +for example ``/home/datascience/decompressed_artifact/``. +When the source code is a compressed file/archive (zip/tar) or a folder, you need to also specify the entrypoint +using :py:meth:`~ads.jobs.ScriptRuntime.with_entrypoint`. The path of the entrypoint should be a path relative to +the working directory. Note that this directory cannot be changed when using :py:class:`~ads.jobs.ScriptRuntime`. +See :ref:`Python Runtime Working Directory ` for more details. diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst index 475d62ffb..fd20212fa 100644 --- a/docs/source/user_guide/jobs/runtime.rst +++ b/docs/source/user_guide/jobs/runtime.rst @@ -57,6 +57,12 @@ Note that: * Undefined variable enclosed by ``${...}`` will be ignored. * Double dollar signs ``$$`` will be substituted by a single one ``$``. +See also: +`Service Provided Environment Variables `_ + + +.. _runtime_args: + Command Line Arguments ====================== diff --git a/docs/source/user_guide/jobs/tabs/container_runtime.rst b/docs/source/user_guide/jobs/tabs/container_runtime.rst new file mode 100644 index 000000000..3f702f1ca --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/container_runtime.rst @@ -0,0 +1,75 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, ContainerRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + ContainerRuntime() + .with_image(".ocir.io//") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_entrypoint(["/bin/sh", "-c"]) + .with_cmd("sleep 5 && echo $GREETINGS") + ) + ) + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: container + spec: + cmd: sleep 5 && echo $GREETINGS + entrypoint: + - /bin/sh + - -c + env: + - name: GREETINGS + value: Welcome to OCI Data Science + image: .ocir.io// \ No newline at end of file diff --git a/docs/source/user_guide/jobs/tabs/git_runtime.rst b/docs/source/user_guide/jobs/tabs/git_runtime.rst new file mode 100644 index 000000000..90a9e87a2 --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/git_runtime.rst @@ -0,0 +1,86 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, GitPythonRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + GitPythonRuntime() + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_gpu_v1") + # Specify the git repository + # Optionally, you can specify the branch or commit + .with_source("https://github.com/pytorch/tutorials.git") + # Entrypoint is a relative path from the root of the git repo. + .with_entrypoint("beginner_source/examples_nn/polynomial_nn.py") + # Copy files in "beginner_source/examples_nn" to object storage after job finishes. + .with_output( + output_dir="beginner_source/examples_nn", + output_uri="oci://bucket_name@namespace/path/to/dir" + ) + ) + ) + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: gitPython + spec: + conda: + slug: pytorch19_p37_gpu_v1 + type: service + entrypoint: beginner_source/examples_nn/polynomial_nn.py + env: + - name: GREETINGS + value: Welcome to OCI Data Science + outputDir: beginner_source/examples_nn + outputUri: oci://bucket_name@namespace/path/to/dir + url: https://github.com/pytorch/tutorials.git \ No newline at end of file diff --git a/docs/source/user_guide/jobs/tabs/git_runtime_args.rst b/docs/source/user_guide/jobs/tabs/git_runtime_args.rst new file mode 100644 index 000000000..3b7c1eb13 --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/git_runtime_args.rst @@ -0,0 +1,39 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + runtime = ( + GitPythonRuntime() + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_gpu_v1") + # Specify the git repository + .with_source("https://example.com/your_repository.git") + # Entrypoint is a relative path from the root of the git repo. + .with_entrypoint("my_source/my_module.py", func="my_function") + .with_argument("arg1", "arg2", key1="val1", key2="val2") + ) + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: gitPython + spec: + args: + - arg1 + - arg2 + - --key1 + - val1 + - --key2 + - val2 + conda: + slug: pytorch19_p37_gpu_v1 + type: service + entryFunction: my_function + entrypoint: my_source/my_module.py + env: + - name: GREETINGS + value: Welcome to OCI Data Science + url: https://example.com/your_repository.git diff --git a/docs/source/user_guide/jobs/tabs/script_runtime.rst b/docs/source/user_guide/jobs/tabs/script_runtime.rst new file mode 100644 index 000000000..41816f4df --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/script_runtime.rst @@ -0,0 +1,76 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, ScriptRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + ScriptRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.sh is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.sh") + ) + ) + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: script + spec: + conda: + slug: pytorch19_p37_cpu_v1 + type: service + entrypoint: my_package/my_script.sh + scriptPathURI: local/path/to/code_dir diff --git a/docs/source/user_guide/jobs/tabs/training_mnist.rst b/docs/source/user_guide/jobs/tabs/training_mnist.rst new file mode 100644 index 000000000..a60799d6e --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/training_mnist.rst @@ -0,0 +1,78 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, GitPythonRuntime + + job = ( + Job(name="Training MNIST with PyTorch") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + .with_shape_name("VM.GPU3.1") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + .with_block_storage_size(50) + ) + .with_runtime( + GitPythonRuntime(skip_metadata_update=True) + .with_source(url="https://github.com/pytorch/examples.git", branch="main") + .with_entrypoint(path="mnist/main.py") + .with_service_conda("pytorch110_p38_gpu_v1") + # Pass the arguments as: main.py --epochs 10 --save-model + .with_argument(**{"epochs": 10, "save-model": None}) + .with_output( + "mnist_rnn.pt", + output_uri="oci://bucket_name@namespace/path/to/dir" + ) + ) + ) + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: gitPython + spec: + conda: + slug: pytorch19_p37_gpu_v1 + type: service + entrypoint: beginner_source/examples_nn/polynomial_nn.py + env: + - name: GREETINGS + value: Welcome to OCI Data Science + outputDir: beginner_source/examples_nn + outputUri: oci://bucket_name@namespace/path/to/dir + url: https://github.com/pytorch/tutorials.git \ No newline at end of file diff --git a/docs/source/user_guide/model_training/training_with_oci.rst b/docs/source/user_guide/model_training/training_with_oci.rst index 9975adefb..f924fe9ce 100644 --- a/docs/source/user_guide/model_training/training_with_oci.rst +++ b/docs/source/user_guide/model_training/training_with_oci.rst @@ -2,6 +2,16 @@ Training with OCI ################# -.. include:: ../jobs/components/overview.rst +Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `_ +enables you to define and run repeatable machine learning tasks on a fully managed infrastructure. +You can have Compute resource on demand and run applications that perform tasks such as +data preparation, model training, hyperparameter tuning, and batch inference. -.. include:: ../jobs/toc.rst +Here is an example for training MNIST model with PyTorch using source code directly from GitHub. + +.. include:: ../jobs/tabs/training_mnist.rst + +For more details, see: + +* :doc:`../jobs/index` +* :doc:`../jobs/run_git` From 0ad0482e5f417eb3110381462980e51d9c7bce45 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Fri, 24 Feb 2023 10:23:08 -0500 Subject: [PATCH 056/147] Update docs based on comments. --- docs/source/user_guide/cli/opctl/_template/jobs.rst | 6 +++--- docs/source/user_guide/jobs/infrastructure.rst | 6 +++--- docs/source/user_guide/jobs/policies.rst | 4 ++-- docs/source/user_guide/jobs/tabs/name_substitution.rst | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/user_guide/cli/opctl/_template/jobs.rst b/docs/source/user_guide/cli/opctl/_template/jobs.rst index 457008bba..23aca5965 100644 --- a/docs/source/user_guide/cli/opctl/_template/jobs.rst +++ b/docs/source/user_guide/cli/opctl/_template/jobs.rst @@ -1,6 +1,6 @@ -++++++++++++++++ -Working with CLI -++++++++++++++++ +++++++++++++++++++++ +Working with the CLI +++++++++++++++++++++ Prerequisite ------------ diff --git a/docs/source/user_guide/jobs/infrastructure.rst b/docs/source/user_guide/jobs/infrastructure.rst index ddc8c9c8d..ab17834e6 100644 --- a/docs/source/user_guide/jobs/infrastructure.rst +++ b/docs/source/user_guide/jobs/infrastructure.rst @@ -16,7 +16,7 @@ For example: .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) - # Minimum block storage size is 50 + # Minimum block storage size is 50 (GB) .with_block_storage_size(50) .with_log_group_id("") .with_log_id("") @@ -65,7 +65,7 @@ The :py:class:`~ads.jobs.DataScienceJob` class provides two static methods to ob * You can get a list of currently supported compute shapes by calling :py:meth:`~ads.jobs.DataScienceJob.instance_shapes`. -* can get a list of shapes are available for fast launch by calling +* can get a list of shapes that are available for fast launch by calling :py:meth:`~ads.jobs.DataScienceJob.fast_launch_shapes`. Specifying a fast launch shape will allow your job to start as fast as possible. @@ -86,7 +86,7 @@ Logging ======= Logging is not required to create the job. -However, it is highly recommended to enable logging for debugging and monitoring purpose. +However, it is highly recommended to enable logging for debugging and monitoring. In the preceding example, both the log OCID and corresponding log group OCID are specified with the :py:class:`~ads.jobs.DataScienceJob` instance. diff --git a/docs/source/user_guide/jobs/policies.rst b/docs/source/user_guide/jobs/policies.rst index 54f935583..2a5541682 100644 --- a/docs/source/user_guide/jobs/policies.rst +++ b/docs/source/user_guide/jobs/policies.rst @@ -1,5 +1,5 @@ -I AM Policies -************* +IAM Policies +************ Oracle Cloud Infrastructure Identity and Access Management (IAM) lets you specify policies to control the access to your cloud resources. diff --git a/docs/source/user_guide/jobs/tabs/name_substitution.rst b/docs/source/user_guide/jobs/tabs/name_substitution.rst index f855185cd..fa0db9401 100644 --- a/docs/source/user_guide/jobs/tabs/name_substitution.rst +++ b/docs/source/user_guide/jobs/tabs/name_substitution.rst @@ -18,7 +18,7 @@ ) .with_runtime( PythonRuntime() - .with_service_conda("pytorch19_p37_cpu_v1") + .with_service_conda("pytorch110_p38_gpu_v1") .with_environment_variable(DATASET_NAME="MyData") .with_source("local/path/to/training_script.py") .with_output("output", "oci://bucket_name@namespace/prefix/${JOB_RUN_OCID}") From 8009e63f452d43c6b62b5728fd0173b18e37fdce Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Fri, 24 Feb 2023 10:23:38 -0500 Subject: [PATCH 057/147] Add YAML tab for runtime configs. --- docs/source/user_guide/jobs/runtime.rst | 100 +++++++----------- .../user_guide/jobs/tabs/runtime_args.rst | 83 +++++++++++++++ .../jobs/tabs/runtime_custom_conda.rst | 23 ++++ .../user_guide/jobs/tabs/runtime_envs.rst | 41 +++++++ .../jobs/tabs/runtime_service_conda.rst | 24 +++++ 5 files changed, 207 insertions(+), 64 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/runtime_args.rst create mode 100644 docs/source/user_guide/jobs/tabs/runtime_custom_conda.rst create mode 100644 docs/source/user_guide/jobs/tabs/runtime_envs.rst create mode 100644 docs/source/user_guide/jobs/tabs/runtime_service_conda.rst diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst index fd20212fa..0d4dade59 100644 --- a/docs/source/user_guide/jobs/runtime.rst +++ b/docs/source/user_guide/jobs/runtime.rst @@ -19,22 +19,9 @@ You can set environment variables for a runtime by calling :py:meth:`~ads.jobs.PythonRuntime.with_environment_variable()`. Environment variables enclosed by ``${...}`` will be substituted. For example: -.. code-block:: python - - from ads.jobs import PythonRuntime - - runtime = ( - PythonRuntime() - .with_environment_variable( - HOST="10.0.0.1", - PORT="443", - URL="http://${HOST}:${PORT}/path/", - ESCAPED_URL="http://$${HOST}:$${PORT}/path/", - MISSING_VAR="This is ${UNDEFINED}", - VAR_WITH_DOLLAR="$10", - DOUBLE_DOLLAR="$$10" - ) - ) +.. include:: ../jobs/tabs/runtime_envs.rst + +.. code-block:: python3 for k, v in runtime.environment_variables.items(): print(f"{k}: {v}") @@ -47,7 +34,7 @@ will show the following environment variables for the runtime: PORT: 443 URL: http://10.0.0.1:443/path/ ESCAPED_URL: http://${HOST}:${PORT}/path/ - MISSING_VAR: This is This is ${UNDEFINED} + MISSING_VAR: This is ${UNDEFINED} VAR_WITH_DOLLAR: $10 DOUBLE_DOLLAR: $10 @@ -69,19 +56,37 @@ Command Line Arguments The command line arguments for running your script or function can be configured by calling :py:meth:`~ads.jobs.PythonRuntime.with_argument()`. For example: -.. code-block:: python +.. tabs:: - from ads.jobs import PythonRuntime + .. code-tab:: python + :caption: Python - runtime = ( - PythonRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_arguments( - "arg1", "arg2", - key1="val1", - key2="val2" - ) - ) + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + .with_argument( + "arg1", "arg2", + key1="val1", + key2="val2" + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + scriptPathURI: oci://bucket_name@namespace/path/to/script.py + args: + - arg1 + - arg2 + - --key1 + - val1 + - --key2 + - val2 will configured the job to call your script by: @@ -94,25 +99,7 @@ to your desired order. You can check ``runtime.args`` to see the added arguments Here are a few more examples: -.. code-block:: python - - runtime = PythonRuntime().with_argument(key1="val1", key2="val2").with_argument("pos1") - print(runtime.args) - # ["--key1", "val1", "--key2", "val2", "pos1"] - - runtime = PythonRuntime() - runtime.with_argument("pos1") - runtime.with_argument(key1="val1", key2="val2.1 val2.2") - runtime.with_argument("pos2") - print(runtime.args) - # ['pos1', '--key1', 'val1', '--key2', 'val2.1 val2.2', 'pos2'] - - runtime = PythonRuntime() - runtime.with_argument("pos1") - runtime.with_argument(key1=None, key2="val2") - runtime.with_argument("pos2") - print(runtime.args) - # ["pos1", "--key1", "--key2", "val2", "pos2"] +.. include:: ../jobs/tabs/runtime_args.rst Conda Environment ================= @@ -125,28 +112,13 @@ for your workload. You can use the slug name to specify a `_. For example, to use the TensorFlow conda environment: -.. code-block:: python - - from ads.jobs import PythonRuntime - - runtime = ( - PythonRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - # Use slug name for conda environment provided by data science service - .with_service_conda("tensorflow28_p38_cpu_v1") - ) +.. include:: ../jobs/tabs/runtime_service_conda.rst You can also use a custom conda environment published to OCI Object Storage by passing the ``uri`` to :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`, for example: -.. code-block:: python - - runtime = ( - PythonRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_custom_conda("oci://bucket@namespace/conda_pack/pack_name") - ) +.. include:: ../jobs/tabs/runtime_custom_conda.rst By default, ADS will try to determine the region based on the authenticated API key or resource principal. If your custom conda environment is stored in a different region, diff --git a/docs/source/user_guide/jobs/tabs/runtime_args.rst b/docs/source/user_guide/jobs/tabs/runtime_args.rst new file mode 100644 index 000000000..67beef924 --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/runtime_args.rst @@ -0,0 +1,83 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + runtime = PythonRuntime() + runtime.with_argument(key1="val1", key2="val2") + runtime.with_argument("pos1") + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + args: + - --key1 + - val1 + - --key2 + - val2 + - pos1 + +.. code-block:: python + + print(runtime.args) + # ['--key1', 'val1', '--key2', 'val2', 'pos1'] + +.. tabs:: + + .. code-tab:: python + :caption: Python + + runtime = PythonRuntime() + runtime.with_argument("pos1") + runtime.with_argument(key1="val1", key2="val2.1 val2.2") + runtime.with_argument("pos2") + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + args: + - pos1 + - --key1 + - val1 + - --key2 + - val2.1 val2.2 + - pos2 + +.. code-block:: python + + print(runtime.args) + # ['pos1', '--key1', 'val1', '--key2', 'val2.1 val2.2', 'pos2'] + +.. tabs:: + + .. code-tab:: python + :caption: Python + + runtime = PythonRuntime() + runtime.with_argument("pos1") + runtime.with_argument(key1=None, key2="val2") + runtime.with_argument("pos2") + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + args: + - pos1 + - --key1 + - --key2 + - val2 + - pos2 + +.. code-block:: python + + print(runtime.args) + # ["pos1", "--key1", "--key2", "val2", "pos2"] \ No newline at end of file diff --git a/docs/source/user_guide/jobs/tabs/runtime_custom_conda.rst b/docs/source/user_guide/jobs/tabs/runtime_custom_conda.rst new file mode 100644 index 000000000..3cf150ea8 --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/runtime_custom_conda.rst @@ -0,0 +1,23 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + .with_custom_conda("oci://bucket@namespace/conda_pack/pack_name") + ) + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + conda: + type: published + uri: oci://bucket@namespace/conda_pack/pack_name + scriptPathURI: oci://bucket_name@namespace/path/to/script.py diff --git a/docs/source/user_guide/jobs/tabs/runtime_envs.rst b/docs/source/user_guide/jobs/tabs/runtime_envs.rst new file mode 100644 index 000000000..59852cd83 --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/runtime_envs.rst @@ -0,0 +1,41 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_environment_variable( + HOST="10.0.0.1", + PORT="443", + URL="http://${HOST}:${PORT}/path/", + ESCAPED_URL="http://$${HOST}:$${PORT}/path/", + MISSING_VAR="This is ${UNDEFINED}", + VAR_WITH_DOLLAR="$10", + DOUBLE_DOLLAR="$$10" + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + env: + - name: HOST + value: 10.0.0.1 + - name: PORT + value: '443' + - name: URL + value: http://${HOST}:${PORT}/path/ + - name: ESCAPED_URL + value: http://$${HOST}:$${PORT}/path/ + - name: MISSING_VAR + value: This is ${UNDEFINED} + - name: VAR_WITH_DOLLAR + value: $10 + - name: DOUBLE_DOLLAR + value: $$10 diff --git a/docs/source/user_guide/jobs/tabs/runtime_service_conda.rst b/docs/source/user_guide/jobs/tabs/runtime_service_conda.rst new file mode 100644 index 000000000..186dccb7e --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/runtime_service_conda.rst @@ -0,0 +1,24 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + # Use slug name for conda environment provided by data science service + .with_service_conda("tensorflow28_p38_cpu_v1") + ) + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + conda: + type: service + slug: tensorflow28_p38_cpu_v1 + scriptPathURI: oci://bucket_name@namespace/path/to/script.py From 76de4ee87920f470e20b18509ccc012a2ab5f486 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Fri, 24 Feb 2023 10:26:00 -0500 Subject: [PATCH 058/147] Update overview.rst --- docs/source/user_guide/jobs/components/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/jobs/components/overview.rst b/docs/source/user_guide/jobs/components/overview.rst index df69d5975..bc64b86eb 100644 --- a/docs/source/user_guide/jobs/components/overview.rst +++ b/docs/source/user_guide/jobs/components/overview.rst @@ -20,7 +20,7 @@ You can also sequence jobs and keep the state by writing state information to `Object Storage `_ For example, you may want to experiment with how different model classes perform on the same training data -by using the ADSTuner to perform hyperparameter tuning on each model class. +by using the [ADSTuner](https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/ads_tuner.html) to perform hyperparameter tuning on each model class. You could do this in parallel by having a different job run for each class of models. For a given job run, you could pass an environment variable that identifies the model class that you want to use. Each model can write its results to the Logging service or Object Storage. From 3b153f286f61728793ce2edddcf274d78c386447 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Fri, 24 Feb 2023 10:33:52 -0500 Subject: [PATCH 059/147] Update overview.rst and infrastructure.rst base on comments. --- docs/source/user_guide/jobs/components/overview.rst | 6 +++--- docs/source/user_guide/jobs/infrastructure.rst | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/jobs/components/overview.rst b/docs/source/user_guide/jobs/components/overview.rst index bc64b86eb..93019b8ef 100644 --- a/docs/source/user_guide/jobs/components/overview.rst +++ b/docs/source/user_guide/jobs/components/overview.rst @@ -1,9 +1,9 @@ Oracle Cloud Infrastructure (OCI) `Data Science Jobs (Jobs) `_ enables you to define and run repeatable machine learning tasks on a fully managed infrastructure. -You can have Compute resource on demand and run applications that perform tasks such as +You can create a compute resource on demand and run applications that perform tasks such as data preparation, model training, hyperparameter tuning, and batch inference. -Training with OCI involves two types resources: **Job** and **Job Run**. +Running workloads with Data Science Jobs involves two types resources: **Job** and **Job Run**. A **Job** is a template that describes the training task. It contains configurations about the *infrastructure*, such as @@ -26,5 +26,5 @@ For a given job run, you could pass an environment variable that identifies the Each model can write its results to the Logging service or Object Storage. Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. -The following sections provides details on running training workloads with OCI Data Science Jobs using ADS Jobs APIs. +The following sections provides details on running workloads with OCI Data Science Jobs using ADS Jobs APIs. You can use similar APIs to `Run a OCI DataFlow Application `_. diff --git a/docs/source/user_guide/jobs/infrastructure.rst b/docs/source/user_guide/jobs/infrastructure.rst index ab17834e6..3eeac37bc 100644 --- a/docs/source/user_guide/jobs/infrastructure.rst +++ b/docs/source/user_guide/jobs/infrastructure.rst @@ -34,6 +34,8 @@ The following configurations are optional: * Log Group ID * Log ID +For more details about the mandatory and optional parameters, see :py:class:`~ads.jobs.DataScienceJob`. + Using Configurations from Notebook ================================== From 191cfd90c023ba5af4299fd300fa575367d31413 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 22:47:59 -0800 Subject: [PATCH 060/147] ODSC-39261: update the automl user guide --- docs/source/ads.automl.rst | 30 - .../loading_data/connect_legacy.rst | 14 - .../model_catalog/model_catalog.rst | 1 - .../model_serialization/quick_start.rst | 59 +- .../user_guide/model_training/automl.rst | 63 -- .../model_training/automl/index.rst | 18 + .../model_training/{ => automl}/overview.rst | 0 .../model_training/automl/quick_start.rst | 125 +++ .../user_guide/model_training/index.rst | 2 +- .../accumulated_local_effects.rst | 146 --- .../feature_dependence.rst | 187 ---- .../feature_importance.rst | 137 --- .../figures/ads_mlx_ale.png | Bin 186543 -> 0 bytes .../figures/ads_mlx_ale_pdp_x1.png | Bin 20643 -> 0 bytes .../figures/ads_mlx_ale_x1.png | Bin 23814 -> 0 bytes ...lx_boston_whatif_explore_predictions_1.png | Bin 25013 -> 0 bytes ...lx_boston_whatif_explore_predictions_2.png | Bin 29202 -> 0 bytes ...lx_boston_whatif_explore_predictions_3.png | Bin 17738 -> 0 bytes .../ads_mlx_boston_whatif_explore_sample.png | Bin 73261 -> 0 bytes .../figures/ads_mlx_titanic_ice_age.png | Bin 109311 -> 0 bytes .../figures/ads_mlx_titanic_ice_sex.png | Bin 29251 -> 0 bytes .../figures/ads_mlx_titanic_local.png | Bin 118075 -> 0 bytes .../ads_mlx_titanic_local_diagnostics.png | Bin 232145 -> 0 bytes .../figures/ads_mlx_titanic_pdp_age.png | Bin 30321 -> 0 bytes .../ads_mlx_titanic_pdp_age_dataframe.png | Bin 101011 -> 0 bytes .../ads_mlx_titanic_pdp_age_diagnostics.png | Bin 164561 -> 0 bytes .../ads_mlx_titanic_pdp_pclass_age.png | Bin 30846 -> 0 bytes .../figures/ads_mlx_titanic_pdp_sex.png | Bin 17118 -> 0 bytes .../figures/ads_mlx_titanic_pi_bar.png | Bin 22407 -> 0 bytes .../figures/ads_mlx_titanic_pi_box.png | Bin 21340 -> 0 bytes .../ads_mlx_titanic_pi_diagnostics.png | Bin 98059 -> 0 bytes .../figures/ads_mlx_titanic_pi_scatter.png | Bin 24804 -> 0 bytes .../model_explainability/index.rst | 63 -- .../model_explainability/lime.rst | 166 --- .../model_explainability/whatif.rst | 97 -- .../user_guide/model_training/usage.rst | 960 ------------------ 36 files changed, 183 insertions(+), 1885 deletions(-) delete mode 100644 docs/source/ads.automl.rst delete mode 100644 docs/source/user_guide/model_training/automl.rst create mode 100644 docs/source/user_guide/model_training/automl/index.rst rename docs/source/user_guide/model_training/{ => automl}/overview.rst (100%) create mode 100644 docs/source/user_guide/model_training/automl/quick_start.rst delete mode 100644 docs/source/user_guide/model_training/model_explainability/accumulated_local_effects.rst delete mode 100644 docs/source/user_guide/model_training/model_explainability/feature_dependence.rst delete mode 100644 docs/source/user_guide/model_training/model_explainability/feature_importance.rst delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale_pdp_x1.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale_x1.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_1.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_2.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_3.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_sample.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_ice_age.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_ice_sex.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_local.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_local_diagnostics.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age_dataframe.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age_diagnostics.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_pclass_age.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_sex.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_bar.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_box.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_diagnostics.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_scatter.png delete mode 100644 docs/source/user_guide/model_training/model_explainability/index.rst delete mode 100644 docs/source/user_guide/model_training/model_explainability/lime.rst delete mode 100644 docs/source/user_guide/model_training/model_explainability/whatif.rst delete mode 100644 docs/source/user_guide/model_training/usage.rst diff --git a/docs/source/ads.automl.rst b/docs/source/ads.automl.rst deleted file mode 100644 index c45b7223b..000000000 --- a/docs/source/ads.automl.rst +++ /dev/null @@ -1,30 +0,0 @@ -ads.automl package -================== - -Submodules ----------- - -ads.automl.driver module ------------------------- - -.. automodule:: ads.automl.driver - :members: - :undoc-members: - :show-inheritance: - -ads.automl.provider module --------------------------- - -.. automodule:: ads.automl.provider - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: ads.automl - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/user_guide/loading_data/connect_legacy.rst b/docs/source/user_guide/loading_data/connect_legacy.rst index f3a856b45..57996133b 100644 --- a/docs/source/user_guide/loading_data/connect_legacy.rst +++ b/docs/source/user_guide/loading_data/connect_legacy.rst @@ -221,20 +221,6 @@ Close the cursor and connection using the ``.close()`` method: connection.close() -Train a Models with ADB -======================== - -After you load your data from ADB, the ``ADSDataset`` object is created, which allows you to build models using AutoML. - -.. code-block:: python3 - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - - train, test = ds.train_test_split() - model, baseline = AutoML(train, provider= OracleAutoMLProvider()).train(model_list=["LGBMClassifier"]) - - Update ADB Tables ================= diff --git a/docs/source/user_guide/model_catalog/model_catalog.rst b/docs/source/user_guide/model_catalog/model_catalog.rst index bd754208b..b7e610962 100644 --- a/docs/source/user_guide/model_catalog/model_catalog.rst +++ b/docs/source/user_guide/model_catalog/model_catalog.rst @@ -58,7 +58,6 @@ The ADS SDK automatically captures some of the metadata for you. It captures pr A model can be saved to the model catalog using the generic approach or the ``ADSModel`` approach: * The generic approach creates a generic model artifact using ``.prepare_generic_model()``, and saves it to the model catalog. -* The ``ADSModel`` approach prepares an artifact from the ``ADSModel`` object, and saves it to the model catalog using the ``.prepare()`` method. ``ADSModel`` objects are typically created from the AutoML engine. Data scientists can also convert models trained with other machine learning libraries into an ``ADSModel`` object (using the ``.from_estimator()`` method). **Notes:** diff --git a/docs/source/user_guide/model_serialization/quick_start.rst b/docs/source/user_guide/model_serialization/quick_start.rst index 9fa8c7d9f..05b902c0e 100644 --- a/docs/source/user_guide/model_serialization/quick_start.rst +++ b/docs/source/user_guide/model_serialization/quick_start.rst @@ -13,38 +13,57 @@ Create a model, prepare it, verify that it works, save it to the model catalog, .. code-block:: python3 - import logging + import pandas as pd + import numpy as np import tempfile - import warnings - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.catalog.model import ModelCatalog + from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score + from sklearn.linear_model import LogisticRegression + from sklearn.compose import make_column_selector as selector + from sklearn.impute import SimpleImputer + from sklearn.preprocessing import StandardScaler, OneHotEncoder + from sklearn.compose import ColumnTransformer + from sklearn.pipeline import Pipeline + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + + import ads + import automl + from automl import init + from ads.model import AutoMLModel from ads.common.model_metadata import UseCaseType - from ads.dataset.dataset_browser import DatasetBrowser from ads.model.framework.automl_model import AutoMLModel - ds = DatasetBrowser.sklearn().open("wine").set_target("target") - train, test = ds.train_test_split(test_size=0.1, random_state = 42) + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train( - model_list=['LogisticRegression', 'DecisionTreeClassifier'], - random_state = 42, - time_budget = 500 - ) + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + init(engine='local') + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + ads.set_auth("resource_principal") artifact_dir = tempfile.mkdtemp() automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) - automl_model.prepare(inference_conda_env="generalml_p37_cpu_v1", - training_conda_env="generalml_p37_cpu_v1", + automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", + training_conda_env="automlx_p38_cpu_v1", use_case_type=UseCaseType.BINARY_CLASSIFICATION, - X_sample=test.X, + X_sample=X_test, force_overwrite=True) - automl_model.verify(test.X.iloc[:10]) + automl_model.verify(X_test.iloc[:2]) model_id = automl_model.save(display_name='Demo AutoMLModel model') deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') - automl_model.predict(test.X.iloc[:10]) + automl_model.predict(X_test.iloc[:2]) automl_model.delete_deployment(wait_for_completion=True) ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) diff --git a/docs/source/user_guide/model_training/automl.rst b/docs/source/user_guide/model_training/automl.rst deleted file mode 100644 index 3ac223ea4..000000000 --- a/docs/source/user_guide/model_training/automl.rst +++ /dev/null @@ -1,63 +0,0 @@ -The Oracle AutoML Pipeline -========================== - -An AutoML Pipeline consists of these four main stages: - -.. image:: figures/pipeline.png - -The stages operate in sequence:  - -.. contents:: - -Algorithm Selection -------------------- - -With a given dataset and a prediction task, the goal is to identify the algorithm that maximizes the model score. This best algorithm is not always intuitive and simply picking a complex model is suboptimal for many use cases. The ADS algorithm selection stage is designed to rank algorithms based on their estimated predictive performance on the given dataset. - -.. image:: figures/algorithm_selection.png - -For a given dataset, the algorithm selection process is as follows: - -#. Extract relevant dataset characteristics, such as dataset shape, feature correlations, and appropriate meta-features. -#. Invoke specialized score-prediction metamodels that were learned to predict algorithm performance across a wide variety of datasets and domains. -#. Rank algorithms based on their predicted performance. -#. Select the optimal algorithm. - - -Adaptive Sampling ------------------ - -Adaptive sampling iteratively subsamples the dataset and evaluates each sample to obtain a score for a specific algorithm. The goal is to find the smallest sample size that adequately represents the full dataset. It is used in subsequent pipeline stages without sacrificing the quality of the model. - -.. image:: figures/adaptive_sampling.png - -The adaptive sampling process is as follows: - -#. For a given algorithm and dataset, identify a representative sample. -#. Leverage meta-learning to predict algorithm performance on the given sample. -#. Iterate until the score converges. -#. The identified sample is then used for subsequent stages of the AutoML Pipeline. - - -Feature Selection ------------------ - -The feature selection stage aims to select a subset of features that are highly predictive of the target. This speeds up model training without loss of predictive performance. The ADS feature selection approach leverages meta-learning to intelligently identify the optimal feature subset for a given algorithm and dataset. The high level process is: - -.. image:: figures/feature_selection.png - -For a given dataset, the feature selection process is as follows: - -#. Obtain the dataset meta-features, similar to those obtained in the algorithm selection stage. -#. Rank all features using multiple ranking algorithms. Feature rankings are ordered lists of features from most to least important. -#. For each feature ranking, the optimal feature subset is identified. -#. Algorithm performance is predicted by leveraging meta-learning on a given feature subset. -#. Iterating over multiple feature subsets, the optimal subset is determined. - - -Hyperparameter Tuning ---------------------- - -The hyperparameter tuning stage determines the optimal values for the model's hyperparameters. Generally, tuning is the most time-consuming stage of an AutoML pipeline. Therefore, the hyperparameter tuning process is designed with efficiency and scalability as first-order requirements. The ADS tuning strategy is summarized as: - -.. image:: figures/tuning.png diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst new file mode 100644 index 000000000..3421f1d63 --- /dev/null +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -0,0 +1,18 @@ +The Oracle AutoMLx +================== + +.. note:: + The ADS AutoML is deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with the Oracle AutoMLx library. + With AutoMLx moving into the open source domain, working directly with the library has many benefits. + One of these is that ADS and AutoMLx can now have independent version cadences. + AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. + To offer this functionality, the best way is for the ADS to work together as separate libraries. + The AutoMlx library can be found in `automlx_p38_cpu_v#` conda pack. The documentation can be found at this link `https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html>`__. Notebook examples can be found at this link `https://github.com/oracle-samples/automlx>`__. + +.. toctree:: + :hidden: + :maxdepth: 1 + + + overview + quick_start diff --git a/docs/source/user_guide/model_training/overview.rst b/docs/source/user_guide/model_training/automl/overview.rst similarity index 100% rename from docs/source/user_guide/model_training/overview.rst rename to docs/source/user_guide/model_training/automl/overview.rst diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst new file mode 100644 index 000000000..1dc0f218b --- /dev/null +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -0,0 +1,125 @@ +============================================================ +Quick Start +============================================================ + +This section provides a quick introduction to build a classifier using the Oracle AutoMLx tool for Iris dataset. +The dataset is a multi-class classification dataset, and more details about the dataset +can be found at `Iris dataset `_. We demonstrate +the preliminary steps required to train a model with the Oracle AutoMLx tool. We then explain the tuned model. + +Load dataset +------------ +We start by reading in the dataset from Scikit-learn. + +.. code-block:: python3 + + >>> import pandas as pd + >>> from sklearn.datasets import load_iris + >>> data = load_iris() + >>> df = pd.DataFrame(data['data'], columns=data['feature_names']) + >>> y = pd.Series(data['target']) + +This toy dataset only contains numerical data. +We now separate the predictions (`y`) from the training data (`X`) for both the training (`70%`) and test (`30%`) datasets. +The training set will be used to create a Machine Learning model using AutoMLx, +and the test set will be used to evaluate the model's performance on unseen data. + +.. code-block:: python3 + + >>> from sklearn.model_selection import train_test_split + >>> X_train, X_test, y_train, y_test = train_test_split(df, + y, + train_size=0.7, + random_state=0) + >>> X_train.shape, X_test.shape + ((105, 4), (45, 4)) + +Set the AutoMLx engine +---------------------- +AutoMLx offers the `init() `__ function, which allows to initialize the parallel engine. +By default, the AutoMLx pipeline uses the *dask* parallel engine. One can also set the engine to *local*, +which uses python's multiprocessing library for parallelism instead. + +.. code-block:: python3 + + >>> import automl + >>> from automl import init + + >>> init(engine='local') + [2023-01-12 05:48:31,814] [automl.xengine] Local ProcessPool execution (n_jobs=36) + +Train a model using AutoMLx +--------------------------- +The Oracle AutoMLx solution provides a pipeline that automatically finds a tuned model given a prediction task and a training dataset. +In particular it allows to find a tuned model for any supervised prediction task, e.g. classification or regression +where the target can be binary, categorical or real-valued. + +AutoMLx consists of five main modules: + #. **Preprocessing** : Clean, impute, engineer, and normalize features. + #. **Algorithm Selection** : Identify the right classification algorithm for a given dataset. + #. **Adaptive Sampling** : Select a subset of the data samples for the model to be trained on. + #. **Feature Selection** : Select a subset of the data features, based on the previously selected model. + #. **Hyperparameter Tuning** : Find the right model parameters that maximize score for the given dataset. + +All these pieces are readily combined into a simple AutoMLx pipeline which +automates the entire Machine Learning process with minimal user input/interaction. + +The AutoMLx API is quite simple to work with. We create a :obj:`automl.Pipeline`__ instance. +Next, the training data is passed to the `fit() `__ function which executes the previously mentioned steps. + +.. code-block:: python3 + + >>> est = automl.Pipeline(task='classification') + >>> est.fit(X_train, y_train) + Pipeline() + +A model is then generated (`est`) and can be used for prediction tasks. +Here, we use the `F1_score` scoring metric to evaluate the performance of this model on unseen data (`X_test`). + +.. code-block:: python3 + + >>> from sklearn.metrics import f1_score + >>> y_pred = est.predict(X_test) + >>> score_default = f1_score(y_test, y_pred, average='macro') + >>> print(f'Score on test data : {score_default}') + Score on test data : 0.975983436853002 + + +The `automl.Pipeline`__ can also fit regression, forecasting and anomaly detection models. +Please check out the rest of the documentation for more details about advanced configuration parameters. + +Explain a classifier +-------------------- +For a variety of decision-making tasks, getting only a prediction as model output is not sufficient. +A user may wish to know why the model outputs that prediction, or which data features are relevant for that prediction. +For that purpose the Oracle AutoMLx solution defines the `automl.interface.mlx.MLExplainer`__ object, which allows to compute a variety of model explanations for any AutoMLx-trained pipeline or scikit-learn-like model. +`automl.interface.mlx.MLExplainer`__ takes as argument the trained model, the training data and labels, as well as the task. + +.. code-block:: python3 + +>>> explainer = automl.MLExplainer(est, + X_train, + y_train, + task="classification") + +Let's explain the model's performance (relative to the provided train labels) using Global Feature Importance. This technique would change +if a given feature were dropped from the dataset, without retraining the model. +This notion of feature importance considers each feature independently from all other features. + +The method :obj:`explain_model() ` allows to compute such feature importances. It also provides 95% confidence intervals for each feature importance attribution. + + >>> result_explain_model_default = explainer.explain_model() + >>> result_explain_model_default.to_dataframe() + feature attribution upper_bound lower_bound + 0 petal width (cm) 0.350644 0.416850 0.284437 + 1 petal length (cm) 0.272190 0.309005 0.235374 + 2 sepal length (cm) 0.000000 0.000000 0.000000 + 3 sepal width (cm) 0.000000 0.000000 0.000000 + +The oracle AutoMLx solution offers advanced configuration options and allows one to change the effect of feature interactions and interaction evaluations. +It also provides other model and prediction explanation techniques, such as: + - `Local feature importance `__, for example, using Kernel SHAP or an enhanced LIME; + - `Feature Dependence Explanations `__, such as partial dependence plots or accumulated local effects; + - `Interactive What-IF explainers `__, which let users explore a model's predictions; and + - `Counterfactual explanations __`, which show how to change a row to obtain a desired outcome. +Please check out the `automl.interface.mlx.MLExplainer`__ documentation for more details. \ No newline at end of file diff --git a/docs/source/user_guide/model_training/index.rst b/docs/source/user_guide/model_training/index.rst index 2f207b2de..796bf0a56 100644 --- a/docs/source/user_guide/model_training/index.rst +++ b/docs/source/user_guide/model_training/index.rst @@ -21,7 +21,7 @@ TensorBoard provides the visualization and the tooling that is needed to watch a distributed_training/overview tensorboard/tensorboard model_evaluation/index - model_explainability/index + automl/index diff --git a/docs/source/user_guide/model_training/model_explainability/accumulated_local_effects.rst b/docs/source/user_guide/model_training/model_explainability/accumulated_local_effects.rst deleted file mode 100644 index 33ec045d8..000000000 --- a/docs/source/user_guide/model_training/model_explainability/accumulated_local_effects.rst +++ /dev/null @@ -1,146 +0,0 @@ -Accumulated Local Effects -************************* - -Overview -======== - -Similar to Partial Dependence Plots (PDP), Accumulated Local Effects (ALE) is a model-agnostic global explanation method that evaluates the relationship between feature values and target variables. However, in the event that features are highly correlated, PDP may include unlikely combinations of feature values in the average prediction calculation due to the independent manipulation of feature values across the marginal distribution. This lowers the trust in the PDP explanation when features have strong correlation. Unlike PDP, ALE handles feature correlations by averaging and accumulating the difference in predictions across the conditional distribution, which isolates the effects of the specific feature. This comes at the cost of requiring a larger number of observations and a near uniform distribution of those observations so that the conditional distribution can be reliably determined. - -Description -=========== - -ALE highlights the effects that specific features have on the predictions of a machine learning model by partially isolating the effects of other features. Therefore, it tends to be robust against correlated features. The resulting ALE explanation is centered around the mean effect of the feature, such that the main feature effect is compared relative to the average prediction of the data. - -Correlated features can negatively affect the quality of many explanation techniques. Specifically, many challenges arise when the black-box model is used to make predictions on unlikely artificial data. That is data that that fall outside of the expected data distribution but are used in an explanation because they are not independent and the technique is not sensitive to this possibility. This can occur, for example, when the augmented data samples are not generated according the feature correlations or the effects of other correlated features are included in the evaluation of the feature of interest. Consequently, the resulting explanations may be misleading. In the context of PDP, the effect of a given feature may be heavily biased by the interactions with other features. - -To address the issues associated with correlated features, ALE: - -* Uses the conditional distribution of the feature of interest to generate augmented data. This tends to create more realistic data that using marginal distribution. This helps to ensure that evaluated feature values, e.g., ``xi``, are only compared with instances from the dataset that have similar values to ``xi``. -* Calculates the average of the differences in model predictions over the augmented data, instead of the average of the predictions themselves. This helps to isolate the effect of the feature of interest. For example, assuming we are evaluating the effect of a feature at value ``xi``, ALE computes the average of the difference in model predictions of the values in the neighborhood of ``xi``. That is, that observation within ``xi`` ±ϵ that meet the conditional requirement. This helps to reduce the effects of correlated features. - -The following example demonstrates the challenges with accurately evaluating the effect of a feature on a model’s predictions when features are highly correlated. Let us assume that features ``x1`` and ``x2`` are highly correlated. We can artificially construct ``x2`` by starting with ``x1`` and adding a small amount of random noise. Further assume that the target value is the product of these two features (e.g., y = ``x1`` * ``x2``). Since ``x1`` and ``x2`` are almost identical, the target value has a quadratic relationship with them. -A decision tree is trained on this dataset. Then different explanation techniques, PDP (first column), ICE (second column), and ALE (third column), are used to evaluate the effect of the features on the model predictions. Features ``x1`` and ``x2`` are evaluated in the first and second row, respectively. The following image demonstrates that PDP is unable to accurately identify the expected relationship due to the assumption that the features are not correlated. An examination of the ICE plots revels the quadratic relationship between the features and the target. However, the when taking as an aggregate, this effect disappears. In contrast, ALE is able to properly capture the isolated effect of each feature, highlighting the quadratic relationship. - -.. image:: figures/ads_mlx_ale.png - -The following summarizes the steps in computing ALE explanation (note: MLX supports one-feature ALE): - -* Start with a trained model. -* Select a feature to explain (for example, one of the important features identified in the global feature importance explanations). -* Compute the intervals of the selected feature to define the upper and lower bounds used to compute the difference in model predictions when the feature is increased or decreased. - - - Numerical features: using the selected feature’s value distribution extracted from the train dataset, MLX selects multiple different intervals from the feature’s distribution to evaluate (e.g., based on percentiles). The number of intervals to use and the range of the feature’s distribution to consider are configurable. - - Categorical features: since ALE computes the difference in model predictions between an increase and decrease in a feature’s value, features must have some notion of order. This can be challenging for categorical features, as there may not be a notion of order (e.g., eye color). To address this, MLX estimates the order of categorical feature values based on a categorical feature encoding technique. MLX provides multiple different encoding techniques based on the input data (e.g., ``distance_similarity``: computes a similarity matrix between all categorical feature values and the other feature values, and orders based on similarity. Target-based approaches estimate the similarity/order based on the relationship of categorical feature values with the target variable. The supported techniques include, target encoding, ``target``, James-Stein encoding, ``jamesstein``, Generalized Linear Mixed Model encoding, ``glmm``, M-estimate encoding, ``mestimate``, and Weight of Evidence encoding, ``woe``. The categorical feature value order is then used to compute the upper (larger categorical value) and lower (smaller categorical value) bounds for the selected categorical feature. - -* For each interval, MLX approximates the conditional distribution by identifying the samples that are in the neighborhood of the sample of interest. It then calculates the difference in the model prediction when the selected feature’s value of the samples is replaced by the upper and lower limits of the interval. If **N** different intervals are selected from the feature’s distribution, this process results in **2N** different augmented datasets It is **2N** as each selected feature of the sample are replaced with the upper and lower limits of the interval. The model inference then generates **2N** different model predictions, which are used to calculate the **N** differences. -* The prediction differences within each interval are averaged and accumulated in order, such that the ALE of a feature value that lies in the **k-th** interval is the sum of the effects of the first through the **k-th** interval. -* Finally, the accumulated feature effects at each interval is centered, such that the mean effect is zero. - -Interpretation -============== - -* Continuous or discrete numerical features: Visualized as line graphs. Each line represents the change in the model prediction when the selected feature has the given value compared to the average prediction. For example, an ALE value of ±b at xj = k indicates that when the value of feature j is equal to k, the model prediction is higher/lower by b compared to the average prediction. The x-axis shows the selected feature values and the y-axis shows the delta in the target prediction variable relative to the average prediction (e.g., the prediction probability for classification tasks and the raw predicted values for regression tasks). -* Categorical features: Visualized as vertical bar charts. Each bar represents the change in the model prediction when the selected feature has the given value compared to the average prediction. The interpretation of the value of the bar is similar to continuous features. The x-axis shows the different categorical values for the selected feature and the y-axis shows the change in the predicted value relative to the average prediction. This would be the prediction probability for classification tasks and the raw predicted values for regression tasks. - -Limitations -=========== - -There is an increased computational cost for performing an ALE analysis because of the large number of models that need to be computed relative to PDP. On a small dataset, this is generally not an issue. However, on larger datasets it can be. It is possible to parallelize the process and to also compute it in a distributed manner. - -The main disadvantage comes from the problem of sparsity of data. There needs to be sufficient number of observations in each neighborhood that is used in order to make a reasonable estimation. Even with large dataset this can be problematic if the data is not uniformly sampled, which is rarely the case. Also, with higher dimensionality the problem is made increasingly more difficult because of this curse of dimensionality. - -Depending on the class of model that is being use, it is common practice to remove highly correlated features. In this cases there is some rational to using a PDP for interpretation. However, if there is correlation in the data and the sampling of the data is suitable for an ALE analysis, it may be the preferred approach. - -Examples -======== - -The following is a purposefully extreme, but realistic, example that demonstrates the effects of highly correlated features on PDP and ALE explanations. The data set has three columns, ``x1``, ``x2`` and ``y``. - -* ``x1`` is generated from a uniform distribution with a range of [-5, 5]. -* ``x2`` is ``x1`` with some noise. ``x1`` and ``x2`` are highly correlated for illustration purposes. -* ``y`` is our target which is generated from an interaction term of ``x1 * x2`` and ``x2``. - -This model is trained using a Sklearn RegressorMixin model and wrapped in an ``ADSModel`` object. Please note that the ADS model explainers work with any model that is wrapped in an ADSModel object. - -.. code-block:: python3 - - import numpy as np - import pandas as pd - from ads.dataset.factory import DatasetFactory - from ads.common.model import ADSModel - from sklearn.base import RegressorMixin - - x1 = (np.random.rand(500) - 0.5) * 10 - x2 = x1 + np.random.normal(loc=0, scale=0.5, size=500) - y = x1 * x2 - - correlated_df = pd.DataFrame(np.stack((x1, x2, y), axis=1), columns=['x1', 'x2', 'y']) - correlated_ds = DatasetFactory.open(correlated_df, target='y') - - correlated_train, _ = correlated_ds.train_test_split(test_size=0) - - - class CorrelatedRegressor(RegressorMixin): - ''' - implement the true model - ''' - def fit(self, X=None, y=None): - self.y_bar_ = X.iloc[:, 0].to_numpy() * X.iloc[:, 1].to_numpy() + X.iloc[:, 1].to_numpy() - - def predict(self, X=None): - return X.iloc[:, 0].to_numpy() * X.iloc[:, 1].to_numpy() + X.iloc[:, 1].to_numpy() - - - # train a RegressorMixin model - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - correlated_regressor = CorrelatedRegressor() - correlated_regressor.fit(correlated_train.X, correlated_train.y) - - # Build ads models from ExtraTrees regressor - correlated_model = ADSModel.from_estimator(correlated_regressor, name="TrueModel") - - - # Create the ADS explainer object, which is used to construct - # global and local explanation objects. The ADSExplainer takes - # as input the model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - correlated_explainer = ADSExplainer(correlated_train, correlated_model, training_data=correlated_train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - correlated_global_explainer = correlated_explainer.global_explanation(provider=MLXGlobalExplainer()) - - - # A summary of the global accumulated local effects explanation - # algorithm and how to interpret the output - correlated_global_explainer.accumulated_local_effects_summary() - - # compute a PDP between x1 and the target, y - pdp_x1 = correlated_global_explainer.compute_partial_dependence("x1") - pdp_x1.show_in_notebook() - -.. image:: figures/ads_mlx_ale_pdp_x1.png - -The PDP plot shows a rug plot of the actual ``x1`` values along the x-axis and the relationship between ``x1`` and ``y`` appears as a line. However, it is known that the true relationship is not linear. ``y`` is the product of ``x1`` and ``x2``. Since ``x2`` nearly identical to ``x1``, effectively the relationship between ``x1`` and ``y`` is quadratic. The high level of correlation between ``x1`` and ``x2`` violates one of the assumptions of the PDP. As demonstrated, the bias created by this correlation results in a poor representation of the global relationship between ``x1`` and ``y``. - -.. code-block:: python3 - - # Compute the ALE on x1 - ale_x1 = correlated_global_explainer.compute_accumulated_local_effects("x1") - ale_x1.show_in_notebook() - -.. image:: figures/ads_mlx_ale_x1.png - -In comparison, the ALE plot does not have as strong a requirement that the features are uncorrelated. As such, there is very little bias introduced when they are. The following ALE plot demonstrates that it is able to accurately represent the relationship between ``x1`` and ``y`` as being quadratic. This is due to the fact that ALE uses the conditional distribution of these two features. This can be thought of as only using those instances where the values of ``x1`` and ``x2`` are close. - -In general, ALE plots are unbiased with correlated features as they use conditional probabilities. The PDP method uses the marginal probability and that can introduce a bias when there are highly correlated features. The advantage is that when the data is not rich enough to adequately determine all of the conditional probabilities or when the features are not highly correlated, it can be an effective method to assess the global impact of a feature in a model. - -References -========== - -- `Accumulated Local Effects (ALE) Plot `_ -- `Visualizing the effects of predictor variables in black box supervised learning models `_ - - diff --git a/docs/source/user_guide/model_training/model_explainability/feature_dependence.rst b/docs/source/user_guide/model_training/model_explainability/feature_dependence.rst deleted file mode 100644 index 9eec27796..000000000 --- a/docs/source/user_guide/model_training/model_explainability/feature_dependence.rst +++ /dev/null @@ -1,187 +0,0 @@ -Feature Dependence Explanations -******************************* - -Overview -======== - -Feature Dependence Explanations (PDP and ICE) are model-agnostic global explanation methods that evaluate the relationship between feature values and model target predictions. - -Description -=========== - -PDP and ICE highlight the marginal effect that specific features have on the predictions of a machine learning model. These explanation methods visualize the effects that different feature values have on the model's predictions. - -These are the main steps in computing PDP or ICE explanations: - -* Start with a trained machine learning model. -* Select a feature to explain (for example, one of the important features identified in the global feature permutation importance explanations.) -* Using the selected feature's value distribution extracted from the training dataset, ADS selects multiple different values from the feature's distribution to evaluate. The number of values to use and the range of the feature's distribution to consider are configurable. -* ADS replaces every sample in the provided dataset with the same feature value from the feature distribution and computes the model inference on the augmented dataset. This process is repeated for all of the selected values from the feature's distribution. If *N* different values are selected from the feature's distribution, this process results in *N* different datasets. Each with the selected feature having the same value for all samples in the corresponding dataset. The model inference then generates *N* different model predictions, each with *M* values (one for each sample in the augmented dataset.) -* For ICE, the model predictions for each augmented sample in the provided dataset are considered separately when the selected feature's value is replaced with a value from the feature distribution. This results in *N x M* different values. -* For PDP, the average model prediction is computed across all augmented dataset samples. This results in *N* different values (each an average of *M* predictions). - -The preceding is an example of one-feature PDP and ICE explanations. PDP also supports two-feature explanations while ICE only supports one feature. The main steps of the algorithm are the same though the explanation is computed on two features instead of one. - -* Select two features to explain. -* ADS computes the cross-product of values selected from the feature distributions to generate a list of different value combinations for the two selected features. For example, assuming we have selected *N* values from the feature distribution for each feature: - - [(:math:`X_{1}^{1}`, :math:`X_{2}^{1}`), - (:math:`X_{1}^{1}`, :math:`X_{2}^{2}`), :math:`\dots`, - (:math:`X_{1}^{1}`, :math:`X_{2}^{N-1}`), - (:math:`X_{1}^{1}`, :math:`X_{2}^{N}`), - (:math:`X_{1}^{2}`, :math:`X_{2}^{1}`), - (:math:`X_{1}^{2}`, :math:`X_{2}^{2}`), :math:`\dots`, - (:math:`X_{1}^{N}`, :math:`X_{2}^{N-1}`), - (:math:`X_{1}^{N}`, :math:`X_{2}^{N}`)] - -* For each feature value combination, ADS replaces every sample in the provided set with these two feature values and computes the model inference on the augmented dataset. There are *M* different samples in the provided dataset and *N* different values for each selected feature. This results in :math:`N^{2}` predictions from the model, each an average of *M* predictions. - -Interpretation -============== - -PDP ---- - -* One-feature - - - Continuous or discrete numerical features: Visualized as line graphs, each line represents the average prediction from the model (across all samples in the provided dataset) when the selected feature is replaced with the given value. The x-axis shows the selected feature values and the y-axis shows the predicted target (e.g., the prediction probability for classification tasks and the raw predicted values for regression tasks). - - Categorical features: Visualized as vertical bar charts. Each bar represents the average prediction from the model (across all samples in the provided dataset) when the selected feature is replaced with the given value. The x-axis shows the different values for the selected feature and the y-axis shows the predicted target (e.g., the prediction probability for classification tasks and the raw predicted values for regression tasks). - -* Two-feature - - - Visualized as a heat map. The x and y-axis both show the selected feature values. The heat map color represents the average - prediction from the model (across all samples in the provided dataset) when the selected features are replaced with the corresponding values. - -ICE ---- - -* Continuous or discrete numerical features: Visualized as line graphs. While PDP shows the average prediction across all samples in the provided dataset, ICE plots every sample from the provided dataset (when the selected feature is replaced with the given value) separately. The x-axis shows the selected feature values and the y-axis shows the predicted target (for example, the prediction probability for classification tasks and the raw predicted values for regression tasks). The median value can be plotted to highlight the trend. The ICE plots can also be centered around the first prediction from the feature distribution (for example, each prediction subtracts the predicted value from the first sample). -* Categorical features: Visualized as violin plots. The x-axis shows the different values for the selected feature and the y-axis shows the predicted target (for example, the prediction probability for classification tasks and the raw predicted values for regression tasks). - -Both PDP and ICE visualizations display the feature value distribution from the training dataset on the corresponding axis. For example, the one-feature line graphs, bar charts, and violin plots show the feature value distribution on the x-axis. The heat map shows the feature value distributions on the respective x-axis or y-axis. - -Examples -======== - -The following example generates and visualizes global partial dependence plot (PDP) and Individual Conditional Expectation (ICE) explanations on the `Titanic dataset `_. The model is constructed using the ADS ``OracleAutoMLProvider`` (selected model: XGBClassifier), however, the ADS model explainers work with any model (classifier or regressor) that is -wrapped in an ADSModel object. - -.. code-block:: python3 - - from ads.dataset.factory import DatasetFactory - from os import path - import requests - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - import logging - from ads.automl.provider import OracleAutoMLProvider - from ads.automl.driver import AutoML - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct - # global and local explanation objects. The ADSExplainer takes - # as input the model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - global_explainer = explainer.global_explanation( - provider=MLXGlobalExplainer()) - - # A summary of the global partial feature dependence explanation - # algorithm and how to interpret the output can be displayed with - global_explainer.partial_dependence_summary() - - # Compute the 1-feature PDP on the categorical feature, "sex", - # and numerical feature, "age" - pdp_sex = global_explainer.compute_partial_dependence("sex") - pdp_age = global_explainer.compute_partial_dependence( - "age", partial_range=(0, 1)) - - # ADS supports PDP visualizations for both 1-feature and 2-feature - # Feature Dependence explanations, and ICE visualizations for 1-feature - # Feature Dependence explanations (see "Interpretation" above) - - # Visualize the categorical feature PDP for the True (Survived) label - pdp_sex.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_pdp_sex.png - -.. code-block:: python3 - - # Visualize the numerical feature PDP for the True (Survived) label - pdp_age.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_pdp_age.png - -.. code-block:: python3 - - # Compute the 2-feature PDP on the categorical feature, "pclass", and - # numerical feature, "age" - pdp_pclass_age = global_explainer.compute_partial_dependence( - ['pclass', 'age'], partial_range=(0, 1)) - pdp_pclass_age.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_pdp_pclass_age.png - -.. code-block:: python3 - - # Visualize the ICE plot for the categorical feature, "sex" - pdp_sex.show_in_notebook(mode='ice', labels=True) - -.. image:: figures/ads_mlx_titanic_ice_sex.png - -.. code-block:: python3 - - # Visualize the ICE plot for the numerical feature, "age", and center - # around the first prediction (smallest age) - pdp_age.show_in_notebook(mode='ice', labels=True, centered=True) - -.. image:: figures/ads_mlx_titanic_ice_age.png - -.. code-block:: python3 - - # The raw explanation data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - pdp_age.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_pdp_age_diagnostics.png - -.. code-block:: python3 - - # The explanation can also be returned as Pandas.DataFrame with - pdp_age.as_dataframe() - -.. image:: figures/ads_mlx_titanic_pdp_age_dataframe.png - - -References -========== - -- `Partial Dependence Plot `_ -- `Vanderbilt Biostatistics - titanic data `_ - diff --git a/docs/source/user_guide/model_training/model_explainability/feature_importance.rst b/docs/source/user_guide/model_training/model_explainability/feature_importance.rst deleted file mode 100644 index d6fe4f5b7..000000000 --- a/docs/source/user_guide/model_training/model_explainability/feature_importance.rst +++ /dev/null @@ -1,137 +0,0 @@ -Feature Importance Explanations -******************************* - -Overview -========= - -Feature permutation importance is a model-agnostic global explanation method that provides insights into a machine learning model's behavior. It estimates and ranks feature importance based on the impact each feature has on the trained machine learning model's predictions. - -Description -=========== - -Feature permutation importance measures the predictive value of a feature for any black box estimator, classifier, or regressor. It does this by evaluating how the prediction error increases when a feature is not available. Any scoring metric can be used to measure the prediction error. For example, :math:`F_1` for classification or R\ :sup:`2` \ for regression. To avoid actually removing features and retraining the estimator for each feature, the algorithm randomly shuffles the feature values effectively adding noise to the feature. Then, the prediction error of the new dataset is compared with the prediction error of the original dataset. If the model heavily relies on the column being shuffled to accurately predict the target variable, this random re-ordering causes less accurate predictions. If the model does not rely on the feature for its predictions, the prediction error remains unchanged. - -The following summarizes the main steps in computing feature permutation importance explanations: - -* Start with a trained machine learning model. -* Calculate the baseline prediction error on the given dataset. For example, train dataset or test dataset. -* For each feature: - - 1. Randomly shuffle the feature column in the given dataset. - 2. Calculate the prediction error on the shuffled dataset. - 3. Store the difference between the baseline score and the shuffled dataset score as the feature importance. For example, baseline score - shuffled score. - -* Repeat the preceding three steps multiple times then report the average. Averaging mitigates the effects of random shuffling. -* Rank the features based on the average impact each feature has on the model's score. Features that have a larger impact on the score when shuffled are assigned higher importance than features with minimal impact on the model's score. -* In some cases, randomly permuting an unimportant feature can actually have a positive effect on the model's prediction so the feature's contribution to the model's predictions is effectively noise. In the feature permutation importance visualizations, ADS caps any negative feature importance values at zero. - -Interpretation -============== - -Feature permutation importance explanations generate an ordered list of features along with their importance values. Interpreting the output of this algorithm is straightforward. Features located at higher ranks have more impact on the model predictions. Features at lower ranks have less impact on the model predictions. Additionally, the importance values represent the relative importance of features. - -The output supports three types of visualizations. They are all based on the same data but present the data differently for various use cases: - -* **Bar chart** (``'bar'``): The bar chart shows the model's view of the relative feature importance. The x-axis highlights feature importance. A longer bar indicates higher importance than a shorter bar. Each bar also shows the average feature importance value along with the standard deviation of importance values across all iterations of the algorithm (mean importance +/- standard deviation*). Negative importance values are capped at zero. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. -* **Box plot** (``'box_plot'``): The detailed box plot shows the feature importance values across the iterations of the algorithm. These values are used to compute the average feature importance and the corresponding standard deviations shown in the bar chart. The x-axis shows the impact that permuting a given feature had on the model's prediction score. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. The minimum, first quartile, median, third quartile, and a maximum of the feature importance values across different iterations of the algorithm are shown by each box. -* **Detailed scatter plot** (``'detailed'``): The detailed bar chart shows the feature importance values for each iteration of the algorithm. These values are used to compute the average feature importance values and the corresponding standard deviations shown in the bar chart. The x-axis shows the impact that permuting a given feature had on the model's prediction score. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. The color of each dot in the graph indicates the quality of the permutation for this iteration, which is computed by measuring the correlation of the permuted feature column relative to the original feature colum. For example, how different is the permuted feature column versus the original feature column. - -Examples -======== - -This example generates and visualizes a global feature permutation importance explanation on the `Titanic dataset `_. The model is constructed using the ADS ``OracleAutoMLProvider``. However, the ADS model explainers work with any model (classifier or regressor) that is wrapped in an ``ADSModel`` object. - -.. code-block:: python3 - - import logging - import requests - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.dataset.factory import DatasetFactory - from os import path - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct global - # and local explanation objects. The ADSExplainer takes as input the - # model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - global_explainer = explainer.global_explanation( - provider=MLXGlobalExplainer()) - - # A summary of the global feature permutation importance algorithm and - # how to interpret the output can be displayed with - global_explainer.feature_importance_summary() - - # Compute the global Feature Permutation Importance explanation - importances = global_explainer.compute_feature_importance() - - # ADS supports multiple visualizations for the global Feature - # Permutation Importance explanations (see "Interpretation" above) - - # Simple bar chart highlighting the average impact on model score - # across multiple iterations of the algorithm - importances.show_in_notebook() - -.. image:: figures/ads_mlx_titanic_pi_bar.png - -.. code-block:: python3 - - # Box plot highlighting the mean, median, quartiles, and min/max - # impact on model score across multiple iterations of the algorithm - importances.show_in_notebook('box_plot') - -.. image:: figures/ads_mlx_titanic_pi_box.png - -.. code-block:: python3 - - # Detailed scatter plot highlighting the individual impacts on - # model score across multiple iterations of the algorithm - importances.show_in_notebook('detailed') - -.. image:: figures/ads_mlx_titanic_pi_scatter.png - -.. code-block:: python3 - - # The raw explanaiton data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - importances.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_pi_diagnostics.png - -References -========== - -* `Feature importance `_ -* `Perutation importance `_ -* `Vanderbilt Biostatistics - titanic data `_ - diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale.png deleted file mode 100644 index b4ef95dfdce4c069456061b451a9087a3fa4eb8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186543 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

tl$){&1#s&;o-zY) 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

vVYcJnuV69_u$*cct&P!;%;qd(fe<*YlGICx= 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 LI!`K;pS}4%D}Nod diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale_pdp_x1.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_ale_pdp_x1.png deleted file mode 100644 index 6e27c5c7fefc78415727ee72668f0c3ad24d86ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20643 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 L^YJVu2Pgj*?g6R~ diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_1.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_1.png deleted file mode 100644 index ceeec8226c7f0009b5fa3c903a53109e9bcae858..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25013 zcmeFZRa}&97cUG0NJ@uDH;90w(jXuu-8G<;G}0X^pdek+-7$2RNq2YG&_lxj`$nJe z@%_HTeYQQ|H{Nq!v97h&f8{kFloh4#V%*0-KtQ-FDY%`goLuJgaoy+gPoa$wJ8DubEL76(F56s%pEUY7#Vf+va(<}xT?N?|54Sb^=n&i z+gIvuM&GGZKk4f)+{0b?>PO(!_@fOilVj^mkK7^LeK@z;>zbGklHj1pAW(PgkLW6A z`I-v}i4HX)C0R8&8PjAPVN#$IMT$Y}1dCd)wkaFon+T#w2q7W`n#nw3`7q5k4z&yZ zr`~TMO#=KCEE9QzNsugV&7Jot*e~?_COSz!u!z;5cCf!EYx5iaD)o7$O?5oduTl0+ zJ%7$4L9vYQ(TODYu^dowyC1nds(Zxz=o9z7N7`H)TuaSoW00ZP?LfRM6AbHuDx& zKJiCenKus7+ixuIK6MG*x#MGXC(vdc13v_R5!)R8aC39hym52W8sdL~w7e~bjX>{evGCCf9|zz=s)f3?leWTh0b@HG_7^60 zFHPCqZ0rF(As`632>>5$Or2g(yV+RVItsW6)Bb!z0Qh|Sn1h!3=POQD!nE27%G45e z4yM$+>^$s`Xhkrnsi}n=Ow0sSC7=B@9QdCw?JFlIdjSp(S65edS8jGY2XhWietv$A zM_e3STx`G_Y>w`>PA}ZpY#r%-P4f3VlBSNv4i@%K7IwDOxAVSuY3J-DOiO#a&_Dlv z?bFoF;@_2Q9seQ=Aduts8xBtPM;!mm4Ga~!eJY@A;bv;BEoot6YU>EBA;S5H^Rdv+ z0sr&Wzf1l$ROjEJ++3W$kNoYMzeWmi+*0rxMZb>g=TqRkL@&f(Q@AmBOUEOmvcRfity=sxD=x;J57m0oM z4grKl?f2K8IFe6%Ho9dWQ7N1v2uVn~1MiZ39G|MPpgZ0gkI#{hzjMkD&Chz%zXe-K zFi(vHo_5D-fmsL`pQD+L7N{`|iq897mSB-TQU39fl)wwp#I!Y5!VR++$yaf=Lp7YP zwu-N^nbw@Dwj%LjgMaWb%!96|P5NMxu*U#P$pv9knA}`nMa9LLt>~Tjh7hyi=MA$6 zdmJ%eYL?VEpC6YyLuVUZ>)qeA8mb6?J=`45Q>s6Is9o0PW?*iX>^5{n3!~| z^(L*J&ig(FkL^rXn98F!kxbNPwiB^x-m|lipPgGQ&?wSW)zy8#9xpH8x=Vhosh(;@ z+wRosVMZFK({Q~3mu+P>={>?sEO1(*saptSO9~Kus zoNV^2?MdKnuX}>;gU6ukU!qqlCob;4fiT)7dpa9;O3=-<6{1*BP_T6DfrGa1%+F^t zWfG&(3gNi_jaK9FK$w(HruRoSR#J~PLGEC3UmvL}(WWfEl zv3^ngD%&`W|5>hL%7Y@!5-yM3Zoy~H&+a07tU33ark`>(wd<}IZDqaPYin7hkoRR2 z3WuExLN>wrDTY#}!)6|7+uPPzc|QHA!WG8t*vl_JwAfeJO!MhfnuQJyQ!(2~wHNMe znQl={lv?;yU+JH{cG_z$&D-_go2o84h}%tYZJZ4>mde5gxo40qg57iqr zW0j~|8$_VSS|cZ`tHNGq_RFTiTsfr{lNF{>@$p(E)|RS2y11N7P6?%7BZ~W&$}|Q2 zE3tkWD4M1EELRuD+<{43T+kQm4blSlW!Jp{26p@2TpA7vPp{^nQo4!RHty751T~?G zWW7BVYa?+h*)}#sY?;%-X)RS8BEdXTl9I(y7~iY4oz*>x@JcyScD^F0d#{@EVwY&u zu6^9uZ@$Ib(_*YRK?NQag|E}-VzZz5Tw#mwRZ-zoQ|`H@xv%&0tE{(IZQlb6Ma-Y2 z8%86v7Q#k0u0$D^hNBJXcwL_T!y2)+`yh|HL-_e&DE~-LYz+WQv_0PO)Jy?mRwBaR0Hb^>$8LP_67)i)lsq zN-kvq9{3tcsET^wZ`3Mq89ytnlvH5L;WODkW^oxl982A*80>ZLZ-Ej+@Si~6pfnc_ z5)#@(S#}!2{2GAD-Eca)9TOEr`}WRVE}t)lW5Qwj#hX@vg@3?>L|Ilf#$3g zIohtXvO!9^KGkDf+^shCbro&xd)u?);s(@^{i`qCWY_vQlj9-ZdQ*k6QmYNJjbW>EnEyk zUlPqa{`6B?8r31feDT8YN3WxROa}S5_mn9rDn1?~?aDZFV)=V%01XpR|ouk zs3*c*ywz0oEoYIH_8(!}W{^}Zp#O%aXd~icrz={cZw-UC&zV7J>Jom`t@mZN-~21W zKR@|VASb&p)0;~ED2Gok04R(Z4?Fq2aDfXC^Sy|4z{Dv>KSWD_S<+ zDF0k~;;A3?JaMli4c>1YxTPas7a-e`?&OchVr|4A}?-(RJ+8X{^zQ#U(k83D~3&inln|vHV9vZYLcipza7yW#gy&gGab(fM|YlaD4P9 zw=YCA?qA985M=k16v-0r|Zi_t!^vk%b_w%1}^FCEf z?x-RR+;=K`lDW;wj!wCds>dGr@rcKLd6vTzdc;3et)^GM7+gs&T;gL-mHOYqL8Fe? z0M%@F-kcRtt!7p#Vqa&ELYUNYd-*`{)_GpAs+vA*#)^QTc_*LhPZ`Gp)nLI+w`Z9) zkGJHz&FY6L^`u|d>%+Q>1Gu2sG(!&`OaBNZXB1UTBU4e~`*Y(mPKbKg6lQ>Ey)z*u^B&ih4Nl2i__EWy`! zOa_eOV2?l(lWf_7Y2zlazUqU71X4VK4X9UfCSps;Z@$;j2b9U%%cnI334lv zYte^eWYzHcCcoK0Z<_ZatGB$PrnODBW#W1IUeimO@|%|of2<)Zg#ERQ&ia-Jes6`3ZIL@kIJ{E`2xpy2uePnR|I zUdvI$O3^r30spz8`AaO%0@Fqq=kL>%eG)q~i{zB+UW=S2W@Z0gUf{R$#%rfoYuIjz zzEK~RyxrqqxGwek5HadhkoiZ4&fQrN9pj!(*N%iNgM)|I?TX5X08h}Th}AKF$1QyA zwhVz?4*oRUhyF!b@7?W`cq1Y2>~QF z%2KBP&+Y&%w4?(J5E-w?+dovz4+T&)wh3lJ^xroKtmi)k7@F~Cq5XfVkdZiGvM~8D zq@Mm%?tlNJ^8spJt+LGhf0c{~)g+t)(tpXb!hj2Ovs#T^(6h>~e0#zE`bn{lqFlf& znYa`HP;JyjvHpEZxQ_l7tsbihB(tBF+4VM~$skKxVBB|1X!P}M@5X1id?S^($bvr# zbuvg(_O!?s&>}BqU6gGD~BC z2lIM5E-@BwK#MIN0+aO-U*V|ZYApX{f&FYy0&$#W_b3%*Rp^eA_+EERM6yqh1-zm2 z^Q#Bh=t+Z*wnjeF9B(CgVmVX>B1Bge4`RpAc7a3{PrE8Pg4US9zEZ*DpUJbCi=u^Z z4@0aN#YySf&`uW5n_m#NIPTd?eS!jB0th?V<8u?MR3!&8!E6JvzA^2Z z{Rq00OkbG~fcKL&u_M28jk_O9nz0x9@%;6_((~Tm4-DTq*OJI$o~wrLw-r<^C?~%9=(I ziBBh&Kd4W}FfW_=mk2^{9)>qB>)Y&|E65nUdrcw$XDqvw+5gPQM?Q6)(Y*%?w{moYXIO^$R<($Kj4=EfZyHE6BK_S z7Ge=Vo?f*nGs&O3pl{P%}|_9 zI;B>c%M%j4(d7R*7J&P}xc(-1Y7?xr-()yW2+6FgYLd(Og8cl_V3~pUJ41gnALj!j zli>g5jWy|tFML#evd+CRnfKx>eT$=#xGO(8P^NN*ob<87C-@U@+kN5x^<1z(EG|VW zc8nW?am-WR^2Z_THFnRFMqn0DdBjbF56 z=w3{gE-1Nj^yN#Y5On)F-C}WO#aQ5h27dY5@5uOz$-SM$=A5HbMC~r(*F|>TjO_7h zyY4F}3C%_cJqC48r>ZU3Nl7hKp(pn+zBA-RB_!a{L^0nbR$<}8e=26^w*-IwKKFT| zoP8gof*O;$6u%uoA%AoxS8}E7U=O;6Y@f~+Pu|WO_@!N={t3~g=PFZ^hzd4zN?f4Y zifvY)I+p)1zO(FeP$&F5YmU~b;+RS<{h!kCO{~pD+((UfT_oau!He_y&`Jj3=9c9D z5AOh}@!Qy2S!X*~xefM{hq%S(S5kHz>a*bw^;!gk)`H~elly_W)iLp%7nb(r)b z>gxdBWwkL^?59KS1YJQ+Xcxa#vx}GI8$F+M1*x+K#~Zs5p0*0rV47up+P>*0j}d|( zD`yf`y`snWUd@8+Ow#Nf!g5TTd-v-Z^W~m5)0& znS1KnmL@iu{opSul@|7*%stq*_vd}fJIUQ7W&I(Vk?C)R*ofUx5G6nhX zKQ$ssJl)1B$Z3l_F>{P(oJJUDso#^UO4#{VB$+ORIL+3i1!i!&`%_H-lT&vSrk)_& z&q~QF*IJ9QKMZB6yyN^!PpRtxC5&#oD)?m`-k_0B#m`+LTgVmuT3ajy`Rti8lZeQl zp1Pki5SNt<6aRyhe=DoM06Ks*AHs3|N9O>e?6p;e!XW+!LZmAK2;sc6{Pj=PYfA$_ zAYdad`}f>`>wSJSLhV2i0TtCV1b-MZMu->e*aWgY$2OD`EfGff?f4rk`7i2!ji>~O z>?LLpMaGTDV{$VYRE5xRZhA&~6tFnB`GUL^@BRj6nQU#fyRR^#MM1KR3x^*b8Z6@M zBWV~`DmKgfYq6A}M)Dik1>>wG%nhQg1%NU}X&uYI-hWkDdfUJ>Mo%j4# zh@QA6%j?!U$(Tt*(%pGt`t6Yx?4`2fZ*RY3ZcB#Wd+ErG93>@&dU6r;xBKz)V9JRW z^xUkQ`S1S;aw@ms+I=?CKMx#N55VvV566ED=ild^$q0mNyt-Wf6Ry>OR4`%QP_xO# z;FE7PeB{3*=9>n0k08G9Q2DZcQ3_8^F; z_7+a%o5J+<4o(DVC5im!QrZO0O``L&q5@T2XqZ9h6MW#LADOpk`JDIOdQ-BRj)i)W zZ9jTNSf&gH8=C+tXNJ~vS}slLF~fG_G00{$c5pe4r3U+%P4^{|QcE|ngYGn2?$8Wd4aP`t ziCzreif&JJDGDjR_FK-ZG;mltV87IOhfinp+J7(4!*nSx)rh6m_PBF?SMILu;V<97 z-wor_{gpH>I5VkhCq;$nG z<+ovhE0y!x5i1REA(kYf#fkth~!$AJyBU;+o1x`C8-K;kqWR zx=J#@O3b3#T=LdaaO50!%5~s{S2?D|J{IC3a+v6kf@iD^CX*Nt6H|VZl@IzcJWBD& zP})aUrCK5vLOii1vv+DQY?UGGIxro;jmhvU5WlV!?TJ98^!+F2iH-sv_qJlBcNK_)o$6HL zWcDZzFkxCH2eK`?SQR&sj&m~`_ zIua7l+7G~n%yU4syc9-S6hgkmWNHF$@%pP*GapYxBA=)rg}weUJjzD#HJVaHE`o~v z%Pr1O3xK>m1XiOC)keOITPn8PmlU){^VQ3uQz%8#)@vJ~_T9$|>TrZ8##Aa%QL1bq zUU|Il{?3trMtGan@JA+MzT9frc6p;IxL9~R>Pnf=+QHG2iaqJ04_ zNQLm8PqEq&4iHyHY28k$wP-t8#jBL+c3pxtIG$E7J#-#N^5V-KfUt;skVLpkC$#!H z=UQ#VFo6zdy*qby%3~29(sZr)sw<$Schxa`ILyULx+~uMO=DMbnD6cI&*IfL?#QydR`@eotcv%t@&j}%2IEI zWzPF@^(%)@1f(U2UGLDP%YuoVElWw@*;I|KZF8a;?a!(6(Kn3BUMr4NFfB7L!vU;O+PQBK4ijgG6JJ^J;C&DhbCqmTVCf)05SFNYB7D_5s zk6ZTXC!(g#|XhcxWH>Uyit+F!J-eu+~&~ z5q$3)HQV@7^Vtq6`H)&mDy*n@x{UWMQCORq0knz<*r#Yy)+^RAO2-&^=s2sdD7Yl0 zt<}(>TdDO-%WQ9Ajs|BY@_~oqV}KtoIUcR4$8S*K|H2Wp3Hi1WNPmegU#wKO56+#V zq}tWaI2*x{D}L^soTnCe%+f~Z57K6C@}V!U>8=EZnOx^Bp`saLW3uOU#(5m~7Oeb6 zt-Lesn)&u0dEiFF-lY%yYs0GnlzLjf3UypboW5b{Z@KWto9It&l(el|df3^?*a6Yl z+NY66FNO=l=H^4<-A(tvFH|_AbvwyAtyZ8c>%7s}(tQ6;hen@$%eP1*?6`#arU#D1 z590wB&i&wnV7B-(kEdT$3RVa@WWtl_N`oTvav0Jisw`&!@5tLz&=l~xOdurZ(BWt> z{FQq&$6wR~A;?RNPxvc)52mu#81sU^F5*I1qK!Hpw1+u;h`u*6vIg%CWvMVKAJ=w+NI%ZYb_^X*H{|-|#ogLj881Y&H}IrA zAJrep4#iF9b6Q-U*F#7nYhE{ZgfCF@_utGSkMKSm6o2*lL)ikDkSu)el6fhL_usvJ z!F=NNV6-|epn=KI*+xKvaD~PErA^`o`G~z~t%pi2`!x@B%74@;%&)G8M@e|N5*UO& zp1V@bRZOEZ5Ul1M1EGDS?%;S3nfyFX9AYJSc)o#V@AIrZRzHiWjh_t2)`q1btlJz} zck&B>%+e}b`;NMrq`1#yc<+6-i{qH8xqV5uJ!#dYWA|aE_VKG#Q$WKAJJ)JjZU0Ckdp*W*& zbXZrR)0EL3mUtp5HS4SLmwas5?!!Ma=n{{%uYFtSMd3oyPW{b_f`^z4dIkssvZA&s zBnUFU%WI>UOBNE!eN#^T8!;lVI5j8ob9C6{$?k(%1J4%t{GnZUfO#st!u?C%=#|3u zK8h(xDQILP1h2X9raY@Ojx-vqa{53iTD7+QiDvnIg$N+}daW-k{qCI~5y}kIY(F_o z)0(tPJramTq_~=FO%eO_SZlFOS(Vy)9BUNxfUL!bo&=^sZpD>PSyI8X466F-bCK?M z{io1Bx%Z8gB0JxxXBID;$wfedI=0ddNWwYS=5d6o;%S=P&MnXPE)6@~mlcZ}qb zw;yml!!XG%N^coq^E}z*p4-eNo=)QSO~4}a`qZOEhXEvh319a}T9mz)E04qlJyeat z?fs-4yO69V4=LsdDWY4rde`;BmOv~u{i ztTnaeI$6%^_BBGm{5Ic=215=LBFBp7A8O5Cd~&j$m(zsI(&OA5TgoaZM5>K)vbS!& zs5gRpS6Eo33SDlLn30DG{TnSjz?&!^W}1I~(6uWpvN<-%A~Djn^D`CXe})ctZ+n30 z*5$FnFIMp8ecbw5F^-UMmyfMuyteytvl6|cGQH!ICj`kW<$gg3+F#k@<0UUero)eK z?tUitWdZ=RDhE<(U5Hy{y$Y%GK7FgjHKevu=7W5pvc#wdymKqWzDVK1&iT*Xyfdw5 zTZXk&Zkm-^xQV}6jSAIvy%{k8V7)MM>n7IWz!g2qAqQlIUZW2-Y0ku z;R2jTExPF?4S*MzreQB?5Y|g3Gc(}1H*2xsBB%M^(uiZyV9Z;E5;vHZ$p{Y zZ_9UN*RgFr?hv&?qB&ZV*b9nF4Qn-*hJRD=pSx0*NbeJGk?(CE_it7_y;)}9H(q&U z5tn3a9ez(h8GJr(uBu$8m$<^i1zz_3bMS4VS}p#m&1gk|*L81|k?doe-e_UZ(*l(9 zIzvm`0H!|&NXn-737;>nUUWN?bP&}F(cY-kjsQ}m<&)AZQ`)rQWRaXg)I!$(cb?DR z5`Dw73^Ko=HqSur058uyQBdDH+x`_-`pqH(9Y;M6oqWV2dlG9UR8Es?hdK@ zO9I3`og^N@?^qc2eRo)vuD^}QFM%OSW403y595oYN>irf=E6V;XXi0>T2Y|CnCf?h z90xgiM(8o9s&>gh3p4vw`1Y5?1A+@*t0nsCv$<|An;%}2akXl?g*2DG?)&o;K$Yw6tIp2Y*8PXF@nP8YYYtPb zYr$S$NoKcEO+TdiW*D(aorAkeH8N&?vGv2s}BZFp~vu$p-Z^N)D9R<<#<_MUVV&}F(l7pzsH zjk{Xr5UQ|g^nDM*aK2W!=wl^GRk&n&si&5Qgp#-_u;MSM8mjWVlq;|KIyh3(aD>7O z8%_@S#e#^QTc0{U*)vh!wS75imRSwvx)ax)<|!XYIcu1#cnxAYl#P>dzHyFwd~adP z!y7^z5bP3R=<=ziUfz^Zb&KB203(EUtJ)LCF^W!VLhg?LK7!#5cGF3X>SV$mHwcax zB(R!Wq;Mq=jg>uucdK^P{wU9{ik|FNEt@Q4G@A^q7@Dp^j~xQ1#vqZnGK7eBEBBQ% z#}bPjFRoD|DFkPtrYC5!MwQjgC*(*k>cI*gu<&c7EpU;<#k(El^{ieMB&%l0b!Xk9^ni=H!7t72^q4 zn)}5MZ3BQGH-W7pH0B@48RJt#hoHx~UT7n7={Gdk=SxWg(x_A_wSHU69Kx_`X#r7R zT*$hCq5JC&5^@Ax zjEL*aBHQ;l50!4YU@voMR+=UK!h|!I;9>MR^Q@~QI=SLSv^1ZsgUoO)?ea62DR1x$ zZ=Zl&p3?PUm^7cw0jY2=N$PSGxwH6tbyt6skY*qOw4Up^_1GGAnjF)6wL*{#o4SGD zXddqc&`2}xb_7;9n>Tv-b3!wVuW4>WKr;-&!o4?(1=~xti+cy;h^&}^yE5-Py{cKt zYFb&>_V$K3wr$^eR_*j`UR7a>lLG>s9wG1BuQIh-;W*hp%{@hzs_g9ixbr>W z)hN>-%==U#d_(ZTMs`qaUS0^N6UQKwH2fl_+3Fp;#AE;)((3)!$?_%gi(DRu$IU~s zXzJ2N#{y}(BO^?`N&Fv=9~K^xo`@s(uMj`UPg093MRP*<;I!8@pS&VMx2@+1r1`#N z3d0sjL_4ZT4@YL;PpgM~l4E|s{-jr!`- zL=kahYu$9AMx!j+JIsv$+^@qYP5W%A<5KD4;)=#qVb`^gXs~Oh0mIFmts?jFXrLQx%Kre8FKntQW>>oVDFnQha?UlEDU8;V(yOC-JZQYa#&g+ z2}DHoCg1l&oN45MJQUUy&+ubB*bs>iKtIZbJ{lK9Lv3^+7Dyb$2FN%4_~JQpe@JLL z*x}-JgVt5cpI@BXBT z`$_q9_NjOZR(yu^X}nLnL4IlAPRyDD9m#T~Ao=QJEisNKu1k-!?8D$0&=c(orYVaa z3*?hQz8c4Cl9Uu$i(}y*-q7QY@XB`kmQRzZ$0JqA+Hk?gl|3%K@(@?pcDLo{Xwe}q z=L2Y|sMZyc(HARQU$Zq^iVaH)f?I&E86XsuVc-@ub&iCbT?*3r&nOc-iSmCa-MW$w}hA)K-nmUbr zr0&Qzkl#4S-^)mt!EC``n+pr@!^Y~PSjXA+RW@`vei~{Z$iL;a^-Vh_#h4nGz3f>D zK1FI=$x*OU^Gbd;w_)0znhy*jTkf3Hkz|P{*NA*B5$4dfWSTL(O(85}n2crj z9-+8*aUvH>At;r|>zvPmL3=Lc!qUv!Rj)IvK`*pQa`w^D?>om=4Y%8Rt~LOu?++>t zpLOYEg+t16`6r$FHQ%d!CbLs=>P~IGetew#1;|OS*3q2YW7iRnbt6U8LnkDW*Y?}v zXxK&BnM%mGtg-B%=O|9auJ+1NB4#_5)6A9lp}Fjma@#P_0Ue=nn1*Iw>@s*X6RGYX zJ2=pa=8X>yV6L?6H%OoC9(R7Wg#JjdJfZ*+MnLk^<5p+9JzUDccWv}(DfE#+a4}w| zaFN?|5>xtmCX}N#Cnv)=x4g>}Ei0`5WS8K49Jy9^VSu&4RR`i}@8)}H(4k*Iy#J8> zK(@9|aoMYYPF`Ka=PH)>GFtQuEpK}^kC;irRbY_hj^cgWR+kSLCQ&9H?=hC7)3&2@ z4&jclH|=#k$Lp*gND7XBo*PvJP^}3PAQZQmprSX*&Pq2W52%{iBVw zN5sMyBki`rEdCCM@BLCxek4PqTt$xN!?viM`}HZ{5j}XHve&#(bDwTZZ#JKn-&Q|c z!S>z#-0)$3hfy0*swP&c3IdZ=4~mJ*U2t%exAgX54%3ydIkj@zS?AqMJQA+<+r&7Qa0YcvR|+p0aD`gmDQ>2K&++8Sc{WZ512fw{Xb<7?!oZ z(*jS{X{%SBPO-F(^9g&=;jKqmtsi~@n$KWVT^PnI}Rs6M8u^YBC5&=Xc$|79)tRe69@UrGeqt>t z7b?a86#ilt_K9y}HZdoZqY>0R?(fvRps%G{pO zo6lX_V{!E<^MSn?sf%4ABt@sXHTE*i9<)G$h^|Hx5-Hi>`Z}MgMPLeOdtZGF5h}?g z-JE_UFzTF029b+IEG{uRHOqmcNuup13A}mxv-Ib$jY-Zsz#@J3E*KZI(rL^+>08E!E9Rz%nUCn&tH zHDG`FK&T!Cduu&HN?ziQM-@gVN111M$IYB;NXv9cYOZ5y19O4&X#{Rr5`wSk&e+Qs zz_r4aLGZt%WZ_*~+$sbD%A3Q_Bl$D+3#mc!p$oE9(j1SD!%XeFuf(!VdkyKAcyl?Gsl`EHol&OX0?63FlMA$X9-c+oQtbWX9d2jchMByvhxXkJ z>xX-T*z2gK9|;$VLyv&A>RZq<@h`A!S)U027sNks*j8cBdhA^dt;Wj!LYwwbZ!z}k zMZ$Da)ibvJTu*5vS>FtUiW%0Rqk{9-N3_<_zN)p{8my|Wt1h{SL+`x2HWg~UPT$_h z`vCRA`n?mvPFuoe>Q}p<+ge?Ev<9`~WWY7bAU^&lIi08J@=QS+Y0%vM)|2;Oo=REc zppOERrZYrY4Qaf_qTa0CI3r5bC1A-9qxzgn}J<+;{gT7RAC z=a;?MQ|6khHNyN1G(2PR?dtIsjx0qbZJ(&=lnq$dSr3_m6PeWlnWIPwdvuYZ_^L=@6y z#GK-0kx!3ei5~w-$5)AS%9(DKX3uvuMV>;y{k@gn(VM!Hv#U+_mQ0>@b5x-;>dcsXmBedE+8uJT~6Y=LlU9w z$@k~MZ9n)Uy%dir9!lT!Zc**!7uj-3+Xy%~+V6jrv!!k~JZbC&_;*uTe6pwpq?@#! zrvW0f)LAa>0S*fZL?}*zTbL4}Mbqn>*+?H=>HA?o8dDA7M|1^0?h~;e_aFD6j%wtT zW$!%|dkVCSE|O$8@n@b3HfDOHI=qqRvpwm&<9tx$EFW}#F^V?LYcn#z5w7<6SwW*y zc0!pL_$^jsHwV2?VLpZ1tO%$^FvPXsz)GogKP=I_UIoa#(wnD==ucHy=I)+uM*mg? zh$Sn1B3}~N36(qHCt_g%oe&SO!l37K9uxJu31M0n^OrzlZqYzl7wxE{&mjK+=8FuUAQh0XfjbzI!`dwwE>1y`AIYX<+ium`|XKF9Qed z(124f=;gcNnD0%6QA3}-jeMLM`LK0rX$U83RT1wN*n%#ecD7u-L=JN9X5kkVhSA4c zs}%Ka@hUoyg*1qfUAD5Mj2Q4!);}JtGb+lf+HmUTWl=3G{UBxB5PyRPr$W5Mc%p7k zej)$zt%r4Ycf6q)Tg=yslL(HMD;;8X9R?*4r)b@LvpD$r6=Y@4H8lS5{MA!(=;O9t ztrX)C=GJ^k2d>dtd3+EtkkqU)${Hz?X&l*sTsyStVYr<8ra)gTy3P=$S>@vh?`pP^ zNPP8dbRESEN)TKWPUzS17Tuic+kz}weBWinoLFrdMZ6x57Os#sc(Oe>OT(0pX`DTU z2|pDM_u}jHU942F3YsYy8y;{yDwC+_=C?wX@482Z*f%-xL~EV7W-ItrToN6fG*+A@ zn}ezH4Y|c5w~2s05N7<@8^o4A>uXxbYpYBF zbNvvk#C&kv{s_HwsomL@5F`Zw(Pw97>7d}Hvox|G<7~9=BnpeeR}B{AGiPftVG3pd z35xmhr-`n)xSjB-!X}w!+A&8_w(?S4yVLgAV}0S>Y)M_kJZbhsicuIlT3OmhJT2q% zC-+=wWTGsYym^<0M@>MD(>sw&qh(hieL|BRHs(GVCg!|6sax%A(=MOv2`g^o4 z$emB+^smEjcmOS0LX-{vK@$sfjpxlRNZ|_K2x~~8KL%4=DvKL3kJt9FD*JXHJ-Wi| zcaVChsN=KgUQO%oTQDC~IhtYv>RgnG%)0YqQ(i6|SLm4m^~sHcLVlFAL1Ob^hIx*} zbezNP1m3f-+oraQcDuuu)r1aaxw`yx4>uR-=#h(=fyTLj!{J~ywJR>?P1>QBl#VIq z>wc{J)Jrlf8aujILOoO58f-#N-*fzDj*bw$eNbw8-B*%%+avWe>Gn72RS;W5_b4pc zPlD1Kb1E+$ce#^M*cGjNyfTcOJcQ?rkN}r1PxI$LtV-Sx7t0j)Pgb11n{Q$hnOB-~ zlOCC6fZweU?q_N(93(+KC9Z#PR;#cQlqPA`tKw@l+7sd*RDi;bs#Au@rkc&rO@~=w z5}@aC^3kiexI{ax(kxamLWx#kuf`yvRhrGtYj{?v(6iko){29IQ$&O?G9zwy7@9Hg>lDeZN@~2tPFZ$>jF{A`)Ts!cP zUd|Kb@7ZVU`K7DPDW+{SI>#8C&)(Q>+|+`ZpqCl9B~&Y>ppxs79~Z7R>)4dejRONH z5q%WN(0RqO2U3z*AnJe7Yf?rz+Hh;Mm zqYWY2SPIyk<|>-q6P% z*FaFz^NFItZRz-jZO7h{DL7%jP`ai7Y~bB3?)}>4{DH5|QQ5K;2LwKE*!tGkSNsds zq4xUgv3v1#gsabpBp<8wEkDx`#r4`k3P-j*ePSFpt}W#7?e*x5mz#m0tJ8g}>36NU zY4LEp%ROZhx31i#l#4MRQ^V>XHP=Z8yVBQ~)ZQZ~5lKJt-nZ~t6-Tv@eG!D7dAwer zoK1FEDx0d7FYfAo-Gi48ug&Cdf5aQV9L}#FcLM|0ySD(DktjpuY@hSTgn2DZ^1u4v z+Ds5erPq!sRRW!UN!swvh;G!$XF32b8GnyOXKR6xGDdUiVS4oMQ#dXfYIU1Eu}stY%b zoUZ;_Eh$Tp(Lv)g^);M%3`=(~8P94=F>*Cw1`>L!*h@0@dnjOFs+~U-pc;<{MKv z*ClEKH&?W2HY?`NjS7kl_HJr3?Fx$W9=*$(g`Rw(N6uu6uow5K`rH8&rh#O6J&K~h zoaqrh2hi-X{fzb^><$nm7>MD_`sm&5?9-C)IgWWxV98I|$fwxs@x4F7SM~SujKHv!$Ah*-_5bqZ;sEyt${^w&*_ue zk&;kUOT?Jm@f#xvSlJLhNIzD{DL_*tBuqI=(?B zNBYZqCly4Ib?YWw+WN5W+L#qfJIkMKKDSlA2}L92gi&}nrKDZ{&V5dF3@;g~JSXdQ z<=TTIoWZ?mJ)5FPMv7_fC06vrc~uSij#_m`<$J(!{oUq>TUONe^)$PyB39&efjFbV z$OsBP=m5B}7#<|h{`<8{|96A~MbK#6_jw2e2}=e2<$ssYZV&%ad z6R}b*{LihN4;=f0>y+m{h2!}fQ^z8cwew7;v#Gl4!Trfwu_=PyLlprM7e^)wt|{Wn z5y0hZ@&W$;UItYr@z;594@YUbCdiKB%nRBNPT-zrWqAN1cgU`sLAc)eKzV+KskHw! z-XMbZYZ%P{&YOu9bnzC2`@t`NzmVxCjB*=!^-O{f3YKs-)?;jW_5FP>?7)+ClS~Q@ z#Cb@M~1paS}AJTSY3~KB}R*(i;2wCi^m@5AoEZ zbPZ`Gf*Ke$&gHBv zP@PR4_B&CWEm1*=bqL(WjpltKZv(|#jgd!fAK;0s6reTgmv;ZG za7`#1T|F}d!nN>1_tI$dBrz3}W!6A;E>k*OX8yhRf2W!1z>XPSS&CeHtxpovj0B6E>ZYUlI>VzW%F z0y>I%XAN@{_gtyvNN&(J4TwZydhSG~^{dk%gbB?QuoKov?oF5ZODIx;-REsC`A{kI zUhuEp2NdZJg{Be=6V7@fO_{*fczfkHCx@BoKh!ARwts&V`OW|&q)z9kMtW&*guxF2 zq-aA&$uF%qlp-mi)nXp2qLh(q+QmL>!GE{3Y#Dm~C6OJ{Viu-m!4uwmEE z^dI%`e}M7@2_~KDM26UMJ<#Ws<|X(&>y4MM_L_>?-m(7qezu|K*{-_rr&cg&Yt1Iu z*|S+e&nqgk6mug4#VqGTn)^y~ILPWwOqa7(Hjp&v=}PmmWLFLv&R#=ZCisnuK#GV9 zb9$ufzye!+Ov%e+D2cNpYH4(YWY+xV#tusCE3l(!h1gW&v386GO7Tfuj5Vug z89Gddb(fn%d|TUYFuQeDIQA*S%kco{3FVglt@jDQ5R+{S_N-bFj((uZBA9KM*0E5Z zqD#Nm6=F=5yYt|q^(Ub5!_9e_SVi2&h!w^O!w-;|-05hZ3=R;p?oZVod2jqzAsv8B z>WCGPo%cfIUFevN!syHv_p^GSIWFq0cJW0}LBM`-j6hWgR&Pq7+gzE-S!g9IyG@`- zt;@T){;rQbtVh`WFByQu1FJ>YZ>S`Og*=zAp;Bvvc^7rQahz$HW7WzRYePZ!ot8qk zYkcrXk=CX4ER?51E98>5w?t@vObsNIN~lx)gGmQU?Cs%v)u|?2;hOdyoM3pP9uszN zA*OmuVm)b;-AG-1SJEfNcjv=Q!^pp#=G2?WeAd`(Q@O0I;Ny*Hes8``;~5b})Koep zheGNqnBQoh^WB&$peY$v(4f4m(!7Z@+Pr1gRJitH`e?oh1~0N@VZpP=CLV4SU&Zq& zYH2G6g-e7vbw$u2>Cv{|Eqzjkv%|mzHHkmkT#Y$SlFPAbS1)+=x^=&QU#J1ZE2@{N zN9pkUEhc{fG#=JrtLd3<(>bFV+SL~u*?YpU`O~M(-!85XYQganG#|lryIK7b&ezVl zT4ue6ayKobm<>n+hDs;*fM2zk)7Xga6b-Z;C7Eb1Zdcr)h#J-oMUZ)e`N%9miaYJw zH3_}~KkPTV(e?)9fqf11Ea|jAxx}=7mzl{2ja`$a2;r8uj0Y;CaaN@2_=)p?(MgU zLaQ!p!uD7R0Kf1xm_P~ZqoAXB(Es^nHv?60lqYE5yXx6gE`%5cVv~x--%IC45V1Y%J@qBKl8Zq%UjBkJhhn4^h%AJ&`r$WxDAZJfVnH zjyAj4>ScjdCaM`063=br>?+Nx&RRlpEt2mK4pvu6xNH+zOny<>^nxd@QMBAVRulRh zdv>h;wqRK3K?hS0*5lw~$0K95KH$pYzt%Th7sO69Z0M*13wWqwnbAZLG zLeDnJ5g~@RRm*YG>`;zGp_7^#GB72e&=67S--QMEDG0U!AP+lLwMy)vX(1W7rI*9N z{69aO;7pj#)EegJ_46X-KeXhB`L)bnh1fNi_Ufjs0M|YFjSYs%n|<9b8J$V|k=+|Be*Gwq@Fc5E$E;I8q_ z0STo}Qx7X(E4no=R_LlZ(EopK`|=V3opU4yA9e(vPNOu^&PBoB40uNFl?8~c-sbEt z_t}3pNz_KcVvT%}U-tw9@3!yF(7>A6Q_x1Aiz|J3{wX@`U8rL0|7qjQ1EJp9IDV7T zy|Sgql9Zi_WE)GA#Bl8-yRs!AGa)-|uB9+&vWxJJCCZY0kaetClI-i)m$A-*_l)W$ z-T&sVIm_>y^E~H!p6_RG$lzS3NYQX*p)xJK($eTO7uKn{>@>^VCOg0rO;dXkGn9o= z)Huc9A-v8r@sBIuNPK|LYP|%C@K4+~^Z65HWiETFbHXr($bL(KhI|+qhDRLR3(2g^e#B&Q@QtE3FSlzvxGE!P! z(VBVFWGZYcN}%rPyJ4z_$Kd{2vo)l4cKRRbE7&Zvf4+IyX=grY?~iDUUW@`_Du!IF z($^$l8)9?PrIPp4Y2WDSs5Lh44ynE~A72DI=9HL)IIVrvvPkT!ku^QlFHd-5T_i>XO+>EKXS&15>UF^XO);rp_JzFSZ~Rm*<=&Vz%O)v9?6Usn*TgoIHX za|%Y~p9PSnC(D;+9_8s+jiztIv%zIA&g8ig!KN9f3N!`h?HPDUve>)ZPG z2gk(Iy$li-O+#Kc3@@J3?li9MtneN&mJJP1SZ%H5@0r97O{zfyjbF&kXpN~{c3Cf9 zUOCMT<#OsXKN^Lz{h;_C^qdJdykf*Th!$_t=1*DLtz@%!=*< zGyvry;j!i&4whhone&ZuPpOGdL>=qrMi!!Z{wiqs){!fB8!F7)Dqp|v_%PSxc-;LW zXIM%Ff0+3jgC1-@-K(0FPcN_dxLc5Gw27fjwgl&T6UDecz=_Uj?=LL0N?FQo~8#W+=% z>D8yGs0Pud4O~xR3w3F;iyp<2wkL+=S(!Y)So?g_bd6^1R{n^O^4Svb$(wW^(s z3gxuzIsGeT4Y_ADSY+P2avhVhP5B%t-YRvgPWORs@O(=&y7`Y+=!dDmJBF$|>Gf|6 zB1hIN9+7F8P}!Xf<3Vle#wgRq0VAFQ8#_VjC13Ng2u(Du;eyKXoc%!|!O+kccvLD@ ztPon|pwnE3*;p{vwf-y%ZU)+PbLQLGD}tG=zTI)#hVU(B%g?A_QNw7m+irex&A^p? zFtb9T`VhW5N7{2wX6EM?bx4D41VIz)^&aPzDAvjVfs0k8^v-wZl`49;=9JHV7KLwq zaInpP+Uua#NdosU!Mg3eA)lVn-(%*jjid>*;TYqb5yv$;d=K+&3X}^?0Fml)eTq8; zDfnU=B5rTy^oReOW1F4D0*K!BY$o{)RV?iV7K0|^C7u&TkwR)=r>!mC|C-ty)8|w> zAL#nd=$`QIi;BJcoV>R8sJ5`JEG_2Z&%OS=kxDO60kNXqxLfhLXMy<_fqQNJ)A+}G zXMp^*EGj@r0zPg0n~nSX_XDF;Fac)cvFg@6$H})QM9hIOMY(XTe-Hcb!Lyl}vq5qwl70W+{s{)gUXw2nKoQ$} z|B|ABM6Vr77nR5f{Fshtnboph2DQ`~{Q;+bnRsB0)8!1mpu?>cmKc{VQ1CNz*-dO! zBn~{A_4eLI!MGZE!w~mQq*WZL(X}x6#NkeO>NdVnz()4e z!`7!irmAP!BqQq{{s$^aHW>4cK}unG#VHMHhcdpN=oPX&*kwM6*%?gaHjHM04OrW% z_I+-l+dL}FNiXalijENk+$5&-&NHDyzK^w!w)Q?OI9#8tYGG_vL0WtaeGkkdkkQ=B zu#srKz@`aA0b5~1r*H)qas5OS-ZqY3nO3SOBkSR5V4yAo#0AmSsqK!1P&CnVV;-5_ zwb2)qDcRsWv$j$fe<#v^MRG=6d*r}WV@}vJNqWFgEGoK201g_u0rvtcNlc{gHW6KS zWtCa`)bP&M+hs(|C<1F1duO|AxdQ>vhG zQd-5ajMykW-m1(XVlezvQXZjMLC|hxlezj9O*+Q~I9c%LjV&hr0o#lEW63+ZbOYyt zk=ZYEDt2n6Dxdpq$wTI!RKc1y;S(=C6PN}J!LzOzh4d>l@S-HQ^;B06RNaEWSPvm1lPD_IoIaKddzAx^^+Az6aIA|=vg|oGBD6@ z@vK6{v!;_TX-bH>PBN>y-cTGXCSxh6RFB-eYC(KKQ9SkFV<6en;M{3k8><2Y^^V|}>2y2?6cDNkT(PlQQt-9=L2wM)*NKrRK z5kb>(d`pwBDfe9urrBMs`v}S(k@JX@HZ$tWInCfFegEtk3vvGzV>gwQzjxdoWn~?H zZ;8FoIwU1|42}b+2KLK%N?DH3etdgg$7=^Qhye{xw}2vzv!K2|T7%5EKutvq0rFoTekm7u-WVoz2g>|V>!{6y^Y=x{?At1^dKN~aP z&9xo6T+p7N+cDD<;p1JxCbv$gj=YRq;};0+5kj{Z-i~XEn)v7m>_|zrA6_J*(KBLh zBe3bg;&SI(Q7e)h*(372a`@#uYy*^EWb4DBy1Q-mU1g=@2R2GGuTc$%eoH5slos&3 z_U{Lp(|MtTF;ArRdV?r{DWhu!zkQ34*_Bc3(cq)unzjtrLb{5skD^sZ7UW>{&zV_- zt{A_gjRfyHt3l%%B)krUh$-zxHnVf_cL*V7_17V`5wj+_x(-h0g{#H-=WV{4(6se% zX)K`UVNh-A){M+;(*Mqx5K6O#^3_`nGN%&7 z2@i`2qZeTVr$W^-CyVPEy6lDJ9 z(o#IVn~KfP_s|g*@20Iy4S{ke1xU=UX<}%dCgaqTBPwn5yUiP^-4^xXv5K>aQ-V`= zULIe3Y!uUQy;;zRj3SSG=&ccs6h_Xn(Kz%7>pM@4u+uX3FMh#7ki(KZ`o$Y+#U?I#E+%z;2_Y1J`9! z{e>)^w#IPn2ME#YgMj)!90Kx7d<3ED9hp`UFRr{7rm-8c$($hV37)+`jNM+`Cc_2) zd(=m8DHyfMeX;OJjRq%=YryD_LDN(FdlQ4LNe-dVAv4p7I-#}~QsZ^!`zG)`yqIbp zv>I4V9p!Kt$l8;XiSe<}$l4}@rlCYHx!XH8Kv6`Bn>ph$E8F)*EKJD|lR_GvZ%*H& z8x&+C=#A`N{!W`_q*@!BKs3#z5Uk_=u*2`GInBPx>sqzdrqmh%q1ZI-rzM=wE;wUf{j)FgIZP=)U$=z zE>12}V10hJT&9%A3+wF0o@`s&0icFAKlrTsNTL~hBFifRWST~6DZy;)ptRr4XrEo3 zEGc0?27{d`PVgPdhI>X!PRPIa7~o~`9&xB@X4(69WE`G#p7xz@2^8(|P4D^ZhPe+@ z8OqYryCq#WNwIdguHeO!SGH@hae~PHB}q%Eo%R$$@SyU>0#e=a1K%d%&1ej>(6 zcQ(uf^r=_GTU$kC>~Y^6=KfLuc~#GlsJQphVmF0O^XqJGZrD!%J3u)~-bV7CFMb30 zpuoOMgCPJTWltgtHtI5YqbABJ@5@aAP6KQKT}b26J@OK{x4=^S`#Z{Rm%!Okfr_(w z_A{a1{iQ%6XZ5yi;rnxI5i$TwPh*dp{+ry(=>xh-4ki0~dgK+ElqSd*{o5fV4-TQt zUbxk-Y4icgzd;@?85Pm|+cB32jycjCsn0q)%f87tpbNIL j`ULl-{Vk93E$zs(Z3b4ZFNy)k{~u5#jcaeOT0Htcd{!qG diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_2.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_2.png deleted file mode 100644 index b7bef7c20e3adcabc62aaa86ed6ab54888c540ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29202 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

XZ+?_Iu;7s~Zl3}oemo?7FL z*7j3&HfNbvXIib0QT%727NM{;ql42v4Z!)KkTwdC33$0=*t+|}%mTFuefiX0{#$)M zR6aEgaY1Ad+sW7v;tz2;tC$E9-5ZuQUmboq?64prZ_SUNjO~l7(aEt~>YH;K9YF+D z9!Nc?82ABC@-V4!T;cjuT`j>~v6zJer^7z|p(HMsXgt+#yUm@7*C8xK(LVaUrSU`T zhWMLnTHWDIZF8U{LQG`5^2x=vvq*JBwo*R-VKcWr`+6H>r!6YA9aM+Ls^t!_rM~3 zgfm0>#ZM}~9L@{P1kO@D&!4gimU$yG`Bbe28veoTLmP#8^RCg%DBU{E(#KV`)2!rB zOrDJe9WTWv%;b>zQMh+kN?#N|{yb}(ws$u9EU5tyS`7u`yo76kd7Zc8F&a7Y#&0Hx zTdn+d7_ovgqfy_K8fzPzG8O>Nl)6%WSMiK_&dkm|BEIdtmGluKbi1x&^*eGpId6Az zU}-J8sK}Ki_8FJK7?gm0Vk{^mBlu%pWUi7@*dMf71`hEG+g#{&wO?iV zyL#;IQO*FTxiY2thusFm!DULa4DAQKm3EY(oHgHjA`)k+KUHRemv)3WYo;Hi5{p8$ z^&5k9ry@=8BQ93McIe82o=caiJ>sw1bwWn@kzt!ybgA=M6coB8$40#s?s$94OCl_6 zJ|1&L24bzIgz`5J^DJ(D)zLDkqI{&mnS3g#LB<%N0gq$@d5xk|k_<0vMOr|*i1{mzh$|)idr0}9p z5MU78n**h57@v-#T+R*)#Wyz{grhV=u9|PB814#&o(gy28k9Isir$y{@By13aOrh= zqyebTto9c8INWxT{{5-bZ{Jkk1`?zF*fIkL2A6eT-o!Y>W0tY!k=17ezYpc_*8Ioj zfZ)?O)26PKpe&%S6?Lq;f<#R>sVJPk)VWZrjkyBt27|xyu zQJ?_P(|_0s$oF%9TPnr%j;O%NzJa5B>pwLzMqYk+BrC|))@~ZT;Vb{~WqOjkLXCB7 zc_Z#R6!=Y@32)SfRWdD4oHex|FA@32JN)`U^9;9+TiYIX=@rIRY<|{d=6Sz+s2i)= z?VJrvm&os+z4)*)PSDBcrx^Ax6!>co6`3PCdUO*lpYNqNZeU%FHoo8j#zd1M!FSgR z(hkHfowjTscpp);D?xpbHfEXnAh?_1smw9d*`OW}_%)FUO!wh9=KAYWww%&wtA8?C ze<`q#0fI9{4XFhqjGuaqKV2Aqtj0>H%!U=%MG%l{pM=l=18E>Q50{s>?r|us)@bB{ zrSEs2;=_SVj30Ux=R*iHV$%Q8CLoH|HC?Y+kE5HIlUn2m8yuk2Fi1G=1`@CE8^qBL z7yU_jP_q2wKh&nH5;lU;slfCrY_WJgd&B+afGfFY8)Wt`sb9Q-eLyfp&TC zwyis@i#Rg>$ZaQ-Pp&00CYq@K{W2Z2V$yVPNAe$mz>A3N#Y$Pzl)rTm47A6%%gqcI z`I;OQHIq`gwLA+LxyrOvL|KN1htEkIy%y33#3(>4Gh_e!jd~51x-|cGkvNhJ-BH+VyG8QZKhA))%t5TjXo< z46+BiN!VJAsJ?v{8XWXbZ1A8CGBP>PHS8Vhfiq;%$7^X7*e~)k&)d9^?*l;aUh?~| zwk#dql{~#WxNMnWKIj#Q{HrEJ=XG)7kgfAm^m&5JbE;=cyHuW6ISN(fCtWLKE4MD_ zT+(KCc$Ym?*S}&Ww{8AV3jSeIr!3q5<%7Z;qm?*}L^Oy-bJ6~vpkEyihdV>;uF(bS zC+318?X{iSGe?mAgOk0Kd*(&j>6JU<3~UCqIu0iU)A|VqouDI*D~DU%eiSRJs?CRa z1@>4ZS4v)&NI7T3uBWJ_vxTkj=tAt%7TTF2A!x(6r*G@;2n&$U_yNRy+|^F^w!*xS z-`KuU;a<#$VnpoOFL!XO0AW24ImP*Q*A~vxsyKanNWG7T90{)@pScM_#_2Kr7ETdY zfxTzrR(e@WUn$syL05GAVGrT5+4iUqpPx_TuGI4MKz<}Bqh39WA5dp!7WFcqiicT; zIyEc2Y8lKQDFGdKl|!lXr{)l5m}lwc6SsWX&UI)L2*&)jw;A{0msTHAFR3Zn87SKe ze{{NcP9qh7jS&Jk06keWp#vlV)%I9HKzP>dwW1uj6^|-NbXK`n;}q zwe6?8L5G8H9qUg{8eE!R0Ka`~bSAqck$l1A5G9j3CLuoEmQK~QU|e(lM>LfFGF?vW z_)gb<>MTT8jBr|e1`L)0A7k7vjQR&s#1r~*b@TkXrxuGbGTl6DuZ5Z|?c!!qbzSEx zgC9%$zRv15;SYx_ouX3?#O{&g1IFP#PBn(DMy=|J$y(#;b5@G?C{L zlFb>HfkaXWA-{KVZ`x3@rQLKz$Sd`|zeuz3W`vs=ahiu2&tJl=5ER2CrfsPos>A7oVerN5M``!uh=Jk zMLfCl8xP}n|SIX&n~a(s{;FXww1u`#rbALszF>?={-TgD;o)6*8()Oa8VAU zEdJ8n@qVkYV~%wbbeh!iiY;lBw{?bRmgYHNGC!jP0Wf)+HKNuaE{*&NX}{vetCqxZ z@}+W<5yQyaTFo(}-43mKp@2TX-;iAK&hRa4r?Ww=ns@=T`{*R_aZ9rm^j~DgKK&H; z*ulZ5jmY1A;8ub{q*+#MTF$K~JG1T2@qJgo9hws2)n>}uD*7=omG4xdEff@ta59hv z2pu;W1_QfQr-I+Qii{|;_q;nZZ14N{wh~#^F5^Q~Glv~ITQBN`w#`BBZNR?F>L+Ji z1_8e2y0j>2Xsf;e|ep!FQ5;X&ZIsO!$B?&#qVulBQ;u@)(oas*`#ew7Yi|fH1g7 z%;#w5dT!4BOfKB&z=6r)+R8&HHG1C%9a}I00iDH;e0 ze{X7>uZoP$>)9syKIC?DO2>^Y>XYRPNAhk1Y-V_MHb`=|s6djeAx zlsOqKD7Bm7cIWfH1WUpOR zkMvjHc91^#5nD%rqW5u?1j8P^cbe`9BGKgI@Lp}BZ(>hVZyujb2r!E-2kvs~Xzu%< zm!BE&#i(27rPhnx+LXGgTVKZ`Of|}0j+L|MUKSR#$W~>>FB8Bi&4XMBP-dCj3lnLo z6)~1HE#MYEsCev|Ce+O40{T8kD<{O*N0vCgIv_jT%8I)xj4JDv4wbt>U9D!dMe`4c z_Z}7$Nc1JGaNAedtLh^yUfA`s*Mz(Ky37oYjcefd4*g!o(nh?mrEYVW)6*@GD=A z$^X;n`kVgmE`$b=v)2N?w^|VvF(4|VtNCGc)PBH5C<}U`hV_mL@I9^`55gcz1Zm1y zw~T%^-1h9MS!bW`7;ngtOPVNJ{GR?y=l5A~I&xkEf^Yg-U6@&21z*0iV^SKel(!fn zwsWJB$9;cUN2PG+HyZ$Dh98r}onb^24mqFNcIzAe{88sC_9`a|pvg7(fU4xy5MJBv zt-4|7mLOa@__&9u1?oYk_&=4DXd(gD6OP^H0x~LmY1{^G`V|=xN_VvlsEPfvF66%a z2Ri`5poHI6ajp4n^i_zYR=fbGwmV>->iezNtmRbU38KJ;iEJKIWwpRjLNbAC+=u0vbDmd4Cl+IJ8WsmfY@T9u}2#J+^08 zeRwZ3Lgp_UQ-H!8FUjIX1swd9rHIq`OaM``vsSi`0nx-)<^IteTS{I+NU5Ao_1|f~ z47h(i2~gYOH1+sT-(QwCPzlp#&Nliz>hBp$&*Hg&a{PZX{q_pKZHyvQfvJZ3tsr`x zcMcBZ7cKxv&H~y;>5p*ryWmdMlN4y;>&HEf_}nLZ%DARDiR8p{L1L^S+p%yD`WeWOnB;HFvbx@RCG#H*qz^T-QU2L9UC&ps{!Ib@A`Za-b<1sP7p^}I9KTnDUn2o} zSa}!Yl~3*M2i^0H`(a{^Uv_)D;+5-c2 zO_Ukm2fL3W-+a9^pQqf%4^!5QQ{5^@` zpW2t*(IQ@($!Buz-}SHT?OkD+tJQz_nZm^DZmiq!L4;P`cz~~3XKy;AMi|hcjB6rl zgt=Ajtu60;9FO<29&Sk!`elgVJ^43pytNaY7a{=jdWb_eom%fp-$E45?td`Wjoywe zb(1rfb^6Ak7;xn;vZeWmE858G$!xh}2cV-Vb$P-;dklGpLJ!O`39`8HHap)DHl`)i z+z=NxM#^pfw_WlYa!-o|q&Scgoge|fdJnmo^tH6)X>+3$vH!T+jSIgOLHjnZgY8qq zK$r7GR)tcCZND=g@sED4VDqo~c2vpzQ)=MS#}9h{lUyT;Hb`EU1Dz)r#x+0kgc1Gi zjS#l8p+6F8@g>RGrZm=O_@QBMEN`vLPXcrVYMj3_z@R^sHQHnK=@C+w^{e{Zw>{-q z{5kI)Kj6QoU*bJgs$Up4JYJwbX6-g(coS6i2U-lfwTLtu0rk(^G{;dkhJni+;82d< zZ$SHO8`;|O;xWq&%~c|TzDro-5D{oiJj1_Eh&_}#h%p&PiKjt+259X(Q{+)ZgfArI z5FiF(6<(O0F(q%zN`IXdeE6mXE9zLWxBeKq@%j~m?+sj2#<1N1{Y*WNFWbJv;o`5# z*i7TQ0g5|Ze_idL3oJ{9YX~h5KO9%=WH`|)5uygvoN_zY?_T57o}@T+VIj#x_&{fj z-rKfD;?8WZ5nPYe3A3GSd)E0aC}Wh}mga;_(B^s4M#(d=TtgGerQ_LD!{y31ni}#2 zuvX{Fw|>_x|IpxPd1BKW2{Pido@`Ah0}#_2>Q66<05cdJIto9z60Z+@H(}q*XKei< zMMLNo*A9JDh|1onM zz~kg;*$lkXRQ`7p|HjQ*Sov|a#T5cnDh-l>w;r;p1G62ClrNy?i~HH$!u0hVmjdakja1%G5>2j z=E|4@Djv@Ovk6NKF-IIkRUBtMx2vEmeNgSi+$gjC$Bs1`#vIqNI(eUFN0q7GI&Y<@#W#SW77b{H4kndFe_~+7{tP zAVZ^{>3rrx$~lRt-KnF%qu}YW^u+71L^q|M3rScn#>wsGT$H`A-oxj6m0$^jwwzxU zR$6=`a&nl*s7x}=vCJt6d2#>1CZo`#tcJ^PyB!rGOmpq{f)s_Cb!!*7j$ol$C9`dor&f-hFxShTW|u$-x(_iqSxV zG44*-GgzRbv%)xMphhm=2Z?lQoBFm^Hc_-$UU3jG`^6N-Ot=E$TwmE*B;A#=3-R;) zp8Oi0+GK0!um{gX4g4N!*Q0p}jm#`OaFl3%LcQYx7~y` zD|fGBxhS<Ne@Se*#stI70CWDS z=tjGGO=$Qi4Tc9K1A|f5kJWH(^i+QPljt?HNZ^-mv>DyGCBo!=UOo&=YJS20bR#HL75m%i%8P=EQuC38;&CQ~*`7qSN{qhyW`F#=R z0?ZH$wvxgLV7@90ixiSDedDsV?*Zp?24-K1qKvtbVb510xB>?64Xi+W1yL?u{v@pT)Hqhf@aaAN4TwgU!r!-O<}(>Eh2T?IaqgZ>ZarWqES#|m{rPY zwfcZNVBx@Xqsyrw*FC=^pRI^fJ#2UiwILq~_2&spxg>20dHrU83XgQpf|Y<9;iV%P z^WObe{!cIIOP9v;572WQpSh0)b8HOF?TdPm0XrsGhjScu1>hl=$;t@Teff{6DFbE0 zyKm2gabI2B=vOy~sqx3p0zoVL@r1WuTi+~n{KZ?|pkA0R$9IYpZeaQJseS49yZL-> zZb0UEhGgpNKJ@TD5{fO#3C{yK`=L%GsSXuZ_{A$~hHK1$(q-Mdp!|z`l8@@@(K@{j zEz>~p7YK~Gf9TvYObT`Qfga8`k{J{aI9L`1kg@`1>)^J{K96ry|Hud>MGHc37PkGF zc&np0ckBMP{6cR_5{G?VLqd7zRvYg}*TY%7SAJ}XCRKvqax9@&&@vj0ONDGs&z)J? z*o}PQs_^PZ7}W14ja_c3+_pnoaV^S2{VIM_2-;kY`?bV|=T8F+m&nx3LBrx(VWR6D zhd?U^^qH+A(iv)N28z8sp4tih z2~2{G`r8#s_IsvS#Tkix{flI4b53Axbs@}U_;8U`}x2U0>@YtxLHdz_J{-h ziN{D>TzsYkcvl1kTdWP(D~4glzP3*K?vIOgAvWCu7LZtIGUCmW@R>aX2Bm-97mpbv zCw(qO=Xsjb46dkua0qk=1V9_(n?pm(EEd(ei)wPY;v+>mNF$j-FWnqMr%OvUz?)gR zvGdk!%_0|3%04UA(n}+$2ku(@9SAA0!q4s7vwA=?X+Vpenjxpy&zdJ8bL~+xtYZ&3 zl#=5L)fV83O9tJn4aL}q^LDF>mo=nC7lzt+H?f?CJg91EOvFAx{{2*ADce0u!{wW@8<;a)onBT#8#1ov_ss3L1w?Ae>1 zk!MJ_UOpw2zj;iR;P{{-hta6gt_$6EJx7JKTTO|Z`u>eqq(a^vznDn7K7MDqb+XHN96U%)JXVv-?WDNkeUr^*YMT_O$Ev>JJztQ;z@%SFXL%b~!ZEg2t^XhXw`5fG<#? zFbjE|Q3%hajL2Q6`DjaD-TpV-9Fb2LgN$UXrE&6=!VTEEqP#&Gai6R6_aUf2DX*@9lNLxr|k*a{oWU7AuzNa*Yq!VsBqxG{^?MKahcI$!O_ zC=|Kc_bt>Q@)Iu}^~IGe+eF3Guy3}vV`!*NFtK0hMsbbEUEsOb_YtKkY$#OI!gaxSNA&&AY_R}P$FR*ol)JEC`Up($}zaix(eq}p^!kVf~Y*7oqHrOiq?D~3T zwv?^0!s0=n?#+?>sR$p|&s zP+z3`o(eRMY6F|!Kssat179tgcyHMEpaYYj^01UceigCZgbjMvcC&MZ)#U4EW}*HU zHn`menUxVJ(r?g3A_A5yS#73KLDC%@f|&PObviUGL(D*D7L%|c?8DciJ`{@K?KiAZ z)cJ?YdB;|dzuYJ2`*^gJ3R+5tri^WBqsODAqx%yf<4{Z;j@`O{A%Iv zYi#%^bq;vrYtMG+1zbuAd7*9EVk+^{Q>kv0(N+A1foQDf{GsS-nU`6q%T^{B& zktIxnm^;pzTyO9aKHJxObBX2gQjN%SuIGRev0XKYYLD-G;T{A4UQhWZT0WvFCAV_uwgjHTqc*@+Lr%Zr42d-027+c@}>j#4c8N z=41#3VQt{#TLkglkpMIg4PBgdPUXM zQ*|=n1({voG2jbqPvU%F9Yt=SH{IEc@I)|&TPKAFKuJhG^)sh-0{k@uATJt8A@OhG z=+{$g}aI&-X)vRBDcn=iHB_zc&5a*TK`%^# zY^(A3ULmF`%B%e0)|Uz=bJ7&Ts_JTH6(YUg(*4D7kHD8-K_I+-+}{HDm-+1R^?SOB zbh^eKwzu^Tl?}aT=xgWFD9}>JNo+GSguVh7kC3-;eVY~A-9vkIlh`}rN%aO(6{VN3 z=tnufl2`EaIC1{`kwN?G4D12*R{5j6ejdkJ(~D z`sx^+loz~_w6k*9(UN|k^D#SHLO;>y8tiaP#cBR1wy9eUYqeqb_jTYAYF>Os6rZN- zFD9wG!pjk!V~6utl(Ms3J+uWENyHT%aMuE@X3tZj&{Dgt6nqPlgrqHXKR$8J2tfh; z*eh(9j$d&M-hn-_u& zK8o-9f7l=P+H0@1S@U6-x$i5^>$=YKIFIwVC*b+Br?^;TSO^FRxN@>mFAxxrg%J>t zmNC$PPZ-!V7!eQqH_+96B**fwLr+gvx1;wlGnS3x z3xEIM7rOP$4ZRJ`;1=Cha8jJ6<~#{#zS)z|wYseVBaMC2t4IFu%4zu957(Oqyan+# zstke^$1p@k`FGY_$jEf)VTlPZ5)!ZtR}m%!J5i+>#7=O)8s#-#5n4nL4TJ6@Qeqg+ zAr=kOY~g_&2;zEMkW>i?mT?Rf5GIl2KxJtDiTCt0JSRFy1DM6i&^y@t$s0U}o2474 z8(xfudsfS#RSINq2#KY(Mt&wH!?8gJb#p*DDmWf;#Bq~wsBx`v@yC2sjq;;!pfOY9y3y`$NDusH!9unvi4uhi_+loSOG zEZ?)~8CvQau|eNk0qcZ-0D%euZ{Hi)>4Bl|Ek4)^LWODnxk3Q4*!OolZ}J@-?o8UA@ENHpPNCAEYzgT-Wz?e1^N)- z=MsSYbN&DL=68>O+^PQiPCg!vKkxkGoBz5K!VaIn9~1hIwf^}OFqa4xg#BOBi(pa9 z*p46|h#|;HiNAs(ZY83}-EST9?Kidw36w{Z=6aYCf!d0ICBx+PIkgy`WTeNs@t|`BNGjT9>G{0Z~-qb<#%9KW;7rXP4_e}HSL;CCLd`7Xs$Q*WdOB!!lIa#mPRiq`1WAAbE)W& z-cCt510!Qs*2_$kWF7)A;++@91Elcq@E0j!h>e-Uno@W^efvx8>yy?5i(G#Vb^zqL znrXGr#Mabqq=*NB;22?l-s}vFxI54ae9t6UzV&BTXAWOKn)lCmG#UTuPzGt>$pBxQ z#h|www&`EPrw=2?;6MHZ+^j(U=}&{e-`gM~X4Vx+$=_ngbJNd-9_v zDxk(ah|2QT=JRAAhv;7xG9~onMhzjVnKx{_-8O8U+c+bdMQdET+5ML4rVc&%YpyVG zSoF_k_Q98c?R|?;7XynG@2m_M_6lYG9ER4Jd(PrKukH0S?eDQQw9+ErBC$r09;zRK z^Fw6rY&f7d;x7n{%H6@h?OZM?J+C6135OnY}l!7vuuLi2I9PXBhlG^QXngdm!`7hphOc^c&x z*K@-6g<2T~krArBIL#vw3-L*ih z2iiIN7!>eVGhUw%q?HS`ILBh|_S?V?wBx>@uYb20m@b{zAfd&Z;A4inCfW-^_}@Hy zsf*~J$t}#x5v~@Y^weiBUl8=mL$d-2REoW1p>25au8S!N?wcR-q@nxW47YUD=P%PW zznqN33DK_{aSGQXA=iZ!#vut@XfD-J&|r#sWZ3t(v||%E8675Q%zZ_X6D)lhfwvP@ zxaLZrT@~R&T+6dMHm=Znwgp9O?)`T?X~e-xSoFA%{Y{bR2{UE6gZ*o^tO1gKmsM+1Ap0L9htfh15UsIK?EG`D**wjH{a4dR6(vUQzI%9A%lY$AW>ii14)6 zG~jY^^i@xj!s`gv#HR|cKB>kFceQpW;3|Q|x=$oLal)5${G%dl4DZ7uA zz-?PN!e+xi;d)0)?q|JHWg^v_Qecf6m`L)&pDwdQ9i{lyQA&}ipSt?0ddeKc%13K*cp?hq6)h{ z48KVupEdGpZ|%D%pueenYrC?0p4w87#IP^gJ6M&@=JZpTVer$Ay@1S+XuaI@D!Y2B zo;iK47-vA$g-oaIl!w;PSye` ztJd~m6;XVO#6IYQmEV<6O7Sdl#p?Vq=P6P?_L-^dSB9{r z1EO&M?sxJ@C*BOj63EqxCNqbQ$q$QclOmxxJ9?bw3{|t+5#_^HxLJ}joF~jH%X*vI z`FS+9I4K!B3*gf{VBC4j1Z^#d zV?_1K9S~n;!=!j@I!%kix-z2TVjjpK0v03Sb!YCge#C5|+}*h_K2>|nC^~mAXnJ(^6IC6ak+aMi<;tLl7r+Mz61|Rx?7*uWiQVHp7=5jX1`hVL1!>q zb?$WOfBaccZ!=VN3F^(3e166$H*uAyb>C-U{nYv4W9~XM+WqCV6XLEivVHy}(j`Na zKCh1*@01wK^d02_gi|wG-ETxY9p_}_%G%~y`Y83OX_EbH0}}Z(3*zN`thEC8-)z49 zb=gl+GP%Umd2e3v08)~xFyVwj;3QN6^OAM&zUUVK15B5d#v(gYJ0;XcS z1N4->?wL=q(7gOvHI6rxM!HIIyF?$$X}2FkqM&9z(=Dl5WydywnxwP0I%(bCZT~wC z0f9mf@gf-^#*~8h=i=f zS~XpWRp?rrU1QH@g_WL@m1;1_I&$^stH{Ysg%f)fMiSr^5dABC@M zPE8eg&N^6NKYl)KRIM(+y?MJO;~=&EC?d0Wi9mh*ZHdO>gOKPb?%SW6#^R`=>x0%$@9#ippDgJY;FR#!JR~k)$3m=K5Dcz9V=@(4AvRAgzKiadsD?`Kk2o8!utS#c^!N1z-2(-7jUIsFJPcDqzMje?UsH+J1 z1u5Fy!rRT6hw=4Knt2`ul%G_ROVPBvZXvSQ+EGt1$$jF>#(KCqDVIaBL-#KtfF&q` z6@-Nag)}mW85~Y@FB2m3wo-^~WcocShSKWI-lx6pKQx<62mq zS2f>m3hS(@JPIpZ*h(v3=?TuX*YR4}X5L#i`Fu0c{BgU@wh@NC2y6M5FZSLLc&JnBydC-nN_PdNPB$D?apMW5=-ji?@tOpLlG) ziOs!~r%NL4+ZHC)BCN^rKj#dp(w10NjGwHfZ%k*GM3Ul-te5Y;?kDFGSNm1F$q;(p zShwfTN_H>S=2ry+N%j;~ddXZZ-Fa2*CZ`S%akjQWsi^2~Ih%6jI8lWCV*=$CP7MU* zY|$MXy<4Sfo??#%Og=L}yEacMPz|<7#|sMv4j??qmrhh8eRcq8-c0=OghWBR>X4jnk?9%$#)xXwZm|o%YtI z%qHvZcE5bm6f39MC%?;bOVa9D&@h^@hN?-3A8JU8^`+-0S=65Sh3q?tlwEJlTnwz}Aj7%&{Q4r>fqTwqef^B>1V! zOfg;M3;wW7A<>D?{sW8x`SWosOPj;3uWC!t)2zRIKR76R*wvntt%+AfK_8E-Uo|-8 zO3F%))?fy`VDuA=-Ymb80*;<-lO7$QEwAe39O~Y2-eha zn_qFAHamo=iTL?_qo$h|S?Usm2DUwh)Pv*pDvjQ5pcI)S=oc9K#YO&VSJA9N{$(hu zb0H_@Qb?{z@645x!}Os|;%|`d6(AN9M3qqEjG608<{Y<>(a>OpGs1_<>#ASqtevwu z0~HJF4~}mvuGWJDWUi-0-n-Fbk~TQnTHHUWvK7D89Xb#s^)76jz?jOVaTQ0F^W za%}b&s2f-gEYd zXMTn?TSg&Psq>P3nh-Q(e1H=xH70+sP(RJ5rB)8%ijzWS4|b88F4Jsx(euJG0C{27 zyA@Dz`XDq_>+e`cGS>E6nFogz6@JI|D7+ste!!8$=R^L@h{R;MCblRfRxjHi0$3T* zq{PyYez$+IJAK$NCIC{!m3g%b{bi4rQ!=U9arE28EQS%3u3XX?y;qxf?jNe9DlVYD zIF+T-0}=^Hh@Ukt87D8TGPe0v5IQ!ymUdjLHz({8G{gPQwK-M_O*nwq9^`?#5cDdUCZM|Pol|l99v1t zOioOWnRZwejX@6*m4n(2muuz{PD2kT`nK1Fh5B6oU^##sFDxSsy~K5adP~prJ~3I< ziPsxiuD6n5!E2n{h96_|4gD%UYNVkc_kCJNIIbA33cQWl=fM<%sz1KksL7xZ1#{-* znYs3dei5Vld352YZot}v9m^$m%x%5Nwb{?m|1pr^gAPq*9%;93_~@67V9TmSsDglf zM|wG?9EVa7K4i_Pz2X50S&B&>=Y#m7y%$8=TNppQR@-|RV`3b8`l1>?R!Cb;rrJNV z4#Y&o!c})$_$k;Gsv|mQbQN9Vjvw>z*w~p`4GAfx!YTYot>aTI8G~8Ui(g6>W{ZcG zvIZ`OxkX%YuNp|sXtqGVa00k5_ni(hVjTFX4+L*ziS_jRKmz_QJ$=S37oV$97CZ{S zYHjA3jrgtGq|6a4SQppia41gg@CYkERm+@Q?JqGn*o*w*WQL2y1SUN+_pHPh2JvOi znzEasNT9@#HsR0@iv@kq|0G&?Q-bzD=oB@lT~Jk+Ue=OPK!HMLV{MIV5#C8I= zv6;bb4LTCHR_inX4CMJTZ<1pVOL`a7){LI=Q{QB+0dleyB9jq8T;0B zLj9oL-}gClkwN)_Qmx08f4bK@I#>p z@_xN>nHI4RzXXb2wz=)?Pa)(APfXT>>36+UrXNMxdOapZ2p}P# z4vaxtK96L|(<@D;-%v!C2ELgWBruq`YA->=GP?XQU95$*Ll4#X%;R*yB1B^+Z_`HV zCyOhkNMk8B0W}=6jg@#yln3@Mm(u|GdXL9uTl?k^lSZFop4wNm7;>Y?`u>czW%~$S zxIa_y6b8zLyzhTfSA^QV2X9MooXMM`GiSuE*{Qi9LaI?S0<3EO;6t_R2j2oqJ%?`>TiAqG`_;9HD!RQ%1i&OS_-Jl^FTL+A0?Rs{BC z9wy3OW`^KlyoSC%k)pK4?U&jmJf%drbaICXFJ-swi)6-CLMa`Wa-zT=LsZq?x7N&8 zL+os~$p&3s#NP*W%{NTDB@I|~PBB*{=E0i5>NHAOJy>(iQDc3#KgA7rEd5qT6?8L2 zMKQ(WGP$;-`aaq&z2l|JurLn*L3ONNu^F43r&yaOLOW8^M{}5 z*zW~jB8>A@4GO=-)0qW_?fGSYuOB}8tozwCD3h-_*EUXqVs*0lex!YctGUOTPq=2bSTwBTkb9IV_*`1P~WRm2wBiS=TBU*O~xq{rRDlthWa z^~A(!wQQ|gv_FK7ocNEV^*e@7hMF%KwTLT!Z(!`h1t%}Wf_Q@dx2Z}V3D`MB->hXS zUO=<3%=nP7!nZo>6s+) zaHV^n!9u++M;Xp}bFPbg^G@#N1oKA1`ocP9kpBsZ;F@CWb-I zY?%pyX{m{E_hxktHz)VYt-k$nh7}3oAWp?v68np-87k>ACspOB>3Wa~mKvjd+iIea z2P*l#b*_D8Px1%9-@pA4;)Ze5^fao#q#(0WSE=8^g>6U?>YiyJxLO*~{Brr|K}Iw$ z-Q^y793r1(lNf3kgToN_Ymy*Zs1XeIMkc;-Gd-yd3)eLp^XBD4A=cc6 zkMWyVd44+mA@v`*g?}^WhIB9%Jj?Dj+lAEMEBGIt8cYOhaB_Y@8AxvP7zPv?YBW0P8uT_*xYRfHV<&sgqq_) zKmI0-BLUL*{|*2DSs%V>|Jgk22tlEt#5EC-k;8KF=QA2*7U6gzXJR4LqDkjXYJb&T zJPG`J%A~;l$FWr>AFVR@w}GSD_0C2(X?cUW>*h=j|BR@nCg1*$PjS4w9d# zvd?h4JRu+=S~*P3c~g*YX403y3|w_2pd&+YvOA{(c72vC!Wt@kq9zllOLcc2Bl5xX zT%cBCc(%9j!H>WE?KefzcrK&BSGj6~OE^HCl?VG20r77iHrL_r5Hy#M2A~eJym4T zzPu8vGuG=kV;|cS&;3*;lqzp?@2g@GWRd~9>tZ$8W4Qr_%r-Urw$a*g`)9QAL$VFn zp#z^!H>H4$luqB_&d(n?uKTUTFLG2_V_D|+02^X<+46BTVEfutJMKCtH~qoK0iFWG z3<;4?IPdL`hGO9JWZdgY+tqi17*X#= zv*il3Ywaj$Q-QZty;tAz$U!Ts!A|j*7e#9c$ zf*h{)k2_CWHRE~}gPO`*&uv`lFOg8%I>PCGh_GsujTF=5g5`trbn3OOZjLv{i4*3w z%jZ5|=S`NFbiXg3F^=eZ{YJ|usL^G&_D#Rp#zW6vZR{)HLgRkF{$meEj^n zl|`@Vfj#zG&#SP!o4sB$HraWDS-sdWAQ11X&X0FQB8G>*dM^UXpO}xW9eh*za#UhA z{Ow}hwRwWl0N9_Aj&p8rz*69MRBlHqX5;yZJKEI_d64}ULT8I8I_fL!f zVaBaBo+a|lLM3Y}&}jw5Lo0`6oyRJ(yflD+HF+xr!S30^b2m zMGpti`H0?LmAE^T-A3?PYP&%2n!2@Jwx1Buf}Vf~NL)~Wl{h^rv0d%sG8>|&RQ48Z z6uCZnd2LrmQ@V3Jt~0I#qJuWRMDZRF6JG88lDP@>?tqQ|`bez(-1-RE&6-}7ws z(>J+eusb#v&8v6`A9TZnHK!Sx%MpZvA5V z9m0W zCyZE)3aivW7856oAKs(6>cg|HM{6oB@z}pv4M3*>%0@wAbOy6=IQ#;fgi0BPfo(Ep zucEvsim{kS62yt87GId89tq|xBP`Jhb_Txy>w?}obTY)6Il;k98k&LdU<6G)V<@JWfM?7PsfA&?kN@Qq&&M(>Iri*Fk@g0=(;baQp% z(l+?!wgwY7EY=?$yF%qx@`(^(0)B5_T5`LoOdJNQ#a4> zl3~c`8`wtrLq3o5D`;O=W+=p#m5=6YP4#(O3v|3J1}K#Xl!Pz)WD6AYdHXzlZ6p0` zXIEaN$=)r~zO{?yCfPtnm3QLT|UjmpF^oVd6v5r_16xuXL11cQTM3wZK+7%*?Wt7q*Y zl&@13N_6Vot7fio^*jA5GLzlkI-l<3>6yp1%#Xg&iS|Op?xJ*@)=OsJ2tM$|Ax+b> z{8a&B@x?6DO9-#lYeJ_$_Elfypvo5aMym>`iR1iRCJ`nA^9-Qk`Kd?Zhh&W)sWurx zDP)(1)brhUa=PbsRF{+xSqz~O%<-AeGQWGmTMX2EPPIzI|+~pi{*Egh>*3 zpE<1wr`?y|J5>|vwU?Q<2WbQao@|x$|6C1~1L^w43q1IC(RP1|BNDBoBGuKByUt9% z?k@?*1WHqGA@lW{fo4CvDRN;}lb>=@%pVq)N*;Q<2$kj4%thoLGEb^|eu~NGvyqKNOpB%}XvXuK0s7 z1su0mI6~VAxwnJq$oFvd;|nvM;MK9JaSQc&A95ry)1cuSFdK1oGF!bm2Kp47uXDwxNf;e8WEzw=b*gH-Fhu!ecBD1U+1z-Ahp4Ei z`>{Oe_b9@YzVJ>?m0QlS5i&iC0uVR!hd-xznlmnFnPSlZ!Tx@)-hZ>Vo$3 z(>V_p`6+?>i%bEW_tV#%+pNw;@m zxYq;QjbKnPytz8(dN=Y=xpNu7l3f%2+L|v51)+N>xmwk#1cZcI2dQuz_!BZ=P{J()C>YZ}%>eOTyS0N*8A4M&Yn;YVP(r`D8@&d^s`;gI%pqY^VO#Jzm7E$~oX!0)ML6JgU=j_)|mRew%hm$M1z| z_3pL2YsBo@EY{Z6Y(}+bi$Po#6CCl}CLwlP6N5(7s`scxSb#MwxdP&-&A66h)_{22 zgRYz9NM)3Bl{X|UtsXar)HCj@iH`EgB5rn-FEgM0wDNmidAU=aVLqPE1>`OO5d9R0 z*6VtMo0b6wWO_{~$F558T&*fSw*A{$QthkNZa-^A-L=Fj5) zAc3idj$>qWd3&n7zr-$a5 z6^^6JTB_ey0G%NX9?g5>Vbv(V(X{@UXaNAk7XZ?D?|Hg0svv#8%l9GktM6f0m{GUD zp)gOHvaHvFqj)F4<|9E~+aTk|s<{K8N6?qE&YP^vZ}JmjV7~6BJ2Tr$jX5wGArzDi zpDLPu|oChTLEoAcF`F%8Sth{!Mi*a#VMIPPeYinvy2I;}*1evk~J*sB=H*V1_O z>U#=}*88cl=#CCq;e4ii5bJFIQ()_(j^k00K@}QEU4t9Rk@fU)<)a^YakO3>Z+=li zvi92a<(eOay}?*TR((jGKx4IV-uMId+(2s)f4ZN`6NE z*?_ClRq6c-L*PhyP_kP-=RRDR7pv_o$0weUvX7@6Fw7x>hPL1Z!9e$3MYWgepw3l{ z=K`{+9F6jP8{YxXIIj-%N}CL4wHj;bv(M$5Fk4R;Cd6?k)fd3s==S$|d=AhXjA}`De?Ge0=XLDA6og%w{mlMM0BuBJ*ji`m zR{4AGkdq06pU-8W_+%vOTmsGF~X;}%YMX& z3!0{`k}3ScQpPp@%;-qyRHQyxG%dZ%+KObs=n`}kX7g@m2i@#XT5$g%DNbh`ljSxJ zSF>N|*yUn2UF28q2*$eZ1vVi24zNO%3U}C`VTem9Rv{SG=3}|>Kw>60{7orCui3W? zWkClN#R0YMnMjO_BVpF5t5Hl8eBE6KxbM+JA7x9;;c6;p5WD5^#wei=k(Y4WXbKyU z%h}6)s%p^qD91X#BH#U-80rc_^5aq|`~NWm3z4vCRI?;P|2>IfP_a)KD^DFd9#!tb z2Mpq7C=O55U?HFHJ{O2(BGif#svL$Vfk~qWk_J6#n}Q7p1oy()o7C*=O2LM>?Y>r3 zTgBZyY?p-kIE%*G@ZAURS&`U|4O-Z`OEl=B3Hsw;Fa_&bleLhO ztpR!$6+hbp!#Ey9FH9V(dxaVwDy7>o7F?-3uB@3=bK^d=Ptf>-G3ZI82sRM$MsWG2 zQ<4~2Q8v-!9VDzkA2r8rF0eFL2FEWcVkFpBtIJ&LCgq39EnOY))pmZ-7FHB= z|5MhE!iT76VT==kgQFqWAMCrIFEaF)=F>HZb*Blnb5NE~_-eM;rOVH^wt8ON-4gem zvQY(fzt)~^J^OjW%KX{hyYqJK-wvOz>xh{fyI{AK`yEp4pL%-_g z<}S{^%u=w|I>*^v(f?b11E?V_Bz3ac2k%H|3~x^BZ$JJtJ9NE+D#733WcBEZzR{hJ zJyEUz5WU3C`9~q(KVS+tyaEBt`do`m^>4CT2WaPp zz`*%#Gq4mmS^X+DDEe=*I#x{VCShbwe!1*O$L;v_2O)+ecvoyXn3nr6#we^onhr!w&5n$Q69!276@c@;oL4?8e>j0n%oauM zamCroljJ}p;;IJpvuYMA!UyLzAy<3N2Mx%09dN|Ssb$P(T)Go4%y#(ii8N$|4{=~^ zcd{gtMZH+6?&?56z04v$SEIa7BnKJ&UdLFT#`ZC#kV6N6r0giW&vx-iVrFW20A}@3 z3OLRE_S$h<3r@h%GcvxTIxMqZk`c$tuil=kpUc=UbviT_x8%iHS1IrLS@wR)a6Dhj z`37JpV}T--QJL&7CRmWXZlf3Ki?6cy@j{OIbymu^0RDHNECX;bzzvj)&k)ZaH*(K^ z@VMId+nT9a7+*|o zlp4an14MJwG+ZyNIpkl}i2b}Z1!p^)_J!DXEc4Z-fd5w?Q(1cWIq2W)2$4Q4?{+v+ z)WaPf2DTTi;3Fjm!0gPC(Wg{I$OQVr$SE#?fE@`0 zcX>s{;2OY@{``UyK3bq7Dtv-Y?V5s1J7NTM`FZSmVm(iC{)ffni%qR`8C)VeQwt(C zP5#>4X=Nkc&Z)x-gHNfW0npwBWCklS8XxeX1_@{dpeFf! z^aLmz8hg3AT^tSGeiMJ%2GG43{d`G8kaW@Oqey3IB>@!`l^1DJm~J9Mc57gO*{-cC z^Z-I1m(8-Q*>pt_YqA@2Ky*8O!P)G z^&9{-+c=n16xg_R01238q7rFr>>8J-^8q)7eN zKr^Ps&Pb-?VP@yO1RBjPU}eWwx5A%Nsi+{sP*M$1K#+#BdlkENm+*v%OlI{S3e^xM zA!7${Y_k)|*W$jv%8GbZpsmHIoW!zs&=BOk8_E0rr>QX9cLwnR+5B* zP$=qx<}tl=HwV-Gy7dMzxq-OLtWgH&(g16|EmNS)|M#J&Wm%+2TIT_Nv(fD6Ks=u+ znpeMoB^(6|>7cu}({PA{$Yt08+P?mni9Y*)hc`@=)!Pr<3*A=+i%b(lVS2xD;Ec1I z1kbaL$k|fQ^ol~zPPSHx|JSgx==)P+y16Ign(6CY2fhXpTcnb4+Hhp}NK_8oPT0VW zV9BHnb>NV}J>`&S2L1H4cIH^>4Q$v4^HBSknfRHl0(YYg2moq+=s$A zL(qAF#L;OZEAthoadSCTuNW2T#PJP%MayB*2v9?}rlzli2<;41Ic`m;%x7f$OBkY{ z!z}iXNO(wWy$jheJ_AR;d%n?or4gNa8Uc>yJ3y@)=5F!Sri^s*{i$K3ALK%&XeH>3S@tx6>*#*VHhGE};jGFAm;d~|5TrhZvd&##Daee>VbCoEOh{K;{BGGX+E0RK& z)C`v;B4;65yWCld`KpGI4Tm zePPYS&Ce;14$}4sA4*!XIs4Dxwm%W>neej~{YJIQX-0=(793{7j=V`i4Wx-e%GF!% zF%Ds-639U^Nb2GZ*L4cJ@qJsI3ucYN!2zfVzLTS1=LE0D%DHO+?n@Ax?`p4b@}U5a z*?xe{{3rX*rSToq=dRjW{(*1O`XUp&Tfnne3Kqd7G)H$Rk!||8B4x8o2fklPE z*qYfo(dq8xf5}s^KAQQ;=y2PO_Rny?A=ZG|&@A~cH`7szQfx%)JslKj^49~0VZ#6t zT`qmxbn&md`Oh0eW`M?IY3*$KGxY=B+mIoKtYZ!kH7dU4A?SF96i3Rz#>OU;(_moY ziC|*agTQ0_Ur!Rk?0}}fB0r?79{!RqNN2=_hk2%e;DHiI{Kr$G5{U@SQxezr=Fk5H zF#n97jR|0NyV*IEL+`Mzz~$1)-{%k^9{;^V{=vr`D*z{IbB_9J{okYWPb=R6E*bM! z$aD2B*IStYw8B>301>{^3Why^j#J&y_pa{1X>cr@kzOH=%Y1r=iA{Y55XmFE2uXJz zDP6V!TB*E3Iy|~#Iq(Po)qO4~8xHUAzc5XBE3rDdNmO@0u8$yK{^#|`(3^j84E#-4 zH_*!R0d~*uziSH@t-ziG<|5p1MCebxV=fy3pcPGbn3MCJRzMqox&Bv-E@t>05qR8_ zpr)z0nSqPTHq)|8Lf|3$1S{}Zvog)2NAgWgOF=sauNEgh#dzfnB#fhf5xGLOnmG-S|48V#uEusFEN&bA)#uXpl)LI5qhnj zOj*XlOU6Kfi4myRoI(YSx}w6FUw)GX;-v6SJqh{k)uA$g=z_*3De%U>0=Vb^Ai_4k z0i~;~HBteauhR}w?<3fCL?WY-?=C_qN-+;3Hm(O0Q}>Q&0T6)TL)E8on-)g0Ub1}w z*lPwF8u8|V!lmWTNSnQR@qftW0iM23faIf!m4QK;k6P4SQ{TWKQLcuKt{*5p(>>%9 z7P4OSfvYJZXWUL_vLZwQs;25T)sHIT>ijTg016mIb7%p|e_HEu8kogS05EROJ(5<_4ZHXnMu^S0JEhI(3R{@Ln!v4d9K0_hVP!a zXS9I$bQK_`V*%!Q^Za;iu=$?33jp7Y02AOz@dbXf*rc8l4}kWDUjRZjKj}_Y>M% z@r_gi;15pE$^yI6p05wn3+)6T^Vj#Sb}%@b&-)XF-kgflFH+3IV0sBkquHeh~4`nE<09bfTrjB_D z;L>9NntS+xfSxN+}iOb9DbN`AAzso#Pj;C%GGGc6gIPBake&B!dy0F<;0C_o(K z3{=PRwH_-&_I+YTOU+)w=EZA4SZMvBOy2&UG#0GNNsy$7iK*aB%;Q!=ba<+8v_17E z)b9#7G?i@4hhB%=62`#P-?jl~YP%S~+UeaSk;sy6ddaQ_0ZGVhN=^ih^QJgS6b3T$ zS3v#WI5LV|M?2Qze1(`8FDlXVuY$cHoJL*l{W4C~?>&$Qpfn|ET%Q&nOu`X5L=m8# zImjsj$V3SJV}P83nHL(6fNN*b(%=GR{!p_oql6reAdX<^$LUyrGbDD@P>mFdjWbKF zd;&ns^GmveDXuTa6B1*IAb@e>(4UB)Mw;$XcoenkeiZr1WOwh=@F*Xj2F zVNiuR3-;%(2_E2BY!eRMN6<6oRfdDfU9oqLg#9n>_U3sgrEuK;g`guiaKVX?Z5-?uO6s{thHKPYOVSvAJA zPmEk66XWhKS(qfIVb+?sH`$VEAkkw+cs}f_m8VwJ^gtA&*{gB$5U31nl#ZDie&Jws zlri0^{*ozE>q2JIJR6*Dt!yyX>oF|0rTYS@244PW58mH5%#e>B>YbHBM4^Jnw1L`w z-KU!Pz{t>0oK>(tL^4>E)RLx!CxyR}o;OzN{i^0?G*XwQGDE$L~LhN`HC=(_^8b~)*dQ97D`P-ZLPn(fZ@lh>_ zab8L`OCmSZqyD4gsGydL-N(=Z3KLa$gcJ=TH)jS!{3NU12fB=!_w0Glc1a{w;59wo zN>_ba6gu$2M=!nUA>e3bbw9IS#bIaBsw&-CCBM6qxM3Z+xi$L}4TLaE^Z{r)6kqWT zZnKyRNmCJM!zc^vw#8(uM=|Ppq4UQGZ+v5Xng+*d6>r zafSv8?gD?qQQ%%c9BR%n{MI5POChnG;A;<^4&)BEGP)_PAqDXp{0+ip9cwMG_ z_aB{ctMKGK4Z8DgtGcB70gCYAkzs=nWPES5C*Qucoe?X0 zp_?t}bv$Y`2-&`pItK~Yk&&I(VNgZ+J?DWIBFqWgYa2J9p=8ABz-sg^2I~i(Vr|ai zj3X`B?s%cE<9nfdKlS%|s3uh#2PiG%TKYHC|q0(~~ZwvT}%vsb|a?%NtG9y5mt%;pb@sb8Jfz_egj$U6AJ4gSE`pfXdL0~CQ_QOBf zTIXn*2|$+)73xV=0zo7L0H$0(5UyQ@bJ&0qS_KfAu_=A#2?tPrXE@zcJDuB`Ya;+U zRR!1VaJ#z#GL;tqQ-d=n2zm@K@KFE*ukhpDsNrCmbXM2T3hQ(L=|BBhYDR|#+4F2^ zYkT(@P>jf~_9eoZ-f(iun!)_3_2JBwPNw82z^%vvnpq=2P2dne+r_nthz9*c%H7u2 zj0(@k0d>aMDnUP^HgxvlWIN||)8YAI5c|*3<`NhW=@Xr#_TZRaS9;+0clI3oY8ZiZq$nrUWMDGYcP@`u8!CQkIaBpo z&|&K#ygYl8UwLc%^W#||h?tlu6msC7qJ8#Bv)bW(wkDuosJ=-Gr4SgJJq3{L#;!h4 z;W#>-o^3>hpG_-J$B#60VQDuzna$CHK_!6MIm)og5q9i_I zVY-8K_X6Xn7+nzo*g6SQSO(Os4(g|lMu66+7}&DHye*g;%O3z?ZPGlp8<3pAQ_X9s z&zAMqs@boyXhpB~yx}Zi44YQA{WXv%W&l-+M=5oeJ3>ys;!Rqhrj`vS6MHHsvRx!u#@`X5`BQi{xIG<>0iQkQ8C|r zxMrZDD1#&rg5ci0*V6#rgX3T)fEV3%CZq9oJ5y@mMi(D0Hd3hE%&$h7C0F)6`lflM zYQH`tEwr##%>(f7F3FiSX743UGy&arDT^I|d)vs_IssOY23{n3!1{J#@ZQ>y3!tr7 z2BZVSu7EB*2GA8%EHC||K=l;MClZF^cukv5hM}_e8kl%^u>h^z2`DhL7aN?`>1b); z1&&9)2~$N0Ov+S^?mWS|YfqX(QOZn4fK8bx`tba4m6&H|KE&~+aK0Ud>e1Djl(e*& zCGkPSp0^L=% zs+NMGsPP_XQX=ZEAm%Vk{1hhA38RC(ErW+z<<`c*dLWk5G@}9q!;<~&3^s1v3>UB^9CT0%>773BXnmn{O;g#CZE)Ow^JGa~Q zh|i29q)sEPLDiwc42TxfYUZ_7UcLDLi35S zXGV!O85_oS8?Kz)$I}DiVV=Y0^`%aQ4Wx`UNoe)FnZ~^JE!af(KffmO;|<~!z)?HZ zx*S)CxtVm1gvf9QZ=<13hw0)eW2;qiSnsGIx*q9APoQeCrF=FixunB$3?bP==o&)_ zoqO&xhuy*(tA$q~b{f{#8ftkiH7IXBVv?FK@kMacT~QyanZqdXjYin8IsmOlnQe^; zd(7bY&f;V#G#1arao)`E3rUy0Mqgcc#xOwjeGu(>+AT~I%S1+l$J6KTk^I1%ZI(?u zm(aDN|JVtUzM?MT!!!08WxSYhnny2t(tz|TPLaEG7@zA6a5-q#u$;ME==S?*r~_K< z;2LIMjutNi7A*tA5`SU=-8uEsyau_E6sSkUHM*-3pzBft{76{?x057k^<-8k42@{>Xw= z{@fgB2MyX3IHK%mq%MBl*=l))6?2i?zyX;QkgR8+Lw6wWQMJl zckH0y@ug>ygp}+?4zwcw_59DokJ2ZaQU0y&5I;s zx|%)HL=g&{*&W>5wYxuGB|ZIJdTR1TuL};kSJDH)Y|3#{)6N*G{WDvOL${h{QTKVO z76rEC#!;aXd)cbpe2+)PsFY_lnqv2%^XEJPy0W&0xO zx>NXDBKC=EI9B@j=Gd-O`KygUfv_1*4$*N9rOs7D(b+_S$nz;l-FvCq1cHUBqsur& z-m7Bf&KV8$R-3ltxF0PnqJ5m>Y*f3&^oS)eYDXLz95mw#ig@)#lH0n@Mza93Yq(rx zWa8cXP=yvvEvL%qx4x}fEE&&oUFfpuU2&jP4R=mugu^ujjW{fuT8o>LT6ez<@~@)3 z^dQQrCCJytk|FCA_YS$#859v$R(w^jH8tPPjWuRRYsT{Sq^`}5iWTJ?u5IV?nQ0Mx z0K^iwsn*d>A@MBkcm$7sE;)R-PW)5Bd~X)%QxBOLhLZY?2~xtiN0pr);phs ze{5A6CS?Xp{yksDjT8EeDMvun!iVHVp2twN6h=H~KO$+IyOfU+=FDM}_)~lSM8yjP zMz2Ys*`>Ns^)vkDdbnRav83F4m{u@>%3eePnns(SyROCHE?cYsP=Oa~N6Z-sqzgL* zpUg1r86&V1r11Hwga7Y`bBSx0sVo(hB?=gA6I$~=Bcva0Rk0kYN+g&2KDm61?@P= z=d~9f4XT5(WpU}wd#h+5z&n6QW`j`3n654iPn;1TOG*;}O?&{C;o_t<0BRq!q!wyJ z*FFh-`BQ-AX^O+2DH1npQl3m)lZr+eQ zvj)1^NT8VrYE6tlM)!FOSVuww1QENN+wxA+O2+TU<3N?B&j~sQe6MnNAL_HdQn|nrn`SNcc?JZU)x2cF3yPu_wQvqheA2;WV6u*~umBKMBr(n-U3 z_&NKA#ME7K@^*c!Rjy~N4Ya|Hf;cQ_n>Uz z*{b|s*p8fx%=qc=E&sDY|D}~4pz|4J^Zo)e#%DYz(rDKtA<(h&P5S#Y2IThI^7lkdN5RC5s8S9>O%Dd?0m31gV3J6>xkv$1@~;<%SoE%6_g0E5wgldWl#(Y zJ_KD@uPWBe9QIxrmOeX#Dk6L71 zYS{&ty&_}Fyd`$VW`AeR)BFgSYikbL$>83N;9eSgKSV`Vd3LJ`S`@qHBdldol9XXF zs=savZQxT6)tiFUY)HNQQLEq73cR3_SyxZzWOMyrRWJJPm448D3?Yf)A6eKH&@02Bup zZ95DXt7{K_-T9~qVzI45DZEq%+Qtu?^Dk}P4ss1@D)+$)`PA0CE2U2kZk)LnDn*th zD9Wxg)o&1grOrT4Fx}+`_vtiA(*nXY9KFU&J-!O1$Bb^^@qPr^Ru%&rUW?aP+|+_> zct;rQA2lF~Wbmq%_~uQxV2(RoIqMicwggqQ8y+!zA_8ae@3DEnBeGySCxU*?b=BoQ zQQ`|y`Bmq;t`}OX&AyE7`nby|I643NadfD);A{R#%%!^>-`T03sr`VNR2K{Gx1rwL zJ2{p5v=Hdl8}8xE(KM=)#$cp+Nq;_5k57ixG^TX1vezq6olg6zyc+aBER$e9G&*B+ zh;wcOlq6N)5W-FF!Y z>Vfqu_gr18+a^|BLPPMyUP0M2^r<6lALcplnL@zFj2pI0Fr&|s`E^-l1tpny2O1YL zRZGlX5;_%G(!B*sm9Nqtek#)$M&ao+5E{+`Q_gtjqBCC@?X+3*Tg|+;PW-dvR=Xgc z6J=`QBf-ZKN4Gy;&r>l)?gXX))4@XH$`ExS$6Fw#>7jA9E+WCe?qMY(aYmQ-jsfqwV^~?-q6}}_I%ey!=-)3(k)?xw%*K1PEsY!9h1dJ$G z2&=y#BYV#+n>aE|DuhKHyx*q=n(s->;$3_S^u~f5hF@BHZt4bFV$AHBi@t?7R36=x z`P{XQ^J_9*(D%nJM?)A=ZNGbmP~u(sQxrObVXPTyN}w5J-n?ji)J)>QN5hIIrTt2f z^~$@gw%7+ncUwMBMr!KZ4@*546*Jz+P@}HHKP#9wX>B-5x;kA;$V!WMM|O~?nvIkK z=$i6!$Zg7?@^&YO}h2|33x1q2{WW*}$jopiK0j*+t{kKFTN57`9TFG$gF;|lupB488;T_RL?Ql#)S zGaB5H(^=#?aN)nBsc)N$bfeKH$paX@0^rTQe(XFxnC>J(|6`)VGe*?JTRauj; zQn=s&6??)YP<|ne&EgS8DGoLFXn{`YY_r!EzJ0d<0b7 zMK?f9&D98hC|V#%h~T$kgcNlYU#-XF8M?tU{|jJX<)bzNT4jVL90B43G34nWRN=)D zDU1dsBcM_cIOo<%EA)un3+Gg^Pb*xs>Ed9K^Mk)2XjYH3nfbI6L^Vu*dsq++m)7Y= z#&mOh_+K71z31VhN4bfucSaHK{&Mw(>cKu`a)Sf&gh0n1lLphJn7K7gC!j-eP2iP zPn8NpOZ*&J`nnDd&d;4*=DcEJdP&drED#@zK2Fj*q&z>{m(Bu$C2?iIz!aM@@DBK* zYE9IWb`N33ivb4e^W@XtMtUd@dKt6{(D91RTePfn9UlBP1u?+-{zgwHTe9o<%OlpZ zZ(mkQzWe$9qDJFIp)m*Qg63v9x7z&5`3nbbs!W6q$e8HfgC*1bX#*y|hm+~H(;YWK zYl;{I8u#EwnXuxjeB>Ml3ohmtBxk=^-k*)*z2_Yh-x}1D(ZeR^w90zU+Y+pD>#I#9 z3yhn5p>&s3lOR~5K)$uPS>fKj40-_N&s~v>fdY#PFD%bQsqsdit$6$G>iXZeD(x*! zoqY%y9?oWh&iM!{EHt6ghS?$O)p@4z1|ei)c;XHtP#3}^`oE8^H_H0pFg5qMqhpq| z(f8!NU;3#HO6f&b_jXJado+7+u$Qk z0da?b_|O%hdGvEwf9mbM4-tE^Uc4?B zCjk4up2Y60mvQ_EE8s1WSN=q()>RWJCXW#%GzRiBRi8jCSM`1<;UV-5>_|eCN(sUS z()3tgU3~d4pUnPgJ*U@IhSsg|-L}OWqE8olNlS4~(cZtp&_5=M$$;W}>{PS@$hz%C zNZ%))jmM*9qMxkev5EuHS}SE%nHGmfa%TNxXHee6jY41@^Bm#MGyfu<_x_2B-YPuL z1uOdtDR#YcPFpdde?rIWsWHw?&+WqaTH1h^3{1VQxRm$7h?6nt!e>WItFxMq*f+nu zdW5?B`eN6J*`vM9xrp!&M5MHr0VX4$OsE7j$&CgoYJweY&+r%>Q+_QDrT^x8hLpR% z5!TqvsNT6Uo)DG{!=gt$WUrWlH^g&@5_;~u&1u=7nkG9hYc;<}Fxi-u+f9IEEDA~5 z7$j~Sc25$4<4Zun;Ap?YK1({_GUY!FO|%-E2B~=fJpngVK=x zIS2E(i*qR#$!<%j-wtDyCoN3(JB59eHYk0&*)!lSHkCAb1I~rhQgQ*57Uu~(F+B17 z*fpFV&+Ya+Zs&4|9q!b(ePz2_(=)UD)ZE<6ytYbI^zrB`VQniL)%{DDnwMMXCH~ZR zygjA7ZQu7k-?{rU|BC4YO6;u3aq^Jq`^IFR11wdZ0|P0acPPryDLFN{P(IjS7*zsZ zhq+5)c#Jq4_EVRb#0~q(;}+}QW+vrvWaKAlvtO)lJRE80z+B+XEgqfV%yXb$rj|gy z?h_g?t3m$GS@Ny{370s1FK3X0pXigH8?(e;tJ|q?(3pS;9`K4W`!V-*md~f7e-f*S zkyubd!iw-}G$z#rF^`D5)s1p!!TQ7T^d%8i+{ zACKw+ezD#IE%_QQ9}%mE7(!B#q-;b1B72GUeb|u+M!3I!d0#@mAa3mDq$7MB)}mLw zudpWRH5|gu?h-&@*fV?x0@BfpmFNQl0@Ce%Zp6jo%i6vG5^-caA-P8;vn?~H=|v|? zVb_;%G*OBWgmV<6W3+*9Tr>ZGOe^-<_UYA7#u&RM+%<$96kXx z(+QkNc@b&NZ&W5H_mlBF53R|r7j#@{!mQp4$N7-X`E;tCOi@j($Nra5lb~rm8aX=_ z7K9dD*eT}#IAbg5^7A?d^OzK})l0<7moMk;-@F+y7WaNk-x*4QD@LJiStX3&<*;xd z{enf1*e_^5t8M{S-OmMvhR+KRkcllY$oCx>e%65u(WZ6-hZquOHdOf}hZs??hP(rk zhEtV~4#1}^6n8to4#fqeVkGpyxv2(UTszfP7LNq-dcj^mlLuU4o4vB@Kwluq5}WV5 z&9i~a`tl1P>=dYY`+KPS#{|JEL&9)Zu z8HM<65llN^8To|Iq=_TXL2MbGqxWTx_SJlYpWn8?xbh4niqh#$5Rfpto%YGGG^;I^i~u(DD@2{CWu?Z8{FGp*%GfeqfQ6_q2 zF~an#!Q^7MK*=#AQv{PHA`1tqRle?pq2oCA!T#fKC5rEv1kJj7ml~lYJPlND?@Yb$ z@2v$kNOc%kc`#*6{oG<{tsJ^5=^@e;Cs!iwde1}hRM>g>fcDm4y1Z;iO9(G7zmm5D z+PvG0uB*}u+J$sy-Z%q6`b2>J#@gDNv*_TeQBUU%GbsUvIw`_V;J5bsdw4@>rXA+( z-19+aB@K{@X#7Qs2(`GUE54ZifEKiCFYkdKW*}f!-a_F5|5? zRh0oZjRZU(l&;q~etv$D$J$zJi^eG!m?#iiMl^WgE|@?^$!dF(*zYViK@5XkDym+R z?PGZtFkjBh_)J%=hZ__&nL@?VLa)ln;psVxI4pCWgF9DcnuU=ZK5?0t zMC(NSI#7h+OI5fVigBGigV(MEAcYO^Ys|-2mkCGu5k4sqD*&p<3Fkl{5COg^^*fI+ zilPwXWhjPLwt9oeB`y`v#zexqvk(&c1XUjl@26RS(~xPbMGL`}+>3>@K%NfDHY`N` z;A}s%xDos3jT$~v@8#(sElxCR?9MFei5~RIs#0-mZx8I4^IS&yt-^>|G?7<~2?60t zPl>lRwf%}n0x6!2V?1o;$P?~11ZdVMi*m`kd1hVu1#tkLtH0#lEECOTtAtA!!F|l- z8!7w@5L7~yq)!wl+6z&9mJtP71xeA@3&%BOtiq&tNZpIqVeO*;i<%Poi#fNFo_x{y z#(Oq^MDP39X&evvConYizq!S>#DjaeSSkZl1I~xtkVBI<`+IvO7KE*S`een#W+s?k zXA2MO8AvQKeo_b?1#Z>DrgJSezG<_{AQsHnnBv>B!X^Cb1|*g@JaY6J!Ud*`2Wsz=+D7 zG2HJ=r`MkFln>NLQ}u{q&3OCZd&*x=xkuA5fjydU^YhGe#S}jB`VCCZ&;qJ27~>v3 zgk8U>cyg^TT}y(@oLI1SEHp&YS(QuQdC^DQ&>tvV4C?HxZbmN-Yd0?=56qIwsuoX} zp8;y1yntc<_*N_(0}Tix(gXZIkp{__$W;Q9>uk!(C5A@BXXI`mjZ@oJ`ta));|5ny zOI`hqYT9;!#5<8WsED&S_%<#1%3xr7NCE9o5_l#zOWnJ${}$9J?EA*kNeJh`8N4YN ztKnwgG)lAeJJ-0w%uvna=WrYPo*r^VppJuTEwEh+X+ zJ!rq87TZ>$^_+(_3T=TmiJ>}{Pm+qqbcjs*;i{{#5tiK!sH|T9XlN(2#Ks{Q?;r>z zDsPu5ggmqlu+r{RBF&W`?K)@@JNxH`<^J3-Ee2V~6XheFpzo&5T1(g2TE6o8I+443 z+Jg<(8OJshOOI7OZdvr5HM*X0am1pv^gF-IH;@ctX&PE6#igf-VMX}>W2j<<0JBi4 zd~DY@v(3etSUw!2Z+mXZ{_3}z7h@|HBW z2BdC9zY>Llh-H;SDbD%A95rBjg6{2Q-XJnHeUi2pDn4s}pf z52;SpTJfPw=y#rJaf26em8D)-eB8xSNANg;L6>(|B6$pz;6Ki;|N7mdkQHD`E-x?V zaSs-=ROWx*by1sQOa_NX-yoM#2CQi9X8|$&3|*+d8HNUQ;glOSDdZ%7jjhFpB6G-hsjs&kw*JMq{(W2__CZ8ZA1>(|KCk>clm=P9J#jHf`WTT851qzKR3!gg*7{> z+5Q+!^SFzvF%eE^UDud<_89D>v_&A@?y36uV^~kBoFb*4Vp%J0>$o~zMm0s9UHr|P zH@1*ifZ_0lgh6ELU6<;(1e0LcF=Z`*ut%XzO&<99*TGw*2x_A?76uXfY(&L~%rr>y z0SGnzAO)nD0$dk@wlcsaEAp|?hmZw!u|%c|VbD@kAN(5L+d&-0p(`U5p8`Z(cdRV{ zOSDyB3=KgM_@YI>9HhIvPM_Qc-^7|iC15e9g}nF7;A&Ee<>7`{t|-Lx4&%!-EHGn% zV^IWMC*mCe6aQFvjOdAnlZ4fHbxu)1VKq1Y+i>^|5N9sn8 zhKl78pF0v`8~qEGlOn`XY}&UwVz_fA&tp*LAa!eha+chk1}kLJLS{oq3Vo`w2P4}< z`I#WW?l8z^ZHKbkpAadV3Au`mZc)S>LdvBU`C_McVBKfj~Mg_J@I0%@qJ zv$!vT%_S1ifI_6LoBRgeE8riIoGBQwSOm?xqo7VUH0h}7kg_QUF*-nQmQdjmXajl> zFJ^$bDkCe4kV_e~X$beZFp1dy>#PUv)VfxDeyh>R#Ncp(b{C+)TM!n9u!ATl7j=Rb z@B)-YQ;BFVD%^Q2U~+|O9pL9fWzt-Z!d!3NU(4S)15b1J=RdX9T7U?No?D0{GZqtf z;qx1l77w3!kP62jqDoHlFX&c(FN5GS;G!=|Bo9WwY$R@uvvF^%TL>%%$tY64U7^Cw z`pjHNg-W- zSC<~LW)uG0726r}(!6r{#0|Pq;62R5)eHyFr(G3bY!&kT?gU{Q$PKVNaJZ1*)>!DD z4*~-U%7;$e-kVH8>&1dJvmM-drKQM5uYkg5j4$!qd|Ux<)8<Ylx*y%)G**`K4d6HPIYh=d?p~|QqtBTwsXCpmCuDJ9VBL;M42(OXLL#UGqx@0 z2`yUKgoHFfeDPWA{9l`srHt9L@5VTGe|Y<_;WGngj`W)~ zHf{1M?V`K7732mi(E`QQ)~@K%Y!ZXt0*x6ftv-(W}zl!Z&uHc?;lHy@yNyZ-bzTy z&FMLF;=LSKbyrd=H#Gio+|NrO4CusXAIE6x*diY_0&98vZO*Q}W2Au8IW~3n(*o^* z4}+ph*iW3nNzGj;l<+GmXZo%F6&7Rw|7T!@57sEDZ6RbifBeUIBrt|HPKp$tK5;Z+ zm>{mW8}l6-al@n>fhM3}{2E^R_p$$T_{=tRBClh2;f~el1ta%rc}Ony@#DB-!fg{9 zojI}SCS`=Pdbd5f$9k1saX zWiP(VSfMcX?VFZz!Bvv!2^yyh$IjaBhHpO;CiKx=lPAl8?}7}OD1h| zYFnw;^~8C6VxdtRU6yq0jvbJl5*m*(ptDQqSfx#BoJA@#Yt_Kz%kd2Z2(Y0sp7Ge} zD!ruN`<;@(`g+g5MfSw0cH^ZKVsf8Wd3Gl0pARpBT+|4jmTq=d4N=Y52@U0`*^>5+ zv>7_(dnNBix4#aSlHa_-`RCvce_x@#~qU!xfUPZ^cVhUv0Zal z&XAc@Dt4N$6ET=L#8MPaN_+n)qJOG+nS!l}gjH!hoLRVdt`HT%E1eYf*_%>vFc!u3 zlp)q+I7IhG=*kt=7ee$=>(m;Blu69dyc0D17NM{~g@}W*Zl%OyUE7!btnGx;V%2M5 z83|W(4D9&k)cZedynaPWX6Te4(!qVK1yLSGc$9d`p*1pV(SbSyts!TRA|oy-E5`4c zPB)KI6i-)Ozd5t)T1&*9hUrgC3BSP*!WP$+%YtL6StoZHR1?Ok%M{Om_bwTi@fk|=r3mQMe~ zw#-8&YK6v8tZ4I}Vk%C%Cj^J2^d1=LoAs5Lgzbltzdq2=5ct-;jj>c$eXFk{dq?Z= zPT|KJbJC3bSuGtE4g*x$fidFmwK+MXUuwjBymi>;WTZ+GmAzNP6Xu5(F6mnkks<59 zw`nsynX&A)wIxK!q@C8GudYg*FUjum?0a4`Bfa;;lW)SATNT8);wcj??MbFpQxh}1 zWH+4B3^=Yunyv(V4{@4}T>Wc{8RShzv14Fgo#W8Y3Szz>KUrUSaC5Hzlc3g34!Nfs zy5|g5TBCwK*q-xrG_~-6mb<{I8U+x5bNZ^qc^cch+ctS&Quv;f1vm8~h}q^W?HCv7 zzdPR^`|{L&!`O`oigjtzf%6{+Z}N+9>#YT7-k$k*<$C-^$Nc2sI-k?pi9L`})_*QV zCnxGDr_X)FwDBmes_jZiY`f4<9 z(}`hD44?8$W}CL$TSE=mFG1Se)3h;Kc1|51DoMV!=Vyv$#=TsaJlCpj@=Tog+tbfB zRT^zio4*D51%(NU4n$pI(wZxbFpueADNJlkWRtRe|!!EiNT9{phn(Bb}3Q7m!hKXu}t9n^&N8> ze%Jqz1@MP(pE~UU9MjC~ZFEj?po2lj5Ro4F-w%r=i>8?bt{ALH=367~(To{l;E=br zQe*Wc`g?*wW^E6&pX)$PUTG7?Ep?{j5(i2<0tmo^4{Jm5P!%{%^Y5V02uUcDAhy_C zgqDgbnxo55N?nOWz;nV5d_zcyGXru7kc2G%;NUF>V#Z_ZFQhEUgt@^f?0E+2HeZn6KfFF{g+ z0<35}MbyfolYjzM2FI+I2+hetK$z51e9#6X7BVl@B<^h?yBLut^Rw5xR#H#gZ6pFbyQRsU7|agAI_8wPujw@jk1LhSZ4Mse|QD z;=lyJ6!kj%tj8a^6gm(LS!f0qPoKy^hhg%zGlqFf+=-jO4uj)hIQD7ZJ29%%BY-x6 z%H$X}0qP3!$PexMq0a#q8xBM?-XVANe|yn{&xIu=@c{Q70V&(i^MyZjFj@90sX=%a zC9i?rs`7s&?)-yoJ=DqE`LTVeT#fD_@emmq8BO0JP(Z!MH(D!6P^ec@Q&o$Kl=+WT;NMOsHO{&UNf%~xQgDiy^?R@QDNeN3l)Dtj zdiLQ}D~7VpqiJ#&Hu6T|{Z4ia895`cenl_@l0+ww>Z6&R(5;P0cz|F8_uz;4ZRe3X z9x==l58V$hmUPZs%>S1w2 zQbI~6sm>k|$0ZoCrWMu1F1%oTW7QJ;R{M@pZFRr-$Rf_`vy#jlM_i&hF_=fDoUfvu zKQCQZ&`^q^C`^~lkDj_@Cz!~TrjQ_~DW-h)<#!&NSV?B`@T%6*&)<)Sk&Lj5W#9hhu@q6f zjGMfrth=9?xr*|>LH6wy0v2^=WK{i?jiXv5&FY#$uR{8mYvX=%3ETcK%R-nU>Hf&n>1sWXOq`DrAbx(vPFMOZdCJ< zfNsEd$?j4CxtIbDmOWm3?&oI5m%R<7*7|db2A5>}Lzg4{CQS2eGp!6b;uMmjnR)gV zjL4L}7iltIk{?Ubn3`B%$xnRAJ2A}~Q@wvjaerJ^aj00SMO5o9t;3CgYbI^C>T8yp zr#!xqN0-@o$wjgA-WZ@|?`+=qCNmz#=Z;zQ@p0^6=dvixEe^4329J3;=$`le_Lp>f z9i5cHVyHB+-zUPAZ*o>q@|n7kxZ?Lx>3}CLS^;{Fi3V6Gn^BjD5_`{#!>luM^2(oS zwr-DZUr_$ImBgZ`c8g%(PDHis%Z%kA=DyNbN(mfZ<|-c#2VZQ@_GZ7`VmbVksu}c| zVSxo*uPi^p-t-M-?Cd*Q<-TrSrF#{$7*=gjN zR_2=cKdyug#vBRWV3{tE*Io14BHYIQsVkrSUB)hyUAHm7;hy!i@F-?~KVFuW>BB$F^h-1=>&db`=v(06x4NU#sB`3S1a@Nyj z!2bj9%li?<&qDY1vqh@DY8(FoKceKmE$6`8=ws}xl^+)@<|C_ZD|J_mN?v+>8W4#| zKV_-`uV}Icb^4-o1z$ifzQ(iPVsRx*F3}NLXR4o7Tsqqpg}&9FeEK`{VRty2*8qXV zHJm3=QKuy1??+U-1{_kp`TB)Gkgokl!f-cgZpOxTF+GX((cbC?dtrOfxXBQys!32nbv6`x|DV4 zj~&*>!q&I%PG1<*Q>v-dHPY~;cR6|WTkO zxiWI7DOhMcefy49=k?ps>v47uM)A0?73)-b$IldPvlME_sS1ZsTUgr?2l(gAw5!mj zIPgDi&tEkfcb~Q6(TK?usBC)JKlnV<%|1DERgRLIqsVeuLo;b3kG(C5r753C8@IS3 z{Hb+TH2L(#Y31<`MyJWNWXR2kg$Mc|3MzIaLpZL)KTsk(s+4fks=wHuks!3Jn;j>u ze^*zpVaoXEx>f+`yd_p3#aP1F{i#_|{m(7xt^=+NE{<}KY#2`K=lJ`-oTL8wDSy@d zGNp18=5|X_-*-V?n>;R4?I4UNjcq)vlArt7#o7z*&v=kO-&oM3Nxk2QarN(kf;|CC zuXNK$=Jy&eBX#bk53xv$i|XF4-NO)Ny%*P}264p&!X^2qsW^!!#wYWav$oTtMF-bx z+%}(1jF6H4m^Pj6-}IoOPTY?u$O~`3868FUgeF<9b9jlKxawTc%r^LoB1YrJx_a{=#gPz zYkb8u_4`V-#q$|55fSXTzuDiHQM@QUkN5D*&jlln6^b9#DR%C*ls}dj?pVcNUJ9_V zU;247EqRaREX$h?0sEc!NUI0dS;AU-K9B#>z1xh_n$e}c6BtLGU2i6>+gGQ zAAT%lX{HBCR8`dLliH;B+=?kC3cPW9+U|)SQttZ)B2h|U#^C-VS8dOV*IN& zl3{a2t+FiDL_fozN~M{9_lJG0O<{Y>Wo?_gpIsT#H&J{V!DCs=+U5+rH?yKO9Z6w} zSNm3D{vyJWH$igEt)2eMKqOacV4|zw(F=G#5+}cP%@jp1E&}{RnpSLSiyToy`&MS%YcEXI`G=Be|~@ zOcQyVi-*Gjm<#d~`!~MSx-mWJsr&UkrEL7IPw0EfrS$;H8<7h&*bMenj(qC42_11y z@5ie8I-lW~FlFILJA;+6d2fG)c)g(!_@0yEM}_dcAIVw$ zZ@CS>yi^#j4r?bcSJZoz#Awl_dap&-OmNn)%e3)qgv|MYw@rR6=+S1+WR3Ax~u3sa5>pu9Mjmc=!#WOZFHyL*IO>u=Pm{q z&3D(8V0X}$-dQ-m^!0}v-Wc`rlOhIZ#k7|sG5a&FHS9i$F*}pmr7!jENFKfvZap?u zuCk3)_IsK7j5m5`#szn3hT|{{WNg|p5(G&zL>JM<4yhT48s4S-p6zh!@TQ%{BXW(3 z5Z(BN3|o&ec*3fx-aYE)BV1JsLQ6g_DQLw+3_g9Z{;YNV>uglkl9&a)r%bfI|H4lD z8}lPNvY|t_t$E!MWi!XMSs4|X|KIzKoL#ukkqzTlK!Byf(uItb(n=0 zQEV1c`8sh+=rHQb@}95h2QMnFMt9fqQ7mkSzuuS1<94jAshur5eZ_r(qL^yelN&Yp zH!(lOfxfNz_?-qJ)9O?Ihd+jE7b+NNri(sruJMz+Z6Rs3c-U98y;3r9cZ%_-#`9>B z-!p_K#;J7zr|s&KcdSLPCU;t%cgOhMu6v4gL9u{QqfJ)6r*;h8j@|IqWyZM6A^d=DLq8<6!te;Ucif}n`gINo4}~rk znt+G^B5VSuvAWO@H`io?JzByuVUOd8o*0TB3sJ^}!jka7V^H9nkZr(z$dk^o2MQPf3-dG@zhXt+VO~cQuC|;kJp7X2Q3ckhp-jrGHP#X_TIgGC1 zt+lq9XtCZYij0v?)eCY-PdeiYGP-XSAD#EF**|x1B&~VA)MLSY!T7!QP^inJ;m#x* zRHf!Ej>FlD2a;F6ytSigEA)2oXERfpvWONh9tR=XKU163_&L(>e5?Url?s07#&Vj! z(p$M~=0|dYQ)Zs0G%fam8Rco*LXLCs-5C=Z41-T?{qH_sdxXCw#>jP$dq{fmtzPMV zMWfb(V%?W?owu?cvNRQ3k~=6+{WdN>A8MR4_<&}gmFwOcm&=@{?qUfwr zKWC&2HfZo145DXNe<`8tQrR^Vl;RytiIa~Jy;UV;CP)X0kbf~=`!%fA5Qc}*uR%c2 zpP@+c@P|dyEMNRb0acFm3wk<_a~1b>qNSg7@xPHg<3Yvrw$-PB`NKiEl80BzdDM-_ zUAwodV`A2Ufn|C2GutETex7>7x`tV(g6JOtgT!X${8)Evt^Q^_ylFgcNthaI@@u|0 z$#6h{j|53e`0MIW{|_}XP{{le8yo%i4Z`74`kP#!BJuYxR4&3@Ep&Ev{EbE6;i=D! zi~pl<_|FGW`9cMA3t-%_L%73FSN|2d{O7Xq9wN2Zz4c<-6CB2EXGfBM`^KM6^1qZ= zUl6axJ#nhWmq39O)>h}z|JB2gqeL@~VxBma@pULmYa1G9h8(|Vqmu#kh(nm-pDCo1#V6az22K#KK@l zKtyMoVL#XZCyWjDC(Td@KVPBApG5)UG)=_ok3*r?>~L|lnGjwV%Sp#N&6}Wp_Xoq z&T*|idp4GgNY)I*Xsm;#=D}%1eT=w;kPT6{Q@QFpCb(e-nJC!%#2Kq;i&I!wrz&;4 z9@6FRf$xXW26QBulY^M!JihkoQYfTy0hw`w=~t|nJ_2=#y}K6)suA56uzlVm%L3RN zno|s~uuWPY_7A*UQLKaS`Y2`b*E&-`0iQRL9t|Grvml$Q&sXypm;*7tL@56WRT!a1 z*adMA9hHgsN$l z5}1&!+7P{Yx8#wlMSq5WS;`{_k$cf3zSKy_8wHz$^;Bvi8~LejL&P0k6v#%AXeneP zxjbfVvKk>!nZNC{Mgo@JzlUAaK7j6QifUnsXB{F$UXCLEO{y74iu*i_7*l4DX(V5; zBg8q4D-?;O+popyI6EH{s=p>+j3j@22m>d(p7wYd3( zasyybKvbFfr`!@_jgO5Pq{~<`Z#Pp+9%CtW!6qO`u(WQtBjkBZBZ8h6b$j!id7JfA zi--vcJg!=hLB)>jU73-=9O+XQMnosnbu$JWV<=?ja7YvJ{%VqC8tENEoVW2-M@T!9x zyH46mkb%bFTcI46ya6H5$o7`$o$ey7EiE6SIBwm8{OwU4oYK%@*y3kf#4suyv_ao2 ztyjkyM9!9zO<)qN4i2~8$T&}t#dyqCIItJ(9(c!kF^Z_CZ|C9fG zrS8~Nky1#8w=08VMlN}p*S(WEii8~(aU+x|NmXQP3>)-C^gAD$PoARyy0}nwmiHbM=ge{D(Ur zP?OaE0ZG*lGz+U!bJhFDCXP`opkY@)WhjySoHv?y1{J@Ohg2guMr%$x<1b3r1!4b6 zkABRY9te$0jYgH{7^{ZzVq@mt8TCB+`a!8n2FLG(FwbFg`KJb2jVx7$&}Z&Dx79h>_bB@}}$ORV$8S#)rp|jJ) zd13nCYR0f7Im#6H;RIM8GzxU96Qrzy9Fonbyj6FK7hUs8)5zKM)p{zTnUcT{xXOkm zyN}qpk&QYDNGJ)lQxb@CZnwAh9+3~v0HNB{ezS_=L z&tU&6wLE1qv_iJM4yOJ+3Wd`U)lhH<#=2e zf1L}?>+S*&ySyz%_}E2&E*}#>_IiW75!pZe@(Qd!|8HCUX}9ug zAX;>J6c3irW9=w`h#b%AVV(9k_L?fxi^H9bm~{Qt1E)q4ZOUcS5laUR^W1MI8QYAPyG z(A_I;h5`gZ_Cjpmc>1oZ;j|Dp_w}bMquhYS8(6tZ)*2oGdN#guv~U01mwSpYTN~`1 zR-*l%J~gEXr50d(^9NzPuY}1@1q^9eI#Mw6UEOBpGh5UahIpV%F!$Ta&U7Qe7&>H- zY9+>>%mV>TFn9a36}^8!Jn-A^(@q4aDErdCtRX(N63DYCZ7i-T0vA9I{cADH;nGh- zZoh`i_XfdOn7IvGp==#O^hWgePdamluoCpXQc>48pd}B2*m`5SsV!Yyf$q`XS^m^$<9`>-wT7Ak_6F z4ji3NvLSKfWjsEzd2J?yA=!eJ?E^No9!l1ih>U$BTe9PD7UD#*$G#P7aDE)jc(U+* zn<-R#8*BON{EC_kQ=|bH1MsXS`$Z!2t-Y zweEG_b6)cb!#|%NQ+smF(h2zOjz}nrAH)g8nb&yd>#eEYD$zVVM5}xau6TOlrJKro zoe674P>Lf+^r8vJv&rfsV!W8WT@VT`>hfW&+@+1!GYH|MNAGPrEx8t9Ey48}_qb{< zO2ib@_NU|DD|f@Xi!v^Dj%XNnU@LZfR+26Mq8#}Cd<(&QQtL-py9yUzk{~_dqze^% zi--zKtAc?OnT148icDemmjr=7m22xKP*}V7J^MifHA7GL5Eyan8+k`~RtcUrm8M3I z-I$JsHr(66rIX7Sh(c}Dib7H>HejIlq(bA4>ue~?en|X-;dB!PUt@=^5%Ui4##`U< zQ`XFskdI==33T;MuS<;Sm6B`W%l0F`(H^V}YF9@mO?}s_%O*_HPu-c(FCPZEu6(fZ z67NIG|5jIzmHTS-%@_=+9VI3BE)h?FOqx7{es#%@dpz$HGHlZR6nA)R++_Ok zmvf{Kn()UvoK;Wdqv`J48Ql8%l0MmB&hU^hkBWG59&vA{D|h{tC#yHu$K-sU@_$K)=J%6i zL0oVynyxo$<;-}L|La^5bvMrB?dALA?E1QN>Kg*+(5d#Y zWglRUeK7nP{odvA96|CW+Mosl!KbfkrTLRy946BZUa1~ADQ!D1eQ;4^8V1rxC+%G@ z7KbEWDvW#`%3(xUE{f;sAtIry`yI3Fx6MYi_AHA=pM7DJK*2l%8;qTC9!S#FS z#89?1emZL}kjg3;e5Vx=ky4xnVCLu8f~TnRJ(&LRUeoitqr&$cSM6mG<}r00wehtX zeZ}U=uA3v~2Sx_W=94QC>KM!xO|x@mZL>{wQ=Uhz3OH`wpj|IoJ(|kuh!kvxL`XKx zoYExr6v``| z7FYTN`&{y_UHF-;V$i!n*s<+?t}fRp3My%%r@CLd3SI2?Co2*4Z4=*A#1u{~eXhlz zF_!0TEzb9u7-U8L91yi~2gmzZ3LgY61ixDi(d96X*54`$1?`d;l;k{MGgFLz53l8_ zGO+aWwks!dzNcJuwYT$0-!3D>m30XpJ7UQTrG1J)Yo`6n;ftdi%j7L-1~Wlh`%@jr z=bW(q1HG|%;2GhD#|uZjoyxiL7RMI8XkQd&*ic-uYhU_A@uR-~IQqkKtqAnWkAu^b zj%DRj)@SdDDNsB;gO_jKYfyjIm3NC@>qqQm$&;*40H?Z94|VFKiAsjLJiFapKs-l_ z$%EoDxwF^cRr2Hz95ips@ujE>Ua3sFygtv`BxlX0ctiz`PE`I9&DE3Cm$OS4`5ZEJ zP$^^^>SlXrD>{U+=i+ng`^6ZqW+9cP|9WZK*~+otb?vRNyyngmsfLAz+)qk1Zp*5K zaoar?cCDU$F1UVYp$OG%E>du^hgs>+WBN_t9#pVHbTqqGtjx=j68Z9vt3;*_-_}`S z`U&0AZUdKNc1=CUcXX<)%b4|aj=w%TC*ZX{`JXe<1sDXp0cxW$X%C!SHX^Ptk+9M1-C z`~EYEHIMe2T$oRXQ< z@%}{?9(KoIdludwOq0ms?4uu%E)@e+{jG;eB5QfY4g<;Jo3{;I=jABk1}z62{VjD+ z7Cvq+Uc;ZHby=z-LD~v+TGiv8SVZ3XdU-iXT|LVef$GNy1s}z;h&8Ee%$FAXzft8J zW{o&oSkq#6bD6bOBPopew|N%Ae0pUJ{DR2yvA$QI=H@{rQ4#J*SYmQPd2Vr#s#{sy zF_q(H4hPZD*RDKU?~X1ZC>KR!l6n<;49&o1LpV|abOPV`YO{NsV;}M ztd0$poWF8<<}y}|N3oVF(Q(t+?1fKL^JcS8)F?qawokO);@9%0gk^%2=^V0zog_YG z>d%wQ8-~DG;fqk6-m8<>HQI$B=gQkM;nT!QrYyt!c!s(s!Yz#zh;g)2O1T-TfzD~& z_~k6iI$3!10KFR{P6=KeKF~Z5bu*zOZ$9y-1R?nI1S2GpxH)vQHoearWKK6}H$Nbx zis*RLups(RW|oFr%$w zsJ;8L77f?vj)>QJnfSZ0?hfoMQ}4T8m9@wRv2L7^P4xz1$tlbxg^i;3OH6Y5fjN9( zW=7jEjfsA-UCoOeh&*eBC)l#rz%_pGp3RJU3kP?-GHVh0*Ud6bIu(?ZjySu}Bic#x zf=_dFw#43Bc-j28EWVP6kwF~koEcM(e&ncRiux^2xMO*`dfaZL-*xjM4E1E~0u62oKcWI(y18 zzg%9YX}v{+%?x=rf{odu4I}4-PQ^N3;}2hJ0*^@vJAts4)j-HLc=s;$mJh}1o=UUK zSewbM>cz8# z=C7Bq7IcQJVHxs)df{*nnak&af%i8(pI&h^rs@=C+`ivW$wI!WC;GScV%y~du*Qkd%J`#~g z>9IV-7Ej>tiQ(}e4NkzqQ=5rx-j;Tof*2`7D;HV4jyzO*iL?ZxU9LDSO!@2S+<9O* zF~L?Wu1aR0(&?@KKBX16>38P?sS)lM^nSY&pEPL3!Ok!xEyB~GV7h|9S&a2qrfPh? z=$=&+{*lVple#A-`5%M*T-D{r-JZutaJXsZ)H+PFv7+I&*VCAt)OlcQ$r)**@`;|6 zDI42KScH=4pCWDs4DQ#xGFXlHn`+%4!dcs z6t?GVP1hlUtc8Y1`H0H5bk!`{7U4Tj-r)_p+oH2gFXoaI{#89v`i=`_y1z;3Y~U=7 z^?RY=U85&_c@zcD((~i;uzV;dX`KW+^VF^KltBaf6t8{sAE{Lbf0-k6nrilF_!`hNyrQG|-1n+pl&-&n{kfEcX%%qIWY?Zdxa z5hB+V17xy)j!)s=wT}@{#ix$Se?t{XlC7Gr8+LvEU)WG97pipr{517~YX%=F*mwHQe) zMDzYW{@uHO@<8Esa06EJZ;dZ-)gL0CXsnEszD`ZLXIsy;52Fq6chj7Qa^{eFYmUdn zbhFacYwjMcZl!7HdG*V8{YE1--ZgWDDs-*6H0i?RW!c%;BF}>@5l#e;T4EPr1ookx z2--lR2M->!IdtCs{)kA1G)8^miJBVi8es+#Y7$<;Nd-O}M{_WDI}?sGH*#${?@5<= zlaxdQh7tu=NDT!b2GJyf6}j5yJUHd;UyPR( z@>`8Qg|Uz+?G?BrIeY{-UvdYb0 zQRCyfHR_YnckG(&2n+&JOya>L$n1-ac`1i>R)d)5|MAfpurGk(`_Tgx`oG*SCnbFNw8o!z^mmW}H2yV)Iu=JC4=!cuu`M54)n+ z!Q$ocRH3OLJZc9aYlJ-(gh-Vg6^12v95C$w|ef(u?HwRnkq{ zdcsY_08!!yMt$+@-z%SJoKU>7&rG(<%~o?^u!^Hzz-4?{qdg;Dw|9(4ZFQmWaD%2D z?T9zO;~xD5l3&!v#x|R64Am$P#-qN47%X^g?j%vw8A=Z2>m5`eyhh0f znsmUnq{ZG+;;R$d65{qb7|epNR_s!Wdlwt_Davy8^s}@SZ9Fe6ta2%2O4QwVeg3_n z2%H?UVohPd_H3r^@nv5J*V=48IQ$##)NYBIm4i&|op`QVWE2u=I&|g>^)q5h%G)pp zJ}5Yrl9CZIBe+`5jLxeBq}}L;!6iqA~(z-}fW`V< zB9FetetXjVK^h!sU`3nfx@&w- z$FV>;hnxTYTXp|{dv^wK5RpcsZ?;!0zdryKneI1vG#id;EuLhu%%3QOtgRjGXq#Zd z^@dzM)7L6tq1S?uv&)}IomCZ4)g==+P|&+{e2TD;`O%IaXkD{vlMH5Ac9OU8hB-5Z1#lL-$s?k~AeH)w>Rks` z=Jwzy$~d+PKwimri=GrA;9}R+HkJjL>XHuJWkJ44#5E3Jmes~Gn35d=~kmqS!;!5Q0mp?jRdOfU3xnuL^2H2-lQy9w5|!<&^4*Fx2m1@RQKzoDSI+ zdY88vq{KBlEzSyf(&7a*N7U+h`5lU?if7t{jI5d-9v+{WJa*eyo@pzz5FoPTZhzi% z9Y_bay zLDJ`9;nl|@-Jx?9YYb)4zkiA;6^sG={2kInWJ=#I@7EQD3bb)54bdxpRC|_PBpf)_ zP2O&$Ymd1p<6v5~wx}Vp@KiZ_J8zV=w4GA%kCN+56?!*n#SfY=jL8KT=a~1VJ$g=q^664Nh-u@yX8^7q*UN-0q1Gv864 z^Nr7an^x${i#9~A^_g4x?BkP4hGqU4Ur)UaXHDxT%e4(`C{iDuf5FFk^rv9=kxFK& z%5d7TA!_se*I6ESo+KBpuA*HfQ~lm~-#y?zB~_j-mOt)7^(Bh9Jy}h=%I(L~Z_m`t zQL^S?ny|fX5ER(1H9&~<2a`Wd<+SITCq%D|-n{nN-hqlyCBX1=vjr|`23WZ*lmAlF zzh4YlVu&EN1B>h9aXqeGUrf~nb?GZx)3zmhgrqO_q;0MwzsR%TJ@<6ztU?=V;dJ>j zqu=a#P;+&m%zj*;v5+-?-G2OSnU_2_mBsj-#}si6+f5fQ$OG!KFu2rN9{^86&OVWg zBjR&|W&50GBoTi2DxTmt1Mp)6dqurkA}E=GSW{6QFNSYw-?F0ZFl#P>7w4#zn-*Xw zOJyA|bR8GlXr5{T8Ko?J)@`h5<@D^~+Ea~eo3j(?sz@bvbTLD#ZMs&wum5#+YFNeT zEa2X?G&q?lFJ{LI@mIP>ijTW!#7(#vtVFzOPKY9)vI(H1RWf-yUJ43sNG&spKTR_l zng6|l+>8nTjD27PxRVYVJc|zD+`RXLeKOla1y8ggc^kmjBZ_kw5sOiPu2x-G=ESj% zZxU`CS#Z(^1fn40(J|-6sOR$G*I^2v+O8*4(dJb5qz6IbqFi(kg=IAU{%P*FJT4c; zRNH3CQ%qqYn>9DRa-~|{y;Q2-ZHk7BerV{-L>9|E$7yc^(D%g6PHul2*UWgjblv!t zgodgjn*72HFN&1OGXayaMyY#L9IP4?EB=0l}XhADYm)2B99%rIzytx^b5uc zsDQHcSjTj`Pxxru{;tAXg7f9{VG7?jLFlAh@|e1XJ*ok-`!H^?@E!w;!yda(89hTQ z{{^s*`(7DC(7At0ncLUTz3pcFw93z$Uw_qM2ln2G$dQ`yVG2H7dgnth9>+auL4VDL zjlkn-hS(qfRy)BEYgs$OSYPRZEuisS|LnY`3Im z1T?T%tM}fakA!u=R$+`XU|lHA z*D9aZU%cGdJ@zr^C(-*gD1d`xiTv-943X1HVhRUX(64vr_8+t)F?s)|&Pu^cLb~aj zW#nt;956;F>}j*dGyD8MJBp3BRb1i%KZo5O`IghZM4GH2(lv)hqjx{1oLQr(yBSh$ zm%VAArPIKxR5LoL^(5#fFF8`VhG2h1qWU}Au*+5W9NQ%R(D~JSWuus=^C)@r`qo}U zBwnQ|ec6Vu2hoiM=jcVbf+vke29d`l*QXxOm?syGcBc&*I*+z~aU3VN^7c zk;5d+cN{n5O*n3-e0t7K|5VF*nm|u5F_VycX0|DqXz+czUMHu^j-a}uT*Fa--4}o9 zYwYSuXeWkIB&3p+7I?}D8AiQ+eSYK&!Uh)Oy+*SBVdZEAr)kBQzV+DlB8`=mJfi`z zyjp8%7Qzc?;nW;t_b^bg4Or#vpf1X%7EY!R^Unx8HIa7F#}qEM=;6t=5S)-1k;9mb zPwRB~Ff$^l7e>ytY5zmi;Kc-X2m9hOsibOMyHA-x?`@yKjxK^tp|Tc3+U6YQag83H z+A4fGBGH|?i(vDP$JH(`5|uso@&JHMu;M9It%o8afTZ*xnM0-pjF ziV1~By`!L20kl$TV#)Q%aH@1rvbLzSG#FB(u;X&3MK+007##|rE#V&Uh4_9&OdB;Z ztc1HF_cDGB%P}K`q&hhLC9f6iI#I*RypM-?cD|y#wkW66$$<5 zbv~S@CNB8f9wxD5qE}^PF7l7u=hk#_nUM^jxX!{5+!1LP%l*FEY4U>saxF6ZIE-jyyJ}(9RG`0=^ZZKkAOG==e0Og1N!i$#$YzF|323`(zIL zGw6cP9_Kc&5-!f~Kgo<?59~|q)Z2?KIo4Xu(dxE{GQOj6pDmYdCqgsXj4c;qM}B8 zwl;XGQ{A#RBTz#ioue+5PD3+<+QU{aJuhR!Pm-u1*OWq4zD&%8Y zhol>*=xR2v>v(ZXt@>|O?BHhLHSvY)8Afr93(d44`||oo@vt_trtUoRb=(GL*FT%F z&8yMoBVJZXeTxB_X)i8*urE?J`!H7;_6*ftM4xmQ>>5&E;O~c~p=NkdxO|S0C$A*7@>*>YV-}m%YWZII=`@s)bYfFk%9C985`v-=UypeHLx=OA_{FsrIM?&i_{3q}~S?8e0 zN7;z&W%`8IxRWp&9iyIhmg%)rnno#Mrr^!x2K{>8)V*DMU!Z;S;?)BBlw^lR`|9Cq z?(Pg{of(Z0m}<{j)pS>tpZvOHjs9L@{f@8VBm<8ZMX|LZ(bCH*8(UGR*j;a1N!s1m zo6CL5dF1z)fiQ0EtiIruDGB+OGI#Jon+`XHuX}4{Mb-DnnvzM^^1`jJdOOcU-|_Dx zxepQS)HZ4wG-zr^Hcf)3==Dd)HXj81F4@ori-0yN*@g&dTl~XfD-BWqd?3y> zqDaf5v!LOw#}te(Y^+ely7BG1UB-DGo9I1qPHZyO+WVcMsdAP$+TZNbE$7W;Clh29v1D`|iFFb8t5ZU+4cA@l5vFN{` zz#D|u03$?_H2xQ~=j-TJ-lRf1l0+ynS_?nCVqL{@fBL?OyMN{(jp} zLN?QqpKVYPIVBKS$xCbB&aT!-h-Ru@Ixz6GMK~Y_6UiLq`DMQU_lVkKFfy*jU(nXl z=Ru^ff~#n7$>d4w>-+4>($CBA)5&y$^L8D7U0(JHFRYN#t-5_UFZvtR%btlFtAQar z$-wQ>rj<&-68%i7=YhTZ=KcG{wHW-p_&+?VobwLp~NmJ3H5 zhDy|w<~MA!_jIo5bPx*TuM4crKH%&iWCks^>mCP#nh0#~#bEBcN(i#~vjfIN%lxI2 zZeP1hfOK$5L4`;-*99^KQ@pNS=%ZBe3B|d%At>1Lx^00tjUx`!jgmf7XXcn+7l1kR zLH0*ESVLz4;Rrd9g9~-Z6Tv+P#37zNzf9T-dX+~_lAeT15FB3^dg3+4E*O&rt^W4I*0TNnk9u11mgl z50GEony)ZP<#JJql%y;JS5`pqfR|7DF9Z)D*(|Vyw}ZRYfpFlzj&^~xuhx^Fmh#k# zgMS<|Mlyv>tcu=Mwx-tWVngt2+4869ti$oWZXF|St2Fz_W+MoE`c-`T-?*=15pa~- zGo!_BftsYvf#a)(tp|X9zlyhL$rb;o=lTyo>idha{A5HpsEFeIqjXabackZCdnM*U z%|yvw{A|!;M0>#FK5TFsumDJh*LBzQn<0eG==9k7d%zRkO)!oS-Nv~|!fW}5 zKqn-W)5Bw1a$q5*CCRRY;nx!+1CLt4>^JHYF;}ptI1prZL_DUF$OgZ5rHieSa^koB z>9DJ(r?6ZW5bPiT`UKU)!3ydvbg%~OJNH7)ya8I^1xwpxYnb=@DJkeH>hlC6;|J2x z)BvT`=bczn?ptC~3WP^Iq&(w^Lf0L{ zr#$u^i@6x@Ui?1Xl)O|Lnlktg#<5gFr?k@hMb6y1J^;%Oo@ECJa^i01QvX+3it-|J zoYnjm1t;os^+!l#<$pQQ>bA+fjoN{gPk_GGb1M-ELk$%W;7SE{_1sV0B1%&iKQsv}-_Ssgq!s%P>vtkIenb4}cj!Q=LNiZ|5!fAt;|Ji+lK%_Gz zzk7zp-z(I{CF|?1Wy-6ns`mY~(vezf=VV>eHc!5ZNEV=#+Lkp5N55{{8%oV<8bW&e ziEK?->>;4e1K6(sOpD_((9cG}$gYe*$79-UVl9=xp`Jx&(;mbdqO%3z&$kY51nm(B z1Sr47KkYy|M%r~3(=c*f2o~jAhz_D>_Le7P0e@U@_RamU1-83vPjd|Mm)cX>xr zDg-&o6ObfqY?`M$6bS)2(K~Z3IKn*Bn6`;r!s=yzAiu2ecG&*v z;6sF!vn4&X#X~6Ymw9$yMEkfwNXAkh2uj>-ewM=G0tihx!0sawj;O)(M>Q#T-6}Un z?JSPT&a z#AVf`U`=h6oB`w!aP=drtH9Y3#+`3!YSM1mpicKdpHc1oZXjWGAm<)-h`VLJ&j=b8 z7D#3|^$e6Uo+G;U_3o?@A+FJdx&+#m0rO?w$9vXSrHmQl``e6LEKZ*2>xZ_s<2wrVw#^2RYv^4?XTdg}J}(kSh&(MOD|rx{YQxOlv0-?vV6vNy)-l(C+w4__D+)A54GXM z8amyC*oK+|QOgNkVpf%28QO;xU2R%JI|`_TDFVOC;~wAywfNewi*h>1Q&!d5h`o(c z$mu9w3R=I!>FW|sjyEL%V!5q- z7F6`)r}X6VwwP+OMm0a)tBn!HoX0Q@a|>&f)G$PJF&>diQNSSH7= zW#X3J(;GH)=<9^U0ct?(78A&c&%t-Spp})~NoMi$i!sM7>^|~A!uPVIOvAHxhpCS> zY|n@KgkBqVG)}jK zwYrc$T1!6|9CFLUv#eBVyz=lG^jcD=y;tLI>j#J~cnz<_ZOc6C4fKkcrslwofV7ezwLD6KAo3ljZiteoal}TBdjcd%)iH+AFk}@=(1+2FCE-R>q~kjzP~c9}%?E#H_?DJnKV|nC9oE6tc@??% z-Xz34Q*zgJbU2gv$}eSry3uQT=BeOtUAR>6?ah-nsUGFeb-5M^P0d}BF)5`RRbN+% z(@>{84*0k`-uCrGbl~QQGY@6W@hP8Thw}-Ph({>WJ7;Paoy9?G;TRE7gvmX}+wV<> zB(hE34&K#v%XRFw@)E@hP@i4-+)w`F+(YT6C8Wx)r>LH2ce6%Y{rFwWZuEJdlCT>y z(WUUQ-tL@8>os&wW^}6UN}>zj_|Qu%>EJpYFLo^FW{OJYytw<hP-!`fICEAebUxT}Ddxy|wJ~mX64Ha8=Uq#z1M}ha{jBM>nY}<&(7xH^N zgyZLx%ukPr`ItHgXbg-e)qZUENLvs|wC9Z4Em?i6d4VjUk4HC&`Bhf_5=+UYx7V;S zL6v+Fk0-2m&{2!hF}36cvEz2afK9qlL<*1cH51e?eV;dCf*oX$>cjAra#&Wh48e(8 zS=m*P={HT>x4Xs(AC%=-u<6c^jU9dxIu`^BrMi~&@61X))Y(3rK5cQyZV2a*2uX0) z>Ty+g1ZU%&7uqfr3E%65xV>!ljBl`H|KwqcWSEm`cTBtJRbe*xV6`9I#gQy*e1?^X z+w8dXk%f1)(=#wT>}OA1_@r`ZRNGdGpK(-=d`@lb8OC?Ysc2S-di%1Lvfv%6=|Se_ z^wA5hU1!)L`XP>uuf28`6cXibb8lX4i-pyQ5BSQlVQQyOmISLLGPrHO+@s0re@p&; z0`btd^QO;nX|}6c_7Xe!-n|r~h^E)vSHciE>Sj4t#U0KZ7(&89wB@M(p|7rmON2 zpVy^T5~GJ;jKI&+>hJ}v0G^Jrf8lKa0)hWrmsgGU@YvVuBWii zh;-V=hmc2j5!Mq-depc!?_4ZB+oo;%x_#+k{WDCN8TpDnTfO$W@h3l3-1F=?pT#-OZPWTLk5ay!eVovL1X;8Wxls$E8Dzp-0gI=FdyMN zk)(WKVQ=rL>j*asTWOspV$Smm7X7Mj1e7(h?>NjHdDel2OgeN1?O#h=tCt9vL&IRKz82K->o8<ZUHW~UT3R!2f5FG{Ez4au|JP6sXmD_m@C>9aW)_a@v`Z0Bp2-y~sK7YO#?W)LDWxsbz zX7}{D%YSVYrrkN``Q}dRyKeMH-rUl`Sk6m=(SwHCtO!m_wFGfW+sO{?4Lju5^plU6 zh!VbPLa}9Vr}0|%?EqWI?>skT64AQm&x+*L;u=KcDy5S#bZewAz3Sv7Wy1cZM=+($ zykJB%dX24Y=ybVpqNQXF4x_?Z2K&&0d6oL%p-fr`HP7BvT;O%SB0lT-q=+c1$j1il zNxMaKq*>bef8Y9U9Ce%ifz7SeV3A52lM2zR}ST4 z!$5}B{$W?%?U(%aOPT9Ay=^YTq0FMnF8<4Ty7RI~nWXU*7=zK)ae+Nz@+MfChK#q&nq17Glbufm+tR)lYXj^1dF5-=iHM(am$u8TV+pX&xJo@!ZO@PrR z?uP;X3{O+i{r0x9>tzIloar);pYb@0edkVm?;3ZSEai@F z|ACOY)@pgy5ZX)~pD$92;m(oX{q7EDFU^w++?uxZSEv+A<_49$Lo;;PH4{5Sw<=8L zI@+VVL!p4+(0=fC%hltoRUws0)qq)5k5UqsZbtU(oyIX{z{%-AQjZ$hpY}HCntVw) zWu@>u^tfKg0R?oEa zRk!t6xT!P{gPgWvT+3Qk*}7eoj))NHzdyj(@P-?9gSz6kC>>@u0eUy(jmj~8XQnzC zv?tfecHSvDc@)9;5-T!X5LLxGI+`w=*lgjRbcG$UC{4Uh9f?@*PdQ0c+5hvw(2q$< z)%zBUJg6Z*w&EcLc6j$e?KRMQpBn+NbC8|DXC4K8Ja5MeFrG{xRHvlo z@|3+p{N}%&!zXfI(d%M07l(YEn9wQwIt510o9{B&|j>;Tj=r!$mqr(nyA4Vk?RAN8tmac zS`fPE34mgu2MB$_UCoE_^_hQ?xISO(PJIm*UN~da9yx>N29wV`kThsOZnrn)Mn*&= zwP15tS|wO!4BX>t7LRfVLFESEJx4DLyiUy4D>0cyrNu=bA>;*~z>yQD_in9#OC2^B z0LKPn1akL?#`>ok{lA{DDB{K;{6KzKUb@~nf06z;S$Gf*74x;BZ>4~8-e}SLDuN;P=?3I-^E>J0G{TPlN^rvAt!*dxgwVVx_C&BNt6V;UW{g}DsG+5N zUH2MNTo);~(xs18y=95?X}hGvUWhQ-hEPrx0#UFJu-uu_+ZbED(3F3J8Gp52v3d8Q zmGE9C-^L86zb3zflgsgW(v=?$X8};mh5>z~Cvo=5zWmq9X~dlWvRqeDXni4kb`qbu zaU|gd_Al8dU6+t4x!j+b5~GDDU^UH{VFGZrjpbxzIXSizVT>7s&Itf4j+oLN9Z&0l zqfNvSz8J)oYQRD14&1^@*7`GF3^*!<^+`E&es&_l5rNA&xn2?y1YJWnp=@V=-B{dP zMw|Ay+jD3S1F^5K!yj1+6wzJ~e_25|&ja$8LUe~2HpqtEaJ*e zsYI@rcLFe_F_@F0_rXl3%iRFpvv)UdaH)P!bf*nGLr{%&u?f+XFlblfid3IU*P zy%2^Ru60-?`*duhUhq!bV$*o>*RB(@gStD=kMbB5l?g-n5xioNO5WBHC!8FFK7u(A zW;V(EX2Ldvii}p0frvQBMQA=o<`u-QmcTN0SH?=!`T6Pvyk-Ymy}R4%I#L$R-m_qQ(9oU{`wMU~&s+q<8IISqTU_SF%&-MD9%9s-N-$<*9Ww9wyyrYO4i` zK6j;PK>H3QQL9HfAO)u<$|b2na{t=Tee+Ov$NSX)$hCyaM?m{O!6?Q|%;XKJsA#}f zTH8jV$%nG{foL}I?#9s-4!(2?CZ$PNHx^6MG#Um5jnlA^_&VNB6Agn#V1o?t4-gP) zm0C}%m(4}(F&V?|cU0lO#QIUNVqKG5; zZ1wzkmSIPnWlL8-CG9!VTyK8GSVvE$Ai3Nao?*IlUnStpU%OLU53%9uLvE%9JHBOl z{zoZpZri2>virAaudmKeVYMmV2omU+2V;RJM2sOmNvJaCLK&%QLYn(!#~ZQ>Hx%c} zEIc#EcFg1@=A<5)wspm_e((G1=QEIC!OM?icszCY1E!i4**{NxB%=Eg$gb5fQm1!S z?-Kw0l~z+YEGt@Rpwn$jkAQ10W)e2#dypl z=+YM8@!rhs*LhuK2KmNCuvNcS$E!ZyedrEDFRP}o>pJRD_}pI zPxbsm4uh7*mN3D2KZ}@J>J*uVUcd8fpbNK%)HJZtxU%TQjD`K3^J5&>*g{8qDHy#`<*g2V zPhGQT;D!y>*?rm<;}z6udo7OZ!@Y=%NlO?m<#(vc|MlJsPh!qnx{Q_C&I!!j1={y- z@fPn|wSZC+I3{)~&X=Gd8in)F4)+YE7RkR;CA`1Z|FTAuA@I94Dz>UG$D_p#6NsFG zzUfu`sTp3>g{ttaYN_EssuKe;31tf+GNzZ49f-q-q6Hxmm$@bvX-5vjwvqDsODLJT zyv{ZR>8r{6AUyIF!dC_gqu?7?O2ae@)9PV@tMX^k?iSQ9LU9R*7h3<`T9UEmzb;eq z7|Z5uIFzIU1$lUB89n&`DxiVOgYVuWstUAbP$rb>xy%IWPX~B_>)U?vkw1a>FhrHV z1TpN(;p^_%#fT%%ji4=7f$jwyZF}h^I(O4+L*!jYb$TEp*(RfgG1Tvq&1DzAc4H zC@{n{zUzw_;QQed^uoB3M&xopP5Gb&k>FlUGm5UTO8W|GcX9sM0*TdPAU%)4a-?!v z)BEd8=?jyr4ov|<=`iG4z65#74j$b4(kd3eeV2;aFm?`?KtJ=q^1GlDIg&O4^@3Bc z9C=ft44i5LIB#(l7^M*sfU82i3a93z1OoBnTKbBtaAXKn?*wxBV#f^+j+as{Hgt=u zM8A%}(~aU_ttZ`|{2??Rm5A#9vHu^v4Wt+^D3EwK5KU4_M50L}ykqywXz!64$Pw zpss>-A>wE*2ITY<;{>Mn;XfK$MPahckVOfQ~_bB)+Vw5u>aY&AYo9s?n($k1-KIs8M zRhsA~uJ@AJSH<1pdwvLVLJ5?p#~xf&`)|JEOcJku$xhidGB=9FATxA5IHnQNb#~X?`>FBmnb#yvN>Yy8QtST4*if)8(JWcSyOAe6U2-AM2(dfE zvA*U7+G&L_I_!Tj=hg^eo@f6B_1Bwetdpafs}0RSb|Azzl`_W>O=cbh;mEttr=;?j zG!Ns8%x@C&lCx=PslSG+{waem)X0KiME=|VC``DI47U*FidzA*w?ROkVeuNGwwtl} zyS0`aC2Z{<9LxBYQS1Sm>=?YT%Q9)hEsdw^9FJ012-4LllHG)-wD@K3e2n*W;q^;) znGO93(w4yJOEKXsvtrr@;sMvY5$$3_OTwBoE2X`gA0rj9HZ%%FH{#jU+1`_Vb9WGV z&)6o<<~kQ{=o?_Ds$nF;jqh6KRX2My^8?Xyo#Su~0MF(kd|N0Wjg;a3cdT zxyd9YCDp$?X?o9PK-clMADu4uX&y}63fEl%jyqpkc0QKYVyMToG*LxfJY={=q{@iO z!77@46e}XA0gs7J*ca|DPR*vU*(u<=ojj(|F z(hz0^#*0N?)Uum3#7$BNnlsSzXf!hA@oJt?z6i;H+IcV>LmZ=lx)LPkj@7a5}rDlRx0G6m|s{Vw}xXFWdzvCqA7K?(tLy@w(%LQ2AIViUKjf_ z(-R7HV$~meO9zznK97(iGyS_`u|~BMWHiNjNxS&5r}z2DbB&XFX}6MAgsu+rL4)74 z6n{a;wUeD$gv;djFnPN7pP#4uai`vjYZKDa#$}$zt^;>EYWMkAh01B{6OSO#OPS+g zkWp3*OTC&wL->6hIWh7NM<16jezY_;Z$7KPnt$*dv)rP5o;fLEa7Jq+O6YDKXZ5l1 z7SbFa4xwRs6=O-HS?vy?k<>QBA56`!y5&X61TDt|SeN{-hI_dBe2$GrY{*g#^$CzU zbQ@5EZ$J2J(P2kO5(z3dUTHqQYXt#nSO!byZzqUb zHn}2U(Y{PVC1{yp#JbX9(H1)S5M|h*l)#|)}Lj@=p?#m?UQTL8si8 zzutVnd9e`C>AZ9<<-fiV3V`vA#f+zamB56)8vzLH)Q+*gkdnhl+;R1(!4J?0jR9FE zJubKWW{at_U)x(2h)CZn>Ud&!hM-9daMges*bFOtvcSk6g4@H#)%5|MFtP35tF zJ|ALLaRLU(qk}`bZ&c>O7&a^ z)t#VQc=k}UIjzp$GX+BAN3Uz@8bRIW@!llvACUWsJiLVy`S+3jm{*aY-gTOqpz}Vr zwe*~ib?TmQc26$?JKRL@c>7PE1m3n>GYk`UWC6o|$@H116^F=v=oypN+Lx=isKXp3j*TwuL0WOo}<@rgW zy{zD-oz)A#1i^ZE3dms35ozFhc5OGs5h3vxOJUd9Okc1tA#vdzf}P$7&>hbOya`cD z7-Nr?AufK$4AH7ADR}f~;BsNL09s?k=j4@r#s}7z3D*!1GM72DlU~l#XOr$*3#|8t zz8kz29CyitqDvo<(f(Ec+ebVqe%7RB^l^`3+JdV~{jzoK(tNSSeD_M*anzP`z^;Dc zbV*F5*tk|-o?4CpT_>-&B1I+$|M76pvE9D*|89HOZzNN$?ECy)MRZjipdxHNo8+Ot zeKlNQBw*5vo7v#`j(USj?{kDiU9STDMisHcsPmYk=GO0%sT@@eSZr$`M*j~M%{A#` z$R%7DV3;u9*bilb1=ub|spmayd;hD4O_T^~?<#VgdHTZvHT|T@jdp?-L~Z4CiAGLg zX4eUDuCty$}h=Z@!o=jK2W?*8*O_d9LO z=qYy>5n+UyP31PO+=50FR2f05JOItLd%{dX>plI0)y3NT-?Oz)^MfB_D|V({+XGU~ zQDrTYR=_C1%J+tC!o|H7Kut^ZXrEIwft-z-wn+utEH%5EbG=X_Rz*T-AIN|OxV%V` zGEDU(H_n%ejTIFdVwB`qKjpHMULoLANN*J_*cA}!q!?F-8YN1mzxpu6#Ssz!4mM5P z#{zydTZ!m?bGrt?d2K6n_||@D<;AOJC!EEB<)ndCPmk(Wz^*>5Ov4N&>s`t_A5G)U__^rT+I} z6{u%1%VL7N?jE!V#FG^w1V-WY644#7wbB;rWsyyOm_&A3h*DqGtA+8qHHu6OIlCU- zbQqeg$x9{vvev}w%kKP*KX34HQk)R(D&jzXnSA3#xrVcdA&V!|&PfYHHrWqY$)B4f8msCzFKAm5 z!Hn*F4MAL^<~g!*p%wwG=O@=GEIR#8>=wfG^XJ{#b9*a1iPlY*w6pBQb_|layz@uD zOLq@VY>%D|ECK7)IZxtcO2XWlTioj6++(>_>wawR!IF2AkJn`OPq4;sQL3?NBfMVp zr`k^@4t~~BP@|nbu~XMtR2|pjL0#_CnbOeQiE}&&`+R<%0+1vg;0)J+FZ}BHweMw* zTxPKhcAD}`N;06tI^Zpr?A%&tmnSfmH^eC75Jy`|q5T+a3GxLtA>pJ(6uGIyRVs8l?;8y3R zuo4)P!17|9PHR^}6TWVE&=Q%ht2cvQf=!vm8v=ySOptIkiQNJSC9|99s5W=dqYk4T z^@AxakWR~*etQ@4(2g|&(h|@VsehcqI~Yygx;zY)U?oG;2uEBweG#96+Hq>@ z1@sKD88A)`PhzD(5ZkyQKOLg$2A5;d0Xc;GLI`JnCT}>7yxNO?<`TW0|J5)4WvIds z%gV69B9f#`Hn;8S>6FD^76sJ2gUd=*4Mi+#xVIZxU74q*9x@&tQL_r%Gy>NqFO$tn881zh)`?c{kU;K`k< z(|1v0>lv496Ra7?pdCzLt2J=PGltRHyn@jhd>26v$8pq}nlS|3C$~!tzE0=&<`6XT zmyogS@tL>Lp-J|-t{8YA$PIrPIc-FNrMJGisamHByfv4r3VBNDf$fugA({1Bi~DrJFwhlAlgH(rx~m+vom*(!i!p*DSLM|Asgg5d z19wLqn?nVspH0S;KOGIcQ|=JPFVsBEHn`7=WBuiRbJK~`40zSJgmYV%=MpX($1ON( ziM~udU{qq)ULoLY*=!_H%vW@}RxRz=Ygb!-a-FJkP&bDfT>{v;GHaG5fpFB`Q$-PwD*6jVLQaZj3-|8}A=Ccn`oI zGujqpz#re}CUyACrHgCGoZo+5C4>ksPMrqDZNZnLLzD&#^kC#^n3vUoD2~@=>*A{u zQ9`q=!Ue5SX4Ap(f{Rf~lJ|=H!FYdV`$T)GF_YENF&p>z2T)mQRK?Yua4iwFfQRn~ zp0@Lsj?BzW;*X-kcYlCdFW6aP=*K4wBXEL5uu@x8yZgarY&Bv?WEybstjSsJzl{4B zqAtY^>`^uGkKK&4h1?p-0VG5kqw8TJfOXEX_SI>B&Br_$7KVebIg>& znB&v;g@-UTeXQC>ZRB?j{&p3$W=QEyw8W9vgW#2ZkU7XW9}MoJ z$G+*(`)%8y*vsmX=>d>H{-vE#a^G@XR{bX^d(1#RSfn2+S`WzraRf);G!xhOM3h4@ z2>)#>E%5}&81=E#O=vja*85+lFXlC{N>FrKf})2p83Z}ED|@@U1M9ZSZZM;u-Yh8L zOA-NNSt$Xsqr=iAL0irbu(Hhm%uCa1!BULtm zE~x?5jD=9tQtl}-I*7SgoH{z}IZeehfqL3JXzaF==pckl2HC^8yiQviEHZ6DFsKD& zIUM|`v8nv%-g=JvL9?PmVgYDV*!xg1E(3(#mu8u$o+sG_jx#BKec-ca@F2XTUBXVA z1(^yl?Tfk6diNSLcI^&!dx5rZEbT|0XO!&R))UdT1VmngAbS3-MBTP8pe|7dXAkJx z<*222u1ek!;ZGdn2Q?<8=mh);_uM{s9_payYZ36+Yl%x>MD3Q>9gQ*<47v6^F4YI| z`wU-DX%doajT~E5C#lqc7kSb5oVkF^$+2k=%~Js6@}Hq%YYLx%HDnRILOlM_8A?1g z%kJYMQ`x9RP)TnRzR-8xoHHB{wmnRCn!AJotlc7ramkNwBWpa^0o&xB{_H++=NZy* zq+g-r;dTvN?%u2Gd2UOGz0XSq_w9y3cUhx3Mub|#e-=0ewl~1g9vZSuY62z2M z*eE55UXdlpWQ=#o{40GSSg>sce-9X>_wgJmpV3+u8Ll=s`0pJ8DlZ?10q5-%E9C(@ z^^w8~FN~j5F=tN&lKCOZ3Q+X@APG4OFnVBn3hM=ynC0ex8|ZVtOrobmUAH&xzLpqN zEmzcPe{HeFa!0MzE^Tx8!x$9-0aFx|1wdOLR`Nxw59;C4*LM0kZ8D3D^7l$o+!%1` ze-5l3S5cK(s4qFT@z+=dSz1n*a~^%+BQl3v3$M+wofhAAWZ13vkh#I7-!icvnKgux z!!vD>K=Qe+3S?kTZ`gc2_OpeX8lXv|DP20uRgAaO0ZhEUHKg%h)sR-}- zaknUo*LddG46PKgA*cb1t=PWm1Tm)l zCkUf~z+sS2;Q1Gr(FmuWfO)wkH@?S(85RzUeMJY5;VH+7pLR_C!Y{M+E_)tt+6z;x z@%=cap?IBpn%rmh02V$#psne(KpgJAmr-EnT7SX{Ze}}_=|8+p4m^6$k=gB`RhY+rHXFAmQkc#;oN_i z{q44&__xIrKh}JE#SJ|$)HjwPG=U?iM*Ahfb1{1W?P5zl0kPyq|Ei3XWd-~Kj5S#C z5cHND2qFG1V1ez$amd*gOJ-HO(g9cWyV%?7c%E7uW9WI67-nS;aOmD!8CEe|??vuH z4(6brQn}RDz868Ld=CANY~Yzyya4_i(1GTCTu)8ks--F7jc!2cn!)&ki^nbjh4Ai- zW*Dn@u%$$Bt|V3}I?RFGq7$TpF}i0+921yQkEyraV`X1GAfY0zH+E&^XbWJk>V$@#aX4)zW`G0Jr#_t2ablURqFXRN{! zKy(zz{0N!7`|if9K8RMWyyw8GgjMx^Jk}~LV-+@JSwnF+WVA$6e+bkXhSX4}zxdI8 zaMgf5S&<8J8LW7!l-ORzX_jSdmUrTqION6}RrfrFhDkG2Utal|Ew=buB@?ci#5(n! zUG?;5MP8fJtXAthE5W(BH)rB}JMkEu7MrR#uaznbcH5TBa*;EA1Yu-$K*vLaMMT1L2gUh$B_LV183uB$fSh!^*nv zd~ZWTFKd6edqY)FKY|cB3{xaBR8yhK^Q51+=^!aHy#U{pWfgP5?7_%t;BlCM(oh(o z7!xc7n9FzSC>smx7axP7g!%Ph66f8>4xuI)v+!hkL!Wt1WA@-H7{~o;lEeMI;iE0P z>SqDmlaTdq@jNdgm;rZ@VY*tn#=#RQ=trIslchB(BBKkxg1BY`D(i z!HcEV>sImPPD(l+dT-Bp6s~P_=(3DmNWj}C?m*VMg7danU(Qu48rWK29f~jTf??Wx zf)|&OS(t~Z#WnA+orRWSCPw8d+aT*oO}@vYz6SlCl6MXnwOWscc~^@sT<91d)m3bp zG@p1)DVUxo7u$8^d+P@~|JT45x3kdcl)F6SytKL=0rRL4jL7tj zorlEZgqei#RjFV-O%8M$Otyv+Im51hR_wx2fj>b?02a=Hk$~r5f^wn29zR=zrNXpC z?Fy3?D4-+?DJ5dalZa0X-$4s#fDgTN|l#cZIurmkGC9ORH4)itkL+ z*|7{kH2n!*#KZ~~Zr)HKcd+Vv{wZAQYhRrv!kcC|78B&gb}{Oj+8NIsL+`v%**aiS zLk|DYvhS@6V#g-^iU5oVWe1EuJwftxT32=k`mN*}Ki(Hg>%Sp%Zy|Izw#n#2Ei)?? zWouxg?|qfRA#L`#)=%n0VPyPWQlm@b156y#%y-^jEvet9!gZ&z$jvFK+q&JqiVm20 zpp+oenKSpY<>T3=i6aKCS3%TU^25y&k@_2PxWW*JlPOG7g$9otvXwTRtS+9{;UR@-%}(n^uW7$E8)vPt=E2Bz%x6rU`=iJr-j_C2(bm-KDrt!rx3PA{R@%W( zr^?jXzGF$+V}M4d94(LlCpVrpxLNb|2XA|3w^*N&e5A_61M=m&qCKLYs~yE>-drtL zDpls88_sKP%?p^jb#VWzSVB+`?mTI5-!vzt@yiv|j|Bciij-I9s-@43`H(i$ld-N@ zNpjMNEs(9}{|tQ`_l@>m^YU}7R=|`U*Rw}Fo40Cx#>r9>GrKh$f8lj*vtN-_D)cNH zeCH*PIhTVo^=9TGMe$@Us#F~R74W99v%n@KLU~zCKO%p#JVID15FnMmh~6z67bky1 z7@r{ES{^Oe{_yE{MDlW%==<}VLhv|O+thbOdaJ>xv!S|2bIE07Kd}Yh#d3emsLWq(b0$6Xq`Y7Kx#+QG4 zw%7c?_c^qDn0ZQS1cV_dVSX6Qk_gJvKwL2hFAaLTUr?5Acz7Q@rvV~R^FimLfv6=| z?2AE@>g8F-h!s$nFfxyY*w7JqZ9C2UVt$_Rt+Gcr{m9mO2KjmqCSsc3-N*cEX&CQ6 z0vGFt><*wXZp!Bue(|&=!6X?pq;_QiU&Fm}etrk#FhHhX=MNe%Z-VN12!232tOiI} z)JwEl@_=l=@7E83Uh?{3|-(R%{ox$fkPwxEZ_sb_JdN_TZ$%J|Hho zHcQQDg5oTRWfp`#qO%sDYHn-D+2YGGEk!n6(Qvjn8YgXyxaC|kIeSW4r)nMaLQ0)!W*fPv)!+Qt(PagjhFpnC3@~ zl5B{{5}vIlW-AQhD{p+yVRi_t5{4!`_^-f8G%hRH6FnaQg)`WL;J#iEQ2GfZ0zy20 za_ERLV88*7*%TBE!ceQ;ulu!08hSqhglNSZ(qw8%2pTK`s>8m9LKs*q7IXl*%LkXM zZ<2~bSb}(W6L2QU%BiV(Jp}0?;>VAp*_EhE!+P|EV>3SplX##CFn@r`G!--vv zRvdA|C+7!&6@wH@|yHznGa+<0-LDYuaq7{UKPM)ICy$^BRAVfk6#E&#yP)wNV7X|sG z<2KjEnFRuh#np!v#SehDxp2_FzjYm_uL4>WhbO?cS}Q}|6Fbjl;qL`r;!kHWPx&zH z04?ONfVgT1J?;Uk*Fg%9dRBI*Nda)n2lV)!E%rt<1oZm>+F1}suTG=I672g-Ap1!# zU?9w6wVg~{=sp8>U@^a$uK=6{Si1%AMk{1}SecB>;XuuWU}z51B5mDclZ$gN+2Yzx@e`=FM9vxENIe+iR@PQL?& zi>=~j2XOs8EZ3bF^4>-8AaWr@kCwVfus;JCOF%{Ve`#l@Bqhy6{72A-HqVzP92K5o|4fT zzrFm1(g>@$+bA%(lr)N6%7))PkJ8ebb$K$0{p;Z%47K=%5~54!A6BNmJ{*bHnMj1E z0o7<;eh4ghw5^d={b3Tj83?zz?#yxN8iE%WtKgUY6}&-9qh@Ib{4M4QRPOemsK!CJ zusP&%_w?JhU6aPpx2)QZ+#Kt(d49E)d(_sSMvR9AWxs(dYkB9)c(bK~SUB$I0#vu&@?W}E>lB^T&BAspZsL9^qgIWw>4XYAA?G*F)%#(<5oTjz|bfTSW z$%hA@VriK$S`x3t``b&T%-CPR_09Y`H!a1|7EYR%e|4}0$XFng;cD|Azv?YOaB=0MA zq^n3E7#MCvk+7_(`H~Nmg3caSBj)y#qq^0Qq{&YMynu(^iOyor{QV(ntlWNaPI?^9 z>92@o{t3pB@uu)#6e#ZtpCxv1ijiBe6P~;J7w``8KaO{(Asa>24Zg09bFRU@HX!tJ zIT)p_|Ni#s1;iqdqb3U*Gb^1gp9<;kG;<-#?her>? zld&zcU)wTxhws!fOv`m}jKM`PnTnQPr(0%8M!kDqrn?>&1&3WsGi<=)_N7p;}8-q zG~;TUO8Wx21&bYGB2rcqrmnGs^-x%;P+!(j%bn|1ROuRa3vyAF>Hwa)uuLuYoi7&$uM07+v3Np zP8k(bSPolsyjX~l*9zi-GOcP2j`-UEsd^?;iWGDEhbR8$E*R3*%SCYrMS01ztY;M@qgEWHx^SqnOk3JHhBa>ntudY@L2@w_4beJND zXqyLn_+}Cn_6CMefQY;w#@q!Qt2)U;Y4TL=IYVHjsT?0}Z(YQ3;DXAoz;w{LyN!*wFR9=0PS9iS6sSBS)O&M_Mz(p=m3z=ZGIUi+f_B!j7 zQbiR7eV>5ec1a$i=xiiI0sAFjChDr^?Ld-L{S^NwV-1Iou?3LJ@uqdL>A$kyibP%L zjSj{IKy~2z;P$jdcLx18lzXaWG&_2~)cQo}`k%$J--S`x*eFmX7OyBi&JC}3hEz8f z?~j`DQMmLPs6HGvx5=fxLlsR=18IwrHd7%n&$_@8Lr zrztPtD(<>caBmyFNRe_^R!d%pVQ;Bonsqrb)eVs8`afSrEQ7;@R}kTszcNo15_mdF z1Oz*xMqi!hqwWJ|_)v&TE9XiPXfxHy2q1Sb_?0tO*~*lAWqdcU%TC85SR)zFb4Cn0 z&40=>n8OiL1dw<7lmyYI@~h?BUsbE(jmRT%5NL7&lCwaTlMuP5_ zU)m+v2VrNd5Ik-b17KS+&?qr(;8&NEQ3$#BQJt%~Abr-GS{4k&I?)aE$8&Yh30r*ivX z_cGzmtrw!tNWGesa6B6SR*}W-gfe=k(sEknT13@51WZO}zh>qyC~;yGlpd;C-tCTL zNxm%Zbh$r3brel7xwUQVs2Q!wcbU}Uk)o&HxHT^(b-JVYH*8_RYbvI1DK7Fi`pE4!Be;3d~_dG*iqGeHBt%{@Nv55JK7&~L&e0m@qQ`*{28 z@4}P=(z57C+{tGD})6pu-{BCNO(fQ07{;abAh$D+MFPQ## zgyBO(z&Ky=@N7%bj5^48QcYAKKBff!>Ake2rnILtW@e1uXK7Jg;9>m49AE ze_)m8DYD|Z2W52P5TXMeug|}Kwaxwg7J=~)O~Bs{a7tnmLVa!O@rLCD=pr$B)gR@P zLh~nv^dPWteA*J1_nnLWPCAj8H~>Pqz?BeO;)RCB1+B(vjn#jlC;k~)9xmL-g8JYadO&KvN+=j{La;o;FzKx@eY$uO!V z=34!_epkpI@QxiWx5>3t@sMad%UY;ET2uexcJPBPef&34LyG8HoD}G{+~?`k80#hZ zY1yWuu{cX|Y8N-~!5p;Mgk3QC-$mj-?~ycGszuT}@|Pm~-yyXn#s0xUFZDgR%YNzp z?$dw1irkFKOT{L)!;~f@-!%Q_l@LR)w=@AME|>6{bCDI$BmiS zI#)ZT(ZGgyg=vrO+Uy?_LyCy>;$skE4^^y(F7!u761ofPc4S6=kx~I-dUna%WEY diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_ice_age.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_ice_age.png deleted file mode 100644 index 5053ae99e6e5fd0e0f5de61be17c9eb7982ca8b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109311 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 t+{ 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%0DXV~YYLd#wg|J;*hE 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 diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_local_diagnostics.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_local_diagnostics.png deleted file mode 100644 index 389c3b98b3970176e8482b10f9dee0473164e266..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232145 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�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�{^Qh#2bm%nb|@Fw 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 >F82n0^casQdQ4ebHJiY4xeqory-pjRU3e3D-D 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 diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age_dataframe.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age_dataframe.png deleted file mode 100644 index 6a3a486daac96c6d89dc075baef1cbababf11e78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101011 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 diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age_diagnostics.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pdp_age_diagnostics.png deleted file mode 100644 index 070a97e4947476ef88ffc0bde2d4e73bab9e9e2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164561 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

`h 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 z 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 diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_diagnostics.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_diagnostics.png deleted file mode 100644 index 7f13be3602f2e5e0af0a7f5aabc4cf93e388c72f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98059 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 diff --git a/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_scatter.png b/docs/source/user_guide/model_training/model_explainability/figures/ads_mlx_titanic_pi_scatter.png deleted file mode 100644 index 9488c99c1793ae17f25569c8fd66d158efa847c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24804 zcmZsC1zZ)|8!g@4iXh$H&7r%yLAsG{LAp`t5CrM&4w3FIDe3Nze#51Nz>uXRMU}z8AY8z}z|r7ffN!QL-V=d=Al zh!80{+M8M0n1X>xhQ%kss($Lj>Djs+#|MY_A_?CDKMqeseh!8(E~F}qAxT9{0R_!l zS^vK1eJET4wl21k1HE4{Gx2vU(sz%KZ7DP{j2jSOpB!1y?ZmoSOEo_tgIO`Ia%z91C)|>OE0T(ZK=)-E zF8IX9H{6FADX!2dD)x({7#{5N;7>_qa3XwCUvU&p>^_l8ky-Ul%y2YuUr2>gzwaWy zxH?!_S*yCWqRY847-9V0OC%-V<)sz8>&qza9r^i{#{EpJXd^ldlhufya&)8XpN~a6u)HtUUtCX5Vh~b~#lumLQg?qeeL+hH4Z1|#* zMuKEWdumdZotwmX5tH^tNyhgdQb zq$0V--q^ljbizu423XsyE%tt^`^n>i=^DN2#X~rv(A2}j$)y>LZ{)V(x~Z+!PVVpW zjI*#$R&8E)>90mQi17)MD*jB|6s4IqBaLzLw5?x7nc({NQw0gapWgw3fe232{46oL zO=%^Sd^jFX1$qt%XWY~qMyInTxv#vgrViS$*_<-tTT^J5e&Duwu8)=^DzhMwk21@F z3CQP*-Arr)A`^$pi7xj?#E2!^< z@IoO@42f4Dz`tqMmD&Xpw@P(B&4EmjpfvQh-MvhTu{;PaoY=y-*BNa_;Ge%JFt7h z*#(Y#)A-3-{>m3RCKD_?Fe@8Lahi&v9~V1V8kCbsmLH!_qRL(PQZ2?x$R*S6%X&WX zq&XXooCxdaw1t~98qdoiF-Gw+QG1X^7HTH`*ul2Ww*EQO4R7%Kv2U723rse$uPCsq zpj1Msd-lJlwd=Mod^L zR9(F2fPrr8)!H?bpTnz-Z}00B+%fcD+sB}`Q?7{p^7iI_AbLP*#eakllvbojLR)|{ zf+8eNEDtMB92Kq?+NO-ePzyB^MSX+C9918zDO60UPb5InO8P-Em`r1w?_+FrimDKg zaHHrhxerCrNBX|jbW`WOu5s zQkN(fDqE@Us(Z?jE6U~5?Fw5F)}(nxdIs=(l~v?WyHwkxDpM_07ARQA=F1XRsZfDe z=~JD{bX44^?DwOQpHiJNh|kl`)Xw(QuZaoHdNJm0sXMPduRjm1-S$#_NH(e@Ay>WF zRkK9JR#Q)HQHfvNr@%A*#?pnGK#Vh|YUjnd+deI#+P)+t=*nco>6vDF)6HU zE$r7RVkyWeZftE9%++@_<2BONDbpy51Bb4_o|4Xt_wNa~4z)xe+Ec6MguKdCLA&XhfW%uY4$2Fcc zSPCf2+^}|&RH=|Y2^H1}%?X!Wxm-xI6f>JDzABz7iA^jg#yNs8s%zRe4| zH7)omX)~9>8@oM|RHLSu%P;ALdgXepdJ&5{UNT;#KJoXqu;C(p-)Tb3LO7$(}P-#>}xKK<=E9cGp7(D*U&fE*Dg0jHxjoRH-hj?NRr6;hUB~2tvdnXhJ6s0T%Ub)L3r_nnR4zVZGru@Ky z>6kUr=^zYA4Pr!jv?X-a|J7XRF+#dek|5zMI!=NsMi_aAwt-ngy=zqMQBBuKAN=wY zo(ldM^E6{En>ryUt)1Ayl>33Zk+%~B12Kh>K}WCm0F z`$2^gy9tv*{)w5oOl0TS>IlnUp>q!8Z<7cUS_OURNI9~JF*B0mJe0rENaNO+@0ijL z?PBPJA$Fa1*+tYb5GDjlUP=+~Kv+7}z9fk*?He=|?Dcfwb1HNcpbyh>v?=IU9vioA zNNCt|GFXRRzpq4UiEJviZTNP|KH#NMuD-7YUm~kNSKDu)_O0+?%D)J;n7CBRz=?mE zqmAwHetx!w9=Z{_2=T@j?(R#eVu7OaY}K&O;^FHtL4L*(%~`ZvwOt!s z2AmF@y4QV&dEpt|9Jx083+v66GiLi{W1CIn`sA?EYSL%Yx3z`}U?$+xMZ1=?kxy+sIuK?dWqK65A;5J+CZ!$7+oSjW2rSqK~+Qc$FsM*f%*) zIN?*)hfap{C<7E~YwGLLehqEb-RB|6tEH<8w(KCC$bL(%sPZtE(C#V`*-{?4sG+y= zau+zfJGk^XNXHds&~3zXIPr!pTH7a&k;!9d(i89q^2pgKJe%samYqb6UJs@l zhYQld?5v*tn1PIKzJiCv)hn2WsypX0gQJ?f4+^g@-`?u;e-gNzH#kl^ZN3Y7h?obz z^;Zc158yNyemD0heP`vIfBc%}arR{X?&3tS`LUkk73adE_^H|cL9mtyP5ptN5~i*}P-7xdPF zxUf%}AZNb`F+Z4&W5M3`>6$Q#rtlHsdzqtvl|uR1lN%dnvl|;PTgAS44KbR+Td+at zjFcrXc22b-y<5u(`>swZ{V|XA!3Smyo9lhs$Rkv-9*(71BW^nYb=6HZrOf2yz-WNa zaA4pemS9l8Cvf2T5_p1vL4FJdg9Y9(fTw6C#GhXwTrwg5d8vRy%VTVB%V1<;|HhQT-PYl$2^gO{5Ae~})Y*v0-PXp= ziN~FvNC7S7HNJdBKPZf*>2tPJ*!=8VkT+}w;zEQ~BH^uQMMP9Ap7M(*@>PNe_t@q&@mb}6b2Fa??EGhAPu}vpef{10&A>i zZd|@rPhMkl;*kUx29FeT!_z;iKx|9-%n)QE&y-l~S)ITd6Zlo-nujr>*$LS0*F5&s{pwgV!7o(Xh;1GGs?96%2$TOgxAA zlmpy)#g;o$IkC<_EBf=FKoXHceE8X3AoF6QKAxh<;GSPkCTF-r;=Azkh4u&s)hq7~ z>!aios}vyS_jC<^{1c>KK_D9p#YPKl6`hH!26pirhB|>4ml4nV-h%7>*m$=<^}z|% zkN(r2RnEvmU-aK9krkJ z?wwSs?ekTFg7L36$+W4cZatKZ*qp|TZ(O)BnPq$q*(*}Rulea1W56L8qxvELUx%RS zP24%cVo`9$>cAoKr$00>s19MHv>0T+=v!@6Dm*p|v|!ZNrOYue*%2=G zW@bFH_7{C`58x0HzlWleGMV(E-ThjNao0^UkqT{Fl z*e`G9*y7yj;$Z%si>>VNoX8#j82{ZE$Q(i_b$cg0fjlSuxQrOUl6DhAaNatW7U}sB>b%CAq>zw z*c?pGrgxmkQ;=x&zGkwWXA_l>SX=M=gtn*mqt>=Yd8R~%v-$q=hs8pD#e@_3fWCO5 z3h~lEaj~OB%X8iFX$F&2RS|!@qIK9FjOW*H!c1zx36%pA(ArKDRA21rgcQ6-ru(s< zwl%yMcGmujy!KLTx@$JlI?M)$(~b7US@~|Bu3&M+6!r@hbqJXUHHMT`KPei9{<%xi zTIZ_CM-&K^DI2-wm$xx{eol%0Ez&2vuOV$_5oL938A?p(VWZPkd{i8RJ}n&a?do=W zyPeZ9{lwLo>!bDK@9|f`^$!~u%yHDXTgw3Rz%|)Vy)>As8wxR$RXOsbG4{SYi@*My z`&HA(yBtnZGWev#%4tMOfqZ@GbnM!Y-=pvC#!__d1>H&iajPfc|jo#DG8aAy$oes%;g8+AZo(QB*5uu)S>y`!KkGFWx~#)M?zoI~5K^UqLS zgvuiEE1mgqGh10&>5JV~*~U2qwX0s8vz)9wtzRg!>*K;sFef~9#TLf^QEh2hG*uB+ zS-CNy`Mx)SgRHvP{5CFQMy;w#Ecm=c8UBY)#C)y7^&E#yZYL#-zf*HsK6Q=ART zsu$DR-=&28*m z`BtL>Z_FT1t=LIfMwhv5LYq{J3bINYQ_=(^7vYb$%_>LPxTu2{oHxvSuqlNCAV-uNAydJoa)=O%s@;|3;(#B_#tg9t) z0n6I{XEpmbVT07+G!eu*8t+ZiMSr!}foNQO^E&^+oKq}tQZd`FambwDSoxFCqu<|X zJmZl&k@irj_c@_w7>tkA`#!X)yUpf3DbHz6NV**kv0h~vKyH!-;waC!q1_8YyprdH zp4>J&@ap58#wY3YN6jko=WGf_Y)JhoEH2#~;%i;4o$~*ii;2Y>j}_msZB%jOPhKOD zL;uAKh~RtV8zOc2h>R2s&atYyjynNHbys}!&E&+TF=Zm=^0AIQo;x3M`27?y^S8g zhm(jQ6kBX`sd&34?tQiBc2lTbL&M{;8`vqz-mmaYuPgBY7-Yp-8?DJQgV%aZu3}x`*h&qKrWrAz(&-}~l!^W{17%_|VU%bQ3O6`z zlv_?v+@18i-d<>66`piHjN)7hEo;4z?x|V;@qpuq1UXwat3lD>uqVp1dE@>LJdpwh z*Mv3B$Eyst$d*9^RIeOd-Bxcc`?y$YO(%w0vCzooi3hh zF*!(+ZTQURfx&J)?Yw$(wfrvI@Y{QNORbhK%86?4ixvI=Cp2fMVq8o#Wjbp@a`j?O ze++VI!^emF(D3knS8>(dN&;s5XJP=y1!2^GWQ|kvl|(E&Q@h5>S7|%Y9;XT4Atsc7 z;t&2O@-M?qQ z0ogO|D%~(K68Dz&!}+!vY`bGw^10HpPpU+LH(~U*dT5`wQxhTLzbMc|5STDEN?9hpJsY+W0Ok^I@w_;f*O;RlYxVI0U{a6H+vMp1Z0Yo( zvz5UFZfau&^XOCwiyAX;&CDWaC%tZ|c3KD}OKUR4!tz^^!HdDSSLEen{|p@v2RfPz z!D)e-6`ieKtg;ThFBb z?%t)dr)Ll49WaqRKWo7V7`B6b)#w$E#Peq{?ay@H8OP&s&Nq(z$Y96$lHDrMzV%u> z&wjB{jZr?1fC>^8rL)!N_SMFV6L5dhEkA`suv>)&oP=1UyU#sF$>EO{j&>4Hp@*&nOt(Xp*7oZ@!GH?Wnm`qq7>A;_s~}&a+sEqVArY)jJ(se zw}$upa1X&emy3&Ce9|{&k0?sP)DOy+cSd4ax6t1z@CQ~G1;n{V8S=aKQ43b#8=s+T z3L2p3R^JbQWwgGxWL3^BQ^-hjeT>@mYJB)=$|C;H_%-}WW&CmHG`M%Z#`^7q9H-G_ zzS6v!<|hg{Cewbbl89H_Sq};)Z+k1EFt$cAdeXR^h(eCkO0==-7G3d6+a5ftt!Gex z`P}i5Tt>0Q)5UpvRH)}*4d>Gauoj$*2tE?Md$@~TBn75V+maHC_b&&2bNZCGi41z9 z1uB%@$KPNr78}`d7<5C&vL&od72*V8meH8B+ranL`^TFEzd8Q;gcOwP{D9Y8rsJYN z`vKL@Q7mo1RsfF8YcZi#l3J7Xv+MBHl-}9n99Cn6bj1(mX$lvAW$#1x37lnDJUo}z z4%gBsOwZ1}YJ`viFEmKCRPk-QTLu4|dXPDl30fN$|l@=F_<53B1q5dh9T#adP6PFwOm(r3x6<_-h&b(yLK%G*;#xQ#A*!5Qx! zw3a#T7VsW#mmkqEF|)TSx*AHkO<+~4?lPkz@Y$8N$zE|I>Z+?}Lwu<9IvWX)mXb#9 zK0z%1s<6RyltlU?(H^R)GF-#ge@nvGx`!r!xn}Ww`z( z&zCqJd*tH>Brl4N;Fx4ae9UHobTTXA9Q0NW(E3aOfyHP+`OB`s+*Er zBf7eLm_7R?_0M?Bgx3@nG1Qg6*V!Y-Opc19)|k7W2XGr11%(x)%E5dMv9FO2fum{x zYf}%}vW5A`=dPdML(&(_LdQ+jn-duH3iE73fK2wozKy`uv<&1A5nJ;8Y-^+^ODy7C zb?;=Y=L8^E*0=;&>j3vc1txmNL@=U;_1U29%}dYG-cROdLm)hr>2}AFs28-(R^`MQ z3$S~XGDenKm5|eJVh{PC zk|3NcKaLx9sDpU(MKwX-yEfX-AZe1*g3Zg9$-dfF z3F6zZEr$0Sf`LdjK4Np_c|SUS$y`v*g^zBfmpCiWo?h}ZEv3N91cZYUDKsX{{}sag zZnAv1;fZsyssgjzPgc=dHVX1SzfV)6{5i~f)H#@3P{phD(bv>8LfI;=q3hA8BM{zw zBVME2=hpRLRNXb8w?*@?KY>1CvB@o4I0#`Lg0e~9lt+DfDXiTDDs246B6SpnB1`C)#pDZX7C&YcRgtdWNAzZ`PO55|>eba-uN{6h6K! zpP4gEAWbLo8#{cHS}}~ti1pSj>5)gC^3{;hSV&vYtul+6<}Yoz)aFmMNHltgll%N; z;q;%LkwiJfd!4Osh$Ld`5#D^kv`*ts!se>z%|Ugvasp7`1m)SY00-zV6ll4%6?&4r zTt_qoLn-VNUpB#IG=++7W*Bln7I#* z)^?uiB;%GqT2gWppNzw9AwR~5)}XaHyOIv^lhdBbPEwgjv|QVbCsAuc&yI!xW)9zP zL<08;;`T7+REwWnb6g|ZyI&~*;E|KE{qvW_T;}O2uaX@O}XZa^!OTB!4g#9127~yUF;@FS;nP zT%(PJ1(`&f!*0X|*S4x^qBuR=pK zKBxRvU$kZsJ0dUhD|#*oivp+3Z@hoH0>=RB_#W#>4RDG8(dh}fWEL^|xTjQbxjz@6 zW8Z@9b9*Va(I4OPgz&Zh_bA&jUW7w^N@cRg3&o$bnkp2!o0idR^W|SRT*anU-*$0b zB_2rhPv9{a|4R3#NB>MB!zdXc(FTa@UgzPO}s!5`ywYIY&CWa3_Yb0H87mSX3*$S89Pk zhabMa`r9FKCP!O`w+9Q;$puXa%1A0F>eB#!+mI+j=K{=aVad=VH74y{J`ZU+nLj{1 zmJ=>8h-kw*OT!pDNUqrF3Bv47mqdhy!clUwT)ZS8Kt9pm7yvj^A{+HHX(Xb8)65b^ zucNo#TLSnqW3|;3jR>Km`jgngI(WwMK{~x|T{Gs-*$`R4f9=#kSK{EY^==x^I_$P< z*uV)bV)8;mTL0H~i9&mY_lC7?-`3y(? z!=nrp(PlkfHrN}&#m`nPP+8(2UG|5jd)b#}Fhtqkp-*kn` z@3hu`QQkrfk3(05icBE&SL5|ts%vMt2+Mql5Bu-JwL<7WdiOJQ&ZV!VgF$)%u77cTXGu1coLl zFDvBUC)u+hmxzc>^EHNJt_?tmuZZ!#hPwd$(BKy7tw4B05nn zG^PJnsdvN=6+3VxDle7Vkb`p+zuIQ9Y-kriIUKW%mb0MkW*_l0%EByVz&f{~Ctc_| z2yf&+K`x?DxUXc?dfDY}gyj2pz-n0J(>7hKg$Z!pYD#N@T$w}$IMk68?~wM%(3poF zX+{pO!!GzkJ-5Z6C%y&{uOv3hk9LxA&A0~M=vnB?0CU)0Y~nNyl^Q)30!dQbxUX*!70k_n-V{p zD_oR=9iKOj{AU;uX<#r3Pf_FL4y4LITyJGq%#;$i?$3U?Zx3hGY)~)NEkJ#JDQ;%w z0wGP)cnA3Fq;OGqQQxl0tqCOWjDZtO*gYn(VWH0hrz-%fK^ui8p^ z4o`b1a>_X>TlT1w6sg~@>ETShFlU79vTcLB5pYF7i{Gs5PP*eQMwK1aO8Bu$vYxob zn%JZJv^w@eLQM;nBP7;-c8ffpzR=?r$df-0MIqn;p%CCiq-$-VrHdh{wIUI>VkInA zJ~3Njkzflz@qf4{80zZ~sWR4W`Vj?`D)ul_yV_E80aiY)QWYIeL(;lBu`e8Z~RF^j@rpJNM3oj=)&$NhZr274aYM-HtUa%b8u_gmD&wM+Z7r$ zYD{F!o3EiokCqq&^xZN z($zq`paI*6>rxP|N~s+g^u?jgEKZo2mF}%4xR)xKWU|$1*~oQHWBD}j5A1+@12K(t z5#OXK(6ce5xZ!8SEFMp#1OZ57T^~p#&dK#D{sAl?tzA2ysKa;KOM3H>-HG4pax$S& zLEst+5tm_d2H;Pl`AVeCj}NyNv*pBqJe-$jZpyss2M#$}ti{T2>%|^>PW6^uIC(t+ z`(PRdWa3T{#Mi5RhdNrg6yM)F=F}cdT3B-P0`b-h)qw#g_=bWkp^>(u@IX3ucP&UW zvt+j{aBWvdON-ryj=1dx2Ag#ZXQY1w6E_a~mgwvIiS!{Rfx}u!@HQ&GY9<&th+-|Jo~CK)qRGZ6z~Pe-_xL!S zbs5i{6X&SLrcE8d+|QzU)9-DsEKQrd>o^xPtba`NH8*}yZTsuUE}C2-Li)M}HF!iL z-oF%B89va|ho#DLBG>((X6F3d8d#R92yB5%6c}9m1#{+QR(&4@eAv-)+iP3~-F7+7 z#jiCMbKta1`duGN0|8>Uw$R{|ubdO!>b3<-?{cV!FwVjo4c?Z2;o4$1u2LbyBmGQ==kc3Esl4>qsZ2%?-X+`6aXIqsICPqd1{g7+ zp`#l95YTMJ0HZMM`$$fu-^}%rQ@-l37oEzHIo3}H%BtYP5BT(tqP?yK@df%jnv*R3 zNS|ZX88Ta_ABDb5!sk0~^zTGrKKONW{2Myp^?fQx*_)-Ka$8B5 z!=|6v#(Ji7N~W_iRdbZeWez#~^qEqfqJT#L0&K4Un3fEs_5DV3BakzAhYLT|JhpyT z`>LGiwxkgKT)J(SgHU(TFJPAGVs99SJrnBOIcsh~tzRr#LYbWKT)5w* zW=^2|7f}1J5SQxD2j*J`YJETr{*0)P!EeCoG_>tUOw0Z?NDr!jH%AS$zrlBUcW;`DV9hDYVH7 z(4;lQI{!dAATLCLlxh7kgg6iwq2evKKYe z4w2T&bx-B=jok~KSFQuHKwpY~BEalIu0 zE^!DrB#hxoyZ_v9g7yC>(QFXJ=wFx&KA*n>h$EnDQh8qN2Fa)MJk?~s*V_KzL;tC! zrM=tS(JlSVN508|IHd9PdE$NnQvAtE0O~5hx2HonY-asD&NjaQt>?lcrS)o8a~z(b zI*1HSp9%sOwa0Vng4KMuy_H)Sip_Q&3y>49q!TFP^CvxoBTWB}R2YA>2uJEK42%JD zbw%8VIb3yxN(D}cn2668Vx1fUKJgQ}QfE^XM#m0Nn(rCUkzubKO6A;BIBRSyP|3UT z#-)qriAomy&j^bGg}Q$_W_h9_=K*>vZg^BrT56~7sXj~+9gh3~}Hh4vD`g2*H`=}js=f*>0f?xubIo%F*oevkZP+OjAobJb& zG0ci-WpH1e2_g6J&x~v@P|#Ah$v8Ha7>#@^ag;rMP>fA^a_ko{3|F8Gk7wGO1bL=d zYY8M)2{K4`YBFMHMdFQqS10^!C}QzaBPDB;I($9G7XZPm93U%c{9ZWk?k^H4)v8oE zX5`z#U4~mj%WZ$u{!@$x4P9>{Y3x)A3>)Z#58d}Uom4T`66;KPc5>xRZp286#3rTT zB=R|h%1Jh{_NWyasiPRnZj4(o{i{BMAOy(KFmm_x^{u3Ux+TAflPDnZojlwg83V9f z_BI@f;e|=>3axO2jg$*viF-4+2&UWkF{0uKsrZNi z!u3^CrM@>t@oRd2mur-~=GeIabVm{BK%_k|EXI{M^Xp}Ta<+oNSbaIe9mQp8)DahO z!dB#N$P#~zL&ttZOKo^`R1PusJ!!a%Uw$ftzx7>7B3`(!n&4*}QuwHR$6(!&F9Hr1 zn~VXbN~MxJh&|I4(P|(cR}|N{zHV}UwEX(X6jp1$91RG&hS#SX_1@PkC5Qm@~T}y@P3_(wfj>D=C)}RDo)YPXq}PmIZb5F zBR6u-{0)I-(>n<3wVAZ>sKvZkT^BsM@3|fM_YmbqZ7RM%{gc>on~rSphFE#&vFn4m z$Ah)zv~9pfdUCr?6~RvTXO%{q)8OoKc>`FscU>PS(f1-RB!BJNlgZDA#`V49*?6HR z0P5vzBX+(v3vA{eqK^5aA;l6vb)1C|6H@>(#CZ2pjT%527A@I;gY^9tyD||9 z(%-9ItSyur{@9%OEkgqH)%I(F@oj3_ny5h74M-DZ@hQaZW4>)cy>nP zs8yKWbi(Z~HsQ|bd(i_(f~Zga3m~-KEVYU=kdSnk@`IbBLa5=;rvwz&3~aL`!k^ch zTEJ?S7D33|-jwsr_giuiA6>l9k>?I<9>>l%S)+j#ZsPD8t0j)im&D_o0=wamDbk>O z`K7F|U~-#DlLkv@w(4NwbZHL}D-^092VB49#|J`336a1hZO8;BNTcg1*Q~MOiG1Ri z8d1qAE4YRV1mTc{-F!;)fgde?Z?65bqz_ZCcs5Rgf2Wkn0FbaQLRNS%FN6I&+P%s6 zS1Bru(pQJAw{{DjdnNgr6(k9CN~w@l#O@7HO!%HzqMv!)P>}ICCZ{=HG|N`?r!?T( z)qg7xs;F%N;>rHGo=f$ZOwU;V1pMp+JUNdjc`|R4hf5 z+LikP)CR45ogI`D6-eBoD+v={sNE11g;aW=G~+36F#BTXoTnWJkL-0F5k&Mqq|mlrYs)E z4Q`COvnQ9tB&2g=;`3Q^&qSi6NhsHKhhk7q+b-c#)%NFKFD*3THTfZ^k}mg4DZb6$ z$>CYM5}QowmG`PLL;i*$CZzszWwzfpSf!$IZlbK$EA)>6^=HQiB9+rV7%!PDw3H;6 zVoW9+OGzf)8_(PN>P)G}xIGg1~PFPxRuVf5XV^PhJHzPb>X|Bg>+oIun_zO)? z?{1>)){@wqFEt-dJHqctvjPszoyS1DB!j7E#q0PrWt#}d&NEdJ@bK_>oOZ8nE)Vvc zg5*;M;}dvLlgzbMUAw|57BYnbWCu*+q-6BORg?ZC2#PK6V@cDZy)T?)jBgQi6 zll*|UN8o0g^bSZAo~}x+o&D6WEA887i1Pt^WP6r!BDQDoAJxxa&TyXgM>@>t?GTSX zwTrxP67_$ja&n?221t6qepyY~{qSs5G~x9h4OQ$+<0Rykfd8Xl7Td}tmY{7#io&Nk>F-$G9p zs?h=oL;bcY@p}j${P_Z_6wFiQetnoIInMQa!->h4-J@1a9IB4%rhz)k*yk9x97!2pfW^lz*KjseF5Ar+>MMVG=p69;gHpkF>E z25qgF84ptaw+!xA|KffC(5l0WCf*;OSKq~NG#Hpx#XHu+p zFbaLMwFi<(FAMu?Mgg^BmTNb^?&$kOIR@BY(gfc_9DidprlxuK7gVXVLYCt$yE0HO9=Ohjiq|GXQ@78I)xc zM}@Ol&-daOm=dEvoxC@t(e+?nsMu+z4A3atNybYmt0x z8zbqwuybk{fJsG&ek7uQ!Oic>iFa)AUyCuZKm6<0UEl6cqMJDd3O89>o{Td2ss$Lr z!ro0a#F=z1tG_NM6a>2f)#bv~5Y|$>GRkkvo{vt&7xFX+VwoS_xsCY8e3s#FcF{`@ zh4IN>SggtpbaPO*Omx^`5|s{BG!>3cDsJ&u2Jo%39S5HxlACI44TMx~CyFO2C)EZ`&S4t_~O94zbU;KUI9dvnPQRh{Wr5D4HSQQ|-zNI4C9y)eyzP zvBrJ`ALs;hX&pu|w&v?O^o9+;nWF`m=I1(`7kz!w7iJDIprALPH5yA(UJm&P)<3JC zkZgObsV1NUf3XAfO+cyYZ1y-C%@RWlC9JetPw-R3F{o4(MI%2(a^_L+ds_@yjR zk^vl5~4JlUa&@zuW zAf=iE9&K)R{pxQYj3aNc#RoWzylYvWj`nuyOu9lXwp|WjK9#^bqW2s7 zg{?`8;$Jhah-dtc9e3*On*RwCj}O?L*Ks(XtS)@^6n=Z2%=l^U1Nho>o!=F1ZjJ{= z6B^>eHN)ry!Ahrxf>kBmKS>Q#jsd5HPd-T&4>0m1B_$;^elg*-?x>3s3cy@EEcHpO5ki#^O&I;kps(YIw`2KGNA$@c@ z+&k3uu_O>zZ9bx;!!+ueC+_~L7T`sm^}*<_v@I-_O~l{L(9Wi;AXY*1d>jIOxU0P_ ziy!Ckh>9lVc}>R(^JBF@FiN5oLb;ep-7aK9U}|I)TGgNxe7L7WvvhsAWcp4|rK4b2 z&0gRJKItm4WEKzm-PS{qy3rreh5{{t5o$}G!o)HbP^UT;-IkHLBkKWn=SlGvxcy}Q zb@3qE4G&ag&M=?Dn5US~EucfxY|`d&q>6-L)ILteycR6YGwQM|eG-a=kCs}7#A>cp zrk8Wt(hxRAqjJoWs}pDo3r35ABx^D!l;o)u;$`NnBt0)wwWg$b0SrV!f|T%*Mo&2Y zBS(<@Y8L_+`MQJXr&XN_E=4BkK6$mYea9Scc9nM<0_>WFIhP`bh*YLZu-`A0a$`ll zOu$D!6g6p^+L{gu?#0zD#?uyJ^Y|DuQidCGGx|!=6fyq@;I>m(qx^TYd?t-8PWU8& z3+JS}1cv5gvNv5aKROAhD+fF_JL5S?fd04N7|!XaHwB+hDrbVc2%Idc7A+-0N^y2X zM1%;ieGPsz=dw2@aO+J6P`JHVxBzTZV`T<{{O{;-S^=%aqC5bWSLYqe`%gO2Y|Lg4 zC2>U-n)6?^NLY8vNDqEN;~C?8Q`Vy9j9EUE>^?bb*ENl5JmbkMRt_Bx!7N-_cxSld zSW@-ISur_EP!Fn;%6egu6WsRYtQ8?Q>>8z7Ytu)!)mP=dsG|y67_61v+VSIeC6%pu z?~v)F{n^wH{5F)4B0n*g^DW`T)_84}d-F!B;2P}|6x;25PabG5c4+#_*Kv!8(le?`&Gc2yvU17U9FgzZ++1kV=62N zZ){F4Y>WW6F5sW(c_P_>2usS$j84Go+F5NmVQ3KhYfR#`WsbaSx4`XwRT7U&Veovp zkx0w*Fu}~L2EgvIEoS)gHzv9s?%k;LCby$$)|g9Y10R@b~0jet!x3~G?iL^^(LPWG2Ik z_8?a-0%0IWCULaE$-FnYsr9LWT-@J$4e{Aej6fi5iU^;#>)M~YcQX`lwBLGf6;%U9 zP}!8Xe-A?u>f_Xl5~)5*eDY?0l3{cpC*&-qB>;eLM$au>AXIaDA`BCnyXAW#v9+t1 z!DZlob=X9-UfcpcY28X528Bj)6l6m874U;p0b*hyg>UNlir@ym*EzN0edl{iEnfWS zJL-T!xX{rl{EQtv!ayH+xCHq!+*Ha{a%H~(>U>R5sd&^&6q{`EC}AKYU)*CV@coqh z|JC%-@`m#V$~dHF85Lk^-A~?xLYS*>e|;!xdq7AI=J)l-Doy(}=d9JuBu$~7UAxW%>CR!p=&qm*9Dw`$5 z9N7mY;D&)npjhVGe#7G+_rHiSR2iQfB~4j79rueuhJXbCYOSV=!+h^=EZuTR#8C(n znt|df2@lUaVqdbxmmCHsfkE;=PLtjMsE?CLA{}(m1DH^C3yz4b_m?_I1Uy(ko)!Uy zlv0s8Jz$oE-55yhseil1hrZ>ib>r*eAKq%9_kTHEAO(4k*!oWbB0TqR)pE5rj(-A9 zjxUlQNG#^73tH2lZjXZ}$^-(Xl%B7KkT8ft%7htjVCDd@JLtzPXe|+Za zaQ>p!(QK(X;*bNSNjYCCD>a~Qe33-+4`u^e&kK2I|IAb(45}9i>JR~kf--;Sj9 zWMw0JLavg9C@3gA^#qBD!KI`e#n}RMpin+T->;ezaeOPWqWhOt_O+|e);(St;#%2!8&M4W`m8}O z9{M+6CyIsFRm3L`->7@qi%cz*i+JProAG5tydw6sJ}ZqKv!oY&tXr@?%~v6yh(y2& zm=L5KX=giSxz6=LM{x`sc{~8y2;bek zrMDkW<90=_clZfm*LLsPe6{4oa{z#IF(6ke0rDLZfOY%&`ffadyJ?(L{!bTI9TnyJ zbp=sCqy~miK)R8XF6oj~N?Ha)!lyry^Qr{Wx6@Tmd zkF{QA=6UNpd+)RN*}HuJiWt>TXL&g`mDb2vN_W&S?{sCi-ogz-0!T^eEKVk+#7ntT z^Egs&9=J_ENwQgDj~Di$d>khh&1X;YOwx2VHeu;Tsq&V3(JQ}(i}6yiEk7(cc{c3L zc1)APE&eS9e1H3!tYv|I38vKH=0o*^#muN;w>3j!DWO9m z)YZHn{7QvK7YZ`T#oU)-&PjK(_zM=D6(V&0#J?f)L_@8UgCdD6%F*P2jLe;2-;uOe z__3y~XvA&9Hyn4E@bYdT^7@OO9rPZS^Xa1UbZqgd($C~tQFBuK`|9C%V1A!k-uK9# zy%2ORw3=#4oH(%(+T382o6KBDbD(~L{X zsZRvd{nx#|EZj-I6Q4GH_FJ#BW-~a$_!6|h8H;=kHm}DNyqL0&F81=t^9qDOP9xF& zr%S>8s=d8CuH%m`v_)MR)~VZ(gs>AH#8igDvsm4Bh>+Q22f26_o?VM7dR^Zt>2@b@ zRLho&W7&hE+YH4zuZ(|u%SLxoBDGC4i_JxQxgoxht0JH;I3Pf0IDv_1Gs?nWguPjx!&8|?#;?*8O!?*A1Y89zgRps z>b42a6HtZj?Rjt-mg8_4&LQR*{IK5Z=Dsk#m@9SW42e5>7Jy*|VR=L;XX^v|ch1M0 zFISsvC!N&Grnsp8i4yU9Yl}s7B=t=ISa2SxY)*Qi(%^cEP}U%OW^D7(xN`FOAl~0t zCBTfHC_~M4UtG{)46xKsnG4(WOP zOIdGxOFw1+r17Foop_$m5C9gZne-5vK9y;J39sYC^GL|abCL2|hnD=Ui@ljz1^T>p z#-!(-HJ!z5>tPF4H<)2$&DPf-SCk)F-~ZD>4NR0Sy~TMc0Lgi8xi{aWldi#gcU`{X z-XIa#Ldid&nTk=%*X$SAvkDvYuGp@sL116&QOuns{Pl$P9m;4sP&+#PISA_ zJl2-!TyHX6p`Ab(YSx~qZS56)n-Ig5<2i!jKPV9}&Fs)q?r~o#DQCdWi+z{FHTA_v z-~pwR3nlSOnZFnoi#MS2UZ&hf%+g|U2h6ydMo-;Uwm;a%KMhBJKp{*?WSG$(j-Bz+ zUu8c%=7*EzQ90izmEzocAx@OsEb-4m8Kd2C=LjpO_fSPU&lV^I*j+^)rT&e*&*@^C zr;{0}!4QDkX@m#zf7|o_5Qi!YXtF=IT^W>HS5J}HUV?PE`E{2dzb{?) zY#}zL68-b9CKx6C{OMJ*R#=}I4N~m4(Fgr|)7+TmbdCESz{y{@fnxcHA^WdlNgM!J zWMZf+ph8P#DgEN<`rr>MOvjI8u@UOEU7mUoXe$Dmy@RYl03t9Q%&*TmlV z^Q1@T;)H4odGFjes&c^a-dSz>vDDw2rA+dUr+qtK^qr>FpLQZRRstK*v^kPmECaB+ zR~q}IXN zIagPO1;iXG$ML6!$#k5~9kr={ejVgXq=0XaL0p^)WWINXT50ou^#l6)L%zv8o{BkJ zG3ARLH%9B}sh-*;en-J%Zk`gON99}aE=O)_V#CCb-Bby-lh}plq9+bk9H)1lG5QVG z-MmiSI?_Mz6Am5Ifu`;!T=qZQw_O(#o>?S!@ToO9+TRZcI0K<|T6rn>2rga{zyV#U z1?)J<>1ui?$c=7|*%qaPP=_9M-~g!X;tIA-1_#*!Z_P~UgMFuQ=xs!<0BjS|!|SNx zAfv?Jfz)RsNOj4j%xa0~`3$oG*ffoBA(OucZnct_;E{1Q0%D&Fy6&6My)gn?L-O~B zTdgVXa-Vh)*o*Gtj@Z6?U$GgPm~2SFu`F9;QvbQaRu8s1R*EenUS{{!)$n2KG@peZ zzS$=}^%=a(SlAdMeL-})BuZ*(0s~)zTcLgT7@8B5umKA&t0ylGY;|=2mmARhwm<

9xW|t)Z;!IEQ_wnc9R!BUNA$H1|w^b_$?2Q&Y?xkv`hl zRh5jpOsD9}qcHBNC}=-fQ2m%nq%Tp*uVEo4miP|9KLs$lK4H_zp`ceBfeX(?!7I`_ z_bJhM3qfZ$R?Z8}Oo=wJoCX15OMXNhRx^@J_6!ji{p*iqnNKK{qE9rTL%K5HtcNPl z%`dD4J=FLZEERcvnAKVE3i3EUfKPriual}-_L zrLuvcJ*}-zF2gd&92172Q=94eF*aR<6-C&w9QI)U`}L!55n!v8_A%!t$E~Rn+h_+M z^B5XVDY7nvrh6^FQ{*ZSDeSe;1V|*7fIUdUA*7v%k zJ1O#Lngz(RbZSXK!0+fyWybw>?n03&b`nVPML;M+%}+s5aYX~g#G^;}8eWuz>u@?l zkGM?g^ozbNjh5w_biOL!Us%45?0Fn4b{09AkvszOEfZ1KhMuk`Jajl9%>Pl(A6e3xxhls4i;H<)EnTr=6LwjQuJ+PwF)ne~r6#srXnKfE ztriKH)Ds;yMl1uy{JQ4X`>SO=^n84XdtCH$5gz~qUb2i$4w?NVMn9xd@dE=a`uLE_ z#;BmtIw9zos)3ZQFjmJ@DPYOdE6xu6JWFRih?&!x(pqjmGt8uToG6^*g7s=3`|PvZ z_A7(=AwURZ-XQy@>RO|&M6DFPGjZ3m^Zk6`MLyqmt{Ys>2jl(0#M55LTAg6u+;qvP zBzh|xC3jWN$gymb zcrWvKCfdNe3lD*Lb!d!Ngd7nqU$R`$U)u;%0jyrJ(N3tMuJJUS9D7r{-?D=Jz9yB^ zEa6;vQ-iabS(Qb4a6p_i^D}}490?*Sl$lTtSW{yoi+Kfe{Cct#Wk;8zA+$r<48!&mu22( z913|c!|Wa@Mh&jcyK@1gmMJZOJ2dmOnGz=4ZYeZub&x(%qdr#__vvCQ63>}Z&!{<^ zzAnhs!=tKN<#Q`MV%Ckz8@QIdZlk3rVkB~2JmH=_JX=pa~kocNyZ!#==1~BME3`5VV z7tM<~7>U%d=86|!Go?BK0vJiCn*8C51ai*9dgQl2*UpUIj&V6#0A5xxM?ZTu6U!#{ z#5ni|s(W6}F_t>6Fi^tOh;`iVN%QzUHR)Dbd z0eDu41I;kggX?yab=4nK8=>hzx9enyZZwvF>yAs6^TCXPG(iEfhz9he4}i9)*V+Rr z(AfcOrHIZt(28Y6xQF-Ce1EZ-JlK+-(0xiTU*0qO?)VoTcim>-RhHnn%_*?hFwoOu zl0}>Ym&1T-%WKw2CCS50*%a=Oi=8%yZrM5n&Pb`kJ{A#rnZvW40ab`O55!W;T-fT@ z4#h%_77e{=LNt`z)vq=-C2Q#sn13dNPSrxVu$^~Er) z`xSv!E12dnpYFCZM462gn;SlgB~DPuAnjN>cp+%_9)v`+xyFY3Jf9s04T0QslHm2j z{q3=&L-6EQ>|T7z<|*Oz<8w#a-j~Dm+6|X4(6sFLnfZ75*a19#0`GsDyrpr_Ix)O{ z{X8(!Ba3hXiBgpT5MZtwSmUx32<31^+*Pz;=Lvhb2^4hNcpv~Jr zWC>Ep^i(YJVrWAK!&ii~sG?{j!VSu-mV84o?@H;9t&b8zIa3t2v@& zaO&8i)MMX*O-q?~O+#lc>^8F_K68TpZdRc_b4$t;==bTJ-+w>HmO^l?M{gveo#s?n za&%?bTE23dF&jns1j*XTq=FO{mR=a?|T9D)z2*|{O}3e`{hZ906?7&PcR0ro`?bm zgSJLgxCOeW*F#dRhX;1VLHTC8nEE=k&SU8J-Tm_R%_ox1w0b)&o;8()K}-JMZ1BN# zm=O2i`cpn291qmd7Mw(I699%SF>AtvNkX@ZZrARN+5TJzK_^b_)uWd*!XlW8z_dZR zBU=!dvG=h?a$cY2+y&Cl{Z)MP?3XVwDv2eXE1?P}>;@)fQuHO}29i&Lb4WPpe4#;E zPO1r65}&a*G;F>mnEHNHW86kFoyJ@!4*R4m!JvR=vCv+K!y4tf=f-|@sdRgQrpr+| zCQr>Ein%|{=~ME3rK*GV+MnxvXz`?*)FKHD9VVw~SLRmjMp0u@D4)sNjfRL+3GW#& zQ7Kwj&|6;89U2SMX6_iP-<@1pH2u{On*8b&D_97yAy-vd_ysIP=;;pj3(;v_GbUDf6ZZn2n`*f?gsNOlQ z#|2#tU@{wICdmyt{uW~lDa7=1QK|P2q=oH z^s3c=KRK-krS=f##f?*cdbG3~F(@I%mTU=sKY6I+uaa`byo~LX?V^_kMLL3W{!{~q p_P>G)oQdJS;QvS{qw>=y7;EOZju~FKo9MBs%JS-Rg)(M={|EoaOZ5N% diff --git a/docs/source/user_guide/model_training/model_explainability/index.rst b/docs/source/user_guide/model_training/model_explainability/index.rst deleted file mode 100644 index 4653a12b8..000000000 --- a/docs/source/user_guide/model_training/model_explainability/index.rst +++ /dev/null @@ -1,63 +0,0 @@ -.. _mlx-8: - -#################### -Model Explainability -#################### - -**Prerequisites** - -* Currently Oracle AutoML and MLX libraries are available only via Data Science Conda Packs. -* See `here `_ for supported conda packs -* To install conda packs locally, see :doc:`Working with Conda Packs<../../cli/opctl/localdev/condapack>` - - -Machine learning and deep learning are becoming ubiquitous due to: - -* The ability to solve complex problems in a variety of different domains. -* The growth in the performance and efficiency of modern computing resources. -* The widespread availability of large amounts of data. - -However, as the size and complexity of problems continue to increase, so does the complexity of the machine learning algorithms applied to these problems. The inherent and growing complexity of machine learning algorithms limits the ability to understand what the model has learned or why a given prediction was made, acting as a barrier to the adoption of machine learning. Additionally, there may be legal or regulatory requirements to be able to explain the outcome of a prediction from a machine learning model, resulting in the use of biased models at the cost of accuracy. - -Machine learning explainability (MLX) is the process of explaining and interpreting machine learning and deep learning models. - -MLX can help machine learning developers to: - -* Better understand and interpret the model's behavior. - - - Which features does the model consider important? - - What is the relationship between the feature values and the target predictions? - -* Debug and improve the quality of the model. - - - Did the model learn something unexpected? - - Does the model generalize or did it learn something specific to the training dataset? - -* Increase trust in the model and confidence in deploying the model. - -MLX can help users of machine learning algorithms to: - -* Understand why the model made a certain prediction. - - - Why was my bank loan denied? - -Some useful terms for MLX: - -* **Explainability**: The ability to explain the reasons behind a machine learning model’s prediction. -* **Global Explanations**: Understand the general behavior of a machine learning model as a whole. -* **Interpretability**: The level at which a human can understand the explanation. -* **Local Explanations**: Understand why the machine learning model made a specific prediction. -* **Model-Agnostic Explanations**: Explanations treat the machine learning model and feature pre-processing as a black box, instead of using properties from the model to guide the explanation. -* **WhatIf Explanations**: Understand how changes in the value of features affects the model's prediction. - -The ADS explanation module provides interpretable, model-agnostic, local and global explanations. - -.. toctree:: - :hidden: - :maxdepth: 1 - - accumulated_local_effects - feature_dependence - feature_importance - lime - whatif diff --git a/docs/source/user_guide/model_training/model_explainability/lime.rst b/docs/source/user_guide/model_training/model_explainability/lime.rst deleted file mode 100644 index b11218f0b..000000000 --- a/docs/source/user_guide/model_training/model_explainability/lime.rst +++ /dev/null @@ -1,166 +0,0 @@ -Enhanced LIME -************* - -Overview -======== - -Local explanations target specific predictions from the machine learning model. The goal is to understand why the model made a particular prediction. - -There are multiple different forms of local explanations, such as feature attribution explanations and exemplar-based explanations. ADS -supports local feature attribution explanations. They help to identify the most important features leading towards a given prediction. - -While a given feature might be important for the model in general, the values in a particular sample may cause certain features to have -a larger impact on the model's predictions than others. Furthermore, given the feature values in a specific sample, local explanations can also estimate the contribution that each feature had towards or against a target prediction. For example, does the current value of the feature have a positive or negative effect on the prediction probability of the target class? Does the feature increase or decrease the predicted regression target value? - -The Enhanced Local Interpretable Model-Agnostic Explanation (LIME) is a model-agnostic local explanation method. It provides insights into why a machine learning model made a specific prediction. - -Description -=========== - -ADS provides an enhanced version of Local Interpretable Model-Agnostic Explanations (LIME), which improves on the explanation quality, performance, and interpretability. The key idea behind LIME is that while the global behavior of a machine learning model might be very complex, the local behavior may be much simpler. In ADS, local refers to the behavior of the model on similar samples. LIME tries to approximate the local behavior of the complex machine learning model through the use of a simple, inherently interpretable surrogate model. For example, a linear model. If the surrogate model is able to accurately approximate the complex model's local behavior, ADS -can generate a local explanation of the complex model from the interpretable surrogate model. For example, when data is centered and scaled the magnitude and sign of the coefficients in a linear model indicate the contribution each feature has towards the target variable. - -The predictions from complex machine learning models are challenging to explain and are generally considered as a black box. As such, ADS refers to the model to be explained as the black box model. ADS supports classification and regression models on tabular or text-based datasets (containing a single text-based feature). - -The main steps in computing a local explanation for tabular datasets are: - -* Start with a trained machine learning model (the black box model). -* Select a specific sample to explain (x\ :sub:`exp`\). -* Randomly generate a large sample space in a nearby neighborhood around x\ :sub:`exp`\. The sample space is generated based on the feature distributions from the training dataset. Each sample is then weighted based on its distance from x\ :sub:`exp` \ to give higher weight to samples that are closer to x\ :sub:`exp`\. ADS provides several enhancements, over the standard algorithm, to improve the quality and locality of the sample generation and weighting methods. -* Using the black box model, generate a prediction for each of the randomly generated local samples. For classification tasks, compute the prediction probabilities using ``predict_proba()``. For regression tasks, compute the predicted regression value using ``predict()``. -* Fit a linear surrogate model on the predicted values from the black box model on the local generated sample space. If the surrogate model is able to accurately match the output of the black box model (referred to as surrogate model fidelity), the surrogate model can act as a proxy for explaining the local behavior of the black box model. For classification tasks, the surrogate model is a linear regression model fit on the prediction probabilities of the black box model. Consequently, for multinomial classification tasks, a separate surrogate model is required to explain each class. In that case, the explanation indicates if a feature contributes towards the specified class or against the specified class (for example, towards one of the other N classes). For regression tasks, the surrogate model is a linear regression model fit on the predicted regression values from the black box model. -* There are two available techniques for fitting the surrogate model: - - - Use the features directly: - - The raw (normalized) feature values are used to fit the linear surrogate model directly. This results in a normal linear model. A positive coefficient indicates that when the feature value increases, the target variable increases. A negative coefficient indicates that when a feature value increases, the target variable decreases. Categorical features are converted to binary values. A value of 1 indicates that the feature in the generated sample has the same value as x\ :sub:`exp` \ and a value of 0 indicates that the feature in the generated sample has a different value than x\ :sub:`exp`\. - - - Translate the features to an interpretable feature space: - - Continuous features are converted to categorical features by discretizing the feature values (for example, quartiles, deciles, and entropy-based). Then, all features are converted to binary values. A value of 1 indicates that the feature in the generated sample has the same value as x\ :sub:`exp` \ (for example, the same categorical value or the continuous feature falls in the same bin) and a value of 0 indicates that the feature in the generated sample has a different value than x\ :sub:`exp` \ (for example, a different categorical value or the continuous feature falls in a different bin). The interpretation of the linear model here is a bit different from the regression model. A positive coefficient indicates that when a feature has the same value as x\ :sub:`exp` \ (for example, the same category), the feature increased the prediction output from the black box model. Similarly, negative coefficients indicate that when a feature has the same value as x\ :sub:`exp`\, the feature decreased the prediction output from the black box model. This does not say what happens when the feature is in a different category than x\ :sub:`exp`. It only provides information when the specific feature has the same value as x\ :sub:`exp` \ and if it positively or negatively impacts the - black box model's prediction. - -* The explanation is an ordered list of feature importances extracted from the coefficients of the linear surrogate model. The magnitude of the coefficients indicates the relative feature importance and the sign indicates whether the feature has a positive or negative impact on the black box model's prediction. -* The algorithm is similar to text-based datasets. The main difference is in the random local sample space generation. Instead of randomly generating samples based on the feature distributions, a large number of local samples are generated by randomly removing subsets of words from the text sample. Each of the randomly generated samples is converted to a binary vector-based on the existence of a word. For example, the original sample to explain, x\ :sub:`exp`\, contains 1s for every word. If the randomly generated sample has the same word as x\ :sub:`exp`\ , it is a value of 1. If the word has been removed in the randomly generated sample, it is a value of 0. In this case, the linear surrogate model evaluates the behavior of the model when the word is there or not. - -Additionally, an upper bound can be set on the number of features to include in the explanation (for example, explain the top-N most important features). If the specified number of features is less than the total number of features, a simple feature selection method is applied prior to fitting the linear surrogate model. The black box model is still evaluated on all features, but the surrogate model is only fits on the subset of features. - -Interpretation -============== - -ADS provides multiple enhancements to the local visualizations from LIME. The explanation is presented as a grid containing information about the black box model, information about the local explainer, and the actual local explanation. Each row in the grid is described as: - -* Model (first row) - - - The left column presents information about the black box model and the model's prediction. For example, the type of the black box model, the true label/value for the selected sample to explain, the predicted value from the black box model, and the prediction probabilities (classification) or prediction values (regression). - - The right column displays the sample to explain. For tabular datasets, this is a table showing the feature names and corresponding values for this sample. For text datasets, this shows the text sample to explain. - -* Explainer (second row) - - - The left column presents the explainer configuration parameters, such as the underlying local explanation algorithm used (for example, LIME), the type of surrogate model (for example, linear), the number of randomly generated local samples (for example, 5000) to train the local surrogate model (:math:`N_t`), whether continuous features were discretized or not. - - The right column provides a legend describing how to interpret the model explanations. - -* Explanations (remaining rows) - - - For classification tasks, a local explanation can be generated for each of the target labels (since the surrogate model is fit to the prediction probabilities from the black box model). For binary classification, the explanation for one class will mirror the other. For multinomial classification, the explanations describe how each feature contributes towards or against the specified target class. If the feature contributes against the specified target class (for example, decreases the prediction probability), it increases the prediction probability of one or more other target classes. The explanation for each target class is shown as a - separate row in the Explanation section. - - The Feature Importances section presents the actual local explanation. The explanation is visualized as a horizontal bar chart of feature importance values, ordered by relative feature importance. Features with larger bars (top) are more important than features with shorter bars (bottom). Positive feature importance values (to the right) indicate that the feature increases the prediction target value. Negative feature importance values (to the left) indicate that the feature decreases the prediction target value. Depending on whether continuous features are discretized or not changes the interpretation of this value (for example, whether the specific feature value indicates a positive/negative attribution, or whether an increase/decrease in the feature value indicates a positive/negative attribution). If the features are discretized, the corresponding range is included. The feature importance value is shown beside each bar. This can either be the raw coefficient taken from the linear surrogate model or can be normalized such that all importance values sum to one. For text datasets, the explanation is visualized as a word cloud. Important words that have a large positive contribution towards a given prediction (for example, increase the prediction value) are shown larger than unimportant words that have a less positive impact on the target prediction. - -* The Explanation Quality section presents information about the quality of the explanation. It is further broken down into two sections: - - - Sample Distance Distributions - - This section presents the sample distributions used to train (:math:`N_t`) and evaluate (:math:`N_{v_\#}`) the local surrogate model based on the distances (Euclidean) of the generated samples from the sample to explain. This highlights the locality of generated sample spaces where the surrogate model (explainer) is trained and evaluated. The distance distribution from the sample to explain for the actual dataset used to train the black box model, Train, is also shown. This highlights the locality of :math:`N_t` relative to the entire train dataset. For the generated evaluation sample spaces (:math:`N_{v_\#}`), the sample space is generated based on a percentile value of the distances in Train relative to the sample to explain. For example, :math:`N_{v_4}` is generated with the maximum distance being limited to the 4\ :sup:`th` percentile of the distances in train from the sample to explain. - - Evaluation Metrics - - This section presents the fidelity of the surrogate model relative to the black box model on the randomly generated sample spaces used to fit and evaluate the surrogate model. In other words, this section evaluates how accurately the surrogate model approximates the local behavior of the complex black box model. Multiple different regression and classification metrics are supported. For classification tasks, ADS supports both regression and classification metrics. Regression metrics are computed on the raw prediction probabilities between the surrogate model and the black box model. For classification metrics, the prediction probabilities are converted to the corresponding target labels and are compared between the surrogate model and the black box model. Explanations for regression tasks only support regression metrics. Supported regression metrics: MSE, RMSE (default), R\ :sup:`2`, MAPE, SMAPE, Two-Sample Kolmogorov-Smirnov Test, Pearson Correlation (default), and Spearman Correlation. Supported classification metrics: :math:`F_1`, Accuracy, Recall, and ROC_AUC. - - - Performance - - Explanation time in seconds. - -Example -======== - -This example generates and visualizes local explanations on the `Titanic dataset `_. The model is constructed using the ADS ``OracleAutoMLProvider``. However, the ADS model explainers work with any model (classifier or regressor) that is wrapped in an ``ADSModel`` object. - -.. code-block:: python3 - - import logging - import requests - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.dataset.factory import DatasetFactory - from os import path - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct - # global and local explanation objects. The ADSExplainer takes - # as input the model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a local explanation object using - # the MLXLocalExplainer provider - from ads.explanations.mlx_local_explainer import MLXLocalExplainer - local_explainer = explainer.local_explanation( - provider=MLXLocalExplainer()) - - # A summary of the local explanation algorithm and how to interpret - # the output can be displayed with - local_explainer.summary() - - # Select a specific sample (instance/row) to generate a local - # explanation for - sample = 13 - - # Compute the local explanation on our sample from the test set - explanation = local_explainer.explain(test.X.iloc[sample:sample+1], - test.y.iloc[sample:sample+1]) - - # Visualize the explanation for the label True (Survived). See - # the "Interpretation" section above for more information - explanation.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_local.png - -.. code-block:: python3 - - # The raw explanaiton data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - explanation.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_local_diagnostics.png - -References -========== - -* `LIME `_ -* `Vanderbilt Biostatistics - titanic data `_ -* `Why Should I Trust You? Explaining the Predictions of Any Classifier `_ - diff --git a/docs/source/user_guide/model_training/model_explainability/whatif.rst b/docs/source/user_guide/model_training/model_explainability/whatif.rst deleted file mode 100644 index 25961dcf4..000000000 --- a/docs/source/user_guide/model_training/model_explainability/whatif.rst +++ /dev/null @@ -1,97 +0,0 @@ -WhatIf Explainer -**************** - -Description -=========== - -The WhatIf explainer tool helps to understand how changes in an observation affect a model's prediction. Use it to explore a model's behavior on a single observation or the entire dataset by asking "what if" questions. - -The WhatIf explainer has the following methods: - -* ``explore_predictions``: Explore the relationship between feature values and the model predictions. -* ``explore_sample``: Modify the values in an observation and see how the prediction changes. - -Example -======== - -In this example, a WhatIf explainer is created, and then the ``explore_predictions()``, and ``explore_sample()`` methods are demonstrated. A tree-based model is used to make predictions on the Boston housing dataset. - -.. code-block:: python3 - - from ads.common.model import ADSModel - from ads.dataset.dataset_browser import DatasetBrowser - from ads.dataset.label_encoder import DataFrameLabelEncoder - from ads.explanations.explainer import ADSExplainer - from ads.explanations.mlx_whatif_explainer import MLXWhatIfExplainer - from sklearn.ensemble import ExtraTreesRegressor - from sklearn.pipeline import make_pipeline - from sklearn.preprocessing import LabelEncoder - import logging - import warnings - - logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.ERROR) - warnings.filterwarnings('ignore') - - ds = DatasetBrowser.sklearn().open("boston").set_target("target") - train, test = ds.train_test_split(test_size=0.2) - - X_boston = train.X.copy() - y_boston = train.y.copy() - - le = DataFrameLabelEncoder() - X_boston = le.fit_transform(X_boston) - - # Model Training - ensemble_regressor = ExtraTreesRegressor(n_estimators=245, random_state=42) - ensemble_regressor.fit(X_boston, y_boston) - model = ADSModel.from_estimator(make_pipeline(le, ensemble_regressor), name="ExtraTreesRegressor") - - # Build a WhatIf Explainer - explainer = ADSExplainer(test, model, training_data=train) - whatif_explainer = explainer.whatif_explanation(provider=MLXWhatIfExplainer()) - -The Sample Explorer method, ``explore_sample()``, opens a GUI that has a single observation. The values of that sample can then be changed. By clicking **Run Inference**, the model computes the prediction with the updated feature values. The interface shows the original values and the values that have been changed. - -``example_sample()`` accepts the ``row_idx`` parameter that specifies the index of the observation that is to be evaluated. The default is zero (0). The ``features`` parameter lists the feature names that are shown in the interface. By default, it displays all features. For datasets with a large number of features, this can be cumbersome so the ``max_features`` parameter can be used to display only the first *n* features. - -The following command opens the Sample Explorer. Change the values then click **Run Inference** to see how the prediction changes. - -.. code-block:: python3 - - whatif_explainer.explore_sample() - -.. image:: figures/ads_mlx_boston_whatif_explore_sample.png - - -The Predictions Explorer method, ``explore_predictions()``, allows the exploration of model predictions across either the marginal distribution (1-feature) or the joint distribution (2-features). - -The method ``explore_predictions()`` has several optional parameters including: - -* ``discretization``: (str, optional) Discretization method applies the x-axis if the feature ``x`` is continuous. The valid options are 'quartile', 'decile', or 'percentile'. The default is None. -* ``label``: (str or int, optional) Target label or target class name to explore only for classification problems. The default is None. -* ``plot_type``: (str, optional) Type of plot. For classification problems the valid options are 'scatter', 'box', or 'bar'. For a regression problem, the valid options are 'scatter' or 'box'. The default is 'scatter'. -* ``x``: (str, optional) Feature column on x-axis. The default is None. -* ``y``: (str, optional) Feature column or model prediction column on the y-axis, by default it is the target. - -When only ``x`` is set, the chart shows the relationship between the features ``x`` and the target ``y``. - -.. code-block:: python3 - - whatif_explainer.explore_predictions(x='AGE') - -.. image:: figures/ads_mlx_boston_whatif_explore_predictions_1.png - -If features are specified for both ``x`` and ``y``, the plot uses color to indicate the value of the target. - -.. code-block:: python3 - - whatif_explainer.explore_predictions(x='AGE', y='CRIM') - -.. image:: figures/ads_mlx_boston_whatif_explore_predictions_2.png - -.. code-block:: python3 - - whatif_explainer.explore_predictions(x='RAD', plot_type='box', discretization='decile') - -.. image:: figures/ads_mlx_boston_whatif_explore_predictions_3.png - diff --git a/docs/source/user_guide/model_training/usage.rst b/docs/source/user_guide/model_training/usage.rst deleted file mode 100644 index a9c70f441..000000000 --- a/docs/source/user_guide/model_training/usage.rst +++ /dev/null @@ -1,960 +0,0 @@ -Using ``OracleAutoMLProvider`` -****************************** - -To demonstrate the ``OracleAutoMLProvider`` API, this example builds a classifier using the ``OracleAutoMLProvider`` tool for the public Census Income dataset. The dataset is a binary classification dataset and more details about the dataset are found at https://archive.ics.uci.edu/ml/datasets/Adult. Various options provided by the Oracle AutoML tool are explored allowing you to exercise control over the AutoML training process. The different models trained by Oracle AutoML are then evaluated. - -Setup -===== - -Load the necessary modules: - -.. code:: python3 - - import seaborn as sns - import pickle - import pandas as pd - import matplotlib.pyplot as plt - import logging - import gzip - - from ads.evaluations.evaluator import ADSEvaluator - from ads.dataset.factory import DatasetFactory - from ads.automl.provider import OracleAutoMLProvider - from ads.automl.driver import AutoML - - plt.rcParams['figure.figsize'] = [10, 7] - plt.rcParams['font.size'] = 15 - sns.set(color_codes=True) - sns.set(font_scale=1.5) - sns.set_palette("bright") - sns.set_style("whitegrid") - -Dataset -======= - -Start by reading in the dataset from UCI. The dataset is not properly formatted, the separators have spaces between them, and the test set has a corrupt row at the top. These options are specified to the Pandas CSV reader. The dataset has already been pre-split into training and test sets. The training set is used to create a machine learning model using Oracle AutoML, and the test set is used to evaluate the model’s performance on unseen data. - -.. code:: python3 - - column_names = [ - 'age', - 'workclass', - 'fnlwgt', - 'education', - 'education-num', - 'marital-status', - 'occupation', - 'relationship', - 'race', - 'sex', - 'capital-gain', - 'capital-loss', - 'hours-per-week', - 'native-country', - 'income', - ] - - df = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data', - names=column_names, sep=',\s*', na_values='?') - test_df = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test', - names=column_names, sep=',\s*', na_values='?', skiprows=1) - -Retrieve some of the values in the data: - -.. code:: python3 - - df.head() - -.. csv-table:: Adult - :header-rows: 1 - - **age**,**workclass**,**fnlwgt**,**education**,**education-num**,**marital-status**,**occupation**,**relationship**,**race**,**sex**,**capital-gain**,**capital-loss**,**hours-per-week**,**native-country**,**income_level** - 39,State-gov,77516,Bachelors,13,Never-married,Adm-clerical,Not-in-family,White,Male,2174,0,40,United-States,<=50K - 50,Self-emp-not-inc,83311,Bachelors,13,Married-civ-spouse,Exec-managerial,Husband,White,Male,0,0,13,United-States,<=50K - 38,Private,215646,HS-grad,9,Divorced,Handlers-cleaners,Not-in-family,White,Male,0,0,40,United-States,<=50K - 53,Private,234721,11th,7,Married-civ-spouse,Handlers-cleaners,Husband,Black,Male,0,0,40,United-States,<=50K - 28,Private,338409,Bachelors,13,Married-civ-spouse,Prof-specialty,Wife,Black,Female,0,0,40,Cuba,<=50K - 37,Private,284582,Masters,14,Married-civ-spouse,Exec-managerial,Wife,White,Female,0,0,40,United-States,<=50K - - -The Adult dataset contains a mix of numerical and string data, making it a challenging problem to train machine learning models on. - -.. code:: python3 - - pd.DataFrame({'Data type': df.dtypes}).T - -.. csv-table:: Adult Data Types - :header-rows: 1 - - **age**,**workclass**,**fnlwgt**,**education**,**education-num**,**marital-status**,**occupation**,**relationship**,**race**,**sex**,**capital-gain**,**capital-loss**,**hours-per-week**,**native-country**,**income_level** - int64,object,int64,object,int64,object,object,object,object,object,int64,int64,int64,object,object - - -The dataset is also missing many values, further adding to its complexity. The Oracle AutoML solution automatically handles missing -values by intelligently dropping features with too many missing values, and filling in the remaining missing values based on the feature type. - -.. code:: python3 - - pd.DataFrame({'% missing values': df.isnull().sum() * 100 / len(df)}).T - -.. csv-table:: Adult Data Types - :header-rows: 1 - - ,**age**,**workclass**,**fnlwgt**,**education**,**education-num**,**marital-status**,**occupation**,**relationship**,**race**,**sex**,**capital-gain**,**capital-loss**,**hours-per-week**,**native-country**,**income_level** - % missing values,0.0,5.638647,0.0,0.0,0.0,0.0,5.660146,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 - - -Visualize the distribution of the target variable in the training data. - -.. code:: python3 - - target_col = 'income' - sns.countplot(x="income", data=df) - -.. image:: figures/output_15_1.png - -The test set has a different set of labels from the training set. The test set labels have an extra period (.) at the end causing incorrect scoring. - -.. code:: python3 - - print(df[target_col].unique()) - print(test_df[target_col].unique()) - -.. parsed-literal:: - - ['<=50K' '>50K'] - ['<=50K.' '>50K.'] - -Remove the trailing period (.) from the test set labels. - -.. code:: python3 - - test_df[target_col] = test_df[target_col].str.rstrip('.') - print(test_df[target_col].unique()) - -.. parsed-literal:: - - ['<=50K' '>50K'] - -Convert the Pandas dataframes to ``ADSDataset`` to use with ADS APIs. - -.. code:: python3 - - train = DatasetFactory.open(df).set_target(target_col) - test = DatasetFactory.open(test_df).set_target(target_col) - -If the data is not already pre-split into train and test sets, you can split it with the ``train_test_split()`` or ``train_validation_test_split()`` method. This example of loading the data and splitting it into an 80%/20% train and test set. - -.. code:: python3 - - ds = DatasetFactory.open("path/data.csv").set_target('target') - train, test = ds.train_test_split(test_size=0.2) - -Splitting the data into train, validation, and test returns three data subsets. If you don't specify the test and validation sizes, the data is split 80%/10%/10%. This example assigns a 70%/15%/15% split: - -.. code:: python3 - - data_split = ds.train_validation_test_split( - test_size=0.15, - validation_size=0.15 - ) - train, validation, test = data_split - print(data_split) # print out shape of train, validation, test sets in split - -``OracleAutoMLProvider`` -======================== - -The Oracle AutoML solution automatically provides a tuned machine learning pipeline that best models the given a training dataset and prediction task at hand. The dataset can be any supervised prediction task. For example, classification or regression where the target can be a simple binary or a multinomial value or a real valued column in a table, respectively. - -The Oracle AutoML solution is selected using the ``OracleAutoMLProvider`` object that delegates model training to the AutoML package. - -AutoML consists four main modules: - -#. **Algorithm Selection** - Identify the right algorithm for a given dataset, choosing from: - - * AdaBoostClassifier - * DecisionTreeClassifier - * ExtraTreesClassifier - * KNeighborsClassifier - * LGBMClassifier - * LinearSVC - * LogisticRegression - * RandomForestClassifier - * SVC - * XGBClassifier - -#. **Adaptive Sampling** - Choose the right subset of samples for evaluation while trying to balance classes at the same time. -#. **Feature Selection** - Choose the right set of features that maximize score for the chosen algorithm. -#. **Hyperparameter Tuning** - Find the right model parameters that maximize score for the given dataset. - -All these modules are readily combined into a simple AutoML pipeline that automates the entire machine learning process with minimal user input and interaction. The ``OracleAutoMLProvider`` class supports two arguments: - -#. **n_jobs**: Specifies the degree of parallelism for Oracle AutoML. -1 (the default) means that AutoML uses all available cores. -#. **loglevel**: The verbosity of output for Oracle AutoML. Can be specified using the Python `logging module `_. - -Create an ``OracleAutoMLProvider`` object that uses all available cores and disable any logging. - -.. code:: python3 - - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - -Train -===== - -The AutoML API is quite simple to work with. Create an instance of Oracle AutoML (``oracle_automl``). Then the training data is passed to the ``fit()`` function that does the following: - -#. Preprocesses the training data. -#. Identifies the best algorithm. -#. Identifies the best set of features. -#. Identifies the best set of hyperparameters for this data. - -A model is then generated that can be used for prediction tasks. ADS uses the ``roc_auc`` scoring metric to evaluate the performance of this model on unseen data (``X_test``). - -.. code:: python3 - - oracle_automl = AutoML(train, provider=ml_engine) - automl_model1, baseline = oracle_automl.train() - -.. raw:: html - - -
- -

- -.. raw:: html - - -
-

Training complete (66.81 seconds)

-
- -.. raw:: html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Training Dataset size(32561, 14)
Validation Dataset sizeNone
CV5
Target variableincome
Optimization Metricroc_auc
Initial number of Features14
Selected number of Features9
Selected Features[age, workclass, education, education-num, occupation, relationship, capital-gain, capital-loss, hours-per-week]
Selected AlgorithmLGBMClassifier
End-to-end Elapsed Time (seconds)66.81
Selected Hyperparameters{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}
Mean Validation Score0.923
AutoML n_jobs64
AutoML version0.3.1
- -.. csv-table:: Adult - :header-rows: 1 - - Rank based on Performance,Algorithm,#Samples,#Features,Mean Validation Score,Hyperparameters,CPU Time - 2,LGBMClassifier_HT,32561,9,0.9230,"{'boosting_type': 'gbdt', 'class_weight': 'balanced', 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",5.7064 - 3,LGBMClassifier_HT,32561,9,0.9230,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.0012000000000000001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",4.0975 - 4,LGBMClassifier_HT,32561,9,0.9230,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.0011979297617518694, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",3.1736 - 5,LGBMClassifier_HT,32561,9,0.9227,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 127, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",5.9078 - 6,LGBMClassifier_HT,32561,9,0.9227,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0, 'reg_lambda': 0}",3.9490 - ...,...,...,...,...,...,... - 188,LGBMClassifier_FRanking_FS,32561,1,0.7172,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.5153 - 189,LGBMClassifier_AVGRanking_FS,32561,1,0.7081,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.5611 - 190,LGBMClassifier_RFRanking_FS,32561,2,0.7010,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.9917 - 191,LGBMClassifier_AdaBoostRanking_FS,32561,1,0.5567,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.7886 - 192,LGBMClassifier_RFRanking_FS,32561,1,0.5190,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.0109 - - -During the Oracle AutoML process, a summary of the optimization process is printed: - -#. Information about the training data. -#. Information about the AutoML Pipeline. For example,the selected features that AutoML found to be most predictive in the training data, the selected algorithm that was the best choice for this data, and the model hyperparameters for the selected algorithm. -#. A summary of the different trials that AutoML performs in order to identify the best model. - -The Oracle AutoML Pipeline automates much of the data science process, trying out many different machine learning parameters quickly in a parallel fashion. The model provides a ``print_trials`` API to output -all the different trials performed by Oracle AutoML. The API has two arguments: - -#. **max_rows**: Specifies the total number of trials that are printed. By default, all trials are printed. -#. **sort_column**: Column to sort results by. Must be one of: - - * Algorithm - * #Samples - * #Features - * Mean Validation Score - * Hyperparameters - * CPU Time - -.. code:: python3 - - oracle_automl.print_trials(max_rows=20, sort_column='Mean Validation Score') - -.. csv-table:: - :header-rows: 1 - - Rank based on Performance,Algorithm,#Samples,#Features,Mean Validation Score,Hyperparameters,CPU Time - 2,LGBMClassifier_HT,32561,9,0.9230,"{'boosting_type': 'gbdt', 'class_weight': 'balanced', 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",5.7064 - 3,LGBMClassifier_HT,32561,9,0.9230,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.0012000000000000001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",4.0975 - 4,LGBMClassifier_HT,32561,9,0.9230,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.0011979297617518694, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",3.1736 - 5,LGBMClassifier_HT,32561,9,0.9227,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 127, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}",5.9078 - 6,LGBMClassifier_HT,32561,9,0.9227,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 8, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0, 'reg_lambda': 0}",3.9490 - ...,...,...,...,...,...,... - 188,LGBMClassifier_FRanking_FS,32561,1,0.7172,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.5153 - 189,LGBMClassifier_AVGRanking_FS,32561,1,0.7081,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.5611 - 190,LGBMClassifier_RFRanking_FS,32561,2,0.7010,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.9917 - 191,LGBMClassifier_AdaBoostRanking_FS,32561,1,0.5567,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.7886 - 192,LGBMClassifier_RFRanking_FS,32561,1,0.5190,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.0109 - - -ADS also provides the ability to visualize the results of each stage of the AutoML pipeline. The following plot shows the scores predicted by algorithm selection for each algorithm. The horizontal line shows the average score across all algorithms. Algorithms below the line are colored turquoise, whereas those with a score higher than the mean are colored teal. You can see that the LightGBM classifier achieved the highest predicted score (orange bar) and is chosen for subsequent stages of the pipeline. - -.. code:: python3 - - oracle_automl.visualize_algorithm_selection_trials() - -.. image:: figures/output_30_0.png - - -After algorithm selection, adaptive sampling aims to find the smallest dataset sample that can be created without compromising validation set score for the algorithm chosen (LightGBM). - -.. note:: - If you have fewer than 1000 data points in your dataset, adaptive sampling is not run and visualizations are not generated. - -.. code:: python3 - - oracle_automl.visualize_adaptive_sampling_trials() - -.. image:: figures/output_32_0.png - -After finding a sample subset, the next goal of Oracle AutoML is to find a relevant feature subset that maximizes score for the chosen algorithm. Oracle AutoML feature selection follows an intelligent search strategy. It looks at various possible feature rankings and subsets, and identifies that smallest feature subset that does not compromise on score for the chosen algorithm ``ExtraTreesClassifier``). The orange line shows the optimal number of features chosen by feature selection (9 features - [age, workclass, education, education-num, occupation, relationship, capital-gain, capital-loss, hours-per-week]). - -.. code:: python3 - - oracle_automl.visualize_feature_selection_trials() - -.. image:: figures/output_34_0.png - - -Hyperparameter tuning is the last stage of the Oracle AutoML pipeline It focuses on improving the chosen algorithm’s score on the reduced dataset (given by adaptive sampling and feature selection). ADS uses a novel algorithm to search across many hyperparamter dimensions. Convergence is automatic when optimal hyperparameters are identified. Each trial in the following graph represents a particular hyperparamter combination for the selected model. - -.. code:: python3 - - oracle_automl.visualize_tuning_trials() - -.. image:: figures/output_36_0.png - -Model List -========== - -The Oracle AutoML solution also has a ``model_list`` argument, allowing you to control the what algorithms AutoML considers during -its optimization process. ``model_list`` is specified as a list of strings, which can be any combination of the following: - -For classification: - -* AdaBoostClassifier -* DecisionTreeClassifier -* ExtraTreesClassifier -* KNeighborsClassifier -* LGBMClassifier -* LinearSVC -* LogisticRegression -* RandomForestClassifier -* SVC -* XGBClassifier - -For regression: - -* AdaBoostRegressor -* DecisionTreeRegressor -* ExtraTreesRegressor -* KNeighborsRegressor -* LGBMRegressor -* LinearSVR -* LinearRegression -* RandomForestRegressor -* SVR -* XGBRegressor - -This example specifies that AutoML only consider the ``LogisticRegression`` classifier because it is a good algorithm for this dataset. - -.. code:: python3 - - automl_model2, _ = oracle_automl.train(model_list=['LogisticRegression']) - -.. raw:: html - - -
- -
- -
-

AUTOML

-
-

AutoML Training (OracleAutoMLProvider)...

- -
- -.. raw:: html - - -
-

Training complete (22.24 seconds)

-
- -.. raw:: html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Training Dataset size(32561, 14)
Validation Dataset sizeNone
CV5
Target variableincome
Optimization Metricroc_auc
Initial number of Features14
Selected number of Features13
Selected Features[age, workclass, fnlwgt, education, education-num, marital-status, occupation, relationship, race, sex, capital-gain, capital-loss, hours-per-week]
Selected AlgorithmLogisticRegression
End-to-end Elapsed Time (seconds)22.24
Selected Hyperparameters{'C': 57.680029607093125, 'class_weight': None, 'solver': 'lbfgs'}
Mean Validation Score0.8539
AutoML n_jobs64
AutoML version0.3.1
- - -.. csv-table:: - :header-rows: 1 - - Rank based on Performance,Algorithm,#Samples,#Features,Mean Validation Score,Hyperparameters,CPU Time - 2,LogisticRegression_HT,32561,13,0.8539,"{'C': 57.680029607093125, 'class_weight': 'balanced', 'solver': 'lbfgs'}",2.4388 - 3,LogisticRegression_HT,32561,13,0.8539,"{'C': 57.680029607093125, 'class_weight': None, 'solver': 'newton-cg'}",6.8440 - 4,LogisticRegression_HT,32561,13,0.8539,"{'C': 57.680029607093125, 'class_weight': None, 'solver': 'warn'}",1.6099 - 5,LogisticRegression_HT,32561,13,0.8539,"{'C': 57.680029607093125, 'class_weight': 'balanced', 'solver': 'warn'}",3.2381 - 6,LogisticRegression_HT,32561,13,0.8539,"{'C': 57.680029607093125, 'class_weight': 'balanced', 'solver': 'liblinear'}",3.0313 - ...,...,...,...,...,...,... - 71,LogisticRegression_MIRanking_FS,32561,2,0.6867,"{'C': 1.0, 'class_weight': 'balanced', 'solver': 'liblinear', 'random_state': 12345}",1.4268 - 72,LogisticRegression_AVGRanking_FS,32561,1,0.6842,"{'C': 1.0, 'class_weight': 'balanced', 'solver': 'liblinear', 'random_state': 12345}",0.2242 - 73,LogisticRegression_RFRanking_FS,32561,2,0.6842,"{'C': 1.0, 'class_weight': 'balanced', 'solver': 'liblinear', 'random_state': 12345}",1.2302 - 74,LogisticRegression_AdaBoostRanking_FS,32561,1,0.5348,"{'C': 1.0, 'class_weight': 'balanced', 'solver': 'liblinear', 'random_state': 12345}",0.2380 - 75,LogisticRegression_RFRanking_FS,32561,1,0.5080,"{'C': 1.0, 'class_weight': 'balanced', 'solver': 'liblinear', 'random_state': 12345}",0.2132 - -Built-in Scoring Metric -======================= - -The Oracle AutoML tool tries to maximize a given scoring metric, by looking at different algorithms, features, and hyperparameter choices. By default, the score metric is set to ``roc_auc`` for binary classification, ``recall_macro`` for multinomial classification, and ``neg_mean_squared_error`` for regression. You can also provide your own scoring metric using the ``score_metric`` argument, allowing AutoML to maximize using that metric. The scoring metric can be specified as a string. - -- For binary classification, the supported metrics are ‘roc_auc’, ‘accuracy’, ‘f1’, ‘precision’, ‘recall’, ‘f1_micro’, ‘f1_macro’, ‘f1_weighted’, ‘f1_samples’, ‘recall_micro’, ‘recall_macro’, ‘recall_weighted’, ‘recall_samples’, ‘precision_micro’, ‘precision_macro’, ‘precision_weighted’, and ‘precision_samples’. - -- For multinomial classification, the supported metrics are ‘recall_macro’, ‘accuracy’, ‘f1_micro’, ‘f1_macro’, ‘f1_weighted’, ‘f1_samples’, ‘recall_micro’, ‘recall_weighted’, ‘recall_samples’, ‘precision_micro’, ‘precision_macro’, ‘precision_weighted’, ‘precision_samples’ - For regression, one of ‘neg_mean_squared_error’, ‘r2’, ‘neg_mean_absolute_error’, ‘neg_mean_squared_log_error’, and ‘neg_median_absolute_error’. - -- In this example, AutoML will optimize on the ‘f1_macro’ scoring metric: - -.. code:: python3 - - automl_model3, _ = oracle_automl.train(score_metric='f1_macro') - -Custom Scoring Metric -===================== - -Alternatively, the ``score_metric`` can be specified as a user-defined function of the form. - -:: - - def score_fn(y_true, y_pred): - logic here - return score - -The scoring function needs to the be encapsulated as a scikit-learn scorer using the `make_scorer function `_. - -This example leverages the scikit-learn’s implementation of the balanced accuracy scoring function. Then a scorer function is created (``score_fn``) and passed to the ``score_metric argument`` of train. - -.. code:: python3 - - import numpy as np - from sklearn.metrics import make_scorer, f1_score - - # Define the scoring function - score_fn = make_scorer(f1_score, greater_is_better=True, needs_proba=False, average='macro') - automl_model4, _ = oracle_automl.train(score_metric=score_fn) - -.. raw:: html - - -
- -
- -
-

AUTOML

-
-

AutoML Training (OracleAutoMLProvider)...

- -
- -.. raw:: html - - -
-

Training complete (71.19 seconds)

-
- -.. raw:: html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Training Dataset size(32561, 14)
Validation Dataset sizeNone
CV5
Target variableincome
Optimization Metricmake_scorer(f1_score, average=macro)
Initial number of Features14
Selected number of Features9
Selected Features[age, workclass, education, education-num, occupation, relationship, capital-gain, capital-loss, hours-per-week]
Selected AlgorithmLGBMClassifier
End-to-end Elapsed Time (seconds)71.19
Selected Hyperparameters{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0.0023849484694627374, 'reg_lambda': 0}
Mean Validation Score0.7892
AutoML n_jobs64
AutoML version0.3.1
- -.. csv-table:: - :header-rows: 1 - - Rank based on Performance,Algorithm,#Samples,#Features,Mean Validation Score,Hyperparameters,CPU Time - 2,LGBMClassifier_HT,32561,9,0.7892,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0.0023949484694617373, 'reg_lambda': 0}",3.6384 - 3,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 1e-10, 'reg_lambda': 0}",4.0626 - 4,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 1.0000099999e-05, 'reg_lambda': 0}",5.3854 - 5,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': 'balanced', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0, 'reg_lambda': 0}",2.7319 - 6,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.0012000000000000001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0, 'reg_lambda': 0}",4.9743 - ...,...,...,...,...,...,... - 182,LGBMClassifier_AdaBoostRanking_FS,32561,2,0.5889,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",4.0190 - 183,LGBMClassifier_AVGRanking_FS,32561,1,0.5682,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.3313 - 184,LGBMClassifier_RFRanking_FS,32561,2,0.5645,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.8365 - 185,LGBMClassifier_AdaBoostRanking_FS,32561,1,0.5235,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.2191 - 186,LGBMClassifier_RFRanking_FS,32561,1,0.4782,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.9353 - - -Time Budget -=========== - -The Oracle AutoML tool also supports a user given time budget in seconds. This time budget works as a hint, and AutoML tries to terminate computation as soon as the time budget is exhausted by returning the current best model. The model returned depends on the stage that AutoML was in when the time budget was exhausted. - -If the time budget is exhausted before: - -#. Preprocessing completes, then a Naive Bayes model is returned for classification and Linear Regression for regression. -#. Algorithm selection completes, the partial results for algorithm selection are used to evaluate the best candidate that is returned. -#. Hyperparameter tuning completes, then the current best known hyperparameter configuration is returned. - -Given the small size of this dataset, a small time budget of 10 seconds is specified using the ``time_budget`` argument. The time budget in this case is exhausted during algorithm selection, and the currently known best model (``LGBMClassifier``) is returned. - -.. code:: python3 - - automl_model5, _ = oracle_automl.train(time_budget=10) - -.. raw:: html - - -
- -
- -
-

AUTOML

-
-

AutoML Training (OracleAutoMLProvider)...

- -
- -.. raw:: html - - -
-

Training complete (12.35 seconds)

-
- -.. raw:: html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Training Dataset size(32561, 14)
Validation Dataset sizeNone
CV5
Target variableincome
Optimization Metricroc_auc
Initial number of Features14
Selected number of Features1
Selected Features[capital-loss]
Selected AlgorithmLGBMClassifier
End-to-end Elapsed Time (seconds)12.35
Selected Hyperparameters{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0, 'class_weight': None}
Mean Validation Score0.5578
AutoML n_jobs64
AutoML version0.3.1
- -.. csv-table:: - :header-rows: 1 - - Rank based on Performance,Algorithm,#Samples,#Features,Mean Validation Score,Hyperparameters,CPU Time - 2,LGBMClassifier_HT,32561,9,0.7892,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0.0023949484694617373, 'reg_lambda': 0}",3.6384 - 3,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 1e-10, 'reg_lambda': 0}",4.0626 - 4,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 1.0000099999e-05, 'reg_lambda': 0}",5.3854 - 5,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': 'balanced', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0, 'reg_lambda': 0}",2.7319 - 6,LGBMClassifier_HT,32561,9,0.7890,"{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.0012000000000000001, 'n_estimators': 100, 'num_leaves': 32, 'reg_alpha': 0, 'reg_lambda': 0}",4.9743 - ...,...,...,...,...,...,... - 182,LGBMClassifier_AdaBoostRanking_FS,32561,2,0.5889,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",4.0190 - 183,LGBMClassifier_AVGRanking_FS,32561,1,0.5682,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.3313 - 184,LGBMClassifier_RFRanking_FS,32561,2,0.5645,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.8365 - 185,LGBMClassifier_AdaBoostRanking_FS,32561,1,0.5235,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",2.2191 - 186,LGBMClassifier_RFRanking_FS,32561,1,0.4782,"{'boosting_type': 'gbdt', 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 1, 'class_weight': 'balanced'}",1.9353 - - -Minimum Feature List -==================== - -The Oracle AutoML Pipeline also supports a ``min_features`` argument. AutoML ensures that these features are part of the final model that it creates, and these are not dropped during the feature selection phase. - -It can take three possible types of values: - -* If int, 0 < min_features <= n_features -* If float, 0 < min_features <= 1.0 -* If list, names of features to keep. For example, [‘a’, ‘b’] means keep features ‘a’ and ‘b’. - -.. code:: python3 - - automl_model6, _ = oracle_automl.train(min_features=['fnlwgt', 'native-country']) - -.. raw:: html - - -
- -
- -
-

AUTOML

-
-

AutoML Training (OracleAutoMLProvider)...

- -
- -.. raw:: html - - -
-

Training complete (78.20 seconds)

-
- -.. raw:: html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Training Dataset size(32561, 14)
Validation Dataset sizeNone
CV5
Target variableincome
Optimization Metricroc_auc
Initial number of Features14
Selected number of Features14
Selected Features[age, workclass, fnlwgt, education, education-num, marital-status, occupation, relationship, race, sex, capital-gain, capital-loss, hours-per-week, native-country]
Selected AlgorithmLGBMClassifier
End-to-end Elapsed Time (seconds)78.2
Selected Hyperparameters{'boosting_type': 'gbdt', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': 5, 'min_child_weight': 0.001, 'n_estimators': 133, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0}
Mean Validation Score0.9235
AutoML n_jobs64
AutoML version0.3.1
- -| - -Compare Models -============== - -A model trained using AutoML can easily be deployed into production because it behaves similar to any standard Machine Learning model. This example evaluates the model on unseen data stored in test. Each of the generated AutoML models is renamed making them easier to visualize. ADS uses ``ADSEvaluator`` to visualize behavior for each of the models on the test set, including the baseline. - -.. code:: python3 - - automl_model1.rename('AutoML_Default') - automl_model2.rename('AutoML_ModelList') - automl_model3.rename('AutoML_ScoringString') - automl_model4.rename('AutoML_ScoringFunction') - automl_model5.rename('AutoML_TimeBudget') - automl_model6.rename('AutoML_MinFeatures') - evaluator = ADSEvaluator(test, models=[automl_model1, automl_model2, automl_model3, automl_model4, automl_model5, automl_model6, baseline], - training_data=train, positive_class='>50K') - evaluator.show_in_notebook(plots=['normalized_confusion_matrix']) - evaluator.metrics - -.. image:: figures/output_48_4.png - -.. image:: figures/output_48_5.png - From 7935e7735e0c08cac7e50f4a9880dd1a9d8a97ce Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 22:51:20 -0800 Subject: [PATCH 061/147] fix the link --- docs/source/user_guide/model_training/automl/index.rst | 2 +- docs/source/user_guide/model_training/automl/overview.rst | 3 +++ docs/source/user_guide/model_training/automl/quick_start.rst | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index 3421f1d63..54c80766e 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -7,7 +7,7 @@ The Oracle AutoMLx One of these is that ADS and AutoMLx can now have independent version cadences. AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. To offer this functionality, the best way is for the ADS to work together as separate libraries. - The AutoMlx library can be found in `automlx_p38_cpu_v#` conda pack. The documentation can be found at this link `https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html>`__. Notebook examples can be found at this link `https://github.com/oracle-samples/automlx>`__. + The AutoMlx library can be found in `automlx_p38_cpu_v#` conda pack. The documentation can be found at this `link `__. Notebook examples can be found at this `link `__. .. toctree:: :hidden: diff --git a/docs/source/user_guide/model_training/automl/overview.rst b/docs/source/user_guide/model_training/automl/overview.rst index 8b07b59da..eb8db8fa4 100644 --- a/docs/source/user_guide/model_training/automl/overview.rst +++ b/docs/source/user_guide/model_training/automl/overview.rst @@ -1,3 +1,6 @@ +Overview +======== + Pipeline ******** diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst index 1dc0f218b..72b5a313d 100644 --- a/docs/source/user_guide/model_training/automl/quick_start.rst +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -1,6 +1,5 @@ -============================================================ Quick Start -============================================================ +=========== This section provides a quick introduction to build a classifier using the Oracle AutoMLx tool for Iris dataset. The dataset is a multi-class classification dataset, and more details about the dataset From 65e676ef787910c201ee02f068577610c069bf63 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:00:05 -0800 Subject: [PATCH 062/147] fix the doc format --- .../model_training/automl/overview.rst | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/source/user_guide/model_training/automl/overview.rst b/docs/source/user_guide/model_training/automl/overview.rst index eb8db8fa4..e49f56840 100644 --- a/docs/source/user_guide/model_training/automl/overview.rst +++ b/docs/source/user_guide/model_training/automl/overview.rst @@ -1,23 +1,25 @@ Overview ======== +-------- Pipeline -******** +-------- An AutoML Pipeline consists of these four main stages: -.. image:: figures/pipeline.png +.. image:: ../figures/pipeline.png The stages operate in sequence:  .. contents:: +------------------- Algorithm Selection -=================== +------------------- With a given dataset and a prediction task, the goal is to identify the algorithm that maximizes the model score. This best algorithm is not always intuitive and simply picking a complex model is suboptimal for many use cases. The ADS algorithm selection stage is designed to rank algorithms based on their estimated predictive performance on the given dataset. -.. image:: figures/algorithm_selection.png +.. image:: ../figures/algorithm_selection.png For a given dataset, the algorithm selection process is as follows: @@ -26,13 +28,13 @@ For a given dataset, the algorithm selection process is as follows: #. Rank algorithms based on their predicted performance. #. Select the optimal algorithm. - +----------------- Adaptive Sampling -================= +----------------- Adaptive sampling iteratively subsamples the dataset and evaluates each sample to obtain a score for a specific algorithm. The goal is to find the smallest sample size that adequately represents the full dataset. It is used in subsequent pipeline stages without sacrificing the quality of the model. -.. image:: figures/adaptive_sampling.png +.. image:: ../figures/adaptive_sampling.png The adaptive sampling process is as follows: @@ -41,13 +43,13 @@ The adaptive sampling process is as follows: #. Iterate until the score converges. #. The identified sample is then used for subsequent stages of the AutoML Pipeline. - +----------------- Feature Selection -================= +----------------- The feature selection stage aims to select a subset of features that are highly predictive of the target. This speeds up model training without loss of predictive performance. The ADS feature selection approach leverages meta-learning to intelligently identify the optimal feature subset for a given algorithm and dataset. The high level process is: -.. image:: figures/feature_selection.png +.. image:: ../figures/feature_selection.png For a given dataset, the feature selection process is as follows: @@ -57,11 +59,11 @@ For a given dataset, the feature selection process is as follows: #. Algorithm performance is predicted by leveraging meta-learning on a given feature subset. #. Iterating over multiple feature subsets, the optimal subset is determined. - +--------------------- Hyperparameter Tuning -===================== +--------------------- The hyperparameter tuning stage determines the optimal values for the model's hyperparameters. Generally, tuning is the most time-consuming stage of an AutoML pipeline. Therefore, the hyperparameter tuning process is designed with efficiency and scalability as first-order requirements. The ADS tuning strategy is summarized as: -.. image:: figures/tuning.png +.. image:: ../figures/tuning.png From 64aa68e781d4b1805f9de24dea28282329504d69 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:21:56 -0800 Subject: [PATCH 063/147] fix more format --- .../model_training/automl/index.rst | 4 ++-- .../model_training/automl/quick_start.rst | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index 54c80766e..18ef1a754 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -13,6 +13,6 @@ The Oracle AutoMLx :hidden: :maxdepth: 1 - - overview quick_start + overview + diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst index 72b5a313d..885de0e0d 100644 --- a/docs/source/user_guide/model_training/automl/quick_start.rst +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -63,7 +63,7 @@ AutoMLx consists of five main modules: All these pieces are readily combined into a simple AutoMLx pipeline which automates the entire Machine Learning process with minimal user input/interaction. -The AutoMLx API is quite simple to work with. We create a :obj:`automl.Pipeline`__ instance. +The AutoMLx API is quite simple to work with. We create a :obj:`automl.Pipeline `__ instance. Next, the training data is passed to the `fit() `__ function which executes the previously mentioned steps. .. code-block:: python3 @@ -84,19 +84,19 @@ Here, we use the `F1_score` scoring metric to evaluate the performance of this m Score on test data : 0.975983436853002 -The `automl.Pipeline`__ can also fit regression, forecasting and anomaly detection models. +The `automl.Pipeline `__ can also fit regression, forecasting and anomaly detection models. Please check out the rest of the documentation for more details about advanced configuration parameters. Explain a classifier -------------------- For a variety of decision-making tasks, getting only a prediction as model output is not sufficient. A user may wish to know why the model outputs that prediction, or which data features are relevant for that prediction. -For that purpose the Oracle AutoMLx solution defines the `automl.interface.mlx.MLExplainer`__ object, which allows to compute a variety of model explanations for any AutoMLx-trained pipeline or scikit-learn-like model. -`automl.interface.mlx.MLExplainer`__ takes as argument the trained model, the training data and labels, as well as the task. +For that purpose the Oracle AutoMLx solution defines the `automl.interface.mlx.MLExplainer `__ object, which allows to compute a variety of model explanations for any AutoMLx-trained pipeline or scikit-learn-like model. +`automl.interface.mlx.MLExplainer `__ takes as argument the trained model, the training data and labels, as well as the task. .. code-block:: python3 ->>> explainer = automl.MLExplainer(est, + >>> explainer = automl.MLExplainer(est, X_train, y_train, task="classification") @@ -107,6 +107,8 @@ This notion of feature importance considers each feature independently from all The method :obj:`explain_model() ` allows to compute such feature importances. It also provides 95% confidence intervals for each feature importance attribution. +.. code-block:: python3 + >>> result_explain_model_default = explainer.explain_model() >>> result_explain_model_default.to_dataframe() feature attribution upper_bound lower_bound @@ -117,8 +119,8 @@ The method :obj:`explain_model() `__, for example, using Kernel SHAP or an enhanced LIME; - - `Feature Dependence Explanations `__, such as partial dependence plots or accumulated local effects; - - `Interactive What-IF explainers `__, which let users explore a model's predictions; and - - `Counterfactual explanations __`, which show how to change a row to obtain a desired outcome. -Please check out the `automl.interface.mlx.MLExplainer`__ documentation for more details. \ No newline at end of file +#. `Local feature importance `__, for example, using Kernel SHAP or an enhanced LIME; +#. `Feature Dependence Explanations `__, such as partial dependence plots or accumulated local effects; +#. `Interactive What-IF explainers `__, which let users explore a model's predictions; and +#. `Counterfactual explanations `__, which show how to change a row to obtain a desired outcome. +Please check out the `automl.interface.mlx.MLExplainer `__ documentation for more details. \ No newline at end of file From e7cae2e336347db46a54bac0d7bbacd40f31d24b Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:22:49 -0800 Subject: [PATCH 064/147] fix more format --- docs/source/user_guide/model_training/automl/quick_start.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst index 885de0e0d..893f39fa4 100644 --- a/docs/source/user_guide/model_training/automl/quick_start.rst +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -119,8 +119,10 @@ The method :obj:`explain_model() `__, for example, using Kernel SHAP or an enhanced LIME; #. `Feature Dependence Explanations `__, such as partial dependence plots or accumulated local effects; #. `Interactive What-IF explainers `__, which let users explore a model's predictions; and #. `Counterfactual explanations `__, which show how to change a row to obtain a desired outcome. + Please check out the `automl.interface.mlx.MLExplainer `__ documentation for more details. \ No newline at end of file From 34230b0ef1292ffa2536b1830de2e3a69c3587c8 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:25:46 -0800 Subject: [PATCH 065/147] fix more format --- docs/source/user_guide/model_training/automl/quick_start.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst index 893f39fa4..a5d28e26b 100644 --- a/docs/source/user_guide/model_training/automl/quick_start.rst +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -63,7 +63,7 @@ AutoMLx consists of five main modules: All these pieces are readily combined into a simple AutoMLx pipeline which automates the entire Machine Learning process with minimal user input/interaction. -The AutoMLx API is quite simple to work with. We create a :obj:`automl.Pipeline `__ instance. +The AutoMLx API is quite simple to work with. We create a `automl.Pipeline `__ instance. Next, the training data is passed to the `fit() `__ function which executes the previously mentioned steps. .. code-block:: python3 From 3ec58153e06ccafddfd50dc4e2e671a54b70a778 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:35:18 -0800 Subject: [PATCH 066/147] remove automl --- docs/source/ads.rst | 1 - .../accumulated_local_effects.rst | 146 -------------- .../feature_dependence.rst | 187 ------------------ .../feature_importance.rst | 137 ------------- .../figures/ads_mlx_ale.png | Bin 186543 -> 0 bytes .../figures/ads_mlx_ale_pdp_x1.png | Bin 20643 -> 0 bytes .../figures/ads_mlx_ale_x1.png | Bin 23814 -> 0 bytes ...lx_boston_whatif_explore_predictions_1.png | Bin 25013 -> 0 bytes ...lx_boston_whatif_explore_predictions_2.png | Bin 29202 -> 0 bytes ...lx_boston_whatif_explore_predictions_3.png | Bin 17738 -> 0 bytes .../ads_mlx_boston_whatif_explore_sample.png | Bin 73261 -> 0 bytes .../figures/ads_mlx_titanic_ice_age.png | Bin 109311 -> 0 bytes .../figures/ads_mlx_titanic_ice_sex.png | Bin 29251 -> 0 bytes .../figures/ads_mlx_titanic_local.png | Bin 118075 -> 0 bytes .../ads_mlx_titanic_local_diagnostics.png | Bin 232145 -> 0 bytes .../figures/ads_mlx_titanic_pdp_age.png | Bin 30321 -> 0 bytes .../ads_mlx_titanic_pdp_age_dataframe.png | Bin 101011 -> 0 bytes .../ads_mlx_titanic_pdp_age_diagnostics.png | Bin 164561 -> 0 bytes .../ads_mlx_titanic_pdp_pclass_age.png | Bin 30846 -> 0 bytes .../figures/ads_mlx_titanic_pdp_sex.png | Bin 17118 -> 0 bytes .../figures/ads_mlx_titanic_pi_bar.png | Bin 22407 -> 0 bytes .../figures/ads_mlx_titanic_pi_box.png | Bin 21340 -> 0 bytes .../ads_mlx_titanic_pi_diagnostics.png | Bin 98059 -> 0 bytes .../figures/ads_mlx_titanic_pi_scatter.png | Bin 24804 -> 0 bytes .../model_explainability/global.rst | 18 -- .../user_guide/model_explainability/index.rst | 15 -- .../user_guide/model_explainability/lime.rst | 166 ---------------- .../user_guide/model_explainability/local.rst | 16 -- .../user_guide/model_explainability/mlx.rst | 62 ------ .../model_explainability/overview.rst | 44 ----- .../permutation_importance.rst | 146 -------------- .../model_explainability/whatif.rst | 97 --------- .../frameworks/automlmodel.rst | 2 +- .../model_training/automl/overview.rst | 1 - docs/source/user_guide/overview/overview.rst | 37 ---- .../user_guide/quickstart/quickstart.rst | 173 +--------------- 36 files changed, 4 insertions(+), 1244 deletions(-) delete mode 100644 docs/source/user_guide/model_explainability/accumulated_local_effects.rst delete mode 100644 docs/source/user_guide/model_explainability/feature_dependence.rst delete mode 100644 docs/source/user_guide/model_explainability/feature_importance.rst delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_ale.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_ale_pdp_x1.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_ale_x1.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_1.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_2.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_3.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_sample.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_ice_age.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_ice_sex.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_local.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_local_diagnostics.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age_dataframe.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age_diagnostics.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_pclass_age.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_sex.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_bar.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_box.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_diagnostics.png delete mode 100644 docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_scatter.png delete mode 100644 docs/source/user_guide/model_explainability/global.rst delete mode 100644 docs/source/user_guide/model_explainability/index.rst delete mode 100644 docs/source/user_guide/model_explainability/lime.rst delete mode 100644 docs/source/user_guide/model_explainability/local.rst delete mode 100644 docs/source/user_guide/model_explainability/mlx.rst delete mode 100644 docs/source/user_guide/model_explainability/overview.rst delete mode 100644 docs/source/user_guide/model_explainability/permutation_importance.rst delete mode 100644 docs/source/user_guide/model_explainability/whatif.rst diff --git a/docs/source/ads.rst b/docs/source/ads.rst index 38af463d2..b2db11add 100644 --- a/docs/source/ads.rst +++ b/docs/source/ads.rst @@ -6,7 +6,6 @@ Subpackages .. toctree:: - ads.automl ads.catalog ads.common ads.bds diff --git a/docs/source/user_guide/model_explainability/accumulated_local_effects.rst b/docs/source/user_guide/model_explainability/accumulated_local_effects.rst deleted file mode 100644 index 33ec045d8..000000000 --- a/docs/source/user_guide/model_explainability/accumulated_local_effects.rst +++ /dev/null @@ -1,146 +0,0 @@ -Accumulated Local Effects -************************* - -Overview -======== - -Similar to Partial Dependence Plots (PDP), Accumulated Local Effects (ALE) is a model-agnostic global explanation method that evaluates the relationship between feature values and target variables. However, in the event that features are highly correlated, PDP may include unlikely combinations of feature values in the average prediction calculation due to the independent manipulation of feature values across the marginal distribution. This lowers the trust in the PDP explanation when features have strong correlation. Unlike PDP, ALE handles feature correlations by averaging and accumulating the difference in predictions across the conditional distribution, which isolates the effects of the specific feature. This comes at the cost of requiring a larger number of observations and a near uniform distribution of those observations so that the conditional distribution can be reliably determined. - -Description -=========== - -ALE highlights the effects that specific features have on the predictions of a machine learning model by partially isolating the effects of other features. Therefore, it tends to be robust against correlated features. The resulting ALE explanation is centered around the mean effect of the feature, such that the main feature effect is compared relative to the average prediction of the data. - -Correlated features can negatively affect the quality of many explanation techniques. Specifically, many challenges arise when the black-box model is used to make predictions on unlikely artificial data. That is data that that fall outside of the expected data distribution but are used in an explanation because they are not independent and the technique is not sensitive to this possibility. This can occur, for example, when the augmented data samples are not generated according the feature correlations or the effects of other correlated features are included in the evaluation of the feature of interest. Consequently, the resulting explanations may be misleading. In the context of PDP, the effect of a given feature may be heavily biased by the interactions with other features. - -To address the issues associated with correlated features, ALE: - -* Uses the conditional distribution of the feature of interest to generate augmented data. This tends to create more realistic data that using marginal distribution. This helps to ensure that evaluated feature values, e.g., ``xi``, are only compared with instances from the dataset that have similar values to ``xi``. -* Calculates the average of the differences in model predictions over the augmented data, instead of the average of the predictions themselves. This helps to isolate the effect of the feature of interest. For example, assuming we are evaluating the effect of a feature at value ``xi``, ALE computes the average of the difference in model predictions of the values in the neighborhood of ``xi``. That is, that observation within ``xi`` ±ϵ that meet the conditional requirement. This helps to reduce the effects of correlated features. - -The following example demonstrates the challenges with accurately evaluating the effect of a feature on a model’s predictions when features are highly correlated. Let us assume that features ``x1`` and ``x2`` are highly correlated. We can artificially construct ``x2`` by starting with ``x1`` and adding a small amount of random noise. Further assume that the target value is the product of these two features (e.g., y = ``x1`` * ``x2``). Since ``x1`` and ``x2`` are almost identical, the target value has a quadratic relationship with them. -A decision tree is trained on this dataset. Then different explanation techniques, PDP (first column), ICE (second column), and ALE (third column), are used to evaluate the effect of the features on the model predictions. Features ``x1`` and ``x2`` are evaluated in the first and second row, respectively. The following image demonstrates that PDP is unable to accurately identify the expected relationship due to the assumption that the features are not correlated. An examination of the ICE plots revels the quadratic relationship between the features and the target. However, the when taking as an aggregate, this effect disappears. In contrast, ALE is able to properly capture the isolated effect of each feature, highlighting the quadratic relationship. - -.. image:: figures/ads_mlx_ale.png - -The following summarizes the steps in computing ALE explanation (note: MLX supports one-feature ALE): - -* Start with a trained model. -* Select a feature to explain (for example, one of the important features identified in the global feature importance explanations). -* Compute the intervals of the selected feature to define the upper and lower bounds used to compute the difference in model predictions when the feature is increased or decreased. - - - Numerical features: using the selected feature’s value distribution extracted from the train dataset, MLX selects multiple different intervals from the feature’s distribution to evaluate (e.g., based on percentiles). The number of intervals to use and the range of the feature’s distribution to consider are configurable. - - Categorical features: since ALE computes the difference in model predictions between an increase and decrease in a feature’s value, features must have some notion of order. This can be challenging for categorical features, as there may not be a notion of order (e.g., eye color). To address this, MLX estimates the order of categorical feature values based on a categorical feature encoding technique. MLX provides multiple different encoding techniques based on the input data (e.g., ``distance_similarity``: computes a similarity matrix between all categorical feature values and the other feature values, and orders based on similarity. Target-based approaches estimate the similarity/order based on the relationship of categorical feature values with the target variable. The supported techniques include, target encoding, ``target``, James-Stein encoding, ``jamesstein``, Generalized Linear Mixed Model encoding, ``glmm``, M-estimate encoding, ``mestimate``, and Weight of Evidence encoding, ``woe``. The categorical feature value order is then used to compute the upper (larger categorical value) and lower (smaller categorical value) bounds for the selected categorical feature. - -* For each interval, MLX approximates the conditional distribution by identifying the samples that are in the neighborhood of the sample of interest. It then calculates the difference in the model prediction when the selected feature’s value of the samples is replaced by the upper and lower limits of the interval. If **N** different intervals are selected from the feature’s distribution, this process results in **2N** different augmented datasets It is **2N** as each selected feature of the sample are replaced with the upper and lower limits of the interval. The model inference then generates **2N** different model predictions, which are used to calculate the **N** differences. -* The prediction differences within each interval are averaged and accumulated in order, such that the ALE of a feature value that lies in the **k-th** interval is the sum of the effects of the first through the **k-th** interval. -* Finally, the accumulated feature effects at each interval is centered, such that the mean effect is zero. - -Interpretation -============== - -* Continuous or discrete numerical features: Visualized as line graphs. Each line represents the change in the model prediction when the selected feature has the given value compared to the average prediction. For example, an ALE value of ±b at xj = k indicates that when the value of feature j is equal to k, the model prediction is higher/lower by b compared to the average prediction. The x-axis shows the selected feature values and the y-axis shows the delta in the target prediction variable relative to the average prediction (e.g., the prediction probability for classification tasks and the raw predicted values for regression tasks). -* Categorical features: Visualized as vertical bar charts. Each bar represents the change in the model prediction when the selected feature has the given value compared to the average prediction. The interpretation of the value of the bar is similar to continuous features. The x-axis shows the different categorical values for the selected feature and the y-axis shows the change in the predicted value relative to the average prediction. This would be the prediction probability for classification tasks and the raw predicted values for regression tasks. - -Limitations -=========== - -There is an increased computational cost for performing an ALE analysis because of the large number of models that need to be computed relative to PDP. On a small dataset, this is generally not an issue. However, on larger datasets it can be. It is possible to parallelize the process and to also compute it in a distributed manner. - -The main disadvantage comes from the problem of sparsity of data. There needs to be sufficient number of observations in each neighborhood that is used in order to make a reasonable estimation. Even with large dataset this can be problematic if the data is not uniformly sampled, which is rarely the case. Also, with higher dimensionality the problem is made increasingly more difficult because of this curse of dimensionality. - -Depending on the class of model that is being use, it is common practice to remove highly correlated features. In this cases there is some rational to using a PDP for interpretation. However, if there is correlation in the data and the sampling of the data is suitable for an ALE analysis, it may be the preferred approach. - -Examples -======== - -The following is a purposefully extreme, but realistic, example that demonstrates the effects of highly correlated features on PDP and ALE explanations. The data set has three columns, ``x1``, ``x2`` and ``y``. - -* ``x1`` is generated from a uniform distribution with a range of [-5, 5]. -* ``x2`` is ``x1`` with some noise. ``x1`` and ``x2`` are highly correlated for illustration purposes. -* ``y`` is our target which is generated from an interaction term of ``x1 * x2`` and ``x2``. - -This model is trained using a Sklearn RegressorMixin model and wrapped in an ``ADSModel`` object. Please note that the ADS model explainers work with any model that is wrapped in an ADSModel object. - -.. code-block:: python3 - - import numpy as np - import pandas as pd - from ads.dataset.factory import DatasetFactory - from ads.common.model import ADSModel - from sklearn.base import RegressorMixin - - x1 = (np.random.rand(500) - 0.5) * 10 - x2 = x1 + np.random.normal(loc=0, scale=0.5, size=500) - y = x1 * x2 - - correlated_df = pd.DataFrame(np.stack((x1, x2, y), axis=1), columns=['x1', 'x2', 'y']) - correlated_ds = DatasetFactory.open(correlated_df, target='y') - - correlated_train, _ = correlated_ds.train_test_split(test_size=0) - - - class CorrelatedRegressor(RegressorMixin): - ''' - implement the true model - ''' - def fit(self, X=None, y=None): - self.y_bar_ = X.iloc[:, 0].to_numpy() * X.iloc[:, 1].to_numpy() + X.iloc[:, 1].to_numpy() - - def predict(self, X=None): - return X.iloc[:, 0].to_numpy() * X.iloc[:, 1].to_numpy() + X.iloc[:, 1].to_numpy() - - - # train a RegressorMixin model - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - correlated_regressor = CorrelatedRegressor() - correlated_regressor.fit(correlated_train.X, correlated_train.y) - - # Build ads models from ExtraTrees regressor - correlated_model = ADSModel.from_estimator(correlated_regressor, name="TrueModel") - - - # Create the ADS explainer object, which is used to construct - # global and local explanation objects. The ADSExplainer takes - # as input the model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - correlated_explainer = ADSExplainer(correlated_train, correlated_model, training_data=correlated_train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - correlated_global_explainer = correlated_explainer.global_explanation(provider=MLXGlobalExplainer()) - - - # A summary of the global accumulated local effects explanation - # algorithm and how to interpret the output - correlated_global_explainer.accumulated_local_effects_summary() - - # compute a PDP between x1 and the target, y - pdp_x1 = correlated_global_explainer.compute_partial_dependence("x1") - pdp_x1.show_in_notebook() - -.. image:: figures/ads_mlx_ale_pdp_x1.png - -The PDP plot shows a rug plot of the actual ``x1`` values along the x-axis and the relationship between ``x1`` and ``y`` appears as a line. However, it is known that the true relationship is not linear. ``y`` is the product of ``x1`` and ``x2``. Since ``x2`` nearly identical to ``x1``, effectively the relationship between ``x1`` and ``y`` is quadratic. The high level of correlation between ``x1`` and ``x2`` violates one of the assumptions of the PDP. As demonstrated, the bias created by this correlation results in a poor representation of the global relationship between ``x1`` and ``y``. - -.. code-block:: python3 - - # Compute the ALE on x1 - ale_x1 = correlated_global_explainer.compute_accumulated_local_effects("x1") - ale_x1.show_in_notebook() - -.. image:: figures/ads_mlx_ale_x1.png - -In comparison, the ALE plot does not have as strong a requirement that the features are uncorrelated. As such, there is very little bias introduced when they are. The following ALE plot demonstrates that it is able to accurately represent the relationship between ``x1`` and ``y`` as being quadratic. This is due to the fact that ALE uses the conditional distribution of these two features. This can be thought of as only using those instances where the values of ``x1`` and ``x2`` are close. - -In general, ALE plots are unbiased with correlated features as they use conditional probabilities. The PDP method uses the marginal probability and that can introduce a bias when there are highly correlated features. The advantage is that when the data is not rich enough to adequately determine all of the conditional probabilities or when the features are not highly correlated, it can be an effective method to assess the global impact of a feature in a model. - -References -========== - -- `Accumulated Local Effects (ALE) Plot `_ -- `Visualizing the effects of predictor variables in black box supervised learning models `_ - - diff --git a/docs/source/user_guide/model_explainability/feature_dependence.rst b/docs/source/user_guide/model_explainability/feature_dependence.rst deleted file mode 100644 index 9eec27796..000000000 --- a/docs/source/user_guide/model_explainability/feature_dependence.rst +++ /dev/null @@ -1,187 +0,0 @@ -Feature Dependence Explanations -******************************* - -Overview -======== - -Feature Dependence Explanations (PDP and ICE) are model-agnostic global explanation methods that evaluate the relationship between feature values and model target predictions. - -Description -=========== - -PDP and ICE highlight the marginal effect that specific features have on the predictions of a machine learning model. These explanation methods visualize the effects that different feature values have on the model's predictions. - -These are the main steps in computing PDP or ICE explanations: - -* Start with a trained machine learning model. -* Select a feature to explain (for example, one of the important features identified in the global feature permutation importance explanations.) -* Using the selected feature's value distribution extracted from the training dataset, ADS selects multiple different values from the feature's distribution to evaluate. The number of values to use and the range of the feature's distribution to consider are configurable. -* ADS replaces every sample in the provided dataset with the same feature value from the feature distribution and computes the model inference on the augmented dataset. This process is repeated for all of the selected values from the feature's distribution. If *N* different values are selected from the feature's distribution, this process results in *N* different datasets. Each with the selected feature having the same value for all samples in the corresponding dataset. The model inference then generates *N* different model predictions, each with *M* values (one for each sample in the augmented dataset.) -* For ICE, the model predictions for each augmented sample in the provided dataset are considered separately when the selected feature's value is replaced with a value from the feature distribution. This results in *N x M* different values. -* For PDP, the average model prediction is computed across all augmented dataset samples. This results in *N* different values (each an average of *M* predictions). - -The preceding is an example of one-feature PDP and ICE explanations. PDP also supports two-feature explanations while ICE only supports one feature. The main steps of the algorithm are the same though the explanation is computed on two features instead of one. - -* Select two features to explain. -* ADS computes the cross-product of values selected from the feature distributions to generate a list of different value combinations for the two selected features. For example, assuming we have selected *N* values from the feature distribution for each feature: - - [(:math:`X_{1}^{1}`, :math:`X_{2}^{1}`), - (:math:`X_{1}^{1}`, :math:`X_{2}^{2}`), :math:`\dots`, - (:math:`X_{1}^{1}`, :math:`X_{2}^{N-1}`), - (:math:`X_{1}^{1}`, :math:`X_{2}^{N}`), - (:math:`X_{1}^{2}`, :math:`X_{2}^{1}`), - (:math:`X_{1}^{2}`, :math:`X_{2}^{2}`), :math:`\dots`, - (:math:`X_{1}^{N}`, :math:`X_{2}^{N-1}`), - (:math:`X_{1}^{N}`, :math:`X_{2}^{N}`)] - -* For each feature value combination, ADS replaces every sample in the provided set with these two feature values and computes the model inference on the augmented dataset. There are *M* different samples in the provided dataset and *N* different values for each selected feature. This results in :math:`N^{2}` predictions from the model, each an average of *M* predictions. - -Interpretation -============== - -PDP ---- - -* One-feature - - - Continuous or discrete numerical features: Visualized as line graphs, each line represents the average prediction from the model (across all samples in the provided dataset) when the selected feature is replaced with the given value. The x-axis shows the selected feature values and the y-axis shows the predicted target (e.g., the prediction probability for classification tasks and the raw predicted values for regression tasks). - - Categorical features: Visualized as vertical bar charts. Each bar represents the average prediction from the model (across all samples in the provided dataset) when the selected feature is replaced with the given value. The x-axis shows the different values for the selected feature and the y-axis shows the predicted target (e.g., the prediction probability for classification tasks and the raw predicted values for regression tasks). - -* Two-feature - - - Visualized as a heat map. The x and y-axis both show the selected feature values. The heat map color represents the average - prediction from the model (across all samples in the provided dataset) when the selected features are replaced with the corresponding values. - -ICE ---- - -* Continuous or discrete numerical features: Visualized as line graphs. While PDP shows the average prediction across all samples in the provided dataset, ICE plots every sample from the provided dataset (when the selected feature is replaced with the given value) separately. The x-axis shows the selected feature values and the y-axis shows the predicted target (for example, the prediction probability for classification tasks and the raw predicted values for regression tasks). The median value can be plotted to highlight the trend. The ICE plots can also be centered around the first prediction from the feature distribution (for example, each prediction subtracts the predicted value from the first sample). -* Categorical features: Visualized as violin plots. The x-axis shows the different values for the selected feature and the y-axis shows the predicted target (for example, the prediction probability for classification tasks and the raw predicted values for regression tasks). - -Both PDP and ICE visualizations display the feature value distribution from the training dataset on the corresponding axis. For example, the one-feature line graphs, bar charts, and violin plots show the feature value distribution on the x-axis. The heat map shows the feature value distributions on the respective x-axis or y-axis. - -Examples -======== - -The following example generates and visualizes global partial dependence plot (PDP) and Individual Conditional Expectation (ICE) explanations on the `Titanic dataset `_. The model is constructed using the ADS ``OracleAutoMLProvider`` (selected model: XGBClassifier), however, the ADS model explainers work with any model (classifier or regressor) that is -wrapped in an ADSModel object. - -.. code-block:: python3 - - from ads.dataset.factory import DatasetFactory - from os import path - import requests - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - import logging - from ads.automl.provider import OracleAutoMLProvider - from ads.automl.driver import AutoML - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct - # global and local explanation objects. The ADSExplainer takes - # as input the model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - global_explainer = explainer.global_explanation( - provider=MLXGlobalExplainer()) - - # A summary of the global partial feature dependence explanation - # algorithm and how to interpret the output can be displayed with - global_explainer.partial_dependence_summary() - - # Compute the 1-feature PDP on the categorical feature, "sex", - # and numerical feature, "age" - pdp_sex = global_explainer.compute_partial_dependence("sex") - pdp_age = global_explainer.compute_partial_dependence( - "age", partial_range=(0, 1)) - - # ADS supports PDP visualizations for both 1-feature and 2-feature - # Feature Dependence explanations, and ICE visualizations for 1-feature - # Feature Dependence explanations (see "Interpretation" above) - - # Visualize the categorical feature PDP for the True (Survived) label - pdp_sex.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_pdp_sex.png - -.. code-block:: python3 - - # Visualize the numerical feature PDP for the True (Survived) label - pdp_age.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_pdp_age.png - -.. code-block:: python3 - - # Compute the 2-feature PDP on the categorical feature, "pclass", and - # numerical feature, "age" - pdp_pclass_age = global_explainer.compute_partial_dependence( - ['pclass', 'age'], partial_range=(0, 1)) - pdp_pclass_age.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_pdp_pclass_age.png - -.. code-block:: python3 - - # Visualize the ICE plot for the categorical feature, "sex" - pdp_sex.show_in_notebook(mode='ice', labels=True) - -.. image:: figures/ads_mlx_titanic_ice_sex.png - -.. code-block:: python3 - - # Visualize the ICE plot for the numerical feature, "age", and center - # around the first prediction (smallest age) - pdp_age.show_in_notebook(mode='ice', labels=True, centered=True) - -.. image:: figures/ads_mlx_titanic_ice_age.png - -.. code-block:: python3 - - # The raw explanation data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - pdp_age.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_pdp_age_diagnostics.png - -.. code-block:: python3 - - # The explanation can also be returned as Pandas.DataFrame with - pdp_age.as_dataframe() - -.. image:: figures/ads_mlx_titanic_pdp_age_dataframe.png - - -References -========== - -- `Partial Dependence Plot `_ -- `Vanderbilt Biostatistics - titanic data `_ - diff --git a/docs/source/user_guide/model_explainability/feature_importance.rst b/docs/source/user_guide/model_explainability/feature_importance.rst deleted file mode 100644 index d6fe4f5b7..000000000 --- a/docs/source/user_guide/model_explainability/feature_importance.rst +++ /dev/null @@ -1,137 +0,0 @@ -Feature Importance Explanations -******************************* - -Overview -========= - -Feature permutation importance is a model-agnostic global explanation method that provides insights into a machine learning model's behavior. It estimates and ranks feature importance based on the impact each feature has on the trained machine learning model's predictions. - -Description -=========== - -Feature permutation importance measures the predictive value of a feature for any black box estimator, classifier, or regressor. It does this by evaluating how the prediction error increases when a feature is not available. Any scoring metric can be used to measure the prediction error. For example, :math:`F_1` for classification or R\ :sup:`2` \ for regression. To avoid actually removing features and retraining the estimator for each feature, the algorithm randomly shuffles the feature values effectively adding noise to the feature. Then, the prediction error of the new dataset is compared with the prediction error of the original dataset. If the model heavily relies on the column being shuffled to accurately predict the target variable, this random re-ordering causes less accurate predictions. If the model does not rely on the feature for its predictions, the prediction error remains unchanged. - -The following summarizes the main steps in computing feature permutation importance explanations: - -* Start with a trained machine learning model. -* Calculate the baseline prediction error on the given dataset. For example, train dataset or test dataset. -* For each feature: - - 1. Randomly shuffle the feature column in the given dataset. - 2. Calculate the prediction error on the shuffled dataset. - 3. Store the difference between the baseline score and the shuffled dataset score as the feature importance. For example, baseline score - shuffled score. - -* Repeat the preceding three steps multiple times then report the average. Averaging mitigates the effects of random shuffling. -* Rank the features based on the average impact each feature has on the model's score. Features that have a larger impact on the score when shuffled are assigned higher importance than features with minimal impact on the model's score. -* In some cases, randomly permuting an unimportant feature can actually have a positive effect on the model's prediction so the feature's contribution to the model's predictions is effectively noise. In the feature permutation importance visualizations, ADS caps any negative feature importance values at zero. - -Interpretation -============== - -Feature permutation importance explanations generate an ordered list of features along with their importance values. Interpreting the output of this algorithm is straightforward. Features located at higher ranks have more impact on the model predictions. Features at lower ranks have less impact on the model predictions. Additionally, the importance values represent the relative importance of features. - -The output supports three types of visualizations. They are all based on the same data but present the data differently for various use cases: - -* **Bar chart** (``'bar'``): The bar chart shows the model's view of the relative feature importance. The x-axis highlights feature importance. A longer bar indicates higher importance than a shorter bar. Each bar also shows the average feature importance value along with the standard deviation of importance values across all iterations of the algorithm (mean importance +/- standard deviation*). Negative importance values are capped at zero. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. -* **Box plot** (``'box_plot'``): The detailed box plot shows the feature importance values across the iterations of the algorithm. These values are used to compute the average feature importance and the corresponding standard deviations shown in the bar chart. The x-axis shows the impact that permuting a given feature had on the model's prediction score. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. The minimum, first quartile, median, third quartile, and a maximum of the feature importance values across different iterations of the algorithm are shown by each box. -* **Detailed scatter plot** (``'detailed'``): The detailed bar chart shows the feature importance values for each iteration of the algorithm. These values are used to compute the average feature importance values and the corresponding standard deviations shown in the bar chart. The x-axis shows the impact that permuting a given feature had on the model's prediction score. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. The color of each dot in the graph indicates the quality of the permutation for this iteration, which is computed by measuring the correlation of the permuted feature column relative to the original feature colum. For example, how different is the permuted feature column versus the original feature column. - -Examples -======== - -This example generates and visualizes a global feature permutation importance explanation on the `Titanic dataset `_. The model is constructed using the ADS ``OracleAutoMLProvider``. However, the ADS model explainers work with any model (classifier or regressor) that is wrapped in an ``ADSModel`` object. - -.. code-block:: python3 - - import logging - import requests - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.dataset.factory import DatasetFactory - from os import path - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct global - # and local explanation objects. The ADSExplainer takes as input the - # model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - global_explainer = explainer.global_explanation( - provider=MLXGlobalExplainer()) - - # A summary of the global feature permutation importance algorithm and - # how to interpret the output can be displayed with - global_explainer.feature_importance_summary() - - # Compute the global Feature Permutation Importance explanation - importances = global_explainer.compute_feature_importance() - - # ADS supports multiple visualizations for the global Feature - # Permutation Importance explanations (see "Interpretation" above) - - # Simple bar chart highlighting the average impact on model score - # across multiple iterations of the algorithm - importances.show_in_notebook() - -.. image:: figures/ads_mlx_titanic_pi_bar.png - -.. code-block:: python3 - - # Box plot highlighting the mean, median, quartiles, and min/max - # impact on model score across multiple iterations of the algorithm - importances.show_in_notebook('box_plot') - -.. image:: figures/ads_mlx_titanic_pi_box.png - -.. code-block:: python3 - - # Detailed scatter plot highlighting the individual impacts on - # model score across multiple iterations of the algorithm - importances.show_in_notebook('detailed') - -.. image:: figures/ads_mlx_titanic_pi_scatter.png - -.. code-block:: python3 - - # The raw explanaiton data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - importances.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_pi_diagnostics.png - -References -========== - -* `Feature importance `_ -* `Perutation importance `_ -* `Vanderbilt Biostatistics - titanic data `_ - diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_ale.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_ale.png deleted file mode 100644 index b4ef95dfdce4c069456061b451a9087a3fa4eb8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186543 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

tl$){&1#s&;o-zY) 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

vVYcJnuV69_u$*cct&P!;%;qd(fe<*YlGICx= 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 LI!`K;pS}4%D}Nod diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_ale_pdp_x1.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_ale_pdp_x1.png deleted file mode 100644 index 6e27c5c7fefc78415727ee72668f0c3ad24d86ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20643 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 L^YJVu2Pgj*?g6R~ diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_1.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_1.png deleted file mode 100644 index ceeec8226c7f0009b5fa3c903a53109e9bcae858..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25013 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 diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_2.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_boston_whatif_explore_predictions_2.png deleted file mode 100644 index b7bef7c20e3adcabc62aaa86ed6ab54888c540ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29202 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

XZ+?_Iu;7s~Zl3}oemo?7FL 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 diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_ice_age.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_ice_age.png deleted file mode 100644 index 5053ae99e6e5fd0e0f5de61be17c9eb7982ca8b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109311 zcmeFZWn5HmyEaTSh_nLINa>%HM!LI10YN}Onjr;-25IRKkd{WIhaS4SySp2PhIfto zx%a;B_rvq)eZOr!%!2h>T3<(Lj6@-B} z<3?tSghYcRFD3pSqQBpO2?c*}dTcs$$17`ZC&pH%{ceOT_5C>k+OucIEx7-fb6bvU zt36}El4>qSeopWG?d40&uiE(P(MHJV#w5?O@>>X>*Pr%!dsKNG+BROmMZoE2i=JBp z!kv^`^A%e6#hgu6#kHF{+7+e)NinJ~qd@d1A!7eJaO8nl=vPw5;s-1l{_S)Wh#N$| zG7$0qx_AmWnYy<*(EFd)0#}Os@0I^8(*K{Ol#GkQtX0g?7td<-a4L^R=qp7-Acyu6 zMIKTi3aGXjLwdQi4VT&Ac6Lgq&#s#`C~m=duIOUH*6S?UVN{;!2fO!m-v+b6wLgNJ>vQ_I;VH46UtvF->i zT@Rj4cZoQsx|pRpX}X@qrhoFfYNN(2T3MrhygG$fH#J~g-Yz{hemZiSA}8xY!t*-z z>kAS&{}tV)O1E5)m!^X&ble;g<2JuWr4^k1e=NCq7GbDV(STKxKl2^(f>&&iG+jvx z+@Fv22`-*Q1a-QMhrI{^)T%o+naCYCDj`vxmQUP`%Ws;cnf2P&HR9bWxzHS90ZX5^BI= z>pnL(*+UejjdVPJyLidw(y*sDg;g=8BKX4(_pNzDJRg@LLPejA#b~gOI)@cLc1nFF z5?lUI;`Tw{d{{C$>0zBo^fh0@j`CE&1z*$kfJ2_(Lc#U@%~X?@)vEPbveUEi!i)64**%ypt8Z87V(%yv3C|iuArS7C3;8^|oMA&-;fm;+!c#n$A*> z;uP6xvsr+jS(e8YJa2RB)RLOj?+#hy3;8Xlz04(_xS3p+QK_db55-e#y>I6>>ZTfO zj7b*YVx48eGW9Aq)%6Ppy{ShYTjh@rw+VZnw%*RP5g`n9dKpmH!m@G{x*Q-Dbwo+&!)-l`T?ZZ0#+uLe3?A$5Zkrz6D>8!du!AE#_l?mr`y6+2 z5_qL5z4XsBNq%TGp8g2$q#SVf&@9o{ix7rScffXQs+;Z)$dgx7T^3#Eb9;~f+NIKS zKqMLgTtogepqdM1nY+yOmc1ri)LlG{FABQN6^Eoh-fbmJH5@wOjMwj% zE4l(^s&4~nBj?rUix51l*>nl!Br{V|`!DDFEJ+*X?;=T?e$5CtpAP6L9gN*`N0D5a zVaOywugZMrEpAw40!H_82Ij#Q7E=m`3NaV9U<8is`0=}phRelzp_33*Ywn_{y2;Ak zNU!s(^1b4E_~^0Gu3bX?d`s?SN(u zoFkFjeGQR^lSn?-KRT6`ndB2MS2l);>D`7aCNy=OsK$S7jIGQj*wcO@YRkKv`_pt|msjn5Ok-zmCiZJ_Y49(ZsuqTw|9YrRA?fJ9M-ITe7v zl&F0T{V!{WJZ*frPdy%Qm)NTtFN(U@+F8I5Z+Jy|W!<}qsPQ9oc1X*#32*DWmoP{Z z>C`QXmZ)rOP>64L@P8@aM~rY0ORVn|@E32WTS)+`i@K;W<~;S$)xMTzaILkAbti&-k-e<1dx&dmA|{(lpZX$T58?i4=yL2b0rZ0`BhS zSj4t-f1W1_UacZn3=&$W4b{SRd49Ud*W_AzyKp1|#oeQ8W>F$rUEyL2glq{e&5Vp`2YhCXEJ6X};?IKB}9!fGE^+DX_qTjSX{tH2>Zznw0p!iV;e4wE0vitrLSBmeGH zhd{11nUW3=Iqxf!`hzN6fg{)%kTPd2^8Thls|LT%ln&jr1GM?-X>;?Nyy)fK6|Zkr zC^uWft^CTyDB;jr;8WCXvw{+|az6mzEjz+GP;C-(p; z<-beyC=d${AOf~<=k9m^E|~unJYXmPD|o5T{}+q59dd>YSm|q2&DqqS`d|uWm?^yQ zOvAqd>fBGojW@dvK-p@W_WOs^PDpbSk5%snExivmc7EjNO0Tnq$Wef@)V9~D*fF)- zsN=h9q0VF;CW<#u74^2ZO4PzDOBU#UggpUF*&Xgj4--aC8QTUR0MyW3lNrla6={p# zjc`%GW}fPUV(T0#>>o?Q#WZckl>@Bh6dBc4!91L~3LW5oQTV$?Vvh&y*Wmd&go{+m z3@fQrknA{rmpaIxq9w3k4nv-9Bt2}b!!hlhVymEPkw8No;Nw*B1uF+3CcvUdgBIuX zFvr5P9Y;4O`Bp(;mMzvC zdf3@y=2(F$;o{D++&_O8v_xzv53wbSHuxXSzw6s0)}OBt{vGdMjR`40>=5{1i$9A4 zw(M#-urtAcb#z{uQXL1xD&0_(JP~i| z|Gr^bw9CHzo}}cB16$OvQYm2rE($UMp4RB+U!jo!LQCPb95vtrg-;MEianl~uRRHX#NIFj=9k8Dk`&fDyCZ5UTlM_R)_vKU2zEP2s;V4W!Xw|x4B-%6rKLPBaFbO@wzr2 z4;>Qf0DW#qWYJuX@H*g#bV}=$*An^jJrqc&&~U z0Q13F$t_&*oE=NktXg#2lWu)Nc)MSr>bl95{t-TB+obY^;QjQW{?`%djW`XJ*WS0) z-gEvP+$aObY3_#t4Vpc1yl!ZkMLHi4*bZPQD#9DtKKky^HVd^H<%`_CZJ^-H34e}! z)A9W|b8opxAMfNpUuw;KkiPR$-E;zF@xqJs(jEF*c}s|Zo%7X75PKCTnf}9)=jDQ+ z?gQ{dtkeJq=`A$4-)(K-Nk>3QlR0oTD^Occ<&IgE=SmXz9G`am>h`E3Xch_%NeS@v zz7y?t?ClJDO_q!Rwd9V^%lEm&mJv9b%K*?mat*nCjFy3?lZM-7*QZyPR^7@G$3|_p z02I!n@V-02aR9))YOgOQ)wD%hX#C+{8el8MvDXTqYn=8y#uO!+aQREh#$(*^b1dtHp7J@4$G()>>0YW6)^dG4;PJfTB%t;@AK55&X|OsIot;E z29GyWwxFv&Y04qT@3iDn&bBH`JP$hK{DwRxrD*_z{>}>+v`UGAwS&Lbc4>k{+t*Ot zd802lYUKO>zB&=%If7Xzldm5Mx8Z&_CYd2Kg3{G4GjS+U&R`nF8GOV7jY)p8a(`g! zJr|`%t@OTmT6Q&g{qpf*k{(8n#pr@q$sCW{Jvv0~ z;P#E`>tn0`Lo3e*1QA_j5cD>T9x{p9_ex+RD@AC%%Y-NDv~dVVpij(<&(qC}M=JhR z_JTxVPSN89m!%g&6ZAHB_4{iq+ochn^pjXX5qh!5@PEr@hk+f>X}(-OJ{&zJ`B@<- zFLDX2THm`2y$ubpVJcy^C2>`HTnn~IHxXyhWK5O6&#xWIAKgw9Uiu(*Oc1R($e2p$ z9Zj9veDH;A=uqZL$-{05Mg&~Wi(y!5`hB-8kT9QP{GZ%U3#89~t1{*mOO+OzZYD~| zlxb%5x(|S8GKS9xGNX@!#ol!eL9Q%j7ciB%Fh6Dyzj)b7%R(h5Kr*FtW4-TR4TmA& zLSIN#Px_1vvY2z@tq6nrmpHfxK$T$qScA&@v)pUtR>7V8q$3Ez${K+@`9GP|*1hfF zG@G}i2P zFyYwu@Sn#LHz!}zspSgo#h7tNI#}AJQ4$FZV$n(Xqm~S@gOvmB7*|p^gVWNX2*YRn zPkB3$6KDDZezf@%VtG~HbX$LV>HT<#gB6H>F_8h^QHlB}S5sAgh1ob!WK)?)zY)}5 z8y>k=Za8=Db1VLJBCsM6S#m(?e7X5n&Wne{_e#PClNCbWbe>SN zO&bxk5MRET0MDbINQblDh^8q*z7|Zu1-4V|_c%N|WjYt^5Lk=~j*yH1xHS~xsrcJ) zR8HZ-_z`ymdeb{TZwLjmd?6V{kryw-mKSf6Zfcqtu;C>lUr|yEgehGt!io20;f$j83;LUFMdcswb zu$a$nl)30~Lg!(rj*uSfW#E)JX>)th*7%ZJkF}=>SS?5zoYim^@ z&jTJzk{hTjS@Pr|O_vQvKM))*0WpF+CZ`Slysq(>Jw{I>T$nb3iFm+?MyCR#z5x8O zS*poPHhR8|I<5YtxEH42HbFM~H2?Rif{_-`nHweJ zcdS=5gvz3(hd%?%k}g69-gN`GV@NZRctsi{;f(ioJ3}U8X`PYpwM%WEUaud}=Wv*t zTdW!>=9rOyIBln`SDUR3aNN-QlMuG}nR-i#0PM8c0nabhpETw8ul?UzNJ0>KRA#OI zY*3I-N8HQBJ*5<1q=bl(LMl}CM%dpzCJ23jt!xskJ^FPgiceiQf>y19Hm>m!R~CfZ@;sYs_>Uhy%RHL6 z0Fl@GD-m$LQoVbP~@K*$65^#je*ZJ;iaUo)wR&{F+P(`t97sUvTs3`Of|2 zQWGt^NQ^#*|MYJhO{*ZJ?b8v#e4%z@!ya*NIcM@gzg0;$;jfsy*<8i09i@_1u*kz2 z%Y%|!$}zIDEGoHBCrrBCp_e&uMJ4iznvv%}>)*HAD;>{Fda-;PM>^YCVO=GpI#p~6 z(IPpE5P5mMkSH%sxTtOxn?uP)xNhI=KvHYAsEBj)brz^%RVnhsA>PM+)PEaN8`-fc zmfXT1^@qe`S{R6+y7N`B4ppm|{|K+Re>DJp13?IaeE(x%*dmFqJyyx)Q!vxx4iTvB zLK;F#0$96Z3x`r2)HRBCH;9-3OxeDf8Omk}_~T*SUhy71E7iFzM@S?ZMB8=ulI0a! z|i!QkG!wgXIOx2ki#ho87`!J#S|Px2qLZO_ZopD1D6xnp};XPvsIL+j>G(eh<5= zIehpsCQAKAk}|OTjn}T~rrqOe{gt4TJ#Ch-1(f*%9p%>JRciS(-rb+EpPi53>+x%q zZ+e{;vNT{ICvgFwvAvvqMVi!O1>4{iCeR32U5PZm48RxDJ|@#h$#Z$0{y`=% z+2gip*hwo&K$VG2Xo8Hk=0ebf>Fe_>xo>mO=6=r+;>1VVMoaH3nR(`4I|f;Gk$aNb z7LiHN$Wf7YS_5K#zx`xcd8-2d7eNrbm&0rL=jy?b{WYf-3IA9G9lPZ_6+n+Vbs~WH zuUm|_Vv>2=tJV09>MXGxcT~K70G+W*DWkP;6~WRCeOu}-vW}31B~0aZSRUmSd57kEM+^hk zFtdNn0?ewV=ip1qeWa(jczylxd(Zh7B{9Cw`{Y zi|)ck@E}B=Ff%0Fxo%O0B!s{bm+T*9viY2-JB82J-f_%+R$RfntkpW7PcM)*^Ldwe zJ{E2Dh_^dYXjvP*zD_lLMJEx>H`Qr3=XnMFu3r*|%O?^$lynk1gu; zgBGZ!;m@!g=ajz$EgFGj8wP=Cjw{f&1!tHRGiC4byd}+;$Sn3NuB7Cisj^U|@s9!# z6Zq{56UX}IH*v?X>(}e2nQ-cj=+v2Ozo~Rx zr=zaTzX-Rd!`gJQ%T|rpTXG@=lE-e{0PkEJqC-#QZa^BZMZQwBwms9#>|uq{&!4M0 z%=lRZtL69E7j?6=5mj21v1C=5JeBnrGrwKDU^{%e{;N_xDqSyy*Foer zMWQKx=kTPiZK}V^6XjHZ{|b&J)ZYhDpGF1!aiaO6rjE~Me#31WJoind)YAD{lq!Wr zkLlBDZHFABnzqbQ616M45Sv=wF{Sx=c@VW1#1-hNP7yWWP%rf1%g}b0Vp*RO9(Z_K&E={)!o#*Hkp%)i4!DaWI0VJMuHcPq~!HTfwrF)}KZwMF( zqaqNULL7^mAn&W7&E;u}yi`pDZ6ZyS#v7i>ReM18Q~P$@+;xP~HY! zRRl(r2eG%G0z)EFwa2a4x{hY6V8Od)sj$EU1me#!@$-N6)akRTkbf8-HtipL&L_V4 z*F`+fk|bYc)4T;yfL?fyu#N+r_L5V8?5JaMBj>Ht?a>c-J1|N!eDd*$tF+)E!=IUV zm|9@$mgM8R$N}c6TzviBUL%wDx+9HF(NUl0!r9G(-G#(tMbC!`bR@d3N_AID=cL;d z=P^aET9m21SBcWK4_$}cZvz={8)Ka9tPB(c|6FBlm$1))OfT zBb<{|e>vPhTgJN&QgyD_0M)=LJv;r^<{WrOH5&|Ix{>4FT zD>-LWuIteoq7Q4LZ&S-7b?C85r_@hH{hL{3y<|UgtQ3BT<}R=ZY3+3xF*guLdo6|i zq$lvM`~#l-k~TGmy7AlKC>0&i*+nc9z{ZT{e zs>!N4Hmf5{$Cv~0qW2*l_SAt|sB%xT{Y6?`RL-md-)xgv@A~i&%AFk6A1x+!rg%SG z4P#k&vlOUvkPXumd9zf#(wI-RY>QQDSmO^q8}Cy~9Dr zlNP&_hSL&Rg)C4yCu_P$%ZZ7aike?XBe<|EDrnP}7!gS~|13|-Ox=W*tC2mlr5Aiu z-6n%0Dv1xoeFZ<`9p~x6jpM68tI(3QoVyobk=+iW5G?l^p?+{bFmf<9&EPByqJLC``|1N~}ZBxu>n6o^RU8IAuM#?U8OR%;NzRmZ#@E8au4OEgZIe zC%I&I@<)%w&|FK%?O5RVj_-!qFG`-Kb=IG`B^HDD!bVago^{sWsc=nw2mXu!WUiGz z-{3idWLZGeb`BJ+p}a>AmrdSu@9U{RxH>#&L6{whAOddCC;unepiGX#s@y6Is&907 zJ(357gtY$4;#cksd3>Mb-OAcLj@*~#h*~bF^&FO*3TL<#=wFD%;_8ESzSx)9JLR+aSyFf{NZhb9}OIJF-41`wEX$^(lJ;i6R@Fi)=a!NgSTPePYrNS26&q2%> zb)YkbJ_iXKSHNS1p2WZDf5!lWG973bNFl>nX7`5(H9(~{1BtQb)x64Om{USR*aB4R z0X|>pt}jY+F~2ShybN=0(w9RXOcFY-ZS42PxIpgRt_``Gfs>!XE-Lmb6-+L-5h#(B{<`8(F;>R)P3AM*@3o6!29d0=_=#q5oFOCzzSrIP{NQSVnuE1h@ z2U_sw5ynB67|?G$cameY3jf3!5LW=`TKpR~y3dtZAa4~WVPrG%(Z_4vur!?E<*Efz zcoy}2K5b0YtJxz2?+K!5jHa!Jp4Ch(sZ_AiyiHpUx^iglLsevJl&Q?2rv!0{r0>Wg zs_8y}q`b++YXnM3D{E;y;q&3#2O(9j(^zsr15b59#;=|m+KHx~1+mU~(+1waD1^n$ zTQJs(Tw$7qJ7vqw(s57;6<=lohO;m!2~@Oc45LmG&2|&(-nE)QuakJ?dJNm&%Wk1| z)V4T54qBP@C4qMeW2)kbrPuX(gMXrcqVBpy)RH}@(2(vY<)o?uut@Z>3eaD& zdVmlk5L7yiIfO{aVT_6zksp63wqmP)`%LG*#_$Ye2l^58R21-Uo^?W;fAq0==|`Ru zvA1X7dTcp7>nCJnNHzzLfF$$NfF=>2bE`|;SZWe;r3K#2{8$ab)UGp#0<8(H=-Oe ziHhgl&h|KZ>A07+a8n1wv%o5Mx-9kpK+RRsO2=U%ex zzmM~PUqh|iK>M$pNQq5JSEz!N@R*Nk$gYZEK@%aD@(@{Z3n&M0Xe;0vf8|fYIuWo`GEulX8lpphqjf3oh456lBJqnbLxvyIY{vSdUrs4 zh428LNy(m6gCK=8UJdNW4x7fO(C}7#J!V^xuGurBN#cPJph{5dew3p+Dhq*ERnE|H z@?*}vz=86mitE!Hfs5N@UNbu}D0gG#3gh>i3GaQ}png1|o|^UARNMM!mO4S-Q{y^O zs9y;Q&Cn;;u2DMkRf}fS;oUEIW4IhEf`YOCWjG=Nlb9Wvz$lkR#kj8sq1CW#$RaNl z(H^Ec*BA}>x?@Yi@{0YSM6qTTU1$73;XLt(-quZQg>u%tH8z)@a|=SlHrb;1bsSCB zLHU}#iLHO)&aGRsep$|I&Y4bz_D}RHnRle}O7z8q?DFmj@!H?48(PAyYl>39CZs=< zA{5Z*9@^yK+)GnVVr(<>_ zmh3%g-&gRu=XGAnI~C!J@xZGzubUm5pQGq$p;0cdHOdWtA{F!UX6j?0Yd84`_f27t zy~3lSbCN&2{8iF(LL}ayhE}V;wUU$2y7&o`x96%E+X+O$kS>cg z!}SU_)u+@=crPc)gxa_}0x5Zf-;lv)a(+_17hS|@Gt)T=s+@U6b{?Dx8BRZ24Kdt~ zfQw)qne#gZlF2}Txv}X@e3d!)q@q^QKkRx9b!J!(gp?JS$~5CBsthXxge|IjHB1$< za?a~C>~15Wy}v^qHQr^gaaij+)vb!bM+ z60~i=!5T-wopguWCONPnTyFB-@l%X`S4r10|9YirLk}j-QIW0Z-p^FN88vVt?WrX@D3zHl{0X8e6WlarXv6rVnAER zvk=n=&co>qEG)qkT1M7rTK`OT;2Rim+8N5oD4*agQ4z%SWdCcyC4nr(NE9W9Qy(tu z<#o%`;5VBq-G3*zRvix&;tiJJ8KdQBZm#;JjxM{WkpW~-0j z+5O_nikG`<@QaJ*jmiZRGB9J;_KKpgn6I6LX-1|_&2FA670*tAzF@`kIuLs-#o##s zib$Wr>!FVgb$4@sV$S|CIEOu!S$}lxIo;Ts-m%;@L=>@>u-YhHD&8?iX?d zuKIXAx|y87uD_>)L+}6x^26>AW^!bGTiS@9cNj0io;2&ZEL3{zYNYS7l#963v-w1g zhwGU6ziP$$MRxDoc@^_Q&um8zDN{0qYmh&y%y|7>5I_lg@3*OD#aG($^Q?K^KX=zY z2Kw)Mj>7~JXF&T4<^fELn;#>g5-YD~vDOALU3>H>>W_X>4D(rzZz5?t4)83TB%?OM z68Bq|*c^vTK=I#3iE~Fno7y8D&_gy<37^Pkd%xpr1yYy1&83F7aFJRRZ&8scK}@w= zR2C(>XW`xpsonEdG3p7KDfHl=j~9zH1)2RI{mwbWxO=A+w*QO=%m?Mx^jIQP5i&a< zfN|>-hurQ+`>J*;)~-%5TQviH(RC{|Sp=0}EAnn$IvQ=t-;ZNi!7WWbzY@wLU({pV zUc&gBC;DKH)*@0~iR@-mu<$CD&}Pl4qohyp32lOqGXY$uP*fo)-k5acAQb3EHi90S zt`J~vMjG;Xo&H#x0$B|SEwHUMRTglX`0Y?lTy%&>Lfq@ym%ztc`&5YWo~;NEOTQ^D z@b0k6!?m_+cfus^2Xo&A<1q1~+f2Db51_bEz9rZv=#1$`MWI##wJL-ZgwucHMPnR= z8Uj+k9|E3H-$oFVzSQ~sInh6y<=Ar3krc^aU0iopYo)WalT5Gn5c`Ha@{$tUjA zX%k_f{iqKUP|{tx2shU9GM|UF)|F^s&lig;Q33QDUy`~KKh2vOddXX?0)b^8)TB%) z-7W1ZL?>ti7++PBOgn^h2^Mc$WU`x{qeJ&u#2~}Z-r=6Un^U|M3v3Ax!K@8UlDPq0 z#3AkBNDrZBV?`T##0D;+pVuFtV@G)3;3v)`z@Ev7Rz9(nKd&L>Af{Q1f`I;cv?^C~ zcl|Yw|LKllEp1TIj@~!STS;_y_%!uvi`*GntGJERo6T^`Y{h#H-{$rpj<8rrTT)ed zwmenTGGdO$-ae@7DG5e<2t8!9?Z?`xIi#}wq=Io8YZ4@iek2{LD*x zI%&Hyx=y{1^L4}-;ZPaDo<_0+X)fb@S?XxEp_}kaL|HorV%PmeVQOy#>D`^!_))0& zqsg*o?@_nv4@J3$YYUn$mSCTesXL~)Bf3Gs|)L*y8Ywzq)uKy($3NRwfhL?{%f{HV&JY}B z!fz#uF|z#M*wb%7U@oU%^l#glC*Owc$jPo<7QC3mBP<9LmL~KO6V6uqFI>lcBG8+| zP~C$;$`qs*CsfE5cl~A}8WC@I^G!dcc%1fZq&N<~MhqiW;QRL5qb57|<16+U{@Db^ zd=EF9b&FiX4C`>i!cjk}8F6a19~O4i=;vZyenRaN!%vbvz^f#J zd|<@*A}cS~iq#QkrFuJfour3tSX81eI4oV7>v%gPyHPbs>o*SAe`;$nd}tK6&1 z4ZPne2$*+#KkW$!F#Qm@Z14Vww(Al9pGqn{9fQ>@Rw?am3hK~Va_>Fe4dMg?eH%aT zsilW1mUJ=fuuIH+Zvjx(k~B8gh2aC3jp`^&+dDd317yZk8G6iWSaOgy45Y3#H9z!Z7C;b*k62mal^Wi<*D6m z{JRoh#d+b=P*y3m+a(BVT@qNCZ=_2n}y>tfd)}$ z_97nS2-ahS)`7gm#NR)=#gPx6g{GciIdXc>^6EMdh>?m~J~GVDau=3Or6y&EKK#b6 zbc-IX>M)3`uac%Zxm(}@ojl5I@0g9mN`bSiwe2ESb{{>>JV-3cQjI?DRuG&#l6IYT z1c=R{MDd?Ly<~{Wq;Y^_0T@uAEOW{DWq&IqQ-H;e64NFZY0yvy zvzhpP%E!8s-Uy^sVx5HnUaRhSqS)AIqUu_g=x?zy)4Jn>qYl*h^Vb_uE=T%uxsw&(6jYS3ykN+r(zjNE+7j#j~DCQZV>< zP5cVM;o7iWE=PYkp)^{}?ppUfHahifH}A~g`&R6hW&aISdbRAEVJO%yk)_N<0hAHB zi)81@3Z$N~qj1oTxnM|CnD=;(qSPy37)74yi!84Zi2*YyN-B4QQ|JTw%n08BzlFrp zZNEg@;h;B^%dJ>Y*wR%eA6a=e9~!YWVlpr5Cn}LVyUB`oH5wh3-TZixdLL<975Fh3 zkF4qvc=00~zF5{J5pil{+`rbrYPrQY!Z;T6iip%IO>9Xm(TaSR_AUEO#z3X)kM&vV zVN%syOFVZ6wrAp@Vwd z_jduwfUJszq}N*7+Iw2at6k_;JOSoRH|@Hn>jx14LGWzs`7l1*bZzwY$+q!k2XV_K zx2&{p$EW7~q@;g>%6Py#fQy25fDMlpJb-G@`F|ac2&W)ZKx?bi{PW7C{?Mtf3Nd&6 zuy7PVr|)VqW2A%vJNW*5WAyWB=|;RRd=^f%s5gZxE{s1a1PTMj7^};$HbzXjXZ_re zLN8B{$Xl<0w*DTsDvb~(n1%|Ui+jer7Gt4&LIz@7OY={P%@pIbBp8etg>MNm5m}ok zQ2LIKf`=X+N`0>!Zl*ljgY9vU5VOP^&TP0{9nI2F4!pp0H_s^hQ@{?RR^YH^ElG6` zttyO}n6QS$k!3h=%)z}YapZv5L>>D@A9g|8SCrL0$pu=AFsAuT-{))5Q2O_?OSnYs z*&VAd%FRBFj#Zb;O7@BwL=8g>{iGt$(Lyn2gGI3dlW68DLZj5!iS4tA`jmu8yI5>E zh?Z%UJ7XEeEOQ&6G6vPKNI|C0Qr8T>*j~~TQ*FnBDiV#5e^1V}<=))oENJ_v@vf@L zg7sUzmr144909~XUQ*-6swTMh%dm)5eW82=nr6(}Jo$iJN`m|l`NCZoUuP{%Sws?- zytDDGn(FOjzOew|67MNk_u~?zB8^eA{S5z$&~o0SKnyqpTFw;c0{fnmz6o}7`e{s7 zre{<$E%HXV^@{EA@T%V8Y7kwL#j4$H7_y_-8Z>~T2gaSGAOwcOcJsG!f$2z4pmqH! zKPA(Zl4ebl#99kjO^8sREdg=`lyhq(jdgEK^3x-MxR6(*t?otT!P42n|2=To>`Y^Yc_n%}-#36Ig6mg|0aR4eF1h$iyCO>WQfiQn zZjNT%gRs$QE?+|dtri8ZQRvS;rkbh*{&lR^FI5}QdKieHbo3xXCGSnu0a;e=9_A+= z9NDlFK6Exic}!%9SvQkX93ct}L$}+xrNyx4=)(SJFCpo-^LZm*Chw(_6_pw0-(7h3 zuo`#NO_Kbh#tW-oMnh^?d&AHFFA%av@lp7m@PCj>p z3FRC^JBmMVN{OUAA=D(U@q7_XopFmFUfS4qv3lj*sq2zA#Wo(TsiZ-Uv20+43o$>d z>4hVXs7{Kl$|jg2pC<@5%k~-s22e0w_S{ z6r0O)0l$c`Ns2px$GbCfRymLSSonAEN+anRlZoHuW9bj?ov+W*``Ca`jOB zJe+bOKE6)jw-b2t(Wan_^;#|;dY?@NeR0r=67jer%k#S8aF)H35+zs!+i7>8mEhYz zYg9l8nJGG(#hmQ8b-iGY=Rc3hB8GB)jVr|g{_k5Uc;+oXQ}UgwR_3RHqoiv1&s=(k z2FU@F^&s|%3xK^51)5$q_Cm)Eyq?F8HEJi-a_>53mMp=!*z_bH<5HJWP`RbQhf0;c zQxw{z*u8S{=mKAhFBuD+S}F#WC(GP7g!^~d6R$~UE=mtFl*UH6tc}l_$s3Q$gu0CD z$byeEocCX@M>khTUnJ#6X3l><7@BWpO(|HRbb9|a2zW6DO>!SA{VZjR+c`kU+VNC$ z6B|feM|60Cf2?ODoJA{2)zY8#l#Z)P^A0^d_a{ds2h1rUHz0o`rK%zfAo@Iso zmXMfaiKYS1pb_Q)L5;dDu9NUDZEMes;-uR@|M`9SJDC=4?JpC0im5b- z<2tHpMq1K4f~}>8gC(+7Y#H_DXc>B_QxZ($b0;br=*!~7oQq1L(_57ky*WF*CfQ%_ zfJJwHoh`LYg)~d$LXO={HS4BhgNs2?_h$yyy4rR@4KNd2? z0WU1yUypXtu2W&qv256f&Ne;|AxkVUJb(Fgx5Fs%4T^vX?dGq4s((%p&gI7FRMuBv zd-`4XqT0+d)_+jZ9f4cgO<2wmm$4^1j~l(CdPvyTAldCtH%0O&r;)UZ{D=XT)iuIs z$us#u(WyEcaH`>bI%*ua;W8!NYI#d85<^>77!tSP_u+(_HXV%icPNVgIx`dbLVu%@(|5HE*o%G^ahu-?(>m@~ADd6%_md%uKme zmUPv%K)&^4%k51#c06y9V+!VMs{$;Qj(#gTimP}=K)E&9zgiRp^<4&0CbGbOXF+oo5k;?*h#ov^!^Baeg7+N6D4Gg|I1j6h@VW0FOpc4 z>o%%kr#l|FhBc2-d2XSGF!eR)!>X-b~=>peUB&u+mO9c@am_il?+dcaw%+t>t0dA5NCpR<4vC_EA~_ z!m|?uG+p!vmHP&SsH-y=URNGi>Y4Rgl36a^55Qy3Pn&jypKZ!jZ?y4%RZWtja!ZJ-=1dqL^ijFAA5E9o0){02@vR`nFG9ISKWEg&FI5iM%5% zI?J6st62`UXG-Wcjm&1*wvDg%r)uWtie!aW39@iYo=$xevxEeF#pbTBQCrRCjU>~@ zm3`w&1#}WO&RrS`Jx*1nNY~%S2Gf!!)}g0TR6n|@>y&!JAXiQAy8L8Ywk4#QA_T+(+D!W z%J-J8nT$ngQ9XH8W9Kv~Psw{E#v|n6@~FzO@B#a-=JIf6{=mv>j@8lUqBciX66)A> zRR>Z5kZ1lLQ^aoXljjUCdN220e!k^z9M4oiWv*-*>H2M~;)upm7*SxcPCm9h`cN5Q zfjuV;&D9$eKZ{&zxYoRXs7GZhi4WrDf+CL>BgMsY_6&IUVh=bajadZHUIUUel2}u3$K) z3ymv2)>@Ouf`0}%3i)jC3OjAKi#Boj-hOr|7%)?k4Z8}5{SiD_r4=pXOgAUSpHrjF zI?`HHCq9$OP$M}Gu?P-;PmWB^21vHob!IZouj|gKP8u^jnU_nPC8h(n)+%RoI&^q- zyHwZfseSZ5i*U}YTNstm7Bm2_S2|9SKQWa$e@&uwL!B$SODoeMq= zNrCWdm&v_SeKEk52S@I7N*A51w*EgfodsJIT)4IAh8aS-8)+n^o1sIxI|l*jj-jMO zQo3In>F$(Hhfe8|F2C`d@BD{-?fvX$t#vQH_+K2!t2$-SMo4Rt%hH)4Cpq{;gE!nb zBObq{`tu^@3`eqnqL^{OGVkt6tYyr-u?)}M>qk}NrKqwiW#Q(<{Fh@d?i}+A=m8r} zD&sbVmz@ zRre#!u3wFwMz?z$2`VB!t>6i2a)?TGH@{^dP-2w49{?W%uz)5DI)nk8n;45Ke>zA+ zD;a)yPu@Rq`(Nw(N-98GW19di+tFqGi|Z={ESM$#L=;JLJMv#7$~ZO)H5=e|KuiN% zg71(HZrN_cpXbMFh(-lmBe9~f4`!)d&V6PUFp`V_nQET1_#p?VNYOEVfBD^yu+D`a{8oCz5;&$UBEzF9PDg34$qAj#J|kf4OkeYn7*iusr|d3TGlNfC znh1|MV@kpg0&pay1wcd?d^Lz$;x5dy3d}?Kk|Qz#MeNFt0QS)>&c}y&LG8=U)!hB|l3VKS1dYiq{ zbNnJV*~6#`7A7AU)ioYgXdgMcH!l9gD@RiZHb)&IOFO8$pJE;0Dkq{Ha3P5pDt{ch z9zs54K5AjlwdlSQ4S)GXBr5un$Yx8N2I! zCLWVD)X6m})$+R*pmd0GCI6&RewA(5tvm-twWs$+%hv!v2NB`+m~dUl9tL3Z=rih% zh~0E{*6HqpOHvqeLErXOoiU0yCdt>2)JP~Q0vKU*M9JdNKwEK5;I~?Qu005uH74Ag zCD^#{z6T@uyq2$@W))L=dGl=zihFJ+Dnldj_g(uNO|D$9pc-$wf2>wzcm}*c+ApyI z$jar2l<1v{6Jc73SaW6FskriQsp0EbXx+|rqfE|*UQI$sr2R_zt?!h)yg4ZAib|^& zxaQS7ybfe>6WRZK^;}uvJ6M<1+>ym!n3h&e7MV*Qtzh31Xv*3kK>1}|u;nqKF+pN% z94!jT@cdKCbNpNK6W@hHyoc;>Y%^bss9Z#&mB9fWkJsHA88XlpHkoSX*DQW>?vl@j zVtu)RIQk4&g8w}>=RD-K{~7C3^&GZy^wK}|;}9GTd*g_04@@ta+$J;2vxZ zzF@X=S*ba`36qs(@vlt$&KB3?_$MNHTr2S)xIEh8cA2<^W@uthdf}baGOxyYd3GgG z%0`bwJSbY|ZW_Bx?54c{Wy|&A8VK%~*IxQhDLZg)EABD@>F1q?-zT@Q-be|>h>C<5 zYGZOxlu;E>r{CO%`W>Wb0~mK0sr#(JduV41igYo0gA;0U0yE{Ua-~)U^BIW0(uS3^ zp8SWQ%)+^%P}Q4tM;CM~Nj|BwgHW&(eAN>paNO&~Wf8LNF^4BUhZ?kjpWyE;zYz-} zqG%L|L3+Dc0fM-11=P3`JeL=8&H{-FIXuU7#d|acsG73lrN=(6B7nbh%L_4Q3!|JG zwN6Mp-gHec?aN_0Zq9qvWm!DYSSA@3basZe&(_jRE~#t|;Mp|Qo)ae$fhEb`IztkC zaU%my7N<;FRDaQTIm|N*J1p*=;wi_TTiLUk7Sq-}Y>e#O&!N=4mwnlbPWlMHdIaW&_>Dzu73zZ`BF)^%E zd7rnDC{KDr_9vZ7(dF@zp_6~mhgi>rWQNtm`zOJg*LuZ_Gh2V(fF|felw=p0ogo^~ zl!GnSQ;$<(ykHB(Yr5RXvadM{wj1Fiu7%R(Yrrgk3$#4K*E*Q z%cixrG5m9`8c@E#h{VEG=D)!%Jdy!aUzwBJwmtX4b$?9PLVmd60cnj}0YwYLS5r zisJcB976-S%|K`;kurrdw3;6^${tUbCIQYi-;d!esLDV)m(c$@x2aD3t^;=CUfr#I znI4jsaKPGK`h50VOHwlh%8X|D>mi5TrLiWFnUMUp2;cW78SvN+jRp-;MrnrgODkoC zk(fH%L}|QOy2JcS%w5Wp-5fAF?rg0De&36cdv3o&z|X!?(SMZz!{GW$!@g+1#NQ&F z}=U1AIVNmgRSfZYYRC7>Ap!G_j zAGDl(LC@)+%mT2K4wW+QVo-sfVf1ew)}(jEAXGu24l;4rKC||pGd0o+O(K0=(Isbc zHQ)B)TfXAwo^utE+d0v@)xvJD`W3h@HNcaha~hUBR3lW#4-ZH7l+pp>2<}bi&+oa6 z_Q4dUf0HwHWlIu&4dX_CWFQ|@V=GUHWbE?6HZqb)ER)H7;gxhnWzC`oeJl!SxlIrl z7pfIxvXUo{c|X(T!S3&fq|4-m^zNGy zxpF%CS#Rn?nl50>)0G7huPvH{^I0gt%r--mYOr$etZH@Tifg{yoYF^)N?g97TrAeVVA2!wvb$h~C-u z)U@Oo#=0NeDiO#6SZ#*oF-Mi@(Y()WSGi{H=9F0V@)J3s?4zWHy-kFx?BmTK6NC_B z+SbAVMhfW0KvF85LrCH9n5VYH$VYiBhT^SFZ5%L&C35N!UCFeODwIkgCMJSvcWJ4*2S?H`vfHA zAz<7xyyvS6t*ei6g{Ow!_2Rlb2J?Nzke*8;2Qf}^;%NrPc=o4-sG;W!@(+6@0fUM* zKN4E>L|L^gaTyZ+Mm#nbpTU}_9~btNup@&?f_b)Y+s%9%8Ay!@d2jZ;N0qDVtXny2e*zd+tu}TvcM|BREK_wCJDk7F+d@OzXKPTl1r1V%w>{ zhSRSh7vKHO!UkRQnd4Crkr?ll zRezl50-Ju854gXYayp@~Qy13u zC<3Z<4;hKv;4KIPy&%*$i#=yWnhZuwP{E&`zf$H(Hx2Z$^bK~oLh>`-Q;2`(>l~uU z@%?+DTu7bjO_F*n;@GlI4Oe%-%AWyw%lqUSlhYftLJ}w$zl9}^>EWwoW8nl zvTzQ|K3CX>0mJ*`KMIx@yuc#B5e6sahU0|cH32|x)~j$V)Ka?vf0gxo%5Il*M2^0V zJLqJ{77Kr@b~nM3K&yX_^Kjs$Eo>`PKN&<3rS{l6NP+oOTb$WQ??0UWT~mk19q&#- zna%b4%3zdbnqIYs#D zb$g#B;bl+QG5l2bc2QUnZ~A-p8FU@(I-HsEbRfG<8y)LW4sy&%-?bumVZ{ERNYYM- z@Q;>>w*d+PT)~XqNcD`J^j>d|zXeEfAh-Y6i7;qIE$`sFkOW`_SY@42KD!A<`{a{3 zjWK<(kRDvKSZMx0sOzVQ!~+#-q;Vkg=o_Ne8okVUDVm^aqEYUoWF$dN{#|8aZ1N7L zHh?PBCr%$R1;gVDPh6;|s-OdfT&8<$89$cMKU|y~Cd88<_GCo*10}E9cJM)RE1DB; zvoR9+3-utIfdW+(wawN)phMRhZ1wC2joxgOU*P9|8fh}h;Bi@rccyZ?;x|`Hv2p|_ z9XKz8?9{rS$Uh(x<*(P8i0ZksD~88UMA&?Mp)!vC0Am6>u^fTX65LUv$p5NxPiS(czeZGg( z^Ly8C1a$6wQJbz6$Qn|;TA#VrU^GiYwbzz!s6PdLrufAn%4eeeu@4RzA_`^AiJPlG zKhWO+U0m{OC7Ks93T{)$Sx)KfX-%6fQU&m6n{0~Q9Y-Tk`|Zfk3Bd^{DR1s0!W*vj zoKouO7em;|Ut8@8|IYn(5cq6YyH42R)Ost0Ujs0E;(ZNo-Zr;|Tx1Y#y3n|5nTqJSpd3}$gHM7pnU zcyUkzK)HtdduM(8a}AUZ@2mZI3!-A^qIvRetlxT9E}@z!s9;b#x!|C3A7b8}>0Df- z2@5N@?RLqKlTx#&b|#&zW$;5*pF-hLnCrbfUR7ISmL_^D^z+&D5U)e5@poWE!(m^a zkfsF{s1oY^)C68>_KxkacIB4Ro2ZfNV%(pt5zg`$Yz7;aXE?8qfK##*MuuEIF%J&u z8I@zrOEvG4?Fy>l*62_ZbKEWRGYr&iw$JfRn_~v_FPh6mTdvD%{A}E|q9WnTY0W=C zsG{#2u({j5fayacc4krh{S(21j&RUVXq-6M$_C_5$Kx;vz;H!o~( zrX6*`j7~vlk;|$k%~c||Sp&`)_AcJoZgA#>8^vRPVUr(SUXhr?A+vBPC5)-dnl*$- zkEw}SxY+A0xhgMsRQ;Z5&v9%ZBO-8} z0@E#mF*BZ8vatT-z6JHdqr!fS)}d5*jIjR%5N2}&9T&!q<>8-~ zNOL4VZLD)dZ}we2P9pd5o+qzIFq{?@pF7yTAp_1(MU7efNOS4wzYPWF9EGzF-7za3 zDuo_G3uZzZmFA4>tHgyzvWSiNf2Ey!5u_Bv7jsjtM;xPPQ>CPOv%}O}Lr`5A#fMQb zX}c|3SV`#B8pGLm9*RV6h+4k&5rR)!0Y?@B)j&ouz4m7b;SM-R<9z%cHqGvK%Exv+ z_ev84Vsuvm-{d(3N`f>F3wpi?4Xp|keG4K`WMm#aay`}XCjIb1AJbzfs;i#Shja;d zdnUeu9WoHEJ$H>gl>)~Bb1^(DcM^G7h6)@%+Zi7E*7_KKmwKti0Sy*+iC{KJKV%Q_ z{4z`HO7w+D{pA9I39lw9Y;V(*PSS&$?w5yGrHYn)w}zj-qnINPpYtXKVIVoXbi^DY5kQ0gA5)39B(WY-R-uu-kr>~x2@;#3j zu%?qix?xUd_V5h5h`33K3u7)}hX$0*{)KTG2EVSxeF4%cldX*3C2E0}$v%Wr}Ymx9PqO`VLHw0h^%2TK3w{yDI8_{aS)yL@)hqv90Ouf3fd>(v$7{E7J~5vQSg@G$DLY&yAB2pE!t&(84UE>EwnPMBXo7!F0q030{xcs7{-V6%}p|p`NN~6_u~}2U|lgq zqsO<5CVTJMtOD-lNqg2O%ro=?NQUqg%^7)FhE6t!AqKD#`-0wr8fywxpyH3iXk|y#?ZgtcmozeK+RO*eN2fGhiMRwG@1JgxJUWEV83&5rH)rXVB z)qFtsxxw(zq1i$&ypfc(Zh<_CC&cn5W4nJwq^ML0yN-WpVZO4BZ@t{reFXFY=eiJk z#oU5v>@gx7&ZCF}Xe?&j=q<_zt|oc+g&3^p<2XrdUAN}X^tsC`82bzb=RIl_z%tjp zi+OmRIy$O@gM*j0jZ{XpBTDhrFB>K0T7I$v6R)WzZQVzTz3l9FA+JLb-F7@CyH|+C zN_)_-Z&svbK09E?3-@T{f^N12;K7a+WlOeTO|gLEA%qehXIAGs|A%^MVcfC=R= zphtp+QN3&(s1t<3Z_^%`_a<^6O+-&cLvE-kp1iFs?j*7WL|8r%ut6~#93#18CMF1Z z>@}QEEyu*61eNvMTcoHf{!_+!%rJAhH#Yb~F;#=wFXC1-afL|31RyI!z(8a}Rwcf< z-u2p}5aKRncI7J}$lIBE1anHbYY|Myn;3b;mJ&%-nM#A1D+Gv}L>n>j;|*NLBN>$} zmgQ1&$X7dg6=LnAuNNor%xR3aO5{}2?I1_AK1u3v&>WVq1D0Z?z$G;VRf9w_-kXKx z)gR0)hSAO;^PENyTs1?LlfyH63{OtLmZ%N>-asB|(M|!Q3r!F@&Hg;N(2g}l~_x2KBDk*qCtk%IGtw7<6u@UcQ?c7X{@&dejgzz&0Lvg? zo=CUIO= zJ15x23S?vYYq{gPpdrs^V24DtA~{o?o|?%4YhVJ6 zxZT2`Y16^4kN@o-1aXy3mUG@-`dS>9b#d+`yB2Vnh5YVQc@~32xI5ryXk6t7Ci0;* zqGmB;-u5{gpYiNP2O<+DyLtl4QFlYze^AF7CM5fz%bymds*$NG&>U6rl~T1@7A)zq z*BkhcR~_IfH~whGHpXhQk0*dOE1ia=YE*NdVHJ2x&E#fZHsWRlpvM$vtkVs@+l>wj z+>#1hP!S(4md@4;+>t-466Q+&Ff4LA-w6W+ZP(@yc4uO`B* zKlKoWS+#!BcLw;rneLQEz*cHuu(?OtW>(I1G~&z?vImQuc1L{QZjPP8hF{vpE5=|i zfxg@LU`{6iY(=Al#NoFZX51dHf)?%_)aiK>l@t)^2cjc+fs78EO+{ZUY~18ijtlBY zKXMdo@nGcXvKi{UoxxUYSUN~WlynO7XCh~M#nB7Z#$U#Ug52Spnb3Kr3)$A6muk4~ZgT`mM z+#pWUE!6!tR06n^K#5YzO|9I@H3*D>ACe*>kQ7g3B4HVpm+`0H&F;Vo>z{MSz ziY+hvWj+JA_#lG|{L1eTw5SJz+Hgj(fFr#KYmb{b=lFjb528|aTN=nd&wkdU-}3tI zrhzgHKe#eb4jZqLbgv6c28&Ev2Y`Sx5Rf6y6*WZU5Yr)jF*a1%eL#W2SOQwsYuFFR zp}_)-E|cMNc>9F$z+xT;y~&VU*jO~rNiZTG`fKbh$D)%z@I^}uFddW?SQ1M5x-}5d_T)W>umb>jj?pAtvAdB95Gvx#cb$!i7g&qVc|@IGfeAk1Z;4!@{?y2M zTXHrj{UxE9qcXe9wf#fC-rpH#-TAkdh_OiTR^wCJFgGn!Q2`e?VE@VISze@2%(llv zd2#5C>v4II&LthYKgpNNgqz;D?y^k4t%Yl_5Vjylgo12$L6=4)H<^CC6oh_OY{=u( z+~l~3OdX&IX5ROhB@E9o5E&!Rb9f!4`bqPLQbuclMQC4i-~8tgVq9lBnqO~BtMdgu zz`s|(VTr+FhaPu98RA*Q`9X)Ct#~@asth7FnlW^qTd(-5RR}MR!}E~M##><-S;)hN zn=WD@oOev=58_jBuEFWw!7sSVdgC?KKXOT7FEpL;6G{MoyfNRP>pfD{;m8)VjRjp& z)0^{H3ocY4YE3@|I@UT`*CYD_$;-abQ>&Ws(Y{dq{)FFxG$^L{=jQ5YvB!TP?Ng?y zS3EIsZH2+j6L*WJB$pB1)t$zq&YGuRD8`mOONk%F95sb7l5(s9qXX!dSRHCDpJTKZ zl9xS3WH|XccbkXXxBmy&M!ao(Z6%CQ2#Mioq!WS7H>hM62=1OnUNlDBLBY!J(Hj3& zMQ5Noi*%a5%V4811>&G_9Z6(R$3i)uqVuJwX)YHDeDvh0`miL_#O1RUkMWy`ok`wq zVFK+x`Hmkme7=%Av zntFy=Yt6w6p#Tfuw=zK5p;0I09rliX1zBR9;bwrWX_LHPQyS}0qtf1J96LTW>@Mme zTK;uVV8doeM?e^UP!4=LK$D;e64nS{1I|Fh-qrjX#n-~-J$SM}@oTQ> zi-x61`eOLECrYJ?vJ{$H>v6h>!A4Y|GgNzT!UQwN>)+qLCPRK! zhtyq(s*Ija^j#)P?22P7_P|K-riv^wnG}?k-bGsCP5gN@)aXP~x>3R`eo*>d5|mhx zi)OMIG1?E-7xfEGgbMNwF~zH_#z32r1gEwOR)=y8*`LV1x6{NmOH?^cnGGyjEZtDP z>C`P!ew?{3FG=%Or(~i!mNIHm?i}zcH{6G=u;@k0lN)tNZav6+iE@$YmF8WgMCuIu z#uwh39gNRR#4N~NVXnJtlbb|P(Kh!Y(WewZ1uLfFvo5HWXmG!k(VN6ao8BV=HF_rP zIp>K=uo$j$vM})}Vx@@&qGi zjYqXX7$|*d*>)ArO8Co*$Gj*3i52@=h(;~K`wN58&l;1dr@#H>8V*7ud6-9|2mUVP zG|NoHGZA-7<7DDDYAvy1?R%6(dQ;XF|4cQdtxXepN@+HMo+&A?u>Q$I!i%LH(@jzH zdoY$#7TifP?G#PsZAw@o)cb-qm*^jwbVeYqq6-$hKqLFD?cy#F>XlrsfRjVza7`MW z3BLGF?Gq1`SWWR?pqB3^B5I6qRgHF~`;`#DFzkhk|L49dVEpeYZ!&k#NEv2?uetdz z<>^l2nc=|Gj;N9^H5X_XenfB@m5!S&1X2GDkdn~%5Ez1}{d)T&{`bfCfV*EY zqeqlzAzpuBEd-GR+kAsUVm?=s9k87OJLMZfw%=jhydZaRVPzi@6$$@E=#}{TWum&kYYt{8>CDD0UM%GS_7v9cVp$gRQ*DRAXb+H ztUYOCTo+d=aAPmZsjLt1jVj2FL||!HX9(Y3sQ-7Y%u{c`Sfa>0M^l@ON)5qWSi8{O zZbq0yLtn7$Kr~bC3Y!D{X=*eWe7zh%jNA=qhOES zYBfl9VQDUpx}44PkTdJMozB+IH<3fcEXIc(sLpr|ddyvamav{G;N5jkhY_9yAWW`s z+-qFs<9T5sf6s}lv>Setf74wJqm)J^2@&4y&RiqQXZpdUQ5o!#p68?U5X=+OR07<(vWzQ9~9m!bHSUIoy?!< zmv1fT;)jSgn(qZva^uCG4q<yql)&6k-`uW5iO+#BQHtYcif&+^haIhi;Nm(T=1=Sth{ILCpVjz>KOJmefv>UV^Z z5GRX}HeR9}eP+*nAL5@v(G554;`?MjVDIqw1S7h_ssQ}>tx)jd6Lr3H8m8p!G#ub}0mq}5Y%ReoUv6FeG_|{Xi1HwYOQd*&=$9uFx)eE5Z z53d*h80^O=N%WJu@xnEI>Yw3m5Jz!0_-DQk=NZqJ{{tHb<!*wnj#0A7usmL5j8=kDvbw0 zZdM;1q8CpcyMdF$$K6WLt!B6D^z__#59KQH2Ga9!NFvDwyRz6e3kGsh#@PUcE%5Jh z?<>D*lIoy@+m@=U`G)wfYFMTtBusHDHn+59>L2S*aXTdcq#uPbF`C1I5MIru|NNu5 z#PN#WxHv%Bk5|sHCI@f!_b`redO9(+H?9SAfF@rAQpgCJN9e!Aku0gxG;<)bBai0j zACZ=Kb@PldI>YUkMPNaffuk86z5nN7Y&1ufY69>14gROSFWXnO-5(y_a01&&&N70? zX}T*%U)|RyJWV|U4f)S~tjQk&@B5%g> zo+NTthJQzA!S80&dbYIF?TW6rq~z{RjpeE3oNDQyB||O86#^RhAZYn2^1CYQvl7zp zySQY8wOpx9(Qv?WuN8ntSPH~^oeW-yYDbg6Xs^Ir_Bn8zrHuFW+g-8$-nNu0 z$=Aq`HO4Zd1&u<4*>H6~E=515QA}4Z^a89KKPco54sk=Wxi_ade5)YDx&?AA(Kr&I zF#j@!U&umIBDSFN>;@IelJG{%&1GXow00v06)J3oCy*+%6h=#TXAQUJCmsS-&nz3O zO9B=zE3X#s_?+PrLL-aSuh-c=!T1>tsg2)mBZO28iEXn0T=EJQ3{6icOkhHL%9;`r zN`=LZY`05c`#)1d*w%?vj-?ED4iR0v!#)z=MoYbZlB?KVOQ29Rxa5GNxyXl@V~Q+k zOHT9bg-nG|d6hkgD*^hP;cRpx1GeEMstYV7Yl;VFsjse08~m~NfBrOBd*(+WUP?7@ zHy^ufZfTHYhd(uhwMoJnaf=nP_X|13e5a5BFOZ0}$)<;KF^#U2Xsj{@>AIQ-{?@`0 z=#y||>@EgFk*f9!kMHr*E5i;IHSACwZLP=_R)>$8aUM%pD^X;tBZb7D=bZBD&y;dG zV;^&Bsc<5yP8P^SMQ`@W3tr1>kq21d>ELZa*fS4uq%ysnu`TBE(-Fo=*HOVR8bn*w z^;pOMBt65!Bi7XA$0)Nzc{kAHkXW*G#^p@xp3B8=uRnF|O|yTFLd@cCY%&YGWE5H5 zd}(@&LiUNNK-$_01Hd337RbofLvmz=UnB6&_yN|T zt&&Kh4-rd`Gyoq3X?~Az>kheUa_~8}shM0)6u-r;r!B>;w6plXdcU*Y0)}qC>c)OF zBae6T0d^e^c9vCfC40$lVXR`D2{5TwZ%tN0OUZPOBSWO_q$E!wv=bP57qYi=1y8<^ zkzimuu13O=4&EzZ6ayuA=8KoJFK8289X# znhWLeR0!R#N4I)$7J!vb!k z-p11_bs;$sGI&rZ`Itk~)Drlo_7CN^D1hRZg{%3c2xHd{DdX^H$enE&C% z;EPI;`01muSY_UoqklI>-2VHo5U@hnUjEs5^p_>o^NQ?bEGapx|5WHYcu%r;0aBpB zXbeuQ7=TA5pIJc0lgKQ(lkn4>E1TLdK&3fsr*T$a)*(}EqxLDjUux-Y0%Z>qPLYQqMk zJ<6LR@}Ia4A}p-t%))sKF=A3V$A|$*FJZ_5v95}%P_IsUdG^}K$+e;U^Ffb#AV5C3 zC)fT|U`y@SIzvkEqKq=$zNf#@=t^3k0m}8|7U^TT`JNY_uZ6KA?jl}R+84uWZHB;w z1vxCs?8>buBo|;?bF3>C_M9((%yD4Hpr(09Vvu=hy3Uw%yP0S4aqur{lKG$NKaHmD z+XVAfCpwme!$RW)&F#y#g{JjIS&Y~NzT5w6w}*Z41R?416ZS!ncdN}#L(lnr!35M+ zb-OZ`$7K8t5AJjEa){uReiW&NTM3}MWJE7h&7G@*Yv&lKVF+^C2N(}=VW8kF$@kB| z9*l%%X%n%#J-%MrO1ZZ!Ni~F-y0@>*AtIq^?d_9sE`w<)Oq6pxt9<|5#zc)v`*q?6LZwmbP^89np=3s~ z?Y|yFh?Gv)RgOZ_;{`i(^bvL#D^QW@deZ!>f#rLR;3>~3!C4UCSXx++R3&&y=9>#& zvka5#st6Ov4#6J0dz=mm{DPK@OcF!$%HcPk8Ho>)=fS{fq9*rXozhG%=i!o(-! zGb-FN7wMlZ>rK0W6#V39ov~B&{wa)?p+|{a7V~jO(bw3nqQWBy)OIYb@9>r0#_Z|u z53j%6EylM2zweta_CNBMqT|wgfBgDhJLx@xMv-UX5mj6=P6{sj-t1r6kcH3}L?{Q4eT6g{VqV<(ptyP#@Yc$4to%~p1uT1!bZEUIIA+FHDCaidYUQby zwH}Ug>KN@RcDqI+ya~AvS*;?;{*AhDCg|`j58~fmnl4dc>|IWXj%@Je9Ks-fnA{es zXRiF>fjioboR?JH{9&Wf_?YIR3bNQd?&h9TKA4ql`;vzpVU^nR1A&n0=;1Syh0AIDE!|=By2ZM{a{q|6U94l;nn?L$hyCjwR-zq98K$Y=H zga;=`g;JtI2aoT=Ub+-Hq$Qx^f$xf!{7z&_!cU<(mT;m-w{5zA+m1@JZMrtgZVI%n za*@4ToNk4BSy?X;3E=bU^xkRuGZB8SajJtyt4gdQsz*{`X%0!-I!%ou>T!37s>)KO zW)JKmu$K@_Btf43xGT8XzMufFIYYgqZyt2FptVlI5T;8dvLbrK^|j}=#Wx+tqf90Q z3wa`lz?xBSimVs7dQBl|bmGV*|f)Hgj@TD#k_r8}YV$D*S50xVH?p29vH(K|+e8Aht@g{bmd+Bddm7724vNP*&o5W% zrmwMNA(L@X#%$)Y*s!%CfS<|JHR!vpC2rMW%eX_By=wm-3P)>Nbt~Hcz&kry>_H*L za21=2*F^e?YF%qomo>Otg`-U|7rVadC)tbfPNCU!nD?Y~@FsN?--nCGB;o@Z9i1um zcGu}Rdp@9Fip>(536L!z$iXvIN{$1r@pW5N3Gig$Uy|iOc|G%Dn-#KlRs> zbnnO%BG*Rw0w?z_x(dESxy4R?J(QBaOppL}h-9mgp2@sZ_;_7WgP$ZR z_U9ES$dKbuukyDXoZDW;mI-XUXG~biF`Gx` z=mGlN!=EuWGT}aDsCBVG#4n>kOek1*s92kg}+W= z8x-(Lo@$@SVV&xI21b!IOZ9t znUnAz3J)~zFgZKx@>&~pgO`<(MD$cc5LyY|!EdqPE5Mz+WUNon$_lj*`Gn!!e%-7M zET|o9nG+q)G=4g1Z~ncIDXZJn_opa93@VKhBNS7Mo{`>s!4mKqSyj2CiQ_YJ(b04s zEMFf8Pg<^J;{*5YACaO9&^Y7%Bqqdw5&Q=4+Z2#X_aQ(w^B{tO4$x~;Sio`hH&s=K zTvNVI8xX-2xMNd%D#XCyO!Q9CB5u4DU3sV<{ROdOFEa{BEN0U|l@KW0r@D(cFx=LVIcPmcng3_?b);{d9 zid*T|a#2a(K&+)w#qfHs{II-PACPS#*}|pIf$z>OW^o{o)m6Eb)Eo&}wtL#67*$7}S|{vf0J* zvxAETVxO6MO42&%7Y62R()NRH9`xqH zghC9)BM4_O+?j_U_*ytij)ARg2dRf8=_b&L52)tB(&}soAwa{k2@YrMe_rs<>p!m@ zSc@Pg4W}l6H7@<_^pi4e#rmftbZaa|Nw`P#W4P(x)VqjO4-%ZH7%+oqwI9FS=OO%7o zwSeCx*bm^RFMyfolNgbQi)-{daz_zI+7lXe1W}`);LjW$lXF*$DFR1uTtJ4LR&3_% zW`r#2sXYwk0NC>RXrl1^T)R0q!56s@;fv+9>f?Hzib4Ix6Px%ia-vGJUXhvF*MKw? zr{Soc)wdSs?~abI25pEhzqTCKtPsClA1b*PTu;rJ-k_dks02MJGx~^qsGzn5r%qa( zYfL*i-x&M5lvrcg?pmmR|3AhbU_Fs82Uq?)6u`Ut<*`+1E5fzTEA>$6nw^o2hm z^=%OHafo%oQ(<204v^nFA$1$*`9SGvinIg+Yr4dr_EQTUzV=CQ7kfC?<(W%unYP3d`FJTyza#* z{BHSbeN<{i*ek;fmSh_0Zyx@A1R^o*HO$x`1ofS5$iaB&_;#IG62WyQRV7>45F7gi zo~|$g%_)PRqZQ!R#wp|HeYzn0)PJ5GfM;i`o~31UC8yt67I9G$z8y00b^eNe#ZK*H zQ2JuJqx`y={JDe>s>@!9;uJca06{#<=Fq;`&;Q_oMplW~a%}5QYHZHqdqb1SnJ;4` zmwSzDx1%PBL&wS{1p?~Bd*JMvexl4(mhc^+ zpnOq#(iTg=N;YX~9)>EFCaHN$k8Q%ADPy<=Azu*|ie~dHMoZ;bF&i>cRezr&^w74{ zUe%VM3_EL_B*?F=6G3{e#wkxJ9_8Ct@p1U87;swxWqgA{vBkUKF21L)j&@;QIc@<3M;&2Vyf3edA~{rKai3;k~4wjEDjtR)hEI;qbtmtSYNAM+{CeA zMn!7##Z=*0Qav_!5rwUYf~w%zlGD_Vr@A`_U6(|y5#~R74mTvd$*eA2IPPD9aCB$X z+Lm(TYzqTbeB7s|7w8v3ZXZosj-IQmbbSW4eFZpdtEK!mx3#_4vh9 z!*)7_m6~20zsSk%6Nhyj!7;&k$75l;vy)x+)k(=Y}UA<j6vi zRDc;JlvxhfG~jkoG;!apwmBuz4#nh}k*l7T=|!8ZWc<`;oT@wqO(Kc4)R{%?hz^3^UG;RU3v}c5x7?>1ms%#9woio z;%#nd$E$RO^3Rgv4VnLE)JWqqF`_qnAe73$-B(HV-&++MBg;6QX@Jyodt(PbKgZZR zBw_VohX2RYSB5qHhHY=8G?LOGDI){{=`?7N+GwSuVMx~iLAp~K2?Yd3NW)ONyGvwr zju_3m|NB1A`*~k>9QW_K@9R3RGi2m;b^p$;PUwWg8dkZ0*VL>R$@)+4^ymp(FyUbe zw(A9}iMSB^cxAiqloxesYg|GUUPTB9q7KXAu0qj?58cS1KO44SP_N||Up}k%?@fN$ zZ~c|W)Lkge??SYXoF|`PuM_rCuPaUg6-J}2R64YRTq}et0LFSn9?_N8u5M(0UT8v>{yT(qp?U+F?jc%d!b!Ujo zn5ba^0~HmPYk>)cGjBqDrM*r??(kqQAdb^I3RO2A!JuGPom+o9Ogm}ec{h{zbUDay ziJtl8LEYg&Y2L~o(jaZZcXvQM#A7^SJs7LDRdox+^N{ZFB>i@l`@bxQ{K5ZOg7}4z z&_hUrBl12^QRMC?zG2=uiVWjI0W&|04LCgV2D0)AOCUeYbBP#$(zdhbUS;x%M~sO9 zDOqpd1f9KUTg0+Hbb z@w88b_2>v}x11$5SXAM`)+Ymr)J|&zHKv=a50Df2z-BN%{)^{pgXs`Co}cbFJxU?t z&zhB;F~ah?GR3vonR!?|+Er#dAGB5K_#&&uOpP#pqP3u8ohh}swY2)%k7$>=FWEVO zYqcRSQR)0tqoe$8bajPcsgGtYXVU%;S^&{fOe~ITHqWI@r)`bQAuOZAf%`vs>&6Lg z0FJc5$qgJTx3HQIqGv?lDICA7s2S88LH-j!5wi^#!lM&zxm+4;9ID}f8P;ai%omK^ zf~oif*Aiu$T;(h{@qR`wvHtAB#+#~0Fngl-aelj}K4jtsW9fX=#J08c@J=K+VNmZ*dKwxr&w{w$sUjepDASu=*rhX zF6vb`dSm_J5`ve(!{f^7bWrM?PiJy96tIxVX}Ii!StEJkegh=OsN0tqb!?LE$yaS* zd7Ok9($wIGER1)39+LYnILugmlh?;y(8<0PZ(==ethvz2PV6rb`6F*b96Vei;$vuj zlfqdo_M+rm)W`dwy07e8V17UfNrXLZGK}z+OTypkpUdG@c7Dc*gXo`($%p!!z+@GR z6*hR0O}|9e7BqvbB-?tR z(LP=Io@dw>8TM)4i*-bVn(8huH5}kVv_6en*!+e#_(T%yT=2}}$yW}oy9%tuIq;LO zFb#eO4fpL#m@Cou+~@~aqA6-K%N&iCsiUM4zcORRg{Bg|4w@*3%=P=Rfsc;RvY_bK zb%3K3@%>#tnziM3;fsJOT5-NAT6ipzQ$!8C5UE#`9fB*wTdF^ky>B0vms|IZoaY5{?du1@QwEv(;)y;1n&DWVMUK8bu97Pa%S(!Q~PljIb(>X|nX&3@da zkmpbfQ#`+l<^^+q>tu=h;fd>d^P76J87>+wF7937-~qS!>u*pxK}hB7$VtDaOGT~w zpps^}zjG~!1rQgFL)T?4N~7tN-d_UkVu3%~jv16UTcNzi|F$sQ(%Xoed7jdAt(h3F zk|n#(PBcoFClENo_2XLKK(ckV$hayd6kK^HRuVgoZ~<^K?8%E>#){58Y_JfCde7QE zQ%_b9yT-M7hVfUrwk3to8oLhP{q&R1io5=W(35jm?DdL_d0g(23dzYIJ#cC27+?)x z-_eAa4xL(F;rD&9Xbm*%(2`gfwa@H@dH4NpJFGRjD3C4<4O!|ZHicMDq`~qcJl`ybx5Co zdS#T)*!J;RpYE7qc5vlIGkV1jo!{$)NZ8*Xi>k{xWB&)5Z*I6Ie%F5T%4*mk?{k9X zw81PW(T3u;wNkcuRQoS?eY4$}`#kJ7Q2$eHaeRY?M`I!O!Z;Kut+GFu5T*Sw9S{JJ zG(GhU`&cy&^x3MHy&`vkH-TM-`zPGF_k$CvaELWrdO8mj3uM~`KR(9~2qoh t+{ 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%0DXV~YYLd#wg|J;*hE zeeqG3#v~ypqyGM4*%sp?z&xi8b97yp=41(g@r9m2_=CMPFWAZCe}_C-H8?i7%P7V8 z)^Mr*NxAXzh_Bg7cpSv{rJd6Jqp+a>x~pvHHxrtkX1TFx`)SXILJs28=g<95bNW+m z!%2yM04iLvZWCIbjQ6}+bkUGL<2|q{?vYVyM}wx;W*g@(283JuW4>HdvaI+UJd75V ztmM5|5O(_aXVK_;@p7P;O}CkKF!PN-!pk(9;UJz^{neXFO`cy)EvB+^RtXX39mBTU z?sq(PIN_%)n&i8-J>_lO7;(;O(tiq_Ke*n#xR1!4R-iupx|$$?d8<^!Wrez2;H@`W zYhbg!l<~Lg3}iXa%arUrRRuOPk0zm@Z22061@NVT(m>MsCKj6+NIp^!eryf;mQtBu zOC@HJ_(0o!945m#Gt-Ao8OMBKGbEu`5Ja|7)K({F7 zj|pn^b1@L!GHWHeWqh!R-7#H;ZJ0qX_-(II-{Y|*9Cczwg5l=URse8Y75hc?NK$RO zvlu&6r+>UgCC~9cJC%!_>JfOCJ#GG1Azc6~js2RM*^**k1_QBL8?98+od0&WOH!c; zm-dntBE7%=X7%BI{#!$?l|GAstcO7dXmtw_!YEGc+po|O8(-Hhab8rv@uoz{0L_~^ zj$fxHCb@}z=3ta1@Pxz!o;|0JeI*JT(fvlku6FU z^=T*|r32#{Rx=d`ANBY5@H)nF=?U+R#5caha-VbtQmT>>;K(btkg1!ajt(^0FSZdi zf|OvKAZ!`!+FefEh|~bW{Tb+VvA8_2&qOSv3E;X>E*)qTVetC+(G+kcYxQ<>g`N0A zCqcdt#3FNx>-T`g1n?7ZJRy(myAVObFZrFrX=l)?#Pe82NXb9Ovs-|(qr;p`b zOKlh%E8i;~qDSZn{Qlnv-apOAvw~k7f*9}DK#vA>mUI%2G!~z2xrKM;dNydh-&?JU zAAaDw?NuTi!~Q&Xm}ssGZ#UFJjvIil1Z+LxSi~}zLs@xsvD7V z(}nm4bTDTpO77ubNBC=)QHF{qlN{Je+kPmivTJEdj@Q`mEX;+#(8?;F55+B{8+?om zaw}f{6;9m$FPyh*aKfG-p}?OoE~)-bn~FE$Ofn10dQ0^8+ZcUz5XMO2oG?)|O@HPu zIi8#vD>=|zOi{;s4d*!Ok$hA8MSax)zcQ#=ED8AJzCoV!qw%j1J3SM|p0tm=frHEW zZ*cIONm3q$#aEYHqSXA&T=~fWz>fgjm-Njbq%q1Ry|3uG&0V=cI%AaNA4 z3Z-2XPLPYP*rq&0QaXsR_yDL!;YA^~I|;x`qpt;AD0?mIdtPcH~X?nPc|km(AC2Bgl$96K#mvCTJ`nkf|)W zAF|!o<)o0^*{!8NWiQe6SSyXRb!*~Q^|(m`EP$a^UCbY;Da(rdrj~Q|Q1|nB(Xi>p zhl-dmhNp%BvIfhSk3vT`9tWX#QK}nc$!asM$%D!ReWweWK4(ww&#&WT|Hy1L5#kXY zYwj?WuE?mRgfG`${SVAGja5{ERhRNCNUEAbjr`bZXOE2fWnk+~LtF~K%6(t7+0>nj z)|dVDNn5lSQcZ_@IeQoP9XMQ`_dD)leu^+&1?Z31GQe<#=gVtXAb_ai=lUHff3&Jr z^AbLS9#^miK=d@DSQE z7>_9A+EwAi(S#bt?BMx3$BuhV!P!!(|35jzIgP21%OpDyf5iH(yukpGcB2Z+o<^y3 zG1v}OH;=*1nMbc!F1bv;MutG`9-0!~tZ;Fhz$?Fm@uZ6-Zj#-Nk(~>Vy@8ZaZs|+a z?j|MPuQzz+>BfQeC{cVg!vz-vQ;8j(BumYP55quW52c0dS%}GdicrJr9x7Ul8?T1C zkwF+kxq&G%RzJNW)_teT=+%m4FELr_B3RhWw5PT1--r(XGHNUx*l^L;$UxGFulK)P z0C%^Hr{9u&j<8y}br(i+S~2j5sWJcmYwb<><%Lc${$=Ol@B}!GP91EDnniB#Z{?~4 zwCD!DK69L?^rhK&MAmO~03%fslDQ<8B}9Ouv?7F6(?rufNQaCB8Y)PkO(GG*@{u9p zqbfw7v{PW|@0SYV?S@JmD;fhPK1)uPQdk;EIf&cQm%1;JLO-zkOlw4KBux_ktE#;1 z$rlE{AM#NiUdW1jxQF&5)NybrHzX8D+@4a?Fm zwL2jh>GuFUeiZyv(7A@g?>=)Pnf7u+m!wD|V&x>x>&(E+na$u)9ZROYvG6;Z1DRf< z?<1&W=Y?>oSXtJ1dBOB2&vSrHP(FqyqnpoW{5;T(vRRfY#K)UO9gSQ?{_q9+K}OQ& zFMii4m_X{zGr9w&x2L|;HCzbUsK1>GtB9?I4?g*zKg^MqJuzC}^2-CLBW%lwU+VAH zAKzMPwxMtNGCp@Eb*)Nuy-STFy4Fog=%p0c z^Nj#_Kh8rF_fj_y`p1;l^EI2C+1SAhH)1Dt5ZaBPGSmq)fo2xFBjCOg6Yy@x zVUgM$aZc+UOh`EO`ejtJjOU9h-J*fP!U|av9{J2r1|E+Yj?s5|yI52Hm8)@1Q*e{Y zb^%ni^+QO}yxN!t%_gC1`G7|RmYB`U!Q%T>Kvn1A z+ze>PNElD7BGfEfvtc2A7OQp7I1E=DpHn-@gc$KE<*5T*w9c(Bi?nZDOP!l# zCEjsXEW?wzBYE5z7eJ#hqvG2CkgSN?u?8Ba=XvwxSwuCf76R#4khXz;3@Q$#%a^cf z!f{{~IPp@*MCy;z@%YW?Sl|t$kWFbn zjr9_^%qahSMsY1i8jOZ^RTId!xQ%oHlwnRz_0Fh%lvAr}@;*=yFoVDD@Pi+bD_lEu zZQN?F;|Fn}{1xrF({AB+M}J`l5*&sh#3V(_9t)$~O2D_S4Uev4vi1$m<%hex2Y0z+2(s}R zjGSDZ4%?vCBCrJj0Krk$%wzwl6!xjbI3LjiA#uM+lvw0pD(p*hIYh{a)Cm*kAjFnebzV6THg5e z^`b2z2cz15=Yjp~l#pk4{0&PJT5pR~NV&wz<7%0b@&Y^Gk$tZ>4>Q~0P4?9ha>GCA zAc${Q%;hpzfXw$xCJH^<@7A|aSJZX@?4LGa^w=rI2Gd6 z9jh|GLwXdfq1j`cK3dt186ymn`~!Nj(HKb?shkG@~2aOz`DuY2JfBx<|a-Z8TmS^C+!tQgFh$Uko+}K`}&-0L+*_Y@0hDu-?*M z-Y}-~HLZhRA4@wb1hrAc?G%+B_GK?b8dMOXmyhSkYo1pt_AY#h9MpFDoDuc6K2r4z zXut*FJxe1om)JD$U&~Ix^9IrlQtTn;CY%Ny-72&SV`UUrA3S@X4EVLRcGZ(SchV<>M>!IMa|EPM=jA(XOBei%#k z9AcxCe$%(mNsp?M{YsVm$ueN1L}6#Mo!8B~J`9Ry-|y_mUL@BQW_|be_Co56fnl=e z7GADDDy;xd+gK<3fzdT;R>)`Cgy;7cK*gu)D;-`M~PWG7s!gyZ=i zxU(q-Ge609z%cD>V3q zqJ$$8ZQ{nKSw7I6H~iuNn4F(C865Ho4-ojlcauowhM$Gp zcdfI~;=|O$5Mqg-J{{AgKf@4_iPMllVsYR%}uCt6HHd~?9CM(r~BZZ4Eec5c~s?`KPww_gjH2M{3y2P7A9KZAD^|^iVjNNnR@pGYo#w-b8jyL?!;ho;!$Wfx*`-kS zSEH${YNJ$nNE-`aWhXZ05QW-!$X^VhxzaF!OGnmgs`?g~+eaqWNOJ!C?nor>N}ViD zeyB#OT=e4yY7bY%HhBU*rUW`8MO=zN81zwm0&V9!Jgr-Jo z{}PBMKHgGU$2O=8P))mq=uAUm!vI7AiTY_V0v%0Sk)~muL{v6Hs5$}dRTw^MJ5zu%uLFa`JrHK)z1p_TUZ}#_s_C;)4Fb;ZXlrI2_YZH?OsD)Ve9G`dqwXcXo zdRGBJ1&;FZ^?J*HLI=C*kw2e5=__qJ6LZN^5d5f22AGJHs|Fk7PW9Q${m2S6befzb zb7T>GGI?10S2OU-)#qmze(R`>=d&MT4r3b&#tUV6n^ZE(cv5SBm;uc5j1Gl>Hb<5N z0X=>c)-Z?hP9m1m*>&^XTh)-P1&lukhi>4#EavxM&TknY@B%coQ%5KaJ2BLBfnMg_mHXwr=dW$~B;7(d?+{4d z9bt?Cd)z?Fo=#P>>{-)*=Kx$A4x<99rgU!_IkIc$i8M|da*$r71k3DBFS9Qz?$DLD zEdNs>Tv_r*5+Nf7y?REeEn=&Sw}H|KK@8F!XV=`A-qm1hYAOuOg!8*&nK`eB009R5 zte3>;C(eY%bD!8vA!ZNg=}X&sRRzubXKH^fKB1tH+qX0o&w005)6F}hl5U#f-J4m< zyjf+Zc(5L?yXjhlj<{j2FyG zQ9k;EjnzTl?LEZ$RwYuDG5IN9LrRBn---#TLeCW5#nj_mJBTbhm@Vi3I7@mIY2hdD^yeci4&qY@Tfx@IK&+8#t%PtOdjWHhwFVpt03;Ps5HS_*(c>1t% z;mtv-8`;xevOe&epm$DJp%|>_{y<{WD-9EV<-v~H4JD+9*I4Ps zFHl#sogkTUn?+&NtVDiX8%`C?c1J5w3Bh~T!JXK09OiOSk{=$rvME>OXi+2v6jti~ z9*z(m_RH-xL~_B;ZRbDVXZQQ+y=4nY{J4%`-_IbUay{hgiFgnFAA%@$|Dy+2NK8gQ zP7MT-&wKeEVPfRxZ*)%EF`{ixxKF>hkuLbW0g{+K_&9OvnM7juMmMh|*1P*ZNHOqd zBlb?CsY$*_Hkbe`ZA_NcBnPWzomN{cvy?jU&gmDoJ#Pn^xp}vk(+W%fsnUO)f4zfC zjlJumLZIcj7#8v8j>67xWgh%l#V^mgC$(E};M#z_$7mH-hcHMvd zWfdZw{J=*}ulmf5B$~k^p9^bd^514V5^dA-z|jgax4Y|&Wrvt!E0rzJe4F>*^|D}y zc{=8_rC<Xnr8mEl`b;`cC0i>y*V)?( zKvwYbTS*`#JAD&UGk}^4!3NbHew}S=^Z}oBkJc2Ky;@gYv54RERK?&9Eh zfaNgtMa0kUVQB@3(Jo9>PM7(dDQ2I6htD=k0e@X-HX-7LBfExte^QoS@d6emfn^Zx zAqz;i$SXVhjTGkw?HP8(f*x`MU)R zSZYQaf+_{y@JDQvt6=$M@NR`O?H?NX$_vn^6E>o%;c>TL>i9y}zC4!h$XF?&3Q}9tb z_9zfH9|W?}$bPQx&nEmH9rh~m(byK*DJJBiORsQ!(|;IkipLcygDm*&C*$GFm!&sc zRG#yuIB31AK$#gKE>nv+Rq+5w_CAq&TqYP*uX{5<9F`$A`jsV%UzRhQTB0lvEDc&> zBr5Mf4E3r>$eYK^0Ro80$Q>=1OhY(2*F>MNjD^{p{Po7rGlaXT?r<+Me?peR0q`;reKsicZoGXAT@PfRV$ zLhsH4@3ZRzsq&BRdT%;`d{`HkG;FvyVK=DMqm{Qgo@9?%B27f3BSwhVe4nZ_F3})Al8N@eVff@ zx$PXnsPaE+=*X~0E>VpDy`~KA>n)ORjdW7kSLAc&943WIti9JkR!{SD2F(}OpJ};R zh)c1Hk}J1O1}HaoSnHcGNHb<(ESO)$wi zdu?8_ee)q*auZ{lQhyzaB!mS{@|_P|Ast-ip8Zy&2%KT#bQqw|f?dw_s=iq`!v za#lGp+DE)!P{-GJDxxyBJow0zoHZsBzkyOV zjC!pdb5r;l?lKi&ELglOYZjtAKC2MFL6NdH*ir4zFI_YoU?S^R8*`Y?2VTc}Et_ME zc((RxTVHov23~?5)CLZMkz>ngN@2`^i<0M5**&dU3ndaT9^73VC> zgDM{e5I(&7btOJOM`vslnTF=HyxnxP?w%#X)=?r=W%mb{J?8@-v>muc;5Q|^-(73IS} zkvb-rXZQZ^@@-b&@PQdNA{oQJqjCX1(P6bnNln+q#a*CQjQd2kJdHt5mZE=zl5mU3 zv()KE+R(xp+DEstIw}T#Re|e{wXK z%HBvX|GE2{X@B9)#xy0T8Wo|>oRU}gA2sx{N>`_bSDaTHT&ZJ5RWd5i%kukz%L4gn zRZlrm7$7iZaUWAa1baHV{+|dk?-lkNfEC6ccdvhLxH!YxF%_k2wl7qb!b={bH0`Dh zAm33gYABQ%2-t`yjla!=9LRB~(Ou$t1H3J8QwC+zIvq1(%^kAvc zqOG3BPYeR2g$K*Tm~+jBaSku?_Hi(vKY)EszWn(GZk`ag?59AI;NS3mB}bJofe^DP zv@2fbyxX40d|3zU8se?Dm1wM0D-;ozzJ;S~*6OPE@Q-p21W~>rr?9=wB>%~@>)f*} z4am|WWuDRgR>&oK1bYZq(?z{wA>_q7PA(mOlk5OEWYP*D^RgP^-4aAbf%rQFwUv_Jyz?` zitrnEIz>Kk$AEP@`no0U6{Q~z#d4eGU4*+r@73(hNd7erV7tj7l+qTvbxG>@BV0$l z;eM&lBdvj$TAp|WOhJ7YCuP?o$S~^vrpN0dUx!@Vj{BMWJ?_vLqDC18;2+Ir79|Xy{!{KMXf}V#O0Q+Di7l3tOPpwYqzC{ykOP^qYG<5d^{w)DQOuV zRMk?j0Pu@zlid8b__Qp*ft-fdR-kAp4D+Lm>id+M-lx<>xM#x>f} zwy+H?nHeyRHZdP5v6+M1Gzm<-b}zjU8u6J zyCKPXb=WFfL3eP;cbxvEvdjSs(86W)&`4jy)H7D_dc$ucD39Rpf=n_OEn3aM&FVUj z^05`Pp{l7f7lc-D1JDL(O#!djL%YOqgn z2?f7HVT(+C6Ec@arlI1pt@Pv1QuUZ1twzxkL|XwW6#+U(wZ%bXzcwPDl(*96doUm` z5M~!-7-kIh7+qEvD=z1Y!(vXmF&goF5%%&X3-z*Ni~}$(9A@ca=WS<+XT5`5?2>{PL!FkwS}lMSmtWB%X=%t&&~&!*0sw{5_lU$~egFi9ZKy_h9}%+@0J+>*^n0 zjA&Wr(s@^-lm4JmWFw3GIJ0ba>}}Z6YQa-lkgE1Z4RS<9$TxFg=ccQxFLL2#ceAu9 zG`_gWDu;IgmQuStUn%M=>(08~8o6d=ti)%|Ld7R2;)r zc^GZabt^w`c(!~!e+Xu|bI{|`GS3{x)7p^Ey7>83_~3atw6In!v+{XzNS8LIo4Ann zT?V+NRXV0}?ca5@VHOIvn)n4=>@@Cc1H<;>!yl<0+p78Z9sYXM6Ho86?q0Fmanss5 zido|Du#$IwVUsrCJlcg2rmtd`SeO{GX{Y{lLc~AC{y}v~?hI17Bd?3%A^OxL@}`H3 z+)L?rnCDo-geEFQdiX26FQuJ_v%EaqhBc|sUb~Bsfyjp!A%piHfmkYK?TIeQf{isb zodelxzFI^}9gxd17-x0|9%33Sv;)Nw$;nxn9$Rcoh!D83P?Q!ud z2G`DJ`dXte|GLf-o!J%Ow1lO}0F~R4t-8KB8!fF8CXQ~pM(Zy10+t%~=B3C65m^BN zqmI@`nbxg*54)5h>N1Gxn=|nf%dhNYcd@tXHgeUS=X4z{b3y}AJY^w2_SKEg^Be48i+u;g8_1t=wuyI1NAyTl~>ZCPShC37~VQs+66eYFtfV|9*w+ zK0h)h?{+9Jd-3`#%4aPP?I?0GouF=|o?BPmg4NFZz~j5{1OPSF-7tziG zix7x{C&Y?pKDW{~&ON`IVWZee`n-9@kT`6sbhAy#p>nV$V`H@~!h?gZmr*w3NMtpZ z-6RFp)LQaW+g`uLqpXw&j{BtUeD{_|TiV_O%&|)S5#pyY2zWHu(z*g?@qhkfr&3>JGyJ^ioV1geQuZ~;XTet zlva=x#=o>lZFsV)KQ^Wf$(hK#zWc|>l35KS;yUUqoLL>% zJ^jZ#x*3abqwRE_DKizpdY*tb&6K;>_)g4Grr}zpB!n=o2u`9{z zX)bW82XI~JBZzSSCs;Nxvn@y&#Jbg}td9{*O0$9oZdB7#8EiJT3>o?vuP9bDg1?OwH?-AF z#yyC90tMwWnsT?2L;{p@Cf?)d&>4NG?Dk9b^JPvZ+}f}}xJs zBF5~@YMbEJ!5Gxv9+rD|w@IsD<8$5Mz3PF0fp-|NVcuJg!CX$`)(`l+a3J6dWq~u7 zHGvg(w8?wZhwFdtxO!G6OE=v#KEB!BQ%k>mJ947#0qT%7BZxeGME6r1?6AU77c*-d zV_%&qObpaO0IJ{ZGtO)up^}827c0r_R_93^hyi41fEnO@0l4arb|5a!PYP<5o*gIr z7SP5!x1-LET^T`T*R)7VW1?7MC_G@cOY;DLpIGCgmcVlMk0oj{7)+)Mi_baRg=>q+ z%Jp?S6$~X98y_!=J$iS-WRci;GWgybB4y_>eh>dRH&n}^KEA#yv8lRX>HWv`vE~q= z;(MKCCrUXIGlb9jL9Sxvpe3Dm!MeNs+IZ zIl@0TR)00mIy;IQrS+=3RhKeyxpT1^YX4xhfyHe2G3d_iVrpOX%o)pid!Fw0VVjwz>|uO=(5aTAT)SCr9nG~P2N|;%$Kyj7 z0SyOx8o4|D)-l~oRSF;2+1tr)7&B|V#C`eaV{GRwu7`-VSK4%;K;t8wuVdGGp`q4o=GLtZ_O&=e6`fJaJ0M)Qgpy-j}& zp6DcjO*plM@W80xv8nZw1Tmdi$l=o6f$wG7D{6a}pP;v|{>o%w2WxI{D4{>RB|*L% z*r9}`YGSid&FiiI-;(wWRmjS zE8#e&`fn&@Yj#muT-uyG#OSo}nbuuwMEXtiWyEcLvna+9;}Fo^0cT=Tk?uox#0Aw; zN>u4G+#tVcau9Q9@0yBWx(nC@5K;oe6E zSK8R!p}GIj+b;thq6kdG3_tM=Z>HI%JGEd_I3g)wut{>iYGe}(o9A}Farxe*wQLl# zmmOscG^s&1|0DdA7w7Ig{vry9Mrpe%{>4;9Ve08oA5yU-oEk|t=734uuARajgh8{m zxS+PhF!`85wdgod@fe^%<{8;Jy~&i9fS<-&9)bD~>sF6FR2p4Ht@-rs<=g;7ycitm zA%#D2Ee{SyguJzW3==-~lpO@Tw4j{1VVoEud+WijEpYyIaDcbpx?CVME#Uroob*k< zxQ043ze1ztJ#==m3#oRS4a^{jdw@`g1I^^pp1lK>+&^XV9)0u^J}QyeR9kqAYc?Uc{R(Rz`KuLG<}OQdt&Yoz%n5tf;x=t8T3Jl?0kMNysA)XwxqVC zpx|)r9MvNU4b4{drFVmf8H+o-OkJ;0fnv^4SKTW#v8@};<&3QZbw13{U>$?mxgHt_ zT$x`*nEUltxD7AG3)w5tkyVlqWJd>=OGm`hl1fIZtdP=doa8*^2U!J zc7-JVP7{6HsoA%WXWkE~a5f9vJNkRTClF*=`H9;upUPaDmBGn^SA)Qa%nkwK882me zpc2Tlq7;S^g@9hbB!+JYsz$@%r0ChPqWTtI4M1*fG&ya`z!(nL32u~V+(l>vART>A z;QGS+ZpZL-dfQM8h-{?4Ceu0^Qqe_JRqf2g$~G@CfDWhUSL1E7pd9(KlGqvbKBJ{N zLsdLdzUnAU#lURY^IY(kRF>Wtc>7@`h9Mc9EbYWGGsYPPztG`d4wEp@tT~pwe*W!d zvZttwP#&%H^kt%<<~ucsFk72++M45cF1t?`KZO1n@VE*eN)`6sWLKZ!@Boyom}XAK z5QWK<-ET8&TcbaxV%q}xcJ?Lj4D$XfR;QQFON8pntNm%;QBqzo@lZ7(RXv7>6ff=A zk33IbMnBz3wd24hI260+JZs+OYMjib+@I5)f5IsN3UJ9u<+P;*UBXFS#?yvHM%@T9 zs_4Wy#}`kp`MTUD4z>Q(y*;Aa(iw;3-sePEX0BlqgmJ$_782ag*nV6Ic#&c`-=*eUeF$ggX|Qhw2`CjArus`w)!k~IP|B6!~mV|EAq^8aseTyF-31u|1^QzfJF_d zfLCdmwGt(`GsQtb&cPQCx2J(L?|boc);hC-J4fq%PN{L%I0 z4ag);tUa=s8)oy|!k;T?|84Y39^c!Vvh#1->+hq_xxjohkG0c&P_L-W@TC13{1#p| zN`VDgtTViY)CaaY@DJbx@>dX7qiaKu^VssCllR`BjO3OLqqapJ0r%}YyQCk>C~S#k zLmCW)%T`oKv|=Ll{J)$t=_-ax^3loEI7Dp+HbgmWE^J=}o*I-mrh8-;{EALIFb@@(5osp=kF zepD0xqQ{IK8+?7^@^npH!m7z)*u>B>MQ*hrE`k*E^-jTjT!?0&g^Y3JwQ4; zqU&V@Z@K)c)P{6Eno4llBrY>EnX=LVpNs= zUP$ok$ZQieB1K@gF)Bx&Jdwc2ppSrghh+S2&t7(3`kzzoy2(;%-pR`dH8yxS$*I55@yFbOKwplW&^X0`A96wdpq)WIAjBm~q# zi09KBvAu`uUYXF(Jyp9wXwDD3ERi+#9vreTC0NzfMbU|4{EggJ>?l?^tMg5+E5j}B zm=UVOikP6&?^?@h_`Ax;tpzlFe$v_WZ9GBJX~WdR9e(|BQ;f zGYRq&PdzxjY3_^k_1zD|m}?T1YY3Y@a^9*Yh#OZ+!F`g@29rH6(UMj`D)5tF(6uEx z$W&M?58)%ZlhmK~w~}2HPR@%|Ub~p_kmTQZh|xy0-|p33K4k%X?$yH)P9fNOb0s+S zUsi)0?$!6$u<1n9r`2g$EOnyp*6auJ(n*vC!3lNlbx0r}DgaBA*Ef#SY0E#RO zFXN2@b}D1x<|jI?D}VrvUrofHo&phoXpJ<9k%9(_rh*_8Dq2hUEgl!VIv;}GdBYuJ z_ZM&M%J~Rk2IL`PUi zCX>d0#}oc5(>XcnUMYS@%wAz@i1#LGy7pNZ2Djn73~e|3dx-F-puj6Fj#QTE&U%h9nV?#&vmw4t8Q3L=Ge1#V$XBR7nzXs<2QZB zAtty?>q|zPEP6cM=V3?Sz93SaaL#Q-ty(9AS1m=jFo`k`W)-9Y@srmIl)gRcX?GJU z%<}s_p3e*EH(Eo~#w6KA6|{x*CT4w*!C$fFR(s1Kh{I)YzIde?&Ko)aLWYy%QaI-= zH=mhcZu6S|aIeuWeNQ9hGH8#c>#+Cm_CA~EPcQ4GCp$@B4Q9Qdb>sMgEaOFt+rxhZ zwP>)`lf>}&#h@rzGqU~gGPK(;Ne*A`WX3o`CMLBI;S1+jh6}WHIk!e58gEhvYzV(@ zFyD8;;(6;7=9-!5c-kG$<2t=D`G5|rSq$gwvf0-bpG%_mVVTkn+ zE5(4(_uSrkF`2VRcfV?p^bh-R+=gpSf61`zrZ**#Ety=|dnhY!@hMMZ6N9{_n2}6% z&5~1(QBM=lKWy~w0_RAx;0Lx1#rc_%5ZcaM;LIo>9&g z71R0b2>3b0Wq~EBdpdz}``!}6WCNhGlBJJ~Aief%U(=i>O(MHCS>QCbK-fN>;$$=ZM!mO(i= zNU~rjlDwQYyL^Aqcu6gTdMB$*mXG`vF65tx^8#0e1Ao2SJ{2XA)wf=F(A`#z9F_Ka zB{_dPl%(o9u1lJqI;M|wqbS|47t8ofld^jma(Ib8sX`_{7AMMj1G(KWwZMCk{8h$Q z>`4uf<<<*|9Q5wS9V_`1c(^WA{ zaYiDMJ1IY13A{HRULFH75KLVCR?gc?im#;SF%aBKyi6|Hs_FF9oW9@K+LD7jI*)H% z)alDkSUfDpDhFTa=%r-t#hK@oh$HlH4Q#x%sECrG9)Aoy>Z*kb$*x`43e&uiVaZ7Nne@3Hb#h5b1+_Oj>my0wsM3c4^H$}nx3a^YbK=_-=$sD2 z(8kN@LvSWr-blmDs6R&6(Kk=)deze~2AYWH znl(QaWh#d|X)vZX!EL?kcVzHT`@BUaS@(QAiRet6NQPId;T*lXDRFVS2qIXV(rZ95 zya3tnp*-79ld_5}nBqtJc2LEU3Zb3yk+%}0s~SJvSjx&5Y>NH)tL?VYavOl#p>-Nn zCM6e}=fdj%uQkQqN%H8nV@r#Awfc=~x&jOW}dT zD2AKb^rno4G-zSG5FT--8jm3&_vN2-s%--WjQBJ}ki#%fx zxMK7XqVJ*_Q>Kxcd7Gm3LkU*t7H)&6bhWVrQT?#bnHC?aY!OUMma4~y8*O1w3>1@j50w7SJ4d)8ld#D*e?%AK^^xI>VAa*>SqfWDRM z14B#RUTkp4c_tONR)|#cr5QtoE6sI6;Un02Fi}&O{jx1e>=GWpzczXZ3tkD@^R@YawH5 zo8d(KGkWTfkbU6wc1D}k=|;fati5jni^TZ*R1%An8hotkB@hUv^hAw_7R0;KQ*JvL zIwcLa=aqwJr^JhL`Q{TNRtX=;&LFO}8O>Dll5j{^VWCxpfzb&{p1gCPXj*C(Jcjw& zmGHeJ>FGXjk-gF8Sc^Kr=H5}DMy4irA*ji*JnlQr#?=DpfMS9h1E*$nt7OS~noMiY ztxm@o%jnhUF=Rzt2^%v0A>dfsah%&8bMV>k4ua+;wuLmXjyR%-7aVuo<>e6L9RB4G zmM1IT%x~~GnV(AJh;!2V8gcL7#CN&&A#J~xI)VTOxz7ED`GDCMNa`aXWzBy+{(Y~f zYS*7jyHvD4pTKo4^A^y~dap{6=KP~$=h;3yZII|H$*>@Is~9E~f;^c9U+@kpKt?4G z+d@WQGNU_q$b(RZ_SKMl*D#kmg)Hv($E7UI=AnNouYc`Cf^*sD6uXgqy1a4f%5g+v zyJoJWXf=+=b+%XWL(wX^qtfE*Wis{V84K%zv5FOf%{h(^IbTLwZkoiuvtT>(T#??D zlLY1BKRNdu|B+XiaI=3Jp!M_5Wu_qNQv3eA0r0J1@zY?0_BH&oBWX0{w(9P^-BT@M zQbA{LeN|@LF4q6{EIQ>b-H|YJD{bpc-50~Sy^t(^Hno`#5VwcWiv#S1ngS`B(44RK zZ^_9Y;b6Z*5Uc{0Q2+KLxDG$$3t`pcLr;z%uKwUUoIw$xO_C-MM-Cp~2>2X=M=x}7VZ=tb=CE2d7N?U8?#Wn!DNTkb^lyw9xJp1YVDp4 zb{+UBnV)z4Noa%yjeQg^aBcp4C2jzFvnF9p&ofAfS2luR1=_LKdzOSJ$0h$>Mn)Pq z!YE5Pq9fCWR|h(vd*6VvRBvc5{j0g7b)TMuE zKN7U1mEGgFWlXnu=YYe-6XoTLE-2LEuY+8iq$0)Gnr(rNAkffJNj}0I&Rst`<6U;%+APBfX?sKoMbY=u# zxs@*dQ38CdQO;N+z~?^W_mZ<3kUCjv{VbqP_bv^R`ZP%=fF%=?zM~N`{)?hDghCJZ zoSHPlGr^fJIBVVF1moVcWw+WD^WGisLxn+)tf5#gxkk7((QqHi#z3-0*}~$aMlJS( z!?afLj$V43e6LGh!1m3YWTXkLQe9Olxl-{}~Avhv^zDsg^eNHkypWlYN=)!7NdjLJ&AwaNl zEWVIKs{;OBBPp7)txRgyGo znl)Cmxmtb&a;3R(%frN1(sgm*i^Vn`ZO@ZG@=LzUSAGGbIaTOU;m@UDQ6ByJ^j_k1RLLdVI`*{Q3XYO36R1l_RUVr@_7Mt9MmzpN(4qI z1U3L}u#on#aKMhT49>J#K-fwrf)B$|!(RuyK4C_oV*Xld!7EM%i=;h%H_Q=OJPm{w z2=gSMKTBx$?DdGqvG9~Ibsv$;W1Vw*YYTWpXMVhqW4Z>x5akVQJc#J1Pg3=gl{gY3 z*q_$&Bwjz={U%pg1)~EvY(r%-#;Yn$e)reM*&*P!^9V4nUb=>TZWK+Ut;KOp+PX<3Vcu(sX~>nNLB9XaPn<$dQ%M8 zHeKxHmf?U$%mWqRkmk|(*5>J!QGbU@cLezjIKRh~OAl5kI-CsQkH5rXeIgUUZGI7& zLT*s9^ojj4y#k`AX>3|?{&2g!?a??{#-OCC1N=1dNHE2>^JX#KS;g}R%OJWN8^0BN z5p?#A#pNW$z-LJBgzBj__6#~9qFb&^0c_wUq=0~lhOw$Y!{G_#EknJ#_G;!R*a~|M zNb4@%(xAF}-ro-d$f-JCz`C8EcK>HNxT9uD{5KHiA>}|K=feqs0iw1vyP~x7jzeQR>9A95+T{ZgcCg7zlz!}Ruk~>tA zSg;5__a^g93`KfWZcE9qnq6U7Y|FUCDQ9bdKhx=ZeLLwhqN)yxdo7HyFe0&--1MMo zLT9>zJk%M_#9*nsgF*xJH$T;TXGCP``SQzdg>3)QYqucX3Z7`}hiz}#Dw;`*b*&e= z1XMF#?>wL@8vc`Yp{9u(R&mqL#?*!FYu3i;P*HuV)8)S^rBNazivVg4h`c|wrpFG* z11c=~<-)Yz+}6W2F0~)$E1)L#=cQ#8dWj15_QRJ`m2PrzC}Sj56~P-2F_$tc5cWN> zHnUvwQvsGJw*_&+iFSpN^a_yz&Ko_0*?o-?&uS$hU!i`g5-$L@Mf|b};_tRrXaHr$ zu$VCt+i!?-pq7v*_69&`)Y9a4A_-35AV06Lws^1dJ^&zSo_k;n~+3uKMwmNB0RFLuRXFNB5K^YQJ%i7CeM9y3i-cPwM%4k;)&^fmn{DiQ>?@l2QgD;_T=%vT-AKX(jN=%*y5Gm0?h;hXNSKG z;c8oScLlK$H%dvx41>LOI9YM5XLm)LDwgD?W}6h3z=Q|?sb0_4Pn&UG^|<-edc?ez zZLz!w<6NPg=`$5kc^5Uj@Kh%C^>#3lv`Nh*coj@6_s12Y$|>NF72drqX_^l#pCe%v zmj_)4P66mF`ki}9VEna!2LK^4f_@6C`OL20%f(Lz}Nc~#NBUY<)*NoGjN^0hG&Yt{2Hl(AMohD0_g;dng82Tap3+qyVv>n+y z9i=C}*Qej#rp5{5&;tlJhyh`Bi=$zWV1neIlfQbTIGU&ef77aY12tz#tvX;6@-(c# z4JoHoP9g_S^MjGap>7M6nuNTAC4VUBD0F+v$Kz& zQJa4E)sBK^Z*pju`?z#@}8;fZ^%$XX!D~oUzvJ;cEdP|ETU<&*+^it2;02!JPTkm zUu`(xj6>^AfVjB>{;-~iW6>m?smHlp;)aG3;2q&gshZE8084na&d$w`uyGf)S`#Dhc{@VbTwHi!Ju;6zM{Ldt zM)gfE2k{tcI#Ui9;rMy_MQ@JVTof!bdr#>6JY+Q4%akb|O)NPKFbo|r!i}p(HxJ2i zv>ue6KfUoU9Kt&6n?*0g(qFtyUv4)W( zreR@m>B7V->a8{aUcH`JCW?|ytuw}dh2p3f1OGeb3WbByP2b1Q#`ki&!zmN7-1Sxf zI2NxyZiZg<*!JMb8u_&B)zsV<%ku5c(g@@-8#Kj>B%5aJ zrzk?pRX9gR)29tw*cn6aeFFD>Gm3V<$6@6X03*}jYcGk{nz9QW z`~eE_FE~Q;6{PE>Gk2e%X^RYU!gPcIcBxo{q&+NEaDJrTJexJ`IoX}!@*0?QC0fhm zI>|P}q-L1gDWuEDkjf$?#?X9DAu~lfYhjg|FIcjn2OZO&`s`q@>r9OA;V`2{iR4H=ahnhCn)M_2 z0lwD~n<*tU9aVdmla-{CFG3|n^vv8>!s0H0`J4_!|Yd@6Ong*8zOnl>jwNBPrl7*m%jJG&dA8I zAet24z*+d}U*Q5%#B=s5(sX{EisMdlMa;0U{=^8frB4z zYrk}T{}2@`3UpXwZ`k4BI}!Pf{|S6?mp>6oW`ch z-3Aw7Uw--W+}w3GVkuy<)MIva$P^&28CF0A6dfV%BdY@>K?hWHN2vTVOjk$*Nw5fh zfLdue_ey_SoP0&Jf(?K$+TQ8T`*V#ufVCRNbH(9p`wX^2P4M^L-5PK)2h;1ou<ENlo^eTItKn{+k}-Dx9glZEH+%>g>?3Om*YfGMeBa290=#R(23eUH=*n zYzxaU0+b6$1EK+T*EY`#Y~(ncb23AF`{`*7NRta@dI>Q|JVUq;Uhn3h#fd?OUw2~K zEz_ZkFu8f_sznHsMtNuQXZzVJ`?kyvLS#B%>)Xl75N|Bgp~;zNSj1 z9SXrv+OyW-?@z9u8=8(y+QNZBH~s?d0;b4u{pAqmt{^vjTBA-8KMdKUISzhLy)ZsxAR zD`B>cCX1ybzedyfDuZmVe|nDcIlYuy7v%FgnD|d3XtCuZa99_7o#)<7K%#AN6P>Bo zXciJ4h{7VT(g$+`vI{%Q#e_=M4(c)7?tO40_h{hKDZOluTo#E+akd9Csh!uq5A2sy-x0TEH zLymz}5H=uGZWN_~-qi&g7w1@W-VqXHItSZ&b1?TEHzT1$8OfMP^qqhq-KQVqLql1y z-$z8mOIj6xq2+<$9vl?!4qv;%1SLMdlM=Krnv7!*S6x`2lwQ&HV_{`@;XRozzBXwj zvQfQhll(3Jj>hl{vS<|4As4a->xsYI6(G>VvOI}%TStd<2XMq*D$bbM3dtXCzAJ>1 z3O{mVhivKe_p3&hbFQC%M3`OI^-TXAznr(uU;57|;n-*4rCH_j>csZ>Qzo)(+(gmHE|Du`SSnx&X?bM*%g@ziRe`rV?RVX&;9pn zp>F#ngt0Tg+kbgAsig)878(TvO%S~ww4LUnVfE^#v!@#1!Vg-?$_CcD=4>#f8dVYE!ROl62wmgrp+a~nOhJTMKOtL6T*#h<7dM=D9kH` zn!Bhku-dp85$+bKIKPsPlejoNPNJZmS=XkU$d-mXquSh5cY zl+%Z1Jjv))O{T*JSgLGp$kFck_k9D@FeTiK)uU%AWFdIfPekkuOd`*oeIV#|nXnBcvu`C84D}!P!eK%=*C*3uAc=0`S zEO`5TzRGZ#(_g^;{qhfMPl^r`P=AK+zYBa>fdexq(fvFBLGS{R$4w7+hn+Sw_zM=H zO;XX&GNx0Br)k~!7FH*$CGD86p3KBzXWGtwSXNU*OafTMnHS{IbN?pGDxT-NS?fLU zk!8&+o)PFdAfvx;N>fsdaQ1DO1Y8%5U8}<_F1t+@3T{uU z2h#Ew-4PUenYY#PNdGzZ^pr>g^Yyk3~vF?c^3rB zM>ob_0Q*g~GIROeN;%l~MPS}a z=?shV@nzM1+TX$$+ldAQML(qx0oMde*x;N-Mnk$mI=4DhfS2>3PmW)Zt=w6;tf#LQ zq(@W()a*p#g-aKta@?pklVtq$p+}gaja2(nHW9lsfqWq#Y-U&il zMF~koN*ZL41_P81>28Su=^h3pq(QooE@_5_k(2=h=>~y8>5!r0zW&cW_uLPB2ljsV zde&OM^(=E4rSmoGQTyCZOYrx4yTgAL*PJ9Mu^sD-^g+la7=FMs--8CXFY@6$Vb<&7 zS*o)qTP*pmo91kH*oacD5ZmxpuS?aXx=AOf{rRFoaM)M3%Icr^i*sgMM9=v&Kbo;U zNM)W_+F5Gr%vUmsr@vZ4d;rASG1;{E{6FQ^^`COnatr^QThz_LVhW)QiGv6l%7Fet zF{vwGUOQZuR>lAkmGCWca|bvK;Io7+_mTwP_4^rNT(?F&t|(OTt=SHGkAt!bS_M)7 z-JKK?e+SLs_&V+GBkK-+c=qbfZp&5$R#V4N-El1Rfc{md&Nj;6?1E6T*zZEc(=MeD z231KyLiFbC`l4NSg{KlG?vEg*vV4=MD~(#}@Kkpb>R1s9#M2-SIdD3NA}2i1wapRH zPH&Q$>1XNFEKzP1{_XhucWrId8+;kN_`CeKOka6)`P6>8Jfdcfg%An-2~gs#ngZ!1 z4F><5nodShJOE90A{0yvpzx10COJAw_;&ndqNfmoYs?2o{WrxvvWce+OdDiirL04xU zH)>ep39B8?eWP2XHEVWF`%?WDYJG$2qKkjirGQ&a_YtEE@x=O||DD>v|DM{Xm0VM! z6h>0Yo=|U$BtoXTO!uB*GR?%Sz>S4=C2v5j_g@VRs&%IW;h(x-TNFZ@yNxWMZe7MF z^9^y%tMX;oAmLv#B}xxiBSV_;@(ftp-iowo%m?U3B^o@!Z~=)OYV*GXtG zvNl-onwqS)ANWl!-J0RreMd3+GB9kG?@15IbayiMS=N4hc7jcVN{R2Z(XK}O`=77K zB1rDNqsZwDVTnY&k-I4M%xcKCO`mFA7bd%{p4BneTz_eYU&B=E0T;EJL%GPf@8wUF z4Qv$_rXAm0E!|x80@E>7-@?fLxFqX>Dg2q~Zo9fL?1LRj(oqC|_}eUReQKgOolL)t zllCuKBrW7Kb;1Sm-hQP5b1vy~;l)MM)$&Kd#g{#r&-pys2-=khk*>=^i}Ij1IZM{B zOz&&bceLwjC^fWJ71J2zXx)tT)!SdLPZ9!F-J`5FXS7Rf)k?2HH}tGSZvDE^q4+gF zdWXI6Y(E;~2E#%(U+fyviQSLNQ0Cyq|Gg%QWS}$5)}T&y#W-!-)%tl}F?O$c)|Xsc z=fPD1g-T8qZG}7rB)!aOP;P1_24PQUy~i!w5|s5nV$$l8F3A5VbQra7vFsQZvVGjN zC-tUQrCF5y{NU0L;zc-6?DXlsBmerRk14>Q=@@+&|T|Il*O4A=qbvo|wkko4?@#%c+#gYjp(XkbBO12==pez;C+uIMMQ z%yvz{_9hwIt@#RYI`S`T$LT9Q6MTvHJF|x^fCx-}TfWx-T-lS1G&?k53p~d_3{5dN zsy?&OY`8{wVT(+ocPPw#>ypOk{VGY;beqr3)qrobbi0E|LxpBBxK!_&^n!}cg84D})+bG4tCLogYqYsj zD{B?STunj7q8Q#v15nf!p`Y@bHjCObki2i3-^*yNCA{E4ieh^P z<1#&_rjB00;hmtm?s{DeWDw*6(xqYWf4n<~EI9tz*=YrNIIQ$|*5l*GlAd~Thwsj< zz0Yy&MDG-`V=T6dBa}dbWt_9KL9u!tYlKYDO3)j$nS(|;RON?s2d(B>{*wC0LA)|m z^#t7TH&3&skS)6LbM4S4(bng4f(@n!be(Kt2D#3_Ge)wyOc?Zi8|CS4@1Y%^l`tC; z6DoD|CCbpEgMs2smW@#9kwwzyn)4qvv2E{ows@1KC9Tx7<0;_QAFMQxoY|m7XSt&& z5kN;(7|#8ZP&FC>`?0-O{*6sJJO>2cA# z3?#@x(l=*Y^NXlHMl4*_S6}pZZiLTlUvKYSe$QYl(yGF>RVJ!s%%SHtRZl+wSGbY4 zfA`G_3XV0tKX6g0sfmr9nW@=BTqNqWA10D*{ph*JO7?bKe2{QP`g3MK(vMM8L}yVm zaH}I7xOv8ITSq-Ra|32tw2O^%j@YJj5fRcy$9!~UmpX;)k{fR<+@%x;H?xOy&x24y zW{)jjMza~Py`D9Z#v47lA6eW!?GBDdXQ>XIL;7b~wk<)9=(p`XG1B8z@$HqBWXhZ5 z+k!Ld1peH8{whRs*bQ+g#`6ToPB$#RuU$4{4N;M}iA!9X|UB`mVeDiwOBL3^NTfhDytqxo*i>DrgTA<`1Mi3D|$J7Om|T zKC(`Q%5~m?B0)5RAj5<^>vzNx@J%c!AfvYwod2ld6bLy)-&|w{p@mph2 zLRgN41M1?reExZ)pP>5Cb6 z$LbFMZ~nWQQmzL*Q4l?*!|!T&J7mm~=+6$JMt(Ns zSY1?@!d!CoaaiTlrQ-!j)Q##AZqGSEY#&~J5Zn1!R!jYue&(?R{oOYW15{0@vWdyjPbHt~qM%n|Md_R(l@z^sh)3Mkv-tB; z2VtrXZ1f<+cB68Y|G46r`S9#;dRGoYr?#^VS$%N~iEUZ3e3uag@!D#afeAFd_FeOC z>9G8qc7a>7Q(JHx9B%0yReDz9yafa#Z?i}BB zWT>>Z?}}}rw!VG4Br&6LM)%&7Y8DBTfvNMfRJ+I4+vnbW zC26;}rSVjNVFd6o$Pi0=SzYV4nTD`tN!P-|eanx}e!mh|y$`N2qI=t^lqSSGST>{yJKPt)sSV@L5{=|p zzbgeU=mc%-VsmKRH5VQ1H075?3Egiop?5s}O?#_Cl`U&b@%t?oh@&KY;U3!)Q*-bI zC#9FXsWc~0FHU)+8{JbvLz_pRZ_M`WM;7j*2!0ELDg%8-RnpE(0ssq>XW@o&{xO%H zA=&gituCYg*Kw#~@kwUjE4bx=XITBW{&Cn>|2^NOISvQn$EYT<5v~HmNr>7BAexpI z>??aCAUdx>10_XSv&d9q#lL!``L}n@dqeb|;OnpLiS}Hd?)eo-ob^dFzG3cYaJD%6 zzP){S?%rgH$)iK425026>fz5-V&fo|!L*C|>|YLbhk}Xh=<2!7=Vwzpb97BupY1`V zv&$jJ$_DlxDAsW0#00SK>gDOhUD_cdX~?iv4z<`Fg;8 z3-e}cN<{G|vPWH>L96|=hYz;Xmcj7%us9JB{+!X>M?DFw22!5JoDOU0FfsRdf=H99 zVS2B#hs+4|a_>o5gWv`~FLk1iYkj&*{YXfr2>QT<;Bd1G*A9;_Izqujsw$S`gJtp8 z7K5q#;0q~q*^s%EHZ4-bnAhc==Spd%~l5~jC$c`#9rrf@3%du zEGbE&ED52ps#Xf$>8_+{QP>x7+|DEKU%DvkX5>zf8>UO+$=D}2wV~SeQ71{dMWB3> z%8X6+;o^daJrdPyUIf)9EEq#kmg_a}t9ODPR5W^&CfX(yw8cU(*L#2h)&l}-1CSx7 zXq7$4v31R{uyzH#@F@HJO4S)Em!iw}d!C&u@~Y=|h9+;RH~pS2-?Iq|Ri9El5Rwr`VcGtItyRrra&fxB*zh*FX^D~c%cae9NrRWg-zN~K<*BgrXAG<)YsZbA5k zJ;Jo+lH9;@lNAmWOeuL4v_qV-e=!1x8_06=~Y12;aa_*(-C= zjc7WXCZ&zy135Bj$2P}X2cd8$e;**MI#vHuzajsz72>(R2Q%BjJzo-1c-2rz%C+Lz zHEX;?Btb?i_~`Wq;ezRUkNs}|mcawx>zj1w1Eswh#ag^{7JVLh=!_BJ6H})q$-FW$IFEc|N(|9Y}h35au(>`XXKs{`Iz_6NPL5CuEcMf=Lb{*sd3U5y2h04fFN+|+ga zTyXS<0RHP%Zgs*xro@59={wv*7A##Ha@v-68nlH|zRpbY7uCz-o6X5I5~%Mq2f@qt zLOBQJP<67IB6`^U_-6Wkx{+1$=!IgB{I|FlE>>d-+M>G8GgI44b^4=8l^=Bd>0@_3 zf0$Fsjr&D^{t;pJNY+ub{G%UE(sqO7(9HS^H(MQTJi5XYS^b3RYWC$gtz3fQ|FQtj z-fI7sw>dX_d0q9&l#pBRa_71NFW)k>qwkAgw z+W`|VE0zIz69zebniIe8&vs@>E~AKv<;kSCpPgHUZ1vbRu!muEmj-TRDU&MR?@l~^ z(a}14u;o&>MA`LSovGT>4B{0Ur+vf!?;?=?7nYAd{gCEGVlmOSs6cS*J?LME@m&tV zo0~P&>wNGlDQ{6xwNpaiHB7qCEa_-n*lO>AQcprtvY#X{2G6>KNBsQdenH#qfwU#t zVL@M|O1=F420&3AHUcjvg;%g{eQ6Q070t23jqe+_{%p?7<{C*msUVdC3unjyfm~D< znt3B>Sn2osc1PX6%^NnkvsSX#)Ec!lgXKM%Ev7n?)JGFUgEL>ykH1Js`E)ugJmZRT z;0(~HZg6sjEUttIyWBcOoB(gZpKvS0(vh2Y|BmAncYGNizdMGr=qcJJflG-8WRd$) zt~!9+mE<&zXrG#o3d5AcDgo2it5Zj(RIQcl^}Jf*+G4~){4LsN6`Ks|3C z7E+CVw}xSp8lKivKfz8>foIqxZCb%g-~4EttUtWpW$ z+h2ePU>o9%FHqvQQ`1jJ;UI%>(Q#%8WHXsx%jjsb;IV}Z<=ux%y|n@W(UaG@)6(@X zy%PMN^om8S;dw%7G9393N$jxx$5`rlwWhAVVp-DM8Fl@aVkz$(EYPRwCVuDdZdZEQ zyX^q=nuZvl@R6V;UXS~?pLU^I=09@>pRd8^Ft$8P^`$Q7rCQjg%Rd!HbI8Z0ZHf;y zP7iS!U5qxX^lE3Zp%VFHiZA%ya3zN!eS8B$CA*Svn!O;s`KMuo`Hd=ix*P$z0@X}_ z=9A}T47Rt(RyH(VG^5Tl1aY7_3AsS53D1%SN?iBS)4AleWhT2<86xN=zf1C669NxU zOr7q!@6s=FYzN9GD;shT$L>BtaGoIb@fV5EChcuY4SYkWIFcV- zrlL1Vm}8Gm!cD|dj=D{e6K~AsB;%CQHoGbd`w=I<^YtM*DH2>&?sHVy;fK{@;vsW@ zBCD|kr^vTf%ErC=5(+d-=MpYn zbXNGcAyx!ZJ+D%O-^7>whGnYD~yeNs>FnUJ_j>zd1R)PbW9o;5YGAwA#+6*$+O4+Pe4c*KP7JbniiK zAnjXKnL+kNA`bYnfqz{LN^~_+2*Y&slPPg;a3~SVmC67UH@Nn;D+L>16c|$nFF*N~ zm*T1Fw>4j`f(f54D=~p&z3FWP{oN@KchRf{ zSlB~o{B5L~U-*Efj5G#Jh}J-x(V(YC<=W3*`CGXfwy&n&)7Z7@iLWCZoIhbn*C>mbrXn|*DRm`bWHsz@#a_VLl1vJ5x2 zDa`ee7A$gT-Aq>C?sS3P!n?jMnD36@zeDPJ6LR!jGB2_S-nsQLbtwB_oY~)B?!g;r z!9gPH>mpJN9aFbuLOiL(K-wX@A$)+*i4|Gw@;zqIkEH0zUhQk7-ot&v>_;YNo3+{o z^Ax(HS+H+mgs1E6?Zh1Eq#RKGq4WsPi0V5g-_C_EjCbg=J4Zh)_-K~BVZp3H89_IFY+r`Ed7VLuvWC>@pK4JUdr^L zhD*S)O~7P|2&kh8;e=>!trwJSv;=z9{Ne_MHa2v@-=$8~V|?EJE)+(NvprDa!(Mzi zJ3IXR@=wEHpm^$xyImjbGX z>VmKQi6#XlbeTbMYPkHDqRF`i;HHP3mAH4)$!_efC0YW(Wdk`TMMj$b?8|A21o{5Z ztG}J?ed|NmS}mU?fUEHY^kk{o*|fDxa!t6WE+>}h39>aWp@xsCGy>JyX(lPxR&&YS z?ve8n$#OMND6crjrYLi}=bjoBe|cr_5Y#b6{N+JUk1pn(Ow!(bvImW{vrEovhk$-A zMcf-9nFedZJijgtHhpkBmbMG z^h-ciK+eApyqpw@pGo<|4=wROwxgr=7CPw<5umL7PWXq+ba3tA9=?UjOic+tY^21* zk}4-lijH=&==LU;*?H}QJ2_06@kihjp^Z2jc-mby>7@gj&tc&n&XsMJQg%!xj;Cg{ zeb~aK`%a?X$G+J-4GwK+Z=JIc@F8a(hna7pQFgtjIm9b@uK9-6>0eRq*MCJhsh?n_ z-t+3!f=dn)Zj)(_791xUWUQEO{7HqdHkB>e=q7oV));$I1<;{007T#OI_|J!4r@Yn ztEP-@x?sOUL`3Jfa(Z}byH?eEc(+AFye(Dmy(d9}=6cFNMiv{KPmMIyCg6ZN;3o&d zbS&bIX8z?&&=i$2TIb&TE5Rg!T5@6vZt}Mjd+{Q^Qb1v6QLPFS1Kwp5@ z^^T2I_99xl+z7l<9)o1gKcI+fZ~ci=q|w+ZVZv)1S40CwhHM-%gAF!%?M}%Z@DrT@ zy^Ko=*NhusiibN(4? zjtMgx5Unqd|1o7&&;Co;Ro#eF7>q!){I7qhDS%xPIOP)Wc8)?K67KN#XWJmF9TuxC zed!%A1Dn+r6q}T;q^LGClKKY-rbtt^n=*1-OSTYLRj) zB9Va!Js6@NC@jdbZUtlj1@h^}YO_1wGWGj1WW*Nsw7nYM6ZjmZs$&?oniPRp9S0uY zGkA2lMf3R11)3bO6>Dd@82G<6CzhT}?>u!LGcm?~{>yS&+a=t1Rvk$EqcH?3^ ze$*=QuWpHi)^R|YRiy-k-lWU-;vQ00+p0)^$KymbpDh_Pc3dx($0z9ML%6=J9Y2Y$ zYnzhyU;66C;6%EyUV)UR6{}wceEF0@(yJHil z1Qt#=fgIph%dUPjFe%o1UEOZZL`2_M!^y?lnsqummDKV!KI-BJK(DA=g}xA3&L_Ba zFulC}bq}?TILXfB@oe4mPY=VB5&mA6%bN#7NiZ5HUIou9zZ+i?y5U*XZ(wVhULMatv*N6bV&J-QFlZK{(`d1njy)A@SfW%NAsfuOTm2>p33lO?X(S0=?_aBTM+ z+5B3*EkF1n#VX)Md8=1jqMynJIitt!Ib(5B+DtZw>{gV+G>65tIsT6SkS#E_LFGV1Tq z!}SKJ1g0NDiRn5A*`8Yl$^O6&)n0O~2ibs;3SG-Q$6J|Gff#S4RU;5H`mi)U>b*Lq zL~**xnEA6}vHU;=wlyu12wo0||pK2#7sU^G;n|*jZ^_!bJ;YH~i zj3V$%(0MG>(|gW4D;vc4Sk-D8s@aObNepS1klBK|=f^6{Q~wrPM9~LFNZO&_Bs*Lr za+W*k4hMY&J8r+|8e9+1UGzHrQ4g-pF6HwT6RS1v56;}Yt@Hkn;4@D6q&^Tq8N+>GFqr>_QEHS;d>0%g zPx(rz1F_p6G12!}PS-5xB-^M(Aj#!3^`r>f?ZYubFz6G z>{N8Q9D~v~f2Eyh2!)DyTV4wJ9Oon~tumf8hFtBNlW_dIJmO{@_o~EXEF6eyy@T!! z3Ua{0a(lYXZ37=FL>&r>mZ%#0{<`$@cCP=mZFt;H$S{tqLw@n_?nE;>PiSGw?eq{r zH+Ay#>WIA!Ri}hqpkBK$&8IN%n6nUk;G1T&c20|poPpE?HT(?yu!G+W)vyqMV9?>i z8Kp5H;%_7BdHYl&iNlb%Z84BgKKi1n59&WRnd{wE|l*>8! zTszDg$`Ej=q0S4u_@27+Rcw(&;1dUcWq6&mcJtn+IYXoAHRVZknYJ+dSF>ZerWLBe z{H`?)x-`2gylK&5w8$his9#qkZbU^U0cN42uX;O#C&HyW#!-oCWs%3$a#gQ=Y!P>L zzR2%w|C|0U#EbUDB4AM#Cf*VW<+d3l3W{TD0{iqnpbU1{9sAMC=xu8C75$7;z?id} zT^_cJEN$;S3XL=ZJ)O+b5HVJ=iy~Zwz9)z^>J{{r?c0m^O4Xk1^b zKX4VU~cBPQ24Bh!II)~GlGv-Ud!|h3WRJ`CRe4d-74j$ww?QydeYw3 zgx=dmU56YI5Amn#li$@SZEJiax zy^^xPxt9dpNWDm=_k&mrA0UWMV z`!yeQa;1BM$Cx9_22!Rf+qmk!3UcKgz8<@fpM|c= zSpZ^s^RUhg7@ZlY)}(31ia=>~YW_4y>_`Or@8w z5Nm7AoUjx2Lrsw-RB(19jOoJK7Sd{|{8RjlCp~~(V7vI&xzDw_d?GNnVl*rBTW?yf zoRR--JbCy{>e7X=_vQWQuct8)dYxQ=@&iMoJqaEuad#o(RN2V0pA^g&)nKpw0y6b7 zHMP)4zNv9 zwy3JEJ=E;5Wpp4!e6_cQEN9I6{K;}+UVJ^*zP$_4w7?A5v7evRTj$*DIdnU?zp$}9 zzbEL`qJYKsFuRq$AUxMHla%q)NKj6DL0(LHBuAx-V154+`T3y|3YjOZsJpz@6sh^x zy4I@tnMNCll-C!>N_uv^FsTR}kp&0)4^C>=TW1_G=NmxTaQnE6W=nn)XCgX#Rbr17 z5bTi3defrQe04}S?xb^mR%}HJyR7C7zN(47X8v$H~F#lri-wfEDz>ga*x52YM1uU;8sMOK3>Y97(m{7~^1Bv=>tb8yP< z;0!q;2u=>zAy%fHr<-tW)RCHHH>Ub7YZ3-b>VA_5<&@od^+lyD$?THWSLd^Z5*y^+ zgx=CM#G_hri z0x>RpRtg>#3rz$Ap?g$OJ|*eMhwl%Q=cG4=g(EBuJsr z1h`DP&cCuanj=3N8BvZX+2PQBixhsYIMvFhHIQO@99T{y2zH&V9YRO8Tz~oJ)|m0s zzyHJftH@sywve14Lc6QI8lt1fp-QJ@zyBPvGdyN(hm>xFE4`Jwip@&crf@7^x|db< z_ZUZL#HBkp8QBQcQ-FOGii!nnBGdFr%p>&3u!1(AF|NTheZK8~!qeb;{g)13%jDik zs!f(2Pl%z=_?~sNinWh-`hP_Br9P}9+a!E7ojNno6%~)-6um3+@vcMPW90y%s|h9^s@&HCFzQ2&(NQ^vboqbkU9w0 zNvQP0a*;hFC=cEj%%|QzW=7oK7FYvB}h=3@@$PcoPYKsCN~@M8mI69Mruz-t&a;2dfGF< z4&n@bcWV6O2V$p;0#Y!DP|{Y?ulZG0#yjH|LJRwbcbb@Km_q-$j3D+THJvMF7WMVg z(z-CCrvfoi%G%&`(0eES?}MXB@6bR(WQ^!~$~r@&yVZ-xKOm~rIN3Dx<7JwdyUu0S zkwodxr$^Q0#qZ<=BO8dtJ~+4#XZge@EBg%6*TtNk%NpkOD0O1Ov>smR`HZ3jbEH#A ztvdk|aXN3C)ps&pm5A{q_3N#8Om^K?|7*z-MQLX~Cg)k_xq~gYtwxMqk)G=MrqqlI zpKaaNOxCB#;fprvxnYf>ileQn`pa3{BTS_Ut?z`*i0_w79UgIimLdpw9vuPJT@!)G z1Gjd;9#P`)af_cC%_K|?giMi4er_I8<>i}Mjgf9|J+7~WC&{q0k##8`&VGS}pp49U zUL3sP#o7)@U9R9x73JEQ5o=fuseLk;U}u|cU-V%@xclY!<^*;=xxG#uxwEw=y&rgZ z&F8-%=8=D*Utd~9?;zB4kA^v?_1@YX;6=uoq$S-+tViD9tzqP_d-yz0VuqzuXV5An zj%NMVJN&1}E&jMsp@$I*<~sUo7?q!j<0+toiR==5wcD_5s)7WjLt`~om)`);e0G0p zuB^Pl0D%4~ru9O|e0do;xue5Cg>UB|6lf1vd7SF>;0aLNkk{I1^&Cn&44+)HjBU+N zw}1!Z+tsPh=VD_&dWg-O;#H;OyklX4JH7e>Q~h)jJbmm@y>%e-MM5-#CA@)T`bl=o zv7p}Jk8lbjTh=I9oRc&~V+FfC90_KA;B+1rCiQ-ZglOzOw*R1OGnYvO<8Agu!sFuj zu!_e3!}v7}Lm}e8kv(sfd^lsUSaa!?Y?S1nUl5PInay_8$y_PuJf#jLE&rUN23eea z;k_WDt`3vY@U^E-cG)DcWG1uit$P&dKda}r`baA6N<{P%iHtX${UG4Jw<<^D&86CV zx0WV997g`OeZ^izmO}|=XmY9(#t()(?`|7~&#jjvqyO5)u zr?boLKRWEqm%aH$#EHKfIU3;XH2pE%zBRV+rZ$}P>cSd`4;7ovRd@^b!uigtjXp)F zh&FnxWf4~)r*Lg?3bN#b$xtKDmkFh(Tk(LV!KKPG4tz{M(#Bx32l^KB98G9d&cI=& z@R(W~n;)-Y_k;OlMaa3jnj3Rv40zxS@XU=TOK<5}?R})U=Ic4@2j&Xn`8xbIt~*o! z0zj|rydxveQ8d|>#QL={@)n3V7BJ96TDT#VxKJPG#XzoCI&m>d5I02X8 zCe~?=Nr#lb8f32GuXk~OJAkHOhZG)qp?c2I%CWKybKG|PB)uu_;upn0*3FTs;v0WE zpojS*H-GbaHfGag+_!1~UcApBt)>QnH(tl0#$v$ocv6 z%U_#9?!KkIvyS=Uye#jhRpJFBAKxyDWYa*Wz%bSAU`v4#QBy>_gx}xw!+iig+itTO zP-`-2{QCpj`GSH+2LNcXRG|!$(`ZarcpxI@1|!l(dZL&RS#Qz$C*}1_fUJk}5aB+g zzgMQWur8{p{9Sj3tT;iWcQT&CyWfqWLKXdNIoXJP^4l;r*~2iAa4+4TPE`{ETd`S< zpxP6?iU**2_Lp%Tdp-{AhP_^FYD6Zgj++QA)w;VV`@54Wl~*0SI&@W@1v;TjrDJY* zn<4`5ik2cCR;DhZF6ScOp9jB(?;BHBl6}4iyhx)}Z8j^365tvKJO}0(53>&(U64BL zK3#N+BVI=D)nzJuCd$&NW4Ba+!=T#-)=431&L|uNJgXA0x%Z5ZYm$XcX8_-VRnJZ{ z4X;(YCEe?Nikm7T;-@~|rW!m}=IbjJ#m3rG+A@Oc`+PTtd~`8n)g*~Oyv02d#I?Ei zS^#?hm(YfGoLJ9K6OZ$uC2r5Ml+t*>IABZrYjZSDf#xJgL?LZ~#fl>U6L}b&`^9bb zwe*imf#!Qpu~|b-UElM`DNj@f7P`j~VGZ}YG=KjdpnFmqqCSwkU!Zni%& zp*DI;4p=!37*SEs)7Fy`-RzLjVWfc<6NK|l&SdcJYMX*4-a_mt&{VO;ts5pw` zsrPR$yQS-hqG-&$jtPMPntDm0FQry$OC*2wKGvpP4L-B)b1I4xn6W~|Rv9rHwIU+j zuDJL5d2i2c>5(03hE`mjD?ez)Op{dcHN^0VWFf#dfVYwh>*;?tKt$_A5O;X+e7pXh zX2jG06Uhj<^#6OvrnP+$AHHQ02zPTdPZTfKy&678&6 zbZROmli|?_NTMenZTam~(7WIEoqCnJTjf%99=%TbmvRH0YttA>IbOMpi}c4mJ#VAW z$(}J&9IDGP#dDbw(cm?jdy2WNf4z0!M5oH2CpS#MwoF7q9vkw8e1M~T>40;NME6MJ z+F3SKUHI4cZxqe||A!=E1KF&2;bH;|9j_^F)DXB2B$!_>kn>-Gzi)5;?1z>0SfR^4 z1%f}JvAx~V=&akZCD93}5m*iJIL(Yxq$r7|aP|}(np{no2(QbU>G@j8XxGPV&8|JR zW*7KE)Wyh0eEyQHWK{JgjP|93*dIkqTvUczuyX8Lc2u#9aErxNpD$#8@}%eB`cuiE z+K1*qf=#PkHRzskhtT@pg#ouOh%Ej#8NW=OWHQ%L;=EZ#`L>i}-h&hKYJ2N8YP8eu z_?z?9pkGF*KM?Wwtz0baL|rW!F7yfHNl%TbS9(M5nw2S5tNGA8FT~^JYnuK5@q^I| zSo|^(J_&r5KD9}SM%Z_S97eq&&=BJTM3_W_*y%C}fB^mi=1kYtm1wsg#|-43()29O ze!3s>Cv(rw2Kpx<;taw^U>3C5Jmr`{(4UV`F|#gNsIg1>GEu)2RKfK3kuvg0`4s#5 zI>w2@Y;vS>-ZKrrQcdO^wCALMFM~}#8=7b30;mpSM{|{0`-tER+E;R&#!mr|0I|?~ zm>5MjAniD<#%my<|4i|(jHAOFu>XlYAL*nA58>12>6}{ zwX?Kh-A~LXW22Nh34_3I5H=s*?otTZR$6KCy|S?tXO_kZi9$p8n*QY0EP1wT?t0Ex zVG#xmN%hckbTp8t$52St8G7ycCX?*F4|TbIMJ6{MUyv`{yPT`;#+Zd9sj=xN-;8x+TpHQ=2!L}vyl?-k_}TD$)tAM$N)jzL=>Qg?MKzY;aPdd+lXuex-cEd%x#1By7cQd{HbYSHD}o^#>o?uQ<&ID)S2b?WOS;10lh@fO_MP3Ee; z_<2l}9+osUb&j5Q0we)4$abu9T)>#p{JC1~2k=w4H$ehwFI059fM)9$Kogs1?Z?|S zioVQiGehVT2VTBlI`sdZsFM^5>{vR>1ms@t!`|;5OfIiQZ?aCfrJt?E9xg=iIcjUx zS-|ppy3x}MHmkiZeNF)_^T!#kn7|i;9ofKdk<5PH348hTdiOEL0yTHf>7= zW$<;ZG7Owhh?igf-Eq`ucikvZ7T5f7t~d)b<{$O6AO`%=I(&gZ?rZp7w&`!v?<8^U z^cNTPOGSzyG2tQDuv}rsx{vEn-q#iDV0uW!AM@RpOQB8VVqbdYQ`Kx~fo} z@bAF>2iBGU^n(;BDl<a#`_!H$ zx1^GC%|=r*Ds`X<7^QNmG|=H)@LY1)`bgo3DRG2nH-VH}tx{-WZN72`^3+5VQuuQy z9?a0M8zT+ThVaR%yIFLj))z?{jBW1r-fNRzv1p86QE~?T*5$$ra?b1}fav9uzVYtL z(fCela-NQOr@%OIefRZyqvkl0O}iYq#`#&GkS2l^F2cxiT|-;f6IsOWcR`)-_m6x-pFA@(8^L`7zJP;Q z=sAR$hD(ALtOyj07Nta<*Yp!WC)*)N*Vu14HFe(#S~Cg0uYzL(XQHp*(mi<@x}uQ< zTy!fBGwEla*7?_0OaeB(&k<=c!tSNT;)}ceH1C7KtaS?x74sZ4N9u8lP+0?gV0C-vkM82L6A(I{DkziawPG z+01uW7(a7lj*&S0&N@li1%G6(K|)HWh%UM=8geAbZ~_nq06Y;j{aNWQ{mn_gw$RVi z1C`MR45r_#iGqac4#}*K@cMHmS04vDTHXOa%ao)IrVEs0z<`)rN{mw z#ERlEKxR^DgIP{(ZB1q;-8-$E{r^3?k;Zce<`7rG2gs0L^>1%U)L@6$V0^r&V=E@qj z`pWEr;4PS7pcg32pMRE~Fy5F@wDWEv%mC#MKBDiRZ+E%0)m?8qkIV8xis&dNv=1D* zle=s{S|u1k-6>9jUA;_hBeGb7rE~4BXTJ|S37;jz{XeX|Ra_j=vo*@#?h>3pa1A6t z@B|CNA;{qF9^Bm_5M*!&8XN`<4DJ$K2Z99&Zo%zq{^y+Uo`?IpPxpPMdv{mu>Rqd9 zty<1@ZO+2Viy{ka&UaDv3tSNf3iPc2J|YRUR914e)mPATKg_udJ4?oXxj3-9nW}Wq zXos!(ZO&rGgkuH1f!DnvG*5yoX(})hWDNiB7s$??ry6;y8JG& zUfEH;ipToqGt|Tt(19V-%60J+MD6-o0$#|U5_OP`0`x5Re1dv3|8L_yHdOmnimZX~ ztdcRANJe6eyB1VBJ5w25FmXnn)oeHxOVpqfUwq~N zI3)v5I8fj+^)@+kyEiFoo|%A#r}(0E$ypMRUO)~3C}I~#Idc!&eQt)TT2sq`LlWHJDY^tVkDqHZ zR_lpRu5uvg?iP2gTzspr9l`dGNRq^pDN{HMlFOBUqzcYiVx z4-tP~ysAWn13IabjXqTa##+~bb6h8S5yvRoZ3PDB8S?<-fC)PRg15rUXMO`B)$5im zrENq6nys~KBQbZ-3%VgS0n6iLPGuzG1FeqRS=0p;6Tm7R0L92X%>Bna*#xFp128RfT~(rwC`_DKNPB=FE?nNDD^XQPM`L3FI9a%jx@rwhexo zxa?ff$bIrnU#&0Yuv<0QZgk)jZnS5|`tk(_-++%#eXi0#f38wT!`t?+*kX)MV zWRF1HSKAK!_jLshCB!7s@{8x%kV`fPeKYbt|LoiYwE>tsH7^VG-38_jQvBt0LCe0x3Gq{Liq5CcXkgLrSNB5#cANv{uW#kqn;*0ZtcRFMK`^Ux}>g^PR`^@9s zb9I0>C6CQ896UG6_CAN3Kn9rs8?_&4;*cU21f0Jp^rmX~l;g?)x6_K+^7#hz#Mh=Y z=V{wk))JohnQ$(HRN)}TPq=q|ijXbskyPT0Gwtmy@_c#gLB7_rR=t;QtksP!Wlzi^ zbg0RopTxWfiiz%vC~P5?U?{3djaoa%I0+?>o_mMl4bml^*PvtuJpymwNB$iU=RKV* zUJ}s_Li~}3ewql;3*l=}o&rZ9tNm~%)bY^0QZtajJ-2ABVXAbI76Ccrp_f^#c9-2U z$IF5N>w;PK5D)!v&hbIB#}jdF&vsGYFUb7jtrxbSoq4b1Dy;+J%y;6`kLOuNGdtsc}TPauQd9TOH<6<@I~;kErxcRnUR5Vc<`Ge_4-&GhhntO;j0 zrRW#57r^-a#7l4LuQ5NNz$IJxo|B~-sO()#w*=b zNr6IUm;AT!Gr{h^1itiS{x!k2IDf^?U;HZ(Z#(6?1N!Gmz-P$#-3p5&xS>WR2zr<( zC!WLVIa4(m&kA`TBi^W}fPxH6l8G$H{>TM^Vi$BQi2dhB!Vhh>!>HUA)EA?TiJVbTrAmU;`7z1u2R6iOyhsWhBZbuvR-Zg2Sj-45FS~0&DWUciiK`o zd(fddck|3Q4sVzfCE;lAaVf4E{|l2_VwHy>&Cmr;VI`n^^4ohl9m+HFht9@KVfFQj>+_NbLXWY`vm$#rJ!wme6fMQK1yT^;}rjtCenTva9 zwg(r)VOiV1{q6{drQPMYZKTofl6A?!Ifik2UXTZa?hk^YsaWR2AQuy%8_h!+{IX7! zdBol*(|6w$Ev$5@0SmeD7^g>sS)r|o?q<^fC)rkI1#4mex7n6{l3(2_Q!q5Dl6gQs zuszjK_?NMSXRDo8H9S-l1REhwpO54Fzk+>hy!obiJh8-~j25x@fv0;~9W9GBTVj0? zVRePL&9zstne22ITigL&>wYg0Y!mVaalhDUbaHKtqVZ^1C%@*yXbev;Qt#B~hm~dh zLlEm^@PFq6j3kh#=-Ub_tCAqf%Z0csyr&c4579Rn+pbKr`VmrZqKt*F+v1^|2J0-+ z4f%T+lcmSv5RDO{$fVEh(@8=Z_Zk(uVSV);C~+*(jmW-mi}eVfLAtx0DG_tqTu~3H z7J~k3AY+macp1~2MHm4ET_5?Z-tliC=_Pwjl)CQ$lhZMBM*B*G>?oJN>37<-UocoB zj2VZ2;c$j2#nNMM4O@)P@cD5d%-ethNBA()d0L&d!!BdVLa2PGwrF|e4J1mGVeWqm z%uuhxhH&pRS}&$?7l}19F<5f%4wg+Hi30WKMJcBS$B?xiPi3H~e!NK$08n#h=wtuR z$f0T(7@|NWC~kOZ@J5zgIzFVEH>BCMBzu?ZAedRU3*=Pt(%~aNv=|LDq800cq)QIO_I7WFy+G`!2aO3_{ggSA^i;{N- z$QGphWlRcT?oZtS0yt-PB2kU6@6+9C2UrFb*8X@=EzpUJ4l+ADD3SQWg2K}ik54JR z>wP%|6t;~?(xp7Ve?;>v&RZf2$5mHQwXCB+!BhZh-FS8q-{V9&6*>uYr78cmjA9h# zZaHJq8-YF}(xsZ+{p{H#BtasBc{&L)KHXw_$}mUu?bXGtTP*Sgy_KL|3U8aEZ9fU) zezP?sMMp$VH%th-q&1fn1&98g)f$YpTRs;rN$Q(<6@eA{n%XN5o8Tn^iLQfNP@M3c z-Jyf8vELU&qt2EyvnGGPh2UCAcThTfP!z%7cij;0WtsC~N|fqeZY{wC7BXg1h|V_; z3DmW(Lvdaz+-nT}B1(~&?FKQsT60yLO~AphY3I?-q3S?fKnr)Fz3re@A#=@N(P@a|Xfd7bM_+w*;uLp=^pg zW6008VeK@RDp^w!To3J!?ZTo=@_}2IyyX~sR@FHJ#%I^5E+lgc%Tq1{>gJJ2Zb6gp z%Cy?bYQ=l45C!r4{z_67t+v}`LCLWqlMR(9kQQq6?fxNB1?tICtDn{G9+uHNp%@`y z2hvQ${>%@xEfT5lM3BLxjYP*s@1bn{Q&sPR`1!?RvjS2o$VuYG33;k0IlvMUz3#$u zSkSisfl?02ZopMvL&j=hZt%@;655MB6pH2QbWMA^DAAGq1L)9uT{KVIbAzO)7Cn0v z0?j(Z#n#R2dlR)p(};#+mq088E2ud`T6@W{)syFv$~aYWo`_pJ_&fU2u`ngnSJxVp znq;A^VE&fNWMsd?$@z(KTp_{4hM@wG9llf{ec&gmzuu42W-w3`++8Z1YEnCw96q*0_zN0#e~YO8*ge_>T|lV%!k%|BhwO@q~26w7KQ zfkfBc-(O55xZ%6HOSpf?=d!KQG z#_b0tUydV?Vc>OOSXj5m;3Lr2utpiR)MDPA&pfHAf(R78dAqX^gk&M-|I-Vw@_Oq( zlpUw^$=6k8Pk^Lm9Y~It#A&1vvZ|azzHq6wwyb#CF%mA2r`3o+G#&Zu&~e{TQ|&#i z4POA%w6j4%dnGWU%))mld9fbIPy!M>`knCbABzRHYp27q0R{w#U4{h92o>3=D#*SA zgmIuHZmxK0x}J>T*=5p#aE0f860()`Wz2dLE1@-z^a4o<(acm$Rd|_<^shT8GTp4u zfS>dmWfFc8cH@k;Sf|`lMkGR~@$1&*hD8Pby`oVGyfso??Q50E@D4JLxfU2r|=h;5>5+Q=T}uU_{S zi(ZDm9>7rPA1pq~G4IjrmhEp>ucpACXOaqZ;6E{+B9WjBSh$rRPKgU%!2%kOd|8rC z_PV&hxdOX<#SLHhDd*U+&iDbC(QWJ%3OdAqNzu5^10Vsn0W}_f0Djc9Us?7YP%tq} zDp3_aes!;?`5%)g0tM&))?)@X0w2gJu3-OCZ7I#8)f3@jP0(Y8YiaD%wW-4OQ0?jfh)}vDOw|@ zCkLe3*J|jMMXin_e}^~G@x_}^>{===#*aKB1b@Ux4%5ETp9jUyunt76RD>T5_w^Z?L_&a@hcBaF*^zs zs_PurSRkoF*(0G*u`;M*Glq43dym@PjhJ#JS(zFsnJ?q@jqEN zFz#Q*V=4|pBe~*X)Q@{;Id^8$*-#YX647492+wf@Y$6$5OK5>(`1>(bc@78d^#!&& z&0HOsh6ne}1cqS6qk3g~ajrP3Rd!|Lu#q6V+vHg?uE}J5+BojD`nJ~@aeiR(30VbH zKTGM(V^6MA4kE<|CbYC(nVeBmnflWy`H>tgeT~bXw|g!z&n+%p9j=$_wOh z=sPw#WNn1T8skpRz^gb%--5*7pY$XKh;mLs!xaTygQ9DlnO>XypyDSwGq0dYNBDQP z)k0gIqtJ55(6IWqMFWZ0JJ8of1PeVBsSG=l4-fO23e>Ue7pnc&zi{~2 z4zrYsb3oT-fBa$Z%=mQpd1Y{izlrU8Xd;==gN#O|(}u%;dIuA_zx%V|IW`>Y58*#i z0{IsuHpYY1?N(D5T!uDTE>s#9M3TPBXOL&UUX0v{SFnI5<#Cv1(6J@8@F&?jQAVLo# zUf|SK9^?yT5E{h1`j%agrHzA|Ss!6U6jL}*rmd!5-(k8%LbbyJxv{CddP< zpE9-}$}e=?$PC3!@e@?ZZ{?kte(pmlEH)iv|i+s$1|N2lg)3~eA3PcHFjrdyrGjr zGB1X+)yzxwBh@cU`I}VLlF6kdyWH#tqoKo7>_w63^^2cw4}w|m5r{gqg{F>pt$8*P zEY0I{!O?Fhty6mzyui!>9b45|$;WyBl9|`ui$K*I7hZ*Dt65wjBy;RWoMHx^ieF}S z5+tx0_^DAOjC8Pf+Cme50mE)QI^JvsC3-~4F1W=ScB0YMW)+0hz2_L_q`}xs-|`(T z#3OfQWp}RQ<%yP)#Bu4_DAUzm4m32!h*?AxpWoRRx4i-Dbzy2ipiTb= zCd$OafB{{0Kh8jEBt1t|Y`&bkelU(FU!!+Q1KA6j;;%F1V*2Yom6#Vu#XSm8xEscg ziVjDEJh$*iF zvb+^VZ~nTU_haE12%DEsw}&Z`Vlnr(t!I6^h`&R6iE9w_Lj6XZ8+&-ZcV-K*M67O- zX=xaYcKUaM?JRp@H~aKM1`pP*DsxV^_Mc2??-u%t;fdX#?oH(&V~ij(^M2B8C;YX} z7;!W^gb(}-@!lw*W*_oI;9BIf%D2OX@xxL%cFncp-OCBpg8|xMGo0?m^HIOS6d3s-9j@UDw2lS^# zm{{a8fjl_Q(b@iTuBp9Yv3{n#W{}%^GA3iG^OZ7wCqCtLO??{AFt2PKDp2^-YP)7x z3q|6uNgdW~4WiH#0j2HX>9No@eoRMw4Ld`4!Sln65qAyaAL5(N_MOFS+IdURZhMcF z&uh#4@9~Q)X6hMO+Us|Mndo(PmCM~NHUA_q&OwiZ>IW=C7%ynHKHZ_sApb*D z-$Y9bTO?u1L4xknTr=W5;FPCBzp8}`aoo&2yy7o%jv>v%xZjz1XFjXd-Pu?79>y?3 zrz!-lI%YG4*@Xs;B6AiKpQ#ugmO6$Gq44%(4I@#GLD}DcQ#(z z1_-9`PdW|nq!zB>J<!*6ar-f}37;lIEYEl@v(L?7h%^?Eg^8+2DC< zr17E93X40G3?Rhr>#jzdq>W`vFG9&$iGNI6C>w^7FIiX%zn+?YQyxE?!oyCO8A&e1 zOD!%|DKr{9ey-%R9}#t*M5C!M)r6au!1I^PuzZVvFA|aS#fCsWsx5mgLAT38LO_Wg z^Ba<1PmyGOVNvl;{kI;SAT+5a!q9g&+x{mEG#gPc29X=u(=Q9g5q;P9)z)8{*Cj=N zyw#h&fF4D~kSccJgHX{>m6mD0m$16r2*@uNa^XNHiei+{D%@QPT7ShbwX_{`>I3Vp8gQ#?9GoS$Z}yc$}5oeDXjMHN6W5UZx+8m;{Hiy z-7g{5KV07X)=gQrsIn*1+p;s_MRD7j+I#8*nsHiP%;C!S4%j^37CvXrNT}U0CuvQ+ z6sK6Ta>{EiEC6FZC_RLz?>h?NWHVbp>EE%(zWUt9upge>SW@wWljx@gvXiDOBx)Sy zo*aGk@7Vt;^;-Ejw^6@H;}y;Ham-Q5mDgT@-shZu=Lc(MD$dpJ$&X`{R)t=NS_8K^ z?IXcWhTL;cK3FS{v7aQhPxz4+)LeyFgk*$lD_SEli~qWLrz_zWO!r=C4dg&e4n(h( ziNuro-exjp--nK~lwTfqJU!-=BWLqvHyX&euE1%qvC2u83azIm>5=QRzxm$OOM^*b z>}dFh`?1szlgEL)R$!XG zm~K3l_0IWDpt4xj%*Amp0oF~#&bE=-akb)eEXcC{!81|oY_N+WXh$Cw`XN|pH0)mG0ADT32Tw745?Rah;hXfC3#=^#Jp=GWDtV=bKJ@@ z_+JiTz_1pZ=S`*{VQVr8AR0Y!UmQ=6O!%|j?{KiwC`dX3TPJcH8$tX;f|$2C*)~#2 zspTBOxL$%i)9~$fh7t; z3Wx-XI&k}7GDo(#sZv)#W8-;*ASZN#UM|E8=pi6gcxr^-+wLBe5Ju)+SR(oIMe_-@ ziO13MPiF}F-lrp+>quM|Ijh3%WawV7nZ<9YZm%ZO^*Ks?hxf{Hg>ApfbADw-i_$rJRnG0awL(u&QhD$hmHIyDV+fYL zoCA8j+aA`Wvi`6~DnB~}?~IPUOwZPn>7!&7Z|MAnfS?mM_?Lzl*t$U%@m@oq{-u=A zVWPRmrtm~C+A##Qn1v|nd?+L$CG#6#njrP6%^`+5YO|{=oeZTF+ke{^k z+rh)D12T*Sk)V`@SD)@7qI_Ub2JVZqqr_jz-e1cQn>qAusjr6TWIJ}mw}93=xeWlJQ@6B8?xBN2<`2~0BPW)gqi zE2Dix`@|fDKC&dEP{Q*P;cvxmCwhbm+@`$Jq=Y>{37Lah(VUhkDGbgS*nIu6( z?)%0TH8`9A5%m7njUyNCn0y_HeQjlEI2?;UGvlGd1U|)=f~^t!(8m5*FOSr*ewIG& z%T&>{;;mX?CCpq@q;bC-Q-jVP1!0I6q`wWbQ6=fl8{@)pEqEv~plss$7aHM#v8Cl5H*G zfIrY@+vnk4XMwOnUY;ptt}#=4vMB!ivz8xnLGA9MyZ%Ta)s^@Lzf?roCD%wshSW%V zC{D@?QY~n&GM>t@JoD8zx*=3b`_;lrPBTP z#r6)YBkn8XKOX|hF+#IdB+%Y6KkJ&#FOq&$ZI^K8`Ph0<{8UeP7xJmRL4BH1zUER~ zB3UR;s$VD@(l6I5lv!IHuBgvB)Wcn(o1`xdP2u9^Db>8@!P)+5-{HsPlUF8bb9lQ} z+?F`#=**}YHxu7Utsh1p0wgeS?dQt?c=#|pAiZXzwFOaemp_iX7Tian-%tf7&NsjM zyg0J5{t)V}dMkoO3Ujl)cRc0rq~xt%vqyLOmR~oBVoS9YKe;|Zu;Jcm!X~6$9GNyX z>u%wy83}>K;9?Rm$&*rxWD9R->T;iwlX6a3lw~SqLUeQRs)b0;LNz|Aw-Z)|aP`uQ za$bK}8&=H6)iJ2Jrz6C#L{~yaoYQ!bAdvkr<#4$^U#yKg1aITpw(!bnJkY_~I?yG% z=s*m=jdnG{Q%B`_9z3Ks8jc>!d7;rOP<=qrO!sCgOgU7#+3G#XMbJdaJydX58e|+M z&Wh-`J(_;%2tMwWOx1hPG^l^#*b1uN6BcdJLgGzRcLz(m2f}dxhCrkGw(S9vnHqS<&DNu(!x{fU|~R)MPCO z{es|&U;KAU(+YEjau^&oG z{+cnzQhy6Y2Ccjxde-1`e%Vn@jp9$?IAz zUSU1hj_7d|>HEF>1A5$4y+ayM9m7`sjKu;lBR^ol0J0KzKY@h=5Q zvkTs_`U*96=h+KGY8ejG{Z~S-6=j|I64v}h$V6<<-YWV1VeFPm@)_w!eNu;@onZ^Z z%l%)`O;f9Fc8s*S{;MA5CSX_PuH|Acuc-2^Myb42824n;F=tg;(rzh_5Yu|F4rHRyY9eUa9lLObMq7QL>2uWw?dog+U8 zaUkPsink|K8}ln-pcv$7dl9tg`0_;wvN2YY+`9ff1ItJZQX_Z8I86}y!7&CS1gc?? zRu#e8iv)@q6$_Dey_~7$$ar^5xdD$is;)h4pD3imQZ-b9S=>nS{67nnGNz7i)uk(e zzkGESs-yi|q46>G14i`>Z&tJl1|!(34j3{na3_lWgAbfBJ4R4&Vs2E@IAoceesWfs z!p$}36v}o{g!B^sdnn|G0{Ku;kh|2~{Fm-kxR#a=!w6vpru=M>yCq%ha?t1M=83<5 z%}|x$!h)>>Zi$g{XaX*6ANBEHWt?;5l!?!lbPp=ZaP%g^SXVJKN&L!eXN_&zM$4~Y z$9H{0#o2RUI8IiQA&`8?sq~@gx+-bBf0QNs8p+ zl}aN2`ki#4{izqEX4RAtQe38aj-`SZpO{Cj)xEf137rWa!}$99@<$Sz(Gi6{hvsnv zH#5`iZ6e7{M!Q?nVW%odNq3|yCdK|m8RBzOKpVv(?TX0zt{zU7ii)8qh zkp8XTlI&?4)*~9d@iK2yCn)UrZ^74iq!-JwdmK9QeyVR7t0XaBZQLmSKI;)x?=XZ% zNI{F&8MUW*H*eC5=ZvRR+e%EB*j=Zrzf39nHK?mD8HrX4t?F671GEuArkLn$rKMP8pPG>u^t zgFV+ms+yx>*NC}cc<&2_ZeNOSNT~8HDCV?U3ym-htsFRH3bf8hEwFtv6ff9!767IgcMLe!_{*6sgA*iZUk}%DjQPURL?Nj0&tkQS*W2 zr9%DF5nXzIPED_ASjR>*`-X|&)S^S{A*x(48L9D00`}8rmBb$=Y&oK#=_m=hVYn(1 zKRS`$7d@DqOO*-LSI}1TCSroYulv~8k>w24@9Mdukil<<@K*QFML;=d(f89z)-h+Y zCB8NN(N%4pj7oA^8_MQ$@ig@W7UI{&jfnDMbH;jF^>oSnD9S&DRH~baPCN9-*D5NX zrVhuTe3+#Y#rne;9AeOX+YXZ#FJb2EMUPvb=xp2k<^S5erpvW;))}TPzUQVj$FPV3 zV_1SCFLd+SNoFWu0P0aYCwUU!b!_RUz_|df6^u0cOjF(~w-hk90cLDR#n(7aA4^UK ze<_b%X}M4({(5(|(h%14Zqb_EbN8R;+qA2E5FMxNFVF3%I?DM=8ZzO z^(-}Ec$ZOvk5O1Rlue%GFo)OplNW3%dHP#D;SF_(ksa4cZ`zMoZK)= zuCZdDEhzKyZ80_l@*sE8dxU)P2D{;Tb(7E3R&emAH}cpc(WOW)r@%q3-g@R+kA$wJ zokhNkK4Oi-#qhTewG_(?xWtUE(`|WSe zf>ms#y`80AMdr%Z&&?XUt9v4{p9C6ewxdR&cb1h~);>zpzlid!srT9!MXXi6J&xT8 zQx6u>chExKZxoH&rGmv@$JO=&nY;bJCx1AG0R=#I$tJ04zuo&7p|sunCqCu_)A5HH zDxZT|=huW}>Jw1Xp<{qgGmudiq>#m^PxcYt z-mN}8TK(>W#>5~~A5?(r@*_CwYtj3^hlK+A=mPpJia;6F(UpM!_q9q zpbDRL_FpDFNlHSjn2uAQgqht~2tB>7R3H8dD{?2rS2UyJqjC!|W%vxifA9#MDwaPo zdaH>myM^B~Wi^VA=+s1W=#*-wx~0@SAb)OIlFbSH_ID)OSqyTKb9P?9d9Fqw!XLsLl9^zmUEbnH}53?J|Xc_yuOHH6WSH|drl z&G$jZp|qz(5CaOolKlgVmt=8oXrb=BPT%9_0XJxN6jo}Xrrl=QEkbs=-}0@>sl0Pj&-NRqbF`@iR(#Gk78P)#I=p>TYq*xRQ*T~&Xzw0 zg#E)!QfktSY~sjVe*n z|MUVpT0$swJu{pBS~Gjr4sK+6ta$B6d&53kj^|J-iC)aq%<0&+1^yiX^c=0)gdeZ} z{PD2!515oDla8T-aKe63uJfpOeh3Xqo$hq{vUU=TBe2YKOP#9ky<3BTOVI1wO(oJb zDv{GyDIZM61L7f$PF)gua_CAU-a@xM`|o@?n@L|FKRbNheo!I>`(lUk#xo5 zH{VB>C51_>Ah<_yMdadIK=NB%+QF60yqcJE8E?KO(MqRJZSWn%KxjoPwF-9rl=iPw?vbo7#Uwa=U8Xj!Nu zOB}@pr5f}94%iW1P51dmVeP7TqQ-=b5u5~Rj(454xi;2#l}`eaYQtg68xsG(f1%j# zGGr7nCgp>Bt{anu-wIxJpzfy2U3xzpk3QFE1K$d|E*<=Ukn$it`0x>Jy`1*Yf^M?d z&)MxPdsz@leMWa>$H#F|$@#}gOX8Z|Y)ea7%>2+?C69CV2j63HWp|)ZiVv=}%>e3y z;)|avW*%QAcEIda6|kC_)m=yM>lF7nn=DMrjnlD-BO=1hMUX`f2`_u+d9SZQ_K$4y zj(yATihTho&6w(DtX$BF#>IH3LfN)3_c&oKxZEw}UenF|W6$JcdQ{_D<%ebw1xsY^ z^1sy`kGI`ZNA=T6&KegvK1bx^Ub%dhO56t)nf9cY0Ee(;?Zp7~q3!Nwmy(^YS`-jU7V9GNq> z$Uj%(cy`Mvl0y5_$(gPXGDEaAv(9z+L<(awX3~Lc>q3c-N(t758G*7#(8rFQ9Ixq< z(uwb)dvpm<5zPidwz#tO@^JYPUi1 z+h;Q9>hHePrRk9H>Vs@JOXk5{efF`c5?=8cWbXBV_v7Y#1&WQvaH0~pB;;F#QRT)? z`igQ0saTSP5}J^9sauvy&dD&*sC6@MZR+;h%#wT`lAB!8s4M9BG_U18-MAkUcgMr9 z&gBE$7A=VfjkC##%&O@hxs;M7kPC6ySC+bz3xtNMlu#Rlao$RZmAe!i4C#$Sr#zb&Y(;KIj`m zxc1+`>_Hr0U}-kgiWt(<6Yh1;fEYO2qHpZ>y1J;g?p+)7SarCZcP ziYL?sLf-tY5ZrF9$WtKWi&jh2sEORjTbC;_%%UGLRi&8%g>(ZgV_V;I-5Nd_oS)59 z`&wtQ8DBhVQIl@zy5%%rmLP?|?k&*x>&@MfplkMzM`^&}qTd5xSN(GbOVn>fjM(^b z=@Bn@M^9%pFSJKfQv(3OB-96J<99V(1OBmsULu4-n(v$-Wx!k*AGhm3%Fer`zXb)N ziS=z*?HaXG_8*5}4XySzLh5Vwk`JHh+3vt>Ra3iv|4}ElFcfxkjT4SumD1X1DkPEC zDJ)vsZn4XA9mk2)|0D*8E);>YBsTKLP|eB zUGI`IvA4%m`~q`HlWvIkYfLLtK7O)H7)K?qH_9>{cfwlP=3D_vosvMQ1wokNcW_ey z$x@B>Ggut&{ExTxmxo2FUZ2dq#fdS0t_npI=$^PYyHFDU3T^_y z6g~sbzybUe@xaOzYb9ROuEzxYysq`%DX=iWnE!uV47f~?X-?Rc@am1y^{#RNg}NcIfBw`4 z>OK12I(a_q8`5fO8;8~O63REP{C@N~1|c8od;BKJ@!Y`ZpuNw@n&76hvlvYy&@}Ke zzapD%UcCn>3l#eSBb-gK9h{!J_%=_FP<;vt=qM4)5>UEozS+(+9R~0;M?iXHmGbFw zXF8&X#^->*;#URNzG z=e~N{{U_k_4OrpFI214>C1L{}P$Z&`6XSq{S2n_J5WCo`VZ1OvZTyI3_LMtSpQ675 zm$@9--8zi}`X{@J6LvV83)C`FpWV~_&20_xaW@t>)#Lq+_!WzbA=b*I{by2Gfv$jh zHXjiAs)8nA9YC&f0T?u)=(!$69S5B7CZp}OIsW7BRDiqpq%7rG0oPkI&j*~?%=+1{ zWS{-2-h`4A$o5}$;>tFy9Xf6Cvj8SW@&sI^i-8eq#of!Du|cE@NG~5&*T2bgrK|Kl zv%KP>?I7VY*Woma!5)7H2eyH-i>bPXe#Oe-SFp#MeRE<`V&!%w-E%4Adp#kX2>+!-z ze-m>>ecT04I+5gU5xaCZ<5fu$6=$C3iChFzd2J@P5rxV$?=f@QlERY0-4BQw*QoE< zvXT*jtrRf}ye|1?W6277r>|{!E*!l5vOT&YTd+2zVzw@QlHy zBOF9?+NN<6QYU*q6dVS3-wW}62`%@Zf?9M5TU!l?8Dp)~R(H#>1_JL;8*st@SuO-) z<=vO<&?IHCpAa8-7qj@y(Djofo+vS1$l8yIK2lu{j{1oYe$osvbMVXcN(N6uK+!(p zIN<57&Eg8Gkglkb!l>>PME7;9i&DCGOLE1mE2n}zrY7S3x`Fp? z7?z}l0KD&-|MkAB***6o`<+Q)=3caDU9GGBcNaSA0Jq7uOSpU73{nR~sx5e>AwSm2 z?E&=l|62Ih=rw@oE6S|W%_!jjl3x0jvehxma1D#JBup8sm ze@i=n%(-xSh~xfnc-FbObYn0}t)pD&I<~oeNXc@Yk~@9MZ(*~QF?V*dPIDkN(6f6y zgLM2$5PiBoCxuCRRK`=0^<6Aw%S@kJ_Jm$A1Q;i5*1-Vd_xOy z<|Z_7tDKfrEVb*(MsY-c{x`EMF#zsI zbQD=_WkWDLg8LY|7{&yokoG-Ucr<=Mav87du$zDIa3*%G&%_*#QjJorGdIls^ozQn zYR!I0=y&7VuXamPeb>pHac>~rR!Fq8EsNb9OxGz{9`_L}mjm4V-4H#9Pj-_ee?Z}D zlH+lIFCcWuT6y~e7B*;mevou#PsjF~ZL&hPa=iT04BM(*zkrKbtt*l^^pz{1oYW>r z=?HVazMbz_;n@k1txme?I##3k1MCO61t4EzLT)*3zq?)O51tQhvJEDi^aReRm+;4M zN{59dwaWNo*G0+C+o)ynN$6Dmh%4>8P~IWjJK&F|3G zaH$*#7xUu-e@xjlunG^AF8NHaz0wkI#l5u(BiwF<9=Mx{ViBW zGVf2mT|En?6AfP+KRq6wKJj85-vMM(ryd-Ee;fJ%4;RKUoQ*%Oi~WsjRLNihzg^Xh zA_zHY^h}}{yMg>-p$WF=aRD9STJp%7JPsXf7jO&wP6Px!yq_qJ(y8+XKK6ouuOoF# zvy;F&Zhkp-a|hgvoB*R4Sf57}v>81gas~G*>NcLt^J}e|0(rac0Hv}W6h@;@iMHjH zjTtAumw$fwLr+sts0;|2haMA`RB4ZIhoDGBUO?|tzH^WGB(5`i-AZ@l9M$e)T7Q9uckJDZTq! zd{7u7TF*FFce-*CE+Ez6hA2E54>-k~&sS=vPSARyEX}UpfWqqI-7)PGw+$Z< z%8J?L`l@ETZt)3Z@f2U7To1CqbeH)fo(E^Y!9AeDT`Izd{yfa zMa=>oulW5s-j$|35iTU$11OfQ15*cZwArUcB?%_(1Az1|^qB|KW>$*|pj$d8GCnFcyZ)c{ zt~DO2we2HeqOcXpVWgtNG*V9GT$qX|3ONjuY$P?#gC?gOc2gt?#VE?IP0l%v!^|W_ zB_vcdLx|LjQ;s#ywd}WNdq2GI`|bVo_^l7tJ*;)Fd+v4p|JVPz@9VmCgH~8Q`d89M zhM7#rQ)fZnBTd9s{h6t{_W;2;tSuI{G*;IP9TM`z?%d!B*^FIKJjhe|+B}hXV+SWY z=N@(2o*k1T0Re4yi=T}QL(Zeq4=DStHLh$+E3j7O*pnaN-dT(qK5wg#*E0^g05GW^ zxd7h2Jbu1CRS4ttq$8!8UKArcoo}8kS$;mG#Zl!`W=_m=-FnHzYA)&rRIt6)(vRki zqnYYW@>2B!oB)aaeeBXt*Ug2V9IZxUF5JY2j&%2R#JIfi>BCo_;l;`O9i-XW@-@US zuz1uVV+Lh{8Zwm5<}$@ODJzOgfs8y+5Uhh)9qaOA)UK9;tr;sx3&v8@H8{MvEZ%x) zd)G*=3vb$m0P68*V_WHAeLwWqH%1ZKb>mlyckb~H=nd>znOhc^v?ls>K8xJ@1>YsL zh!hfuZno6R{0&!C4u+5j6m+V66Ztzem;C!=g5i$0jmZA9$e`U}-^XOdzi4r^3Lq)5 zfrERX0(q(}GI+(HLvU}Ps$Sk3bQT@Ejz;@fx6sRZI;_5fM)G)g#Cm4Lx3z?8jXinA zONzJvED>EL8SCMYm25>NH-|xT{HHU8Sn3X{8g?|!!%QXv$r5p?2_3HY;+AIKOcnJ+ zGez!DH7h~ym|}AZ8HW$-JfSa>ul#WYXsTb7Rk5Jj;9%v1D7NxWs(9b@r%b=vA79UB z?V4Rkm!At@C}f?(vO`gK@1@BmxIQH0%%lt(n}tb4giB3Q3H~y&?Uo zg-#-N`$+h=0lC{{)XH2I`(@bhHB$!r{V#|wq)>L|er8psmIo;FK-3Du$c!kN@xF0> zVwDW$Q$&3)9~^AHDJn!Gu0Q=?;KY1U2N@OZDa}LkW}9k5PqUaT0ZGer$$pW|jAr2~ zOMT^QA#>{9MQvtsjSpRdRPNBqPF!Ll>Mhqh_fflIRr6?)-q+$L#j5Fer8_^>>Sp`# zWJ$$uVi}ccA0!X&`|;?3(>>$3mD;nDv|Z1R=^n3fzBo6yNtacV2NEH5`x8ca0q4Yrf3WPJD~Gsxdaxr4URE6+Xdw56;_I$JfR zhldNnSL}rR(IH;^rUa3(=_{=J1s%f$pF=QtII5`7)t(tKJv2nOxDdY5Ew_4Cfr%Fp z>pK$oaZfzfB)DTnEJ}WdS6^7{42KY>@I7iqEW|RaX{y%Yje%sN*m22Ry{POSUBh}^`!>hUXV?0+p_A6w^$g2EzJ&`}_oh$WSIo;b^t_m>tQRefoj+9B@@&6h z>IQ*RN-irTi&&51DKe;z9$<#+lKqOjj3QFk-u!KG{6cz!$Q4E#qu^@WW|paU&Pg?! z%pElvWp*n%Y`MG1+tE+-GAzuSuRohHjhcBPWhG{xTtuAP%`e#y8rLZ1ovS8waHU%G z#;WnOE2wzB6{qw6vL}<_ny$qjwu$8%Dl34EZv5mqb z<%H?K7{%2leDoIaj-z!JN?Wc`%YNUjq9jl`%dIP71>Qc0^86w2R7(8D?Z;OkzAN1L zdupulA4fP`@G|?RU5*TLDnNbzaROiE>nV3KB!JmW= z-MmEq?NtBIw*;XhjC+>tlj@#oUCR1u?Ne?i4gA(~_~h#}#MtTVPq?c)D+M1B`Rw37C1=@F>3U&j?CesK0uCT@RRQ~_F`h5}M% z&%7Z|c0#dI=W0Iwz!KliS)1=naTHse)R7{myWd>jb!y+oWFtNojw+R;jHzFoAVjW| zPP?S5^Zb|!cdElG?oVpycG^TrVa8D&&E_H~BysMk&gH>>4R1|lhIl77l(rhkk<@5o z?rJSZ-rUMD@D}a6HKqs%!-WjgyX*2R-@1hVAOc*>4<1h*t~9IpQc%P2Fqll|E>5;K zDbwaV3)cuM1RO=!RDAFoa5z02W+Y$;1zng3$sn5{(TypeWQ&KB^>GY;hj9&v}$iH>+e>q#P*~GU?G|GYd_H^Yi zH7vGK#zim4x_)s`ZD5?acoxi*rcFSZ?`AHfaNFT<3P}tJw4E)^hV9)bV6ZOPY3%tA z!v-=1Kp}~s>OB2k0?w)FF$*1+UeyTq@Kom@R6XwphlvnDP_jRUheMLn1KC9+pfYRy6yR1Ix#4At$!03SGi2?6o5#f{Pg#Ob+7GnH}Hc=cO}a`h87oROHYZ`<_C)}?)ew&k!jx&^CaYoHF`fHd zHA@WfsdPr}B{N_a4W$We+H*<#YG>EfmIzm*GBD$acJEqg2hY0@GSM|rrvb3xwrcm0 z+{(BY!Mi9e8X@fofd}p98(vpPGBuhlsO-P2PjC6zi;N2suu^85-uG@d!uPQr_8S|k z)!vUD+crJ7_}EYA!M&*a7FbA$&K%pK{z7Seo?(};;VgaY6+_68YUx7kxbq1%Of~iE zM$sj@PS`oG!&ZOJCVqX~CFGqDv;icE)d|R7GVozv*C93czUSs5NJX4{som6i;fRYs zoI;R2BKp1a7Pg;W1JN?i-hoh3|;pf{KE1QS|5na*#y^B3-Hz@dK;xNo~~TA zw+-VQJ;~>2YEjzKop(%$I{)fxC zph%Q*tm#<}NH?nXq+%8Zx~%Z$ZZf55t%m4YmxXs-W+JnpY9-H<2{~7_Rsl5QoUTN1 zhS+gv13IIHSJ^N-s=|-Li*0^a923mHUnW>Jza~T)|1L>dFDUtndCY>`io38MNwp|U zI5t^Wk)%|>4@wtKFYm*Csbbb6{$oEi{D)ty21|0zg1`-f)H?Kj4YMG~lF)ug~L8)!ro+ZABTsz^=|`^o8?&)S5G8yMlZ3J)AdutKrmr z?xIv0&+c-qo_wWSYkt7F!EJ5gG`doEW9Z44#1m*^<|*4CuZHmC$lU@nUtD;hI&+_r zkxO)*@6A7BD`JQbn!2n10hpJs(WS6xO`LN{IK_kRn7om4j9US=(c{!AuqgPcV%U<0 zKfcElCx;UlqlZDLzFz6etmml|0vca_dk$PP2_~_D&sLAa)J=HoY}k5|PGJ$W#pf0~ z?jlu{YS-t?2m8G^rW#SG60NXCg2ix-j7;o9oi#OceBD?cguzdA`Uqe=pBgFidQ

zm)6>Aoy-a553C% z2QSl%$j~_5vp08HTZ1gP+X~T#{h*AlpvA9a6nM&`)>rDw+nz>5tmMcZzh3J33#tLL zk7j`-A_paSEo<8)1PI=A=X!?eFZhOzMb1Oxmr{R=%Q2b(?G{^_H|9Rp~bACvD`*5)b(L?OX=%U;SqjKWo58fDLY>oJ_R zyv@xBRBf^C&rdENwg*-aY5LDlE)D4aJ(LotC2tS}8nHyfpCr=HxnU+Es+R-k7fru) zoDmuCMaoUafsIy+J?VB;lL2xXFBTEcwQ20t>K}3v4K^Sp`M97S#KlNEY|RJ#U_7iH z-UsBn`fsx9k8b91$8w_yMd*>WFe&DNF!mphfF?q;?WNE+)Y%~7IidfVC~ zoC1kv0X|cS{{=0vO^RWd*pSXV{JNM4DJwn6I$$?(U9=%h_E>4FmeweNPFzJj_0rT&mhI4x?wG++l{QnPpX72=islXzAFFW zSl|^q5sV_hZk0p=wsvYMLLb|Lt-;XXmfC0kzSAwZlXsNA2686BFNiG=f%S;z1MFVM z+z)~U8Ysj-Bgo2JXXhk&)8WiRCpu->D@g&Z4C1cH1{^R2>Is>8N0To>?|qOs8)YlW%J9b&!Y7_)LB!&06ia_7&5bet zr4Ze_UfPLB_ndx-M-a!}@A;M^y-PFVjtabRw^;Hoku#+wy6jWlqjKT%0rV;$V2 zsL^np>jkhF@PKT@2LpUui{D+|Q6Q_;?HavQ9Y$U8oP^WlaI5&_(919PnI3rs)n^th z3M6vRElt#2I?!=?`rcS%_-PS*1;S%FEz~N9G~3t_kh|k4ZQXwmU?E1P#EneF?wnwF zcyUoRT9Mc7CBvt&83G*=cXE(3qjdstilLq$DQEQ}bcUUK``jvwnVarzm%q(dvN;$o zzqx&Z$ef86dz4YO zXx@X_fWi6$(yd^&{KN+a>s3BzyQ+P2PJHms<}xld$A4cr)I`tGH6;%n>0yaDf4`6_ z$gm8;P^c#I?^o*tlpo=;31`xPa4Y@uYU>mK?+*}e8)pbD>Z6Zv+`k`Dh+et$ujf%$ zT*oDbTQp{LCjIj`9GD?yhxEUX$AL{{n(SBd=1Mv-81-^uGs94A1}X41ix)&=jI;Y62Wt{+hwe*=otM}xOr&SIw8L5;b76&ZrRrV>LE!IU z4XILrXUpt3Bh)lB!^L_zImgv5GxXsEWW?Wt>lv~8)GQR75jxZ-`D7#>k#4pG?6OAu zovQ=Ay((Y^+&&z?E(lh!w9MVvyJGy7zugd8-nb8}614T6Zw_+jL-5>LsF{RUk$!Co zIC!Cz!rL4C+MpnwQ#H!WeruK=`v7PT4!&E3eCwVT5=slnhn7d1)c07`IHiq*^d$3q z|6291?IR*0XgjZ3qY$@ZycHgQr+jPt zuDC+ZXT$8CbWhO1w>8OG)y~t?-N83zl`p6nHp^ldGp6p>TV)*ONV>$*3z?%s3E7ko z2x$Z9KgQ$%V}`6+$$()FR4Bz$4Bj$TmIPG;7oy2Up9%%Gld86MY#1LOA7RMJ%zSEy zA+5^}MRbu)hDt={qpchZV(cIPd+h#{hm=rH>=jM`vJalV! zP%)u)&R<74=w!2Y>HK6%WBjdg+I!}$=997=Z>nC;rK_C$kFZ{e@+xppZCr!iU`k`D zFx-fX@bt<2BsbigW z8<$cW^VJ>83~*Wm+XQQ$TpSICq>3gR)LPbi!fC2i%s(VQ+cCxj@AU97O1l?Q4+h-d z<$JY|5OR8Xb+Dj(cB9Oqwk&hXc=Bkp;2e6_dIv9LcR&h$@)Qqy0+ocpsQi-Hr;-Pvo@!W{n(Pad)lY)N8Uz_ZsDUoQ0)cro;mcrn(W4K#;H{;yK#-rMju`#{aAYl|HY%;)vFjEVo zy-(s+C9L^l?FA==LO5Cp8xt_tV?;3|9RbD>0(SYYz~nYQQYMo*cTT`#{>(@s(x*1RxfWSA7=SUoQ+ zu~iQ$2ZP7prinluJ!jt86JiL8lENl)f(}O}9ohu5=(v4<#SK`Qi`ADJ-+!Z#z!oPp zhv;BPk&eRN2%-*dWb>w;5|*gs2yQet`r1J79_houeB-gv=_tu zs~$bcamsIJz8zvV>euo+QU#1sD{n8}6%(s--l3rDSwxpyVGE(g?u}MWus764R%XG& z*|iWh2~!uBGNRsZVY_fBrnuYt{^I)-39`^WgE%F)b=rE>GW$spqNV zX-Dzwie=Bd`4Ax&C-V&HEY}=Uz)Oc%BFz zd5tsgG2@q4(63%uO1iAE7{iy3T$ZFS(9T*Ie`hLKpF%@TT@iS+qhzzl1kB<_r-NWm zK>()N5H<^3lUbF0+>CtS@sHQv=UYN5w_;kE6awpZh_rQ{#>GWO-*nXP?^qXz%t<9n zm$ot(uyPXhljAS>cITGm2+Qrmu&_y*d%*?&d+6Pp?@u%88&~x)Z_kzqcG67Yw`>i* zT|W|j3=8=_Q!Cuw-ky5RbJwiW_GUuBZP{)i%!`{(B@U!MES;a7a4uS9-wDyt*T3c= zBd~t*d(=R1{s1g-^pn~53G9l^Q-L2Q0=Al-xV4{G2FVAOG2YK2>>NTa>t`R*-^}hdZLA8`a+Z1>BKP0SqDtU8%-S|KG1w;LDuJ72O&Bx$^&}^8ZaG_6YqS>`LTId^Z5cRa8~4 z`z>yKt*Q$=62qdc5=Gg6Cs~={$ou$PZQ+F5Q}+`VUM4800IaI>yEBr6LBw-eb7QvN z=!a3Gj|*yV!Xi38Uh)ZH{lAYy=xvzvZH&V4_m|Q>8<~*K6!aOUgQ60#Z5xPbBLpap zD?j+yaqOKbMS0UcQ^NnqC-I5ENA2zG1j2j|F$-+?TS#2I#MIP?<>d#T^K>$<8M`7f zgstNf6DdjRGA+3O>p~vVxW7WvWjy^Jw49_NM?A+S@6W-*gTHfYI-XTFb&%nz8M3=u z^I-v-`JeFipHoB+4wZ#AshhGgZiDYuyy*STLZjFEv9bpnU&EiPKJlmRrN_wEYX0{W z#$yr1!f^KN2061N>A_rpTP5=xm=-QBu6C*?+=lAKTPJiKYZuY|MhvMu_uocc=KR>O zlO*I06*I7kI8;$+I5nh7IjOlF*aq;H4Q1%vCM2G-Rf^OG4ypO0Ar-KrbG3k*2uG(e z!t1{Uoo;uf(X=zd&q55<%*YB_jH&L9a9xR5%(r+sEZy+o{3*UeXNjPXpkUEs7p6kG zJ!^ZneVM=$FG)|&-L#&Y82w)igCWT83EASwI3oK|l=YPlH6uYp*MP*+qWt{)r2T)t z;j-m9QPI^U?adG_x5<<7;x#FU$K9>67btjLTx<&fymku)<1V+`FWx^=IEvd}kGLd0 zN^bKr)~ff}z2|ncx5kTuQ}qR*ZcMU7@U;9>)d#z6quqTi{(7IJI?$h@qKSkV~ytnHVZ-(+-7>Tr&R#ZKx?)M-Xk_GvlM^|+nU}^(8`f;q&6wDjIrxW z7bwtSo1?m{j$SJWF;OVLTg6+fpVupg3TB&O?qFE_sm5R1z~#R=w84$|%+BSr@=#`9&VCiYY;9GFA7x8>DsoGTK zZzP6~fA&-@Xx@6Y<7oMfNH86Nz`uZ{;SD&T57HvHaH zB8u+bEXa<}o$c!_bv>vv@6~9OY$Sni=gjV08|=QVpe?sG+eO3gzv*A-m{AGs>}`w; zTXfdE_tI3Kx$)XVK3qZ&cD)m;!tH*B#bs&J6Rb!M*;h93h1KF7`X0sKpUMe> zD|6muVSB_(^ZsS0w^pk^l$aQ4s)Kmbr%N`m%jf&Z91N`3J5FBGw6@nIH=U3liRt8&|Gb>uS@bNkl${8E{hP6 z7U^R>Si73^K#=Gf;6I@pp5k$0{kIOse>o)i%TnRtDqBfz*QA-45R|F}|ag*s9 zdel{d1CF+)k9=OBn{NI7qNiW6`BIJf;`>Oo5>4Vyr`vWyZxtvnDxCL93m~s48s3&_ zjbE1pKC%Ve<2WvnW>!<_c7k{2Z?ff=Dea80p6+kabOcU_aJdH-vM*?i(zg?csQKfR zj!#-Y3@dG>EA7I|@?{zDx<2B{cjCbe64?RRAO|7a8`&)oOOx2^$aQG@HcECMHzftX zJeL6WFe$g`wz*7^v-o*5i2bQ*Hp7NCeJmV{^c)xF+c87#9BU$pBDCK{LpMOYe+<0I zJ3KXgk33{_?n!a!t&s$>N7nNbC0*LPjica(cvMYPbiMa_s#sl2%_0S0FLA#Q1!+X98(5Lx%gfN4us5}h$-(afo|?x;EQ z1nu-kq;`%yFz5e30Yh*DM@B^@i%>;BAM-YA+2TI_G^xDwy?2hZ#@Lz|ZiDqB^?Ur& zT_zoX zn>k$Sg1kU`(-mblJC%D91P2z$vX{&3zYdMc(6m`IZJiA*!uzSn>@b%@2{79!))+n| z@)mt1y_@!XT~0qv^p7`9?r{nM?>X-U4$f{syp3L=0{njL2OB@+dUkf}+wImX6bU2A z>bP1j^;`&~G$1BegoH1A_d$XWHR`PmnWR5eKggl?A(MQ#;Y9tcXl-Q^^OQSwXKRer zxBvlu;RH`*v1WEHVH)9hPD(vuGtO&k>gu;rd?Qm&jMdBv^Y%F_sUy*<*5Y(AkD+-K zf%jV$7?NRM!W?Zvw2 zknKnS7S?a2+a~_Pd&yy5VJn5+`NN@!tIXTI0 z?IA~>%vu=N^Sb}4dv_QSAz6WCBy_>Kd-%946r^f9UIYPTik`d_P48LS)Tg&w+H3w;YnDzRVyllSo0o;2`bPESkC>O_rtt`%yS zmnnIzl9gY48m~wsobE&q1aF@kbYIeJb%lYuTBZBGxJRPzymX$Z&p z_DkpOkDgCo+G5CrOx=;9sPjC-o(WO$O{7>)^1~ZkWo3J=Z_X~a4s#cVBD2FyRp_`; zZB|AtU6v>&K{7p6D8!QP$#a6KY{^(Bihf8Qc49pGn1d78ftbTXbm# z4vV(1GKLq2a0CGV9Cm_i1|l&3p@kc**O$|UJr=b!uf8UNQ#G47g3uA5J#-}J z?N^SA2p{Rs^DgjHyA$IYVeF!rWN7Go+6P+#C~8hLI|jlKksZ6BXw7 z$u*&2lP4+f6n@|54?ozDZhsDC96=j)jc(Y0JxE6HM_6hh;T%r4=qG?^lFfCREqj)o zlWWs+=keU#{yFYKJlrUW?>I?DiAWzX9n*I!U$aAFJ9ibPBngI%!;FTMpTcNS=KS@? z4+;jtBg8~pO=ZRRxp53K!_E7QHV_&ClJMNWexwO~j8&JcnHL*|+6-#-^>; zWuW1uE95VLkS{QzYV@oUltQ(X(1iN+z;u;YFEIxswt53|<;b`x4Z&C3&jX*;e(whc zkdYTOyt#w^$!6O3*wD;zph_qG;RDVkO>*D^;)@0!zna>6_Do*0P#RZA6h%f8iswk` zi2lln%L;P5pI{ac8cGq#uLM*2$?R#HPzyre3f@koJtIvHepVFTBzv);2O-9t!|ZS* zDk))u7OfP65sa3u$>pcULcaZQacBqgT(XsNrE~WE-Z-;7WV#%RY-M=b7qw8+P+lKm zgiS9anRDx7R%GKjk5b}dx2dOX(sNYiFb#?cI|;N@sFBRz{w&3+YI6-pBqy0Kq~*Pr zqDQ#%{QYzS);6yzuc<+df4in$G&^Wb=pAp2ndz9>0J$+1-!q8qqXG4MGIy&^Sjqw)pS0>Or+($4yq zpZIFygkK&s40bSI!pl`4A~h$K*s#$cu8si|RBL6}!AOygwD(V1r-}D50HG(e3R~9U zT%w_)Td5S5MHsZ|zQ~Y5O`d5~uAkjqC88bz39%^+uMZb)6Opt&hMtxdeReS41=tMQ zsFl8quJ!Vk_=!@BM!W2^w8)mTO}DA0fZCn28{R+BkqmGpKv}~T&~lSRnKys$L`|+x z##)^_BqKwifJ3bR>^-@>51vMFBh*MX2Dt>?S{=v{wf#h-n<;_|*rfJq$I%M^J*U9) zqcvAI{~|YieeyZKHC8}S5!~;J{|pEXk)Y$pe0eFzug8CRvAe=%6kib?7M2^uk6mI@ z%LX7m1a*`!=`f||=u*O51+}(yqSvF{Hq`F9=sY<;F+dSVj*Z=kJlS6A5_Lx_>E_*_ z6Ed%^>tO)wJT|4;?qqi3pFi2C8WeG!xEi?qnW61>PU)n{O*gZ8F$AVoRNMHYlH}`o zjD0@XwzpC`liNb&{4D!!VMD}EUF&_S#hTcU!wqi)^P&LqH?2V-Y$9l*`pJQMMTEaEv zJ<{UE73hNjW(JwS8w^hdmlxxsZq>b1wqeuGm-dBSeDx>wJ2D-XY9{YIIMXwHwDy7< z4Tac|2S2GW*wRpbARm#dNWQ#ACnZ}AAy(<2Ob#HZR}D5iu9V3E)quJx+(XBvC2099 zM!PtY!8)*VimW^^KQ;9+%@;O1GVple7Ms zL>eUlc}Z_)Go0@HqShP0GZ5D2*`*8ui4@6~K;2;wUyc z^!WlsNb8$hFNcbB7>X|TH)livNsR)TrNhKC3O~qpxFO>lrMbEQ!sfAV`KSt0sz_8^ zR_v5rzbl8n#3V`j|9GX9QsKFxebwTl?onV@Jgc@k;q_oCNlALuC*(K#(uKS|abFK>qqh^J z&>@%ZQc~Th2nyo)I2m0`48Od(@1uVizkK%l7t2QL`mdYxrX=j=C3&?o_d|devxc$E z5cGe?xl;zqRa|HuF>f=T+;~Y2p_7;+IB8&yzl@th1J0rGnLuSNpF#c|Vl9V~YTgC1 zqobqu)%2g(iR$+P`LDAovJTv8X9A#sX!srRTr}}>spzMI?@l}vTB%p z9~&_34=4|OX>h5UdRk$m7pHh-9E znDMl4HrGz_e~d+s>!7|GV~B-8oJp4(y(I;YmwqIX4E@d9$_Q_Y@l>GZPs8%x(M7QC zj&}>YwO1rHR|(`je^1}S^e$C@U8lH`%X{$DlvL7ktQRA>5uRblJEA zaqbk^?*cN3mU?OjO5edEtyWK9gV36o>!CVisky(kzxqtOGpdc1=_s$BGibh?a+ulZ zy5$kwGhP|QUT=qm#Mu=3lRBcF;@unkj@OncpfYB5oz3hzuEP{=;tbqZ?+iY|&*3P6 z3%J-rEd5J?U~yuDP!BeqXKU^q?2ougp*%^hE!GD;y3XU8fz6(v&^v8298`xq17}fV z07hlE-kS{@2f^F?Tlj2?ijtTVx4vbGoNY?)gWDBtllHHf;p6mvuhJzyhq2 zWGDC-tlh>;df~1&gUwa)bR36vSi2HWd(;c6B+bsEY8_0X#x!PL1U#LVqXlG|{bk@7 zC-9I$JQ0F#uweEps_V=QXd}w;^E!nBcnj@aFe|t@<8BD!wlD8V^9Nx8iAqC6v{4e4 zBo^+qR8bPp0eQ=*VC<$`Gct!N1+}i~xWWbJG4xH}R#dN_^Cu!>V7e|pt}xG@Um*9` zMP9XiDqEb`z%Dae@GCl3qBpguxy~K)yO_6?ji_%c2~b#OZtIlhB!R2~;d{TnGy5$z z^7r{0$8%HSyi(CKmvOy-nB#hFNS&-A(HpxbMY9#DtP|7kuOfKJvgBs(AK8@7*(*{L zQ3If@9dA`qw^~Mb#)j{f$nx246UbgBujRjDb%kk)#)5NwCCoIUJl)>7Ehk8lqH2=; zlaO&o8W#<=l*`KtX6DoD&}!dVHwh2MM9I&4V2{wqrdU)#K&qkGbjQ#Ty++F7${o6} z8}g}LFH6&bdwlY(qq(+U^~Vct+ZXMeM}gtPuV~#@e>^RF>k)^VIlya)Yg-O34Xbo1 z(}HjpCMk74BUhU6DFnP)9D61bn7i+aL!Mefd5c+rmCt;)c=xr2>i zBpKzju6MV}*OpgR+4A$6GZVxac8AM?xu?eyh9Vr=R_{xDoQoCD&a>oYAHmJM*fV5(||GC7PJ;ca#Bka_-W#X-~2`%bpKF_1efJ0)#1A(rJ z#!O<&_;#J2u+3}1ji1F6uShD^oH_3C550^Sv$L;FIk}9Rg3TOaF4vJQfjcsFrHiKk z2dHT?jxas{MiA2~?D&h%F_VZz6OWtSZN!0{VNnF!7|0+rsr?^0PurZ68t9r_tOmTVDfvQ zAfKH#)V#zY)=4+t$2i9!`?PNcD+^#nX)g-p3C#Ah0K5Z{^W~xBnhT->PrIum+dIctWfh zWcA4bb??uxdu#A@^&6s44{LiH`|02TyGXk?#`xt8TWv9M7|#OfeC+lTr{TYXu%K{n zF%ppPPb3fzND8RJ);>vOo?7hXw+6YDhdvM)I<^_;4Kod@qc@d;=;Z(SE`K#1RBDM> zKvLdXxvI_GI?^tO=fj)mwJm1e)L@xM+ZAAecipiw>wjXt>L4LKE!OHeg<=Ehh~lwd(92{ADS$ZGBnzqlDYc(bng{CM2~Bw8^K zZ7$Bw7^PfU#KgtZ6v7E9Iy)#7H^#*8UurA>lT9LJzX%d^2O~x8-R9D(2q|{CI5%Ir zrT?5-lzq`03U=ek0#;+!FhekbjbD{d~DgjXs+yPEKzZdzQKq?xR~nGjDibF|T&; zNb%*<&wWF*DBOGq0i~9jj02ZPadLN32%YDa=Z!tg?qXue`n#N$!u3WsQ~{B#^g%@P zxH5C*8_8ThJ5t^n52hN*HuNdJ_lbkv#kK8l?|X`yvDsJ_p=NH=}( z{u&}Uyj0@a1!oczxZ= zd$g`@h4SZS6Z3u>EY0Uf&>QpScv}i%x66e**uVi}g++w86RLb+Q3+nxxMf&UEap{T zEtQs#K~rF=5S6PKKO|I8$nk;g+WGv!jj}H#?==f;KgXKPestX5kZuFS7Ko1sKn;-O zal9~5FwoZa6h!c>njUM)x}$N}LQXwUyI*7-=1?*_J3G2dx~|JNLT9?lRrFmI&I2FH ztKA{mBOW+7F}QaY3haLyh9!BS<)=>bZ{BkGH#yoKXsnY9_*{q_uZnLDSZ)AegbitE z#rCzNfTAUmNRm{m-2>{DbgAPyQRapB>711G#b-jXcF%~>>Z~B=i}D@Jm(LWv9~E{t zDAnXPDYjfYRO4*QBOYG;t%peb0^y4yx4lFHTpmtV zuD~LkNZJ=zv9SnM==~YwB~~PHQrV$p%{Rso4r_w~a!?Yw{n3gdNF+&MmKbIhi6wKu zdBRqrQGh{-PDtnitW9Eix=CiyJ)rLCyUMhg1?={OeM9WxJql)iRXPjAu4Q_i! zN9p22jm^=5L#vgojGz{-pm{Bxkn&Sv^%Tp79YIiVxK00crnqw(luoyd$!*;x?_twT zcB0hO@X0*1o!>on94{i+tZu7EnCT7%XgnLWC-5DU{qCI}Zaa(?$L$>bJL$C~zN1;! z0hD&BgEyC(_otm>&FsbbaIJT@-cC#@W5hsJWRTbQCg2Ty1$t7;=M&R)n~PT$?VQBe z*g$m!yO*_f0NesHQtzK*!*?8ofNXK?OWx@0yl}aDg*B8-+v>^QiT)L-oaWh~X=P0B zX2l&VXLbv+-Q=y`Px%M|68Ns$YNh=l8Css5t@}adcV5}9x-=_H`UxoVmwQ=m$muZ0 zdlqeJ{g@u(TiWD6?Cz9bIYJb%W)Ai7k+T2{XG4iKPMaE~t7o4`BV9U*ja36nlC`F; zlCrv%KaAR7baAZE@~L(Rt)$|H#9#y9BpUYTIPr;ul3|@znySIqDtz9)yOeWPJ~;cS#D17HB9FnP*CbLWe{O zJ1Q2LJ^2ttUDE;}YKmXaYz>+X^TuXslfJX{%#c1`7S$a-W>6NVKK^uKLN@3cCbc*G z5%?0xK@xzvE`b$%uDoxOjJ7K#5J$uZR7is`_f$4BP1ati#jFJhzSjZ|=b6YlgR@qDEAhGXK}R z1Q6HrfnjllQ*ls;meRlwMGR>rfIV}ea3iTOfWr`&<^U_*W<6u4O~l@NdKSi zH3pv$qvPm5O2Y}>owqr)&}r_$>ePP zvgqk^net9sPC-EeP$M!H1Rs}prT{tH!3&p}k8i9RC4sWV%5b`36+zd`LF?Nlk2vs|!O^p)q8@(z^_HeC>A#0n{u+#p4E4v96|jSFB3 zo0{5=SkyV*g!y7M>eY@kYylgha+p>;exqXDn8TK{eGAmlY7P+zNyq8YF6%09_BvT; z`u&TiRrN=Vxl@z?K%y?a{aq7|9{7)90-gSLL6iCGS7!niY~Fwq3fl{v6)z)n-f`dT zG|5J%dHfi)T2g0~u<^=GhHmJAM;`#0XDimFR@_t7OC8LW3CGglhbQwFWk9kclhXP> zAa7Fwt|`8o8sbeGj;eN^wc5KoPgCA{%zXrCf>RW=dH>x#_)gv&`9rKfyG)dto~%0| zsE46HQ!q-ow7~ks(F>wXhmw4#vm2&nGn;WKGwb90+D`jhG}*}hQ=#>DXk!;JE+XNB z81Zm{Sv;6@PHUx0E66)nvXvU!e%(b-qX4@h)ucKxXIV7x^|7Nos50AJX$#m{)_QMQ zPmR?fXI!&#pK+;Ocvs%60u+RaiHYc8Mjw!ia7g8JthwD>`mX(*#(L73-&6LdTZ+79 ziiLehj&SXmwX1ws)7Lf8dTZ3R0L&yoJRfcs4JI{N7sn6lmR1k%tKl@$h{U1gJ(r0H zDA=9}nwIfTbT3uUUz{Ig0xrJ!Xq6dDu=87de7}{>B(Vb&-2?;ujzK&eVSB$?%2hzg znzWv8V$$)HNosRlzW4B9qr|o|n>FF!p%44cPPy$KiIzg7_hG}eE^tWd_$WVkldC9% zP`~kJCeCgQ(PY}$RI_t$lfV2;pZbHBXkra3eoF`i{x)9p^gXC{z}m71twNSFQyYjE zTZ@1w?Eq0CRr(u{z+VaT1tL{O$w!QUpN;X~`+4->RZ=*2S>`|>lzLvfs~#07=NZ6y zr^M#*)n5bq2G(AhM~g%8p^8;IHe1$;EH_sw3}4B2U0f2K|FImOt6A9IAV8#35GE)J z>n>kYC{uX!jTT>TF0W&ji&(yz+)jKnDMN&`p69m*m&NNTmPN;Efr z_RAMYSzC6ZT>w0b3_JM!V-%~ep7UQ9T3r}#ThPy1{EMvp&SpyD;e8q6`Z1fje{#&> z{E;*$q=9*whNsh# z*EQC20QOnL7l(rrR{M=&%we^;g|AJJV*UX+Eatmn*KYO(&~h4Tg8Lr;oc{@Qa3m!s zzX(iCi~_I_J#K1!^K&fSb`M{{kPCNV7Sh~9s6m^N;A45YUoMCo zQJjS)mdx2>W`3>o-Sk(1VdKT+)9*5SiJe1#OHXp)QUI3APYv55qI(%(OpL8PfI5W7 z{>EJihanu_Z{8WVeKNs)cnawY!Eqo2xnO|oZ25T`H7cNLaG|eMuPaJ zY3*eGv`7rW{R(I~EhW5E%ayw3R$@_{&qbX>i=_f0ZdW&YR9apep(t`M^a{Z86J#Kl3YsS}@Al)|mb9#cH_udso^y{$U8oaA{xT8E2G>o|ri zqoA|cV=dI9?4%qE8c8m5b4LVo zYSVRBs;VbbPMH~juk#P38Nh0_ zfE?R*WhyOmW@OQ|h6r9Sly0r{68UQQ{MWN3tyWR&-9XkFU20Kl`z2Q>dAaejO?Zq<$q#$vzpBEPs!+@IWVmO6@(xXT3 zaf>~*UsDsQ$1DJYYF=UbnwCWB@J0`0_TG=Wrcj$n&?a`-|A8SHC@mo=RF)!6Kr;b# z6hxjkbIiu|DYRb$>&SLCz7#03r(H4jWbTp;K1EmwmRdDDh5^+Lw$1A9P;4axZ&jg5sj@JF6Q@fW-K6pZihsCVO)Z6eKU5!uUD__Tk({f{>r+k19Q&jx| z8grU5_M zv6}Tz#IBc?7=B?%nK~@gbf2=%Ua%E-! zTooo60QQX5sXXX!J^)7rvo5`O`EsxJ%fK_x_58aTf~W)_hm{1A>YSO825N zRwBK1;AI1hJ`ndB9bB0kJIc z&DbBPcnzv}FM}oLVVNoz1yGIdEp>J*1+7Z_re7|u-1Y2Wrlb2)Hg}h)Uq~BFNluP+ zQD%G7qgn%+o%pRY-jjpwI~7}7uH(J+*AYsZ9|WIaHbc zN`gcP%ddao^8FzK=~Ku3F)D9*m*%RuDwCPZz`ZBYt3Q=nX~PaSYv=b59rW00Ksn{m zrD}F4o<)Y*4Yf_MxU`g};qad#`>X282QHb{ohS61>q#6j0jjwi98gH!Q>nzT3>gHT zXG9$l>`-0FSzF8vD(tm=yh;-9DS>N5G^eBh)QJwD74TZ~;UdZBZ9vII($@S#DWqD= zf-AQ$)892z2R<`X!O1~Mk;(Ng2Yz{WIF@9NE@5)5bFl<%B&{S6@f$cmw7Pi?xl?9* ztW+w6TKj+`yMQ{?LZjaA*@|yyL%#5!rQ6;$+yML+sE@1V-SB=~FXQuz>O=fWX#Zzs z&9*)k)PzjUx&QNetLn{^AWm?61&`cUZcTlPq4ACx+{4G|0-e5t zj?S3TVV0ul>^oNCAMmv?Zv}e(s+Bmr(#p*NmRUglh_&drLL;8%YdAM8q-AkmlRkL1 z9%bt4{}n_FZ655sBl)I3x~!)?4ZO584%v8Q0Z9l!;a>E0s{rM5RK`46Dmu+DZspS5 z?|+E`T!J(@G+UFgO2S)vp%Zv=Ip{d_H!;FQvpMCbRuqd<;LiPm2;gcQi_j6vpGwUt zf6u-FlJhpu=NobJDLq~1{75Y~nk*Ek>6IfKIK-|)wK2hsg3LJ-`Rdw}#hO2+`}#DNqKp3U2FlQXD0`>atJ0$1SAw>_WW&qq-3Fr8kR{=2~hK)P0X3% zwL)gj$W!d9W9-jyHjbvfy=HwI*6Lfyxd#5$0(=dJvgm_U(R8Xn)|VWl!fYn*&7Rl2 z0OF%wVF59ZjVVzU8BeHZn(v1fQE+oy{z{W07J8e`(GEE&udOn{*4$UZ5E2jyB=_{KfMI^2*R;ur5 z^0XlelxEFKjLC{$-_SZBDqNmXh&wP-6SckT+$vU>rU%Kz#7EJ6F2F65;<|}T(9%`x zQ0nR2FD4L5oq7UVnw1$)plI3!x=&5|S+TMqpr6rUNJ9PsTF7m3J*Q_t;?z*iNy8+B zWzTGn-!}EmU21WTCXo?<9cpl#1=)Oc;3#GtdiCyOJ*sKpTm6*r=h7|&#{$g7xq<;E zovLJrzXjSzFd(yMgTY{kl{8a#z_*}&kLZ9dIzokuQ?H1~OBO7x&lP-8Unz>pI*x<* z$OgKctI>AnR$c7(AtJI^%MU#}-q|}PzMu!#B#-C-lt-|~sWITdRSpC&O+CGvFV*8{ zDM7y3ad9?(vG5-V^vH0*-6P;12EKDRZHTYAj8h#9r)Ab33UZF6KYHV~y{Zmu+X@$C z&fWoY6U&9guzV@a%me0^f7*-(!JLHQnwpwgM7ws=UeIREWNMDJB=t}Z@`2QM5sxCV z(p&e))QRU_ERZ^D_?RpvVGq2ny3Iz7#shO;JQreImrTrO<5CJP+DEh15~|CINJ&Yd zV*7QhBde%r-P1Qy_0WKaCu{mw(*c}EN|+h;UAgsp1p$x=F?|_Q_{g-%GhaeT{sm-z zg?za6etFX)jihAFMDR;hKE#Oygs&dqy^0V8su-jNq?i(A9$0157MAn2!tqj<0_e0r zK9FVvs>yp_nV!+2KowsN)LnFcF}OJt!}3|MBrt5_lsHp%h)1c2I(uSu^X2udj4Y<= z5j({vGypTDEm8w$I6-@`WaIJQv z;j6ff_b1Krpt%{S;I%m_oKE+&9owQ(C76b?r>{;0tQ>Pwa$s2*+z&~8Sfib&V*k`f zROXY%)nRihvL6yXBnUwuSXfc;_@FZ@vp>_!62j_0rL8Y~!3^?q>3Yv+!7TFM z{XNbR=(H}UAp#>k>^d-Ox#0fgYL4gf^M`4+*R;0jt>}lcQWFEH z-)DT4!50Dm1CV*0x5z%u$)Ew}^U$EMzm|sTgFhGXPackC!v^Pef|zrI_jXa%BbH4+ zYY53pyT$|5pQC?KnX7+Dh$PayZVmtnNL9Y*U-DZeHEKJI5qZ-g#UdBpW$$KWYs+5W z6z>|4)x4T0HaQf(_c&)4@FB*w@fR1NLc?Xv!Hbr)WS(5q?joVY>%|;bgNVz<>genY zPM?KU5Lw2N>rUOu+8~WsUg2Nw+49;7B=>^}kncyy>m@u}E;u2QL4JTQ0mhwn)PnS{=Otzk)TV~PRV8UKAyoa2y z_WplF{E!36I}oSX578J;?M?#u?71*s`)OQFdss2oXoqZWIFytFh(vbMkUDFj*)6zN zOr&f$rHtT*bkgTEg=Kne%#6P*Ki|fRrB<%FPVhtg=XZVzAp9DgQzvDF0b`5qyh^r% z5K7czdsh+{CY88cpMDeU1M^R}W{WGKV-|DYuiRstn!9ki=}!!w`^B^#@I@bVbAZaP zpb4H60dH`v`&O4Yl7!Ey;5O#x;%$9fY@`1E4qxeZIMToG#Ab%E2$G|yC)_4jGOG4W z+-H*A&HXA2id4GQX7N(M@b3KV9Gg(L3~BDH0cb0sm6ZlUv2r7MgGM(l4jI?3ptR>Y z_4k@UUCxTC>DjlHP#)YA8Z${^&4ORk56+;4x(+r{f!Eok5kjoy?KDaW+o4tk?~;%` zVCl1Gm1p5-B3dD-jV+;awlLGCZ)vHHGo5qH(v+4!X zFo*Ur;Ks1CRhuPTk|BgmMFCq}EVWdBbBK6vXQHi}Ju|Y(7d?nhwP6C$s1Pt0Quq_Y7 za{oT&KMWe80N%~s))=TkFBZRwtZd}HK?w?Kb(a&*6)GM{P%^Fhzu~=0-OqvG(fO1# zP?l-iGr@6}c}lZ&0hrGkjN{N80P*abzPG5ol5^X2YKGC=8pt!R={$!y0(ZVI%Pw+; zEgw>XDxT$Y(h=9jdGE$|4;nNARjuI9EO1N(0RL*si13K<-nkBwv&AN~W{={Lv81L;unzj`}Y z!(VSJ%`l?A3eUZ}{xu5}=#`*q8`?}_F*9+Fgc3h~Qa9f0JLxrfD+%Fp!`0s;{KC@Q zEYsAI-!#hecAPiNG=fOw;EyiXfYzEfR}ehGCfX!AMs5Ks z`+GY_>p7z*>mOd9TADiE75y4f!Jl*IaJ3IY%+hy#N|C zOu9^2B1tTq&5jV%*t{!t9UC4(lGz>|o|bAC6ZA|K)*Nvm9+oRcZ&dkRz5Jd|>?BEv z1J)h)YZkXmu2w46N}L4#)#BE$U>NFxB}xjqv*9jeMCtw>-30n1_495-W?y&a;^uyJ z=i&0&+7giYzJWL0Xm3Xu+_ED!@S;(magf<>0ikYdeCfIye)+LkoLxka4-H{5(gQ%H zjRu-Vdj^zLEWGnKldp+%l_Ttb>Hc2>*#}#B%lQrSTG!19t&)c zgD-(vc(L~tsbXPV@HRg|#F3evb%6(f7;%5Zrp}H{+}wp)8SD2*Z!+Avj~JjIF?^ih zIojw|DOhZ3K7RmG18>#OF`BLY|Gy#)S+_YkEFHVOy)6y?C=HMeL=mzfaDaA1vdX$% ziWT+>wECVIji!w~NPeNKvulFUz9Lq?l9!W<1Y$Jv^gnW+%P>t;+42-!o1or6r{PGp z2_@?1-1r9BPCXI4XS}cLk$OJw7Z(Ut#-GAx&{<~H$RSlb!hPr7e`OjA!#p8p>(*Ex zA9%R%gze-*co5N>dt~N<<$i!!bv0e|bpzci)&F04Ul|tF7rhI^P%2$gG9aOZbO}gF zGbkvafPhMOO1Ba!NF!|kiZs%VSac{Q-6A0&U3U%s)ce05@5g(2c;=Bao_)^QvDP|k zz3;oDQkQVT<0JjG36%+&UOdlNKecuaZRaYL`Kvg3oT@(5)~oK`hpmUUKCisWy5Ag5 z*QsD(D(NSAYcRxUOr-e$|JlFn$Mql2;pGWZPfVc7BpOV@)PXaNLRj69m$MN@ix$GG zL8Yw5m|JwE8>35_#hWm~j4Sx@;ZC+!CtZ`b5S-Sp%PfQ&xi(WRmMUXpzrJ!fx{v-QUSGC(uauwA@2>TC#Fupw( z8hoL=Z)G4IgZcW!admc^IM!(AM9Z07jl_Mck+DhD+G+#SAmNf?{CfFB`Gu>bH%V~G z_~{hCkFia+G2fpynN-E3B+@>94c_s1*lrqOqb0&BGPjZPVxZCN0wRv{R?fJ2zU@sj zQ5ZTXdy#Y)LU~D!qxm<<;QEUx44Z`8u;w&Ij)a+{|1108nSm79XM?Rr*G{3A2d#aiI)7t>2KOMkolvY>HUEP zZM>EQ;@hd#29fVy5E!P2-_Ic;*Svakq2OHWfPgeF_L`HyyYeXlgZb@1UFX8LMabzW zspOx>`24?E4LKezu9E;jQV!#!uOy5gxk=tgUob5N+P`3%lbGRy9Gn<=)&VPGG9kcS$i7QxW*dqP?`a?#o zh#N^JDUkz)x8H%_76)V-sx7~;KmMIE2?oFfsDdf0sd2US@Hx+_vspLg-h4~h-_rhb zYs`XzQxR%XW!TZ-$eDd&m_lJ@qVX>nwI zMuExFg?#`q7dI#m8l5A6YR)4d?7RdHMa!I1G6cN8=?gXXV|8{@E^KP@n%KE9(qiFZ zgs z&ArvQWz{rztHNLjU&{gOw4~>u@^&$<#0kR*RLy%4mAmZd_jXSRWfrjOu1=l?XgT($ z6Qj?_=HA7#M~XSJ0R@iW%8ona)@R2`e`$`-=E=#tqh|G%4gV5%6rb?)dhL60KC5)XlVpa5>#cml@flSE`nWbjy^D zIZj%frFj)+#?&SjeiBZ)2SKbzck1bvp-29GA=Y|GUZ&i^1)HuU6h#I9df+Y!trfl# zZPupmoWkz@;7JC545gU@Q(5Vm?^Ub45+?8U{R*WChoPF5pcII#L7`}=E~*3WpN~Gp zT?#!CehrkwzUVGJpiH|VQ7p*1j)RH)NsyPJT>UP7Fu#$-PGK5P_4^xrAf{{E@>15( z8?fAoUq5JXBmI*Xh<*Mg{FB*g)Ez$4y$j+`&n|9#}cF0|j# zu7Ibx;mSuWj+2IQU&3h_rxysXRM>eZwzMUGd0&TzfyZKTzA6#LE}cx8p5&P~>x&tW zOTI&BX>knr5r(NPssr4>Zv?L^T@=1kqxsnrX#&r`0c3xh6oKq*7Vf~CjP18L1=MDV-+Ux$_cjG1+W-u9-R{sA*+ofP z0wJMVxCONC`1%0MUmdBesHo3y$93LLnury%BcZH`a26-pOz@9aCjuEbNnf=wH!Z?P z#wxe{q~5KTaX8$a#eL~Czl&3Bp)6krRBJQsLw;lEjI|p65MIHH+s-{=eM%7eT?(Ay znIx!-X zx{%u z8>*a%0g)yk3>b*EE>ebiN9{+Xk@~lU_X6+pK4sG=7xJd88D+dsIm1IY#`WaebQzS1 z|7a0e+arLi+X#%-SbD?C`P!m5cK6;>L>NA6X6L;U(Q`1Mqokx{tmRcs)m&&*>`%kX z?WY%9C!LwNE^7MRf5qhne9ZmC9yc{IX*JO#UAlhT6{OJU z$w`YOa%fPFX;ZDnO_HZT;{scSt4~_E_Psh$CLxsnnue#5tnYQUj&CiQERy>ac?(Lk zS}Uc&>W=QPY~#Yw^H_Xa0LF;cl7E912b5(vps9-i(?T6AD8~xGTVKXNH_%qzF{_z` zqw_hNEWAgYeG!WWcQQrHyE957IQU94x%_e^H3aXr@beJA81I#9c4i{9jQV8bL>z3; zIz8?|2_!bmv^zr{~10$i~im&a3Ng z_nP$KN#?n?j?c&Hsq_a7kRWOk`i=y;#kXPVsyPK1<xfq)n(rB@p z4tcDN5?%+gKpTgKb_i0N7FB?+VBCw;r7P|u(ckM%qLnn!ZsdB>SLS^}IjJ~9Q>Mp4 zuxt8^4TgKe9+5#Cz=6sP&YYeD02qOt_h^hE>so22~BmJ#2{tEEZ3NMPj{o?Okm{#bt&`INDiP?*_8VQYdoeemjXs`$=|y z>>KyYeT(N9&&E>F#OV#Bkgn@t1hg8ry#x^oTb?lVT=XX!Ee|zY7Vo>?Dkr^{1{N>4 z&+zNb1~16zdY_Bin08DWIpV+jY2AuH8B$WSe0KeSRQeAz@Ef-7TrJz&hN7jz@ao~s zq}?3pb{S#DhB_MId8ezTosHH?2-_~T(k_d>F{HiHg3jO9lBap^t0K!G2h6AMZM*C5 zeBv;oxgkNI4)O<{0a;9OkCD=L{40=Mx%AsdZ8<9tgSZVCHP+|E`erzl{c?=cj{4zQ zo0S`-;4zt%O+m9QQ6nAj?Y55IDi3frdl=uCOlIMo=$nnMO; zHC8cgCs~}5x61PEy(RM*%+A27nO((oyQ7KFn{@Bp9w|L5L8E45rWsRdOAcapZsm2w zg3ZgQce=jKJcZgxRFFs+efU}1cd1Gvsk~B0+%;m51GnI=0a`ws^{O5N@043OYiFg( zI30H7Mx2w5(g|zfRco`x1(oTCU8bJ{as7-_-kP2;oZC){r~8W+1%0JwUXLcv$NW#E zsDJ}aN)l4}+3A4jNuSIZT6^Dic6T+3%4nLKwaukgZ9;wq3<|JHHJ7uB6v;k|MF;JT z8or$FXG|F=91sBUDAe4fDYr+~6uP`#aVcaS=$gYaGLt@Wg&~66@P5 zN3~{*{uV%3O91lA0Nril^aVgyuaddKmr%{=q=^zIl8ewsy@afRd$JbeFLiEzP+E!SzI1b-w%B= z_wbYHjG#oS!8a`o`H)0u3WJXW;`?eNJ`;v_;0{`rnxI!&15kCz>#SEF`&Wlpdl!C` zmqUo49E|f8ijqUe=v(R3$`EJmeGVFRwLFZ-D%ZBvH#-y`L#%|C_KO0fC-ir9k!_`Dfpo zgcCzZ`u3kU7qJ@OL9npvhV%L^04|kusd|RpM~FD{{R9I-j{;M-`5%hk2~%NCQK4ZG<-NhbDc>pAO>nwBQ7d{4U5LeIa#}Y&KAC&s1Do99!_|apQGq5^EE{+4Sw>e2OsVsyjrFaK7$g#jcE7n3!ve5%XLF z@o2O1>+&N%;^M|~jHG=o(1b5sey-E2+R}d|wUw*1{+H!VVH(GsAQUs9!}MJ+fz@tq-dM0 zdtz*&ZF$BVPc^68T%NM%H`0UZWDqT#`Xqy05E7vvbEZP2A}DD@Rx3$(UhBtHS)|5? zqw>OWNLa7O-kAK*f7gX=NUq1CUtS3F3SEwwPz;YjrGxus@nrz)ojDU>p5_sJlWx1| zItzAl^$^6WHzvY>k?_F7BMf6wWRX2%ZiGh7izv3oY<0R$J5`oRxCI&1F=Dq=gxUG4 zSX|ihPGgAo*~e{@HaAIt>CAX-ZO zhn9KSb=0V8qOa(_&-aeizeQFRt%jk*15EOSVf*WWC}atla!Lhx$dy~yFOL1j{aMq0 zHiYE!COay-ZT8YBhN0!E3EP80wSB}n&6S6rN7k~r?#s3uZ61A<-0@vn;?Xk~1VSxt=^ywF&M&*IJm+f{RFWhnK zA`j+6ey}w7ijiAe8dvyc%n40pIpK@SBWBNDz5c6($iBLMnA`nmt9Zdo*Uo*$Ea6VS zx9@ZWH^%j7El--|$@~WlG#dPItQ&A8g{rJw3cL+68lo`Tu>7y4L=vcJUbTmLhAW+W z27{+rX9gmLMi=pVL=9Tw;lj4_I&@U(7e-U||*aUxOv zMVK#(yu~FTxdZz?_;(<;X%aeQ#DTr!k99Qc;2&do=PG=MMDeir-igQ-$M4?8(u zJbcL)&kL%D#~V0wC1M>-_8dPlKIwQmJ+XrT`V^+Oq*v$W=Ej7Y0EO=W`SqCv0PD#v zeJhO{TVLoH@5;XR>W=v&$m36Xj04sYXMer$t2F-h9Ny7rY`s`t{;iz-gKp3XO)kOY zDAON)7;HN4e(>uEaLV>cx4)HG-l%fPUfTfJ2(^^g=5^oB0*RoeL_u?hql2xf2#MvZ z`*o(R)M@x>euv@>(j;mJ&i;OOD?iaYdoRGnwSb?E=c!iF$B2y>2pCC`(pSQX<=wrE^)cB&4kIb*=^P?kl-ya{3aB?lbWw)#qO3TI@{uY@fGM zH1YlAEWPD1jxW72>fGs((z)epxHY^r7c-Ucl#sHIZ4dW zzhPmU%`EV5yIvR(^@ipcV*O-9#xmIy%m7 za009fRq*K%JjwzA5Oy^oO~3)9R<~4HK`o>WyUcj$U?5-0J1Ptt(xBdVB;^KBIoKd< zyAX-(I^UxJ8x@$y@e*MpNk4P)0Y2DrxS|Mfi+HEuz)P$E(I*a)?H-8qOFq$~wO*}& zs>}p!CESa$2>Sr1^*aO=vCv(a@sAq6UH5OW{_^1V4P0J6?`;Qq|jOu-R^7KUoD`1+c0%DVupI>vd-j5f9Cw}$}brUveko4MA z2ecg*$bm-!ptu#PFszc228Q*%wrLAs#DUK{xv*gE+d=0p?RRLiE9SFXLdk1r0Fd9a zfM81?XH%t=JW&7x>p|YN6%O)UEiaXxOoMzdz#`8ARyd|TS+o@}a2bZRo-brYrlgGF zRHvzrL_511Ip>WCh=|&~3!3RLhN(wjde`0y`%anm+q^_KI z^?9Jh)xMakE-oeFjR4+!>QH0SOin9e^OP`$E|i4vqRB3|PVUor`KAph!TA!_03$dq zBZHMz)GlRb2s(vLOl;u3B^`pGBDetnSu+z&A+u7?>_)0)4^TWjXwS{X^MFVfVR{X& z*Vebd764XRv8oH(8}h$kG)-Y6jaeM$zJ{Ei*8r+5$dn~~*?yo<778F|K{DNq=(J*( z3^O6A`$@vIgwk~Xv=qfBXKVwr(?Wq3!T}qN!~-@=HCrpQ&A+rmgpaQaX-rfDR}yB{ z85(a4Vnd-C?q9P7V-ZtsKW3KJI(UcI9)PV?_s0KzS0VsWm*bC(%RbO&$YY&0=87n8JDxh2M_#UoKg$&pE=mV4P zY=L2|z|qJ;$ai4Tn4GNH4XA=CZl-lc5|$A z#07ZTc|+|yD^^nJn*|)TED8@l17Dwwg=I3KA%~{blJI`K#&wHUpJAQfk)4K^ABDTC?i}bJ6q~ivy{)FFpopEonRUy@SNs* z*l79h+(*xxrbZA|U5|j!`)3rnmK+!^@lL703d@cGAhOzv!82Kgvk+EqJv+iA|9C z7t`mk@Nn1vG5=k8ut%19Z|UY`_NI>3h!EB3dA)KA*uM5fpLl-ZhqV)bOM1z+1Ohah zXWQwot!23R?OglvX!zHv9-@)wrJ8~W`f#Pq;ve4jCO`r|aPz>-9O zA(p}V{hDBs4SsJx1y-j`#O`Bm>6X{aO>+=T$&AmO{&Rg;h$Ud%yIWhQ|Fua_!+`Veh9juKH+p&`GW>CK z{#XGCcmKP8xN1W;Jd+YHZ;brkV=&QA{r}PlpYa27bPH6~)gQBRaHxU$M<(E(1z-;! z%h8(3`2(1Z95;Cvi3aYUR_Qn!SDS$&Dj-KYZ}9}A$B#KV$|=O)e3%i*nyWHML*-lWin zI4rkd?7U90va)`z^Vu_j&;S#Wz&0rH5TUEd$oOY3^`;sT6@Wra48$gh0wJvlysZ$R ztKTZ<+x?+n?dgWkW38;J8Vv&Y&QZyu*#Thj2v95baKC=*Q-hefFeRs14c_XJJpCI6xL84kcQGOR{bI)o128 zhYwa70j}Mq^&po7UWY+a<|)?V%uHO~DTL~=p;TX*I;QN1VUj&ouTK4Pl@pBzN2o4+}q7GpGXVY$QK-Aq4 up9UlT--$L5c$7A>@FD-T{r^xW?UN)$#**{Ynqy^9Cxg}_S$RB`OIfNGg3=K5f_^R8v=pgDl5t9Kp@B&5D2OV zCOX*TX{L#T9@mI)P2>|H}}S#ShqD)9qiZ6}Vvax{Y8s9>}UbON-MM#HN7eTfAE zkCW_^Iq#D!k%OU;Y%$N(Vez#aB&ogCh+gt4UPwm4Q)Rh4e0olapderLaVfNfAo{8W zr$S48FR!~&Paj{G`8V9k+dE4=@_PI1=B7Q9HJ)n)8Nx`HHJs$aN8lje$M@Fo1uo<< z*OHL&3l7+tDrT8-0y04W|7d9v96mZo6r-ToBPV}KSB?eA8D3P|Dql2`#cI}0ijH4--x@cc;NIqKoq2KSR%NVSxud7EhZ+XO=+u0lu zX&SozsL0<)370>C-e&}Vs)?MVN@E3V{ zN~nW*E91r`WUS9w^To9|4%yrYMKMgmn({8RxcD(!pSN|~O~)zgY^q{`-?Hgq7e zChaKQwG_tZ3>4}jb`tQzbO;^mG*}d?q6$EZ&x1sT7QDjIn!d~Qfs{B*MXE55p(Le* zPEX_w#l85)((ZW<`9Df%rylW>sLS$=PuqIBK}9J>YO-pmin2uwNCoN_z%e9LWcT?m)>&!8P_abcM@GWoi#5RN(N|!U6(n!#? z(I+Z}F&KP=zeuRf)RPvKX_DJy^k*u2!7ieiOYN?m7Z^}G|)n)m__=JeQR?`x^cYJU4 zZl&H^9f{J}SMUNE-D+Jd-9f!Cc`jNj?>+>wYJAfBWR_BFoM-&X$Fwd!tblyN&+frj zah8c zi=v5w9k;E|C)Ov{AvQjfkH1ymEK@!cH`9~9-Ik~JvhHJ@N^NE>?8&P-j=D8Fu8Fxq ze`#Oau&+2L?kn{w;ylcf`lMSKNi#grv%)8F6vntCe5zg??HD>5kZ zRB+VFE!rvNn&O(2EW3G;KWfgyOGX^y(N`4|C!G*9Y#uLN+*y*JVNq+*W)ZSzMPEQL zdAGM@xMalkv*El~+@8y_=rRTqDp65n$HSM(;e(30e3OQg?nUZFC^nfkPix>cqBV-E zw_UjodRITK)^k*ms71R)XOjqVSg10q23e=9;Ie)G@I}S8Zt~3&CYz!ywTa=)4DsoQ zPg33E+_T*)albQ7G0hhZt{$;38)4O`*tid$J8V6CY0*4$oS*&1q{^htB>MY(Usd0? z{wY`X7*Vo;-K^m@;X-lxaenB6TAEt{%>*!ZTv{O=QgsjoVHe7H@dE zGbq#39`L-Ccw6>BvoXNK)nmv*Z1Xt0xz4xgLi!}=i%rd0&B^Gm1Mb5GukF1AUtV|; z{Mm7`FS(a<%bV65A4>0omJ{!GbEB${hT(>l{!jfGB$)jq&YRBO?%E&8o}rx+pShox zohx1#oJ(SH<0#>l+#WnIaSm8;ccd(_-sgFQ0Sa-PprCOix?hc#qL<)1@l9$$g|F zm50S#LRX0DST-$cy=&Qn^FzXud zyE(hRcET*mNWwH{V*cKIBY5am9jOgFyfvaJqGsKYkcmZb!~COx23c_?--}_5xkUKz z(Tc-z+miRe2d@x6`~a34`6xvdyAM)t6gN|+q=VA(9&j@p66<5nzeX$ksd14GOKp?% zXQvl@MN5>Mk?gJWnpK6g?$L%d$H*q({m5Ir`@Ig)^_;NOP^DvK+6`no*Y^~3ac>8Q zttAJ1T;Z;7TqHOmja;6Ve$bit_@ptlam&^02ilLTcQ~yv&E@uuo%;eqz6MqL+eTOw zYNlV_f3UsR`R4jlP#JzX?OSCtSMhnlcK(~Iub=BU(3;T7Zk-2UUgp2mD%H~YTr=wb z{TKCwB=)oI2Rl^(7w0?jEd^}7?|WBz&4iqU9-IySDvrwS6D)eVJ^Q1@ZpLQYW@5FO z(UcKG<(|rc%EkNPFCPv2?y|A^w!2SE53R(lG}jZ<556B{lVQuOr*xcexq4EVx37_+ zd9NEU`_7i9-|XPGFRh@vR}SC5_cpg*vR|If<#dS0ek%I3BJepgD|5})>e%D5>$1=z z_qc7zfBrc7cnDvP%C^$Iv_ibZ)k*ix1LiVHc(T{}szrPE$kFtXFU2};uVM$mm&CMo zrYoNl+kuJqR>M|D{pxW$!qQ^eQ$R@z~c(;@9QRWAC5Yq%xcjn#i2?{4mOvw;AJA zi#eN3B)p${7jC>c_|*4AV@BhFXH!7IWMj=<*ih{1%eO-Jn`}I@H;y+YJco9sd%tae zGuRn!opcvE|J9n)13MF+YYn;}KbeOQcypgS9nlZJD(L?ZKa{%;FTMW0bb{VkbLm!T zwo_M}s7Zb7d|@h{BysW8Y&UDa{7GdeBS$d zd9)|la?>D4B{X}Zuy3>dGtAR?=eXU|Z(fj4V*iKiYWPIFd~U78ozvmV**(VNq38)l zGiNilHY2|$XR!UN3B%RT>FM-~YD!4Ac^8szL*{-d@;k#JwJ#yiHO)bSzOLlDeXypCfPxjwqH(t&y@VdA}Jl+T^gJlEDAZ)02xK6uC_mkEba z`aZ&gyhROkWVEt+C17PW|2V;t8hJcZtaOFh4W~e2;_&W*%I13^jA!-A**C>}*Z%0s z#KOVtV>hVfCM0$?O{5(F>grn?D%+^5Ls-FYObAlA9RwBpLINKO@PR;3UW7p~z+Xb} zk;_B=&r#%zJe2?ZM)g3PD6K22tPK9@TDe$TJGk09y6G&WYlEg{?DP!X4As>{tsLz+ zEgm{rT622YJ0UKCz`aDlPkU=O3mPx`rw*>7UgC7W&kzN_5xcqQXnvpK_C%b{P+f~g z*3re9Mu3x_lbcQgn}&u4?()z^R7XzXpUc7j#OZ9^+?+(YxI8^QIX(F}9bF!A@ra0s zaB=f;@$zzjGdNtm9o#IuI2>H*|1|QScI2#Gtz7J!-0U13Xb|mMSUS49iPO;`ZuH-O zfBI?dW%u_^4zB+^7I;7|#1SqYPHwLMwhb9sgW;&(+#R*3lkZ=qB-ZeE+%l-v|HK3xD6z=FTbDSkpFvRG#xRze!2znd0vWUI)77O#m5=!tS5 zs?vWqng__Gy2W_TIXS~n;Rab`2C4sS&^xGid0&OlE&sb6OlcZDSMP|L>A3o>$qV@Z z)YcMn&~B5_$0TyecA{Z{zg_CNixf3X);l!t_r^2 z>tBv^OZNOV8|h|<&HzR|$ed0pkzB2?xAp}DYnCVojBr?k!*sb3?Q!FuA%a6M4s0Gx7O4lX`tlb_UhZ}WukJ1P$G@DsM05pd28^nk{RFQS zc$P2ktZQh8Gx!wipI!VEx-k4T+xXMIW}@7PZ)2)7sobpH6#wU!8pSYtYECrl+fYos z&(jt63*9z8?r&ALZ(hH*|MuE>xo>|=@@Bow`+k*qP%^ub&gy7(+1fa%+iZig&(CVB zel_j@@YD%`Y*<~N+Fp+;w-vF$ty!?LuUtTh5i@EOcYlw-Umgsc9RPAv=7E| z#M#R9D+nK;REs?QTm@bHGq4WyQi&Vy39SRb43~ihh7+I%)jzul$l$ko@k_|=qnu>6 zV>AiJ$L}%Zd@AuUaR&FzDOR2Pb&m`C*T!NbIw7CyBEdv6r+q?1*D94670uHCJcM zp4n?`thM=H91<09TXfyzbWZ5u$<~~LNBU{wd^p38j?dV`0?*+lB7N_Byd4=24RgAV zk->N7@kA_78ca;Bhv?F@JdxloCmJ*+O>W52X9r`F-%tXo;+N-AS#+7iLyPQ-GyM76 zi|!eTmlq%k$DY<+}aYOcd9mLoiWUdT(#%af8{MGttme)pSZyw*;) zKPRHH$OZMTX-h*`NWaU`=BK9E>r^5pweg?ttcNxuT=^5b`(ntg;ze&3!>JF`z1a+_ z^1dWTlX9t47}x1-|C(#PPb2zhG^^D4OHhL}!gg>XUcozLYY)ULAPQXjXg7DUIvdT^ zcK2+5W%9er%7?*8O-T-DG+7DC*0=h~fVPWY&F7I4Ikqa{r-vl(c9V}cCKbE=&o_!+ zi2Hg-ANZtknxN{k-m5UaH?|jx80J}(K)T5a#vwmo`IHLeB9oEPSEnlO8`O6{X6+w* zt(^2>sW-Y%Is~19`=kCb^qlC?FnlFSg@o zYi4DkGbA`56I$A~{p)+PXfRwh4Ce)tI)$0^Az7EjLlJb0Ld{%>VJGMvzN?>Bo?_&-bPgM04}bOfQSjAo0`cFhgN z@%dX6c8bj8Kc&h?`^SR0$s<+B+c=f|TI92UF#*QL`*2h#BoCIpKQNZwr152bR{ z)bKbj$@zX;-uwLR+Ck3EUM`r&sxM~$`eJ9Od8e(cK47Qye(i&cHYY31^G@z7Jyq>(SKU{nIZ_mCMSA1@?QJ2Xolp)%p7bb-=0gL9P8a zK@p!r+mcKBxfUZWHtOTD~LrrBAH-o1{Y648+)SOZJ= zUW^37X&L}`@ZN{hD>5w?Nu`63kal~g{pMKMj8?;ixdhYDOeB7V zPQl=waOT{jW_@{fmMEYwg4p=zG}0d+K{Q>sz!a?f3ExS%I|F7 zyWGYVe?QI_pPZLLMj`^=V$Y~R4oeKSrK!=l1fy4oshYe3m?LbV^A4CEH&}-Fqv!X# z9UZ&Fa6Rr=9$&>zET0`tY6@uR*3YU^HK5x`iBn+ew+HyEj{zWcse!j2WB?pJ95|_W zW_*uE55+iLgK9Jc`~Cb!UkYh%S(dRSRZhZ5q)@viV|L$!az8SEIvJb>Va33tF2gU< zzukUy{Bo_jA2w;F&sPTv83={Q7-F)14no0jqn0_`Um0qO#h%=pDP055t+bzpH+JFM zJL~BF{qrwN?rWpj1W{_bK_Zg0O0ff><~2EOKl#n6Pr&<>JiLZF5)_UZ<&#(AB;KlN zse|ct=;vzt)847LUq9`tkw^}sMir!Uy}7=E(X$hzeNva6jI(Q0FLxPColEcMa%eA)2B zch7H;KK75A7utki>Rw*&b;MeEE&4<-l*xBX#QAJ}GBEpej%0~zA#t(IC&mfiHRcfV zi3cO+l_~I~2*0c${FCugHT=Dg!>2d*jBn^9rasN#A7CC%-*^0CheSUm+0X_Y-3Ppx zrOvC}#cqn?lb3_x%JBy+pIVhB<8Gi+?waC9$;E0JyvAvCns>xJrrZ``?pxDGRFVM? zxICu^!TLJts1D?AApLj>@PVChS$tbx{LKr~R-cXI@qAhPVyyHDU%ZhR4yilu}jg_;uc2 zD_0w?JLTW#tou}EFhlyW*5SJ-u8myLC%3{2F^_Fbg8BoA`}GdELjI%tQs&FXWf>;R z?rqYxOIciq_tJx;F%Zpj!0h_-tH2nknI8e&Po?g;R$p~31i+FD3j7o<*0VFT89+oQ zIK_2pA|^it;)J901ED9JE>XiK%^qKzmp!9NXnU*$mcQaBMRYf(ATQlo+?Dx`lo)9r zD(`QOx+OI4XCOMaR(HyYkaWc@uxS&?aNgA z7_QTU`X{ulcxN?DWb1-UA%>n|d>rabFux`D?aw@qTpI5vqIMBwL{i$^#>BmR+LW8g6t8|MsB9Csf zH%PLVA8riZw9>Mln=O51H+nZP+vs}qyiKyN{KLr8ueqz&ZKvr{gGtOnFOQjp3+9WD z7&uc;WXmPFrE@12yTfmtKb-)ms6e4(bIwvR(0->a7+~5Y3W3MAk`6?>z*CV9(K9J0 zF$sisUDfs!8ZN1A;T=}KmrFjrk-LLcb3Nm3gEOVL{wPl3>-r;yNULbVYD}9&M zVV05u_MO+4mj`2(SPWt&sY;X5@#`-g#GRg)IhFM)=$9MP>JKGO`>q>YpKnz@X%+I= zHf$9-PcWX7_1^8oI5JHq?j}s~!E^lF*IKSmQr(gk+>*>?X39=m93rjlSg4dJ!`{i3 z&e1~{d|Wpg3py#{iN=?lC!rw0V@`Or&q2$%&K&LASrz|9bUe2Pgk|S9RsvtbBQTEW zKTnnFDwaFk&*XHHzcDrwwCFO~kt-L@6jmw>b)K92PqnC@pyQjj-Cv$zZ+9h(@EH+~~CW=k;YMK0Y zEg>$ss7f80$WS|*4jAY6nn&^5bFJUv(2mf0@L_)0=oZg0hJ@z4y1j*m?J*h1W0GE#fY_X~Dkx zRQ7h469?=a?KX{@MTz4gP6to=Ct^j(Zl7)%E-#i_@fOU4YCk?!(yQ zq6ucZGAzaTSIE-6QBinZIP_w))8!HZ?@0-fVhyLF{kC5&uTF}=1*7);6&~q;Vx{p~W*Zgyj9vOLsF9rk>>^gLLzOQgy zh!?u!xpV)(`0?6Pv+R~cSGUEmp2dS*ElY}11#7$2{ z3#aiihnjyrA<<-F56!C=!MHXY+3{)RN{rkp*T*KdZNMdbF!ya9dQJCZE{R>T{OySWnsHYT2WXO9|r1o<qP=W<@X?>D>w+<$@zOey4b%18(9R41%Y_}rpN z(E&4L&+0Z+LNSxM~{+g}WTNEvmpev1h6Y32w&xA`}(eviqDtEX9_ zS#ES$`Piv+JI)y*32{VF2kf_*BLBk(1fm89NqLQ|+>8uDaYQUA{xYxNNH9d+M(Z{a zk@8>up;Mk_;o_m?b7Trc=K~@7n*Wf>s2D+kq#2jGSaDp0y8pUB0~v{nH#>4g@b|+8 zV&3;UYbDmB?HK#{^|SMLyGHRD$wmfd5uY1)@B&MB;V)&4nP2BUlvB z89z4X)wI7|w+sUN*rirE{xTdiZ3KWDc4QpAL{N)=HYs+%gv6i?w4nWG4+N`62>Eu# zUPVua*?;3o!U#05uNsn3|DQ+`fag;M5Zbn-2p{~5g8|*6<0GLtAoH)VjuFux!}J&UZO;LYXX~B$vR^MbkSX85QJlw9CGu3HhwWC0Akm0s>4IkUbCBoH2 zjI2~GQG|A4BsTRk(I+!+>xizw`5yrKTLCf8akZG|ug~9XQ2KYn5BsT~A&3DhylB4d zwNsb#1tB z^HbSx!VN>x>hwzJ>1QoKbQI-&Z*or>lekc;e72VD`2+B-DS*XQ4o1cL0phJ`{-B#G zi*B{snQc|8RpX~l_M039QwJxAj5tGS9|XKy^UrQti(tT*r;e@%kP= z9YGIZi%YLV&4_d6C6Gls;2$;@+kMyyb0iI{i9TIZCe)?tIgU2 zUckLwzBIX)iLkTiyiNcl!jnwJ7&2OD%uJ=3!5Aai?6aGz^9#Tfl$wI!5Jb|XcVSk~ zeC-|)=>#a`Ovl#;M>A#y`K@-7Ocjz^Oh3QX3rP#?+{NEDe)llaKa29ux~rC@>65oX zkjsWkV;5lbQWat;2K`UI7dKK$_%&Q3J)LdPX41@IhsK}seQx!w_n}81@6%V$7spUW z95G+g%Ay}7Y+`Ly7}eN(d}|_BGo(qlt89L8{PUdq9SHb{pADsPk@{iK4$#ExRv}lO zffx;KUYwtNELYNOG)shtPJ(})jve)m+}!!m78<@n2s-Y^9p)&NnXiC5-PvZD}Aj~sNw^A_5-{Y9nDpc8TfH|cHl$hcuF7c8KHo@ zND%l4|GN;49F5+1a+E~tX% z9#O@5?@h^Rw+Eo*_Gs-szMeR(#u+oNI|tS;%+uIdaC~fI#gV z*1(c>xJh-=bmIvkQ_B*rhVOQuY<{`|F-DD_MFH559Of1=V^o+UH-HQs%e}R1@obb{yZRV`e_s5gM^<_ z1!?4HFNblQMu^bo7c_WLl;2wMli!uZc+0l^X@ykz8KK9Vh3ndAMLXBwV2kr|7`6yMpKNlz zHqy+H_gEJ2k1Y*V5;dXC{*E}6^%fsZ@4*AS*d=) z#Q7i73J)^O=)&wG$RXwr5y4fDS-&5K1X<_8z{k8MW75bf$-#SM$gf%O?9u9QhQM18 zJ9J9mI^N3U&qhJtPMW0&x*-Vce+NwDSDcu(5L=z+;dhWx)!#rhfFIP;q;2xJ1k4(1 z3*anY&B^ee$Qa_n;D)`MA@~v{7Cd=qS9r8}3#B2L!Ja#5W&zb-x@9AfIQz1`39oK} zl^fnTcNW>e{g`5rR;`$+&A@z ztJXd3(D$U%Wl`xk`6npj*?+}4aGi22!Mtcf8^W0h=;}Oo=9+MSL6_E}ah)HZhh^5b z?|)$XdKh;OIg$Cgn9Ptosob&bzz{u;E4$`30G`;$JvM6V$)}JrjJx zt>Zn!V`fbz?UjxIQo+F;{-%6bsPYBNcN5pkqOT7%WX+7!qE{*{wC)tm$xs_VYtlIN zr4EaSAs3B_6w!9WYq^KRW2HUP@ySBaTXC=6@>ro4DZJ{XM%E_DC{JDx^Qpw`zvJ{DtjgCBlGrrJV zWfoK>Qh9FvrLjlJ@-~;!y;*;Nrx#Jj(_85-AMDaUU>Bg|rduz>3eAm6+ZbX(f(hB| zrAy3L(Y<(R4b9V6v1^FT?S0v2gEs28CsB&Zx?j8gKXmVjU?@IsCENa1nnp#==-SLKRa!LaPQ|t4v%(+D-MI z+bv1z%yN^zd^3FYc>7DuRyW44pl~SYVgyfO*6_&bdYW-joec8hN$I5x&}6zt;L)nf3d8blv0=Kb2I{O4$?03 zJru9nVZS27_&Hxz9!DSB0Igx7I#D3c3(`a1 zISt-s`BY5o5V~T#%CdmIO5}laOPMeEqByePsGxu#v-t1zuE@(<@u zqVK}T0HYGh1l4s{Sr^-jkVI`eTYU)~S%G46vkOEu5vm3(p&><}32@&5G(A4 z1_!{s9zU19ojKYKnEokyJq9zsz2`YXj)q2Pnr()SXjhU@{G6!$;-h)(0Mj{oMe@2PF2XcT@{s%U&-RdqPIlL@_^;;1ai!g#n10sCM zR|=37y&4;J4zr%noBL!RaBr#&T}aCc&Ui7E%NyQX_F|hDjf8f-F*l6FG1a;BbO4a@ zW^BYe0G&FBj3}c`E3XnKv#1yZ7plYL2BKY-Kb`dypyum8V(TI%&c4AXXmHLi^XR0B zu+D>M*w5Xi{)Ynz(l4XNnM(ni>0#&q zBC}bsppt2plYmF2cA@z~SUAWsW~ztHWhTwtJok@?zJ2G<7x&D`1mPj`hL$Iz_uUgRo0wHkDJ1WOv@2GQZFMT& z@ZrgqM%Q8qk;Uj+vW(BI*Zj!aF4J$}-CDM)|cb^*&-5KCfsRsDV16q^g4CG{oEZCX`UUXrLSXi0^=`tuILb-nXk=P1Z zvRR|p*pe_c{^;U|hay3zy+69w_2Pf%J$vXuU-DvYB31F$^O2TGxDuf=YHMkt<{&u!EmOU$;!eQ9|3XMnZred6|!Q z?#FIds3#}4%YdF-s#JNDnjX3hH-4tnI2?~4qUhn_j?{rNK%{P_vX$eocFqRSm3lLX%% zM8TH=gf$Q}uMx6#g|4Nbdz+Qef%Lh=&?`g`3iU^I`7uD?pLoT^RP1m#9plW(`%1hx z4wI*oO`%ejtm{b>mMPB!*KaKnawLE}G%4tlGRVJ}px@~D^j{8H)vS-pIp{x+7`fOt z{S(&y`PP@*E|F=l7Eqo{qk;=n728EN{86ri*T?}i9)z?=STfB*FU6#s9 ze2LAK2PZx_Yy=&{#g$%FevGTlF?X{npLoZB-aJEoDQN$(3FRpK03jv2pEF^Tu@CyWY=W?Ojv{95=- zkO{U{%$FtNP=wlxI@&gB<)ER~6?fSA^}T&r?&^p)?(}eP=&oT3U5nGIv05Q2U(&?O zWrS!kf(ofT?>QPS$b~R`799GL;r%76UDJ$bNoZg`mx~t;Bo)wYRF`wZfNuslF?jp# zoJ?`OB?EH7)k`B_4p!0;CA|Ovm()yYuLyf}#F2fqgb0&RKVgjF2u9fvB0xc-j_ba@ zJkad)X-GG3bz1CdntGM9>ob%q+5VQmVY;O_y+ zFAxz<6b@I(tpHW0n>TL_r^v@3MmmC35Q5HNEmbdR0(wUeZk3WR;<{?Vb;5C*T>o=mIgM)-q>4hk3g0 zE_8k^2%6`H!6$-)qiPMSEzd8qMO=#xe||G~<-d|*Aj2+8Io)b`;Po&~GiHQ;4>8+< zR3ULfHSh9_R-j)8>{c6e^+fM@j8^e9-gz~$q9iOw9?BT{4J0+#fnMMpX)85gRS-mz z!E&e15;RLSl|2#gRYO4R`4A9Wta1dJz=YnbwOE0%%ZdQHkqXb+RK_bm!|Q8go@2A-^aCyg zTF3OD+2f~MVLl+v*4?4m&_P2^qM3Ly9+?@*XNMDt_x!?C;P}ApoytSn`vFC>6~YiO zvsxdmG@2_}sXF7aGiL(Qkc@!Z7{^dpMmpM3z)}0XY8Hfo$Vf8`efApHa*}cb&Xs9C znEGrmiY3}&KLeUVcj^%{Lu#sin$vMVJZF87uN03L=s`$8x8k_&zgN4F+K!;2H0*By zR?z3QN($>c{|k@reg5FJmtFu3?k6vF9J0J6(koGCO+!GvF#rWrQ>@V89S)t>5k6YG z54l}_C=F%`l=;s+r4n(GLt~!iRBQmuW!VYwnz|3nXPvf{`lEK0xwI>bBzWBgQVSW6 zauR_`D>R}L-fATPG*ZWyo5;0NdCGS_%{n=gXff4Jva4{jI*)8Cqb%4Be0d zJ6$b*{rrxPGpBw%A+q8867^Sh2MRtsk90X``qE=0u;b~McgjFgjD}xi+iE+WheW;m z>yAJrLgB21JWlMDryw3q`d;0$yFtw*=44QAm4+&bN&SsKYj(!Z@%#{ez~=^B9T^;v z#gX$Vh~p74Ya|1Xd6rynJSO%!JA?1hbOSe}Bds$j(&ou5e()H7Va7EU+3OHVio;KG z)Y>?5Z$OAMRcU6nI`XpkIWmAFhZkVn({6K_KH%=i1L{v?N5=E#f{t^urcH!b^xR+X zpkz7W_OK{S%MSQZqW*m8*j8FFbwP!Yz|mxWbkwhP4vQcml zjh8NvxJve<$N=^4X1eWah%>2qt_Od%MQ2z|o|Tg{a! z7jSkuJFM3YU3`ZGvKSARMR2*;$AIcpjtgmupe^9y)MP0I!Ns&%@b`p?#{O2OCNe@Q zmZ(lqDjDSpCu4F{04&P;L>9KSfEw-K`a51{%NdT~{h|8#jLj;su8z-|W3JvU+yO&l86LP%w6^82n0-Jj(G=gS zk~_Sv;pVkx!65eABmQw`ODITu`1O@7VV($nBT=(dOO{hdJ)t3A4_qM&%!AMJQ$OzaJ(?_)Sz) zB=(c&l#HrP9gstCm(XQ5)vhoW5Erx^r;#QrZwZqZqmihxMdxtBq!M0wHfcMp|2qrANG{dm&cKz2u#9ck;z1T*UaAEP!S zbVVXVTOCr#{F{4q5<)7UJqAk|A!u}?jRAUnRiGNP{#<3z#7jZ)w&Y$}YRma%*~cdL zCniXE(Qf0&;Zi_z2XrM)s8Oft5UE}v4v6KR6`wzg_-U}_5r4_7> zqAsEaxf2oz@5Se&+l;x1vO7D-`aiqfLTeY8r8I4J(`a3PC*Y9OAwwsYzbwnM(Dx#| zql6r0?;?VUN|GKdQ?B;QcbrI3DW7;B4nW7fqsAh|+}0B~V`Dz;S55Ka9Pr4HS!&p+ zLE`RRR8f$=uO`IR{~<%I<|S6|I1jUQr!bp;emXp(?p-|KrfbLhTXhZ%LM=>p0B^!{La1`g-VuLOQr^wbG=SsV$Y@IG3R^!X6o{=a2Ulj-?ksHthww5wG+aNUWHEJe>n` z#ylFOd?@AIle9{s8m1`$h4m0NR0;MukXekvLwG{4HAF$q*E&&F`7a4{%ta$nXFMME z77h}hFHyK+t5Gd`b|yH_Fkd`uDuZWG{&%?$Vq$^}D3Ugg8tPxP*bohoFKYgf)JXe# zLLzdbL4h=&Y6aTcA@}EIi0{B=knQ~cv^gguWzQ65S5O86eTXVd?wvqPmZXa8tOj8_ zL5-eBB4=mpb^BM67YY);I0apnI>7M!V-;u&L7&i7m3gxNz*EFGR1Pfckdmr&y?;G6 z33%*I9UV31zmJVd0B|!6D?3xle_8|7<8y%EtCDX|{wu#N4V3qM5!lhnzZt~eZ3das zg5mjp>Rty2Dkz=#m0HeCjfY%jP?-vrwOHI`v-jD)HMx*OR$H)LZ>ARcBB&zLdU*ak zI_S?c6@+wvDuP$^6nz9Vs2~AFZ{~lL!f!h+g$IL^vEBnB$rKPr4kF56Jbr%7A3X>Z zLzLFkJjh@}bS%Oh8R&>pqlno)5NX0`fy^@%EL@#QZtD-XzmFv`$NLX6`~o_JM3A!C zdb0hwdJ~Ne_%lT54$v+$8N7XvNGlt*euOCmz#YIOC=pUiJ&}x{oJpt2Z9}V6)O|BK zY6#3|C19cI)HHM8|Mxd_O%%BqxIm;1DS1`Ejw+o_4QHUpbCSJ;SkZ2CZ%p)L%nrLd;Vy$AU#R?fwgP|To_bRJpBk^A{F&wn5QA{e^OGLT_> z=eD7Pkf;3|31vo90R^}D0omVs5SB{IKukx$+x%ME>hUvk)^pzfSVCx4H84l*B$eaO zovJm=fiT%+YnlVNhByF&( zgeZ@B^2&Oc>NoT#2e}dKIkgBiKZ&y+Ngv-?tAeaZ41;FQ1LIm-+TEv%X!$`wG`N(4 z$qdRbN=^gG?15tYHoKt~5Gium%P8{sJjW(x0NJ^$5sG018mm2yFJ6IA{u`>BuEIhYracYteU1#|LS1mcPWi{#)NXF zGeATMQg9{+QUt-mu8I$3OF|nC9U(FidJtAf@yEl(DU|7#B9D&G$o){e&hb#qG}4y_ z2+d#0K2+wR`fVJf8c@0ACwzBWNgc>?5Fw}}uqVr!MtsHpvN2Dtq!AFNRjM4JX%U!eA^2;adZ-clmE!UY z-M?lmLI61{MtoZ9K;gHc%Y^8^`z_81q{JFe+UV1<&iU7U;(%r7b4J=d{@d*Z1<hHPlL_`LH*Lm`EzDIGOmA=X? zz^zk8skKM+sx1IX@gSn=3^DbAPDBMH=dFPW;&J8cqZmhR@bH!*#V1lkJb#eE7}?-xCL73UQyq1YnX>!J1tGfJ+CU zK5|jF5_S+f&iS91jLZUC%L2?n5>TR;fXqE3U{ziKOtH}jdZGmCA|8TDNnXny%nZqA zZHRT+c76T?QHar3?X_o#s63RK9bd}n0QPGz7)9h+Gg<2^dUkSM7x_1s*wBA9O{n10>k zdqL)MWSH^~P!7D{=Vu?-DnHKzT%WBqWrO-U1aU0bsQ*S!xf`LI1{o%LpvssQUO?gi zMm%Tv{w!f2b2G5(2!nXUi3t3W;{{<_wtLYK@r=E)mW=zqeJ3GOL?h5pl`tP5JS6a; z3S`viyLsMs(1S98hQ)5OC^m*dK&ABom^n4e4L8>pKYxO^p$e?}k5};$FF=H^1oRdSd&s*_6nicFYl25%vQQUZ-091d+p)q;^}n>M-G{S>+t&)38^ zzC;~_s!!D0Zh!lKF!vT-Rc>F@FeM=k(%m8*(y4?A8JoMbO2jjhb^9poy0^R=6fr^oNfix$f zsrLha1l-K`oD%0Tpp%WG21{YI3lv~{O$a_>gJ%0?I^5cI!p+FA9!WY_M=h}10_$!h z7rLoX-H%|~S)qQ}1_$1~uQ~|R`pisy*|6f*&@*q~j{L4uFC) z;cH6o(-nO<$mYO!Kx|^z3K!Mmc|=?Eoaz@uFS3=J)VH-x=`o^Hv|`wcB~o2trW0eG z*4Zx<@1^lVhU~w<2}c3Jb&Q;}@&CnPm}C&-(qb`2ejjqkdx*e@z5=)o5jj+f_CMGw z0*NV`G;ODlEb`|^1OrS>qqMxxjra?84uaSU?~KIe_UEO3FWMIZJ4*)R^!|(2h_;Y; zJt!Tx1!`xj=g^vw!L@v?y&_uQum1G{069k_3Q=nADKWzGY@11ZJB7GeS8hh5oDm8qTjkx9d5JBR?&%_`=58jV1NVc z5tY(1MsaHZdJhCSg6$U#c$+Q|E6yB2OnsTD4|M#)KXfI#z=)kYvvMhQo6|n1Wd!5* zpUJQLLv9aDSd;f7;y|my z;&y$CR8_|+`hW>J;aLESI;Vj)JVXna|I0V$({QHLC%}kb0kvJ*%F!zzE#xE?NQLO> zAY&H*)@G*+c1JTj;L%sAi34D$h(pKmd(PiWQVTE($3V2y4MMs8@oLwnn{#txfWT15 zgw#M^thfcP1*?->1e27-H|LR%m($6*)b;7s8kjs&(5^O}V8)36!`NTIu+dxb?5=hI z?y13qaZRvkiM$O9NCbaz8D2W=pQS*3Nv+-DaYuM)1_Hn8%_@Z#5XF=8XgaOo%t?4O z0#VZ|Ah)thwe*sPWRBr_7YA8#ffx{+#siR|xVz864U2$O3ReIw{sQBXc0~FiZV4_B ztTA7z-w(kWfFLSV&F_mDBNRLNKZXi+B~a^gTQCND|3<$ z4~Y57E`pB`7!$DTcN?XFpt(s0M}2MBqHqNm#9sh3!*w37_c1g++}HXy)14dd(*P_| zTIzN_Wmr*+d|k^4M)yGidzuTk-~NV|({f&o#SJt!S3%vRE#YIcF7PY~%y#$@aFx=1 zOTgo-O(&o$#r^1L=-#vhMqk+tdhz!>e5-~s1KviJ1o9Y@H+&Tlpn3ziU*pl-ujgfA z3iJv62w?EC^V$2{v_`U!SNFSR0}yTIy9^0bNM;3Og<#6p$G@k%6+b_r*Kx@k8Ys0~ zXz*rd0a603MfB@g;P0RSks=m1Tz`j98qYo!s{s_#9T`VPthS4PnE@-ZOo7@``Qa91y2v z()M#$Hv|KC$6&xn8XOiHFn2-7B)|-j4GRGHkNN4d3?IHf+^mEaP(!JprxW17x*gUM z^V=5)!5MT#h!b*K6nMkUfMK^U;6(bVE>wlevu+^VvEnhQJfTEBRLhuwl}KIjfQCOP z{{bcvy8*4V%Qs5Ak#hqf0*YlS&=pbyoNVve9I4or4@Jm5+gftol9A};}> zy_EVrG0^}%^#I`}oyYA_fHQtSaBKi4t70q7^2t517DfuFjwiCw9r+Y8Ur}KAvT{a0 z0sUcc+OuwME)FIxtprol0N<2$u{BkeGk*b!#EI9r5E&|iB6EGmmH)jG=9+$!C-m{v zM(R$TDMn?S#7Z*)*{G5I{%qbi3>-p%+QaOoCKxp>$5Q`^C+iFX0?o9-guk3&$tEFW zj$02o`_bwCs&5{j6&WXRw=)L~8aVkoM3>u3>p27j1te2nAeD^8@@(>r7jlHu)u6DI z2GvW&*&`Tie<8Oe!9f%9hDQQkZ9&+`8rtzruh4e{!fSm16*c!ZjN+451B+ui0UjwF zr?QA1kPl0X4{;0#WKi8NGHuS)R+>$hOG(0f>%#NK#$Nq~eE~cvb{!4-5@^8SaqM%` z@mwu8K;GGQV+QsDDBK0$#&;@YYbM`uK>yo?akj;B7|MCYB>)sz2PTmSH$W`1dtwaC z5>Dk2Hr$l;j)#H4!&IeZs+@zjkjO8xlr(Llaeq(W3(R>n&eWIG7XS!C=8`*!IE~Vr z5E<&a!X+*nD*;N)B^Os9OLzdn&u_9W{@8Kes41IZyA*nS!-$F7ht(mRLmDJA27})H zM%A(~NB&R(4-l#~joAq3YwEhVIbOpY1&K_C6C4G3_Z)x5!CPc%ilh{9z~IUrAb)0y zzs~6cH3Oy@oQ0OA#InNQra{JwSwUbMx_?`9(#VY{AINcf<(u9}`O&5+_l@J~V2}$q zfX-+s0=q9?wfLhFWzX}UwtJ`3FN6?_N0zY!KV`w^^sLij*Ro}nbdBSJB~`|+OQ0=` zvqArM1CZTY7FsyQV|0!T5;wm9JRZiE3YZBasgBD?E-Glgj|?4|*+kh;NarElqQt4C zv%VzWvU6dV)vuDL0t^3CS54+18P0wD_R+m$7$zYu8UAa~P!fjkVQ(|*+ak8pEU~hZ z_(qgYAgnZ$n@J5sxWf#E2(^3HHF{k(LeMY#@!N=Y675XCU^9gSmO)?5$wO`f)B6%C{X^&8H!!K8s>x zQU!IM6}m%;7_xIl>q3CeU4MK>Q@;mPDHC4eMw9_h{SeS)$2KMP+cK)*HN*qyPkVf| ztuPW|TX%AFi$XU?Rv+C}eZdR)=$Fn*MoD=0Li-nN&7-s{;z$vy;zR16rW0~EZwT2e z+|wFbOvRS!uCTf`X+3#{1feLgQZx{?Pi^{QSyG+t*f1eumAG=f0^$%{27Z>XJz;4W zkq^|ACd4+Zo!K)Y4Lm#b+elK(N>&vCb5gEM($^c;iWa%{4Tk%QB#T0bQ#J#M6xN!xq8(lMR!b5|+XUrB9~p;Jvz<{wM;a+2 zpCjv#S{PYO5SiNxZKDQ>#$FJyjOQ`YG7i7WXL1xA#=UNzkx?R+bf=gcYV5_oF5u%$ z?;@c(ZAOQAGKUyOD)?-{{id+m_xGR+q`2XRz75yLCE&nq7V~_NObV8k);cpj;lcLw z@C210?QT>(VTn|AV$+EUS&c_f)yL%|Zft_KerD6Mxw*F=8Ilu-99f zx8;n4oZ7A5g!8iZ~) z$Q|J$J%yI(I_)5`{37uMQ)B}B_asr(R{&ByqfF$;^5=LW{hOZ~R1j`hTSP)3YRPH^ z%KUx~fZTA8L4pi;ixuY%5BsvWhcRyxB@CKMwbI;%beRhY?g_MOIB0xeuM?YSCwFQU zV$;d{C&WBUWAAMHgV*6W0TgO|n(`svBi4FBwnXxgVcB}j?$q4^4Q3w=pe znvUTSBEb=Q^^EIXx?P`74Eeu2>#z_e3mo zzW?YIqnQ2M+~*bfA;^LL3bf*6F7M|}7W&bSI{TA9?Oq~P3p;(HKXa72;o)KxUWqB3 zDWG@5l4i>{CQ=wr67~?~Qr-!`V|*h5z$X?6gRjW5kqVTmUmvgb)9g)`|0MO#?gq9* zQX?G$GnHp{4Rsaeo4v|5dV`+mFA?9}P(svSkRK6hq>^d@;x~8&SK0PTrdlg@lL;P= zJ-RU>O(zOtF*U~XR(;9>cWaq)!QmvI}WYjy6jWs)^Cy|Q{9l8 zS^~o;KSURQea(m%NH`^lVL6~We=WB@-_T!t?-r_88uM6Pr;xwV=m?7|mPqN3krdWp zB7oUT=#hKPI{$i zq*AAsmsC!B=XT_mO;NOh_D?A!<`1?LPZT+$tPQU~+~LO{1-)olmcL`_i-smDL_R2S-_+NdaC480=fLK4s_jJAL;(1s!!mvcxc zJ_*8Jpwllrn`8J;poP?j!tUN8CauR;47T2%8!r|HGCEZ!;HL#?fxe`Xv#gG<`o0qP zXZ7hpBa6z8Vrqi<75t0JayKybee(sMl&RnNRnW=_r$`=P_GJxm?X>k>95KQ2* z9lsAfH3kck)!rxJ&m=YoIuwqK0+!>Cp2ud$(~duqg|G}Vm#IFNpPz@KNF>n7?WztW z;&>ddu^4?jz6!$JA(^f?atcF`f{Og{VC=-?R(J_3@3K9b_rlE(^||H-+a{CU_xBVH zJ+uPC8L=X+N=FDz#DdP{5dNw=mW8Yyia@mhdlDFG+=7vzb2l56LYJ)9(GdU?{en1> zIUX%TjL{}Hen1=c0$QYk^t)zb!@c5uxr*$43H*2qXjqa%_#pEJKY1t>D%k|i?qSv7 zIEKEXDvjFN%44>?RYOD=px$^~HbvCJ4qU(}T68;W$<%V8#=U~a$*!*}8UuKu$=gzX z!SCD-*ciHytT7|qN~USI`akUP{2b0ix~sQM`uQw>aAQOT2R{_szI0tpaPAO$10JfNy@N7!7lAO19O?y*<1--T`S zM~a3)4hP{kLG}C(?9pP)E zW3obmaD~oi*OkQ%Kr5vO>Zb7)DrJ38*NkgTvQuE8vDgclo|ORchnfZ$q)Ya30Ft3n z!vRv|Dywu(^X|Oi7f?1`XoBFreA$c)dK@hQGbEb!HbY_v6Lp2$3!lO>CK1$G(m-9Z z3ZOZJgs4Za)5OTYh<6Mn;s3a}RRF5{kW8Pi63I<~8m5jOv*ZVTEkc5X22dI07K45m zq6V;>8?!N@d;q1^T3|&k(lufW@^qb(je1jCW@g-TrE|;t9sFy;;n|mfbb;#r^lLEP zyQ|cg8UxoWVLa8mNR79Zuds87Se(jBg!T|n0NbA7pv>-ubo`$&W#YloYGWioEO#1Q z4*da3ART_fbcomshR_llWxL-zqy@vGtX1iCVV%9pn40aK2)0JX|Kfc-Z!VkfzmK6? z5PdY5iVedd6+!JRxbS-sBHiqUMYGTmShQ3aNCGFl9PbL0hFBy0ht{6NYcDSaK~Z=L zxj;HIk5NwB#Zv2XQk=Wx)B!zXYlU%b@*)n*Qw<4qhBi@pzFh)VQ>EB`?(s&)9k6Xc zu~uQZ2N(|h!8FlnVxXKGkOoA~*bNdoS3)1#$>VLVz+Mm;Q|~Oz6ZWSTpGaUvt%2@d z#Z(7BzB}=d_SfW=r+oYEJl?S)0YA+tpTsw0v$l|d$-9k$Ez#kdq%gz<%cRb2p*X>2 zK8Thv8<0IUx#z~ z)8zx|`Be_dXuSTiy7*%0ndDrKrH8rz6cyji*%s5*NKV;_x&tT-MyFQmuKh8QuMIRr5@Kub4D zA7l5Fx`^+~;D_oUVDPQ{3$yiJep(efT~}GiOm{5`$eg;w!b zjB0NTXnyev#_a*PUzc1zBm%C5u0&_;Syv=~EXr+Ks(|v(klUX|Zv&AH2IBKgqgGtE z=w5fRjMv$f_>7;jMP|!IiiL?kdZG3U;-3Wh353{Byb?J^1JQx@N^6n)%4F1t*Z$TQ zg^X<&jotnhZm+3sR3+hnDF5Zjzfd7#Q*KHd*!undSYZ<%f))5K(oPzb>bIB7lph2w zWtl#$ZoaJsoHq)I&vY0FeELF~DDuJq6fsHD3YW?|7Y5BZK4nC}aQ7Et@b zqzb&Ss|$@ufQ`$MCX~fjFg)%^F8VKL@$g!I9M;*_cY}ciz_-ESOa|S>`iI+j&4hR!S!ADq-K{Iq!{iWi&_&B3- z=8LOO(~>7-#WX*^E&UAfhHIhu!Xb$^Wh{$%(mV1&FZMYd$@P;~TxHm)&#D@|fDY}# zj+r=~tv)H$+hTXh7~6SQBI3^9e{Sgz(J^5C$$>zKtnczZ6PWo20&LZ?kt2yki}3EaRR043W=pfFT0Eam zjv9|t>~x^+Owy&gGsnHiH0UFwfLr&tkULzUt^Bv_MTbeE8$nlEdUSVl5Ij8_H)twm zA!g%gozi=ldz?gJReYki?Ci=7x@|Zn^L(~DRaVeFRaxt{YVfwiuB4?kW54Ci^uqo>WO94XcJI>s-g6% zggBK2d8anWf&|dMXLd;8?UY_|xu?2pS9LfLaONxJnbONTboiIB<%@_TT+4~x^}Zgy zhMB;>b@3lKg$e1xLSe}NSN(u?wLI`x^f0LVYn=ZTISfd;m{7~c|IzXg6EYy$7zk1P zg9-mZh7ivH7!iR#8~qP%S`>!_ z)ELmi^$V9 zBK!j&f3Ims4lHx}EBlN8SyEyT_|Rlu|NTU2jL#yDbI-QAQ2c@s{(ie}z;Tn~JDii? zU+WTi3hq)mP@De!yZ7KONko(XqjUQcdJx6IA3wn&{qwJX!qGEup}APAXMewaI;6gO z(E|JT@7@9`nBaqVT?*ixJCOdd@x|C@;KTl5xgd0-0Xm4EZASrXU8AA+muv6hN3b{x zCgt^DI5n|FpgQ@HkO;IM^CNc_^L0-06GxPgdtpW8g{XLIt(zTB2UIMJG^{}N@+K%C zmIGrKqSSu4X?y^Es0g?hpW@&l&kt09Io0c+cb^9Xc1@Zv05@VDC<2?qNAvv~DK2ur zr;rgeIbr}^7u30e$*H}9-#=GvBMFAPvu+^FFJP`-16GaNI6oj%FS!Py8pJr*Zo!F#_yO2QWCCdBOnL(v=8<5C zNJjznXt_eW75;bsTPAK$?gDfYp4TJd&&(m-%aSZW&ItJ@&>RXOrt=5#B18-rz#HQn zOaOwE(+*(yukfgZn_L23kUowDe|t`s1R!NUK&9|I$N^ki7_c-qTW%bQq~p;N%xg0G zbk1_9Rnt0IS3;O3G` zgg)i6eu)85S+cucTIFff*YNiKF@@y;I{{wpE2}8jtV@`zE&T-RUI?Tm4v;yyL;DvY z%^0NKy)%O$8W4qfNVeI?Zat73nuj^~2Oyk+zJ>E9kN^TgfKa*Kxxy{{Fk#9Xa~Ns} zIhNGr{TW!m&?Hr5rHAb95d*RyD@@Y8^P~jG1chN=uE$d z9Tp1FUIDp-KA>2`GQQ3*0{zfq8Q=bRjvO~kflM$d8(aU-g3JPpKUM(8Y@i?va2Csx zIEpV;kSSmhNL$pXnN=Cwhzkj*z5xj>N_fYo3yv$OqGYl;&A~<}3bujOY#-qrCzjUL9BUtPl#FyR5Ga~xI~)=Tjf9{ z#^>+{PC!e8V4@Z5j|94_hvJLC^yJu1DaHNTIGHaOY)EG?Lk$}AvEa1z07HXJHzrU8 zm}v5Y=DP7jQF2o-FE9YD-KqsMANKfVUeJOatS!W{K?s*S?`X^@UgeyN#B-1 zra;08MchzvImplU_`f+Ak-rm;2HsRInqr@I?2CiV^SaGvJ z^A&JrSp^O@(*^g7KIjs0;-le*aXMCSfohNv<-iguOb;x@^vKNIex+QgFgO0S^Cd^tD=Bn zQ5rdB3Zhv2^8-fFL2`*bb+Y{THy|a@gY+GX%UJd=FAcV}wG`NM%v&e(|0SlycObto zGbtGa|JRaVjsWXX8{Vk@FUvP&foo7#De1-iFG;r1fYeu&IpFdi4Dpl(hp47pE1v1U zg#=22cd(X9z4%Y60Gd(2%-y(|UG=|(V8nuVScwoi_zQzDL)xdq-ugfPOX1|q;2k<> z+~yPiTdx|#Mmy_KkJ(?s2Q}~xA=1}%-2bij|EU`%BfOakV#)db978ON=K0DqcvM@M z&TulkIssDoG`eVr=;C+cOM|F&gTC>(UY5KsPG6ShoVB45A zo*o#nVh2g05&KP!90_n)7R^-zGs<^iJ!-5#4;yL;^6~nYfdK*z1F>gG1HpGZh@w}3 zl#+&|aUherb_QJ{J>bvp-nPuY3A&}3AP5mUqzbvMLd-J+z!~qmc*JHqs(k;W{keZ> zi;+~5B@Ae$p?(O6cQywp+r86}`4<>G>o7gfxUm6A@)%eE{Q{=N>2Do22SpDmLE1H{ zjm}$%Wm3g8x0X;81BO6@w?cQPBSSbeD?u+|f>sXaMQwHzHJrpD{bxvi1{S+rzDU@5 zpaT^_C5g5LCc2u|Gk}T?68W80Lb-2WuYlh@cLjP|8em6oUJ!LZgUu)~2&0*I!(jR)~^mSDDc53H>V2*nsK=hN7QJ?`s7 znt)5yRI%eX!pYwk{M~r{j2dGOZjl($x(zfs(sC-c2lHeu^_=e}fJ2YOgDD_(m?6zo z5CoBbW<(eq5G6WKx;m$B2qB3PLHA@}w?MuWA^ZeqSx;c@hd`{6I5CKe*8UVGq0GOd0b`dzA)J<@|p$$WoHZpbvQe=DLJBN|uS0=7N>@)-m zarV6fTjLq*EV7fh%2$J)$nQ&E5Hj1bKGl$YtN{sDG1L-hOt@56$G#h#T?10hsfi5t zqq|HzIf)h$vm6jhCw2Ip0u0k8^c-ZeaGLLepxHjE8#8$-0I-R3JO|X1r?B$&QwOpApX8e>L#3|w?&vNDB5qiDNw@F8zzC9 zXA1Ix!;^74AnP7mU|W8X%np2d3d6tr1ok#2b- zOJ}=NBV~3wAH`vpJXe5S+k_jU!0t*XdMlam`B4k{4nU737uLNdA3rpIHb;cDVVOn1 zNe?31pxBggMKnA^tysAO{;Fma=O79=_M+oV)Po*9>3g)mky&gU{GV<}QVP&Zl3}j# zcw$j&MSG?*dpNxJB%{ABjE9^F5F9ubRf)m zkINH&5W-NjbP8sV4G!uNF;xVLXVUb^d$p_5#4 zG}!}su%$0CWt9jC4hfoy{ViEMA4=-FOBe#P>%m*}xBzxUt_@36JEFE+q=16N8mO5n z+>JL2FRZkQ>^K+;_+U0#YS7RZ)`|CT00nB|jL8B=w5?^9!CbDAyUS++q@@(Yd+M6Y zDRLZkb;Z{UCP70ko=DrQvjd2B`F0W)UnP3@3ubuGp;5jDo39ed;aYVfKM1ZjLT>=3 zA63$8Ec8|JOQqE=tBhZ^F-lsN{B{`*uj$$AgDxiSmZ&zL<4uOqN}aad|OLYK8OWbuCQZk6Deo#V-|2<+HFS?w-t_W^?NcA#Sph8^<(I z7mJGoPkwBq+>hgOd)g2xn)_z8p3i$*{!Ft_IDg(oH*dbeyk^lGx%AzIHvWm9GJ_9%@aNDdPa=&;%Y|(!(z5K(T^C_Fs@4f5 z_EZZNLE*Dl$8+bc~}ogor$dO|rcCDH7* zOC1nL9#2g>WFh6VG|42>q<-K$fwSb$cr$kbOZmE=;*Qum5H+@bSDz!y1tX*nqjT0t zeu|jb{c_3a%)yUAO&bx0s||C&s+~c@Ixu6ny>?5M)tg|om57Yx1CckrOa^nx0PjW; zMOX}8qs#nB%GEwRcU801-HxX4!OBybGa+iyv0IuE6$ z3w=3YmYZSMe%QgE)W&BOR^h|l<_ae4_p5nHF%f1p)&tlk&?*)hIH z%>dCZZmG8MrZ(VBU*2N7-IRa|!yKhgm+Vk|>UCafH@A4Ik|2FtM(mk|;Ka4a5#L0$ zToF!#BviBO##ta$82%v2n=>oT1E2I>+aKw;b!l1qKGqIh7ezuTOwR9jI=)JhZhZ*2 zgM&@(Qp=!XdnJ5KCCu&{WpvqpaNFOEXGww6{oXp0uT75o;N@=W1SO35PefFmv6c(B zg^RTehsBfB2Um?dH*kgBHF}czxllkBThr zPlO*hEQ?*&7&e|~nnt;%d-2?XZzv2T@-;N zDtwF!y~6QhEsNaDH2bw%XOmnrlz)*?oM|6LIgyVNhRGYd#EBT%i6k>)$_=Rll zOiN^gygoe((n7jqffv4G2GJKlWoV@l{BxX`xn_BdxV~3x=Qh7i=+Fy&Rb`3cjwhPX z!Zw3QA8oUqt97Av;cj2N=uDd`n07ptr@G6QCJAGR@Ue=DMKY|o&v#jZ$%k_%l!8^) z^Q(0;ci0yL%#9@-UncYX_h&F9$CTd?<1jW{wiZlQCp#_&)qcpmP=<{TLc#=ZGUgv6Moxa6d^j zth-jtpU1JRFvYAJn^xdSS@%Kg>=Yz08xi>~vrQ<;OMpw(hGGT#LR)a=-Mg@v`SYIy z?$!_AAJ(tS@HfUf-p$Ys6Nq%1w4Q7$Hu9xzT&F&1@Z^Nnru%^!9liv=Y2R~4E;Snl>bH{t-%_2G`v5643@=Q3yX)|mKs(^KlW&(G%%uDiP(cyLu&&Mi$Uh1&UI+gmB6h+^HIsA1|>V*l)+8QuMm^R>r(EE}ufq2<8% zoCCfjpJi4KmRoWS^{M!a?)XjmIqU-+@AaFHEI?iWP4fC^(*W0Z^%Elr!NKNec@4Ym zjVsL_g9Mpl;7X5HP3YNCaLI62KFf?78{;gCeNYBVI#5UgX7%yb{4xaQ-j7V;+Iql|{8Sai?FnpQ*Sro@H%0(_xay6^eoqo&s6>984h(<=~t`H3T zGCZN9HoOwAx=3LlNtUq@t=a(+(bPibWEoCh3)eN(lj6;mQYSGFn+jWsM`}^hsr9z( zZqV|0r9!OVKbgwO8@EMi{8S$w-t6uiyQU;bP+c0wC*y=U7|hk`?$oly+k0b(k-dbw zRo+;j{@xvDptI~aM;I+!Th8KZ(YAr$PKl*f+8R;L^L?^=8Me}HjuCF!Pi`e65@*Z; zFcqRi4cuOsb-3NszTwUobOvNHa+-OtpSKzYBkj>%v{Y$@Yzv+|Z{a3Vd9kdUr*HRe zU*&B6)!ccxAjKXD+qF$vicIX~cXtl0!u$6f5*-oFTOJ{|#Jy)8MDogR98)@(3uK%4 zO^E3~Hsi0txwL#^prxOz4@qcO*mjqt7owYGJ0%`$e%$nw*EF9lJCh($C2wkyx!Eka zL;Rj~wp|yJ^3zH7V52*+V{3pP8#bRUKK9L8+#BRvD$mD8c5j{)XkL)Q>nvxn&9f?& zJ&mGt*<}nEE>x|XTqmmG4omWRHQ;Vl);BhT*?u5>S51X6&M?GQ63e#eBSLokD3Z4= zuStX1%@A)R@ZP?%cJA|QzVRIPv8ASVksfY2r;dKTeb|*2Ax{m=Ib1S|cI&=3WI?-` z+c%4J_T^XYXAZ8N4!fD(_i}4?8SbhxzKk=BfEQ;KX}oQ@Tm;|29FVVZ_T43K)Ga6O z%XD`C+8VP%^%L6BfcH0Y$j-9dL?EfmnuVO-^1=V_!+$KU_ukHljGP4ae;i9d4UeQb_WStJ^r^o#Nb7|)-V0IoSH!zh5TI>kb2QQ_=Y=xbyz!nzd>n_hN6~OT+!Iml5e~ zat{Pc-495fP8DV>P4&Ep6oWbAOAfjEg;uc>yj3` z@YlbmmU^R|b53iAV*A&_j2j+Y+nnHF&jEND9xFD0q0n0!Z+pCE?&psomvu)7 ztJpn26@gXs*$$Iv}496T!bFKsX%E*|a|TKN~Q`d!a*Jg#$E zA-k^HXy>uZ3eiGUkt5DlWa{ne;bo_A1F2&D{8hh$#+jSP6q^;f&SLTXcCr769KX5v z$OFJ0HdSYZmdV;64Zy9yHi${;}+?Vj+&jfFF%Qn6h zBi>9;G;qx>p*i5EoD~=rB5Lk6JC23bKXNIR5W4D;pIiGJ2> z3idycJsgn*G~UfL_PLS*;EI-V!BxTS8RG;3taFx73kb2c? z7{;cI*J$asqnf{dr1@~B8GLb3Gg03WS022fq=tK8N7ZPYVqPcjwym&Id^cUJe>&i{ zZ89SRGt4s+<1RB7mS0uhYZ6Prg_=I zlVaUbRHZ{|)63KFa92C8?zmLB5)>TPKiDIJLB01ZC%vK;X4!-ABj>@uy%{mt^`Ln9 zIlSB5wc6awtDUOGhr0t|p|d>sKB3zt1fef(v%sfoUari-`0(LM_`xs=?{bOkeyLz% zL-^r}4Gt<;&-_+gYqoCvc`*UmQJ|!r+cVeKrh=8_*OR*8Bo7-8x2ZU8TNUO#oC8@w z%+5namLtzUF`KJ?ytyxaxGwGlK#M3i?I#Hi51v8T7$JA8gKFd0aeVffK)X2q&nS91 zGvZFITZWw}J4c2c!NL_atI!shePxS*4t6}yZ8GV#cq41=$Y7TX}Gft`4n4^Q;cl)V?h4N5Kjz6uZv= z%0G)lD=eM$T5=Ykd-OO$XU1=m|BG}vPgRAp!=2;2h zU=M-olq-JO+2rL7%}E_MWVR20%zTot`AiPyW<>1lr_u@B#v4JT_4@tiC?1yqtyYK4 ztyZgFTMw%`QAKUnX%av&iUDVfU*tSSfjG+Qdg#+C(sI>sf!bC~ubqZXuU@*v{f^eL3NAH<5W^w2{T)wzUKz ziDrsFPCYK#_)DKc?#2t3Ms9)Yp>R1VtZNHF#~#)B2&dj5GnQA$q~|38sD{aQ)U~S(5W=dt)E}-V$RH&PYIDi^Q~cK+JawxyEL|HH;K&S(4NXAX@O0xHU9{je zSRtlMue(ju&Oe`^HzkuYV@+*+D@hW4N8Vd=iooW-dgH_9&s??Wt>z)+KSqOpF?BXE z{j~o1U;+`!(BXy0!v+1@@ze`|TFXcvB^ox}M9p{wE*;+9 zKv&-C3ZLsaHjU+tW+14d_?8OpB7b`Qz5v;tm+u za3CY;SgZCb1-Yv+-%JW=P4rUy_SEOd}J=myT%*V84t zj%IYQ8L41YsK1l5mFE>T_4YjYyrFshrK21}a_B4Pumw!o*Tyr|#%NOs#w4kgq1xKH zJ&P5El;bEyuPBT{RTk9yHaejn_(Vo*Nb*u~gH!Z<7KulaLwMt#c~yvHHXXrncA%yg zKL~cOjOcwFxzYE+gsHF4!Y&^JQJAx4X~AI2A)3rY$7{dIL4-AHWk=(pMy>XqHgkR& zLuMPlCBB`|SP&(fbES32x|4)EnCg--_iT(1TgC()fhx+h&|Y}oI=qL~bY?{7_A)QB zMtDm~zWW|?Daoc^O0BoBaop4?uZ+)~m-@oWLKfEQ=(A!Sr99dE{dr~h@2tl5zV}!? zl*tuQO=aTM@@OozO%a;v2TM1@WXlOvtWRf@1eN5&CDV3D&3;kZaB>E}RM2GCuPqP=aC9ARezqlSdZ z{if6xEExXR&WYBuTyVoShR<#T+&2S0?mqFu8N^1VjIHIcyDmDoE~1DV>s7d0Q>cB7 zC3S#~7Uaj?WU>Z_*_Hg&(VkKv5Un>2-|8jGE(NU_BBMndPr8SmVLib+KjZ4VYF(`y zokhS!z3P%?ebNfy05EF&Mhm@o2}kSZZz?HES@FS|5PLb+necu^_?~Ku{JM-_Q7jmZX^ZMkYPedfh<)dqU#0}L9Z`C7 z&$dD64>TF$bIyBq71Mf(bn5hHK|`E-vo@>_P7&OajSNCTDR#_pUKj!oTNsJCnR}T^ z-6-oi?Z%o}Higf1&?GD1D~{$=bDBQ9R4LM2G2))U)#k45l7+|8JWOQs_rB`7NPirg z+x<$;5Z$C5QyMP0WNDD+6YFSYj84gtKjhr=<7+^ghn zD7t2=&&5@3G|X(iP%JcU*L8BLQane5Jsp;+;~(cOc_VohuM7oN2ra(g*X4MF-|^KBc|7oX5i#S@y48D`*NNkI zwoHP%C9N+QR(uHh2-V3-#`(5330IIjSe%D~{lAQ&+3c{uU;Q{5LWb|N2A=t$8FI8t z*}T*vc@0pphhicxWz#!wKXB}%tmq%6T-4DO+dw|;)-BumyM}|ChF#UY68di%Y<~9U zsmmZxjGXqQn8PFPzwnNGn|r%u>4a;kJ7S<#Y*@g=V93!@8AYgDfC1Eye8SM-=j3ms~@Eyh4MULw_*4{@UE?AOoBrKX#MplPkzJ}MD zI@Ou_lz&63n|zqzN90ec6FbUeER61@DKuv)90>%Y`oS1oZb?goNOP#Kxe;&JF;XNdIT1?(q7KJakO#NJwHqO1XLBo=5W3Q;Z1j$dM0 z;$cgb(l;nzYpQ9Ajd| zwoP~+iAGrOBGbomH_W1LMrbB;sq8ZTZECr1NF{`M{p&pCI&`a7`f0W~`ycn=V@{Rn6slh`)B^$CbC4R~`#0S0>?%h+bALMt*MGC=i zeSJ|^N8Q{oa$CL6Ul6*5A{B65T6k-%p(dSoRi*(KSRp6 zO$IfYvcG~{;^Q46b+O)D0qEIwHpk`RJQ{;(6#+$rcm0LmD}Pq*Flk4hH{72U%fs^s zsr6isrz)r%ur=85q~v>KsqZ|dv-MtTuJXdevbgT`)#&(kWSrC+y0E3BnqMdAOqO*1 z*a$F{2dHV01c6d>FZj@HM2gcIdcS3qpo|mAJ3FHmhIN+;#QNnUkPPGc?n$K_N05@f zsOGcdeO7RXRnncg^J1gyB_I}>p#8lDHHinn5cH5Ji;vd6Gj~?(>4DOeNbL(tch^q- zhU9=M?1UJWh06}MsI>+Xmi=tL9R>}JUWD>+3xj+`Il4&q__N$QzK@uRqCOMSWjW;^ z)lEXhd~!^^u9bDO?Qk_%`^s8k)eE7I6~?3XdwQ<9{dkFGR~TJ1ev&HQZrxO$Db}AF z)-uk6m=gg~; z9^XMBF8_wJzunE>=UVn;@}1PWbG7EMbgd$#MY`))5cBEopCsa7A zf~Gkt_PVcG#k7}mpju$M#2z(Gdb*{wZhWSJtGK2Ttq^|X!e*!3S+)^-aKKzmzx*tT zNVVtE$CzaGIUa4D)^$Un^q6kZJlGOMu^;-Ki$-b+=w#dD^TQo%RxFffyG`?pVw>!K z#$#9hEXhGMJK4VYUn&ZaQRwh04c6Tp`#-2p58 z0PE7LXaB8^mt@Fh>o1N7@Mo#4C7m+s_r4+XF@Kn=dOt5ip&MT9as61-99Q?*Ps_O8LuYBr=vmM-vt3y)g&ZYkl|;` zWUvk_vi9VRXUXAV8Mh}1o6w65Px*PP$mbW|10Y;iua;*aw>`@aAGUrfTFqz{OSES5 zb*+L0KWJ0TO5Lo4(__R&trsf_T`(TqYyw zihNYd1A+pyi2(CAjSz-yQU}E>kNV_?_EsvJiiEE4X*znAjU#hr_BzTm7jC-Na$3El zV>T^O-9;iHAF9h6WQA9V$neE;_5EvOjGH2-VXLxJjO;aj~foCu8KCG(_kUUHfgw^v@zvI$wBrw z-`GH-P6|GYQ*3fc89O$3tL;lp%v4@aK; zI9-3m0BswqYF4|`747%eMGn;?hoy>1g13E=EU#4Ti(PIFI>ZO$oKJNF=mhUam&BWA zTSzLK1QmBgJH=y^Ti(U2kGMXH11iYqJXJ7KS z$}Kb@b+8Qy9XVkvd-_O{p2lY&uWSyoB0FsGJB<1EZC9qqcQ%>egZPjg6yAK|#4knb z5QGa4m*yHt1vRU8bbdG(AjD~&d4!;HfzQzvLPrupnT@HXcEz|J@%dq@vhi+j>sy2& zv``4g`_ThXm9%@F!CWCwjnLgXrC55nS>aKSrXQuc95_SxA) zAApz9}8j1>FP*; z=GB@eVZ-4SOQvK}wTb)nNGgHF$xbC{iu1K`YlV-7`eCI>>Ng;e%B3OTi~ z+KYPs9Kp3Nug=2{cc~9-x8_xQH!lWFjOE-%sRD#i{Ey>Y=Bc=Mas-#GyEcE+UsPM^ zUybGnJ(scRpIlGM?}cx1e70hk!p$=;?dbHw|1b-6kpe|A2Z2{8egM63w=SWCBnY~q z;cyohzQ?Ao8N-M5qad;EplGXcPI64q1D{X4(Uu1|qmN)0a37h8S|h?6({_X>GSq7J z;%Ar9Km{V|v~197F0(i`BM&phy14hVnS^~~OHxyITcskCiv4^W#QyDbUK^{5Zxv~o zOGO;({O1xaTTEQ!DD~poOM>=UzJ7#E(q(#Bsvhwt9(nj;{~uLn9TjERt$jLVC=nPM z1VN>S?vO?a5v6;iySrOb5E!JTq(QoSknV1fA%+eK0lx>|bJlm>#UHEz7HfEzXYReP z>$k5hVl|<8$aJkCJNL`DYV%z}+}pYwHk*!bKP#SplW&UjEQ3-i{RXrwL#FY;pFppj z4K?}zg4yo`3*9^r1KUDH)5=|0m#!7w%gcxb-IdeiCD1p_Da~8js}5|0*-1A;e{NfK z#_^tn-@)<7*}citF7kHj8)cp=0C%B>K@VNy*0HLH?bhEh#N0I^?`^P_cM0lMb!0Rr zK3`E&`y3O3D!eYN!_HPOQo04uRA(FD(>x0?8$Z)>b}Pd263-t~)7DU&UC)pDaWLg( z(fDncbJUudjSw91>g#JV~N`!uU5THO;lC^MKIq`?fMMMI0u7_|MHtkfEF_TYt5gYVh?Kd}#dlH?zRyu2*q= zCs*Y$Hyf!uUvEyVxuzm%fR-J0o~-%%$v<=4;7Lj zdADCq{MvQ1^8P_xZacf&jfa0>awnd%e>mx9dQi*?LmL|r7R$KdlW`;}g2u$Ys!R3k z-1B)Adq+|7p>ly*3Y~SZQZOD*rd{I|X6utojHxbpG5di&>y$7_%iv$YN zwW$cJgyBEIbhm3u0ZTxOI>hLnphiVt6r2;S*++go>Vx1p`@(K`4mjDdf$ZHK$*;M0 zwS{8~$U_^M)Ex3`$d$ISHWf#)v_!2?Esu70|H;?)fP=#p>mr`!6~`2#>NSy=pjhJ& z&u^mVe=zH3`_EPqak!B_=eKt)(ZvV$mYTd~&$>S^B#a=$dVf3HdgzLjD$NCljGhR? zzqo`r34N2Z|9Jr@>8~IA_7EC7PX!;N%I{&uUJ&BpzU&g%&Z^zGuzo1G7r?MIYp`mX z!Vvb2QLZ;&;@Z{|2F#53T0^hq5cu0ITyfc3JMM5OB zNL^*Ylz6jZA3GaHwxWHm; zM>N6oZ#RsLkP5}8HeRHFLgXlC!~#>!}AqOI6mh=?f8n@CE-W7@bk*P~-rHvyPVTSfkxf?wOr!~ffBjSXxK54=n0U-I5T zKKqZiX#+xE1u0tn&kx?AqNg+};G1geYCyR)B^b4np(HDjmch|}iw`HXD%6PGq-h0i zt09hLujoVmE0pIV!E;dNYLdx}xw2N3|1QB7qXOCajB8qzk@@cb$4GrB!EDGM%v62Y z(Uu$Vh&mp8bu-(ZN!#lowJ8)GULAu*)iP9kq5>bz_2#e}G&|;%SeczWU&i?&vK*5) zpV+YP$L-md`7CXKiv2Hu)fj40d143yDD!1radZ9!@ynE6JsocQK1{CJpFk_F=^n*u zlzZ2StBB0>;Gc{_E8$@KvCr9|@teE3GHn_DAYWT1dULEca=}R`61CZ8ZTM|m2q374 zT#F83g0#*>e8AW}d@();`gIF!P}=0P5UU&~&{xeA`%lp?Y1|>z<977f_%s~sS-~Om(BdD-phw{zJc|t6EeUGV!lDJYvc|nr{ z(JK$neTa_4Go^XSG_(E^!Q%k@ z{jz+6&3hJJ(m<-=soK50q6u?a_}vOPxzqsPraFaeDc4B(t*`56TFIvL=LByMKJF6T zfAmm!F2KzUh-)rJ0-pKX(j@1nj=VZ3Zc8_d5;7a95`|Xb+NrQdNhr5}7zM6ZxhyY& z5|bhH%kQ>>_}AOMBCwY&&qUpBwgg&$>U?XtMw->nTYskC`NCrI;!kWKIw?Be>fXYJ zF~<<_tXnJGC$IVC^9py1mrU0${8%gKfD(iI54PwUs-<8$3)fITuuS(WzaOxNc*kX< zLsEDF6Y`BI6I{tNA_(;WP{}OBBJKTgc;Pt+-D%0O4_|cSSSBp+DejlBDz6E?sPjU= zBIR|7^<;Ey9*?o(^|?RGQpG0kY8cphACv}{7eD{k!nj}4tU_`&@-TMidL&*2s3 zH1yjqM3I-Laebd$QnpduC^}2WlI7#{oA@??LmY6vXmNN1(bZ_fvQnN#7x--N8QyVe z&OVrY-4A9;Pkj^7jFFaS`XuK-`fvH#!ab=Ua^GaX4qQhFD=h}4a*Ll=ofPWpu{vjR zlQ!(w)1H-+`XNf6SX(UMMyG zPeTJyUdIqLPA1pM>$k2Bz9&(J>7JG(k%GDl5@$RTiFUFV<9b;ejDo&u<_FthPlhg( z+hDg3#l148mh&BVIO9P8r}Nvii`uG>)b%c<8Nr`;S8Y^mnUaJsLCk$TnAkKfZ-xE1 zlIuy)I$7z3jcxo{4eX8?V)5bNUrdUF`+%$D}RL?hAE^vQS9~B_!K=(2FQUC|=nuB@!!LRarpDCI| zj@P~6OLWVHWqxn+)a+~Jq7a-(|JO25LlDS-Lan+*cMT0A)@WJqz7NlIYr@0<4X zH&8Z@R8@$GBKZ+egvU7kf(epf1-+o@Rv+2XmI#E{iDQu@Us`ZUrcAT*A5mOBARJ(& zE}Ypk+;tM)=or5VYZr~y=)}g~A)h)T_<`MEB##TK7J&CNI~1g}vj|LPOIP z@A7A=JVc^V9FAoSV&?Bg+%OvIool*|W1V$<6`3UyeD@b(Cee0xU_biD$O-N|Dkd@X#M(r5G^LDfQZrIMPnhVyz#Z(jNy!oRw91qEZgAnNB^ud-+HSQEd%!R#ya_hlj z*LzIW;6Ti&2)w{rkDrb^@ruVRn!$*sqLFOq5R__Pk@_kx?tI2awz{r8>ZLK_ z%G;yI;v1!GM$7Uwv1)8zjz)epcWPTJ=0H*m31~RyO!J_ta-uD>N%QJ%RjvS3f#{!>a$S(xAR9^e+u~>g{}u> zV1Vz31mc zdxAbGh)6A}bPQ=ba3!OhSu@%ghXy~YpN9{fbkn|z`k3sqkDe9Frip%zt&W*_)x&}~ zvF&xA3JXjiZ(lve9?v}T;Rzmp$_BG0 zoMu+~4t?L($)3FmfE;hCW58-PIxuAdNO^(7oN>5mJC6Gk)ppx*iK#(h6o2s_Kln?x zz5zzhhkHH!)-+_Ci6r7C_cYC5Fk??ox4+9WLj0 zZn4=`H?o!Wkucc|8=Z~^0#_3{bkgv{hPjZ4C6OwV(3aNO1Clk>t<^WBGYkBeEYG^X zY>*@mlQ?cWOd+afT3g0ZTzRo~YktAAK|4K7Z(46Xz&qVjHmX$XyMCrD)mL^EZk6P#W=gfcYqTQ8G@EpoPw#Z&*-+ z;VJ1jn7H|+3(v6j%OMlWyH^C z73{-NLRPPs-(hpQpmC+_`G<=vF$iGgt=oF>AJTP58t;0bnF2q))58;DPGX9-gD%qLIX(dUQm4NoW{3rRxsIJk((?SNQczds?wC^!mOHN^yHy#DZ}B#-un4jGo}QD7K!4|`fZKN z?H4W&r?vgZY-j2$+lSUIJ}p|(EOJ{Bm_Tg5+rZXX-V>M$44qn?F~$dPmcc4)#WY(9 z3#>4EQ090uhUOacGO_hBZm`btcEWpPRK&uJsMR&Y+Mq5wQlnL^gi^^(I2#kv&s$^P zrjN7)3!1N-zuX}m*Z$TlIU7g|79;S&cS(|Z9e<<8^&S2olR=5*No_SHE-+jq&>W%r z;8ShS&UCZ;s)!huAjxUzRG0n$Z?6KN-z4}5`Xa8zw!oP2(s^=pDIzgws`Zf^_x2m2 zovH3`eWZq;nHLZUYlW$I*rU|t4Tya2vjp*Z>S3JSe(>W7nrWI7W91EFofo{1M|85I zNZ%#%TTFZJ?$)vg8*L=Fe#O`HQ;>2inYH;PhV$OODRscgxLnNTE{3tmMSUqEF07v3 zrp#@$H491D@Zr$Q7@&>Z%F8e8<}WsW_vz^-CvoA=nh8g-|5T+bC0{r{2Mud_M8`Rk z3s;?XhojsHhi}J>gj?PrfK#>*xl47E#9%b=6meskEBJM$))!tqHf%r$lh5yK&b;z| zTnJF^*LqTtgP{{CHk~Ras*=B5Z1$foGJb5fxWRNoL!yf4iqPpbiAsMKYNxJdbpV-# zZ?@_uEVm1euQyKGo&jBUZLFXI|QCP?-fw0HEXCf@SoJxJk(*sh2JJT^1+csF(|5-5dXW4Yj?1WsnB@)fc=H^;Q( z|CnzYBcAd$?@fn{U$Vq`4nfV-cm)g#oyX*=mpe4%*oVJY`W-x&A{1Z;85O~ris0J6 z&vFWzCObags06U5d^`-1`~A<{0Q_lXkWY2hVuQfof$9IW`TSTM)$qtOKpGO^THZh0 zzkc{S(1XuBEkKsdnr&6DoO-qVM=i*R2ow|NIO=g>akH*a|8MyEw~5i4gO7IhlU}?3 z-RaYyFNQuKLGQ$FKPL;K1#kZyvS0s^WN?IgN1WIouQJPPtq` z;ZG3Se%tq(9C0?IFPau-TJ2IMpUQo=jD-fk+%iy;pc9xie}==xtD$(M?tCu$Jd>Z5Jkmcg#VLXS2#daXjBfoKQCR zFqPm>LdwR^&)<)hE6pmSPV$+yEcm=^^Ggk`T>gr6>Is8_Gsb`4B;k!Vx^yi#!OpDv z*7Q6{{{q$ZAREP)y>Yp3QW)kFGwTMZU7(`4H6FFmQsLMJvyBAdLbHLzq{i3A%3^45 zqu}iH!U{sul#~FeFJdr8Ea3|&JJM@^PwRY=U%}0qX`MEzb0omczrbB^*4^CrV}aNE z?BN@TA3+nv2*FFi!2m8FK4fa(YP*wIG|rV zc{X~(DOe;2WW3(b7Uv2TA1pZ1qntU3)}3rCDRinw_NJsfzZ*h=2w7MB51q594xLh( z^P0s#yZ@lx(F&f+9HyQY=96EH(e17Vi(L`kUS^-kIpqL2I())A-6I4a22UmqxqO() z++Bx@$&G+Z=@u*YTXeEeI2Br|hmsPBH#k8q=B*2-9-W)!2CWLOtv7q9h}RC^`8G0t zkVh6dc}kn$jf+5kR=cUbqVkAd4~)W>mHdjGB6@&=n#?x_8kC$I&IH<1`@y7h(f;j!DOK>^MvQus*d}WAOP^FiVr^V$qGr2TXgSJvFqbgMzJ`l0CPIymhh0M z8m@i4*!8ZL-CCQRYYah}S+D6Z%tg1psM{drXhR|A73<&Od(~Qs;~As)Zao7(1ySPrwtG0 z#eS;SNg13QM}Yu$3U2f0YZUIN=Jwh$iOG_n0b9 zGxMl3(@nmaQTxA&fXj>RsqI=yc`iWNj`t>v|8@toA=HB1%K)C*|0fpKB+Q^i6vJ`& zgpZsoY6Z+;Lax9iP`F}D#(CL=Kl;%^m#tVRWhm0fX<`#sRaP{sfvl&1b+ux4P(FW= zyaM3^k&i0wCT;?A$R`ryP>70XzuB;G1kp`yKIl-u#`gwRRqF?-gPW5Ve23E6!LkhM zzwByOQfPw5C&yN4Q}kjwGG(rOKVAPO!OO{yF)slpqWsW4hZ4PB>tiMWvy^)8H&NTZm3;tQCGciH4Hd{giEsn$&*7iMt>Pn_l2q>$01z_YtsKgbU1%Yy2bl1`*d+`k*N+@nl{fg_6kN(~SNKXYGp z3v~0py%XPhq1xZzCNNOVdg)i*<|f5;6$YdmNSv%)4f%q;GDN&CASBmIHN1<2Udm&e z>x4Umri$=1%Uo2R)giyB%gD=SF3@NCm~b;49(C zqdK6c3%1JP04l<#mW9>dmZRTSO|RBwNJNh(a#O5gQ^v2HV&h|@*b1HgWSkroX@Y0D z;f048I=vHo-2W|qCGi&3S5-IyeyoYVCK19wA!qeENLHA`0PiLSorvDwTIEiV%z}Yz z-Y1>4aKlr8J?Q^Y&W}R--Af+#OTt{$*~f?oQ8+YlFAbSiA5d zwDyzC9JRf1tF0kkxNYWPAynE6Nf?>qr$I*xe^k2pUQpqA@cN)QsxH_^ zyx{+nA-vQ9ZPPcY%rH7UsIiJz$!sH;H<9kl)H)xJE8Z z>4j91A*LQ2wR^Z0d<4q%_ESLZR%1or`=jkW5lw8881S-5p7?+c)+9F(G+J0H^jN7E z!Doy=H64-EX&}!OQAJm*6Lcp5zC?rBk-XLdn&g<>R8gh_p_AQ zdjIMN-NNlTSC~-VwDdmWjMS&U5UxbkzIwC>u0$b)v9^RviIUrudSv49z^&OLS?{>f z4(>G2zkWoICVo{oMjTL8bJer}t2Ii5FR!FySM#sjyTl!e^NZhe_2GS2&VF@1&i*?# zUmC4OiwcstL(&u@{=}$MM-ae5op=s#Ce1#y0V`-cXctXfY`ScZb=PVM%Ji^ehpe6H z%kICjVppRg2C7rvkw-H($z2p#ls%0~N#GpG1l=VB%8GZ&CSTW~AvCq`#XSTIr?-6ddCOD?2dXd7&szV9&4yCG#&Y z%^j=7UmL~Lw7t``*p^GuruyNvf;*d3Z69S7@;wnP)1eEO^)+P9mF!~$IYbqOp)&3J zD+bZn0qhA?l7c0ry6dnV?;MiXhVrci9D6;5ak&Pk=W1_>O9kDi_Ma;iycVFzo2W1+ zs=p|EwWK26^L^pd+?*N*0JMDqFIuX@{~*Tg|RE-KHbdW!~xZz@{cJD$g_p zd|wx9lb~!`1oEQlzzA-WCHt`%CdrvvnH{DzX>Xh!F^TUMyvW`Cic=<#z%`63mE$zVJE#Qyk9aqDaOJInS}knohgG zW68zV%R|x%582nui-Lz-CC`>^R<#M&l0H&H%@0EChnSaga?X`9+JsUcuX;Rtf1Jrt z>b;5{-=#X4@D8}`1w!4##^fzp{9KDYl+wLsgr^I1?H6aej71C0f$`qEUu``zOBGOxn-&cP!D>tK<@0iKyt9mp%gMScz6 zPMXx{eS3UF0WB5Hv+FL**4H=m=Wicf=BvxF-*U6Ei5(t*MSs+BWqnB*wClHDHZ;VME_jgowto`q!2bYFX5Oj@i3oi zoqO>I2+62|A*lK6V=TQ?&Ig8+Hz#lN|A>&A4N)*6g1JbGDMZj%7mQ-bf%Q6XXCEES zCV?X+KkaWP8`N`+?HCR5;KXX8^O`?$Hr~iCI)AK*zNIZv0071(h!t(=P{*BIGj#+DJbNc0<)g)}v_$Ek})Jm2GpXS&>!d zCp&Q)No!RZv!v!;wPd-<3pJnuD7wM8;I12Gjn8I{-Qr%eXguuOP7#JJJ3+=_L55yh z?E9(Dz2^}|9GqJTE_EWsjUEFG#;lfZIP&(J;YXucO|5V=S8nY5Qr||5Way3kiV=rXqn)2JAdQ8ki=WmJ<=uV z^FHCR+~ekqM+lrEt&;9Gb!1bifkmThf8^r*qc%nd;f8jUueuPi7YiE9)h}x~^|+E_ zKTvN`($lz+VG&?-)l@@PtgGk2m5Rl=Yd3FAst0%eweLU!&;MYe&w~&C_c1rtj??wH zfX%b&G(Dx!@_&QSM(Y2j%4N+*OaDFo|5N8ESb)ucIWW7`O7*`a?EiiyU;~Du&pP&w zRu=xF3IH4%f5bdD);{+kE=0LUX_@-U;xjZ-46|TEaBt2Iwqccf02PmbKIUhjcxC@5 z$96H)7%ke8(af(vg07bXojF5r6&75H^MfN%!r8LWcyD{NluSntLf9*_qJr(_GK*Id zb9RBKnm(!f(*H3%xng4GaNUct=5zlbgcE(+S+YG+ATvSocHf+3xWvyjDv+23)Kp{8pYoQYf|Y#*M~Tvv+d(as}Q z@Dk$)s|v=#uIDOk%tw0Fnt#GWA2N zdb<4F@Yp$OrtQn^OIBB0Cln_2JlM2}v!^d=qeYF?0zHlrCkZ?>!)-R#Py|DJe ztA>RT+p{zRm!k#lmmHTyZXSBNWDbP$8x385YX9>pV1;xX3rs3gvpEcOFg{|XO_v~g zuSEye-{vDwt|vD8-qzurY2o1n)oT&`4OtmA;;)ZvVgGCwma*(I+!r7MOrRE@y@&6# zi#||5=HjAFkQbO^3h;Ug?h0WivrdbqCUQtTM^@$PSNI6PX{8`o&y#vl3W~l&iCdG8 zy6)xEg6UmN&-Jv8?LF4WlDwr4k~!rWLIfNbIU=9x)sEH06$zKa)|KYLKgq!H=0+K_ z{3Cn~PwoEXY&;frNbih9K}VW?TuzdI8TNBJypa09X>>~cDos@$EiX^98HaC8Pjr`J zUNf8waf4az>9TRE=JoF5Zp|A{_AE8e+6JPq`T#uID?vwCw|&0r)^oX)iKPv)dWGN< zXG`$=Syj097Q0x>>yqWX?Y%dYEg(%e)=KcnpT_e+xToAok-yq5=@{j-i@#$aqZd4m zg2t3SL(L8N>yc(PqA(bjjb{vXdsO!vyAhj^jfmM&rA-z*nSzjz^@?$G_tYpP^_4@I zJ=7>>J^2Mb2hYSohTymw6m-R_L7Qm)aVew9*)-ICg4bezzU2nTJtpJmTVu3ni9E%J zlnTRqaxP|QnBpMB2QT`|peE&od0tYEW7uG~w|p3n8*OuP=(sypZubX77rjVsS9IPW z9S5`z=3%mwzHWv!PVru|@WBd*ic`!u$;UNGHC}ipXoK9z{OlgKyw1k_zRhiP$NKph zNAysaVZ=#c{~zc7Q0xUp^rDB+ozbQSRR*!^wJb;Dj5Uv2Hmt*mj@_a?W?eqdFu*9Tv7yQJv~Q>;PAuVIyr$>I}7umY-u2PuMv}hQs&Z3shw} z)lCcji~x+urXV8F74t*BP>X!?+L3tt%JO z0Jp5(R!DuSmlCxu9qKmIVQ`@8QZd~aEs}tRmf9wu(GR~u1+S2q;>A7UM)rEWh$FWr z?CBp;IwCtR9=0Wxito7{WaP)+#n%99$o7v4Bz#1^P4hST__sEn-t_ zG-noyjC2u*{H9w<;1ye{U!*?BJCVt2PUbBZDhf*V4}#2 z!xm~(L9}vx+Th_Of8vA17jL@7NV0N$CJYX&G<6veZbKujiBQro+k@l=nv(OE*4Tqb z&wZbWyuMD_C2oFs!|Vo$-h54t?ufIIETY*g`&dgvrteAT5aLiL^&!NHgxu{%Ue$sP z%K>?a!?>Ae}I6!{)rZEa=-e z2@==;CJt!Obz@MGGV_m~5i=Ug^o)vlP5$q9Q84AW{k|lLf2M2 zEe0X+3aMsLAItfTkNcW(CD>=I7bK*;%C@n3Gbkwe-2J4 zcdwVbz7qBIpG;ffo#jJ9mf|yY{2Lu@jLQuhwQIv_0V-fc_cE}SWaaotUS|j85aAyy zNfUepsta_(WA)p*UBwZP-EtsF^(B=%C_?pPVhyYg^0%@r*CJ5z3@2eb;N~Ebb3B%#X z;cdH_t|;i}t*XD}N&-K#GTaLg`kjF)H(L*y!Yn1+dqG>klIM)R4l?T&MtHbBnifZX zZP*d{8k=fB7%X&v-M6sU0vceXHvYsnhRuHyoNdDQmH=@NjL>Qk+m;*3ShU`H@-yRj zm5v@P?CQT-?>ghXqxNIXU|mn}*;2iWlW%V14bh)$DZAvSevbpWe2*2Kb+Puy05P%1 zq&5qSJ*yiLIq<81q{0N>Ni>sY$Z+;#3kc0n9&b$$ZzSWlNbS`m0@wY9U>rj1LKc~w zvG+uSD%}}98?1CaL@d)3n-`r|+ZKeU=1UeiHh1#$>4A!O8LsZT$zp+TBJv=-@Z$>Q zd{y<`y-y7cHEkSI;*aLt4!}wiI)~q8_9_NS{_&gu(HVpq4Xk?4Y><-D-W?ZiN)Ug- zpyvE8Se#62NRf3HR2M*;Sg6kALWHiM8W$gYPZfyG`Gz0Kzv=R_w>r?>X_M)ZwBmx| z>||J`3?6=OK+lOacYero%o$-Xk328&FI8kl&mX#Az3?$L(Ds8rKc$_w>Hhc6s>EU$ z$KGphB!==e_@mOx0>0QNbpgm!%4vzswMr;&UifsIyyyn8=U38VGxWRnO~LFn))C&w zkBfvCTTQMx(_$G2H6&}shla=GJfU2(b*9-$j$EhgLlk` zHzA~CU}_XpAhM`bU+to`^TblZH<`ZNQ&qK_XPK!fRZ@S7ZVWd;fVxQJId#}a?u)nF z$KND`Q_pqqb|Ae*%DzuMdq4ZPC9KxOBo+M63*k2w)|jwx)F6s&e#(5%mCK7)Z#G#+ z1lmqw^a%5EMGQ}N^6J+_g1lD4>Mt#rmnFBZ%(6SYpvsu1--RFHA*#NaitTi>mW2NH z4t!KES4Q3w+AJ1GbQ=8-AN*C@!u?{Lx@{38jXWP9zdNIfwYku&xdsJ`9)Oc`W zQ+LO8j}vM6=b-lo?<6+@{;^k^upBWl8u}q~(UIM+3htpB-U_r(!Y^H|f6Gvk#r%Z+ zwvh2dwZ-;w?1=4DNuYfiS-d>(0IK4fo4Q+SDwA70+S~DET~CKs;Xwsw(fa?> z>=>v6f@%%QCo2rZl10AKffH9kg+#jOYknq{+|KBhuo_UH=_Vu`MQ`(%(e1~ zQTd{>RAxI|p{qHi1_-E^r*IaHBsksUDxj#t9oKu2Fq3W7YJi1B(sLN~LTfKKAUd?n=1a`)7rg8$2GW8XdBy27y7X==bw-_C zlj)#~`=&oIy7m+svWK36oy@yE_~N(qt_Qoc*o?CpLm@d|R<HK z2=B7W*_cc)0qj|dsQqZzJ$SES2%n+gFrPrwOsc#oKoGb$NK?w0e-!aCX2FN<2C3O# zHK+oStGo$^Vw3|BLgqai|A#S?~uEpth`;Gr(Vt9M+5<28RGU zmFP1zGnib{`c*U1+TExH96~(NOHemls9dda#aq6KR^C&PNwV-`QMB+fLY)V_vNT$fc1QW%bY7T?H59 z?i>bNRr|i{WgDKEgXZNeuL0d(0Ba1yVG(q0v;DtW0K>!*{K)7n&DOmSyL^MxjCZMc zn?E?HX+ea(JJOTgVZGJeBRweg{CY1;-VV7(PXS@6spbCyvKr8G>%Z{4b1b z2FfAf$(j}t=O9JB;tPrAKLZOVC@7^f+VOw#Bys|Y+irgY*q4}1+p>VaE*D=Pv3d+y zcTyU+Zar0}n#;o2vlQF-G$Aq}qhu{euGnJIu-&0!(T(`&7fU;uZ`tj^x}lQDDx0mm zwO1W8dvxg!vU!n+>l>Y-1BQgN&nc2Ryy)FZ%0<0&^AiAKa3;4)Ghi7e_%0+pOTA(%Xdd%XM zc+)9mBOp|d7w#?YN6}l0lr=h#X_QJ#fp}y9u@a4!6J7QfNli~1ZfmU>ke@i&!bkgqKlTl=cWP9UkCw@5DRYQAbg5dbKz;&UP){oF8I~878?;O z22Ntd>RL3DksV8{LU(J^lMwHUp$eaw;!9Hw=hWVBZ%=Mq|8@ine;H0`H12#r^PM_@ zfT;aOp46c@33J)HKc-_jQG!(=;`BG3V8L1 zwu`hERnU^rHrW!{@a*!+tc5uK-h z)m4O^u@uM>F#ejuf<_XVbOrQlHuv$j18E62Bh#ISi@$29H0ey)gDok*VlpvQZ1Q{0 z#7fU=@#@tp^c&onk|ik*>X7nF9m;l2iMPfJioB!3$WjnXCvYu@-Tv8X@fChTS8Kr} ztNfq{jVsqti{nqnQ!3P4ta`$AogeGyIq^`$=pNmnwEa?t7R0O`UD|?bqKX=WD2hDG zP2r!m8zImZctXQOO2~k7ct$X)SA>P^xZnAx;-fGGK&I%~)o7}_KTm17nKkQq2rGe0 z*8c3rrH4tX;$TE$Mw_4t4^BgtRnrpxtp2^fw&IZBo#_*TQ&V05$x9}J!17IX;KXI* zL>d%H7|SRc`m)Df+bkBvVyg5aT*zsf+fS1!N-di8GahSSuHn%({AP4XMeBcV>1aEK z>!Bc+66mAgS4*1RI3r>GLlhy|Y>R=*pFWmz0Hj~Bs9$vU2l08Zww1w3-ViSOf%Zw6 zCz)!kdEZ*k7Va`P;u>!^8FYvmYaM`TO6F*cH58b+Vq(7%v&vXL0s_P&l7(|(Q9)i8 zU(YaC4lR;`6~+@~Txd_@!+(8N$Y<@EN&Wz;s(koYnG{Qwc<)BEM1(}{eYY=GPmSEL z*9nV^eo?CbJ)>-UV3mU!J@T!Q3|_%CwcDKWi+L9ena3{-6HubtMAyPnb9T+3%_h5TeCcjfVcR>#gN%)iKM&cJqgmcYcO|u($@MK;lCDE~w}}$hiTcFM zf8XP;#SR1|iYnDizATJ_9gY~O5Y=T#9Fj7V5O9T^-+yxaf#>u^rO6axB2%EGGMGNq zno+oE6@?Ec?T#3uhXin6>KUwj9vE-UYkwx@3m4L=%XO8pV>!SvqnPXcy6@%;?tNi2 zR&YUz&Gj_HRSBKwT}bJ|v|oM}vBq^bwT=&s?H}O@HJ)$oK$A4 zY0{wgS!%b@HG8lS0+89$;-^om?MMY3(PlC&#X?DgwNDBiU+Rg&1nD@GRUKZ&B6B*Q z|0o@MEjE5!EE49<_qK4_p^}XqF)kdc!D$UFx}<&;7K2i73WTD(;f4*K9<2Z2#Nr?p z`9hk-Q7`_SuNmt=BfFz~@K+ktL4#G5hDNPscmD~E{M1K7_Esll2#GD9g?r7$cv5G} zxbBj=mc4H;Pt|NOvPK-%)s?7!`~=~)J}G!1T93>M`z+pJZ3})02z8dXpXiIvTv*xw zO9|_a{lQYIb^6onONb7~g*!EAQEl?GAKb$57;9hA0lrdOc#OejY+yzpsuhmP!Xq3D z4QMQ@LHnU0Skf7qPNxpXpPcwR>H_(#tSCx$>U&>#vLEFJjE#XH7%0*t~rKZNn zvLmle<-yJXk*``<&dtZBj@xc6laQ!OtF7TJcIDkuTp!qrz zBjeLu1w)p-%f(26O|3Q{eDXdv5ZQ-|{vi-;VpIG#9@U>wSJkC(`w}MSzq>&z3I9E9 zicW}LqR7qZxt5b#-|%1k63{V~Ja|%NaEH`*yUerVL@XhBz98F%s; zbdTn$OleEI;}N54;iyir0zNq{(`$@!vCK1gZF?(hYZ1W*zb>S7HIsEwRSwtbRpKzzI%QR4b*&2kDX==jbg0#Ou!NUSBPEH4-TPB6f2$~BpzKGozOA5^ zZfePDjgM!|o!F%TwT7Zqx2CIs{ryC69s8vCJSlYR?-TMBmC0Jqa;4TIG20i3kudJ2 z7_BEmu5KdW9&3@d31tzp1za2Epwr5?yLyF=Ndm(a8=B%una4y-hrF$Tu<0!@E4Qv5 zAAXBx74QAKoIS<%C)t8KskkQ4|J3;AumabnuVbYbrH}~pKG&HX+hfJnKw*KUy0Pc1FY(n;zSoy4^U*n*xYkT}?sy zVxF?<^~s(yBd2@t#EhFCTP-^J6iFtQF(hhC^h=KE5tiIs)=)@@h4oMtQ0 z?NNO@cY2;Sii2s`&#p|^vJT#Cro8{$FDO{_V^H5zgZ>^Sm>`ihmu{{#I~7`{e4P~N zal#e9XXX1;dxk7Kh9$%0-Ac9VaMq32pEQQsU5}s98*wpea(^yjuJa5pc)}S&D;ga& zcefq1e5E8YS#IJY`5L}%d^>e4b8+^pJNe!FAPh51mw);53O;!U0->J+ij^usp4;h} zJ$?a=!~v}F)hEHZ^;8^DYS@U|6}i~gR<S1N=j&p`vH^|FVj)h;0g z7GTB6ro(h>=||f1%OP|h4$ByWgLg+ z=H!yDZ#d?-?&n^;tEutX50KxN#f+%Eo=x=V+t7;zTVxp)(y>H71n6oCROlCa&HQ)vDF?~v9Oxkm*F=Sl($v) zJW1-|qwc|PoPlo99NqqbgV%NXqikdVaGY`?St`QW1R^w%iYwz5Ln2KfPLlr!miS5E9Y_rJblD`9r zWu|8F zJI1IM222qTZxq5QrHMR6c2mxS@yPZmU6TA`xk^bCbOrToixM|YBq}5ow zpdOehgT*NMaqnsxHQ^8C zog0)7*4BsH-(w5cJNbYPER$@=cu6H{xf~>y`yy%xLn6V7h|hS!SwyNqM}TBuaAeLF zzWPVl=kFyp`r;Wgd3Fu?rwu9N)+@ewcbuk!e53P3!U_@mTH58gw(i zVF^Ps*F+I*G!Use`|)wL^i z&rWxAJ^b+c(akdssT1O99^S)D`oQFx%piv%r4~!C=rceH@a2ZOo2x4x!J*40bNuR2 zVfNJ9y_2|g*I2AFVQLI6Gg*~S;U-emrL5%$pZQ@iPsFsr5mdCU;d*E>$rxu`6Hl@xO8rMM5Y$o$7PMwyfBKLfvxr*``j{KVig}Jmg0ovn&IXXlW9b4=n|{w7HY55 z^wW&VQ!eGJ0`D7?ca04vh4;`--B;b5EisZDXNnlu%P+9XJa8UO8fP$z*S?5s3Hj^lK*{H4z_Vt$Gg~s_S^TK zuLf!bp=~RQBVc>%#YLrTKb&m!CI?rS*VO;nA+kLP?bDxYvW28678}@o&}Xw((ZiG* zc6R=&glE}SHiDo%Eo&Y>hPESXKhgri_U`4($3hBe22FWZ4}hRI3bC8oGUHx}prd3t zkDke|jEM?tnWs14mTvw$i^S-p79#Mm={lxu^XK~_hr4Cn|K%LEBQ&T?Ws;Ed3@73_ zd!{ZG|4;6hL@s7I*-Y|_9#@7$JeH{|L7%f>T4wonh$BmQe|Asdf!6S3b%Tzjael;Z zmH37x>RQDel-G>VHdOwZHP7K)5bj}_b zuQwFLB0^i$%-h&@(d$AMtGz*3JG|_@)mKs#V(~M&)q+-Y+M2@K@J+vhBW+O1Ax@h{ zC@B!?UHlhZK?0pt^-s`Et-^Iy@!g1%nUD_gWqOQ4rh1Z!jaUkeLT-AZ@l9`jx%M_0 zwTHJcM6@kMkfHWFgMA4`BO9LUG$joI@*5vd=rgNF)I4W8A!Oa8I7S%Z{k)MH-K6?P z3@8NqfHUg7`6{}1)jbtqHoNkYbTRmoU3ks|%BaNto5LiS9Ouua2S$kkL7>a)yuwC- zwz*$jWSd8n4d#tja^}?|;U=|!E@OQEt%$7etU)Wb6Z{UM-M=JzDQ9j#TcZ5f%Rjwq zo*A^>iP!o;k{R3P#Bcqw@K=-gw!1!bdUCNiVD)gxFO)pmav^$j_)GxLCz1)?wR70E z1+BSl`kno@KW$2cmao0}j*gD#7E5>N9R7~BQJRv9jc&F3Ow0LLHLIc^mnQQU$uzf! zpNvydt|I|O>yWXYShMIwq95Kfs(e9?@4x9Y&Xt{eRl5~)9BkOsY3kb7C3P>p%NRYI zJ#GS<)79o>M&feuDiuSM9K4)-9yt)~pY2ObKJ~rhi#!fY)4{ZDdeJ|IeyMpqFkT>s zjqNzjw}0_cd=z4*%@MEO`B}bsBz9(I8mqqS@r>!F!1=IjtPI=-$r@eK`RdV&ikNxF zKaFCV{bAp~62P%>W1*>sWTvx5YsVlca)MqoUMg(w97D33Ni z_1TxXsU)Yrg_`bozn}Ef zVpe&**7a%XYnrB#IJ511bAk_MwYZTk@D7JPwoksC#4DQ5KKztAt}RA8_tI;&qK)gF zWH`tW9_b0vVtm{!mpibrGCTj$zrH0h!+&65c_4nc1Nw#>=|XF6qeL(djg8-rhJ(DAI(f=cziEl51+b78X+ z2a{HOU3~{&*nzuqez)C>HF#qB0sHjl}x`H?N~FeK%;vKZ;QLCJ(=Ad ze)yA_{x|L#Z(5~Jo)6AtIEN2pI?b5V&lEO|DhTp7|000DL48N_O_xtx<;4+f27!;- zf_C#nyYzUd?=7^ixcNw#N4;6wanB5V$Bv2ie0(SGo*|6@H?Ja1-TFct*$wo72+gxe z?Romc`xU|vJ=hXOxl4Dr@PgpfE(qUbuB}6NN5}Zc4;U-iR`H1v$kdaxr(v)aEF90Z zy~SS`>F4`8!7jJ6X5ulYrcT+uQdoULQFWd@P}_b3l@0128o{831Tv>{zTLJzRmPuz z`B?KwIY?%c5G(MaV)2j#g=O!BQdhAGi_3N~xi(g0nlS!Tl=t={mo_w+SgQ@M!-?vToh_JFY;y+J&35urol zf*#gU%e}NcW*>Y;kRNXnr0I2H9)IyndxziA<`Pci)Srh8D>o6Wc*+%|!!?S;EKQ+Z zy_(*ECNa(}=*t$g$^B5i;ns64;gayYWA)8!Q(%m@ufdKrinX{A%OODvxgp=%`u63w zUP8$`tc<)IVb=>Jh%a4WEhEcfzsa}#h+Nirx2!Hkgr>Ffl7Cdib;ETkanKb_Z#~Ue zGgx2Z^u^|*k#}J{^1Ic~!oahUICu$6Gt{Xe1xQAa}x3mpNhK>6k`|Jrvep7~6m#(KYQ9aY^9Lo_BkE1sFomVCP zoBEyD3T>^_gGAGGi3{|c5tJ6nG^CMkMGqtK=;!r!vn=Ni!zA~^DCC7tgY0T6(^+s3 z{Nu4SENeJUvljf$Gd?BxI$N*^WCe<%^RrVG=f+VbXH57~9zz@PZb z1_vx79Q4=$W#43UmGsc<)u`EHKeW87*W6Q-Q98elpu$9?+V|YcpzUub^AB zS5xIRf6~<%@W}#mQqv#VqIs0SK8e=ibsfo{ zvzipX{d^^4wA4g zJjnL=+5p{s>f?=3G2}vsNF*Z+K0tW1vTvy2j%D(Ool>`6m3PP57q0~z`yw}8mSpu6 z<57}W&-sE@Sz;S#5VnpZHBJrdUm#d;uKAS4@?o@Myd|})?CrHhnRl2SIl;IA-PR?&BWfX%jtBOmXQ-lcKjD-$;#+^WHzD5~~b|&Xc1Q6Q|W6 zmrf8PHy)6mu6(LNH}LzO-z^H9VV=%AnO=X;f0r?2wvh&Uu6Pc)w`>m1E%F_nERi)1 zOfnR7FH*{D-9hjT}6{~E_)&qZtpMU+uGsG*5n3`<{dwLuSX8^ zdkk|LRZx~x>R7PT5`*H1+VBHO+B5t~vz7MUa|qgy<(JrF>lQjypii^)M9u^@Dk(%)vsf`qHe$3%O-ckl@Ye4CjNvTA)RF01!l#Mc68 zFfY|wJX{a6-B2*m${5viCM)#Y{07fIRPmiT`H_i{XOz?$7^0pbJV-?Md~`l^>gwri zTo8(yAHuh6bv&jZXvbbOTiBaF&t}+&5<;Br)jl{is6CwbJ~+@q?@p3E6&`<`gkBeE z2GMQ_T7U|GzjH5Nza&glz#<6LM_*mikaA8UCSlJ9li-!P>#A5&)bA}!S4qUOn#4i zF$~`l1A`op=Ch6EC=e#`ZcCjve3-Sv@N5R(%=Cf4yS*cQ{BVH2 zx$+({qkf#aj?-HkLOjk@K9@LNQH^YZmE+gPCG?y{{#lTF7<(1 zbw-c)KBqqaKE`5!`Z$Vwf~eyI{b`%3W0c<4Epq=$%z6AIOGM6Z_u}I%p6-tK9j}p{ zS-rR$ydyMg`A)Jdh%?aez4E$DzPPXbh~asS{$2j~HOi_B3kRGI+mXjq9`4Fl|7}Vk zxHdAMWA&rAxR3>j)2=)=*5%=|i-W5ZIM>?NM9H3gmE*yoReA-O3>s^H=f2sd=GcYJ z`JSwwyDg9TtgZw%i|!AsoSg|84|ki&*i!|6F$Q@ouXE__+h_KeQR8py&K$Vi&u4zO zpYRWDXr0Z>la8aFFxT7v7$sdSgO%h{>c;zcVzHU3e5U+I2He-tc_oKOb&O+2wv5`? z=&kqSe!`PZvi^(o5H@ja>l*?mShloas zPehRj9?Y>$l$>%PRMZBke>>pnL_*#i@<@+jMt$1BtXU;hE>Ha|Q1Xg4!0!EYWu86l zm_Z`8cpcs08wHMw66Myty2JhE%LQy~-%jp*flW%gU<4^reK;iU;5V)u53B+`NXe5` z=E@}%s;~a&K5E?hEj~V2&cINiL7-n%_(TDFtP<7A{TDdN!8`IiIH)- zF@W7k#<~0=@BR2!Ld%{7)NEMc&nuZv2FYThW|v&WG&;$|n-q{lI=RFNCz=BE1Ik6i zHOCaQL>`^LCC^q^?T&aQ&pm=k;knf6OQ?nOyIlXsE`e5A;t4KY?r`uDHvykZdB*KZ z81or1MykV9xj{;hyU3lE_U&>oiE7!_SfTQ(6pYd+_D+)^XS_=k#R#OQA-nR>g_#Dk zBKQ05bm)d#M0V@p{&qJ?>2QckVDbuuf^f`s)tGUZwc`F=j3MVMJF5kZ&{_!RHGrIcv6UFUPo73I~BDc(rLt6gV&$DP>3 zqvLz7=i_SC3{PY;1?ES;s?alradsw)GuU-fNbIR~zL3<&C0NckmymL!R>;^ZR4Gd6 zxlqDGBz)f2aP`u#}iTEI*%Tp1989WxE ziN5lZLSV1gdv zmZ@2oAPdF(k`i>l)=70~ zcEm@1;rjJ$A^RXt!`SdS`tA9oURQ`Tq+KOSk)+##ocPvpQm4QiNfFt)@Ku_BZJx0I zW4nht*X`+V1wrlpG@m5kD5GuQ*836@!5WnAiG1-065;9Dz3966!garT^6Xnw($r0~UIR9B`HjB6aD^dAJtK0>VCd5B}>A7Kh{x(uR$}bo%a9TCggy9T+!z<0OZs zYh$Tizpp+2h)Zw0H&tPGIqy8H9Ls5~J6&$zPwE9J)@^p1q55vp_YCq?b2J=J@gp8{ zsg5Z&wJgRMys$-6l$$^)r$Y<{^WLv4Q6AmV4aPU0eekUd7D!HnxUzW%XT!fb$Nn0y zZlcwsp{AWWHg0r2d=~N+d{?1%xLB&XW-gKv;#-srSHHi{fY*Yx@NWc`$B&X z?LU5y$v3)(DVPR5&V+P_g>9B_WLP|dO!RSTZiZwCXIe)9K9YQGvTbxM#7M$9_FL55 zOT@CESF#tv&egYsKj6zY$JO*KzjZZ=Kb&ok_F!eb46#^RpI7ycHzdcI3FR>dgV#p2 zw96Jb#3E2|<&_0|G6)_BQ9j?N*2(aa+Zs_P#Qu!%pQuF{orZLUolN#3E;)^~obj)v z{QW5{NjhxA`rBzBL%zZEROZj5c)*!!cUb!A@mf%V(BivA9{nz+mwA2b#(#PK@9RMW zl@KYylnyZx)y6*|2o_5U1NLJ-!m^?1f1?ug20};yvc@TILJ1RzDthOcvEQ?*Hp*^E zaeG|Z6g#B;%9X}$EYe~yF>Y7dc~H~y%sXx~GOp`~XNZiH5=b<%m?(}}Xm;0=S4;p; zIwJR3La_dRU$gX0&}K;}B6>Yu9KGV96#4S&t$Ozd;9)gQua1@*j4|c)EoXX2ytVyF z_7=bEot^+U;sXN58G;BkE2b-aSULp`MZ1(C2{S{>!Hf9~5MkmhXKT2f!^*AIM+axJ z#l!YWtuQ?i4Oa9y=N;m{sq;k6FTZRq^FM7FiH_W*z(RT!8C+jhhP*MLrjd#s)|Ptp zW97Bj>sd`idb$2yHZ9Zq%MaA{-;2GVrB14wJ_v=?R_} zpVRJyoIj-M!}N$5zbtgff%_NmjSlSx;lTod==mc=tfFz3(lgtP0~_RUVNM&j*Eyg| z3Ui}8Y@6G@Z_V)&&&2dk|LeD|P=xWx9V|c~I79sG%!q^=`cBuS&Fk)1 z^89@^O6MBqq6dX!B++n)K9WV)&}rlg-ln2u$X{0$0$mq3?+>-CoAa_!G6s6%Xo~bj zo$=dS+jf1*qc8%1M^2=Ao$wCDQ14tenN^%lt+aSSwi8Zrjr7Cu(Bo`u5sdkFAu1@x zAKA5^Lxz@@U6up!a;+CzAYZ#*9S$Lasu;sSY%@{KUE&FQP zEE6}Ne74RuC+Meky$;AM zD!R)QN@9bg^_1*As^AqyEfJY!g?I*?%Ga$;0zuYeZmaRH@dJZ@p`1_%GNl`GPCym9 zAIfh&{Vn;PDVYFwd#xC9arQy3b^#GS>ds##sC_A~im(xhj{AoPP%%Sov$ul$+qAYh z91=x^1i+zTggHc;^s2=#K_BZcLP9g%@?V}-bd!Br#td}a`XyX@WYwM%>at$L=;mh) ztQVP@<^9;pd)we;_AABh8YZ2Fch>M;I2l~FA{zs#G*TTbgbq=wY#$F{@x97xEN4}R zYTpgF>!4mr1LN>P5RoF47N_QmPg+yXkwEnghC6PYx9x&lJel8267#nz1*6mBx_eNO zXlrVh{!*hd*U6&RYF@Lk!TaIbY*HhJ&-LWXCcGaOwhO5f84JW5#?Pfc3F608J0x%` z`vm9ZXPq02_^sH(#qZxgG3)6D!%uM>E;MU3?kMNU;I8uUfu$dk*$Ph7rNj@5APxks z|0k`!pc*>=S+tifIx*juO%_`WqwHhXz0rawub|`Po=X(Iq%EGIMCMFobZ4lAgO^YM zFR=l(wNjuy=lE3i{Xw%w-GGP+WtDkY92GX$$~wHQ{YuV-46-mXYq!x~xJn5zJq>zp zbtF(?vMg5>bbqlwyX*2MMBPQ9C*rx8!fuV*`3}!DA{^FaFby`_z%W%LBztQl7(*_R zDP3N_eQ!KpF7?TbU$5-Dr+iFFApwxoqukH4k-w)5^*3OV5yW7~vE!Urx(S1Z2;ETj zHlHj~Oh`l$6|NSNu|p0{))Vz6hG<)OkGLgkxx0-zjx;3ZLH+>soWHQ8mLVH0;k>`N zN660nEb*C>LBQBoy(oQ7ER)vbNX9jqD^1aM_BcC?c=Dn~r_sq=O`u_(Wg5{^$T=0a zs1?EXqm_q+lxU(rQ^i#9n{ zK06-;$xJ$z_eEx9$EveKXc8N|z-lGTXK z;K@Hvjus+E?raQ*agP}g(+%tTYcGGFo*e+gpR9QD*){c_=SP16cZiGSC=>tZ@roj( z3kN3*8kJ(mPZp^EeQOb1o zrq1iXqP@F>hP76B^eqbzA2m0?20G*~41eBPhzncl#UQ8_gNbfWYz2^$7I>jI#m|A) zGcK-@di_$f>I)ICldLtpa=vVglY0{hbp5=6J2O5(_eGFe`iSvTucc{q`CEp-hcG;5 zCePc8_>@-`KhvETXty+jn!&e}p8?K^4)noICeGpYXb(VE$`(W3_bXK{h-A=fnLFek zNO|?T&vY-VbrRT0$Bo;^TC{7&GN(dyOYdxnevzkfb%n9Ue3uUVNCV8|Jo} zttrv<253z_rDU}EmCk5Nnd|9BN|{VNog1G~H%dX;F}Yjm_XoGlI-{OQ*l|m-?2X-# zyfEb&Az!!*5U{y>lndm~n;vd<2P+yawQ8*#M>dBt%N&!BzsjfZGG9oB<7c_rC@gHw z^+@npv;b0<_Zp1I>fWl?<^F7e>(!qGq>Xe*fo&wtyd?{YsHGK_z~o?3kQz1r!g5(! zaNA|H{DhgURADGS&nY7R7@fHI_JGr&nS|R;EP+XfuI9Gh`}e~E!=OM@28)9R3BTLh zC4NxQKzwDn(B#_S_`U602=Z$HjqGCam~|PB3?m8HBt|u>-jTUq>|wg7@1G|!Xk?p2 zo9>4(w?x-<8xi^zP5&wdLF-HHH^m%@l#4n0q0n@8<6YJj#b*raq;b@8mC`3*K6asL zfujXNO{?8aB9%?$kZbw< z;C>CHO}T&ucZ-DL8A^=zz&T}6_ul|J^uY2dz*_lvUrCPqh;${x^FpJ5fzJc1M(4lFc0E zUW(PFR+uh5hkDw85a19)S}Qd{^y` z?bqrm<{H^W%>qN;6bP?~l2)0mv5ZqGQtbs~-CJ1-cisfkhRAt)N*Ca6zL_Zq9cL;Ls2!Ql|j#aeT40da}GCM-#<)h~V!&V;WA?FBr~72RvAH0jIN zY7(Zr+#Z$ek>m;ey`Pl4HRBj{U>V-qpTXbACXcOi15NMbg-)0@J<|j{-Cl@3v9<)6 z+M~N);#R>3V_y_I8j+%);hvNdVXGGfSyfdAj?$(cfkk5j?B@Tqa@5j%vr`18kg3rv zIm&`&2bKWjt7I)tXEtsUMl?o_RY&ggDUgX4i(tFzfgB>;^By!}-Qh-pd?N}z=(!BC zjm8LPsY14JH|>oT?^lveG3C$4s5wYJP~9`9J~f>UMCMMK3UHUGZPeMt-gTaF zL}bwu^&uHt=hSs8&bYU_D9G0ICP8x622v+6-7Y*tYOV%5v4mYu))Wtov`ruyNEsvS z<|9Y}1%k8Ry}!GOK#plz+>h1JXC5Cim4=4OFLWT@#jkTi=MpL44|vfL6w^M$Sag%+deoptGd23!VUAFI;lm00QP!HA zj~d+RDO?hFB*BXLXg+G#MPu%8!Q#lF8>95gvpo68q=`7{ScDJ|6-KCKefq) zVq8NXUH-*qe}DQxp^p@)2I{{sQY#hkE{W$U4J$w? z0C+~>ynI7SCXW4SvgRHDu~?9BBgCVcnpWk!8_uo-*hOEpuK~KSNOKY98iut1+8|Im zD^5em4r}E-Y|2^Sa#=g_YYlMOovAK@f;_$XM(0G3O-r^hsFw@Tz5Vi8_g8;1TOUBZ zRKUk2fMXL>A5pW;gQIa+oe#7NbsB407!=o<0nxvHcYT_&07@a-AUXb=0i0zo@PE00 zR(8`a(R`)R>{iW4xakB+6`GrX&P~6N&lJ$%aXu(M|MXH(1iX0my8HP~o~y0g9L*r$ z!`$WPq2wC?t?%nW{Gms`%U^7EuT?HJ=m>HnM$rdk(wIFWI`OBvsn7g%hm(5VIzJN_ zi}r}U?`+q7cp{0o$pL^JbsH`PqY`pH(%prCGFxSyV?yfGW&z;mm&iY^DG?M2Woy&9 zY};48>W6GIp?2tj$+9T4g?WtMwhk>!oWC*~BpR3j^-BK9&%*Eu@pLL_9ul%UUiq@g zd;r#-&HmEwO-K*)kc73y$WSYP1Lg*iUHa80DISwQqPz*|mK?3WYqS*AbkTZ$;7`2zLKzlb*p$yI1B7nZ@E-ZD%wro6PROVH(FMNr}cwVn3R`2CX$DK8(b_(mmGhO|+7eJF+s^vE=W3QX-f|L@&u8*2SphUp; z`{A~X!qj%;qb!@#+f$LZIi-LiatoJKyM*HD&Ei&n7>6*~JuOiUB>5yk4pn23rgkVy zZhqFe9EAGZ;djgJ!Kl4t6tPML%JNQ>G(i2;L;(+g1YB)v)_?sc%fCXZbIfsG7~2So z`15|kWIm;9AjG`Wfztn_21K8WZW?w=?MrQU#%KqE50aY z8!#S(zLEH4j@M{<ptUnMh*yR@m7b5?j%0{iokLJD7g(0&7nD3 zMa2E>C0cp?$=TQGv;}y22VZ#KwcWWe3wgf zH4{|y5G^EvY_p;u5(*In|KEzha@^^_-3R!rG3ASS=S69d?yXl(>e?qz`@#5e-7Wc} zSLxEz0eDo@qw4dFNn1S%07TdAsS2ZF4)x(cY>ZNtu~1~L{37+8u|f(}q@7;UW}pR0 z(DF<2H}J!vrf+k**el#zq75+UZhB?C{3NEy`A`>k4iSy$*9Y5*3P%@f>y~R%+`uHC zS6+86mZM)3b11DBn!J)_lURfS&q^8gj4>8K#!1Xo0yfjL7KFdh%m!}Ze)FKkF|zk8 zzXKF#VD%9CIU^&tJL)8?JJe-)YcQ`_G#;7bJHO*g(?r{LGDM}`X=Py=nc#IjF&@Ix z^H?UzNlPCyq;)|N|rDTBg=DpXNV`_5S!I+V05AURi2Iuo|BXPoR)Al_| z8r}p)fSEIP%f9QL>hIB)T4cV~mJR=MK7}lLp&M>CI?s0y2L(e4a8~nM>At(hDb|0= zX9r3>xyz8EL(j&CqCMu(EL`bduxE}|0k zFFtjSAF>Q1R$eYoh6N7YTkqT4KU(bxO)TAk3c;f?6arQ$j}I#rGzZUhE}g=Q2M(h` zlRcTYW`YrC++r=ct)Stt6%W>X*Vs-rpV?3rT9@X5)ic5%>pxlV!`rN8SvcA$DsvhX z^;pLJVz6iW5+VQcoGjLp-~b(t{f$3qRW^$N$MLv%PoUf8X%B+oROId%Qx9}{u%uqX zy0K!*gkS%82u5oFiR?!G#f-)F`KlfQSc+pwFAQn`+B(*RE0X{9nLPBf|Nrm)55`3)SlMWLVvCGrC8Bn%_3%3{ zr^h0Hb)Lle`$5Pbww<&W+2cH3YJY~jP>Jd~&s7);NfJQn<*Tvm&jaq4`*92W?oui2 z=EmEVBx`_dvt2k32#qtoy?Eg!5PyBbtn>BL*jEjm()tZTx##x_>d@?eZLL?iV#lRi zSEs(@uB2jQ^>q@&KEb#@N%5dqg{`HSPXUBUWP~6z++=p?wbLQ-?r=1+JzenQ4Uh28J&0*%s1s|6jua5HgoY3KF+@0NVt}ga8Vl=D8o-txcCo&z`^-IH% zQ3J}vu(kN%1R3f{m4JgbAIU$~>a}*ikSbK-lmeBvwXtnw*2`U2R?|JS?ctIlXic%^ zaEf+_e^hZWQ;?b0>EP4Q_ar8rMu!QWvU=*^#fDWLjb_bP+rzsig-((xxb3#@6-Hf#%{ps|E zErPO%VN&bh^e1BtRB~Qegb&+9*w@8R~E*5b;wEw38`|`=q+SP6| zVWn#zu`qGq`h97?Uilj(0%numSgPEvIEAE)uion*5W_JS2wE%@7tYz84pd$>QVD!x zy!J|7>q}K*k;h5saxgDZuUE2L|4A!Y!up&29hkKkX_$q!0x;4Ga}NG#c=bD9!!AP) zj()eGdn{aKiQ)y4%8M{$hczov#lXTdO>BPqG5i&YG zUml4!!^x7rVK-{%-9@TY47orUYYakc3Rss)*qi4ol+o=jPnSH2c&(a}>y$+=W9p}> z#Z87+4%bf2^`y!Y-L4oG>xMh*Cey18({6!wit0QyZ`LDRfrB6?L^#1CVMv3`D8BRpaZUKEOLBZH9}-cY`Bd$5enZm(a-Ic>v^KH z9Qn`PO18fWWTDQQHKfU>^Dve*%_&!!4L!3lavX_HPWSpT$sn)BSg4dgl2cSFQ?MZ8 z@;2%PFVpRX?wNK1gJSzxh-ojR$Ffml9O--ZYd+t9GvhrLFnkww?v6lsN?#FP1Cv2+T_JCvv93-cRB(Gd+FA#EKSB z*W=!8%S{bnNdSd!gJaiSL(`PqBw-73LpW7HEYcxZ$m;{61 zz8n~))eNh^FPvQXO|6+6A%)`-M39LYCg8~5ZGgcY(S1pu{7`Hoz8?vN3b zwKWOVMz!=6Z2;^6MY^pEwEWi2>SLosI5si`U)Q)0p-Q}DXpM**rwIkhYIVxBGKB`J zJcSxHrA;34r&n=rfO^R-FLn9yruSj?1k&k|2Sxp@xYXqx#qM0z3w+pe<@31UNKFt_ zh2WNjNvFg5LH&rCPpUE=>d@tu09I6s={?L1@#=2^JRcx%L)?AaNOc2Lpm%~kbg3K} z{|HpkexzEr5IE-}WB`GQ7u;(WUg)n85`ppCxK?t2KYdB&Lsfia_dK#Wf!HZC(%u`0 zrtET%A0PFz4ZX_%Is7Do9pqbd^ByyU%4w!SiE1s`ciw$)nuv{jk4k2oFE8}HFyH{d zAoKZ22aqFqYAfKu*EUkERSl+Ut@WBUP+H5>E3)$kG6nUHfGjWe$uJ;mq=Cr5Q)If{ zzJL#CDp^5daSeL+7=skWZd8u5x1@G~t`4m@AXHnWOqXBI8mLg;et9FzvPH74|K)pC zD3U*GImF69T1G`$)Etjao2dPUgwf;{^t26l-FD4E$E zHeY^yqy5b^B3CX|6{>kzSKNf^Xt?EEgvF9(0O@4RX@FSPe4$D3{@LZjd;mAJjz6Fj zN|OgvGBecLYoIzb^36=)xDgQVB~z?V1c?amCl45 zn8+A}e1%LV7qXPMV6>oVW}IgA``)$QxN&zbAnrQnG7P8UOW#r|0|n8&nd;X!mxm<& z(qM?vjY4{|sqbhJ=mV&JW);n43V+H@eX$@TOX_zyHnTov_8%rZlnRu})%_TtfYA|Q zsj%~^$D*`e!m57#nfY8@h5iXzw@kaP%km>vFy^xO+eLXGN2bPq&Ea}?+~b(G0T?}_ zF8=7E9RL*R)`jf#4 z86xGf=r9sHKRUqwBqe$I^p5~7I~``Cmk6kWp=wCQ&%8uz4P@fo8+;7AqgQggKgn75$HyN&@;DXpa8a`ESgZ@0tP)sjq(lK0`#d z)n#)aoC9K{B;rA)N+JX7D=W7Ty0FPV3Ne1ei6PJ?P`vC+CG$0wgTY<}1*{1<@bB{&xR z^5u4=71WlXm#-N16;M0pfeL;@yT7vu=vw@neP+DDaW@z6m?NNQg&K6!OOyGI;CY7Q zT%E07wFR_leMsj*L=JTaaDDHgcKGM=6NSmrmW&|GBh$ou)Hegh(d7Q-obbg{kt)Cm zDwXRCPXpc8(iJH7#+dXa^a6D?_z&aBPx>+`Kq7yzwGx%}!S)tGNg9Wy;(sn}0a%93 zbXsNVlFxv3BAliJoCF=0^WGHMD~pLSd#HRDY#$vtD5NHTp=F&o7j*&@E zvA{hQ#adl7q1G=i?41} zem~r+csOm3hMyhuxVbh7%d@R!PN?Kvnc(#QzC1;R$vz?TW$$^7m$P1hT#gwiHkzp@s(Tx6lPaSWVZ zI36b3A2X1J12bCnwnS4!k4olKG>P|};?li;QX}}QJb;)_z$)PlB$e15FQS3EUyFeT z?+~R*Kn&7q)fCSG>dTL(y#FWQ`qzPGEqZ6X$fBW^v>I20!EpLnz(CkgrRf0OKq_aV zYKi7`{|!(JSm3vSHE59mq?AY#&&9mNmzOhDSIbX073f%S?4@>%sMm4G3t#kbIMvqO&?53pqX&v<&??HtJ{rwe8?8V~>p<*2|uGEgq2(k*|B62Ok4 zbLq?Q!k&B5lmM)`AJjq$rpAJL3(#tN2-4>;q8h7t9Mk@uy1pt6?pOTLANl z1ClQ!zN8Q-IR64{L%`Zc>@){(Vv`*KE|eB%S5zvV4CTKHQF4evCuEn(Ng#b-MahtL zsGm)&r5R0dwjQqjZoeu2f<&{y!TdaJaM;UK=;P(xCh`Bp-dhG$*}l=E(hVDs?hsIEl#otA5JaT}q)S4jyOm9+l!T&~NSAbn zAP6YkEl7)iAfD?%fA9OAnRC9L4`==}dmKgf-cQ`m73*4S-E`@=TgzR7gMAN6OT17f zBbtRpR;;461K%Vz90jqtR+J=tCn7S9l z{PDuWZc~i{Mk!)-%tgFF81JMu7MYFsil$>HoCh}E{7=haepn{+S`8XMpDN=AW&2ai z^-y_D2jRbGo5UqB^1{q8_Fu@xF#Hqe;u!Jsj2|=JY7~u=Ye50{Y0iA+ZsWgmbKtePxf!jF#n4mY@~^8TD9v;tL=Um>l%K97S;z6wK6 zb2*c+$QW@4k}yl@SA^f8Axe}>MUYhu7RJX=g27j#d@=Qt3Dp&MH>OMqGZM=n^sNLR z*hg2mK*d8pLC|&6Y@2F!V9I;-9lh@9$Q?yf_DMX<4#A0#(AoH$4kS;_4WgYOKxU~I=vYahO>fl!V`YoU*!@1dO3bWM5Y z!xaCxkn|2rTo!s|f5YtWMaR}$`@*lisgxS89j-1z>u0L0wAe>{ZJP0P#7%i2u*PQz znQ#vw{EHQ{mK9EGdVHfigE^atN$ruP7+WlP&=c_xqA+whfzJXN(#~PVk6&U z$Xtk)i{M;^MLVgngJiw}tzlSexq1>08x9e55-5ef=FL=Yi<~?OstsZoFl+hv;5FRy zaL&LpMuM6oiEMdh;WTJ*jaT=y@)0%^BJe7C&wYax!>T7s`aGSmW#N})>Rm?QkU|?y#Ef4oLJQywhOGb(KDrnYDarDeoqk05gnK}iCF=|Z zMI|xj<`r}KRffAY+Kau}0*F)q$b-ndDipx87aMnW0P6{epR5U7; z>5}WUf-^P2Ae4KDbc#SgczQpZoa|?pK>g>p@^btz?8iAf$aVB{wcw)b6f_(-ZVJ5X z(DcxBwso7J)rNzx@a7?;Ccvypdu6CV02uXo!^^zO>kn-xh%7B?o2bs~szS0U8;7nk z7cQpI?aD!rgrD1+3aIBVkKV@TTV7NCLimUI$T^*Is=Djf?2H=?{^$$W(naLx$*<2X~+D03qN_1FunKVa;X}5xo~q6Qw;( zQ{i;k+Cvsmhg2h|_rQJMsl8Gr)HPka(bb8(pr&iVw3A%f` zMdbaotLYcy@Bfvz1tO&}cB=!dsvvTn+clM+-{`1==8zZRKENeFipn`Y;`?aTvmg~J zJKCR%MPw;zF!29u5$OdmQg{l2Yd~|#~-v2_{Kh+yBplgtdt!S<@ zl)K-X>DQN#*0(~9xR?b{xs67XY@ze*TjF|>&L+Z8Q2Xt@p|DP6+V+tgWunZBq`lTa zRXAf(=X=iS<<-YrKtrf<521lt;gcg7+1WM+AgPM!CdN&^(&k`dNpEf`mhs$t;tLb{ zl`m0`(g^+>%zX+d!NvPFkr|meKGZxaI2Nq&H@>+Ee#NyA=xcRaYYC?uZH+Nl6enw> z%VrbECo6b$@pD6GU~YHk32rC zme`e=)*YXU%_-0_m<(c$5voCSep(40CslMfH+1u!5AzUqwJav^N;!!Bb`sYxzws}z zU=k<^1wlu1rzHcA*;6isu$YIM@78nmU#4qnN(%;czVG=D2XfVEycQnmVG}(Qv;8Ee zsBB*3thCyAUHOmV0)wIuIDVy^m1lx@I12p^-J@G}dL%b&JCuD6p+!stR&FOSMlRUR zG(Fe`)ak=$nf(I5)X4~}(97Y(8l@K=3+WRaQcd( zKD9+NJVHlf#l*$ve`hR!gwh2V3qSM)pY&(+tDS+XlyHtJph9INoBVDH5?^LH8-C;8 z&zT{hKs#XSi}ZV+FY53z!Y$x3_fynYuyqGbkVVupA74;(1IvjP$m-!yuq6N?H4m-c zMGfwgZW}EOEOnI;tfjStz%$q0+&mlIOVWoPo@5Yp_(VI=E4`QTF-P0wAt}>2Bny83 zHge<6Q2x7#Mg3*QIpMcAU$cY2I~g*3hu_|WO;x{3$Fo=|c7`aL@8j<~(y>duKhlR0 z{ng^kayJwvY>AkYQlM=yQ6WL0ccO@(8KJ=zXkm`Ew9xNV^{=WyVEe8!@QW$xF$=1! zg!r5)rkG?3(zx{NLWK9WssD8L#~ckpX-AMt{?|U9Ors;ZpKw?`fHkET(LhQmQVX4Y zk3H~Cg7Y)-jjOLfa#Z4V8pkMez%AAX(5oAJVfoe&~+jqfvUe)gNt9T=i3AgJd)>oJE zAtF((9zm6Bo@-{n{dciXBUI50Dnz1f#1(`N+pSN*I7AmT7C>H?t5NvQ;Yz1;GnR28Atj|cV`QIjcq6RPHk(^#2(ihJx!^%Tw8N2pBH zOT?bULBe@H4mjkMk^53CCTX-(n8Zk z0PfY%kFmSXzjlYz7rWES%$3N3*)-0y-E5i;uYs|@+f*H3C|bD0w5&jlPJvcaU*_~MFno&YbxZCVkdg4&S-JAuXbHD?`yb|yKC;yJ3~E{ zDB2krKIj({n#0KK>1)z~{xjtC6A+3!h$Qs6WoN=U*S&8e&+L}l_GC8B^R+$j{8PW9 z1fWA@7GSqVx>e$!ftOlu^+WZ9A^!yPfAg5~)8oKJig4*@eH)igPvonnK#v~OPLb#a zWSd2e)0e!zzl!Pf-R|VRh#6lHE}qD(HQ1p9i$PD|EBF?igTf;LmxNw5-{^g54`?M` zx0eDi2K+wi1NeXQ%sm}z$ald%6nOs@BK#DAB@Oi6Z^u>QzrJXM#Ss*TY8X~?HJKyA zpH%JUx?CZaOdY&gvzlL>k?-hQkPWE>Rn_nK3Jdlu>^2Sa_-s7s=V!9um$|2D(ZXUf zp9*v>)Ef3txJFE8)Q@`7Nm&A@F42mIL__>$p=#p#S9lnXS;T zUDKe_^Zxg7!HpzjIpze)9X(rqb_(+@tj_5V6ymPSpB_1$D)=J~L%G5?$zT3JsK&ZY z@O^De55oFOZI5pgPUDr@G}i+V;y=>|Xxs^Y0Iql@RcI0#T_7h%cV|lKMM56EW|KO1 zYUzZ)2jf&_0H%twxHldGMr8_cbro?Ra6LH?N}x;aOmkQlpd?67 zr!BXV@QDZ-!wchq^fh#myTWm7F=^@V%C?1UI^{@*Tf!)uF&lcV~XcF+tt&#oPUqN99q7F&xedM8x$;J^_ zISEbwz)xWPZO{%i3iX(=eTORFE@}&=!vvs>?qtt7Tuz?ce0?1$o!;6ilaGuF^C3 zu8o$RlYv2x2S8AMQEgNDRY^z1UL|$r8Pgbl9^zr7z$x@|br2^oGQh+qvwOBToP1<8 zSgBOX#E^-{#$5YsW4NZS9F-gJSh%xKH(#HPHVlBu@AJb_<>AP^&c}yRriOjkOB>0N zlbDnq@bHY%ie;jL!h@G1wDOZdi7zkZ@=skN&LtwK+5I1(9%tMsSP%q(t$Ba)#h*We z;3g19QMw>wuRujpxOP>WzEBmoRRcID0ne}N`nUm7n$NYzdTfs}MH#LDsW*!S&_}Mr zDTe5@<6{z$#|X|45cMG9VfYws z=v_{GicLJkR|UOTG{4dmSdbXMn$)`mt-?gBD+|$tR+f|jh*J3QzY>JR`G92=j>ylh z6afJ^&`82d8s$~s&0z_Gvg>fp2)u{jA8@E#1vT3dJwNV_mcDicnf>aPe{;f3kl9 zpj(?d>cGm8+Xu-h@o$?BNhIEzW5SrVA7>HdMg_rytD$vUZ49G?u7~V0l2R^ClFsvJ z6(t3F?6XQw3odxjxi&tt_k(V$3im8^WyQl+>X3HHLl2Pd+ijvB2$cq!SyE^9>rb=0 zK+!Quimn5Ryw0>xC@Vz&Vu1RUqzlSrJax%&PqUL7j{sDAW2VBUm-(@BN?y9CW4eoP z7hgpO@Y#8kBS5|%JnArb>49MFy_sMY_a>o-_?0AmS(_g~{*oAFFK1hB+o#Y#HnD-q zIay!`8EUdO_8mm%N=$6nT#a(+%}dS91Q-LVBEZuay*bg$g%Yc-#ra0k8sBB+hf5pK zuAckhn>(UFTL6?5fF;kH`0eWO#yE4l#-k5rt!1bD;e*6%Ul%h>}4TV5?wvCP%p^b?i4d}#xuf8}D3U5cCX&R=p{ zxw)I_|Gn?BOxKuFD9zgE*NLT|WT)AMIU%X?iwDho1ffEJP7u$9wZ8x$(1;Eb61p?j zyN0&}1&rn;994aAz)VkZi)^HWAhQp4T(;eU zli1+H=9CJm6qQOHba3zr-3?u$WY$L4Lx{a59V2Q2J|Shl(8Jg>Xyq+4$I;Swgq`Yj z(P8)#bsrxW|H$rjHr^R!zpYl9R&PnRQnn_x;65tmASKX1fvF8_M5Z) zf2z@6uzfPAhOS3SgRnkHk}S=;&HI{Y+0PRNCbCQ40k0~pK6<~DHL!j!%0sF2OcdP% zywelCD91wRXYM>^#brw{@mah0lDQg-jJu|I)70FEtES|b*mxJg4>Wy)LHbsC{}SgZ zk;y)4BZk`=u2U9yD=<1c1Cy{@XR0>2uckk5CS_G(@m^-zEnEk{PiB7_i)mKv zquHA=z95p9>%BX?l&XNhpW0U-_g`U=x(oVC)){Oio-kh}Ofbpj_ zAg(r$N(auwI($jhnS}AE9gr6IB|}~FQo_sl%=C}(>>WodC-jeP5JlZzDP;_F;LTgF zi$20Zbf%vg3<%Y4NsrZ+rNEsDUzWrd3r8hEIvbFJ%$8XH``8fG&S=I|l*Kjpr6oNB z288N^d!213+3&xmVw|CJqowo{xHF7q=-<(sae|&)k*Rdc_`lJq^Jo^QITwqo>UkH# z?j@np#9^#BWbw}BHPzM$>d=G;%2TCyL=)j`iSfr>V);2^lt;}ruaDx(=wnetCUZEw zFL2`pS;`q`3uu%`KoE0{cAWR|iP9L_I0BB}zMjd*U17xJj(&Na*{ek&rpTg+Cw&;5 z9(pf|)`Vc=%xnsWk_B{vxuC(uEDFDtIQekhS*b?;XeX3^d)=7k4~1L;8E(#Ro*stz zWRRs+gJhniFNuon<-4(G%tJViII5cc=oDnGoBm{PMgryhl|ZvIiK~fb)$rx)$$=FO zEz#`Bl^#;}#ZD1wh>;*8h)_u+4DE4u{D6P_MPTKG5nlpg7Q)9$nBi>GTb`DP&mQh( zZp!WcaIW>ApUV2m?DeE&p?R04GNJQ0klQ=lUFC+}JPtS`U7*s&;-fD11(}eCwRg3^ zY-AXcQcfrbz{P=rTEt(aTOQL)+4LcSqqxrx2V<8lz*B{x;H6#L0A)}NK`4qz(mf9l zylb=PDW3(4lL`)^5{>etEx8&^lfQ%eh0Zdf`u>qr(yC#9x4BX&%9uEr&$#L)y_gd( z34fkILaBma(yR`E+;eb+y!6-$ln6qju?F2} zsT+`2KZcdj&7HS6AK2~!BI$FZ4)o=fZ6spjtRUk}fyk-Hdb+4koSG_Z#v4?wk0{Pu zIZ0$tMJGbDbLaN+Ux1Jbn>As-zVxc*WJu$snz{__oj=>ZbLhl~jI&cduI-uh!YLDh3|X7W|(rndweT;D)2 z4bt$c4&cYT*<~|Jd#l3fLl1bi*u0fc!gV>0@CNTq(t>kUZ`ovKNLr=!Lx!kksW9n> zn#`Aw+#&7rW0?2SGoR5?yPo@Xz6aO>9@0H(C3F9;cllIFdXXK<8M=10D%Y>(TVObsSaY=9b-mb9?JT$xTmY{al`Ig5eZ3Dw zgcZQ;&anoizA)Uq4s>p+7F0|j{lz=W=8LFC1pNgLP8WF?>IE(CCP3(~Z9ii!el*-1 z{VrEg=ed7{s8j~ARD=s$sTw%3aHI|am*W)KH1xhNJsy-X6;+B=y@wqBdQt(c9Cag< z4|pUGo)!qF&(w=~()86%g0c%C6y*sen)n~&BCrz-RL{Yn|=(i2WE#(Y%W)-*ymWBo^0Q3HFmMxu+Ar$YEx64GRu0L)3dVJoII-Redc_(zb9J!TveVqd zO+B6t3y(~d)fpd@I%X;C$`Sfv4~i0?exukBzz(n)f7BqdVl2RQ_(apy%_m#Y;4{K$ z*>#Jw7&|rLdjrn65ezxad2#VKLea1)*Bvzn_VmMSeY&7Yv#P)-B57XmCoZO4{UnnVEV zO#)t_#AnIB3dao^m?)$BpGQlV$n5J`N1r5DI>>Sya}wM9@U~#p>E$Ae;(L9wtb-kI z;0LO#Kk@GoJX`iztU+wtJn>B*%KUW(yASW9Y>&+av@roA2xfdn2nwK1JhEu|LbAz# z{fKk+Wpg3_E8}30Z_D(%iA@wu{NAhoO)y*g7jmIMqVE>50oDn@GH_P_0SkQg)*nHA zYirLc1?%@b$ih;@COs8_CG^58CN2M7Dv5Tu@>G$yWRrEiNqrWjZxZR$$$@TRLtpy8 zPvZcAr9`G__F+h?02U+~TAxi(e8~EQ5-IiOl!}$Mx37QwqE`C?aOYEPwa>nr@Hb9Z> z4gcp7{srY!S)=d~{SGpMR^hY0fs7Ye@4=M|DlylD+kA>Z3fRmENbp1qX>jxvzc~Ov z{^ciP6gkWtk|Yeyg$So?5EldO^9ncuad+zVqub|B28|H)&jcJFC5V6k2gV2BG9lSF zbXGejw49Dg8a8gNs`uW#a{y0@73kAJ!ac5J5<-RCf4L{v*YVQh50E>|dJPlN;#$`N z>E=0D-e?vEnCz+%-WYP!#t$EF6I1;6-}x?LsEJ8F8M24JU6TA|m69X3ko z?VfAjhw-oX0>QK3kVg;9_H3qHD-QW=r2I4|VQ}RQ)Wf{?wvtyK4%jc4r`*RJv%nSa z=AQbaa|;LXrc8Gq6BmF`g>2~>UoL#&ub7#^Oj|9V$1X}$08h7Bl!YnHhW|gZr@4&X*V@jtW!?6`H&duu8k~vUH zgn7M#*4IIN9Xc_m4DkO`%z}ChA;!I6&|aGNfr<7P#eJBC*^L72y~X4`n9A76xTe&@ z90)N)eaZi=Va_%*wPB7h_6d1ovE2Yy`#`u?_c~ zfRyuAEsT`OIXj{(_Wrv{kEqoPvqodCpdfF*v1-f;Gxz@2H@L5g=9o+yi?SQU3X!_P zG6}Yq0rz8|&Rj;& z2j$JNW@=b3A!2hy*O?~P!Kgj0y)DSN%Zr+`2pr&}rjSfg$f*Uhbu z+g%K@;3lB2W7gFZJUfkh0-;o5XZfs4G-6X81{XHYGrC$ef5IVAOsOy&Pb*44$EeM` zn1vl;;Yn?#kBJ{lIa*9Cs#nqFK{AD=TYRS*bFLp}ff0ybrvSew=Hho{7&m?D$`r9MVv z$36@a&d*KNxX>+#Vb-*9Yw9)6JCx@7<@grY*lcCNqigL)#l*K%#~7cleb2&l!?&JK z`R!#Uo!9GmuY^bz>U|HktFRN&x}UY{6Ah#U;Yq#POf_GW+HW3jVw(M9G115shj}Ax z{L%4NJIlJ_FZSs8hCcz19E0~_Xs3v^b%kbVP)ffKW=!n`DG!wHazgC+SfFgR>GgC| z)6_UrlvVTom>(j!*AbzLzUp~<=H0Gn6q&MQVfPIq z{HTQJOOaEF;TI{#O4Gd zFDdVAJ!)2>m85Ian?RKkkeAi16xumqgoj`AGzi&`QeSQ&iqKQ|7<1zR9<{wi4r$5! z4TZO6{wLuZ4NOH0GjlF@&Y!ae63A}$8~d!4CBSUxd8Ose+gOw_g@RJn)ni-f+62L3 zz>bWwMIJ1Aw(}7DRS7Xu=F&7G+?7W!A`ENKzLPwige|_so#bjD_MhtSEEHxuew#Ip z{}yJDP$Rmp^;=E+TbH47p&)$ry>X}f553{<$MO8hyZwnZ4X26C)!dX*L^+b%#BYBJO}Xj6f+uloyKd;XY#S-5GX9h`khwdF5x7koiP5hLD;x7IY=+1_aEabEYWD z8lv7A1ihN@F*}x<%v$Myktrfx|>d=7;oW;v5?>qVVTj6%xb^-*Yy?vvs*`ir%kOz(Z*vG1%lYPGrW6zdu zuHoCtTpwP4jMXTZn^k{ubL*Y=_7}z=15o54yCpaPp~-#HYsmmcIp09|z!TSW3|Vau zu(--?z}ZT{4Y@H!Vv*JmTzYCjOYs8+*Wz3HK+zeqdzopzDB?6O?z8+f>>Jd(=(HEC zn#9af!^nujez@@I$21XcqlYlfFoaTqX-t354^YK-phIv6eV~=vb>K*i!PPMy2PE~K zW#|6X07MPTFh*x|pC{fqw zmEC|`mu-Ld{LNRWcy^Dlnr?PM;G8UtIt5%57;i!L!jidD?O0eFA(eLHF-zL*_STd@ zC2I<4pU`n|9CF-U9r6g!|KLe^rqBWJ8@yal3va(V!isy~23Z2*QOnHSwaQ24<)D+DNW z`cPgQOPRZXD9+v71#12xNQzba@Xof|zFW%Ap0*|ozjv|GbDI1F=*HS{C5@Y5k@*Ar zd1RdI11A}cyZg4F&e>~=I+pcWBR8K_0kE8mfiYD+=2T5L7604tjyfD=GYI zA5cdXAUdtSbfXQ3s|H}1nFlR}CQo9F%05^Mm4KD`1ei#eS4?`pZKMdg!Wx51uYF~l z{4!&F>rX@`e(7nQ^THLI8L6+&6Qe48x5dE&$Cw$R$i7_yr>vzIue9j{uk*rgzThU6 zsZ$C%L-$rHJ=z8TxoEK*;R863aWe8j{47c<71VjSm-E?jFnVoX%jjK$E9fGS^fJRa zA~Gj>%qqn=sx*GBxc(vE)YVbmQA2FrHOBjWqb@ytPMZpvhMkc@Ydk^Bs8KXfA;izW zzH4I}U`y5(%W@a$_flj3g9U!SO%{QXBi(zyf%#K)qx3bD^8w;?j^t}+aj{oAra%X| z+B0yw&zfE1^$y^N_B6LW%_={>?1tL<`A68Ds>}s{0oKCZp?p!mZ+&IgPP1TcJvNIV zOJsk0)}#lb3H*3|mE3^Q${%P_w)DNGw%83~_O#WHWUpyv*d5bQNrFEw|xn9}KBkR_6_S2_p zTRp|>^UVzSSRN#jLj$J2VPiU?sR$>J4-v|#xLNBiSwuton*c&x%KDrpr$vp4v{TWqi><_(jcq$%n{vd08M|=|B!_k9wg=%q zUICv$du(BPjr=7HFr2m3lX;7vJ9+=hcI_YtkJ+~D&KGBaq4}$$5Yk7%n&=y#USgwU zb%*GBASYM3F9s9w-)o8$zpZIEz>bxyuf)clEawj)i;&3fb}8AVW!7fz4|h?Rd+JC74~m}b@Jgz-66tN;eOB~BM-?%5gEYPy@~pI z&j70b+7Unw{1Stb^l19>>SW=T_6&)3Zmd+kOaDWfz#ND^fB)P(qvg&0nQi zWgFDk2vJfDXKkJJ1jg(o`!jXQ z*^m3;C_P2djx4uf1nmR+BsaOuj|2c0Zr$|)*W{asd+iay4-Dktt_fk=7A&W229>`7 z&0v0Jx}aHb$&Wq}1w)#5#y^IvDx%$)1o~VR2Q=A5{hnNHq+cYHLTh24 z$G~bt#C)TI0q>@^ulvJ}300y|qEvlfeL6ka7Ar8=B%t^4_ziKU%QgkSR8L=>;7Io- zq@ZuTngurz<#EG;)EadYB^$#?zVq#;`{Z$FMAWh*a^~K=&387g)^FJt{tauQ=X#i+ zqN~|~#XVGXJXuXVE|yG2=#v6g>Ps7AAp)!uJ+5Mhav@hLh$oni`tFt44YXy)yjNDj z#Uah1oKZ~?u8YffM%pOnL7YRJt@(lW`-0Lqc@2Kc=!eHrIm38WMz-GxDsivfZV{_X z^uHFX#qd722j563olO5;5h1JlNP7Z3-{uQuKI|@)gHCSY`3s&z>I#A^{-!m%9Q2gD zIQX|$f)@r1l{hxuT9BiH#z9vR#P9g~ax97C7|bE>5EJLrHc&m>aLaR-7&YFy&pH3& z(&#(W!-hJVM)Y--V8^=H9mCtn9>ZpMd&RMy#H1SAj?s62+f|U~#bankHxT2FO5S$& zo33~u+&Y&3bG7uU2_{9F+P1@i^LE?H6!TeXYDr&w8opceRrM0FZt=KW-11a;)Y16< z>7g)xyTo?-beKL~XY4v@)#%r{d;qruBY6+ex2uZC(~kMlv1^n))<(Y&TvT%2`^4jl zOT$L*hovqigGoJgW3rn(qA~xFSB#L!v{tE*0{Z0nssuBIkT9z_C+8z2uYsQY_*R=F z7J|ttV7D%9&#?>*7{|2I5(!gZ#TK(7jZkdyDhM2-Sk zAqNTE6P8jQ{TxmAT{i0q`qsnMF^zu;w%iJQqz|j}i77#b*J9ebPsFErWkzikpJe0e zDDuD`4>m!cU+w5V#Aa=$9voDDs*8P%-x1FdXU*IRYf!y~h5F-@TvtfsuDZ9XX6?xA zUZTq5*K|rTu9fBzFF$8m5)0+`89mltyW`ZA_x-h&96PKzHq3A_%NLkUw}z_R?DrnD zj?l|LKy7V7moI`FuT+wLuo4HBJm_Z(nXluQe>{yRj8F~Dp>c9$ncHX|^A(I^c{b6R zxZ|2A*l=e5;Z-qdUELlLoJ5+Z<1{l7Ur4f5Gd{m&Y2TOm8K2R(?R+<8CP?nZI{Ny` z)sd}VPy4vUtq_-9j}_A-s$MG#+#LA@A2zydNo8f z-onoN6g51r94O|SS5?R=C`eF&ooe=UHz@xY**EII)6A;1_YEMd>!tN!T3tfDK{QxL z%3t%_Q*I!S{BP`i|C#mX!QVoaVRDa^CDrzX90}HpOWo5Zxmavd3PbxfXNY3ZQEp5E zO|H#5+l(3XYvArcTJhFM{mG$JmK>9B!%NB#oHe}0p;%E0J(lv)lG3%(ca1*?Q(o8# zjFwL6`BVNTvV&k{ewG^^3RsN+6=J;x)jIWB>9Du?-_T2F>07nKUa zyYik{B)XAuzDl@k##Ya2943&lstKC#?RV#6E> z*KkXDd2kt9ou0B9DS-aIK>8DcAc=2Mo#Ryuk0$3cBcFLP5z z?e7oZ#}Vo9Gi_On36p<+_J4lw|L@mD?)8Pd8_DNB8kdgyrwKNgYb9CRAC)j5|`()`1&+Ssc8&@F<`_A`odEXeWR!$t-xww(Q%melkT6ROBU zrjRp*ZkC`U*VDQWEOsgDXLrBtcJJ)f%;=@c%cMO}Gu)&2=VXJ-73A1UQl)HlZv6WY z5GKNdVb(ub`{&s~{(J%k;T^nOWVgPY{`b92oN*Lb0q|v%G>Op+Vz(9-KbD&{Vm$bPK-{u-G$s8C#-1 zfPPXGbv-u^+S$c|v-Q@Ak>_`RZ)I?2%4QcToa6ZS4xxesgF?f*Jtn={d&K96t7jfE zWT4-I`HC@U`eqpHF8_NnN>BDts;OFw&{E!kO1T)M3B!tH(w0ycmm_3emV`I2{})3b zn8>BdHSwl#XL;sK01x~jnI$Oi*5JLxAh{2_`<&^&$1tnNYL?uMMJnC^Gb{P(Z^d1C zuo(rw8LgzZvO8`o_rz@%$k1^LRt(Mz%WR)9F{U%V)zV-TXy?zgP98d zI;cm=q0k(Ixts;>t2&uC`YVA?Ftn8GvFIZ*}~A zNo0F1s@VuIkBQnC=^y$jQwTq4*-hXT1J_L)80~!dG3j%647M|LfQG4D;zEKUE1!I{0_KIqW5Q&-U1uE z7OG-RL1*cvEf~HRK?gNTDlv~@5Wn}t%F=Qe%2$h&+upC4yyoNie}Nd(3>mER!FD0x zTA@7bDTlf0s`?%h5q@B;WX2CCUVURJYDM>-rw|o%lQmHJ{>x1F;$~9GZlpZojRQGS zA*H5xp3zKR8q)KXxC;-Dgn{wM1W5kv^<}?)`32WA!_%nTA+7ot3SEx_ z4xY{m&^Lh;MO_lcrW3yk9bTxbrecoYZ)aknyeTQKs}Uyu8@Vj3Y}lUne*<}9+_(Na z?N*3hj#7B&{8cVErIMZ8myv?ifsYnpoa9E$=&uSM8LswVn&h7HeEvNU$|URb%T9MA z8;fzjNmb^Wz`{y{9$$+=*1yK(ck?b;kgAyI!~|Twav*~De0;tGAM`$d3wAmQEfQ2_ z*VV>efxc&%_c6d)<(clIcKjpR7eH1HyGi>Rwkuf>r*dAmECBOp6v` z0wU6aQ{4yG;M^)CmdI(NQZL#k1F!1-dRPq0GtkcTOu{aaq10Azs6W6!W*EzbrU9jpf_A{P3A%EQ_)dTL{%KJoJDr8kaVbG$sHKJdt1eEA1x~ z@&1!9`Nm^zYto0`N)Y(*z0PNzAsiZ`>3j=QEM3+X?%+y0+i9%5_MeJRB=5ya|G0O* zpzi0J$V0dSM(@MH!Fb8o$p5!8X$x9(8MBKQOOS_Ufs3TxI0@mZYF@jX%)#CEJhlyG zBDs!5hT938_x>*jaBW?;h&jVWcUgcavMY7Dg*imkRLlR~k^vib=a5GQI~oz<4Y5*L zht3?&w=)jiYwX9R%u<_QV(s7@ElYX|P!+#cRn%D)H`ajuZ10uV`7-)VlJq#H)RzNSs@=+t_pHr8+!j_Jw-5{?p>h#&i&FI9J;*)boh^% zPeM3eL<@XKo_nD11nhnuX43`-;=ZRJ^H%8QCfItBjs~1fDMf=@v7iktaPx*P9KoqpOHm{V9@m88k>@7HCf;PubniH=NSz!OL2_NdBPEdrO170Kc{4lws8!SaX^_fCo7O zJgZ5(Q;c!L7tc@H3zwLa4fv&>q~ChtzGf{gniQJGk?+JjuRrdYT-eF4RGlnr`c?7y z&u#61tTjYM8a1>J5qN_40-&sW?jhF2)h6fw>coH``AeAw8k)wPtVbz<)+)IgRaQsp_YZ`@e^yFuvY_izC|@`L^83 zfZ|5R@_VJj{(~fuCB7npfu-tZLMwGc>sRFSe2D@yhc!VZ6Gp|Mkv0K6@UwDEb2QwbF56p4EV^O+g#vaRoC$|6dr@WN@sD5kox}2^v$27IF z2Q#%s)eH;r*RnBfaYtYI zmu?@a^j-a?m?msh4*jo-Ly=_@tI%v%kf?q5!gF`jzRHO_#8pS+BOyi? zg|14TPidlSU%x*EN*l36i|TywwVYdUnsO#mPU)MV2P=kl_y(5>rZv-+%%T3r<+df6 zd96K3tg}wFySg@CXK(e~$XCMUO>k9nk-b}M@d<~WvYfbuHSS^QK3jiTr0fi&viP?- zN)~9Ufs%(r800Kk4G9vkuS9$NcVFhhDsv{7kc3Qp7n5PZMQh7);ok*gr%08`rSB4r z4`yJ`1NY-PAX2#a*X(n-lO5=omvI&7*FWydSGH^;+-!+G{Z=g73OB<{S~FSDd0v%! z*^7KrfN?9CL>EC)r@}q+4}hpTz8AW2?b1Vvrvk%+rmgImG52pqi%tIq%KEh-x8M-r zm}#8amEP|Xk>4f+E=$J0w@|e_7PTvP8_!Lae(TzE45<>jF2eOWp%X7tC7g`GWH^}E?=};~ov89* zayIhUC#XPc&?(ttc?`Ks4m^(emLT^_;+1T=-U8=xGA}P0s$)Y-gZ;;PwJzRK%lPOMTizOTp=Dq{rW zNVBzWQ+Lh*c7HFVws2WkoG8F@yJ=MGc8$gh@8KVvYI0X;b7{53;OEi1FVz2p*0jl@HE$P%lp-e3G z%d^=Lsx35Bzs+UUG1kvzH0$8>g~#&dGvT*^uzA?yY2SAl);7eobID18=8{vWf0~*R z&N7!xQvmOR;6ke0UBS8`P>pL5#T)cvb*?4z6RL>d{?;$a&^9QVG;;AF%cc2GlgJuq zf6Aqk0_BaZJF*>gVEs# zi6rtb*RLxwFX@msqg~;#kq?tH6c+l8d%0BmT6c|HTMpgG)oY)qw2%L7&ExbiT?06n z={7?iHva7RtU;X5Zu*5ZM&vzc94BmjU!*jVi7c<8$1rk|f-GsLXvf>eOB$`XLO6bq z=MXno#8JyUbuF-{RNsxWi64P&CzkP4BIe!m;RY( zNLliquWpS!r3-^4!-qDMrldNH1Xc^rY=bbg5L1es38kC3cnxvPtGU@))MEJc-JCG+ z$m%wo#@eYAe#ukUNSu5=FG%kA7^m!GNyl?J4a_K)b1u$L8-gfq9^mLKD=nPQ?oo=r zDf9Q+;zqGI%ZPCoH(D;nH7HQ8wT_qj9pcIel^u8b3`sVB9)LcO#D&v1U!db5AR`YqB zN>bs#$ndogtoNH`Q44p&3pFZ&biAn3_W4nQI6E}N;@xe*v>Gq{{_TlX#V~oluB5nK zt*BafAp^e(Q!JMqGnMKzZ0if>A}Ie|7SKEQvWtv9FT{y<{8VA#puWsi_EzWIvfF92kcJVuVgAk=ZdS^TDLHXV4#pL%A16NZ*u^g>!o!)+Yd~EnL!q0ld zrtbWY<|Xyyds{8>v4(ZLrS(n|-eOkr`9!%y(AV`R%gw*xwlmb$(Lm0XFi!3YT4QQj zWXZs}KHipMvKHyURlQ?hm>8-f@$R?xd4b{M+NR;$g1vlVSAJFG~1c zalQ5LjdoUhGHfX9(y8A0U)*Yzl{KCYkMj)qKPQJl-obR|(YZHvw!xRo4r1HY=D)7K2gogE=r+wT6$_1>aA%-~&h3Cj3&4 ze?hp{YSVcoJ;l(rwewdkv)e;C%mhq@a&W@%)w2dBs5{aw%Ax1R!qU|QmhAY zgyTPs66qRQVaN=#^iiv`&5&)D^CD}#cZ-qT6H&Lqn7v=QeeG{W+BI%WK#`;+fv3=P zh-^FvObyu#Ef|!(gXyg%FNFd4k8xjUCO8hT0h5Oxz>*ySX2-HQf*%=HyT9{EL-xAL zR9zVDaE(8B-gW;9y4%yt#%bhEqD^_u=jcc-aF3A zwyXe$#u~sq_m{!ceeYeCMmyp;);c(1OaC6l=z(S3-h%C8iM!#^**~?aq&U7PBKRgn zL;WhWDbC5L*l8d(bP3xwwA(WPGPUGg< z_W;*;5pkOqG&w)t5XUV7^eq9^uh%atfwt0KZ|P;{78v4WP(Aw9;o6dGp?Y53`jQOm z7Z_nY*{5hQq0Dw|S%j8!Ne;XWrrdV!H;@^^qY@$Tgz?Tg-koXZR=H?|j{+vr$(uSL z)N&p?JKV1m5u3-t+hl90LMCE62g2=1eq(XUHBKrFUrevp;y^Qg2V|XmhZGb4_ZRPc z?m*bNV))?Du?(9vNn0fUf<}F8ol)Sd{uP8JAodF3Zm(B&PP11{bb7$nz)pqn_4|Om zx$orxAWK<>$9OMLk}9p|rB;RG72>0CnoAQ49-SFQm*+7}Qt&PlI610TIt3JA^rvt~{W*t-rjWnar?w0iW|_5Ixi(V!r#P z;s8`~3#X;yMp6cOR%vauMrx$+5=R(HID0S3Ge`04P0YzKt6wyp@O`;W@A-A^XH-;F zW%0ECxR~r-)T+vT&i;mMan`fpC%rih=209XfYAJAs>1<*X*09uG)6`5D}&INO+T=K z@{zVC7Y+SL-+uF@1w{arjfQ?|O0HP~_*-TXD?MnC&pvSQeUwWeNHs`JZL9YCaf%n8 zgp0`{*L3z*zY8ud#+RP!cz9isQvQOB4c`&4dM+RPAjA`C@??ZLRAPC>w9WtfQQ^{s zYaH8ms&0{Q5`S-4ipjr*14Hl{^08MRd+VsG)-G-o5s?rn0Rfeg zl2TAW8U-W`kWOiokOn0MrA3gG6lv)WX^?K|?rtfmZ!Ub*^WAa(xntZj7!J>N@6B4z zdScG`i%D=b3G)p4Cep-UfBYJ-L55iPNlh>t4bArq*~{=Jph#E$bgtmP)ZfZeY4B!-+D+=w zuW(H6xzG>3#1C2@-JQ9#D_d=^&-l1sOemzqggNhYiZR}I$xurae$o&wWP1KN<-=w$ zgtz=I8DNDLMzQ71l<~5>=$o{f!$zG=BWKLj#ZK-r01t|J2yKTq~i5Fl;5aH@=zU zvi(VqHXN1tau#>aaZ!wNEF5*jYHqxPEoj-3>ucMt`!^-%1NJEbK#2a$PnQJ!B00&g zo56q;4YzU{m=?5tzMH~n%Dhk_l2iI{4%s>>$@ntq!p^X*`nxRDH)vYROQ( z3Gna@5;Q}xQekG=Z~LGbNxM~he+Pq&shBpecKWccd8PMSy;3oI!ddy=3}ksV~~kX5h?Qp@;2}RWDmgDhH>4v~-tsMx3mqR0rpxPl3B`9=C&V zxGeL)dEY9wPPv$Kj>^is0++FLvA)2O$8F~yH#DYVRoEN#H|D_#MUj)szRkdDxsgWH z#+R3kMn^jr8S}Nb@4Ctr;#OUfu=2yV#WG5^`#>gJ6MGKr97dqUfGKdt3I!a0HJmB9 z8I!pBXL#zuP^2<;K>u@@P&^}*z9kT0Ns=AoAf1pWL=F&JNiDj*Tm_@Qk4Pl81LL~1 z?j+@Fa3}2t<-pXe=Z!qF!xO)dOf`eu9hpGWU)34kR!^72?*||uM)_=H<_9)~r>bc& zty@2u&ML z+|btE_cg=su#*z~Ou97jS?S2mNfP?x+meX}(LA^upQ$auZ@(ARQ^;{o(*e4fA5@A5Z*eZG-6Jx74My9|wFi118u4$(O?CaCr~0IIcg{W#3o;HvbxNq#z(~ezai( z_!cUiuIE;{Oa`a*ciV?^>Ns;LR5lDOXZ2M}_0#oCB@OQoKA}a5T;NNj%P2AF@g>g7 z1tb1~t9vS&{y@capwuz~?riCnL$6ZIa3)w5F;7+^*jrnhG~+}hnB+_Hf-iWr$dV?c z-K)b=QDD~-cz+}LPtY$^f+wEbp!>cBbVU7_^pWFqdXGrNQ$7CtJ91B}7;xdyVDPEX ze{@v*HAB|tG>iY8%U?ow<@0F)Uy>m>{d0nD0nTTwjC+OqX2#ovT(z{b|Bb-&s32F} zht_uQsKBvkbkzC8ao4ti%A+XQxtc!zKfy8e<7JF#W!)~8_cNPtDr6&_J$bC=wE$bp z0nT-12PONP!&MS=RS3#hzQoT4v)x`V0o3--bxjLFzGexwMLW;|MG1s5E70R0590c{ zmtRqN%JN+{@E?HC!U);^Z^`R4y14or zTqL{Q0LimHOw~wmj_&iUx$NqK-ExnoA7+&fi51KWkVkl zwzl1?(AHiXiFv{9wGsSlmTy10*$py42`#NbsaU>u#WtV0%alg0Y?PRS1XB!mv}6V7Ig5mdy{>QsTxkYq>wq z%(a?k9gKAQ!SHDr4l^6B5eVSYVNkyJ2O!`p;sQ_Nyfa{+3KGK=h`6kK>rRuVAIbQm z^<9@q-{Df2(nnbZMp^eN+W<&11m=S}fP))wlM=&GrdTos>vj++smmjHTrb3tp~PG@ z;M~>?##%;~7m&8ETl?=gB)>%Q`ACo7_&ZS0qX#eSmd-mWX156_b=pj>4Zg_C2hSZI zDWo5S8G^Ec6G8*{_l%bmFC6b2T<(JBPtv9j<7(cl-6w7-3X5O}0Ym}f zz#;Kc=9|Cx)YBiL|6e;ROU%+2iad!Syf0gc1AAXnh^otcPC%MEV$sMP-a?@sjnydO zqS1@+&eH|NG51$*>gysbxj2F+9(KBqAH#L4(SjokUWV^j#bWB$NFoBM>&{?KYNNgY z_}v#NDMjvkfi86)`AC0mL9)^h2(Tf9!FfR5=dSX}ClHibt&fcb!}I~`1j`j9l>|~U zO{o#7ZliQ_eX1m~f1N!eLMNbUd;q?~sXsu_)z|u9SM_1lBaV99YXZ7Jux(lh%;Q;u z%)p~w$Pf6#dK;f3z%6a7yA-DG@(;O3IGIWw!3&U?zI5Zd2*EwQ3UJza5wQF!MY?h5 zHwwa%FWC&6_MoW9XPO6RjTN*}`165$pd#1Q(b$WQi>3alr@u`alB5T zX6Q};`Mrm^Uevs@#)mkJe(SShMb@wUuf_K4b4R7a?Dr|&pnukBV^P}FR4-ZX{=PF< zY?Q}*>GVi@g84K}>DF@xY8rede3_Ra%i%lIVLI>4nUeW|46s`7ho5>u3lT(zMQnTNEVYr&`}wa#%FcP4U~|G1rjM{lRLc4ieXMMlnbdOi*8QS`4>q96>|6I_ z`jEX*hzdVe)$cKw^}Pl@fZzTRM$1OMd$$!PJEm1$CB^`f&eh_yY48~wF*qkEL-&IIDl73J{@#1`2m0S!2QKcloI9I)wS&CMfGs#A zL0VaAc1>=s-zD?G`54z7B=)3C$N2G8vAs2Wjwv4)tHrMOF8rD}?+3UjdFiS}%y+Ye zYu_n8GTQDvo5WpGGsoJiM>sq?peiWIFcb68d0VSZdMWGz`lg!`9|hVCHz$misD_50 z2n33xH!EGgUgXKJpepTpfS#(Cq+XtSk97Q?2E&@UpBcL``K6%hnHZ{ywCi%$H;N~7 zVe`7(SA+5vVFt;Vh?Hk1wt2^kvYq3giiFTd86E8x+|nVwtdVk4P=W44taPbskCck+ z-U3wCXET~D%-0w!hD>hRHz*-86ziqB+XNI@C3;*IBS}mbheWK*bY_@MSJ6}U2FBk` z#QCsVlows-t@dtrpYN=O4z7N|4wu7f(#~LrQsBk>B-3g%g=R9UpSsviE~_a7&QOQn zNCBqBY8O-$OI&p@#Pr_@Y2{4fooVLY8YOrIa-on_q*wQRZj2*Bb_;m0zQZ6A_7j#-s z5$usdo1}Gl;TdCc#`Y=S%n2izTbYKHo%Epid?mrO-hjZ22lsXRWspI6^6rM z_>vCjWN!Den)RdxN67W4UM)To@-QtC%%ieF>hyNai)|a+WES6plK2zZg=~FZp%0t) z8aW3-6NdH4FZN0w{0;x8KfFN;8^n&0q~og(CAP(^;ENw)&os_cKC6^NYa@)V!(U&y@tR4kO2I<(kq-{(@88u^O? zx2Gy0vLVvZ@o)abr9vurm7AV-2m=}mfSqF6p`be>USB+(b_RCx(ydP2x6Fr3r1EV| z!b(@Zv?{oW25F$J)>OtEe=as6_4RO`IdlHXI2@Y67kW#_-(U|PW#zDAUDfPZMzwzs zvEss>*aVL{J54vwqjG-J|J{4ceJSI7bnCCBdbB6XMYkQK4-Y%oKf0IP`ReY#Avu_k zWMxtR>-79-6mns`i__M4@DZ=#PdRw+9yNyY>nj1_-J1bQ)C(c+gD+AwVF^?T3wMSD z*R~UMUXDcda(&?08XGq4Eg0H?AieG6wkVCDiIj z8d3p`)FYR2PtTxH8Z#hVFuvq8rvuWq@a?@uvDZNiH*^wT}9@DV^T7Sa%z9Y zCHB(*dhClU1qzB_fP{$f)8E(xbYm084!Fy9TfXy?H90}6*#3XNY&t+l#j}O4S3MMY zRVaY45?_;Q4Xu`KZCQ3(l7H)my#+yd%2wJ_S-9`p*DKvyvFvFo`P?T%xolEi1xMQ- zN2}!(^KOOe>?kI=H?*YSKl{8VLw-MZ$G}vo_uQGg)8{?AA{VNXwf<#U?tM`B^ zwM3FR=(1RE)8i(WHr=is+Q}`a3n2{U9_b{VeFsvb^q}%m6kIRp1aC9T>{r z`u7?G54nUcr^2ux+|`U%6O}U&vE0c{@+FGbv?^|OaC>5GAn(W&iN2}4Zxvm1}!fn z8d&t(qfHRMTL_Zd-}5XoFezI^q`yCKg6bWDP4Lc!>7>CXu~SE)iKI5w5JzX0rGfAC zsvU&pHt;r&_JpZ6P98?3(iRp~la8$Hg6Do|p^STbPydX7IJawV8&5MFvJ|EBaXx9H zKEyVl0Um<(l$(w*P|@evPy(;F1JKf@gHMd|=Cl()JxRf=>Vue%XZOHPCMsQ{Y!I^* zl$)&$I$iM+JCMc!pbVT~AWXl?ZT_9qpdT=T7vK`{Qbi9c#0mhR ztRF)yvS?%k;pFl{kO?y6hq(v=0&HM)fk7q3J@SF-zA>;}SM88I8gkbGP(07SKZSE3 z-Ajl1F(>q~8-hSO9q^fn7v0wMS86*aPvG^~3AdWGnOxn@EOawahQEyNpKhyUu|9`L+6US!V$9~cW(AT)$KTnv`0XV+TU^%S2Gg)*}jI|l~@aBWjHb7#+rjr+>`x(Fe5VJjdt zvv_xjB*j@m!o}3EBo)DUn(A%Ok4coIKw>Z{LhbayN{`EQxIbaT)alAUcn7gBBG2N` zuEns$>7%(00MDjw2QKv%6iaWVg+lsjGY5SRjX`D{Kg4Ewa{O@>Gx<)U9>pgvvsJOD zGGRBXYzFj5DALnUcSs>tjGHgVArG|{(1)@|Olgku$#F&m8&{d=&)a}4wqN8PcTpSW z-~RZ^l?*Y2Ay3`_&91@5=P6t4C4!N!o-Xi+8m%pzU}1r$F;}0XWZ@iAJ#$!r`6!4` z$^9qx z1inf53G8-@vvx?vTpppsykIkD>0TGuepMg_i2dgFv2VZ0Tb4?9^HD#e<{(Rg1XMy- z?CckAWViGDCZX>z_S43JfAmDOaf0a@E7jsiRR-X=jT)0JoSo)%;L4~^I$&@Y8@k-8#Rd$3Dz zr^k4?Kep)tyEBNXBeSMo32>olpkoe)hT@f5LT$p*{BX<7wHp7V!c9za_i-#W$CLHL zv%W3rrh(N^YC5ea;PgmIeu;HT!ONH ztETBYu4Z3``U(Ws?-#KjDN@bhKdobVY%nWvT?C#BP|`{)t={S^B)r~2^aao#s#7^( z)Nxn6{t%1~pi08qvhlVwE$pId*9kV3w5E~+TiHp1fAdNRqp$Y4a(`i0QYLkCuSb{x zq+hEZ=<4eRS&UpYmI})Hpy{r>-$5>t_QJc_O`abXU6%*S7x{(X5L3eZst&thqPp<< z%PZ*zk*i}!(g#WIssll{hq%L?&rE!!u4KM6S}LT>8hggSej=R5iJiAfdr)OuhHT%~ z$qDNFrBRMM13OSAz57+(A>r{2e1>u~0?zgG?t4^d_d#Cm<}vu21k^y|iWJ8DO!cS@ zsv#>~MJKz-1p?0i*Qy&As%v+Bja0KbZeb)sex}f98jJ463l>sbuc5$QP23K}F)rh6 zb>Q$5u3On^6Ww=bS8}&QK(I*6>p%-LWEw?>CFaaE#BbJ)_4_Qdrw2$Y%yKU!DH=<) zBW%h)AkFGL$75Rdn}gek==n<`q&#K@={m)wA}pCC_7%E(`tjyzu4GpJ9B-~{_^r`? zhPr?aTma7)KXaXxBRegHY6xCDZ&VcpRR3(*6%_TFXwNvj05yOm7eQoW4>>fGqCH>E z2E~E=C6@V1pX{Ps`5>m7PBBnhIRIrq&rz0 z^9!3bXoRb4M__wChpd-%ff z)2tYvcI|+sgwG;)JjHA$YaSIKhYnv?W7qASVxxMw$UO4O+? z&ujS4hTuJNlKglXbwgmwZuQ+-dW^cu0QpO{jZYr-H|+y>VL6XDe5%!awUA({dpDP8 zoZVGEf)wlWrSGxSvm?5d{RraSmh1Ej47f&Y*`hZ)q^i^+gWk3?uiQ^#XD`AK1Z1G4 zb(aibAVkOMMbIUa^9wo)-(v|Edf>`I7KpEOj`61BxjZ5y%$$@&X->pdk_{89J0Fr( zlX|Z*u3pm|X8`AiXWey%n-Vo^z!_pzX(Vhv3@h?l->mbghUj7$O)HN;EJD4~=Q{ep z1W#3-NYT^rSNJ9cZ62Zz?YSyKxsO4~T-Fm_{7l(#4$$ug*~cY~Us_qm37;|75G*JTYp+=B z&=@cY-3-JfmeY3`AI0bw$8l$ygVIWF+M%0Z@|s_4b412k1D>*2)7u1MVe%^-p=ss4 zz|N|_Vkv!@^YKkWj(d>Z6rP8L^*gKv3aT+GWK`B$246&#W-z5_0OG5GLu(;1p}Gv$ zb-o6oykS$I0uRShUi8#KAyuC>E87m?T?-9H{D)N~JQF})3yC5lJLreT(MKG?Y~vwK z=ZGV~+`Phztlk^`q3;j zZW?+73@EWHGrkzFbX8vUQo8RT*2cOHnhWleA?Q8O13g=HF8?5A(^%HG@Z?mjLWEXD zSY7<@$5`LwgV0QQweAe+r3#s};K&DBv$sv(vWrEWnSia$X8+)fFpTkrGUe<0I)NlcjXe>*3htG19!4;Yd>5B}2cLvn6k^c`f(BG4A>YP~2e z>zCk!o0FTv>TM!R%yoC*gk5QBq}~uEon=wc2rBF=`#W&nE-1~ezl_|y$9GEf&=711 z%4;IJ3HSd!ZFuZ^{z8(tXPu|I@n60Fn-subB*ZBJ+o(>HVy@o<{?S`8e-j%yqO9#_ zQeJ@;iMuzE)!cB$wV_ADBFdHZ_x#BM;5wV^kX=twRZNU=PQ01(_G6Abx+LYsj)!S4 zezyF*G{{!4qJ<=FLrM6qjr<2L7olWBgPb)y)O&#pxk~YKsEgK(V{!x)hZ|CLU$;=A?12-@HRm_a_&GKjcO(m^jruDrT`JXW!GPOK`UjN+haZkW-GPC%t~4a9pR4I z=r=SaHmSk6UZm9n_~aW!vB#|Vr-V0@qzB@)MQgNq*0BK*>-zLicn7mRB=Pe!Q5!RdZ%hl6Lhg{>|WM^6+ojb z&G-qJVkhlYu+Sn*zoNBq{ZTDTNE;N(OTR4$)aMj~Z@q0R{BOE;o%fR|wKum9LkU7! zZcl5B%l~@OMtxwue0wNeo@OaaZVI&`#uQ=W+M#+%JCyhDEOgh8#6r}U5#i5TY*zyb zxB=)+Rk-c*Ix{9d8s$y>U!d6+sY}8_yEn?MDvov{mL@^Z^d$;v0;aduh0kA|_7w4v zqIqg0M^utL24LhI2RLNDP|SqjY0&G`N9upc6Qprr+q18UN$n)4oHM3!>o9D}BdoGB zm2+&hM29`x!OajmX6dypfFO}Ij?k|6zviV>!!wng&K8ydEtQD{rpYuA;m0! zRGk!uE>YgJT-6@RdiJqZ_1iEy4!OaI|ClB<$GPYTZzVp=AciN@QlVDsPN-V#C|DcQp!)% z|8_}^VZ@Tc!C>j$y;HZ1ZOrxiJ4hB)e9URwKYpXupHREo_GD7?9i{gL$mXr9Q=ud^ zA?IscS6wZ1cJ}Aih}#63QC^LI3<+r``g1L>H*1xyXH8azi`!7Qp;45(#t$y;=EMj1 zENwB}oc5Rq-bGa&x{n_=U3W>W6OC@JK~PqUfP(R+P91*CLt$!;sOZE+YM4eLXy6aK zbXe^WdRI`%QzTKnirwR}u4#FLP~jESAXWj2!v+PqApIXKPTC7(^H}YW<`5?#=0?_; zll30KNzhLygNX=x4G2C^IdDBf{gxi?BNo5~tLCu0`5VwMq0d(4uQd zX50_M-}lQ>nb8}C4_>>)j8Pcz)NnREb$EUw7iy&0RO6*H^S_{U^k1y2Yvrn$=arR` z?uWc}uZ@}#YR+@?3gIZ|Jc}3TZGxnzugC8U&4r6pwoKb8H68E*? zhfsP%q!Xc$vFKRNX2ht~MK<5+R>z{Yfe`MCe?@ zv5^LhiR4g*RaJW#$08iAPCC_;=SkQYs9cLnSy*^tJ8oxR?m%uEU|Nw=`#BoUGpRK`;d&}GU==j>W+jq zNcYlzlqn87n``9uiE}E(Sv_?Y=SAttru|`%PV2-?~W0-g|R=d1Y|lmfG{ z(e%}t!!QiRO{C7H9=eA(7(fZN@D`rGS3gh`*)L57?i-p9RN|5~Gkle_1`B5gRfiM` z$DdDZeP4+2Tx^m8ki;9uX-_U_JSv~OP)V$z_%dP17^_Ob_rbzZo8_s25GGC5#X~hr z){vb4X=A|fwep8X%O#geonZQ28Uv#SEp9N17*2gy#l54P6P#h<&e&Ur0J6$N;Rk`H zX3U>38i86`vQRP4U&%59>}<=gm;oNUD|MG^&Fh-0w$1~3;@r~jF*R`w4CV@rWIR^MxnenVcej$Y zqiU-grw=_JJ22qq>3sLi2Khz_^ZYBsNi5xVZ8Rg|S~IA9A50ur~G>S@Qt)p!Yh|Og1gR#(28cEE7oZ{ zzE4XS({>jJ zLbNA;+7CqcN&L&F#5n=BY1|QcdhI{2okifCbv*h1WZ8e-N1zsc?j$3t{Or4-y%bA= z$lO2E0Z9t_5ixK0_UL)yK!47(v6#Msml($R>2xt|)z8vP{~~9`G#|1$XIjqHX|Hn) zzjPxEWlpjJH@uSK_=1s#uYV^f!l=L@!MOHZFcd(r(8?~#;%NItzFK!L>E3bOiCw7V z*7uZyu2Ib>kONo|D2F_2Vk?6qZJNs`sLZK#&^n~p-cr27?Bx&+`DURyM5aUr(ToSekE^hQ zg?Q47o+OW3aUi3Mfk?!3H*6^ovIv-Qy^f>XS(F!&X&ESb98aiPFl2 zdN@ztZ?VD`rcGk~FDp99D3`(d_54rRLI#VKcto_iMJRMGDgkk4F;>*^r&1C!U zN#eQc(jl}Jb<#7od-OW?5*LyJ#ADymTNTuwZ z_>Yq4DKv}>oChw&swq1}yp<#o}7gYr+GqkY298BARVRa=X@}d4a!{LNPMgrP2KhJiJ_eK~2e}1Ol3*7?*c_cmfKiV%5 zN_9wH6PG^9|3i^PVlgnMi%Y*@G)Hb0M)R9xf4%?lSAMniK$dwf4mtk-r%Bs|`v1D? z$OgMGhl{RHN82rtlkeBJtgIgH`#bl4mx(C!Q9N}l`h5}cWJrwhTKh)kzpIKzf!8kC ziQkC(&ue67_H~IT2}_R#gbOUUew{eir+;4*aL1ZyI&_ZY{5y-NKU_nTy{DU@oJ+@I zn5k0M3n3tcr&at=i_MGnFEcnO9P50)_M?r{-QgAomstT0`CW>^cWvhj$!j)_9Yzb_ z+|IR{2q&h%lBZ@oC5tgOC)_)QiEkj>^zcO-r%ZbE{tOm!B{8vg!7Qr1?2_3mgo7bi z>H8YrXPH%q$mGihu7hXNt*7N9lKwVqP}V@9>(r45;RpTaGt-EN9LhlMn&XRR#q``J zDyA9WAuN~V;#wM<$0469xbv0xSk^LtSc;96X6kHD$EgGXA(e$8X+8+tCQ9!Zc2qv( zS-8FgxhpqoQWPSB(XgK(dJXMN5OOxtzG`H&? zy^mhq-}NZT$(24PD7i1S0-HNZ_*Pb1A9-*yrAbxwOG{>e#~QtUw;}otCLrY5N9|@jz34fyFk@fk~I~ z3km{G!o>D`&Q{wj#?N<|Ro!Q^BJbK}-x>X!*s5OC;+5nanx6Bi2Ww0=yxSvzfhFYY z{odi87hL_7X7z(c>N`1__lG&|X}@mp#9XzTPR&;+eWm9%0^IcF2u->HqX(;c%?fGO z%%RMhgapY%#zymY+k@Mj7Kc{6GoBs$9l2CD+2~Q7Cw=W#$yPnSqH32v8(Ca+h$0I% zY255lNS72<&DW2TXVC2Bbf4Xe%+)et@F4i^zDo-aFa7A6N?o$?!sA1Brr;-CVlrV1 zP2X>QB~KqKYHado!^H{4r;_F)UXuT8A)o$qRKZ<S%Vllp=kt))P-x|1}%K^NJO;ObSEoQwB;SpX7}v0)s!B%yifU z_h4UT!??QkO~;XoenIea`*;zfW%b7Kx3OPKH2%-rveF4}P{cT&Fdt^0l>1^jo}YcI z)OVrmQe~H!zkrrvy=iSx1&l=amDe54f}VoD;bfE90<}rTp6%#gmCB_Xaw$G!IX%s~J%gd>$w!~`7bSkZ{n8-j)#6i=(% z1G}A5X#=!P>IZ}BX9)kz?IO>q4iZQg;blL&qzal@OZySSYlCR}#3LxSBD9SNc0F%( z$k8~Rj~xym9yWnIG!H6CeZc!3&zKl>M-&~P2hmcJxw{M8=>bU6RseV?xPS3(ZT>{i znQXQE=kf+oqSxCa?{;6apT^K28D4yeT;)PQo|)Q zJBxlH3sDwe%GA=KLg$)98!$)WeNqC{h*QD`A__i>%hzFAabzOW2|yK)IYeE+AtI z>Z%|G2GKcU4UUk{nZ5T+wC98wz`dvuQ%+y+LgWQgI;kSR)OROaNvA z@4U5Qt0GIJX~;W4hPZ5?Qrh$?8{A!*1#XftG)1*pN-yNO<>dk_nsmaV<58GY1xGtW z?a{9m84=xf^T!ui?~$HdtdFQ6>N5p*=?C`;O*+^7hA+Xn<=?l@!u&)W0cu-g6^@*EWOK8VsnyPxx!JNl9-e|47X@ zQPxDG?K9BV=fQ8z#tpt407ogsq9UD zQgd}H6L#SY-e^b7WAN0!Osyge{L$FP{wdmE)CCO;oTr=4jE~ibo*P`I%SYJs*nf(q$s*!5RSS~=L+?@i$rP&{5BsN3TLJ$qm4 zXsNB)T#^L4!<^1J=5S~v;L6JnJ^)J{pWT*jJYbmGlDLYOu&C@S`go%g!}x^JKSa_T z)I`S>%`2s2iSErK+v7V53EIW$**3pZSF8|}5UE4j@J|>4p5r>}p+f(q6&&9j{S9;U%|hnf<%(v+i~iT{+FH{d zoJz&6Vla7=o>kTMWq_`HVsr0BK9TD(I5|Ahw;}2%KEdcvKc0V~`ObXUT0*h3bd7#u z>VX-zJT-^({ZqBvV>`5U@0Gc9YV@&vG z!~qyb1cWfd$w80*B^n}>8W5!Y|NMpf8VZ{roXJNNIq46_&0Y-}B`JTC(%OGz(2|`pjqmOFapno<&fQ-rd z^)Hxf*lXqx;T&FsP!_*Wl%&D$ugjtNz!p$!wIJIk3RuK0^X}!FS}wtCngUX4P8WZS z&q-%G?DvEOWUrVjBtI6IEYhAAja6$SWqkWDzP#>I$tfS`(SmUB5TyHB#t+(k+MA}E zW1Pl6gI;phU&&Lo(>$F&5GTXW;~z+xh>+@dC)^2<-F7VFBGNnsIFGMN?L@DKRK|yI zslS_P_O{k;yutNna9j@wAQ3eFksJVmur|-xIJWNXZOYx{^wf?owK^90U#>vBGUl z#UmsCmmBV5W{hbmU8uhI04gy3V;Vd29~seyJ)U%ql2DuEqCD34h7CtokPFy+q}hCiCvJ~PJ5y7 z3r6wUwr%dhp_sxE584M)f^E#%v(>!=@)qKWsbXTZOGD6)UZWD9#tWF&?k;e0F z>N!Rbx~;hrE{~TPtWUM^#Vt_5&7crsixwq4a}c`>1@8dk{uJ$ewY{k4`;p2QD#KtH zGI0z7MLdqSCl^bA zqloyBv0QTUv<5xK?zW5znhc8TWe4xe>QBy0sraEjNyhS`B*zayyF?_ysU*aJhlZ82 zb@yfc!p{QVm0=s6UB^J9HU8a=Z>ud)Ymt_#Ww{1J5TF<#urlHnU~TXXX0b6iRjkG> zBk|eAYkY&bDH)Z@+j_k#<_g)ho_m?UT9_89$BXP(H)!<49|}l7@2z zO+_*m$hxcb#ZIwH2^(Eqvadl8nqE>e`0fJfWZPFP0r@3mSy^pSQPBd3tmav7C?S2! zBMO1HoJi&iVZs0qXHOf2kmw+@%=8$6;SoVyFBot<>IWu^UUMj0A=0q|+8922buXd| z`ZKB7CP7QQQWe~D?>-p-?1*5tTqY;SN2VG_Muz+swKvQ`l6vOW*a7hOVoLT#S;3}{gDRuwQdm?xR(C5XB z;3wF*0uQhrvXw5g?-qNtciU4Y|Cpwflg~%|fM44bM{8a?=#uytq~_Y4)ZX8GqBf@y zEFbZ))S~wt3Mva-tdOiG+UN@a$}@&Kp%SN#GYZI9I!L@$v1bs~?46j@TfXV$!?_0j zND7_7XgpELv;#!d06K1PER`t{FB(^V*X%#tk)ZBi5 zjsN+~%*+npI)fl$9VvR#_URKD3Bw}xGUNz3si~>SAIDzZe$t#AB3c#qTtohK9EwuD z-%z2Mt?<~d?Irb&Ls;V_jynpeLz9g&Fw&;ZL>~|X*gGqk;C_9s4yyp^d8Zh$1*D*& zBl(q#ohMU^Ir5Jmj~67UE~cnmM;k10dCo#~{guQ=zwf6H3e16-S z6>=801&*Z9$=p4@OrsH1MfMY}{py%=*u+JNfy2YYx~sTL_fXEDb*TtxUUa&1KJ~2w z60iary>s&`ot(rSd`)P9bh`pT{Q}!H4X!mWLV9LF!7&**mgHacyYrnj=j@u0C#>G8 z4Q-UjjrC+ZQpX=QGcv|Lsmy>Wci)($3I>bbyF`6sgDDHetl z!MD{lec9m@4Z&8(Z(Zn4Djk|;e5{b38rYP=CRM!fj8a9+H8*h8A&yPYaU&2<{QQ}b zYqEmqW%1|7bPWt%e}M?Hu$UrlF*m(^`A(nOlmOwc>|~0#U&er%`(>P?5-a?%6VVh#Dj*}6z9N?;o=J-;H)*<+Ww&?dDgu#V8# zL*E#*Zh`7~0Cv*hV+L;dQMf-foRpF?c4Dbi3Yr|W=Q0+>VmHy0rKVQ6PVg~Qg!02m z7#{9YDSmxh;xm7=R#p#vau=>`lo=1Oljx>K_|3}Y7CSklycM|<^XnFIOj5h^;}p1` zL8|T24XH(n_#yHDJRx|Bi&%E8Ga9QP+&*pNB%_YC{6q1{9WTk}JF@8jP9JI^O(6?C zBMh2?7+G)rID6}dw3>nfanveKve=_4q{WXKhlPel^o!>T6zxcr1+=x}`YXUANLfpK zj!87XfGsm;+mpr_GTHaVe#)(@zp@a-s)$V<=h5skqe&&$+5G_OtvUopAvVZ zQ_r=>m>~d!Pv#FC(K&2&hNR<9ul$!jb7QdrLbM_vsFIkK)v@iN5Y2jFvl%;Qm^6TC zv+cVnWr3gOJ`dwQ?a`z{((|a>lXCZ)YuiKTi&Ed#ga`~4jokQJZc(>7lKb`OnU>&d zou1;^98>$NE9LlZ%L%r`a5u6tJkILb4G;{z0}v!E1uP> z*i4zdzhq*l?$msn_R&zMl+5Uc1PR~5;wCtf6@MbRL->7XOHsK;I+@Objq%YIL#%vy z`ln8!moVeG&NvEe4;amJkR2$mH)U+O2MQc$Y&LHt9*v%4o9j3n_N9H2-mW+H5(rOu zS$$GA-x?)I&r5GXz;A2!^Ls~QKY4pgOU}i+SDncHTapOVxYUP0E4sJwK7j4ZFZ!Re ztMtt@EZQ#Y00l>3hMK#oqpJsSR}!L_|c{J~w@Z1~F=#R*$fV;6fAY(L?=a?7IW^D;f<5)uS#C zE!+<0tt)|P>Y9a}ywBq+QI-D*CF0Yhna__O+gVpoF>Y(dHVP-MJ`+RsBfMm$$K*zB z)Ko4gs0)-ItjqJ`Q}m21J`DW8$%(9Rxkj;nGjZu65rll2n3x*Ckznr3x#FFXmbF^O_sG{xD&{L;T z^!@n152CnQ;g9PtFIZe>JdcV&2^S3)a}U0Z(duSD(ciCEN5f$6o)MS5`1h5!X!9S# zRVga)+s*Fzz_?LH(WewW!x7X4}s~ zcTW;%trKs(L4FzX8{K7GC^@QV$}Ns6R(^(Sjtmo?SUq?nb&LGZ72!QR^QNW2T=3;y zn2CV%YbxH?z34R*k!5OEm#Bmmp`n9vb-?_7AN*Pk(iVK_8( z_00{qRQgm#MrJt~&gULdGrs_c+Br)G2=lGIADfMes~J^%Z;2Dyl9m?JFZFE@<*#VP z6C7o=;OgA!J1nxU&&(V}SEZz(kxlHUv3&wI6#Ta8fkUtKb?G0~2W30zzcZ?kz@U8j z%oPfJYIbz9r=yY8Uvx`A-UO56%}pH6HwI)^M1W$8YQ*$m(~%uS!Fpr|j1;+tI=?&^0y8>?twHT@Rh<~bl_Ebs-)6c2pB!3Q<4_0; z_gO-zK|x-gXhFlGS5i_EkQ0r@MY`o=Y1RXkuZZI*mvayw*ob|){xiZuT4mL&+90u?8M0nb7 zB##xE#iT?0crFsl6Y5CQ5q8_V;7L zTt>e_@{)~2#JlOE6_T$6UwqQou=h$2FXlUQ&Oeokm4(HmV+$H#hJ@Q^bczWkKHZWy z-JvivC}-TH?{FQzI?h?(FlbYPXuwSobAVWv%xpQ7ggr!`NO6SVn)9J~#BHO%()(^> z2mNfB%H%i`)u{=QQ0HX<$9QWQPiG1r5y7-?@e7gn`EZVDU=mbe#XEc$fClq@{xTO% z^&p|7%o2OO8FZVF0TjJyR+@6M2S%Zamp??Hg!B)s994TBJx+nxb_eqN(nOUzD@}cD znMSG?#$=NoMp*!yy$Ol*0QmHD#U?#x>lssW^xu{}Q6fOL2BCFSMrZ6Vppz>l`wVIP zCk<$wcu|AauP_!n<%i}=xGx1IB}g*cZS%umJ*~dEG~zuDBwK68+f!Q>mX_U0boQc1 zBg_>LSL7WYIuv#RO=tx1zk#P^y$aRJ0&j0DrW-dlTct*$PxMr@Mu_5igH6yMR`a&}~1_@%jEL|7)s8+s#kq?&gOzW*e}; z-MY|s%(_WuQvTa`@WijWet**vwDvKa$9Ea?&u-}=6bBo)m*g#qf3H--E0}V(X#TzO z|KUBF5riEw?bZ?Gr=t!#Ro*ugVg2Ta+&W?cJ7{Dm?N3dMoOj40X~xzqIHVh(My9W?SQhO$8d+MdgbXY!I4Nj#Vy7E#?Do0&j|0EzUHHWVg{-af;Vkb_ z9YWN6(W>c`R-irGPW86uRHwI%;wpq@$cpP89Gn|tBPgY>Cx%TeQ5qJiznjVjw>wCTL58ZcbM?zkI{=u~pM)WMb{-29=5!!<@ zy~ehM@z$R?fmZ}ib-dR2&$)(&5d$ym{h#$&szO2>D9!{7N1iz|cp)wUdgY-oP~g7d z@*~hNXmEG{Olgh6n-TR$4N%~r^|L7wFd>c@V8|9EnL*M6D5(8`0yW3y2tktw15+cn zKnk1R88%pL-S~l10O+3?!AYYgqsQ diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_local_diagnostics.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_local_diagnostics.png deleted file mode 100644 index 389c3b98b3970176e8482b10f9dee0473164e266..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232145 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�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�{^Qh#2bm%nb|@Fw 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 >F82n0^casQdQ4ebHJiY4xeqory-pjRU3e3D-D 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 diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age_dataframe.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age_dataframe.png deleted file mode 100644 index 6a3a486daac96c6d89dc075baef1cbababf11e78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101011 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 diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age_diagnostics.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pdp_age_diagnostics.png deleted file mode 100644 index 070a97e4947476ef88ffc0bde2d4e73bab9e9e2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164561 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

`h z7Ii$=7M;Am%i+CKYiKthIS5Uqjzq|E$}p~A!d5`Y`ibF4TA0u*MvDtnk(a3jD;pvk&B0)dfI;TnB11VfguLZx2C#y zO;SjW&w-So9H1kbnOee$7^C1m>my?woVj2qMhygoHhMx@_raS0J6I@yK}aEFjq_xU zdH;BRWqghphc7&c7!ZJABAL|}tIHIY$YYJlpB|M-Az zIl*9XUT{Egt<4xvfT+kV2Rom5<&t;`D>?no{)LIsq07E{@AKBr?SJ+s%_1UzU==*0 zgkiMT=2%W>`=I(1ZhI0mHvWFG95+nW&+D5v;^PFpBN^yq?~uc?#oBSx>=7?CW<0)+ zZJ(ewNhOEZqh2~j^D%M7&TF~6j}`C>Zs*j}BmFAndx&Ks(L>vRmNPESh%>u4(JntW z;B5H3yC3(S?vD?Bar04tt|kzEVH2B7##~fcU89R?`1R z>ygpKv}sfoZG_2FKhZX!LzwP=KqGuKc;ujIWZ$T8Oe-m$>p=PMk8Jwzv*-(F4qxTx zS_U&1ppcQ@CsjTrPfPMINeZv6vhr@D_YpFLh(ML@O9`0!mtk zM(>gR#>h_iu#V`$CiED08)0Spws5zMBSYi$8B2+{hEdELH?}DT3oEwxcGc|BMnAhQC8a*!iIO zD;yBRn!87$T}24+FOF$INy%D?6Cds-?j`>h{fX%|7RxznGd&Mx?F<`%v920=3>AJB zZBg{ltNOYBF9m~)>EFpf*%1+}3X8c5>Nl_;Ix8%#tjT)EB29c|-24)p0xk73jl&GJ z1WJ{?F}U}Ff?qR4R|zZ3N$fKZzdfUi!@oH6@*jtxK&wW%^qtf*&Tw_Qe7eTokQ!lm zfdQ|oziAc;p+z-UBJaoP?kiP8x!}`!#YIl&2jcSV^Qfqxld?*Zu3vwNw3uQE@a4!2-2<8C{}98H&O@q0Yeg=ru%45S-?cI&;^vW8sLIu0jw#b(~cplpIa+ zSMYJ0I>$DLbTUVdkKJJzV&&LK@F~Bp&i<9Y2oqGkf-9xnWUzclFl!txCWK-}=s3b9 zecBJi;kux?+&Bb1g!cX+i7R-Vc1+bv8q{NMkxj}gb-*&KUwjL6D5X5rWzJ7kMOwqx&uk`>B!?3KMK zGh3OJl@TRLzxy=apYQLl{;0?4ocn%_>w2yWw{7OII9-oBfU79nhKPLW5HTj$JnM6H zYn;nVi-^$v1UKRXwtb2_YptqWVi?{e2}{6nseJ^H*HY{b+4qs zFlUXbX|Gbz{O*;={nj3?8-9TYPax)aoCx%-a)*AiB6xom-vpO^Fx&pKhdM8Z{RA@U zZ%+NgBws4oG?@vOkkQ7D5*bOC1{KZMQQe}t^Ku#;bQDcCLOTai=*MHDlE-$JRN6G` zG3+&0xME+86geo;fH+8-hX`H>irRrDZ?j4}zgi@Mc}fT{sY+-8Ds&Mawrt6+}+aQi)HSqS)3L5}vF^ zI``dqp=(q+cRz5ideZ}kxKeX`Bs@HO@QIR3QB4MbQa_T&&d=N)o~DnpXCmP0{QM6W zP6}n$w-el1=E_;{ZH;gM??euME)Yd2bK2w#ezR#+spMFSi6B#GN$BepBh}_Z0&rih z=lkGq)QH7N0C-WFJ$?9=Wr@|8fW;uF;Sy*mj2*dzNJN$3?4qd5MyQe%}e!y07!Xx?i15*d2*5M3yMk zy!}~tYtY4p;KamE#z@4Cy@gnJ7IpTFnm;>)FB3B@5pf{j3>wkPc zGd2#JgdK1AaOplSdbkK}rGaZ+33ytTh6w!t%#nT?w}Yo++hl;rmN5LZ)OLRT>ScM% zZPQL$UgyyaUS?y;QA`Kdb}3%$lY_gfRV9{F**KE$6naa5xFl`x33_iuv1oAc;LebR z&uePVqdc=Nfto+1AmFJCqY4Xyu)FYZ&pfzP1@Q|{1^A_P?$?d=0#&p~A?vLX4CRHh z!t;N>ZZ&eYaOQ<8gac7}&&_n_j&z6Fe+TgzsD)kZ$b60Yw_g)u$K)2q4t_7WiFS-u zuz;&Z5;%Oq)BEq#<|>~v?wMNGq2@%ea&MX{LfitRi$;qHy# zQv(N=sQN9yIwVw~@}07`u6EJ!X;i#$C!E=CEdC#Vx&}&M!@a0RCJeyEJuSL}r|zAEm?~QiDRj>J6y(Fr9A>yUgZ}#jKA%Xyh$E~=iXlF_mml&dTO?k3?Mo)27C{sQB zUC-z5v3fzu5!;!x_jEBP)s1Mz`=5rx5D2VKE!WKXVdcmp1msS6NbHq`_XyXMq>WqF z2381{3Zy=2Es$b@ySj9*6>dm{LyW6|mF;Mt{29>nRd3&n_>b>~;9hh}q%S?jXLgOg z8dvzSuk>=R(G{BH-T6j#4;QTw*#($ccU`?^bflxMOD){`lEOk@;@{ql&|;O3+$KY| zb5c~{56|?IU#23vl^`D^{dJq~eIg#a4T8Nq76bR3z!8Y=E+v`9yPo!3irUgN8YzeW zdl=ndx=4J9VYqlgR*Mka0ErXf_W|%yn$RUFTsSAvt8`E!-XPNq0gk`?Nfw=8Kb$OCze+^q&wd zBhd2wUPc-mh6hi9E?MG7ZRbZs+ZNxCpNEfYf}a86cW+bK>BE*y(!^8ZWKG$xNeNd2 z|9vtZE`AX*p|_yQv&{^n)Ez3CPfFQ%x8is)n{^8C2Jfk-;wwEKdYHT&*laezkgC!G!uJG+I+J~ z)f%uaj`F#hr){<^XkCxr$0A??^EgeE9q!jpkSbfycAjL`WPI=Z_4ANl?(=w#_?U!l z-s7w9p8e$Nk(mE$9jN_m0}p_QO{{$Pg?YK{rev7e%xGgJ;^%gz|G=X4KyODx<025k%p6~Nyp{t7apDCksNkEGnt zUi4{!177gMD2yZ97Wlq`UqnZ`E>Fg|e#HS4HQaYfEyB%*6o6l%41f3}j)VZ$ge;o} zK=Ltk#rW0&thGa?lVf%32cq6l?Yg*>&n}D*=hHsir7M8p;;C$$Gt(L)%O?$IXCH5V z()mk;2K6rs0BBMO7K!ewHRK!p3_W=OY`sjL_$`YVyZif(_R;DX*0@0rxa0+w zxr=*%y@;KNk{yzIz)e7GKh#p`t=uRe&$3^hZnR~p#uD%Wxx!-Yo z`xOb}O9*(htH&=;g`cy$^2BqPtmeeetzqi+L*@^60u(U}9RheUCN#6@dJ3)QfVmee zZ8V(hatwHNGhFHLpx5VwYM8jRo?~0_mL@D1j;&F zzvCnRw5PL1y68Kj(>SrE5*b;ULa4Qjj}rhp@8;8HaZsK;)Sp!0O75n6bksvYi`db3 zgLq)O^3Csv%X2fO?$vZzeF+!buhoCWQQI}zU->gULbVcaG^V$u>K)*vb73inq782h zKlDr$QBV-+8dNqv70d)UJlNQ%|JJGDSGnN$eCix{qx-Zy`#36I7_^v+5W@A%gtgOJ z1=#ik*sWXj2;w{!{I;5x6=1OjdRI2QWr^Y09K4OAUlZ<{gx>6{aEvH!Oql$;9)Z4S?2EGix!ZVkPQG?PLl%D-kLS9J(LceiV zPyErRXpqzS#XXP>#b)=YlqFO?<#Mz;FcI>P?`Wiktw3&E9%*~FZ2)$?8A5%k3gx14 znoByBEkD4m@0%cMr2105shZ}pmO?#xMEPAmF|zaA0442hMB*%NbZ;Xrr~t2K25J<$GMpo12cX?DJ!OaFgj-YN$XWvNed6Wo!hf8!8SArCHGazV?{_CjjJ5 zJRz9rBK%(PVCcbZ?g1^?z)zdx7b6Ss5SySNs0aHg^X?@kJ3yyy{zI5y{skvZ3dPi0 zM^yYNeHRT{b*Fw-P*Ga}K~F5UgKw}g%0)N2POQ{^E{oo$j)#l(0&>fuNPmH zfj4400wxw-Y72mPVT1Fg$0NPyyK98M+fP7Od5elXWaT>>ApLk82Pl>F+GiyE#{}xf zr@e4;IA0R0-G*&4&rb`p01nS^I1BchM?u-(?BM}fa(mv}Bb`un86-2+%)@=TbV}04 zAlGk*w`lF2i`eP3I7<#@8WSp9w63$%h0^;^w4?n%CPfitt-zg3*E5I`EcZmJCcRjH z?16;s+47W-;o%*0^jxI?v{aRyG2PMJ)_)bVk#fDF!D5q^owYIOj1QS zobdFWu`M!!H-y@Yam5;~0kK+ndN=UpE^z?mQ-W47h$!GVmEA05K*XeJL)Z)k(`j#_uP&pnXT+})+|6fE;oRc;TT zIeCj$!7ZL5@2yq++0-ATyr$Q2t9wvl66$P3%>L3y27OV~Td z&g!^TF+j*Isbsf1q^c`(sUo617Fn(y#B#1nLifcsg+$u{JPYw*B>(5pK8Tlvm0O|Msy zld%IlpLLGvCe*nE>AcLp%*d2>dKFFoj1HQ>_zOIKAwa1w9T18)CXiuEcsm1JwS4J* zq=^>YknV$aN0N;ZaT|I0@0)!_3X=ufd@p;tmfV{?G6C&*9r;6}3+|ZO?4vzrSA8Az zZkUYpc$(x}W}9=&k9cd`-k)_p$c0}|($;b)sv|gakkbWz17l(qo2KlF2MfXM&wh}7 zZs_}2w@r0vwEu3JCyyLZZO49f%l6DaTE4aGIsP{4J<);>^W$nbd~oYAS7zIz-e1=y z(0@{GbjayI^?V*?`wTYf_+ymlBmZRye`0M=cG_!p^7)yATz?3Qu;irhyMn+z`;n3| zFiW(aQP7i8Ke&PbpIGYR{E5bUxI#IF0r#oksjKymCpRiiQU;&On(MsA&iue}B7#cg zwX|Rm)0DkS9ecflcRGDk?4}v6k!4nV-;pY*6mb%F) z2R&iq^A><>Ah&Xxl-i3pv&NuaIV&>XBC<7HEL$&IeFbRcO$7Q%dVhsoro7zddS&dV z*B$YTCr=!e`v*{T4hJ?|DmSxhntJZt2xwKgHAeJ6rSN@3Pl{K{_Qa;h$t6mYEbn>{ zNjXZyty{nj`Nb6ze}OGe4s>uWmM70SL=d<~i84U5>9_m^$^V52C_`P*?m7*m+kbQjPzmBt@i+c$mHq>J$CcFs)%2m{vu~# zz&5+BOl0C!Vo9<5H1=b-2hrv|cF%tO=qfb2*500p@yI*`g$cpgi{Auw!y%e-}KPPMg2{Y5%LV`yZEKH__S0BG4 zxum}`CH1zIOB^2+cGIU3wTx9)Ntnu*Eci1KcPdS85%;;KJhR;93X&BlC0YKcgDk{d zz1i@iOkX`$`qk$@D5^7{cl+-rEV#2S+b*v>&-)k*q#5045#}IgvS-V;s|!zf4b|V> zjqHnzrTk1M(k*7EY=J627m+*csQ-+MFo}2bYutATm9lLRoPn0b|4*C^Y z)ySd2Lc6eoZOhsp%ni}4E0GzV+o}cX)xD|eo~XJ}z6$5cr$v{?nVL}w zg`q~g>MmBle!B~9Nn_e@(1TvEyu$)=y$JZZ!D>(Cz?*;deHv_3KZcaRJ<4c0rq_ z5n~3qRnoQZlq}pNk==G3!Qx8ju^2LsuON?IQ$)ecM(_>W|VvXL~F)i%=N>sL~);#QDy+NAV_J!W{;SS}xE;}Zpq_r~Q? zg#!A)XY0l3x2R(8(W%ksWk8Ig3JIZd$!Nw9P`J*;HC*87VSm+ z?Z9>(#VtFLH%lnEh}u+mW7ffnfH=Dwz0-IvVG*vgXe{$6^?oPC^B2z(R~CGr8~Wp~ zBF^qY4YblE4?-{5dW9!kcZ&r&!0Piy$}vR?5B{*YS<~{)@hWqH56QM;pjb-@EUipSkfX) zA z-p~{D0wUgzDr$gwj6kb?1U+=4f~eHgNZ_XQ*Za^D`RDKhpsz7DTBHB)3kQ#jrcz0M z2A+#pm$4~B{t_Mq#hM1@{J`=NaDhBPr{w>Ng10vB$=`{j_x#=Z*c4;<*RtI;Rixw{ zNg-{!X}g;@)OrNyI#NF%nr$AcWA(9zMd4In*~8D(S>c%hcbuj}FNxSXL8Cd2IV>FF|=%`>B0=R1AmrwX& zT$t!kN&33AB(|x_If;1Qav8U6T@%wKKkEyX5|1g{Bw@#|Wh6@Cj?vQGpkgGvcrov7 zcNqP=cKn_tgo9gux!&j*7b_KBJyl_4(fOua?<-N)d{dVrHQUQ$F)|`~V=84_5nU=< z$_#rH%g&=+&rU$*RMALUviKWy2uo!Z0joteOTCNEsT~419=@Yvf>1mv2Q1290a&ri zKF#zLfthKTLxLAl>RNa@h!vn3~u8~7fP1s?dumr z?L0J$RI%{#jqck0!L8T9ADtBlThB#0MmQiGaOvUsyV;SO4)g+oE5uatN2=)fs0)Kr z#Cv)wnv8ylQeqSVl`={VKHWWmv!0jlHAugN{}Jw$U@3_?!bGZ`P&h*2k6WAJ8T#Y3 zkvS#N$B5W^DIg!lMNrwgETRcH5J@rI0uIvJN8$(@gDI)#D!Js%MTIPQZl~+Nt`I|f zmw8y3Lw6lxQwX~aLf%<`d66rUS>w;jbcR#GK zCPDoE(@P^i@{a`a5S0X~_%r1ziy*qeap?JQq=qqd@7#>gf=I}*Nv*b;5Ge^gM4kTF zfcJ|JyYWlqAUD}*Z&6ERpz34$K-JA(!xV<bP^3c zP_7nRb;g8L-wU=l*CEL~jzLJLrb8S22TFWO0CccGe){kCb={I&^htnQ$ zX>QhH)ckFip~5b7UNmkdJ;>1>;oWYKw$*vj>Z|f zZ}GTR=9E88m)by`FUS;=9G>-GE+U|{GmZo4x;Jsv>U{Iewr}TL)_FRtu6Qw(=GCBi zWpSgaN#XLSPwU6C4?QyEarYNA?C290pnU_PpLsq)bVS^G8L=OzQQD3PCR9V&OR*0@ zqFTtp-meCfb63goCmLMp5&ld&G&2LE7yqNbR0@E+2vYEZ;_@hf|Gvzn;pH`~zTtnD z4v2u&OZUw-Wuvh>(TS4CmW}NpAH#fWp(HOwnHapxXN-{h5Dp#Os3@_rjqvcWbpBip z%+6}v#EnX1rto}r>l|+zik*dD*(}mucV`2j0X(KVKK@4;$by|qkhQ^Ljyv`__(c%* z=e;1JFq$o6Zg5s%Y1E=X#O!a5QyOb`|FqioS=DtWFdvw5Jho6^BOWw z-9xk$`~7(E@6EEt-(nuoNOx8CsOr}(a;EC-eW|LOBCT|S8%V8c2{uqd;E_{de@A8i zl1z@gZDRQHzPrH-66L*p)O_`w=Kg!=nr#EBSZrnPJ-t9SANHOZBHKlQ$kw}rd8!7J zx#0MN_QTF7krEnkGQCSqst)06f3{rWs7YIXdehPiF^m>)PE@)0ttxAMQ3!5$ZT3+` zwS1&8#Yhteu^+u=7OpRUjXNOpU*h3#h^Q2#G3}jt#IA7DtL~2y5yKrOg0lrH3L9E( zF_C#I(rlJK}2ZS09>Em-qF+Tuko zXSXhfxGI5z&n7`YnEWlZZLWYFxRJU7S7IGB`?>#uHBPA4-wfH-MySm(88^gf;O9^~ zxz>dxDD9ES#}C=qiQJCNK{6fqFl?E(+lD7fbW%SvrHboeXx<7UCGLOFUL z)vjRw;EMa5Dyc1${YA#9HFP8`)EwPC3R2-9!cu>Fx*>lo6POKpL~c*~o?JQCytcOG z84wYNW>UQcr7DFNvYH7@Jv!|A6=GS0uA6-|YpA_LLA=Sa_GBO!EpMb=^m=2@HteMQ zq~AqAj+_8DpKL+P0110Z{GN1hNwr_O*vn3!Ml`Ul((zd1h!X8PmNn>$_Dz;SXq^Au zNCl)}c@TDel}+=Weq=tDF{x>YK^M`x=^R!s=dG$GZpn9-fzO>j5|Q7!bMK&G2EvZ( zS3h~Yb0+oPE3%{p?%qa-qN`+v6Vef!?wvc>^st@q5z$UX+Tr1Kfh!rAj#+y6X8Us} zqYi-BN5&2D^gWIc#-JnX)a{HUx7IdKq0DVQqw=FY^7b55pLA4PDJ-X`-A+IZU`sjQ z#3re2w7wh|V^BV}e{sE2p{(HAy05$;zm8eWM@zPgF?vRf?tAQMLdVL}ZJtzv8)<_^K+kcU&G8lYzh}pY%>kH@ zMmC+iy0Hi%0e@P1ognw&!cxzcp0n4bM1Z&l$L!2q!a}QXgMQkk^U-MIS&ChZEmTttj^OCZvw5Ln>S-3>RNd2C!J@2Ej_2=)oiNId{37C6I55#pT zne?2-vkRA#7klLmdtQJ#7EV@m7El6_UZb7FhvVexOa?SPim``oJ5pCP?b+=-t>}8L z+>3)Ry~M(BqqZ~o#QHupk(+=J&`&@)!Q{ioNM>6by3Kc~JzalYTN#+r7r1-`x1ITP zA5u5tGd%Kt->-$;DYWcXpQteNu0$#rlyz^^NgRRr!A4P0m< z(af!_FZXy|_9D-0E-Rj_&)<2sq|5r(#XQa^YxrYQ2j{2E!qAK-w|bH{dYkN^YHsGj zaU|({TB88NP}P2~61L2d%LmwvQYnfKS~inZGTIsxAg5jwj$@*^5cElSc(4Dx@Y9|t z9U1&!J7S|a@nW%mo4TF9NXqMbM8EpD!^rX`@VC0AFR0ec3lYBP92b;6{KA+w!BBtqHDMgdPWGd5;cXHHTs|w*s~= zBDB~-$-e04c#%dLR|1wG{UI3lr<0yHs{S{q>-Js%N^7ni(fc@YkJe>& zTHesIvGd!DF{cSxu*H?QExvUH5@}tq&g6Sr_m!u=tY6CP6sHq?+9uD#QO1^_zTnq_ z=llc+#jXvuI5W0fo+!`WPmSE)IHmoaFV(^z%V7R2g$G?1Z)-*+bX9xw6AHe5T&$$% zrJJPv>j(~eh#T;Q*~oU96F*VF%bA2SUQ=Z}4X~EBLV-KO$SVLa+xUj3RuwEMa@(Fz zu-wM3&kS}%(9ldMze6@!ZS?w5vi+ED+EKq#W~i!<%?TQpN7zW{|KMpRQ)$Jx^^df4#=e?iXHc6->nACJ}o zj8RV|ZfQ%HLj`R_w(9|+X4eqs!;yrUumZtRCMLU8OWgXcW3Ho@Yk?!?iW|QGU6JSz zF2NDqhoM~QeD~8@^gIJespe(cP!P z2k!!3Ts+Ib+p@>RI&m=@NR4uFz=wP?lie=zOGfztyv}L>>Tv`jr=wA$*q8$y1uY|QhxRo*-&Kg}9>XF|6 z`0{7Jg?syeOWID9Eq=$E8JrVf0JXVk3Am0Vt%wxxAiP^6nhXS7Sv4kJuAp#rYqp~{ zK;+}=9k<+{Ls8Vc&_fl4)rGII+P`F=d+ybG%A^SBg_tB9G@p0m0{grF`Xj+O=FuFP z{N9s$lwW~_*{^jsxNW}#D}9guZqrTGMGjiikA68H>7{fiL<7<;zq#>|jfUKBV@u;b zY!|UPTsNNrzbOKQgg{OW6l^-xyfF*dl_h4LE6YM`ZN^^tCrw)D$C*4~Y@8F4*!wkc z_Rqov?ZQ7MBWFaNoRJSJ@|kCgN%U( zQEhOA9u{tzAShfGT%nmW4&Aby@l9F_tqtm>xb}U7!{hUlH))v=p(psa&sDRLYU;~_ z)`+DB7gVJ-^&I}0!3}clG$4oaV(bK;J-tfkLF%Bof2yZWsfFrFfNAfj$trPsG{B{~0(KW;UbflP_{8)*obNvKzZX|fLOHI3poxhP z^BdZXp$6RyN;Jq=a?|DCN*XN2Bs^UIKM4;bU^>b-<>@3oEY<&1Fl(R=3?fbLZw&PK zKW~LdEPYb%dZqiXk+7-bz{pMAE9oJ_d#V~QdNBr-q;OP?JEL*+E=Zkl+42}KwnQg^ zYSe)2^!;SzQ1~?Gmc5qGo2FT~r<=}ZR?6)J`uGUt4l2r)o1Q@{E-#*CxDHGoXt>ce z5e@bF+wDFD4DGpEuT z_qL};!Ad{Ya`{FUST0J_tchz+y{QZJ_5c9vY*W=}2#?HQK&~_FBW{rX5++3<$(P%4 zxPOE22}vtloH>dy#5S$;IyR|T>3wBuA1{B_y7YrqTOj{o{`uR?`1%O;@aK?#p7q{p z8r*F*+WMG1a981Ek!m)W3hZQ5jeax|c?}6&rGV-~4m0&%S9?{)NphHnX1+WGGFp@C z|K$wb>S`8z0fZ9jVWX)BX|dIDkF~c1&FmVxAN>w16`+ehU{CjAQ(V)kzkVRKa=rh% zNZGvz*Z%4-JNCqni0$&E2C@kafxg64Y_4FfAEX~g=$-)ghA18M%%lV2<@DFRgK zgz3nM2XIMAFZfigMu&Q2tG?kxoLD>3b zm9T)YItQ{Znl^?%*9a8hD5o6!)b6l0PPQpKNtz^FZ~iZ{&wx!;HkRMsG^tFChZg(f z04Oh3m|IU!Ny*W0k6w(jfE8B04wSt0+7Bz^>LaEf0m7lO$E2H1yd<` z78!!5HRp&DzKz4616ONTsv4xM!AgKo?4udgHRiCjgpa&d7p{hLU;db~49Mw_?7hpf z#(3O!-!0|j>cAfj5mNS?^gLBid`A&yWmj*rE%J+)KV6QZ3zsEoR*yYPF-u{Ej@Rf+ zRB2N#tRxi9PxFI|whK1_cHEsDJ~FN`ZKI@CXs67DpZs0LlXQo{%MQF$PPw4++KDq! zQbk5U88`&i^4T&Dc6qS*U0}o98DAOlqkk;u=rh#Dffez!=|Xs*ne44lIrJeIRhTVb z#pg&yH)@!J7?9CQUq|h+oJ~u6#u(5l=5dE3q8!NF5GT;wN6Y>DF4L$#D3i?!s|fOp z=aL3~m^EU!S<%1#DfT7@Wx18ps+GT%GZRg`Xv758yjstvrL(qkuGZdjoMMIR=U{ld zj$JX>Fwbi=qF{YP&^T^?c-H^Jge`6$t#hU!f(?SxqgIGNrsq>NI@EZJs zeE%hvZLX7Wmd#?_AQt-D8(V( zdm73L|aWfb5_RJYj*g5C>u7Qxmechm3mMA(roKeRyE8$4seVH9%A2%D~m`Yld{eI^j25Vx6Q9 zulVLj%u7wJv>&`7;c?XKyZ=z?_iZ;Qk1>c2it!03*+>m}gf<3c74$3vO0=3hUU^F9 zJBZh70%Kyc8N7FVTcAU|Gv2`bhH|pI30Og5P?azHe^N_C7QsNSQr)T-OU=Ib_ak?S%#qHUcQWC(`#nw2~?81Lzb+-U>u&^Z{W$g>ERXFRv1CFsZZO z$`hN8jTflvUP~=QHhGg`MP*D^6#tiO#1wW9_aTtrL`W@l7c_!kTbKtT&1rae1*IfD zh znz3xb@9nIqep0ft7(Va>O()QpW8LL^Yz&gqvv`_K#V+>6GBNTRR&O_8KttN@fI$}zG38t0NTbz!`#hwG_VL< zS>@tCo6WNZ!i8+r^`yi6gMg@=w-@LJo7C7UwuD6RXLQ}bSqaKaOAtuv;F`1$P`%DQ zX=b4pwEDjYDd1&IWE*iobA%efI?b_QMO?*{+19tCb*erlBd^3w4>Hhw8nwG>?f(p2iWC8IJ#oC^1)KEwu|p^+ z&Sullw-M7YW6(&L7+RY&Ev|0gHDsb0YIrrhrpgD(@E zf8`}c&AeA+?eouL#T16c_S5V$zqUM9IFd+koCn z*sXch-%Ijg2rzu+-U>t0&nLAozeyzmyxg$wD?q92&ny4y^2<7sBMFrMbKgn(4YiQi zJAiU(7WmRnsh8of@Es>Yz^{n&31+^7!xb<9W+tsK)5OZObf(j*J%ipXFBj0>$qoVl zN|}M?Mf`iPTm>c`rj8`|y+_wiUjid}g|Y6hM}X&jrgJy7YA)f?hw&WdoggJsyrGPY z>gp~dcl~)lzDpczoFJMl@vjcPhtWBl@{z*)zO}!o3-$-nhz|46+DWgzE@`*wY-oBw z(T-L@#{);v(j9C;+x*r$pfm#8Wgs2vWx9fS*kRkA&;hwSKtV{_0z}931RyGVSN8(K z=oaSKJ79VaOf#vsQX&8TwkD+HJAk$xc=Jo#{SGV_+XY~v5zZM9#!%*9Ir*Um&}5i7 z0t$J)nVh@8`dkHsAB-=<`|iJ3CyMI^^0KHFOdB_o3MtyTWm*uX}9iw zV8R&&9n{?H2CLww)c{g$Ik&nIZY|=j8C^LSVbj|(N8LD?C7G`JZ5$n@6c(hU0zcHt&qO#1X^d*~-()IAcUB$SNl__(&PNEoCe@I>k{Mnl0W|7kf{<^!FPLIA%b z!F9#YfO^ws0l*92dUtz*N{8$t8bdWH-jSpD5n)x-#Hs=?3!Z1bhUdW887iJ;g+qtR z9H6LG3<3st8O1$9-PRWaH<3I?hLRJ}8dFljopTD%vbJauzDF7IEjrm6Y{+(Ck2MZs zYUG|vn55VK%w6Fp#sq^t_y_cI_` zak(h9!NKCTZkTyhr~K&(V~(J2`40J|L4ZFJNbxfg4W4=Wu7BRp`zNz5qeIC*Z_i}d zt%(3t*?R{kQLzqykmhZ(4>xCvIx9sx5U!R%bpWVFry1aeVX{}ih@|IFKADQyu)~5C zYFP_XJb%u2T{X^`%;O2*ZZVPyYg%{++r75gvB6f$*;wIr8+1kCWQb%CR?0)1FmsDS zUGXX?GN z=aA*bQgy^}twgwILugp*iDOweS7?9z5v8>WcXu?1Iy4gC-UF?4C|R-_(G zH{f$^v_Q*RcpFnRHQ^o^5Vz{Se7o9R?=QmwDxA41!m4UxvK=vUBc3pw3q_GDn*+|q zJ?6E+=F-zO98(u!F{XLffNo1rZ3E^P2%J0j>lXSQ6zZ>ag{ckanwp}-UZ__xrM#dY z8U>o5;yGeeg?`-lW1L@jl<(dhr;D!VZj(=AZenxqS}`W~@aFj1{td-2F-qdiUaj!( zNmV!v@cuT}>3$S}X_0#?u9N~!aJBA0V9v{(?E(K=Q?zhb1LtlmYx>BKIKN1~5brjr zhKoG}0t=l(-WWJnx>a|%FHa_oum}jORFo#Q8Nd09sX61u8PLRjGz1ky;imH2p9u5^ zqElGDDl-WaoAN>IdwK0!3saiSbh2V^&y0+zUs4kt*FZ3^nsSh8gAkgEs;*sm#vy!H zmfaM8`eI-T?P@i41k4R<19x5jVRnOyG--}&3^%GHCu=7c;~iMy2^1HeaZncSrzrgS z0W!z(3RrB()EmBKezBYOIo8mNqqK{XMVl#vQm^iQx1?fn zk9owe?HC1T84W7B>uia5%R2*2eI|P$k4!U0ZWncMkIBXf`o*3-hc#2?apctlhu!P6 z(?vGL39^>{1MMxRfgC58fT&q%b(j6idwmR)kMlQxiFS=O4>L;C-OZ^glYIZCY}wzo zfl)lMP2-`vp)JPULHH#)_jz=4kWO+(rd<@eB=-I`{HiDJxasx)SO(^sTT4ElIW`ljB#c>U#|X5^wC}ofbtfd7id5!V-PpS@pC_Fl zq-kDg{SG*MG{D<;nd|mOFC_o4CY0DX$K*Tsp0Z_WEG2~|TA(vY1JYq9c8}5Bh#vc# z+lzpu-ER9yQy-_tn^NPsq*OqMOJ+ z+f_fa5XfH%CaAwFb8jY#J8)VRrw;4czldkI!{1 z<`&?76f-XQV;jvr1)Z+4PfN{_$lbA*bNM=WYY8tl^Vli{?asJ3x{VDhNqID2%oL!r z)UQz(;=JNz7ReM(ILmU$N5GT6e?%jdKGQfdl0u1)Be7vJ= zR7np-0DcEQBi+rbzrb7i4Xhm2AAFv!-Q?uN9aI4H=7GmVdV(@#3OQdNt*-_0AZaOl zO1H*SG>gG|;U^ZD30b4hj)4j$%g7k$Rc!T)Lar0CntV9$#~#9!=Rkv)gGOxG<`R`( zPHv86emp6y-tjcle~}Wt^XFwe{8#ZK_Dsb6#(=8*|_JP z)Cv`8l}(V2I+tD9EW7jKp7DZl7NF0(8FNhKN{&jE1rUp}J~S{i9Z;ov|2!}zL&>8a z?h1sg?jG&^LBl0}N2}q{h1`HiW%rAkQieZcc>v1S*NR-Pk0>Tu@)!kgC6JAY>)=`w zjqk~XByszt@X-1cn+IkHB9_4RZ;IV5{m_p3ld$rQzJ67r77W#dXmB!W9#{TSj_Fd5}SHO!=enrZg&AB#%+e5!e z8UHTj<)o(nAD?4)@h#wSrt(o8>#&~ikw)T*a+Cr|lZHT^Rc^?@iPBxGkdD|6e(vMY z%)YtPD%j^?#>UCg7$BTbHmk(oq0k3VlnX~z=o?+;BtJjnfyI%t=IR^OJ#+~U$#gR3k~wRaOWcmhnP2{3w3OIk zN|iU)X_aAQi@`-+s)&3LVggJSx!11%G%ij=UJ3$s<7+S;y1D1}|jWr8L9{=F-U%A7XLCU#p{`?Zy>w`4K^owpWi zOiVSTC5T=SZio_&A@uSl;UCnKXQDJPq^6wS{D`YaF{mW7F}V=n4Xg|3v~IOfwGpsX z_Jkc$_*lpdIwb~Q|JptudJiB}R|eA)e;ld5fY1`t%w|}SGMxW0Fxr;eNAtxRLHE!l z-#*`W(>51C?D0C6Wj-bu%VUlAru1BPlc?dw!_6;kZ5u)UZVk7 zW>(q!R{d8%0sPT@4TCfHCWU5~G_cq)CrVvRdDP7OyQ#AGF>q6unYlkD?&7n_FkUa4 zQk|!BsdIZ6oXWkg_Y(tvH@^5a@vfD?+|bCW!vI%+T1iu_Fj5H|;|&ID^$?_7o5EHO zpd#D8X@hScFrvab%nHnO9==ut;>3X@-wG`PvFc$}OzN{nVc(0xj?ExEkKr!Gew_X0 zLeTPN`?uA0mXD`7k6Is8l|RzU57~eBV^86g3GhU|`Q@=ENR0!ym^}D$ z@*e`V7}sr+1cE&S+q<`Jqa-V%XT!>U)93t5q*f*t%tiWke_&$6^9);vRx5B9Kb!CG%_7M+-dY#5^ocGdp7L=EPkV^Pp zfxZ+#%CfcM7R;T?d}^P$w7je-da~>Yn*wsp35chh3_fCuiIpZw(HC35W-vw>qwlC3 ziu?aVFeaJ9lpyca^kFgflherSRrFWN%RygW8x>}3(lwD6l_LOr-H6Xu_Hhw0#$vqt zrXVK#LI75YkyNrNJMX4+ce|KLl=*lnFyS>gdL{w6#O<|2cTkuJXg^4SOw1JJZIkGy zs_^@xolV$_57S$mF1UzZ{r}$p%}fqJYJ_(=n7({?z31y#bYn7aW+Z+1UdHDl5px0a zrpf}Q6CHuxYid6*CR_v%NIZEmb$va+N7b9>ph7Z&n+Oy=4f zxgw2o#eWr1fgH9S+3Su2cMpCfNkJFZnA2186X|7U`Iwbmlm8>v zFgM+#4g*i5FtdBO4=N~Lu9Nv8OWduSFtdRfN&*3g6Ubj^<|O&#>*XlG)A#g{*i*Gc?nH2wF0ov z1I!OrY%7S)m4TVaR~~VsutpU`VRAKLDlrqs)`vSNeqv@i2_Ivt!0#HgD#oAQru+(* zc=hxjbhrkBso4Zh$oQGpNDhxu?y!1n+{wSVyN7OllV>W_{63`Je;-CjT2=AMGyulZ z)7~_!R$ck6fUS^6b>4VHob{-q!zswmuFKjkpTSN!c@tp*%nfEku z24g`58l{`uflu5Pfy91OtbEW&@g`lp1CZnzg#hBhdEWtH&cRkR7nYOiCTN7dlCMCA4kKrpw&0{+}&{?T)2-YaO1-0O+%mYS_U z>7h6fc~Ijj(CEwsM4~jpp|`vim-rJU;}d3yczl5}0wcpe8BYCdZQ|O8Pq(-N^cG%< z%H3PB3MSf%F0`Ej)Pw@_GoXf0S5Ntkd<40Mldp-Uw$nN1OXE6*qo5V1Zsp6FGhE(p znLx1h{^qw0%3np(cOU+D&_xjG3;Emy`}IJiyY9?j?VNfeL?Mi7x9Zfh>>%X! zYkb4L`lM4GC`?gvr|L;cpoRkKTa$~pUpdtV0|su6eiKQ6&{!iiN_Y7z!WOnj^6tudv? zv@xX8{ia4sE1F9X_?l7=-sk#XV2Z_@j(RyX%|Hv|#LkF!mr*zt#^E%)-Cl4}PB%)Px2`d|m?a&FZh57x?h4amOgD zfsg7}m@})Uu{Pg0T6+ZkR1|4b*2rLqTf?B{YvZEgQ9yr#M+`{L)7RyMCU+Q0RX+cWQ= zQ4%cD+m09l;z&0lFoN}4(J$USBoDCwVD|rl6S>-`%k+jRIy6*-0c}lQvs>FnvOmC7 zvS_oHXa6?AtFo;}} z5gdOI&jOWyPR%o%mSAOU96B8I8`^l`lJb_LW)c35lhr5wMiB?NQXq#`Yb8gR9W`oj zE3T`0Gbz{_pdx-`@_yd(S_^(vDesz5=XFi_SU>IFWXEK8f-~t&8651L`=tGONt-4;T6GP)ORDdpI_lg zscsc~U}(V%=}h|Tfwz@14PHyH!imX#!u$Aj3T=)zc5JI|xSN{7Y4)}#_qH+)dJ}zF zlAw(d7iIYo7oVv+=ns&Az~%&Gu5)~<@*NKxzu;Az(3d3h89U97d$Tm-DYq3`8M#Qb zuke6z!=3&6!sJigw0sWZl-<14U4IM@XjTpfx;0W61$lW+8unn@(jT@8lJ+)9@aV)A zw3WbaV(hRpD7TS<7S$Rj{xPL!kGggBpI1>vLzFlUZsw@!!E{2Jr^@&1ft#z zOAa@uoR^PNLp!s35#fH%51%rHRmUd^T0Gfyh0N->!yE+vdrfU$n#vHQW{-W^#?-JH z?kl8eV35Ujy7TyLDZ=P9v?=S(0}Tkx!J`f3$QQ7*|bH+J$YI~mk zcF^b8Tqua8!zkj^O+<8^+jjYtqSa_Lwl@zj>8K=W>`F|2vX!yJa8@3K-O;|K4%wO? zc~KE2B&Km}ZeWu8EuM(tMi4U9aoN(N8WH=CsXmJ=Fd?tq3ZX zU-XRB4oT_H9!mF6{gt!eUg)4OCFtDA)yu;}T1chBw|?zDZ(eRB6yi6VU*kflp^Wxq zv({Jru;#bhg8IdwVZUuJ@VAU{z#^u2srFbQw%65v*Rn&jp}tVL_jP`_|LrCK0Sp^$ zDAaOl?onRD{|SJ*rRR2$wR~keTo&qf4x5hp7Zijr54=EOJPK_~c+8`z6ExD=k6`f> zd7|})TKkQf_2x^gF~`giq+$}h1-h1kr=qRJ7OA*2aPkaHe(&qpr^#6xHoF%q%O>bC z)j9f_QBJ%E2gz=%2H|pMrNA79u*8XqR&H7Va*Jb~e=Gy0AezTFfEZ=BV2kX_&|z*+ z&U6y%=6n=Q@NGW37YR;_;;vfWt9e>%UQ}ASN&JbJBenYb5cEfL&JMUKJLXye$wiR? z_JDgRLm^90cY!w|MFu6g70><+WG+nAyMZ(chLby9za6V+IV#b-D=Vam-j^yujl#5o zT{``_bn?LsIcWNl@)66b_22zmn=9hx&!yE<++B#vRya&|YKGHL zqS#ye38)L<>^u(QqURUk1Z4oki0bI#?;1V7!QKp&=cD^o<)&XG8N_ixD+4bL`tUbL@3*B&apwN_3}d2HQ=@3NEe zF7Y(6SfAtAfZe!Nx6V{?;gv{?I@RjH)(2wDdpz5)NzyjKt6HoD~Xy1cUjHPx-k&d~<28q#{7ZdCaxyBI;Wxk&;Ab7yrLwU{b zBt5uSE(iN^PkU^V#r7-}LUL|j)td$*u2OjxNY3XReac045`!7Xb zfp9!hypTh}2(7jDHp2lz;0xz`j4iLw(RQ3M-?OI!1_wef;4%un#>_L`HB7t+wv-ka z-*%JR@*}UKEjOIJPFTYM{g5w&6x{H{FgT*D#|h~*^CF3DO|TD}^m0B}6l?J14pntB z?<*aDG>voZj%Ifw>txw}aJr|h4%n}{rN<&%rm7a5_SylQ7+@*;-y;HnDTAPGEwpUd{qwC9S;croY=Ytyb zz%WrdmP%6p)ivzir?5zXmY>C`osf4 zdLdMRH^$?@$lAQ$B@UP@SO;v#!N*_o&I6pKo!OmV_TidcGY_rdC2S-0BH7?c$<(Be zjGT>1e(4m(!gUF7=V?@8$jQHhfdh=-7k+=Yy2<5eil68^IzNv@!#|)sO69eV=$(+a zfJn%VY_3I~a`uP0S0o0Q^=?XpF-GU&?HF!ej+Rqv(LdLs%3#AGP$r=)w0$~U+6=Jow?a@G^Mb*_y^%Ao%E=u+Fs3ix7H84?O2sxTm-M+HLz z_*H`YhFZPPN;-q*O)lOv!i}(GvDSp?WMn}u!BihPM*y38j&4;%u0{ezylYW4{NeB%xgIP+l=y0mDI6chytz#F1 z@K5o7wi*ej_fceJq*H$-m{5p;$T(td#SG$8+z^4>0EClPT9?{Hy@p_?LdTsx6Zg89 zZFg|ugaN+Pi|(Ye*Lm`|k1a;}sdrX2K!DN~hOz78l8#u>Yd^U*!Z-DTWm=d`atNmO z(?@W;DS0(>u8{O0v90=)ok%xSo?oID%oBh7!Oyo^B3UAJjbQ&1YT-a}U=WfJvt{y#;7FjS8%f%q?z+32`WY5=^$&%%H3mPK*jfupO1fZE<_q{CVr_hu`T|#@ z8kWXpGqckqWQ1SDk8cUtj)zIK{yud2a;VH-@I^+U{DY+5T6uEAuWy8#AXIA}##qzh z@ee{eA18pP*M?7-*;uCOrzd;|LKM*5D35ArHnhWIne9|HOKo-KMV;l{O)zka59;S9rP)(pBu#>p1TkdoW zWr7qRePNvTy|_AJk1A)C+ zGAQfw=a=(pI(hz+vz+iypsDX8jAhHprRG>DlSPjj!A+o50(_|&^#V6Lfz^*fK&xDW z0i>@qo(U{6PVki|#W8E1_1HReLZj4kAM}$ud>V_KrAh+Js@)su7ZieZJ>TzYb{$%#TRsh0nsM+LVIWt~o2X;2Hr8-sjJx)(+dlfJT>~+Ff$U|)eGnnq`yD)eK7f(vvKQJ} z5Lu__j{HKYsF%m$EaKudDbnBOvPQ;Ra;#FULSMigl;X+^%vIg1le2)hDscJ%is?`f zu^)#Ub$evjAG_oqfn`y~X<2b|Dh`deyUH4^407c~BcIn1kPUgzuOjyK~W0kX= zCT=r>y}J{AhOe*i9YXqYiif5l*o@MPxtScSXnP(i?RbcnE?JQlwpWGEgoiqgRoIDj zD!^|+gIZ%owUBu0@FP_|Az@3a72L4wcE8koFHz^_j^* zN)PhI1bwQ2K6{ymq(sbj{CY1b>oZ&J#=vzLeI*d~HBURILWi|Gi?{c|h!8q4Ml&WI z_<~%5;qvdzNPT+G;Ha{yHNQ%6s#qXf_O@KiHNo|3cNjetUpDgcKn3La~RYsZXG_a zs+%85qy=E3AiX3(OiYDuFNI@UL zlEQdnkf=WU2~QnT4j1r#s||#QS9~J~1XD0hXVZSX0BDnyjX)1uiJ#xU=Oe@ePp0p1 z#_*;*)(-91>nk^^wT;=P<<5I4{hasUE&L@lieXDWuKu@w+{Y82fKo?a^zf>qgr*W@ zwFB4Sf8`rz6l)wLz90B0!pB}p|7UWJMT-!(FDQ52L{ZeYFDS;@l;A$-{6F;%YbvB? z?;y}0fn-D?r&`jk6>5XDJg11*$d0aBpKRSVy7Cv*_WWL|0Ex&_dAN~@EPuylqHi0$0tozy~iPa!-4$s<&d zk>I~C@Hk?r;f==`DpK+Vz;~s5Y%=1@aMA5#i};1j9mzjUAb^b z+5ZX+kKij}VfgZVs9HDf1xeu8+n5%s&$EUP{S}84Zhxcn!0iJW`Y|h)9YUWu9YA_&oZ*Y=mml@zKSr8^?#}A zA+NI~yrTwO6CxRl11JBydZ54uG{r^yz-+$BR9Y0}Uf_VvRSdqDYK1J!p97M}4+Z5(_+0T?&2L0gb!)E%S z`mK&~QsoE&3^we7)!kPb+}v!)SbQ6EJqF5LnPC^4@d#Caslkb#K4GA zlG;S6W-S`Mp@*o*Mr;u5_#(L5Kt)g3ocE&mY+hvfat`+>&LzUp|oR05t9SVLIrrwx9W{38U)`A=iv-Z zk*wDtbZi=2)N)ES>k6FF40;{naXgSL2=5e3I#2rpN-OZl2Af>kb}!hQn#D1_gy0*Z zzY(bAaKHGgX4XNC5+wO$LzBl&)}0Mqx{10)4$5*xI59~$HJL*ET#02RT<`F$)|AqU z28Eu+v3rDg5DFM&=Y1iPH9-3nr~5|c0ZGQ3%{)NJ%2DPsCY;RaPCCx`@U7Je*?#$E ztw9MgA0~t>w7LtdhlazOE)$>s2|rs}jcn&9;Fum?qY{N1l0#!ir+EjbNOnFIp$TFB zZb`J^%W~c6O9+`TgQKe9fUq`7vf)@&<1*>5>8y2?DSG!a?kxMFs-Jrkpmu87$Bory`=+Q(G=Dj)1T4@Au@QDArK6*MM{fGWw;b6rn4)r) zQU?rbadrU+!(d5TXO%SWmj&Pn@rvfgGblKUl7>CYziZeluhdXzd2(+nO8Ub;?4zys z{WF`w_`a7HOT?CaidQ7PZQ26<+qKq0yE&gYf7mmsg|S%W?X&S2p8G%I3xjL+@@E8P ztXDJKY8koldv(~ej1zyQ+C9y>v6wE13>v}P3B!)eL)ev#5C6gYIinDu4arIKNuTb1 zE&~kO3Aue~+)jNg9PxIARG6)l&qAuq;PqfK8QSoUG&hG>gNQ`m*SCRE}WCABBfMl^oQ1fpR*8ljN&io}W3wd-&5Vi4vcoyCU& zx4ixSy2|vZ>t+q6d1{kqIeNfxlTHpYC9OmepU`hWM@P6VlfS=l@b8NgZoZX*CcQ`o zU39R5L=C0|$Y}4LzfQDeVs&b%Gt%gAtq1RmZTh$ihzxo2#yz(YVg~Dpb@mK& z-1Ek43w!C;xNjj+u&T34xU&Na0>u0Ot{KD zAk;3JN7VDJ1oOrC2wYMUeF0kWJnL^u1)fD>hHhhkv^}M%5v%q6EaTU%kC&!&VTHW? z(`mhd@Hq>&d5(!{j z`&HTw8fVt?lZ3!k{sTZ07ERFoULy~jP4J3m%8Y|I5wXQWG1cl`ZoVmu5 z``Xa7H6$bfWhL6@^WoTW+!(t?#7zlw@-f5@X!UP!O!!B^SVjBr(p=$JR_W^W2Pazo zJsQ?;c-Mk?8ys7QHSx;2H>|{5#)bnFcHI{=8FDPR#{~Y+-&ZRp!qkWobxGf(@GSGs z;sH!rN|!pP1*CiOpd+bcr~P^irzzFA&OUs&;c6 z6{b_V4mXZ5f20P2@?rY(isfcX9~f&P+aT>E)dzio7`RjPo@N|8h7-h;z|9`-Krgb- zEJX7__+j_Vmjr4xOZR`RO@YEuZM8ylt=${W$I&nzjoojhm{K;hope+0h#}tft|-?d zRYoBE%dNv1kyqau^ak*O?FBk<&^VDzQR$lMSPXs!I>%1vF_iO$e4c0i!Tm$4{7w$i zPgHnpk|~V2fHz&EBM%W+z3;PpF3jb0+{36(NrUNLWQv(~C?vBfp zvd9Kg>W&JKl5EWP-~ZEcwzc1U=_+h`glCndi&NZ-QWx6Z z1>Z?Sbr+Z(7=AsnRzVvwee=BV@y0X!W?+M=9mfFa1zl9R z5TI3#Pm6J546b>%a%;^6R<55cbyf6qz~a=g=Hn~0B;9|HD1pMTHg_SlR*$&Mw&=(B z(cG|LL5*eYIc4QOM1BhaonR!H^qK<+YHr=#s!agV>oO-~N#`rvcIQp4l^%xNY)&12JF7ycv$^U^BKapwbLIoa}RJ8Js+u99*~ z2yfx*zjIRjkDfOjB<0C?T+oV2#_|+E5;pk-9k zLJx~W(PAVA!Vd#csm4SWqE%a${^@GtPW!7US_foNbo?Y;ez36EXAj`1dvS-@B%d5b zuCNs1=3gU%{c1SPo85AFY`gN9r`&c-x3L}j)G~UBb|NXe=NSjCuao1avD66nZ-E#G z6`NfkH1O~IX&ql5_0dVe9x8`AFC)zA4J3Ppnf3o}^Bejd%bUZi7{-`t15`&Fi0Cc_ zt+s)|Mht0|9wry<`A0QOK168Qym3;mzGYN4Z9XRid<92}75f;a6qNfsy`Z?mA|2gV z%QQ?Po6%tR3drt+K;4j*y&wRfl?Y9r(sFb3m+%$+HYWP-dqCMx22ly)rl+xF$$%K>bO|%6{gSZa-T0UuyvJ=_ z+;_gQCsda?fxjE_=tg4mm*S6tvj(AlPwzQ+n>+2XwS2s&MQB{^_{T(4c|YEbd{*^F z5)89Kc0`Yc-O?B)I3wuA%l2VHqus_~%WZ{r&0$0j>Mc`^4nO{6eW8R4Tv>Q4UfMdC zkotBE7AxOWyy1EPmwdSXxR=K}$lNg4HGSr6gTZ!4HX~O-ghQI~1H^c;38cZ<=N}ACy3xMYa3r@~1(<+J9QN@6im!-q zDDrpzY%)pe)p6+%6Fx}e=^6rQ9U(&xUbhQX@j!Cu@37m5MZf3KVdO?>)2m+vfKN-JgZffkjC@S_U3&z)dWJh0htv9c#ST<^3p) zXV3;ABB%)wh%K16uI1OgM_7>8LeHl$L2u;qY}xN2d$$KbDY z16_JDaj?6^2r|v%4b^2J9#6vo=n1Zu|3Z1?>4oiRQNo^12coN2OBxI2(4&;$FMx7e z*R?NAbUPQBtMwfV#ZKK&VurkI)EBLw_5b9I%b{N@7ed+j49jC_O(@5YC8K0fTt!?6 z$7Z@VL05LCuZ;IRp^;1aWHeow2#)0|!}Qt9y#0cbsbpT~SLxnoR1*+JZT=W?&HL)| zZvj{D0(8*s-YH>n%v>5~#@t1InYFwKx9z&}1LvfbhIM z;0V#EGg6gK8;I0RZqGNXU1-~dpqKo?YKhm zJvL!R3q=%Jz(6Leu5_Z-)EQ5>IRU3``>pQ2fa%klpX=L9S83J9Y-! zpLebT+x3vZjyf9>kr&}Vb;GEpguIHVzq2s^>Q<{>p=TsfP3;?LxsuV5jj zgPF-}K?x(!Ez)jd*RT7k)l52|Qyjg<)fP}}qQa4L593Ito!=Ra)QQj7Z2ITkOLU>9 zs6RN&6sOLwv*6W!S#2Nel=KKxm(8W^40*pE#uUGbK8%OO1nKy^h4~{al%yP2Zkj%p zsidlegzj^bXDWfdrABUKj=;ob=u0wAtn|p8+Q`LK8IBcMr*62@9pR`RdLKh;5&Vgl zzD+moQ*3H~XEDvav7fYT6LJack;=VP1L>bot6Rm3UsB>^?cMWFSrGbaz|n9*D#Q1S z8<||;rU1!zD{ACb4&D@_h@5{PUd%X-e;erty`$or;Y#s*gE-)*nGK=bYjfVGVc^?% zklUM{y{yPI6l{^$o}B$Xsr@wX%{|rgc*6yv^%s@sCRon(G;?>iGvAHfAkJ8GBty=c z>oAW2{Qd;1gjFA^X0wma+J8N9fqZ`AzTAW} zH)574#Ayl*TJBbd_c2t6j@ndM6luKrt$rm22aOu{UAP`@KdgH{Gf*fDCwWpK`aP)v zN8gZhMRw{=Vd*Ts*Do|q-T?f1D#_=JVzqhQHvhIsbAd(h?{g8B?b;7HMD$mDf4EgJXB>h3 zNaJHv5mK2|7trL8{QObUW!K#{I2#j6DjgRZ3O|&NL14+=BheGiz!B!)mh(>+?bMxf`pWItOokZKsq5yGm-%!Zo-`ErD`$n7{yqW` zmq3rUy}!B_)+pYQEOZSVhocEQj?P8Hc9r=YfJoqK|K*{%A#&o-cDQrJ^rB57~BRwzsFUXo!Y<1r@Dt_(DK!(YyJfMxX1n%WDomtFMJRhm5*&!uzbLG|KS{klvy zNE>#;suGeCJ>&gcXpwGR`g{&Hl2ms0wx-)%O}w4OA6r>LHg%4HSh;!Vot-NuppNore4Ocov~)}6JZ@X%M5iD1a;#)x*&AE*cQ?x#_zQJ zRAoP%tUb1k*GRqX$L^m3MqiPk6SO*OuTb3za2aMWy*yl{OaP2jM%G_PyYN=I0s8yS z@ZIaI++5SL&z>PNoY=Sgr%#Q_3piQ6()D~EF0OJF4sDKosnDWVfLwQvk!)6;VQPD= z&Gd;lbs*gH@Lp@V?-N3UKYJ*q^j;lYX!L}N70Pwgq6(9%iZyWiuP|W5nhyE5f?NVl z(tg=7s8g|X>AFAUrW$3^R)~752|X18DKcw^{ z_cX`foS8$^MXct2cRemR8&n0N(+ z?5-d*m|284-^gpy&#FSSsy%NOBAiPNCtc-b%gH+I<41XdFFSdZps*saLzA1TU!{&W*e_Ch-Q#@Jfn_g#?ece(vS}>VKGQxwIq$_5YvCpG`m3);>o51~ zRz(6mY-1IRW+Hrv_@E23NtbGZP-5;d-#31GDij-d;+=S=DrxNJY#DWloxeWN<{TSu zEt>_5yt>P`sA1iWmN6s-OQxs2k5HM=sSdI=08YI(vzfHHEY_Y7y_dOBy1o%C)oAeh7eK9rOp6ZQIuX| zYxUe7H`!9Ci)fi*SF8>m^X~{8lzxFwo6dw)cf9Ak01Ygi#d?)p5nT}_OL{w8C@;F$8PFT9OdbebHTTycif7$pfUD!ZCDhZ%|!Ka5F@nGH)!=a{&nxgA=rh1 z>Q+fT;OaP^1Ne_nkTxA47KBK=G076>0secHUUKjA*Q1J5>Q*|>_f6D2Kx>(Oz+s?Jha1r2*^ z2GgO=;)BI2NC$VzXHB>2+9SY~UXUq$A5W*JBP2>?r| z-MxslMK|VO7PT6Y>8u&?(5|WaYL@1#D_^q3;af3h`0c34Ua49DM;|~tb<#g~IrG_>()K<7KBn#By9(_{4c)^GN8(}Rota}fYK zuqiP;H$7#HUPOc7|0z%+q4azfcov^v$RIjjJsBs8Rq6UF-Qt5CzQE2B)Npq4zfXi|(Wo)hQh! z!D`ch>%{1o8yD!>@={DTW(mq%EBMJf!GUN^7KiWoSYsWw??`#a=2w*J^%BYyJU&BB z3MB(bMO5B-KO8j#YZg6rB&3|W1~9&GO-O|7mYMDsD`D52-ukEj9A?ldMK*lP5jj)*#p45MSvu4V|SL>j4k;3}P zewgqS8CR@wew4WJttlLtC%Av;Bg?%|lk4+94-CKeja-ZTiZxLzC$L)%SEV;rRKd3g3ybBgiTh`}Jfa;u9;hR#fD`qkEd(I(O$E3wba>n#$_P~+t{gyvk zCKD(~3|XU~$+~9hsT;()W*V!NX%VI4p%_KaQ$t{hutFeCrnk=eM`PZ!^pUJAv)(1} z9VF(ekvi+6ve3@eo3**;d~E!w`)b$+ zM`DHOBBk>{E2EKQI7}+#EwLnKs>bAxmyK{?0f30JMPRXx3W9Lc2)9RA?jP-$7`(_;`GSy z(B*lbj_5X$S-@O*6y(urv~GcqO+cuGt2mqN1@QSiQLE#Q-d`IYW-Sfvtyvk_%!f_kz$fnx)J62%P+-7) zHm|VN_^HgKx|FBP`A5BT%(NfIdNIbQD23Nj)_OY7Nw}T=e+otsuiEOYt$*g9g#QIP zBC1F=)PYqt!?jWPrg$d0_hsCnJ|CEF-)dL}C7BXs_@2R0B;Zq= zLsAl;!jT~Cay5GPPf<9H6N|d*HrB~|IF5} zE%U>&g5t{Pq+<%cR+kSO7Ilx$i&~~aas2wagHzR1`t)Bw3U%sk6W8u;dcU-+DKMAICfw=c0 zui>)VHB49UN7wq?Hh?$?ZKG0M4d5x?E@G$n{e2X7hSB zpCd6M9#1gIuGa10v_d-F(AOz#i-1$({Z^>=S=x#r z0z=ge{10ORjK@vD%n5l#a0TQ=+lhBMh#uC)J)E@Z0ld&sM?Vtik5nmJ`y?Yz7RcwU zN0~r2R%HoBa+V;1Rnn?k@1TchA3S&we6oCAAXscmGn*o?HmmbJm^U-v!Mhw+c@=M4 zpKqzw8ZNS5WDdyUY!B;P57JLovEH9+60_@OeShXJ+aq~~@X>qih^`c03AD;*AA_z~ zlsT=kf%o|J5mmL@#SO$3A729H9N20LZM|aTEZvhBo)WxDhq191&c)q*-EsB>>i?RA zc!lCrENv{`0g`t(Aj!?7o~_{qG|mFN7hP=2NhciI7wS^hWPjGPOY`VZ=LdmPbW20 ziaGInCzScahg^l`FViKk7A!!A$+|VL3)4WEXBXkXBqz9Bc<^o}RnLE^eaKx)x}40m ztEocHZMNEle(nME>GKqjhtOp{+<3G;q5hPViq7hJI5uKrM_C-hb@$QU?D^TLc74XVb<;= z^0v`rm9))^rrLN}Qxo(F2613niuB4X_yx8bK34VpbGPyZzVP||QL)EPAj~E66voHs z)UQzze*jSzay`EK{>P6a6W)on^Ky6VbyzY`F{^T$XYyqbGB6YcDHKFmum>Q>SM7a3 z+hXHB;dc1+P&+EMA*XR`Bi-Kme6S|~)!Xxn(x5N^r1`tB6`UF2`bBZVUo+`8a#=hP3f z3b-g@xDaE6EfYoC)+NbAa4dvQH0}mimmg zb_q067wfOoMt8*g2B3Hb*gIF_TyI218yC>typ}Ect_Q`@?U@D`Zy%!tGk?NSaPpD}uvzMpX>wy0=aIQoq2 zIOpLst-OB*H`=EoyUPNM)2WZW<1fpB{O0v<8Qf)jP(T|!p2KRqRW}~h;5LWED-cOU%Ukw zRG2gUot*80wms_e&X-f~jxHDn*ZMkVV}C7QRJrA0Be&W+McBrkvo)uHRlQgj)BA4^VR!&T$Y6QJiAFVTVv!BC?JhA zLY{mIxGfA|1wNe2U8f+wG^~imfKkBSQ?OLxxS8+FA-}tMW@C+YH&64Lr!rH|=&?$aF&Yg#kJ1hq?*|G`NJ^y+H2Gm|~Q~b!Q zFD(q)+FoeY`Ic^@A3WJ`btMHhkg-WI5}Jmg@G7>?wBCr=+I(_|7r%ndo~YK3DSV$XTcUQz{1itYlRkDD8Db zz?HVlbw3{7odS|Yqeu&TOf6!*&8YVK_mt~GIZRrebtr~vdd|Ob^T_z;XrWMRy}V=- zbn{V4j7qov8qa#`?)IX1mrNMd9?wa1G_VPe?KGbW%YF;h9#S98Ui6#STv>GchAz7w zeG9BOmU4KD?MZ&?+3fRA603ML2fvaUn1D7mYzD51IjE|v-OX#X?4i_B>4an*mQ6Hk zB8lAw9_1-$@xWpIt?3Y{pF1TyLw+=3M3aF_VRbPrd=i*t^Z6iEFQK6ZO{(ar0cpNq1j+?>ufkyFuOp%ecDr zog#}lA*hg$7g-x?I~3TET$KX5OaqTp?@7vW!Jf} zx)PMoV>*4)JEYVj#a1cFb}1g&W;UEm{$xZb%YIny!^r;}Op~_&PnE1?qQ%kh|DlXZ z(vGFmNj_4vyioRO9uF7leWdDz|Gyq0JX*l*%S(aSaiN<|>+C$*u>}^c-R2ve|Ei&; z+BEMVG zgrU$JjSc;4#!`B#>M;M3jbInT%k=trFQ%3}|p<4fbrV4>-cK z6pQ}zlIlq|KI8>y48cC~Nt7x!+nQPhmO&KFJi@CBWY9d8B8F)EXB7f&NLAJpPZot( zlvalI0od8K8aMj}RgLb(Inqyd2$9~mJ1~nOHKWg)k#3qs`T{w!73@LM#<_R>gwM4^ z$ugMG{$l}zR2>14#QG-y3s>Kz->niK<1Hbk+5Lez_d$PI!TM{H+gNqcPKnU=P;?DO zs2jv+9&6`SUx0fDz=Z<`_5bjyVTOQ4#&>rF5+%LS**WY8|M`yxX% zfnOVBN;|)g1ty1-A^bE%$aM>#0S5V-IMz*_dzoQO{%Q*7AlD7WC>b!?xeS|0d-_;8 zMz7561K$tp0)Zg6r(e&7Zl5{soc`}WqX2j#^mH}yz(!eWOlRjqniTU7fgeFHpXoDZ z$4$$+ZASW7cz^w$&KXX$h{I540w@RG*?!Jq({`%8T`ls3jED^gXe%Ji!Pnb2ALM9Y z4-Tv*Fw5~B$6!5Dpru$+nD!xObPCrjlck%E)!0KdsG%Ok0 z%Wf`zHJH8x;L!5yh(=L9eYw7=E$TH_Z548SK=N+U1y-&uZ@;!avr&@3ftmALwH1ih zcr#b(S7RG)Yl%!WG3&ZRGnXKlo-`)c_zH*uApF7uch^a4b@+PZY#1kM`kdDiO#lRx_M{WOM{hYxqZxGn+U(E9o( zE^XdgX9K$%;42QfpIcFuS{6=LD+X_yUJU~9l5bj~@no-t+JpfQdN|^|Q{mXzFY>dp z)7?|GF>sBeQMG6R$bKPt%Ssa>3b-f2vLQbkamH-;n>zje1 zH${nSqj8UY=ed0g)EA)h)=lxSbJK^rM({M&kN+2;HHilp{7#SFeK#Oex3@70G?C+u zv?-5p^+|seI@=*xwQu8>a7vmR0_dWpnq>TA@Fpn3|WPH@s zW*|ED&y91F;76mL|39|QGOEh_{rU=mG#ok(-GWGWgEUB|NH<6$NOvRMA>G|bcZYOy z=mwDveeR<(^ZUPeUd>vpH8UK}9oKhXdw;gL{2IS(wq518eJ@BdzI#du3TB}TY;VIx zx^;kb2ev%zBUsf3*RpckdkSJzi@o|M;L562=5>4eycNlncu`=JR+H78qMYgd z>n`6eG`_Oq*XR0mlO){&XTv^#U#|VS_RFaF_oqBq#zr81dx#HVrWg7V`YwJ`F@7^z z^(fHq!wYcVvl zbo(GAh{$g*D66LKdDl0ih#IBbr%hWWMbB)jTVpP+AApV%hBE})((G(C z{~$T@*2PS~z0aGDS#kF55nyN+U84+$9KyS4r#&rZJ*5Plgc=Hu!}keXr%ZSh+ya+x zDfZ1~2zCg9;rDEf7nYN~Y!Sl7FZh6E7m|H@Nl{ouyf?Jj${};o1z~n_r3;{5zwJvg zAh`6(+nT?61XAy+e3zR0r9jn>Q4`(n*P(h(;MAl1LuCK9%g22nXdOy}r0&Q?i{64J zh3iwHG$>w1oOfBcg^d`6(!tJjLO}Vqrz#v?3@$?C&n36+tG`}iA4dtL|&o%69LGN&5?EzdDkC}!~+8z{;SX0A8<(@76y=bVUL{30HfeWbd_$- zEA31hS1k7LWz#x15y4Q*Vii#{lu#nHu+U?cI`Qt; zK^5pdyyuBN^J*Hp`PP*vTf&$9is;9c$>K6IT|J9UfaN%xp`u9?z!FUfMw#vVWm6xy zs_^p7iU{D|A0BVGLaJiTegfvRk`~r+<%$#4z(>4OEJuEVf-VeAD)fQGp~fyH4!$3~ zShFJ8k9NtZvnF7E#9Cu|`FVMi#$Hrl46qR<}3$}&wo;w2t%zs3vl zG^gBSw&sB^M1QLvOJ`K(z{>}o*B}axB7M6PKaSSSPWrSjSxSex<0wB=Ty>wT9s#9m zEHU;_9E#iXN1>SB)B)RW&M=4aM01wf!-~zwpRpP{Mw@#STp{f4U+nhMZ0JjhN+ZO97mNDqV1cRD5p3 z5kP9s+ld3B+6#6cuUSUwGx&*4u}*_AK1uJ+K8UA_x3#kx7rvFomY?^3IWLn3>m=aD zmUEYTr1@sNI73^A+Av|17DOw#Y}U+C@4qekN;2X1+#Zsy|!cQ7gV@X^57GiAVd}4o|6nCp3kYLIQV(CXw^OckvdKd84>sDpXQ3&%jIW4ux3a$7GRddr4 zKT_0e*WXY~C*#|%YhvTk5<$duYKVp8vj+>;?OD~1lP>&sWU`5jQws400h9X+4vDg5_k9sg&p zc`pLYHG^T3saik~n}I8!@jtwyW(?HueNf5)i~1?f6DRH^OATYKxQ-sCJ6!icz>pMPSTdT@Syv1(FUabHrV?%m_C)34Op!d z_fK~FS2B;PUr|fP7cZ9uf5-~>J}HGSe{j5g=?xg->gkh6E@54+2d$O-1t30`FjqVVN8xg;0IP5&M`m;=6?WY1b4vTe2c zrwBE7r&a#8Mj)R9R&tbc*;>V?;~#X3uuo^+9@PMlSw{Z|zlH4k?Ku;Q+lXp8?kz2l zCMeTpC^(_Wkflern^hB8hGRl-QXN&^(IvhG7#EdQ+&jZiNQrsbIlPRjJ~%g1q8W{k9{WI-F<;N{5eJ1wDp(z%I2CtR z!#2<#Ul~JE;Y)Dj4At=ocz>2mX`%|2rq_f`*-C#Jjc5v(VHo9z6!Pq2UjqUu3FHRQB&zm8faCxa>WdhV`Z7;T>X zXx#Pbd%kSXY+KS)v~LA?Yx;0jJ^CQY+l^jOufuKEv!_F>m@NG7lQzvjpq_k{ru*{n%_{@P5veoa5Ht}xn3nkfTLm>0) zAp|8YyB3y%FUN%&+5Dx!d!kEA=8A;+Hblwmv3lJFMPzU+unstiMt(4qzx`$ZrT90* z8EDMn4HWZMxw+){bHE)Xeo@c|%BCz}3vkBnn^laCj7kYmh!}i{RLI||v_|bG9Pgjk z!_=?CES+sX{x->|$8!+tYh2#HCjY2fe)^OcrJ$Lp6b$1NdSJ-oUP9o1*Ux9US{) z?@-w5Y3I=gzcJw;XnyNO?POFwKgdxbiSC;GW)m z;;T}(OK3zqYx-BC%K{dE38dw|F7tBUong4wJ7XQrdduVql%j%u88^1Sc5P+s*{wf~ zHHJ)xFi9wM1h`ZS?Ln}{GA8f$p||;9c(tP)iM>aO%BXlbL7j6EZL`q!nC99DD%Xml z)yriuVA=zoL&_~2D|-4Su0QC0>Zb!LXWsw>5CtdYb*UVc&t3#D zbv;l2#76u(>zGSfVg1rEOb5>4!9*%%ceT}@m&bp39t=GkrV1~Q={U+w$X~*|##eEy z=lzkw_Y!gw?ktQ;LHyUd6G}z={ZNA0=5y&v>K>-XV7?BXW3hQ?I2SO21+5Sod6$pZK`Ul z?)!Fykc2)n4N7|FF9O$eA3h)CFOdiA0_ZNjojMhSjsXhGdQo=|5nJj&#GM;^QEqDP zg5hDcV1)##altv&O6(6S=$?EJ1^0^TGJDWphB3(KevToZgL3$LARRn($eN5-iSh1M zwfcLS-=%<=#ssB=3%jTND`)`c`&WN{sEw+(^*1yM-FiBLp!eV?Lh_|*9}tjhc6D_9 z?_cqihN5=;B*I5&=)5j-NoNoF^U&nVH#SBT_XaJZqBjPyw*dPBGq=@Git#B#+Vro} z!H{ou|3_UbMgX)tvWf=az>5o8p<)4TzBH?SSxuz zkoD-uHbjL{oYfbk$vyo%uSkFYAnn-<1M*8`>RGxU#OEb+DFG_pNRw*t+l#kgi#)fTt4Uh2sMxrBi;fWFAE-V z%>7Xox+MVXON#e@bTF*G>4Z@40*>0X&qve^1sW;42nti&rk@$O&8pExxp(fX|C8E) zgyeAu!nSdUA87FwAR@1Zp@wGt0ab*gfyS6A??vkT_hqEcdliR(>sjj&C?A{@nsFj1 zu&4zBl00KE1wZrZOc#U-VS6}dJP5ofTDJb<=#HAa=ywr+hR&(XJC_{E4#zl}D9$Hk zP3`AL>EFv2g)&@9^(|+dh?g#hSXp>ce#XQfm2)zMF6VdbtM-N{&#&XA67SNUE|NMJ z#|0m(^EZN|e?rS6I8KtKQ1>K!Y`gs6I6!SHJcFK)FI&*X@6ti`odk~NrL5)A)Bi4* zd5nKyzG(Gd3zqw&HpC~W6+YG*5#%%jGeS6!lIu~&kqdqMKso1I>>|au?WB+SEvgk@ zSEQq%AT|~admYH?5#FVHef@hR0%Fxf82EgWy}}Cs?~DDrNc&)+;1#`2#*+uIT25K{ zM;3E7PsRjmlX_zFG38qsE(hI@X27;30G9~e(rdBV*^&PW;!H82^xhzA>}fX@KNGRc zo)L$CsXJib14(@eUW#e-@Ofwl#XnP{`U@C;s!~ohrxs^@GL8oTM=`1TcKyFUJU7gA z05rmy?W8Z!7yMTf8#c4N04z`C0alQ9UxdM7Y z7(lgC{*s+xT1WH|CpNk{acc?CVk&Ho*_?T1uhSFJ=kEc7Zlzu5a@MOOjgEUY`}>1J zJODbY*{3VU$1?yGQEWN@c2Q{o+tX47c@_}PwyBSVGZpvEW`UpQyvO^i6Z#VVL%JxQ zZ5c?q0RmKg-+)~liDcdy89aVO;Lu<(Wa*>?jNjxu=^1B*Q6<2M6?SuB*fZ#QYj_x0&Yn5P+ zLS3-nSI6C$ZCDjCSZXgU7Zz~^5b4x-)R-4Uv%LY+y`|*S#mF0ty@PK)SWWFQ|(z5|pB05+m<)9B3%58eF3S$F;)9cKj;3SfGoeI1RV4Wqy-h$}x( z2$1#a6I;tMseRo2Z4_IKTD87IddR!0#c`M^5ca1w5IXgdNTscvzH*CYv%n=63BS6) zIK_LT@&`hpjT*?$rcb8h&(OgTjM{J09ZRI&hFv7_OQo;mEetaUKhA%s^#|I6+df()M9Zk$>G5^Emc z*>LrgNBsUbRQ6`^N2nZgu(ADq@m#Os#{iJIhFK%5eXq4oMb1|Rg*;c_I(I{S+VIBH zfHnS8A7Bmnx_kqy_h4G}?*nm?uLFV(YZ7oOZcP5UI*Os^Bj8Vh;QRa6O^3z>+Zy;l z(N$&l^<@OxxFR|K{`v&}PdR5njMm)gU5FP@ZiX{J{3%@m%^b=0+^NNB9|}Xhh?;Z@ zfY&K?zHT5vivuhV?}AmVSLw2x^0-Cui8%oFZjcw6fqM{-T^Xa{kAD>{ozARoX5f(zNuP_KWF1+aFFDp!F`!A)6pc^TddD$Ck|C zB4p?O&hpoWlQS;qNa?T<3hO~}c6P;YmdOX)0V2I~RzZ#a&%jQ!-~_s0X&5jdOA=Cd z4S52Q)dFttO05ftKOi*uX1HaTm= zO}#A-rM|RS6LZFZcw3)D&)LxL!-F`fxP%JqPWU z0Ly`HxU&5Rum#1Z>f8~|e>M}oAE8-CObt{QwO-!1c{E=-Owd|Y14dRsg%ueYI%P^wcbI)yJiedze zOd^MhV5l&anvJWmiPwzy`m&!EFE8RKTJY`qBEbya-XlVp-( zw2fdOZ(`3cW{vUy0fSh2I^&JaD41qu!Pu*GzXzu*T1bJ+y;CH)Sohy}Cjv$^ntk9B zB$)KW@PVxKDdSGIp3H?IJ^D{&O!yJnERUK;fMTjEliV6lP7G*Jkrifozo=4b;?b)4 z{|^WQx{ql2z*{tTEfpX#lB7@o%<3YT+_G+^4VANn`y1w3ta6IOX>RqIaM||rywWpy z+V38EA#~I?U$-^0gXe8xv9(D6ytsCvxLnDTjmYJvy7JgBIi*xza^nr+mD96g9Y@Y1 zPPAoe3Jehx^l98!XF1PdbsDC;>;Y1EtL#+UlmneCxX$u7WfZsQ&E9*~(fo&{eqezj zd_+ZDR&MziAEkRVdPg7bm=>y%ZSq>HrdM4p%YJ#0tcQ0AemHhevc8h)(K4-1dggj4 zTDLwjP*n@l=KE0>OOC(l$(tU5&*hzn>BSu>+&p37+!hTb*YtOS`6zoca~byi1lnc< zsb%FiZ~h*~OkS8$WfLH^g+KyrcUu$UbYgRZ&^<~O!dnxt0udd_Q_+m^1L=@#$E#!F zguCfL;S1dFS~I!qV&CCW}lh9BdnWBoEr6Y_2k zyXiYhYj-4ebRM_3-X4anU9d3Jy9m5JGO9MhCrNAhQ0L#f5YLhuMBi&qY^Oj)k<8mW z|3|1|HUnw}KruY-9IcT5=1H*FAL0hfXj}8YKl7nHr-kN%qxJOP6U2#n0;X?e=NpHA z4nO&n zWQT~Zb|h3j30C7lKcpW7$#y8(tos{Z0}b=6K1V+q5x#QTh3bwB_wFIhw+d_iqwxgc z1lW(sEhrr?oxe=jURAwYGQ~c$8;q6|`G~67ncx7tHumdG=5$DqHCgKM{f$v+v>1aa zr#-SKxjcr}s}7dIu(EFuixfKt;7Jji@L`!x{5#-8`4A?PEs1wwl-^)k7?{*wt<2k{ z`5>!={tMp!6#~_AoWNC~E*z!>0U1^vV8q;WlK>Bp=L9H;z$@4wZv0KeW<*X*?DWU% zT&Qlwi!eDy#08gOAVV35hWeb(mF?F5ZU%*wgIDy|!hq?x4=S6RvNjty?Yqw1PeGPTO6<1V@!pV%- zDgUUo`3Q-4$xA*h<*S^*We{$>9cH-uLc(2i014?+TIL92ir(WU-YmO{o!@&D%yPTk zNz2mmg6W7*#?DSl)39$&?HC!0EHfDh&&L(5>jI_TmFd7TFkIzSh}+otY#!kzB|M-J z5QAV3k!2aC4aNAkwmN<9F=O;yFy(6SmFS0aJ}eJi`6mjon~wJK@>#!wgnU4mLWr;; z12eUZ3Z1eX>N}m!!fOd@`$$R9iAovwR*+Mf>*Haj*+(3ZYiV(7%dG>Wq|W~^CJ2AA z!l0#-N#OOYt{{oH!{c<;IyOy?ApnWv(hyz7xBbffjJXtvT+^4r8rn@mti-z}94R5d zUogRX#XjuJJI@)3&A^wxYDkQ4UG3D$O{}Rv30L@BGN1@$(~;Y%I(tqQI~Q;w0b;1) zn;fzbG}afaC0L@wb}q`i%r;k)gknz5SBcpU;b;;3f|y5BcK}g{d&%BIbT0~CDGju^ zL4uO22sWC>5Fk4p@<$hJDyLgjpn4Z_V;N;p2I1t>}e*l79@KxQ|esh9foOn)n*|=ftkNe#(tu_48KJ;TAJOpf`r8gpKBF% z$G#%;KCvi*)lLSxQ2R9x+tM#P-FhWlV1H`h5Q)00Zr}2XQ@tUaE!U(=m}nVT=j(NA zTK2n9e^w3q5XqV@6Fzuhr2TzNA21G%i4l|9cLIa_1T3U2yUSw%SBn~HHN zG1h$Xei=aFkx0`QiF_SMDOb8;R6T?the%{;9?uY^ML$K@Cr?2$YZ@v_?_Vk|Q{oH} z4bw_EDcd8F)vY{t)%$}&)vq99{t+TI9BSl|Sp;LdkCOJ4-bex=E&Dm3;2C^gp#Q7j z_4Hq!B(KnJJ&2}#fYLgeb$QFrN@KduWdJ5cLmZt?DOIjE z4AoJHAO>+2mBQRknY3*nqzKT9WpX7^I5Zs?O+X++1d7qp!3_VaO>Qb@{RJAdjKgU= z;xY3#JzP~QtTWwJ82yEl+jt>AGJrJ|hqF;nI6!zDm~-?9pc#RvsDvuNY?#XF^ig(Oa$~SQyouF&Fu$(L7-;;{aCjahs?W>M zL_ui&_56Bl+2VFC>KeuN$y#zNJpLA&Y9*=wWtqEUyL2D4KNWC=IZ&fcq6a03c)uT5xVOCS9r(wEvNnhyhg|!L zjcgE+oF$fPW8tgP-kWh1eeVnDd;Fb%X}jep>H#0Rx9RVtFLMASu);TLjo@?%BoHWv zoDMXw#0?fhlg6hWN)S4{S7KKVF+C4PQ?M?!5+zD8v{I?rGE!a#kfl6edjFyT>Z^ib zuq81l4uGiB&u;nv0wG(N>4f6drNf^Ceey@BB%E?8!IhB(rTuE?I;%m6ahxZ-W(`x> zB#xsj{+3Uprsv5lebEFxigL64;#%5w##BOu(NcQlcPMx)t^I9(3yc8sR3~vvvrAuF z+rKa1P83mn?N~K4#fP?6|NacnFe0JnBm)WIL1o_1hKdkJxtul6VX8*ol~e%M@w|`9 zVOj}YsE~3p=-Hj_wxj(27uL~pI)p7d*k^n#sYWFi%UpO`JwP(sl8qO{`*Q1E&M45p z5sx!-&%fH~su|?YlZtV|97oU5w*8x!k^FOO!N*qdT!P z0I3yW(TK6%6)OW%p|n{WIWBA3Gjpm378>`TLy>NDS#56E{@h6-Yj{kPX005PzH`{e zbFZ!9nKm4&#SO>)P#{gUc=`EEGzMLL65`Y976p*YCK;Jivt={)f>O;oU} zf_#1(;G1MABcubR+%mbHoF5b-wn6lW_+?W8e%bh&f@KFSM`!}Svn8Fo;PCd_h~iKR z!YEkM-7$f&&n?X-+I?Jy;BhTGs>wt#i5kiIkBhjwOD3H<=b;tL$ ze`ZiY3Q_OmCUSTuo<@70FdiB_g(2~sK}dW{D_hQV{_8hbM@GcVN2z7TLJogQv0x;& zH|Uvwno;p8>_}FW4*gehHf}ITU4!m!tf>%!AX0}R;~Fl-VKKISXkv1GXI)}j0d`g0 z#}+*W-#R&@3Md{;yPfN!5*UJ91BBod@EZaZbSggZoa7eN1n4Vd@B&ngDySqhl%ic+ zW558Plpa$yex_N_IPXGm*og3rbu)2>vJTu15rkaOY0HDNTK1ZMn;bU(^4e#&pJVpQ zW^WvT^pzX%Rv@3+gwy$3$f%&u*$so%(}ldfPzi=pu6WhN#sqLTi!XqCBuyD? z{gwP30$<2pSvm;7J;T`aZPSxaD1XG?!3Du-?u21Hue$(*6b(*AZki3;P&YQ^y$ycm zr0%T)&il%lic=aM_;y*n*5%rEU%c%c2bW7p$cc;R zV9{_75s?|PM9nIfEgBV@8Tv&{K2(2qNOvk44)3b|4mtXD;iFUr=z+F7j4^|~@u>vs z(oNZK+QG8AC#~wZH>iwN$)jZSJ10GyCPK|<%$EhHJbY5TbC@K!5$+)msQyGR`Zd^G z4Vx;dSQU-NaGsKtLoS`T5)BIax*tT=FcXsAat(GcUDbG|38oz`JEgY!gcJAVW&Kq2o3u>ts(qR(V-)OCXpOc@U_xFrzWSC?V~<;gPetKJON;*aiVCmAF(B5kJY|e) z{k>RIfIXrZ=3zGi9NVv99TwZ#R=2N+-CoSq6h83ZXvr)8ZVUX|YQM}y1hN^m;Y{O( zUKwypjwAfmqRTRftT;|d znh2++B|%OVVBvz>7+Zd0TZG~a(n5I6Q8$OTUe#)sa<8)=Yj+0i;IVUbNO{tyPZ(F$ zQrOBsaCbyy6dg!I1Myh{2`F_FLkh|TEwhaL9#M#n(9tJAboA7DslhGG7-n=MCIe(y zKEyBgr5>j7Wm@&0!8U9z6tQ)v1kKCU216`Lm6ug1Aq5Ryx%-<6jFp$so3P|K=MB?L< ze4fnu71+^d_XJgsLD;uJ67@{CyTiMvSx2*POX)vN=qv(}-lCePZl!>Fix2ru^+Bzt zxrd%C@nV;-PmFA*=m*~l_bq=gn%w4EECid06sgQ!x^tbOy8S+fR$ZQWCxh-p7`~eROOPA#Ud$jyMBnd?+ ze!pxZV=G41wI&S14s8u(Ow8;XH*B-Y{M=0+7Y+#WdTv45&2gkHaz+8{0r84fVAE7N z6nLvTD98KKhHpJoLCfjI7+wxc>yF*3!8x^|-;Z5!ad9vo(8tWKJ0W!NwJxGyU$f+c z+Vj`Q&G($d67F~9lekOR?F3k>l$bC-tS%AxVV>lz)up|tIY)#x_JV8j7g z^fVUhXLej{PL<5gI86WtQ25s{=10eI?l1Va%&4qK%V?Debq-qw9@sreCC`u}=`!83 z^D}=wesEk;2B%FI*Epzjd>W#iHbYX{Tj>3fl0%;u9%6TFJy%S~NAU--zCIXg&8)s? zD$b9eM4cT)#gju|>Y6If7g7-|sUK80kw&x?@R(f?&MBGp!-YJ<(x$cbAUS;v$ySBl z6MuLUY(9nY8ZmMoCWZtvV;hn)RN2s0=@L$y^w8VWxUEq4IXX}Bf~3rqQhem4y82<0 z9Yj89v5G+Y4>!CNOc<|om6S(ZXtFors9or#UT%vtmr ziL-n+^ce=wHlS_JPude{ygcaN-3}eY#Kpr;F`h%T4Yz+|Kqxvw5QwE)SH6hjw-DN{ z>r1o#ESd|lJ#4{3-1@am7<)T-)$cIhc96NpUG;U9zh^WA`$uqKs>XMF^%&7pK4uQ9 zmQyH)qquSnZ%WAPDA~aBYFJ<|l&Y{U|F|h$A)!8I;+%)-jd^OCsAzjDHa5gdon$^T zy?{xggQwKF#PE&H%vq~6XXJeF4(sVK5&asQZ8qbU%|*d0i2N0q+qbPqk}dg+MyDK< z)>&qz0dAYL9|hES&F_I$xaEoxi=Ot!ADlz9B!Uu8-B$+f;-}!5FAnCp{EwDBbhABMokQf> zD)vsAp=$_UCLEB#@AGmF4~D(Wuc+A);Q>qaPb;J==-aFT65{?w){F}cp<)F#xvMh* z>}$Wc;jQuc`dy8l(^jFKah>XYBc&V3;jy_obYD$7O#6OZeI0Ep^0e@B#NxUN^| z>(o?x6Hv^#`&x~74w$(6fKV7kt}aLL#)Los;mC+S)hMmro z*9zAsv~GusYn;=P5q@mxSLt;>w(4K-^Wi1?KPe^xgt$i_yz~oGsnpq39X^9h2OE$xJMXgxEUP3}WjyeCkt2RNs!e}* zz3p|jB2;X28<`hWStL6SGSEzhTrNm-u`n6Yc#w{Q$P0bAiWb6;m!;M?e^C7}{|mqV7e|$(TRlx|$9vx;JSjnIT6(Em{2Qp(R5y-2cls0i6QR z{&WgFZ*s-?pYu>3j*R)bETw@rme@w?(B>hk1uD+YwA|VM@0bWYK>_5(Ch5@GWN{

r3!kA*MRD3G-0~M&}6F^_N+56={RWHK#r7?qKtg`*yKMyW9@Ta zfJK78XIeTZr@-~~03IKxFgSSM{$B{oH5Xc+07HP$&5vOjNTUUL#fV)&S>i)T!M-g{ zCZ*5^#Mv=ZM6ww@UdS-Po>a()~Ep9Wgf5r4d-p!h|T;^?lz-yMZ_(wGA zd!QS#)K~+9kbq?pShiOBSz~Zy&q&whj*(&dE2PnZx|J}{n*aL+qlAGfXD~?f7|OGV z&Ub=6RGjNz8Kr!|-Qs{yp7QzN*MGliGBGp(gkTXKRy}nvp2k&)^=h&a3sEE}Ra`v> zJ=sncTZ&x;E#?Iq)mZkx8c=}{-%UtPlY?hNV;%GAsNLz{dxLK2$+jQDV#N0e4F(g4 zz?s{Z@8XnT+9f(j!8Yp@33=_54Xe=m;+{CZdzt^DF=&)PqX;V=5IJjkzQV;OVr7c9 z-m&(#haBw7#SLS`M^Trxf_T9z?pA{n1;r2M5>qu`-gU;1C&BzE?hD5&Wp89wRNvIW zrqjmbWf%Q`ShG4RUTrQmjkx$y`Yt~HlgA2_uDuClxYUd7a!7MTl;rs&=#W|1U<^ zW2Rmlp0DtP+@4Ejd;#1RmfpD0lmpG6lkla_zc>H24vliWfTv^^BgVl|D`;|I!Q_T& z>hGihyG0U!Ttw*bA~$^7AQLAZ7_RrFRK0;QL{RicVq)#Tc79U141=t?=UQR4o&3Wj}Mn`9{ z7)jJG2>lV%pbo1ZG~bH1;w_=+0ImW{-uA!#5BydH6d6h5^X?tW|KphYNHLjUJlpad zwGNtrBK0^IS;crt{nwAOfq;SmBk)PQB_DR0UU0j#uEwfB>tTNkx}}A9|Jw5=EUD1_ z#wnM|PG419Ko=HdRgC@xP+k5reY7x2uL8N4B@!@$DX<-nVC}8>X1>)Qsb2QTPzP=J z-ml2L@Jr9@@4E%!IV3iuOi7v%=jDxSNC2`yE1A|?8XVa67;@g z<4l)McHGKP29};O+<SIJVG77YdsCdMUak%)w3Wd z2WTX_QD`S#g&lPJ)gR50?6Z;1`RTW_iWBCp)SZ5~y&>R(5;Ao>UOWR4FDH!cj%SoE z7lR%te=N`(w)|LXD<^@bbpnBejO-_M`3&CrFDgk^FJqg5g{(65vof?7pSWqG=c+T7 z(mSEyhn86)Notq6CiEKqhLqY73m~;L7yq$R0Wyetgos5G5Q;@x!e)ggu1h+I?g50x z=FdJIFI1b5rZby16al%a6V#nwe`;rZ4I+IWVb@f!)TcI~A8tg@bUa-PK~l0Bh)m$nZ?pfgtfe$;KQJqkRmZb!`obLZzO^(uE*?XkP5l@Z)a*BG$c6Gzf)tAT(Y zHfGGE3sd{;W(_GH2zN#_(eyZ%W`(eRPrps|K#hx!>5Lf&gI`334EvIm?& zOZmr2IfQ*VZ^My*sFg9Foz@u?rY_h6m7UL9O*?*FRpKycZNB>8{d6VjLVZBJ*k888 z%7g2|1cGf-=VI#dGXt3owkVBe0gF;mFzJaH#hcHsxz;ZFITZj8cj_k4(mca=OI)T^ zTX3-Y=+f&;7bS4LAWA{#dFYSWLCzc!c}D$f)wkY&b}DOgQVf0eFi00k(&`-rS=bkD zSM|IBOH%A%+-uPsAGmdq;oj%3I=lXPq;H75_)G$dk){B$)eAJ)1k>8+v0hs?y8-B>cjaq9V5DM~^DuW*Q9b$t} za!z}f?%xmIkza<=%H`l&(lO_4ZXOrSveaVPPW@+4FX~JsoI=j|W!0l=7j+&0I}1kd z0YU^GW*yX2K3z~W;V`84Yex^>zQvE5MZ0}gT_>>8V~4@lybH);72hsms;)M5!q|`{ zy6ujpsGud$p-_DkuL^mFLEh=G_1e~YDkPpZnbalo;6X?-20?Mp)Dk73{|3c(n6ZdGte3JKv<(*D{&^|RP@Y%ckF+%p=Y@s6 zPN$TU0;fcuHEp3$zm41Xb3p;&AKmNNw$(Ge!#Ff$twNlakc@&yL!6%q&2HoMkB|7w z^C7y$eaq^S(so<>68QE4lyW8dlH&G&^b`Mx5Ri(64@EPd0;~)b3IuN8ZDpu=w#9>7 z>BZRHGvstM%1Fwr7iFV&tb5rX-&3Jy0lnfa>CcN(7lE$3_(r&TV=ySuHo5Ak-|D8} z^9xeDZtjcKc>-Bo?xJnWDme#0q_9MbT=iFGb;D!Ss*dk-b?nC^c4kS;*Cb*4l?0BO z!(Eq@SzW9c7{Fmrk0(XvKNp@A=iTsu9cC5!$C+m?{*^^_($%Kc^FB`K4$DN-#E;5Q zG$23<5$gvO+l838jlBYt2=RvQ!mCGmY#)M5$cKez@`Zo~6{A4=gho?v=z?ovZWON! zfkAotBa9r3aw=7*O3>4+-jkaC`*mS0Z{q3aw6IqX8K~GHeB7?XW9qA1jdv zubGmYRBodtViu!|oUn(8p9h0Q%y?6!Fokf#`OK9(nYc7UZT>dzWa*Iew& ztVSyTeCJhtK2e$}(e5KS9wgR9OA>^mU-2VpwTUC! zMOV~3N{*CF&D*h?04rstVY|F7I)hr9WKUo_Q)aD(qf*#qX1nB){@qT9V(1D^|DWFI z>Y=q(la8CXiZ$>FJtml$K1#zB@!)%-Hiu=47)uOjk?}C$PYF?kF#7a zwwE>*`9UEREOgJZMVK{YkFjIKFn31DwFWJ_pSm_4$}iLVB#MU2Be3z-OEG<3MDS$? za%k_Rg=k&yWh&#_1CEq!gXiOQW5?_sRjHAoTPjjY;WQ z4lIx5t&4#0+Hfd8`^f7m7p-69VX@jb-T*EMHvY1;UL(@0WJjD-6BoeSPU6NyS~>+I zz{&FbU?N&O?V=BsUTrGb+GgXy(tl5^@v#;9xRFo;e27!3jzg^Nl1oO#d6t*9Bh<52 z#S@HspfRbb{lY_)ep_UeEt6(maH#d*?+u%)E_GW9jvt>)w#e~<&k|SgY8CA2+@JQL zQ(0$Xl<<0rBL=^cGlj7fH)=CtV;s3ghQFyz)q9b(7jgN=8|Fn(+P34$PaEdjjPvS; zi$@L==0fx86=&&=@kD398>diKp!r&P$Kwk0b%U<#Q^TR1HMK}RZ_vt5 z>aIDGe^~&lc0H)6XPC5zj<&Si>JOI_e~uH@fa`lWWH1q_;LGy=tgK67`ifMg4P3^R zC;yI++%RHr(0BYX0&_*g2j2#s`~BI4Hr@XwVQ$JvN69?h@|3+S@Am75XkzS^$T)Gi zT9=2a2W3fa88R>>9#w-2hq8k<9n?cpyaJwV&V6Yl(ShNZf+jCVy&gJ;ax(le;+e@e zg3wy)<$A$@)mbL`E)AJ)`DK&+h|mscexpZlGtEN7Bl_=i^b!%rX?d=L>ko&qRH@Vb z#nVnPtEw_VK#TNHqv-}TyT|N?IPDb$N%<)dy1PQVNae~+n|~}_xkPc} zU?h@rhp>GgT`&>FELGeNCjE6DEMk6lmB?$8o?d`F4u4$B&nIrn&56%V0>rTmhU4j9 zu_N$kTZf=*PHu`PLp=do^$+?e0K?8SDv<6J;62%85Wo(7`SW!?f}o$|5%#@FzA-C5 z)OX8|oaz{_nLL!*7N3U_L3GMHjbr^^s$Ynq5AC+;prVlJbmho6#?P}14 zkRx{g$i_adY`ac%k+0hbrOi~7Py!RuZ5o;AyO^IJGmrQ8Ghs9^)~diBj{zUy=mboj z3s5U=rQ-2lN6F+Z`GRnt3uVTvO4Bqo0ugtHYGQmO+YkwY&4H?+0ol^3xBg7 zh<_w5EZ4Bb9wWJLj}|j5*MimJnDXlQ*r)DsS}@nLat2Xly(D``^*~T=-`Nddc8&^v zTKN;mGWa4qK@3_uHYz8;2$uC7d}a$#3=z9Q+k*iUGwVA9n5Ks!Qi}R|y&Ns63U3G4 zX4)TrOJ8+yTxu)w21>LE^>Z&zNmaKiYtwP2RBv}WJX3Z_XrqvvWc?K_+hWnFxz@1iKtRibx4D zU6t)z#$pRd+}y5WXm!T~L?-Ppp`<#4MfX>flyEQjb?+gBqgCAPYHR3K?+#$>(Gv;J zf;GPX-r=J%#NsPBNd0j5=b6GRs!z7CU|gfLEA6YLCDCU<7yMkMh%r1qizlXWL6l~w z28B~z)l!Egw5lk_n!&jk_K0S*wE7?0M`^&1t&#e@i=Ya*96IE*p$mUO@Aszj+{4SC zpT{x46VV%e2CU)8Mnbd57o^#wZ}bZ|c9o1Y-ge>RDrr=wsh{B1O_94G3;i{9br9Rlaz1@-VOCBHhc(m zh;RI1z4-!(6O39@`5@CHc@V2`r=3`&^#G+t4jT+>h zttARe*St}JO(Z_-A9)fFnEYyRT#i42FPdH9Mp0D+-L&u6Y)&31hZQ3``tI%UXyFzU|=DUpsDDq&z`W7Lvl0WFQ5a@wzB(34T_p!($aisE~PM3|hu)@$s{aF!`_$?&@K{t1q{50jmX zRR&@_y$W@vO$53`_nNJ))eUb|;jAsPLY_^*0;k9Kct{S!mE6c4)|Q*bmY(;q84Rdb z1jt6Q#C#?^AewW1IsOjr=+CbUBPnHxwB+$pW;p!1ZG)vkkNu_^jNJ+`UMjLk%uB^; zjQrvumoe$!v&X;GR=}gE9YBaE%hQYiMi8^9z$WEGK7;qib)j(qTkgtx&Z$gPw-?JbX7Rs^ z0Z*Q4UMg~|hb;3720n(QF|+?D z<_iNy7o`Y74ohZq=i10fMCa?{Mcvu^MQeXjpQr_2@ouIO#p9JOsi?rhK${-Satr)f zp#7?HX!7y;FPInqBLYykxzcX${l5|yfMP^8RN}vX=SlZJ!H?Mo;S%tLND7ZCFNK?} zf;}${SJn#tRT`LAK>MkbFmHL}Qft_9pHF{0>||#Ld+w`~mL2u93|(s*F-Nu_nkNlb zWH!;cM>)^IBZA)lr0NR!Q`L8gmLVpLpAo!dQ7_ohc-+jsbe7yz% za6thGfpXE%o#}G9)TXQwIN8|_ zL*}IPCEu}$flo?GE0mEwn)knjEnESbxWMtNfUBW$xLfB%FsGwu)kBa_Kfl#nSg7o2LXTBAa1FgV z*gCVm^bAxA$}KdZmQSySuVSWk4#dCZ>%m_BFCr-tCfwmDe%#!CU0-SO2bzRmzj+*E z5*1%PvIDf7D|fLryckf&=?bsh8D9pqj?_<;@y`zcsx{GVDd7}Otiiu_V6ZPFN_d`v zV)aGrNh`qdoA=23h&HzTeq74a8r!8h=|%m;JEI1d6K!gFBjmGm z9>-MHN@#EZG9^hmyFB#Tz>3+8%KD%U7@*1*?IuND2C_}?t02y)muY58e}lr1b`ymy zzI4^X7%Yc?D7!~f*m|Ju&9#Y(IiQ9N7to+cq0X;Wi{7`ciXWSIaH+HWN&7B7E@)i> z)j=iG@JTY>D0^`YkaxflQ&zh_^Yv=5c)UR8V9nLApyy>25)~8>D;aRuCivy?c!3`To{j z_pW>YQy0iO%sKn){rbFPQC$L3(BsVNDP3V0Mr)wZnn=SBkpO8ld8>G3LmG;-R*N^3 zu5LLmgcpVaAZKJmWw?3?4pt&0u* z7y7lWrAd9U?3g4TW&udMO}|4!EV3}OUlDZ#>xu)^!M@S)1tl2mpq!0}O+78Y@s;Wm z!VD5`T_iMY?d&DDr~;dA;bC$4WLt!l1c*~QCHK}qw&4m`^^?6TYXMP8C7g@WLzs}n z9|r7_w}AkI9(BIf)o5rpgEo(-_}s9;%}NX6YR}MR;YC4QN!hdDt(@R@)J4(Zvz1OU zBS-MVn&y@x){2upY{o}s;(gH4Gn*&Z+OoUn`hc)!N zKr3(8!%T#CeW;eG`_V_X*zAM>z|`1Yt!ll!jQfoR(H~(QJQcJXXK~sB30i#LD@bSk zwR$@(=udlXvRwHS_>AWsnw%llV;}GpKQ1~0VTd<{`t}q#U?NgqS(kj`5WRBC6Q3N-g3)q~Xcv{O%y+1#?02x)> zBL^Q;ue!hvY~P}%2lO$2$m9iigTOWBG?yT*8W`2S;|0+jzSB5KES<0lEMYZ~_&r<^ zo*LD!aaB8SA;Ri!pmm1%MOg)`f?8Ov0V|2SZy$92Q^N}(3cuyLNm(Z_X-|77wwJ*CDwcDM!in5Q*b`b( zcHZm7X4pJ%)HN+Wa_9Cm%WKUQy+^#k%<2p+jozvaM|_W)jc8kt2>LTIqASRGf!OY4 zhs-aZ@+dJXTBTw_%>`)Sx+`j*sUG|ceJB~+eDca;Ji4z~Z$gi1fm0aP&zT3Wu;{^f zVqK_OKtepWfmfPCD*wzwAp-N1M<%<}c~SJz2lPJGycQ=jo5hY{` z-F@QO`|d6~mX)P&hHipxzY(3vhmvQWhphS(J}4?nHQ&T_Tv;08Xdh7y-*ap@I({a% zPsFwWUQ`e4eyn|W+gd+i@G-sK*!B6n1&?^iFscE0M=yq*lONPzB{%*KyM0NB_@?{T z@MP1CsRey!MlZNq0-s)jS#pp}GNlN;oTmj@<$(Nr42L{pr2+i$1s%<>4E5@lJ`++EuG$P~!gJZS)4(k9xvb zS{j34T_p*{^Jis(!6y9kl)#F3a(u@0{ebCMPfz8Si1ndm;Ojb9Fud6suB3M{5*`rjbXOc4(Y!_g|=_!4ShPScLTjv z;+x_!nrmv;1Ww_5Qzf`OZw^4Ho=&bJgrXLsk#zi@uou7l;0$4{nc*RpihF;n%fWo# zREbf5BSt%zEc=JRe>?k8A>@p3MzDQq!DMc8J124NtoC0Q7UH$+h&%KLz-0}*APu}8 zu$S0t=}eTzHTLo21%?FBMqf*sL|j2^;e)uaa=Dx6y&o4M3%vX;Z5QE*NM zR@ylaPp#?tSVDbXlOx(u<59(_A_WXMv~{#9<3bAj91v;fXo(2*fLwSF+$f z!&<6ftp|5RTW#9WCkK;?n=dEJ3P1frOcl$8fik;2mZSX@Jv}AKf$3dcyE8%cqm>UU z4{Bo)jJHm7@}um-+?1M<;@%8~2^E)?5IR)xl0EL2wC6Nzs9A<<>xqBy z-QEe9j@3Zf%>w4vv4Kiie`VWSPM5;8N(x-V}ofvGji+dk~>)v4$nu19lbn;eDLz- zongb-V^v%-!gjKNZ&@8rf2mh>Lrm&Eeq&US>GE+>I{%+%>@Jz`>F*Zdq!|oGi?V|;7($Zay&8HeU_hES3_IVsWeFR$txnCN@+q5RY5*oHis)TH_L4U&E;G$z;?s!Z6v6z9V`)O zGZ*;4)HGe#=6N3q-wvl5!_Q#kllP!xlw6vil7B52*GuTsesr1M}eHJDoeOGU65)s5XWNulOkljjuSN+ zN_??W5(|Y!TTEl2Y7oG`7zn!Iv8dA10pqn!q0L^Dmh6*&BR7_@PvO$_>hKOc!CM1u zvi1DdAse%S*2c_IP$-WgOJ1Ga+bgrCo!Cs8*xaMd$CHMAHCAf_v|3IYi6KODoRdwj zjc%{oM6GbfI=#Gii%mntZiuMOpY>*YO}f{TKIt91)T<;AC1@Tfw&a~-{|QE*OeJpN zdPsE8&O}4=>NPZBaCfd!vcW=ogiUv(zmJ!x67yDtG(q3T?axHXuD@1&(-}qqB036} zFWcQ;ZmFO{f0*$F`y=g9M(mjXsNGbS<`K0XytTPXc9u~ zhE@F7@?h<`|0D3qB!{|stN-vG5L;`YB)x`JD}@=C?UqP^!@_0L_9~HGc@tI9+fRmw zr!~C&qq`_Fmw7|y`fg6A1`a{JsB4Ie-qeb#7;6JzXu^ISQ>;{EUNot|d)kahEG5V1 z^c4{i#ws=U)*}Ru?zob%cNbDXO}A4yH2Z&fqrwa9D;t%+69tz&?fb1z5m+atB(l~% z5!aiyVMlXm)ecZdms!%W$dSHaIiNh~oQI_@szjh@yyo&1AH!WbEOMEtPTUJf7n4Fe zL0k*|qGL>@_G{}WI+8p5(^H$HDF~|2%$bCJ^=_B5FOc>4-0g+X`x6>HKi{-DZT0dhxMJ3`bU<@EFPuNM_17ZzUcINmEy9{$`kqL*E+ z`I3xrb$f6F^7ASLc|y3RI##?}1Oejgha_V)q0Vu_at}6wnn0`R zk-UChf(V_+b>erb5tU@yk0u02_W&x!#`G3jO3f2S@@*n4jFYf*2D*!Cq`j4pLT8b&s zf26Fd+xdG`$MZrGG1(Gcp7(b>eMC%g?rN(-Ct(#T^dBZM=XC=)wd$su!TE4wn+^m^ zzO(!Z;#jDfPfJ|l=T!!9LP^tdfP~;-9pSvr5>%A`Un~$LifT;yF`&fl^T#utTLYA1 zfYfy!bZ;&0u7Pmm53kGmJdLfmd_gmV=v}gk+fVMVj58Ee_HR-}b`2Lj<%YTbPs*|^ zGpcb|h=3jQZkO|`nTP3bc6u5V(9v#;1DS~$Gm*kf*Eb_iLIDJBUf_5EGeeN%9tcX# z%YER;8mPoo`-tAihK6sqNSvf(Mui{b^1h*=$fOQ_gfGV{?0K{Tmz}Z_Iq>B5xY~K} zqOf`$8tQk}xXfv$TbPmfuRgDX%D+^c33jWTB_Kgz5`Iz;IW-7i8Y%(CZwQAJFBLlC zerGeJA4|VW3V$k4A=J=u7^!p>S|HkqJVWVD~r1(!@N8mC+1mnGE{h)V3q_MugQ{PqC;oDCz z1L4|pKKmmCVM^FmQV$hs`d75dGp4IJMY3B- zyvR%%hU%%iEDPEa)6Pu4vzb)CbZUlTwHKiBa#f3?+PaHWdDd9-eLNXw&^(xvjv!Ex zOWH{!;%&2g%`C$ST8#@n8QSSK2L8dq$ne0d5ZM-5Vp;z;aZM3iG&?!|@YCF|H$e(89w0%cly{z1SLSEH+Oe(N8>DA^JLoMkL>+YW-;&xk-@7Ru zLZ>F*@OwA=3klMo8e8aCIdH4+=I_*+v%q37HPxJJp6O@n*Vt=&gR^7_M>#9G)qIj< zV^fj@FpF9i+JucI`=?0VBhJ_hu+;rg_-HHLw$2iB^WROeKQE=f-&V*>6?2LDe?0D> zoYNw40Ce5tN{TpQNASLrA$|NHJ@1K{=4ncNG*N1m5&iZc2)L3QbM8Am*!a7b1|opp zNY4MDX_3TjZ?nFM_rK?Tn-4W$oOtJu`KiO-ZvZS`k9hljrTt>Z{~raPqgv2J_(4xa z$sh5a5l~Ov4BID$ zn;aX0Oz81k0M$62T?T+j!0T|~n2Nt~U&w(AlxA@wKnRf*xk>X{;nCs}c-HZ|2&K<6 zzqRR{6jm_5_AIQ|-Mc`}Sde!Y(bL0_Cn}ilKk+-P_eT4AmOL4s+Mlh8pJyR2w@@5h z?~6qX{ZQT}4O-sSQg1C%dn>FchI%+8!OS89RW?pOJ#ewb&8`7bIw9Er7mPldTqkhm zN+M}{Ln$c7U!qqbw{w6n_jkWxGrA9izJ#jnEH^sV%rTsPY{~re*^r^S?OA-&>q+tM ze)Tw(5wGKXP}4Bp5_v{6tv6RshM$1>b_L9cp_loH3Qde8mmL7Skx*f4j51tx zI=u|wrN$> zX3a_eyYd*r#B6Hk5)7EP!3Ow=Y13(rGwvD2bW)t)5>N6M1QEg{8;8>69XM^5NupMx z(JN2#eb~-^DNIIV_!3ku+>h|wcrX6p6C1EqvVb5iVsjqdy-w{-hnTc?@s12w5i_$G_~j~DBwj%Ke->;sPcw_+_)Jx zBa+q#4p}OD`Q0_OCqjacQC_KT+i&{3`-*%v1m|6Y|zlnpxD z>%T19Kvs515W4mTM_U8u6%QV;IT8dXTB z99v${UIF2;r?FCX4pFe`+Xo4F)pKCOS71TXqxM#w(xk>n{Qm0>LYXF_xeK|aN?b-i z?$kvvK*IT9InHZ&%sX%Zg%BOc_s&&g6}P2rk<+H3@(fv;^I7WWitpsc8ayRvt zt%PPL!g=e6c(WeWREB?6O&C3SW@Dsn2tr39Zae4AdfPpyHKJHu&`JH&YP@Q6s{KF%VNd~thu4^o6Zj_dE56rLD5CeEu6+17eDIf3K}YGwIReAR2_GT)XDE8ZrtcXfRrU|JecWV z(ihgU>?o{AN%Bn?Y>`bx|5o|#ZZ9Ha<-}hsR{Bf_e}T0p%V6Qy29g7KgyEhFy7KqJ zMtwJJoKUWtQp_VPKD@q#D%DDFTSjhhD!obEN&%pr(eWdG-50}Eu~?xr@-zxR8p?zF zaP@SlVX6SCh)QTKj{YzwP%~O|ZH&=Ili>VT;AN8gXYdbI6UB{*`c9p0I2my;d9FmVd#%h`2Ro52Z1-Fd{B~EX} zM)!9xI}1bty+lntYo9_WSO8Q4>w>#8W_ZTW?(Tchq~PO0s*bx{dh+pf#AvAhZCbw)QPh@!>6 zF~E;h4>ngETlWj0s8Ga_-atf(JzPrR2U8O{H)u7lMh|a>&n6Ww`-kyCz3wdEutAZc zky9`acT2#s4cmUTznhs~y%s5B(o=2s>3wDR*4jZzKFV3;iMb~r!rO*2(~frCrOHfs z(%jK#!o-*p%?0!=@uX_e^!eZLb;xTF(g_um`~$+PD|T8j6j^UWd%r+<0z27AD{EOv zpBts!`HAc#INee7OcR-~uzOw6=bRMeCjAl#1N9#TZ&?`0ot15Ptg)ta3cC}Ii6-Qj z=cG?Re({lqBv>Kwl4C29$qH_b=SkB)VoKL%O@z6jT6euuv_ zDG%?NJ|s!0xJNG7bbb(LOxm=wnU9!1#S6tD3Ji@U+M8qGjOM_J4l3*ug0uPP#L5&X z%7ByDBK2GhcIku8?72K&HYT~_clSt(n+}!a*k-!1ODAl4un#(K`*3{z4G5n}IMJ29 z3(v=rgHLj(J2#mQdm0~t=e8uyWYop)D@_;ky}rjz0XA#57r>C~GQ4m1_%?`cVi)sz zl~c3z2R?D38h=XJVUsNgMa|1HRL8O?k;EM8<{zV6eAmv>FC^z!fIU*0ciA@9t8Z*r z@C>YO5%Ps5MztLTw>#+v+-oKC!j-j|^b1sGh1gCOax*?{_KDU4#~%y-MmO%J1VpQa zm%8k*_`c_Pk6cFBqJ)G61%dtW5dp)l=PQd+LjK=L_xTJ09L!AeH;i5o{I_?>SIn&0 zAHKYGRrq%^ikwT+G6Kb&rMvBrV}!J%+_cNTrwoX{0XlfU1CWO1>zRHRZJ1mqMsjHG zpyN=5cI=&5BX~VeOTwj%F*erk9rHVxAE2fvBovo6Z=7>a-Q+^-8NnG)D7 zHTgJKxFa~NbJ&>eS8S+t4J1>-SEbRYD_(!&$iEOHu_rhInAzNqxBi}kz)+(MTx<7c zsh@JDcYQYL&e(`!ThkdULQ0u!gIs=(lgqzmCxcM((QqoTaovrRHFS}pk|G?wNvge2 zcN#mM;?Iw6*y8t5`;$c}?-MN|J5naTMY~V0Jyqm8c>Qd39fB75Ien8p9BpQ#Gag@Q zFH-lrQqHJpc|hEpHk!F1#4#EO6@T{kDbLfu|K+s~gmqZ9*MAROP^^=ukA)Vf1x3ZK zLx~LgC{Vr7_Op)3jYA$LyUDf&VduQlC>-$Ylly_9yQ#4~h6wSNPr5s#=?lXTQWY09 z_ta=)_k#1IW<<|qUI$%}rAoM4S6C0FoVy)Jq}}|YMj60)a%%m>=uuDiSM8%!;gd7% z*Isk}?97V1Z|w&zG3S%q11pp)3%IM-zxY$Wz2Fq!_x+aocIIIEBV#u}URQ zNka47N&C%b4fMUjE@eoG3s=wbO$bN%`Rfji%1a#@)uWJBd{PUOk3D~Y0)%+m2MVvd zH(_RBA>m)AmW{!qRh}NloA2lXzZ=WURj>-%c7k$P9_w0bC$GOEy5BXlLt|G%$kn>k zN5g?reJ$910ZxbpsV~zXVKo}qM<@lVc2akXHIuBhzvR_n`@oQEU*l9U$V9e%x3}DW zujXm=4>O*73`E^J!Wo3&frC7K@#d!n5==Pk>MwNyStpZ>Qx|a?c?KMRQ9DtwrAoOC zbZhN|kr0#9X}9H@uiik*yocopB%ob5hC5+O8AP8a6|M~IdiH)z)db>@ySy0Gu<{F} z5#ESyG)jd`E;Nhbc{`^+SGvHh1zO73TB!09x7fNXw-}Wg+C&gk{`5|N>9ybl4131{ z;$$>52N82lP08hSRr?W*8h|Iy1wshzHC&rHfq@A`25z91{Jw8v&o z@Y`)^ph=~gIUgenKI7z3+^~h%Uz#-(-3*vVe+0B1)y)6Noih;y&Yr0zd|juG@cJS23-H96PcUb3~hj% zJyMDGSQ|I+QsU{cJOwCA4d-XKwL?mil@QNBxw4vI`^}ZyLY2 z|62BgZh+~qNI)j-QwC0U^a)o;p*q&afh6M;d5_MU+##eP5CJkO9P0yFbK*$l-lgSq z)UNV&VIw5Go8z3-*r_?Cy!x^G5ZZZ!9d`nTV2|9@mF_zzkmZUJdjm z)1Q4go-tfbQF z{blBzT7)`~Ndt|E48lU|8PKZ5?c*w)`J{%L^6ONAV7DTw*3{q2evy!l^LQ_h7SzkDJi}7afjLMGwg1TTshkCVciZZciRVr- z7xy&s5f58vRCsEN8yGF>ISwn-`#*Bb@odm94?_N-bIesO*1k1PR=%!Bgw&_?Q7K|5 zKZQ`m=|*c)91v(-f$K&Do)S732}nnk-zpX!=qwpF>npfoaV z=|1}$@1*qg@4-6w^aRL=&h_f(RzZJbih^V>1##fm`yprW)PnjuHy;p<{U|tyw+)Q@ zY*zOQzNQhV7CIYK4G01&$TK(J%(|HI6r}vk+0pM`kMxWt@wNdpH}#+LbP8gTf13mW z3ZMIBZnc#^e1J?b{d4OC$?C>vmsCu+X8wvZ2kI|P`Tqop;JZwbn@l7`(n{;qwX*^- zGT&xXu7Dni3t?d7bud?BHlC}D0O}EbPQ=>YfH=PHARRFb!m`-#e4buz|pun8J;e-#CZx7KX4FQ`XJf;!o++4p_Kl}P~r`vK*P@a zR1uW#K<*UTh=l}W(wK&0MHmADPwzdCjJi%96SH8|nExO5;a@=J2mqNG;H$eQ-;SGs zF_ybdna4DkRM6*mT{fG~u^f~{=;_ONp)N{=e)tmJ8d*7XYd%if^_J-$Qa0_DWI{aK zk;PEiL2em2fRRRP(2Cm-!D^9x|ZjK_;O<1RV1zFI`dxNIizuFasM(X(jj~&Zq z69~BQwer*t)+RRQf-Da~5Ah@He~JRC0^L#V7Qx>ZR?y$Ws=sSGZHNTHgkavKfCs)r zvl3l?2R(j8h#=+Jpu6{YvIcyo!4~u5&xVVe~Er{0CZD0;_?+0 zHxu(^cb#sP>fW(Q3#NN&tB?0DA!947l-qpRkFn4d!&$a0w}zw4uz zBxN2@GRgLgn_XlWKH?_-B3zkJ5$m`86eO1;d=iRb#mJ(=ZC-&7?ON^L$wsbj=HI`! z{CwuYH`*$gGa~9R1-}UdbSt1v6GD=}J{oFgUEZik$aV39a&4*FF7{TN8)kI4 zbrAEtAHf^1WOZFU!3?1YO^y#s1*L*`meE{}r8T_o3#dGQh#9^>45a+8ERcnW)Cr9HOFEIr0CUAJMZ!$@;#vSby%icmHg)1p_Pn9Vlc47iCRGzw zLCh4F#C4AX2JpT`J}u1HbM{^M^A*{cwMy4D1rN5NhRPp^wl;|@b#BKF&$slyb`F#g zSa>1Knm4GCf#$QqN5qV62vepRz}GuKWLA#E4Ho0>0lZ01gJf7kFvkF(H6pv3PC4l# zJQM;`z={3Xoigbki#2_3VYZ#IK@kUUG@IE@vami1Go;mYLB29atrDtbUR5g|(`)CX zPwS?%`=#(6X z)j%`l4m2p_{z??d%>Z+H3%emVqxxEqZ=1Nd1skmCdV^d5S!a{6h%IV9`#;ljOB1qs zE&0MG!-wsj{d&=tX95lzVzYb^Q?-AtnFOo1vrbP6S;HLUUFiOdejaq6p7VAKe$OpE=6xmYw}E7P`Om5ZY`B3?ZUq-$CN7bgt`59sB5$~FMa^60u4bJNe@oJT+t@}C&Rtjs+m@H52?l4?=ieQKPb zTpjLEX*mfabQ%*o3klFOAw|e_Jx(y{ulzuoJmWEV!l?4lYI%WKJ*t*fg4{QS=z#Gf z8_2FzgOtg+2|Q5^S4lXcK2%27-Yj%dek4D?Dc1KD$d2TT21ru;bXU-a!|9)aAbwos z5N!ynX0FvY`p`-{iRDCb4ILvo*1no3tF~id_Jh?KSk>?OnJKO!5`txohEbYay&s5( zMq)^>v?m>C>m_Nu@@fC*PxB4PKsA)qOXfg9MhSKf=PkXv;70{K<%EW{`Id>pWEHE9Me}Qmp>!@HY-?;eZM`bc=?fw$mJO63xJ^; zfZr+bs7f1^{X4Y>UJdO3JgO*l|4$xOs_m*hRnI%yx71^Z|8Ow`hd(UbC~9VIQmM}_ ztK5o$kJz?%tRR#(&k*fJ&&kfGcn;h{9ATwi&;ZTDI{eyhfstUqZfKO;h^73+YHL@* zC#BH6YB%*pqS2%9+M(J*=~`ZS!3S~zbHHR|9)neCSjX7{gbFl}xewB&3xS~&?UE#E zsWmrzOu`edYn8-V%g{DWW@obxW?oR{xVCkEkTv>w_Ts14HU6yo!|u;1p6AAYS=4hl z@@)hL$}`C%TAnXNCJ0BACuq>LTq^k8EZoi;I(g`Bll%{Nu=RI`x5Lbt*Kn zZ@iupSsOSW)o?wue5EG7of&XqyEbH(+scI+)-x`{G6-Th^@k6K7 zNS`gm@AI=|tzcBwJG1sB7NgzkGC%-G)E7Mp5$Z;OYX#gkT%Ydpl!c6wfC1R z+2wiULjDv?_jRV6JW&(TrI;()zQeZCz3j(;xdAszJCc7oqvgltUO%_lb6UZ&V501J z3sRh;S2i8c9qo!0quNj|wiS6pSV5A$VdbL!UazH7Vt)=)6{nt8pk5hV+=^p`yL5*S zQ>-VWItu-FE7-db(k!ReFwHLE>3S#;&R9EVCkjW=jBq=G#JzLh$WVa7&)|PMPMAj3n|bB~Xm?3&KN_PTgZJ)bE@=nv4^i zNKU#4rYGfcx_M`f-X+d!FXmzhEU0Nhq8eKYyS z-8YjE4Aj)gKL~?%wx46wyj)S|Vcy27F+poV!eIe}=i%C7c{Vw;lUJB|BsG>IzN>z) zsJXHIf}M8$-`N!#K!umZn1I>qVR|P+;3?z-JD$2;`rh`l%o|Lx$_0Y*9BF&2rXsYG z2mNB2N+(%%#znH*O(~y7OBL>?ljHmaf7YO}a&|0Byn@WMfy;xMvpQHBPrL$R(XIJL z{O)VsIqBNx?WdA&$g&uwkNPbg)h3aP7P2dJd{yaL*tF> zQr&!`ROIO}T4=wJY~6Dya0tU$O%q*z|B#KC^?LCrHK%1}_rx(j(IAC*OXbve)l=N7 zB$W9?Sk<#TJWU7S|Ut-varGFenCf=6JHjK#&r%1C%@wzZ`fWORFdi_c&FIo=z zvKP)m#Qy#MO%fzRtc$r+4~fXSnkNu_W=zlH?r%|KAP1Fd73)sN!bn>0ew1e;e*$-E z`(s({MA@ry@2;`NV?^GAtuTP`Akl4jA}U1S*%~nAUFO|}OFsLyUuu7iXJv%2Gb;$T z;;6N^s8r!B$4}z@w4T_>)|mT5KV2z1zeH!%J11I#sAq#-gyPP<|z=#@)mX~PU6@$;0WlpR<-L1F(7pCf zu1k^r!;8hwe!BX)C1XH~7x0m^MvHj1s<1{eNo3=@ZgV&oDxV+(iFqMbt#Fs@rh}q3YTHT;Kx0L@($m$9X~>;Uj+Es>}e7uq|jPDr=T(0wL%34b_>7G(Iq+ zv^dTRIR!r_9q4+QO`CjqwASPBT^x0R@Qqh`^3)&*)-us8L8RE#t&nNs52cp*ofb{5 zuG@T)Ymelqy)mavqklhV%3-V%5c3-P;5k!>L9xOusBtxC0>FMu0Q=n(V@@$s zI0>-D#4Z!(wzho+z5=I~aZUk_<`iK)RXU}b?uko4p9XDDog1CxwHl-! zQfx*Fj--edA?Pj{P`>ptM>=wx{fC}m@|g~Ug^n)w7|DV2G(4Ss-9NK7RVcw$r^MhU zOGQo7V1izpQ<0>ARbZzw0D2lFcF|a~c0ZIRi4omK^b)*r>eYrR1f$&lA~iz>)azm5 z3Y#Bu)6MUr36AeIpLg*axPGe#))@9OHNcj8)v0dsSg61pwTYX~4Gx=V+Zv5%;-*>i zH!oZ5lqLAEvZL|-n&p6R8>m#tiFeqdI4Zii@)sTF|JQ)p7SNjc1d2f@B~9|*Bzc<^ z)mT}lgk}X8P_L$6Tj36de8sgvEH=h)Vaa#)lm;UzlQ@-`+9@K zV2JMdmo@>PV@sz!0PL&G7i$5wqX-7#;Y6#niJjT*Q8LE{z(x(|!kQIxsV7^quSZC@t&9JhMg zs>rKpXr6iyaSntT@Tw7ML&1M;SADR_BUnBF2Z>*=-j_mFWk0bvfF}EEK#3xrT|$Pxne-Ad4nmZFKAQ=O)tC<_0|=R|Z&@(DlLr4>CaRbW;g)eG3$64h@K5KD3HI~z1V_j!4NmX2!As95 zW^Y!F^Z5aF1U-ScVYNjLeQkcnx4$>d7e*w(kv?k}5iO5Kmf`kG2$^?=z(7qRMv~%) z#1@1W-z6N1y|T1I9d}6S@>ca67SgIK_XuTe2qXfYX)C_;TwyMl0~PjUupn~_T!Cnm zkjXj0ZX}XUsFP(JA)xG?TEP)yoB5aZ;mLAG9_{aouTm*S(v>uEdEUNW#e0PR+}m2u z)@PuW0|(vxe+6?$5U9P>e@HL=8EBd25{(%&Us|AlJA%IeTNJK3+%yhY>q$>*@aLbo zvFe(Gls3X^be^mgqEdbYu)Orb4cK##Ge-8f-q4$T{l=i%$gl@loa^Rl^WK-ha>79% zX!-=mz)@<3xU2cjy2j>jEe}i)47HTBF*hp*_&#Bi9t*Rfv1u*EA(g@~PXoS@<$<8QA$#U(Z3W#}=0FtYEKXIhIGYh)b_1FN0LdS*Pcs?E{Y_h4N3!3T-IVPv zB-nGwS5<2I_rg$0R0no~6v)I4S%cBkGs z&5Qw8d!mo%DJd^f^fE+z9I(e_OQ9V*8BB0k7si4-IE(hAo^g1SY zNURu>qw-Z=gm0jBUxF!u0|8pBmZQ`F@Uc%4-FumJQ+|V@LMBZp96A_4AgRQzl%Nz6 zAtKClmxDsMfE2}Fh zcfSU6$Z~fdlSE^7w-Wb^@w}Ur7YSx1=7c`vM54-*;ieP z4ftZ+KW#IGibxvrTonI00G zX<^<}X{jTPc@`r7&Z+M(O5^L(|2y)?6e#6Yc)x;_!#^%_e)J!HdBfC*Jtr*#N1Ok&>S3naz_paik_gHT&SPc1x^uYH~L?As$_ zZWS~0Y0VyF|L9j^J?GaYq>#1me*(C8sr>I!5zoe;oL2lND3IO5!h3BwITgGo`gc|j z%(5IvbsL-(rMTs6{1$NMKw9el&7#B8MSq&XjOv=W&%|-6o`Q0H^>t)=JY>Kbo0TuX zqJ3(6{vgL059x~CZ~6HHUji5ADmy}O&s$WHWs>fabwS>ZgPKoZ(41wpowmK!u$DRX z?l+I0*JtyJi~Y~QNFm{^8D6|G{e41bL%BpgV}tRKsv0s+U11SeCW*~JamDk>AqoOw z5;4Jg-BsQ}{+6rCC3zCI=al8kp5`k(Q&#nAmR?%QmkADGCEcbs!nhzpl~sj<2(xD* zE%ddQ-G+5kUW5Or9OqrrZ7X_ab8KPH+X*&fcFR4U0kf#M;f${f-UxnwnNB>^k3e0h zDl<+uKQJ4lnt>Ph+4^B)EEG>SbOYK5%6yP$xcAN+5fs{y9CgiBge4~fy-D3N?>M=Wu{wC3=%mR7&i*~%%0spZ2IC6fB|NP?T z_g&job`p2DbrD!=JpA!x>)p`6tt5`IWV3AJT1WjJppY#6)EfG>Z^Y6F>awI(u54>MFe5(&v%11E??0Ix_^F+~h zXol?vAz{EVyCEsOp}YD!2IP5&Y*`(BM+u4|)RBak<3eub)l09tOZRb;L*gjp;cYl$ zX`DWMmgc769W&#E6IT__@1r+kc`KL)oZV$YH(-J4 zb)|?ptb&}gFNUqDW()K>$$o`ao(IKM!(1y@0%$E|FRu3jRh|2(FGtq5mxV|3$A1J#R!G;TSy5qN)zAeqbcjY;1C3)O>)F;D@(6-j zW4CqQxY1h*a5=UY#{E&s%uN6`PsP!JUk1vXo5-HRkBIlTqy>gOMKp_d;`~C52WYcI zY(iI~a^u^DkaPdQmaHob(GmlolVA*xQVSox|w z3GLWkWW8yB1pUwhd0$hy_ggwkHzMGt8g`;wr;;CULPRp0CJVJwBK`FkFr0aeO?ez1 ziNZz?FRnkopa;ajrNrW=nKE38n9U7}eY-{yKkdUU*98`8a$t6Ts9Vy6F*bL+F+fwV z3R11JS)8$`U!e@>>ZK2>R~;RH4@;+=1Lo9+CN}HknM;FbV|(a_A6(CWpO!EH23idf z7_0Cbo|kqhWw}m{=6;ERtFRt`h4RxorwNyvU4wvaTnF!(Wa@a$$Y1BMXvz+x&0&wb zQ5OQOM{UvDWbw7!L2wZRv51iF*E>@ppIR9?$xjz&;O!=&?!|);Mrl%F$@7%}8n^q! zCc8?*IDPfvcPlUmJObw)=it7_Ql-+s({pE}4bV=2B zM1KgxxVqx-L{~dEi915=@+}fRrBv)SFo*U-%Ujv#g!?KFY`?9~ow@xw0M;s@W0)_7 zHWB$aOxO56v08LVJlVkXDQnk_YwB~){&8zCWI-x=IBiUh%O<|0?f@W&vU zU(n8wO@gsF{ws!&sO`js{29xw-Kr;_BF2!w&Qag0i08Jh`5qJq2(L*l|4H?%9jaV| z*ZuUK4tbL*_A?HHz-5z@+V>HBXE_^tia^EMQ6nDR98B6+3fmsn@H(sdvO~E;GCK`n zsUC7k9-outA{-ka*xBp3PJ8T&Pk@`K!vRVCAE364I7C zzcoJK(ho!M>RcAyPXEhqpjdcHmAed2{per+MP&ddFd)x{GsL5NC9hL+B2lg)njHRu z0n=WlQBg&797i)j{L0%|C_J^&*&gF6dph)vn1Rdaa%D;8w2F#)iXKIRdlw~?XGY$L zzz@DNwa2w?Z>RrrZ<3-KyF*QP;S2+mckw#`0MtvJw5YozqCS<)W|Sf((yK5;Z3KgB zG$v0#1*|xSb!g>J-PK!i4MzSaq#yWBC71-<+W#-!&N8a1t&jTB9fwYl?vf7aF6r(L z=>|cNPU&t5>25{3yHi4t7DSLx^j$}<_j%s>@P2te-7)SM_a62>`|P#W{LlHD;i!~) z&mse~^cbT|#pV%S(*o37mbw3DKo_X)L<*&r&@wP1k> z*zJrmbTik@tfVXH7PIBA#QlM7z8gs#555b%_#kd5w1hUz!AAs=;$SLLlAppe$1cC| z@lRMEh)3N(i2@!zcwO6UY(@Z7dZOR^>J3sNw%Po=l=f>6S=hWi$SP%$DWbFx_s5Sz zuXOI5PUHlt0ZVi|$+SQ*C7nFwrws_96@Eo!D4fq4v zZw7wHTX*4P5pR8=_C0wo>0CZ9RVhB{!=@4D6^Gh)9`vY_;#{vMr(XR0+70#Y`SJea z-TMbFr^Iau;+0u&Azb}F<8A@iU$%eidt(*HkSu4-U|^CS4tM+Y53mqC?|kqbgH?AE z&VgJ7chOl+%rda_zK<4mwL85*ikQjy*Lfe7ftslz%V>)hc}9c84o!(e(wjwfh(bes zh$Y|N-SA<$N706P!0qpG`EOjE0`l`J1FVoLVW_;PJ7{YS=J^2)sGWun{SGMc9iY-) znlMQ^kC3zMd5xaIAG_W&;O&sF=Z2q=3-|m7S_9r#LePA-2d-Sp*OrPms}q6m{wvM8 z{HKrNtHjp3f8*Hk-C{tC27MGgSpN@w6wj2Qz_L^q)|yVQ(^U?;*c=(}0OIxc>W7{<7}_5J!uFL8Dx(Vwb|d#)+sHH52PPxz4+AyWoMiYYVtA4%Z#2dnizD**e?t5(krdd8bV*L3XE64^y^A zvIgnXvr67R%6`}G6^k+6c$C46?s(S?AO@Ljba;Bj;9Qh|%YX0!-C&+L;_u~CKoVY@ z_}gC)7$ArFuM9!Q;ea#;Xoj5<$m6IhQR>6R#YmDf@#mwNqbwr_A>eG%HcRiQLw^1BB$h9Tw%;TLopF0TT7pl9)0 zD~Cn`dAz~|1_?^MyD%xPDt#!Gj8yhk zw0`zx57^P`ziniYGLg$K_Y2|28~_+z16DQ~9Cct`6bR$T^{d8|{A&geUMJj!O^p8_E7z?t+ z>era{w!Y$Px9I#1+Tm^l7o{Ih*xTx3(UTt*Y=WGEc8`6{0a`O>(W(fWs?Kekp9kk5 zyJe>M8h*#kh5qNzsEDq^RxL#VDkZt1;YwxX&S&}Z!VA3dXaQVKbnlO(Xk%;c)<3=3 zB3ssV?ca$?`WEJ8TsxFZs)B}zFiCJd-0%_;K?y?$q&3C$Mj0Y9-K4VOTHm&Z;hGJx zj<=`mCBKw!$A>0eMJ!IHR|EK;@iS^A(+YS_S_(+9FKqh&$r29-gHcMi9>yd}7avE9 zi6;8>y*qhwWOAVF<(TuEBjn)fXIAVW?@U!q@oR`YkqF}tSfhZ!uZx>H*_J|5*a?g| zRVNdHGOF_m_??}5$-UPh&`xM8jn^0OSz*^q4=}zgHn-kL*kM2m|%LaPml+4ib-IkofRV!^e+gL!>mh zhQ}>4#Y8X0d98C;Yqn#zRrM|y<4KYq$){_lXJ>}8njl0t0-taT+vqpO1lo)sxR-_2 zvD>i;no;3TxyItszx%Lpzo227uOfb@;)IR~JJp-G&z&48O)>FMGk3zZq23@MUKmd0 zfv=pkFuO0Sfn#%O(oW(~$ev3m&DkKSvu0VkyA%d%8S>ka_rWOXygj`q?)l#hGk`(R z9w*oLQ>J!RJ0tts^Ov7)`~9S$ldevFx~y}mKj#HQcwTQ^IU;GEan_Msk`Htz6L*~_ z7t?#`01K&~&N@t9X!s$gIcFX5r=|HcZI~ikdvQW8+A2Ze74RO)jG>h9aizK1=AVNY zj(*m*v73%<3@!GBnRzmwCwa1z@~F8l+!7g4=CDVm6dnIN83vO;Y5jcKWI)+{|2Cq8 z{?3$GtpBPhCkH7_?;u^&wPN%x^jD7p8Et?}R8z`Qe>?N{$aF#7odWW~?&V>j9=DHs zB#I*Q=`>0rygX_BK8Sf?KOUuVj1!?b%J&aNkJ4hhfs(1|`}8IL#5a1GaOQ-%9k8OG zw{P{5I2E^Tmxh0t8g30XP|Cz(yyqKaNyMwvhaR)DY9;P{%9W^S?$EzIal@0Xc>5}O zepacIpO^c9;tiz=nRD39(j0*LT{d0J&SeqgK*xV;B7RVAtX!MqVVW^NkjqiC3oP zS_Sa;wSQ@_40*E!ZoIu#_QTtUzFsl2{d~LJ{*lo^AXUBtmQ-k}gW`ElPDwsqmjKyE z*JS~I$Z183WJ;LL@}FGn=Lzx^stNc0wK}05&ZVyO-y&UI3E*8JO@Bpk_#iUA=dM;OG@w`i!EWh$QC=O`bqxR z?wJj?+RD3Stzzk4+%ULD1qjfwV;yynaQOcjgZJC;rsPk19X9+q-eoHoDQ%T%-l6rC z;aWr<59CeKb$K|H*mFkioEnU)@nX>m_8i;sKomhXc4Ji$M<@w z;v8nFY&&(h)^fA@qzM6`y>6QK8ME~o^IDwzu$95H7wALSKQXw?EAGoyCl34MsM?%1 z=+uagzAqOd$WlH->&>eW=5p=aLcS6qW1?v?r>Pyh6B4c@-t<+lL8?g=tj@HjSbWI+ za-!iH889~!q*byUOir9*H%kg1S8lluhmILuHND!q)b0+?6;|^5;y%3yM&xBb z2v2^r0XC87Gr6B*H17Zi1Ac|K3fGz?Qb*hV3;c`#T_8M?t4x-j3I(DQWO(^z(iIg{ z9_uw)hXIJIQM>9S4n&lTjMdB>gTIPj2YJ)%N9nR_m zJcYy;++04g@%%f;OPQQvKYmx61W`^OpAKnN6W`&gwf5dfY()h1iGL{4szks{4|4k$oU_U?5e0CsS);;csO z_YS7bu_rnfjmE%;G$Bk$FSz+Jo8r5NA={8)W- zGOYrNl2b)&(O8R?yEg%%?fM^29v5u3kpp5~=7TT+t*{YnjO54=75-ioeH@)adGiiV zB?U7!E@b}!5}iqv%6s@;-ob%JWQ2Ae zwTJG0%v!Rc^5jEUWf3+-Pw1YZH*ko|VNDvmh~;&$<%k?Jw|P^uQ6Un$FtGigGGkh) z=+RN##Yya%+`>V6M?##M6J}!bKw_ZF2XnBk5U9@7zLOK6I$#NogOm>3fB!=(?ZbyM z8-^{Xhmf;zRU!`16x@GX>k4LNLJ%uFdY3Q0)n21!zc~sYrXiXBwY3~AJ~iR@aBaE)#r9cn_yoUCLuBcwgF3Z!aV^ya+X)xZ4XJM^jWwP z`6zKc6By*=55not6d^c;C>Mh!Y{UcCt$vs@=~4_$9a5D^#rEYb>USKw*>aXtk<_aN zjP742_#!wh22&>LltMTIBhUF8)-45vv=gm}(tM#(9`oqK+v<`g>#VCp2W)e0HDPV? zLZ1;9oz1a7PJK+MQ@=WV<%>VU_`2SF};rF z!l8Irz1%zxk#Nj2Rs%(vT#FrO88TVA*Mg zde@95uEiXDpVk4ptApjcpa(*i0?DS5d?L^AyYU~i0zp)_Sm;aLhmV$EZasPrTn`?e z689b%rVq=B*Su7sD}mNP$l+Md70%T>XAF~~$XaYh4V|nMz}ThGeP3-`Jb&1IZWtRC z$Y0bR0)q8zbX7IcxFj4b8UWvK0K@K5f_29w;8*xu2ZMBU`dj@T?rO5FV)>N1Q5FQ% z%~3I@sO|Vfpo8Ud;+jE7hvbMAs@Z)X68-6_mp2z-rWB5UnmA-mhf;nQYunRLBDy=9 z5Ygj|^tRu(-r#cBdCJ4%1a?d)WsdNA`6_2osZqq1ubG>AQb&DrxY_z7`y9&4Mwf-A za8sxG_32M6#a@@#Kg->#tq79{NOM}P&k2;TiGxtf95nRPi{6feGf7eUbUMpYhn@Q? zAe9zwiw4@3nn;Ag8sWRd#6`+0IMdcx>|ekwA;V0mIz|YZO8W!c^6QNv*|hOUr8=V) zt&hGpT(oO9UGv@&9xP>8xSJ09Qc2U)qjp&JuOm0LwG<=YNL}4lO&le50BBbwK7fspWY849v5qb7hleKi|!`5{!#_1-%YCA#j8=n zo;TH54nD>+Xy#l=EPng=StcD46Yo-1)XeV)FyP+*KQ=?uwlX=ziy)p0UDW&?qXkpO zfXFN4Qm}yX1vr_Jb{~~uDoOUuL^xgbbch&8tQda>fSJ@M^LFtmsZ8|zvZn{tnoifR z{w~(tbz&fWi)^WzAVuyrOo`F$sR6yO2a-Us6Li534S4~ZOXQVlDlm~~iyWY`wqS5L z7xX7j3>8`W9zbsSP zpZ4n8=x&kq5jMrchvlqB!evetI?0o3>Fcmh15#m~ceJiCcH)yteg9DTWCrBtz?V19 z;H#tp2v1rSw2xs4L+eWyc?KW9Qkyvg!$u}F6P?Zf5dHfg(C&!Bk$mh@mH)jvf{p1d zlvk&KqCyiqLKaeVf4iTL1q8gMDQq8CQSJ|(032B=sjjnBbAP(|(-C{Y*fY8ceFBn0 z^=8tQ?c2Kh)6SG9Sszd*^@RMGR}k-C{?&3h;2}^}o!8hOR$UR>DjpTpL*zu1YELoS zINNaI8LT35nzuVu8TFb}_VrB5;kKj@%rv_!y-t0ubYnj|>~gO^tr09`0=)ne03^IF zAmM5MUnD${`@^RW#zWc8T`PE`2hD#$b>W7ne?fK3M{nyx+*a+%!k~Pj*KBX*qbHTh zKkF}1?pPw1)W0svq>T4k(9&T<;A1K;T2OZqBDlSUY9bQ`;?G}kW`hXsb(pA|89-(L zZx`|eUqv^lMzH+f33d2pyhiro%B}d+!O6l2_HLCMV>lBPT9vRWLBs~;YAw}A-OE?= zoIuH|(9u~`a;I661^pMer)eEuZvVBxnJP*bYzk0)+mfrqJ$z)c8)NdL2;b(FK>cH{ zc45FfX3~{$v;D`T_v0z3G<8qcXLF`YN;|dyHVlj(S{UNo|6j@?K;@c&AV=}5j9voMq)%b zf~LTZIR-Z8>$gU^;WGRdHKJ`YZ$>EP1ryByA9f1W4v_6nTY}}P4Qp<+tdaagUkv`_JnaZnPr{k<- z>NE!>vSf1eVHn?l#51;jug0l)b{=16V0(M92a~4vY#Rj(&WARspQ8R}55MYY?fs(oVaE76#ok`VcC|N17k1e#O4FtLIgBDUwrT+~ ztU8$#8-g8Ux*Li#Q2`z*vc#d1K|3aXF#n#qQ=*c~EChm}P8T{RX5KIA;evA$lC$Z@ zsv8mTt?~LcXX@*7k$}8!dd$B3tpVRY><5T=)HZ%wJ@aO)D)p;Hzwt9h+;jt)55r!J z?y&$U46g8cCX3jtj_4vZ&pIUW(Rei7<@EVn50KOJ^gFdbuh`_XS|u#?fk}5+O7J|J zRV$9bj1A;Nl7430D?&pTaYjR?=3}zqAUy?3%Whv+G6mwBim#6!Fg*nMe0>zJ!Y^!6 z>rcnfycIp=yM8Fn%rJ&o*5E}H3yNmsW^&o2NnDn;UIapO;o1vw@P#=~{Jj_Tw^P$@XQQLsSJy#sm_(}i8~X--Mq9zJ zAX_p1??~W~LXutFA30XS5e(tzFTtowd=$)xR8eA73?75VIKP8r#gv^MqC6#R zwNka$Y%IP3+Q?yTXQ2WhPK&(T<2)gfu{s!9t->N z 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 diff --git a/docs/source/user_guide/model_explainability/global.rst b/docs/source/user_guide/model_explainability/global.rst deleted file mode 100644 index 9302ce697..000000000 --- a/docs/source/user_guide/model_explainability/global.rst +++ /dev/null @@ -1,18 +0,0 @@ -Global Explainers -================= - -Global explanations help to understand the model's general behavior. - -There are multiple forms of global explanations. For example, global explanations: - -- Can identify the important features that the model considers when making its predictions. -- Highlight the relationship between different feature values and the model's predictions. -- Present the instances that are most influential towards the prediction of a given class and value. - -.. toctree:: - :maxdepth: 2 - :caption: Supported global explanation techniques: - - permutation_importance - feature_dependence - accumulated_local_effects diff --git a/docs/source/user_guide/model_explainability/index.rst b/docs/source/user_guide/model_explainability/index.rst deleted file mode 100644 index b53524898..000000000 --- a/docs/source/user_guide/model_explainability/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _mlx-8: - -#################### -Model Explainability -#################### - -.. toctree:: - :maxdepth: 1 - - overview - accumulated_local_effects - feature_dependence - feature_importance - lime - whatif diff --git a/docs/source/user_guide/model_explainability/lime.rst b/docs/source/user_guide/model_explainability/lime.rst deleted file mode 100644 index b11218f0b..000000000 --- a/docs/source/user_guide/model_explainability/lime.rst +++ /dev/null @@ -1,166 +0,0 @@ -Enhanced LIME -************* - -Overview -======== - -Local explanations target specific predictions from the machine learning model. The goal is to understand why the model made a particular prediction. - -There are multiple different forms of local explanations, such as feature attribution explanations and exemplar-based explanations. ADS -supports local feature attribution explanations. They help to identify the most important features leading towards a given prediction. - -While a given feature might be important for the model in general, the values in a particular sample may cause certain features to have -a larger impact on the model's predictions than others. Furthermore, given the feature values in a specific sample, local explanations can also estimate the contribution that each feature had towards or against a target prediction. For example, does the current value of the feature have a positive or negative effect on the prediction probability of the target class? Does the feature increase or decrease the predicted regression target value? - -The Enhanced Local Interpretable Model-Agnostic Explanation (LIME) is a model-agnostic local explanation method. It provides insights into why a machine learning model made a specific prediction. - -Description -=========== - -ADS provides an enhanced version of Local Interpretable Model-Agnostic Explanations (LIME), which improves on the explanation quality, performance, and interpretability. The key idea behind LIME is that while the global behavior of a machine learning model might be very complex, the local behavior may be much simpler. In ADS, local refers to the behavior of the model on similar samples. LIME tries to approximate the local behavior of the complex machine learning model through the use of a simple, inherently interpretable surrogate model. For example, a linear model. If the surrogate model is able to accurately approximate the complex model's local behavior, ADS -can generate a local explanation of the complex model from the interpretable surrogate model. For example, when data is centered and scaled the magnitude and sign of the coefficients in a linear model indicate the contribution each feature has towards the target variable. - -The predictions from complex machine learning models are challenging to explain and are generally considered as a black box. As such, ADS refers to the model to be explained as the black box model. ADS supports classification and regression models on tabular or text-based datasets (containing a single text-based feature). - -The main steps in computing a local explanation for tabular datasets are: - -* Start with a trained machine learning model (the black box model). -* Select a specific sample to explain (x\ :sub:`exp`\). -* Randomly generate a large sample space in a nearby neighborhood around x\ :sub:`exp`\. The sample space is generated based on the feature distributions from the training dataset. Each sample is then weighted based on its distance from x\ :sub:`exp` \ to give higher weight to samples that are closer to x\ :sub:`exp`\. ADS provides several enhancements, over the standard algorithm, to improve the quality and locality of the sample generation and weighting methods. -* Using the black box model, generate a prediction for each of the randomly generated local samples. For classification tasks, compute the prediction probabilities using ``predict_proba()``. For regression tasks, compute the predicted regression value using ``predict()``. -* Fit a linear surrogate model on the predicted values from the black box model on the local generated sample space. If the surrogate model is able to accurately match the output of the black box model (referred to as surrogate model fidelity), the surrogate model can act as a proxy for explaining the local behavior of the black box model. For classification tasks, the surrogate model is a linear regression model fit on the prediction probabilities of the black box model. Consequently, for multinomial classification tasks, a separate surrogate model is required to explain each class. In that case, the explanation indicates if a feature contributes towards the specified class or against the specified class (for example, towards one of the other N classes). For regression tasks, the surrogate model is a linear regression model fit on the predicted regression values from the black box model. -* There are two available techniques for fitting the surrogate model: - - - Use the features directly: - - The raw (normalized) feature values are used to fit the linear surrogate model directly. This results in a normal linear model. A positive coefficient indicates that when the feature value increases, the target variable increases. A negative coefficient indicates that when a feature value increases, the target variable decreases. Categorical features are converted to binary values. A value of 1 indicates that the feature in the generated sample has the same value as x\ :sub:`exp` \ and a value of 0 indicates that the feature in the generated sample has a different value than x\ :sub:`exp`\. - - - Translate the features to an interpretable feature space: - - Continuous features are converted to categorical features by discretizing the feature values (for example, quartiles, deciles, and entropy-based). Then, all features are converted to binary values. A value of 1 indicates that the feature in the generated sample has the same value as x\ :sub:`exp` \ (for example, the same categorical value or the continuous feature falls in the same bin) and a value of 0 indicates that the feature in the generated sample has a different value than x\ :sub:`exp` \ (for example, a different categorical value or the continuous feature falls in a different bin). The interpretation of the linear model here is a bit different from the regression model. A positive coefficient indicates that when a feature has the same value as x\ :sub:`exp` \ (for example, the same category), the feature increased the prediction output from the black box model. Similarly, negative coefficients indicate that when a feature has the same value as x\ :sub:`exp`\, the feature decreased the prediction output from the black box model. This does not say what happens when the feature is in a different category than x\ :sub:`exp`. It only provides information when the specific feature has the same value as x\ :sub:`exp` \ and if it positively or negatively impacts the - black box model's prediction. - -* The explanation is an ordered list of feature importances extracted from the coefficients of the linear surrogate model. The magnitude of the coefficients indicates the relative feature importance and the sign indicates whether the feature has a positive or negative impact on the black box model's prediction. -* The algorithm is similar to text-based datasets. The main difference is in the random local sample space generation. Instead of randomly generating samples based on the feature distributions, a large number of local samples are generated by randomly removing subsets of words from the text sample. Each of the randomly generated samples is converted to a binary vector-based on the existence of a word. For example, the original sample to explain, x\ :sub:`exp`\, contains 1s for every word. If the randomly generated sample has the same word as x\ :sub:`exp`\ , it is a value of 1. If the word has been removed in the randomly generated sample, it is a value of 0. In this case, the linear surrogate model evaluates the behavior of the model when the word is there or not. - -Additionally, an upper bound can be set on the number of features to include in the explanation (for example, explain the top-N most important features). If the specified number of features is less than the total number of features, a simple feature selection method is applied prior to fitting the linear surrogate model. The black box model is still evaluated on all features, but the surrogate model is only fits on the subset of features. - -Interpretation -============== - -ADS provides multiple enhancements to the local visualizations from LIME. The explanation is presented as a grid containing information about the black box model, information about the local explainer, and the actual local explanation. Each row in the grid is described as: - -* Model (first row) - - - The left column presents information about the black box model and the model's prediction. For example, the type of the black box model, the true label/value for the selected sample to explain, the predicted value from the black box model, and the prediction probabilities (classification) or prediction values (regression). - - The right column displays the sample to explain. For tabular datasets, this is a table showing the feature names and corresponding values for this sample. For text datasets, this shows the text sample to explain. - -* Explainer (second row) - - - The left column presents the explainer configuration parameters, such as the underlying local explanation algorithm used (for example, LIME), the type of surrogate model (for example, linear), the number of randomly generated local samples (for example, 5000) to train the local surrogate model (:math:`N_t`), whether continuous features were discretized or not. - - The right column provides a legend describing how to interpret the model explanations. - -* Explanations (remaining rows) - - - For classification tasks, a local explanation can be generated for each of the target labels (since the surrogate model is fit to the prediction probabilities from the black box model). For binary classification, the explanation for one class will mirror the other. For multinomial classification, the explanations describe how each feature contributes towards or against the specified target class. If the feature contributes against the specified target class (for example, decreases the prediction probability), it increases the prediction probability of one or more other target classes. The explanation for each target class is shown as a - separate row in the Explanation section. - - The Feature Importances section presents the actual local explanation. The explanation is visualized as a horizontal bar chart of feature importance values, ordered by relative feature importance. Features with larger bars (top) are more important than features with shorter bars (bottom). Positive feature importance values (to the right) indicate that the feature increases the prediction target value. Negative feature importance values (to the left) indicate that the feature decreases the prediction target value. Depending on whether continuous features are discretized or not changes the interpretation of this value (for example, whether the specific feature value indicates a positive/negative attribution, or whether an increase/decrease in the feature value indicates a positive/negative attribution). If the features are discretized, the corresponding range is included. The feature importance value is shown beside each bar. This can either be the raw coefficient taken from the linear surrogate model or can be normalized such that all importance values sum to one. For text datasets, the explanation is visualized as a word cloud. Important words that have a large positive contribution towards a given prediction (for example, increase the prediction value) are shown larger than unimportant words that have a less positive impact on the target prediction. - -* The Explanation Quality section presents information about the quality of the explanation. It is further broken down into two sections: - - - Sample Distance Distributions - - This section presents the sample distributions used to train (:math:`N_t`) and evaluate (:math:`N_{v_\#}`) the local surrogate model based on the distances (Euclidean) of the generated samples from the sample to explain. This highlights the locality of generated sample spaces where the surrogate model (explainer) is trained and evaluated. The distance distribution from the sample to explain for the actual dataset used to train the black box model, Train, is also shown. This highlights the locality of :math:`N_t` relative to the entire train dataset. For the generated evaluation sample spaces (:math:`N_{v_\#}`), the sample space is generated based on a percentile value of the distances in Train relative to the sample to explain. For example, :math:`N_{v_4}` is generated with the maximum distance being limited to the 4\ :sup:`th` percentile of the distances in train from the sample to explain. - - Evaluation Metrics - - This section presents the fidelity of the surrogate model relative to the black box model on the randomly generated sample spaces used to fit and evaluate the surrogate model. In other words, this section evaluates how accurately the surrogate model approximates the local behavior of the complex black box model. Multiple different regression and classification metrics are supported. For classification tasks, ADS supports both regression and classification metrics. Regression metrics are computed on the raw prediction probabilities between the surrogate model and the black box model. For classification metrics, the prediction probabilities are converted to the corresponding target labels and are compared between the surrogate model and the black box model. Explanations for regression tasks only support regression metrics. Supported regression metrics: MSE, RMSE (default), R\ :sup:`2`, MAPE, SMAPE, Two-Sample Kolmogorov-Smirnov Test, Pearson Correlation (default), and Spearman Correlation. Supported classification metrics: :math:`F_1`, Accuracy, Recall, and ROC_AUC. - - - Performance - - Explanation time in seconds. - -Example -======== - -This example generates and visualizes local explanations on the `Titanic dataset `_. The model is constructed using the ADS ``OracleAutoMLProvider``. However, the ADS model explainers work with any model (classifier or regressor) that is wrapped in an ``ADSModel`` object. - -.. code-block:: python3 - - import logging - import requests - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.dataset.factory import DatasetFactory - from os import path - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct - # global and local explanation objects. The ADSExplainer takes - # as input the model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a local explanation object using - # the MLXLocalExplainer provider - from ads.explanations.mlx_local_explainer import MLXLocalExplainer - local_explainer = explainer.local_explanation( - provider=MLXLocalExplainer()) - - # A summary of the local explanation algorithm and how to interpret - # the output can be displayed with - local_explainer.summary() - - # Select a specific sample (instance/row) to generate a local - # explanation for - sample = 13 - - # Compute the local explanation on our sample from the test set - explanation = local_explainer.explain(test.X.iloc[sample:sample+1], - test.y.iloc[sample:sample+1]) - - # Visualize the explanation for the label True (Survived). See - # the "Interpretation" section above for more information - explanation.show_in_notebook(labels=True) - -.. image:: figures/ads_mlx_titanic_local.png - -.. code-block:: python3 - - # The raw explanaiton data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - explanation.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_local_diagnostics.png - -References -========== - -* `LIME `_ -* `Vanderbilt Biostatistics - titanic data `_ -* `Why Should I Trust You? Explaining the Predictions of Any Classifier `_ - diff --git a/docs/source/user_guide/model_explainability/local.rst b/docs/source/user_guide/model_explainability/local.rst deleted file mode 100644 index 78697e4fd..000000000 --- a/docs/source/user_guide/model_explainability/local.rst +++ /dev/null @@ -1,16 +0,0 @@ -Local Explainers -================ - -Local explanations target specific predictions from the machine learning model. The goal is to understand why the model made a particular prediction. - -There are multiple different forms of local explanations, such as feature attribution explanations and examplar-based explanations. ADS -currently supports local feature attribution explanations. They help to identify the most important features leading towards a given prediction. - -While a given feature might be important for the model in general, the values in a particular sample may cause certain features to have -a larger impact on the model's predictions than others. Furthermore, given the feature values in a specific sample, local explanations can also estimate the contribution that each feature had towards or against a target prediction. For example, does the current value of the feature have a positive or negative effect on the prediction probability of the target class? Does the feature increase or decrease the predicted regression target value? - -.. toctree:: - :maxdepth: 2 - :caption: Supported local explanation techniques: - - lime diff --git a/docs/source/user_guide/model_explainability/mlx.rst b/docs/source/user_guide/model_explainability/mlx.rst deleted file mode 100644 index ef9f1dd2e..000000000 --- a/docs/source/user_guide/model_explainability/mlx.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. _mlx-8: - -******************** -Model Explainability -******************** - -Machine learning and deep learning are becoming ubiquitous due to: - -- The ability to solve complex problems in a variety of different domains. -- The growth in the performance and efficiency of modern computing resources. -- The widespread availability of large amounts of data. - -However, as the size and complexity of problems continue to increase, so does the complexity of the machine learning algorithms applied to these problems. The inherent and growing complexity of machine learning algorithms limits the ability to understand what the model has learned or why a given prediction was made, acting as a barrier to the adoption of machine learning. Additionally, there may be legal or regulatory requirements to be able to explain the outcome of a prediction from a machine learning model, resulting in the use of biased models at the cost of accuracy. - -Machine learning explainability (MLX) is the process of explaining and interpreting machine learning and deep learning models. - -MLX can help machine learning developers to: - -- Better understand and interpret the model's behavior. - - - Which features does the model consider important? - - What is the relationship between the feature values and the target predictions? - - -- Debug and improve the quality of the model. - - - Did the model learn something unexpected? - - Does the model generalize or did it learn something specific to the training dataset? - - -- Increase trust in the model and confidence in deploying the model. - -MLX can help users of machine learning algorithms to: - -- Understand why the model made a certain prediction. - - - Why was my bank loan denied? - -Some useful terms for MLX: - -- **Explainability**: The ability to explain the reasons behind a machine learning model’s prediction. - -- **Interpretability**: The level at which a human can understand the explanation. - -- **Global Explanations**: Understand the general behavior of a machine learning model as a whole. - -- **Local Explanations**: Understand why the machine learning model made a specific prediction. - -- **WhatIf Explanations**: Understand how changes in the value of features affects the model's prediction. - -- **Model-Agnostic Explanations**: Explanations treat the machine learning model and feature pre-processing as a black box, instead of using properties from the model to guide the explanation. - -The ADS explanation module provides interpretable, model-agnostic, local and global explanations. - -These explanation techniques in ADS are described and include examples: - -.. toctree:: - :maxdepth: 3 - - global - local - whatif diff --git a/docs/source/user_guide/model_explainability/overview.rst b/docs/source/user_guide/model_explainability/overview.rst deleted file mode 100644 index f41f1cae6..000000000 --- a/docs/source/user_guide/model_explainability/overview.rst +++ /dev/null @@ -1,44 +0,0 @@ -Overview -======== - -Machine learning and deep learning are becoming ubiquitous due to: - -* The ability to solve complex problems in a variety of different domains. -* The growth in the performance and efficiency of modern computing resources. -* The widespread availability of large amounts of data. - -However, as the size and complexity of problems continue to increase, so does the complexity of the machine learning algorithms applied to these problems. The inherent and growing complexity of machine learning algorithms limits the ability to understand what the model has learned or why a given prediction was made, acting as a barrier to the adoption of machine learning. Additionally, there may be legal or regulatory requirements to be able to explain the outcome of a prediction from a machine learning model, resulting in the use of biased models at the cost of accuracy. - -Machine learning explainability (MLX) is the process of explaining and interpreting machine learning and deep learning models. - -MLX can help machine learning developers to: - -* Better understand and interpret the model's behavior. - - - Which features does the model consider important? - - What is the relationship between the feature values and the target predictions? - -* Debug and improve the quality of the model. - - - Did the model learn something unexpected? - - Does the model generalize or did it learn something specific to the training dataset? - -* Increase trust in the model and confidence in deploying the model. - -MLX can help users of machine learning algorithms to: - -* Understand why the model made a certain prediction. - - - Why was my bank loan denied? - -Some useful terms for MLX: - -* **Explainability**: The ability to explain the reasons behind a machine learning model’s prediction. -* **Global Explanations**: Understand the general behavior of a machine learning model as a whole. -* **Interpretability**: The level at which a human can understand the explanation. -* **Local Explanations**: Understand why the machine learning model made a specific prediction. -* **Model-Agnostic Explanations**: Explanations treat the machine learning model and feature pre-processing as a black box, instead of using properties from the model to guide the explanation. -* **WhatIf Explanations**: Understand how changes in the value of features affects the model's prediction. - -The ADS explanation module provides interpretable, model-agnostic, local and global explanations. - diff --git a/docs/source/user_guide/model_explainability/permutation_importance.rst b/docs/source/user_guide/model_explainability/permutation_importance.rst deleted file mode 100644 index 50b3190e5..000000000 --- a/docs/source/user_guide/model_explainability/permutation_importance.rst +++ /dev/null @@ -1,146 +0,0 @@ -Feature Permutation Importance Explanations -=========================================== - -Overview ---------- - -Feature permutation importance is a model-agnostic global explanation method that provides insights into a machine learning model's behavior. It estimates and ranks feature importance based on the impact each feature has on the trained machine learning model's predictions. - -Description ------------ - -Feature permutation importance measures the predictive value of a feature for any black box estimator, classifier, or regressor. It does this by evaluating how the prediction error increases when a feature is not available. Any scoring metric can be used to measure the prediction error. For example, :math:`F_1` for classification or R\ :sup:`2` \ for regression. To avoid actually removing features and retraining the estimator for each feature, the algorithm randomly shuffles the feature values effectively adding noise to the feature. Then, the prediction error of the new dataset is compared with the prediction error of the original dataset. If the model heavily relies on the column being shuffled to accurately predict the target variable, this random re-ordering causes less accurate predictions. If the model does not rely on the feature for its predictions, the prediction error remains unchanged. - -The following summarizes the main steps in computing feature permutation importance explanations: - -- Start with a trained machine learning model. - -- Calculate the baseline prediction error on the given dataset. For example, train dataset or test dataset. - -- For each feature: - - 1. Randomly shuffle the feature column in the given dataset. - - 2. Calculate the prediction error on the shuffled dataset. - - 3. Store the difference between the baseline score and the shuffled dataset score as the feature importance. For example, baseline score - shuffled score. - -- Repeat the preceding three steps multiple times then report the average. Averaging mitigates the effects of random shuffling. - -- Rank the features based on the average impact each feature has on the model's score. Features that have a larger impact on the score when shuffled are assigned higher importance than features with minimal impact on the model's score. - -- In some cases, randomly permuting an unimportant feature can actually have a positive effect on the model's prediction so the feature's contribution to the model's predictions is effectively noise. In the feature permutation importance visualizations, ADS caps any negative feature importance values at zero. - -Interpretation --------------- - -Feature permutation importance explanations generate an ordered list of features along with their importance values. Interpreting the output of this algorithm is straightforward. Features located at higher ranks have more impact on the model predictions. Features at lower ranks have less impact on the model predictions. Additionally, the importance values represent the relative importance of features. - -The output supports three types of visualizations. They are all based on the same data but present the data differently for various use cases: - -- **Bar chart** ``('bar')``: The bar chart shows the model's view of the relative feature importance. The x-axis highlights feature importance. A longer bar indicates higher importance than a shorter bar. Each bar also shows the average feature importance value along with the standard deviation of importance values across all iterations of the algorithm (mean importance +/- standard deviation*). Negative importance values are capped at zero. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. - -- **Box plot** ``('box_plot')``: The detailed box plot shows the feature importance values across the iterations of the algorithm. These values are used to compute the average feature importance and the corresponding standard deviations shown in the bar chart. The x-axis shows the impact that permuting a given feature had on the model's prediction score. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. The minimum, first quartile, median, third quartile, and a maximum of the feature importance values across different iterations of the algorithm are shown by each box. - -- **Detailed scatter plot** ``('detailed')``: The detailed bar chart shows the feature importance values for each iteration of the algorithm. These values are used to compute the average feature importance values and the corresponding standard deviations shown in the bar chart. The x-axis shows the impact that permuting a given feature had on the model's prediction score. The y-axis shows the different features in the relative importance order. The top being the most important, and the bottom being the least important. The color of each dot in the graph indicates the quality of the permutation for this iteration, which is computed by measuring the correlation of the permuted feature column relative to the original feature colum. For example, how different is the permuted feature column versus the original feature column. - - -Examples --------- - -This example generates and visualizes a global Feature Permutation Importance explanation on the Titanic dataset (https://www.openml.org/d/40945). The model is constructed using the ADS ``OracleAutoMLProvider`` (selected model: XGBClassifier). However, the ADS model explainers work with any model (classifier or regressor) that is wrapped in an ``ADSModel`` object. - -.. code-block:: python - - from ads.dataset.factory import DatasetFactory - from os import path - import requests - - # Prepare and load the dataset - titanic_data_file = '/tmp/titanic.csv' - if not path.exists(titanic_data_file): - # fetch sand save some data - print('fetching data from web...', end=" ") - # Data source: https://www.openml.org/d/40945 - r = requests.get('https://www.openml.org/data/get_csv/16826755/phpMYEkMl') - with open(titanic_data_file, 'wb') as fd: - fd.write(r.content) - print("Done") - ds = DatasetFactory.open( - titanic_data_file, target="survived").set_positive_class(True) - ds = ds.drop_columns(['name', 'ticket', 'cabin', 'boat', - 'body', 'home.dest']) - ds = ds[ds['age'] != '?'].astype({'age': 'float64'}) - ds = ds[ds['fare'] != '?'].astype({'fare': 'float64'}) - train, test = ds.train_test_split(test_size=0.2) - - # Build the model using AutoML. 'model' is a subclass of type ADSModel. - # Note that the ADSExplainer below works with any model (classifier or - # regressor) that is wrapped in an ADSModel - import logging - from ads.automl.provider import OracleAutoMLProvider - from ads.automl.driver import AutoML - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train() - - # Create the ADS explainer object, which is used to construct global - # and local explanation objects. The ADSExplainer takes as input the - # model to explain and the train/test dataset - from ads.explanations.explainer import ADSExplainer - explainer = ADSExplainer(test, model, training_data=train) - - # With ADSExplainer, create a global explanation object using - # the MLXGlobalExplainer provider - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - global_explainer = explainer.global_explanation( - provider=MLXGlobalExplainer()) - - # A summary of the global feature permutation importance algorithm and - # how to interpret the output can be displayed with - global_explainer.feature_importance_summary() - - # Compute the global Feature Permutation Importance explanation - importances = global_explainer.compute_feature_importance() - - # ADS supports multiple visualizations for the global Feature - # Permutation Importance explanations (see "Interpretation" above) - - # Simple bar chart highlighting the average impact on model score - # across multiple iterations of the algorithm - importances.show_in_notebook() - -.. image:: figures/ads_mlx_titanic_pi_bar.png - -.. code-block:: python - - # Box plot highlighting the mean, median, quartiles, and min/max - # impact on model score across multiple iterations of the algorithm - importances.show_in_notebook('box_plot') - -.. image:: figures/ads_mlx_titanic_pi_box.png - -.. code-block:: python - - # Detailed scatter plot highlighting the individual impacts on - # model score across multiple iterations of the algorithm - importances.show_in_notebook('detailed') - -.. image:: figures/ads_mlx_titanic_pi_scatter.png - -.. code-block:: python - - # The raw explanaiton data used to generate the visualizations, as well - # as the runtime performance information can be extracted with - importances.get_diagnostics() - -.. image:: figures/ads_mlx_titanic_pi_diagnostics.png - -References ----------- - -- `perutation importance `_ - -- `feature importance `_ - -- `Vanderbilt Biostatistics - titanic data `_ diff --git a/docs/source/user_guide/model_explainability/whatif.rst b/docs/source/user_guide/model_explainability/whatif.rst deleted file mode 100644 index 25961dcf4..000000000 --- a/docs/source/user_guide/model_explainability/whatif.rst +++ /dev/null @@ -1,97 +0,0 @@ -WhatIf Explainer -**************** - -Description -=========== - -The WhatIf explainer tool helps to understand how changes in an observation affect a model's prediction. Use it to explore a model's behavior on a single observation or the entire dataset by asking "what if" questions. - -The WhatIf explainer has the following methods: - -* ``explore_predictions``: Explore the relationship between feature values and the model predictions. -* ``explore_sample``: Modify the values in an observation and see how the prediction changes. - -Example -======== - -In this example, a WhatIf explainer is created, and then the ``explore_predictions()``, and ``explore_sample()`` methods are demonstrated. A tree-based model is used to make predictions on the Boston housing dataset. - -.. code-block:: python3 - - from ads.common.model import ADSModel - from ads.dataset.dataset_browser import DatasetBrowser - from ads.dataset.label_encoder import DataFrameLabelEncoder - from ads.explanations.explainer import ADSExplainer - from ads.explanations.mlx_whatif_explainer import MLXWhatIfExplainer - from sklearn.ensemble import ExtraTreesRegressor - from sklearn.pipeline import make_pipeline - from sklearn.preprocessing import LabelEncoder - import logging - import warnings - - logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.ERROR) - warnings.filterwarnings('ignore') - - ds = DatasetBrowser.sklearn().open("boston").set_target("target") - train, test = ds.train_test_split(test_size=0.2) - - X_boston = train.X.copy() - y_boston = train.y.copy() - - le = DataFrameLabelEncoder() - X_boston = le.fit_transform(X_boston) - - # Model Training - ensemble_regressor = ExtraTreesRegressor(n_estimators=245, random_state=42) - ensemble_regressor.fit(X_boston, y_boston) - model = ADSModel.from_estimator(make_pipeline(le, ensemble_regressor), name="ExtraTreesRegressor") - - # Build a WhatIf Explainer - explainer = ADSExplainer(test, model, training_data=train) - whatif_explainer = explainer.whatif_explanation(provider=MLXWhatIfExplainer()) - -The Sample Explorer method, ``explore_sample()``, opens a GUI that has a single observation. The values of that sample can then be changed. By clicking **Run Inference**, the model computes the prediction with the updated feature values. The interface shows the original values and the values that have been changed. - -``example_sample()`` accepts the ``row_idx`` parameter that specifies the index of the observation that is to be evaluated. The default is zero (0). The ``features`` parameter lists the feature names that are shown in the interface. By default, it displays all features. For datasets with a large number of features, this can be cumbersome so the ``max_features`` parameter can be used to display only the first *n* features. - -The following command opens the Sample Explorer. Change the values then click **Run Inference** to see how the prediction changes. - -.. code-block:: python3 - - whatif_explainer.explore_sample() - -.. image:: figures/ads_mlx_boston_whatif_explore_sample.png - - -The Predictions Explorer method, ``explore_predictions()``, allows the exploration of model predictions across either the marginal distribution (1-feature) or the joint distribution (2-features). - -The method ``explore_predictions()`` has several optional parameters including: - -* ``discretization``: (str, optional) Discretization method applies the x-axis if the feature ``x`` is continuous. The valid options are 'quartile', 'decile', or 'percentile'. The default is None. -* ``label``: (str or int, optional) Target label or target class name to explore only for classification problems. The default is None. -* ``plot_type``: (str, optional) Type of plot. For classification problems the valid options are 'scatter', 'box', or 'bar'. For a regression problem, the valid options are 'scatter' or 'box'. The default is 'scatter'. -* ``x``: (str, optional) Feature column on x-axis. The default is None. -* ``y``: (str, optional) Feature column or model prediction column on the y-axis, by default it is the target. - -When only ``x`` is set, the chart shows the relationship between the features ``x`` and the target ``y``. - -.. code-block:: python3 - - whatif_explainer.explore_predictions(x='AGE') - -.. image:: figures/ads_mlx_boston_whatif_explore_predictions_1.png - -If features are specified for both ``x`` and ``y``, the plot uses color to indicate the value of the target. - -.. code-block:: python3 - - whatif_explainer.explore_predictions(x='AGE', y='CRIM') - -.. image:: figures/ads_mlx_boston_whatif_explore_predictions_2.png - -.. code-block:: python3 - - whatif_explainer.explore_predictions(x='RAD', plot_type='box', discretization='decile') - -.. image:: figures/ads_mlx_boston_whatif_explore_predictions_3.png - diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index 3d9ca7d77..407662246 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -17,7 +17,7 @@ The following steps take your trained ``AutoML`` model and deploy it into produc **Creating an Oracle Labs AutoML Model** -Create an ``OracleAutoMLProvider`` object and use it to define how an Oracle Labs ``AutoML`` model is trained. +Train a model using AutoMLx. .. code-block:: python3 diff --git a/docs/source/user_guide/model_training/automl/overview.rst b/docs/source/user_guide/model_training/automl/overview.rst index e49f56840..f7683bf69 100644 --- a/docs/source/user_guide/model_training/automl/overview.rst +++ b/docs/source/user_guide/model_training/automl/overview.rst @@ -12,7 +12,6 @@ An AutoML Pipeline consists of these four main stages: The stages operate in sequence:  .. contents:: - ------------------- Algorithm Selection ------------------- diff --git a/docs/source/user_guide/overview/overview.rst b/docs/source/user_guide/overview/overview.rst index 198230c4a..37e4a18cf 100644 --- a/docs/source/user_guide/overview/overview.rst +++ b/docs/source/user_guide/overview/overview.rst @@ -85,34 +85,6 @@ Main Features Save and load a copy of any dataset in binary optimized Parquet format. By snapshotting a dataset, a URL is returned that can be used by anyone with access to the resource to load the data exactly how it was at that point with all transforms materialized. -* **Model Training** - - .. figure:: figures/dot-decision-tree.png - :align: center - - **Example showing a visualized Decision Tree** - - The Oracle AutoML engine, that produces ``ADSModel`` models, automates: - - - Feature Selection - - Algorithm Selection - - Feature Encoding - - Hyperparameter Tuning - - - Create your own models using any library. If they resemble ``sklearn`` estimators, you can promote them to ``ADSModel`` objects and use them in evaluations, explanations, and model catalog operations. If they do not support the ``sklearn`` behavior, you can wrap them in a Lambda then use them. - - .. figure:: figures/automl.png - :align: center - - **Example showing how to invoke AutoML** - - .. figure:: figures/automl-hyperparameter-tuning.png - :align: center - - **Example showing the AutoML hyper-parameter tuning trials** - - * **Model Evaluations** Model evaluation generates a comprehensive suite of evaluation metrics and suitable visualizations to measure model performance against new data, and can rank models over time to ensure optimal behavior in production. Model evaluation goes beyond raw performance to take into account expected baseline behavior. It uses a cost API so that the different impacts of false positives and false negatives can be fully incorporated. @@ -129,15 +101,6 @@ Main Features **Example showing some model evaluation plots** -* **Model Interpretation and Explainablility** - - Model explanation makes it easier to understand why machine learning models return the results that they do by identifying relative importance of features and relationships between features and predictions. Data Science offers the first commercial implementation - of model-agnostic explanation. For example, a compliance officer can be certain that a model is not making decisions in violation of GDPR or regulations against discrimination. - - For data scientists, it enables them to ensure that any model they build is generating results based on predictors that make sense. Understanding why a model behaves the way it does is critical to users and regulators. Data Science ensures that deployed models are more accurate, robust, and compliant with relevant regulations. - - Oracle provides Machine Learning Explainability (MLX), which is a package that explains the internal mechanics of a machine learning system to better understand models. Models are in the ``ADSModel`` format. You use MLX to explain models from different training platforms. You create an ``ADSModel`` from a REST end point then use the ADS model explainability to explain a model that's remote. - * **Interact with the Model Catalog** diff --git a/docs/source/user_guide/quickstart/quickstart.rst b/docs/source/user_guide/quickstart/quickstart.rst index 9d0e6f189..44063a493 100644 --- a/docs/source/user_guide/quickstart/quickstart.rst +++ b/docs/source/user_guide/quickstart/quickstart.rst @@ -20,7 +20,7 @@ For a guide on ADS features, check out the overview. This Quick Start guide is a * `Model Training with ADS`_ * `Creating an ADSModel from Other Machine Learning Libraries`_ * `Saving and Loading Models to the Model Catalog`_ -* `Model Evaluations and Explanations with ADS`_ +* `Model Evaluations with ADS`_ Setting up ADS -------------- @@ -166,99 +166,11 @@ or the ``ads_data_visualizations`` notebook example in the notebook session envi :height: 150 :alt: ADS Model Training -Model Training with ADS ------------------------ - -ADS includes the ``Oracle AutoML Provider``. It is an automated machine learning module that is simple to use, fast to -run, and performs comparably with its alternatives. You can also create your own machine learning provider and let ADS -take care of the housekeeping. - -Detailed examples are included in the ``ads-example`` folder in the notebook session environment. - -AutoML provides these features: - -- An ideal feature set. -- Minimal sampling size. -- The best algorithm to use (you can also restrict AutoML to your favorite algorithms). -- The best set of algorithm specific hyperparameters. - -How to train a model using ``ADSDataset``: - -.. code-block:: python3 - - import pandas as pd - from ads.automl.provider import OracleAutoMLProvider - from ads.automl.driver import AutoML - from ads.dataset.factory import DatasetFactory - - # this is the default AutoML provider for regression and classification problem types. - # over time Oracle will introduce other providers for other training tasks. - ml_engine = OracleAutoMLProvider() - - # use an example where Pandas opens the dataset - df = pd.read_csv("https://raw.githubusercontent.com/darenr/public_datasets/master/iris_dataset.csv") - ds = DatasetFactory.open(df, target='variety') - - train, test = ds.train_test_split() - - automl = AutoML(train, provider=ml_engine) - - model, baseline = automl.train(model_list=[ - 'LogisticRegression', - 'LGBMClassifier', - 'XGBClassifier', - 'RandomForestClassifier'], time_budget=10) - -At this point, AutoML has built a baseline model. In this -case, it is a Zero-R model (majority class is always predicted), along with a tuned model. - -You can use ``print(model)`` to get a model's parameters and their values: - -.. code-block:: python3 - - print(model) - - -.. code-block:: python3 - - Framework: automl.models.classification.sklearn.lgbm - Estimator class: LGBMClassifier - Model Parameters: {'boosting_type': 'dart', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0} - - -You can get details about a model, such as its selected algorithm, training data size, -and initial features using the ``show_in_notebook()`` method: - -.. code-block:: python3 - - model.show_in_notebook() - - -.. code-block:: python3 - - Model Name AutoML Classifier - Target Variable variety - Selected Algorithm LGBMClassifier - Task classification - Training Dataset Size (128, 4) - CV 5 - Optimization Metric recall_macro - Selected Hyperparameters {'boosting_type': 'dart', 'class_weight': None, 'learning_rate': 0.1, 'max_depth': -1, 'min_child_weight': 0.001, 'n_estimators': 100, 'num_leaves': 31, 'reg_alpha': 0, 'reg_lambda': 0} - Is Regression None - Initial Number of Features 4 - Initial Features [sepal.length, sepal.width, petal.length, petal.width] - Selected Number of Features 1 - Selected Features [petal.width] - - -From here you have two ``ADSModel`` objects that can be used in ADS's evaluation and explanation -modules along with any other ``ADSModel`` instances. - Creating an ADSModel from Other Machine Learning Libraries ---------------------------------------------------------- -You are not limited to using models that were created using Oracle AutoML. You can `promote` other models to ADS +You can `promote` models to ADS so that they too can be used in evaluations and explanations. ADS provides a static method that promotes an estimator-like object to an ``ADSModel``. @@ -348,7 +260,7 @@ Then construct or reconstruct the ``ADSModel`` object with: There's more details to interacting with the model catalog in :ref:`Model Catalog `. -Model Evaluations and Explanations with ADS +Model Evaluations with ADS ------------------------------------------- Model Evaluations @@ -390,82 +302,3 @@ the ``calculate_cost()`` method. You can also add in your own custom metrics, see the :ref:`Model Evaluation ` for more details. - -Model Explanations -================== - -ADS provides a module called Machine learning explainability (MLX), which is the process -of explaining and interpreting machine learning and deep learning models. - -MLX can help machine learning developers to: - - - Better understand and interpret the model's behavior. For example: - - Which features does the model consider important? - - What is the relationship between the feature values and the target predictions? - - - Debug and improve the quality of the model. For example: - - Did the model learn something unexpected? - - Does the model generalize or did it learn something specific to the train/validation/test datasets? - - - Increase confidence in deploying the model. - -MLX can help end users of machine learning algorithms to: - - - Understand why the model has made a certain prediction. For example: - - Why was my bank loan denied? - - -Some useful terms for MLX: - - - **Explainability**: The ability to explain the reasons behind a machine learning model’s prediction. - - **Interpretability**: The level at which a human can understand the explanation. - - **Global Explanations**: Understand the behavior of a machine learning model as a whole. - - **Local Explanations**: Understand why the machine learning model made a single prediction. - - **Model-Agnostic Explanations**: Explanations treat the machine learning model (and feature pre-processing) as a black-box, - instead of using properties from the model to guide the explanation. - -MLX provides interpretable model-agnostic local and global explanations. - -How to get global explanations: - -.. code-block:: python3 - - from ads.explanations.explainer import ADSExplainer - from ads.explanations.mlx_global_explainer import MLXGlobalExplainer - - # our model explainer class - explainer = ADSExplainer(test, model) - - # let's created a global explainer - global_explainer = explainer.global_explanation(provider=MLXGlobalExplainer()) - - # Generate the global feature importance explanation - importances = global_explainer.compute_feature_importance() - -Visualize the top six features in a bar chart (the default). - -.. code-block:: python3 - - # Visualize the top 6 features as a bar chart - importances.show_in_notebook(n_features=6) - -Visualize the top five features in a detailed scatter plot: - -.. code-block:: python3 - - # Visualize a detailed scatter plot - importances.show_in_notebook(n_features=5, mode='detailed') - -Get the dictionary object that is used to generate the visualizations so that you can create your own: - -.. code-block:: python3 - - # Get the dictionary object used to generate the visualizations - importances.get_global_explanation() - -MLX can also do much more. For example, Partial Dependence Plots (PDP) and Individual -Conditional Expectation explanations along with local explanations can provide insights -into why a machine learning model made a specific prediction. - -For more detailed examples and a thorough overview of MLX, see the :ref:`MLX documentation ` and -the ``ads_OracleMLXProvider`` examples in the ``ads-example`` folder of the notebook session environment. From ed5585f6ffb4fd2bb0296edea48e9be727cf4986 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:41:31 -0800 Subject: [PATCH 067/147] update the example --- .../frameworks/automlmodel.rst | 141 ++++++++++------ .../model_serialization/automlmodel.rst | 156 ++++++++++++------ 2 files changed, 197 insertions(+), 100 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index 407662246..d5ad1e439 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -21,20 +21,44 @@ Train a model using AutoMLx. .. code-block:: python3 - import logging - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.dataset.dataset_browser import DatasetBrowser - - ds = DatasetBrowser.sklearn().open("wine").set_target("target") - train, test = ds.train_test_split(test_size=0.1, random_state = 42) - - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train( - model_list=['LogisticRegression', 'DecisionTreeClassifier'], - random_state = 42, time_budget = 500) - + import pandas as pd + import numpy as np + import tempfile + from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score + from sklearn.linear_model import LogisticRegression + from sklearn.compose import make_column_selector as selector + from sklearn.impute import SimpleImputer + from sklearn.preprocessing import StandardScaler, OneHotEncoder + from sklearn.compose import ColumnTransformer + from sklearn.pipeline import Pipeline + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + + import ads + import automl + from automl import init + from ads.model import AutoMLModel + from ads.common.model_metadata import UseCaseType + from ads.model.framework.automl_model import AutoMLModel + + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + init(engine='local') + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) Initialize ========== @@ -59,39 +83,56 @@ Example .. code-block:: python3 - import logging - import tempfile - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.common.model_metadata import UseCaseType - from ads.dataset.dataset_browser import DatasetBrowser - from ads.model.framework.automl_model import AutoMLModel - - ds = DatasetBrowser.sklearn().open("wine").set_target("target") - train, test = ds.train_test_split(test_size=0.1, random_state = 42) - - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train( - model_list=['LogisticRegression', 'DecisionTreeClassifier'], - random_state = 42, - time_budget = 500 - ) - - artifact_dir = tempfile.mkdtemp() - automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) - automl_model.prepare( - inference_conda_env="generalml_p38_cpu_v1", - training_conda_env="generalml_p38_cpu_v1", - use_case_type=UseCaseType.BINARY_CLASSIFICATION, - X_sample=test.X, - force_overwrite=True, - training_id=None - ) - automl_model.verify(test.X.iloc[:10]) - model_id = automl_model.save(display_name='Demo AutoMLModel model') - deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') - automl_model.predict(test.X.iloc[:10]) - automl_model.delete_deployment(wait_for_completion=True) - + import pandas as pd + import numpy as np + import tempfile + from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score + from sklearn.linear_model import LogisticRegression + from sklearn.compose import make_column_selector as selector + from sklearn.impute import SimpleImputer + from sklearn.preprocessing import StandardScaler, OneHotEncoder + from sklearn.compose import ColumnTransformer + from sklearn.pipeline import Pipeline + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + + import ads + import automl + from automl import init + from ads.model import AutoMLModel + from ads.common.model_metadata import UseCaseType + from ads.model.framework.automl_model import AutoMLModel + + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + init(engine='local') + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + + ads.set_auth("resource_principal") + artifact_dir = tempfile.mkdtemp() + automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) + automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", + training_conda_env="automlx_p38_cpu_v1", + use_case_type=UseCaseType.BINARY_CLASSIFICATION, + X_sample=X_test, + force_overwrite=True) + automl_model.verify(X_test.iloc[:2]) + model_id = automl_model.save(display_name='Demo AutoMLModel model') + deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') + automl_model.predict(X_test.iloc[:2]) + automl_model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) diff --git a/docs/source/user_guide/model_serialization/automlmodel.rst b/docs/source/user_guide/model_serialization/automlmodel.rst index ce49f2fc7..f9c1bd973 100644 --- a/docs/source/user_guide/model_serialization/automlmodel.rst +++ b/docs/source/user_guide/model_serialization/automlmodel.rst @@ -15,23 +15,63 @@ The following steps take your trained ``AutoML`` model and deploy it into produc **Creating an Oracle Labs AutoML Model** -Create an ``OracleAutoMLProvider`` object and use it to define how an Oracle Labs ``AutoML`` model is trained. +Train a model using AutoMLx. .. code-block:: python3 - import logging - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.dataset.dataset_browser import DatasetBrowser - - ds = DatasetBrowser.sklearn().open("wine").set_target("target") - train, test = ds.train_test_split(test_size=0.1, random_state = 42) + import pandas as pd + import numpy as np + import tempfile + from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score + from sklearn.linear_model import LogisticRegression + from sklearn.compose import make_column_selector as selector + from sklearn.impute import SimpleImputer + from sklearn.preprocessing import StandardScaler, OneHotEncoder + from sklearn.compose import ColumnTransformer + from sklearn.pipeline import Pipeline + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + + import ads + import automl + from automl import init + from ads.model import AutoMLModel + from ads.common.model_metadata import UseCaseType + from ads.model.framework.automl_model import AutoMLModel - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train( - model_list=['LogisticRegression', 'DecisionTreeClassifier'], - random_state = 42, time_budget = 500) + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + init(engine='local') + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + + ads.set_auth("resource_principal") + artifact_dir = tempfile.mkdtemp() + automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) + automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", + training_conda_env="automlx_p38_cpu_v1", + use_case_type=UseCaseType.BINARY_CLASSIFICATION, + X_sample=X_test, + force_overwrite=True) + automl_model.verify(X_test.iloc[:2]) + model_id = automl_model.save(display_name='Demo AutoMLModel model') + deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') + automl_model.predict(X_test.iloc[:2]) + automl_model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) Initialize @@ -143,41 +183,57 @@ Example .. code-block:: python3 - import logging - import tempfile - - from ads.automl.driver import AutoML - from ads.automl.provider import OracleAutoMLProvider - from ads.common.model_metadata import UseCaseType - from ads.dataset.dataset_browser import DatasetBrowser - from ads.model.framework.automl_model import AutoMLModel - from ads.catalog.model import ModelCatalog - - ds = DatasetBrowser.sklearn().open("wine").set_target("target") - train, test = ds.train_test_split(test_size=0.1, random_state = 42) - - ml_engine = OracleAutoMLProvider(n_jobs=-1, loglevel=logging.ERROR) - oracle_automl = AutoML(train, provider=ml_engine) - model, baseline = oracle_automl.train( - model_list=['LogisticRegression', 'DecisionTreeClassifier'], - random_state = 42, - time_budget = 500 - ) - - artifact_dir = tempfile.mkdtemp() - automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) - automl_model.prepare( - inference_conda_env="generalml_p37_cpu_v1", - training_conda_env="generalml_p37_cpu_v1", - use_case_type=UseCaseType.BINARY_CLASSIFICATION, - X_sample=test.X, - force_overwrite=True, - training_id=None - ) - automl_model.verify(test.X.iloc[:10]) - model_id = automl_model.save(display_name='Demo AutoMLModel model') - deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') - automl_model.predict(test.X.iloc[:10]) - automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + import pandas as pd + import numpy as np + import tempfile + from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score + from sklearn.linear_model import LogisticRegression + from sklearn.compose import make_column_selector as selector + from sklearn.impute import SimpleImputer + from sklearn.preprocessing import StandardScaler, OneHotEncoder + from sklearn.compose import ColumnTransformer + from sklearn.pipeline import Pipeline + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + + import ads + import automl + from automl import init + from ads.model import AutoMLModel + from ads.common.model_metadata import UseCaseType + from ads.model.framework.automl_model import AutoMLModel + + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + init(engine='local') + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + + ads.set_auth("resource_principal") + artifact_dir = tempfile.mkdtemp() + automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) + automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", + training_conda_env="automlx_p38_cpu_v1", + use_case_type=UseCaseType.BINARY_CLASSIFICATION, + X_sample=X_test, + force_overwrite=True) + automl_model.verify(X_test.iloc[:2]) + model_id = automl_model.save(display_name='Demo AutoMLModel model') + deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') + automl_model.predict(X_test.iloc[:2]) + automl_model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) From 193311cc49e7f31be4b0a7d78c8d8e95eb9fb8b1 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 26 Feb 2023 23:44:50 -0800 Subject: [PATCH 068/147] update the example --- .../frameworks/automlmodel.rst | 2 +- .../model_registration/quick_start.rst | 60 +++++++++++++++++++ .../model_serialization/automlmodel.rst | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index d5ad1e439..fcb29cb28 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -83,7 +83,7 @@ Example .. code-block:: python3 - import pandas as pd + import pandas as pd import numpy as np import tempfile from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 2c066cd44..1b567366e 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -257,6 +257,66 @@ Create a model, prepare it, verify that it works, save it to the model catalog, #Register TensorFlow model model_id = tf_model.save(display_name="TensorFlow Model") +AutoMLx Frameworks +------------------ + +.. code-block:: python3 + + import pandas as pd + import numpy as np + import tempfile + from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score + from sklearn.linear_model import LogisticRegression + from sklearn.compose import make_column_selector as selector + from sklearn.impute import SimpleImputer + from sklearn.preprocessing import StandardScaler, OneHotEncoder + from sklearn.compose import ColumnTransformer + from sklearn.pipeline import Pipeline + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + + import ads + import automl + from automl import init + from ads.model import AutoMLModel + from ads.common.model_metadata import UseCaseType + from ads.model.framework.automl_model import AutoMLModel + + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + init(engine='local') + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + + ads.set_auth("resource_principal") + artifact_dir = tempfile.mkdtemp() + automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) + automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", + training_conda_env="automlx_p38_cpu_v1", + use_case_type=UseCaseType.BINARY_CLASSIFICATION, + X_sample=X_test, + force_overwrite=True) + automl_model.verify(X_test.iloc[:2]) + model_id = automl_model.save(display_name='Demo AutoMLModel model') + deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') + automl_model.predict(X_test.iloc[:2]) + automl_model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + + Other Frameworks ---------------- diff --git a/docs/source/user_guide/model_serialization/automlmodel.rst b/docs/source/user_guide/model_serialization/automlmodel.rst index f9c1bd973..1cb860190 100644 --- a/docs/source/user_guide/model_serialization/automlmodel.rst +++ b/docs/source/user_guide/model_serialization/automlmodel.rst @@ -183,7 +183,7 @@ Example .. code-block:: python3 - import pandas as pd + import pandas as pd import numpy as np import tempfile from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score From 46da9a418783a4898c4097fd6484312f90f8dd8e Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 11:56:57 -0500 Subject: [PATCH 069/147] Add quick_start_job.rst --- .../user_guide/jobs/tabs/quick_start_job.rst | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 docs/source/user_guide/jobs/tabs/quick_start_job.rst diff --git a/docs/source/user_guide/jobs/tabs/quick_start_job.rst b/docs/source/user_guide/jobs/tabs/quick_start_job.rst new file mode 100644 index 000000000..cf35a6bd1 --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/quick_start_job.rst @@ -0,0 +1,74 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, PythonRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + PythonRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch110_p38_cpu_v1") + # Source code of the job, can be local or remote. + .with_source("https://raw.githubusercontent.com/oracle-samples/oci-data-science-ai-samples/432357b123b7401ef67b116fe19aec217ca920f0/jobs/python/job%2Bsamples/greeting-env-cmd.py") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument + .with_argument(greeting="Good morning") + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: python + spec: + args: + - --greeting + - Good morning + conda: + slug: pytorch110_p38_cpu_v1 + type: service + env: + - name: NAME + value: Welcome to OCI Data Science. + scriptPathURI: https://raw.githubusercontent.com/oracle-samples/oci-data-science-ai-samples/432357b123b7401ef67b116fe19aec217ca920f0/jobs/python/job%2Bsamples/greeting-env-cmd.py From 3b92fa290802c5fa701c30b6fea6e2965cede37e Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 11:57:54 -0500 Subject: [PATCH 070/147] Update run_notebook.rst --- docs/source/user_guide/jobs/run_notebook.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/jobs/run_notebook.rst b/docs/source/user_guide/jobs/run_notebook.rst index 72dc40e71..433664fac 100644 --- a/docs/source/user_guide/jobs/run_notebook.rst +++ b/docs/source/user_guide/jobs/run_notebook.rst @@ -35,7 +35,7 @@ Download the Outputs If you specify the output location using :py:meth:`~ads.jobs.NotebookRuntime.with_output`. All files in the working directory, including the notebook with outputs, will be saved to output location (``oci://bucket_name@namespace/path/to/dir``) after the job finishes running. -You can download the output by calling the :py:meth:`~ads.jobs.NotebookRuntime.download` method. +You can download the output by calling the :py:meth:`~ads.jobs.Job.download` method. Exclude Cells ============= From 0705361e7ab699809701e6f82a3f1182fbafbdd1 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 11:58:30 -0500 Subject: [PATCH 071/147] Update policies.rst and run_container.rst --- docs/source/user_guide/jobs/policies.rst | 10 +++++++++- docs/source/user_guide/jobs/run_container.rst | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/source/user_guide/jobs/policies.rst b/docs/source/user_guide/jobs/policies.rst index 2a5541682..5f6a9d9d7 100644 --- a/docs/source/user_guide/jobs/policies.rst +++ b/docs/source/user_guide/jobs/policies.rst @@ -3,7 +3,12 @@ IAM Policies Oracle Cloud Infrastructure Identity and Access Management (IAM) lets you specify policies to control the access to your cloud resources. -This section contain the policies recommended for Data Science Jobs. +This section describe the policies you might need for running Data Science Jobs. + +.. warning:: + + The policies presented in this page are intended to show the ``resource_type`` used by the job and job runs. + You should further restrict the access to the resources base on your needs. .. admonition:: Policy subject @@ -48,6 +53,9 @@ The following policy is needed for running a container job: See also: +* `Learn Best Practices for Setting Up Your Tenancy `_ +* `IAM with Identity Domains _` +* `IAM without Identity Domains _` * `Dynamic Group `_ * `Data Science Policies `_ * `Object Storage `_ diff --git a/docs/source/user_guide/jobs/run_container.rst b/docs/source/user_guide/jobs/run_container.rst index 6bf15faab..bbcfc3800 100644 --- a/docs/source/user_guide/jobs/run_container.rst +++ b/docs/source/user_guide/jobs/run_container.rst @@ -7,6 +7,9 @@ The :py:class:`~ads.jobs.ContainerRuntime` class allows you to run a container i To use the :py:class:`~ads.jobs.ContainerRuntime`, you need to first push the image to `OCI container registry `_. + + Note that you cannot build a docker image inside an OCI Data Science Notebook Session. + For more details, see: * `Creating a Repository `_ From b0dd2573f23c4d6ca0be1617caf7fcf5e38ae109 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 12:37:01 -0500 Subject: [PATCH 072/147] Merge infrastructure and runtime page --- .../user_guide/jobs/infra_and_runtime.rst | 260 ++++++++++++++++++ .../source/user_guide/jobs/infrastructure.rst | 100 ------- docs/source/user_guide/jobs/runtime.rst | 147 ---------- docs/source/user_guide/jobs/toc.rst | 3 +- 4 files changed, 261 insertions(+), 249 deletions(-) create mode 100644 docs/source/user_guide/jobs/infra_and_runtime.rst delete mode 100644 docs/source/user_guide/jobs/infrastructure.rst delete mode 100644 docs/source/user_guide/jobs/runtime.rst diff --git a/docs/source/user_guide/jobs/infra_and_runtime.rst b/docs/source/user_guide/jobs/infra_and_runtime.rst new file mode 100644 index 000000000..9e8a973f1 --- /dev/null +++ b/docs/source/user_guide/jobs/infra_and_runtime.rst @@ -0,0 +1,260 @@ +Infrastructure and Runtime +************************** + +This page describes the configurations of **Infrastructure** and **Runtime** defining a Data Science Job. + +.. include:: ../jobs/components/toc_local.rst + +Example +======= + +The following example configures the infrastructure and runtime to run a Python script. + +.. include:: ../jobs/tabs/quick_start_job.rst + +Infrastructure +============== + +The Data Science Job infrastructure is defined by a :py:class:`~ads.jobs.DataScienceJob` instance. +For example: + +.. code-block:: python3 + + from ads.jobs import DataScienceJob + + infrastructure = ( + DataScienceJob() + .with_compartment_id("") + .with_project_id("") + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum block storage size is 50 (GB) + .with_block_storage_size(50) + .with_log_group_id("") + .with_log_id("") + ) + +When creating a :py:class:`~ads.jobs.DataScienceJob` instance, the following configurations are required: + +* Compartment ID +* Project ID +* Compute Shape + +The following configurations are optional: + +* Block Storage Size, defaults to 50 (GB) +* Log Group ID +* Log ID + +For more details about the mandatory and optional parameters, see :py:class:`~ads.jobs.DataScienceJob`. + +Using Configurations from Notebook +---------------------------------- + +If you are creating a job from an OCI Data Science +`Notebook Session `_, +the same infrastructure configurations from the notebook session will be used as defaults. +You can initialize the :py:class:`~ads.jobs.DataScienceJob` +with the logging configurations and override the other options as needed. For example: + +.. code-block:: python3 + + from ads.jobs import DataScienceJob + + infrastructure = ( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # Use a GPU shape for the job, + # regardless of the shape used by the notebook session + .with_shape_name("VM.GPU2.1") + # compartment ID, project ID, subnet ID and block storage will be + # the same as the ones set in the notebook session + ) + +Compute Shapes +-------------- + +The :py:class:`~ads.jobs.DataScienceJob` class provides two static methods to obtain the support compute shapes: + +* You can get a list of currently supported compute shapes by calling + :py:meth:`~ads.jobs.DataScienceJob.instance_shapes`. +* can get a list of shapes that are available for fast launch by calling + :py:meth:`~ads.jobs.DataScienceJob.fast_launch_shapes`. + Specifying a fast launch shape will allow your job to start as fast as possible. + +Networking +---------- + +Data Science Job offers two types of networking: default networking (managed egress) and custom networking. +Default networking allows job runs to access public internet through a NAT gateway and OCI service through +a service gateway, both are configured automatically. Custom networking requires you to specify a subnet ID. +You can control the network access through the subnet and security lists. + +If you specified a subnet ID, your job will be configured to have custom networking. +Otherwise, default networking will be used. Note that when you are in a Data Science Notebook Session, +the same networking configuration is be used by default. +You can specify the networking manually by calling :py:meth:`~ads.jobs.DataScienceJob.with_job_infrastructure_type()`. + +Logging +------- + +Logging is not required to create the job. +However, it is highly recommended to enable logging for debugging and monitoring. + +In the preceding example, both the log OCID and corresponding log group OCID are specified +with the :py:class:`~ads.jobs.DataScienceJob` instance. +If your administrator configured the permission for you to search for logging resources, +you can skip specifying the log group OCID because ADS can automatically retrieve it. + +If you specify only the log group OCID and no log OCID, +a new Log resource is automatically created within the log group to store the logs, +see also `ADS Logging <../logging/logging.html>`_. + +Runtime +======= + +The *runtime* of a job defines the source code of your workload, environment variables, CLI arguments +and other configurations for the environment to run the workload. + +Depending on the source code, ADS provides different types of *runtime* for defining a data science job, +including: + +.. include:: ../jobs/components/runtime_types.rst + + +Environment Variables +--------------------- + +You can set environment variables for a runtime by calling +:py:meth:`~ads.jobs.PythonRuntime.with_environment_variable()`. +Environment variables enclosed by ``${...}`` will be substituted. For example: + +.. include:: ../jobs/tabs/runtime_envs.rst + +.. code-block:: python3 + + for k, v in runtime.environment_variables.items(): + print(f"{k}: {v}") + +will show the following environment variables for the runtime: + +.. code-block:: text + + HOST: 10.0.0.1 + PORT: 443 + URL: http://10.0.0.1:443/path/ + ESCAPED_URL: http://${HOST}:${PORT}/path/ + MISSING_VAR: This is ${UNDEFINED} + VAR_WITH_DOLLAR: $10 + DOUBLE_DOLLAR: $10 + +Note that: + +* You can use ``$$`` to escape the substitution. +* Undefined variable enclosed by ``${...}`` will be ignored. +* Double dollar signs ``$$`` will be substituted by a single one ``$``. + +See also: +`Service Provided Environment Variables `_ + + +.. _runtime_args: + +Command Line Arguments +---------------------- + +The command line arguments for running your script or function can be configured by calling +:py:meth:`~ads.jobs.PythonRuntime.with_argument()`. For example: + +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import PythonRuntime + + runtime = ( + PythonRuntime() + .with_source("oci://bucket_name@namespace/path/to/script.py") + .with_argument( + "arg1", "arg2", + key1="val1", + key2="val2" + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: runtime + type: python + spec: + scriptPathURI: oci://bucket_name@namespace/path/to/script.py + args: + - arg1 + - arg2 + - --key1 + - val1 + - --key2 + - val2 + +will configured the job to call your script by: + +.. code-block:: bash + + python script.py arg1 arg2 --key1 val1 --key2 val2 + +You can call :py:meth:`~ads.jobs.PythonRuntime.with_argument()` multiple times to set the arguments +to your desired order. You can check ``runtime.args`` to see the added arguments. + +Here are a few more examples: + +.. include:: ../jobs/tabs/runtime_args.rst + +Conda Environment +----------------- + +Except for :py:class:`~ads.jobs.ContainerRuntime`, +all the other runtime options allow you to configure a +`Conda Environment `_ +for your workload. You can use the slug name to specify a +`conda environment provided by the data science service +`_. +For example, to use the TensorFlow conda environment: + +.. include:: ../jobs/tabs/runtime_service_conda.rst + +You can also use a custom conda environment published to OCI Object Storage +by passing the ``uri`` to :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`, +for example: + +.. include:: ../jobs/tabs/runtime_custom_conda.rst + +By default, ADS will try to determine the region based on the authenticated API key or resource principal. +If your custom conda environment is stored in a different region, +you can specify the ``region`` when calling :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`. + +For more details on custom conda environment, see +`Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy +`__. + + +Override Configurations +----------------------- + +When you call :py:meth:`ads.jobs.Job.run`, a new job run will be started with the configuration defined in the **job**. +You may want to override the configuration with custom variables. For example, +you can customize job run display name, override command line argument, specify additional environment variables, +and add free form tags: + +.. code-block:: python3 + + job_run = job.run( + name="", + args="new_arg --new_key new_val", + env_var={"new_env": "new_val"}, + freeform_tags={"new_tag": "new_tag_val"} + ) diff --git a/docs/source/user_guide/jobs/infrastructure.rst b/docs/source/user_guide/jobs/infrastructure.rst deleted file mode 100644 index 3eeac37bc..000000000 --- a/docs/source/user_guide/jobs/infrastructure.rst +++ /dev/null @@ -1,100 +0,0 @@ -Infrastructure -************** - -The Data Science Job infrastructure is defined by a :py:class:`~ads.jobs.DataScienceJob` instance. -For example: - -.. code-block:: python3 - - from ads.jobs import DataScienceJob - - infrastructure = ( - DataScienceJob() - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - # Minimum block storage size is 50 (GB) - .with_block_storage_size(50) - .with_log_group_id("") - .with_log_id("") - ) - -When creating a :py:class:`~ads.jobs.DataScienceJob` instance, the following configurations are required: - -* Compartment ID -* Project ID -* Compute Shape - -The following configurations are optional: - -* Block Storage Size, defaults to 50 (GB) -* Log Group ID -* Log ID - -For more details about the mandatory and optional parameters, see :py:class:`~ads.jobs.DataScienceJob`. - -Using Configurations from Notebook -================================== - -If you are creating a job from an OCI Data Science -`Notebook Session `_, -the same infrastructure configurations from the notebook session will be used as defaults. -You can initialize the :py:class:`~ads.jobs.DataScienceJob` -with the logging configurations and override the other options as needed. For example: - -.. code-block:: python3 - - from ads.jobs import DataScienceJob - - infrastructure = ( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # Use a GPU shape for the job, - # regardless of the shape used by the notebook session - .with_shape_name("VM.GPU2.1") - # compartment ID, project ID, subnet ID and block storage will be - # the same as the ones set in the notebook session - ) - -Compute Shapes -============== - -The :py:class:`~ads.jobs.DataScienceJob` class provides two static methods to obtain the support compute shapes: - -* You can get a list of currently supported compute shapes by calling - :py:meth:`~ads.jobs.DataScienceJob.instance_shapes`. -* can get a list of shapes that are available for fast launch by calling - :py:meth:`~ads.jobs.DataScienceJob.fast_launch_shapes`. - Specifying a fast launch shape will allow your job to start as fast as possible. - -Networking -========== - -Data Science Job offers two types of networking: default networking (managed egress) and custom networking. -Default networking allows job runs to access public internet through a NAT gateway and OCI service through -a service gateway, both are configured automatically. Custom networking requires you to specify a subnet ID. -You can control the network access through the subnet and security lists. - -If you specified a subnet ID, your job will be configured to have custom networking. -Otherwise, default networking will be used. Note that when you are in a Data Science Notebook Session, -the same networking configuration is be used by default. -You can specify the networking manually by calling :py:meth:`~ads.jobs.DataScienceJob.with_job_infrastructure_type()`. - -Logging -======= - -Logging is not required to create the job. -However, it is highly recommended to enable logging for debugging and monitoring. - -In the preceding example, both the log OCID and corresponding log group OCID are specified -with the :py:class:`~ads.jobs.DataScienceJob` instance. -If your administrator configured the permission for you to search for logging resources, -you can skip specifying the log group OCID because ADS can automatically retrieve it. - -If you specify only the log group OCID and no log OCID, -a new Log resource is automatically created within the log group to store the logs, -see also `ADS Logging <../logging/logging.html>`_. diff --git a/docs/source/user_guide/jobs/runtime.rst b/docs/source/user_guide/jobs/runtime.rst deleted file mode 100644 index 0d4dade59..000000000 --- a/docs/source/user_guide/jobs/runtime.rst +++ /dev/null @@ -1,147 +0,0 @@ -Runtime -******* - -The *runtime* of a job defines the source code of your workload, environment variables, CLI arguments -and other configurations for the environment to run the workload. - -.. include:: ../jobs/components/toc_local.rst - -Depending on the source code, ADS provides different types of *runtime* for defining a data science job, -including: - -.. include:: ../jobs/components/runtime_types.rst - - -Environment Variables -===================== - -You can set environment variables for a runtime by calling -:py:meth:`~ads.jobs.PythonRuntime.with_environment_variable()`. -Environment variables enclosed by ``${...}`` will be substituted. For example: - -.. include:: ../jobs/tabs/runtime_envs.rst - -.. code-block:: python3 - - for k, v in runtime.environment_variables.items(): - print(f"{k}: {v}") - -will show the following environment variables for the runtime: - -.. code-block:: text - - HOST: 10.0.0.1 - PORT: 443 - URL: http://10.0.0.1:443/path/ - ESCAPED_URL: http://${HOST}:${PORT}/path/ - MISSING_VAR: This is ${UNDEFINED} - VAR_WITH_DOLLAR: $10 - DOUBLE_DOLLAR: $10 - -Note that: - -* You can use ``$$`` to escape the substitution. -* Undefined variable enclosed by ``${...}`` will be ignored. -* Double dollar signs ``$$`` will be substituted by a single one ``$``. - -See also: -`Service Provided Environment Variables `_ - - -.. _runtime_args: - -Command Line Arguments -====================== - -The command line arguments for running your script or function can be configured by calling -:py:meth:`~ads.jobs.PythonRuntime.with_argument()`. For example: - -.. tabs:: - - .. code-tab:: python - :caption: Python - - from ads.jobs import PythonRuntime - - runtime = ( - PythonRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_argument( - "arg1", "arg2", - key1="val1", - key2="val2" - ) - ) - - .. code-tab:: yaml - :caption: YAML - - kind: runtime - type: python - spec: - scriptPathURI: oci://bucket_name@namespace/path/to/script.py - args: - - arg1 - - arg2 - - --key1 - - val1 - - --key2 - - val2 - -will configured the job to call your script by: - -.. code-block:: bash - - python script.py arg1 arg2 --key1 val1 --key2 val2 - -You can call :py:meth:`~ads.jobs.PythonRuntime.with_argument()` multiple times to set the arguments -to your desired order. You can check ``runtime.args`` to see the added arguments. - -Here are a few more examples: - -.. include:: ../jobs/tabs/runtime_args.rst - -Conda Environment -================= - -Except for :py:class:`~ads.jobs.ContainerRuntime`, -all the other runtime options allow you to configure a -`Conda Environment `_ -for your workload. You can use the slug name to specify a -`conda environment provided by the data science service -`_. -For example, to use the TensorFlow conda environment: - -.. include:: ../jobs/tabs/runtime_service_conda.rst - -You can also use a custom conda environment published to OCI Object Storage -by passing the ``uri`` to :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`, -for example: - -.. include:: ../jobs/tabs/runtime_custom_conda.rst - -By default, ADS will try to determine the region based on the authenticated API key or resource principal. -If your custom conda environment is stored in a different region, -you can specify the ``region`` when calling :py:meth:`~ads.jobs.PythonRuntime.with_custom_conda`. - -For more details on custom conda environment, see -`Publishing a Conda Environment to an Object Storage Bucket in Your Tenancy -`__. - - -Override Configurations -======================= - -When you call :py:meth:`ads.jobs.Job.run`, a new job run will be started with the configuration defined in the **job**. -You may want to override the configuration with custom variables. For example, -you can customize job run display name, override command line argument, specify additional environment variables, -and add free form tags: - -.. code-block:: python3 - - job_run = job.run( - name="", - args="new_arg --new_key new_val", - env_var={"new_env": "new_val"}, - freeform_tags={"new_tag": "new_tag_val"} - ) diff --git a/docs/source/user_guide/jobs/toc.rst b/docs/source/user_guide/jobs/toc.rst index 1e6ffbbde..35d906fd1 100644 --- a/docs/source/user_guide/jobs/toc.rst +++ b/docs/source/user_guide/jobs/toc.rst @@ -3,8 +3,7 @@ ../jobs/data_science_job ../jobs/policies - ../jobs/infrastructure - ../jobs/runtime + ../jobs/infra_and_runtime ../jobs/run_python ../jobs/run_notebook ../jobs/run_script From 6ddb3ca09a1542133a017a8b2d8b55a2143ac7cf Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 12:40:59 -0500 Subject: [PATCH 073/147] Update data_science_job.rst and run_python.rst --- .../user_guide/jobs/data_science_job.rst | 86 ++++++++++++------- docs/source/user_guide/jobs/run_python.rst | 14 ++- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 24074a30f..f6cb53f96 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -8,44 +8,27 @@ Quick Start .. include:: ../jobs/components/toc_local.rst -Create and Run a Job -==================== +Define a Job +============ -In ADS, a job is defined by :doc:`infrastructure` and :doc:`runtime`. +In ADS, a job is defined by :doc:`infra_and_runtime`. The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.DataScienceJob` instance. The runtime can be an instance of: .. include:: ../jobs/components/runtime_types.rst -Here is an example to define and run a Python :py:class:`~ads.jobs.Job`: +Here is an example to define and run a Python :py:class:`~ads.jobs.Job` with the source code from an http +`link `_: -.. include:: ../jobs/tabs/python_runtime.rst +.. include:: ../jobs/tabs/quick_start_job.rst -For more details, see :doc:`infrastructure` configurations and see :doc:`runtime` configurations. +The source code can be a script, a Jupyter notebook, a folder or a zip file. +The source code location can be a local or remote, including OCI Object Storage. -In :py:class:`~ads.jobs.PythonRuntime`, -the ``entrypoint`` can be a Python script, a Python function or a Jupyter notebook. +The :py:class:`~ads.jobs.PythonRuntime` is designed for :doc:`Running a Python Workload `. +You can also :doc:`run_notebook`, :doc:`run_script` and :doc:`run_git` -Once the job is created, the job OCID can be accessed through ``job.id``. -Once the job run is created, the job run OCID can be accessed through ``run.id``. - -The :py:meth:`~ads.jobs.DataScienceJobRun.watch` method is useful to monitor the progress of the job run. -It will stream the logs to terminal and return once the job is finished. -Logging configurations are required for this method to show logs. - -Here is an example of the logs: - -.. code-block:: text - - 2021-10-28 17:17:58 - Job Run ACCEPTED - 2021-10-28 17:18:07 - Job Run ACCEPTED, Infrastructure provisioning. - 2021-10-28 17:19:19 - Job Run ACCEPTED, Infrastructure provisioned. - 2021-10-28 17:20:48 - Job Run ACCEPTED, Job run bootstrap starting. - 2021-10-28 17:23:41 - Job Run ACCEPTED, Job run bootstrap complete. Artifact execution starting. - 2021-10-28 17:23:50 - Job Run IN_PROGRESS, Job run artifact execution in progress. - 2021-10-28 17:23:50 - - 2021-10-28 17:23:50 - - 2021-10-28 17:23:50 - ... +For more details, see :doc:`infra_and_runtime` configurations. YAML @@ -87,8 +70,47 @@ For more details on ``ads opctl``, see :doc:`../cli/opctl/_template/jobs`. The job infrastructure, runtime and job run also support YAML serialization/deserialization. -Loading Existing Job or Job Run -=============================== +Run a Job and Monitor outputs +============================= + +Once the job is defined or loaded from YAML, you can call the :py:meth:`~ads.jobs.Job.create` method +to create the job on OCI. To start a job run, you can call the :py:meth:`~ads.jobs.Job.run` method, +which returns a :py:class:`~ads.jobs.DataScienceJobRun` instance. +Once the job or job run is created, the job OCID can be accessed through ``job.id`` or ``run.id``. + +The :py:meth:`~ads.jobs.DataScienceJobRun.watch` method is useful to monitor the progress of the job run. +It will stream the logs to terminal and return once the job is finished. +Logging configurations are required for this method to show logs. + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + +Here is an example of the logs: + +.. code-block:: text + + Job OCID: + Job Run OCID: + 2023-02-27 15:58:01 - Job Run ACCEPTED + 2023-02-27 15:58:11 - Job Run ACCEPTED, Infrastructure provisioning. + 2023-02-27 15:59:06 - Job Run ACCEPTED, Infrastructure provisioned. + 2023-02-27 15:59:29 - Job Run ACCEPTED, Job run bootstrap starting. + 2023-02-27 16:01:08 - Job Run ACCEPTED, Job run bootstrap complete. Artifact execution starting. + 2023-02-27 16:01:18 - Job Run IN_PROGRESS, Job run artifact execution in progress. + 2023-02-27 16:01:11 - Good morning, your environment variable has value of (Welcome to OCI Data Science.) + 2023-02-27 16:01:11 - Job Run 02-27-2023-16:01:11 + 2023-02-27 16:01:11 - Job Done. + 2023-02-27 16:01:22 - Job Run SUCCEEDED, Job run artifact execution succeeded. Infrastructure de-provisioning. + + +Load Existing Job or Job Run +============================ You can load an existing job or job run using the OCID from OCI: @@ -122,8 +144,8 @@ With a ``Job`` object, you can get a list of job runs: # Gets a list of job runs for a specific job. runs = job.run_list() -Deleting a Job or Job Run -========================= +Delete a Job or Job Run +======================= You can delete a job or job run by calling the ``delete()`` method. diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index 7e6c4d050..856b9e3c0 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -3,7 +3,7 @@ Run a Python Workload The :py:class:`~ads.jobs.PythonRuntime` is designed for running a Python workload. You can configure the environment variables, command line arguments and conda environment -as described in :doc:`runtime`. This section shows the additional enhancements provided by +as described in :doc:`infra_and_runtime`. This section shows the additional enhancements provided by :py:class:`~ads.jobs.PythonRuntime`. .. include:: ../jobs/components/toc_local.rst @@ -12,7 +12,17 @@ Here is an example to define and run a job using :py:class:`~ads.jobs.PythonRunt .. include:: ../jobs/tabs/python_runtime.rst -The :py:class:`~ads.jobs.PythonRuntime` uses an driver script from ADS as the entry point for the job run. +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + + +The :py:class:`~ads.jobs.PythonRuntime` uses an driver script from ADS for the job run. It performs additional operations before and after invoking your code. You can examine the driver script by downloading the job artifact from the OCI Console. From 611e946b2102533685d4b838e6b0ee4b291053a7 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 12:42:52 -0500 Subject: [PATCH 074/147] Update code examples. --- .../jobs/tabs/container_runtime.rst | 19 ++-- .../user_guide/jobs/tabs/git_runtime.rst | 19 ++-- .../user_guide/jobs/tabs/notebook_runtime.rst | 22 ++-- .../user_guide/jobs/tabs/python_runtime.rst | 103 ++++++++++-------- .../user_guide/jobs/tabs/runtime_args.rst | 2 +- .../user_guide/jobs/tabs/script_runtime.rst | 16 +-- .../user_guide/jobs/tabs/training_mnist.rst | 2 +- 7 files changed, 104 insertions(+), 79 deletions(-) diff --git a/docs/source/user_guide/jobs/tabs/container_runtime.rst b/docs/source/user_guide/jobs/tabs/container_runtime.rst index 3f702f1ca..5d0d39148 100644 --- a/docs/source/user_guide/jobs/tabs/container_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/container_runtime.rst @@ -32,13 +32,6 @@ ) ) - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - .. code-tab:: yaml :caption: YAML @@ -72,4 +65,14 @@ env: - name: GREETINGS value: Welcome to OCI Data Science - image: .ocir.io// \ No newline at end of file + image: .ocir.io// + + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() diff --git a/docs/source/user_guide/jobs/tabs/git_runtime.rst b/docs/source/user_guide/jobs/tabs/git_runtime.rst index 90a9e87a2..eaf396127 100644 --- a/docs/source/user_guide/jobs/tabs/git_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/git_runtime.rst @@ -41,13 +41,6 @@ ) ) - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - .. code-tab:: yaml :caption: YAML @@ -83,4 +76,14 @@ value: Welcome to OCI Data Science outputDir: beginner_source/examples_nn outputUri: oci://bucket_name@namespace/path/to/dir - url: https://github.com/pytorch/tutorials.git \ No newline at end of file + url: https://github.com/pytorch/tutorials.git + + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() diff --git a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst index ac47ff3dc..001075f0b 100644 --- a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst @@ -36,15 +36,6 @@ ) ) - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - # Download the notebook back to local - run.download("/path/to/local/dir") - .. code-tab:: yaml :caption: YAML @@ -82,4 +73,15 @@ - remove notebookEncoding: utf-8 notebookPathURI: https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb - outputUri: oci://bucket_name@namespace/path/to/dir \ No newline at end of file + outputUri: oci://bucket_name@namespace/path/to/dir + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() + # Download the notebook back to local + run.download("/path/to/local/dir") diff --git a/docs/source/user_guide/jobs/tabs/python_runtime.rst b/docs/source/user_guide/jobs/tabs/python_runtime.rst index e7d5b74fd..cdaeec539 100644 --- a/docs/source/user_guide/jobs/tabs/python_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/python_runtime.rst @@ -6,51 +6,48 @@ from ads.jobs import Job, DataScienceJob, PythonRuntime job = ( - Job(name="My Job") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - # For default networking, no need to specify subnet ID - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - PythonRuntime() - # Specify the service conda environment by slug name. - .with_service_conda("pytorch19_p37_cpu_v1") - # The job artifact can be a single Python script, a directory or a zip file. - .with_source("local/path/to/code_dir") - # Set the working directory - # When using a directory as source, the default working dir is the parent of code_dir. - # Working dir should be a relative path beginning from the source directory (code_dir) - .with_working_dir("code_dir") - # The entrypoint is applicable only to directory or zip file as source - # The entrypoint should be a path relative to the working dir. - # Here my_script.py is a file in the code_dir/my_package directory - .with_entrypoint("my_package/my_script.py") - # Add an additional Python path, relative to the working dir (code_dir/other_packages). - .with_python_path("other_packages") - # Copy files in "code_dir/output" to object storage after job finishes. - .with_output("output", "oci://bucket_name@namespace/path/to/dir") - ) + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + PythonRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument, arg1 --key arg2 + .with_argument("arg1", key="arg2") + # Set the working directory + # When using a directory as source, the default working dir is the parent of code_dir. + # Working dir should be a relative path beginning from the source directory (code_dir) + .with_working_dir("code_dir") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.py is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.py") + # Add an additional Python path, relative to the working dir (code_dir/other_packages). + .with_python_path("other_packages") + # Copy files in "code_dir/output" to object storage after job finishes. + .with_output("output", "oci://bucket_name@namespace/path/to/dir") + ) ) - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - .. code-tab:: yaml :caption: YAML @@ -77,13 +74,31 @@ kind: runtime type: python spec: + args: + - arg1 + - --key + - arg2 conda: slug: pytorch19_p37_cpu_v1 type: service entrypoint: my_package/my_script.py + env: + - name: NAME + value: Welcome to OCI Data Science. outputDir: output outputUri: oci://bucket_name@namespace/path/to/dir pythonPath: - other_packages scriptPathURI: local/path/to/code_dir - workingDir: code_dir \ No newline at end of file + workingDir: code_dir + workingDir: code_dir + + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() diff --git a/docs/source/user_guide/jobs/tabs/runtime_args.rst b/docs/source/user_guide/jobs/tabs/runtime_args.rst index 67beef924..8a7bbbadc 100644 --- a/docs/source/user_guide/jobs/tabs/runtime_args.rst +++ b/docs/source/user_guide/jobs/tabs/runtime_args.rst @@ -80,4 +80,4 @@ .. code-block:: python print(runtime.args) - # ["pos1", "--key1", "--key2", "val2", "pos2"] \ No newline at end of file + # ["pos1", "--key1", "--key2", "val2", "pos2"] diff --git a/docs/source/user_guide/jobs/tabs/script_runtime.rst b/docs/source/user_guide/jobs/tabs/script_runtime.rst index 41816f4df..d5b550a70 100644 --- a/docs/source/user_guide/jobs/tabs/script_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/script_runtime.rst @@ -36,13 +36,6 @@ ) ) - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - .. code-tab:: yaml :caption: YAML @@ -74,3 +67,12 @@ type: service entrypoint: my_package/my_script.sh scriptPathURI: local/path/to/code_dir + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() diff --git a/docs/source/user_guide/jobs/tabs/training_mnist.rst b/docs/source/user_guide/jobs/tabs/training_mnist.rst index a60799d6e..ea1589b73 100644 --- a/docs/source/user_guide/jobs/tabs/training_mnist.rst +++ b/docs/source/user_guide/jobs/tabs/training_mnist.rst @@ -75,4 +75,4 @@ value: Welcome to OCI Data Science outputDir: beginner_source/examples_nn outputUri: oci://bucket_name@namespace/path/to/dir - url: https://github.com/pytorch/tutorials.git \ No newline at end of file + url: https://github.com/pytorch/tutorials.git From d33be5951160c70bfb6d1a5109fc4c91ebc11943 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 12:43:10 -0500 Subject: [PATCH 075/147] Update runtime_types.rst and toc_local.rst --- docs/source/user_guide/jobs/components/runtime_types.rst | 1 + docs/source/user_guide/jobs/components/toc_local.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/user_guide/jobs/components/runtime_types.rst b/docs/source/user_guide/jobs/components/runtime_types.rst index aacbbdc06..bcfc65b76 100644 --- a/docs/source/user_guide/jobs/components/runtime_types.rst +++ b/docs/source/user_guide/jobs/components/runtime_types.rst @@ -1,2 +1,3 @@ .. include:: ../jobs/components/runtime_non_byoc.rst + * :py:class:`~ads.jobs.ContainerRuntime` for container images. diff --git a/docs/source/user_guide/jobs/components/toc_local.rst b/docs/source/user_guide/jobs/components/toc_local.rst index 1a10fe7b0..b70e7c035 100644 --- a/docs/source/user_guide/jobs/components/toc_local.rst +++ b/docs/source/user_guide/jobs/components/toc_local.rst @@ -1,4 +1,4 @@ .. contents:: In this Page - :depth: 2 + :depth: 3 :local: :backlinks: none \ No newline at end of file From b2de39fc407ba19af700ee09d65caf755f5c58c4 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 14:14:15 -0500 Subject: [PATCH 076/147] Update overview.rst, runtime_non_byoc.rst, and 12 more files... --- .../source/user_guide/jobs/data_science_job.rst | 17 +++++++++-------- docs/source/user_guide/jobs/index.rst | 17 +++++++++++++++-- .../user_guide/jobs/infra_and_runtime.rst | 4 ++-- .../jobs/{components => }/overview.rst | 3 ++- docs/source/user_guide/jobs/run_python.rst | 2 +- .../jobs/{components => }/runtime_non_byoc.rst | 8 ++++---- .../jobs/{components => }/runtime_types.rst | 2 +- docs/source/user_guide/jobs/toc.rst | 14 -------------- .../jobs/{components => }/toc_local.rst | 0 .../user_guide/pipeline/pipeline_step.rst | 6 +++--- 10 files changed, 37 insertions(+), 36 deletions(-) rename docs/source/user_guide/jobs/{components => }/overview.rst (92%) rename docs/source/user_guide/jobs/{components => }/runtime_non_byoc.rst (79%) rename docs/source/user_guide/jobs/{components => }/runtime_types.rst (54%) delete mode 100644 docs/source/user_guide/jobs/toc.rst rename docs/source/user_guide/jobs/{components => }/toc_local.rst (100%) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index f6cb53f96..717e86467 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -6,7 +6,7 @@ Quick Start Before creating a job, ensure that you have policies configured for Data Science resources. See also: :doc:`policies` and `About Data Science Policies `_. -.. include:: ../jobs/components/toc_local.rst +.. include:: ../jobs/toc_local.rst Define a Job ============ @@ -15,20 +15,21 @@ In ADS, a job is defined by :doc:`infra_and_runtime`. The Data Science Job infrastructure is configured through a :py:class:`~ads.jobs.DataScienceJob` instance. The runtime can be an instance of: -.. include:: ../jobs/components/runtime_types.rst +.. include:: ../jobs/runtime_types.rst -Here is an example to define and run a Python :py:class:`~ads.jobs.Job` with the source code from an http -`link `_: +Here is an example to define and run a Python :py:class:`~ads.jobs.Job`. .. include:: ../jobs/tabs/quick_start_job.rst -The source code can be a script, a Jupyter notebook, a folder or a zip file. -The source code location can be a local or remote, including OCI Object Storage. - The :py:class:`~ads.jobs.PythonRuntime` is designed for :doc:`Running a Python Workload `. -You can also :doc:`run_notebook`, :doc:`run_script` and :doc:`run_git` +The source code is specified by :py:meth:`~ads.jobs.PythonRuntime.with_source` (``path/to/script.py``). +It can be a script, a Jupyter notebook, a folder or a zip file. +The source code location can be a local or remote, including HTTP URL and OCI Object Storage. +An `example Python script `_ +is available on `Data Science AI Sample GitHub Repository `_. For more details, see :doc:`infra_and_runtime` configurations. +You can also :doc:`run_notebook`, :doc:`run_script` and :doc:`run_git`. YAML diff --git a/docs/source/user_guide/jobs/index.rst b/docs/source/user_guide/jobs/index.rst index 669caec27..1e714286c 100644 --- a/docs/source/user_guide/jobs/index.rst +++ b/docs/source/user_guide/jobs/index.rst @@ -2,6 +2,19 @@ Data Science Jobs ################# -.. include:: ../jobs/components/overview.rst +.. include:: ../jobs/overview.rst -.. include:: ../jobs/toc.rst +.. toctree:: + :maxdepth: 1 + + ../jobs/data_science_job + ../jobs/policies + ../jobs/infra_and_runtime + ../jobs/run_python + ../jobs/run_notebook + ../jobs/run_script + ../jobs/run_container + ../jobs/run_git + ../cli/opctl/_template/jobs + ../cli/opctl/_template/monitoring + ../cli/opctl/localdev/local_jobs diff --git a/docs/source/user_guide/jobs/infra_and_runtime.rst b/docs/source/user_guide/jobs/infra_and_runtime.rst index 9e8a973f1..9f559bb74 100644 --- a/docs/source/user_guide/jobs/infra_and_runtime.rst +++ b/docs/source/user_guide/jobs/infra_and_runtime.rst @@ -3,7 +3,7 @@ Infrastructure and Runtime This page describes the configurations of **Infrastructure** and **Runtime** defining a Data Science Job. -.. include:: ../jobs/components/toc_local.rst +.. include:: ../jobs/toc_local.rst Example ======= @@ -122,7 +122,7 @@ and other configurations for the environment to run the workload. Depending on the source code, ADS provides different types of *runtime* for defining a data science job, including: -.. include:: ../jobs/components/runtime_types.rst +.. include:: ../jobs/runtime_types.rst Environment Variables diff --git a/docs/source/user_guide/jobs/components/overview.rst b/docs/source/user_guide/jobs/overview.rst similarity index 92% rename from docs/source/user_guide/jobs/components/overview.rst rename to docs/source/user_guide/jobs/overview.rst index 93019b8ef..fa209af8c 100644 --- a/docs/source/user_guide/jobs/components/overview.rst +++ b/docs/source/user_guide/jobs/overview.rst @@ -20,7 +20,8 @@ You can also sequence jobs and keep the state by writing state information to `Object Storage `_ For example, you may want to experiment with how different model classes perform on the same training data -by using the [ADSTuner](https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/ads_tuner.html) to perform hyperparameter tuning on each model class. +by using the `ADSTuner `_ +to perform hyperparameter tuning on each model class. You could do this in parallel by having a different job run for each class of models. For a given job run, you could pass an environment variable that identifies the model class that you want to use. Each model can write its results to the Logging service or Object Storage. diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index 856b9e3c0..aed2232b0 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -6,7 +6,7 @@ You can configure the environment variables, command line arguments and conda en as described in :doc:`infra_and_runtime`. This section shows the additional enhancements provided by :py:class:`~ads.jobs.PythonRuntime`. -.. include:: ../jobs/components/toc_local.rst +.. include:: ../jobs/toc_local.rst Here is an example to define and run a job using :py:class:`~ads.jobs.PythonRuntime`: diff --git a/docs/source/user_guide/jobs/components/runtime_non_byoc.rst b/docs/source/user_guide/jobs/runtime_non_byoc.rst similarity index 79% rename from docs/source/user_guide/jobs/components/runtime_non_byoc.rst rename to docs/source/user_guide/jobs/runtime_non_byoc.rst index cb5e3f286..ae5760a76 100644 --- a/docs/source/user_guide/jobs/components/runtime_non_byoc.rst +++ b/docs/source/user_guide/jobs/runtime_non_byoc.rst @@ -1,11 +1,11 @@ * :py:class:`~ads.jobs.PythonRuntime` for Python code stored locally, OCI object storage, or other remote location supported by - `fsspec `_. See :doc:`run_python`. + `fsspec `_. See :doc:`../jobs/run_python`. * :py:class:`~ads.jobs.GitPythonRuntime` - for Python code from a Git repository. See :doc:`run_git`. + for Python code from a Git repository. See :doc:`../jobs/run_git`. * :py:class:`~ads.jobs.NotebookRuntime` for a single Jupyter notebook stored locally, OCI object storage, or other remote location supported by - `fsspec `_. See :doc:`run_notebook`. + `fsspec `_. See :doc:`../jobs/run_notebook`. * :py:class:`~ads.jobs.ScriptRuntime` for bash or shell scripts stored locally, OCI object storage, or other remote location supported by - `fsspec `_. See :doc:`run_script`. \ No newline at end of file + `fsspec `_. See :doc:`../jobs/run_script`. \ No newline at end of file diff --git a/docs/source/user_guide/jobs/components/runtime_types.rst b/docs/source/user_guide/jobs/runtime_types.rst similarity index 54% rename from docs/source/user_guide/jobs/components/runtime_types.rst rename to docs/source/user_guide/jobs/runtime_types.rst index bcfc65b76..f8cdc5cba 100644 --- a/docs/source/user_guide/jobs/components/runtime_types.rst +++ b/docs/source/user_guide/jobs/runtime_types.rst @@ -1,3 +1,3 @@ -.. include:: ../jobs/components/runtime_non_byoc.rst +.. include:: ../jobs/runtime_non_byoc.rst * :py:class:`~ads.jobs.ContainerRuntime` for container images. diff --git a/docs/source/user_guide/jobs/toc.rst b/docs/source/user_guide/jobs/toc.rst deleted file mode 100644 index 35d906fd1..000000000 --- a/docs/source/user_guide/jobs/toc.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. toctree:: - :maxdepth: 1 - - ../jobs/data_science_job - ../jobs/policies - ../jobs/infra_and_runtime - ../jobs/run_python - ../jobs/run_notebook - ../jobs/run_script - ../jobs/run_container - ../jobs/run_git - ../cli/opctl/_template/jobs - ../cli/opctl/_template/monitoring - ../cli/opctl/localdev/local_jobs diff --git a/docs/source/user_guide/jobs/components/toc_local.rst b/docs/source/user_guide/jobs/toc_local.rst similarity index 100% rename from docs/source/user_guide/jobs/components/toc_local.rst rename to docs/source/user_guide/jobs/toc_local.rst diff --git a/docs/source/user_guide/pipeline/pipeline_step.rst b/docs/source/user_guide/pipeline/pipeline_step.rst index 4ef16c44a..47729ecde 100644 --- a/docs/source/user_guide/pipeline/pipeline_step.rst +++ b/docs/source/user_guide/pipeline/pipeline_step.rst @@ -93,7 +93,7 @@ When constructing a Custom Scrip step ``infrastructure``, you specify the Comput A Custom Script step can have different types of ``runtime`` depending on the source code you run: -.. include:: ../jobs/components/runtime_non_byoc.rst +.. include:: ../jobs/runtime_non_byoc.rst All of these runtime options allow you to configure a `Data Science Conda Environment `__ for running your code. @@ -182,7 +182,7 @@ To define a Custom Script step with ``PythonRuntime`` you can use: kind: runtime spec: conda: - slug: pytorch19_p37_cpu_v1 + slug: pytorch110_p38_cpu_v1 type: service entrypoint: zip_or_dir/my_package/entry.py outputDir: output @@ -200,7 +200,7 @@ To define a Custom Script step with ``PythonRuntime`` you can use: runtime = ( PythonRuntime() - .with_service_conda("pytorch19_p37_cpu_v1") + .with_service_conda("pytorch110_p38_cpu_v1") # The job artifact directory is named "zip_or_dir" .with_source("local/path/to/zip_or_dir", entrypoint="zip_or_dir/my_package/entry.py") # Change the working directory to be inside the job artifact directory From ec9af199c81be63942367b29cbc061688b225778 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 14:14:32 -0500 Subject: [PATCH 077/147] Update example code. --- .../user_guide/jobs/tabs/name_substitution.rst | 2 +- docs/source/user_guide/jobs/tabs/python_runtime.rst | 4 ++-- .../source/user_guide/jobs/tabs/quick_start_job.rst | 4 ++-- docs/source/user_guide/jobs/tabs/script_runtime.rst | 13 +++++++++++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/jobs/tabs/name_substitution.rst b/docs/source/user_guide/jobs/tabs/name_substitution.rst index fa0db9401..3273e6ce5 100644 --- a/docs/source/user_guide/jobs/tabs/name_substitution.rst +++ b/docs/source/user_guide/jobs/tabs/name_substitution.rst @@ -49,7 +49,7 @@ type: python spec: conda: - slug: pytorch19_p37_cpu_v1 + slug: pytorch110_p38_cpu_v1 type: service env: - name: DATASET_NAME diff --git a/docs/source/user_guide/jobs/tabs/python_runtime.rst b/docs/source/user_guide/jobs/tabs/python_runtime.rst index cdaeec539..ee1b7d682 100644 --- a/docs/source/user_guide/jobs/tabs/python_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/python_runtime.rst @@ -26,7 +26,7 @@ .with_runtime( PythonRuntime() # Specify the service conda environment by slug name. - .with_service_conda("pytorch19_p37_cpu_v1") + .with_service_conda("pytorch110_p38_cpu_v1") # The job artifact can be a single Python script, a directory or a zip file. .with_source("local/path/to/code_dir") # Environment variable @@ -79,7 +79,7 @@ - --key - arg2 conda: - slug: pytorch19_p37_cpu_v1 + slug: pytorch110_p38_cpu_v1 type: service entrypoint: my_package/my_script.py env: diff --git a/docs/source/user_guide/jobs/tabs/quick_start_job.rst b/docs/source/user_guide/jobs/tabs/quick_start_job.rst index cf35a6bd1..c1333d305 100644 --- a/docs/source/user_guide/jobs/tabs/quick_start_job.rst +++ b/docs/source/user_guide/jobs/tabs/quick_start_job.rst @@ -28,7 +28,7 @@ # Specify the service conda environment by slug name. .with_service_conda("pytorch110_p38_cpu_v1") # Source code of the job, can be local or remote. - .with_source("https://raw.githubusercontent.com/oracle-samples/oci-data-science-ai-samples/432357b123b7401ef67b116fe19aec217ca920f0/jobs/python/job%2Bsamples/greeting-env-cmd.py") + .with_source("path/to/script.py") # Environment variable .with_environment_variable(NAME="Welcome to OCI Data Science.") # Command line argument @@ -71,4 +71,4 @@ env: - name: NAME value: Welcome to OCI Data Science. - scriptPathURI: https://raw.githubusercontent.com/oracle-samples/oci-data-science-ai-samples/432357b123b7401ef67b116fe19aec217ca920f0/jobs/python/job%2Bsamples/greeting-env-cmd.py + scriptPathURI: path/to/script.py diff --git a/docs/source/user_guide/jobs/tabs/script_runtime.rst b/docs/source/user_guide/jobs/tabs/script_runtime.rst index d5b550a70..0c55c048f 100644 --- a/docs/source/user_guide/jobs/tabs/script_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/script_runtime.rst @@ -26,9 +26,13 @@ .with_runtime( ScriptRuntime() # Specify the service conda environment by slug name. - .with_service_conda("pytorch19_p37_cpu_v1") + .with_service_conda("pytorch110_p38_cpu_v1") # The job artifact can be a single Python script, a directory or a zip file. .with_source("local/path/to/code_dir") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument + .with_argument("100 linux \"hi there\"") # The entrypoint is applicable only to directory or zip file as source # The entrypoint should be a path relative to the working dir. # Here my_script.sh is a file in the code_dir/my_package directory @@ -62,10 +66,15 @@ kind: runtime type: script spec: + args: + - 100 linux "hi there" conda: - slug: pytorch19_p37_cpu_v1 + slug: pytorch110_p38_cpu_v1 type: service entrypoint: my_package/my_script.sh + env: + - name: NAME + value: Welcome to OCI Data Science. scriptPathURI: local/path/to/code_dir .. code-block:: python From 1f97dc3fbdb169bdb347bb8f4c1d3528bc9445f7 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 14:14:48 -0500 Subject: [PATCH 078/147] Remove ads.tests.rst --- docs/source/ads.tests.rst | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 docs/source/ads.tests.rst diff --git a/docs/source/ads.tests.rst b/docs/source/ads.tests.rst deleted file mode 100644 index 61bec4b34..000000000 --- a/docs/source/ads.tests.rst +++ /dev/null @@ -1,29 +0,0 @@ -ads.tests package -================= - -Submodules ----------- - -ads.tests.test\_notebooks module --------------------------------- - -.. automodule:: ads.tests.test_notebooks - :members: - :undoc-members: - :show-inheritance: - -ads.tests.utils module ----------------------- - -.. automodule:: ads.tests.utils - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.tests - :members: - :undoc-members: - :show-inheritance: From 073a661b6c23f44cc3c915ad1bd66805e8faa659 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 14:46:00 -0500 Subject: [PATCH 079/147] Update jobs.rst, data_science_job.rst, and 2 more files... --- docs/source/user_guide/cli/opctl/_template/jobs.rst | 4 ++-- docs/source/user_guide/jobs/data_science_job.rst | 13 ++++++------- docs/source/user_guide/jobs/infra_and_runtime.rst | 7 ++++--- docs/source/user_guide/jobs/run_python.rst | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/source/user_guide/cli/opctl/_template/jobs.rst b/docs/source/user_guide/cli/opctl/_template/jobs.rst index 23aca5965..222de2eb9 100644 --- a/docs/source/user_guide/cli/opctl/_template/jobs.rst +++ b/docs/source/user_guide/cli/opctl/_template/jobs.rst @@ -5,14 +5,14 @@ Working with the CLI Prerequisite ------------ -.. include:: jobs_local_prerequisite.rst +:doc:`Install ADS CLI <../../quickstart>` Running a Pre Defined Job ------------------------- .. code-block:: shell - aads opctl run -j + ads opctl run -j Delete Job or Job Run --------------------- diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 717e86467..a30b38504 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -1,10 +1,11 @@ Quick Start *********** -.. admonition:: OCI Data Science Policies +.. admonition:: Prerequisite Before creating a job, ensure that you have policies configured for Data Science resources. - See also: :doc:`policies` and `About Data Science Policies `_. + + See :doc:`policies` and `About Data Science Policies `_. .. include:: ../jobs/toc_local.rst @@ -79,10 +80,6 @@ to create the job on OCI. To start a job run, you can call the :py:meth:`~ads.jo which returns a :py:class:`~ads.jobs.DataScienceJobRun` instance. Once the job or job run is created, the job OCID can be accessed through ``job.id`` or ``run.id``. -The :py:meth:`~ads.jobs.DataScienceJobRun.watch` method is useful to monitor the progress of the job run. -It will stream the logs to terminal and return once the job is finished. -Logging configurations are required for this method to show logs. - .. code-block:: python # Create the job on OCI Data Science @@ -92,7 +89,9 @@ Logging configurations are required for this method to show logs. # Stream the job run outputs run.watch() -Here is an example of the logs: +The :py:meth:`~ads.jobs.DataScienceJobRun.watch` method is useful to monitor the progress of the job run. +It will stream the logs to terminal and return once the job is finished. +Logging configurations are required for this method to show logs. Here is an example of the logs: .. code-block:: text diff --git a/docs/source/user_guide/jobs/infra_and_runtime.rst b/docs/source/user_guide/jobs/infra_and_runtime.rst index 9f559bb74..e7dba4ca0 100644 --- a/docs/source/user_guide/jobs/infra_and_runtime.rst +++ b/docs/source/user_guide/jobs/infra_and_runtime.rst @@ -113,6 +113,8 @@ If you specify only the log group OCID and no log OCID, a new Log resource is automatically created within the log group to store the logs, see also `ADS Logging <../logging/logging.html>`_. +With logging configured, you can call :py:meth:`~ads.jobs.DataScienceJobRun.watch` method to stream the logs. + Runtime ======= @@ -217,10 +219,9 @@ Here are a few more examples: Conda Environment ----------------- -Except for :py:class:`~ads.jobs.ContainerRuntime`, -all the other runtime options allow you to configure a +You can configure a `Conda Environment `_ -for your workload. You can use the slug name to specify a +for running your workload. You can use the slug name to specify a `conda environment provided by the data science service `_. For example, to use the TensorFlow conda environment: diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index aed2232b0..c3075bc65 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -2,7 +2,7 @@ Run a Python Workload ********************* The :py:class:`~ads.jobs.PythonRuntime` is designed for running a Python workload. -You can configure the environment variables, command line arguments and conda environment +You can configure the environment variables, command line arguments, and conda environment as described in :doc:`infra_and_runtime`. This section shows the additional enhancements provided by :py:class:`~ads.jobs.PythonRuntime`. From 61e04ebf03b1c5cb42131117a50cc5f1f07f2d0e Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 15:43:03 -0500 Subject: [PATCH 080/147] Update run_git.rst, run_python.rst, and 4 more files... --- docs/source/user_guide/jobs/run_git.rst | 30 +++++- docs/source/user_guide/jobs/run_python.rst | 3 + docs/source/user_guide/jobs/run_script.rst | 3 + .../user_guide/jobs/tabs/training_job.rst | 92 +++++++++++++++++++ .../user_guide/jobs/tabs/training_mnist.rst | 78 ---------------- .../model_training/training_with_oci.rst | 34 ++++++- 6 files changed, 155 insertions(+), 85 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/training_job.rst delete mode 100644 docs/source/user_guide/jobs/tabs/training_mnist.rst diff --git a/docs/source/user_guide/jobs/run_git.rst b/docs/source/user_guide/jobs/run_git.rst index 7be2f968a..aef5c6190 100644 --- a/docs/source/user_guide/jobs/run_git.rst +++ b/docs/source/user_guide/jobs/run_git.rst @@ -3,12 +3,18 @@ Run Code from Git Repo The :py:class:`~ads.jobs.GitPythonRuntime` allows you to run source code from a Git repository as a job. +.. include:: ../jobs/toc_local.rst + +PyTorch Example +=============== + The following example shows how to run a `PyTorch Neural Network Example to train third order polynomial predicting y=sin(x) `_. .. include:: ../jobs/tabs/git_runtime.rst + Git Repository ============== @@ -38,7 +44,7 @@ Entrypoint The entrypoint specifies how the source code is invoked. The :py:meth:`~ads.jobs.GitPythonRuntime.with_entrypoint` supports the following arguments: -* ``path``: Required. The relative path for the script, module, or file to start the job. +* ``path``: Required. The relative path of the script/module from the root of the Git repository. * ``func``: Optional. The function in the script specified by ``path`` to call. If you don't specify it, then the script specified by ``path`` is run as a Python script in a subprocess. @@ -60,6 +66,18 @@ The arguments can be strings, ``list`` of strings or ``dict`` containing only st Arguments are not used when the entrypoint is a notebook. +Working Directory +================= + +By default, the working directory is the root of the git repository. +This can be configured by can be configured by :py:meth:`~ads.jobs.GitPythonRuntime.with_working_dir` +using a relative path from the root of the Git repository. + +Note that the entrypoint should always specified as a relative path from the root of the Git repository, +regardless of the working directory. +The python paths and output directory should be specified relative to the working directory. + + Python Paths ============ @@ -68,17 +86,19 @@ The working directory is added to the Python paths automatically. You can call :py:meth:`~ads.jobs.GitPythonRuntime.with_python_path` to add additional python paths as needed. The paths should be relative paths from the working directory. + Outputs ======= -The :py:meth:`~ads.jobs.GitPythonRuntime.with_output` method allows you to specify the output path ``output_path`` +The :py:meth:`~ads.jobs.GitPythonRuntime.with_output` method allows you to specify the output path ``output_dir`` in the job run and a remote URI (``output_uri``). -Files in the ``output_path`` are copied to the remote output URI after the job run finishes successfully. -Note that the ``output_path`` should be a path relative to the working directory. +Files in the ``output_dir`` are copied to the remote output URI after the job run finishes successfully. +Note that the ``output_dir`` should be a path relative to the working directory. OCI object storage location can be specified in the format of ``oci://bucket_name@namespace/path/to/dir``. Please make sure you configure the I AM policy to allow the job run dynamic group to use object storage. + Metadata ======== The :py:class:`~ads.jobs.GitPythonRuntime` updates metadata as free-form tags of the job run @@ -93,6 +113,6 @@ after the job run finishes. The following tags are added automatically: The new values overwrite any existing tags. If you want to skip the metadata update, set ``skip_metadata_update`` to ``True`` when initializing the runtime: -.. code-block:: python3 +.. code-block:: python runtime = GitPythonRuntime(skip_metadata_update=True) diff --git a/docs/source/user_guide/jobs/run_python.rst b/docs/source/user_guide/jobs/run_python.rst index c3075bc65..5c8917f11 100644 --- a/docs/source/user_guide/jobs/run_python.rst +++ b/docs/source/user_guide/jobs/run_python.rst @@ -8,6 +8,9 @@ as described in :doc:`infra_and_runtime`. This section shows the additional enha .. include:: ../jobs/toc_local.rst +Example +======= + Here is an example to define and run a job using :py:class:`~ads.jobs.PythonRuntime`: .. include:: ../jobs/tabs/python_runtime.rst diff --git a/docs/source/user_guide/jobs/run_script.rst b/docs/source/user_guide/jobs/run_script.rst index ee053f9a3..580ad3d5e 100644 --- a/docs/source/user_guide/jobs/run_script.rst +++ b/docs/source/user_guide/jobs/run_script.rst @@ -15,6 +15,9 @@ Here is an example: .. include:: ../jobs/tabs/script_runtime.rst +An `example script `_ +is available on `Data Science AI Sample GitHub Repository `_. + Working Directory ================= diff --git a/docs/source/user_guide/jobs/tabs/training_job.rst b/docs/source/user_guide/jobs/tabs/training_job.rst new file mode 100644 index 000000000..55094a0ae --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/training_job.rst @@ -0,0 +1,92 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, GitPythonRuntime + + job = ( + Job(name="Training RNN with PyTorch") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + .with_shape_name("VM.GPU3.1") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # Default block storage size is 50GB + .with_block_storage_size(50) + ) + .with_runtime( + GitPythonRuntime(skip_metadata_update=True) + # Use service conda pack + .with_service_conda("pytorch110_p38_gpu_v1") + # Specify training source code from GitHub + .with_source(url="https://github.com/pytorch/examples.git", branch="main") + # Entrypoint is a relative path from the root of the Git repository + .with_entrypoint("word_language_model/main.py") + # Pass the arguments as: "--epochs 5 --save model.pt --cuda" + .with_argument(epochs=5, save="model.pt", cuda=None) + # Set working directory, which will also be added to PYTHONPATH + .with_working_dir("word_language_model") + # Save the output to OCI object storage + # output_dir is relative to working directory + .with_output(output_dir=".", output_uri="oci://bucket@namespace/prefix") + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: gitPython + spec: + args: + - --epochs + - '5' + - --save + - model.pt + - --cuda + branch: main + conda: + slug: pytorch110_p38_gpu_v1 + type: service + entrypoint: word_language_model/main.py + outputDir: . + outputUri: oci://bucket@namespace/prefix + skipMetadataUpdate: true + url: https://github.com/pytorch/examples.git + workingDir: word_language_model + + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() \ No newline at end of file diff --git a/docs/source/user_guide/jobs/tabs/training_mnist.rst b/docs/source/user_guide/jobs/tabs/training_mnist.rst deleted file mode 100644 index ea1589b73..000000000 --- a/docs/source/user_guide/jobs/tabs/training_mnist.rst +++ /dev/null @@ -1,78 +0,0 @@ -.. tabs:: - - .. code-tab:: python - :caption: Python - - from ads.jobs import Job, DataScienceJob, GitPythonRuntime - - job = ( - Job(name="Training MNIST with PyTorch") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - .with_shape_name("VM.GPU3.1") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - .with_block_storage_size(50) - ) - .with_runtime( - GitPythonRuntime(skip_metadata_update=True) - .with_source(url="https://github.com/pytorch/examples.git", branch="main") - .with_entrypoint(path="mnist/main.py") - .with_service_conda("pytorch110_p38_gpu_v1") - # Pass the arguments as: main.py --epochs 10 --save-model - .with_argument(**{"epochs": 10, "save-model": None}) - .with_output( - "mnist_rnn.pt", - output_uri="oci://bucket_name@namespace/path/to/dir" - ) - ) - ) - - # Create the job on OCI Data Science - job.create() - # Start a job run - run = job.run() - # Stream the job run outputs - run.watch() - - .. code-tab:: yaml - :caption: YAML - - kind: job - spec: - name: "My Job" - infrastructure: - kind: infrastructure - type: dataScienceJob - spec: - blockStorageSize: 50 - compartmentId: - jobInfrastructureType: STANDALONE - jobType: DEFAULT - logGroupId: - logId: - projectId: - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - shapeName: VM.Standard.E3.Flex - subnetId: - runtime: - kind: runtime - type: gitPython - spec: - conda: - slug: pytorch19_p37_gpu_v1 - type: service - entrypoint: beginner_source/examples_nn/polynomial_nn.py - env: - - name: GREETINGS - value: Welcome to OCI Data Science - outputDir: beginner_source/examples_nn - outputUri: oci://bucket_name@namespace/path/to/dir - url: https://github.com/pytorch/tutorials.git diff --git a/docs/source/user_guide/model_training/training_with_oci.rst b/docs/source/user_guide/model_training/training_with_oci.rst index f924fe9ce..3e6763cec 100644 --- a/docs/source/user_guide/model_training/training_with_oci.rst +++ b/docs/source/user_guide/model_training/training_with_oci.rst @@ -7,9 +7,39 @@ enables you to define and run repeatable machine learning tasks on a fully manag You can have Compute resource on demand and run applications that perform tasks such as data preparation, model training, hyperparameter tuning, and batch inference. -Here is an example for training MNIST model with PyTorch using source code directly from GitHub. +Here is an example for training RNN on `Word-level Language Modeling `_, +using the source code directly from GitHub. -.. include:: ../jobs/tabs/training_mnist.rst +.. include:: ../jobs/tabs/training_job.rst + +The job run will: + +* Setup the PyTorch conda environment +* Fetch the source code from GitHub +* Run the training script with the specific arguments +* Save the outputs to OCI object storage + +Following are the example outputs of the job run: + +.. code-block:: text + + 2023-02-27 20:26:36 - Job Run ACCEPTED + 2023-02-27 20:27:05 - Job Run ACCEPTED, Infrastructure provisioning. + 2023-02-27 20:28:27 - Job Run ACCEPTED, Infrastructure provisioned. + 2023-02-27 20:28:53 - Job Run ACCEPTED, Job run bootstrap starting. + 2023-02-27 20:33:05 - Job Run ACCEPTED, Job run bootstrap complete. Artifact execution starting. + 2023-02-27 20:33:08 - Job Run IN_PROGRESS, Job run artifact execution in progress. + 2023-02-27 20:33:31 - | epoch 1 | 200/ 2983 batches | lr 20.00 | ms/batch 8.41 | loss 7.63 | ppl 2064.78 + 2023-02-27 20:33:32 - | epoch 1 | 400/ 2983 batches | lr 20.00 | ms/batch 8.23 | loss 6.86 | ppl 949.18 + 2023-02-27 20:33:34 - | epoch 1 | 600/ 2983 batches | lr 20.00 | ms/batch 8.21 | loss 6.47 | ppl 643.12 + 2023-02-27 20:33:36 - | epoch 1 | 800/ 2983 batches | lr 20.00 | ms/batch 8.22 | loss 6.29 | ppl 537.11 + 2023-02-27 20:33:37 - | epoch 1 | 1000/ 2983 batches | lr 20.00 | ms/batch 8.22 | loss 6.14 | ppl 462.61 + 2023-02-27 20:33:39 - | epoch 1 | 1200/ 2983 batches | lr 20.00 | ms/batch 8.21 | loss 6.05 | ppl 425.85 + ... + 2023-02-27 20:35:41 - ========================================================================================= + 2023-02-27 20:35:41 - | End of training | test loss 4.96 | test ppl 142.94 + 2023-02-27 20:35:41 - ========================================================================================= + ... For more details, see: From 7e4e774b5880644915422e1f223f8d4db0a8d72c Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 15:53:16 -0500 Subject: [PATCH 081/147] Update examples. --- .../user_guide/jobs/tabs/git_notebook.rst | 77 +++++++++++++++++++ .../user_guide/jobs/tabs/git_runtime.rst | 64 +++++++-------- .../jobs/tabs/name_substitution.rst | 34 ++++---- .../user_guide/jobs/tabs/notebook_runtime.rst | 54 ++++++------- .../user_guide/jobs/tabs/script_runtime.rst | 64 +++++++-------- 5 files changed, 185 insertions(+), 108 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/git_notebook.rst diff --git a/docs/source/user_guide/jobs/tabs/git_notebook.rst b/docs/source/user_guide/jobs/tabs/git_notebook.rst new file mode 100644 index 000000000..5c42b3ecb --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/git_notebook.rst @@ -0,0 +1,77 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, GitPythonRuntime + + job = ( + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + GitPythonRuntime() + # Use service conda pack + .with_service_conda("pytorch110_p38_gpu_v1") + # Specify training source code from GitHub + .with_source(url="https://github.com/karpathy/minGPT.git") + # Entrypoint is a relative path from the root of the Git repository + .with_entrypoint("demo.ipynb") + ) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: job + spec: + name: "My Job" + infrastructure: + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + jobInfrastructureType: STANDALONE + jobType: DEFAULT + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: + runtime: + kind: runtime + type: gitPython + spec: + conda: + slug: pytorch19_p37_gpu_v1 + type: service + entrypoint: demo.ipynb + url: https://github.com/karpathy/minGPT.git + + +.. code-block:: python + + # Create the job on OCI Data Science + job.create() + # Start a job run + run = job.run() + # Stream the job run outputs + run.watch() diff --git a/docs/source/user_guide/jobs/tabs/git_runtime.rst b/docs/source/user_guide/jobs/tabs/git_runtime.rst index eaf396127..e8a9b596d 100644 --- a/docs/source/user_guide/jobs/tabs/git_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/git_runtime.rst @@ -6,39 +6,39 @@ from ads.jobs import Job, DataScienceJob, GitPythonRuntime job = ( - Job(name="My Job") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - # For default networking, no need to specify subnet ID - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - GitPythonRuntime() - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - # Specify the service conda environment by slug name. - .with_service_conda("pytorch19_p37_gpu_v1") - # Specify the git repository - # Optionally, you can specify the branch or commit - .with_source("https://github.com/pytorch/tutorials.git") - # Entrypoint is a relative path from the root of the git repo. - .with_entrypoint("beginner_source/examples_nn/polynomial_nn.py") - # Copy files in "beginner_source/examples_nn" to object storage after job finishes. - .with_output( - output_dir="beginner_source/examples_nn", - output_uri="oci://bucket_name@namespace/path/to/dir" + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + GitPythonRuntime() + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_gpu_v1") + # Specify the git repository + # Optionally, you can specify the branch or commit + .with_source("https://github.com/pytorch/tutorials.git") + # Entrypoint is a relative path from the root of the git repo. + .with_entrypoint("beginner_source/examples_nn/polynomial_nn.py") + # Copy files in "beginner_source/examples_nn" to object storage after job finishes. + .with_output( + output_dir="beginner_source/examples_nn", + output_uri="oci://bucket_name@namespace/path/to/dir" + ) ) - ) ) .. code-tab:: yaml diff --git a/docs/source/user_guide/jobs/tabs/name_substitution.rst b/docs/source/user_guide/jobs/tabs/name_substitution.rst index 3273e6ce5..6d46b35f7 100644 --- a/docs/source/user_guide/jobs/tabs/name_substitution.rst +++ b/docs/source/user_guide/jobs/tabs/name_substitution.rst @@ -6,23 +6,23 @@ from ads.jobs import Job, DataScienceJob, PythonRuntime job = ( - Job(name="Training on ${DATASET_NAME}") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - .with_compartment_id("") - .with_project_id("") - .with_shape_name("VM.Standard.E3.Flex") - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - ) - .with_runtime( - PythonRuntime() - .with_service_conda("pytorch110_p38_gpu_v1") - .with_environment_variable(DATASET_NAME="MyData") - .with_source("local/path/to/training_script.py") - .with_output("output", "oci://bucket_name@namespace/prefix/${JOB_RUN_OCID}") - ) + Job(name="Training on ${DATASET_NAME}") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + .with_compartment_id("") + .with_project_id("") + .with_shape_name("VM.Standard.E3.Flex") + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + ) + .with_runtime( + PythonRuntime() + .with_service_conda("pytorch110_p38_gpu_v1") + .with_environment_variable(DATASET_NAME="MyData") + .with_source("local/path/to/training_script.py") + .with_output("output", "oci://bucket_name@namespace/prefix/${JOB_RUN_OCID}") + ) ) .. code-tab:: yaml diff --git a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst index 001075f0b..5b9e6638f 100644 --- a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst @@ -6,34 +6,34 @@ from ads.jobs import Job, DataScienceJob, NotebookRuntime job = ( - Job(name="My Job") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - # For default networking, no need to specify subnet ID - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - NotebookRuntime() - .with_notebook( - path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", - encoding='utf-8' + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow28_p38_cpu_v1") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_exclude_tag(["ignore", "remove"]) + .with_output("oci://bucket_name@namespace/path/to/dir") ) - .with_service_conda("tensorflow28_p38_cpu_v1") - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_exclude_tag(["ignore", "remove"]) - .with_output("oci://bucket_name@namespace/path/to/dir") - ) ) .. code-tab:: yaml diff --git a/docs/source/user_guide/jobs/tabs/script_runtime.rst b/docs/source/user_guide/jobs/tabs/script_runtime.rst index 0c55c048f..a9095d9af 100644 --- a/docs/source/user_guide/jobs/tabs/script_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/script_runtime.rst @@ -6,38 +6,38 @@ from ads.jobs import Job, DataScienceJob, ScriptRuntime job = ( - Job(name="My Job") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - # For default networking, no need to specify subnet ID - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - ScriptRuntime() - # Specify the service conda environment by slug name. - .with_service_conda("pytorch110_p38_cpu_v1") - # The job artifact can be a single Python script, a directory or a zip file. - .with_source("local/path/to/code_dir") - # Environment variable - .with_environment_variable(NAME="Welcome to OCI Data Science.") - # Command line argument - .with_argument("100 linux \"hi there\"") - # The entrypoint is applicable only to directory or zip file as source - # The entrypoint should be a path relative to the working dir. - # Here my_script.sh is a file in the code_dir/my_package directory - .with_entrypoint("my_package/my_script.sh") - ) + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + .with_log_group_id("") + .with_log_id("") + # The following infrastructure configurations are optional + # if you are in an OCI data science notebook session. + # The configurations of the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + # For default networking, no need to specify subnet ID + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + .with_block_storage_size(50) + ) + .with_runtime( + ScriptRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch110_p38_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument + .with_argument("100 linux \"hi there\"") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.sh is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.sh") + ) ) .. code-tab:: yaml From d142609b34a1d2711d7cb2a5f23f520ca7dc596e Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Mon, 27 Feb 2023 16:08:17 -0500 Subject: [PATCH 082/147] Update run_notebook.rst --- docs/source/user_guide/jobs/run_notebook.rst | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/jobs/run_notebook.rst b/docs/source/user_guide/jobs/run_notebook.rst index 433664fac..6ca200a5a 100644 --- a/docs/source/user_guide/jobs/run_notebook.rst +++ b/docs/source/user_guide/jobs/run_notebook.rst @@ -3,13 +3,7 @@ Run a Notebook The :py:class:`~ads.jobs.NotebookRuntime` allows you to run a single Jupyter notebook as a job. -If your notebook needs extra dependencies like custom module or data files, you can use -:py:class:`~ads.jobs.PythonRuntime` or :py:class:`~ads.jobs.GitPythonRuntime` and set your notebook as the entrypoint. - -See also: - -* :doc:`run_python` -* :doc:`run_git` +.. include:: ../jobs/toc_local.rst TensorFlow Example ================== @@ -51,3 +45,18 @@ To tag cells in a notebook, see The :py:meth:`~ads.jobs.NotebookRuntime.with_exclude_tag` take a list of tags as argument Cells with any matching tags are excluded from the job run. In the above example, cells with ``ignore`` or ``remove`` are excluded. + +Notebook with Dependencies +========================== + +If your notebook needs extra dependencies like custom module or data files, you can use +:py:class:`~ads.jobs.PythonRuntime` or :py:class:`~ads.jobs.GitPythonRuntime` and set your notebook as the entrypoint. + +See also: + +* :doc:`run_python` +* :doc:`run_git` + +Here is an example of running the `minGPT demo notebook `_. + +.. include:: ../jobs/tabs/git_notebook.rst From cca3c721bdcefb658a98c266026faba97085c263 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 27 Feb 2023 17:28:45 -0800 Subject: [PATCH 083/147] ODSC-39261: adding quick start for AutoML model --- docs/source/ads.model_framework.rst | 8 - .../_template/summary_status.rst | 2 +- .../frameworks/automlmodel.rst | 289 +++++++++---- .../model_registration/quick_start.rst | 59 --- .../model_serialization/automlmodel.rst | 381 ++++++++++-------- .../boilerplate/summary_status.rst | 2 +- .../model_training/automl/index.rst | 8 +- .../model_training/automl/overview.rst | 68 ---- 8 files changed, 411 insertions(+), 406 deletions(-) delete mode 100644 docs/source/user_guide/model_training/automl/overview.rst diff --git a/docs/source/ads.model_framework.rst b/docs/source/ads.model_framework.rst index bb1923ab4..97d23c095 100644 --- a/docs/source/ads.model_framework.rst +++ b/docs/source/ads.model_framework.rst @@ -4,14 +4,6 @@ ads.model.framework package Submodules ---------- -ads.model.framework.automl\_model module ----------------------------------------- - -.. automodule:: ads.model.framework.automl_model - :members: - :undoc-members: - :show-inheritance: - ads.model.framework.lightgbm\_model module ------------------------------------------ diff --git a/docs/source/user_guide/model_registration/_template/summary_status.rst b/docs/source/user_guide/model_registration/_template/summary_status.rst index 3ea4dd084..3cc8759fc 100644 --- a/docs/source/user_guide/model_registration/_template/summary_status.rst +++ b/docs/source/user_guide/model_registration/_template/summary_status.rst @@ -1,3 +1,3 @@ -You can call the ``.summary_status()`` method after a model serialization instance such as ``AutoMLModel``, ``GenericModel``, ``SklearnModel``, ``TensorFlowModel``, or ``PyTorchModel`` is created. The ``.summary_status()`` method returns a Pandas dataframe that guides you through the entire workflow. It shows which methods are available to call and which ones aren't. Plus it outlines what each method does. If extra actions are required, it also shows those actions. +You can call the ``.summary_status()`` method after a model serialization instance such as ``GenericModel``, ``SklearnModel``, ``TensorFlowModel``, or ``PyTorchModel`` is created. The ``.summary_status()`` method returns a Pandas dataframe that guides you through the entire workflow. It shows which methods are available to call and which ones aren't. Plus it outlines what each method does. If extra actions are required, it also shows those actions. The following image displays an example summary status table created after a user initiates a model instance. The table's Step column displays a Status of Done for the initiate step. And the ``Details`` column explains what the initiate step did such as generating a ``score.py`` file. The Step column also displays the ``prepare()``, ``verify()``, ``save()``, ``deploy()``, and ``predict()`` methods for the model. The Status column displays which method is available next. After the initiate step, the ``prepare()`` method is available. The next step is to call the ``prepare()`` method. \ No newline at end of file diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index fcb29cb28..e0e506645 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -3,79 +3,13 @@ AutoMLModel *********** -See `API Documentation <../../../ads.model_framework.html#ads.model.framework.automl_model.AutoMLModel>`__ +.. note:: -Overview -======== +The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:link <_Oralce_AutoMlx>`` for more detailed information. -The ``ads.model.framework.automl_model.AutoMLModel`` class in ADS is designed to rapidly get your AutoML model into production. The ``.prepare()`` method creates the model artifacts needed to deploy the model without you having to configure it or write code. The ``.prepare()`` method serializes the model and generates a ``runtime.yaml`` and a ``score.py`` file that you can later customize. +To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. -.. include:: ../_template/overview.rst - -The following steps take your trained ``AutoML`` model and deploy it into production with a few lines of code. - - -**Creating an Oracle Labs AutoML Model** - -Train a model using AutoMLx. - -.. code-block:: python3 - - import pandas as pd - import numpy as np - import tempfile - from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score - from sklearn.linear_model import LogisticRegression - from sklearn.compose import make_column_selector as selector - from sklearn.impute import SimpleImputer - from sklearn.preprocessing import StandardScaler, OneHotEncoder - from sklearn.compose import ColumnTransformer - from sklearn.pipeline import Pipeline - from sklearn.datasets import fetch_openml - from sklearn.model_selection import train_test_split - - import ads - import automl - from automl import init - from ads.model import AutoMLModel - from ads.common.model_metadata import UseCaseType - from ads.model.framework.automl_model import AutoMLModel - - dataset = fetch_openml(name='adult', as_frame=True) - df, y = dataset.data, dataset.target - - # Several of the columns are incorrectly labeled as category type in the original dataset - numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] - for col in df.columns: - if col in numeric_columns: - df[col] = df[col].astype(int) - - - X_train, X_test, y_train, y_test = train_test_split(df, - y.map({'>50K': 1, '<=50K': 0}).astype(int), - train_size=0.7, - random_state=0) - - init(engine='local') - est = automl.Pipeline(task='classification') - est.fit(X_train, y_train) - -Initialize -========== - -Instantiate an ``AutoMLModel()`` object with an ``AutoML`` model. Each instance accepts the following parameters: - -* ``artifact_dir: str``: Artifact directory to store the files needed for deployment. -* ``auth: (Dict, optional)``: Defaults to ``None``. The default authentication is set using the ``ads.set_auth`` API. To override the default, use ``ads.common.auth.api_keys()`` or ``ads.common.auth.resource_principal()`` and create the appropriate authentication signer and the ``**kwargs`` required to instantiate the ``IdentityClient`` object. -* ``estimator: (Callable)``: Trained AutoML model. -* ``properties: (ModelProperties, optional)``: Defaults to ``None``. The ``ModelProperties`` object required to save and deploy a model. - -.. include:: ../_template/initialize.rst - -Summary Status -============== - -.. include:: ../_template/summary_status.rst +The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production with a few lines of code. Example @@ -85,23 +19,14 @@ Example import pandas as pd import numpy as np - import tempfile - from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score - from sklearn.linear_model import LogisticRegression - from sklearn.compose import make_column_selector as selector - from sklearn.impute import SimpleImputer - from sklearn.preprocessing import StandardScaler, OneHotEncoder - from sklearn.compose import ColumnTransformer - from sklearn.pipeline import Pipeline from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split import ads import automl from automl import init - from ads.model import AutoMLModel + from ads.model import GenericModel from ads.common.model_metadata import UseCaseType - from ads.model.framework.automl_model import AutoMLModel dataset = fetch_openml(name='adult', as_frame=True) df, y = dataset.data, dataset.target @@ -112,7 +37,6 @@ Example if col in numeric_columns: df[col] = df[col].astype(int) - X_train, X_test, y_train, y_test = train_test_split(df, y.map({'>50K': 1, '<=50K': 0}).astype(int), train_size=0.7, @@ -123,16 +47,207 @@ Example est.fit(X_train, y_train) ads.set_auth("resource_principal") - artifact_dir = tempfile.mkdtemp() - automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) + automl_model = GenericModel(estimator=est, artifact_dir="automl_model_artifact") automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", training_conda_env="automlx_p38_cpu_v1", use_case_type=UseCaseType.BINARY_CLASSIFICATION, X_sample=X_test, force_overwrite=True) - automl_model.verify(X_test.iloc[:2]) + + +Open ``automl_model_artifact/score.py`` and edit the code to instantiate the model class. The edits are highlighted - + +.. code-block:: python3 + :emphasize-lines: 21,30 + + # score.py 1.0 generated by ADS 2.8.1 on 20230226_214703 + import json + import os + import sys + import importlib + from cloudpickle import cloudpickle + from functools import lru_cache + from io import StringIO + import logging + import sys + import automl + import pandas as pd + import numpy as np + + model_name = 'model.pkl' + + """ + Inference script. This script is used for prediction by scoring server when schema is known. + """ + + def init_automl_logger(): + logger = logging.getLogger("automl") + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(logging.ERROR) + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + handler.setFormatter(formatter) + logger.addHandler(handler) + automl.init(engine="local", engine_opts={"n_jobs": 1}, logger=logger) + + + @lru_cache(maxsize=10) + def load_model(model_file_name=model_name): + """ + Loads model from the serialized format + + Returns + ------- + model: a model instance on which predict API can be invoked + """ + init_automl_logger() + model_dir = os.path.dirname(os.path.realpath(__file__)) + if model_dir not in sys.path: + sys.path.insert(0, model_dir) + contents = os.listdir(model_dir) + if model_file_name in contents: + print(f'Start loading {model_file_name} from model directory {model_dir} ...') + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), model_file_name), "rb") as file: + loaded_model = cloudpickle.load(file) + + print("Model is successfully loaded.") + return loaded_model + else: + raise Exception(f'{model_file_name} is not found in model directory {model_dir}') + + @lru_cache(maxsize=1) + def fetch_data_type_from_schema(input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema['schema']: + data_type[col['name']] = col['dtype'] + else: + print("input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.automl_model.AutoMLModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization.") + return data_type + + def deserialize(data, input_schema_path, task=None): + """ + Deserialize json serialization data to data in original type when sent to predict. + + Parameters + ---------- + data: serialized input data. + input_schema_path: path of input schema. + task: Machine learning task, supported: classification, regression, anomaly_detection, forecasting. Defaults to None. + + Returns + ------- + data: deserialized input data. + + """ + + if isinstance(data, bytes): + return pd.read_json(StringIO(data.decode("utf-8"))) + + data_type = data.get('data_type', '') if isinstance(data, dict) else '' + json_data = data.get('data', data) if isinstance(data, dict) else data + + if task and task == "forecasting": + if data_type: + data_type = data_type.split("'")[1] + try: + module, spec = ".".join(data_type.split(".")[:-1]), data_type.split(".")[-1] + lib = importlib.import_module(name=module) + func = getattr(lib, spec) + return pd.DataFrame(index=func(json_data)) + except: + logging.warning("Cannot autodetect the type of the input data. By default, convert input data to pd.DatetimeIndex and feed the model an empty pandas DataFrame with index as input data. If assumption is not correct, modify the score.py and check with .verify() before saving model with .save().") + return pd.DataFrame(index=pd.DatetimeIndex(json_data)) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=fetch_data_type_from_schema(input_schema_path)) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + + return json_data + + def pre_inference(data, input_schema_path, task=None): + """ + Preprocess data + + Parameters + ---------- + data: Data format as expected by the predict API of the core estimator. + input_schema_path: path of input schema. + task: Machine learning task, supported: classification, regression, anomaly_detection, forecasting. Defaults to None. + + Returns + ------- + data: Data format after any processing. + + """ + data = deserialize(data, input_schema_path, task) + return data + + def post_inference(yhat): + """ + Post-process the model results + + Parameters + ---------- + yhat: Data format after calling model.predict. + + Returns + ------- + yhat: Data format after any processing. + + """ + if isinstance(yhat, pd.core.frame.DataFrame): + yhat = yhat.values + return yhat.tolist() + + def predict(data, model=load_model(), input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns prediction given the model and data to predict + + Parameters + ---------- + model: Model instance returned by load_model API + data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame + input_schema_path: path of input schema. + + Returns + ------- + predictions: Output from scoring server + Format: {'prediction': output from model.predict method} + + """ + task = model.task if hasattr(model, "task") else None + features = pre_inference(data, input_schema_path, task) + yhat = post_inference( + model.predict(features) + ) + return {'prediction': yhat} + + +Verify score.py changes by running inference locally + .. code-block:: python3 + + automl_model.verify(X_test.iloc[:2], auto_serialize_data=True) + +Save model and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. + .. code-block:: python3 + model_id = automl_model.save(display_name='Demo AutoMLModel model') deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') - automl_model.predict(X_test.iloc[:2]) - automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + automl_model.predict(X_test.iloc[:2], auto_serialize_data=True) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 1b567366e..a88c78398 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -257,65 +257,6 @@ Create a model, prepare it, verify that it works, save it to the model catalog, #Register TensorFlow model model_id = tf_model.save(display_name="TensorFlow Model") -AutoMLx Frameworks ------------------- - -.. code-block:: python3 - - import pandas as pd - import numpy as np - import tempfile - from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score - from sklearn.linear_model import LogisticRegression - from sklearn.compose import make_column_selector as selector - from sklearn.impute import SimpleImputer - from sklearn.preprocessing import StandardScaler, OneHotEncoder - from sklearn.compose import ColumnTransformer - from sklearn.pipeline import Pipeline - from sklearn.datasets import fetch_openml - from sklearn.model_selection import train_test_split - - import ads - import automl - from automl import init - from ads.model import AutoMLModel - from ads.common.model_metadata import UseCaseType - from ads.model.framework.automl_model import AutoMLModel - - dataset = fetch_openml(name='adult', as_frame=True) - df, y = dataset.data, dataset.target - - # Several of the columns are incorrectly labeled as category type in the original dataset - numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] - for col in df.columns: - if col in numeric_columns: - df[col] = df[col].astype(int) - - - X_train, X_test, y_train, y_test = train_test_split(df, - y.map({'>50K': 1, '<=50K': 0}).astype(int), - train_size=0.7, - random_state=0) - - init(engine='local') - est = automl.Pipeline(task='classification') - est.fit(X_train, y_train) - - ads.set_auth("resource_principal") - artifact_dir = tempfile.mkdtemp() - automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) - automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", - training_conda_env="automlx_p38_cpu_v1", - use_case_type=UseCaseType.BINARY_CLASSIFICATION, - X_sample=X_test, - force_overwrite=True) - automl_model.verify(X_test.iloc[:2]) - model_id = automl_model.save(display_name='Demo AutoMLModel model') - deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') - automl_model.predict(X_test.iloc[:2]) - automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) - Other Frameworks ---------------- diff --git a/docs/source/user_guide/model_serialization/automlmodel.rst b/docs/source/user_guide/model_serialization/automlmodel.rst index 1cb860190..5825b86d9 100644 --- a/docs/source/user_guide/model_serialization/automlmodel.rst +++ b/docs/source/user_guide/model_serialization/automlmodel.rst @@ -3,41 +3,30 @@ AutoMLModel *********** -Overview -======== +.. note:: -The ``AutoMLModel`` class in ADS is designed to rapidly get your AutoML model into production. The ``.prepare()`` method creates the model artifacts needed to deploy the model without you having to configure it or write code. The ``.prepare()`` method serializes the model and generates a ``runtime.yaml`` and a ``score.py`` file that you can later customize. +The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:link <_Oralce_AutoMlx>`` for more detailed information. -.. include:: _template/overview.rst +To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. -The following steps take your trained ``AutoML`` model and deploy it into production with a few lines of code. +The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production with a few lines of code. -**Creating an Oracle Labs AutoML Model** - -Train a model using AutoMLx. +Example +======= .. code-block:: python3 import pandas as pd import numpy as np - import tempfile - from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score - from sklearn.linear_model import LogisticRegression - from sklearn.compose import make_column_selector as selector - from sklearn.impute import SimpleImputer - from sklearn.preprocessing import StandardScaler, OneHotEncoder - from sklearn.compose import ColumnTransformer - from sklearn.pipeline import Pipeline from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split import ads import automl from automl import init - from ads.model import AutoMLModel + from ads.model import GenericModel from ads.common.model_metadata import UseCaseType - from ads.model.framework.automl_model import AutoMLModel dataset = fetch_openml(name='adult', as_frame=True) df, y = dataset.data, dataset.target @@ -48,7 +37,6 @@ Train a model using AutoMLx. if col in numeric_columns: df[col] = df[col].astype(int) - X_train, X_test, y_train, y_test = train_test_split(df, y.map({'>50K': 1, '<=50K': 0}).astype(int), train_size=0.7, @@ -59,181 +47,218 @@ Train a model using AutoMLx. est.fit(X_train, y_train) ads.set_auth("resource_principal") - artifact_dir = tempfile.mkdtemp() - automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) + automl_model = GenericModel(estimator=est, artifact_dir="automl_model_artifact") automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", training_conda_env="automlx_p38_cpu_v1", use_case_type=UseCaseType.BINARY_CLASSIFICATION, X_sample=X_test, force_overwrite=True) - automl_model.verify(X_test.iloc[:2]) - model_id = automl_model.save(display_name='Demo AutoMLModel model') - deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') - automl_model.predict(X_test.iloc[:2]) - automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) - - -Initialize -========== - -Instantiate an ``AutoMLModel()`` object with an ``AutoML`` model. Each instance accepts the following parameters: - -* ``artifact_dir: str``: Artifact directory to store the files needed for deployment. -* ``auth: (Dict, optional)``: Defaults to ``None``. The default authentication is set using the ``ads.set_auth`` API. To override the default, use ``ads.common.auth.api_keys()`` or ``ads.common.auth.resource_principal()`` and create the appropriate authentication signer and the ``**kwargs`` required to instantiate the ``IdentityClient`` object. -* ``estimator: (Callable)``: Trained AutoML model. -* ``properties: (ModelProperties, optional)``: Defaults to ``None``. The ``ModelProperties`` object required to save and deploy a model. - -.. include:: _template/initialize.rst - -Summary Status -============== - -.. include:: _template/summary_status.rst - -Model Deployment -================ - -Prepare -------- - -The prepare step is performed by the ``.prepare()`` method. It creates several customized files that are used to run the model once it is deployed. These include: - -* ``input_schema.json``: A JSON file that defines the nature of the feature data. It includes information about the features. This includes metadata such as the data type, name, constraints, summary statistics, and feature type. -* ``model.pkl``: The default file name of the serialized model. You can change the file name with the ``model_file_name`` attribute. By default, the model is stored in a pickle file. To save your file in an ONNX format, use the ``as_onnx`` parameter. -* ``output_schema.json``: A JSON file that defines the dependent variable. This file includes metadata for the dependent variable, such as the data type, name, constraints, summary statistics, and feature type. -* ``runtime.yaml``: This file contains information needed to set up the runtime environment on the deployment server. It includes information about the conda environment used to train the model, the environment for deploying the model, and the Python version to use. -* ``score.py``: This script contains the ``load_model()`` and ``predict()`` functions. The ``load_model()`` function understands the format of the saved model and loads it into memory. The ``predict()`` function makes inferences for the deployed model. You can add hooks to perform operations before and after the inference. You can also modify this script with your specifics. - -To create the model artifacts, use the ``.prepare()`` method. The ``.prepare()`` method includes parameters for storing model provenance information. - -.. include:: _template/prepare.rst - -Verify ------- -.. include:: _template/verify.rst -* ``data (Union[dict, str])``: The data is used to test if deployment works in the local environment. - -Save ----- - -.. include:: _template/save.rst - -Deploy ------- - -.. include:: _template/deploy.rst - -Predict -------- - -.. include:: _template/predict.rst - -* ``data: Any``: JSON serializable data to used for making inferences. - -The ``.predict()`` and ``.verify()`` methods take the same data formats. You must ensure that the data passed into and returned by the ``predict()`` function in the ``score.py`` file is JSON serializable. - -Load -==== - -You can restore serialization models from model artifacts, from model deployments or from models in the model catalog. This section provides details on how to restore serialization models. - -.. include:: _template/loading_model_artifact.rst +Open ``automl_model_artifact/score.py`` and edit the code to instantiate the model class. The edits are highlighted - .. code-block:: python3 - - from ads.model.framework.automl_model import AutoMLModel - - model = AutoMLModel.from_model_artifact( - uri="/folder_to_your/artifact.zip", - model_file_name="model.pkl", - artifact_dir="/folder_store_artifact" - ) - -.. include:: _template/loading_model_catalog.rst - -.. code-block:: python3 - - from ads.model.framework.automl_model import AutoMLModel - - model = AutoMLModel.from_model_catalog(model_id="", - model_file_name="model.pkl", - artifact_dir="/folder_store_artifact") - -.. include:: _template/loading_model_deployment.rst - -.. code-block:: python3 - - from ads.model.generic_model import AutoMLModel - - model = AutoMLModel.from_model_deployment( - model_deployment_id="", - model_file_name="model.pkl", - artifact_dir=tempfile.mkdtemp()) - -Delete a Deployment -=================== - -.. include:: _template/delete_deployment.rst - -Example -======= - -.. code-block:: python3 - + :emphasize-lines: 21,30 + + # score.py 1.0 generated by ADS 2.8.1 on 20230226_214703 + import json + import os + import sys + import importlib + from cloudpickle import cloudpickle + from functools import lru_cache + from io import StringIO + import logging + import sys + import automl import pandas as pd import numpy as np - import tempfile - from sklearn.metrics import roc_auc_score, confusion_matrix, make_scorer, f1_score - from sklearn.linear_model import LogisticRegression - from sklearn.compose import make_column_selector as selector - from sklearn.impute import SimpleImputer - from sklearn.preprocessing import StandardScaler, OneHotEncoder - from sklearn.compose import ColumnTransformer - from sklearn.pipeline import Pipeline - from sklearn.datasets import fetch_openml - from sklearn.model_selection import train_test_split - import ads - import automl - from automl import init - from ads.model import AutoMLModel - from ads.common.model_metadata import UseCaseType - from ads.model.framework.automl_model import AutoMLModel + model_name = 'model.pkl' + + """ + Inference script. This script is used for prediction by scoring server when schema is known. + """ + + def init_automl_logger(): + logger = logging.getLogger("automl") + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(logging.ERROR) + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + handler.setFormatter(formatter) + logger.addHandler(handler) + automl.init(engine="local", engine_opts={"n_jobs": 1}, logger=logger) + + + @lru_cache(maxsize=10) + def load_model(model_file_name=model_name): + """ + Loads model from the serialized format + + Returns + ------- + model: a model instance on which predict API can be invoked + """ + init_automl_logger() + model_dir = os.path.dirname(os.path.realpath(__file__)) + if model_dir not in sys.path: + sys.path.insert(0, model_dir) + contents = os.listdir(model_dir) + if model_file_name in contents: + print(f'Start loading {model_file_name} from model directory {model_dir} ...') + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), model_file_name), "rb") as file: + loaded_model = cloudpickle.load(file) + + print("Model is successfully loaded.") + return loaded_model + else: + raise Exception(f'{model_file_name} is not found in model directory {model_dir}') + + @lru_cache(maxsize=1) + def fetch_data_type_from_schema(input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema['schema']: + data_type[col['name']] = col['dtype'] + else: + print("input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.automl_model.AutoMLModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization.") + return data_type + + def deserialize(data, input_schema_path, task=None): + """ + Deserialize json serialization data to data in original type when sent to predict. + + Parameters + ---------- + data: serialized input data. + input_schema_path: path of input schema. + task: Machine learning task, supported: classification, regression, anomaly_detection, forecasting. Defaults to None. + + Returns + ------- + data: deserialized input data. + + """ + + if isinstance(data, bytes): + return pd.read_json(StringIO(data.decode("utf-8"))) + + data_type = data.get('data_type', '') if isinstance(data, dict) else '' + json_data = data.get('data', data) if isinstance(data, dict) else data + + if task and task == "forecasting": + if data_type: + data_type = data_type.split("'")[1] + try: + module, spec = ".".join(data_type.split(".")[:-1]), data_type.split(".")[-1] + lib = importlib.import_module(name=module) + func = getattr(lib, spec) + return pd.DataFrame(index=func(json_data)) + except: + logging.warning("Cannot autodetect the type of the input data. By default, convert input data to pd.DatetimeIndex and feed the model an empty pandas DataFrame with index as input data. If assumption is not correct, modify the score.py and check with .verify() before saving model with .save().") + return pd.DataFrame(index=pd.DatetimeIndex(json_data)) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=fetch_data_type_from_schema(input_schema_path)) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + + return json_data + + def pre_inference(data, input_schema_path, task=None): + """ + Preprocess data + + Parameters + ---------- + data: Data format as expected by the predict API of the core estimator. + input_schema_path: path of input schema. + task: Machine learning task, supported: classification, regression, anomaly_detection, forecasting. Defaults to None. + + Returns + ------- + data: Data format after any processing. + + """ + data = deserialize(data, input_schema_path, task) + return data + + def post_inference(yhat): + """ + Post-process the model results + + Parameters + ---------- + yhat: Data format after calling model.predict. + + Returns + ------- + yhat: Data format after any processing. + + """ + if isinstance(yhat, pd.core.frame.DataFrame): + yhat = yhat.values + return yhat.tolist() + + def predict(data, model=load_model(), input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns prediction given the model and data to predict + + Parameters + ---------- + model: Model instance returned by load_model API + data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame + input_schema_path: path of input schema. + + Returns + ------- + predictions: Output from scoring server + Format: {'prediction': output from model.predict method} + + """ + task = model.task if hasattr(model, "task") else None + features = pre_inference(data, input_schema_path, task) + yhat = post_inference( + model.predict(features) + ) + return {'prediction': yhat} + + +Verify score.py changes by running inference locally + .. code-block:: python3 - dataset = fetch_openml(name='adult', as_frame=True) - df, y = dataset.data, dataset.target - - # Several of the columns are incorrectly labeled as category type in the original dataset - numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] - for col in df.columns: - if col in numeric_columns: - df[col] = df[col].astype(int) - + automl_model.verify(X_test.iloc[:2]) - X_train, X_test, y_train, y_test = train_test_split(df, - y.map({'>50K': 1, '<=50K': 0}).astype(int), - train_size=0.7, - random_state=0) +Save model and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. + .. code-block:: python3 - init(engine='local') - est = automl.Pipeline(task='classification') - est.fit(X_train, y_train) - - ads.set_auth("resource_principal") - artifact_dir = tempfile.mkdtemp() - automl_model = AutoMLModel(estimator=model, artifact_dir=artifact_dir) - automl_model.prepare(inference_conda_env="automlx_p38_cpu_v1", - training_conda_env="automlx_p38_cpu_v1", - use_case_type=UseCaseType.BINARY_CLASSIFICATION, - X_sample=X_test, - force_overwrite=True) - automl_model.verify(X_test.iloc[:2]) model_id = automl_model.save(display_name='Demo AutoMLModel model') deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') automl_model.predict(X_test.iloc[:2]) - automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) +Run Prediction with oci raw-request command + + .. code-block:: python3 + + + export uri=https://modeldeployment.{region}.oci.customer-oci.com/ocid1.datasciencemodeldeployment.oc1.xxx.xxxxx/predict +oci raw-request \ + --http-method POST \ + --target-uri $uri \ + --request-body file://data-payload \ No newline at end of file diff --git a/docs/source/user_guide/model_serialization/boilerplate/summary_status.rst b/docs/source/user_guide/model_serialization/boilerplate/summary_status.rst index 055fbb8f6..363728847 100644 --- a/docs/source/user_guide/model_serialization/boilerplate/summary_status.rst +++ b/docs/source/user_guide/model_serialization/boilerplate/summary_status.rst @@ -1,4 +1,4 @@ -You can call the ``.summary_status()`` method after a model serialization instance such as ``AutoMLModel``, ``GenericModel``, ``SklearnModel``, ``TensorFlowModel``, or ``PyTorchModel`` is created. The ``.summary_status()`` method returns a Pandas dataframe that guides you through the entire workflow. It shows which methods are available to call and which ones aren't. Plus it outlines what each method does. If extra actions are required, it also shows those actions. +You can call the ``.summary_status()`` method after a model serialization instance such as ``GenericModel``, ``SklearnModel``, ``TensorFlowModel``, or ``PyTorchModel`` is created. The ``.summary_status()`` method returns a Pandas dataframe that guides you through the entire workflow. It shows which methods are available to call and which ones aren't. Plus it outlines what each method does. If extra actions are required, it also shows those actions. The following image displays an example summary status table created after a user initiates a model instance. The table's Step column displays a Status of Done for the initiate step. And the ``Details`` column explains what the initiate step did such as generating a ``score.py`` file. The Step column also displays the ``prepare()``, ``verify()``, ``save()``, ``deploy()``, and ``predict()`` methods for the model. The Status column displays which method is available next. After the initiate step, the ``prepare()`` method is available. The next step is to call the ``prepare()`` method. diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index 18ef1a754..470f314c0 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -1,8 +1,9 @@ -The Oracle AutoMLx -================== +.. _Oralce_AutoMlx: +Oracle AutoMLx +============== .. note:: - The ADS AutoML is deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with the Oracle AutoMLx library. + The ADS AutoML is deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with Oracle AutoMLx library. With AutoMLx moving into the open source domain, working directly with the library has many benefits. One of these is that ADS and AutoMLx can now have independent version cadences. AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. @@ -14,5 +15,4 @@ The Oracle AutoMLx :maxdepth: 1 quick_start - overview diff --git a/docs/source/user_guide/model_training/automl/overview.rst b/docs/source/user_guide/model_training/automl/overview.rst deleted file mode 100644 index f7683bf69..000000000 --- a/docs/source/user_guide/model_training/automl/overview.rst +++ /dev/null @@ -1,68 +0,0 @@ -Overview -======== - --------- -Pipeline --------- - -An AutoML Pipeline consists of these four main stages: - -.. image:: ../figures/pipeline.png - -The stages operate in sequence:  - -.. contents:: -------------------- -Algorithm Selection -------------------- - -With a given dataset and a prediction task, the goal is to identify the algorithm that maximizes the model score. This best algorithm is not always intuitive and simply picking a complex model is suboptimal for many use cases. The ADS algorithm selection stage is designed to rank algorithms based on their estimated predictive performance on the given dataset. - -.. image:: ../figures/algorithm_selection.png - -For a given dataset, the algorithm selection process is as follows: - -#. Extract relevant dataset characteristics, such as dataset shape, feature correlations, and appropriate meta-features. -#. Invoke specialized score-prediction metamodels that were learned to predict algorithm performance across a wide variety of datasets and domains. -#. Rank algorithms based on their predicted performance. -#. Select the optimal algorithm. - ------------------ -Adaptive Sampling ------------------ - -Adaptive sampling iteratively subsamples the dataset and evaluates each sample to obtain a score for a specific algorithm. The goal is to find the smallest sample size that adequately represents the full dataset. It is used in subsequent pipeline stages without sacrificing the quality of the model. - -.. image:: ../figures/adaptive_sampling.png - -The adaptive sampling process is as follows: - -#. For a given algorithm and dataset, identify a representative sample. -#. Leverage meta-learning to predict algorithm performance on the given sample. -#. Iterate until the score converges. -#. The identified sample is then used for subsequent stages of the AutoML Pipeline. - ------------------ -Feature Selection ------------------ - -The feature selection stage aims to select a subset of features that are highly predictive of the target. This speeds up model training without loss of predictive performance. The ADS feature selection approach leverages meta-learning to intelligently identify the optimal feature subset for a given algorithm and dataset. The high level process is: - -.. image:: ../figures/feature_selection.png - -For a given dataset, the feature selection process is as follows: - -#. Obtain the dataset meta-features, similar to those obtained in the algorithm selection stage. -#. Rank all features using multiple ranking algorithms. Feature rankings are ordered lists of features from most to least important. -#. For each feature ranking, the optimal feature subset is identified. -#. Algorithm performance is predicted by leveraging meta-learning on a given feature subset. -#. Iterating over multiple feature subsets, the optimal subset is determined. - ---------------------- -Hyperparameter Tuning ---------------------- - -The hyperparameter tuning stage determines the optimal values for the model's hyperparameters. Generally, tuning is the most time-consuming stage of an AutoML pipeline. Therefore, the hyperparameter tuning process is designed with efficiency and scalability as first-order requirements. The ADS tuning strategy is summarized as: - -.. image:: ../figures/tuning.png - From 3fcd0518996838556bd90b763b933d4fcbf479c9 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 27 Feb 2023 17:39:51 -0800 Subject: [PATCH 084/147] ODSC-39261: fix the link --- .../frameworks/automlmodel.rst | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index e0e506645..b74f9cc9f 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -5,7 +5,7 @@ AutoMLModel .. note:: -The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:link <_Oralce_AutoMlx>`` for more detailed information. +The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:`link <_Oralce_AutoMlx>` for more detailed information. To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. @@ -58,7 +58,7 @@ Example Open ``automl_model_artifact/score.py`` and edit the code to instantiate the model class. The edits are highlighted - .. code-block:: python3 - :emphasize-lines: 21,30 + :emphasize-lines: 21,22,23,24,25,26,27,28,29,30,101,102,103,104,105,106,107,108,109,110,171 # score.py 1.0 generated by ADS 2.8.1 on 20230226_214703 import json @@ -91,7 +91,6 @@ Open ``automl_model_artifact/score.py`` and edit the code to instantiate the mod logger.addHandler(handler) automl.init(engine="local", engine_opts={"n_jobs": 1}, logger=logger) - @lru_cache(maxsize=10) def load_model(model_file_name=model_name): """ @@ -162,16 +161,15 @@ Open ``automl_model_artifact/score.py`` and edit the code to instantiate the mod json_data = data.get('data', data) if isinstance(data, dict) else data if task and task == "forecasting": - if data_type: + try: data_type = data_type.split("'")[1] - try: - module, spec = ".".join(data_type.split(".")[:-1]), data_type.split(".")[-1] - lib = importlib.import_module(name=module) - func = getattr(lib, spec) - return pd.DataFrame(index=func(json_data)) - except: - logging.warning("Cannot autodetect the type of the input data. By default, convert input data to pd.DatetimeIndex and feed the model an empty pandas DataFrame with index as input data. If assumption is not correct, modify the score.py and check with .verify() before saving model with .save().") - return pd.DataFrame(index=pd.DatetimeIndex(json_data)) + module, spec = ".".join(data_type.split(".")[:-1]), data_type.split(".")[-1] + lib = importlib.import_module(name=module) + func = getattr(lib, spec) + return pd.DataFrame(index=func(json_data)) + except: + logging.warning("Cannot autodetect the type of the model input data. By default, convert input data to pd.DatetimeIndex and feed the model with an empty pandas DataFrame with index as input data. If assumption is not correct, modify the score.py and check with .verify() before saving model with .save().") + return pd.DataFrame(index=pd.DatetimeIndex(json_data)) if "pandas.core.series.Series" in data_type: return pd.Series(json_data) if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): @@ -240,7 +238,7 @@ Open ``automl_model_artifact/score.py`` and edit the code to instantiate the mod return {'prediction': yhat} -Verify score.py changes by running inference locally +Verify score.py changes by running inference locally. .. code-block:: python3 automl_model.verify(X_test.iloc[:2], auto_serialize_data=True) From 70ea2dd04fbbf8bcab07b06dceade155b7cd1d71 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 27 Feb 2023 17:40:34 -0800 Subject: [PATCH 085/147] ODSC-39261: fix the link --- .../user_guide/model_registration/frameworks/automlmodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index b74f9cc9f..b53d4c28f 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -5,7 +5,7 @@ AutoMLModel .. note:: -The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:`link <_Oralce_AutoMlx>` for more detailed information. +The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:`link ` for more detailed information. To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. From cefbeb5d8b8969e21f1f2712df38308457a86567 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 27 Feb 2023 17:43:48 -0800 Subject: [PATCH 086/147] remove class docs --- docs/source/ads.explanations.rst | 61 -------------------------------- docs/source/ads.model.rst | 8 ----- docs/source/ads.rst | 1 - 3 files changed, 70 deletions(-) delete mode 100644 docs/source/ads.explanations.rst diff --git a/docs/source/ads.explanations.rst b/docs/source/ads.explanations.rst deleted file mode 100644 index 8077b1a4e..000000000 --- a/docs/source/ads.explanations.rst +++ /dev/null @@ -1,61 +0,0 @@ -ads.explanations package -======================== - -Submodules ----------- - -ads.explanations.base\_explainer module ---------------------------------------- - -.. automodule:: ads.explanations.base_explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.explainer module ---------------------------------- - -.. automodule:: ads.explanations.explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_global\_explainer module ----------------------------------------------- - -.. automodule:: ads.explanations.mlx_global_explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_interface module --------------------------------------- - -.. automodule:: ads.explanations.mlx_interface - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_local\_explainer module ---------------------------------------------- - -.. automodule:: ads.explanations.mlx_local_explainer - :members: - :undoc-members: - :show-inheritance: - -ads.explanations.mlx\_whatif\_explainer module ----------------------------------------------- - -.. automodule:: ads.explanations.mlx_whatif_explainer - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: ads.explanations - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/ads.model.rst b/docs/source/ads.model.rst index 87731a054..11a273681 100644 --- a/docs/source/ads.model.rst +++ b/docs/source/ads.model.rst @@ -52,14 +52,6 @@ ads.model.extractor.model\_artifact module :undoc-members: :show-inheritance: -ads.model.extractor.automl\_extractor module --------------------------------------------- - -.. automodule:: ads.model.extractor.automl_extractor - :members: - :undoc-members: - :show-inheritance: - ads.model.extractor.xgboost\_extractor module --------------------------------------------- diff --git a/docs/source/ads.rst b/docs/source/ads.rst index b2db11add..08c89799a 100644 --- a/docs/source/ads.rst +++ b/docs/source/ads.rst @@ -14,7 +14,6 @@ Subpackages ads.dataflow ads.dataset ads.evaluations - ads.explanations ads.feature_engineering ads.hpo ads.jobs From 13f62dfa3d2ed7e8a26beb5ceaa09b9090c21ad5 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 27 Feb 2023 18:08:12 -0800 Subject: [PATCH 087/147] add links --- docs/source/user_guide/model_training/automl/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index 470f314c0..3815837e0 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -3,7 +3,7 @@ Oracle AutoMLx ============== .. note:: - The ADS AutoML is deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with Oracle AutoMLx library. + The ADS `AutoML `__, `AutoMLProvider `__, `MLXGlobalExplainer `, `MLXLocalExplainer `__ and `MLXWhatIfExplainer `__, `AutoMLModel `__, `AutoMLExtractor `__ are deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with Oracle AutoMLx library. With AutoMLx moving into the open source domain, working directly with the library has many benefits. One of these is that ADS and AutoMLx can now have independent version cadences. AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. From 5801dafb647c323fd9354349a99df2b5b7747de5 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 28 Feb 2023 14:18:49 -0800 Subject: [PATCH 088/147] fix the link --- docs/source/user_guide/model_training/automl/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index 3815837e0..e65ed50dc 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -3,7 +3,7 @@ Oracle AutoMLx ============== .. note:: - The ADS `AutoML `__, `AutoMLProvider `__, `MLXGlobalExplainer `, `MLXLocalExplainer `__ and `MLXWhatIfExplainer `__, `AutoMLModel `__, `AutoMLExtractor `__ are deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with Oracle AutoMLx library. + The ADS `AutoML `__, `AutoMLProvider `__, `MLXGlobalExplainer `__, `MLXLocalExplainer `__ and `MLXWhatIfExplainer `__, `AutoMLModel `__, `AutoMLExtractor `__ are deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with Oracle AutoMLx library. With AutoMLx moving into the open source domain, working directly with the library has many benefits. One of these is that ADS and AutoMLx can now have independent version cadences. AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. From 82b5038a83d41bac8ff52220c858b9e54758a360 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 28 Feb 2023 14:36:18 -0800 Subject: [PATCH 089/147] ODSC-38627: adding class doc and resolve comments --- docs/source/ads.model.rst | 8 +++++++ docs/source/ads.model_framework.rst | 8 +++++++ .../frameworks/huggingfacemodel.rst | 21 +++++++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/docs/source/ads.model.rst b/docs/source/ads.model.rst index 87731a054..356702fd2 100644 --- a/docs/source/ads.model.rst +++ b/docs/source/ads.model.rst @@ -116,6 +116,14 @@ ads.model.extractor.pytorch\_extractor module :undoc-members: :show-inheritance: +ads.model.extractor.huggingface\_extractor module +--------------------------------------------- + +.. automodule:: ads.model.extractor.huggingface_extractor + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/docs/source/ads.model_framework.rst b/docs/source/ads.model_framework.rst index bb1923ab4..20589c716 100644 --- a/docs/source/ads.model_framework.rst +++ b/docs/source/ads.model_framework.rst @@ -52,6 +52,14 @@ ads.model.framework.xgboost\_model module :undoc-members: :show-inheritance: +ads.model.framework.huggingface\_model module +----------------------------------------- + +.. automodule:: ads.model.framework.huggingface_model + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 1998b822f..77d089b4b 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -164,12 +164,19 @@ Model deployment endpoints can be invoked with the oci sdk. This example invokes >>> import requests >>> import oci - >>> import ads + >>> from ads.common.auth import default_signer + >>> import cloudpickle + >>> import PIL.Image >>> import cloudpickle >>> headers = {"Content-Type": "application/octet-stream"} >>> endpoint = huggingface_pipeline_model.model_deployment.url + "/predict" - >>> preds = requests.post(endpoint, data=image_bytes, auth=ads.common.auth.default_signer()['signer'], headers=headers).json() + ## download the image + image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + image = PIL.Image.open(requests.get(image_link, stream=True).raw) + image_bytes = cloudpickle.dumps(image) + + >>> preds = requests.post(endpoint, data=image_bytes, auth=default_signer()['signer'], headers=headers).json() >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) [{'score': 0.9879, 'label': 'LABEL_184'}, {'score': 0.9973, 'label': 'snow'}, @@ -182,9 +189,11 @@ Example .. code-block:: python3 from transformers import pipeline + from ads.model import HuggingFacePipelineModel + import tempfile import PIL.Image - import ads + from ads.common.auth import default_signer import requests import cloudpickle @@ -205,10 +214,10 @@ Example # Autogenerate score.py, serialized model, runtime.yaml conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" - python_version = "3.x" + python_version = "3.x" # Remember to update 3.x with your actual python version, e.g. 3.8 zero_shot_image_classification_model.prepare(inference_conda_env=conda_pack_path, inference_python_version = python_version, force_overwrite=True) - ## Test data + ## Convert payload to bytes data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} body = cloudpickle.dumps(data) # convert image to bytes @@ -231,7 +240,7 @@ Example zero_shot_image_classification_model.predict(body) ### Invoke the model by sending bytes - auth = ads.common.auth.default_signer()['signer'] + auth = default_signer()['signer'] endpoint = zero_shot_image_classification_model.model_deployment.url + "/predict" headers = {"Content-Type": "application/octet-stream"} requests.post(endpoint, data=body, auth=auth, headers=headers).json() From 32f80a15ecbffb74364d5097ac6cbc342d55672d Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 28 Feb 2023 15:45:39 -0800 Subject: [PATCH 090/147] resolve comments --- docs/source/user_guide/model_registration/quick_start.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 5dc1f210e..86345f331 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -321,7 +321,10 @@ Other Frameworks preds = catboost_estimator.predict(X_test) # Instantiate ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model - catboost_model = GenericModel(estimator=catboost_estimator, artifact_dir=tempfile.mkdtemp()) + catboost_model = GenericModel(estimator=catboost_estimator, + artifact_dir=tempfile.mkdtemp(), + model_save_serializer="cloudpickle", + model_input_serializer="json") # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json catboost_model.prepare( From a91bb43f85e3eeda9f208168ed74f4c63153bbff Mon Sep 17 00:00:00 2001 From: Lu Peng Date: Tue, 28 Feb 2023 17:27:15 -0800 Subject: [PATCH 091/147] Updated doc. --- docs/source/user_guide/model_deployment/deploy.rst | 4 +++- docs/source/user_guide/model_deployment/update.rst | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/model_deployment/deploy.rst b/docs/source/user_guide/model_deployment/deploy.rst index 96b7042f2..b04a41133 100644 --- a/docs/source/user_guide/model_deployment/deploy.rst +++ b/docs/source/user_guide/model_deployment/deploy.rst @@ -67,6 +67,8 @@ The Data Science Model Deployment supports service managed conda runtime and cus ModelDeploymentContainerRuntime ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To use the ``ModelDeploymentContainerRuntime``, you need to first push the image to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. + You can define the model deployment container runtime by passing the following properties to ``ModelDeploymentContainerRuntime`` object: * ``model_uri``: The model ocid or path to model artifacts directory that is used in the model deployment. @@ -179,7 +181,7 @@ A ``ModelDeployment`` object can be serialized to a YAML file by calling ``to_ya kind: infrastructure ... """") - + deployment.deploy(wait_for_completion=False) Here is an example of a YAML file representing the ``ModelDeployment`` with docker container runtime defined in the preceding examples: diff --git a/docs/source/user_guide/model_deployment/update.rst b/docs/source/user_guide/model_deployment/update.rst index 349e08fbc..59dde30cb 100644 --- a/docs/source/user_guide/model_deployment/update.rst +++ b/docs/source/user_guide/model_deployment/update.rst @@ -15,6 +15,6 @@ Or, you could update the instance shape with: .. code-block:: python3 - deployment.infrastructure.with_shape_name("VM.Standard.E4.Flex") + deployment.infrastructure.with_shape_name("VM.Standard.E4.Flex").with_shape_config_details(ocpus=2, memory_in_gbs=32) deployment.update() From 654542edcb61df439d9fcdb058b7713839e8df6b Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 1 Mar 2023 13:37:23 -0500 Subject: [PATCH 092/147] Update infrastructure config example code. --- .../user_guide/jobs/infra_and_runtime.rst | 47 +++++++++-------- .../jobs/tabs/container_runtime.rst | 51 ++++++++++--------- .../user_guide/jobs/tabs/git_notebook.rst | 11 ++-- .../user_guide/jobs/tabs/git_runtime.rst | 11 ++-- .../user_guide/jobs/tabs/infra_config.rst | 42 +++++++++++++++ .../jobs/tabs/name_substitution.rst | 1 - .../user_guide/jobs/tabs/notebook_runtime.rst | 11 ++-- .../user_guide/jobs/tabs/python_runtime.rst | 11 ++-- .../user_guide/jobs/tabs/quick_start_job.rst | 11 ++-- .../user_guide/jobs/tabs/script_runtime.rst | 11 ++-- .../user_guide/jobs/tabs/training_job.rst | 16 +++--- 11 files changed, 140 insertions(+), 83 deletions(-) create mode 100644 docs/source/user_guide/jobs/tabs/infra_config.rst diff --git a/docs/source/user_guide/jobs/infra_and_runtime.rst b/docs/source/user_guide/jobs/infra_and_runtime.rst index e7dba4ca0..f8bb479dc 100644 --- a/docs/source/user_guide/jobs/infra_and_runtime.rst +++ b/docs/source/user_guide/jobs/infra_and_runtime.rst @@ -18,23 +18,7 @@ Infrastructure The Data Science Job infrastructure is defined by a :py:class:`~ads.jobs.DataScienceJob` instance. For example: -.. code-block:: python3 - - from ads.jobs import DataScienceJob - - infrastructure = ( - DataScienceJob() - .with_compartment_id("") - .with_project_id("") - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - # Minimum block storage size is 50 (GB) - .with_block_storage_size(50) - .with_log_group_id("") - .with_log_id("") - ) +.. include:: ../jobs/tabs/infra_config.rst When creating a :py:class:`~ads.jobs.DataScienceJob` instance, the following configurations are required: @@ -55,11 +39,21 @@ Using Configurations from Notebook If you are creating a job from an OCI Data Science `Notebook Session `_, -the same infrastructure configurations from the notebook session will be used as defaults. +the same infrastructure configurations from the notebook session will be used as defaults, including: + +* Compartment ID +* Project ID +* Subnet ID +* Compute Shape +* Block Storage Size + You can initialize the :py:class:`~ads.jobs.DataScienceJob` with the logging configurations and override the other options as needed. For example: -.. code-block:: python3 +.. tabs:: + + .. code-tab:: python + :caption: Python from ads.jobs import DataScienceJob @@ -69,11 +63,22 @@ with the logging configurations and override the other options as needed. For ex .with_log_id("") # Use a GPU shape for the job, # regardless of the shape used by the notebook session - .with_shape_name("VM.GPU2.1") + .with_shape_name("VM.GPU3.1") # compartment ID, project ID, subnet ID and block storage will be # the same as the ones set in the notebook session ) + .. code-tab:: yaml + :caption: YAML + + kind: infrastructure + type: dataScienceJob + spec: + logGroupId: + logId: + shapeName: VM.GPU3.1 + + Compute Shapes -------------- @@ -136,7 +141,7 @@ Environment variables enclosed by ``${...}`` will be substituted. For example: .. include:: ../jobs/tabs/runtime_envs.rst -.. code-block:: python3 +.. code-block:: python for k, v in runtime.environment_variables.items(): print(f"{k}: {v}") diff --git a/docs/source/user_guide/jobs/tabs/container_runtime.rst b/docs/source/user_guide/jobs/tabs/container_runtime.rst index 5d0d39148..65f8a1fc9 100644 --- a/docs/source/user_guide/jobs/tabs/container_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/container_runtime.rst @@ -6,30 +6,32 @@ from ads.jobs import Job, DataScienceJob, ContainerRuntime job = ( - Job(name="My Job") - .with_infrastructure( - DataScienceJob() - .with_log_group_id("") - .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. - .with_compartment_id("") - .with_project_id("") - # For default networking, no need to specify subnet ID - .with_subnet_id("") - .with_shape_name("VM.Standard.E3.Flex") - # Shape config details are applicable only for the flexible shapes. - .with_shape_config_details(memory_in_gbs=16, ocpus=1) - .with_block_storage_size(50) - ) - .with_runtime( - ContainerRuntime() - .with_image(".ocir.io//") - .with_environment_variable(GREETINGS="Welcome to OCI Data Science") - .with_entrypoint(["/bin/sh", "-c"]) - .with_cmd("sleep 5 && echo $GREETINGS") - ) + Job(name="My Job") + .with_infrastructure( + DataScienceJob() + # Configure logging for getting the job run outputs. + .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. + .with_log_id("") + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). + .with_block_storage_size(50) + ) + .with_runtime( + ContainerRuntime() + .with_image(".ocir.io//") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_entrypoint(["/bin/sh", "-c"]) + .with_cmd("sleep 5 && echo $GREETINGS") + ) ) .. code-tab:: yaml @@ -45,7 +47,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/git_notebook.rst b/docs/source/user_guide/jobs/tabs/git_notebook.rst index 5c42b3ecb..bc37e02e7 100644 --- a/docs/source/user_guide/jobs/tabs/git_notebook.rst +++ b/docs/source/user_guide/jobs/tabs/git_notebook.rst @@ -9,18 +9,20 @@ Job(name="My Job") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -47,7 +49,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/git_runtime.rst b/docs/source/user_guide/jobs/tabs/git_runtime.rst index e8a9b596d..0487d5223 100644 --- a/docs/source/user_guide/jobs/tabs/git_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/git_runtime.rst @@ -9,18 +9,20 @@ Job(name="My Job") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -54,7 +56,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/infra_config.rst b/docs/source/user_guide/jobs/tabs/infra_config.rst new file mode 100644 index 000000000..683ef7a8b --- /dev/null +++ b/docs/source/user_guide/jobs/tabs/infra_config.rst @@ -0,0 +1,42 @@ +.. tabs:: + + .. code-tab:: python + :caption: Python + + from ads.jobs import Job, DataScienceJob, GitPythonRuntime + + infrastructure = ( + DataScienceJob() + # Configure logging for getting the job run outputs. + .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. + .with_log_id("") + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). + .with_block_storage_size(50) + ) + + .. code-tab:: yaml + :caption: YAML + + kind: infrastructure + type: dataScienceJob + spec: + blockStorageSize: 50 + compartmentId: + logGroupId: + logId: + projectId: + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + shapeName: VM.Standard.E3.Flex + subnetId: diff --git a/docs/source/user_guide/jobs/tabs/name_substitution.rst b/docs/source/user_guide/jobs/tabs/name_substitution.rst index 6d46b35f7..00590ccc3 100644 --- a/docs/source/user_guide/jobs/tabs/name_substitution.rst +++ b/docs/source/user_guide/jobs/tabs/name_substitution.rst @@ -36,7 +36,6 @@ type: dataScienceJob spec: compartmentId: - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst index 5b9e6638f..eedd259c1 100644 --- a/docs/source/user_guide/jobs/tabs/notebook_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/notebook_runtime.rst @@ -9,18 +9,20 @@ Job(name="My Job") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -49,7 +51,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/python_runtime.rst b/docs/source/user_guide/jobs/tabs/python_runtime.rst index ee1b7d682..ed10c2d5f 100644 --- a/docs/source/user_guide/jobs/tabs/python_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/python_runtime.rst @@ -9,18 +9,20 @@ Job(name="My Job") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -61,7 +63,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/quick_start_job.rst b/docs/source/user_guide/jobs/tabs/quick_start_job.rst index c1333d305..82839c925 100644 --- a/docs/source/user_guide/jobs/tabs/quick_start_job.rst +++ b/docs/source/user_guide/jobs/tabs/quick_start_job.rst @@ -9,18 +9,20 @@ Job(name="My Job") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -49,7 +51,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/script_runtime.rst b/docs/source/user_guide/jobs/tabs/script_runtime.rst index a9095d9af..813a40972 100644 --- a/docs/source/user_guide/jobs/tabs/script_runtime.rst +++ b/docs/source/user_guide/jobs/tabs/script_runtime.rst @@ -9,18 +9,20 @@ Job(name="My Job") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # For default networking, no need to specify subnet ID .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -53,7 +55,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: diff --git a/docs/source/user_guide/jobs/tabs/training_job.rst b/docs/source/user_guide/jobs/tabs/training_job.rst index 55094a0ae..a9faaf511 100644 --- a/docs/source/user_guide/jobs/tabs/training_job.rst +++ b/docs/source/user_guide/jobs/tabs/training_job.rst @@ -9,15 +9,20 @@ Job(name="Training RNN with PyTorch") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. .with_log_id("") - .with_shape_name("VM.GPU3.1") - # The following infrastructure configurations are optional - # if you are in an OCI data science notebook session. - # The configurations of the notebook session will be used as defaults. + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") - # Default block storage size is 50GB + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) ) .with_runtime( @@ -51,7 +56,6 @@ blockStorageSize: 50 compartmentId: jobInfrastructureType: STANDALONE - jobType: DEFAULT logGroupId: logId: projectId: From 3e24cd2b19a3173aaea21131157a38e1a519b26a Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 13:11:21 -0800 Subject: [PATCH 093/147] ODSC-36246: adding more example --- .../model_registration/quick_start.rst | 1 - .../model_serialization/genericmodel.rst | 283 ++++++++++++++++-- 2 files changed, 265 insertions(+), 19 deletions(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 86345f331..2086c7c6a 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -296,7 +296,6 @@ Other Frameworks from ads.model.generic_model import GenericModel from catboost import CatBoostRegressor - ads.set_auth(auth="resource_principal") # Initialize data diff --git a/docs/source/user_guide/model_serialization/genericmodel.rst b/docs/source/user_guide/model_serialization/genericmodel.rst index 4d9bd175d..c0ae89fe6 100644 --- a/docs/source/user_guide/model_serialization/genericmodel.rst +++ b/docs/source/user_guide/model_serialization/genericmodel.rst @@ -139,26 +139,57 @@ By default, the ``GenericModel`` serializes to a pickle file. The following exam .. code-block:: python3 import tempfile - from ads.catalog.model import ModelCatalog + + import ads from ads.model.generic_model import GenericModel + from catboost import CatBoostRegressor - class Toy: - def predict(self, x): - return x ** 2 - model = Toy() - - generic_model = GenericModel(estimator=model, artifact_dir=tempfile.mkdtemp()) - generic_model.summary_status() - generic_model.prepare( - inference_conda_env="dataexpl_p37_cpu_v3", - model_file_name="toy_model.pkl", - force_overwrite=True - ) - generic_model.verify(2) - model_id = generic_model.save() - generic_model.deploy() - generic_model.predict(2) - generic_model.delete_deployment(wait_for_completion=True) + + ads.set_auth(auth="resource_principal") + + # Initialize data + + X_train = [[1, 4, 5, 6], + [4, 5, 6, 7], + [30, 40, 50, 60]] + + X_test = [[2, 4, 6, 8], + [1, 4, 50, 60]] + + y_train = [10, 20, 30] + + # Initialize CatBoostRegressor + catboost_estimator = CatBoostRegressor(iterations=2, + learning_rate=1, + depth=2) + # Train a CatBoostRegressor model + catboost_estimator.fit(X_train, y_train) + + # Get predictions + preds = catboost_estimator.predict(X_test) + + # Instantiate ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model + catboost_model = GenericModel(estimator=catboost_estimator, + artifact_dir=tempfile.mkdtemp(), + model_save_serializer="cloudpickle", + model_input_serializer="json") + + # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + catboost_model.prepare( + inference_conda_env="oci://bucket@namespace/path/to/your/conda/pack", + inference_python_version="your_python_version", + X_sample=X_train, + y_sample=y_train, + ) + + # Verify generated artifacts + catboost_model.verify(X_test, auto_serialize_data=True) + + # Register CatBoostRegressor model + model_id = catboost_model.save(display_name="CatBoost Model") + catboost_model.deploy() + catboost_model.predict(X_test) + catboost_model.delete_deployment(wait_for_completion=True) ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. @@ -182,3 +213,219 @@ You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.p model.predict(2) model.delete_deployment(wait_for_completion=True) ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model.model_id) + + +Example -- Save Your Own Model +============================== + +By default, the ``serialize`` in ``GenericModel`` class is True, and it will serialize the model using cloudpickle. However, you can set ``serialize=False`` to disable it. And serialize the model on your own. You just need to copy the serialized model into the ``.artifact_dir``. This example shows step by step how you can do that. +The example is illustrated using an AutoMLx model. + +.. code-block:: python3 + + import automl + import ads + from automl import init + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + from ads.model import GenericModel + + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + X_train.shape, X_test.shape + + # create a AutoMLx model + init(engine='local') + + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + + # Authentication + ads.set_auth(auth="resource_principal") + + # Serialize your model. You can choose your own way to serialize your model. + import cloudpickle + with open("./model.pkl", "wb") as f: + cloudpickle.dump(est, f) + + model = GenericModel(est, artifact_dir = "model_artifact_folder", serialize=False) + model.prepare(inference_conda_env="automlx_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) + +Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder and add implement the ``load_model`` function. You can also edit ``pre_inference`` and ``post_inference`` function. Below is an example implementation of the score.py. +Replace your score.py with the code below. + +.. code-block:: python3 + :emphasize-lines: 28, 29, 30, 31, 122 + + # score.py 1.0 generated by ADS 2.8.2 on 20230301_065458 + import os + import sys + import json + from functools import lru_cache + + model_name = 'model.pkl' + + + """ + Inference script. This script is used for prediction by scoring server when schema is known. + """ + + @lru_cache(maxsize=10) + def load_model(model_file_name=model_name): + """ + Loads model from the serialized format + + Returns + ------- + model: a model instance on which predict API can be invoked + """ + model_dir = os.path.dirname(os.path.realpath(__file__)) + if model_dir not in sys.path: + sys.path.insert(0, model_dir) + contents = os.listdir(model_dir) + if model_file_name in contents: + import cloudpickle + with open(os.path.join(model_dir, model_name), "rb") as f: + model = cloudpickle.load(f) + return model + else: + raise Exception(f'{model_file_name} is not found in model directory {model_dir}') + + @lru_cache(maxsize=1) + def fetch_data_type_from_schema(input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema['schema']: + data_type[col['name']] = col['dtype'] + else: + print("input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.sklearn_model.SklearnModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization.") + return data_type + + def deserialize(data, input_schema_path): + """ + Deserialize json serialization data to data in original type when sent to predict. + + Parameters + ---------- + data: serialized input data. + input_schema_path: path of input schema. + + Returns + ------- + data: deserialized input data. + + """ + + import pandas as pd + import numpy as np + import base64 + from io import BytesIO + if isinstance(data, bytes): + return data + + data_type = data.get('data_type', '') if isinstance(data, dict) else '' + json_data = data.get('data', data) if isinstance(data, dict) else data + + if "numpy.ndarray" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode('utf-8'))) + return np.load(load_bytes, allow_pickle=True) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=fetch_data_type_from_schema(input_schema_path)) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + return json_data + + def pre_inference(data, input_schema_path): + """ + Preprocess data + + Parameters + ---------- + data: Data format as expected by the predict API of the core estimator. + input_schema_path: path of input schema. + + Returns + ------- + data: Data format after any processing. + + """ + return deserialize(data, input_schema_path) + + def post_inference(yhat): + """ + Post-process the model results + + Parameters + ---------- + yhat: Data format after calling model.predict. + + Returns + ------- + yhat: Data format after any processing. + + """ + return yhat.tolist() + + def predict(data, model=load_model(), input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns prediction given the model and data to predict + + Parameters + ---------- + model: Model instance returned by load_model API. + data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame. + input_schema_path: path of input schema. + + Returns + ------- + predictions: Output from scoring server + Format: {'prediction': output from model.predict method} + + """ + features = pre_inference(data, input_schema_path) + yhat = post_inference( + model.predict(features) + ) + return {'prediction': yhat} + +Save the score.py and now call verify to check if it works locally. + +.. code-block:: python3 + + model.verify(X_test.iloc[:2], auto_serialize_data=True) + +After verify run successfully, you can save the model to model catalog, deploy and call predict to invoke the endpoint. + +.. code-block:: python3 + + model_id = model.save(display_name='Demo AutoMLModel model') + deploy = model.deploy(display_name='Demo AutoMLModel deployment') + model.predict(X_test.iloc[:2].to_json()) From 65cbcd1b8623c25f7900bd20bf4596805faedfd6 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 13:37:59 -0800 Subject: [PATCH 094/147] ODSC-36246 : resolve comments --- docs/source/user_guide/model_registration/quick_start.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 2086c7c6a..d6c1f56c0 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -333,9 +333,9 @@ Other Frameworks y_sample=y_train, ) - # Verify generated artifacts + # Verify generated artifacts. The payload looks like this: [[2, 4, 6, 8], [1, 4, 50, 60]] catboost_model.verify(X_test, auto_serialize_data=True) - + # Register CatBoostRegressor model model_id = catboost_model.save(display_name="CatBoost Model") From 666baf16f30371ab8b0b65aae8014bf15304b762 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 13:52:35 -0800 Subject: [PATCH 095/147] adding examples --- .../frameworks/genericmodel.rst | 298 ++++++++++++++++++ .../model_serialization/genericmodel.rst | 4 +- 2 files changed, 299 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst index f45dd166f..c599d4bc4 100644 --- a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst @@ -111,3 +111,301 @@ You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.p # To delete the deployed endpoint uncomment the line below # model.delete_deployment(wait_for_completion=True) + + +Example -- CatBoost +=================== + +Here is a more realistic example using CatBoost model. + +.. code-block:: python3 + + import tempfile + import ads + from ads.model.generic_model import GenericModel + from catboost import CatBoostRegressor + + ads.set_auth(auth="resource_principal") + + # Initialize data + + X_train = [[1, 4, 5, 6], + [4, 5, 6, 7], + [30, 40, 50, 60]] + + X_test = [[2, 4, 6, 8], + [1, 4, 50, 60]] + + y_train = [10, 20, 30] + + # Initialize CatBoostRegressor + catboost_estimator = CatBoostRegressor(iterations=2, + learning_rate=1, + depth=2) + # Train a CatBoostRegressor model + catboost_estimator.fit(X_train, y_train) + + # Get predictions + preds = catboost_estimator.predict(X_test) + + # Instantiate ads.model.generic_model.GenericModel using the trained Custom Model using the trained CatBoost Classifier model + catboost_model = GenericModel(estimator=catboost_estimator, + artifact_dir=tempfile.mkdtemp(), + model_save_serializer="cloudpickle", + model_input_serializer="json") + + # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json + catboost_model.prepare( + inference_conda_env="oci://bucket@namespace/path/to/your/conda/pack", + inference_python_version="your_python_version", + X_sample=X_train, + y_sample=y_train, + ) + + # Verify generated artifacts. Payload looks like this: [[2, 4, 6, 8], [1, 4, 50, 60]] + catboost_model.verify(X_test, auto_serialize_data=True) + + # Register CatBoostRegressor model + model_id = catboost_model.save(display_name="CatBoost Model") + catboost_model.deploy() + catboost_model.predict(X_test) + catboost_model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + +You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. + +.. code-block:: python3 + + import tempfile + from ads.catalog.model import ModelCatalog + from ads.model.generic_model import GenericModel + + class Toy: + def predict(self, x): + return x ** 2 + estimator = Toy() + + model = GenericModel(estimator=estimator) + model.summary_status() + # If you are running the code inside a notebook session and using a service pack, `inference_conda_env` can be omitted. + model.prepare_save_deploy(inference_conda_env="dataexpl_p37_cpu_v3") + model.verify(2) + model.predict(2) + model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model.model_id) + + +Example -- Save Your Own Model +============================== + +By default, the ``serialize`` in ``GenericModel`` class is True, and it will serialize the model using cloudpickle. However, you can set ``serialize=False`` to disable it. And serialize the model on your own. You just need to copy the serialized model into the ``.artifact_dir``. This example shows step by step how you can do that. +The example is illustrated using an AutoMLx model. + +.. code-block:: python3 + + import automl + import ads + from automl import init + from sklearn.datasets import fetch_openml + from sklearn.model_selection import train_test_split + from ads.model import GenericModel + + dataset = fetch_openml(name='adult', as_frame=True) + df, y = dataset.data, dataset.target + + # Several of the columns are incorrectly labeled as category type in the original dataset + numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] + for col in df.columns: + if col in numeric_columns: + df[col] = df[col].astype(int) + + + X_train, X_test, y_train, y_test = train_test_split(df, + y.map({'>50K': 1, '<=50K': 0}).astype(int), + train_size=0.7, + random_state=0) + + X_train.shape, X_test.shape + + # create a AutoMLx model + init(engine='local') + + est = automl.Pipeline(task='classification') + est.fit(X_train, y_train) + + # Authentication + ads.set_auth(auth="resource_principal") + + # Serialize your model. You can choose your own way to serialize your model. + import cloudpickle + with open("./model.pkl", "wb") as f: + cloudpickle.dump(est, f) + + model = GenericModel(est, artifact_dir = "model_artifact_folder", serialize=False) + model.prepare(inference_conda_env="automlx_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) + +Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder and add implement the ``load_model`` function. You can also edit ``pre_inference`` and ``post_inference`` function. Below is an example implementation of the score.py. +Replace your score.py with the code below. + +.. code-block:: python3 + :emphasize-lines: 28, 29, 30, 31, 122 + + # score.py 1.0 generated by ADS 2.8.2 on 20230301_065458 + import os + import sys + import json + from functools import lru_cache + + model_name = 'model.pkl' + + + """ + Inference script. This script is used for prediction by scoring server when schema is known. + """ + + @lru_cache(maxsize=10) + def load_model(model_file_name=model_name): + """ + Loads model from the serialized format + + Returns + ------- + model: a model instance on which predict API can be invoked + """ + model_dir = os.path.dirname(os.path.realpath(__file__)) + if model_dir not in sys.path: + sys.path.insert(0, model_dir) + contents = os.listdir(model_dir) + if model_file_name in contents: + import cloudpickle + with open(os.path.join(model_dir, model_name), "rb") as f: + model = cloudpickle.load(f) + return model + else: + raise Exception(f'{model_file_name} is not found in model directory {model_dir}') + + @lru_cache(maxsize=1) + def fetch_data_type_from_schema(input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema['schema']: + data_type[col['name']] = col['dtype'] + else: + print("input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.sklearn_model.SklearnModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization.") + return data_type + + def deserialize(data, input_schema_path): + """ + Deserialize json serialization data to data in original type when sent to predict. + + Parameters + ---------- + data: serialized input data. + input_schema_path: path of input schema. + + Returns + ------- + data: deserialized input data. + + """ + + import pandas as pd + import numpy as np + import base64 + from io import BytesIO + if isinstance(data, bytes): + return data + + data_type = data.get('data_type', '') if isinstance(data, dict) else '' + json_data = data.get('data', data) if isinstance(data, dict) else data + + if "numpy.ndarray" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode('utf-8'))) + return np.load(load_bytes, allow_pickle=True) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=fetch_data_type_from_schema(input_schema_path)) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + return json_data + + def pre_inference(data, input_schema_path): + """ + Preprocess data + + Parameters + ---------- + data: Data format as expected by the predict API of the core estimator. + input_schema_path: path of input schema. + + Returns + ------- + data: Data format after any processing. + + """ + return deserialize(data, input_schema_path) + + def post_inference(yhat): + """ + Post-process the model results + + Parameters + ---------- + yhat: Data format after calling model.predict. + + Returns + ------- + yhat: Data format after any processing. + + """ + return yhat.tolist() + + def predict(data, model=load_model(), input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns prediction given the model and data to predict + + Parameters + ---------- + model: Model instance returned by load_model API. + data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame. + input_schema_path: path of input schema. + + Returns + ------- + predictions: Output from scoring server + Format: {'prediction': output from model.predict method} + + """ + features = pre_inference(data, input_schema_path) + yhat = post_inference( + model.predict(features) + ) + return {'prediction': yhat} + +Save the score.py and now call verify to check if it works locally. + +.. code-block:: python3 + + model.verify(X_test.iloc[:2], auto_serialize_data=True) + +After verify run successfully, you can save the model to model catalog, deploy and call predict to invoke the endpoint. + +.. code-block:: python3 + + model_id = model.save(display_name='Demo AutoMLModel model') + deploy = model.deploy(display_name='Demo AutoMLModel deployment') + model.predict(X_test.iloc[:2].to_json()) diff --git a/docs/source/user_guide/model_serialization/genericmodel.rst b/docs/source/user_guide/model_serialization/genericmodel.rst index c0ae89fe6..88c80357a 100644 --- a/docs/source/user_guide/model_serialization/genericmodel.rst +++ b/docs/source/user_guide/model_serialization/genericmodel.rst @@ -139,12 +139,10 @@ By default, the ``GenericModel`` serializes to a pickle file. The following exam .. code-block:: python3 import tempfile - import ads from ads.model.generic_model import GenericModel from catboost import CatBoostRegressor - ads.set_auth(auth="resource_principal") # Initialize data @@ -182,7 +180,7 @@ By default, the ``GenericModel`` serializes to a pickle file. The following exam y_sample=y_train, ) - # Verify generated artifacts + # Verify generated artifacts. Payload looks like this: [[2, 4, 6, 8], [1, 4, 50, 60]] catboost_model.verify(X_test, auto_serialize_data=True) # Register CatBoostRegressor model From 976fd11f378296dc593585c7145b2153abdf05a9 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 14:55:43 -0800 Subject: [PATCH 096/147] ODSC-36246: fix the wording --- .../frameworks/genericmodel.rst | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst index c599d4bc4..f2c8fb170 100644 --- a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst @@ -172,28 +172,6 @@ Here is a more realistic example using CatBoost model. catboost_model.delete_deployment(wait_for_completion=True) ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) -You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. - -.. code-block:: python3 - - import tempfile - from ads.catalog.model import ModelCatalog - from ads.model.generic_model import GenericModel - - class Toy: - def predict(self, x): - return x ** 2 - estimator = Toy() - - model = GenericModel(estimator=estimator) - model.summary_status() - # If you are running the code inside a notebook session and using a service pack, `inference_conda_env` can be omitted. - model.prepare_save_deploy(inference_conda_env="dataexpl_p37_cpu_v3") - model.verify(2) - model.predict(2) - model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model.model_id) - Example -- Save Your Own Model ============================== @@ -244,11 +222,11 @@ The example is illustrated using an AutoMLx model. model = GenericModel(est, artifact_dir = "model_artifact_folder", serialize=False) model.prepare(inference_conda_env="automlx_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) -Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder and add implement the ``load_model`` function. You can also edit ``pre_inference`` and ``post_inference`` function. Below is an example implementation of the score.py. +Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder to add implementation of the ``load_model`` function. You can also add your preprocessing steps in ``pre_inference`` function and postprocessing steps in ``post_inference`` function. Below is an example implementation of the score.py. Replace your score.py with the code below. .. code-block:: python3 - :emphasize-lines: 28, 29, 30, 31, 122 + :emphasize-lines: 28, 29, 30, 31, 123 # score.py 1.0 generated by ADS 2.8.2 on 20230301_065458 import os @@ -396,7 +374,7 @@ Replace your score.py with the code below. ) return {'prediction': yhat} -Save the score.py and now call verify to check if it works locally. +Save the score.py and now call ``.verify()`` to check if it works locally. .. code-block:: python3 @@ -409,3 +387,25 @@ After verify run successfully, you can save the model to model catalog, deploy a model_id = model.save(display_name='Demo AutoMLModel model') deploy = model.deploy(display_name='Demo AutoMLModel deployment') model.predict(X_test.iloc[:2].to_json()) + +You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. + +.. code-block:: python3 + + import tempfile + from ads.catalog.model import ModelCatalog + from ads.model.generic_model import GenericModel + + class Toy: + def predict(self, x): + return x ** 2 + estimator = Toy() + + model = GenericModel(estimator=estimator) + model.summary_status() + # If you are running the code inside a notebook session and using a service pack, `inference_conda_env` can be omitted. + model.prepare_save_deploy(inference_conda_env="dataexpl_p37_cpu_v3") + model.verify(2) + model.predict(2) + model.delete_deployment(wait_for_completion=True) + ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model.model_id) From b45855350d0850b3f6276c532ff7e6b51f97b072 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 15:33:05 -0800 Subject: [PATCH 097/147] ODSC-38627: fixed based on comments --- .../frameworks/huggingfacemodel.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 77d089b4b..e55064bd2 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -103,7 +103,6 @@ Deploy and Generate Endpoint ... deployment_predict_log_id="ocid1.log.oc1.xxx.xxxxx", ... ) >>> print(f"Endpoint: {huggingface_pipeline_model.model_deployment.url}") - https://modeldeployment.{region}.oci.customer-oci.com/ocid1.datasciencemodeldeployment.oc1.xxx.xxxxx Run Prediction against Endpoint =============================== @@ -115,9 +114,7 @@ Run Prediction against Endpoint >>> import requests >>> import cloudpickle >>> image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" - >>> image = PIL.Image.open(requests.get(image_url, stream=True).raw) - >>> image_bytes = cloudpickle.dumps(image) >>> # Generate prediction by invoking the deployed endpoint >>> preds = huggingface_pipeline_model.predict(image)["prediction"] @@ -171,10 +168,10 @@ Model deployment endpoints can be invoked with the oci sdk. This example invokes >>> headers = {"Content-Type": "application/octet-stream"} >>> endpoint = huggingface_pipeline_model.model_deployment.url + "/predict" - ## download the image - image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" - image = PIL.Image.open(requests.get(image_link, stream=True).raw) - image_bytes = cloudpickle.dumps(image) + >>> ## download the image + >>> image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + >>> image = PIL.Image.open(requests.get(image_link, stream=True).raw) + >>> image_bytes = cloudpickle.dumps(image) >>> preds = requests.post(endpoint, data=image_bytes, auth=default_signer()['signer'], headers=headers).json() >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) @@ -200,7 +197,6 @@ Example ## download the image image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" image = PIL.Image.open(requests.get(image_link, stream=True).raw) - image_bytes = cloudpickle.dumps(image) ## download the pretrained model classifier = pipeline(model="openai/clip-vit-large-patch14") From 41dc901a0efed20ed6165a23b08c41ce1b08667a Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 15:59:44 -0800 Subject: [PATCH 098/147] adding release notes --- docs/source/release_notes.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index 74b351ebb..e371a55a4 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -2,6 +2,24 @@ Release Notes ============= +2.8.2 +----- +Release date: March 2, 2023 + +* Remove support for Python 3.7. +* Improved the DataScienceMode.create() to support timeout argument and auto extract region from the signer and signer config. +* Support Jupyter Notebook as ``entrypoint`` when defining Data Science jobs with ``PythonRuntime`` and ``GitPythonRuntime``. +* Support environment variable substitution in Data Science job names and output URI. +* Support JSON serialization of list/dictionary when assigning them as Data Science jobs environment variables. +* Support saving the notebook to output URI even if the job run failed when running a Data Science job using ``NotebookRuntime``. +* Added ``job.build()`` method to Data Science job to load default values from environment. +* Added ``DataScienceJob.fast_launch_shapes()`` method to list fast launch shapes available for Data Science job. +* Added :doc:`HuggingFacePipelineModel <./user_guide/model_registration/frameworks/huggingfacemodel>` class to support prepare, save, deploy and predict for HuggingFace pipelines. +* Updated Data Science job run YAML representation to include configurations inherited from the job. +* Fixed custom conda environment not showing in Data Science Job YAML specification. +* Fixed an issue where model saving was failing in notebook session without ipywidgets installed. +* Fixed "Unknown archive format" error in ads.jobs.PythonRuntime, when the source code folder name ends with "zip". List of supported archive files are: "zip", "tar.gz", "tar" and "tgz". + 2.8.1 ----- Release date: February 16, 2023 From 94e0e74f1d15e9e344736c06e4d07cd4f509d2ac Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 16:37:30 -0800 Subject: [PATCH 099/147] remove modelcatalog --- .../user_guide/model_deployment/predict.rst | 2 +- .../frameworks/genericmodel.rst | 2 +- .../model_serialization/automlmodel.rst | 3 +-- .../model_serialization/genericmodel.rst | 2 +- .../model_serialization/lightgbmmodel.rst | 2 +- .../model_serialization/pytorchmodel.rst | 2 +- .../model_serialization/quick_start.rst | 15 ++++++++------- .../model_serialization/sklearnmodel.rst | 2 +- .../model_serialization/tensorflowmodel.rst | 2 +- .../model_serialization/xgboostmodel.rst | 3 +-- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/source/user_guide/model_deployment/predict.rst b/docs/source/user_guide/model_deployment/predict.rst index 86a0ea64f..75c733381 100644 --- a/docs/source/user_guide/model_deployment/predict.rst +++ b/docs/source/user_guide/model_deployment/predict.rst @@ -93,7 +93,7 @@ Model framework serialization pytorch_model.predict(byte_im)['prediction'][0][:10] pytorch_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + pytorch_model.delete() The change needed in `score.py`: diff --git a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst index f2c8fb170..7fff87030 100644 --- a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst @@ -170,7 +170,7 @@ Here is a more realistic example using CatBoost model. catboost_model.deploy() catboost_model.predict(X_test) catboost_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + catboost_model.delete() # delete the model Example -- Save Your Own Model diff --git a/docs/source/user_guide/model_serialization/automlmodel.rst b/docs/source/user_guide/model_serialization/automlmodel.rst index ce49f2fc7..bed8b8c89 100644 --- a/docs/source/user_guide/model_serialization/automlmodel.rst +++ b/docs/source/user_guide/model_serialization/automlmodel.rst @@ -179,5 +179,4 @@ Example deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') automl_model.predict(test.X.iloc[:10]) automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) - + automl_model.delete() diff --git a/docs/source/user_guide/model_serialization/genericmodel.rst b/docs/source/user_guide/model_serialization/genericmodel.rst index 88c80357a..038260802 100644 --- a/docs/source/user_guide/model_serialization/genericmodel.rst +++ b/docs/source/user_guide/model_serialization/genericmodel.rst @@ -188,7 +188,7 @@ By default, the ``GenericModel`` serializes to a pickle file. The following exam catboost_model.deploy() catboost_model.predict(X_test) catboost_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + catboost_model.delete() # delete the model You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. diff --git a/docs/source/user_guide/model_serialization/lightgbmmodel.rst b/docs/source/user_guide/model_serialization/lightgbmmodel.rst index 5ed77cf57..1f6b4722c 100644 --- a/docs/source/user_guide/model_serialization/lightgbmmodel.rst +++ b/docs/source/user_guide/model_serialization/lightgbmmodel.rst @@ -283,5 +283,5 @@ Example lightgbm_model.deploy() lightgbm_model.predict(X_test_transformed[:10])['prediction'] lightgbm_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + lightgbm_model.delete() diff --git a/docs/source/user_guide/model_serialization/pytorchmodel.rst b/docs/source/user_guide/model_serialization/pytorchmodel.rst index 29b282ad8..0b2548ec8 100644 --- a/docs/source/user_guide/model_serialization/pytorchmodel.rst +++ b/docs/source/user_guide/model_serialization/pytorchmodel.rst @@ -195,5 +195,5 @@ Example pytorch_model.deploy() pytorch_model.predict(test_data) pytorch_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + pytorch_model.delete() diff --git a/docs/source/user_guide/model_serialization/quick_start.rst b/docs/source/user_guide/model_serialization/quick_start.rst index 9fa8c7d9f..f59febf60 100644 --- a/docs/source/user_guide/model_serialization/quick_start.rst +++ b/docs/source/user_guide/model_serialization/quick_start.rst @@ -46,7 +46,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, deploy = automl_model.deploy(display_name='Demo AutoMLModel deployment') automl_model.predict(test.X.iloc[:10]) automl_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + automl_model.delete() GenericModel ------------ @@ -72,7 +72,8 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model.deploy() model.predict(2) model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + model.delete() + LightGBMModel @@ -105,7 +106,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model_deployment = lightgbm_model.deploy() lightgbm_model.predict(X_test) lightgbm_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + lightgbm_model.delete() PyTorchModel @@ -148,7 +149,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model_deployment = torch_model.deploy() torch_model.predict(test_data) torch_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + torch_model.delete() SklearnModel @@ -178,7 +179,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model_deployment = sklearn_model.deploy() sklearn_model.predict(X_test) sklearn_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + sklearn_model.delete() TensorFlowModel @@ -216,7 +217,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model_deployment = tf_model.deploy() tf_model.predict(x_test[:1]) tf_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + tf_model.delete() XGBoostModel @@ -247,7 +248,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model_deployment = xgboost_model.deploy() xgboost_model.predict(X_test) xgboost_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + xgboost_model.delete() Shortcut ======== diff --git a/docs/source/user_guide/model_serialization/sklearnmodel.rst b/docs/source/user_guide/model_serialization/sklearnmodel.rst index 5e4b47fb0..819f2af3f 100644 --- a/docs/source/user_guide/model_serialization/sklearnmodel.rst +++ b/docs/source/user_guide/model_serialization/sklearnmodel.rst @@ -250,5 +250,5 @@ Examples sklearn_model.deploy() sklearn_model.predict(X_test.head(2)) sklearn_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + sklearn_model.delete() diff --git a/docs/source/user_guide/model_serialization/tensorflowmodel.rst b/docs/source/user_guide/model_serialization/tensorflowmodel.rst index 2739220b0..7302d86ae 100644 --- a/docs/source/user_guide/model_serialization/tensorflowmodel.rst +++ b/docs/source/user_guide/model_serialization/tensorflowmodel.rst @@ -184,5 +184,5 @@ Example tensorflow_model_deployment = model.deploy() tensorflow_model.predict(x_test[:1]) tensorflow_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) + tensorflow_model.delete() diff --git a/docs/source/user_guide/model_serialization/xgboostmodel.rst b/docs/source/user_guide/model_serialization/xgboostmodel.rst index 573c63940..d9cf4dd46 100644 --- a/docs/source/user_guide/model_serialization/xgboostmodel.rst +++ b/docs/source/user_guide/model_serialization/xgboostmodel.rst @@ -285,5 +285,4 @@ Example xgboost_model.deploy() xgboost_model.predict(X_test_transformed[:10])['prediction'] xgboost_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) - + xgboost_model.delete() From b6fee53f73877f4c5944c7165e3075fb454f32eb Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 1 Mar 2023 20:11:13 -0500 Subject: [PATCH 100/147] Update ads.jobs.rst, data_science_job.rst, and 2 more files... --- docs/source/ads.jobs.rst | 1 + docs/source/user_guide/jobs/data_science_job.rst | 5 +++-- docs/source/user_guide/jobs/overview.rst | 2 +- docs/source/user_guide/jobs/run_script.rst | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/source/ads.jobs.rst b/docs/source/ads.jobs.rst index 2e7daa34e..85bc19221 100644 --- a/docs/source/ads.jobs.rst +++ b/docs/source/ads.jobs.rst @@ -69,3 +69,4 @@ Module contents :members: :undoc-members: :show-inheritance: + :inherited-members: diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index a30b38504..7af32013b 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -36,13 +36,14 @@ You can also :doc:`run_notebook`, :doc:`run_script` and :doc:`run_git`. YAML ==== -A job can be defined using YAML, as shown in the "YAML" tab. +A job can be defined using YAML, as shown in the "YAML" tab in the example above. Here are some examples to load/save the YAML job configurations: .. code-block:: python # Load a job from a YAML file job = Job.from_yaml(uri="oci://bucket_name@namespace/path/to/job.yaml") + # Save a job to a YAML file job.to_yaml(uri="oci://bucket_name@namespace/path/to/job.yaml") @@ -134,7 +135,7 @@ To get a list of existing jobs in a specific compartment: from ads.jobs import Job - # Load a job + # Get a list of jobs in a specific compartment. jobs = Job.datascience_job("") With a ``Job`` object, you can get a list of job runs: diff --git a/docs/source/user_guide/jobs/overview.rst b/docs/source/user_guide/jobs/overview.rst index fa209af8c..657921932 100644 --- a/docs/source/user_guide/jobs/overview.rst +++ b/docs/source/user_guide/jobs/overview.rst @@ -28,4 +28,4 @@ Each model can write its results to the Logging service or Object Storage. Then you can run a final sequential job that uses the best model class, and trains the final model on the entire dataset. The following sections provides details on running workloads with OCI Data Science Jobs using ADS Jobs APIs. -You can use similar APIs to `Run a OCI DataFlow Application `_. +You can use similar APIs to :doc:`Run a OCI DataFlow Application <../apachespark/quickstart>`. diff --git a/docs/source/user_guide/jobs/run_script.rst b/docs/source/user_guide/jobs/run_script.rst index 580ad3d5e..2f63d9b0b 100644 --- a/docs/source/user_guide/jobs/run_script.rst +++ b/docs/source/user_guide/jobs/run_script.rst @@ -4,7 +4,7 @@ Run a Script This section shows how to create a job to run a script. The :py:class:`~ads.jobs.ScriptRuntime` is designed for you to define job artifacts and configurations supported by OCI -Data Science jobs natively. It can be used with any script types that is supported by the OCI Data Science jobs, +Data Science Jobs natively. It can be used with any script types that is supported by the OCI Data Science Jobs, including shell scripts and python scripts. The source code can be a single script, files in a folder or a zip/tar file. From 555516c9dfb14b7cbb01e3bf8c4ad37e31dc1175 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 17:24:30 -0800 Subject: [PATCH 101/147] switch to sklearn --- .../frameworks/genericmodel.rst | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst index 7fff87030..21e2c20f2 100644 --- a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst @@ -181,46 +181,31 @@ The example is illustrated using an AutoMLx model. .. code-block:: python3 - import automl - import ads - from automl import init - from sklearn.datasets import fetch_openml - from sklearn.model_selection import train_test_split + import tempfile + from ads import set_auth from ads.model import GenericModel + from sklearn.datasets import load_iris + from sklearn.linear_model import LogisticRegression + from sklearn.model_selection import train_test_split - dataset = fetch_openml(name='adult', as_frame=True) - df, y = dataset.data, dataset.target - - # Several of the columns are incorrectly labeled as category type in the original dataset - numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] - for col in df.columns: - if col in numeric_columns: - df[col] = df[col].astype(int) - - - X_train, X_test, y_train, y_test = train_test_split(df, - y.map({'>50K': 1, '<=50K': 0}).astype(int), - train_size=0.7, - random_state=0) - - X_train.shape, X_test.shape - - # create a AutoMLx model - init(engine='local') + set_auth(auth="resource_principal") - est = automl.Pipeline(task='classification') - est.fit(X_train, y_train) + # Load dataset and Prepare train and test split + iris = load_iris() + X, y = iris.data, iris.target + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25) - # Authentication - ads.set_auth(auth="resource_principal") + # Train a LogisticRegression model + sklearn_estimator = LogisticRegression() + sklearn_estimator.fit(X_train, y_train) # Serialize your model. You can choose your own way to serialize your model. import cloudpickle with open("./model.pkl", "wb") as f: - cloudpickle.dump(est, f) + cloudpickle.dump(sklearn_estimator, f) - model = GenericModel(est, artifact_dir = "model_artifact_folder", serialize=False) - model.prepare(inference_conda_env="automlx_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) + model = GenericModel(sklearn_estimator, artifact_dir = "model_artifact_folder", serialize=False) + model.prepare(inference_conda_env="generalml_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder to add implementation of the ``load_model`` function. You can also add your preprocessing steps in ``pre_inference`` function and postprocessing steps in ``post_inference`` function. Below is an example implementation of the score.py. Replace your score.py with the code below. @@ -378,7 +363,7 @@ Save the score.py and now call ``.verify()`` to check if it works locally. .. code-block:: python3 - model.verify(X_test.iloc[:2], auto_serialize_data=True) + model.verify(X_test[:2], auto_serialize_data=True) After verify run successfully, you can save the model to model catalog, deploy and call predict to invoke the endpoint. @@ -386,7 +371,7 @@ After verify run successfully, you can save the model to model catalog, deploy a model_id = model.save(display_name='Demo AutoMLModel model') deploy = model.deploy(display_name='Demo AutoMLModel deployment') - model.predict(X_test.iloc[:2].to_json()) + model.predict(X_test[:2].tolist()) You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. From 207be673386dee53bb8d439b0e88e6b2bdb4a10d Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 1 Mar 2023 20:28:26 -0500 Subject: [PATCH 102/147] Update data_science_job.rst --- docs/source/user_guide/jobs/data_science_job.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/user_guide/jobs/data_science_job.rst b/docs/source/user_guide/jobs/data_science_job.rst index 7af32013b..6cf1d2c62 100644 --- a/docs/source/user_guide/jobs/data_science_job.rst +++ b/docs/source/user_guide/jobs/data_science_job.rst @@ -20,6 +20,9 @@ The runtime can be an instance of: Here is an example to define and run a Python :py:class:`~ads.jobs.Job`. +Note that a job can be defined either using Python APIs or YAML. +See the next section for how to load and save the job with YAML. + .. include:: ../jobs/tabs/quick_start_job.rst The :py:class:`~ads.jobs.PythonRuntime` is designed for :doc:`Running a Python Workload `. From 8b5d45afc9d84aa5dfce84ebdb2c75ad46cedc3f Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 21:43:51 -0800 Subject: [PATCH 103/147] resolve comments --- .../model_registration/frameworks/automlmodel.rst | 3 +-- docs/source/user_guide/model_training/automl/index.rst | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index b53d4c28f..d8159226c 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -5,8 +5,7 @@ AutoMLModel .. note:: -The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See this :ref:`link ` for more detailed information. - +Working with AutoML has moved from within ADS to working directly with the automlx library. To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production with a few lines of code. diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index e65ed50dc..e85566d57 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -3,12 +3,14 @@ Oracle AutoMLx ============== .. note:: - The ADS `AutoML `__, `AutoMLProvider `__, `MLXGlobalExplainer `__, `MLXLocalExplainer `__ and `MLXWhatIfExplainer `__, `AutoMLModel `__, `AutoMLExtractor `__ are deprecated in 2.8.2 and will be removed in 3.0.0. This is to in favor of working directly with Oracle AutoMLx library. + Working with AutoML has moved from within ADS to working directly with the AutoMLx library. With AutoMLx moving into the open source domain, working directly with the library has many benefits. One of these is that ADS and AutoMLx can now have independent version cadences. AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. To offer this functionality, the best way is for the ADS to work together as separate libraries. - The AutoMlx library can be found in `automlx_p38_cpu_v#` conda pack. The documentation can be found at this `link `__. Notebook examples can be found at this `link `__. + AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at `this link `__. + Notebook examples are in `Oracle's samples repository `__. + .. toctree:: :hidden: From 5b83d9a3b16a3b06bfd7178da5b1216d1cd18101 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 22:04:08 -0800 Subject: [PATCH 104/147] ODSC-39261: resolving the comments --- docs/source/user_guide/model_training/automl/index.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index e85566d57..32e8aa9c3 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -4,12 +4,10 @@ Oracle AutoMLx .. note:: Working with AutoML has moved from within ADS to working directly with the AutoMLx library. - With AutoMLx moving into the open source domain, working directly with the library has many benefits. - One of these is that ADS and AutoMLx can now have independent version cadences. AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. To offer this functionality, the best way is for the ADS to work together as separate libraries. - AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at `this link `__. - Notebook examples are in `Oracle's samples repository `__. + AutoMLx are preinstalled in conda pack automlx_p38_cpu_v#, and can now be updated independently of ADS. AutoMLx documentation may be found at `this link `__. + Notebook examples are in `Oracle's samples repository `__ and a migration tutorial can be found at :doc:`../../../user_guide/model_training/automl/quick_start` section. .. toctree:: From fe926f5d381724e9bad39fbe643ef23660658db3 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 22:07:48 -0800 Subject: [PATCH 105/147] resolve comments --- .../user_guide/model_registration/frameworks/automlmodel.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index d8159226c..69adba906 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -238,11 +238,15 @@ Open ``automl_model_artifact/score.py`` and edit the code to instantiate the mod Verify score.py changes by running inference locally. + + .. code-block:: python3 automl_model.verify(X_test.iloc[:2], auto_serialize_data=True) Save model and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. + + .. code-block:: python3 model_id = automl_model.save(display_name='Demo AutoMLModel model') From cfbdbc287e590c352b1cab8051896392ad5dcb6e Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 1 Mar 2023 22:09:19 -0800 Subject: [PATCH 106/147] resolve comments --- .../user_guide/model_registration/frameworks/automlmodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index 69adba906..104bd8ba9 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -8,7 +8,7 @@ AutoMLModel Working with AutoML has moved from within ADS to working directly with the automlx library. To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. -The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production with a few lines of code. +The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production. Example From 87c86d6ed789202589ef274d7c6b29c947d7ccbe Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 2 Mar 2023 10:12:45 -0500 Subject: [PATCH 107/147] Fix broken link in policies.rst --- docs/source/user_guide/jobs/policies.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/jobs/policies.rst b/docs/source/user_guide/jobs/policies.rst index 5f6a9d9d7..638dd6516 100644 --- a/docs/source/user_guide/jobs/policies.rst +++ b/docs/source/user_guide/jobs/policies.rst @@ -54,8 +54,8 @@ The following policy is needed for running a container job: See also: * `Learn Best Practices for Setting Up Your Tenancy `_ -* `IAM with Identity Domains _` -* `IAM without Identity Domains _` +* `IAM with Identity Domains `_ +* `IAM without Identity Domains `_ * `Dynamic Group `_ * `Data Science Policies `_ * `Object Storage `_ From a26f2bb049c8de4de7e6203498b314eb3254d48e Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Thu, 2 Mar 2023 13:09:48 -0500 Subject: [PATCH 108/147] Update ads.jobs.rst --- docs/source/ads.jobs.rst | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/source/ads.jobs.rst b/docs/source/ads.jobs.rst index 85bc19221..c99a0c55d 100644 --- a/docs/source/ads.jobs.rst +++ b/docs/source/ads.jobs.rst @@ -1,11 +1,16 @@ ads.jobs package ================ +.. toctree:: + :maxdepth: 3 + + ads.jobs + Subpackages ----------- .. toctree:: - :maxdepth: 4 + :maxdepth: 1 ads.jobs.builders ads.jobs.schema @@ -15,52 +20,58 @@ Submodules ---------- ads.jobs.ads\_job module ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ads.jobs.ads_job :members: :undoc-members: :show-inheritance: + :inherited-members: ads.jobs.cli module -------------------- +~~~~~~~~~~~~~~~~~~~ .. automodule:: ads.jobs.cli :members: :undoc-members: :show-inheritance: + :inherited-members: ads.jobs.env\_var\_parser module --------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ads.jobs.env_var_parser :members: :undoc-members: :show-inheritance: + :inherited-members: ads.jobs.extension module -------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ads.jobs.extension :members: :undoc-members: :show-inheritance: + :inherited-members: ads.jobs.serializer module --------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ads.jobs.serializer :members: :undoc-members: :show-inheritance: + :inherited-members: ads.jobs.utils module ---------------------- +~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ads.jobs.utils :members: :undoc-members: :show-inheritance: + :inherited-members: Module contents --------------- From 05506e0d0d6daf76f1a6c6bb7b5e8995726d87c2 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 2 Mar 2023 10:32:01 -0800 Subject: [PATCH 109/147] fix the doc --- .../model_registration/frameworks/huggingfacemodel.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index e55064bd2..11be1d542 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -118,7 +118,7 @@ Run Prediction against Endpoint >>> # Generate prediction by invoking the deployed endpoint >>> preds = huggingface_pipeline_model.predict(image)["prediction"] - >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds['prediction']]) + >>> print([{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds]) [{'score': 0.9879, 'label': 'LABEL_184'}, {'score': 0.9973, 'label': 'snow'}, {'score': 0.9972, 'label': 'cat'}] @@ -196,7 +196,7 @@ Example ## download the image image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" - image = PIL.Image.open(requests.get(image_link, stream=True).raw) + image = PIL.Image.open(requests.get(image_url, stream=True).raw) ## download the pretrained model classifier = pipeline(model="openai/clip-vit-large-patch14") From 3b9910db18ac494a99d085d1597ec449706fe92d Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 2 Mar 2023 10:38:25 -0800 Subject: [PATCH 110/147] fix the code --- .../model_registration/frameworks/huggingfacemodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 11be1d542..942a8fa58 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -206,7 +206,7 @@ Example ) ## Initiate a HuggingFacePipelineModel instance - zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) + zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, serialized model, runtime.yaml conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" From da4821b22714cdaa410c48177c8fc1395e65b197 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 2 Mar 2023 14:41:32 -0800 Subject: [PATCH 111/147] update the notes --- .../model_registration/frameworks/huggingfacemodel.rst | 2 +- docs/source/user_guide/model_training/automl/index.rst | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 942a8fa58..3fc6449f7 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -232,7 +232,7 @@ Example deployment_log_group_id = log_group_id, deployment_access_log_id = log_id, deployment_predict_log_id = log_id) - zero_shot_image_classification_model.predict(image) + zero_shot_image_classification_model.predict(data) zero_shot_image_classification_model.predict(body) ### Invoke the model by sending bytes diff --git a/docs/source/user_guide/model_training/automl/index.rst b/docs/source/user_guide/model_training/automl/index.rst index 32e8aa9c3..aa5f97d0d 100644 --- a/docs/source/user_guide/model_training/automl/index.rst +++ b/docs/source/user_guide/model_training/automl/index.rst @@ -3,12 +3,10 @@ Oracle AutoMLx ============== .. note:: - Working with AutoML has moved from within ADS to working directly with the AutoMLx library. - AutoMLx is under active development, and will be expanding into areas of explainability, fairness and deep learning. - To offer this functionality, the best way is for the ADS to work together as separate libraries. - AutoMLx are preinstalled in conda pack automlx_p38_cpu_v#, and can now be updated independently of ADS. AutoMLx documentation may be found at `this link `__. - Notebook examples are in `Oracle's samples repository `__ and a migration tutorial can be found at :doc:`../../../user_guide/model_training/automl/quick_start` section. - + Building Oracle AutoMLx models starting with oracle-ads 2.8.2, is now performed directly with the AutoMLx library. + AutoMLx documentation can be found `here `__. + Oracle AutoMLx is installed in the conda pack `automlx_p38_cpu_v#`. + Notebook examples are `here `__ and a migration tutorial can be found in the :doc:`../../../user_guide/model_training/automl/quick_start` section. .. toctree:: :hidden: From cbf43e33fdef1f40f6ab1f7c1669788ba7364ad4 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 2 Mar 2023 16:05:48 -0800 Subject: [PATCH 112/147] update --- docs/source/ads.model.extractor.rst | 8 -- .../frameworks/automlmodel.rst | 4 +- .../frameworks/genericmodel.rst | 10 +- .../_template/summary_status.rst | 2 +- .../model_serialization/automlmodel.rst | 4 +- .../model_serialization/genericmodel.rst | 109 ++++++++---------- .../user_guide/model_training/index.rst | 2 +- 7 files changed, 58 insertions(+), 81 deletions(-) diff --git a/docs/source/ads.model.extractor.rst b/docs/source/ads.model.extractor.rst index e21802b46..f7f430201 100644 --- a/docs/source/ads.model.extractor.rst +++ b/docs/source/ads.model.extractor.rst @@ -4,14 +4,6 @@ ads.model.extractor package Submodules ---------- -ads.model.extractor.automl\_extractor module --------------------------------------------- - -.. automodule:: ads.model.extractor.automl_extractor - :members: - :undoc-members: - :show-inheritance: - ads.model.extractor.keras\_extractor module ------------------------------------------- diff --git a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst index 104bd8ba9..9ece1520c 100644 --- a/docs/source/user_guide/model_registration/frameworks/automlmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/automlmodel.rst @@ -8,7 +8,7 @@ AutoMLModel Working with AutoML has moved from within ADS to working directly with the automlx library. To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. -The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production. +The following example takes your trained ``AutoML`` model using ``GenericModel`` and deploys it into production. Example @@ -244,7 +244,7 @@ Verify score.py changes by running inference locally. automl_model.verify(X_test.iloc[:2], auto_serialize_data=True) -Save model and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. +Save the model, and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. .. code-block:: python3 diff --git a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst index 21e2c20f2..724671a60 100644 --- a/docs/source/user_guide/model_registration/frameworks/genericmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/genericmodel.rst @@ -11,7 +11,7 @@ Overview The ``ads.model.generic_model.GenericModel`` class in ADS provides an efficient way to serialize almost any model class. This section demonstrates how to use the ``GenericModel`` class to prepare model artifacts, verify models, save models to the model catalog, deploy models, and perform predictions on model deployment endpoints. -The ``GenericModel`` class works with any unsupported model framework that has a ``.predict()`` method. For the most common model classes such as scikit-learn, XGBoost, LightGBM, TensorFlow, and PyTorch, and AutoML, we recommend that you use the ADS provided, framework-specific serializations models. For example, for a scikit-learn model, use SKLearnmodel. For other models, use the ``GenericModel`` class. +The ``GenericModel`` class works with any unsupported model framework that has a ``.predict()`` method. For the most common model classes such as scikit-learn, XGBoost, LightGBM, TensorFlow, and PyTorch, we recommend that you use the ADS provided, framework-specific serializations models. For example, for a scikit-learn model, use SKLearnmodel. For other models, use the ``GenericModel`` class. .. include:: ../_template/overview.rst @@ -177,7 +177,7 @@ Example -- Save Your Own Model ============================== By default, the ``serialize`` in ``GenericModel`` class is True, and it will serialize the model using cloudpickle. However, you can set ``serialize=False`` to disable it. And serialize the model on your own. You just need to copy the serialized model into the ``.artifact_dir``. This example shows step by step how you can do that. -The example is illustrated using an AutoMLx model. +The example is illustrated using a Sklearn model. .. code-block:: python3 @@ -369,8 +369,8 @@ After verify run successfully, you can save the model to model catalog, deploy a .. code-block:: python3 - model_id = model.save(display_name='Demo AutoMLModel model') - deploy = model.deploy(display_name='Demo AutoMLModel deployment') + model_id = model.save(display_name='Demo Sklearn model') + deploy = model.deploy(display_name='Demo Sklearn deployment') model.predict(X_test[:2].tolist()) You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. @@ -393,4 +393,4 @@ You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.p model.verify(2) model.predict(2) model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model.model_id) + model.delete() diff --git a/docs/source/user_guide/model_serialization/_template/summary_status.rst b/docs/source/user_guide/model_serialization/_template/summary_status.rst index 251e3288c..feebc9577 100644 --- a/docs/source/user_guide/model_serialization/_template/summary_status.rst +++ b/docs/source/user_guide/model_serialization/_template/summary_status.rst @@ -1,4 +1,4 @@ -You can call the ``.summary_status()`` method after a model serialization instance such as ``AutoMLModel``, ``GenericModel``, ``SklearnModel``, ``TensorFlowModel``, or ``PyTorchModel`` is created. The ``.summary_status()`` method returns a Pandas dataframe that guides you through the entire workflow. It shows which methods are available to call and which ones aren't. Plus it outlines what each method does. If extra actions are required, it also shows those actions. +You can call the ``.summary_status()`` method after a model serialization instance such as ``GenericModel``, ``SklearnModel``, ``TensorFlowModel``, or ``PyTorchModel`` is created. The ``.summary_status()`` method returns a Pandas dataframe that guides you through the entire workflow. It shows which methods are available to call and which ones aren't. Plus it outlines what each method does. If extra actions are required, it also shows those actions. The following image displays an example summary status table created after a user initiates a model instance. The table's Step column displays a Status of Done for the initiate step. And the ``Details`` column explains what the initiate step did such as generating a ``score.py`` file. The Step column also displays the ``prepare()``, ``verify()``, ``save()``, ``deploy()``, and ``predict()`` methods for the model. The Status column displays which method is available next. After the initiate step, the ``prepare()`` method is available. The next step is to call the ``prepare()`` method. diff --git a/docs/source/user_guide/model_serialization/automlmodel.rst b/docs/source/user_guide/model_serialization/automlmodel.rst index 52efa7a5c..76069fcd6 100644 --- a/docs/source/user_guide/model_serialization/automlmodel.rst +++ b/docs/source/user_guide/model_serialization/automlmodel.rst @@ -9,7 +9,7 @@ The ``ads.model.framework.automl_model.AutoMLModel`` class is deprecated. See th To deploy an AutoMlx model, use `GenericModel <../../../ads.model.html#ads.model.generic_model.GenericModel>`__ class. -The following example take your trained ``AutoML`` model using ``GenericModel`` and deploy it into production with a few lines of code. +The following example takes your trained ``AutoML`` model using ``GenericModel`` and deploys it into production with a few lines of code. Example @@ -245,7 +245,7 @@ Verify score.py changes by running inference locally automl_model.verify(X_test.iloc[:2]) -Save model and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. +Save model, and Deploy the model. After it is successfully deployed, invoke the endpoint by calling .predict() function. .. code-block:: python3 model_id = automl_model.save(display_name='Demo AutoMLModel model') diff --git a/docs/source/user_guide/model_serialization/genericmodel.rst b/docs/source/user_guide/model_serialization/genericmodel.rst index 038260802..ef853c3da 100644 --- a/docs/source/user_guide/model_serialization/genericmodel.rst +++ b/docs/source/user_guide/model_serialization/genericmodel.rst @@ -8,7 +8,7 @@ Overview The ``GenericModel`` class in ADS provides an efficient way to serialize almost any model class. This section demonstrates how to use the ``GenericModel`` class to prepare model artifacts, verify models, save models to the model catalog, deploy models, and perform predictions on model deployment endpoints. -The ``GenericModel`` class works with any unsupported model framework that has a ``.predict()`` method. For the most common model classes such as scikit-learn, XGBoost, LightGBM, TensorFlow, and PyTorch, and AutoML, we recommend that you use the ADS provided, framework-specific serializations models. For example, for a scikit-learn model, use SKLearnmodel. For other models, use the ``GenericModel`` class. +The ``GenericModel`` class works with any unsupported model framework that has a ``.predict()`` method. For the most common model classes such as scikit-learn, XGBoost, LightGBM, TensorFlow, and PyTorch, we recommend that you use the ADS provided, framework-specific serializations models. For example, for a scikit-learn model, use SKLearnmodel. For other models, use the ``GenericModel`` class. .. include:: _template/overview.rst @@ -190,83 +190,46 @@ By default, the ``GenericModel`` serializes to a pickle file. The following exam catboost_model.delete_deployment(wait_for_completion=True) catboost_model.delete() # delete the model -You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. - -.. code-block:: python3 - - import tempfile - from ads.catalog.model import ModelCatalog - from ads.model.generic_model import GenericModel - - class Toy: - def predict(self, x): - return x ** 2 - estimator = Toy() - - model = GenericModel(estimator=estimator) - model.summary_status() - # If you are running the code inside a notebook session and using a service pack, `inference_conda_env` can be omitted. - model.prepare_save_deploy(inference_conda_env="dataexpl_p37_cpu_v3") - model.verify(2) - model.predict(2) - model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model.model_id) - Example -- Save Your Own Model ============================== By default, the ``serialize`` in ``GenericModel`` class is True, and it will serialize the model using cloudpickle. However, you can set ``serialize=False`` to disable it. And serialize the model on your own. You just need to copy the serialized model into the ``.artifact_dir``. This example shows step by step how you can do that. -The example is illustrated using an AutoMLx model. +The example is illustrated using a Sklearn model. .. code-block:: python3 - import automl - import ads - from automl import init - from sklearn.datasets import fetch_openml - from sklearn.model_selection import train_test_split + import tempfile + from ads import set_auth from ads.model import GenericModel + from sklearn.datasets import load_iris + from sklearn.linear_model import LogisticRegression + from sklearn.model_selection import train_test_split - dataset = fetch_openml(name='adult', as_frame=True) - df, y = dataset.data, dataset.target - - # Several of the columns are incorrectly labeled as category type in the original dataset - numeric_columns = ['age', 'capitalgain', 'capitalloss', 'hoursperweek'] - for col in df.columns: - if col in numeric_columns: - df[col] = df[col].astype(int) - - - X_train, X_test, y_train, y_test = train_test_split(df, - y.map({'>50K': 1, '<=50K': 0}).astype(int), - train_size=0.7, - random_state=0) - - X_train.shape, X_test.shape - - # create a AutoMLx model - init(engine='local') + set_auth(auth="resource_principal") - est = automl.Pipeline(task='classification') - est.fit(X_train, y_train) + # Load dataset and Prepare train and test split + iris = load_iris() + X, y = iris.data, iris.target + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25) - # Authentication - ads.set_auth(auth="resource_principal") + # Train a LogisticRegression model + sklearn_estimator = LogisticRegression() + sklearn_estimator.fit(X_train, y_train) # Serialize your model. You can choose your own way to serialize your model. import cloudpickle with open("./model.pkl", "wb") as f: - cloudpickle.dump(est, f) + cloudpickle.dump(sklearn_estimator, f) - model = GenericModel(est, artifact_dir = "model_artifact_folder", serialize=False) - model.prepare(inference_conda_env="automlx_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) + model = GenericModel(sklearn_estimator, artifact_dir = "model_artifact_folder", serialize=False) + model.prepare(inference_conda_env="generalml_p38_cpu_v1",force_overwrite=True, model_file_name="model.pkl", X_sample=X_test) -Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder and add implement the ``load_model`` function. You can also edit ``pre_inference`` and ``post_inference`` function. Below is an example implementation of the score.py. +Now copy the model.pkl file and paste into the ``model_artifact_folder`` folder. And open the score.py in the ``model_artifact_folder`` folder to add implementation of the ``load_model`` function. You can also add your preprocessing steps in ``pre_inference`` function and postprocessing steps in ``post_inference`` function. Below is an example implementation of the score.py. Replace your score.py with the code below. .. code-block:: python3 - :emphasize-lines: 28, 29, 30, 31, 122 + :emphasize-lines: 28, 29, 30, 31, 123 # score.py 1.0 generated by ADS 2.8.2 on 20230301_065458 import os @@ -414,16 +377,38 @@ Replace your score.py with the code below. ) return {'prediction': yhat} -Save the score.py and now call verify to check if it works locally. +Save the score.py and now call ``.verify()`` to check if it works locally. .. code-block:: python3 - model.verify(X_test.iloc[:2], auto_serialize_data=True) + model.verify(X_test[:2], auto_serialize_data=True) After verify run successfully, you can save the model to model catalog, deploy and call predict to invoke the endpoint. .. code-block:: python3 - model_id = model.save(display_name='Demo AutoMLModel model') - deploy = model.deploy(display_name='Demo AutoMLModel deployment') - model.predict(X_test.iloc[:2].to_json()) + model_id = model.save(display_name='Demo Sklearn model') + deploy = model.deploy(display_name='Demo Sklearn deployment') + model.predict(X_test[:2].tolist()) + +You can also use the shortcut ``.prepare_save_deploy()`` instead of calling ``.prepare()``, ``.save()`` and ``.deploy()`` seperately. + +.. code-block:: python3 + + import tempfile + from ads.catalog.model import ModelCatalog + from ads.model.generic_model import GenericModel + + class Toy: + def predict(self, x): + return x ** 2 + estimator = Toy() + + model = GenericModel(estimator=estimator) + model.summary_status() + # If you are running the code inside a notebook session and using a service pack, `inference_conda_env` can be omitted. + model.prepare_save_deploy(inference_conda_env="dataexpl_p37_cpu_v3") + model.verify(2) + model.predict(2) + model.delete_deployment(wait_for_completion=True) + model.delete() \ No newline at end of file diff --git a/docs/source/user_guide/model_training/index.rst b/docs/source/user_guide/model_training/index.rst index 796bf0a56..70ae59165 100644 --- a/docs/source/user_guide/model_training/index.rst +++ b/docs/source/user_guide/model_training/index.rst @@ -3,7 +3,7 @@ Train Models ============ In this section you will learn about model training on the Data Science cloud service using a variety of popular frameworks. This section -covers the popular ``sklearn`` framework, along with gradient boosted tree estimators like LightGBM and XGBoost, Oracle AutoML and +covers the popular ``sklearn`` framework, along with gradient boosted tree estimators like LightGBM and XGBoost, and deep learning packages likes TensorFlow and PyTorch. The section covers how to serialize models and make use of the OCI Model Catalog to store model artifacts and meta data all using From baa96cd18d04738ea318db1068c64da3a41e3502 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 2 Mar 2023 17:43:01 -0800 Subject: [PATCH 113/147] adding conda --- .../model_registration/frameworks/huggingfacemodel.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 3fc6449f7..36296257a 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -39,6 +39,16 @@ Load a `ImageSegmentationPipeline }] +Prepare a Custom Conda Pack +=========================== + +You can start with the PyTorch conda pack with slug ``pytorch110_p38_cpu_v1``. + +#. Run ``pip install timm`` since image segmentation model requires ``timm``. +#. Then use ``odsc conda init -b your_bucket_name -n bucket_namespace`` to config where to store the published conda pack if you have not done this yet. +#. Lastly, run ``odsc conda publish -s pytorch110_p38_cpu_v1``. +#. Once it's done, you can find the path of the pusblished conda pack under the Environment Explorer ``Published`` tab. Refresh the page if you cannot find it. + Prepare Model Artifact ====================== From 496349d51f14bbb84c745f0689e602752e4b6d06 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Thu, 2 Mar 2023 17:46:00 -0800 Subject: [PATCH 114/147] adding conda --- .../model_registration/frameworks/huggingfacemodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index 36296257a..a68bf515e 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -42,7 +42,7 @@ Load a `ImageSegmentationPipeline Date: Thu, 2 Mar 2023 18:19:29 -0800 Subject: [PATCH 115/147] update the ads folder and setup.py --- ads/ads_version.json | 2 +- ads/automl/driver.py | 17 +- ads/automl/provider.py | 23 +- ads/catalog/model.py | 14 +- ads/common/auth.py | 31 +- ads/common/decorator/deprecate.py | 30 +- ads/common/decorator/runtime_dependency.py | 3 +- ads/common/model_artifact.py | 12 +- ads/common/oci_mixin.py | 64 +- ads/common/serializer.py | 9 +- ads/common/utils.py | 89 +- ads/dataset/progress.py | 64 +- ads/evaluations/__init__.py | 102 +- ads/evaluations/evaluation_plot.py | 25 +- ads/evaluations/evaluator.py | 607 ++++++- ads/evaluations/statistical_metrics.py | 118 +- ads/explanations/__init__.py | 11 +- ads/explanations/base_explainer.py | 8 +- ads/explanations/explainer.py | 8 +- ads/explanations/mlx_global_explainer.py | 7 +- ads/explanations/mlx_interface.py | 35 +- ads/explanations/mlx_local_explainer.py | 7 +- ads/explanations/mlx_whatif_explainer.py | 11 +- ads/feature_engineering/schema.py | 2 +- ads/hpo/search_cv.py | 18 +- ads/jobs/__init__.py | 15 + ads/jobs/ads_job.py | 83 +- ads/jobs/builders/base.py | 39 +- ads/jobs/builders/infrastructure/base.py | 40 +- ads/jobs/builders/infrastructure/dataflow.py | 162 +- ads/jobs/builders/infrastructure/dsc_job.py | 289 +++- .../infrastructure/dsc_job_runtime.py | 140 +- ads/jobs/builders/runtimes/artifact.py | 70 +- ads/jobs/builders/runtimes/base.py | 92 +- .../builders/runtimes/container_runtime.py | 40 +- ads/jobs/builders/runtimes/python_runtime.py | 212 ++- ads/jobs/env_var_parser.py | 34 +- ads/jobs/serializer.py | 246 +-- ads/jobs/templates/driver_notebook.py | 137 +- ads/jobs/templates/driver_oci.py | 573 ++----- ads/jobs/templates/driver_python.py | 338 +--- ads/jobs/templates/driver_utils.py | 500 ++++++ ads/model/__init__.py | 7 +- ads/model/artifact.py | 41 +- ads/model/artifact_downloader.py | 8 +- ads/model/artifact_uploader.py | 11 +- ads/model/common/utils.py | 29 +- ads/model/datascience_model.py | 50 +- ads/model/deployment/common/utils.py | 44 +- ads/model/deployment/model_deployer.py | 38 +- ads/model/deployment/model_deployment.py | 1440 +++++++++++++---- .../model_deployment_infrastructure.py | 520 ++++++ .../deployment/model_deployment_properties.py | 17 +- .../deployment/model_deployment_runtime.py | 534 ++++++ ads/model/extractor/automl_extractor.py | 7 +- ads/model/extractor/huggingface_extractor.py | 88 + .../extractor/model_info_extractor_factory.py | 4 +- ads/model/framework/automl_model.py | 90 +- ads/model/framework/huggingface_model.py | 396 +++++ ads/model/framework/lightgbm_model.py | 231 +-- ads/model/framework/pytorch_model.py | 256 +-- ads/model/framework/sklearn_model.py | 353 +--- ads/model/framework/spark_model.py | 149 +- ads/model/framework/tensorflow_model.py | 251 +-- ads/model/framework/xgboost_model.py | 258 +-- ads/model/generic_model.py | 554 +++++-- .../model_artifact_validate.py | 5 +- ads/model/model_metadata.py | 6 +- ads/model/model_metadata_mixin.py | 5 +- ads/model/model_version_set.py | 6 +- ads/model/runtime/env_info.py | 47 +- ads/model/runtime/utils.py | 114 +- ads/model/serde/__init__.py | 5 + ads/model/serde/common.py | 40 + ads/model/serde/model_input.py | 547 +++++++ ads/model/serde/model_serializer.py | 1184 ++++++++++++++ ads/model/service/oci_datascience_model.py | 42 +- .../oci_datascience_model_deployment.py | 600 +++++++ ads/opctl/backend/ads_model_deployment.py | 177 ++ ads/opctl/backend/base.py | 22 +- ads/opctl/cli.py | 67 +- ads/opctl/cmds.py | 48 +- ads/opctl/config/merger.py | 4 +- ads/opctl/constants.py | 3 +- .../common/abstract_cluster_provider.py | 44 +- ads/pipeline/ads_pipeline_run.py | 6 +- ads/templates/score-pkl.jinja2 | 82 +- ads/templates/score_generic.jinja2 | 82 +- .../score_huggingface_pipeline.jinja2 | 217 +++ ads/templates/score_lightgbm.jinja2 | 51 +- ads/templates/score_onnx.jinja2 | 1 + ads/templates/score_onnx_new.jinja2 | 21 + ads/templates/score_oracle_automl.jinja2 | 70 +- ads/templates/score_pyspark.jinja2 | 1 + ads/templates/score_pytorch.jinja2 | 32 +- ads/templates/score_scikit-learn.jinja2 | 51 +- ads/templates/score_tensorflow.jinja2 | 45 +- ads/templates/score_xgboost.jinja2 | 41 +- setup.py | 10 +- 99 files changed, 9863 insertions(+), 3516 deletions(-) create mode 100644 ads/jobs/templates/driver_utils.py create mode 100644 ads/model/deployment/model_deployment_infrastructure.py create mode 100644 ads/model/deployment/model_deployment_runtime.py create mode 100644 ads/model/extractor/huggingface_extractor.py create mode 100644 ads/model/framework/huggingface_model.py create mode 100644 ads/model/serde/__init__.py create mode 100644 ads/model/serde/common.py create mode 100644 ads/model/serde/model_input.py create mode 100644 ads/model/serde/model_serializer.py create mode 100644 ads/model/service/oci_datascience_model_deployment.py create mode 100644 ads/opctl/backend/ads_model_deployment.py create mode 100644 ads/templates/score_huggingface_pipeline.jinja2 diff --git a/ads/ads_version.json b/ads/ads_version.json index a3343c314..10441fff1 100644 --- a/ads/ads_version.json +++ b/ads/ads_version.json @@ -1,3 +1,3 @@ { - "version": "2.8.1" + "version": "2.8.2" } diff --git a/ads/automl/driver.py b/ads/automl/driver.py index 9e9867250..af029be20 100644 --- a/ads/automl/driver.py +++ b/ads/automl/driver.py @@ -1,16 +1,19 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from __future__ import absolute_import, division, print_function +import copy + import numpy as np import pandas as pd import scipy -import copy + from ads.common import logger, utils +from ads.common.decorator.deprecate import deprecated from ads.common.model import ADSModel from ads.dataset import helper from ads.dataset.classification_dataset import ( @@ -37,6 +40,10 @@ } +@deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, +) def get_ml_task_type(X, y, classes): """ Gets the ML task type and returns it. @@ -79,6 +86,10 @@ def get_ml_task_type(X, y, classes): class AutoML: + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__( self, training_data, @@ -87,7 +98,7 @@ def __init__( baseline="dummy", client=None, ): - r""" + """ Creates an Automatic machine learning object. Parameters diff --git a/ads/automl/provider.py b/ads/automl/provider.py index 208419610..729d4fba1 100644 --- a/ads/automl/provider.py +++ b/ads/automl/provider.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import logging @@ -41,6 +41,10 @@ class AutoMLProvider(ABC): implement train() and get_transformer_pipeline(). """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self): self.X_train = None self.y_train = None @@ -143,6 +147,10 @@ class BaselineModel(object): interface. Labels (y) are encoded using DataFrameLabelEncoder. """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, est): self.est = est self.df_label_encoder = DataFrameLabelEncoder() @@ -250,6 +258,10 @@ def get_transformer_pipeline(self): msg = "Baseline" return [("automl_preprocessing", AutoMLPreprocessingTransformer(msg))] + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, est): """ Generates a baseline model using the Zero Rule algorithm by default. For a classification @@ -309,6 +321,7 @@ class OracleAutoMLProvider(AutoMLProvider, ABC): @deprecated( "2.6.7", details="Oracle AutoML is recommended to be directly instantiated by importing automlx package", + raise_error=True, ) def __init__( self, n_jobs=-1, loglevel=None, logger_override=None, model_n_jobs: int = 1 @@ -870,6 +883,10 @@ def visualize_tuning_trials(self, ylabel=None): class AutoMLPreprocessingTransformer(object): # pragma: no cover + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, msg): self.msg = msg @@ -915,6 +932,10 @@ def __repr__(self): class AutoMLFeatureSelection(object): # pragma: no cover + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, msg): self.msg = msg diff --git a/ads/catalog/model.py b/ads/catalog/model.py index d107a620a..719fd3318 100644 --- a/ads/catalog/model.py +++ b/ads/catalog/model.py @@ -49,7 +49,7 @@ OCI_REGION_METADATA, PROJECT_OCID, ) -from ads.dataset.progress import DummyProgressBar, TqdmProgressBar +from ads.dataset.progress import TqdmProgressBar from ads.feature_engineering.schema import Schema from ads.model.model_version_set import ModelVersionSet, _extract_model_version_set_id from ads.model.deployment.model_deployer import ModelDeployer @@ -1045,7 +1045,7 @@ def _download_large_artifact( model_id: str, target_dir: str, bucket_uri: str, - progress: Union[TqdmProgressBar, DummyProgressBar], + progress: TqdmProgressBar, remove_existing_artifact: Optional[bool] = True, ) -> None: """ @@ -1062,7 +1062,7 @@ def _download_large_artifact( The OCI Object Storage URI where model artifacts will be copied to. The `bucket_uri` is only necessary for downloading large artifacts with size is greater than 2GB. Example: `bucket_uri=oci://@/prefix/`. - progress: Union[TqdmProgressBar, DummyProgressBar] + progress: TqdmProgressBar The progress bar. remove_existing_artifact: (bool, optional). Defaults to `True`. Whether artifacts uploaded to object storage bucket need to be removed or not. @@ -1125,7 +1125,7 @@ def _download_small_artifact( self, model_id: str, target_dir: str, - progress: Union[TqdmProgressBar, DummyProgressBar], + progress: TqdmProgressBar, ) -> None: """ Downloads the model artifacts from model catalog to target_dir based on `model_id`. @@ -1137,7 +1137,7 @@ def _download_small_artifact( The OCID of the model to download. target_dir: str The target location of model artifacts. - progress: Union[TqdmProgressBar, DummyProgressBar] + progress: TqdmProgressBar The progress bar. Returns @@ -1422,9 +1422,7 @@ def upload_model( progress.update("Done") return self.get_model(model.data.id) - def _prepare_model_artifact( - self, model_artifact, progress: Union[TqdmProgressBar, DummyProgressBar] - ) -> str: + def _prepare_model_artifact(self, model_artifact, progress: TqdmProgressBar) -> str: """Prepares model artifacts to save in the Model Catalog. Returns diff --git a/ads/common/auth.py b/ads/common/auth.py index f3dbd7ab9..9bd2973b6 100644 --- a/ads/common/auth.py +++ b/ads/common/auth.py @@ -46,9 +46,10 @@ class AuthState(metaclass=SingletonMeta): oci_config_path: str = None oci_key_profile: str = None oci_config: str = None - oci_signer: str = None - oci_signer_callable: str = None - oci_signer_kwargs: str = None + oci_signer: Any = None + oci_signer_callable: Callable = None + oci_signer_kwargs: Dict = None + oci_client_kwargs: Dict = None def __post_init__(self): self.oci_iam_type = self.oci_iam_type or os.environ.get( @@ -67,6 +68,7 @@ def __post_init__(self): self.oci_signer = self.oci_signer self.oci_signer_callable = self.oci_signer_callable self.oci_signer_kwargs = self.oci_signer_kwargs or {} + self.oci_client_kwargs = self.oci_client_kwargs or {} def set_auth( @@ -77,6 +79,7 @@ def set_auth( signer: Optional[Any] = None, signer_callable: Optional[Callable] = None, signer_kwargs: Optional[Dict] = {}, + client_kwargs: Optional[Dict] = {}, ) -> None: """ Save type of authentication, profile, config location, config (keypair identity) or signer, which will be used @@ -101,6 +104,9 @@ def set_auth( signer_kwargs: Optional[Dict], default None parameters accepted by the signer. Check documentation: https://docs.oracle.com/en-us/iaas/tools/python/latest/api/signing.html + client_kwargs: Optional[Dict], default None + Additional keyword arguments for initializing the OCI client. + Example: client_kwargs = {"timeout": 60} Examples -------- @@ -110,6 +116,8 @@ def set_auth( >>> ads.set_auth("api_key", oci_config_location = "other_config_location") # use non-default oci_config_location + >>> ads.set_auth("api_key", client_kwargs={"timeout": 60}) # default signer with connection and read timeouts set to 60 seconds for the client. + >>> other_config = oci.config.from_file("other_config_location", "OTHER_PROFILE") # Create non-default config >>> ads.set_auth(config=other_config) # Set api keys type of authentication based on provided config @@ -149,7 +157,7 @@ def set_auth( auth_state.oci_config = config auth_state.oci_key_profile = profile - if auth == AuthType.API_KEY: + if auth == AuthType.API_KEY and not signer and not signer_callable: if os.path.exists(os.path.expanduser(oci_config_location)): auth_state.oci_config_path = oci_config_location else: @@ -160,6 +168,7 @@ def set_auth( auth_state.oci_signer = signer auth_state.oci_signer_callable = signer_callable auth_state.oci_signer_kwargs = signer_kwargs + auth_state.oci_client_kwargs = client_kwargs def api_keys( @@ -326,6 +335,7 @@ def default_signer(client_kwargs: Optional[Dict] = None) -> Dict: ---------- client_kwargs : dict kwargs that are required to instantiate the Client if we need to override the defaults. + Example: client_kwargs = {"timeout": 60} Returns ------- @@ -364,7 +374,10 @@ def default_signer(client_kwargs: Optional[Dict] = None) -> Dict: signer_dict = { "config": configuration, "signer": signer, - "client_kwargs": client_kwargs, + "client_kwargs": { + **(auth_state.oci_client_kwargs or {}), + **(client_kwargs or {}), + }, } logger.info(f"Using authentication signer type {type(signer)}.") return signer_dict @@ -373,7 +386,10 @@ def default_signer(client_kwargs: Optional[Dict] = None) -> Dict: oci_config_location=auth_state.oci_config_path, oci_key_profile=auth_state.oci_key_profile, oci_config=auth_state.oci_config, - client_kwargs=client_kwargs, + client_kwargs={ + **(auth_state.oci_client_kwargs or {}), + **(client_kwargs or {}), + }, ) signer_generator = AuthFactory().signerGenerator(auth_state.oci_iam_type) return signer_generator(signer_args).create_signer() @@ -778,6 +794,9 @@ def __init__(self, **kwargs): a callable object that returns signer signer_kwargs: Optional[Dict], default None parameters accepted by the signer + client_kwargs: Optional[Dict], default None + Additional keyword arguments for initializing the OCI client. + Example: client_kwargs = {"timeout": 60} """ self.kwargs = kwargs diff --git a/ads/common/decorator/deprecate.py b/ads/common/decorator/deprecate.py index e737c7c2c..d5b137218 100644 --- a/ads/common/decorator/deprecate.py +++ b/ads/common/decorator/deprecate.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import warnings @@ -9,6 +9,10 @@ from functools import wraps +class NotSupportedError(Exception): + pass + + class TARGET_TYPE(Enum): CLASS = "Class" METHOD = "Method" @@ -16,10 +20,11 @@ class TARGET_TYPE(Enum): def deprecated( - deprecated_in: str, + deprecated_in: str = None, removed_in: str = None, details: str = None, target_type: str = None, + raise_error: bool = False, ): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted @@ -44,14 +49,19 @@ def wrapper(*args, **kwargs): else: _target_type = target_type or TARGET_TYPE.METHOD.value target_name = target.__name__ - - msg = ( - f"{_target_type} {target_name} is " - f"deprecated in {deprecated_in} and will be " - f"removed in {removed_in if removed_in else 'a future release'}." - f"{'' if not details else ' ' + details}" - ) - warnings.warn(msg, DeprecationWarning, stacklevel=2) + if deprecated_in: + msg = ( + f"{_target_type} {target_name} is " + f"deprecated in {deprecated_in} and will be " + f"removed in {removed_in if removed_in else 'a future release'}." + f"{'' if not details else ' ' + details}" + ) + else: + msg = details + if raise_error: + raise NotSupportedError(msg) + else: + warnings.warn(msg, DeprecationWarning, stacklevel=2) return target(*args, **kwargs) return wrapper diff --git a/ads/common/decorator/runtime_dependency.py b/ads/common/decorator/runtime_dependency.py index 8494dce1a..c6316f156 100644 --- a/ads/common/decorator/runtime_dependency.py +++ b/ads/common/decorator/runtime_dependency.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """ @@ -63,6 +63,7 @@ class OptionalDependency: ONNX = "oracle-ads[onnx]" OPTUNA = "oracle-ads[optuna]" SPARK = "oracle-ads[spark]" + HUGGINGFACE = "oracle-ads[huggingface]" def runtime_dependency( diff --git a/ads/common/model_artifact.py b/ads/common/model_artifact.py index 79e15c8cb..3e0626d8b 100644 --- a/ads/common/model_artifact.py +++ b/ads/common/model_artifact.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import warnings @@ -274,10 +274,12 @@ def __fetch_repo_details(self, training_code_info): else: repository_url = "file://" + repo.working_dir # no remote repo - # get git branch - git_branch = format(repo.active_branch) - # get git commit + git_branch = None + git_commit = None try: + # get git branch + git_branch = format(repo.active_branch) + # get git commit git_commit = format(str(repo.head.commit.hexsha)) training_code_info.GIT_COMMIT = git_commit except ValueError: @@ -1388,7 +1390,7 @@ def populate_metadata(self, model=None, use_case_type=None): The use case type of the model. model: (Any, optional). Defaults to None. This is an optional model object which is only used to extract taxonomy metadata. - Supported models: automl, keras, lightgbm, pytorch, sklearn, tensorflow, and xgboost. + Supported models: keras, lightgbm, pytorch, sklearn, tensorflow, and xgboost. If the model is not under supported frameworks, then extracting taxonomy metadata will be skipped. use_case_type: (str, optional). Default to None. The use case type of the model. diff --git a/ads/common/oci_mixin.py b/ads/common/oci_mixin.py index e05ea032a..44307a4a2 100644 --- a/ads/common/oci_mixin.py +++ b/ads/common/oci_mixin.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """Contains Mixins for integrating OCI data models @@ -18,7 +18,7 @@ import oci import yaml -from ads.common.auth import default_signer +from ads.common import auth from ads.common.decorator.utils import class_or_instance_method from ads.common.utils import camel_to_snake from ads.config import COMPARTMENT_OCID @@ -79,16 +79,16 @@ def _get_auth(cls): client_kwargs.update(cls.kwargs) if cls.config is None and cls.signer is None: - auth = default_signer(client_kwargs) + oci_auth = auth.default_signer(client_kwargs) elif not cls.signer and cls.config: - auth = {"config": cls.config, "client_kwargs": client_kwargs} + oci_auth = {"config": cls.config, "client_kwargs": client_kwargs} else: - auth = { + oci_auth = { "config": cls.config, "signer": cls.signer, "client_kwargs": client_kwargs, } - return auth + return oci_auth @class_or_instance_method def init_client(cls, **kwargs): @@ -98,7 +98,7 @@ def init_client(cls, **kwargs): Parameters ---------- **kwargs : - Additional keyword arguments for initalizing the OCI client. + Additional keyword arguments for initializing the OCI client. Returns ------- @@ -116,7 +116,7 @@ def _init_client(cls, client, **kwargs): client : The OCI client class to be initialized, e.g., oci.data_science.DataScienceClient **kwargs : - Additional keyword arguments for initalizing the OCI client. + Additional keyword arguments for initializing the OCI client. Returns ------- @@ -545,18 +545,21 @@ def from_dict(cls, data): return cls.create_instance(**data) @classmethod - def deserialize(cls, data, to_cls=None): + def deserialize(cls, data: dict, to_cls: str = None): """Deserialize data Parameters ---------- - data : - - to_cls : - (Default value = None) + data : dict + A dictionary containing the data to be deserialized. - Returns - ------- + to_cls : str + The name of the OCI model class to be initialized using the data. + The OCI model class must be from the same OCI service of the OCI client (self.client). + Defaults to None, the parent OCI model class name will be used + if current class is inherited from an OCI model. + If parent OCI model class is not found or not from the same OCI service, + the data will be returned as is. """ if to_cls is None: @@ -744,12 +747,24 @@ def delete(self): return self def __getattribute__(self, name: str): + """Returns an attribute value of the resource. + + This method will try to sync the values from OCI service when it is not already available locally. + Some attribute value may not be available locally if the previous OCI API call returns an OCI model + that contains only a subset of the attributes. + For example, JobSummary model contains only a subset of the attributes from the Job model. + + Parameters + ---------- + name : str + Attribute name. + """ skip_lookup = ["id", "attribute_map", "_oci_attributes"] if name in skip_lookup or name.startswith("_"): return super().__getattribute__(name) - # Ignore if _oci_attributes is not intialized + # Ignore if _oci_attributes is not initialized if not hasattr(self, "_oci_attributes"): return super().__getattribute__(name) @@ -757,6 +772,7 @@ def __getattribute__(self, name: str): hasattr(self, "attribute_map") and name in self.attribute_map and name not in self._oci_attributes + and hasattr(self, "id") and self.id ): # Do not sync if it is in the sync process @@ -768,9 +784,23 @@ def __getattribute__(self, name: str): if not super().__getattribute__(name): try: self.sync(merge_strategy=MergeStrategy.MERGE) + except oci.exceptions.ServiceError as ex: + # 400 errors are usually cause by the user + if ex.status == 400: + logger.error("%s - %s: %s", self.__class__, ex.code, ex.message) + self._oci_attributes = self.attribute_map + else: + logger.error( + "Failed to synchronize the properties of %s due to service error:\n%s", + self.__class__, + str(ex), + ) except Exception as ex: logger.error( - "Failed to synchronize the properties of %s: %s", self, str(ex) + "Failed to synchronize the properties of %s: %s\n%s", + self.__class__, + type(ex), + str(ex), ) return super().__getattribute__(name) diff --git a/ads/common/serializer.py b/ads/common/serializer.py index 0b6f33954..1399e1316 100644 --- a/ads/common/serializer.py +++ b/ads/common/serializer.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and its affiliates. + +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import json @@ -209,11 +210,15 @@ def to_yaml(self, uri: str = None, dumper: callable = dumper, **kwargs) -> str: For other storage connections consider e.g. host, port, username, password, etc. Returns: - string: Serialized version of object + Union[str, None] + Serialized version of object. + None in case when `uri` provided. """ yaml_string = yaml.dump(self.to_dict(**kwargs), Dumper=dumper) if uri: self._write_to_file(s=yaml_string, uri=uri, **kwargs) + return None + return yaml_string @classmethod diff --git a/ads/common/utils.py b/ads/common/utils.py index fb9f22454..d41577009 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -1,12 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from __future__ import absolute_import, print_function -import base64 import collections import contextlib import copy @@ -21,11 +20,12 @@ import string import sys import tempfile +from datetime import datetime from enum import Enum -from io import DEFAULT_BUFFER_SIZE, BytesIO +from io import DEFAULT_BUFFER_SIZE from pathlib import Path from textwrap import fill -from typing import Dict, List, Optional, Union +from typing import Dict, Optional, Union from urllib import request from urllib.parse import urlparse @@ -36,19 +36,23 @@ from ads.common import logger from ads.common.decorator.deprecate import deprecated from ads.common.word_lists import adjectives, animals -from ads.dataset.progress import DummyProgressBar, TqdmProgressBar +from ads.dataset.progress import TqdmProgressBar from cycler import cycler -from datetime import datetime from pandas.core.dtypes.common import is_datetime64_dtype, is_numeric_dtype from sklearn.model_selection import train_test_split from tqdm import tqdm -from . import auth as authutil - +from ads.common import logger +from ads.common.decorator.deprecate import deprecated from ads.common.decorator.runtime_dependency import ( - runtime_dependency, OptionalDependency, + runtime_dependency, ) +from ads.common.word_lists import adjectives, animals +from ads import config +from ads.dataset.progress import DummyProgressBar, TqdmProgressBar + +from . import auth as authutil # For Model / Model Artifact libraries lib_translator = {"sklearn": "scikit-learn"} @@ -682,18 +686,28 @@ def replace_spaces(lst): return [s.replace(" ", "_") for s in lst] -def get_progress_bar(max_progress, description="Initializing"): - """this will return an instance of ProgressBar, sensitive to the runtime environment""" +def get_progress_bar( + max_progress: int, description: str = "Initializing", verbose: bool = False +) -> TqdmProgressBar: + """Returns an instance of the TqdmProgressBar class. - # - # this will return either a DummyProgressBar (non-notebook) or TqdmProgressBar (notebook environement) - # - if is_notebook(): # pragma: no cover - return TqdmProgressBar( - max_progress, description=description, verbose=is_debug_mode() - ) - else: - return DummyProgressBar() + Parameters + ---------- + max_progress: int + The number of steps for the progressbar. + description: (str, optional). Defaults to "Initializing". + The first step description. + verbose: (bool, optional). Defaults to `False` + If the progress should show the debug information. + + Returns + ------- + TqdmProgressBar + An instance of the TqdmProgressBar. + """ + return TqdmProgressBar( + max_progress, description=description, verbose=verbose or is_debug_mode() + ) class JsonConverter(json.JSONEncoder): @@ -1520,3 +1534,38 @@ def batch_convert_case(spec: dict, to_fmt: str) -> Dict: else: converted[converter(k)] = v return converted + + +def extract_region(auth: Optional[Dict] = None) -> Union[str, None]: + """Extracts region information from the environment variables and signer. + + Parameters + ---------- + auth: Dict + The ADS authentication config used to initialize the client. + Contains keys - config, signer and client_kwargs. + + Returns + ------- + Union[str, None] + The region identifier. For example: `us-ashburn-1`. + Returns `None` if region cannot be extracted. + """ + auth = auth or authutil.default_signer() + + if auth.get("config", {}).get("region"): + return auth["config"]["region"] + + if ( + auth.get("signer") + and hasattr(auth["signer"], "region") + and auth["signer"].region + ): + return auth["signer"].region + + try: + return json.loads(config.OCI_REGION_METADATA)["regionIdentifier"] + except: + pass + + return None diff --git a/ads/dataset/progress.py b/ads/dataset/progress.py index 3300ff962..e6c13ea5f 100644 --- a/ads/dataset/progress.py +++ b/ads/dataset/progress.py @@ -1,18 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import time from abc import abstractmethod from oci._vendor import six -from tqdm import tqdm_notebook -from ads.common.decorator.runtime_dependency import ( - runtime_dependency, - OptionalDependency, -) +from tqdm.auto import tqdm class ProgressBar(object): @@ -29,64 +25,16 @@ def __exit__(self, exc_type, exc_val, exc_tb): pass -class IpythonProgressBar(ProgressBar): - @runtime_dependency(module="ipywidgets", install_from=OptionalDependency.NOTEBOOK) - def __init__(self, max_progress=100, description="Running", verbose=False): - self.max_progress = max_progress - - from ads.common import logger - - from ipywidgets import Label, IntProgress - - self.progress_label = Label(description) - self.progress_bar = IntProgress(min=0, max=max_progress) - self.verbose = verbose - if self.verbose: - self.start_time = time.time() - - @runtime_dependency(module="IPython", install_from=OptionalDependency.NOTEBOOK) - def __enter__(self): - from IPython.core.display import display - - display(self.progress_label) - display(self.progress_bar) - return self - - def update(self, description=None): - """ - Updates the progress bar - """ - if self.verbose: - print( - str.format("{0:.3f}", time.time() - self.start_time), - self.progress_label.value, - ) - self.start_time = time.time() - self.progress_bar.value += 1 - if description is not None: - self.progress_label.value = description - - def __exit__(self, exc_type, exc_val, exc_tb): - if exc_tb is None: - self.progress_label.value = "Done" - self.progress_bar.value = self.max_progress - return - raise exc_type(exc_val).with_traceback(exc_tb) - - class TqdmProgressBar(ProgressBar): def __enter__(self): return self - @runtime_dependency(module="ipywidgets", install_from=OptionalDependency.NOTEBOOK) def __init__(self, max_progress=100, description="Running", verbose=False): self.max_progress = max_progress - - from ads.common import logger - - self.progress_bar = tqdm_notebook( - range(max_progress), desc="loop1", mininterval=0, leave=False + self.progress_bar = tqdm( + total=max_progress, desc="loop1", leave=False, mininterval=0 ) + self.progress_bar.set_description(description) self.verbose = verbose if self.verbose: @@ -111,7 +59,7 @@ def update(self, description=None): self.start_time = time.time() self.progress_bar.update(1) if description is not None: - self.progress_bar.set_description(description) + self.progress_bar.set_description(description, refresh=True) def __exit__(self, exc_type, exc_val, exc_tb): if exc_tb is None: diff --git a/ads/evaluations/__init__.py b/ads/evaluations/__init__.py index 7cd9c7c03..63e9160ad 100644 --- a/ads/evaluations/__init__.py +++ b/ads/evaluations/__init__.py @@ -1,11 +1,109 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import logging +from typing import List +from numpy.typing import ArrayLike logger = logging.getLogger(__name__) -from .statistical_metrics import * + +class EvaluatorMixin: + def evaluate( + self, + X: ArrayLike, + y: ArrayLike, + y_pred: ArrayLike = None, + y_score: ArrayLike = None, + X_train: ArrayLike = None, + y_train: ArrayLike = None, + classes: List = None, + positive_class: str = None, + legend_labels: dict = None, + perfect: bool = True, + filename: str = None, + use_case_type: str = None, + ): + """Creates an ads evaluation report. + + Parameters + ---------- + X : DataFrame-like + The data used to make a prediction. + Can be set to None if `y_preds` is given. (And `y_scores` for more thorough analysis). + y : array-like + The true values corresponding to the input data + y_pred : array-like, optional + The predictions from each model in the same order as the models + y_score : array-like, optional + The predict_probas from each model in the same order as the models + X_train : DataFrame-like, optional + The data used to train the model + y_train : array-like, optional + The true values corresponding to the input training data + classes : List or None, optional + A List of the possible labels for y, when evaluating a classification use case + positive_class : str or int, optional + The class to report metrics for binary dataset. If the target classes is True or False, + positive_class will be set to True by default. If the dataset is multiclass or multilabel, + this will be ignored. + legend_labels : dict, optional + List of legend labels. Defaults to `None`. + If legend_labels not specified class names will be used for plots. + use_case_type : str, optional + The type of problem this model is solving. This can be set during `prepare()`. + Examples: "binary_classification", "regression", "multinomial_classification" + Full list of supported types can be found here: `ads.common.model_metadata.UseCaseType` + filename: str, optional + If filename is given, the html report will be saved to the location specified. + + Examples + -------- + >>> import tempfile + >>> from ads.evaluations.evaluator import Evaluator + >>> from sklearn.tree import DecisionTreeClassifier + >>> from sklearn.datasets import make_classification + >>> from sklearn.model_selection import train_test_split + >>> from ads.model.framework.sklearn_model import SklearnModel + >>> from ads.common.model_metadata import UseCaseType + >>> + >>> X, y = make_classification(n_samples=1000) + >>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) + >>> est = DecisionTreeClassifier().fit(X_train, y_train) + >>> model = SklearnModel(estimator=est, artifact_dir=tempfile.mkdtemp()) + >>> model.prepare( + inference_conda_env="generalml_p38_cpu_v1", + training_conda_env="generalml_p38_cpu_v1", + X_sample=X_test, + y_sample=y_test, + use_case_type=UseCaseType.BINARY_CLASSIFICATION, + ) + >>> model.evaluate(X_test, y_test, filename="report.html") + """ + + from ads.evaluations.evaluator import Evaluator + + y_preds, y_scores = ( + ([y_pred], [y_score]) if y_pred is not None else (None, None) + ) + report = Evaluator( + models=[self], + X=X, + y=y, + y_preds=y_preds, + y_scores=y_scores, + X_train=X_train, + y_train=y_train, + classes=classes, + positive_class=positive_class, + legend_labels=legend_labels, + use_case_type=use_case_type, + ) + if filename is None: + report.display(perfect=perfect) + else: + report.save(filename=filename, perfect=perfect) + return report diff --git a/ads/evaluations/evaluation_plot.py b/ads/evaluations/evaluation_plot.py index e3581f5e2..13a9f88a3 100644 --- a/ads/evaluations/evaluation_plot.py +++ b/ads/evaluations/evaluation_plot.py @@ -1,11 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from __future__ import print_function, absolute_import, division +import base64 +from io import BytesIO import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.lines as mlines @@ -29,6 +31,15 @@ MAX_CHARACTERS_LEN = 13 +def _fig_to_html(fig): + tmpfile = BytesIO() + fig.savefig(tmpfile, format="png") + encoded = base64.b64encode(tmpfile.getvalue()).decode("utf-8") + + html = "".format(encoded) + return html + + class EvaluationPlot: """EvaluationPlot holds data and methods for plots and it used to output them @@ -199,11 +210,11 @@ def render_legend_labels(label_dict): pd.Series(label_dict, index=label_dict.keys()), columns=["Shortened labels"], ) - from IPython.core.display import display + from IPython.core.display import display, HTML display( HTML( - encodings.style.set_precision(4) + encodings.style.format(precision=4) .set_properties(**{"text-align": "center"}) .set_table_styles( [dict(selector="", props=[("text-align", "center")])] @@ -213,7 +224,7 @@ def render_legend_labels(label_dict): '

" ) - .render() + .to_html() ) ) @@ -226,7 +237,7 @@ def render_legend_labels(label_dict): cls.legend_labels = legend_labels else: logger.error( - "The provided `legend_labels` is neither a Python dict nor does not possess all possible class labels." + "The provided `legend_labels` is either not a Python dict or does not possess all possible class labels." ) return @@ -374,6 +385,7 @@ def plot( cls.get_legend_labels(legend_labels) mpl.style.use("default") + html_raw = [] for i, plot_type in enumerate(plots): fig_title, fig = None, None try: @@ -389,6 +401,7 @@ def plot( transform=ax_title.transAxes, ) ax_title.axis("off") + html_raw.append(_fig_to_html(fig_title)) if cls.prob_type == "_bin" and plot_type in cls.double_overlay_plots: fig, ax = plt.subplots(1, 2, figsize=(12, 4.5), dpi=144) elif cls.prob_type == "_bin" and plot_type in ["roc_curve", "pr_curve"]: @@ -410,6 +423,7 @@ def plot( ax = ax.flatten() getattr(cls, "_" + plot_type)(ax, evaluation) fig.tight_layout() + html_raw.append(_fig_to_html(fig)) except KeyError as e: try: if fig_title: @@ -424,6 +438,7 @@ def plot( f"metrics had complications. Ensure that `predict` and `predict_proba` " f"are valid." ) + return html_raw @classmethod def _lift_and_gain_chart(cls, ax, evaluation): diff --git a/ads/evaluations/evaluator.py b/ads/evaluations/evaluator.py index bb971e6b9..694fe13e0 100644 --- a/ads/evaluations/evaluator.py +++ b/ads/evaluations/evaluator.py @@ -1,36 +1,587 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd - from cycler import cycler +import logging import matplotlib as mpl +import numpy as np +from numpy.typing import ArrayLike +import pandas as pd import re +from sklearn.preprocessing import LabelEncoder +import tempfile +from typing import List, Any -from ads.common import logger -import logging - -mlogger = logging.getLogger("matplotlib") -mlogger.setLevel(logging.WARNING) - +logging.getLogger("matplotlib").setLevel(logging.WARNING) mpl.rcParams["image.cmap"] = "BuGn" mpl.rcParams["axes.prop_cycle"] = cycler( color=["teal", "blueviolet", "forestgreen", "peru", "y", "dodgerblue", "r"] ) -from ads.evaluations.evaluation_plot import EvaluationPlot -from ads.evaluations.statistical_metrics import ModelEvaluator -from ads.dataset.dataset_with_target import ADSDatasetWithTarget -from ads.common.model import ADSModel +from ads.common.data import ADSData from ads.common.decorator.runtime_dependency import ( runtime_dependency, OptionalDependency, ) +from ads.common.decorator.deprecate import deprecated +from ads.common import logger +from ads.common.model import ADSModel +from ads.common.model_metadata import UseCaseType +from ads.dataset.dataset_with_target import ADSDatasetWithTarget +from ads.evaluations.evaluation_plot import EvaluationPlot +from ads.evaluations.statistical_metrics import ( + ModelEvaluator, + DEFAULT_BIN_CLASS_METRICS, + DEFAULT_MULTI_CLASS_METRICS, + DEFAULT_REG_METRICS, + DEFAULT_BIN_CLASS_LABELS_MAP, + DEFAULT_MULTI_CLASS_LABELS_MAP, + DEFAULT_REG_LABELS_MAP, +) +from ads.model.generic_model import GenericModel, VERIFY_STATUS_NAME + +METRICS_TO_MINIMIZE = ["hamming_loss", "hinge_loss", "mse", "mae"] +POSITIVE_CLASS_NAMES = ["yes", "y", "t", "true", "1"] + + +class Evaluator(object): + """ + BETA FEATURE + Evaluator is the new and preferred way to evaluate a model of list of models. + It contains a superset of the features of the soon-to-be-deprecated ADSEvaluator. + + Methods + ------- + display() + Shows all plots and metrics within the jupyter notebook. + html() + Returns the raw string of the html report + save(filename) + Saves the html report to the provided file location. + add_model(model) + Adds a model to the existsing report. See documentation for more details. + add_metric(metric_fn) + Adds a metric to the existsing report. See documentation for more details. + add_plot(plotting_fn) + Adds a plot to the existing report. See documentation for more details. + + """ + + def __init__( + self, + models: List[GenericModel], + X: ArrayLike, + y: ArrayLike, + y_preds: List[ArrayLike] = None, + y_scores: List[ArrayLike] = None, + X_train: ArrayLike = None, + y_train: ArrayLike = None, + classes: List = None, + positive_class: str = None, + legend_labels: dict = None, + use_case_type: UseCaseType = None, + ): + """Creates an ads evaluator object. + + Parameters + ---------- + models : ads.model.GenericModel instance + Test data to evaluate model on. + The object can be built using from one of the framworks supported in `ads.model.framework` + X : DataFrame-like + The data used to make a prediction. + Can be set to None if `y_preds` is given. (And `y_scores` for more thorough analysis). + y : array-like + The true values corresponding to the input data + y_preds : list of array-like, optional + The predictions from each model in the same order as the models + y_scores : list of array-like, optional + The predict_probas from each model in the same order as the models + X_train : DataFrame-like, optional + The data used to train the model + y_train : array-like, optional + The true values corresponding to the input training data + positive_class : str or int, optional + The class to report metrics for binary dataset. If the target classes is True or False, + positive_class will be set to True by default. If the dataset is multiclass or multilabel, + this will be ignored. + legend_labels : dict, optional + List of legend labels. Defaults to `None`. + If legend_labels not specified class names will be used for plots. + classes : List or None, optional + A List of the possible labels for y, when evaluating a classification use case + use_case_type : str, optional + The type of problem this model is solving. This can be set during `prepare()`. + Examples: "binary_classification", "regression", "multinomial_classification" + Full list of supported types can be found here: `ads.common.model_metadata.UseCaseType` + + Examples + -------- + >>> import tempfile + >>> from ads.evaluations.evaluator import Evaluator + >>> from sklearn.tree import DecisionTreeClassifier + >>> from sklearn.datasets import make_classification + >>> from sklearn.model_selection import train_test_split + >>> from ads.model.framework.sklearn_model import SklearnModel + >>> from ads.common.model_metadata import UseCaseType + >>> + >>> X, y = make_classification(n_samples=1000) + >>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) + >>> est = DecisionTreeClassifier().fit(X_train, y_train) + >>> model = SklearnModel(estimator=est, artifact_dir=tempfile.mkdtemp()) + >>> model.prepare( + inference_conda_env="generalml_p38_cpu_v1", + training_conda_env="generalml_p38_cpu_v1", + X_sample=X_test, + y_sample=y_test, + use_case_type=UseCaseType.BINARY_CLASSIFICATION, + ) + >>> report = Evaluator([my_model], X=X_test, y=y_test) + >>> report.display() + + """ + self._verify_models(models) + self.X, self.y, self.X_train, self.y_train = X, y, X_train, y_train + self.legend_labels = legend_labels + self.positive_class = positive_class + + self._determine_problem_type(models, use_case_type) + self._determine_classes(classes) + + self.model_names = [] + self.evaluation = pd.DataFrame() + self.add_models(models, y_preds=y_preds, y_scores=y_scores) + + def _verify_models(self, models): + assert isinstance( + models, list + ), f"The `models` argument must be a list of models, instead got: {models}" + for m in models: + if not isinstance(m, GenericModel): + raise ValueError( + f"Please register and prepare model {m} with ads. More information here: https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/introduction.html#register" + ) + sum_stat = m.summary_status().reset_index() + if ( + sum_stat.loc[sum_stat["Step"] == VERIFY_STATUS_NAME, "Status"] + == "Not Available" + ).any(): + raise ValueError( + f"Model {m} has not been prepared, and `verify` cannot be run (including the pre and post processing from the score.py). This may cause issues. Prepare the model in accordance with the documentation: https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/model_artifact.html#prepare-the-model-artifact" + ) + + def _determine_problem_type(self, models, use_case_type): + if use_case_type is not None: + self.problem_type = use_case_type + problem_type = models[0].metadata_taxonomy["UseCaseType"].value + if problem_type is not None: + for m in models: + assert ( + problem_type == m.metadata_taxonomy["UseCaseType"].value + ), f"Cannot compare models of different Use Case types. The first model is of type {problem_type}, while model: {m} is of Use Case type: {m.metadata_taxonomy['UseCaseType'].value}" + self.problem_type = problem_type + else: + if not models[0].schema_output.keys: + raise ValueError( + f"The Use Case Type of this model, {models[0]}, is ambigious. Please re-run Evaluator with `use_case_type` set to a valid type (full list found here: ads.common.model_metadata.UseCaseType). To avoid setting this in the future, set the `use_case_type` when preparing the model. Or update your model's use_case_type attribute here: `model.metadata_taxonomy['UseCaseType'].value` More information here: https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/model_metadata.html#taxonomy-metadata" + ) + logger.warn( + f"The Use Case Type of this model, {models[0]}, is ambigious. Please set the `model.metadata_taxonomy['UseCaseType'].value` attribute to one of the options in ads.common.model_metadata.UseCaseType" + ) + + output_col = models[0].schema_output.keys[0] + if models[0].schema_output[output_col].feature_type != "Continuous": + if len(np.unique(self.y)) == 2: + self.problem_type = UseCaseType.BINARY_CLASSIFICATION + else: + self.problem_type = UseCaseType.MULTINOMIAL_CLASSIFICATION + else: + self.problem_type = UseCaseType.REGRESSION + logger.info(f"Set Use Case Type to: {self.problem_type}") + + def _determine_classes(self, classes): + if self.problem_type in [UseCaseType.REGRESSION]: + self.classes = [] + self.metrics_to_show = DEFAULT_REG_METRICS + self.is_classifier = False + else: + self.is_classifier = True + self.classes = ( + classes or np.unique(self.y_train) + if self.y_train is not None + else np.unique(self.y) + ) + self.num_classes = len(self.classes) + if len(self.classes) == 2: + self.metrics_to_show = DEFAULT_BIN_CLASS_METRICS + if ( + self.positive_class is None + or self.positive_class not in self.classes + ): + self.positive_class = next( + ( + x + for x in list(self.classes) + if str(x).lower() in POSITIVE_CLASS_NAMES + ), + self.classes[0], + ) + logger.info( + f"Using {self.positive_class} as the positive class. Use `positive_class` to set this value." + ) + else: + self.metrics_to_show = DEFAULT_MULTI_CLASS_METRICS + + def _get_model_name(self, model): + name = str(model.algorithm) + "_" + str(model.framework) + name_edit = re.sub(r" ?\([^)]+\)", "", name) + if name_edit in self.model_names: + name_edit += "_1" + num_tries = 1 + while name_edit in self.model_names: + num_tries += 1 + name_edit = name_edit[:-1] + str(num_tries) + self.model_names.append(name_edit) + return name_edit + + def _score_data(self, model, X): + y_pred = model.verify(X)["prediction"] + + y_score = None + # we will compute y_score only for binary classification cases because only for binary classification can + # we use it for ROC Curves and AUC + if self.is_classifier and hasattr(model.estimator, "predict_proba"): + if len(self.classes) == 2: + # positive label index is assumed to be 0 if the ADSModel does not have a positive class defined + positive_class_index = 0 + # For prediction probability, we only consider the positive class. + if self.positive_class is not None: + if self.positive_class not in list(self.classes): + raise ValueError( + "Invalid positive class '%s' for model %s. Positive class should be one of %s." + % ( + self.positive_class, + model.estimator.__class__.__name__, + list(self.classes), + ) + ) + positive_class_index = list(self.classes).index(self.positive_class) + y_score = model.estimator.predict_proba(X)[:, positive_class_index] + else: + y_score = model.estimator.predict_proba(X) + return y_pred, y_score + + def add_models( + self, + models: List[GenericModel], + y_preds: List[Any] = None, + y_scores: List[Any] = None, + ): + """Add a model to an existing Evaluator to avoid re-calculating the values. + + Parameters + ---------- + models : List[ads.model.GenericModel] + Test data to evaluate model on. + The object can be built using from one of the framworks supported in `ads.model.framework` + y_preds : list of array-like, optional + The predictions from each model in the same order as the models + y_scores : list of array-like, optional + The predict_probas from each model in the same order as the models + + Returns + ------- + self + + Examples + -------- + >>> evaluator = Evaluator(models = [model1, model2], X=X, y=y) + >>> evaluator.add_models(models = [model3]) + """ + + assert isinstance(models, List), "The `models` parameter must be of type list." + if self.is_classifier: + self._le = LabelEncoder().fit(self.y) + for i, m in enumerate(models): + m_name = self._get_model_name(m) + + if y_preds is None: + y_pred, y_score = self._score_data(m, self.X) + else: + y_pred = y_preds[i] + y_score = y_scores[i] if isinstance(y_scores, list) else None + if self.is_classifier: + y_true, y_pred = self._le.transform(self.y), self._le.transform(y_pred) + classes = self._le.transform(self.classes) + pos_class = None + if len(self.classes) == 2: + pos_class = self._le.transform([self.positive_class])[0] + else: + y_true, y_pred, classes, pos_class = self.y, y_pred, None, None + new_model_metrics = ModelEvaluator( + y_true=y_true, + y_pred=y_pred, + model_name=m_name, + classes=classes, + y_score=y_score, + positive_class=pos_class, + ).get_metrics() + self.evaluation = pd.concat( + [self.evaluation, new_model_metrics], axis=1, sort=False + ) + return self + + @runtime_dependency(module="IPython", install_from=OptionalDependency.NOTEBOOK) + @runtime_dependency( + module="ipywidgets", + object="HTML", + install_from=OptionalDependency.NOTEBOOK, + ) + def display( + self, + plots=None, + perfect=False, + baseline=True, + legend_labels=None, + precision=4, + metrics_labels=None, + ): + """Visualize evaluation report. + + Parameters + ---------- + plots : list, optional + Filter the plots that are displayed. Defaults to None. The name of the plots are as below: + + - regression - residuals_qq, residuals_vs_fitted + - binary classification - normalized_confusion_matrix, roc_curve, pr_curve + - multi class classification - normalized_confusion_matrix, precision_by_label, recall_by_label, f1_by_label + perfect: bool, optional (default False) + If True, will show how a perfect classifier would perform. + baseline: bool, optional (default True) + If True, will show how a random classifier would perform. + legend_labels : dict, optional + Rename legend labels, that used for multi class classification plots. Defaults to None. + legend_labels dict keys are the same as class names. legend_labels dict values are strings. + If legend_labels not specified class names will be used for plots. + precision: int, optional (default 4) + The number of decimal points to show for each score/loss value + metrics_labels: List, optional + The metrics that should be included in the html table. + + Returns + ------- + None + Nothing. Outputs several evaluation plots as specified by `plots`. + + Examples + -------- + + >>> evaluator = Evaluator(models=[model1, model2], X=X, y=y) + >>> evaluator.display() + + >>> legend_labels={'class_0': 'green', 'class_1': 'yellow', 'class_2': 'red'} + >>> multi_evaluator = Evaluator(models=[model1, model2], X=X, y=y, legend_labels=legend_labels) + >>> multi_evaluator.display(plots=["normalized_confusion_matrix", + ... "precision_by_label", "recall_by_label", "f1_by_label"]) + """ + from IPython.core.display import display, HTML + + legend_labels = ( + legend_labels if legend_labels is not None else self.legend_labels + ) + if legend_labels is None and self.is_classifier: + legend_labels = dict( + zip([str(x) for x in self._le.transform(self.classes)], self.classes) + ) + # pass to plotting class + self._get_plots_html( + plots=plots, perfect=perfect, baseline=baseline, legend_labels=legend_labels + ) + display( + HTML(self._get_metrics_html(precision=precision, labels=metrics_labels)) + ) + + def html( + self, + plots=None, + perfect=False, + baseline=True, + legend_labels=None, + precision=4, + metrics_labels=None, + ): + """Get raw HTML report. + + Parameters + ---------- + plots : list, optional + Filter the plots that are displayed. Defaults to None. The name of the plots are as below: + + - regression - residuals_qq, residuals_vs_fitted + - binary classification - normalized_confusion_matrix, roc_curve, pr_curve + - multi class classification - normalized_confusion_matrix, precision_by_label, recall_by_label, f1_by_label + perfect: bool, optional (default False) + If True, will show how a perfect classifier would perform. + baseline: bool, optional (default True) + If True, will show how a random classifier would perform. + legend_labels : dict, optional + Rename legend labels, that used for multi class classification plots. Defaults to None. + legend_labels dict keys are the same as class names. legend_labels dict values are strings. + If legend_labels not specified class names will be used for plots. + precision: int, optional (default 4) + The number of decimal points to show for each score/loss value + metrics_labels: List, optional + The metrics that should be included in the html table. + + Returns + ------- + None + Nothing. Outputs several evaluation plots as specified by `plots`. + + Examples + -------- + + >>> evaluator = Evaluator(models=[model1, model2], X=X, y=y) + >>> raw_html = evaluator.html() + """ + html_plots = self._get_plots_html( + plots=plots, perfect=perfect, baseline=baseline, legend_labels=legend_labels + ) + html_metrics = self._get_metrics_html( + precision=precision, labels=metrics_labels + ) + html_raw = ( + "

Evaluation Report

\ +

Evaluation Plots

" + + " \ + ".join( + html_plots + ) + + f"

Evaluation Metrics

\ +

{html_metrics}

" + ) + return html_raw + + def save(self, filename: str, **kwargs): + """Save HTML report. + + Parameters + ---------- + filename: str + The name and path of where to save the html report. + plots : list, optional + Filter the plots that are displayed. Defaults to None. The name of the plots are as below: + + - regression - residuals_qq, residuals_vs_fitted + - binary classification - normalized_confusion_matrix, roc_curve, pr_curve + - multi class classification - normalized_confusion_matrix, precision_by_label, recall_by_label, f1_by_label + perfect: bool, optional (default False) + If True, will show how a perfect classifier would perform. + baseline: bool, optional (default True) + If True, will show how a random classifier would perform. + legend_labels : dict, optional + Rename legend labels, that used for multi class classification plots. Defaults to None. + legend_labels dict keys are the same as class names. legend_labels dict values are strings. + If legend_labels not specified class names will be used for plots. + precision: int, optional (default 4) + The number of decimal points to show for each score/loss value + metrics_labels: List, optional + The metrics that should be included in the html table. + + Returns + ------- + None + Nothing. Outputs several evaluation plots as specified by `plots`. + + Examples + -------- + + >>> evaluator = Evaluator(models=[model1, model2], X=X, y=y) + >>> evaluator.save("report.html") + """ + raw_html = self.html(**kwargs) + with open(filename, "w") as f: + f.write(raw_html) + + def _get_plots_html( + self, + plots=None, + perfect=False, + baseline=True, + legend_labels=None, + ): + return EvaluationPlot.plot( + self.evaluation, plots, len(self.classes), perfect, baseline, legend_labels + ) + + def _get_metrics_html(self, precision=4, labels=None): + def highlight_max(s): + """Highlight the maximum in a Series yellow. + + Parameters + ---------- + s : series object + the series being evaluated + + Returns + ------- + list + containing background color data or empty if not max + """ + if s.name not in METRICS_TO_MINIMIZE: + is_max = s == s.max() + else: + is_max = s == s.min() + return ["background-color: lightgreen" if v else "" for v in is_max] + + def _pretty_label(df, labels, copy=True): + """ + Output specified labels in proper format. + If the labels are provided in then used them. Otherwise, use default. + + Parameters + ---------- + labels : dictionary + map printing specific labels for metrics display + + Returns + ------- + dataframe + dataframe with index names modified according to input labels + """ + df_display = df.loc[list(labels.keys())] + + if copy: + df_display = df_display.copy() + for k, v in labels.items(): + df_display.rename(index={k: v}, inplace=True) + return df_display + + if labels is None: + if self.is_classifier: + if len(self.classes) == 2: + labels = DEFAULT_BIN_CLASS_LABELS_MAP + else: + labels = DEFAULT_MULTI_CLASS_LABELS_MAP + else: + labels = DEFAULT_REG_LABELS_MAP + html_raw = ( + _pretty_label(self.evaluation, labels) + .style.apply(highlight_max, axis=1) + .format(precision=precision) + .set_properties(**{"text-align": "center"}) + .set_table_attributes("class=table") + .set_caption( + '
' + + "Evaluation Metrics:
" + ) + .to_html() + ) + return html_raw class ADSEvaluator(object): @@ -42,7 +593,7 @@ class ADSEvaluator(object): evaluations : list[DataFrame] list of evaluations. is_classifier : bool - Whether the model has a non-empty `classes_` attribute indicating the presence of class labels. + Whether the dataset looks like a classification problem (versus regression). legend_labels : dict List of legend labels. Defaults to `None`. metrics_to_show : list[str] @@ -87,6 +638,8 @@ def __init__( positive_class=None, legend_labels=None, show_full_name=False, + classes=None, + classification_threshold=50, ): """Creates an ads evaluator object. @@ -110,6 +663,11 @@ def __init__( If legend_labels not specified class names will be used for plots. show_full_name : bool, optional Show the name of the evaluator object. Defaults to `False`. + classes : List or None, optional + A List of the possible labels for y, when evaluating a classification use case + classification_threshold : int, defaults to 50 + The maximum number of unique values that y must have to qualify as classification. + If this threshold is exceeded, Evaluator assumes the model is regression. Examples -------- @@ -124,13 +682,28 @@ def __init__( ... legend_labels=legend_labels) """ - + if any(isinstance(m, ADSModel) for m in models): + logger.warn( + f"ADSModel is being deprecated. Users should instead use GenericModel or one of its subclasses. More information here: https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/introduction.html#register" + ) self.evaluations = [] if isinstance(training_data, ADSDatasetWithTarget): training_data, _ = training_data.train_test_split(test_size=0.0) if isinstance(test_data, ADSDatasetWithTarget): test_data, _ = test_data.train_test_split(test_size=0.0) + if not isinstance(test_data, ADSData): + raise ValueError( + "Expected test_data to be of type ADSData. More information here: https://accelerated-data-science.readthedocs.io/en/latest/ads.common.html#ads.common.data.ADSData" + ) + if training_data and not isinstance(training_data, ADSData): + raise ValueError( + "Expected training_data to be of type ADSData. More information here: https://accelerated-data-science.readthedocs.io/en/latest/ads.common.html#ads.common.data.ADSData" + ) + assert isinstance( + models, list + ), "The `models` argument should be a list of GenericModels. More information here: https://accelerated-data-science.readthedocs.io/en/latest/ads.common.html#ads.common.data.ADSData" + self.test_data = test_data self.training_data = training_data self.classes = [] diff --git a/ads/evaluations/statistical_metrics.py b/ads/evaluations/statistical_metrics.py index 716bfc2c2..3bee06eac 100644 --- a/ads/evaluations/statistical_metrics.py +++ b/ads/evaluations/statistical_metrics.py @@ -11,7 +11,7 @@ import pandas as pd from sklearn import metrics from sklearn.preprocessing import LabelEncoder -from ads.common import utils, logger +from ads.common import logger from ads.common.decorator.runtime_dependency import ( runtime_dependency, OptionalDependency, @@ -19,6 +19,50 @@ __all__ = ["ModelEvaluator"] +DEFAULT_BIN_CLASS_METRICS = [ + "accuracy", + "hamming_loss", + "precision", + "recall", + "f1", + "auc", +] +DEFAULT_MULTI_CLASS_METRICS = [ + "accuracy", + "hamming_loss", + "precision_weighted", + "precision_micro", + "recall_weighted", + "recall_micro", + "f1_weighted", + "f1_micro", +] +DEFAULT_REG_METRICS = ["r2_score", "mse", "mae"] +DEFAULT_BIN_CLASS_LABELS_MAP = { + "accuracy": "Accuracy", + "hamming_loss": "Hamming distance", + "kappa_score": "Cohen's kappa coefficient", + "precision": "Precision", + "recall": "Recall", + "f1": "F1", + "auc": "ROC AUC", +} +DEFAULT_MULTI_CLASS_LABELS_MAP = { + "accuracy": "Accuracy", + "hamming_loss": "Hamming distance", + "precision_weighted": "Precision Weighted Average", + "precision_micro": "Precision Micro Average", + "recall_weighted": "Recall Weighted Average", + "recall_micro": "Recall Micro Average", + "f1_weighted": "F1 Weighted Average", + "f1_micro": "F1 Micro Average", +} +DEFAULT_REG_LABELS_MAP = { + "r2_score": "r-Squared Score", + "root_mean_squared_error": "Root Mean Squared Error", + "median_absolute_error": "Median Absolute Error", +} + class ModelEvaluator: """ @@ -105,34 +149,38 @@ def get_metrics(self): ) def _get_general_metrics(self): - args = [self.y_true, self.y_pred, self.classes] - scoring_functions = { - "classification_report": ("classification_report", len(args)), - "kappa_score": ("cohen_kappa_score", len(args)), - "raw_confusion_matrix": ("confusion_matrix", len(args)), - "hamming_loss": ("hamming_loss", len(args) - 1), - "hinge_loss": ("hinge_loss", len(args)), - } - - self.safe_metrics_call(scoring_functions, *args) - - args = [self.y_true, self.y_pred] - scoring_functions_without_labs = { - "accuracy": ("accuracy_score", len(args)), - "zero_one_loss": ("zero_one_loss", len(args)), - } - self.safe_metrics_call(scoring_functions_without_labs, *args) - - # could still add: brier_score_loss, log_loss, matthews_corrcoef - - cm = self.metrics["raw_confusion_matrix"] - a = cm.astype("float") - b = cm.sum(axis=1)[:, np.newaxis] - normalized_cm = np.divide(a, b, out=np.zeros_like(a), where=b != 0).tolist() - self.metrics["confusion_matrix"] = normalized_cm - - vc = pd.DataFrame(self.y_true).value_counts() - self.metrics["class_balance"] = min(vc) / max(vc) + try: + args = [self.y_true, self.y_pred] + kwargs = {"labels": self.classes} + + scoring_functions = { + "classification_report": ("classification_report", len(args)), + "kappa_score": ("cohen_kappa_score", len(args)), + "raw_confusion_matrix": ("confusion_matrix", len(args)), + "hinge_loss": ("hinge_loss", len(args)), + } + self.safe_metrics_call(scoring_functions, *args, **kwargs) + + args = [self.y_true, self.y_pred] + scoring_functions_without_labs = { + "accuracy": ("accuracy_score", len(args)), + "zero_one_loss": ("zero_one_loss", len(args)), + "hamming_loss": ("hamming_loss", len(args)), + } + self.safe_metrics_call(scoring_functions_without_labs, *args) + + cm = self.metrics["raw_confusion_matrix"] + a = cm.astype("float") + b = cm.sum(axis=1)[:, np.newaxis] + normalized_cm = np.divide(a, b, out=np.zeros_like(a), where=b != 0).tolist() + self.metrics["confusion_matrix"] = normalized_cm + + vc = pd.DataFrame(self.y_true).value_counts() + self.metrics["class_balance"] = min(vc) / max(vc) + except: + raise ValueError( + f"Errors arose when attempting to compute metrics. Metrics broke in the state: {self.metrics}" + ) def _get_binary_metrics(self): self._get_general_metrics() @@ -453,7 +501,7 @@ def _get_regression_metrics(self): ] self.metrics["residual_quantiles"] = scipy.stats.zscore(resid_quant) - def safe_metrics_call(self, scoring_functions, *args): + def safe_metrics_call(self, scoring_functions, *args, **kwargs): """Applies the sklearn function in `scoring_functions` to parameters in `args`. Parameters @@ -474,9 +522,15 @@ def safe_metrics_call(self, scoring_functions, *args): try: if fn == "confusion_matrix": self.metrics[name] = getattr(metrics, fn)( - **{"y_true": args[0], "y_pred": args[1], "labels": args[2]} + **{ + "y_true": args[0], + "y_pred": args[1], + "labels": kwargs["labels"], + } ) else: - self.metrics[name] = getattr(metrics, fn)(*(args[:n_params])) + self.metrics[name] = getattr(metrics, fn)( + *(args[:n_params]), **kwargs + ) except Exception as e: self.metrics[name] = f"Error unable to compute {fn}, due to: {e}" diff --git a/ads/explanations/__init__.py b/ads/explanations/__init__.py index eca437808..6d1102b6e 100644 --- a/ads/explanations/__init__.py +++ b/ads/explanations/__init__.py @@ -1,16 +1,21 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import logging -import mlx logger = logging.getLogger(__name__) from .mlx_global_explainer import * from .mlx_local_explainer import * +from .mlx_whatif_explainer import * -mlx.initjs() +try: + import mlx + + mlx.initjs() +except: + pass diff --git a/ads/explanations/base_explainer.py b/ads/explanations/base_explainer.py index b59e49749..b56bd8202 100644 --- a/ads/explanations/base_explainer.py +++ b/ads/explanations/base_explainer.py @@ -1,17 +1,21 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from abc import abstractmethod -from ads.common.model import ADSModel +from ads.common.decorator.deprecate import deprecated class Explainer: """Base class for `GlobalExplainer` and `LocalExplainer`""" + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self): self.X_train_ = None self.y_train_ = None diff --git a/ads/explanations/explainer.py b/ads/explanations/explainer.py index e983024a8..847158ff0 100644 --- a/ads/explanations/explainer.py +++ b/ads/explanations/explainer.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from ads.explanations import MLXGlobalExplainer, MLXLocalExplainer from ads.explanations.mlx_whatif_explainer import MLXWhatIfExplainer from ads.explanations.mlx_interface import _reset_index -from ads.common import utils +from ads.common.decorator.deprecate import deprecated class ADSExplainer(object): @@ -16,6 +16,10 @@ class ADSExplainer(object): explanation objects. """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, test_data, model, training_data=None): """ Creates an ads explainer object. diff --git a/ads/explanations/mlx_global_explainer.py b/ads/explanations/mlx_global_explainer.py index 5956f01bd..22f14b39c 100644 --- a/ads/explanations/mlx_global_explainer.py +++ b/ads/explanations/mlx_global_explainer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import numpy as np @@ -20,6 +20,7 @@ runtime_dependency, OptionalDependency, ) +from ads.common.decorator.deprecate import deprecated class MLXGlobalExplainer(GlobalExplainer): @@ -43,6 +44,10 @@ class MLXGlobalExplainer(GlobalExplainer): """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self): super(GlobalExplainer, self).__init__() self.explainer = None diff --git a/ads/explanations/mlx_interface.py b/ads/explanations/mlx_interface.py index c6e5feab4..2e4a424df 100644 --- a/ads/explanations/mlx_interface.py +++ b/ads/explanations/mlx_interface.py @@ -1,16 +1,21 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -from mlx import LimeExplainer -from mlx import FDExplainer, AleExplainer -from mlx import PermutationImportance +try: + from mlx import LimeExplainer + from mlx import FDExplainer, AleExplainer + from mlx import PermutationImportance + from mlx.whatif import WhatIf +except: + pass from ads.common import logger from ads.dataset.helper import is_text_data -from mlx.whatif import WhatIf + import pandas as pd +from ads.common.decorator.deprecate import deprecated def _reset_index(x): @@ -37,6 +42,10 @@ def check_tabular_or_text(est, X): return "text" if is_text_data(X) else "tabular" +@deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, +) def init_lime_explainer( explainer, est, @@ -137,6 +146,10 @@ def init_lime_explainer( return exp +@deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, +) def init_permutation_importance_explainer( explainer, est, @@ -215,6 +228,10 @@ def init_permutation_importance_explainer( return exp +@deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, +) def init_partial_dependence_explainer( explainer, est, X_train, y_train, mode, class_names=None, client=None ): @@ -269,6 +286,10 @@ def init_partial_dependence_explainer( return exp +@deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, +) def init_ale_explainer( explainer, est, X_train, y_train, mode, class_names=None, client=None ): @@ -322,6 +343,10 @@ def init_ale_explainer( return exp +@deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, +) def init_whatif_explainer( explainer, est, diff --git a/ads/explanations/mlx_local_explainer.py b/ads/explanations/mlx_local_explainer.py index 235a9b77e..8ee1e1e8c 100644 --- a/ads/explanations/mlx_local_explainer.py +++ b/ads/explanations/mlx_local_explainer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import numpy as np @@ -9,6 +9,7 @@ from ads.common import logger from ads.explanations.base_explainer import LocalExplainer from ads.explanations.mlx_interface import init_lime_explainer +from ads.common.decorator.deprecate import deprecated class MLXLocalExplainer(LocalExplainer): @@ -23,6 +24,10 @@ class MLXLocalExplainer(LocalExplainer): - Regression """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self): super(LocalExplainer, self).__init__() self.explainer = None diff --git a/ads/explanations/mlx_whatif_explainer.py b/ads/explanations/mlx_whatif_explainer.py index a1e8292c5..aadbd710e 100644 --- a/ads/explanations/mlx_whatif_explainer.py +++ b/ads/explanations/mlx_whatif_explainer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import numpy as np @@ -10,6 +10,7 @@ from ads.explanations.mlx_interface import init_whatif_explainer from ads.common import logger from ads.common.utils import _log_multivalue_feature_column_error +from ads.common.decorator.deprecate import deprecated class MLXWhatIfExplainer(WhatIfExplainer): @@ -28,6 +29,10 @@ class MLXWhatIfExplainer(WhatIfExplainer): """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self): super(MLXWhatIfExplainer, self).__init__() self.explainer = None @@ -188,5 +193,9 @@ def explore_predictions( class WhatIfExplanationsException(TypeError): + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, msg): super(WhatIfExplanationsException, self).__init__(msg) diff --git a/ads/feature_engineering/schema.py b/ads/feature_engineering/schema.py index b21f144c1..82a5f9a09 100644 --- a/ads/feature_engineering/schema.py +++ b/ads/feature_engineering/schema.py @@ -145,7 +145,7 @@ class Attribute(DataClassSerializable): * feature_type - Feature type of data - Integer, String, etc. Matches with ads feature types. * name - Name of the feature * domain - Represented by the Domain class - * required - Boolean - True of False + * required - Boolean - True or False * description - Description about the column/feature * order - order of the column/feature in the data diff --git a/ads/hpo/search_cv.py b/ads/hpo/search_cv.py index 0927a1702..24a772ecc 100644 --- a/ads/hpo/search_cv.py +++ b/ads/hpo/search_cv.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import importlib @@ -104,6 +104,18 @@ class NoRestartError(Exception): pass +class DataScienceObjective: + """This class is to replace the previous lambda function to solve the problem that python does not allow pickle local function/lambda function.""" + + def __init__(self, objective, X_res, y_res): + self.objective = objective + self.X_res = X_res + self.y_res = y_res + + def __call__(self, trial): + return self.objective(self.X_res, self.y_res, trial) + + class ADSTuner(BaseEstimator): """ Hyperparameter search with cross-validation. @@ -1146,8 +1158,6 @@ def _tune( self._step_name, ) - objective_func = lambda trial: objective(X_res, y_res, trial) - if synchronous: logger.info( "Optimizing hyperparameters using {} " @@ -1162,7 +1172,7 @@ def _tune( self.sampler, self.storage, self.load_if_exists, - objective_func, + DataScienceObjective(objective, X_res, y_res), self._global_start, self._global_stop, ), diff --git a/ads/jobs/__init__.py b/ads/jobs/__init__.py index 6ec966992..d0190e3aa 100644 --- a/ads/jobs/__init__.py +++ b/ads/jobs/__init__.py @@ -34,3 +34,18 @@ ) else: raise e + +__all__ = [ + "Job", + "DataScienceJob", + "DataScienceJobRun", + "PythonRuntime", + "GitPythonRuntime", + "NotebookRuntime", + "ScriptRuntime", + "ContainerRuntime", + "DataFlow", + "DataFlowRun", + "DataFlowRuntime", + "DataFlowNotebookRuntime", +] diff --git a/ads/jobs/ads_job.py b/ads/jobs/ads_job.py index 355ec0f3a..bd68c5c35 100644 --- a/ads/jobs/ads_job.py +++ b/ads/jobs/ads_job.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from typing import List, Union from urllib.parse import urlparse @@ -23,36 +23,57 @@ class Job(Builder): - """Represents a Job containing infrastructure and runtime. + """Represents a Job defined by infrastructure and runtime. - Example - ------- - Here is an example for creating and running a job: - - .. code-block:: python + Examples + -------- + Here is an example for creating and running a job:: from ads.jobs import Job, DataScienceJob, PythonRuntime + # Define an OCI Data Science job to run a python script job = ( Job(name="") .with_infrastructure( DataScienceJob() + # Configure logging for getting the job run outputs. + .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. + .with_log_id("") + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. .with_compartment_id("") .with_project_id("") .with_subnet_id("") .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). .with_block_storage_size(50) - .with_log_group_id("") - .with_log_id("") ) .with_runtime( - ScriptRuntime() - .with_source("oci://bucket_name@namespace/path/to/script.py") - .with_service_conda("tensorflow26_p37_cpu_v2") - .with_environment_variable(ENV="value") - .with_argument("argument", key="value") - .with_freeform_tag(tag_name="tag_value") + PythonRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch110_p38_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument, arg1 --key arg2 + .with_argument("arg1", key="arg2") + # Set the working directory + # When using a directory as source, the default working dir is the parent of code_dir. + # Working dir should be a relative path beginning from the source directory (code_dir) + .with_working_dir("code_dir") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.py is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.py") + # Add an additional Python path, relative to the working dir (code_dir/other_packages). + .with_python_path("other_packages") + # Copy files in "code_dir/output" to object storage after job finishes. + .with_output("output", "oci://bucket_name@namespace/path/to/dir") ) ) # Create and Run the job @@ -62,11 +83,10 @@ class Job(Builder): If you are in an OCI notebook session and you would like to use the same infrastructure configurations, the infrastructure configuration can be simplified. - Here is another example of creating and running a jupyter notebook as a job: - - .. code-block:: python + Here is another example of creating and running a jupyter notebook as a job:: from ads.jobs import Job, DataScienceJob, NotebookRuntime + # Define an OCI Data Science job to run a jupyter Python notebook job = ( Job(name="") @@ -79,7 +99,7 @@ class Job(Builder): .with_runtime( NotebookRuntime() .with_notebook("path/to/notebook.ipynb") - .with_service_conda(tensorflow26_p37_cpu_v2") + .with_service_conda(tensorflow28_p38_cpu_v1") # Saves the notebook with outputs to OCI object storage. .with_output("oci://bucket_name@namespace/path/to/dir") ) @@ -125,6 +145,7 @@ def from_datascience_job(job_id) -> "Job": ------- Job A job instance. + """ dsc_infra = DataScienceJob.from_id(job_id) job = ( @@ -204,7 +225,7 @@ def __init__(self, name: str = None, infrastructure=None, runtime=None) -> None: or by calling with_infrastructure() and with_runtime(). The infrastructure should be a subclass of ADS job Infrastructure, e.g., DataScienceJob, DataFlow. - The runtime should be a subclass of ADS job Runtime, e.g., PythonRuntime, ScriptRuntime. + The runtime should be a subclass of ADS job Runtime, e.g., PythonRuntime, NotebookRuntime. Parameters ---------- @@ -219,6 +240,7 @@ def __init__(self, name: str = None, infrastructure=None, runtime=None) -> None: Job infrastructure, by default None runtime : Runtime, optional Job runtime, by default None. + """ super().__init__() if name: @@ -332,6 +354,15 @@ def with_name(self, name: str) -> "Job": """ return self.set_spec("name", name) + def build(self) -> "Job": + """Load default values from the environment for the job infrastructure.""" + build_method = getattr(self.infrastructure, "build", None) + if build_method and callable(build_method): + build_method() + else: + raise NotImplementedError + return self + def create(self, **kwargs) -> "Job": """Creates the job on the infrastructure. @@ -378,6 +409,18 @@ def run( ------- Job Run Instance A job run instance, depending on the infrastructure. + + Examples + -------- + To run a job and override the configurations:: + + job_run = job.run( + name="", + args="new_arg --new_key new_val", + env_var={"new_env": "new_val"}, + freeform_tags={"new_tag": "new_tag_val"} + ) + """ return self.infrastructure.run( name=name, diff --git a/ads/jobs/builders/base.py b/ads/jobs/builders/base.py index e03a1ad31..eb8564c5f 100644 --- a/ads/jobs/builders/base.py +++ b/ads/jobs/builders/base.py @@ -1,21 +1,27 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -from typing import Any, Dict - +import copy +from typing import Any, Dict, TypeVar, Type from ads.jobs.serializer import Serializable +Self = TypeVar("Self", bound="Builder") +"""Special type to represent the current enclosed class. + +This type is used by factory class method or when a method returns ``self``. +""" + + class Builder(Serializable): attribute_map = {} def __init__(self, spec: Dict = None, **kwargs) -> None: - """Initialize the object with specifications. - - User can either pass in the specification as a dictionary or through keyword arguments. + """To initialize the object, + user can either pass in the specification as a dictionary or through keyword arguments. Parameters ---------- @@ -42,7 +48,7 @@ def _load_default_properties(self): """ return {} - def _standardize_spec(self, spec): + def _standardize_spec(self, spec) -> dict: if not spec: return {} snake_to_camel_map = {v: k for k, v in self.attribute_map.items()} @@ -51,7 +57,7 @@ def _standardize_spec(self, spec): spec[snake_to_camel_map[key]] = spec.pop(key) return spec - def set_spec(self, k: str, v: Any): + def set_spec(self: Self, k: str, v: Any) -> Self: """Sets a specification property for the object. Parameters @@ -63,7 +69,7 @@ def set_spec(self, k: str, v: Any): Returns ------- - Builder + Self This method returns self to support chaining methods. """ if v is not None: @@ -107,14 +113,23 @@ def type(self) -> str: def to_dict(self) -> dict: """Converts the object to dictionary with kind, type and spec as keys.""" + spec = copy.deepcopy(self._spec) + for key, value in spec.items(): + if hasattr(value, "to_dict"): + value = value.to_dict() + spec[key] = value + return { "kind": self.kind, "type": self.type, - # "apiVersion": self.api_version, - "spec": self._spec, + "spec": spec, } @classmethod - def from_dict(cls, obj_dict: dict): + def from_dict(cls: Type[Self], obj_dict: dict) -> Self: """Initialize the object from a Python dictionary""" return cls(spec=obj_dict.get("spec")) + + def __repr__(self) -> str: + """Displays the object as YAML.""" + return self.to_yaml() diff --git a/ads/jobs/builders/infrastructure/base.py b/ads/jobs/builders/infrastructure/base.py index 057e74687..ae84a484b 100644 --- a/ads/jobs/builders/infrastructure/base.py +++ b/ads/jobs/builders/infrastructure/base.py @@ -1,10 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +from ads.common import utils as common_utils from ads.jobs.builders.base import Builder from ads.jobs.builders.runtimes.base import Runtime +import logging + +logger = logging.getLogger(__name__) class Infrastructure(Builder): @@ -106,6 +111,8 @@ def list_jobs(self, **kwargs) -> list: class RunInstance: + _DETAILS_LINK = "" + def create(self): """Create a RunInstance Object.""" raise NotImplementedError() @@ -120,5 +127,34 @@ def watch(self): raise NotImplementedError() def delete(self): - """Delete or cancel a run.""" + """Delete a run.""" raise NotImplementedError() + + def cancel(self): + """Cancel a run.""" + raise NotImplementedError() + + @property + def run_details_link(self) -> str: + """ + Link to run details page in OCI console + + Returns + ------- + str + The link to the details page in OCI console. + """ + if not self._DETAILS_LINK: + return "" + + try: + return self._DETAILS_LINK.format( + region=common_utils.extract_region(), id=self.id + ) + except Exception as ex: + print(str(ex)) + logger.info( + "Error occurred in attempt to extract the link to the resource. " + f"Details: {ex}" + ) + return "" diff --git a/ads/jobs/builders/infrastructure/dataflow.py b/ads/jobs/builders/infrastructure/dataflow.py index 65f63a89b..2fef81957 100644 --- a/ads/jobs/builders/infrastructure/dataflow.py +++ b/ads/jobs/builders/infrastructure/dataflow.py @@ -1,8 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + import copy import datetime import io @@ -15,33 +16,63 @@ import fsspec import oci.data_flow +import oci.util as oci_util import yaml from ads.common import utils from ads.common.auth import default_signer +from ads.common.decorator.utils import class_or_instance_method from ads.common.oci_client import OCIClientFactory from ads.common.oci_mixin import OCIModelMixin -from ads.common.utils import camel_to_snake, batch_convert_case -from ads.config import OCI_REGION_METADATA +from ads.common.utils import batch_convert_case, camel_to_snake from ads.jobs.builders.infrastructure.base import Infrastructure, RunInstance from ads.jobs.builders.infrastructure.utils import normalize_config from ads.jobs.builders.runtimes.python_runtime import DataFlowRuntime from ads.model.runtime.env_info import InferenceEnvInfo from oci.data_flow.models import CreateApplicationDetails, CreateRunDetails -import oci.util as oci_util from tqdm import tqdm logger = logging.getLogger(__name__) CONDA_PACK_SUFFIX = "#conda" +SLEEP_INTERVAL = 3 def conda_pack_name_to_dataflow_config(conda_uri): return { - "spark.archives": conda_uri + CONDA_PACK_SUFFIX, # .replace(" ", "%20") + "spark.archives": conda_uri + CONDA_PACK_SUFFIX, "dataflow.auth": "resource_principal", } +def _env_variables_to_dataflow_config( + env_vars: Dict[str, str] = None +) -> Dict[str, str]: + """Prepares environment variables for the application. + Similar environment variables will be setup for the driver and executor. + + Parameters + ---------- + env_vars: (Dict[str, str], optional). Defaults to `None` + The dictionary with SRC env variables. + Example: {"env1": "value1"} + + Returns + ------- + Dict[str, str] + Dictionary with pre populated environment variables. + Example: {"spark.executorEnv.env1": "value1", "spark.driverEnv.env1": "value1"} + """ + if not env_vars: + return {} + + result = {} + for level in ("spark.executorEnv", "spark.driverEnv"): + for env_name, env_value in env_vars.items(): + result[f"{level}.{env_name}"] = env_value + + return result + + class DataFlowApp(OCIModelMixin, oci.data_flow.models.Application): @classmethod def init_client(cls, **kwargs) -> oci.data_flow.data_flow_client.DataFlowClient: @@ -98,6 +129,9 @@ def to_yaml(self) -> str: class DataFlowRun(OCIModelMixin, oci.data_flow.models.Run, RunInstance): + _DETAILS_LINK = ( + "https://console.{region}.oraclecloud.com/data-flow/runs/details/{id}" + ) TERMINATED_STATES = [ oci.data_flow.models.Run.LIFECYCLE_STATE_CANCELED, @@ -162,7 +196,7 @@ def logs(self) -> "DataFlowLogs": """ return DataFlowLogs(run_id=self.id) - def wait(self, interval: int = 3) -> "DataFlowRun": + def wait(self, interval: int = SLEEP_INTERVAL) -> "DataFlowRun": """ Wait for a run to terminate. @@ -189,7 +223,7 @@ def wait(self, interval: int = 3) -> "DataFlowRun": current = self.status return self - def watch(self, interval: int = 3) -> "DataFlowRun": + def watch(self, interval: int = SLEEP_INTERVAL) -> "DataFlowRun": """This is an alias of `wait()` method. It waits for a run to terminate. Parameters @@ -207,10 +241,6 @@ def watch(self, interval: int = 3) -> "DataFlowRun": ) return self.wait(interval=interval) - def __repr__(self) -> str: - """Displays the object as YAML.""" - return self.to_yaml() - def to_yaml(self) -> str: """Serializes the object into YAML string. @@ -223,36 +253,34 @@ def to_yaml(self) -> str: run["lifecycleState"] = self.status return yaml.safe_dump(run) - def delete(self) -> None: + def delete(self) -> "DataFlowRun": """ - Cancel a Data Flow run if it is not yet terminated. + Cancel and delete a Data Flow run if it is not yet terminated. + Will be executed asynchronously. Returns ------- - None + self + The dataflow run instance. """ if self.status not in self.TERMINATED_STATES: self.client.delete_run(self.id) self.lifecycle_state = oci.data_flow.models.Run.LIFECYCLE_STATE_CANCELING - @property - def run_details_link(self): - """ - Link to run details page in OCI console + return self + + def cancel(self) -> "DataFlowRun": + """Cancel a Data Flow run if it is not yet terminated. + Will be executed synchronously. Returns ------- - DisplayHandle - html display + self + The dataflow run instance. """ - signer = default_signer() - if "region" in signer["config"]: - region = signer["config"]["region"] - else: - region = json.loads(OCI_REGION_METADATA)["regionIdentifier"] - return ( - f"https://console.{region}.oraclecloud.com/data-flow/runs/details/{self.id}" - ) + self.delete() + self.wait() + return self class _Log: @@ -349,6 +377,7 @@ class DataFlow(Infrastructure): CONST_MEMORY_IN_GBS = "memory_in_gbs" CONST_OCPUS = "ocpus" CONST_ID = "id" + CONST_PRIVATE_ENDPOINT_ID = "private_endpoint_id" attribute_map = { CONST_COMPARTMENT_ID: "compartmentId", @@ -366,6 +395,7 @@ class DataFlow(Infrastructure): CONST_MEMORY_IN_GBS: "memoryInGBs", CONST_OCPUS: CONST_OCPUS, CONST_ID: CONST_ID, + CONST_PRIVATE_ENDPOINT_ID: "privateEndpointId", } def __init__(self, spec: dict = None, **kwargs): @@ -381,7 +411,8 @@ def __init__(self, spec: dict = None, **kwargs): if f"with_{camel_to_snake(k)}" in self.__dir__() and v is not None } defaults.update(spec) - super(DataFlow, self).__init__(defaults, **kwargs) + super().__init__(defaults, **kwargs) + self.df_app = DataFlowApp(**self._spec) self.runtime = None self._name = None @@ -719,6 +750,22 @@ def with_executor_shape_config( }, ) + def with_private_endpoint_id(self, private_endpoint_id: str) -> "DataFlow": + """ + Set the private endpoint ID for a Data Flow job infrastructure. + + Parameters + ---------- + private_endpoint_id: str + The OCID of a private endpoint. + + Returns + ------- + DataFlow + the Data Flow instance itself + """ + return self.set_spec(self.CONST_PRIVATE_ENDPOINT_ID, private_endpoint_id) + def __getattr__(self, item): if f"with_{item}" in self.__dir__(): return self.get_spec(item) @@ -787,6 +834,7 @@ def create(self, runtime: DataFlowRuntime, **kwargs) -> "DataFlow": runtime_config = runtime.configuration or dict() runtime_config.update(conda_pack_name_to_dataflow_config(conda_uri)) runtime.with_configuration(runtime_config) + payload.update( { "display_name": self.name, @@ -897,8 +945,7 @@ def run( logger.debug(f"Creating a DataFlow Run with payload {payload}") run = DataFlowRun(**payload).create() if wait: - interval = kwargs["interval"] if "interval" in kwargs else 3 - run.wait(interval) + run.wait(kwargs.pop("interval", SLEEP_INTERVAL)) return run def run_list(self, **kwargs) -> List[DataFlowRun]: @@ -996,10 +1043,11 @@ def to_dict(self) -> dict: dict serialized job as a dictionary """ + spec = self._convert_shape_config(copy.deepcopy(self._spec), "camel") return { "kind": self.kind, "type": self.type, - "spec": batch_convert_case(self._spec, "camel"), + "spec": batch_convert_case(spec, "camel"), } @classmethod @@ -1017,11 +1065,53 @@ def from_dict(cls, config: dict) -> "DataFlow": DataFlow a Data Flow job instance """ - return cls(spec=batch_convert_case(config["spec"], "snake")) + spec = cls._convert_shape_config(copy.deepcopy(config["spec"]), "snake") + return cls(spec=batch_convert_case(spec, "snake")) - def __repr__(self) -> str: - """Displays the object as YAML.""" - return self.to_yaml() + @class_or_instance_method + def _convert_shape_config(cls, spec: Dict, to_format: str) -> Dict: + """Converts the format of shape config details from camel to snake, or vice versa. + + Parameters + ---------- + spec: dict + dictionary of specs + to_format: str + the format that's converted to + + Returns + ------- + Dict + dictionary with converted shape config details + """ + shape_config_map = [ + cls.CONST_DRIVER_SHAPE_CONFIG, + cls.CONST_EXECUTOR_SHAPE_CONFIG, + cls.attribute_map[cls.CONST_DRIVER_SHAPE_CONFIG], + cls.attribute_map[cls.CONST_EXECUTOR_SHAPE_CONFIG], + ] + converted_map = { + "camel": { + cls.CONST_MEMORY_IN_GBS: cls.attribute_map[cls.CONST_MEMORY_IN_GBS], + cls.CONST_OCPUS: cls.CONST_OCPUS, + }, + "snake": { + cls.attribute_map[cls.CONST_MEMORY_IN_GBS]: cls.CONST_MEMORY_IN_GBS, + cls.CONST_OCPUS: cls.CONST_OCPUS, + }, + } + for shape_config in shape_config_map: + shape_config_value = spec.pop(shape_config, {}) + if shape_config_value: + temp_maps = {} + for key, value in shape_config_value.items(): + converted_key = converted_map[to_format].get(key, None) + if converted_key: + temp_maps[converted_key] = value + else: + temp_maps[key] = value + spec[shape_config] = copy.deepcopy(temp_maps) + return spec def to_yaml(self) -> str: """Serializes the object into YAML string. diff --git a/ads/jobs/builders/infrastructure/dsc_job.py b/ads/jobs/builders/infrastructure/dsc_job.py index e156dd6d2..44f29e906 100644 --- a/ads/jobs/builders/infrastructure/dsc_job.py +++ b/ads/jobs/builders/infrastructure/dsc_job.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from __future__ import annotations @@ -9,14 +9,18 @@ import logging import os import time +import traceback import uuid from io import DEFAULT_BUFFER_SIZE +from string import Template from typing import Any, Dict, List, Optional, Union import fsspec import oci.data_science import oci.util as oci_util import yaml +from oci.data_science.models import JobInfrastructureConfigurationDetails +from oci.exceptions import ServiceError from ads.common import utils from ads.common.oci_datascience import DSCNotebookSession, OCIDataScienceMixin from ads.common.oci_logging import OCILog @@ -29,11 +33,12 @@ from ads.jobs.builders.runtimes.artifact import Artifact from ads.jobs.builders.runtimes.container_runtime import ContainerRuntime from ads.jobs.builders.runtimes.python_runtime import GitPythonRuntime -from oci.data_science.models import JobInfrastructureConfigurationDetails -from oci.exceptions import ServiceError logger = logging.getLogger(__name__) +SLEEP_INTERVAL = 3 +WAIT_SECONDS_AFTER_FINISHED = 90 + class DSCJob(OCIDataScienceMixin, oci.data_science.models.Job): """Represents an OCI Data Science Job @@ -88,6 +93,8 @@ class DSCJob(OCIDataScienceMixin, oci.data_science.models.Job): JobInfrastructureConfigurationDetails.JOB_INFRASTRUCTURE_TYPE_ME_STANDALONE ) + CONST_DEFAULT_BLOCK_STORAGE_SIZE = 50 + def __init__(self, artifact: Union[str, Artifact] = None, **kwargs) -> None: """Initialize a DSCJob object. @@ -108,10 +115,7 @@ def __init__(self, artifact: Union[str, Artifact] = None, **kwargs) -> None: "jobType": "DEFAULT", } if not self.job_infrastructure_configuration_details: - self.job_infrastructure_configuration_details = { - "jobInfrastructureType": self.DEFAULT_INFRA_TYPE, - "blockStorageSize": 50, - } + self.job_infrastructure_configuration_details = {} @property def artifact(self) -> Union[str, Artifact]: @@ -151,11 +155,17 @@ def _load_infra_from_notebook(self, nb_config): if shape_name != nb_config.shape: nb_shape_config_details = {} - infra_type = infra.get("jobInfrastructureType", self.DEFAULT_INFRA_TYPE) + infra_type = infra.get("jobInfrastructureType") block_storage = infra.get( "blockStorageSizeInGBs", nb_config.block_storage_size_in_gbs ) - subnet_id = infra.get("subnetId", nb_config.subnet_id) + subnet_id = infra.get( + "subnetId", + nb_config.subnet_id + if infra_type + != JobInfrastructureConfigurationDetails.JOB_INFRASTRUCTURE_TYPE_ME_STANDALONE + else None, + ) job_shape_config_details = infra.get("jobShapeConfigDetails", {}) memory_in_gbs = job_shape_config_details.get( "memoryInGBs", nb_shape_config_details.get("memory_in_gbs") @@ -173,11 +183,8 @@ def _load_infra_from_notebook(self, nb_config): if shape_name != nb_config.shape: nb_shape_config_details = {} - infra_type = ( - infra.job_infrastructure_type - if getattr(infra, "job_infrastructure_type", None) - else self.DEFAULT_INFRA_TYPE - ) + infra_type = getattr(infra, "job_infrastructure_type", None) + block_storage = ( infra.block_storage_size_in_gbs if getattr(infra, "block_storage_size_in_gbs", None) @@ -186,7 +193,12 @@ def _load_infra_from_notebook(self, nb_config): subnet_id = ( infra.subnet_id if getattr(infra, "subnet_id", None) - else nb_config.subnet_id + else ( + nb_config.subnet_id + if infra_type + != JobInfrastructureConfigurationDetails.JOB_INFRASTRUCTURE_TYPE_ME_STANDALONE + else None + ) ) job_shape_config_details = oci_util.to_dict( getattr(infra, "job_shape_config_details", {}) or {} @@ -213,6 +225,12 @@ def _load_infra_from_notebook(self, nb_config): "jobInfrastructureType": JobInfrastructureConfigurationDetails.JOB_INFRASTRUCTURE_TYPE_STANDALONE, } ) + else: + self.job_infrastructure_configuration_details.update( + { + "jobInfrastructureType": self.DEFAULT_INFRA_TYPE, + } + ) # Specify shape config details if memory_in_gbs or ocpus: @@ -230,10 +248,12 @@ def load_properties_from_env(self) -> None: if "NB_SESSION_OCID" in os.environ: try: nb_session = DSCNotebookSession.from_ocid(os.environ["NB_SESSION_OCID"]) - except: + except Exception: + logger.debug("Failed to load config from notebook.") + logger.debug(traceback.format_exc()) # If there is an error loading the notebook infra configurations. # Ignore it by setting nb_session to None - # This will skip loadding the default configure. + # This will skip loading the default configure. nb_session = None if nb_session: nb_config = getattr( @@ -248,6 +268,31 @@ def load_properties_from_env(self) -> None: self.project_id = nb_session.project_id super().load_properties_from_env() + def load_defaults(self) -> DSCJob: + self.load_properties_from_env() + if not self.job_infrastructure_configuration_details: + self.job_infrastructure_configuration_details = {} + # Convert the dict to JobInfrastructureConfigurationDetails object + if isinstance(self.job_infrastructure_configuration_details, dict): + # Default networking + if not self.job_infrastructure_configuration_details.get( + "jobInfrastructureType" + ): + self.job_infrastructure_configuration_details[ + "jobInfrastructureType" + ] = self.DEFAULT_INFRA_TYPE + self.job_infrastructure_configuration_details = self.deserialize( + self.job_infrastructure_configuration_details, + JobInfrastructureConfigurationDetails.__name__, + ) + + # Default block storage size + if not self.job_infrastructure_configuration_details.block_storage_size_in_gbs: + self.job_infrastructure_configuration_details.block_storage_size_in_gbs = ( + self.CONST_DEFAULT_BLOCK_STORAGE_SIZE + ) + return self + def _create_with_oci_api(self) -> None: res = self.client.create_job( self.to_oci_model(oci.data_science.models.CreateJobDetails) @@ -285,11 +330,9 @@ def create(self) -> DSCJob: # Set default display_name if not specified - randomly generated easy to remember name generated self.display_name = utils.get_random_name_for_resource() try: - self.load_properties_from_env() - except Exception as ex: - logger.error( - "Failed to load default properties from environment: %s", str(ex) - ) + self.load_defaults() + except Exception: + logger.exception("Failed to load default properties.") # Check compartment ID and project ID before calling the OCI API if not self.compartment_id: raise ValueError("Specify compartment ID for data science job.") @@ -471,6 +514,10 @@ class DataScienceJobRun( ): """Represents a Data Science Job run""" + _DETAILS_LINK = ( + "https://console.{region}.oraclecloud.com/data-science/job-runs/{id}" + ) + TERMINAL_STATES = [ oci.data_science.models.JobRun.LIFECYCLE_STATE_SUCCEEDED, oci.data_science.models.JobRun.LIFECYCLE_STATE_FAILED, @@ -587,22 +634,32 @@ def _check_and_print_status(self, prev_status) -> str: status = self._job_run_status_text() if status != prev_status: if self.lifecycle_state in self.TERMINAL_STATES and self.time_finished: - timestamp = self.time_finished + timestamp = self.time_finished.strftime("%Y-%m-%d %H:%M:%S") else: timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") print(f"{timestamp} - {status}") return status - def watch(self, interval: float = 3) -> DataScienceJobRun: + def watch( + self, + interval: float = SLEEP_INTERVAL, + wait: float = WAIT_SECONDS_AFTER_FINISHED, + ) -> DataScienceJobRun: """Watches the job run until it finishes. Before the job start running, this method will output the job run status. - Once the job start running, the logs will be streamed until the job is success, failed or cancelled. + Once the job start running, + the logs will be streamed until the job is success, failed or cancelled. Parameters ---------- - interval : int + interval : float Time interval in seconds between each request to update the logs. Defaults to 3 (seconds). + wait : float + Time in seconds to keep updating the logs after the job run finished. + It may take some time for logs to appear in OCI logging service + after the job run is finished. + Defaults to 90 (seconds). """ @@ -614,11 +671,11 @@ def stop_condition(): # Stop if time_finished is not available. if not self.time_finished: return True - # Stop only if time_finished is over 1 minute ago. + # Stop only if time_finished is over 2 minute ago. # This is for the time delay between job run stopped and the logs appear in oci logging. if ( datetime.datetime.now(self.time_finished.tzinfo) - - datetime.timedelta(minutes=1) + - datetime.timedelta(seconds=wait) > self.time_finished ): return True @@ -669,7 +726,7 @@ def cancel(self) -> DataScienceJobRun: self.client.cancel_job_run(self.id) while self.lifecycle_state != "CANCELED": self.sync() - time.sleep(3) + time.sleep(SLEEP_INTERVAL) return self def __repr__(self) -> str: @@ -684,7 +741,41 @@ def to_yaml(self) -> str: str YAML stored in a string. """ - return yaml.safe_dump(self.to_dict()) + # Here the job YAML is used as the base for the job run + job_dict = self.job.to_dict() + + # Update infrastructure from job run + run_dict = self.to_dict() + infra_specs = [ + run_dict, + run_dict.get("jobInfrastructureConfigurationDetails", {}), + run_dict.get("logDetails", {}), + ] + for infra_spec in infra_specs: + for key in infra_spec: + if key in job_dict["spec"]["infrastructure"]["spec"]: + job_dict["spec"]["infrastructure"]["spec"][key] = infra_spec[key] + + # Update runtime from job run + from ads.jobs import Job + + job = Job.from_dict(job_dict) + envs = job.runtime.envs + run_config_override = run_dict.get("jobConfigurationOverrideDetails", {}) + envs.update(run_config_override.get("environmentVariables", {})) + job.runtime.with_environment_variable(**envs) + if run_config_override.get("commandLineArguments"): + job.runtime.set_spec( + "args", + run_config_override.get("commandLineArguments"), + ) + + # Update kind, id and name + run_dict = job.to_dict() + run_dict["kind"] = "jobRun" + run_dict["spec"]["id"] = self.id + run_dict["spec"]["name"] = self.display_name + return yaml.safe_dump(run_dict) @property def job(self): @@ -721,7 +812,30 @@ def download(self, to_dir): class DataScienceJob(Infrastructure): - """Represents the OCI Data Science Job infrastructure.""" + """Represents the OCI Data Science Job infrastructure. + + To configure the infrastructure for a Data Science Job:: + + infrastructure = ( + DataScienceJob() + # Configure logging for getting the job run outputs. + .with_log_group_id("") + # Log resource will be auto-generated if log ID is not specified. + .with_log_id("") + # If you are in an OCI data science notebook session, + # the following configurations are not required. + # Configurations from the notebook session will be used as defaults. + .with_compartment_id("") + .with_project_id("") + .with_subnet_id("") + .with_shape_name("VM.Standard.E3.Flex") + # Shape config details are applicable only for the flexible shapes. + .with_shape_config_details(memory_in_gbs=16, ocpus=1) + # Minimum/Default block storage size is 50 (GB). + .with_block_storage_size(50) + ) + + """ CONST_PROJECT_ID = "projectId" CONST_COMPARTMENT_ID = "compartmentId" @@ -812,8 +926,6 @@ def __init__(self, spec: Dict = None, **kwargs) -> None: super().__init__(spec=spec, **kwargs) if not self.job_type: self.with_job_type("DEFAULT") - if not self.job_infrastructure_type: - self.with_job_infrastructure_type(DSCJob.DEFAULT_INFRA_TYPE) self.dsc_job = DSCJob() self.runtime = None self._name = None @@ -1107,12 +1219,12 @@ def _prepare_log_config(self) -> dict: if self.log_id and not self.log_group_id: try: log_obj = OCILog.from_ocid(self.log_id) - except ResourceNotFoundError: + except ResourceNotFoundError as exc: raise ResourceNotFoundError( f"Unable to determine log group ID for Log ({self.log_id})." " The log resource may not exist or You may not have the required permission." " Try to avoid this by specifying the log group ID." - ) + ) from exc self.with_log_group_id(log_obj.log_group_id) if self.log_group_id and not self.log_id: @@ -1132,7 +1244,7 @@ def _prepare_log_config(self) -> dict: return log_config def _update_from_dsc_model( - self, dsc_job: oci.data_science.models.Job + self, dsc_job: oci.data_science.models.Job, overwrite: bool = True ) -> DataScienceJob: """Update the properties from an OCI data science job model. @@ -1141,6 +1253,10 @@ def _update_from_dsc_model( dsc_job: oci.data_science.models.Job An OCI data science job model. + overwrite: bool + Whether to overwrite the existing values. + If this is set to False, only the empty/None properties will be updated. + Returns ------- DataScienceJob @@ -1153,15 +1269,22 @@ def _update_from_dsc_model( for infra_attr, dsc_attr in self.payload_attribute_map.items(): value = get_value(dsc_job, dsc_attr) - if value: - if infra_attr not in sub_level: + if not value: + continue + if infra_attr not in sub_level: + if overwrite or not self._spec.get(infra_attr): self._spec[infra_attr] = value - else: - self._spec[infra_attr] = {} - for sub_infra_attr, sub_dsc_attr in sub_level[infra_attr].items(): - sub_value = get_value(value, sub_dsc_attr) - if sub_value: - self._spec[infra_attr][sub_infra_attr] = sub_value + else: + sub_spec = self._spec.get(infra_attr, {}) + self._spec[infra_attr] = {} + for sub_infra_attr, sub_dsc_attr in sub_level[infra_attr].items(): + sub_value = get_value(value, sub_dsc_attr) + if not sub_value: + continue + if overwrite or not sub_spec.get(sub_infra_attr): + sub_spec[sub_infra_attr] = sub_value + if sub_spec: + self._spec[infra_attr] = sub_spec return self def _update_job_infra(self, dsc_job: DSCJob) -> DataScienceJob: @@ -1187,10 +1310,7 @@ def _update_job_infra(self, dsc_job: DSCJob) -> DataScienceJob: } if not dsc_job.job_infrastructure_configuration_details: - dsc_job.job_infrastructure_configuration_details = { - self.CONST_JOB_INFRA: DSCJob.DEFAULT_INFRA_TYPE, - self.CONST_BLOCK_STORAGE: 50, - } + dsc_job.job_infrastructure_configuration_details = {} for snake_attr, camel_attr in attr_map.items(): value = self.get_spec(snake_attr) @@ -1203,6 +1323,11 @@ def _update_job_infra(self, dsc_job: DSCJob) -> DataScienceJob: ] = JobInfrastructureConfigurationDetails.JOB_INFRASTRUCTURE_TYPE_STANDALONE return self + def build(self) -> DataScienceJob: + self.dsc_job.load_defaults() + self._update_from_dsc_model(self.dsc_job, overwrite=False) + return self + def create(self, runtime, **kwargs) -> DataScienceJob: """Creates a job with runtime. @@ -1224,13 +1349,17 @@ def create(self, runtime, **kwargs) -> DataScienceJob: for attr in ["project_id", "compartment_id"]: if getattr(self, attr): payload[attr] = getattr(self, attr) - if isinstance(runtime, GitPythonRuntime) or isinstance( + + if self.name: + display_name = Template(self.name).safe_substitute(runtime.envs) + elif isinstance(runtime, GitPythonRuntime) or isinstance( runtime, ContainerRuntime ): - payload["display_name"] = self.name or utils.get_random_name_for_resource() + display_name = utils.get_random_name_for_resource() else: - payload["display_name"] = self.name + display_name = None + payload["display_name"] = display_name payload["job_log_configuration_details"] = self._prepare_log_config() self.dsc_job = DSCJob(**payload) @@ -1261,7 +1390,7 @@ def run( Returns ------- - DSCJobRun + DataScienceJobRun A Data Science Job Run instance. """ @@ -1276,6 +1405,12 @@ def run( if freeform_tags: tags.update(freeform_tags) + if name: + envs = self.runtime.envs + if env_var: + envs.update(env_var) + name = Template(name).safe_substitute(envs) + return self.dsc_job.run( display_name=name, command_line_arguments=args, @@ -1369,7 +1504,7 @@ def list_jobs(cls, compartment_id: str = None, **kwargs) -> List[DataScienceJob] ] @classmethod - def instance_shapes(cls, compartment_id: str = None) -> list: + def instance_shapes(cls, compartment_id: str = None, **kwargs) -> list: """Lists the supported shapes for running jobs in a compartment. Parameters @@ -1382,10 +1517,56 @@ def instance_shapes(cls, compartment_id: str = None) -> list: Returns ------- list - A list of dictionaries containing the information of the supported shapes. + A list of oci.data_science.models.JobShapeSummary objects + containing the information of the supported shapes. + + Examples + -------- + To get a list of shape names:: + + shapes = DataScienceJob.fast_launch_shapes( + compartment_id=os.environ["PROJECT_COMPARTMENT_OCID"] + ) + shape_names = [shape.name for shape in shapes] + """ shapes = oci.pagination.list_call_get_all_results( DSCJob.init_client().list_job_shapes, DSCJob.check_compartment_id(compartment_id), + **kwargs, + ).data + return shapes + + @classmethod + def fast_launch_shapes(cls, compartment_id: str = None, **kwargs) -> list: + """Lists the supported fast launch shapes for running jobs in a compartment. + + Parameters + ---------- + compartment_id : str, optional + The compartment ID for running the jobs, by default None. + This is optional in a OCI Data Science notebook session. + If this is not specified, the compartment ID of the notebook session will be used. + + Returns + ------- + list + A list of oci.data_science.models.FastLaunchJobConfigSummary objects + containing the information of the supported shapes. + + Examples + -------- + To get a list of shape names:: + + shapes = DataScienceJob.fast_launch_shapes( + compartment_id=os.environ["PROJECT_COMPARTMENT_OCID"] + ) + shape_names = [shape.shape_name for shape in shapes] + + """ + shapes = oci.pagination.list_call_get_all_results( + DSCJob.init_client().list_fast_launch_job_configs, + DSCJob.check_compartment_id(compartment_id), + **kwargs, ).data return shapes diff --git a/ads/jobs/builders/infrastructure/dsc_job_runtime.py b/ads/jobs/builders/infrastructure/dsc_job_runtime.py index c257d556f..26e233bb4 100644 --- a/ads/jobs/builders/infrastructure/dsc_job_runtime.py +++ b/ads/jobs/builders/infrastructure/dsc_job_runtime.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """Contains classes for conversion between ADS runtime and OCI Data Science Job implementation. This module is for ADS developers only. @@ -19,6 +19,7 @@ import shlex from typing import Optional from urllib import parse +from ads.common.utils import extract_region from ads.jobs.builders.runtimes.base import Runtime from ads.jobs.builders.runtimes.python_runtime import ( CondaRuntime, @@ -32,6 +33,7 @@ ScriptArtifact, NotebookArtifact, PythonArtifact, + GitPythonArtifact, ) from ads.jobs.builders.infrastructure.utils import get_value @@ -409,15 +411,7 @@ class CondaRuntimeHandler(RuntimeHandler): CONST_CONDA_BUCKET = "CONDA_ENV_BUCKET" def __get_auth_region(self) -> str: - config = self.data_science_job.dsc_job.auth.get("config") - signer = self.data_science_job.dsc_job.auth.get("signer") - if "region" in config and config["region"]: - region_to_use = config.get("region") - elif hasattr(signer, "region"): - region_to_use = signer.region - else: - region_to_use = None - return region_to_use + return extract_region(self.data_science_job.dsc_job.auth) def _translate_env(self, runtime: CondaRuntime) -> dict: """Translate the environment variable. @@ -430,36 +424,38 @@ def _translate_env(self, runtime: CondaRuntime) -> dict: Returns ------- dict - A dictionary contianing environment variables for OCI data science job. + A dictionary containing environment variables for OCI data science job. """ envs = super()._translate_env(runtime) if runtime.conda: envs[self.CONST_CONDA_TYPE] = runtime.conda.get( - PythonRuntime.CONST_CONDA_TYPE + CondaRuntime.CONST_CONDA_TYPE ) if ( - runtime.conda.get(PythonRuntime.CONST_CONDA_TYPE) - == PythonRuntime.CONST_CONDA_TYPE_SERVICE + runtime.conda.get(CondaRuntime.CONST_CONDA_TYPE) + == CondaRuntime.CONST_CONDA_TYPE_SERVICE ): envs.update( { self.CONST_CONDA_SLUG: runtime.conda.get( - PythonRuntime.CONST_CONDA_SLUG + CondaRuntime.CONST_CONDA_SLUG ), } ) elif ( - runtime.conda.get(PythonRuntime.CONST_CONDA_TYPE) - == PythonRuntime.CONST_CONDA_TYPE_CUSTOM + runtime.conda.get(CondaRuntime.CONST_CONDA_TYPE) + == CondaRuntime.CONST_CONDA_TYPE_CUSTOM ): - uri = runtime.conda.get(PythonRuntime.CONST_CONDA_URI) + uri = runtime.conda.get(CondaRuntime.CONST_CONDA_URI) p = parse.urlparse(uri) if not (p.username and p.hostname and p.path): raise ValueError( f"Invalid URI for custom conda pack: {uri}. " "A valid URI should have the format: oci://your_bucket@namespace/object_name" ) - region = self.__get_auth_region() + region = runtime.conda.get(CondaRuntime.CONST_CONDA_REGION) + if not region: + region = self.__get_auth_region() if not region: raise AttributeError( "Unable to determine the region for the custom conda pack. " @@ -499,7 +495,7 @@ def _extract_envs(self, dsc_job) -> dict: return spec @staticmethod - def __extract_conda_env(envs) -> Optional[dict]: + def __extract_conda_env(envs: dict) -> Optional[dict]: """Extracts conda pack specification from environment variables Parameters @@ -514,11 +510,39 @@ def __extract_conda_env(envs) -> Optional[dict]: """ if not envs: return None - if "CONDA_ENV_TYPE" in envs and "CONDA_ENV_SLUG" in envs: + if ( + CondaRuntimeHandler.CONST_CONDA_TYPE in envs + and CondaRuntimeHandler.CONST_CONDA_SLUG in envs + ): return { - "type": envs.pop("CONDA_ENV_TYPE"), - "slug": envs.pop("CONDA_ENV_SLUG"), + CondaRuntime.CONST_CONDA_TYPE: envs.pop( + CondaRuntimeHandler.CONST_CONDA_TYPE + ), + CondaRuntime.CONST_CONDA_SLUG: envs.pop( + CondaRuntimeHandler.CONST_CONDA_SLUG + ), + } + if ( + envs.get(CondaRuntimeHandler.CONST_CONDA_TYPE) + == CondaRuntime.CONST_CONDA_TYPE_CUSTOM + and CondaRuntimeHandler.CONST_CONDA_BUCKET in envs + and CondaRuntimeHandler.CONST_CONDA_BUCKET in envs + and CondaRuntimeHandler.CONST_CONDA_OBJ_NAME in envs + ): + bucket = envs.pop(CondaRuntimeHandler.CONST_CONDA_BUCKET) + namespace = envs.pop(CondaRuntimeHandler.CONST_CONDA_NAMESPACE) + name = envs.pop(CondaRuntimeHandler.CONST_CONDA_OBJ_NAME) + conda_spec = { + CondaRuntime.CONST_CONDA_TYPE: envs.pop( + CondaRuntimeHandler.CONST_CONDA_TYPE + ), + CondaRuntime.CONST_CONDA_URI: f"oci://{bucket}@{namespace}/{name}", } + if CondaRuntimeHandler.CONST_CONDA_REGION in envs: + conda_spec[CondaRuntime.CONST_CONDA_REGION] = envs.pop( + CondaRuntimeHandler.CONST_CONDA_REGION + ) + return conda_spec return None @@ -575,9 +599,6 @@ def _extract_envs(self, dsc_job) -> dict: def _extract_artifact(self, dsc_job): """Extract the job artifact from data science job. - This is the base method which does not extract the job artifact. - Sub-class should implement the extraction if needed. - Parameters ---------- dsc_job : DSCJob or oci.datascience.models.Job @@ -589,7 +610,7 @@ def _extract_artifact(self, dsc_job): A runtime specification dictionary for initializing a runtime. """ spec = super()._extract_artifact(dsc_job) - spec.update({ScriptRuntime.CONST_SCRIPT_PATH: dsc_job.artifact}) + spec.update({ScriptRuntime.CONST_SCRIPT_PATH: str(dsc_job.artifact)}) return spec @@ -673,7 +694,7 @@ class NotebookRuntimeHandler(CondaRuntimeHandler): SPEC_MAPPINGS = { NotebookRuntime.CONST_NOTEBOOK_PATH: CONST_NOTEBOOK_NAME, NotebookRuntime.CONST_OUTPUT_URI: CONST_OUTPUT_URI, - NotebookRuntime.CONST_TAG: CONST_EXCLUDE_TAGS, + NotebookRuntime.CONST_EXCLUDE_TAG: CONST_EXCLUDE_TAGS, NotebookRuntime.CONST_NOTEBOOK_ENCODING: CONST_NOTEBOOK_ENCODING, } @@ -707,8 +728,24 @@ def _extract_envs(self, dsc_job) -> dict: """ spec = super()._extract_envs(dsc_job) envs = spec.pop(NotebookRuntime.CONST_ENV_VAR, {}) - if self.CONST_NOTEBOOK_NAME not in envs: + if not ( + self.CONST_NOTEBOOK_NAME in envs + and NotebookRuntimeHandler.CONST_ENTRYPOINT in envs + ): raise IncompatibleRuntime() + # Remove job run entrypoint since it is the same for notebook runtime. + envs.pop(NotebookRuntimeHandler.CONST_ENTRYPOINT) + # Extract exclude tags + exclude_tags = envs.pop(NotebookRuntimeHandler.CONST_EXCLUDE_TAGS, None) + if exclude_tags: + # Exclude tags are in a JSON serialized string + try: + exclude_tags = json.loads(exclude_tags) + except ValueError: + # Ignore de-serialization error + pass + spec[NotebookRuntime.CONST_EXCLUDE_TAG] = exclude_tags + spec.update(self._extract_specs(envs, self.SPEC_MAPPINGS)) spec[NotebookRuntime.CONST_ENV_VAR] = envs return spec @@ -718,6 +755,7 @@ class GitPythonRuntimeHandler(CondaRuntimeHandler): """Runtime Handler for GitPythonRuntime""" RUNTIME_CLASS = GitPythonRuntime + PATH_DELIMITER = ":" CONST_GIT_URL = "GIT_URL" CONST_GIT_BRANCH = "GIT_BRANCH" @@ -731,6 +769,9 @@ class GitPythonRuntimeHandler(CondaRuntimeHandler): CONST_PYTHON_PATH = "PYTHON_PATH" CONST_OUTPUT_DIR = "OUTPUT_DIR" CONST_OUTPUT_URI = "OUTPUT_URI" + CONST_WORKING_DIR = "WORKING_DIR" + + CONST_JOB_ENTRYPOINT = "JOB_RUN_ENTRYPOINT" SPEC_MAPPINGS = { GitPythonRuntime.CONST_GIT_URL: CONST_GIT_URL, @@ -742,6 +783,7 @@ class GitPythonRuntimeHandler(CondaRuntimeHandler): GitPythonRuntime.CONST_GIT_SSH_SECRET_ID: CONST_GIT_SSH_SECRET_ID, GitPythonRuntime.CONST_OUTPUT_DIR: CONST_OUTPUT_DIR, GitPythonRuntime.CONST_OUTPUT_URI: CONST_OUTPUT_URI, + GitPythonRuntime.CONST_WORKING_DIR: CONST_WORKING_DIR } def _translate_artifact(self, runtime: Runtime): @@ -758,9 +800,7 @@ def _translate_artifact(self, runtime: Runtime): str Path to the git driver script. """ - return os.path.join( - os.path.dirname(__file__), "../../templates", "driver_oci.py" - ) + return GitPythonArtifact() def _translate_env(self, runtime: GitPythonRuntime) -> dict: """Translate the environment variable. @@ -773,7 +813,7 @@ def _translate_env(self, runtime: GitPythonRuntime) -> dict: Returns ------- dict - A dictionary contianing environment variables for OCI data science job. + A dictionary containing environment variables for OCI data science job. """ if not runtime.conda: raise ValueError( @@ -786,6 +826,8 @@ def _translate_env(self, runtime: GitPythonRuntime) -> dict: ) if runtime.skip_metadata_update: envs[self.CONST_SKIP_METADATA] = "1" + # Add entrypoint as the ADS driver is packed in a zip file. + envs[self.CONST_JOB_ENTRYPOINT] = GitPythonArtifact.CONST_DRIVER_SCRIPT return envs def _extract_envs(self, dsc_job) -> dict: @@ -806,6 +848,8 @@ def _extract_envs(self, dsc_job) -> dict: if self.CONST_GIT_URL not in envs or self.CONST_ENTRYPOINT not in envs: raise IncompatibleRuntime() + # Remove entrypoint as it's added by ADS + envs.pop(self.CONST_JOB_ENTRYPOINT, None) spec.update(self._extract_specs(envs, self.SPEC_MAPPINGS)) if GitPythonRuntime.CONST_PYTHON_PATH in spec: spec[GitPythonRuntime.CONST_PYTHON_PATH] = spec[ @@ -859,7 +903,7 @@ def _translate_env(self, runtime: ContainerRuntime) -> dict: Returns ------- dict - A dictionary contianing environment variables for OCI data science job. + A dictionary containing environment variables for OCI data science job. """ if not runtime.image: raise ValueError("Specify container image for ContainerRuntime.") @@ -873,9 +917,29 @@ def _translate_env(self, runtime: ContainerRuntime) -> dict: return envs @staticmethod - def __split_args(args): + def split_args(args: str) -> list: + """Splits the cmd or entrypoint arguments for BYOC job into a list. + BYOC jobs uses environment variables to store the values of cmd and entrypoint. + In the values, comma(,) is used to separate cmd or entrypoint arguments. + In YAML, the arguments are formatted into a list (Exec form). + + >>> ContainerRuntimeHandler.split_args("/bin/bash") + ["/bin/bash"] + >>> ContainerRuntimeHandler.split_args("-c,echo Hello World") + ['-c', 'echo Hello World'] + + Parameters + ---------- + args : str + Arguments in a comma separated string. + + Returns + ------- + list + Arguments in a list + """ if not args: - return None + return [] return [ arg.strip() for arg in args.split(ContainerRuntimeHandler.CMD_DELIMITER) ] @@ -898,10 +962,10 @@ def _extract_envs(self, dsc_job): if self.CONST_CONTAINER_IMAGE not in envs: raise IncompatibleRuntime() spec[ContainerRuntime.CONST_IMAGE] = envs.pop(self.CONST_CONTAINER_IMAGE) - cmd = self.__split_args(envs.pop(self.CONST_CONTAINER_CMD, "")) + cmd = self.split_args(envs.pop(self.CONST_CONTAINER_CMD, "")) if cmd: spec[ContainerRuntime.CONST_CMD] = cmd - entrypoint = self.__split_args(envs.pop(self.CONST_CONTAINER_ENTRYPOINT, "")) + entrypoint = self.split_args(envs.pop(self.CONST_CONTAINER_ENTRYPOINT, "")) if entrypoint: spec[ContainerRuntime.CONST_ENTRYPOINT] = entrypoint if envs: diff --git a/ads/jobs/builders/runtimes/artifact.py b/ads/jobs/builders/runtimes/artifact.py index 9f0e376f4..470159c0c 100644 --- a/ads/jobs/builders/runtimes/artifact.py +++ b/ads/jobs/builders/runtimes/artifact.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import contextlib import logging @@ -36,6 +36,8 @@ class Artifact: """ + CONST_DRIVER_UTILS = "driver_utils.py" + def __init__(self, source, runtime=None) -> None: # Get the full path of source file if it is local file. if not urlparse(source).scheme: @@ -125,7 +127,12 @@ def copy_from_uri(uri, to_path, unpack=False, **storage_options): scheme = urlparse(uri).scheme # temp_dir is used only if the uri is zip/tar file with tempfile.TemporaryDirectory() as temp_dir: - if unpack and (str(uri).endswith("zip") or str(uri).endswith("tar.gz")): + if unpack and ( + str(uri).endswith(".zip") + or str(uri).endswith(".tar.gz") + or str(uri).endswith(".tar") + or str(uri).endswith(".tgz") + ): unpack_path = to_path to_path = temp_dir else: @@ -175,6 +182,9 @@ def build(self): driver_script = os.path.join( os.path.dirname(__file__), "../../templates", self.CONST_DRIVER_SCRIPT ) + driver_utils = os.path.join( + os.path.dirname(__file__), "../../templates", self.CONST_DRIVER_UTILS + ) notebook_path = os.path.join(self.temp_dir.name, os.path.basename(self.source)) output_path = ( os.path.join( @@ -187,6 +197,7 @@ def build(self): with zipfile.ZipFile(output_path, "w") as zip_file: zip_file.write(notebook_path, os.path.basename(notebook_path)) zip_file.write(driver_script, os.path.basename(driver_script)) + zip_file.write(driver_utils, os.path.basename(driver_utils)) self.path = output_path @@ -209,7 +220,9 @@ def build(self): "Please specify entrypoint when script source is a directory." ) output = os.path.join(self.temp_dir.name, basename) - shutil.make_archive(output, "zip", os.path.dirname(source), base_dir=basename) + shutil.make_archive( + output, "zip", os.path.dirname(source), base_dir=basename + ) self.path = output + ".zip" return # Otherwise, use the artifact directly @@ -227,20 +240,23 @@ class PythonArtifact(Artifact): USER_CODE_DIR = "code" def build(self): - """Prepares job artifact for script runtime. - If the source is a file, it will be returned as is. - If the source is a directory, it will be compressed as a zip file. - """ - driver_script = os.path.join( - os.path.dirname(__file__), "../../templates", self.CONST_DRIVER_SCRIPT - ) + """Prepares job artifact for PythonRuntime.""" basename = os.path.basename(str(self.source).rstrip("/")).split(".", 1)[0] artifact_dir = os.path.join(self.temp_dir.name, basename) code_dir = os.path.join(artifact_dir, self.USER_CODE_DIR) os.makedirs(artifact_dir, exist_ok=True) # Copy the driver script - shutil.copy(driver_script, os.path.join(artifact_dir, self.CONST_DRIVER_SCRIPT)) + for filename in [ + self.CONST_DRIVER_UTILS, + self.CONST_DRIVER_SCRIPT, + NotebookArtifact.CONST_DRIVER_SCRIPT, + ]: + file_path = os.path.join( + os.path.dirname(__file__), "../../templates", filename + ) + + shutil.copy(file_path, os.path.join(artifact_dir, filename)) # Copy user code os.makedirs(code_dir, exist_ok=True) @@ -269,10 +285,38 @@ def build(self): f"Invalid entrypoint. {self.runtime.entrypoint} does not exist." ) if os.path.exists(possible_entrypoint): - err_message += f" Do you mean {os.path.join(self.runtime.working_dir, basename, self.runtime.entrypoint)}?" - raise ValueError(err_message) + suggested_entrypoint = os.path.join(basename, self.runtime.entrypoint) + err_message += f" Do you mean {suggested_entrypoint}?" + logger.warning(err_message) # Zip the job artifact output = os.path.join(self.temp_dir.name, basename) shutil.make_archive(output, "zip", artifact_dir, base_dir="./") self.path = output + ".zip" + + +class GitPythonArtifact(Artifact): + + CONST_DRIVER_SCRIPT = "driver_oci.py" + + def __init__(self) -> None: + super().__init__("", runtime=None) + + def build(self): + """Prepares job artifact for GitPythonRuntime.""" + artifact_dir = os.path.join(self.temp_dir.name, "ads_driver") + os.makedirs(artifact_dir, exist_ok=True) + for filename in [ + self.CONST_DRIVER_UTILS, + self.CONST_DRIVER_SCRIPT, + NotebookArtifact.CONST_DRIVER_SCRIPT, + ]: + file_path = os.path.join( + os.path.dirname(__file__), "../../templates", filename + ) + + shutil.copy(file_path, os.path.join(artifact_dir, filename)) + + # Zip the job artifact + shutil.make_archive(artifact_dir, "zip", artifact_dir, base_dir="./") + self.path = artifact_dir + ".zip" diff --git a/ads/jobs/builders/runtimes/base.py b/ads/jobs/builders/runtimes/base.py index 199655065..75e37568e 100644 --- a/ads/jobs/builders/runtimes/base.py +++ b/ads/jobs/builders/runtimes/base.py @@ -1,14 +1,22 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +from __future__ import annotations import re -from typing import Dict +from typing import Dict, TypeVar from ads.jobs.builders.base import Builder from ads.jobs import env_var_parser +Self = TypeVar("Self", bound="Runtime") +"""Special type to represent the current enclosed class. + +This type is used by factory class method or when a method returns ``self``. +""" + + class Runtime(Builder): """Base class for job runtime""" @@ -34,8 +42,9 @@ def __init__(self, spec: Dict = None, **kwargs) -> None: self.with_environment_variable(**env) @property - def kind(self): - """Kind of the object to be stored in YAML. All runtimes will have "runtime" as kind. + def kind(self) -> str: + """Kind of the object to be stored in YAML. + All runtime implementations will have "runtime" as kind. Subclass will have different types. """ return "runtime" @@ -48,12 +57,10 @@ def type(self) -> str: class_name = re.sub(r"Runtime$", "", class_name) return class_name[0].lower() + (class_name[1:] if len(class_name) > 1 else "") - def with_argument(self, *args, **kwargs): + def with_argument(self: Self, *args, **kwargs) -> Self: """Adds command line arguments to the runtime. - Existing arguments will be preserved. + This method can be called (chained) multiple times to add various arguments. - For example, runtime.with_argument(key="val").with_argument("path/to/file") will result in: - "--key val path/to/file" Parameters ---------- @@ -68,13 +75,34 @@ def with_argument(self, *args, **kwargs): Returns ------- - Runtime + Self This method returns self to support chaining methods. Raises ------ ValueError Keyword arguments with space in a key. + + Examples + -------- + >>> runtime = Runtime().with_argument(key1="val1", key2="val2").with_argument("pos1") + >>> print(runtime.args) + ["--key1", "val1", "--key2", "val2", "pos1"] + + >>> runtime = Runtime() + >>> runtime.with_argument("pos1") + >>> runtime.with_argument(key1="val1", key2="val2.1 val2.2") + >>> runtime.with_argument("pos2") + >>> print(runtime.args) + ['pos1', '--key1', 'val1', '--key2', 'val2.1 val2.2', 'pos2'] + + >>> runtime = Runtime() + >>> runtime.with_argument("pos1") + >>> runtime.with_argument(key1=None, key2="val2") + >>> runtime.with_argument("pos2") + >>> print(runtime.args) + ["pos1", "--key1", "--key2", "val2", "pos2"] + """ arg_values = self.get_spec(self.CONST_ARGS, []) args = [str(arg) for arg in args] @@ -90,35 +118,69 @@ def with_argument(self, *args, **kwargs): self.set_spec(self.CONST_ARGS, arg_values) return self - def with_environment_variable(self, **kwargs): + def with_environment_variable(self: Self, **kwargs) -> Self: """Sets environment variables + Environment variables enclosed by ``${...}`` will be substituted. + + * You can use ``$$`` to escape the substitution. + * Undefined variable enclosed by ``${}`` will be ignored. + * Double dollar signs ``$$`` will be substituted by a single one ``$``. + Returns ------- - Runtime + Self This method returns self to support chaining methods. + + Examples + -------- + >>> runtime = ( + ... PythonRuntime() + ... .with_environment_variable( + ... HOST="10.0.0.1", + ... PORT="443", + ... URL="http://${HOST}:${PORT}/path/", + ... ESCAPED_URL="http://$${HOST}:$${PORT}/path/", + ... MISSING_VAR="This is ${UNDEFINED}", + ... VAR_WITH_DOLLAR="$10", + ... DOUBLE_DOLLAR="$$10" + ... ) + ... ) + >>> for k, v in runtime.environment_variables.items(): + ... print(f"{k}: {v}") + HOST: 10.0.0.1 + PORT: 443 + URL: http://10.0.0.1:443/path/ + ESCAPED_URL: http://${HOST}:${PORT}/path/ + MISSING_VAR: This is ${UNDEFINED} + VAR_WITH_DOLLAR: $10 + DOUBLE_DOLLAR: $10 + + """ if not kwargs: return self envs = [{"name": k, "value": v} for k, v in kwargs.items()] return self.set_spec(self.CONST_ENV_VAR, envs) - def with_freeform_tag(self, **kwargs): + def with_freeform_tag(self: Self, **kwargs) -> Self: """Sets freeform tag Returns ------- - Runtime + Self This method returns self to support chaining methods. """ return self.set_spec(self.CONST_TAG, kwargs) - def with_maximum_runtime_in_minutes(self, maximum_runtime_in_minutes: int): + def with_maximum_runtime_in_minutes( + self: Self, maximum_runtime_in_minutes: int + ) -> Self: """Sets maximum runtime in minutes Returns ------- - Runtime + Self This method returns self to support chaining methods. """ return self.set_spec( diff --git a/ads/jobs/builders/runtimes/container_runtime.py b/ads/jobs/builders/runtimes/container_runtime.py index 0cbb4b600..6b3c6d02d 100644 --- a/ads/jobs/builders/runtimes/container_runtime.py +++ b/ads/jobs/builders/runtimes/container_runtime.py @@ -11,6 +11,15 @@ class ContainerRuntime(Runtime): """Represents a container job runtime To define container runtime: + + >>> ContainerRuntime() + >>> .with_image("iad.ocir.io//") + >>> .with_cmd("sleep 5 && echo Hello World") + >>> .with_entrypoint(["/bin/sh", "-c"]) + >>> .with_environment_variable(MY_ENV="MY_VALUE") + + Alternatively, you can define the ``entrypoint`` and ``cmd`` along with the image. + >>> ContainerRuntime() >>> .with_image( >>> "iad.ocir.io//", @@ -18,22 +27,17 @@ class ContainerRuntime(Runtime): >>> cmd="sleep 5 && echo Hello World", >>> ) >>> .with_environment_variable(MY_ENV="MY_VALUE") - or - >>> ContainerRuntime() - >>> .with_image("iad.ocir.io//") - >>> .with_cmd("sleep 5 && echo Hello World") - >>> .with_entrypoint(["/bin/sh", "-c"]) - >>> .with_environment_variable(MY_ENV="MY_VALUE") - Docker ENTRYPOINT and CMD can be either "exec form" or "shell form" (See references). + The entrypoint and cmd can be either "exec form" or "shell form" (See references). The exec form is used when a list is passed in. The shell form is used when a space separated string is passed in. When using the ContainerRuntime with OCI Data Science Job, the exec form is recommended. - For most images, when the entrypoint is set to ["/bin/sh", "-c"], - cmd can be a string as if you are running shell command. + For most images, when the entrypoint is set to ``["/bin/sh", "-c"]``, + ``cmd`` can be a string as if you are running shell command. - Reference: + References + ---------- https://docs.docker.com/engine/reference/builder/#entrypoint https://docs.docker.com/engine/reference/builder/#cmd @@ -45,7 +49,7 @@ class ContainerRuntime(Runtime): attribute_map = { CONST_IMAGE: CONST_IMAGE, CONST_ENTRYPOINT: CONST_ENTRYPOINT, - CONST_CMD: CONST_CMD + CONST_CMD: CONST_CMD, } attribute_map.update(Runtime.attribute_map) @@ -54,7 +58,9 @@ def image(self) -> str: """The container image""" return self.get_spec(self.CONST_IMAGE) - def with_image(self, image: str, entrypoint: Union[str, list, None] = None, cmd: str = None): + def with_image( + self, image: str, entrypoint: Union[str, list, None] = None, cmd: str = None + ) -> "ContainerRuntime": """Specify the image for the container job. Parameters @@ -68,7 +74,7 @@ def with_image(self, image: str, entrypoint: Union[str, list, None] = None, cmd: Returns ------- - self + ContainerRuntime The runtime instance. """ self.with_entrypoint(entrypoint) @@ -80,7 +86,7 @@ def entrypoint(self) -> str: """Entrypoint of the container job""" return self.get_spec(self.CONST_ENTRYPOINT) - def with_entrypoint(self, entrypoint: Union[str, list]): + def with_entrypoint(self, entrypoint: Union[str, list]) -> "ContainerRuntime": """Specifies the entrypoint for the container job. Parameters @@ -90,7 +96,7 @@ def with_entrypoint(self, entrypoint: Union[str, list]): Returns ------- - self + ContainerRuntime The runtime instance. """ self._spec[self.CONST_ENTRYPOINT] = entrypoint @@ -101,7 +107,7 @@ def cmd(self) -> str: """Command of the container job""" return self.get_spec(self.CONST_CMD) - def with_cmd(self, cmd: str): + def with_cmd(self, cmd: str) -> "ContainerRuntime": """Specifies the command for the container job. Parameters @@ -111,7 +117,7 @@ def with_cmd(self, cmd: str): Returns ------- - self + ContainerRuntime The runtime instance. """ self._spec[self.CONST_CMD] = cmd diff --git a/ads/jobs/builders/runtimes/python_runtime.py b/ads/jobs/builders/runtimes/python_runtime.py index 5fdea8079..9f90efb29 100644 --- a/ads/jobs/builders/runtimes/python_runtime.py +++ b/ads/jobs/builders/runtimes/python_runtime.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from __future__ import annotations import json import os -from typing import Any +from typing import Any, Dict from ads.jobs.builders.runtimes.base import Runtime from ads.common.auth import default_signer @@ -15,7 +15,10 @@ class CondaRuntime(Runtime): - """Represents a job runtime with conda pack""" + """Represents a job runtime with conda pack + This is the base class for Runtime using conda environment. + The ``CondaRuntime`` is not designed to be used directly when creating a job. + """ CONST_CONDA = "conda" CONST_CONDA_TYPE = "type" @@ -30,12 +33,28 @@ class CondaRuntime(Runtime): @property def conda(self) -> dict: - """The conda pack specification + """The conda environment specification. + + For service conda environment, the specification contains: + + * ``type``, the type of the conda environment. + This is always ``service`` for service conda environment. + * ``slug``, the slug of the conda environment. + + For custom conda environment, the specification contains: + + * ``type``, the type of the conda environment. + This is always ``published`` for custom conda environment. + * ``uri``, the uri of the conda environment, e.g. oci://bucket@namespace/prefix/to/conda + * ``region``, the region of the bucket in which the conda environment is stored. + By default, ADS will determine the region based on the authenticated API key or resource principal. + This is only needed if your conda environment is stored in a different region. Returns ------- dict - A dictionary with "type" and "slug" as keys. + A dictionary containing the conda environment specifications. + """ return self.get_spec(self.CONST_CONDA) @@ -62,6 +81,7 @@ def with_service_conda(self, slug: str): def with_custom_conda(self, uri: str, region: str = None): """Specifies the custom conda pack for running the job + Make sure you have configured the IAM policy for the job run to access the conda environment. Parameters ---------- @@ -72,7 +92,8 @@ def with_custom_conda(self, uri: str, region: str = None): this is shown as the "source" of the conda pack. region: str, optional The region of the bucket storing the custom conda pack, by default None. - If region is not specified, ADS will use the region from your authentication credentials, + If region is not specified, ADS will use the region from your authentication credentials: + * For API Key, config["region"] is used. * For Resource Principal, signer.region is used. @@ -98,7 +119,36 @@ def with_custom_conda(self, uri: str, region: str = None): class ScriptRuntime(CondaRuntime): - """Represents job runtime with scripts and conda pack""" + """Represents job runtime with scripts and conda pack. + + This runtime is designed to define job artifacts and configurations supported by OCI Data Science Jobs natively. + It can be used with any script types that is supported by the OCI Data Science Jobs, + including shell scripts and python scripts. + + To run a script with all dependencies contained in a local folder:: + + runtime = ( + ScriptRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch110_p38_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument + .with_argument("100 linux \"hi there\"") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.sh is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.sh") + ) + + + References + ---------- + https://docs.oracle.com/en-us/iaas/data-science/using/jobs-artifact.htm + + """ CONST_ENTRYPOINT = "entrypoint" CONST_SCRIPT_PATH = "scriptPathURI" @@ -120,7 +170,7 @@ def with_script(self, uri: str): Parameters ---------- uri : str - URI to the Python or Shell script, which can be any URI supported by fsspec, + URI to the source code script, which can be any URI supported by fsspec, including http://, https:// and OCI object storage. For example: oci://your_bucket@your_namespace/path/to/script.py @@ -147,7 +197,8 @@ def with_source(self, uri: str, entrypoint: str = None): If the source code is a single file, URI can be any URI supported by fsspec, including http://, https:// and OCI object storage. For example: oci://your_bucket@your_namespace/path/to/script.py - If the source code is a directory, only local directory is supported. + URI can also be a folder or a zip file containing the source code. + In that case, entrypoint is required. entrypoint : str, optional The relative path of the script to be set as entrypoint when source is a zip/tar/directory. @@ -189,6 +240,7 @@ class _PythonRuntimeMixin(Runtime): CONST_PYTHON_PATH = "pythonPath" CONST_ENTRYPOINT = "entrypoint" CONST_ENTRY_FUNCTION = "entryFunction" + CONST_WORKING_DIR = "workingDir" attribute_map = { CONST_OUTPUT_DIR: "output_dir", @@ -196,6 +248,7 @@ class _PythonRuntimeMixin(Runtime): CONST_PYTHON_PATH: "python_path", CONST_ENTRYPOINT: CONST_ENTRYPOINT, CONST_ENTRY_FUNCTION: "entry_function", + CONST_WORKING_DIR: "working_dir", } attribute_map.update(Runtime.attribute_map) @@ -215,7 +268,7 @@ def with_output(self, output_dir: str, output_uri: str): Returns ------- - self + Self The runtime instance. """ self.set_spec(self.CONST_OUTPUT_DIR, output_dir) @@ -266,6 +319,29 @@ def with_entrypoint(self, path: str, func: str = None): self.set_spec(self.CONST_ENTRY_FUNCTION, func) return self + def with_working_dir(self, working_dir: str): + """Specifies the working directory in the job run. + By default, the working directory will the directory containing the user code (job artifact directory). + This can be changed by specifying a relative path to the job artifact directory. + + Parameters + ---------- + working_dir : str + The path of the working directory. + This can be a relative path from the job artifact directory. + + Returns + ------- + self + The runtime instance. + """ + return self.set_spec(self.CONST_WORKING_DIR, working_dir) + + @property + def working_dir(self) -> str: + """The working directory for the job run.""" + return self.get_spec(self.CONST_WORKING_DIR, ".") + @property def output_dir(self) -> str: """Directory in the Job run container for saving output files generated in the job""" @@ -293,43 +369,65 @@ def entry_function(self) -> str: class PythonRuntime(ScriptRuntime, _PythonRuntimeMixin): - """Represents a job runtime using ADS driver script to run Python code""" + """Represents a job runtime using ADS driver script to run Python code + + Example:: + + runtime = ( + PythonRuntime() + # Specify the service conda environment by slug name. + .with_service_conda("pytorch110_p38_cpu_v1") + # The job artifact can be a single Python script, a directory or a zip file. + .with_source("local/path/to/code_dir") + # Environment variable + .with_environment_variable(NAME="Welcome to OCI Data Science.") + # Command line argument, arg1 --key arg2 + .with_argument("arg1", key="arg2") + # Set the working directory + # When using a directory as source, the default working dir is the parent of code_dir. + # Working dir should be a relative path beginning from the source directory (code_dir) + .with_working_dir("code_dir") + # The entrypoint is applicable only to directory or zip file as source + # The entrypoint should be a path relative to the working dir. + # Here my_script.py is a file in the code_dir/my_package directory + .with_entrypoint("my_package/my_script.py") + # Add an additional Python path, relative to the working dir (code_dir/other_packages). + .with_python_path("other_packages") + # Copy files in "code_dir/output" to object storage after job finishes. + .with_output("output", "oci://bucket_name@namespace/path/to/dir") + ) - CONST_WORKING_DIR = "workingDir" - attribute_map = {CONST_WORKING_DIR: "working_dir"} + """ + + attribute_map = {} attribute_map.update(ScriptRuntime.attribute_map) attribute_map.update(_PythonRuntimeMixin.attribute_map) - def with_working_dir(self, working_dir: str): - """Specifies the working directory in the job run. - By default, the working directory will the directory containing the user code (job artifact directory). - This can be changed by specifying a relative path to the job artifact directory. - Parameters - ---------- - working_dir : str - The path of the working directory. - This can be a relative path from the job artifact directory. - - Returns - ------- - self - The runtime instance. - """ - return self.set_spec(self.CONST_WORKING_DIR, working_dir) +class NotebookRuntime(CondaRuntime): + """Represents a job runtime with Jupyter notebook - @property - def working_dir(self) -> str: - """The working directory for the job run.""" - return self.get_spec(self.CONST_WORKING_DIR, ".") + To run a job with a Jupyter notebook, + you can define the run time as:: + runtime = ( + NotebookRuntime() + .with_notebook( + path="https://raw.githubusercontent.com/tensorflow/docs/master/site/en/tutorials/customization/basics.ipynb", + encoding='utf-8' + ) + .with_service_conda("tensorflow28_p38_cpu_v1") + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + .with_exclude_tag(["ignore", "remove"]) + .with_output("oci://bucket_name@namespace/path/to/dir") + ) -class NotebookRuntime(CondaRuntime): - """Represents a job runtime with Jupyter notebook""" + """ CONST_NOTEBOOK_PATH = "notebookPathURI" CONST_NOTEBOOK_ENCODING = "notebookEncoding" - CONST_OUTPUT_URI = "outputURI" + CONST_OUTPUT_URI = "outputUri" + CONST_OUTPUT_URI_ALT = "outputURI" CONST_EXCLUDE_TAG = "excludeTags" attribute_map = { @@ -340,6 +438,15 @@ class NotebookRuntime(CondaRuntime): } attribute_map.update(CondaRuntime.attribute_map) + def __init__(self, spec: Dict = None, **kwargs) -> None: + if spec and self.CONST_OUTPUT_URI_ALT in spec: + val = spec.pop(self.CONST_OUTPUT_URI_ALT) + spec[self.CONST_OUTPUT_URI] = val + if self.CONST_OUTPUT_URI_ALT in kwargs: + val = kwargs.pop(self.CONST_OUTPUT_URI_ALT) + kwargs[self.CONST_OUTPUT_URI] = val + super().__init__(spec, **kwargs) + @property def notebook_uri(self) -> str: """The URI of the notebook""" @@ -350,8 +457,8 @@ def notebook_encoding(self) -> str: """The encoding of the notebook""" return self.get_spec(self.CONST_NOTEBOOK_ENCODING) - def with_notebook(self, path: str, encoding="utf-8"): - """Specifies the notebook to be converted to python script and run as a job. + def with_notebook(self, path: str, encoding="utf-8") -> NotebookRuntime: + """Specifies the notebook to be run as a job. Parameters ---------- @@ -371,7 +478,7 @@ def exclude_tag(self) -> list: """A list of cell tags indicating cells to be excluded from the job""" return self.get_spec(self.CONST_EXCLUDE_TAG, []) - def with_exclude_tag(self, *tags): + def with_exclude_tag(self, *tags) -> NotebookRuntime: """Specifies the cell tags in the notebook to exclude cells from the job script. Parameters @@ -397,7 +504,7 @@ def output_uri(self) -> list: """URI for storing the output notebook and files""" return self.get_spec(self.CONST_OUTPUT_URI) - def with_output(self, output_uri: str): + def with_output(self, output_uri: str) -> NotebookRuntime: """Specifies the output URI for storing the output notebook and files. Parameters @@ -415,7 +522,28 @@ def with_output(self, output_uri: str): class GitPythonRuntime(CondaRuntime, _PythonRuntimeMixin): - """Represents a job runtime with source code from git repository""" + """Represents a job runtime with source code from git repository + + Example:: + + runtime = ( + GitPythonRuntime() + .with_environment_variable(GREETINGS="Welcome to OCI Data Science") + # Specify the service conda environment by slug name. + .with_service_conda("pytorch19_p37_gpu_v1") + # Specify the git repository + # Optionally, you can specify the branch or commit + .with_source("https://github.com/pytorch/tutorials.git") + # Entrypoint is a relative path from the root of the git repo. + .with_entrypoint("beginner_source/examples_nn/polynomial_nn.py") + # Copy files in "beginner_source/examples_nn" to object storage after job finishes. + .with_output( + output_dir="beginner_source/examples_nn", + output_uri="oci://bucket_name@namespace/path/to/dir" + ) + ) + + """ CONST_GIT_URL = "url" CONST_BRANCH = "branch" @@ -541,7 +669,7 @@ def with_argument(self, *args, **kwargs): return super().with_argument(*args, **kwargs) @property - def ssh_secret_ocid(self): + def ssh_secret_ocid(self) -> str: """The OCID of the OCI Vault secret storing the Git SSH key.""" return self.get_spec(self.CONST_GIT_SSH_SECRET_ID) diff --git a/ads/jobs/env_var_parser.py b/ads/jobs/env_var_parser.py index 6790ba2b9..ac084c801 100644 --- a/ads/jobs/env_var_parser.py +++ b/ads/jobs/env_var_parser.py @@ -1,19 +1,25 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import copy +import json from typing import Union, List, Dict from configparser import ConfigParser, ExtendedInterpolation from configparser import ( InterpolationSyntaxError, InterpolationDepthError, - InterpolationMissingOptionError, ) from configparser import NoSectionError, NoOptionError, MAX_INTERPOLATION_DEPTH class EnvVarInterpolation(ExtendedInterpolation): + """Modified version of ExtendedInterpolation to ignore errors + + https://github.com/python/cpython/blob/main/Lib/configparser.py + """ + def before_set(self, parser, section: str, option: str, value: str) -> str: return value @@ -43,7 +49,6 @@ def _interpolate_some(self, parser, option, accum, rest, section, map, depth): "Bad interpolation variable reference %r" % rest, ) path = m.group(1).split(":") - rest = rest[m.end() :] sect = section opt = option try: @@ -56,11 +61,15 @@ def _interpolate_some(self, parser, option, accum, rest, section, map, depth): v = parser.get(sect, opt, raw=True) else: raise InterpolationSyntaxError( - option, section, "More than one ':' found: %r" % (rest,) + option, + section, + "More than one ':' found: %r" % (rest[m.end() :],), ) except (KeyError, NoSectionError, NoOptionError): - accum.append(rawval) + accum.append(rest) return + + rest = rest[m.end() :] if "$" in v: self._interpolate_some( parser, @@ -97,8 +106,23 @@ def parse(env_var: Union[Dict, List[dict]]) -> dict: # Convert kubernetes style env to dict if isinstance(env_var, list): env_var = {ev["name"]: ev["value"] for ev in env_var} + else: + env_var = copy.deepcopy(env_var) config = ConfigParser(interpolation=EnvVarInterpolation()) config.optionxform = str + for k in env_var.keys(): + if env_var[k] is None: + # Convert None to empty string + env_var[k] = "" + elif not isinstance(env_var[k], str): + # If the value is not a string, + # try to dump it as json string + try: + env_var[k] = json.dumps(env_var[k]) + except Exception: + # Cast the value to string if it is not json serializable + env_var[k] = str(env_var[k]) + config["envs"] = env_var return {k: config["envs"].get(k) for k in env_var.keys()} diff --git a/ads/jobs/serializer.py b/ads/jobs/serializer.py index 76aa455a5..0aed1a3e2 100644 --- a/ads/jobs/serializer.py +++ b/ads/jobs/serializer.py @@ -1,89 +1,71 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import json from abc import ABC, abstractmethod -from ads.jobs.schema.validator import ValidatorFactory -from dataclasses import asdict, dataclass, field -from typing import Dict, List, Union +from typing import Union, TypeVar, Type import fsspec -import json import yaml -try: - from yaml import CSafeDumper as dumper - from yaml import CSafeLoader as loader -except: - from yaml import SafeDumper as dumper - from yaml import SafeLoader as loader + +Self = TypeVar("Self", bound="Serializable") +"""Special type to represent the current enclosed class. + +This type is used by factory class method or when a method returns ``self``. +""" class Serializable(ABC): - """Base class that represents a serializable item. - - Methods - ------- - to_dict(self) -> dict - Serializes the object into a dictionary. - from_dict(cls, obj_dict) -> cls - Returns an instance of the class instantiated from the dictionary provided. - _write_to_file(s, uri, **kwargs) - Write string s into location specified by uri - _read_from_file(uri, **kwargs) - Returns contents from location specified by URI - to_json(self, uri=None, **kwargs) - Returns object serialized as a JSON string - from_json(cls, json_string=None, uri=None, **kwargs) - Creates an object from JSON string provided or from URI location containing JSON string - to_yaml(self, uri=None, **kwargs) - Returns object serialized as a YAML string - from_yaml(cls, yaml_string=None, uri=None, **kwargs) - Creates an object from YAML string provided or from URI location containing YAML string - from_string(cls, obj_string=None: str, uri=None, **kwargs) - Creates an object from string provided or from URI location containing string - """ + """Base class that represents a serializable item.""" @abstractmethod def to_dict(self) -> dict: - """Serializes instance of class into a dictionary""" - pass + """Serializes an instance of class into a dictionary""" @classmethod @abstractmethod def from_dict(cls, obj_dict: dict): - """Returns an instance of the class instantiated by the dictionary provided + """Initialize an instance of the class from a dictionary - Args: - obj_dict (dict): Dictionary representation of the object + Parameters + ---------- + obj_dict : dict + Dictionary representation of the object """ - pass @staticmethod def _write_to_file(s: str, uri: str, **kwargs) -> None: - """Write string s into location specified by uri - - Args: - s (string): content - uri (string): URI location to save string s - kwargs (dict): keyword arguments to be passed into fsspec.open(). For OCI object storage, this should be config="path/to/.oci/config". - For other storage connections consider e.g. host, port, username, password, etc. + """Write string content into a file specified by uri + + Parameters + ---------- + s : str + The content to be written. + uri : str + URI of the file to save the content. + kwargs : dict + keyword arguments to be passed into fsspec.open(). + For OCI object storage, this can be config="path/to/.oci/config". """ with fsspec.open(uri, "w", **kwargs) as f: f.write(s) @staticmethod def _read_from_file(uri: str, **kwargs) -> str: - """Returns contents from location specified by URI + """Returns contents from a file specified by URI - Args: - uri (string): URI location - kwargs (dict): keyword arguments to be passed into fsspec.open(). For OCI object storage, this should be config="path/to/.oci/config". - For other storage connections consider e.g. host, port, username, password, etc. + Parameters + ---------- + uri : str + The URI of the file. - Returns: - string: Contents in file specified by URI + Returns + ------- + str + The content of the file as a string. """ with fsspec.open(uri, "r", **kwargs) as f: return f.read() @@ -91,16 +73,22 @@ def _read_from_file(uri: str, **kwargs) -> str: def to_json( self, uri: str = None, encoder: callable = json.JSONEncoder, **kwargs ) -> str: - """Returns object serialized as a JSON string - - Args: - uri (string, optional): URI location to save the JSON string. Defaults to None. - encoder (callable, optional): Encoder for custom data structures. Defaults to JSONEncoder. - kwargs (dict): keyword arguments to be passed into fsspec.open(). For OCI object storage, this should be config="path/to/.oci/config". - For other storage connections consider e.g. host, port, username, password, etc. - - Returns: - string: Serialized version of object + """Returns the object serialized as a JSON string + + Parameters + ---------- + uri : str, optional + URI location to save the JSON string, by default None + encoder : callable, optional + Encoder for custom data structures, by default json.JSONEncoder + kwargs : dict + keyword arguments to be passed into fsspec.open(). + For OCI object storage, this can be config="path/to/.oci/config". + + Returns + ------- + str + object serialized as a JSON string """ json_string = json.dumps(self.to_dict(), cls=encoder) if uri: @@ -109,12 +97,12 @@ def to_json( @classmethod def from_json( - cls, + cls: Type[Self], json_string: str = None, uri: str = None, decoder: callable = json.JSONDecoder, **kwargs - ): + ) -> Self: """Creates an object from JSON string provided or from URI location containing JSON string Args: @@ -129,6 +117,29 @@ def from_json( Returns: cls: Returns instance of the class + + Parameters + ---------- + json_string : str, optional + JSON string, by default None + uri : str, optional + URI location of file containing JSON string, by default None + decoder : callable, optional + Decoder for custom data structures, by default json.JSONDecoder + kwargs : dict + keyword arguments to be passed into fsspec.open(). + For OCI object storage, this can be config="path/to/.oci/config". + + Returns + ------- + Type[Self] + Object initialized from JSON data. + + Raises + ------ + ValueError + Both json_string and uri are empty, or + The input is not a valid JSON. """ if json_string: return cls.from_dict(json.loads(json_string, cls=decoder)) @@ -137,45 +148,62 @@ def from_json( return cls.from_dict(json_dict) raise ValueError("Must provide either JSON string or URI location") - def to_yaml(self, uri: str = None, dumper: callable = dumper, **kwargs) -> str: + def to_yaml( + self, uri: str = None, dumper: callable = yaml.SafeDumper, **kwargs + ) -> Union[str, None]: """Returns object serialized as a YAML string - Args: - uri (string, optional): URI location to save the YAML string. Defaults to None. - dumper (callable, optional): Custom YAML Dumper. Defaults to CDumper/SafeDumper. - kwargs (dict): keyword arguments to be passed into fsspec.open(). For OCI object storage, this should be config="path/to/.oci/config". - For other storage connections consider e.g. host, port, username, password, etc. - - Returns: - string: Serialized version of object + Parameters + ---------- + uri : str, optional + URI location to save the YAML string, by default None + dumper : callable, optional + Custom YAML Dumper, by default yaml.SafeDumper + kwargs : dict + keyword arguments to be passed into fsspec.open(). + For OCI object storage, this can be config="path/to/.oci/config". + + Returns + ------- + Union[str, None] + If uri is specified, None will be returned. + Otherwise, the yaml content will be returned. """ yaml_string = yaml.dump(self.to_dict(), Dumper=dumper) if uri: self._write_to_file(s=yaml_string, uri=uri, **kwargs) + return None + return yaml_string @classmethod def from_yaml( - cls, + cls: Type[Self], yaml_string: str = None, uri: str = None, - loader: callable = loader, + loader: callable = yaml.SafeLoader, **kwargs - ): - """Creates an object from YAML string provided or from URI location containing YAML string - - Args: - yaml_string (string, optional): YAML string. Defaults to None. - uri (string, optional): URI location of file containing YAML string. Defaults to None. - loader (callable, optional): Custom YAML loader. Defaults to CLoader/SafeLoader. - kwargs (dict): keyword arguments to be passed into fsspec.open(). For OCI object storage, this should be config="path/to/.oci/config". - For other storage connections consider e.g. host, port, username, password, etc. - - Raises: - ValueError: Raised if neither string nor uri is provided - - Returns: - cls: Returns instance of the class + ) -> Self: + """Initializes an object from YAML string or URI location containing the YAML + + Parameters + ---------- + yaml_string : str, optional + YAML string, by default None + uri : str, optional + URI location of file containing YAML, by default None + loader : callable, optional + Custom YAML loader, by default yaml.SafeLoader + + Returns + ------- + Self + Object initialized from the YAML. + + Raises + ------ + ValueError + Raised if neither string nor uri is provided """ if yaml_string: return cls.from_dict(yaml.load(yaml_string, Loader=loader)) @@ -186,27 +214,31 @@ def from_yaml( @classmethod def from_string( - cls, + cls: Type[Self], obj_string: str = None, uri: str = None, - loader: callable = loader, + loader: callable = yaml.SafeLoader, **kwargs - ): - """Creates an object from string provided or from URI location containing string - - Args: - obj_string (str, optional): String representing the object - uri (str, optional): URI location of file containing string. Defaults to None. - loader (callable, optional): Custom YAML loader. Defaults to CLoader/SafeLoader. - kwargs (dict): keyword arguments to be passed into fsspec.open(). For OCI object storage, this should be config="path/to/.oci/config". - For other storage connections consider e.g. host, port, username, password, etc. - - Returns: - cls: Returns instance of the class + ) -> Self: + """Initializes an object from YAML/JSON string or URI location containing the YAML/JSON + + Parameters + ---------- + obj_string : str, optional + YAML/JSON string, by default None + uri : str, optional + URI location of file containing YAML/JSON, by default None + loader : callable, optional + Custom YAML loader, by default yaml.SafeLoader + + Returns + ------- + Self + Object initialized from the YAML. """ return cls.from_yaml(yaml_string=obj_string, uri=uri, loader=loader, **kwargs) - def __repr__(self): + def __repr__(self) -> str: """Returns printable version of object Returns: diff --git a/ads/jobs/templates/driver_notebook.py b/ads/jobs/templates/driver_notebook.py index cae3be3e2..3f225b8dd 100644 --- a/ads/jobs/templates/driver_notebook.py +++ b/ads/jobs/templates/driver_notebook.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """This module runs a Jupyter Python notebook with nbconvert and print the outputs. This is a driver script auto-generated by Oracle ADS. @@ -20,17 +20,21 @@ import logging import json import os -import subprocess from typing import Optional -from urllib.parse import urlparse import nbformat -import oci -from nbconvert.preprocessors import ExecutePreprocessor +from nbconvert.preprocessors import ExecutePreprocessor, CellExecutionError +try: + # This is used by ADS and testing + from .driver_utils import OCIHelper, JobRunner, set_log_level +except ImportError: + # This is used when the script is in a job run. + from driver_utils import OCIHelper, JobRunner, set_log_level -logger = logging.getLogger(__name__) +logger = logging.getLogger(__name__) +logger = set_log_level(logger) class ADSExecutePreprocessor(ExecutePreprocessor): """Customized Execute Preprocessor for running notebook.""" @@ -77,7 +81,7 @@ def preprocess_cell(self, cell, resources, *args, **kwargs): if tag in self.exclude_tags: return cell, resources except Exception as ex: - logger.error("An error occurred when reading cell tags: %s", str(ex)) + logger.exception("An error occurred when reading cell tags.") # Run the cell cell, resources = super().preprocess_cell(cell, resources, *args, **kwargs) # Print cell output @@ -87,95 +91,15 @@ def preprocess_cell(self, cell, resources, *args, **kwargs): try: self._print_cell_outputs(cell) except Exception as ex: - logger.error("An error occurred when reading cell outputs: %s", str(ex)) + logger.exception("An error occurred when reading cell outputs.") return cell, resources -class OCIHelper: - @staticmethod - def init_oci_client(client_class): - """Initializes OCI client with API key or Resource Principal. - - Parameters - ---------- - client_class : - The class of OCI client to be initialized. - """ - if os.environ.get("OCI_RESOURCE_PRINCIPAL_VERSION"): - logger.info( - "Initializing %s with Resource Principal...", client_class.__name__ - ) - client = client_class( - {}, signer=oci.auth.signers.get_resource_principals_signer() - ) - else: - logger.info("Initializing %s with API Key...", {client_class.__name__}) - client = client_class(oci.config.from_file()) - return client - - @staticmethod - def copy_outputs(output_dir: str, output_uri: dict) -> None: - """Copies the output files to object storage bucket. - - Parameters - ---------- - output_dir : str - Path of the output directory containing files to be copied. - output_uri : str - URI of the object storage path to store the output files. - """ - output_dir = os.path.abspath(os.path.expanduser(output_dir)) - if not os.path.exists(output_dir): - logger.error("Output directory %s not found.", output_dir) - return - if not output_uri: - logger.error( - "OUTPUT_URI is not defined in environment variable. No file is copied." - ) - return - logger.info("Copying files in %s to %s...", output_dir, output_uri) - parsed = urlparse(output_uri) - bucket_name = parsed.username - namespace = parsed.hostname - prefix = parsed.path - oci_os_client = OCIHelper.init_oci_client( - oci.object_storage.ObjectStorageClient - ) - - if not prefix: - prefix = "" - prefix = prefix.strip("/") - - for path, _, files in os.walk(output_dir): - for name in files: - file_path = os.path.join(path, name) - - with open(file_path, "rb") as pkf: - # Get the relative path of the file to keep the directory structure - relative_path = os.path.relpath(file_path, output_dir) - if prefix: - file_prefix = os.path.join(prefix, relative_path) - else: - # Save file to bucket root if prefix is empty. - file_prefix = relative_path - - logger.debug( - f"Saving {relative_path} to {bucket_name}@{namespace}/{file_prefix}" - ) - - oci_os_client.put_object( - namespace, - bucket_name, - file_prefix, - pkf, - ) - - def run_notebook( notebook_path: str, working_dir: Optional[str] = None, exclude_tags: Optional[list] = None, -) -> None: +) -> Optional[CellExecutionError]: """Runs a notebook Parameters @@ -187,6 +111,12 @@ def run_notebook( If this is None, the same directory of the notebook_path will be used. exclude_tags : list, optional Tags for excluding cells, by default None + + Returns + ------- + CellExecutionError or None + Exception object when there is an error in a notebook cell. + Otherwise, None. """ # Read the notebook encoding = os.environ.get("NOTEBOOK_ENCODING", "utf-8") @@ -202,28 +132,23 @@ def run_notebook( ep = ADSExecutePreprocessor(exclude_tags=exclude_tags, kernel_name="python") - from nbconvert.preprocessors import CellExecutionError - try: ep.preprocess(nb, {"metadata": {"path": working_dir}}) - except CellExecutionError: + ex = None + except CellExecutionError as exc: msg = "Error executing the notebook.\n\n" - msg += f'See notebook "{notebook_filename_out}" for the traceback.' logger.error(msg) - raise + ex = exc finally: with open(notebook_filename_out, mode="w", encoding=encoding) as f: nbformat.write(nb, f) + return ex -def run_driver() -> None: +def main() -> None: """Runs the driver to execute a notebook.""" - try: - # Ignore the error as conda-unpack may not exists - subprocess.check_output("conda-unpack", stderr=subprocess.STDOUT, shell=True) - except subprocess.CalledProcessError as ex: - logger.debug("conda-unpack exits with non-zero return code %s", ex.returncode) - logger.debug(ex.output) + JobRunner().conda_unpack() + notebook_file_path = os.path.join( os.path.dirname(__file__), os.environ.get("JOB_RUN_NOTEBOOK") ) @@ -236,10 +161,14 @@ def run_driver() -> None: tags = json.loads(tags) logger.info("Excluding cells with any of the following tags: %s", tags) # Run the notebook - run_notebook(notebook_file_path, working_dir=output_dir, exclude_tags=tags) + ex = run_notebook(notebook_file_path, working_dir=output_dir, exclude_tags=tags) + # Save the outputs - OCIHelper.copy_outputs(output_dir, os.environ.get("OUTPUT_URI")) + OCIHelper.copy_outputs(output_dir) + + if ex: + raise ex if __name__ == "__main__": - run_driver() + main() diff --git a/ads/jobs/templates/driver_oci.py b/ads/jobs/templates/driver_oci.py index 732b0d792..961747436 100644 --- a/ads/jobs/templates/driver_oci.py +++ b/ads/jobs/templates/driver_oci.py @@ -1,9 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -"""This module runs user code from git repository. +""" +This is a driver script from Oracle ADS to run Python script in OCI Data Science Jobs. The following environment variables are used: GIT_URL: URL to the Git repository. @@ -55,17 +56,14 @@ """ import base64 -import importlib -import json +import logging import os import random import shutil import string -import subprocess -import sys -import time import traceback import uuid +from time import sleep, time from typing import Optional from urllib.parse import urlparse from urllib.request import getproxies @@ -74,18 +72,51 @@ import oci import requests from oci.data_science import DataScienceClient -from oci.object_storage import ObjectStorageClient +try: + # This is used by ADS and testing + from .driver_utils import ( + OCIHelper, + JobRunner, + set_log_level, + CONST_ENV_ENTRY_FUNC, + CONST_ENV_CODE_DIR, + CONST_ENV_JOB_RUN_OCID, + ) +except ImportError: + # This is used when the script is in a job run. + from driver_utils import ( + OCIHelper, + JobRunner, + set_log_level, + CONST_ENV_ENTRY_FUNC, + CONST_ENV_CODE_DIR, + CONST_ENV_JOB_RUN_OCID, + ) + + +CONST_ENV_ENTRYPOINT = "GIT_ENTRYPOINT" +CONST_ENV_GIT_URL = "GIT_URL" +CONST_ENV_GIT_BRANCH = "GIT_BRANCH" +CONST_ENV_GIT_COMMIT = "GIT_COMMIT" +CONST_ENV_SECRET_OCID = "GIT_SECRET_OCID" -SSH_DIR = os.path.join("home", "datascience", "ssh_" + str(uuid.uuid4())) -SSH_KEY_FILE_PATH = os.path.join(SSH_DIR, "id_rsa") DEFAULT_CODE_DIR = "~/Code/" +SSH_DIR = os.path.join("/home", "datascience", "ssh_" + str(uuid.uuid4())) +SSH_KEY_FILE_PATH = os.path.join(SSH_DIR, "id_rsa") +SSH_CONFIG_FILE_PATH = os.path.join(SSH_DIR, "config") + + +logger = logging.getLogger(__name__) +logger = set_log_level(logger) class GitManager: """Contains methods for fetching code from Git repository""" - def __init__(self, repo_url: str, code_dir: str = None): + def __init__( + self, repo_url: str, code_dir: str = os.environ.get(CONST_ENV_CODE_DIR) + ): """Initialize the GitManager Parameters @@ -103,90 +134,105 @@ def __init__(self, repo_url: str, code_dir: str = None): if not repo_url: raise ValueError("Specify the URL of Git repository.") self.repo_url = repo_url + # Use default directory if code_dir is set to None or empty string. if not code_dir: - code_dir = os.environ.get("CODE_DIR") - # Use default directory if CODE_DIR is set to None or empty string. - if not code_dir: - code_dir = os.path.join(DEFAULT_CODE_DIR, os.path.basename(repo_url).split(".", 1)[0]) + code_dir = os.path.join( + DEFAULT_CODE_DIR, os.path.basename(repo_url).split(".", 1)[0] + ) self.code_dir = os.path.abspath(os.path.expanduser(code_dir)) - print(f"Initializing code directory at {self.code_dir} ...") + logger.info("Initializing code directory at %s", self.code_dir) # Rename the existing directory if one already exists if os.path.exists(self.code_dir) and os.listdir(self.code_dir): - shutil.move( - self.code_dir, + logger.warning( + "Directory %s already exists and is not empty.", self.code_dir + ) + new_name = ( self.code_dir + "_" - + "".join(random.choice(string.ascii_lowercase) for i in range(5)), + + "".join(random.choice(string.ascii_lowercase) for i in range(5)) ) + shutil.move( + self.code_dir, + new_name, + ) + logger.warning("Renamed %s to %s", self.code_dir, new_name) os.makedirs(self.code_dir, exist_ok=True) self.repo = None self.commit = None + def _config_ssh_proxy(self, proxy): + return_code = JobRunner.run_command("command -v socat", level=logging.DEBUG) + if return_code: + logger.warning( + "You have ssh_proxy configured. " + "Please install the 'socat' package into your environment " + "if you would like to use proxy for Git clone via SSH." + ) + else: + ssh_config = f"ProxyCommand socat - PROXY:{proxy.hostname}:%h:%p,proxyport={proxy.port}" + logger.debug("Adding proxy for SSH: %s", ssh_config) + with open(SSH_CONFIG_FILE_PATH, "a", encoding="utf-8") as f: + f.write(ssh_config) + logger.debug("SSH config saved to %s", SSH_CONFIG_FILE_PATH) + + def _config_known_hosts(self, host: str): + if JobRunner.run_command( + f"ssh-keyscan -H {host} >> {SSH_DIR}/known_hosts", level=logging.DEBUG + ): + logger.debug("Added %s to known hosts.", host) + else: + logger.warning( + "Failed to add %s to known hosts." + "You may need to configure your subnet security list to allow traffic on port 22.", + host, + ) + # Test the connection, for debugging purpose + if logger.level == logging.DEBUG: + os.system(f"ssh -vT git@{host}") + + def _config_ssh_key(self): + if os.path.exists(SSH_KEY_FILE_PATH): + # Ignore the fingerprint checking + ssh_cmd = f'ssh -i "{SSH_KEY_FILE_PATH}" ' + if os.path.exists(SSH_CONFIG_FILE_PATH): + ssh_cmd += f'-F "{SSH_CONFIG_FILE_PATH}" ' + ssh_cmd += ( + '-o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" ' + ) + if logger.level == logging.DEBUG: + ssh_cmd += "-v " + logger.debug("SSH command: %s", ssh_cmd) + return {"GIT_SSH_COMMAND": ssh_cmd} + return None + def fetch_repo(self): """Clones the Git repository.""" - print(f"Cloning Repo from {self.repo_url} ...") - repo_url = self.repo_url - if repo_url.startswith("git@"): - repo_url = "ssh://" + repo_url - host = urlparse(repo_url).hostname - ssh_config_path = os.path.join(SSH_DIR, "config") - if urlparse(repo_url).scheme == "ssh": - os.makedirs(os.path.dirname(ssh_config_path), exist_ok=True) + logger.debug("Cloning from %s", self.repo_url) + env = None + if self.repo_url.startswith(("git@", "ssh://")): proxies = getproxies() if ("http" in proxies or "https" in proxies) and "ssh" not in proxies: - print( + logger.warning( "You have http/https proxy configured. " - "Please set ssh_proxy in environment variable install the 'socat' package " - "if you would like to use the proxy for Git clone via SSH." + "Please set ssh_proxy in environment variable and install the 'socat' package " + "if you would like to use the proxy for Git clone via SSH. " + "conda install -c conda-forge socat" ) if "ssh" in proxies: proxy = urlparse(proxies["ssh"]) - try: - subprocess.check_output("command -v socat", shell=True) - ssh_config = f"ProxyCommand socat - PROXY:{proxy.hostname}:%h:%p,proxyport={proxy.port}" - print(f"Adding proxy for SSH: {ssh_config}") - with open(ssh_config_path, "a", encoding="utf-8") as f: - f.write(ssh_config) - print(f"SSH config saved to {ssh_config_path}") - except subprocess.CalledProcessError: - print( - "You have ssh_proxy configured. " - "Please install the 'socat' package into your environment " - "if you would like to use proxy for Git clone via SSH." - ) + self._config_ssh_proxy(proxy=proxy) + env = self._config_ssh_key() - try: - cmd = f"ssh-keyscan -H {host} >> {SSH_DIR}/known_hosts" - subprocess.check_output(cmd, shell=True) - print(f"Added {host} to known hosts.") - except subprocess.CalledProcessError as ex: - print(f"'{cmd}' exited with code {ex.returncode}.") - print(ex.output) - print( - "You may need to configure your subnet security list to allow traffic on port 22." - ) + if logger.level == logging.DEBUG: + os.system("git --version") - ssh_key_path = os.path.expanduser(SSH_KEY_FILE_PATH) - if os.path.exists(ssh_key_path): - # Test the connection, for debugging purpose - os.system(f"ssh -vT git@{host}") - # Ignore the fingerprint checking - ssh_cmd = f'ssh -i "{ssh_key_path}" ' - if os.path.exists(ssh_config_path): - ssh_cmd += f'-F "{ssh_config_path}" ' - ssh_cmd += ( - ' -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"' - ) - env = {"GIT_SSH_COMMAND": ssh_cmd} - else: - env = None self.repo = git.Repo.clone_from(self.repo_url, self.code_dir, env=env) - print(f"Cloned repo to: {self.code_dir}") + logger.info("Cloned repo to: %s", self.code_dir) return self - def setup_code(self, branch: Optional[str] = None, commit: Optional[str] = None): + def checkout_code(self, branch: Optional[str] = None, commit: Optional[str] = None): """Checkouts the branch or commit of the Git repository. If neither branch nor commit is specified, the tip of the default branch will be used. @@ -201,46 +247,20 @@ def setup_code(self, branch: Optional[str] = None, commit: Optional[str] = None) """ if commit: - print(f"Checking out commit: {commit}") + logger.info("Checking out commit: %s", commit) self.repo.git.checkout(commit) elif branch: - print(f"Checking out branch: {branch}") + logger.info("Checking out branch: %s", branch) self.repo.git.checkout(branch) else: - print(f"Checking out the latest commit {self.repo.head.commit.hexsha}...") - self.commit = self.repo.head.commit.hexsha + logger.info( + "Checking out the latest commit %s", self.repo.head.commit.hexsha + ) + self.commit = self.repo.head.commit.hexsha return self class CredentialManager: - @staticmethod - def init_oci_client(client_class): - """Initializes OCI client with API key or Resource Principal. - - Parameters - ---------- - client_class : - The class of OCI client to be initialized. - """ - if os.environ.get("OCI_IAM_TYPE", "").lower() == "api_key": - print(f"Initializing {client_class.__name__} with API Key...") - client = client_class( - oci.config.from_file( - file_location=os.environ.get( - "OCI_CONFIG_LOCATION", oci.config.DEFAULT_LOCATION - ), - profile_name=os.environ.get( - "OCI_CONFIG_PROFILE", oci.config.DEFAULT_PROFILE - ), - ) - ) - else: - print(f"Initializing {client_class.__name__} with Resource Principal...") - client = client_class( - {}, signer=oci.auth.signers.get_resource_principals_signer() - ) - return client - @staticmethod def read_secret(secret_id): """Reads and decode the value of of a secret from OCI vault. @@ -255,7 +275,7 @@ def read_secret(secret_id): str The value of the secret decoded with ASCII. """ - secret_client = CredentialManager.init_oci_client(oci.secrets.SecretsClient) + secret_client = OCIHelper.init_oci_client(oci.secrets.SecretsClient) secret_bundle = secret_client.get_secret_bundle(secret_id) base64_secret_bytes = secret_bundle.data.secret_bundle_content.content.encode( "ascii" @@ -286,7 +306,7 @@ def _set_ssh_key(self): f.write(content) # Set the correct permission for the SSH key. os.chmod(self.key_file_path, 0o600) - print(f"SSH key saved to {self.key_file_path}") + logger.info("SSH key saved to %s", self.key_file_path) def _backup_ssh_key(self): """Backup the existing SSH key if one exists at the same file location.""" @@ -326,10 +346,14 @@ def __exit__(self, tp, value, tb): pass -class JobRunner: +class GitJobRunner(JobRunner): """Contains methods for running the job.""" - def __init__(self, job_run_ocid: str = ""): + def __init__( + self, + git_manager: GitManager, + job_run_ocid: str = os.environ.get(CONST_ENV_JOB_RUN_OCID), + ): """Initialize the job runner Parameters @@ -339,101 +363,16 @@ def __init__(self, job_run_ocid: str = ""): For local testing purpose, job run OCID can be set to empty string """ self.job_run_ocid = job_run_ocid - self.git_manager = None + self.git_manager = git_manager self.artifacts = [] self.source_info = {} + super().__init__(code_dir=git_manager.code_dir) - @staticmethod - def setup_python_path(python_path="", code_dir=""): - """Adds additional python paths.""" - if not python_path: - python_path = os.environ.get("PYTHON_PATH", "") - python_paths = python_path.split(os.pathsep) - python_paths.append(code_dir) - for path in python_paths: - python_path = os.path.join(code_dir, path) - if python_path not in sys.path: - sys.path.append(python_path) - print(f"Python Path: {sys.path}") - - @staticmethod - def run_command(command): - # Conda activate - # https://docs.conda.io/projects/conda/en/latest/release-notes.html#id241 - conda_prefix = sys.executable.split("/bin/python", 1)[0] - cmd = f"CONDA_BASE=$(conda info --base) && source $CONDA_BASE/etc/profile.d/conda.sh && conda activate {conda_prefix}; " - cmd += command - process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=os.environ.copy(), shell=True - ) - # Steam the outputs - while True: - output = process.stdout.readline() - if process.poll() is not None and output == b"": - break - if output: - print(output.decode().strip(), flush=True) - time.sleep(0.5) - return process.returncode - - def _run_function(self, module_path: str, entry_function: str, argv: list): - """Runs the entry function in module specified by module path. - - Parameters - ---------- - module_path : str - The path to the module containing the entry function. - entry_function : str - The name of the entry function. - argv : list - Argument list from command line. - This list will be parsed into positional arguments and keyword arguments. - """ - spec = importlib.util.spec_from_file_location("module", module_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - method = getattr(module, entry_function) - - args, kwargs = ArgumentParser(argv).parse() - - print(f"Invoking method: {entry_function} with args: {args}, kwargs: {kwargs}") - method(*args, **kwargs) - - def _run_script(self, module_path: str, arguments: Optional[list] = None): - """Runs the script specified by the user - - Parameters - ---------- - module_path : str - The path to the entry script. - arguments : list, optional - A list of command line arguments, by default None. - """ - # Use -u option to run python so that the outputs will not be buffered. - # This will allow user to see the logs sooner in long running job. - commands = [sys.executable, "-u", module_path] - if arguments: - commands.extend(arguments) - return_code = self.run_command(" ".join(commands)) - if return_code != 0: - # If there is an error, exit the main process with the same return code. - sys.exit(return_code) - - def fetch_code(self): - """Gets the source code from Git repository.""" - print("Beginning Git Clone...") - self.git_manager = ( - GitManager(os.environ.get("GIT_URL")) - .fetch_repo() - .setup_code( - branch=os.environ.get("GIT_BRANCH", None), - commit=os.environ.get("GIT_COMMIT", None), - ) - ) - print("Code Fetch completed.") - return self - - def run(self, argv=None): + def run( + self, + entrypoint: str = os.environ.get(CONST_ENV_ENTRYPOINT), + entry_function: str = os.environ.get(CONST_ENV_ENTRY_FUNC), + ): """Runs the job Parameters @@ -442,86 +381,15 @@ def run(self, argv=None): A list of arguments for the entry script/function, by default None """ - if not argv: - argv = [] - - entry_function = os.environ.get("ENTRY_FUNCTION") - entry_script = os.environ.get("GIT_ENTRYPOINT") self.source_info = { "repo": self.git_manager.repo_url, "commit": self.git_manager.commit, - "module": entry_script, + "module": entrypoint, "method": entry_function if entry_function else "", } - module_path = os.path.join(self.git_manager.code_dir, entry_script) - - if entry_function: - print(f"Running function: {entry_function} in {module_path}...") - self._run_function(module_path, entry_function, argv) - else: - print(f"Running script: {module_path}") - self._run_script(module_path, argv) - return self - - def copy_artifacts(self, output_dir: str, output_uri: dict) -> None: - """Copies the output files to object storage bucket. - - Parameters - ---------- - bucket_info : dict - Containing information for the object storage bucket. - The dictionary should have the following keys: - name: the name of the bucket. - namespace: the namespace of the bucket. - prefix: the prefix for saving the files. - prefix is optional, defaults to Job run ID. - - output_dir : str - Path of the output directory containing files to be copied. - """ - output_dir = os.path.abspath(os.path.expanduser(output_dir)) - if not os.path.exists(output_dir): - print(f"Output directory f{output_dir} not found.") - return - if not output_uri: - print("OUTPUT_URI is not defined. No file is copied.") - return - print(f"Copying files in {output_dir} to {output_uri}...") - parsed = urlparse(output_uri) - bucket_name = parsed.username - namespace = parsed.hostname - prefix = parsed.path - oci_os_client = CredentialManager.init_oci_client(ObjectStorageClient) - - if not prefix: - prefix = "" - prefix = prefix.strip("/") - - for path, _, files in os.walk(output_dir): - for name in files: - file_path = os.path.join(path, name) - - with open(file_path, "rb") as pkf: - # Get the relative path of the file to keep the directory structure - relative_path = os.path.relpath(file_path, output_dir) - if prefix: - file_prefix = os.path.join(prefix, relative_path) - else: - # Save file to bucket root if prefix is empty. - file_prefix = relative_path - - print( - f"Saving {relative_path} to {bucket_name}@{namespace}/{file_prefix}" - ) - - oci_os_client.put_object( - namespace, - bucket_name, - file_prefix, - pkf, - ) - self.artifacts.append(f"{bucket_name}@{namespace}/{file_prefix}") + entrypoint = os.path.join(self.code_dir, entrypoint) + return super().run(entrypoint=entrypoint, entry_function=entry_function) @staticmethod def _raise_for_error(response: requests.Response): @@ -546,7 +414,7 @@ def update_job_run_metadata_with_rest_api( Metadata to be saved as freeform tags. """ endpoint = f"{client.base_client.endpoint}/jobRuns/{self.job_run_ocid}" - print(f"Request endpoint: {endpoint}") + logger.debug("Request endpoint: %s", endpoint) headers = {"accept": "application/json", "content-type": "application/json"} signer = client.base_client.signer # Get the existing tags @@ -569,162 +437,69 @@ def update_job_run_metadata_with_rest_api( ) response = requests.put(endpoint, headers=headers, json=body, auth=signer) self._raise_for_error(response) - # print(response.json()) + logger.debug(response.json()) def save_metadata(self) -> None: """Saves the metadata to job run""" - print("Saving Metdata to job run...") + logger.info("Saving metadata to job run...") tags = {} # Source info for key, val in self.source_info.items(): - print(f"{key} = {val}") + logger.debug("%s = %s", key, val) if val: tags[key] = str(val) # Output files if self.artifacts: prefix = "oci://" + os.path.commonprefix(self.artifacts) - print(f"Job Output: {prefix}") + logger.debug("Job output: %s", prefix) tags["outputs"] = prefix - client = CredentialManager.init_oci_client(DataScienceClient) + client = OCIHelper.init_oci_client(DataScienceClient) oci.retry.DEFAULT_RETRY_STRATEGY.make_retrying_call( self.update_job_run_metadata_with_rest_api, client, tags ) - print("Updated Job Run metadata.") - - -def check_internet(): - """Checks the internet connection by sending GET request to oracle.com""" - print("Checking internet connection...") - try: - response = requests.get("https://oracle.com", timeout=3) - print(f"Request Status Code: {response.status_code}") - response.raise_for_status() - except Exception as ex: - print(str(ex)) - print("Internet is not available!") - - -class ArgumentParser: - """Contains methods for parsing arguments for entry function.""" - - def __init__(self, argument_list: list) -> None: - """Initialize the parser with a list of arguments - - Parameters - ---------- - argument_list : list - A list of arguments. - """ - self.argument_list = argument_list - - @staticmethod - def decode_arg(val): - """Decodes the value of the argument if it is a JSON payload. - - Parameters - ---------- - val : str - The argument value in a string. - - Returns - ------- - Any - None, if the val is None. - String value, if the val is a string but not a JSON payload. - Otherwise, the object after JSON decoded. - """ - if val is None: - return None - try: - return json.loads(val) - except json.decoder.JSONDecodeError: - return val - - @staticmethod - def join_values(value_list): - """Joins the values of a keyword argument. - - Parameters - ---------- - value_list : list - Values in a list of strings. - - Returns - ------- - str or None - The value of the argument as a string. - """ - if value_list: - return " ".join(value_list) - return None - - def parse(self): - """Parses the arguments - - Returns - ------- - (list, dict) - A tuple of positional arguments (list) and keyword arguments (dict). - """ - args = [] - kwargs = {} - parsing_kwargs = False - key = None - val = [] - for arg in self.argument_list: - arg = str(arg) - if len(arg) > 2 and arg.startswith("--"): - if key: - # Save previous key and val - kwargs[key] = self.join_values(val) - parsing_kwargs = True - key = arg[2:] - # Reset val - val = [] - elif parsing_kwargs: - val.append(arg) - else: - args.append(arg) - # Save the last key and val - if key: - kwargs[key] = self.join_values(val) - - args = [self.decode_arg(arg) for arg in args] - kwargs = {k: self.decode_arg(v) for k, v in kwargs.items()} - return args, kwargs + logger.debug("Updated Job Run metadata.") def main(): """The main function for running the job.""" - job_run_ocid = os.environ.get("JOB_RUN_OCID") - print(f"Job Run ID is: {job_run_ocid}") + second_started = time() - check_internet() - runner = JobRunner(job_run_ocid) - with GitSSHKey(os.environ.get("GIT_SECRET_OCID")): - runner.fetch_code().setup_python_path(code_dir=runner.git_manager.code_dir) - runner.run(sys.argv[1:]) + with GitSSHKey(os.environ.get(CONST_ENV_SECRET_OCID)): + git_manager = ( + GitManager(os.environ.get(CONST_ENV_GIT_URL)) + .fetch_repo() + .checkout_code( + branch=os.environ.get(CONST_ENV_GIT_BRANCH, None), + commit=os.environ.get(CONST_ENV_GIT_COMMIT, None), + ) + ) + + runner = GitJobRunner(git_manager) + runner.set_working_dir().setup_python_path().run() # Copy outputs - if "OUTPUT_DIR" in os.environ: - print(f"Found OUTPUT_DIR is configured as: {os.environ['OUTPUT_DIR']}") - runner.copy_artifacts( - output_dir=os.environ["OUTPUT_DIR"], - output_uri=os.environ.get("OUTPUT_URI"), - ) - else: - print("OUTPUT_DIR is not configured. Skipping copy artifacts") + runner.artifacts = OCIHelper.copy_outputs() # Save metadata only if job run OCID is available - if job_run_ocid and "SKIP_METADATA_UPDATE" not in os.environ: + if ( + os.environ.get(CONST_ENV_JOB_RUN_OCID) + and "SKIP_METADATA_UPDATE" not in os.environ + ): try: + # Wait before updating the metadata. + # Job run might still be in the ACCEPTED state shortly after it started, + # Cannot update job run while in ACCEPTED, CANCELLING, DELETED or NEEDS_ATTENTION state + second_elapsed = time() - second_started + if second_elapsed < 90: + sleep(90 - second_elapsed) runner.save_metadata() - except: + except Exception: + logger.error("An error occurred when saving the metadata.") # Allow the job run to finish successfully even if the driver script failed to save the metadata. - traceback.print_exc() + logger.debug(traceback.format_exc()) - print("Job completed.") + logger.info("Job completed.") if __name__ == "__main__": diff --git a/ads/jobs/templates/driver_python.py b/ads/jobs/templates/driver_python.py index 2276df8f0..b83241e49 100644 --- a/ads/jobs/templates/driver_python.py +++ b/ads/jobs/templates/driver_python.py @@ -1,9 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -"""This is a driver script auto-generated by Oracle ADS to run Python script in OCI Data Science Jobs. +""" +This is a driver script auto-generated by Oracle ADS to run Python script in OCI Data Science Jobs. The following environment variables are used: CODE_ENTRYPOINT: @@ -29,335 +30,18 @@ """ -import importlib -import json -import logging -import os -import runpy -import subprocess -import sys -from typing import Optional -from urllib.parse import urlparse - -import oci - - -logger = logging.getLogger(__name__) - - -# Constants -CONST_METADATA = "METADATA" -USER_CODE_DIR = "code" - - -class OCIHelper: - @staticmethod - def init_oci_client(client_class): - """Initializes OCI client with API key or Resource Principal. - - Parameters - ---------- - client_class : - The class of OCI client to be initialized. - """ - if os.environ.get("OCI_RESOURCE_PRINCIPAL_VERSION"): - logger.info( - "Initializing %s with Resource Principal...", client_class.__name__ - ) - client = client_class( - {}, signer=oci.auth.signers.get_resource_principals_signer() - ) - else: - logger.info("Initializing %s with API Key...", {client_class.__name__}) - client = client_class(oci.config.from_file()) - return client - - @staticmethod - def copy_outputs(output_dir: str, output_uri: dict) -> None: - """Copies the output files to object storage bucket. - - Parameters - ---------- - output_dir : str - Path of the output directory containing files to be copied. - output_uri : str - URI of the object storage path to store the output files. - """ - output_dir = os.path.abspath(os.path.expanduser(output_dir)) - if not os.path.exists(output_dir): - logger.error("Output directory %s not found.", output_dir) - return - if not output_uri: - logger.error( - "OUTPUT_URI is not defined in environment variable. No file is copied." - ) - return - logger.debug("Copying files in %s to %s...", output_dir, output_uri) - parsed = urlparse(output_uri) - bucket_name = parsed.username - namespace = parsed.hostname - prefix = parsed.path - oci_os_client = OCIHelper.init_oci_client( - oci.object_storage.ObjectStorageClient - ) - - if not prefix: - prefix = "" - prefix = prefix.strip("/") - - for path, _, files in os.walk(output_dir): - for name in files: - file_path = os.path.join(path, name) - - with open(file_path, "rb") as pkf: - # Get the relative path of the file to keep the directory structure - relative_path = os.path.relpath(file_path, output_dir) - if prefix: - file_prefix = os.path.join(prefix, relative_path) - else: - # Save file to bucket root if prefix is empty. - file_prefix = relative_path - - logger.debug( - f"Saving {relative_path} to {bucket_name}@{namespace}/{file_prefix}" - ) - - oci_os_client.put_object( - namespace, - bucket_name, - file_prefix, - pkf, - ) - - -class JobRunner: - """Contains methods for running the job.""" - - def __init__(self, code_dir: str) -> None: - """Initialize the job runner - - Parameters - ---------- - code_dir : str - the path to the directory containing the user code. - """ - self.code_dir = code_dir - - def setup_python_path(self, python_paths: str): - """Adds additional python paths. - - Parameters - ---------- - python_paths : str - Additional python paths to be added to sys.path. - Multiple paths can be separated by os.pathsep, which is colon(:) for Linux and Mac. - - """ - python_paths = python_paths.split(os.pathsep) - python_paths.append(self.code_dir) - for path in python_paths: - python_path = os.path.abspath(os.path.expanduser(path)) - if python_path not in sys.path: - sys.path.append(python_path) - logger.debug("Python Path: %s", sys.path) - return self - - def _run_function(self, module_path: str, entry_function: str, argv: list): - """Runs the entry function in module specified by module path. - - Parameters - ---------- - module_path : str - The path to the module containing the entry function. - entry_function : str - The name of the entry function. - argv : list - Argument list from command line. - This list will be parsed into positional arguments and keyword arguments. - """ - spec = importlib.util.spec_from_file_location("module", module_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - method = getattr(module, entry_function) - - args, kwargs = ArgumentParser(argv).parse() - - logger.debug( - "Invoking method: %s with args: %s, kwargs: %s", - entry_function, - args, - kwargs, - ) - method(*args, **kwargs) - - def run( - self, - entrypoint: str, - entry_function: Optional[str] = None, - argv: Optional[list] = None, - ): - """Runs the user code - - Parameters - ---------- - entrypoint : str - Path to the file serve as the entrypoint. - entry_function : str, optional - Name of the function in the entrypoint, by default None. - If this is not specified, the entrypoint will be run as a python script. - argv : list, optional - Arguments to be parsed and passed into the entry_function, by default None. - The arguments here are used only when running the entry_function. - When entry_function is None, sys.argv[1:] will be used as command line argument. - - """ - if not argv: - argv = [] - - module_path = entrypoint - - if entry_function: - logger.info(f"Running function: {entry_function} in {module_path}...") - self._run_function(module_path, entry_function, argv) - else: - logger.info(f"Running script: {module_path}") - runpy.run_path(module_path, run_name="__main__") - return self - - -class ArgumentParser: - """Contains methods for parsing arguments for entry function.""" - - def __init__(self, argument_list: list) -> None: - """Initialize the parser with a list of arguments - - Parameters - ---------- - argument_list : list - A list of arguments. - """ - self.argument_list = argument_list - - @staticmethod - def decode_arg(val): - """Decodes the value of the argument if it is a JSON payload. - - Parameters - ---------- - val : str - The argument value in a string. - - Returns - ------- - Any - None, if the val is None. - String value, if the val is a string but not a JSON payload. - Otherwise, the object after JSON decoded. - """ - if val is None: - return None - try: - return json.loads(val) - except json.decoder.JSONDecodeError: - return val - - @staticmethod - def join_values(value_list): - """Joins the values of a keyword argument. - - Parameters - ---------- - value_list : list - Values in a list of strings. - - Returns - ------- - str or None - The value of the argument as a string. - """ - if value_list: - return " ".join(value_list) - return None - - def parse(self): - """Parses the arguments - - Returns - ------- - (list, dict) - A tuple of positional arguments (list) and keyword arguments (dict). - """ - args = [] - kwargs = {} - parsing_kwargs = False - key = None - val = [] - for arg in self.argument_list: - arg = str(arg) - if len(arg) > 2 and arg.startswith("--"): - if key: - # Save previous key and val - kwargs[key] = self.join_values(val) - parsing_kwargs = True - key = arg[2:] - # Reset val - val = [] - elif parsing_kwargs: - val.append(arg) - else: - args.append(arg) - # Save the last key and val - if key: - kwargs[key] = self.join_values(val) - - args = [self.decode_arg(arg) for arg in args] - kwargs = {k: self.decode_arg(v) for k, v in kwargs.items()} - return args, kwargs +try: + # This is used by ADS and testing + from .driver_utils import OCIHelper, JobRunner +except ImportError: + # This is used when the script is in a job run. + from driver_utils import OCIHelper, JobRunner def main(): """The main function for running the job.""" - job_run_ocid = os.environ.get("JOB_RUN_OCID") - logger.info("Job Run ID is: %s", job_run_ocid) - - # Conda unpack - try: - # Ignore the error as conda-unpack may not exists - subprocess.check_output("conda-unpack", stderr=subprocess.STDOUT, shell=True) - except subprocess.CalledProcessError as ex: - logger.debug("conda-unpack exits with non-zero return code %s", ex.returncode) - logger.debug(ex.output) - - code_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), USER_CODE_DIR) - # Change the working diretory to the root of user code - if os.environ.get("WORKING_DIR"): - working_dir = os.path.join(code_dir, os.environ.get("WORKING_DIR")) - else: - working_dir = code_dir - os.chdir(working_dir) - # Add working dir to sys.path - if working_dir not in sys.path: - sys.path.append(working_dir) - - # Run user code - runner = JobRunner(code_dir) - runner.setup_python_path(os.environ.get("PYTHON_PATH", "")).run( - entrypoint=os.environ.get("CODE_ENTRYPOINT"), - entry_function=os.environ.get("ENTRY_FUNCTION"), - argv=sys.argv[1:], - ) - - logger.info("Job completed.") - - # Copy outputs - if "OUTPUT_DIR" in os.environ: - logger.debug("OUTPUT_DIR is: %s", os.environ["OUTPUT_DIR"]) - OCIHelper.copy_outputs( - output_dir=os.environ["OUTPUT_DIR"], - output_uri=os.environ.get("OUTPUT_URI"), - ) - else: - logger.debug("OUTPUT_DIR is not configured. Skipping copy artifacts") + JobRunner().set_working_dir().conda_unpack().setup_python_path().run() + OCIHelper.copy_outputs() if __name__ == "__main__": diff --git a/ads/jobs/templates/driver_utils.py b/ads/jobs/templates/driver_utils.py new file mode 100644 index 000000000..4a063a1eb --- /dev/null +++ b/ads/jobs/templates/driver_utils.py @@ -0,0 +1,500 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import importlib +import json +import logging +import os +import runpy +import subprocess +import sys +import time +import traceback +from typing import List +from urllib.parse import urlparse + + +import oci + + +CONST_ENV_LOG_LEVEL = "OCI_LOG_LEVEL" +CONST_ENV_WORKING_DIR = "WORKING_DIR" +CONST_ENV_CODE_DIR = "CODE_DIR" +CONST_ENV_JOB_RUN_OCID = "JOB_RUN_OCID" +CONST_ENV_PYTHON_PATH = "PYTHON_PATH" +CONST_ENV_ENTRYPOINT = "CODE_ENTRYPOINT" +CONST_ENV_ENTRY_FUNC = "ENTRY_FUNCTION" +CONST_ENV_OUTPUT_DIR = "OUTPUT_DIR" +CONST_ENV_OUTPUT_URI = "OUTPUT_URI" +CONST_ENV_OCI_RP = "OCI_RESOURCE_PRINCIPAL_VERSION" +CONST_ENV_ADS_IAM = "OCI_IAM_TYPE" +CONST_API_KEY = "api_key" + + +DEFAULT_CODE_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + os.environ.get(CONST_ENV_CODE_DIR, "code"), +) + + +def set_log_level(the_logger: logging.Logger): + """Sets the log level of a logger based on the environment variable. + This will also set the log level of logging.lastResort. + + Parameters + ---------- + the_logger : logging.Logger + A logger object. + """ + # Do nothing if env var is not set or is empty/None + if not os.environ.get(CONST_ENV_LOG_LEVEL): + return the_logger + log_level = os.environ.get(CONST_ENV_LOG_LEVEL) + try: + the_logger.setLevel(log_level) + logging.lastResort.setLevel(log_level) + the_logger.info(f"Log level set to {log_level}") + except Exception: + # Catching all exceptions here + # Setting log level should not interrupt the job run even if there is an exception. + the_logger.warning("Failed to set log level.") + the_logger.debug(traceback.format_exc()) + return the_logger + + +logger = logging.getLogger(__name__) +set_log_level(logger) + + +class OCIHelper: + """Contains helper functions to call OCI APIs""" + + @staticmethod + def init_oci_client(client_class): + """Initializes OCI client with API key or Resource Principal. + + Parameters + ---------- + client_class : + The class of OCI client to be initialized. + """ + if ( + os.environ.get(CONST_ENV_ADS_IAM, "").lower() == CONST_API_KEY + or CONST_ENV_OCI_RP not in os.environ + ): + logger.info("Initializing %s with API Key...", {client_class.__name__}) + client = client_class( + oci.config.from_file( + file_location=os.environ.get( + "OCI_CONFIG_LOCATION", oci.config.DEFAULT_LOCATION + ), + profile_name=os.environ.get( + "OCI_CONFIG_PROFILE", oci.config.DEFAULT_PROFILE + ), + ) + ) + else: + logger.info( + "Initializing %s with Resource Principal...", client_class.__name__ + ) + client = client_class( + {}, signer=oci.auth.signers.get_resource_principals_signer() + ) + return client + + @staticmethod + def copy_to_oci_object_storage( + output_dir: str, namespace: str, bucket: str, prefix: str + ) -> List[str]: + """Copies the output files to OCI object storage + + Parameters + ---------- + output_dir : str + Path of the output directory containing files to be copied. + namespace : str + Namespace of the object storage location. + bucket : str + Bucket name of the object storage location. + prefix : str + Prefix (path) of the object storage location. + + Returns + ------- + list + A list of URIs for files that are copied to output_uri + """ + client = OCIHelper.init_oci_client(oci.object_storage.ObjectStorageClient) + + if not prefix: + prefix = "" + prefix = prefix.strip("/") + + copied = [] + for path, _, files in os.walk(output_dir): + for name in files: + file_path = os.path.join(path, name) + # Get the relative path of the file to keep the directory structure + relative_path = os.path.relpath(file_path, output_dir) + if prefix: + file_prefix = os.path.join(prefix, relative_path) + else: + # Save file to bucket root if prefix is empty. + file_prefix = relative_path + + logger.debug( + "Saving %s to %s@%s/%s", + relative_path, + bucket, + namespace, + file_prefix, + ) + + with open(file_path, "rb") as pkf: + client.put_object( + namespace, + bucket, + file_prefix, + pkf, + ) + copied.append(f"{bucket}@{namespace}/{file_prefix}") + return copied + + @staticmethod + def substitute_output_uri(output_uri): + """Expand shell variables of form $var and ${var}. + Unknown variables are left unchanged. + """ + try: + return os.path.expandvars(output_uri) + except Exception: + logger.warning("Failed to expand output URI with environment variables.") + logger.debug(traceback.format_exc()) + # Do nothing if there is an error. + return output_uri + + @staticmethod + def copy_outputs( + output_dir: str = os.environ.get("OUTPUT_DIR"), + output_uri: str = os.environ.get("OUTPUT_URI"), + ) -> List[str]: + """Copies the output files to remote URI. + + No file will be copied if either output_dir or output_uri is empty or None. + + Parameters + ---------- + output_dir : str + Path of the output directory containing files to be copied. + By default, os.environ.get("OUTPUT_DIR") + output_uri : str + URI of the object storage path to store the output files. + By default, os.environ.get("OUTPUT_URI") + + Returns + ------- + list + A list of URIs for files that are copied to output_uri + """ + if not output_dir: + logger.info("OUTPUT_DIR is not defined. No file is copied.") + return + output_dir = os.path.abspath(os.path.expanduser(output_dir)) + if not os.path.exists(output_dir): + logger.error("Output directory %s not found.", output_dir) + return + + if not output_uri: + logger.info("OUTPUT_URI is not defined. No file is copied.") + return + output_uri = OCIHelper.substitute_output_uri(output_uri) + + logger.debug("Copying files in %s to %s...", output_dir, output_uri) + parsed = urlparse(output_uri) + # Only OCI object storage is supported at the moment + + bucket = parsed.username + namespace = parsed.hostname + if not bucket or not namespace: + logger.error( + "Invalid bucket name or namespace in output URI: %s", output_uri + ) + logger.error( + "Output URI should have the format of oci://bucket@namespace/path/to/dir" + ) + return + + prefix = parsed.path + return OCIHelper.copy_to_oci_object_storage( + output_dir, namespace, bucket, prefix + ) + + +class ArgumentParser: + """Contains methods for parsing arguments for entry function.""" + + def __init__(self, argument_list: list) -> None: + """Initialize the parser with a list of arguments + + Parameters + ---------- + argument_list : list + A list of arguments. + """ + self.argument_list = argument_list + + @staticmethod + def decode_arg(val: str): + """Decodes the value of the argument if it is a JSON payload. + + Parameters + ---------- + val : str + The argument value in a string. + + Returns + ------- + Any + None, if the val is None. + String value, if the val is a string but not a JSON payload. + Otherwise, the object after JSON decoded. + """ + if val is None: + return None + try: + return json.loads(val) + except json.decoder.JSONDecodeError: + return val + + @staticmethod + def join_values(value_list: list): + """Joins the values of a keyword argument. + + Parameters + ---------- + value_list : list + Values in a list of strings. + + Returns + ------- + str or None + The value of the argument as a string. + """ + if value_list: + return " ".join(value_list) + return None + + def parse(self): + """Parses the arguments + + Returns + ------- + (list, dict) + A tuple of positional arguments (list) and keyword arguments (dict). + """ + args = [] + kwargs = {} + parsing_kwargs = False + key = None + val = [] + for arg in self.argument_list: + arg = str(arg) + if len(arg) > 2 and arg.startswith("--"): + if key: + # Save previous key and val + kwargs[key] = self.join_values(val) + parsing_kwargs = True + key = arg[2:] + # Reset val + val = [] + elif parsing_kwargs: + val.append(arg) + else: + args.append(arg) + # Save the last key and val + if key: + kwargs[key] = self.join_values(val) + + args = [self.decode_arg(arg) for arg in args] + kwargs = {k: self.decode_arg(v) for k, v in kwargs.items()} + return args, kwargs + + +class JobRunner: + def __init__(self, code_dir: str = DEFAULT_CODE_DIR) -> None: + """Initialize the job runner + + Parameters + ---------- + code_dir : str + The path to the directory containing the user code. + """ + logger.info("Job Run ID is: %s", os.environ.get(CONST_ENV_JOB_RUN_OCID)) + self.code_dir = code_dir + + @staticmethod + def run_command( + command: str, activate_conda: bool = False, level: int = logging.INFO + ) -> int: + """Runs a shell command and logs the outputs with specific log level. + + Parameters + ---------- + command : str + The shell command + activate_conda : bool, optional + Indicate if conda environment should be activated for running the command, by default False + level : int, optional + Logging level for the command outputs, by default logging.INFO + + Returns + ------- + int + The return code of the command. + """ + logger.debug(">>> %s", command) + if activate_conda: + # Conda activate + # https://docs.conda.io/projects/conda/en/latest/release-notes.html#id241 + conda_prefix = sys.executable.split("/bin/python", 1)[0] + cmd = ( + "CONDA_BASE=$(conda info --base) && " + + "source $CONDA_BASE/etc/profile.d/conda.sh && " + + f"conda activate {conda_prefix}; " + + command + ) + else: + cmd = command + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=os.environ.copy(), + shell=True, + ) + # Steam the outputs + while True: + output = process.stdout.readline() + if process.poll() is not None and output == b"": + break + if output: + # logging will flush outputs by default + logger.log(level=level, msg=output.decode().strip()) + time.sleep(0.5) + return process.returncode + + def conda_unpack(self): + if self.run_command("conda-unpack"): + logger.info("conda-unpack exits with non-zero return code.") + return self + + def set_working_dir(self, working_dir: str = os.environ.get(CONST_ENV_WORKING_DIR)): + """Sets the working directory for the job run. + + Parameters + ---------- + working_dir : str, optional + Working directory, by default os.environ.get("WORKING_DIR") + If working_dir is not set, the working dir will be set to the code dir. + If working_dir is a relative path, it will be joined with the code dir. + + """ + if working_dir: + working_dir = os.path.join(self.code_dir, working_dir) + else: + working_dir = self.code_dir + os.chdir(working_dir) + # Add working dir to sys.path + if working_dir not in sys.path: + sys.path.append(working_dir) + return self + + def setup_python_path( + self, python_paths: str = os.environ.get(CONST_ENV_PYTHON_PATH, "") + ): + """Adds additional python paths. + Relative paths are expanded based on the current working directory. + This method should be called after setting the working directory (if needed). + + Parameters + ---------- + python_paths : str + Additional python paths to be added to sys.path, + by default, os.environ.get("PYTHON_PATH", "") + Multiple paths can be separated by os.pathsep, which is colon(:) for Linux and Mac. + + """ + path_list = python_paths.split(os.pathsep) + path_list.append(self.code_dir) + for path in path_list: + python_path = os.path.abspath(os.path.expanduser(path)) + if python_path not in sys.path: + sys.path.append(python_path) + logger.debug("Python Path: %s", sys.path) + return self + + def _run_function(self, module_path: str, entry_function: str, argv: list): + """Runs the entry function in module specified by module path. + + Parameters + ---------- + module_path : str + The path to the module containing the entry function. + entry_function : str + The name of the entry function. + argv : list + Argument list from command line. + This list will be parsed into positional arguments and keyword arguments. + """ + spec = importlib.util.spec_from_file_location("module", module_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + method = getattr(module, entry_function) + + args, kwargs = ArgumentParser(argv).parse() + + logger.debug( + "Invoking method: %s with args: %s, kwargs: %s", + entry_function, + args, + kwargs, + ) + method(*args, **kwargs) + + def run( + self, + entrypoint: str = os.environ.get(CONST_ENV_ENTRYPOINT), + entry_function: str = os.environ.get(CONST_ENV_ENTRY_FUNC), + ): + """Runs the user code + + Parameters + ---------- + entrypoint : str + Path to the file serve as the entrypoint, + by default, os.environ.get("CODE_ENTRYPOINT") + + entry_function : str, optional + Name of the function in the entrypoint, + by default, os.environ.get("ENTRY_FUNCTION"). + If this is not set, the entrypoint will be run as a python script. + + """ + if not entrypoint: + raise ValueError(f"Invalid entrypoint: {str(entrypoint)}") + if entry_function: + logger.info("Running function: %s in %s", entry_function, entrypoint) + self._run_function(entrypoint, entry_function, sys.argv[1:]) + elif entrypoint.endswith(".ipynb"): + from driver_notebook import run_notebook + + logger.info("Running notebook: %s", entrypoint) + # Pass in the absolute path to make sure the working dir is notebook directory + run_notebook(os.path.abspath(os.path.expanduser(entrypoint))) + else: + logger.info("Running script: %s", entrypoint) + runpy.run_path(entrypoint, run_name="__main__") + logger.info("Job run completed.") + return self diff --git a/ads/model/__init__.py b/ads/model/__init__.py index e4c415f9b..cd714e108 100644 --- a/ads/model/__init__.py +++ b/ads/model/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from ads.model.generic_model import GenericModel, ModelState @@ -14,10 +14,15 @@ from ads.model.framework.tensorflow_model import TensorFlowModel from ads.model.framework.xgboost_model import XGBoostModel from ads.model.framework.spark_model import SparkPipelineModel +from ads.model.framework.huggingface_model import HuggingFacePipelineModel from ads.model.deployment.model_deployer import ModelDeployer from ads.model.deployment.model_deployment import ModelDeployment from ads.model.deployment.model_deployment_properties import ModelDeploymentProperties + +from ads.model.serde.common import SERDE +from ads.model.serde.model_input import ModelInputSerializer + from ads.model.model_version_set import ModelVersionSet, experiment from ads.model.service.oci_datascience_model_version_set import ( ModelVersionSetNotExists, diff --git a/ads/model/artifact.py b/ads/model/artifact.py index 1da4afb95..d991adeef 100644 --- a/ads/model/artifact.py +++ b/ads/model/artifact.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import fnmatch @@ -12,16 +12,20 @@ import tempfile import uuid from typing import Dict, Optional, Tuple - from ads.common import auth as authutil from ads.common import logger, utils from ads.config import CONDA_BUCKET_NAME, CONDA_BUCKET_NS from ads.model.runtime.env_info import EnvInfo, InferenceEnvInfo, TrainingEnvInfo from ads.model.runtime.runtime_info import RuntimeInfo from jinja2 import Environment, PackageLoader +import warnings +from ads import __version__ +from datetime import datetime MODEL_ARTIFACT_VERSION = "3.0" REQUIRED_ARTIFACT_FILES = ("runtime.yaml", "score.py") +SCORE_VERSION = "1.0" +ADS_VERSION = __version__ class ArtifactNestedFolderError(Exception): @@ -161,6 +165,7 @@ def prepare_runtime_yaml( force_overwrite: bool = False, namespace: str = CONDA_BUCKET_NS, bucketname: str = CONDA_BUCKET_NAME, + auth: dict = None, ) -> None: """Generate a runtime yaml file and save it to the artifact directory. @@ -182,9 +187,13 @@ def prepare_runtime_yaml( force_overwrite : (bool, optional). Defaults to False. Whether to overwrite existing files. namespace: (str, optional) - The namespace of region. + The namespace of region. Defaults to environment variable CONDA_BUCKET_NS. bucketname: (str, optional) - The bucketname of service pack. + The bucketname of service pack. Defaults to environment variable CONDA_BUCKET_NAME. + auth :(Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. Raises ------ @@ -203,6 +212,7 @@ def prepare_runtime_yaml( conda_pack=inference_conda_env, bucketname=bucketname, namespace=namespace, + auth=auth, ) if training_conda_env: @@ -211,6 +221,7 @@ def prepare_runtime_yaml( conda_pack=training_conda_env, bucketname=bucketname, namespace=namespace, + auth=auth, ) else: training_conda_env = TrainingEnvInfo() @@ -228,7 +239,7 @@ def prepare_runtime_yaml( or runtime_info.model_deployment.inference_conda_env.inference_python_version.strip() == "" ): - raise ValueError( + warnings.warn( "Cannot automatically detect the inference python version. `inference_python_version` must be provided." ) runtime_file_path = os.path.join(self.artifact_dir, "runtime.yaml") @@ -243,7 +254,11 @@ def prepare_runtime_yaml( @staticmethod def _populate_env_info( - clss: EnvInfo, conda_pack: str, bucketname: str = None, namespace: str = None + clss: EnvInfo, + conda_pack: str, + bucketname: str = None, + namespace: str = None, + auth: dict = None, ) -> "EnvInfo": """Populates the Training/InferenceEnvInfo instance. @@ -259,6 +274,10 @@ def _populate_env_info( The namespace of region. bucketname: (str, optional) The bucketname of service pack. + auth: (Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. Returns ------- @@ -268,7 +287,7 @@ def _populate_env_info( if conda_pack.startswith("oci://"): return clss.from_path(conda_pack) return clss.from_slug( - env_slug=conda_pack, bucketname=bucketname, namespace=namespace + env_slug=conda_pack, bucketname=bucketname, namespace=namespace, auth=auth ) def prepare_score_py( @@ -284,6 +303,7 @@ def prepare_score_py( The file name of the serialized model. **kwargs: (dict) use_torch_script: bool + data_deserializer: str Returns ------- @@ -307,10 +327,15 @@ def prepare_score_py( ): raise FileExistsError(f"{jinja_template_filename}.jinja2 does not exists.") scorefn_template = self._env.get_template(f"{jinja_template_filename}.jinja2") + time_suffix = datetime.today().strftime("%Y%m%d_%H%M%S") + context = { "model_file_name": self.model_file_name, - "use_torch_script": kwargs.get("use_torch_script", False), + "SCORE_VERSION": SCORE_VERSION, + "ADS_VERSION": ADS_VERSION, + "time_created": time_suffix, } + context.update(kwargs) with open(os.path.join(self.artifact_dir, "score.py"), "w") as sfl: sfl.write(scorefn_template.render(context)) diff --git a/ads/model/artifact_downloader.py b/ads/model/artifact_downloader.py index 39634e30e..85d2b7669 100644 --- a/ads/model/artifact_downloader.py +++ b/ads/model/artifact_downloader.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import os @@ -13,7 +13,7 @@ from zipfile import ZipFile from ads.common import utils -from ads.model.common.utils import extract_region +from ads.common.utils import extract_region from ads.model.service.oci_datascience_model import OCIDataScienceModel @@ -139,8 +139,8 @@ def __init__( super().__init__( dsc_model=dsc_model, target_dir=target_dir, force_overwrite=force_overwrite ) - self.auth = auth - self.region = region or extract_region() + self.auth = auth or dsc_model.auth + self.region = region or extract_region(self.auth) self.bucket_uri = bucket_uri self.overwrite_existing_artifact = overwrite_existing_artifact self.remove_existing_artifact = remove_existing_artifact diff --git a/ads/model/artifact_uploader.py b/ads/model/artifact_uploader.py index f7690534e..b40840708 100644 --- a/ads/model/artifact_uploader.py +++ b/ads/model/artifact_uploader.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import os @@ -9,7 +9,6 @@ from abc import ABC, abstractmethod from typing import Dict, Optional -from ads.common import auth as authutil from ads.common import utils from ads.model.common import utils as model_utils from ads.model.service.oci_datascience_model import OCIDataScienceModel @@ -63,7 +62,7 @@ def _prepare_artiact_tmp_zip(self) -> str: Parameters ---------- - progress: (Union[TqdmProgressBar, DummyProgressBar], optional). Defaults to `None`. + progress: (TqdmProgressBar, optional). Defaults to `None`. The progress indicator. Returns @@ -101,7 +100,7 @@ def _upload(self): """Uploads model artifacts to the model catalog.""" self.progress.update("Uploading model artifacts to the catalog") with open(self.artifact_zip_path, "rb") as file_data: - self.dsc_model.create_model_artifact(file_data.read()) + self.dsc_model.create_model_artifact(file_data) class LargeArtifactUploader(ArtifactUploader): @@ -146,8 +145,8 @@ def __init__( raise ValueError("The `bucket_uri` must be provided.") super().__init__(dsc_model=dsc_model, artifact_path=artifact_path) - self.auth = auth or authutil.default_signer() - self.region = region or model_utils.extract_region() + self.auth = auth or dsc_model.auth + self.region = region or utils.extract_region(self.auth) self.bucket_uri = bucket_uri self.overwrite_existing_artifact = overwrite_existing_artifact self.remove_existing_artifact = remove_existing_artifact diff --git a/ads/model/common/utils.py b/ads/model/common/utils.py index d39d0960d..bfcb2723c 100644 --- a/ads/model/common/utils.py +++ b/ads/model/common/utils.py @@ -1,18 +1,22 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import json import os import tempfile -import yaml -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional from zipfile import ZipFile +import yaml + from ads.common import utils -from ads.config import OCI_REGION_METADATA + + +DEPRECATE_AS_ONNX_WARNING = "This attribute `as_onnx` will be deprecated in the future. You can choose specific format by setting `model_save_serializer`." +DEPRECATE_USE_TORCH_SCRIPT_WARNING = "This attribute `use_torch_script` will be deprecated in the future. You can choose specific format by setting `model_save_serializer`." def _extract_locals( @@ -90,23 +94,6 @@ def fetch_manifest_from_conda_location(env_location: str): return manifest -def extract_region() -> Union[str, None]: - """Extracts region information from the environment variables. - - Returns - ------- - Union[str, None] - The region identifier. For example: `us-ashburn-1`. - Returns `None` if region not extracted. - """ - region = None - try: - region = json.loads(OCI_REGION_METADATA)["regionIdentifier"] - except: - pass - return region - - def zip_artifact(artifact_dir: str) -> str: """Prepares model artifacts ZIP archive. diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 058725d15..7e543ac63 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -495,7 +495,7 @@ def with_artifact(self, uri: str): ---------- uri: str Path to artifact directory or to the ZIP archive. - It could contain a seriliazed model(required) as well as any files needed for deployment. + It could contain a serialized model(required) as well as any files needed for deployment. The content of the source folder will be zipped and uploaded to the model catalog. Examples @@ -544,22 +544,24 @@ def create(self, **kwargs) -> "DataScienceModel": In addition can be also provided the attributes listed below. - - bucket_uri: (str, optional). Defaults to None. + bucket_uri: (str, optional). Defaults to None. The OCI Object Storage URI where model artifacts will be copied to. The `bucket_uri` is only necessary for uploading large artifacts which size is greater than 2GB. Example: `oci://@/prefix/`. - - overwrite_existing_artifact: (bool, optional). Defaults to `True`. + overwrite_existing_artifact: (bool, optional). Defaults to `True`. Overwrite target bucket artifact if exists. - - remove_existing_artifact: (bool, optional). Defaults to `True`. + remove_existing_artifact: (bool, optional). Defaults to `True`. Wether artifacts uploaded to object storage bucket need to be removed or not. - - region: (str, optional). Defaults to `None`. + region: (str, optional). Defaults to `None`. The destination Object Storage bucket region. By default the value will be extracted from the `OCI_REGION_METADATA` environment variable. - - auth: (Dict, optional). Defaults to `None`. - The default authetication is set using `ads.set_auth` API. + auth: (Dict, optional). Defaults to `None`. + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + timeout: (int, optional). Defaults to 10 seconds. + The connection timeout in seconds for the client. Returns ------- @@ -604,6 +606,7 @@ def create(self, **kwargs) -> "DataScienceModel": remove_existing_artifact=kwargs.pop("remove_existing_artifact", True), region=kwargs.pop("region", None), auth=kwargs.pop("auth", None), + timeout=kwargs.pop("timeout", None), ) # Sync up model @@ -619,6 +622,7 @@ def upload_artifact( region: Optional[str] = None, overwrite_existing_artifact: Optional[bool] = True, remove_existing_artifact: Optional[bool] = True, + timeout: Optional[int] = None, ) -> None: """Uploads model artifacts to the model catalog. @@ -629,7 +633,7 @@ def upload_artifact( The `bucket_uri` is only necessary for uploading large artifacts which size is greater than 2GB. Example: `oci://@/prefix/`. auth: (Dict, optional). Defaults to `None`. - The default authetication is set using `ads.set_auth` API. + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. @@ -640,6 +644,8 @@ def upload_artifact( Overwrite target bucket artifact if exists. remove_existing_artifact: (bool, optional). Defaults to `True`. Wether artifacts uploaded to object storage bucket need to be removed or not. + timeout: (int, optional). Defaults to 10 seconds. + The connection timeout in seconds for the client. """ # Upload artifact to the model catalog if not self.artifact: @@ -649,6 +655,13 @@ def upload_artifact( ) return + if timeout: + self.dsc_model._client = None + self.dsc_model.__class__.kwargs = { + **(self.dsc_model.__class__.kwargs or {}), + "timeout": timeout, + } + if bucket_uri or utils.folder_size(self.artifact) > _MAX_ARTIFACT_SIZE_IN_BYTES: if not bucket_uri: raise ModelArtifactSizeError( @@ -666,7 +679,8 @@ def upload_artifact( ) else: artifact_uploader = SmallArtifactUploader( - dsc_model=self.dsc_model, artifact_path=self.artifact + dsc_model=self.dsc_model, + artifact_path=self.artifact, ) artifact_uploader.upload() @@ -680,6 +694,7 @@ def download_artifact( region: Optional[str] = None, overwrite_existing_artifact: Optional[bool] = True, remove_existing_artifact: Optional[bool] = True, + timeout: Optional[int] = None, ): """Downloads model artifacts from the model catalog. @@ -688,7 +703,7 @@ def download_artifact( target_dir: str The target location of model artifacts. auth: (Dict, optional). Defaults to `None`. - The default authetication is set using `ads.set_auth` API. + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. @@ -705,6 +720,8 @@ def download_artifact( Overwrite target bucket artifact if exists. remove_existing_artifact: (bool, optional). Defaults to `True`. Wether artifacts uploaded to object storage bucket need to be removed or not. + timeout: (int, optional). Defaults to 10 seconds. + The connection timeout in seconds for the client. Raises ------ @@ -719,6 +736,13 @@ def download_artifact( ) return + if timeout: + self.dsc_model._client = None + self.dsc_model.__class__.kwargs = { + **(self.dsc_model.__class__.kwargs or {}), + "timeout": timeout, + } + artifact_info = self.dsc_model.get_artifact_info() artifact_size = int(artifact_info.get("content-length")) if not bucket_uri and artifact_size > _MAX_ARTIFACT_SIZE_IN_BYTES: @@ -886,7 +910,7 @@ def sync(self): return self._update_from_oci_dsc_model(OCIDataScienceModel.from_id(self.id)) def _init_complex_attributes(self): - """Reinitiates complex attributes.""" + """Initiates complex attributes.""" self.with_custom_metadata_list(self.custom_metadata_list) self.with_defined_metadata_list(self.defined_metadata_list) self.with_provenance_metadata(self.provenance_metadata) @@ -1035,10 +1059,6 @@ def _load_default_properties(self) -> Dict: return defaults - def __repr__(self) -> str: - """Displays the object as YAML.""" - return self.to_yaml() - def __getattr__(self, item): if f"with_{item}" in self.__dir__(): return self.get_spec(item) diff --git a/ads/model/deployment/common/utils.py b/ads/model/deployment/common/utils.py index 39228deda..fb02ec8b7 100644 --- a/ads/model/deployment/common/utils.py +++ b/ads/model/deployment/common/utils.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """Utilities used by the model deployment package """ @@ -21,14 +21,12 @@ import oci import fsspec from oci.data_science.models import CreateModelDetails -from ads.common.auth import default_signer -from ads.common.oci_client import OCIClientFactory +from ads.common import auth, oci_client from ads.common.decorator.runtime_dependency import ( runtime_dependency, OptionalDependency, ) -from ads.model.common.utils import _is_json_serializable -from .progress_bar import TqdmProgressBar, DummyProgressBar +from ads.config import COMPARTMENT_OCID, PROJECT_OCID logger = logging.getLogger(__name__) @@ -107,6 +105,7 @@ def send_request( dry_run: bool = False, is_json_payload: bool = False, header: dict = {}, + **kwargs, ): """Sends the data to the predict endpoint. @@ -145,24 +144,6 @@ def send_request( return requests.post(endpoint, **request_kwargs).json() -def get_progress_bar(max_progress, description="Initializing"): - """get_progress_bar return an instance of ProgressBar, sensitive to the runtime environment - - Args: - max_progress (int): Progress bar max - description (str, optional): Progress bar description (defaults to "Initializing") - - Returns: - An instance of ProgressBar. Either a DummyProgressBar (non-notebook) or TqdmProgressBar - (notebook environement) - """ - - if is_notebook(): # pragma: no cover - return TqdmProgressBar(max_progress, description=description, verbose=False) - else: - return DummyProgressBar() - - # State Constants class State(Enum): ACTIVE = auto() @@ -214,9 +195,9 @@ class OCIClientManager: def __init__(self, config=None) -> None: if not config: - config = default_signer() + config = auth.default_signer() self.config = config - self.ds_client = OCIClientFactory(**config).data_science + self.ds_client = oci_client.OCIClientFactory(**config).data_science self.ds_composite_client = ( oci.data_science.DataScienceClientCompositeOperations(self.ds_client) ) @@ -267,6 +248,7 @@ def prepare_artifact(self, model_uri: str, properties: Dict) -> str: Returns: str: model ocid """ + properties_dict = {} if properties: properties_dict = ( properties @@ -307,10 +289,18 @@ def _upload_artifact(self, model_zip: str, properties: dict) -> str: Returns: str: model ocid """ + project_id = properties.get("project_id", PROJECT_OCID) + compartment_id = properties.get("compartment_id", COMPARTMENT_OCID) + + if not project_id or not compartment_id: + raise ValueError( + "Both `project_id` and `compartment_id` need to be provided. You can pass them through kwargs or `ModelDeploymentProperties` object." + ) + create_model_details = CreateModelDetails( display_name=properties.get("display_name", None), - project_id=properties["project_id"], - compartment_id=properties["compartment_id"], + project_id=project_id, + compartment_id=compartment_id, ) model = self.ds_client.create_model(create_model_details).data diff --git a/ads/model/deployment/model_deployer.py b/ads/model/deployment/model_deployer.py index 802eced53..3f3b324ff 100644 --- a/ads/model/deployment/model_deployer.py +++ b/ads/model/deployment/model_deployer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """ APIs to interact with Oracle's Model Deployment service. @@ -38,6 +38,7 @@ >>> deployer.list_deployments() # Optionally pass in a status """ from typing import Dict, Union +import warnings import oci.pagination import pandas as pd @@ -93,6 +94,11 @@ def __init__(self, config: dict = None, ds_client: DataScienceClient = None): ds_client: oci.data_science.data_science_client.DataScienceClient The Oracle DataScience client. """ + if config: + warnings.warn( + "`config` will be deprecated in 3.0.0 and will be ignored now. Please use `ads.set_auth()` to config the auth information." + ) + self.ds_client = None self.ds_composite_client = None @@ -103,11 +109,7 @@ def __init__(self, config: dict = None, ds_client: DataScienceClient = None): ) if not (self.ds_client != None and self.ds_composite_client != None): - if not config: - config = default_signer() - self.config = config - - self.client_manager = OCIClientManager(config) + self.client_manager = OCIClientManager() self.ds_client = self.client_manager.ds_client self.ds_composite_client = self.client_manager.ds_composite_client @@ -143,9 +145,11 @@ def deploy( ModelDeployment A ModelDeployment instance. """ + warnings.warn( + "Make sure to provide `compartment_id`, `project_id`, `instance_count`, `instance_shape`, `memory_in_gbs` and `ocpus` either through `kwargs` or `properties`" + ) model_deployment = ModelDeployment( properties, - config=self.config, **kwargs, ) return model_deployment.deploy( @@ -187,6 +191,9 @@ def deploy_from_model_uri( A ModelDeployment instance """ + warnings.warn( + "Make sure to provide `compartment_id`, `project_id`, `instance_count`, `instance_shape`, `memory_in_gbs` and `ocpus` either through `kwargs` or `properties`" + ) if properties: model_id = self.client_manager.prepare_artifact( model_uri=model_uri, properties=properties @@ -275,9 +282,11 @@ def get_model_deployment(self, model_deployment_id: str) -> ModelDeployment: ).data model_deployment_object = ModelDeployment( oci_model_deployment_object, - config=self.config, ) - return model_deployment_object + model_deployment_object.set_spec( + model_deployment_object.CONST_ID, oci_model_deployment_object.id + ) + return model_deployment_object.sync() except Exception as e: utils.get_logger().error( "Getting model deployment failed with error: %s", e @@ -382,10 +391,13 @@ def list_deployments(self, status=None, compartment_id=None, **kwargs) -> list: self.ds_client.list_model_deployments, compartment_id, **kwargs ).data - return [ - ModelDeployment(deployment, config=self.config) - for deployment in deployments - ] + result = [] + for deployment in deployments: + model_deployment = ModelDeployment(deployment) + model_deployment.set_spec(model_deployment.CONST_ID, deployment.id) + result.append(model_deployment.sync()) + + return result def show_deployments( self, diff --git a/ads/model/deployment/model_deployment.py b/ads/model/deployment/model_deployment.py index 52671097d..2ca896cb4 100644 --- a/ads/model/deployment/model_deployment.py +++ b/ads/model/deployment/model_deployment.py @@ -1,31 +1,57 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import collections +import copy import datetime -import json +import oci +import warnings import time -from typing import Dict, Union, List, Any +from typing import Dict, List, Union, Any import oci.loggingsearch +from ads.common import auth as authutil import pandas as pd -from ads.common.auth import default_signer -from ads.common.data_serializer import InputDataSerializer -from ads.common.oci_client import OCIClientFactory +from ads.model.serde.model_input import JsonModelInputSERDE +from ads.common import auth, oci_client from ads.common.oci_logging import ( + LOG_INTERVAL, LOG_RECORDS_LIMIT, ConsolidatedLog, OCILog, ) +from ads.config import COMPARTMENT_OCID, PROJECT_OCID +from ads.jobs.builders.base import Builder +from ads.jobs.builders.infrastructure.utils import get_value from ads.model.common.utils import _is_json_serializable from ads.model.deployment.common.utils import send_request +from ads.model.deployment.model_deployment_infrastructure import ( + MODEL_DEPLOYMENT_INFRASTRUCTURE_TYPE, + ModelDeploymentInfrastructure, +) +from ads.model.deployment.model_deployment_runtime import ( + ModelDeploymentCondaRuntime, + ModelDeploymentContainerRuntime, + ModelDeploymentRuntime, + ModelDeploymentRuntimeType, + OCIModelDeploymentRuntimeType, +) +from ads.model.service.oci_datascience_model_deployment import ( + OCIDataScienceModelDeployment, +) +from ads.common import utils as ads_utils from .common import utils from .common.utils import OCIClientManager, State from .model_deployment_properties import ModelDeploymentProperties +from oci.data_science.models import ( + LogDetails, + CreateModelDeploymentDetails, + UpdateModelDeploymentDetails, +) DEFAULT_WAIT_TIME = 1200 DEFAULT_POLL_INTERVAL = 10 @@ -35,17 +61,29 @@ DEFAULT_RETRYING_REQUEST_ATTEMPTS = 3 TERMINAL_STATES = [State.ACTIVE, State.FAILED, State.DELETED, State.INACTIVE] +MODEL_DEPLOYMENT_KIND = "deployment" +MODEL_DEPLOYMENT_TYPE = "modelDeployment" + class ModelDeploymentLogType: PREDICT = "predict" ACCESS = "access" +class ModelDeploymentMode: + HTTPS = "HTTPS_ONLY" + STREAM = "STREAM_ONLY" + + class LogNotConfiguredError(Exception): pass -class ModelDeployment: +class ModelDeploymentFailedError(Exception): + pass + + +class ModelDeployment(Builder): """ A class used to represent a Model Deployment. @@ -59,18 +97,31 @@ class ModelDeployment: Workflow request id workflow_steps: (int) The number of steps in the workflow - url: (str) - The model deployment url endpoint - ds_client: (DataScienceClient) - The data science client used by model deployment - ds_composite_client: (DataScienceCompositeClient) - The composite data science client used by the model deployment - workflow_req_id: (str) - Workflow request id - model_deployment_id: (str) - model deployment id + dsc_model_deployment: (OCIDataScienceModelDeployment) + The OCIDataScienceModelDeployment instance. state: (State) Returns the deployment state of the current Model Deployment object + created_by: (str) + The user that creates the model deployment + lifecycle_state: (str) + Model deployment lifecycle state + lifecycle_details: (str) + Model deployment lifecycle details + time_created: (datetime) + The time when the model deployment is created + display_name: (str) + Model deployment display name + description: (str) + Model deployment description + freeform_tags: (dict) + Model deployment freeform tags + defined_tags: (dict) + Model deployment defined tags + runtime: (ModelDeploymentRuntime) + Model deployment runtime + infrastructure: (ModelDeploymentInfrastructure) + Model deployment infrastructure + Methods ------- @@ -84,17 +135,127 @@ class ModelDeployment: Activates a model deployment deactivate(wait_for_completion, max_wait_time, poll_interval) Deactivates a model deployment - list_workflow_logs() - Returns a list of the steps involved in deploying a model + list(status, compartment_id, project_id, **kwargs) + List model deployment within given compartment and project. + with_display_name(display_name) + Sets model deployment display name + with_description(description) + Sets model deployment description + with_freeform_tags(freeform_tags) + Sets model deployment freeform tags + with_defined_tags(defined_tags) + Sets model deployment defined tags + with_runtime(self, runtime) + Sets model deployment runtime + with_infrastructure(self, infrastructure) + Sets model deployment infrastructure + from_dict(obj_dict) + Deserializes model deployment instance from dict + from_id(id) + Loads model deployment instance from ocid + sync() + Updates the model deployment instance from backend + + + Examples + -------- + Build model deployment from builder apis: + >>> ds_model_deployment = (ModelDeployment() + ... .with_display_name("TestModelDeployment") + ... .with_description("Testing the test model deployment") + ... .with_freeform_tags(tag1="val1", tag2="val2") + ... .with_infrastructure( + ... (ModelDeploymentInfrastructure() + ... .with_project_id() + ... .with_compartment_id() + ... .with_shape_name("VM.Standard.E4.Flex") + ... .with_shape_config_details( + ... ocpus=1, + ... memory_in_gbs=16 + ... ) + ... .with_replica(1) + ... .with_bandwidth_mbps(10) + ... .with_web_concurrency(10) + ... .with_access_log( + ... log_group_id=, + ... log_id= + ... ) + ... .with_predict_log( + ... log_group_id=, + ... log_id= + ... )) + ... ) + ... .with_runtime( + ... (ModelDeploymentContainerRuntime() + ... .with_image() + ... .with_image_digest() + ... .with_entrypoint() + ... .with_server_port() + ... .with_health_check_port() + ... .with_env({"key":"value"}) + ... .with_deployment_mode("HTTPS_ONLY") + ... .with_model_uri()) + ... ) + ... ) + >>> ds_model_deployment.deploy() + >>> ds_model_deployment.status + >>> ds_model_deployment.with_display_name("new name").update() + >>> ds_model_deployment.deactivate() + >>> ds_model_deployment.sync() + >>> ds_model_deployment.list(status="ACTIVE") + >>> ds_model_deployment.delete() + + Build model deployment from yaml + >>> ds_model_deployment = ModelDeployment.from_yaml(uri=) """ + _PREFIX = "datascience_model_deployment" + + CONST_ID = "id" + CONST_CREATED_BY = "createdBy" + CONST_DISPLAY_NAME = "displayName" + CONST_DESCRIPTION = "description" + CONST_FREEFORM_TAG = "freeformTags" + CONST_DEFINED_TAG = "definedTags" + CONST_MODEL_DEPLOYMENT_URL = "modelDeploymentUrl" + CONST_INFRASTRUCTURE = "infrastructure" + CONST_RUNTIME = "runtime" + CONST_LIFECYCLE_STATE = "lifecycleState" + CONST_LIFECYCLE_DETAILS = "lifecycleDetails" + CONST_TIME_CREATED = "timeCreated" + + attribute_map = { + CONST_ID: "id", + CONST_CREATED_BY: "created_by", + CONST_DISPLAY_NAME: "display_name", + CONST_DESCRIPTION: "description", + CONST_FREEFORM_TAG: "freeform_tags", + CONST_DEFINED_TAG: "defined_tags", + CONST_MODEL_DEPLOYMENT_URL: "model_deployment_url", + CONST_INFRASTRUCTURE: "infrastructure", + CONST_RUNTIME: "runtime", + CONST_LIFECYCLE_STATE: "lifecycle_state", + CONST_LIFECYCLE_DETAILS: "lifecycle_details", + CONST_TIME_CREATED: "time_created", + } + + initialize_spec_attributes = [ + "display_name", + "description", + "freeform_tags", + "defined_tags", + "infrastructure", + "runtime", + ] + model_input_serializer = JsonModelInputSERDE() + def __init__( self, properties: Union[ModelDeploymentProperties, Dict] = None, config: Dict = None, - workflow_req_id: str = None, model_deployment_id: str = None, model_deployment_url: str = "", + spec: Dict = None, **kwargs, ): """Initializes a ModelDeployment object. @@ -108,29 +269,51 @@ def __init__( ADS auth dictionary for OCI authentication. This can be generated by calling `ads.common.auth.api_keys()` or `ads.common.auth.resource_principal()`. If this is `None` then the `ads.common.default_signer(client_kwargs)` will be used. - workflow_req_id: (str, optional). Defaults to None. - Workflow request id. model_deployment_id: (str, optional). Defaults to None. Model deployment OCID. model_deployment_url: (str, optional). Defaults to empty string. Model deployment url. + spec: (dict, optional). Defaults to None. + Model deployment spec. kwargs: - Keyword arguments for initializing `ModelDeploymentProperties`. + Keyword arguments for initializing `ModelDeploymentProperties` or `ModelDeployment`. """ - if config is None: - utils.get_logger().info("Using default configuration.") - config = default_signer() + if spec and properties: + raise ValueError( + "You can only pass in either `spec` or `properties` to initialize model deployment instance." + ) + + if config: + warnings.warn( + "`config` will be deprecated in 3.0.0 and will be ignored now. Please use `ads.set_auth()` to config the auth information." + ) - # self.config is ADS auth dictionary for OCI authentication. - self.config = config + if properties: + warnings.warn( + "`properties` will be deprecated in 3.0.0. Please use `spec` or the builder pattern to initialize model deployment instance." + ) + + if model_deployment_url or model_deployment_id: + warnings.warn( + "`model_deployment_url` and `model_deployment_id` will be deprecated in 3.0.0 and will be ignored now. These two fields will be auto-populated from the service side." + ) + + initialize_spec = {} + initialize_spec_kwargs = {} + if spec: + initialize_spec = spec + initialize_spec_kwargs = self._extract_spec_kwargs(**kwargs) + elif not properties and not spec: + if self.CONST_INFRASTRUCTURE in kwargs or self.CONST_RUNTIME in kwargs: + initialize_spec_kwargs = self._extract_spec_kwargs(**kwargs) + + super().__init__(spec=initialize_spec, **initialize_spec_kwargs) self.properties = ( properties if isinstance(properties, ModelDeploymentProperties) - else ModelDeploymentProperties( - oci_model_deployment=properties, config=self.config, **kwargs - ) + else ModelDeploymentProperties(oci_model_deployment=properties, **kwargs) ) self.current_state = ( @@ -138,30 +321,256 @@ def __init__( if self.properties.lifecycle_state else State.UNKNOWN ) - self.url = ( - model_deployment_url - if model_deployment_url - else self.properties.model_deployment_url - ) - self.model_deployment_id = ( - model_deployment_id if model_deployment_id else self.properties.id - ) - self.workflow_state_progress = [] - self.workflow_steps = DEFAULT_WORKFLOW_STEPS + self._access_log = None + self._predict_log = None + self.dsc_model_deployment = OCIDataScienceModelDeployment() - client_manager = OCIClientManager(config) - self.ds_client = client_manager.ds_client - self.ds_composite_client = client_manager.ds_composite_client - self.workflow_req_id = workflow_req_id + @property + def kind(self) -> str: + """The kind of the object as showing in YAML. - if self.ds_client: - self.log_search_client = OCIClientFactory(**self.config).create_client( - oci.loggingsearch.LogSearchClient - ) + Returns + ------- + str + deployment + """ + return MODEL_DEPLOYMENT_KIND - self._access_log = None - self._predict_log = None + @property + def type(self) -> str: + """The type of the object as showing in YAML. + + Returns + ------- + str + deployment + """ + return MODEL_DEPLOYMENT_TYPE + + @property + def model_deployment_id(self) -> str: + """The model deployment ocid. + + Returns + ------- + str + The model deployment ocid. + """ + return self.get_spec(self.CONST_ID, None) + + @property + def created_by(self) -> str: + """The user that creates the model deployment. + + Returns + ------- + str + The user that creates the model deployment. + """ + return self.get_spec(self.CONST_CREATED_BY, None) + + @property + def url(self) -> str: + """Model deployment url. + + Returns + ------- + str + Model deployment url. + """ + return self.get_spec(self.CONST_MODEL_DEPLOYMENT_URL, None) + + @property + def lifecycle_state(self) -> str: + """Model deployment lifecycle state. + + Returns + ------- + str + Model deployment lifecycle state. + """ + return self.get_spec(self.CONST_LIFECYCLE_STATE, None) + + @property + def lifecycle_details(self) -> str: + """Model deployment lifecycle details. + + Returns + ------- + str + Model deployment lifecycle details. + """ + return self.get_spec(self.CONST_LIFECYCLE_DETAILS, None) + + @property + def time_created(self) -> datetime: + """The time when the model deployment is created. + + Returns + ------- + datetime + The time when the model deployment is created. + """ + return self.get_spec(self.CONST_TIME_CREATED, None) + + @property + def display_name(self) -> str: + """Model deployment display name. + + Returns + ------- + str + Model deployment display name. + """ + return self.get_spec(self.CONST_DISPLAY_NAME, None) + + def with_display_name(self, display_name: str) -> "ModelDeployment": + """Sets the name of model deployment. + + Parameters + ---------- + display_name: str + The name of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self.set_spec(self.CONST_DISPLAY_NAME, display_name) + + @property + def description(self) -> str: + """Model deployment description. + + Returns + ------- + str + Model deployment description. + """ + return self.get_spec(self.CONST_DESCRIPTION, None) + + def with_description(self, description: str) -> "ModelDeployment": + """Sets the description of model deployment. + + Parameters + ---------- + description: str + The description of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self.set_spec(self.CONST_DESCRIPTION, description) + + @property + def freeform_tags(self) -> Dict: + """Model deployment freeform tags. + + Returns + ------- + Dict + Model deployment freeform tags. + """ + return self.get_spec(self.CONST_FREEFORM_TAG, {}) + + def with_freeform_tags(self, **kwargs) -> "ModelDeployment": + """Sets the freeform tags of model deployment. + + Parameters + ---------- + kwargs + The freeform tags of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self.set_spec(self.CONST_FREEFORM_TAG, kwargs) + + @property + def defined_tags(self) -> Dict: + """Model deployment defined tags. + + Returns + ------- + Dict + Model deployment defined tags. + """ + return self.get_spec(self.CONST_DEFINED_TAG, {}) + + def with_defined_tags(self, **kwargs) -> "ModelDeployment": + """Sets the defined tags of model deployment. + + Parameters + ---------- + kwargs + The defined tags of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self.set_spec(self.CONST_DEFINED_TAG, kwargs) + + @property + def runtime(self) -> "ModelDeploymentRuntime": + """Model deployment runtime. + + Returns + ------- + ModelDeploymentRuntime + Model deployment runtime. + """ + return self.get_spec(self.CONST_RUNTIME, None) + + def with_runtime(self, runtime: ModelDeploymentRuntime) -> "ModelDeployment": + """Sets the runtime of model deployment. + + Parameters + ---------- + runtime: ModelDeploymentRuntime + The runtime of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self.set_spec(self.CONST_RUNTIME, runtime) + + @property + def infrastructure(self) -> "ModelDeploymentInfrastructure": + """Model deployment infrastructure. + + Returns + ------- + ModelDeploymentInfrastructure + Model deployment infrastructure. + """ + return self.get_spec(self.CONST_INFRASTRUCTURE, None) + + def with_infrastructure( + self, infrastructure: ModelDeploymentInfrastructure + ) -> "ModelDeployment": + """Sets the infrastructure of model deployment. + + Parameters + ---------- + infrastructure: ModelDeploymentInfrastructure + The infrastructure of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self.set_spec(self.CONST_INFRASTRUCTURE, infrastructure) def deploy( self, @@ -169,7 +578,7 @@ def deploy( max_wait_time: int = DEFAULT_WAIT_TIME, poll_interval: int = DEFAULT_POLL_INTERVAL, ): - """deploy deploys the current ModelDeployment object + """Deploys the current ModelDeployment object Parameters ---------- @@ -186,28 +595,31 @@ def deploy( ------- ModelDeployment The instance of ModelDeployment. + + Raises + ------ + ModelDeploymentFailedError + If model deployment fails to deploy """ - response = self.ds_composite_client.create_model_deployment_and_wait_for_state( - self.properties.build() + create_model_deployment_details = ( + self._build_model_deployment_details() + if self._spec + else self.properties.build() ) - self.workflow_req_id = response.headers["opc-work-request-id"] - res_payload = json.loads(str(response.data)) - self.current_state = State._from_str(res_payload["lifecycle_state"]) - self.model_deployment_id = res_payload["id"] - self.url = res_payload["model_deployment_url"] - if wait_for_completion: - try: - self._wait_for_progress_completion( - State.ACTIVE.name, - DEFAULT_WORKFLOW_STEPS, - [State.FAILED.name, State.INACTIVE.name], - max_wait_time, - poll_interval, - ) - except Exception as e: - utils.get_logger().error(f"Error while trying to deploy: {str(e)}") - raise e - return self + + response = self.dsc_model_deployment.create( + create_model_deployment_details=create_model_deployment_details, + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, + ) + + if response.lifecycle_state == State.FAILED.name: + raise ModelDeploymentFailedError( + f"Model deployment {response.id} failed to deploy: {response.lifecycle_details}" + ) + + return self._update_from_oci_model(response) def delete( self, @@ -233,31 +645,12 @@ def delete( ModelDeployment The instance of ModelDeployment. """ - - response = self.ds_composite_client.delete_model_deployment_and_wait_for_state( - self.model_deployment_id + response = self.dsc_model_deployment.delete( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, ) - # response.data from deleting model is None, headers are populated - self.workflow_req_id = response.headers["opc-work-request-id"] - oci_model_deployment_object = self.ds_client.get_model_deployment( - self.model_deployment_id - ).data - self.current_state = State._from_str( - oci_model_deployment_object.lifecycle_state - ) - if wait_for_completion: - try: - self._wait_for_progress_completion( - State.DELETED.name, - DELETE_WORKFLOW_STEPS, - [State.FAILED.name, State.INACTIVE.name], - max_wait_time, - poll_interval, - ) - except Exception as e: - utils.get_logger().error(f"Error while trying to delete: {str(e)}") - raise e - return self + return self._update_from_oci_model(response) def update( self, @@ -295,45 +688,113 @@ def update( ModelDeployment The instance of ModelDeployment. """ + updated_properties = properties if not isinstance(properties, ModelDeploymentProperties): - properties = ModelDeploymentProperties( - oci_model_deployment=properties, config=self.config, **kwargs + updated_properties = ModelDeploymentProperties( + oci_model_deployment=properties, **kwargs ) - if wait_for_completion: - wait_for_states = ["SUCCEEDED", "FAILED"] - else: - wait_for_states = [] + update_model_deployment_details = ( + updated_properties.to_update_deployment() + if properties or updated_properties.oci_model_deployment or kwargs + else self._update_model_deployment_details() + ) + response = self.dsc_model_deployment.update( + update_model_deployment_details=update_model_deployment_details, + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, + ) + + return self._update_from_oci_model(response) + + def watch( + self, + log_type: str = ModelDeploymentLogType.ACCESS, + time_start: datetime = None, + interval: int = LOG_INTERVAL, + log_filter: str = None, + ) -> "ModelDeployment": + """Streams the access and/or predict logs of model deployment. + + Parameters + ---------- + log_type: str, optional + The log type. Can be `access`, `predict` or None. + Defaults to access. + time_start : datetime.datetime, optional + Starting time for the log query. + Defaults to None. + interval : int, optional + The time interval between sending each request to pull logs from OCI logging service. + Defaults to 3. + log_filter : str, optional + Expression for filtering the logs. This will be the WHERE clause of the query. + Defaults to None. + + Returns + ------- + ModelDeployment + The instance of ModelDeployment. + """ + status = "" + while not self._stop_condition(): + status = self._check_and_print_status(status) + time.sleep(interval) + + time_start = time_start or self.time_created try: - response = ( - self.ds_composite_client.update_model_deployment_and_wait_for_state( - self.model_deployment_id, - properties.to_update_deployment(), - wait_for_states=wait_for_states, - waiter_kwargs={ - "max_interval_seconds": poll_interval, - "max_wait_seconds": max_wait_time, - }, - ) + count = self.logs(log_type).stream( + source=self.model_deployment_id, + interval=interval, + stop_condition=self._stop_condition, + time_start=time_start, + log_filter=log_filter, ) - if "opc-work-request-id" in response.headers: - self.workflow_req_id = response.headers["opc-work-request-id"] - # Refresh the properties when model is active - if wait_for_completion: - self.properties = ModelDeploymentProperties( - oci_model_deployment=self.ds_client.get_model_deployment( - self.model_deployment_id - ).data, - config=self.config, + + if not count: + print( + "No logs in the last 14 days. Please reset time_start to see older logs." ) - except Exception as e: - utils.get_logger().error( - "Updating model deployment failed with error: %s", format(e) - ) - raise e + return self.sync() + except KeyboardInterrupt: + print("Stop watching logs.") + pass - return self + def _stop_condition(self): + """Stops the sync once the model deployment is in a terminal state.""" + return self.state in TERMINAL_STATES + + def _check_and_print_status(self, prev_status) -> str: + """Check and print the next status. + + Parameters + ---------- + prev_status: str + The previous model deployment status. + + Returns + ------- + str: + The next model deployment status. + """ + status = self._model_deployment_status_text() + if status != prev_status: + timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") + print(f"{timestamp} - {status}") + return status + + def _model_deployment_status_text(self) -> str: + """Formats the status message. + + Returns + ------- + str: + The model deployment life status and life cycle details. + """ + details = f", {self.lifecycle_details}" if self.lifecycle_details else "" + return f"Model Deployment {self.lifecycle_state}" + details @property def state(self) -> State: @@ -342,11 +803,7 @@ def state(self) -> State: while request_attempts < DEFAULT_RETRYING_REQUEST_ATTEMPTS: request_attempts += 1 try: - oci_state = self.ds_client.get_model_deployment( - retry_strategy=oci.retry.DEFAULT_RETRY_STRATEGY, - model_deployment_id=self.model_deployment_id, - ).data.lifecycle_state - self.current_state = State._from_str(oci_state) + self.current_state = State._from_str(self.sync().lifecycle_state) break except: pass @@ -359,36 +816,41 @@ def status(self) -> State: """Returns the deployment state of the current Model Deployment object""" return self.state - def list_workflow_logs(self) -> list: - """Returns a list of the steps involved in deploying a model - - Returns - ------- - list - List of dictionaries detailing the status of each step in the deployment process. - """ - if self.workflow_req_id == "" or self.workflow_req_id == None: - utils.get_logger().info("Workflow req id not available") - raise Exception - return self.ds_client.list_work_request_logs(self.workflow_req_id).data - def predict( self, json_input=None, data: Any = None, + serializer: "ads.model.ModelInputSerializer" = model_input_serializer, auto_serialize_data: bool = False, **kwargs, ) -> dict: - """Returns prediction of input data run against the model deployment endpoint + """Returns prediction of input data run against the model deployment endpoint. + + Examples + -------- + >>> import numpy as np + >>> from ads.model import ModelInputSerializer + >>> class MySerializer(ModelInputSerializer): + ... def serialize(self, data): + ... serialized_data = 1 + ... return serialized_data + >>> model_deployment = ModelDeployment.from_id() + >>> prediction = model_deployment.predict( + ... data=np.array([1, 2, 3]), + ... serializer=MySerializer(), + ... auto_serialize_data=True, + ... )['prediction'] Parameters ---------- json_input: Json serializable - Json payload for the prediction. + JSON payload for the prediction. data: Any Data for the prediction. - auto_serialize_data: bool. - Whether to auto serialize input data. Defauls to `False`. + serializer: ads.model.ModelInputSerializer + Defaults to ads.model.JsonModelInputSerializer. + auto_serialize_data: bool + Defaults to False. Indicate whether to auto serialize input data using `serializer`. If `auto_serialize_data=False`, `data` required to be bytes or json serializable and `json_input` required to be json serializable. If `auto_serialize_data` set to True, data will be serialized before sending to model deployment endpoint. @@ -400,12 +862,12 @@ def predict( Returns ------- - dict + dict: Prediction results. """ endpoint = f"{self.url}/predict" - signer = self.config.get("signer") + signer = authutil.default_signer()["signer"] header = { "signer": signer, "content_type": kwargs.get("content_type", None), @@ -422,10 +884,15 @@ def predict( if auto_serialize_data: data = data or json_input - serialized_data = InputDataSerializer(data=data) - return serialized_data.send(endpoint, **header) + serialized_data = serializer.serialize(data=data) + return send_request( + data=serialized_data, + endpoint=endpoint, + is_json_payload=_is_json_serializable(serialized_data), + header=header, + ) - elif json_input is not None: + if json_input is not None: if not _is_json_serializable(json_input): raise ValueError( "`json_input` must be json serializable. " @@ -438,7 +905,12 @@ def predict( ) data = json_input - is_json_payload = True if _is_json_serializable(data) else False + is_json_payload = _is_json_serializable(data) + if not isinstance(data, bytes) and not is_json_payload: + raise TypeError( + "`data` is not bytes or json serializable. Set `auto_serialize_data` to `True` to serialize the input data." + ) + prediction = send_request( data=data, endpoint=endpoint, is_json_payload=is_json_payload, header=header ) @@ -468,34 +940,13 @@ def activate( ModelDeployment The instance of ModelDeployment. """ - response = ( - self.ds_composite_client.activate_model_deployment_and_wait_for_state( - self.model_deployment_id - ) - ) - self.workflow_req_id = response.headers["opc-work-request-id"] - oci_model_deployment_object = self.ds_client.get_model_deployment( - self.model_deployment_id - ).data - self.current_state = State._from_str( - oci_model_deployment_object.lifecycle_state + response = self.dsc_model_deployment.activate( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, ) - self.model_deployment_id = oci_model_deployment_object.id - self.url = oci_model_deployment_object.model_deployment_url - if wait_for_completion: - try: - self._wait_for_progress_completion( - State.ACTIVE.name, - DEFAULT_WORKFLOW_STEPS, - [State.FAILED.name, State.INACTIVE.name], - max_wait_time, - poll_interval, - ) - except Exception as e: - utils.get_logger().error(f"Error while trying to activate: {str(e)}") - raise e - return self + return self._update_from_oci_model(response) def deactivate( self, @@ -521,117 +972,13 @@ def deactivate( ModelDeployment The instance of ModelDeployment. """ - response = ( - self.ds_composite_client.deactivate_model_deployment_and_wait_for_state( - self.model_deployment_id - ) + response = self.dsc_model_deployment.deactivate( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, ) - self.workflow_req_id = response.headers["opc-work-request-id"] - oci_model_deployment_object = self.ds_client.get_model_deployment( - self.model_deployment_id - ).data - self.current_state = State._from_str( - oci_model_deployment_object.lifecycle_state - ) - self.model_deployment_id = oci_model_deployment_object.id - self.url = oci_model_deployment_object.model_deployment_url - if wait_for_completion: - try: - self._wait_for_progress_completion( - State.INACTIVE.name, - DEACTIVATE_WORKFLOW_STEPS, - [State.FAILED.name], - max_wait_time, - poll_interval, - ) - except Exception as e: - utils.get_logger().error(f"Error while trying to deactivate: {str(e)}") - raise e - return self - - def _wait_for_progress_completion( - self, - final_state: str, - work_flow_step: int, - disallowed_final_states: List[str], - max_wait_time: int = DEFAULT_WAIT_TIME, - poll_interval: int = DEFAULT_POLL_INTERVAL, - ): - """_wait_for_progress_completion blocks until progress is completed. - - Parameters - ---------- - final_state: str - Final state of model deployment aimed to be reached. - work_flow_step: int - Number of work flow step of the request. - disallowed_final_states: list[str] - List of disallowed final state to be reached. - max_wait_time: int - Maximum amount of time to wait in seconds (Defaults to 1200). - Negative implies infinite wait time. - poll_interval: int - Poll interval in seconds (Defaults to 10). - """ - - start_time = time.time() - prev_message = "" - prev_workflow_stage_len = 0 - with utils.get_progress_bar(work_flow_step) as progress: - if max_wait_time > 0 and utils.seconds_since(start_time) >= max_wait_time: - utils.get_logger().error( - f"Max wait time ({max_wait_time} seconds) exceeded." - ) - while ( - max_wait_time < 0 or utils.seconds_since(start_time) < max_wait_time - ) and self.current_state.name.upper() != final_state: - if self.current_state.name.upper() in disallowed_final_states: - utils.get_logger().info( - f"Operation failed due to deployment reaching state {self.current_state.name.upper()}. Use Deployment ID for further steps." - ) - break - - prev_state = self.current_state.name - try: - model_deployment_payload = json.loads( - str( - self.ds_client.get_model_deployment( - self.model_deployment_id - ).data - ) - ) - self.current_state = ( - State._from_str(model_deployment_payload["lifecycle_state"]) - if "lifecycle_state" in model_deployment_payload - else State.UNKNOWN - ) - workflow_payload = self.ds_client.list_work_request_logs( - self.workflow_req_id - ).data - if isinstance(workflow_payload, list) and len(workflow_payload) > 0: - if prev_message != workflow_payload[-1].message: - for _ in range( - len(workflow_payload) - prev_workflow_stage_len - ): - progress.update(workflow_payload[-1].message) - prev_workflow_stage_len = len(workflow_payload) - prev_message = workflow_payload[-1].message - prev_workflow_stage_len = len(workflow_payload) - if prev_state != self.current_state.name: - if "model_deployment_url" in model_deployment_payload: - self.url = model_deployment_payload["model_deployment_url"] - utils.get_logger().info( - f"Status Update: {self.current_state.name} in {utils.seconds_since(start_time)} seconds" - ) - except Exception as e: - # utils.get_logger().warning( - # "Unable to update deployment status. Details: %s", format( - # e) - # ) - pass - time.sleep(poll_interval) - progress.update("Done") + return self._update_from_oci_model(response) def _log_details(self, log_type: str = ModelDeploymentLogType.ACCESS): """Gets log details for the provided `log_type`. @@ -652,14 +999,22 @@ def _log_details(self, log_type: str = ModelDeploymentLogType.ACCESS): Deployment doesn't have requested log configuration. """ - if not self.properties.category_log_details or not getattr( + if self.properties.category_log_details and getattr( self.properties.category_log_details, log_type ): - raise LogNotConfiguredError( - f"Deployment `{self.model_deployment_id}` " - f"has no `{log_type}` log configuration." - ) - return getattr(self.properties.category_log_details, log_type) + return getattr(self.properties.category_log_details, log_type) + elif self.infrastructure: + category_log_details = self._build_category_log_details() + log = category_log_details.get(log_type, None) + if log and log.get("logId", None) and log.get("logGroupId", None): + return LogDetails( + log_id=log.get("logId"), log_group_id=log.get("logGroupId") + ) + + raise LogNotConfiguredError( + f"Deployment `{self.model_deployment_id}` " + f"has no `{log_type}` log configuration." + ) @property def predict_log(self) -> OCILog: @@ -672,8 +1027,13 @@ def predict_log(self) -> OCILog: """ if not self._predict_log: log_details = self._log_details(log_type=ModelDeploymentLogType.PREDICT) + compartment_id = ( + self.infrastructure.compartment_id + if self.infrastructure + else self.properties.compartment_id + ) self._predict_log = OCILog( - compartment_id=self.properties.compartment_id, + compartment_id=compartment_id or COMPARTMENT_OCID, id=log_details.log_id, log_group_id=log_details.log_group_id, source=self.model_deployment_id, @@ -692,8 +1052,13 @@ def access_log(self) -> OCILog: """ if not self._access_log: log_details = self._log_details(log_type=ModelDeploymentLogType.ACCESS) + compartment_id = ( + self.infrastructure.compartment_id + if self.infrastructure + else self.properties.compartment_id + ) self._access_log = OCILog( - compartment_id=self.properties.compartment_id, + compartment_id=compartment_id or COMPARTMENT_OCID, id=log_details.log_id, log_group_id=log_details.log_group_id, source=self.model_deployment_id, @@ -788,3 +1153,482 @@ def prepare_log_record(log): limit=limit, ) return pd.DataFrame([prepare_log_record(log.data) for log in logs]) + + def sync(self) -> "ModelDeployment": + """Updates the model deployment instance from backend. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return self._update_from_oci_model( + OCIDataScienceModelDeployment.from_id(self.model_deployment_id) + ) + + @classmethod + def list( + cls, + status: str = None, + compartment_id: str = None, + project_id: str = None, + **kwargs, + ) -> List["ModelDeployment"]: + """Lists the model deployments associated with current compartment id and status + + Parameters + ---------- + status : str + Status of deployment. Defaults to None. + Allowed values: `ACTIVE`, `CREATING`, `DELETED`, `DELETING`, `FAILED`, `INACTIVE` and `UPDATING`. + compartment_id : str + Target compartment to list deployments from. + Defaults to the compartment set in the environment variable "NB_SESSION_COMPARTMENT_OCID". + If "NB_SESSION_COMPARTMENT_OCID" is not set, the root compartment ID will be used. + An ValueError will be raised if root compartment ID cannot be determined. + project_id : str + Target project to list deployments from. + Defaults to the project id in the environment variable "PROJECT_OCID". + kwargs : + The values are passed to oci.data_science.DataScienceClient.list_model_deployments. + + Returns + ------- + list + A list of ModelDeployment objects. + """ + deployments = OCIDataScienceModelDeployment.list( + status=status, + compartment_id=compartment_id, + project_id=project_id, + **kwargs, + ) + return [cls()._update_from_oci_model(deployment) for deployment in deployments] + + @classmethod + def list_df( + cls, + status: str = None, + compartment_id: str = None, + project_id: str = None, + ) -> pd.DataFrame: + """Returns the model deployments associated with current compartment and status + as a Dataframe that can be easily visualized + + Parameters + ---------- + status : str + Status of deployment. Defaults to None. + Allowed values: `ACTIVE`, `CREATING`, `DELETED`, `DELETING`, `FAILED`, `INACTIVE` and `UPDATING`. + compartment_id : str + Target compartment to list deployments from. + Defaults to the compartment set in the environment variable "NB_SESSION_COMPARTMENT_OCID". + If "NB_SESSION_COMPARTMENT_OCID" is not set, the root compartment ID will be used. + An ValueError will be raised if root compartment ID cannot be determined. + project_id : str + Target project to list deployments from. + Defaults to the project id in the environment variable "PROJECT_OCID". + + Returns + ------- + DataFrame + pandas Dataframe containing information about the ModelDeployments + """ + model_deployments = cls.list( + status=status, compartment_id=compartment_id, project_id=project_id + ) + if isinstance(status, str) or status == None: + status = State._from_str(status) + display = pd.DataFrame() + ids, urls, status_list = [], [], [] + for model_deployment in model_deployments: + state_of_model = State._from_str(model_deployment.lifecycle_state) + if status == State.UNKNOWN or status.name == state_of_model.name: + ids.append(model_deployment.model_deployment_id) + urls.append(model_deployment.url) + status_list.append(model_deployment.lifecycle_state) + display["deployment_id"] = ids + display["deployment_url"] = urls + display["current_state"] = status_list + return display + + @classmethod + def from_id(cls, id: str) -> "ModelDeployment": + """Loads the model deployment instance from ocid. + + Parameters + ---------- + id: str + The ocid of model deployment. + + Returns + ------- + ModelDeployment + The ModelDeployment instance (self). + """ + return cls()._update_from_oci_model(OCIDataScienceModelDeployment.from_id(id)) + + @classmethod + def from_dict(cls, obj_dict: Dict) -> "ModelDeployment": + """Loads model deployment instance from a dictionary of configurations. + + Parameters + ---------- + obj_dict: Dict + A dictionary of configurations. + + Returns + ------- + ModelDeployment + The model deployment instance. + """ + if not isinstance(obj_dict, dict): + raise ValueError( + "The config data for initializing the model deployment is invalid." + ) + spec = ads_utils.batch_convert_case( + copy.deepcopy(obj_dict.get("spec")), "camel" + ) + + mappings = { + cls.CONST_INFRASTRUCTURE: { + MODEL_DEPLOYMENT_INFRASTRUCTURE_TYPE: ModelDeploymentInfrastructure, + }, + cls.CONST_RUNTIME: { + ModelDeploymentRuntimeType.CONDA: ModelDeploymentCondaRuntime, + ModelDeploymentRuntimeType.CONTAINER: ModelDeploymentContainerRuntime, + }, + } + model_deployment = cls() + + for key, value in spec.items(): + if key in mappings: + mapping = mappings[key] + child_config = value + if child_config.get("type") not in mapping: + raise NotImplementedError( + f"{key.title()} type: {child_config.get('type')} is not supported." + ) + model_deployment.set_spec( + key, mapping[child_config.get("type")].from_dict(child_config) + ) + else: + model_deployment.set_spec(key, value) + + return model_deployment + + def to_dict(self) -> Dict: + """Serializes model deployment to a dictionary. + + Returns + ------- + dict + The model deployment serialized as a dictionary. + """ + spec = copy.deepcopy(self._spec) + for key, value in spec.items(): + if hasattr(value, "to_dict"): + value = value.to_dict() + spec[key] = value + + return { + "kind": self.kind, + "type": self.type, + "spec": ads_utils.batch_convert_case(spec, "camel"), + } + + def _update_from_oci_model(self, oci_model_instance) -> "ModelDeployment": + """Updates model deployment instance from OCIDataScienceModelDeployment. + + Parameters + ---------- + oci_model_instance: OCIDataScienceModelDeployment + The OCIDataScienceModelDeployment instance. + + Returns + ------- + ModelDeployment + The model deployment instance. + """ + self.dsc_model_deployment = oci_model_instance + for key, value in self.attribute_map.items(): + if hasattr(oci_model_instance, value): + self.set_spec(key, getattr(oci_model_instance, value)) + + infrastructure = ModelDeploymentInfrastructure() + self._extract_from_oci_model( + infrastructure, oci_model_instance, infrastructure.sub_level_attribute_maps + ) + + model_deployment_configuration_details = getattr( + oci_model_instance, "model_deployment_configuration_details", None + ) + environment_configuration_details = getattr( + model_deployment_configuration_details, + "environment_configuration_details", + None, + ) + runtime = ( + ModelDeploymentContainerRuntime() + if getattr( + environment_configuration_details, + "environment_configuration_type", + None, + ) + == OCIModelDeploymentRuntimeType.CONTAINER + else ModelDeploymentCondaRuntime() + ) + + self._extract_from_oci_model(runtime, oci_model_instance) + infrastructure.set_spec( + infrastructure.CONST_WEB_CONCURRENCY, + runtime.env.get("WEB_CONCURRENCY", None), + ) + + self.set_spec(self.CONST_INFRASTRUCTURE, infrastructure) + self.set_spec(self.CONST_RUNTIME, runtime) + + return self + + @staticmethod + def _extract_from_oci_model( + dsc_instance: Union[ModelDeploymentInfrastructure, ModelDeploymentRuntime], + oci_model_instance: OCIDataScienceModelDeployment, + sub_level: Dict = {}, + ) -> Union[ModelDeploymentInfrastructure, ModelDeploymentRuntime]: + """Extract attributes from OCIDataScienceModelDeployment. + + Parameters + ---------- + dsc_instance: Union[ModelDeploymentInfrastructure, ModelDeploymentRuntime] + The ModelDeploymentInfrastructure or ModelDeploymentRuntime instance. + oci_model_instance: OCIDataScienceModelDeployment + The OCIDataScienceModelDeployment instance. + sub_level: Dict + The sub level attribute maps of ModelDeploymentInfrastructure or ModelDeploymentRuntime + + Returns + ------- + Union[ModelDeploymentInfrastructure, ModelDeploymentRuntime] + The ModelDeploymentInfrastructure or ModelDeploymentRuntime instance. + """ + for infra_attr, dsc_attr in dsc_instance.payload_attribute_map.items(): + value = get_value(oci_model_instance, dsc_attr) + if value: + if infra_attr not in sub_level: + dsc_instance._spec[infra_attr] = value + else: + dsc_instance._spec[infra_attr] = {} + for sub_infra_attr, sub_dsc_attr in sub_level[infra_attr].items(): + sub_value = get_value(value, sub_dsc_attr) + if sub_value: + dsc_instance._spec[infra_attr][sub_infra_attr] = sub_value + return dsc_instance + + def _build_model_deployment_details(self) -> CreateModelDeploymentDetails: + """Builds CreateModelDeploymentDetails from model deployment instance. + + Returns + ------- + CreateModelDeploymentDetails + The CreateModelDeploymentDetails instance. + """ + if not (self.infrastructure and self.runtime): + raise ValueError( + "Missing parameter runtime or infrastructure. Try reruning it after parameters are fully configured." + ) + + create_model_deployment_details = { + self.CONST_DISPLAY_NAME: self.display_name or self._random_display_name(), + self.CONST_DESCRIPTION: self.description, + self.CONST_DEFINED_TAG: self.defined_tags, + self.CONST_FREEFORM_TAG: self.freeform_tags, + self.runtime.CONST_DEPLOYMENT_MODE: self.runtime.deployment_mode, + self.infrastructure.CONST_COMPARTMENT_ID: self.infrastructure.compartment_id + or COMPARTMENT_OCID, + self.infrastructure.CONST_PROJECT_ID: self.infrastructure.project_id + or PROJECT_OCID, + self.infrastructure.CONST_MODEL_DEPLOYMENT_CONFIG_DETAILS: self._build_model_deployment_configuration_details(), + self.infrastructure.CONST_CATEGORY_LOG_DETAILS: self._build_category_log_details(), + } + + return OCIDataScienceModelDeployment( + **create_model_deployment_details + ).to_oci_model(CreateModelDeploymentDetails) + + def _update_model_deployment_details(self) -> UpdateModelDeploymentDetails: + """Builds UpdateModelDeploymentDetails from model deployment instance. + + Returns + ------- + UpdateModelDeploymentDetails + The UpdateModelDeploymentDetails instance. + """ + if not (self.infrastructure and self.runtime): + raise ValueError( + "Missing parameter runtime or infrastructure. Try reruning it after parameters are fully configured." + ) + + update_model_deployment_details = { + self.CONST_DISPLAY_NAME: self.display_name, + self.CONST_DESCRIPTION: self.description, + self.CONST_DEFINED_TAG: self.defined_tags, + self.CONST_FREEFORM_TAG: self.freeform_tags, + self.infrastructure.CONST_MODEL_DEPLOYMENT_CONFIG_DETAILS: self._build_model_deployment_configuration_details(), + self.infrastructure.CONST_CATEGORY_LOG_DETAILS: self._build_category_log_details(), + } + return OCIDataScienceModelDeployment( + **update_model_deployment_details + ).to_oci_model(UpdateModelDeploymentDetails) + + def _build_model_deployment_configuration_details(self) -> Dict: + """Builds model deployment configuration details from model deployment instance. + + Returns + ------- + Dict: + Dict contains model deployment configuration details. + """ + infrastructure = self.infrastructure + runtime = self.runtime + + instance_configuration = { + infrastructure.CONST_INSTANCE_SHAPE_NAME: infrastructure.shape_name, + infrastructure.CONST_MODEL_DEPLOYMENT_INSTANCE_SHAPE_CONFIG_DETAILS: infrastructure.shape_config_details, + } + + scaling_policy = { + infrastructure.CONST_POLICY_TYPE: "FIXED_SIZE", + infrastructure.CONST_INSTANCE_COUNT: infrastructure.replica, + } + + if not runtime.model_uri: + raise ValueError( + "Missing parameter model uri. Try reruning it after model uri is configured." + ) + + model_id = runtime.model_uri + if not model_id.startswith("ocid"): + model_id = OCIClientManager().prepare_artifact( + model_uri=runtime.model_uri, + properties=dict( + display_name=self.display_name, + compartment_id=self.infrastructure.compartment_id + or COMPARTMENT_OCID, + project_id=self.infrastructure.project_id or PROJECT_OCID, + ), + ) + + model_configuration_details = { + infrastructure.CONST_BANDWIDTH_MBPS: infrastructure.bandwidth_mbps, + infrastructure.CONST_INSTANCE_CONFIG: instance_configuration, + runtime.CONST_MODEL_ID: model_id, + infrastructure.CONST_SCALING_POLICY: scaling_policy, + } + + environment_variables = runtime.env + if infrastructure.web_concurrency: + environment_variables["WEB_CONCURRENCY"] = str( + infrastructure.web_concurrency + ) + runtime.set_spec(runtime.CONST_ENV, environment_variables) + environment_configuration_details = { + runtime.CONST_ENVIRONMENT_CONFIG_TYPE: runtime.environment_config_type, + runtime.CONST_ENVIRONMENT_VARIABLES: runtime.env, + } + + if runtime.environment_config_type == OCIModelDeploymentRuntimeType.CONTAINER: + if not hasattr( + oci.data_science.models, + "OcirModelDeploymentEnvironmentConfigurationDetails", + ): + raise EnvironmentError( + "Container runtime hasn't been supported in the current OCI SDK installed." + ) + environment_configuration_details["image"] = runtime.image + environment_configuration_details["imageDigest"] = runtime.image_digest + environment_configuration_details["cmd"] = runtime.cmd + environment_configuration_details["entrypoint"] = runtime.entrypoint + environment_configuration_details["serverPort"] = runtime.server_port + environment_configuration_details[ + "healthCheckPort" + ] = runtime.health_check_port + + model_deployment_configuration_details = { + infrastructure.CONST_DEPLOYMENT_TYPE: "SINGLE_MODEL", + infrastructure.CONST_MODEL_CONFIG_DETAILS: model_configuration_details, + runtime.CONST_ENVIRONMENT_CONFIG_DETAILS: environment_configuration_details, + } + + if runtime.deployment_mode == ModelDeploymentMode.STREAM: + if not hasattr(oci.data_science.models, "StreamConfigurationDetails"): + raise EnvironmentError( + "Model deployment mode hasn't been supported in the current OCI SDK installed." + ) + model_deployment_configuration_details[ + infrastructure.CONST_STREAM_CONFIG_DETAILS + ] = { + runtime.CONST_INPUT_STREAM_IDS: runtime.input_stream_ids, + runtime.CONST_OUTPUT_STREAM_IDS: runtime.output_stream_ids, + } + + return model_deployment_configuration_details + + def _build_category_log_details(self) -> Dict: + """Builds category log details from model deployment instance. + + Returns + ------- + Dict: + Dict contains category log details. + """ + if self.infrastructure.log_group_id and self.infrastructure.log_id: + log_group_details = { + self.infrastructure.CONST_LOG_GROUP_ID: self.infrastructure.log_group_id, + self.infrastructure.CONST_LOG_ID: self.infrastructure.log_id, + } + return { + self.infrastructure.CONST_ACCESS: log_group_details, + self.infrastructure.CONST_PREDICT: log_group_details, + } + + return { + self.infrastructure.CONST_ACCESS: { + self.infrastructure.CONST_LOG_GROUP_ID: self.infrastructure.access_log.get( + "logGroupId", None + ), + self.infrastructure.CONST_LOG_ID: self.infrastructure.access_log.get( + "logId", None + ), + }, + self.infrastructure.CONST_PREDICT: { + self.infrastructure.CONST_LOG_GROUP_ID: self.infrastructure.predict_log.get( + "logGroupId", None + ), + self.infrastructure.CONST_LOG_ID: self.infrastructure.predict_log.get( + "logId", None + ), + }, + } + + def _random_display_name(self): + """Generates a random display name.""" + return f"{self._PREFIX}-{ads_utils.get_random_name_for_resource()}" + + def _extract_spec_kwargs(self, **kwargs) -> Dict: + """Extract spec related keyword arguments from kwargs. + + Parameters + ---------- + kwargs + + Returns + ------- + Dict: + Dict contains model deployment spec related keyword arguments. + """ + spec_kwargs = {} + for attribute in self.initialize_spec_attributes: + if attribute in kwargs: + spec_kwargs[attribute] = kwargs[attribute] + return spec_kwargs diff --git a/ads/model/deployment/model_deployment_infrastructure.py b/ads/model/deployment/model_deployment_infrastructure.py new file mode 100644 index 000000000..4dafd263b --- /dev/null +++ b/ads/model/deployment/model_deployment_infrastructure.py @@ -0,0 +1,520 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/from typing import Dict + + +from typing import Any, Dict +from ads.jobs.builders.base import Builder + +MODEL_DEPLOYMENT_INFRASTRUCTURE_TYPE = "datascienceModelDeployment" +MODEL_DEPLOYMENT_INFRASTRUCTURE_KIND = "infrastructure" + + +class ModelDeploymentInfrastructure(Builder): + """A class used to represent a Model Deployment Infrastructure. + + Attributes + ---------- + compartment_id: str + The compartment id of model deployment + project_id: str + The project id of model deployment + shape_name: str + The shape name of model deployment + shape_config_details: Dict + The shape config details of model deployment + replica: int + The replica of model deployment + bandwidth_mbps: int + The bandwidth of model deployment in mbps + access_log: Dict + The access log of model deployment + predict_log: Dict + The predict log of model deployment + log_group_id: str + The access and predict log group id of model deployment + log_id: str + The access and predict log id of model deployment + web_concurrency: int + The web concurrency of model deployment + + Methods + ------- + with_compartment_id(compartment_id) + Sets the compartment id of model deployment + with_project_id(project_id) + Sets the project id of model deployment + with_shape_name(shape_name) + Sets the shape name of model deployment + with_shape_config_details(shape_config_details) + Sets the shape config details of model deployment + with_replica(replica) + Sets the replica of model deployment + with_bandwidth_mbps(bandwidth_mbps) + Sets the bandwidth of model deployment in mbps + with_access_log(log_group_id, log_id) + Sets the access log of model deployment + with_predict_log(log_group_id, log_id) + Sets the predict log of model deployment + with_log_group_id(log_group_id) + Sets the access and predict log group id of model deployment + with_log_id(log_id) + Sets the access and predict log id of model deployment + with_web_concurrency(web_concurrency) + Sets the web concurrency of model deployment + + Example + ------- + Build infrastructure from builder apis: + >>> infrastructure = ModelDeploymentInfrastructure() + ... .with_project_id() + ... .with_compartment_id() + ... .with_shape_name("VM.Standard.E4.Flex") + ... .with_shape_config_details( + ... ocpus=1, + ... memory_in_gbs=16 + ... ) + ... .with_replica(1) + ... .with_bandwidth_mbps(10) + ... .with_web_concurrency(10) + ... .with_access_log( + ... log_group_id=, + ... log_id= + ... ) + ... .with_predict_log( + ... log_group_id=, + ... log_id= + ... ) + >>> infrastructure.to_dict() + + Build infrastructure from yaml: + >>> infrastructure = ModelDeploymentInfrastructure.from_yaml(uri=) + """ + + CONST_PROJECT_ID = "projectId" + CONST_COMPARTMENT_ID = "compartmentId" + CONST_MODEL_DEPLOYMENT_CONFIG_DETAILS = "modelDeploymentConfigurationDetails" + CONST_DEPLOYMENT_TYPE = "deploymentType" + CONST_MODEL_CONFIG_DETAILS = "modelConfigurationDetails" + CONST_INSTANCE_CONFIG = "instanceConfiguration" + CONST_SHAPE_NAME = "shapeName" + CONST_INSTANCE_SHAPE_NAME = "instanceShapeName" + CONST_SHAPE_CONFIG_DETAILS = "shapeConfigDetails" + CONST_MODEL_DEPLOYMENT_INSTANCE_SHAPE_CONFIG_DETAILS = ( + "modelDeploymentInstanceShapeConfigDetails" + ) + CONST_OCPUS = "ocpus" + CONST_MEMORY_IN_GBS = "memoryInGBs" + CONST_SCALING_POLICY = "scalingPolicy" + CONST_POLICY_TYPE = "policyType" + CONST_REPLICA = "replica" + CONST_INSTANCE_COUNT = "instanceCount" + CONST_BANDWIDTH_MBPS = "bandwidthMbps" + CONST_CATEGORY_LOG_DETAILS = "categoryLogDetails" + CONST_ACCESS_LOG = "accessLog" + CONST_ACCESS = "access" + CONST_PREDICT_LOG = "predictLog" + CONST_PREDICT = "predict" + CONST_LOG_ID = "logId" + CONST_LOG_GROUP_ID = "logGroupId" + CONST_WEB_CONCURRENCY = "webConcurrency" + CONST_STREAM_CONFIG_DETAILS = "streamConfigurationDetails" + + attribute_map = { + CONST_PROJECT_ID: "project_id", + CONST_COMPARTMENT_ID: "compartment_id", + CONST_SHAPE_NAME: "shape_name", + CONST_SHAPE_CONFIG_DETAILS: "shape_config_details", + CONST_OCPUS: "ocpus", + CONST_MEMORY_IN_GBS: "memory_in_gbs", + CONST_REPLICA: "replica", + CONST_BANDWIDTH_MBPS: "bandwidth_mbps", + CONST_ACCESS_LOG: "access_log", + CONST_PREDICT_LOG: "predict_log", + CONST_LOG_ID: "log_id", + CONST_LOG_GROUP_ID: "log_group_id", + CONST_WEB_CONCURRENCY: "web_concurrency", + } + + shape_config_details_attribute_map = { + CONST_MEMORY_IN_GBS: "memory_in_gbs", + CONST_OCPUS: "ocpus", + } + + access_log_attribute_map = { + CONST_LOG_ID: "log_id", + CONST_LOG_GROUP_ID: "log_group_id", + } + + predict_log_attribute_map = { + CONST_LOG_ID: "log_id", + CONST_LOG_GROUP_ID: "log_group_id", + } + + MODEL_CONFIG_DETAILS_PATH = ( + "model_deployment_configuration_details.model_configuration_details" + ) + + payload_attribute_map = { + CONST_PROJECT_ID: "project_id", + CONST_COMPARTMENT_ID: "compartment_id", + CONST_SHAPE_NAME: f"{MODEL_CONFIG_DETAILS_PATH}.instance_configuration.instance_shape_name", + CONST_SHAPE_CONFIG_DETAILS: f"{MODEL_CONFIG_DETAILS_PATH}.instance_configuration.model_deployment_instance_shape_config_details", + CONST_REPLICA: f"{MODEL_CONFIG_DETAILS_PATH}.scaling_policy.instance_count", + CONST_BANDWIDTH_MBPS: f"{MODEL_CONFIG_DETAILS_PATH}.bandwidth_mbps", + CONST_ACCESS_LOG: "category_log_details.access", + CONST_PREDICT_LOG: "category_log_details.predict", + CONST_DEPLOYMENT_TYPE: "model_deployment_configuration_details.deployment_type", + CONST_POLICY_TYPE: f"{MODEL_CONFIG_DETAILS_PATH}.scaling_policy.policy_type", + } + + sub_level_attribute_maps = { + CONST_SHAPE_CONFIG_DETAILS: shape_config_details_attribute_map, + CONST_ACCESS_LOG: access_log_attribute_map, + CONST_PREDICT_LOG: predict_log_attribute_map, + } + + def __init__(self, spec: Dict = None, **kwargs) -> None: + super().__init__(spec, **kwargs) + + @property + def kind(self) -> str: + """The kind of the object as showing in YAML. + + Returns + ------- + str + infrastructure + """ + return MODEL_DEPLOYMENT_INFRASTRUCTURE_KIND + + @property + def type(self) -> str: + """The type of the object as showing in YAML. + + Returns + ------- + str + datascienceModelDeployment + """ + return MODEL_DEPLOYMENT_INFRASTRUCTURE_TYPE + + @property + def compartment_id(self) -> str: + """The model deployment compartment id. + + Returns + ------- + str + The model deployment compartment id. + """ + return self.get_spec(self.CONST_COMPARTMENT_ID, None) + + def with_compartment_id( + self, compartment_id: str + ) -> "ModelDeploymentInfrastructure": + """Sets the compartment id of model deployment. + + Parameters + ---------- + compartment_id: str + The compartment id of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_COMPARTMENT_ID, compartment_id) + + @property + def project_id(self) -> str: + """The model deployment project id. + + Returns + ------- + str + The model deployment project id. + """ + return self.get_spec(self.CONST_PROJECT_ID, None) + + def with_project_id(self, project_id: str) -> "ModelDeploymentInfrastructure": + """Sets the project id of model deployment. + + Parameters + ---------- + project_id: str + The project id of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_PROJECT_ID, project_id) + + @property + def shape_config_details(self) -> Dict: + """The model deployment shape config details. + + Returns + ------- + Dict + The model deployment shape config details. + """ + return self.get_spec(self.CONST_SHAPE_CONFIG_DETAILS, {}) + + def with_shape_config_details( + self, ocpus: float, memory_in_gbs: float, **kwargs: Dict[str, Any] + ) -> "ModelDeploymentInfrastructure": + """Sets the shape config details of model deployment. + + Parameters + ---------- + ocpus: float + The ocpus number. + memory_in_gbs: float + The memory in gbs number. + kwargs: Dict + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec( + self.CONST_SHAPE_CONFIG_DETAILS, + { + self.CONST_OCPUS: ocpus, + self.CONST_MEMORY_IN_GBS: memory_in_gbs, + **kwargs, + }, + ) + + @property + def shape_name(self) -> str: + """The model deployment shape name. + + Returns + ------- + str + The model deployment shape name. + """ + return self.get_spec(self.CONST_SHAPE_NAME, None) + + def with_shape_name(self, shape_name: str) -> "ModelDeploymentInfrastructure": + """Sets the shape name of model deployment. + + Parameters + ---------- + shape_name: str + The shape name of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_SHAPE_NAME, shape_name) + + @property + def replica(self) -> int: + """The model deployment instance count. + + Returns + ------- + int + The model deployment instance count. + """ + return self.get_spec(self.CONST_REPLICA, None) + + def with_replica(self, replica: int) -> "ModelDeploymentInfrastructure": + """Sets the instance count of model deployment. + + Parameters + ---------- + replica: int + The instance count of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_REPLICA, replica) + + @property + def bandwidth_mbps(self) -> int: + """The model deployment bandwidth in mbps. + + Returns + ------- + int + The model deployment bandwidth in mbps. + """ + return self.get_spec(self.CONST_BANDWIDTH_MBPS, None) + + def with_bandwidth_mbps( + self, bandwidth_mbps: int + ) -> "ModelDeploymentInfrastructure": + """Sets the bandwidth of model deployment. + + Parameters + ---------- + bandwidth_mbps: int + The bandwidth of model deployment in mbps. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_BANDWIDTH_MBPS, bandwidth_mbps) + + @property + def access_log(self) -> Dict: + """The model deployment access log. + + Returns + ------- + Dict + The model deployment access log. + """ + return self.get_spec(self.CONST_ACCESS_LOG, {}) + + def with_access_log( + self, log_group_id: str, log_id: str + ) -> "ModelDeploymentInfrastructure": + """Sets the access log of model deployment. + + Parameters + ---------- + log_group_id: str + The access log group id of model deployment. + log_id: str + The access log id of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec( + self.CONST_ACCESS_LOG, + {self.CONST_LOG_GROUP_ID: log_group_id, self.CONST_LOG_ID: log_id}, + ) + + @property + def predict_log(self) -> Dict: + """The model deployment predict log. + + Returns + ------- + Dict + The model deployment predict log. + """ + return self.get_spec(self.CONST_PREDICT_LOG, {}) + + def with_predict_log( + self, log_group_id: str, log_id: str + ) -> "ModelDeploymentInfrastructure": + """Sets the predict log of model deployment. + + Parameters + ---------- + log_group_id: str + The predict log group id of model deployment. + log_id: str + The predict log id of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec( + self.CONST_PREDICT_LOG, + {self.CONST_LOG_GROUP_ID: log_group_id, self.CONST_LOG_ID: log_id}, + ) + + @property + def log_group_id(self) -> str: + """The model deployment log group id. + + Returns + ------- + str + The model deployment log group id. + """ + return self.get_spec(self.CONST_LOG_GROUP_ID, None) + + def with_log_group_id(self, log_group_id: str) -> "ModelDeploymentInfrastructure": + """Sets the log group id of model deployment. + + Parameters + ---------- + log_group_id: str + The predict and access log group id of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_LOG_GROUP_ID, log_group_id) + + @property + def log_id(self) -> str: + """The model deployment log id. + + Returns + ------- + str + The model deployment log id. + """ + return self.get_spec(self.CONST_LOG_ID, None) + + def with_log_id(self, log_id: str) -> "ModelDeploymentInfrastructure": + """Sets the log id of model deployment. + + Parameters + ---------- + log_id: str + The predict and access log id of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_LOG_ID, log_id) + + @property + def web_concurrency(self) -> int: + """The model deployment web concurrency. + + Returns + ------- + int + The model deployment web concurrency. + """ + return self.get_spec(self.CONST_WEB_CONCURRENCY, None) + + def with_web_concurrency( + self, web_concurrency: int + ) -> "ModelDeploymentInfrastructure": + """Sets the web concurrency of model deployment. + + Parameters + ---------- + web_concurrency: int + The web concurrency of model deployment. + + Returns + ------- + ModelDeploymentInfrastructure + The ModelDeploymentInfrastructure instance (self). + """ + return self.set_spec(self.CONST_WEB_CONCURRENCY, web_concurrency) diff --git a/ads/model/deployment/model_deployment_properties.py b/ads/model/deployment/model_deployment_properties.py index 5c1938a13..9734b9cdb 100644 --- a/ads/model/deployment/model_deployment_properties.py +++ b/ads/model/deployment/model_deployment_properties.py @@ -1,14 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from typing import Any, Dict, Optional, Union +import warnings import oci.data_science.models as data_science_models from ads.common import utils -from ads.common.auth import default_signer -from ads.common.oci_client import OCIClientFactory from ads.common.oci_datascience import OCIDataScienceMixin from .common.utils import OCIClientManager @@ -124,14 +123,16 @@ def __init__( model_id is None AND not specified in oci_model_deployment.model_deployment_configuration_details.model_configuration_details. """ - if not config: - config = default_signer() - self.config = config + if config: + warnings.warn( + "`config` will be deprecated in 3.0.0 and will be ignored now." + ) + self.model_id = model_id self.model_uri = model_uri - self.ds_client = OCIClientFactory(**self.config).data_science oci_kwargs = {} + self.oci_model_deployment = oci_model_deployment if oci_model_deployment: # User specified oci_model_deployment, which could be an OCI model object or a dict if isinstance(oci_model_deployment, dict): @@ -452,7 +453,7 @@ def build(self) -> data_science_models.CreateModelDeploymentDetails: """ if self.model_uri: - self.model_id = OCIClientManager(self.config).prepare_artifact( + self.model_id = OCIClientManager().prepare_artifact( model_uri=self.model_uri, properties=dict( display_name="testing", diff --git a/ads/model/deployment/model_deployment_runtime.py b/ads/model/deployment/model_deployment_runtime.py new file mode 100644 index 000000000..824ed452c --- /dev/null +++ b/ads/model/deployment/model_deployment_runtime.py @@ -0,0 +1,534 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/from typing import Dict + + +from typing import Dict, List +from ads.jobs.builders.base import Builder + +MODEL_DEPLOYMENT_RUNTIME_KIND = "runtime" + + +class ModelDeploymentRuntimeType: + CONDA = "conda" + CONTAINER = "container" + + +class OCIModelDeploymentRuntimeType: + CONDA = "DEFAULT" + CONTAINER = "OCIR_CONTAINER" + + +class ModelDeploymentRuntime(Builder): + """A class used to represent a Model Deployment Runtime. + + Attributes + ---------- + env: Dict + The environment variables of model deployment runtime. + deployment_mode: str + The deployment mode of model deployment. + input_stream_ids: List + The input stream ids of model deployment. + output_stream_ids: List + The output stream ids of model deployment. + model_uri: str + The model uri of model deployment. + + Methods + ------- + with_env(env) + Sets the environment variables of model deployment + with_deployment_mode(deployment_mode) + Sets the deployment mode of model deployment + with_input_stream_ids(input_stream_ids) + Sets the input stream ids of model deployment + with_output_stream_ids(output_stream_ids) + Sets the output stream ids of model deployment + with_model_uri(model_uri) + Sets the model uri of model deployment + """ + + CONST_MODEL_ID = "modelId" + CONST_MODEL_URI = "modelUri" + CONST_ENV = "env" + CONST_ENVIRONMENT_VARIABLES = "environmentVariables" + CONST_ENVIRONMENT_CONFIG_TYPE = "environmentConfigurationType" + CONST_DEPLOYMENT_MODE = "deploymentMode" + CONST_INPUT_STREAM_IDS = "inputStreamIds" + CONST_OUTPUT_STREAM_IDS = "outputStreamIds" + CONST_ENVIRONMENT_CONFIG_DETAILS = "environmentConfigurationDetails" + + attribute_map = { + CONST_ENV: "env", + CONST_ENVIRONMENT_VARIABLES: "environment_variables", + CONST_INPUT_STREAM_IDS: "input_stream_ids", + CONST_OUTPUT_STREAM_IDS: "output_stream_ids", + CONST_DEPLOYMENT_MODE: "deployment_mode", + CONST_MODEL_URI: "model_uri", + } + + ENVIRONMENT_CONFIG_DETAILS_PATH = ( + "model_deployment_configuration_details.environment_configuration_details" + ) + STREAM_CONFIG_DETAILS_PATH = ( + "model_deployment_configuration_details.stream_configuration_details" + ) + MODEL_CONFIG_DETAILS_PATH = ( + "model_deployment_configuration_details.model_configuration_details" + ) + + payload_attribute_map = { + CONST_ENV: f"{ENVIRONMENT_CONFIG_DETAILS_PATH}.environment_variables", + CONST_INPUT_STREAM_IDS: f"{STREAM_CONFIG_DETAILS_PATH}.input_stream_ids", + CONST_OUTPUT_STREAM_IDS: f"{STREAM_CONFIG_DETAILS_PATH}.output_stream_ids", + CONST_DEPLOYMENT_MODE: "deployment_mode", + CONST_MODEL_URI: f"{MODEL_CONFIG_DETAILS_PATH}.model_id", + } + + def __init__(self, spec: Dict = None, **kwargs) -> None: + super().__init__(spec, **kwargs) + + @property + def kind(self) -> str: + """The kind of the object as showing in YAML. + + Returns + ------- + str + runtime + """ + return MODEL_DEPLOYMENT_RUNTIME_KIND + + @property + def env(self) -> Dict: + """The environment variables of model deployment. + + Returns + ------- + Dict + The environment variables of model deployment. + """ + return self.get_spec(self.CONST_ENV, {}) + + def with_env(self, env: Dict) -> "ModelDeploymentRuntime": + """Sets the environment variables of model deployment. + + Parameters + ---------- + env: Dict + The environment variables of model deployment. + + Returns + ------- + ModelDeploymentRuntime + The ModelDeploymentRuntime instance (self). + """ + return self.set_spec(self.CONST_ENV, env) + + @property + def deployment_mode(self) -> str: + """The deployment mode of model deployment. + + Returns + ------- + str + The deployment mode of model deployment. + """ + return self.get_spec(self.CONST_DEPLOYMENT_MODE, None) + + def with_deployment_mode(self, deployment_mode: str) -> "ModelDeploymentRuntime": + """Sets the deployment mode of model deployment. + Can be HTTPS_ONLY or STREAM_ONLY. + + Parameters + ---------- + deployment_mode: str + The deployment mode of model deployment. + + Returns + ------- + ModelDeploymentRuntime + The ModelDeploymentRuntime instance (self). + """ + return self.set_spec(self.CONST_DEPLOYMENT_MODE, deployment_mode) + + @property + def input_stream_ids(self) -> List[str]: + """The input stream ids of model deployment. + + Returns + ------- + List + The input stream ids of model deployment. + """ + return self.get_spec(self.CONST_INPUT_STREAM_IDS, []) + + def with_input_stream_ids( + self, input_stream_ids: List[str] + ) -> "ModelDeploymentRuntime": + """Sets the input stream ids of model deployment. + + Parameters + ---------- + input_stream_ids: List + The input stream ids of model deployment. + + Returns + ------- + ModelDeploymentRuntime + The ModelDeploymentRuntime instance (self). + """ + return self.set_spec(self.CONST_INPUT_STREAM_IDS, input_stream_ids) + + @property + def output_stream_ids(self) -> List[str]: + """The output stream ids of model deployment. + + Returns + ------- + List + The output stream ids of model deployment. + """ + return self.get_spec(self.CONST_OUTPUT_STREAM_IDS, []) + + def with_output_stream_ids( + self, output_stream_ids: List[str] + ) -> "ModelDeploymentRuntime": + """Sets the output stream ids of model deployment. + + Parameters + ---------- + output_stream_ids: List + The output stream ids of model deployment. + + Returns + ------- + ModelDeploymentRuntime + The ModelDeploymentRuntime instance (self). + """ + return self.set_spec(self.CONST_OUTPUT_STREAM_IDS, output_stream_ids) + + @property + def model_uri(self) -> str: + """The model uri of model deployment. + + Returns + ------- + List + The model uri of model deployment. + """ + return self.get_spec(self.CONST_MODEL_URI, None) + + def with_model_uri(self, model_uri: str) -> "ModelDeploymentRuntime": + """Sets the model uri of model deployment. + + Parameters + ---------- + model_uri: str + The model uri of model deployment. + + Returns + ------- + ModelDeploymentRuntime + The ModelDeploymentRuntime instance (self). + """ + return self.set_spec(self.CONST_MODEL_URI, model_uri) + + +class ModelDeploymentCondaRuntime(ModelDeploymentRuntime): + """A class used to represent a Model Deployment Conda Runtime. + + Examples + -------- + >>> conda_runtime = ModelDeploymentCondaRuntime() + ... .with_env({"key":"value"}) + ... .with_deployment_mode("HTTPS_ONLY") + ... .with_model_uri() + >>> conda_runtime.to_dict() + """ + + @property + def type(self) -> str: + """The type of the object as showing in YAML. + + Returns + ------- + str + conda + """ + return ModelDeploymentRuntimeType.CONDA + + @property + def environment_config_type(self) -> str: + """The environment config type of model deployment. + + Returns + ------- + str + DEFAULT + """ + return OCIModelDeploymentRuntimeType.CONDA + + +class ModelDeploymentContainerRuntime(ModelDeploymentRuntime): + """A class used to represent a Model Deployment Container Runtime. + + Attributes + ---------- + image: str + The image of model deployment container runtime. + image_digest: str + The image digest of model deployment container runtime. + cmd: List + The cmd of model deployment container runtime. + entrypoint: List + The entrypoint of model deployment container runtime. + server_port: int + The server port of model deployment container runtime. + health_check_port: int + The health check port of model deployment container runtime. + + Methods + ------- + with_image(image) + Sets the image of model deployment container runtime + with_image_digest(image_digest) + Sets the image digest of model deployment container runtime + with_cmd(cmd) + Sets the cmd of model deployment container runtime + with_entrypoint(entrypoint) + Sets the entrypoint of model deployment container runtime + with_server_port(server_port) + Sets the server port of model deployment container runtime + with_health_check_port(health_check_port) + Sets the health check port of model deployment container runtime + + Examples + -------- + Build runtime from builder apis: + >>> container_runtime = ModelDeploymentContainerRuntime() + ... .with_image() + ... .with_image_digest() + ... .with_entrypoint() + ... .with_server_port() + ... .with_health_check_port() + ... .with_env({"key":"value"}) + ... .with_deployment_mode("HTTPS_ONLY") + ... .with_model_uri() + >>> container_runtime.to_dict() + + Build runtime from yaml: + >>> container_runtime = ModelDeploymentContainerRuntime.from_yaml(uri=) + """ + + CONST_IMAGE = "image" + CONST_IMAGE_DIGEST = "imageDigest" + CONST_CMD = "cmd" + CONST_ENTRYPOINT = "entrypoint" + CONST_SERVER_PORT = "serverPort" + CONST_HEALTH_CHECK_PORT = "healthCheckPort" + + attribute_map = { + **ModelDeploymentRuntime.attribute_map, + CONST_IMAGE: "image", + CONST_IMAGE_DIGEST: "image_digest", + CONST_CMD: "cmd", + CONST_ENTRYPOINT: "entrypoint", + CONST_SERVER_PORT: "server_port", + CONST_HEALTH_CHECK_PORT: "health_check_port", + } + + payload_attribute_map = { + **ModelDeploymentRuntime.payload_attribute_map, + CONST_IMAGE: f"{ModelDeploymentRuntime.ENVIRONMENT_CONFIG_DETAILS_PATH}.image", + CONST_IMAGE_DIGEST: f"{ModelDeploymentRuntime.ENVIRONMENT_CONFIG_DETAILS_PATH}.image_digest", + CONST_CMD: f"{ModelDeploymentRuntime.ENVIRONMENT_CONFIG_DETAILS_PATH}.cmd", + CONST_ENTRYPOINT: f"{ModelDeploymentRuntime.ENVIRONMENT_CONFIG_DETAILS_PATH}.entrypoint", + CONST_SERVER_PORT: f"{ModelDeploymentRuntime.ENVIRONMENT_CONFIG_DETAILS_PATH}.server_port", + CONST_HEALTH_CHECK_PORT: f"{ModelDeploymentRuntime.ENVIRONMENT_CONFIG_DETAILS_PATH}.health_check_port", + } + + @property + def type(self) -> str: + """The type of the object as showing in YAML. + + Returns + ------- + str + container + """ + return ModelDeploymentRuntimeType.CONTAINER + + @property + def environment_config_type(self) -> str: + """The environment config type of model deployment. + + Returns + ------- + str + OCIR_CONTAINER + """ + return OCIModelDeploymentRuntimeType.CONTAINER + + @property + def image(self) -> str: + """The image of model deployment container runtime. + + Returns + ------- + str + The image of model deployment container runtime. + """ + return self.get_spec(self.CONST_IMAGE, None) + + def with_image(self, image: str) -> "ModelDeploymentContainerRuntime": + """Sets the image of model deployment container runtime. + + Parameters + ---------- + image: str + The image of model deployment container runtime. + + Returns + ------- + ModelDeploymentContainerRuntime + The ModelDeploymentContainerRuntime instance (self). + """ + return self.set_spec(self.CONST_IMAGE, image) + + @property + def image_digest(self) -> str: + """The image digest of model deployment container runtime. + + Returns + ------- + str + The image digest of model deployment container runtime. + """ + return self.get_spec(self.CONST_IMAGE_DIGEST, None) + + def with_image_digest(self, image_digest: str) -> "ModelDeploymentContainerRuntime": + """Sets the image digest of model deployment container runtime. + + Parameters + ---------- + image_digest: str + The image digest of model deployment container runtime. + + Returns + ------- + ModelDeploymentContainerRuntime + The ModelDeploymentContainerRuntime instance (self). + """ + return self.set_spec(self.CONST_IMAGE_DIGEST, image_digest) + + @property + def cmd(self) -> List[str]: + """The command lines of model deployment container runtime. + + Returns + ------- + List + The command lines of model deployment container runtime. + """ + return self.get_spec(self.CONST_CMD, []) + + def with_cmd(self, cmd: List[str]) -> "ModelDeploymentContainerRuntime": + """Sets the cmd of model deployment container runtime. + + Parameters + ---------- + cmd: List + The cmd of model deployment container runtime. + + Returns + ------- + ModelDeploymentContainerRuntime + The ModelDeploymentContainerRuntime instance (self). + """ + return self.set_spec(self.CONST_CMD, cmd) + + @property + def entrypoint(self) -> List[str]: + """The entry point of model deployment container runtime. + + Returns + ------- + List + The entry point of model deployment container runtime. + """ + return self.get_spec(self.CONST_ENTRYPOINT, []) + + def with_entrypoint( + self, entrypoint: List[str] + ) -> "ModelDeploymentContainerRuntime": + """Sets the entrypoint of model deployment container runtime. + + Parameters + ---------- + entrypoint: List + The entrypoint of model deployment container runtime. + + Returns + ------- + ModelDeploymentContainerRuntime + The ModelDeploymentContainerRuntime instance (self). + """ + return self.set_spec(self.CONST_ENTRYPOINT, entrypoint) + + @property + def server_port(self) -> int: + """The server port of model deployment container runtime. + + Returns + ------- + int + The server port of model deployment container runtime. + """ + return self.get_spec(self.CONST_SERVER_PORT, None) + + def with_server_port(self, server_port: int) -> "ModelDeploymentContainerRuntime": + """Sets the server port of model deployment container runtime. + + Parameters + ---------- + server_port: List + The server port of model deployment container runtime. + + Returns + ------- + ModelDeploymentContainerRuntime + The ModelDeploymentContainerRuntime instance (self). + """ + return self.set_spec(self.CONST_SERVER_PORT, server_port) + + @property + def health_check_port(self) -> int: + """The health check port of model deployment container runtime. + + Returns + ------- + int + The health check port of model deployment container runtime. + """ + return self.get_spec(self.CONST_HEALTH_CHECK_PORT, None) + + def with_health_check_port( + self, health_check_port: int + ) -> "ModelDeploymentContainerRuntime": + """Sets the health check port of model deployment container runtime. + + Parameters + ---------- + health_check_port: List + The health check port of model deployment container runtime. + + Returns + ------- + ModelDeploymentContainerRuntime + The ModelDeploymentContainerRuntime instance (self). + """ + return self.set_spec(self.CONST_HEALTH_CHECK_PORT, health_check_port) diff --git a/ads/model/extractor/automl_extractor.py b/ads/model/extractor/automl_extractor.py index f10d2738a..0f739df6d 100644 --- a/ads/model/extractor/automl_extractor.py +++ b/ads/model/extractor/automl_extractor.py @@ -1,9 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +from ads.common.decorator.deprecate import deprecated from ads.model.extractor.model_info_extractor import ModelInfoExtractor from ads.model.model_metadata import Framework @@ -19,6 +20,10 @@ class AutoMLExtractor(ModelInfoExtractor): The estimator to extract metadata from. """ + @deprecated( + details="Working with AutoML has moved from within ADS to working directly with the AutoMLx library. AutoMLx are preinstalled in conda pack automlx_p38_cpu_v2 and later, and can now be updated independently of ADS. AutoMLx documentation may be found at https://docs.oracle.com/en-us/iaas/tools/automlx/latest/html/multiversion/v23.1.1/index.html. Notebook examples are in Oracle's samples repository: https://github.com/oracle-samples/oci-data-science-ai-samples/tree/master/notebook_examples and a migration tutorial can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_training/automl/quick_start.html .", + raise_error=True, + ) def __init__(self, model): self.model = model diff --git a/ads/model/extractor/huggingface_extractor.py b/ads/model/extractor/huggingface_extractor.py new file mode 100644 index 000000000..66f69a288 --- /dev/null +++ b/ads/model/extractor/huggingface_extractor.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*-- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +from ads.model.extractor.model_info_extractor import ( + ModelInfoExtractor, + normalize_hyperparameter, +) +from ads.model.model_metadata import Framework +from ads.common.decorator.runtime_dependency import ( + runtime_dependency, + OptionalDependency, +) + + +class HuggingFaceExtractor(ModelInfoExtractor): + """Class that extract model metadata from HuggingFacePipeline models. + + Attributes + ---------- + model: object + The model to extract metadata from. + estimator: object + The estimator to extract metadata from. + + Methods + ------- + framework(self) -> str + Returns the framework of the model. + algorithm(self) -> object + Returns the algorithm of the model. + version(self) -> str + Returns the version of framework of the model. + hyperparameter(self) -> dict + Returns the hyperparameter of the model. + """ + + def __init__(self, model): + self.model = model + + @property + def framework(self): + """Extracts the framework of the model. + + Returns + ---------- + str: + The framework of the model. + """ + return Framework.TRANSFORMERS + + @property + def algorithm(self): + """Extracts the algorithm of the model. + + Returns + ---------- + object: + The algorithm of the model. + """ + return self.model.__class__.__name__ + + @property + @runtime_dependency( + module="transformers", install_from=OptionalDependency.HUGGINGFACE + ) + def version(self): + """Extracts the framework version of the model. + + Returns + ---------- + str: + The framework version of the model. + """ + return transformers.__version__ + + @property + def hyperparameter(self): + """Extracts the hyperparameters of the model. + + Returns + ---------- + dict: + The hyperparameters of the model. + """ + return normalize_hyperparameter(self.model.model.config.to_dict()) diff --git a/ads/model/extractor/model_info_extractor_factory.py b/ads/model/extractor/model_info_extractor_factory.py index b4d9a4baf..37c191f0b 100644 --- a/ads/model/extractor/model_info_extractor_factory.py +++ b/ads/model/extractor/model_info_extractor_factory.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from ads.model.model_metadata import Framework @@ -14,6 +14,7 @@ from ads.model.extractor.spark_extractor import SparkExtractor from ads.model.extractor.pytorch_extractor import PytorchExtractor from ads.model.extractor.tensorflow_extractor import TensorflowExtractor +from ads.model.extractor.huggingface_extractor import HuggingFaceExtractor ORDERED_FRAMEWORKS = [ @@ -42,6 +43,7 @@ class ModelInfoExtractorFactory: Framework.TENSORFLOW: TensorflowExtractor, Framework.PYTORCH: PytorchExtractor, Framework.SPARK: SparkExtractor, + Framework.TRANSFORMERS: HuggingFaceExtractor, } @staticmethod diff --git a/ads/model/framework/automl_model.py b/ads/model/framework/automl_model.py index efc47581c..08fb702de 100644 --- a/ads/model/framework/automl_model.py +++ b/ads/model/framework/automl_model.py @@ -1,21 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import os -from typing import Callable, Dict, List, Optional, Tuple, Union -import cloudpickle -import numpy as np -import pandas as pd +from typing import Callable, Dict, List, Optional from ads.common import logger from ads.model.extractor.automl_extractor import AutoMLExtractor from ads.model.generic_model import FrameworkSpecificModel from ads.model.model_properties import ModelProperties - -DEFAULT_PKL_FORMAT_MODEL_FILE_NAME = "model.pkl" +from ads.model.serde.common import SERDE +from ads.common.decorator.deprecate import deprecated class AutoMLModel(FrameworkSpecificModel): @@ -31,8 +27,6 @@ class AutoMLModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained automl estimator/model using oracle automl. framework: str @@ -55,6 +49,7 @@ class AutoMLModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -101,7 +96,7 @@ class AutoMLModel(FrameworkSpecificModel): >>> from ads.automl.provider import OracleAutoMLProvider >>> from ads.dataset.dataset_browser import DatasetBrowser >>> from ads.model.framework.automl_model import AutoMLModel - >>> from ads.common.model_metadata import UseCaseType + >>> from ads.model.model_metadata import UseCaseType >>> ds = DatasetBrowser.sklearn().open("wine").set_target("target") >>> train, test = ds.train_test_split(test_size=0.1, random_state = 42) @@ -121,12 +116,18 @@ class AutoMLModel(FrameworkSpecificModel): _PREFIX = "automl" + @deprecated( + details=f"Working with AutoML has moved from within ADS to working directly with the automlx library. Please use `GenericModel` https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/frameworks/genericmodel.html class instead. An example can be found at https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/frameworks/automlmodel.html", + raise_error=True, + ) def __init__( self, estimator: Callable, artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = None, + model_input_serializer: Optional[SERDE] = None, **kwargs, ): """ @@ -144,6 +145,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -162,6 +167,8 @@ def __init__( artifact_dir=artifact_dir, properties=properties, auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, **kwargs, ) self._extractor = AutoMLExtractor(estimator) @@ -169,64 +176,3 @@ def __init__( self.algorithm = self._extractor.algorithm self.version = self._extractor.version self.hyperparameter = self._extractor.hyperparameter - - @staticmethod - def _handle_model_file_name(as_onnx: bool, model_file_name: str): - if as_onnx: - raise NotImplementedError( - "AutoML framework does not support onnx serialization." - ) - - if not model_file_name: - return DEFAULT_PKL_FORMAT_MODEL_FILE_NAME - - if model_file_name and not model_file_name.endswith(".pkl"): - raise ValueError( - "`model_file_name` has to be ending with `.pkl` for pkl format." - ) - return model_file_name - - def serialize_model( - self, - force_overwrite: Optional[bool] = False, - X_sample: Optional[ - Union[ - Dict, - str, - List, - Tuple, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - ] - ] = None, - **kwargs: Dict, - ): - """ - Serialize and save AutoML model using pkl. - - Parameters - ---------- - force_overwrite: (bool, optional). Defaults to False. - If set as True, overwrite serialized model if exists. - X_sample: Union[Dict, str, List, np.ndarray, pd.core.series.Series, pd.core.frame.DataFrame,]. Defaults to None. - Contains model inputs such that model(X_sample) is a valid invocation of the model. - Used to generate input schema. - - Returns - ------- - None - Nothing. - """ - model_path = os.path.join(self.artifact_dir, self.model_file_name) - if os.path.exists(model_path) and not force_overwrite: - raise ValueError( - "Model file already exists and will not be overwritten. " - "Set `force_overwrite` to True if you wish to overwrite." - ) - else: - if not os.path.exists(self.artifact_dir): - os.makedirs(self.artifact_dir) - - with open(model_path, "wb") as outfile: - cloudpickle.dump(self.estimator, outfile) diff --git a/ads/model/framework/huggingface_model.py b/ads/model/framework/huggingface_model.py new file mode 100644 index 000000000..6fbf1a237 --- /dev/null +++ b/ads/model/framework/huggingface_model.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*-- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +import PIL + +from ads.model.extractor.huggingface_extractor import HuggingFaceExtractor +from ads.model.generic_model import FrameworkSpecificModel +from ads.model.model_properties import ModelProperties +from ads.model.serde.model_serializer import HuggingFaceSerializerType +from ads.common.decorator.runtime_dependency import ( + runtime_dependency, + OptionalDependency, +) +from ads.model.serde.model_serializer import ModelSerializerType +from ads.model.serde.common import SERDE + + +class HuggingFacePipelineModel(FrameworkSpecificModel): + """HuggingFacePipelineModel class for estimators from HuggingFace framework. + + Attributes + ---------- + algorithm: str + The algorithm of the model. + artifact_dir: str + Artifact directory to store the files needed for deployment. + auth: Dict + Default authentication is set using the `ads.set_auth` API. To override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create + an authentication signer to instantiate an IdentityClient object. + estimator: Callable + A trained HuggingFace Pipeline using transformers. + framework: str + "transformers", the framework name of the model. + hyperparameter: dict + The hyperparameters of the estimator. + metadata_custom: ModelCustomMetadata + The model custom metadata. + metadata_provenance: ModelProvenanceMetadata + The model provenance metadata. + metadata_taxonomy: ModelTaxonomyMetadata + The model taxonomy metadata. + model_artifact: ModelArtifact + This is built by calling prepare. + model_deployment: ModelDeployment + A ModelDeployment instance. + model_file_name: str + Name of the serialized model. + model_id: str + The model ID. + properties: ModelProperties + ModelProperties object required to save and deploy model. + runtime_info: RuntimeInfo + A RuntimeInfo instance. + schema_input: Schema + Schema describes the structure of the input data. + schema_output: Schema + Schema describes the structure of the output data. + serialize: bool + Whether to serialize the model to pkl file by default. If False, you need to serialize the model manually, + save it under artifact_dir and update the score.py manually. + version: str + The framework version of the model. + + Methods + ------- + delete_deployment(...) + Deletes the current model deployment. + deploy(..., **kwargs) + Deploys a model. + from_model_artifact(uri, model_file_name, artifact_dir, ..., **kwargs) + Loads model from the specified folder, or zip/tar archive. + from_model_catalog(model_id, model_file_name, artifact_dir, ..., **kwargs) + Loads model from model catalog. + introspect(...) + Runs model introspection. + predict(data, ...) + Returns prediction of input data run against the model deployment endpoint. + prepare(..., **kwargs) + Prepare and save the score.py, serialized model and runtime.yaml file. + reload(...) + Reloads the model artifact files: `score.py` and the `runtime.yaml`. + save(..., **kwargs) + Saves model artifacts to the model catalog. + summary_status(...) + Gets a summary table of the current status. + verify(data, ...) + Tests if deployment works in local environment. + + Examples - Image Classification + ------------------------------- + >>> from transformers import pipeline + >>> import tempfile + >>> import PIL.Image + >>> import ads + >>> import requests + >>> import cloudpickle + >>> ## Download image data + >>> image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" + >>> image = PIL.Image.open(requests.get(image_link, stream=True).raw) + >>> image_bytes = cloudpickle.dumps(image) # convert image to bytes + >>> ## Download a pretrained model + >>> vision_classifier = pipeline(model="google/vit-base-patch16-224") + >>> preds = vision_classifier(images=image) + >>> ## Initiate a HuggingFacePipelineModel instance + >>> vision_model = HuggingFacePipelineModel(vision_classifier, artifact_dir=tempfile.mkdtemp()) + >>> ## Prepare + >>> vision_model.prepare(inference_conda_env="pytorch110_p38_cpu_v1", force_overwrite=True) + >>> ## Verify + >>> vision_model.verify(image) + >>> vision_model.verify(image_bytes) + >>> ## Save + >>> vision_model.save() + >>> ## Deploy + >>> log_group_id = "" + >>> log_id = "" + >>> vision_model.deploy(deployment_bandwidth_mbps=1000, + ... wait_for_completion=False, + ... deployment_log_group_id = log_group_id, + ... deployment_access_log_id = log_id, + ... deployment_predict_log_id = log_id) + >>> ## Predict from endpoint + >>> vision_model.predict(image) + >>> vision_model.predict(image_bytes) + >>> ### Invoke the model + >>> auth = ads.common.auth.default_signer()['signer'] + >>> endpoint = vision_model.model_deployment.url + "/predict" + >>> headers = {"Content-Type": "application/octet-stream"} + >>> requests.post(endpoint, data=image_bytes, auth=auth, headers=headers).json() + + Examples - Image Segmentation + ----------------------------- + >>> from transformers import pipeline + >>> import tempfile + >>> import PIL.Image + >>> import ads + >>> import requests + >>> import cloudpickle + >>> ## Download image data + >>> image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" + >>> image = PIL.Image.open(requests.get(image_link, stream=True).raw) + >>> image_bytes = cloudpickle.dumps(image) # convert image to bytes + >>> ## Download pretrained model + >>> segmenter = pipeline(task="image-segmentation") + >>> preds = segmenter(image) + >>> ## Initiate a HuggingFacePipelineModel instance + >>> segmentation_model = HuggingFacePipelineModel(segmenter, artifact_dir=empfile.mkdtemp()) + >>> ## Prepare + >>> conda = "oci://bucket@namespace/path/to/conda/pack" + >>> python_version = "3.8" + >>> segmentation_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + >>> ## Verify + >>> segmentation_model.verify(data=image) + >>> segmentation_model.verify(data=image_bytes) + >>> ## Save + >>> segmentation_model.save() + >>> log_group_id = "" + >>> log_id = "" + >>> ## Deploy + >>> segmentation_model.deploy(deployment_bandwidth_mbps=1000, + wait_for_completion=False, + deployment_log_group_id = log_group_id, + deployment_access_log_id = log_id, + deployment_predict_log_id = log_id) + >>> ## Predict from endpoint + >>> segmentation_model.predict(image) + >>> segmentation_model.predict(image_bytes) + >>> ## Invoke the model + >>> auth = ads.common.auth.default_signer()['signer'] + + >>> endpoint = segmentation_model.model_deployment.url + "/predict" + >>> headers = {"Content-Type": "application/octet-stream"} + >>> requests.post(endpoint, data=image_bytes, auth=auth, headers=headers).json() + + Examples - Zero Shot Image Classification + ----------------------------------------- + >>> from transformers import pipeline + >>> import tempfile + >>> import PIL.Image + >>> import ads + >>> import requests + >>> import cloudpickle + >>> ## Download the image data + >>> image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + >>> image = PIL.Image.open(requests.get(image_link, stream=True).raw) + >>> image_bytes = cloudpickle.dumps(image) + >>> ## Download a pretrained model + >>> classifier = pipeline(model="openai/clip-vit-large-patch14") + >>> classifier( + images=image, + candidate_labels=["animals", "humans", "landscape"], + ) + >>> ## Initiate a HuggingFacePipelineModel instance + >>> zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) + >>> conda = "oci://bucket@namespace/path/to/conda/pack" + >>> python_version = "3.8" + >>> ## Prepare + >>> zero_shot_image_classification_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + >>> data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} + >>> body = cloudpickle.dumps(data) # convert image to bytes + >>> ## Verify + >>> zero_shot_image_classification_model.verify(data=data) + >>> zero_shot_image_classification_model.verify(data=body) + >>> ## Save + >>> zero_shot_image_classification_model.save() + >>> ## Deploy + >>> log_group_id = "" + >>> log_id = "" + >>> zero_shot_image_classification_model.deploy(deployment_bandwidth_mbps=1000, + wait_for_completion=False, + deployment_log_group_id = log_group_id, + deployment_access_log_id = log_id, + deployment_predict_log_id = log_id) + >>> ## Predict from endpoint + >>> zero_shot_image_classification_model.predict(image) + >>> zero_shot_image_classification_model.predict(body) + >>> ### Invoke the model + >>> auth = ads.common.auth.default_signer()['signer'] + >>> endpoint = zero_shot_image_classification_model.model_deployment.url + "/predict" + >>> headers = {"Content-Type": "application/octet-stream"} + >>> requests.post(endpoint, data=body, auth=auth, headers=headers).json() + """ + + _PREFIX = "huggingface" + model_save_serializer_type = HuggingFaceSerializerType + + @runtime_dependency( + module="transformers", install_from=OptionalDependency.HUGGINGFACE + ) + def __init__( + self, + estimator: Callable, + artifact_dir: str, + properties: Optional[ModelProperties] = None, + auth: Dict = None, + model_save_serializer: Optional[SERDE] = model_save_serializer_type.HUGGINGFACE, + model_input_serializer: Optional[SERDE] = ModelSerializerType.CLOUDPICKLE, + **kwargs, + ): + """ + Initiates a HuggingFacePipelineModel instance. + + Parameters + ---------- + estimator: Callable + HuggingFacePipeline Model + artifact_dir: str + Directory for generate artifact. + properties: (ModelProperties, optional). Defaults to None. + ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. + auth :(Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. + + Returns + ------- + HuggingFacePipelineModel + HuggingFacePipelineModel instance. + + Examples + -------- + >>> from transformers import pipeline + >>> import tempfile + >>> import PIL.Image + >>> import ads + >>> import requests + >>> import cloudpickle + >>> ## download the image + >>> image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + >>> image = PIL.Image.open(requests.get(image_link, stream=True).raw) + >>> image_bytes = cloudpickle.dumps(image) + >>> ## download the pretrained model + >>> classifier = pipeline(model="openai/clip-vit-large-patch14") + >>> classifier( + images=image, + candidate_labels=["animals", "humans", "landscape"], + ) + >>> ## Initiate a HuggingFacePipelineModel instance + >>> zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=empfile.mkdtemp()) + >>> ## Prepare a model artifact + >>> conda = "oci://bucket@namespace/path/to/conda/pack" + >>> python_version = "3.8" + >>> zero_shot_image_classification_model.prepare(inference_conda_env=conda, inference_python_version = python_version, force_overwrite=True) + >>> ## Test data + >>> data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} + >>> body = cloudpickle.dumps(data) # convert image to bytes + >>> ## Verify + >>> zero_shot_image_classification_model.verify(data=data) + >>> zero_shot_image_classification_model.verify(data=body) + >>> ## Save + >>> zero_shot_image_classification_model.save() + >>> ## Deploy + >>> log_group_id = "" + >>> log_id = "" + >>> zero_shot_image_classification_model.deploy(deployment_bandwidth_mbps=100, + wait_for_completion=False, + deployment_log_group_id = log_group_id, + deployment_access_log_id = log_id, + deployment_predict_log_id = log_id) + >>> zero_shot_image_classification_model.predict(image) + >>> zero_shot_image_classification_model.predict(body) + >>> ### Invoke the model by sending bytes + >>> auth = ads.common.auth.default_signer()['signer'] + >>> endpoint = zero_shot_image_classification_model.model_deployment.url + "/predict" + >>> headers = {"Content-Type": "application/octet-stream"} + >>> requests.post(endpoint, data=body, auth=auth, headers=headers).json() + """ + if not isinstance(estimator, transformers.pipelines.base.Pipeline): + raise TypeError( + f"{str(type(estimator))} is not supported in HuggingFacePipelineModel." + ) + super().__init__( + estimator=estimator, + artifact_dir=artifact_dir, + properties=properties, + auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, + **kwargs, + ) + self._extractor = HuggingFaceExtractor(estimator) + self.framework = self._extractor.framework + self.algorithm = self._extractor.algorithm + self.version = self._extractor.version + self.hyperparameter = self._extractor.hyperparameter + self.task = self.estimator.task + self._score_args["task"] = self.estimator.task + + def _handle_model_file_name( + self, as_onnx: bool = False, model_file_name: str = None + ): + """ + The artifact directory to store model files. + + Parameters + ---------- + as_onnx: bool. Defaults to False + If set as True, it will be ignored as onnx conversion is not supported. + model_file_name: str + Will be ignored as huggingface pipeline requires to folder to store the model + files and those files will be stored at the artifact directory. + + Returns + ------- + str + The artifact directory. + """ + return self.artifact_dir + + def serialize_model( + self, + as_onnx: bool = False, + force_overwrite: bool = False, + X_sample: Optional[Union[Dict, str, List, PIL.Image.Image]] = None, + **kwargs, + ) -> None: + """ + Serialize and save HuggingFace model using model specific method. + + Parameters + ---------- + as_onnx: (bool, optional). Defaults to False. + If set as True, convert into ONNX model. + force_overwrite: (bool, optional). Defaults to False. + If set as True, overwrite serialized model if exists. + X_sample: Union[Dict, str, List, PIL.Image.Image]. Defaults to None. + A sample of input data that will be used to generate input schema and detect onnx_args. + + Returns + ------- + None + Nothing. + """ + + if as_onnx: + raise NotImplementedError( + "HuggingFace Pipeline to onnx conversion is not supported." + ) + + super().serialize_model( + as_onnx=False, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, + ) diff --git a/ads/model/framework/lightgbm_model.py b/ads/model/framework/lightgbm_model.py index be372036e..89cff67b2 100644 --- a/ads/model/framework/lightgbm_model.py +++ b/ads/model/framework/lightgbm_model.py @@ -1,30 +1,21 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import os from typing import Any, Callable, Dict, List, Optional, Tuple, Union import numpy as np import pandas as pd from ads.common import logger from ads.model.extractor.lightgbm_extractor import LightgbmExtractor -from ads.common.decorator.runtime_dependency import ( - runtime_dependency, - OptionalDependency, -) -from ads.common.data_serializer import InputDataSerializer -from ads.model.generic_model import ( - FrameworkSpecificModel, - DEFAULT_ONNX_FORMAT_MODEL_FILE_NAME, - DEFAULT_JOBLIB_FORMAT_MODEL_FILE_NAME, - DEFAULT_TXT_FORMAT_MODEL_FILE_NAME, -) +from ads.model.generic_model import FrameworkSpecificModel from ads.model.model_properties import ModelProperties -from joblib import dump +from ads.model.serde.model_serializer import LightGBMModelSerializerType +from ads.model.common.utils import DEPRECATE_AS_ONNX_WARNING +from ads.model.serde.common import SERDE class LightGBMModel(FrameworkSpecificModel): @@ -40,8 +31,6 @@ class LightGBMModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained lightgbm estimator/model using Lightgbm. framework: str @@ -64,6 +53,7 @@ class LightGBMModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -131,6 +121,7 @@ class LightGBMModel(FrameworkSpecificModel): """ _PREFIX = "lightgbm" + model_save_serializer_type = LightGBMModelSerializerType def __init__( self, @@ -138,6 +129,8 @@ def __init__( artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = None, + model_input_serializer: Optional[SERDE] = None, **kwargs, ): """ @@ -156,6 +149,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -197,11 +194,19 @@ def __init__( or model_type.startswith(" List: - """Auto generate intial types. - - Parameters - ---------- - X_sample: (Any) - Train data. - - Returns - ------- - List - Initial types. - """ - if X_sample is not None and hasattr(X_sample, "shape"): - auto_generated_initial_types = [ - ("input", FloatTensorType([None, X_sample.shape[1]])) - ] - elif hasattr(self.estimator, "num_feature"): - n_cols = self.estimator.num_feature() - auto_generated_initial_types = [("input", FloatTensorType([None, n_cols]))] - elif hasattr(self.estimator, "n_features_in_"): - n_cols = self.estimator.n_features_in_ - auto_generated_initial_types = [("input", FloatTensorType([None, n_cols]))] - else: - raise ValueError( - "`initial_types` can not be detected. Please directly pass initial_types." - ) - return auto_generated_initial_types + if as_onnx: + logger.warning(DEPRECATE_AS_ONNX_WARNING) + self.set_model_save_serializer("lightgbm_onnx") + + super().serialize_model( + as_onnx=as_onnx, + initial_types=initial_types, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, + ) diff --git a/ads/model/framework/pytorch_model.py b/ads/model/framework/pytorch_model.py index 669f23ca5..cab8eb8a5 100644 --- a/ads/model/framework/pytorch_model.py +++ b/ads/model/framework/pytorch_model.py @@ -1,13 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import base64 -import os -from io import BytesIO from typing import Dict, List, Optional, Tuple, Union import numpy as np @@ -18,9 +15,14 @@ runtime_dependency, OptionalDependency, ) -from ads.common.data_serializer import InputDataSerializer from ads.model.generic_model import FrameworkSpecificModel from ads.model.model_properties import ModelProperties +from ads.model.serde.model_serializer import PyTorchModelSerializerType +from ads.model.common.utils import ( + DEPRECATE_AS_ONNX_WARNING, + DEPRECATE_USE_TORCH_SCRIPT_WARNING, +) +from ads.model.serde.common import SERDE ONNX_MODEL_FILE_NAME = "model.onnx" PYTORCH_MODEL_FILE_NAME = "model.pt" @@ -39,8 +41,6 @@ class PyTorchModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained pytorch estimator/model using Pytorch. framework: str @@ -63,6 +63,7 @@ class PyTorchModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -115,6 +116,7 @@ class PyTorchModel(FrameworkSpecificModel): """ _PREFIX = "pytorch" + model_save_serializer_type = PyTorchModelSerializerType @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) def __init__( @@ -123,6 +125,8 @@ def __init__( artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = model_save_serializer_type.TORCH, + model_input_serializer: Optional[SERDE] = None, **kwargs, ): """ @@ -140,6 +144,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -151,6 +159,8 @@ def __init__( artifact_dir=artifact_dir, properties=properties, auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, **kwargs, ) self._extractor = PytorchExtractor(estimator) @@ -160,43 +170,6 @@ def __init__( self.hyperparameter = self._extractor.hyperparameter self.version = torch.__version__ - def _handle_model_file_name(self, as_onnx: bool, model_file_name: str) -> str: - """ - Process file name for saving model. - For ONNX model file name must be ending with ".onnx". - For joblib model file name must be ending with ".joblib". - If not specified, use "model.onnx" for ONNX model and "model.joblib" for joblib model. - - Parameters - ---------- - as_onnx: bool - If set as True, convert into ONNX model. - model_file_name: str - File name for saving model. - - Returns - ------- - str - Processed file name. - """ - if not model_file_name: - return ONNX_MODEL_FILE_NAME if as_onnx else PYTORCH_MODEL_FILE_NAME - if as_onnx: - if model_file_name and not model_file_name.endswith(".onnx"): - raise ValueError( - "`model_file_name` has to be ending with `.onnx` for onnx format." - ) - else: - if model_file_name and not ( - model_file_name.endswith(".pt") or model_file_name.endswith(".pth") - ): - raise ValueError( - "`model_file_name` has to be ending with `.pt` or `.pth` " - "for Pytorch format." - ) - return model_file_name - - @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) def serialize_model( self, as_onnx: bool = False, @@ -247,15 +220,6 @@ def serialize_model( None Nothing. """ - model_path = os.path.join(self.artifact_dir, self.model_file_name) - - if os.path.exists(model_path) and not force_overwrite: - raise ValueError( - f"The {model_path} already exists, set force_overwrite to True if you wish to overwrite." - ) - - os.makedirs(self.artifact_dir, exist_ok=True) - if use_torch_script is None: logger.warning( "In future the models will be saved in TorchScript format by default. Currently saving it using torch.save method." @@ -270,173 +234,33 @@ def serialize_model( ) use_torch_script = False - if as_onnx: - onnx_args = kwargs.get("onnx_args", None) - - input_names = kwargs.get("input_names", ["input"]) - output_names = kwargs.get("output_names", ["output"]) - dynamic_axes = kwargs.get("dynamic_axes", None) - - self.to_onnx( - path=model_path, - onnx_args=onnx_args, - X_sample=X_sample, - input_names=input_names, - output_names=output_names, - dynamic_axes=dynamic_axes, - ) - - elif use_torch_script: - compiled_model = torch.jit.script(self.estimator) - torch.jit.save(compiled_model, model_path) - - else: - torch.save(self.estimator.state_dict(), model_path) + if as_onnx and use_torch_script: + raise ValueError("You can only save Pytorch model into one format.") - @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) - def to_onnx( - self, - path: str = None, - onnx_args=None, - X_sample: Optional[ - Union[ - Dict, - str, - List, - Tuple, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - ] - ] = None, - input_names: List[str] = ["input"], - output_names: List[str] = ["output"], - dynamic_axes=None, - ): - """ - Exports the given Pytorch model into ONNX format. - - Parameters - ---------- - path: str, default to None - Path to save the serialized model. - onnx_args: (tuple or torch.Tensor), default to None - Contains model inputs such that model(onnx_args) is a valid - invocation of the model. Can be structured either as: 1) ONLY A - TUPLE OF ARGUMENTS; 2) A TENSOR; 3) A TUPLE OF ARGUMENTS ENDING - WITH A DICTIONARY OF NAMED ARGUMENTS - X_sample: Union[list, tuple, pd.Series, np.ndarray, pd.DataFrame]. Defaults to None. - A sample of input data that will be used to generate input schema and detect onnx_args. - input_names: (List[str], optional). Defaults to ["input"]. - Names to assign to the input nodes of the graph, in order. - output_names: (List[str], optional). Defaults to ["output"]. - Names to assign to the output nodes of the graph, in order. - dynamic_axes: (dict, optional). Defaults to None. - Specify axes of tensors as dynamic (i.e. known only at run-time). - - Returns - ------- - None - Nothing - - Raises - ------ - AssertionError - if onnx module is not support by the current version of torch - ValueError - if X_sample is not provided - if path is not provided - """ - - assert hasattr(torch, "onnx"), ( - f"This version of pytorch {torch.__version__} does not appear to support onnx " - "conversion." - ) + if as_onnx: + logger.warning(DEPRECATE_AS_ONNX_WARNING) + self.set_model_save_serializer(self.model_save_serializer_type.ONNX) - if onnx_args is None: - if X_sample is not None: - logger.warning( - "Since `onnx_args` is not provided, `onnx_args` is " - "detected from `X_sample` to export pytorch model as onnx." - ) - onnx_args = X_sample - else: - raise ValueError( - "`onnx_args` can not be detected. The parameter `onnx_args` must be provided to export pytorch model as onnx." - ) + if use_torch_script: + logger.warning(DEPRECATE_USE_TORCH_SCRIPT_WARNING) + self.set_model_save_serializer(self.model_save_serializer_type.TORCHSCRIPT) - if not path: - raise ValueError( - "The parameter `path` must be provided to save the model file." - ) - - torch.onnx.export( - self.estimator, - args=onnx_args, - f=path, - input_names=input_names, - output_names=output_names, - dynamic_axes=dynamic_axes, + super().serialize_model( + as_onnx=as_onnx, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, ) - @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) - def get_data_serializer( - self, - data: Union[ - Dict, - str, - List, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - "torch.Tensor", - ], - data_type: str = None, - ): - """Returns serializable input data. - - Parameters - ---------- - data: Union[Dict, str, list, numpy.ndarray, pd.core.series.Series, - pd.core.frame.DataFrame, torch.Tensor] - Data expected by the model deployment predict API. - data_type: str - Type of the data. - - Returns - ------- - InputDataSerializer - A class containing serialized input data and original data type - information. - - Raises - ------ - TypeError - if provided data type is not supported. - """ - data_type = data_type if data_type else type(data) - if data_type == "image": - try: - import torchvision.transforms as transforms - - convert_tensor = transforms.ToTensor() - data = convert_tensor(data) - data_type = str(type(data)) - except ModuleNotFoundError: - raise ModuleNotFoundError( - f"The `torchvision` module was not found. Please run " - f"`pip install {OptionalDependency.PYTORCH}`." - ) - if isinstance(data, torch.Tensor): - buffer = BytesIO() - torch.save(data, buffer) - data = base64.b64encode(buffer.getvalue()).decode("utf-8") + def _to_tensor(self, data): try: - return InputDataSerializer(data, data_type=data_type) - except: - raise TypeError( - "The supported data types are Dict, str, list, bytes," - "numpy.ndarray, pd.core.series.Series, " - "pd.core.frame.DataFrame, torch.Tensor. Please " - "convert to the supported data types first. " + import torchvision.transforms as transforms + + convert_tensor = transforms.ToTensor() + data = convert_tensor(data) + except ModuleNotFoundError: + raise ModuleNotFoundError( + f"The `torchvision` module was not found. Please run " + f"`pip install {OptionalDependency.PYTORCH}`." ) + return data diff --git a/ads/model/framework/sklearn_model.py b/ads/model/framework/sklearn_model.py index a39d132f4..48daa88cf 100644 --- a/ads/model/framework/sklearn_model.py +++ b/ads/model/framework/sklearn_model.py @@ -1,29 +1,20 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import os -from typing import Any, Callable, Dict, List, Optional, Tuple, Union import numpy as np import pandas as pd from ads.common import logger -from ads.common.decorator.runtime_dependency import ( - runtime_dependency, - OptionalDependency, -) from ads.model.extractor.sklearn_extractor import SklearnExtractor -from ads.common.data_serializer import InputDataSerializer -from ads.model.generic_model import ( - FrameworkSpecificModel, - DEFAULT_ONNX_FORMAT_MODEL_FILE_NAME, - DEFAULT_JOBLIB_FORMAT_MODEL_FILE_NAME, -) +from ads.model.generic_model import FrameworkSpecificModel from ads.model.model_properties import ModelProperties -from joblib import dump -from pandas.api.types import is_numeric_dtype, is_string_dtype +from ads.model.serde.model_serializer import SklearnModelSerializerType +from ads.model.common.utils import DEPRECATE_AS_ONNX_WARNING +from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from ads.model.serde.common import SERDE class SklearnModel(FrameworkSpecificModel): @@ -39,8 +30,6 @@ class SklearnModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained sklearn estimator/model using scikit-learn. framework: str @@ -63,6 +52,7 @@ class SklearnModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -126,6 +116,7 @@ class SklearnModel(FrameworkSpecificModel): """ _PREFIX = "sklearn" + model_save_serializer_type = SklearnModelSerializerType def __init__( self, @@ -133,6 +124,8 @@ def __init__( artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = model_save_serializer_type.JOBLIB, + model_input_serializer: Optional[SERDE] = None, **kwargs, ): """ @@ -150,6 +143,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -189,6 +186,8 @@ def __init__( artifact_dir=artifact_dir, properties=properties, auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, **kwargs, ) self._extractor = SklearnExtractor(estimator) @@ -197,49 +196,6 @@ def __init__( self.version = self._extractor.version self.hyperparameter = self._extractor.hyperparameter - @staticmethod - def _handle_model_file_name(as_onnx: bool, model_file_name: str): - """ - Process file name for saving model. - For ONNX model file name must be ending with ".onnx". - For joblib model file name must be ending with ".joblib". - If not specified, use "model.onnx" for ONNX model and "model.joblib" for joblib model. - - Parameters - ---------- - as_onnx: bool - If set as True, convert into ONNX model. - model_file_name: str - File name for saving model. - - Returns - ------- - str - Processed file name. - - Raises - ------ - ValueError: If the input model_file_name does not corresponding to serialize format. - """ - - if not model_file_name: - return ( - DEFAULT_ONNX_FORMAT_MODEL_FILE_NAME - if as_onnx - else DEFAULT_JOBLIB_FORMAT_MODEL_FILE_NAME - ) - if as_onnx: - if model_file_name and not model_file_name.endswith(".onnx"): - raise ValueError( - "`model_file_name` has to be ending with `.onnx` for onnx format." - ) - else: - if model_file_name and not model_file_name.endswith(".joblib"): - raise ValueError( - "`model_file_name` has to be ending with `.joblib` for joblib format." - ) - return model_file_name - def serialize_model( self, as_onnx: Optional[bool] = False, @@ -278,271 +234,14 @@ def serialize_model( None Nothing. """ - model_path = os.path.join(self.artifact_dir, self.model_file_name) - if os.path.exists(model_path) and not force_overwrite: - raise ValueError( - "Model file already exists and will not be overwritten. " - "Set `force_overwrite` to True if you wish to overwrite." - ) - else: - if not os.path.exists(self.artifact_dir): - os.makedirs(self.artifact_dir) - if as_onnx: - onx = self.to_onnx( - initial_types=initial_types, X_sample=X_sample, **kwargs - ) - with open(model_path, "wb") as f: - f.write(onx.SerializeToString()) - else: - dump(self.estimator, model_path) - - @runtime_dependency(module="onnx", install_from=OptionalDependency.ONNX) - @runtime_dependency(module="xgboost", install_from=OptionalDependency.BOOSTED) - @runtime_dependency(module="lightgbm", install_from=OptionalDependency.BOOSTED) - @runtime_dependency(module="skl2onnx", install_from=OptionalDependency.ONNX) - @runtime_dependency(module="onnxmltools", install_from=OptionalDependency.ONNX) - @runtime_dependency( - module="onnxmltools.convert.xgboost.operator_converters.XGBoost", - object="convert_xgboost", - install_from=OptionalDependency.ONNX, - ) - @runtime_dependency( - module="onnxmltools.convert.lightgbm.operator_converters.LightGbm", - object="convert_lightgbm", - install_from=OptionalDependency.ONNX, - ) - def to_onnx( - self, - initial_types: List[Tuple] = None, - X_sample: Optional[ - Union[ - Dict, - str, - List, - Tuple, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - ] - ] = None, - **kwargs, - ): - """ - Produces an equivalent ONNX model of the given scikit-learn model. - - Parameters - ---------- - initial_types: (List[Tuple], optional). Defaults to None. - Each element is a tuple of a variable name and a type. - X_sample: Union[Dict, str, List, np.ndarray, pd.core.series.Series, pd.core.frame.DataFrame,]. Defaults to None. - Contains model inputs such that model(X_sample) is a valid invocation of the model. - Used to generate initial_types. - - Returns - ------- - onnx.onnx_ml_pb2.ModelProto - An ONNX model (type: ModelProto) which is equivalent to the input scikit-learn model. - """ - auto_generated_initial_types = None - if not initial_types: - if X_sample is None: - raise ValueError( - " At least one of `X_sample` or `initial_types` must be provided." - ) - auto_generated_initial_types = self.generate_initial_types(X_sample) - if str(type(self.estimator)).startswith(" List: - """Auto generate intial types. - - Parameters - ---------- - X_sample: (Any) - Train data. - - Returns - ------- - List - Initial types. - """ - if SklearnModel._is_all_numerical_array_dataframe(X_sample): - # if it's a dataframe and all the columns are numerical. Or - # it's not a dataframe, also try this. - if hasattr(X_sample, "shape") and len(X_sample.shape) >= 2: - auto_generated_initial_types = [ - ( - "input", - skl2onnx.common.data_types.FloatTensorType( - [None, X_sample.shape[1]] - ), - ) - ] - elif hasattr(self.estimator, "n_features_in_"): - n_cols = self.estimator.n_features_in_ - auto_generated_initial_types = [ - ( - "input", - skl2onnx.common.data_types.FloatTensorType([None, n_cols]), - ) - ] - else: - raise ValueError( - "`initial_types` can not be detected. Please directly pass initial_types." - ) - elif SklearnModel.is_either_numerical_or_string_dataframe(X_sample): - # for dataframe and not all the columns are numerical, then generate - # the input types of all the columns one by one. - auto_generated_initial_types = [] - - for i, col in X_sample.iteritems(): - if is_numeric_dtype(col.dtypes): - auto_generated_initial_types.append( - ( - col.name, - skl2onnx.common.data_types.FloatTensorType([None, 1]), - ) - ) - else: - auto_generated_initial_types.append( - ( - col.name, - skl2onnx.common.data_types.StringTensorType([None, 1]), - ) - ) - else: - try: - auto_generated_initial_types = ( - skl2onnx.common.data_types.guess_data_type( - np.array(X_sample) if isinstance(X_sample, list) else X_sample - ) - ) - except: - auto_generated_initial_types = None - return auto_generated_initial_types - - @staticmethod - def _is_all_numerical_array_dataframe( - data: Union[pd.DataFrame, np.ndarray] - ) -> bool: - """Check whether all the columns are numerical for numpy array and dataframe. - For data with any other data types, it will return False. - - Parameters - ---------- - data: Union[pd.DataFrame, np.ndarray] - - Returns - ------- - bool - Whether all the columns in a pandas dataframe or numpy array are all numerical. - """ - return ( - isinstance(data, pd.DataFrame) - and all([is_numeric_dtype(dtype) for dtype in data.dtypes]) - or (isinstance(data, np.ndarray) and is_numeric_dtype(data.dtype)) - ) - - @staticmethod - def is_either_numerical_or_string_dataframe(data: pd.DataFrame) -> bool: - """Check whether all the columns are either numerical or string for dataframe.""" - return isinstance(data, pd.DataFrame) and all( - [ - is_numeric_dtype(col.dtypes) or is_string_dtype(col.dtypes) - for _, col in data.iteritems() - ] + if as_onnx: + logger.warning(DEPRECATE_AS_ONNX_WARNING) + self.set_model_save_serializer(self.model_save_serializer_type.ONNX) + + super().serialize_model( + as_onnx=as_onnx, + initial_types=initial_types, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, ) diff --git a/ads/model/framework/spark_model.py b/ads/model/framework/spark_model.py index d75f8557d..70df6663d 100644 --- a/ads/model/framework/spark_model.py +++ b/ads/model/framework/spark_model.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import os @@ -15,119 +15,18 @@ OptionalDependency, ) from ads.model.extractor.spark_extractor import SparkExtractor -from ads.common.data_serializer import InputDataSerializer +from ads.model.serde.model_input import SparkModelInputSerializerType +from ads.model.serde.model_serializer import SparkModelSerializerType from ads.model.generic_model import ( FrameworkSpecificModel, DEFAULT_MODEL_FOLDER_NAME, ) from ads.model.model_properties import ModelProperties +from ads.model.serde.common import SERDE SPARK_DATAFRAME_SCHEMA_PATH = "_input_data_schema.json" -@runtime_dependency( - module="pyspark", - short_name="sql", - object="sql", - install_from=OptionalDependency.SPARK, -) -def _serialize_via_spark(data): - """ - If data is either a spark SQLDataFrames and spark.pandas dataframe/series - Return pandas version and data type of original - Else - Return data and None - """ - try: # runtime_dependency could not import this for unknown reason - import pyspark.pandas as ps - - ps_available = True - except: - ps_available = False - - def _get_or_create_spark_session(): - return sql.SparkSession.builder.appName("Convert pandas to spark").getOrCreate() - - if isinstance(data, sql.DataFrame): - data_type = type(data) - elif ps_available and ( - isinstance(data, ps.DataFrame) or isinstance(data, ps.Series) - ): - data_type = type(data) - data = data.to_spark() - elif isinstance(data, sql.types.Row): - spark_session = _get_or_create_spark_session() - data = spark_session.createDataFrame(data) - data_type = type(data) - elif isinstance(data, pd.core.frame.DataFrame): - data_type = type(data) - spark_session = _get_or_create_spark_session() - data = spark_session.createDataFrame(data) - elif isinstance(data, list): - if not len(data): - raise TypeError(f"Data cannot be empty. Provided data parameter is: {data}") - if isinstance(data[0], sql.types.Row): - spark_session = _get_or_create_spark_session() - data = spark_session.createDataFrame(data) - data_type = type(data) - else: - logger.warn( - f"ADS does not serialize data type: {type(data)} for Spark Models. User should proceed at their own risk. ADS supported data types are: `pyspark.sql.DataFrame`, `pandas.DataFrame`, and `pyspark.pandas.DataFrame`." - ) - return data, type(data), None - else: - logger.warn( - f"ADS does not serialize data type: {type(data)} for Spark Models. User should proceed at their own risk. ADS supported data types are: `pyspark.sql.DataFrame`, `pandas.DataFrame`, and `pyspark.pandas.DataFrame`." - ) - return data, type(data), None - return data, data_type, data.schema - - -class SparkDataSerializer(InputDataSerializer): - """[An internal class] - Defines the contract for input data to spark pipeline models - - """ - - @runtime_dependency( - module="pyspark", - short_name="sql", - object="sql", - install_from=OptionalDependency.SPARK, - ) - def __init__( - self, - data: Union[ - Dict, - str, - List, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - ], - data_type=None, - ): - """ - Parameters - ---------- - data: Union[Dict, str, list, numpy.ndarray, pd.core.series.Series, - pd.core.frame.DataFrame] - Data expected by the model deployment predict API. - data_type: Any, defaults to None. - Type of the data. If not provided, it will be checked against data. - - """ - data, data_type, _ = _serialize_via_spark(data) - if isinstance(data, sql.DataFrame): - data = data.toJSON().collect() - try: - super().__init__(data=data, data_type=data_type) - except: - raise TypeError( - f"Data type: {type(data)} unsupported. Please use `pyspark.sql.DataFrame`, `pyspark.pandas.DataFrame`, `pandas.DataFrame`." - ) - - class SparkPipelineModel(FrameworkSpecificModel): """SparkPipelineModel class for estimators from the pyspark framework. @@ -141,8 +40,6 @@ class SparkPipelineModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained pyspark estimator/model using pyspark. framework: str @@ -164,6 +61,7 @@ class SparkPipelineModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -227,6 +125,8 @@ class SparkPipelineModel(FrameworkSpecificModel): """ _PREFIX = "spark" + model_input_serializer_type = SparkModelInputSerializerType + model_save_serializer_type = SparkModelSerializerType @runtime_dependency( module="pyspark", @@ -240,6 +140,8 @@ def __init__( artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = model_save_serializer_type.SPARK, + model_input_serializer: Optional[SERDE] = model_input_serializer_type.SPARK, **kwargs, ): """ @@ -257,6 +159,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to `ads.model.serde.model_input.SparkModelInputSERDE`. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -298,9 +204,10 @@ def __init__( artifact_dir=artifact_dir, properties=properties, auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, **kwargs, ) - self.data_serializer_class = SparkDataSerializer self._extractor = SparkExtractor(estimator) self.framework = self._extractor.framework self.algorithm = self._extractor.algorithm @@ -366,19 +273,13 @@ def serialize_model( raise NotImplementedError( "The Spark to Onnx Conversion is not supported because it is unstable. Please set as_onnx to False (default) to perform a spark model serialization" ) - if not X_sample: - raise TypeError( - "X_Sample is required to serialize spark models. Please pass in an X_sample to `prepare`." - ) - model_path = os.path.join(self.artifact_dir, self.model_file_name) - if os.path.exists(model_path) and not force_overwrite: - raise ValueError( - "Model file already exists and will not be overwritten. " - "Set `force_overwrite` to True if you wish to overwrite." - ) - if not os.path.exists(self.artifact_dir): - os.makedirs(self.artifact_dir) - self.estimator.write().overwrite().save(model_path) + + super().serialize_model( + as_onnx=as_onnx, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, + ) @runtime_dependency( module="pyspark", @@ -397,7 +298,11 @@ def _prepare_data_for_schema( self.artifact_dir, self.model_file_name + SPARK_DATAFRAME_SCHEMA_PATH, ) - X_sample, data_type, schema = _serialize_via_spark(X_sample) + ( + X_sample, + data_type, + schema, + ) = self.get_data_serializer()._serialize_via_spark(X_sample) if not schema: raise TypeError( f"Data type: {data_type} unsupported. Please use `pyspark.sql.DataFrame`, `pyspark.pandas.DataFrame`, or `pandas.DataFrame`." diff --git a/ads/model/framework/tensorflow_model.py b/ads/model/framework/tensorflow_model.py index 67109caad..f68b02ad0 100644 --- a/ads/model/framework/tensorflow_model.py +++ b/ads/model/framework/tensorflow_model.py @@ -1,11 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import os from typing import Dict, List, Optional, Tuple, Union import numpy as np @@ -16,12 +15,11 @@ OptionalDependency, ) from ads.model.extractor.tensorflow_extractor import TensorflowExtractor -from ads.common.data_serializer import InputDataSerializer from ads.model.generic_model import FrameworkSpecificModel from ads.model.model_properties import ModelProperties - -ONNX_MODEL_FILE_NAME = "model.onnx" -TENSORFLOW_MODEL_FILE_NAME = "model.h5" +from ads.model.serde.model_serializer import TensorflowModelSerializerType +from ads.model.common.utils import DEPRECATE_AS_ONNX_WARNING +from ads.model.serde.common import SERDE class TensorFlowModel(FrameworkSpecificModel): @@ -37,8 +35,6 @@ class TensorFlowModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained tensorflow estimator/model using Tensorflow. framework: str @@ -61,6 +57,7 @@ class TensorFlowModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -132,6 +129,7 @@ class TensorFlowModel(FrameworkSpecificModel): """ _PREFIX = "tensorflow" + model_save_serializer_type = TensorflowModelSerializerType @runtime_dependency( module="tensorflow", @@ -144,6 +142,8 @@ def __init__( artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = model_save_serializer_type.TENSORFLOW, + model_input_serializer: Optional[SERDE] = None, **kwargs, ): """ @@ -161,6 +161,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -172,6 +176,8 @@ def __init__( artifact_dir=artifact_dir, properties=properties, auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, **kwargs, ) self._extractor = TensorflowExtractor(estimator) @@ -182,45 +188,6 @@ def __init__( self.version = tf.version.VERSION - def _handle_model_file_name(self, as_onnx: bool, model_file_name: str) -> str: - """ - Process file name for saving model. - For ONNX model file name must be ending with ".onnx". - For joblib model file name must be ending with ".joblib". - If not specified, use "model.onnx" for ONNX model and "model.joblib" for joblib model. - - Parameters - ---------- - as_onnx: bool - If set as True, convert into ONNX model. - model_file_name: str - File name for saving model. - - Returns - ------- - str - Processed file name. - """ - if not model_file_name: - return ONNX_MODEL_FILE_NAME if as_onnx else TENSORFLOW_MODEL_FILE_NAME - if as_onnx: - if model_file_name and not model_file_name.endswith(".onnx"): - raise ValueError( - "`model_file_name` has to be ending with `.onnx` for onnx format." - ) - else: - if model_file_name and not model_file_name.endswith(".h5"): - raise ValueError( - "`model_file_name` has to be ending with `.h5` " - "for Tensorflow model format." - ) - return model_file_name - - @runtime_dependency( - module="tensorflow", - short_name="tf", - install_from=OptionalDependency.TENSORFLOW, - ) def serialize_model( self, as_onnx: bool = False, @@ -261,195 +228,27 @@ def serialize_model( Nothing. """ - model_path = os.path.join(self.artifact_dir, self.model_file_name) - - if os.path.exists(model_path) and not force_overwrite: - raise ValueError( - f"The {model_path} already exists, set force_overwrite to True if you wish to overwrite." - ) - - os.makedirs(self.artifact_dir, exist_ok=True) - if as_onnx: logger.warning( "This approach supports converting tensorflow.keras models to " "onnx format. If the defined model includes other tensorflow " "modules (e.g., tensorflow.function), please use GenericModel instead." ) - opset_version = kwargs.get("opset_version", None) - input_signature = kwargs.get("input_signature", None) - - self.to_onnx( - path=model_path, - input_signature=input_signature, - X_sample=X_sample, - opset_version=opset_version, - ) - - else: - self.estimator.save(model_path) + logger.warning(DEPRECATE_AS_ONNX_WARNING) + self.set_model_save_serializer(self.model_save_serializer_type.ONNX) - @runtime_dependency(module="tf2onnx", install_from=OptionalDependency.ONNX) - @runtime_dependency( - module="tensorflow", - short_name="tf", - install_from=OptionalDependency.TENSORFLOW, - ) - def to_onnx( - self, - path: str = None, - input_signature=None, - X_sample: Optional[ - Union[ - Dict, - str, - List, - Tuple, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - ] - ] = None, - opset_version=None, - ): - """ - Exports the given Tensorflow model into ONNX format. - - Parameters - ---------- - path: str, default to None - Path to save the serialized model. - input_signature: a tuple or a list of tf.TensorSpec objects. default to None. - Define the shape/dtype of the input so that model(input_signature) is a valid invocation of the model. - X_sample: Union[list, tuple, pd.Series, np.ndarray, pd.DataFrame]. Defaults to None. - A sample of input data that will be used to generate input schema and detect input_signature. - opset_version: int. Defaults to None. - The opset to be used for the ONNX model. - - Returns - ------- - None - Nothing - - Raises - ------ - ValueError - if path is not provided - """ - - if not path: - raise ValueError( - "The parameter `path` must be provided to save the model file." - ) - if input_signature is None: - if hasattr(self.estimator, "input_shape"): - if not isinstance(self.estimator.input, list): - # single input - detected_input_signature = ( - tf.TensorSpec( - self.estimator.input_shape, - dtype=self.estimator.input.dtype, - name="input", - ), - ) - else: - # multiple input - detected_input_signature = [] - for i in range(len(self.estimator.input)): - detected_input_signature.append( - tf.TensorSpec( - self.estimator.input_shape[i], - dtype=self.estimator.input[i].dtype, - ) - ) - - elif X_sample is not None and hasattr(X_sample, "shape"): - logger.warning( - "Since `input_signature` is not provided, `input_signature` is " - "detected from `X_sample` to export tensorflow model as " - "onnx." - ) - X_sample_shape = list(X_sample.shape) - X_sample_shape[0] = None - detected_input_signature = ( - tf.TensorSpec(X_sample_shape, dtype=X_sample.dtype, name="input"), - ) - else: - raise ValueError( - "The parameter `input_signature` must be provided to export " - "tensorflow model as onnx." - ) - try: - tf2onnx.convert.from_keras( - self.estimator, - input_signature=detected_input_signature, - opset=opset_version, - output_path=path, - ) - except: - raise ValueError( - "`input_signature` can not be autodetected. The parameter `input_signature` must be provided to export " - "tensorflow model as onnx." - ) - - else: - tf2onnx.convert.from_keras( - self.estimator, - input_signature=input_signature, - opset=opset_version, - output_path=path, - ) + super().serialize_model( + as_onnx=as_onnx, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, + ) @runtime_dependency( module="tensorflow", short_name="tf", install_from=OptionalDependency.TENSORFLOW, ) - def get_data_serializer( - self, - data: Union[ - Dict, - str, - List, - np.ndarray, - pd.core.series.Series, - pd.core.frame.DataFrame, - "tf.Tensor", - ], - data_type: str = None, - ): - """Returns serializable input data. - - Parameters - ---------- - data: Union[Dict, str, list, numpy.ndarray, pd.core.series.Series, - pd.core.frame.DataFrame, tf.Tensor] - Data expected by the model deployment predict API. - data_type: str - Type of the data. - - Returns - ------- - InputDataSerializer - A class containing serialized input data and original data type information. - - Raises - ------ - TypeError - if provided data type is not supported. - """ - try: - data_type = data_type or type(data) - if data_type == "image": - data = tf.convert_to_tensor(data) - data_type = str(type(data)) - if isinstance(data, tf.Tensor): - data = data.numpy() - return InputDataSerializer(data, data_type=data_type) - except: - raise TypeError( - "The supported data types are Dict, str, list, " - "numpy.ndarray, pd.core.series.Series, " - "pd.core.frame.DataFrame, tf.Tensor, bytes. Please " - "convert to the supported data types first. " - ) + def _to_tensor(self, data): + data = tf.convert_to_tensor(data) + return data diff --git a/ads/model/framework/xgboost_model.py b/ads/model/framework/xgboost_model.py index 5d7a80717..10d95cab6 100644 --- a/ads/model/framework/xgboost_model.py +++ b/ads/model/framework/xgboost_model.py @@ -1,10 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -import os from typing import Any, Dict, List, Optional, Tuple, Union import numpy as np @@ -14,14 +13,12 @@ runtime_dependency, OptionalDependency, ) -from ads.common.data_serializer import InputDataSerializer from ads.model.extractor.xgboost_extractor import XgboostExtractor -from ads.model.generic_model import ( - FrameworkSpecificModel, - DEFAULT_ONNX_FORMAT_MODEL_FILE_NAME, - DEFAULT_JSON_FORMAT_MODEL_FILE_NAME, -) +from ads.model.generic_model import FrameworkSpecificModel from ads.model.model_properties import ModelProperties +from ads.model.serde.model_serializer import XgboostModelSerializerType +from ads.model.common.utils import DEPRECATE_AS_ONNX_WARNING +from ads.model.serde.common import SERDE class XGBoostModel(FrameworkSpecificModel): @@ -37,8 +34,6 @@ class XGBoostModel(FrameworkSpecificModel): Default authentication is set using the `ads.set_auth` API. To override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create an authentication signer to instantiate an IdentityClient object. - ds_client: DataScienceClient - The data science client used by model deployment. estimator: Callable A trained xgboost estimator/model using Xgboost. framework: str @@ -61,6 +56,7 @@ class XGBoostModel(FrameworkSpecificModel): The model ID. properties: ModelProperties ModelProperties object required to save and deploy model. + For more details, check https://accelerated-data-science.readthedocs.io/en/latest/ads.model.html#module-ads.model.model_properties. runtime_info: RuntimeInfo A RuntimeInfo instance. schema_input: Schema @@ -123,6 +119,7 @@ class XGBoostModel(FrameworkSpecificModel): """ _PREFIX = "xgboost" + model_save_serializer_type = XgboostModelSerializerType @runtime_dependency(module="xgboost", install_from=OptionalDependency.BOOSTED) def __init__( @@ -131,6 +128,8 @@ def __init__( artifact_dir: str, properties: Optional[ModelProperties] = None, auth: Dict = None, + model_save_serializer: Optional[SERDE] = model_save_serializer_type.XGBOOST, + model_input_serializer: Optional[SERDE] = None, **kwargs, ): """ @@ -149,6 +148,10 @@ def __init__( The default authetication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize data. Returns ------- @@ -191,6 +194,8 @@ def __init__( artifact_dir=artifact_dir, properties=properties, auth=auth, + model_save_serializer=model_save_serializer, + model_input_serializer=model_input_serializer, **kwargs, ) self._extractor = XgboostExtractor(estimator) @@ -199,48 +204,6 @@ def __init__( self.version = self._extractor.version self.hyperparameter = self._extractor.hyperparameter - @staticmethod - def _handle_model_file_name(as_onnx: bool, model_file_name: str): - """ - Process file name for saving model. - For ONNX model file name must be ending with ".onnx". - For JSON model file name must be ending with ".json". - If not specified, use "model.onnx" for ONNX model and "model.json" for JSON model. - - Parameters - ---------- - as_onnx: bool - If set as True, convert into ONNX model. - model_file_name: str - File name for saving model. - - Returns - ------- - str - Processed file name. - - Raises - ------ - ValueError: If the input model_file_name does not corresponding to serialize format. - """ - if not model_file_name: - return ( - DEFAULT_ONNX_FORMAT_MODEL_FILE_NAME - if as_onnx - else DEFAULT_JSON_FORMAT_MODEL_FILE_NAME - ) - if as_onnx: - if model_file_name and not model_file_name.endswith(".onnx"): - raise ValueError( - "`model_file_name` has to be ending with `.onnx` for onnx format." - ) - else: - if model_file_name and not model_file_name.endswith(".json"): - raise ValueError( - "`model_file_name` has to be ending with `.json` for JSON format." - ) - return model_file_name - @runtime_dependency(module="xgboost", install_from=OptionalDependency.BOOSTED) def serialize_model( self, @@ -282,185 +245,14 @@ def serialize_model( None Nothing. """ - self.model_file_name = self._handle_model_file_name( - as_onnx, self.model_file_name + if as_onnx: + logger.warning(DEPRECATE_AS_ONNX_WARNING) + self.set_model_save_serializer(self.model_save_serializer_type.ONNX) + + super().serialize_model( + as_onnx=as_onnx, + initial_types=initial_types, + force_overwrite=force_overwrite, + X_sample=X_sample, + **kwargs, ) - model_path = os.path.join(self.artifact_dir, self.model_file_name) - if os.path.exists(model_path) and not force_overwrite: - raise ValueError( - "Model file already exists and will not be overwritten. " - "Set `force_overwrite` to True if you wish to overwrite." - ) - else: - if not os.path.exists(self.artifact_dir): - os.makedirs(self.artifact_dir) - if as_onnx: - onx = self.to_onnx( - initial_types=initial_types, X_sample=X_sample, **kwargs - ) - with open(model_path, "wb") as f: - f.write(onx.SerializeToString()) - else: - self.estimator.save_model(model_path) - - @runtime_dependency(module="onnx", install_from=OptionalDependency.ONNX) - @runtime_dependency(module="xgboost", install_from=OptionalDependency.BOOSTED) - @runtime_dependency( - module="skl2onnx", - object="convert_sklearn", - install_from=OptionalDependency.ONNX, - ) - @runtime_dependency( - module="skl2onnx", - object="update_registered_converter", - install_from=OptionalDependency.ONNX, - ) - @runtime_dependency( - module="skl2onnx.common.data_types", - object="FloatTensorType", - install_from=OptionalDependency.ONNX, - ) - @runtime_dependency( - module="skl2onnx.common.shape_calculator", - object="calculate_linear_classifier_output_shapes", - install_from=OptionalDependency.ONNX, - ) - @runtime_dependency( - module="skl2onnx.common.shape_calculator", - object="calculate_linear_regressor_output_shapes", - install_from=OptionalDependency.ONNX, - ) - @runtime_dependency(module="onnxmltools", install_from=OptionalDependency.ONNX) - @runtime_dependency( - module="onnxmltools.convert.xgboost.operator_converters.XGBoost", - object="convert_xgboost", - install_from=OptionalDependency.ONNX, - ) - def to_onnx( - self, - initial_types: List[Tuple] = None, - X_sample: Union[list, tuple, pd.DataFrame, pd.Series, np.ndarray] = None, - **kwargs, - ): - """ - Produces an equivalent ONNX model of the given Xgboost model. - - Parameters - ---------- - initial_types: (List[Tuple], optional). Defaults to None. - Each element is a tuple of a variable name and a type. - X_sample: Union[Dict, str, List, np.ndarray, pd.core.series.Series, pd.core.frame.DataFrame,]. Defaults to None. - Contains model inputs such that model(X_sample) is a valid invocation of the model. - Used to generate initial_types. - - Returns - ------- - onnx.onnx_ml_pb2.ModelProto - An ONNX model (type: ModelProto) which is equivalent to the input xgboost model. - """ - auto_generated_initial_types = None - if not initial_types: - auto_generated_initial_types = self.generate_initial_types(X_sample) - - model_types = [] - if str(type(self.estimator)).startswith(" List: - """Auto generate intial types. - - Parameters - ---------- - X_sample: (Any) - Train data. - - Returns - ------- - List - Initial types. - """ - if hasattr(self.estimator, "n_features_in_"): - # sklearn api - n_cols = self.estimator.n_features_in_ - return [("input", FloatTensorType([None, n_cols]))] - elif hasattr(self.estimator, "feature_names") and self.estimator.feature_names: - # xgboost learning api - n_cols = len(self.estimator.feature_names) - return [("input", FloatTensorType([None, n_cols]))] - if X_sample is None: - raise ValueError( - " At least one of `X_sample` or `initial_types` must be provided." - ) - if ( - X_sample is not None - and hasattr(X_sample, "shape") - and len(X_sample.shape) >= 2 - ): - auto_generated_initial_types = [ - ("input", FloatTensorType([None, X_sample.shape[1]])) - ] - else: - raise ValueError( - "`initial_types` can not be detected. Please directly pass initial_types." - ) - return auto_generated_initial_types diff --git a/ads/model/generic_model.py b/ads/model/generic_model.py index e7296825a..5f163b7d1 100644 --- a/ads/model/generic_model.py +++ b/ads/model/generic_model.py @@ -9,20 +9,33 @@ import shutil import tempfile import yaml -from PIL import Image -from enum import Enum -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -import cloudpickle import numpy as np import pandas as pd import requests import yaml +from PIL import Image +from enum import Enum +from typing import Any, Callable, Dict, List, Optional, Tuple, Union from ads.common import utils from ads.common import auth as authutil from ads.common import logger -from ads.common.data_serializer import InputDataSerializer +from ads.model.serde.model_input import ( + ModelInputSerializer, + ModelInputDeserializer, + ModelInputSerializerType, + ModelInputSerializerFactory, + SUPPORTED_MODEL_INPUT_SERIALIZERS, +) +from ads.model.serde.model_serializer import ( + ModelSerializerType, + ModelSerializer, + ModelSerializerFactory, + ModelDeserializer, + CloudPickleModelSerializer, + SUPPORTED_MODEL_SERIALIZERS, +) from ads.common.decorator.utils import class_or_instance_method +from ads.evaluations import EvaluatorMixin from ads.model.transformer.onnx_transformer import ONNXTransformer from ads.model.model_introspect import ( TEST_STATUS, @@ -71,6 +84,7 @@ from ads.model.datascience_model import DataScienceModel from ads.model.common.utils import zip_artifact +from ads.model.serde.common import SERDE _TRAINING_RESOURCE_ID = JOB_RUN_OCID or NB_SESSION_OCID _COMPARTMENT_OCID = NB_SESSION_COMPARTMENT_OCID or JOB_RUN_COMPARTMENT_OCID @@ -79,10 +93,7 @@ MODEL_DEPLOYMENT_INSTANCE_COUNT = 1 MODEL_DEPLOYMENT_BANDWIDTH_MBPS = 10 -DEFAULT_ONNX_FORMAT_MODEL_FILE_NAME = "model.onnx" -DEFAULT_JSON_FORMAT_MODEL_FILE_NAME = "model.json" -DEFAULT_JOBLIB_FORMAT_MODEL_FILE_NAME = "model.joblib" -DEFAULT_TXT_FORMAT_MODEL_FILE_NAME = "model.txt" + DEFAULT_MODEL_FOLDER_NAME = "model" ONNX_DATA_TRANSFORMER = "onnx_data_transformer.json" @@ -99,6 +110,13 @@ Framework.SPARK, ] +VERIFY_STATUS_NAME = "verify()" +PREPARE_STATUS_NAME = "prepare()" +INITIATE_STATUS_NAME = "initiate" +SAVE_STATUS_NAME = "save()" +DEPLOY_STATUS_NAME = "deploy()" +PREDICT_STATUS_NAME = "predict()" + class DataScienceModelType(str, metaclass=ExtendedEnumMeta): MODEL_DEPLOYMENT = "datasciencemodeldeployment" @@ -151,7 +169,7 @@ def _prepare_artifact_dir(artifact_dir: str = None) -> str: return artifact_dir -class GenericModel(MetadataMixin, Introspectable): +class GenericModel(MetadataMixin, Introspectable, EvaluatorMixin): """Generic Model class which is the base class for all the frameworks including the unsupported frameworks. @@ -185,6 +203,8 @@ class GenericModel(MetadataMixin, Introspectable): Name of the serialized model. model_id: str The model ID. + model_input_serializer: SERDE + Instance of ads.model.SERDE. Used for serialize/deserialize data. properties: ModelProperties ModelProperties object required to save and deploy model. runtime_info: RuntimeInfo @@ -229,6 +249,8 @@ class GenericModel(MetadataMixin, Introspectable): Restarts the model deployment. save(..., **kwargs) Saves model artifacts to the model catalog. + set_model_input_serializer(serde) + Registers serializer used for serializing data passed in verify/predict. summary_status(...) Gets a summary table of the current status. verify(data, ...) @@ -250,8 +272,8 @@ class GenericModel(MetadataMixin, Introspectable): >>> model = GenericModel(estimator=estimator, artifact_dir=tempfile.mkdtemp()) >>> model.summary_status() >>> model.prepare( - ... inference_conda_env="dataexpl_p37_cpu_v3", - ... inference_python_version="3.7", + ... inference_conda_env="dbexp_p38_cpu_v1", + ... inference_python_version="3.8", ... model_file_name="toy_model.pkl", ... training_id=None, ... force_overwrite=True @@ -274,6 +296,8 @@ class GenericModel(MetadataMixin, Introspectable): _summary_status = None _PREFIX = "generic" + model_input_serializer_type = ModelInputSerializerType + model_save_serializer_type = ModelSerializerType def __init__( self, @@ -282,6 +306,8 @@ def __init__( properties: Optional[ModelProperties] = None, auth: Optional[Dict] = None, serialize: bool = True, + model_save_serializer: Optional[SERDE] = None, + model_input_serializer: Optional[SERDE] = None, **kwargs: dict, ): """GenericModel Constructor. @@ -301,6 +327,10 @@ def __init__( serialize: (bool, optional). Defaults to True. Whether to serialize the model to pkl file by default. If False, you need to serialize the model manually, save it under artifact_dir and update the score.py manually. + model_save_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE or str, optional). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model input. """ self.estimator = estimator self.auth = auth or authutil.default_signer() @@ -313,7 +343,6 @@ def __init__( .with_output_schema(Schema()) ) - self.data_serializer_class = InputDataSerializer self.model_file_name = None self.artifact_dir = _prepare_artifact_dir(artifact_dir) self.model_artifact = None @@ -325,6 +354,7 @@ def __init__( self.model_deployment = None self.runtime_info = None self._as_onnx = kwargs.pop("as_onnx", False) + self._score_args = {} if properties: self.properties = ( @@ -337,6 +367,39 @@ def __init__( self._serialize = serialize self._summary_status = SummaryStatus() + self._init_serde( + model_input_serde=model_input_serializer, + model_save_serializer=model_save_serializer, + ) + + def _init_serde( + self, + model_input_serde: Union[SERDE, str] = None, + model_save_serializer: Union[SERDE, str] = None, + ): + """Initializes serde. + + Parameters + ---------- + model_save_serializer: (SERDE or str). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model. + model_input_serializer: (SERDE or str). Defaults to None. + Instance of ads.model.SERDE. Used for serialize/deserialize model input. + """ + if model_input_serde is None: + logger.warning( + "In the future model input will be serialized by `cloudpickle` by " + "default. Currently, model input are serialized into a dictionary " + "containing serialized input data and original data type information." + 'Set `model_input_serializer="cloudpickle"` to use cloudpickle model input serializer.' + ) + self.set_model_input_serializer( + model_input_serializer=model_input_serde + or self.model_input_serializer_type.JSON + ) + self.set_model_save_serializer( + model_save_serializer or self.model_save_serializer_type.CLOUDPICKLE + ) @property def metadata_custom(self): @@ -406,6 +469,139 @@ def _to_yaml(self): """Converts the model attributes to yaml format.""" return yaml.safe_dump(self._to_dict()) + def set_model_input_serializer( + self, + model_input_serializer: Union[str, SERDE], + ): + """Registers serializer used for serializing data passed in verify/predict. + + Examples + -------- + >>> generic_model.set_model_input_serializer(GenericModel.model_input_serializer_type.CLOUDPICKLE) + + >>> # Register serializer by passing the name of it. + >>> generic_model.set_model_input_serializer("cloudpickle") + + >>> # Example of creating customized model input serializer and registing it. + >>> from ads.model import SERDE + >>> from ads.model.generic_model import GenericModel + + >>> class MySERDE(SERDE): + ... def __init__(self): + ... super().__init__() + ... def serialize(self, data): + ... serialized_data = 1 + ... return serialized_data + ... def deserialize(self, data): + ... deserialized_data = 2 + ... return deserialized_data + + >>> class Toy: + ... def predict(self, x): + ... return x ** 2 + + >>> generic_model = GenericModel( + ... estimator=Toy(), + ... artifact_dir=tempfile.mkdtemp(), + ... model_input_serializer=MySERDE() + ... ) + + >>> # Or register the serializer after creating model instance. + >>> generic_model.set_model_input_serializer(MySERDE()) + + Parameters + ---------- + model_input_serializer: (str, or ads.model.SERDE) + name of the serializer, or instance of SERDE. + """ + if isinstance(model_input_serializer, str): + self.model_input_serializer = ModelInputSerializerFactory.get( + model_input_serializer + ) + else: + self.model_input_serializer = model_input_serializer + + try: + serializer_name = self.model_input_serializer.name + if serializer_name not in SUPPORTED_MODEL_INPUT_SERIALIZERS: + logger.warn( + "Replace the code of `deserialize()` in `score.py` with " + "the your own implementation of `deserialize()`." + ) + except AttributeError: + self.model_input_serializer.name = "customized" + logger.warn( + "Model input will be serialized by `serialize()` " + "defined in your provided `model_input_serializer`. " + "Replace the code of `deserialize()` in `score.py` with " + "the your own implementation of `deserialize()`." + ) + + def set_model_save_serializer(self, model_save_serializer: Union[str, SERDE]): + """Registers serializer used for saving model. + + Examples + -------- + >>> generic_model.set_model_save_serializer(GenericModel.model_save_serializer_type.CLOUDPICKLE) + + >>> # Register serializer by passing the name of it. + >>> generic_model.set_model_save_serializer("cloudpickle") + + >>> # Example of creating customized model save serializer and registing it. + >>> from ads.model import SERDE + >>> from ads.model.generic_model import GenericModel + + >>> class MySERDE(SERDE): + ... def __init__(self): + ... super().__init__() + ... def serialize(self, data): + ... serialized_data = 1 + ... return serialized_data + ... def deserialize(self, data): + ... deserialized_data = 2 + ... return deserialized_data + + >>> class Toy: + ... def predict(self, x): + ... return x ** 2 + + >>> generic_model = GenericModel( + ... estimator=Toy(), + ... artifact_dir=tempfile.mkdtemp(), + ... model_save_serializer=MySERDE() + ... ) + + >>> # Or register the serializer after creating model instance. + >>> generic_model.set_model_save_serializer(MySERDE()) + + Parameters + ---------- + model_save_serializer: (ads.model.SERDE or str) + name of the serializer or instance of SERDE. + """ + if isinstance(model_save_serializer, str): + self.model_save_serializer = ModelSerializerFactory.get( + model_save_serializer + ) + else: + self.model_save_serializer = model_save_serializer + + try: + serializer_name = self.model_save_serializer.name + if serializer_name not in SUPPORTED_MODEL_SERIALIZERS: + logger.warn( + "Replace the code of `load_model()` in `score.py` with " + "the your own implementation of `deserialize()`." + ) + except AttributeError: + self.model_save_serializer.name = "customized" + logger.warn( + "Model will be saved by `serialize()` " + "defined in your provided `model_save_serializer`. " + "Replace the code of `load_model()` in `score.py` with " + "the your own implementation of `deserialize()`." + ) + def serialize_model( self, as_onnx: bool = False, @@ -435,27 +631,81 @@ def serialize_model( Nothing """ if self._serialize: - with open( - os.path.join(self.artifact_dir, self.model_file_name), "wb" - ) as outfile: - cloudpickle.dump(self.estimator, outfile) + if not self.model_file_name: + self.model_file_name = self._handle_model_file_name(as_onnx=as_onnx) + self._serialize_model_helper( + initial_types, force_overwrite, X_sample, **kwargs + ) else: raise SerializeModelNotImplementedError( "`serialize_model` is not implemented." ) - def _handle_model_file_name(self, as_onnx: bool, model_file_name: str): + def _serialize_model_helper( + self, + initial_types: List[Tuple] = None, + force_overwrite: bool = False, + X_sample: any = None, + **kwargs, + ): + model_path = self._check_model_file( + self.model_file_name, force_overwrite=force_overwrite + ) + self.get_model_serializer().serialize( + estimator=self.estimator, + model_path=model_path, + X_sample=X_sample, + initial_types=initial_types, + **kwargs, + ) + + def _check_model_file(self, model_file_name, force_overwrite): + model_path = os.path.join(self.artifact_dir, model_file_name) + + if os.path.exists(model_path) and not force_overwrite: + raise ValueError( + f"The {model_path} already exists, set force_overwrite to True if you wish to overwrite." + ) + + os.makedirs(self.artifact_dir, exist_ok=True) + return model_path + + def _handle_model_file_name(self, as_onnx: bool, model_file_name: str = None): + if as_onnx: + self._set_model_save_serializer_to_onnx() + if not model_file_name: if not self._serialize: raise NotImplementedError("`model_file_name` has to be provided.") else: - model_file_name = "model.pkl" + model_file_name = f"model.{self._get_model_file_suffix()}" + if as_onnx: assert model_file_name.endswith( ".onnx" - ), "Wrong file extension. Expecting .onnx suffix." + ), "Wrong file extension. Expecting `.onnx` suffix." + return model_file_name + def _get_model_file_suffix(self): + try: + suffix = self.model_save_serializer.model_file_suffix + return suffix + except AttributeError as e: + logger.error( + "Please specify `model_file_suffix` in `model_save_serializer`. " + ) + raise e + + def _set_model_save_serializer_to_onnx(self): + try: + self.set_model_save_serializer(self.model_save_serializer_type.ONNX) + except AttributeError as e: + logger.error( + f"This framework {self._PREFIX} to Onnx Conversion is not supported. Please set `as_onnx=False` (default) to perform other model serialization." + ) + raise e + def _onnx_data_transformer( self, X: Union[pd.DataFrame, pd.Series], @@ -614,6 +864,9 @@ def prepare( raise ValueError("`inference_conda_env` must be specified.") self._as_onnx = as_onnx + if as_onnx: + self._set_model_save_serializer_to_onnx() + self.model_file_name = self._handle_model_file_name( as_onnx=as_onnx, model_file_name=model_file_name ) @@ -646,6 +899,7 @@ def prepare( force_overwrite=force_overwrite, namespace=namespace, bucketname=DEFAULT_CONDA_BUCKET_NAME, + auth=self.auth, ) self._summary_status.update_status( @@ -706,16 +960,20 @@ def prepare( else: if self.framework and self.framework != "other": jinja_template_filename = "score_" + self.framework + if self.framework == "transformers": + jinja_template_filename = "score_" + "huggingface_pipeline" else: jinja_template_filename = ( "score-pkl" if self._serialize else "score_generic" ) - self.model_artifact.prepare_score_py( jinja_template_filename=jinja_template_filename, model_file_name=self.model_file_name, - **kwargs, + data_deserializer=self.model_input_serializer.name, + model_serializer=self.model_save_serializer.name, + **{**kwargs, **self._score_args}, ) + self._summary_status.update_status( detail="Generated score.py", status=ModelState.DONE.value ) @@ -729,6 +987,7 @@ def prepare( ignore_pending_changes=ignore_pending_changes, max_col_num=max_col_num, ) + self._summary_status.update_status( detail="Populated metadata(Custom, Taxonomy and Provenance)", status=ModelState.DONE.value, @@ -746,6 +1005,111 @@ def prepare( ) return self + def _handle_input_data( + self, data: Any = None, auto_serialize_data: bool = True, **kwargs + ): + """Handle input data and serialize it as required. + + Parameters + ---------- + data: Any + Data for the prediction. + auto_serialize_data: bool + Defaults to True. Indicate whether to serialize the input data. + + kwargs: + storage_options: dict + Passed to ADSImage.open. + + Raises + ------ + TypeError: + `data` is not json serializable or bytes. Set `auto_serialize_data` to `True` to serialize the input data. + ValueError: + Either use `image` argument through kwargs to pass in image file or use `data` argument to pass the data. + + Returns + ------- + object: Data used for a request. + """ + if isinstance(data, bytes): + return data + if not auto_serialize_data: + if not _is_json_serializable(data) and not isinstance(data, bytes): + raise TypeError( + "`data` is not json serializable or bytes. Set `auto_serialize_data` to `True` to serialize the input data." + ) + return data + + if data is None and "image" not in kwargs.keys(): + raise ValueError( + "Either use `image` argument through kwargs to pass in image file or use `data` argument to pass the data." + ) + + if "image" in kwargs.keys(): + data = self._handle_image_input(image=kwargs.pop("image"), **kwargs) + + serialized_data = self.model_input_serializer.serialize(data=data, **kwargs) + return serialized_data + + def _handle_image_input(self, image, **kwargs): + """Validates the image input and converts it to tensor. + + Parameters + ---------- + image: PIL.Image Object or uri. + image file path or opened image file. + + kwargs: + storage_options: dict + Passed to ADSImage.open. + + Raises + ------ + ValueError: Cannot open or identify the given image file. + + Returns + ------- + tensor: tf.tensor or torch.tensor. + """ + if not isinstance(image, Image.Image): + try: + image = ADSImage.open( + path=image, storage_options=kwargs.pop("storage_options", {}) + ).img + except Exception as e: + raise ValueError( + f"Cannot open or identify the given image file. See details: {e}" + ) + tensor = self._to_tensor(image) + return tensor + + def _to_tensor(self, data): + """Only PyTorchModel and TensorflowModel will implement this method. + + Args: + data (Any): Data needs to be converted to tensor. + + Raises: + NotImplementedError: Only PyTorchModel and TensorflowModel will implement this method. + """ + raise NotImplementedError( + "Only PyTorchModel and TensorflowModel will implement this method." + ) + + def get_data_serializer(self): + """Gets data serializer. + + Returns + ------- + object: ads.model.Serializer object. + """ + return self.model_input_serializer + + def get_model_serializer(self): + """Gets model serializer.""" + return self.model_save_serializer + def verify( self, data: Any = None, @@ -772,6 +1136,8 @@ def verify( Data used to test if deployment works in local environment. reload_artifacts: bool. Defaults to True. Whether to reload artifacts or not. + is_json_payload: bool + Defaults to False. Indicate whether to send data with a `application/json` MIME TYPE. auto_serialize_data: bool. Whether to auto serialize input data. Defauls to `False` for GenericModel, and `True` for other frameworks. `data` required to be json serializable if `auto_serialize_data=False`. @@ -789,25 +1155,16 @@ def verify( Dict A dictionary which contains prediction results. """ - data, data_type = self._handle_image_input(data, **kwargs) endpoint = f"http://127.0.0.1:8000/predict" + data = self._handle_input_data(data, auto_serialize_data, **kwargs) - if not auto_serialize_data: - if not _is_json_serializable(data): - raise ValueError( - "`data` must be json serializable. " - "Set `auto_serialize_data` to True, or serialize the provided input data first." - ) - request_body = send_request( - data=data, - endpoint=endpoint, - dry_run=True, - is_json_payload=True, - **kwargs, - ) - else: - serialized_data = self.get_data_serializer(data=data, data_type=data_type) - request_body = serialized_data.send(endpoint, dry_run=True, **kwargs) + request_body = send_request( + data, + endpoint, + dry_run=True, + is_json_payload=_is_json_serializable(data), + **kwargs, + ) if reload_artifacts: self.model_artifact.reload() @@ -987,6 +1344,7 @@ def from_model_catalog( remove_existing_artifact=remove_existing_artifact, auth=auth, region=kwargs.pop("region", None), + timeout=kwargs.pop("timeout", None), ) result_model = cls.from_model_artifact( uri=artifact_dir, @@ -1345,6 +1703,8 @@ def save( region: (str, optional). Defaults to `None`. The destination Object Storage bucket region. By default the value will be extracted from the `OCI_REGION_METADATA` environment variables. + timeout: (int, optional). Defaults to 10 seconds. + The connection timeout in seconds for the client. Also can be any attribute that `oci.data_science.models.Model` accepts. @@ -1851,7 +2211,10 @@ def prepare_save_deploy( return self.model_deployment def predict( - self, data: Any = None, auto_serialize_data: bool = False, **kwargs + self, + data: Any = None, + auto_serialize_data: bool = False, + **kwargs, ) -> Dict[str, Any]: """Returns prediction of input data run against the model deployment endpoint. @@ -1895,8 +2258,6 @@ def predict( ValueError If `data` is empty or not JSON serializable. """ - data, data_type = self._handle_image_input(data, **kwargs) - if not self.model_deployment: raise ValueError("Use `deploy()` method to start model deployment.") @@ -1904,78 +2265,18 @@ def predict( if current_state != ModelDeploymentState.ACTIVE.name: raise NotActiveDeploymentError(current_state) - if not auto_serialize_data: - prediction = self.model_deployment.predict(json_input=data) - else: - serialized_data = self.get_data_serializer( - data=data, data_type=data_type - ).to_dict() - prediction = self.model_deployment.predict( - json_input=serialized_data, **kwargs - ) + data = self._handle_input_data(data, auto_serialize_data, **kwargs) + prediction = self.model_deployment.predict( + data=data, + serializer=self.get_data_serializer(), + **kwargs, + ) self._summary_status.update_status( detail="Called deployment predict endpoint", status=ModelState.DONE.value ) return prediction - def get_data_serializer(self, data: any, data_type: str = None): - """The data_serializer_class class is set in ``init`` and used here. - Frameworks should subclass the InputDataSerializer class, then - set that as the ``self.data_serializer_class``. - Frameworks should avoid overwriting this method whenever possible. - - Parameters - ---------- - data: (Any) - data to be passed to model for prediction. - data_type: str - Type of the data. - - Returns - ------- - data - Serialized data. - """ - return self.data_serializer_class(data=data, data_type=data_type) - - def _handle_image_input(self, data, **kwargs) -> bytes: - """Validate the argument pass in verify and predict. - - Properties - ---------- - kwargs: - content_type: str, used to indicate the media type of the resource. - image: PIL.Image Object or uri. - image file path or opened image file. - storage_options: dict - Passed to ADSImage.open. - - Raises - ------ - ValueError: Either use `image` argument through kwargs to pass in image file or use `data` argument to pass the data. - - Returns - ------- - bytes: Binary data. - """ - if data is not None: - return data, None - ARGUMENT_ERROR_MSG = "Either use `image` argument through kwargs to pass in image file or use `data` argument to pass the data." - if "image" in kwargs.keys(): - image = kwargs.get("image") - if not isinstance(image, Image.Image): - try: - image = ADSImage.open( - path=image, storage_options=kwargs.pop("storage_options", {}) - ).img - except Exception as e: - raise ValueError( - f"Cannot open or identify the given image file. See details: {e}" - ) - return image, "image" - raise ValueError(ARGUMENT_ERROR_MSG) - def summary_status(self) -> pd.DataFrame: """A summary table of the current status. @@ -2281,32 +2582,47 @@ class SummaryStatus: def __init__(self): summary_data = [ - ["initiate", "Initiated the model", ModelState.DONE.value, ""], - ["prepare()", "Generated runtime.yaml", ModelState.AVAILABLE.value, ""], - ["prepare()", "Generated score.py", ModelState.AVAILABLE.value, ""], - ["prepare()", "Serialized model", ModelState.AVAILABLE.value, ""], + [INITIATE_STATUS_NAME, "Initiated the model", ModelState.DONE.value, ""], [ - "prepare()", + PREPARE_STATUS_NAME, + "Generated runtime.yaml", + ModelState.AVAILABLE.value, + "", + ], + [PREPARE_STATUS_NAME, "Generated score.py", ModelState.AVAILABLE.value, ""], + [PREPARE_STATUS_NAME, "Serialized model", ModelState.AVAILABLE.value, ""], + [ + PREPARE_STATUS_NAME, "Populated metadata(Custom, Taxonomy and Provenance)", ModelState.AVAILABLE.value, "", ], [ - "verify()", + VERIFY_STATUS_NAME, "Local tested .predict from score.py", ModelState.NOTAVAILABLE.value, "", ], - ["save()", "Conducted Introspect Test", ModelState.NOTAVAILABLE.value, ""], [ - "save()", + SAVE_STATUS_NAME, + "Conducted Introspect Test", + ModelState.NOTAVAILABLE.value, + "", + ], + [ + SAVE_STATUS_NAME, "Uploaded artifact to model catalog", ModelState.NOTAVAILABLE.value, "", ], - ["deploy()", "Deployed the model", ModelState.NOTAVAILABLE.value, ""], [ - "predict()", + DEPLOY_STATUS_NAME, + "Deployed the model", + ModelState.NOTAVAILABLE.value, + "", + ], + [ + PREDICT_STATUS_NAME, "Called deployment predict endpoint", ModelState.NOTAVAILABLE.value, "", diff --git a/ads/model/model_artifact_boilerplate/artifact_introspection_test/model_artifact_validate.py b/ads/model/model_artifact_boilerplate/artifact_introspection_test/model_artifact_validate.py index 35a659b5b..070bd6911 100644 --- a/ads/model/model_artifact_boilerplate/artifact_introspection_test/model_artifact_validate.py +++ b/ads/model/model_artifact_boilerplate/artifact_introspection_test/model_artifact_validate.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import argparse @@ -19,6 +19,7 @@ import oci import requests import yaml +from ads.model.runtime.utils import SERVICE_PACKS logger = logging.getLogger() logging.basicConfig(level=logging.DEBUG) @@ -174,7 +175,7 @@ def check_runtime_yml(file_path) -> Tuple[bool, str]: if VARIABLES["runtime_env_type"] == "data_science": response = requests.request("GET", PAR_URL) if response.ok: - service_pack_list = response.json().get("service_packs") + service_pack_list = response.json().get(SERVICE_PACKS) service_pack = None for service_pack_item in service_pack_list: pack_path = urlparse(service_pack_item["pack_path"]) diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index 31329bea3..2d074a671 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -1615,11 +1615,13 @@ def fetch_training_code_details( else: repository_url = "file://" + repo.working_dir # no remote repo - # get git branch - git_branch = format(repo.active_branch) # get git commit + git_branch = None git_commit = None + try: + # get git branch + git_branch = format(repo.active_branch) git_commit = format(str(repo.head.commit.hexsha)) or None except Exception: logger.warning("No commit found.") diff --git a/ads/model/model_metadata_mixin.py b/ads/model/model_metadata_mixin.py index b0a6cfd8d..f3803cf10 100644 --- a/ads/model/model_metadata_mixin.py +++ b/ads/model/model_metadata_mixin.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -303,12 +303,13 @@ def populate_metadata( and self.metadata_taxonomy[MetadataTaxonomyKeys.ALGORITHM].value is None ): logger.info( - "To auto-extract taxonomy metadata the model must be provided. Supported models: automl, keras, lightgbm, pytorch, sklearn, tensorflow, pyspark, and xgboost." + "To auto-extract taxonomy metadata the model must be provided. Supported models: keras, lightgbm, pytorch, sklearn, tensorflow, pyspark, and xgboost." ) if use_case_type is None: use_case_type = self.metadata_taxonomy[ MetadataTaxonomyKeys.USE_CASE_TYPE ].value + self._populate_metadata_taxonomy( model=self.estimator, use_case_type=use_case_type ) diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index c6133ce88..19d13b82e 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import contextlib @@ -639,10 +639,6 @@ def from_dict(cls, config: dict) -> "ModelVersionSet": """ return cls(spec=batch_convert_case(config.get("spec"), "snake")) - def __repr__(self) -> str: - """Displays the object as YAML.""" - return self.to_yaml() - def __getattr__(self, item): if f"with_{item}" in self.__dir__(): return self.get_spec(item) diff --git a/ads/model/runtime/env_info.py b/ads/model/runtime/env_info.py index 9df25e977..22fb80209 100644 --- a/ads/model/runtime/env_info.py +++ b/ads/model/runtime/env_info.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import logging @@ -66,6 +66,7 @@ def from_slug( env_slug: str, namespace: str = CONDA_BUCKET_NS, bucketname: str = CONDA_BUCKET_NAME, + auth: dict = None, ) -> "EnvInfo": """Initiate an EnvInfo object from a slug. Only service pack is allowed to use this method. @@ -77,31 +78,37 @@ def from_slug( namespace of region. bucketname: (str, optional) bucketname of service pack. + auth: (Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. Returns ------- EnvInfo An EnvInfo instance. """ - if not namespace: - raise ValueError( - "Cannot detect `namespace` information automatically " - "as the environment variable `CONDA_BUCKET_NS` is not found. " - "`namespace` must be provided." - ) + if "oci://" not in env_slug: + warnings.warn("slug will be deprecated. Provide conda pack path instead.") + if not bucketname: warnings.warn( f"`bucketname` is not provided, defaults to `{DEFAULT_CONDA_BUCKET_NAME}`." ) bucketname = DEFAULT_CONDA_BUCKET_NAME - _, service_pack_slug_mapping = get_service_packs(namespace, bucketname) - if env_slug in service_pack_slug_mapping: - env_type = PACK_TYPE.SERVICE_PACK.value - env_path, python_version = service_pack_slug_mapping[env_slug] - else: - raise ValueError( - "The {env_slug} is not a service pack. Use `from_path` method by passing in the object storage path." - ) + _, service_pack_slug_mapping = get_service_packs( + namespace, bucketname, auth=auth + ) + env_type, env_path, python_version = None, None, None + if service_pack_slug_mapping: + if env_slug in service_pack_slug_mapping: + env_type = PACK_TYPE.SERVICE_PACK.value + env_path, python_version = service_pack_slug_mapping[env_slug] + else: + warnings.warn( + "The {env_slug} is not a service pack. Use `from_path` method by passing in the object storage path." + ) + return cls._populate_env_info( env_slug=env_slug, env_type=env_type, @@ -110,13 +117,17 @@ def from_slug( ) @classmethod - def from_path(cls, env_path: str) -> "EnvInfo": + def from_path(cls, env_path: str, auth: dict = None) -> "EnvInfo": """Initiate an object from a conda pack path. Parameters ---------- env_path: str conda pack path. + auth: (Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. Returns ------- @@ -128,7 +139,9 @@ def from_path(cls, env_path: str) -> "EnvInfo": python_version = "" env_slug = "" service_pack_path_mapping = {} - service_pack_path_mapping, _ = get_service_packs(namespace, bucketname) + service_pack_path_mapping, _ = get_service_packs( + namespace, bucketname, auth=auth + ) if env_path.startswith("oci://") and service_pack_path_mapping: if env_path in service_pack_path_mapping: env_type = PACK_TYPE.SERVICE_PACK.value diff --git a/ads/model/runtime/utils.py b/ads/model/runtime/utils.py index 4cfd113ab..4224b660e 100644 --- a/ads/model/runtime/utils.py +++ b/ads/model/runtime/utils.py @@ -5,17 +5,19 @@ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import json -import logging import os from functools import lru_cache from typing import Dict, Tuple +import logging +import fsspec import requests import yaml -from ads.common.utils import PAR_LINK -from ads.common.object_storage_details import ObjectStorageDetails from cerberus import DocumentError, Validator +from ads.common.object_storage_details import ObjectStorageDetails +from ads.common.utils import PAR_LINK + MODEL_PROVENANCE_SCHEMA_PATH = os.path.join( os.path.dirname(os.path.abspath(__file__)), "schemas", @@ -31,6 +33,7 @@ "schemas", "training_env_info_schema.yaml", ) +SERVICE_PACKS = "service_packs" class SchemaValidator: @@ -96,8 +99,44 @@ def _load_schema_validator(self): return schema_validator -@lru_cache(maxsize=1) -def get_service_packs(namespace: str, bucketname: str) -> Tuple[Dict, Dict]: +def _get_index_json_through_bucket( + namespace: str, bucketname: str, auth: dict = None +) -> list: + """get the index json from the object storage. + + Parameters + ---------- + namespace: str + The Object Storage namespace. + bucketname: str + The Object Storage bucketname. + auth: (Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + + Returns + ------- + list: A list of dictionary which contains service packs information. + """ + auth = auth or {} + service_pack_list = [] + try: + uri = f"oci://{bucketname}@{namespace}/service_pack/index.json" + with fsspec.open(uri, "r", **auth) as f: + service_packs = json.loads(f.read()) + service_pack_list = service_packs.get(SERVICE_PACKS) + except Exception as e: + logging.warn(e) + logging.warn( + "Failed to retrieve the full conda pack path from slug. Pass conda pack path 'oci://@/' instead of slug." + ) + return service_pack_list + + +def get_service_packs( + namespace: str, bucketname: str, auth: dict = None +) -> Tuple[Dict, Dict]: """Get the service pack path mapping and service pack slug mapping. Note: deprecated packs are also included. @@ -107,6 +146,10 @@ def get_service_packs(namespace: str, bucketname: str) -> Tuple[Dict, Dict]: namespace of the service pack. bucketname: str bucketname of the service pack. + auth: (Dict, optional). Defaults to None. + The default authetication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. Returns ------- @@ -118,34 +161,41 @@ def get_service_packs(namespace: str, bucketname: str) -> Tuple[Dict, Dict]: service_pack_slug_mapping = {} try: response = requests.request("GET", PAR_LINK) + + # if there is internet if response.ok: - service_pack_list = response.json().get("service_packs") - for service_pack in service_pack_list: - # Here we need to replace the namespace and bucketname - # with the bucket and namespace of the region that - # user is in. The reason is that the mapping is generated - # from the index.json file which has static namespace - # and bucket of prod. however, namespace will change based - # on the region. also, dev has different bucketname. - pack_path = ObjectStorageDetails( - bucket=bucketname, - namespace=namespace, - filepath=ObjectStorageDetails.from_path( - service_pack.get("pack_path") - ).filepath, - ).path - service_pack_path_mapping[pack_path] = ( - service_pack.get("slug"), - service_pack.get("python"), - ) - service_pack_slug_mapping[service_pack["slug"]] = ( - pack_path, - service_pack.get("python"), - ) + service_pack_list = response.json().get(SERVICE_PACKS) + # response not good. + else: + service_pack_list = _get_index_json_through_bucket( + namespace=namespace, bucketname=bucketname, auth=auth + ) except Exception as e: - logging.warning(e) - logging.warning( - "Failed to auto generate the `service_pack_path_mapping`. " - "Hence, the slug, environment type and python version field are not populated." + # not internet + service_pack_list = _get_index_json_through_bucket( + namespace=namespace, bucketname=bucketname, auth=auth + ) + + for service_pack in service_pack_list: + # Here we need to replace the namespace and bucketname + # with the bucket and namespace of the region that + # user is in. The reason is that the mapping is generated + # from the index.json file which has static namespace + # and bucket of prod. however, namespace will change based + # on the region. also, dev has different bucketname. + pack_path = ObjectStorageDetails( + bucket=bucketname, + namespace=namespace, + filepath=ObjectStorageDetails.from_path( + service_pack.get("pack_path") + ).filepath, + ).path + service_pack_path_mapping[pack_path] = ( + service_pack.get("slug"), + service_pack.get("python"), + ) + service_pack_slug_mapping[service_pack.get("slug")] = ( + pack_path, + service_pack.get("python"), ) return service_pack_path_mapping, service_pack_slug_mapping diff --git a/ads/model/serde/__init__.py b/ads/model/serde/__init__.py new file mode 100644 index 000000000..b8d0460f5 --- /dev/null +++ b/ads/model/serde/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*-- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ diff --git a/ads/model/serde/common.py b/ads/model/serde/common.py new file mode 100644 index 000000000..96a680575 --- /dev/null +++ b/ads/model/serde/common.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*-- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import abc + + +class Serializer: + """Abstract base class for creation of new serializers.""" + + def serialize(self, **kwargs): + """Serialize data/model into specific type. + + Returns + ------- + object: Serialized data/model. + """ + raise NotImplementedError("`serialize()` method needs to be implemented.") + + +class Deserializer: + """Abstract base class for creation of new deserializers.""" + + def deserialize(self, **kwargs): + """Deserialize data/model into original type. + + Returns + ------- + object: deserialized data/model. + """ + raise NotImplementedError("`deserialize()` method needs to be implemented.") + + +class SERDE(Serializer, Deserializer): + """A layer contains two groups which can interact with each other to serialize and + deserialize supported data structure using supported data format. + """ + + name = "" diff --git a/ads/model/serde/model_input.py b/ads/model/serde/model_input.py new file mode 100644 index 000000000..e6856affb --- /dev/null +++ b/ads/model/serde/model_input.py @@ -0,0 +1,547 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import cloudpickle +import numpy as np +import pandas as pd +import base64 +import os +import json +from io import BytesIO +from ads.common.decorator.runtime_dependency import ( + runtime_dependency, + OptionalDependency, +) +from ads.common import logger +from ads.model.serde.common import Serializer, Deserializer +from functools import lru_cache +from pickle import UnpicklingError +from typing import Dict, List, Union + +SUPPORTED_MODEL_INPUT_SERIALIZERS = ["json", "cloudpickle", "spark"] + + +class ModelInputSerializerType: + JSON = "json" + CLOUDPICKLE = "cloudpickle" + + +class SparkModelInputSerializerType: + SPARK = "spark" + + +class ModelInputSerializer(Serializer): + """Abstract base class for creation of new data serializers.""" + + def __init__(self): + super().__init__() + + def serialize(self, data): + return data + + +class JsonModelInputSerializer(ModelInputSerializer): + """ + ADS data serializer. Serialize data of various formats to into a + dictionary containing serialized input data and original data type + information. + + + Examples + -------- + >>> from ads.model.serde.model_input import JsonModelInputSerializer + + >>> # numpy array will be converted to base64 encoded string, + >>> # while `data_type` will record its original type: `numpy.ndarray` + >>> import numpy as np + >>> input_data = np.array([1, 2, 3]) + >>> serialized_data = JsonModelInputSerializer().serialize(data=input_data) + >>> serialized_data + { + 'data': 'k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZS + wgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI + CAgICAgICAgICAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=', + 'data_type': "" + } + + >>> # `pd.core.frame.DataFrame` will be converted to json by `.to_json()` + >>> # while `data_type` will record its original type: `pandas.core.frame.DataFrame` + >>> import pandas as pd + >>> df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]}) + >>> serialized_data = JsonModelInputSerializer().serialize(data=df) + >>> serialized_data + { + 'data': '{"col1":{"0":1,"1":2},"col2":{"0":3,"1":4}}', + 'data_type': "" + } + + >>> # `pandas.core.series.Series` will be converted to list by `.tolist()` + >>> # while `data_type` will record its original type: `pandas.core.series.Series` + >>> ser = pd.Series(data={'a': 1, 'b': 2, 'c': 3}, index=['a', 'b', 'c']) + >>> serialized_data = JsonModelInputSerializer().serialize(data=ser) + >>> serialized_data + { + 'data': [1, 2, 3], + 'data_type': "" + } + + >>> # `torch.Tensor` will be converted to base64 encoded string, + >>> # while `data_type` will record its original type: `torch.Tensor` + >>> import torch + >>> tt = torch.tensor([[1, 2, 3], [4, 5, 6]]) + >>> serialized_data = JsonModelInputSerializer().serialize(data=tt) + >>> serialized_data + { + 'data': 'UEsDBAAACAgAAAAAAAAAAAAAAAAAAAAAAAAQABIAYXJjaGl2ZS9kYXRhLnBrbEZCDgBaWlpaW + lpaWlpaWlpaWoACY3RvcmNoLl91dGlscwpfcmVidWlsZF90ZW5zb3JfdjIKcQAoKFgHAAAAc3RvcmFnZXEB + Y3RvcmNoCkxvbmdTdG9yYWdlCnECWAEAAAAwcQNYAwAAAGNwdXEESwZ0cQVRSwBLAksDhnEGSwNLAYZxB4l + jY29sbGVjdGlvbnMKT3JkZXJlZERpY3QKcQgpUnEJdHEKUnELLlBLBwim2iAhmQAAAJkAAABQSwMEAAAICA + AAAAAAAAAAAAAAAAAAAAAAAA4AKwBhcmNoaXZlL2RhdGEvMEZCJwBaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWl + paWlpaWlpaWlpaWlpaWloBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAUAAAAAAAAABgAAAAAAA + ABQSwcI9z/uVjAAAAAwAAAAUEsDBAAACAgAAAAAAAAAAAAAAAAAAAAAAAAPABMAYXJjaGl2ZS92ZXJzaW9u + RkIPAFpaWlpaWlpaWlpaWlpaWjMKUEsHCNGeZ1UCAAAAAgAAAFBLAQIAAAAACAgAAAAAAACm2iAhmQAAAJk + AAAAQAAAAAAAAAAAAAAAAAAAAAABhcmNoaXZlL2RhdGEucGtsUEsBAgAAAAAICAAAAAAAAPc/7lYwAAAAMA + AAAA4AAAAAAAAAAAAAAAAA6QAAAGFyY2hpdmUvZGF0YS8wUEsBAgAAAAAICAAAAAAAANGeZ1UCAAAAAgAAA + A8AAAAAAAAAAAAAAAAAgAEAAGFyY2hpdmUvdmVyc2lvblBLBgYsAAAAAAAAAB4DLQAAAAAAAAAAAAMAAAAA + AAAAAwAAAAAAAAC3AAAAAAAAANIBAAAAAAAAUEsGBwAAAACJAgAAAAAAAAEAAABQSwUGAAAAAAMAAwC3AAA + A0gEAAAAA', + 'data_type': "" + } + + >>> # `tensorflow.Tensor` will be converted to base64 encoded string, + >>> # while `data_type` will record its original type: `tensorflow.python.framework.ops.EagerTensor`. + >>> import torch + >>> c = tf.constant([[1.0, 2.0], [3.0, 4.0]]) + >>> serialized_data = JsonModelInputSerializer().serialize(data=c) + >>> serialized_data + { + 'data': 'k05VTVBZAQB2AHsnZGVzY3InOiAnPGY0JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXB + lJzogKDIsIDIpLCB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA + gICAgICAgIAoAAIA/AAAAQAAAQEAAAIBA', + 'data_type': "" + } + + >>> # dict, str, list, tuple will be saved as orignal type + >>> # and `data_type` will record its type. + >>> mystring = "this is a string." + >>> serialized_data = JsonModelInputSerializer().serialize(data=mystring) + >>> serialized_data + { + 'data': 'this is a string.', + 'data_type': "" + } + """ + + def __init__(self): + super().__init__() + + @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) + def _convert_torch_tensor(self, data): + buffer = BytesIO() + torch.save(data, buffer) + data = base64.b64encode(buffer.getvalue()).decode("utf-8") + return data + + @runtime_dependency( + module="tensorflow", + short_name="tf", + install_from=OptionalDependency.TENSORFLOW, + ) + def _convert_tf_tensor(self, data): + data = data.numpy() + return data + + def serialize( + self, + data: Union[ + Dict, + str, + List, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ], + ): + """Serialize data into a dictionary containing serialized input + data and original data type information. + + Parameters + ---------- + data: Union[Dict, str, list, numpy.ndarray, pd.core.series.Series, + pd.core.frame.DataFrame, bytes] + Data expected by the model deployment predict API. + + Returns + ------- + Dict + A dictionary containing serialized input data and original data type + information. + + Raises + ------ + TypeError + if provided data type is not supported. + """ + data_type = str(type(data)) + if "torch.Tensor" in data_type: + data = self._convert_torch_tensor(data) + if "tensorflow.python.framework.ops.EagerTensor" in data_type: + data = self._convert_tf_tensor(data) + + if isinstance(data, np.ndarray): + np_bytes = BytesIO() + np.save(np_bytes, data, allow_pickle=True) + data = base64.b64encode(np_bytes.getvalue()).decode("utf-8") + elif isinstance(data, pd.core.series.Series): + data = data.tolist() + elif isinstance(data, pd.core.frame.DataFrame): + data = data.to_json() + elif ( + isinstance(data, dict) + or isinstance(data, str) + or isinstance(data, list) + or isinstance(data, tuple) + or isinstance(data, bytes) + ): + pass + else: + raise TypeError( + "The supported data types are Dict, str, list, bytes, tuple, " + "numpy.ndarray, pd.core.series.Series, tf.Tensor, torch.Tensor, " + "pd.core.frame.DataFrame. Please convert to the supported data " + "types first. " + ) + + data_dict = { + "data": data, + "data_type": data_type, + } + return data_dict + + +class CloudpickleModelInputSerializer(ModelInputSerializer): + """Serialize data of various formats to bytes.""" + + def __init__(self): + super().__init__() + + def serialize(self, data): + """Serialize data into bytes. + + Parameters + ---------- + data (object): Data to be serialized. + + Returns + ------- + object: Serialized data used for a request. + """ + serialized_data = cloudpickle.dumps(data) + return serialized_data + + +class SparkModelInputSerializer(JsonModelInputSerializer): + """[An internal class] + Defines the contract for input data to spark pipeline models. + + """ + + def __init__(self): + super().__init__() + + @runtime_dependency( + module="pyspark", + short_name="sql", + object="sql", + install_from=OptionalDependency.SPARK, + ) + def serialize( + self, + data: Union[ + Dict, + str, + List, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ], + ): + """ + Parameters + ---------- + data: Union[Dict, str, list, numpy.ndarray, pd.core.series.Series, + pd.core.frame.DataFrame] + Data expected by the model deployment predict API. + + """ + data, _, _ = self._serialize_via_spark(data) + if isinstance(data, sql.DataFrame): + data = data.toJSON().collect() + + try: + data = super().serialize(data=data) + except: + raise TypeError( + f"Data type: {type(data)} unsupported. Please use `pyspark.sql.DataFrame`, `pyspark.pandas.DataFrame`, `pandas.DataFrame`." + ) + return data + + @runtime_dependency( + module="pyspark", + short_name="sql", + object="sql", + install_from=OptionalDependency.SPARK, + ) + def _serialize_via_spark(self, data): + """ + If data is either a spark SQLDataFrames and spark.pandas dataframe/series + Return pandas version and data type of original + Else + Return data and None + """ + try: # runtime_dependency could not import this for unknown reason + import pyspark.pandas as ps + + ps_available = True + except: + ps_available = False + + def _get_or_create_spark_session(): + return sql.SparkSession.builder.appName( + "Convert pandas to spark" + ).getOrCreate() + + if isinstance(data, sql.DataFrame): + data_type = type(data) + elif ps_available and ( + isinstance(data, ps.DataFrame) or isinstance(data, ps.Series) + ): + data_type = type(data) + data = data.to_spark() + elif isinstance(data, sql.types.Row): + spark_session = _get_or_create_spark_session() + data = spark_session.createDataFrame(data) + data_type = type(data) + elif isinstance(data, pd.core.frame.DataFrame): + data_type = type(data) + spark_session = _get_or_create_spark_session() + data = spark_session.createDataFrame(data) + elif isinstance(data, list): + if not len(data): + raise TypeError( + f"Data cannot be empty. Provided data parameter is: {data}" + ) + if isinstance(data[0], sql.types.Row): + spark_session = _get_or_create_spark_session() + data = spark_session.createDataFrame(data) + data_type = type(data) + else: + logger.warn( + f"ADS does not serialize data type: {type(data)} for Spark Models. User should proceed at their own risk. ADS supported data types are: `pyspark.sql.DataFrame`, `pandas.DataFrame`, and `pyspark.pandas.DataFrame`." + ) + return data, type(data), None + else: + logger.warn( + f"ADS does not serialize data type: {type(data)} for Spark Models. User should proceed at their own risk. ADS supported data types are: `pyspark.sql.DataFrame`, `pandas.DataFrame`, and `pyspark.pandas.DataFrame`." + ) + return data, type(data), None + return data, data_type, data.schema + + +class ModelInputDeserializer(Deserializer): + """Abstract base class for creation of new data deserializers.""" + + def __init__(self, name="customized"): + super().__init__() + self.name = name + + def deserialize(self, data): + return data + + +class JsonModelInputDeserializer(ModelInputDeserializer): + """ADS data deserializer. Deserialize data to into its original type.""" + + def __init__(self, name="json"): + super().__init__(name=name) + + @lru_cache(maxsize=1) + def _fetch_data_type_from_schema( + input_schema_path=os.path.join( + os.path.dirname(os.path.realpath(__file__)), "input_schema.json" + ) + ): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema["schema"]: + data_type[col["name"]] = col["dtype"] + else: + print( + "input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.pytorch_model.PyTorchModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization." + ) + return data_type + + def deserialize(self, data: dict): + """Deserialize data into its original type. + + Parameters + ---------- + data: (dict) + + Returns + ------- + objects: Deserialized data used for a prediction. + + Raises + ------ + TypeError + if provided data type is not supported. + """ + if isinstance(data, bytes): + logger.warning( + "bytes are passed directly to the model. If the model expects a specific data format, you need to write the conversion logic in `deserialize()` yourself." + ) + return data + + data_type = data.get("data_type", "") if isinstance(data, dict) else "" + json_data = data.get("data", data) if isinstance(data, dict) else data + if "torch.Tensor" in data_type: + return self._load_torch_tensor(json_data) + if "tensorflow.python.framework.ops.EagerTensor" in data_type: + return self._load_tf_tensor(json_data) + if "numpy.ndarray" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode("utf-8"))) + return np.load(load_bytes, allow_pickle=True) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=self._fetch_data_type_from_schema()) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + + return json_data + + @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) + def _load_torch_tensor(self, data): + load_bytes = BytesIO(base64.b64decode(data.encode("utf-8"))) + return torch.load(load_bytes) + + @runtime_dependency( + module="tensorflow", + short_name="tf", + install_from=OptionalDependency.TENSORFLOW, + ) + def _load_tf_tensor(self, data): + load_bytes = BytesIO(base64.b64decode(data.encode("utf-8"))) + return tf.convert_to_tensor(np.load(load_bytes, allow_pickle=True)) + + +class SparkModelInputDeserializer(ModelInputDeserializer): + def __init__(self, name="spark"): + super().__init__(name=name) + + def deserialize(data): + """ + Not implement. See spark template. + """ + pass + + +class CloudpickleModelInputDeserializer(ModelInputDeserializer): + """Use cloudpickle to deserialize data into its original type.""" + + def __init__(self, name="cloudpickle"): + super().__init__(name=name) + + def deserialize(self, data): + """Deserialize data into its original type. + + Parameters + ---------- + data (object): Data to be deserialized. + + Returns + ------- + object: deserialized data used for a prediction. + """ + deserialized_data = data + try: + deserialized_data = cloudpickle.loads(data) + except TypeError: + pass + except UnpicklingError: + logger.warning( + "bytes are passed directly to the model. If the model expects a specific data format, you need to write the conversion logic in `deserialize()` yourself." + ) + return deserialized_data + + +class JsonModelInputSERDE(JsonModelInputSerializer, JsonModelInputDeserializer): + name = "json" + + +class CloudpickleModelInputSERDE( + CloudpickleModelInputSerializer, CloudpickleModelInputDeserializer +): + name = "cloudpickle" + + +class SparkModelInputSERDE(SparkModelInputSerializer, SparkModelInputDeserializer): + name = "spark" + + +class ModelInputSerializerFactory: + """Data Serializer Factory. + + Examples + -------- + >>> serializer, deserializer = ModelInputSerializerFactory.get("cloudpickle") + """ + + _factory = {} + _factory["json"] = JsonModelInputSERDE + _factory["cloudpickle"] = CloudpickleModelInputSERDE + _factory["spark"] = SparkModelInputSERDE + + @classmethod + def get(cls, se: str = "json"): + """Gets data serializer and corresponding deserializer. + + Parameters + ---------- + se (str): + The name of the required serializer. + + Raises + ------ + ValueError: + Raises when input is unsupported format. + + Returns + ------- + serde (ads.model.serde.common.SERDE): + Intance of `ads.model.serde.common.SERDE". + """ + serde_cls = cls._factory.get(se, None) + if serde_cls: + return serde_cls() + else: + raise ValueError( + f"This {se} format is not supported." + f"Currently support the following format: {SUPPORTED_MODEL_INPUT_SERIALIZERS}." + ) diff --git a/ads/model/serde/model_serializer.py b/ads/model/serde/model_serializer.py new file mode 100644 index 000000000..96845dc09 --- /dev/null +++ b/ads/model/serde/model_serializer.py @@ -0,0 +1,1184 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import cloudpickle +import numpy as np +import pandas as pd +from ads.model.serde.common import Serializer, Deserializer +from ads.common.decorator.runtime_dependency import ( + runtime_dependency, + OptionalDependency, +) +from ads.common import logger +from pandas.api.types import is_numeric_dtype, is_string_dtype +from typing import Any, Dict, List, Optional, Tuple, Union +from joblib import dump + + +MODEL_SERIALIZATION_TYPE_ONNX = "onnx" +MODEL_SERIALIZATION_TYPE_CLOUDPICKLE = "cloudpickle" +MODEL_SERIALIZATION_TYPE_TORHCSCRIPT = "torchscript" +MODEL_SERIALIZATION_TYPE_TORCH = "torch" +MODEL_SERIALIZATION_TYPE_TORCH_ONNX = "torch_onnx" +MODEL_SERIALIZATION_TYPE_TF = "tf" +MODEL_SERIALIZATION_TYPE_TF_ONNX = "tf_onnx" +MODEL_SERIALIZATION_TYPE_JOBLIB = "joblib" +MODEL_SERIALIZATION_TYPE_SKLEARN_ONNX = "sklearn_onnx" +MODEL_SERIALIZATION_TYPE_LIGHTGBM = "lightgbm" +MODEL_SERIALIZATION_TYPE_LIGHTGBM_ONNX = "lightgbm_onnx" +MODEL_SERIALIZATION_TYPE_XGBOOST = "xgboost" +MODEL_SERIALIZATION_TYPE_XGBOOST_UBJ = "xgboost_ubj" +MODEL_SERIALIZATION_TYPE_XGBOOST_TXT = "xgboost_txt" +MODEL_SERIALIZATION_TYPE_XGBOOST_ONNX = "xgboost_onnx" +MODEL_SERIALIZATION_TYPE_SPARK = "spark" +MODEL_SERIALIZATION_TYPE_HUGGINGFACE = "huggingface" + + +SUPPORTED_MODEL_SERIALIZERS = [ + MODEL_SERIALIZATION_TYPE_ONNX, + MODEL_SERIALIZATION_TYPE_CLOUDPICKLE, + MODEL_SERIALIZATION_TYPE_TORHCSCRIPT, + MODEL_SERIALIZATION_TYPE_TORCH, + MODEL_SERIALIZATION_TYPE_TORCH_ONNX, + MODEL_SERIALIZATION_TYPE_TF, + MODEL_SERIALIZATION_TYPE_TF_ONNX, + MODEL_SERIALIZATION_TYPE_JOBLIB, + MODEL_SERIALIZATION_TYPE_SKLEARN_ONNX, + MODEL_SERIALIZATION_TYPE_LIGHTGBM, + MODEL_SERIALIZATION_TYPE_LIGHTGBM_ONNX, + MODEL_SERIALIZATION_TYPE_XGBOOST, + MODEL_SERIALIZATION_TYPE_XGBOOST_ONNX, + MODEL_SERIALIZATION_TYPE_SPARK, + MODEL_SERIALIZATION_TYPE_HUGGINGFACE, +] + + +class ModelSerializerType: + CLOUDPICKLE = MODEL_SERIALIZATION_TYPE_CLOUDPICKLE + ONNX = MODEL_SERIALIZATION_TYPE_ONNX + + +class PyTorchModelSerializerType: + TORCH = MODEL_SERIALIZATION_TYPE_TORCH + TORCHSCRIPT = MODEL_SERIALIZATION_TYPE_TORHCSCRIPT + ONNX = MODEL_SERIALIZATION_TYPE_TORCH_ONNX + + +class TensorflowModelSerializerType: + TENSORFLOW = MODEL_SERIALIZATION_TYPE_TF + ONNX = MODEL_SERIALIZATION_TYPE_TF_ONNX + + +class LightGBMModelSerializerType: + LIGHTGBM = MODEL_SERIALIZATION_TYPE_LIGHTGBM + ONNX = MODEL_SERIALIZATION_TYPE_LIGHTGBM_ONNX + + +class SklearnModelSerializerType: + JOBLIB = MODEL_SERIALIZATION_TYPE_JOBLIB + CLOUDPICKLE = MODEL_SERIALIZATION_TYPE_CLOUDPICKLE + ONNX = MODEL_SERIALIZATION_TYPE_SKLEARN_ONNX + + +class XgboostModelSerializerType: + XGBOOST = MODEL_SERIALIZATION_TYPE_XGBOOST + ONNX = MODEL_SERIALIZATION_TYPE_XGBOOST_ONNX + + +class SparkModelSerializerType: + SPARK = MODEL_SERIALIZATION_TYPE_SPARK + + +class HuggingFaceSerializerType: + HUGGINGFACE = MODEL_SERIALIZATION_TYPE_HUGGINGFACE + + +class ModelSerializer(Serializer): + """Base class for creation of new model serializers.""" + + def __init__(self, model_file_suffix): + super().__init__() + self.model_file_suffix = model_file_suffix + + +class ModelDeserializer(Deserializer): + """Base class for creation of new model deserializers.""" + + def deserialize(self, **kwargs): + raise NotImplementedError + + +class CloudPickleModelSerializer(ModelSerializer): + """Uses `Cloudpickle` to save model.""" + + def __init__(self, model_file_suffix="pkl"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + """Uses `cloudpickle.dump` to save model. See https://docs.python.org/3/library/pickle.html#pickle.dump for more details. + + Args: + estimator: The model to be saved. + model_path: The file object or path of the model in which it is to be stored. + kwargs: + model_save: (dict, optional). + The dictionary where contains the availiable options to be passed to `cloudpickle.dump`. + """ + cloudpickle_kwargs = kwargs.pop("model_save", {}) + with open(model_path, "wb") as f: + cloudpickle.dump(estimator, f, **cloudpickle_kwargs) + + +class JobLibModelSerializer(ModelSerializer): + """Uses `Joblib` to save model.""" + + def __init__(self, model_file_suffix="joblib"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + """Uses `joblib.dump` to save model. See https://joblib.readthedocs.io/en/latest/generated/joblib.dump.html for more details. + + Args: + estimator: The model to be saved. + model_path: The file object or path of the model in which it is to be stored. + kwargs: + model_save: (dict, optional). + The dictionary where contains the availiable options to be passed to `joblib.dump`. + """ + joblib_kwargs = kwargs.pop("model_save", {}) + dump(estimator, model_path, **joblib_kwargs) + + +class SparkModelSerializer(ModelSerializer): + """Save Spark Model.""" + + def __init__(self, model_file_suffix=""): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + estimator.write().overwrite().save(model_path) + + +class PyTorchModelSerializer(ModelSerializer): + """Save PyTorch Model using torch.save(). See https://pytorch.org/docs/stable/generated/torch.save.html for more details.""" + + def __init__(self, model_file_suffix="pt"): + super().__init__(model_file_suffix=model_file_suffix) + + @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) + def serialize(self, estimator, model_path, **kwarg): + torch.save(estimator.state_dict(), model_path) + + +class TorchScriptModelSerializer(ModelSerializer): + """Save PyTorch Model using torchscript. See https://pytorch.org/tutorials/beginner/saving_loading_models.html#export-load-model-in-torchscript-format for more details.""" + + def __init__(self, model_file_suffix="pt"): + super().__init__(model_file_suffix=model_file_suffix) + + @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) + def serialize(self, estimator, model_path, **kwargs): + compiled_model = torch.jit.script(estimator) + torch.jit.save(compiled_model, model_path) + + +class LightGBMModelSerializer(ModelSerializer): + """Save LightGBM Model through save_model into txt.""" + + def __init__(self, model_file_suffix="txt"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + estimator.save_model(model_path) + + +class XgboostJsonModelSerializer(ModelSerializer): + """Save Xgboost Model through xgboost.save_model into JSON.""" + + def __init__(self, model_file_suffix="json"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + """Save Xgboost Model through xgboost.save_model .See + https://xgboost.readthedocs.io/en/stable/python/python_api.html#xgboost.Booster.save_model + for more details. + + Args: + estimator: The model to be saved. + model_path: The file object or path of the model in which it is to be stored. + """ + estimator.save_model(model_path) + + +class XgboostTxtModelSerializer(ModelSerializer): + """Save Xgboost Model through xgboost.save_model into txt.""" + + def __init__(self, model_file_suffix="txt"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + """Save Xgboost Model through xgboost.save_model .See + https://xgboost.readthedocs.io/en/stable/python/python_api.html#xgboost.Booster.save_model + for more details. + + Args: + estimator: The model to be saved. + model_path: The file object or path of the model in which it is to be stored. + """ + estimator.save_model(model_path) + + +class XgboostUbjModelSerializer(ModelSerializer): + """Save Xgboost Model through xgboost.save_model into binary JSON.""" + + def __init__(self, model_file_suffix="ubj"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + """Save Xgboost Model through xgboost.save_model .See + https://xgboost.readthedocs.io/en/stable/python/python_api.html#xgboost.Booster.save_model + for more details. + + Args: + estimator: The model to be saved. + model_path: The file object or path of the model in which it is to be stored. + """ + estimator.save_model(model_path) + + +class TensorFlowModelSerializer(ModelSerializer): + """Save Tensorflow Model.""" + + def __init__(self, model_file_suffix="h5"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + estimator.save(model_path) + + +class HuggingFaceModelSerializer(ModelSerializer): + """Save HuggingFace Pipeline.""" + + def __init__(self, model_file_suffix=""): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize(self, estimator, model_path, **kwargs): + estimator.save_pretrained(save_directory=model_path) + estimator.model.config.use_pretrained_backbone = False + estimator.model.config.save_pretrained(save_directory=model_path) + + +class OnnxModelSerializer(ModelSerializer): + """Base class for creation of onnx converter for each model framework.""" + + def __init__(self, model_file_suffix="onnx"): + super().__init__(model_file_suffix=model_file_suffix) + + def serialize( + self, + estimator, + model_path, + initial_types: List[Tuple] = None, + X_sample: Optional[ + Union[ + Dict, + str, + List, + Tuple, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ] + ] = None, + **kwargs, + ): + """Save model into onnx format. + + Args: + estimator: The model to be saved. + model_path: The file object or path of the model in which it is to be stored. + initial_types: (List[Tuple], optional) + a python list. Each element is a tuple of a variable name and a data type. + X_sample: (any, optional). Defaults to None. + Contains model inputs such that model(X_sample) is a valid + invocation of the model, used to valid model input type. + """ + self.estimator = estimator + onx = self._to_onnx( + initial_types=initial_types, + X_sample=X_sample, + **kwargs, + ) + with open(model_path, "wb") as f: + f.write(onx.SerializeToString()) + + def _to_onnx( + self, + initial_types: List[Tuple] = None, + X_sample: Optional[ + Union[ + Dict, + str, + List, + Tuple, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ] + ] = None, + **kwargs, + ): + raise NotImplementedError + + +class SklearnOnnxModelSerializer(OnnxModelSerializer): + """Converts Skearn Model into Onnx.""" + + def __init__(self): + super().__init__() + + @runtime_dependency(module="onnx", install_from=OptionalDependency.ONNX) + @runtime_dependency(module="xgboost", install_from=OptionalDependency.BOOSTED) + @runtime_dependency(module="lightgbm", install_from=OptionalDependency.BOOSTED) + @runtime_dependency(module="skl2onnx", install_from=OptionalDependency.ONNX) + @runtime_dependency(module="onnxmltools", install_from=OptionalDependency.ONNX) + @runtime_dependency( + module="onnxmltools.convert.xgboost.operator_converters.XGBoost", + object="convert_xgboost", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency( + module="onnxmltools.convert.lightgbm.operator_converters.LightGbm", + object="convert_lightgbm", + install_from=OptionalDependency.ONNX, + ) + def _to_onnx( + self, + initial_types: List[Tuple] = None, + X_sample: Optional[ + Union[ + Dict, + str, + List, + Tuple, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ] + ] = None, + **kwargs, + ): + """ + Produces an equivalent ONNX model of the given scikit-learn model. + + Parameters + ---------- + initial_types: (List[Tuple], optional). Defaults to None. + Each element is a tuple of a variable name and a type. + X_sample: Union[Dict, str, List, np.ndarray, pd.core.series.Series, pd.core.frame.DataFrame,]. Defaults to None. + Contains model inputs such that model(X_sample) is a valid invocation of the model. + Used to generate initial_types. + + Returns + ------- + onnx.onnx_ml_pb2.ModelProto + An ONNX model (type: ModelProto) which is equivalent to the input scikit-learn model. + """ + auto_generated_initial_types = None + if not initial_types: + if X_sample is None: + raise ValueError( + " At least one of `X_sample` or `initial_types` must be provided." + ) + auto_generated_initial_types = self._generate_initial_types(X_sample) + if str(type(self.estimator)).startswith(" List: + """Auto generate intial types. + + Parameters + ---------- + X_sample: (Any) + Train data. + + Returns + ------- + List + Initial types. + """ + if self._is_all_numerical_array_dataframe(X_sample): + # if it's a dataframe and all the columns are numerical. Or + # it's not a dataframe, also try this. + if hasattr(X_sample, "shape") and len(X_sample.shape) >= 2: + auto_generated_initial_types = [ + ( + "input", + skl2onnx.common.data_types.FloatTensorType( + [None, X_sample.shape[1]] + ), + ) + ] + elif hasattr(self.estimator, "n_features_in_"): + n_cols = self.estimator.n_features_in_ + auto_generated_initial_types = [ + ( + "input", + skl2onnx.common.data_types.FloatTensorType([None, n_cols]), + ) + ] + else: + raise ValueError( + "`initial_types` can not be detected. Please directly pass initial_types." + ) + elif self.is_either_numerical_or_string_dataframe(X_sample): + # for dataframe and not all the columns are numerical, then generate + # the input types of all the columns one by one. + auto_generated_initial_types = [] + + for i, col in X_sample.iteritems(): + if is_numeric_dtype(col.dtypes): + auto_generated_initial_types.append( + ( + col.name, + skl2onnx.common.data_types.FloatTensorType([None, 1]), + ) + ) + else: + auto_generated_initial_types.append( + ( + col.name, + skl2onnx.common.data_types.StringTensorType([None, 1]), + ) + ) + else: + try: + auto_generated_initial_types = ( + skl2onnx.common.data_types.guess_data_type( + np.array(X_sample) if isinstance(X_sample, list) else X_sample + ) + ) + except: + auto_generated_initial_types = None + return auto_generated_initial_types + + @staticmethod + def _is_all_numerical_array_dataframe( + data: Union[pd.DataFrame, np.ndarray] + ) -> bool: + """Check whether all the columns are numerical for numpy array and dataframe. + For data with any other data types, it will return False. + + Parameters + ---------- + data: Union[pd.DataFrame, np.ndarray] + + Returns + ------- + bool + Whether all the columns in a pandas dataframe or numpy array are all numerical. + """ + return ( + isinstance(data, pd.DataFrame) + and all([is_numeric_dtype(dtype) for dtype in data.dtypes]) + or (isinstance(data, np.ndarray) and is_numeric_dtype(data.dtype)) + ) + + @staticmethod + def is_either_numerical_or_string_dataframe(data: pd.DataFrame) -> bool: + """Check whether all the columns are either numerical or string for dataframe.""" + return isinstance(data, pd.DataFrame) and all( + [ + is_numeric_dtype(col.dtypes) or is_string_dtype(col.dtypes) + for _, col in data.iteritems() + ] + ) + + +class LightGBMOnnxModelSerializer(OnnxModelSerializer): + """Converts LightGBM model into onnx format.""" + + def __init__(self): + super().__init__() + + @runtime_dependency( + module="skl2onnx.common.data_types", + object="FloatTensorType", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency( + module="onnxmltools.convert", + object="convert_lightgbm", + install_from=OptionalDependency.ONNX, + ) + def _to_onnx( + self, + initial_types: List[Tuple] = None, + X_sample: Optional[ + Union[ + Dict, + str, + List, + Tuple, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ] + ] = None, + **kwargs, + ): + """ + Produces an equivalent ONNX model of the given LightGBM model. + + Parameters + ---------- + initial_types: (List[Tuple], optional). Defaults to None. + Each element is a tuple of a variable name and a type. + X_sample: Union[Dict, str, List, np.ndarray, pd.core.series.Series, pd.core.frame.DataFrame,]. Defaults to None. + Contains model inputs such that model(X_sample) is a valid invocation of the model. + Used to generate initial_types. + + Returns + ------ + An ONNX model (type: ModelProto) which is equivalent to the input LightGBM model. + """ + auto_generated_initial_types = None + if not initial_types: + auto_generated_initial_types = self._generate_initial_types(X_sample) + try: + return convert_lightgbm( + self.estimator, + initial_types=auto_generated_initial_types, + target_opset=kwargs.pop("target_opset", None), + **kwargs, + ) + except: + raise ValueError( + "`initial_types` can not be detected. Please directly pass initial_types." + ) + else: + return convert_lightgbm( + self.estimator, + initial_types=initial_types, + target_opset=kwargs.pop("target_opset", None), + **kwargs, + ) + + @runtime_dependency( + module="skl2onnx.common.data_types", + object="FloatTensorType", + install_from=OptionalDependency.ONNX, + ) + def _generate_initial_types(self, X_sample: Any) -> List: + """Auto generate intial types. + + Parameters + ---------- + X_sample: (Any) + Train data. + + Returns + ------- + List + Initial types. + """ + if X_sample is not None and hasattr(X_sample, "shape"): + auto_generated_initial_types = [ + ("input", FloatTensorType([None, X_sample.shape[1]])) + ] + elif hasattr(self.estimator, "num_feature"): + n_cols = self.estimator.num_feature() + auto_generated_initial_types = [("input", FloatTensorType([None, n_cols]))] + elif hasattr(self.estimator, "n_features_in_"): + n_cols = self.estimator.n_features_in_ + auto_generated_initial_types = [("input", FloatTensorType([None, n_cols]))] + else: + raise ValueError( + "`initial_types` can not be detected. Please directly pass initial_types." + ) + return auto_generated_initial_types + + +class XgboostOnnxModelSerializer(OnnxModelSerializer): + """Converts Xgboost model into onnx format.""" + + def __init__(self): + super().__init__() + + @runtime_dependency(module="onnx", install_from=OptionalDependency.ONNX) + @runtime_dependency(module="xgboost", install_from=OptionalDependency.BOOSTED) + @runtime_dependency( + module="skl2onnx", + object="convert_sklearn", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency( + module="skl2onnx", + object="update_registered_converter", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency( + module="skl2onnx.common.data_types", + object="FloatTensorType", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency( + module="skl2onnx.common.shape_calculator", + object="calculate_linear_classifier_output_shapes", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency( + module="skl2onnx.common.shape_calculator", + object="calculate_linear_regressor_output_shapes", + install_from=OptionalDependency.ONNX, + ) + @runtime_dependency(module="onnxmltools", install_from=OptionalDependency.ONNX) + @runtime_dependency( + module="onnxmltools.convert.xgboost.operator_converters.XGBoost", + object="convert_xgboost", + install_from=OptionalDependency.ONNX, + ) + def _to_onnx( + self, + initial_types: List[Tuple] = None, + X_sample: Union[list, tuple, pd.DataFrame, pd.Series, np.ndarray] = None, + **kwargs, + ): + """ + Produces an equivalent ONNX model of the given Xgboost model. + + Parameters + ---------- + initial_types: (List[Tuple], optional). Defaults to None. + Each element is a tuple of a variable name and a type. + X_sample: Union[Dict, str, List, np.ndarray, pd.core.series.Series, pd.core.frame.DataFrame,]. Defaults to None. + Contains model inputs such that model(X_sample) is a valid invocation of the model. + Used to generate initial_types. + + Returns + ------- + onnx.onnx_ml_pb2.ModelProto + An ONNX model (type: ModelProto) which is equivalent to the input xgboost model. + """ + auto_generated_initial_types = None + if not initial_types: + auto_generated_initial_types = self._generate_initial_types(X_sample) + + model_types = [] + if str(type(self.estimator)).startswith(" List: + """Auto generate intial types. + + Parameters + ---------- + X_sample: (Any) + Train data. + + Returns + ------- + List + Initial types. + """ + if hasattr(self.estimator, "n_features_in_"): + # sklearn api + n_cols = self.estimator.n_features_in_ + return [("input", FloatTensorType([None, n_cols]))] + elif hasattr(self.estimator, "feature_names") and self.estimator.feature_names: + # xgboost learning api + n_cols = len(self.estimator.feature_names) + return [("input", FloatTensorType([None, n_cols]))] + if X_sample is None: + raise ValueError( + " At least one of `X_sample` or `initial_types` must be provided." + ) + if ( + X_sample is not None + and hasattr(X_sample, "shape") + and len(X_sample.shape) >= 2 + ): + auto_generated_initial_types = [ + ("input", FloatTensorType([None, X_sample.shape[1]])) + ] + else: + raise ValueError( + "`initial_types` can not be detected. Please directly pass initial_types." + ) + return auto_generated_initial_types + + +class PytorchOnnxModelSerializer(OnnxModelSerializer): + """Converts Pytorch model into onnx format.""" + + def __init__(self): + super().__init__() + + @runtime_dependency(module="torch", install_from=OptionalDependency.PYTORCH) + def serialize( + self, + estimator, + model_path: str, + X_sample: Optional[ + Union[ + Dict, + str, + List, + Tuple, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ] + ] = None, + **kwargs, + ): + """ + Exports the given Pytorch model into ONNX format. + + Parameters + ---------- + path: str, default to None + Path to save the serialized model. + onnx_args: (tuple or torch.Tensor), default to None + Contains model inputs such that model(onnx_args) is a valid + invocation of the model. Can be structured either as: 1) ONLY A + TUPLE OF ARGUMENTS; 2) A TENSOR; 3) A TUPLE OF ARGUMENTS ENDING + WITH A DICTIONARY OF NAMED ARGUMENTS + X_sample: Union[list, tuple, pd.Series, np.ndarray, pd.DataFrame]. Defaults to None. + A sample of input data that will be used to generate input schema and detect onnx_args. + kwargs: + input_names: (List[str], optional). Defaults to ["input"]. + Names to assign to the input nodes of the graph, in order. + output_names: (List[str], optional). Defaults to ["output"]. + Names to assign to the output nodes of the graph, in order. + dynamic_axes: (dict, optional). Defaults to None. + Specify axes of tensors as dynamic (i.e. known only at run-time). + + Returns + ------- + None + Nothing + + Raises + ------ + AssertionError + if onnx module is not support by the current version of torch + ValueError + if X_sample is not provided + if path is not provided + """ + onnx_args = kwargs.get("onnx_args", None) + input_names = kwargs.get("input_names", ["input"]) + output_names = kwargs.get("output_names", ["output"]) + dynamic_axes = kwargs.get("dynamic_axes", None) + + assert hasattr(torch, "onnx"), ( + f"This version of pytorch {torch.__version__} does not appear to support onnx " + "conversion." + ) + + if onnx_args is None: + if X_sample is not None: + logger.warning( + "Since `onnx_args` is not provided, `onnx_args` is " + "detected from `X_sample` to export pytorch model as onnx." + ) + onnx_args = X_sample + else: + raise ValueError( + "`onnx_args` can not be detected. The parameter `onnx_args` must be provided to export pytorch model as onnx." + ) + + if not model_path: + raise ValueError( + "The parameter `model_path` must be provided to save the model file." + ) + + torch.onnx.export( + estimator, + args=onnx_args, + f=model_path, + input_names=input_names, + output_names=output_names, + dynamic_axes=dynamic_axes, + ) + + +class TensorFlowOnnxModelSerializer(OnnxModelSerializer): + """Converts Tensorflow model into onnx format.""" + + def __init__(self): + super().__init__() + + @runtime_dependency(module="tf2onnx", install_from=OptionalDependency.ONNX) + @runtime_dependency( + module="tensorflow", + short_name="tf", + install_from=OptionalDependency.TENSORFLOW, + ) + def serialize( + self, + estimator, + model_path: str = None, + X_sample: Optional[ + Union[ + Dict, + str, + List, + Tuple, + np.ndarray, + pd.core.series.Series, + pd.core.frame.DataFrame, + ] + ] = None, + **kwargs, + ): + """ + Exports the given Tensorflow model into ONNX format. + + Parameters + ---------- + model_path: str, default to None + Path to save the serialized model. + X_sample: Union[list, tuple, pd.Series, np.ndarray, pd.DataFrame]. Defaults to None. + A sample of input data that will be used to generate input schema and detect input_signature. + + + Returns + ------- + None + Nothing + + Raises + ------ + ValueError + if model_path is not provided + """ + opset_version = kwargs.get("opset_version", None) + input_signature = kwargs.get("input_signature", None) + + if not model_path: + raise ValueError( + "The parameter `model_path` must be provided to save the model file." + ) + if input_signature is None: + if hasattr(estimator, "input_shape"): + if not isinstance(estimator.input, list): + # single input + detected_input_signature = ( + tf.TensorSpec( + estimator.input_shape, + dtype=estimator.input.dtype, + name="input", + ), + ) + else: + # multiple input + detected_input_signature = [] + for i in range(len(estimator.input)): + detected_input_signature.append( + tf.TensorSpec( + estimator.input_shape[i], + dtype=estimator.input[i].dtype, + ) + ) + + elif X_sample is not None and hasattr(X_sample, "shape"): + logger.warning( + "Since `input_signature` is not provided, `input_signature` is " + "detected from `X_sample` to export tensorflow model as " + "onnx." + ) + X_sample_shape = list(X_sample.shape) + X_sample_shape[0] = None + detected_input_signature = ( + tf.TensorSpec(X_sample_shape, dtype=X_sample.dtype, name="input"), + ) + else: + raise ValueError( + "The parameter `input_signature` must be provided to export " + "tensorflow model as onnx." + ) + try: + tf2onnx.convert.from_keras( + estimator, + input_signature=detected_input_signature, + opset=opset_version, + output_path=model_path, + ) + except: + raise ValueError( + "`input_signature` can not be autodetected. The parameter `input_signature` must be provided to export " + "tensorflow model as onnx." + ) + + else: + tf2onnx.convert.from_keras( + estimator, + input_signature=input_signature, + opset=opset_version, + output_path=model_path, + ) + + +class OnnxModelSaveSERDE(OnnxModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_ONNX + + +class CloudpickleModelSaveSERDE(CloudPickleModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_CLOUDPICKLE + + +class JoblibModelSaveSERDE(JobLibModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_JOBLIB + + +class SparkModelSaveSERDE(SparkModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_SPARK + + +class HuggingFacePipelineSaveSERDE(HuggingFaceModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_HUGGINGFACE + + +class TorchScriptModelSaveSERDE(TorchScriptModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_TORHCSCRIPT + + +class PyTorchModelSaveSERDE(PyTorchModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_TORCH + + +class PyTorchOnnxModelSaveSERDE(PytorchOnnxModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_TORCH_ONNX + + +class TensorFlowModelSaveSERDE(TensorFlowModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_TF + + +class TensorFlowOnnxModelSaveSERDE(TensorFlowOnnxModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_TF_ONNX + + +class SklearnOnnxModelSaveSERDE(SklearnOnnxModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_SKLEARN_ONNX + + +class LightGBMModelSaveSERDE(LightGBMModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_LIGHTGBM + + +class LightGBMOnnxModelSaveSERDE(LightGBMOnnxModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_LIGHTGBM_ONNX + + +class XgboostJsonModelSaveSERDE(XgboostJsonModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_XGBOOST + + +class XgboostUbjModelSaveSERDE(XgboostUbjModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_XGBOOST_UBJ + + +class XgboostTxtModelSaveSERDE(XgboostTxtModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_XGBOOST_TXT + + +class XgboostOnnxModelSaveSERDE(XgboostOnnxModelSerializer, ModelDeserializer): + name = MODEL_SERIALIZATION_TYPE_XGBOOST_ONNX + + +class ModelSerializerFactory: + """Model Serializer Factory. + + Returns + ------- + model_save_serde: Intance of `ads.model.SERDE`". + """ + + _factory = {} + _factory[MODEL_SERIALIZATION_TYPE_CLOUDPICKLE] = CloudpickleModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_ONNX] = OnnxModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_TORHCSCRIPT] = TorchScriptModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_TORCH] = PyTorchModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_TORCH_ONNX] = PyTorchOnnxModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_TF] = TensorFlowModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_TF_ONNX] = TensorFlowOnnxModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_JOBLIB] = JoblibModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_SKLEARN_ONNX] = SklearnOnnxModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_LIGHTGBM] = LightGBMModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_LIGHTGBM_ONNX] = LightGBMOnnxModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_XGBOOST] = XgboostJsonModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_XGBOOST_UBJ] = XgboostUbjModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_XGBOOST_TXT] = XgboostTxtModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_XGBOOST_ONNX] = XgboostOnnxModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_SPARK] = SparkModelSaveSERDE + _factory[MODEL_SERIALIZATION_TYPE_HUGGINGFACE] = HuggingFacePipelineSaveSERDE + + @classmethod + def get(cls, se: str): + serde = cls._factory.get(se, None) + if serde: + return serde() + else: + raise ValueError( + f"This {se} format is not supported." + f"Currently support the following format: {SUPPORTED_MODEL_SERIALIZERS}." + ) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 86dbf5bf9..1190f50ea 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -16,7 +16,7 @@ from ads.common.oci_datascience import OCIDataScienceMixin from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource -from ads.model.common.utils import extract_region +from ads.common.utils import extract_region from ads.model.deployment.model_deployer import ModelDeployer from oci.data_science.models import ( ArtifactExportDetailsObjectStorage, @@ -346,7 +346,7 @@ def import_model_artifact(self, bucket_uri: str, region: str = None) -> None: If model artifact not found. """ bucket_details = ObjectStorageDetails.from_path(bucket_uri) - region = region or extract_region() + region = region or extract_region(self.auth) try: work_request_id = self.client.import_model_artifact( model_id=self.id, @@ -393,7 +393,7 @@ def export_model_artifact(self, bucket_uri: str, region: str = None): None """ bucket_details = ObjectStorageDetails.from_path(bucket_uri) - region = region or extract_region() + region = region or extract_region(self.auth) work_request_id = self.client.export_model_artifact( model_id=self.id, @@ -551,7 +551,7 @@ def _wait_for_work_request(self, work_request_id: str, num_steps: int = 3) -> No work_request_id: str Work Request OCID. num_steps: (int, optional). Defaults to 3. - Number of steps for progress indicator. + Number of steps for the progress indicator. Returns ------- @@ -560,33 +560,41 @@ def _wait_for_work_request(self, work_request_id: str, num_steps: int = 3) -> No STOP_STATE = ( WorkRequest.STATUS_SUCCEEDED, WorkRequest.STATUS_CANCELED, - WorkRequest.STATUS_CANCELING, WorkRequest.STATUS_FAILED, ) - work_request_logs = None + work_request_logs = [] i = 0 with utils.get_progress_bar(num_steps) as progress: while not work_request_logs or len(work_request_logs) < num_steps: time.sleep(_REQUEST_INTERVAL_IN_SEC) - work_request = self.client.get_work_request(work_request_id) - work_request_logs = self.client.list_work_request_logs( - work_request_id - ).data - if work_request_logs: - new_work_request_logs = work_request_logs[i:] + new_work_request_logs = [] + + try: + work_request = self.client.get_work_request(work_request_id).data + work_request_logs = self.client.list_work_request_logs( + work_request_id + ).data + except Exception as ex: + logger.warn(ex) + + new_work_request_logs = ( + work_request_logs[i:] if work_request_logs else [] + ) for wr_item in new_work_request_logs: progress.update(wr_item.message) i += 1 - if work_request.data.status in STOP_STATE: - if work_request.data.status != WorkRequest.STATUS_SUCCEEDED: - if work_request_logs: - raise Exception(work_request_logs[-1].message) + if work_request and work_request.status in STOP_STATE: + if work_request.status != WorkRequest.STATUS_SUCCEEDED: + if new_work_request_logs: + raise Exception(new_work_request_logs[-1].message) else: raise Exception( - "An error occurred in attempt to perform the operation. Check the service logs to get more details." + "Error occurred in attempt to perform the operation. " + "Check the service logs to get more details. " + f"{work_request}" ) else: break diff --git a/ads/model/service/oci_datascience_model_deployment.py b/ads/model/service/oci_datascience_model_deployment.py new file mode 100644 index 000000000..b6649bea9 --- /dev/null +++ b/ads/model/service/oci_datascience_model_deployment.py @@ -0,0 +1,600 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +from functools import wraps +import json +import time +import logging +from typing import Callable, List +from ads.common.oci_datascience import OCIDataScienceMixin +from ads.common import utils as progress_bar_utils +from ads.config import PROJECT_OCID +from ads.model.deployment.common import utils +from ads.model.deployment.common.utils import OCIClientManager, State +import oci + +from oci.data_science.models import ( + CreateModelDeploymentDetails, + UpdateModelDeploymentDetails, +) + +DEFAULT_WAIT_TIME = 1200 +DEFAULT_POLL_INTERVAL = 10 +DEACTIVATE_WORKFLOW_STEPS = 2 +DELETE_WORKFLOW_STEPS = 2 +ACTIVATE_WORKFLOW_STEPS = 6 +CREATE_WORKFLOW_STEPS = 6 +ALLOWED_STATUS = [ + State.ACTIVE.name, + State.CREATING.name, + State.DELETED.name, + State.DELETING.name, + State.FAILED.name, + State.INACTIVE.name, + State.UPDATING.name, +] + +logger = logging.getLogger(__name__) + +MODEL_DEPLOYMENT_NEEDS_TO_BE_DEPLOYED = "Missing model deployment id. Model deployment needs to be deployed before it can be accessed." + + +def check_for_model_deployment_id(msg: str = MODEL_DEPLOYMENT_NEEDS_TO_BE_DEPLOYED): + """The decorator helping to check if the ID attribute sepcified for a datascience model deployment. + + Parameters + ---------- + msg: str + The message that will be thrown. + + Raises + ------ + MissingModelDeploymentIdError + In case if the ID attribute not specified. + + Examples + -------- + >>> @check_for_id(msg="Some message.") + ... def test_function(self, name: str, last_name: str) + ... pass + """ + + def decorator(func: Callable): + @wraps(func) + def wrapper(self, *args, **kwargs): + if not self.id: + raise MissingModelDeploymentIdError(msg) + return func(self, *args, **kwargs) + + return wrapper + + return decorator + + +class MissingModelDeploymentIdError(Exception): + pass + + +class MissingModelDeploymentWorkflowIdError(Exception): + pass + + +class OCIDataScienceModelDeployment( + OCIDataScienceMixin, + oci.data_science.models.ModelDeployment, +): + """Represents an OCI Data Science Model Deployment. + This class contains all attributes of the `oci.data_science.models.ModelDeployment`. + The main purpose of this class is to link the `oci.data_science.models.ModelDeployment` + and the related client methods. + Linking the `ModelDeployment` (payload) to Create/Update/Delete/Activate/Deactivate methods. + + The `OCIDataScienceModelDeployment` can be initialized by unpacking the properties stored in a dictionary: + + .. code-block:: python + + properties = { + "compartment_id": "", + "name": "", + "description": "", + } + ds_model_deployment = OCIDataScienceModelDeployment(**properties) + + The properties can also be OCI REST API payload, in which the keys are in camel format. + + .. code-block:: python + + payload = { + "compartmentId": "", + "name": "", + "description": "", + } + ds_model_deployment = OCIDataScienceModelDeployment(**payload) + + Methods + ------- + activate(self, ...) -> "OCIDataScienceModelDeployment": + Activates datascience model deployment. + create(self, ...) -> "OCIDataScienceModelDeployment" + Creates datascience model deployment. + deactivate(self, ...) -> "OCIDataScienceModelDeployment": + Deactivates datascience model deployment. + delete(self, ...) -> "OCIDataScienceModelDeployment": + Deletes datascience model deployment. + update(self, ...) -> "OCIDataScienceModelDeployment": + Updates datascience model deployment. + list(self, ...) -> list[oci.data_science.models.ModelDeployment]: + List oci.data_science.models.ModelDeployment instances within given compartment and project. + from_id(cls, model_deployment_id: str) -> "OCIDataScienceModelDeployment": + Gets model deployment by OCID. + + Examples + -------- + >>> oci_model_deployment = OCIDataScienceModelDeployment.from_id() + >>> oci_model_deployment.deactivate() + >>> oci_model_deployment.activate(wait_for_completion=False) + >>> oci_model_deployment.description = "A brand new description" + ... oci_model_deployment.update() + >>> oci_model_deployment.sync() + >>> oci_model_deployment.list(status="ACTIVE") + >>> oci_model_deployment.delete(wait_for_completion=False) + """ + + def __init__( + self, + config: dict = None, + signer: oci.signer.Signer = None, + client_kwargs: dict = None, + **kwargs, + ) -> None: + super().__init__(config, signer, client_kwargs, **kwargs) + self.workflow_req_id = None + + @check_for_model_deployment_id( + msg="Model deployment needs to be deployed before it can be activated or deactivated." + ) + def activate( + self, + wait_for_completion: bool = True, + max_wait_time: int = DEFAULT_WAIT_TIME, + poll_interval: int = DEFAULT_POLL_INTERVAL, + ) -> "OCIDataScienceModelDeployment": + """Activates datascience model deployment. + + Parameters + ---------- + wait_for_completion: bool + Flag set for whether to wait for process to be completed. + Defaults to True. + max_wait_time: int + Maximum amount of time to wait in seconds (Defaults to 1200). + Negative implies infinite wait time. + poll_interval: int + Poll interval in seconds (Defaults to 10). + + Returns + ------- + OCIDataScienceModelDeployment + The `OCIDataScienceModelDeployment` instance (self). + """ + logger.info(f"Activating model deployment `{self.id}`.") + response = self.client.activate_model_deployment( + self.id, + ) + + if wait_for_completion: + + self.workflow_req_id = response.headers.get("opc-work-request-id", None) + oci_model_deployment_object = self.client.get_model_deployment(self.id).data + current_state = State._from_str(oci_model_deployment_object.lifecycle_state) + model_deployment_id = self.id + + try: + self._wait_for_progress_completion( + State.ACTIVE.name, + ACTIVATE_WORKFLOW_STEPS, + [State.FAILED.name, State.INACTIVE.name], + self.workflow_req_id, + current_state, + model_deployment_id, + max_wait_time, + poll_interval, + ) + except Exception as e: + logger.error( + f"Error while trying to activate model deployment: {self.id}" + ) + raise e + + return self.sync() + + def create( + self, + create_model_deployment_details: CreateModelDeploymentDetails, + wait_for_completion: bool = True, + max_wait_time: int = DEFAULT_WAIT_TIME, + poll_interval: int = DEFAULT_POLL_INTERVAL, + ) -> "OCIDataScienceModelDeployment": + """Creates datascience model deployment. + + Parameters + ---------- + wait_for_completion: bool + Flag set for whether to wait for process to be completed. + Defaults to True. + max_wait_time: int + Maximum amount of time to wait in seconds (Defaults to 1200). + Negative implies infinite wait time. + poll_interval: int + Poll interval in seconds (Defaults to 10). + + Returns + ------- + OCIDataScienceModelDeployment + The `OCIDataScienceModelDeployment` instance (self). + """ + response = self.client.create_model_deployment(create_model_deployment_details) + self.update_from_oci_model(response.data) + logger.info(f"Creating model deployment `{self.id}`.") + + if wait_for_completion: + + self.workflow_req_id = response.headers.get("opc-work-request-id", None) + res_payload = json.loads(str(response.data)) + current_state = State._from_str(res_payload["lifecycle_state"]) + model_deployment_id = self.id + + try: + self._wait_for_progress_completion( + State.ACTIVE.name, + CREATE_WORKFLOW_STEPS, + [State.FAILED.name, State.INACTIVE.name], + self.workflow_req_id, + current_state, + model_deployment_id, + max_wait_time, + poll_interval, + ) + except Exception as e: + logger.error( + f"Error while trying to create model deployment: {self.id}" + ) + raise e + + return self.sync() + + @check_for_model_deployment_id( + msg="Model deployment needs to be deployed before it can be activated or deactivated." + ) + def deactivate( + self, + wait_for_completion: bool = True, + max_wait_time: int = DEFAULT_WAIT_TIME, + poll_interval: int = DEFAULT_POLL_INTERVAL, + ) -> "OCIDataScienceModelDeployment": + """Deactivates datascience model deployment. + + Parameters + ---------- + wait_for_completion: bool + Flag set for whether to wait for process to be completed. + Defaults to True. + max_wait_time: int + Maximum amount of time to wait in seconds (Defaults to 1200). + Negative implies infinite wait time. + poll_interval: int + Poll interval in seconds (Defaults to 10). + + Returns + ------- + OCIDataScienceModelDeployment + The `OCIDataScienceModelDeployment` instance (self). + """ + logger.info(f"Deactivating model deployment `{self.id}`.") + response = self.client.deactivate_model_deployment( + self.id, + ) + + if wait_for_completion: + + self.workflow_req_id = response.headers.get("opc-work-request-id", None) + oci_model_deployment_object = self.client.get_model_deployment(self.id).data + current_state = State._from_str(oci_model_deployment_object.lifecycle_state) + model_deployment_id = self.id + + try: + self._wait_for_progress_completion( + State.INACTIVE.name, + DEACTIVATE_WORKFLOW_STEPS, + [State.FAILED.name], + self.workflow_req_id, + current_state, + model_deployment_id, + max_wait_time, + poll_interval, + ) + except Exception as e: + logger.error( + f"Error while trying to deactivate model deployment: {self.id}" + ) + raise e + + return self.sync() + + @check_for_model_deployment_id( + msg="Model deployment needs to be deployed before it can be deleted." + ) + def delete( + self, + wait_for_completion: bool = True, + max_wait_time: int = DEFAULT_WAIT_TIME, + poll_interval: int = DEFAULT_POLL_INTERVAL, + ) -> "OCIDataScienceModelDeployment": + """Deletes datascience model deployment. + + Parameters + ---------- + wait_for_completion: bool + Flag set for whether to wait for process to be completed. + Defaults to True. + max_wait_time: int + Maximum amount of time to wait in seconds (Defaults to 1200). + Negative implies infinite wait time. + poll_interval: int + Poll interval in seconds (Defaults to 10). + + Returns + ------- + OCIDataScienceModelDeployment + The `OCIDataScienceModelDeployment` instance (self). + """ + logger.info(f"Deleting model deployment `{self.id}`.") + response = self.client.delete_model_deployment( + self.id, + ) + + if wait_for_completion: + + self.workflow_req_id = response.headers.get("opc-work-request-id", None) + oci_model_deployment_object = self.client.get_model_deployment(self.id).data + current_state = State._from_str(oci_model_deployment_object.lifecycle_state) + model_deployment_id = self.id + + try: + self._wait_for_progress_completion( + State.DELETED.name, + DELETE_WORKFLOW_STEPS, + [State.FAILED.name, State.INACTIVE.name], + self.workflow_req_id, + current_state, + model_deployment_id, + max_wait_time, + poll_interval, + ) + except Exception as e: + logger.error( + f"Error while trying to delete model deployment: {self.id}" + ) + raise e + + return self.sync() + + @check_for_model_deployment_id( + msg="Model deployment needs to be deployed before it can be updated." + ) + def update( + self, + update_model_deployment_details: UpdateModelDeploymentDetails, + wait_for_completion: bool = True, + max_wait_time: int = DEFAULT_WAIT_TIME, + poll_interval: int = DEFAULT_POLL_INTERVAL, + ) -> "OCIDataScienceModelDeployment": + """Updates datascience model deployment. + + Parameters + ---------- + update_model_deployment_details: UpdateModelDeploymentDetails + Details to update model deployment. + wait_for_completion: bool + Flag set for whether to wait for process to be completed. + Defaults to True. + max_wait_time: int + Maximum amount of time to wait in seconds (Defaults to 1200). + Negative implies infinite wait time. + poll_interval: int + Poll interval in seconds (Defaults to 10). + + Returns + ------- + OCIDataScienceModelDeployment + The `OCIDataScienceModelDeployment` instance (self). + """ + if wait_for_completion: + wait_for_states = [ + oci.data_science.models.WorkRequest.STATUS_SUCCEEDED, + oci.data_science.models.WorkRequest.STATUS_FAILED, + ] + else: + wait_for_states = [] + + try: + response = self.client_composite.update_model_deployment_and_wait_for_state( + self.id, + update_model_deployment_details, + wait_for_states=wait_for_states, + waiter_kwargs={ + "max_interval_seconds": poll_interval, + "max_wait_seconds": max_wait_time, + }, + ) + self.workflow_req_id = response.headers.get("opc-work-request-id", None) + except Exception as e: + logger.error(f"Error while trying to update model deployment: {self.id}") + raise e + + return self.sync() + + @classmethod + def list( + cls, + status: str = None, + compartment_id: str = None, + project_id: str = None, + **kwargs, + ) -> list: + """Lists the model deployments associated with current compartment id and status + + Parameters + ---------- + status : str + Status of deployment. Defaults to None. + Allowed values: `ACTIVE`, `CREATING`, `DELETED`, `DELETING`, `FAILED`, `INACTIVE` and `UPDATING`. + compartment_id : str + Target compartment to list deployments from. + Defaults to the compartment set in the environment variable "NB_SESSION_COMPARTMENT_OCID". + If "NB_SESSION_COMPARTMENT_OCID" is not set, the root compartment ID will be used. + An ValueError will be raised if root compartment ID cannot be determined. + project_id : str + Target project to list deployments from. + Defaults to the project id in the environment variable "PROJECT_OCID". + kwargs : + The values are passed to oci.data_science.DataScienceClient.list_model_deployments. + + Returns + ------- + list + A list of oci.data_science.models.ModelDeployment objects. + + Raises + ------ + ValueError + If compartment_id is not specified and cannot be determined from the environment. + """ + compartment_id = compartment_id or OCIClientManager().default_compartment_id() + + if not compartment_id: + raise ValueError( + "Unable to determine compartment ID from environment. Specify `compartment_id`." + ) + + project_id = project_id or PROJECT_OCID + if project_id: + kwargs["project_id"] = project_id + + if status is not None: + if status not in ALLOWED_STATUS: + raise ValueError( + f"Allowed `status` values are: {', '.join(ALLOWED_STATUS)}." + ) + kwargs["lifecycle_state"] = status + + # https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/pagination.html#module-oci.pagination + return oci.pagination.list_call_get_all_results( + cls().client.list_model_deployments, compartment_id, **kwargs + ).data + + @classmethod + def from_id(cls, model_deployment_id: str) -> "OCIDataScienceModelDeployment": + """Gets datascience model deployment by OCID. + + Parameters + ---------- + model_deployment_id: str + The OCID of the datascience model deployment. + + Returns + ------- + OCIDataScienceModelDeployment + An instance of `OCIDataScienceModelDeployment`. + """ + return super().from_ocid(model_deployment_id) + + def _wait_for_progress_completion( + self, + final_state: str, + work_flow_step: int, + disallowed_final_states: List[str], + work_flow_request_id: str, + state: State, + model_deployment_id: str, + max_wait_time: int = DEFAULT_WAIT_TIME, + poll_interval: int = DEFAULT_POLL_INTERVAL, + ): + """_wait_for_progress_completion blocks until progress is completed. + + Parameters + ---------- + final_state: str + Final state of model deployment aimed to be reached. + work_flow_step: int + Number of work flow step of the request. + disallowed_final_states: list[str] + List of disallowed final state to be reached. + work_flow_request_id: str + The id of work flow request. + state: State + The current state of model deployment. + model_deployment_id: str + The ocid of model deployment. + max_wait_time: int + Maximum amount of time to wait in seconds (Defaults to 1200). + Negative implies infinite wait time. + poll_interval: int + Poll interval in seconds (Defaults to 10). + """ + + start_time = time.time() + prev_message = "" + prev_workflow_stage_len = 0 + current_state = state or State.UNKNOWN + with progress_bar_utils.get_progress_bar(work_flow_step) as progress: + if max_wait_time > 0 and utils.seconds_since(start_time) >= max_wait_time: + utils.get_logger().error( + f"Max wait time ({max_wait_time} seconds) exceeded." + ) + while ( + max_wait_time < 0 or utils.seconds_since(start_time) < max_wait_time + ) and current_state.name.upper() != final_state: + if current_state.name.upper() in disallowed_final_states: + utils.get_logger().info( + f"Operation failed due to deployment reaching state {current_state.name.upper()}. Use Deployment ID for further steps." + ) + break + + prev_state = current_state.name + try: + model_deployment_payload = json.loads( + str(self.client.get_model_deployment(model_deployment_id).data) + ) + current_state = ( + State._from_str(model_deployment_payload["lifecycle_state"]) + if "lifecycle_state" in model_deployment_payload + else State.UNKNOWN + ) + workflow_payload = self.client.list_work_request_logs( + work_flow_request_id + ).data + if isinstance(workflow_payload, list) and len(workflow_payload) > 0: + if prev_message != workflow_payload[-1].message: + for _ in range( + len(workflow_payload) - prev_workflow_stage_len + ): + progress.update(workflow_payload[-1].message) + prev_workflow_stage_len = len(workflow_payload) + prev_message = workflow_payload[-1].message + prev_workflow_stage_len = len(workflow_payload) + if prev_state != current_state.name: + utils.get_logger().info( + f"Status Update: {current_state.name} in {utils.seconds_since(start_time)} seconds" + ) + except Exception as e: + # utils.get_logger().warning( + # "Unable to update deployment status. Details: %s", format( + # e) + # ) + pass + time.sleep(poll_interval) + progress.update("Done") diff --git a/ads/opctl/backend/ads_model_deployment.py b/ads/opctl/backend/ads_model_deployment.py new file mode 100644 index 000000000..151c7719e --- /dev/null +++ b/ads/opctl/backend/ads_model_deployment.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# -*- coding: utf-8; -*- + +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +from typing import Dict +import ads +from ads.common.auth import create_signer, AuthContext +from ads.common.oci_client import OCIClientFactory +from ads.opctl.backend.base import Backend +from ads.model.deployment import ModelDeployment + +from oci.data_science.models import ModelDeployment as OCIModelDeployment + + +class ModelDeploymentBackend(Backend): + def __init__(self, config: Dict) -> None: + """ + Initialize a ModelDeployment object given config dictionary. + + Parameters + ---------- + config: dict + dictionary of configurations + """ + self.config = config + self.oci_auth = create_signer( + config["execution"].get("auth"), + config["execution"].get("oci_config", None), + config["execution"].get("oci_profile", None), + ) + self.auth_type = config["execution"].get("auth") + self.profile = config["execution"].get("oci_profile", None) + self.client = OCIClientFactory(**self.oci_auth).data_science + + def apply(self) -> None: + """ + Deploy model deployment from YAML. + """ + wait_for_completion = self.config["execution"].get("wait_for_completion") + max_wait_time = self.config["execution"].get("max_wait_time") + poll_interval = self.config["execution"].get("poll_interval") + with AuthContext(): + ads.set_auth(auth=self.auth_type, profile=self.profile) + model_deployment = ModelDeployment.from_dict(self.config) + model_deployment.deploy( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, + ) + print("Model Deployment id: ", model_deployment.model_deployment_id) + + def delete(self) -> None: + """ + Delete model deployment from OCID. + """ + model_deployment_id = self.config["execution"].get("run_id") + wait_for_completion = self.config["execution"].get("wait_for_completion") + max_wait_time = self.config["execution"].get("max_wait_time") + poll_interval = self.config["execution"].get("poll_interval") + with AuthContext(): + ads.set_auth(auth=self.auth_type, profile=self.profile) + model_deployment = ModelDeployment.from_id(model_deployment_id) + if model_deployment.lifecycle_state in [ + OCIModelDeployment.LIFECYCLE_STATE_DELETED + or OCIModelDeployment.LIFECYCLE_STATE_DELETING + ]: + print( + f"Model deployment {model_deployment.model_deployment_id} is either deleted or being deleted." + ) + return + if model_deployment.lifecycle_state not in [ + OCIModelDeployment.LIFECYCLE_STATE_ACTIVE, + OCIModelDeployment.LIFECYCLE_STATE_FAILED, + OCIModelDeployment.LIFECYCLE_STATE_INACTIVE, + ]: + raise Exception( + f"Can't delete model deployment {model_deployment.model_deployment_id} when it's in {model_deployment.lifecycle_state} state." + ) + model_deployment.delete( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, + ) + print( + f"Model Deployment {model_deployment.model_deployment_id} has been deleted." + ) + + def activate(self) -> None: + """ + Activate model deployment from OCID. + """ + model_deployment_id = self.config["execution"].get("run_id") + wait_for_completion = self.config["execution"].get("wait_for_completion") + max_wait_time = self.config["execution"].get("max_wait_time") + poll_interval = self.config["execution"].get("poll_interval") + with AuthContext(): + ads.set_auth(auth=self.auth_type, profile=self.profile) + model_deployment = ModelDeployment.from_id(model_deployment_id) + if ( + model_deployment.lifecycle_state + == OCIModelDeployment.LIFECYCLE_STATE_ACTIVE + ): + print( + f"Model deployment {model_deployment.model_deployment_id} is already active." + ) + return + + if ( + model_deployment.lifecycle_state + == OCIModelDeployment.LIFECYCLE_STATE_INACTIVE + ): + model_deployment.activate( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, + ) + print( + f"Model Deployment {model_deployment.model_deployment_id} has been activated." + ) + else: + raise Exception( + f"Can't activate model deployment {model_deployment.model_deployment_id} when it's in {model_deployment.lifecycle_state} state." + ) + + def deactivate(self) -> None: + """ + Deactivate model deployment from OCID. + """ + model_deployment_id = self.config["execution"].get("run_id") + wait_for_completion = self.config["execution"].get("wait_for_completion") + max_wait_time = self.config["execution"].get("max_wait_time") + poll_interval = self.config["execution"].get("poll_interval") + with AuthContext(): + ads.set_auth(auth=self.auth_type, profile=self.profile) + model_deployment = ModelDeployment.from_id(model_deployment_id) + if ( + model_deployment.lifecycle_state + == OCIModelDeployment.LIFECYCLE_STATE_INACTIVE + ): + print( + f"Model deployment {model_deployment.model_deployment_id} is already inactive." + ) + return + + if ( + model_deployment.lifecycle_state + == OCIModelDeployment.LIFECYCLE_STATE_ACTIVE + ): + model_deployment.deactivate( + wait_for_completion=wait_for_completion, + max_wait_time=max_wait_time, + poll_interval=poll_interval, + ) + print( + f"Model Deployment {model_deployment.model_deployment_id} has been deactivated." + ) + else: + raise Exception( + f"Can't deactivate model deployment {model_deployment.model_deployment_id} when it's in {model_deployment.lifecycle_state} state." + ) + + def watch(self) -> None: + """ + Watch Model Deployment from OCID. + """ + model_deployment_id = self.config["execution"].get("run_id") + log_type = self.config["execution"].get("log_type") + interval = self.config["execution"].get("interval") + log_filter = self.config["execution"].get("log_filter") + with AuthContext(): + ads.set_auth(auth=self.auth_type, profile=self.profile) + model_deployment = ModelDeployment.from_id(model_deployment_id) + model_deployment.watch( + log_type=log_type, interval=interval, log_filter=log_filter + ) diff --git a/ads/opctl/backend/base.py b/ads/opctl/backend/base.py index 6d23c3c90..3e3563982 100644 --- a/ads/opctl/backend/base.py +++ b/ads/opctl/backend/base.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from abc import abstractmethod @@ -67,6 +67,26 @@ def apply(self) -> None: None """ + def activate(self) -> None: + """ + Activate a remote service. + + Returns + ------- + None + """ + raise NotImplementedError("`activate` has not been implemented yet.") + + def deactivate(self) -> None: + """ + Deactivate a remote service. + + Returns + ------- + None + """ + raise NotImplementedError("`deactivate` has not been implemented yet.") + def run_diagnostics(self): """ Implement Diagnostics check appropriate for the backend diff --git a/ads/opctl/cli.py b/ads/opctl/cli.py index 194548c2d..6835024b2 100644 --- a/ads/opctl/cli.py +++ b/ads/opctl/cli.py @@ -5,7 +5,6 @@ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import fsspec -import json import os import click @@ -13,7 +12,9 @@ from ads.common.auth import AuthType from ads.common import auth as authutil +from ads.opctl.cmds import activate as activate_cmd from ads.opctl.cmds import cancel as cancel_cmd +from ads.opctl.cmds import deactivate as deactivate_cmd from ads.opctl.cmds import configure as configure_cmd from ads.opctl.cmds import delete as delete_cmd from ads.opctl.cmds import init_vscode as init_vscode_cmd @@ -224,6 +225,42 @@ def init_vscode(**kwargs): type=click.Choice(["api_key", "resource_principal"]), default=None, ), + click.option( + "--wait-for-completion", + help="either to wait for process to complete or not", + is_flag=True, + required=False, + ), + click.option( + "--max-wait-time", + help="maximum wait time in seconds for progress to complete", + type=int, + required=False, + default=1200, + ), + click.option( + "--poll-interval", + help="poll interval in seconds", + type=int, + required=False, + default=10, + ), + click.option( + "--log-type", help="the type of logging.", required=False, default=None + ), + click.option( + "--log-filter", + help="expression for filtering the logs.", + required=False, + default=None, + ), + click.option( + "--interval", + help="log interval in seconds", + type=int, + required=False, + default=3, + ), ] @@ -328,7 +365,6 @@ def _add_options(func): ) @click.option( "--auto_increment", - "-i", default=False, is_flag=True, help="Increments tag of the image while rebuilding", @@ -434,12 +470,6 @@ def cancel(**kwargs): @commands.command() @click.argument("ocid", nargs=1) -@click.option( - "--log_type", - default=None, - required=False, - help="The type of logging.", -) @add_options(_options) def watch(**kwargs): """ @@ -449,11 +479,30 @@ def watch(**kwargs): suppress_traceback(kwargs["debug"])(watch_cmd)(**kwargs) +@commands.command() +@click.argument("ocid", nargs=1) +@add_options(_options) +def activate(**kwargs): + """ + Activates a data science service. + """ + suppress_traceback(kwargs["debug"])(activate_cmd)(**kwargs) + + +@commands.command() +@click.argument("ocid", nargs=1) +@add_options(_options) +def deactivate(**kwargs): + """ + Deactivates a data science service. + """ + suppress_traceback(kwargs["debug"])(deactivate_cmd)(**kwargs) + + commands.add_command(ads.opctl.conda.cli.commands) commands.add_command(ads.opctl.spark.cli.commands) commands.add_command(ads.opctl.distributed.cli.commands) - # @commands.command() # @click.option("--debug", "-d", help="set debug mode", is_flag=True, default=False) # def list(debug): diff --git a/ads/opctl/cmds.py b/ads/opctl/cmds.py index 6fe874651..16e33c7e2 100644 --- a/ads/opctl/cmds.py +++ b/ads/opctl/cmds.py @@ -16,6 +16,7 @@ from ads.common.oci_datascience import DSCNotebookSession from ads.common.extended_enum import ExtendedEnumMeta from ads.opctl.backend.ads_ml_job import MLJobBackend, MLJobDistributedBackend +from ads.opctl.backend.ads_model_deployment import ModelDeploymentBackend from ads.opctl.backend.local import ( LocalBackend, LocalBackendDistributed, @@ -63,12 +64,14 @@ class DataScienceResource(str, metaclass=ExtendedEnumMeta): JOB = "datasciencejob" DATAFLOW = "dataflowapplication" PIPELINE = "datasciencepipeline" + MODEL_DEPLOYMENT = "datasciencemodeldeployment" class DataScienceResourceRun(str, metaclass=ExtendedEnumMeta): JOB_RUN = "datasciencejobrun" DATAFLOW_RUN = "dataflowrun" PIPELINE_RUN = "datasciencepipelinerun" + MODEL_DEPLOYMENT = "datasciencemodeldeployment" DATA_SCIENCE_RESOURCE_BACKEND_MAP = { @@ -78,12 +81,14 @@ class DataScienceResourceRun(str, metaclass=ExtendedEnumMeta): DataScienceResourceRun.DATAFLOW_RUN: "dataflow", DataScienceResource.PIPELINE: "pipeline", DataScienceResourceRun.PIPELINE_RUN: "pipeline", + DataScienceResourceRun.MODEL_DEPLOYMENT: "deployment", } DATA_SCIENCE_RESOURCE_RUN_BACKEND_MAP = { DataScienceResourceRun.JOB_RUN: "job", DataScienceResourceRun.DATAFLOW_RUN: "dataflow", DataScienceResourceRun.PIPELINE_RUN: "pipeline", + DataScienceResourceRun.MODEL_DEPLOYMENT: "deployment", } @@ -92,6 +97,7 @@ class _BackendFactory: BACKEND_NAME.JOB.value: MLJobBackend, BACKEND_NAME.DATAFLOW.value: DataFlowBackend, BACKEND_NAME.PIPELINE.value: PipelineBackend, + BACKEND_NAME.MODEL_DEPLOYMENT.value: ModelDeploymentBackend, } LOCAL_BACKENDS_MAP = { @@ -206,7 +212,6 @@ def run(config: Dict, **kwargs) -> Dict: else: if not kwargs["dry_run"]: verify_and_publish_image(kwargs["nopush"], config) - print("running image: " + config["spec"]["cluster"]["spec"]["image"]) cluster_def = YamlSpecParser.parse_content(config) @@ -359,6 +364,7 @@ def delete(**kwargs) -> None: DataScienceResourceRun.JOB_RUN in kwargs["ocid"] or DataScienceResourceRun.DATAFLOW_RUN in kwargs["ocid"] or DataScienceResourceRun.PIPELINE_RUN in kwargs["ocid"] + or DataScienceResourceRun.MODEL_DEPLOYMENT in kwargs["ocid"] ): kwargs["run_id"] = kwargs.pop("ocid") elif ( @@ -412,6 +418,46 @@ def watch(**kwargs) -> None: return _BackendFactory(p.config).backend.watch() +def activate(**kwargs) -> None: + """ + Activate a ModelDeployment. + + Parameters + ---------- + kwargs: dict + keyword argument, stores command line args + + Returns + ------- + None + """ + kwargs["run_id"] = kwargs.pop("ocid") + if not kwargs.get("backend"): + kwargs["backend"] = _get_backend_from_run_id(kwargs["run_id"]) + p = ConfigProcessor().step(ConfigMerger, **kwargs) + return _BackendFactory(p.config).backend.activate() + + +def deactivate(**kwargs) -> None: + """ + Deactivate a ModelDeployment. + + Parameters + ---------- + kwargs: dict + keyword argument, stores command line args + + Returns + ------- + None + """ + kwargs["run_id"] = kwargs.pop("ocid") + if not kwargs.get("backend"): + kwargs["backend"] = _get_backend_from_run_id(kwargs["run_id"]) + p = ConfigProcessor().step(ConfigMerger, **kwargs) + return _BackendFactory(p.config).backend.deactivate() + + def init_vscode(**kwargs) -> None: """ Create a .devcontainer.json file for local development. diff --git a/ads/opctl/config/merger.py b/ads/opctl/config/merger.py index 69a682e4b..f055fa516 100644 --- a/ads/opctl/config/merger.py +++ b/ads/opctl/config/merger.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import os @@ -40,7 +40,7 @@ class ConfigMerger(ConfigProcessor): """ def process(self, **kwargs) -> None: - config_string = Template(json.dumps(self.config)).substitute(os.environ) + config_string = Template(json.dumps(self.config)).safe_substitute(os.environ) self.config = json.loads(config_string) # 1. merge and overwrite values from command line args self._merge_config_with_cmd_args(kwargs) diff --git a/ads/opctl/constants.py b/ads/opctl/constants.py index 7b2131e86..cf59e8174 100644 --- a/ads/opctl/constants.py +++ b/ads/opctl/constants.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from enum import Enum @@ -32,4 +32,5 @@ class BACKEND_NAME(Enum): JOB = "job" DATAFLOW = "dataflow" PIPELINE = "pipeline" + MODEL_DEPLOYMENT = "deployment" LOCAL = "local" diff --git a/ads/opctl/distributed/common/abstract_cluster_provider.py b/ads/opctl/distributed/common/abstract_cluster_provider.py index 30076caad..0dbc1e620 100644 --- a/ads/opctl/distributed/common/abstract_cluster_provider.py +++ b/ads/opctl/distributed/common/abstract_cluster_provider.py @@ -34,6 +34,7 @@ class ClusterProvider: The worker and main coordinate cluster configuration using the directory provided via `WORK_DIR`. The main node creates config.json in the `WORK_DIR`. The worker nodes polls the config.json and exports the configuration as environment variables """ + SYNC_SCRIPT_PATH = "/etc/datascience/sync.sh" DEFAULT_CODE_DIR = "/code" @@ -79,18 +80,21 @@ def __init__(self, mode, ephemeral=True, life_span="0h", work_dir=""): def sync(self, loop=True): sync_artifacts = os.environ.get("SYNC_ARTIFACTS", 0) - print(f'sync_artifacts - {sync_artifacts}') + print(f"sync_artifacts - {sync_artifacts}") if sync_artifacts == "1": bkt_name, prefix = self.get_sync_loc() if bkt_name is None: - print("WARNING: Sync 'WORKSPACE', 'WORKSPACE_PREFIX' or 'work_dir' not configured. Skipping Sync") + print( + "WARNING: Sync 'WORKSPACE', 'WORKSPACE_PREFIX' or 'work_dir' not configured. Skipping Sync" + ) return sync_script_fn = self.SYNC_SCRIPT_PATH if not os.path.exists(sync_script_fn): sync_script = self.get_sync_script() self.create_sync_script(sync_script_fn, sync_script) - subprocess.Popen([sync_script_fn, bkt_name, prefix, '-l']) if loop else subprocess.Popen( - [sync_script_fn, bkt_name, prefix]) + subprocess.Popen( + [sync_script_fn, bkt_name, prefix, "-l"] + ) if loop else subprocess.Popen([sync_script_fn, bkt_name, prefix]) if not loop: sleep_duration = int(os.environ.get("POST_PROCESSING_WAIT", 60)) print(f"post processing wait..{sleep_duration} seconds") @@ -100,27 +104,25 @@ def sync(self, loop=True): def get_sync_loc(self): bckt_name = os.environ.get("WORKSPACE") pfx = os.environ.get("WORKSPACE_PREFIX") - if bckt_name is None : - scheme = urlparse( - self.work_dir - ) + if bckt_name is None: + scheme = urlparse(self.work_dir) if scheme.scheme == "oci": bckt_name = scheme.netloc.split("@")[0] pfx = scheme.path - pfx = pfx.strip('//') + pfx = pfx.strip("//") return bckt_name, pfx def profile_cmd(self): - profile = os.environ.get("PROFILE", '0') + profile = os.environ.get("PROFILE", "0") cmd = [] - if profile == '1': + if profile == "1": print("Profiler ON") - cmd = os.environ.get("PROFILE_CMD").split(' ') + cmd = os.environ.get("PROFILE_CMD").split(" ") return cmd @staticmethod - def create_sync_script(sync_script_fn,sync_script): - sync_script_fn_obj = open(sync_script_fn, 'w') + def create_sync_script(sync_script_fn, sync_script): + sync_script_fn_obj = open(sync_script_fn, "w") sync_script_fn_obj.write(sync_script) sync_script_fn_obj.close() os.chmod(sync_script_fn, 755) @@ -148,12 +150,11 @@ def _fetch_git(self, code_dir): secret_ocid = os.environ.get(cluster_config_helper.OCI__RUNTIME_GIT_SECRET_ID) # with GitSSHKey does nothing if secret_ocid is None or empty with GitSSHKey(secret_ocid): - GitManager(uri, code_dir).fetch_repo().setup_code( + GitManager(uri, code_dir=code_dir).fetch_repo().checkout_code( branch=branch, commit=commit ) - JobRunner.setup_python_path( + JobRunner(code_dir=code_dir).setup_python_path( python_path=os.environ.get(cluster_config_helper.OCI__RUNTIME_PYTHON_PATH), - code_dir=code_dir, ) def _fetch_remote(self, code_dir): @@ -280,7 +281,9 @@ def setup_configuration(self, config: dict = None): # tmpdir = self.my_work_dir # self.tmpdir = tmpdir config = config or self.configuration() - print(f"Writing configuration: {config} to file: {self.config_file}", flush=True) + print( + f"Writing configuration: {config} to file: {self.config_file}", flush=True + ) with fsspec.open(self.config_file, "w", **self.authinfo) as conf: conf.write(json.dumps(config)) @@ -408,7 +411,8 @@ def when_ready(self, func, *args, **kwargs): func(*args, **kwargs) -sync_script_str = '''#!/bin/bash + +sync_script_str = """#!/bin/bash loop=false @@ -441,4 +445,4 @@ def when_ready(self, func, *args, **kwargs): fi sleep $sleep_duration done - ''' \ No newline at end of file + """ diff --git a/ads/pipeline/ads_pipeline_run.py b/ads/pipeline/ads_pipeline_run.py index ab3e6df16..82c399307 100644 --- a/ads/pipeline/ads_pipeline_run.py +++ b/ads/pipeline/ads_pipeline_run.py @@ -36,7 +36,7 @@ } LOG_INTERVAL = 3 -SLEEP_INTERVAL = 5 +SLEEP_INTERVAL = 3 MAXIMUM_TIMEOUT_SECONDS = 1800 LOG_RECORDS_LIMIT = 100 ALLOWED_OPERATION_KWARGS = [ @@ -110,6 +110,10 @@ class PipelineRun( Syncs status of Pipeline run. """ + _DETAILS_LINK = ( + "https://console.{region}.oraclecloud.com/data-science/pipeline-runs/{id}" + ) + def __init__( self, config: dict = None, diff --git a/ads/templates/score-pkl.jinja2 b/ads/templates/score-pkl.jinja2 index 5e4ecd9c8..200c20bbc 100644 --- a/ads/templates/score-pkl.jinja2 +++ b/ads/templates/score-pkl.jinja2 @@ -1,6 +1,9 @@ +# score.py {{SCORE_VERSION}} generated by ADS {{ADS_VERSION}} on {{time_created}} import json import os -from cloudpickle import cloudpickle +import cloudpickle +import pandas as pd +import numpy as np from functools import lru_cache {% for import in misc_imports %} import {{import}} @@ -35,40 +38,98 @@ def load_model(model_file_name=model_name): else: raise Exception(f'{model_file_name} is not found in model directory {model_dir}') -def deserialize(data): +@lru_cache(maxsize=1) +def fetch_data_type_from_schema(input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema['schema']: + data_type[col['name']] = col['dtype'] + else: + print("input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.sklearn_model.SklearnModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization.") + return data_type + +def deserialize(data, input_schema_path): """ Deserialize json serialization data to data in original type when sent to predict. Parameters ---------- data: serialized input data. + input_schema_path: path of input schema. Returns ------- data: deserialized input data. """ +{% if data_deserializer == "json" %} + import base64 + from io import BytesIO if isinstance(data, bytes): return data data_type = data.get('data_type', '') if isinstance(data, dict) else '' json_data = data.get('data', data) if isinstance(data, dict) else data + + if "numpy.ndarray" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode('utf-8'))) + return np.load(load_bytes, allow_pickle=True) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=fetch_data_type_from_schema(input_schema_path)) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + return json_data -def pre_inference(data): +{% elif data_deserializer == "cloudpickle" %} + from pickle import UnpicklingError + deserialized_data = data + try: + deserialized_data = cloudpickle.loads(data) + except TypeError: + pass + except UnpicklingError: + logger.warning( + "bytes are passed directly to the model. If the model expects a specific data format, you need to write the conversion logic in `deserialize()` yourself." + ) + + return deserialized_data + +{% else %} + # Add further data deserialization if needed + return data +{% endif %} + +def pre_inference(data, input_schema_path): """ Preprocess data Parameters ---------- data: Data format as expected by the predict API of the core estimator. + input_schema_path: path of input schema. Returns ------- data: Data format after any processing. """ - return deserialize(data) + return deserialize(data, input_schema_path) def post_inference(yhat): """ @@ -83,16 +144,21 @@ def post_inference(yhat): yhat: Data format after any processing. """ + if isinstance(yhat, pd.core.frame.DataFrame): + yhat = yhat.values + if isinstance(yhat, np.ndarray): + yhat = yhat.tolist() return yhat -def predict(data, model=load_model()): +def predict(data, model=load_model(), input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): """ Returns prediction given the model and data to predict Parameters ---------- - model: Model instance returned by load_model API - data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame + model: Model instance returned by load_model API. + data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame. + input_schema_path: path of input schema. Returns ------- @@ -100,7 +166,7 @@ def predict(data, model=load_model()): Format: {'prediction': output from model.predict method} """ - features = pre_inference(data) + features = pre_inference(data, input_schema_path) yhat = post_inference( model.predict(features) ) diff --git a/ads/templates/score_generic.jinja2 b/ads/templates/score_generic.jinja2 index 18e6c6bd5..556e85ce5 100644 --- a/ads/templates/score_generic.jinja2 +++ b/ads/templates/score_generic.jinja2 @@ -1,5 +1,7 @@ +# score.py {{SCORE_VERSION}} generated by ADS {{ADS_VERSION}} on {{time_created}} import os import sys +import json from functools import lru_cache model_name = '{{model_file_name}}' @@ -28,40 +30,101 @@ def load_model(model_file_name=model_name): else: raise Exception(f'{model_file_name} is not found in model directory {model_dir}') -def deserialize(data): +@lru_cache(maxsize=1) +def fetch_data_type_from_schema(input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): + """ + Returns data type information fetch from input_schema.json. + + Parameters + ---------- + input_schema_path: path of input schema. + + Returns + ------- + data_type: data type fetch from input_schema.json. + + """ + data_type = {} + if os.path.exists(input_schema_path): + schema = json.load(open(input_schema_path)) + for col in schema['schema']: + data_type[col['name']] = col['dtype'] + else: + print("input_schema has to be passed in in order to recover the same data type. pass `X_sample` in `ads.model.framework.sklearn_model.SklearnModel.prepare` function to generate the input_schema. Otherwise, the data type might be changed after serialization/deserialization.") + return data_type + +def deserialize(data, input_schema_path): """ Deserialize json serialization data to data in original type when sent to predict. Parameters ---------- data: serialized input data. + input_schema_path: path of input schema. Returns ------- data: deserialized input data. """ +{% if data_deserializer == "json" %} + import pandas as pd + import numpy as np + import base64 + from io import BytesIO if isinstance(data, bytes): return data data_type = data.get('data_type', '') if isinstance(data, dict) else '' json_data = data.get('data', data) if isinstance(data, dict) else data + + if "numpy.ndarray" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode('utf-8'))) + return np.load(load_bytes, allow_pickle=True) + if "pandas.core.series.Series" in data_type: + return pd.Series(json_data) + if "pandas.core.frame.DataFrame" in data_type or isinstance(json_data, str): + return pd.read_json(json_data, dtype=fetch_data_type_from_schema(input_schema_path)) + if isinstance(json_data, dict): + return pd.DataFrame.from_dict(json_data) + return json_data -def pre_inference(data): +{% elif data_deserializer == "cloudpickle" %} + import cloudpickle + from pickle import UnpicklingError + deserialized_data = data + try: + deserialized_data = cloudpickle.loads(data) + except TypeError: + pass + except UnpicklingError: + logger.warning( + "bytes are passed directly to the model. If the model expects a specific data format, you need to write the conversion logic in `deserialize()` yourself." + ) + + return deserialized_data + +{% else %} + # Add further data deserialization if needed + return data +{% endif %} + +def pre_inference(data, input_schema_path): """ Preprocess data Parameters ---------- data: Data format as expected by the predict API of the core estimator. + input_schema_path: path of input schema. Returns ------- data: Data format after any processing. """ - return deserialize(data) + return deserialize(data, input_schema_path) def post_inference(yhat): @@ -79,17 +142,15 @@ def post_inference(yhat): """ return yhat - -def predict(data, model=load_model()): +def predict(data, model=load_model(), input_schema_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "input_schema.json")): """ Returns prediction given the model and data to predict Parameters ---------- - model: Model instance returned by load_model API - data: Data has to be json serialiable, hence, accepted data type includes - only Dict and Str. You need to process the data in order to transform - the data from Dict or str format to the type that the model accepts. + model: Model instance returned by load_model API. + data: Data format as expected by the predict API of the core estimator. For eg. in case of sckit models it could be numpy array/List of list/Pandas DataFrame. + input_schema_path: path of input schema. Returns ------- @@ -97,8 +158,7 @@ def predict(data, model=load_model()): Format: {'prediction': output from model.predict method} """ - - features = pre_inference(data) + features = pre_inference(data, input_schema_path) yhat = post_inference( model.predict(features) ) diff --git a/ads/templates/score_huggingface_pipeline.jinja2 b/ads/templates/score_huggingface_pipeline.jinja2 new file mode 100644 index 000000000..8125a54ac --- /dev/null +++ b/ads/templates/score_huggingface_pipeline.jinja2 @@ -0,0 +1,217 @@ +# score.py {{SCORE_VERSION}} generated by ADS {{ADS_VERSION}} +import os +import sys +from functools import lru_cache +from transformers import pipeline +import json +from typing import Dict, List +import numpy as np +from io import BytesIO +import base64 +import logging +import torch + + +task = '{{task}}' + +""" + Inference script. This script is used for prediction by scoring server when schema is known. +""" + +@lru_cache(maxsize=10) +def load_model(model_file_name=""): + """ + Loads model from the serialized format + + Returns + ------- + model: a model instance on which predict API can be invoked. + + """ + model_dir = os.path.dirname(os.path.realpath(__file__)) + if model_dir not in sys.path: + sys.path.insert(0, model_dir) + + try: + + model = pipeline(task, model = model_dir) + print("Model is successfully loaded.") + except Exception as e: + raise Exception(f'Failed to load the model. ' + str(e)) + + return model + + +def deserialize(data): + """ + Deserialize json-serialized data to data in original type when sent to +predict. + + Parameters + ---------- + data: serialized input data. + + Returns + ------- + data: deserialized input data. + + """ +{% if data_deserializer == "json" %} + if isinstance(data, bytes): + logging.warning( + "bytes are passed directly to the model. If the model expects a specific data format, you need to write the conversion logic in `deserialize()` yourself." + ) + return data + + data_type = data.get('data_type', '') if isinstance(data, dict) else '' + json_data = data.get('data', data) if isinstance(data, dict) else data + + if "numpy.ndarray" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode('utf-8'))) + return np.load(load_bytes, allow_pickle=True) + if "torch.Tensor" in data_type: + load_bytes = BytesIO(base64.b64decode(json_data.encode('utf-8'))) + return torch.load(load_bytes) + if isinstance(json_data, bytes): + import cloudpickle + return cloudpickle.loads(json_data) + + return json_data + +{% elif data_deserializer == "cloudpickle" %} + import cloudpickle + from pickle import UnpicklingError + deserialized_data = data + try: + deserialized_data = cloudpickle.loads(data) + except TypeError: + pass + except UnpicklingError: + logger.warning( + "bytes are passed directly to the model. If the model expects a specific data format, you need to write the conversion logic in `deserialize()` yourself." + ) + + return deserialized_data + +{% else %} + # Add further data deserialization if needed + return data +{% endif %} + +def pre_inference(data): + """ + Preprocess json-serialized data to feed into predict function. + + Parameters + ---------- + data: Data format as expected by the predict API of the core estimator. + + Returns + ------- + data: Data format after any processing. + + """ + data = deserialize(data) + + # Add further data preprocessing if needed + return data + +def post_inference(yhat): + """ + Post-process the model results. + + Parameters + ---------- + yhat: Data format after calling model.predict. + + Returns + ------- + yhat: Data format after any processing. + + """ + if isinstance(yhat, dict): + return serialize_prediction(yhat) + elif isinstance(yhat, list): + serialized_yhat = [] + for yhat_i in yhat: + serialized_yhat.append(serialize_prediction(yhat_i)) + return serialized_yhat + elif isinstance(yhat, torch.Tensor): + return yhat.tolist() + + if task == "conversational": + raise NotImplementedError("The model output of task `conversational` is not json serializable. Add code here to populate json serializable output.") + + # Add further data postprocessing if needed + return yhat + + +def serialize_prediction(yhat): + """ + Serialize the values in the prediction. + + Parameters + ---------- + yhat: model prediction. + + Returns + ------- + yhat: Serialized model prediction. + + """ + if isinstance(yhat, dict): + for key, value in yhat.items(): + if isinstance(value, torch.Tensor): + yhat[key] = value.tolist() + elif str(type(value)).startswith("=3.1.2", "matplotlib>=3.1.3", "numpy>=1.19.2", - "oci>=2.90.3", + "oci>=2.93.1", "ocifs>=1.1.3", "pandas>1.2.1,<1.6", "python_jsonschema_objects>=0.3.13", "PyYAML>=5.4,<6", "requests", - "scikit-learn>=0.23.2", + "scikit-learn>=0.23.2,<1.2", "tabulate>=0.8.9", "tqdm>=4.59.0", "psutil>=5.7.2", @@ -58,7 +58,7 @@ "pandavro>=1.6.0", "datefinder>=0.7.1", "htmllistparse>=0.6.0", - "sqlalchemy>=1.4.1", + "sqlalchemy>=1.4.1, <=1.4.46", "oracledb>=1.0", ], "opctl": [ @@ -72,6 +72,7 @@ "mysql": ["mysql-connector-python"], "bds": ["ibis-framework[impala]", "hdfs[kerberos]", "sqlalchemy"], "spark": ["pyspark>=3.0.0"], + "huggingface": ["transformers"], } this_directory = Path(__file__).parent @@ -153,7 +154,6 @@ def update_extra_with_internal_packages(): "Intended Audience :: Developers", "License :: OSI Approved :: Universal Permissive License (UPL)", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -161,7 +161,7 @@ def update_extra_with_internal_packages(): keywords="Oracle Cloud Infrastructure, OCI, Machine Learning, ML, Artificial Intelligence, AI, Data Science, Cloud, Oracle", include_package_data=True, install_requires=install_requires, - python_requires=">=3.7", + python_requires=">=3.8", setup_requires=setup_requires, extras_require=extras_require, tests_require=[ From cf908c7ae1c5672ccc3069afb83f02bddcb68a2f Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Fri, 3 Mar 2023 09:43:42 -0500 Subject: [PATCH 116/147] Update conf.py to insert correct ads path when building api docs. --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index b04bfff07..99c315fe2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -9,7 +9,7 @@ # This causes documentation within the __init__ method to be pulled into the documentation properly autoclass_content = "both" -sys.path.insert(0, os.path.abspath("../../advanced-ds")) +sys.path.insert(0, os.path.abspath("../../ads")) # -- Project information ----------------------------------------------------- From 33324eea1b093894b2d7c304351a38389d1ed21f Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Fri, 3 Mar 2023 09:49:10 -0500 Subject: [PATCH 117/147] Update toc tree level in ads.jobs.rst. --- docs/source/ads.jobs.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/ads.jobs.rst b/docs/source/ads.jobs.rst index c99a0c55d..b64b62cc2 100644 --- a/docs/source/ads.jobs.rst +++ b/docs/source/ads.jobs.rst @@ -6,11 +6,12 @@ ads.jobs package ads.jobs + Subpackages ----------- .. toctree:: - :maxdepth: 1 + :maxdepth: 3 ads.jobs.builders ads.jobs.schema From a6348e3403bba5a16cb183ea97d277e5f2e9dfee Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Fri, 3 Mar 2023 16:39:11 -0800 Subject: [PATCH 118/147] quick docs --- ads/model/framework/huggingface_model.py | 15 +++-- docs/source/conf.py | 2 +- .../frameworks/huggingfacemodel.rst | 2 +- .../model_registration/quick_start.rst | 62 +++++++++++++++++++ .../model_training/automl/quick_start.rst | 8 +++ 5 files changed, 81 insertions(+), 8 deletions(-) diff --git a/ads/model/framework/huggingface_model.py b/ads/model/framework/huggingface_model.py index 6fbf1a237..4e6232b96 100644 --- a/ads/model/framework/huggingface_model.py +++ b/ads/model/framework/huggingface_model.py @@ -92,8 +92,9 @@ class HuggingFacePipelineModel(FrameworkSpecificModel): verify(data, ...) Tests if deployment works in local environment. - Examples - Image Classification - ------------------------------- + Examples + -------- + >>> # Image Classification >>> from transformers import pipeline >>> import tempfile >>> import PIL.Image @@ -133,8 +134,9 @@ class HuggingFacePipelineModel(FrameworkSpecificModel): >>> headers = {"Content-Type": "application/octet-stream"} >>> requests.post(endpoint, data=image_bytes, auth=auth, headers=headers).json() - Examples - Image Segmentation - ----------------------------- + Examples + -------- + >>> # Image Segmentation >>> from transformers import pipeline >>> import tempfile >>> import PIL.Image @@ -177,8 +179,9 @@ class HuggingFacePipelineModel(FrameworkSpecificModel): >>> headers = {"Content-Type": "application/octet-stream"} >>> requests.post(endpoint, data=image_bytes, auth=auth, headers=headers).json() - Examples - Zero Shot Image Classification - ----------------------------------------- + Examples + -------- + >>> # Zero Shot Image Classification >>> from transformers import pipeline >>> import tempfile >>> import PIL.Image diff --git a/docs/source/conf.py b/docs/source/conf.py index 99c315fe2..50ac59b00 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -9,7 +9,7 @@ # This causes documentation within the __init__ method to be pulled into the documentation properly autoclass_content = "both" -sys.path.insert(0, os.path.abspath("../../ads")) +sys.path.insert(0, os.path.abspath("../..")) # -- Project information ----------------------------------------------------- diff --git a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst index a68bf515e..6a5f9cd94 100644 --- a/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/huggingfacemodel.rst @@ -219,7 +219,7 @@ Example zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=tempfile.mkdtemp()) # Autogenerate score.py, serialized model, runtime.yaml - conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" + conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" # your published conda pack python_version = "3.x" # Remember to update 3.x with your actual python version, e.g. 3.8 zero_shot_image_classification_model.prepare(inference_conda_env=conda_pack_path, inference_python_version = python_version, force_overwrite=True) diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index a48324488..1752eff5a 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -278,6 +278,68 @@ Create a model, prepare it, verify that it works, save it to the model catalog, model_id = tf_model.save(display_name="TensorFlow Model") +HuggingFace Pipelines +--------------------- + +.. code-block:: python3 + + from transformers import pipeline + from ads.model import HuggingFacePipelineModel + + import tempfile + import PIL.Image + from ads.common.auth import default_signer + import requests + import cloudpickle + + ## download the image + image_url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png" + image = PIL.Image.open(requests.get(image_url, stream=True).raw) + + ## download the pretrained model + classifier = pipeline(model="openai/clip-vit-large-patch14") + classifier( + images=image, + candidate_labels=["animals", "humans", "landscape"], + ) + + ## Initiate a HuggingFacePipelineModel instance + zero_shot_image_classification_model = HuggingFacePipelineModel(classifier, artifact_dir=tempfile.mkdtemp()) + + # Autogenerate score.py, serialized model, runtime.yaml + conda_pack_path = "oci://bucket@namespace/path/to/conda/pack" + python_version = "3.x" # Remember to update 3.x with your actual python version, e.g. 3.8 + zero_shot_image_classification_model.prepare(inference_conda_env=conda_pack_path, inference_python_version = python_version, force_overwrite=True) + + ## Convert payload to bytes + data = {"images": image, "candidate_labels": ["animals", "humans", "landscape"]} + body = cloudpickle.dumps(data) # convert image to bytes + + # Verify generated artifacts + zero_shot_image_classification_model.verify(data=data) + zero_shot_image_classification_model.verify(data=body) + + # Register HuggingFace Pipeline model + zero_shot_image_classification_model.save() + + ## Deploy + log_group_id = "" + log_id = "" + zero_shot_image_classification_model.deploy(deployment_bandwidth_mbps=100, + wait_for_completion=False, + deployment_log_group_id = log_group_id, + deployment_access_log_id = log_id, + deployment_predict_log_id = log_id) + zero_shot_image_classification_model.predict(data) + zero_shot_image_classification_model.predict(body) + + ### Invoke the model by sending bytes + auth = default_signer()['signer'] + endpoint = zero_shot_image_classification_model.model_deployment.url + "/predict" + headers = {"Content-Type": "application/octet-stream"} + requests.post(endpoint, data=body, auth=auth, headers=headers).json() + + Other Frameworks ---------------- diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst index a5d28e26b..70284f2f2 100644 --- a/docs/source/user_guide/model_training/automl/quick_start.rst +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -1,3 +1,9 @@ +Prerequisites +============= + +You need to check if AutoMLx library is installed in your environment. For more information on the conda environments that contain AutoMLx, check `this page `__. + + Quick Start =========== @@ -6,6 +12,8 @@ The dataset is a multi-class classification dataset, and more details about the can be found at `Iris dataset `_. We demonstrate the preliminary steps required to train a model with the Oracle AutoMLx tool. We then explain the tuned model. + + Load dataset ------------ We start by reading in the dataset from Scikit-learn. From 9a3c75229a54cb5366c62e704028e532911aae96 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Fri, 3 Mar 2023 20:49:38 -0800 Subject: [PATCH 119/147] fix prereq --- .../user_guide/model_training/automl/quick_start.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/source/user_guide/model_training/automl/quick_start.rst b/docs/source/user_guide/model_training/automl/quick_start.rst index 70284f2f2..d32cd2390 100644 --- a/docs/source/user_guide/model_training/automl/quick_start.rst +++ b/docs/source/user_guide/model_training/automl/quick_start.rst @@ -1,12 +1,10 @@ -Prerequisites -============= - -You need to check if AutoMLx library is installed in your environment. For more information on the conda environments that contain AutoMLx, check `this page `__. - - Quick Start =========== +.. admonition:: Prerequisite + + You need to check if AutoMLx library is installed in your environment. For more information on the conda environments that contain AutoMLx, check `this page `__. + This section provides a quick introduction to build a classifier using the Oracle AutoMLx tool for Iris dataset. The dataset is a multi-class classification dataset, and more details about the dataset can be found at `Iris dataset `_. We demonstrate From 67336aac155ae6c564465a2992e3610f2833d77f Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:32:53 -0800 Subject: [PATCH 120/147] Update delete.rst Fixed wording --- docs/source/user_guide/model_deployment/delete.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_deployment/delete.rst b/docs/source/user_guide/model_deployment/delete.rst index ec418a087..bd0ee1a01 100644 --- a/docs/source/user_guide/model_deployment/delete.rst +++ b/docs/source/user_guide/model_deployment/delete.rst @@ -5,7 +5,7 @@ A model deployment can be deleted using a ``ModelDeployment`` object. When a model deployment is deleted, it deletes the load balancer instances associated with it. However, it doesn't delete other resources like log group, log, or model. -If you have a ``ModelDeployment`` object, you can use the ``.delete()`` method to delete the model that is associated with that object. The optional ``wait_for_completion`` parameter accepts a Boolean and determines if the process is blocking or not. +If you have a ``ModelDeployment`` object, you can use the ``.delete()`` method to delete the model that is associated with that object. The optional ``wait_for_completion`` parameter accepts a boolean value and determines if the process is blocking or not. In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. From 075b84b23d96747454a5460de39f98e23d8e5b7f Mon Sep 17 00:00:00 2001 From: John DeSanto <202220+jdesanto@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:17:28 -0800 Subject: [PATCH 121/147] Update logs.rst Fixed case --- docs/source/user_guide/model_deployment/logs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_deployment/logs.rst b/docs/source/user_guide/model_deployment/logs.rst index ec52149bd..3e44e53fd 100644 --- a/docs/source/user_guide/model_deployment/logs.rst +++ b/docs/source/user_guide/model_deployment/logs.rst @@ -1,7 +1,7 @@ Logs **** -The model deployment process creates a set of workflow logs. Optionally, you can also configure the Logging service to capture access and predict logs. +The model deployment process creates a set of workflow logs. Optionally, you can also configure the logging service to capture access and predict logs. In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. From aaa35f53b14e980f0c381883d6ab5f0bb18ccb06 Mon Sep 17 00:00:00 2001 From: Lu Peng Date: Tue, 7 Mar 2023 14:18:51 -0800 Subject: [PATCH 122/147] Updated pr. --- .../access_a_model_deployment.rst | 13 - .../user_guide/model_deployment/accessing.rst | 19 -- .../user_guide/model_deployment/activate.rst | 13 + .../model_deployment/deactivate.rst | 13 + .../user_guide/model_deployment/delete.rst | 4 +- .../user_guide/model_deployment/deploy.rst | 239 +++++++++--------- .../user_guide/model_deployment/index.rst | 8 +- .../user_guide/model_deployment/inventory.rst | 66 ----- .../user_guide/model_deployment/list.rst | 64 +++++ .../user_guide/model_deployment/logs.rst | 10 + .../user_guide/model_deployment/overview.rst | 19 -- .../user_guide/model_deployment/start.rst | 148 +++++++++++ .../user_guide/model_deployment/state.rst | 30 --- .../user_guide/model_deployment/update.rst | 3 +- 14 files changed, 371 insertions(+), 278 deletions(-) delete mode 100644 docs/source/user_guide/model_deployment/access_a_model_deployment.rst delete mode 100644 docs/source/user_guide/model_deployment/accessing.rst create mode 100644 docs/source/user_guide/model_deployment/activate.rst create mode 100644 docs/source/user_guide/model_deployment/deactivate.rst delete mode 100644 docs/source/user_guide/model_deployment/inventory.rst create mode 100644 docs/source/user_guide/model_deployment/list.rst delete mode 100644 docs/source/user_guide/model_deployment/overview.rst create mode 100644 docs/source/user_guide/model_deployment/start.rst delete mode 100644 docs/source/user_guide/model_deployment/state.rst diff --git a/docs/source/user_guide/model_deployment/access_a_model_deployment.rst b/docs/source/user_guide/model_deployment/access_a_model_deployment.rst deleted file mode 100644 index 26ea32e6c..000000000 --- a/docs/source/user_guide/model_deployment/access_a_model_deployment.rst +++ /dev/null @@ -1,13 +0,0 @@ -Access a Model Deployment -************************* - -When a model is deployed the ``.deploy()`` method of the ``ModelDeployment`` class will return an updated ``ModelDeployment`` object. This object can be used to interact with the actual model deployment. However, if the model has already been deployed, it is possible to obtain a ``ModelDeployment`` object. Use the ``.from_id()`` method when the model deployment OCID is known. - -The next code snippet creates a new ``ModelDeployment`` object that has access to the created model deployment. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployment - - existing_deployment = ModelDeployment.from_id(id="") - diff --git a/docs/source/user_guide/model_deployment/accessing.rst b/docs/source/user_guide/model_deployment/accessing.rst deleted file mode 100644 index 64af7de9c..000000000 --- a/docs/source/user_guide/model_deployment/accessing.rst +++ /dev/null @@ -1,19 +0,0 @@ -Accessing -========= - -When a model is deployed the ``.deploy()`` method of the ``ModelDeployer`` class will return a ``ModelDeployment`` object. This object can be used to interact with the actual model deployment. However, if the model has already been deployed, it is possible to obtain a ``ModelDeployment`` object. Use the ``.get_model_deployment()`` method when the model deployment OCID is known. - -The next code snippet creates a new ``ModelDeployment`` object that has access -to the created model deployment. - -.. code:: ipython3 - - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - existing_deployment = deployer.get_model_deployment(model_deployment_id="") - - - - - diff --git a/docs/source/user_guide/model_deployment/activate.rst b/docs/source/user_guide/model_deployment/activate.rst new file mode 100644 index 000000000..157d4699c --- /dev/null +++ b/docs/source/user_guide/model_deployment/activate.rst @@ -0,0 +1,13 @@ +Activate +******** + +An inactive model deployment can be activated using a ``ModelDeployment`` object. See `Activate Model Deployments `_ for more details. + +If you have a ``ModelDeployment`` object, you can use the ``.activate()`` method to activate the model that is associated with that object. + +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. + +.. code-block:: python3 + + deployment.activate(wait_for_completion=True) + diff --git a/docs/source/user_guide/model_deployment/deactivate.rst b/docs/source/user_guide/model_deployment/deactivate.rst new file mode 100644 index 000000000..744b7fd01 --- /dev/null +++ b/docs/source/user_guide/model_deployment/deactivate.rst @@ -0,0 +1,13 @@ +Deactivate +********** + +An activate model deployment can be deactivated using a ``ModelDeployment`` object. Deactivating a model deployment shuts down the instances that are associated with your deployment. See `Deactivate Model Deployments `_ for more details. + +If you have a ``ModelDeployment`` object, you can use the ``.deactivate()`` method to deactivate the model that is associated with that object. + +In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. + +.. code-block:: python3 + + deployment.deactivate(wait_for_completion=True) + diff --git a/docs/source/user_guide/model_deployment/delete.rst b/docs/source/user_guide/model_deployment/delete.rst index ec418a087..9d7c85a8f 100644 --- a/docs/source/user_guide/model_deployment/delete.rst +++ b/docs/source/user_guide/model_deployment/delete.rst @@ -5,11 +5,11 @@ A model deployment can be deleted using a ``ModelDeployment`` object. When a model deployment is deleted, it deletes the load balancer instances associated with it. However, it doesn't delete other resources like log group, log, or model. -If you have a ``ModelDeployment`` object, you can use the ``.delete()`` method to delete the model that is associated with that object. The optional ``wait_for_completion`` parameter accepts a Boolean and determines if the process is blocking or not. +If you have a ``ModelDeployment`` object, you can use the ``.delete()`` method to delete the model that is associated with that object. In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. .. code-block:: python3 - deployment = deployment.delete(wait_for_completion=True) + deployment.delete(wait_for_completion=True) diff --git a/docs/source/user_guide/model_deployment/deploy.rst b/docs/source/user_guide/model_deployment/deploy.rst index b04a41133..6e4263330 100644 --- a/docs/source/user_guide/model_deployment/deploy.rst +++ b/docs/source/user_guide/model_deployment/deploy.rst @@ -1,37 +1,27 @@ Deploy ****** -The ``.deploy()`` method of the ``ModelDeployment`` class is used to create a model deployment. It has the following parameters: +To deploy a model deployment, you'll need to define a ``ModelDeployment`` object and call the ``.deploy()`` of it. You could either use API or YAML to define the ``ModelDeployment`` object. - * ``max_wait_time``: The timeout limit, in seconds, for the deployment process to wait until it is active. Defaults to 1200 seconds. - * ``poll_interval``: The interval between checks of the deployment status in seconds. Defaults to 30 seconds. - * ``wait_for_completion``: Blocked process until the deployment has been completed. Defaults to ``True``. +Deploy a ModelDeployment on Docker Container Runtime +==================================================== -To deploy a ``ModelDeployment``, you need to define a ``ModelDeployment`` object first and then call ``.deploy()``. There are two ways to define a ``ModelDeployment`` object. +The ADS ``ModelDeploymentContainerRuntime`` class allows you to run a container image using OCI data science model deployment. -Builder Pattern -=============== +To use the ``ModelDeploymentContainerRuntime``, you need to first build a docker container image. See `` for the end-to-end example. Once you have the image, push it to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. -Infrastructure --------------- +To configure ``ModelDeploymentContainerRuntime``, you must specify the container ``image``. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). -You define the model deployment infrastructure by passing the following properties to ``ModelDeploymentInfrastructure``: +Below is an example of deploying model on docker container runtime: - * ``access_log``: Log group OCID and log OCID for the access logs. - * ``bandwidth_mbps``: The bandwidth limit on the load balancer in Mbps. - * ``compartment_id``: Compartment OCID that the model deployment belongs to. - * ``replica``: The number of instances to deploy. - * ``shape_name``: The instance shape name to use. For example, "VM.Standard.E4.Flex". - * ``shape_config_details``: The instance shape configure details to use if flexible shape is selected for ``shape_name``. - * ``predict_log``: Log group OCID and log OCID for the predict logs. - * ``project_id``: Project OCID that the model deployment will belong to. - * ``web_concurrency``: The web concurrency to use. +.. tabs:: -Below is an example to define a ``ModelDeploymentInfrastructure`` object - -.. code-block:: python3 + .. code-tab:: Python3 + :caption: Python from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure + from ads.model.deployment.model_deployment_runtime import ModelDeploymentContainerRuntime + from ads.model.deployment import ModelDeployment infrastructure = ( ModelDeploymentInfrastructure() @@ -55,40 +45,6 @@ Below is an example to define a ``ModelDeploymentInfrastructure`` object ) ) - -Runtime -------- - -The Data Science Model Deployment supports service managed conda runtime and customized docker container runtime. - - * ``ModelDeploymentContainerRuntime`` allows you to deploy model deployment on customized docker container runtime. - * ``ModelDeploymentCondaRuntime`` allows you to deploy model deployment on service-managed conda runtime. - -ModelDeploymentContainerRuntime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the ``ModelDeploymentContainerRuntime``, you need to first push the image to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. - -You can define the model deployment container runtime by passing the following properties to ``ModelDeploymentContainerRuntime`` object: - - * ``model_uri``: The model ocid or path to model artifacts directory that is used in the model deployment. - * ``deployment_mode``: The mode of model deployment. Allowed deployment modes are ``HTTPS_ONLY`` and ``STREAM_ONLY``. Optional. - * ``input_stream_ids``: The input stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. - * ``output_stream_ids``: The output stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. - * ``env``: The environment variables. Optional. - * ``image``: The full path of docker container image to the OCIR registry. The acceptable formats are: ``.ocir.io//:`` and ``.ocir.io//:@digest``. Required. - * ``image_digest``: The docker container image digest. Optional. - * ``entrypoint``: The entrypoint to docker container image. Optional. - * ``server_port``: The server port of docker container image. Optional. - * ``health_check_port``: The health check port of docker container image. Optional. - * ``cmd``: The additional commands to docker container image. The commands can be args to the entrypoint or the only command to execute in the absence of an entrypoint. Optional. - -Below is an example to define a ``ModelDeploymentContainerRuntime`` object - -.. code-block:: python3 - - from ads.model.deployment.model_deployment_runtime import ModelDeploymentContainerRuntime - container_runtime = ( ModelDeploymentContainerRuntime() .with_image("") @@ -101,49 +57,111 @@ Below is an example to define a ``ModelDeploymentContainerRuntime`` object .with_model_uri("") ) + deployment = ( + ModelDeployment() + .with_display_name("Model Deployment Demo using ADS") + .with_description("The model deployment description") + .with_freeform_tags({"key1":"value1"}) + .with_infrastructure(infrastructure) + .with_runtime(container_runtime) + ) -ModelDeploymentCondaRuntime -~~~~~~~~~~~~~~~~~~~~~~~~~~~ + deployment.deploy() -You can define the model deployment conda runtime by passing the following properties to ``ModelDeploymentCondaRuntime`` object: + .. code-tab:: Python3 + :caption: YAML - * ``model_uri``: The model ocid or path to model artifacts that is used in the model deployment. - * ``deployment_mode``: The deployment mode. The allowed deployment modes are ``HTTPS_ONLY`` and ``STREAM_ONLY``. Optional. - * ``input_stream_ids``: The input stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. - * ``output_stream_ids``: The output stream ids for model deployment. Required when deployment mode is ``STREAM_ONLY``. - * ``env``: The environment variables. Optional. + from ads.model.deployment import ModelDeployment -Below is an example to define a ``ModelDeploymentCondaRuntime`` object + yaml_string = """ + kind: deployment + spec: + displayName: Model Deployment Demo using ADS + description: The model deployment description + freeform_tags: + key1: value1 + infrastructure: + kind: infrastructure + type: datascienceModelDeployment + spec: + compartmentId: + projectId: + accessLog: + logGroupId: + logId: + predictLog: + logGroupId: + logId: + shapeName: VM.Standard.E4.Flex + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + replica: 1 + bandWidthMbps: 10 + webConcurrency: 10 + runtime: + kind: runtime + type: container + spec: + modelUri: + image: + imageDigest: + entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] + serverPort: 5000 + healthCheckPort: 5000 + env: + WEB_CONCURRENCY: "10" + deploymentMode: HTTPS_ONLY + """ -.. code-block:: python3 + deployment = ModelDeployment.from_yaml(yaml_string) + deployment.deploy() - from ads.model.deployment.model_deployment_runtime import ModelDeploymentCondaRuntime - conda_runtime = ( - ModelDeploymentCondaRuntime() - .with_env({"key":"value"}) - .with_deployment_mode("HTTPS_ONLY") - .with_model_uri("") - ) +Deploy a ModelDeployment on Conda Runtime +========================================= +To deploy a model deployment on conda runtime, you need to configure ``ModelDeploymentCondaRuntime``. -ModelDeployment -~~~~~~~~~~~~~~~ +Below is an example of deploying model on conda runtime: -You can define the model deployment by passing the following properties to ``ModelDeployment`` object: +.. tabs:: - * ``defined_tags``: A dictionary of defined tags to be attached to the model deployment. Optional. - * ``description``: A description of the model deployment. Optional. - * ``display_name``: A name that identifies the model deployment in the Console. - * ``freeform_tags``: A dictionary of freeform tags to be attached to the model deployment. Optional. - * ``runtime``: The runtime configuration to be attached to the model deployment. - * ``infrastructure``: The infrastructure configuration to be attached to the model deployment. + .. code-tab:: Python3 + :caption: Python -Below is an example to define and deploy a ``ModelDeployment`` object with custom docker container runtime + from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure + from ads.model.deployment.model_deployment_runtime import ModelDeploymentCondaRuntime + from ads.model.deployment import ModelDeployment -.. code-block:: python3 + infrastructure = ( + ModelDeploymentInfrastructure() + .with_project_id("") + .with_compartment_id("") + .with_shape_name("VM.Standard.E4.Flex") + .with_shape_config_details( + ocpus=1, + memory_in_gbs=16 + ) + .with_replica(1) + .with_bandwidth_mbps(10) + .with_web_concurrency(10) + .with_access_log( + log_group_id="", + log_id="" + ) + .with_predict_log( + log_group_id="", + log_id="" + ) + ) - from ads.model.deployment import ModelDeployment + conda_runtime = ( + ModelDeploymentCondaRuntime() + .with_env({"key":"value"}) + .with_deployment_mode("HTTPS_ONLY") + .with_model_uri("") + ) deployment = ( ModelDeployment() @@ -151,44 +169,17 @@ Below is an example to define and deploy a ``ModelDeployment`` object with custo .with_description("The model deployment description") .with_freeform_tags({"key1":"value1"}) .with_infrastructure(infrastructure) - .with_runtime(container_runtime) + .with_runtime(conda_runtime) ) - deployment.deploy(wait_for_completion=False) - - -YAML Serialization -================== - -A ``ModelDeployment`` object can be serialized to a YAML file by calling ``to_yaml()``, which returns the YAML as a string. You can easily share the YAML with others, and reload the configurations by calling ``from_yaml()``. The ``to_yaml()`` and ``from_yaml()`` methods also take an optional ``uri`` argument for saving and loading the YAML file. This argument can be any URI to the file location supported by `fsspec `__, including Object Storage. For example: - -.. code-block:: python3 - - # Save the model deployment configurations to YAML file - deployment.to_yaml(uri="oci://bucket_name@namespace/path/to/deployment.yaml") - - # Load the model deployment configurations from YAML file - deployment = ModelDeployment.from_yaml(uri="oci://bucket_name@namespace/path/to/deployment.yaml") - - # Save the model deployment configurations to YAML in a string - yaml_string = ModelDeployment.to_yaml() + deployment.deploy() - # Load the model deployment configurations from a YAML string - deployment = ModelDeployment.from_yaml(""" - kind: deployment - spec: - infrastructure: - kind: infrastructure - ... - """") - - deployment.deploy(wait_for_completion=False) + .. code-tab:: Python3 + :caption: YAML -Here is an example of a YAML file representing the ``ModelDeployment`` with docker container runtime defined in the preceding examples: - - -.. code-block:: yaml + from ads.model.deployment import ModelDeployment + yaml_string = """ kind: deployment spec: displayName: Model Deployment Demo using ADS @@ -213,19 +204,19 @@ Here is an example of a YAML file representing the ``ModelDeployment`` with dock ocpus: 1 replica: 1 bandWidthMbps: 10 + webConcurrency: 10 runtime: kind: runtime - type: container + type: conda spec: modelUri: - image: - imageDigest: - entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] - serverPort: 5000 - healthCheckPort: 5000 env: WEB_CONCURRENCY: "10" deploymentMode: HTTPS_ONLY + """ + + deployment = ModelDeployment.from_yaml(yaml_string) + deployment.deploy() **ADS ModelDeployment YAML schema** diff --git a/docs/source/user_guide/model_deployment/index.rst b/docs/source/user_guide/model_deployment/index.rst index d2f53a003..362c0db31 100644 --- a/docs/source/user_guide/model_deployment/index.rst +++ b/docs/source/user_guide/model_deployment/index.rst @@ -6,15 +6,15 @@ Model Deployment .. toctree:: :maxdepth: 1 - overview - access_a_model_deployment + start + activate attributes + deactivate delete deploy - inventory + list logs predict properties - state update diff --git a/docs/source/user_guide/model_deployment/inventory.rst b/docs/source/user_guide/model_deployment/inventory.rst deleted file mode 100644 index f51866ab4..000000000 --- a/docs/source/user_guide/model_deployment/inventory.rst +++ /dev/null @@ -1,66 +0,0 @@ -Inventory -********* - -List -==== - -The ``.list_deployments()`` method of the ``ModelDeployer`` class returns a list of ``ModelDeployment`` objects. The optional ``compartment_id`` parameter limits the search to a specific compartment. By default, it uses the same compartment that the notebook is in. The optional ``status`` parameter limits the returned ``ModelDeployment`` objects to those model deployments that have the specified status. Values for the ``status`` parameter would be 'ACTIVE', 'INACTIVE', or 'FAILED'. - -The code snippet obtains a list of active deployments in the compartment specified by ``compartment_id``, and prints the display name. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - for active in deployer.list_deployments(status="ACTIVE", compartment_id=compartment_id): - print(active.properties.display_name) - -Show -==== - -The ``.show_deployments()`` method is a helper function that works the same way as the ``.list_deployments()`` method except it returns a dataframe of the results. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - deployer.show_deployments(compartment_id=compartment_id, status="ACTIVE") - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - -
model_iddeployment_urlcurrent_state
0ocid1.datasciencemodeldeployment..&l;tunique_ID>https://modeldeployment.us-ashburn-1...ACTIVE
-
- diff --git a/docs/source/user_guide/model_deployment/list.rst b/docs/source/user_guide/model_deployment/list.rst new file mode 100644 index 000000000..83f9e2c3d --- /dev/null +++ b/docs/source/user_guide/model_deployment/list.rst @@ -0,0 +1,64 @@ +Inventory +********* + +List +==== + +The ``.list()`` method of the ``ModelDeployment`` class returns a list of ``ModelDeployment`` objects. + +The code snippet obtains a list of active deployments in the compartment specified by ``compartment_id``, and prints the display name. + +.. code-block:: python3 + + from ads.model.deployment import ModelDeployment + + for active in ModelDeployment.list(status="ACTIVE", compartment_id=compartment_id): + print(active.display_name) + +Show +==== + +The ``.list_df()`` method is a helper function that works the same way as the ``.list()`` method except it returns a dataframe of the results. + +.. code-block:: python3 + + from ads.model.deployment import ModelDeployment + + ModelDeployment.list_df(compartment_id=compartment_id, status="ACTIVE") + +.. raw:: html + +
+ + + + + + + + + + + + + + + + + + +
deployment_iddeployment_urlcurrent_state
0ocid1.datasciencemodeldeployment..&l;tunique_ID>https://modeldeployment.us-ashburn-1...ACTIVE
+
+ diff --git a/docs/source/user_guide/model_deployment/logs.rst b/docs/source/user_guide/model_deployment/logs.rst index ec52149bd..abe91b4d7 100644 --- a/docs/source/user_guide/model_deployment/logs.rst +++ b/docs/source/user_guide/model_deployment/logs.rst @@ -30,4 +30,14 @@ This method returns a dataframe where each row represents a log entry. deployment.show_logs(log_type="access", limit=10) +``Watch`` +--------- + +You can stream the predict and access log of a model deployment using the ``.watch()`` method of a ``ModelDeployment`` object. + +.. code-block:: python3 + + deployment.watch() # stream predict and access log + deployment.watch(log_type="access") # stream access log + diff --git a/docs/source/user_guide/model_deployment/overview.rst b/docs/source/user_guide/model_deployment/overview.rst deleted file mode 100644 index 5520d86f3..000000000 --- a/docs/source/user_guide/model_deployment/overview.rst +++ /dev/null @@ -1,19 +0,0 @@ -Overview -******** - -Model deployments are a managed resource within the Oracle Cloud Infrastructure (OCI) Data Science service. They allow you to deploy machine learning models as web applications (HTTP endpoints). They provide real-time predictions and enables you to quickly productionalize your models. - -The ``ads.model.deployment`` module allows you to deploy models using the Data Science service. This module is built on top of the ``oci`` Python SDK. It is designed to simplify data science workflows. - -A `model artifact `__ is a ZIP archive of the files necessary to deploy your model. The model artifact contains the `score.py `__ file. This file has the Python code that is used to load the model and perform predictions. The model artifact also contains the `runtime.yaml `__ file. This file is used to define the conda environment used by the model deployment. - -ADS supports deploying a model artifact from the Data Science `model catalog `__, or the URI of a directory that can be in the local block storage or in Object Storage. - -You can integrate model deployments with the `OCI Logging service `__. The system allows you to store access and prediction logs ADS provides APIs to simplify the interaction with the Logging service, see -`ADS Logging <../logging/logging.html>`__. - -The ``ads.model.deployment`` module provides the following classes, which are used to deploy and manage the model. - -* ``ModelDeployer``: It creates a new deployment. It is also used to delete, list, and update existing deployments. -* ``ModelDeployment``: Encapsulates the information and actions for an existing deployment. - diff --git a/docs/source/user_guide/model_deployment/start.rst b/docs/source/user_guide/model_deployment/start.rst new file mode 100644 index 000000000..d5fae2b36 --- /dev/null +++ b/docs/source/user_guide/model_deployment/start.rst @@ -0,0 +1,148 @@ +Quick Start +*********** + +Model deployments are a managed resource within the Oracle Cloud Infrastructure (OCI) Data Science service. They allow you to deploy machine learning models as web applications (HTTP endpoints). They provide real-time predictions and enables you to quickly productionalize your models. + +The ``ads.model.deployment`` module allows you to deploy models using the Data Science service. This module is built on top of the ``oci`` Python SDK. It is designed to simplify data science workflows. + +A `model artifact `__ is a ZIP archive of the files necessary to deploy your model. The model artifact contains the `score.py `__ file. This file has the Python code that is used to load the model and perform predictions. The model artifact also contains the `runtime.yaml `__ file. This file is used to define the conda environment used by the model deployment. + +ADS supports deploying a model artifact from the Data Science `model catalog `__, or the URI of a directory that can be in the local block storage or in Object Storage. + +You can integrate model deployments with the `OCI Logging service `__. The system allows you to store access and prediction logs ADS provides APIs to simplify the interaction with the Logging service, see +`ADS Logging <../logging/logging.html>`__. + +The ``ads.model.deployment`` module provides the ``ModelDeployment`` class, which is used to deploy and manage the model. Checkout the example below to learn how to deploy models. + +Example +======= + +.. tabs:: + + .. code-tab:: Python3 + :caption: Python + + from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure + from ads.model.deployment.model_deployment_runtime import ModelDeploymentContainerRuntime + from ads.model.deployment import ModelDeployment + + infrastructure = ( + ModelDeploymentInfrastructure() + .with_project_id("") + .with_compartment_id("") + .with_shape_name("VM.Standard.E4.Flex") + .with_shape_config_details( + ocpus=1, + memory_in_gbs=16 + ) + .with_replica(1) + .with_bandwidth_mbps(10) + .with_web_concurrency(10) + .with_access_log( + log_group_id="", + log_id="" + ) + .with_predict_log( + log_group_id="", + log_id="" + ) + ) + + container_runtime = ( + ModelDeploymentContainerRuntime() + .with_image("") + .with_image_digest("") + .with_entrypoint(["python","/opt/ds/model/deployed_model/api.py"]) + .with_server_port(5000) + .with_health_check_port(5000) + .with_env({"key":"value"}) + .with_deployment_mode("HTTPS_ONLY") + .with_model_uri("") + ) + + deployment = ( + ModelDeployment() + .with_display_name("Model Deployment Demo using ADS") + .with_description("The model deployment description") + .with_freeform_tags({"key1":"value1"}) + .with_infrastructure(infrastructure) + .with_runtime(container_runtime) + ) + + deployment.deploy() # deploy model + deployment.with_display_name("Updated name").update() # update deployment with new name + deployment.predict(data={"line" : "12"}) # predict + deployment.watch(log_type="access") # stream the access log of deployment + deployment.delete() # delete the model deployment + + .. code-tab:: Python3 + :caption: YAML + + from ads.model.deployment import ModelDeployment + + yaml_string = """ + kind: deployment + spec: + displayName: Model Deployment Demo using ADS + description: The model deployment description + freeform_tags: + key1: value1 + infrastructure: + kind: infrastructure + type: datascienceModelDeployment + spec: + compartmentId: + projectId: + accessLog: + logGroupId: + logId: + predictLog: + logGroupId: + logId: + shapeName: VM.Standard.E4.Flex + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + replica: 1 + bandWidthMbps: 10 + webConcurrency: 10 + runtime: + kind: runtime + type: container + spec: + modelUri: + image: + imageDigest: + entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] + serverPort: 5000 + healthCheckPort: 5000 + env: + WEB_CONCURRENCY: "10" + deploymentMode: HTTPS_ONLY + """ + + deployment = ModelDeployment.from_yaml(yaml_string) + deployment.deploy() # deploy model + deployment.with_display_name("Updated name").update() # update deployment with new name + deployment.predict(data={"line" : "12"}) # predict + deployment.watch(log_type="access") # stream the access log of deployment + deployment.delete() # delete the model deployment + + +################ +Model Deployment +################ + +.. toctree:: + :maxdepth: 1 + + activate + attributes + deactivate + delete + deploy + list + logs + predict + properties + update diff --git a/docs/source/user_guide/model_deployment/state.rst b/docs/source/user_guide/model_deployment/state.rst deleted file mode 100644 index d99632b96..000000000 --- a/docs/source/user_guide/model_deployment/state.rst +++ /dev/null @@ -1,30 +0,0 @@ -State -***** - -``ModelDeployer`` -================= - -The ``.get_model_deployment_state()`` method of the ``ModelDeployer`` class accepts a model deployment OCID and returns an ``enum`` state. This is a convenience method to obtain the model deployment state when the model deployment OCID is known. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployer - - deployer = ModelDeployer() - deployer.get_model_deployment_state(model_deployment_id="").name - -.. parsed-literal:: - - 'ACTIVE' - -``ModelDeployment`` -=================== - -You can determine the state of the model deployment using the ``current_state.name`` attribute of a ``ModelDeployment`` object. This returns a string with values like 'ACTIVE', 'INACTIVE', and 'FAILED'. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -.. code-block:: python3 - - deployment.current_state.name - diff --git a/docs/source/user_guide/model_deployment/update.rst b/docs/source/user_guide/model_deployment/update.rst index 59dde30cb..af187941e 100644 --- a/docs/source/user_guide/model_deployment/update.rst +++ b/docs/source/user_guide/model_deployment/update.rst @@ -15,6 +15,7 @@ Or, you could update the instance shape with: .. code-block:: python3 - deployment.infrastructure.with_shape_name("VM.Standard.E4.Flex").with_shape_config_details(ocpus=2, memory_in_gbs=32) + deployment.infrastructure.with_shape_name("VM.Standard.E4.Flex") + deployment.with_shape_config_details(ocpus=2, memory_in_gbs=32) deployment.update() From 491973f8637c56533843d869335b0bfdfc66c07e Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 13 Mar 2023 15:09:31 -0700 Subject: [PATCH 123/147] remove real ocid --- docs/source/user_guide/apachespark/dataflow.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/apachespark/dataflow.rst b/docs/source/user_guide/apachespark/dataflow.rst index f7bc7232f..9ca01ceec 100644 --- a/docs/source/user_guide/apachespark/dataflow.rst +++ b/docs/source/user_guide/apachespark/dataflow.rst @@ -153,7 +153,7 @@ You could submit a notebook using ADS SDK APIs. Here is an example to submit a n df = ( DataFlow() .with_compartment_id( - "ocid1.compartment.oc1..aaaaaaaapvb3hearqum6wjvlcpzm5ptfxqa7xfftpth4h72xx46ygavkqteq" + "ocid1.compartment.oc1." ) .with_driver_shape("VM.Standard.E4.Flex") .with_driver_shape_config(ocpus=2, memory_in_gbs=32) From b09fa0424a9c64db298046e64f959d2a8b6dd586 Mon Sep 17 00:00:00 2001 From: Mayoor Rao Date: Thu, 16 Mar 2023 10:54:36 -0700 Subject: [PATCH 124/147] fix typo in the documentation --- docs/source/user_guide/secrets/mysql.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/secrets/mysql.rst b/docs/source/user_guide/secrets/mysql.rst index ee3e89a3b..43d01a87a 100644 --- a/docs/source/user_guide/secrets/mysql.rst +++ b/docs/source/user_guide/secrets/mysql.rst @@ -40,7 +40,7 @@ The ``MySQLDBSecretKeeper.save`` API serializes and stores the credentials to th * ``freeform_tags`` (dict, optional): Freeform tags to be used for saving the secret in the OCI Console. * ``name`` (str): Name of the secret when saved in the vault. -The secret has the following informatio: +The secret has the following information: * ``database`` * ``host`` @@ -66,7 +66,7 @@ Save Credentials connection_parameters={ "user_name":"", "password":"", - "service_name":"service_name", + "database":"database", "host":"", "port":"", } @@ -123,7 +123,7 @@ The ``.load_secret()`` method has the following parameters: - ``auth``: Provide overriding ``auth`` information if the ``auth`` information is different from the ``ads.set_auth`` setting. - ``export_env``: The default is ``False``. If set to ``True``, the credentials are exported as environment variabled when used with the ``with`` operator. -- ``export_prefix``: The default name for environment variable is user_name, password, service_name. and wallet_location. You can add a prefix to avoid name collision. +- ``export_prefix``: The default name for environment variable is user_name, password, database. and wallet_location. You can add a prefix to avoid name collision. - ``format``: (Optional) If ``source`` is a file, then this value must be ``json`` or ``yaml`` depending on the file format. - ``source``: Either the file that was exported from ``export_vault_details``, or the OCID of the secret. From 244692fef74797fadde8cf8607f80dce550f8019 Mon Sep 17 00:00:00 2001 From: Mayoor Rao Date: Fri, 17 Mar 2023 10:06:24 -0700 Subject: [PATCH 125/147] link fix and remove redundant files --- docs/source/user_guide/secrets/adb.rst | 305 ------------------- docs/source/user_guide/secrets/authtoken.rst | 202 ------------ docs/source/user_guide/secrets/bds.rst | 255 ---------------- docs/source/user_guide/secrets/oracle.rst | 2 +- docs/source/user_guide/secrets/overview.rst | 13 - 5 files changed, 1 insertion(+), 776 deletions(-) delete mode 100644 docs/source/user_guide/secrets/adb.rst delete mode 100644 docs/source/user_guide/secrets/authtoken.rst delete mode 100644 docs/source/user_guide/secrets/bds.rst delete mode 100644 docs/source/user_guide/secrets/overview.rst diff --git a/docs/source/user_guide/secrets/adb.rst b/docs/source/user_guide/secrets/adb.rst deleted file mode 100644 index 72e4f49d5..000000000 --- a/docs/source/user_guide/secrets/adb.rst +++ /dev/null @@ -1,305 +0,0 @@ -Autonomous Database -=================== - -To connect to Autonomous Database you need the following: - -- user name -- password -- service name -- `wallet file - `_ - -The ``ADBSecretKeeper`` class saves the ADB credentials to the OCI Vault service. - - -Saving Credentials ------------------- - -**Prerequisites** - -- OCID of the vault created in the OCI Console. -- OCID of the master key to use for encrypting the secret content stored inside the vault. -- OCID of the compartment where the vault resides. This defaults to the compartment of the notebook session when - used in a Data Science notebook session. - -**ADBSecretKeeper** - -ADBSecretKeeper uses following parameter: - -- ``user_name: str``. The user name to be stored. -- ``password: str``. The password of the database. -- ``service_name: str``. Set the service name of the database. -- ``wallet_location: str``. Path to the wallet ZIP file. -- ``vault_id: str``. OCID of the vault. -- ``key_id: str``. OCID of the master key used for encrypting the secret. -- ``compartment_id: str``. OCID of the compartment where the vault is located. This defaults to the compartment of the notebook session when - used in a Data Science notebook session. - - -**ADBSecretKeeper.save** - -``ADBSecretKeeper.save`` API serializes and stores the credentials to Vault using the following parameters: - -- ``name (str)`` – Name of the secret when saved in Vault. -- ``description (str)`` – Description of the secret when saved in Vault. -- ``freeform_tags (dict, optional)``. Default None. Free form tags to use for saving the secret in the OCI Console. -- ``defined_tags (dict, optional.)``. Default None. Save the tags under predefined tags in the OCI Console. -- ``save_wallet (bool, optional.)``. Default False. If set to True, then the wallet file is serialized. - -When stored without the wallet information, the secret content has following information: - -- ``user_name`` -- ``password`` -- ``service_name`` - -To store wallet file content, set ``save_wallet`` to ``True``. The wallet content is stored by extracting all the -files from the wallet ZIP file, and then each file is stored in the vault as a secret. The list of OCIDs corresponding -to each file along with username, password, and service name is stored in a separate secret. -The secret corresponding to each file content has following information: - -- filename -- content of the file - -A **meta secret** is created to save the username, password, service name, and the secret ids of the files within the wallet file. It has following attributes: - -- ``user_name`` -- ``password`` -- ``wallet_file_name`` -- ``wallet_secret_ids`` - -The wallet file is reconstructed when ``ADBSecretKeeper.load_secret`` is called using the OCID of the **meta secret**. - -Examples -++++++++ - -**Saving a Secret Without Saving the Wallet File** - -.. code:: python3 - - import ads - ads.set_auth('resource_principal') # If using resource principal authentication - from ads.secrets.adb import ADBSecretKeeper - - connection_parameters={ - "user_name":"admin", - "password":"", - "service_name":"service_high", - "wallet_location":"/home/datascience/Wallet_--------.zip" - } - - ocid_vault = "ocid1.vault.oc1.." - ocid_master_key = "ocid1.key.oc1.." - ocid_mycompartment = "ocid1.compartment.oc1.." - - adw_keeper = ADBSecretKeeper(vault_id=ocid_vault, - key_id=ocid_master_key, - compartment_id=ocid_mycompartment, - **connection_parameters) - - # Store the credentials without storing the wallet file - adw_keeper.save("adw_employee_att2", "My DB credentials", freeform_tags={"schema":"emp"}) - print(adw_keeper.secret_id) - -``'ocid1.vaultsecret.oc1..'`` - -**Saving a Secret with the Wallet File** - -.. code:: python3 - - import ads - ads.set_auth('resource_principal') # If using resource principal authentication - from ads.secrets.adb import ADBSecretKeeper - - connection_parameters={ - "user_name":"admin", - "password":"", - "service_name":"service_high", - "wallet_location":"/home/datascience/Wallet_--------.zip" - } - - ocid_vault = "ocid1.vault.oc1.." - ocid_master_key = "ocid1.key.oc1.." - ocid_mycompartment = "ocid1.compartment.oc1.." - - adw_keeper = ADBSecretKeeper(vault_id=ocid_vault, - key_id=ocid_master_key, - compartment_id=ocid_mycompartment, - **connection_parameters) - - # Set `save_wallet`=True to save wallet file - - adw_keeper.save("adw_employee_att2", - "My DB credentials", - freeform_tags={"schema":"emp"}, - save_wallet=True - ) - - print(adw_keeper.secret_id) - -``'ocid1.vaultsecret.oc1..'`` - -You can save the vault details in a file for later reference or using it within your code using ``export_vault_details`` -API calls. The API currently enables you to export the information as a YAML file or a JSON file. - -.. code:: python3 - - adw_keeper.export_vault_details("my_db_vault_info.json", format="json") - -To save as a YAML file: - -.. code:: python3 - - adw_keeper.export_vault_details("my_db_vault_info.yaml", format="yaml") - -Loading Credentials -------------------- - -**Prerequisite** - -- OCID of the secret stored in vault. - -**ADBSecretKeeper.load_secret** - -``ADBSecretKeeper.load_secret`` API deserializes and loads the credentials from Vault. You could use this API in one of -the following ways - - -Using a ``with`` statement: - -.. code:: python3 - - with ADBSecretKeeper.load_secret('ocid1.vaultsecret.oc1..') as adwsecret: - print(adwsecret['user_name']) - -Without using a ``with`` statement: - -.. code:: python3 - - adwsecretobj = ADBSecretKeeper.load_secret('ocid1.vaultsecret.oc1..') - adwsecret = adwsecretobj.to_dict() - print(adwsecret['user_name']) - - -``load_secret`` takes following parameters - - -- ``source``: Either the file that was exported from ``export_vault_details`` or the OCID of the secret -- ``format``: Optional. If ``source`` is a file, then this value must be ``json`` or ``yaml`` depending on the file format. -- ``export_env``: Default is False. If set to True, the credentials are exported as environment variable when used with - the ``with`` operator. -- ``export_prefix``: The default name for environment variable is user_name, password, service_name, and wallet_location. You - can add a prefix to avoid name collision -- ``auth``: Provide overriding authorization information if the authorization information is different from the ``ads.set_auth`` setting. -- ``wallet_dir``: Optional. Directory path where the wallet zip file will be saved after the contents are retrieved from Vault. If wallet content is not available in the provided secret OCID, this attribute is ignored. -- ``wallet_location``: Optional. Path to the local wallet zip file. If vault secret does not have wallet file content, set this variable so that it will be available in the exported credential. If provided, this path takes precedence over the wallet file informat in the secret. - -If the wallet file was saved in the vault, then the ZIP file of the same name is created by ``load_secret``. By default the ZIP file is created in the working directory -To update the location, you can set the directory path with ``wallet_dir``. - -Examples -++++++++ - -**Access Credentials with a With Statement** - -.. code:: python3 - - import ads - ads.set_auth('resource_principal') # If using resource principal authentication - from ads.secrets.adb import ADBSecretKeeper - - with ADBSecretKeeper.load_secret( - "ocid1.vaultsecret.oc1.." - ) as adw_creds2: - print (adw_creds2["user_name"]) # Prints the user name - - print (adw_creds2["user_name"]) # Prints nothing. The credentials are cleared from the dictionary outside the ``with`` block - - -**Contextually Export Credentials as an Environment Variable Using a With Statement** - -To expose credentials as an environment variable, set ``export_env=True``. The following keys are exported: - -+------------------+---------------------------+ -| Secret attribute | Environment Variable Name | -+==================+===========================+ -| user_name | user_name | -+------------------+---------------------------+ -| password | password | -+------------------+---------------------------+ -| service_name | service_name | -+------------------+---------------------------+ -| wallet_location | wallet_location | -+------------------+---------------------------+ - -.. code:: python3 - - import os - import ads - - ads.set_auth('resource_principal') # If using resource principal authentication - from ads.secrets.adb import ADBSecretKeeper - - with ADBSecretKeeper.load_secret( - "ocid1.vaultsecret.oc1..", - export_env=True - ): - print(os.environ.get("user_name")) # Prints the user name - - print(os.environ.get("user_name")) # Prints nothing. The credentials are cleared from the dictionary outside the ``with`` block - -**Avoiding Name Collision with Your Existing Environment Variables** - -You can avoid name collision by setting a prefix string using ``export_prefix`` along with ``export_env=True``. For example, if you set prefix as ``myprocess``, -then the keys are exported as: - -+------------------+---------------------------+ -| Secret attribute | Environment Variable Name | -+==================+===========================+ -| user_name | myprocess.user_name | -+------------------+---------------------------+ -| password | myprocess.password | -+------------------+---------------------------+ -| service_name | myprocess.service_name | -+------------------+---------------------------+ -| wallet_location | myprocess.wallet_location | -+------------------+---------------------------+ - -.. code:: python3 - - import os - import ads - - ads.set_auth('resource_principal') # If using resource principal authentication - from ads.secrets.adb import ADBSecretKeeper - - with ADBSecretKeeper.load_secret( - "ocid1.vaultsecret.oc1..", - export_env=True, - export_prefix="myprocess" - ): - print(os.environ.get("myprocess.user_name")) # Prints the user name - - print(os.environ.get("myprocess.user_name")) # Prints nothing. The credentials are cleared from the dictionary outside the ``with`` block - -**Setting wallet location when wallet file is not part of the stored vault secret** - -To specify a local wallet ZIP file, set the path to the ZIP file with ``wallet_location``: - -.. code:: python3 - - import ads - ads.set_auth('resource_principal') # If using resource principal authentication - from ads.secrets.adb import ADBSecretKeeper - - with ADBSecretKeeper.load_secret( - "ocid1.vaultsecret.oc1..", - wallet_location="path/to/my/local/wallet.zip" - ) as adw_creds2: - print (adw_creds2["wallet_location"]) # Prints `path/to/my/local/wallet.zip` - - print (adw_creds2["wallet_location"]) # Prints nothing. The credentials are cleared from the dictionary outside the ``with`` block - - - - - - - diff --git a/docs/source/user_guide/secrets/authtoken.rst b/docs/source/user_guide/secrets/authtoken.rst deleted file mode 100644 index 71f744fbb..000000000 --- a/docs/source/user_guide/secrets/authtoken.rst +++ /dev/null @@ -1,202 +0,0 @@ -Auth Token -========== - -``AuthTokenSecretKeeper`` helps you to save the Auth Token or Access Token string to the OCI Vault service. - -Saving Credentials ------------------- - -**Prerequisite** - -- OCID of the Vault created on OCI console -- OCID of the master key that will be used for encrypting the secret content stored inside Vault -- OCID of the compartment where the Vault resides. This will be defaulted to the compartment of the Notebook session, if - used within a OCI Data Science notebook session. - -**AuthTokenSecretKeeper** - -AuthTokenSecretKeeper takes following constructor parameter - - -- ``auth_token: str``. Provide the Auth Token or Access Token string to be stored -- ``vault_id: str``. ocid of the vault -- ``key_id: str``. ocid of the master key used for encrypting the secret -- ``compartment_id: (str, optional)``. Default is None. ocid of the compartment where the vault is located. This will be defaulted to the compartment of the Notebook session, if - used within a OCI Data Science notebook session. - - -**AuthTokenSecretKeeper.save** - -``AuthTokenSecretKeeper.save`` API serializes and stores the credentials to Vault. It takes following parameters - - -- ``name (str)`` – Name of the secret when saved in the vault. -- ``description (str)`` – Description of the secret when saved in the vault. -- ``freeform_tags (dict, optional)`` – Freeform tags to use when saving the secret in the OCI Console. -- ``defined_tags (dict, optional.)`` – Save the tags under predefined tags in the OCI Console. - -The secret content has following information - - -- auth_token - -Examples -++++++++ - -**Saving Auth Token string** - -.. code:: python3 - - import ads - from ads.secrets.auth_token import AuthTokenSecretKeeper - - ads.set_auth('resource_principal') # If using resource principal authentication - - ocid_vault = "ocid1.vault.oc1..." - ocid_master_key = "ocid1.key.oc1.." - ocid_mycompartment = "ocid1.compartment.oc1.." - - authtoken2 = AuthTokenSecretKeeper( - vault_id=ocid_vault, - key_id=ocid_master_key, - compartment_id=ocid_mycompartment, - auth_token="" - ).save( - "my_xyz_auth_token2", - "This is my key for git repo xyz", - freeform_tags={"gitrepo":"xyz"} - ) - print(authtoken2.secret_id) - -You can save the vault details in a file for later reference or using it within your code using ``export_vault_details`` -API. The API currently let us export the information as a ``yaml`` file or a ``json`` file. - -.. code:: python3 - - authtoken2.export_vault_details("my_db_vault_info.json", format="json") - -To save as a ``yaml`` file - -.. code:: python3 - - authtoken2.export_vault_details("my_db_vault_info.yaml", format="yaml") - -Loading Credentials -------------------- - -**Prerequisite** - -- OCID of the secret stored in OCI Vault. - -**AuthTokenSecretKeeper.load_secret** - -``AuthTokenSecretKeeper.load_secret`` API deserializes and loads the credentials from Vault. You could use this API in one of -the following ways - - -Option 1: Using ``with`` statement - -.. code:: python3 - - with AuthTokenSecretKeeper.load_secret('ocid1.vaultsecret.oc1..') as authtoken: - print(authtoken['user_name'] - -Option 2: Without using ``with`` statement. - -.. code:: python3 - - authtoken = AuthTokenSecretKeeper.load_secret('ocid1.vaultsecret.oc1..') - authtokendict = authtoken.to_dict() - print(authtokendict['user_name']) - - -``load_secret`` takes following parameters - - -- ``source``: Either the file that was exported from ``export_vault_details`` or the OCID of the secret -- ``format``: Optional. If ``source`` is a file, then this value must be ``json`` or ``yaml`` depending on the file format. -- ``export_env``: Default is False. If set to True, the credentials are exported as environment variable when used with - the ``with`` operator. -- ``export_prefix``: The default name for environment variable is user_name, password, service_name, and wallet_location. You - can add a prefix to avoid name collision -- ``auth``: Provide overriding authorization information if the authorization information is different from the ``ads.set_auth`` setting. - - -Examples -++++++++ - -**Access credentials within With Statement** - -.. code:: python3 - - import ads - from ads.secrets.auth_token import AuthTokenSecretKeeper - - ads.set_auth('resource_principal') # If using resource principal authentication - - with AuthTokenSecretKeeper.load_secret(source="ocid1.vaultsecret.oc1...'}`` - - -**Contextually export credentials as environment variable using With statement** - -To expose credentials through environment variable, set ``export_env=True``. The following keys are exported - - -+------------------+---------------------------+ -| Secret attribute | Environment Variable Name | -+==================+===========================+ -| auth_token | auth_token | -+------------------+---------------------------+ - -.. code:: python3 - - import ads - from ads.secrets.auth_token import AuthTokenSecretKeeper - import os - - ads.set_auth('resource_principal') # If using resource principal authentication - - with AuthTokenSecretKeeper.load_secret( - source="ocid1.vaultsecret.oc1...", - export_env=True - ): - print(os.environ.get("auth_token")) # Prints the auth token - - print(os.environ.get("auth_token")) # Prints nothing. The credentials are cleared from the dictionary outside the ``with`` block - -**Avoding name collision with your existing environment variables** - -Name collision can be avoided by providing a prefix string through ``export_prefix`` along with ``export_env=True``. Example, if you set prefix as ``kafka`` -The keys are exported as - - -+------------------+---------------------------+ -| Secret attribute | Environment Variable Name | -+==================+===========================+ -| auth_token | kafka.auth_token | -+------------------+---------------------------+ - - -.. code:: python3 - - import ads - from ads.secrets.auth_token import AuthTokenSecretKeeper - import os - - ads.set_auth('resource_principal') # If using resource principal authentication - - with AuthTokenSecretKeeper.load_secret( - source="ocid1.vaultsecret.oc1...", - export_env=True, - export_prefix="kafka" - ): - print(os.environ.get("kafka.auth_token")) # Prints the auth token - - print(os.environ.get("kafka.auth_token")) # Prints nothing. The credentials are cleared from the dictionary outside the ``with`` block - - - - - - - - - diff --git a/docs/source/user_guide/secrets/bds.rst b/docs/source/user_guide/secrets/bds.rst deleted file mode 100644 index 16676d866..000000000 --- a/docs/source/user_guide/secrets/bds.rst +++ /dev/null @@ -1,255 +0,0 @@ -.. _secretbds: - -Oracle Big Data Service -======================= - -Available with ADS v2.5.10 and greater - -To connect to Oracle Big Data Service(BDS) you need the following: - -- ``principal``: The unique identity to which Kerberos can assign tickets. It will be used to generate the kerberos ticket. -- ``kerb5 config file``: krb5.conf file which can be copied from /etc/krb5.conf from the master node of the BDS cluster. It will be used to generate the kerberos ticket. -- ``keytab file``: The principal's keytab file which can be downloaded from the master node of the BDS cluster. It will be used to generate the kerberos ticket. -- ``hdfs host``: hdfs host name which will be used to connect to the hdfs file system. -- ``hdfs port``: hdfs port which will be used to connect to the hdfs file system. -- ``hive host``: hive host name which will be used to connect to the Hive Server. -- ``hive port``: hive port which will be used to connect to the Hive Server. - - -The ``BDSSecretKeeper`` class saves the BDS credentials to the OCI Vault service. - - -Saving Credentials ------------------- - -**Prerequisites** - -- OCID of the vault created in the OCI Console. -- OCID of the master key to use for encrypting the secret content stored inside the vault. -- OCID of the compartment where the vault resides. This defaults to the compartment of the notebook session when - used in a Data Science notebook session. - - -**BDSSecretKeeper** - -You can also save the connection parameters as well as the files needed to configure the kerberos -authentication into vault. This will allow you to use repetitively in -different notebook sessions, machines, and Jobs. - -``BDSSecretKeeper`` requires the following fields: - -- ``principal``: str. The unique identity to which Kerberos can assign tickets. -- ``hdfs_host``: str. The hdfs host name from the bds cluster. -- ``hive_host``: str. The hive host name from the bds cluster. -- ``hdfs_port``: str. The hdfs port from the bds cluster. -- ``hive_port``: str. The hive port from the bds cluster. -- ``kerb5_path``: str. The ``krb5.conf`` file path. -- ``keytab_path``: str. The path to the keytab file. -- ``vault_id: str``. OCID of the vault. -- ``key_id: str``. OCID of the master key used for encrypting the secret. -- ``compartment_id: str``. OCID of the compartment where the vault is located. This defaults to the compartment of the notebook session when - used in a Data Science notebook session. - - -**BDSSecretKeeper.save** - -``BDSSecretKeeper.save`` API serializes and stores the credentials to Vault using the following parameters: - -- ``name (str)`` – Name of the secret when saved in Vault. -- ``description (str)`` – Description of the secret when saved in Vault. -- ``freeform_tags (dict, optional)``. Default None. Free form tags to use for saving the secret in the OCI Console. -- ``defined_tags (dict, optional.)``. Default None. Save the tags under predefined tags in the OCI Console. -- ``save_files (bool, optional.)``. Default True. If set to True, then the keytab and kerb5 config files are serialized and saved. - -Examples -++++++++ - -**Saving a Secret With the Keytab and kerb5 config Files** - -.. code:: ipython3 - - import ads - import fsspec - import os - - from ads.secrets.big_data_service import BDSSecretKeeper - from ads.bds.auth import has_kerberos_ticket, refresh_ticket, krbcontext - - ads.set_auth('resource_principal') - - principal = "" - hdfs_host = "" - hive_host = "" - hdfs_port = - hive_port = - vault_id = "ocid1.vault.oc1.iad.*********" - key_id = "ocid1.key.oc1.iad.*********" - - secret = BDSSecretKeeper( - vault_id=vault_id, - key_id=key_id, - principal=principal, - hdfs_host=hdfs_host, - hive_host=hive_host, - hdfs_port=hdfs_port, - hive_port=hive_port, - keytab_path=keytab_path, - kerb5_path=kerb5_path - ) - - saved_secret = secret.save(name="your_bds_config_secret_name", - description="your bds credentials", - freeform_tags={"schema":"emp"}, - defined_tags={}, - save_files=True) - - - -**Saving a Secret Without Saving the Keytab and kerb5 config File** - -.. code:: ipython3 - - import ads - import fsspec - import os - - from ads.secrets.big_data_service import BDSSecretKeeper - from ads.bds.auth import has_kerberos_ticket, refresh_ticket, krbcontext - - ads.set_auth('resource_principal') - - principal = "" - hdfs_host = "" - hive_host = "" - hdfs_port = - hive_port = - vault_id = "ocid1.vault.oc1.iad.*********" - key_id = "ocid1.key.oc1.iad.*********" - - bds_keeper = BDSSecretKeeper( - vault_id=vault_id, - key_id=key_id, - principal=principal, - hdfs_host=hdfs_host, - hive_host=hive_host, - hdfs_port=hdfs_port, - hive_port=hive_port, - keytab_path=keytab_path, - kerb5_path=kerb5_path - ) - - saved_secret = bds_keeper.save(name="your_bds_config_secret_name", - description="your bds credentials", - freeform_tags={"schema":"emp"}, - defined_tags={}, - save_files=False) - - print(saved_secret.secret_id) - -``'ocid1.vaultsecret.oc1..'`` - -Loading Credentials -------------------- - -**Prerequisite** - -- OCID of the secret stored in vault. - -**BDSSecretKeeper.load_secret** - -``BDSSecretKeeper.load_secret`` API deserializes and loads the credentials from Vault. You could use this API in one of -the following ways - - -Using a ``with`` statement: - -.. code:: python3 - - with BDSSecretKeeper.load_secret('ocid1.vaultsecret.oc1..') as bdssecret: - print(bdssecret['hdfs_host']) - -Without using a ``with`` statement: - -.. code:: python3 - - bdssecretobj = BDSSecretKeeper.load_secret('ocid1.vaultsecret.oc1..') - bdssecret = bdssecretobj.to_dict() - print(bdssecret['hdfs_host']) - - -``load_secret`` takes following parameters - - -- ``source``: Either the file that was exported from ``export_vault_details`` or the OCID of the secret -- ``format``: Optional. If ``source`` is a file, then this value must be ``json`` or ``yaml`` depending on the file format. -- ``export_env``: Default is False. If set to True, the credentials are exported as environment variable when used with - the ``with`` operator. -- ``export_prefix``: The default name for environment variable is user_name, password, service_name, and wallet_location. You - can add a prefix to avoid name collision -- ``auth``: Provide overriding authorization information if the authorization information is different from the ``ads.set_auth`` setting. -- ``keytab_dir``: Optional. Directory path where the ``keytab`` ZIP file is saved after the contents are retrieved from the vault. If the ``keytab`` content is not available in the specified secret OCID, then this attribute is ignored. - -If the ``keytab`` and kerb5 configuration files were saved in the vault, then a ``keytab`` and kerb5 configuration file of the same name is created by ``load_secret``. By default, the ``keytab`` file is created in the ``keytab_path`` specified in the secret. -To update the location, set the directory path with ``key_dir``. However, the kerb5 configuration file is always saved in the "~/.bds_config/krb5.conf" path. - -Note that ``keytab`` and kerb5 configuration files are saved only when the -content is saved into the vault. - -After you load and save the configuration parameters files, you can -call the ``krbcontext`` context manager to create a Kerberos ticket. - - -Examples -++++++++ - -**Access Credentials Using a With Statement** - -To specify a local ``keytab`` file, set the path to the ZIP file with ``wallet_location``: - -.. code:: ipython3 - - from pyhive import hive - - with BDSSecretKeeper.load_secret(saved_secret.secret_id, keytab_dir="~/path/to/save/keytab_file/") as cred: - with krbcontext(principal=cred["principal"], keytab_path=cred['keytab_path']): - hive_cursor = hive.connect(host=cred["hive_host"], - port=cred["hive_port"], - auth='KERBEROS', - kerberos_service_name="hive").cursor() - - - -Now you can query the data from Hive: - -.. code:: ipython3 - - hive_cursor.execute(""" - select * - from your_db.your_table - limit 10 - """) - - import pandas as pd - pd.DataFrame(hive_cursor.fetchall(), columns=[col[0] for col in hive_cursor.description]) - -**Access Credentials Without Using a With Statement** - -Loading from secret id: - -.. code:: ipython3 - - bdssecretobj = BDSSecretKeeper.load_secret(saved_secret.secret_id) - bdssecret = bdssecretobj.to_dict() - print(bdssecret) - -Loading from a JSON file: - -.. code:: ipython3 - - bdssecretobj = BDSSecretKeeper.load_secret(source="./my_bds_vault_info.json", format="json") - bdssecretobj.to_dict() - -Loading from a YAML file: - -.. code:: ipython3 - - bdssecretobj = BDSSecretKeeper.load_secret(source="./my_bds_vault_info.yaml", format="yaml") - bdssecretobj.to_dict() \ No newline at end of file diff --git a/docs/source/user_guide/secrets/oracle.rst b/docs/source/user_guide/secrets/oracle.rst index b4e467f7d..9b28dcaa5 100644 --- a/docs/source/user_guide/secrets/oracle.rst +++ b/docs/source/user_guide/secrets/oracle.rst @@ -11,7 +11,7 @@ To connect to an Oracle Database you need the following: The ``OracleDBSecretKeeper`` class saves the Oracle Database credentials to the OCI Vault service. -See `API Documentation <../../ads.secrets.oracledb.OracleDBSecretKeeper>`__ for more details +See `API Documentation <../../ads.secrets.html#ads.secrets.oracledb.OracleDBSecretKeeper>`__ for more details Save Credentials diff --git a/docs/source/user_guide/secrets/overview.rst b/docs/source/user_guide/secrets/overview.rst deleted file mode 100644 index 310e2330a..000000000 --- a/docs/source/user_guide/secrets/overview.rst +++ /dev/null @@ -1,13 +0,0 @@ -Overview -******** - -Services such as OCI Database and Streaming require users to provide credentials. These credentials must be safely accessed at runtime. `OCI Vault `_ provides a mechanism for safe storage and access of secrets. ``SecretKeeper`` uses Vault as a backend to store and retrieve the credentials. The data structure of the credentials varies from service to service. There is a ``SecretKeeper`` specific to each data structure. - -These classes are provided: - -* ``ADBSecretKeeper``: Stores credentials for the Oracle Autonomous Database, with or without the wallet file. -* ``AuthTokenSecretKeeper``: Stores an Auth Token or Access Token string. This could be an Auth Token to use to connect to Streaming, Github, or other systems that used Auth Tokens or Access Token strings. -* ``BDSSecretKeeper``: Stores credentials for Oracle Big Data Service with or without Keytab and kerb5 configuration files. -* ``MySQLDBSecretKeeper``: Stores credentials for the MySQL database. This class will work with many databases that authenticate with a username and password only. -* ``OracleDBSecretKeeper``: Stores credentials for the Oracle Database. - From 04fc6335294dc476be5acac5fb167b9f9859f44b Mon Sep 17 00:00:00 2001 From: Lu Peng Date: Sun, 19 Mar 2023 20:31:22 -0700 Subject: [PATCH 126/147] Updated pr. --- .../user_guide/model_deployment/activate.rst | 13 - .../model_deployment/attributes.rst | 38 -- .../model_deployment/deactivate.rst | 13 - .../user_guide/model_deployment/delete.rst | 15 - .../user_guide/model_deployment/deploy.rst | 406 ------------------ .../user_guide/model_deployment/index.rst | 20 - .../user_guide/model_deployment/list.rst | 64 --- .../user_guide/model_deployment/logs.rst | 43 -- .../user_guide/model_deployment/predict.rst | 142 ------ .../user_guide/model_deployment/start.rst | 148 ------- .../user_guide/model_deployment/update.rst | 21 - .../model_registration/introduction.rst | 1 + .../model_registration/model_deploy.rst | 9 + .../model_registration/model_deploy_byoc.rst | 187 ++++++++ 14 files changed, 197 insertions(+), 923 deletions(-) delete mode 100644 docs/source/user_guide/model_deployment/activate.rst delete mode 100644 docs/source/user_guide/model_deployment/attributes.rst delete mode 100644 docs/source/user_guide/model_deployment/deactivate.rst delete mode 100644 docs/source/user_guide/model_deployment/delete.rst delete mode 100644 docs/source/user_guide/model_deployment/deploy.rst delete mode 100644 docs/source/user_guide/model_deployment/index.rst delete mode 100644 docs/source/user_guide/model_deployment/list.rst delete mode 100644 docs/source/user_guide/model_deployment/logs.rst delete mode 100644 docs/source/user_guide/model_deployment/predict.rst delete mode 100644 docs/source/user_guide/model_deployment/start.rst delete mode 100644 docs/source/user_guide/model_deployment/update.rst create mode 100644 docs/source/user_guide/model_registration/model_deploy_byoc.rst diff --git a/docs/source/user_guide/model_deployment/activate.rst b/docs/source/user_guide/model_deployment/activate.rst deleted file mode 100644 index 157d4699c..000000000 --- a/docs/source/user_guide/model_deployment/activate.rst +++ /dev/null @@ -1,13 +0,0 @@ -Activate -******** - -An inactive model deployment can be activated using a ``ModelDeployment`` object. See `Activate Model Deployments `_ for more details. - -If you have a ``ModelDeployment`` object, you can use the ``.activate()`` method to activate the model that is associated with that object. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -.. code-block:: python3 - - deployment.activate(wait_for_completion=True) - diff --git a/docs/source/user_guide/model_deployment/attributes.rst b/docs/source/user_guide/model_deployment/attributes.rst deleted file mode 100644 index 71e4e6276..000000000 --- a/docs/source/user_guide/model_deployment/attributes.rst +++ /dev/null @@ -1,38 +0,0 @@ -Attributes -********** - -The ``ModelDeployment`` class has a number of attributes that are assigned by the system. They provide a mechanism to determine the state of the model deployment, the URI to make predictions, the model deployment OCID, etc. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -OCID -==== - -The ``.model_deployment_id`` of the ``ModelDeployment`` class specifies -the OCID of the model deployment. - -.. code-block:: python3 - - deployment.model_deployment_id - -State -===== - -You can determine the state of the model deployment using the ``.current_state`` enum attribute of a ``ModelDeployment`` object. This returns an enum object and the string value can be determined with ``.current_state.name``. It will have values like 'ACTIVE', 'INACTIVE', and 'FAILED'. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -.. code-block:: python3 - - deployment.current_state.name - - -URL -=== - -The URL of the model deployment to use to make predictions using an HTTP request. The request is made to the URL given in the ``.url`` attribute of the ``ModelDeployment`` class. You can make HTTP requests to this endpoint to have the model make predictions, see the `Predict `__ section and `Invoking a Model Deployment `__ documentation for details. - -.. code-block:: python3 - - deployment.url - diff --git a/docs/source/user_guide/model_deployment/deactivate.rst b/docs/source/user_guide/model_deployment/deactivate.rst deleted file mode 100644 index 744b7fd01..000000000 --- a/docs/source/user_guide/model_deployment/deactivate.rst +++ /dev/null @@ -1,13 +0,0 @@ -Deactivate -********** - -An activate model deployment can be deactivated using a ``ModelDeployment`` object. Deactivating a model deployment shuts down the instances that are associated with your deployment. See `Deactivate Model Deployments `_ for more details. - -If you have a ``ModelDeployment`` object, you can use the ``.deactivate()`` method to deactivate the model that is associated with that object. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -.. code-block:: python3 - - deployment.deactivate(wait_for_completion=True) - diff --git a/docs/source/user_guide/model_deployment/delete.rst b/docs/source/user_guide/model_deployment/delete.rst deleted file mode 100644 index 9d7c85a8f..000000000 --- a/docs/source/user_guide/model_deployment/delete.rst +++ /dev/null @@ -1,15 +0,0 @@ -Delete -****** - -A model deployment can be deleted using a ``ModelDeployment`` object. - -When a model deployment is deleted, it deletes the load balancer instances associated with it. However, it doesn't delete other resources like log group, log, or model. - -If you have a ``ModelDeployment`` object, you can use the ``.delete()`` method to delete the model that is associated with that object. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -.. code-block:: python3 - - deployment.delete(wait_for_completion=True) - diff --git a/docs/source/user_guide/model_deployment/deploy.rst b/docs/source/user_guide/model_deployment/deploy.rst deleted file mode 100644 index 6e4263330..000000000 --- a/docs/source/user_guide/model_deployment/deploy.rst +++ /dev/null @@ -1,406 +0,0 @@ -Deploy -****** - -To deploy a model deployment, you'll need to define a ``ModelDeployment`` object and call the ``.deploy()`` of it. You could either use API or YAML to define the ``ModelDeployment`` object. - -Deploy a ModelDeployment on Docker Container Runtime -==================================================== - -The ADS ``ModelDeploymentContainerRuntime`` class allows you to run a container image using OCI data science model deployment. - -To use the ``ModelDeploymentContainerRuntime``, you need to first build a docker container image. See `` for the end-to-end example. Once you have the image, push it to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. - -To configure ``ModelDeploymentContainerRuntime``, you must specify the container ``image``. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). - -Below is an example of deploying model on docker container runtime: - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure - from ads.model.deployment.model_deployment_runtime import ModelDeploymentContainerRuntime - from ads.model.deployment import ModelDeployment - - infrastructure = ( - ModelDeploymentInfrastructure() - .with_project_id("") - .with_compartment_id("") - .with_shape_name("VM.Standard.E4.Flex") - .with_shape_config_details( - ocpus=1, - memory_in_gbs=16 - ) - .with_replica(1) - .with_bandwidth_mbps(10) - .with_web_concurrency(10) - .with_access_log( - log_group_id="", - log_id="" - ) - .with_predict_log( - log_group_id="", - log_id="" - ) - ) - - container_runtime = ( - ModelDeploymentContainerRuntime() - .with_image("") - .with_image_digest("") - .with_entrypoint(["python","/opt/ds/model/deployed_model/api.py"]) - .with_server_port(5000) - .with_health_check_port(5000) - .with_env({"key":"value"}) - .with_deployment_mode("HTTPS_ONLY") - .with_model_uri("") - ) - - deployment = ( - ModelDeployment() - .with_display_name("Model Deployment Demo using ADS") - .with_description("The model deployment description") - .with_freeform_tags({"key1":"value1"}) - .with_infrastructure(infrastructure) - .with_runtime(container_runtime) - ) - - deployment.deploy() - - .. code-tab:: Python3 - :caption: YAML - - from ads.model.deployment import ModelDeployment - - yaml_string = """ - kind: deployment - spec: - displayName: Model Deployment Demo using ADS - description: The model deployment description - freeform_tags: - key1: value1 - infrastructure: - kind: infrastructure - type: datascienceModelDeployment - spec: - compartmentId: - projectId: - accessLog: - logGroupId: - logId: - predictLog: - logGroupId: - logId: - shapeName: VM.Standard.E4.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - replica: 1 - bandWidthMbps: 10 - webConcurrency: 10 - runtime: - kind: runtime - type: container - spec: - modelUri: - image: - imageDigest: - entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] - serverPort: 5000 - healthCheckPort: 5000 - env: - WEB_CONCURRENCY: "10" - deploymentMode: HTTPS_ONLY - """ - - deployment = ModelDeployment.from_yaml(yaml_string) - deployment.deploy() - - -Deploy a ModelDeployment on Conda Runtime -========================================= - -To deploy a model deployment on conda runtime, you need to configure ``ModelDeploymentCondaRuntime``. - -Below is an example of deploying model on conda runtime: - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure - from ads.model.deployment.model_deployment_runtime import ModelDeploymentCondaRuntime - from ads.model.deployment import ModelDeployment - - infrastructure = ( - ModelDeploymentInfrastructure() - .with_project_id("") - .with_compartment_id("") - .with_shape_name("VM.Standard.E4.Flex") - .with_shape_config_details( - ocpus=1, - memory_in_gbs=16 - ) - .with_replica(1) - .with_bandwidth_mbps(10) - .with_web_concurrency(10) - .with_access_log( - log_group_id="", - log_id="" - ) - .with_predict_log( - log_group_id="", - log_id="" - ) - ) - - conda_runtime = ( - ModelDeploymentCondaRuntime() - .with_env({"key":"value"}) - .with_deployment_mode("HTTPS_ONLY") - .with_model_uri("") - ) - - deployment = ( - ModelDeployment() - .with_display_name("Model Deployment Demo using ADS") - .with_description("The model deployment description") - .with_freeform_tags({"key1":"value1"}) - .with_infrastructure(infrastructure) - .with_runtime(conda_runtime) - ) - - deployment.deploy() - - .. code-tab:: Python3 - :caption: YAML - - from ads.model.deployment import ModelDeployment - - yaml_string = """ - kind: deployment - spec: - displayName: Model Deployment Demo using ADS - description: The model deployment description - freeform_tags: - key1: value1 - infrastructure: - kind: infrastructure - type: datascienceModelDeployment - spec: - compartmentId: - projectId: - accessLog: - logGroupId: - logId: - predictLog: - logGroupId: - logId: - shapeName: VM.Standard.E4.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - replica: 1 - bandWidthMbps: 10 - webConcurrency: 10 - runtime: - kind: runtime - type: conda - spec: - modelUri: - env: - WEB_CONCURRENCY: "10" - deploymentMode: HTTPS_ONLY - """ - - deployment = ModelDeployment.from_yaml(yaml_string) - deployment.deploy() - - -**ADS ModelDeployment YAML schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - deployment - spec: - required: true - type: dict - schema: - displayName: - type: string - required: false - description: - type: string - required: false - freeform_tags: - type: dict - required: false - defined_tags: - type: dict - required: false - infrastructure: - type: dict - required: true - runtime: - type: dict - required: true - -**ADS Model Deployment Infrastructure YAML Schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - infrastructure - type: - required: true - type: string - allowed: - - datascienceModelDeployment - spec: - compartmentId: - type: string - required: true - projectId: - type: string - required: true - bandWidthMbps: - type: integer - required: false - webConcurrency: - type: integer - required: false - logGroupId: - type: string - required: false - logId: - type: string - required: false - accessLog: - type: dict - nullable: true - required: false - schema: - logId: - required: false - type: string - logGroupId: - required: false - type: string - predictLog: - type: dict - nullable: true - required: false - schema: - logId: - required: false - type: string - logGroupId: - required: false - type: string - shapeName: - type: string - required: false - shapeConfigDetails: - type: dict - nullable: true - required: false - schema: - ocpus: - required: true - type: float - memoryInGBs: - required: true - type: float - replica: - type: integer - required: false - -**ADS Model Deployment Conda Runtime YAML Schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - conda - spec: - modelUri: - type: string - required: true - env: - type: dict - required: false - inputStreamIds: - type: list - required: false - outputStreamIds: - type: list - required: false - deploymentMode: - type: string - required: false - -**ADS Model Deployment Container Runtime YAML Schema** - -.. code-block:: yaml - - kind: - required: true - type: string - allowed: - - runtime - type: - required: true - type: string - allowed: - - container - spec: - modelUri: - type: string - required: true - image: - type: string - required: true - imageDigest: - type: string - required: false - entrypoint: - type: list - required: false - cmd: - type: list - required: false - serverPort: - type: integer - required: false - healthCheckPort: - type: integer - required: false - env: - type: dict - required: false - inputStreamIds: - type: list - required: false - outputStreamIds: - type: list - required: false - deploymentMode: - type: string - required: false - diff --git a/docs/source/user_guide/model_deployment/index.rst b/docs/source/user_guide/model_deployment/index.rst deleted file mode 100644 index 362c0db31..000000000 --- a/docs/source/user_guide/model_deployment/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -################ -Model Deployment -################ - - -.. toctree:: - :maxdepth: 1 - - start - activate - attributes - deactivate - delete - deploy - list - logs - predict - properties - update - diff --git a/docs/source/user_guide/model_deployment/list.rst b/docs/source/user_guide/model_deployment/list.rst deleted file mode 100644 index 83f9e2c3d..000000000 --- a/docs/source/user_guide/model_deployment/list.rst +++ /dev/null @@ -1,64 +0,0 @@ -Inventory -********* - -List -==== - -The ``.list()`` method of the ``ModelDeployment`` class returns a list of ``ModelDeployment`` objects. - -The code snippet obtains a list of active deployments in the compartment specified by ``compartment_id``, and prints the display name. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployment - - for active in ModelDeployment.list(status="ACTIVE", compartment_id=compartment_id): - print(active.display_name) - -Show -==== - -The ``.list_df()`` method is a helper function that works the same way as the ``.list()`` method except it returns a dataframe of the results. - -.. code-block:: python3 - - from ads.model.deployment import ModelDeployment - - ModelDeployment.list_df(compartment_id=compartment_id, status="ACTIVE") - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - -
deployment_iddeployment_urlcurrent_state
0ocid1.datasciencemodeldeployment..&l;tunique_ID>https://modeldeployment.us-ashburn-1...ACTIVE
-
- diff --git a/docs/source/user_guide/model_deployment/logs.rst b/docs/source/user_guide/model_deployment/logs.rst deleted file mode 100644 index 6d6b55afc..000000000 --- a/docs/source/user_guide/model_deployment/logs.rst +++ /dev/null @@ -1,43 +0,0 @@ -Logs -**** - -The model deployment process creates a set of workflow logs. Optionally, you can also configure the logging service to capture access and predict logs. - -In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -Access/Predict -============== - -The ``.show_logs()`` and ``.logs()`` methods in the ``ModelDeployment`` class exposes the predict and access logs. The parameter ``log_type`` accepts ``predict`` and ``access`` to specify which logs to return. When it's not specified, the access logs are returned. The parameters ``time_start`` and ``time_end`` restrict the logs to time periods between those entries. The ``limit`` parameter limits the number of log entries that are returned. - -Logs are not collected in real-time. Therefore, it is possible that logs have been emitted by the model deployment but are not currently available with the ``.logs()`` and ``.show_logs()`` methods. - -``logs`` --------- - -This method returns a list of dictionaries where each element of the list is a log entry. Each element of the dictionary is a key-value pair from the log. - -.. code-block:: python3 - - deployment.logs(log_type="access", limit=10) - -``show_logs`` -------------- - -This method returns a dataframe where each row represents a log entry. - -.. code-block:: python3 - - deployment.show_logs(log_type="access", limit=10) - -``Watch`` ---------- - -You can stream the predict and access log of a model deployment using the ``.watch()`` method of a ``ModelDeployment`` object. - -.. code-block:: python3 - - deployment.watch() # stream predict and access log - deployment.watch(log_type="access") # stream access log - - diff --git a/docs/source/user_guide/model_deployment/predict.rst b/docs/source/user_guide/model_deployment/predict.rst deleted file mode 100644 index 86a0ea64f..000000000 --- a/docs/source/user_guide/model_deployment/predict.rst +++ /dev/null @@ -1,142 +0,0 @@ -Predict -******* - -Predictions can be made by calling the HTTP endpoint associated with the model deployment. The ``ModelDeployment`` object ``url`` attribute specifies the endpoint. You could also use the ``ModelDeployment`` object with the ``.predict()`` method. The format of the data that is passed to the HTTP endpoint depends on the setup of the model artifact. The default setup is to pass in a Python dictionary that has been converted to a JSON data structure. The first level defines the feature names. The second level uses an identifier for the observation (for example, row in the dataframe), and the value associated with it. Assuming the model has features F1, F2, F3, F4, and F5, then the observations are identified by the values 0, 1, and 2 and the data would look like this: - -===== == == == == == -Index F1 F2 F3 F4 F5 -===== == == == == == -0 11 12 13 14 15 -1 21 22 23 24 25 -2 31 32 33 34 35 -===== == == == == == - -The Python dictionary representation would be: - -.. code-block:: python3 - - test = { - 'F1': { 0: 11, 1: 21, 2: 31}, - 'F2': { 0: 12, 1: 22, 2: 32}, - 'F3': { 0: 13, 1: 23, 2: 33}, - 'F4': { 0: 14, 1: 24, 2: 34}, - 'F5': { 0: 15, 1: 25, 2: 35} - } - - -You can use the ``ModelDeployment`` object to call the HTTP endpoint. The returned -result is the predictions for the three observations. - -.. code-block:: python3 - - deployment.predict(test) - -.. parsed-literal:: - - {'prediction': [0, 2, 0]} - - -Model Deploy now supports binary payloads. You no longer need to convert binary images to Base64 encoded strings when making inferences. - -Example -======= - -The following example shows how to use `predict()` with image bytes: -The `score.py` file does not provide default deserialization for bytes input. You need to provide your own implementations. -The model used in this example has its raw training data `normalized `_. The next cell reproduces these transformations. The original image is 256x384 pixels and the training data is 224x224. Therefore, the image is resized and cropped. The color variation in the image is also adjusted to match the training data. The image is converted to a `Tensor` object. This object is a four-dimensional tensor and the first dimension has only a single level. This dimension is removed using the `.unsqueeze()` method. - -Load data - -.. code-block:: python3 - - from PIL import Image - im = Image.open('') - im.convert("RGB").save("") - - with open('', 'rb') as f: - byte_im = f.read() - -Example model - -.. code-block:: python3 - - # load the pre-trained model. - model = resnet18(pretrained=True) - # set the model to inference mode - _ = model.eval() - -Model framework serialization - -.. code-block:: python3 - - artifact_dir = "" - pytorch_model = PyTorchModel(estimator=model, artifact_dir=artifact_dir) - conda_env = 'computervision_p37_cpu_v1' - - # Create a sample of the y values. - y_sample = [0] * len(prediction_not_normalized) - y_sample[prediction_normalized.index(max_value)] = 1 - - pytorch_model.prepare( - inference_conda_env=conda_env, - training_conda_env=conda_env, - use_case_type=UseCaseType.IMAGE_CLASSIFICATION, - X_sample=image_tensor, - y_sample=y_sample, - training_id=None, - force_overwrite=True - ) - pytorch_model.verify(byte_im)['prediction'][0][:10] - model_id = pytorch_model.save(display_name='Test PyTorchModel model Bytes Input', timeout=600) - - deploy = pytorch_model.deploy(display_name='Test PyTorchModel deployment') - pytorch_model.predict(byte_im)['prediction'][0][:10] - - pytorch_model.delete_deployment(wait_for_completion=True) - ModelCatalog(compartment_id=os.environ['NB_SESSION_COMPARTMENT_OCID']).delete_model(model_id) - - -The change needed in `score.py`: - -.. code-block:: python3 - - def deserialize(data): - if isinstance(data, bytes): - return data - - ... - - - def pre_inference(data): - data = deserialize(data) - - import base64 - import io - import torchvision.transforms as transforms - - from PIL import Image - img_bytes = io.BytesIO(data) - image = Image.open(img_bytes) - - # preprocess the data to make it accepted by the model - preprocess = transforms.Compose([ - transforms.Resize(256), - transforms.CenterCrop(224), - transforms.ToTensor(), - transforms.Normalize( - mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225] - ), - ]) - input_tensor = preprocess(image) - input_batch = input_tensor.unsqueeze(0) - - return input_batch - - def post_inference(yhat): - if isinstance(yhat, torch.Tensor): - from torch.nn import Softmax - softmax = Softmax(dim=1) - return softmax(yhat).tolist() - - return yhat \ No newline at end of file diff --git a/docs/source/user_guide/model_deployment/start.rst b/docs/source/user_guide/model_deployment/start.rst deleted file mode 100644 index d5fae2b36..000000000 --- a/docs/source/user_guide/model_deployment/start.rst +++ /dev/null @@ -1,148 +0,0 @@ -Quick Start -*********** - -Model deployments are a managed resource within the Oracle Cloud Infrastructure (OCI) Data Science service. They allow you to deploy machine learning models as web applications (HTTP endpoints). They provide real-time predictions and enables you to quickly productionalize your models. - -The ``ads.model.deployment`` module allows you to deploy models using the Data Science service. This module is built on top of the ``oci`` Python SDK. It is designed to simplify data science workflows. - -A `model artifact `__ is a ZIP archive of the files necessary to deploy your model. The model artifact contains the `score.py `__ file. This file has the Python code that is used to load the model and perform predictions. The model artifact also contains the `runtime.yaml `__ file. This file is used to define the conda environment used by the model deployment. - -ADS supports deploying a model artifact from the Data Science `model catalog `__, or the URI of a directory that can be in the local block storage or in Object Storage. - -You can integrate model deployments with the `OCI Logging service `__. The system allows you to store access and prediction logs ADS provides APIs to simplify the interaction with the Logging service, see -`ADS Logging <../logging/logging.html>`__. - -The ``ads.model.deployment`` module provides the ``ModelDeployment`` class, which is used to deploy and manage the model. Checkout the example below to learn how to deploy models. - -Example -======= - -.. tabs:: - - .. code-tab:: Python3 - :caption: Python - - from ads.model.deployment.model_deployment_infrastructure import ModelDeploymentInfrastructure - from ads.model.deployment.model_deployment_runtime import ModelDeploymentContainerRuntime - from ads.model.deployment import ModelDeployment - - infrastructure = ( - ModelDeploymentInfrastructure() - .with_project_id("") - .with_compartment_id("") - .with_shape_name("VM.Standard.E4.Flex") - .with_shape_config_details( - ocpus=1, - memory_in_gbs=16 - ) - .with_replica(1) - .with_bandwidth_mbps(10) - .with_web_concurrency(10) - .with_access_log( - log_group_id="", - log_id="" - ) - .with_predict_log( - log_group_id="", - log_id="" - ) - ) - - container_runtime = ( - ModelDeploymentContainerRuntime() - .with_image("") - .with_image_digest("") - .with_entrypoint(["python","/opt/ds/model/deployed_model/api.py"]) - .with_server_port(5000) - .with_health_check_port(5000) - .with_env({"key":"value"}) - .with_deployment_mode("HTTPS_ONLY") - .with_model_uri("") - ) - - deployment = ( - ModelDeployment() - .with_display_name("Model Deployment Demo using ADS") - .with_description("The model deployment description") - .with_freeform_tags({"key1":"value1"}) - .with_infrastructure(infrastructure) - .with_runtime(container_runtime) - ) - - deployment.deploy() # deploy model - deployment.with_display_name("Updated name").update() # update deployment with new name - deployment.predict(data={"line" : "12"}) # predict - deployment.watch(log_type="access") # stream the access log of deployment - deployment.delete() # delete the model deployment - - .. code-tab:: Python3 - :caption: YAML - - from ads.model.deployment import ModelDeployment - - yaml_string = """ - kind: deployment - spec: - displayName: Model Deployment Demo using ADS - description: The model deployment description - freeform_tags: - key1: value1 - infrastructure: - kind: infrastructure - type: datascienceModelDeployment - spec: - compartmentId: - projectId: - accessLog: - logGroupId: - logId: - predictLog: - logGroupId: - logId: - shapeName: VM.Standard.E4.Flex - shapeConfigDetails: - memoryInGBs: 16 - ocpus: 1 - replica: 1 - bandWidthMbps: 10 - webConcurrency: 10 - runtime: - kind: runtime - type: container - spec: - modelUri: - image: - imageDigest: - entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] - serverPort: 5000 - healthCheckPort: 5000 - env: - WEB_CONCURRENCY: "10" - deploymentMode: HTTPS_ONLY - """ - - deployment = ModelDeployment.from_yaml(yaml_string) - deployment.deploy() # deploy model - deployment.with_display_name("Updated name").update() # update deployment with new name - deployment.predict(data={"line" : "12"}) # predict - deployment.watch(log_type="access") # stream the access log of deployment - deployment.delete() # delete the model deployment - - -################ -Model Deployment -################ - -.. toctree:: - :maxdepth: 1 - - activate - attributes - deactivate - delete - deploy - list - logs - predict - properties - update diff --git a/docs/source/user_guide/model_deployment/update.rst b/docs/source/user_guide/model_deployment/update.rst deleted file mode 100644 index af187941e..000000000 --- a/docs/source/user_guide/model_deployment/update.rst +++ /dev/null @@ -1,21 +0,0 @@ -Update -****** - -The ``.update()`` method of the ``ModelDeployment`` class is used to make changes to a deployed model. Check out the `Editing Model Deployments `__ for a -list of what properties can be updated. - -A common use case is to change the underlying model that is deployed. In the following code snippets, the variable ``deployment`` is a ``ModelDeployment`` object. This object can be obtained from a call to ``.deploy()`` or ``.from_id()``. - -.. code-block:: python3 - - deployment.runtime.with_model_uri("") - deployment.update() - -Or, you could update the instance shape with: - -.. code-block:: python3 - - deployment.infrastructure.with_shape_name("VM.Standard.E4.Flex") - deployment.with_shape_config_details(ocpus=2, memory_in_gbs=32) - deployment.update() - diff --git a/docs/source/user_guide/model_registration/introduction.rst b/docs/source/user_guide/model_registration/introduction.rst index 2e2352046..99a41d54c 100644 --- a/docs/source/user_guide/model_registration/introduction.rst +++ b/docs/source/user_guide/model_registration/introduction.rst @@ -60,6 +60,7 @@ Deploying model :maxdepth: 1 model_deploy + model_deploy_byoc Loading model ----------------- diff --git a/docs/source/user_guide/model_registration/model_deploy.rst b/docs/source/user_guide/model_registration/model_deploy.rst index b48713690..d7cb3805a 100644 --- a/docs/source/user_guide/model_registration/model_deploy.rst +++ b/docs/source/user_guide/model_registration/model_deploy.rst @@ -1,3 +1,6 @@ +Deploy Model on Conda Runtime +***************************** + Once you have ADS Model object, you can call ``deploy`` function to deploy the model and generate the endpoint. Here is an example of deploying LightGBM model: @@ -84,6 +87,12 @@ Observability lightgbm_model.model_deployment.logs().tail() +You cal also call the ``.watch()`` from model deployment instance to stream the logs + +.. code-block:: python3 + + lightgbm_model.model_deployment.watch() + Update Model Deployment ------------------------- diff --git a/docs/source/user_guide/model_registration/model_deploy_byoc.rst b/docs/source/user_guide/model_registration/model_deploy_byoc.rst new file mode 100644 index 000000000..665b41e4d --- /dev/null +++ b/docs/source/user_guide/model_registration/model_deploy_byoc.rst @@ -0,0 +1,187 @@ +Deploy Model on Container Runtime +********************************* + +The ADS ``GenericModel`` and ``ModelDeployment`` classes allow you to run a container image using OCI data science model deployment. + +To deploy model on container runtime, you need to first build a docker container image. See `` for the end-to-end example. Once you have the image, push it to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. + +Deploy Using GenericModel Class +=============================== + +When the container runtime is ready, you can call ``deploy`` function to deploy the model and generate the endpoint. You must specify the container ``deployment_image``. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). For more details regarding the parameters allowed for container runtime, see ``. + +Below is an example of deploying Sklearn model on container runtime using ``SklearnModel`` class: + +.. code-block:: python3 + + import os + import pandas as pd + from joblib import load + from ads.model import SklearnModel + + # Load data + data = pd.read_json() + data_test = data.transpose() + X = data_test.drop(data_test.loc[:, "Line":"# Letter"].columns, axis=1) + X_test = X.iloc[int("12"), :].values.reshape(1, -1) + + # Load model + clf_lda = load() + + # Instantiate ads.model.SklearnModel + sklearn_model = SklearnModel(estimator=clf_lda, artifact_dir=) + + # Prepare related artifacts + sklearn_model.prepare( + model_file_name=, + ignore_conda_error=True, # make sure to set ignore_conda_error=True for container runtime + ) + + # Verify model locally + sklearn_model.verify(X_test) + + # Register Sklearn model + sklearn_model.save() + + # Deploy Sklearn model on container runtime + sklearn_model.deploy( + display_name="Sklearn Model BYOC", + deployment_log_group_id="ocid1.loggroup.oc1.xxx.xxxxx", + deployment_access_log_id="ocid1.log.oc1.xxx.xxxxx", + deployment_predict_log_id="ocid1.log.oc1.xxx.xxxxx", + deployment_image="iad.ocir.io//:", + entrypoint=["python", "/opt/ds/model/deployed_model/api.py"], + server_port=5000, + health_check_port=5000, + environment_variables={"test_key": "test_value"}, + ) + + # Get endpoint of deployed model + model_deployment_url = sklearn_model.model_deployment.url + + # Generate prediction by invoking the deployed endpoint + sklearn_model.predict(data={"line": "12"}) + + +Deploy Using ModelDeployment Class +================================== + +To deploy a model deployment, you can define a ``ModelDeployment`` object and call the ``.deploy()`` of it. You could either use API or YAML to define the ``ModelDeployment`` object. + +When configuring the ``ModelDeploymentContainerRuntime`` object, you must specify the container `image`. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). For more details regarding the parameters allowed for container runtime, see ``. + +Below is an example of deploying model on container runtime using ``ModelDeployment`` class: + +.. tabs:: + + .. code-tab:: Python3 + :caption: Python + + from ads.model.deployment import ModelDeployment, ModelDeploymentInfrastructure, ModelDeploymentContainerRuntime + + # configure model deployment infrastructure + infrastructure = ( + ModelDeploymentInfrastructure() + .with_project_id("") + .with_compartment_id("") + .with_shape_name("VM.Standard.E4.Flex") + .with_shape_config_details( + ocpus=1, + memory_in_gbs=16 + ) + .with_replica(1) + .with_bandwidth_mbps(10) + .with_web_concurrency(10) + .with_access_log( + log_group_id="", + log_id="" + ) + .with_predict_log( + log_group_id="", + log_id="" + ) + ) + + # configure model deployment runtime + container_runtime = ( + ModelDeploymentContainerRuntime() + .with_image("iad.ocir.io//:") + .with_image_digest("") + .with_entrypoint(["python","/opt/ds/model/deployed_model/api.py"]) + .with_server_port(5000) + .with_health_check_port(5000) + .with_env({"key":"value"}) + .with_deployment_mode("HTTPS_ONLY") + .with_model_uri("") + ) + + # configure model deployment + deployment = ( + ModelDeployment() + .with_display_name("Model Deployment Demo using ADS") + .with_description("The model deployment description") + .with_freeform_tags({"key1":"value1"}) + .with_infrastructure(infrastructure) + .with_runtime(container_runtime) + ) + + # Deploy model on container runtime + deployment.deploy() + + # Generate prediction by invoking the deployed endpoint + deployment.predict(data=) + + .. code-tab:: Python3 + :caption: YAML + + from ads.model.deployment import ModelDeployment + + yaml_string = """ + kind: deployment + spec: + displayName: Model Deployment Demo using ADS + description: The model deployment description + freeform_tags: + key1: value1 + infrastructure: + kind: infrastructure + type: datascienceModelDeployment + spec: + compartmentId: + projectId: + accessLog: + logGroupId: + logId: + predictLog: + logGroupId: + logId: + shapeName: VM.Standard.E4.Flex + shapeConfigDetails: + memoryInGBs: 16 + ocpus: 1 + replica: 1 + bandWidthMbps: 10 + webConcurrency: 10 + runtime: + kind: runtime + type: container + spec: + modelUri: + image: iad.ocir.io//: + imageDigest: + entrypoint: ["python","/opt/ds/model/deployed_model/api.py"] + serverPort: 5000 + healthCheckPort: 5000 + env: + WEB_CONCURRENCY: "10" + deploymentMode: HTTPS_ONLY + """ + + # Initialize ads.ModelDeployment + deployment = ModelDeployment.from_yaml(yaml_string) + + # Deploy model on container runtime + deployment.deploy() + + # Generate prediction by invoking the deployed endpoint + deployment.predict(data=) From f1871a2f4a863fbe97c40c7de517d8b62a7969b6 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 19 Mar 2023 21:39:05 -0700 Subject: [PATCH 127/147] ODSC-40388: add class doc for serde and also add ml pipeline in the readme --- README.md | 3 ++- docs/source/ads.model.framework.rst | 33 +++++++++++++++++------ docs/source/ads.model.serde.rst | 41 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 docs/source/ads.model.serde.rst diff --git a/README.md b/README.md index ca21db925..40a319ba7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![PyPI](https://img.shields.io/pypi/v/oracle-ads.svg)](https://pypi.org/project/oracle-ads/) [![Python](https://img.shields.io/pypi/pyversions/oracle-ads.svg?style=plastic)](https://pypi.org/project/oracle-ads/) -The [Oracle Accelerated Data Science (ADS) SDK](https://accelerated-data-science.readthedocs.io/en/latest/index.html) is maintained by the Oracle Cloud Infrastructure (OCI) [Data Science service](https://docs.oracle.com/en-us/iaas/data-science/using/data-science.htm) team. It speeds up common data science activities by providing tools that automate and simplify common data science tasks. Additionally, provides data scientists a friendly pythonic interface to OCI services. Some of the more notable services are OCI Data Science, Model Catalog, Model Deployment, Jobs, Data Flow, Object Storage, Vault, Big Data Service, Data Catalog, and the Autonomous Database. ADS gives you an interface to manage the life cycle of machine learning models, from data acquisition to model evaluation, interpretation, and model deployment. +The [Oracle Accelerated Data Science (ADS) SDK](https://accelerated-data-science.readthedocs.io/en/latest/index.html) is maintained by the Oracle Cloud Infrastructure (OCI) [Data Science service](https://docs.oracle.com/en-us/iaas/data-science/using/data-science.htm) team. It speeds up common data science activities by providing tools that automate and simplify common data science tasks. Additionally, provides data scientists a friendly pythonic interface to OCI services. Some of the more notable services are OCI Data Science, Model Catalog, Model Deployment, Jobs, ML Pipelines, Data Flow, Object Storage, Vault, Big Data Service, Data Catalog, and the Autonomous Database. ADS gives you an interface to manage the life cycle of machine learning models, from data acquisition to model evaluation, interpretation, and model deployment. With ADS you can: @@ -14,6 +14,7 @@ With ADS you can: - Deploy models as HTTP endpoints with [Model Deployment](https://docs.oracle.com/en-us/iaas/data-science/using/model-dep-about.htm). - Launch distributed ETL, data processing, and model training jobs in Spark with [OCI Data Flow](https://docs.oracle.com/en-us/iaas/data-flow/using/home.htm). - Train machine learning models in OCI Data Science [Jobs](https://docs.oracle.com/en-us/iaas/data-science/using/jobs-about.htm). + - Define and run an end-to-end machine learning orchestration covering all the steps of machine learning lifecycle in a repeatable, continuous [ML Pipelines](https://accelerated-data-science.readthedocs.io/en/latest/user_guide/pipeline/overview.html#). - Manage the life cycle of conda environments through the `ads conda` command line interface (CLI). ## Installation diff --git a/docs/source/ads.model.framework.rst b/docs/source/ads.model.framework.rst index db4760ff5..5397170ab 100644 --- a/docs/source/ads.model.framework.rst +++ b/docs/source/ads.model.framework.rst @@ -4,6 +4,24 @@ ads.model.framework package Submodules ---------- +ads.model.framework.automl\_model module +---------------------------------------- + +.. automodule:: ads.model.framework.automl_model + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + +ads.model.framework.huggingface\_model module +--------------------------------------------- + +.. automodule:: ads.model.framework.huggingface_model + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + ads.model.framework.lightgbm\_model module ------------------------------------------ @@ -11,6 +29,7 @@ ads.model.framework.lightgbm\_model module :members: :undoc-members: :show-inheritance: + :inherited-members: ads.model.framework.pytorch\_model module ----------------------------------------- @@ -19,6 +38,7 @@ ads.model.framework.pytorch\_model module :members: :undoc-members: :show-inheritance: + :inherited-members: ads.model.framework.sklearn\_model module ----------------------------------------- @@ -27,6 +47,7 @@ ads.model.framework.sklearn\_model module :members: :undoc-members: :show-inheritance: + :inherited-members: ads.model.framework.spark\_model module --------------------------------------- @@ -35,6 +56,7 @@ ads.model.framework.spark\_model module :members: :undoc-members: :show-inheritance: + :inherited-members: ads.model.framework.tensorflow\_model module -------------------------------------------- @@ -43,6 +65,7 @@ ads.model.framework.tensorflow\_model module :members: :undoc-members: :show-inheritance: + :inherited-members: ads.model.framework.xgboost\_model module ----------------------------------------- @@ -51,14 +74,7 @@ ads.model.framework.xgboost\_model module :members: :undoc-members: :show-inheritance: - -ads.model.framework.huggingface\_model module ------------------------------------------ - -.. automodule:: ads.model.framework.huggingface_model - :members: - :undoc-members: - :show-inheritance: + :inherited-members: Module contents --------------- @@ -67,3 +83,4 @@ Module contents :members: :undoc-members: :show-inheritance: + :inherited-members: diff --git a/docs/source/ads.model.serde.rst b/docs/source/ads.model.serde.rst new file mode 100644 index 000000000..942c938e0 --- /dev/null +++ b/docs/source/ads.model.serde.rst @@ -0,0 +1,41 @@ +ads.model.serde package +======================= + +Submodules +---------- + +ads.model.serde.common module +----------------------------- + +.. automodule:: ads.model.serde.common + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + +ads.model.serde.model\_input module +----------------------------------- + +.. automodule:: ads.model.serde.model_input + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + +ads.model.serde.model\_serializer module +---------------------------------------- + +.. automodule:: ads.model.serde.model_serializer + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + +Module contents +--------------- + +.. automodule:: ads.model.serde + :members: + :undoc-members: + :show-inheritance: + :inherited-members: From ffadd6c3f12a2408db3b33bbc8c47c2e78ca35c0 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Sun, 19 Mar 2023 21:40:52 -0700 Subject: [PATCH 128/147] remove automl --- docs/source/ads.model.framework.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/source/ads.model.framework.rst b/docs/source/ads.model.framework.rst index 5397170ab..72a5cc5f3 100644 --- a/docs/source/ads.model.framework.rst +++ b/docs/source/ads.model.framework.rst @@ -4,15 +4,6 @@ ads.model.framework package Submodules ---------- -ads.model.framework.automl\_model module ----------------------------------------- - -.. automodule:: ads.model.framework.automl_model - :members: - :undoc-members: - :show-inheritance: - :inherited-members: - ads.model.framework.huggingface\_model module --------------------------------------------- From eddeae7bf8f282ff72050ef696b9c246de2efa2a Mon Sep 17 00:00:00 2001 From: Lu Peng Date: Mon, 20 Mar 2023 09:32:44 -0700 Subject: [PATCH 129/147] Updated service doc link --- .../user_guide/model_registration/model_deploy_byoc.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/model_registration/model_deploy_byoc.rst b/docs/source/user_guide/model_registration/model_deploy_byoc.rst index 665b41e4d..7692f6911 100644 --- a/docs/source/user_guide/model_registration/model_deploy_byoc.rst +++ b/docs/source/user_guide/model_registration/model_deploy_byoc.rst @@ -3,12 +3,12 @@ Deploy Model on Container Runtime The ADS ``GenericModel`` and ``ModelDeployment`` classes allow you to run a container image using OCI data science model deployment. -To deploy model on container runtime, you need to first build a docker container image. See `` for the end-to-end example. Once you have the image, push it to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. +To deploy model on container runtime, you need to first build a docker container image. See `Bring Your Own Container `_ for the end-to-end example. Once you have the image, push it to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. Deploy Using GenericModel Class =============================== -When the container runtime is ready, you can call ``deploy`` function to deploy the model and generate the endpoint. You must specify the container ``deployment_image``. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). For more details regarding the parameters allowed for container runtime, see ``. +When the container runtime is ready, you can call ``deploy`` function to deploy the model and generate the endpoint. You must specify the container ``deployment_image``. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). For more details regarding the parameters allowed for container runtime, see `BYOC Required Interfaces `_. Below is an example of deploying Sklearn model on container runtime using ``SklearnModel`` class: @@ -68,7 +68,7 @@ Deploy Using ModelDeployment Class To deploy a model deployment, you can define a ``ModelDeployment`` object and call the ``.deploy()`` of it. You could either use API or YAML to define the ``ModelDeployment`` object. -When configuring the ``ModelDeploymentContainerRuntime`` object, you must specify the container `image`. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). For more details regarding the parameters allowed for container runtime, see ``. +When configuring the ``ModelDeploymentContainerRuntime`` object, you must specify the container `image`. You can optionally specify the `entrypoint` and `cmd` for running the container (See `Understand how CMD and ENTRYPOINT interact `_). For more details regarding the parameters allowed for container runtime, see `BYOC Required Interfaces `_. Below is an example of deploying model on container runtime using ``ModelDeployment`` class: From 2bf51e177a267d99f5bc7c72a88dc9cb5fe9ed79 Mon Sep 17 00:00:00 2001 From: Lu Peng Date: Mon, 20 Mar 2023 12:22:06 -0700 Subject: [PATCH 130/147] Updated pr. --- docs/source/user_guide/model_registration/model_deploy.rst | 4 ++-- .../user_guide/model_registration/model_deploy_byoc.rst | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/user_guide/model_registration/model_deploy.rst b/docs/source/user_guide/model_registration/model_deploy.rst index d7cb3805a..43761516d 100644 --- a/docs/source/user_guide/model_registration/model_deploy.rst +++ b/docs/source/user_guide/model_registration/model_deploy.rst @@ -1,5 +1,5 @@ -Deploy Model on Conda Runtime -***************************** +Deploy Model with Conda Runtime +******************************* Once you have ADS Model object, you can call ``deploy`` function to deploy the model and generate the endpoint. diff --git a/docs/source/user_guide/model_registration/model_deploy_byoc.rst b/docs/source/user_guide/model_registration/model_deploy_byoc.rst index 7692f6911..7996932b9 100644 --- a/docs/source/user_guide/model_registration/model_deploy_byoc.rst +++ b/docs/source/user_guide/model_registration/model_deploy_byoc.rst @@ -1,7 +1,7 @@ -Deploy Model on Container Runtime -********************************* +Deploy Model with Container Runtime +*********************************** -The ADS ``GenericModel`` and ``ModelDeployment`` classes allow you to run a container image using OCI data science model deployment. +The ADS ``GenericModel`` and ``ModelDeployment`` classes allow you to deploy model using container images. To deploy model on container runtime, you need to first build a docker container image. See `Bring Your Own Container `_ for the end-to-end example. Once you have the image, push it to `OCI container registry `_. See `Creating a Repository `_ and `Pushing Images Using the Docker CLI `_ for more details. From 6ee00ec5b71bf96c4e7c0980c20be7036763806f Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 20 Mar 2023 15:26:20 -0700 Subject: [PATCH 131/147] ODSC-40388: fix the dataflow doc --- docs/source/user_guide/apachespark/quickstart.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index c5786bbc8..d1b721e4c 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -4,14 +4,14 @@ Quick Start Data Flow is a hosted Apache Spark server. It is quick to start, and can scale to handle large datasets in parallel. ADS provides a convenient API for creating and maintaining workloads on Data Flow. -Submit a Dummy Python Script to DataFlow -======================================== +Submit a Toy Python Script to DataFlow +====================================== From a Python Environment ------------------------- Submit a python script to DataFlow entirely from your python environment. -The following snippet uses a dummy python script that prints "Hello World" +The following snippet uses a toy python script that prints "Hello World" followed by the spark version, 3.2.1. .. code-block:: python @@ -33,8 +33,7 @@ followed by the spark version, 3.2.1. if __name__ == "__main__": main() - """ - ) + name = f"dataflow-app-{str(uuid4())}" dataflow_configs = ( DataFlow() @@ -54,6 +53,8 @@ followed by the spark version, 3.2.1. df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) df.create() df_run = df.run() + """ + ) From the Command Line --------------------- From e08753c5c20b4d15896feb11dc43854e6536291a Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 20 Mar 2023 16:16:46 -0700 Subject: [PATCH 132/147] ODSC-40388: fix the format --- .../source/user_guide/apachespark/quickstart.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index d1b721e4c..995f7cb45 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -25,14 +25,16 @@ followed by the spark version, 3.2.1. with open(os.path.join(td, "script.py"), "w") as f: f.write( """ - import pyspark + import pyspark - def main(): - print("Hello World") - print("Spark version is", pyspark.__version__) + def main(): + print("Hello World") + print("Spark version is", pyspark.__version__) - if __name__ == "__main__": - main() + if __name__ == "__main__": + main() + """ + ) name = f"dataflow-app-{str(uuid4())}" dataflow_configs = ( @@ -53,8 +55,6 @@ followed by the spark version, 3.2.1. df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) df.create() df_run = df.run() - """ - ) From the Command Line --------------------- From bffce5332af5284f54622eba11447073eee3e133 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 20 Mar 2023 16:29:17 -0700 Subject: [PATCH 133/147] ODSC-40388: fix the formatting --- docs/source/user_guide/apachespark/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index 995f7cb45..570d4c447 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -33,7 +33,7 @@ followed by the spark version, 3.2.1. if __name__ == "__main__": main() - """ + """ ) name = f"dataflow-app-{str(uuid4())}" From 624fb71f780dd8d8077236800ef199543d8cca0e Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 20 Mar 2023 16:37:43 -0700 Subject: [PATCH 134/147] adding release notes --- docs/source/release_notes.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index e371a55a4..744dd528e 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -2,6 +2,12 @@ Release Notes ============= +2.8.3 +----- +Release date: March 21, 2023 + +* Added support for BYOC and environment variables for :py:class:`~ads.model.GenericModel`. + 2.8.2 ----- Release date: March 2, 2023 From 66667c9bddecde9b322842d116168b6a77a27eff Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Mon, 20 Mar 2023 16:43:05 -0700 Subject: [PATCH 135/147] adding release notes --- docs/source/release_notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index 744dd528e..ceb718806 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -7,6 +7,7 @@ Release Notes Release date: March 21, 2023 * Added support for BYOC and environment variables for :py:class:`~ads.model.GenericModel`. +* Added default values for configuring parameters in :py:class:`~ads.model.ModelDeployment`, such as default flex shape, ocpus, memory in gbs, bandwidth, instance count. 2.8.2 ----- From b37fbccea1f93fcaf78803113be6a55ba10cac52 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Wed, 8 Mar 2023 09:15:01 -0500 Subject: [PATCH 136/147] Update model_artifact.rst to add region identifier to policy example. --- docs/source/user_guide/model_registration/model_artifact.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/user_guide/model_registration/model_artifact.rst b/docs/source/user_guide/model_registration/model_artifact.rst index fc8be4934..484996963 100644 --- a/docs/source/user_guide/model_registration/model_artifact.rst +++ b/docs/source/user_guide/model_registration/model_artifact.rst @@ -171,8 +171,11 @@ If you don't have an Object Storage bucket, create one using the OCI SDK or the Allow service datascience to manage object-family in compartment where ALL {target.bucket.name=''} - Allow service objectstorage to manage object-family in compartment where ALL {target.bucket.name=''} + Allow service objectstorage- to manage object-family in compartment where ALL {target.bucket.name=''} +Because Object Storage is a regional service, you must authorize the Object Storage service for each region. +To determine the region identifier value of an Oracle Cloud Infrastructure region, +see `Regions and Availability Domains `_. See `API documentation <../../ads.model.html#id15>`__ for more details. The following saves the :doc:`framework specific wrapper ` object, ``model``, to the model catalog and returns the OCID from the model catalog: From 801477b8a058ed3308072d5af680089d0d04c339 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 21 Mar 2023 09:43:04 -0700 Subject: [PATCH 137/147] change temp dir to current dir --- .../model_registration/frameworks/tensorflowmodel.rst | 11 ++--------- .../user_guide/model_registration/quick_start.rst | 3 +-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst b/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst index 1e743f863..d7c2e606c 100644 --- a/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst @@ -43,10 +43,8 @@ Prepare Model Artifact from ads.common.model_metadata import UseCaseType from ads.model.framework.tensorflow_model import TensorFlowModel - import tempfile - artifact_dir = tempfile.mkdtemp() - tensorflow_model = TensorFlowModel(estimator=model, artifact_dir=artifact_dir) + tensorflow_model = TensorFlowModel(estimator=model, artifact_dir="./") tensorflow_model.prepare( inference_conda_env="tensorflow28_p38_cpu_v1", training_conda_env="tensorflow28_p38_cpu_v1", @@ -233,8 +231,6 @@ Example import tensorflow as tf - import tempfile - # Load MNIST Data mnist = tf.keras.datasets.mnist (trainx, trainy), (testx, testy) = mnist.load_data() @@ -253,11 +249,8 @@ Example model.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) model.fit(trainx, trainy, epochs=1) - - artifact_dir = tempfile.mkdtemp() - # Prepare Model Artifact for TensorFlow model - tensorflow_model = TensorFlowModel(estimator=model, artifact_dir=artifact_dir) + tensorflow_model = TensorFlowModel(estimator=model, artifact_dir="./") tensorflow_model.prepare( inference_conda_env="tensorflow28_p38_cpu_v1", training_conda_env="tensorflow28_p38_cpu_v1", diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 1752eff5a..063eedc23 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -246,7 +246,6 @@ Create a model, prepare it, verify that it works, save it to the model catalog, .. code-block:: python3 from ads.model.framework.tensorflow_model import TensorFlowModel - import tempfile import tensorflow as tf mnist = tf.keras.datasets.mnist @@ -266,7 +265,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, tf_estimator.fit(x_train, y_train, epochs=1) # Instantite ads.model.framework.tensorflow_model.TensorFlowModel using the pre-trained TensorFlow Model - tf_model = TensorFlowModel(tf_estimator, artifact_dir=tempfile.mkdtemp()) + tf_model = TensorFlowModel(tf_estimator, artifact_dir="./") # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json tf_model.prepare(inference_conda_env="tensorflow28_p38_cpu_v1") From 7cb04593db2466c601c2811689009d8bdcdcfe82 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 21 Mar 2023 11:33:08 -0700 Subject: [PATCH 138/147] ODSC-40388: fix the folder --- .../model_registration/frameworks/tensorflowmodel.rst | 6 ++++-- .../source/user_guide/model_registration/model_artifact.rst | 4 ++-- .../model_registration/model_file_customization.rst | 4 ++-- docs/source/user_guide/model_registration/quick_start.rst | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst b/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst index d7c2e606c..60f5429a4 100644 --- a/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst +++ b/docs/source/user_guide/model_registration/frameworks/tensorflowmodel.rst @@ -43,8 +43,9 @@ Prepare Model Artifact from ads.common.model_metadata import UseCaseType from ads.model.framework.tensorflow_model import TensorFlowModel + from uuid import uuid4 - tensorflow_model = TensorFlowModel(estimator=model, artifact_dir="./") + tensorflow_model = TensorFlowModel(estimator=model, artifact_dir=f"./model-artifact-{str(uuid4())}") tensorflow_model.prepare( inference_conda_env="tensorflow28_p38_cpu_v1", training_conda_env="tensorflow28_p38_cpu_v1", @@ -230,6 +231,7 @@ Example from ads.model.framework.tensorflow_model import TensorFlowModel import tensorflow as tf + from uuid import uuid4 # Load MNIST Data mnist = tf.keras.datasets.mnist @@ -250,7 +252,7 @@ Example model.fit(trainx, trainy, epochs=1) # Prepare Model Artifact for TensorFlow model - tensorflow_model = TensorFlowModel(estimator=model, artifact_dir="./") + tensorflow_model = TensorFlowModel(estimator=model, artifact_dir=f"./model-artifact-{str(uuid4())}") tensorflow_model.prepare( inference_conda_env="tensorflow28_p38_cpu_v1", training_conda_env="tensorflow28_p38_cpu_v1", diff --git a/docs/source/user_guide/model_registration/model_artifact.rst b/docs/source/user_guide/model_registration/model_artifact.rst index fc8be4934..2538e8a8b 100644 --- a/docs/source/user_guide/model_registration/model_artifact.rst +++ b/docs/source/user_guide/model_registration/model_artifact.rst @@ -46,7 +46,7 @@ Here is an example for preparing a model artifact for ``TensorFlow`` model. .. code-block:: python3 from ads.model.framework.tensorflow_model import TensorFlowModel - import tempfile + from uuid import uuid4 import tensorflow as tf from ads.common.model_metadata import UseCaseType @@ -66,7 +66,7 @@ Here is an example for preparing a model artifact for ``TensorFlow`` model. tf_estimator.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) tf_estimator.fit(x_train, y_train, epochs=1) - tf_model = TensorFlowModel(tf_estimator, artifact_dir=tempfile.mkdtemp()) + tf_model = TensorFlowModel(tf_estimator, artifact_dir=f"./model-artifact-{str(uuid4())}") # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json tf_model.prepare(inference_conda_env="generalml_p38_cpu_v1", diff --git a/docs/source/user_guide/model_registration/model_file_customization.rst b/docs/source/user_guide/model_registration/model_file_customization.rst index b10e7d045..bd351a27b 100644 --- a/docs/source/user_guide/model_registration/model_file_customization.rst +++ b/docs/source/user_guide/model_registration/model_file_customization.rst @@ -12,8 +12,8 @@ Step1: Train your estimator and then generate the Model artifact as shown below from ads.catalog.model import ModelCatalog from ads.model.framework.tensorflow_model import TensorFlowModel - import tempfile import tensorflow as tf + from uuid import uuid4 from ads.common.model_metadata import UseCaseType mnist = tf.keras.datasets.mnist @@ -32,7 +32,7 @@ Step1: Train your estimator and then generate the Model artifact as shown below tf_estimator.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) tf_estimator.fit(x_train, y_train, epochs=1) - tf_model = TensorFlowModel(tf_estimator, artifact_dir=tempfile.mkdtemp()) + tf_model = TensorFlowModel(tf_estimator, artifact_dir=f"./model-artifact-{str(uuid4())}") # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json tf_model.prepare(inference_conda_env="generalml_p38_cpu_v1", diff --git a/docs/source/user_guide/model_registration/quick_start.rst b/docs/source/user_guide/model_registration/quick_start.rst index 063eedc23..c3bece9c2 100644 --- a/docs/source/user_guide/model_registration/quick_start.rst +++ b/docs/source/user_guide/model_registration/quick_start.rst @@ -247,6 +247,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, from ads.model.framework.tensorflow_model import TensorFlowModel import tensorflow as tf + from uuid import uuid4 mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() @@ -265,7 +266,7 @@ Create a model, prepare it, verify that it works, save it to the model catalog, tf_estimator.fit(x_train, y_train, epochs=1) # Instantite ads.model.framework.tensorflow_model.TensorFlowModel using the pre-trained TensorFlow Model - tf_model = TensorFlowModel(tf_estimator, artifact_dir="./") + tf_model = TensorFlowModel(tf_estimator, artifact_dir=f"./model-artifact-{str(uuid4())}") # Autogenerate score.py, pickled model, runtime.yaml, input_schema.json and output_schema.json tf_model.prepare(inference_conda_env="tensorflow28_p38_cpu_v1") From 598caca6c1e65828cc03de532ffebe0ed74bb38c Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 21 Mar 2023 11:35:53 -0700 Subject: [PATCH 139/147] fix the indent --- .../source/user_guide/apachespark/quickstart.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index 570d4c447..f5d78aa6f 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -24,16 +24,16 @@ followed by the spark version, 3.2.1. with tempfile.TemporaryDirectory() as td: with open(os.path.join(td, "script.py"), "w") as f: f.write( - """ - import pyspark + """ + import pyspark - def main(): - print("Hello World") - print("Spark version is", pyspark.__version__) + def main(): + print("Hello World") + print("Spark version is", pyspark.__version__) - if __name__ == "__main__": - main() - """ + if __name__ == "__main__": + main() + """ ) name = f"dataflow-app-{str(uuid4())}" From 189259898a8423cb289484ae4b946d4a4da69197 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 21 Mar 2023 12:36:03 -0700 Subject: [PATCH 140/147] fix indent --- .../user_guide/apachespark/quickstart.rst | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index f5d78aa6f..28b1dcd90 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -21,21 +21,20 @@ followed by the spark version, 3.2.1. import os import tempfile - with tempfile.TemporaryDirectory() as td: - with open(os.path.join(td, "script.py"), "w") as f: - f.write( - """ - import pyspark + SCRIPT_CONTENT = """ + import pyspark - def main(): - print("Hello World") - print("Spark version is", pyspark.__version__) + def main(): + print("Hello World") + print("Spark version is", pyspark.__version__) - if __name__ == "__main__": - main() - """ - ) + if __name__ == "__main__": + main() + """ + with tempfile.TemporaryDirectory() as td: + with open(os.path.join(td, "script.py"), "w") as f: + f.write(SCRIPT_CONTENT) name = f"dataflow-app-{str(uuid4())}" dataflow_configs = ( DataFlow() From 74535f57bf5916cbbb02ff3f33227eaf60fa6801 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Tue, 21 Mar 2023 12:42:33 -0700 Subject: [PATCH 141/147] fix comments --- docs/source/release_notes.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index ceb718806..f6bf60098 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -6,8 +6,9 @@ Release Notes ----- Release date: March 21, 2023 -* Added support for BYOC and environment variables for :py:class:`~ads.model.GenericModel`. -* Added default values for configuring parameters in :py:class:`~ads.model.ModelDeployment`, such as default flex shape, ocpus, memory in gbs, bandwidth, instance count. +* Added support for :py:class:`~ads.jobs.NotebookRuntime` to use directory as job artifact. +* Added support for custom containers (Bring Your Own Container or BYOC) and environment variables for :py:class:`~ads.model.GenericModel`. +* Added default values for configuring parameters in :py:class:`~ads.model.ModelDeployment`, such as default flex shape, ocpus, memory in gbs, bandwidth, and instance count. 2.8.2 ----- From dcc9d9b875c60b882010a53c14675f4990880a83 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Mar 2023 16:05:18 -0400 Subject: [PATCH 142/147] Update release_notes.rst --- docs/source/release_notes.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index f6bf60098..d6ceea429 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -6,9 +6,10 @@ Release Notes ----- Release date: March 21, 2023 -* Added support for :py:class:`~ads.jobs.NotebookRuntime` to use directory as job artifact. * Added support for custom containers (Bring Your Own Container or BYOC) and environment variables for :py:class:`~ads.model.GenericModel`. * Added default values for configuring parameters in :py:class:`~ads.model.ModelDeployment`, such as default flex shape, ocpus, memory in gbs, bandwidth, and instance count. +* Added support for :py:class:`~ads.jobs.NotebookRuntime` to use directory as job artifact. +* Added support for :py:class:`~ads.jobs.PythonRuntime` and :py:class:`~ads.jobs.GitPythonRuntime` to use shell script as entrypoint. 2.8.2 ----- From dc471ea5ae6ccc4ddd06a36e2303d59f46405160 Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Mar 2023 16:38:50 -0400 Subject: [PATCH 143/147] Change tab to space in quickstart.rst. --- .../user_guide/apachespark/quickstart.rst | 444 +++++++++--------- 1 file changed, 222 insertions(+), 222 deletions(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index 28b1dcd90..d2ded8175 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -16,44 +16,44 @@ followed by the spark version, 3.2.1. .. code-block:: python - from ads.jobs import DataFlow, Job, DataFlowRuntime - from uuid import uuid4 - import os - import tempfile - - SCRIPT_CONTENT = """ - import pyspark - - def main(): - print("Hello World") - print("Spark version is", pyspark.__version__) - - if __name__ == "__main__": - main() - """ - - with tempfile.TemporaryDirectory() as td: - with open(os.path.join(td, "script.py"), "w") as f: - f.write(SCRIPT_CONTENT) - name = f"dataflow-app-{str(uuid4())}" - dataflow_configs = ( - DataFlow() - .with_compartment_id("oci.xx.") - .with_logs_bucket_uri("oci://@/") - .with_driver_shape("VM.Standard.E4.Flex") - .with_driver_shape_config(ocpus=2, memory_in_gbs=32) - .with_executor_shape("VM.Standard.E4.Flex") - .with_executor_shape_config(ocpus=4, memory_in_gbs=64) - .with_spark_version("3.2.1") - ) - runtime_config = ( - DataFlowRuntime() - .with_script_uri(os.path.join(td, "script.py")) - .with_script_bucket("oci://@/") - ) - df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) - df.create() - df_run = df.run() + from ads.jobs import DataFlow, Job, DataFlowRuntime + from uuid import uuid4 + import os + import tempfile + + SCRIPT_CONTENT = """ + import pyspark + + def main(): + print("Hello World") + print("Spark version is", pyspark.__version__) + + if __name__ == "__main__": + main() + """ + + with tempfile.TemporaryDirectory() as td: + with open(os.path.join(td, "script.py"), "w") as f: + f.write(SCRIPT_CONTENT) + name = f"dataflow-app-{str(uuid4())}" + dataflow_configs = ( + DataFlow() + .with_compartment_id("oci.xx.") + .with_logs_bucket_uri("oci://@/") + .with_driver_shape("VM.Standard.E4.Flex") + .with_driver_shape_config(ocpus=2, memory_in_gbs=32) + .with_executor_shape("VM.Standard.E4.Flex") + .with_executor_shape_config(ocpus=4, memory_in_gbs=64) + .with_spark_version("3.2.1") + ) + runtime_config = ( + DataFlowRuntime() + .with_script_uri(os.path.join(td, "script.py")) + .with_script_bucket("oci://@/") + ) + df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) + df.create() + df_run = df.run() From the Command Line --------------------- @@ -65,27 +65,27 @@ Assuming you have the following two files written in your current directory as ` .. code-block:: python - # script.py - import pyspark - def main(): - print("Hello World") - print("Spark version is", pyspark.__version__) - if __name__ == "__main__": - main() + # script.py + import pyspark + def main(): + print("Hello World") + print("Spark version is", pyspark.__version__) + if __name__ == "__main__": + main() .. code-block:: yaml - # dataflow.yaml - kind: job - spec: - name: dataflow-app- - infrastructure: - kind: infrastructure - spec: - compartmentId: oci.xx. - logsBucketUri: oci://@/ - driverShape: VM.Standard.E4.Flex + # dataflow.yaml + kind: job + spec: + name: dataflow-app- + infrastructure: + kind: infrastructure + spec: + compartmentId: oci.xx. + logsBucketUri: oci://@/ + driverShape: VM.Standard.E4.Flex driverShapeConfig: ocpus: 2 memory_in_gbs: 32 @@ -93,19 +93,19 @@ Assuming you have the following two files written in your current directory as ` executorShapeConfig: ocpus: 4 memory_in_gbs: 64 - sparkVersion: 3.2.1 - numExecutors: 1 - type: dataFlow - runtime: - kind: runtime - spec: - scriptUri: script.py - scriptBucket: oci://@/ + sparkVersion: 3.2.1 + numExecutors: 1 + type: dataFlow + runtime: + kind: runtime + spec: + scriptUri: script.py + scriptBucket: oci://@/ .. code-block:: shell - ads jobs run -f dataflow.yaml + ads jobs run -f dataflow.yaml Real Data Flow Example with Conda Environment @@ -119,83 +119,83 @@ From a Python Environment .. code-block:: python - from ads.jobs import DataFlow, Job, DataFlowRuntime - from uuid import uuid4 - import os - import tempfile - - with tempfile.TemporaryDirectory() as td: - with open(os.path.join(td, "script.py"), "w") as f: - f.write( - ''' - from pyspark.sql import SparkSession - import click - - @click.command() - @click.argument("app_name") - @click.option( - "--limit", "-l", help="max number of row to print", default=10, required=False - ) - @click.option("--verbose", "-v", help="print out result in verbose mode", is_flag=True) - def main(app_name, limit, verbose): - Create a Spark session - spark = SparkSession.builder.appName(app_name).getOrCreate() - - Load a csv file from dataflow public storage - df = ( - spark.read.format("csv") - .option("header", "true") - .option("multiLine", "true") - .load( - "oci://oow_2019_dataflow_lab@bigdatadatasciencelarge/usercontent/kaggle_berlin_airbnb_listings_summary.csv" - ) - ) - - Create a temp view and do some SQL operations - df.createOrReplaceTempView("berlin") - query_result_df = spark.sql( - """ - SELECT - city, - zipcode, - CONCAT(latitude,',', longitude) AS lat_long - FROM berlin - """ - ).limit(limit) - - # Convert the filtered Spark DataFrame into JSON format - # Note: we are writing to the spark stdout log so that we can retrieve the log later at the end of the notebook. - if verbose: - rows = query_result_df.toJSON().collect() - for i, row in enumerate(rows): - print(f"record {i}") - print(row) - - if __name__ == "__main__": - main() - ''' - ) - name = f"dataflow-app-{str(uuid4())}" - dataflow_configs = ( - DataFlow() - .with_compartment_id("oci.xx.") - .with_logs_bucket_uri("oci://@/") - .with_driver_shape("VM.Standard.E4.Flex") - .with_driver_shape_config(ocpus=2, memory_in_gbs=32) - .with_executor_shape("VM.Standard.E4.Flex") - .with_executor_shape_config(ocpus=4, memory_in_gbs=64) - .with_spark_version("3.2.1") - ) - runtime_config = ( - DataFlowRuntime() - .with_script_uri(os.path.join(td, "script.py")) - .with_script_bucket("oci://@/") - .with_custom_conda(uri="oci://@/") - .with_arguments(["run-test", "-v", "-l", "5"]) - ) - df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) - df.create() - df_run = df.run() + from ads.jobs import DataFlow, Job, DataFlowRuntime + from uuid import uuid4 + import os + import tempfile + + with tempfile.TemporaryDirectory() as td: + with open(os.path.join(td, "script.py"), "w") as f: + f.write( + ''' + from pyspark.sql import SparkSession + import click + + @click.command() + @click.argument("app_name") + @click.option( + "--limit", "-l", help="max number of row to print", default=10, required=False + ) + @click.option("--verbose", "-v", help="print out result in verbose mode", is_flag=True) + def main(app_name, limit, verbose): + Create a Spark session + spark = SparkSession.builder.appName(app_name).getOrCreate() + + Load a csv file from dataflow public storage + df = ( + spark.read.format("csv") + .option("header", "true") + .option("multiLine", "true") + .load( + "oci://oow_2019_dataflow_lab@bigdatadatasciencelarge/usercontent/kaggle_berlin_airbnb_listings_summary.csv" + ) + ) + + Create a temp view and do some SQL operations + df.createOrReplaceTempView("berlin") + query_result_df = spark.sql( + """ + SELECT + city, + zipcode, + CONCAT(latitude,',', longitude) AS lat_long + FROM berlin + """ + ).limit(limit) + + # Convert the filtered Spark DataFrame into JSON format + # Note: we are writing to the spark stdout log so that we can retrieve the log later at the end of the notebook. + if verbose: + rows = query_result_df.toJSON().collect() + for i, row in enumerate(rows): + print(f"record {i}") + print(row) + + if __name__ == "__main__": + main() + ''' + ) + name = f"dataflow-app-{str(uuid4())}" + dataflow_configs = ( + DataFlow() + .with_compartment_id("oci.xx.") + .with_logs_bucket_uri("oci://@/") + .with_driver_shape("VM.Standard.E4.Flex") + .with_driver_shape_config(ocpus=2, memory_in_gbs=32) + .with_executor_shape("VM.Standard.E4.Flex") + .with_executor_shape_config(ocpus=4, memory_in_gbs=64) + .with_spark_version("3.2.1") + ) + runtime_config = ( + DataFlowRuntime() + .with_script_uri(os.path.join(td, "script.py")) + .with_script_bucket("oci://@/") + .with_custom_conda(uri="oci://@/") + .with_arguments(["run-test", "-v", "-l", "5"]) + ) + df = Job(name=name, infrastructure=dataflow_configs, runtime=runtime_config) + df.create() + df_run = df.run() From the Command Line @@ -205,92 +205,92 @@ Again, assume you have the following two files written in your current directory .. code-block:: python - # script.py - from pyspark.sql import SparkSession - import click - - @click.command() - @click.argument("app_name") - @click.option( - "--limit", "-l", help="max number of row to print", default=10, required=False - ) - @click.option("--verbose", "-v", help="print out result in verbose mode", is_flag=True) - def main(app_name, limit, verbose): - Create a Spark session - spark = SparkSession.builder.appName(app_name).getOrCreate() - - Load a csv file from dataflow public storage - df = ( - spark.read.format("csv") - .option("header", "true") - .option("multiLine", "true") - .load( - "oci://oow_2019_dataflow_lab@bigdatadatasciencelarge/usercontent/kaggle_berlin_airbnb_listings_summary.csv" - ) - ) - - Create a temp view and do some SQL operations - df.createOrReplaceTempView("berlin") - query_result_df = spark.sql( - """ - SELECT - city, - zipcode, - CONCAT(latitude,',', longitude) AS lat_long - FROM berlin - """ - ).limit(limit) - - # Convert the filtered Spark DataFrame into JSON format - # Note: we are writing to the spark stdout log so that we can retrieve the log later at the end of the notebook. - if verbose: - rows = query_result_df.toJSON().collect() - for i, row in enumerate(rows): - print(f"record {i}") - print(row) - - - if __name__ == "__main__": - main() + # script.py + from pyspark.sql import SparkSession + import click + + @click.command() + @click.argument("app_name") + @click.option( + "--limit", "-l", help="max number of row to print", default=10, required=False + ) + @click.option("--verbose", "-v", help="print out result in verbose mode", is_flag=True) + def main(app_name, limit, verbose): + Create a Spark session + spark = SparkSession.builder.appName(app_name).getOrCreate() + + Load a csv file from dataflow public storage + df = ( + spark.read.format("csv") + .option("header", "true") + .option("multiLine", "true") + .load( + "oci://oow_2019_dataflow_lab@bigdatadatasciencelarge/usercontent/kaggle_berlin_airbnb_listings_summary.csv" + ) + ) + + Create a temp view and do some SQL operations + df.createOrReplaceTempView("berlin") + query_result_df = spark.sql( + """ + SELECT + city, + zipcode, + CONCAT(latitude,',', longitude) AS lat_long + FROM berlin + """ + ).limit(limit) + + # Convert the filtered Spark DataFrame into JSON format + # Note: we are writing to the spark stdout log so that we can retrieve the log later at the end of the notebook. + if verbose: + rows = query_result_df.toJSON().collect() + for i, row in enumerate(rows): + print(f"record {i}") + print(row) + + + if __name__ == "__main__": + main() .. code-block:: yaml - # dataflow.yaml - kind: job - spec: - name: dataflow-app- - infrastructure: - kind: infrastructure - spec: - compartmentId: oci.xx. - logsBucketUri: oci://@/ - driverShape: VM.Standard.E4.Flex - driverShapeConfig: - ocpus: 2 - memory_in_gbs: 32 - executorShape: VM.Standard.E4.Flex - executorShapeConfig: - ocpus: 4 - memory_in_gbs: 64 - sparkVersion: 3.2.1 - numExecutors: 1 - type: dataFlow - runtime: - kind: runtime - spec: - scriptUri: script.py - scriptBucket: oci://@/ - conda: - uri: oci://@/ - type: published - args: - - "run-test" - - "-v" - - "-l" - - "5" + # dataflow.yaml + kind: job + spec: + name: dataflow-app- + infrastructure: + kind: infrastructure + spec: + compartmentId: oci.xx. + logsBucketUri: oci://@/ + driverShape: VM.Standard.E4.Flex + driverShapeConfig: + ocpus: 2 + memory_in_gbs: 32 + executorShape: VM.Standard.E4.Flex + executorShapeConfig: + ocpus: 4 + memory_in_gbs: 64 + sparkVersion: 3.2.1 + numExecutors: 1 + type: dataFlow + runtime: + kind: runtime + spec: + scriptUri: script.py + scriptBucket: oci://@/ + conda: + uri: oci://@/ + type: published + args: + - "run-test" + - "-v" + - "-l" + - "5" .. code-block:: shell - ads jobs run -f dataflow.yaml \ No newline at end of file + ads jobs run -f dataflow.yaml From ab83f2e27edd057c5eabbd69144b0460aa71da6a Mon Sep 17 00:00:00 2001 From: Qiu Qin Date: Tue, 21 Mar 2023 16:42:32 -0400 Subject: [PATCH 144/147] Change tab to space in quickstart.rst. --- .../source/user_guide/apachespark/quickstart.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/user_guide/apachespark/quickstart.rst b/docs/source/user_guide/apachespark/quickstart.rst index d2ded8175..d97f76ad0 100644 --- a/docs/source/user_guide/apachespark/quickstart.rst +++ b/docs/source/user_guide/apachespark/quickstart.rst @@ -160,7 +160,7 @@ From a Python Environment zipcode, CONCAT(latitude,',', longitude) AS lat_long FROM berlin - """ + """ ).limit(limit) # Convert the filtered Spark DataFrame into JSON format @@ -232,13 +232,13 @@ Again, assume you have the following two files written in your current directory Create a temp view and do some SQL operations df.createOrReplaceTempView("berlin") query_result_df = spark.sql( - """ - SELECT - city, - zipcode, - CONCAT(latitude,',', longitude) AS lat_long - FROM berlin - """ + """ + SELECT + city, + zipcode, + CONCAT(latitude,',', longitude) AS lat_long + FROM berlin + """ ).limit(limit) # Convert the filtered Spark DataFrame into JSON format From 687dfc852f348cc54cb4ddc95790f265a61958e6 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 22 Mar 2023 16:07:24 -0700 Subject: [PATCH 145/147] update the ads folder and setup.py --- ads/__init__.py | 3 +- ads/ads_version.json | 2 +- ads/common/config.py | 118 +++- ads/common/oci_datascience.py | 5 +- ads/common/oci_logging.py | 22 +- ads/common/utils.py | 9 +- ads/config.py | 64 +- .../feature_type/handler/warnings.py | 10 +- ads/jobs/builders/infrastructure/dataflow.py | 4 +- .../infrastructure/dsc_job_runtime.py | 82 ++- ads/jobs/builders/runtimes/artifact.py | 142 +++-- ads/jobs/builders/runtimes/python_runtime.py | 131 ++-- ads/jobs/templates/driver_notebook.py | 15 +- ads/jobs/templates/driver_oci.py | 53 +- ads/jobs/templates/driver_utils.py | 158 ++--- ads/model/__init__.py | 24 + ads/model/artifact.py | 54 +- ads/model/base_properties.py | 8 +- ads/model/deployment/__init__.py | 10 +- ads/model/deployment/model_deployment.py | 59 +- ads/model/framework/huggingface_model.py | 2 +- ads/model/framework/lightgbm_model.py | 2 +- ads/model/framework/pytorch_model.py | 2 +- ads/model/framework/sklearn_model.py | 7 +- ads/model/framework/spark_model.py | 2 +- ads/model/framework/tensorflow_model.py | 2 +- ads/model/framework/xgboost_model.py | 2 +- ads/model/generic_model.py | 597 +++++++++++++----- ads/model/model_metadata_mixin.py | 7 +- ads/model/model_properties.py | 3 +- ads/opctl/backend/ads_dataflow.py | 15 +- ads/opctl/backend/ads_ml_job.py | 24 +- ads/opctl/backend/ads_ml_pipeline.py | 18 +- ads/opctl/backend/ads_model_deployment.py | 15 +- .../common/abstract_cluster_provider.py | 3 +- setup.py | 2 +- 36 files changed, 1142 insertions(+), 534 deletions(-) diff --git a/ads/__init__.py b/ads/__init__.py index ebb506589..13f01004d 100644 --- a/ads/__init__.py +++ b/ads/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from __future__ import print_function, division, absolute_import @@ -26,6 +26,7 @@ from ads.feature_engineering.accessor.dataframe_accessor import ADSDataFrameAccessor from ads.common import auth from ads.common.auth import set_auth +from ads.common.config import Config os.environ["GIT_PYTHON_REFRESH"] = "quiet" diff --git a/ads/ads_version.json b/ads/ads_version.json index 10441fff1..360e36224 100644 --- a/ads/ads_version.json +++ b/ads/ads_version.json @@ -1,3 +1,3 @@ { - "version": "2.8.2" + "version": "2.8.3" } diff --git a/ads/common/config.py b/ads/common/config.py index 4ba26a807..e79cfc884 100644 --- a/ads/common/config.py +++ b/ads/common/config.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import os @@ -9,10 +9,12 @@ from configparser import ConfigParser from copy import copy from enum import Enum -from typing import Callable, Dict, List, Optional, Tuple, Union, Any +from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from urllib.parse import urlparse import fsspec import yaml + from ads.common import auth as authutil from ads.common.decorator.argument_to_case import ArgumentCase, argument_to_case @@ -287,6 +289,7 @@ def __init__( self._config_parser = ExtendedConfigParser(uri=self.uri, auth=self.auth) def _on_change(self): + """This method will be called when config modified.""" pass def default(self) -> ConfigSection: @@ -345,7 +348,7 @@ def section_set( The new config section will be added in case if it doesn't exist. Otherwise the existing config section will be merged with the new fields. - Paramaters + Parameters ---------- key: str A key of a config section. @@ -362,7 +365,7 @@ def section_set( Raises ------ ValueError - If section with goven key is already exist and `replace` flag set to False. + If section with given key is already exist and `replace` flag set to False. TypeError If input `info` has a wrong format. """ @@ -389,7 +392,7 @@ def section_set( return self._config[key] @argument_to_case(case=ArgumentCase.UPPER, arguments=["key"]) - def section_remove(self, key: str) -> None: + def section_remove(self, key: str) -> "Config": """Removes config section form config. Parameters @@ -404,26 +407,62 @@ def section_remove(self, key: str) -> None: """ self._config.pop(key, None) self._on_change() + return self + + def save( + self, + uri: Optional[str] = None, + auth: Optional[Dict] = None, + force_overwrite: Optional[bool] = False, + ) -> "Config": + """Saves config to a config file. - def save(self) -> None: - """Saves config data to a config file. + Parameters + ---------- + uri: (str, optional). Defaults to `~/.ads/config`. + The path to the config file. Can be local or Object Storage file. + auth: (Dict, optional). Defaults to None. + The default authentication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + force_overwrite: (bool, optional). Defaults to `False`. + Overwrites the config if exists. Returns ------- None Nothing """ - self._config_parser.with_dict(self.to_dict()).save() + uri = uri or self.uri + auth = auth or self.auth or authutil.default_signer() + self._config_parser.with_dict(self.to_dict()).save( + uri=uri, auth=auth, force_overwrite=force_overwrite + ) + return self + + def load(self, uri: Optional[str] = None, auth: Optional[Dict] = None) -> "Config": + """Loads config from a config file. - def load(self) -> "Config": - """Loads config data from a config file. + Parameters + ---------- + uri: (str, optional). Defaults to `~/.ads/config`. + The path where the config file needs to be saved. Can be local or Object Storage file. + auth: (Dict, optional). Defaults to None. + The default authentication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. Returns ------- Config A config object. """ - return self.with_dict(self._config_parser.read().to_dict()) + uri = uri or self.uri + auth = auth or self.auth or authutil.default_signer() + + return self.with_dict( + self._config_parser.read(uri=uri, auth=auth).to_dict(), replace=True + ) def with_dict( self, @@ -507,7 +546,7 @@ def __init__( uri: (str, optional). Defaults to `~/.ads/config`. The path to the config file. Can be local or Object Storage file. auth: (Dict, optional). Defaults to None. - The default authetication is set using `ads.set_auth` API. If you need to override the + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. """ @@ -515,15 +554,46 @@ def __init__( self.auth = auth or authutil.default_signer() self.uri = uri - def save(self) -> None: + def save( + self, + uri: Optional[str] = None, + auth: Optional[Dict] = None, + force_overwrite: Optional[bool] = False, + ) -> None: """Saves the config to the file. + Parameters + ---------- + uri: (str, optional). Defaults to `~/.ads/config`. + The path to the config file. Can be local or Object Storage file. + auth: (Dict, optional). Defaults to None. + The default authentication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + force_overwrite: (bool, optional). Defaults to `False`. + Overwrites the config if exists. + Returns ------- None - nothing + + Raise + ----- + FileExistsError + In case if file exists and force_overwrite is false. """ - with fsspec.open(self.uri, mode="w", **self.auth) as f: + uri = uri or self.uri + auth = auth or self.auth or authutil.default_signer() + + if not force_overwrite: + dst_path_scheme = urlparse(uri).scheme or "file" + if fsspec.filesystem(dst_path_scheme, **auth).exists(uri): + raise FileExistsError( + f"The `{uri}` exists. Set `force_overwrite` to True " + "if you wish to overwrite." + ) + + with fsspec.open(uri, mode="w", **auth) as f: self.write(f) def to_dict(self) -> Dict[str, Any]: @@ -532,19 +602,31 @@ def to_dict(self) -> Dict[str, Any]: Returns ------- Dict[str, Any] - Cofig in a dictionary format. + Config in a dictionary format. """ return {s: dict(self[s]) for s in self.keys() if self[s]} - def read(self) -> "ExtendedConfigParser": + def read( + self, uri: Optional[str] = None, auth: Optional[Dict] = None + ) -> "ExtendedConfigParser": """Reads config file. + uri: (str, optional). Defaults to `~/.ads/config`. + The path to the config file. Can be local or Object Storage file. + auth: (Dict, optional). Defaults to None. + The default authentication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + Returns ------- ExtendedConfigParser Config parser object. """ - with fsspec.open(self.uri, "r", **self.auth) as f: + uri = uri or self.uri + auth = auth or self.auth or authutil.default_signer() + + with fsspec.open(uri, "r", **auth) as f: self.read_string(f.read()) return self diff --git a/ads/common/oci_datascience.py b/ads/common/oci_datascience.py index c33817c16..75bdfc353 100644 --- a/ads/common/oci_datascience.py +++ b/ads/common/oci_datascience.py @@ -1,15 +1,16 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import oci.data_science from ads.common.oci_mixin import OCIModelMixin +from ads.common.decorator.utils import class_or_instance_method class OCIDataScienceMixin(OCIModelMixin): - @classmethod + @class_or_instance_method def init_client(cls, **kwargs) -> oci.data_science.DataScienceClient: return cls._init_client(client=oci.data_science.DataScienceClient, **kwargs) diff --git a/ads/common/oci_logging.py b/ads/common/oci_logging.py index 64ad4acb9..c0c96e58b 100644 --- a/ads/common/oci_logging.py +++ b/ads/common/oci_logging.py @@ -11,6 +11,7 @@ import oci.logging import oci.loggingsearch +import oci.exceptions from ads.common.decorator.utils import class_or_instance_method from ads.common.oci_mixin import OCIModelMixin, OCIWorkRequestMixin from ads.common.oci_resource import OCIResource, ResourceNotFoundError @@ -370,12 +371,21 @@ def _search_logs( time_end=self.format_datetime(time_end), search_query=search_query, ) - response = self.search_client.search_logs( - search_details, limit=LIMIT_PER_REQUEST - ) - records = response.data.results - logger.debug("%d logs received.", len(records)) - if len(records) >= LIMIT_PER_REQUEST: + result_too_large = False + try: + response = self.search_client.search_logs( + search_details, limit=LIMIT_PER_REQUEST + ) + records = response.data.results + logger.debug("%d logs received.", len(records)) + except oci.exceptions.ServiceError as ex: + if ex.status == 400 and "search result is too large" in ex.message: + logger.debug(ex.message) + records = [] + result_too_large = True + else: + raise oci.exceptions.ServiceError from ex + if result_too_large or len(records) >= LIMIT_PER_REQUEST: mid = time_start + (time_end - time_start) / 2 # The log search API used RFC3339 time format. # The minimum time unit of RFC3339 format is 1000 microseconds. diff --git a/ads/common/utils.py b/ads/common/utils.py index d41577009..981216f00 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -1259,12 +1259,13 @@ def copy_from_uri( to_path = temp_dir else: unpack_path = None + fs = fsspec.filesystem(scheme, **auth) - try: - fs.get(uri, to_path, recursive=True) - except IsADirectoryError: + + if not (uri.endswith("/") or fs.isdir(uri)) and os.path.isdir(to_path): to_path = os.path.join(to_path, os.path.basename(str(uri).rstrip("/"))) - fs.get(uri, to_path, recursive=True) + + fs.get(uri, to_path, recursive=True) if unpack_path: shutil.unpack_archive(to_path, unpack_path) diff --git a/ads/config.py b/ads/config.py index 425ebbfa2..8b35e2cbc 100644 --- a/ads/config.py +++ b/ads/config.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8; -*- -# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# Copyright (c) 2020, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import contextlib @@ -38,6 +38,60 @@ NO_CONTAINER = os.environ.get("NO_CONTAINER") +def export( + uri: Optional[str] = DEFAULT_CONFIG_PATH, + auth: Dict = None, + force_overwrite: Optional[bool] = False, +) -> Config: + """Exports the ADS config. + + Parameters + ---------- + uri: (str, optional). Defaults to `~/.ads/config`. + The path to the config file. Can be local or Object Storage file. + auth: (Dict, optional). Defaults to None. + The default authentication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + force_overwrite: (bool, optional). Defaults to `False`. + Overwrites the config if exists. + + Returns + ------- + ads.Config + The ADS config object. + """ + return Config().load().save(uri=uri, auth=auth, force_overwrite=force_overwrite) + + +def load( + uri: Optional[str] = DEFAULT_CONFIG_PATH, + auth: Dict = None, + force_overwrite: Optional[bool] = False, +) -> Config: + """Imports the ADS config. + + The config will be imported from the URI and saved the `~/.ads/config`. + + Parameters + ---------- + uri: (str, optional). Defaults to `~/.ads/config`. + The path where the config file needs to be saved. Can be local or Object Storage file. + auth: (Dict, optional). Defaults to None. + The default authentication is set using `ads.set_auth` API. If you need to override the + default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate + authentication signer and kwargs required to instantiate IdentityClient object. + force_overwrite: (bool, optional). Defaults to `False`. + Overwrites the config if exists. + + Returns + ------- + ads.Config + The ADS config object. + """ + return Config().load(uri=uri, auth=auth).save(force_overwrite=force_overwrite) + + @contextlib.contextmanager def open( uri: Optional[str] = DEFAULT_CONFIG_PATH, @@ -56,7 +110,7 @@ def open( mode: (str, optional). Defaults to `r`. The config mode. Supported values: ['r', 'w'] auth: (Dict, optional). Defaults to None. - The default authetication is set using `ads.set_auth` API. If you need to override the + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. @@ -88,6 +142,8 @@ def open( defined_globals[key] = frame.f_globals[key] frame.f_globals[key] = section_obj[key] + frame.f_globals["config"] = section_obj + try: yield section_obj finally: @@ -95,10 +151,12 @@ def open( for key in section_keys: frame.f_globals.pop(key, None) + frame.f_globals.pop("config", None) + # Restores original globals for key in defined_globals.keys(): frame.f_globals[key] = defined_globals[key] # Saving config if it necessary if mode == Mode.WRITE: - config.save() + config.save(force_overwrite=True) diff --git a/ads/feature_engineering/feature_type/handler/warnings.py b/ads/feature_engineering/feature_type/handler/warnings.py index 709053979..b11fc730a 100644 --- a/ads/feature_engineering/feature_type/handler/warnings.py +++ b/ads/feature_engineering/feature_type/handler/warnings.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """ @@ -81,17 +81,17 @@ def high_cardinality_handler(s: pd.Series) -> pd.DataFrame: """ num_unique = s.nunique(dropna=False) df = pd.DataFrame([], columns=["Warning", "Message", "Metric", "Value"]) - if num_unique >= 15: + if num_unique == len(s): df.loc[0] = [ "high-cardinality", - f"{num_unique} unique values", + f"every value is distinct", "count", num_unique, ] - elif num_unique == len(s): + elif num_unique >= 15: df.loc[0] = [ "high-cardinality", - f"every value is distinct", + f"{num_unique} unique values", "count", num_unique, ] diff --git a/ads/jobs/builders/infrastructure/dataflow.py b/ads/jobs/builders/infrastructure/dataflow.py index 2fef81957..26d0c2ca1 100644 --- a/ads/jobs/builders/infrastructure/dataflow.py +++ b/ads/jobs/builders/infrastructure/dataflow.py @@ -791,10 +791,10 @@ def create(self, runtime: DataFlowRuntime, **kwargs) -> "DataFlow": if not self.name: self.name = utils.get_random_name_for_resource() payload = copy.deepcopy(self._spec) - runtime.convert(overwrite=kwargs.get("overwrite", False)) + overwrite = kwargs.pop("overwrite", runtime.overwrite) or False + runtime.convert(overwrite=overwrite) if not runtime.script_uri: raise ValueError("script uri must be specified in runtime.") - overwrite = kwargs.get("overwrite", False) if runtime.script_uri.split(":")[0] != "oci": if runtime.script_bucket: runtime.with_script_uri( diff --git a/ads/jobs/builders/infrastructure/dsc_job_runtime.py b/ads/jobs/builders/infrastructure/dsc_job_runtime.py index 26e233bb4..fcda6cc63 100644 --- a/ads/jobs/builders/infrastructure/dsc_job_runtime.py +++ b/ads/jobs/builders/infrastructure/dsc_job_runtime.py @@ -182,7 +182,7 @@ def _translate_config(self, runtime: Runtime) -> dict: if runtime.args: # shlex.join() is not available until python 3.8 job_configuration_details["command_line_arguments"] = " ".join( - shlex.quote(arg) for arg in runtime.args + shlex.quote(arg) for arg in runtime.get_spec(runtime.CONST_ARGS) ) return job_configuration_details @@ -207,7 +207,7 @@ def _translate_specs( Returns ------- dict - A dictionary contianing environment variables for OCI data science job. + A dictionary containing environment variables for OCI data science job. """ envs = {} for spec_key, dsc_key in spec_mappings.items(): @@ -225,7 +225,7 @@ def _extract_specs(envs: dict, spec_mappings: dict) -> dict: Parameters ---------- envs : dict - A dictionary contianing environment variables from OCI data science job. + A dictionary containing environment variables from OCI data science job. spec_mappings : dict Mapping from runtime properties to environment variables. This mapping is the same as the one in _translate_spec(). @@ -680,6 +680,32 @@ def _extract_envs(self, dsc_job) -> dict: spec[PythonRuntime.CONST_ENV_VAR] = envs return spec + def _extract_artifact(self, dsc_job): + """Extract the job artifact from data science job. + + Parameters + ---------- + dsc_job : DSCJob or oci.datascience.models.Job + The data science job containing runtime information. + + Returns + ------- + dict + A runtime specification dictionary for initializing a runtime. + """ + spec = super()._extract_artifact(dsc_job) + # It is not possible to get the actual script path + # since the information is not stored in the job. + # Here we only extract the name of the artifact. + spec.update( + { + PythonRuntime.CONST_SCRIPT_PATH: os.path.splitext( + str(dsc_job.artifact) + )[0] + } + ) + return spec + class NotebookRuntimeHandler(CondaRuntimeHandler): """Runtime Handler for NotebookRuntime""" @@ -692,18 +718,36 @@ class NotebookRuntimeHandler(CondaRuntimeHandler): CONST_NOTEBOOK_ENCODING = "NOTEBOOK_ENCODING" SPEC_MAPPINGS = { - NotebookRuntime.CONST_NOTEBOOK_PATH: CONST_NOTEBOOK_NAME, NotebookRuntime.CONST_OUTPUT_URI: CONST_OUTPUT_URI, NotebookRuntime.CONST_EXCLUDE_TAG: CONST_EXCLUDE_TAGS, NotebookRuntime.CONST_NOTEBOOK_ENCODING: CONST_NOTEBOOK_ENCODING, } def _translate_artifact(self, runtime: NotebookRuntime): - return NotebookArtifact(runtime.notebook_uri, runtime) + source = runtime.source if runtime.source else runtime.notebook_uri + return NotebookArtifact(source, runtime) def _translate_env(self, runtime: NotebookRuntime) -> dict: envs = super()._translate_env(runtime) - envs[self.CONST_NOTEBOOK_NAME] = os.path.basename(runtime.notebook_uri) + + if runtime.notebook: + # runtime.notebook should always be a relative path from the root of the source. + # In NotebookArtifact, when zipping the files, + # a top level folder having the same name as the basename of runtime.source + # is used to contain all the user artifacts. + # The basename of runtime.source will also be used as the name of the artifact zip file. + envs[self.CONST_NOTEBOOK_NAME] = os.path.join( + os.path.basename(runtime.source), runtime.notebook + ) + elif runtime.notebook_uri: + # For running a single notebook. + envs[self.CONST_NOTEBOOK_NAME] = os.path.basename(runtime.notebook_uri) + else: + raise ValueError( + "Notebook not specified. " + "Please specify the notebook using with_notebook_uri() or with_source() method." + ) + envs[self.CONST_ENTRYPOINT] = NotebookArtifact.CONST_DRIVER_SCRIPT if runtime.notebook_encoding: envs[self.CONST_NOTEBOOK_ENCODING] = runtime.notebook_encoding @@ -728,15 +772,12 @@ def _extract_envs(self, dsc_job) -> dict: """ spec = super()._extract_envs(dsc_job) envs = spec.pop(NotebookRuntime.CONST_ENV_VAR, {}) - if not ( - self.CONST_NOTEBOOK_NAME in envs - and NotebookRuntimeHandler.CONST_ENTRYPOINT in envs - ): + if not (self.CONST_NOTEBOOK_NAME in envs and self.CONST_ENTRYPOINT in envs): raise IncompatibleRuntime() # Remove job run entrypoint since it is the same for notebook runtime. - envs.pop(NotebookRuntimeHandler.CONST_ENTRYPOINT) + envs.pop(self.CONST_ENTRYPOINT) # Extract exclude tags - exclude_tags = envs.pop(NotebookRuntimeHandler.CONST_EXCLUDE_TAGS, None) + exclude_tags = envs.pop(self.CONST_EXCLUDE_TAGS, None) if exclude_tags: # Exclude tags are in a JSON serialized string try: @@ -746,6 +787,21 @@ def _extract_envs(self, dsc_job) -> dict: pass spec[NotebookRuntime.CONST_EXCLUDE_TAG] = exclude_tags + # Extract notebook name + notebook = envs.pop(self.CONST_NOTEBOOK_NAME) + if "/" in notebook: + # This indicate notebook is uploaded as part of a folder/zip + # When the source is a folder, the notebook name will have the format of + # folder/path/to/notebook.ipynb + ( + spec[NotebookRuntime.CONST_SOURCE], + spec[NotebookRuntime.CONST_ENTRYPOINT], + ) = str(notebook).split("/", 1) + else: + # When the source is a single notebook, the notebook name will be the filename only. + # notebook.ipynb + spec[NotebookRuntime.CONST_NOTEBOOK_PATH] = notebook + spec.update(self._extract_specs(envs, self.SPEC_MAPPINGS)) spec[NotebookRuntime.CONST_ENV_VAR] = envs return spec @@ -783,7 +839,7 @@ class GitPythonRuntimeHandler(CondaRuntimeHandler): GitPythonRuntime.CONST_GIT_SSH_SECRET_ID: CONST_GIT_SSH_SECRET_ID, GitPythonRuntime.CONST_OUTPUT_DIR: CONST_OUTPUT_DIR, GitPythonRuntime.CONST_OUTPUT_URI: CONST_OUTPUT_URI, - GitPythonRuntime.CONST_WORKING_DIR: CONST_WORKING_DIR + GitPythonRuntime.CONST_WORKING_DIR: CONST_WORKING_DIR, } def _translate_artifact(self, runtime: Runtime): diff --git a/ads/jobs/builders/runtimes/artifact.py b/ads/jobs/builders/runtimes/artifact.py index 470159c0c..6ded0978b 100644 --- a/ads/jobs/builders/runtimes/artifact.py +++ b/ads/jobs/builders/runtimes/artifact.py @@ -29,14 +29,15 @@ class Artifact: For example, the NotebookArtifact implements the build() method to convert the notebook to python script. with NotebookArtifact(runtime) as artifact: - # The build() method will be called when entering the context manager - # The final artifact for the job will be stored in artifact.path - upload_artifact(artifact.path) - # Files are cleaned up when exit or if there is an exception. + + * The build() method will be called when entering the context manager + * The final artifact for the job will be stored in artifact.path upload_artifact(artifact.path) + * Files are cleaned up when exit or if there is an exception. """ CONST_DRIVER_UTILS = "driver_utils.py" + CONST_DRIVER_NOTEBOOK = "driver_notebook.py" def __init__(self, source, runtime=None) -> None: # Get the full path of source file if it is local file. @@ -103,7 +104,7 @@ def copy_from_uri(uri, to_path, unpack=False, **storage_options): Parameters ---------- - uri : URI of the file + uri : str The URI of the source file or directory, which can be local path of OCI object storage URI. to_path : path-like object The local destination path. @@ -120,9 +121,10 @@ def copy_from_uri(uri, to_path, unpack=False, **storage_options): ------- str or path-like object The actual path of file/directory at destination. - For copying a single file and to_path is a filename, this will be the same as to_path. - For copying a single file and to_path is a directory, this will be to_path + filename. - For copying a directory, this will be to_path + directory name. + + * For copying a single file and to_path is a filename, this will be the same as to_path. + * For copying a single file and to_path is a directory, this will be to_path + filename. + * For copying a directory, this will be to_path + directory name. """ scheme = urlparse(uri).scheme # temp_dir is used only if the uri is zip/tar file @@ -169,38 +171,6 @@ def build(self): raise NotImplementedError() -class NotebookArtifact(Artifact): - """Represents a NotebookRuntime job artifact""" - - CONST_DRIVER_SCRIPT = "driver_notebook.py" - - def __init__(self, source, runtime) -> None: - super().__init__(source, runtime=runtime) - - def build(self): - """Prepares job artifact for notebook runtime""" - driver_script = os.path.join( - os.path.dirname(__file__), "../../templates", self.CONST_DRIVER_SCRIPT - ) - driver_utils = os.path.join( - os.path.dirname(__file__), "../../templates", self.CONST_DRIVER_UTILS - ) - notebook_path = os.path.join(self.temp_dir.name, os.path.basename(self.source)) - output_path = ( - os.path.join( - self.temp_dir.name, - str(os.path.basename(self.source)).split(".", maxsplit=1)[0], - ) - + ".zip" - ) - self.copy_from_uri(self.source, notebook_path) - with zipfile.ZipFile(output_path, "w") as zip_file: - zip_file.write(notebook_path, os.path.basename(notebook_path)) - zip_file.write(driver_script, os.path.basename(driver_script)) - zip_file.write(driver_utils, os.path.basename(driver_utils)) - self.path = output_path - - class ScriptArtifact(Artifact): """Represents a ScriptRuntime job artifact""" @@ -233,34 +203,57 @@ class PythonArtifact(Artifact): """Represents a PythonRuntime job artifact""" CONST_DRIVER_SCRIPT = "driver_python.py" - # Temp archive dir storing the zip/tar file from the user - ARCHIVE_DIR = "archive" # The directory to store user code # This directory must match the USER_CODE_DIR in driver_python.py USER_CODE_DIR = "code" - def build(self): - """Prepares job artifact for PythonRuntime.""" - basename = os.path.basename(str(self.source).rstrip("/")).split(".", 1)[0] - artifact_dir = os.path.join(self.temp_dir.name, basename) - code_dir = os.path.join(artifact_dir, self.USER_CODE_DIR) - os.makedirs(artifact_dir, exist_ok=True) + def __init__(self, source, runtime=None) -> None: + super().__init__(source, runtime) + self.basename = None + self.artifact_dir = None + self.code_dir = None + + def _copy_artifacts(self, drivers=None): + """Copies the drivers and artifacts to the temp artifact dir.""" + # The basename of the job artifact, + # this will be the name of the zip file uploading to OCI + self.basename = os.path.basename(str(self.source).rstrip("/")).split(".", 1)[0] + # The temp dir path for storing the artifacts, including drivers and user code + self.artifact_dir = os.path.join(self.temp_dir.name, self.basename) + # The temp dir path for storing the user code + self.code_dir = os.path.join(self.artifact_dir, self.USER_CODE_DIR) + os.makedirs(self.artifact_dir, exist_ok=True) + + if not drivers: + drivers = [] # Copy the driver script - for filename in [ - self.CONST_DRIVER_UTILS, - self.CONST_DRIVER_SCRIPT, - NotebookArtifact.CONST_DRIVER_SCRIPT, - ]: + for filename in drivers: file_path = os.path.join( os.path.dirname(__file__), "../../templates", filename ) - shutil.copy(file_path, os.path.join(artifact_dir, filename)) + shutil.copy(file_path, os.path.join(self.artifact_dir, filename)) # Copy user code - os.makedirs(code_dir, exist_ok=True) - Artifact.copy_from_uri(self.source, code_dir, unpack=True) + os.makedirs(self.code_dir, exist_ok=True) + Artifact.copy_from_uri(self.source, self.code_dir, unpack=True) + + def _zip_artifacts(self): + """Create a zip file from the temp artifact dir.""" + output = os.path.join(self.temp_dir.name, self.basename) + shutil.make_archive(output, "zip", self.artifact_dir, base_dir="./") + return output + ".zip" + + def build(self): + """Prepares job artifact for PythonRuntime.""" + self._copy_artifacts( + drivers=[ + self.CONST_DRIVER_UTILS, + self.CONST_DRIVER_SCRIPT, + self.CONST_DRIVER_NOTEBOOK, + ] + ) # Check if entrypoint is valid # If the user code is a directory, @@ -270,29 +263,52 @@ def build(self): if self.runtime and self.runtime.entrypoint: if not os.path.exists( os.path.join( - code_dir, self.runtime.working_dir, self.runtime.entrypoint + self.code_dir, self.runtime.working_dir, self.runtime.entrypoint ) ): # The specific entrypoint does not exist. # Check if user forgot to specify the top level directory. possible_entrypoint = os.path.join( - code_dir, + self.code_dir, self.runtime.working_dir, - basename, + self.basename, self.runtime.entrypoint, ) err_message = ( f"Invalid entrypoint. {self.runtime.entrypoint} does not exist." ) if os.path.exists(possible_entrypoint): - suggested_entrypoint = os.path.join(basename, self.runtime.entrypoint) + suggested_entrypoint = os.path.join( + self.basename, self.runtime.entrypoint + ) err_message += f" Do you mean {suggested_entrypoint}?" logger.warning(err_message) # Zip the job artifact - output = os.path.join(self.temp_dir.name, basename) - shutil.make_archive(output, "zip", artifact_dir, base_dir="./") - self.path = output + ".zip" + self.path = self._zip_artifacts() + + +class NotebookArtifact(PythonArtifact): + """Represents a NotebookRuntime job artifact""" + + CONST_DRIVER_SCRIPT = PythonArtifact.CONST_DRIVER_NOTEBOOK + + def build(self): + """Prepares job artifact for notebook runtime""" + # Copy job artifacts + self._copy_artifacts([self.CONST_DRIVER_UTILS, self.CONST_DRIVER_NOTEBOOK]) + if self.runtime.notebook: + notebook_path = os.path.join( + os.path.basename(self.runtime.source), self.runtime.notebook + ) + if not os.path.exists(os.path.join(self.code_dir, notebook_path)): + raise ValueError( + f"Invalid notebook path: {self.runtime.notebook}\n" + + f"Please make sure your {self.runtime.source} contains the notebook and " + + "the notebook path is relative to the root of the source." + ) + # Zip the job artifact + self.path = self._zip_artifacts() class GitPythonArtifact(Artifact): diff --git a/ads/jobs/builders/runtimes/python_runtime.py b/ads/jobs/builders/runtimes/python_runtime.py index 9f90efb29..dcfe07fd9 100644 --- a/ads/jobs/builders/runtimes/python_runtime.py +++ b/ads/jobs/builders/runtimes/python_runtime.py @@ -407,7 +407,7 @@ class PythonRuntime(ScriptRuntime, _PythonRuntimeMixin): class NotebookRuntime(CondaRuntime): """Represents a job runtime with Jupyter notebook - To run a job with a Jupyter notebook, + To run a job with a single Jupyter notebook, you can define the run time as:: runtime = ( @@ -422,6 +422,9 @@ class NotebookRuntime(CondaRuntime): .with_output("oci://bucket_name@namespace/path/to/dir") ) + Note that the notebook path can be local or remote path supported by fsspec, + including OCI object storage path like ``oci://bucket@namespace/path/to/notebook`` + """ CONST_NOTEBOOK_PATH = "notebookPathURI" @@ -429,12 +432,16 @@ class NotebookRuntime(CondaRuntime): CONST_OUTPUT_URI = "outputUri" CONST_OUTPUT_URI_ALT = "outputURI" CONST_EXCLUDE_TAG = "excludeTags" + CONST_SOURCE = "source" + CONST_ENTRYPOINT = "entrypoint" attribute_map = { CONST_NOTEBOOK_PATH: "notebook_path_uri", CONST_NOTEBOOK_ENCODING: "notebook_encoding", CONST_OUTPUT_URI: "output_uri", CONST_EXCLUDE_TAG: "exclude_tags", + CONST_SOURCE: "source", + CONST_ENTRYPOINT: "entrypoint", } attribute_map.update(CondaRuntime.attribute_map) @@ -459,11 +466,15 @@ def notebook_encoding(self) -> str: def with_notebook(self, path: str, encoding="utf-8") -> NotebookRuntime: """Specifies the notebook to be run as a job. + Use this method if you would like to run a single notebook. + Use ``with_source()`` method if you would like to run a notebook with additional dependency files. Parameters ---------- path : str The path of the Jupyter notebook + encoding : str + The encoding for opening the notebook. Defaults to utf-8. Returns ------- @@ -506,11 +517,12 @@ def output_uri(self) -> list: def with_output(self, output_uri: str) -> NotebookRuntime: """Specifies the output URI for storing the output notebook and files. + All files in the directory containing the notebook will be saved. Parameters ---------- output_uri : str - URI for storing the output notebook and files. + URI for a directory storing the output notebook and files. For example, oci://bucket@namespace/path/to/dir Returns @@ -520,6 +532,46 @@ def with_output(self, output_uri: str) -> NotebookRuntime: """ return self.set_spec(self.CONST_OUTPUT_URI, output_uri) + def with_source(self, uri: str, notebook: str, encoding="utf-8"): + """Specify source code directory containing the notebook and dependencies for the job. + Use this method if you would like to run a notebook with additional dependency files. + Use the `with_notebook()` method if you would like to run a single notebook. + + In the following example, local folder "path/to/source" contains the notebook and dependencies, + The local path of the notebook is "path/to/source/relative/path/to/notebook.ipynb":: + + runtime.with_source(uri="path/to/source", notebook="relative/path/to/notebook.ipynb") + + Parameters + ---------- + uri : str + URI of the source code directory. This can be local or on OCI object storage. + notebook : str + The relative path of the notebook from the source URI. + encoding : str + The encoding for opening the notebook. Defaults to utf-8. + + Returns + ------- + Self + The runtime instance. + + """ + self.set_spec(self.CONST_SOURCE, uri) + self.set_spec(self.CONST_ENTRYPOINT, notebook) + self.set_spec(self.CONST_NOTEBOOK_ENCODING, encoding) + return self + + @property + def source(self) -> str: + """The source code location.""" + return self.get_spec(self.CONST_SOURCE) + + @property + def notebook(self) -> str: + """The path of the notebook relative to the source.""" + return self.get_spec(self.CONST_ENTRYPOINT) + class GitPythonRuntime(CondaRuntime, _PythonRuntimeMixin): """Represents a job runtime with source code from git repository @@ -622,52 +674,6 @@ def commit(self) -> str: """Git commit ID (SHA1 hash)""" return self.get_spec(self.CONST_COMMIT) - @staticmethod - def _serialize_arg(arg: Any) -> str: - """Serialize the argument. - This returns the argument "as is" if it is a string AND not a valid JSON payload. - Otherwise the argument will be serialized with JSON. - - Parameters - ---------- - arg : Any - argument to be serialized - - Returns - ------- - str - Serialized argument as a string - """ - if arg is None: - return None - if isinstance(arg, str): - try: - json.loads(arg) - except json.JSONDecodeError: - return arg - return json.dumps(arg) - - def with_argument(self, *args, **kwargs): - """Specifies the arguments for running the script/function. - - When running a python script, the arguments will be the command line arguments. - For example, with_argument("arg1", "arg2", key1="val1", key2="val2") - will generate the command line arguments: "arg1 arg2 --key1 val1 --key2 val2" - - When running a function, the arguments will be passed into the function. - Arguments can also be list, dict or any JSON serializable object. - For example, with_argument("arg1", "arg2", key1=["val1a", "val1b"], key2="val2") - will be passed in as "your_function("arg1", "arg2", key1=["val1a", "val1b"], key2="val2") - - Returns - ------- - self - The runtime instance. - """ - args = [self._serialize_arg(arg) for arg in args] - kwargs = {k: self._serialize_arg(v) for k, v in kwargs.items()} - return super().with_argument(*args, **kwargs) - @property def ssh_secret_ocid(self) -> str: """The OCID of the OCI Vault secret storing the Git SSH key.""" @@ -682,6 +688,7 @@ class DataFlowRuntime(CondaRuntime): CONST_SCRIPT_PATH = "scriptPathURI" CONST_CONFIGURATION = "configuration" CONST_CONDA_AUTH_TYPE = "condaAuthType" + CONST_OVERWRITE = "overwrite" attribute_map = { CONST_SCRIPT_BUCKET: "script_bucket", CONST_ARCHIVE_URI: "archive_bucket", @@ -689,6 +696,7 @@ class DataFlowRuntime(CondaRuntime): CONST_SCRIPT_PATH: "script_path_uri", CONST_CONFIGURATION: CONST_CONFIGURATION, CONST_CONDA_AUTH_TYPE: "conda_auth_type", + CONST_OVERWRITE: CONST_OVERWRITE, } attribute_map.update(Runtime.attribute_map) @@ -773,13 +781,13 @@ def script_uri(self) -> str: """The URI of the source code""" return self.get_spec(self.CONST_SCRIPT_PATH) - def with_script_uri(self, path) -> "DataFlowRuntime": + def with_script_uri(self, path: str) -> "DataFlowRuntime": """ Set script uri. Parameters ---------- - uri: str + path: str uri to the script Returns @@ -854,6 +862,29 @@ def configuration(self) -> dict: """Configuration for Spark""" return self.get_spec(self.CONST_CONFIGURATION) + def with_overwrite(self, overwrite: bool) -> "DataFlowRuntime": + """ + Whether to overwrite the existing script in object storage (script bucket). + If the Object Storage bucket already contains a script with the same name, + then it will be overwritten with the new one if the `overwrite` flag equal to `True`. + + Parameters + ---------- + overwrite: bool + Whether to overwrite the existing script in object storage (script bucket). + + Returns + ------- + DataFlowRuntime + The DataFlowRuntime instance (self). + """ + return self.set_spec(self.CONST_OVERWRITE, overwrite) + + @property + def overwrite(self) -> str: + """Whether to overwrite the existing script in object storage (script bucket).""" + return self.get_spec(self.CONST_OVERWRITE) + def convert(self, **kwargs): pass diff --git a/ads/jobs/templates/driver_notebook.py b/ads/jobs/templates/driver_notebook.py index 3f225b8dd..4681136b5 100644 --- a/ads/jobs/templates/driver_notebook.py +++ b/ads/jobs/templates/driver_notebook.py @@ -36,6 +36,11 @@ logger = logging.getLogger(__name__) logger = set_log_level(logger) +# The directory for storing the user code/notebook. +# The basename of the directory should match the ADS PythonArtifact.USER_CODE_DIR +CODE_DIR = os.path.join(os.path.dirname(__file__), "code") + + class ADSExecutePreprocessor(ExecutePreprocessor): """Customized Execute Preprocessor for running notebook.""" @@ -149,12 +154,10 @@ def main() -> None: """Runs the driver to execute a notebook.""" JobRunner().conda_unpack() - notebook_file_path = os.path.join( - os.path.dirname(__file__), os.environ.get("JOB_RUN_NOTEBOOK") - ) - output_dir = os.path.join(os.path.dirname(__file__), "outputs") - # Create the output directory - os.makedirs(output_dir, exist_ok=True) + notebook_file_path = os.path.join(CODE_DIR, os.environ.get("JOB_RUN_NOTEBOOK")) + # By default, the output directory will be the one containing the notebook + output_dir = os.environ.get("OUTPUT_DIR", os.path.dirname(notebook_file_path)) + # Exclude tags tags = os.environ.get("NOTEBOOK_EXCLUDE_TAGS") if tags: diff --git a/ads/jobs/templates/driver_oci.py b/ads/jobs/templates/driver_oci.py index 961747436..d92847bc4 100644 --- a/ads/jobs/templates/driver_oci.py +++ b/ads/jobs/templates/driver_oci.py @@ -61,6 +61,7 @@ import random import shutil import string +import sys import traceback import uuid from time import sleep, time @@ -74,25 +75,11 @@ from oci.data_science import DataScienceClient try: - # This is used by ADS and testing - from .driver_utils import ( - OCIHelper, - JobRunner, - set_log_level, - CONST_ENV_ENTRY_FUNC, - CONST_ENV_CODE_DIR, - CONST_ENV_JOB_RUN_OCID, - ) + # This is used in a job run. + import driver_utils except ImportError: - # This is used when the script is in a job run. - from driver_utils import ( - OCIHelper, - JobRunner, - set_log_level, - CONST_ENV_ENTRY_FUNC, - CONST_ENV_CODE_DIR, - CONST_ENV_JOB_RUN_OCID, - ) + # This is used when importing by other ADS module and testing. + from . import driver_utils CONST_ENV_ENTRYPOINT = "GIT_ENTRYPOINT" @@ -108,14 +95,16 @@ logger = logging.getLogger(__name__) -logger = set_log_level(logger) +logger = driver_utils.set_log_level(logger) class GitManager: """Contains methods for fetching code from Git repository""" def __init__( - self, repo_url: str, code_dir: str = os.environ.get(CONST_ENV_CODE_DIR) + self, + repo_url: str, + code_dir: str = os.environ.get(driver_utils.CONST_ENV_CODE_DIR), ): """Initialize the GitManager @@ -163,7 +152,9 @@ def __init__( self.commit = None def _config_ssh_proxy(self, proxy): - return_code = JobRunner.run_command("command -v socat", level=logging.DEBUG) + return_code = driver_utils.JobRunner.run_command( + "command -v socat", level=logging.DEBUG + ) if return_code: logger.warning( "You have ssh_proxy configured. " @@ -178,7 +169,7 @@ def _config_ssh_proxy(self, proxy): logger.debug("SSH config saved to %s", SSH_CONFIG_FILE_PATH) def _config_known_hosts(self, host: str): - if JobRunner.run_command( + if driver_utils.JobRunner.run_command( f"ssh-keyscan -H {host} >> {SSH_DIR}/known_hosts", level=logging.DEBUG ): logger.debug("Added %s to known hosts.", host) @@ -275,7 +266,9 @@ def read_secret(secret_id): str The value of the secret decoded with ASCII. """ - secret_client = OCIHelper.init_oci_client(oci.secrets.SecretsClient) + secret_client = driver_utils.OCIHelper.init_oci_client( + oci.secrets.SecretsClient + ) secret_bundle = secret_client.get_secret_bundle(secret_id) base64_secret_bytes = secret_bundle.data.secret_bundle_content.content.encode( "ascii" @@ -346,13 +339,13 @@ def __exit__(self, tp, value, tb): pass -class GitJobRunner(JobRunner): +class GitJobRunner(driver_utils.JobRunner): """Contains methods for running the job.""" def __init__( self, git_manager: GitManager, - job_run_ocid: str = os.environ.get(CONST_ENV_JOB_RUN_OCID), + job_run_ocid: str = os.environ.get(driver_utils.CONST_ENV_JOB_RUN_OCID), ): """Initialize the job runner @@ -371,7 +364,7 @@ def __init__( def run( self, entrypoint: str = os.environ.get(CONST_ENV_ENTRYPOINT), - entry_function: str = os.environ.get(CONST_ENV_ENTRY_FUNC), + entry_function: str = os.environ.get(driver_utils.CONST_ENV_ENTRY_FUNC), ): """Runs the job @@ -388,7 +381,9 @@ def run( "method": entry_function if entry_function else "", } + # For git, the entrypoint is relative to the root of the repository entrypoint = os.path.join(self.code_dir, entrypoint) + return super().run(entrypoint=entrypoint, entry_function=entry_function) @staticmethod @@ -454,7 +449,7 @@ def save_metadata(self) -> None: logger.debug("Job output: %s", prefix) tags["outputs"] = prefix - client = OCIHelper.init_oci_client(DataScienceClient) + client = driver_utils.OCIHelper.init_oci_client(DataScienceClient) oci.retry.DEFAULT_RETRY_STRATEGY.make_retrying_call( self.update_job_run_metadata_with_rest_api, client, tags ) @@ -479,11 +474,11 @@ def main(): runner.set_working_dir().setup_python_path().run() # Copy outputs - runner.artifacts = OCIHelper.copy_outputs() + runner.artifacts = driver_utils.OCIHelper.copy_outputs() # Save metadata only if job run OCID is available if ( - os.environ.get(CONST_ENV_JOB_RUN_OCID) + os.environ.get(driver_utils.CONST_ENV_JOB_RUN_OCID) and "SKIP_METADATA_UPDATE" not in os.environ ): try: diff --git a/ads/jobs/templates/driver_utils.py b/ads/jobs/templates/driver_utils.py index 4a063a1eb..d51475feb 100644 --- a/ads/jobs/templates/driver_utils.py +++ b/ads/jobs/templates/driver_utils.py @@ -8,11 +8,13 @@ import logging import os import runpy +import shlex +import stat import subprocess import sys import time import traceback -from typing import List +from typing import List, Optional, Any from urllib.parse import urlparse @@ -245,49 +247,11 @@ def __init__(self, argument_list: list) -> None: """ self.argument_list = argument_list - @staticmethod - def decode_arg(val: str): - """Decodes the value of the argument if it is a JSON payload. - - Parameters - ---------- - val : str - The argument value in a string. - - Returns - ------- - Any - None, if the val is None. - String value, if the val is a string but not a JSON payload. - Otherwise, the object after JSON decoded. - """ - if val is None: - return None - try: - return json.loads(val) - except json.decoder.JSONDecodeError: - return val - - @staticmethod - def join_values(value_list: list): - """Joins the values of a keyword argument. - - Parameters - ---------- - value_list : list - Values in a list of strings. - - Returns - ------- - str or None - The value of the argument as a string. - """ - if value_list: - return " ".join(value_list) - return None - def parse(self): - """Parses the arguments + """Parses the arguments into args and kwargs. + args is a list of positional arguments. + kwargs is a dictionary of keyword arguments. + The "--" will be removed from the keywords. Returns ------- @@ -296,29 +260,19 @@ def parse(self): """ args = [] kwargs = {} - parsing_kwargs = False key = None - val = [] for arg in self.argument_list: - arg = str(arg) - if len(arg) > 2 and arg.startswith("--"): - if key: - # Save previous key and val - kwargs[key] = self.join_values(val) - parsing_kwargs = True + if not isinstance(arg, str): + args.append(arg) + elif len(arg) > 2 and arg.startswith("--"): key = arg[2:] - # Reset val - val = [] - elif parsing_kwargs: - val.append(arg) + kwargs[key] = None + elif key: + kwargs[key] = arg + key = None else: args.append(arg) - # Save the last key and val - if key: - kwargs[key] = self.join_values(val) - args = [self.decode_arg(arg) for arg in args] - kwargs = {k: self.decode_arg(v) for k, v in kwargs.items()} return args, kwargs @@ -336,7 +290,7 @@ def __init__(self, code_dir: str = DEFAULT_CODE_DIR) -> None: @staticmethod def run_command( - command: str, activate_conda: bool = False, level: int = logging.INFO + command: str, activate_conda: bool = False, level: Optional[int] = None ) -> int: """Runs a shell command and logs the outputs with specific log level. @@ -347,7 +301,10 @@ def run_command( activate_conda : bool, optional Indicate if conda environment should be activated for running the command, by default False level : int, optional - Logging level for the command outputs, by default logging.INFO + Logging level for the command outputs, by default None. + If this is set to a log level from logging, e.g. logging.DEBUG, + the command outputs will be logged with the level. + If this is None, the command outputs will be printed. Returns ------- @@ -374,19 +331,26 @@ def run_command( env=os.environ.copy(), shell=True, ) - # Steam the outputs + # Stream the outputs while True: output = process.stdout.readline() if process.poll() is not None and output == b"": break if output: - # logging will flush outputs by default - logger.log(level=level, msg=output.decode().strip()) - time.sleep(0.5) + msg = output.decode() + if level is None: + # output already contains the line break + print(msg, flush=True, end="") + else: + # logging will flush outputs by default + logger.log(level=level, msg=msg) + # Add a small delay so that + # outputs from the subsequent code will have different timestamp for oci logging + time.sleep(0.05) return process.returncode def conda_unpack(self): - if self.run_command("conda-unpack"): + if self.run_command("conda-unpack", level=logging.DEBUG): logger.info("conda-unpack exits with non-zero return code.") return self @@ -412,7 +376,7 @@ def set_working_dir(self, working_dir: str = os.environ.get(CONST_ENV_WORKING_DI return self def setup_python_path( - self, python_paths: str = os.environ.get(CONST_ENV_PYTHON_PATH, "") + self, python_path: str = os.environ.get(CONST_ENV_PYTHON_PATH, "") ): """Adds additional python paths. Relative paths are expanded based on the current working directory. @@ -420,18 +384,20 @@ def setup_python_path( Parameters ---------- - python_paths : str + python_path : str Additional python paths to be added to sys.path, by default, os.environ.get("PYTHON_PATH", "") Multiple paths can be separated by os.pathsep, which is colon(:) for Linux and Mac. """ - path_list = python_paths.split(os.pathsep) + if not python_path: + return self + path_list = python_path.split(os.pathsep) path_list.append(self.code_dir) for path in path_list: - python_path = os.path.abspath(os.path.expanduser(path)) - if python_path not in sys.path: - sys.path.append(python_path) + abs_path = os.path.abspath(os.path.expanduser(path)) + if abs_path not in sys.path: + sys.path.append(abs_path) logger.debug("Python Path: %s", sys.path) return self @@ -463,6 +429,38 @@ def _run_function(self, module_path: str, entry_function: str, argv: list): ) method(*args, **kwargs) + def _run_script(self, entrypoint: str): + if ( + os.path.isdir(entrypoint) + or entrypoint.endswith(".py") + or entrypoint.endswith(".zip") + ): + logger.info("Running script: %s", entrypoint) + # The file path may refer directly to a Python script + # or else it may refer to a zipfile or directory containing a top level __main__.py script. + # See https://docs.python.org/3/library/runpy.html#runpy.run_path + # Arguments from sys.argv will be passed into the script + runpy.run_path(entrypoint, run_name="__main__") + else: + if os.path.exists(entrypoint): + # User should make the file executable before committing it to Git + # e.g. git update-index --chmod=+x my_script.sh + # Here we make the entrypoint executable just in case + try: + st = os.stat(entrypoint) + os.chmod(entrypoint, st.st_mode | stat.S_IEXEC) + except Exception: + # Ignore any error here and continue to try to run the script. + # Show the exception for debugging + logger.debug(traceback.format_exc()) + # Run the entrypoint as shell command with conda activated + cmd = shlex.join([entrypoint] + sys.argv[1:]) + return_code = self.run_command(cmd, activate_conda=True) + # Exit the job run with the same return code if it is non-zero. + if return_code: + logger.error("CMD exited with return code %s.", return_code) + sys.exit(return_code) + def run( self, entrypoint: str = os.environ.get(CONST_ENV_ENTRYPOINT), @@ -484,6 +482,10 @@ def run( """ if not entrypoint: raise ValueError(f"Invalid entrypoint: {str(entrypoint)}") + entrypoint_abs_path = os.path.abspath(os.path.expanduser(entrypoint)) + if not os.path.exists(entrypoint_abs_path): + raise ValueError(f"Entrypoint {entrypoint_abs_path} not found.") + if entry_function: logger.info("Running function: %s in %s", entry_function, entrypoint) self._run_function(entrypoint, entry_function, sys.argv[1:]) @@ -491,10 +493,14 @@ def run( from driver_notebook import run_notebook logger.info("Running notebook: %s", entrypoint) + # Exclude tags + tags = os.environ.get("NOTEBOOK_EXCLUDE_TAGS") + if tags: + tags = json.loads(tags) + logger.info("Excluding cells with any of the following tags: %s", tags) # Pass in the absolute path to make sure the working dir is notebook directory - run_notebook(os.path.abspath(os.path.expanduser(entrypoint))) + run_notebook(os.path.abspath(os.path.expanduser(entrypoint)), exclude_tags=tags) else: - logger.info("Running script: %s", entrypoint) - runpy.run_path(entrypoint, run_name="__main__") + self._run_script(entrypoint_abs_path) logger.info("Job run completed.") return self diff --git a/ads/model/__init__.py b/ads/model/__init__.py index cd714e108..65895eda9 100644 --- a/ads/model/__init__.py +++ b/ads/model/__init__.py @@ -28,3 +28,27 @@ ModelVersionSetNotExists, ModelVersionSetNotSaved, ) + +__all__ = [ + "GenericModel", + "ModelState", + "DataScienceModel", + "ModelProperties", + "AutoMLModel", + "LightGBMModel", + "PyTorchModel", + "SklearnModel", + "TensorFlowModel", + "XGBoostModel", + "SparkPipelineModel", + "HuggingFacePipelineModel", + "ModelDeployer", + "ModelDeployment", + "ModelDeploymentProperties", + "SERDE", + "ModelInputSerializer", + "ModelVersionSet", + "experiment", + "ModelVersionSetNotExists", + "ModelVersionSetNotSaved", +] diff --git a/ads/model/artifact.py b/ads/model/artifact.py index d991adeef..bcc6b9dd8 100644 --- a/ads/model/artifact.py +++ b/ads/model/artifact.py @@ -38,7 +38,7 @@ class ArtifactRequiredFilesError(Exception): def __init__(self, required_files: Tuple[str]): super().__init__( "Not all required files presented in artifact folder. " - f"Required files: {required_files}" + f"Required files for conda runtime: {required_files}. If you are using container runtime, set `ignore_conda_error=True`." ) @@ -115,6 +115,7 @@ def __init__( artifact_dir: str, model_file_name: str = None, reload: Optional[bool] = False, + ignore_conda_error: Optional[bool] = False, ): """Initializes a ModelArtifact instance. @@ -145,7 +146,9 @@ def __init__( sys.path.insert(0, self.artifact_dir) self.model_file_name = model_file_name self._env = Environment(loader=PackageLoader("ads", "templates")) - if reload: + self.ignore_conda_error = ignore_conda_error + self.model = None + if reload and not ignore_conda_error: self.reload() # Extracts the model_file_name from the score.py. if ( @@ -166,6 +169,7 @@ def prepare_runtime_yaml( namespace: str = CONDA_BUCKET_NS, bucketname: str = CONDA_BUCKET_NAME, auth: dict = None, + ignore_conda_error: bool = False, ) -> None: """Generate a runtime yaml file and save it to the artifact directory. @@ -207,6 +211,12 @@ def prepare_runtime_yaml( """ runtime_info = RuntimeInfo.from_env() runtime_info.model_artifact_version = MODEL_ARTIFACT_VERSION + if ignore_conda_error: + runtime_info.model_provenance.training_code.artifact_directory = ( + self.artifact_dir + ) + runtime_info.save() + return runtime_info inference_conda_env = ModelArtifact._populate_env_info( InferenceEnvInfo, conda_pack=inference_conda_env, @@ -369,6 +379,7 @@ def from_uri( model_file_name: str = None, force_overwrite: Optional[bool] = False, auth: Optional[Dict] = None, + ignore_conda_error: Optional[bool] = False, ): """Constructs a ModelArtifact object from the existing model artifacts. @@ -418,19 +429,32 @@ def from_uri( force_overwrite=force_overwrite, auth=auth, ) - try: - _validate_artifact_dir(artifact_dir) - except ArtifactNestedFolderError as exc: - with tempfile.TemporaryDirectory() as temp_dir: - utils.copy_from_uri( - uri=exc.folder, to_path=temp_dir, force_overwrite=True - ) - utils.copy_from_uri( - uri=temp_dir, to_path=artifact_dir, force_overwrite=True - ) - - return cls(artifact_dir, model_file_name, reload=True) + if not ignore_conda_error: + try: + _validate_artifact_dir(artifact_dir) + except ArtifactNestedFolderError as exc: + with tempfile.TemporaryDirectory() as temp_dir: + utils.copy_from_uri( + uri=exc.folder, to_path=temp_dir, force_overwrite=True + ) + utils.copy_from_uri( + uri=temp_dir, to_path=artifact_dir, force_overwrite=True + ) + + return cls( + artifact_dir, + model_file_name, + reload=True, + ignore_conda_error=ignore_conda_error, + ) def __getattr__(self, item): """Makes the functions in `score.py` directly accessable by ModelArtifact class.""" - return getattr(self.score, item) + + try: + return getattr(self.score, item) + except: + if self.ignore_conda_error: + logger.warn("`verify` is not guarenteed to work for byoc case.") + else: + raise diff --git a/ads/model/base_properties.py b/ads/model/base_properties.py index b39e8adf4..0ffc743db 100644 --- a/ads/model/base_properties.py +++ b/ads/model/base_properties.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ import dataclasses @@ -191,7 +191,7 @@ def to_config( force_overwrite: (bool, optional). Defaults to False. Whether to overwrite existing files or not. auth: (Dict, optional). Defaults to None. - The default authetication is set using `ads.set_auth` API. If you need to override the + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. @@ -202,7 +202,7 @@ def to_config( """ config = Config(uri=uri, auth=auth) config.section_set(key=profile, info=self.to_dict(), replace=force_overwrite) - config.save() + config.save(force_overwrite=force_overwrite) @classmethod def from_config( @@ -221,7 +221,7 @@ def from_config( profile: str The config profile name. auth: (Dict, optional). Defaults to None. - The default authetication is set using `ads.set_auth` API. If you need to override the + The default authentication is set using `ads.set_auth` API. If you need to override the default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate authentication signer and kwargs required to instantiate IdentityClient object. diff --git a/ads/model/deployment/__init__.py b/ads/model/deployment/__init__.py index da52efb46..0f36c3641 100644 --- a/ads/model/deployment/__init__.py +++ b/ads/model/deployment/__init__.py @@ -1,8 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2021, 2022 Oracle and its affiliates. -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. +# Copyright (c) 2021, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from .model_deployer import ModelDeployer @@ -10,5 +9,12 @@ DEFAULT_POLL_INTERVAL, DEFAULT_WAIT_TIME, ModelDeployment, + ModelDeploymentMode, ) from .model_deployment_properties import ModelDeploymentProperties +from .model_deployment_infrastructure import ModelDeploymentInfrastructure +from .model_deployment_runtime import ( + ModelDeploymentRuntime, + ModelDeploymentCondaRuntime, + ModelDeploymentContainerRuntime, +) diff --git a/ads/model/deployment/model_deployment.py b/ads/model/deployment/model_deployment.py index 2ca896cb4..97af00d8a 100644 --- a/ads/model/deployment/model_deployment.py +++ b/ads/model/deployment/model_deployment.py @@ -64,6 +64,12 @@ MODEL_DEPLOYMENT_KIND = "deployment" MODEL_DEPLOYMENT_TYPE = "modelDeployment" +MODEL_DEPLOYMENT_INSTANCE_SHAPE = "VM.Standard.E4.Flex" +MODEL_DEPLOYMENT_INSTANCE_OCPUS = 1 +MODEL_DEPLOYMENT_INSTANCE_MEMORY_IN_GBS = 16 +MODEL_DEPLOYMENT_INSTANCE_COUNT = 1 +MODEL_DEPLOYMENT_BANDWIDTH_MBPS = 10 + class ModelDeploymentLogType: PREDICT = "predict" @@ -1443,7 +1449,8 @@ def _build_model_deployment_details(self) -> CreateModelDeploymentDetails: self.CONST_DESCRIPTION: self.description, self.CONST_DEFINED_TAG: self.defined_tags, self.CONST_FREEFORM_TAG: self.freeform_tags, - self.runtime.CONST_DEPLOYMENT_MODE: self.runtime.deployment_mode, + self.runtime.CONST_DEPLOYMENT_MODE: self.runtime.deployment_mode + or ModelDeploymentMode.HTTPS, self.infrastructure.CONST_COMPARTMENT_ID: self.infrastructure.compartment_id or COMPARTMENT_OCID, self.infrastructure.CONST_PROJECT_ID: self.infrastructure.project_id @@ -1493,13 +1500,30 @@ def _build_model_deployment_configuration_details(self) -> Dict: runtime = self.runtime instance_configuration = { - infrastructure.CONST_INSTANCE_SHAPE_NAME: infrastructure.shape_name, - infrastructure.CONST_MODEL_DEPLOYMENT_INSTANCE_SHAPE_CONFIG_DETAILS: infrastructure.shape_config_details, + infrastructure.CONST_INSTANCE_SHAPE_NAME: infrastructure.shape_name + or MODEL_DEPLOYMENT_INSTANCE_SHAPE, } + if instance_configuration[infrastructure.CONST_INSTANCE_SHAPE_NAME].endswith( + "Flex" + ): + instance_configuration[ + infrastructure.CONST_MODEL_DEPLOYMENT_INSTANCE_SHAPE_CONFIG_DETAILS + ] = { + infrastructure.CONST_OCPUS: infrastructure.shape_config_details.get( + "ocpus", None + ) + or MODEL_DEPLOYMENT_INSTANCE_OCPUS, + infrastructure.CONST_MEMORY_IN_GBS: infrastructure.shape_config_details.get( + "memory_in_gbs", None + ) + or MODEL_DEPLOYMENT_INSTANCE_MEMORY_IN_GBS, + } + scaling_policy = { infrastructure.CONST_POLICY_TYPE: "FIXED_SIZE", - infrastructure.CONST_INSTANCE_COUNT: infrastructure.replica, + infrastructure.CONST_INSTANCE_COUNT: infrastructure.replica + or MODEL_DEPLOYMENT_INSTANCE_COUNT, } if not runtime.model_uri: @@ -1520,12 +1544,22 @@ def _build_model_deployment_configuration_details(self) -> Dict: ) model_configuration_details = { - infrastructure.CONST_BANDWIDTH_MBPS: infrastructure.bandwidth_mbps, + infrastructure.CONST_BANDWIDTH_MBPS: infrastructure.bandwidth_mbps + or MODEL_DEPLOYMENT_BANDWIDTH_MBPS, infrastructure.CONST_INSTANCE_CONFIG: instance_configuration, runtime.CONST_MODEL_ID: model_id, infrastructure.CONST_SCALING_POLICY: scaling_policy, } + if runtime.env: + if not hasattr( + oci.data_science.models, + "ModelDeploymentEnvironmentConfigurationDetails", + ): + raise EnvironmentError( + "Environment variable hasn't been supported in the current OCI SDK installed." + ) + environment_variables = runtime.env if infrastructure.web_concurrency: environment_variables["WEB_CONCURRENCY"] = str( @@ -1592,24 +1626,27 @@ def _build_category_log_details(self) -> Dict: self.infrastructure.CONST_PREDICT: log_group_details, } - return { - self.infrastructure.CONST_ACCESS: { + logs = {} + if self.infrastructure.access_log: + logs[self.infrastructure.CONST_ACCESS] = { self.infrastructure.CONST_LOG_GROUP_ID: self.infrastructure.access_log.get( "logGroupId", None ), self.infrastructure.CONST_LOG_ID: self.infrastructure.access_log.get( "logId", None ), - }, - self.infrastructure.CONST_PREDICT: { + } + if self.infrastructure.predict_log: + logs[self.infrastructure.CONST_PREDICT] = { self.infrastructure.CONST_LOG_GROUP_ID: self.infrastructure.predict_log.get( "logGroupId", None ), self.infrastructure.CONST_LOG_ID: self.infrastructure.predict_log.get( "logId", None ), - }, - } + } + + return logs def _random_display_name(self): """Generates a random display name.""" diff --git a/ads/model/framework/huggingface_model.py b/ads/model/framework/huggingface_model.py index 4e6232b96..5e7b7c3f8 100644 --- a/ads/model/framework/huggingface_model.py +++ b/ads/model/framework/huggingface_model.py @@ -238,7 +238,7 @@ class HuggingFacePipelineModel(FrameworkSpecificModel): def __init__( self, estimator: Callable, - artifact_dir: str, + artifact_dir: Optional[str] = None, properties: Optional[ModelProperties] = None, auth: Dict = None, model_save_serializer: Optional[SERDE] = model_save_serializer_type.HUGGINGFACE, diff --git a/ads/model/framework/lightgbm_model.py b/ads/model/framework/lightgbm_model.py index 89cff67b2..1aa64d59a 100644 --- a/ads/model/framework/lightgbm_model.py +++ b/ads/model/framework/lightgbm_model.py @@ -126,7 +126,7 @@ class LightGBMModel(FrameworkSpecificModel): def __init__( self, estimator: Callable, - artifact_dir: str, + artifact_dir: Optional[str] = None, properties: Optional[ModelProperties] = None, auth: Dict = None, model_save_serializer: Optional[SERDE] = None, diff --git a/ads/model/framework/pytorch_model.py b/ads/model/framework/pytorch_model.py index cab8eb8a5..f33ca0ae1 100644 --- a/ads/model/framework/pytorch_model.py +++ b/ads/model/framework/pytorch_model.py @@ -122,7 +122,7 @@ class PyTorchModel(FrameworkSpecificModel): def __init__( self, estimator: callable, - artifact_dir: str, + artifact_dir: Optional[str] = None, properties: Optional[ModelProperties] = None, auth: Dict = None, model_save_serializer: Optional[SERDE] = model_save_serializer_type.TORCH, diff --git a/ads/model/framework/sklearn_model.py b/ads/model/framework/sklearn_model.py index 48daa88cf..0af2039a0 100644 --- a/ads/model/framework/sklearn_model.py +++ b/ads/model/framework/sklearn_model.py @@ -121,7 +121,7 @@ class SklearnModel(FrameworkSpecificModel): def __init__( self, estimator: Callable, - artifact_dir: str, + artifact_dir: Optional[str] = None, properties: Optional[ModelProperties] = None, auth: Dict = None, model_save_serializer: Optional[SERDE] = model_save_serializer_type.JOBLIB, @@ -180,7 +180,10 @@ def __init__( str(type(estimator)).startswith(" Self: """GenericModel Constructor. Parameters @@ -332,6 +344,7 @@ def __init__( model_input_serializer: (SERDE or str, optional). Defaults to None. Instance of ads.model.SERDE. Used for serialize/deserialize model input. """ + self.estimator = estimator self.auth = auth or authutil.default_signer() self.dsc_model = ( @@ -351,7 +364,11 @@ def __init__( self.version = None self.hyperparameter = None self._introspect = ModelIntrospect(self) - self.model_deployment = None + self.model_deployment = ( + ModelDeployment() + .with_infrastructure(ModelDeploymentInfrastructure()) + .with_runtime(ModelDeploymentContainerRuntime()) + ) self.runtime_info = None self._as_onnx = kwargs.pop("as_onnx", False) self._score_args = {} @@ -371,6 +388,7 @@ def __init__( model_input_serde=model_input_serializer, model_save_serializer=model_save_serializer, ) + self.ignore_conda_error = False def _init_serde( self, @@ -633,6 +651,10 @@ def serialize_model( if self._serialize: if not self.model_file_name: self.model_file_name = self._handle_model_file_name(as_onnx=as_onnx) + if not self.estimator: + raise ValueError( + "Parameter `estimator` has to be provided when `serialize=True`, or you can set `serialize=False`." + ) self._serialize_model_helper( initial_types, force_overwrite, X_sample, **kwargs ) @@ -766,6 +788,7 @@ def prepare( training_id: str = _TRAINING_RESOURCE_ID, ignore_pending_changes: bool = True, max_col_num: int = DATA_SCHEMA_MAX_COL_NUM, + ignore_conda_error: bool = False, **kwargs: Dict, ) -> "GenericModel": """Prepare and save the score.py, serialized model and runtime.yaml file. @@ -816,6 +839,8 @@ def prepare( max_col_num: (int, optional). Defaults to utils.DATA_SCHEMA_MAX_COL_NUM. Do not generate the input schema if the input has more than this number of features(columns). + ignore_conda_error: (bool, optional). Defaults to False. + Parameter to ignore error when collecting conda information. kwargs: impute_values: (dict, optional). The dictionary where the key is the column index(or names is accepted @@ -846,6 +871,11 @@ def prepare( elif not self.properties.training_id: self.properties.training_id = _TRAINING_RESOURCE_ID + self.ignore_conda_error = ignore_conda_error + if self.ignore_conda_error: + logger.info( + "`ignore_conda_error` is set to True and `.verify()` is targeted to test the generated score.py on the local conda environment, not the container." + ) if not self.properties.inference_conda_env: try: conda_prefix = os.environ.get("CONDA_PREFIX", None) @@ -853,7 +883,10 @@ def prepare( if "pack_path" in manifest: self.properties.inference_conda_env = manifest["pack_path"] else: - raise ValueError("`inference_conda_env` must be specified.") + if not self.ignore_conda_error: + raise ValueError( + "`inference_conda_env` must be specified for conda runtime. If you are using container runtime, set `ignore_conda_error=True`." + ) self.properties.inference_python_version = ( manifest["python"] if "python" in manifest @@ -861,7 +894,10 @@ def prepare( else self.properties.inference_python_version ) except: - raise ValueError("`inference_conda_env` must be specified.") + if not self.ignore_conda_error: + raise ValueError( + "`inference_conda_env` must be specified for conda runtime. If you are using container runtime, set `ignore_conda_error=True`." + ) self._as_onnx = as_onnx if as_onnx: @@ -900,61 +936,60 @@ def prepare( namespace=namespace, bucketname=DEFAULT_CONDA_BUCKET_NAME, auth=self.auth, + ignore_conda_error=self.ignore_conda_error, ) self._summary_status.update_status( detail="Generated runtime.yaml", status=ModelState.DONE.value ) - if as_onnx: - X_sample = self._onnx_data_transformer( - X_sample, - impute_values=kwargs.pop("impute_values", {}), - force_overwrite=force_overwrite, - ) - try: - self.serialize_model( - as_onnx=as_onnx, - force_overwrite=force_overwrite, - initial_types=initial_types, - X_sample=X_sample, - **kwargs, - ) - self._summary_status.update_status( - detail="Serialized model", status=ModelState.DONE.value - ) - except SerializeModelNotImplementedError as e: - if not os.path.exists( - os.path.join(self.artifact_dir, self.model_file_name) - ): - self._summary_status.update_action( - detail="Serialized model", - action=( - "Model is not automatically serialized. " - f"Serialize the model as `{self.model_file_name}` and " - f"save to the {self.artifact_dir}." - ), - ) - self._summary_status.update_status( - detail="Serialized model", status=ModelState.NEEDSACTION.value + if self.estimator: + if as_onnx: + X_sample = self._onnx_data_transformer( + X_sample, + impute_values=kwargs.pop("impute_values", {}), + force_overwrite=force_overwrite, ) - logger.warning( - f"{self.model_file_name} not found in {self.artifact_dir}. " - f"Save the serialized model under {self.artifact_dir}." + try: + self.serialize_model( + as_onnx=as_onnx, + force_overwrite=force_overwrite, + initial_types=initial_types, + X_sample=X_sample, + **kwargs, ) - self._summary_status.update_action( - detail="Generated score.py", - action=( - "`load_model` is not automatically generated. " - "Finish implementing it and call .verify to check if it works." - ), + self._summary_status.update_status( + detail="Serialized model", status=ModelState.DONE.value ) - except Exception as e: - raise e + except SerializeModelNotImplementedError as e: + if not os.path.exists( + os.path.join(self.artifact_dir, self.model_file_name) + ): + self._summary_status.update_action( + detail="Serialized model", + action=( + "Model is not automatically serialized. " + f"Serialize the model as `{self.model_file_name}` and " + f"save to the {self.artifact_dir}." + ), + ) + self._summary_status.update_status( + detail="Serialized model", status=ModelState.NEEDSACTION.value + ) + logger.warning( + f"{self.model_file_name} not found in {self.artifact_dir}. " + f"Save the serialized model under {self.artifact_dir}." + ) + self._summary_status.update_action( + detail="Generated score.py", + action=( + "`load_model` is not automatically generated. " + "Finish implementing it and call .verify to check if it works." + ), + ) + except Exception as e: + raise e - self._summary_status.update_status( - detail="Generated runtime.yaml", status=ModelState.DONE.value - ) if as_onnx: jinja_template_filename = "score_onnx_new" else: @@ -986,19 +1021,24 @@ def prepare( training_id=self.properties.training_id, ignore_pending_changes=ignore_pending_changes, max_col_num=max_col_num, + ignore_conda_error=self.ignore_conda_error, ) self._summary_status.update_status( detail="Populated metadata(Custom, Taxonomy and Provenance)", status=ModelState.DONE.value, ) + self._summary_status.update_status( detail="Local tested .predict from score.py", status=ModelState.AVAILABLE.value, ) - self._summary_status.update_status( - detail="Conducted Introspect Test", status=ModelState.AVAILABLE.value - ) + + if not self.ignore_conda_error: + self._summary_status.update_status( + detail="Conducted Introspect Test", status=ModelState.AVAILABLE.value + ) + self._summary_status.update_status( detail="Uploaded artifact to model catalog", status=ModelState.AVAILABLE.value, @@ -1197,15 +1237,16 @@ def introspect(self) -> pd.DataFrame: @classmethod def from_model_artifact( - cls, + cls: Type[Self], uri: str, model_file_name: str = None, - artifact_dir: str = None, + artifact_dir: Optional[str] = None, auth: Optional[Dict] = None, force_overwrite: Optional[bool] = False, properties: Optional[ModelProperties] = None, + ignore_conda_error: Optional[bool] = False, **kwargs: dict, - ) -> "GenericModel": + ) -> Self: """Loads model from a folder, or zip/tar archive. Parameters @@ -1229,10 +1270,12 @@ def from_model_artifact( Whether to overwrite existing files or not. properties: (ModelProperties, optional). Defaults to None. ModelProperties object required to save and deploy model. + ignore_conda_error: (bool, optional). Defaults to False. + Parameter to ignore error when collecting conda information. Returns ------- - GenericModel + Self An instance of `GenericModel` class. Raises @@ -1252,21 +1295,26 @@ def from_model_artifact( model_file_name=model_file_name, force_overwrite=force_overwrite, auth=auth, + ignore_conda_error=ignore_conda_error, ) model = cls( estimator=model_artifact.model, artifact_dir=artifact_dir, auth=auth, properties=properties, + **kwargs, ) model.model_file_name = model_file_name or model_artifact.model_file_name model.model_artifact = model_artifact + model.ignore_conda_error = ignore_conda_error model.reload_runtime_info() model._summary_status.update_status( - detail="Generated score.py", status=ModelState.DONE.value + detail="Generated score.py", + status=ModelState.DONE.value, ) model._summary_status.update_status( - detail="Generated runtime.yaml", status=ModelState.DONE.value + detail="Generated runtime.yaml", + status=ModelState.DONE.value, ) model._summary_status.update_status( detail="Serialized model", status=ModelState.DONE.value @@ -1279,17 +1327,18 @@ def from_model_artifact( @classmethod def from_model_catalog( - cls, + cls: Type[Self], model_id: str, model_file_name: str = None, - artifact_dir: str = None, + artifact_dir: Optional[str] = None, auth: Optional[Dict] = None, force_overwrite: Optional[bool] = False, properties: Optional[Union[ModelProperties, Dict]] = None, bucket_uri: Optional[str] = None, remove_existing_artifact: Optional[bool] = True, + ignore_conda_error: Optional[bool] = False, **kwargs, - ) -> "GenericModel": + ) -> Self: """Loads model from model catalog. Parameters @@ -1315,6 +1364,8 @@ def from_model_catalog( size is greater than 2GB. Example: `oci://@/prefix/`. remove_existing_artifact: (bool, optional). Defaults to `True`. Wether artifacts uploaded to object storage bucket need to be removed or not. + ignore_conda_error: (bool, optional). Defaults to False. + Parameter to ignore error when collecting conda information. kwargs: compartment_id : (str, optional) Compartment OCID. If not specified, the value will be taken from the environment variables. @@ -1326,7 +1377,7 @@ def from_model_catalog( Returns ------- - GenericModel + Self An instance of GenericModel class. """ local_vars = _extract_locals(locals()) @@ -1353,6 +1404,8 @@ def from_model_catalog( auth=auth, force_overwrite=force_overwrite, properties=properties, + ignore_conda_error=ignore_conda_error, + **kwargs, ) result_model.dsc_model = dsc_model @@ -1361,30 +1414,35 @@ def from_model_catalog( status=ModelState.DONE.value, ) result_model._summary_status.update_action( - detail="Populated metadata(Custom, Taxonomy and Provenance)", action="" + detail="Populated metadata(Custom, Taxonomy and Provenance)", + action="", ) result_model._summary_status.update_status( detail="Local tested .predict from score.py", status=ModelState.AVAILABLE.value, ) result_model._summary_status.update_status( - detail="Conducted Introspect Test", status=ModelState.AVAILABLE.value + detail="Conducted Introspect Test", + status=ModelState.AVAILABLE.value + if not result_model.ignore_conda_error + else ModelState.NOTAVAILABLE.value, ) return result_model @classmethod def from_model_deployment( - cls, + cls: Type[Self], model_deployment_id: str, model_file_name: str = None, - artifact_dir: str = None, + artifact_dir: Optional[str] = None, auth: Optional[Dict] = None, force_overwrite: Optional[bool] = False, properties: Optional[Union[ModelProperties, Dict]] = None, bucket_uri: Optional[str] = None, remove_existing_artifact: Optional[bool] = True, + ignore_conda_error: Optional[bool] = False, **kwargs, - ) -> "GenericModel": + ) -> Self: """Loads model from model deployment. Parameters @@ -1410,6 +1468,8 @@ def from_model_deployment( size is greater than 2GB. Example: `oci://@/prefix/`. remove_existing_artifact: (bool, optional). Defaults to `True`. Wether artifacts uploaded to object storage bucket need to be removed or not. + ignore_conda_error: (bool, optional). Defaults to False. + Parameter to ignore error when collecting conda information. kwargs: compartment_id : (str, optional) Compartment OCID. If not specified, the value will be taken from the environment variables. @@ -1421,7 +1481,7 @@ def from_model_deployment( Returns ------- - GenericModel + Self An instance of GenericModel class. """ model_deployment = ModelDeployer(config=auth).get_model_deployment( @@ -1441,6 +1501,7 @@ def from_model_deployment( properties=properties, bucket_uri=bucket_uri, remove_existing_artifact=remove_existing_artifact, + ignore_conda_error=ignore_conda_error, **kwargs, ) model._summary_status.update_status( @@ -1538,17 +1599,18 @@ def update_deployment( @classmethod def from_id( - cls, + cls: Type[Self], ocid: str, model_file_name: str = None, - artifact_dir: str = None, + artifact_dir: Optional[str] = None, auth: Optional[Dict] = None, force_overwrite: Optional[bool] = False, properties: Optional[Union[ModelProperties, Dict]] = None, bucket_uri: Optional[str] = None, remove_existing_artifact: Optional[bool] = True, + ignore_conda_error: Optional[bool] = False, **kwargs, - ) -> "GenericModel": + ) -> Self: """Loads model from model OCID or model deployment OCID. Parameters @@ -1582,7 +1644,7 @@ def from_id( Returns ------- - GenericModel + Self An instance of GenericModel class. """ ocid = ocid.lower() @@ -1596,6 +1658,7 @@ def from_id( properties=properties, bucket_uri=bucket_uri, remove_existing_artifact=remove_existing_artifact, + ignore_conda_error=ignore_conda_error, **kwargs, ) elif DataScienceModelType.MODEL in ocid: @@ -1608,6 +1671,7 @@ def from_id( properties=properties, bucket_uri=bucket_uri, remove_existing_artifact=remove_existing_artifact, + ignore_conda_error=ignore_conda_error, **kwargs, ) else: @@ -1626,10 +1690,13 @@ def reload_runtime_info(self) -> None: # reload runtime.yaml runtime_yaml_file = os.path.join(self.artifact_dir, "runtime.yaml") if not os.path.exists(runtime_yaml_file): - raise FileNotFoundError( - f"`runtime.yaml` does not exist in {self.artifact_dir}. " - "Use `RuntimeInfo` class to populate it." - ) + if self.ignore_conda_error: + return self.runtime_info + else: + raise FileNotFoundError( + f"`runtime.yaml` does not exist in {self.artifact_dir}. " + "Use `RuntimeInfo` class to populate it." + ) self.runtime_info = RuntimeInfo.from_yaml(uri=runtime_yaml_file) def reload(self) -> "GenericModel": @@ -1729,19 +1796,23 @@ def save( self.properties.project_id = self.properties.project_id or PROJECT_OCID # check if the runtime_info sync with the runtime.yaml. - runtime_file_path = os.path.join(self.artifact_dir, "runtime.yaml") - runtime_info_from_yaml = RuntimeInfo.from_yaml(uri=runtime_file_path) - if self.runtime_info != runtime_info_from_yaml: - raise RuntimeInfoInconsistencyError( - "`.runtime_info` does not sync with runtime.yaml file. Call " - "`.runtime_info.save()` if you updated `runtime_info`. " - "Call `.reload()` if you updated runtime.yaml file." - ) - # reload to check if load_model works in score.py, i.e. - # whether the model file has been serialized, and whether it can be loaded - # successfully. - self.reload() - if not ignore_introspection: + try: + runtime_file_path = os.path.join(self.artifact_dir, "runtime.yaml") + runtime_info_from_yaml = RuntimeInfo.from_yaml(uri=runtime_file_path) + if self.runtime_info != runtime_info_from_yaml: + raise RuntimeInfoInconsistencyError( + "`.runtime_info` does not sync with runtime.yaml file. Call " + "`.runtime_info.save()` if you updated `runtime_info`. " + "Call `.reload()` if you updated runtime.yaml file." + ) + # reload to check if load_model works in score.py, i.e. + # whether the model file has been serialized, and whether it can be loaded + # successfully. + self.reload() + except: + if not self.ignore_conda_error: + raise + if not self.ignore_conda_error and not ignore_introspection: self._introspect() if self._introspect.status == TEST_STATUS.NOT_PASSED: msg = ( @@ -1793,7 +1864,11 @@ def save( self._summary_status.update_status( detail="Deployed the model", status=ModelState.AVAILABLE.value ) - self.model_deployment = None + self.model_deployment = ( + ModelDeployment() + .with_infrastructure(ModelDeploymentInfrastructure()) + .with_runtime(ModelDeploymentContainerRuntime()) + ) return self.model_id @@ -1820,10 +1895,34 @@ def deploy( deployment_predict_log_id: Optional[str] = None, deployment_memory_in_gbs: Optional[float] = None, deployment_ocpus: Optional[float] = None, + deployment_image: Optional[str] = None, **kwargs: Dict, ) -> "ModelDeployment": """ - Deploys a model. The model needs to be saved to the model catalog at first. + Deploys a model. The model needs to be saved to the model catalog at first. You can deploy the model + on either conda or container runtime. The customized runtime allows you to bring your own service container. + To deploy model on container runtime, make sure to build the container and push it to OCIR. + For more information, see https://docs.oracle.com/en-us/iaas/data-science/using/mod-dep-byoc.htm. + + Example + ------- + # This is an example to deploy model on container runtime + >>> model = GenericModel(estimator=estimator, artifact_dir=tempfile.mkdtemp()) + >>> model.summary_status() + >>> model.prepare( + ... model_file_name="toy_model.pkl", + ... ignore_conda_error=True, # set ignore_conda_error=True for container runtime + ... force_overwrite=True + ... ) + >>> model.verify() + >>> model.save() + >>> model.deploy( + ... deployment_image="iad.ocir.io//:", + ... entrypoint=["python", "/opt/ds/model/deployed_model/api.py"], + ... server_port=5000, + ... health_check_port=5000, + ... environment_variables={"key":"value"} + ... ) Parameters ---------- @@ -1851,6 +1950,8 @@ def deploy( The access log OCID for the access logs. https://docs.oracle.com/en-us/iaas/data-science/using/model_dep_using_logging.htm deployment_predict_log_id: (str, optional). Defaults to None. The predict log OCID for the predict logs. https://docs.oracle.com/en-us/iaas/data-science/using/model_dep_using_logging.htm + deployment_image: (str, optional). Defaults to None. + The OCIR path of docker container image. Required for deploying model on container runtime. kwargs: project_id: (str, optional). Project OCID. If not specified, the value will be taken from the environment variables. @@ -1865,6 +1966,24 @@ def deploy( Freeform tags of the model deployment. defined_tags: (Dict[str, dict[str, object]], optional). Defaults to None. Defined tags of the model deployment. + image_digest: (str, optional). Defaults to None. + The digest of docker container image. + cmd: (List, optional). Defaults to empty. + The command line arguments for running docker container image. + entrypoint: (List, optional). Defaults to empty. + The entrypoint for running docker container image. + server_port: (int, optional). Defaults to 8080. + The server port for docker container image. + health_check_port: (int, optional). Defaults to 8080. + The health check port for docker container image. + deployment_mode: (str, optional). Defaults to HTTPS_ONLY. + The deployment mode. Allowed values are: HTTPS_ONLY and STREAM_ONLY. + input_stream_ids: (List, optional). Defaults to empty. + The input stream ids. Required for STREAM_ONLY mode. + output_stream_ids: (List, optional). Defaults to empty. + The output stream ids. Required for STREAM_ONLY mode. + environment_variables: (Dict, optional). Defaults to empty. + The environment variables for model deployment. Also can be any keyword argument for initializing the `ads.model.deployment.ModelDeploymentProperties`. See `ads.model.deployment.ModelDeploymentProperties()` for details. @@ -1906,8 +2025,6 @@ def deploy( self.properties.deployment_bandwidth_mbps or MODEL_DEPLOYMENT_BANDWIDTH_MBPS ) - self.model_deployment = None - if not self.model_id: raise ValueError( "The model needs to be saved to the Model Catalog " @@ -1924,43 +2041,169 @@ def deploy( "cannot be used without `deployment_log_group_id`." ) - model_deployment_properties = ( - ModelDeploymentProperties(self.model_id, config=self.auth, **kwargs) - .with_prop("display_name", display_name) - .with_prop("description", description) - .with_prop("project_id", self.properties.project_id) - .with_prop("compartment_id", self.properties.compartment_id) - .with_instance_configuration( - { - "INSTANCE_SHAPE": self.properties.deployment_instance_shape, - "INSTANCE_COUNT": self.properties.deployment_instance_count, - "bandwidth_mbps": self.properties.deployment_bandwidth_mbps, - "memory_in_gbs": self.properties.deployment_memory_in_gbs, - "ocpus": self.properties.deployment_ocpus, - } + existing_infrastructure = self.model_deployment.infrastructure + existing_runtime = self.model_deployment.runtime + + web_concurrency = ( + kwargs.pop("web_concurrency", None) + or existing_infrastructure.web_concurrency + ) + if not ( + self.properties.compartment_id or existing_infrastructure.compartment_id + ): + raise ValueError("`compartment_id` has to be provided.") + if not (self.properties.project_id or existing_infrastructure.project_id): + raise ValueError("`project_id` has to be provided.") + infrastructure = ( + ModelDeploymentInfrastructure() + .with_compartment_id( + self.properties.compartment_id or existing_infrastructure.compartment_id + ) + .with_project_id( + self.properties.project_id or existing_infrastructure.project_id + ) + .with_bandwidth_mbps( + self.properties.deployment_bandwidth_mbps + or existing_infrastructure.bandwidth_mbps ) + .with_shape_name( + self.properties.deployment_instance_shape + or existing_infrastructure.shape_name + ) + .with_replica( + self.properties.deployment_instance_count + or existing_infrastructure.replica + ) + .with_web_concurrency(web_concurrency) + ) + + ocpus = ( + self.properties.deployment_ocpus + or existing_infrastructure.shape_config_details.get("ocpus") + ) + memory_in_gbs = ( + self.properties.deployment_memory_in_gbs + or existing_infrastructure.shape_config_details.get("memory_in_gbs") + ) + + if infrastructure.shape_name.endswith("Flex"): + infrastructure.with_shape_config_details( + ocpus=ocpus or MODEL_DEPLOYMENT_INSTANCE_OCPUS, + memory_in_gbs=memory_in_gbs or MODEL_DEPLOYMENT_INSTANCE_MEMORY_IN_GBS, + ) + + access_log_id = ( + self.properties.deployment_access_log_id + or existing_infrastructure.access_log.get("log_id") + ) + access_log_group_id = ( + self.properties.deployment_log_group_id + or existing_infrastructure.access_log.get("log_group_id") ) # specifies the access log id - if self.properties.deployment_access_log_id: - model_deployment_properties.with_access_log( - self.properties.deployment_log_group_id, - self.properties.deployment_access_log_id, + if access_log_id: + infrastructure.with_access_log( + log_group_id=access_log_group_id, + log_id=access_log_id, ) + predict_log_id = ( + self.properties.deployment_predict_log_id + or existing_infrastructure.predict_log.get("log_id") + ) + predict_log_group_id = ( + self.properties.deployment_log_group_id + or existing_infrastructure.predict_log.get("log_group_id") + ) + # specifies the predict log id - if self.properties.deployment_predict_log_id: - model_deployment_properties.with_predict_log( - self.properties.deployment_log_group_id, - self.properties.deployment_predict_log_id, + if predict_log_id: + infrastructure.with_predict_log( + log_group_id=predict_log_group_id, + log_id=predict_log_id, ) - self.model_deployment = ModelDeployer(config=self.auth).deploy( - properties=model_deployment_properties, + environment_variables = ( + kwargs.pop("environment_variables", {}) or existing_runtime.env + ) + deployment_mode = ( + kwargs.pop("deployment_mode", ModelDeploymentMode.HTTPS) + or existing_runtime.deployment_mode + ) + + runtime = None + image = self.properties.deployment_image or existing_runtime.image + if image: + image_digest = ( + kwargs.pop("image_digest", None) or existing_runtime.image_digest + ) + cmd = kwargs.pop("cmd", []) or existing_runtime.cmd + entrypoint = kwargs.pop("entrypoint", []) or existing_runtime.entrypoint + server_port = ( + kwargs.pop("server_port", None) or existing_runtime.server_port + ) + health_check_port = ( + kwargs.pop("health_check_port", None) + or existing_runtime.health_check_port + ) + runtime = ( + ModelDeploymentContainerRuntime() + .with_image(image) + .with_image_digest(image_digest) + .with_cmd(cmd) + .with_entrypoint(entrypoint) + .with_server_port(server_port) + .with_health_check_port(health_check_port) + .with_deployment_mode(deployment_mode) + .with_model_uri(self.model_id) + .with_env(environment_variables) + ) + else: + runtime = ( + ModelDeploymentCondaRuntime() + .with_env(environment_variables) + .with_deployment_mode(deployment_mode) + .with_model_uri(self.model_id) + ) + + if deployment_mode == ModelDeploymentMode.STREAM: + input_stream_ids = ( + kwargs.pop("input_stream_ids", []) or existing_runtime.input_stream_ids + ) + output_stream_ids = ( + kwargs.pop("output_stream_ids", []) + or existing_runtime.output_stream_ids + ) + if not (input_stream_ids and output_stream_ids): + raise ValueError( + "Parameter `input_stream_ids` and `output_stream_ids` need to be provided for `STREAM_ONLY` mode." + ) + + runtime.with_input_stream_ids(input_stream_ids) + runtime.with_output_stream_ids(output_stream_ids) + + freeform_tags = ( + kwargs.pop("freeform_tags", {}) or self.model_deployment.freeform_tags + ) + defined_tags = ( + kwargs.pop("defined_tags", {}) or self.model_deployment.defined_tags + ) + + model_deployment = ( + ModelDeployment() + .with_display_name(display_name or self.model_deployment.display_name) + .with_description(description or self.model_deployment.description) + .with_defined_tags(**defined_tags) + .with_freeform_tags(**freeform_tags) + .with_infrastructure(infrastructure) + .with_runtime(runtime) + ) + + self.model_deployment = model_deployment.deploy( wait_for_completion=wait_for_completion, max_wait_time=max_wait_time, poll_interval=poll_interval, - **kwargs, ) self._summary_status.update_status( detail="Deployed the model", @@ -1986,6 +2229,7 @@ def prepare_save_deploy( training_id: str = _TRAINING_RESOURCE_ID, ignore_pending_changes: bool = True, max_col_num: int = DATA_SCHEMA_MAX_COL_NUM, + ignore_conda_error: bool = False, model_display_name: Optional[str] = None, model_description: Optional[str] = None, model_freeform_tags: Optional[dict] = None, @@ -2002,6 +2246,7 @@ def prepare_save_deploy( deployment_predict_log_id: Optional[str] = None, deployment_memory_in_gbs: Optional[float] = None, deployment_ocpus: Optional[float] = None, + deployment_image: Optional[str] = None, bucket_uri: Optional[str] = None, overwrite_existing_artifact: Optional[bool] = True, remove_existing_artifact: Optional[bool] = True, @@ -2056,6 +2301,8 @@ def prepare_save_deploy( max_col_num: (int, optional). Defaults to utils.DATA_SCHEMA_MAX_COL_NUM. Do not generate the input schema if the input has more than this number of features(columns). + ignore_conda_error: (bool, optional). Defaults to False. + Parameter to ignore error when collecting conda information. model_display_name: (str, optional). Defaults to None. The name of the model. If a model_display_name is not provided in kwargs, a randomly generated easy to remember name with timestamp will be generated, @@ -2093,6 +2340,8 @@ def prepare_save_deploy( Specifies the size of the memory of the model deployment instance in GBs. deployment_ocpus: (float, optional). Defaults to None. Specifies the ocpus count of the model deployment instance. + deployment_image: (str, optional). Defaults to None. + The OCIR path of docker container image. Required for deploying model on container runtime. bucket_uri: (str, optional). Defaults to None. The OCI Object Storage URI where model artifacts will be copied to. The `bucket_uri` is only necessary for downloading large artifacts with @@ -2115,6 +2364,24 @@ def prepare_save_deploy( compartment_id : (str, optional). Compartment OCID. If not specified, the value will be taken either from the environment variables or model properties. + image_digest: (str, optional). Defaults to None. + The digest of docker container image. + cmd: (List, optional). Defaults to empty. + The command line arguments for running docker container image. + entrypoint: (List, optional). Defaults to empty. + The entrypoint for running docker container image. + server_port: (int, optional). Defaults to 8080. + The server port for docker container image. + health_check_port: (int, optional). Defaults to 8080. + The health check port for docker container image. + deployment_mode: (str, optional). Defaults to HTTPS_ONLY. + The deployment mode. Allowed values are: HTTPS_ONLY and STREAM_ONLY. + input_stream_ids: (List, optional). Defaults to empty. + The input stream ids. Required for STREAM_ONLY mode. + output_stream_ids: (List, optional). Defaults to empty. + The output stream ids. Required for STREAM_ONLY mode. + environment_variables: (Dict, optional). Defaults to empty. + The environment variables for model deployment. timeout: (int, optional). Defaults to 10 seconds. The connection timeout in seconds for the client. max_wait_time : (int, optional). Defaults to 1200 seconds. @@ -2168,6 +2435,7 @@ def prepare_save_deploy( training_id=self.properties.training_id, ignore_pending_changes=ignore_pending_changes, max_col_num=max_col_num, + ignore_conda_error=ignore_conda_error, impute_values=kwargs.pop("impute_values", None), ) # Set default model_display_name if not specified - randomly generated easy to remember name generated @@ -2206,6 +2474,7 @@ def prepare_save_deploy( deployment_predict_log_id=self.properties.deployment_predict_log_id, deployment_memory_in_gbs=self.properties.deployment_memory_in_gbs, deployment_ocpus=self.properties.deployment_ocpus, + deployment_image=deployment_image, kwargs=kwargs, ) return self.model_deployment @@ -2285,8 +2554,12 @@ def summary_status(self) -> pd.DataFrame: pd.DataFrame The summary stable of the current status. """ - if self.model_file_name and not os.path.exists( - os.path.join(self.artifact_dir, self.model_file_name) + if ( + not self.ignore_conda_error + and self.model_file_name + and not os.path.exists( + os.path.join(self.artifact_dir, self.model_file_name) + ) ): self._summary_status.update_action( detail="Serialized model", diff --git a/ads/model/model_metadata_mixin.py b/ads/model/model_metadata_mixin.py index f3803cf10..5da762630 100644 --- a/ads/model/model_metadata_mixin.py +++ b/ads/model/model_metadata_mixin.py @@ -270,6 +270,7 @@ def populate_metadata( training_id: str = None, ignore_pending_changes: bool = True, max_col_num: int = DATA_SCHEMA_MAX_COL_NUM, + ignore_conda_error: bool = False, ): """Populates input schema and output schema. If the schema exceeds the limit of 32kb, save as json files to the artifact directory. @@ -313,7 +314,8 @@ def populate_metadata( self._populate_metadata_taxonomy( model=self.estimator, use_case_type=use_case_type ) - self._populate_metadata_custom() + if not ignore_conda_error: + self._populate_metadata_custom() self.populate_schema( data_sample=data_sample, X_sample=X_sample, @@ -326,7 +328,8 @@ def populate_metadata( training_id=training_id, ) self._summary_status.update_action( - detail="Populated metadata(Custom, Taxonomy and Provenance)", action="" + detail="Populated metadata(Custom, Taxonomy and Provenance)", + action="", ) self._summary_status.update_status( detail="Populated metadata(Custom, Taxonomy and Provenance)", diff --git a/ads/model/model_properties.py b/ads/model/model_properties.py index 46641d1ab..311b36f83 100644 --- a/ads/model/model_properties.py +++ b/ads/model/model_properties.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*-- -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ from dataclasses import dataclass @@ -35,6 +35,7 @@ class ModelProperties(BaseProperties): deployment_predict_log_id: str = None deployment_memory_in_gbs: Union[float, int] = None deployment_ocpus: Union[float, int] = None + deployment_image: str = None def _adjust_with_env(self) -> None: """Adjusts env variables. This method is used within `with_env` method.""" diff --git a/ads/opctl/backend/ads_dataflow.py b/ads/opctl/backend/ads_dataflow.py index 1eb26b13a..b8d197118 100644 --- a/ads/opctl/backend/ads_dataflow.py +++ b/ads/opctl/backend/ads_dataflow.py @@ -57,8 +57,7 @@ def run(self) -> None: """ Create DataFlow and DataFlow Run from OCID or cli parameters. """ - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): if self.config["execution"].get("ocid", None): data_flow_id = self.config["execution"]["ocid"] run_id = Job.from_dataflow_job(data_flow_id).run().id @@ -99,8 +98,7 @@ def cancel(self): if not self.config["execution"].get("run_id"): raise ValueError("Can only cancel a DataFlow run.") run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): DataFlowRun.from_ocid(run_id).delete() def delete(self): @@ -109,13 +107,11 @@ def delete(self): """ if self.config["execution"].get("id"): data_flow_id = self.config["execution"]["id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): Job.from_dataflow_job(data_flow_id).delete() elif self.config["execution"].get("run_id"): run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): DataFlowRun.from_ocid(run_id).delete() def watch(self): @@ -123,7 +119,6 @@ def watch(self): Watch DataFlow Run from OCID. """ run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): run = DataFlowRun.from_ocid(run_id) run.watch() diff --git a/ads/opctl/backend/ads_ml_job.py b/ads/opctl/backend/ads_ml_job.py index 46fb27ff9..0cbc9ee10 100644 --- a/ads/opctl/backend/ads_ml_job.py +++ b/ads/opctl/backend/ads_ml_job.py @@ -64,8 +64,7 @@ def apply(self) -> None: """ Create Job and Job Run from YAML. """ - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): job = Job.from_dict(self.config) job.create() job_run = job.run() @@ -77,8 +76,7 @@ def run(self) -> None: Create Job and Job Run from OCID or cli parameters. """ # TODO Check that this still runs smoothly for distributed - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): if self.config["execution"].get("ocid", None): job_id = self.config["execution"]["ocid"] run_id = ( @@ -139,14 +137,12 @@ def delete(self): """ if self.config["execution"].get("id"): job_id = self.config["execution"]["id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): Job.from_datascience_job(job_id).delete() print(f"Job {job_id} has been deleted.") elif self.config["execution"].get("run_id"): run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): DataScienceJobRun.from_ocid(run_id).delete() print(f"Job run {run_id} has been deleted.") @@ -155,8 +151,7 @@ def cancel(self): Cancel Job Run from OCID. """ run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): DataScienceJobRun.from_ocid(run_id).cancel() print(f"Job run {run_id} has been cancelled.") @@ -166,8 +161,7 @@ def watch(self): """ run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): run = DataScienceJobRun.from_ocid(run_id) run.watch() @@ -377,8 +371,7 @@ def generate_worker_name(worker_jobrun_conf, i): return f"{worker_jobrun_conf['name']}-{i}" def run_diagnostics(self, cluster_info, dry_run=False, **kwargs): - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): main_jobrun_conf, worker_jobrun_conf_list = self.prepare_job_config( cluster_info=cluster_info ) @@ -423,8 +416,7 @@ def run(self, cluster_info, dry_run=False) -> None: * The Job Definition will contain all the environment variables defined at the cluster/spec/config level, environment variables defined by the user at runtime/spec/env level and `OCI__` derived from the yaml specification * The Job Run will have overrides provided by the user under cluster/spec/{main|worker}/config section and `OCI__MODE`={MASTER|WORKER} depending on the run type """ - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): main_jobrun_conf, worker_jobrun_conf_list = self.prepare_job_config( cluster_info=cluster_info ) diff --git a/ads/opctl/backend/ads_ml_pipeline.py b/ads/opctl/backend/ads_ml_pipeline.py index d109524de..d618259da 100644 --- a/ads/opctl/backend/ads_ml_pipeline.py +++ b/ads/opctl/backend/ads_ml_pipeline.py @@ -36,8 +36,7 @@ def apply(self) -> None: """ Create Pipeline and Pipeline Run from YAML. """ - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): pipeline = Pipeline.from_dict(self.config) pipeline.create() pipeline_run = pipeline.run() @@ -49,8 +48,7 @@ def run(self) -> None: Create Pipeline and Pipeline Run from OCID. """ pipeline_id = self.config["execution"]["ocid"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): pipeline = Pipeline.from_ocid(ocid=pipeline_id) pipeline_run = pipeline.run() print("PIPELINE RUN OCID:", pipeline_run.id) @@ -61,14 +59,12 @@ def delete(self) -> None: """ if self.config["execution"].get("id"): pipeline_id = self.config["execution"]["id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): Pipeline.from_ocid(pipeline_id).delete() print(f"Pipeline {pipeline_id} has been deleted.") elif self.config["execution"].get("run_id"): run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): PipelineRun.from_ocid(run_id).delete() print(f"Pipeline run {run_id} has been deleted.") @@ -77,8 +73,7 @@ def cancel(self) -> None: Cancel Pipeline Run from OCID. """ run_id = self.config["execution"]["run_id"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): PipelineRun.from_ocid(run_id).cancel() print(f"Pipeline run {run_id} has been cancelled.") @@ -88,6 +83,5 @@ def watch(self) -> None: """ run_id = self.config["execution"]["run_id"] log_type = self.config["execution"]["log_type"] - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): PipelineRun.from_ocid(run_id).watch(log_type=log_type) diff --git a/ads/opctl/backend/ads_model_deployment.py b/ads/opctl/backend/ads_model_deployment.py index 151c7719e..a02bf6332 100644 --- a/ads/opctl/backend/ads_model_deployment.py +++ b/ads/opctl/backend/ads_model_deployment.py @@ -41,8 +41,7 @@ def apply(self) -> None: wait_for_completion = self.config["execution"].get("wait_for_completion") max_wait_time = self.config["execution"].get("max_wait_time") poll_interval = self.config["execution"].get("poll_interval") - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): model_deployment = ModelDeployment.from_dict(self.config) model_deployment.deploy( wait_for_completion=wait_for_completion, @@ -59,8 +58,7 @@ def delete(self) -> None: wait_for_completion = self.config["execution"].get("wait_for_completion") max_wait_time = self.config["execution"].get("max_wait_time") poll_interval = self.config["execution"].get("poll_interval") - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): model_deployment = ModelDeployment.from_id(model_deployment_id) if model_deployment.lifecycle_state in [ OCIModelDeployment.LIFECYCLE_STATE_DELETED @@ -95,8 +93,7 @@ def activate(self) -> None: wait_for_completion = self.config["execution"].get("wait_for_completion") max_wait_time = self.config["execution"].get("max_wait_time") poll_interval = self.config["execution"].get("poll_interval") - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): model_deployment = ModelDeployment.from_id(model_deployment_id) if ( model_deployment.lifecycle_state @@ -132,8 +129,7 @@ def deactivate(self) -> None: wait_for_completion = self.config["execution"].get("wait_for_completion") max_wait_time = self.config["execution"].get("max_wait_time") poll_interval = self.config["execution"].get("poll_interval") - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): model_deployment = ModelDeployment.from_id(model_deployment_id) if ( model_deployment.lifecycle_state @@ -169,8 +165,7 @@ def watch(self) -> None: log_type = self.config["execution"].get("log_type") interval = self.config["execution"].get("interval") log_filter = self.config["execution"].get("log_filter") - with AuthContext(): - ads.set_auth(auth=self.auth_type, profile=self.profile) + with AuthContext(auth=self.auth_type, profile=self.profile): model_deployment = ModelDeployment.from_id(model_deployment_id) model_deployment.watch( log_type=log_type, interval=interval, log_filter=log_filter diff --git a/ads/opctl/distributed/common/abstract_cluster_provider.py b/ads/opctl/distributed/common/abstract_cluster_provider.py index 0dbc1e620..12a3930ce 100644 --- a/ads/opctl/distributed/common/abstract_cluster_provider.py +++ b/ads/opctl/distributed/common/abstract_cluster_provider.py @@ -20,7 +20,8 @@ import psutil from ads.jobs import Job from ads.opctl.distributed.common import cluster_config_helper -from ads.jobs.templates.driver_oci import GitSSHKey, GitManager, JobRunner +from ads.jobs.templates.driver_oci import GitSSHKey, GitManager +from ads.jobs.templates.driver_utils import JobRunner from ads.jobs.builders.runtimes.artifact import Artifact diff --git a/setup.py b/setup.py index fa585c1d2..753b4a88a 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ "gitpython>=3.1.2", "matplotlib>=3.1.3", "numpy>=1.19.2", - "oci>=2.93.1", + "oci>=2.96.0", "ocifs>=1.1.3", "pandas>1.2.1,<1.6", "python_jsonschema_objects>=0.3.13", From a82f6a269f2710f7b92726e4a12784517cdab694 Mon Sep 17 00:00:00 2001 From: Ziqun Ye Date: Wed, 22 Mar 2023 16:08:44 -0700 Subject: [PATCH 146/147] modify the release date --- docs/source/release_notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index d6ceea429..172dc07b3 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -4,7 +4,7 @@ Release Notes 2.8.3 ----- -Release date: March 21, 2023 +Release date: March 22, 2023 * Added support for custom containers (Bring Your Own Container or BYOC) and environment variables for :py:class:`~ads.model.GenericModel`. * Added default values for configuring parameters in :py:class:`~ads.model.ModelDeployment`, such as default flex shape, ocpus, memory in gbs, bandwidth, and instance count. From 14fc6324b3e42212524c983f8defe89bd4658956 Mon Sep 17 00:00:00 2001 From: Liuda Rudenka Date: Thu, 23 Mar 2023 12:05:58 -0500 Subject: [PATCH 147/147] Add workflow yml files to main branch (#116) --- .../workflows/run-unittests-default_setup.yml | 65 +++++++++++++ .github/workflows/run-unittests.yml | 95 ++++++++++++------- 2 files changed, 125 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/run-unittests-default_setup.yml diff --git a/.github/workflows/run-unittests-default_setup.yml b/.github/workflows/run-unittests-default_setup.yml new file mode 100644 index 000000000..abf572b01 --- /dev/null +++ b/.github/workflows/run-unittests-default_setup.yml @@ -0,0 +1,65 @@ +name: Unit Tests unitary/default_setup/* + +on: + workflow_dispatch: + +# Cancel in progress workflows on pull_requests. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + name: python ${{ matrix.python-version }}, tests/unitary/default_setup + runs-on: ubuntu-latest + timeout-minutes: 20 + + strategy: + fail-fast: false + matrix: + python-version: ["3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + + # Caching python libraries installed with pip + # https://github.com/actions/cache/blob/main/examples.md#python---pip + - uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/test-requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + +# - name: "Test config setup" +# shell: bash +# env: +# ADS_OCI_CONFIG_REALLIKE: ${{ secrets.ADS_OCI_CONFIG_REALLIKE }} +# ADS_OCI_PRIVATE_KEY_REALLIKE: ${{ secrets.ADS_OCI_PRIVATE_KEY_REALLIKE }} +# run: | +# set -x # print commands that are executed +# mkdir -p /home/runner/.oci +# ls -lha +# echo "$ADS_OCI_CONFIG_REALLIKE" > "/home/runner/.oci/config" +# echo "$ADS_OCI_PRIVATE_KEY_REALLIKE" > "/home/runner/.oci/oci_ads_user.pem" +# echo "key_file=/home/runner/.oci/oci_ads_user.pem" >> "/home/runner/.oci/config" + +# - name: "Run default_setup tests folder ONLY with minimum ADS dependencies" +# timeout-minutes: 15 +# shell: bash +# env: +# NB_SESSION_COMPARTMENT_OCID: ocid1.compartment.oc1. +# NoDependency: True +# run: | +# set -x # print commands that are executed +# $CONDA/bin/conda init +# source /home/runner/.bashrc +# pip install -r test-requirements.txt +# python -m pytest -v -p no:warnings --durations=5 tests/unitary/default_setup diff --git a/.github/workflows/run-unittests.yml b/.github/workflows/run-unittests.yml index 2ca6c6b94..8dc2a6781 100644 --- a/.github/workflows/run-unittests.yml +++ b/.github/workflows/run-unittests.yml @@ -1,16 +1,7 @@ -name: Unit Tests +name: Unit Tests unitary on: workflow_dispatch: - push: - branches: - - main - - 'release/**' - - develop - paths: - - '!docs/**' - - pull_request: # Cancel in progress workflows on pull_requests. # https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value @@ -21,47 +12,81 @@ concurrency: permissions: contents: read + +# hack for https://github.com/actions/cache/issues/810#issuecomment-1222550359 +env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5 + jobs: test: - name: ${{ matrix.tests-type }}, python ${{ matrix.python-version }} + name: python ${{ matrix.python-version }}, ${{ matrix.test-path }}, ignore ${{ matrix.ignore-path }} runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: 90 strategy: fail-fast: false matrix: - python-version: ["3.7","3.8","3.9","3.10"] - tests-type: ["DefaultSetup"] + python-version: ["3.8", "3.9", "3.10"] + test-path: ["tests/unitary", "tests/unitary/with_extras/model"] + include: + - test-path: "tests/unitary" + ignore-path: "tests/unitary/with_extras/model" steps: - uses: actions/checkout@v3 -# - uses: actions/cache@v3 -# with: -# path: ~/.cache/pip -# key: ${{ runner.os }}-pip-${{ hashFiles('**/test-requirements.txt') }} -# restore-keys: | -# ${{ runner.os }}-pip- + + # Caching python libraries installed with pip + # https://github.com/actions/cache/blob/main/examples.md#python---pip + - uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/dev-requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - name: "Setup test env" - run: | - pip install coverage pytest-codecov tox==4.2.8 +# - name: "Test config setup" +# shell: bash +# env: +# ADS_OCI_CONFIG_REALLIKE: ${{ secrets.ADS_OCI_CONFIG_REALLIKE }} +# ADS_OCI_PRIVATE_KEY_REALLIKE: ${{ secrets.ADS_OCI_PRIVATE_KEY_REALLIKE }} +# run: | +# set -x # print commands that are executed +# mkdir -p /home/runner/.oci +# ls -lha +# echo "$ADS_OCI_CONFIG_REALLIKE" > "/home/runner/.oci/config" +# echo "$ADS_OCI_PRIVATE_KEY_REALLIKE" > "/home/runner/.oci/oci_ads_user.pem" +# echo "key_file=/home/runner/.oci/oci_ads_user.pem" >> "/home/runner/.oci/config" + +# - name: "Test env setup" +# timeout-minutes: 20 +# shell: bash +# run: | +# set -x # print commands that are executed +# sudo apt-get install libkrb5-dev graphviz +# $CONDA/bin/conda init +# source /home/runner/.bashrc +# pip install setuptools +# pip install -r dev-requirements.txt - - name: "Run unit tests" - timeout-minutes: 45 - shell: bash - run: | - set -x # print commands that are executed -# coverage erase -# ./scripts/runtox.sh "${{ matrix.python-version }}-${{ matrix.tests-type }}" --cov --cov-report= -# coverage combine .coverage-* -# coverage html -i +# - name: "Run unitary tests folder with maximum ADS dependencies" +# timeout-minutes: 60 +# shell: bash +# env: +# NB_SESSION_COMPARTMENT_OCID: ocid1.compartment.oc1. +# CONDA_PREFIX: /usr/share/miniconda +# run: | +# python -m pytest -v -p no:warnings --durations=5 \ +# ${{ matrix.test-path }} \ +# --ignore "${{ matrix.ignore-path }}" \ +# --cov --cov-append --cov-report=html - # Uploading test artifacts - # https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts#uploading-build-and-test-artifacts -# - name: "Upload artifact" +# Uploading test artifacts +# https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts#uploading-build-and-test-artifacts +# - name: "Save html coverage report" # uses: actions/upload-artifact@v3 # with: # name: code-coverage-report

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 diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_diagnostics.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_diagnostics.png deleted file mode 100644 index 7f13be3602f2e5e0af0a7f5aabc4cf93e388c72f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98059 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 diff --git a/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_scatter.png b/docs/source/user_guide/model_explainability/figures/ads_mlx_titanic_pi_scatter.png deleted file mode 100644 index 9488c99c1793ae17f25569c8fd66d158efa847c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24804 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