def model_quality_monitor(sagemaker_session):
    monitor = ModelQualityMonitor(
        role=ROLE,
        instance_count=INSTANCE_COUNT,
        instance_type=INSTANCE_TYPE,
        volume_size_in_gb=VOLUME_SIZE_IN_GB,
        max_runtime_in_seconds=MAX_RUNTIME_IN_SECONDS,
        sagemaker_session=sagemaker_session,
        env=TEST_ENV,
        tags=TEST_TAGS,
    )
    return monitor
def test_model_quality_monitor(sagemaker_session,
                               scheduled_model_quality_monitor, endpoint_name,
                               ground_truth_input):
    monitor = scheduled_model_quality_monitor
    monitor._wait_for_schedule_changes_to_apply()

    # stop it as soon as possible to avoid any execution
    monitor.stop_monitoring_schedule()
    _verify_monitoring_schedule(
        monitor=monitor,
        schedule_status="Stopped",
    )
    _verify_model_quality_job_description(
        sagemaker_session=sagemaker_session,
        monitor=monitor,
        endpoint_name=endpoint_name,
        ground_truth_input=ground_truth_input,
    )

    # attach to schedule
    monitoring_schedule_name = monitor.monitoring_schedule_name
    job_definition_name = monitor.job_definition_name
    monitor = ModelQualityMonitor.attach(
        monitor_schedule_name=monitor.monitoring_schedule_name,
        sagemaker_session=sagemaker_session,
    )
    assert monitor.monitoring_schedule_name == monitoring_schedule_name
    assert monitor.job_definition_name == job_definition_name

    # update schedule
    monitor.update_monitoring_schedule(
        max_runtime_in_seconds=UPDATED_MAX_RUNTIME_IN_SECONDS,
        schedule_cron_expression=UPDATED_CRON)
    assert monitor.monitoring_schedule_name == monitoring_schedule_name
    assert monitor.job_definition_name != job_definition_name
    _verify_monitoring_schedule(monitor=monitor,
                                schedule_status="Scheduled",
                                schedule_cron_expression=UPDATED_CRON)
    _verify_model_quality_job_description(
        sagemaker_session=sagemaker_session,
        monitor=monitor,
        endpoint_name=endpoint_name,
        ground_truth_input=ground_truth_input,
        max_runtime_in_seconds=UPDATED_MAX_RUNTIME_IN_SECONDS,
    )

    # delete schedule
    monitor.delete_monitoring_schedule()
