

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Fortschrittliche Themen
<a name="model-monitor-advanced-topics"></a>

Die folgenden Abschnitte enthalten komplexere Aufgaben, in denen erklärt wird, wie Sie die Überwachung mithilfe von Vor- und Nachverarbeitungsskripts anpassen, Ihren eigenen Container erstellen und wie Sie CloudFormation damit einen Überwachungsplan erstellen.

**Topics**
+ [Benutzerdefinierte Überwachungszeitpläne](model-monitor-custom-monitoring-schedules.md)
+ [Erstellen Sie einen Überwachungsplan für einen Echtzeit-Endpunkt mit einer CloudFormation benutzerdefinierten Ressource](model-monitor-cloudformation-monitoring-schedules.md)

# Benutzerdefinierte Überwachungszeitpläne
<a name="model-monitor-custom-monitoring-schedules"></a>

Zusätzlich zur Verwendung der integrierten Überwachungsmechanismen können Sie eigene benutzerdefinierte Überwachungspläne und -verfahren mithilfe von Vorverarbeitungs- und Nachverarbeitungsskripten oder mithilfe eines eigenen Containers erstellen.

**Topics**
+ [Vorverarbeitung und Nachbearbeitung](model-monitor-pre-and-post-processing.md)
+ [Support für Ihre eigenen Container mit Amazon SageMaker Model Monitor](model-monitor-byoc-containers.md)

# Vorverarbeitung und Nachbearbeitung
<a name="model-monitor-pre-and-post-processing"></a>

Sie können benutzerdefinierte Python-Skripte für die Vor- und Nachverarbeitung verwenden, um die Eingabe in Ihren Modellmonitor zu transformieren oder den Code nach einem erfolgreichen Überwachungslauf zu erweitern. Laden Sie diese Skripts auf Amazon S3 hoch und referenzieren Sie sie, wenn Sie Ihren Modellmonitor erstellen.

Das folgende Beispiel zeigt, wie Sie Überwachungspläne mit Vor- und Nachverarbeitungsskripten anpassen können. Ersetzen Sie diese *user placeholder text* durch Ihre eigenen Informationen.

```
import boto3, os
from sagemaker import get_execution_role, Session
from sagemaker.model_monitor import CronExpressionGenerator, DefaultModelMonitor

# Upload pre and postprocessor scripts
session = Session()
bucket = boto3.Session().resource("s3").Bucket(session.default_bucket())
prefix = "demo-sagemaker-model-monitor"
pre_processor_script = bucket.Object(os.path.join(prefix, "preprocessor.py")).upload_file("preprocessor.py")
post_processor_script = bucket.Object(os.path.join(prefix, "postprocessor.py")).upload_file("postprocessor.py")

# Get execution role
role = get_execution_role() # can be an empty string

# Instance type
instance_type = "instance-type"
# instance_type = "ml.m5.xlarge" # Example

# Create a monitoring schedule with pre and postprocessing
my_default_monitor = DefaultModelMonitor(
    role=role,
    instance_count=1,
    instance_type=instance_type,
    volume_size_in_gb=20,
    max_runtime_in_seconds=3600,
)

s3_report_path = "s3://{}/{}".format(bucket, "reports")
monitor_schedule_name = "monitor-schedule-name"
endpoint_name = "endpoint-name"
my_default_monitor.create_monitoring_schedule(
    post_analytics_processor_script=post_processor_script,
    record_preprocessor_script=pre_processor_script,
    monitor_schedule_name=monitor_schedule_name,
    # use endpoint_input for real-time endpoint
    endpoint_input=endpoint_name,
    # or use batch_transform_input for batch transform jobs
    # batch_transform_input=batch_transform_name,
    output_s3_uri=s3_report_path,
    statistics=my_default_monitor.baseline_statistics(),
    constraints=my_default_monitor.suggested_constraints(),
    schedule_cron_expression=CronExpressionGenerator.hourly(),
    enable_cloudwatch_metrics=True,
)
```

