mirror of
https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
synced 2026-09-28 16:11:56 +00:00
Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0968fb77db | ||
|
|
471c57132a | ||
|
|
14c87ae702 | ||
|
|
72bad1f6ee | ||
|
|
e2a6610c2d | ||
|
|
145cdd0928 | ||
|
|
04e1697bca | ||
|
|
be785d0389 | ||
|
|
976e346a3d | ||
|
|
e37caca496 | ||
|
|
7a48fe8850 | ||
|
|
915a1edba8 | ||
|
|
424f947b04 | ||
|
|
c95f73c1a8 | ||
|
|
9da033c887 | ||
|
|
f61f9bfdc2 | ||
|
|
b2a17dfc83 | ||
|
|
7072219d8b | ||
|
|
57db2f4014 | ||
|
|
4604b0a0a9 | ||
|
|
c8c26a12ba | ||
|
|
3a8fa3e312 | ||
|
|
bbfba5aaaf | ||
|
|
e30b2182b0 | ||
|
|
4c4dade55a | ||
|
|
5690d50430 | ||
|
|
10c33f934f | ||
|
|
0c43cbd563 | ||
|
|
b640a8f545 | ||
|
|
a575528b11 | ||
|
|
42a2c4d082 | ||
|
|
0bcf44e9fa | ||
|
|
9ac2774ece | ||
|
|
975c9fe6bc | ||
|
|
417410f382 | ||
|
|
8fdfbe4e31 | ||
|
|
80f977546c | ||
|
|
c5fe281e32 | ||
|
|
49a2ddaf08 | ||
|
|
e9cba94bb4 | ||
|
|
fc83cfcea5 | ||
|
|
e3aa9e8f35 | ||
|
|
37c52abc11 | ||
|
|
c8320c8764 | ||
|
|
b5d51e61b6 | ||
|
|
aa5464151d | ||
|
|
386cecf4c2 | ||
|
|
dffdb15c17 | ||
|
|
2f3691eb71 | ||
|
|
f456d86555 | ||
|
|
f534b4e7a5 | ||
|
|
de247cd78b | ||
|
|
22ad74eb8d | ||
|
|
baf69999fc | ||
|
|
d5057da9bb | ||
|
|
24904a5999 | ||
|
|
881a2b45c5 |
@@ -62,6 +62,12 @@ parser.add_argument(
|
||||
help="The GCP region. This is used to inject a variable value into the notebook before running.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--variable_service_account",
|
||||
type=str,
|
||||
help="A service account. This is used to inject a variable value into the notebook before running. This is not the account that will run the notebook.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--staging_bucket",
|
||||
type=str,
|
||||
@@ -110,6 +116,7 @@ execute_changed_notebooks_helper.process_and_execute_notebooks(
|
||||
artifacts_bucket=args.artifacts_bucket,
|
||||
variable_project_id=args.variable_project_id,
|
||||
variable_region=args.variable_region,
|
||||
variable_service_account=args.variable_service_account,
|
||||
private_pool_id=args.private_pool_id,
|
||||
should_parallelize=args.should_parallelize,
|
||||
timeout=args.timeout,
|
||||
|
||||
@@ -17,6 +17,7 @@ import concurrent
|
||||
import dataclasses
|
||||
import datetime
|
||||
import functools
|
||||
import git
|
||||
import operator
|
||||
import os
|
||||
import pathlib
|
||||
@@ -72,6 +73,7 @@ def _process_notebook(
|
||||
notebook_path: str,
|
||||
variable_project_id: str,
|
||||
variable_region: str,
|
||||
variable_service_account: str,
|
||||
):
|
||||
# Read notebook
|
||||
with open(notebook_path) as f:
|
||||
@@ -83,6 +85,7 @@ def _process_notebook(
|
||||
replacement_map={
|
||||
"PROJECT_ID": variable_project_id,
|
||||
"REGION": variable_region,
|
||||
"SERVICE_ACCOUNT": variable_service_account,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -117,6 +120,7 @@ def process_and_execute_notebook(
|
||||
artifacts_bucket: str,
|
||||
variable_project_id: str,
|
||||
variable_region: str,
|
||||
variable_service_account: str,
|
||||
private_pool_id: Optional[str],
|
||||
deadline: datetime,
|
||||
notebook: str,
|
||||
@@ -151,6 +155,7 @@ def process_and_execute_notebook(
|
||||
notebook_path=notebook,
|
||||
variable_project_id=variable_project_id,
|
||||
variable_region=variable_region,
|
||||
variable_service_account=variable_service_account,
|
||||
)
|
||||
|
||||
# Upload the pre-processed code to a GCS bucket
|
||||
@@ -232,20 +237,40 @@ def get_changed_notebooks(
|
||||
|
||||
# Find notebooks
|
||||
notebooks = []
|
||||
|
||||
# Instantiate GitPython objects
|
||||
repo = git.Repo(os.getcwd())
|
||||
index = repo.index
|
||||
|
||||
if base_branch:
|
||||
print(f"Looking for notebooks that changed from branch: {base_branch}")
|
||||
notebooks = subprocess.check_output(
|
||||
["git", "diff", "--name-only", f"origin/{base_branch}..."] + test_paths
|
||||
)
|
||||
# Get the point at which this branch branches off from main
|
||||
branching_commits = repo.merge_base("HEAD", f"origin/{base_branch}")
|
||||
|
||||
if len(branching_commits) > 0:
|
||||
branching_commit = branching_commits[0]
|
||||
print(f"Looking for notebooks that changed from branch: {branching_commit}")
|
||||
|
||||
notebooks = [
|
||||
diff.b_path
|
||||
for diff in index.diff(branching_commit, paths=test_paths)
|
||||
if diff.b_path is not None
|
||||
]
|
||||
else:
|
||||
notebooks = []
|
||||
else:
|
||||
print(f"Looking for all notebooks.")
|
||||
notebooks = subprocess.check_output(["git", "ls-files"] + test_paths)
|
||||
notebooks = notebooks.decode("utf-8").split("\n")
|
||||
|
||||
notebooks = notebooks.decode("utf-8").split("\n")
|
||||
notebooks = [notebook for notebook in notebooks if notebook.endswith(".ipynb")]
|
||||
notebooks = [notebook for notebook in notebooks if len(notebook) > 0]
|
||||
notebooks = [notebook for notebook in notebooks if pathlib.Path(notebook).exists()]
|
||||
|
||||
if len(notebooks) > 0:
|
||||
print(f"Found {len(notebooks)} notebooks:")
|
||||
for notebook in notebooks:
|
||||
print(f"\t{notebook}")
|
||||
|
||||
return notebooks
|
||||
|
||||
|
||||
@@ -256,6 +281,7 @@ def process_and_execute_notebooks(
|
||||
artifacts_bucket: str,
|
||||
variable_project_id: str,
|
||||
variable_region: str,
|
||||
variable_service_account: str,
|
||||
private_pool_id: Optional[str],
|
||||
should_parallelize: bool,
|
||||
timeout: int,
|
||||
@@ -315,6 +341,7 @@ def process_and_execute_notebooks(
|
||||
artifacts_bucket,
|
||||
variable_project_id,
|
||||
variable_region,
|
||||
variable_service_account,
|
||||
private_pool_id,
|
||||
deadline,
|
||||
),
|
||||
@@ -329,6 +356,7 @@ def process_and_execute_notebooks(
|
||||
artifacts_bucket=artifacts_bucket,
|
||||
variable_project_id=variable_project_id,
|
||||
variable_region=variable_region,
|
||||
variable_service_account=variable_service_account,
|
||||
private_pool_id=private_pool_id,
|
||||
deadline=deadline,
|
||||
notebook=notebook,
|
||||
@@ -385,6 +413,7 @@ def process_and_execute_notebooks(
|
||||
notebook_path=notebook,
|
||||
variable_project_id=variable_project_id,
|
||||
variable_region=variable_region,
|
||||
variable_service_account=variable_service_account,
|
||||
)
|
||||
|
||||
execute_notebook_helper.execute_notebook(
|
||||
|
||||
@@ -10,19 +10,37 @@ steps:
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'python3 .cloud-build/CheckPythonVersion.py'
|
||||
# Install Python dependencies
|
||||
- python3 .cloud-build/CheckPythonVersion.py
|
||||
# Create a virtual environment
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'python3 -m pip install -U pip && python3 -m pip install -U --user -r .cloud-build/requirements.txt'
|
||||
- python3 -m venv workspace/env
|
||||
# Install Python dependencies
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- . workspace/env/bin/activate &&
|
||||
python3 -m pip install -U pip &&
|
||||
python3 -m pip install -U -r .cloud-build/requirements.txt
|
||||
# pip freeze
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 -m pip freeze
|
||||
# Install Python dependencies and run testing script
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'python3 -m pip install -U pip && python3 -m pip freeze && python3 .cloud-build/execute_notebook_cli.py --notebook_source "${_NOTEBOOK_GCS_URI}" --output_file_or_uri "${_NOTEBOOK_OUTPUT_GCS_URI}"'
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 .cloud-build/execute_notebook_cli.py --notebook_source "${_NOTEBOOK_GCS_URI}" --output_file_or_uri "${_NOTEBOOK_OUTPUT_GCS_URI}"
|
||||
env:
|
||||
- 'IS_TESTING=1'
|
||||
timeout: 86400s
|
||||
@@ -4,32 +4,47 @@ steps:
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'gcloud config list'
|
||||
- gcloud config list
|
||||
# Check the Python version
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'python3 .cloud-build/CheckPythonVersion.py'
|
||||
# Fetch base branch if required
|
||||
- python3 .cloud-build/CheckPythonVersion.py
|
||||
# Fetch full repo for diff purposes
|
||||
- name: gcr.io/cloud-builders/git
|
||||
args: [fetch, --unshallow]
|
||||
# Create a virtual environment
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'if [ -n "${_BASE_BRANCH}" ]; then git fetch origin "${_BASE_BRANCH}":refs/remotes/origin/"${_BASE_BRANCH}"; else echo "Skipping fetch."; fi'
|
||||
- python3 -m venv workspace/env
|
||||
# Install Python dependencies
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- . workspace/env/bin/activate &&
|
||||
python3 -m pip install -U pip &&
|
||||
python3 -m pip install -U -r .cloud-build/requirements.txt
|
||||
# pip freeze
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'python3 -m pip install -U pip && python3 -m pip install -U --user -r .cloud-build/requirements.txt'
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 -m pip freeze
|
||||
# Install Python dependencies and run testing script
|
||||
# TODO: Only pass in private_pool_id if it is set
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- 'python3 -m pip install -U pip && python3 -m pip freeze && python3 .cloud-build/execute_changed_notebooks_cli.py --test_paths_file "${_TEST_PATHS_FILE}" --base_branch "${_FORCED_BASE_BRANCH}" --container_uri ${_PYTHON_IMAGE} --staging_bucket ${_GCS_STAGING_BUCKET} --artifacts_bucket ${_GCS_STAGING_BUCKET}/executed_notebooks/PR_${_PR_NUMBER}/BUILD_${BUILD_ID} --variable_project_id ${PROJECT_ID} --variable_region ${_GCP_REGION} `if [ ! -z "${_PRIVATE_POOL_NAME}" ]; then echo "--private_pool_id ${_PRIVATE_POOL_NAME}"; fi`'
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 .cloud-build/execute_changed_notebooks_cli.py --test_paths_file "${_TEST_PATHS_FILE}" --base_branch "${_FORCED_BASE_BRANCH}" --container_uri ${_PYTHON_IMAGE} --staging_bucket ${_GCS_STAGING_BUCKET} --artifacts_bucket ${_GCS_STAGING_BUCKET}/executed_notebooks/PR_${_PR_NUMBER}/BUILD_${BUILD_ID} --variable_project_id ${PROJECT_ID} --variable_region ${_GCP_REGION} --variable_service_account ${_GCP_SERVICE_ACCOUNT} `if [ ! -z "${_PRIVATE_POOL_NAME}" ]; then echo "--private_pool_id ${_PRIVATE_POOL_NAME}"; fi`
|
||||
env:
|
||||
- 'IS_TESTING=1'
|
||||
timeout: 86400s
|
||||
|
||||
@@ -10,3 +10,4 @@ google-cloud-aiplatform
|
||||
google-cloud-storage
|
||||
google-cloud-build
|
||||
ratemate
|
||||
GitPython
|
||||
@@ -35,8 +35,8 @@ Variables in conditionals can also be replaced:
|
||||
|
||||
def get_updated_value(content: str, variable_name: str, variable_value: str) -> str:
|
||||
return re.sub(
|
||||
rf"({variable_name}.*?=.*?[\",\'])\[.+?\]([\",\'].*?)",
|
||||
rf"\1{variable_value}\2",
|
||||
rf"({variable_name}.*? = .*?[\",\'])\[.+?\]([\",\'].*?)",
|
||||
rf"\g<1>{variable_value}\g<2>",
|
||||
content,
|
||||
flags=re.M,
|
||||
)
|
||||
@@ -79,3 +79,26 @@ def test_region():
|
||||
variable_value="us-central1",
|
||||
)
|
||||
assert new_content == 'REGION = "us-central1" # @param {type:"string"}'
|
||||
|
||||
|
||||
def test_region_equal_equals_ignore():
|
||||
# Tests that == is ignored
|
||||
new_content = get_updated_value(
|
||||
content='REGION == "[your-region]" # @param {type:"string"}',
|
||||
variable_name="REGION",
|
||||
variable_value="us-central1",
|
||||
)
|
||||
assert new_content == 'REGION == "[your-region]" # @param {type:"string"}'
|
||||
|
||||
|
||||
def test_service_account():
|
||||
# Tests that == is ignored
|
||||
new_content = get_updated_value(
|
||||
content='SERVICE_ACCOUNT = "[your-service-account]" # @param {type:"string"}',
|
||||
variable_name="SERVICE_ACCOUNT",
|
||||
variable_value="12345-compute@developer.gserviceaccount.com",
|
||||
)
|
||||
assert (
|
||||
new_content
|
||||
== 'SERVICE_ACCOUNT = "12345-compute@developer.gserviceaccount.com" # @param {type:"string"}'
|
||||
)
|
||||
|
||||
@@ -6,5 +6,5 @@ black==22.3.0
|
||||
pyupgrade==2.34.0
|
||||
isort==5.10.1
|
||||
flake8==4.0.1
|
||||
nbqa==1.3.1
|
||||
nbqa==1.4.0
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ if [ ${#notebooks[@]} -gt 0 ]; then
|
||||
|
||||
if [ "$is_test" = true ]; then
|
||||
echo "Running nbfmt..."
|
||||
python3 -m tensorflow_docs.tools.nbfmt --remove_outputs --test "$notebook"
|
||||
python3 -m tensorflow_docs.tools.nbfmt --test "$notebook"
|
||||
NBFMT_RTN=$?
|
||||
# echo "Running black..."
|
||||
# python3 -m nbqa black "$notebook" --check
|
||||
@@ -93,7 +93,7 @@ if [ ${#notebooks[@]} -gt 0 ]; then
|
||||
python3 -m nbqa isort "$notebook"
|
||||
ISORT_RTN=$?
|
||||
echo "Running nbfmt..."
|
||||
python3 -m tensorflow_docs.tools.nbfmt --remove_outputs "$notebook"
|
||||
python3 -m tensorflow_docs.tools.nbfmt "$notebook"
|
||||
NBFMT_RTN=$?
|
||||
echo "Running flake8..."
|
||||
python3 -m nbqa flake8 "$notebook" --show-source --extend-ignore=W391,E501,F821,E402,F404,W503,E203,E722,W293,W291
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
/pytorch_text_classification_using_vertex_sdk_and_gcloud @RajeshThallam @ultrons
|
||||
/sklearn_text_classification_from_script_using_vertex_sdk @maxhardt
|
||||
/pluto_on_workbench @wkharold
|
||||
/cpr-examples @samthrasher
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
testdata/*
|
||||
build.py
|
||||
test.py
|
||||
state_dict.pth
|
||||
config.json
|
||||
@@ -0,0 +1,5 @@
|
||||
cpr_model_server.py
|
||||
entrypoint.py
|
||||
state_dict.pth
|
||||
config.json
|
||||
**/__pycache__
|
||||
@@ -0,0 +1,93 @@
|
||||
# CPR Example: PyTorch Image Models (timm)
|
||||
|
||||
## About CPR
|
||||
|
||||
CPR ([custom prediction routines](https://github.com/googleapis/python-aiplatform/blob/custom-prediction-routine/google/cloud/aiplatform/prediction/README.md)) is a framework designed by Google Cloud developers to make it easier to combine machine learning models with custom preprocessing and postprocessing logic in a real-time serving application.
|
||||
|
||||
## Using this example
|
||||
|
||||
This code is a self-contained example of a custom model server project built using CPR.
|
||||
|
||||
As is, you can use it to serve the ViT-Small image classification model from Ross Wightman's [`timm`](https://github.com/rwightman/pytorch-image-models) library of image model implementations in PyTorch. Both CPU and GPU are supported.
|
||||
|
||||
You can also consider using the code here as a template for your own CPR project if you want to use a different model from `timm`, a different PyTorch model, or an entirely different framework.
|
||||
|
||||
### Requirements
|
||||
|
||||
In order to use this example, you'll need Docker and Python 3 installed on your system.
|
||||
|
||||
To get started, first create a virtual environment in an empty directory:
|
||||
```sh
|
||||
mkdir cpr-example
|
||||
python3 -m venv cpr-example
|
||||
cd cpr-example && source bin/activate
|
||||
```
|
||||
|
||||
Then, clone the [vertex-ai-samples repo](https://github.com/GoogleCloudPlatform/vertex-ai-samples) in that directory:
|
||||
```sh
|
||||
git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
|
||||
cd vertex-ai-samples/community-content/cpr-examples/timm_serving
|
||||
```
|
||||
|
||||
Finally, install the Python modules required to build and run the model server:
|
||||
```sh
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Predictor
|
||||
|
||||
The `TimmPredictor` class in `timm_serving/predictor.py` implements most of the important logic for the server.
|
||||
|
||||
- `load(artifacts_dir)`: The predictor's `load` method is called when the server starts up in order to set up the predictor, usually by loading model weights and any artifacts needed for preprocessing and postprocessing. In this example, we initialize the saved model from the `state_dict.pth` file located inside the `artifacts_dir` folder and create the preprocessing transform from the model config.
|
||||
|
||||
- `preprocess`, `predict`, `postprocess`: These methods are applied in sequence to the deserialized JSON data from each request.
|
||||
- `preprocess` decodes images from base64 and apply cropping, scaling and normalizing transforms.
|
||||
- `predict` runs the ViT-Small model on the preprocessed images and returns class scores.
|
||||
- `postprocess` finds the top five classes and packs the class names, probabilities, and indices in a serializable result.
|
||||
|
||||
### Building the container
|
||||
|
||||
To build the model server locally, run the build command:
|
||||
```sh
|
||||
python build.py build
|
||||
```
|
||||
|
||||
You can edit configuration values such as the model server's base image, the name and tag assigned to the image, and the path where model weights are stored locally.
|
||||
|
||||
When you run the build command, model weights are downloaded and the model server container is built.
|
||||
|
||||
### Running local tests
|
||||
|
||||
`test.py` contains a suite of unit tests for the predictor as well as end-to-end tests for the model server.
|
||||
|
||||
To run the tests:
|
||||
```sh
|
||||
python test.py
|
||||
```
|
||||
|
||||
All of the test images are public domain.
|
||||
- [Cat](https://commons.wikimedia.org/wiki/File:Stray_cat_on_wall.jpg)
|
||||
- [Airplane](https://commons.wikimedia.org/wiki/File:Airplanes_jets.jpg)
|
||||
- The infamous [mandrill](https://commons.wikimedia.org/wiki/File:Wikipedia-sipi-image-db-mandrill-4.2.03.png)
|
||||
|
||||
### Deploying to Vertex AI
|
||||
|
||||
Before uploading or deploying the container, you'll need to modify `config.py` to set appropriate values for:
|
||||
- `project_id`: Your GCP project id.
|
||||
- `region`: Region where the model will be uploaded and deployed.
|
||||
- `repository`: [Artifact Registry repository](https://cloud.google.com/artifact-registry/docs/repositories/create-repos) in your project where the container image will be uploaded.
|
||||
- `artifacts_gcs_dir`: Folder in a [Google Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) where the model weights will be uploaded.
|
||||
|
||||
Once this is done, first upload the model:
|
||||
```sh
|
||||
python build.py upload
|
||||
```
|
||||
|
||||
Then deploy it:
|
||||
```sh
|
||||
python build.py deploy
|
||||
```
|
||||
|
||||
If you run the deploy command again, it will create a new endpoint. If you want to undeploy the model, you can do so using the Vertex AI dashboard on the Google Cloud console, or use `gcloud ai endpoints undeploy` from the command line.
|
||||
|
||||
After deploying successfully, you can run `python build.py probe` to send a sample request to the deployed model.
|
||||
@@ -0,0 +1,117 @@
|
||||
# Copyright 2022 Google LLC
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Build the model server container."""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
from typing import Sequence
|
||||
|
||||
from absl import app
|
||||
from absl import logging
|
||||
from config import CPRConfig
|
||||
from google.cloud import aiplatform
|
||||
from google.cloud.aiplatform import prediction as cpr
|
||||
import smart_open
|
||||
import timm
|
||||
from timm_serving import predictor
|
||||
import torch
|
||||
|
||||
|
||||
def build_container(config: CPRConfig, tag: str) -> cpr.LocalModel:
|
||||
"""Build the model server container.
|
||||
|
||||
Args:
|
||||
tag: Output image tag.
|
||||
|
||||
Returns:
|
||||
LocalModel exposing the built model server.
|
||||
"""
|
||||
return cpr.LocalModel.build_cpr_model(
|
||||
src_dir=os.path.join(os.getcwd()),
|
||||
output_image_uri=tag,
|
||||
base_image=config.base_image,
|
||||
predictor=predictor.TimmPredictor,
|
||||
requirements_path=os.path.join(os.getcwd(), "requirements.txt"),
|
||||
)
|
||||
|
||||
|
||||
def save_model_artifact(destination: str) -> None:
|
||||
"""Save a copy of the model state dict."""
|
||||
model = timm.create_model(predictor.TimmPredictor.TIMM_MODEL_NAME, pretrained=True)
|
||||
dest_file = os.path.join(destination, predictor.TimmPredictor.WEIGHTS_FILE)
|
||||
with smart_open.open(dest_file, "wb") as f:
|
||||
torch.save(model, f)
|
||||
logging.info("Saved model to %s", dest_file)
|
||||
logging.info("%s parameters", sum(p.numel() for p in model.parameters()))
|
||||
|
||||
|
||||
def upload_model(config: CPRConfig) -> aiplatform.Model:
|
||||
"""Tag and upload the model server."""
|
||||
ar_tag = (
|
||||
f"{config.region}-docker.pkg.dev/{config.project_id}"
|
||||
f"/{config.repository}/{config.image}"
|
||||
)
|
||||
local_model = build_container(config, tag=ar_tag)
|
||||
aiplatform.init(project=config.project_id, location=config.region)
|
||||
local_model.push_image()
|
||||
aip_model = aiplatform.Model.upload(
|
||||
local_model=local_model,
|
||||
display_name=predictor.TimmPredictor.TIMM_MODEL_NAME,
|
||||
artifact_uri=config.artifact_gcs_dir,
|
||||
)
|
||||
config.model_name = aip_model.resource_name
|
||||
config.save()
|
||||
return aip_model
|
||||
|
||||
|
||||
def deploy_model(config: CPRConfig) -> aiplatform.Endpoint:
|
||||
"""Deploy the model server to a Vertex Prediction endpoint."""
|
||||
aiplatform.init(project=config.project_id, location=config.region)
|
||||
aip_model = aiplatform.Model(model_name=config.model_name)
|
||||
endpoint = aip_model.deploy(machine_type=config.machine_type)
|
||||
config.endpoint_name = endpoint.resource_name
|
||||
config.save()
|
||||
return endpoint
|
||||
|
||||
|
||||
def probe_prediction(config: CPRConfig, request_path: str) -> None:
|
||||
"""Send a sample prediction request to the Vertex Prediction endpoint."""
|
||||
aiplatform.init(project=config.project_id, location=config.region)
|
||||
aip_endpoint = aiplatform.Endpoint(endpoint_name=config.endpoint_name)
|
||||
with open(request_path) as f:
|
||||
logging.info(aip_endpoint.predict(**json.load(f)))
|
||||
|
||||
|
||||
def main(argv: Sequence[str]):
|
||||
config = CPRConfig()
|
||||
if pathlib.Path(config.config_file).exists():
|
||||
config.load()
|
||||
|
||||
actions = set(argv[1:])
|
||||
if "build" in actions:
|
||||
build_container(config, config.image)
|
||||
save_model_artifact(config.artifact_local_dir)
|
||||
if "upload" in actions:
|
||||
save_model_artifact(config.artifact_gcs_dir)
|
||||
upload_model(config)
|
||||
if "deploy" in actions:
|
||||
deploy_model(config)
|
||||
if "probe" in actions:
|
||||
probe_prediction(config, request_path="sample_request.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(main)
|
||||
@@ -0,0 +1,76 @@
|
||||
# Copyright 2022 Google LLC
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import dataclasses
|
||||
import json
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CPRConfig(object):
|
||||
"""Configure the build process by editing the default values here.
|
||||
|
||||
config_file: File path used to save values in this config. (Some
|
||||
values, such as the model name, are generated at build time and
|
||||
depended on by future steps, so saving it allows this script to
|
||||
deploy the model without re-uploading it, for example.)
|
||||
|
||||
base_image: Base Docker image on top of which the model server will
|
||||
be built. By default, a Debian-based Python 3 image without GPU
|
||||
support will be used.
|
||||
|
||||
image: Name and tag assigned to the built model server image.
|
||||
|
||||
artifact_local_dir: Local directory where a copy of the pretrained model weights
|
||||
will be saved.
|
||||
|
||||
region: Google Cloud Region where the model will be uploaded during the
|
||||
build process.
|
||||
|
||||
project_id: Google Cloud project ID.
|
||||
|
||||
repository: Name of the Artifact Registry repository where the container
|
||||
will be uploaded.
|
||||
|
||||
artifact_gcs_dir: Location on GCS where a copy of the pretrained model
|
||||
weights will be uploaded.
|
||||
|
||||
model_name: Full resource path of the uploaded model. This is a write-only
|
||||
field, the value is generated by Vertex AI when the model is uploaded.
|
||||
|
||||
endpoint_name: Full resource path of the created endpoint. This is a
|
||||
write-only field, the value is generated by Vertex AI when the model is
|
||||
deployed to an endpoint.
|
||||
|
||||
machine_type: Machine type to use when deploying the model.
|
||||
"""
|
||||
|
||||
config_file: str = "config.json"
|
||||
base_image: str = "python:3.10-bullseye"
|
||||
image: str = "timm_predictor:latest"
|
||||
artifact_local_dir: str = ""
|
||||
region: str = "us-central1"
|
||||
project_id: str = "samthrasher-experimental"
|
||||
repository: str = "cpr-images"
|
||||
artifact_gcs_dir: str = "gs://samthrasher-cpr-example/timm-vit224/"
|
||||
model_name: str = ""
|
||||
endpoint_name: str = ""
|
||||
machine_type: str = "n1-standard-2"
|
||||
|
||||
def save(self):
|
||||
with open(self.config_file, "w") as f:
|
||||
json.dump(dataclasses.asdict(self), f, indent=2)
|
||||
|
||||
def load(self):
|
||||
with open(self.config_file) as f:
|
||||
self.__init__(**json.load(f))
|
||||
@@ -0,0 +1,8 @@
|
||||
absl-py==1.1.0
|
||||
fastapi==0.75.2
|
||||
uvicorn==0.18.2
|
||||
timm==0.5.4
|
||||
smart_open==6.0.0
|
||||
|
||||
google-cloud-storage>=1.26.0,<2.0.0dev
|
||||
google-cloud-aiplatform[prediction] @ git+https://github.com/googleapis/python-aiplatform.git@custom-prediction-routine
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,249 @@
|
||||
"""Test the timm_serving predictor."""
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
from typing import List, Dict
|
||||
|
||||
from absl import flags
|
||||
from absl import logging
|
||||
from absl.testing import absltest
|
||||
from config import CPRConfig
|
||||
import fastapi
|
||||
from google.cloud import aiplatform
|
||||
from google.cloud.aiplatform import prediction as cpr
|
||||
import PIL
|
||||
from timm_serving import predictor
|
||||
import torch
|
||||
|
||||
VIT_SMALL_PARAMS = 22878952
|
||||
|
||||
|
||||
def b64_encode_file(path: str) -> str:
|
||||
"""Encode a file's contents as base64.
|
||||
|
||||
Args:
|
||||
path: Path to the file.
|
||||
|
||||
Returns:
|
||||
Base64-encoded contents of the file.
|
||||
"""
|
||||
with open(path, "rb") as f:
|
||||
return str(base64.b64encode(f.read()), encoding="utf-8")
|
||||
|
||||
|
||||
def make_instance_dict(
|
||||
image_paths: List[str], base64_encodings: List[str]
|
||||
) -> Dict[str, List[str]]:
|
||||
"""Generate a dictionary similar to a parsed prediction server request.
|
||||
|
||||
Args:
|
||||
image_paths: Paths to image files to include.
|
||||
base64_encodings: Pre-encoded base64 strings.
|
||||
|
||||
Returns:
|
||||
Dictionary of instances in the format accepted by the preprocessor.
|
||||
"""
|
||||
instances = [s for s in base64_encodings]
|
||||
for path in image_paths:
|
||||
instances.append(b64_encode_file(path))
|
||||
return {"instances": instances}
|
||||
|
||||
|
||||
def count_parameters(model: torch.nn.Module):
|
||||
"""Count the parameters in a Pytorch model.
|
||||
|
||||
Args:
|
||||
model: Pytorch model (nn.Module).
|
||||
|
||||
Returns:
|
||||
Number of parameters in the model.
|
||||
|
||||
"""
|
||||
return sum(p.numel() for p in model.parameters())
|
||||
|
||||
|
||||
class PredictorUnitTests(absltest.TestCase):
|
||||
"""Unit tests for timm_serving.predictor."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.config = CPRConfig()
|
||||
self.config.load()
|
||||
self.predictor = predictor.TimmPredictor()
|
||||
|
||||
def test_load_from_saved_state_dict_ok(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
self.assertEqual(count_parameters(self.predictor._model), VIT_SMALL_PARAMS)
|
||||
|
||||
def test_load_bad_path(self):
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
self.predictor.load("testdata/")
|
||||
with self.assertRaisesRegex(ValueError, "not a directory"):
|
||||
self.predictor.load("blah")
|
||||
|
||||
def test_load_bad_data(self):
|
||||
with self.assertRaises(pickle.UnpicklingError):
|
||||
self.predictor.load("testdata/bad_model_1")
|
||||
with self.assertRaisesRegex(RuntimeError, "Invalid magic number"):
|
||||
self.predictor.load("testdata/bad_model_2")
|
||||
|
||||
def test_preprocess_ok(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
instance_dict = make_instance_dict(
|
||||
base64_encodings=[],
|
||||
image_paths=[
|
||||
"testdata/airplane.jpg",
|
||||
"testdata/mandrill.tiff",
|
||||
"testdata/mandrill.tiff",
|
||||
"testdata/cat_alpha.png",
|
||||
],
|
||||
)
|
||||
result = self.predictor.preprocess(instance_dict)
|
||||
self.assertEqual(result.size(), torch.Size([4, 3, 224, 224]))
|
||||
self.assertEqual(result.dtype, torch.float32)
|
||||
|
||||
def test_preprocess_no_instances(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
with self.assertRaises(fastapi.HTTPException) as ctx:
|
||||
self.predictor.preprocess({})
|
||||
self.assertEqual(ctx.exception.status_code, 400)
|
||||
self.assertRegex(ctx.exception.detail, 'must contain "instances"')
|
||||
|
||||
def test_preprocess_wrong_shape_instances(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
instance_dict = {"instances": [[b64_encode_file("testdata/mandrill.tiff")]]}
|
||||
with self.assertRaises(fastapi.HTTPException) as ctx:
|
||||
self.predictor.preprocess(instance_dict)
|
||||
self.assertEqual(ctx.exception.status_code, 400)
|
||||
self.assertRegex(ctx.exception.detail, "not 'list'")
|
||||
|
||||
def test_preprocess_bad_base64(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
instance_dict = make_instance_dict(base64_encodings=["!@#$"], image_paths=[])
|
||||
with self.assertRaises(fastapi.HTTPException) as ctx:
|
||||
self.predictor.preprocess(instance_dict)
|
||||
self.assertEqual(ctx.exception.status_code, 400)
|
||||
self.assertRegex(ctx.exception.detail, "[Bb]ase64")
|
||||
|
||||
def test_preprocess_not_image_data(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
instance_dict = make_instance_dict(
|
||||
base64_encodings=[], image_paths=["testdata/bad.jpg"]
|
||||
)
|
||||
with self.assertRaises(fastapi.HTTPException) as ctx:
|
||||
self.predictor.preprocess(instance_dict)
|
||||
self.assertEqual(ctx.exception.status_code, 400)
|
||||
self.assertRegex(ctx.exception.detail, "image file")
|
||||
|
||||
def test_predict_ok(self):
|
||||
self.predictor.load(self.config.artifact_local_dir)
|
||||
inputs = torch.zeros(size=[2, 3, 224, 224], dtype=torch.float32)
|
||||
if torch.cuda.device_count() > 0:
|
||||
inputs = inputs.cuda()
|
||||
result = self.predictor.predict(inputs)
|
||||
self.assertEqual(result.size(), torch.Size([2, 1000]))
|
||||
self.assertEqual(result.dtype, torch.float32)
|
||||
|
||||
def test_postprocess_ok(self):
|
||||
class_probs = torch.zeros(size=[2, 1000])
|
||||
class_probs[0, 0] = 1
|
||||
class_probs[1, 123] = 1
|
||||
result = self.predictor.postprocess(class_probs)
|
||||
predictions = result["predictions"]
|
||||
self.assertLen(predictions[0]["class_names"], 5)
|
||||
self.assertLen(predictions[0]["indices"], 5)
|
||||
self.assertLen(predictions[0]["probabilities"], 5)
|
||||
self.assertLen(predictions[1]["class_names"], 5)
|
||||
self.assertLen(predictions[1]["indices"], 5)
|
||||
self.assertLen(predictions[1]["probabilities"], 5)
|
||||
self.assertContainsSubsequence(predictions[0]["class_names"][0], "tench")
|
||||
self.assertContainsSubsequence(
|
||||
predictions[1]["class_names"][0], "spiny lobster"
|
||||
)
|
||||
|
||||
|
||||
class ServerEndToEndTests(absltest.TestCase):
|
||||
"""End-to-end tests for the model server, using LocalEndpoint."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.config = CPRConfig()
|
||||
self.config.load()
|
||||
self.local_model = cpr.LocalModel(
|
||||
serving_container_spec=aiplatform.gapic.ModelContainerSpec(
|
||||
image_uri=self.config.image
|
||||
)
|
||||
)
|
||||
|
||||
self.local_endpoint = self.local_model.deploy_to_local_endpoint(
|
||||
artifact_uri=self.config.artifact_local_dir or os.getcwd()
|
||||
)
|
||||
self.local_endpoint.serve()
|
||||
|
||||
def tearDown(self):
|
||||
self.local_endpoint.stop()
|
||||
super().tearDown()
|
||||
|
||||
def test_e2e_healthcheck_ok(self):
|
||||
health_check_response = self.local_endpoint.run_health_check()
|
||||
self.assertEqual(health_check_response.status_code, 200)
|
||||
self.assertEqual(health_check_response.content, b"{}")
|
||||
|
||||
def test_e2e_predict_ok(self):
|
||||
predict_request = json.dumps(
|
||||
make_instance_dict(
|
||||
base64_encodings=[],
|
||||
image_paths=[
|
||||
"testdata/mandrill.tiff",
|
||||
],
|
||||
)
|
||||
)
|
||||
response = self.local_endpoint.predict(
|
||||
request=predict_request, headers={"Content-Type": "application/json"}
|
||||
)
|
||||
logging.info(response.content)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
predictions = response.json()["predictions"]
|
||||
self.assertContainsSubsequence(predictions[0]["class_names"][0], "baboon")
|
||||
|
||||
def test_e2e_predict_bad_json_returns_400(self):
|
||||
predict_request = "blah"
|
||||
response = self.local_endpoint.predict(
|
||||
request=predict_request, headers={"Content-Type": "application/json"}
|
||||
)
|
||||
logging.info(response.content)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_e2e_predict_no_instances_returns_400(self):
|
||||
predict_request = json.dumps({})
|
||||
response = self.local_endpoint.predict(
|
||||
request=predict_request, headers={"Content-Type": "application/json"}
|
||||
)
|
||||
logging.info(response.content)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_e2e_predict_bad_base64_returns_400(self):
|
||||
predict_request = json.dumps(
|
||||
make_instance_dict(base64_encodings=["blah"], image_paths=[])
|
||||
)
|
||||
response = self.local_endpoint.predict(
|
||||
request=predict_request, headers={"Content-Type": "application/json"}
|
||||
)
|
||||
logging.info(response.content)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_e2e_predict_bad_image_returns_400(self):
|
||||
predict_request = json.dumps(
|
||||
make_instance_dict(base64_encodings=[], image_paths=["testdata/bad.jpg"])
|
||||
)
|
||||
response = self.local_endpoint.predict(
|
||||
request=predict_request, headers={"Content-Type": "application/json"}
|
||||
)
|
||||
logging.info(response.content)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
absltest.main()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1 @@
|
||||
some non-image data
|
||||
@@ -0,0 +1 @@
|
||||
some non-image data
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 348 KiB |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
# Copyright 2022 Google LLC
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Adapts a pretrained TIMM image classification model to the CPR framework.
|
||||
|
||||
Documentation for the TIMM (Torch IMage Models) library is here:
|
||||
https://rwightman.github.io/pytorch-image-models/
|
||||
|
||||
Its source can also be found here:
|
||||
https://github.com/rwightman/pytorch-image-models
|
||||
"""
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import io
|
||||
import os
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from fastapi import HTTPException
|
||||
from google.cloud.aiplatform import prediction as cpr
|
||||
from pathlib import Path
|
||||
import PIL
|
||||
import smart_open
|
||||
import timm
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
|
||||
with open(Path(__file__).parent.absolute().joinpath("imagenet.txt")) as f:
|
||||
IMAGENET_CLASSES = f.read().splitlines()
|
||||
|
||||
|
||||
class TimmPredictor(cpr.predictor.Predictor):
|
||||
"""Predictor class for image models based on TIMM."""
|
||||
|
||||
TIMM_MODEL_NAME = os.getenv("TIMM_MODEL_NAME", default="vit_small_patch32_224")
|
||||
WEIGHTS_FILE = "state_dict.pth"
|
||||
NUM_TOP_CLASSES_TO_RETURN = 5
|
||||
|
||||
def __init__(self):
|
||||
self._cuda = torch.cuda.device_count() > 0
|
||||
|
||||
def load(self, artifacts_uri: str = ""):
|
||||
"""Initializes the model and preprocessing transforms.
|
||||
|
||||
Args:
|
||||
artifacts_uri: Directory where state dict is stored. Can be a
|
||||
GCS URI or local path.
|
||||
"""
|
||||
if artifacts_uri:
|
||||
artifact_path = os.path.join(artifacts_uri)
|
||||
if not (os.path.isdir(artifact_path) or artifact_path.startswith("gs://")):
|
||||
raise ValueError("Provided artifact_uri is not a directory.")
|
||||
else:
|
||||
artifact_path = os.getcwd()
|
||||
|
||||
artifact_path = os.path.join(artifact_path, self.WEIGHTS_FILE)
|
||||
with smart_open.open(artifact_path, "rb") as f:
|
||||
self._model = torch.load(f)
|
||||
|
||||
if self._cuda:
|
||||
self._model.cuda()
|
||||
|
||||
config = timm.data.resolve_data_config(model=self.TIMM_MODEL_NAME, args=[])
|
||||
self._transform = timm.data.create_transform(
|
||||
is_training=False, use_prefetcher=False, **config
|
||||
)
|
||||
|
||||
def preprocess(self, request_dict: Dict[str, List[str]]) -> torch.Tensor:
|
||||
"""Performs preprocessing.
|
||||
|
||||
By default, the server expects a request body consisting of a valid JSON
|
||||
object. This will be parsed by the handler before it's evaluated by the
|
||||
preprocess method.
|
||||
|
||||
Args:
|
||||
request_dict: Parsed request body. We expect that the input consists of
|
||||
a list of base64-encoded image files under the "instances" key. (Any
|
||||
image format that PIL.image.open can handle is okay.)
|
||||
|
||||
Returns:
|
||||
torch.Tensor containing the preprocessed images as a batch. If GPU is
|
||||
available, the result tensor will be stored on GPU.
|
||||
"""
|
||||
|
||||
if "instances" not in request_dict:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail='Request must contain "instances" as a top-level key.',
|
||||
)
|
||||
|
||||
tensors = []
|
||||
|
||||
for (i, image) in enumerate(request_dict["instances"]):
|
||||
# We use Base64 encoding to handle image data.
|
||||
# This is probably the best we can do while still using JSON input.
|
||||
# Overriding the input format requires building a custom Handler.
|
||||
try:
|
||||
image_bytes = base64.b64decode(image, validate=True)
|
||||
except (binascii.Error, TypeError) as e:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Base64 decoding of the input image at index {i} failed:"
|
||||
f" {str(e)}",
|
||||
)
|
||||
|
||||
try:
|
||||
pil_image = PIL.Image.open(io.BytesIO(image_bytes)).convert("RGB")
|
||||
except PIL.UnidentifiedImageError:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"The input image at index {i} could not be identified as an"
|
||||
" image file.",
|
||||
)
|
||||
|
||||
tensors.append(self._transform(pil_image))
|
||||
|
||||
with torch.inference_mode():
|
||||
result = torch.stack(tensors)
|
||||
if self._cuda:
|
||||
result = result.cuda()
|
||||
return result
|
||||
|
||||
def predict(self, instances: torch.Tensor) -> torch.Tensor:
|
||||
"""Performs prediction.
|
||||
|
||||
Args:
|
||||
instances: torch.Tensor with type torch.float32 and shape
|
||||
[?, 3, 224, 224], containing the pre-processed input images.
|
||||
|
||||
Returns:
|
||||
Vector of scores with type torch.float32 and shape [?, 1000],
|
||||
representing the model's estimate of the likelihood that the
|
||||
input belongs to the Imagenet class with that index.
|
||||
"""
|
||||
with torch.inference_mode():
|
||||
class_scores = self._model(instances)
|
||||
return class_scores
|
||||
|
||||
def postprocess(
|
||||
self, class_scores: torch.Tensor
|
||||
) -> Dict[str, List[Dict[str, Union[str, int, float]]]]:
|
||||
"""Translate the model output into a classification result.
|
||||
|
||||
Args:
|
||||
class_scores: torch.Tensor with type torch.float32 and shape
|
||||
[?, 1000], containing the scores assigned to each class by
|
||||
the model.
|
||||
|
||||
Returns:
|
||||
Dictionary containing the list of classification results. Each
|
||||
classification result contains the probabilities, class names, and
|
||||
class indices of the classes with the top class scores as reported by
|
||||
the model.
|
||||
"""
|
||||
class_probs = F.softmax(class_scores, dim=1)
|
||||
top_k = class_probs.topk(self.NUM_TOP_CLASSES_TO_RETURN)
|
||||
top_k_values = top_k.values.numpy().tolist()
|
||||
top_k_indices = top_k.indices.numpy().tolist()
|
||||
predictions = [
|
||||
dict(
|
||||
probabilities=values,
|
||||
indices=indices,
|
||||
class_names=[IMAGENET_CLASSES[int(class_num)] for class_num in indices],
|
||||
)
|
||||
for (values, indices) in zip(top_k_values, top_k_indices)
|
||||
]
|
||||
return {"predictions": predictions}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
/sdk/sdk_* @andrewferlitsch
|
||||
/gapic @andrewferlitsch
|
||||
/gapic/custom/showcase_custom_image_classification_online_explain_example_based_api.ipynb @inardini
|
||||
/ml_ops @andrewferlitsch
|
||||
/model_monitoring/* @mco-gh
|
||||
/structured_data/rapid_prototyping_* @rafael-carvalho
|
||||
@@ -18,7 +19,11 @@
|
||||
/sdk/pytorch_lightning_custom_container_training.ipynb @brianchunkang
|
||||
/tensorboard @yfang1
|
||||
/feature_store @nayaknishant @morgandu
|
||||
/prediction @googleapis/vertex-prediction-team
|
||||
/vertex_endpoints/tf_hub_obj_detection/deploy_tfhub_object_detection_on_vertex_endpoints.ipynb @entrpn
|
||||
/vertex_endpoints/nvidia-triton/nvidia-triton-custom-container-prediction.ipynb @RajeshThallam
|
||||
/vertex_endpoints/optimized_tensorflow_runtime @vlasenkoalexey
|
||||
/notebooks/community/ml_ops/stage2/get_started_with_visionapi_and_automl.ipynb @mansari
|
||||
/notebooks/community/ml_ops/stage2/get_started_with_visionapi_and_automl.ipynb @mansari
|
||||
/notebooks/community/neo4j/graph_paysim.ipynb @benofben @laeg
|
||||
/notebooks/community/ml_ops/stage1/get_started_with_visionapi_and_vertex_datasets.ipynb @mansari
|
||||
/notebooks/community/pipelines/google_cloud_pipeline_components_bqml_pipeline_demand_forecasting.ipynb @inardini
|
||||
+3294
File diff suppressed because it is too large
Load Diff
@@ -97,11 +97,9 @@
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"\n",
|
||||
"* **Prepare a VPC network**. To reduce any network overhead that might lead to unnecessary increase in overhead latency, it is best to call the ANN endpoints from your VPC via a direct [VPC Peering](https://cloud.google.com/vertex-ai/docs/general/vpc-peering) connection. The following section describes how to setup a VPC Peering connection if you don't have one. This is a one-time initial setup task. You can also reuse existing VPC network and skip this section.\n",
|
||||
"* **WARNING:** The MatchingIndexEndpoint.match method (to create online queries against your deployed index) has to be executed in a Vertex AI Workbench notebook instance that is created with the following requirements:\n",
|
||||
" * **In the same region as where your ANN service is deployed** (for example, if you set `REGION = \"us-central1\"` as same as the tutorial, the notebook instance has to be in `us-central1`).\n",
|
||||
" * **Make sure you select the VPC network you created for ANN service** (instead of using the \"default\" one). That is, you will have to create the VPC network below and then create a new notebook instance that uses that VPC. \n",
|
||||
" * If you run it in the colab or a Vertex AI Workbench notebook instance in a different VPC network or region, the gRPC API will fail to peer the network (InactiveRPCError)."
|
||||
"* **Prepare a VPC network**. To reduce any network overhead that might lead to unnecessary increase in overhead latency, it is best to call the ANN endpoints from your VPC via a direct [VPC Peering](https://cloud.google.com/vertex-ai/docs/general/vpc-peering) connection. \n",
|
||||
" * The following section describes how to setup a VPC Peering connection if you don't have one. \n",
|
||||
" * This is a one-time initial setup task. You can also reuse existing VPC network and skip this section."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -112,11 +110,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"<your_project_id>\" # @param {type:\"string\"}\n",
|
||||
"PROJECT_ID = \"python-docs-samples-tests\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"NETWORK_NAME = \"my-vpc-network\" # @param {type:\"string\"}\n",
|
||||
"NETWORK_NAME = \"ann-vpc-network\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"PEERING_RANGE_NAME = \"my-haystack-range\""
|
||||
"PEERING_RANGE_NAME = \"ann-haystack-range\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -143,6 +141,7 @@
|
||||
"! gcloud compute addresses create {PEERING_RANGE_NAME} --global --prefix-length=16 --network={NETWORK_NAME} --purpose=VPC_PEERING --project={PROJECT_ID} --description=\"peering range\"\n",
|
||||
"\n",
|
||||
"# Set up peering with service networking\n",
|
||||
"# Your account must have the \"Compute Network Admin\" role to run the following.\n",
|
||||
"! gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com --network={NETWORK_NAME} --ranges={PEERING_RANGE_NAME} --project={PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
@@ -155,6 +154,20 @@
|
||||
"* Authentication: Rerun the `gcloud auth login` command in the Vertex AI Workbench notebook terminal when you are logged out and need the credential again."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d5de53b31bf1"
|
||||
},
|
||||
"source": [
|
||||
"## Make sure the following cells are run from inside the VPC network that you created in the previous step.\n",
|
||||
"\n",
|
||||
"* **WARNING:** The MatchingIndexEndpoint.match method (to create online queries against your deployed index) has to be executed in a Vertex AI Workbench notebook instance that is created with the following requirements:\n",
|
||||
" * **In the same region as where your ANN service is deployed** (for example, if you set `REGION = \"us-central1\"` as same as the tutorial, the notebook instance has to be in `us-central1`).\n",
|
||||
" * **Make sure you select the VPC network you created for ANN service** (instead of using the \"default\" one). That is, you will have to create the VPC network below and then create a new notebook instance that uses that VPC. \n",
|
||||
" * If you run it in the colab or a Vertex AI Workbench notebook instance in a different VPC network or region, \"Create Online Queries\" section will fail."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -271,7 +284,7 @@
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"PROJECT_ID = \"\"\n",
|
||||
"PROJECT_ID = \"python-docs-samples-tests\"\n",
|
||||
"\n",
|
||||
"# Get your Google Cloud project ID from gcloud\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
@@ -734,6 +747,28 @@
|
||||
"INDEX_RESOURCE_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "0f1a9fbecabb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"Using the resource name, you can retrieve an existing MatchingEngineIndex."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1ddb70647d98"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tree_ah_index = aiplatform.MatchingEngineIndex(INDEX_RESOURCE_NAME)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -742,7 +777,7 @@
|
||||
"source": [
|
||||
"### Create Brute Force Index (for Ground Truth)\n",
|
||||
"\n",
|
||||
"The brute force index uses a naive brute force method to find the nearest neighbors. This method is not fast or efficient. Hence brute force indices are not recommended for production usage. They are to be used to find the \"ground truth\" set of neighbors, so that the \"ground truth\" set can be used to measure recall of the indices being tuned for production usage. To ensure an apples to apples comparison, the `distanceMeasureType` and `featureNormType`, `dimensions` of the brute force index should match those of the production indices being tuned.\n",
|
||||
"The brute force index uses a naive brute force method to find the nearest neighbors. This method is not fast or efficient. Hence brute force indices are not recommended for production usage. They are to be used to find the \"ground truth\" set of neighbors, so that the \"ground truth\" set can be used to measure recall of the indices being tuned for production usage. To ensure an apples to apples comparison, the `distanceMeasureType` and `dimensions` of the brute force index should match those of the production indices being tuned.\n",
|
||||
"\n",
|
||||
"Create the brute force index configuration:"
|
||||
]
|
||||
@@ -777,6 +812,19 @@
|
||||
"INDEX_BRUTE_FORCE_RESOURCE_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "865fcad494d7"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"brute_force_index = aiplatform.MatchingEngineIndex(\n",
|
||||
" \"projects/1012616486416/locations/us-central1/indexes/6738176690918260736\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -941,7 +989,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"DEPLOYED_INDEX_ID = \"tree_ah_glove_deployed\""
|
||||
"DEPLOYED_INDEX_ID = f\"tree_ah_glove_deployed_{TIMESTAMP}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -976,7 +1024,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"DEPLOYED_BRUTE_FORCE_INDEX_ID = \"glove_brute_force_deployed\""
|
||||
"DEPLOYED_BRUTE_FORCE_INDEX_ID = f\"glove_brute_force_deployed_{TIMESTAMP}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1023,344 +1071,13 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test query\n",
|
||||
"query = [\n",
|
||||
" -0.11333,\n",
|
||||
" 0.48402,\n",
|
||||
" 0.090771,\n",
|
||||
" -0.22439,\n",
|
||||
" 0.034206,\n",
|
||||
" -0.55831,\n",
|
||||
" 0.041849,\n",
|
||||
" -0.53573,\n",
|
||||
" 0.18809,\n",
|
||||
" -0.58722,\n",
|
||||
" 0.015313,\n",
|
||||
" -0.014555,\n",
|
||||
" 0.80842,\n",
|
||||
" -0.038519,\n",
|
||||
" 0.75348,\n",
|
||||
" 0.70502,\n",
|
||||
" -0.17863,\n",
|
||||
" 0.3222,\n",
|
||||
" 0.67575,\n",
|
||||
" 0.67198,\n",
|
||||
" 0.26044,\n",
|
||||
" 0.4187,\n",
|
||||
" -0.34122,\n",
|
||||
" 0.2286,\n",
|
||||
" -0.53529,\n",
|
||||
" 1.2582,\n",
|
||||
" -0.091543,\n",
|
||||
" 0.19716,\n",
|
||||
" -0.037454,\n",
|
||||
" -0.3336,\n",
|
||||
" 0.31399,\n",
|
||||
" 0.36488,\n",
|
||||
" 0.71263,\n",
|
||||
" 0.1307,\n",
|
||||
" -0.24654,\n",
|
||||
" -0.52445,\n",
|
||||
" -0.036091,\n",
|
||||
" 0.55068,\n",
|
||||
" 0.10017,\n",
|
||||
" 0.48095,\n",
|
||||
" 0.71104,\n",
|
||||
" -0.053462,\n",
|
||||
" 0.22325,\n",
|
||||
" 0.30917,\n",
|
||||
" -0.39926,\n",
|
||||
" 0.036634,\n",
|
||||
" -0.35431,\n",
|
||||
" -0.42795,\n",
|
||||
" 0.46444,\n",
|
||||
" 0.25586,\n",
|
||||
" 0.68257,\n",
|
||||
" -0.20821,\n",
|
||||
" 0.38433,\n",
|
||||
" 0.055773,\n",
|
||||
" -0.2539,\n",
|
||||
" -0.20804,\n",
|
||||
" 0.52522,\n",
|
||||
" -0.11399,\n",
|
||||
" -0.3253,\n",
|
||||
" -0.44104,\n",
|
||||
" 0.17528,\n",
|
||||
" 0.62255,\n",
|
||||
" 0.50237,\n",
|
||||
" -0.7607,\n",
|
||||
" -0.071786,\n",
|
||||
" 0.0080131,\n",
|
||||
" -0.13286,\n",
|
||||
" 0.50097,\n",
|
||||
" 0.18824,\n",
|
||||
" -0.54722,\n",
|
||||
" -0.42664,\n",
|
||||
" 0.4292,\n",
|
||||
" 0.14877,\n",
|
||||
" -0.0072514,\n",
|
||||
" -0.16484,\n",
|
||||
" -0.059798,\n",
|
||||
" 0.9895,\n",
|
||||
" -0.61738,\n",
|
||||
" 0.054169,\n",
|
||||
" 0.48424,\n",
|
||||
" -0.35084,\n",
|
||||
" -0.27053,\n",
|
||||
" 0.37829,\n",
|
||||
" 0.11503,\n",
|
||||
" -0.39613,\n",
|
||||
" 0.24266,\n",
|
||||
" 0.39147,\n",
|
||||
" -0.075256,\n",
|
||||
" 0.65093,\n",
|
||||
" -0.20822,\n",
|
||||
" -0.17456,\n",
|
||||
" 0.53571,\n",
|
||||
" -0.16537,\n",
|
||||
" 0.13582,\n",
|
||||
" -0.56016,\n",
|
||||
" 0.016964,\n",
|
||||
" 0.1277,\n",
|
||||
" 0.94071,\n",
|
||||
" -0.22608,\n",
|
||||
" -0.021106,\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"response = my_index_endpoint.match(\n",
|
||||
" deployed_index_id=DEPLOYED_INDEX_ID, queries=[query], num_neighbors=NUM_NEIGHBOURS\n",
|
||||
" deployed_index_id=DEPLOYED_INDEX_ID, queries=test[:1], num_neighbors=NUM_NEIGHBOURS\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "_mNwdU9_B_Ez"
|
||||
},
|
||||
"source": [
|
||||
"### Batch Query\n",
|
||||
"\n",
|
||||
"You can run multiple queries in a single match call:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "A0XL0PJ1GoM9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test query\n",
|
||||
"queries = [\n",
|
||||
" [\n",
|
||||
" -0.11333,\n",
|
||||
" 0.48402,\n",
|
||||
" 0.090771,\n",
|
||||
" -0.22439,\n",
|
||||
" 0.034206,\n",
|
||||
" -0.55831,\n",
|
||||
" 0.041849,\n",
|
||||
" -0.53573,\n",
|
||||
" 0.18809,\n",
|
||||
" -0.58722,\n",
|
||||
" 0.015313,\n",
|
||||
" -0.014555,\n",
|
||||
" 0.80842,\n",
|
||||
" -0.038519,\n",
|
||||
" 0.75348,\n",
|
||||
" 0.70502,\n",
|
||||
" -0.17863,\n",
|
||||
" 0.3222,\n",
|
||||
" 0.67575,\n",
|
||||
" 0.67198,\n",
|
||||
" 0.26044,\n",
|
||||
" 0.4187,\n",
|
||||
" -0.34122,\n",
|
||||
" 0.2286,\n",
|
||||
" -0.53529,\n",
|
||||
" 1.2582,\n",
|
||||
" -0.091543,\n",
|
||||
" 0.19716,\n",
|
||||
" -0.037454,\n",
|
||||
" -0.3336,\n",
|
||||
" 0.31399,\n",
|
||||
" 0.36488,\n",
|
||||
" 0.71263,\n",
|
||||
" 0.1307,\n",
|
||||
" -0.24654,\n",
|
||||
" -0.52445,\n",
|
||||
" -0.036091,\n",
|
||||
" 0.55068,\n",
|
||||
" 0.10017,\n",
|
||||
" 0.48095,\n",
|
||||
" 0.71104,\n",
|
||||
" -0.053462,\n",
|
||||
" 0.22325,\n",
|
||||
" 0.30917,\n",
|
||||
" -0.39926,\n",
|
||||
" 0.036634,\n",
|
||||
" -0.35431,\n",
|
||||
" -0.42795,\n",
|
||||
" 0.46444,\n",
|
||||
" 0.25586,\n",
|
||||
" 0.68257,\n",
|
||||
" -0.20821,\n",
|
||||
" 0.38433,\n",
|
||||
" 0.055773,\n",
|
||||
" -0.2539,\n",
|
||||
" -0.20804,\n",
|
||||
" 0.52522,\n",
|
||||
" -0.11399,\n",
|
||||
" -0.3253,\n",
|
||||
" -0.44104,\n",
|
||||
" 0.17528,\n",
|
||||
" 0.62255,\n",
|
||||
" 0.50237,\n",
|
||||
" -0.7607,\n",
|
||||
" -0.071786,\n",
|
||||
" 0.0080131,\n",
|
||||
" -0.13286,\n",
|
||||
" 0.50097,\n",
|
||||
" 0.18824,\n",
|
||||
" -0.54722,\n",
|
||||
" -0.42664,\n",
|
||||
" 0.4292,\n",
|
||||
" 0.14877,\n",
|
||||
" -0.0072514,\n",
|
||||
" -0.16484,\n",
|
||||
" -0.059798,\n",
|
||||
" 0.9895,\n",
|
||||
" -0.61738,\n",
|
||||
" 0.054169,\n",
|
||||
" 0.48424,\n",
|
||||
" -0.35084,\n",
|
||||
" -0.27053,\n",
|
||||
" 0.37829,\n",
|
||||
" 0.11503,\n",
|
||||
" -0.39613,\n",
|
||||
" 0.24266,\n",
|
||||
" 0.39147,\n",
|
||||
" -0.075256,\n",
|
||||
" 0.65093,\n",
|
||||
" -0.20822,\n",
|
||||
" -0.17456,\n",
|
||||
" 0.53571,\n",
|
||||
" -0.16537,\n",
|
||||
" 0.13582,\n",
|
||||
" -0.56016,\n",
|
||||
" 0.016964,\n",
|
||||
" 0.1277,\n",
|
||||
" 0.94071,\n",
|
||||
" -0.22608,\n",
|
||||
" -0.021106,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" -0.99544,\n",
|
||||
" -2.3651,\n",
|
||||
" -0.24332,\n",
|
||||
" -1.0321,\n",
|
||||
" 0.42052,\n",
|
||||
" -1.1817,\n",
|
||||
" -0.16451,\n",
|
||||
" -1.683,\n",
|
||||
" 0.49673,\n",
|
||||
" -0.27258,\n",
|
||||
" -0.025397,\n",
|
||||
" 0.34188,\n",
|
||||
" 1.5523,\n",
|
||||
" 1.3532,\n",
|
||||
" 0.33297,\n",
|
||||
" -0.0056677,\n",
|
||||
" -0.76525,\n",
|
||||
" 0.49587,\n",
|
||||
" 1.2211,\n",
|
||||
" 0.83394,\n",
|
||||
" -0.20031,\n",
|
||||
" -0.59657,\n",
|
||||
" 0.38485,\n",
|
||||
" -0.23487,\n",
|
||||
" -1.0725,\n",
|
||||
" 0.95856,\n",
|
||||
" 0.16161,\n",
|
||||
" -1.2496,\n",
|
||||
" 1.6751,\n",
|
||||
" 0.73899,\n",
|
||||
" 0.051347,\n",
|
||||
" -0.42702,\n",
|
||||
" 0.16257,\n",
|
||||
" -0.16772,\n",
|
||||
" 0.40146,\n",
|
||||
" 0.29837,\n",
|
||||
" 0.96204,\n",
|
||||
" -0.36232,\n",
|
||||
" -0.47848,\n",
|
||||
" 0.78278,\n",
|
||||
" 0.14834,\n",
|
||||
" 1.3407,\n",
|
||||
" 0.47834,\n",
|
||||
" -0.39083,\n",
|
||||
" -1.037,\n",
|
||||
" -0.24643,\n",
|
||||
" -0.75841,\n",
|
||||
" 0.7669,\n",
|
||||
" -0.37363,\n",
|
||||
" 0.52741,\n",
|
||||
" 0.018563,\n",
|
||||
" -0.51301,\n",
|
||||
" 0.97674,\n",
|
||||
" 0.55232,\n",
|
||||
" 1.1584,\n",
|
||||
" 0.73715,\n",
|
||||
" 1.3055,\n",
|
||||
" -0.44743,\n",
|
||||
" -0.15961,\n",
|
||||
" 0.85006,\n",
|
||||
" -0.34092,\n",
|
||||
" -0.67667,\n",
|
||||
" 0.2317,\n",
|
||||
" 1.5582,\n",
|
||||
" 1.2308,\n",
|
||||
" -0.62213,\n",
|
||||
" -0.032801,\n",
|
||||
" 0.1206,\n",
|
||||
" -0.25899,\n",
|
||||
" -0.02756,\n",
|
||||
" -0.52814,\n",
|
||||
" -0.93523,\n",
|
||||
" 0.58434,\n",
|
||||
" -0.24799,\n",
|
||||
" 0.37692,\n",
|
||||
" 0.86527,\n",
|
||||
" 0.069626,\n",
|
||||
" 1.3096,\n",
|
||||
" 0.29975,\n",
|
||||
" -1.3651,\n",
|
||||
" -0.32048,\n",
|
||||
" -0.13741,\n",
|
||||
" 0.33329,\n",
|
||||
" -1.9113,\n",
|
||||
" -0.60222,\n",
|
||||
" -0.23921,\n",
|
||||
" 0.12664,\n",
|
||||
" -0.47961,\n",
|
||||
" -0.89531,\n",
|
||||
" 0.62054,\n",
|
||||
" 0.40869,\n",
|
||||
" -0.08503,\n",
|
||||
" 0.6413,\n",
|
||||
" -0.84044,\n",
|
||||
" -0.74325,\n",
|
||||
" -0.19426,\n",
|
||||
" 0.098722,\n",
|
||||
" 0.32648,\n",
|
||||
" -0.67621,\n",
|
||||
" -0.62692,\n",
|
||||
" ],\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -1369,7 +1086,7 @@
|
||||
"source": [
|
||||
"### Compute Recall\n",
|
||||
"\n",
|
||||
"Use deployed brute force Index as the ground truth to calculate the recall of ANN Index:"
|
||||
"Use the deployed brute force Index as the ground truth to calculate the recall of ANN Index. Note that you can run multiple queries in a single match call."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1402,18 +1119,20 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Calculate recall by determining how many neighbors were correctly retrieved as compared to the brute-force option.\n",
|
||||
"correct_neighbors = 0\n",
|
||||
"recalled_neighbors = 0\n",
|
||||
"for tree_ah_neighbors, brute_force_neighbors in zip(\n",
|
||||
" tree_ah_response_test, brute_force_response_test\n",
|
||||
"):\n",
|
||||
" tree_ah_neighbor_ids = [neighbor.id for neighbor in tree_ah_neighbors]\n",
|
||||
" brute_force_neighbor_ids = [neighbor.id for neighbor in brute_force_neighbors]\n",
|
||||
"\n",
|
||||
" correct_neighbors += len(\n",
|
||||
" recalled_neighbors += len(\n",
|
||||
" set(tree_ah_neighbor_ids).intersection(brute_force_neighbor_ids)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"recall = correct_neighbors / (len(test) * NUM_NEIGHBOURS)\n",
|
||||
"recall = recalled_neighbors / len(\n",
|
||||
" [neighbor for neighbors in brute_force_response_test for neighbor in neighbors]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Recall: {}\".format(recall))"
|
||||
]
|
||||
|
||||
@@ -88,6 +88,17 @@ The steps performed include:
|
||||
- Cancel a data labeling job.
|
||||
```
|
||||
|
||||
[Get Started with Vision API and Vertex AI Datasets](get_started_with_visionapi_and_vertex_datasets.ipynb)
|
||||
|
||||
```
|
||||
The steps performed include:
|
||||
|
||||
- Using Vision API to perform Optical Character Recognition (OCR) to extract text from PDF files.
|
||||
- Processing the results and saving them to text files.
|
||||
- Generating a Vertex AI Dataset import file.
|
||||
- Creating a new unlabelled text entity extraction Vertex AI Dataset resource in Vertex AI.
|
||||
```
|
||||
|
||||
### E2E Stage Example
|
||||
|
||||
[Stage 1: Data Management](mlops_data_management.ipynb)
|
||||
|
||||
+994
@@ -0,0 +1,994 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "copyright"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copyright 2022 Google LLC\n",
|
||||
"#\n",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"# you may not use this file except in compliance with the License.\n",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "4JIDiHvGasba"
|
||||
},
|
||||
"source": [
|
||||
"This notebook was contributed by [Mohammad Al-Ansari](https://github.com/Mansari)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2xDiUNIZINWp"
|
||||
},
|
||||
"source": [
|
||||
"# E2E ML on GCP: MLOps stage 1 : data management: create an unlabelled Vertex AI AutoML text entity extraction dataset from PDFs using Vision API\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_visionapi_and_vertex_datasets.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_visionapi_and_vertex_datasets.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\">\n",
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_visionapi_and_vertex_datasets.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"</table>\n",
|
||||
"<br/><br/><br/>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "H0alLPo_A-LK"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook will create an unlabelled `Vertex AI AutoML` text entity extraction dataset based on a collection of PDF files stored in a Cloud Storage bucket. \n",
|
||||
"\n",
|
||||
"The notebook can be modified to create different types of text datasets including sentiment analysis and classification."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "W4IBLTKOA5nl"
|
||||
},
|
||||
"source": [
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset used for this tutorial is the [Patent PDF Samples with Extracted Structured Data](https://console.cloud.google.com/marketplace/product/global-patents/labeled-patents) from Google Public Data Sets. \n",
|
||||
"\n",
|
||||
"This dataset includes data extracted from over 300 patent documents issued in the US and EU. The dataset includes links to Cloud Storage blobs for the first page of each patent, in addition to a number of extracted entities. \n",
|
||||
"\n",
|
||||
"The data is published as a [public dataset](https://cloud.google.com/bigquery/public-data) on `BigQuery`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3f8c2f702ccd"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn to use `Vision API` to extract text from PDF files stored on a Cloud Storage bucket. You will then process the results and create an unlabelled `Vertex AI Dataset`, compatible with `AutoML`, for text entity extraction.\n",
|
||||
"\n",
|
||||
"You can then either use Google Cloud console to annotate / label the dataset, or create a labelling job as demonstrated in [this notebook](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_data_labeling.ipynb).\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud services:\n",
|
||||
"\n",
|
||||
"- `Vision AI`\n",
|
||||
"- `Vertex AI AutoML`\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"1. Using `Vision API` to perform Optical Character Recognition (OCR) to extract text from PDF files.\n",
|
||||
"2. Processing the results and saving them to text files.\n",
|
||||
"3. Generating a `Vertex AI Dataset` import file.\n",
|
||||
"4. Creating a new unlabelled text entity extraction `Vertex AI Dataset` resource in `Vertex AI`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "CgLDJ419LPJs"
|
||||
},
|
||||
"source": [
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vision API\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing), [Vision API pricing](https://cloud.google.com/vision/pricing), [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "va2g7m9wLTjA"
|
||||
},
|
||||
"source": [
|
||||
"### Set up your local development environment\n",
|
||||
"\n",
|
||||
"If you are using Colab or Vertex AI Workbench Notebooks, your environment already meets all the requirements to run this notebook. You can skip this step.\n",
|
||||
"\n",
|
||||
"Otherwise, make sure your environment meets this notebook's requirements. You need the following:\n",
|
||||
"\n",
|
||||
"- The Vision API SDK\n",
|
||||
"- The Vertex AI SDK\n",
|
||||
"- The Cloud Storage SDK\n",
|
||||
"- Git\n",
|
||||
"- Python 3\n",
|
||||
"- virtualenv\n",
|
||||
"- Jupyter notebook running in a virtual environment with Python 3\n",
|
||||
"\n",
|
||||
"The Cloud Storage guide to [Setting up a Python development environment](https://cloud.google.com/python/setup) and the [Jupyter installation guide](https://jupyter.org/install) provide detailed instructions for meeting these requirements. The following steps provide a condensed set of instructions:\n",
|
||||
"\n",
|
||||
"1. [Install and initialize the SDKs](https://cloud.google.com/sdk/docs/).\n",
|
||||
"\n",
|
||||
"2. [Install Python 3](https://cloud.google.com/python/setup#installing_python).\n",
|
||||
"\n",
|
||||
"3. [Install virtualenv](https://cloud.google.com/python/setup#installing_and_using_virtualenv) and create a virtual environment that uses Python 3. Activate the virtual environment.\n",
|
||||
"\n",
|
||||
"4. To install Jupyter, run `pip3 install jupyter` on the command-line in a terminal shell.\n",
|
||||
"\n",
|
||||
"5. To launch Jupyter, run `jupyter notebook` on the command-line in a terminal shell.\n",
|
||||
"\n",
|
||||
"6. Open this notebook in the Jupyter Notebook Dashboard.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "X2tZAmugAe6h"
|
||||
},
|
||||
"source": [
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"Install the packages required for executing this notebook. You can ignore errors for the `pip` dependecy resolver as they do not impact this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "BQOsJ1hZAZu0"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-storage google-cloud-vision google-cloud-aiplatform $USER_FLAG -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "yzvvcmCuAon3"
|
||||
},
|
||||
"source": [
|
||||
"### Restart the kernel\n",
|
||||
"\n",
|
||||
"Once you've installed the additional packages, you need to restart the notebook kernel so it can find the packages."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6qEonzbuAoI_"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "pGbbyN7rAuRM"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"\n",
|
||||
"### GPU runtime\n",
|
||||
"\n",
|
||||
"*Make sure you're running this notebook in a GPU runtime if you have that option. In Colab, select* **Runtime > Change Runtime Type > GPU**\n",
|
||||
"\n",
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"2. [Make sure that billing is enabled for your project.](https://cloud.google.com/billing/docs/how-to/modify-project)\n",
|
||||
"\n",
|
||||
"3. [Enable the following APIs: Vision API, Vertex AI APIs, Compute Engine APIs, and Cloud Storage.](https://console.cloud.google.com/flows/enableapi?apiid=vision.googleapis.com,aiplatform.googleapis.com,compute_component,storage-component.googleapis.com)\n",
|
||||
"\n",
|
||||
"4. If you are running this notebook locally, you will need to install the [Cloud SDK]((https://cloud.google.com/sdk)).\n",
|
||||
"\n",
|
||||
"5. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "AE97adtnAzrr"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "nWlzLu5ELxWd"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if PROJECT_ID == \"\" or PROJECT_ID is None or PROJECT_ID == \"[your-project-id]\":\n",
|
||||
" # Get your GCP project id from gcloud\n",
|
||||
" shell_output = ! gcloud config list --format 'value(core.project)' 2>/dev/null\n",
|
||||
" PROJECT_ID = shell_output[0]\n",
|
||||
" print(\"Project ID:\", PROJECT_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "GB5b27r0LxqE"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "pMJdU1K5xG7D"
|
||||
},
|
||||
"source": [
|
||||
"### Regions\n",
|
||||
"\n",
|
||||
"#### Vision AI\n",
|
||||
"\n",
|
||||
"You can now specify continent-level data storage and Optical Character Regonition (OCR) processing by setting the `VISION_AI_REGION` variable. You can select one of the following options:\n",
|
||||
"\n",
|
||||
"* USA country only: `us`\n",
|
||||
"* The European Union: `eu`\n",
|
||||
"\n",
|
||||
"Learn more about [Vision AI regions for OCR](https://cloud.google.com/vision/docs/pdf#regionalization)\n",
|
||||
"\n",
|
||||
"#### Vertex AI\n",
|
||||
"\n",
|
||||
"You can also change the `VERTEX_AI_REGION` variable, which is used for operations throughout the rest of this notebook. Below are regions supported for Vertex AI. We recommend that you choose the region closest to you.\n",
|
||||
"\n",
|
||||
"- Americas: `us-central1`\n",
|
||||
"- Europe: `europe-west4`\n",
|
||||
"- Asia Pacific: `asia-east1`\n",
|
||||
"\n",
|
||||
"You may not use a multi-regional bucket for training with Vertex AI. Not all regions provide support for all Vertex AI services.\n",
|
||||
"\n",
|
||||
"Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "5EhEAOK5xIKc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"VISION_AI_REGION = \"[your-region]\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"if VISION_AI_REGION == \"[your-region]\":\n",
|
||||
" VISION_AI_REGION = \"us\"\n",
|
||||
"\n",
|
||||
"VERTEX_AI_REGION = \"[your-region]\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"if VERTEX_AI_REGION == \"[your-region]\":\n",
|
||||
" VERTEX_AI_REGION = \"us-central1\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "xkgvWoXkxM1r"
|
||||
},
|
||||
"source": [
|
||||
"### Timestamp\n",
|
||||
"\n",
|
||||
"If you are in a live tutorial session, you might be using a shared test account or project. To avoid name collisions between users on resources created, you create a timestamp for each instance session, and append onto the name of resources which will be created in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "gr0HTpQZxNy4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d%H%M%S\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "AA-ns5CcBA9U"
|
||||
},
|
||||
"source": [
|
||||
"### Vertex AI dataset import schema\n",
|
||||
"\n",
|
||||
"This constant tells Vertex AI the schema for importing the dataset. In this tutorial you are going to use the value for text extraction, but you can also change it to any of the values below for other use cases:\n",
|
||||
"\n",
|
||||
"- \n",
|
||||
"`aiplatform.schema.dataset.ioformat.text.single_label_classification`\n",
|
||||
"\n",
|
||||
"- \n",
|
||||
"`aiplatform.schema.dataset.ioformat.text.multi_label_classification`\n",
|
||||
"\n",
|
||||
"- \n",
|
||||
"`aiplatform.schema.dataset.ioformat.text.extraction`\n",
|
||||
"\n",
|
||||
"- \n",
|
||||
"`aiplatform.schema.dataset.ioformat.text.sentiment`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jnOb6Pp-4w5P"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"DATASET_IMPORT_SCHEMA = aiplatform.schema.dataset.ioformat.text.extraction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ekbg-G7UA-bK"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Vertex AI Workbench**, your environment is already authenticated. Skip this step. If you receive errors still, you may have to grant the service account that is your Workbench notebook is running under access to the services listed below.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions when prompted to authenticate your account via oAuth.\n",
|
||||
"\n",
|
||||
"**Otherwise**, follow these steps:\n",
|
||||
"\n",
|
||||
"In the Cloud Console, go to the [Create service account key](https://console.cloud.google.com/apis/credentials/serviceaccountkey) page.\n",
|
||||
"\n",
|
||||
"**Click Create service account**.\n",
|
||||
"\n",
|
||||
"In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"\n",
|
||||
"In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex\" into the filter box, and select **Vertex AI Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"\n",
|
||||
"Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lCRrULxKBAfa"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# If you are running this notebook in Colab, run this cell and follow the\n",
|
||||
"# instructions to authenticate your GCP account. This provides access to your\n",
|
||||
"# Cloud Storage bucket and lets you submit training jobs and prediction\n",
|
||||
"# requests.\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"IS_COLAB = \"google.colab\" in sys.modules\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\") and not os.getenv(\n",
|
||||
" \"DL_ANACONDA_HOME\"\n",
|
||||
"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # If you are running this notebook locally, replace the string below with the\n",
|
||||
" # path to your service account key and run this cell to authenticate your GCP\n",
|
||||
" # account.\n",
|
||||
" elif not os.getenv(\"IS_TESTING\"):\n",
|
||||
" %env GOOGLE_APPLICATION_CREDENTIALS ''"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "rHB6fbonMMbI"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"When you initialize the Vertex AI SDK for Python, you specify a Cloud Storage staging bucket. The staging bucket is where all the data associated with your dataset and model resources are retained across sessions. This bucket will be also used to store the output of the Vision API SDK PDF-to-text conversion process.\n",
|
||||
"\n",
|
||||
"Set the name of your Cloud Storage bucket below. Bucket names must be globally unique across all Google Cloud projects, including those outside of your organization."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ZSM5j0nfMOVK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_NAME = \"[your-bucket-name]\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = f\"gs://{BUCKET_NAME}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "i6H2iQX2MP-s"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if BUCKET_NAME == \"\" or BUCKET_NAME is None or BUCKET_NAME == \"[your-bucket-name]\":\n",
|
||||
" BUCKET_NAME = PROJECT_ID + \"aip-\" + TIMESTAMP\n",
|
||||
" BUCKET_URI = \"gs://\" + BUCKET_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "AOsnYE5cMQX4"
|
||||
},
|
||||
"source": [
|
||||
"**Only if your bucket doesn't already exist**: Run the following cell to create your Cloud Storage bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "33RgSjhyMR6C"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l $VERTEX_AI_REGION -p $PROJECT_ID $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "UpKfi0VfMTwe"
|
||||
},
|
||||
"source": [
|
||||
"Finally, validate access to your Cloud Storage bucket by examining its contents:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "G9dMjMnkMVNt"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil ls -al $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "k2qH7YCI0vnG"
|
||||
},
|
||||
"source": [
|
||||
"### Set up variables\n",
|
||||
"\n",
|
||||
"Next, set up some variables used throughout the tutorial.\n",
|
||||
"\n",
|
||||
"### Import libraries and define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "TB5-_2Xh01NH"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform, storage, vision"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-v7gY_KABIn8"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vision API SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the `Vision AI` SDK for Python for your project and region."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "DRbf--kWBLpx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vision_client_options = {\n",
|
||||
" \"quota_project_id\": PROJECT_ID,\n",
|
||||
" \"api_endpoint\": f\"{VISION_AI_REGION}-vision.googleapis.com\",\n",
|
||||
"}\n",
|
||||
"vision_client = vision.ImageAnnotatorClient(client_options=vision_client_options)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "CA4nNVbBZ25d"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the `Vertex AI` SDK for Python for your project, region and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "awWpNW1vZ6uV"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"aiplatform.init(\n",
|
||||
" project=PROJECT_ID, location=VERTEX_AI_REGION, staging_bucket=BUCKET_URI\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "debBBljMDqkM"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Cloud Storage SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the `Cloud Storage` SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ZtzmI9tpDr4e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"storage_client = storage.Client(project=PROJECT_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mvD0BxVXMtJe"
|
||||
},
|
||||
"source": [
|
||||
"## Tutorial\n",
|
||||
"\n",
|
||||
"Now you are ready to start creating an unlabelled `Vertex AI Dataset` text entity extraction dataset from PDF files."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "EurEFM3GBap9"
|
||||
},
|
||||
"source": [
|
||||
"### Convert PDF files to text using Vision API\n",
|
||||
"\n",
|
||||
"First, you make a `Vision API` request to OCR to text the PDFs from the Patent samples stored in the Cloud Storage bucket.\n",
|
||||
"\n",
|
||||
"*Note:* `Visions API` only allows batches of 100 document submissions at a time."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "uXVPOvjTBeK3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ORIGIN_BUCKET_NAME = \"gcs-public-data--labeled-patents\"\n",
|
||||
"# You can add a path if needed\n",
|
||||
"ORIGIN_BUCKET_PATH = \"\"\n",
|
||||
"\n",
|
||||
"DESTINATION_BUCKET_NAME = BUCKET_NAME\n",
|
||||
"DESTINATION_BUCKET_PATH = \"ocr-output\"\n",
|
||||
"\n",
|
||||
"gcs_destination_uri = f\"gs://{DESTINATION_BUCKET_NAME}/{DESTINATION_BUCKET_PATH}\"\n",
|
||||
"\n",
|
||||
"# Specify the feature for the Vision API processor\n",
|
||||
"feature = vision.Feature(type_=vision.Feature.Type.DOCUMENT_TEXT_DETECTION)\n",
|
||||
"\n",
|
||||
"# Retrieve a list of all files in the bucket and path\n",
|
||||
"blobs = storage_client.list_blobs(\n",
|
||||
" ORIGIN_BUCKET_NAME, prefix=ORIGIN_BUCKET_PATH, delimiter=\"/\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create a collection of requests. The SDK requires a separate request per each\n",
|
||||
"# file that we want to extract text from\n",
|
||||
"async_requests = []\n",
|
||||
"\n",
|
||||
"# Visions API only supports processing up to 100 documents at a time\n",
|
||||
"# so we will process the first 100 elements only\n",
|
||||
"sliced_blob_list = list(blobs)[:100]\n",
|
||||
"\n",
|
||||
"# Loop through the source bucket and create a request for each file there\n",
|
||||
"for blob in sliced_blob_list:\n",
|
||||
" # Build input_config\n",
|
||||
" # Ensure we are only processing PDF files\n",
|
||||
" if blob.name.endswith(\".pdf\"):\n",
|
||||
" gcs_source = vision.GcsSource(uri=f\"gs://{ORIGIN_BUCKET_NAME}/{blob.name}\")\n",
|
||||
" input_config = vision.InputConfig(\n",
|
||||
" gcs_source=gcs_source, mime_type=\"application/pdf\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Build output config\n",
|
||||
" # Get file name\n",
|
||||
" file_name = os.path.splitext(os.path.basename(blob.name))[0]\n",
|
||||
" gcs_destination = vision.GcsDestination(\n",
|
||||
" uri=f\"{gcs_destination_uri}/{file_name}-\"\n",
|
||||
" )\n",
|
||||
" output_config = vision.OutputConfig(gcs_destination=gcs_destination)\n",
|
||||
"\n",
|
||||
" # Build request object and add to the collection\n",
|
||||
" async_request = vision.AsyncAnnotateFileRequest(\n",
|
||||
" features=[feature], input_config=input_config, output_config=output_config\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" async_requests.append(async_request)\n",
|
||||
"\n",
|
||||
"print(f\"Created {len(async_requests)} requests\")\n",
|
||||
"\n",
|
||||
"# Submit the batch OCR job\n",
|
||||
"\n",
|
||||
"operation = vision_client.async_batch_annotate_files(requests=async_requests)\n",
|
||||
"print(\"Submitting the batch OCR job\")\n",
|
||||
"\n",
|
||||
"print(\"Waiting for the operation to finish... this will take a short while\")\n",
|
||||
"\n",
|
||||
"response = operation.result(timeout=420)\n",
|
||||
"\n",
|
||||
"print(\"Completed!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "7b15473e1937"
|
||||
},
|
||||
"source": [
|
||||
"#### Quick peek at extracted annotated JSON files\n",
|
||||
"\n",
|
||||
"Next, you take a peek at the contents of one of the extracted JSON annotated files."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4366442c1373"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"json_files = ! gsutil ls {gcs_destination_uri}\n",
|
||||
"\n",
|
||||
"example = json_files[0]\n",
|
||||
"! gsutil cat {example} | head -n 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "QWmeHWPIHako"
|
||||
},
|
||||
"source": [
|
||||
"### Process results and build the import file\n",
|
||||
"\n",
|
||||
"The `Vision API` output is in JSON format, and contains detailed text extraction data. You only need the full text output, so you will processs the JSON results, extract the text output, and save it in new text files to be used later in the tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "WDLtiejKHug6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"print(\"Extracting text from Vision API output and saving it to text files\")\n",
|
||||
"\n",
|
||||
"ocr_blobs = storage_client.list_blobs(\n",
|
||||
" DESTINATION_BUCKET_NAME, prefix=DESTINATION_BUCKET_PATH\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"output_bucket = storage_client.bucket(DESTINATION_BUCKET_NAME)\n",
|
||||
"\n",
|
||||
"# begin building the import file content\n",
|
||||
"import_file_entries = []\n",
|
||||
"\n",
|
||||
"for ocr_blob in ocr_blobs:\n",
|
||||
" # Only process .json files, in case we previously processed files and had .txt files\n",
|
||||
" if ocr_blob.name.endswith(\".json\"):\n",
|
||||
" print(f\"Extracting text from {ocr_blob.name}\")\n",
|
||||
" # read each blob into a stream\n",
|
||||
" contents = ocr_blob.download_as_string()\n",
|
||||
" # load as JSON\n",
|
||||
" json_object = json.loads(contents)\n",
|
||||
" # extract text\n",
|
||||
" full_text = \"\"\n",
|
||||
" for response in json_object[\"responses\"]:\n",
|
||||
" if response[\"fullTextAnnotation\"]:\n",
|
||||
" full_text += response[\"fullTextAnnotation\"][\"text\"] + \"\\r\\n\"\n",
|
||||
"\n",
|
||||
" # save as a blob\n",
|
||||
" output_blob_name = f\"{ocr_blob.name}.txt\"\n",
|
||||
" import_file_blob = output_bucket.blob(output_blob_name)\n",
|
||||
" import_file_blob.upload_from_string(full_text)\n",
|
||||
"\n",
|
||||
" # create import file listing\n",
|
||||
" import_file_entry = {\n",
|
||||
" \"textGcsUri\": f\"gs://{DESTINATION_BUCKET_NAME}/{output_blob_name}\"\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" import_file_entries.append(import_file_entry)\n",
|
||||
"\n",
|
||||
"print(\"Extraction completed!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0a5aae0eab44"
|
||||
},
|
||||
"source": [
|
||||
"#### Quick peek at extracted text files\n",
|
||||
"\n",
|
||||
"Next, you take a peek at the contents of one of the extracted text files."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "76ce5f57b1ae"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"example = import_file_entries[0][\"textGcsUri\"]\n",
|
||||
"\n",
|
||||
"! gsutil cat {example}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "hqTLS_AmLWQP"
|
||||
},
|
||||
"source": [
|
||||
"### Generate and save import file to be used in `Vertex AI Dataset` resource\n",
|
||||
"\n",
|
||||
"You will now build the import file that will be used to create the `Vertex AI Dataset` resource."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "_xFvOdQ_LWne"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"IMPORT_FILE_PATH = \"import_file\"\n",
|
||||
"\n",
|
||||
"# Convert import file entries to JSON Lines format\n",
|
||||
"import_file_content = \"\"\n",
|
||||
"for entry in import_file_entries:\n",
|
||||
" import_file_content += json.dumps(entry) + \"\\n\"\n",
|
||||
"\n",
|
||||
"print(f\"Created import file based on {len(import_file_entries)} annotations\")\n",
|
||||
"\n",
|
||||
"# Upload content to GCS to be used in our next step\n",
|
||||
"gcs_annotation_file_name = f\"{IMPORT_FILE_PATH}/import_file_{TIMESTAMP}.jsonl\"\n",
|
||||
"import_file_blob = output_bucket.blob(gcs_annotation_file_name)\n",
|
||||
"import_file_blob.upload_from_string(import_file_content)\n",
|
||||
"\n",
|
||||
"print(f\"Uploaded import file to {output_bucket.name}/{gcs_annotation_file_name}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6dVjFftOaKdw"
|
||||
},
|
||||
"source": [
|
||||
"### Create an unlabelled `Vertex AI Dataset` resource\n",
|
||||
"\n",
|
||||
"Next, you create the `Dataset` resource using the `create` method for the `TextDataset` class, which takes the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the `Dataset` resource.\n",
|
||||
"- `gcs_source`: A list of one or more dataset index files to import the data items into the `Dataset` resource.\n",
|
||||
"- `import_schema_uri`: The data labeling schema for the data items.\n",
|
||||
"\n",
|
||||
"This operation may take ten to twenty minutes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ciM9HLGCaOTJ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(\"Creating dataset ...\")\n",
|
||||
"\n",
|
||||
"dataset = aiplatform.TextDataset.create(\n",
|
||||
" display_name=\"Text Dataset \" + TIMESTAMP,\n",
|
||||
" gcs_source=[f\"gs://{output_bucket.name}/{gcs_annotation_file_name}\"],\n",
|
||||
" import_schema_uri=DATASET_IMPORT_SCHEMA,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Completed!\")\n",
|
||||
"\n",
|
||||
"print(dataset.resource_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2vagHf5T6Jd4"
|
||||
},
|
||||
"source": [
|
||||
"**Congratulations, your dataset is now ready for annotations!**\n",
|
||||
"\n",
|
||||
"You have two options:\n",
|
||||
"\n",
|
||||
"* Use Google Cloud Console to manually annotate the dataset in `Vertex AI`. Checkout [this link](https://cloud.google.com/vertex-ai/docs/datasets/label-using-console#entity-extraction) for more details on how to do so.\n",
|
||||
"* Create a labelling job to request data labelling. Check out [this link](https://cloud.google.com/vertex-ai/docs/datasets/data-labeling-job) and [this notebook](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_data_labeling.ipynb) for more details and examples.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cleanup:migration,new"
|
||||
},
|
||||
"source": [
|
||||
"# Cleaning up\n",
|
||||
"\n",
|
||||
"To clean up all GCP resources used in this project, you can [delete the GCP\n",
|
||||
"project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aoJ18d8Y_jAy"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set this to true only if you'd like to delete your bucket\n",
|
||||
"delete_bucket = False\n",
|
||||
"\n",
|
||||
"# Delete the dataset using the Vertex AI fully qualified identifier for the dataset\n",
|
||||
"dataset.delete()\n",
|
||||
"\n",
|
||||
"# Delete the bucket created\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil rm -r $BUCKET_URI"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"collapsed_sections": [],
|
||||
"name": "get_started_with_visionapi_and_vertex_datasets.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -35,18 +35,36 @@ The second stage in MLOps is experimenting in developing one or more baseline mo
|
||||
|
||||
### Get Started
|
||||
|
||||
[Get Started with Vertex Experiments and Vertex ML Metadata](get_started_vertex_experiments.ipynb)
|
||||
[Get Started with Logging](get_started_with_logging.ipynb)
|
||||
|
||||
```
|
||||
The steps performed include:
|
||||
|
||||
- Use Python logging to log training configuration/results locally.
|
||||
- Use Google Cloud Logging to log training configuration/results in cloud storage.
|
||||
- Create a Vertex AI `Experiment` resource.
|
||||
- Instantiate an experiment run.
|
||||
- Log parameters for the run.
|
||||
- Log metrics for the run.
|
||||
- Display the logged experiment run.
|
||||
```
|
||||
|
||||
[Get Started with Vertex Experiments and Vertex ML Metadata](get_started_vertex_experiments.ipynb)
|
||||
|
||||
```
|
||||
The steps performed include:
|
||||
|
||||
- Local (notebook) Training
|
||||
- Create an experiment
|
||||
- Create a first run in the experiment
|
||||
- Log parameters and metrics
|
||||
- Create artifact lineage
|
||||
- Visualize the experiment results
|
||||
- Execute a second run
|
||||
- Compare the two runs in the experiment
|
||||
- Cloud (`Vertex AI`) Training
|
||||
- Within the training script:
|
||||
- Create an experiment
|
||||
- Log parameters and metrics
|
||||
- Create artifact lineage
|
||||
- Create a `Vertex AI Training` custom job
|
||||
- Execute the custom job
|
||||
- Visualize the experiment results
|
||||
```
|
||||
|
||||
[Get Started with Vertex TensorBoard](get_started_vertex_tensorboard.ipynb)
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage2/get_started_bqml_training.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\\\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
@@ -1209,7 +1209,7 @@
|
||||
"\n",
|
||||
"You need to set some additional IAM permissions for BigQuery ML to automatically upload and register the model after training. Depending on your service account, the setting of the permissions below may fail. In this case, we recommend executing the permissions in a Cloud Shell.\n",
|
||||
"\n",
|
||||
"Learn more about [Setting permissions for Model Registry](https://cloud.devsite.corp.google.com/bigquery-ml/docs/managing-models-vertex\n"
|
||||
"Learn more about [Setting permissions for Model Registry](https://cloud.google.com/bigquery-ml/docs/managing-models-vertex)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,684 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "copyright"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copyright 2022 Google LLC\n",
|
||||
"#\n",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"# you may not use this file except in compliance with the License.\n",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "title:generic,gcp"
|
||||
},
|
||||
"source": [
|
||||
"# E2E ML on GCP: MLOps stage 2 : experimentation: get started with Logging\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage2/get_started_with_logging.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\">\n",
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage2/get_started_with_logging.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/stage2/get_started_with_logging.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"</table>\n",
|
||||
"<br/><br/><br/>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "overview:mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This tutorial demonstrates how to use Vertex AI for E2E MLOps on Google Cloud in production. This tutorial covers stage 2 : experimentation: get started with Logging."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "objective:mlops,stage2,get_started_vertex_experiments"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn how to use Python and Cloud logging awhen training with `Vertex AI`.\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `Cloud Logging`\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Use Python logging to log training configuration/results locally.\n",
|
||||
"- Use Google Cloud Logging to log training configuration/results in cloud storage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "recommendation:mlops,stage2,logging"
|
||||
},
|
||||
"source": [
|
||||
"### Recommendations\n",
|
||||
"\n",
|
||||
"When doing E2E MLOps on Google Cloud, the following are some of the best practices for logging data when experimenting or formally training a model.\n",
|
||||
"\n",
|
||||
"#### Python Logging\n",
|
||||
"\n",
|
||||
"Use Python's logging package when doing ad-hoc training locally.\n",
|
||||
"\n",
|
||||
"#### Cloud Logging\n",
|
||||
"\n",
|
||||
"Use `Google Cloud Logging` when doing training on the cloud.\n",
|
||||
"\n",
|
||||
"#### Experiments\n",
|
||||
"\n",
|
||||
"Use Vertex AI Experiments in conjunction with logging when performing experiments to compare results for different experiment configurations.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"- Vertex AI\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Installations\n",
|
||||
"\n",
|
||||
"Install the following packages for executing this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\") and not os.getenv(\"VIRTUAL_ENV\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-logging $USER_FLAG -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"source": [
|
||||
"### Restart the kernel\n",
|
||||
"\n",
|
||||
"Once you've installed the additional packages, you need to restart the notebook kernel so it can find the packages."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "project_id"
|
||||
},
|
||||
"source": [
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI, Compute Engine, Cloud Storage and Cloud Logging APIs](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component,storage_component,logging).\n",
|
||||
"\n",
|
||||
"1. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
"1. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, you may be able to get your project ID using `gcloud`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if PROJECT_ID == \"\" or PROJECT_ID is None or PROJECT_ID == \"[your-project-id]\":\n",
|
||||
" # Get your GCP project id from gcloud\n",
|
||||
" shell_output = ! gcloud config list --format 'value(core.project)' 2>/dev/null\n",
|
||||
" PROJECT_ID = shell_output[0]\n",
|
||||
" print(\"Project ID:\", PROJECT_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_gcloud_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### Region\n",
|
||||
"\n",
|
||||
"You can also change the `REGION` variable, which is used for operations\n",
|
||||
"throughout the rest of this notebook. Below are regions supported for Vertex AI. We recommend that you choose the region closest to you.\n",
|
||||
"\n",
|
||||
"- Americas: `us-central1`\n",
|
||||
"- Europe: `europe-west4`\n",
|
||||
"- Asia Pacific: `asia-east1`\n",
|
||||
"\n",
|
||||
"You may not use a multi-regional bucket for training with Vertex AI. Not all regions provide support for all Vertex AI services.\n",
|
||||
"\n",
|
||||
"Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"[your-region]\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"if REGION == \"[your-region]\":\n",
|
||||
" REGION = \"us-central1\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "timestamp"
|
||||
},
|
||||
"source": [
|
||||
"#### Timestamp\n",
|
||||
"\n",
|
||||
"If you are in a live tutorial session, you might be using a shared test account or project. To avoid name collisions between users on resources created, you create a timestamp for each instance session, and append the timestamp onto the name of resources you create in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "timestamp"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d%H%M%S\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f3bd8c0d0469"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Vertex AI Workbench Notebooks**, your environment is already authenticated. Skip this step.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions when prompted to authenticate your account via oAuth.\n",
|
||||
"\n",
|
||||
"**Otherwise**, follow these steps:\n",
|
||||
"\n",
|
||||
"In the Cloud Console, go to the [Create service account key](https://console.cloud.google.com/apis/credentials/serviceaccountkey) page.\n",
|
||||
"\n",
|
||||
"1. **Click Create service account**.\n",
|
||||
"\n",
|
||||
"2. In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"\n",
|
||||
"3. In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex AI\" into the filter box, and select **Vertex AI Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"4. Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"\n",
|
||||
"5. Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "e0953a00668e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# If you are running this notebook in Colab, run this cell and follow the\n",
|
||||
"# instructions to authenticate your GCP account. This provides access to your\n",
|
||||
"# Cloud Storage bucket and lets you submit training jobs and prediction\n",
|
||||
"# requests.\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"IS_COLAB = \"google.colab\" in sys.modules\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\") and not os.getenv(\n",
|
||||
" \"DL_ANACONDA_HOME\"\n",
|
||||
"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # If you are running this notebook locally, replace the string below with the\n",
|
||||
" # path to your service account key and run this cell to authenticate your GCP\n",
|
||||
" # account.\n",
|
||||
" elif not os.getenv(\"IS_TESTING\"):\n",
|
||||
" %env GOOGLE_APPLICATION_CREDENTIALS ''"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "setup_vars"
|
||||
},
|
||||
"source": [
|
||||
"### Set up variables\n",
|
||||
"\n",
|
||||
"Next, set up some variables used throughout the tutorial.\n",
|
||||
"### Import libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "import_aip:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import logging\n",
|
||||
"\n",
|
||||
"import google.cloud.aiplatform as aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,region"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,region"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "python_logging"
|
||||
},
|
||||
"source": [
|
||||
"## Python Logging\n",
|
||||
"\n",
|
||||
"The Python logging package is widely used for logging within Python scripts. Commonly used features:\n",
|
||||
"\n",
|
||||
"- Set logging levels.\n",
|
||||
"- Send log output to console.\n",
|
||||
"- Send log output to a file.\n",
|
||||
"\n",
|
||||
"### Logging Levels in Python Logging\n",
|
||||
"\n",
|
||||
"The logging levels in order (from least to highest) and each level inclusive of the previous level are :\n",
|
||||
"\n",
|
||||
"1. Informational\n",
|
||||
"2. Warnings\n",
|
||||
"3. Errors\n",
|
||||
"4. Debugging\n",
|
||||
"\n",
|
||||
"By default, the logging level is set to error level.\n",
|
||||
"\n",
|
||||
"### Logging output to console\n",
|
||||
"\n",
|
||||
"By default, the Python logging package outputs to the console. Note, in the example the debug log message is not outputted since the default logging level is set to error."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "python_logging"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def logging_examples():\n",
|
||||
" logging.info(\"Model training started...\")\n",
|
||||
" logging.warning(\"Using older version of package ...\")\n",
|
||||
" logging.error(\"Training was terminated ...\")\n",
|
||||
" logging.debug(\"Hyperparameters were ...\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"logging_examples()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "python_logging_level"
|
||||
},
|
||||
"source": [
|
||||
"### Setting logging level\n",
|
||||
"\n",
|
||||
"To set the logging level, you get the logging handler using `getLogger()`. You can have multiple logging handles. When `getLogger()` is called without any arguments, it gets the default handler named ROOT. With the handler, you set the logging level with the method `setLevel()`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "python_logging_level"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"logging.getLogger().setLevel(logging.DEBUG)\n",
|
||||
"\n",
|
||||
"logging_examples()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "python_logging_remove"
|
||||
},
|
||||
"source": [
|
||||
"### Clearing handlers\n",
|
||||
"\n",
|
||||
"At times, you may desire to reconfigure your logging. A common practice in this case is to first remove all existing logging handles for a fresh start."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "python_logging_remove"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for handler in logging.root.handlers[:]:\n",
|
||||
" logging.root.removeHandler(handler)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "python_logging_file"
|
||||
},
|
||||
"source": [
|
||||
"### Output to a local file\n",
|
||||
"\n",
|
||||
"You can preserve your logging output to a file that is local to where the Python script is running with the method `BasicConfig()`, that takes the following parameters:\n",
|
||||
"\n",
|
||||
"- `filename`: The file path to the local file to write the log output to.\n",
|
||||
"- `level`: Sets the level of logging that is written to the logging file.\n",
|
||||
"\n",
|
||||
"*Note:* You cannot use a Cloud Storage bucket as the output file."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "python_logging_file"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"logging.basicConfig(filename=\"mylog.log\", level=logging.DEBUG)\n",
|
||||
"\n",
|
||||
"logging_examples()\n",
|
||||
"\n",
|
||||
"! cat mylog.log"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cloud_logging"
|
||||
},
|
||||
"source": [
|
||||
"## Logging with Google Cloud Logging\n",
|
||||
"\n",
|
||||
"You can preserve and retrieve your logging output to `Google Cloud Logging` service. Commonly used features:\n",
|
||||
"\n",
|
||||
"- Set logging levels.\n",
|
||||
"- Send log output to storage.\n",
|
||||
"- Retrieve log output from storage.\n",
|
||||
"\n",
|
||||
"### Logging Levels in Cloud Logging\n",
|
||||
"\n",
|
||||
"The logging levels in order (from least to highest) are, with each level inclusive of the previous level:\n",
|
||||
"\n",
|
||||
"1. Informational\n",
|
||||
"2. Warnings\n",
|
||||
"3. Errors\n",
|
||||
"4. Debugging\n",
|
||||
"\n",
|
||||
"By default, the logging level is set to warning level.\n",
|
||||
"\n",
|
||||
"### Configurable and storing log data.\n",
|
||||
"\n",
|
||||
"To use the `Google Cloud Logging` service, you do the following steps:\n",
|
||||
"\n",
|
||||
"1. Create a client to the service.\n",
|
||||
"2. Obtain a handler for the service.\n",
|
||||
"3. Create a logger instance and set logging level.\n",
|
||||
"4. Attach logger instance to the service.\n",
|
||||
"\n",
|
||||
"Learn more about [Logging client libraries](https://cloud.google.com/logging/docs/reference/libraries)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cloud_logging"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import google.cloud.logging\n",
|
||||
"from google.cloud.logging.handlers import CloudLoggingHandler\n",
|
||||
"\n",
|
||||
"# Connect to the Cloud Logging service\n",
|
||||
"cl_client = google.cloud.logging.Client(project=PROJECT_ID)\n",
|
||||
"handler = CloudLoggingHandler(cl_client, name=\"mylog\")\n",
|
||||
"\n",
|
||||
"# Create a logger instance and logging level\n",
|
||||
"cloud_logger = logging.getLogger(\"cloudLogger\")\n",
|
||||
"cloud_logger.setLevel(logging.INFO)\n",
|
||||
"\n",
|
||||
"# Attach the logger instance to the service.\n",
|
||||
"cloud_logger.addHandler(handler)\n",
|
||||
"\n",
|
||||
"# Log something\n",
|
||||
"cloud_logger.error(\"bad news\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cloud_logging_write"
|
||||
},
|
||||
"source": [
|
||||
"### Logging output\n",
|
||||
"\n",
|
||||
"Logging output at specific levels is identical to Python logging with respect to method and method names. The only difference is that you use your instance of the cloud logger in place of logging."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cloud_logging_write"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"cloud_logger.info(\"Model training started...\")\n",
|
||||
"cloud_logger.warning(\"Using older version of package ...\")\n",
|
||||
"cloud_logger.error(\"Training was terminated ...\")\n",
|
||||
"cloud_logger.debug(\"Hyperparameters were ...\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cloud_logging_list"
|
||||
},
|
||||
"source": [
|
||||
"### Get logging entries\n",
|
||||
"\n",
|
||||
"To get the logged output, you:\n",
|
||||
"\n",
|
||||
"1. Retrieve the log handle to the service.\n",
|
||||
"2. Using the handle, call the method `list_entries()`.\n",
|
||||
"3. Iterate through the entries."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cloud_logging_list"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"logger = cl_client.logger(\"mylog\")\n",
|
||||
"\n",
|
||||
"for entry in logger.list_entries():\n",
|
||||
" timestamp = entry.timestamp.isoformat()\n",
|
||||
" print(\"* {}: {}: {}\".format(timestamp, entry.severity, entry.payload))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"# Cleaning up\n",
|
||||
"\n",
|
||||
"To clean up all Google Cloud resources used in this project, you can [delete the Google Cloud\n",
|
||||
"project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "get_started_with_logging.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -177,6 +177,39 @@ The steps performed in this tutorial include:
|
||||
- Execute the `Vertex AI Pipeline`.
|
||||
```
|
||||
|
||||
|
||||
[Get Started with Vertex AI Model Registry](get_started_with_model_registry.ipynb)
|
||||
|
||||
```
|
||||
The steps performed in this tutorial include:
|
||||
|
||||
- Create and register a first version of a model to `Vertex AI Model Registry`
|
||||
- Create and register a second version of a model to `Vertex AI Model Registry`
|
||||
- List all versions of a `Model` resource.
|
||||
- Change the default version of a `Model` resource`
|
||||
- Deploy the default version of a `Model` resource.
|
||||
- Delete a model version from a `Model` resource.
|
||||
- Delete a `Model` resource along with all model versions.
|
||||
```
|
||||
|
||||
[Get Started with AutoML Tabular Pipeline Workflow](get_started_with_automl_tabular_pipeline_workflow.ipynb)
|
||||
|
||||
```
|
||||
The steps performed in this tutorial include:
|
||||
|
||||
- Define training specification.
|
||||
- Dataset specification
|
||||
- Hyperparameter overide specification
|
||||
- machine specifications
|
||||
- Construct tabular workflow pipeline.
|
||||
- Compile and execute pipeline.
|
||||
- View evaluation metrics artifact.
|
||||
- Export AutoML model as an OSS TF model.
|
||||
- Create `Endpoint` resource.
|
||||
- Deploy exported OSS TF model.
|
||||
- Make a prediction.
|
||||
```
|
||||
|
||||
### E2E Stage Example
|
||||
|
||||
[Stage 3: Formalization](mlops_formalization.ipynb)
|
||||
|
||||
+1350
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+35
-11
@@ -40,7 +40,7 @@
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage4/get_started_with_vertex_ml_metadata_and_automl.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\\\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
@@ -255,6 +255,30 @@
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "23988890fef6"
|
||||
},
|
||||
"source": [
|
||||
"#### Get your project number\n",
|
||||
"\n",
|
||||
"Now that the project ID is set, you get your corresponding project number."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2d6950574e1d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"shell_output = ! gcloud projects list --filter=\"PROJECT_ID:'{PROJECT_ID}'\" --format='value(PROJECT_NUMBER)'\n",
|
||||
"PROJECT_NUMBER = shell_output[0]\n",
|
||||
"print(\"Project Number:\", PROJECT_NUMBER)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -730,7 +754,7 @@
|
||||
"\n",
|
||||
"artifact_item = Artifact(\n",
|
||||
" display_name=\"flowers_\" + TIMESTAMP,\n",
|
||||
" uri=\"https://us-central1-aiplatform.googleapis.com/v1/\" + dataset.resource_name,\n",
|
||||
" uri=f\"https://{REGION}-aiplatform.googleapis.com/v1/\" + dataset.resource_name,\n",
|
||||
" name=dataset.resource_name,\n",
|
||||
" schema_title=\"google.VertexDataset\",\n",
|
||||
" metadata={\"data_type\": \"image\", \"annotation_type\": \"image classification\"},\n",
|
||||
@@ -738,7 +762,7 @@
|
||||
")\n",
|
||||
"\n",
|
||||
"artifact_dataset = clients[\"metadata\"].create_artifact(\n",
|
||||
" parent=\"projects/759209241365/locations/us-central1/metadataStores/default\",\n",
|
||||
" parent=f\"projects/{PROJECT_NUMBER}/locations/{REGION}/metadataStores/default\",\n",
|
||||
" artifact=artifact_item,\n",
|
||||
" artifact_id=dataset.resource_name.split(\"/\")[-1],\n",
|
||||
")\n",
|
||||
@@ -869,7 +893,7 @@
|
||||
"source": [
|
||||
"artifact_item = Artifact(\n",
|
||||
" display_name=\"flowers_\" + TIMESTAMP,\n",
|
||||
" uri=\"https://us-central1-aiplatform.googleapis.com/v1/\" + model.resource_name,\n",
|
||||
" uri=f\"https://{REGION}-aiplatform.googleapis.com/v1/\" + model.resource_name,\n",
|
||||
" name=model.resource_name,\n",
|
||||
" schema_title=\"google.VertexModel\",\n",
|
||||
" metadata={\"model_type\": \"image classification\"},\n",
|
||||
@@ -877,7 +901,7 @@
|
||||
")\n",
|
||||
"\n",
|
||||
"artifact_model = clients[\"metadata\"].create_artifact(\n",
|
||||
" parent=\"projects/759209241365/locations/us-central1/metadataStores/default\",\n",
|
||||
" parent=f\"projects/{PROJECT_NUMBER}/locations/{REGION}/metadataStores/default\",\n",
|
||||
" artifact=artifact_item,\n",
|
||||
" artifact_id=model.resource_name.split(\"/\")[-1],\n",
|
||||
")\n",
|
||||
@@ -941,7 +965,7 @@
|
||||
"source": [
|
||||
"artifact_item = Artifact(\n",
|
||||
" display_name=\"flowers_\" + TIMESTAMP,\n",
|
||||
" uri=\"https://us-central1-aiplatform.googleapis.com/v1/\"\n",
|
||||
" uri=f\"https://{REGION}-aiplatform.googleapis.com/v1/\"\n",
|
||||
" + model_evaluations[0].resource_name,\n",
|
||||
" name=model_evaluations[0].resource_name,\n",
|
||||
" schema_title=\"system.SlicedClassificationMetrics\",\n",
|
||||
@@ -950,7 +974,7 @@
|
||||
")\n",
|
||||
"\n",
|
||||
"artifact_metrics = clients[\"metadata\"].create_artifact(\n",
|
||||
" parent=\"projects/759209241365/locations/us-central1/metadataStores/default\",\n",
|
||||
" parent=f\"projects/{PROJECT_NUMBER}/locations/{REGION}/metadataStores/default\",\n",
|
||||
" artifact=artifact_item,\n",
|
||||
" artifact_id=model_evaluations[0].resource_name.split(\"/\")[-1],\n",
|
||||
")\n",
|
||||
@@ -1011,7 +1035,7 @@
|
||||
"source": [
|
||||
"artifact_item = Artifact(\n",
|
||||
" display_name=\"flowers_\" + TIMESTAMP,\n",
|
||||
" uri=\"https://us-central1-aiplatform.googleapis.com/v1/\" + endpoint.resource_name,\n",
|
||||
" uri=f\"https://{REGION}-aiplatform.googleapis.com/v1/\" + endpoint.resource_name,\n",
|
||||
" name=endpoint.resource_name,\n",
|
||||
" schema_title=\"google.VertexEndpoint\",\n",
|
||||
" metadata={\"param\": \"value\"},\n",
|
||||
@@ -1019,7 +1043,7 @@
|
||||
")\n",
|
||||
"\n",
|
||||
"artifact_endpoint = clients[\"metadata\"].create_artifact(\n",
|
||||
" parent=\"projects/759209241365/locations/us-central1/metadataStores/default\",\n",
|
||||
" parent=f\"projects/{PROJECT_NUMBER}/locations/{REGION}/metadataStores/default\",\n",
|
||||
" artifact=artifact_item,\n",
|
||||
" artifact_id=endpoint.resource_name.split(\"/\")[-1],\n",
|
||||
")\n",
|
||||
@@ -1058,7 +1082,7 @@
|
||||
"from google.cloud.aiplatform_v1beta1.types import Execution\n",
|
||||
"\n",
|
||||
"execution = clients[\"metadata\"].create_execution(\n",
|
||||
" parent=\"projects/759209241365/locations/us-central1/metadataStores/default\",\n",
|
||||
" parent=f\"projects/{PROJECT_NUMBER}/locations/{REGION}/metadataStores/default\",\n",
|
||||
" execution=Execution(\n",
|
||||
" display_name=\"AutoML training and deployment\",\n",
|
||||
" schema_title=\"system.ContainerExecution\",\n",
|
||||
@@ -1157,7 +1181,7 @@
|
||||
"from google.cloud.aiplatform_v1beta1.types import Context\n",
|
||||
"\n",
|
||||
"context = clients[\"metadata\"].create_context(\n",
|
||||
" parent=\"projects/759209241365/locations/us-central1/metadataStores/default\",\n",
|
||||
" parent=f\"projects/{PROJECT_NUMBER}/locations/{REGION}/metadataStores/default\",\n",
|
||||
" context=Context(\n",
|
||||
" display_name=\"flowers_\" + TIMESTAMP,\n",
|
||||
" schema_title=\"system.Pipeline\",\n",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"id": "title"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex SDK: E2E ML on GCP: MLOps stage 4 : formalization: get started with Vertex AI Explanations\n",
|
||||
"# Vertex SDK: E2E ML on GCP: MLOps stage 4 : evaluation: get started with Vertex AI Explanations\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"id": "title:generic,gcp"
|
||||
},
|
||||
"source": [
|
||||
"# E2E ML on GCP: MLOps stage 5 : Get started with Vertex AI Endpoints\n",
|
||||
"# E2E ML on GCP: MLOps stage 5 : deployment: Get started with Vertex AI Endpoints\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage5/get_started_with_vertex_endpoints.ipynb\">\n",
|
||||
|
||||
@@ -171,20 +171,6 @@
|
||||
"! pip3 install --upgrade tensorflow $USER_FLAG -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "38379eb00a31"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Temporary, until feature pushed to Pypi\n",
|
||||
"! pip3 uninstall google-cloud-aiplatform -y\n",
|
||||
"\n",
|
||||
"! pip install --user git+https://github.com/googleapis/python-aiplatform.git@private-ep"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
|
||||
@@ -0,0 +1,839 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d3069d95",
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "d3069d95"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Copyright & License (click to expand)\n",
|
||||
"# Copyright 2021 Google LLC\n",
|
||||
"#\n",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"# you may not use this file except in compliance with the License.\n",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "546c53de",
|
||||
"metadata": {
|
||||
"id": "546c53de"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Batch Prediction with Model Monitoring\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_monitoring/batch_prediction_model_monitoring.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Open in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_monitoring/batch_prediction_model_monitoring.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\">\n",
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "53fd1070",
|
||||
"metadata": {
|
||||
"id": "53fd1070"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"In this notebook, you will learn how to use Model Monitoring with batch prediction requests on a deployed Vertex AI Model resource. In a companion notebook, <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/model_monitoring/model_monitoring.ipynb\" target=\"_blank\">Vertex AI Model Monitoring with Explainable AI Feature Attributions</a>, you can learn about how to apply model monitoring to streaming, real-time predictions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8b26c855",
|
||||
"metadata": {
|
||||
"id": "8b26c855"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- Vertex AI Model Monitoring\n",
|
||||
"- Vertex AI Batch Prediction\n",
|
||||
"- Vertex AI Model resource\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Upload a pre-trained model as a Vertex AI Model resource.\n",
|
||||
"- Generate batch prediction requests.\n",
|
||||
"- Interpret the statistics, visualizations, other data reported by the model monitoring feature.\n",
|
||||
"\n",
|
||||
"### Costs \n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertext AI\n",
|
||||
"* Google Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertext AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d52ba95b",
|
||||
"metadata": {
|
||||
"id": "d52ba95b"
|
||||
},
|
||||
"source": [
|
||||
"### What is Vertex AI batch prediction?\n",
|
||||
"\n",
|
||||
"<a href=\"https://cloud.google.com/vertex-ai/docs/predictions/batch-predictions\" target=\"_blank\">Batch Prediction</a> is a service that processes a collection (i.e. a batch) of machine learning inference requests in bulk, with less stringent response time requirements than real-time or streaming predictions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e64fb18a",
|
||||
"metadata": {
|
||||
"id": "e64fb18a"
|
||||
},
|
||||
"source": [
|
||||
"### What is Vertex AI model monitoring?\n",
|
||||
"\n",
|
||||
"<a href=\"https://cloud.google.com/vertex-ai/docs/model-monitoring\" target=\"_blank\">Model Monitoring</a> is a service that automatically determines \n",
|
||||
"whether production machine learning traffic differs from training data, or varies substantially over time, in terms of model predictions or feature attributions. When that happens, you can be alerted automatically and responsively, whereby, you can detect model decay which may negatively impact your operations -- such as your customer experience and/or revenue."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9d839347",
|
||||
"metadata": {
|
||||
"id": "9d839347"
|
||||
},
|
||||
"source": [
|
||||
"### The example model\n",
|
||||
"\n",
|
||||
"The model you'll use in this notebook is based on [this blog post](https://cloud.google.com/blog/topics/developers-practitioners/churn-prediction-game-developers-using-google-analytics-4-ga4-and-bigquery-ml). The idea behind this model is that your company has extensive log data describing how your game users have interacted with the site. The raw data contains the following categories of information:\n",
|
||||
"\n",
|
||||
"- identity - unique player identitity numbers\n",
|
||||
"- demographic features - information about the player, such as the geographic region in which a player is located\n",
|
||||
"- behavioral features - counts of the number of times a player has triggered certain game events, such as reaching a new level\n",
|
||||
"- churn propensity - this is the label or target feature, it provides an estimated probability that this player will churn, i.e. stop being an active player.\n",
|
||||
"\n",
|
||||
"The blog article referenced above explains how to use BigQuery to store the raw data, pre-process it for use in machine learning, and train a model. Because this notebook focuses on model monitoring, rather than training models, you're going to reuse a pre-trained version of this model, which has been exported to Google Cloud Storage. In the next section, you will setup your environment and import this model into your own project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "738fce1f",
|
||||
"metadata": {
|
||||
"id": "738fce1f"
|
||||
},
|
||||
"source": [
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"Install the packages required for executing this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4536fe4e",
|
||||
"metadata": {
|
||||
"id": "4536fe4e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"assert sys.version_info.major == 3, \"This notebook requires Python 3.\"\n",
|
||||
"\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\") and not os.getenv(\"VIRTUAL_ENV\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"# Install Python package dependencies.\n",
|
||||
"! pip3 install -q tensorflow-data-validation $USER_FLAG\n",
|
||||
"! pip3 install -q google-api-core $USER_FLAG\n",
|
||||
"! pip3 install -q google-cloud-aiplatform $USER_FLAG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6e98402b",
|
||||
"metadata": {
|
||||
"id": "6e98402b"
|
||||
},
|
||||
"source": [
|
||||
"### Restart the kernel\n",
|
||||
"\n",
|
||||
"After you install the SDK, you need to restart the notebook kernel so it can find the packages. You can restart kernel from *Kernel -> Restart Kernel*, or running the following:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9775c9ff",
|
||||
"metadata": {
|
||||
"id": "9775c9ff"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Automatically restart kernel after installs\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d5737134",
|
||||
"metadata": {
|
||||
"id": "d5737134"
|
||||
},
|
||||
"source": [
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\n",
|
||||
"\n",
|
||||
"1. If you are running this notebook locally, you will need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
"1. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cfb1a1d5",
|
||||
"metadata": {
|
||||
"id": "cfb1a1d5"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, you may be able to get your project ID using `gcloud`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cf8535e4",
|
||||
"metadata": {
|
||||
"id": "cf8535e4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"PROJECT_ID = \"\"\n",
|
||||
"\n",
|
||||
"# Get your Google Cloud project ID from gcloud\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" shell_output = ! gcloud config list --format 'value(core.project)' 2>/dev/null\n",
|
||||
" PROJECT_ID = shell_output[0]\n",
|
||||
" print(\"Project ID: \", PROJECT_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "05a2d397",
|
||||
"metadata": {
|
||||
"id": "05a2d397"
|
||||
},
|
||||
"source": [
|
||||
"Otherwise, set your project ID here.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1c2be4bd",
|
||||
"metadata": {
|
||||
"id": "1c2be4bd"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if PROJECT_ID == \"\" or PROJECT_ID is None:\n",
|
||||
" PROJECT_ID = \"python-docs-samples-tests\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c129705c",
|
||||
"metadata": {
|
||||
"id": "c129705c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "71404c9f",
|
||||
"metadata": {
|
||||
"id": "71404c9f"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your email address\n",
|
||||
"This is used for delivering model monitoring notifications.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4b1d2b69",
|
||||
"metadata": {
|
||||
"id": "4b1d2b69"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"EMAIL_ADDRESS = \"[your-email-address]\" # @param {type:\"string\"}\n",
|
||||
"if not EMAIL_ADDRESS or EMAIL_ADDRESS == \"[your-email-address]\":\n",
|
||||
" print(\"EMAIL_ADDRESS not specified, please correct before proceeding.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "83340af4",
|
||||
"metadata": {
|
||||
"id": "83340af4"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your region\n",
|
||||
"\n",
|
||||
"You can also change the `REGION` variable, which is used for operations\n",
|
||||
"throughout the rest of this notebook. Below are regions supported for Vertex AI. We recommend that you choose the region closest to you.\n",
|
||||
"\n",
|
||||
"- Americas: `us-central1`\n",
|
||||
"- Europe: `europe-west4`\n",
|
||||
"- Asia Pacific: `asia-east1`\n",
|
||||
"\n",
|
||||
"You may not use a multi-regional bucket for training with Vertex AI. Not all regions provide support for all Vertex AI services.\n",
|
||||
"\n",
|
||||
"Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4814ea21",
|
||||
"metadata": {
|
||||
"id": "4814ea21"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "20a546c3",
|
||||
"metadata": {
|
||||
"id": "20a546c3"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Vertex AI Workbench notebooks**, your environment is already\n",
|
||||
"authenticated. Skip this step.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions\n",
|
||||
"when prompted to authenticate your account via oAuth.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "06c51076",
|
||||
"metadata": {
|
||||
"id": "06c51076"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# If you are running this notebook in Colab, run this cell and follow the\n",
|
||||
"# instructions to authenticate your GCP account. This provides access to your\n",
|
||||
"# Cloud Storage bucket and lets you submit training jobs and prediction\n",
|
||||
"# requests.\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# Determine notebook environment.\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\") and not os.getenv(\"VIRTUAL_ENV\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"IS_COLAB = \"google.colab\" in sys.modules\n",
|
||||
"\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"if not IS_WORKBENCH_NOTEBOOK and not IS_USER_MANAGED_WORKBENCH_NOTEBOOK:\n",
|
||||
" if IS_COLAB:\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # If you are running this notebook locally, replace the string below with the\n",
|
||||
" # path to your service account key and run this cell to authenticate your GCP\n",
|
||||
" # account.\n",
|
||||
" elif not os.getenv(\"IS_TESTING\"):\n",
|
||||
" %env GOOGLE_APPLICATION_CREDENTIALS ''"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6b01af18",
|
||||
"metadata": {
|
||||
"id": "6b01af18"
|
||||
},
|
||||
"source": [
|
||||
"### Upload the model\n",
|
||||
"\n",
|
||||
"The churn propensity model you'll be using in this notebook has been trained in BigQuery ML and exported to a Google Cloud Storage bucket. This illustrates how you can easily export a trained model and move a model from one cloud service to another. \n",
|
||||
"\n",
|
||||
"Next, import the model. **If you've already imported your model, you can skip this step.**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9638ad2c",
|
||||
"metadata": {
|
||||
"id": "9638ad2c"
|
||||
},
|
||||
"source": [
|
||||
"<span id=\"papermill-error-cell\" style=\"color:red; font-family:Helvetica Neue, Helvetica, Arial, sans-serif; font-size:2em;\">Execution using papermill encountered an exception here and stopped:</span>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "926e3ba8",
|
||||
"metadata": {
|
||||
"id": "926e3ba8"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"import json\n",
|
||||
"import time\n",
|
||||
"import re\n",
|
||||
"import tensorflow as tf\n",
|
||||
"import tensorflow_data_validation as tfdv\n",
|
||||
"from tensorflow_data_validation.utils import io_util \n",
|
||||
"from tensorflow_metadata.proto.v0 import statistics_pb2\n",
|
||||
"\n",
|
||||
"MODEL_DISPLAY_NAME=f\"batch_prediction_monitoring_test_model_{datetime.now().strftime('%Y%m%d%H%M%S')}\"\n",
|
||||
"CONTAINER_IMAGE_URI=\"us-docker.pkg.dev/cloud-aiplatform/prediction/tf2-cpu.2-4:latest\"\n",
|
||||
"ARTIFACT_URI=\"gs://mco-mm/churn\"\n",
|
||||
"\n",
|
||||
"output = ! gcloud ai models upload \\\n",
|
||||
" --region=$REGION \\\n",
|
||||
" --display-name=$MODEL_DISPLAY_NAME \\\n",
|
||||
" --artifact-uri=$ARTIFACT_URI \\\n",
|
||||
" --container-image-uri=$CONTAINER_IMAGE_URI \\\n",
|
||||
" --format=\"value(model)\"\n",
|
||||
"MODEL_ID = output[1].split(\"/\")[5]\n",
|
||||
"print(f\"Model {MODEL_ID} created.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a4305ddf",
|
||||
"metadata": {
|
||||
"id": "a4305ddf"
|
||||
},
|
||||
"source": [
|
||||
"## Submit a batch prediction request with model monitoring enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "053fde99",
|
||||
"metadata": {
|
||||
"id": "053fde99"
|
||||
},
|
||||
"source": [
|
||||
"### Create Cloud Storage buckets\n",
|
||||
"\n",
|
||||
"This cell creates two Cloud Storage buckets:\n",
|
||||
"\n",
|
||||
"- `gs://PROJECT_ID_bp_mm_input` contains the batch prediction request data.\n",
|
||||
"- `gs://PROJECT_ID_bp_mm_output` contains the batch prediction output as well as the model monitoring results."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b832ad31",
|
||||
"metadata": {
|
||||
"id": "b832ad31"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copy files to your projects gs bucket to avoid permission issues.\n",
|
||||
"# Ignore any error(s) for bucket already exists.\n",
|
||||
"OUTPUT_GS_PATH = f\"gs://{PROJECT_ID.replace('-', '_')}_bp_mm_output\"\n",
|
||||
"INPUT_GS_PATH = f\"gs://{PROJECT_ID.replace('-', '_')}_bp_mm_input\"\n",
|
||||
"PUBLIC_TRAINING_DATASET = \"gs://bp_mm_public_data/churn/churn_bp_insample.csv\"\n",
|
||||
"TRAINING_DATASET = f\"{INPUT_GS_PATH}/churn_bp_insample.csv\"\n",
|
||||
"TRAINING_DATASET_FORMAT = \"csv\"\n",
|
||||
"\n",
|
||||
"! gsutil mb -p {PROJECT_ID} -l {REGION} -b on {INPUT_GS_PATH}\n",
|
||||
"! gsutil mb -p {PROJECT_ID} -l {REGION} -b on {OUTPUT_GS_PATH}\n",
|
||||
"! gsutil copy $PUBLIC_TRAINING_DATASET $INPUT_GS_PATH"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "34c95126",
|
||||
"metadata": {
|
||||
"id": "34c95126"
|
||||
},
|
||||
"source": [
|
||||
"### Create batch prediction job\n",
|
||||
"\n",
|
||||
"This step parameterizes and builds the data structure representing the batch prediction request, with model monitoring enabled.\n",
|
||||
"\n",
|
||||
"The BatchPredictionJob object specifies the input source, the data format, and the computing resources requests for the batch prediction. Learn more about <a href=\"https://cloud.google.com/vertex-ai/docs/samples/aiplatform-create-batch-prediction-job-sample\" target=\"_blank\">BatchPredictionJob</a>.\n",
|
||||
"\n",
|
||||
"The ModelMonitoringConfig object specifies the alerting email address, the training dataset, the features to be monitored, and their associated alerting thresholds. Learn more about <a href=\"https://cloud.google.com/vertex-ai/docs/model-monitoring\" target=\"_blank\">ModelMonitoringConfig</a>."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3a54368a",
|
||||
"metadata": {
|
||||
"id": "3a54368a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"now = datetime.now()\n",
|
||||
"INPUT_URI = \"gs://bp_mm_public_data/churn/churn_bp_outsample.jsonl\"\n",
|
||||
"OUTPUT_URI = OUTPUT_GS_PATH\n",
|
||||
"INSTANCES_FORMAT = \"jsonl\"\n",
|
||||
"PREDICTIONS_FORMAT = \"jsonl\"\n",
|
||||
"JOB_NAME_PREFIX = \"bp_mm_demo\"\n",
|
||||
"MODEL_NAME = f\"projects/{PROJECT_ID}/locations/{REGION}/models/{MODEL_ID}\"\n",
|
||||
"MACHINE_TYPE = \"n1-standard-8\"\n",
|
||||
"BATCH_PREDICTION_JOB_NAME = JOB_NAME_PREFIX + \"_\" + now.strftime(\"%Y%m%d%H%M%S\")\n",
|
||||
"\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import (\n",
|
||||
" BatchDedicatedResources, BatchPredictionJob, GcsDestination, GcsSource,\n",
|
||||
" MachineSpec, ModelMonitoringAlertConfig, ModelMonitoringConfig,\n",
|
||||
" ModelMonitoringObjectiveConfig, ThresholdConfig)\n",
|
||||
"\n",
|
||||
"batch_prediction_job = BatchPredictionJob(\n",
|
||||
" display_name=BATCH_PREDICTION_JOB_NAME,\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" input_config=BatchPredictionJob.InputConfig(\n",
|
||||
" instances_format=INSTANCES_FORMAT, gcs_source=GcsSource(uris=[INPUT_URI])\n",
|
||||
" ),\n",
|
||||
" output_config=BatchPredictionJob.OutputConfig(\n",
|
||||
" predictions_format=PREDICTIONS_FORMAT,\n",
|
||||
" gcs_destination=GcsDestination(output_uri_prefix=OUTPUT_URI),\n",
|
||||
" ),\n",
|
||||
" dedicated_resources=BatchDedicatedResources(\n",
|
||||
" machine_spec=MachineSpec(machine_type=MACHINE_TYPE),\n",
|
||||
" starting_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
" ),\n",
|
||||
" # Model monitoring service will be triggerred if provide following configs.\n",
|
||||
" model_monitoring_config=ModelMonitoringConfig(\n",
|
||||
" alert_config=ModelMonitoringAlertConfig(\n",
|
||||
" email_alert_config=ModelMonitoringAlertConfig.EmailAlertConfig(\n",
|
||||
" user_emails=[EMAIL_ADDRESS]\n",
|
||||
" )\n",
|
||||
" ),\n",
|
||||
" objective_configs=[\n",
|
||||
" ModelMonitoringObjectiveConfig(\n",
|
||||
" training_dataset=ModelMonitoringObjectiveConfig.TrainingDataset(\n",
|
||||
" data_format=TRAINING_DATASET_FORMAT,\n",
|
||||
" gcs_source=GcsSource(uris=[TRAINING_DATASET]),\n",
|
||||
" ),\n",
|
||||
" training_prediction_skew_detection_config=ModelMonitoringObjectiveConfig.TrainingPredictionSkewDetectionConfig(\n",
|
||||
" skew_thresholds={\n",
|
||||
" \"cnt_user_engagement\": ThresholdConfig(value=0.001),\n",
|
||||
" \"julianday\": ThresholdConfig(value=0.001),\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
" )\n",
|
||||
" ],\n",
|
||||
" ),\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cae39778",
|
||||
"metadata": {
|
||||
"id": "cae39778"
|
||||
},
|
||||
"source": [
|
||||
"### Submit batch prediction job\n",
|
||||
"\n",
|
||||
"This step submits the batch prediction request created in the previous step. If successful, it returns a JSON document summarizing the request, which is displayed in the cell output below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bcdd4a47",
|
||||
"metadata": {
|
||||
"id": "bcdd4a47"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform_v1beta1.services.job_service import \\\n",
|
||||
" JobServiceClient\n",
|
||||
"\n",
|
||||
"API_ENDPOINT = f\"{REGION}-aiplatform.googleapis.com\"\n",
|
||||
"client = JobServiceClient(client_options={\"api_endpoint\": API_ENDPOINT})\n",
|
||||
"out = client.create_batch_prediction_job(\n",
|
||||
" parent=f\"projects/{PROJECT_ID}/locations/{REGION}\",\n",
|
||||
" batch_prediction_job=batch_prediction_job,\n",
|
||||
")\n",
|
||||
"BATCH_PREDICTION_JOB_ID = out.name.split(\"/\")[-1]\n",
|
||||
"print(\"BATCH_PREDICTION_JOB_ID:\", BATCH_PREDICTION_JOB_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "49ec90a0",
|
||||
"metadata": {
|
||||
"id": "49ec90a0"
|
||||
},
|
||||
"source": [
|
||||
"## Verify prediction results\n",
|
||||
"\n",
|
||||
"The batch prediction request will be completed after about **17 mins**, and the model monitoring result will be available **10 mins** after that. The request below obtains the batch prediction job id, which is a unique number associated with asynchronous requests like this one."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c30496b5",
|
||||
"metadata": {
|
||||
"id": "c30496b5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# If auto-testing, wait for request completion\n",
|
||||
"if os.getenv(\"IS_TESTING\"):\n",
|
||||
" time.sleep(1800)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "831651c2",
|
||||
"metadata": {
|
||||
"id": "831651c2"
|
||||
},
|
||||
"source": [
|
||||
"### Browse storage bucket\n",
|
||||
"\n",
|
||||
"Click on the link below, which opens the Cloud Storage object viewer on the output bucket you created above, to see the results of your batch prediction request.\n",
|
||||
"\n",
|
||||
"When everything is ready, you'll see two folders in this bucket (the precise names will vary):\n",
|
||||
"\n",
|
||||
"- `prediction-batch_prediction_monitoring_test_model_20220714100939-2022_07_14T03_10_10_069Z` - this folder contains the your batch prediction results, i.e. the predictions produced by your model for each input in the batch\n",
|
||||
"- `job-8794345073597743104` - this folder contains the model monitoring results, including the model schema, monitoring thresholds and other config settings, statistics, and anomalies\n",
|
||||
"\n",
|
||||
"*Note:* that until the request and the monitoring job are completed, you may see empty or partial results in this bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a705c10b",
|
||||
"metadata": {
|
||||
"id": "a705c10b"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(f\"https://console.cloud.google.com/storage/browser/{OUTPUT_URI.lstrip('gs://')}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2bbdddac",
|
||||
"metadata": {
|
||||
"id": "2bbdddac"
|
||||
},
|
||||
"source": [
|
||||
"### Visualize the batch prediction result\n",
|
||||
"\n",
|
||||
"Run the following cell to examine the batch prediction results with a tabular and visual analysis using the <a href=\"https://www.tensorflow.org/tfx/data_validation/get_started\" target=\"_blank\">TensorFlow Data Validation</a> package"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f6c674e9",
|
||||
"metadata": {
|
||||
"id": "f6c674e9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! rm -f ./training_stats.pb\n",
|
||||
"! rm -f ./prediction_stats.pb\n",
|
||||
"\n",
|
||||
"TRAINING_STATS_SUBPATH = \"stats_training/stats/training_stats\"\n",
|
||||
"PREDICTION_STATS_SUBPATH = \"stats_and_anomalies/stats/current_stats\"\n",
|
||||
"STATS_GCS_FOLDER = OUTPUT_URI = (\n",
|
||||
" OUTPUT_GS_PATH + \"/job-\" + BATCH_PREDICTION_JOB_ID + \"/bp_monitoring/\"\n",
|
||||
")\n",
|
||||
"TRAINING_STATS_GCS_PATH = STATS_GCS_FOLDER + TRAINING_STATS_SUBPATH\n",
|
||||
"print(\"Looking up statistics from: \" + TRAINING_STATS_GCS_PATH)\n",
|
||||
"PREDICTION_STATS_GCS_PATH = STATS_GCS_FOLDER + PREDICTION_STATS_SUBPATH\n",
|
||||
"print(\"Looking up statistics from: \" + PREDICTION_STATS_GCS_PATH)\n",
|
||||
"\n",
|
||||
"! gsutil cp $TRAINING_STATS_GCS_PATH ./training_stats.pb\n",
|
||||
"! gsutil cp $PREDICTION_STATS_GCS_PATH ./prediction_stats.pb\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# util function to load stats binary file from GCS\n",
|
||||
"def load_stats_binary(input_path):\n",
|
||||
" stats_proto = statistics_pb2.DatasetFeatureStatisticsList()\n",
|
||||
" stats_proto.ParseFromString(\n",
|
||||
" io_util.read_file_to_string(input_path, binary_mode=True)\n",
|
||||
" )\n",
|
||||
" return stats_proto\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tfdv.visualize_statistics(load_stats_binary(\"./training_stats.pb\"))\n",
|
||||
"tfdv.visualize_statistics(load_stats_binary(\"./prediction_stats.pb\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "233b1266",
|
||||
"metadata": {
|
||||
"id": "233b1266"
|
||||
},
|
||||
"source": [
|
||||
"### Check skew results\n",
|
||||
"\n",
|
||||
"Finally, you can check the skew results by examining a file in the output bucket. The JSON file contains a report indicating how much the batch prediction data deviates from the training data, on a feature-by-feature basis."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4e8c00a7",
|
||||
"metadata": {
|
||||
"id": "4e8c00a7"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"SKEW_GS_PATH = (\n",
|
||||
" STATS_GCS_FOLDER\n",
|
||||
" + \"stats_and_anomalies/anomalies/training_prediction_skew_anomalies\"\n",
|
||||
")\n",
|
||||
"! gsutil cat $SKEW_GS_PATH"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "497a0016",
|
||||
"metadata": {
|
||||
"id": "497a0016"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up\n",
|
||||
"\n",
|
||||
"To clean up all Google Cloud resources used in this project, you can [delete the Google Cloud\n",
|
||||
"project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "eabc3f81",
|
||||
"metadata": {
|
||||
"id": "eabc3f81"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete model resource\n",
|
||||
"! gcloud ai models delete $MODEL_NAME --quiet\n",
|
||||
"\n",
|
||||
"# Delete Cloud Storage resources\n",
|
||||
"! gsutil -m rm -r $INPUT_GS_PATH\n",
|
||||
"! gsutil -m rm -r $OUTPUT_GS_PATH"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0aa0219d",
|
||||
"metadata": {
|
||||
"id": "0aa0219d"
|
||||
},
|
||||
"source": [
|
||||
"## Learn more...\n",
|
||||
"\n",
|
||||
"**Congratulations!** You've now learned how to monitor batch predictions, and how to find and interpret the results. Check out the following resources to learn more about model monitoring and ML Ops.\n",
|
||||
"\n",
|
||||
"- [TensorFlow Data Validation](https://www.tensorflow.org/tfx/guide/tfdv)\n",
|
||||
"- [Data Understanding, Validation, and Monitoring At Scale](https://blog.tensorflow.org/2018/09/introducing-tensorflow-data-validation.html)\n",
|
||||
"- [Vertex Product Documentation](https://cloud.google.com/vertex-ai)\n",
|
||||
"- [Vertex AI Model Monitoring Reference Docs](https://cloud.google.com/vertex-ai/docs/reference)\n",
|
||||
"- [Vertex AI Model Monitoring blog article](https://cloud.google.com/blog/topics/developers-practitioners/monitor-models-training-serving-skew-vertex-ai)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"collapsed_sections": [],
|
||||
"name": "batch_prediction_model_monitoring.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copyright 2021 Google LLC\n",
|
||||
"# Copyright 2022 Google LLC\n",
|
||||
"#\n",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"# you may not use this file except in compliance with the License.\n",
|
||||
@@ -42,6 +42,11 @@
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/laeg/vertex-ai-samples/main/notebooks/community/neo4j/graph_paysim.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
"</td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
@@ -143,7 +148,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install --quiet --upgrade neo4j"
|
||||
"!pip install --quiet --upgrade graphdatascience==1.0.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -255,7 +260,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"from neo4j import GraphDatabase"
|
||||
"from graphdatascience import GraphDataScience"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -266,7 +271,19 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"driver = GraphDatabase.driver(DB_URL, auth=(DB_USER, DB_PASS))"
|
||||
"# If you are connecting the client to an AuraDS instance, you can get the recommended non-default configuration settings of the Python Driver applied automatically. To achieve this, set the constructor argument aura_ds=True\n",
|
||||
"gds = GraphDataScience(DB_URL, auth=(DB_USER, DB_PASS), aura_ds=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "f14915ddd1fb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"gds.set_database(DB_NAME)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -287,19 +304,16 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# node labels\n",
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" CALL db.labels() YIELD label\n",
|
||||
" CALL apoc.cypher.run('MATCH (:`'+label+'`) RETURN count(*) as freq', {})\n",
|
||||
" YIELD value\n",
|
||||
" RETURN label, value.freq AS freq\n",
|
||||
"result = gds.run_cypher(\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )\n",
|
||||
"df = pd.DataFrame(result)\n",
|
||||
"display(df)"
|
||||
"CALL db.labels() YIELD label\n",
|
||||
"CALL apoc.cypher.run('MATCH (:`'+label+'`) RETURN count(*) as freq', {})\n",
|
||||
"YIELD value\n",
|
||||
"RETURN label, value.freq AS freq\n",
|
||||
"\"\"\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"display(result)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -311,20 +325,17 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# relationship types\n",
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" CALL db.relationshipTypes() YIELD relationshipType as type\n",
|
||||
" CALL apoc.cypher.run('MATCH ()-[:`'+type+'`]->() RETURN count(*) as freq', {})\n",
|
||||
" YIELD value\n",
|
||||
" RETURN type AS relationshipType, value.freq AS freq\n",
|
||||
" ORDER by freq DESC\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )\n",
|
||||
"df = pd.DataFrame(result)\n",
|
||||
"display(df)"
|
||||
"result = gds.run_cypher(\n",
|
||||
" \"\"\"\n",
|
||||
"CALL db.relationshipTypes() YIELD relationshipType as type\n",
|
||||
"CALL apoc.cypher.run('MATCH ()-[:`'+type+'`]->() RETURN count(*) as freq', {})\n",
|
||||
"YIELD value\n",
|
||||
"RETURN type AS relationshipType, value.freq AS freq\n",
|
||||
"ORDER by freq DESC\n",
|
||||
"\"\"\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"display(result)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -336,23 +347,20 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# transaction types\n",
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" MATCH (t:Transaction)\n",
|
||||
" WITH sum(t.amount) AS globalSum, count(t) AS globalCnt\n",
|
||||
" WITH *, 10^3 AS scaleFactor\n",
|
||||
" UNWIND ['CashIn', 'CashOut', 'Payment', 'Debit', 'Transfer'] AS txType\n",
|
||||
" CALL apoc.cypher.run('MATCH (t:' + txType + ')\n",
|
||||
" RETURN sum(t.amount) as txAmount, count(t) AS txCnt', {})\n",
|
||||
" YIELD value\n",
|
||||
" RETURN txType,value.txAmount AS TotalMarketValue\n",
|
||||
"result = gds.run_cypher(\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )\n",
|
||||
"df = pd.DataFrame(result)\n",
|
||||
"display(df)"
|
||||
" MATCH (t:Transaction)\n",
|
||||
" WITH sum(t.amount) AS globalSum, count(t) AS globalCnt\n",
|
||||
" WITH *, 10^3 AS scaleFactor\n",
|
||||
" UNWIND ['CashIn', 'CashOut', 'Payment', 'Debit', 'Transfer'] AS txType\n",
|
||||
" CALL apoc.cypher.run('MATCH (t:' + txType + ')\n",
|
||||
" RETURN sum(t.amount) as txAmount, count(t) AS txCnt', {})\n",
|
||||
" YIELD value\n",
|
||||
" RETURN txType,value.txAmount AS TotalMarketValue\n",
|
||||
" \"\"\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"display(result)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -375,18 +383,14 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" CALL gds.graph.create.cypher('client_graph', \n",
|
||||
" 'MATCH (c:Client) RETURN id(c) as id, c.num_transactions as num_transactions, c.total_transaction_amnt as total_transaction_amnt, c.is_fraudster as is_fraudster',\n",
|
||||
" 'MATCH (c:Client)-[:PERFORMED]->(t:Transaction)-[:TO]->(c2:Client) return id(c) as source, id(c2) as target, sum(t.amount) as amount, \"TRANSACTED_WITH\" as type ')\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )\n",
|
||||
"df = pd.DataFrame(result)\n",
|
||||
"display(df)"
|
||||
"# We get a tuple back with an object that represents the graph projection and the results of the GDS call\n",
|
||||
"G, results = gds.graph.project.cypher(\n",
|
||||
" \"client_graph\",\n",
|
||||
" \"MATCH (c:Client) RETURN id(c) as id, c.num_transactions as num_transactions, c.total_transaction_amnt as total_transaction_amnt, c.is_fraudster as is_fraudster\",\n",
|
||||
" 'MATCH (c:Client)-[:PERFORMED]->(t:Transaction)-[:TO]->(c2:Client) return id(c) as source, id(c2) as target, sum(t.amount) as amount, \"TRANSACTED_WITH\" as type ',\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"display(results)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -406,25 +410,19 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" CALL gds.fastRP.mutate('client_graph',{\n",
|
||||
" relationshipWeightProperty:'amount',\n",
|
||||
" iterationWeights: [0.0, 1.00, 1.00, 0.80, 0.60],\n",
|
||||
" featureProperties: ['num_transactions', 'total_transaction_amnt'],\n",
|
||||
" propertyRatio: 0.25, \n",
|
||||
" nodeSelfInfluence: 0.15,\n",
|
||||
" embeddingDimension: 16,\n",
|
||||
" randomSeed: 1, \n",
|
||||
" mutateProperty:'embedding'\n",
|
||||
" })\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )\n",
|
||||
"df = pd.DataFrame(result)\n",
|
||||
"display(df)"
|
||||
"results = gds.fastRP.mutate(\n",
|
||||
" G,\n",
|
||||
" relationshipWeightProperty=\"amount\",\n",
|
||||
" iterationWeights=[0.0, 1.00, 1.00, 0.80, 0.60],\n",
|
||||
" featureProperties=[\"num_transactions\", \"total_transaction_amnt\"],\n",
|
||||
" propertyRatio=0.25,\n",
|
||||
" nodeSelfInfluence=0.15,\n",
|
||||
" embeddingDimension=16,\n",
|
||||
" randomSeed=1,\n",
|
||||
" mutateProperty=\"embedding\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"display(result)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -444,19 +442,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" CALL gds.graph.streamNodeProperties\n",
|
||||
" ('client_graph', ['embedding', 'num_transactions', 'total_transaction_amnt', 'is_fraudster'])\n",
|
||||
" YIELD nodeId, nodeProperty, propertyValue\n",
|
||||
" RETURN nodeId, nodeProperty, propertyValue\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )\n",
|
||||
"df = pd.DataFrame(result)\n",
|
||||
"df.head()"
|
||||
"node_properties = gds.graph.streamNodeProperties(\n",
|
||||
" G, [\"embedding\", \"num_transactions\", \"total_transaction_amnt\", \"is_fraudster\"]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"node_properties.head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -476,7 +466,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = df.pivot(index=\"nodeId\", columns=\"nodeProperty\", values=\"propertyValue\")\n",
|
||||
"x = node_properties.pivot(\n",
|
||||
" index=\"nodeId\", columns=\"nodeProperty\", values=\"propertyValue\"\n",
|
||||
")\n",
|
||||
"x = x.reset_index()\n",
|
||||
"x.columns.name = None\n",
|
||||
"x.head()"
|
||||
@@ -699,8 +691,8 @@
|
||||
"id": "ArK3cfKsdT1x"
|
||||
},
|
||||
"source": [
|
||||
"## Train and deploy a model on GCP\n",
|
||||
"We'll use the engineered features to train an AutoML Tables model, then deploy it to an endpoint"
|
||||
"## Train and deploy a model with Vertex AI\n",
|
||||
"We'll use the engineered features to train an AutoML Tabular Data, then deploy it to an endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -782,8 +774,8 @@
|
||||
"id": "-NnDaATyWY7z"
|
||||
},
|
||||
"source": [
|
||||
"## Loading Data into GCP Feature Store\n",
|
||||
"In this section, we'll take our dataframe with newly engineered features and load that into GCP feature store."
|
||||
"## Loading Data into Vertex AI Feature Store\n",
|
||||
"In this section, we'll take our dataframe with newly engineered features and load that into Vertex AI Feature Store."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1083,14 +1075,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with driver.session(database=DB_NAME) as session:\n",
|
||||
" result = session.read_transaction(\n",
|
||||
" lambda tx: tx.run(\n",
|
||||
" \"\"\"\n",
|
||||
" CALL gds.graph.drop('client_graph')\n",
|
||||
" \"\"\"\n",
|
||||
" ).data()\n",
|
||||
" )"
|
||||
"gds.graph.drop(G)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+1427
File diff suppressed because it is too large
Load Diff
+1760
File diff suppressed because it is too large
Load Diff
+1866
File diff suppressed because it is too large
Load Diff
+1770
File diff suppressed because it is too large
Load Diff
+1921
File diff suppressed because it is too large
Load Diff
+1857
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@
|
||||
/migration @aferlitsch
|
||||
/explainabl_ai @aferlitsch
|
||||
/pipelines @aferlitsch
|
||||
/experiments @inardini
|
||||
|
||||
/model_monitoring/model_monitoring.ipynb @mco-gh
|
||||
/ml_metadata/sdk-metric-parameter-tracking-for-custom-jobs.ipynb @jialuzh
|
||||
@@ -23,3 +24,4 @@
|
||||
/pipelines/google_cloud_pipeline_components_bqml_text.ipynb @inardini
|
||||
/pipelines/google_cloud_pipelines_dataproc_tabular @inardini
|
||||
/automl/automl_forecasting_bqml_arima_plus_comparison.ipynb @TheMichaelHu
|
||||
/automl/automl_tabular_on_vertex_pipelines.ipynb @helinwang
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/ai/platform/notebooks/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/tree/main/notebooks/official/automl/automl-text-classification.ipynb\">\n",
|
||||
" <a href=\"https://console.cloud.google.com/ai/platform/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/official/automl/automl-text-classification.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
@@ -61,7 +61,7 @@
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook walks you through the major phases of building and using a text classification model on [Vertex AI](https://cloud.google.com/vertex-ai/docs/). \n",
|
||||
"This notebook walks you through the major phases of building and using an AutoML text classification model on [Vertex AI](https://cloud.google.com/vertex-ai/docs/). \n",
|
||||
"\n",
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
@@ -74,7 +74,7 @@
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `AutoML Training`\n",
|
||||
"- `Vertex AI Model resource`\n",
|
||||
"- `Vertex AI Model` resource\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
@@ -192,7 +192,7 @@
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install {USER_FLAG} --upgrade google-cloud-aiplatform google-cloud-storage jsonlines"
|
||||
"! pip3 install {USER_FLAG} --upgrade google-cloud-aiplatform google-cloud-storage jsonlines -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -536,7 +536,7 @@
|
||||
"id": "32c971919605"
|
||||
},
|
||||
"source": [
|
||||
"## Create a dataset and import your data\n",
|
||||
"## Create a `Dataset` resource and import your data\n",
|
||||
"\n",
|
||||
"The notebook uses the 'Happy Moments' dataset for demonstration purposes. You can change it to another text classification dataset that [conforms to the data preparation requirements](https://cloud.google.com/vertex-ai/docs/datasets/prepare-text#classification).\n",
|
||||
"\n",
|
||||
@@ -584,7 +584,7 @@
|
||||
"source": [
|
||||
"## Train your text classification model\n",
|
||||
"\n",
|
||||
"Once your dataset has finished importing data, you are ready to train your model. To do this, you first need the full resource name of your dataset, where the full name has the format `projects/[YOUR_PROJECT]/locations/us-central1/datasets/[YOUR_DATASET_ID]`. If you don't have the resource name handy, you can list all of the datasets in your project using `TextDataset.list()`. \n",
|
||||
"Once your dataset has finished importing data, you are ready to train your model. To do this, you first need the full resource name of your dataset, where the full name has the format `projects/[YOUR_PROJECT]/locations/[YOUR_REGIO)N]/datasets/[YOUR_DATASET_ID]`. If you don't have the resource name handy, you can list all of the datasets in your project using `TextDataset.list()`. \n",
|
||||
"\n",
|
||||
"As shown in the following code block, you can pass in the display name of your dataset in the call to `list()` to filter the results.\n"
|
||||
]
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/automl/automl_forecasting_bqml_arima_plus_comparison.ipynb\">\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/official/automl/automl_forecasting_bqml_arima_plus_comparison.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1258
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1059,15 +1059,11 @@
|
||||
"DRIFT_DEFAULT_THRESHOLDS = \"country,cnt_user_engagement\" # @param {type:\"string\"}\n",
|
||||
"DRIFT_CUSTOM_THRESHOLDS = \"cnt_level_start_quickplay:.01\" # @param {type:\"string\"}\n",
|
||||
"ATTRIB_SKEW_DEFAULT_THRESHOLDS = \"country,cnt_user_engagement\" # @param {type:\"string\"}\n",
|
||||
"ATTRIB_SKEW_CUSTOM_THRESHOLDS = (\n",
|
||||
" \"cnt_level_start_quickplay:.01\" # @param {type:\"string\"}\n",
|
||||
")\n",
|
||||
"ATTRIB_DRIFT_DEFAULT_THRESHOLDS = (\n",
|
||||
" \"country,cnt_user_engagement\" # @param {type:\"string\"}\n",
|
||||
")\n",
|
||||
"ATTRIB_DRIFT_CUSTOM_THRESHOLDS = (\n",
|
||||
" \"cnt_level_start_quickplay:.01\" # @param {type:\"string\"}\n",
|
||||
")"
|
||||
"# fmt: off\n",
|
||||
"ATTRIB_SKEW_CUSTOM_THRESHOLDS = \"cnt_level_start_quickplay:.01\" # @param {type:\"string\"}\n",
|
||||
"ATTRIB_DRIFT_DEFAULT_THRESHOLDS = \"country,cnt_user_engagement\" # @param {type:\"string\"}\n",
|
||||
"ATTRIB_DRIFT_CUSTOM_THRESHOLDS = \"cnt_level_start_quickplay:.01\" # @param {type:\"string\"}\n",
|
||||
"# fmt: on"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -33,20 +33,21 @@
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb\">\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb\">\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\">\n",
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb\">\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"</table>\n",
|
||||
"<br/><br/><br/>"
|
||||
@@ -83,14 +84,25 @@
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you create a custom image classification model using Vertex AI Pipelines with pre-built Google Cloud Pipeline Components for custom training.\n",
|
||||
"In this tutorial, you learn to use `Vertex AI Pipelines` and `Google Cloud Pipeline Components` to build a custom model.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `Vertex AI Pipelines`\n",
|
||||
"- `Google Cloud Pipeline Components`\n",
|
||||
"- `Vertex AI Training`\n",
|
||||
"- `Vertex AI Model` resource\n",
|
||||
"- `Vertex AI Endpoint` resource\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Train a custom model.\n",
|
||||
"- Upload the trained model as a `Model` resource.\n",
|
||||
"- Create an `Endpoint` resource.\n",
|
||||
"- Deploy the `Model` resource to the `Endpoint` resource.\n",
|
||||
"- Create a KFP pipeline:\n",
|
||||
" - Train a custom model.\n",
|
||||
" - Upload the trained model as a `Model` resource.\n",
|
||||
" - Create an `Endpoint` resource.\n",
|
||||
" - Deploy the `Model` resource to the `Endpoint` resource.\n",
|
||||
" - Make a batch prediction request.\n",
|
||||
"\n",
|
||||
"Learn more about [Google Cloud Pipeline Components](https://cloud.google.com/vertex-ai/docs/pipelines/build-pipeline)."
|
||||
]
|
||||
@@ -123,7 +135,7 @@
|
||||
"source": [
|
||||
"### Set up your local development environment\n",
|
||||
"\n",
|
||||
"If you are using Colab or Google Cloud Notebook, your environment already meets all the requirements to run this notebook. You can skip this step.\n",
|
||||
"If you are using Colab or Vertex AI Workbench Notebook, your environment already meets all the requirements to run this notebook. You can skip this step.\n",
|
||||
"\n",
|
||||
"Otherwise, make sure your environment meets this notebook's requirements. You need the following:\n",
|
||||
"\n",
|
||||
@@ -156,7 +168,7 @@
|
||||
"source": [
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"Install the latest version of Vertex AI SDK for Python."
|
||||
"Install the packages required for executing this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -169,63 +181,21 @@
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# Google Cloud Notebook\n",
|
||||
"if os.path.exists(\"/opt/deeplearning/metadata/env_version\") or os.getenv(\"IS_TESTING\"):\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\") and not os.getenv(\"VIRTUAL_ENV\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"else:\n",
|
||||
" USER_FLAG = \"\"\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform {USER_FLAG} -q\n",
|
||||
"! pip3 install -U google-cloud-storage {USER_FLAG} -q\n",
|
||||
"! pip3 install {USER_FLAG} kfp google-cloud-pipeline-components --upgrade -q\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform $USER_FLAG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_storage"
|
||||
},
|
||||
"source": [
|
||||
"Install the latest GA version of *google-cloud-storage* library as well."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "install_storage"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install -U google-cloud-storage $USER_FLAG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_gcpc"
|
||||
},
|
||||
"source": [
|
||||
"Install the latest GA version of *google-cloud-pipeline-components* library as well."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "install_gcpc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install kfp google-cloud-pipeline-components --upgrade $USER_FLAG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b12be5c73a33"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! pip3 install --upgrade --force-reinstall $USER_FLAG tensorflow==2.5 kfp google-cloud-aiplatform google-cloud-storage google-cloud-pipeline-components"
|
||||
]
|
||||
@@ -389,23 +359,30 @@
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Google Cloud Notebook**, your environment is already authenticated. Skip this step.\n",
|
||||
"**If you are using Vertex AI Workbench Notebook**, your environment is already authenticated. Skip this step.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions when prompted to authenticate your account via oAuth.\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions\n",
|
||||
"when prompted to authenticate your account via oAuth.\n",
|
||||
"\n",
|
||||
"**Otherwise**, follow these steps:\n",
|
||||
"\n",
|
||||
"In the Cloud Console, go to the [Create service account key](https://console.cloud.google.com/apis/credentials/serviceaccountkey) page.\n",
|
||||
"1. In the Cloud Console, go to the [**Create service account key**\n",
|
||||
" page](https://console.cloud.google.com/apis/credentials/serviceaccountkey).\n",
|
||||
"\n",
|
||||
"**Click Create service account**.\n",
|
||||
"2. Click **Create service account**.\n",
|
||||
"\n",
|
||||
"In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"3. In the **Service account name** field, enter a name, and\n",
|
||||
" click **Create**.\n",
|
||||
"\n",
|
||||
"In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex\" into the filter box, and select **Vertex Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"4. In the **Grant this service account access to project** section, click the **Role** drop-down list. Type \"Vertex AI\"\n",
|
||||
"into the filter box, and select\n",
|
||||
" **Vertex AI Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"5. Click *Create*. A JSON file that contains your key downloads to your\n",
|
||||
"local environment.\n",
|
||||
"\n",
|
||||
"Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
"6. Enter the path to your service account key as the\n",
|
||||
"`GOOGLE_APPLICATION_CREDENTIALS` variable in the cell below and run the cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -424,8 +401,11 @@
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# If on Google Cloud Notebook, then don't execute this code\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\"):\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"IS_COLAB = \"google.colab\" in sys.modules\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\") and not os.getenv(\n",
|
||||
" \"DL_ANACONDA_HOME\"\n",
|
||||
"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
@@ -473,8 +453,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if BUCKET_URI == \"\" or BUCKET_URI is None or BUCKET_URI == \"gs://[your-bucket-name]\":\n",
|
||||
" BUCKET_URI = \"gs://\" + PROJECT_ID + \"aip-\" + TIMESTAMP"
|
||||
"if BUCKET_NAME == \"\" or BUCKET_NAME is None or BUCKET_NAME == \"[your-bucket-name]\":\n",
|
||||
" BUCKET_NAME = PROJECT_ID + \"aip-\" + TIMESTAMP\n",
|
||||
" BUCKET_URI = \"gs://\" + BUCKET_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -552,9 +533,16 @@
|
||||
" or SERVICE_ACCOUNT is None\n",
|
||||
" or SERVICE_ACCOUNT == \"[your-service-account]\"\n",
|
||||
"):\n",
|
||||
" # Get your GCP project id from gcloud\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].strip()\n",
|
||||
" # Get your service account from gcloud\n",
|
||||
" if not IS_COLAB:\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].replace(\"*\", \"\").strip()\n",
|
||||
"\n",
|
||||
" if IS_COLAB:\n",
|
||||
" shell_output = ! gcloud projects describe $PROJECT_ID\n",
|
||||
" project_number = shell_output[-1].split(\":\")[1].strip().replace(\"'\", \"\")\n",
|
||||
" SERVICE_ACCOUNT = f\"{project_number}-compute@developer.gserviceaccount.com\"\n",
|
||||
"\n",
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
@@ -602,7 +590,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import google.cloud.aiplatform as aip"
|
||||
"import google.cloud.aiplatform as aip\n",
|
||||
"import tensorflow as tf\n",
|
||||
"from google_cloud_pipeline_components.experimental.custom_job import utils\n",
|
||||
"from kfp.v2 import compiler, dsl\n",
|
||||
"from kfp.v2.dsl import component"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -627,29 +619,6 @@
|
||||
"PIPELINE_ROOT = \"{}/pipeline_root/bikes_weather\".format(BUCKET_URI)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "additional_imports"
|
||||
},
|
||||
"source": [
|
||||
"Additional imports."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "e3fca6d3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import tensorflow as tf\n",
|
||||
"from google_cloud_pipeline_components.experimental.custom_job import utils\n",
|
||||
"from kfp.v2 import compiler, dsl\n",
|
||||
"from kfp.v2.dsl import component"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -684,7 +653,7 @@
|
||||
"\n",
|
||||
"Set the variables `TRAIN_GPU/TRAIN_NGPU` and `DEPLOY_GPU/DEPLOY_NGPU` to use a container image supporting a GPU and the number of GPUs allocated to the virtual machine (VM) instance. For example, to use a GPU container image with 4 Nvidia Telsa K80 GPUs allocated to each VM, you would specify:\n",
|
||||
"\n",
|
||||
" (aip.AcceleratorType.NVIDIA_TESLA_K80, 4)\n",
|
||||
" (aip.gapic.AcceleratorType.NVIDIA_TESLA_K80, 4)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Otherwise specify `(None, None)` to use a container image to run on a CPU.\n",
|
||||
@@ -749,7 +718,7 @@
|
||||
"if os.getenv(\"IS_TESTING_TF\"):\n",
|
||||
" TF = os.getenv(\"IS_TESTING_TF\")\n",
|
||||
"else:\n",
|
||||
" TF = \"2-1\"\n",
|
||||
" TF = \"2-5\"\n",
|
||||
"\n",
|
||||
"if TF[0] == \"2\":\n",
|
||||
" if TRAIN_GPU:\n",
|
||||
@@ -1389,8 +1358,9 @@
|
||||
"batch_job = aip.BatchPredictionJob(batch_job_id)\n",
|
||||
"batch_job.delete()\n",
|
||||
"\n",
|
||||
"# uncomment to delete your bucket\n",
|
||||
"# ! gsutil rm -rf {BUCKET_URI}"
|
||||
"delete_bucket = False\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil rm -rf {BUCKET_URI}"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -33,18 +33,18 @@
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/pipelines/pipelines_intro_kfp.ipynb\">\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/pipelines/pipelines_intro_kfp.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/pipelines/pipelines_intro_kfp.ipynb\">\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/pipelines/pipelines_intro_kfp.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\">\n",
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/official/pipelines/pipelines_intro_kfp.ipynb\">\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/pipelines/pipelines_intro_kfp.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
@@ -72,9 +72,15 @@
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you use the KFP SDK to build pipelines that generate evaluation metrics.\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `Vertex AI Pipelines`\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Define and compile a pipeline.\n",
|
||||
"- Define and compile a `Vertex AI` pipeline.\n",
|
||||
"- Schedule a recurring pipeline run.\n",
|
||||
"- Specify which service account to use for a pipeline run."
|
||||
]
|
||||
@@ -111,7 +117,7 @@
|
||||
"source": [
|
||||
"### Set up your local development environment\n",
|
||||
"\n",
|
||||
"If you are using Colab or Google Cloud Notebook, your environment already meets all the requirements to run this notebook. You can skip this step.\n",
|
||||
"If you are using Colab or Vertex AI Workbench Notebook, your environment already meets all the requirements to run this notebook. You can skip this step.\n",
|
||||
"\n",
|
||||
"Otherwise, make sure your environment meets this notebook's requirements. You need the following:\n",
|
||||
"\n",
|
||||
@@ -144,7 +150,7 @@
|
||||
"source": [
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"Install the latest version of Vertex AI SDK for Python."
|
||||
"Install the packages required for executing this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -157,53 +163,20 @@
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# Google Cloud Notebook\n",
|
||||
"if os.path.exists(\"/opt/deeplearning/metadata/env_version\"):\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"else:\n",
|
||||
" USER_FLAG = \"\"\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\") and not os.getenv(\"VIRTUAL_ENV\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform $USER_FLAG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_storage"
|
||||
},
|
||||
"source": [
|
||||
"Install the latest GA version of *google-cloud-storage* library as well."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Q9cY6x132Ouw"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install -U google-cloud-storage $USER_FLAG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_kfp"
|
||||
},
|
||||
"source": [
|
||||
"Install the latest GA version of *KFP SDK* library as well."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "nbULuPjF2Oux"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install $USER kfp --upgrade"
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform {USER_FLAG} -q\n",
|
||||
"! pip3 install -U google-cloud-storage {USER_FLAG} -q\n",
|
||||
"! pip3 install {USER_FLAG} kfp google-cloud-pipeline-components --upgrade -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -397,23 +370,30 @@
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Google Cloud Notebook**, your environment is already authenticated. Skip this step.\n",
|
||||
"**If you are using Vertex AI Workbench Notebook**, your environment is already authenticated. Skip this step.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions when prompted to authenticate your account via oAuth.\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions\n",
|
||||
"when prompted to authenticate your account via oAuth.\n",
|
||||
"\n",
|
||||
"**Otherwise**, follow these steps:\n",
|
||||
"\n",
|
||||
"In the Cloud Console, go to the [Create service account key](https://console.cloud.google.com/apis/credentials/serviceaccountkey) page.\n",
|
||||
"1. In the Cloud Console, go to the [**Create service account key**\n",
|
||||
" page](https://console.cloud.google.com/apis/credentials/serviceaccountkey).\n",
|
||||
"\n",
|
||||
"**Click Create service account**.\n",
|
||||
"2. Click **Create service account**.\n",
|
||||
"\n",
|
||||
"In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"3. In the **Service account name** field, enter a name, and\n",
|
||||
" click **Create**.\n",
|
||||
"\n",
|
||||
"In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex\" into the filter box, and select **Vertex Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"4. In the **Grant this service account access to project** section, click the **Role** drop-down list. Type \"Vertex AI\"\n",
|
||||
"into the filter box, and select\n",
|
||||
" **Vertex AI Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"5. Click *Create*. A JSON file that contains your key downloads to your\n",
|
||||
"local environment.\n",
|
||||
"\n",
|
||||
"Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
"6. Enter the path to your service account key as the\n",
|
||||
"`GOOGLE_APPLICATION_CREDENTIALS` variable in the cell below and run the cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -432,8 +412,11 @@
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# If on Google Cloud Notebook, then don't execute this code\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\"):\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"IS_COLAB = \"google.colab\" in sys.modules\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\") and not os.getenv(\n",
|
||||
" \"DL_ANACONDA_HOME\"\n",
|
||||
"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
@@ -481,8 +464,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if BUCKET_URI == \"\" or BUCKET_URI is None or BUCKET_URI == \"gs://[your-bucket-name]\":\n",
|
||||
" BUCKET_URI = \"gs://\" + PROJECT_ID + \"aip-\" + TIMESTAMP"
|
||||
"if BUCKET_NAME == \"\" or BUCKET_NAME is None or BUCKET_NAME == \"[your-bucket-name]\":\n",
|
||||
" BUCKET_NAME = PROJECT_ID + \"aip-\" + TIMESTAMP\n",
|
||||
" BUCKET_URI = \"gs://\" + BUCKET_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -560,12 +544,15 @@
|
||||
" or SERVICE_ACCOUNT is None\n",
|
||||
" or SERVICE_ACCOUNT == \"[your-service-account]\"\n",
|
||||
"):\n",
|
||||
" # Get your service account from gcloud\n",
|
||||
" if not IS_COLAB:\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].replace(\"*\", \"\").strip()\n",
|
||||
"\n",
|
||||
" # Get your GCP project id from gcloud\n",
|
||||
"\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
"\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].replace(\"*\", \"\").strip()\n",
|
||||
" if IS_COLAB:\n",
|
||||
" shell_output = ! gcloud projects describe $PROJECT_ID\n",
|
||||
" project_number = shell_output[-1].split(\":\")[1].strip().replace(\"'\", \"\")\n",
|
||||
" SERVICE_ACCOUNT = f\"{project_number}-compute@developer.gserviceaccount.com\"\n",
|
||||
"\n",
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
@@ -614,7 +601,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import google.cloud.aiplatform as aip"
|
||||
"from typing import NamedTuple\n",
|
||||
"\n",
|
||||
"import google.cloud.aiplatform as aip\n",
|
||||
"from kfp import dsl\n",
|
||||
"from kfp.v2 import compiler\n",
|
||||
"from kfp.v2.dsl import component"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -664,30 +656,6 @@
|
||||
"PIPELINE_ROOT = \"{}/pipeline_root/intro\".format(BUCKET_URI)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "additional_imports"
|
||||
},
|
||||
"source": [
|
||||
"Additional imports."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "import_kfp:namedtuple"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import NamedTuple\n",
|
||||
"\n",
|
||||
"from kfp import dsl\n",
|
||||
"from kfp.v2 import compiler\n",
|
||||
"from kfp.v2.dsl import component"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -1267,7 +1235,7 @@
|
||||
"except Exception as e:\n",
|
||||
" print(e)\n",
|
||||
"\n",
|
||||
"if delete_bucket and \"BUCKET_URI\" in globals():\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil rm -r $BUCKET_URI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
"source": [
|
||||
"### Install additional packages\n",
|
||||
"\n",
|
||||
"Install additional package dependencies not installed in your notebook environment, such as {XGBoost, AdaNet, or TensorFlow Hub TODO: Replace with relevant packages for the tutorial}. Use the latest major GA version of each package."
|
||||
"Install additional package dependencies not installed in your notebook environment, such as XGBoost, AdaNet, or TensorFlow Hub. Use the latest major GA version of each package."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -247,7 +247,7 @@
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com). {TODO: Update the APIs needed for your tutorial. Edit the API names, and update the link to append the API IDs, separating each one with a comma. For example, container.googleapis.com,cloudbuild.googleapis.com}\n",
|
||||
"1. [Enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com).\n",
|
||||
"\n",
|
||||
"1. If you are running this notebook locally, you will need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
|
||||
Reference in New Issue
Block a user