

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Topik lanjutan
<a name="model-monitor-advanced-topics"></a>

Bagian berikut berisi tugas-tugas lanjutan yang menjelaskan cara menyesuaikan pemantauan menggunakan skrip preprocessing dan postprocessing, cara membuat container Anda sendiri, dan cara menggunakannya CloudFormation untuk membuat jadwal pemantauan.

**Topics**
+ [Jadwal pemantauan khusus](model-monitor-custom-monitoring-schedules.md)
+ [Buat Jadwal Pemantauan untuk Titik Akhir Real-time dengan Sumber Daya CloudFormation Kustom](model-monitor-cloudformation-monitoring-schedules.md)

# Jadwal pemantauan khusus
<a name="model-monitor-custom-monitoring-schedules"></a>

Selain menggunakan mekanisme pemantauan bawaan, Anda dapat membuat jadwal dan prosedur pemantauan kustom Anda sendiri menggunakan skrip pra-pemrosesan dan pasca-pemrosesan atau dengan menggunakan atau membangun wadah Anda sendiri.

**Topics**
+ [Preprocessing dan Postprocessing](model-monitor-pre-and-post-processing.md)
+ [Support untuk Kontainer Anda Sendiri Dengan Monitor SageMaker Model Amazon](model-monitor-byoc-containers.md)

# Preprocessing dan Postprocessing
<a name="model-monitor-pre-and-post-processing"></a>

Anda dapat menggunakan skrip Python preprocessing dan postprocessing khusus untuk mengubah input ke monitor model Anda atau memperluas kode setelah pemantauan berhasil dijalankan. Unggah skrip ini ke Amazon S3 dan referensikan saat membuat monitor model Anda.

Contoh berikut menunjukkan bagaimana Anda dapat menyesuaikan jadwal pemantauan dengan skrip preprocessing dan postprocessing. Ganti *user placeholder text* dengan informasi Anda sendiri.

```
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**
+ [Skrip Preprocessing](#model-monitor-pre-processing-script)
+ [Pengambilan Sampel Kustom](#model-monitor-pre-processing-custom-sampling)
+ [Skrip Pasca Pemrosesan](#model-monitor-post-processing-script)

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

Gunakan skrip preprocessing saat Anda perlu mengubah input ke monitor model Anda.

Sebagai contoh, misalkan output dari model Anda adalah array`[1.0, 2.1]`. Wadah Amazon SageMaker Model Monitor hanya berfungsi dengan struktur JSON tabular atau pipih, seperti. `{“prediction0”: 1.0, “prediction1” : 2.1}` Anda dapat menggunakan skrip preprocessing seperti berikut ini untuk mengubah array menjadi struktur JSON yang benar.

```
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(",")) }
```

Dalam contoh lain, misalkan model Anda memiliki fitur opsional dan Anda gunakan `-1` untuk menunjukkan bahwa fitur opsional memiliki nilai yang hilang. Jika Anda memiliki monitor kualitas data, Anda mungkin ingin menghapus `-1` dari array nilai input sehingga tidak termasuk dalam perhitungan metrik monitor. Anda dapat menggunakan skrip seperti berikut ini untuk menghapus nilai-nilai tersebut.

```
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(","))}
```

Script preprocessing Anda menerima `inference_record` sebagai satu-satunya masukan. Cuplikan kode berikut menunjukkan contoh file. `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"
}
```

Cuplikan kode berikut menunjukkan struktur kelas penuh untuk sebuah. `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())
```

## Pengambilan Sampel Kustom
<a name="model-monitor-pre-processing-custom-sampling"></a>

Anda juga dapat menerapkan strategi pengambilan sampel khusus dalam skrip preprocessing Anda. Untuk melakukan ini, konfigurasikan wadah pra-bangun pihak pertama Model Monitor untuk mengabaikan persentase catatan sesuai dengan laju pengambilan sampel yang Anda tentukan. Dalam contoh berikut, handler mengambil sampel 10 persen dari catatan dengan mengembalikan catatan dalam 10 persen panggilan handler dan daftar kosong sebaliknya.

```
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(","))}
```

### Logging kustom untuk skrip preprocessing
<a name="model-monitor-pre-processing-custom-logging"></a>

 Jika skrip preprocessing Anda mengembalikan kesalahan, periksa pesan pengecualian yang masuk CloudWatch ke debug. Anda dapat mengakses logger CloudWatch melalui `preprocess_handler` antarmuka. Anda dapat mencatat informasi apa pun yang Anda butuhkan dari skrip Anda CloudWatch. Ini dapat berguna saat men-debug skrip preprocessing Anda. Contoh berikut menunjukkan bagaimana Anda dapat menggunakan `preprocess_handler` antarmuka untuk log ke 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
```

