diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6b6abc5eb2..fb03d0937e 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,3 +1,4 @@
# See for instructions on this file https://help.github.com/articles/about-codeowners/
/python-sdk/tutorials/automl-with-azureml @rtanase @cesardelatorre @anupsms @lauralj103 @jeff-shepherd @raduk @swatig007 @martinezgjuan @MercyPrasanna @PhaniShekhar @yoyolin @sjinthehouse @vadthyavath @mispa-ms
+/tutorials/ @sdgilley
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0c8ff6a495..bf83b7814b 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,7 +4,10 @@
# Checklist
-- [ ] I have read the [contribution guidelines](https://github.com/Azure/azureml-examples/blob/main/CONTRIBUTING.md)
+- [ ] I have read the contribution guidelines.
+ - [General](https://github.com/Azure/azureml-examples/blob/main/CONTRIBUTING.md)
+ - [SDK](https://github.com/Azure/azureml-examples/blob/main/sdk/CONTRIBUTING.md)
+ - [CLI](https://github.com/Azure/azureml-examples/blob/main/cli/CONTRIBUTING.md)
- [ ] I have coordinated with the docs team (mldocs@microsoft.com) if this PR deletes files or changes any file names or file extensions.
- [ ] Pull request includes test coverage for the included changes.
- [ ] This notebook or file is added to the [CODEOWNERS](https://github.com/Azure/azureml-examples/blob/main/.github/CODEOWNERS) file, pointing to the author or the author's team.
diff --git a/.github/workflows/cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline.yml b/.github/workflows/cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline.yml
new file mode 100644
index 0000000000..c405332fcb
--- /dev/null
+++ b/.github/workflows/cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline.yml
@@ -0,0 +1,51 @@
+# This code is autogenerated.
+# Code is generated by running custom script: python3 readme.py
+# Any manual changes to this file may cause incorrect behavior.
+# Any manual changes will be overwritten if the code is regenerated.
+
+name: cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "4 6/12 * * *"
+ pull_request:
+ branches:
+ - main
+ paths:
+ - cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/**
+ - infra/bootstrapping/**
+ - .github/workflows/cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline.yml
+ - cli/run-pipeline-jobs.sh
+ - cli/setup.sh
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: check out repo
+ uses: actions/checkout@v2
+ - name: azure login
+ uses: azure/login@v1
+ with:
+ creds: ${{secrets.AZUREML_CREDENTIALS}}
+ - name: bootstrap resources
+ run: |
+ echo '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}';
+ bash bootstrap.sh
+ working-directory: infra/bootstrapping
+ continue-on-error: false
+ - name: setup-cli
+ run: |
+ source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
+ source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
+ bash setup.sh
+ working-directory: cli
+ continue-on-error: true
+ - name: run job
+ run: |
+ source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
+ source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
+ bash -x ../../../run-job.sh pipeline.yml
+ working-directory: cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component
diff --git a/.github/workflows/sdk-featurestore_sample-test_featurestore_cli_samples.yml b/.github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_cli_samples.yml
similarity index 83%
rename from .github/workflows/sdk-featurestore_sample-test_featurestore_cli_samples.yml
rename to .github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_cli_samples.yml
index a31b9cb64a..ace1e47a45 100644
--- a/.github/workflows/sdk-featurestore_sample-test_featurestore_cli_samples.yml
+++ b/.github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_cli_samples.yml
@@ -3,7 +3,7 @@
# Any manual changes to this file may cause incorrect behavior.
# Any manual changes will be overwritten if the code is regenerated.
-name: sdk-featurestore_sample-test_featurestore_cli_samples
+name: sdk-featurestore_sample-automation-test-test_featurestore_cli_samples
# This file is created by sdk/python/readme.py.
# Please do not edit directly.
on:
@@ -14,8 +14,8 @@ on:
branches:
- main
paths:
- - sdk/python/featurestore_sample/**
- - .github/workflows/sdk-featurestore_sample-test_featurestore_cli_samples.yml
+ - sdk/python/featurestore_sample/automation-test/**
+ - .github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_cli_samples.yml
- sdk/python/dev-requirements.txt
- infra/bootstrapping/**
- sdk/python/setup.sh
@@ -60,10 +60,10 @@ jobs:
continue-on-error: true
- name: setup feature-store resources
run: |
- bash -x setup-resources-cli.sh test_featurestore_cli_samples.ipynb
+ bash -x automation-test/setup-resources-cli.sh automation-test/test_featurestore_cli_samples.ipynb
working-directory: sdk/python/featurestore_sample
continue-on-error: true
- - name: run featurestore_sample/test_featurestore_cli_samples.ipynb
+ - name: run featurestore_sample/automation-test/test_featurestore_cli_samples.ipynb
run: |
source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
@@ -71,10 +71,10 @@ jobs:
bash "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh" replace_template_values "test_featurestore_cli_samples.ipynb";
[ -f "../../.azureml/config" ] && cat "../../.azureml/config";
papermill -k python test_featurestore_cli_samples.ipynb test_featurestore_cli_samples.output.ipynb
- working-directory: sdk/python/featurestore_sample
+ working-directory: sdk/python/featurestore_sample/automation-test
- name: upload notebook's working folder as an artifact
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test_featurestore_cli_samples
- path: sdk/python/featurestore_sample
+ path: sdk/python/featurestore_sample/automation-test
diff --git a/.github/workflows/sdk-featurestore_sample-test_featurestore_sdk_samples.yml b/.github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_sdk_samples.yml
similarity index 83%
rename from .github/workflows/sdk-featurestore_sample-test_featurestore_sdk_samples.yml
rename to .github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_sdk_samples.yml
index 360b615937..0cd5cf9434 100644
--- a/.github/workflows/sdk-featurestore_sample-test_featurestore_sdk_samples.yml
+++ b/.github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_sdk_samples.yml
@@ -3,7 +3,7 @@
# Any manual changes to this file may cause incorrect behavior.
# Any manual changes will be overwritten if the code is regenerated.
-name: sdk-featurestore_sample-test_featurestore_sdk_samples
+name: sdk-featurestore_sample-automation-test-test_featurestore_sdk_samples
# This file is created by sdk/python/readme.py.
# Please do not edit directly.
on:
@@ -14,8 +14,8 @@ on:
branches:
- main
paths:
- - sdk/python/featurestore_sample/**
- - .github/workflows/sdk-featurestore_sample-test_featurestore_sdk_samples.yml
+ - sdk/python/featurestore_sample/automation-test/**
+ - .github/workflows/sdk-featurestore_sample-automation-test-test_featurestore_sdk_samples.yml
- sdk/python/dev-requirements.txt
- infra/bootstrapping/**
- sdk/python/setup.sh
@@ -60,10 +60,10 @@ jobs:
continue-on-error: true
- name: setup feature-store resources
run: |
- bash -x setup-resources.sh test_featurestore_sdk_samples.ipynb
+ bash -x automation-test/setup-resources.sh automation-test/test_featurestore_sdk_samples.ipynb
working-directory: sdk/python/featurestore_sample
continue-on-error: true
- - name: run featurestore_sample/test_featurestore_sdk_samples.ipynb
+ - name: run featurestore_sample/automation-test/test_featurestore_sdk_samples.ipynb
run: |
source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
@@ -71,10 +71,10 @@ jobs:
bash "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh" replace_template_values "test_featurestore_sdk_samples.ipynb";
[ -f "../../.azureml/config" ] && cat "../../.azureml/config";
papermill -k python test_featurestore_sdk_samples.ipynb test_featurestore_sdk_samples.output.ipynb
- working-directory: sdk/python/featurestore_sample
+ working-directory: sdk/python/featurestore_sample/automation-test
- name: upload notebook's working folder as an artifact
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test_featurestore_sdk_samples
- path: sdk/python/featurestore_sample
+ path: sdk/python/featurestore_sample/automation-test
diff --git a/.github/workflows/sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline.yml b/.github/workflows/sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline.yml
new file mode 100644
index 0000000000..fe75f40369
--- /dev/null
+++ b/.github/workflows/sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline.yml
@@ -0,0 +1,75 @@
+# This code is autogenerated.
+# Code is generated by running custom script: python3 readme.py
+# Any manual changes to this file may cause incorrect behavior.
+# Any manual changes will be overwritten if the code is regenerated.
+
+name: sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline
+# This file is created by sdk/python/readme.py.
+# Please do not edit directly.
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "41 6/12 * * *"
+ pull_request:
+ branches:
+ - main
+ paths:
+ - sdk/python/jobs/pipelines/1l_flow_in_pipeline/**
+ - .github/workflows/sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline.yml
+ - sdk/python/dev-requirements.txt
+ - infra/bootstrapping/**
+ - sdk/python/setup.sh
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: check out repo
+ uses: actions/checkout@v2
+ - name: setup python
+ uses: actions/setup-python@v2
+ with:
+ python-version: "3.8"
+ - name: pip install notebook reqs
+ run: pip install -r sdk/python/dev-requirements.txt
+ - name: azure login
+ uses: azure/login@v1
+ with:
+ creds: ${{secrets.AZUREML_CREDENTIALS}}
+ - name: bootstrap resources
+ run: |
+ echo '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}';
+ bash bootstrap.sh
+ working-directory: infra/bootstrapping
+ continue-on-error: false
+ - name: setup SDK
+ run: |
+ source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
+ source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
+ bash setup.sh
+ working-directory: sdk/python
+ continue-on-error: true
+ - name: setup-cli
+ run: |
+ source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
+ source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
+ bash setup.sh
+ working-directory: cli
+ continue-on-error: true
+ - name: run jobs/pipelines/1l_flow_in_pipeline/flow_in_pipeline.ipynb
+ run: |
+ source "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh";
+ source "${{ github.workspace }}/infra/bootstrapping/init_environment.sh";
+ bash "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh" generate_workspace_config "../../.azureml/config.json";
+ bash "${{ github.workspace }}/infra/bootstrapping/sdk_helpers.sh" replace_template_values "flow_in_pipeline.ipynb";
+ [ -f "../../.azureml/config" ] && cat "../../.azureml/config";
+ papermill -k python flow_in_pipeline.ipynb flow_in_pipeline.output.ipynb
+ working-directory: sdk/python/jobs/pipelines/1l_flow_in_pipeline
+ - name: upload notebook's working folder as an artifact
+ if: ${{ always() }}
+ uses: actions/upload-artifact@v2
+ with:
+ name: flow_in_pipeline
+ path: sdk/python/jobs/pipelines/1l_flow_in_pipeline
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d14591b2fd..0edef474f8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -29,13 +29,13 @@ This repository contains notebooks and sample code that demonstrate how to devel
## Issues
-All forms of feedback are welcome through [issues](https://github.com/Azure/azureml-examples/issues/new/choose) - please follow the pre-defined templates where applicable.
+All forms of feedback are welcome through [issues](https://github.com/Azure/azureml-examples/issues/new/choose). Please follow the pre-defined templates where applicable.
## Repository structure
Azure Machine Learning has multiple developer experiences. The subdirectories at the root of the repo correspond to a developer experience, with the slight exception of `notebooks`.
-The `notebooks` directory is intended for iterative, interactive code development examples such as exploratory data anlysis or querying logged metrics.
+The `notebooks` directory is intended for iterative, interactive code development examples such as exploratory data analysis or querying logged metrics.
## Pull Requests
@@ -67,30 +67,33 @@ Also if adding new examples or changing existing descriptions, run the `readme.p
python readme.py
```
-This will also generate a GitHub Actions workflow file for any new examples in the `.github/workflows` directory (with exceptions) to test the examples on the PR and regularly after merging into the main branch. PRs which edit existing examples will generally trigger a workflow to test the example. See the specific contributing guidelines for the subdirectories for further details. If the new notebook uses compute cluster, please add it to the `sdk/python/notebooks_config.ini` file so the compute clusters will be properly deleted after notebook run was finished. Create a section with the notebook name and add the option `COMPUTE_NAMES` with the compute cluster name.
+This will also generate a GitHub Actions workflow file for any new examples in the `.github/workflows` directory (with exceptions) to test the examples on the PR and regularly after merging into the main branch. PRs which edit existing examples will generally trigger a workflow to test the example. See the specific contributing guidelines for the subdirectories for further details. If the new notebook uses a compute cluster, please add it to the `sdk/python/notebooks_config.ini` file so the compute clusters will be properly deleted after notebook run was finished. Create a section with the notebook name and add the option `COMPUTE_NAMES` with the compute cluster name.
### Discoverability
Examples in this repository can be indexed in the [Microsoft code samples browser](https://docs.microsoft.com/samples), enabling organic discoverability. To accomplish this:
-- add an excellent `README.md` file in the example directory
+- add an excellent `README.md` file in the example directory noting the overview, objective, and estimated runtime. (Note than estimated runtimes should not exceed 30 minutes).
- add required YAML frontmatter at the top of the `README.md`
-The YAML frontmatter is this:
+The YAML frontmatter format looks like this:
```YAML
---
page_type: sample
languages:
- azurecli
-- python
+- language1
+- language2
products:
- azure-machine-learning
description: Example description.
---
```
-**Edit the description** and update the languages as needed.
+Edit the description and update the languages as needed.
+
+The Code Samples browser content is updated twice a week, so it may take a few days for your changes to be reflected.
### Other resources
* [CLI contributing guide.](cli/CONTRIBUTING.md)
diff --git a/README.md b/README.md
index 52889cbb8d..a02856f036 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,47 @@
----
-page_type: sample
-languages:
-- azurecli
-- python
-products:
-- azure-machine-learning
-description: Top-level directory for official Azure Machine Learning sample code and examples.
----
-
# Azure Machine Learning examples
-[![Python code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![license: MIT](https://img.shields.io/badge/License-MIT-purple.svg)](LICENSE)
Welcome to the Azure Machine Learning examples repository!
-## Contents
-
-|directory||description|
-|-|-|-|
-|[`.github`](.github)||GitHub files like issue templates and actions workflows.|
-|[`cli`](cli)||Azure Machine Learning CLI v2 examples.||
-|[`sdk`](sdk)||Azure Machine Learning SDK v2 examples.||
-||[`python`](sdk/python/)| Azure Machine Learning Python SDK v2 examples.|
-||[`dotnet`](sdk/dotnet)| Azure Machine Learning .Net SDK v2 examples.|
-|[`setup`](setup)||Folder with setup scripts|
-||[`setup-ci`](setup/setup-ci)|Setup scripts to customize and configure ||an Azure Machine Learning compute instance.|
-||[`setupdsvm`](setup/setup-dsvm/RStudio/)|Setup RStudio on Data Science Virtual Machine (DSVM)|
-||[`setup-repo`](setup/setup-repo)|Setup scripts for Azure/azureml-examples.|
-|[`tutorials`](tutorials/)||Azure Machine Learning end to end Python SDK v2 tutorials|
+## About This Repository
+
+The azureml-examples repository contains examples and tutorials to help you learn how to use Azure Machine Learning
+(Azure ML) services and features.
+
+
+### Getting Started
+
+If you're getting started with Azure ML, consider working through our [tutorials] for the v2 Python SDK. You may
+also want to read through our [documentation](#supplementary-documentation).
+
+### SDKs
+
+The `sdk/` folder houses the examples for the Azure ML SDKs across several languages.
+
+We have an extensive collection of examples for the [Azure ML Python SDK v2][azure cli ml extension v2 overview] in
+[`sdk/python`][azureml python sdk v2 examples].
+
+We also offer some examples for our SDKs in other languages:
+
+* .NET: [`sdk/dotnet`][azureml dotnet sdk v2 examples]
+* TypeScript: [`sdk/typescript`][azureml typescript sdk v2 examples]
+
+### Azure Machine Learning extension for Azure CLI
+
+The [`cli/` folder][azureml cli extension examples] hosts our examples to use the
+[Azure Machine Learning extension][azure cli ml extension v2 overview] for [Azure CLI][azure cli overview].
+
+_Note_: If you're looking for examples that submit Azure ML jobs that run non-Python code, see:
+
+* **R**: [`cli/jobs/single-step/r`](./cli/jobs/single-step/r)
+
+
+## Supplementary Documentation
+
+- [Azure Machine Learning Documentation](https://docs.microsoft.com/azure/machine-learning)
+- [AzureML Python SDK v2 Overview]
+- [Azure CLI ML extension v2 Overview]
## Contributing
@@ -38,7 +51,11 @@ We welcome contributions and suggestions! Please see the [contributing guideline
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). Please see the [code of conduct](CODE_OF_CONDUCT.md) for details.
-## Reference
-
-- [Documentation](https://docs.microsoft.com/azure/machine-learning)
-
+[tutorials]: ./tutorials
+[azure cli overview]: https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli
+[azureml cli extension examples]: ./cli
+[azureml python sdk v2 examples]: ./sdk/python
+[azureml dotnet sdk v2 examples]: ./sdk/dotnet
+[azureml typescript sdk v2 examples]: ./sdk/typescript
+[azure cli ml extension v2 overview]: https://learn.microsoft.com/en-us/azure/machine-learning/concept-v2?view=azureml-api-2#azure-machine-learning-cli-v2
+[azureml python sdk v2 overview]: https://learn.microsoft.com/en-us/azure/machine-learning/concept-v2?view=azureml-api-2#azure-machine-learning-python-sdk-v2
diff --git a/cli/README.md b/cli/README.md
index 066b48bed5..b2d18a7b55 100644
--- a/cli/README.md
+++ b/cli/README.md
@@ -183,6 +183,7 @@ path|status|description
[jobs/pipelines-with-components/image_classification_with_densenet/pipeline.yml](jobs/pipelines-with-components/image_classification_with_densenet/pipeline.yml)|[![jobs/pipelines-with-components/image_classification_with_densenet/pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-image_classification_with_densenet-pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-image_classification_with_densenet-pipeline.yml)|Train densenet for image classification
[jobs/pipelines-with-components/nyc_taxi_data_regression/pipeline.yml](jobs/pipelines-with-components/nyc_taxi_data_regression/pipeline.yml)|[![jobs/pipelines-with-components/nyc_taxi_data_regression/pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-nyc_taxi_data_regression-pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-nyc_taxi_data_regression-pipeline.yml)|Train regression model based on nyc taxi dataset
[jobs/pipelines-with-components/nyc_taxi_data_regression/single-job-pipeline.yml](jobs/pipelines-with-components/nyc_taxi_data_regression/single-job-pipeline.yml)|[![jobs/pipelines-with-components/nyc_taxi_data_regression/single-job-pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-nyc_taxi_data_regression-single-job-pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-nyc_taxi_data_regression-single-job-pipeline.yml)|Single job pipeline to train regression model based on nyc taxi dataset
+[jobs/pipelines-with-components/pipeline_job_with_flow_as_component/pipeline.yml](jobs/pipelines-with-components/pipeline_job_with_flow_as_component/pipeline.yml)|[![jobs/pipelines-with-components/pipeline_job_with_flow_as_component/pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-pipeline_job_with_flow_as_component-pipeline.yml)|The hello world pipeline job with flow as component
[jobs/pipelines-with-components/pipeline_with_hyperparameter_sweep/pipeline.yml](jobs/pipelines-with-components/pipeline_with_hyperparameter_sweep/pipeline.yml)|[![jobs/pipelines-with-components/pipeline_with_hyperparameter_sweep/pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-pipeline_with_hyperparameter_sweep-pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-pipeline_with_hyperparameter_sweep-pipeline.yml)|Tune hyperparameters using TF component
[jobs/pipelines-with-components/pipeline_with_pipeline_component/nyc_taxi_data_regression_with_pipeline_component/data_pipeline/data_pipeline.yml](jobs/pipelines-with-components/pipeline_with_pipeline_component/nyc_taxi_data_regression_with_pipeline_component/data_pipeline/data_pipeline.yml)|[![jobs/pipelines-with-components/pipeline_with_pipeline_component/nyc_taxi_data_regression_with_pipeline_component/data_pipeline/data_pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-pipeline_with_pipeline_component-nyc_taxi_data_regression_with_pipeline_component-data_pipeline-data_pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-pipeline_with_pipeline_component-nyc_taxi_data_regression_with_pipeline_component-data_pipeline-data_pipeline.yml)|pipeline component with data prep and transformation
[jobs/pipelines-with-components/pipeline_with_pipeline_component/nyc_taxi_data_regression_with_pipeline_component/pipeline.yml](jobs/pipelines-with-components/pipeline_with_pipeline_component/nyc_taxi_data_regression_with_pipeline_component/pipeline.yml)|[![jobs/pipelines-with-components/pipeline_with_pipeline_component/nyc_taxi_data_regression_with_pipeline_component/pipeline](https://github.com/Azure/azureml-examples/workflows/cli-jobs-pipelines-with-components-pipeline_with_pipeline_component-nyc_taxi_data_regression_with_pipeline_component-pipeline/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/cli-jobs-pipelines-with-components-pipeline_with_pipeline_component-nyc_taxi_data_regression_with_pipeline_component-pipeline.yml)|Train regression model based on nyc taxi dataset
diff --git a/cli/assets/environment/conda-yamls/pydata.yml b/cli/assets/environment/conda-yamls/pydata.yml
index ca5f79904f..599f9a3380 100644
--- a/cli/assets/environment/conda-yamls/pydata.yml
+++ b/cli/assets/environment/conda-yamls/pydata.yml
@@ -5,7 +5,7 @@ dependencies:
- python=3.8
- pip=21.2.4
- pip:
- - numpy==1.21.2
+ - numpy==1.22
- scipy==1.7.1
- pandas==1.3.0
- scikit-learn==0.24.2
@@ -17,7 +17,7 @@ dependencies:
- azureml-mlflow==1.34.0
- matplotlib==3.4.3
- tqdm==4.62.2
- - joblib==1.0.1
+ - joblib==1.2.0
- jupyter==1.0.0
- ipykernel==6.4.1
- papermill==2.3.3
diff --git a/cli/assets/environment/docker-contexts/python-and-pip/requirements.txt b/cli/assets/environment/docker-contexts/python-and-pip/requirements.txt
index e447f28916..1cde537ab1 100644
--- a/cli/assets/environment/docker-contexts/python-and-pip/requirements.txt
+++ b/cli/assets/environment/docker-contexts/python-and-pip/requirements.txt
@@ -1,5 +1,5 @@
# pydata
-numpy==1.21.2
+numpy==1.22
scipy==1.7.1
pandas==1.3.0
scikit-learn==0.24.2
@@ -19,7 +19,7 @@ matplotlib==3.4.3
# python tools
tqdm==4.62.2
-joblib==1.0.1
+joblib==1.2.0
jupyter==1.0.0
ipykernel==6.4.1
papermill==2.3.3
diff --git a/cli/endpoints/online/deploy-with-packages/mlflow-model/deploy.sh b/cli/endpoints/online/deploy-with-packages/mlflow-model/deploy.sh
index a93006f7f7..b6b7ed53ac 100644
--- a/cli/endpoints/online/deploy-with-packages/mlflow-model/deploy.sh
+++ b/cli/endpoints/online/deploy-with-packages/mlflow-model/deploy.sh
@@ -29,7 +29,7 @@ az ml online-endpoint invoke -n $ENDPOINT_NAME -d with-package -f sample-request
#
#
-az ml online-deployment create -f model-deployment.yml -e $ENDPOINT_NAME
+az ml online-deployment create --with-package -f model-deployment.yml -e $ENDPOINT_NAME
#
#
@@ -38,4 +38,4 @@ az ml online-endpoint delete -n $ENDPOINT_NAME --yes
#
az ml model package -n $MODEL_NAME -l latest --file package-external.yml
-#
\ No newline at end of file
+#
diff --git a/cli/endpoints/online/managed/minimal/single-model-registered/env.yml b/cli/endpoints/online/managed/minimal/single-model-registered/env.yml
index 565192fef9..54e6470e1b 100644
--- a/cli/endpoints/online/managed/minimal/single-model-registered/env.yml
+++ b/cli/endpoints/online/managed/minimal/single-model-registered/env.yml
@@ -9,5 +9,5 @@ dependencies:
- scipy=1.10.1
- pip:
- azureml-defaults==1.49.0
- - joblib==1.0.1
+ - joblib==1.2.0
- azureml-inference-server-http
\ No newline at end of file
diff --git a/cli/endpoints/online/model-1/environment/conda-managedidentity.yaml b/cli/endpoints/online/model-1/environment/conda-managedidentity.yaml
index c6372ca394..4e72c53a00 100644
--- a/cli/endpoints/online/model-1/environment/conda-managedidentity.yaml
+++ b/cli/endpoints/online/model-1/environment/conda-managedidentity.yaml
@@ -9,6 +9,6 @@ dependencies:
- scipy=1.7.1
- pip:
- azureml-defaults==1.38.0
- - joblib==1.0.1
+ - joblib==1.2.0
- azure-storage-blob==12.11
- azure-identity==1.7
diff --git a/cli/jobs/deepspeed/deepspeed-training/src/train.py b/cli/jobs/deepspeed/deepspeed-training/src/train.py
index 77d5a4c6d8..0965793214 100644
--- a/cli/jobs/deepspeed/deepspeed-training/src/train.py
+++ b/cli/jobs/deepspeed/deepspeed-training/src/train.py
@@ -1,249 +1,264 @@
-# Copyright (c) Microsoft Corporation.
-# Licensed under the MIT License.
-"""Train a model using deepspeed."""
-import argparse
-import os
-import deepspeed
-import torch
-import torchvision
-import torchvision.transforms as transforms
-import time
-from model import Net, nn
-
-# import MLflow if available. Continue with a warning if not installed on the system.
-try:
- import mlflow
-except ImportError:
- print("MLFlow logging failed. Continuing without MLflow.")
- pass
-
-
-def add_argument():
- """Add arguements for deepspeed."""
- parser = argparse.ArgumentParser(description="CIFAR")
-
- # train
- parser.add_argument(
- "-b", "--batch_size", default=32, type=int, help="mini-batch size (default: 32)"
- )
- parser.add_argument(
- "-e",
- "--epochs",
- default=3,
- type=int,
- help="number of total epochs (default: 3)",
- )
-
- # distributed training
- parser.add_argument(
- "--local_rank",
- type=int,
- default=-1,
- help="local rank passed from distributed launcher",
- )
- parser.add_argument("--global_rank", default=-1, type=int, help="global rank")
- parser.add_argument(
- "--with_aml_log", default=True, help="Use Azure ML metric logging"
- )
-
- # Include DeepSpeed configuration arguments
- parser = deepspeed.add_config_arguments(parser)
-
- args = parser.parse_args()
-
- return args
-
-
-# Need args here to set ranks for multi-node training with download=True
-args = add_argument()
-if args.with_aml_log:
- tracking_uri = mlflow.get_tracking_uri()
- print("Current tracking uri: {}".format(tracking_uri))
-########################################################################
-# The output of torchvision datasets are PILImage images of range [0, 1].
-# We transform them to Tensors of normalized range [-1, 1].
-# .. note::
-# If running on Windows and you get a BrokenPipeError, try setting
-# the num_worker of torch.utils.data.DataLoader() to 0.
-
-transform = transforms.Compose(
- [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
-)
-
-trainset = torchvision.datasets.CIFAR10(
- root="./data", train=True, download=False, transform=transform
-)
-trainloader = torch.utils.data.DataLoader(
- trainset, batch_size=4, shuffle=True, num_workers=2
-)
-
-testset = torchvision.datasets.CIFAR10(
- root="./data", train=False, download=False, transform=transform
-)
-testloader = torch.utils.data.DataLoader(
- testset, batch_size=4, shuffle=False, num_workers=2
-)
-
-classes = (
- "plane",
- "car",
- "bird",
- "cat",
- "deer",
- "dog",
- "frog",
- "horse",
- "ship",
- "truck",
-)
-
-########################################################################
-# 2. Define a Convolutional Neural Network
-# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-# Copy the neural network from the Neural Networks section before and modify it to
-# take 3-channel images (instead of 1-channel images as it was defined).
-
-net = Net()
-parameters = filter(lambda p: p.requires_grad, net.parameters())
-
-
-# Initialize DeepSpeed to use the following features
-# 1) Distributed model
-# 2) Distributed data loader
-# 3) DeepSpeed optimizer
-model_engine, optimizer, trainloader, __ = deepspeed.initialize(
- args=args, model=net, model_parameters=parameters, training_data=trainset
-)
-
-########################################################################
-# 3. Define a Loss function and optimizer
-# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-# Let's use a Classification Cross-Entropy loss and SGD with momentum.
-
-criterion = nn.CrossEntropyLoss()
-
-# DeepSpeed optimizer is already set so no need to use the following
-# optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
-
-########################################################################
-# 4. Train the network
-# ^^^^^^^^^^^^^^^^^^^^
-#
-# Showcasing logging metrics to automl.
-if args.with_aml_log:
- mlflow.log_metrics({"hello": 12345})
-# This is when things start to get interesting.
-# We simply have to loop over our data iterator, and feed the inputs to the
-# network and optimize.
-for epoch in range(args.epochs): # loop over the dataset multiple times
-
- running_loss = 0.0
- for i, data in enumerate(trainloader):
- # get the inputs; data is a list of [inputs, labels]
- pre = time.time()
- inputs, labels = (
- data[0].to(model_engine.local_rank),
- data[1].to(model_engine.local_rank),
- )
-
- outputs = model_engine(inputs.half())
- loss = criterion(outputs, labels)
-
- model_engine.backward(loss)
- model_engine.step()
- post = time.time()
- Time_per_step = post - pre
-
- # print statistics
- running_loss += loss.item()
- # if i % 2000 == 1999: # print every 2000 mini-batches
- loss = running_loss / 2000
- print("[%d, %5d] loss: %.3f" % (epoch + 1, i + 1, loss))
- if args.with_aml_log:
- try:
- mlflow.log_metrics({"loss": loss})
- except NameError:
- print("MLFlow logging failed. Continuing without MLflow.")
- pass
- running_loss = 0.0
-
-print("Finished Training")
-
-########################################################################
-# 5. Test the network on the test data
-# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-#
-# We have trained the network for 2 passes over the training dataset.
-# But we need to check if the network has learnt anything at all.
-#
-# We will check this by predicting the class label that the neural network
-# outputs, and checking it against the ground-truth. If the prediction is
-# correct, we add the sample to the list of correct predictions.
-#
-# Okay, first step. Let us display an image from the test set to get familiar.
-
-dataiter = iter(testloader)
-images, labels = dataiter.next()
-
-# print images
-print("GroundTruth: ", " ".join("%5s" % classes[labels[j]] for j in range(4)))
-
-########################################################################
-# Okay, now let us see what the neural network thinks these examples above are:
-
-outputs = net(images.to(model_engine.local_rank).half())
-
-########################################################################
-# The outputs are energies for the 10 classes.
-# The higher the energy for a class, the more the network
-# thinks that the image is of the particular class.
-# So, let's get the index of the highest energy:
-_, predicted = torch.max(outputs, 1)
-
-print("Predicted: ", " ".join("%5s" % classes[predicted[j]] for j in range(4)))
-
-########################################################################
-# The results seem pretty good.
-#
-# Let us look at how the network performs on the whole dataset.
-
-correct = 0
-total = 0
-with torch.no_grad():
- for data in testloader:
- images, labels = data
- outputs = net(images.to(model_engine.local_rank).half())
- _, predicted = torch.max(outputs.data, 1)
- total += labels.size(0)
- correct += (predicted == labels.to(model_engine.local_rank)).sum().item()
-
-print(
- "Accuracy of the network on the 10000 test images: %d %%" % (100 * correct / total)
-)
-
-########################################################################
-# That looks way better than chance, which is 10% accuracy (randomly picking
-# a class out of 10 classes).
-# Seems like the network learnt something.
-#
-# Hmmm, what are the classes that performed well, and the classes that did
-# not perform well:
-
-class_correct = list(0.0 for i in range(10))
-class_total = list(0.0 for i in range(10))
-with torch.no_grad():
- for data in testloader:
- images, labels = data
- outputs = net(images.to(model_engine.local_rank).half())
- _, predicted = torch.max(outputs, 1)
- c = (predicted == labels.to(model_engine.local_rank)).squeeze()
- for i in range(4):
- label = labels[i]
- class_correct[label] += c[i].item()
- class_total[label] += 1
-
-for i in range(10):
- print(
- "Accuracy of %5s : %2d %%"
- % (classes[i], 100 * class_correct[i] / class_total[i])
- )
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+"""Train a model using deepspeed."""
+import argparse
+import os
+import deepspeed
+import torch
+import torchvision
+import torchvision.transforms as transforms
+import time
+from model import Net, nn
+from mlflow.exceptions import MlflowException
+
+# import MLflow if available. Continue with a warning if not installed on the system.
+try:
+ import mlflow
+except ImportError:
+ print("MLFlow logging failed. Continuing without MLflow.")
+ pass
+
+
+def add_argument():
+ """Add arguements for deepspeed."""
+ parser = argparse.ArgumentParser(description="CIFAR")
+
+ # train
+ parser.add_argument(
+ "-b", "--batch_size", default=32, type=int, help="mini-batch size (default: 32)"
+ )
+ parser.add_argument(
+ "-e",
+ "--epochs",
+ default=3,
+ type=int,
+ help="number of total epochs (default: 3)",
+ )
+
+ # distributed training
+ parser.add_argument(
+ "--local_rank",
+ type=int,
+ default=-1,
+ help="local rank passed from distributed launcher",
+ )
+ parser.add_argument("--global_rank", default=-1, type=int, help="global rank")
+ parser.add_argument(
+ "--with_aml_log", default=True, help="Use Azure ML metric logging"
+ )
+
+ # Include DeepSpeed configuration arguments
+ parser = deepspeed.add_config_arguments(parser)
+
+ args = parser.parse_args()
+
+ return args
+
+
+# Need args here to set ranks for multi-node training with download=True
+args = add_argument()
+if args.with_aml_log:
+ tracking_uri = mlflow.get_tracking_uri()
+ print("Current tracking uri: {}".format(tracking_uri))
+########################################################################
+# The output of torchvision datasets are PILImage images of range [0, 1].
+# We transform them to Tensors of normalized range [-1, 1].
+# .. note::
+# If running on Windows and you get a BrokenPipeError, try setting
+# the num_worker of torch.utils.data.DataLoader() to 0.
+
+transform = transforms.Compose(
+ [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
+)
+
+trainset = torchvision.datasets.CIFAR10(
+ root="./data", train=True, download=False, transform=transform
+)
+trainloader = torch.utils.data.DataLoader(
+ trainset, batch_size=4, shuffle=True, num_workers=2
+)
+
+testset = torchvision.datasets.CIFAR10(
+ root="./data", train=False, download=False, transform=transform
+)
+testloader = torch.utils.data.DataLoader(
+ testset, batch_size=4, shuffle=False, num_workers=2
+)
+
+classes = (
+ "plane",
+ "car",
+ "bird",
+ "cat",
+ "deer",
+ "dog",
+ "frog",
+ "horse",
+ "ship",
+ "truck",
+)
+
+########################################################################
+# 2. Define a Convolutional Neural Network
+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+# Copy the neural network from the Neural Networks section before and modify it to
+# take 3-channel images (instead of 1-channel images as it was defined).
+
+net = Net()
+parameters = filter(lambda p: p.requires_grad, net.parameters())
+
+
+# Initialize DeepSpeed to use the following features
+# 1) Distributed model
+# 2) Distributed data loader
+# 3) DeepSpeed optimizer
+model_engine, optimizer, trainloader, __ = deepspeed.initialize(
+ args=args, model=net, model_parameters=parameters, training_data=trainset
+)
+
+########################################################################
+# 3. Define a Loss function and optimizer
+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+# Let's use a Classification Cross-Entropy loss and SGD with momentum.
+
+criterion = nn.CrossEntropyLoss()
+
+# DeepSpeed optimizer is already set so no need to use the following
+# optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
+
+########################################################################
+# 4. Train the network
+# ^^^^^^^^^^^^^^^^^^^^
+#
+# Showcasing logging metrics to automl.
+if args.with_aml_log:
+ try:
+ mlflow.log_metrics({"hello": 12345})
+ except MlflowException as e:
+ if "too many 429" in e.args[0]:
+ # Handle multiple requests here as we may run to many
+ # instances of this script depending on settings.
+ print("Unable to write metric because of throttling.")
+ else:
+ raise
+# This is when things start to get interesting.
+# We simply have to loop over our data iterator, and feed the inputs to the
+# network and optimize.
+for epoch in range(args.epochs): # loop over the dataset multiple times
+ running_loss = 0.0
+ for i, data in enumerate(trainloader):
+ # get the inputs; data is a list of [inputs, labels]
+ pre = time.time()
+ inputs, labels = (
+ data[0].to(model_engine.local_rank),
+ data[1].to(model_engine.local_rank),
+ )
+
+ outputs = model_engine(inputs.half())
+ loss = criterion(outputs, labels)
+
+ model_engine.backward(loss)
+ model_engine.step()
+ post = time.time()
+ Time_per_step = post - pre
+
+ # print statistics
+ running_loss += loss.item()
+ # if i % 2000 == 1999: # print every 2000 mini-batches
+ loss = running_loss / 2000
+ print("[%d, %5d] loss: %.3f" % (epoch + 1, i + 1, loss))
+ if args.with_aml_log and i % 100 == 0:
+ try:
+ mlflow.log_metrics({"loss": loss})
+ except NameError:
+ print("MLFlow logging failed. Continuing without MLflow.")
+ except MlflowException as e:
+ if "too many 429" in e.args[0]:
+ # Handle multiple requests here as we may run to many
+ # instances of this script depending on settings.
+ print("Unable to write metric because of throttling.")
+ else:
+ raise
+
+ running_loss = 0.0
+
+print("Finished Training")
+
+########################################################################
+# 5. Test the network on the test data
+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#
+# We have trained the network for 2 passes over the training dataset.
+# But we need to check if the network has learnt anything at all.
+#
+# We will check this by predicting the class label that the neural network
+# outputs, and checking it against the ground-truth. If the prediction is
+# correct, we add the sample to the list of correct predictions.
+#
+# Okay, first step. Let us display an image from the test set to get familiar.
+
+dataiter = iter(testloader)
+images, labels = dataiter.next()
+
+# print images
+print("GroundTruth: ", " ".join("%5s" % classes[labels[j]] for j in range(4)))
+
+########################################################################
+# Okay, now let us see what the neural network thinks these examples above are:
+
+outputs = net(images.to(model_engine.local_rank).half())
+
+########################################################################
+# The outputs are energies for the 10 classes.
+# The higher the energy for a class, the more the network
+# thinks that the image is of the particular class.
+# So, let's get the index of the highest energy:
+_, predicted = torch.max(outputs, 1)
+
+print("Predicted: ", " ".join("%5s" % classes[predicted[j]] for j in range(4)))
+
+########################################################################
+# The results seem pretty good.
+#
+# Let us look at how the network performs on the whole dataset.
+
+correct = 0
+total = 0
+with torch.no_grad():
+ for data in testloader:
+ images, labels = data
+ outputs = net(images.to(model_engine.local_rank).half())
+ _, predicted = torch.max(outputs.data, 1)
+ total += labels.size(0)
+ correct += (predicted == labels.to(model_engine.local_rank)).sum().item()
+
+print(
+ "Accuracy of the network on the 10000 test images: %d %%" % (100 * correct / total)
+)
+
+########################################################################
+# That looks way better than chance, which is 10% accuracy (randomly picking
+# a class out of 10 classes).
+# Seems like the network learnt something.
+#
+# Hmmm, what are the classes that performed well, and the classes that did
+# not perform well:
+
+class_correct = list(0.0 for i in range(10))
+class_total = list(0.0 for i in range(10))
+with torch.no_grad():
+ for data in testloader:
+ images, labels = data
+ outputs = net(images.to(model_engine.local_rank).half())
+ _, predicted = torch.max(outputs, 1)
+ c = (predicted == labels.to(model_engine.local_rank)).squeeze()
+ for i in range(4):
+ label = labels[i]
+ class_correct[label] += c[i].item()
+ class_total[label] += 1
+
+for i in range(10):
+ print(
+ "Accuracy of %5s : %2d %%"
+ % (classes[i], 100 * class_correct[i] / class_total[i])
+ )
diff --git a/cli/jobs/parallel/3a_mnist_batch_identification/script/dump_mltable.py b/cli/jobs/parallel/3a_mnist_batch_identification/script/dump_mltable.py
deleted file mode 100644
index 3abc9ffd40..0000000000
--- a/cli/jobs/parallel/3a_mnist_batch_identification/script/dump_mltable.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import yaml
-import argparse
-import os
-
-d = {"paths": [{"file": "./mnist"}]}
-
-parser = argparse.ArgumentParser(allow_abbrev=False, description="dump mltable")
-
-parser.add_argument("--output_folder", type=str, default=0)
-args, _ = parser.parse_known_args()
-dump_path = os.path.join(args.output_folder, "MLTable")
-with open(dump_path, "w") as yaml_file:
- yaml.dump(d, yaml_file, default_flow_style=False)
-print("Saved MLTable file")
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/README.md b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/README.md
new file mode 100644
index 0000000000..3e49af29e6
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/README.md
@@ -0,0 +1,15 @@
+This is a dummy pipeline job with anonymous reference of a flow as a component. Flow directory is copied from [sample in promptflow repository](https://github.com/microsoft/promptflow/tree/main/examples/flows/standard/basic) and remove connection dependency to avoid using promptflow connection in azure ml example repository.
+
+Prerequirements:
+1. `.promptflow/flow.tools.json` in the flow directory is required to use a flow as a component. Usually you can use `pf flow validate` or `pf run validate` to generate it.
+2. You should either update connection name in `flow.dag.yaml` or update `connection.yaml` with your own api information and use `pf connection create --file connection.yaml` to create a workspace connection.
+3. You need to either edit the compute cluster in `pipeline.yml` or create a compute cluster named `cpu-cluster` in your workspace.
+4. Please ensure that there are `$schema` in your `flow.dag.yaml` and `run.yaml`
+ 1. `flow.dag.yaml`: `$schema: https://azuremlschemas.azureedge.net/promptflow/latest/Flow.schema.json`
+ 2. `run.yaml`: `$schema: https://azuremlschemas.azureedge.net/promptflow/latest/Run.schema.json`
+
+After that, you can run `az ml job create --file pipeline.yml` to submit the pipeline job.
+
+References:
+- [microsoft/promptflow: Build high-quality LLM apps](https://github.com/microsoft/promptflow)
+- [Reference - Prompt flow docuentation](https://microsoft.github.io/promptflow/reference/index.html)
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/.promptflow/flow.tools.json b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/.promptflow/flow.tools.json
new file mode 100644
index 0000000000..b067b11dc8
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/.promptflow/flow.tools.json
@@ -0,0 +1,17 @@
+{
+ "package": {},
+ "code": {
+ "hello.jinja2": {
+ "type": "prompt",
+ "inputs": {
+ "text": {
+ "type": [
+ "string"
+ ]
+ }
+ },
+ "description": "Please replace the template with your own prompt.",
+ "source": "hello.jinja2"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/README.md b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/README.md
new file mode 100644
index 0000000000..8642d513ad
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/README.md
@@ -0,0 +1,131 @@
+# Basic standard flow
+A basic standard flow using custom python tool that calls Azure OpenAI with connection info stored in environment variables.
+
+Tools used in this flow:
+- `prompt` tool
+- custom `python` Tool
+
+Connections used in this flow:
+- None
+
+## Prerequisites
+
+Install promptflow sdk and other dependencies:
+```bash
+pip install -r requirements.txt
+```
+
+## Run flow
+
+- Prepare your Azure Open AI resource follow this [instruction](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal) and get your `api_key` if you don't have one.
+
+- Setup environment variables
+
+Ensure you have put your azure open ai endpoint key in [.env](.env) file. You can create one refer to this [example file](.env.example).
+
+```bash
+cat .env
+```
+
+- Test flow/node
+```bash
+# test with default input value in flow.dag.yaml
+pf flow test --flow .
+
+# test with flow inputs
+pf flow test --flow . --inputs text="Java Hello World!"
+
+# test node with inputs
+pf flow test --flow . --node llm --inputs prompt="Write a simple Hello World program that displays the greeting message when executed."
+```
+
+- Create run with multiple lines data
+```bash
+# using environment from .env file (loaded in user code: hello.py)
+pf run create --flow . --data ./data.jsonl --stream
+```
+
+- List and show run meta
+```bash
+# list created run
+pf run list
+
+# get a sample run name
+name=$(pf run list -r 10 | jq '.[] | select(.name | contains("basic_variant_0")) | .name'| head -n 1 | tr -d '"')
+
+# show specific run detail
+pf run show --name $name
+
+# show output
+pf run show-details --name $name
+
+# visualize run in browser
+pf run visualize --name $name
+```
+
+## Run flow with connection
+Storing connection info in .env with plaintext is not safe. We recommend to use `pf connection` to guard secrets like `api_key` from leak.
+
+- Show or create `open_ai_connection`
+```bash
+# create connection from `azure_openai.yml` file
+# Override keys with --set to avoid yaml file changes
+pf connection create --file ../../../connections/azure_openai.yml --set api_key= api_base=
+
+# check if connection exists
+pf connection show -n open_ai_connection
+```
+
+- Test using connection secret specified in environment variables
+**Note**: we used `'` to wrap value since it supports raw value without escape in powershell & bash. For windows command prompt, you may remove the `'` to avoid it become part of the value.
+
+```bash
+# test with default input value in flow.dag.yaml
+pf flow test --flow . --environment-variables AZURE_OPENAI_API_KEY='${open_ai_connection.api_key}' AZURE_OPENAI_API_BASE='${open_ai_connection.api_base}'
+```
+
+- Create run using connection secret binding specified in environment variables, see [run.yml](run.yml)
+```bash
+# create run
+pf run create --flow . --data ./data.jsonl --stream --environment-variables AZURE_OPENAI_API_KEY='${open_ai_connection.api_key}' AZURE_OPENAI_API_BASE='${open_ai_connection.api_base}'
+# create run using yaml file
+pf run create --file run.yml --stream
+
+# show outputs
+name=$(pf run list -r 10 | jq '.[] | select(.name | contains("basic_variant_0")) | .name'| head -n 1 | tr -d '"')
+pf run show-details --name $name
+```
+
+## Run flow in cloud with connection
+- Assume we already have a connection named `open_ai_connection` in workspace.
+```bash
+# set default workspace
+az account set -s
+az configure --defaults group= workspace=
+```
+
+- Create run
+```bash
+# run with environment variable reference connection in azureml workspace
+pfazure run create --flow . --data ./data.jsonl --environment-variables AZURE_OPENAI_API_KEY='${open_ai_connection.api_key}' AZURE_OPENAI_API_BASE='${open_ai_connection.api_base}' --stream --runtime demo-mir
+# run using yaml file
+pfazure run create --file run.yml --stream --runtime demo-mir
+```
+
+- List and show run meta
+```bash
+# list created run
+pfazure run list -r 3
+
+# get a sample run name
+name=$(pfazure run list -r 100 | jq '.[] | select(.name | contains("basic_variant_0")) | .name'| head -n 1 | tr -d '"')
+
+# show specific run detail
+pfazure run show --name $name
+
+# show output
+pfazure run show-details --name $name
+
+# visualize run in browser
+pfazure run visualize --name $name
+```
\ No newline at end of file
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/flow.dag.yaml b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/flow.dag.yaml
new file mode 100644
index 0000000000..71352c41d8
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/flow.dag.yaml
@@ -0,0 +1,28 @@
+$schema: https://azuremlschemas.azureedge.net/promptflow/latest/Flow.schema.json
+inputs:
+ text:
+ type: string
+ default: Hello World!
+outputs:
+ output:
+ type: string
+ reference: ${llm.output}
+nodes:
+- name: hello_prompt
+ type: prompt
+ source:
+ type: code
+ path: hello.jinja2
+ inputs:
+ text: ${inputs.text}
+- name: llm
+ type: python
+ source:
+ type: code
+ path: hello.py
+ inputs:
+ prompt: ${hello_prompt.output}
+ deployment_name: text-davinci-003
+ max_tokens: "120"
+environment:
+ python_requirements_txt: requirements.txt
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/hello.jinja2 b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/hello.jinja2
new file mode 100644
index 0000000000..df86ef4f5e
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/hello.jinja2
@@ -0,0 +1,2 @@
+{# Please replace the template with your own prompt. #}
+Write a simple {{text}} program that displays the greeting message when executed.
\ No newline at end of file
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/hello.py b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/hello.py
new file mode 100644
index 0000000000..f7b7164d1b
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/hello.py
@@ -0,0 +1,51 @@
+import os
+import openai
+
+from dotenv import load_dotenv
+from promptflow import tool
+
+# The inputs section will change based on the arguments of the tool function, after you save the code
+# Adding type to arguments and return value will help the system show the types properly
+# Please update the function name/signature per need
+
+
+def to_bool(value) -> bool:
+ return str(value).lower() == "true"
+
+
+@tool
+def my_python_tool(
+ prompt: str,
+ # for AOAI, deployment name is customized by user, not model name.
+ deployment_name: str,
+ suffix: str = None,
+ max_tokens: int = 120,
+ temperature: float = 1.0,
+ top_p: float = 1.0,
+ n: int = 1,
+ logprobs: int = None,
+ echo: bool = False,
+ stop: list = None,
+ presence_penalty: float = 0,
+ frequency_penalty: float = 0,
+ best_of: int = 1,
+ logit_bias: dict = {},
+ user: str = "",
+ **kwargs,
+) -> str:
+ if "AZURE_OPENAI_API_KEY" not in os.environ:
+ # load environment variables from .env file
+ load_dotenv()
+
+ if "AZURE_OPENAI_API_KEY" not in os.environ:
+ raise Exception("Please specify environment variables: AZURE_OPENAI_API_KEY")
+
+ conn = dict(
+ api_key=os.environ["AZURE_OPENAI_API_KEY"],
+ api_base=os.environ["AZURE_OPENAI_API_BASE"],
+ api_type=os.environ.get("AZURE_OPENAI_API_TYPE", "azure"),
+ api_version=os.environ.get("AZURE_OPENAI_API_VERSION", "2023-07-01-preview"),
+ )
+
+ # return directly to avoid using promptflow connection in azure ml example repository
+ return f"fake answer based on {prompt}"
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/requirements.txt b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/requirements.txt
new file mode 100644
index 0000000000..35c1b55b32
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/requirements.txt
@@ -0,0 +1,3 @@
+promptflow[azure]
+promptflow-tools
+python-dotenv
\ No newline at end of file
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/data/data.jsonl b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/data/data.jsonl
new file mode 100644
index 0000000000..d71f1ca42a
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/data/data.jsonl
@@ -0,0 +1,3 @@
+{"text": "Python Hello World!"}
+{"text": "C Hello World!"}
+{"text": "C# Hello World!"}
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/pipeline.yml b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/pipeline.yml
new file mode 100644
index 0000000000..bd2308384b
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/pipeline.yml
@@ -0,0 +1,30 @@
+$schema: https://azuremlschemas.azureedge.net/latest/pipelineJob.schema.json
+type: pipeline
+display_name: pipeline_job_with_flow
+description: The hello world pipeline job with flow as component
+
+compute: "azureml:cpu-cluster"
+
+inputs:
+ flow_input:
+ type: uri_file
+ path: "./data/data.jsonl"
+
+jobs:
+ flow_node_from_dag:
+ type: parallel
+ component: ./basic/flow.dag.yaml
+ inputs:
+ data: ${{parent.inputs.flow_input}}
+ text: "${data.text}"
+ environment_variables:
+ AZURE_OPENAI_API_KEY:
+ AZURE_OPENAI_API_BASE:
+ AZURE_OPENAI_API_TYPE: azure
+
+ flow_node_from_run:
+ type: parallel
+ component: ./run.yml
+ inputs:
+ data: ${{parent.inputs.flow_input}}
+ text: "${data.text}"
diff --git a/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/run.yml b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/run.yml
new file mode 100644
index 0000000000..41fa9cb451
--- /dev/null
+++ b/cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/run.yml
@@ -0,0 +1,6 @@
+$schema: https://azuremlschemas.azureedge.net/promptflow/latest/Run.schema.json
+flow: ./basic
+environment_variables:
+ AZURE_OPENAI_API_KEY:
+ AZURE_OPENAI_API_BASE:
+ AZURE_OPENAI_API_TYPE: azure
\ No newline at end of file
diff --git a/cli/jobs/pipelines/mnist-batch-identification-using-parallel/script/dump_mltable.py b/cli/jobs/pipelines/mnist-batch-identification-using-parallel/script/dump_mltable.py
index 3abc9ffd40..145d1d23d1 100644
--- a/cli/jobs/pipelines/mnist-batch-identification-using-parallel/script/dump_mltable.py
+++ b/cli/jobs/pipelines/mnist-batch-identification-using-parallel/script/dump_mltable.py
@@ -2,7 +2,7 @@
import argparse
import os
-d = {"paths": [{"file": "./mnist"}]}
+d = {"paths": [{"folder": "./mnist"}]}
parser = argparse.ArgumentParser(allow_abbrev=False, description="dump mltable")
diff --git a/cli/jobs/single-step/scikit-learn/iris-notebook/docker-context/requirements.txt b/cli/jobs/single-step/scikit-learn/iris-notebook/docker-context/requirements.txt
index de50cb9013..b3eaac6f85 100644
--- a/cli/jobs/single-step/scikit-learn/iris-notebook/docker-context/requirements.txt
+++ b/cli/jobs/single-step/scikit-learn/iris-notebook/docker-context/requirements.txt
@@ -1,5 +1,5 @@
# pydata
-numpy==1.21.2
+numpy==1.22
scipy==1.7.1
pandas==1.3.0
scikit-learn==0.24.2
diff --git a/cli/jobs/single-step/scikit-learn/iris/docker-context/requirements.txt b/cli/jobs/single-step/scikit-learn/iris/docker-context/requirements.txt
index 3b936a731e..4e10419149 100644
--- a/cli/jobs/single-step/scikit-learn/iris/docker-context/requirements.txt
+++ b/cli/jobs/single-step/scikit-learn/iris/docker-context/requirements.txt
@@ -1,5 +1,5 @@
# pydata
-numpy==1.21.2
+numpy==1.22
scipy==1.7.1
pandas==1.3.0
scikit-learn==0.24.2
diff --git a/infra/templates/notebook_template.ipynb b/infra/templates/notebook_template.ipynb
new file mode 100644
index 0000000000..3ef7c22e0e
--- /dev/null
+++ b/infra/templates/notebook_template.ipynb
@@ -0,0 +1,194 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# {{TODO: Title}}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Objective\n",
+ "\n",
+ "{{TODO: Add a one or two sentences describing the learning outcomes for this notebook}}\n",
+ "\n",
+ "This tutorial uses the following Azure Machine Learning services:\n",
+ "\n",
+ "- {{TODO: Add short descriptions and links to documentation for Azure Machine Learning services used}}\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Time\n",
+ "\n",
+ "You should expect to spend {{TODO: Add expected runtime for sample}} running this sample. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## About this example\n",
+ "\n",
+ "{{TODO: Include a paragraph or two explaining what this example demonstrates, who should be interested in it, and what you need to know before you get started.}}\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Dataset\n",
+ "\n",
+ "{{Optional TODO: Include a paragraph with information about any datasets used and where to obtain it. Make sure the dataset is accessible to the public. If you ***must*** ensure that that dataset can legally be hosted in this repository. **Microsoft Employees**: Add your dataset to the azuremlexamples storage account.}}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Before you begin\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Installation\n",
+ "\n",
+ "Install the following packages required to execute this notebook. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Install the packages\n",
+ "! pip3 install --quiet ../../requirements.txt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Parameters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# {{TODO: Declare any variables that a user would need to update. See: https://papermill.readthedocs.io/en/latest/usage-parameterize.html }}\n",
+ "\n",
+ "# subscription_id: str= \"\"\n",
+ "# resource_group_name: str= \"\"\n",
+ "# workspace_name=\"\"\n",
+ "\n",
+ "should_cleanup: bool = False"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Run this Example"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Setup your MLClient"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import MLClient\n",
+ "from azure.identity import DefaultAzureCredential\n",
+ "\n",
+ "# authenticate\n",
+ "credential = DefaultAzureCredential()\n",
+ "\n",
+ "# Get a handle to the workspace\n",
+ "ml_client = MLClient(\n",
+ " credential=credential,\n",
+ " subscription_id=subscription_id,\n",
+ " resource_group_name=resource_group_name,\n",
+ " workspace_name=workspace_name,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "{{TODO: Add cells here with notebook content}}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Cleaning up\n",
+ "\n",
+ "To clean up all Azure ML resources used in this example, you can delete the individual resources you created in this tutorial.\n",
+ "\n",
+ "If you made a resource group specifically to run this example, you could instead [delete the resource group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/delete-resource-group)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "if should_cleanup:\n",
+ " # Delete endpoint resource\n",
+ " # e.g. `ml_client.endpoint.begin_delete(endpoint).wait()`\n",
+ "\n",
+ " # Delete model resource\n",
+ " # e.g. `ml_client.endpoint.begin_delete(model).wait()`"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "collapsed_sections": [],
+ "name": "notebook_template.ipynb",
+ "toc_visible": true
+ },
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/sdk/CONTRIBUTING.md b/sdk/CONTRIBUTING.md
new file mode 100644
index 0000000000..8b89bcba8d
--- /dev/null
+++ b/sdk/CONTRIBUTING.md
@@ -0,0 +1,7 @@
+[Azure/azureml-examples overall contributing guide.](../CONTRIBUTING.md)
+
+## Adding a new SDK sample
+
+All SDK samples should be provided as Jupyter notebooks. Please follow the [pre-defined notebook template](../infra/templates/notebook_template.ipynb) and the instructions on [the main CONTRIBUTING page](../CONTRIBUTING.md).
+
+**Important:** PRs from forks of this repository are likely to fail automated workflows due to access to secrets. PRs from forks will be considered but may experience additional delay for testing.
diff --git a/sdk/python/README.md b/sdk/python/README.md
index 76a6e76656..effbc04f4e 100644
--- a/sdk/python/README.md
+++ b/sdk/python/README.md
@@ -187,6 +187,7 @@ Test Status is for branch - **_main_**
|jobs|pipelines|[pipeline_with_train_eval_pipeline_component](jobs/pipelines/1j_pipeline_with_pipeline_component/pipeline_with_train_eval_pipeline_component/pipeline_with_train_eval_pipeline_component.ipynb)|Create pipeline with CommandComponents from local YAML file|[![pipeline_with_train_eval_pipeline_component](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1j_pipeline_with_pipeline_component-pipeline_with_train_eval_pipeline_component-pipeline_with_train_eval_pipeline_component.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1j_pipeline_with_pipeline_component-pipeline_with_train_eval_pipeline_component-pipeline_with_train_eval_pipeline_component.yml)|
|jobs|pipelines|[automl-forecasting-demand-hierarchical-timeseries-in-pipeline](jobs/pipelines/1k_demand_forecasting_with_pipeline_components/automl-forecasting-demand-hierarchical-timeseries-in-pipeline/automl-forecasting-demand-hierarchical-timeseries-in-pipeline.ipynb)|*no description*|[![automl-forecasting-demand-hierarchical-timeseries-in-pipeline](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1k_demand_forecasting_with_pipeline_components-automl-forecasting-demand-hierarchical-timeseries-in-pipeline-automl-forecasting-demand-hierarchical-timeseries-in-pipeline.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1k_demand_forecasting_with_pipeline_components-automl-forecasting-demand-hierarchical-timeseries-in-pipeline-automl-forecasting-demand-hierarchical-timeseries-in-pipeline.yml)|
|jobs|pipelines|[automl-forecasting-demand-many-models-in-pipeline](jobs/pipelines/1k_demand_forecasting_with_pipeline_components/automl-forecasting-demand-many-models-in-pipeline/automl-forecasting-demand-many-models-in-pipeline.ipynb)|*no description*|[![automl-forecasting-demand-many-models-in-pipeline](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1k_demand_forecasting_with_pipeline_components-automl-forecasting-demand-many-models-in-pipeline-automl-forecasting-demand-many-models-in-pipeline.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1k_demand_forecasting_with_pipeline_components-automl-forecasting-demand-many-models-in-pipeline-automl-forecasting-demand-many-models-in-pipeline.yml)|
+|jobs|pipelines|[flow_in_pipeline](jobs/pipelines/1l_flow_in_pipeline/flow_in_pipeline.ipynb)|Create pipeline using components to run a distributed job with tensorflow|[![flow_in_pipeline](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-1l_flow_in_pipeline-flow_in_pipeline.yml)|
|jobs|pipelines|[train_mnist_with_tensorflow](jobs/pipelines/2a_train_mnist_with_tensorflow/train_mnist_with_tensorflow.ipynb)|Create pipeline using components to run a distributed job with tensorflow|[![train_mnist_with_tensorflow](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-2a_train_mnist_with_tensorflow-train_mnist_with_tensorflow.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-2a_train_mnist_with_tensorflow-train_mnist_with_tensorflow.yml)|
|jobs|pipelines|[train_cifar_10_with_pytorch](jobs/pipelines/2b_train_cifar_10_with_pytorch/train_cifar_10_with_pytorch.ipynb)|Get data, train and evaluate a model in pipeline with Components|[![train_cifar_10_with_pytorch](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-2b_train_cifar_10_with_pytorch-train_cifar_10_with_pytorch.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-2b_train_cifar_10_with_pytorch-train_cifar_10_with_pytorch.yml)|
|jobs|pipelines|[nyc_taxi_data_regression](jobs/pipelines/2c_nyc_taxi_data_regression/nyc_taxi_data_regression.ipynb)|Build pipeline with components for 5 jobs - prep data, transform data, train model, predict results and evaluate model performance|[![nyc_taxi_data_regression](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-2c_nyc_taxi_data_regression-nyc_taxi_data_regression.yml/badge.svg?branch=main)](https://github.com/Azure/azureml-examples/actions/workflows/sdk-jobs-pipelines-2c_nyc_taxi_data_regression-nyc_taxi_data_regression.yml)|
diff --git a/sdk/python/assets/environment/conda-yamls/pydata.yml b/sdk/python/assets/environment/conda-yamls/pydata.yml
index ca5f79904f..599f9a3380 100644
--- a/sdk/python/assets/environment/conda-yamls/pydata.yml
+++ b/sdk/python/assets/environment/conda-yamls/pydata.yml
@@ -5,7 +5,7 @@ dependencies:
- python=3.8
- pip=21.2.4
- pip:
- - numpy==1.21.2
+ - numpy==1.22
- scipy==1.7.1
- pandas==1.3.0
- scikit-learn==0.24.2
@@ -17,7 +17,7 @@ dependencies:
- azureml-mlflow==1.34.0
- matplotlib==3.4.3
- tqdm==4.62.2
- - joblib==1.0.1
+ - joblib==1.2.0
- jupyter==1.0.0
- ipykernel==6.4.1
- papermill==2.3.3
diff --git a/sdk/python/assets/environment/docker-contexts/python-and-pip/requirements.txt b/sdk/python/assets/environment/docker-contexts/python-and-pip/requirements.txt
index e447f28916..1cde537ab1 100644
--- a/sdk/python/assets/environment/docker-contexts/python-and-pip/requirements.txt
+++ b/sdk/python/assets/environment/docker-contexts/python-and-pip/requirements.txt
@@ -1,5 +1,5 @@
# pydata
-numpy==1.21.2
+numpy==1.22
scipy==1.7.1
pandas==1.3.0
scikit-learn==0.24.2
@@ -19,7 +19,7 @@ matplotlib==3.4.3
# python tools
tqdm==4.62.2
-joblib==1.0.1
+joblib==1.2.0
jupyter==1.0.0
ipykernel==6.4.1
papermill==2.3.3
diff --git a/sdk/python/endpoints/online/model-1/environment/conda-managedidentity.yml b/sdk/python/endpoints/online/model-1/environment/conda-managedidentity.yml
index c6372ca394..4e72c53a00 100644
--- a/sdk/python/endpoints/online/model-1/environment/conda-managedidentity.yml
+++ b/sdk/python/endpoints/online/model-1/environment/conda-managedidentity.yml
@@ -9,6 +9,6 @@ dependencies:
- scipy=1.7.1
- pip:
- azureml-defaults==1.38.0
- - joblib==1.0.1
+ - joblib==1.2.0
- azure-storage-blob==12.11
- azure-identity==1.7
diff --git a/sdk/python/endpoints/online/model-1/environment/conda.yaml b/sdk/python/endpoints/online/model-1/environment/conda.yaml
index 836a4b9ba6..0b17178571 100644
--- a/sdk/python/endpoints/online/model-1/environment/conda.yaml
+++ b/sdk/python/endpoints/online/model-1/environment/conda.yaml
@@ -10,4 +10,4 @@ dependencies:
- pip:
- azureml-defaults==1.47.0
- inference-schema[numpy-support]==1.5
- - joblib==1.0.1
\ No newline at end of file
+ - joblib==1.2.0
\ No newline at end of file
diff --git a/sdk/python/endpoints/online/model-2/environment/conda.yaml b/sdk/python/endpoints/online/model-2/environment/conda.yaml
index 836a4b9ba6..0b17178571 100644
--- a/sdk/python/endpoints/online/model-2/environment/conda.yaml
+++ b/sdk/python/endpoints/online/model-2/environment/conda.yaml
@@ -10,4 +10,4 @@ dependencies:
- pip:
- azureml-defaults==1.47.0
- inference-schema[numpy-support]==1.5
- - joblib==1.0.1
\ No newline at end of file
+ - joblib==1.2.0
\ No newline at end of file
diff --git a/sdk/python/endpoints/online/sample/model-1/environment/conda.yaml b/sdk/python/endpoints/online/sample/model-1/environment/conda.yaml
index 30ba15728a..6cd061ef6b 100644
--- a/sdk/python/endpoints/online/sample/model-1/environment/conda.yaml
+++ b/sdk/python/endpoints/online/sample/model-1/environment/conda.yaml
@@ -10,4 +10,4 @@ dependencies:
- pip:
- azureml-defaults==1.33.0
- inference-schema[numpy-support]==1.3.0
- - joblib==1.0.1
\ No newline at end of file
+ - joblib==1.2.0
\ No newline at end of file
diff --git a/sdk/python/endpoints/online/sample/model-2/environment/conda.yaml b/sdk/python/endpoints/online/sample/model-2/environment/conda.yaml
index c23bd7085a..d7f58b315b 100644
--- a/sdk/python/endpoints/online/sample/model-2/environment/conda.yaml
+++ b/sdk/python/endpoints/online/sample/model-2/environment/conda.yaml
@@ -10,4 +10,4 @@ dependencies:
- pip:
- azureml-defaults==1.33.0
- inference-schema[numpy-support]==1.3.0
- - joblib==1.0.1
+ - joblib==1.2.0
diff --git a/sdk/python/featurestore_sample/featurestore_cli_job.py b/sdk/python/featurestore_sample/automation-test/featurestore_cli_job.py
similarity index 66%
rename from sdk/python/featurestore_sample/featurestore_cli_job.py
rename to sdk/python/featurestore_sample/automation-test/featurestore_cli_job.py
index 598fe31fa1..474b80e024 100644
--- a/sdk/python/featurestore_sample/featurestore_cli_job.py
+++ b/sdk/python/featurestore_sample/automation-test/featurestore_cli_job.py
@@ -14,20 +14,20 @@
) as f:
exec(f.read())
+# print("=======Test CLI Notebook 2============")
+# with open(
+# "notebooks/sdk_and_cli/2. Enable materialization and backfill feature data.py"
+# ) as f:
+# exec(f.read())
+
print("=======Test CLI Notebook 2============")
with open(
- "notebooks/sdk_and_cli/2. Enable materialization and backfill feature data.py"
+ "notebooks/sdk_and_cli/2. Experiment and train models using features.py"
) as f:
exec(f.read())
print("=======Test CLI Notebook 3============")
with open(
- "notebooks/sdk_and_cli/3. Experiment and train models using features.py"
-) as f:
- exec(f.read())
-
-print("=======Test CLI Notebook 4============")
-with open(
- "notebooks/sdk_and_cli/4. Enable recurrent materialization and run batch inference.py"
+ "notebooks/sdk_and_cli/3. Enable recurrent materialization and run batch inference.py"
) as f:
exec(f.read())
diff --git a/sdk/python/featurestore_sample/featurestore_sdk_job.py b/sdk/python/featurestore_sample/automation-test/featurestore_sdk_job.py
similarity index 66%
rename from sdk/python/featurestore_sample/featurestore_sdk_job.py
rename to sdk/python/featurestore_sample/automation-test/featurestore_sdk_job.py
index b6bd4eb33a..0a2bb7dadd 100644
--- a/sdk/python/featurestore_sample/featurestore_sdk_job.py
+++ b/sdk/python/featurestore_sample/automation-test/featurestore_sdk_job.py
@@ -28,3 +28,22 @@
# print("=======Test Notebook 4============")
# with open("notebooks/sdk_only/4. Enable online store and run online inference.py") as f:
# exec(f.read())
+
+
+print("======clean up==========")
+from azure.ai.ml import MLClient
+from azure.ai.ml.identity import AzureMLOnBehalfOfCredential
+
+ml_client = MLClient(
+ AzureMLOnBehalfOfCredential(),
+ subscription_id="",
+ resource_group_name="",
+)
+
+result = ml_client.feature_stores.begin_delete(
+ name="",
+ permanently_delete=True,
+ delete_dependent_resources=False,
+).result()
+
+print(result)
diff --git a/sdk/python/featurestore_sample/setup-resources-cli.sh b/sdk/python/featurestore_sample/automation-test/setup-resources-cli.sh
similarity index 83%
rename from sdk/python/featurestore_sample/setup-resources-cli.sh
rename to sdk/python/featurestore_sample/automation-test/setup-resources-cli.sh
index 411fd92d38..597586cbe4 100644
--- a/sdk/python/featurestore_sample/setup-resources-cli.sh
+++ b/sdk/python/featurestore_sample/automation-test/setup-resources-cli.sh
@@ -46,13 +46,11 @@ az ml compute create --name $COMPUTE_CLUSTER_NAME --type $COMPUTE_TYPE --size $C
#
NOTEBOOK_1="notebooks/sdk_and_cli/1. Develop a feature set and register with managed feature store"
-NOTEBOOK_2="notebooks/sdk_and_cli/2. Enable materialization and backfill feature data"
-NOTEBOOK_3="notebooks/sdk_and_cli/3. Experiment and train models using features"
-NOTEBOOK_4="notebooks/sdk_and_cli/4. Enable recurrent materialization and run batch inference"
+NOTEBOOK_2="notebooks/sdk_and_cli/2. Experiment and train models using features"
+NOTEBOOK_3="notebooks/sdk_and_cli/3. Enable recurrent materialization and run batch inference"
jupytext --to py "${NOTEBOOK_1}.ipynb"
jupytext --to py "${NOTEBOOK_2}.ipynb"
jupytext --to py "${NOTEBOOK_3}.ipynb"
-jupytext --to py "${NOTEBOOK_4}.ipynb"
#
#
@@ -65,22 +63,22 @@ sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_
s//$FEATURE_VERSION/g;
s//$FEATURESTORE_NAME/g;" "${NOTEBOOK_1}.py"
-sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
- s//$FEATURESTORE_NAME/g;
- s//$FEATURE_STORAGE_ACCOUNT_NAME/g;
- s//$USER_ID/g
- s//$STORAGE_ACCOUNT_NAME/g
- s//$STORAGE_FILE_SYSTEM_NAME/g
- s//$FEATURE_VERSION/g;;
- s//$UAI_NAME/g;" "${NOTEBOOK_2}.py"
+# sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
+# s//$FEATURESTORE_NAME/g;
+# s//$FEATURE_STORAGE_ACCOUNT_NAME/g;
+# s//$USER_ID/g
+# s//$STORAGE_ACCOUNT_NAME/g
+# s//$STORAGE_FILE_SYSTEM_NAME/g
+# s//$FEATURE_VERSION/g;;
+# s//$UAI_NAME/g;" "${NOTEBOOK_2}.py"
sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
s//$FEATURESTORE_NAME/g;
s//$COMPUTE_CLUSTER_NAME/g;
s//$COMPUTE_TYPE/g;
s//$COMPUTE_SIZE/g;
- s//$FEATURE_VERSION/g;" "${NOTEBOOK_3}.py"
+ s//$FEATURE_VERSION/g;" "${NOTEBOOK_2}.py"
sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
s//$FEATURESTORE_NAME/g;
- s//$FEATURE_VERSION/g;" "${NOTEBOOK_4}.py"
\ No newline at end of file
+ s//$FEATURE_VERSION/g;" "${NOTEBOOK_3}.py"
\ No newline at end of file
diff --git a/sdk/python/featurestore_sample/setup-resources.sh b/sdk/python/featurestore_sample/automation-test/setup-resources.sh
similarity index 76%
rename from sdk/python/featurestore_sample/setup-resources.sh
rename to sdk/python/featurestore_sample/automation-test/setup-resources.sh
index 85540ba7ce..a63447b8a8 100644
--- a/sdk/python/featurestore_sample/setup-resources.sh
+++ b/sdk/python/featurestore_sample/automation-test/setup-resources.sh
@@ -12,6 +12,8 @@ RAND_NUM=$RANDOM
UAI_NAME=fstoreuai${RAND_NUM}
REDIS_NAME=${RESOURCE_GROUP}rds
VERSION=$(((RANDOM%1000)+1))
+FEATURESTORE_NAME="my-featurestore"${VERSION}
+SDK_PY_JOB_FILE="automation-test/featurestore_sdk_job.py"
#
#
@@ -30,13 +32,17 @@ sed -i "s//$SUBSCRIPTION_ID/g;
s//$RESOURCE_GROUP/g;
s//$AML_WORKSPACE_NAME/g;" $1
+sed -i "s//$SUBSCRIPTION_ID/g;
+ s//$RESOURCE_GROUP/g;
+ s//$FEATURESTORE_NAME/g;" $SDK_PY_JOB_FILE
+
#
sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
- s//$VERSION/g;" "${NOTEBOOK_1}.py"
+ s//$FEATURESTORE_NAME/g;" "${NOTEBOOK_1}.py"
sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
- s//$VERSION/g;" "${NOTEBOOK_2}.py"
+ s//$FEATURESTORE_NAME/g;" "${NOTEBOOK_2}.py"
sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
- s//$VERSION/g;" "${NOTEBOOK_3}.py"
+ s//$FEATURESTORE_NAME/g;" "${NOTEBOOK_3}.py"
sed -i "s/display/$OUTPUT_COMMAND/g;s/.\/Users\/\/featurestore_sample/.\//g;
s//$REDIS_NAME/g;
- s//$VERSION/g;" "${NOTEBOOK_4}.py"
\ No newline at end of file
+ s//$FEATURESTORE_NAME/g;" "${NOTEBOOK_4}.py"
\ No newline at end of file
diff --git a/sdk/python/featurestore_sample/test_featurestore_cli_samples.ipynb b/sdk/python/featurestore_sample/automation-test/test_featurestore_cli_samples.ipynb
similarity index 82%
rename from sdk/python/featurestore_sample/test_featurestore_cli_samples.ipynb
rename to sdk/python/featurestore_sample/automation-test/test_featurestore_cli_samples.ipynb
index 6e75ca58d0..6494653368 100644
--- a/sdk/python/featurestore_sample/test_featurestore_cli_samples.ipynb
+++ b/sdk/python/featurestore_sample/automation-test/test_featurestore_cli_samples.ipynb
@@ -36,6 +36,22 @@
")"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import yaml\n",
+ "\n",
+ "contents = \"\"\n",
+ "with open(\"../project/env/conda.yml\", \"r\") as stream:\n",
+ " try:\n",
+ " contents = yaml.safe_load(stream)\n",
+ " except yaml.YAMLError as exc:\n",
+ " print(exc)"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -44,8 +60,8 @@
"source": [
"spark_job = spark(\n",
" display_name=\"featurestore_sample_test\",\n",
- " code=\"./\",\n",
- " entry={\"file\": \"featurestore_cli_job.py\"},\n",
+ " code=\"../\",\n",
+ " entry={\"file\": \"automation-test/featurestore_cli_job.py\"},\n",
" driver_cores=1,\n",
" driver_memory=\"1g\",\n",
" executor_cores=1,\n",
@@ -55,7 +71,7 @@
" \"instance_type\": \"Standard_E8S_V3\",\n",
" \"runtime_version\": \"3.2.0\",\n",
" },\n",
- " environment=Environment(conda_file=\"project/env/conda.yml\"),\n",
+ " conf={\"spark.synapse.library.python.env\": contents},\n",
")\n",
"\n",
"returned_spark_job = ml_client.jobs.create_or_update(spark_job)\n",
diff --git a/sdk/python/featurestore_sample/test_featurestore_sdk_samples.ipynb b/sdk/python/featurestore_sample/automation-test/test_featurestore_sdk_samples.ipynb
similarity index 82%
rename from sdk/python/featurestore_sample/test_featurestore_sdk_samples.ipynb
rename to sdk/python/featurestore_sample/automation-test/test_featurestore_sdk_samples.ipynb
index 48ebda4498..7a38c17eb8 100644
--- a/sdk/python/featurestore_sample/test_featurestore_sdk_samples.ipynb
+++ b/sdk/python/featurestore_sample/automation-test/test_featurestore_sdk_samples.ipynb
@@ -36,6 +36,22 @@
")"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import yaml\n",
+ "\n",
+ "contents = \"\"\n",
+ "with open(\"../project/env/conda.yml\", \"r\") as stream:\n",
+ " try:\n",
+ " contents = yaml.safe_load(stream)\n",
+ " except yaml.YAMLError as exc:\n",
+ " print(exc)"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -44,8 +60,8 @@
"source": [
"spark_job = spark(\n",
" display_name=\"featurestore_sample_test\",\n",
- " code=\"./\",\n",
- " entry={\"file\": \"featurestore_sdk_job.py\"},\n",
+ " code=\"../\",\n",
+ " entry={\"file\": \"automation-test/featurestore_sdk_job.py\"},\n",
" driver_cores=1,\n",
" driver_memory=\"1g\",\n",
" executor_cores=1,\n",
@@ -55,7 +71,7 @@
" \"instance_type\": \"Standard_E8S_V3\",\n",
" \"runtime_version\": \"3.2.0\",\n",
" },\n",
- " environment=Environment(conda_file=\"project/env/conda.yml\"),\n",
+ " conf={\"spark.synapse.library.python.env\": contents},\n",
")\n",
"\n",
"returned_spark_job = ml_client.jobs.create_or_update(spark_job)\n",
diff --git a/sdk/python/featurestore_sample/featurestore/featuresets/transactions_custom_source/feature_process_code/transaction_transform.py b/sdk/python/featurestore_sample/featurestore/featuresets/transactions_custom_source/feature_process_code/transaction_transform.py
new file mode 100644
index 0000000000..2af583bbb4
--- /dev/null
+++ b/sdk/python/featurestore_sample/featurestore/featuresets/transactions_custom_source/feature_process_code/transaction_transform.py
@@ -0,0 +1,46 @@
+from pyspark.sql import functions as F
+from pyspark.sql.window import Window
+from pyspark.ml import Transformer
+from pyspark.sql.dataframe import DataFrame
+
+
+class TransactionFeatureTransformer(Transformer):
+ def _transform(self, df: DataFrame) -> DataFrame:
+ days = lambda i: i * 86400
+ w_3d = (
+ Window.partitionBy("accountID")
+ .orderBy(F.col("timestamp").cast("long"))
+ .rangeBetween(-days(3), 0)
+ )
+ w_7d = (
+ Window.partitionBy("accountID")
+ .orderBy(F.col("timestamp").cast("long"))
+ .rangeBetween(-days(7), 0)
+ )
+ res = (
+ df.withColumn("transaction_7d_count", F.count("transactionID").over(w_7d))
+ .withColumn(
+ "transaction_amount_7d_sum", F.sum("transactionAmount").over(w_7d)
+ )
+ .withColumn(
+ "transaction_amount_7d_avg", F.avg("transactionAmount").over(w_7d)
+ )
+ .withColumn("transaction_3d_count", F.count("transactionID").over(w_3d))
+ .withColumn(
+ "transaction_amount_3d_sum", F.sum("transactionAmount").over(w_3d)
+ )
+ .withColumn(
+ "transaction_amount_3d_avg", F.avg("transactionAmount").over(w_3d)
+ )
+ .select(
+ "accountID",
+ "timestamp",
+ "transaction_3d_count",
+ "transaction_amount_3d_sum",
+ "transaction_amount_3d_avg",
+ "transaction_7d_count",
+ "transaction_amount_7d_sum",
+ "transaction_amount_7d_avg",
+ )
+ )
+ return res
diff --git a/sdk/python/featurestore_sample/featurestore/featuresets/transactions_custom_source/source_process_code/source_process.py b/sdk/python/featurestore_sample/featurestore/featuresets/transactions_custom_source/source_process_code/source_process.py
new file mode 100644
index 0000000000..5e338487ac
--- /dev/null
+++ b/sdk/python/featurestore_sample/featurestore/featuresets/transactions_custom_source/source_process_code/source_process.py
@@ -0,0 +1,35 @@
+# ---------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# ---------------------------------------------------------
+from datetime import datetime
+
+
+class CustomSourceTransformer:
+ def __init__(self, **kwargs):
+ self.path = kwargs.get("source_path")
+ self.timestamp_column_name = kwargs.get("timestamp_column_name")
+ if not self.path:
+ raise Exception("`source_path` is not provided")
+ if not self.timestamp_column_name:
+ raise Exception("`timestamp_column_name` is not provided")
+
+ def process(
+ self, start_time: datetime, end_time: datetime, **kwargs
+ ) -> "pyspark.sql.DataFrame":
+ from pyspark.sql import SparkSession
+ from pyspark.sql.functions import col, lit, to_timestamp
+
+ spark = SparkSession.builder.getOrCreate()
+ df = spark.read.json(self.path)
+
+ if start_time:
+ df = df.filter(
+ col(self.timestamp_column_name) >= to_timestamp(lit(start_time))
+ )
+
+ if end_time:
+ df = df.filter(
+ col(self.timestamp_column_name) < to_timestamp(lit(end_time))
+ )
+
+ return df
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/1. Develop a feature set and register with managed feature store.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/1. Develop a feature set and register with managed feature store.ipynb
index 80cd942606..05b0f1c86b 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/1. Develop a feature set and register with managed feature store.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/1. Develop a feature set and register with managed feature store.ipynb
@@ -25,23 +25,7 @@
}
},
"source": [
- "#### Important\n",
- "\n",
- "This feature is currently in public preview. This preview version is provided without a service-level agreement, and it's not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/)."
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "Azure ML managed feature store lets you discover, create and operationalize features. Features are the connective tissue in ML lifecycle, starting from prototyping where you experiment with various features to operationalization where models are deployed and feature data is looked up during inference. For information on basics concept of feature store, see [feature store concepts](fs-concepts).\n",
+ "Azure ML managed feature store lets you discover, create and operationalize features. Features are the connective tissue in ML lifecycle, starting from prototyping where you experiment with various features to operationalization where models are deployed and feature data is looked up during inference. For information on basics concept of feature store, see [feature store concepts](https://learn.microsoft.com/azure/machine-learning/concept-what-is-managed-feature-store).\n",
"\n",
"In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle:\n",
"\n",
@@ -66,6 +50,10 @@
},
"source": [
"## Prerequisites\n",
+ "\n",
+ "> [!NOTE]\n",
+ "> This tutorial uses Azure Machine Learning notebook with **Serverless Spark Compute**.\n",
+ "\n",
"Before following the steps in this article, make sure you have the following prerequisites:\n",
"\n",
"* An Azure Machine Learning workspace. If you don't have one, use the steps in the [Quickstart: Create workspace resources](https://learn.microsoft.com/en-us/azure/machine-learning/quickstart-create-resources?view=azureml-api-2) article to create one.\n",
@@ -77,10 +65,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### (new for sdk/cli track) Note\n",
+ "### Note\n",
"This tutorial series has two tracks:\n",
- "1. SDK only track: Uses only python SDKs. Suitable if pure python based development and deployment is preferred.\n",
- "1. SDK & CLI track: Uses CLI for CRUD operations (create/update/delete) and python SDK for feature set development and testing only. This is useful in ci/cd or GitOps scenarios where CLI/yaml is preferred."
+ "1. SDK only track: Uses only Python SDKs. This is suitable if pure Python-based development and deployment is preferred.\n",
+ "1. SDK & CLI track: Uses CLI for CRUD (create, read, update, and delete) operations and Python SDK for feature set development and testing only. This is useful in CI/CD or GitOps scenarios where CLI/YAML is preferred."
]
},
{
@@ -94,49 +82,41 @@
}
},
"source": [
- "## Setup "
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "### (updated: common) Prepare the notebook environment for development\n",
- "Note: This tutorial uses Azure Machine Learning spark notebook for development.\n",
+ "## Set up\n",
+ "\n",
+ "This tutorial uses the Python feature store core SDK (`azureml-featurestore`) for feature set development and testing. The CLI is used for create, read, update, and delete (CRUD) operations, on feature stores, feature sets, and feature store entities.\n",
"\n",
- "1. Clone the examples repository to your local machine: To run the tutorial, first clone the [examples repository - (azureml-examples)](https://github.com/azure/azureml-examples) with this command:\n",
+ "You don't need to explicitly install these resources for this tutorial, because in the set-up instructions shown here, the `conda.yml` file covers them.\n",
+ "\n",
+ "To prepare the notebook environment for development:\n",
+ "\n",
+ "1. Clone the [azureml-examples](https://github.com/azure/azureml-examples) repository to your local GitHub resources with this command:\n",
"\n",
" `git clone --depth 1 https://github.com/Azure/azureml-examples`\n",
"\n",
- " You can also download a zip file from the [examples repository (azureml-examples)](https://github.com/azure/azureml-examples). At this page, first select the `code` dropdown, and then select `Download ZIP`. Then, unzip the contents into a folder on your local device.\n",
+ " You can also download a zip file from the [azureml-examples](https://github.com/azure/azureml-examples) repository. At this page, first select the `code` dropdown, and then select `Download ZIP`. Then, unzip the contents into a folder on your local device.\n",
+ "\n",
+ "1. Upload the feature store samples directory to the project workspace\n",
"\n",
- "1. Upload the feature store samples directory to project workspace.\n",
- " * Open Azure Machine Learning studio UI of your Azure Machine Learning workspace\n",
- " * Select **Notebooks** in left nav\n",
- " * Select your user name in the directory listing\n",
- " * Select **upload folder**\n",
- " * Select the feature store samples folder from the cloned directory path: `azureml-examples/sdk/python/featurestore-sample`\n",
+ " 1. In the Azure Machine Learning workspace, open the Azure Machine Learning studio UI.\n",
+ " 1. Select **Notebooks** in left navigation panel.\n",
+ " 1. Select your user name in the directory listing.\n",
+ " 1. Select ellipses (**...**) and then select **Upload folder**.\n",
+ " 1. Select the feature store samples folder from the cloned directory path: `azureml-examples/sdk/python/featurestore-sample`.\n",
"\n",
- "1. Running the tutorial:\n",
- "* Option 1: Create a new notebook, and execute the instructions in this document step by step. \n",
- "* Option 2: Open the existing notebook named `1. Develop a feature set and register with managed feature store.ipynb`, and run it step by step. The notebooks are available in `featurestore_sample/notebooks` directory. You can select from `sdk_only` or `sdk_and_cli`. You may keep this document open and refer to it for additional explanation and documentation links.\n",
+ "1. Run the tutorial\n",
"\n",
- "1. Select **Serverless Spark compute** in the top nav \"Compute\" dropdown. This operation might take one to two minutes. Wait for a status bar in the top to display **configure session**.\n",
+ " * Option 1: Create a new notebook, and execute the instructions in this document, step by step.\n",
+ " * Option 2: Open existing notebook `featurestore_sample/notebooks/sdk_and_cli/1. Develop a feature set and register with managed feature store.ipynb`. You may keep this document open and refer to it for more explanation and documentation links.\n",
"\n",
- "1. Select \"configure session\" from the top nav (this could take one to two minutes to display):\n",
+ " 1. Select **Serverless Spark Compute** in the top navigation **Compute** dropdown. This operation might take one to two minutes. Wait for a status bar in the top to display **Configure session**.\n",
+ " 1. Select **Configure session** in the top status bar.\n",
+ " 1. Select **Python packages**.\n",
+ " 1. Select **Upload conda file**.\n",
+ " 1. Select file `azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml` located on your local device.\n",
+ " 1. (Optional) Increase the session time-out (idle time in minutes) to reduce the serverless spark cluster startup time.\n",
"\n",
- " 1. Select **configure session** in the top status bar\n",
- " 1. Select **Python packages**\n",
- " 1. Select **Upload conda file**\n",
- " 1. Select file `azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml` located on your local device\n",
- " 1. (Optional) Increase the session time-out (idle time) to reduce the serverless spark cluster startup time."
+ "__Important:__ Except for this step, you need to run all the other steps every time you have a new spark session/session time out.\n"
]
},
{
@@ -150,7 +130,8 @@
}
},
"source": [
- "#### Start spark session"
+ "#### Start spark session\n",
+ "Execute the following code cell to start the Spark session. It wil take approximately 10 minutes to install all dependencies and start the Spark session."
]
},
{
@@ -174,7 +155,7 @@
},
"outputs": [],
"source": [
- "# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
+ "# Run this cell to start the spark session (any code block will start the session). This can take around 10 mins.\n",
"print(\"start spark session\")"
]
},
@@ -189,7 +170,8 @@
}
},
"source": [
- "#### Setup root directory for the samples"
+ "#### Setup root directory for the samples\n",
+ "This code cell sets up the root directory for the samples."
]
},
{
@@ -235,9 +217,9 @@
}
},
"source": [
- "#### (new for sdk/cki track) Setup CLI\n",
+ "#### Setup CLI\n",
"\n",
- "1. Install azure ml cli extention\n",
+ "1. Install AzureML CLI extention\n",
"1. Authenticate\n",
"1. Set the default subscription"
]
@@ -265,7 +247,7 @@
},
"outputs": [],
"source": [
- "# install azure ml cli extension\n",
+ "# Install AzureML CLI extension\n",
"!az extension add --name ml"
]
},
@@ -292,7 +274,7 @@
},
"outputs": [],
"source": [
- "# authenticate\n",
+ "# Authenticate\n",
"!az login"
]
},
@@ -336,20 +318,19 @@
},
"source": [
"## Note\n",
- "Feature store Vs Project workspace: You will use a featurestore to reuse features across projects. You will use a project workspace(the current workspace) to train and inference models, by leveraging features from feature stores. Many project workspaces can share and reuse a same feature store.\n",
+ "Feature store Vs Project workspace: You will use a feature store to reuse features across projects. You will use a project workspace (the current workspace) to train and inference models, by leveraging features from feature stores. Many project workspaces can share and reuse a same feature store.\n",
"\n",
- "## (updated for sdk/cki track) Note\n",
- "In this tutorial you will be using cli and feature store core SDK:\n",
- "Uses CLI for CRUD operations (create/update/delete) and python SDK for feature set development and testing only. This is useful in ci/cd or GitOps scenarios where CLI/yaml is preferred.\n",
+ "## Note\n",
+ "In this tutorial you will be using CLI and feature store core SDK:\n",
"\n",
- "1. CLI: You will use CLI for CRUD operations (create/update/delete) on feature-store, feature-set and feature-store-entity.\n",
- "2. Feature store core sdk: This sdk (`azureml-featurestore`) is meant to be used for feature set development and consumption (you will learn more about these operations later):\n",
- "- List/Get registered feature set\n",
- "- Generate/resolve feature retrieval spec\n",
- "- Execute featureset definition to generate Spark dataframe\n",
- "- Generate training using a point-in-time join\n",
+ "1. CLI: You will use CLI for CRUD (create, read, update, and delete) operations on feature store, feature set and feature store entities.\n",
+ "2. Feature store core SDK: This SDK (`azureml-featurestore`) is meant to be used for feature set development and consumption (you will learn more about these operations later):\n",
+ " - List/Get registered feature set\n",
+ " - Generate/resolve feature retrieval specification\n",
+ " - Execute featureset definition to generate Spark dataframe\n",
+ " - Generate training data using a point-in-time join\n",
"\n",
- "For this tutorial so you do not need to install any of these explicitly, since the instructions already cover them (conda yaml in the above step include these)"
+ "For this tutorial so you do not need to install any of these explicitly, since the instructions already cover them (`conda.yml` in the above step include these)"
]
},
{
@@ -378,7 +359,7 @@
},
"source": [
"#### Step 1a: Set feature store parameters\n",
- "Set name, location and other values for the feature store"
+ "Set name, location and other values for the feature store."
]
},
{
@@ -408,7 +389,15 @@
"featurestore_name = \"\"\n",
"featurestore_location = \"eastus\"\n",
"featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]"
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "\n",
+ "feature_store_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.MachineLearningServices/workspaces/{ws_name}\".format(\n",
+ " sub_id=featurestore_subscription_id,\n",
+ " rg=featurestore_resource_group_name,\n",
+ " ws_name=featurestore_name,\n",
+ ")\n",
+ "\n",
+ "print(feature_store_arm_id)"
]
},
{
@@ -463,7 +452,7 @@
},
"source": [
"#### Step 1c: Initialize AzureML feature store core SDK client\n",
- "As explained above, this is used to develop and consume features"
+ "As explained above, this is used to develop and consume features."
]
},
{
@@ -498,6 +487,35 @@
")"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 1d. Grant \"AzureML Data Scientist\" role on the feature store to your user identity"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- Get your AAD object id from Azure portal following this instruction: https://learn.microsoft.com/en-us/partner-center/find-ids-and-domain-names#find-the-user-object-id\\\n",
+ "\n",
+ "Assign **AzureML Data Scientist** role to your user identity so that it can create resources in feature store workspace. Please note that it may take some time for the permissions to propagate."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "assign-aad-ds-role-cli"
+ },
+ "outputs": [],
+ "source": [
+ "your_aad_objectid = \"\"\n",
+ "\n",
+ "!az role assignment create --role \"AzureML Data Scientist\" --assignee-object-id $your_aad_objectid --assignee-principal-type User --scope $feature_store_arm_id"
+ ]
+ },
{
"attachments": {},
"cell_type": "markdown",
@@ -509,7 +527,7 @@
}
},
"source": [
- "## Step 2: Prototype and develop a transaction rolling aggregation featureset in this notebook"
+ "## Step 2: Prototype and develop a transaction rolling aggregation feature set in this notebook"
]
},
{
@@ -526,7 +544,7 @@
"#### Step 2a: Explore the transactions source data\n",
"\n",
"#### Note\n",
- "The sample data used in this notebook is hosted in a public accessible blob container. It can only be read in Spark via `wasbs` driver. When you create feature sets using your own source data, please host them in adls gen2 account and use `abfss` driver in the data path. "
+ "The sample data used in this notebook is hosted in a public accessible blob container. It can only be read in Spark via `wasbs` driver. When you create feature sets using your own source data, please host them in ADLS Gen2 account and use `abfss` driver in the data path. "
]
},
{
@@ -549,7 +567,7 @@
},
"outputs": [],
"source": [
- "# remove the \".\" in the roor directory path as we need to generate absolute path to read from spark\n",
+ "# Remove the \".\" in the roor directory path as we need to generate absolute path to read from Spark.\n",
"transactions_source_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/transactions-source/*.parquet\"\n",
"transactions_src_df = spark.read.parquet(transactions_source_data_path)\n",
"\n",
@@ -568,9 +586,9 @@
}
},
"source": [
- "#### Step 2b: Develop a transactions featureset locally\n",
+ "#### Step 2b: Develop a transactions feature set locally\n",
"\n",
- "Featureset specification is a self-contained definition of feature set that can be developed and tested locally.\n",
+ "Feature set specification is a self-contained definition of feature set that can be developed and tested locally.\n",
"\n",
"Lets create the following rolling window aggregate features:\n",
"- transactions 3-day count\n",
@@ -581,9 +599,9 @@
"- transactions amount 7-day avg\n",
"\n",
"__Action__:\n",
- "- Inspect the feature transformation code file: `featurestore/featuresets/transactions/spec/transformation_code/transaction_transform.py`. You will see how is the rolling aggregation defined for the features. This is a spark transformer.\n",
+ "- Inspect the feature transformation code file: `featurestore/featuresets/transactions/spec/transformation_code/transaction_transform.py`. You will see how is the rolling aggregation defined for the features. This is a Spark transformer.\n",
"\n",
- "To understand the feature set and transformations in more detail, see [feature store concepts](fs-concepts-url-todo) and [transformation concepts](fs-transformation-concepts-todo)."
+ "To understand the feature set and transformations in more detail, see [feature store concepts](https://learn.microsoft.com/azure/machine-learning/concept-what-is-managed-feature-store)."
]
},
{
@@ -637,10 +655,22 @@
" source_lookback=DateTimeOffset(days=7, hours=0, minutes=0),\n",
" temporal_join_lookback=DateTimeOffset(days=1, hours=0, minutes=0),\n",
" infer_schema=True,\n",
- ")\n",
- "# Generate a spark dataframe from the feature set specification\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Generate a spark dataframe from the feature set specification.\n",
"transactions_fset_df = transactions_featureset_spec.to_spark_dataframe()\n",
- "# display few records\n",
+ "# Display few records.\n",
"display(transactions_fset_df.head(5))"
]
},
@@ -655,19 +685,19 @@
}
},
"source": [
- "#### Step 2c: Export as feature set spec\n",
- "Inorder to register the feature set spec with the feature store, it needs to be saved in a specific format. \n",
- "Action: Please inspect the generated `transactions` FeaturesetSpec: Open this file from the file tree to see the spec: `featurestore/featuresets/accounts/spec/FeaturesetSpec.yaml`\n",
+ "#### Step 2c: Export as feature set specification\n",
+ "In order to register the feature set specification with the feature store, it needs to be saved in a specific format. \n",
+ "Action: Please inspect the generated `transactions` FeaturesetSpec: Open this file from the file tree to see the specification: `featurestore/featuresets/accounts/spec/FeaturesetSpec.yaml`\n",
"\n",
- "Spec contains these important elements:\n",
+ "Specification contains these important elements:\n",
"\n",
- "1. `source`: reference to a storage. In this case a parquet file in a blob storage.\n",
- "1. `features`: list of features and their datatypes. If you provide transformation code (see Day 2 section), the code has to return a dataframe that maps to the features and datatypes.\n",
- "1. `index_columns`: the join keys required to access values from the feature set\n",
+ "1. `source`: Reference to a storage. In this case a parquet file in a blob storage.\n",
+ "1. `features`: List of features and their datatypes. If you provide transformation code (see Day 2 section), the code has to return a dataframe that maps to the features and datatypes.\n",
+ "1. `index_columns`: The join keys required to access values from the feature set\n",
"\n",
- "Learn more about it in the [top level feature store entities document](fs-concepts-todo) and the [feature set spec yaml reference](reference-yaml-featureset-spec.md).\n",
+ "Learn more about it in the [top level feature store entities document](https://learn.microsoft.com/azure/machine-learning/concept-top-level-entities-in-managed-feature-store) and the [feature set specification YAML reference](https://learn.microsoft.com/azure/machine-learning/reference-yaml-featureset-spec).\n",
"\n",
- "The additional benefit of persisting it is that it can be source controlled."
+ "The additional benefit of persisting the feature set specification is that it can be source controlled."
]
},
{
@@ -692,12 +722,12 @@
"source": [
"import os\n",
"\n",
- "# create a new folder to dump the feature set spec\n",
+ "# Create a new folder to dump the feature set specification.\n",
"transactions_featureset_spec_folder = (\n",
" root_dir + \"/featurestore/featuresets/transactions/spec\"\n",
")\n",
"\n",
- "# check if the folder exists, create one if not\n",
+ "# Check if the folder exists, create one if it does not exist.\n",
"if not os.path.exists(transactions_featureset_spec_folder):\n",
" os.makedirs(transactions_featureset_spec_folder)\n",
"\n",
@@ -715,8 +745,8 @@
}
},
"source": [
- "## Step 3: Register a feature-store entity\n",
- "Entity helps enforce best practice that same join key definitions are used across featuresets which uses the same logical entities. Examples of entities are account entity, customer entity etc. Entities are typically created once and reused across feature-sets. For information on basics concept of feature store, see [feature store concepts](fs-concepts)."
+ "## Step 3: Register a feature store entity\n",
+ "Entity helps enforce best practice that same join key definitions are used across feature sets which uses the same logical entities. Examples of entities are account entity, customer entity etc. Entities are typically created once and reused across feature-sets. For information on basics concept of feature store, see [feature store concepts](https://learn.microsoft.com/azure/machine-learning/concept-what-is-managed-feature-store)."
]
},
{
@@ -757,7 +787,7 @@
}
},
"source": [
- "## Step 4: Register the transaction featureset with the featurestore\n",
+ "## Step 4: Register the transaction feature set with the feature store\n",
"You register a feature set asset with the feature store so that you can share and reuse with others. You also get managed capabilities like versioning and materialization (we will learn in this tutorial series).\n",
"\n",
"The feature set asset has reference to the feature set spec that you created earlier and additional properties like version and materialization settings."
@@ -803,14 +833,52 @@
}
},
"source": [
- "#### Explore the FeatureStore UI\n",
- "* Goto the [Azure ML global landing page](https://ml.azure.com/home?flight=FeatureStores). \n",
- "* Click on `Feature stores` in the left nav\n",
- "* You will see the list of feature stores that you have access to. Click on the feature store that you created above.\n",
+ "#### Explore the feature store UI\n",
+ "* Goto the [Azure ML global landing page](https://ml.azure.com/home).\n",
+ "* Click on **Feature stores** in the left navigation.\n",
+ "* You will see the list of feature stores that you have access to. Click on the feature store that you have created above.\n",
"\n",
- "You can see the feature set and entity that you created.\n",
+ "You can see the feature sets and entities that you have created.\n",
"\n",
- "Note: Creating and updating feature store assets are possible only through SDK and CLI. You can use the UI to search/browse the feature store."
+ "Note: Creating and updating feature store assets (feature sets and entities) is possible only through SDK and CLI. You can use the UI to search/browse the feature store."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Grant \"Storage Blob Data Reader\" role on the offline store to your user identity\n",
+ "If feature data is materialized, then you need this role to read feature data from offline materialization store.\n",
+ "- Get information about the offline materialization store from the Feature Store **Overview** page in the Feature Store UI. The storage account subscription ID, storage account resource group name, and storage account name for offline materialization store can be found on **Offline materialization store** card. \n",
+ "![OFFLINE_STORE_INFO](./images/offline-store-information.png) \n",
+ "\n",
+ "To learn more about access control, see access control document in the docs.\n",
+ "\n",
+ "Execute the following code cell for role assignment. Please note that it may take some time for permissions to propagate. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "grant-rbac-to-user-identity-cli"
+ },
+ "outputs": [],
+ "source": [
+ "storage_subscription_id = \"\"\n",
+ "storage_resource_group_name = \"\"\n",
+ "storage_account_name = \"\"\n",
+ "\n",
+ "# Set the ADLS Gen2 storage account ARM ID\n",
+ "gen2_storage_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account}\".format(\n",
+ " sub_id=storage_subscription_id,\n",
+ " rg=storage_resource_group_name,\n",
+ " account=storage_account_name,\n",
+ ")\n",
+ "\n",
+ "print(gen2_storage_arm_id)\n",
+ "\n",
+ "!az role assignment create --role \"Storage Blob Data Reader\" --assignee-object-id $your_aad_objectid --assignee-principal-type User --scope $gen2_storage_arm_id"
]
},
{
@@ -840,9 +908,9 @@
"source": [
"#### Step 5a: Load observation data\n",
"\n",
- "We start by exploring the observation data. Observation data is typically the core data used in training and inference data. This is then joined with feature data to create the full training data. Observation data is the data captured during the time of the event: in this case it has core transaction data including transaction id, account id, transaction amount. In this case, since it is for training, it also has the target variable appended (is_fraud).\n",
+ "We start by exploring the observation data. Observation data is typically the core data used in training and inference data. This is then joined with feature data to create the full training data. Observation data is the data captured during the time of the event: in this case it has core transaction data including transaction ID, account ID, transaction amount. In this case, since it is for training, it also has the target variable appended (`is_fraud`).\n",
"\n",
- "To learn more core concepts including observation data, refer to the docs"
+ "To learn more core concepts including observation data, refer to the feature store documentation."
]
},
{
@@ -884,7 +952,7 @@
}
},
"source": [
- "#### Step 5c: Get the registered featureset and list its features"
+ "#### Step 5b: Get the registered feature set and list its features"
]
},
{
@@ -907,9 +975,9 @@
},
"outputs": [],
"source": [
- "# look up the featureset by providing name and version\n",
+ "# Look up the featureset by providing a name and a version.\n",
"transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
- "# list its features\n",
+ "# List its features.\n",
"transactions_featureset.features"
]
},
@@ -929,11 +997,14 @@
"transient": {
"deleting": false
}
- }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
},
"outputs": [],
"source": [
- "# print sample values\n",
+ "# Print sample values.\n",
"display(transactions_featureset.to_spark_dataframe().head(5))"
]
},
@@ -948,8 +1019,8 @@
}
},
"source": [
- "#### Step 5d: Select features and generate training data\n",
- "In this step we will select features that we would like to be part of training data and use the feature store sdk to generate the training data."
+ "#### Step 5c: Select features and generate training data\n",
+ "In this step we will select features that we would like to be part of training data and use the feature store SDK to generate the training data."
]
},
{
@@ -974,22 +1045,33 @@
"source": [
"from azureml.featurestore import get_offline_features\n",
"\n",
- "# you can select features in pythonic way\n",
+ "# You can select features in pythonic way.\n",
"features = [\n",
" transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n",
" transactions_featureset.get_feature(\"transaction_amount_7d_avg\"),\n",
"]\n",
"\n",
- "# you can also specify features in string form: featurestore:featureset:version:feature\n",
+ "# You can also specify features in string form: featureset:version:feature.\n",
"more_features = [\n",
" \"transactions:1:transaction_3d_count\",\n",
" \"transactions:1:transaction_amount_3d_avg\",\n",
"]\n",
"\n",
"more_features = featurestore.resolve_feature_uri(more_features)\n",
- "features.extend(more_features)\n",
- "\n",
- "# generate training dataframe by using feature data and observation data\n",
+ "features.extend(more_features)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Generate training dataframe by using feature data and observation data.\n",
"training_df = get_offline_features(\n",
" features=features,\n",
" observation_data=observation_data_df,\n",
@@ -1012,9 +1094,7 @@
}
},
"source": [
- "You can see how the features are appended to the training data using a point-in-time join.\n",
- "\n",
- "We have reached the end of the tutorial. Now you have your training data using features from feature store. You can either save it to storage for later use, or run model training on it directly."
+ "You can see how the features are appended to the training data using a point-in-time join."
]
},
{
@@ -1027,48 +1107,8 @@
}
},
"source": [
- "#### Grant your user account \"Blob data reader\" role on the offline store\n",
- "If feature data is materialized, then you need this role to read feature data from offline materialization store.\n",
- "\n",
- "Get your AAD object id from Azure portal following this instruction: https://learn.microsoft.com/en-us/partner-center/find-ids-and-domain-names#find-the-user-object-id\n",
- "\n",
- "To learn more about access control, see access control document in the docs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# This utility function is created for ease of use in the docs tutorials. It uses standard azure API's. You can optionally inspect it `featurestore/setup/setup_storage_uai.py`\n",
- "your_aad_objectid = \"\"\n",
- "\n",
- "!az role assignment create --role \"Storage Blob Data Reader\" --assignee-object-id $your_aad_objectid --assignee-principal-type User --scope $gen2_container_arm_id"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 6: Enable offline materialization on transactions featureset\n",
- "Once materialization is enabled on a featureset, you can perform backfill (this tutorial) or schedule recurrent materialization jobs (next part of the tutorial)"
+ "## Step 6: Enable offline materialization on transactions feature set\n",
+ "Once materialization is enabled on a feature set, you can perform backfill (this tutorial) or schedule recurrent materialization jobs (shown in a later tutorial)."
]
},
{
@@ -1082,6 +1122,7 @@
"outputs_hidden": false,
"source_hidden": false
},
+ "name": "enable-offline-mat-txns-fset-cli",
"nteract": {
"transient": {
"deleting": false
@@ -1108,8 +1149,10 @@
}
},
"source": [
- "## Step 7: Backfill data for transactions featureset\n",
- "As explained in the beginning of this tutorial, materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. Materializing the features will increase its reliability and availability. All feature queries will now use the values from the materialization store. In this step you perform a one-time backfill for a feature window of __three months__.\n",
+ "## Step 7: Backfill data for transactions feature set\n",
+ "Materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. Materializing the features will increase its reliability and availability. All feature queries will use the materialized values from the materialization store. In this step you perform a one-time backfill for a feature window of __18 months__.\n",
+ "\n",
+ "The following code cell will materialize data by current status *None* or *Incomplete* for the defined feature window. \n",
"\n",
"#### Note\n",
"How to determine the window of backfill data needed? It has to match with the window of your training data. For e.g. if you want to train with two years of data, then you will want to be able to retrieve features for the same window, so you will backfill for a two year window."
@@ -1126,6 +1169,7 @@
"outputs_hidden": false,
"source_hidden": false
},
+ "name": "backfill-txns-fset-cli",
"nteract": {
"transient": {
"deleting": false
@@ -1134,10 +1178,10 @@
},
"outputs": [],
"source": [
- "feature_window_start_time = \"2023-01-01T00:00.000Z\"\n",
- "feature_window_end_time = \"2023-04-01T00:00.000Z\"\n",
+ "feature_window_start_time = \"2022-01-01T00:00.000Z\"\n",
+ "feature_window_end_time = \"2023-06-30T00:00.000Z\"\n",
"\n",
- "!az ml feature-set backfill --name transactions --version 1 --feature-store-name $featurestore_name --resource-group $featurestore_resource_group_name --feature-window-start-time $feature_window_start_time --feature-window-end-time $feature_window_end_time"
+ "!az ml feature-set backfill --name transactions --version 1 --by-data-status \"['None', 'Incomplete']\" --feature-window-start-time $feature_window_start_time --feature-window-end-time $feature_window_end_time --feature-store-name $featurestore_name --resource-group $featurestore_resource_group_name"
]
},
{
@@ -1150,7 +1194,7 @@
}
},
"source": [
- "Lets print sample data from the featureset. You can notice from the output information that the data was retrieved from the materialiazation store. `get_offline_features()` method that is used to retrieve training/inference data will also use the materialization store by default."
+ "Let's print sample data from the feature set. You can notice from the output information that the data was retrieved from the materilization store. `get_offline_features()` method that is used to retrieve training/inference data will also use the materialization store by default."
]
},
{
@@ -1164,15 +1208,19 @@
"outputs_hidden": false,
"source_hidden": false
},
+ "name": "sample-txns-fset-data-cli",
"nteract": {
"transient": {
"deleting": false
}
- }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
},
"outputs": [],
"source": [
- "# look up the featureset by providing name and version\n",
+ "# Look up the feature set by providing a name and a version and display few records.\n",
"transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
"display(transactions_featureset.to_spark_dataframe().head(5))"
]
@@ -1190,7 +1238,7 @@
"source": [
"## Cleanup\n",
"\n",
- "Tutorial of `Enable recurrent materialization and run batch inference` has instructions deleting the resources"
+ "Tutorial `3. Enable recurrent materialization and run batch inference` has instructions deleting the resources."
]
},
{
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/2. Experiment and train models using features.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/2. Experiment and train models using features.ipynb
index a8421326b1..cd2d4b8951 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/2. Experiment and train models using features.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/2. Experiment and train models using features.ipynb
@@ -33,21 +33,6 @@
"- Run training pipeline that uses the Feature retrieval spec to train a new model. This pipeline will use the built in feature-retrieval component to generate the training data"
]
},
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Important\n",
- "\n",
- "This feature is currently in public preview. This preview version is provided without a service-level agreement, and it's not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/)."
- ]
- },
{
"cell_type": "markdown",
"metadata": {
@@ -59,7 +44,7 @@
},
"source": [
"# Prerequisites\n",
- "1. Please ensure you have executed `Develop a feature set and register with managed feature store` tutorial"
+ "1. Please ensure you have executed `1. Develop a feature set and register with managed feature store` tutorial"
]
},
{
@@ -546,7 +531,19 @@
" ),\n",
" index_columns=[Column(name=\"accountID\", type=ColumnType.string)],\n",
" infer_schema=True,\n",
- ")\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
"# Generate a spark dataframe from the feature set specification\n",
"accounts_fset_df = accounts_featureset_spec.to_spark_dataframe()\n",
"# display few records\n",
@@ -708,8 +705,19 @@
"# Load the observation data. To understand observatio ndata, refer to part 1 of this tutorial\n",
"observation_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/observation_data/train/*.parquet\"\n",
"observation_data_df = spark.read.parquet(observation_data_path)\n",
- "obs_data_timestamp_column = \"timestamp\"\n",
- "\n",
+ "obs_data_timestamp_column = \"timestamp\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
"# generate training dataframe by using feature data and observation data\n",
"training_df = get_offline_features(\n",
" features=features,\n",
@@ -796,7 +804,19 @@
"outputs": [],
"source": [
"# look up the featureset by providing name and version\n",
- "accounts_featureset = featurestore.feature_sets.get(\"accounts\", \"1\")\n",
+ "accounts_featureset = featurestore.feature_sets.get(\"accounts\", \"1\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
"# get access to the feature data\n",
"accounts_feature_df = accounts_featureset.to_spark_dataframe()\n",
"display(accounts_feature_df.head(5))\n",
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/3. Enable recurrent materialization and run batch inference.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/3. Enable recurrent materialization and run batch inference.ipynb
index c80b6f4916..6fde470956 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/3. Enable recurrent materialization and run batch inference.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/3. Enable recurrent materialization and run batch inference.ipynb
@@ -3,34 +3,27 @@
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "# Tutorial #3: Enable recurrent materialization and run batch inference"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "# Tutorial #3: Enable recurrent materialization and run batch inference"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "_Managed feature store is in private preview, which is subject to the [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/en-us/support/legal/preview-supplemental-terms/)_"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
+ },
"source": [
"In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing.\n",
"\n",
@@ -39,83 +32,71 @@
"You will perform the following:\n",
"- Enable recurrent materialization for the `transactions` feature set\n",
"- Run batch inference pipeline on the registered model"
- ],
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "# Prerequisites\n",
+ "1. Please ensure you have executed the previous parts of this tutorial series"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "# Prerequisites\n",
- "1. Please ensure you have executed the previous parts of this tutorial series"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "# Setup"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "# Setup"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
+ },
"source": [
"#### (updated) Configure Azure ML spark notebook\n",
"\n",
"1. Running the tutorial: You can either create a new notebook, and execute the instructions in this document step by step or open the existing notebook named `Enable recurrent materialization and run batch inference`, and run it. The notebooks are available in `featurestore_sample/notebooks` directory. You can select from `sdk_only` or `sdk_and_cli`. You may keep this document open and refer to it for additional explanation and documentation links.\n",
"1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n",
"1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "#### Start spark session"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "#### Start spark session"
+ ]
},
{
"cell_type": "code",
- "source": [
- "# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
- "print(\"start spark session\")"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -131,37 +112,29 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
+ "print(\"start spark session\")"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "#### Setup root directory for the samples"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "#### Setup root directory for the samples"
+ ]
},
{
"cell_type": "code",
- "source": [
- "import os\n",
- "\n",
- "# please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
- "# You can find the name from the directory structure inm the left nav\n",
- "root_dir = \"./Users//featurestore_sample\"\n",
- "\n",
- "if os.path.isdir(root_dir):\n",
- " print(\"The folder exists.\")\n",
- "else:\n",
- " print(\"The folder does not exist. Please create or fix the path\")"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -177,32 +150,41 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
+ "# You can find the name from the directory structure inm the left nav\n",
+ "root_dir = \"./Users//featurestore_sample\"\n",
+ "\n",
+ "if os.path.isdir(root_dir):\n",
+ " print(\"The folder exists.\")\n",
+ "else:\n",
+ " print(\"The folder does not exist. Please create or fix the path\")"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "#### (new for sdk/cki track) Setup CLI\n",
- "\n",
- "1. Install azure ml cli extention\n",
- "1. Authenticate\n",
- "1. Set the default subscription"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "#### (new for sdk/cki track) Setup CLI\n",
+ "\n",
+ "1. Install azure ml cli extention\n",
+ "1. Authenticate\n",
+ "1. Set the default subscription"
+ ]
},
{
"cell_type": "code",
- "source": [
- "!az extension add --name ml"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"jupyter": {
@@ -218,14 +200,14 @@
"tags": [
"active-ipynb"
]
- }
+ },
+ "outputs": [],
+ "source": [
+ "!az extension add --name ml"
+ ]
},
{
"cell_type": "code",
- "source": [
- "!az login"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"jupyter": {
@@ -241,18 +223,14 @@
"tags": [
"active-ipynb"
]
- }
+ },
+ "outputs": [],
+ "source": [
+ "!az login"
+ ]
},
{
"cell_type": "code",
- "source": [
- "import os\n",
- "\n",
- "subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "\n",
- "!az account set -s $subscription_id"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -271,41 +249,33 @@
"tags": [
"active-ipynb"
]
- }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "\n",
+ "!az account set -s $subscription_id"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "#### Initialize the project workspace CRUD client\n",
- "This is the current workspace where you will be running the tutorial notebook from"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "#### Initialize the project workspace CRUD client\n",
+ "This is the current workspace where you will be running the tutorial notebook from"
+ ]
},
{
"cell_type": "code",
- "source": [
- "### Initialize the MLClient of this project workspace\n",
- "import os\n",
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
- "\n",
- "# connect to the project workspace\n",
- "ws_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
- ")"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -321,33 +291,41 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "### Initialize the MLClient of this project workspace\n",
+ "import os\n",
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
+ "\n",
+ "# connect to the project workspace\n",
+ "ws_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
+ ")"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "#### Initialize the feature store variables\n",
- "Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "#### Initialize the feature store variables\n",
+ "Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"
+ ]
},
{
"cell_type": "code",
- "source": [
- "featurestore_name = (\n",
- " \"\" # use the same name from part #1 of the tutorial\n",
- ")\n",
- "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -363,37 +341,32 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "featurestore_name = (\n",
+ " \"\" # use the same name from part #1 of the tutorial\n",
+ ")\n",
+ "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
- "source": [
- "#### Initialize the feature store core sdk client"
- ],
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
+ },
+ "source": [
+ "#### Initialize the feature store core sdk client"
+ ]
},
{
"cell_type": "code",
- "source": [
- "# feature store client\n",
- "from azureml.featurestore import FeatureStoreClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "featurestore = FeatureStoreClient(\n",
- " credential=AzureMLOnBehalfOfCredential(),\n",
- " subscription_id=featurestore_subscription_id,\n",
- " resource_group_name=featurestore_resource_group_name,\n",
- " name=featurestore_name,\n",
- ")"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -409,11 +382,31 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "# feature store client\n",
+ "from azureml.featurestore import FeatureStoreClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "featurestore = FeatureStoreClient(\n",
+ " credential=AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ " name=featurestore_name,\n",
+ ")"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
"source": [
"## Step 1: Enable recurrent materialization on the `transactions` featureset\n",
"\n",
@@ -425,26 +418,10 @@
"- Later recurrent jobs will be submitted at every window after the first job.\n",
"\n",
"As explained in the previous parts of the tutorials, once data is materialized (backfill/recurrent materialization), feature retrieval will use the materialized data by default."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "feature_set_schedule_yaml = (\n",
- " root_dir\n",
- " + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled_with_schedule.yaml\"\n",
- ")\n",
- "\n",
- "!az ml feature-set update --file $feature_set_schedule_yaml --resource-group $featurestore_resource_group_name --feature-store-name $featurestore_name"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -460,11 +437,27 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "feature_set_schedule_yaml = (\n",
+ " root_dir\n",
+ " + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled_with_schedule.yaml\"\n",
+ ")\n",
+ "\n",
+ "!az ml feature-set update --file $feature_set_schedule_yaml --resource-group $featurestore_resource_group_name --feature-store-name $featurestore_name"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
"source": [
"#### Track status of the recurrent materialization jobs in the feature store studio UI\n",
"This job will every three hours. \n",
@@ -473,18 +466,18 @@
"\n",
"1. Feel free to execute the next step for now (batch inference).\n",
"1. In three hours check the recurrent job status via the UI"
- ],
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
+ },
"source": [
"## Step 2: Run the batch-inference pipeline\n",
"\n",
@@ -496,26 +489,10 @@
"1. Batch inference: This step uses the batch inference input data from previous step, runs inference on the model and outputs the data by appending the predicted value.\n",
"\n",
"__Note:__ In this example we use a job for batch inference. You can also use Azure ML's batch endpoints."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "# set the batch inference pipeline path\n",
- "batch_inference_pipeline_path = (\n",
- " root_dir + \"/project/fraud_model/pipelines/batch_inference_pipeline.yaml\"\n",
- ")\n",
- "\n",
- "!az ml job create --file $batch_inference_pipeline_path --resource-group $project_ws_rg --workspace-name $project_ws_name"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -531,11 +508,27 @@
"deleting": false
}
}
- }
+ },
+ "outputs": [],
+ "source": [
+ "# set the batch inference pipeline path\n",
+ "batch_inference_pipeline_path = (\n",
+ " root_dir + \"/project/fraud_model/pipelines/batch_inference_pipeline.yaml\"\n",
+ ")\n",
+ "\n",
+ "!az ml job create --file $batch_inference_pipeline_path --resource-group $project_ws_rg --workspace-name $project_ws_name"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
"source": [
"#### Inspect the batch inference output data\n",
"1. In the pipeline view, double click on `inference_step` -> in `outputs` card, copy the `Data` field. It will be something like `azureml_995abbc2-3171-461e-8214-c3c5d17ede83_output_data_data_with_prediction:1`. \n",
@@ -543,27 +536,10 @@
"1. You will see the `predict_is_fraud` column generated by the batch inference pipeline\n",
"\n",
"Explanation: Since we did not provide a `name` and `version` in the `outputs` of the `inference_step` in the batch inference pipeline (`/project/fraud_mode/pipelines/batch_inference_pipeline.yaml`), the system created an untracked data asset with a guid as name and version as 1. In the next cell we will be getting the data path from the asset and displaying it."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "# inf_data_output = ws_client.data.get(name=\"azureml_1c106662-aa5e-4354-b5f9-57c1b0fdb3a7_output_data_data_with_prediction\", version=\"1\")\n",
- "inf_data_output = ws_client.data.get(\n",
- " name=\"azureml_0a7417c8-409a-4536-a069-4ea23a08ebfe_output_data_data_with_prediction\",\n",
- " version=\"1\",\n",
- ")\n",
- "inf_output_df = spark.read.parquet(inf_data_output.path)\n",
- "display(inf_output_df.head(5))"
- ],
- "outputs": [],
"execution_count": null,
"metadata": {
"gather": {
@@ -582,11 +558,28 @@
"tags": [
"active-ipynb"
]
- }
+ },
+ "outputs": [],
+ "source": [
+ "# inf_data_output = ws_client.data.get(name=\"azureml_1c106662-aa5e-4354-b5f9-57c1b0fdb3a7_output_data_data_with_prediction\", version=\"1\")\n",
+ "inf_data_output = ws_client.data.get(\n",
+ " name=\"azureml_0a7417c8-409a-4536-a069-4ea23a08ebfe_output_data_data_with_prediction\",\n",
+ " version=\"1\",\n",
+ ")\n",
+ "inf_output_df = spark.read.parquet(inf_data_output.path)\n",
+ "display(inf_output_df.head(5))"
+ ]
},
{
"attachments": {},
"cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
"source": [
"## Cleanup\n",
"If you created a resource group for the tutorial, you can delete the resource group to delete all the resources associated with this tutorial.\n",
@@ -596,14 +589,7 @@
"* Delete the feature store: Go to the resource group in the azure portal, select the feature store and delete it\n",
"* Follow the instructions [here](https://review.learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-manage-user-assigned-managed-identities?pivots=identity-mi-methods-azp&view=azureml-api-2#delete-a-user-assigned-managed-identity) to delete the user assigned managed identity\n",
"* Delete the offline store (storage account): Go to the resource group in the azure portal, select the storage you created and delete it"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ ]
}
],
"metadata": {
@@ -612,18 +598,18 @@
"name": "synapse_pyspark"
},
"kernelspec": {
- "name": "synapse_pyspark",
+ "display_name": "Synapse PySpark",
"language": "Python",
- "display_name": "Synapse PySpark"
+ "name": "synapse_pyspark"
},
"language_info": {
- "name": "python",
- "version": "3.8.0",
- "mimetype": "text/x-python",
+ "codemirror_mode": "ipython",
"file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
"pygments_lexer": "ipython",
- "codemirror_mode": "ipython",
- "nbconvert_exporter": "python"
+ "version": "3.8.0"
},
"microsoft": {
"host": {
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/Enable materialization and backfill feature data.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/Enable materialization and backfill feature data.ipynb
deleted file mode 100644
index 11af53f320..0000000000
--- a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/Enable materialization and backfill feature data.ipynb
+++ /dev/null
@@ -1,1197 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "# Tutorial #2: Enable materialization and backfill feature data"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing. \n",
- "\n",
- "In the part 1 of the tutorial you learnt how to create a feature set and use it to generate training data. When you query the featureset, the transformations will be applied on the source on-the-fly to compute the features before returning the values. This is fine for prototyping. However when you run training and inference in production environment, it is recommended that you materialize the features for higher reliability and availability. Materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. All feature queries will now use the values from the materialization store.\n",
- "\n",
- "In this tutorial (part 2 of the series) you will:\n",
- "- Enable offline store on the feature store by creating and attaching an ADLS gen2 container and a user assigned managed identity\n",
- "- Enable offline materialization on the feature sets, and backfill the feature data"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Important\n",
- "\n",
- "This feature is currently in public preview. This preview version is provided without a service-level agreement, and it's not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "# Prerequsite\n",
- "1. Please ensure you have executed part 1 of the tutorial\n",
- "1. An Azure Resource group, in which you (or the service principal you use) need to have `User Access Administrator` role and `Contributor` role."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "# Setup\n",
- "Summary of setup steps you will execute:\n",
- "- In your project workspace, create Azure ML compute to run training pipeline\n",
- "- In your feature store workspace, create a offline materialization store: create a Azure gen2 storage account and a container in it and attach to feature store. Optionally you can use existing storage container.\n",
- "- Create and assign user-assigned managed identity to feature store. Optionally you can use existing one. This will be used by the system managed materialization jobs i.e. recurrent job that will be used in part 3 of the tutorial\n",
- "- Grant required RBAC permissions to the user-assigned managed identity\n",
- "- Grant required RBAC to your AAD identity. Users (like you) need to have read access to (a) sources (b) materialization store"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Configure Azure ML spark notebook\n",
- "\n",
- "1. Running the tutorial: You can either create a new notebook, and execute the instructions in this document step by step or open the existing notebook named `2. Enable materialization and backfill feature data.ipynb`, and run it. The notebooks are available in `featurestore_sample/notebooks` directory. You can select from `sdk_only` or `sdk_and_cli`. You may keep this document open and refer to it for additional explanation and documentation links.\n",
- "1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n",
- "1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684207958655
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "start-spark-session",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "print(\"started spark session\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Setup root directory for the samples"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684208510317
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "root-dir",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "import os\n",
- "\n",
- "# please update the dir to ./Users/{your-alias} (or any custom directory you uploaded the samples to).\n",
- "# You can find the name from the directory structure inm the left nav\n",
- "root_dir = \"./Users//featurestore_sample\"\n",
- "\n",
- "if os.path.isdir(root_dir):\n",
- " print(\"The folder exists.\")\n",
- "else:\n",
- " print(\"The folder does not exist. Please create or fix the path\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### (new for sdk/cki track) Setup CLI\n",
- "\n",
- "1. Install azure ml cli extention\n",
- "1. Authenticate\n",
- "1. Set the default subscription"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684208553099
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "install-ml-ext-cli",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "# Install azure ml cli extention\n",
- "!az extension add --name ml"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684208689961
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "auth-cli",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "# Authenticate\n",
- "!az login"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684208694751
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "set-default-subs-cli",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "# Set the default subscription\n",
- "import os\n",
- "\n",
- "subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "\n",
- "!az account set -s $subscription_id"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### (new for sdk/cki track) Initialize the project workspace properties\n",
- "This is the current workspace where you will be running the tutorial notebook from."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684197959399
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-ws-crud-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# lookup the subscription id, resource group and workspace name of the current workspace\n",
- "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### (new for sdk/cki track) Initialize the feature store properties\n",
- "Ensure you update the `featurestore_name` and `featurestore_location` to reflect what you created in part 1 of this tutorial"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684208846587
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-fs-crud-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# use the same name from part #1 of the tutorial\n",
- "featurestore_name = \"\"\n",
- "# use the same location from part #1 of the tutorial\n",
- "featurestore_location = \"eastus\"\n",
- "# use the subscription of the project workspace by default\n",
- "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "# use the resource group of the project workspace by default\n",
- "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "\n",
- "feature_store_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.MachineLearningServices/workspaces/{ws_name}\".format(\n",
- " sub_id=featurestore_subscription_id,\n",
- " rg=featurestore_resource_group_name,\n",
- " ws_name=featurestore_name,\n",
- ")\n",
- "\n",
- "print(feature_store_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Initialize the feature store core sdk client"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684197989748
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-fs-core-sdk",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# feature store client\n",
- "from azureml.featurestore import FeatureStoreClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "featurestore = FeatureStoreClient(\n",
- " credential=AzureMLOnBehalfOfCredential(),\n",
- " subscription_id=featurestore_subscription_id,\n",
- " resource_group_name=featurestore_resource_group_name,\n",
- " name=featurestore_name,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Setup offline materialization store\n",
- "You can create a new gen2 storage account and a container, or reuse existing one to be used as the offline materilization store for the feature store"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "##### Note to docs team: \n",
- "The SDK only track has: `Setup utility functions` and the note below it (\"This code ...\"). This is not applicable in the CLI + SDK track, you can remove it in this track"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Set values for the adls gen 2 storage that will be used as materialization store\n",
- "You can optionally override the default settings"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198013967
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "set-offline-store-params",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "## Default Setting\n",
- "# We use the subscription, resource group, region of this active project workspace,\n",
- "# We hard-coded default resource names for creating new resources\n",
- "\n",
- "## Overwrite\n",
- "# You can replace them if you want to create the resources in a different subsciprtion/resourceGroup, or use existing resources\n",
- "\n",
- "storage_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "storage_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "storage_account_name = \"\"\n",
- "# feature store location is used by default. You can change it.\n",
- "storage_location = featurestore_location\n",
- "storage_file_system_name = \"\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Storage container (option 1): create new storage and container"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198050922
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "create-new-storage",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "# create new storage account\n",
- "!az storage account create --name $storage_account_name --enable-hierarchical-namespace true --resource-group $storage_resource_group_name --location $storage_location --subscription $storage_subscription_id"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198081290
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "create-new-storage-container",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "# create new storage container\n",
- "!az storage fs create --name $storage_file_system_name --account-name $storage_account_name --subscription $storage_subscription_id --auth-mode login"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198089900
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "set-container-arm-id-cli",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# set the container arm id\n",
- "gen2_container_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account}/blobServices/default/containers/{container}\".format(\n",
- " sub_id=storage_subscription_id,\n",
- " rg=storage_resource_group_name,\n",
- " account=storage_account_name,\n",
- " container=storage_file_system_name,\n",
- ")\n",
- "\n",
- "print(gen2_container_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Storage container (option 2): If you have an existing storage container that you want to reuse"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198094208
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "use-existing-storage",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# set the container arm id\n",
- "gen2_container_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account}/blobServices/default/containers/{container}\".format(\n",
- " sub_id=storage_subscription_id,\n",
- " rg=storage_resource_group_name,\n",
- " account=storage_account_name,\n",
- " container=storage_file_system_name,\n",
- ")\n",
- "\n",
- "print(gen2_container_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "### Setup user assigned managed identity (UAI)\n",
- "This will be used by the system managed materialization jobs i.e. recurrent job that will be used in part 3 of the tutorial"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Set values for UAI"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198104193
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "set-uai-params",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# User assigned managed identity values. Optionally you may change the values.\n",
- "uai_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "uai_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "uai_name = \"\"\n",
- "# feature store location is used by default. You can change it.\n",
- "uai_location = featurestore_location"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### User-assigned managed identity (option 1): create new one"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683418554848
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "create-new-uai",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "!az identity create --subscription $uai_subscription_id --resource-group $uai_resource_group_name --location $uai_location --name $uai_name"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### User-assigned managed identity (option 2): If you have an existing one that you want to reuse\n",
- "Run `az identity show` to get the UAI information"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683421366072
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "use-existing-uai",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "!az identity show --resource-group $uai_resource_group_name --subscription $uai_subscription_id --name $uai_name"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Retrieve UAI properties"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198195891
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "retrieve-uai-properties",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from azure.mgmt.msi import ManagedServiceIdentityClient\n",
- "from azure.mgmt.msi.models import Identity\n",
- "\n",
- "msi_client = ManagedServiceIdentityClient(\n",
- " AzureMLOnBehalfOfCredential(), uai_subscription_id\n",
- ")\n",
- "managed_identity = msi_client.user_assigned_identities.get(\n",
- " resource_name=uai_name, resource_group_name=uai_resource_group_name\n",
- ")\n",
- "\n",
- "uai_principal_id = managed_identity.principal_id\n",
- "uai_client_id = managed_identity.client_id\n",
- "uai_arm_id = managed_identity.id"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Grant RBAC permission to the user assigned managed identity (UAI)\n",
- "\n",
- "This UAI will be assigned to the feature store shortly. It requires the following permissions:\n",
- "\n",
- "|Scope|\tAction/Role|\n",
- "|--|--|\n",
- "|Feature store\t|AzureML Data Scientist role|\n",
- "|Storage account of feature store offline store\t|Blob storage data contributor role|\n",
- "|Storage accounts of source data\t|Blob storage data reader role|\n",
- "\n",
- "The below cli commands will assign the first two roles to the UAI. In this example \"Storage accounts of source data\" is not applicable since we are reading the sample data from a public access blob storage. If you have your own data sources then you want to assign the required roles to the UAI. To learn more about access control, see access control document in the docs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "grant-rbac-to-uai-fs",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "!az role assignment create --role \"AzureML Data Scientist\" --assignee-object-id $uai_principal_id --assignee-principal-type ServicePrincipal --scope $feature_store_arm_id"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "grant-rbac-to-uai-offline-store",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "!az role assignment create --role \"Storage Blob Data Contributor\" --assignee-object-id $uai_principal_id --assignee-principal-type ServicePrincipal --scope $gen2_container_arm_id"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Grant your user account \"Blob data reader\" role on the offline store\n",
- "If feature data is materialized, then you need this role to read feature data from offline materialization store.\n",
- "\n",
- "Get your AAD object id from Azure portal following this instruction: https://learn.microsoft.com/en-us/partner-center/find-ids-and-domain-names#find-the-user-object-id\n",
- "\n",
- "To learn more about access control, see access control document in the docs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198291857
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "grant-rbac-to-user-identity",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- },
- "outputs": [],
- "source": [
- "# This utility function is created for ease of use in the docs tutorials. It uses standard azure API's. You can optionally inspect it `featurestore/setup/setup_storage_uai.py`\n",
- "your_aad_objectid = \"\"\n",
- "\n",
- "!az role assignment create --role \"Storage Blob Data Reader\" --assignee-object-id $your_aad_objectid --assignee-principal-type User --scope $gen2_container_arm_id"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 1: Enable offline store on the feature store by attaching offline materialization store and UAI"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "__(todo) (new for sdk+cli track)__ Action: inspect the file `xxxx`. The below command will update the feature store by attaching the offline store and UAI."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198312818
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "dump_featurestore_yaml",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# The below code creates a feature store\n",
- "import yaml\n",
- "\n",
- "config = {\n",
- " \"$schema\": \"http://azureml/sdk-2-0/FeatureStore.json\",\n",
- " \"name\": featurestore_name,\n",
- " \"location\": featurestore_location,\n",
- " \"compute_runtime\": {\"spark_runtime_version\": \"3.2\"},\n",
- " \"offline_store\": {\"type\": \"azure_data_lake_gen2\", \"target\": gen2_container_arm_id},\n",
- " \"materialization_identity\": {\"client_id\": uai_client_id, \"resource_id\": uai_arm_id},\n",
- "}\n",
- "\n",
- "feature_store_yaml = root_dir + \"/featurestore/featurestore_with_offline_setting.yaml\"\n",
- "\n",
- "with open(feature_store_yaml, \"w\") as outfile:\n",
- " yaml.dump(config, outfile, default_flow_style=False)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683421376728
- },
- "jupyter": {
- "outputs_hidden": true,
- "source_hidden": false
- },
- "name": "enable-offline-store",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "!az ml feature-store update --file $feature_store_yaml --resource-group $featurestore_resource_group_name --name $featurestore_name"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 2: Enable offline materialization on transactions featureset\n",
- "Once materialization is enabled on a featureset, you can perform backfill (this tutorial) or schedule recurrent materialization jobs (next part of the tutorial)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "__(todo) (new for sdk+cli track)__ Action: inspect the file `xxxx`. The below command will update the transaction feature set to enable offline materilization"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198473486
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "enable-offline-mat-txns-fset",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "transaction_asset_mat_yaml = (\n",
- " root_dir\n",
- " + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled.yaml\"\n",
- ")\n",
- "\n",
- "!az ml feature-set update --file $transaction_asset_mat_yaml --resource-group $featurestore_resource_group_name --workspace-name $featurestore_name"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 3: Backfill data for transactions featureset\n",
- "As explained in the beginning of this tutorial, materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. Materializing the features will increase its reliability and availability. All feature queries will now use the values from the materialization store. In this step you perform a one-time backfill for a feature window of __three months__.\n",
- "\n",
- "#### Note\n",
- "How to determine the window of backfill data needed? It has to match with the window of your training data. For e.g. if you want to train with two years of data, then you will want to be able to retrieve features for the same window, so you will backfill for a two year window."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684208874891
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "backfill-txns-fset",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "feature_window_start_time = \"2023-01-01T00:00.000Z\"\n",
- "feature_window_end_time = \"2023-04-01T00:00.000Z\"\n",
- "\n",
- "!az ml feature-set backfill --name transactions --version 1 --workspace-name $featurestore_name --resource-group $featurestore_resource_group_name --feature-window-start-time $feature_window_start_time --feature-window-end-time $feature_window_end_time"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "Lets print sample data from the featureset. You can notice from the output information that the data was retrieved from the materilization store. `get_offline_features()` method that is used to retrieve training/inference data will also use the materialization store by default ."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684198545115
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "sample-txns-fset-data",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# look up the featureset by providing name and version\n",
- "transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
- "display(transactions_featureset.to_spark_dataframe().head(5))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Cleanup\n",
- "Part 4 of the tutorial has instructions for deleting the resources"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Next steps\n",
- "* Part 3 of tutorial: Experiment and train models using features"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Edit Metadata",
- "kernel_info": {
- "name": "synapse_pyspark"
- },
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.7.13"
- },
- "microsoft": {
- "host": {
- "AzureML": {
- "notebookHasBeenCompleted": true
- }
- },
- "ms_spell_check": {
- "ms_spell_check_language": "en"
- }
- },
- "nteract": {
- "version": "nteract-front-end@1.0.0"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_and_cli/images/offline-store-information.png b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/images/offline-store-information.png
new file mode 100644
index 0000000000..3d0e0010d9
Binary files /dev/null and b/sdk/python/featurestore_sample/notebooks/sdk_and_cli/images/offline-store-information.png differ
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/1. Develop a feature set and register with managed feature store.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_only/1. Develop a feature set and register with managed feature store.ipynb
index 20296d629a..70d06122a6 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_only/1. Develop a feature set and register with managed feature store.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_only/1. Develop a feature set and register with managed feature store.ipynb
@@ -1 +1,1316 @@
-{"cells":[{"attachments":{},"cell_type":"markdown","source":["# Tutorial: Develop a feature set and register with managed feature store"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["Azure ML managed feature store lets you discover, create and operationalize features. Features are the connective tissue in ML lifecycle, starting from prototyping where you experiment with various features to operationalization where models are deployed and feature data is looked up during inference. For information on basics concept of feature store, see [feature store concepts](fs-concepts).\n","\n","In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle.\n","\n","This tutorial is the first part of a three part series. In this tutorial you will:\n","- Create a new minimal feature store resource\n","- Develop and test featureset locally with feature transformation capability\n","- Register a feature-store entity with the feature store\n","- Register the featureset that you developed with the feature store\n","- Generate sample training data dataframe using the features you created\n","- Enable offline materialization on the feature sets, and backfill the feature data\n"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Important\n","\n","This feature is currently in public preview. This preview version is provided without a service-level agreement, and it's not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Prerequisites\n","Before following the steps in this article, make sure you have the following prerequisites:\n","\n","* An Azure Machine Learning workspace. If you don't have one, use the steps in the [Quickstart: Create workspace resources](https://learn.microsoft.com/en-us/azure/machine-learning/quickstart-create-resources?view=azureml-api-2) article to create one.\n","* To perform the steps in this article, your user account must be assigned the owner or contributor role to a resource group where the feature store will be created"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Setup "],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Prepare the notebook environment for development\n","Note: This tutorial uses AzureML spark notebook for development. (placeholder: link to ADB document once ready)\n","\n","1. Clone the examples repository to your local machine: To run the tutorial, first clone the [examples repository (azureml-examples)](https://github.com/azure/azureml-examples)\n","\n","```bash\n","git clone --depth 1 https://github.com/Azure/azureml-examples\n","```\n","\n","Alternatively you can download a zip file from the [examples repository (azureml-examples)](https://github.com/azure/azureml-examples): click on the `code` dropdown and click `Download ZIP`. Then unzip the contents into a folder in your local machine.\n","\n","2. Upload the feature store samples directory to project workspace: Open Azure ML studio UI of your Azure ML workspace -> click on \"Notebooks\" in left nav -> right click on your user name in the directory listing -> click \"upload folder\" -> select the feature store samples folder from the cloned directory path: `azureml-examples/sdk/python/featurestore-sample`\n","\n","3. You can either create a new notebook and paste the instructions in this document step by step and execute OR open the existing notebook titled `1.Develop a feature set and register with managed feature store.ipynb`. You can execute step by step. Keep this document open and refer to it for detailed explanation of the steps. The notebooks are available in the folder: `featurestore_sample/notebooks`. Select either `sdk_only` folder or the `sdk_and_cli` folder. The latter has CLI commands mixed with python sdk useful in ci/cd scenarios.\n","\n","4. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". It may take 1-2 minutes for this activity to complete. Wait for a status bar in the top to display `configure session`\n","\n","5. Click on \"configure session\" -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file `azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml` from your local machine; Also increase the session time out (idle time) if you want to reduce serverless spark cluster startup time.\n","\n","__Important:__ Except for this step, you need to run all the other steps every time you have a new spark session/session time out\n"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Start spark session"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n","print(\"start spark session\")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545108574},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"start-spark-session","nteract":{"transient":{"deleting":false}},"tags":[]}},{"attachments":{},"cell_type":"markdown","source":["#### Setup root directory for the samples"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["import os\n","\n","# Please update below (or any custom directory you uploaded the samples to).\n","# You can find the name from the directory structure in the left nav\n","root_dir = \"./Users//featurestore_sample\"\n","\n","if os.path.isdir(root_dir):\n"," print(\"The folder exists.\")\n","else:\n"," print(\"The folder does not exist. Please create or fix the path\")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545283706},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"root-dir","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Note\n","Feature store Vs Project workspace: You will use a featurestore to reuse features across projects. You will use a project workspace(the current workspace) to train and inference models, by leveraging features from feature stores. Many project workspaces can share and reuse a same feature store.\n","\n","## Note\n","In this tutorial you will be using two SDK's:\n","\n","1. Feature store CRUD sdk: You will use the same SDK, MLClient (package name `azure-ai-ml`), that you use with Azure ML workpace. This will be used for feature store CRUD operations (Create, Update and Delete) for featurestore, featureset and featurestore-entity. This is because feature store is implemented as a type of workspace. \n","2. Feature store core sdk: This sdk (`azureml-featurestore`) is meant to be used for feature set development and consumption (you will learn more about these operations later):\n","- List/Get registered feature set\n","- Generate/resolve feature retrieval spec\n","- Execute featureset definition to generate Spark dataframe\n","- Generate training using a point-in-time join\n","\n","For this tutorial so you do not need to install any of these explicitly, since the instructions already cover them (conda yaml in the above step include these)"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 1: Create a minimal feature store"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 1a: Set feature store parameters\n","Set name, location and other values for the feature store"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# We use the subscription, resource group, region of this active project workspace.\n","# You can optionally replace them to create the resources in a different subsciprtion/resourceGroup, or use existing resources\n","import os\n","\n","featurestore_name = \"my-featurestore\"\n","featurestore_location = \"eastus\"\n","featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n","featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n","version = \"\""],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545370071},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"fs-params","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 1b: Create the feature store"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml import MLClient\n","from azure.ai.ml.entities import (\n"," FeatureStore,\n"," FeatureStoreEntity,\n"," FeatureSet,\n",")\n","from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n","\n","ml_client = MLClient(\n"," AzureMLOnBehalfOfCredential(),\n"," subscription_id=featurestore_subscription_id,\n"," resource_group_name=featurestore_resource_group_name,\n",")\n","\n","\n","fs = FeatureStore(name=featurestore_name, location=featurestore_location)\n","# wait for featurestore creation\n","fs_poller = ml_client.feature_stores.begin_create(fs, update_dependent_resources=True)\n","print(fs_poller.result())"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545478258},"jupyter":{"outputs_hidden":true,"source_hidden":false},"name":"create-fs","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 1c: Initialize AzureML feature store core SDK client\n","As explained above, this is used to develop and consume features"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# feature store client\n","from azureml.featurestore import FeatureStoreClient\n","from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n","\n","featurestore = FeatureStoreClient(\n"," credential=AzureMLOnBehalfOfCredential(),\n"," subscription_id=featurestore_subscription_id,\n"," resource_group_name=featurestore_resource_group_name,\n"," name=featurestore_name,\n",")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545558268},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"init-fs-core-sdk","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 2: Prototype and develop a transaction rolling aggregation featureset in this notebook"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2a: Explore the transactions source data\n","\n","#### Note\n","The sample data used in this notebook is hosted in a public accessible blob container. It can only be read in Spark via `wasbs` driver. When you create feature sets using your own source data, please host them in adls gen2 account and use `abfss` driver in the data path. "],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# remove the \".\" in the roor directory path as we need to generate absolute path to read from spark\n","transactions_source_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/transactions-source/*.parquet\"\n","transactions_src_df = spark.read.parquet(transactions_source_data_path)\n","\n","display(transactions_src_df.head(5))\n","# Note: display(training_df.head(5)) displays the timestamp column in a different format. You can can call transactions_src_df.show() to see correctly formatted value"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545597487},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"explore-txn-src-data","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2b: Develop a transactions featureset locally\n","\n","Featureset specification is a self-contained definition of feature set that can be developed and tested locally.\n","\n","Lets create the following rolling window aggregate features:\n","- transactions 3-day count\n","- transactions amount 3-day sum\n","- transactions amount 3-day avg\n","- transactions 7-day count\n","- transactions amount 7-day sum\n","- transactions amount 7-day avg\n","\n","__Action__:\n","- Inspect the feature transformation code file: `featurestore/featuresets/transactions/spec/transformation_code/transaction_transform.py`. You will see how is the rolling aggregation defined for the features. This is a spark transformer.\n","\n","To understand the feature set and transformations in more detail, see [feature store concepts](fs-concepts-url-todo) and [transformation concepts](fs-transformation-concepts-todo)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azureml.featurestore import create_feature_set_spec, FeatureSetSpec\n","from azureml.featurestore.contracts import (\n"," DateTimeOffset,\n"," FeatureSource,\n"," TransformationCode,\n"," Column,\n"," ColumnType,\n"," SourceType,\n"," TimestampColumn,\n",")\n","\n","\n","transactions_featureset_code_path = (\n"," root_dir + \"/featurestore/featuresets/transactions/transformation_code\"\n",")\n","\n","transactions_featureset_spec = create_feature_set_spec(\n"," source=FeatureSource(\n"," type=SourceType.parquet,\n"," path=\"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/transactions-source/*.parquet\",\n"," timestamp_column=TimestampColumn(name=\"timestamp\"),\n"," source_delay=DateTimeOffset(days=0, hours=0, minutes=20),\n"," ),\n"," transformation_code=TransformationCode(\n"," path=transactions_featureset_code_path,\n"," transformer_class=\"transaction_transform.TransactionFeatureTransformer\",\n"," ),\n"," index_columns=[Column(name=\"accountID\", type=ColumnType.string)],\n"," source_lookback=DateTimeOffset(days=7, hours=0, minutes=0),\n"," temporal_join_lookback=DateTimeOffset(days=1, hours=0, minutes=0),\n"," infer_schema=True,\n",")\n","# Generate a spark dataframe from the feature set specification\n","transactions_fset_df = transactions_featureset_spec.to_spark_dataframe()\n","# display few records\n","display(transactions_fset_df.head(5))"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545617083},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"develop-txn-fset-locally","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2c: Export as feature set spec\n","Inorder to register the feature set spec with the feature store, it needs to be saved in a specific format. \n","Action: Please inspect the generated `transactions` FeaturesetSpec: Open this file from the file tree to see the spec: `featurestore/featuresets/accounts/spec/FeaturesetSpec.yaml`\n","\n","Spec contains these important elements:\n","\n","1. `source`: reference to a storage. In this case a parquet file in a blob storage.\n","1. `features`: list of features and their datatypes. If you provide transformation code (see Day 2 section), the code has to return a dataframe that maps to the features and datatypes.\n","1. `index_columns`: the join keys required to access values from the feature set\n","\n","Learn more about it in the [top level feature store entities document](fs-concepts-todo) and the [feature set spec yaml reference](reference-yaml-featureset-spec.md).\n","\n","The additional benefit of persisting it is that it can be source controlled."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["import os\n","\n","# create a new folder to dump the feature set spec\n","transactions_featureset_spec_folder = (\n"," root_dir + \"/featurestore/featuresets/transactions/spec\"\n",")\n","\n","# check if the folder exists, create one if not\n","if not os.path.exists(transactions_featureset_spec_folder):\n"," os.makedirs(transactions_featureset_spec_folder)\n","\n","transactions_featureset_spec.dump(transactions_featureset_spec_folder, overwrite=True)"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545629168},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"dump-transactions-fs-spec","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 3: Register a feature-store entity\n","Entity helps enforce best practice that same join key definitions are used across featuresets which uses the same logical entities. Examples of entities are account entity, customer entity etc. Entities are typically created once and reused across feature-sets. For information on basics concept of feature store, see [feature store concepts](fs-concepts)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 3a: Initialize the Feature Store CRUD client\n","\n","As explained in the beginning of this tutorial, MLClient is used for CRUD of assets in feature store. The below code looks up the feature store we created in an earlier step. We cannot reuse the same ml_client used above here because the former is scoped at resource group level, which is a prerequisite for creation of feature store. The below one is scoped at feature store level.\n"," "],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# mlclient on feature store\n","fs_client = MLClient(\n"," AzureMLOnBehalfOfCredential(),\n"," featurestore_subscription_id,\n"," featurestore_resource_group_name,\n"," featurestore_name,\n",")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545635654},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"init-fset-crud-client","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 3b: Register `account` entity with the feature store\n","Create account entity that has join key `accountID` of `string` type. "],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml.entities import DataColumn, DataColumnType\n","\n","account_entity_config = FeatureStoreEntity(\n"," name=\"account\",\n"," version=version,\n"," index_columns=[DataColumn(name=\"accountID\", type=DataColumnType.STRING)],\n"," stage=\"Development\",\n"," description=\"This entity represents user account index key accountID.\",\n"," tags={\"data_typ\": \"nonPII\"},\n",")\n","\n","poller = fs_client.feature_store_entities.begin_create_or_update(account_entity_config)\n","print(poller.result())"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545646811},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"register-acct-entity","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 4: Register the transaction featureset with the featurestore\n","You register a feature set asset with the feature store so that you can share and reuse with others. You also get managed capabilities like versioning and materialization (we will learn in this tutorial series).\n","\n","The feature set asset has reference to the feature set spec that you created earlier and additional properties like version and materialization settings."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml.entities import FeatureSetSpecification\n","\n","transaction_fset_config = FeatureSet(\n"," name=\"transactions\",\n"," version=version,\n"," description=\"7-day and 3-day rolling aggregation of transactions featureset\",\n"," entities=[f\"azureml:account:{version}\"],\n"," stage=\"Development\",\n"," specification=FeatureSetSpecification(path=transactions_featureset_spec_folder),\n"," tags={\"data_type\": \"nonPII\"},\n",")\n","\n","poller = fs_client.feature_sets.begin_create_or_update(transaction_fset_config)\n","print(poller.result())"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696545663104},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"register-txn-fset","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Explore the FeatureStore UI\n","* Goto the [Azure ML global landing page](https://ml.azure.com/home?flight=FeatureStores).\n","* Click on `Feature stores` in the left nav\n","* You will see the list of feature stores that you have access to. Click on the feature store that you created above.\n","\n","You can see the feature set and entity that you created.\n","\n","Note: Creating and updating feature store assets are possible only through SDK and CLI. You can use the UI to search/browse the feature store."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 5: Generate a training data dataframe using the registered features"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 5a: Load observation data\n","\n","We start by exploring the observation data. Observation data is typically the core data used in training and inference data. This is then joined with feature data to create the full training data. Observation data is the data captured during the time of the event: in this case it has core transaction data including transaction id, account id, transaction amount. In this case, since it is for training, it also has the target variable appended (is_fraud).\n","\n","To learn more core concepts including observation data, refer to the docs"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["observation_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/observation_data/train/*.parquet\"\n","observation_data_df = spark.read.parquet(observation_data_path)\n","obs_data_timestamp_column = \"timestamp\"\n","\n","display(observation_data_df)\n","# Note: the timestamp column is displayed in a different format. Optionally, you can can call training_df.show() to see correctly formatted value"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1683417449378},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"load-obs-data","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 5c: Get the registered featureset and list its features"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# look up the featureset by providing name and version\n","transactions_featureset = featurestore.feature_sets.get(\"transactions\", version)\n","# list its features\n","transactions_featureset.features"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1683416499081},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"get-txn-fset","nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# print sample values\n","display(transactions_featureset.to_spark_dataframe().head(5))"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1683416508419},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"print-txn-fset-sample-values","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 5d: Select features and generate training data\n","In this step we will select features that we would like to be part of training data and use the feature store sdk to generate the training data."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azureml.featurestore import get_offline_features\n","\n","# you can select features in pythonic way\n","features = [\n"," transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n"," transactions_featureset.get_feature(\"transaction_amount_7d_avg\"),\n","]\n","\n","# you can also specify features in string form: featurestore:featureset:version:feature\n","more_features = [\n"," f\"transactions:{version}:transaction_3d_count\",\n"," f\"transactions:{version}:transaction_amount_3d_avg\",\n","]\n","\n","more_features = featurestore.resolve_feature_uri(more_features)\n","features.extend(more_features)\n","\n","# generate training dataframe by using feature data and observation data\n","training_df = get_offline_features(\n"," features=features,\n"," observation_data=observation_data_df,\n"," timestamp_column=obs_data_timestamp_column,\n",")\n","\n","# Ignore the message that says feature set is not materialized (materialization is optional). We will enable materialization in the next part of the tutorial.\n","display(training_df)\n","# Note: the timestamp column is displayed in a different format. Optionally, you can can call training_df.show() to see correctly formatted value"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1683417499373},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"select-features-and-gen-training-data","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["You can see how the features are appended to the training data using a point-in-time join.\n","\n","We have reached the end of the tutorial. Now you have your training data using features from feature store. You can either save it to storage for later use, or run model training on it directly."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"markdown","source":["## Step 6: Enable offline materialization on transactions featureset\n","Once materialization is enabled on a featureset, you can perform backfill (this tutorial) or schedule recurrent materialization jobs(next part of the tutorial)"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml.entities import (\n"," MaterializationSettings,\n"," MaterializationComputeResource,\n",")\n","\n","transactions_fset_config = fs_client._featuresets.get(\n"," name=\"transactions\", version=version\n",")\n","\n","transactions_fset_config.materialization_settings = MaterializationSettings(\n"," offline_enabled=True,\n"," resource=MaterializationComputeResource(instance_type=\"standard_e8s_v3\"),\n"," spark_configuration={\n"," \"spark.driver.cores\": 4,\n"," \"spark.driver.memory\": \"36g\",\n"," \"spark.executor.cores\": 4,\n"," \"spark.executor.memory\": \"36g\",\n"," \"spark.executor.instances\": 2,\n"," },\n"," schedule=None,\n",")\n","\n","fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n","print(fs_poller.result())"],"outputs":[],"execution_count":null,"metadata":{"jupyter":{"source_hidden":false,"outputs_hidden":false},"nteract":{"transient":{"deleting":false}}}},{"cell_type":"markdown","source":["Optionally, you can save the the above feature set asset as yaml"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["## uncomment to run\n","transactions_fset_config.dump(\n"," root_dir\n"," + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled.yaml\"\n",")"],"outputs":[],"execution_count":null,"metadata":{"jupyter":{"source_hidden":false,"outputs_hidden":false},"nteract":{"transient":{"deleting":false}}}},{"cell_type":"markdown","source":["## Step 7: Backfill data for transactions featureset\n","As explained in the beginning of this tutorial, materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. Materializing the features will increase its reliability and availability. All feature queries will now use the values from the materialization store. In this step you perform a one-time backfill for a feature window of __three months__.\n","\n","#### Note\n","How to determine the window of backfill data needed? It has to match with the window of your training data. For e.g. if you want to train with two years of data, then you will want to be able to retrieve features for the same window, so you will backfill for a two year window."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from datetime import datetime\n","\n","st = datetime(2023, 1, 1, 0, 0, 0, 0)\n","ed = datetime(2023, 4, 1, 0, 0, 0, 0)\n","\n","poller = fs_client.feature_sets.begin_backfill(\n"," name=\"transactions\",\n"," version=version,\n"," feature_window_start_time=st,\n"," feature_window_end_time=ed,\n",")\n","print(poller.result().job_id)"],"outputs":[],"execution_count":null,"metadata":{"jupyter":{"source_hidden":false,"outputs_hidden":false},"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# get the job URL, and stream the job logs\n","fs_client.jobs.stream(poller.result().job_id)"],"outputs":[],"execution_count":null,"metadata":{"jupyter":{"source_hidden":false,"outputs_hidden":false},"nteract":{"transient":{"deleting":false}},"tags":["active-ipynb"]}},{"cell_type":"markdown","source":["Lets print sample data from the featureset. You can notice from the output information that the data was retrieved from the materilization store. `get_offline_features()` method that is used to retrieve training/inference data will also use the materialization store by default."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# look up the featureset by providing name and version\n","transactions_featureset = featurestore.feature_sets.get(\"transactions\", version)\n","display(transactions_featureset.to_spark_dataframe().head(5))"],"outputs":[],"execution_count":null,"metadata":{"jupyter":{"source_hidden":false,"outputs_hidden":false},"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Cleanup\n","\n","Tutorial of \"Enable recurrent materialization and run batch inference\" has instructions deleting the resources"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Next steps\n","* Experiment and train models using features"],"metadata":{"nteract":{"transient":{"deleting":false}}}}],"metadata":{"celltoolbar":"Edit Metadata","kernel_info":{"name":"synapse_pyspark"},"kernelspec":{"name":"synapse_pyspark","language":"Python","display_name":"Synapse PySpark"},"language_info":{"name":"python","version":"3.8.0","mimetype":"text/x-python","file_extension":".py","pygments_lexer":"ipython","codemirror_mode":"ipython","nbconvert_exporter":"python"},"microsoft":{"host":{"AzureML":{"notebookHasBeenCompleted":true}},"ms_spell_check":{"ms_ignore_dictionary":["dataframe","featureset","operationalization","operationalize"],"ms_spell_check_language":"en"}},"nteract":{"version":"nteract-front-end@1.0.0"},"synapse_widget":{"version":"0.1","state":{"e28c2a28-1078-4071-94a5-6ceca9b5c439":{"type":"Synapse.DataFrame","sync_state":{"table":{"rows":[{"0":"B1E2828A-4A9A-43EF-BFCB-1BF8FEA572A3","1":"A1055520426549020","2":"299.0","3":"AUD","4":"21.0","6":"119.17","7":"312.55965","9":"A","10":"P","13":"victoria","14":"3000","15":"au","16":"false","18":"en-AU","19":"CREDITCARD","20":"VISA","24":"2000","25":"New South Wales","26":"AU","33":"M","35":"0.0","36":"2.0","38":"0","39":"java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id=\"Etc/UTC\",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2023,MONTH=5,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=3,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=11,HOUR_OF_DAY=11,MINUTE=44,SECOND=2,MILLISECOND=0,ZONE_OFFSET=?,DST_OFFSET=?]","index":1},{"0":"D57798E0-22FC-420B-9E77-6A5D29A44D2C","1":"A1055520427215940","2":"139.09","3":"USD","4":"14.0","6":"67.19","7":"139.09","9":"A","10":"P","13":"florida","14":"32204","15":"us","16":"false","18":"en-US","19":"CREDITCARD","20":"MC","24":"31411","25":"GA","26":"US","33":"M","35":"0.0","36":"1.0","38":"0","39":"java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id=\"Etc/UTC\",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2023,MONTH=1,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=11,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=1,HOUR=7,HOUR_OF_DAY=19,MINUTE=54,SECOND=55,MILLISECOND=0,ZONE_OFFSET=?,DST_OFFSET=?]","index":2},{"0":"E5C8E826-C06A-4C21-B2EF-654C558E3525","1":"A1055520427375210","2":"14.99","3":"USD","4":"9.0","6":"135.245","7":"14.99","9":"A","10":"P","13":"georgia","14":"30309","15":"us","16":"false","18":"en-US","19":"CREDITCARD","20":"VISA","24":"60532","25":"IL","26":"US","33":"M","35":"1.0","36":"0.0","38":"0","39":"java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id=\"Etc/UTC\",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2023,MONTH=1,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=27,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=45,SECOND=5,MILLISECOND=0,ZONE_OFFSET=?,DST_OFFSET=?]","index":3},{"0":"6F951FE2-1EA9-4E52-87CF-E0395107B3DA","1":"A1055520427377310","2":"381.8","3":"USD","4":"21.0","6":"99.33","7":"381.8","9":"A","10":"P","13":"connecticut","14":"6854","15":"us","16":"false","18":"en-US","19":"CREDITCARD","20":"VISA","24":"6850","25":"CT","26":"US","33":"M","35":"1.0","36":"1.0","38":"0","39":"java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id=\"Etc/UTC\",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2023,MONTH=2,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=10,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=2,HOUR_OF_DAY=2,MINUTE=19,SECOND=49,MILLISECOND=0,ZONE_OFFSET=?,DST_OFFSET=?]","index":4},{"0":"936E2FF7-2171-44CC-9E43-3C4FF3B06519","1":"A1055520427478840","2":"699.47","3":"SEK","6":"217.12400000000002","7":"109.606949","9":"A","10":"P","13":"madrid","14":"28001","15":"es","16":"false","18":"sv-SE","19":"CREDITCARD","20":"VISA","24":"192 71","26":"SE","33":"M","35":"0.0","36":"2.0","38":"0","39":"java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id=\"Etc/UTC\",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2023,MONTH=2,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=1,HOUR=0,HOUR_OF_DAY=12,MINUTE=49,SECOND=36,MILLISECOND=0,ZONE_OFFSET=?,DST_OFFSET=?]","index":5}],"schema":[{"key":"0","name":"transactionID","type":"string"},{"key":"1","name":"accountID","type":"string"},{"key":"2","name":"transactionAmount","type":"string"},{"key":"3","name":"transactionCurrencyCode","type":"string"},{"key":"4","name":"localHour","type":"string"},{"key":"5","name":"transactionDeviceId","type":"string"},{"key":"6","name":"transactionIPaddress","type":"string"},{"key":"7","name":"transactionAmountUSD","type":"string"},{"key":"8","name":"transactionCurrencyConversionRate","type":"string"},{"key":"9","name":"transactionScenario","type":"string"},{"key":"10","name":"transactionType","type":"string"},{"key":"11","name":"transactionMethod","type":"string"},{"key":"12","name":"transactionDeviceType","type":"string"},{"key":"13","name":"ipState","type":"string"},{"key":"14","name":"ipPostcode","type":"string"},{"key":"15","name":"ipCountryCode","type":"string"},{"key":"16","name":"isProxyIP","type":"string"},{"key":"17","name":"browserType","type":"string"},{"key":"18","name":"browserLanguage","type":"string"},{"key":"19","name":"paymentInstrumentType","type":"string"},{"key":"20","name":"cardType","type":"string"},{"key":"21","name":"cardNumberInputMethod","type":"string"},{"key":"22","name":"paymentInstrumentID","type":"string"},{"key":"23","name":"paymentBillingAddress","type":"string"},{"key":"24","name":"paymentBillingPostalCode","type":"string"},{"key":"25","name":"paymentBillingState","type":"string"},{"key":"26","name":"paymentBillingCountryCode","type":"string"},{"key":"27","name":"paymentBillingName","type":"string"},{"key":"28","name":"shippingAddress","type":"string"},{"key":"29","name":"shippingPostalCode","type":"string"},{"key":"30","name":"shippingCity","type":"string"},{"key":"31","name":"shippingState","type":"string"},{"key":"32","name":"shippingCountry","type":"string"},{"key":"33","name":"cvvVerifyResult","type":"string"},{"key":"34","name":"responseCode","type":"string"},{"key":"35","name":"digitalItemCount","type":"string"},{"key":"36","name":"physicalItemCount","type":"string"},{"key":"37","name":"purchaseProductType","type":"string"},{"key":"38","name":"is_fraud","type":"string"},{"key":"39","name":"timestamp","type":"string"}],"truncated":false},"isSummary":false,"language":"scala"},"persist_state":{"view":{"type":"details","tableOptions":{},"chartOptions":{"chartType":"bar","categoryFieldKeys":["0"],"seriesFieldKeys":["2"],"aggregationType":"count","isStacked":false}}}},"ec65117b-abbf-43c9-9ef9-b89d6fb644fd":{"type":"Synapse.DataFrame","sync_state":{"table":{"rows":[{"0":"A1055520426191820","1":"2023-01-01 19:15:53","2":"1","3":"133.28","4":"133.28","5":"1","6":"133.28","7":"133.28","index":1},{"0":"A1055520426191820","1":"2023-01-12 00:04:10","2":"1","3":"543.66","4":"543.66","5":"1","6":"543.66","7":"543.66","index":2},{"0":"A1055520426719380","1":"2023-04-17 21:42:09","2":"1","3":"217.09","4":"217.09","5":"1","6":"217.09","7":"217.09","index":3},{"0":"A1055520426806680","1":"2023-04-08 14:43:09","2":"1","3":"923.98","4":"923.98","5":"1","6":"923.98","7":"923.98","index":4},{"0":"A1055520426936410","1":"2023-03-27 03:40:54","2":"1","3":"139.99","4":"139.99","5":"1","6":"139.99","7":"139.99","index":5}],"schema":[{"key":"0","name":"accountID","type":"string"},{"key":"1","name":"timestamp","type":"timestamp"},{"key":"2","name":"transaction_3d_count","type":"bigint"},{"key":"3","name":"transaction_amount_3d_sum","type":"double"},{"key":"4","name":"transaction_amount_3d_avg","type":"double"},{"key":"5","name":"transaction_7d_count","type":"bigint"},{"key":"6","name":"transaction_amount_7d_sum","type":"double"},{"key":"7","name":"transaction_amount_7d_avg","type":"double"}],"truncated":false},"isSummary":false,"language":"scala"},"persist_state":{"view":{"type":"details","tableOptions":{},"chartOptions":{"chartType":"bar","categoryFieldKeys":["0"],"seriesFieldKeys":["3"],"aggregationType":"sum","isStacked":false}}}}}}},"nbformat":4,"nbformat_minor":2}
\ No newline at end of file
+{
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Tutorial #1: Develop a feature set and register with managed feature store"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "Azure ML managed feature store lets you discover, create and operationalize features. Features are the connective tissue in ML lifecycle, starting from prototyping where you experiment with various features to operationalization where models are deployed and feature data is looked up during inference. For information on basics concept of feature store, see [feature store concepts](fs-concepts).\n",
+ "\n",
+ "In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle.\n",
+ "\n",
+ "This tutorial is the first part of a three part series. In this tutorial you will:\n",
+ "- Create a new minimal feature store resource\n",
+ "- Develop and test feature set locally with feature transformation capability\n",
+ "- Register a feature store entity with the feature store\n",
+ "- Register the feature set that you developed with the feature store\n",
+ "- Generate sample training data dataframe using the features you created\n",
+ "- Enable offline materialization on the feature sets, and backfill the feature data\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Prerequisites\n",
+ "\n",
+ "> [!NOTE]\n",
+ "> This tutorial uses Azure Machine Learning notebook with **Serverless Spark Compute**.\n",
+ "\n",
+ "Before following the steps in this article, make sure you have the following prerequisites:\n",
+ "\n",
+ "* An Azure Machine Learning workspace. If you don't have one, use the steps in the [Quickstart: Create workspace resources](https://learn.microsoft.com/en-us/azure/machine-learning/quickstart-create-resources?view=azureml-api-2) article to create one.\n",
+ "* To perform the steps in this article, your user account must be assigned the owner or contributor role to a resource group where the feature store will be created"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Set up\n",
+ "\n",
+ "This tutorial uses the Python feature store core SDK (`azureml-featurestore`). The Python SDK is used for create, read, update, and delete (CRUD) operations, on feature stores, feature sets, and feature store entities.\n",
+ "\n",
+ "You don't need to explicitly install these resources for this tutorial, because in the set-up instructions shown here, the `conda.yml` file covers them.\n",
+ "\n",
+ "To prepare the notebook environment for development:\n",
+ "\n",
+ "1. Clone the [azureml-examples](https://github.com/azure/azureml-examples) repository to your local GitHub resources with this command:\n",
+ "\n",
+ " `git clone --depth 1 https://github.com/Azure/azureml-examples`\n",
+ "\n",
+ " You can also download a zip file from the [azureml-examples](https://github.com/azure/azureml-examples) repository. At this page, first select the `code` dropdown, and then select `Download ZIP`. Then, unzip the contents into a folder on your local device.\n",
+ "\n",
+ "1. Upload the feature store samples directory to the project workspace\n",
+ "\n",
+ " 1. In the Azure Machine Learning workspace, open the Azure Machine Learning studio UI.\n",
+ " 1. Select **Notebooks** in left navigation panel.\n",
+ " 1. Select your user name in the directory listing.\n",
+ " 1. Select ellipses (**...**) and then select **Upload folder**.\n",
+ " 1. Select the feature store samples folder from the cloned directory path: `azureml-examples/sdk/python/featurestore-sample`.\n",
+ "\n",
+ "1. Run the tutorial\n",
+ "\n",
+ " * Option 1: Create a new notebook, and execute the instructions in this document, step by step.\n",
+ " * Option 2: Open existing notebook `featurestore_sample/notebooks/sdk_only/1. Develop a feature set and register with managed feature store.ipynb`. You may keep this document open and refer to it for more explanation and documentation links.\n",
+ "\n",
+ " 1. Select **Serverless Spark Compute** in the top navigation **Compute** dropdown. This operation might take one to two minutes. Wait for a status bar in the top to display **Configure session**.\n",
+ " 1. Select **Configure session** in the top status bar.\n",
+ " 1. Select **Python packages**.\n",
+ " 1. Select **Upload conda file**.\n",
+ " 1. Select file `azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml` located on your local device.\n",
+ " 1. (Optional) Increase the session time-out (idle time in minutes) to reduce the serverless spark cluster startup time.\n",
+ "\n",
+ "__Important:__ Except for this step, you need to run all the other steps every time you have a new spark session/session time out.\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Start spark session\n",
+ "Execute the following code cell to start the Spark session. It wil take approximately 10 minutes to install all dependencies and start the Spark session."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545108574
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "start-spark-session",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "# Run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
+ "print(\"start spark session\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Setup root directory for the samples\n",
+ "This code cell sets up the root directory for the samples."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545283706
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "root-dir",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# Please update below (or any custom directory you uploaded the samples to).\n",
+ "# You can find the name from the directory structure in the left navigation panel.\n",
+ "root_dir = \"./Users//featurestore_sample\"\n",
+ "\n",
+ "if os.path.isdir(root_dir):\n",
+ " print(\"The folder exists.\")\n",
+ "else:\n",
+ " print(\"The folder does not exist. Please create or fix the path\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Note\n",
+ "Feature store Vs Project workspace: You will use a feature store to reuse features across projects. You will use a project workspace (the current workspace) to train and inference models, by leveraging features from feature stores. Many project workspaces can share and reuse the same feature store.\n",
+ "\n",
+ "## Note\n",
+ "In this tutorial you will be using two SDKs:\n",
+ "\n",
+ "1. Feature store CRUD SDK: You will use the same SDK, MLClient (package name `azure-ai-ml`), that you use with Azure ML workspace. This will be used for feature store CRUD operations (create, read, update, and delete) for feature store, feature set and feature store entities. This is because feature store is implemented as a type of workspace. \n",
+ "2. Feature store core SDK: This SDK (`azureml-featurestore`) is meant to be used for feature set development and consumption (you will learn more about these operations later):\n",
+ "- List/Get registered feature set\n",
+ "- Generate/resolve feature retrieval spec\n",
+ "- Execute feature set definition to generate Spark dataframe\n",
+ "- Generate training data using a point-in-time join\n",
+ "\n",
+ "For this tutorial, you do not need to install any of these explicitly, since the instructions already cover them (`conda.yml` in the above step include these)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 1: Create a minimal feature store"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 1a: Set feature store parameters\n",
+ "Set name, location and other values for the feature store."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545370071
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "fs-params",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# We use the subscription, resource group, region of this active project workspace.\n",
+ "# You can optionally replace them to create the resources in a different subsciprtion/resource group, or use existing resources.\n",
+ "import os\n",
+ "\n",
+ "featurestore_name = \"\"\n",
+ "featurestore_location = \"eastus\"\n",
+ "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 1b: Create the feature store"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545478258
+ },
+ "jupyter": {
+ "outputs_hidden": true,
+ "source_hidden": false
+ },
+ "name": "create-fs",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.entities import (\n",
+ " FeatureStore,\n",
+ " FeatureStoreEntity,\n",
+ " FeatureSet,\n",
+ ")\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "ml_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "fs = FeatureStore(name=featurestore_name, location=featurestore_location)\n",
+ "# wait for feature store creation\n",
+ "fs_poller = ml_client.feature_stores.begin_create(fs)\n",
+ "print(fs_poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 1c: Initialize AzureML feature store core SDK client\n",
+ "As explained above, this is used to develop and consume features."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545558268
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-fs-core-sdk",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# feature store client\n",
+ "from azureml.featurestore import FeatureStoreClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "featurestore = FeatureStoreClient(\n",
+ " credential=AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ " name=featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 2: Prototype and develop a transaction rolling aggregation feature set in this notebook"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2a: Explore the transactions source data\n",
+ "\n",
+ "#### Note\n",
+ "The sample data used in this notebook is hosted in a public accessible blob container. It can only be read in Spark via `wasbs` driver. When you create feature sets using your own source data, please host them in ADLS Gen2 account and use `abfss` driver in the data path. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545597487
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "explore-txn-src-data",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# remove the \".\" in the roor directory path as we need to generate absolute path to read from spark\n",
+ "transactions_source_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/transactions-source/*.parquet\"\n",
+ "transactions_src_df = spark.read.parquet(transactions_source_data_path)\n",
+ "\n",
+ "display(transactions_src_df.head(5))\n",
+ "# Note: display(training_df.head(5)) displays the timestamp column in a different format. You can can call transactions_src_df.show() to see correctly formatted value"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2b: Develop a transactions feature set locally\n",
+ "\n",
+ "Feature set specification is a self-contained definition of feature set that can be developed and tested locally.\n",
+ "\n",
+ "Lets create the following rolling window aggregate features:\n",
+ "- transactions 3-day count\n",
+ "- transactions amount 3-day sum\n",
+ "- transactions amount 3-day avg\n",
+ "- transactions 7-day count\n",
+ "- transactions amount 7-day sum\n",
+ "- transactions amount 7-day avg\n",
+ "\n",
+ "__Action__:\n",
+ "- Inspect the feature transformation code file: `featurestore/featuresets/transactions/spec/transformation_code/transaction_transform.py`. You will see how is the rolling aggregation defined for the features. This is a Spark transformer.\n",
+ "\n",
+ "To understand the feature set and transformations in more detail, see [feature store concepts](https://learn.microsoft.com/azure/machine-learning/concept-what-is-managed-feature-store)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545617083
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "develop-txn-fset-locally",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import create_feature_set_spec, FeatureSetSpec\n",
+ "from azureml.featurestore.contracts import (\n",
+ " DateTimeOffset,\n",
+ " FeatureSource,\n",
+ " TransformationCode,\n",
+ " Column,\n",
+ " ColumnType,\n",
+ " SourceType,\n",
+ " TimestampColumn,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "transactions_featureset_code_path = (\n",
+ " root_dir + \"/featurestore/featuresets/transactions/transformation_code\"\n",
+ ")\n",
+ "\n",
+ "transactions_featureset_spec = create_feature_set_spec(\n",
+ " source=FeatureSource(\n",
+ " type=SourceType.parquet,\n",
+ " path=\"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/transactions-source/*.parquet\",\n",
+ " timestamp_column=TimestampColumn(name=\"timestamp\"),\n",
+ " source_delay=DateTimeOffset(days=0, hours=0, minutes=20),\n",
+ " ),\n",
+ " transformation_code=TransformationCode(\n",
+ " path=transactions_featureset_code_path,\n",
+ " transformer_class=\"transaction_transform.TransactionFeatureTransformer\",\n",
+ " ),\n",
+ " index_columns=[Column(name=\"accountID\", type=ColumnType.string)],\n",
+ " source_lookback=DateTimeOffset(days=7, hours=0, minutes=0),\n",
+ " temporal_join_lookback=DateTimeOffset(days=1, hours=0, minutes=0),\n",
+ " infer_schema=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Generate a Spark dataframe from the feature set specification\n",
+ "transactions_fset_df = transactions_featureset_spec.to_spark_dataframe()\n",
+ "# Display few records\n",
+ "display(transactions_fset_df.head(5))"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2c: Export as feature set specification\n",
+ "In order to register the feature set specification with the feature store, it needs to be saved in a specific format. \n",
+ "Action: Please inspect the generated `transactions` FeaturesetSpec: Open this file from the file tree to see the specification: `featurestore/featuresets/accounts/spec/FeaturesetSpec.yaml`\n",
+ "\n",
+ "specification contains these important elements:\n",
+ "\n",
+ "1. `source`: Reference to a storage. In this case a parquet file in a blob storage.\n",
+ "2. `features`: List of features and their datatypes. If you provide transformation code, the code has to return a dataframe that maps to the features and data types.\n",
+ "3. `index_columns`: The join keys required to access values from the feature set\n",
+ "\n",
+ "Learn more about it in the [top level feature store entities document](https://learn.microsoft.com/azure/machine-learning/concept-top-level-entities-in-managed-feature-store) and the [feature set specification YAML reference](https://learn.microsoft.com/azure/machine-learning/reference-yaml-featureset-spec).\n",
+ "\n",
+ "The additional benefit of persisting the feature set specification is that it can be source controlled."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545629168
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "dump-transactions-fs-spec",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# Create a new folder to dump the feature set specification.\n",
+ "transactions_featureset_spec_folder = (\n",
+ " root_dir + \"/featurestore/featuresets/transactions/spec\"\n",
+ ")\n",
+ "\n",
+ "# Check if the folder exists, create one if it does not exist.\n",
+ "if not os.path.exists(transactions_featureset_spec_folder):\n",
+ " os.makedirs(transactions_featureset_spec_folder)\n",
+ "\n",
+ "transactions_featureset_spec.dump(transactions_featureset_spec_folder, overwrite=True)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 3: Register a feature store entity\n",
+ "Entity helps enforce best practice that same join key definitions are used across featuresets which uses the same logical entities. Examples of entities are account entity, customer entity etc. Entities are typically created once and reused across feature sets. For information on basics concept of feature store, see [feature store concepts](https://learn.microsoft.com/azure/machine-learning/concept-what-is-managed-feature-store)."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 3a: Initialize the feature store CRUD client\n",
+ "\n",
+ "As explained in the beginning of this tutorial, `MLClient` is used for CRUD of assets in feature store. The below code looks up the feature store we created in an earlier step. We cannot reuse the same `ml_client` used above here because the former is scoped at the resource group level, which is a prerequisite for creation of feature store. The below one is scoped at feature store level.\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545635654
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-fset-crud-client",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# MLClient for feature store.\n",
+ "fs_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " featurestore_subscription_id,\n",
+ " featurestore_resource_group_name,\n",
+ " featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 3b: Register `account` entity with the feature store\n",
+ "Create account entity that has join key `accountID` of `string` type. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545646811
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "register-acct-entity",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import DataColumn, DataColumnType\n",
+ "\n",
+ "account_entity_config = FeatureStoreEntity(\n",
+ " name=\"account\",\n",
+ " version=\"1\",\n",
+ " index_columns=[DataColumn(name=\"accountID\", type=DataColumnType.STRING)],\n",
+ " stage=\"Development\",\n",
+ " description=\"This entity represents user account index key accountID.\",\n",
+ " tags={\"data_typ\": \"nonPII\"},\n",
+ ")\n",
+ "\n",
+ "poller = fs_client.feature_store_entities.begin_create_or_update(account_entity_config)\n",
+ "print(poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 4: Register the transaction feature set with the feature store\n",
+ "You register a feature set asset with the feature store so that you can share and reuse with others. You also get managed capabilities like versioning and materialization (we will learn in this tutorial series).\n",
+ "\n",
+ "The feature set asset has reference to the feature set spec that you created earlier and additional properties like version and materialization settings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696545663104
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "register-txn-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import FeatureSetSpecification\n",
+ "\n",
+ "transaction_fset_config = FeatureSet(\n",
+ " name=\"transactions\",\n",
+ " version=\"1\",\n",
+ " description=\"7-day and 3-day rolling aggregation of transactions featureset\",\n",
+ " entities=[f\"azureml:account:1\"],\n",
+ " stage=\"Development\",\n",
+ " specification=FeatureSetSpecification(path=transactions_featureset_spec_folder),\n",
+ " tags={\"data_type\": \"nonPII\"},\n",
+ ")\n",
+ "\n",
+ "poller = fs_client.feature_sets.begin_create_or_update(transaction_fset_config)\n",
+ "print(poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Explore the feature store UI\n",
+ "* Goto the [Azure ML global landing page](https://ml.azure.com/home).\n",
+ "* Click on **Feature stores** in the left navigation.\n",
+ "* You will see the list of feature stores that you have access to. Click on the feature store that you have created above.\n",
+ "\n",
+ "You can see the feature sets and entities that you have created.\n",
+ "\n",
+ "Note: Creating and updating feature store assets (feature sets and entities) is possible only through SDK and CLI. You can use the UI to search/browse the feature store."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Grant \"Storage Blob Data Reader\" role on the offline store to your user identity\n",
+ "If feature data is materialized, then you need this role to read feature data from offline materialization store.\n",
+ "- Get your AAD object id from Azure portal following this instruction: https://learn.microsoft.com/en-us/partner-center/find-ids-and-domain-names#find-the-user-object-id\\\n",
+ "- Get information about the offline materialization store from the Feature Store **Overview** page in the Feature Store UI. The storage account subscription ID, storage account resource group name, and storage account name for offline materialization store can be found on **Offline materialization store** card. \n",
+ "![OFFLINE_STORE_INFO](./images/offline-store-information.png) \n",
+ "\n",
+ "To learn more about access control, see access control document in the docs.\n",
+ "\n",
+ "Execute the following code cell for role assignment. Please note that it may take some time for permissions to propagate. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "grant-rbac-to-user-identity",
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# This utility function is created for ease of use in the docs tutorials. It uses standard azure API's.\n",
+ "# You can optionally inspect it `featurestore/setup/setup_storage_uai.py`.\n",
+ "import sys\n",
+ "\n",
+ "sys.path.insert(0, root_dir + \"/featurestore/setup\")\n",
+ "from setup_storage_uai import grant_user_aad_storage_data_reader_role\n",
+ "\n",
+ "your_aad_objectid = \"\"\n",
+ "storage_subscription_id = \"\"\n",
+ "storage_resource_group_name = \"\"\n",
+ "storage_account_name = \"\"\n",
+ "\n",
+ "grant_user_aad_storage_data_reader_role(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " your_aad_objectid,\n",
+ " storage_subscription_id,\n",
+ " storage_resource_group_name,\n",
+ " storage_account_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 5: Generate a training data dataframe using the registered features"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 5a: Load observation data\n",
+ "\n",
+ "We start by exploring the observation data. Observation data is typically the core data used in training and inference data. This is then joined with feature data to create the full training data. Observation data is the data captured during the time of the event: in this case it has core transaction data including transaction ID, account ID, transaction amount. In this case, since it is for training, it also has the target variable appended (`is_fraud`).\n",
+ "\n",
+ "To learn more core concepts including observation data, refer to the feature store documentation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1683417449378
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "load-obs-data",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "observation_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/observation_data/train/*.parquet\"\n",
+ "observation_data_df = spark.read.parquet(observation_data_path)\n",
+ "obs_data_timestamp_column = \"timestamp\"\n",
+ "\n",
+ "display(observation_data_df)\n",
+ "# Note: the timestamp column is displayed in a different format. Optionally, you can can call training_df.show() to see correctly formatted value"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 5b: Get the registered feature set and list its features"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1683416499081
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "get-txn-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Look up the featureset by providing a name and a version.\n",
+ "transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
+ "# List its features.\n",
+ "transactions_featureset.features"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1683416508419
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "print-txn-fset-sample-values",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Print sample values.\n",
+ "display(transactions_featureset.to_spark_dataframe().head(5))"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 5c: Select features and generate training data\n",
+ "In this step we will select features that we would like to be part of training data and use the feature store SDK to generate the training data."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1683417499373
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "select-features-and-gen-training-data",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import get_offline_features\n",
+ "\n",
+ "# You can select features in pythonic way.\n",
+ "features = [\n",
+ " transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n",
+ " transactions_featureset.get_feature(\"transaction_amount_7d_avg\"),\n",
+ "]\n",
+ "\n",
+ "# You can also specify features in string form: featureset:version:feature.\n",
+ "more_features = [\n",
+ " f\"transactions:1:transaction_3d_count\",\n",
+ " f\"transactions:1:transaction_amount_3d_avg\",\n",
+ "]\n",
+ "\n",
+ "more_features = featurestore.resolve_feature_uri(more_features)\n",
+ "features.extend(more_features)\n",
+ "\n",
+ "# Generate training dataframe by using feature data and observation data.\n",
+ "training_df = get_offline_features(\n",
+ " features=features,\n",
+ " observation_data=observation_data_df,\n",
+ " timestamp_column=obs_data_timestamp_column,\n",
+ ")\n",
+ "\n",
+ "# Ignore the message that says feature set is not materialized (materialization is optional). We will enable materialization in the subsequent part of the tutorial.\n",
+ "display(training_df)\n",
+ "# Note: the timestamp column is displayed in a different format. Optionally, you can can call training_df.show() to see correctly formatted value"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "You can see how the features are appended to the training data using a point-in-time join."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 6: Enable offline materialization on transactions feature set\n",
+ "Once materialization is enabled on a feature set, you can perform backfill (this tutorial) or schedule recurrent materialization jobs (shown in a later tutorial)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "enable-offline-mat-txns-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import (\n",
+ " MaterializationSettings,\n",
+ " MaterializationComputeResource,\n",
+ ")\n",
+ "\n",
+ "transactions_fset_config = fs_client._featuresets.get(name=\"transactions\", version=\"1\")\n",
+ "\n",
+ "transactions_fset_config.materialization_settings = MaterializationSettings(\n",
+ " offline_enabled=True,\n",
+ " resource=MaterializationComputeResource(instance_type=\"standard_e8s_v3\"),\n",
+ " spark_configuration={\n",
+ " \"spark.driver.cores\": 4,\n",
+ " \"spark.driver.memory\": \"36g\",\n",
+ " \"spark.executor.cores\": 4,\n",
+ " \"spark.executor.memory\": \"36g\",\n",
+ " \"spark.executor.instances\": 2,\n",
+ " },\n",
+ " schedule=None,\n",
+ ")\n",
+ "\n",
+ "fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n",
+ "print(fs_poller.result())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "Optionally, you can save the the above feature set asset as YAML."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "dump-txn-fset-yaml",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "## uncomment to run\n",
+ "transactions_fset_config.dump(\n",
+ " root_dir\n",
+ " + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled.yaml\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 7: Backfill data for transactions feature set\n",
+ "Materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. Materializing the features will increase its reliability and availability. All feature queries will use the materialized values from the materialization store. In this step you perform a one-time backfill for a feature window of __18 months__.\n",
+ "\n",
+ "#### Note\n",
+ "How to determine the window of backfill data needed? It has to match with the window of your training data. For e.g. if you want to train with two years of data, then you will want to be able to retrieve features for the same window, so you will backfill for a two year window."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "backfill-txns-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "\n",
+ "st = datetime(2022, 1, 1, 0, 0, 0, 0)\n",
+ "ed = datetime(2023, 6, 30, 0, 0, 0, 0)\n",
+ "\n",
+ "poller = fs_client.feature_sets.begin_backfill(\n",
+ " name=\"transactions\",\n",
+ " version=\"1\",\n",
+ " feature_window_start_time=st,\n",
+ " feature_window_end_time=ed,\n",
+ " # data_status=[\"None\", \"Incomplete\"],\n",
+ ")\n",
+ "# print(poller.result().job_ids)\n",
+ "print(poller.result().job_id)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "stream-mat-job-logs",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Get the job URL, and stream the job logs.\n",
+ "# fs_client.jobs.stream(poller.result().job_ids[0])\n",
+ "fs_client.jobs.stream(poller.result().job_id)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "Let's print sample data from the feature set. You can notice from the output information that the data was retrieved from the materilization store. `get_offline_features()` method that is used to retrieve training/inference data will also use the materialization store by default."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "sample-txns-fset-data",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Look up the feature set by providing a name and a version and display few records.\n",
+ "transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
+ "display(transactions_featureset.to_spark_dataframe().head(5))"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Cleanup\n",
+ "\n",
+ "Tutorial `5. Develop a feature set with custom source` has instructions for deleting the resources."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Next steps\n",
+ "* Experiment and train models using features."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Edit Metadata",
+ "kernel_info": {
+ "name": "synapse_pyspark"
+ },
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.13"
+ },
+ "microsoft": {
+ "host": {
+ "AzureML": {
+ "notebookHasBeenCompleted": true
+ }
+ },
+ "ms_spell_check": {
+ "ms_spell_check_language": "en"
+ }
+ },
+ "nteract": {
+ "version": "nteract-front-end@1.0.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/2. Experiment and train models using features.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_only/2. Experiment and train models using features.ipynb
index a00d589628..25da6aa832 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_only/2. Experiment and train models using features.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_only/2. Experiment and train models using features.ipynb
@@ -1 +1,1221 @@
-{"cells":[{"attachments":{},"cell_type":"markdown","source":["# Tutorial: Experiment and train models using features"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing.\n","\n","In part 1 of the tutorial you learnt how to create a feature set spec with custom transformations, enable materialization and perform backfill. In this tutorial you will will learn how to experiment with features to improve model performance. You will see how feature store increasing agility in the experimentation and training flows. \n","\n","You will perform the following:\n","- Prototype a create new `acccounts` feature set spec using existing precomputed values as features, unlike part 1 of the tutorial where we created feature set that had custom transformations. You will then Register the local feature set spec as a feature set in the feature store\n","- Select features for the model: You will select features from the `transactions` and `accounts` feature sets and save them as a feature-retrieval spec\n","- Run training pipeline that uses the Feature retrieval spec to train a new model. This pipeline will use the built in feature-retrieval component to generate the training data"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Important\n","\n","This feature is currently in public preview. This preview version is provided without a service-level agreement, and it's not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["# Prerequisites\n","1. Please ensure you have executed part 1 of the tutorial"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["# Setup"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Configure Azure ML spark notebook\n","\n","1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n","1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n","\n","\n"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Start spark session"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n","print(\"start spark session\")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550147448},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"start-spark-session","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Setup root directory for the samples"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["import os\n","\n","# please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n","# You can find the name from the directory structure in the left nav\n","root_dir = \"./Users//featurestore_sample\"\n","\n","if os.path.isdir(root_dir):\n"," print(\"The folder exists.\")\n","else:\n"," print(\"The folder does not exist. Please create or fix the path\")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550160587},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"root-dir","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Initialize the project workspace CRUD client\n","This is the current workspace where you will be running the tutorial notebook from"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["### Initialize the MLClient of this project workspace\n","import os\n","from azure.ai.ml import MLClient\n","from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n","\n","project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n","project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n","project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n","version = \"\"\n","\n","# connect to the project workspace\n","ws_client = MLClient(\n"," AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550177764},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"init-ws-crud-client","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Initialize the feature store CRUD client\n","Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml import MLClient\n","from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n","\n","# feature store\n","featurestore_name = \"my-featurestore\" # use the same name from part #1 of the tutorial\n","featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n","featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n","\n","# feature store ml client\n","fs_client = MLClient(\n"," AzureMLOnBehalfOfCredential(),\n"," featurestore_subscription_id,\n"," featurestore_resource_group_name,\n"," featurestore_name,\n",")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550201549},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"init-fs-crud-client","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Initialize the feature store core sdk client"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# feature store client\n","from azureml.featurestore import FeatureStoreClient\n","from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n","\n","featurestore = FeatureStoreClient(\n"," credential=AzureMLOnBehalfOfCredential(),\n"," subscription_id=featurestore_subscription_id,\n"," resource_group_name=featurestore_resource_group_name,\n"," name=featurestore_name,\n",")"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550214535},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"init-fs-core-sdk","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Create compute cluster with name `cpu-cluster-fs` in the project workspace\n","This will be needed when we run training/batch inference jobs"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml.entities import AmlCompute\n","\n","cluster_basic = AmlCompute(\n"," name=\"cpu-cluster-fs\",\n"," type=\"amlcompute\",\n"," size=\"STANDARD_F4S_V2\", # you can replace it with other supported VM SKUs\n"," location=ws_client.workspaces.get(ws_client.workspace_name).location,\n"," min_instances=0,\n"," max_instances=1,\n"," idle_time_before_scale_down=360,\n",")\n","ws_client.begin_create_or_update(cluster_basic).result()"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550257007},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"create-compute-cluster","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 1: Create accounts featureset locally from precomputed data\n","In tutorial part 1, we created a transactions featureset that had custom transformations. Now we will create an accounts featureset that will use precomputed values. \n","\n","For onboarding precomputed features, you can create a featureset spec without writing any transformation code. Featureset spec is a specification to develop and test a featureset in a fully local/dev environment without connecting to any featurestore. In this step you will create the feature set spec locally and sample the values from it. If you want to get managed featurestore capabilities, you need to register the featureset spec with a feature store using a feature asset definition (a future step in this tutorial)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 1a: Explore the source data for accounts\n","\n","##### Note\n"," Note that the sample data used in this notebook is hosted in a public accessible blob container. It can only be read in Spark via `wasbs` driver. When you create feature sets using your own source data, please host them in adls gen2 account and use `abfss` driver in the data path. "],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["accounts_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/accounts-precalculated/*.parquet\"\n","accounts_df = spark.read.parquet(accounts_data_path)\n","\n","display(accounts_df.head(5))"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550281756},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"explore-accts-fset-src-data","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 1b: Create `accounts` feature set spec in local from these precomputed features\n","Note that we do not need any transformation code here since we are referencing precomputed features."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azureml.featurestore import create_feature_set_spec, FeatureSetSpec\n","from azureml.featurestore.contracts import (\n"," DateTimeOffset,\n"," FeatureSource,\n"," TransformationCode,\n"," Column,\n"," ColumnType,\n"," SourceType,\n"," TimestampColumn,\n",")\n","\n","\n","accounts_featureset_spec = create_feature_set_spec(\n"," source=FeatureSource(\n"," type=SourceType.parquet,\n"," path=\"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/accounts-precalculated/*.parquet\",\n"," timestamp_column=TimestampColumn(name=\"timestamp\"),\n"," ),\n"," index_columns=[Column(name=\"accountID\", type=ColumnType.string)],\n"," # account profiles in the source are updated once a year. set temporal_join_lookback to 365 days\n"," temporal_join_lookback=DateTimeOffset(days=365, hours=0, minutes=0),\n"," infer_schema=True,\n",")\n","# Generate a spark dataframe from the feature set specification\n","accounts_fset_df = accounts_featureset_spec.to_spark_dataframe()\n","# display few records\n","display(accounts_fset_df.head(5))"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550310950},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"create-accts-fset-spec","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 1c: Export as feature set spec\n","In order to register the feature set spec with the feature store, it needs to be saved in a specific format. \n","Action: After running the below cell, please inspect the generated `accounts` FeatureSetSpec: Open this file from the file tree to see the spec: `featurestore/featuresets/accounts/spec/FeatureSetSpec.yaml`\n","\n","Spec contains these important elements:\n","\n","1. `source`: reference to a storage. In this case a parquet file in a blob storage.\n","1. `features`: list of features and their datatypes. If you provide transformation code (see Day 2 section), the code has to return a dataframe that maps to the features and datatypes. In case where you do not provide transformation code (in this case of `accounts` because it is precomputed), the system will build the query to to map these to the source \n","1. `index_columns`: the join keys required to access values from the feature set\n","\n","Learn more about it in the [top level feature store entities document](fs-concepts-todo) and the [feature set spec yaml reference](reference-yaml-featureset-spec.md).\n","\n","The additional benefit of persisting it is that it can be source controlled."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["import os\n","\n","# create a new folder to dump the feature set spec\n","accounts_featureset_spec_folder = root_dir + \"/featurestore/featuresets/accounts/spec\"\n","\n","# check if the folder exists, create one if not\n","if not os.path.exists(accounts_featureset_spec_folder):\n"," os.makedirs(accounts_featureset_spec_folder)\n","\n","accounts_featureset_spec.dump(accounts_featureset_spec_folder, overwrite=True)"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550317763},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"dump-accts-fset-spec","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 2: Experiment with unregistered features locally and register with feature store when ready\n","When you are developing features, you might want to test/validate locally before registering with the feature store or running training pipelines in the cloud. In this step you will generate training data for the ML model from combination of features from a local unregistered feature set (`accounts`) and feature set registered in the feature store (`transactions`)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2a: Select features for model"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# get the registered transactions feature set, version 1\n","transactions_featureset = featurestore.feature_sets.get(\"transactions\", version)\n","# Notice that account feature set spec is in your local dev environment (this notebook): not registered with feature store yet\n","features = [\n"," accounts_featureset_spec.get_feature(\"accountAge\"),\n"," accounts_featureset_spec.get_feature(\"numPaymentRejects1dPerUser\"),\n"," transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n"," transactions_featureset.get_feature(\"transaction_amount_3d_sum\"),\n"," transactions_featureset.get_feature(\"transaction_amount_7d_avg\"),\n","]"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550328371},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"select-unreg-features-for-model","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2b: Generate training data locally\n","In this step we generate training data for illustrative purpose. You can optionally train models locally with this. In the upcoming steps in this tutorial, you will train a model in the cloud."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azureml.featurestore import get_offline_features\n","\n","# Load the observation data. To understand observatio ndata, refer to part 1 of this tutorial\n","observation_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/observation_data/train/*.parquet\"\n","observation_data_df = spark.read.parquet(observation_data_path)\n","obs_data_timestamp_column = \"timestamp\"\n","\n","# generate training dataframe by using feature data and observation data\n","training_df = get_offline_features(\n"," features=features,\n"," observation_data=observation_data_df,\n"," timestamp_column=obs_data_timestamp_column,\n",")\n","\n","# Ignore the message that says feature set is not materialized (materialization is optional). We will enable materialization in the next part of the tutorial.\n","display(training_df)\n","# Note: display(training_df.head(5)) displays the timestamp column in a different format. You can can call training_df.show() to see correctly formatted value"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550539714},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"gen-training-data-locally","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2c: Register the `accounts` featureset with the featurestore\n","Once you have experimented with different feature definitions locally and sanity tested it, you can register it with the feature store.\n","For this you will register a featureset asset definition with the feature store.\n"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml.entities import FeatureSet, FeatureSetSpecification\n","\n","accounts_fset_config = FeatureSet(\n"," name=\"accounts\",\n"," version=version,\n"," description=\"accounts featureset\",\n"," entities=[f\"azureml:account:{version}\"],\n"," stage=\"Development\",\n"," specification=FeatureSetSpecification(path=accounts_featureset_spec_folder),\n"," tags={\"data_type\": \"nonPII\"},\n",")\n","\n","poller = fs_client.feature_sets.begin_create_or_update(accounts_fset_config)\n","print(poller.result())"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550813095},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"reg-accts-fset","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 2d: Get registered featureset and sanity test"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# look up the featureset by providing name and version\n","accounts_featureset = featurestore.feature_sets.get(\"accounts\", version)\n","# get access to the feature data\n","accounts_feature_df = accounts_featureset.to_spark_dataframe()\n","display(accounts_feature_df.head(5))\n","# Note: Please ignore this warning: Failure while loading azureml_run_type_providers. Failed to load entrypoint azureml.scriptrun"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550826621},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"sample-accts-fset-data","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 3: Run training experiment\n","In this step you will select a list of features, run a training pipeline, and register the model. You can repeat this step till you are happy with the model performance."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### (Optional) Step 3a: Discover features from Feature Store UI\n","You have already done this in part 1 of the tutorial after registering the `transactions` feature set. Since you also have `accounts` featureset, you can browse the available features:\n","* Goto the [Azure ML global landing page](https://ml.azure.com/home?flight=FeatureStores).\n","* Click on `Feature stores` in the left nav\n","* You will see the list of feature stores that you have access to. Click on the feature store that you created above.\n","\n","You can see the feature sets and entity that you created. Click on the feature sets to browse the feature definitions. You can also search for feature sets across feature stores by using the global search box."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### (Optional) Step 3b: Discover features from SDK"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# List available feature sets\n","all_featuresets = featurestore.feature_sets.list()\n","for fs in all_featuresets:\n"," print(fs)\n","\n","# List of versions for transactions feature set\n","all_transactions_featureset_versions = featurestore.feature_sets.list(\n"," name=\"transactions\"\n",")\n","for fs in all_transactions_featureset_versions:\n"," print(fs)\n","\n","# See properties of the transactions featureset including list of features\n","featurestore.feature_sets.get(name=\"transactions\", version=version).features"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550851764},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"discover-features-from-sdk","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 3c: Select features for the model and export it as a feature-retrieval spec\n","In the previous steps, you selected features from a combination unregistered and registered feature sets for local experimentation and testing. Now you are ready to experiment in the cloud. Saving the selected features as a feature-retrieval spec and using it in the mlops/cicd flow for training/inference increases your agility in shipping models."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["Select features for the model"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# you can select features in pythonic way\n","features = [\n"," accounts_featureset.get_feature(\"accountAge\"),\n"," transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n"," transactions_featureset.get_feature(\"transaction_amount_3d_sum\"),\n","]\n","\n","# you can also specify features in string form: featurestore:featureset:version:feature\n","more_features = [\n"," f\"accounts:{version}:numPaymentRejects1dPerUser\",\n"," f\"transactions:{version}:transaction_amount_7d_avg\",\n","]\n","\n","more_features = featurestore.resolve_feature_uri(more_features)\n","\n","features.extend(more_features)"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550863111},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"select-reg-features","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["Export selected features as a feature-retrieval spec\n","\n","#### Note\n","Feature retrieval spec is a portable definition of list of features associated with a model. This can help streamline ML model development and operationalizing.This will be an input to the training pipeline (used to generate the training data), then will be packaged along with the model, and will be used during inference to lookup the features. It will be a glue that integrates all phases of the ML lifecycle. Changes to training/inference pipeline can be kept minimal as you experiment and deploy. \n","\n","Using feature retrieval spec and the built-in feature retrieval component is optional: you can directly use `get_offline_features()` api as shown above.\n","\n","Note that the name of the spec should be `feature_retrieval_spec.yaml` when it is packaged with the model for the system to recognize it."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["# Create feature retrieval spec\n","feature_retrieval_spec_folder = root_dir + \"/project/fraud_model/feature_retrieval_spec\"\n","\n","# check if the folder exists, create one if not\n","if not os.path.exists(feature_retrieval_spec_folder):\n"," os.makedirs(feature_retrieval_spec_folder)\n","\n","featurestore.generate_feature_retrieval_spec(feature_retrieval_spec_folder, features)"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1696550891771},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"export-as-frspec","nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 4: Train in the cloud using pipelines and register model if satisfactory\n","In this step you will manually trigger the training pipeline. In a production scenario, this could be triggered by a ci/cd pipeline based on changes to the feature-retrieval spec in the source repository."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 4a: Run the training pipeline\n","The training pipeline has the following steps:\n","\n","1. Feature retrieval step: This is a built-in component takes as input the feature retrieval spec, the observation data and timestamp column name. It then generates the training data as output. It runs this as a managed spark job.\n","1. Training step: This step trains the model based on the training data and generates a model (not registered yet)\n","1. Evaluation step: This step validates whether model performance/quailty is within threshold (in our case it is a placeholder/dummy step for illustration purpose)\n","1. Register model step: This step registers the model\n","\n","Note: In part 2 of this tutorial you ran a backfill job to materialize data for `transactions` feature set. Feature retrieval step will read feature values from offline store for this feature set. The behavior will same even if you use `get_offline_features()` api."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"code","source":["from azure.ai.ml import load_job # will be used later\n","\n","training_pipeline_path = (\n"," root_dir + \"/project/fraud_model/pipelines/training_pipeline.yaml\"\n",")\n","training_pipeline_definition = load_job(source=training_pipeline_path)\n","training_pipeline_job = ws_client.jobs.create_or_update(training_pipeline_definition)\n","ws_client.jobs.stream(training_pipeline_job.name)\n","# Note: First time it runs, each step in pipeline can take ~ 15 mins. However subsequent runs can be faster (assuming spark pool is warm - default timeout is 30 mins)"],"outputs":[],"execution_count":null,"metadata":{"gather":{"logged":1683429020656},"jupyter":{"outputs_hidden":false,"source_hidden":false},"name":"run-training-pipeline","nteract":{"transient":{"deleting":false}},"tags":["active-ipynb"]}},{"attachments":{},"cell_type":"markdown","source":["#### Inspect the training pipeline and the model\n","Open the above pipeline run \"web view\" in new window to see the steps in the pipeline.\n"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 4b: Notice the feature retrieval spec in the model artifacts\n","1. In the left nav of the current workspace -> right click on `Models` -> Open in new tab or window\n","1. Click on `fraud_model`\n","1. Click on `Artifacts` in the top nav\n","\n","You can notice that the feature retrieval spec is packaged along with the model. The model registration step in the training pipeline has done this. You created feature retrieval spec during experimentation, now it has become part of the model definition. In the next tutorial you will see how this will be used during inferencing.\n"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Step 5: View the feature set and model dependencies"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 5a: View the list of feature sets associated with the model\n","In the same models page, click on the `feature sets` tab. Here you can see both `transactions` and `accounts` featuresets that this model depends on."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["#### Step 5b: View the list of models using the feature sets\n","1. Open the feature store UI (expalined in a previous step in this tutorial)\n","1. Click on `Feature sets` on the left nav\n","1. Click on any of the feature set -> click on `Models` tab\n","\n","You can see the list of models that are using the feature sets (determined from the feature retrieval spec when the model was registered)."],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Cleanup\n","\n","Tutorial of \"Enable recurrent materialization and run batch inference\" has instructions deleting the resources"],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"cell_type":"markdown","source":[],"metadata":{"nteract":{"transient":{"deleting":false}}}},{"attachments":{},"cell_type":"markdown","source":["## Next steps\n","* Enable recurrent materialization and run batch inference"],"metadata":{"nteract":{"transient":{"deleting":false}}}}],"metadata":{"celltoolbar":"Edit Metadata","kernel_info":{"name":"synapse_pyspark"},"kernelspec":{"name":"synapse_pyspark","language":"Python","display_name":"Synapse PySpark"},"language_info":{"name":"python","version":"3.8.0","mimetype":"text/x-python","file_extension":".py","pygments_lexer":"ipython","codemirror_mode":"ipython","nbconvert_exporter":"python"},"microsoft":{"host":{"AzureML":{"notebookHasBeenCompleted":true}},"ms_spell_check":{"ms_spell_check_language":"en"}},"nteract":{"version":"nteract-front-end@1.0.0"},"synapse_widget":{"version":"0.1","state":{"1f11d9a6-6ce2-4ae1-bebf-2f25c69bb6d4":{"type":"Synapse.DataFrame","sync_state":{"table":{"rows":[{"0":"A1055520426185580","1":"GB","2":"true","3":"0.0","4":"2000.0","5":"2021-12-31 00:00:00","index":1},{"0":"A1055520426191820","1":"US","2":"true","3":"0.0","4":"1.0","5":"2021-12-31 00:00:00","index":2},{"0":"A1055520426549020","1":"AU","2":"true","3":"0.0","4":"1.0","5":"2021-12-31 00:00:00","index":3},{"0":"A1055520426719380","1":"US","2":"true","3":"0.0","4":"2000.0","5":"2021-12-31 00:00:00","index":4},{"0":"A1055520426806680","1":"GB","2":"true","3":"1.0","4":"2000.0","5":"2021-12-31 00:00:00","index":5}],"schema":[{"key":"0","name":"accountID","type":"string"},{"key":"1","name":"accountCountry","type":"string"},{"key":"2","name":"isUserRegistered","type":"boolean"},{"key":"3","name":"numPaymentRejects1dPerUser","type":"double"},{"key":"4","name":"accountAge","type":"double"},{"key":"5","name":"timestamp","type":"string"}],"truncated":false},"isSummary":false,"language":"scala"},"persist_state":{"view":{"type":"details","tableOptions":{},"chartOptions":{"chartType":"bar","categoryFieldKeys":["0"],"seriesFieldKeys":["4"],"aggregationType":"sum","isStacked":false}}}},"2a1ce29e-e5c3-464f-b93d-a70fafa9a437":{"type":"Synapse.DataFrame","sync_state":{"table":{"rows":[{"0":"A1055520426185580","1":"2021-12-31 00:00:00","2":"GB","3":"true","4":"0.0","5":"2000.0","index":1},{"0":"A1055520426191820","1":"2021-12-31 00:00:00","2":"US","3":"true","4":"0.0","5":"1.0","index":2},{"0":"A1055520426549020","1":"2021-12-31 00:00:00","2":"AU","3":"true","4":"0.0","5":"1.0","index":3},{"0":"A1055520426719380","1":"2021-12-31 00:00:00","2":"US","3":"true","4":"0.0","5":"2000.0","index":4},{"0":"A1055520426806680","1":"2021-12-31 00:00:00","2":"GB","3":"true","4":"1.0","5":"2000.0","index":5}],"schema":[{"key":"0","name":"accountID","type":"string"},{"key":"1","name":"timestamp","type":"timestamp"},{"key":"2","name":"accountCountry","type":"string"},{"key":"3","name":"isUserRegistered","type":"boolean"},{"key":"4","name":"numPaymentRejects1dPerUser","type":"double"},{"key":"5","name":"accountAge","type":"double"}],"truncated":false},"isSummary":false,"language":"scala"},"persist_state":{"view":{"type":"details","tableOptions":{},"chartOptions":{"chartType":"bar","categoryFieldKeys":["0"],"seriesFieldKeys":["5"],"aggregationType":"sum","isStacked":false}}}},"b9c758fa-2dd0-416c-bc32-950b9d2c96ca":{"type":"Synapse.DataFrame","sync_state":{"table":{"rows":[{"0":"EFFCEF09-9E31-4B39-AD1B-8E169D27C749","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:34:26","9":"1","10":"366.0","11":"0.0","12":"359.94","13":"359.94","14":"59.99","index":1},{"0":"EE5AF4F5-3678-4552-90BF-C178A1073A13","1":"A985157000491580","2":"399.99","3":"19.0","4":"2023-02-05 09:42:29","9":"1","10":"366.0","11":"0.0","12":"399.99","13":"399.99","14":"399.99","index":2},{"0":"EE5AF4F5-3678-4552-90BF-C178A1073A13","1":"A985157000491580","2":"399.99","3":"19.0","4":"2023-02-05 09:42:29","5":"418.129547","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"399.99","13":"399.99","14":"399.99","index":3},{"0":"2D6EB785-22EC-4879-82A2-C643810EA7C1","1":"A844427390246047","2":"5.39","3":"14.0","4":"2023-02-24 20:18:56","9":"1","10":"2365.0","11":"3.0","12":"43.12","13":"43.12","14":"5.39","index":4},{"0":"C365FB49-1531-4219-977D-CBA8CBEF8389","1":"A1759222234549920","2":"73080.0","3":"8.0","4":"2023-03-05 23:37:08","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":5},{"0":"86E7F3BE-FA69-4D61-A458-12E8E726858D","1":"A1899947009495620","2":"73080.0","3":"10.0","4":"2023-03-13 02:16:29","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":6},{"0":"86E7F3BE-FA69-4D61-A458-12E8E726858D","1":"A1899947009495620","2":"73080.0","3":"10.0","4":"2023-03-13 02:16:29","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":7},{"0":"A8E3D8C2-10C0-429E-B6E1-743A0D5C3C17","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:26:46","9":"1","10":"366.0","11":"0.0","12":"971.82","13":"971.82","14":"53.99","index":8},{"0":"F7C5B3E6-5372-474E-B4A0-60912B726639","1":"A309681307561823","2":"149.44","3":"18.0","4":"2023-01-18 23:25:19","9":"1","10":"366.0","11":"0.0","12":"149.44","13":"149.44","14":"149.44","index":9},{"0":"F7C5B3E6-5372-474E-B4A0-60912B726639","1":"A309681307561823","2":"149.44","3":"18.0","4":"2023-01-18 23:25:19","5":"149.44","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"149.44","13":"149.44","14":"149.44","index":10},{"0":"87FD50BB-5621-4778-9CBF-604522DDC605","1":"A1759222167950540","2":"59.99","3":"23.0","4":"2023-02-17 07:36:43","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":11},{"0":"E70D13AE-274C-404F-B2C2-F1AF18F702B3","1":"A1055521405851730","2":"85.59","4":"2023-02-22 14:29:05","9":"1","10":"366.0","11":"0.0","12":"85.59","13":"85.59","14":"85.59","index":12},{"0":"E70D13AE-274C-404F-B2C2-F1AF18F702B3","1":"A1055521405851730","2":"85.59","4":"2023-02-22 14:29:05","5":"85.59","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"85.59","13":"85.59","14":"85.59","index":13},{"0":"ECB14CCC-D8F9-43C1-8EC2-D94AA69A2ECA","1":"A1055520437302600","2":"1200.68","3":"16.0","4":"2023-02-24 21:55:26","9":"1","10":"1414.0","11":"0.0","12":"1200.68","13":"1200.68","14":"1200.68","index":14},{"0":"ECB14CCC-D8F9-43C1-8EC2-D94AA69A2ECA","1":"A1055520437302600","2":"1200.68","3":"16.0","4":"2023-02-24 21:55:26","5":"1200.68","6":"false","7":"1.0","8":"2.0","9":"1","10":"1414.0","11":"0.0","12":"1200.68","13":"1200.68","14":"1200.68","index":15},{"0":"1CE81EB0-7399-41B6-8E55-79A52BBFE9AC","1":"A362221882887902","2":"0.0","4":"2023-03-06 18:07:51","5":"0.0","6":"false","7":"1.0","8":"1.0","9":"0","10":"1971.0","11":"0.0","12":"0.0","13":"0.0","14":"0.0","index":16},{"0":"76770317-5FD5-4C96-B77B-43BE377FDC6D","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:48:25","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"655.6999999999998","13":"655.6999999999998","14":"65.56999999999998","index":17},{"0":"B6F5ED72-DC16-47A4-9921-3824C780A815","1":"A914801004943538","2":"31.94","3":"19.0","4":"2023-01-20 02:01:58","9":"1","10":"366.0","11":"0.0","12":"91.93","13":"91.93","14":"45.965","index":18},{"0":"B6F5ED72-DC16-47A4-9921-3824C780A815","1":"A914801004943538","2":"31.94","3":"19.0","4":"2023-01-20 02:01:58","5":"31.94","6":"false","7":"1.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"91.93","13":"91.93","14":"45.965","index":19},{"0":"294A76D1-B583-432B-AA49-F32AA4974B94","1":"A1829582519832190","2":"59.99","3":"8.0","4":"2023-02-16 13:50:47","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":20},{"0":"294A76D1-B583-432B-AA49-F32AA4974B94","1":"A1829582519832190","2":"59.99","3":"8.0","4":"2023-02-16 13:50:47","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":21},{"0":"7C8DB972-42E3-4C98-B13B-C338C0714D3F","1":"A1759222167950540","2":"59.99","3":"2.0","4":"2023-02-17 07:55:32","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"239.96","13":"239.96","14":"59.99","index":22},{"0":"24AC8E71-2B1E-4A4A-A416-64736D04783B","1":"A844428082778441","2":"157.07","3":"12.0","4":"2023-03-21 17:36:05","9":"1","10":"366.0","11":"0.0","12":"314.14","13":"314.14","14":"157.07","index":23},{"0":"24AC8E71-2B1E-4A4A-A416-64736D04783B","1":"A844428082778441","2":"157.07","3":"12.0","4":"2023-03-21 17:36:05","5":"156.421301","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"314.14","13":"314.14","14":"157.07","index":24},{"0":"826E2F8E-D033-4287-A065-A26210923F90","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:40:40","9":"1","10":"366.0","11":"0.0","12":"1457.73","13":"1457.73","14":"53.99","index":25},{"0":"3743BB47-3637-40B7-A462-2548CFB7C663","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:05:54","9":"1","10":"2365.0","11":"3.0","12":"517.439999999999","13":"506.65999999999906","14":"5.38999999999999","index":26},{"0":"3743BB47-3637-40B7-A462-2548CFB7C663","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:05:54","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"517.439999999999","13":"506.65999999999906","14":"5.38999999999999","index":27},{"0":"C1BC707F-E92A-4FD4-A709-39C1C7666465","1":"A985157026745005","2":"36540.0","3":"9.0","4":"2023-03-13 00:38:59","9":"1","10":"366.0","11":"0.0","12":"36540.0","13":"36540.0","14":"36540.0","index":28},{"0":"C1BC707F-E92A-4FD4-A709-39C1C7666465","1":"A985157026745005","2":"36540.0","3":"9.0","4":"2023-03-13 00:38:59","5":"403.0362","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"36540.0","13":"36540.0","14":"36540.0","index":29},{"0":"027C22C5-135C-47DF-8C96-3289EB88AE52","1":"A1759222219270900","2":"235.39","3":"20.0","4":"2023-02-28 01:27:26","9":"1","10":"366.0","11":"0.0","12":"235.39","13":"235.39","14":"235.39","index":30},{"0":"027C22C5-135C-47DF-8C96-3289EB88AE52","1":"A1759222219270900","2":"235.39","3":"20.0","4":"2023-02-28 01:27:26","5":"235.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"235.39","13":"235.39","14":"235.39","index":31},{"0":"A0C4B8CE-4587-469B-B447-1B99D45187BD","1":"A844428089229497","2":"261.45","3":"23.0","4":"2023-03-29 07:59:07","9":"1","10":"366.0","11":"0.0","12":"522.9","13":"522.9","14":"261.45","index":32},{"0":"A0C4B8CE-4587-469B-B447-1B99D45187BD","1":"A844428089229497","2":"261.45","3":"23.0","4":"2023-03-29 07:59:07","5":"260.370211","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"522.9","13":"522.9","14":"261.45","index":33},{"0":"8D7BE688-4CD9-4703-B03B-5ECC7BBD11E7","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:04:48","9":"1","10":"366.0","11":"0.0","12":"161.97","13":"161.97","14":"53.99","index":34},{"0":"8D7BE688-4CD9-4703-B03B-5ECC7BBD11E7","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:04:48","5":"53.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"161.97","13":"161.97","14":"53.99","index":35},{"0":"88A8FA38-800E-459D-A11C-77477E41FC5B","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:08:13","9":"1","10":"366.0","11":"0.0","12":"5258.949999999999","13":"5258.949999999999","14":"146.08194444444442","index":36},{"0":"0E2AB375-BA94-41B4-A0A5-9C173653D5F7","1":"A844428044533734","2":"599.0","3":"22.0","4":"2023-01-24 12:38:32","9":"1","10":"366.0","11":"0.0","12":"599.0","13":"599.0","14":"599.0","index":37},{"0":"0E2AB375-BA94-41B4-A0A5-9C173653D5F7","1":"A844428044533734","2":"599.0","3":"22.0","4":"2023-01-24 12:38:32","5":"626.16465","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"599.0","13":"599.0","14":"599.0","index":38},{"0":"88CFD0B0-2A86-4BD2-9CED-4F37E1E9DFFE","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:21:26","9":"1","10":"2365.0","11":"3.0","12":"48.51","13":"48.51","14":"5.39","index":39},{"0":"3FBDA277-FA1B-4355-856A-91A8D9CB828C","1":"A985157015268058","2":"4.99","3":"15.0","4":"2023-02-27 20:34:32","9":"1","10":"367.0","11":"0.0","12":"134.72999999999996","13":"134.72999999999996","14":"4.989999999999998","index":40},{"0":"3FBDA277-FA1B-4355-856A-91A8D9CB828C","1":"A985157015268058","2":"4.99","3":"15.0","4":"2023-02-27 20:34:32","5":"4.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"367.0","11":"0.0","12":"134.72999999999996","13":"134.72999999999996","14":"4.989999999999998","index":41},{"0":"8FCE1AE5-6A19-46A3-8EC1-8D62B83C29A3","1":"A1759221435782970","2":"157.07","3":"18.0","4":"2023-03-05 00:17:53","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":42},{"0":"8FCE1AE5-6A19-46A3-8EC1-8D62B83C29A3","1":"A1759221435782970","2":"157.07","3":"18.0","4":"2023-03-05 00:17:53","5":"156.421301","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":43},{"0":"27281C9B-0119-4284-A39C-603C356C6ADB","1":"A1055521381717350","2":"479.0","3":"16.0","4":"2023-01-05 16:22:27","9":"1","10":"366.0","11":"0.0","12":"479.0","13":"479.0","14":"479.0","index":44},{"0":"27281C9B-0119-4284-A39C-603C356C6ADB","1":"A1055521381717350","2":"479.0","3":"16.0","4":"2023-01-05 16:22:27","5":"754.3771","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"479.0","13":"479.0","14":"479.0","index":45},{"0":"36F5A50E-CBF2-4D33-87C5-1F8F7736F562","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:38:37","9":"1","10":"366.0","11":"0.0","12":"659.89","13":"659.89","14":"59.99","index":46},{"0":"36F5A50E-CBF2-4D33-87C5-1F8F7736F562","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:38:37","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"659.89","13":"659.89","14":"59.99","index":47},{"0":"5D333262-9627-4E42-84EA-615E7031CD48","1":"A1055521406038580","2":"73080.0","3":"3.0","4":"2023-02-22 18:29:43","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":48},{"0":"5D333262-9627-4E42-84EA-615E7031CD48","1":"A1055521406038580","2":"73080.0","3":"3.0","4":"2023-02-22 18:29:43","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":49},{"0":"3C38413D-BA5E-4F68-9CE5-BAF48E13DE3C","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:21:29","9":"1","10":"2365.0","11":"3.0","12":"264.1099999999997","13":"253.32999999999967","14":"5.3899999999999935","index":50},{"0":"3A0ABDCD-6F28-4C1F-BE5A-F341A4084E50","1":"A1759222105153830","2":"108.38","3":"10.0","4":"2023-02-16 18:54:15","9":"1","10":"366.0","11":"0.0","12":"216.76","13":"216.76","14":"108.38","index":51},{"0":"3A0ABDCD-6F28-4C1F-BE5A-F341A4084E50","1":"A1759222105153830","2":"108.38","3":"10.0","4":"2023-02-16 18:54:15","5":"108.38","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"216.76","13":"216.76","14":"108.38","index":52},{"0":"AC86DB3A-EEED-44C1-85CF-79DA0AF47B0F","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:39:29","9":"1","10":"366.0","11":"0.0","12":"1403.74","13":"1403.74","14":"53.99","index":53},{"0":"28C2478A-7236-40ED-A5C4-5BB8A29E561B","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:43:14","9":"1","10":"366.0","11":"0.0","12":"839.86","13":"839.86","14":"59.99","index":54},{"0":"2F6CE5C9-1702-4E7A-8A88-40CE991E99BB","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:34:17","9":"1","10":"366.0","11":"0.0","12":"279.86","13":"279.86","14":"19.990000000000002","index":55},{"0":"B9F2630D-2677-44EF-9DAF-CAACE7EF989E","1":"A1759222164863410","2":"109.99","3":"0.0","4":"2023-01-24 08:59:40","9":"1","10":"366.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":56},{"0":"B9F2630D-2677-44EF-9DAF-CAACE7EF989E","1":"A1759222164863410","2":"109.99","3":"0.0","4":"2023-01-24 08:59:40","5":"109.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":57},{"0":"E1DF3460-24B4-43A6-9E8C-8432ED03D018","1":"A362840681200346","2":"159.85","3":"16.0","4":"2023-02-15 20:36:32","9":"1","10":"366.0","11":"0.0","12":"319.7","13":"319.7","14":"159.85","index":58},{"0":"E1DF3460-24B4-43A6-9E8C-8432ED03D018","1":"A362840681200346","2":"159.85","3":"16.0","4":"2023-02-15 20:36:32","5":"159.189819","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"319.7","13":"319.7","14":"159.85","index":59},{"0":"9D2F95B4-73B9-4427-88FF-B82EB58087BB","1":"A985157008993462","2":"105.48","3":"5.0","4":"2023-02-17 12:03:05","9":"1","10":"366.0","11":"0.0","12":"210.96","13":"210.96","14":"105.48","index":60},{"0":"9D2F95B4-73B9-4427-88FF-B82EB58087BB","1":"A985157008993462","2":"105.48","3":"5.0","4":"2023-02-17 12:03:05","5":"105.48","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"210.96","13":"210.96","14":"105.48","index":61},{"0":"DD2D73FD-7DAF-49A5-AC4A-47117E6DE03C","1":"A1759222095873190","2":"53.49","3":"11.0","4":"2023-01-03 17:37:53","9":"1","10":"366.0","11":"0.0","12":"53.49","13":"53.49","14":"53.49","index":62},{"0":"D1A1DEF3-8373-40A4-8337-742C4BC68770","1":"A914800996051170","2":"215.64","3":"17.0","4":"2023-01-07 22:50:44","5":"215.64","6":"false","7":"0.0","8":"1.0","9":"0","10":"366.0","11":"0.0","12":"571.27","13":"571.27","14":"190.42333333333332","index":63},{"0":"DA660AD0-C47C-4012-9B2A-A74B9867F517","1":"A1829581642988470","2":"239.0","3":"17.0","4":"2023-02-13 20:58:33","9":"1","10":"366.0","11":"0.0","12":"239.0","13":"239.0","14":"239.0","index":64},{"0":"DA660AD0-C47C-4012-9B2A-A74B9867F517","1":"A1829581642988470","2":"239.0","3":"17.0","4":"2023-02-13 20:58:33","5":"120.10945","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"239.0","13":"239.0","14":"239.0","index":65},{"0":"7F5B344A-6274-4032-94D2-A21461F977A2","1":"A914801025693987","2":"278.88","3":"19.0","4":"2023-02-18 02:01:07","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":66},{"0":"7F5B344A-6274-4032-94D2-A21461F977A2","1":"A914801025693987","2":"278.88","3":"19.0","4":"2023-02-18 02:01:07","5":"277.72822599999995","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":67},{"0":"E87F43AB-36CC-481F-A7CB-E5F9C1B08F57","1":"A1899947009495620","2":"62580.0","3":"10.0","4":"2023-03-13 02:18:34","9":"1","10":"366.0","11":"0.0","12":"135660.0","13":"135660.0","14":"67830.0","index":68},{"0":"79BC1796-1C62-4C53-8F6D-301C03D1C4BF","1":"A914801004122929","2":"210.19","3":"11.0","4":"2023-01-18 17:23:13","9":"1","10":"366.0","11":"0.0","12":"210.19","13":"210.19","14":"210.19","index":69},{"0":"79BC1796-1C62-4C53-8F6D-301C03D1C4BF","1":"A914801004122929","2":"210.19","3":"11.0","4":"2023-01-18 17:23:13","5":"210.19","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"210.19","13":"210.19","14":"210.19","index":70},{"0":"A6C7101E-7794-4149-A00C-86F43AC4AF08","1":"A1055521397087470","2":"299.0","3":"23.0","4":"2023-02-09 13:59:50","9":"1","10":"366.0","11":"0.0","12":"299.0","13":"299.0","14":"299.0","index":71},{"0":"A6C7101E-7794-4149-A00C-86F43AC4AF08","1":"A1055521397087470","2":"299.0","3":"23.0","4":"2023-02-09 13:59:50","5":"312.55965","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"299.0","13":"299.0","14":"299.0","index":72},{"0":"6DC4F5F7-598E-4782-9981-5466DF994D2B","1":"A1055520836257310","2":"5.04","3":"5.0","4":"2023-02-28 10:34:02","9":"1","10":"1555.0","11":"0.0","12":"25.2","13":"25.2","14":"5.04","index":73},{"0":"2193EA41-7C0E-442B-9A2A-A7E6F6CF6D0F","1":"A1899947010059410","2":"156450.0","3":"23.0","4":"2023-03-07 14:26:10","5":"1725.6435","6":"false","7":"5.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"302610.0","13":"302610.0","14":"100870.0","index":74},{"0":"BCBDF6C6-1D5A-42F2-A912-E295526FF9B7","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:04:05","9":"1","10":"366.0","11":"0.0","12":"4809.039999999998","13":"4809.039999999998","14":"145.72848484848478","index":75},{"0":"A891E91F-34EB-47BE-87EC-EF06767A0E5E","1":"A914801026387229","2":"301.26","3":"19.0","4":"2023-02-19 04:03:58","9":"1","10":"366.0","11":"0.0","12":"301.26","13":"301.26","14":"301.26","index":76},{"0":"A891E91F-34EB-47BE-87EC-EF06767A0E5E","1":"A914801026387229","2":"301.26","3":"19.0","4":"2023-02-19 04:03:58","5":"301.26","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"301.26","13":"301.26","14":"301.26","index":77},{"0":"06CF9747-053E-4361-8048-CD6D0F16812C","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:45:31","9":"1","10":"2365.0","11":"3.0","12":"355.73999999999944","13":"344.95999999999947","14":"5.389999999999992","index":78},{"0":"3BBA55DF-FAE7-4F71-A7E9-2CE883BE50C2","1":"A985156290472668","2":"5.34","3":"8.0","4":"2023-02-27 15:11:10","5":"5.34","6":"true","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"0.0","12":"21.36","13":"21.36","14":"5.34","index":79},{"0":"F178386E-86A3-4F08-B9B0-6778898CAD4E","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 16:56:06","9":"1","10":"366.0","11":"0.0","12":"158.97","13":"158.97","14":"52.99","index":80},{"0":"50C46472-B928-49BF-82E2-D1BE1C9DBCF4","1":"A1899946943634320","2":"149.99","3":"12.0","4":"2023-01-15 12:57:30","5":"236.21925099999999","6":"false","7":"0.0","8":"1.0","9":"0","10":"366.0","11":"0.0","12":"339.98","13":"339.98","14":"169.99","index":81},{"0":"F5606360-D929-4903-8771-E51207DAF2C6","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:53:31","9":"1","10":"366.0","11":"0.0","12":"909.82","13":"909.82","14":"129.9742857142857","index":82},{"0":"C8AA8B84-1C89-4B76-B2BF-3C10C3444D89","1":"A914801007105746","2":"80.73","3":"9.0","4":"2023-01-25 18:07:59","9":"1","10":"367.0","11":"0.0","12":"257.64","13":"257.64","14":"85.88","index":83},{"0":"F461D35F-457F-40E7-BF42-4C116CF0D921","1":"A296624604869030","2":"139.99","3":"13.0","4":"2023-02-17 21:48:49","9":"1","10":"366.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":84},{"0":"996C6CB5-D9A8-4B88-A723-6A002E6B893F","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:44:00","9":"1","10":"367.0","11":"0.0","12":"79.84","13":"79.84","14":"4.99","index":85},{"0":"CBE31B92-8D8D-422D-BF1C-2883222AC68B","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 16:51:29","9":"1","10":"366.0","11":"0.0","12":"52.99","13":"52.99","14":"52.99","index":86},{"0":"CBE31B92-8D8D-422D-BF1C-2883222AC68B","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 16:51:29","5":"52.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"52.99","13":"52.99","14":"52.99","index":87},{"0":"2120F51D-E109-4481-88D7-023757351EDF","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:05:23","9":"1","10":"366.0","11":"0.0","12":"4959.009999999998","13":"4959.009999999998","14":"145.8532352941176","index":88},{"0":"DF9AFB75-2D64-4A8A-8743-AA39724E34AD","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:39:41","9":"1","10":"366.0","11":"0.0","12":"339.83000000000004","13":"339.83000000000004","14":"19.990000000000002","index":89},{"0":"F325312B-1722-414B-8969-07E1BADC9D75","1":"A1688853647393240","2":"147.69","3":"8.0","4":"2023-01-25 16:45:52","9":"1","10":"366.0","11":"0.0","12":"147.69","13":"147.69","14":"147.69","index":90},{"0":"F325312B-1722-414B-8969-07E1BADC9D75","1":"A1688853647393240","2":"147.69","3":"8.0","4":"2023-01-25 16:45:52","5":"147.69","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"147.69","13":"147.69","14":"147.69","index":91},{"0":"DF029819-0B5D-4201-8195-325BE6202E50","1":"A1829582519835540","2":"63.59","3":"23.0","4":"2023-02-17 08:12:30","9":"1","10":"366.0","11":"0.0","12":"63.59","13":"63.59","14":"63.59","index":92},{"0":"DF029819-0B5D-4201-8195-325BE6202E50","1":"A1829582519835540","2":"63.59","3":"23.0","4":"2023-02-17 08:12:30","5":"63.59","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"63.59","13":"63.59","14":"63.59","index":93},{"0":"8A753C7E-56CE-4D4C-8910-767868183DD5","1":"A914801029782303","2":"148.74","3":"18.0","4":"2023-02-24 00:19:09","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":94},{"0":"8A753C7E-56CE-4D4C-8910-767868183DD5","1":"A914801029782303","2":"148.74","3":"18.0","4":"2023-02-24 00:19:09","5":"148.74","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":95},{"0":"021535A3-03BA-43FF-9ADF-9EB8ED0DCCD2","1":"A844427388621092","2":"85.59","3":"19.0","4":"2023-02-24 00:35:20","9":"1","10":"366.0","11":"0.0","12":"85.59","13":"85.59","14":"85.59","index":96},{"0":"021535A3-03BA-43FF-9ADF-9EB8ED0DCCD2","1":"A844427388621092","2":"85.59","3":"19.0","4":"2023-02-24 00:35:20","5":"85.59","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"85.59","13":"85.59","14":"85.59","index":97},{"0":"5FE36C4E-BDCE-4C58-B35A-2077ED72CDFF","1":"A1055521420182040","2":"752.68","3":"18.0","4":"2023-03-13 00:37:15","9":"1","10":"366.0","11":"0.0","12":"752.68","13":"752.68","14":"752.68","index":98},{"0":"5FE36C4E-BDCE-4C58-B35A-2077ED72CDFF","1":"A1055521420182040","2":"752.68","3":"18.0","4":"2023-03-13 00:37:15","5":"752.68","6":"false","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"752.68","13":"752.68","14":"752.68","index":99},{"0":"415F4169-0DE1-4D1D-B056-823D02BFEDF6","1":"A1829582580775700","2":"65.57","3":"11.0","4":"2023-01-03 18:51:45","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"131.14","13":"131.14","14":"65.57","index":100},{"0":"B634C814-3ADD-41B5-BBA6-34BD46A8EA65","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:18:13","9":"1","10":"366.0","11":"0.0","12":"2409.5199999999995","13":"2409.5199999999995","14":"141.73647058823528","index":101},{"0":"3CF32319-BFF3-4CFE-8792-8C4775AD619F","1":"A1899946873142120","2":"99.98","3":"13.0","4":"2023-01-03 18:45:18","9":"1","10":"366.0","11":"0.0","12":"159.97","13":"159.97","14":"79.985","index":102},{"0":"B634C814-3ADD-41B5-BBA6-34BD46A8EA65","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:18:13","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"2409.5199999999995","13":"2409.5199999999995","14":"141.73647058823528","index":103},{"0":"2589C5DD-012D-429C-A3C1-8333C3ECE2A4","1":"A1829582519835540","2":"59.99","3":"5.0","4":"2023-02-17 11:22:08","9":"1","10":"366.0","11":"0.0","12":"247.16000000000003","13":"247.16000000000003","14":"61.790000000000006","index":104},{"0":"D7E5266B-1C45-4BAA-AAEC-15D771DDF4D9","1":"A1055521406380930","2":"139.99","3":"19.0","4":"2023-02-24 01:12:56","9":"1","10":"366.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":105},{"0":"D7E5266B-1C45-4BAA-AAEC-15D771DDF4D9","1":"A1055521406380930","2":"139.99","3":"19.0","4":"2023-02-24 01:12:56","5":"139.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":106},{"0":"961FCF4A-7930-4C04-B37B-A5FDFA414D9E","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:41:14","9":"1","10":"366.0","11":"0.0","12":"359.82000000000005","13":"359.82000000000005","14":"19.990000000000002","index":107},{"0":"6D7890A5-DA17-43B5-AA06-79189795DEF8","1":"A1688853613564590","2":"62.39","3":"2.0","4":"2023-01-17 08:17:25","9":"1","10":"366.0","11":"0.0","12":"62.39","13":"62.39","14":"62.39","index":108},{"0":"6D7890A5-DA17-43B5-AA06-79189795DEF8","1":"A1688853613564590","2":"62.39","3":"2.0","4":"2023-01-17 08:17:25","5":"62.39","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"62.39","13":"62.39","14":"62.39","index":109},{"0":"156194EC-C71A-4807-B417-BE00180D9FC0","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:38:28","9":"1","10":"2365.0","11":"3.0","12":"118.58","13":"118.58","14":"5.39","index":110},{"0":"10F2E1BB-B84B-425E-9CF4-1E9A6AB041D8","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-26 11:28:28","9":"1","10":"2365.0","11":"3.0","12":"549.779999999999","13":"538.999999999999","14":"5.38999999999999","index":111},{"0":"156194EC-C71A-4807-B417-BE00180D9FC0","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:38:28","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"118.58","13":"118.58","14":"5.39","index":112},{"0":"CFF67307-A9EF-43AC-AE45-D448E2991474","1":"A844428041547410","2":"99.99","4":"2023-01-19 10:39:12","9":"1","10":"366.0","11":"0.0","12":"99.99","13":"99.99","14":"99.99","index":113},{"0":"44E0A6A4-06F8-40B7-9CAA-7E421AB8FD69","1":"A844427703526719","2":"102.48","4":"2023-01-23 03:36:00","9":"1","10":"1980.0","11":"0.0","12":"102.48","13":"102.48","14":"102.48","index":114},{"0":"44E0A6A4-06F8-40B7-9CAA-7E421AB8FD69","1":"A844427703526719","2":"102.48","4":"2023-01-23 03:36:00","5":"102.48","6":"false","7":"2.0","8":"1.0","9":"1","10":"1980.0","11":"0.0","12":"102.48","13":"102.48","14":"102.48","index":115},{"0":"69A49BAA-59B0-4AEC-AD40-EEE900AE308A","1":"A1759222219270870","2":"424.99","3":"22.0","4":"2023-02-28 05:01:08","9":"1","10":"366.0","11":"0.0","12":"849.98","13":"849.98","14":"424.99","index":116},{"0":"ACB1088D-CB05-45F9-9B3E-4709989A7443","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:06:03","9":"1","10":"366.0","11":"0.0","12":"215.96","13":"215.96","14":"53.99","index":117},{"0":"07C1A129-EF6A-4B87-BFE4-C4D4CDF7049B","1":"A844427926468579","2":"54.11","3":"0.0","4":"2023-02-17 06:01:39","9":"1","10":"366.0","11":"0.0","12":"54.11","13":"54.11","14":"54.11","index":118},{"0":"1A68B478-E49B-4052-9BCD-2DC43AFA57B9","1":"A1688853682035130","2":"490.0","4":"2023-03-07 17:23:13","9":"1","10":"366.0","11":"0.0","12":"490.0","13":"490.0","14":"490.0","index":119},{"0":"1A68B478-E49B-4052-9BCD-2DC43AFA57B9","1":"A1688853682035130","2":"490.0","4":"2023-03-07 17:23:13","5":"660.422","6":"false","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"490.0","13":"490.0","14":"490.0","index":120},{"0":"C173A9A3-90B7-4B38-8DAC-D001CDD47814","1":"A1899946873128090","2":"127.48","3":"22.0","4":"2023-02-15 03:51:29","9":"1","10":"366.0","11":"0.0","12":"570.5","13":"570.5","14":"114.1","index":121},{"0":"CC3A3235-D2C0-4050-B3FA-1874B30260D6","1":"A1899946865208980","2":"62580.0","3":"1.0","4":"2023-03-09 16:22:35","9":"1","10":"366.0","11":"0.0","12":"135660.0","13":"135660.0","14":"67830.0","index":122},{"0":"CC3A3235-D2C0-4050-B3FA-1874B30260D6","1":"A1899946865208980","2":"62580.0","3":"1.0","4":"2023-03-09 16:22:35","5":"690.2574","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"135660.0","13":"135660.0","14":"67830.0","index":123},{"0":"C4005755-95ED-4A4F-B1CD-B3943278DA54","1":"A1759222235006700","2":"212.49","3":"7.0","4":"2023-01-19 13:48:20","9":"1","10":"366.0","11":"0.0","12":"212.49","13":"212.49","14":"212.49","index":124},{"0":"C4005755-95ED-4A4F-B1CD-B3943278DA54","1":"A1759222235006700","2":"212.49","3":"7.0","4":"2023-01-19 13:48:20","5":"212.49","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"212.49","13":"212.49","14":"212.49","index":125},{"0":"4B1F2669-E141-4218-8048-95E751DCF8F1","1":"A1759222167950540","2":"59.99","3":"2.0","4":"2023-02-17 07:54:20","9":"1","10":"366.0","11":"0.0","12":"179.97","13":"179.97","14":"59.99","index":126},{"0":"ABA1D7A5-99A8-4416-B120-BD5E39AF612A","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:45:00","9":"1","10":"2365.0","11":"3.0","12":"350.34999999999945","13":"339.5699999999995","14":"5.389999999999992","index":127},{"0":"ABA1D7A5-99A8-4416-B120-BD5E39AF612A","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:45:00","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"350.34999999999945","13":"339.5699999999995","14":"5.389999999999992","index":128},{"0":"CEDBDEF3-E681-4CCF-81AC-8EA4433E3DB2","1":"A985157015268058","2":"4.99","3":"15.0","4":"2023-02-27 20:36:09","9":"1","10":"367.0","11":"0.0","12":"139.71999999999997","13":"139.71999999999997","14":"4.989999999999999","index":129},{"0":"CEDBDEF3-E681-4CCF-81AC-8EA4433E3DB2","1":"A985157015268058","2":"4.99","3":"15.0","4":"2023-02-27 20:36:09","5":"4.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"367.0","11":"0.0","12":"139.71999999999997","13":"139.71999999999997","14":"4.989999999999999","index":130},{"0":"6E4A323D-0142-4015-88CC-74C85F2D1261","1":"A844428039854278","2":"76.19","3":"13.0","4":"2023-01-19 18:36:52","9":"1","10":"366.0","11":"0.0","12":"152.38","13":"152.38","14":"76.19","index":131},{"0":"6E4A323D-0142-4015-88CC-74C85F2D1261","1":"A844428039854278","2":"76.19","3":"13.0","4":"2023-01-19 18:36:52","5":"76.19","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"152.38","13":"152.38","14":"76.19","index":132},{"0":"72B7AED1-0944-4DE8-8BEF-74680BBAC3F1","1":"A1829582519832190","2":"59.99","3":"8.0","4":"2023-02-16 13:55:28","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"179.97","13":"179.97","14":"59.99","index":133},{"0":"938457F3-FCC6-463A-A037-89D33413C348","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:20:56","9":"1","10":"2365.0","11":"3.0","12":"199.4299999999998","13":"188.64999999999984","14":"5.389999999999995","index":134},{"0":"938457F3-FCC6-463A-A037-89D33413C348","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:20:56","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"199.4299999999998","13":"188.64999999999984","14":"5.389999999999995","index":135},{"0":"7CD74B1F-ACC8-4C37-B00F-11151C50D758","1":"A1055520703430980","2":"119.75","3":"20.0","4":"2023-03-25 02:17:14","9":"1","10":"366.0","11":"0.0","12":"119.75","13":"119.75","14":"119.75","index":136},{"0":"7CD74B1F-ACC8-4C37-B00F-11151C50D758","1":"A1055520703430980","2":"119.75","3":"20.0","4":"2023-03-25 02:17:14","5":"119.75","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.75","13":"119.75","14":"119.75","index":137},{"0":"40F8D07E-67EF-4678-8612-FC130DC84F5E","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:00:08","9":"1","10":"366.0","11":"0.0","12":"1359.73","13":"1359.73","14":"135.973","index":138},{"0":"0A82E4ED-93EC-4386-AA16-0EA1FC946B6C","1":"A1899946895455190","2":"19.99","3":"13.0","4":"2023-01-13 19:20:25","9":"1","10":"366.0","11":"0.0","12":"159.92","13":"159.92","14":"19.99","index":139},{"0":"DF0ECDA4-9184-4A60-A227-C9193CD08C63","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:03:15","9":"1","10":"2365.0","11":"3.0","12":"145.52999999999994","13":"134.74999999999997","14":"5.389999999999998","index":140},{"0":"411DF6C0-02DE-4D70-977B-EF261BF1D4A1","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:43:06","9":"1","10":"2365.0","11":"3.0","12":"318.00999999999954","13":"307.22999999999956","14":"5.389999999999993","index":141},{"0":"DF0ECDA4-9184-4A60-A227-C9193CD08C63","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:03:15","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"145.52999999999994","13":"134.74999999999997","14":"5.389999999999998","index":142},{"0":"0B724B9C-2D5A-4DA6-8D99-570DA7C20933","1":"A2111055908627200","2":"74.89","3":"19.0","4":"2023-03-26 01:10:22","9":"1","10":"1518.0","11":"0.0","12":"74.89","13":"74.89","14":"74.89","index":143},{"0":"0B724B9C-2D5A-4DA6-8D99-570DA7C20933","1":"A2111055908627200","2":"74.89","3":"19.0","4":"2023-03-26 01:10:22","5":"74.89","6":"false","7":"1.0","8":"0.0","9":"1","10":"1518.0","11":"0.0","12":"74.89","13":"74.89","14":"74.89","index":144},{"0":"9E6DC7FE-2F2F-4508-BBF2-BC31F249B637","1":"A1759222229520370","2":"169.0","3":"23.0","4":"2023-01-03 14:10:32","9":"1","10":"366.0","11":"0.0","12":"768.0","13":"768.0","14":"384.0","index":145},{"0":"9E6DC7FE-2F2F-4508-BBF2-BC31F249B637","1":"A1759222229520370","2":"169.0","3":"23.0","4":"2023-01-03 14:10:32","5":"176.66415","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"768.0","13":"768.0","14":"384.0","index":146},{"0":"41C41A19-2063-47C1-BAFB-B1BF50578374","1":"A844428029243587","2":"148.74","3":"19.0","4":"2023-01-04 01:23:14","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":147},{"0":"41C41A19-2063-47C1-BAFB-B1BF50578374","1":"A844428029243587","2":"148.74","3":"19.0","4":"2023-01-04 01:23:14","5":"148.74","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":148},{"0":"8C0CDFD5-0A74-405D-9360-B99DC5F5A658","1":"A1829582571100710","2":"239.51","3":"16.0","4":"2023-02-01 21:54:42","9":"1","10":"366.0","11":"0.0","12":"239.51","13":"239.51","14":"239.51","index":149},{"0":"8C0CDFD5-0A74-405D-9360-B99DC5F5A658","1":"A1829582571100710","2":"239.51","3":"16.0","4":"2023-02-01 21:54:42","5":"239.51","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"239.51","13":"239.51","14":"239.51","index":150},{"0":"51D66EFC-6211-4DF0-BB4C-1BCE5F248330","1":"A296624604869030","2":"139.99","3":"13.0","4":"2023-02-17 21:59:16","9":"1","10":"366.0","11":"0.0","12":"279.98","13":"279.98","14":"139.99","index":151},{"0":"51D66EFC-6211-4DF0-BB4C-1BCE5F248330","1":"A296624604869030","2":"139.99","3":"13.0","4":"2023-02-17 21:59:16","5":"139.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"279.98","13":"279.98","14":"139.99","index":152},{"0":"DE15CA12-F07E-4C7C-9C30-19EF38B2B3E6","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:42:16","9":"1","10":"366.0","11":"0.0","12":"1511.72","13":"1511.72","14":"53.99","index":153},{"0":"B30F54F2-BBB5-4DFC-B4D6-F3BA03220790","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:28:43","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":154},{"0":"964F4854-0D2F-4852-BA0E-2FF1CAC93990","1":"A985156954406966","2":"240.45","3":"19.0","4":"2023-02-28 01:50:02","9":"1","10":"366.0","11":"0.0","12":"240.45","13":"240.45","14":"240.45","index":155},{"0":"D7391AE6-467B-4C82-9310-7AA348F9D0C4","1":"A914800267247225","2":"139.99","3":"20.0","4":"2023-03-12 01:47:29","9":"1","10":"370.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":156},{"0":"D7391AE6-467B-4C82-9310-7AA348F9D0C4","1":"A914800267247225","2":"139.99","3":"20.0","4":"2023-03-12 01:47:29","5":"139.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"370.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":157},{"0":"6EE898DD-C20C-4784-9CD5-3A630B4113AB","1":"A914801045428170","2":"85.59","3":"13.0","4":"2023-03-15 18:30:29","9":"1","10":"366.0","11":"0.0","12":"85.59","13":"85.59","14":"85.59","index":158},{"0":"6EE898DD-C20C-4784-9CD5-3A630B4113AB","1":"A914801045428170","2":"85.59","3":"13.0","4":"2023-03-15 18:30:29","5":"85.59","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"85.59","13":"85.59","14":"85.59","index":159},{"0":"C4F7FAB1-FE05-4F4D-8E52-8C6A3EE96115","1":"A844427390246047","2":"5.39","3":"11.0","4":"2023-02-28 17:14:10","9":"1","10":"2365.0","11":"3.0","12":"609.0699999999988","13":"269.49999999999966","14":"5.389999999999989","index":160},{"0":"B49DBC29-4D81-4FEE-8B9F-44415FD7BC80","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:42:26","9":"1","10":"367.0","11":"0.0","12":"74.85000000000001","13":"74.85000000000001","14":"4.99","index":161},{"0":"1BFE4527-0634-4541-AE5B-36B56B92F78B","1":"A985156996696141","2":"1199.0","3":"9.0","4":"2023-01-25 09:00:09","9":"1","10":"366.0","11":"0.0","12":"2398.0","13":"2398.0","14":"1199.0","index":162},{"0":"1BFE4527-0634-4541-AE5B-36B56B92F78B","1":"A985156996696141","2":"1199.0","3":"9.0","4":"2023-01-25 09:00:09","5":"187.8833","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"2398.0","13":"2398.0","14":"1199.0","index":163},{"0":"6BC0D760-19D3-41AB-B24A-87509876A25B","1":"A1759222251254350","2":"516.24","3":"10.0","4":"2023-02-23 18:27:57","9":"1","10":"368.0","11":"0.0","12":"1032.48","13":"1032.48","14":"516.24","index":164},{"0":"6BC0D760-19D3-41AB-B24A-87509876A25B","1":"A1759222251254350","2":"516.24","3":"10.0","4":"2023-02-23 18:27:57","5":"514.107929","6":"true","7":"0.0","8":"1.0","9":"1","10":"368.0","11":"0.0","12":"1032.48","13":"1032.48","14":"516.24","index":165},{"0":"50EA1C3A-CE63-435C-BA22-4846C2024425","1":"A844427328211634","2":"104.7","3":"14.0","4":"2023-02-28 00:47:16","9":"1","10":"1893.0","11":"0.0","12":"104.7","13":"104.7","14":"104.7","index":166},{"0":"50EA1C3A-CE63-435C-BA22-4846C2024425","1":"A844427328211634","2":"104.7","3":"14.0","4":"2023-02-28 00:47:16","5":"104.7","6":"false","7":"1.0","8":"0.0","9":"1","10":"1893.0","11":"0.0","12":"104.7","13":"104.7","14":"104.7","index":167},{"0":"4BE98D88-C717-4C24-809B-1A92A1FFCEBC","1":"A1759222234548900","2":"73080.0","3":"23.0","4":"2023-03-07 15:02:16","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":168},{"0":"C110E452-388B-4D53-A271-DE2C06DB410A","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:17:45","9":"1","10":"366.0","11":"0.0","12":"647.88","13":"647.88","14":"53.99","index":169},{"0":"AF5BC5D5-9280-4F4A-860B-D8A4772823AD","1":"A985157000299534","2":"281.37","3":"14.0","4":"2023-02-03 19:43:12","9":"1","10":"366.0","11":"0.0","12":"438.44","13":"438.44","14":"219.22","index":170},{"0":"3A27F1D6-6B38-461C-ACAE-DCF4F171948C","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:48:53","9":"1","10":"2365.0","11":"3.0","12":"377.2999999999994","13":"366.5199999999994","14":"5.389999999999991","index":171},{"0":"3A27F1D6-6B38-461C-ACAE-DCF4F171948C","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:48:53","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"377.2999999999994","13":"366.5199999999994","14":"5.389999999999991","index":172},{"0":"96007AC1-F260-4648-97B6-3D3527611B1B","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:29:39","9":"1","10":"367.0","11":"0.0","12":"89.82","13":"89.82","14":"4.989999999999999","index":173},{"0":"F825E26E-89D0-43AB-B281-D62588C4779A","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:49:42","9":"1","10":"366.0","11":"0.0","12":"3609.279999999998","13":"3609.279999999998","14":"144.37119999999993","index":174},{"0":"4F5BE866-AC36-4BD4-8DE5-40B48B2AFD9F","1":"A914800364713675","2":"24.99","3":"20.0","4":"2023-01-06 02:18:37","9":"1","10":"911.0","11":"0.0","12":"24.99","13":"24.99","14":"24.99","index":175},{"0":"4F5BE866-AC36-4BD4-8DE5-40B48B2AFD9F","1":"A914800364713675","2":"24.99","3":"20.0","4":"2023-01-06 02:18:37","5":"24.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"911.0","11":"0.0","12":"24.99","13":"24.99","14":"24.99","index":176},{"0":"E23AEB7C-E6E5-45AC-BF52-15318F230F7C","1":"A1688852408331370","2":"159.85","3":"17.0","4":"2023-02-05 21:21:44","9":"1","10":"462.0","11":"0.0","12":"159.85","13":"159.85","14":"159.85","index":177},{"0":"E23AEB7C-E6E5-45AC-BF52-15318F230F7C","1":"A1688852408331370","2":"159.85","3":"17.0","4":"2023-02-05 21:21:44","5":"159.189819","6":"false","7":"1.0","8":"0.0","9":"1","10":"462.0","11":"0.0","12":"159.85","13":"159.85","14":"159.85","index":178},{"0":"3826E27E-AAFD-4443-A7E9-A0586D2F0729","1":"A1829582583896790","2":"127.18","3":"7.0","4":"2023-02-16 12:26:58","9":"1","10":"366.0","11":"0.0","12":"254.36","13":"254.36","14":"127.18","index":179},{"0":"93796897-06CD-4111-8F2D-E27D5CE46B66","1":"A844427218518467","2":"157.94","3":"18.0","4":"2023-02-17 23:30:32","9":"1","10":"1274.0","11":"0.0","12":"315.88","13":"315.88","14":"157.94","index":180},{"0":"194011B7-EB22-403A-9B71-9964CCD499B9","1":"A985157024003479","2":"219.99","3":"9.0","4":"2023-03-09 14:44:26","9":"1","10":"366.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":181},{"0":"194011B7-EB22-403A-9B71-9964CCD499B9","1":"A985157024003479","2":"219.99","3":"9.0","4":"2023-03-09 14:44:26","5":"219.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":182},{"0":"21EB96F0-6C2C-4168-BEEA-A253EE75FE7B","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:57:10","9":"1","10":"2365.0","11":"3.0","12":"458.1499999999992","13":"447.3699999999992","14":"5.389999999999991","index":183},{"0":"9BE72279-7902-492D-9265-744491E4F86D","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 20:16:50","9":"1","10":"367.0","11":"0.0","12":"124.74999999999996","13":"124.74999999999996","14":"4.989999999999998","index":184},{"0":"8717C0FD-51C1-4457-BABB-D356176DDF24","1":"A1899947009495910","2":"538.0","3":"21.0","4":"2023-03-14 13:09:53","9":"1","10":"366.0","11":"0.0","12":"538.0","13":"538.0","14":"538.0","index":185},{"0":"9B5ACCF3-D278-48E9-867E-6027988CEA01","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:24:33","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"196.70999999999998","13":"196.70999999999998","14":"65.57","index":186},{"0":"5B14DBAD-62C5-4144-B131-E1C358C623E7","1":"A985156723649620","2":"139.99","3":"21.0","4":"2023-01-11 02:28:26","9":"1","10":"366.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":187},{"0":"5B14DBAD-62C5-4144-B131-E1C358C623E7","1":"A985156723649620","2":"139.99","3":"21.0","4":"2023-01-11 02:28:26","5":"139.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":188},{"0":"11EDBDC9-2F68-4E09-8DB8-FB0D9B70B121","1":"A1899947003739520","2":"239.24","3":"5.0","4":"2023-02-17 12:13:53","9":"1","10":"366.0","11":"0.0","12":"239.24","13":"239.24","14":"239.24","index":189},{"0":"11EDBDC9-2F68-4E09-8DB8-FB0D9B70B121","1":"A1899947003739520","2":"239.24","3":"5.0","4":"2023-02-17 12:13:53","5":"239.24","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"239.24","13":"239.24","14":"239.24","index":190},{"0":"5CE4020A-ABA5-4B60-9BC3-131A126670C6","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:03:22","9":"1","10":"366.0","11":"0.0","12":"107.98","13":"107.98","14":"53.99","index":191},{"0":"C92FFA51-901D-4F5E-AF5A-A0F05FD9EE49","1":"A1899946263303050","2":"939.0","3":"19.0","4":"2023-02-03 18:26:23","9":"1","10":"366.0","11":"0.0","12":"939.0","13":"939.0","14":"939.0","index":192},{"0":"C92FFA51-901D-4F5E-AF5A-A0F05FD9EE49","1":"A1899946263303050","2":"939.0","3":"19.0","4":"2023-02-03 18:26:23","5":"170.67264","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"939.0","13":"939.0","14":"939.0","index":193},{"0":"0B666A57-4CBF-411F-A855-A7B615726FED","1":"A985157008993462","2":"105.48","3":"5.0","4":"2023-02-17 12:11:06","5":"105.48","6":"false","7":"2.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"316.44","13":"316.44","14":"105.48","index":194},{"0":"16346EB5-B1D7-438D-8F50-E46ADB6AEE9A","1":"A844428082778441","2":"157.07","3":"12.0","4":"2023-03-21 17:31:16","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":195},{"0":"038E2738-E619-431A-8597-5952B7710EF8","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:20:26","9":"1","10":"366.0","11":"0.0","12":"6158.77","13":"6158.77","14":"146.63738095238097","index":196},{"0":"4C959775-D5F7-4529-8198-B5CD3007D04C","1":"A1829581437162310","2":"233.19","3":"7.0","4":"2023-03-27 13:10:38","9":"1","10":"366.0","11":"0.0","12":"233.19","13":"233.19","14":"233.19","index":197},{"0":"4C959775-D5F7-4529-8198-B5CD3007D04C","1":"A1829581437162310","2":"233.19","3":"7.0","4":"2023-03-27 13:10:38","5":"233.19","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"233.19","13":"233.19","14":"233.19","index":198},{"0":"618987FF-BE7A-451C-B8C3-62E31CC9444E","1":"A985156287053363","2":"151.54","3":"18.0","4":"2023-01-28 00:37:51","9":"1","10":"366.0","11":"0.0","12":"151.54","13":"151.54","14":"151.54","index":199},{"0":"618987FF-BE7A-451C-B8C3-62E31CC9444E","1":"A985156287053363","2":"151.54","3":"18.0","4":"2023-01-28 00:37:51","5":"151.54","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"151.54","13":"151.54","14":"151.54","index":200},{"0":"2616D25E-605D-4D13-A747-8056ACC9FECC","1":"A1759221088015650","2":"239.0","3":"14.0","4":"2023-02-15 18:07:30","9":"1","10":"370.0","11":"0.0","12":"239.0","13":"239.0","14":"239.0","index":201},{"0":"2616D25E-605D-4D13-A747-8056ACC9FECC","1":"A1759221088015650","2":"239.0","3":"14.0","4":"2023-02-15 18:07:30","5":"120.10945","6":"false","7":"1.0","8":"0.0","9":"1","10":"370.0","11":"0.0","12":"239.0","13":"239.0","14":"239.0","index":202},{"0":"0D20ED93-B1A8-4C3D-A492-FB63C8C2D3D6","1":"A1688853596363060","2":"60.59","3":"6.0","4":"2023-02-16 12:00:13","9":"1","10":"366.0","11":"0.0","12":"181.77","13":"181.77","14":"60.59","index":203},{"0":"0D20ED93-B1A8-4C3D-A492-FB63C8C2D3D6","1":"A1688853596363060","2":"60.59","3":"6.0","4":"2023-02-16 12:00:13","5":"60.59","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"181.77","13":"181.77","14":"60.59","index":204},{"0":"A6FD29AF-1C86-4558-8D18-89D7F53EC691","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:29:54","9":"1","10":"2365.0","11":"3.0","12":"70.07","13":"70.07","14":"5.39","index":205},{"0":"A06EBEB4-92ED-4F42-8211-F28EC67E5DF5","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-28 13:18:42","9":"1","10":"2365.0","11":"3.0","12":"560.5599999999989","13":"220.98999999999975","14":"5.38999999999999","index":206},{"0":"833FA1C1-EC94-4F61-AE86-EAF339165BD2","1":"A1688852068178080","2":"109.99","3":"20.0","4":"2023-01-07 05:19:25","9":"1","10":"2365.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":207},{"0":"833FA1C1-EC94-4F61-AE86-EAF339165BD2","1":"A1688852068178080","2":"109.99","3":"20.0","4":"2023-01-07 05:19:25","5":"109.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":208},{"0":"14284612-9202-42B8-B994-098182BD9553","1":"A1055521397087470","2":"299.0","3":"23.0","4":"2023-02-09 14:07:44","9":"1","10":"366.0","11":"0.0","12":"598.0","13":"598.0","14":"299.0","index":209},{"0":"14284612-9202-42B8-B994-098182BD9553","1":"A1055521397087470","2":"299.0","3":"23.0","4":"2023-02-09 14:07:44","5":"312.55965","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"598.0","13":"598.0","14":"299.0","index":210},{"0":"B9388B0B-6649-4EC4-8C4F-D6E9293CC8CA","1":"A1899946341703750","2":"406.78","3":"19.0","4":"2023-01-02 00:41:25","9":"1","10":"366.0","11":"0.0","12":"406.78","13":"406.78","14":"406.78","index":211},{"0":"B9388B0B-6649-4EC4-8C4F-D6E9293CC8CA","1":"A1899946341703750","2":"406.78","3":"19.0","4":"2023-01-02 00:41:25","5":"405.099999","6":"false","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"406.78","13":"406.78","14":"406.78","index":212},{"0":"57C6D5D6-43F4-4BD0-9FF5-67E4BB395B99","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:15:43","9":"1","10":"366.0","11":"0.0","12":"2259.5499999999997","13":"2259.5499999999997","14":"141.22187499999998","index":213},{"0":"2FAF5C96-CEE4-4D17-BEE6-EB27DF5351E7","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:20:41","9":"1","10":"2365.0","11":"3.0","12":"258.7199999999997","13":"247.93999999999969","14":"5.3899999999999935","index":214},{"0":"1E8F78CA-543C-46F5-A429-593B1B48D8E9","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:33:02","9":"1","10":"2365.0","11":"3.0","12":"91.63","13":"91.63","14":"5.39","index":215},{"0":"2FAF5C96-CEE4-4D17-BEE6-EB27DF5351E7","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:20:41","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"258.7199999999997","13":"247.93999999999969","14":"5.3899999999999935","index":216},{"0":"3FB022CD-5FEE-43CE-A742-F25EFC9F48DF","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 17:06:11","9":"1","10":"366.0","11":"0.0","12":"370.93","13":"370.93","14":"52.99","index":217},{"0":"636DA8EE-8D04-49D9-9BFE-21FAE9991FE6","1":"A1055521380537310","2":"129.58","3":"13.0","4":"2023-01-03 18:57:29","9":"1","10":"366.0","11":"0.0","12":"323.95000000000005","13":"323.95000000000005","14":"107.98333333333335","index":218},{"0":"4324B9F6-5D21-4327-B80F-99057A3B541C","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:33:20","9":"1","10":"366.0","11":"0.0","12":"299.95","13":"299.95","14":"59.989999999999995","index":219},{"0":"7DCB85C6-1524-4E97-8638-7324B5A49568","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:45:54","9":"1","10":"366.0","11":"0.0","12":"459.7700000000001","13":"459.7700000000001","14":"19.990000000000006","index":220},{"0":"9E492C70-9940-4103-9414-FC8F0C741490","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:27:34","9":"1","10":"366.0","11":"0.0","12":"219.89000000000001","13":"219.89000000000001","14":"19.990000000000002","index":221},{"0":"7DCB85C6-1524-4E97-8638-7324B5A49568","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:45:54","5":"19.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"459.7700000000001","13":"459.7700000000001","14":"19.990000000000006","index":222},{"0":"A4B91F8E-04B2-4A1C-B612-10F42C760989","1":"A844427101383089","2":"519.99","3":"14.0","4":"2023-03-10 22:51:49","9":"1","10":"2365.0","11":"0.0","12":"519.99","13":"519.99","14":"519.99","index":223},{"0":"A4B91F8E-04B2-4A1C-B612-10F42C760989","1":"A844427101383089","2":"519.99","3":"14.0","4":"2023-03-10 22:51:49","5":"519.99","6":"false","7":"0.0","8":"1.0","9":"1","10":"2365.0","11":"0.0","12":"519.99","13":"519.99","14":"519.99","index":224},{"0":"3D4366E9-508C-4391-A2BD-1CD19A6A3E28","1":"A1899946665040220","2":"75.76","3":"10.0","4":"2023-03-23 08:35:31","9":"1","10":"366.0","11":"0.0","12":"75.76","13":"75.76","14":"75.76","index":225},{"0":"3D4366E9-508C-4391-A2BD-1CD19A6A3E28","1":"A1899946665040220","2":"75.76","3":"10.0","4":"2023-03-23 08:35:31","5":"75.76","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"75.76","13":"75.76","14":"75.76","index":226},{"0":"9E9BB950-7681-4213-B8D7-1E71AE232C43","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:55:40","9":"1","10":"366.0","11":"0.0","12":"4059.1899999999973","13":"4059.1899999999973","14":"144.97107142857132","index":227},{"0":"E112F541-98DE-420B-B009-FAA21420164B","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:45:00","9":"1","10":"366.0","11":"0.0","12":"459.7700000000001","13":"459.7700000000001","14":"19.990000000000006","index":228},{"0":"A9D4BF3B-D29D-4BDA-9209-AEED53CF20D3","1":"A1899946943634320","2":"189.99","3":"12.0","4":"2023-01-15 12:46:20","9":"1","10":"366.0","11":"0.0","12":"189.99","13":"189.99","14":"189.99","index":229},{"0":"A9D4BF3B-D29D-4BDA-9209-AEED53CF20D3","1":"A1899946943634320","2":"189.99","3":"12.0","4":"2023-01-15 12:46:20","5":"299.21525099999997","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"189.99","13":"189.99","14":"189.99","index":230},{"0":"B44556BB-1C72-4B05-B89B-8B078FE110FC","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:58:46","9":"1","10":"2365.0","11":"3.0","12":"140.13999999999996","13":"129.35999999999999","14":"5.389999999999999","index":231},{"0":"F6A4299A-ECE2-4DBA-BFC5-07E405117646","1":"A985157039468116","2":"719.0","3":"13.0","4":"2023-03-27 13:39:14","9":"1","10":"366.0","11":"0.0","12":"719.0","13":"719.0","14":"719.0","index":232},{"0":"F6A4299A-ECE2-4DBA-BFC5-07E405117646","1":"A985157039468116","2":"719.0","3":"13.0","4":"2023-03-27 13:39:14","5":"1132.3531","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"719.0","13":"719.0","14":"719.0","index":233},{"0":"84633227-12F0-4157-AEA8-4F844146BC47","1":"A985156984009293","2":"15.85","3":"9.0","4":"2023-01-06 17:45:55","9":"1","10":"366.0","11":"0.0","12":"143.04","13":"143.04","14":"71.52","index":234},{"0":"287F81B6-06F6-4C3B-8714-401FA70EC7DA","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:29:49","9":"1","10":"366.0","11":"0.0","12":"1025.81","13":"1025.81","14":"53.989999999999995","index":235},{"0":"19DB47CE-E027-4D8D-8439-ADCC4BBE9879","1":"A1899946873142120","2":"59.99","3":"13.0","4":"2023-01-03 18:42:51","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":236},{"0":"C00398AE-B078-415C-BB5D-1F29152F4B00","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:26:26","9":"1","10":"2365.0","11":"3.0","12":"53.9","13":"53.9","14":"5.39","index":237},{"0":"C00398AE-B078-415C-BB5D-1F29152F4B00","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:26:26","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"53.9","13":"53.9","14":"5.39","index":238},{"0":"813EDBBA-D2FF-4316-AA18-DCBCC923619D","1":"A1899947010059410","2":"73080.0","3":"23.0","4":"2023-03-07 14:23:01","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":239},{"0":"813EDBBA-D2FF-4316-AA18-DCBCC923619D","1":"A1899947010059410","2":"73080.0","3":"23.0","4":"2023-03-07 14:23:01","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":240},{"0":"4845B1D4-5FDF-49EA-B755-594CBEF3B4E6","1":"A1829582358242680","2":"239.51","3":"9.0","4":"2023-02-23 14:52:47","9":"1","10":"366.0","11":"0.0","12":"479.02","13":"479.02","14":"239.51","index":241},{"0":"4845B1D4-5FDF-49EA-B755-594CBEF3B4E6","1":"A1829582358242680","2":"239.51","3":"9.0","4":"2023-02-23 14:52:47","5":"239.51","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"479.02","13":"479.02","14":"239.51","index":242},{"0":"128EAA6F-8869-498D-8ADD-990F268B7F4D","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:35:59","9":"1","10":"2365.0","11":"3.0","12":"285.6699999999996","13":"274.88999999999965","14":"5.389999999999993","index":243},{"0":"D52E262C-77C6-4C20-B9FA-F5A408BED455","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:31:12","9":"1","10":"367.0","11":"0.0","12":"99.79999999999998","13":"99.79999999999998","14":"4.989999999999999","index":244},{"0":"04A6E453-2077-4703-BA51-A0803BBC7F94","1":"A564049508937862","2":"99.0","4":"2023-02-09 07:53:48","9":"1","10":"2365.0","11":"0.0","12":"99.0","13":"99.0","14":"99.0","index":245},{"0":"04A6E453-2077-4703-BA51-A0803BBC7F94","1":"A564049508937862","2":"99.0","4":"2023-02-09 07:53:48","5":"99.0","6":"false","7":"1.0","8":"1.0","9":"1","10":"2365.0","11":"0.0","12":"99.0","13":"99.0","14":"99.0","index":246},{"0":"043408BB-F09D-4E53-B3BB-A24E0AD4BDF6","1":"A844427390246047","2":"5.39","3":"5.0","4":"2023-02-25 11:17:46","9":"1","10":"2365.0","11":"3.0","12":"220.98999999999975","13":"210.20999999999978","14":"5.389999999999994","index":247},{"0":"FB41B881-4D5F-4357-9363-35FB4856F4B6","1":"A844427390246047","2":"5.39","3":"9.0","4":"2023-02-28 14:34:37","9":"1","10":"2365.0","11":"3.0","12":"592.8999999999988","13":"253.32999999999967","14":"5.389999999999989","index":248},{"0":"043408BB-F09D-4E53-B3BB-A24E0AD4BDF6","1":"A844427390246047","2":"5.39","3":"5.0","4":"2023-02-25 11:17:46","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"220.98999999999975","13":"210.20999999999978","14":"5.389999999999994","index":249},{"0":"63C571C9-4D3A-4A6F-84AF-7339ABF5FDDA","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:15:21","9":"1","10":"2365.0","11":"3.0","12":"177.86999999999986","13":"167.0899999999999","14":"5.389999999999996","index":250},{"0":"63C571C9-4D3A-4A6F-84AF-7339ABF5FDDA","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:15:21","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"177.86999999999986","13":"167.0899999999999","14":"5.389999999999996","index":251},{"0":"E08DDC49-612E-4227-90CA-79EBCF477113","1":"A1055520836257310","2":"5.04","3":"5.0","4":"2023-02-27 10:37:29","5":"5.04","6":"false","7":"1.0","8":"0.0","9":"0","10":"1555.0","11":"0.0","12":"5.04","13":"5.04","14":"5.04","index":252},{"0":"C43487B3-4C79-46C9-B163-EB704D6F28A1","1":"A1899947009494720","2":"538.0","3":"13.0","4":"2023-03-17 04:53:51","9":"1","10":"366.0","11":"0.0","12":"538.0","13":"538.0","14":"538.0","index":253},{"0":"C43487B3-4C79-46C9-B163-EB704D6F28A1","1":"A1899947009494720","2":"538.0","3":"13.0","4":"2023-03-17 04:53:51","5":"725.1164","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"538.0","13":"538.0","14":"538.0","index":254},{"0":"45544389-E451-4D60-9BF6-261176776209","1":"A914801018161878","2":"99.99","3":"3.0","4":"2023-03-19 11:28:54","9":"1","10":"366.0","11":"0.0","12":"99.99","13":"99.99","14":"99.99","index":255},{"0":"45544389-E451-4D60-9BF6-261176776209","1":"A914801018161878","2":"99.99","3":"3.0","4":"2023-03-19 11:28:54","5":"99.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"99.99","13":"99.99","14":"99.99","index":256},{"0":"ADB8EB04-9D65-43A5-9CF1-5F86D904FC18","1":"A1055521380537310","2":"129.58","3":"13.0","4":"2023-01-03 18:59:06","9":"1","10":"366.0","11":"0.0","12":"453.5300000000001","13":"453.5300000000001","14":"113.38250000000002","index":257},{"0":"F2CD36F5-0FC9-4DBF-9D5A-D6B043795C0C","1":"A914801007105746","2":"83.06","3":"8.0","4":"2023-01-23 16:39:58","9":"1","10":"367.0","11":"0.0","12":"83.06","13":"83.06","14":"83.06","index":258},{"0":"A6447B92-1029-48D5-9B03-086284B006CB","1":"A1055521395048230","2":"83.14","3":"12.0","4":"2023-01-26 18:16:50","9":"1","10":"367.0","11":"0.0","12":"164.74","13":"164.74","14":"82.37","index":259},{"0":"A6447B92-1029-48D5-9B03-086284B006CB","1":"A1055521395048230","2":"83.14","3":"12.0","4":"2023-01-26 18:16:50","5":"83.14","6":"false","7":"0.0","8":"1.0","9":"1","10":"367.0","11":"0.0","12":"164.74","13":"164.74","14":"82.37","index":260},{"0":"F6D8E9B1-CC74-48F9-B46C-F74DE6E82491","1":"A1759220976223180","2":"1079.0","3":"12.0","4":"2023-02-12 16:12:13","9":"1","10":"477.0","11":"0.0","12":"1079.0","13":"1079.0","14":"1079.0","index":261},{"0":"F6D8E9B1-CC74-48F9-B46C-F74DE6E82491","1":"A1759220976223180","2":"1079.0","3":"12.0","4":"2023-02-12 16:12:13","5":"542.25145","6":"false","7":"1.0","8":"0.0","9":"1","10":"477.0","11":"0.0","12":"1079.0","13":"1079.0","14":"1079.0","index":262},{"0":"5F25E7A1-C8CF-4262-9365-D8D8BBADBD2A","1":"A1055520529067390","2":"163.16","3":"21.0","4":"2023-02-18 06:00:32","9":"1","10":"708.0","11":"0.0","12":"163.16","13":"163.16","14":"163.16","index":263},{"0":"5F25E7A1-C8CF-4262-9365-D8D8BBADBD2A","1":"A1055520529067390","2":"163.16","3":"21.0","4":"2023-02-18 06:00:32","5":"163.16","6":"false","7":"0.0","8":"1.0","9":"1","10":"708.0","11":"0.0","12":"163.16","13":"163.16","14":"163.16","index":264},{"0":"2389B79E-4A88-416B-BF63-86DDD4CE0095","1":"A985156821621035","2":"152.03","3":"22.0","4":"2023-02-22 05:28:18","9":"1","10":"366.0","11":"0.0","12":"304.06","13":"304.06","14":"152.03","index":265},{"0":"2389B79E-4A88-416B-BF63-86DDD4CE0095","1":"A985156821621035","2":"152.03","3":"22.0","4":"2023-02-22 05:28:18","5":"152.03","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"304.06","13":"304.06","14":"152.03","index":266},{"0":"3FC40238-6110-46A6-BE38-CF80320791A1","1":"A985157015268058","2":"4.99","3":"15.0","4":"2023-02-27 20:28:41","9":"1","10":"367.0","11":"0.0","12":"129.73999999999995","13":"129.73999999999995","14":"4.989999999999998","index":267},{"0":"C2AE42D7-53F6-424F-8470-4B2EF42612EB","1":"A1688853662168790","2":"62580.0","3":"12.0","4":"2023-03-13 04:19:44","9":"1","10":"366.0","11":"0.0","12":"62580.0","13":"62580.0","14":"62580.0","index":268},{"0":"A8410FBF-9EE3-4200-A0ED-05631423E0F9","1":"A985156996696141","2":"1199.0","3":"9.0","4":"2023-01-25 08:58:29","5":"187.8833","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"1199.0","13":"1199.0","14":"1199.0","index":269},{"0":"EA452A8D-BA16-42A9-BC9E-D2A1ECE62675","1":"A844427390246047","2":"5.39","3":"13.0","4":"2023-02-24 19:13:51","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"21.56","13":"21.56","14":"5.39","index":270},{"0":"F70F7BCA-6F5F-4C6C-BEE4-6C92B53E5816","1":"A1829582586398540","2":"587.0","3":"21.0","4":"2023-02-03 21:10:03","9":"1","10":"366.0","11":"0.0","12":"587.0","13":"587.0","14":"587.0","index":271},{"0":"8BD69B6D-0367-4EED-AE01-5A28B815C999","1":"A1899945509487400","2":"10.81","3":"20.0","4":"2023-02-26 03:01:05","9":"1","10":"1310.0","11":"0.0","12":"16.21","13":"16.21","14":"8.105","index":272},{"0":"8BD69B6D-0367-4EED-AE01-5A28B815C999","1":"A1899945509487400","2":"10.81","3":"20.0","4":"2023-02-26 03:01:05","5":"10.81","6":"false","7":"1.0","8":"0.0","9":"1","10":"1310.0","11":"0.0","12":"16.21","13":"16.21","14":"8.105","index":273},{"0":"4DA0DA18-1900-48F9-8FAD-2E31FA77C64F","1":"A1829581772655040","2":"1499.0","3":"14.0","4":"2023-01-16 21:02:41","9":"1","10":"366.0","11":"0.0","12":"1499.0","13":"1499.0","14":"1499.0","index":274},{"0":"4DA0DA18-1900-48F9-8FAD-2E31FA77C64F","1":"A1829581772655040","2":"1499.0","3":"14.0","4":"2023-01-16 21:02:41","5":"118.002779","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"1499.0","13":"1499.0","14":"1499.0","index":275},{"0":"EE63EE82-8A5F-4422-8CFA-3CF0C48D05FD","1":"A1688853524321290","2":"49.98","3":"19.0","4":"2023-01-05 00:32:25","9":"1","10":"366.0","11":"0.0","12":"74.97","13":"74.97","14":"37.485","index":276},{"0":"673A772C-71A1-4541-8468-7298A4FC0844","1":"A1899946873128090","2":"75.58","3":"20.0","4":"2023-02-15 02:34:45","9":"1","10":"366.0","11":"0.0","12":"75.58","13":"75.58","14":"75.58","index":277},{"0":"EC8BF756-2E01-4485-9B7E-12363CAAE435","1":"A1688853432603810","2":"238.14","3":"5.0","4":"2023-02-22 12:12:28","9":"1","10":"366.0","11":"0.0","12":"238.14","13":"238.14","14":"238.14","index":278},{"0":"EC8BF756-2E01-4485-9B7E-12363CAAE435","1":"A1688853432603810","2":"238.14","3":"5.0","4":"2023-02-22 12:12:28","5":"238.14","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"238.14","13":"238.14","14":"238.14","index":279},{"0":"D0F517D0-F18C-41DE-A53F-65C3264C4C5E","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:13:40","9":"1","10":"366.0","11":"0.0","12":"485.91","13":"485.91","14":"53.99","index":280},{"0":"8B6010EB-EB1A-4AFE-9E41-0EB3FFF81CC7","1":"A844428024841325","2":"149.79","3":"6.0","4":"2023-02-14 12:06:06","9":"1","10":"413.0","11":"0.0","12":"149.79","13":"149.79","14":"149.79","index":281},{"0":"8B6010EB-EB1A-4AFE-9E41-0EB3FFF81CC7","1":"A844428024841325","2":"149.79","3":"6.0","4":"2023-02-14 12:06:06","5":"149.79","6":"false","7":"1.0","8":"0.0","9":"1","10":"413.0","11":"0.0","12":"149.79","13":"149.79","14":"149.79","index":282},{"0":"A67953E3-06C2-413F-BD06-8473F7C202F4","1":"A1055521435190160","2":"281.37","3":"17.0","4":"2023-03-29 23:20:24","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":283},{"0":"A67953E3-06C2-413F-BD06-8473F7C202F4","1":"A1055521435190160","2":"281.37","3":"17.0","4":"2023-03-29 23:20:24","5":"280.207942","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":284},{"0":"E2D03B65-3D85-4453-902F-6AED9194A7AD","1":"A1899946969878610","2":"65.54","3":"13.0","4":"2023-01-30 18:38:24","9":"1","10":"366.0","11":"0.0","12":"65.54","13":"65.54","14":"65.54","index":285},{"0":"E2D03B65-3D85-4453-902F-6AED9194A7AD","1":"A1899946969878610","2":"65.54","3":"13.0","4":"2023-01-30 18:38:24","5":"65.54","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"65.54","13":"65.54","14":"65.54","index":286},{"0":"43BF9531-63BE-46A1-AA90-CFD08FEB3BBB","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:41:50","9":"1","10":"367.0","11":"0.0","12":"74.85000000000001","13":"74.85000000000001","14":"4.99","index":287},{"0":"783D965E-660F-4591-8AB5-3F3327E0B8AB","1":"A1759222095873190","2":"160.47","3":"11.0","4":"2023-01-03 17:41:15","9":"1","10":"366.0","11":"0.0","12":"320.94","13":"320.94","14":"106.98","index":288},{"0":"ECC38318-E094-4FAA-9638-6C664AB49DB8","1":"A985157000491580","2":"299.0","3":"19.0","4":"2023-02-05 10:03:12","9":"1","10":"366.0","11":"0.0","12":"698.99","13":"698.99","14":"349.495","index":289},{"0":"29111CE5-5A24-49BB-A358-0793FC585801","1":"A985157010264242","2":"73080.0","3":"15.0","4":"2023-02-19 07:09:04","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":290},{"0":"29111CE5-5A24-49BB-A358-0793FC585801","1":"A985157010264242","2":"73080.0","3":"15.0","4":"2023-02-19 07:09:04","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":291},{"0":"0B787893-9340-4DE8-9CEF-AFFCD6BB8AB0","1":"A914800689334401","2":"109.99","3":"10.0","4":"2023-03-04 18:46:15","9":"1","10":"372.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":292},{"0":"B117C813-5EB9-4747-92B5-C52D520AC13F","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:36:39","9":"1","10":"366.0","11":"0.0","12":"1295.76","13":"1295.76","14":"53.99","index":293},{"0":"4560AC71-6EB0-4590-8C1D-F39E8C2B743A","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:48:57","9":"1","10":"2365.0","11":"3.0","12":"339.5699999999995","13":"328.7899999999995","14":"5.389999999999992","index":294},{"0":"A02AA3CD-C055-4B0D-93B3-5D84CFDED0D7","1":"A1759222251254350","2":"516.24","3":"12.0","4":"2023-02-21 17:46:31","9":"1","10":"368.0","11":"0.0","12":"516.24","13":"516.24","14":"516.24","index":295},{"0":"B40CF2C8-A5D3-439B-BEC7-E8E70A0E3317","1":"A1759222234549810","2":"73080.0","3":"19.0","4":"2023-03-01 10:39:15","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":296},{"0":"B40CF2C8-A5D3-439B-BEC7-E8E70A0E3317","1":"A1759222234549810","2":"73080.0","3":"19.0","4":"2023-03-01 10:39:15","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":297},{"0":"DA98CDF2-61C3-44C2-9ABA-1364BB7671AF","1":"A914800150734044","2":"593.58","3":"16.0","4":"2023-03-13 22:02:01","9":"1","10":"2365.0","11":"0.0","12":"593.58","13":"593.58","14":"593.58","index":298},{"0":"DA98CDF2-61C3-44C2-9ABA-1364BB7671AF","1":"A914800150734044","2":"593.58","3":"16.0","4":"2023-03-13 22:02:01","5":"593.58","6":"false","7":"0.0","8":"2.0","9":"1","10":"2365.0","11":"0.0","12":"593.58","13":"593.58","14":"593.58","index":299},{"0":"53F9A1F3-DB12-474B-8E9B-E6ADF22699AF","1":"A914800996051170","2":"139.99","3":"17.0","4":"2023-01-07 22:30:40","9":"1","10":"366.0","11":"0.0","12":"139.99","13":"139.99","14":"139.99","index":300},{"0":"C61F74AD-F654-4042-9DFF-567978FA7CF7","1":"A1829582571089880","2":"233.19","3":"21.0","4":"2023-01-28 05:57:11","5":"233.19","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"699.5699999999999","13":"699.5699999999999","14":"233.18999999999997","index":301},{"0":"B887357A-871E-425D-B17D-B1E7DF59A7EC","1":"A914801011218316","2":"224.11","3":"7.0","4":"2023-01-29 13:19:28","9":"1","10":"366.0","11":"0.0","12":"224.11","13":"224.11","14":"224.11","index":302},{"0":"B887357A-871E-425D-B17D-B1E7DF59A7EC","1":"A914801011218316","2":"224.11","3":"7.0","4":"2023-01-29 13:19:28","5":"224.11","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"224.11","13":"224.11","14":"224.11","index":303},{"0":"34DE06CA-82E2-44D3-8E83-E650A493EE27","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:50:42","9":"1","10":"366.0","11":"0.0","12":"7958.4100000000035","13":"7958.4100000000035","14":"147.37796296296304","index":304},{"0":"E904965B-8DFE-4819-80DF-FE6E0F50F4E4","1":"A914801013785993","2":"95.02","3":"9.0","4":"2023-02-02 15:05:15","9":"1","10":"371.0","11":"0.0","12":"95.02","13":"95.02","14":"95.02","index":305},{"0":"E904965B-8DFE-4819-80DF-FE6E0F50F4E4","1":"A914801013785993","2":"95.02","3":"9.0","4":"2023-02-02 15:05:15","5":"95.02","6":"false","7":"0.0","8":"1.0","9":"1","10":"371.0","11":"0.0","12":"95.02","13":"95.02","14":"95.02","index":306},{"0":"86EB854A-E34C-444F-92F2-3567FEEA91A3","1":"A1759222219152450","2":"237.04","3":"10.0","4":"2023-01-18 17:19:48","9":"1","10":"366.0","11":"0.0","12":"237.04","13":"237.04","14":"237.04","index":307},{"0":"86EB854A-E34C-444F-92F2-3567FEEA91A3","1":"A1759222219152450","2":"237.04","3":"10.0","4":"2023-01-18 17:19:48","5":"237.04","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"237.04","13":"237.04","14":"237.04","index":308},{"0":"2CBAE444-87AF-4760-9B86-846E0F5A8DBF","1":"A1759221413220100","2":"999999.0","3":"19.0","4":"2023-01-29 12:43:18","9":"1","10":"366.0","11":"0.0","12":"999999.0","13":"999999.0","14":"999999.0","index":309},{"0":"2CBAE444-87AF-4760-9B86-846E0F5A8DBF","1":"A1759221413220100","2":"999999.0","3":"19.0","4":"2023-01-29 12:43:18","5":"102.999897","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"999999.0","13":"999999.0","14":"999999.0","index":310},{"0":"D74E0304-9D1B-40AC-8418-33C7FD7FDBD9","1":"A844428056745591","2":"249.95","3":"12.0","4":"2023-02-16 20:40:53","9":"1","10":"366.0","11":"0.0","12":"299.94","13":"299.94","14":"149.97","index":311},{"0":"E720050B-91AD-4ECE-8F88-CA91D6A18E39","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:41:10","9":"1","10":"2365.0","11":"3.0","12":"307.22999999999956","13":"296.4499999999996","14":"5.389999999999993","index":312},{"0":"96AEA45B-C521-487A-890A-A5EFD5109973","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-28 13:16:17","9":"1","10":"2365.0","11":"3.0","12":"555.1699999999989","13":"215.59999999999977","14":"5.38999999999999","index":313},{"0":"E720050B-91AD-4ECE-8F88-CA91D6A18E39","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:41:10","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"307.22999999999956","13":"296.4499999999996","14":"5.389999999999993","index":314},{"0":"96AEA45B-C521-487A-890A-A5EFD5109973","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-28 13:16:17","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"555.1699999999989","13":"215.59999999999977","14":"5.38999999999999","index":315},{"0":"375FCC05-7C62-492F-92F5-53AF0EEDE57A","1":"A1759222234549920","2":"73080.0","3":"8.0","4":"2023-03-05 23:38:47","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":316},{"0":"375FCC05-7C62-492F-92F5-53AF0EEDE57A","1":"A1759222234549920","2":"73080.0","3":"8.0","4":"2023-03-05 23:38:47","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":317},{"0":"43C9065E-A514-4250-85E7-77C8768EB061","1":"A985157038112964","2":"150.14","3":"21.0","4":"2023-03-26 03:20:21","9":"1","10":"366.0","11":"0.0","12":"150.14","13":"150.14","14":"150.14","index":318},{"0":"43C9065E-A514-4250-85E7-77C8768EB061","1":"A985157038112964","2":"150.14","3":"21.0","4":"2023-03-26 03:20:21","5":"150.14","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"150.14","13":"150.14","14":"150.14","index":319},{"0":"56176B76-FFD4-4252-8BAD-C8F0E105EE2E","1":"A985157008993462","2":"105.48","3":"5.0","4":"2023-02-17 11:52:43","9":"1","10":"366.0","11":"0.0","12":"105.48","13":"105.48","14":"105.48","index":320},{"0":"EA959383-BCF0-465E-AF30-DC2E5938E855","1":"A844427218518467","2":"157.94","3":"15.0","4":"2023-02-17 21:13:23","9":"1","10":"1274.0","11":"0.0","12":"157.94","13":"157.94","14":"157.94","index":321},{"0":"EA959383-BCF0-465E-AF30-DC2E5938E855","1":"A844427218518467","2":"157.94","3":"15.0","4":"2023-02-17 21:13:23","5":"157.94","6":"false","7":"0.0","8":"1.0","9":"1","10":"1274.0","11":"0.0","12":"157.94","13":"157.94","14":"157.94","index":322},{"0":"C37B550B-F3BB-4C9A-9A7F-5133D4C9496C","1":"A1829582514718140","2":"238.14","3":"0.0","4":"2023-02-18 07:08:45","9":"1","10":"366.0","11":"0.0","12":"238.14","13":"238.14","14":"238.14","index":323},{"0":"C37B550B-F3BB-4C9A-9A7F-5133D4C9496C","1":"A1829582514718140","2":"238.14","3":"0.0","4":"2023-02-18 07:08:45","5":"238.14","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"238.14","13":"238.14","14":"238.14","index":324},{"0":"A440EC4D-B52B-4F0E-949A-EB6699D67175","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:19:54","9":"1","10":"2365.0","11":"3.0","12":"253.32999999999967","13":"242.5499999999997","14":"5.389999999999993","index":325},{"0":"E32B5896-DD3D-4C40-BFAB-75C27190A55C","1":"A985156972392398","2":"152.94","3":"2.0","4":"2023-03-13 08:54:53","9":"1","10":"366.0","11":"0.0","12":"152.94","13":"152.94","14":"152.94","index":326},{"0":"E32B5896-DD3D-4C40-BFAB-75C27190A55C","1":"A985156972392398","2":"152.94","3":"2.0","4":"2023-03-13 08:54:53","5":"152.94","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"152.94","13":"152.94","14":"152.94","index":327},{"0":"D12B9922-D2E5-4BD2-BC6C-7486816F0AD7","1":"A985156993509507","2":"75.58","3":"9.0","4":"2023-01-19 17:28:50","9":"1","10":"366.0","11":"0.0","12":"75.58","13":"75.58","14":"75.58","index":328},{"0":"D12B9922-D2E5-4BD2-BC6C-7486816F0AD7","1":"A985156993509507","2":"75.58","3":"9.0","4":"2023-01-19 17:28:50","5":"75.58","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"75.58","13":"75.58","14":"75.58","index":329},{"0":"35AC8DA0-24ED-4461-B732-7994EAC7AD2C","1":"A1055521406038580","2":"73080.0","3":"3.0","4":"2023-02-22 18:23:47","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":330},{"0":"B3B0727B-ADF3-4621-9DFC-D0C8CB6E6F26","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:30:41","9":"1","10":"367.0","11":"0.0","12":"99.79999999999998","13":"99.79999999999998","14":"4.989999999999999","index":331},{"0":"6EB304F3-0D0F-4767-BFBB-2ACD515CAA42","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:37:35","9":"1","10":"2365.0","11":"3.0","12":"296.4499999999996","13":"285.6699999999996","14":"5.389999999999993","index":332},{"0":"389398A4-D295-416A-91AE-E890749EFC58","1":"A1055520836257310","2":"5.04","3":"5.0","4":"2023-02-28 10:30:28","9":"1","10":"1555.0","11":"0.0","12":"20.16","13":"20.16","14":"5.04","index":333},{"0":"E11DC416-B4F6-4637-9D28-1DAC96F009DA","1":"A1829582586398540","2":"119.0","4":"2023-01-20 09:25:52","9":"1","10":"366.0","11":"0.0","12":"905.79","13":"905.79","14":"452.895","index":334},{"0":"E11DC416-B4F6-4637-9D28-1DAC96F009DA","1":"A1829582586398540","2":"119.0","4":"2023-01-20 09:25:52","5":"160.3882","6":"false","7":"1.0","8":"4.0","9":"1","10":"366.0","11":"0.0","12":"905.79","13":"905.79","14":"452.895","index":335},{"0":"E6789403-5C39-4D44-BA7C-3A1F481E718B","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:37:59","9":"1","10":"366.0","11":"0.0","12":"1349.75","13":"1349.75","14":"53.99","index":336},{"0":"7F739A3C-DA4E-4B79-BF65-7D33BFD1BC39","1":"A844428039942859","2":"479.0","3":"22.0","4":"2023-01-16 23:06:38","9":"1","10":"366.0","11":"0.0","12":"479.0","13":"479.0","14":"479.0","index":337},{"0":"7F739A3C-DA4E-4B79-BF65-7D33BFD1BC39","1":"A844428039942859","2":"479.0","3":"22.0","4":"2023-01-16 23:06:38","5":"754.3771","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"479.0","13":"479.0","14":"479.0","index":338},{"0":"834A0619-7166-452A-9D31-1224EBB54505","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:50:48","9":"1","10":"2365.0","11":"3.0","12":"404.2499999999993","13":"393.46999999999935","14":"5.389999999999991","index":339},{"0":"834A0619-7166-452A-9D31-1224EBB54505","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:50:48","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"404.2499999999993","13":"393.46999999999935","14":"5.389999999999991","index":340},{"0":"2E8C3C5B-4ECB-451D-8F50-BD946950FAC4","1":"A1899945509487400","2":"5.4","3":"20.0","4":"2023-02-26 02:50:39","9":"1","10":"1310.0","11":"0.0","12":"5.4","13":"5.4","14":"5.4","index":341},{"0":"2959008D-82E5-4A92-ABAD-15BF31D4D860","1":"A1055521428449680","2":"278.88","3":"22.0","4":"2023-03-22 03:37:49","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":342},{"0":"2959008D-82E5-4A92-ABAD-15BF31D4D860","1":"A1055521428449680","2":"278.88","3":"22.0","4":"2023-03-22 03:37:49","5":"277.72822599999995","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":343},{"0":"5BDE388B-8A30-4D45-8122-F7CA341442C8","1":"A914800889932462","2":"53.36","3":"6.0","4":"2023-02-16 13:00:25","9":"1","10":"366.0","11":"0.0","12":"106.72","13":"106.72","14":"53.36","index":344},{"0":"A7FC7EAE-BB1E-4E93-8BFE-E9C1B71792AB","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:52:49","9":"1","10":"2365.0","11":"3.0","12":"420.4199999999993","13":"409.6399999999993","14":"5.389999999999991","index":345},{"0":"D024ED6A-545B-44B2-827E-D7B9D98C592D","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:48:23","9":"1","10":"2365.0","11":"3.0","12":"377.2999999999994","13":"366.5199999999994","14":"5.389999999999991","index":346},{"0":"EFAE71B3-0EB4-413B-BFA1-541328AD3FB1","1":"A1055520836257310","2":"5.04","3":"6.0","4":"2023-02-28 12:20:56","9":"1","10":"1555.0","11":"0.0","12":"40.32","13":"40.32","14":"5.04","index":347},{"0":"EFAE71B3-0EB4-413B-BFA1-541328AD3FB1","1":"A1055520836257310","2":"5.04","3":"6.0","4":"2023-02-28 12:20:56","5":"5.04","6":"false","7":"1.0","8":"0.0","9":"1","10":"1555.0","11":"0.0","12":"40.32","13":"40.32","14":"5.04","index":348},{"0":"DDE0B1F6-40B2-44CA-B0A7-AE46453D51A0","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:25:29","9":"1","10":"366.0","11":"0.0","12":"917.83","13":"917.83","14":"53.99","index":349},{"0":"74E7FBFA-551A-4324-89BA-6CD22AA7B832","1":"A914800996051170","2":"215.64","3":"17.0","4":"2023-01-07 22:36:31","9":"1","10":"366.0","11":"0.0","12":"355.63","13":"355.63","14":"177.815","index":350},{"0":"74E7FBFA-551A-4324-89BA-6CD22AA7B832","1":"A914800996051170","2":"215.64","3":"17.0","4":"2023-01-07 22:36:31","5":"215.64","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"355.63","13":"355.63","14":"177.815","index":351},{"0":"7833DDCC-84C1-4818-8D57-D635B858475C","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-26 11:27:57","9":"1","10":"2365.0","11":"3.0","12":"549.779999999999","13":"538.999999999999","14":"5.38999999999999","index":352},{"0":"7833DDCC-84C1-4818-8D57-D635B858475C","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-26 11:27:57","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"549.779999999999","13":"538.999999999999","14":"5.38999999999999","index":353},{"0":"138D8A92-4C50-4A5F-BA5B-7E6517F3B0B9","1":"A844428087822266","2":"278.88","3":"10.0","4":"2023-03-27 15:48:08","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":354},{"0":"CACD0238-79D3-4506-B78A-F24E494B3AAC","1":"A1055521395048230","2":"81.6","3":"2.0","4":"2023-01-25 08:02:13","9":"1","10":"367.0","11":"0.0","12":"81.6","13":"81.6","14":"81.6","index":355},{"0":"8FBB1DE1-60CE-4F82-A2E8-8CBAB946FED7","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:19:02","9":"1","10":"2365.0","11":"3.0","12":"194.03999999999982","13":"183.25999999999985","14":"5.389999999999995","index":356},{"0":"E8CA30F7-3805-48E0-A5E6-6FD71ACF2BE2","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:11:36","9":"1","10":"2365.0","11":"3.0","12":"167.0899999999999","13":"156.30999999999992","14":"5.389999999999996","index":357},{"0":"88CA2590-A2EA-4C19-B7E0-6B5C5DFFAABB","1":"A844428041278796","2":"789.87","3":"19.0","4":"2023-01-19 00:39:54","9":"1","10":"366.0","11":"0.0","12":"789.87","13":"789.87","14":"789.87","index":358},{"0":"88CA2590-A2EA-4C19-B7E0-6B5C5DFFAABB","1":"A844428041278796","2":"789.87","3":"19.0","4":"2023-01-19 00:39:54","5":"786.607837","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"789.87","13":"789.87","14":"789.87","index":359},{"0":"F4B5B955-8DD8-4BB5-9281-66E87013C42D","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:07:16","9":"1","10":"366.0","11":"0.0","12":"269.95","13":"269.95","14":"53.989999999999995","index":360},{"0":"A45B5295-84D3-47CD-AFA3-E074A9DF9EF6","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:43:38","9":"1","10":"366.0","11":"0.0","12":"1565.71","13":"1565.71","14":"53.99","index":361},{"0":"F4B5B955-8DD8-4BB5-9281-66E87013C42D","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:07:16","5":"53.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"269.95","13":"269.95","14":"53.989999999999995","index":362},{"0":"E7F77A61-785B-4E7D-8C99-6A237E368BC5","1":"A1899946895455190","2":"19.99","3":"12.0","4":"2023-01-13 17:22:25","9":"1","10":"366.0","11":"0.0","12":"39.98","13":"39.98","14":"19.99","index":363},{"0":"640D2C62-1A20-47FC-9443-79F93E1896C2","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:30:42","9":"1","10":"2365.0","11":"3.0","12":"75.46","13":"75.46","14":"5.39","index":364},{"0":"640D2C62-1A20-47FC-9443-79F93E1896C2","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:30:42","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"75.46","13":"75.46","14":"5.39","index":365},{"0":"CB9D1331-694E-46E6-8A0A-0C16404FD315","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:46:26","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"590.1299999999999","13":"590.1299999999999","14":"65.57","index":366},{"0":"2CABEE6F-6606-48AA-87C9-D96C328A8C5A","1":"A844428046730497","2":"278.88","3":"11.0","4":"2023-01-29 16:24:29","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":367},{"0":"2CABEE6F-6606-48AA-87C9-D96C328A8C5A","1":"A844428046730497","2":"278.88","3":"11.0","4":"2023-01-29 16:24:29","5":"277.72822599999995","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":368},{"0":"17D5B8ED-6683-4647-B551-D2BBE8C798E5","1":"A914800889937190","2":"53.11","3":"13.0","4":"2023-02-16 18:28:59","9":"1","10":"366.0","11":"0.0","12":"53.11","13":"53.11","14":"53.11","index":369},{"0":"17D5B8ED-6683-4647-B551-D2BBE8C798E5","1":"A914800889937190","2":"53.11","3":"13.0","4":"2023-02-16 18:28:59","5":"53.11","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"53.11","13":"53.11","14":"53.11","index":370},{"0":"AAC80D62-E482-4488-801A-941B8B43E034","1":"A844427926468579","2":"54.11","3":"0.0","4":"2023-02-17 06:06:01","9":"1","10":"366.0","11":"0.0","12":"108.22","13":"108.22","14":"54.11","index":371},{"0":"AAC80D62-E482-4488-801A-941B8B43E034","1":"A844427926468579","2":"54.11","3":"0.0","4":"2023-02-17 06:06:01","5":"54.11","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"108.22","13":"108.22","14":"54.11","index":372},{"0":"5ABD00A4-C232-4D58-8A78-63ABE223F182","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:36:58","9":"1","10":"2365.0","11":"3.0","12":"113.19","13":"113.19","14":"5.39","index":373},{"0":"88869C17-D26A-476A-95C9-A6E93170CA62","1":"A1688853596360510","2":"65.16","3":"0.0","4":"2023-02-17 06:07:08","9":"1","10":"366.0","11":"0.0","12":"65.16","13":"65.16","14":"65.16","index":374},{"0":"88869C17-D26A-476A-95C9-A6E93170CA62","1":"A1688853596360510","2":"65.16","3":"0.0","4":"2023-02-17 06:07:08","5":"65.16","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"65.16","13":"65.16","14":"65.16","index":375},{"0":"33A4A802-37D2-454B-9960-1B92DD51B771","1":"A1899947008864830","2":"137.3","3":"11.0","4":"2023-01-14 19:07:33","9":"1","10":"366.0","11":"0.0","12":"137.3","13":"137.3","14":"137.3","index":376},{"0":"33A4A802-37D2-454B-9960-1B92DD51B771","1":"A1899947008864830","2":"137.3","3":"11.0","4":"2023-01-14 19:07:33","5":"137.3","6":"false","7":"5.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"137.3","13":"137.3","14":"137.3","index":377},{"0":"AD22A063-D3FB-4623-B7A2-69A6C8E5CEE0","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:53:28","9":"1","10":"366.0","11":"0.0","12":"3909.2199999999975","13":"3909.2199999999975","14":"144.78592592592582","index":378},{"0":"BE25CE28-7142-4EB4-8157-77654E4816D0","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:24:35","9":"1","10":"366.0","11":"0.0","12":"6458.710000000001","13":"6458.710000000001","14":"146.78886363636366","index":379},{"0":"D3F14FD5-C8C3-4401-8053-A5DCB4086473","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:30:44","9":"1","10":"366.0","11":"0.0","12":"179.97","13":"179.97","14":"59.99","index":380},{"0":"D02E8FB4-3170-4C47-8886-ABE470F197C3","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:03:30","9":"1","10":"366.0","11":"0.0","12":"1659.67","13":"1659.67","14":"138.30583333333334","index":381},{"0":"8FD67A34-3815-4050-AD67-DF29BC551351","1":"A844428045623555","2":"80.79","3":"18.0","4":"2023-01-27 23:59:04","9":"1","10":"366.0","11":"0.0","12":"80.79","13":"80.79","14":"80.79","index":382},{"0":"8FD67A34-3815-4050-AD67-DF29BC551351","1":"A844428045623555","2":"80.79","3":"18.0","4":"2023-01-27 23:59:04","5":"80.79","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"80.79","13":"80.79","14":"80.79","index":383},{"0":"0D385B17-41FC-4D3B-8C36-BFD0FAF6AA20","1":"A1688853647452210","2":"423.99","3":"13.0","4":"2023-02-09 18:26:33","9":"1","10":"366.0","11":"0.0","12":"847.98","13":"847.98","14":"423.99","index":384},{"0":"03D4C75E-6EE7-421E-85F5-BD0BF2158BD4","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:33:21","9":"1","10":"2365.0","11":"3.0","12":"280.27999999999963","13":"269.49999999999966","14":"5.389999999999993","index":385},{"0":"8625D6A1-D881-4521-8246-5F1B2381D71D","1":"A1899946865209040","2":"79380.0","3":"9.0","4":"2023-03-11 00:59:31","9":"1","10":"366.0","11":"0.0","12":"158760.0","13":"158760.0","14":"79380.0","index":386},{"0":"8625D6A1-D881-4521-8246-5F1B2381D71D","1":"A1899946865209040","2":"79380.0","3":"9.0","4":"2023-03-11 00:59:31","5":"875.5614","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"158760.0","13":"158760.0","14":"79380.0","index":387},{"0":"E44AA2DD-8814-4746-839A-4A3D8EE5F243","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:12:20","9":"1","10":"366.0","11":"0.0","12":"431.92","13":"431.92","14":"53.99","index":388},{"0":"DE6C7FAE-FE3C-4316-8585-6C5942D43B19","1":"A1055521395382470","2":"153.29","3":"20.0","4":"2023-02-06 04:24:03","9":"1","10":"366.0","11":"0.0","12":"153.29","13":"153.29","14":"153.29","index":389},{"0":"DE6C7FAE-FE3C-4316-8585-6C5942D43B19","1":"A1055521395382470","2":"153.29","3":"20.0","4":"2023-02-06 04:24:03","5":"153.29","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"153.29","13":"153.29","14":"153.29","index":390},{"0":"8F080C35-BA5C-41A5-8169-0EDBF1F27219","1":"A1759222234549860","2":"62580.0","3":"11.0","4":"2023-03-03 02:24:02","9":"1","10":"366.0","11":"0.0","12":"125160.0","13":"125160.0","14":"62580.0","index":391},{"0":"5AF9F5B3-7386-426B-878E-3F1B7BBF48D9","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:56:55","9":"1","10":"366.0","11":"0.0","12":"1209.76","13":"1209.76","14":"134.4177777777778","index":392},{"0":"73F74DE9-9679-417D-A7A8-6D1D74769D9D","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:28:23","9":"1","10":"366.0","11":"0.0","12":"6758.6500000000015","13":"6758.6500000000015","14":"146.9271739130435","index":393},{"0":"52961216-05ED-4183-8EF5-5237F0352C41","1":"A1899946944829860","2":"59.99","3":"7.0","4":"2023-02-16 13:17:31","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":394},{"0":"9824B652-A52C-42EA-8BF0-A23D678B9527","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:18:49","9":"1","10":"2365.0","11":"3.0","12":"242.5499999999997","13":"231.76999999999973","14":"5.3899999999999935","index":395},{"0":"9824B652-A52C-42EA-8BF0-A23D678B9527","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:18:49","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"242.5499999999997","13":"231.76999999999973","14":"5.3899999999999935","index":396},{"0":"B2F718B0-6639-44C6-9E86-04BCCB6765B6","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:15:03","9":"1","10":"366.0","11":"0.0","12":"539.9","13":"539.9","14":"53.989999999999995","index":397},{"0":"0C8F4CEC-54F7-44BE-A57B-CC2382BA5B2B","1":"A1688853524309620","2":"119.98","3":"22.0","4":"2023-02-15 04:46:23","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"119.98","index":398},{"0":"0C8F4CEC-54F7-44BE-A57B-CC2382BA5B2B","1":"A1688853524309620","2":"119.98","3":"22.0","4":"2023-02-15 04:46:23","5":"119.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"119.98","index":399},{"0":"10366C01-E5BE-4DD8-BD52-1B22971F1F4B","1":"A1688853596241210","2":"65.31","3":"22.0","4":"2023-02-17 03:39:39","9":"1","10":"366.0","11":"0.0","12":"125.30000000000001","13":"125.30000000000001","14":"62.650000000000006","index":400},{"0":"0C27D583-960D-4757-B7A9-E207BB91FF38","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:17:03","9":"1","10":"2365.0","11":"3.0","12":"188.64999999999984","13":"177.86999999999986","14":"5.389999999999995","index":401},{"0":"8DDECA4B-95EF-4FAD-8781-99F8134532FF","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:17:51","9":"1","10":"2365.0","11":"3.0","12":"188.64999999999984","13":"177.86999999999986","14":"5.389999999999995","index":402},{"0":"0C27D583-960D-4757-B7A9-E207BB91FF38","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:17:03","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"188.64999999999984","13":"177.86999999999986","14":"5.389999999999995","index":403},{"0":"8DAB4CC5-905C-4646-904A-F981F3F7D0BA","1":"A844427868914574","2":"389.99","3":"11.0","4":"2023-03-17 11:32:46","9":"1","10":"366.0","11":"0.0","12":"389.99","13":"389.99","14":"389.99","index":404},{"0":"8DAB4CC5-905C-4646-904A-F981F3F7D0BA","1":"A844427868914574","2":"389.99","3":"11.0","4":"2023-03-17 11:32:46","5":"614.195251","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"389.99","13":"389.99","14":"389.99","index":405},{"0":"06243234-E503-47F2-919F-6F9EA1513C3F","1":"A1055521380537310","2":"64.79","3":"13.0","4":"2023-01-03 18:53:51","9":"1","10":"366.0","11":"0.0","12":"64.79","13":"64.79","14":"64.79","index":406},{"0":"06243234-E503-47F2-919F-6F9EA1513C3F","1":"A1055521380537310","2":"64.79","3":"13.0","4":"2023-01-03 18:53:51","5":"64.79","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"64.79","13":"64.79","14":"64.79","index":407},{"0":"630F1681-99D1-4F3C-BCE0-69C6B28E5A98","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:43:12","9":"1","10":"366.0","11":"0.0","12":"419.7900000000001","13":"419.7900000000001","14":"19.990000000000002","index":408},{"0":"69CEC859-4F1A-4837-8898-BAE6DBC4E1BD","1":"A985156998227558","2":"231.56","3":"16.0","4":"2023-01-28 23:47:41","9":"1","10":"366.0","11":"0.0","12":"231.56","13":"231.56","14":"231.56","index":409},{"0":"69CEC859-4F1A-4837-8898-BAE6DBC4E1BD","1":"A985156998227558","2":"231.56","3":"16.0","4":"2023-01-28 23:47:41","5":"231.56","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"231.56","13":"231.56","14":"231.56","index":410},{"0":"A5D379FD-D933-4F68-8763-0C5AF07F7834","1":"A914801013785993","2":"86.98","3":"4.0","4":"2023-02-03 09:47:07","9":"1","10":"371.0","11":"0.0","12":"182.0","13":"182.0","14":"91.0","index":411},{"0":"29140357-C78E-4066-9BE5-CB7D5364BBD0","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:49:56","9":"1","10":"2365.0","11":"3.0","12":"393.46999999999935","13":"382.6899999999994","14":"5.389999999999991","index":412},{"0":"46C30D4B-09B0-432A-AC46-DDD857CBD1FF","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:07:38","9":"1","10":"2365.0","11":"3.0","12":"528.219999999999","13":"517.439999999999","14":"5.38999999999999","index":413},{"0":"33615076-74BA-414F-A51C-470C5AFD1FD7","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:42:37","9":"1","10":"2365.0","11":"3.0","12":"318.00999999999954","13":"307.22999999999956","14":"5.389999999999993","index":414},{"0":"46C30D4B-09B0-432A-AC46-DDD857CBD1FF","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:07:38","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"528.219999999999","13":"517.439999999999","14":"5.38999999999999","index":415},{"0":"F1B5AEFF-0701-44A7-8BFF-F44A9FCD8F2C","1":"A914801050992809","2":"281.37","3":"17.0","4":"2023-03-21 23:24:04","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":416},{"0":"F1B5AEFF-0701-44A7-8BFF-F44A9FCD8F2C","1":"A914801050992809","2":"281.37","3":"17.0","4":"2023-03-21 23:24:04","5":"280.207942","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":417},{"0":"D6AE2567-ECC7-46FF-8389-BCF43C8A81D4","1":"A1055521435373420","2":"278.88","3":"0.0","4":"2023-03-30 06:20:02","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":418},{"0":"BA22D839-F441-414B-8062-5BA7B6875708","1":"A1759222192247110","2":"145.59","3":"3.0","4":"2023-01-11 08:27:26","9":"1","10":"366.0","11":"0.0","12":"145.59","13":"145.59","14":"145.59","index":419},{"0":"BA22D839-F441-414B-8062-5BA7B6875708","1":"A1759222192247110","2":"145.59","3":"3.0","4":"2023-01-11 08:27:26","5":"145.59","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"145.59","13":"145.59","14":"145.59","index":420},{"0":"E9D60DF2-0E4B-4396-A84B-A0B9E88B3B63","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 17:02:50","9":"1","10":"366.0","11":"0.0","12":"264.95","13":"264.95","14":"52.989999999999995","index":421},{"0":"973F228D-6E34-4E2C-A8ED-86A560FFE993","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:37:33","9":"1","10":"366.0","11":"0.0","12":"3159.3699999999985","13":"3159.3699999999985","14":"143.6077272727272","index":422},{"0":"499056C6-A358-4E98-8685-9EC28E333CF7","1":"A1759222094070230","2":"281.37","3":"14.0","4":"2023-01-20 19:36:53","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":423},{"0":"499056C6-A358-4E98-8685-9EC28E333CF7","1":"A1759222094070230","2":"281.37","3":"14.0","4":"2023-01-20 19:36:53","5":"280.207942","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":424},{"0":"DE2C2A1F-DE62-4A91-8FB1-D40C46B96DF6","1":"A985157009164620","2":"151.54","3":"10.0","4":"2023-02-17 17:00:01","9":"1","10":"366.0","11":"0.0","12":"151.54","13":"151.54","14":"151.54","index":425},{"0":"DE2C2A1F-DE62-4A91-8FB1-D40C46B96DF6","1":"A985157009164620","2":"151.54","3":"10.0","4":"2023-02-17 17:00:01","5":"151.54","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"151.54","13":"151.54","14":"151.54","index":426},{"0":"73FD0C82-ABD2-483C-93B3-9E0B6D5F1793","1":"A1759222234548900","2":"73080.0","3":"23.0","4":"2023-03-07 14:59:33","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":427},{"0":"73FD0C82-ABD2-483C-93B3-9E0B6D5F1793","1":"A1759222234548900","2":"73080.0","3":"23.0","4":"2023-03-07 14:59:33","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":428},{"0":"A053C59B-6929-4895-AC1D-658F81AB3CB6","1":"A985157039921196","2":"1438.0","3":"3.0","4":"2023-03-28 04:00:00","9":"1","10":"366.0","11":"0.0","12":"1438.0","13":"1438.0","14":"1438.0","index":429},{"0":"A053C59B-6929-4895-AC1D-658F81AB3CB6","1":"A985157039921196","2":"1438.0","3":"3.0","4":"2023-03-28 04:00:00","5":"2264.7062","6":"true","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"1438.0","13":"1438.0","14":"1438.0","index":430},{"0":"C125DD40-6BE3-40E2-8604-5B9540751467","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:18:59","9":"1","10":"366.0","11":"0.0","12":"701.87","13":"701.87","14":"53.99","index":431},{"0":"C125DD40-6BE3-40E2-8604-5B9540751467","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:18:59","5":"53.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"701.87","13":"701.87","14":"53.99","index":432},{"0":"64A65086-D78C-4C67-994D-06DC95737FB5","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:09:49","9":"1","10":"366.0","11":"0.0","12":"2109.58","13":"2109.58","14":"140.63866666666667","index":433},{"0":"44D80959-7137-4EDB-AEEE-1B7654A9A2A2","1":"A1055521400546730","2":"119.99","4":"2023-02-23 01:16:41","9":"1","10":"366.0","11":"0.0","12":"119.99","13":"119.99","14":"119.99","index":434},{"0":"FBB33185-B7C4-47B3-9118-11693E3BE83C","1":"A1759222167950540","2":"59.99","3":"23.0","4":"2023-02-17 07:35:34","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":435},{"0":"FBB33185-B7C4-47B3-9118-11693E3BE83C","1":"A1759222167950540","2":"59.99","3":"23.0","4":"2023-02-17 07:35:34","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":436},{"0":"F558D2DF-78BC-4A0E-A230-4B7291A94F78","1":"A1899946887996110","2":"235.39","3":"18.0","4":"2023-02-19 23:48:56","9":"1","10":"366.0","11":"0.0","12":"235.39","13":"235.39","14":"235.39","index":437},{"0":"F558D2DF-78BC-4A0E-A230-4B7291A94F78","1":"A1899946887996110","2":"235.39","3":"18.0","4":"2023-02-19 23:48:56","5":"235.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"235.39","13":"235.39","14":"235.39","index":438},{"0":"AA8F1576-451B-4B38-927D-2AB406CC8E8C","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:59:41","9":"1","10":"2365.0","11":"3.0","12":"479.7099999999991","13":"468.92999999999915","14":"5.38999999999999","index":439},{"0":"47E230FD-DD71-432D-8EDA-F6F721A0611B","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:56:09","9":"1","10":"2365.0","11":"3.0","12":"447.3699999999992","13":"436.58999999999924","14":"5.389999999999991","index":440},{"0":"910E41CD-FF30-4CFB-80F2-9799BDC60C48","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:31:33","9":"1","10":"2365.0","11":"3.0","12":"80.85","13":"80.85","14":"5.39","index":441},{"0":"AA8F1576-451B-4B38-927D-2AB406CC8E8C","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:59:41","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"479.7099999999991","13":"468.92999999999915","14":"5.38999999999999","index":442},{"0":"EF766941-87DA-47B7-B312-B351B0172307","1":"A1899947010054330","2":"73080.0","3":"20.0","4":"2023-03-01 12:01:28","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":443},{"0":"EF766941-87DA-47B7-B312-B351B0172307","1":"A1899947010054330","2":"73080.0","3":"20.0","4":"2023-03-01 12:01:28","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":444},{"0":"947519EE-C2DE-42CC-A5CA-B075AEEA05D3","1":"A844427390246047","2":"5.39","3":"22.0","4":"2023-02-26 03:30:51","9":"1","10":"2365.0","11":"3.0","12":"533.609999999999","13":"522.829999999999","14":"5.38999999999999","index":445},{"0":"C70F7DCA-97F0-4486-83DC-B06483D9CFF5","1":"A1829582571089880","2":"233.19","3":"21.0","4":"2023-01-28 05:55:53","9":"1","10":"366.0","11":"0.0","12":"466.38","13":"466.38","14":"233.19","index":446},{"0":"C70F7DCA-97F0-4486-83DC-B06483D9CFF5","1":"A1829582571089880","2":"233.19","3":"21.0","4":"2023-01-28 05:55:53","5":"233.19","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"466.38","13":"466.38","14":"233.19","index":447},{"0":"EA8C66F4-8916-4822-85FA-583C2186501A","1":"A985156821621035","2":"152.03","3":"21.0","4":"2023-02-22 05:05:07","5":"152.03","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"152.03","13":"152.03","14":"152.03","index":448},{"0":"69A8BDFE-9310-4098-B8E9-D5C0D938E851","1":"A844427390246047","2":"5.39","3":"12.0","4":"2023-02-28 17:43:02","9":"1","10":"2365.0","11":"3.0","12":"641.4099999999987","13":"301.8399999999996","14":"5.389999999999989","index":449},{"0":"FF74DFD9-5015-440B-97A1-BDAB4A95B272","1":"A985156971082067","2":"119.98","3":"13.0","4":"2023-01-03 18:33:56","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"119.98","index":450},{"0":"FF74DFD9-5015-440B-97A1-BDAB4A95B272","1":"A985156971082067","2":"119.98","3":"13.0","4":"2023-01-03 18:33:56","5":"119.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"119.98","index":451},{"0":"8875BFC3-B41E-416A-A334-65EB49672BEC","1":"A844428017843376","2":"119.98","3":"15.0","4":"2023-01-03 20:29:02","9":"1","10":"366.0","11":"0.0","12":"227.5","13":"227.5","14":"113.75","index":452},{"0":"B0F016BB-D6B1-4812-91DD-41C1FE9C91BA","1":"A1688853524321290","2":"74.97","3":"19.0","4":"2023-01-05 00:42:34","9":"1","10":"366.0","11":"0.0","12":"149.94","13":"149.94","14":"49.98","index":453},{"0":"B0F016BB-D6B1-4812-91DD-41C1FE9C91BA","1":"A1688853524321290","2":"74.97","3":"19.0","4":"2023-01-05 00:42:34","5":"74.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"149.94","13":"149.94","14":"49.98","index":454},{"0":"0C044F40-9685-4113-882F-D605AAD744BE","1":"A1829582519832190","2":"59.99","3":"8.0","4":"2023-02-16 13:52:42","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":455},{"0":"0C044F40-9685-4113-882F-D605AAD744BE","1":"A1829582519832190","2":"59.99","3":"8.0","4":"2023-02-16 13:52:42","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":456},{"0":"6867FF41-D687-4B12-B2D2-A7B54F3728B9","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:58:52","9":"1","10":"2365.0","11":"3.0","12":"474.31999999999914","13":"463.53999999999917","14":"5.38999999999999","index":457},{"0":"1ECF8464-01D3-40DC-AC34-DC665D7839C4","1":"A844427390246047","2":"5.39","3":"5.0","4":"2023-02-25 11:14:50","9":"1","10":"2365.0","11":"3.0","12":"210.20999999999978","13":"199.4299999999998","14":"5.389999999999994","index":458},{"0":"78A67431-762B-4266-9942-E6EEB4A268BF","1":"A1688853662170320","2":"839.97","3":"19.0","4":"2023-03-13 10:56:33","9":"1","10":"366.0","11":"0.0","12":"839.97","13":"839.97","14":"839.97","index":459},{"0":"78A67431-762B-4266-9942-E6EEB4A268BF","1":"A1688853662170320","2":"839.97","3":"19.0","4":"2023-03-13 10:56:33","5":"839.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"839.97","13":"839.97","14":"839.97","index":460},{"0":"3147828E-D9DA-4C96-AB5C-56ECE7A35403","1":"A985157032922252","2":"211.89","3":"15.0","4":"2023-03-19 22:27:02","9":"1","10":"366.0","11":"0.0","12":"211.89","13":"211.89","14":"211.89","index":461},{"0":"3147828E-D9DA-4C96-AB5C-56ECE7A35403","1":"A985157032922252","2":"211.89","3":"15.0","4":"2023-03-19 22:27:02","5":"211.89","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"211.89","13":"211.89","14":"211.89","index":462},{"0":"76824E16-8144-4C10-8769-DF9049DE4BB3","1":"A1899946873128090","2":"119.98","3":"22.0","4":"2023-02-15 03:38:31","9":"1","10":"366.0","11":"0.0","12":"315.54","13":"315.54","14":"105.18","index":463},{"0":"95B931E2-7A0A-4173-ACA5-8B43ED291019","1":"A1759222219270870","2":"424.99","3":"22.0","4":"2023-02-28 04:59:09","9":"1","10":"366.0","11":"0.0","12":"424.99","13":"424.99","14":"424.99","index":464},{"0":"95B931E2-7A0A-4173-ACA5-8B43ED291019","1":"A1759222219270870","2":"424.99","3":"22.0","4":"2023-02-28 04:59:09","5":"424.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"424.99","13":"424.99","14":"424.99","index":465},{"0":"A541504B-98D3-492E-89A4-994617EFA9CA","1":"A1759222088015940","2":"78750.0","3":"17.0","4":"2023-03-15 08:58:56","9":"1","10":"366.0","11":"0.0","12":"157500.0","13":"157500.0","14":"78750.0","index":466},{"0":"EAA2680C-AD71-4139-BBCA-D74E0B373E33","1":"A1055521383712530","2":"210.99","4":"2023-03-15 20:16:57","9":"1","10":"366.0","11":"0.0","12":"210.99","13":"210.99","14":"210.99","index":467},{"0":"EAA2680C-AD71-4139-BBCA-D74E0B373E33","1":"A1055521383712530","2":"210.99","4":"2023-03-15 20:16:57","5":"210.99","6":"false","7":"1.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"210.99","13":"210.99","14":"210.99","index":468},{"0":"87726A9D-DF55-4685-82A1-5D4F9E022F5A","1":"A914801058711211","2":"145.95","3":"17.0","4":"2023-03-30 23:30:46","9":"1","10":"366.0","11":"0.0","12":"145.95","13":"145.95","14":"145.95","index":469},{"0":"D1119E3D-720D-46A4-9772-C456EA3B0D4D","1":"A1688853596356500","2":"59.99","3":"1.0","4":"2023-02-17 06:46:16","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":470},{"0":"A5DA7BE5-A6BA-492B-B43E-588713661CDE","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:46:32","9":"1","10":"2365.0","11":"3.0","12":"361.1299999999994","13":"350.34999999999945","14":"5.389999999999992","index":471},{"0":"FE12EF10-2C8B-4F97-8D72-8CA106A2FDB6","1":"A1055521380537310","2":"129.58","3":"13.0","4":"2023-01-03 18:56:12","9":"1","10":"366.0","11":"0.0","12":"194.37","13":"194.37","14":"97.185","index":472},{"0":"2DC3FDC7-C677-4479-A4C4-85C9A5E2F4FC","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:11:32","9":"1","10":"366.0","11":"0.0","12":"5558.889999999999","13":"5558.889999999999","14":"146.2865789473684","index":473},{"0":"A3BEE7DE-D634-4C72-A05D-77A80D7D613B","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:44:48","9":"1","10":"366.0","11":"0.0","12":"899.85","13":"899.85","14":"59.99","index":474},{"0":"CD77E40B-B5DF-4C24-9A41-6E55793A90BD","1":"A1899946963731140","2":"62.49","3":"5.0","4":"2023-01-19 12:12:26","9":"1","10":"366.0","11":"0.0","12":"62.49","13":"62.49","14":"62.49","index":475},{"0":"CD77E40B-B5DF-4C24-9A41-6E55793A90BD","1":"A1899946963731140","2":"62.49","3":"5.0","4":"2023-01-19 12:12:26","5":"62.49","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"62.49","13":"62.49","14":"62.49","index":476},{"0":"169212AC-3DBA-4EA6-A5AE-031C407DC3AA","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 17:00:28","5":"52.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"211.96","13":"211.96","14":"52.99","index":477},{"0":"D1834580-E9C8-4D34-9433-0BA410FA0DF4","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:30:31","9":"1","10":"366.0","11":"0.0","12":"239.88000000000002","13":"239.88000000000002","14":"19.990000000000002","index":478},{"0":"D1884A1A-0865-47C3-A8DC-43DC991D661C","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:41:02","9":"1","10":"366.0","11":"0.0","12":"719.88","13":"719.88","14":"59.99","index":479},{"0":"2ED99F79-890A-42CD-81C5-0813D241BD01","1":"A844428040495230","2":"75.58","3":"13.0","4":"2023-01-17 18:24:03","9":"1","10":"366.0","11":"1.0","12":"75.58","13":"75.58","14":"75.58","index":480},{"0":"2ED99F79-890A-42CD-81C5-0813D241BD01","1":"A844428040495230","2":"75.58","3":"13.0","4":"2023-01-17 18:24:03","5":"75.58","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"1.0","12":"75.58","13":"75.58","14":"75.58","index":481},{"0":"A55591A0-4BA2-4DB4-8881-619C2000D5FA","1":"A1759222095873190","2":"106.98","3":"11.0","4":"2023-01-03 17:39:26","9":"1","10":"366.0","11":"0.0","12":"160.47","13":"160.47","14":"80.235","index":482},{"0":"A55591A0-4BA2-4DB4-8881-619C2000D5FA","1":"A1759222095873190","2":"106.98","3":"11.0","4":"2023-01-03 17:39:26","5":"106.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"160.47","13":"160.47","14":"80.235","index":483},{"0":"C49AA590-F545-4A2A-B096-FA1626918587","1":"A1759222185358430","2":"86.74","3":"9.0","4":"2023-01-17 14:44:35","9":"1","10":"366.0","11":"0.0","12":"86.74","13":"86.74","14":"86.74","index":484},{"0":"C49AA590-F545-4A2A-B096-FA1626918587","1":"A1759222185358430","2":"86.74","3":"9.0","4":"2023-01-17 14:44:35","5":"86.74","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"86.74","13":"86.74","14":"86.74","index":485},{"0":"32E64963-58DD-4FBC-9125-ED1ACBFE62EF","1":"A1899946781648580","2":"228.79","3":"9.0","4":"2023-02-23 15:26:52","9":"1","10":"366.0","11":"0.0","12":"457.58","13":"457.58","14":"228.79","index":486},{"0":"32E64963-58DD-4FBC-9125-ED1ACBFE62EF","1":"A1899946781648580","2":"228.79","3":"9.0","4":"2023-02-23 15:26:52","5":"228.79","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"457.58","13":"457.58","14":"228.79","index":487},{"0":"62C66790-989B-43C0-8FCD-12C5829A6D77","1":"A914800278864743","2":"151.54","3":"15.0","4":"2023-02-25 21:28:57","9":"1","10":"604.0","11":"0.0","12":"151.54","13":"151.54","14":"151.54","index":488},{"0":"62C66790-989B-43C0-8FCD-12C5829A6D77","1":"A914800278864743","2":"151.54","3":"15.0","4":"2023-02-25 21:28:57","5":"151.54","6":"false","7":"1.0","8":"0.0","9":"1","10":"604.0","11":"0.0","12":"151.54","13":"151.54","14":"151.54","index":489},{"0":"3E6FEB4B-BCD3-4FFE-BBB3-1CB0BFA39C82","1":"A914800340295166","2":"79.99","3":"18.0","4":"2023-01-15 01:17:44","9":"1","10":"366.0","11":"0.0","12":"79.99","13":"79.99","14":"79.99","index":490},{"0":"3E6FEB4B-BCD3-4FFE-BBB3-1CB0BFA39C82","1":"A914800340295166","2":"79.99","3":"18.0","4":"2023-01-15 01:17:44","5":"79.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"79.99","13":"79.99","14":"79.99","index":491},{"0":"94BEE01F-8FA2-4685-9870-0172A53F3981","1":"A1759222234549860","2":"62580.0","3":"11.0","4":"2023-03-03 02:25:14","9":"1","10":"366.0","11":"0.0","12":"187740.0","13":"187740.0","14":"62580.0","index":492},{"0":"6BA9D314-0ECC-425F-BCA8-FE3DC74581C8","1":"A914801012822158","2":"104.25","3":"6.0","4":"2023-02-01 11:41:23","9":"1","10":"367.0","11":"0.0","12":"104.25","13":"104.25","14":"104.25","index":493},{"0":"3BDC3741-807B-447D-9A8D-9396CB1519BA","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:16:10","9":"1","10":"2365.0","11":"3.0","12":"183.25999999999985","13":"172.47999999999988","14":"5.389999999999995","index":494},{"0":"A1979FD7-32C8-4BE2-AB5F-3FFDDD7C584A","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:24:17","9":"1","10":"366.0","11":"0.0","12":"863.84","13":"863.84","14":"53.99","index":495},{"0":"D141370E-DD9D-4316-A2F9-A5D8244C61A2","1":"A1899946873128090","2":"119.98","3":"20.0","4":"2023-02-15 02:37:52","9":"1","10":"366.0","11":"0.0","12":"195.56","13":"195.56","14":"97.78","index":496},{"0":"D2155FC9-8E47-46A6-B8F3-1395DECE7D82","1":"A844427390246047","2":"5.39","3":"9.0","4":"2023-02-28 14:35:07","9":"1","10":"2365.0","11":"3.0","12":"592.8999999999988","13":"253.32999999999967","14":"5.389999999999989","index":497},{"0":"B6A87790-25B1-4E1D-8F1C-DBF01DA1B7B7","1":"A1759221887824430","2":"153.29","3":"17.0","4":"2023-01-05 02:05:55","9":"1","10":"366.0","11":"0.0","12":"153.29","13":"153.29","14":"153.29","index":498},{"0":"B6A87790-25B1-4E1D-8F1C-DBF01DA1B7B7","1":"A1759221887824430","2":"153.29","3":"17.0","4":"2023-01-05 02:05:55","5":"153.29","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"153.29","13":"153.29","14":"153.29","index":499},{"0":"5403C73E-98E5-47B4-B3A8-82335C48C6C0","1":"A1055521396853140","2":"150.49","3":"18.0","4":"2023-02-09 00:38:33","9":"1","10":"366.0","11":"0.0","12":"150.49","13":"150.49","14":"150.49","index":500},{"0":"5403C73E-98E5-47B4-B3A8-82335C48C6C0","1":"A1055521396853140","2":"150.49","3":"18.0","4":"2023-02-09 00:38:33","5":"150.49","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"150.49","13":"150.49","14":"150.49","index":501},{"0":"1517BB82-21EB-40EF-AF3D-65614C90297C","1":"A1829582583896790","2":"127.18","3":"7.0","4":"2023-02-16 12:25:13","9":"1","10":"366.0","11":"0.0","12":"127.18","13":"127.18","14":"127.18","index":502},{"0":"1517BB82-21EB-40EF-AF3D-65614C90297C","1":"A1829582583896790","2":"127.18","3":"7.0","4":"2023-02-16 12:25:13","5":"127.18","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"127.18","13":"127.18","14":"127.18","index":503},{"0":"C2440D4D-3768-4108-BCAF-E43441537783","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:38:15","9":"1","10":"366.0","11":"0.0","12":"7208.560000000002","13":"7208.560000000002","14":"147.11346938775515","index":504},{"0":"B544758B-8F40-45DE-94EB-BC3B8B7B1401","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:42:16","9":"1","10":"366.0","11":"0.0","12":"399.80000000000007","13":"399.80000000000007","14":"19.990000000000002","index":505},{"0":"B544758B-8F40-45DE-94EB-BC3B8B7B1401","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:42:16","5":"19.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"399.80000000000007","13":"399.80000000000007","14":"19.990000000000002","index":506},{"0":"A66793DD-03E4-4E15-9CDB-79EC4733C3CA","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:39:54","9":"1","10":"367.0","11":"0.0","12":"104.78999999999998","13":"104.78999999999998","14":"4.989999999999999","index":507},{"0":"573FCD19-5AF1-4C96-B2B6-D09E91B02381","1":"A985157025448796","2":"73080.0","3":"21.0","4":"2023-03-11 12:54:28","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":508},{"0":"573FCD19-5AF1-4C96-B2B6-D09E91B02381","1":"A985157025448796","2":"73080.0","3":"21.0","4":"2023-03-11 12:54:28","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":509},{"0":"B76E185F-504D-437A-9865-ECDA737870F3","1":"A914801007105746","2":"93.85","3":"9.0","4":"2023-01-24 17:53:21","9":"1","10":"367.0","11":"0.0","12":"176.91","13":"176.91","14":"88.455","index":510},{"0":"B76E185F-504D-437A-9865-ECDA737870F3","1":"A914801007105746","2":"93.85","3":"9.0","4":"2023-01-24 17:53:21","5":"93.85","6":"false","7":"0.0","8":"1.0","9":"1","10":"367.0","11":"0.0","12":"176.91","13":"176.91","14":"88.455","index":511},{"0":"D43FE025-6D1B-4FE0-8E42-BC3E95A76823","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:47:52","9":"1","10":"2365.0","11":"3.0","12":"334.1799999999995","13":"323.3999999999995","14":"5.389999999999992","index":512},{"0":"9C4E2317-10BB-4BDE-AE2F-09EA29B0FD8A","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:36:14","9":"1","10":"2365.0","11":"3.0","12":"113.19","13":"113.19","14":"5.39","index":513},{"0":"979DF1CE-FE47-47FE-A8ED-ED1119F41065","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 17:03:58","9":"1","10":"366.0","11":"0.0","12":"317.94","13":"317.94","14":"52.99","index":514},{"0":"7829E3D8-EAF7-44CE-8AD6-0F11BC182D65","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:39:40","9":"1","10":"366.0","11":"0.0","12":"458.98999999999995","13":"458.98999999999995","14":"65.57","index":515},{"0":"7829E3D8-EAF7-44CE-8AD6-0F11BC182D65","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:39:40","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"458.98999999999995","13":"458.98999999999995","14":"65.57","index":516},{"0":"105FE6E4-4E43-4042-937C-4E6748781A3D","1":"A914801012822158","2":"92.63","3":"0.0","4":"2023-02-03 05:37:23","9":"1","10":"367.0","11":"0.0","12":"289.08","13":"289.08","14":"96.36","index":517},{"0":"B5550A72-81D1-4F96-8294-690A060C0071","1":"A1688853605359930","2":"66.05","3":"19.0","4":"2023-02-14 01:09:37","9":"1","10":"366.0","11":"0.0","12":"66.05","13":"66.05","14":"66.05","index":518},{"0":"B5550A72-81D1-4F96-8294-690A060C0071","1":"A1688853605359930","2":"66.05","3":"19.0","4":"2023-02-14 01:09:37","5":"66.05","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"66.05","13":"66.05","14":"66.05","index":519},{"0":"0D43D3F2-A352-4700-8FEC-8E02EF44A514","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:34:45","9":"1","10":"367.0","11":"0.0","12":"39.92000000000001","13":"39.92000000000001","14":"4.990000000000001","index":520},{"0":"9606F304-48F8-47E9-A666-2BD97690632F","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:51:47","9":"1","10":"2365.0","11":"3.0","12":"409.6399999999993","13":"398.85999999999933","14":"5.389999999999991","index":521},{"0":"566C00BC-EB37-41D8-9202-CCAC36616EB0","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-21 20:43:37","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"5.39","13":"5.39","14":"5.39","index":522},{"0":"66709ABD-6D12-456B-82D6-CBD25CFBE285","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:39:19","9":"1","10":"367.0","11":"0.0","12":"64.87000000000002","13":"64.87000000000002","14":"4.990000000000001","index":523},{"0":"9E151E5A-C4BE-4AE6-A0F2-62320FD3FD14","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 16:49:39","9":"1","10":"367.0","11":"0.0","12":"9.98","13":"9.98","14":"4.99","index":524},{"0":"9F442404-8F9E-45E0-8A10-D5B4C0B3A36A","1":"A1688853516414440","2":"79380.0","3":"0.0","4":"2023-03-10 15:47:03","9":"1","10":"366.0","11":"0.0","12":"79380.0","13":"79380.0","14":"79380.0","index":525},{"0":"AA345197-EC08-4848-9F43-97862C99593E","1":"A914801028323265","2":"150.31","3":"12.0","4":"2023-02-21 19:00:49","9":"1","10":"366.0","11":"0.0","12":"150.31","13":"150.31","14":"150.31","index":526},{"0":"AA345197-EC08-4848-9F43-97862C99593E","1":"A914801028323265","2":"150.31","3":"12.0","4":"2023-02-21 19:00:49","5":"150.31","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"150.31","13":"150.31","14":"150.31","index":527},{"0":"A75939D1-EE17-4E78-8478-6937CD08D5E9","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:52:01","9":"1","10":"366.0","11":"0.0","12":"759.85","13":"759.85","14":"126.64166666666667","index":528},{"0":"78CF721D-0FC0-4CE6-B458-D1DF03C16C16","1":"A985156986195261","2":"169.0","3":"20.0","4":"2023-01-10 10:28:38","9":"1","10":"366.0","11":"0.0","12":"169.0","13":"169.0","14":"169.0","index":529},{"0":"78CF721D-0FC0-4CE6-B458-D1DF03C16C16","1":"A985156986195261","2":"169.0","3":"20.0","4":"2023-01-10 10:28:38","5":"176.66415","6":"true","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"169.0","13":"169.0","14":"169.0","index":530},{"0":"4A7523C3-17EE-4AB6-92D9-6535A3A41331","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:22:20","9":"1","10":"366.0","11":"0.0","12":"179.91","13":"179.91","14":"19.99","index":531},{"0":"DDBCD8B8-C45B-45BB-8373-E184F6776E09","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:01:13","9":"1","10":"366.0","11":"0.0","12":"4509.099999999998","13":"4509.099999999998","14":"145.45483870967735","index":532},{"0":"104D3F8D-6772-4535-AC30-D78279A10F53","1":"A914801027937767","2":"149.99","3":"14.0","4":"2023-02-21 05:05:44","9":"1","10":"366.0","11":"0.0","12":"149.99","13":"149.99","14":"149.99","index":533},{"0":"104D3F8D-6772-4535-AC30-D78279A10F53","1":"A914801027937767","2":"149.99","3":"14.0","4":"2023-02-21 05:05:44","5":"156.792046","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"149.99","13":"149.99","14":"149.99","index":534},{"0":"A012EE4D-7688-4A68-BAA1-6E372A040ADA","1":"A1055521399136000","2":"5.4","3":"23.0","4":"2023-02-27 04:55:35","9":"1","10":"366.0","11":"0.0","12":"27.0","13":"27.0","14":"5.4","index":535},{"0":"A012EE4D-7688-4A68-BAA1-6E372A040ADA","1":"A1055521399136000","2":"5.4","3":"23.0","4":"2023-02-27 04:55:35","5":"5.4","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"27.0","13":"27.0","14":"5.4","index":536},{"0":"11FAFE8A-E9F5-4FB4-BC04-4C96AE6E852F","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:16:26","9":"1","10":"366.0","11":"0.0","12":"593.89","13":"593.89","14":"53.99","index":537},{"0":"1570C1F3-15CF-473D-A630-24D1151F7D94","1":"A1759222185240050","2":"64.19","3":"3.0","4":"2023-01-17 08:57:19","9":"1","10":"366.0","11":"0.0","12":"64.19","13":"64.19","14":"64.19","index":538},{"0":"1570C1F3-15CF-473D-A630-24D1151F7D94","1":"A1759222185240050","2":"64.19","3":"3.0","4":"2023-01-17 08:57:19","5":"64.19","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"64.19","13":"64.19","14":"64.19","index":539},{"0":"A6B5B02E-3974-4BD3-9F9F-98FF549E114F","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:06:22","9":"1","10":"2365.0","11":"3.0","12":"517.439999999999","13":"506.65999999999906","14":"5.38999999999999","index":540},{"0":"460FA98A-658B-4543-BBC0-EFBFCD7436FD","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:58:07","9":"1","10":"2365.0","11":"3.0","12":"140.13999999999996","13":"129.35999999999999","14":"5.389999999999999","index":541},{"0":"A6B5B02E-3974-4BD3-9F9F-98FF549E114F","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:06:22","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"517.439999999999","13":"506.65999999999906","14":"5.38999999999999","index":542},{"0":"7CD9A9C3-3CE1-49B9-8299-039B7955F0F4","1":"A985156290472668","2":"5.34","3":"17.0","4":"2023-02-27 22:55:15","5":"5.34","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"0.0","12":"26.7","13":"26.7","14":"5.34","index":543},{"0":"FE374A34-0463-4D17-926A-7F697581AA35","1":"A1055520836257310","2":"5.04","3":"5.0","4":"2023-02-28 10:24:44","9":"1","10":"1555.0","11":"0.0","12":"15.120000000000001","13":"15.120000000000001","14":"5.04","index":544},{"0":"D2CE31CE-7D5A-4E80-8D3E-F2E184DFAC98","1":"A844428017843376","2":"107.52","3":"15.0","4":"2023-01-03 20:27:17","9":"1","10":"366.0","11":"0.0","12":"107.52","13":"107.52","14":"107.52","index":545},{"0":"53FB03E4-3872-4E94-B597-12F0C1C8E38D","1":"A360434769010983","2":"149.44","3":"15.0","4":"2023-02-17 21:06:39","9":"1","10":"366.0","11":"0.0","12":"149.44","13":"149.44","14":"149.44","index":546},{"0":"53FB03E4-3872-4E94-B597-12F0C1C8E38D","1":"A360434769010983","2":"149.44","3":"15.0","4":"2023-02-17 21:06:39","5":"149.44","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"149.44","13":"149.44","14":"149.44","index":547},{"0":"16212511-57EB-4206-95EA-1AD25B767926","1":"A985157022379291","2":"279.98","3":"17.0","4":"2023-03-06 23:19:42","9":"1","10":"366.0","11":"0.0","12":"279.98","13":"279.98","14":"279.98","index":548},{"0":"16212511-57EB-4206-95EA-1AD25B767926","1":"A985157022379291","2":"279.98","3":"17.0","4":"2023-03-06 23:19:42","5":"279.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"279.98","13":"279.98","14":"279.98","index":549},{"0":"EDE9E934-451A-491D-84E1-090D1A22D191","1":"A1759222185351430","2":"79.99","3":"7.0","4":"2023-01-17 13:14:40","9":"1","10":"366.0","11":"0.0","12":"79.99","13":"79.99","14":"79.99","index":550},{"0":"EDE9E934-451A-491D-84E1-090D1A22D191","1":"A1759222185351430","2":"79.99","3":"7.0","4":"2023-01-17 13:14:40","5":"79.99","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"79.99","13":"79.99","14":"79.99","index":551},{"0":"6FEBCE16-D75E-4426-92B8-7F7A9CE058BB","1":"A1055521406108610","2":"973.17","3":"11.0","4":"2023-02-22 17:39:23","9":"1","10":"366.0","11":"0.0","12":"973.17","13":"973.17","14":"973.17","index":552},{"0":"6FEBCE16-D75E-4426-92B8-7F7A9CE058BB","1":"A1055521406108610","2":"973.17","3":"11.0","4":"2023-02-22 17:39:23","5":"973.17","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"973.17","13":"973.17","14":"973.17","index":553},{"0":"B4FA5FE0-EBF4-48B9-BDD1-A1D85984E9ED","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:57:42","9":"1","10":"2365.0","11":"3.0","12":"463.53999999999917","13":"452.7599999999992","14":"5.38999999999999","index":554},{"0":"B4FA5FE0-EBF4-48B9-BDD1-A1D85984E9ED","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:57:42","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"463.53999999999917","13":"452.7599999999992","14":"5.38999999999999","index":555},{"0":"F8A27B27-C73C-4DD1-AED7-97F5A6761294","1":"A1899947010054330","2":"73080.0","3":"20.0","4":"2023-03-01 12:00:21","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":556},{"0":"F8A27B27-C73C-4DD1-AED7-97F5A6761294","1":"A1899947010054330","2":"73080.0","3":"20.0","4":"2023-03-01 12:00:21","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":557},{"0":"9751B6CA-AE84-41C9-8BAE-F2608CCE17E4","1":"A1055521435190160","2":"281.37","3":"18.0","4":"2023-03-29 23:22:01","9":"1","10":"366.0","11":"0.0","12":"562.74","13":"562.74","14":"281.37","index":558},{"0":"9751B6CA-AE84-41C9-8BAE-F2608CCE17E4","1":"A1055521435190160","2":"281.37","3":"18.0","4":"2023-03-29 23:22:01","5":"280.207942","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"562.74","13":"562.74","14":"281.37","index":559},{"0":"B375BB8C-242A-4DC6-8FC3-F35EA5ADDADD","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:21:25","9":"1","10":"366.0","11":"0.0","12":"179.91","13":"179.91","14":"19.99","index":560},{"0":"F1FE235C-84E4-4770-BA47-2F70994718C1","1":"A914801011218316","2":"330.86","3":"17.0","4":"2023-02-12 23:03:05","9":"1","10":"366.0","11":"0.0","12":"330.86","13":"330.86","14":"330.86","index":561},{"0":"FC750F5F-8402-4006-AFAC-4DD63C61F2F2","1":"A985156554634689","2":"116.86","3":"14.0","4":"2023-02-17 19:22:51","9":"1","10":"366.0","11":"0.0","12":"116.86","13":"116.86","14":"116.86","index":562},{"0":"FC750F5F-8402-4006-AFAC-4DD63C61F2F2","1":"A985156554634689","2":"116.86","3":"14.0","4":"2023-02-17 19:22:51","5":"116.86","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"116.86","13":"116.86","14":"116.86","index":563},{"0":"5C9ABE6A-E977-4767-BCC8-A9A0952DB43A","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-28 13:19:49","9":"1","10":"2365.0","11":"3.0","12":"565.9499999999989","13":"226.37999999999974","14":"5.38999999999999","index":564},{"0":"42FC2159-6C83-4CBE-A323-DE9E0546A311","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:34:38","9":"1","10":"2365.0","11":"3.0","12":"102.41","13":"102.41","14":"5.39","index":565},{"0":"5EE5C089-EEC1-40F9-B997-D8EA4BDA79C1","1":"A844427390246047","2":"5.39","3":"13.0","4":"2023-02-24 19:19:40","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"26.95","13":"26.95","14":"5.39","index":566},{"0":"0D5E955B-EC9E-48EA-B499-587B73B8AB69","1":"A1055521435373420","2":"278.88","3":"1.0","4":"2023-03-30 06:22:46","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":567},{"0":"0D5E955B-EC9E-48EA-B499-587B73B8AB69","1":"A1055521435373420","2":"278.88","3":"1.0","4":"2023-03-30 06:22:46","5":"277.72822599999995","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":568},{"0":"441AEEE6-AAA6-40CB-9173-FBEF1C355ACC","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:46:27","9":"1","10":"367.0","11":"0.0","12":"114.76999999999997","13":"114.76999999999997","14":"4.989999999999998","index":569},{"0":"36AD5A93-8DB0-4134-93EA-26D0A7EE8C3B","1":"A1759222234549810","2":"73080.0","3":"19.0","4":"2023-03-01 10:41:19","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":570},{"0":"CF161FC1-9EE6-42F9-92F4-84E0B45957D6","1":"A1829582439663370","2":"569.97","3":"11.0","4":"2023-03-15 02:21:19","9":"1","10":"366.0","11":"0.0","12":"569.97","13":"569.97","14":"569.97","index":571},{"0":"CF161FC1-9EE6-42F9-92F4-84E0B45957D6","1":"A1829582439663370","2":"569.97","3":"11.0","4":"2023-03-15 02:21:19","5":"768.205566","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"569.97","13":"569.97","14":"569.97","index":572},{"0":"976B65D5-19C3-48A1-A57A-E67899D3E4DE","1":"A1688853660541850","2":"105.98","3":"14.0","4":"2023-02-15 20:12:39","9":"1","10":"366.0","11":"0.0","12":"105.98","13":"105.98","14":"105.98","index":573},{"0":"976B65D5-19C3-48A1-A57A-E67899D3E4DE","1":"A1688853660541850","2":"105.98","3":"14.0","4":"2023-02-15 20:12:39","5":"105.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"105.98","13":"105.98","14":"105.98","index":574},{"0":"E9AE2FFC-1BA4-45CC-858C-DCE5E3BA51B8","1":"A1759222105153830","2":"108.38","3":"10.0","4":"2023-02-16 18:51:45","9":"1","10":"366.0","11":"0.0","12":"108.38","13":"108.38","14":"108.38","index":575},{"0":"F3606B8D-3008-4781-AC9C-BA159F1BF3B5","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:24:09","9":"1","10":"366.0","11":"0.0","12":"199.9","13":"199.9","14":"19.990000000000002","index":576},{"0":"1353C004-23CE-4887-8611-0986140B9411","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:22:26","9":"1","10":"366.0","11":"0.0","12":"6308.740000000001","13":"6308.740000000001","14":"146.71488372093026","index":577},{"0":"B67BDFBC-B7D7-414A-A781-964A9C80E8B1","1":"A1759222234548420","2":"538.0","3":"14.0","4":"2023-03-02 05:28:47","9":"1","10":"366.0","11":"0.0","12":"538.0","13":"538.0","14":"538.0","index":578},{"0":"B67BDFBC-B7D7-414A-A781-964A9C80E8B1","1":"A1759222234548420","2":"538.0","3":"14.0","4":"2023-03-02 05:28:47","5":"725.1164","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"538.0","13":"538.0","14":"538.0","index":579},{"0":"F1476029-DD3C-4695-924A-0D951E1540CE","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:02:36","9":"1","10":"366.0","11":"0.0","12":"4659.069999999998","13":"4659.069999999998","14":"145.59593749999993","index":580},{"0":"F1476029-DD3C-4695-924A-0D951E1540CE","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:02:36","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"4659.069999999998","13":"4659.069999999998","14":"145.59593749999993","index":581},{"0":"4A4AF956-41C3-4723-8BAD-527243728FE8","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:33:54","9":"1","10":"2365.0","11":"3.0","12":"97.02","13":"97.02","14":"5.39","index":582},{"0":"A721A0ED-7D53-4627-AC26-9D1C825D3E29","1":"A844428035905405","2":"119.75","3":"22.0","4":"2023-01-12 04:15:29","9":"1","10":"366.0","11":"0.0","12":"119.75","13":"119.75","14":"119.75","index":583},{"0":"A721A0ED-7D53-4627-AC26-9D1C825D3E29","1":"A844428035905405","2":"119.75","3":"22.0","4":"2023-01-12 04:15:29","5":"119.75","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.75","13":"119.75","14":"119.75","index":584},{"0":"98D7E9CF-D308-4B97-BF45-00263A93B702","1":"A914801004943538","2":"59.99","4":"2023-01-20 02:02:01","9":"1","10":"366.0","11":"0.0","12":"91.93","13":"91.93","14":"45.965","index":585},{"0":"761ADA43-FAD5-44FC-877C-82B8FD06CA58","1":"A311880327984872","2":"109.99","3":"11.0","4":"2023-02-24 17:06:07","9":"1","10":"366.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":586},{"0":"761ADA43-FAD5-44FC-877C-82B8FD06CA58","1":"A311880327984872","2":"109.99","3":"11.0","4":"2023-02-24 17:06:07","5":"109.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":587},{"0":"1DA8653D-7E02-48A4-AECA-CF30E3784FD3","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:54:40","9":"1","10":"2365.0","11":"3.0","12":"431.19999999999925","13":"420.4199999999993","14":"5.389999999999991","index":588},{"0":"667490FD-D4B7-4039-A69B-C4E70A9F5DE3","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:23:04","9":"1","10":"366.0","11":"0.0","12":"809.85","13":"809.85","14":"53.99","index":589},{"0":"F70513F4-FCD6-4FB3-9BDA-E1339E4DD86F","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:24:53","9":"1","10":"366.0","11":"0.0","12":"2709.459999999999","13":"2709.459999999999","14":"142.6031578947368","index":590},{"0":"F70513F4-FCD6-4FB3-9BDA-E1339E4DD86F","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:24:53","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"2709.459999999999","13":"2709.459999999999","14":"142.6031578947368","index":591},{"0":"22F1F04E-3F22-4BA6-AE81-6329C97E613F","1":"A2181420350587670","2":"149.99","3":"9.0","4":"2023-01-23 17:58:49","9":"1","10":"2365.0","11":"0.0","12":"149.99","13":"149.99","14":"149.99","index":592},{"0":"22F1F04E-3F22-4BA6-AE81-6329C97E613F","1":"A2181420350587670","2":"149.99","3":"9.0","4":"2023-01-23 17:58:49","5":"149.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"0.0","12":"149.99","13":"149.99","14":"149.99","index":593},{"0":"DBC4622E-A4BD-478C-A958-D67E1C11A000","1":"A1759222219189020","2":"222.19","3":"3.0","4":"2023-01-28 08:54:40","9":"1","10":"366.0","11":"0.0","12":"444.38","13":"444.38","14":"222.19","index":594},{"0":"DBC4622E-A4BD-478C-A958-D67E1C11A000","1":"A1759222219189020","2":"222.19","3":"3.0","4":"2023-01-28 08:54:40","5":"222.19","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"444.38","13":"444.38","14":"222.19","index":595},{"0":"C1BB5DA1-3DB9-47B3-BF4E-25C402A14AE8","1":"A914801013775218","2":"92.2","3":"4.0","4":"2023-02-03 09:41:23","9":"1","10":"366.0","11":"0.0","12":"185.82","13":"185.82","14":"92.91","index":596},{"0":"C1BB5DA1-3DB9-47B3-BF4E-25C402A14AE8","1":"A914801013775218","2":"92.2","3":"4.0","4":"2023-02-03 09:41:23","5":"92.2","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"185.82","13":"185.82","14":"92.91","index":597},{"0":"6083FE06-FDAB-4725-8CB2-DDB0B978E826","1":"A1688853596363060","2":"60.59","3":"6.0","4":"2023-02-16 11:58:56","9":"1","10":"366.0","11":"0.0","12":"121.18","13":"121.18","14":"60.59","index":598},{"0":"AB06E897-E5CB-408B-957A-9E77B9EBD71E","1":"A914801013775218","2":"93.62","3":"9.0","4":"2023-02-02 14:49:48","9":"1","10":"366.0","11":"0.0","12":"93.62","13":"93.62","14":"93.62","index":599},{"0":"23FB57DD-40E6-49D4-8F47-29EC2357E548","1":"A564049508937862","2":"0.0","4":"2023-03-06 19:52:07","5":"0.0","6":"false","7":"1.0","8":"1.0","9":"0","10":"2365.0","11":"0.0","12":"482.68","13":"482.68","14":"241.34","index":600},{"0":"DBECA025-8715-4299-B743-F2DC5C5D32FA","1":"A985157010653462","2":"80.92","3":"9.0","4":"2023-02-19 18:10:09","9":"1","10":"366.0","11":"0.0","12":"80.92","13":"80.92","14":"80.92","index":601},{"0":"DBECA025-8715-4299-B743-F2DC5C5D32FA","1":"A985157010653462","2":"80.92","3":"9.0","4":"2023-02-19 18:10:09","5":"80.92","6":"true","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"80.92","13":"80.92","14":"80.92","index":602},{"0":"5E74AE55-CFA6-4337-9B6B-CA9933D5A6D8","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 17:06:09","9":"1","10":"367.0","11":"0.0","12":"29.940000000000005","13":"29.940000000000005","14":"4.990000000000001","index":603},{"0":"531E0DC8-53E7-40A2-A8E4-A28F3ACE3F45","1":"A1688852207604610","2":"99.0","3":"11.0","4":"2023-03-11 01:58:24","9":"1","10":"366.0","11":"0.0","12":"99.0","13":"99.0","14":"99.0","index":604},{"0":"531E0DC8-53E7-40A2-A8E4-A28F3ACE3F45","1":"A1688852207604610","2":"99.0","3":"11.0","4":"2023-03-11 01:58:24","5":"103.48965","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"99.0","13":"99.0","14":"99.0","index":605},{"0":"0F4F57CC-5436-4377-9B91-B6C7D0CC67E8","1":"A844427868914574","2":"389.99","3":"11.0","4":"2023-03-17 11:35:53","9":"1","10":"366.0","11":"0.0","12":"779.98","13":"779.98","14":"389.99","index":606},{"0":"9D92C6BC-E560-4C32-A034-1E1F0814E46E","1":"A844428017843376","2":"107.52","3":"15.0","4":"2023-01-03 20:42:58","9":"1","10":"366.0","11":"0.0","12":"790.02","13":"790.02","14":"112.86","index":607},{"0":"12E12CAD-EC37-4E94-8D22-38BBE241C040","1":"A1899947010057920","2":"5097.0","3":"14.0","4":"2023-02-23 05:22:36","9":"1","10":"366.0","11":"0.0","12":"5097.0","13":"5097.0","14":"5097.0","index":608},{"0":"12E12CAD-EC37-4E94-8D22-38BBE241C040","1":"A1899947010057920","2":"5097.0","3":"14.0","4":"2023-02-23 05:22:36","5":"656.95233","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"5097.0","13":"5097.0","14":"5097.0","index":609},{"0":"F8226068-961C-4004-9061-B71F6C1665A2","1":"A1688853647452210","2":"423.99","3":"13.0","4":"2023-02-09 18:23:44","9":"1","10":"366.0","11":"0.0","12":"423.99","13":"423.99","14":"423.99","index":610},{"0":"F8226068-961C-4004-9061-B71F6C1665A2","1":"A1688853647452210","2":"423.99","3":"13.0","4":"2023-02-09 18:23:44","5":"423.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"423.99","13":"423.99","14":"423.99","index":611},{"0":"2DD082E0-85FA-4967-91C7-FB34908A1D89","1":"A1759222168021270","2":"63.95","3":"0.0","4":"2023-02-17 06:35:22","9":"1","10":"366.0","11":"0.0","12":"63.95","13":"63.95","14":"63.95","index":612},{"0":"2DD082E0-85FA-4967-91C7-FB34908A1D89","1":"A1759222168021270","2":"63.95","3":"0.0","4":"2023-02-17 06:35:22","5":"63.95","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"63.95","13":"63.95","14":"63.95","index":613},{"0":"57E282C4-3F49-485B-B5B8-CACAEEF05078","1":"A1759222088017710","2":"78750.0","3":"16.0","4":"2023-03-15 07:47:32","9":"1","10":"366.0","11":"0.0","12":"78750.0","13":"78750.0","14":"78750.0","index":614},{"0":"F6FBBB59-BFD5-4736-B635-64301B04D71C","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:42:53","9":"1","10":"366.0","11":"0.0","12":"7508.500000000003","13":"7508.500000000003","14":"147.22549019607848","index":615},{"0":"4EC3DEEC-D21B-4515-A3DE-5CC895397F21","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:59:16","9":"1","10":"366.0","11":"0.0","12":"4359.129999999997","13":"4359.129999999997","14":"145.30433333333323","index":616},{"0":"99E39E43-B8E7-48C8-8C6E-EE09902A63E0","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:36:07","9":"1","10":"366.0","11":"0.0","12":"299.85","13":"299.85","14":"19.990000000000002","index":617},{"0":"C6FD141A-3BE6-49A4-84BE-421F35CA31B2","1":"A914800247987042","2":"21.71","3":"19.0","4":"2023-02-24 00:50:43","9":"1","10":"2127.0","11":"0.0","12":"21.71","13":"21.71","14":"21.71","index":618},{"0":"C6FD141A-3BE6-49A4-84BE-421F35CA31B2","1":"A914800247987042","2":"21.71","3":"19.0","4":"2023-02-24 00:50:43","5":"21.71","6":"false","7":"0.0","8":"1.0","9":"1","10":"2127.0","11":"0.0","12":"21.71","13":"21.71","14":"21.71","index":619},{"0":"E4BE3429-CFC8-44D0-B48F-BCD7D875D0E9","1":"A1759222229520370","2":"599.0","3":"23.0","4":"2023-01-03 14:02:57","9":"1","10":"366.0","11":"0.0","12":"599.0","13":"599.0","14":"599.0","index":620},{"0":"E261B8E8-F241-46F3-89AA-5CC0283C8040","1":"A985156999024771","2":"92.5","3":"2.0","4":"2023-02-01 10:37:54","9":"1","10":"367.0","11":"0.0","12":"92.5","13":"92.5","14":"92.5","index":621},{"0":"13CC7673-A59D-45CF-8287-2B4E8EE05396","1":"A1055521399136000","2":"5.4","3":"23.0","4":"2023-02-27 04:46:49","9":"1","10":"366.0","11":"0.0","12":"16.200000000000003","13":"16.200000000000003","14":"5.400000000000001","index":622},{"0":"E52016D2-8898-4D8F-94EE-9D3357692C21","1":"A844428031214472","2":"2199.0","3":"9.0","4":"2023-01-06 13:14:20","9":"1","10":"366.0","11":"0.0","12":"2199.0","13":"2199.0","14":"2199.0","index":623},{"0":"E52016D2-8898-4D8F-94EE-9D3357692C21","1":"A844428031214472","2":"2199.0","3":"9.0","4":"2023-01-06 13:14:20","5":"442.12214400000005","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"2199.0","13":"2199.0","14":"2199.0","index":624},{"0":"10B457AE-179C-457B-90BA-1A0E6D8F5143","1":"A1055521388773990","2":"119.06","3":"20.0","4":"2023-01-15 02:43:22","9":"1","10":"366.0","11":"0.0","12":"119.06","13":"119.06","14":"119.06","index":625},{"0":"8B065D0D-9FBB-4815-B341-DDA432223550","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:35:25","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"262.28","13":"262.28","14":"65.57","index":626},{"0":"E24846FB-89B2-4304-8039-23C9271D1889","1":"A1688853516417610","2":"78750.0","3":"20.0","4":"2023-03-11 11:47:11","9":"1","10":"366.0","11":"0.0","12":"157500.0","13":"157500.0","14":"78750.0","index":627},{"0":"E24846FB-89B2-4304-8039-23C9271D1889","1":"A1688853516417610","2":"78750.0","3":"20.0","4":"2023-03-11 11:47:11","5":"868.6125","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"157500.0","13":"157500.0","14":"78750.0","index":628},{"0":"92CDB9E5-D34B-415C-B5D5-F4BA4C221855","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:57:07","9":"1","10":"366.0","11":"0.0","12":"4209.159999999997","13":"4209.159999999997","14":"145.14344827586197","index":629},{"0":"92CDB9E5-D34B-415C-B5D5-F4BA4C221855","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:57:07","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"4209.159999999997","13":"4209.159999999997","14":"145.14344827586197","index":630},{"0":"CFE1CF54-83D2-4396-88F9-7F0E7C2ED586","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:03:16","9":"1","10":"2365.0","11":"3.0","12":"490.4899999999991","13":"479.7099999999991","14":"5.38999999999999","index":631},{"0":"427FDE58-CA6D-4D48-B528-AD194856D115","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 17:02:10","9":"1","10":"367.0","11":"0.0","12":"19.96","13":"19.96","14":"4.99","index":632},{"0":"A5343E7D-5553-4F25-B916-E18FE75CF2AC","1":"A1688853516414440","2":"125580.0","3":"0.0","4":"2023-03-10 15:51:52","5":"1385.1474","6":"false","7":"2.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"284340.0","13":"284340.0","14":"94780.0","index":633},{"0":"90BAD1BA-E6EE-4EA6-B27F-7162B22CDBFA","1":"A844428017843376","2":"107.52","3":"15.0","4":"2023-01-03 20:39:16","9":"1","10":"366.0","11":"0.0","12":"562.52","13":"562.52","14":"112.50399999999999","index":634},{"0":"F69ECCC1-8BF7-48B4-80D8-75CF064D6017","1":"A914801012822158","2":"92.2","3":"9.0","4":"2023-02-02 14:42:25","9":"1","10":"367.0","11":"0.0","12":"196.45","13":"196.45","14":"98.225","index":635},{"0":"F69ECCC1-8BF7-48B4-80D8-75CF064D6017","1":"A914801012822158","2":"92.2","3":"9.0","4":"2023-02-02 14:42:25","5":"92.2","6":"false","7":"0.0","8":"1.0","9":"1","10":"367.0","11":"0.0","12":"196.45","13":"196.45","14":"98.225","index":636},{"0":"1504CCD5-386C-4201-8362-D235C13D93AC","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:07:35","9":"1","10":"2365.0","11":"3.0","12":"156.30999999999992","13":"145.52999999999994","14":"5.389999999999997","index":637},{"0":"7C1A8002-1E95-4100-B2E3-2EDAEFA7AC4E","1":"A1688853516414440","2":"79380.0","3":"0.0","4":"2023-03-10 15:49:34","9":"1","10":"366.0","11":"0.0","12":"158760.0","13":"158760.0","14":"79380.0","index":638},{"0":"7C1A8002-1E95-4100-B2E3-2EDAEFA7AC4E","1":"A1688853516414440","2":"79380.0","3":"0.0","4":"2023-03-10 15:49:34","5":"875.5614","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"158760.0","13":"158760.0","14":"79380.0","index":639},{"0":"BDA24FA7-E187-478C-82AE-865DBCF769F0","1":"A844428087822266","2":"278.88","3":"10.0","4":"2023-03-27 15:45:21","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":640},{"0":"BDA24FA7-E187-478C-82AE-865DBCF769F0","1":"A844428087822266","2":"278.88","3":"10.0","4":"2023-03-27 15:45:21","5":"277.72822599999995","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":641},{"0":"65020E58-781D-4FFC-BEF2-0FDF87BE671D","1":"A985156981092344","2":"1148.6","3":"20.0","4":"2023-01-02 02:05:50","9":"1","10":"366.0","11":"0.0","12":"1148.6","13":"1148.6","14":"1148.6","index":642},{"0":"65020E58-781D-4FFC-BEF2-0FDF87BE671D","1":"A985156981092344","2":"1148.6","3":"20.0","4":"2023-01-02 02:05:50","5":"1143.856282","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"1148.6","13":"1148.6","14":"1148.6","index":643},{"0":"CA5F63D9-CA1B-41E6-8C42-7F89EA103D1D","1":"A1899946895455190","2":"19.99","3":"13.0","4":"2023-01-13 18:31:01","9":"1","10":"366.0","11":"0.0","12":"59.97","13":"59.97","14":"19.99","index":644},{"0":"8454CEA5-B059-4E44-8ADE-100460ED0992","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:26:24","9":"1","10":"2365.0","11":"3.0","12":"274.88999999999965","13":"264.1099999999997","14":"5.3899999999999935","index":645},{"0":"CEA84E44-9194-48A0-A5F7-5BFA43E671BF","1":"A1055521399136000","2":"5.4","3":"23.0","4":"2023-02-27 04:44:44","9":"1","10":"366.0","11":"0.0","12":"5.4","13":"5.4","14":"5.4","index":646},{"0":"68162486-44E1-44E6-AB46-44EAF9D72EF6","1":"A985157024246345","2":"51.24","4":"2023-03-10 00:44:53","9":"1","10":"366.0","11":"0.0","12":"51.24","13":"51.24","14":"51.24","index":647},{"0":"68162486-44E1-44E6-AB46-44EAF9D72EF6","1":"A985157024246345","2":"51.24","4":"2023-03-10 00:44:53","5":"51.24","6":"false","7":"1.0","8":"3.0","9":"1","10":"366.0","11":"0.0","12":"51.24","13":"51.24","14":"51.24","index":648},{"0":"4BCAC6B0-3937-47E3-9AB8-6519D09321B1","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:01:57","9":"1","10":"366.0","11":"0.0","12":"53.99","13":"53.99","14":"53.99","index":649},{"0":"3F556D13-8CCB-400E-B9BD-582162BE610B","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:36:12","9":"1","10":"366.0","11":"0.0","12":"7058.590000000002","13":"7058.590000000002","14":"147.05395833333338","index":650},{"0":"C09DE962-3605-4A0E-B82F-A09B2B618D1F","1":"A1829582571089880","2":"233.19","3":"21.0","4":"2023-01-28 05:54:27","9":"1","10":"366.0","11":"0.0","12":"233.19","13":"233.19","14":"233.19","index":651},{"0":"D4C38B0B-DFEF-4F21-A66B-AC87B626BBFA","1":"A985156999024771","2":"93.07","3":"6.0","4":"2023-02-02 14:35:38","9":"1","10":"367.0","11":"0.0","12":"185.57","13":"185.57","14":"92.785","index":652},{"0":"D4C38B0B-DFEF-4F21-A66B-AC87B626BBFA","1":"A985156999024771","2":"93.07","3":"6.0","4":"2023-02-02 14:35:38","5":"93.07","6":"false","7":"0.0","8":"1.0","9":"1","10":"367.0","11":"0.0","12":"185.57","13":"185.57","14":"92.785","index":653},{"0":"70A54CA5-D32A-4DB1-8F56-B406D827A7B7","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:08:34","9":"1","10":"2365.0","11":"3.0","12":"161.6999999999999","13":"150.91999999999993","14":"5.389999999999997","index":654},{"0":"8C92EAEE-3E47-4410-89C9-F95000EC0707","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 16:57:54","9":"1","10":"367.0","11":"0.0","12":"14.97","13":"14.97","14":"4.99","index":655},{"0":"8C92EAEE-3E47-4410-89C9-F95000EC0707","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 16:57:54","5":"4.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"367.0","11":"0.0","12":"14.97","13":"14.97","14":"4.99","index":656},{"0":"306D1B72-286E-44A1-AF1E-BC01B6551B83","1":"A1688853662168790","2":"73080.0","3":"13.0","4":"2023-03-13 04:21:12","9":"1","10":"366.0","11":"0.0","12":"135660.0","13":"135660.0","14":"67830.0","index":657},{"0":"306D1B72-286E-44A1-AF1E-BC01B6551B83","1":"A1688853662168790","2":"73080.0","3":"13.0","4":"2023-03-13 04:21:12","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"135660.0","13":"135660.0","14":"67830.0","index":658},{"0":"DE2E99AB-BCA3-4E62-9774-17E62E4C50E0","1":"A985156985071493","2":"74.89","3":"16.0","4":"2023-01-08 15:39:26","9":"1","10":"366.0","11":"1.0","12":"74.89","13":"74.89","14":"74.89","index":659},{"0":"DE2E99AB-BCA3-4E62-9774-17E62E4C50E0","1":"A985156985071493","2":"74.89","3":"16.0","4":"2023-01-08 15:39:26","5":"74.89","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"1.0","12":"74.89","13":"74.89","14":"74.89","index":660},{"0":"3A13F395-6C81-4DD9-ABEB-FE7089DD21E2","1":"A985157000299534","2":"157.07","3":"14.0","4":"2023-02-03 19:29:54","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":661},{"0":"3A13F395-6C81-4DD9-ABEB-FE7089DD21E2","1":"A985157000299534","2":"157.07","3":"14.0","4":"2023-02-03 19:29:54","5":"156.421301","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":662},{"0":"5BCD2DBE-8524-4445-87E0-910F21C71046","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:58:13","9":"1","10":"2365.0","11":"3.0","12":"468.92999999999915","13":"458.1499999999992","14":"5.38999999999999","index":663},{"0":"5BCD2DBE-8524-4445-87E0-910F21C71046","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:58:13","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"468.92999999999915","13":"458.1499999999992","14":"5.38999999999999","index":664},{"0":"56BAD8D6-088E-4AD9-99E4-2C23D10C32B0","1":"A985156345961425","2":"109.99","3":"23.0","4":"2023-01-30 06:48:24","9":"1","10":"366.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":665},{"0":"CCA3EB41-5AB7-4C77-AFD0-BC552C95E019","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-26 11:27:30","9":"1","10":"2365.0","11":"3.0","12":"549.779999999999","13":"538.999999999999","14":"5.38999999999999","index":666},{"0":"2D228D45-7E38-45CF-BD23-8D4F3445FFF8","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 16:52:56","9":"1","10":"366.0","11":"0.0","12":"105.98","13":"105.98","14":"52.99","index":667},{"0":"3FBAFD91-D3AC-41CB-BC17-4CAAAC0C52EA","1":"A844428046730497","2":"278.88","3":"11.0","4":"2023-01-29 16:36:34","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":668},{"0":"18C1A371-128D-4C3E-BFA3-AD8A96C5A26E","1":"A1899946781648580","2":"228.79","3":"9.0","4":"2023-02-23 15:23:32","9":"1","10":"366.0","11":"0.0","12":"228.79","13":"228.79","14":"228.79","index":669},{"0":"61841EEB-00BE-478C-8142-677F86A55BD1","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:26:54","9":"1","10":"366.0","11":"0.0","12":"6608.680000000001","13":"6608.680000000001","14":"146.85955555555557","index":670},{"0":"F6C39AEA-B9AC-4718-9690-A47F81A7C38D","1":"A844427754541367","2":"116.86","3":"15.0","4":"2023-01-25 21:33:42","9":"1","10":"888.0","11":"0.0","12":"116.86","13":"116.86","14":"116.86","index":671},{"0":"F6C39AEA-B9AC-4718-9690-A47F81A7C38D","1":"A844427754541367","2":"116.86","3":"15.0","4":"2023-01-25 21:33:42","5":"116.86","6":"false","7":"1.0","8":"0.0","9":"1","10":"888.0","11":"0.0","12":"116.86","13":"116.86","14":"116.86","index":672},{"0":"624230B8-10F5-4BA5-A45B-BFF259872D75","1":"A985157010652653","2":"73080.0","3":"3.0","4":"2023-02-19 18:58:36","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":673},{"0":"624230B8-10F5-4BA5-A45B-BFF259872D75","1":"A985157010652653","2":"73080.0","3":"3.0","4":"2023-02-19 18:58:36","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":674},{"0":"7B14E6C6-F218-4C61-A29A-B3157D7A0E4A","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:26:55","9":"1","10":"2365.0","11":"3.0","12":"274.88999999999965","13":"264.1099999999997","14":"5.3899999999999935","index":675},{"0":"F6E6F205-C915-4FBF-837A-38163126A25A","1":"A985156290472668","2":"5.34","3":"20.0","4":"2023-02-26 02:10:31","9":"1","10":"2365.0","11":"0.0","12":"16.02","13":"16.02","14":"5.34","index":676},{"0":"F6E6F205-C915-4FBF-837A-38163126A25A","1":"A985156290472668","2":"5.34","3":"20.0","4":"2023-02-26 02:10:31","5":"5.34","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"0.0","12":"16.02","13":"16.02","14":"5.34","index":677},{"0":"218C5731-A37A-4CBA-8D3B-EF20FF23DEAE","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:37:03","9":"1","10":"367.0","11":"0.0","12":"54.890000000000015","13":"54.890000000000015","14":"4.990000000000001","index":678},{"0":"216991DF-33A6-4090-90AA-3E09DDF390E4","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 16:48:52","9":"1","10":"367.0","11":"0.0","12":"9.98","13":"9.98","14":"4.99","index":679},{"0":"1E7C21A8-930E-43D4-8044-8356F16FACB2","1":"A844428039854278","2":"76.19","3":"14.0","4":"2023-01-16 19:23:46","9":"1","10":"366.0","11":"0.0","12":"76.19","13":"76.19","14":"76.19","index":680},{"0":"1E7C21A8-930E-43D4-8044-8356F16FACB2","1":"A844428039854278","2":"76.19","3":"14.0","4":"2023-01-16 19:23:46","5":"76.19","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"76.19","13":"76.19","14":"76.19","index":681},{"0":"2C8A9E18-E28E-4555-B5F9-98F0D34FCEAE","1":"A985157010652653","2":"73080.0","3":"3.0","4":"2023-02-19 18:48:28","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":682},{"0":"E498A57D-E5CF-4883-932E-6B39D7CFF7A0","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:38:18","9":"1","10":"2365.0","11":"3.0","12":"296.4499999999996","13":"285.6699999999996","14":"5.389999999999993","index":683},{"0":"37E89109-BF6A-4056-AAE5-3CECBB90641E","1":"A985156954406966","2":"240.45","3":"19.0","4":"2023-02-28 01:52:31","9":"1","10":"366.0","11":"0.0","12":"480.9","13":"480.9","14":"240.45","index":684},{"0":"37E89109-BF6A-4056-AAE5-3CECBB90641E","1":"A985156954406966","2":"240.45","3":"19.0","4":"2023-02-28 01:52:31","5":"240.45","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"480.9","13":"480.9","14":"240.45","index":685},{"0":"92EA085E-FD70-425B-80EB-4513BFBA8AF1","1":"A985157038112964","2":"150.14","3":"22.0","4":"2023-03-26 03:25:46","9":"1","10":"366.0","11":"0.0","12":"300.28","13":"300.28","14":"150.14","index":686},{"0":"F06A587E-F353-481B-9BBA-FFAC28C60777","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:46:21","9":"1","10":"366.0","11":"0.0","12":"7658.470000000003","13":"7658.470000000003","14":"147.2782692307693","index":687},{"0":"F06A587E-F353-481B-9BBA-FFAC28C60777","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:46:21","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"7658.470000000003","13":"7658.470000000003","14":"147.2782692307693","index":688},{"0":"8C337F6E-E17F-4CDA-833A-CBEF20C65CF9","1":"A985157015268058","2":"4.99","3":"11.0","4":"2023-02-26 17:04:05","9":"1","10":"367.0","11":"0.0","12":"24.950000000000003","13":"24.950000000000003","14":"4.99","index":689},{"0":"8C99CEBB-0BCA-4D2C-BEB7-09A627F46CDF","1":"A1055520836257310","2":"5.04","3":"5.0","4":"2023-02-28 10:45:27","9":"1","10":"1555.0","11":"0.0","12":"30.24","13":"30.24","14":"5.04","index":690},{"0":"C077446E-640F-4769-9527-5A727F34A4B4","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:38:29","9":"1","10":"366.0","11":"0.0","12":"319.84000000000003","13":"319.84000000000003","14":"19.990000000000002","index":691},{"0":"9BC1E2D0-E438-4343-B319-212B06186B71","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:29:01","9":"1","10":"2365.0","11":"3.0","12":"64.67999999999999","13":"64.67999999999999","14":"5.39","index":692},{"0":"1FB77AC2-B16B-468E-B8AD-D83F82586D10","1":"A1829582571084310","2":"222.19","3":"22.0","4":"2023-02-28 04:17:41","9":"1","10":"366.0","11":"0.0","12":"222.19","13":"222.19","14":"222.19","index":693},{"0":"1FB77AC2-B16B-468E-B8AD-D83F82586D10","1":"A1829582571084310","2":"222.19","3":"22.0","4":"2023-02-28 04:17:41","5":"222.19","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"222.19","13":"222.19","14":"222.19","index":694},{"0":"990FAC29-0BAC-4D26-A896-27E71BA5144A","1":"A844428035915015","2":"86.17","3":"23.0","4":"2023-01-12 04:22:36","9":"1","10":"366.0","11":"0.0","12":"86.17","13":"86.17","14":"86.17","index":695},{"0":"990FAC29-0BAC-4D26-A896-27E71BA5144A","1":"A844428035915015","2":"86.17","3":"23.0","4":"2023-01-12 04:22:36","5":"86.17","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"86.17","13":"86.17","14":"86.17","index":696},{"0":"EC085107-C5DF-4814-8E31-374D3D8BD5AD","1":"A564049508937862","2":"482.68","3":"10.0","4":"2023-03-06 18:31:45","5":"482.68","6":"false","7":"1.0","8":"1.0","9":"0","10":"2365.0","11":"0.0","12":"482.68","13":"482.68","14":"482.68","index":697},{"0":"0C9B4BE7-CEDB-4B19-9061-855CEC55B0FF","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:48:25","9":"1","10":"2365.0","11":"3.0","12":"339.5699999999995","13":"328.7899999999995","14":"5.389999999999992","index":698},{"0":"0C9B4BE7-CEDB-4B19-9061-855CEC55B0FF","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:48:25","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"339.5699999999995","13":"328.7899999999995","14":"5.389999999999992","index":699},{"0":"4B54A952-E219-4AD1-8DB2-CAD464FCC43A","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:46:44","9":"1","10":"366.0","11":"0.0","12":"309.94","13":"309.94","14":"103.31333333333333","index":700},{"0":"B6B66DCD-8211-47F8-A921-AED2FD47536F","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:18:16","9":"1","10":"2365.0","11":"3.0","12":"237.1599999999997","13":"226.37999999999974","14":"5.3899999999999935","index":701},{"0":"D74EFAAF-1FCC-4F41-AB01-317001E86EF1","1":"A844427390246047","2":"5.39","3":"14.0","4":"2023-02-24 19:42:15","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"32.339999999999996","13":"32.339999999999996","14":"5.39","index":702},{"0":"B6B66DCD-8211-47F8-A921-AED2FD47536F","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:18:16","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"237.1599999999997","13":"226.37999999999974","14":"5.3899999999999935","index":703},{"0":"976C63FB-B108-4C8E-B190-C37F1FF602B4","1":"A985156290472668","2":"5.34","3":"20.0","4":"2023-02-26 02:09:47","9":"1","10":"2365.0","11":"0.0","12":"10.68","13":"10.68","14":"5.34","index":704},{"0":"976C63FB-B108-4C8E-B190-C37F1FF602B4","1":"A985156290472668","2":"5.34","3":"20.0","4":"2023-02-26 02:09:47","5":"5.34","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"0.0","12":"10.68","13":"10.68","14":"5.34","index":705},{"0":"DFDDB7C2-7E6D-41A1-86CC-84FCF8F3C8E8","1":"A914800689334401","2":"109.99","3":"16.0","4":"2023-03-11 01:06:36","9":"1","10":"372.0","11":"0.0","12":"219.98","13":"109.99","14":"109.99","index":706},{"0":"DFDDB7C2-7E6D-41A1-86CC-84FCF8F3C8E8","1":"A914800689334401","2":"109.99","3":"16.0","4":"2023-03-11 01:06:36","5":"109.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"372.0","11":"0.0","12":"219.98","13":"109.99","14":"109.99","index":707},{"0":"241DBB1D-3092-4FCE-A4A5-00219ECD6C99","1":"A1055521428449680","2":"278.88","3":"22.0","4":"2023-03-22 03:35:42","9":"1","10":"366.0","11":"0.0","12":"278.88","13":"278.88","14":"278.88","index":708},{"0":"C5369F85-BC0A-43F2-B7F2-4930B89DC246","1":"A844428031222008","2":"54.26","3":"19.0","4":"2023-01-05 01:15:30","9":"1","10":"366.0","11":"0.0","12":"54.26","13":"54.26","14":"54.26","index":709},{"0":"C5369F85-BC0A-43F2-B7F2-4930B89DC246","1":"A844428031222008","2":"54.26","3":"19.0","4":"2023-01-05 01:15:30","5":"54.26","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"54.26","13":"54.26","14":"54.26","index":710},{"0":"C7D4CBEF-2E68-41A2-B83A-D064795E296E","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:16:55","9":"1","10":"366.0","11":"0.0","12":"5858.83","13":"5858.83","14":"146.47075","index":711},{"0":"C4286407-3D76-4187-8FD8-EEF5CE3E26A2","1":"A844428047343854","2":"106.24","3":"19.0","4":"2023-02-01 01:23:58","9":"1","10":"366.0","11":"0.0","12":"106.24","13":"106.24","14":"106.24","index":712},{"0":"C4286407-3D76-4187-8FD8-EEF5CE3E26A2","1":"A844428047343854","2":"106.24","3":"19.0","4":"2023-02-01 01:23:58","5":"106.24","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"106.24","13":"106.24","14":"106.24","index":713},{"0":"2104E85A-B76B-419E-8204-3EAB76C7D122","1":"A844427390246047","2":"5.39","3":"13.0","4":"2023-02-24 19:11:03","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"16.169999999999998","13":"16.169999999999998","14":"5.39","index":714},{"0":"FFCA8791-18C3-4C81-9D89-1250936E41E1","1":"A844428082845952","2":"147.83","3":"13.0","4":"2023-03-21 19:04:04","9":"1","10":"366.0","11":"0.0","12":"295.66","13":"295.66","14":"147.83","index":715},{"0":"704EA48E-FAA1-485B-88C9-6F3E21CAC2E7","1":"A914800134032375","2":"109.99","3":"17.0","4":"2023-02-12 22:52:26","9":"1","10":"562.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":716},{"0":"704EA48E-FAA1-485B-88C9-6F3E21CAC2E7","1":"A914800134032375","2":"109.99","3":"17.0","4":"2023-02-12 22:52:26","5":"109.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"562.0","11":"0.0","12":"109.99","13":"109.99","14":"109.99","index":717},{"0":"64C56D07-3CE5-42DD-8ACD-86FCEC01CC7A","1":"A1899946873128090","2":"127.48","3":"22.0","4":"2023-02-15 03:48:42","9":"1","10":"366.0","11":"0.0","12":"443.02000000000004","13":"443.02000000000004","14":"110.75500000000001","index":718},{"0":"64C56D07-3CE5-42DD-8ACD-86FCEC01CC7A","1":"A1899946873128090","2":"127.48","3":"22.0","4":"2023-02-15 03:48:42","5":"127.48","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"443.02000000000004","13":"443.02000000000004","14":"110.75500000000001","index":719},{"0":"A23C51AD-50BC-4719-B489-A29643BB3C74","1":"A1829582519835540","2":"59.99","3":"4.0","4":"2023-02-17 11:20:25","9":"1","10":"366.0","11":"0.0","12":"187.17000000000002","13":"187.17000000000002","14":"62.39000000000001","index":720},{"0":"DE94FF49-79D4-417C-87B2-6682EC4AA382","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:38:47","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"458.98999999999995","13":"458.98999999999995","14":"65.57","index":721},{"0":"B070007C-549E-4211-920D-AF0712332B4B","1":"A985156984009293","2":"127.19","4":"2023-01-06 17:45:56","9":"1","10":"366.0","11":"0.0","12":"143.04","13":"143.04","14":"71.52","index":722},{"0":"B070007C-549E-4211-920D-AF0712332B4B","1":"A985156984009293","2":"127.19","4":"2023-01-06 17:45:56","5":"127.19","6":"false","7":"1.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"143.04","13":"143.04","14":"71.52","index":723},{"0":"F5F315E9-DA90-463E-B981-7D7EB8FFB197","1":"A914801014875116","2":"238.14","3":"15.0","4":"2023-02-03 20:37:59","9":"1","10":"366.0","11":"0.0","12":"238.14","13":"238.14","14":"238.14","index":724},{"0":"F5F315E9-DA90-463E-B981-7D7EB8FFB197","1":"A914801014875116","2":"238.14","3":"15.0","4":"2023-02-03 20:37:59","5":"238.14","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"238.14","13":"238.14","14":"238.14","index":725},{"0":"CF2FC7F2-2924-4EC3-8951-1F5785BE3003","1":"A914800889932462","2":"53.36","3":"6.0","4":"2023-02-16 12:58:46","9":"1","10":"366.0","11":"0.0","12":"53.36","13":"53.36","14":"53.36","index":726},{"0":"CF2FC7F2-2924-4EC3-8951-1F5785BE3003","1":"A914800889932462","2":"53.36","3":"6.0","4":"2023-02-16 12:58:46","5":"53.36","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"53.36","13":"53.36","14":"53.36","index":727},{"0":"0EB011D9-CAF2-4A02-8ACF-03895BE44128","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-21 20:53:06","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"10.78","13":"10.78","14":"5.39","index":728},{"0":"D3507A5B-38A9-40F9-B992-DB1547B281C9","1":"A1688853516417610","2":"78750.0","3":"20.0","4":"2023-03-11 11:45:45","9":"1","10":"366.0","11":"0.0","12":"78750.0","13":"78750.0","14":"78750.0","index":729},{"0":"E617D42C-798C-455C-8372-BC06259DE735","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:11:03","9":"1","10":"366.0","11":"0.0","12":"377.93","13":"377.93","14":"53.99","index":730},{"0":"C0C109CA-5ECF-40E1-8D9E-55025AB4FBCF","1":"A844428041547410","2":"99.99","4":"2023-01-19 10:44:26","9":"1","10":"366.0","11":"0.0","12":"199.98","13":"199.98","14":"99.99","index":731},{"0":"C0C109CA-5ECF-40E1-8D9E-55025AB4FBCF","1":"A844428041547410","2":"99.99","4":"2023-01-19 10:44:26","5":"157.474251","6":"false","7":"1.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"199.98","13":"199.98","14":"99.99","index":732},{"0":"EC41414F-5244-47AC-9F4F-DC7EAF66FA86","1":"A985156344741724","2":"239.0","3":"22.0","4":"2023-02-10 01:50:17","9":"1","10":"366.0","11":"0.0","12":"239.0","13":"239.0","14":"239.0","index":733},{"0":"EC41414F-5244-47AC-9F4F-DC7EAF66FA86","1":"A985156344741724","2":"239.0","3":"22.0","4":"2023-02-10 01:50:17","5":"120.10945","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"239.0","13":"239.0","14":"239.0","index":734},{"0":"6645E186-2EE1-4F51-883A-7C4333C753C3","1":"A296624604869030","2":"139.99","3":"13.0","4":"2023-02-17 22:08:38","9":"1","10":"366.0","11":"0.0","12":"419.97","13":"419.97","14":"139.99","index":735},{"0":"14B43B88-2D00-4D03-BDDA-3484C0D86769","1":"A1688853524321290","2":"24.99","3":"19.0","4":"2023-01-05 00:26:00","9":"1","10":"366.0","11":"0.0","12":"24.99","13":"24.99","14":"24.99","index":736},{"0":"2E0F3E5F-83E2-4414-8D46-00367E4279AD","1":"A844427390246047","2":"5.39","3":"12.0","4":"2023-02-28 17:41:39","9":"1","10":"2365.0","11":"3.0","12":"636.0199999999987","13":"296.4499999999996","14":"5.389999999999989","index":737},{"0":"547D4C95-528D-46FC-A5A5-FA307B44A329","1":"A844427390246047","2":"5.39","3":"5.0","4":"2023-02-25 11:16:20","9":"1","10":"2365.0","11":"3.0","12":"215.59999999999977","13":"204.8199999999998","14":"5.389999999999994","index":738},{"0":"AED3F5DA-87AB-45C8-A0FF-426E353FB102","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:31:04","9":"1","10":"366.0","11":"0.0","12":"1079.8","13":"1079.8","14":"53.989999999999995","index":739},{"0":"AFC2F084-490B-4B4F-AF6A-05B999BC0C81","1":"A914801014875116","2":"238.14","3":"15.0","4":"2023-02-03 20:45:40","5":"238.14","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"476.28","13":"476.28","14":"238.14","index":740},{"0":"3319C4C1-94A7-45C7-AFC3-D1FD19FDEA8E","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:54:06","9":"1","10":"2365.0","11":"3.0","12":"431.19999999999925","13":"420.4199999999993","14":"5.389999999999991","index":741},{"0":"E8E24E34-5D81-4891-BACF-7CD208CD56D4","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 21:09:15","9":"1","10":"2365.0","11":"3.0","12":"161.6999999999999","13":"150.91999999999993","14":"5.389999999999997","index":742},{"0":"3319C4C1-94A7-45C7-AFC3-D1FD19FDEA8E","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:54:06","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"431.19999999999925","13":"420.4199999999993","14":"5.389999999999991","index":743},{"0":"D247B57A-BE03-44A2-ABCB-E73EA6FCD843","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:29:52","9":"1","10":"366.0","11":"0.0","12":"2859.429999999999","13":"2859.429999999999","14":"142.97149999999993","index":744},{"0":"6D96EB12-FD10-4D59-8262-C477E3B4A7FF","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:18:40","9":"1","10":"366.0","11":"0.0","12":"6008.8","13":"6008.8","14":"146.55609756097562","index":745},{"0":"442116BE-E400-48F2-80E8-C15B1DECFB1E","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:51:35","9":"1","10":"366.0","11":"0.0","12":"3759.2499999999977","13":"3759.2499999999977","14":"144.58653846153837","index":746},{"0":"4AD79610-DF94-40EA-AA57-0D0C4C107950","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:49:19","9":"1","10":"2365.0","11":"3.0","12":"382.6899999999994","13":"371.9099999999994","14":"5.389999999999991","index":747},{"0":"D8EFBBA0-BEF9-4BEA-A6F0-4A5CBE4C1287","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:09:43","9":"1","10":"366.0","11":"0.0","12":"5408.919999999999","13":"5408.919999999999","14":"146.187027027027","index":748},{"0":"770A0525-2D27-4173-B6BF-A5EA905162DC","1":"A1055521399136000","2":"5.4","3":"23.0","4":"2023-02-27 04:47:43","9":"1","10":"366.0","11":"0.0","12":"16.200000000000003","13":"16.200000000000003","14":"5.400000000000001","index":749},{"0":"2C5B2DC8-5E8D-407D-8894-A0D0EB229AA3","1":"A914800389046486","2":"107.99","3":"12.0","4":"2023-01-14 17:25:43","9":"1","10":"366.0","11":"0.0","12":"107.99","13":"107.99","14":"107.99","index":750},{"0":"2C5B2DC8-5E8D-407D-8894-A0D0EB229AA3","1":"A914800389046486","2":"107.99","3":"12.0","4":"2023-01-14 17:25:43","5":"107.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"107.99","13":"107.99","14":"107.99","index":751},{"0":"4186FC50-1BBE-4188-94FD-5659E850B7A3","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:45:29","9":"1","10":"366.0","11":"0.0","12":"3459.309999999998","13":"3459.309999999998","14":"144.1379166666666","index":752},{"0":"2132335C-082B-4B7E-9B63-DA007878168D","1":"A844428017843376","2":"107.52","3":"15.0","4":"2023-01-03 20:32:18","9":"1","10":"366.0","11":"0.0","12":"335.02","13":"335.02","14":"111.67333333333333","index":753},{"0":"2132335C-082B-4B7E-9B63-DA007878168D","1":"A844428017843376","2":"107.52","3":"15.0","4":"2023-01-03 20:32:18","5":"107.52","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"335.02","13":"335.02","14":"111.67333333333333","index":754},{"0":"41F4234C-1659-48C5-A9D1-4C2324A0891B","1":"A1899946895455190","2":"19.99","3":"11.0","4":"2023-01-13 16:52:56","9":"1","10":"366.0","11":"0.0","12":"19.99","13":"19.99","14":"19.99","index":755},{"0":"6A2D4479-6262-4C32-95B5-AC5BE0205E24","1":"A1899946895455190","2":"19.99","3":"13.0","4":"2023-01-13 18:47:34","9":"1","10":"366.0","11":"0.0","12":"99.94999999999999","13":"99.94999999999999","14":"19.99","index":756},{"0":"6F7F79E1-1A74-4AF6-9706-830302FC7942","1":"A844428029043738","2":"21.6","3":"10.0","4":"2023-02-07 17:01:22","9":"1","10":"366.0","11":"0.0","12":"21.6","13":"21.6","14":"21.6","index":757},{"0":"6F7F79E1-1A74-4AF6-9706-830302FC7942","1":"A844428029043738","2":"21.6","3":"10.0","4":"2023-02-07 17:01:22","5":"21.6","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"21.6","13":"21.6","14":"21.6","index":758},{"0":"CBCE65DD-1D82-4C65-91BD-683020CF1D6E","1":"A1688853596241210","2":"59.99","3":"6.0","4":"2023-02-16 12:54:17","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":759},{"0":"CBCE65DD-1D82-4C65-91BD-683020CF1D6E","1":"A1688853596241210","2":"59.99","3":"6.0","4":"2023-02-16 12:54:17","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":760},{"0":"CDD70229-CFCE-4192-829D-980CC5F0AD96","1":"A844427390246047","2":"5.39","3":"9.0","4":"2023-02-28 14:30:20","9":"1","10":"2365.0","11":"3.0","12":"571.3399999999989","13":"231.76999999999973","14":"5.38999999999999","index":761},{"0":"209685D0-27D7-42CE-A3A2-24F643DD387F","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:52:17","9":"1","10":"2365.0","11":"3.0","12":"415.0299999999993","13":"404.2499999999993","14":"5.389999999999991","index":762},{"0":"EC68A65E-1901-4CE2-8BAF-92E4734A4D74","1":"A985157026813391","2":"226.95","3":"17.0","4":"2023-03-13 01:35:43","9":"1","10":"366.0","11":"0.0","12":"226.95","13":"226.95","14":"226.95","index":763},{"0":"EC68A65E-1901-4CE2-8BAF-92E4734A4D74","1":"A985157026813391","2":"226.95","3":"17.0","4":"2023-03-13 01:35:43","5":"226.95","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"226.95","13":"226.95","14":"226.95","index":764},{"0":"5F5F30FC-7EA0-41CE-B5A4-FA459FDDBE38","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:09:49","9":"1","10":"366.0","11":"0.0","12":"323.94","13":"323.94","14":"53.99","index":765},{"0":"552F9E0D-5395-4995-9047-D71CD17F48CB","1":"A1055520836257310","2":"5.04","3":"6.0","4":"2023-02-28 12:19:58","9":"1","10":"1555.0","11":"0.0","12":"40.32","13":"40.32","14":"5.04","index":766},{"0":"BD06116A-1C08-45CB-8BAC-515E522168AE","1":"A844428082845952","2":"147.83","3":"13.0","4":"2023-03-21 19:00:49","9":"1","10":"366.0","11":"0.0","12":"147.83","13":"147.83","14":"147.83","index":767},{"0":"BD06116A-1C08-45CB-8BAC-515E522168AE","1":"A844428082845952","2":"147.83","3":"13.0","4":"2023-03-21 19:00:49","5":"147.83","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"147.83","13":"147.83","14":"147.83","index":768},{"0":"63065B44-AA7C-46D9-A9B3-E4E1C36667F8","1":"A2040691497893890","2":"279.98","3":"11.0","4":"2023-01-14 17:58:29","9":"1","10":"366.0","11":"0.0","12":"279.98","13":"279.98","14":"279.98","index":769},{"0":"63065B44-AA7C-46D9-A9B3-E4E1C36667F8","1":"A2040691497893890","2":"279.98","3":"11.0","4":"2023-01-14 17:58:29","5":"279.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"279.98","13":"279.98","14":"279.98","index":770},{"0":"298F7C38-80B2-4CC3-8D84-29A2B5B7797C","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:48:42","9":"1","10":"366.0","11":"0.0","12":"7808.440000000003","13":"7808.440000000003","14":"147.32905660377364","index":771},{"0":"A6420C3F-3816-4043-B8F2-73B7C384475E","1":"A1688852501978320","2":"134.81","3":"14.0","4":"2023-01-27 19:30:57","9":"1","10":"2184.0","11":"0.0","12":"134.81","13":"134.81","14":"134.81","index":772},{"0":"A6420C3F-3816-4043-B8F2-73B7C384475E","1":"A1688852501978320","2":"134.81","3":"14.0","4":"2023-01-27 19:30:57","5":"134.81","6":"false","7":"1.0","8":"0.0","9":"1","10":"2184.0","11":"0.0","12":"134.81","13":"134.81","14":"134.81","index":773},{"0":"FEE49FF0-87EB-402B-8EBB-8F9F4C9CB225","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:33:55","9":"1","10":"366.0","11":"0.0","12":"1187.78","13":"1187.78","14":"53.99","index":774},{"0":"4FD412FF-3716-4007-8C81-80FDA381E04A","1":"A914800889937190","2":"53.11","3":"13.0","4":"2023-02-16 18:37:03","9":"1","10":"366.0","11":"0.0","12":"106.22","13":"106.22","14":"53.11","index":775},{"0":"4D988189-A1DB-41FE-80CA-E338285D302F","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:46:05","9":"1","10":"2365.0","11":"3.0","12":"361.1299999999994","13":"350.34999999999945","14":"5.389999999999992","index":776},{"0":"4D988189-A1DB-41FE-80CA-E338285D302F","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:46:05","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"361.1299999999994","13":"350.34999999999945","14":"5.389999999999992","index":777},{"0":"903D435A-A4E4-489C-95E8-E97B11439B7C","1":"A1829582571084310","2":"222.19","3":"22.0","4":"2023-02-28 04:19:19","9":"1","10":"366.0","11":"0.0","12":"444.38","13":"444.38","14":"222.19","index":778},{"0":"B1DCEA69-8515-497E-8CAF-FEF5A9A4E5C5","1":"A1899947009495910","2":"538.0","3":"22.0","4":"2023-03-14 13:49:32","9":"1","10":"366.0","11":"0.0","12":"1614.0","13":"1614.0","14":"538.0","index":779},{"0":"B1DCEA69-8515-497E-8CAF-FEF5A9A4E5C5","1":"A1899947009495910","2":"538.0","3":"22.0","4":"2023-03-14 13:49:32","5":"725.1164","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"1614.0","13":"1614.0","14":"538.0","index":780},{"0":"AF64911E-A295-4337-BBAF-835F82DD77BA","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:41:16","9":"1","10":"366.0","11":"0.0","12":"3309.3399999999983","13":"3309.3399999999983","14":"143.8843478260869","index":781},{"0":"422D5F57-6B6E-4803-AD06-9BF33330F102","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:48:55","9":"1","10":"366.0","11":"0.0","12":"459.90999999999997","13":"459.90999999999997","14":"114.97749999999999","index":782},{"0":"ABA4D27A-EDFF-47E7-9F92-DE6F02753299","1":"A1688853596356500","2":"59.99","3":"1.0","4":"2023-02-17 06:50:03","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":783},{"0":"ABA4D27A-EDFF-47E7-9F92-DE6F02753299","1":"A1688853596356500","2":"59.99","3":"1.0","4":"2023-02-17 06:50:03","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":784},{"0":"01CE8E80-F8C6-4EA6-92A5-4D3077CE9449","1":"A844427390246047","2":"5.39","3":"14.0","4":"2023-02-24 20:06:05","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"2365.0","11":"3.0","12":"37.73","13":"37.73","14":"5.39","index":785},{"0":"7898FC86-CDBF-469B-B369-5E70B9BAC1F1","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:01:54","9":"1","10":"366.0","11":"0.0","12":"1509.7","13":"1509.7","14":"137.24545454545455","index":786},{"0":"3CFD786C-1D49-4D2D-94DA-C3A0E26ACB02","1":"A914801013785993","2":"93.94","3":"0.0","4":"2023-02-08 06:19:27","5":"93.94","6":"false","7":"0.0","8":"1.0","9":"0","10":"371.0","11":"0.0","12":"275.94","13":"93.94","14":"91.98","index":787},{"0":"209C8814-08A3-4199-99FE-B7E3C99EB101","1":"A1055521400546730","2":"119.99","4":"2023-02-14 20:25:30","9":"1","10":"366.0","11":"0.0","12":"119.99","13":"119.99","14":"119.99","index":788},{"0":"209C8814-08A3-4199-99FE-B7E3C99EB101","1":"A1055521400546730","2":"119.99","4":"2023-02-14 20:25:30","5":"119.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.99","13":"119.99","14":"119.99","index":789},{"0":"C54EC40B-86FC-497A-A701-8FBDE5A2375D","1":"A844427390246047","2":"5.39","3":"11.0","4":"2023-02-28 17:08:37","9":"1","10":"2365.0","11":"3.0","12":"598.2899999999988","13":"258.7199999999997","14":"5.389999999999989","index":790},{"0":"1D803FD7-051D-4D75-94DC-214FC9E55633","1":"A1759222219270900","2":"235.39","3":"20.0","4":"2023-02-28 01:28:55","5":"235.39","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"470.78","13":"470.78","14":"235.39","index":791},{"0":"1AB327E5-AD02-4407-853C-1024966EAEA6","1":"A1055521435204170","2":"286.35","3":"18.0","4":"2023-03-29 23:52:59","9":"1","10":"366.0","11":"0.0","12":"572.7","13":"572.7","14":"286.35","index":792},{"0":"1AB327E5-AD02-4407-853C-1024966EAEA6","1":"A1055521435204170","2":"286.35","3":"18.0","4":"2023-03-29 23:52:59","5":"285.167374","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"572.7","13":"572.7","14":"286.35","index":793},{"0":"5867EAE8-3A64-4EB2-8CCF-F4F6A119B088","1":"A844428035402480","2":"63.89","3":"11.0","4":"2023-01-11 16:24:52","9":"1","10":"366.0","11":"0.0","12":"63.89","13":"63.89","14":"63.89","index":794},{"0":"5867EAE8-3A64-4EB2-8CCF-F4F6A119B088","1":"A844428035402480","2":"63.89","3":"11.0","4":"2023-01-11 16:24:52","5":"63.89","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"63.89","13":"63.89","14":"63.89","index":795},{"0":"F53D4B5F-01AB-48A8-A833-F1CBA086B76D","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:44:03","9":"1","10":"366.0","11":"0.0","12":"439.7800000000001","13":"439.7800000000001","14":"19.990000000000006","index":796},{"0":"F53D4B5F-01AB-48A8-A833-F1CBA086B76D","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:44:03","5":"19.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"439.7800000000001","13":"439.7800000000001","14":"19.990000000000006","index":797},{"0":"0C064A95-7D41-4C84-A18E-816228CAB89E","1":"A1829582571100710","2":"239.51","3":"16.0","4":"2023-02-01 21:56:13","9":"1","10":"366.0","11":"0.0","12":"479.02","13":"479.02","14":"239.51","index":798},{"0":"635526D8-1F35-4ABE-88EC-7D5276A36C51","1":"A1055521381916150","2":"26.54","3":"18.0","4":"2023-01-06 00:33:10","9":"1","10":"366.0","11":"0.0","12":"26.54","13":"26.54","14":"26.54","index":799},{"0":"635526D8-1F35-4ABE-88EC-7D5276A36C51","1":"A1055521381916150","2":"26.54","3":"18.0","4":"2023-01-06 00:33:10","5":"26.54","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"26.54","13":"26.54","14":"26.54","index":800},{"0":"E9D36D5B-FDCD-440A-BC1A-C53A9110CF22","1":"A844428017843376","2":"119.98","3":"15.0","4":"2023-01-03 20:46:52","9":"1","10":"366.0","11":"0.0","12":"910.0","13":"910.0","14":"113.75","index":801},{"0":"AC844DF5-52D9-4A71-A6B8-690139185C7B","1":"A1055521395382470","2":"153.29","3":"20.0","4":"2023-02-06 04:27:22","9":"1","10":"366.0","11":"0.0","12":"306.58","13":"306.58","14":"153.29","index":802},{"0":"AC844DF5-52D9-4A71-A6B8-690139185C7B","1":"A1055521395382470","2":"153.29","3":"20.0","4":"2023-02-06 04:27:22","5":"153.29","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"306.58","13":"306.58","14":"153.29","index":803},{"0":"1901E38A-A427-4BA3-8C82-C9DB2B8C2735","1":"A1829582519832190","2":"59.99","3":"2.0","4":"2023-02-17 08:15:30","9":"1","10":"366.0","11":"0.0","12":"239.96","13":"239.96","14":"59.99","index":804},{"0":"1901E38A-A427-4BA3-8C82-C9DB2B8C2735","1":"A1829582519832190","2":"59.99","3":"2.0","4":"2023-02-17 08:15:30","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"239.96","13":"239.96","14":"59.99","index":805},{"0":"C1399C61-74D6-40B3-8289-AC99FAFF4988","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:36:45","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"327.84999999999997","13":"327.84999999999997","14":"65.57","index":806},{"0":"CD624353-E473-4EE0-8BDF-A818627AA1D1","1":"A985156970845915","2":"99.98","3":"11.0","4":"2023-01-03 16:40:50","9":"1","10":"366.0","11":"0.0","12":"99.98","13":"99.98","14":"99.98","index":807},{"0":"CD624353-E473-4EE0-8BDF-A818627AA1D1","1":"A985156970845915","2":"99.98","3":"11.0","4":"2023-01-03 16:40:50","5":"99.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"99.98","13":"99.98","14":"99.98","index":808},{"0":"6B3337E4-83E7-49DA-97BA-D0DC2B44F42F","1":"A844428017843376","2":"119.98","3":"15.0","4":"2023-01-03 20:41:07","9":"1","10":"366.0","11":"0.0","12":"682.5","13":"682.5","14":"113.75","index":809},{"0":"DCCC41EB-D464-4684-B53D-C89C70D194B3","1":"A844428056745591","2":"49.99","3":"12.0","4":"2023-02-16 20:29:30","9":"1","10":"366.0","11":"0.0","12":"49.99","13":"49.99","14":"49.99","index":810},{"0":"DCCC41EB-D464-4684-B53D-C89C70D194B3","1":"A844428056745591","2":"49.99","3":"12.0","4":"2023-02-16 20:29:30","5":"49.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"49.99","13":"49.99","14":"49.99","index":811},{"0":"B94780BA-6067-4DAD-AA2A-BACE10FA26EA","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:55:42","9":"1","10":"2365.0","11":"3.0","12":"447.3699999999992","13":"436.58999999999924","14":"5.389999999999991","index":812},{"0":"897B514D-E758-4690-84B8-6549559C52EE","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:50:29","9":"1","10":"366.0","11":"0.0","12":"609.88","13":"609.88","14":"121.976","index":813},{"0":"494D137C-73F9-4388-B775-A97A4F2EF725","1":"A985156345961425","2":"109.99","3":"23.0","4":"2023-01-30 06:54:18","9":"1","10":"366.0","11":"0.0","12":"219.98","13":"219.98","14":"109.99","index":814},{"0":"494D137C-73F9-4388-B775-A97A4F2EF725","1":"A985156345961425","2":"109.99","3":"23.0","4":"2023-01-30 06:54:18","5":"109.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"219.98","13":"219.98","14":"109.99","index":815},{"0":"8D4F3115-A1D5-459A-A847-A4C3E43A74A7","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:39:35","9":"1","10":"366.0","11":"0.0","12":"659.89","13":"659.89","14":"59.99","index":816},{"0":"DC52C196-52BA-45B3-9C3A-50EEAAF9A12E","1":"A985156921982186","2":"50.49","3":"15.0","4":"2023-02-15 20:36:44","9":"1","10":"425.0","11":"0.0","12":"50.49","13":"50.49","14":"50.49","index":817},{"0":"DC52C196-52BA-45B3-9C3A-50EEAAF9A12E","1":"A985156921982186","2":"50.49","3":"15.0","4":"2023-02-15 20:36:44","5":"50.49","6":"false","7":"1.0","8":"0.0","9":"1","10":"425.0","11":"0.0","12":"50.49","13":"50.49","14":"50.49","index":818},{"0":"6A4D6552-8D68-49DA-8B16-D5094B3ED747","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:35:26","9":"1","10":"2365.0","11":"3.0","12":"107.8","13":"107.8","14":"5.39","index":819},{"0":"B352C087-14C9-4B8A-B239-0C4D861AF091","1":"A1829582580775700","2":"65.57","3":"12.0","4":"2023-01-03 19:45:31","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"590.1299999999999","13":"590.1299999999999","14":"65.57","index":820},{"0":"CA193A45-31F8-4EB5-A19D-BF5D2602277D","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:40:11","9":"1","10":"366.0","11":"0.0","12":"7358.5300000000025","13":"7358.5300000000025","14":"147.17060000000004","index":821},{"0":"6C9E1AA1-9D12-49F6-8CC2-F4E5B1053CCA","1":"A985157021080233","2":"479.0","3":"18.0","4":"2023-03-06 18:59:46","9":"1","10":"367.0","11":"0.0","12":"479.0","13":"479.0","14":"479.0","index":822},{"0":"6C9E1AA1-9D12-49F6-8CC2-F4E5B1053CCA","1":"A985157021080233","2":"479.0","3":"18.0","4":"2023-03-06 18:59:46","5":"754.3771","6":"false","7":"0.0","8":"1.0","9":"1","10":"367.0","11":"0.0","12":"479.0","13":"479.0","14":"479.0","index":823},{"0":"C4FDAA41-391E-4449-93C8-0C7E984921E8","1":"A1759221221524030","2":"119.99","3":"11.0","4":"2023-01-14 11:13:14","9":"1","10":"1098.0","11":"0.0","12":"119.99","13":"119.99","14":"119.99","index":824},{"0":"C4FDAA41-391E-4449-93C8-0C7E984921E8","1":"A1759221221524030","2":"119.99","3":"11.0","4":"2023-01-14 11:13:14","5":"161.722522","6":"false","7":"1.0","8":"0.0","9":"1","10":"1098.0","11":"0.0","12":"119.99","13":"119.99","14":"119.99","index":825},{"0":"0EE3572D-7311-4F07-B930-0A89C79983EF","1":"A985156712327404","2":"219.99","3":"17.0","4":"2023-02-04 18:18:08","9":"1","10":"544.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":826},{"0":"0EE3572D-7311-4F07-B930-0A89C79983EF","1":"A985156712327404","2":"219.99","3":"17.0","4":"2023-02-04 18:18:08","5":"346.462251","6":"false","7":"2.0","8":"0.0","9":"1","10":"544.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":827},{"0":"8B634588-BDD1-4B61-B2C0-00A24CD77D7B","1":"A914801025693987","2":"278.88","3":"19.0","4":"2023-02-18 02:07:01","9":"1","10":"366.0","11":"0.0","12":"557.76","13":"557.76","14":"278.88","index":828},{"0":"A2709A9F-1697-4CB3-B0A2-8DDF7237D6FF","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:06:48","9":"1","10":"2365.0","11":"3.0","12":"522.829999999999","13":"512.049999999999","14":"5.38999999999999","index":829},{"0":"A2709A9F-1697-4CB3-B0A2-8DDF7237D6FF","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:06:48","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"522.829999999999","13":"512.049999999999","14":"5.38999999999999","index":830},{"0":"7E42BCAB-5590-41AD-A18E-8964C2D9CDAA","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:56:38","9":"1","10":"2365.0","11":"3.0","12":"452.7599999999992","13":"441.9799999999992","14":"5.389999999999991","index":831},{"0":"A161FE84-6920-43CD-9AFE-7D1A04800640","1":"A1688853657886190","2":"53.99","3":"12.0","4":"2023-01-03 18:20:14","9":"1","10":"366.0","11":"0.0","12":"755.86","13":"755.86","14":"53.99","index":832},{"0":"9B6410A2-B896-4E0E-9929-B3D3FF23D091","1":"A1055521396853140","2":"150.49","3":"18.0","4":"2023-02-09 00:43:38","5":"150.49","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"300.98","13":"300.98","14":"150.49","index":833},{"0":"5CAB8F98-F87D-4FB5-8647-F83EF559A9E7","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:51:22","9":"1","10":"2365.0","11":"3.0","12":"409.6399999999993","13":"398.85999999999933","14":"5.389999999999991","index":834},{"0":"086FDEAF-BF57-4B51-93F4-169E3609D287","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:03:44","9":"1","10":"2365.0","11":"3.0","12":"495.8799999999991","13":"485.0999999999991","14":"5.38999999999999","index":835},{"0":"3F889B35-49FC-47BC-9E5E-2DF06FA12290","1":"A844427390246047","2":"5.39","3":"9.0","4":"2023-02-28 14:34:01","9":"1","10":"2365.0","11":"3.0","12":"587.5099999999989","13":"247.93999999999969","14":"5.38999999999999","index":836},{"0":"0FBA8C42-194E-4E18-85DB-D1FA1EB7C05D","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:41:53","9":"1","10":"367.0","11":"0.0","12":"109.77999999999997","13":"109.77999999999997","14":"4.989999999999998","index":837},{"0":"D0A2E6EF-037B-4A22-816B-35DCC965C228","1":"A985156995541712","2":"3298.0","3":"20.0","4":"2023-01-23 02:23:53","9":"1","10":"366.0","11":"0.0","12":"3298.0","13":"3298.0","14":"3298.0","index":838},{"0":"D0A2E6EF-037B-4A22-816B-35DCC965C228","1":"A985156995541712","2":"3298.0","3":"20.0","4":"2023-01-23 02:23:53","5":"259.62185800000003","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"3298.0","13":"3298.0","14":"3298.0","index":839},{"0":"158D5606-DB73-4DAE-8041-331070779CD3","1":"A1055521436064760","2":"155.68","3":"17.0","4":"2023-03-30 23:16:28","9":"1","10":"366.0","11":"0.0","12":"155.68","13":"155.68","14":"155.68","index":840},{"0":"158D5606-DB73-4DAE-8041-331070779CD3","1":"A1055521436064760","2":"155.68","3":"17.0","4":"2023-03-30 23:16:28","5":"155.03704199999999","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"155.68","13":"155.68","14":"155.68","index":841},{"0":"EAD34905-DE0B-4FC8-A4AE-C2248AF29A08","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:37:32","9":"1","10":"366.0","11":"0.0","12":"539.91","13":"539.91","14":"59.989999999999995","index":842},{"0":"81BCECDA-79F8-470D-9407-44198BD3EC06","1":"A1055520964660610","2":"219.99","3":"19.0","4":"2023-01-28 00:31:21","9":"1","10":"1220.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":843},{"0":"81BCECDA-79F8-470D-9407-44198BD3EC06","1":"A1055520964660610","2":"219.99","3":"19.0","4":"2023-01-28 00:31:21","5":"219.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"1220.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":844},{"0":"9E498776-6998-4308-B28B-93AF92DE4924","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:08:07","9":"1","10":"2365.0","11":"3.0","12":"528.219999999999","13":"517.439999999999","14":"5.38999999999999","index":845},{"0":"C869ECF0-2A2F-4898-BC65-AD6422144BA5","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:17:22","9":"1","10":"2365.0","11":"3.0","12":"231.76999999999973","13":"220.98999999999975","14":"5.3899999999999935","index":846},{"0":"9E498776-6998-4308-B28B-93AF92DE4924","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:08:07","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"528.219999999999","13":"517.439999999999","14":"5.38999999999999","index":847},{"0":"237AD504-9222-4DAF-BBB9-5C86C4A0DF90","1":"A844428089229497","2":"261.45","3":"23.0","4":"2023-03-29 07:57:24","9":"1","10":"366.0","11":"0.0","12":"261.45","13":"261.45","14":"261.45","index":848},{"0":"12BE1B15-1042-4C4A-9B0D-30823F42E3D6","1":"A985157041378120","2":"159.0","3":"11.0","4":"2023-03-29 16:34:53","9":"1","10":"366.0","11":"0.0","12":"159.0","13":"159.0","14":"159.0","index":849},{"0":"12BE1B15-1042-4C4A-9B0D-30823F42E3D6","1":"A985157041378120","2":"159.0","3":"11.0","4":"2023-03-29 16:34:53","5":"159.0","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"159.0","13":"159.0","14":"159.0","index":850},{"0":"7F6AEAAA-ED61-4163-8C5E-289FB74045B4","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:50:23","9":"1","10":"2365.0","11":"3.0","12":"398.85999999999933","13":"388.07999999999936","14":"5.389999999999991","index":851},{"0":"A76875A6-59E2-4D02-8FF1-48EADE6B83A7","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:00:13","9":"1","10":"2365.0","11":"3.0","12":"479.7099999999991","13":"468.92999999999915","14":"5.38999999999999","index":852},{"0":"F7690559-B2FA-42E4-910F-DF65A0250586","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:29:47","9":"1","10":"366.0","11":"0.0","12":"179.97","13":"179.97","14":"59.99","index":853},{"0":"D7BC1361-E730-4463-9DE7-2B9D92DB5A5B","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:34:13","9":"1","10":"367.0","11":"0.0","12":"39.92000000000001","13":"39.92000000000001","14":"4.990000000000001","index":854},{"0":"EE72DD0F-F132-4E86-9CC5-9FB6386668A0","1":"A1829582580775700","2":"65.57","3":"11.0","4":"2023-01-03 18:41:24","5":"65.57","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"65.57","13":"65.57","14":"65.57","index":855},{"0":"17B73DB4-D6C9-4F0D-A8CA-A1BD9334FABC","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:42:15","9":"1","10":"366.0","11":"0.0","12":"839.86","13":"839.86","14":"59.99","index":856},{"0":"D797769A-8A65-47DF-8473-35A29AC81B24","1":"A844427390246047","2":"5.39","3":"5.0","4":"2023-02-25 11:15:33","9":"1","10":"2365.0","11":"3.0","12":"215.59999999999977","13":"204.8199999999998","14":"5.389999999999994","index":857},{"0":"DE46E518-A85B-40FA-8D00-0527941DF682","1":"A1829582602002910","2":"589.0","3":"17.0","4":"2023-02-28 17:17:05","9":"1","10":"366.0","11":"0.0","12":"589.0","13":"589.0","14":"589.0","index":858},{"0":"DE46E518-A85B-40FA-8D00-0527941DF682","1":"A1829582602002910","2":"589.0","3":"17.0","4":"2023-02-28 17:17:05","5":"793.8542","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"589.0","13":"589.0","14":"589.0","index":859},{"0":"05EB1AA9-8B8C-416C-9637-01DBCE588FD4","1":"A914801058711211","2":"145.95","3":"17.0","4":"2023-03-30 23:32:11","9":"1","10":"366.0","11":"0.0","12":"291.9","13":"291.9","14":"145.95","index":860},{"0":"05EB1AA9-8B8C-416C-9637-01DBCE588FD4","1":"A914801058711211","2":"145.95","3":"17.0","4":"2023-03-30 23:32:11","5":"145.347227","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"291.9","13":"291.9","14":"145.95","index":861},{"0":"72399945-3AC5-461E-BF85-228552DF71E1","1":"A985156971082067","2":"119.98","3":"13.0","4":"2023-01-03 18:35:42","9":"1","10":"366.0","11":"0.0","12":"239.96","13":"239.96","14":"119.98","index":862},{"0":"C1040C0D-A701-4AD0-B4E3-1BC1F06AB4AA","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:13:25","9":"1","10":"366.0","11":"0.0","12":"5708.86","13":"5708.86","14":"146.38102564102564","index":863},{"0":"7DDF174A-C6BF-4245-8148-650BBEC82DA8","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:36:35","9":"1","10":"366.0","11":"0.0","12":"539.91","13":"539.91","14":"59.989999999999995","index":864},{"0":"1731D105-5F5C-4CEE-955B-AD9B4372603D","1":"A844427390246047","2":"5.39","3":"9.0","4":"2023-02-28 14:32:27","9":"1","10":"2365.0","11":"3.0","12":"576.7299999999989","13":"237.1599999999997","14":"5.38999999999999","index":865},{"0":"C187B038-E19A-46AA-A9E0-5BF87BAEE2C1","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:38:51","9":"1","10":"367.0","11":"0.0","12":"64.87000000000002","13":"64.87000000000002","14":"4.990000000000001","index":866},{"0":"AA20954D-6483-48D8-9E0D-D3BDFF8B197A","1":"A1759222088017710","2":"78750.0","3":"16.0","4":"2023-03-15 07:48:58","9":"1","10":"366.0","11":"0.0","12":"157500.0","13":"157500.0","14":"78750.0","index":867},{"0":"AA20954D-6483-48D8-9E0D-D3BDFF8B197A","1":"A1759222088017710","2":"78750.0","3":"16.0","4":"2023-03-15 07:48:58","5":"868.6125","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"157500.0","13":"157500.0","14":"78750.0","index":868},{"0":"2F246963-29D5-4570-BF39-54494E929938","1":"A1055521390784220","2":"219.99","4":"2023-03-19 22:45:20","9":"1","10":"366.0","11":"1.0","12":"219.99","13":"219.99","14":"219.99","index":869},{"0":"2F246963-29D5-4570-BF39-54494E929938","1":"A1055521390784220","2":"219.99","4":"2023-03-19 22:45:20","5":"219.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"1.0","12":"219.99","13":"219.99","14":"219.99","index":870},{"0":"14EF7AD5-A3AA-4E9E-B377-74A47CDC2785","1":"A1688853662170320","2":"839.97","3":"19.0","4":"2023-03-13 10:58:58","9":"1","10":"366.0","11":"0.0","12":"1679.94","13":"1679.94","14":"839.97","index":871},{"0":"EB09177A-B225-47E7-B177-E8610B3E618F","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:55:13","9":"1","10":"366.0","11":"0.0","12":"1059.79","13":"1059.79","14":"132.47375","index":872},{"0":"EB09177A-B225-47E7-B177-E8610B3E618F","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 18:55:13","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"1059.79","13":"1059.79","14":"132.47375","index":873},{"0":"92C5B50A-E46E-404D-9EFB-7043D6DD7C68","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:32:00","9":"1","10":"366.0","11":"0.0","12":"239.96","13":"239.96","14":"59.99","index":874},{"0":"FFF0CCE8-07E1-4100-9491-2853222482EF","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:44:10","9":"1","10":"2365.0","11":"3.0","12":"123.97","13":"118.58","14":"5.39","index":875},{"0":"6DA73B9F-D74B-45A7-82F7-FD10B2840483","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:05:18","9":"1","10":"2365.0","11":"3.0","12":"506.65999999999906","13":"495.8799999999991","14":"5.38999999999999","index":876},{"0":"0DF72AD1-2DFD-404E-88A8-965334D9F227","1":"A844427390246047","2":"5.39","3":"12.0","4":"2023-02-28 17:38:07","9":"1","10":"2365.0","11":"3.0","12":"630.6299999999987","13":"291.0599999999996","14":"5.389999999999989","index":877},{"0":"9E41C0F4-5345-4439-B996-52841214F9FE","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:47:15","9":"1","10":"2365.0","11":"3.0","12":"328.7899999999995","13":"318.00999999999954","14":"5.389999999999992","index":878},{"0":"EC2B7C43-96E8-4BD7-AE1B-1E4F5B513D29","1":"A306107899458824","2":"279.99","3":"8.0","4":"2023-01-09 12:15:52","9":"1","10":"366.0","11":"0.0","12":"279.99","13":"279.99","14":"279.99","index":879},{"0":"EC2B7C43-96E8-4BD7-AE1B-1E4F5B513D29","1":"A306107899458824","2":"279.99","3":"8.0","4":"2023-01-09 12:15:52","5":"279.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"279.99","13":"279.99","14":"279.99","index":880},{"0":"A0F3B9E3-5B99-4E62-99BC-3159F5F3C5D0","1":"A914800550439914","2":"157.07","3":"11.0","4":"2023-02-05 16:23:11","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":881},{"0":"A0F3B9E3-5B99-4E62-99BC-3159F5F3C5D0","1":"A914800550439914","2":"157.07","3":"11.0","4":"2023-02-05 16:23:11","5":"156.421301","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"157.07","13":"157.07","14":"157.07","index":882},{"0":"1580B155-E0E0-40CB-841D-E2A5D2DB0E7E","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:57:02","9":"1","10":"2365.0","11":"3.0","12":"129.35999999999999","13":"118.58","14":"5.39","index":883},{"0":"00746053-49FB-4681-8AAA-10388C1D59E2","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 20:15:52","9":"1","10":"367.0","11":"0.0","12":"124.74999999999996","13":"124.74999999999996","14":"4.989999999999998","index":884},{"0":"1AE19EA1-54DF-4E2A-889F-E47C3AFE65AF","1":"A1899947009495910","2":"538.0","3":"21.0","4":"2023-03-14 13:11:13","9":"1","10":"366.0","11":"0.0","12":"1076.0","13":"1076.0","14":"538.0","index":885},{"0":"E99493A6-78E3-434D-9AAB-D2B1E0AF1AD9","1":"A1759222088015940","2":"78750.0","3":"17.0","4":"2023-03-15 08:56:31","9":"1","10":"366.0","11":"0.0","12":"78750.0","13":"78750.0","14":"78750.0","index":886},{"0":"E99493A6-78E3-434D-9AAB-D2B1E0AF1AD9","1":"A1759222088015940","2":"78750.0","3":"17.0","4":"2023-03-15 08:56:31","5":"868.6125","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"78750.0","13":"78750.0","14":"78750.0","index":887},{"0":"1F9B9F18-4C20-46FD-8151-B8C6B463ACC0","1":"A1055521385546810","2":"148.74","3":"15.0","4":"2023-01-11 22:15:24","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":888},{"0":"1F9B9F18-4C20-46FD-8151-B8C6B463ACC0","1":"A1055521385546810","2":"148.74","3":"15.0","4":"2023-01-11 22:15:24","5":"148.74","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":889},{"0":"198674DC-ADFB-4FFE-94AB-E920AEA22B88","1":"A1829582414397910","2":"222.19","3":"14.0","4":"2023-01-15 19:38:34","9":"1","10":"366.0","11":"0.0","12":"222.19","13":"222.19","14":"222.19","index":890},{"0":"198674DC-ADFB-4FFE-94AB-E920AEA22B88","1":"A1829582414397910","2":"222.19","3":"14.0","4":"2023-01-15 19:38:34","5":"222.19","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"222.19","13":"222.19","14":"222.19","index":891},{"0":"8EC10EBC-F4BB-4148-9073-4B2BA93C9B34","1":"A985156981066925","2":"150.31","3":"8.0","4":"2023-01-03 14:11:15","9":"1","10":"366.0","11":"0.0","12":"150.31","13":"150.31","14":"150.31","index":892},{"0":"8EC10EBC-F4BB-4148-9073-4B2BA93C9B34","1":"A985156981066925","2":"150.31","3":"8.0","4":"2023-01-03 14:11:15","5":"150.31","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"150.31","13":"150.31","14":"150.31","index":893},{"0":"8CF41C6B-5E7A-44FD-B4F4-975432C25086","1":"A1829582519835540","2":"63.59","3":"23.0","4":"2023-02-17 08:15:04","9":"1","10":"366.0","11":"0.0","12":"127.18","13":"127.18","14":"63.59","index":894},{"0":"8CF41C6B-5E7A-44FD-B4F4-975432C25086","1":"A1829582519835540","2":"63.59","3":"23.0","4":"2023-02-17 08:15:04","5":"63.59","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"127.18","13":"127.18","14":"63.59","index":895},{"0":"702CD93B-73B6-4F45-8FE8-7F83EC633AA0","1":"A985157015268058","2":"4.99","3":"14.0","4":"2023-02-27 19:30:12","9":"1","10":"367.0","11":"0.0","12":"99.79999999999998","13":"99.79999999999998","14":"4.989999999999999","index":896},{"0":"6DCDA408-D527-4CC0-8ECD-BB1DD4C1B51B","1":"A1055520836257310","2":"5.04","3":"5.0","4":"2023-02-28 10:23:20","9":"1","10":"1555.0","11":"0.0","12":"10.08","13":"10.08","14":"5.04","index":897},{"0":"84F42387-D564-4878-8D22-760BCE34AD08","1":"A844428070993534","2":"1307.65","3":"10.0","4":"2023-03-07 17:10:15","9":"1","10":"366.0","11":"0.0","12":"1307.65","13":"1307.65","14":"1307.65","index":898},{"0":"84F42387-D564-4878-8D22-760BCE34AD08","1":"A844428070993534","2":"1307.65","3":"10.0","4":"2023-03-07 17:10:15","5":"1307.65","6":"false","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"1307.65","13":"1307.65","14":"1307.65","index":899},{"0":"A645B1E3-E62B-40EB-8201-F5ED545697E9","1":"A985157023797738","2":"281.37","3":"22.0","4":"2023-03-09 06:18:54","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":900},{"0":"A645B1E3-E62B-40EB-8201-F5ED545697E9","1":"A985157023797738","2":"281.37","3":"22.0","4":"2023-03-09 06:18:54","5":"280.207942","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":901},{"0":"81D69AD5-E46F-46A4-8317-14C6F2908E0D","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:32:17","9":"1","10":"366.0","11":"0.0","12":"1133.79","13":"1133.79","14":"53.989999999999995","index":902},{"0":"5AFF55FE-4D1D-4FFA-892D-AB911971CFFE","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:52:47","9":"1","10":"366.0","11":"0.0","12":"8108.380000000004","13":"8108.380000000004","14":"147.42509090909098","index":903},{"0":"5AFF55FE-4D1D-4FFA-892D-AB911971CFFE","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:52:47","5":"149.97","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"8108.380000000004","13":"8108.380000000004","14":"147.42509090909098","index":904},{"0":"BF43B590-100C-49DE-8DE2-F4F0462CC9CC","1":"A844428061406186","2":"1091.15","3":"8.0","4":"2023-02-23 14:56:14","9":"1","10":"366.0","11":"0.0","12":"1091.15","13":"1091.15","14":"1091.15","index":905},{"0":"BF43B590-100C-49DE-8DE2-F4F0462CC9CC","1":"A844428061406186","2":"1091.15","3":"8.0","4":"2023-02-23 14:56:14","5":"1091.15","6":"false","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"1091.15","13":"1091.15","14":"1091.15","index":906},{"0":"2B17E512-6E72-4890-87BD-3C122AA8604E","1":"A844427390246047","2":"5.39","3":"12.0","4":"2023-02-28 17:32:02","9":"1","10":"2365.0","11":"3.0","12":"619.8499999999988","13":"280.27999999999963","14":"5.389999999999989","index":907},{"0":"2B17E512-6E72-4890-87BD-3C122AA8604E","1":"A844427390246047","2":"5.39","3":"12.0","4":"2023-02-28 17:32:02","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"619.8499999999988","13":"280.27999999999963","14":"5.389999999999989","index":908},{"0":"7C52F75A-B38D-4E3A-8C91-83F9FC04A0FB","1":"A1829582583894660","2":"59.99","3":"15.0","4":"2023-02-15 20:21:36","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":909},{"0":"7C52F75A-B38D-4E3A-8C91-83F9FC04A0FB","1":"A1829582583894660","2":"59.99","3":"15.0","4":"2023-02-15 20:21:36","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"59.99","13":"59.99","14":"59.99","index":910},{"0":"D81F2077-E5A4-4DCC-85CB-5D37243F3386","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 02:53:36","9":"1","10":"2365.0","11":"3.0","12":"425.80999999999926","13":"415.0299999999993","14":"5.389999999999991","index":911},{"0":"1ACA02AF-1E19-429B-9C7E-B62584B5F770","1":"A914801018475755","2":"149.93","3":"18.0","4":"2023-02-07 01:48:31","9":"1","10":"366.0","11":"0.0","12":"149.93","13":"149.93","14":"149.93","index":912},{"0":"1ACA02AF-1E19-429B-9C7E-B62584B5F770","1":"A914801018475755","2":"149.93","3":"18.0","4":"2023-02-07 01:48:31","5":"149.93","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"149.93","13":"149.93","14":"149.93","index":913},{"0":"1E0E93F8-6B15-40BA-ACE4-77CF9B527E0C","1":"A1899946944829860","2":"59.99","3":"7.0","4":"2023-02-16 13:16:34","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":914},{"0":"1E0E93F8-6B15-40BA-ACE4-77CF9B527E0C","1":"A1899946944829860","2":"59.99","3":"7.0","4":"2023-02-16 13:16:34","5":"59.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"119.98","13":"119.98","14":"59.99","index":915},{"0":"925E5659-FF7E-42E9-B91C-9C629D7F2BBA","1":"A1055521388773990","2":"108.24","3":"20.0","4":"2023-01-15 03:03:53","9":"1","10":"366.0","11":"0.0","12":"227.3","13":"227.3","14":"113.65","index":916},{"0":"925E5659-FF7E-42E9-B91C-9C629D7F2BBA","1":"A1055521388773990","2":"108.24","3":"20.0","4":"2023-01-15 03:03:53","5":"108.24","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"227.3","13":"227.3","14":"113.65","index":917},{"0":"DABB8679-0A5B-46D1-9421-D443A45907F8","1":"A1899946895455190","2":"19.99","3":"13.0","4":"2023-01-13 18:48:21","9":"1","10":"366.0","11":"0.0","12":"99.94999999999999","13":"99.94999999999999","14":"19.99","index":918},{"0":"A6A32094-B81A-4B70-B184-7B74C5B694CB","1":"A1829582189343980","2":"129.34","3":"2.0","4":"2023-02-17 07:51:54","9":"1","10":"985.0","11":"0.0","12":"258.68","13":"258.68","14":"129.34","index":919},{"0":"A6A32094-B81A-4B70-B184-7B74C5B694CB","1":"A1829582189343980","2":"129.34","3":"2.0","4":"2023-02-17 07:51:54","5":"129.34","6":"false","7":"2.0","8":"0.0","9":"1","10":"985.0","11":"0.0","12":"258.68","13":"258.68","14":"129.34","index":920},{"0":"F0A25208-F9BB-401D-A840-36823FB1B0A8","1":"A844427390246047","2":"5.39","3":"12.0","4":"2023-02-28 17:34:16","9":"1","10":"2365.0","11":"3.0","12":"625.2399999999988","13":"285.6699999999996","14":"5.389999999999989","index":921},{"0":"051BDF69-D526-4951-A7E0-9A933A9899FF","1":"A844427390246047","2":"5.39","3":"11.0","4":"2023-02-28 17:16:57","9":"1","10":"2365.0","11":"3.0","12":"614.4599999999988","13":"274.88999999999965","14":"5.389999999999989","index":922},{"0":"A53C943B-A88C-4EB6-9A34-C386E9E8879C","1":"A844427390246047","2":"5.39","3":"11.0","4":"2023-02-28 17:14:53","9":"1","10":"2365.0","11":"3.0","12":"609.0699999999988","13":"269.49999999999966","14":"5.389999999999989","index":923},{"0":"FF63B43E-F5B7-4CFD-81FD-AFBFE1BDDFA0","1":"A985157015268058","2":"4.99","3":"15.0","4":"2023-02-27 20:51:59","9":"1","10":"367.0","11":"0.0","12":"144.70999999999998","13":"144.70999999999998","14":"4.989999999999999","index":924},{"0":"4A729E60-7E0A-4080-88C8-A81656F18A24","1":"A1829582358242680","2":"239.51","3":"9.0","4":"2023-02-23 14:49:26","9":"1","10":"366.0","11":"0.0","12":"239.51","13":"239.51","14":"239.51","index":925},{"0":"C4F83897-54F9-4208-987E-DDE9D20F0B4B","1":"A1688853657869890","2":"52.99","3":"11.0","4":"2023-01-03 17:27:59","5":"52.99","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"476.91","13":"476.91","14":"52.99","index":926},{"0":"BD6E876C-4420-4359-B25A-35BE542F82C0","1":"A362840681200346","2":"159.85","3":"16.0","4":"2023-02-15 20:30:03","5":"159.189819","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"159.85","13":"159.85","14":"159.85","index":927},{"0":"66F28A11-228E-4CF9-9872-0677EF57CCCD","1":"A985156921982186","2":"50.49","3":"15.0","4":"2023-02-15 20:50:09","9":"1","10":"425.0","11":"0.0","12":"100.98","13":"100.98","14":"50.49","index":928},{"0":"F815E0BF-EDEA-40BF-9CA7-E529C895BA5E","1":"A985156554634689","2":"116.86","3":"14.0","4":"2023-02-17 19:24:44","9":"1","10":"366.0","11":"0.0","12":"233.72","13":"233.72","14":"116.86","index":929},{"0":"0550F138-938B-4710-83ED-00EC64D186E3","1":"A844427390246047","2":"5.39","3":"21.0","4":"2023-02-26 03:04:17","9":"1","10":"2365.0","11":"3.0","12":"495.8799999999991","13":"485.0999999999991","14":"5.38999999999999","index":930},{"0":"7CD47312-9A89-4118-A977-62919D932541","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:37:33","9":"1","10":"367.0","11":"0.0","12":"54.890000000000015","13":"54.890000000000015","14":"4.990000000000001","index":931},{"0":"45775ADF-C0C1-4AB2-97A6-BEE6F012632F","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:36:29","9":"1","10":"367.0","11":"0.0","12":"49.90000000000001","13":"49.90000000000001","14":"4.990000000000001","index":932},{"0":"7CD47312-9A89-4118-A977-62919D932541","1":"A985157015268058","2":"4.99","3":"10.0","4":"2023-02-27 15:37:33","5":"4.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"367.0","11":"0.0","12":"54.890000000000015","13":"54.890000000000015","14":"4.990000000000001","index":933},{"0":"A45AA627-25B8-40C5-BA84-8581AEA31D28","1":"A1829582571162030","2":"219.99","3":"22.0","4":"2023-02-28 06:34:01","9":"1","10":"366.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":934},{"0":"A45AA627-25B8-40C5-BA84-8581AEA31D28","1":"A1829582571162030","2":"219.99","3":"22.0","4":"2023-02-28 06:34:01","5":"219.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":935},{"0":"65EA39D7-E9AC-4E55-8F4E-0A3C932688B0","1":"A985157032078551","2":"699.0","3":"17.0","4":"2023-03-19 01:27:21","9":"1","10":"366.0","11":"0.0","12":"699.0","13":"699.0","14":"699.0","index":936},{"0":"65EA39D7-E9AC-4E55-8F4E-0A3C932688B0","1":"A985157032078551","2":"699.0","3":"17.0","4":"2023-03-19 01:27:21","5":"112.30134","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"699.0","13":"699.0","14":"699.0","index":937},{"0":"06CA7673-9504-44D4-A9B1-49EC29233AD5","1":"A844428040495230","2":"73.11","3":"13.0","4":"2023-01-19 18:43:29","9":"1","10":"366.0","11":"1.0","12":"148.69","13":"148.69","14":"74.345","index":938},{"0":"077961F4-BD88-4A14-9607-D267E3882119","1":"A1688853596363060","2":"60.59","3":"6.0","4":"2023-02-16 11:57:34","9":"1","10":"366.0","11":"0.0","12":"60.59","13":"60.59","14":"60.59","index":939},{"0":"8D778823-BB51-4CA8-9CEC-84F4D48E035A","1":"A1899947010059410","2":"73080.0","3":"23.0","4":"2023-03-07 14:24:12","9":"1","10":"366.0","11":"0.0","12":"146160.0","13":"146160.0","14":"73080.0","index":940},{"0":"5977650F-EE3E-450F-AD0B-7C103B35B4F5","1":"A844428017843376","2":"119.98","3":"15.0","4":"2023-01-03 20:36:42","9":"1","10":"366.0","11":"0.0","12":"455.0","13":"455.0","14":"113.75","index":941},{"0":"5977650F-EE3E-450F-AD0B-7C103B35B4F5","1":"A844428017843376","2":"119.98","3":"15.0","4":"2023-01-03 20:36:42","5":"119.98","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"455.0","13":"455.0","14":"113.75","index":942},{"0":"BB496ECE-16ED-448A-BE0D-272C234653B5","1":"A1759222234549860","2":"62580.0","3":"8.0","4":"2023-03-02 23:38:37","9":"1","10":"366.0","11":"0.0","12":"62580.0","13":"62580.0","14":"62580.0","index":943},{"0":"BB496ECE-16ED-448A-BE0D-272C234653B5","1":"A1759222234549860","2":"62580.0","3":"8.0","4":"2023-03-02 23:38:37","5":"690.2574","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"62580.0","13":"62580.0","14":"62580.0","index":944},{"0":"A9A9B9C3-90F1-4BD3-9217-0EA2828D65B2","1":"A914800654351337","2":"148.74","3":"9.0","4":"2023-02-20 15:56:57","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":945},{"0":"A9A9B9C3-90F1-4BD3-9217-0EA2828D65B2","1":"A914800654351337","2":"148.74","3":"9.0","4":"2023-02-20 15:56:57","5":"148.74","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"148.74","13":"148.74","14":"148.74","index":946},{"0":"2EB5E726-5854-45B4-A674-FBCFD891AE9A","1":"A362221882887902","2":"680.89","4":"2023-03-06 18:12:12","9":"1","10":"1971.0","11":"0.0","12":"680.89","13":"680.89","14":"340.445","index":947},{"0":"2EB5E726-5854-45B4-A674-FBCFD891AE9A","1":"A362221882887902","2":"680.89","4":"2023-03-06 18:12:12","5":"680.89","6":"false","7":"1.0","8":"1.0","9":"1","10":"1971.0","11":"0.0","12":"680.89","13":"680.89","14":"340.445","index":948},{"0":"3E6BEDBD-7B2A-40CF-87FC-AE161EA47713","1":"A1899946865209040","2":"79380.0","3":"9.0","4":"2023-03-11 00:57:49","9":"1","10":"366.0","11":"0.0","12":"79380.0","13":"79380.0","14":"79380.0","index":949},{"0":"B3FFE8F1-6D4D-49FF-9DC6-97E3F78B18E1","1":"A1829582189343980","2":"129.34","3":"2.0","4":"2023-02-17 07:49:50","9":"1","10":"985.0","11":"0.0","12":"129.34","13":"129.34","14":"129.34","index":950},{"0":"0E1A2979-7A26-4C95-AFDE-E391961B3467","1":"A1899946781631740","2":"233.74","3":"6.0","4":"2023-02-22 12:26:38","9":"1","10":"366.0","11":"0.0","12":"233.74","13":"233.74","14":"233.74","index":951},{"0":"0E1A2979-7A26-4C95-AFDE-E391961B3467","1":"A1899946781631740","2":"233.74","3":"6.0","4":"2023-02-22 12:26:38","5":"233.74","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"233.74","13":"233.74","14":"233.74","index":952},{"0":"A25BC911-EAA9-47D1-B8D2-4086F6CC94CB","1":"A1829582580801010","2":"59.99","3":"15.0","4":"2023-01-03 20:35:29","9":"1","10":"366.0","11":"0.0","12":"419.93","13":"419.93","14":"59.99","index":953},{"0":"59ACE4B3-BD23-4241-B303-4D37EA2DFEE1","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:19:19","9":"1","10":"2365.0","11":"3.0","12":"247.93999999999969","13":"237.1599999999997","14":"5.3899999999999935","index":954},{"0":"D1C8D053-633E-457E-AAE3-2CC1801EC80B","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:22:13","9":"1","10":"2365.0","11":"3.0","12":"264.1099999999997","13":"253.32999999999967","14":"5.3899999999999935","index":955},{"0":"59ACE4B3-BD23-4241-B303-4D37EA2DFEE1","1":"A844427390246047","2":"5.39","3":"6.0","4":"2023-02-25 12:19:19","5":"5.39","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"3.0","12":"247.93999999999969","13":"237.1599999999997","14":"5.3899999999999935","index":956},{"0":"5F4BFC0E-B20F-4310-8971-6D3B8E7C7EF8","1":"A914801030385624","2":"79.99","3":"15.0","4":"2023-02-24 21:24:27","9":"1","10":"366.0","11":"0.0","12":"79.99","13":"79.99","14":"79.99","index":957},{"0":"5F4BFC0E-B20F-4310-8971-6D3B8E7C7EF8","1":"A914801030385624","2":"79.99","3":"15.0","4":"2023-02-24 21:24:27","5":"79.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"79.99","13":"79.99","14":"79.99","index":958},{"0":"630E3459-B5AA-4D4C-A2E4-31E4D86BDD5B","1":"A985156290472668","2":"5.34","3":"20.0","4":"2023-02-26 02:11:24","9":"1","10":"2365.0","11":"0.0","12":"16.02","13":"16.02","14":"5.34","index":959},{"0":"630E3459-B5AA-4D4C-A2E4-31E4D86BDD5B","1":"A985156290472668","2":"5.34","3":"20.0","4":"2023-02-26 02:11:24","5":"5.34","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"0.0","12":"16.02","13":"16.02","14":"5.34","index":960},{"0":"9E012F96-E7AA-4046-8F24-0C85FBB36CF5","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:07:57","9":"1","10":"366.0","11":"0.0","12":"1959.6100000000001","13":"1959.6100000000001","14":"139.97214285714287","index":961},{"0":"17DA4A53-7EDF-4FE4-A997-3C9AF9255294","1":"A844427390246047","2":"5.39","3":"7.0","4":"2023-02-25 12:42:04","9":"1","10":"2365.0","11":"3.0","12":"312.61999999999955","13":"301.8399999999996","14":"5.389999999999993","index":962},{"0":"473551AD-AFD4-49DE-B182-623E23AE5E46","1":"A1055521108927870","2":"219.99","3":"13.0","4":"2023-02-27 20:23:30","9":"1","10":"366.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":963},{"0":"473551AD-AFD4-49DE-B182-623E23AE5E46","1":"A1055521108927870","2":"219.99","3":"13.0","4":"2023-02-27 20:23:30","5":"219.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"219.99","13":"219.99","14":"219.99","index":964},{"0":"E4BA8082-355D-4EA7-B2C8-1D9E07046F30","1":"A985157025448796","2":"73080.0","3":"21.0","4":"2023-03-11 12:47:25","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":965},{"0":"E4BA8082-355D-4EA7-B2C8-1D9E07046F30","1":"A985157025448796","2":"73080.0","3":"21.0","4":"2023-03-11 12:47:25","5":"806.0724","6":"false","7":"2.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":966},{"0":"2EA5AE02-F407-41D5-98D6-EBC2C4BF9DD2","1":"A1055521007937570","2":"1154.4","3":"10.0","4":"2023-02-14 18:35:02","9":"1","10":"1138.0","11":"0.0","12":"1154.4","13":"1154.4","14":"1154.4","index":967},{"0":"2EA5AE02-F407-41D5-98D6-EBC2C4BF9DD2","1":"A1055521007937570","2":"1154.4","3":"10.0","4":"2023-02-14 18:35:02","5":"1154.4","6":"false","7":"0.0","8":"3.0","9":"1","10":"1138.0","11":"0.0","12":"1154.4","13":"1154.4","14":"1154.4","index":968},{"0":"0A282803-01E1-4193-B72A-F135B850DCB5","1":"A844427390246047","2":"5.39","3":"15.0","4":"2023-02-24 20:32:12","9":"1","10":"2365.0","11":"3.0","12":"86.24","13":"86.24","14":"5.39","index":969},{"0":"25C5DCA0-AACA-4FF2-870E-7A08EC230EF6","1":"A1055521399136000","2":"5.4","3":"23.0","4":"2023-02-27 04:51:23","5":"5.4","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"21.6","13":"21.6","14":"5.4","index":970},{"0":"34E50410-3939-4466-BCE5-6207661FAF5C","1":"A1899946865208980","2":"73080.0","3":"0.0","4":"2023-03-09 16:20:26","9":"1","10":"366.0","11":"0.0","12":"73080.0","13":"73080.0","14":"73080.0","index":971},{"0":"BD1D07BF-A461-47A3-8938-D785B38A0E7D","1":"A914801001917388","2":"698.99","3":"8.0","4":"2023-01-15 07:32:32","9":"1","10":"366.0","11":"0.0","12":"698.99","13":"698.99","14":"698.99","index":972},{"0":"BD1D07BF-A461-47A3-8938-D785B38A0E7D","1":"A914801001917388","2":"698.99","3":"8.0","4":"2023-01-15 07:32:32","5":"942.0987220000001","6":"false","7":"0.0","8":"2.0","9":"1","10":"366.0","11":"0.0","12":"698.99","13":"698.99","14":"698.99","index":973},{"0":"985F137B-2E34-46E7-9AB6-EBF9D2C220F4","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 20:06:48","9":"1","10":"366.0","11":"0.0","12":"5108.979999999999","13":"5108.979999999999","14":"145.9708571428571","index":974},{"0":"2FF854D4-3A50-4F9B-9AF9-861612051389","1":"A1899946873142120","2":"149.97","3":"15.0","4":"2023-01-03 20:33:06","9":"1","10":"366.0","11":"0.0","12":"6908.620000000002","13":"6908.620000000002","14":"146.99191489361706","index":975},{"0":"8F4A2E5B-152F-45DB-B0E1-77EB964422F4","1":"A844428050423122","2":"64.49","3":"7.0","4":"2023-02-07 12:22:26","9":"1","10":"366.0","11":"0.0","12":"64.49","13":"64.49","14":"64.49","index":976},{"0":"8F4A2E5B-152F-45DB-B0E1-77EB964422F4","1":"A844428050423122","2":"64.49","3":"7.0","4":"2023-02-07 12:22:26","5":"64.49","6":"false","7":"0.0","8":"1.0","9":"1","10":"366.0","11":"0.0","12":"64.49","13":"64.49","14":"64.49","index":977},{"0":"4D150B63-9F3D-4A5E-A899-FECAF11AA4FE","1":"A985156107272519","2":"118.79","3":"11.0","4":"2023-02-09 17:02:14","9":"1","10":"2365.0","11":"0.0","12":"118.79","13":"118.79","14":"118.79","index":978},{"0":"4D150B63-9F3D-4A5E-A899-FECAF11AA4FE","1":"A985156107272519","2":"118.79","3":"11.0","4":"2023-02-09 17:02:14","5":"118.79","6":"false","7":"1.0","8":"0.0","9":"1","10":"2365.0","11":"0.0","12":"118.79","13":"118.79","14":"118.79","index":979},{"0":"A44E3875-069E-472A-B3FB-5ADC3A7CDF66","1":"A1055521435204170","2":"286.35","3":"18.0","4":"2023-03-29 23:51:16","9":"1","10":"366.0","11":"0.0","12":"286.35","13":"286.35","14":"286.35","index":980},{"0":"68D83F37-50D4-484B-8652-EAA245BFD48D","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:33:26","9":"1","10":"366.0","11":"0.0","12":"3009.3999999999987","13":"3009.3999999999987","14":"143.30476190476185","index":981},{"0":"ECADF883-D7AA-4373-99FB-358A35302900","1":"A1688853652289450","2":"1499.0","3":"14.0","4":"2023-01-08 21:20:27","9":"1","10":"366.0","11":"0.0","12":"1499.0","13":"1499.0","14":"1499.0","index":982},{"0":"ECADF883-D7AA-4373-99FB-358A35302900","1":"A1688853652289450","2":"1499.0","3":"14.0","4":"2023-01-08 21:20:27","5":"118.002779","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"1499.0","13":"1499.0","14":"1499.0","index":983},{"0":"13A9B365-CFF1-44F7-A1EB-19327EDE18C2","1":"A1688853647393240","2":"295.38","3":"8.0","4":"2023-01-25 16:49:33","9":"1","10":"366.0","11":"0.0","12":"443.07","13":"443.07","14":"221.535","index":984},{"0":"B1992BA4-6A40-477F-99FF-2C9AC26688DF","1":"A1688853662172620","2":"569.97","3":"23.0","4":"2023-03-19 15:17:07","9":"1","10":"366.0","11":"0.0","12":"569.97","13":"569.97","14":"569.97","index":985},{"0":"B1992BA4-6A40-477F-99FF-2C9AC26688DF","1":"A1688853662172620","2":"569.97","3":"23.0","4":"2023-03-19 15:17:07","5":"768.205566","6":"false","7":"3.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"569.97","13":"569.97","14":"569.97","index":986},{"0":"A92EA0F2-90D5-413B-A2FE-8A91F3DA1255","1":"A1759222219189020","2":"222.19","3":"3.0","4":"2023-01-28 08:50:57","9":"1","10":"366.0","11":"0.0","12":"222.19","13":"222.19","14":"222.19","index":987},{"0":"ABE168A6-3636-4202-9389-95E44E65881E","1":"A1688853657869890","2":"52.99","3":"10.0","4":"2023-01-03 17:14:56","5":"52.99","6":"false","7":"1.0","8":"0.0","9":"0","10":"366.0","11":"0.0","12":"423.92","13":"423.92","14":"52.99","index":988},{"0":"9B2DBE0F-7C5E-48F1-B184-2066C7CE0AC4","1":"A1899946871435580","2":"281.37","3":"13.0","4":"2023-01-20 18:56:34","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":989},{"0":"9B2DBE0F-7C5E-48F1-B184-2066C7CE0AC4","1":"A1899946871435580","2":"281.37","3":"13.0","4":"2023-01-20 18:56:34","5":"280.207942","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"281.37","13":"281.37","14":"281.37","index":990},{"0":"C5A162B3-E2A9-451F-8A02-FE6A939B43A4","1":"A985156979906180","2":"99.99","3":"14.0","4":"2023-01-07 22:49:51","9":"1","10":"366.0","11":"0.0","12":"99.99","13":"99.99","14":"99.99","index":991},{"0":"C5A162B3-E2A9-451F-8A02-FE6A939B43A4","1":"A985156979906180","2":"99.99","3":"14.0","4":"2023-01-07 22:49:51","5":"99.99","6":"false","7":"1.0","8":"0.0","9":"1","10":"366.0","11":"0.0","12":"99.99","13":"99.99","14":"99.99","index":992},{"0":"47A9F4BA-03C8-4900-BEF0-BADCF3C21D99","1":"A1688853662172620","2":"569.97","3":"23.0","4":"2023-03-19 15:19:14","9":"1","10":"366.0","11":"0.0","12":"1139.94","13":"1139.94","14":"569.97","index":993},{"0":"51729D46-986A-4AD6-927B-959AA1FF1749","1":"A1688853657886190","2":"53.99","3":"13.0","4":"2023-01-03 18:35:19","9":"1","10":"366.0","11":"0.0","12":"1241.77","13":"1241.77","14":"53.99","index":994},{"0":"F15CE4DA-A189-42BF-931E-66398ED48CA6","1":"A1899946873142120","2":"149.97","3":"14.0","4":"2023-01-03 19:21:51","9":"1","10":"366.0","11":"0.0","12":"2559.4899999999993","13":"2559.4899999999993","14":"142.19388888888886","index":995},{"0":"4C5EC030-CD2E-4097-B706-42BE9D808509","1":"A1899946873142120","2":"149.97","3":"13.0","4":"2023-01-03 19:05:37","9":"1","10":"366.0","11":"0.0","12":"1809.64","13":"1809.64","14":"139.20307692307694","index":996},{"0":"27B1F6C4-E881-408C-A29E-CA13AD07997B","1":"A1899946895455190","2":"19.99","3":"13.0","4":"2023-01-13 18:49:35","9":"1","10":"366.0","11":"0.0","12":"119.93999999999998","13":"119.93999999999998","14":"19.99","index":997},{"0":"DE53E28B-69C9-4C7D-AF79-E725235C87F4","1":"A1899946895455190","2":"19.99","3":"14.0","4":"2023-01-13 19:31:56","9":"1","10":"366.0","11":"0.0","12":"259.87","13":"259.87","14":"19.990000000000002","index":998},{"0":"1935138A-D934-4B52-B3F5-FA99C47F03AC","1":"A1688853596241210","2":"65.31","3":"22.0","4":"2023-02-17 03:40:57","9":"1","10":"366.0","11":"0.0","12":"190.61","13":"190.61","14":"63.53666666666667","index":999},{"0":"CDAFFE95-3687-4886-9668-96EB7645063F","1":"A1829582586398540","2":"786.79","3":"10.0","4":"2023-01-20 09:25:50","9":"1","10":"366.0","11":"0.0","12":"905.79","13":"905.79","14":"452.895","index":1000}],"schema":[{"key":"0","name":"transactionID","type":"string"},{"key":"1","name":"accountID","type":"string"},{"key":"2","name":"transactionAmount","type":"double"},{"key":"3","name":"localHour","type":"double"},{"key":"4","name":"timestamp","type":"timestamp"},{"key":"5","name":"transactionAmountUSD","type":"double"},{"key":"6","name":"isProxyIP","type":"boolean"},{"key":"7","name":"digitalItemCount","type":"double"},{"key":"8","name":"physicalItemCount","type":"double"},{"key":"9","name":"is_fraud","type":"bigint"},{"key":"10","name":"accountAge","type":"double"},{"key":"11","name":"numPaymentRejects1dPerUser","type":"double"},{"key":"12","name":"transaction_amount_7d_sum","type":"double"},{"key":"13","name":"transaction_amount_3d_sum","type":"double"},{"key":"14","name":"transaction_amount_7d_avg","type":"double"}],"truncated":false},"isSummary":false,"language":"scala"},"persist_state":{"view":{"type":"details","tableOptions":{},"chartOptions":{"chartType":"bar","categoryFieldKeys":["0"],"seriesFieldKeys":["10"],"aggregationType":"sum","isStacked":false}}}},"9a6fcf39-abcf-44bd-98f3-19b607186f26":{"type":"Synapse.DataFrame","sync_state":{"table":{"rows":[{"0":"A1055520426185580","1":"2021-12-31 00:00:00","2":"GB","3":"true","4":"0.0","5":"2000.0","index":1},{"0":"A1055520426191820","1":"2021-12-31 00:00:00","2":"US","3":"true","4":"0.0","5":"1.0","index":2},{"0":"A1055520426549020","1":"2021-12-31 00:00:00","2":"AU","3":"true","4":"0.0","5":"1.0","index":3},{"0":"A1055520426719380","1":"2021-12-31 00:00:00","2":"US","3":"true","4":"0.0","5":"2000.0","index":4},{"0":"A1055520426806680","1":"2021-12-31 00:00:00","2":"GB","3":"true","4":"1.0","5":"2000.0","index":5}],"schema":[{"key":"0","name":"accountID","type":"string"},{"key":"1","name":"timestamp","type":"timestamp"},{"key":"2","name":"accountCountry","type":"string"},{"key":"3","name":"isUserRegistered","type":"boolean"},{"key":"4","name":"numPaymentRejects1dPerUser","type":"double"},{"key":"5","name":"accountAge","type":"double"}],"truncated":false},"isSummary":false,"language":"scala"},"persist_state":{"view":{"type":"details","tableOptions":{},"chartOptions":{"chartType":"bar","categoryFieldKeys":["0"],"seriesFieldKeys":["5"],"aggregationType":"sum","isStacked":false}}}}}}},"nbformat":4,"nbformat_minor":2}
\ No newline at end of file
+{
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Tutorial #2: Experiment and train models using features"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing.\n",
+ "\n",
+ "In part 1 of the tutorial you learnt how to create a feature set spec with custom transformations, enable materialization and perform backfill. In this tutorial you will will learn how to experiment with features to improve model performance. You will see how feature store increasing agility in the experimentation and training flows. \n",
+ "\n",
+ "You will perform the following:\n",
+ "- Prototype a create new `acccounts` feature set spec using existing precomputed values as features, unlike part 1 of the tutorial where we created feature set that had custom transformations. You will then Register the local feature set spec as a feature set in the feature store\n",
+ "- Select features for the model: You will select features from the `transactions` and `accounts` feature sets and save them as a feature-retrieval spec\n",
+ "- Run training pipeline that uses the Feature retrieval spec to train a new model. This pipeline will use the built in feature-retrieval component to generate the training data"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Prerequisites\n",
+ "1. Please ensure you have executed part 1 of the tutorial"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Setup"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Configure Azure ML spark notebook\n",
+ "\n",
+ "1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n",
+ "1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Start spark session"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550147448
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "start-spark-session",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
+ "print(\"start spark session\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Setup root directory for the samples"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550160587
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "root-dir",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
+ "# You can find the name from the directory structure in the left nav\n",
+ "root_dir = \"./Users//featurestore_sample\"\n",
+ "\n",
+ "if os.path.isdir(root_dir):\n",
+ " print(\"The folder exists.\")\n",
+ "else:\n",
+ " print(\"The folder does not exist. Please create or fix the path\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Initialize the project workspace CRUD client\n",
+ "This is the current workspace where you will be running the tutorial notebook from"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550177764
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-ws-crud-client",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "### Initialize the MLClient of this project workspace\n",
+ "import os\n",
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
+ "\n",
+ "# connect to the project workspace\n",
+ "ws_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Initialize the feature store CRUD client\n",
+ "Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550201549
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-fs-crud-client",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "# feature store\n",
+ "featurestore_name = (\n",
+ " \"\" # use the same name from part #1 of the tutorial\n",
+ ")\n",
+ "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "\n",
+ "# feature store ml client\n",
+ "fs_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " featurestore_subscription_id,\n",
+ " featurestore_resource_group_name,\n",
+ " featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Initialize the feature store core sdk client"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550214535
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-fs-core-sdk",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# feature store client\n",
+ "from azureml.featurestore import FeatureStoreClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "featurestore = FeatureStoreClient(\n",
+ " credential=AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ " name=featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Create compute cluster with name `cpu-cluster-fs` in the project workspace\n",
+ "This will be needed when we run training/batch inference jobs"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550257007
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "create-compute-cluster",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import AmlCompute\n",
+ "\n",
+ "cluster_basic = AmlCompute(\n",
+ " name=\"cpu-cluster-fs\",\n",
+ " type=\"amlcompute\",\n",
+ " size=\"STANDARD_F4S_V2\", # you can replace it with other supported VM SKUs\n",
+ " location=ws_client.workspaces.get(ws_client.workspace_name).location,\n",
+ " min_instances=0,\n",
+ " max_instances=1,\n",
+ " idle_time_before_scale_down=360,\n",
+ ")\n",
+ "ws_client.begin_create_or_update(cluster_basic).result()"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 1: Create accounts featureset locally from precomputed data\n",
+ "In tutorial part 1, we created a transactions featureset that had custom transformations. Now we will create an accounts featureset that will use precomputed values. \n",
+ "\n",
+ "For onboarding precomputed features, you can create a featureset spec without writing any transformation code. Featureset spec is a specification to develop and test a featureset in a fully local/dev environment without connecting to any featurestore. In this step you will create the feature set spec locally and sample the values from it. If you want to get managed featurestore capabilities, you need to register the featureset spec with a feature store using a feature asset definition (a future step in this tutorial)."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 1a: Explore the source data for accounts\n",
+ "\n",
+ "##### Note\n",
+ " Note that the sample data used in this notebook is hosted in a public accessible blob container. It can only be read in Spark via `wasbs` driver. When you create feature sets using your own source data, please host them in adls gen2 account and use `abfss` driver in the data path. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550281756
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "explore-accts-fset-src-data",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "accounts_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/accounts-precalculated/*.parquet\"\n",
+ "accounts_df = spark.read.parquet(accounts_data_path)\n",
+ "\n",
+ "display(accounts_df.head(5))"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 1b: Create `accounts` feature set spec in local from these precomputed features\n",
+ "Note that we do not need any transformation code here since we are referencing precomputed features."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550310950
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "create-accts-fset-spec",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import create_feature_set_spec, FeatureSetSpec\n",
+ "from azureml.featurestore.contracts import (\n",
+ " DateTimeOffset,\n",
+ " FeatureSource,\n",
+ " TransformationCode,\n",
+ " Column,\n",
+ " ColumnType,\n",
+ " SourceType,\n",
+ " TimestampColumn,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "accounts_featureset_spec = create_feature_set_spec(\n",
+ " source=FeatureSource(\n",
+ " type=SourceType.parquet,\n",
+ " path=\"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/accounts-precalculated/*.parquet\",\n",
+ " timestamp_column=TimestampColumn(name=\"timestamp\"),\n",
+ " ),\n",
+ " index_columns=[Column(name=\"accountID\", type=ColumnType.string)],\n",
+ " # account profiles in the source are updated once a year. set temporal_join_lookback to 365 days\n",
+ " temporal_join_lookback=DateTimeOffset(days=365, hours=0, minutes=0),\n",
+ " infer_schema=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Generate a spark dataframe from the feature set specification\n",
+ "accounts_fset_df = accounts_featureset_spec.to_spark_dataframe()\n",
+ "# display few records\n",
+ "display(accounts_fset_df.head(5))"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 1c: Export as feature set spec\n",
+ "In order to register the feature set spec with the feature store, it needs to be saved in a specific format. \n",
+ "Action: After running the below cell, please inspect the generated `accounts` FeatureSetSpec: Open this file from the file tree to see the spec: `featurestore/featuresets/accounts/spec/FeatureSetSpec.yaml`\n",
+ "\n",
+ "Spec contains these important elements:\n",
+ "\n",
+ "1. `source`: reference to a storage. In this case a parquet file in a blob storage.\n",
+ "1. `features`: list of features and their datatypes. If you provide transformation code (see Day 2 section), the code has to return a dataframe that maps to the features and datatypes. In case where you do not provide transformation code (in this case of `accounts` because it is precomputed), the system will build the query to to map these to the source \n",
+ "1. `index_columns`: the join keys required to access values from the feature set\n",
+ "\n",
+ "Learn more about it in the [top level feature store entities document](fs-concepts-todo) and the [feature set spec yaml reference](reference-yaml-featureset-spec.md).\n",
+ "\n",
+ "The additional benefit of persisting it is that it can be source controlled."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550317763
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "dump-accts-fset-spec",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# create a new folder to dump the feature set spec\n",
+ "accounts_featureset_spec_folder = root_dir + \"/featurestore/featuresets/accounts/spec\"\n",
+ "\n",
+ "# check if the folder exists, create one if not\n",
+ "if not os.path.exists(accounts_featureset_spec_folder):\n",
+ " os.makedirs(accounts_featureset_spec_folder)\n",
+ "\n",
+ "accounts_featureset_spec.dump(accounts_featureset_spec_folder, overwrite=True)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 2: Experiment with unregistered features locally and register with feature store when ready\n",
+ "When you are developing features, you might want to test/validate locally before registering with the feature store or running training pipelines in the cloud. In this step you will generate training data for the ML model from combination of features from a local unregistered feature set (`accounts`) and feature set registered in the feature store (`transactions`)."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2a: Select features for model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550328371
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "select-unreg-features-for-model",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# get the registered transactions feature set, version 1\n",
+ "transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
+ "# Notice that account feature set spec is in your local dev environment (this notebook): not registered with feature store yet\n",
+ "features = [\n",
+ " accounts_featureset_spec.get_feature(\"accountAge\"),\n",
+ " accounts_featureset_spec.get_feature(\"numPaymentRejects1dPerUser\"),\n",
+ " transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n",
+ " transactions_featureset.get_feature(\"transaction_amount_3d_sum\"),\n",
+ " transactions_featureset.get_feature(\"transaction_amount_7d_avg\"),\n",
+ "]"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2b: Generate training data locally\n",
+ "In this step we generate training data for illustrative purpose. You can optionally train models locally with this. In the upcoming steps in this tutorial, you will train a model in the cloud."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550539714
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "gen-training-data-locally",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import get_offline_features\n",
+ "\n",
+ "# Load the observation data. To understand observatio ndata, refer to part 1 of this tutorial\n",
+ "observation_data_path = \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/observation_data/train/*.parquet\"\n",
+ "observation_data_df = spark.read.parquet(observation_data_path)\n",
+ "obs_data_timestamp_column = \"timestamp\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# generate training dataframe by using feature data and observation data\n",
+ "training_df = get_offline_features(\n",
+ " features=features,\n",
+ " observation_data=observation_data_df,\n",
+ " timestamp_column=obs_data_timestamp_column,\n",
+ ")\n",
+ "\n",
+ "# Ignore the message that says feature set is not materialized (materialization is optional). We will enable materialization in the next part of the tutorial.\n",
+ "display(training_df)\n",
+ "# Note: display(training_df.head(5)) displays the timestamp column in a different format. You can can call training_df.show() to see correctly formatted value"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2c: Register the `accounts` featureset with the featurestore\n",
+ "Once you have experimented with different feature definitions locally and sanity tested it, you can register it with the feature store.\n",
+ "For this you will register a featureset asset definition with the feature store.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550813095
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "reg-accts-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import FeatureSet, FeatureSetSpecification\n",
+ "\n",
+ "accounts_fset_config = FeatureSet(\n",
+ " name=\"accounts\",\n",
+ " version=\"1\",\n",
+ " description=\"accounts featureset\",\n",
+ " entities=[f\"azureml:account:1\"],\n",
+ " stage=\"Development\",\n",
+ " specification=FeatureSetSpecification(path=accounts_featureset_spec_folder),\n",
+ " tags={\"data_type\": \"nonPII\"},\n",
+ ")\n",
+ "\n",
+ "poller = fs_client.feature_sets.begin_create_or_update(accounts_fset_config)\n",
+ "print(poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 2d: Get registered featureset and sanity test"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550826621
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "sample-accts-fset-data",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# look up the featureset by providing name and version\n",
+ "accounts_featureset = featurestore.feature_sets.get(\"accounts\", \"1\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# get access to the feature data\n",
+ "accounts_feature_df = accounts_featureset.to_spark_dataframe()\n",
+ "display(accounts_feature_df.head(5))\n",
+ "# Note: Please ignore this warning: Failure while loading azureml_run_type_providers. Failed to load entrypoint azureml.scriptrun"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 3: Run training experiment\n",
+ "In this step you will select a list of features, run a training pipeline, and register the model. You can repeat this step till you are happy with the model performance."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### (Optional) Step 3a: Discover features from Feature Store UI\n",
+ "You have already done this in part 1 of the tutorial after registering the `transactions` feature set. Since you also have `accounts` featureset, you can browse the available features:\n",
+ "* Goto the [Azure ML global landing page](https://ml.azure.com/home?flight=FeatureStores).\n",
+ "* Click on `Feature stores` in the left nav\n",
+ "* You will see the list of feature stores that you have access to. Click on the feature store that you created above.\n",
+ "\n",
+ "You can see the feature sets and entity that you created. Click on the feature sets to browse the feature definitions. You can also search for feature sets across feature stores by using the global search box."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### (Optional) Step 3b: Discover features from SDK"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550851764
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "discover-features-from-sdk",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# List available feature sets\n",
+ "all_featuresets = featurestore.feature_sets.list()\n",
+ "for fs in all_featuresets:\n",
+ " print(fs)\n",
+ "\n",
+ "# List of versions for transactions feature set\n",
+ "all_transactions_featureset_versions = featurestore.feature_sets.list(\n",
+ " name=\"transactions\"\n",
+ ")\n",
+ "for fs in all_transactions_featureset_versions:\n",
+ " print(fs)\n",
+ "\n",
+ "# See properties of the transactions featureset including list of features\n",
+ "featurestore.feature_sets.get(name=\"transactions\", version=\"1\").features"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 3c: Select features for the model and export it as a feature-retrieval spec\n",
+ "In the previous steps, you selected features from a combination unregistered and registered feature sets for local experimentation and testing. Now you are ready to experiment in the cloud. Saving the selected features as a feature-retrieval spec and using it in the mlops/cicd flow for training/inference increases your agility in shipping models."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "Select features for the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550863111
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "select-reg-features",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# you can select features in pythonic way\n",
+ "features = [\n",
+ " accounts_featureset.get_feature(\"accountAge\"),\n",
+ " transactions_featureset.get_feature(\"transaction_amount_7d_sum\"),\n",
+ " transactions_featureset.get_feature(\"transaction_amount_3d_sum\"),\n",
+ "]\n",
+ "\n",
+ "# you can also specify features in string form: featurestore:featureset:version:feature\n",
+ "more_features = [\n",
+ " f\"accounts:1:numPaymentRejects1dPerUser\",\n",
+ " f\"transactions:1:transaction_amount_7d_avg\",\n",
+ "]\n",
+ "\n",
+ "more_features = featurestore.resolve_feature_uri(more_features)\n",
+ "\n",
+ "features.extend(more_features)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "Export selected features as a feature-retrieval spec\n",
+ "\n",
+ "#### Note\n",
+ "Feature retrieval spec is a portable definition of list of features associated with a model. This can help streamline ML model development and operationalizing.This will be an input to the training pipeline (used to generate the training data), then will be packaged along with the model, and will be used during inference to lookup the features. It will be a glue that integrates all phases of the ML lifecycle. Changes to training/inference pipeline can be kept minimal as you experiment and deploy. \n",
+ "\n",
+ "Using feature retrieval spec and the built-in feature retrieval component is optional: you can directly use `get_offline_features()` api as shown above.\n",
+ "\n",
+ "Note that the name of the spec should be `feature_retrieval_spec.yaml` when it is packaged with the model for the system to recognize it."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696550891771
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "export-as-frspec",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Create feature retrieval spec\n",
+ "feature_retrieval_spec_folder = root_dir + \"/project/fraud_model/feature_retrieval_spec\"\n",
+ "\n",
+ "# check if the folder exists, create one if not\n",
+ "if not os.path.exists(feature_retrieval_spec_folder):\n",
+ " os.makedirs(feature_retrieval_spec_folder)\n",
+ "\n",
+ "featurestore.generate_feature_retrieval_spec(feature_retrieval_spec_folder, features)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 4: Train in the cloud using pipelines and register model if satisfactory\n",
+ "In this step you will manually trigger the training pipeline. In a production scenario, this could be triggered by a ci/cd pipeline based on changes to the feature-retrieval spec in the source repository."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 4a: Run the training pipeline\n",
+ "The training pipeline has the following steps:\n",
+ "\n",
+ "1. Feature retrieval step: This is a built-in component takes as input the feature retrieval spec, the observation data and timestamp column name. It then generates the training data as output. It runs this as a managed spark job.\n",
+ "1. Training step: This step trains the model based on the training data and generates a model (not registered yet)\n",
+ "1. Evaluation step: This step validates whether model performance/quailty is within threshold (in our case it is a placeholder/dummy step for illustration purpose)\n",
+ "1. Register model step: This step registers the model\n",
+ "\n",
+ "Note: In part 2 of this tutorial you ran a backfill job to materialize data for `transactions` feature set. Feature retrieval step will read feature values from offline store for this feature set. The behavior will same even if you use `get_offline_features()` api."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1683429020656
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "run-training-pipeline",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import load_job # will be used later\n",
+ "\n",
+ "training_pipeline_path = (\n",
+ " root_dir + \"/project/fraud_model/pipelines/training_pipeline.yaml\"\n",
+ ")\n",
+ "training_pipeline_definition = load_job(source=training_pipeline_path)\n",
+ "training_pipeline_job = ws_client.jobs.create_or_update(training_pipeline_definition)\n",
+ "ws_client.jobs.stream(training_pipeline_job.name)\n",
+ "# Note: First time it runs, each step in pipeline can take ~ 15 mins. However subsequent runs can be faster (assuming spark pool is warm - default timeout is 30 mins)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Inspect the training pipeline and the model\n",
+ "Open the above pipeline run \"web view\" in new window to see the steps in the pipeline.\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 4b: Notice the feature retrieval spec in the model artifacts\n",
+ "1. In the left nav of the current workspace -> right click on `Models` -> Open in new tab or window\n",
+ "1. Click on `fraud_model`\n",
+ "1. Click on `Artifacts` in the top nav\n",
+ "\n",
+ "You can notice that the feature retrieval spec is packaged along with the model. The model registration step in the training pipeline has done this. You created feature retrieval spec during experimentation, now it has become part of the model definition. In the next tutorial you will see how this will be used during inferencing.\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 5: View the feature set and model dependencies"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 5a: View the list of feature sets associated with the model\n",
+ "In the same models page, click on the `feature sets` tab. Here you can see both `transactions` and `accounts` featuresets that this model depends on."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Step 5b: View the list of models using the feature sets\n",
+ "1. Open the feature store UI (expalined in a previous step in this tutorial)\n",
+ "1. Click on `Feature sets` on the left nav\n",
+ "1. Click on any of the feature set -> click on `Models` tab\n",
+ "\n",
+ "You can see the list of models that are using the feature sets (determined from the feature retrieval spec when the model was registered)."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Cleanup\n",
+ "\n",
+ "Tutorial \"5. Develop a feature set with custom source\" has instructions for deleting the resources."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Next steps\n",
+ "* Enable recurrent materialization and run batch inference"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Edit Metadata",
+ "kernel_info": {
+ "name": "synapse_pyspark"
+ },
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.13"
+ },
+ "microsoft": {
+ "host": {
+ "AzureML": {
+ "notebookHasBeenCompleted": true
+ }
+ },
+ "ms_spell_check": {
+ "ms_spell_check_language": "en"
+ }
+ },
+ "nteract": {
+ "version": "nteract-front-end@1.0.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/3. Enable recurrent materialization and run batch inference.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_only/3. Enable recurrent materialization and run batch inference.ipynb
index 813069d2e3..a7ee397a7d 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_only/3. Enable recurrent materialization and run batch inference.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_only/3. Enable recurrent materialization and run batch inference.ipynb
@@ -1,620 +1,593 @@
{
- "cells": [
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Tutorial: Enable recurrent materialization and run batch inference"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "_Managed feature store is in private preview, which is subject to the [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/en-us/support/legal/preview-supplemental-terms/)_"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing.\n",
- "\n",
- "In the previous part of the tutorial you learnt to experiment with features, train the model and register the model along with the feature-retrieval spec. In this tutorial you will learn how to run batch inference for the registered model.\n",
- "\n",
- "You will perform the following:\n",
- "- Enable recurrent materialization for the `transactions` feature set\n",
- "- Run batch inference pipeline on the registered model"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Prerequisites\n",
- "1. Please ensure you have executed the previous parts of this tutorial series"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Setup"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Configure Azure ML spark notebook\n",
- "\n",
- "1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n",
- "1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n",
- "\n",
- "\n"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Start spark session"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "code",
- "source": [
- "# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
- "print(\"start spark session\")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696552142635
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "start-spark-session",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Setup root directory for the samples"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Tutorial #3: Enable recurrent materialization and run batch inference"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing.\n",
+ "\n",
+ "In the previous part of the tutorial you learnt to experiment with features, train the model and register the model along with the feature-retrieval spec. In this tutorial you will learn how to run batch inference for the registered model.\n",
+ "\n",
+ "You will perform the following:\n",
+ "- Enable recurrent materialization for the `transactions` feature set\n",
+ "- Run batch inference pipeline on the registered model"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Prerequisites\n",
+ "1. Please ensure you have executed the previous parts of this tutorial series"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Setup"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Configure Azure ML spark notebook\n",
+ "\n",
+ "1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n",
+ "1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Start spark session"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696552142635
},
- {
- "cell_type": "code",
- "source": [
- "import os\n",
- "\n",
- "# please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
- "# You can find the name from the directory structure in the left nav\n",
- "root_dir = \"./Users//featurestore_sample\"\n",
- "\n",
- "if os.path.isdir(root_dir):\n",
- " print(\"The folder exists.\")\n",
- "else:\n",
- " print(\"The folder does not exist. Please create or fix the path\")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696552223358
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "root-dir",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Initialize the project workspace CRUD client\n",
- "This is the current workspace where you will be running the tutorial notebook from"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "name": "start-spark-session",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# run this cell to start the spark session (any code block will start the session ). This can take around 10 mins.\n",
+ "print(\"start spark session\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Setup root directory for the samples"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696552223358
},
- {
- "cell_type": "code",
- "source": [
- "### Initialize the MLClient of this project workspace\n",
- "import os\n",
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
- "version = \"\"\n",
- "\n",
- "# connect to the project workspace\n",
- "ws_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696552235806
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-ws-crud-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Initialize the feature store CRUD client\n",
- "Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "name": "root-dir",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
+ "# You can find the name from the directory structure in the left nav\n",
+ "root_dir = \"./Users//featurestore_sample\"\n",
+ "\n",
+ "if os.path.isdir(root_dir):\n",
+ " print(\"The folder exists.\")\n",
+ "else:\n",
+ " print(\"The folder does not exist. Please create or fix the path\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Initialize the project workspace CRUD client\n",
+ "This is the current workspace where you will be running the tutorial notebook from"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696552235806
},
- {
- "cell_type": "code",
- "source": [
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "# feature store\n",
- "featurestore_name = \"my-featurestore\" # use the same name from part #1 of the tutorial\n",
- "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "\n",
- "# feature store ml client\n",
- "fs_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " featurestore_subscription_id,\n",
- " featurestore_resource_group_name,\n",
- " featurestore_name,\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696552265737
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-fs-crud-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Initialize the feature store core sdk client"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "name": "init-ws-crud-client",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "### Initialize the MLClient of this project workspace\n",
+ "import os\n",
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
+ "\n",
+ "# connect to the project workspace\n",
+ "ws_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Initialize the feature store CRUD client\n",
+ "Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696552265737
},
- {
- "cell_type": "code",
- "source": [
- "# feature store client\n",
- "from azureml.featurestore import FeatureStoreClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "featurestore = FeatureStoreClient(\n",
- " credential=AzureMLOnBehalfOfCredential(),\n",
- " subscription_id=featurestore_subscription_id,\n",
- " resource_group_name=featurestore_resource_group_name,\n",
- " name=featurestore_name,\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696552271317
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-fs-core-sdk",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Step 1: Enable recurrent materialization on the `transactions` featureset\n",
- "\n",
- "In part 2 of this tutorial you enabled materialization and performed backfill on the transactions feature set. Backfill is an ondemand one-time operation to compute and store feature values in the materialization store. However when you want to perform inference of the model in production, you might want to keep the materilization store upto date by setting up recurrent materialization jobs. These jobs run on user defined schedule\n",
- "The recurrent job schedule works in the following way: \n",
- "- A window is defined by the interval and frequency. E.g., interval = 3 and frequency = Hour define a 3-hour window\n",
- "- The first window starts at the start_time defined in the RecurenceTrigger, and so on.\n",
- "- The first recurrent job will be submitted at the begining of the next window after the update time.\n",
- "- Later recurrent jobs will be submitted at every window after the first job.\n",
- "\n",
- "As explained in the previous parts of the tutorials, once data is materialized (backfill/recurrent materialization), feature retrieval will use the materialized data by default."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "name": "init-fs-crud-client",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "# feature store\n",
+ "featurestore_name = (\n",
+ " \"\" # use the same name from part #1 of the tutorial\n",
+ ")\n",
+ "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "\n",
+ "# feature store ml client\n",
+ "fs_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " featurestore_subscription_id,\n",
+ " featurestore_resource_group_name,\n",
+ " featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Initialize the feature store core sdk client"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696552271317
},
- {
- "cell_type": "code",
- "source": [
- "from datetime import datetime\n",
- "from azure.ai.ml.entities import RecurrenceTrigger\n",
- "\n",
- "transactions_fset_config = fs_client.feature_sets.get(\n",
- " name=\"transactions\", version=version\n",
- ")\n",
- "\n",
- "# create a schedule that runs the materialization job every 3 hours\n",
- "transactions_fset_config.materialization_settings.schedule = RecurrenceTrigger(\n",
- " interval=3, frequency=\"Hour\", start_time=datetime(2023, 4, 15, 0, 4, 10, 0)\n",
- ")\n",
- "\n",
- "fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n",
- "\n",
- "print(fs_poller.result())"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696552294321
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "enable-recurrent-mat-txns-fset",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### (Optional) Save the feature set asset yaml with the updated settings"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "name": "init-fs-core-sdk",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# feature store client\n",
+ "from azureml.featurestore import FeatureStoreClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "featurestore = FeatureStoreClient(\n",
+ " credential=AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ " name=featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 1: Enable recurrent materialization on the `transactions` featureset\n",
+ "\n",
+ "In part 2 of this tutorial you enabled materialization and performed backfill on the transactions feature set. Backfill is an ondemand one-time operation to compute and store feature values in the materialization store. However when you want to perform inference of the model in production, you might want to keep the materilization store upto date by setting up recurrent materialization jobs. These jobs run on user defined schedule\n",
+ "The recurrent job schedule works in the following way: \n",
+ "- A window is defined by the interval and frequency. E.g., interval = 3 and frequency = Hour define a 3-hour window\n",
+ "- The first window starts at the start_time defined in the RecurenceTrigger, and so on.\n",
+ "- The first recurrent job will be submitted at the begining of the next window after the update time.\n",
+ "- Later recurrent jobs will be submitted at every window after the first job.\n",
+ "\n",
+ "As explained in the previous parts of the tutorials, once data is materialized (backfill/recurrent materialization), feature retrieval will use the materialized data by default."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696552294321
},
- {
- "cell_type": "code",
- "source": [
- "## uncomment and run\n",
- "# transactions_fset_config.dump(root_dir + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled_with_schedule.yaml\")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1681791896733
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "dump-txn-fset-with-mat-yaml",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Track status of the recurrent materialization jobs in the feature store studio UI\n",
- "This job will every three hours. \n",
- "\n",
- "__Action__:\n",
- "\n",
- "1. Feel free to execute the next step for now (batch inference).\n",
- "1. In three hours check the recurrent job status via the UI"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "name": "enable-recurrent-mat-txns-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from azure.ai.ml.entities import RecurrenceTrigger\n",
+ "\n",
+ "transactions_fset_config = fs_client.feature_sets.get(name=\"transactions\", version=\"1\")\n",
+ "\n",
+ "# create a schedule that runs the materialization job every 3 hours\n",
+ "transactions_fset_config.materialization_settings.schedule = RecurrenceTrigger(\n",
+ " interval=3, frequency=\"Hour\", start_time=datetime(2023, 4, 15, 0, 4, 10, 0)\n",
+ ")\n",
+ "\n",
+ "fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n",
+ "\n",
+ "print(fs_poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### (Optional) Save the feature set asset yaml with the updated settings"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1681791896733
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Step 2: Run the batch-inference pipeline\n",
- "\n",
- "In this step you will manually trigger the batch inference pipeline. In a production scenario, this could be trigerred by a ci/cd pipeline based on model registration/approval.\n",
- "\n",
- "The batch-inference has the following steps:\n",
- "\n",
- "1. Feature retrieval step: This use the same built-in feature retrieval component that we used in the training pipeline in the part 3 of the tutorial. Incase of training pipeline, we provided feature retreival spec as an input to the component. However in case of batch inference we will pass the registered model as the input and the component will look for feature retrieval spec in the model artifact. Another difference is that in case of training, the observation data had the target variable, however incase of batch inference it will not be present. The feature retrieval step will join the observation data with the features and output the data for batch inference.\n",
- "1. Batch inference: This step uses the batch inference input data from previous step, runs inference on the model and outputs the data by appending the predicted value.\n",
- "\n",
- "__Note:__ In this example we use a job for batch inference. You can also use Azure ML's batch endpoints."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "cell_type": "code",
- "source": [
- "from azure.ai.ml import load_job # will be used later\n",
- "\n",
- "# set the batch inference pipeline path\n",
- "batch_inference_pipeline_path = (\n",
- " root_dir + \"/project/fraud_model/pipelines/batch_inference_pipeline.yaml\"\n",
- ")\n",
- "batch_inference_pipeline_definition = load_job(source=batch_inference_pipeline_path)\n",
- "\n",
- "# run the training pipeline\n",
- "batch_inference_pipeline_job = ws_client.jobs.create_or_update(\n",
- " batch_inference_pipeline_definition\n",
- ")\n",
- "\n",
- "# stream the run logs\n",
- "ws_client.jobs.stream(batch_inference_pipeline_job.name)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683430315364
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "run-batch-inf-pipeline",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- }
+ "name": "dump-txn-fset-with-mat-yaml",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "## uncomment and run\n",
+ "# transactions_fset_config.dump(root_dir + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled_with_schedule.yaml\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "#### Track status of the recurrent materialization jobs in the feature store studio UI\n",
+ "This job will every three hours. \n",
+ "\n",
+ "__Action__:\n",
+ "\n",
+ "1. Feel free to execute the next step for now (batch inference).\n",
+ "1. In three hours check the recurrent job status via the UI"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Step 2: Run the batch-inference pipeline\n",
+ "\n",
+ "In this step you will manually trigger the batch inference pipeline. In a production scenario, this could be trigerred by a ci/cd pipeline based on model registration/approval.\n",
+ "\n",
+ "The batch-inference has the following steps:\n",
+ "\n",
+ "1. Feature retrieval step: This use the same built-in feature retrieval component that we used in the training pipeline in the part 3 of the tutorial. Incase of training pipeline, we provided feature retreival spec as an input to the component. However in case of batch inference we will pass the registered model as the input and the component will look for feature retrieval spec in the model artifact. Another difference is that in case of training, the observation data had the target variable, however incase of batch inference it will not be present. The feature retrieval step will join the observation data with the features and output the data for batch inference.\n",
+ "1. Batch inference: This step uses the batch inference input data from previous step, runs inference on the model and outputs the data by appending the predicted value.\n",
+ "\n",
+ "__Note:__ In this example we use a job for batch inference. You can also use Azure ML's batch endpoints."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1683430315364
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "#### Inspect the batch inference output data\n",
- "1. In the pipeline view, double click on `inference_step` -> in `outputs` card, copy the `Data` field. It will be something like `azureml_995abbc2-3171-461e-8214-c3c5d17ede83_output_data_data_with_prediction:1`. \n",
- "1. Paste it in the below cell with name and version separately (notice that the last character is the version, separated by a `:`).\n",
- "1. You will see the `predict_is_fraud` column generated by the batch inference pipeline\n",
- "\n",
- "Explanation: Since we did not provide a `name` and `version` in the `outputs` of the `inference_step` in the batch inference pipeline (`/project/fraud_mode/pipelines/batch_inference_pipeline.yaml`), the system created an untracked data asset with a guid as name and version as 1. In the next cell we will be getting the data path from the asset and displaying it."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
- {
- "cell_type": "code",
- "source": [
- "inf_data_output = ws_client.data.get(\n",
- " name=\"azureml_1c106662-aa5e-4354-b5f9-57c1b0fdb3a7_output_data_data_with_prediction\",\n",
- " version=\"1\",\n",
- ")\n",
- "inf_output_df = spark.read.parquet(inf_data_output.path)\n",
- "display(inf_output_df.head(5))"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1681446888800
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "inspect-batch-inf-output-data",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- }
+ "name": "run-batch-inf-pipeline",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
},
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Cleanup\n",
- "If you created a resource group for the tutorial, you can delete the resource group to delete all the resources associated with this tutorial.\n",
- "\n",
- "Otherwise, you can delete the resources individually:\n",
- "\n",
- "* Delete the feature store: Go to the resource group in the azure portal, select the feature store and delete it\n",
- "* Follow the instructions [here](https://review.learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-manage-user-assigned-managed-identities?pivots=identity-mi-methods-azp&view=azureml-api-2#delete-a-user-assigned-managed-identity) to delete the user assigned managed identity\n",
- "* Delete the offline store (storage account): Go to the resource group in the azure portal, select the storage you created and delete it"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import load_job # will be used later\n",
+ "\n",
+ "# set the batch inference pipeline path\n",
+ "batch_inference_pipeline_path = (\n",
+ " root_dir + \"/project/fraud_model/pipelines/batch_inference_pipeline.yaml\"\n",
+ ")\n",
+ "batch_inference_pipeline_definition = load_job(source=batch_inference_pipeline_path)\n",
+ "\n",
+ "# run the training pipeline\n",
+ "batch_inference_pipeline_job = ws_client.jobs.create_or_update(\n",
+ " batch_inference_pipeline_definition\n",
+ ")\n",
+ "\n",
+ "# stream the run logs\n",
+ "ws_client.jobs.stream(batch_inference_pipeline_job.name)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
}
- ],
- "metadata": {
- "celltoolbar": "Edit Metadata",
- "kernel_info": {
- "name": "synapse_pyspark"
- },
- "kernelspec": {
- "name": "synapse_pyspark",
- "language": "Python",
- "display_name": "Synapse PySpark"
- },
- "language_info": {
- "name": "python",
- "version": "3.8.0",
- "mimetype": "text/x-python",
- "file_extension": ".py",
- "pygments_lexer": "ipython",
- "codemirror_mode": "ipython",
- "nbconvert_exporter": "python"
+ },
+ "source": [
+ "#### Inspect the batch inference output data\n",
+ "1. In the pipeline view, double click on `inference_step` -> in `outputs` card, copy the `Data` field. It will be something like `azureml_995abbc2-3171-461e-8214-c3c5d17ede83_output_data_data_with_prediction:1`. \n",
+ "1. Paste it in the below cell with name and version separately (notice that the last character is the version, separated by a `:`).\n",
+ "1. You will see the `predict_is_fraud` column generated by the batch inference pipeline\n",
+ "\n",
+ "Explanation: Since we did not provide a `name` and `version` in the `outputs` of the `inference_step` in the batch inference pipeline (`/project/fraud_mode/pipelines/batch_inference_pipeline.yaml`), the system created an untracked data asset with a guid as name and version as 1. In the next cell we will be getting the data path from the asset and displaying it."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1681446888800
},
- "microsoft": {
- "host": {
- "AzureML": {
- "notebookHasBeenCompleted": true
- }
- },
- "ms_spell_check": {
- "ms_spell_check_language": "en"
- }
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
},
+ "name": "inspect-batch-inf-output-data",
"nteract": {
- "version": "nteract-front-end@1.0.0"
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "inf_data_output = ws_client.data.get(\n",
+ " name=\"azureml_1c106662-aa5e-4354-b5f9-57c1b0fdb3a7_output_data_data_with_prediction\",\n",
+ " version=\"1\",\n",
+ ")\n",
+ "inf_output_df = spark.read.parquet(inf_data_output.path)\n",
+ "display(inf_output_df.head(5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Cleanup\n",
+ "\n",
+ "Tutorial \"5. Develop a feature set with custom source\" has instructions for deleting the resources."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Edit Metadata",
+ "kernel_info": {
+ "name": "synapse_pyspark"
+ },
+ "kernelspec": {
+ "display_name": "Synapse PySpark",
+ "language": "Python",
+ "name": "synapse_pyspark"
+ },
+ "language_info": {
+ "codemirror_mode": "ipython",
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython",
+ "version": "3.8.0"
+ },
+ "microsoft": {
+ "host": {
+ "AzureML": {
+ "notebookHasBeenCompleted": true
}
+ },
+ "ms_spell_check": {
+ "ms_spell_check_language": "en"
+ }
},
- "nbformat": 4,
- "nbformat_minor": 2
-}
\ No newline at end of file
+ "nteract": {
+ "version": "nteract-front-end@1.0.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/4. Enable online store and run online inference.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_only/4. Enable online store and run online inference.ipynb
index 382e0b5dd5..0d6a8d1a25 100644
--- a/sdk/python/featurestore_sample/notebooks/sdk_only/4. Enable online store and run online inference.ipynb
+++ b/sdk/python/featurestore_sample/notebooks/sdk_only/4. Enable online store and run online inference.ipynb
@@ -1,1328 +1,1340 @@
{
- "cells": [
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Tutorial: Enable online materialization and run online inference\n",
- "So far you have learned how to develop features, materialize them to offline materialization store, perform training, and perform batch inference. In this tutorial you will learn how to use feature store for online/realtime inference use cases.\n",
- "\n",
- "You will perform the following steps:\n",
- "\n",
- "1. Setup Azure Cache for Redis.\n",
- "1. Attach the cache to feature store as the online materialization store and grant necessary permissions.\n",
- "1. Materialize feature sets to the online store.\n",
- "1. Test online deployment with mock data.\n"
- ],
- "metadata": {}
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Prerequisites\n",
- "1. Before proceeding, please ensure that you have already completed previous three tutorials of this series. We will be reusing feature store and some other resources created in the previous tutorials."
- ],
- "metadata": {}
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Prepare the notebook environment for development\n",
- "Note: This tutorial uses Azure Machine Learning notebook with **Serverless Spark Compute**.\n",
- "\n",
- "1. Clone the examples repository to your local machine: To run the tutorial, first clone the [examples repository - (azureml-examples)](https://github.com/azure/azureml-examples) with this command:\n",
- "\n",
- " `git clone --depth 1 https://github.com/Azure/azureml-examples`\n",
- "\n",
- " You can also download a zip file from the [examples repository (azureml-examples)](https://github.com/azure/azureml-examples). At this page, first select the `code` dropdown, and then select `Download ZIP`. Then, unzip the contents into a folder on your local device.\n",
- "\n",
- "2. Running the tutorial:\n",
- "* Option 1: Create a new notebook, and execute the instructions in this document step by step. \n",
- "* Option 2: Open the existing notebook `featurestore_sample/notebooks/sdk_only/5. Enable online store and run online inference.ipynb`. You may keep this document open and refer to it for additional explanation and documentation links.\n",
- "\n",
- " 1. Select **Serverless Spark Compute** in the top navigation **Compute** dropdown. This operation might take one to two minutes. Wait for a status bar in the top to display **Configure session**.\n",
- " 2. Select **Configure session** in the top status bar.\n",
- " 3. Select **Python packages**.\n",
- " 4. Select **Upload conda file**.\n",
- " 5. Select file `azureml-examples/sdk/python/featurestore-sample/project/env/online.yml` located on your local device.\n",
- " 6. (Optional) Increase the session time-out (idle time in minutes) to reduce the serverless spark cluster startup time."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Set up"
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Start Spark session\n",
- "Execute the following code cell to start the Spark session. It wil take approximately 10 minutes to install all dependencies and start the Spark session."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Run this cell to start the spark session (any code block will start the session ). This can take approximately 10 mins.\n",
- "print(\"start spark session\")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696553374079
- },
- "name": "start-spark-session"
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Setup root directory for the samples"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "import os\n",
- "\n",
- "# Please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
- "# You can find the name from the directory structure in the left navigation panel.\n",
- "root_dir = \"./Users//featurestore_sample\"\n",
- "\n",
- "if os.path.isdir(root_dir):\n",
- " print(\"The folder exists.\")\n",
- "else:\n",
- " print(\"The folder does not exist. Please create or fix the path\")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696553404071
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "root-dir",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Initialize the project workspace CRUD client\n",
- "The `MLClient` for the current workspace, where you are running this tutorial notebook, will be used for create, read, update, and delete (CRUD) operations."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "import os\n",
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
- "version = \"\"\n",
- "\n",
- "# Connect to the project workspace\n",
- "ws_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696553434418
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-prj-ws-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Initialize the CRUD client of the feature store workspace\n",
- "The `MLClient` for the feature store workspace for create, read, update, and delete (CRUD) operations on feature store workspace."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "# Feature store\n",
- "featurestore_name = \"my-featurestore\" # use the same name from part #1 of the tutorial\n",
- "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "\n",
- "# Feature store MLClient\n",
- "fs_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " featurestore_subscription_id,\n",
- " featurestore_resource_group_name,\n",
- " featurestore_name,\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "name": "init-fs-ws-client",
- "gather": {
- "logged": 1696553467100
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Initialize the feature store core SDK client\n",
- "This tutorial uses the Python feature store core SDK (`azureml-featurestore`). The SDK client initialized here is used for create, read, update, and delete (CRUD) operations, on feature stores, feature sets, and feature store entities."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azureml.featurestore import FeatureStoreClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "featurestore = FeatureStoreClient(\n",
- " credential=AzureMLOnBehalfOfCredential(),\n",
- " subscription_id=featurestore_subscription_id,\n",
- " resource_group_name=featurestore_resource_group_name,\n",
- " name=featurestore_name,\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "name": "init-fs-core-sdk",
- "gather": {
- "logged": 1696553483336
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Setup Azure cache for Redis\n",
- "This tutorial uses Azure Cache for Redis as the online materialization store. You can either create a new Redis instance or reuse an existing one."
- ],
- "metadata": {}
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Set values for the Azure Cache for Redis that will be used as online materialization store\n",
- "In the following code cell, define the name of the Azure Cache for Redis that you want to create or reuse. Optionally, you can override other default settings."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "ws_location = ws_client.workspaces.get(ws_client.workspace_name).location\n",
- "\n",
- "redis_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "redis_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "redis_name = \"redis1\"\n",
- "redis_location = ws_location"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696553534519
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "redis-settings",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Azure Cache for Redis (option 1): create new Redis instance\n",
- "You can select the Redis cache tier (basic, standard, premium, or enterprise). You should choose a SKU family that is available for the selected cache tier. See this documentation page to learn more about [how selecting different tiers may affect cache performance](https://learn.microsoft.com/azure/azure-cache-for-redis/cache-best-practices-performance). See this link learn more about [pricing for different SKU tiers and families of Azure Cache for Redis](https://azure.microsoft.com/en-us/pricing/details/cache/).\n",
- "\n",
- "Execute the following code cell to create an Azure Cache for Redis with premium tier, SKU family `P` and cache capacity 2. It may take approximately 5-10 minutes to provision the Redis instance."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.mgmt.redis import RedisManagementClient\n",
- "from azure.mgmt.redis.models import RedisCreateParameters, Sku, SkuFamily, SkuName\n",
- "\n",
- "management_client = RedisManagementClient(\n",
- " AzureMLOnBehalfOfCredential(), redis_subscription_id\n",
- ")\n",
- "\n",
- "# It usually takes about 5 - 10 min to finish the provision of the Redis instance.\n",
- "# If the following begin_create() call still hangs for longer than that,\n",
- "# please check the status of the Redis instance on the Azure portal and cancel the cell if the provision has completed.\n",
- "# This sample uses a PREMIUM tier Redis SKU from family P, which may cost more than a STANDARD tier SKU from family C.\n",
- "# Please choose the SKU tier and family according to your performance and pricing requirements.\n",
- "\n",
- "redis_arm_id = (\n",
- " management_client.redis.begin_create(\n",
- " resource_group_name=redis_resource_group_name,\n",
- " name=redis_name,\n",
- " parameters=RedisCreateParameters(\n",
- " location=redis_location,\n",
- " sku=Sku(name=SkuName.PREMIUM, family=SkuFamily.P, capacity=2),\n",
- " ),\n",
- " )\n",
- " .result()\n",
- " .id\n",
- ")\n",
- "\n",
- "print(redis_arm_id)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696554100442
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "provision-redis",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Azure Cache for Redis (option 2): use existing Redis instance\n",
- "Optionally, you can reuse an existing Redis instance with the previously defined name by executing the following code."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "redis_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.Cache/Redis/{name}\".format(\n",
- " sub_id=redis_subscription_id,\n",
- " rg=redis_resource_group_name,\n",
- " name=redis_name,\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696554530800
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "reuse-redis",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Retrieve the user-assigned managed identity (UAI) used for feature store for materialization\n",
- "This code cell retrieves the principal ID, client ID, and ARM ID property values for the UAI that will be used by the feature store for data materialization."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.mgmt.msi import ManagedServiceIdentityClient\n",
- "\n",
- "uai_arm_id = fs_client.feature_stores.get(\n",
- " featurestore_name\n",
- ").materialization_identity.resource_id\n",
- "uai_principal_id = fs_client.feature_stores.get(\n",
- " featurestore_name\n",
- ").materialization_identity.principal_id\n",
- "uai_client_id = fs_client.feature_stores.get(\n",
- " featurestore_name\n",
- ").materialization_identity.client_id\n",
- "\n",
- "print(\"uai_principal_id:\" + uai_principal_id)\n",
- "print(\"uai_client_id:\" + uai_client_id)\n",
- "print(\"uai_arm_id:\" + uai_arm_id)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1696554548087
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "retrieve-uai",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Step 1: Attach online materialization store to the feature store\n",
- "Attach the Azure Cache for Redis to the feature store to be used as the online materialization store."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.ai.ml.entities import (\n",
- " ManagedIdentityConfiguration,\n",
- " FeatureStore,\n",
- " MaterializationStore,\n",
- ")\n",
- "\n",
- "online_store = MaterializationStore(type=\"redis\", target=redis_arm_id)\n",
- "\n",
- "materialization_identity1 = ManagedIdentityConfiguration(\n",
- " client_id=uai_client_id, principal_id=uai_principal_id, resource_id=uai_arm_id\n",
- ")\n",
- "\n",
- "\n",
- "ml_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " subscription_id=featurestore_subscription_id,\n",
- " resource_group_name=featurestore_resource_group_name,\n",
- ")\n",
- "\n",
- "fs = FeatureStore(\n",
- " name=featurestore_name,\n",
- " online_store=online_store,\n",
- " materialization_identity=materialization_identity1,\n",
- ")\n",
- "\n",
- "fs_poller = ml_client.feature_stores.begin_create(fs, update_dependent_resources=True)\n",
- "print(fs_poller.result())"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684887054700
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "attach-online-store",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Step 2: Materialize `accounts` feature set data to online store"
- ],
- "metadata": {}
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Enable materialization on the `accounts` feature set\n",
- "In the previous parts of the tutorial series, we did **not** materialize the accounts feature set since it had precomputed features and was used only for batch inference scenarios. In this step we will enable online materialization so that the features are available in the online store with low latency access. We will also enable offline materialization for consistency. Enabling offline materialization is optional."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.ai.ml.entities import (\n",
- " MaterializationSettings,\n",
- " MaterializationComputeResource,\n",
- ")\n",
- "\n",
- "# Turn on both offline and online materialization on the \"accounts\" featureset.\n",
- "\n",
- "accounts_fset_config = fs_client._featuresets.get(name=\"accounts\", version=version)\n",
- "\n",
- "accounts_fset_config.materialization_settings = MaterializationSettings(\n",
- " offline_enabled=True,\n",
- " online_enabled=True,\n",
- " resource=MaterializationComputeResource(instance_type=\"standard_e8s_v3\"),\n",
- " spark_configuration={\n",
- " \"spark.driver.cores\": 4,\n",
- " \"spark.driver.memory\": \"36g\",\n",
- " \"spark.executor.cores\": 4,\n",
- " \"spark.executor.memory\": \"36g\",\n",
- " \"spark.executor.instances\": 2,\n",
- " },\n",
- " schedule=None,\n",
- ")\n",
- "\n",
- "fs_poller = fs_client.feature_sets.begin_create_or_update(accounts_fset_config)\n",
- "print(fs_poller.result())"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685121352342
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "enable-accounts-material",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Backfill the `account` feature set\n",
- "`backfill` command backfills data to all the materialization stores that are enabled for this feature set. In this case both offline and online materialization is enabled. Therefore `backfill` will be performed on both offline and online materialization stores."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "code",
- "source": [
- "from datetime import datetime, timedelta\n",
- "\n",
- "# Trigger backfill on the \"accounts\" feature set.\n",
- "# Backfill from 01/01/2023 to all the way to 3 hours ago.\n",
- "\n",
- "st = datetime(2020, 1, 1, 0, 0, 0, 0)\n",
- "ed = datetime.now() - timedelta(hours=3)\n",
- "\n",
- "poller = fs_client.feature_sets.begin_backfill(\n",
- " name=\"accounts\",\n",
- " version=version,\n",
- " feature_window_start_time=st,\n",
- " feature_window_end_time=ed,\n",
- ")\n",
- "print(poller.result().job_id)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "name": "start-accounts-backfill"
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "The following code cell tracks completion of the backfill job. Using the premium tier Azure Cache for Redis provisioned earlier, this step may take approximately 10 minutes to complete."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "code",
- "source": [
- "# Get the job URL, and stream the job logs.\n",
- "# With PREMIUM Redis SKU, SKU family \"P\", and cache capacity 2,\n",
- "# it takes approximately 10 minutes to complete.\n",
- "fs_client.jobs.stream(poller.result().job_id)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "name": "track-accounts-backfill",
- "tags": [
- "active-ipynb"
- ]
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Step 3: Materialize `transactions` feature set data to the online store\n",
- "\n",
- "In the previous tutorials, we materialized data of the `transactions` feature set to the offline materialization store. In this step we will:\n",
- "\n",
- "1. Enable online materilization for the `transactions` feature set."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Enable materialization to online store for the \"transactions\" feature set.\n",
- "\n",
- "transactions_fset_config = fs_client._featuresets.get(\n",
- " name=\"transactions\", version=version\n",
- ")\n",
- "transactions_fset_config.materialization_settings.online_enabled = True\n",
- "\n",
- "fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n",
- "print(fs_poller.result())"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1684887083625
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "enable-transact-material",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "markdown",
- "source": [
- "2. Backfill the data to both the online and offline materialization store to ensure that both have the latest data. Note that recurrent materialization job, which was setup earlier in the tutorial 2 of this series, will now materialize data to both online and offline materialization stores."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Trigger backfill on the \"transactions\" feature set to fill in the online/offline store.\n",
- "# Backfill from 01/01/2023 to all the way to 3 hours ago.\n",
- "\n",
- "from datetime import datetime, timedelta\n",
- "\n",
- "st = datetime(2020, 1, 1, 0, 0, 0, 0)\n",
- "ed = datetime.now() - timedelta(hours=3)\n",
- "\n",
- "\n",
- "poller = fs_client.feature_sets.begin_backfill(\n",
- " name=\"transactions\",\n",
- " version=version,\n",
- " feature_window_start_time=st,\n",
- " feature_window_end_time=ed,\n",
- ")\n",
- "print(poller.result().job_id)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685571817460
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "start-transact-material",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "markdown",
- "source": [
- "The following code cell tracks completion of the backfill job. Using the premium tier Azure Cache for Redis provisioned earlier, this step may take approximately 5 minutes to complete."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Get the job URL, and stream the job logs.\n",
- "# With PREMIUM Redis SKU, SKU family \"P\", and cache capacity 2,\n",
- "# it takes approximately 5 minutes to complete.\n",
- "fs_client.jobs.stream(poller.result().job_id)"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685572796715
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "track-transact-material",
- "nteract": {
- "transient": {
- "deleting": false
- }
- },
- "tags": [
- "active-ipynb"
- ]
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Step 4: Test locally\n",
- "In this step we will use our development environment (i.e. this notebook) to lookup features from online materialization store. "
- ],
- "metadata": {}
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "First, we will parse the list of features from the existing feature retrieval specification:"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Parse the list of features from the existing feature retrieval specification.\n",
- "feature_retrieval_spec_folder = root_dir + \"/project/fraud_model/feature_retrieval_spec\"\n",
- "\n",
- "features = featurestore.resolve_feature_retrieval_spec(feature_retrieval_spec_folder)\n",
- "\n",
- "features"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685132938320
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "parse-feat-list",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "Next, we will get feature values from the online materialization store:"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azureml.featurestore import init_online_lookup\n",
- "import time\n",
- "\n",
- "# Initialize the online store client.\n",
- "init_online_lookup(features, AzureMLOnBehalfOfCredential())"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685132960042
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-online-lookup",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "markdown",
- "source": [
- "Now, we will prepare some observation data for testing and use it to lookup features from the online materialization store. During online lookup, it may happen that the keys (`accountID`) defined in the observation sample data do not exist in the Redis (due to `TTL`). If this happens:\n",
- "1. Open Azure portal.\n",
- "2. Navigate to the Redis instance. \n",
- "3. Open console for the Redis instance and check for existing keys using command `KEYS *`.\n",
- "4. Replace `accountID` values in the sample observation data with the existing keys."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "import pyarrow\n",
- "from azureml.featurestore import get_online_features\n",
- "\n",
- "# Prepare test observation data\n",
- "obs = pyarrow.Table.from_pydict(\n",
- " {\"accountID\": [\"A985156952816816\", \"A1055521248929430\", \"A914800935560176\"]}\n",
- ")\n",
- "\n",
- "# Online lookup:\n",
- "# It may happen that the keys defined in the observation sample data above does not exist in the Redis (due to TTL).\n",
- "# If this happens, go to Azure portal and navigate to the Redis instance, open its console and check for existing keys using command \"KEYS *\"\n",
- "# and replace the sample observation data with the existing keys.\n",
- "df = get_online_features(features, obs)\n",
- "df"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685132964129
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "online-feat-loockup",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "Now that we have successfully looked up features from the online store, we will test online features using Azure Machine Learning managed online endpoint."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "# Step 5: Test online features from Azure Machine Learning managed online endpoint\n",
- "Managed online endpoint provide capability for deploying and scoring models for online/realtime inference. Optionally, you can use any inference technology of your choice (like kubernetes).\n",
- "\n",
- "As a part of this step, we will perform the following actions:\n",
- "\n",
- "1. Create an Azure Machine Learning managed online endpoint.\n",
- "1. Grant required role-based access control (RBAC) permissions.\n",
- "1. Deploy the model that we trained in the tutorial 3 of this tutorial series. The scoring script used in this step will have the code to lookup online features.\n",
- "2. Perform scoring of the model with sample data. You will see that the online features are looked up and model scoring is completed successfully."
- ],
- "metadata": {}
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Create Azure Machine Learning managed online endpoint\n",
- "You can learn more about managed online endpoints [here](https://learn.microsoft.com/azure/machine-learning/how-to-deploy-online-endpoints?view=azureml-api-2&tabs=azure-cli). Note that using managed feature store API, you can also lookup online features from other inference platforms based on your need.\n",
- "\n",
- "Following code defines a managed online endpoint with name `fraud-model`."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.ai.ml.entities import (\n",
- " ManagedOnlineDeployment,\n",
- " ManagedOnlineEndpoint,\n",
- " Model,\n",
- " CodeConfiguration,\n",
- " Environment,\n",
- ")\n",
- "\n",
- "\n",
- "endpoint_name = \"fraud-model\"\n",
- "\n",
- "endpoint = ManagedOnlineEndpoint(name=endpoint_name, auth_mode=\"key\")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685155956798
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "define-endpoint",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "markdown",
- "source": [
- "Excute the following code cell to create the managed online endpoint defined in the previous code cell."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "ws_client.online_endpoints.begin_create_or_update(endpoint).result()"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685156110582
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "create-endpoint",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "## Grant required RBAC permissions\n",
- "In this step, we will grant required RBAC permissions to the managed online endpoint on the Redis instance and feature store. The scoring code in the model deployment will need these RBAC permissions to successfully lookup features from the online store using the managed feature store API."
- ],
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Get managed identity of the managed online endpoint"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Get managed identity of the managed online endpoint.\n",
- "endpoint = ws_client.online_endpoints.get(endpoint_name)\n",
- "\n",
- "model_endpoint_msi_principal_id = endpoint.identity.principal_id\n",
- "model_endpoint_msi_principal_id"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685156114744
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "get-endpoint-identity",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Grant `Contributor` role to the online endpoint managed identity on the Azure Cache for Redis \n",
- "We will grant `Contributor` role to the online endpoint managed identity on the Redis instance. This RBAC permission is needed to materialize data into the Redis online store."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "from azure.core.exceptions import ResourceExistsError\n",
- "from azure.mgmt.msi import ManagedServiceIdentityClient\n",
- "from azure.mgmt.msi.models import Identity\n",
- "from azure.mgmt.authorization import AuthorizationManagementClient\n",
- "from azure.mgmt.authorization.models import RoleAssignmentCreateParameters\n",
- "from uuid import uuid4\n",
- "\n",
- "auth_client = AuthorizationManagementClient(\n",
- " AzureMLOnBehalfOfCredential(), redis_subscription_id\n",
- ")\n",
- "\n",
- "scope = f\"/subscriptions/{redis_subscription_id}/resourceGroups/{redis_resource_group_name}/providers/Microsoft.Cache/Redis/{redis_name}\"\n",
- "\n",
- "\n",
- "# The role definition ID for the \"contributor\" role on the redis cache\n",
- "# You can find other built-in role definition IDs in the Azure documentation\n",
- "role_definition_id = f\"/subscriptions/{redis_subscription_id}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c\"\n",
- "\n",
- "# Generate a random UUID for the role assignment name\n",
- "role_assignment_name = str(uuid4())\n",
- "\n",
- "# Set up the role assignment creation parameters\n",
- "role_assignment_params = RoleAssignmentCreateParameters(\n",
- " principal_id=model_endpoint_msi_principal_id,\n",
- " role_definition_id=role_definition_id,\n",
- " principal_type=\"ServicePrincipal\",\n",
- ")\n",
- "\n",
- "# Create the role assignment\n",
- "try:\n",
- " # Create the role assignment\n",
- " result = auth_client.role_assignments.create(\n",
- " scope, role_assignment_name, role_assignment_params\n",
- " )\n",
- " print(\n",
- " f\"Redis RBAC granted to managed identity '{model_endpoint_msi_principal_id}'.\"\n",
- " )\n",
- "except ResourceExistsError:\n",
- " print(\n",
- " f\"Redis RBAC already exists for managed identity '{model_endpoint_msi_principal_id}'.\"\n",
- " )"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685156142743
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "endpoint-redis-rbac",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Grant `AzureML Data Scientist` role to the online endpoint managed identity on the feature store\n",
- "We will grant `AzureML Data Scientist` role to the online endpoint managed identity on the feature store. This RBAC permission is required for successful deployment of the model to the online endpoint."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "auth_client = AuthorizationManagementClient(\n",
- " AzureMLOnBehalfOfCredential(), featurestore_subscription_id\n",
- ")\n",
- "\n",
- "scope = f\"/subscriptions/{featurestore_subscription_id}/resourceGroups/{featurestore_resource_group_name}/providers/Microsoft.MachineLearningServices/workspaces/{featurestore_name}\"\n",
- "\n",
- "# The role definition ID for the \"AzureML Data Scientist\" role.\n",
- "# You can find other built-in role definition IDs in the Azure documentation.\n",
- "role_definition_id = f\"/subscriptions/{featurestore_subscription_id}/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121\"\n",
- "\n",
- "# Generate a random UUID for the role assignment name.\n",
- "role_assignment_name = str(uuid4())\n",
- "\n",
- "# Set up the role assignment creation parameters.\n",
- "role_assignment_params = RoleAssignmentCreateParameters(\n",
- " principal_id=model_endpoint_msi_principal_id,\n",
- " role_definition_id=role_definition_id,\n",
- " principal_type=\"ServicePrincipal\",\n",
- ")\n",
- "\n",
- "# Create the role assignment\n",
- "try:\n",
- " # Create the role assignment\n",
- " result = auth_client.role_assignments.create(\n",
- " scope, role_assignment_name, role_assignment_params\n",
- " )\n",
- " print(\n",
- " f\"Feature store RBAC granted to managed identity '{model_endpoint_msi_principal_id}'.\"\n",
- " )\n",
- "except ResourceExistsError:\n",
- " print(\n",
- " f\"Feature store RBAC already exists for managed identity '{model_endpoint_msi_principal_id}'.\"\n",
- " )"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685156168113
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "endpoint-fs-rbac",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Deploy the model to the online endpoint\n",
- "First, inspect the scoring script `project/fraud_model/online_inference/src/scoring.py`. The scoring script performs the following tasks:\n",
- "\n",
- "1. Load the feature metadata from the feature retrieval specification that was packaged along with the model during model training (tutorial 3 of this tutorial series). This specification has features from both `transactions` and `accounts` feature sets.\n",
- "2. When an input inference request is received, the scoring code looks up the online features using the index keys from the request. In this case for both feature sets, the index column is the `accountID`.\n",
- "3. Passes the features to the model to perform inference and returs the response, a boolean value representing the variable `is_fraud`.\n",
- "\n",
- "First, create managed online deployment definition for model deployment by executing the following code cell:"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "deployment = ManagedOnlineDeployment(\n",
- " name=\"green\",\n",
- " endpoint_name=endpoint_name,\n",
- " model=\"azureml:fraud_model:1\",\n",
- " code_configuration=CodeConfiguration(\n",
- " code=root_dir + \"/project/fraud_model/online_inference/src/\",\n",
- " scoring_script=\"scoring.py\",\n",
- " ),\n",
- " environment=Environment(\n",
- " conda_file=root_dir + \"/project/fraud_model/online_inference/conda.yml\",\n",
- " image=\"mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04\",\n",
- " ),\n",
- " instance_type=\"Standard_DS3_v2\",\n",
- " instance_count=1,\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685156215970
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "define-online-deployment",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "cell_type": "markdown",
- "source": [
- "Next, deploy the model to online enpoint by executing the following code cell. Note that it may take approximately 4-5 minutes to deploy the model."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Model deployment to online enpoint may take 4-5 minutes.\n",
- "ws_client.online_deployments.begin_create_or_update(deployment).result()"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685156672789
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "begin-online-deployment",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "source": [
- "### Test online deployment with mock data\n",
- "Finally, execute the following code to test the online deployment using the mock data. You should see `0` or `1` as the output of this cell."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": [
- "# Test the online deployment using the mock data.\n",
- "sample_data = root_dir + \"/project/fraud_model/online_inference/test.json\"\n",
- "ws_client.online_endpoints.invoke(\n",
- " endpoint_name=endpoint_name, request_file=sample_data, deployment_name=\"green\"\n",
- ")"
- ],
- "outputs": [],
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1685157485313
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "test-online-deployment",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- }
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Tutorial #4: Enable online materialization and run online inference\n",
+ "So far you have learned how to develop features, materialize them to offline materialization store, perform training, and perform batch inference. In this tutorial you will learn how to use feature store for online/realtime inference use cases.\n",
+ "\n",
+ "You will perform the following steps:\n",
+ "\n",
+ "1. Setup Azure Cache for Redis.\n",
+ "1. Attach the cache to feature store as the online materialization store and grant necessary permissions.\n",
+ "1. Materialize feature sets to the online store.\n",
+ "1. Test online deployment with mock data.\n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Prerequisites\n",
+ "1. Before proceeding, please ensure that you have already completed previous three tutorials of this series. We will be reusing feature store and some other resources created in the previous tutorials."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
}
- ],
- "metadata": {
- "celltoolbar": "Edit Metadata",
- "kernel_info": {
- "name": "synapse_pyspark"
- },
- "kernelspec": {
- "name": "synapse_pyspark",
- "language": "Python",
- "display_name": "Synapse PySpark"
- },
- "language_info": {
- "name": "python",
- "version": "3.8.0",
- "mimetype": "text/x-python",
- "file_extension": ".py",
- "pygments_lexer": "ipython",
- "codemirror_mode": "ipython",
- "nbconvert_exporter": "python"
- },
- "microsoft": {
- "host": {
- "AzureML": {
- "notebookHasBeenCompleted": true
- }
- },
- "ms_spell_check": {
- "ms_spell_check_language": "en"
- }
- },
+ },
+ "source": [
+ "## Prepare the notebook environment for development\n",
+ "Note: This tutorial uses Azure Machine Learning notebook with **Serverless Spark Compute**.\n",
+ "\n",
+ "1. Clone the examples repository to your local machine: To run the tutorial, first clone the [examples repository - (azureml-examples)](https://github.com/azure/azureml-examples) with this command:\n",
+ "\n",
+ " `git clone --depth 1 https://github.com/Azure/azureml-examples`\n",
+ "\n",
+ " You can also download a zip file from the [examples repository (azureml-examples)](https://github.com/azure/azureml-examples). At this page, first select the `code` dropdown, and then select `Download ZIP`. Then, unzip the contents into a folder on your local device.\n",
+ "\n",
+ "2. Running the tutorial:\n",
+ "* Option 1: Create a new notebook, and execute the instructions in this document step by step. \n",
+ "* Option 2: Open the existing notebook `featurestore_sample/notebooks/sdk_only/5. Enable online store and run online inference.ipynb`. You may keep this document open and refer to it for additional explanation and documentation links.\n",
+ "\n",
+ " 1. Select **Serverless Spark Compute** in the top navigation **Compute** dropdown. This operation might take one to two minutes. Wait for a status bar in the top to display **Configure session**.\n",
+ " 2. Select **Configure session** in the top status bar.\n",
+ " 3. Select **Python packages**.\n",
+ " 4. Select **Upload conda file**.\n",
+ " 5. Select file `azureml-examples/sdk/python/featurestore-sample/project/env/online.yml` located on your local device.\n",
+ " 6. (Optional) Increase the session time-out (idle time in minutes) to reduce the serverless spark cluster startup time."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "# Set up"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Start Spark session\n",
+ "Execute the following code cell to start the Spark session. It wil take approximately 10 minutes to install all dependencies and start the Spark session."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696553374079
+ },
+ "name": "start-spark-session"
+ },
+ "outputs": [],
+ "source": [
+ "# Run this cell to start the spark session (any code block will start the session ). This can take approximately 10 mins.\n",
+ "print(\"start spark session\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Setup root directory for the samples"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696553404071
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "root-dir",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# Please update the dir to ./Users/ (or any custom directory you uploaded the samples to).\n",
+ "# You can find the name from the directory structure in the left navigation panel.\n",
+ "root_dir = \"./Users//featurestore_sample\"\n",
+ "\n",
+ "if os.path.isdir(root_dir):\n",
+ " print(\"The folder exists.\")\n",
+ "else:\n",
+ " print(\"The folder does not exist. Please create or fix the path\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialize the project workspace CRUD client\n",
+ "The `MLClient` for the current workspace, where you are running this tutorial notebook, will be used for create, read, update, and delete (CRUD) operations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696553434418
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-prj-ws-client",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
+ "\n",
+ "# Connect to the project workspace\n",
+ "ws_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialize the CRUD client of the feature store workspace\n",
+ "The `MLClient` for the feature store workspace for create, read, update, and delete (CRUD) operations on feature store workspace."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696553467100
+ },
+ "name": "init-fs-ws-client"
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "# Feature store\n",
+ "featurestore_name = (\n",
+ " \"\" # use the same name from part #1 of the tutorial\n",
+ ")\n",
+ "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "\n",
+ "# Feature store MLClient\n",
+ "fs_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " featurestore_subscription_id,\n",
+ " featurestore_resource_group_name,\n",
+ " featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialize the feature store core SDK client\n",
+ "This tutorial uses the Python feature store core SDK (`azureml-featurestore`). The SDK client initialized here is used for create, read, update, and delete (CRUD) operations, on feature stores, feature sets, and feature store entities."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696553483336
+ },
+ "name": "init-fs-core-sdk"
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import FeatureStoreClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "featurestore = FeatureStoreClient(\n",
+ " credential=AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ " name=featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setup Azure cache for Redis\n",
+ "This tutorial uses Azure Cache for Redis as the online materialization store. You can either create a new Redis instance or reuse an existing one."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Set values for the Azure Cache for Redis that will be used as online materialization store\n",
+ "In the following code cell, define the name of the Azure Cache for Redis that you want to create or reuse. Optionally, you can override other default settings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696553534519
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "redis-settings",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "ws_location = ws_client.workspaces.get(ws_client.workspace_name).location\n",
+ "\n",
+ "redis_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "redis_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "redis_name = \"redis1\"\n",
+ "redis_location = ws_location"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Azure Cache for Redis (option 1): create new Redis instance\n",
+ "You can select the Redis cache tier (basic, standard, premium, or enterprise). You should choose a SKU family that is available for the selected cache tier. See this documentation page to learn more about [how selecting different tiers may affect cache performance](https://learn.microsoft.com/azure/azure-cache-for-redis/cache-best-practices-performance). See this link learn more about [pricing for different SKU tiers and families of Azure Cache for Redis](https://azure.microsoft.com/en-us/pricing/details/cache/).\n",
+ "\n",
+ "Execute the following code cell to create an Azure Cache for Redis with premium tier, SKU family `P` and cache capacity 2. It may take approximately 5-10 minutes to provision the Redis instance."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696554100442
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "provision-redis",
"nteract": {
- "version": "nteract-front-end@1.0.0"
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.mgmt.redis import RedisManagementClient\n",
+ "from azure.mgmt.redis.models import RedisCreateParameters, Sku, SkuFamily, SkuName\n",
+ "\n",
+ "management_client = RedisManagementClient(\n",
+ " AzureMLOnBehalfOfCredential(), redis_subscription_id\n",
+ ")\n",
+ "\n",
+ "# It usually takes about 5 - 10 min to finish the provision of the Redis instance.\n",
+ "# If the following begin_create() call still hangs for longer than that,\n",
+ "# please check the status of the Redis instance on the Azure portal and cancel the cell if the provision has completed.\n",
+ "# This sample uses a PREMIUM tier Redis SKU from family P, which may cost more than a STANDARD tier SKU from family C.\n",
+ "# Please choose the SKU tier and family according to your performance and pricing requirements.\n",
+ "\n",
+ "redis_arm_id = (\n",
+ " management_client.redis.begin_create(\n",
+ " resource_group_name=redis_resource_group_name,\n",
+ " name=redis_name,\n",
+ " parameters=RedisCreateParameters(\n",
+ " location=redis_location,\n",
+ " sku=Sku(name=SkuName.PREMIUM, family=SkuFamily.P, capacity=2),\n",
+ " ),\n",
+ " )\n",
+ " .result()\n",
+ " .id\n",
+ ")\n",
+ "\n",
+ "print(redis_arm_id)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Azure Cache for Redis (option 2): use existing Redis instance\n",
+ "Optionally, you can reuse an existing Redis instance with the previously defined name by executing the following code."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696554530800
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "reuse-redis",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "redis_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.Cache/Redis/{name}\".format(\n",
+ " sub_id=redis_subscription_id,\n",
+ " rg=redis_resource_group_name,\n",
+ " name=redis_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Retrieve the user-assigned managed identity (UAI) used for feature store for materialization\n",
+ "This code cell retrieves the principal ID, client ID, and ARM ID property values for the UAI that will be used by the feature store for data materialization."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1696554548087
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "retrieve-uai",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.mgmt.msi import ManagedServiceIdentityClient\n",
+ "\n",
+ "uai_arm_id = fs_client.feature_stores.get(\n",
+ " featurestore_name\n",
+ ").materialization_identity.resource_id\n",
+ "uai_principal_id = fs_client.feature_stores.get(\n",
+ " featurestore_name\n",
+ ").materialization_identity.principal_id\n",
+ "uai_client_id = fs_client.feature_stores.get(\n",
+ " featurestore_name\n",
+ ").materialization_identity.client_id\n",
+ "\n",
+ "print(\"uai_principal_id:\" + uai_principal_id)\n",
+ "print(\"uai_client_id:\" + uai_client_id)\n",
+ "print(\"uai_arm_id:\" + uai_arm_id)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Step 1: Attach online materialization store to the feature store\n",
+ "Attach the Azure Cache for Redis to the feature store to be used as the online materialization store."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1684887054700
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "attach-online-store",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import (\n",
+ " ManagedIdentityConfiguration,\n",
+ " FeatureStore,\n",
+ " MaterializationStore,\n",
+ ")\n",
+ "\n",
+ "online_store = MaterializationStore(type=\"redis\", target=redis_arm_id)\n",
+ "\n",
+ "materialization_identity1 = ManagedIdentityConfiguration(\n",
+ " client_id=uai_client_id, principal_id=uai_principal_id, resource_id=uai_arm_id\n",
+ ")\n",
+ "\n",
+ "\n",
+ "ml_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ ")\n",
+ "\n",
+ "fs = FeatureStore(\n",
+ " name=featurestore_name,\n",
+ " online_store=online_store,\n",
+ " materialization_identity=materialization_identity1,\n",
+ ")\n",
+ "\n",
+ "fs_poller = ml_client.feature_stores.begin_create(fs)\n",
+ "print(fs_poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Step 2: Materialize `accounts` feature set data to online store"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Enable materialization on the `accounts` feature set\n",
+ "In the previous parts of the tutorial series, we did **not** materialize the accounts feature set since it had precomputed features and was used only for batch inference scenarios. In this step we will enable online materialization so that the features are available in the online store with low latency access. We will also enable offline materialization for consistency. Enabling offline materialization is optional."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685121352342
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "enable-accounts-material",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import (\n",
+ " MaterializationSettings,\n",
+ " MaterializationComputeResource,\n",
+ ")\n",
+ "\n",
+ "# Turn on both offline and online materialization on the \"accounts\" featureset.\n",
+ "\n",
+ "accounts_fset_config = fs_client._featuresets.get(name=\"accounts\", version=\"1\")\n",
+ "\n",
+ "accounts_fset_config.materialization_settings = MaterializationSettings(\n",
+ " offline_enabled=True,\n",
+ " online_enabled=True,\n",
+ " resource=MaterializationComputeResource(instance_type=\"standard_e8s_v3\"),\n",
+ " spark_configuration={\n",
+ " \"spark.driver.cores\": 4,\n",
+ " \"spark.driver.memory\": \"36g\",\n",
+ " \"spark.executor.cores\": 4,\n",
+ " \"spark.executor.memory\": \"36g\",\n",
+ " \"spark.executor.instances\": 2,\n",
+ " },\n",
+ " schedule=None,\n",
+ ")\n",
+ "\n",
+ "fs_poller = fs_client.feature_sets.begin_create_or_update(accounts_fset_config)\n",
+ "print(fs_poller.result())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "### Backfill the `account` feature set\n",
+ "`backfill` command backfills data to all the materialization stores that are enabled for this feature set. In this case both offline and online materialization is enabled. Therefore `backfill` will be performed on both offline and online materialization stores."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "start-accounts-backfill"
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime, timedelta\n",
+ "\n",
+ "# Trigger backfill on the \"accounts\" feature set.\n",
+ "# Backfill from 01/01/2023 to all the way to 3 hours ago.\n",
+ "\n",
+ "st = datetime(2020, 1, 1, 0, 0, 0, 0)\n",
+ "ed = datetime.now() - timedelta(hours=3)\n",
+ "\n",
+ "poller = fs_client.feature_sets.begin_backfill(\n",
+ " name=\"accounts\",\n",
+ " version=\"1\",\n",
+ " feature_window_start_time=st,\n",
+ " feature_window_end_time=ed,\n",
+ " data_status=[\"None\"],\n",
+ ")\n",
+ "print(poller.result().job_ids)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "The following code cell tracks completion of the backfill job. Using the premium tier Azure Cache for Redis provisioned earlier, this step may take approximately 10 minutes to complete."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "track-accounts-backfill",
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Get the job URL, and stream the job logs.\n",
+ "# With PREMIUM Redis SKU, SKU family \"P\", and cache capacity 2,\n",
+ "# it takes approximately 10 minutes to complete.\n",
+ "fs_client.jobs.stream(poller.result().job_ids[0])"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Step 3: Materialize `transactions` feature set data to the online store\n",
+ "\n",
+ "In the previous tutorials, we materialized data of the `transactions` feature set to the offline materialization store. In this step we will:\n",
+ "\n",
+ "1. Enable online materilization for the `transactions` feature set."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1684887083625
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "enable-transact-material",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Enable materialization to online store for the \"transactions\" feature set.\n",
+ "\n",
+ "transactions_fset_config = fs_client._featuresets.get(name=\"transactions\", version=\"1\")\n",
+ "transactions_fset_config.materialization_settings.online_enabled = True\n",
+ "\n",
+ "fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n",
+ "print(fs_poller.result())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. Backfill the data to both the online and offline materialization store to ensure that both have the latest data. Note that recurrent materialization job, which was setup earlier in the tutorial 3 of this series, will now materialize data to both online and offline materialization stores."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685571817460
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "start-transact-material",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Trigger backfill on the \"transactions\" feature set to fill in the online/offline store.\n",
+ "# Backfill from 01/01/2023 to all the way to 3 hours ago.\n",
+ "\n",
+ "from datetime import datetime, timedelta\n",
+ "\n",
+ "st = datetime(2020, 1, 1, 0, 0, 0, 0)\n",
+ "ed = datetime.now() - timedelta(hours=3)\n",
+ "\n",
+ "\n",
+ "poller = fs_client.feature_sets.begin_backfill(\n",
+ " name=\"transactions\",\n",
+ " version=\"1\",\n",
+ " feature_window_start_time=st,\n",
+ " feature_window_end_time=ed,\n",
+ " # data_status=[\"None\"],\n",
+ ")\n",
+ "# print(poller.result().job_ids)\n",
+ "print(poller.result().job_id)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The following code cell tracks completion of the backfill job. Using the premium tier Azure Cache for Redis provisioned earlier, this step may take approximately 5 minutes to complete."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685572796715
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "track-transact-material",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ },
+ "tags": [
+ "active-ipynb"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Get the job URL, and stream the job logs.\n",
+ "# With PREMIUM Redis SKU, SKU family \"P\", and cache capacity 2,\n",
+ "# it takes approximately 5 minutes to complete.\n",
+ "# fs_client.jobs.stream(poller.result().job_ids[0])\n",
+ "fs_client.jobs.stream(poller.result().job_id)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Step 4: Test locally\n",
+ "In this step we will use our development environment (i.e. this notebook) to lookup features from online materialization store. "
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First, we will parse the list of features from the existing feature retrieval specification:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685132938320
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "parse-feat-list",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Parse the list of features from the existing feature retrieval specification.\n",
+ "feature_retrieval_spec_folder = root_dir + \"/project/fraud_model/feature_retrieval_spec\"\n",
+ "\n",
+ "features = featurestore.resolve_feature_retrieval_spec(feature_retrieval_spec_folder)\n",
+ "\n",
+ "features"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we will get feature values from the online materialization store:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685132960042
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "init-online-lookup",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import init_online_lookup\n",
+ "import time\n",
+ "\n",
+ "# Initialize the online store client.\n",
+ "init_online_lookup(features, AzureMLOnBehalfOfCredential())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, we will prepare some observation data for testing and use it to lookup features from the online materialization store. During online lookup, it may happen that the keys (`accountID`) defined in the observation sample data do not exist in the Redis (due to `TTL`). If this happens:\n",
+ "1. Open Azure portal.\n",
+ "2. Navigate to the Redis instance. \n",
+ "3. Open console for the Redis instance and check for existing keys using command `KEYS *`.\n",
+ "4. Replace `accountID` values in the sample observation data with the existing keys."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685132964129
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "online-feat-loockup",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import pyarrow\n",
+ "from azureml.featurestore import get_online_features\n",
+ "\n",
+ "# Prepare test observation data\n",
+ "obs = pyarrow.Table.from_pydict(\n",
+ " {\"accountID\": [\"A985156952816816\", \"A1055521248929430\", \"A914800935560176\"]}\n",
+ ")\n",
+ "\n",
+ "# Online lookup:\n",
+ "# It may happen that the keys defined in the observation sample data above does not exist in the Redis (due to TTL).\n",
+ "# If this happens, go to Azure portal and navigate to the Redis instance, open its console and check for existing keys using command \"KEYS *\"\n",
+ "# and replace the sample observation data with the existing keys.\n",
+ "df = get_online_features(features, obs)\n",
+ "df"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "Now that we have successfully looked up features from the online store, we will test online features using Azure Machine Learning managed online endpoint."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Step 5: Test online features from Azure Machine Learning managed online endpoint\n",
+ "Managed online endpoint provide capability for deploying and scoring models for online/realtime inference. Optionally, you can use any inference technology of your choice (like kubernetes).\n",
+ "\n",
+ "As a part of this step, we will perform the following actions:\n",
+ "\n",
+ "1. Create an Azure Machine Learning managed online endpoint.\n",
+ "1. Grant required role-based access control (RBAC) permissions.\n",
+ "1. Deploy the model that we trained in the tutorial 3 of this tutorial series. The scoring script used in this step will have the code to lookup online features.\n",
+ "2. Perform scoring of the model with sample data. You will see that the online features are looked up and model scoring is completed successfully."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Create Azure Machine Learning managed online endpoint\n",
+ "You can learn more about managed online endpoints [here](https://learn.microsoft.com/azure/machine-learning/how-to-deploy-online-endpoints?view=azureml-api-2&tabs=azure-cli). Note that using managed feature store API, you can also lookup online features from other inference platforms based on your need.\n",
+ "\n",
+ "Following code defines a managed online endpoint with name `fraud-model`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685155956798
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "define-endpoint",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import (\n",
+ " ManagedOnlineDeployment,\n",
+ " ManagedOnlineEndpoint,\n",
+ " Model,\n",
+ " CodeConfiguration,\n",
+ " Environment,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "endpoint_name = \"fraud-model\"\n",
+ "\n",
+ "endpoint = ManagedOnlineEndpoint(name=endpoint_name, auth_mode=\"key\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Excute the following code cell to create the managed online endpoint defined in the previous code cell."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685156110582
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "create-endpoint",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "ws_client.online_endpoints.begin_create_or_update(endpoint).result()"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Grant required RBAC permissions\n",
+ "In this step, we will grant required RBAC permissions to the managed online endpoint on the Redis instance and feature store. The scoring code in the model deployment will need these RBAC permissions to successfully lookup features from the online store using the managed feature store API."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Get managed identity of the managed online endpoint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685156114744
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "get-endpoint-identity",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Get managed identity of the managed online endpoint.\n",
+ "endpoint = ws_client.online_endpoints.get(endpoint_name)\n",
+ "\n",
+ "model_endpoint_msi_principal_id = endpoint.identity.principal_id\n",
+ "model_endpoint_msi_principal_id"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Grant `Contributor` role to the online endpoint managed identity on the Azure Cache for Redis \n",
+ "We will grant `Contributor` role to the online endpoint managed identity on the Redis instance. This RBAC permission is needed to materialize data into the Redis online store."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685156142743
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "endpoint-redis-rbac",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.core.exceptions import ResourceExistsError\n",
+ "from azure.mgmt.msi import ManagedServiceIdentityClient\n",
+ "from azure.mgmt.msi.models import Identity\n",
+ "from azure.mgmt.authorization import AuthorizationManagementClient\n",
+ "from azure.mgmt.authorization.models import RoleAssignmentCreateParameters\n",
+ "from uuid import uuid4\n",
+ "\n",
+ "auth_client = AuthorizationManagementClient(\n",
+ " AzureMLOnBehalfOfCredential(), redis_subscription_id\n",
+ ")\n",
+ "\n",
+ "scope = f\"/subscriptions/{redis_subscription_id}/resourceGroups/{redis_resource_group_name}/providers/Microsoft.Cache/Redis/{redis_name}\"\n",
+ "\n",
+ "\n",
+ "# The role definition ID for the \"contributor\" role on the redis cache\n",
+ "# You can find other built-in role definition IDs in the Azure documentation\n",
+ "role_definition_id = f\"/subscriptions/{redis_subscription_id}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c\"\n",
+ "\n",
+ "# Generate a random UUID for the role assignment name\n",
+ "role_assignment_name = str(uuid4())\n",
+ "\n",
+ "# Set up the role assignment creation parameters\n",
+ "role_assignment_params = RoleAssignmentCreateParameters(\n",
+ " principal_id=model_endpoint_msi_principal_id,\n",
+ " role_definition_id=role_definition_id,\n",
+ " principal_type=\"ServicePrincipal\",\n",
+ ")\n",
+ "\n",
+ "# Create the role assignment\n",
+ "try:\n",
+ " # Create the role assignment\n",
+ " result = auth_client.role_assignments.create(\n",
+ " scope, role_assignment_name, role_assignment_params\n",
+ " )\n",
+ " print(\n",
+ " f\"Redis RBAC granted to managed identity '{model_endpoint_msi_principal_id}'.\"\n",
+ " )\n",
+ "except ResourceExistsError:\n",
+ " print(\n",
+ " f\"Redis RBAC already exists for managed identity '{model_endpoint_msi_principal_id}'.\"\n",
+ " )"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Grant `AzureML Data Scientist` role to the online endpoint managed identity on the feature store\n",
+ "We will grant `AzureML Data Scientist` role to the online endpoint managed identity on the feature store. This RBAC permission is required for successful deployment of the model to the online endpoint."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685156168113
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "endpoint-fs-rbac",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "auth_client = AuthorizationManagementClient(\n",
+ " AzureMLOnBehalfOfCredential(), featurestore_subscription_id\n",
+ ")\n",
+ "\n",
+ "scope = f\"/subscriptions/{featurestore_subscription_id}/resourceGroups/{featurestore_resource_group_name}/providers/Microsoft.MachineLearningServices/workspaces/{featurestore_name}\"\n",
+ "\n",
+ "# The role definition ID for the \"AzureML Data Scientist\" role.\n",
+ "# You can find other built-in role definition IDs in the Azure documentation.\n",
+ "role_definition_id = f\"/subscriptions/{featurestore_subscription_id}/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121\"\n",
+ "\n",
+ "# Generate a random UUID for the role assignment name.\n",
+ "role_assignment_name = str(uuid4())\n",
+ "\n",
+ "# Set up the role assignment creation parameters.\n",
+ "role_assignment_params = RoleAssignmentCreateParameters(\n",
+ " principal_id=model_endpoint_msi_principal_id,\n",
+ " role_definition_id=role_definition_id,\n",
+ " principal_type=\"ServicePrincipal\",\n",
+ ")\n",
+ "\n",
+ "# Create the role assignment\n",
+ "try:\n",
+ " # Create the role assignment\n",
+ " result = auth_client.role_assignments.create(\n",
+ " scope, role_assignment_name, role_assignment_params\n",
+ " )\n",
+ " print(\n",
+ " f\"Feature store RBAC granted to managed identity '{model_endpoint_msi_principal_id}'.\"\n",
+ " )\n",
+ "except ResourceExistsError:\n",
+ " print(\n",
+ " f\"Feature store RBAC already exists for managed identity '{model_endpoint_msi_principal_id}'.\"\n",
+ " )"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Deploy the model to the online endpoint\n",
+ "First, inspect the scoring script `project/fraud_model/online_inference/src/scoring.py`. The scoring script performs the following tasks:\n",
+ "\n",
+ "1. Load the feature metadata from the feature retrieval specification that was packaged along with the model during model training (tutorial 3 of this tutorial series). This specification has features from both `transactions` and `accounts` feature sets.\n",
+ "2. When an input inference request is received, the scoring code looks up the online features using the index keys from the request. In this case for both feature sets, the index column is the `accountID`.\n",
+ "3. Passes the features to the model to perform inference and returs the response, a boolean value representing the variable `is_fraud`.\n",
+ "\n",
+ "First, create managed online deployment definition for model deployment by executing the following code cell:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685156215970
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "define-online-deployment",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "deployment = ManagedOnlineDeployment(\n",
+ " name=\"green\",\n",
+ " endpoint_name=endpoint_name,\n",
+ " model=\"azureml:fraud_model:1\",\n",
+ " code_configuration=CodeConfiguration(\n",
+ " code=root_dir + \"/project/fraud_model/online_inference/src/\",\n",
+ " scoring_script=\"scoring.py\",\n",
+ " ),\n",
+ " environment=Environment(\n",
+ " conda_file=root_dir + \"/project/fraud_model/online_inference/conda.yml\",\n",
+ " image=\"mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04\",\n",
+ " ),\n",
+ " instance_type=\"Standard_DS3_v2\",\n",
+ " instance_count=1,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, deploy the model to online enpoint by executing the following code cell. Note that it may take approximately 4-5 minutes to deploy the model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685156672789
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "begin-online-deployment",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Model deployment to online enpoint may take 4-5 minutes.\n",
+ "ws_client.online_deployments.begin_create_or_update(deployment).result()"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test online deployment with mock data\n",
+ "Finally, execute the following code to test the online deployment using the mock data. You should see `0` or `1` as the output of this cell."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1685157485313
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "test-online-deployment",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Test the online deployment using the mock data.\n",
+ "sample_data = root_dir + \"/project/fraud_model/online_inference/test.json\"\n",
+ "ws_client.online_endpoints.invoke(\n",
+ " endpoint_name=endpoint_name, request_file=sample_data, deployment_name=\"green\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Cleanup\n",
+ "\n",
+ "Tutorial \"5. Develop a feature set with custom source\" has instructions for deleting the resources."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Edit Metadata",
+ "kernel_info": {
+ "name": "synapse_pyspark"
+ },
+ "kernelspec": {
+ "display_name": "Synapse PySpark",
+ "language": "Python",
+ "name": "synapse_pyspark"
+ },
+ "language_info": {
+ "codemirror_mode": "ipython",
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython",
+ "version": "3.8.0"
+ },
+ "microsoft": {
+ "host": {
+ "AzureML": {
+ "notebookHasBeenCompleted": true
}
+ },
+ "ms_spell_check": {
+ "ms_spell_check_language": "en"
+ }
},
- "nbformat": 4,
- "nbformat_minor": 2
-}
\ No newline at end of file
+ "nteract": {
+ "version": "nteract-front-end@1.0.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/5. Develop a feature set with custom source.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_only/5. Develop a feature set with custom source.ipynb
new file mode 100644
index 0000000000..f02ebc08d6
--- /dev/null
+++ b/sdk/python/featurestore_sample/notebooks/sdk_only/5. Develop a feature set with custom source.ipynb
@@ -0,0 +1,529 @@
+{
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Tutorial #5: Develop a feature set with a custom source\n",
+ "Managed feature store supports defining a custom source for data. A custom source definition allows you to define their own logic to load data from any data storage. This allows support for complex scenarios, such as\n",
+ "- Loading data from multiple tables with a complex join logic.\n",
+ "- Loading data efficiently from data sources that have a custom partition format.\n",
+ "- Support for data sources that do not use natively supported formats, e.g: parquet, `MLTable` and delta table. \n",
+ " \n",
+ "In this tutorial you will configure a feature set to consume data from a user-defined custom data source."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Prerequisites\n",
+ "\n",
+ "> [!NOTE]\n",
+ "> This tutorial uses Azure Machine Learning notebook with **Serverless Spark Compute**.\n",
+ "\n",
+ "1. Please ensure you have executed the first tutorial notebook that includes creation of a feature store and feature set, followed by enabling materialization and performing backfill."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "## Set up\n",
+ "\n",
+ "This tutorial uses the Python feature store core SDK (`azureml-featurestore`). The Python SDK is used for create, read, update, and delete (CRUD) operations, on feature stores, feature sets, and feature store entities.\n",
+ "\n",
+ "You don't need to explicitly install these resources for this tutorial, because in the set-up instructions shown here, the `conda.yaml` file covers them.\n",
+ "\n",
+ "To prepare the notebook environment for development:\n",
+ "\n",
+ "1. Clone the [azureml-examples](https://github.com/azure/azureml-examples) repository to your local GitHub resources with this command:\n",
+ "\n",
+ " `git clone --depth 1 https://github.com/Azure/azureml-examples`\n",
+ "\n",
+ " You can also download a zip file from the [azureml-examples](https://github.com/azure/azureml-examples) repository. At this page, first select the `code` dropdown, and then select `Download ZIP`. Then, unzip the contents into a folder on your local device.\n",
+ "\n",
+ "1. Upload the feature store samples directory to the project workspace\n",
+ "\n",
+ " 1. In the Azure Machine Learning workspace, open the Azure Machine Learning studio UI.\n",
+ " 1. Select **Notebooks** in left navigation panel.\n",
+ " 1. Select your user name in the directory listing.\n",
+ " 1. Select ellipses (**...**) and then select **Upload folder**.\n",
+ " 1. Select the feature store samples folder from the cloned directory path: `azureml-examples/sdk/python/featurestore-sample`.\n",
+ "\n",
+ "1. Run the tutorial\n",
+ "\n",
+ " * Option 1: Create a new notebook, and execute the instructions in this document, step by step.\n",
+ " * Option 2: Open existing notebook `featurestore_sample/notebooks/sdk_only/5. Develop a feature set with custom source.ipynb`. You may keep this document open and refer to it for more explanation and documentation links.\n",
+ "\n",
+ " 1. Select **Serverless Spark Compute** in the top navigation **Compute** dropdown. This operation might take one to two minutes. Wait for a status bar in the top to display **Configure session**.\n",
+ " 1. Select **Configure session** in the top status bar.\n",
+ " 1. Select **Python packages**.\n",
+ " 1. Select **Upload conda file**.\n",
+ " 1. Select file `azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml` located on your local device.\n",
+ " 1. (Optional) Increase the session time-out (idle time in minutes) to reduce the serverless spark cluster startup time."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setup root directory for the samples\n",
+ "This code cell sets up the root directory for the samples. It may needs about 10 minutes to execute this cell as it also installs all Conda dependencies and starts the Spark session."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1691710922464
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "root-dir",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "# Please update the dir to ./Users/{your-alias} (or any custom directory you uploaded the samples to).\n",
+ "# You can find the name from the directory structure in the left navigation panel.\n",
+ "root_dir = \"./Users//featurestore_sample\"\n",
+ "\n",
+ "if os.path.isdir(root_dir):\n",
+ " print(\"The folder exists.\")\n",
+ "else:\n",
+ " print(\"The folder does not exist. Please create or fix the path\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Initialize the CRUD client of the feature store workspace\n",
+ " Initialize the `MLClient` for the feature store workspace, for the create, read, update, and delete (CRUD) operations on the feature store workspace."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1691711012673
+ },
+ "name": "init-fset-crud-client"
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import MLClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "# Feature store\n",
+ "featurestore_name = (\n",
+ " \"my-featurestore\" # use the same name that was used in the tutorial #1\n",
+ ")\n",
+ "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
+ "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
+ "\n",
+ "# Feature store ml client\n",
+ "fs_client = MLClient(\n",
+ " AzureMLOnBehalfOfCredential(),\n",
+ " featurestore_subscription_id,\n",
+ " featurestore_resource_group_name,\n",
+ " featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Initialize the feature store core SDK client\n",
+ "As mentioned earlier, this tutorial uses the Python feature store core SDK (`azureml-featurestore`). This initialized SDK client is used for create, read, update, and delete (CRUD) operations, on feature stores, feature sets, and feature store entities."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1691711017028
+ },
+ "name": "init-fs-core-sdk"
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import FeatureStoreClient\n",
+ "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
+ "\n",
+ "featurestore = FeatureStoreClient(\n",
+ " credential=AzureMLOnBehalfOfCredential(),\n",
+ " subscription_id=featurestore_subscription_id,\n",
+ " resource_group_name=featurestore_resource_group_name,\n",
+ " name=featurestore_name,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Custom source definition\n",
+ "Custom source definition enables you to define your own source loading logic from any data storage. For using this feature, implement a a source processor user-defined function (UDF) class (`CustomerTransactionsTransformer` in this tutorial). This class should define an `__init__(self, **kwargs)` function and a `process(self, start_time, end_time, **kwargs)` function. The `kwargs` dictionary is supplied as a part of the feature set specification definition, which is passed to the UDF. The `start_time` and `end_time` parameters are calculated and passed to the UDF function.\n",
+ "\n",
+ "Below is a sample code of source processor UDF class:\n",
+ "\n",
+ "```python\n",
+ "from datetime import datetime\n",
+ "\n",
+ "\n",
+ "class CustomSourceTransformer:\n",
+ " def __init__(self, **kwargs):\n",
+ " self.path = kwargs.get(\"source_path\")\n",
+ " self.timestamp_column_name = kwargs.get(\"timestamp_column_name\")\n",
+ " if not self.path:\n",
+ " raise Exception(\"`source_path` is not provided\")\n",
+ " if not self.timestamp_column_name:\n",
+ " raise Exception(\"`timestamp_column_name` is not provided\")\n",
+ "\n",
+ " def process(\n",
+ " self, start_time: datetime, end_time: datetime, **kwargs\n",
+ " ) -> \"pyspark.sql.DataFrame\":\n",
+ " from pyspark.sql import SparkSession\n",
+ " from pyspark.sql.functions import col, lit, to_timestamp\n",
+ "\n",
+ " spark = SparkSession.builder.getOrCreate()\n",
+ " df = spark.read.json(self.path)\n",
+ "\n",
+ " if start_time:\n",
+ " df = df.filter(col(self.timestamp_column_name) >= to_timestamp(lit(start_time)))\n",
+ "\n",
+ " if end_time:\n",
+ " df = df.filter(col(self.timestamp_column_name) < to_timestamp(lit(end_time)))\n",
+ "\n",
+ " return df\n",
+ "\n",
+ "```"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create a feature set specification with custom source and experiment with it locally\n",
+ "Now, create a feature set specification with custom source definition and use your development environment to experiment with the feature set. The tutorial notebook attached to **Serverless Spark Compute** serves as the development environment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "create-fs-custom-src"
+ },
+ "outputs": [],
+ "source": [
+ "from azureml.featurestore import create_feature_set_spec\n",
+ "from azureml.featurestore.feature_source import CustomFeatureSource\n",
+ "from azureml.featurestore.contracts import (\n",
+ " SourceProcessCode,\n",
+ " TransformationCode,\n",
+ " Column,\n",
+ " ColumnType,\n",
+ " DateTimeOffset,\n",
+ " TimestampColumn,\n",
+ ")\n",
+ "\n",
+ "transactions_source_process_code_path = (\n",
+ " root_dir\n",
+ " + \"/featurestore/featuresets/transactions_custom_source/source_process_code\"\n",
+ ")\n",
+ "transactions_feature_transform_code_path = (\n",
+ " root_dir\n",
+ " + \"/featurestore/featuresets/transactions_custom_source/feature_process_code\"\n",
+ ")\n",
+ "\n",
+ "udf_featureset_spec = create_feature_set_spec(\n",
+ " source=CustomFeatureSource(\n",
+ " kwargs={\n",
+ " \"source_path\": \"wasbs://data@azuremlexampledata.blob.core.windows.net/feature-store-prp/datasources/transactions-source-json/*.json\",\n",
+ " \"timestamp_column_name\": \"timestamp\",\n",
+ " },\n",
+ " timestamp_column=TimestampColumn(name=\"timestamp\"),\n",
+ " source_delay=DateTimeOffset(days=0, hours=0, minutes=20),\n",
+ " source_process_code=SourceProcessCode(\n",
+ " path=transactions_source_process_code_path,\n",
+ " process_class=\"source_process.CustomSourceTransformer\",\n",
+ " ),\n",
+ " ),\n",
+ " feature_transformation=TransformationCode(\n",
+ " path=transactions_feature_transform_code_path,\n",
+ " transformer_class=\"transaction_transform.TransactionFeatureTransformer\",\n",
+ " ),\n",
+ " index_columns=[Column(name=\"accountID\", type=ColumnType.string)],\n",
+ " source_lookback=DateTimeOffset(days=7, hours=0, minutes=0),\n",
+ " temporal_join_lookback=DateTimeOffset(days=1, hours=0, minutes=0),\n",
+ " infer_schema=True,\n",
+ ")\n",
+ "\n",
+ "udf_featureset_spec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, define a feature window and display feature values in this feature window."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "display-features"
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "\n",
+ "st = datetime(2023, 1, 1)\n",
+ "et = datetime(2023, 6, 1)\n",
+ "\n",
+ "display(\n",
+ " udf_featureset_spec.to_spark_dataframe(\n",
+ " feature_window_start_date_time=st, feature_window_end_date_time=et\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Export as a feature set specification\n",
+ "To register the feature set specification with the feature store, you must save that specification in a specific format. Review the generated `transactions_custom_source` feature set specification. Open this file from the file tree to see the specification: `featurestore/featuresets/transactions_custom_source/spec/FeaturesetSpec.yaml`.\n",
+ "\n",
+ "The specification contains these elements:\n",
+ "\n",
+ "- `features`: A list of features and their datatypes.\n",
+ "- `index_columns`: The join keys required to access values from the feature set.\n",
+ "\n",
+ "To learn more about the specification, see [Understanding top-level entities in managed feature store](https://learn.microsoft.com/azure/machine-learning/concept-top-level-entities-in-managed-feature-store) and [CLI (v2) feature set YAML schema](https://learn.microsoft.com/azure/machine-learning/reference-yaml-feature-set).\n",
+ "\n",
+ "Persisting the feature set specification offers another benefit: the feature set specification can be source controlled."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "name": "dump-txn-fs-spec"
+ },
+ "outputs": [],
+ "source": [
+ "feature_spec_folder = (\n",
+ " root_dir + \"/featurestore/featuresets/transactions_custom_source/spec\"\n",
+ ")\n",
+ "\n",
+ "udf_featureset_spec.dump(feature_spec_folder)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Register the transaction feature set with the feature store\n",
+ "Use the following code to register a feature set asset loaded from custom source with the feature store. You can then reuse that asset and easily share it. Registration of a feature set asset offers managed capabilities, including versioning and materialization."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1691712951072
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "register-txn-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import FeatureSet, FeatureSetSpecification\n",
+ "\n",
+ "transaction_fset_config = FeatureSet(\n",
+ " name=\"transactions_custom_source\",\n",
+ " version=\"1\",\n",
+ " description=\"transactions feature set loaded from custom source\",\n",
+ " entities=[\"azureml:account:1\"],\n",
+ " stage=\"Development\",\n",
+ " specification=FeatureSetSpecification(path=feature_spec_folder),\n",
+ " tags={\"data_type\": \"nonPII\"},\n",
+ ")\n",
+ "\n",
+ "poller = fs_client.feature_sets.begin_create_or_update(transaction_fset_config)\n",
+ "print(poller.result())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the registered feature set, and print related information."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1691712961910
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "get-txn-fset",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Look up the feature set by providing name and version\n",
+ "transactions_fset_config = featurestore.feature_sets.get(\n",
+ " name=\"transactions_custom_source\", version=\"1\"\n",
+ ")\n",
+ "# Print feature set information\n",
+ "print(transactions_fset_config)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Test feature generation from registered feature set\n",
+ "Test feature generation from the registered feature set by using `to_spark_dataframe()` function of the feature set, and display the features."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "gather": {
+ "logged": 1691712966166
+ },
+ "jupyter": {
+ "outputs_hidden": false,
+ "source_hidden": false
+ },
+ "name": "print-txn-fset-sample-values",
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "outputs": [],
+ "source": [
+ "df = transactions_fset_config.to_spark_dataframe()\n",
+ "display(df)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "nteract": {
+ "transient": {
+ "deleting": false
+ }
+ }
+ },
+ "source": [
+ "You should be able to successfully fetch the registered feature set as a Spark dataframe and display it. Now, you can use these features for a point-in-time with observation data and the subsequent steps in you machine learning pipeline. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Cleanup\n",
+ "If you created a resource group for the tutorials, you can delete the resource group to delete all the resources associated with this tutorial.\n",
+ "\n",
+ "Otherwise, you can delete the resources individually:\n",
+ "\n",
+ "* Delete the feature store: Go to the resource group in the Azure portal, select the feature store and delete it.\n",
+ "* Follow the instructions [here](https://review.learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-manage-user-assigned-managed-identities?pivots=identity-mi-methods-azp&view=azureml-api-2#delete-a-user-assigned-managed-identity) to delete the user assigned managed identity.\n",
+ "* Delete the offline store (storage account): Go to the resource group in the Azure portal, select the storage you created and delete it.\n",
+ "* Delete the online store (Redis instance): Go to the resource group in the Azure portal, select the Redis instance you created and delete it."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Edit Metadata",
+ "kernel_info": {
+ "name": "synapse_pyspark"
+ },
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.13"
+ },
+ "microsoft": {
+ "host": {
+ "AzureML": {
+ "notebookHasBeenCompleted": true
+ }
+ },
+ "ms_spell_check": {
+ "ms_spell_check_language": "en"
+ }
+ },
+ "nteract": {
+ "version": "nteract-front-end@1.0.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/Enable materialization and backfill feature data.ipynb b/sdk/python/featurestore_sample/notebooks/sdk_only/Enable materialization and backfill feature data.ipynb
deleted file mode 100644
index b2cb282824..0000000000
--- a/sdk/python/featurestore_sample/notebooks/sdk_only/Enable materialization and backfill feature data.ipynb
+++ /dev/null
@@ -1,1093 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "# Tutorial #2: Enable materialization and backfill feature data"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "In this tutorial series you will experience how features seamlessly integrates all the phases of ML lifecycle: Prototyping features, training and operationalizing. \n",
- "\n",
- "In the part 1 of the tutorial you learnt how to create a feature set and use it to generate training data. When you query the featureset, the transformations will be applied on the source on-the-fly to compute the features before returning the values. This is fine for prototyping. However when you run training and inference in production environment, it is recommended that you materialize the features for higher reliability and availability. Materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. All feature queries will now use the values from the materialization store.\n",
- "\n",
- "In this tutorial (part 2 of the series) you will:\n",
- "- Enable offline store on the feature store by creating and attaching an ADLS gen2 container and a user assigned managed identity\n",
- "- Enable offline materialization on the feature sets, and backfill the feature data"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Important\n",
- "\n",
- "This feature is currently in public preview. This preview version is provided without a service-level agreement, and it's not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "# Prerequsite\n",
- "1. Please ensure you have executed part 1 of the tutorial\n",
- "1. An Azure Resource group, in which you (or the service principal you use) need to have `User Access Administrator` role and `Contributor` role."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "# Setup\n",
- "Summary of setup steps you will execute:\n",
- "- In your project workspace, create Azure ML compute to run training pipeline\n",
- "- In your feature store workspace, create a offline materialization store: create a Azure gen2 storage account and a container in it and attach to feature store. Optionally you can use existing storage container.\n",
- "- Create and assign user-assigned managed identity to feature store. Optionally you can use existing one. This will be used by the system managed materialization jobs i.e. recurrent job that will be used in part 3 of the tutorial\n",
- "- Grant required RBAC permissions to the user-assigned managed identity\n",
- "- Grant required RBAC to your AAD identity. Users (like you) need to have read access to (a) sources (b) materialization store"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Configure Azure ML spark notebook\n",
- "\n",
- "1. In the \"Compute\" dropdown in the top nav, select \"Serverless Spark Compute\". \n",
- "1. Click on \"configure session\" in top status bar -> click on \"Python packages\" -> click on \"upload conda file\" -> select the file azureml-examples/sdk/python/featurestore-sample/project/env/conda.yml from your local machine; Also increase the session time out (idle time) if you want to avoid running the prerequisites frequently\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422232605
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "start-spark-session",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "print(\"started spark session\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Setup root directory for the samples"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422281498
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "root-dir",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "import os\n",
- "\n",
- "# please update the dir to ./Users/{your-alias} (or any custom directory you uploaded the samples to).\n",
- "# You can find the name from the directory structure inm the left nav\n",
- "root_dir = \"./Users//featurestore_sample\"\n",
- "\n",
- "if os.path.isdir(root_dir):\n",
- " print(\"The folder exists.\")\n",
- "else:\n",
- " print(\"The folder does not exist. Please create or fix the path\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Initialize the project workspace CRUD client\n",
- "This is the current workspace where you will be running the tutorial notebook from"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422293418
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-ws-crud-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "### Initialize the MLClient of this project workspace\n",
- "import os\n",
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "project_ws_sub_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "project_ws_rg = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "project_ws_name = os.environ[\"AZUREML_ARM_WORKSPACE_NAME\"]\n",
- "\n",
- "# connect to the project workspace\n",
- "ws_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(), project_ws_sub_id, project_ws_rg, project_ws_name\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Initialize the feature store CRUD client\n",
- "Ensure you update the `featurestore_name` to reflect what you created in part 1 of this tutorial"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422298466
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-fs-crud-client",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from azure.ai.ml import MLClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "# feature store\n",
- "featurestore_name = \"my-featurestore\" # use the same name from part #1 of the tutorial\n",
- "featurestore_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "featurestore_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "\n",
- "# feature store ml client\n",
- "fs_client = MLClient(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " featurestore_subscription_id,\n",
- " featurestore_resource_group_name,\n",
- " featurestore_name,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Initialize the feature store core sdk client"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422304506
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "init-fs-core-sdk",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# feature store client\n",
- "from azureml.featurestore import FeatureStoreClient\n",
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "featurestore = FeatureStoreClient(\n",
- " credential=AzureMLOnBehalfOfCredential(),\n",
- " subscription_id=featurestore_subscription_id,\n",
- " resource_group_name=featurestore_resource_group_name,\n",
- " name=featurestore_name,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Setup offline materialization store\n",
- "You can create a new gen2 storage account and a container, or reuse existing one to be used as the offline materilization store for the feature store"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Setup utility functions\n",
- "Note: The below code sets up utility functions to create storage and user assigned identity. These utility functions use standard azure SDKs. These are provided to keep the tutorial concise. However do not use this for production purposes as it might not implement best practices."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422311529
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "setup-utility-fns",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "import sys\n",
- "\n",
- "sys.path.insert(0, root_dir + \"/featurestore/setup\")\n",
- "from setup_storage_uai import (\n",
- " create_gen2_storage_container,\n",
- " create_user_assigned_managed_identity,\n",
- " grant_rbac_permissions,\n",
- " grant_user_aad_storage_data_reader_role,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Set values for the adls gen 2 storage that will be used as materialization store\n",
- "You can optionally override the default settings"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422350826
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "set-offline-store-params",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from azure.ai.ml.identity import AzureMLOnBehalfOfCredential\n",
- "\n",
- "## Default Setting\n",
- "# We use the subscription, resource group, region of this active project workspace,\n",
- "# We hard-coded resource names for creating new resources\n",
- "\n",
- "## Overwrite\n",
- "# You can replace them if you want to create the resources in a different subsciprtion/resourceGroup, or use existing resources\n",
- "\n",
- "ws_location = ws_client.workspaces.get(ws_client.workspace_name).location\n",
- "\n",
- "# storage\n",
- "storage_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "storage_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "storage_account_name = \"\"\n",
- "storage_location = ws_location\n",
- "storage_file_system_name = \"offlinestore\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Storage container (option 1): create new storage container"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683418495136
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "create-new-storage",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "gen2_container_arm_id = create_gen2_storage_container(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " storage_subscription_id=storage_subscription_id,\n",
- " storage_resource_group_name=storage_resource_group_name,\n",
- " storage_account_name=storage_account_name,\n",
- " storage_location=storage_location,\n",
- " storage_file_system_name=storage_file_system_name,\n",
- ")\n",
- "\n",
- "print(gen2_container_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Storage container (option 2): If you have an existing storage container that you want to reuse"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683421319637
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "use-existing-storage",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "gen2_container_arm_id = \"/subscriptions/{sub_id}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account}/blobServices/default/containers/{container}\".format(\n",
- " sub_id=storage_subscription_id,\n",
- " rg=storage_resource_group_name,\n",
- " account=storage_account_name,\n",
- " container=storage_file_system_name,\n",
- ")\n",
- "\n",
- "print(gen2_container_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "### Setup user assigned managed identity (UAI)\n",
- "This will be used by the system managed materialization jobs i.e. recurrent job that will be used in part 3 of the tutorial"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Set values for UAI"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683421358493
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "set-uai-params",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# User assigned managed identity values. Optionally you may change the values.\n",
- "uai_subscription_id = os.environ[\"AZUREML_ARM_SUBSCRIPTION\"]\n",
- "uai_resource_group_name = os.environ[\"AZUREML_ARM_RESOURCEGROUP\"]\n",
- "uai_name = \"fstoreuai\"\n",
- "uai_location = ws_location"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### User-assigned managed identity (option 1): create new one"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683418554848
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "create-new-uai",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "uai_principal_id, uai_client_id, uai_arm_id = create_user_assigned_managed_identity(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " uai_subscription_id=uai_subscription_id,\n",
- " uai_resource_group_name=uai_resource_group_name,\n",
- " uai_name=uai_name,\n",
- " uai_location=uai_location,\n",
- ")\n",
- "\n",
- "print(\"uai_principal_id:\" + uai_principal_id)\n",
- "print(\"uai_client_id:\" + uai_client_id)\n",
- "print(\"uai_arm_id:\" + uai_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### User-assigned managed identity (option 2): If you have an existing one that you want to reuse"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683421366072
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "use-existing-uai",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from azure.mgmt.msi import ManagedServiceIdentityClient\n",
- "\n",
- "msi_client = ManagedServiceIdentityClient(\n",
- " AzureMLOnBehalfOfCredential(), uai_subscription_id\n",
- ")\n",
- "\n",
- "managed_identity = msi_client.user_assigned_identities.get(\n",
- " uai_resource_group_name, uai_name\n",
- ")\n",
- "\n",
- "uai_principal_id = managed_identity.principal_id\n",
- "uai_client_id = managed_identity.client_id\n",
- "uai_arm_id = managed_identity.id\n",
- "\n",
- "print(\"uai_principal_id:\" + uai_principal_id)\n",
- "print(\"uai_client_id:\" + uai_client_id)\n",
- "print(\"uai_arm_id:\" + uai_arm_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "##### Grant RBAC permission to the user assigned managed identity (UAI)\n",
- "\n",
- "This UAI will be assigned to the feature store shortly. It requires the following permissions:\n",
- "\n",
- "|Scope|\tAction/Role|\n",
- "|--|--|\n",
- "|Feature store\t|AzureML Data Scientist role|\n",
- "|Storage account of feature store offline store\t|Blob storage data contributor role|\n",
- "|Storage accounts of source data\t|Blob storage data reader role|\n",
- "\n",
- "The below code utility function will assign the first two roles to the UAI. In this example \"Storage accounts of source data\" is not applicable since we are reading the sample data from a public access blob storage. If you have your own data sources then you want to assign the required roles to the UAI. To learn more about access control, see access control document in the docs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683418605920
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "grant-rbac-to-uai",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# This utility function is created for ease of use in the docs tutorials. It uses standard azure API's. You can optionally inspect it `featurestore/setup/setup_storage_uai.py`\n",
- "grant_rbac_permissions(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " uai_principal_id,\n",
- " storage_subscription_id=storage_subscription_id,\n",
- " storage_resource_group_name=storage_resource_group_name,\n",
- " storage_account_name=storage_account_name,\n",
- " featurestore_subscription_id=featurestore_subscription_id,\n",
- " featurestore_resource_group_name=featurestore_resource_group_name,\n",
- " featurestore_name=featurestore_name,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "#### Grant your user account \"Blob data reader\" role on the offline store\n",
- "If feature data is materialized, then you need this role to read feature data from offline materialization store.\n",
- "\n",
- "Get your AAD object id from Azure portal following this instruction: https://learn.microsoft.com/en-us/partner-center/find-ids-and-domain-names#find-the-user-object-id\n",
- "\n",
- "To learn more about access control, see access control document in the docs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683126235070
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "grant-rbac-to-user-identity",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# This utility function is created for ease of use in the docs tutorials. It uses standard azure API's. You can optionally inspect it `featurestore/setup/setup_storage_uai.py`\n",
- "your_aad_objectid = \"\"\n",
- "\n",
- "grant_user_aad_storage_data_reader_role(\n",
- " AzureMLOnBehalfOfCredential(),\n",
- " your_aad_objectid,\n",
- " storage_subscription_id,\n",
- " storage_resource_group_name,\n",
- " storage_account_name,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 1: Enable offline store on the feature store by attaching offline materialization store and UAI"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683421376728
- },
- "jupyter": {
- "outputs_hidden": true,
- "source_hidden": false
- },
- "name": "enable-offline-store",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from azure.ai.ml.entities import (\n",
- " ManagedIdentityConfiguration,\n",
- " FeatureStore,\n",
- " MaterializationStore,\n",
- ")\n",
- "\n",
- "offline_store = MaterializationStore(\n",
- " type=\"azure_data_lake_gen2\",\n",
- " target=gen2_container_arm_id,\n",
- ")\n",
- "\n",
- "materialization_identity1 = ManagedIdentityConfiguration(\n",
- " client_id=uai_client_id, principal_id=uai_principal_id, resource_id=uai_arm_id\n",
- ")\n",
- "\n",
- "fs = FeatureStore(\n",
- " name=featurestore_name,\n",
- " offline_store=offline_store,\n",
- " materialization_identity=materialization_identity1,\n",
- ")\n",
- "\n",
- "fs_poller = fs_client.feature_stores.begin_update(fs, update_dependent_resources=True)\n",
- "\n",
- "print(fs_poller.result())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 2: Enable offline materialization on transactions featureset\n",
- "Once materialization is enabled on a featureset, you can perform backfill (this tutorial) or schedule recurrent materialization jobs(next part of the tutorial)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422398454
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "enable-offline-mat-txns-fset",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from azure.ai.ml.entities import (\n",
- " MaterializationSettings,\n",
- " MaterializationComputeResource,\n",
- ")\n",
- "\n",
- "transactions_fset_config = fs_client._featuresets.get(name=\"transactions\", version=\"1\")\n",
- "\n",
- "transactions_fset_config.materialization_settings = MaterializationSettings(\n",
- " offline_enabled=True,\n",
- " resource=MaterializationComputeResource(instance_type=\"standard_e8s_v3\"),\n",
- " spark_configuration={\n",
- " \"spark.driver.cores\": 4,\n",
- " \"spark.driver.memory\": \"36g\",\n",
- " \"spark.executor.cores\": 4,\n",
- " \"spark.executor.memory\": \"36g\",\n",
- " \"spark.executor.instances\": 2,\n",
- " },\n",
- " schedule=None,\n",
- ")\n",
- "\n",
- "fs_poller = fs_client.feature_sets.begin_create_or_update(transactions_fset_config)\n",
- "print(fs_poller.result())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "Optionally, you can save the the above feature set asset as yaml"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422407359
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "dump-txn-fset-yaml",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "## uncomment to run\n",
- "# transactions_fset_config.dump(root_dir + \"/featurestore/featuresets/transactions/featureset_asset_offline_enabled.yaml\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Step 3: Backfill data for transactions featureset\n",
- "As explained in the beginning of this tutorial, materialization is the process of computing the feature values for a given feature window and storing this in an materialization store. Materializing the features will increase its reliability and availability. All feature queries will now use the values from the materialization store. In this step you perform a one-time backfill for a feature window of __three months__.\n",
- "\n",
- "#### Note\n",
- "How to determine the window of backfill data needed? It has to match with the window of your training data. For e.g. if you want to train with two years of data, then you will want to be able to retrieve features for the same window, so you will backfill for a two year window."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422440549
- },
- "jupyter": {
- "outputs_hidden": true,
- "source_hidden": false
- },
- "name": "backfill-txns-fset",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "from datetime import datetime\n",
- "\n",
- "st = datetime(2023, 1, 1, 0, 0, 0, 0)\n",
- "ed = datetime(2023, 4, 1, 0, 0, 0, 0)\n",
- "\n",
- "poller = fs_client.feature_sets.begin_backfill(\n",
- " name=\"transactions\",\n",
- " version=\"1\",\n",
- " feature_window_start_time=st,\n",
- " feature_window_end_time=ed,\n",
- ")\n",
- "print(poller.result().job_id)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "# get the job URL, and stream the job logs\n",
- "fs_client.jobs.stream(poller.result().job_id)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "Lets print sample data from the featureset. You can notice from the output information that the data was retrieved from the materilization store. `get_offline_features()` method that is used to retrieve training/inference data will also use the materialization store by default ."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "gather": {
- "logged": 1683422850258
- },
- "jupyter": {
- "outputs_hidden": false,
- "source_hidden": false
- },
- "name": "sample-txns-fset-data",
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "outputs": [],
- "source": [
- "# look up the featureset by providing name and version\n",
- "transactions_featureset = featurestore.feature_sets.get(\"transactions\", \"1\")\n",
- "display(transactions_featureset.to_spark_dataframe().head(5))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Cleanup\n",
- "Part 4 of the tutorial has instructions for deleting the resources"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "nteract": {
- "transient": {
- "deleting": false
- }
- }
- },
- "source": [
- "## Next steps\n",
- "* Part 3 of tutorial: Experiment and train models using features"
- ]
- }
- ],
- "metadata": {
- "celltoolbar": "Edit Metadata",
- "kernel_info": {
- "name": "synapse_pyspark"
- },
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.7.13"
- },
- "microsoft": {
- "host": {
- "AzureML": {
- "notebookHasBeenCompleted": true
- }
- },
- "ms_spell_check": {
- "ms_spell_check_language": "en"
- }
- },
- "nteract": {
- "version": "nteract-front-end@1.0.0"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/sdk/python/featurestore_sample/notebooks/sdk_only/images/offline-store-information.png b/sdk/python/featurestore_sample/notebooks/sdk_only/images/offline-store-information.png
new file mode 100644
index 0000000000..3d0e0010d9
Binary files /dev/null and b/sdk/python/featurestore_sample/notebooks/sdk_only/images/offline-store-information.png differ
diff --git a/sdk/python/generative-ai/rag/README.md b/sdk/python/generative-ai/rag/README.md
index ce5f12b683..922eb344e6 100644
--- a/sdk/python/generative-ai/rag/README.md
+++ b/sdk/python/generative-ai/rag/README.md
@@ -11,6 +11,7 @@ For more documentation related to RAG refer the AzureML documentation [here](htt
| Category | Article |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Notebooks | [Process Git Repo into Azure Cognitive Search with Embeddings](./notebooks/azure_cognitive_search/acs_mlindex_with_langchain.ipynb) |
+| Notebooks | [Process Git Repo into Pinecone with Embeddings](./notebooks/pinecone/pinecone_mlindex_with_langchain.ipynb) |
| Notebooks | [Process private Git Repo into FAISS Embeddings Index](./notebooks/faiss/faiss_mlindex_with_langchain.ipynb) |
| Notebooks | [QA Test Generation](./notebooks/qa_data_generation.ipynb) |
| Notebooks | [Productionize Vector Index with Test Data Generation, Auto Prompt, Evaluations and Prompt Flow](./notebooks/mlindex_with_testgen.ipynb) |
diff --git a/sdk/python/generative-ai/rag/media/pinecone_connection_ui.png b/sdk/python/generative-ai/rag/media/pinecone_connection_ui.png
new file mode 100644
index 0000000000..7b94c4cbb6
Binary files /dev/null and b/sdk/python/generative-ai/rag/media/pinecone_connection_ui.png differ
diff --git a/sdk/python/generative-ai/rag/notebooks/pinecone/pinecone_mlindex_with_langchain.ipynb b/sdk/python/generative-ai/rag/notebooks/pinecone/pinecone_mlindex_with_langchain.ipynb
new file mode 100644
index 0000000000..4fcec5a5c0
--- /dev/null
+++ b/sdk/python/generative-ai/rag/notebooks/pinecone/pinecone_mlindex_with_langchain.ipynb
@@ -0,0 +1,1043 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%pip install azure-ai-ml\n",
+ "\n",
+ "# To use the latest version of langchain, please uncomment the line right below\n",
+ "# and remove the `langchain` extra. Otherwise, the `langchain` extra will be used instead.\n",
+ "# %pip install langchain\n",
+ "%pip install -U 'azureml-rag[pinecone,langchain]>=0.2.11'\n",
+ "\n",
+ "# If using hugging_face embeddings add `hugging_face` extra, e.g. `azureml-rag[pinecone,hugging_face]`"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create a Pinecone-based Vector Index for Document Retrieval with AzureML\n",
+ "\n",
+ "We'll walk through setting up an AzureML Pipeline that uploads the `retrieval-augmented-generation` directory from this repository, processes the data into chunks, embeds the chunks, and creates a LangChain-compatible Pinecone MLIndex. Furthermore, we will demonstrate how to source data from a public git repository instead and setup a Pipeline Schedule for continuous indexing.\n",
+ "\n",
+ "Note: Support for [namespaces](https://docs.pinecone.io/docs/namespaces) is currently not available. We will be actively working on it and are excited to bring this feature to you!"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Get client for AzureML Workspace\n",
+ "\n",
+ "The workspace is the top-level resource for Azure Machine Learning, providing a centralized place to work with all the artifacts you create when you use Azure Machine Learning. In this section we will connect to the workspace in which the job will be run.\n",
+ "\n",
+ "If you don't have a Workspace and want to create and Index locally see [here to create one](https://learn.microsoft.com/azure/machine-learning/quickstart-create-resources?view=azureml-api-2).\n",
+ "\n",
+ "Enter your Workspace details below, running this still will write a `workspace.json` file to the current folder."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%writefile workspace.json\n",
+ "{\n",
+ " \"subscription_id\": \"\",\n",
+ " \"resource_group\": \"\",\n",
+ " \"workspace_name\": \"\"\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`MLClient` is how you interact with AzureML"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential\n",
+ "from azure.ai.ml import MLClient\n",
+ "from azureml.core import Workspace\n",
+ "\n",
+ "try:\n",
+ " credential = DefaultAzureCredential()\n",
+ " # Check if given credential can get token successfully.\n",
+ " credential.get_token(\"https://management.azure.com/.default\")\n",
+ "except Exception as ex:\n",
+ " # Fall back to InteractiveBrowserCredential in case DefaultAzureCredential not work\n",
+ " credential = InteractiveBrowserCredential()\n",
+ "\n",
+ "try:\n",
+ " ml_client = MLClient.from_config(credential=credential, path=\"workspace.json\")\n",
+ "except Exception as ex:\n",
+ " raise Exception(\n",
+ " \"Failed to create MLClient from config file. Please modify and then run the above cell with your AzureML Workspace details.\"\n",
+ " ) from ex\n",
+ " # ml_client = MLClient(\n",
+ " # credential=credential,\n",
+ " # subscription_id=\"\",\n",
+ " # resource_group_name=\"\",\n",
+ " # workspace_name=\"\"\n",
+ " # )\n",
+ "\n",
+ "ws = Workspace(\n",
+ " subscription_id=ml_client.subscription_id,\n",
+ " resource_group=ml_client.resource_group_name,\n",
+ " workspace_name=ml_client.workspace_name,\n",
+ ")\n",
+ "\n",
+ "print(ml_client)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Which Embeddings Model to use?\n",
+ "\n",
+ "There are currently two supported Embedding options: OpenAI's `text-embedding-ada-002` embedding model or HuggingFace embedding models. Here are some factors that might influence your decision:\n",
+ "\n",
+ "### OpenAI\n",
+ "\n",
+ "OpenAI has [great documentation](https://platform.openai.com/docs/guides/embeddings) on their Embeddings model `text-embedding-ada-002`, it can handle up to 8191 tokens and can be accessed using [Azure OpenAI](https://learn.microsoft.com/azure/cognitive-services/openai/concepts/models#embeddings-models) or OpenAI directly.\n",
+ "If you have an existing Azure OpenAI Instance you can connect it to AzureML, if you don't AzureML provisions a default one for you called `Default_AzureOpenAI`.\n",
+ "The main limitation when using `text-embedding-ada-002` is cost/quota available for the model. Otherwise it provides high quality embeddings across a wide array of text domains while being simple to use.\n",
+ "\n",
+ "### HuggingFace\n",
+ "\n",
+ "HuggingFace hosts many different models capable of embedding text into single-dimensional vectors. The [MTEB Leaderboard](https://huggingface.co/spaces/mteb/leaderboard) ranks the performance of embeddings models on a few axis, not all models ranked can be run locally (e.g. `text-embedding-ada-002` is on the list), though many can and there is a range of larger and smaller models. When embedding with HuggingFace the model is loaded locally for inference, this will potentially impact your choice of compute resources.\n",
+ "\n",
+ "**NOTE:** The default PromptFlow Runtime does not come with HuggingFace model dependencies installed, Indexes created using HuggingFace embeddings will not work in PromptFlow by default. **Pick OpenAI if you want to use PromptFlow**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Run the cells under _either_ heading (OpenAI or HuggingFace) to use the respective embedding model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### OpenAI\n",
+ "\n",
+ "We can use the automatically created `Default_AzureOpenAI` connection.\n",
+ "\n",
+ "If you would rather use an existing Azure OpenAI connection then change `aoai_connection_name` below.\n",
+ "If you would rather use an existing Azure OpenAI resource, but don't have a connection created, modify `aoai_connection_name` and the details under the `# Create New Connection` code comment, or navigate to the PromptFlow section in your AzureML Workspace and use the Connections create UI flow."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "aoai_connection_name = \"Default_AzureOpenAI\"\n",
+ "aoai_connection_id = None"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here we get the connection from the Workspace and save its `id` so we can reference it later."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.utils.connections import (\n",
+ " get_connection_by_name_v2,\n",
+ " create_connection_v2,\n",
+ ")\n",
+ "\n",
+ "try:\n",
+ " aoai_connection = get_connection_by_name_v2(ws, aoai_connection_name)\n",
+ "except Exception as ex:\n",
+ " # Create New Connection\n",
+ " # Modify the details below to match the `Endpoint` and API key of your AOAI resource, these details can be found in Azure Portal\n",
+ " raise RuntimeError(\n",
+ " \"Have you entered your AOAI resource details below? If so, delete me!\"\n",
+ " )\n",
+ " aoai_connection = create_connection_v2(\n",
+ " workspace=ws,\n",
+ " name=aoai_connection,\n",
+ " category=\"AzureOpenAI\",\n",
+ " # 'Endpoint' from Azure OpenAI resource overview\n",
+ " target=\"https://.openai.azure.com/\",\n",
+ " auth_type=\"ApiKey\",\n",
+ " credentials={\n",
+ " # Either `Key` from the `Keys and Endpoint` tab of your Azure OpenAI resource, will be stored in your Workspace associated Azure Key Vault.\n",
+ " \"key\": \"\"\n",
+ " },\n",
+ " metadata={\"ApiType\": \"azure\", \"ApiVersion\": \"2023-05-15\"},\n",
+ " )\n",
+ "\n",
+ "aoai_connection_id = aoai_connection[\"id\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that your Workspace has a connection to Azure OpenAI we will make sure the `text-embedding-ada-002` model has been deployed ready for inference. This cell will fail if there is not deployment for the embeddings model, [follow these instructions](https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#deploy-a-model) to deploy a model with Azure OpenAI."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.utils.deployment import infer_deployment\n",
+ "\n",
+ "aoai_embedding_model_name = \"text-embedding-ada-002\"\n",
+ "try:\n",
+ " aoai_embedding_deployment_name = infer_deployment(\n",
+ " aoai_connection, aoai_embedding_model_name\n",
+ " )\n",
+ " print(\n",
+ " f\"Deployment name in AOAI workspace for model '{aoai_embedding_model_name}' is '{aoai_embedding_deployment_name}'\"\n",
+ " )\n",
+ "except Exception as e:\n",
+ " print(\n",
+ " f\"Deployment name in AOAI workspace for model '{aoai_embedding_model_name}' is not found.\"\n",
+ " )\n",
+ " print(\n",
+ " f\"Please create a deployment for this model by following the deploy instructions on the resource page for '{aoai_connection['properties']['target']}' in Azure Portal.\"\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally we will combine the deployment and model information into a uri form which the AzureML embeddings components expect as input."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "embeddings_model_uri = f\"azure_open_ai://deployment/{aoai_embedding_deployment_name}/model/{aoai_embedding_model_name}\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### HuggingFace\n",
+ "\n",
+ "AzureML's default model from HuggingFace is `all-mpnet-base-v2`, it can be run by most laptops. Any `sentence-transformer` models should be supported, you can learn more about `sentence-transformers` [here](https://huggingface.co/sentence-transformers)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "embeddings_model_uri = \"hugging_face://model/sentence-transformers/all-mpnet-base-v2\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Prepare Pinecone Index\n",
+ "\n",
+ "If you would rather use an existing Pinecone index from a Pinecone project connection then change `pinecone_connection_name` below.\n",
+ "If you would rather use an existing Pinecone index, but don't have a connection to the Pinecone project created, modify `pinecone_connection_name` and the details under the `# Create New Connection` code comment.\n",
+ "\n",
+ "Note: Only Custom Connections are supported right now for connecting to a Pinecone project (where your index lives).\n",
+ "\n",
+ "If creating the connection through the Create Connections UI flow, then please specify the Pinecone project\n",
+ "- API key (must use `api_key` as the key for the key-value pair)\n",
+ "- ID (must use `project_id` as the key for the key-value pair)\n",
+ "- environment (must use `environment` as the key for the key-value pair)\n",
+ "\n",
+ "as key-value pairs in the Custom Connection where the API key key-value pair must be a secret. For example:\n",
+ "\n",
+ "![Create Pinecone Connection UI](../../media/pinecone_connection_ui.png)\n",
+ "\n",
+ "\n",
+ "You can also modify the details under the `# Create New Connection` code comment to create a connection via the SDK!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pinecone_connection_name = \"my_pinecone_project_connection\"\n",
+ "pinecone_connection_id = None"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.utils.connections import (\n",
+ " get_connection_by_name_v2,\n",
+ " create_connection_v2,\n",
+ ")\n",
+ "\n",
+ "try:\n",
+ " pinecone_connection = get_connection_by_name_v2(ws, pinecone_connection_name)\n",
+ "except Exception as ex:\n",
+ " # Create New Connection\n",
+ " # Modify the details below to match the details of the Pinecone project where your index lives, more details here: https://docs.pinecone.io/docs/projects\n",
+ " raise RuntimeError(\n",
+ " \"Have you entered your Pinecone project details below? If so, delete me!\"\n",
+ " )\n",
+ " pinecone_connection = create_connection_v2(\n",
+ " workspace=ws,\n",
+ " name=pinecone_connection_name,\n",
+ " category=\"CustomKeys\",\n",
+ " target=\"_\",\n",
+ " auth_type=\"CustomKeys\",\n",
+ " credentials={\n",
+ " \"keys\": {\n",
+ " # https://docs.pinecone.io/docs/projects#api-keys\n",
+ " \"api_key\": \"\"\n",
+ " }\n",
+ " },\n",
+ " metadata={\n",
+ " # https://docs.pinecone.io/docs/projects#project-environment\n",
+ " \"environment\": \"\",\n",
+ " # https://docs.pinecone.io/docs/projects#project-id\n",
+ " \"project_id\": \"\",\n",
+ " },\n",
+ " )\n",
+ "\n",
+ "pinecone_connection_id = pinecone_connection[\"id\"]"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setup Pipeline to process data into Index\n",
+ "\n",
+ "AzureML [Pipelines](https://learn.microsoft.com/azure/machine-learning/concept-ml-pipelines?view=azureml-api-2) connect together multiple [Components](https://learn.microsoft.com/azure/machine-learning/concept-component?view=azureml-api-2). Each Component defines inputs, code that consumes the inputs and outputs produced from the code. Pipelines themselves can have inputs, and outputs produced by connecting together individual sub Components.\n",
+ "To process your data for embedding and indexing we will chain together multiple components each performing their own step of the workflow.\n",
+ "\n",
+ "The Components are published to a [Registry](https://learn.microsoft.com/azure/machine-learning/how-to-manage-registries?view=azureml-api-2&tabs=cli), `azureml`, which should have access to by default, it can be accessed from any Workspace.\n",
+ "In the below cell we get the Component Definitions from the `azureml` registry."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ml_registry = MLClient(credential=credential, registry_name=\"azureml\")\n",
+ "\n",
+ "# Clones git repository to output folder of pipeline, by default this will be on the default Workspace Datastore `workspaceblobstore`\n",
+ "git_clone_component = ml_registry.components.get(\"llm_rag_git_clone\", label=\"latest\")\n",
+ "# Walks input folder according to provided glob pattern (all files by default: '**/*') and attempts to open them, extract text chunks and further chunk if necessary to fir within provided `chunk_size`.\n",
+ "crack_and_chunk_component = ml_registry.components.get(\n",
+ " \"llm_rag_crack_and_chunk\", label=\"latest\"\n",
+ ")\n",
+ "# Reads input folder of files containing chunks and their metadata as batches, in parallel, and generates embeddings for each chunk. Output format is produced and loaded by `azureml.rag.embeddings.EmbeddingContainer`.\n",
+ "generate_embeddings_component = ml_registry.components.get(\n",
+ " \"llm_rag_generate_embeddings\", label=\"latest\"\n",
+ ")\n",
+ "# Reads an input folder produced by `azureml.rag.embeddings.EmbeddingsContainer.save()` and pushes all embeddings (including metadata) into a Pinecone index. Writes an MLIndex yaml detailing the index and embeddings model information.\n",
+ "update_pinecone_index_component = ml_registry.components.get(\n",
+ " \"llm_rag_update_pinecone_index\", label=\"latest\"\n",
+ ")\n",
+ "# Takes a uri to a storage location where an MLIndex yaml is stored and registers it as an MLIndex Data asset in the AzureML Workspace.\n",
+ "register_mlindex_component = ml_registry.components.get(\n",
+ " \"llm_rag_register_mlindex_asset\", label=\"latest\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Each Component has documentation which provides an overall description of the Components purpose and each of the inputs/outputs.\n",
+ "For example we can see understand what `crack_and_chunk` does by inspecting the Component definition."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(crack_and_chunk_component)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Below a Pipeline is built by defining a python function which chains together the above components inputs and outputs. Arguments to the function are inputs to the Pipeline itself and the return value is a dictionary defining the outputs of the Pipeline."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import Input, Output\n",
+ "from azure.ai.ml.dsl import pipeline\n",
+ "from azure.ai.ml.entities._job.pipeline._io import PipelineInput\n",
+ "from typing import Optional\n",
+ "\n",
+ "\n",
+ "def use_automatic_compute(component, instance_count=1, instance_type=\"Standard_E8s_v3\"):\n",
+ " \"\"\"Configure input `component` to use automatic compute with `instance_count` and `instance_type`.\n",
+ "\n",
+ " This avoids the need to provision a compute cluster to run the component.\n",
+ " \"\"\"\n",
+ " component.set_resources(\n",
+ " instance_count=instance_count,\n",
+ " instance_type=instance_type,\n",
+ " properties={\"compute_specification\": {\"automatic\": True}},\n",
+ " )\n",
+ " return component\n",
+ "\n",
+ "\n",
+ "def optional_pipeline_input_provided(input: Optional[PipelineInput]):\n",
+ " \"\"\"Checks if optional pipeline inputs are provided.\"\"\"\n",
+ " return input is not None and input._data is not None\n",
+ "\n",
+ "\n",
+ "# If you have an existing compute cluster you want to use instead of automatic compute, uncomment the following line, replace `dedicated_cpu_compute` with the name of your cluster.\n",
+ "# Also comment out the `component.set_resources` line in `use_automatic_compute` above and the `default_compute='serverless'` line below.\n",
+ "\n",
+ "\n",
+ "# @pipeline(compute=dedicated_cpu_compute)\n",
+ "@pipeline(default_compute=\"serverless\")\n",
+ "def uri_into_pinecone(\n",
+ " input_data: Input,\n",
+ " embeddings_model: str,\n",
+ " pinecone_config: str,\n",
+ " pinecone_connection_id: str,\n",
+ " asset_name: str,\n",
+ " chunk_size: int = 1024,\n",
+ " data_source_glob: str = None,\n",
+ " data_source_url: str = None,\n",
+ " document_path_replacement_regex: str = None,\n",
+ " aoai_connection_id: str = None,\n",
+ " embeddings_container: Input = None,\n",
+ "):\n",
+ " \"\"\"Pipeline to generate embeddings for a `input_data` source and push them into a Pinecone index.\"\"\"\n",
+ "\n",
+ " crack_and_chunk = crack_and_chunk_component(\n",
+ " input_data=input_data,\n",
+ " input_glob=data_source_glob,\n",
+ " chunk_size=chunk_size,\n",
+ " data_source_url=data_source_url,\n",
+ " document_path_replacement_regex=document_path_replacement_regex,\n",
+ " )\n",
+ " use_automatic_compute(crack_and_chunk)\n",
+ "\n",
+ " generate_embeddings = generate_embeddings_component(\n",
+ " chunks_source=crack_and_chunk.outputs.output_chunks,\n",
+ " embeddings_container=embeddings_container,\n",
+ " embeddings_model=embeddings_model,\n",
+ " )\n",
+ " use_automatic_compute(generate_embeddings)\n",
+ " if optional_pipeline_input_provided(aoai_connection_id):\n",
+ " generate_embeddings.environment_variables[\n",
+ " \"AZUREML_WORKSPACE_CONNECTION_ID_AOAI\"\n",
+ " ] = aoai_connection_id\n",
+ " if optional_pipeline_input_provided(embeddings_container):\n",
+ " # If provided, `embeddings_container` is expected to be a URI to folder, the folder can be empty.\n",
+ " # Each sub-folder is generated by a `create_embeddings_component` run and can be reused for subsequent embeddings runs.\n",
+ " generate_embeddings.outputs.embeddings = Output(\n",
+ " type=\"uri_folder\", path=f\"{embeddings_container.path}/{{name}}\"\n",
+ " )\n",
+ "\n",
+ " # `update_pinecone_index` takes the Embedded data produced by `generate_embeddings` and pushes it into a Pinecone index.\n",
+ " update_pinecone_index = update_pinecone_index_component(\n",
+ " embeddings=generate_embeddings.outputs.embeddings,\n",
+ " pinecone_config=pinecone_config,\n",
+ " )\n",
+ " use_automatic_compute(update_pinecone_index)\n",
+ " if optional_pipeline_input_provided(pinecone_connection_id):\n",
+ " update_pinecone_index.environment_variables[\n",
+ " \"AZUREML_WORKSPACE_CONNECTION_ID_PINECONE\"\n",
+ " ] = pinecone_connection_id\n",
+ "\n",
+ " register_mlindex = register_mlindex_component(\n",
+ " storage_uri=update_pinecone_index.outputs.index,\n",
+ " asset_name=asset_name,\n",
+ " )\n",
+ " use_automatic_compute(register_mlindex)\n",
+ " return {\n",
+ " \"mlindex_asset_uri\": update_pinecone_index.outputs.index,\n",
+ " \"mlindex_asset_id\": register_mlindex.outputs.asset_id,\n",
+ " }"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can learn about the URIs AzureML will accept as data inputs [here](https://learn.microsoft.com/azure/machine-learning/how-to-read-write-data-v2?view=azureml-api-2&tabs=python#paths). Referencing a path on AzureML supported storages (Blob, ADLSgen2, ADLSgen1, Fileshare) works best using [Datastores](https://learn.microsoft.com/azure/machine-learning/how-to-datastore?view=azureml-api-2&tabs=cli-identity-based-access%2Ccli-adls-identity-based-access%2Ccli-azfiles-account-key%2Ccli-adlsgen1-identity-based-access) as they help manage credentials for access.\n",
+ "\n",
+ "You can also reference a local path on your machine and AzureML will upload it to your Workspace default Datastore, usually `workspaceblobstore`, which is what the below cell does. If you have existing Data in a location supported by AzureML (as detailed in the above linked documentation) you can replace the local path below with your own uri, you will also want to update the `data_source_url`, if you're not sure what to put there it can be left blank to start with and the relative path of each file in the data source will be set as the source url in metadata."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# This will upload the `azureml-examples/sdk/python/generative-ai` folder.\n",
+ "input_uri = \"../../../\"\n",
+ "# This url is used as the base_url to combine with paths to processed files, the combined url will be added to each embedded documents metadata.\n",
+ "data_source_url = (\n",
+ " \"https://github.com/Azure/azureml-examples/blob/main/sdk/python/generative-ai/\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `update_pinecone_index` component takes a `pinecone_config` argument which specifies the name of the Index to push chunked and embedded data to. If this index does not exist it will be created, if it does exists it will be reused.\n",
+ "\n",
+ "**Note: if you are using Pinecone's Starter Plan, you are allowed to have only 1 index. In which case, make sure you have deleted any existing index if you are not looking to reuse.** More details on the Starter Plan here: https://docs.pinecone.io/docs/starter-environment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pinecone_config = {\"index_name\": \"pinecone-notebook-local-files-index\"}\n",
+ "\n",
+ "print(update_pinecone_index_component.description)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can create the Pipeline Job by calling the `@pipeline` annotated function and providing input arguments.\n",
+ "`asset_name` will be used when registering the MLIndex Data Asset produced by the `register_mlindex` component in the pipeline. This is how you can refer to the MLIndex within AzureML."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "asset_name = \"azureml_rag_aoai_pinecone_mlindex\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import Input\n",
+ "import json\n",
+ "\n",
+ "pipeline_job = uri_into_pinecone(\n",
+ " input_data=Input(type=\"uri_folder\", path=input_uri),\n",
+ " data_source_url=data_source_url,\n",
+ " pinecone_config=json.dumps(pinecone_config),\n",
+ " pinecone_connection_id=pinecone_connection_id,\n",
+ " # Each run will save latest Embeddings to subfolder under this path, runs will load latest embeddings from container and reuse any unchanged chunk embeddings.\n",
+ " embeddings_container=Input(\n",
+ " type=\"uri_folder\",\n",
+ " path=f\"azureml://datastores/workspaceblobstore/paths/embeddings/{asset_name}\",\n",
+ " ),\n",
+ " embeddings_model=embeddings_model_uri,\n",
+ " # This should be None if using a HuggingFace embeddings model.\n",
+ " aoai_connection_id=aoai_connection_id,\n",
+ " # Name of asset to register MLIndex under\n",
+ " asset_name=asset_name,\n",
+ ")\n",
+ "\n",
+ "# By default AzureML Pipelines will reuse the output of previous component Runs when inputs have not changed.\n",
+ "# If you want to rerun the Pipeline every time each time so that any changes to upstream data sources are processed uncomment the below line.\n",
+ "\n",
+ "# pipeline_job.settings.force_rerun = True"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally we add some properties to `pipeline_job` which ensure the Index generation progress and final Artifact appear in the PromptFlow Vector Index UI."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pipeline_job.properties[\"azureml.mlIndexAssetName\"] = asset_name\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetKind\"] = \"pinecone\"\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetSource\"] = \"Local Data\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Submit Pipeline\n",
+ "\n",
+ "**In case of any errors see [TROUBLESHOOT.md](../../TROUBLESHOOT.md).**\n",
+ "\n",
+ "The output of each step in the pipeline can be inspected via the Workspace UI, click the link under 'Details Page' after running the below cell. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "running_pipeline_job = ml_client.jobs.create_or_update(\n",
+ " pipeline_job, experiment_name=\"uri_to_pinecone\"\n",
+ ")\n",
+ "running_pipeline_job"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ml_client.jobs.stream(running_pipeline_job.name)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Use Index with langchain\n",
+ "\n",
+ "The Data Asset produced by the AzureML Pipeline above contains a yaml file named 'MLIndex' which contains all the information needed to use the Pinecone index.\n",
+ "For instance if an AOAI deployment was used to embed the documents the details of that deployment and a reference to the secret are there.\n",
+ "This allows easy loading of the MLIndex into a langchain retriever."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.mlindex import MLIndex\n",
+ "\n",
+ "question = \"What is RAG?\"\n",
+ "\n",
+ "retriever = MLIndex(\n",
+ " ml_client.data.get(asset_name, label=\"latest\")\n",
+ ").as_langchain_retriever()\n",
+ "retriever.get_relevant_documents(question)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you have not deployed `gpt-35-turbo` on your Azure OpenAI resource the below cell will fail indicated the `API deployment for this resource does not exist`. Follow the previous instructions for deploying `text-embedding-ada-002` to deploy `gpt-35-turbo`, note the chosen deployment name below and use the same or update it if you choose different one."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from langchain.chains import RetrievalQA\n",
+ "from azureml.rag.models import init_llm, parse_model_uri\n",
+ "\n",
+ "model_config = parse_model_uri(\n",
+ " \"azure_open_ai://deployment/gpt-35-turbo/model/gpt-35-turbo\"\n",
+ ")\n",
+ "model_config[\"api_base\"] = aoai_connection[\"properties\"][\"target\"]\n",
+ "model_config[\"key\"] = aoai_connection[\"properties\"][\"credentials\"][\"key\"]\n",
+ "model_config[\"temperature\"] = 0.3\n",
+ "model_config[\"max_retries\"] = 3\n",
+ "\n",
+ "qa = RetrievalQA.from_chain_type(\n",
+ " llm=init_llm(model_config), chain_type=\"stuff\", retriever=retriever\n",
+ ")\n",
+ "\n",
+ "qa.run(question)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Note: To control the number of documents returned when searching try getting the the MLIndex `as_langchain_vectorstore()` instead, this implements the `VectorStore` interface which has more parameters."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Use MLIndex with PromptFlow\n",
+ "\n",
+ "To use the MLindex in PromptFlow the asset_id can be used with the `Vector Index Lookup` Tool. Replace `versions/2` with `versions/latest` to use the latest version."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "asset_id = f\"azureml:/{ml_client.data.get(asset_name, label='latest').id}\"\n",
+ "asset_id.replace(\"resourceGroups\", \"resourcegroups\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Source Data from a Git Repo\n",
+ "\n",
+ "If you want to use a git repo as a data source you can wrap the `uri_to_pinecone` pipeline in a new one which also performs a git_clone."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@pipeline(default_compute=\"serverless\")\n",
+ "def git_to_pinecone(\n",
+ " git_url,\n",
+ " embeddings_model,\n",
+ " pinecone_config,\n",
+ " pinecone_connection_id,\n",
+ " asset_name,\n",
+ " chunk_size=1024,\n",
+ " data_source_glob=None,\n",
+ " data_source_url=None,\n",
+ " document_path_replacement_regex=None,\n",
+ " branch_name=None,\n",
+ " git_connection_id=None,\n",
+ " aoai_connection_id=None,\n",
+ " embeddings_container=None,\n",
+ "):\n",
+ " git_clone = git_clone_component(git_repository=git_url, branch_name=branch_name)\n",
+ " use_automatic_compute(git_clone)\n",
+ " if optional_pipeline_input_provided(git_connection_id):\n",
+ " git_clone.environment_variables[\n",
+ " \"AZUREML_WORKSPACE_CONNECTION_ID_GIT\"\n",
+ " ] = git_connection_id\n",
+ "\n",
+ " return uri_into_pinecone(\n",
+ " git_clone.outputs.output_data,\n",
+ " embeddings_model,\n",
+ " pinecone_config,\n",
+ " pinecone_connection_id,\n",
+ " asset_name,\n",
+ " chunk_size,\n",
+ " data_source_glob,\n",
+ " data_source_url,\n",
+ " document_path_replacement_regex,\n",
+ " aoai_connection_id,\n",
+ " embeddings_container,\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The settings below show how the different git and data_source parameters can be set to process only the AzureML documentation from the larger AzureDocs git repo, and ensure the source url for each document is processed to link to the publicly hosted URL instead of the git url."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "git_url = \"https://github.com/MicrosoftDocs/azure-docs/\"\n",
+ "data_source_glob = \"articles/machine-learning/**/*\"\n",
+ "data_source_url = \"https://learn.microsoft.com/en-us/azure\"\n",
+ "# This regex is used to remove the 'articles' folder from the source url and removes the file extension.\n",
+ "document_path_replacement_regex = r'{\"match_pattern\": \"(.*)/articles/(.*)(\\\\.[^.]+)$\", \"replacement_pattern\": \"\\\\1/\\\\2\"}'\n",
+ "asset_name = \"azure_docs_ml_aoai_pinecone_mlindex\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Since the data being indexed is changing, the target index in Pinecone should also be updated.\n",
+ "\n",
+ "**Note: if you are using Pinecone's Starter Plan, you are allowed to have only 1 index. In which case, make sure you have deleted any existing index before moving on.** More details on the Starter Plan here: https://docs.pinecone.io/docs/starter-environment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pinecone_config = {\n",
+ " \"index_name\": \"azure-docs-machine-learning-aoai-embedding\",\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import Input\n",
+ "import json\n",
+ "\n",
+ "pipeline_job = git_to_pinecone(\n",
+ " git_url=git_url,\n",
+ " data_source_glob=data_source_glob,\n",
+ " data_source_url=data_source_url,\n",
+ " document_path_replacement_regex=document_path_replacement_regex,\n",
+ " pinecone_config=json.dumps(pinecone_config),\n",
+ " pinecone_connection_id=pinecone_connection_id,\n",
+ " # Each run will save latest Embeddings to subfolder under this path, runs will load latest embeddings from container and reuse any unchanged chunk embeddings\n",
+ " embeddings_container=Input(\n",
+ " type=\"uri_folder\",\n",
+ " path=f\"azureml://datastores/workspaceblobstore/paths/embeddings/{asset_name}\",\n",
+ " ),\n",
+ " embeddings_model=embeddings_model_uri,\n",
+ " aoai_connection_id=aoai_connection_id,\n",
+ " # Name of asset to register MLIndex under\n",
+ " asset_name=asset_name,\n",
+ ")\n",
+ "\n",
+ "# Rerun each time so that git_clone isn't cached, if intent is to ingest latest data.\n",
+ "# pipeline_job.settings.force_rerun = True"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# These are added so that in progress index generations can be listed in UI, this tagging is done automatically by UI.\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetName\"] = asset_name\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetKind\"] = \"pinecone\"\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetSource\"] = \"Git Repository\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "running_pipeline_job = ml_client.jobs.create_or_update(\n",
+ " pipeline_job, experiment_name=\"git_to_pinecone\"\n",
+ ")\n",
+ "running_pipeline_job"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ml_client.jobs.stream(running_pipeline_job.name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setup Pipeline to Run on Schedule\n",
+ "\n",
+ "It's possible to setup Pipelines to run on a regular schedule. Below we configure the AzureDocs pipeline to run once every day."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.constants import TimeZone\n",
+ "from azure.ai.ml.entities import JobSchedule, RecurrenceTrigger, RecurrencePattern\n",
+ "from datetime import datetime, timedelta\n",
+ "\n",
+ "\n",
+ "schedule_name = \"azure_docs_ml_aoai_pinecone_mlindex_daily\"\n",
+ "\n",
+ "# Make sure pipeline runs git_clone every trigger\n",
+ "pipeline_job.settings.force_rerun = True\n",
+ "\n",
+ "schedule_start_time = datetime.utcnow() + timedelta(minutes=1)\n",
+ "recurrence_trigger = RecurrenceTrigger(\n",
+ " frequency=\"day\",\n",
+ " interval=1,\n",
+ " # schedule=RecurrencePattern(hours=16, minutes=[15]),\n",
+ " start_time=schedule_start_time,\n",
+ " time_zone=TimeZone.UTC,\n",
+ ")\n",
+ "\n",
+ "job_schedule = JobSchedule(\n",
+ " name=schedule_name,\n",
+ " trigger=recurrence_trigger,\n",
+ " create_job=pipeline_job,\n",
+ " properties={\n",
+ " \"azureml.mlIndexAssetName\": asset_name,\n",
+ " \"azureml.mlIndexAssetKind\": \"pinecone\",\n",
+ " \"azureml.mlIndexAssetSource\": \"Git Repository\",\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Once created the schedule must be enabled.\n",
+ "\n",
+ "**Note:** To see Scheduled Pipelines in the AzureML Workspace UI you must navigate to the 'Jobs' page (beaker icon) AND have a flight enabled in your URL. Take your URL and modify it like so:\n",
+ "- before: https://ml.azure.com/experiments?wsid=/subscriptions/.../resourceGroups/.../providers/Microsoft.MachineLearningServices/workspaces/my_awesome_workspace\n",
+ "- after: https://ml.azure.com/experiments?wsid=/subscriptions/.../resourceGroups/.../providers/Microsoft.MachineLearningServices/workspaces/my_awesome_workspace&flight=schedules"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "job_schedule_res = ml_client.schedules.begin_create_or_update(\n",
+ " schedule=job_schedule\n",
+ ").result()\n",
+ "job_schedule_res"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The schedule can be disabled via the schedules UI or via the below code."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "job_schedule_res = ml_client.schedules.begin_disable(name=schedule_name).result()\n",
+ "job_schedule_res.is_enabled"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### [Optional] Provision Cluster\n",
+ "\n",
+ "You don't have to! The settings on the Pipeline use AzureML Serverless Compute, you can use any SKU you have quota for on demand. If you want to use a cluster that's also supported."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import AmlCompute\n",
+ "\n",
+ "cpu_compute_target = \"rag-cpu\"\n",
+ "\n",
+ "try:\n",
+ " dedicated_cpu_compute = ml_client.compute.get(cpu_compute_target)\n",
+ "except Exception:\n",
+ " # Let's create the Azure Machine Learning compute object with the intended parameters\n",
+ " dedicated_cpu_compute = AmlCompute(\n",
+ " name=cpu_compute_target,\n",
+ " type=\"amlcompute\",\n",
+ " size=\"Standard_E8s_v3\",\n",
+ " min_instances=0,\n",
+ " max_instances=2,\n",
+ " idle_time_before_scale_down=600,\n",
+ " tier=\"Dedicated\",\n",
+ " )\n",
+ "\n",
+ " dedicated_cpu_compute = ml_client.compute.begin_create_or_update(\n",
+ " dedicated_cpu_compute\n",
+ " ).result(timeout=600)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/generative-ai/rag/notebooks/pinecone/s3.yaml b/sdk/python/generative-ai/rag/notebooks/pinecone/s3.yaml
new file mode 100644
index 0000000000..a88d86759b
--- /dev/null
+++ b/sdk/python/generative-ai/rag/notebooks/pinecone/s3.yaml
@@ -0,0 +1,11 @@
+# my_s3_connection.yaml
+$schema: http://azureml/sdk-2-0/Connection.json
+
+type: s3
+name: s3_bucket
+
+target: my-bucket # add the s3 bucket details
+credentials:
+ type: access_key
+ access_key_id: # add access key id
+ secret_access_key: # add access key secret
diff --git a/sdk/python/generative-ai/rag/notebooks/pinecone/s3_to_pinecone_mlindex_with_langchain.ipynb b/sdk/python/generative-ai/rag/notebooks/pinecone/s3_to_pinecone_mlindex_with_langchain.ipynb
new file mode 100644
index 0000000000..6b286d81f4
--- /dev/null
+++ b/sdk/python/generative-ai/rag/notebooks/pinecone/s3_to_pinecone_mlindex_with_langchain.ipynb
@@ -0,0 +1,838 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%pip install azure-ai-ml\n",
+ "\n",
+ "# To use the latest version of langchain, please uncomment the line right below\n",
+ "# and remove the `langchain` extra. Otherwise, the `langchain` extra will be used instead.\n",
+ "# %pip install langchain\n",
+ "%pip install -U 'azureml-rag[pinecone,langchain]>=0.2.11'\n",
+ "\n",
+ "# If using hugging_face embeddings add `hugging_face` extra, e.g. `azureml-rag[pinecone,hugging_face]`"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create a Pinecone-based Vector Index from Data in S3 Using AzureML Data Import\n",
+ "\n",
+ "We'll walk through setting up an AzureML Pipeline which imports data from an S3 bucket, processes the data into chunks, embeds the chunks and creates a LangChain-compatible Pinecone MLIndex.\n",
+ "\n",
+ "Note: Support for [namespaces](https://docs.pinecone.io/docs/namespaces) is currently not available. We will be actively working on it and are excited to bring this feature to you!"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Get client for AzureML Workspace\n",
+ "\n",
+ "The workspace is the top-level resource for Azure Machine Learning, providing a centralized place to work with all the artifacts you create when you use Azure Machine Learning. In this section we will connect to the workspace in which the job will be run.\n",
+ "\n",
+ "If you don't have a Workspace and want to create and Index locally see [here to create one](https://learn.microsoft.com/azure/machine-learning/quickstart-create-resources?view=azureml-api-2).\n",
+ "\n",
+ "Enter your Workspace details below, running this still will write a `workspace.json` file to the current folder."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%writefile workspace.json\n",
+ "{\n",
+ " \"subscription_id\": \"\",\n",
+ " \"resource_group\": \"\",\n",
+ " \"workspace_name\": \"\"\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`MLClient` is how you interact with AzureML"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential\n",
+ "from azure.ai.ml import MLClient\n",
+ "from azureml.core import Workspace\n",
+ "\n",
+ "try:\n",
+ " credential = DefaultAzureCredential()\n",
+ " # Check if given credential can get token successfully.\n",
+ " credential.get_token(\"https://management.azure.com/.default\")\n",
+ "except Exception as ex:\n",
+ " # Fall back to InteractiveBrowserCredential in case DefaultAzureCredential not work\n",
+ " credential = InteractiveBrowserCredential()\n",
+ "\n",
+ "try:\n",
+ " ml_client = MLClient.from_config(credential=credential, path=\"workspace.json\")\n",
+ "except Exception as ex:\n",
+ " raise Exception(\n",
+ " \"Failed to create MLClient from config file. Please modify and then run the above cell with your AzureML Workspace details.\"\n",
+ " ) from ex\n",
+ " # ml_client = MLClient(\n",
+ " # credential=credential,\n",
+ " # subscription_id=\"\",\n",
+ " # resource_group_name=\"\",\n",
+ " # workspace_name=\"\"\n",
+ " # )\n",
+ "\n",
+ "ws = Workspace(\n",
+ " subscription_id=ml_client.subscription_id,\n",
+ " resource_group=ml_client.resource_group_name,\n",
+ " workspace_name=ml_client.workspace_name,\n",
+ ")\n",
+ "\n",
+ "print(ml_client)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Which Embeddings Model to use?\n",
+ "\n",
+ "There are currently two supported Embedding options: OpenAI's `text-embedding-ada-002` embedding model or HuggingFace embedding models. Here are some factors that might influence your decision:\n",
+ "\n",
+ "### OpenAI\n",
+ "\n",
+ "OpenAI has [great documentation](https://platform.openai.com/docs/guides/embeddings) on their Embeddings model `text-embedding-ada-002`, it can handle up to 8191 tokens and can be accessed using [Azure OpenAI](https://learn.microsoft.com/azure/cognitive-services/openai/concepts/models#embeddings-models) or OpenAI directly.\n",
+ "If you have an existing Azure OpenAI Instance you can connect it to AzureML, if you don't AzureML provisions a default one for you called `Default_AzureOpenAI`.\n",
+ "The main limitation when using `text-embedding-ada-002` is cost/quota available for the model. Otherwise it provides high quality embeddings across a wide array of text domains while being simple to use.\n",
+ "\n",
+ "### HuggingFace\n",
+ "\n",
+ "HuggingFace hosts many different models capable of embedding text into single-dimensional vectors. The [MTEB Leaderboard](https://huggingface.co/spaces/mteb/leaderboard) ranks the performance of embeddings models on a few axis, not all models ranked can be run locally (e.g. `text-embedding-ada-002` is on the list), though many can and there is a range of larger and smaller models. When embedding with HuggingFace the model is loaded locally for inference, this will potentially impact your choice of compute resources.\n",
+ "\n",
+ "**NOTE:** The default PromptFlow Runtime does not come with HuggingFace model dependencies installed, Indexes created using HuggingFace embeddings will not work in PromptFlow by default. **Pick OpenAI if you want to use PromptFlow**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Run the cells under _either_ heading (OpenAI or HuggingFace) to use the respective embedding model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### OpenAI\n",
+ "\n",
+ "We can use the automatically created `Default_AzureOpenAI` connection.\n",
+ "\n",
+ "If you would rather use an existing Azure OpenAI connection then change `aoai_connection_name` below.\n",
+ "If you would rather use an existing Azure OpenAI resource, but don't have a connection created, modify `aoai_connection_name` and the details under the `# Create New Connection` code comment, or navigate the PromptFlow section in your AzureML Workspace and use the Connections create UI flow."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "aoai_connection_name = \"Default_AzureOpenAI\"\n",
+ "aoai_connection_id = None"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here we get the connection from the Workspace and save its `id` so we can reference it later."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.utils.connections import (\n",
+ " get_connection_by_name_v2,\n",
+ " create_connection_v2,\n",
+ ")\n",
+ "\n",
+ "try:\n",
+ " aoai_connection = get_connection_by_name_v2(ws, aoai_connection_name)\n",
+ "except Exception as ex:\n",
+ " # Create New Connection\n",
+ " # Modify the details below to match the `Endpoint` and API key of your AOAI resource, these details can be found in Azure Portal\n",
+ " raise RuntimeError(\n",
+ " \"Have you entered your AOAI resource details below? If so, delete me!\"\n",
+ " )\n",
+ " aoai_connection = create_connection_v2(\n",
+ " workspace=ws,\n",
+ " name=aoai_connection,\n",
+ " category=\"AzureOpenAI\",\n",
+ " # 'Endpoint' from Azure OpenAI resource overview\n",
+ " target=\"https://.openai.azure.com/\",\n",
+ " auth_type=\"ApiKey\",\n",
+ " credentials={\n",
+ " # Either `Key` from the `Keys and Endpoint` tab of your Azure OpenAI resource, will be stored in your Workspace associated Azure Key Vault.\n",
+ " \"key\": \"\"\n",
+ " },\n",
+ " metadata={\"ApiType\": \"azure\", \"ApiVersion\": \"2023-05-15\"},\n",
+ " )\n",
+ "\n",
+ "aoai_connection_id = aoai_connection[\"id\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that your Workspace has a connection to Azure OpenAI we will make sure the `text-embedding-ada-002` model has been deployed ready for inference. This cell will fail if there is not deployment for the embeddings model, [follow these instructions](https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#deploy-a-model) to deploy a model with Azure OpenAI."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.utils.deployment import infer_deployment\n",
+ "\n",
+ "aoai_embedding_model_name = \"text-embedding-ada-002\"\n",
+ "try:\n",
+ " aoai_embedding_deployment_name = infer_deployment(\n",
+ " aoai_connection, aoai_embedding_model_name\n",
+ " )\n",
+ " print(\n",
+ " f\"Deployment name in AOAI workspace for model '{aoai_embedding_model_name}' is '{aoai_embedding_deployment_name}'\"\n",
+ " )\n",
+ "except Exception as e:\n",
+ " print(\n",
+ " f\"Deployment name in AOAI workspace for model '{aoai_embedding_model_name}' is not found.\"\n",
+ " )\n",
+ " print(\n",
+ " f\"Please create a deployment for this model by following the deploy instructions on the resource page for '{aoai_connection['properties']['target']}' in Azure Portal.\"\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally we will combine the deployment and model information into a uri form which the AzureML embeddings components expect as input."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "embeddings_model_uri = f\"azure_open_ai://deployment/{aoai_embedding_deployment_name}/model/{aoai_embedding_model_name}\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### HuggingFace\n",
+ "\n",
+ "AzureML's default model from HuggingFace is `all-mpnet-base-v2`, it can be run by most laptops. Any `sentence-transformer` models should be supported, you can learn more about `sentence-transformers` [here](https://huggingface.co/sentence-transformers)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "embeddings_model_uri = \"hugging_face://model/sentence-transformers/all-mpnet-base-v2\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Prepare Pinecone Index\n",
+ "\n",
+ "If you would rather use an existing Pinecone index from a Pinecone project connection then change `pinecone_connection_name` below.\n",
+ "If you would rather use an existing Pinecone index, but don't have a connection to the Pinecone project created, modify `pinecone_connection_name` and the details under the `# Create New Connection` code comment.\n",
+ "\n",
+ "Note: Only Custom Connections are supported right now for connecting to a Pinecone project (where your index lives).\n",
+ "\n",
+ "If creating the connection through the Create Connections UI flow, then please specify the Pinecone project\n",
+ "- API key (must use `api_key` as the key for the key-value pair)\n",
+ "- ID (must use `project_id` as the key for the key-value pair)\n",
+ "- environment (must use `environment` as the key for the key-value pair)\n",
+ "\n",
+ "as key-value pairs in the Custom Connection where the API key key-value pair must be a secret. For example:\n",
+ "\n",
+ "![Create Pinecone Connection UI](../../media/pinecone_connection_ui.png)\n",
+ "\n",
+ "\n",
+ "You can also modify the details under the `# Create New Connection` code comment to create a connection via the SDK!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pinecone_connection_name = \"my_pinecone_project_connection\"\n",
+ "pinecone_connection_id = None"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.utils.connections import (\n",
+ " get_connection_by_name_v2,\n",
+ " create_connection_v2,\n",
+ ")\n",
+ "\n",
+ "try:\n",
+ " pinecone_connection = get_connection_by_name_v2(ws, pinecone_connection_name)\n",
+ "except Exception as ex:\n",
+ " # Create New Connection\n",
+ " # Modify the details below to match the details of the Pinecone project where your index lives, more details here: https://docs.pinecone.io/docs/projects\n",
+ " raise RuntimeError(\n",
+ " \"Have you entered your Pinecone project details below? If so, delete me!\"\n",
+ " )\n",
+ " pinecone_connection = create_connection_v2(\n",
+ " workspace=ws,\n",
+ " name=pinecone_connection_name,\n",
+ " category=\"CustomKeys\",\n",
+ " target=\"_\",\n",
+ " auth_type=\"CustomKeys\",\n",
+ " credentials={\n",
+ " \"keys\": {\n",
+ " # https://docs.pinecone.io/docs/projects#api-keys\n",
+ " \"api_key\": \"\"\n",
+ " }\n",
+ " },\n",
+ " metadata={\n",
+ " # https://docs.pinecone.io/docs/projects#project-environment\n",
+ " \"environment\": \"\",\n",
+ " # https://docs.pinecone.io/docs/projects#project-id\n",
+ " \"project_id\": \"\",\n",
+ " },\n",
+ " )\n",
+ "\n",
+ "pinecone_connection_id = pinecone_connection[\"id\"]"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setup Pipeline to process data into Index"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Setup S3 Data Import\n",
+ "\n",
+ "You will need to modify the `s3.yaml` file in this directory to include the information specific to your s3 bucket. There are other supported Data Import source [outline here](../../../../../../cli/assets/data/README.md)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import load_workspace_connection\n",
+ "\n",
+ "s3_connection_name = \"my_s3_bucket_connection\"\n",
+ "\n",
+ "# Modify `s3.yaml` to include your S3 bucket details\n",
+ "ws_connection = load_workspace_connection(source=\"./s3.yaml\")\n",
+ "ml_client.connections.create_or_update(workspace_connection=ws_connection)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can create a DataImport job that produces a new Data Asset containing data from the S3 bucket."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import DataImport\n",
+ "from azure.ai.ml.data_transfer import import_data\n",
+ "from azure.ai.ml.data_transfer import FileSystem\n",
+ "\n",
+ "# `${{name}}` will be replaced by the Import Job's id, which is a uuid.\n",
+ "s3_import_path = \"azureml://datastores/workspaceblobstore/paths/s3_import/${{name}}\"\n",
+ "import_asset_name = \"s3_import_test\"\n",
+ "\n",
+ "data_import = DataImport(\n",
+ " name=import_asset_name,\n",
+ " source=FileSystem(connection=f\"azureml:{s3_connection_name}\", path=\"*\"),\n",
+ " path=s3_import_path,\n",
+ ")\n",
+ "ml_client.data.import_data(data_import=data_import)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Setup Pinecone MLIndex creation pipeline\n",
+ "\n",
+ "AzureML [Pipelines](https://learn.microsoft.com/azure/machine-learning/concept-ml-pipelines?view=azureml-api-2) connect together multiple [Components](https://learn.microsoft.com/azure/machine-learning/concept-component?view=azureml-api-2). Each Component defines inputs, code that consumes the inputs and outputs produced from the code. Pipelines themselves can have inputs, and outputs produced by connecting together individual sub Components.\n",
+ "To process your data for embedding and indexing we will chain together multiple components each performing their own step of the workflow.\n",
+ "\n",
+ "The Components are published to a [Registry](https://learn.microsoft.com/azure/machine-learning/how-to-manage-registries?view=azureml-api-2&tabs=cli), `azureml`, which should have access to by default, it can be accessed from any Workspace.\n",
+ "In the below cell we get the Component Definitions from the `azureml` registry."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ml_registry = MLClient(credential=credential, registry_name=\"azureml\")\n",
+ "\n",
+ "# Walks input folder according to provided glob pattern (all files by default: '**/*') and attempts to open them, extract text chunks and further chunk if necessary to fir within provided `chunk_size`.\n",
+ "crack_and_chunk_component = ml_registry.components.get(\n",
+ " \"llm_rag_crack_and_chunk\", label=\"latest\"\n",
+ ")\n",
+ "# Reads input folder of files containing chunks and their metadata as batches, in parallel, and generates embeddings for each chunk. Output format is produced and loaded by `azureml.rag.embeddings.EmbeddingContainer`.\n",
+ "generate_embeddings_component = ml_registry.components.get(\n",
+ " \"llm_rag_generate_embeddings\", label=\"latest\"\n",
+ ")\n",
+ "# Reads an input folder produced by `azureml.rag.embeddings.EmbeddingsContainer.save()` and pushes all embeddings (including metadata) into a Pinecone index. Writes an MLIndex yaml detailing the index and embeddings model information.\n",
+ "update_pinecone_index_component = ml_registry.components.get(\n",
+ " \"llm_rag_update_pinecone_index\", label=\"latest\"\n",
+ ")\n",
+ "# Takes a uri to a storage location where an MLIndex yaml is stored and registers it as an MLIndex Data asset in the AzureML Workspace.\n",
+ "register_mlindex_component = ml_registry.components.get(\n",
+ " \"llm_rag_register_mlindex_asset\", label=\"latest\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Each Component has documentation which provides an overall description of the Components purpose and each of the inputs/outputs.\n",
+ "For example we can see understand what `crack_and_chunk` does by inspecting the Component definition."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(crack_and_chunk_component)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Below a Pipeline is built by defining a python function which chains together the above components inputs and outputs. Arguments to the function are inputs to the Pipeline itself and the return value is a dictionary defining the outputs of the Pipeline."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import Input, Output\n",
+ "from azure.ai.ml.dsl import pipeline\n",
+ "from azure.ai.ml.entities._job.pipeline._io import PipelineInput\n",
+ "from typing import Optional\n",
+ "\n",
+ "\n",
+ "def use_automatic_compute(component, instance_count=1, instance_type=\"Standard_E8s_v3\"):\n",
+ " \"\"\"Configure input `component` to use automatic compute with `instance_count` and `instance_type`.\n",
+ "\n",
+ " This avoids the need to provision a compute cluster to run the component.\n",
+ " \"\"\"\n",
+ " component.set_resources(\n",
+ " instance_count=instance_count,\n",
+ " instance_type=instance_type,\n",
+ " properties={\"compute_specification\": {\"automatic\": True}},\n",
+ " )\n",
+ " return component\n",
+ "\n",
+ "\n",
+ "def optional_pipeline_input_provided(input: Optional[PipelineInput]):\n",
+ " \"\"\"Checks if optional pipeline inputs are provided.\"\"\"\n",
+ " return input is not None and input._data is not None\n",
+ "\n",
+ "\n",
+ "# If you have an existing compute cluster you want to use instead of automatic compute, uncomment the following line, replace `dedicated_cpu_compute` with the name of your cluster.\n",
+ "# Also comment out the `component.set_resources` line in `use_automatic_compute` above and the `default_compute='serverless'` line below.\n",
+ "\n",
+ "\n",
+ "# @pipeline(compute=dedicated_cpu_compute)\n",
+ "@pipeline(default_compute=\"serverless\")\n",
+ "def s3_into_pinecone(\n",
+ " input_data: Input,\n",
+ " embeddings_model: str,\n",
+ " pinecone_config: str,\n",
+ " pinecone_connection_id: str,\n",
+ " asset_name: str,\n",
+ " chunk_size: int = 1024,\n",
+ " data_source_glob: str = None,\n",
+ " data_source_url: str = None,\n",
+ " document_path_replacement_regex: str = None,\n",
+ " aoai_connection_id: str = None,\n",
+ " embeddings_container: Input = None,\n",
+ "):\n",
+ " \"\"\"Pipeline to generate embeddings for a `input_data` source and push them into a Pinecone index.\"\"\"\n",
+ "\n",
+ " crack_and_chunk = crack_and_chunk_component(\n",
+ " input_data=input_data,\n",
+ " input_glob=data_source_glob,\n",
+ " chunk_size=chunk_size,\n",
+ " data_source_url=data_source_url,\n",
+ " document_path_replacement_regex=document_path_replacement_regex,\n",
+ " )\n",
+ " use_automatic_compute(crack_and_chunk)\n",
+ "\n",
+ " generate_embeddings = generate_embeddings_component(\n",
+ " chunks_source=crack_and_chunk.outputs.output_chunks,\n",
+ " embeddings_container=embeddings_container,\n",
+ " embeddings_model=embeddings_model,\n",
+ " )\n",
+ " use_automatic_compute(generate_embeddings)\n",
+ " if optional_pipeline_input_provided(aoai_connection_id):\n",
+ " generate_embeddings.environment_variables[\n",
+ " \"AZUREML_WORKSPACE_CONNECTION_ID_AOAI\"\n",
+ " ] = aoai_connection_id\n",
+ " if optional_pipeline_input_provided(embeddings_container):\n",
+ " # If provided, `embeddings_container` is expected to be a URI to folder, the folder can be empty.\n",
+ " # Each sub-folder is generated by a `create_embeddings_component` run and can be reused for subsequent embeddings runs.\n",
+ " generate_embeddings.outputs.embeddings = Output(\n",
+ " type=\"uri_folder\", path=f\"{embeddings_container.path}/{{name}}\"\n",
+ " )\n",
+ "\n",
+ " # `update_pinecone_index` takes the Embedded data produced by `generate_embeddings` and pushes it into a Pinecone index.\n",
+ " update_pinecone_index = update_pinecone_index_component(\n",
+ " embeddings=generate_embeddings.outputs.embeddings,\n",
+ " pinecone_config=pinecone_config,\n",
+ " )\n",
+ " use_automatic_compute(update_pinecone_index)\n",
+ " if optional_pipeline_input_provided(pinecone_connection_id):\n",
+ " update_pinecone_index.environment_variables[\n",
+ " \"AZUREML_WORKSPACE_CONNECTION_ID_PINECONE\"\n",
+ " ] = pinecone_connection_id\n",
+ "\n",
+ " register_mlindex = register_mlindex_component(\n",
+ " storage_uri=update_pinecone_index.outputs.index,\n",
+ " asset_name=asset_name,\n",
+ " )\n",
+ " use_automatic_compute(register_mlindex)\n",
+ " return {\n",
+ " \"mlindex_asset_uri\": update_pinecone_index.outputs.index,\n",
+ " \"mlindex_asset_id\": register_mlindex.outputs.asset_id,\n",
+ " }"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can learn about the URIs AzureML will accept as data inputs [here](https://learn.microsoft.com/azure/machine-learning/how-to-read-write-data-v2?view=azureml-api-2&tabs=python#paths). Referencing a path on AzureML supported storages (Blob, ADLSgen2, ADLSgen1, Fileshare) works best using [Datastores](https://learn.microsoft.com/azure/machine-learning/how-to-datastore?view=azureml-api-2&tabs=cli-identity-based-access%2Ccli-adls-identity-based-access%2Ccli-azfiles-account-key%2Ccli-adlsgen1-identity-based-access) as they help manage credentials for access.\n",
+ "\n",
+ "Here we will reference the Data Asset created from the DataImport Job."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml import Input\n",
+ "\n",
+ "input_data = Input(type=\"uri_folder\", path=f\"azureml:{import_asset_name}@latest\")\n",
+ "data_source_url = \"s3://my-bucket\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `update_pinecone_index` component takes a `pinecone_config` argument which specifies the name of the Index to push chunked and embedded data to. If this index does not exist it will be created, if it does exists it will be reused.\n",
+ "\n",
+ "**Note: if you are using Pinecone's Starter Plan, you are allowed to have only 1 index. In which case, make sure you have deleted any existing index if you are not looking to reuse.** More details on the Starter Plan here: https://docs.pinecone.io/docs/starter-environment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pinecone_config = {\"index_name\": \"s3-data-import-aoai\"}\n",
+ "\n",
+ "print(update_pinecone_index_component.description)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can create the Pipeline Job by calling the `@pipeline` annotated function and providing input arguments.\n",
+ "`asset_name` will be used when registering the MLIndex Data Asset produced by the `register_mlindex` component in the pipeline. This is how you can refer to the MLIndex within AzureML."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "index_asset_name = \"s3_import_test\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "\n",
+ "pipeline_job = s3_into_pinecone(\n",
+ " input_data=input_data,\n",
+ " data_source_url=data_source_url,\n",
+ " pinecone_config=json.dumps(pinecone_config),\n",
+ " pinecone_connection_id=pinecone_connection_id,\n",
+ " # Each run will save latest Embeddings to subfolder under this path, runs will load latest embeddings from container and reuse any unchanged chunk embeddings\n",
+ " embeddings_container=Input(\n",
+ " type=\"uri_folder\",\n",
+ " path=f\"azureml://datastores/workspaceblobstore/paths/embeddings/{index_asset_name}\",\n",
+ " ),\n",
+ " embeddings_model=embeddings_model_uri,\n",
+ " # This should be None if using a HuggingFace embeddings model.\n",
+ " aoai_connection_id=aoai_connection_id,\n",
+ " # Name of asset to register MLIndex under\n",
+ " asset_name=index_asset_name,\n",
+ ")\n",
+ "\n",
+ "# By default AzureML Pipelines will reuse the output of previous component Runs when inputs have not changed.\n",
+ "# If you want to rerun the Pipeline every time each time so that any changes to upstream data sources are processed uncomment the below line.\n",
+ "\n",
+ "# pipeline_job.settings.force_rerun = True"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally we add some properties to `pipeline_job` which ensure the Index generation progress and final Artifact appear in the PromptFlow Vector Index UI."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pipeline_job.properties[\"azureml.mlIndexAssetName\"] = index_asset_name\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetKind\"] = \"pinecone\"\n",
+ "pipeline_job.properties[\"azureml.mlIndexAssetSource\"] = \"Data Import - S3\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Submit Pipeline\n",
+ "\n",
+ "**In case of any errors see [TROUBLESHOOT.md](../../TROUBLESHOOT.md).**\n",
+ "\n",
+ "The output of each step in the pipeline can be inspected via the Workspace UI, click the link under 'Details Page' after running the below cell. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "running_pipeline_job = ml_client.jobs.create_or_update(\n",
+ " pipeline_job, experiment_name=\"s3_to_pinecone\"\n",
+ ")\n",
+ "running_pipeline_job"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ml_client.jobs.stream(running_pipeline_job.name)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Use Index with langchain\n",
+ "\n",
+ "The Data Asset produced by the AzureML Pipeline above contains a yaml file named 'MLIndex' which contains all the information needed to use the Azure Cognitive Search index.\n",
+ "For instance if an AOAI deployment was used to embed the documents the details of that deployment and a reference to the secret are there.\n",
+ "This allows easy loading of the MLIndex into a langchain retriever."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azureml.rag.mlindex import MLIndex\n",
+ "\n",
+ "question = \"What is RAG?\"\n",
+ "\n",
+ "retriever = MLIndex(\n",
+ " ml_client.data.get(index_asset_name, label=\"latest\")\n",
+ ").as_langchain_retriever()\n",
+ "retriever.get_relevant_documents(question)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you have not deployed `gpt-35-turbo` on your Azure OpenAI resource the below cell will fail indicated the `API deployment for this resource does not exist`. Follow the previous instructions for deploying `text-embedding-ada-002` to deploy `gpt-35-turbo`, note the chosen deployment name below and use the same or update it if you choose different one."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from langchain.chains import RetrievalQA\n",
+ "from azureml.rag.models import init_llm, parse_model_uri\n",
+ "\n",
+ "model_config = parse_model_uri(\n",
+ " \"azure_open_ai://deployment/gpt-35-turbo/model/gpt-35-turbo\"\n",
+ ")\n",
+ "model_config[\"api_base\"] = aoai_connection[\"properties\"][\"target\"]\n",
+ "model_config[\"key\"] = aoai_connection[\"properties\"][\"credentials\"][\"key\"]\n",
+ "model_config[\"temperature\"] = 0.3\n",
+ "model_config[\"max_retries\"] = 3\n",
+ "\n",
+ "qa = RetrievalQA.from_chain_type(\n",
+ " llm=init_llm(model_config), chain_type=\"stuff\", retriever=retriever\n",
+ ")\n",
+ "\n",
+ "qa.run(question)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Note: To control the number of documents returned when searching try getting the the MLIndex `as_langchain_vectorstore()` instead, this implements the `VectorStore` interface which has more parameters."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### [Optional] Provision Cluster\n",
+ "\n",
+ "You don't have to! The settings on the Pipeline use AzureML Serverless Compute, you can use any SKU you have quota for on demand. If you want to use a cluster that's also supported."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from azure.ai.ml.entities import AmlCompute\n",
+ "\n",
+ "cpu_compute_target = \"rag-cpu\"\n",
+ "\n",
+ "try:\n",
+ " dedicated_cpu_compute = ml_client.compute.get(cpu_compute_target)\n",
+ "except Exception:\n",
+ " # Let's create the Azure Machine Learning compute object with the intended parameters\n",
+ " dedicated_cpu_compute = AmlCompute(\n",
+ " name=cpu_compute_target,\n",
+ " type=\"amlcompute\",\n",
+ " size=\"Standard_E8s_v3\",\n",
+ " min_instances=0,\n",
+ " max_instances=2,\n",
+ " idle_time_before_scale_down=600,\n",
+ " tier=\"Dedicated\",\n",
+ " )\n",
+ "\n",
+ " dedicated_cpu_compute = ml_client.compute.begin_create_or_update(\n",
+ " dedicated_cpu_compute\n",
+ " ).result(timeout=600)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.9"
+ },
+ "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/jobs/parallel/3a_mnist_batch_identification/script/dump_mltable.py b/sdk/python/jobs/parallel/3a_mnist_batch_identification/script/dump_mltable.py
deleted file mode 100644
index 3abc9ffd40..0000000000
--- a/sdk/python/jobs/parallel/3a_mnist_batch_identification/script/dump_mltable.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import yaml
-import argparse
-import os
-
-d = {"paths": [{"file": "./mnist"}]}
-
-parser = argparse.ArgumentParser(allow_abbrev=False, description="dump mltable")
-
-parser.add_argument("--output_folder", type=str, default=0)
-args, _ = parser.parse_known_args()
-dump_path = os.path.join(args.output_folder, "MLTable")
-with open(dump_path, "w") as yaml_file:
- yaml.dump(d, yaml_file, default_flow_style=False)
-print("Saved MLTable file")
diff --git a/sdk/python/jobs/pipelines/1l_flow_in_pipeline/README.md b/sdk/python/jobs/pipelines/1l_flow_in_pipeline/README.md
new file mode 100644
index 0000000000..961541be5a
--- /dev/null
+++ b/sdk/python/jobs/pipelines/1l_flow_in_pipeline/README.md
@@ -0,0 +1,5 @@
+This is a dummy pipeline job with anonymous reference of a flow as a component. This example has reused the flow in corresponding CLI example, which is copied from [sample in promptflow repository](https://github.com/microsoft/promptflow/tree/main/examples/flows/standard/basic) and remove connection dependency to avoid using promptflow connection in azure ml example repository. Please check [this path](../../../../../cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/) for dependent resources.
+
+References:
+- [microsoft/promptflow: Build high-quality LLM apps](https://github.com/microsoft/promptflow)
+- [Reference - Prompt flow docuentation](https://microsoft.github.io/promptflow/reference/index.html)
diff --git a/sdk/python/jobs/pipelines/1l_flow_in_pipeline/flow_in_pipeline.ipynb b/sdk/python/jobs/pipelines/1l_flow_in_pipeline/flow_in_pipeline.ipynb
new file mode 100644
index 0000000000..20aec64aad
--- /dev/null
+++ b/sdk/python/jobs/pipelines/1l_flow_in_pipeline/flow_in_pipeline.ipynb
@@ -0,0 +1,235 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Use Flow as Component in Pipeline Job\n",
+ "\n",
+ "**Requirements** - In order to benefit from this tutorial, you will need:\n",
+ "- A basic understanding of Machine Learning\n",
+ "- An Azure account with an active subscription - [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F)\n",
+ "- An Azure ML workspace with computer cluster - [Configure workspace](../../configuration.ipynb)\n",
+ "- A python environment\n",
+ "- Installed Azure Machine Learning Python SDK v2 - [install instructions](../../../README.md) - check the getting started section\n",
+ "\n",
+ "**Learning Objectives** - By the end of this tutorial, you should be able to:\n",
+ "- Connect to your AML workspace from the Python SDK\n",
+ "- Create `Pipeline` load flow as components from YAML\n",
+ "\n",
+ "**Motivations** - This notebook explains how to run a pipeline with distributed training component."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 1. Connect to Azure Machine Learning Workspace\n",
+ "\n",
+ "The [workspace](https://docs.microsoft.com/en-us/azure/machine-learning/concept-workspace) is the top-level resource for Azure Machine Learning, providing a centralized place to work with all the artifacts you create when you use Azure Machine Learning. In this section we will connect to the workspace in which the job will be run.\n",
+ "\n",
+ "## 1.1 Import the required libraries"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# import required libraries\n",
+ "from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential\n",
+ "\n",
+ "from azure.ai.ml import MLClient, load_component, Input\n",
+ "from azure.ai.ml.constants import AssetTypes\n",
+ "from azure.ai.ml.dsl import pipeline"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1.2 Configure credential\n",
+ "\n",
+ "We are using `DefaultAzureCredential` to get access to workspace. \n",
+ "`DefaultAzureCredential` should be capable of handling most Azure SDK authentication scenarios. \n",
+ "\n",
+ "Reference for more available credentials if it does not work for you: [configure credential example](../../configuration.ipynb), [azure-identity reference doc](https://docs.microsoft.com/en-us/python/api/azure-identity/azure.identity?view=azure-python)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " credential = DefaultAzureCredential()\n",
+ " # Check if given credential can get token successfully.\n",
+ " credential.get_token(\"https://management.azure.com/.default\")\n",
+ "except Exception as ex:\n",
+ " # Fall back to InteractiveBrowserCredential in case DefaultAzureCredential not work\n",
+ " credential = InteractiveBrowserCredential()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1.3 Get a handle to the workspace\n",
+ "\n",
+ "We use config file to connect to a workspace. The Azure ML workspace should be configured with computer cluster. [Check this notebook for configure a workspace](../../configuration.ipynb)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Get a handle to workspace\n",
+ "ml_client = MLClient.from_config(credential=credential)\n",
+ "\n",
+ "# Retrieve an already attached Azure Machine Learning Compute.\n",
+ "cluster_name = \"cpu-cluster\"\n",
+ "print(ml_client.compute.get(cluster_name))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 2. Load flow as component\n",
+ "\n",
+ "We suppose that there has already been a flow authored with Promptflow SDK/CLI/portal and `.promptflow/flow.tools.json` is already generated in the flow directory:\n",
+ "\n",
+ "- For more information about how to author a flow, please check [official doc site](https://microsoft.github.io/promptflow/).\n",
+ "- If `.promptflow/flow.tools.json` is not generated yet or is not up-to-date, you may use `pf flow validate` or `pf run validate` to generate it - [reference doc](https://microsoft.github.io/promptflow/reference/pf-command-reference.html).\n",
+ "- Please ensure that there are `$schema` in your `flow.dag.yaml` and `run.yaml`, we depends on that to identify whether this is a flow\n",
+ " - `flow.dag.yaml`: `$schema: https://azuremlschemas.azureedge.net/promptflow/latest/Flow.schema.json`\n",
+ " - `run.yaml`: `$schema: https://azuremlschemas.azureedge.net/promptflow/latest/Run.schema.json`\n",
+ "\n",
+ "\n",
+ "Then we can load its flow dag yaml as a component like regular component specs. Here we reused [the flow definition yaml in CLI examples](../../../../../cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/web_classification/flow.dag.yaml)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "flow_from_dag = load_component(\n",
+ " \"../../../../../cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/basic/flow.dag.yaml\"\n",
+ ")\n",
+ "flow_from_run = load_component(\n",
+ " \"../../../../../cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/run.yml\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 3. Pipeline job\n",
+ "## 3.1 Build pipeline"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data_input = Input(\n",
+ " path=\"../../../../../cli/jobs/pipelines-with-components/pipeline_job_with_flow_as_component/data/data.jsonl\",\n",
+ " type=AssetTypes.URI_FILE,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "@pipeline()\n",
+ "def pipeline_func_with_flow(data):\n",
+ " flow_node_from_dag = flow_from_dag(\n",
+ " data=data,\n",
+ " text=\"${data.text}\",\n",
+ " )\n",
+ " flow_node_from_dag.environment_variables = {\n",
+ " \"AZURE_OPENAI_API_KEY\": \"\",\n",
+ " \"AZURE_OPENAI_API_BASE\": \"\",\n",
+ " \"AZURE_OPENAI_API_TYPE\": \"azure\",\n",
+ " }\n",
+ " flow_node_from_run = flow_from_run(\n",
+ " data=data,\n",
+ " text=\"${data.text}\",\n",
+ " )\n",
+ "\n",
+ "\n",
+ "# create pipeline instance\n",
+ "pipeline_job = pipeline_func_with_flow(data=data_input)\n",
+ "pipeline_job.settings.default_compute = \"cpu-cluster\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3.2 Submit pipeline job"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# submit job to workspace\n",
+ "pipeline_job = ml_client.jobs.create_or_update(\n",
+ " pipeline_job, experiment_name=\"pipeline_samples\"\n",
+ ")\n",
+ "pipeline_job"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Wait until the job completes\n",
+ "ml_client.jobs.stream(pipeline_job.name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Next Steps\n",
+ "You can see further examples of running a pipeline job [here](../README.md)"
+ ]
+ }
+ ],
+ "metadata": {
+ "description": {
+ "description": "Create pipeline using components to run a distributed job with tensorflow"
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.17"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/sdk/python/jobs/single-step/tensorflow/train-hyperparameter-tune-deploy-with-keras/train-hyperparameter-tune-deploy-with-keras.ipynb b/sdk/python/jobs/single-step/tensorflow/train-hyperparameter-tune-deploy-with-keras/train-hyperparameter-tune-deploy-with-keras.ipynb
index 57816e9d9a..7d46ce8ecb 100644
--- a/sdk/python/jobs/single-step/tensorflow/train-hyperparameter-tune-deploy-with-keras/train-hyperparameter-tune-deploy-with-keras.ipynb
+++ b/sdk/python/jobs/single-step/tensorflow/train-hyperparameter-tune-deploy-with-keras/train-hyperparameter-tune-deploy-with-keras.ipynb
@@ -234,7 +234,7 @@
" - pip=21.2.4\n",
" - pip:\n",
" - protobuf~=3.20\n",
- " - numpy==1.21.2\n",
+ " - numpy==1.22\n",
" - tensorflow-gpu==2.2.0\n",
" - keras<=2.3.1\n",
" - matplotlib\n",
diff --git a/sdk/python/readme.py b/sdk/python/readme.py
index 89a5f65d50..89231f3a6a 100644
--- a/sdk/python/readme.py
+++ b/sdk/python/readme.py
@@ -478,13 +478,13 @@ def get_featurestore_config_workflow(folder_name, file_name):
if is_sdk_noteobook:
workflow += f"""
run: |
- bash -x setup-resources.sh {file_name}.ipynb
+ bash -x automation-test/setup-resources.sh automation-test/{file_name}.ipynb
working-directory: sdk/python/featurestore_sample
continue-on-error: true\n"""
if is_cli_notebook:
workflow += f"""
run: |
- bash -x setup-resources-cli.sh {file_name}.ipynb
+ bash -x automation-test/setup-resources-cli.sh automation-test/{file_name}.ipynb
working-directory: sdk/python/featurestore_sample
continue-on-error: true\n"""
diff --git a/sdk/python/responsible-ai/README.md b/sdk/python/responsible-ai/README.md
index 2900702ed4..82fe2de40d 100644
--- a/sdk/python/responsible-ai/README.md
+++ b/sdk/python/responsible-ai/README.md
@@ -6,15 +6,38 @@ The Responsible AI components are supported for MLflow models with `scikit-learn
The components accept both models and SciKit-Learn pipelines as input as long as the model or pipeline implements `predict` and `predict_proba` functions that conforms to the `scikit-learn` convention.
If not compatible, you can wrap your model's prediction function into a wrapper class that transforms the output into the format that is supported (`predict` and `predict_proba` of `scikit-learn`), and pass that wrapper class to modules in this repo.
-## Sample directory 📖
+## Directory 📖
+
+
+
+
+| Scenario | Dataset | Data type | RAI component included | Link to sample | Documentation |
+| --- | --- | --- | --- | --- | --- |
+| Regression | [sklearn Diabetes](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html) | Tabular | Explanation, Error Analysis, Counterfactuals | [responsibleaidashboard-diabetes-regression-model-debugging.ipynb](./tabular/responsibleaidashboard-diabetes-regression-model-debugging/responsibleaidashboard-diabetes-regression-model-debugging.ipynb) | [Tabular Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard?view=azureml-api-2) |
+| Regression | [Programmers MLTable data](./tabular/responsibleaidashboard-programmer-regression-model-debugging/data-programmer-regression) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [responsibleaidashboard-programmer-regression-model-debugging.ipynb](./tabular/responsibleaidashboard-programmer-regression-model-debugging/responsibleaidashboard-programmer-regression-model-debugging.ipynb) | [Tabular Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard?view=azureml-api-2) |
+| Binary Classification | [Finance Story](./tabular/responsibleaidashboard-finance-loan-classification/Fabricated_Loan_data.csv) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [Finance_Dashboard.ipynb](./tabular/responsibleaidashboard-finance-loan-classification/responsibleaidashboard-finance-loan-classification.ipynb) | [Documentation](./tabular/responsibleaidashboard-finance-loan-classification/readme.md) |
+| Binary Classification | [Healthcare Story](./tabular/responsibleaidashboard-healthcare-covid-classification/data_covid_classification/) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [Covid_Healthcare_Dashboard.ipynb](./tabular/responsibleaidashboard-healthcare-covid-classification/responsibleaidashboard-healthcare-covid-classification.ipynb) | [Documentation](./tabular/responsibleaidashboard-healthcare-covid-classification/readme.md) |
+| Binary Classification | [Education Story](./tabular/responsibleaidashboard-education-student-attrition-classificaton/Fabricated_Student_Attrition_Data.csv) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [Education_Dashboard.ipynb](./tabular/responsibleaidashboard-education-student-attrition-classificaton/responsibleaidashboard-education-student-attrition-classificaton.ipynb) | [Documentation](./tabular/responsibleaidashboard-education-student-attrition-classificaton/readme.md) |
+| Classification | [Kaggle Housing](https://www.kaggle.com/alphaepsilon/housing-prices-dataset) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [responsibleaidashboard-housing-classification-model-debugging.ipynb](./tabular/responsibleaidashboard-housing-classification-model-debugging/responsibleaidashboard-housing-classification-model-debugging.ipynb) | [Tabular Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard?view=azureml-api-2) |
+| Decision Making | [Kaggle Housing](https://www.kaggle.com/alphaepsilon/housing-prices-dataset) | Tabular | Causal analysis, Counterfactuals | [responsibleaidashboard-housing-decision-making.ipynb](./tabular/responsibleaidashboard-housing-decision-making/responsibleaidashboard-housing-decision-making.ipynb) | [Tabular Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard?view=azureml-api-2) |
+| Decision Making | [sklearn Diabetes](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html) | Tabular | Causal analysis, Counterfactuals | [responsibleaidashboard-diabetes-decision-making.ipynb](./tabular/responsibleaidashboard-diabetes-decision-making/responsibleaidashboard-diabetes-decision-making.ipynb) | [Tabular Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard?view=azureml-api-2) |
+| Text Classification | [DBPedia dataset](https://huggingface.co/datasets/DeveloperOats/DBPedia_Classes) | Text | Explanation, Error Analysis | [responsibleaidashboard-text-classification-DBPedia.ipynb](./text/responsibleaidashboard-text-classification-DBPedia.ipynb) | [Text Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-text-dashboard?view=azureml-api-2) |
+| Multi-label Text Classification | [Covid19 Emergency Event Dataset](https://huggingface.co/datasets/joelito/covid19_emergency_event) | Text | Explanation, Error Analysis | [responsibleaidashboard-multilabel-text-classification-covid-events.ipynb](./text/responsibleaidashboard-multilabel-text-classification-covid-events.ipynb) | [Text Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-text-dashboard?view=azureml-api-2) |
+| Binary Text Classification | [blbooksgenre dataset](https://huggingface.co/datasets/blbooksgenre) | Text | Explanation, Error Analysis | [responsibleaidashboard-text-classification-blbooksgenre.ipynb](./responsibleaidashboard-text-classification-blbooksgenre.ipynb) | [Text Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-text-dashboard?view=azureml-api-2) |
+| Text Classification | [News Article Classification](./text/responsibleaidashboard-text-classification-financial-news/Text_classification_dataset.xlsx) | Text | Explanation, Error Analysis | [Financial_News_Text_classifier.ipynb](./text/responsibleaidashboard-text-classification-financial-news/responsibleaidashboard-text-classification-financial-news.ipynb) | [Documentation](./text/responsibleaidashboard-text-classification-financial-news/readme.md) |
+| Text Question Answering | [Stanford Question Answering Dataset (SQuAD)](https://huggingface.co/datasets/squad) | Text | Explanation, Error Analysis | [responsibleaidashboard-text-question-answering-squad.ipynb](./text/responsibleaidashboard-text-question-answering-squad.ipynb) | [Text Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-text-dashboard?view=azureml-api-2) |
+| AutoML Image Classification | [Fridge Images](https://github.com/microsoft/computervision-recipes/tree/master/scenarios/classification) | Image | Explanation, Error Analysis | [responsibleaidashboard-automl-image-classification-fridge.ipynb](./vision/responsibleaidashboard-automl-image-classification-fridge.ipynb) | [Vision Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-image-dashboard?view=azureml-api-2) |
+| Object Detection | [MIT Computer Vision datasets](https://github.com/microsoft/computervision-recipes) | Image | Explanation, Error Analysis | [responsibleaidashboard-automl-object-detection-fridge-private-data.ipynb](./vision/responsibleaidashboard-automl-object-detection-fridge-private-data.ipynb) | [Vision Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-image-dashboard?view=azureml-api-2) |
+| Image Classification | [Fridge Images](https://github.com/microsoft/computervision-recipes/tree/master/scenarios/classification) | Image | Explanation, Error Analysis | [responsibleaidashboard-image-classification-fridge.ipynb](./vision/responsibleaidashboard-image-classification-fridge.ipynb) | [Vision Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-image-dashboard?view=azureml-api-2) |
+| Multilabel Image Classification | [Fridge Images](https://github.com/microsoft/computervision-recipes/tree/master/scenarios/classification) | Image | Explanation, Error Analysis | [responsibleaidashboard-image-multilabel-classification-fridge.ipynb](./vision/responsibleaidashboard-image-multilabel-classification-fridge.ipynb) | [Vision Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-image-dashboard?view=azureml-api-2) |
+| Image Object Detection | [Object Detection Fridge Images](https://github.com/microsoft/computervision-recipes/tree/master/scenarios/detection) | Image | Explanation, Error Analysis | [responsibleaidashboard-object-detection-MSCOCO.ipynb](./vision/responsibleaidashboard-object-detection-MSCOCO.ipynb) | [Vision Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-image-dashboard?view=azureml-api-2) |
+
+
+To learn more about the different types of Dashboard visit the below tutorials:
+1) [Tabular Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard?view=azureml-api-2)
+2) [Text Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-text-dashboard?view=azureml-api-2)
+3) [Vision Dashboard Generation](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-image-dashboard?view=azureml-api-2)
-| Scenario | Dataset | Data type | RAI component included | Link to sample |
-| --- | --- | --- | --- | --- |
-| Regression | [sklearn Diabetes](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html) | Tabular | Explanation, Error Analysis, Counterfactuals | [responsibleaidashboard-diabetes-regression-model-debugging.ipynb](https://github.com/Azure/azureml-examples/blob/main/sdk/python/responsible-ai/responsibleaidashboard-diabetes-regression-model-debugging/responsibleaidashboard-diabetes-regression-model-debugging.ipynb) |
-| Regression | [Programmers MLTable data](https://github.com/Azure/azureml-examples/tree/main/sdk/python/responsible-ai/responsibleaidashboard-programmer-regression-model-debugging/data-programmer-regression) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [responsibleaidashboard-programmer-regression-model-debugging.ipynb](https://github.com/Azure/azureml-examples/blob/main/sdk/python/responsible-ai/responsibleaidashboard-programmer-regression-model-debugging/responsibleaidashboard-programmer-regression-model-debugging.ipynb) |
-| Classification | [Kaggle Housing](https://www.kaggle.com/alphaepsilon/housing-prices-dataset) | Tabular | Explanation, Error Analysis, Causal analysis, Counterfactuals | [responsibleaidashboard-housing-classification-model-debugging.ipynb](https://github.com/Azure/azureml-examples/blob/main/sdk/python/responsible-ai/responsibleaidashboard-housing-classification-model-debugging/responsibleaidashboard-housing-classification-model-debugging.ipynb) |
-| Decision making | [Kaggle Housing](https://www.kaggle.com/alphaepsilon/housing-prices-dataset) | Tabular | Causal analysis, Counterfactuals | [responsibleaidashboard-housing-decision-making.ipynb](https://github.com/Azure/azureml-examples/blob/main/sdk/python/responsible-ai/responsibleaidashboard-housing-decision-making/responsibleaidashboard-housing-decision-making.ipynb) |
-| Decision making | [sklearn Diabetes](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html) | Tabular | Causal analysis, Counterfactuals | [responsibleaidashboard-diabetes-decision-making.ipynb](https://github.com/Azure/azureml-examples/blob/main/sdk/python/responsible-ai/responsibleaidashboard-diabetes-decision-making/responsibleaidashboard-diabetes-decision-making.ipynb) |
## Supportability 🧰
Currently, we support datasets having numerical and categorical features. The following table provides the scenarios supported for each of the four responsible AI components:
@@ -22,9 +45,10 @@ Currently, we support datasets having numerical and categorical features. The fo
| RAI component | Binary classification | Multi-class classification | Multilabel classification | Regression | Timeseries forecasting | Categorical features | Text features | Image Features | Recommender Systems | Reinforcement Learning |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -- |
-| Explainability | Yes | Yes | No | Yes | No | Yes | No | No | No | No |
-| Error Analysis | Yes | Yes | No | Yes | No | Yes | No | No | No | No |
+| Explainability | Yes | Yes | No | Yes | No | Yes | Yes | Yes | No | No |
+| Error Analysis | Yes | Yes | No | Yes | No | Yes | Yes | Yes | No | No |
| Causal Analysis | Yes | No | No | Yes | No | Yes (max 5 features due to computational cost) | No | No | No | No |
| Counterfactual | Yes | Yes | No | Yes | No | Yes | No | No | No | No |
-Read more about how to use the Responsible AI dashboard [here](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard).
+Read more about how to use the Responsible AI dashboards [here](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-responsible-ai-dashboard).
+