**Topics**
+ [Vorverarbeitungsskript](#model-monitor-pre-processing-script)
+ [Benutzerdefinierte Probenahme](#model-monitor-pre-processing-custom-sampling)
+ [Nachbearbeitungsskript](#model-monitor-post-processing-script)

## Vorverarbeitungsskript
<a name="model-monitor-pre-processing-script"></a>

Verwenden Sie Vorverarbeitungsskripten, wenn Sie die Eingaben für Ihren Modellmonitor transformieren müssen.

Nehmen wir beispielsweise an, die Ausgabe Ihres Modells ist ein Array `[1.0, 2.1]`. Der Amazon SageMaker Model Monitor-Container funktioniert nur mit tabellarischen oder abgeflachten JSON-Strukturen wie. `{“prediction0”: 1.0, “prediction1” : 2.1}` Sie könnten ein Vorverarbeitungsskript wie das folgende verwenden, um das Array in die richtige JSON-Struktur umzuwandeln.

```
def preprocess_handler(inference_record):
    input_data = inference_record.endpoint_input.data
    output_data = inference_record.endpoint_output.data.rstrip("\n")
    data = output_data + "," + input_data
    return { str(i).zfill(20) : d for i, d in enumerate(data.split(",")) }
```

Nehmen wir in einem anderen Beispiel an, dass Ihr Modell optionale Funktionen hat und `-1` angeben, dass das optionale Feature einen fehlenden Wert hat. Wenn Sie über einen Datenqualitätsmonitor verfügen, sollten Sie den `-1` aus dem Eingabe-Werte-Array entfernen, damit er nicht in den metrischen Berechnungen des Monitors berücksichtigt wird. Sie könnten ein Skript wie das folgende verwenden, um diese Werte zu entfernen.

```
def preprocess_handler(inference_record):
    input_data = inference_record.endpoint_input.data
    return {i : None if x == -1 else x for i, x in enumerate(input_data.split(","))}
```

Ihr Vorverarbeitungsskript erhält `inference_record` als einzige Eingabe eine. Der folgende Codeschnipsel zeigt ein Beispiel für ein `inference_record`.

```
{
  "captureData": {
    "endpointInput": {
      "observedContentType": "text/csv",
      "mode": "INPUT",
      "data": "132,25,113.2,96,269.9,107,,0,0,0,0,0,0,1,0,1,0,0,1",
      "encoding": "CSV"
    },
    "endpointOutput": {
      "observedContentType": "text/csv; charset=utf-8",
      "mode": "OUTPUT",
      "data": "0.01076381653547287",
      "encoding": "CSV"
    }
  },
  "eventMetadata": {
    "eventId": "feca1ab1-8025-47e3-8f6a-99e3fdd7b8d9",
    "inferenceTime": "2019-11-20T23:33:12Z"
  },
  "eventVersion": "0"
}
```

Der folgende Codeschnipsel zeigt die vollständige Klassenstruktur für eine `inference_record`.

```
KEY_EVENT_METADATA = "eventMetadata"
KEY_EVENT_METADATA_EVENT_ID = "eventId"
KEY_EVENT_METADATA_EVENT_TIME = "inferenceTime"
KEY_EVENT_METADATA_CUSTOM_ATTR = "customAttributes"

KEY_EVENTDATA_ENCODING = "encoding"
KEY_EVENTDATA_DATA = "data"

KEY_GROUND_TRUTH_DATA = "groundTruthData"

KEY_EVENTDATA = "captureData"
KEY_EVENTDATA_ENDPOINT_INPUT = "endpointInput"
KEY_EVENTDATA_ENDPOINT_OUTPUT = "endpointOutput"

KEY_EVENTDATA_BATCH_OUTPUT = "batchTransformOutput"
KEY_EVENTDATA_OBSERVED_CONTENT_TYPE = "observedContentType"
KEY_EVENTDATA_MODE = "mode"

KEY_EVENT_VERSION = "eventVersion"

class EventConfig:
    def __init__(self, endpoint, variant, start_time, end_time):
        self.endpoint = endpoint
        self.variant = variant
        self.start_time = start_time
        self.end_time = end_time


class EventMetadata:
    def __init__(self, event_metadata_dict):
        self.event_id = event_metadata_dict.get(KEY_EVENT_METADATA_EVENT_ID, None)
        self.event_time = event_metadata_dict.get(KEY_EVENT_METADATA_EVENT_TIME, None)
        self.custom_attribute = event_metadata_dict.get(KEY_EVENT_METADATA_CUSTOM_ATTR, None)


class EventData:
    def __init__(self, data_dict):
        self.encoding = data_dict.get(KEY_EVENTDATA_ENCODING, None)
        self.data = data_dict.get(KEY_EVENTDATA_DATA, None)
        self.observedContentType = data_dict.get(KEY_EVENTDATA_OBSERVED_CONTENT_TYPE, None)
        self.mode = data_dict.get(KEY_EVENTDATA_MODE, None)

    def as_dict(self):
        ret = {
            KEY_EVENTDATA_ENCODING: self.encoding,
            KEY_EVENTDATA_DATA: self.data,
            KEY_EVENTDATA_OBSERVED_CONTENT_TYPE: self.observedContentType,
        }
        return ret


class CapturedData:
    def __init__(self, event_dict):
        self.event_metadata = None
        self.endpoint_input = None
        self.endpoint_output = None
        self.batch_transform_output = None
        self.ground_truth = None
        self.event_version = None
        self.event_dict = event_dict
        self._event_dict_postprocessed = False
        
        if KEY_EVENT_METADATA in event_dict:
            self.event_metadata = EventMetadata(event_dict[KEY_EVENT_METADATA])
        if KEY_EVENTDATA in event_dict:
            if KEY_EVENTDATA_ENDPOINT_INPUT in event_dict[KEY_EVENTDATA]:
                self.endpoint_input = EventData(event_dict[KEY_EVENTDATA][KEY_EVENTDATA_ENDPOINT_INPUT])
            if KEY_EVENTDATA_ENDPOINT_OUTPUT in event_dict[KEY_EVENTDATA]:
                self.endpoint_output = EventData(event_dict[KEY_EVENTDATA][KEY_EVENTDATA_ENDPOINT_OUTPUT])
            if KEY_EVENTDATA_BATCH_OUTPUT in event_dict[KEY_EVENTDATA]:
                self.batch_transform_output = EventData(event_dict[KEY_EVENTDATA][KEY_EVENTDATA_BATCH_OUTPUT])

        if KEY_GROUND_TRUTH_DATA in event_dict:
            self.ground_truth = EventData(event_dict[KEY_GROUND_TRUTH_DATA])
        if KEY_EVENT_VERSION in event_dict:
            self.event_version = event_dict[KEY_EVENT_VERSION]

    def as_dict(self):
        if self._event_dict_postprocessed is True:
            return self.event_dict
        if KEY_EVENTDATA in self.event_dict:
            if KEY_EVENTDATA_ENDPOINT_INPUT in self.event_dict[KEY_EVENTDATA]:
                self.event_dict[KEY_EVENTDATA][KEY_EVENTDATA_ENDPOINT_INPUT] = self.endpoint_input.as_dict()
            if KEY_EVENTDATA_ENDPOINT_OUTPUT in self.event_dict[KEY_EVENTDATA]:
                self.event_dict[KEY_EVENTDATA][
                    KEY_EVENTDATA_ENDPOINT_OUTPUT
                ] = self.endpoint_output.as_dict()
            if KEY_EVENTDATA_BATCH_OUTPUT in self.event_dict[KEY_EVENTDATA]:
                self.event_dict[KEY_EVENTDATA][KEY_EVENTDATA_BATCH_OUTPUT] = self.batch_transform_output.as_dict()
        
        self._event_dict_postprocessed = True
        return self.event_dict

    def __str__(self):
        return str(self.as_dict())
```

## Benutzerdefinierte Probenahme
<a name="model-monitor-pre-processing-custom-sampling"></a>

Sie können in Ihrem Vorverarbeitungsskript auch eine benutzerdefinierte Sampling-Strategie anwenden. Konfigurieren Sie dazu den vorgefertigten Container von Model Monitor aus erster Hand so, dass ein bestimmter Prozentsatz der Datensätze entsprechend der von Ihnen angegebenen Sampling-Rate ignoriert wird. Im folgenden Beispiel nimmt der Handler eine Stichprobe von 10 Prozent der Datensätze vor, indem er den Datensatz bei 10 Prozent der Handler-Aufrufe zurückgibt und andernfalls eine leere Liste ausgibt.

```
import random

def preprocess_handler(inference_record):
    # we set up a sampling rate of 0.1
    if random.random() > 0.1:
        # return an empty list
        return []
    input_data = inference_record.endpoint_input.data
    return {i : None if x == -1 else x for i, x in enumerate(input_data.split(","))}
```

### Benutzerdefiniertes Logging für das Vorverarbeitungsskript
<a name="model-monitor-pre-processing-custom-logging"></a>

 Wenn Ihr Vorverarbeitungsskript einen Fehler zurückgibt, überprüfen Sie die protokollierten Ausnahmemeldungen zum Debuggen. CloudWatch Sie können CloudWatch über die Schnittstelle auf den Logger zugreifen. `preprocess_handler` Sie können alle Informationen, die Sie benötigen, aus Ihrem Skript protokollieren CloudWatch. Dies kann beim Debuggen Ihres Vorverarbeitungsskripts nützlich sein. Das folgende Beispiel zeigt, wie Sie die `preprocess_handler` Schnittstelle verwenden können, um sich anzumelden CloudWatch 

```
def preprocess_handler(inference_record, logger):
    logger.info(f"I'm a processing record: {inference_record}")
    logger.debug(f"I'm debugging a processing record: {inference_record}")
    logger.warning(f"I'm processing record with missing value: {inference_record}")
    logger.error(f"I'm a processing record with bad value: {inference_record}")
    return inference_record
```

## Nachbearbeitungsskript
<a name="model-monitor-post-processing-script"></a>

Verwenden Sie ein Postprocessing-Skript, wenn Sie den Code nach einem erfolgreichen Überwachungslauf erweitern möchten.

```
def postprocess_handler():
    print("Hello from post-proc script!")
```

# Support für Ihre eigenen Container mit Amazon SageMaker Model Monitor
<a name="model-monitor-byoc-containers"></a>

Amazon SageMaker Model Monitor bietet einen vorgefertigten Container mit der Möglichkeit, die von Endpunkten erfassten Daten oder Batch-Transformationsjobs für tabellarische Datensätze zu analysieren. Wenn Sie Ihren eigenen Container bereitstellen möchten, bietet Model Monitor Erweiterungspunkte, die Sie nutzen können.

Wenn Sie einen `MonitoringSchedule` erstellen, startet letztendlich Verarbeitungsaufträge hinter den Kulissen. Daher muss der Container über den im [So erstellen Sie einen eigenen Verarbeitungscontainer (erweitertes Szenario)](build-your-own-processing-container.md) Thema dokumentierten Verarbeitungsauftragsvertrag informiert sein. Beachten Sie, dass Model Monitor den Verarbeitungsauftrag in Ihrem Namen gemäß dem Zeitplan startet. Beim Aufrufen richtet Model Monitor zusätzliche Umgebungsvariablen für Sie ein, damit Ihr Container über genügend Kontext verfügt, um die Daten für die bestimmte Ausführung der geplanten Überwachung zu verarbeiten. Weitere Informationen zu Container-Eingaben finden Sie im [Container-Vertragseingaben](model-monitor-byoc-contract-inputs.md).

Im Container können Sie nun mithilfe der obigen Umgebungsvariablen/des obigen Kontexts den Datensatz für den aktuellen Zeitraum in Ihrem benutzerdefinierten Code analysieren. Sobald diese Analyse abgeschlossen ist, haben Sie die Möglichkeit, Ihre Berichte auszugeben, die zu S3-Bucket hochgeladen werden sollen. Die Berichte, die der vorgefertigte Container generiert, werden in [Container-Vertragsausgaben](model-monitor-byoc-contract-outputs.md) dokumentiert. Wenn Sie möchten, dass die Visualisierung der Berichte in SageMaker Studio funktioniert, sollten Sie dasselbe Format verwenden. Sie können auch reine benutzerdefinierte Berichte ausgeben.

Sie geben auch CloudWatch Metriken aus dem Container aus, indem Sie den Anweisungen unter folgen[CloudWatch Metriken für Bring Your Own Containers](model-monitor-byoc-cloudwatch.md).

**Topics**
+ [Container-Vertragseingaben](model-monitor-byoc-contract-inputs.md)
+ [Container-Vertragsausgaben](model-monitor-byoc-contract-outputs.md)
+ [CloudWatch Metriken für Bring Your Own Containers](model-monitor-byoc-cloudwatch.md)

# Container-Vertragseingaben
<a name="model-monitor-byoc-contract-inputs"></a>

Die Amazon SageMaker Model Monitor-Plattform ruft Ihren Containercode gemäß einem bestimmten Zeitplan auf. Wenn Sie Ihren eigenen Container-Code schreiben möchten, stehen Ihnen die folgenden Umgebungsvariablen zur Verfügung. In diesem Zusammenhang können Sie den aktuellen Datensatz analysieren oder die Constraints auswerten und gegebenenfalls Metriken ausgeben.

Die verfügbaren Umgebungsvariablen sind für Echtzeit-Endpunkte und Batch-Transformationsaufträge identisch, mit Ausnahme der `dataset_format` Variablen. Wenn Sie einen Echtzeit-Endpunkt verwenden, unterstützt die `dataset_format` Variable die folgenden Optionen:

```
{\"sagemakerCaptureJson\": {\"captureIndexNames\": [\"endpointInput\",\"endpointOutput\"]}}
```

Wenn Sie einen Batch-Transformationsauftrag verwenden, `dataset_format` unterstützt der die folgenden Optionen:

```
{\"csv\": {\"header\": [\"true\",\"false\"]}}
```

```
{\"json\": {\"line\": [\"true\",\"false\"]}}
```

```
{\"parquet\": {}}
```

Das folgende Codebeispiel zeigt den vollständigen Satz von Umgebungsvariablen, die für Ihren Container-Code verfügbar sind (und verwendet das `dataset_format` Format für einen Echtzeit-Endpunkt).

```
"Environment": {
 "dataset_format": "{\"sagemakerCaptureJson\": {\"captureIndexNames\": [\"endpointInput\",\"endpointOutput\"]}}",
 "dataset_source": "/opt/ml/processing/endpointdata",
 "end_time": "2019-12-01T16: 20: 00Z",
 "output_path": "/opt/ml/processing/resultdata",
 "publish_cloudwatch_metrics": "Disabled",
 "sagemaker_endpoint_name": "endpoint-name",
 "sagemaker_monitoring_schedule_name": "schedule-name",
 "start_time": "2019-12-01T15: 20: 00Z"
}
```

Parameters 


| Name des Parameters | Description | 
| --- | --- | 
| dataset\$1format |  Bei einem Auftrag, der von einem `MonitoringSchedule`, unterstützt von einem `Endpoint`, gestartet wurde, handelt es sich um `sageMakerCaptureJson` mit den Erfassungsindizes `endpointInput` oder `endpointOutput` oder beides. Für einen Batch-Transformationsjob gibt dies das Datenformat an, ob CSV, JSON oder Parquet.  | 
| dataset\$1source |  Wenn Sie einen Echtzeit-Endpunkt verwenden, den lokalen Pfad, in dem die Daten, die dem durch `start_time` und `end_time` angegebenen Überwachungszeitraum entsprechen, verfügbar sind. In diesem Pfad sind die Daten in ` /{endpoint-name}/{variant-name}/yyyy/mm/dd/hh` verfügbar. Manchmal laden wir mehr als das herunter, was durch die Start- und Endzeiten angegeben wird. Es liegt an dem Containercode, die Daten nach Bedarf zu analysieren.  | 
| output\$1path |  Der lokale Pfad zum Schreiben von Ausgabeberichten und anderen Dateien. Sie müssen diesen Parameter in der Anforderung `CreateMonitoringSchedule` als `MonitoringOutputConfig.MonitoringOutput[0].LocalPath` angeben. Es wird in den in `MonitoringOutputConfig.MonitoringOutput[0].S3Uri` angegebenen Pfad `S3Uri` hochgeladen.  | 
| publish\$1cloudwatch\$1metrics |  Für einen von `CreateMonitoringSchedule` gestarteten Auftrag, ist dieser Parameter auf `Enabled` eingestellt. Der Container kann wählen, unter welcher Adresse die CloudWatch Amazon-Ausgabedatei geschrieben werden soll`[filepath]`.  | 
| sagemaker\$1endpoint\$1name |  Wenn Sie einen Echtzeit-Endpunkt verwenden, den Namen des `Endpoint`, für den dieser geplante Auftrag gestartet wurde.  | 
| sagemaker\$1monitoring\$1schedule\$1name |  Der Name des `MonitoringSchedule`, der diesen Auftrag gestartet hat.  | 
| \$1sagemaker\$1endpoint\$1datacapture\$1prefix\$1 |  Wenn Sie einen Echtzeit-Endpunkt verwenden, muss das Präfix, das im `DataCaptureConfig` Parameter des `Endpoint`. Der Container kann dies verwenden, wenn er direkt auf mehr Daten zugreifen muss, als die SageMaker KI bereits am `dataset_source` Pfad heruntergeladen hat.  | 
| start\$1time, end\$1time |  Das Zeitfenster für diesen Analyselauf. Beispiel: Bei einem Auftrag, dessen Ausführung um 05:00 UTC geplant ist, und einem Auftrag, der am 20/02/2020 ausgeführt wird, ist `start_time`: 2020-02-19T06:00:00Z und `end_time`: ist 2020-02-20T05:00:00Z  | 
| baseline\$1constraints: |  Der lokale Pfad der in ` BaselineConfig.ConstraintResource.S3Uri` angegebenen Baseline-Einschränkungsdatei. Dies ist nur verfügbar, wenn dieser Parameter in der `CreateMonitoringSchedule`-Anforderung angegeben wurde.  | 
| baseline\$1statistics |  Der lokale Pfad zur Baseline-Statistikdatei, die in `BaselineConfig.StatisticsResource.S3Uri` angegeben wird. Dies ist nur verfügbar, wenn dieser Parameter in der `CreateMonitoringSchedule`-Anforderung angegeben wurde.   | 

# Container-Vertragsausgaben
<a name="model-monitor-byoc-contract-outputs"></a>

Der Container kann die im Pfad `*dataset_source*` verfügbaren Daten analysieren und Berichte in den Pfad `*output_path*.` schreiben. Der Containercode kann beliebige Berichte schreiben, die Ihren Anforderungen entsprechen.

Wenn Sie die folgende Struktur und den folgenden Vertrag verwenden, werden bestimmte Ausgabedateien von SageMaker KI in der Visualisierung und API speziell behandelt. Dies gilt nur für Tabellendatensätze.

Ausgabedateien für Tabellendatensätze


| Dateiname | Description | 
| --- | --- | 
| statistics.json |  Für diese Datei wird erwartet, dass für jede Funktion im Datensatz, die analysiert wird, spaltenförmige Statistiken vorhanden sind. Das Schema für diese Datei finden Sie im nächsten Abschnitt.  | 
| constraints.json |  Von dieser Datei wird erwartet, dass die Beschränkungen für Funktionen beachtet werden. Das Schema für diese Datei finden Sie im nächsten Abschnitt.  | 
| constraints\$1violations.json |  Es wird erwartet, dass diese Datei die Liste der Verstöße enthält, die in diesem aktuellen Datensatz gefunden wurden, verglichen mit der Datei der Baseline-Statistik und -Einschränkungen, die im Pfad `baseline_constaints` und `baseline_statistics` angegeben ist.  | 

Wenn der `publish_cloudwatch_metrics` Wert ein `"Enabled"` Containercode ist, kann er außerdem CloudWatch Amazon-Metriken an diesem Standort ausgeben:`/opt/ml/output/metrics/cloudwatch`. Das Schema für diese Dateien wird in den folgenden Abschnitten beschrieben.

**Topics**
+ [Schema für Statistiken (Datei statistics.json)](model-monitor-byoc-statistics.md)
+ [Schema für Einschränkungen (Datei constraints.json)](model-monitor-byoc-constraints.md)

# Schema für Statistiken (Datei statistics.json)
<a name="model-monitor-byoc-statistics"></a>

Das in der Datei `statistics.json` definierte Schema gibt die statistischen Parameter an, die für die Baseline und die erfassten Daten berechnet werden sollen. Es konfiguriert auch den Bucket, der von [KLL](https://datasketches.apache.org/docs/KLL/KLLSketch.html) verwendet werden soll, einer sehr kompakten Quantil-Skizze mit verzögertem Komprimierungsschema.

```
{
    "version": 0,
    # dataset level stats
    "dataset": {
        "item_count": number
    },
    # feature level stats
    "features": [
        {
            "name": "feature-name",
            "inferred_type": "Fractional" | "Integral",
            "numerical_statistics": {
                "common": {
                    "num_present": number,
                    "num_missing": number
                },
                "mean": number,
                "sum": number,
                "std_dev": number,
                "min": number,
                "max": number,
                "distribution": {
                    "kll": {
                        "buckets": [
                            {
                                "lower_bound": number,
                                "upper_bound": number,
                                "count": number
                            }
                        ],
                        "sketch": {
                            "parameters": {
                                "c": number,
                                "k": number
                            },
                            "data": [
                                [
                                    num,
                                    num,
                                    num,
                                    num
                                ],
                                [
                                    num,
                                    num
                                ][
                                    num,
                                    num
                                ]
                            ]
                        }#sketch
                    }#KLL
                }#distribution
            }#num_stats
        },
        {
            "name": "feature-name",
            "inferred_type": "String",
            "string_statistics": {
                "common": {
                    "num_present": number,
                    "num_missing": number
                },
                "distinct_count": number,
                "distribution": {
                    "categorical": {
                         "buckets": [
                                {
                                    "value": "string",
                                    "count": number
                                }
                          ]
                     }
                }
            },
            #provision for custom stats
        }
    ]
}
```

**Hinweise**  
Die angegebenen Metriken werden von der SageMaker KI bei späteren Visualisierungsänderungen erkannt. Der Container kann bei Bedarf weitere Metriken ausgeben.
[KLL-Skizze](https://datasketches.apache.org/docs/KLL/KLLSketch.html) ist die erkannte Skizze. Benutzerdefinierte Container können ihre eigene Darstellung schreiben, diese wird jedoch von der SageMaker KI in Visualisierungen nicht erkannt.
Standardmäßig wird die Verteilung in 10 Buckets materialisiert. Sie können diesen Wert nicht ändern.

# Schema für Einschränkungen (Datei constraints.json)
<a name="model-monitor-byoc-constraints"></a>

Eine constraints.json-Datei wird verwendet, um die Einschränkungen auszudrücken, die ein Datensatz erfüllen muss. Amazon SageMaker Model Monitor-Container können die Datei constraints.json verwenden, um Datensätze anhand dieser Daten auszuwerten. Vorgefertigte Container bieten die Möglichkeit, die Datei constraints.json automatisch für einen Baseline-Datensatz zu generieren. Wenn Sie Ihren eigenen Container mit ähnlichen Fähigkeiten bereitstellen oder Sie können die Datei constraints.json auf andere Weise erstellen. Hier ist das Schema für die Einschränkungsdatei, die der vorgefertigte Container verwendet. Beim Bereitstellen eigener Container kann das gleiche Format übernommen oder bei Bedarf erweitert werden.

```
{
    "version": 0,
    "features":
    [
        {
            "name": "string",
            "inferred_type": "Integral" | "Fractional" | 
                    | "String" | "Unknown",
            "completeness": number,
            "num_constraints":
            {
                "is_non_negative": boolean
            },
            "string_constraints":
            {
                "domains":
                [
                    "list of",
                    "observed values",
                    "for small cardinality"
                ]
            },
            "monitoringConfigOverrides":
            {}
        }
    ],
    "monitoring_config":
    {
        "evaluate_constraints": "Enabled",
        "emit_metrics": "Enabled",
        "datatype_check_threshold": 0.1,
        "domain_content_threshold": 0.1,
        "distribution_constraints":
        {
            "perform_comparison": "Enabled",
            "comparison_threshold": 0.1,
            "comparison_method": "Simple"||"Robust",
            "categorical_comparison_threshold": 0.1,
            "categorical_drift_method": "LInfinity"||"ChiSquared"
        }
    }
}
```

Das `monitoring_config` Objekt enthält Optionen für die Überwachung des Auftrages für die Funktion. In der folgenden Tabelle werden die einzelnen Optionen beschrieben.

Überwachung von Beschränkungen

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/de_de/sagemaker/latest/dg/model-monitor-byoc-constraints.html)

# CloudWatch Metriken für Bring Your Own Containers
<a name="model-monitor-byoc-cloudwatch"></a>

Wenn der `publish_cloudwatch_metrics` Wert `Enabled` in der `Environment` Map in der `/opt/ml/processing/processingjobconfig.json` Datei enthalten ist, gibt der Container-Code CloudWatch Amazon-Metriken an diesem Speicherort aus:`/opt/ml/output/metrics/cloudwatch`. 

Das Schema für diese Datei basiert eng auf der CloudWatch `PutMetrics` API. Der Namespace ist hier nicht angegeben. Standardmäßig ist Folgendes:
+ `For real-time endpoints: /aws/sagemaker/Endpoint/data-metrics`
+ `For batch transform jobs: /aws/sagemaker/ModelMonitoring/data-metrics`

Sie können jedoch Dimensionen angeben. Wir empfehlen Ihnen, mindestens die folgenden Abmessungen hinzuzufügen:
+ `Endpoint` und `MonitoringSchedule` für Echtzeit-Endpunkte
+ `MonitoringSchedule` für Batch-Transformationsaufträge

Die folgenden JSON-Snippets zeigen, wie Sie Ihre Dimensionen festlegen.

Einen Echtzeit-Endpunkt finden Sie im folgenden JSON-Snippet, das die Dimensionen `Endpoint` und `MonitoringSchedule` enthält.

```
{ 
    "MetricName": "", # Required
    "Timestamp": "2019-11-26T03:00:00Z", # Required
    "Dimensions" : [{"Name":"Endpoint","Value":"endpoint_0"},{"Name":"MonitoringSchedule","Value":"schedule_0"}]
    "Value": Float,
    # Either the Value or the StatisticValues field can be populated and not both.
    "StatisticValues": {
        "SampleCount": Float,
        "Sum": Float,
        "Minimum": Float,
        "Maximum": Float
    },
    "Unit": "Count", # Optional
}
```

Für einen Batch-Transformationsauftrag sehen Sie sich den folgenden JSON-Snippet an, der die Dimension `MonitoringSchedule` enthält:

```
{ 
    "MetricName": "", # Required
    "Timestamp": "2019-11-26T03:00:00Z", # Required
    "Dimensions" : [{"Name":"MonitoringSchedule","Value":"schedule_0"}]
    "Value": Float,
    # Either the Value or the StatisticValues field can be populated and not both.
    "StatisticValues": {
        "SampleCount": Float,
        "Sum": Float,
        "Minimum": Float,
        "Maximum": Float
    },
    "Unit": "Count", # Optional
}
```

# Erstellen Sie einen Überwachungsplan für einen Echtzeit-Endpunkt mit einer CloudFormation benutzerdefinierten Ressource
<a name="model-monitor-cloudformation-monitoring-schedules"></a>

Wenn Sie einen Echtzeit-Endpunkt verwenden, können Sie eine CloudFormation benutzerdefinierte Ressource verwenden, um einen Überwachungsplan zu erstellen. Die benutzerdefinierte Ressource ist in Python. Informationen zur Bereitstellung finden Sie unter [Python Lambda-Bereitstellung](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html).

## Benutzerdefinierte Ressource
<a name="model-monitor-cloudformation-custom-resource"></a>

Fügen Sie Ihrer CloudFormation Vorlage zunächst eine benutzerdefinierte Ressource hinzu. Dies verweist auf eine AWS Lambda Funktion, die Sie als nächstes erstellen. 

Mit dieser Ressource können Sie die Parameter für den Überwachungsplan anpassen. Sie können weitere Parameter hinzufügen oder entfernen, indem Sie die CloudFormation Ressource und die Lambda-Funktion in der folgenden Beispielressource ändern.

```
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Resources": {
        "MonitoringSchedule": {
            "Type": "Custom::MonitoringSchedule",
            "Version": "1.0",
            "Properties": {
                "ServiceToken": "arn:aws:lambda:us-west-2:111111111111:function:lambda-name",
                "ScheduleName": "YourScheduleName",
                "EndpointName": "YourEndpointName",
                "BaselineConstraintsUri": "s3://your-baseline-constraints/constraints.json",
                "BaselineStatisticsUri": "s3://your-baseline-stats/statistics.json",
                "PostAnalyticsProcessorSourceUri": "s3://your-post-processor/postprocessor.py",
                "RecordPreprocessorSourceUri": "s3://your-preprocessor/preprocessor.py",
                "InputLocalPath": "/opt/ml/processing/endpointdata",
                "OutputLocalPath": "/opt/ml/processing/localpath",
                "OutputS3URI": "s3://your-output-uri",
                "ImageURI": "111111111111.dkr.ecr.us-west-2.amazonaws.com/your-image",
                "ScheduleExpression": "cron(0 * ? * * *)",
                "PassRoleArn": "arn:aws:iam::111111111111:role/AmazonSageMaker-ExecutionRole"
            }
        }
    }
}
```

## Benutzerdefinierter Lambda-Ressourcencode
<a name="model-monitor-cloudformation-lambda-custom-resource-code"></a>

Diese CloudFormation benutzerdefinierte Ressource verwendet die [Custom Resource AWS Helper-Bibliothek](https://github.com/aws-cloudformation/custom-resource-helper), die Sie mithilfe von pip installieren können. `pip install crhelper` 

Diese Lambda-Funktion wird CloudFormation während der Erstellung und Löschung des Stacks aufgerufen. Diese Lambda-Funktion ist verantwortlich für das Erstellen und Löschen des Überwachungszeitplans und die Verwendung der Parameter, die in der benutzerdefinierten Ressource, die im vorherigen Abschnitt beschrieben ist, definiert sind.

```
import boto3
import botocore
import logging

from crhelper import CfnResource
from botocore.exceptions import ClientError


logger = logging.getLogger(__name__)
sm = boto3.client('sagemaker')

# cfnhelper makes it easier to implement a CloudFormation custom resource
helper = CfnResource()

# CFN Handlers

def handler(event, context):
    helper(event, context)


@helper.create
def create_handler(event, context):
    """
    Called when CloudFormation custom resource sends the create event
    """
    create_monitoring_schedule(event)


@helper.delete
def delete_handler(event, context):
    """
    Called when CloudFormation custom resource sends the delete event
    """
    schedule_name = get_schedule_name(event)
    delete_monitoring_schedule(schedule_name)


@helper.poll_create
def poll_create(event, context):
    """
    Return true if the resource has been created and false otherwise so
    CloudFormation polls again.
    """
    schedule_name = get_schedule_name(event)
    logger.info('Polling for creation of schedule: %s', schedule_name)
    return is_schedule_ready(schedule_name)

@helper.update
def noop():
    """
    Not currently implemented but crhelper will throw an error if it isn't added
    """
    pass

# Helper Functions

def get_schedule_name(event):
    return event['ResourceProperties']['ScheduleName']

def create_monitoring_schedule(event):
    schedule_name = get_schedule_name(event)
    monitoring_schedule_config = create_monitoring_schedule_config(event)

    logger.info('Creating monitoring schedule with name: %s', schedule_name)

    sm.create_monitoring_schedule(
        MonitoringScheduleName=schedule_name,
        MonitoringScheduleConfig=monitoring_schedule_config)

def is_schedule_ready(schedule_name):
    is_ready = False

    schedule = sm.describe_monitoring_schedule(MonitoringScheduleName=schedule_name)
    status = schedule['MonitoringScheduleStatus']

    if status == 'Scheduled':
        logger.info('Monitoring schedule (%s) is ready', schedule_name)
        is_ready = True
    elif status == 'Pending':
        logger.info('Monitoring schedule (%s) still creating, waiting and polling again...', schedule_name)
    else:
        raise Exception('Monitoring schedule ({}) has unexpected status: {}'.format(schedule_name, status))

    return is_ready

def create_monitoring_schedule_config(event):
    props = event['ResourceProperties']

    return {
        "ScheduleConfig": {
            "ScheduleExpression": props["ScheduleExpression"],
        },
        "MonitoringJobDefinition": {
            "BaselineConfig": {
                "ConstraintsResource": {
                    "S3Uri": props['BaselineConstraintsUri'],
                },
                "StatisticsResource": {
                    "S3Uri": props['BaselineStatisticsUri'],
                }
            },
            "MonitoringInputs": [
                {
                    "EndpointInput": {
                        "EndpointName": props["EndpointName"],
                        "LocalPath": props["InputLocalPath"],
                    }
                }
            ],
            "MonitoringOutputConfig": {
                "MonitoringOutputs": [
                    {
                        "S3Output": {
                            "S3Uri": props["OutputS3URI"],
                            "LocalPath": props["OutputLocalPath"],
                        }
                    }
                ],
            },
            "MonitoringResources": {
                "ClusterConfig": {
                    "InstanceCount": 1,
                    "InstanceType": "ml.t3.medium",
                    "VolumeSizeInGB": 50,
                }
            },
            "MonitoringAppSpecification": {
                "ImageUri": props["ImageURI"],
                "RecordPreprocessorSourceUri": props['PostAnalyticsProcessorSourceUri'],
                "PostAnalyticsProcessorSourceUri": props['PostAnalyticsProcessorSourceUri'],
            },
            "StoppingCondition": {
                "MaxRuntimeInSeconds": 300
            },
            "RoleArn": props["PassRoleArn"],
        }
    }


def delete_monitoring_schedule(schedule_name):
    logger.info('Deleting schedule: %s', schedule_name)
    try:
        sm.delete_monitoring_schedule(MonitoringScheduleName=schedule_name)
    except ClientError as e:
        if e.response['Error']['Code'] == 'ResourceNotFound':
            logger.info('Resource not found, nothing to delete')
        else:
            logger.error('Unexpected error while trying to delete monitoring schedule')
            raise e
```