## Skrip Pasca Pemrosesan
<a name="model-monitor-post-processing-script"></a>

Gunakan skrip postprocessing saat Anda ingin memperpanjang kode setelah pemantauan yang berhasil dijalankan.

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

# Support untuk Kontainer Anda Sendiri Dengan Monitor SageMaker Model Amazon
<a name="model-monitor-byoc-containers"></a>

Amazon SageMaker Model Monitor menyediakan wadah bawaan dengan kemampuan untuk menganalisis data yang diambil dari titik akhir atau pekerjaan transformasi batch untuk kumpulan data tabular. Jika Anda ingin membawa wadah Anda sendiri, Model Monitor menyediakan titik ekstensi yang dapat Anda manfaatkan.

Di bawah tenda, saat Anda membuat`MonitoringSchedule`, Model Monitor akhirnya memulai pekerjaan pemrosesan. Oleh karena itu wadah perlu mengetahui kontrak kerja pemrosesan yang didokumentasikan dalam [Cara Membangun Wadah Pemrosesan Anda Sendiri (Skenario Lanjutan)](build-your-own-processing-container.md) topik tersebut. Perhatikan bahwa Model Monitor memulai pekerjaan pemrosesan atas nama Anda sesuai jadwal. Saat menjalankan, Model Monitor menyiapkan variabel lingkungan tambahan untuk Anda sehingga penampung Anda memiliki konteks yang cukup untuk memproses data untuk eksekusi tertentu dari pemantauan terjadwal. Untuk informasi tambahan tentang input kontainer, lihat. [Input Kontrak Kontainer](model-monitor-byoc-contract-inputs.md)

Dalam wadah, menggunakan variabel/konteks lingkungan di atas, Anda sekarang dapat menganalisis kumpulan data untuk periode saat ini dalam kode kustom Anda. Setelah analisis ini selesai, Anda dapat memilih untuk mengeluarkan laporan Anda untuk diunggah ke bucket S3. Laporan yang dihasilkan oleh kontainer bawaan didokumentasikan[Output Kontrak Kontainer](model-monitor-byoc-contract-outputs.md). Jika Anda ingin visualisasi laporan berfungsi di SageMaker Studio, Anda harus mengikuti format yang sama. Anda juga dapat memilih untuk mengeluarkan laporan yang sepenuhnya disesuaikan.

Anda juga memancarkan CloudWatch metrik dari wadah dengan mengikuti petunjuk di. [CloudWatch Metrik untuk Membawa Kontainer Anda Sendiri](model-monitor-byoc-cloudwatch.md)

**Topics**
+ [Input Kontrak Kontainer](model-monitor-byoc-contract-inputs.md)
+ [Output Kontrak Kontainer](model-monitor-byoc-contract-outputs.md)
+ [CloudWatch Metrik untuk Membawa Kontainer Anda Sendiri](model-monitor-byoc-cloudwatch.md)

# Input Kontrak Kontainer
<a name="model-monitor-byoc-contract-inputs"></a>