def test_run_model_quality_monitor_baseline(
    sagemaker_session,
    endpoint_name,
    data_path,
    ground_truth_input,
    upload_actual_data,
):
    monitor = ModelQualityMonitor(
        role=ROLE,
        instance_count=INSTANCE_COUNT,
        instance_type=INSTANCE_TYPE,
        volume_size_in_gb=VOLUME_SIZE_IN_GB,
        max_runtime_in_seconds=MAX_RUNTIME_IN_SECONDS,
        sagemaker_session=sagemaker_session,
        env=TEST_ENV,
        tags=TEST_TAGS,
    )

    baselining_job_name = utils.unique_name_from_base(
        "model-quality-baselining-job")
    print("Creating baselining job: {}".format(baselining_job_name))
    monitor.suggest_baseline(
        baseline_dataset=data_path,
        dataset_format=DatasetFormat.csv(),
        problem_type=PROBLEM_TYPE,
        job_name=baselining_job_name,
        ground_truth_attribute=HEADER_OF_LABEL,
        inference_attribute=HEADER_OF_PREDICTED_LABEL,
    )

    monitoring_schedule_name = utils.unique_name_from_base(
        "model-quality-suggest-baseline")
    s3_uri_monitoring_output = os.path.join(
        "s3://",
        sagemaker_session.default_bucket(),
        endpoint_name,
        monitoring_schedule_name,
        "monitor_output",
    )
    monitor.create_monitoring_schedule(
        endpoint_input=EndpointInput(
            endpoint_name=endpoint_name,
            destination=ENDPOINT_INPUT_LOCAL_PATH,
            start_time_offset=START_TIME_OFFSET,
            end_time_offset=END_TIME_OFFSET,
            inference_attribute=INFERENCE_ATTRIBUTE,
        ),
        ground_truth_input=ground_truth_input,
        problem_type=PROBLEM_TYPE,
        output_s3_uri=s3_uri_monitoring_output,
        monitor_schedule_name=monitoring_schedule_name,
        schedule_cron_expression=CRON,
    )
    _verify_execution_status(monitor)

    _verify_model_quality_job_description(
        sagemaker_session=sagemaker_session,
        monitor=monitor,
        endpoint_name=endpoint_name,
        ground_truth_input=ground_truth_input,
    )

    monitor.delete_monitoring_schedule()
    def _generate_model_monitor(self, mm_type: str) -> Optional[ModelMonitor]:
        """Generates a ModelMonitor object

        Generates a ModelMonitor object with required config attributes for
            QualityCheckStep and ClarifyCheckStep

        Args:
            mm_type (str): The subclass type of ModelMonitor object.
                A valid mm_type should be one of the following: "DefaultModelMonitor",
                "ModelQualityMonitor", "ModelBiasMonitor", "ModelExplainabilityMonitor"

        Return:
            sagemaker.model_monitor.ModelMonitor or None if the mm_type is not valid

        """
        if mm_type == "DefaultModelMonitor":
            monitor = DefaultModelMonitor(
                role=self.role,
                instance_count=self.instance_count,
                instance_type=self.instance_type,
                volume_size_in_gb=self.volume_size_in_gb,
                volume_kms_key=self.volume_kms_key,
                output_kms_key=self.output_kms_key,
                max_runtime_in_seconds=self.max_runtime_in_seconds,
                base_job_name=self.base_job_name,
                sagemaker_session=self.sagemaker_session,
                env=self.env,
                tags=self.tags,
                network_config=self.network_config,
            )
        elif mm_type == "ModelQualityMonitor":
            monitor = ModelQualityMonitor(
                role=self.role,
                instance_count=self.instance_count,
                instance_type=self.instance_type,
                volume_size_in_gb=self.volume_size_in_gb,
                volume_kms_key=self.volume_kms_key,
                output_kms_key=self.output_kms_key,
                max_runtime_in_seconds=self.max_runtime_in_seconds,
                base_job_name=self.base_job_name,
                sagemaker_session=self.sagemaker_session,
                env=self.env,
                tags=self.tags,
                network_config=self.network_config,
            )
        elif mm_type == "ModelBiasMonitor":
            monitor = ModelBiasMonitor(
                role=self.role,
                instance_count=self.instance_count,
                instance_type=self.instance_type,
                volume_size_in_gb=self.volume_size_in_gb,
                volume_kms_key=self.volume_kms_key,
                output_kms_key=self.output_kms_key,
                max_runtime_in_seconds=self.max_runtime_in_seconds,
                base_job_name=self.base_job_name,
                sagemaker_session=self.sagemaker_session,
                env=self.env,
                tags=self.tags,
                network_config=self.network_config,
            )
        elif mm_type == "ModelExplainabilityMonitor":
            monitor = ModelExplainabilityMonitor(
                role=self.role,
                instance_count=self.instance_count,
                instance_type=self.instance_type,
                volume_size_in_gb=self.volume_size_in_gb,
                volume_kms_key=self.volume_kms_key,
                output_kms_key=self.output_kms_key,
                max_runtime_in_seconds=self.max_runtime_in_seconds,
                base_job_name=self.base_job_name,
                sagemaker_session=self.sagemaker_session,
                env=self.env,
                tags=self.tags,
                network_config=self.network_config,
            )
        else:
            logging.warning(
                'Expected model monitor types: "DefaultModelMonitor", "ModelQualityMonitor", '
                '"ModelBiasMonitor", "ModelExplainabilityMonitor"')
            return None
        return monitor
