Accessing Service from Python

Data drift occurs when a machine model’s performance declines or is different on unseen data compared to its training data due distribution changes in the data over time. In this notebook, we explore and visualize data drift on a simple XGBoost model, that predicts credit card acceptance based on an applicant’s age, credit score, years of education, and years in employment. This tutorial is a counterpart to the data drift monitoring tutorial.

Prerequisites

Follow the instructions within the Installing Open Data Hub section. Additionally, follow the instructions in the Deploy Model section in the Data Drift tutorial. Before proceeding, check that you have the following:

  1. ODH installation

  2. A TrustyAI Operator

  3. A model-namespace project containing an instance of the TrustyAI Service

  4. A model storage container

  5. A Seldon MLServer serving runtime

  6. The delpoyed credit model

Imports

import os
import subprocess
import warnings
warnings.filterwarnings("ignore")

import matplotlib.pyplot as plt

from trustyai.utils.api.api import TrustyAIApi
from trustyai.utils.extras.metrics_service import TrustyAIMetricsService

Clone the Data Drift Repository

For the purposes of recreating the data drift demo, we will be reusing the data in that repository.

git clone https://github.com/trustyai-explainability/odh-trustyai-demos.git
cd odh-trustyai-demos/3-DataDrift

Initialize Metrics Service

In order to use the metrics service, we first have to initialize it using our OpenShift login token and model namespace.

TOKEN = os.environ.get("TOKEN", "None")
trustyService = TrustyAIMetricsService(
    token = TOKEN,
    namespace="model-namespace",
    verify=False
)

Upload Model Training Data To TrustyAI

trustyService.upload_payload_data(
    json_file="data/training_data.json"
)

Label Data Fields

name_mapping = {
    "modelId": "gaussian-credit-model",
    "inputMapping":
      {
        "credit_inputs-0": "Age",
        "credit_inputs-1": "Credit Score",
        "credit_inputs-2": "Years of Education",
        "credit_inputs-3": "Years of Employment"
      },
    "outputMapping": {
      "predict-0": "Acceptance Probability"
    }
}

trustyService.label_data_fields(payload=name_mapping)

Examining TrustyAI’s Model Metadata

trustyService.get_model_metadata()
[{'metrics': {'scheduledMetadata': {'metricCounts': {}}},
  'data': {'inputSchema': {'items': {'credit_inputs-2': {'type': 'DOUBLE',
      'name': 'credit_inputs-2',
      'values': None,
      'index': 2},
     'credit_inputs-3': {'type': 'DOUBLE',
      'name': 'credit_inputs-3',
      'values': None,
      'index': 3},
     'credit_inputs-0': {'type': 'DOUBLE',
      'name': 'credit_inputs-0',
      'values': None,
      'index': 0},
     'credit_inputs-1': {'type': 'DOUBLE',
      'name': 'credit_inputs-1',
      'values': None,
      'index': 1}},
    'remapCount': 2,
    'nameMapping': {'credit_inputs-0': 'Age',
     'credit_inputs-1': 'Credit Score',
     'credit_inputs-2': 'Years of Education',
     'credit_inputs-3': 'Years of Employment'},
    'nameMappedItems': {'Years of Education': {'type': 'DOUBLE',
      'name': 'credit_inputs-2',
      'values': None,
      'index': 2},
     'Age': {'type': 'DOUBLE',
      'name': 'credit_inputs-0',
      'values': None,
      'index': 0},
     'Years of Employment': {'type': 'DOUBLE',
      'name': 'credit_inputs-3',
      'values': None,
      'index': 3},
     'Credit Score': {'type': 'DOUBLE',
      'name': 'credit_inputs-1',
      'values': None,
      'index': 1}}},
   'outputSchema': {'items': {'predict-0': {'type': 'FLOAT',
      'name': 'predict-0',
      'values': None,
      'index': 4}},
    'remapCount': 2,
    'nameMapping': {'predict-0': 'Acceptance Probability'},
    'nameMappedItems': {'Acceptance Probability': {'type': 'FLOAT',
      'name': 'predict-0',
      'values': None,
      'index': 4}}},
   'observations': 1000,
   'modelId': 'gaussian-credit-model'}}

Register Drift Monitoring

drift_monitoring = {
    "modelId": "gaussian-credit-model",
    "referenceTag": "TRAINING"
}
trustyService.get_metric_request(
    payload=drift_monitoring,
    metric="drift/meanshift", reoccuring=True
)
'{"requestId":"709174f5-a3f4-4ae9-8f7e-a56b708836ff","timestamp":"2024-03-06T14:23:17.740+00:00"}'

Check the Metrics

Let’s get the meanshift values for the training data we just uploaded to the TrustyAI service for the past 5 minutes.

train_df = trustyService.get_metric_data(
    metric="trustyai_meanshift",
    time_interval="[5m]"
)
display(train_df.head())
timestamp Age Credit Score Years of Education Years of Employment

2024-03-06 09:23:18

1.0

1.0

1.0

1.0

2024-03-06 09:23:22

1.0

1.0

1.0

1.0

2024-03-06 09:23:26

1.0

1.0

1.0

1.0

2024-03-06 09:23:30

1.0

1.0

1.0

1.0

2024-03-06 09:23:34

1.0

1.0

1.0

1.0

Let’s also visualize the meanshift in a plot similar to the one displayed in ODH Observe → Metrics tab. We will define a helper function so that we can use it again for the unseen data.

def plot_meanshift(df):
    """
    :param df: A pandas DataFrame returned by the TrustyAIMetricsService().get_metric_request
               function with columns corresponding to the timestamp and name of the metric
    returns a scatterplot with the timestamp on the x-axis and the specific metric on the y-axis
    """
    plt.figure(figsize=(12,5))
    for col in df.columns[1:]:
        plt.plot(
            df["timestamp"],
            df[col]
        )
    plt.xlabel("timestamp")
    plt.ylabel("meanshift")
    plt.xticks(rotation=45)
    plt.legend(df.columns[1:])
    plt.tight_layout()
    plt.show()

plot_meanshift(train_df)
Mean Shift plot

Collect "Real-World" Inferences

model_name = "gaussian-credit-model"
model_route = TrustyAIApi().get_service_route(
    name=model_name,
    namespace=trustyService.namespace
)

for batch in list(range(0, 596, 5)):
    trustyService.upload_data_to_model(
        model_route=f"{model_route}/v2/models/gaussian-credit-model",
        json_file=f"data/data_batches/{batch}.json"
    )

Observe Drift

Let’s check if our model is behaving differently on the unseen data.

test_df = trustyService.get_metric_data(
    metric="trustyai_meanshift",
    time_interval="[5m]"
    )
display(test_df.head())
timestamp Age Credit Score Years of Education Years of Employment

2024-03-06 09:23:18

1.0

1.0

1.0

1.0

2024-03-06 09:23:22

1.0

1.0

1.0

1.0

2024-03-06 09:23:26

1.0

1.0

1.0

1.0

2024-03-06 09:23:30

1.0

1.0

1.0

1.0

2024-03-06 09:23:34

1.0

1.0

1.0

1.0

plot_meanshift(test_df)
Mean Shift plot

As observed, the meanshift values for each of the features have changed drastically from the training to test data, dropping below 1.0. In particular, Age and Credit Score are significantly different according to a p-value of 0.05. Thus, it is clear that our model suffers from data drift.