Platform Amazon SageMaker Model Monitor memanggil kode penampung Anda sesuai dengan jadwal yang ditentukan. Jika Anda memilih untuk menulis kode kontainer Anda sendiri, variabel lingkungan berikut tersedia. Dalam konteks ini, Anda dapat menganalisis kumpulan data saat ini atau mengevaluasi kendala jika Anda memilih dan memancarkan metrik, jika berlaku.

Variabel lingkungan yang tersedia adalah sama untuk titik akhir real-time dan pekerjaan transformasi batch, kecuali untuk `dataset_format` variabel. Jika Anda menggunakan titik akhir real-time, `dataset_format` variabel mendukung opsi berikut:

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

Jika Anda menggunakan pekerjaan transformasi batch, `dataset_format` mendukung opsi berikut:

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

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

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

Contoh kode berikut menunjukkan set lengkap variabel lingkungan yang tersedia untuk kode kontainer Anda (dan menggunakan `dataset_format` format untuk titik akhir waktu nyata).

```
"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"
}
```

Parameter 


| Nama Parameter | Deskripsi | 
| --- | --- | 
| dataset\$1format |  Untuk pekerjaan yang dimulai dari yang `MonitoringSchedule` didukung oleh`Endpoint`, ini `sageMakerCaptureJson` dengan indeks penangkapan`endpointInput`, atau`endpointOutput`, atau keduanya. Untuk pekerjaan transformasi batch, ini menentukan format data, apakah CSV, JSON, atau Parquet.  | 
| dataset\$1source |  Jika Anda menggunakan titik akhir real-time, jalur lokal di mana data yang sesuai dengan periode pemantauan, sebagaimana ditentukan oleh `start_time` dan`end_time`, tersedia. Pada jalur ini, data tersedia di` /{endpoint-name}/{variant-name}/yyyy/mm/dd/hh`. Kami terkadang mengunduh lebih dari yang ditentukan oleh waktu mulai dan akhir. Terserah kode kontainer untuk mengurai data sesuai kebutuhan.  | 
| output\$1path |  Jalur lokal untuk menulis laporan output dan file lainnya. Anda menentukan parameter ini dalam `CreateMonitoringSchedule` permintaan sebagai`MonitoringOutputConfig.MonitoringOutput[0].LocalPath`. Itu diunggah ke `S3Uri` jalur yang ditentukan dalam`MonitoringOutputConfig.MonitoringOutput[0].S3Uri`.  | 
| publish\$1cloudwatch\$1metrics |  Untuk pekerjaan yang diluncurkan oleh`CreateMonitoringSchedule`, parameter ini diatur ke`Enabled`. Wadah dapat memilih untuk menulis file CloudWatch output Amazon di`[filepath]`.  | 
| sagemaker\$1endpoint\$1name |  Jika Anda menggunakan titik akhir real-time, nama tempat `Endpoint` pekerjaan terjadwal ini diluncurkan.  | 
| sagemaker\$1monitoring\$1schedule\$1name |  Nama `MonitoringSchedule` yang meluncurkan pekerjaan ini.  | 
| \$1sagemaker\$1endpoint\$1datacapture\$1prefix\$1 |  Jika Anda menggunakan titik akhir real-time, awalan yang ditentukan dalam `DataCaptureConfig` parameter. `Endpoint` Wadah dapat menggunakan ini jika perlu mengakses lebih banyak data secara langsung daripada yang sudah diunduh oleh SageMaker AI di `dataset_source` jalur.  | 
| start\$1time, end\$1time |  Jendela waktu untuk analisis ini berjalan. Misalnya, untuk pekerjaan yang dijadwalkan berjalan pada 05:00 UTC dan pekerjaan yang berjalan pada 20/02/2020,`start_time`: adalah 2020-02-19T 06:00:00 Z dan: adalah 2020-02-20T 05:00:00 Z `end_time`  | 
| baseline\$1constraints: |  Jalur lokal dari file kendala dasar yang ditentukan dalam. ` BaselineConfig.ConstraintResource.S3Uri` Ini hanya tersedia jika parameter ini ditentukan dalam `CreateMonitoringSchedule` permintaan.  | 
| baseline\$1statistics |  Jalur lokal ke file statistik dasar yang ditentukan dalam. `BaselineConfig.StatisticsResource.S3Uri` Ini hanya tersedia jika parameter ini ditentukan dalam `CreateMonitoringSchedule` permintaan. :   | 

