mirror of
https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
synced 2026-09-26 14:42:04 +00:00
59 KiB
59 KiB
In [ ]:
# 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.In [ ]:
import os
# The Vertex AI Workbench Notebook product has specific requirements
IS_WORKBENCH_NOTEBOOK = os.getenv("DL_ANACONDA_HOME")
IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(
"/opt/deeplearning/metadata/env_version"
)
# Vertex AI Notebook requires dependencies to be installed with '--user'
USER_FLAG = ""
if IS_WORKBENCH_NOTEBOOK:
USER_FLAG = "--user"
! pip3 install --upgrade google-cloud-aiplatform kfp fsspec gcsfs {USER_FLAG} -q --no-warn-conflictsIn [ ]:
# Automatically restart kernel after installs
import os
if not os.getenv("IS_TESTING"):
# Automatically restart kernel after installs
import IPython
app = IPython.Application.instance()
app.kernel.do_shutdown(True)In [ ]:
PROJECT_ID = "[your-project-id]" # @param {type:"string"}In [ ]:
if PROJECT_ID == "" or PROJECT_ID is None or PROJECT_ID == "[your-project-id]":
# Get your GCP project id from gcloud
shell_output = ! gcloud config list --format 'value(core.project)' 2>/dev/null
PROJECT_ID = shell_output[0]
print("Project ID:", PROJECT_ID)In [ ]:
! gcloud config set project $PROJECT_IDIn [ ]:
REGION = "[your-region]" # @param {type: "string"}
if REGION == "[your-region]":
REGION = "us-central1"In [ ]:
import random
import string
# Generate a uuid of a specifed length(default=8)
def generate_uuid(length: int = 8) -> str:
return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
UUID = generate_uuid()In [ ]:
# If you are running this notebook in Colab, run this cell and follow the
# instructions to authenticate your GCP account. This provides access to your
# Cloud Storage bucket and lets you submit training jobs and prediction
# requests.
import os
import sys
# If on Vertex AI Workbench, then don't execute this code
IS_COLAB = "google.colab" in sys.modules
if not os.path.exists("/opt/deeplearning/metadata/env_version") and not os.getenv(
"DL_ANACONDA_HOME"
):
if "google.colab" in sys.modules:
from google.colab import auth as google_auth
google_auth.authenticate_user()
# If you are running this notebook locally, replace the string below with the
# path to your service account key and run this cell to authenticate your GCP
# account.
elif not os.getenv("IS_TESTING"):
%env GOOGLE_APPLICATION_CREDENTIALS '[your-service-account-key-path]'In [ ]:
shell_output = ! gcloud projects list --filter="PROJECT_ID:'{PROJECT_ID}'" --format='value(PROJECT_NUMBER)'
PROJECT_NUMBER = shell_output[0]
print("Project Number:", PROJECT_NUMBER)In [ ]:
BUCKET_NAME = "[your-bucket-name]" # @param {type:"string"}
BUCKET_URI = f"gs://{BUCKET_NAME}"In [ ]:
if BUCKET_NAME == "" or BUCKET_NAME is None or BUCKET_NAME == "[your-bucket-name]":
BUCKET_NAME = PROJECT_ID + "-aip-" + UUID
BUCKET_URI = f"gs://{BUCKET_NAME}"In [ ]:
! gcloud storage buckets create --location=$REGION --project=$PROJECT_ID $BUCKET_URIIn [ ]:
! gcloud storage ls --all-versions --long $BUCKET_URIIn [ ]:
SERVICE_ACCOUNT = "[your-service-account]" # @param {type:"string"}In [ ]:
if (
SERVICE_ACCOUNT == ""
or SERVICE_ACCOUNT is None
or SERVICE_ACCOUNT == "[your-service-account]"
):
# Get your service account from gcloud
if not IS_COLAB:
shell_output = !gcloud auth list 2>/dev/null
SERVICE_ACCOUNT = shell_output[2].replace("*", "").strip()
else: # IS_COLAB:
shell_output = ! gcloud projects describe $PROJECT_ID
project_number = shell_output[-1].split(":")[1].strip().replace("'", "")
SERVICE_ACCOUNT = f"{project_number}-compute@developer.gserviceaccount.com"
print("Service Account:", SERVICE_ACCOUNT)In [ ]:
! gcloud storage buckets add-iam-policy-binding $BUCKET_URI --member=serviceAccount:{SERVICE_ACCOUNT} --role=roles/storage.objectCreator
! gcloud storage buckets add-iam-policy-binding $BUCKET_URI --member=serviceAccount:{SERVICE_ACCOUNT} --role=roles/storage.objectViewerIn [ ]:
import kfp.v2.compiler as compiler
import kfp.v2.dsl as dsl
import pandas as pd
import tensorflow as tf
from google.cloud import aiplatform as vertex_ai
from kfp.v2.dsl import Metrics, Model, Output, component
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.layers import IntegerLookup, Normalization, StringLookupIn [ ]:
EXPERIMENT_NAME = "[your-experiment-name]" # @param {type:"string"}In [ ]:
if EXPERIMENT_NAME == "[your-experiment-name]" or EXPERIMENT_NAME is None:
EXPERIMENT_NAME = "my-experiment-" + UUIDIn [ ]:
def dataframe_to_dataset(dataframe):
"""
Convert a Pandas dataframe to a tf.data.Dataset.
Args:
dataframe: Pandas dataframe
Returns:
tf.data.Dataset
"""
dataframe = dataframe.copy()
labels = dataframe.pop("target")
ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
return ds
def encode_numerical_feature(feature, name, dataset):
"""
Create a normalizer and encode a numerical feature.
Args:
feature: the feature to encode
name: the name of the feature
dataset: tf.data.Dataset
Returns:
the encoded feature
"""
# Create a Normalization layer for our feature
normalizer = Normalization()
# Prepare a Dataset that only yields our feature
feature_ds = dataset.map(lambda x, y: x[name])
feature_ds = feature_ds.map(lambda x: tf.expand_dims(x, -1))
# Learn the statistics of the data
normalizer.adapt(feature_ds)
# Normalize the input feature
encoded_feature = normalizer(feature)
return encoded_feature
def encode_categorical_feature(feature, name, dataset, is_string):
"""
Encode a categorical feature.
Args:
feature: the feature to encode
name: the name of the feature
dataset: tf.data.Dataset
is_string: whether the feature is a string
Returns:
the encoded feature
"""
lookup_class = StringLookup if is_string else IntegerLookup
# Create a lookup layer which will turn strings into integer indices
lookup = lookup_class(output_mode="binary")
# Prepare a Dataset that only yields our feature
feature_ds = dataset.map(lambda x, y: x[name])
feature_ds = feature_ds.map(lambda x: tf.expand_dims(x, -1))
# Learn the set of possible string values and assign them a fixed integer index
lookup.adapt(feature_ds)
# Turn the string input into integer indices
encoded_feature = lookup(feature)
return encoded_featureIn [ ]:
vertex_ai.init(project=PROJECT_ID, location=REGION, staging_bucket=BUCKET_URI)In [ ]:
vertex_ai_tb = vertex_ai.Tensorboard.create()In [ ]:
vertex_ai.init(experiment=EXPERIMENT_NAME, experiment_tensorboard=vertex_ai_tb)In [ ]:
RUN_NAME = "run-1"
my_run = vertex_ai.start_run(RUN_NAME)In [ ]:
file_url = "http://storage.googleapis.com/download.tensorflow.org/data/heart.csv"
dataset_artifact = vertex_ai.Artifact.create(
schema_title="system.Dataset",
resource_id=f"{EXPERIMENT_NAME}-heart-data",
uri=file_url,
display_name="heart data",
)In [ ]:
params = dict(
dataset_uri=dataset_artifact.uri,
dataset_fraction_split=0.2,
dataset_batch=32,
random_state=1337,
)
vertex_ai.log_params(params)
vertex_ai.get_experiment_df()In [ ]:
dataframe = pd.read_csv(file_url)
dataframe.head()In [ ]:
with vertex_ai.start_execution(
schema_title="system.ContainerExecution", display_name=f"{RUN_NAME} data split"
) as exc:
exc.assign_input_artifacts([dataset_artifact])
# Train, test and validation split
val_dataframe = dataframe.sample(
frac=params["dataset_fraction_split"], random_state=params["random_state"]
)
test_dataframe = val_dataframe.sample(
frac=params["dataset_fraction_split"], random_state=params["random_state"]
)
train_dataframe = dataframe.drop(val_dataframe.index)
train_uri = f"{BUCKET_URI}/{EXPERIMENT_NAME}/{RUN_NAME}/data/heart_train.csv"
test_uri = f"{BUCKET_URI}/{EXPERIMENT_NAME}/{RUN_NAME}/data/heart_test.csv"
val_uri = f"{BUCKET_URI}/{EXPERIMENT_NAME}/{RUN_NAME}/data/heart_val.csv"
# Materialize data
train_dataframe.to_csv(train_uri)
val_dataframe.to_csv(val_uri)
test_dataframe.to_csv(val_uri)
# Create Vertex AI Datasets
train_metadata = vertex_ai.Artifact.create(
schema_title="system.Dataset", uri=train_uri, display_name="train split"
)
val_metadata = vertex_ai.Artifact.create(
schema_title="system.Dataset", uri=val_uri, display_name="val split"
)
test_metadata = vertex_ai.Artifact.create(
schema_title="system.Dataset", uri=test_uri, display_name="test split"
)
exc.assign_output_artifacts([train_metadata, val_metadata, test_metadata])In [ ]:
train_ds = (
dataframe_to_dataset(train_dataframe)
.batch(params["dataset_batch"])
.shuffle(buffer_size=len(train_dataframe))
)
val_ds = dataframe_to_dataset(val_dataframe).batch(params["dataset_batch"])
test_ds = dataframe_to_dataset(test_dataframe).batch(params["dataset_batch"])In [ ]:
# Categorical features encoded as integers
sex = keras.Input(shape=(1,), name="sex", dtype="int64")
cp = keras.Input(shape=(1,), name="cp", dtype="int64")
fbs = keras.Input(shape=(1,), name="fbs", dtype="int64")
restecg = keras.Input(shape=(1,), name="restecg", dtype="int64")
exang = keras.Input(shape=(1,), name="exang", dtype="int64")
ca = keras.Input(shape=(1,), name="ca", dtype="int64")
# Categorical feature encoded as string
thal = keras.Input(shape=(1,), name="thal", dtype="string")
# Numerical features
age = keras.Input(shape=(1,), name="age")
trestbps = keras.Input(shape=(1,), name="trestbps")
chol = keras.Input(shape=(1,), name="chol")
thalach = keras.Input(shape=(1,), name="thalach")
oldpeak = keras.Input(shape=(1,), name="oldpeak")
slope = keras.Input(shape=(1,), name="slope")
all_inputs = [
sex,
cp,
fbs,
restecg,
exang,
ca,
thal,
age,
trestbps,
chol,
thalach,
oldpeak,
slope,
]
# Integer categorical features
sex_encoded = encode_categorical_feature(sex, "sex", train_ds, False)
cp_encoded = encode_categorical_feature(cp, "cp", train_ds, False)
fbs_encoded = encode_categorical_feature(fbs, "fbs", train_ds, False)
restecg_encoded = encode_categorical_feature(restecg, "restecg", train_ds, False)
exang_encoded = encode_categorical_feature(exang, "exang", train_ds, False)
ca_encoded = encode_categorical_feature(ca, "ca", train_ds, False)
# String categorical features
thal_encoded = encode_categorical_feature(thal, "thal", train_ds, True)
# Numerical features
age_encoded = encode_numerical_feature(age, "age", train_ds)
trestbps_encoded = encode_numerical_feature(trestbps, "trestbps", train_ds)
chol_encoded = encode_numerical_feature(chol, "chol", train_ds)
thalach_encoded = encode_numerical_feature(thalach, "thalach", train_ds)
oldpeak_encoded = encode_numerical_feature(oldpeak, "oldpeak", train_ds)
slope_encoded = encode_numerical_feature(slope, "slope", train_ds)
all_features = layers.concatenate(
[
sex_encoded,
cp_encoded,
fbs_encoded,
restecg_encoded,
exang_encoded,
slope_encoded,
ca_encoded,
thal_encoded,
age_encoded,
trestbps_encoded,
chol_encoded,
thalach_encoded,
oldpeak_encoded,
]
)In [ ]:
params.update(n_units=32, activation="relu", dropout_rate=0.5)
vertex_ai.log_params(params)
vertex_ai.get_experiment_df()In [ ]:
x = layers.Dense(params["n_units"], activation=params["activation"])(all_features)
x = layers.Dropout(params["dropout_rate"])(x)
output = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(all_inputs, output)
model.compile("adam", "binary_crossentropy", metrics=["accuracy"])In [ ]:
with vertex_ai.start_execution(
schema_title="system.ContainerExecution", display_name=f"{RUN_NAME} train"
) as exc:
exc.assign_input_artifacts([train_metadata, val_metadata, test_metadata])
params.update(epochs=10)
vertex_ai.log_params(params)
history = model.fit(train_ds, epochs=params["epochs"], validation_data=val_ds)
for i in range(history.params["epochs"]):
vertex_ai.log_time_series_metrics(
dict(
train_loss=history.history["loss"][i],
train_accuracy=history.history["accuracy"][i],
val_loss=history.history["val_loss"][i],
val_accuracy=history.history["val_accuracy"][i],
)
)
metrics = model.evaluate(test_ds, return_dict=True)
vertex_ai.log_metrics(
dict(
loss=metrics["loss"],
accurancy=metrics["accuracy"],
)
)
model_uri = f"{BUCKET_URI}/{EXPERIMENT_NAME}/{RUN_NAME}/model/"
model.save(model_uri)
model_metadata = vertex_ai.Artifact.create(
schema_title="system.Model", uri=model_uri, display_name="trained heart model"
)
exc.assign_output_artifacts([model_metadata])In [ ]:
vertex_ai.get_experiment_df()In [ ]:
my_run.get_time_series_data_frame()In [ ]:
print("Vertex AI Experiments:")
print(
f"https://console.cloud.google.com/ai/platform/experiments/experiments?folder=&organizationId=&project={PROJECT_ID}"
)In [ ]:
vertex_ai.end_run()In [ ]:
@component(packages_to_install=["tensorflow", "pandas"])
def tabular_trainer(
dataset_uri: str,
dataset_fraction_split: float,
dataset_batch: int,
random_state: int,
n_units: int,
activation: str,
dropout_rate: float,
epochs: int,
metrics: Output[Metrics],
model_metadata: Output[Model],
):
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.layers import (IntegerLookup, Normalization,
StringLookup)
dataframe = pd.read_csv(dataset_uri)
dataframe.head()
val_dataframe = dataframe.sample(
frac=dataset_fraction_split, random_state=random_state
)
test_dataframe = val_dataframe.sample(
frac=dataset_fraction_split, random_state=random_state
)
train_dataframe = dataframe.drop(val_dataframe.index)
def dataframe_to_dataset(dataframe):
"""
Convert a Pandas dataframe to a tf.data.Dataset.
Args:
dataframe: Pandas dataframe
Returns:
tf.data.Dataset
"""
dataframe = dataframe.copy()
labels = dataframe.pop("target")
ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
return ds
train_ds = (
dataframe_to_dataset(train_dataframe)
.batch(dataset_batch)
.shuffle(buffer_size=len(train_dataframe))
)
val_ds = dataframe_to_dataset(val_dataframe).batch(dataset_batch)
test_ds = dataframe_to_dataset(test_dataframe).batch(dataset_batch)
def encode_numerical_feature(feature, name, dataset):
"""
Create a normalizer and encode a numerical feature.
Args:
feature: the feature to encode
name: the name of the feature
dataset: tf.data.Dataset
Returns:
the encoded feature
"""
# Create a Normalization layer for our feature
normalizer = Normalization()
# Prepare a Dataset that only yields our feature
feature_ds = dataset.map(lambda x, y: x[name])
feature_ds = feature_ds.map(lambda x: tf.expand_dims(x, -1))
# Learn the statistics of the data
normalizer.adapt(feature_ds)
# Normalize the input feature
encoded_feature = normalizer(feature)
return encoded_feature
def encode_categorical_feature(feature, name, dataset, is_string):
"""
Encode a categorical feature.
Args:
feature: the feature to encode
name: the name of the feature
dataset: tf.data.Dataset
is_string: whether the feature is a string
Returns:
the encoded feature
"""
lookup_class = StringLookup if is_string else IntegerLookup
# Create a lookup layer which will turn strings into integer indices
lookup = lookup_class(output_mode="binary")
# Prepare a Dataset that only yields our feature
feature_ds = dataset.map(lambda x, y: x[name])
feature_ds = feature_ds.map(lambda x: tf.expand_dims(x, -1))
# Learn the set of possible string values and assign them a fixed integer index
lookup.adapt(feature_ds)
# Turn the string input into integer indices
encoded_feature = lookup(feature)
return encoded_feature
# Categorical features encoded as integers
sex = keras.Input(shape=(1,), name="sex", dtype="int64")
cp = keras.Input(shape=(1,), name="cp", dtype="int64")
fbs = keras.Input(shape=(1,), name="fbs", dtype="int64")
restecg = keras.Input(shape=(1,), name="restecg", dtype="int64")
exang = keras.Input(shape=(1,), name="exang", dtype="int64")
ca = keras.Input(shape=(1,), name="ca", dtype="int64")
# Categorical feature encoded as string
thal = keras.Input(shape=(1,), name="thal", dtype="string")
# Numerical features
age = keras.Input(shape=(1,), name="age")
trestbps = keras.Input(shape=(1,), name="trestbps")
chol = keras.Input(shape=(1,), name="chol")
thalach = keras.Input(shape=(1,), name="thalach")
oldpeak = keras.Input(shape=(1,), name="oldpeak")
slope = keras.Input(shape=(1,), name="slope")
all_inputs = [
sex,
cp,
fbs,
restecg,
exang,
ca,
thal,
age,
trestbps,
chol,
thalach,
oldpeak,
slope,
]
# Integer categorical features
sex_encoded = encode_categorical_feature(sex, "sex", train_ds, False)
cp_encoded = encode_categorical_feature(cp, "cp", train_ds, False)
fbs_encoded = encode_categorical_feature(fbs, "fbs", train_ds, False)
restecg_encoded = encode_categorical_feature(restecg, "restecg", train_ds, False)
exang_encoded = encode_categorical_feature(exang, "exang", train_ds, False)
ca_encoded = encode_categorical_feature(ca, "ca", train_ds, False)
# String categorical features
thal_encoded = encode_categorical_feature(thal, "thal", train_ds, True)
# Numerical features
age_encoded = encode_numerical_feature(age, "age", train_ds)
trestbps_encoded = encode_numerical_feature(trestbps, "trestbps", train_ds)
chol_encoded = encode_numerical_feature(chol, "chol", train_ds)
thalach_encoded = encode_numerical_feature(thalach, "thalach", train_ds)
oldpeak_encoded = encode_numerical_feature(oldpeak, "oldpeak", train_ds)
slope_encoded = encode_numerical_feature(slope, "slope", train_ds)
all_features = layers.concatenate(
[
sex_encoded,
cp_encoded,
fbs_encoded,
restecg_encoded,
exang_encoded,
slope_encoded,
ca_encoded,
thal_encoded,
age_encoded,
trestbps_encoded,
chol_encoded,
thalach_encoded,
oldpeak_encoded,
]
)
x = layers.Dense(n_units, activation=activation)(all_features)
x = layers.Dropout(dropout_rate)(x)
output = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(all_inputs, output)
model.compile("adam", "binary_crossentropy", metrics=["accuracy"])
model.fit(train_ds, epochs=epochs, validation_data=val_ds)
m = model.evaluate(test_ds, return_dict=True)
metrics.metadata.update(
dict(
loss=m["loss"],
accurancy=m["accuracy"],
)
)
model.save(model_metadata.uri)In [ ]:
@dsl.pipeline(name="simple-tabular-pipeline")
def pipeline(
dataset_uri: str,
dataset_fraction_split: float,
dataset_batch: int,
random_state: int,
n_units: int,
activation: str,
dropout_rate: float,
epochs: int,
):
tabular_trainer(
dataset_uri=dataset_uri,
dataset_fraction_split=dataset_fraction_split,
dataset_batch=dataset_batch,
random_state=random_state,
n_units=n_units,
activation=activation,
dropout_rate=dropout_rate,
epochs=epochs,
)
compiler.Compiler().compile(pipeline_func=pipeline, package_path="pipeline.json")In [ ]:
job = vertex_ai.PipelineJob(
display_name="my pipeline run",
template_path="pipeline.json",
job_id=f"pipeline-{RUN_NAME}",
pipeline_root=BUCKET_URI,
parameter_values={**params},
)
job.submit(experiment=EXPERIMENT_NAME)In [ ]:
vertex_ai.get_experiment_df()In [ ]:
job.wait()In [ ]:
vertex_ai.get_experiment_df()In [ ]:
print("Vertex AI Experiments:")
print(
f"https://console.cloud.google.com/ai/platform/experiments/experiments?folder=&organizationId=&project={PROJECT_ID}"
)In [ ]:
# Delete pipeline
job.delete()
# Delete experiment
exp = vertex_ai.Experiment(EXPERIMENT_NAME)
exp.delete(delete_backing_tensorboard_runs=True)
# Delete Tensorboard
vertex_ai_tb.delete()
# Delete Artifacts
artifacts_list = vertex_ai.Artifact.list()
for artifact in artifacts_list:
vertex_ai.Artifact.delete(artifact)
# Delete Contexts
context_list = vertex_ai.Context.list()
for context in context_list:
vertex_ai.Context.delete(context)
# Delete Cloud Storage objects that were created
delete_bucket = False
if delete_bucket or os.getenv("IS_TESTING"):
! gcloud storage rm --recursive --continue-on-error {BUCKET_URI}