示例#5
0
def main(resources, train_data):

    # configurarion
    AWS_DEFAULT_REGION = os.getenv('AWS_DEFAULT_REGION', 'eu-west-1')
    AWS_PROFILE = os.getenv('AWS_PROFILE', 'default')
    AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID', None)
    AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY', None)
    b3_session, sm_client, sm_runtime, sm_session = get_sm_session(
        region=AWS_DEFAULT_REGION,
        profile_name=AWS_PROFILE,
        aws_access_key_id=AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AWS_SECRET_ACCESS_KEY)
    BASE_JOB_PREFIX = os.getenv('BASE_JOB_PREFIX', 'sts')
    ROLE_ARN = os.getenv('AWS_ROLE', sagemaker.get_execution_role())
    outputs = resources

    bucket = sm_session.default_bucket()
    prefix = "{}/{}".format(BASE_JOB_PREFIX, resources['endpoint']['name'])
    if 'monitor' not in resources:
        raise ValueError("Monitoring not enabled")

    if 's3_capture_upload_path' not in resources['monitor']:
        raise ValueError("Monitoring not enabled")

    baseline_prefix = prefix + "/baselining"
    baseline_data_prefix = baseline_prefix + "/data"
    baseline_results_prefix = baseline_prefix + "/results"
    baseline_data_uri = "s3://{}/{}".format(bucket, baseline_data_prefix)
    baseline_results_uri = "s3://{}/{}".format(bucket, baseline_results_prefix)
    outputs['monitor'].update({
        'baseline': {
            'data_uri': baseline_data_uri,
            'results_uri': baseline_results_uri
        }
    })
    _l.info("Baseline data uri: {}".format(baseline_data_uri))
    _l.info("Baseline results uri: {}".format(baseline_results_uri))

    ground_truth_upload_path = f"s3://{bucket}/{prefix}/ground_truth_data"
    _l.info(f"Ground truth uri: {ground_truth_upload_path}")
    outputs['monitor'].update({'ground truth uri': ground_truth_upload_path})

    # Create a baselining job with training dataset
    _l.info("Executing a baselining job with training dataset")
    _l.info(f"baseline_data_uri: {train_data['baseline']['validate']}")
    my_monitor = ModelQualityMonitor(
        role=ROLE_ARN,
        sagemaker_session=sm_session,
        max_runtime_in_seconds=1800  # 30 minutes
    )
    my_monitor.suggest_baseline(
        baseline_dataset=train_data['baseline']['validate'] + "/baseline.csv",
        dataset_format=DatasetFormat.csv(header=True),
        problem_type="Regression",
        inference_attribute="prediction",
        ground_truth_attribute="label",
        output_s3_uri=baseline_results_uri,
        wait=True)
    baseline_job = my_monitor.latest_baselining_job
    _l.info("suggested baseline contrains")
    _l.info(
        pprint.pformat(baseline_job.suggested_constraints().
                       body_dict["regression_constraints"]))
    _l.info("suggested baseline statistics")
    _l.info(
        pprint.pformat(baseline_job.baseline_statistics().
                       body_dict["regression_metrics"]))

    monitor_schedule_name = (
        f"{BASE_JOB_PREFIX}-mq-sch-{datetime.datetime.utcnow():%Y-%m-%d-%H%M}")
    _l.info(f"Monitoring schedule name: {monitor_schedule_name}")
    outputs['monitor'].update({'schedule_name': monitor_schedule_name})
    endpointInput = EndpointInput(
        resources['endpoint']['name'],
        "/opt/ml/processing/input_data",
        inference_attribute='0'  # REVIEW:
    )

    my_monitor.create_monitoring_schedule(
        monitor_schedule_name=monitor_schedule_name,
        endpoint_input=endpointInput,
        output_s3_uri=baseline_results_uri,
        problem_type="Regression",
        ground_truth_input=ground_truth_upload_path,
        constraints=baseline_job.suggested_constraints(),
        # run the scheduler hourly
        schedule_cron_expression=CronExpressionGenerator.hourly(),
        enable_cloudwatch_metrics=True,
    )
    mq_schedule_details = my_monitor.describe_schedule()
    while mq_schedule_details['MonitoringScheduleStatus'] == 'Pending':
        _l.info(f'Waiting for {monitor_schedule_name}')
        time.sleep(3)
        mq_schedule_details = my_monitor.describe_schedule()
    _l.debug(
        f"Model Quality Monitor - schedule details: {pprint.pformat(mq_schedule_details)}"
    )
    _l.info(
        f"Model Quality Monitor - schedule status: {mq_schedule_details['MonitoringScheduleStatus']}"
    )

    # save outputs to a file
    with open('deploymodel_out.json', 'w') as f:
        json.dump(outputs, f, default=json_default)