# Output Kontrak Kontainer
<a name="model-monitor-byoc-contract-outputs"></a>

Wadah dapat menganalisis data yang tersedia di `*dataset_source*` jalur dan menulis laporan ke jalur di `*output_path*.` Kode kontainer dapat menulis laporan apa pun yang sesuai dengan kebutuhan Anda.

Jika Anda menggunakan struktur dan kontrak berikut, file output tertentu diperlakukan secara khusus oleh SageMaker AI dalam visualisasi dan API. Ini hanya berlaku untuk kumpulan data tabular.

File Output untuk Dataset Tabular


| Nama berkas | Deskripsi | 
| --- | --- | 
| statistics.json |  File ini diharapkan memiliki statistik kolumnar untuk setiap fitur dalam kumpulan data yang dianalisis. Skema untuk file ini tersedia di bagian selanjutnya.  | 
| constraints.json |  File ini diharapkan memiliki kendala pada fitur yang diamati. Skema untuk file ini tersedia di bagian selanjutnya.  | 
| constraints\$1violations.json |  File ini diharapkan memiliki daftar pelanggaran yang ditemukan dalam kumpulan data saat ini dibandingkan dengan statistik dasar dan file kendala yang ditentukan di jalur dan. `baseline_constaints` `baseline_statistics`  | 

Selain itu, jika `publish_cloudwatch_metrics` nilainya adalah kode `"Enabled"` kontainer dapat memancarkan CloudWatch metrik Amazon di lokasi ini:. `/opt/ml/output/metrics/cloudwatch` Skema untuk file-file ini dijelaskan di bagian berikut.

**Topics**
+ [Skema untuk Statistik (file statistik.json)](model-monitor-byoc-statistics.md)
+ [Skema untuk Kendala (file kendala json)](model-monitor-byoc-constraints.md)

# Skema untuk Statistik (file statistik.json)
<a name="model-monitor-byoc-statistics"></a>

Skema yang didefinisikan dalam `statistics.json` file menentukan parameter statistik yang akan dihitung untuk baseline dan data yang ditangkap. Ini juga mengonfigurasi bucket yang akan digunakan oleh [KLL](https://datasketches.apache.org/docs/KLL/KLLSketch.html), sketsa kuantil yang sangat ringkas dengan skema pemadatan malas.

```
{
    "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
        }
    ]
}
```

**Catatan**  
Metrik yang ditentukan dikenali oleh SageMaker AI dalam perubahan visualisasi selanjutnya. Wadah dapat memancarkan lebih banyak metrik jika diperlukan.
Sketsa [KLL adalah sketsa](https://datasketches.apache.org/docs/KLL/KLLSketch.html) yang diakui. Wadah khusus dapat menulis representasi mereka sendiri, tetapi tidak akan dikenali oleh SageMaker AI dalam visualisasi.
Secara default, distribusi diwujudkan dalam 10 ember. Kau tak bisa mengubah ini.

# Skema untuk Kendala (file kendala json)
<a name="model-monitor-byoc-constraints"></a>

File constraints.json digunakan untuk mengekspresikan kendala yang harus dipenuhi oleh dataset. Container Amazon SageMaker Model Monitor dapat menggunakan file constraints.json untuk mengevaluasi kumpulan data. Kontainer bawaan menyediakan kemampuan untuk menghasilkan file constraints.json secara otomatis untuk dataset dasar. Jika Anda membawa wadah Anda sendiri, Anda dapat menyediakannya dengan kemampuan serupa atau Anda dapat membuat file constraints.json dengan cara lain. Berikut adalah skema untuk file kendala yang digunakan kontainer bawaan. Bawa wadah Anda sendiri dapat mengadopsi format yang sama atau meningkatkannya sesuai kebutuhan.

```
{
    "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"
        }
    }
}
```

`monitoring_config`Objek berisi opsi untuk pekerjaan pemantauan untuk fitur tersebut. Tabel berikut menjelaskan setiap opsi.

Kendala Pemantauan

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

# CloudWatch Metrik untuk Membawa Kontainer Anda Sendiri
<a name="model-monitor-byoc-cloudwatch"></a>

Jika `publish_cloudwatch_metrics` nilainya ada `Enabled` di `Environment` peta dalam `/opt/ml/processing/processingjobconfig.json` file, kode penampung memancarkan CloudWatch metrik Amazon di lokasi ini:. `/opt/ml/output/metrics/cloudwatch` 

Skema untuk file ini sangat didasarkan pada CloudWatch `PutMetrics` API. Namespace tidak ditentukan di sini. Ini defaultnya sebagai berikut:
+ `For real-time endpoints: /aws/sagemaker/Endpoint/data-metrics`
+ `For batch transform jobs: /aws/sagemaker/ModelMonitoring/data-metrics`

Namun, Anda dapat menentukan dimensi. Kami menyarankan Anda menambahkan dimensi berikut minimal:
+ `Endpoint`dan `MonitoringSchedule` untuk titik akhir real-time
+ `MonitoringSchedule`untuk pekerjaan transformasi batch

Cuplikan JSON berikut menunjukkan cara mengatur dimensi Anda.

Untuk titik akhir real-time, lihat cuplikan JSON berikut yang mencakup dan dimensi: `Endpoint` `MonitoringSchedule`

```
{ 
    "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
}
```

Untuk pekerjaan transformasi batch, lihat cuplikan JSON berikut yang mencakup dimensi: `MonitoringSchedule`

```
{ 
    "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
}
```

# Buat Jadwal Pemantauan untuk Titik Akhir Real-time dengan Sumber Daya CloudFormation Kustom
<a name="model-monitor-cloudformation-monitoring-schedules"></a>

Jika Anda menggunakan titik akhir real-time, Anda dapat menggunakan sumber daya CloudFormation khusus untuk membuat jadwal pemantauan. Sumber daya kustom ada di Python. Untuk menerapkannya, lihat Penerapan [Python Lambda](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html).

## Sumber Daya Kustom
<a name="model-monitor-cloudformation-custom-resource"></a>

Mulailah dengan menambahkan sumber daya khusus ke CloudFormation template Anda. Ini menunjuk ke AWS Lambda fungsi yang Anda buat di langkah berikutnya. 

Sumber daya ini memungkinkan Anda untuk menyesuaikan parameter untuk jadwal pemantauan Anda dapat menambah atau menghapus lebih banyak parameter dengan memodifikasi CloudFormation sumber daya dan fungsi Lambda dalam sumber daya contoh berikut.

```
{
    "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"
            }
        }
    }
}
```

## Kode Sumber Daya Kustom Lambda
<a name="model-monitor-cloudformation-lambda-custom-resource-code"></a>

Sumber daya CloudFormation kustom ini menggunakan AWS pustaka [Custom Resource Helper](https://github.com/aws-cloudformation/custom-resource-helper), yang dapat Anda instal dengan pip menggunakan. `pip install crhelper` 

Fungsi Lambda ini dipanggil oleh CloudFormation selama pembuatan dan penghapusan tumpukan. Fungsi Lambda ini bertanggung jawab untuk membuat dan menghapus jadwal pemantauan dan menggunakan parameter yang ditentukan dalam sumber daya khusus yang dijelaskan di bagian sebelumnya.

```
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
```