{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [모듈 3.5] 엔드포인트 배포 스텝 개발 (SageMaker Model Building Pipeline 배포 스텝)\n", "\n", "이 노트북은 아래와 같은 목차로 진행 됩니다. 전체를 모두 실행시에 완료 시간은 약 10분 소요 됩니다.\n", "\n", "- 1. 모델 엔드포인트 배포 개요\n", "- 2. 기본 라이브러리 로딩\n", "- 3. 모델 빌딩 파이프라인 의 스텝(Step) 생성\n", "- 4. 파리마터, 단계, 조건을 조합하여 최종 파이프라인 정의 및 실행\n", "- 5. 세이지 메이커 스튜디오에서 확인하기\n", "- 6. 앤드포인트 추론 테스트\n", "- 7. 엔드포인트 삭제\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. 모델 엔드포인트 배포 개요 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## 1.1. SageMaker 호스팅 아키텍쳐\n", "- 일반적인 아키텍쳐의 그림이고, 오토 스케일링이 적용이 되어 있습니다.\n", "\n", "![sagemaker-endpoints.png](img/sagemaker-endpoints.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.2 프로세싱 스텝 \n", "- 엔드포인트 배포는 \"deploy_model.py\" 의 스크립트를 실행하여 배포를 합니다. 그래서 여기서는 \"프로세싱 스텝\" 을 사용합니다.\n", "- 프로세싱 단계의 개발자 가이드 \n", " - [프로세싱 스텝](https://docs.aws.amazon.com/ko_kr/sagemaker/latest/dg/build-and-manage-steps.html#step-type-processing)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. 기본 라이브러리 로딩\n", "\n", "세이지 메이커 관련 라이브러리를 로딩 합니다." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stored variables and their in-db values:\n", "bucket -> 'sagemaker-us-east-1-585843180719'\n", "claims_data_uri -> 's3://sagemaker-us-east-1-585843180719/sagemaker-w\n", "customers_data_uri -> 's3://sagemaker-us-east-1-585843180719/sagemaker-w\n", "hyperparameters -> {'scale_pos_weight': '29', 'max_depth': '3', 'eta'\n", "image_uri -> '683313688378.dkr.ecr.us-east-1.amazonaws.com/sage\n", "input_data_uri -> 's3://sagemaker-us-east-1-585843180719/sagemaker-w\n", "preprocessing_code -> 'src/preprocessing.py'\n", "project_prefix -> 'sagemaker-webinar-pipeline-base'\n", "sagemaker_model -> 'pipelines-ch0l86ldquad-fraud-basic-createmo-lyzrs\n", "test_preproc_dir_artifact -> 's3://sagemaker-us-east-1-585843180719/sagemaker-w\n", "train_model_artifact -> 's3://sagemaker-us-east-1-585843180719/sagemaker-w\n", "train_preproc_dir_artifact -> 's3://sagemaker-us-east-1-585843180719/sagemaker-w\n" ] } ], "source": [ "import boto3\n", "import sagemaker\n", "import pandas as pd\n", "\n", "region = boto3.Session().region_name\n", "role = sagemaker.get_execution_role()\n", "sagemaker_session = sagemaker.session.Session()\n", "%store -r \n", "\n", "%store" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. 모델 빌딩 파이프라인 의 스텝(Step) 생성\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3.1 모델 빌딩 파이프라인 변수 생성\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [], "source": [ "from sagemaker.workflow.parameters import (\n", " ParameterInteger,\n", " ParameterString,\n", ")\n", "\n", "processing_instance_count = ParameterInteger(\n", " name=\"ProcessingInstanceCount\",\n", " default_value=1\n", ")\n", "processing_instance_type = ParameterString(\n", " name=\"ProcessingInstanceType\",\n", " default_value=\"ml.m5.xlarge\"\n", ")\n", "endpoint_instance_type = ParameterString(\n", " name=\"EndpointInstanceType\",\n", " default_value=\"ml.m5.xlarge\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3.2 배포에 사용할 스크립트 코드 S3 업로딩\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "suffix: 460471\n", "s3_deploy_code_uri: \n", " s3://sagemaker-us-east-1-585843180719/sagemaker-webinar-pipeline-base/code/deploy_model.py\n" ] } ], "source": [ "from datetime import datetime\n", "suffix = datetime.now().microsecond\n", "print(\"suffix: \", suffix)\n", "\n", "\n", "local_deploy_code_path = 'src/deploy_model.py'\n", "s3_deploy_code_path = f\"s3://{bucket}/{project_prefix}/code\"\n", "s3_deploy_code_uri = sagemaker.s3.S3Uploader.upload(\n", " local_path=local_deploy_code_path, \n", " desired_s3_uri=s3_deploy_code_path,\n", ")\n", "print(\"s3_deploy_code_uri: \\n\", s3_deploy_code_uri)\n", "\n", "pipeline_endpoint_name = 'pipeline-endpoint-' + str(suffix)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3.3. 배포에 사용할 프로세서 정의" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "The input argument instance_type of function (sagemaker.image_uris.retrieve) is a pipeline variable (), which is not allowed. The default_value of this Parameter object will be used to override it. Please make sure the default_value is valid.\n" ] } ], "source": [ "from sagemaker.sklearn.processing import SKLearnProcessor\n", "from sagemaker.workflow.steps import ProcessingStep\n", "\n", "deploy_model_processor = SKLearnProcessor(\n", " framework_version=\"1.0-1\",\n", " role= role,\n", " instance_type= processing_instance_type, \n", " instance_count= processing_instance_count,\n", " base_job_name='fraud-scratch-deploy-model',\n", " sagemaker_session=sagemaker_session)\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3.4 모델 엔트포인트 생성 스탭 생성\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "tags": [] }, "outputs": [], "source": [ "deploy_step = ProcessingStep(\n", " name='Fraud-Basic-Endpoint',\n", " processor=deploy_model_processor,\n", " job_arguments=[\n", " \"--model_name\", sagemaker_model, \n", " \"--region\", region,\n", " \"--endpoint_instance_type\", endpoint_instance_type,\n", " \"--endpoint_name\", pipeline_endpoint_name\n", " ],\n", " code=s3_deploy_code_uri)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 4. 파리마터, 단계, 조건을 조합하여 최종 파이프라인 정의 및 실행\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4.1 파이프라인 정의\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[중요] `pipeline_endpoint_name` 에 '_' 언데바를 넣으면 에러가 납니다. '-' 대시는 가능합니다." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "tags": [] }, "outputs": [], "source": [ "from sagemaker.workflow.pipeline import Pipeline\n", "\n", "pipeline_name = project_prefix\n", "pipeline = Pipeline(\n", " name=pipeline_name,\n", " parameters=[\n", " processing_instance_type, \n", " processing_instance_count,\n", " endpoint_instance_type\n", " ],\n", " steps=[deploy_step],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4.2 파이프라인 정의 확인\n", "위에서 정의한 파이프라인 정의는 Json 형식으로 정의 되어 있습니다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "{'Version': '2020-12-01',\n", " 'Metadata': {},\n", " 'Parameters': [{'Name': 'ProcessingInstanceType',\n", " 'Type': 'String',\n", " 'DefaultValue': 'ml.m5.xlarge'},\n", " {'Name': 'ProcessingInstanceCount', 'Type': 'Integer', 'DefaultValue': 1},\n", " {'Name': 'EndpointInstanceType',\n", " 'Type': 'String',\n", " 'DefaultValue': 'ml.m5.xlarge'}],\n", " 'PipelineExperimentConfig': {'ExperimentName': {'Get': 'Execution.PipelineName'},\n", " 'TrialName': {'Get': 'Execution.PipelineExecutionId'}},\n", " 'Steps': [{'Name': 'Fraud-Basic-Endpoint',\n", " 'Type': 'Processing',\n", " 'Arguments': {'ProcessingResources': {'ClusterConfig': {'InstanceType': {'Get': 'Parameters.ProcessingInstanceType'},\n", " 'InstanceCount': {'Get': 'Parameters.ProcessingInstanceCount'},\n", " 'VolumeSizeInGB': 30}},\n", " 'AppSpecification': {'ImageUri': '683313688378.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:1.0-1-cpu-py3',\n", " 'ContainerArguments': ['--model_name',\n", " 'pipelines-ch0l86ldquad-fraud-basic-createmo-lyzrspepk1',\n", " '--region',\n", " 'us-east-1',\n", " '--endpoint_instance_type',\n", " {'Get': 'Parameters.EndpointInstanceType'},\n", " '--endpoint_name',\n", " 'pipeline-endpoint-460471'],\n", " 'ContainerEntrypoint': ['python3',\n", " '/opt/ml/processing/input/code/deploy_model.py']},\n", " 'RoleArn': 'arn:aws:iam::585843180719:role/TeamRole',\n", " 'ProcessingInputs': [{'InputName': 'code',\n", " 'AppManaged': False,\n", " 'S3Input': {'S3Uri': 's3://sagemaker-us-east-1-585843180719/sagemaker-webinar-pipeline-base/code/deploy_model.py',\n", " 'LocalPath': '/opt/ml/processing/input/code',\n", " 'S3DataType': 'S3Prefix',\n", " 'S3InputMode': 'File',\n", " 'S3DataDistributionType': 'FullyReplicated',\n", " 'S3CompressionType': 'None'}}]}}]}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import json\n", "\n", "definition = json.loads(pipeline.definition())\n", "definition" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4.3 파이프라인 정의를 제출하고 실행하기 \n", "\n", "파이프라인 정의를 파이프라인 서비스에 제출합니다. 함께 전달되는 역할(role)을 이용하여 AWS에서 파이프라인을 생성하고 작업의 각 단계를 실행할 것입니다. " ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "tags": [] }, "outputs": [], "source": [ "pipeline.upsert(role_arn=role)\n", "execution = pipeline.start()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "{'PipelineArn': 'arn:aws:sagemaker:us-east-1:585843180719:pipeline/sagemaker-webinar-pipeline-base',\n", " 'PipelineExecutionArn': 'arn:aws:sagemaker:us-east-1:585843180719:pipeline/sagemaker-webinar-pipeline-base/execution/g2mrqerf6oje',\n", " 'PipelineExecutionDisplayName': 'execution-1678716430105',\n", " 'PipelineExecutionStatus': 'Executing',\n", " 'CreationTime': datetime.datetime(2023, 3, 13, 14, 7, 10, 28000, tzinfo=tzlocal()),\n", " 'LastModifiedTime': datetime.datetime(2023, 3, 13, 14, 7, 10, 28000, tzinfo=tzlocal()),\n", " 'CreatedBy': {},\n", " 'LastModifiedBy': {},\n", " 'ResponseMetadata': {'RequestId': '77da98e7-b9fc-4d9d-81b8-a24fc6deaddd',\n", " 'HTTPStatusCode': 200,\n", " 'HTTPHeaders': {'x-amzn-requestid': '77da98e7-b9fc-4d9d-81b8-a24fc6deaddd',\n", " 'content-type': 'application/x-amz-json-1.1',\n", " 'content-length': '427',\n", " 'date': 'Mon, 13 Mar 2023 14:07:10 GMT'},\n", " 'RetryAttempts': 0}}" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "execution.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4.4 파이프라인 실행 기다리기" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "tags": [] }, "outputs": [], "source": [ "execution.wait()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4.5 파이프라인 실행 단계 기록 보기" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "[{'StepName': 'Fraud-Basic-Endpoint',\n", " 'StartTime': datetime.datetime(2023, 3, 13, 14, 7, 11, 352000, tzinfo=tzlocal()),\n", " 'EndTime': datetime.datetime(2023, 3, 13, 14, 13, 48, 236000, tzinfo=tzlocal()),\n", " 'StepStatus': 'Succeeded',\n", " 'AttemptCount': 0,\n", " 'Metadata': {'ProcessingJob': {'Arn': 'arn:aws:sagemaker:us-east-1:585843180719:processing-job/pipelines-g2mrqerf6oje-fraud-basic-endpoint-le0j0qqqsl'}}}]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "execution.list_steps()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 5. 세이지 메이커 스튜디오에서 확인하기\n", "- 아래의 그림 처럼 SageMaker Studio에 로긴후에 따라하시면, SageMaker Studio 에서도 실행 내역을 확인할 수 있습니다.\n", " - SageMaker Studio 개발자 가이드 --> [SageMaker Studio](https://docs.aws.amazon.com/ko_kr/sagemaker/latest/dg/studio.html)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![endpoint-sm.png](img/endpoint-sm.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 6. 앤드포인트 추론 테스트" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 추론에 사용할 Payload 생성 및 추론\n", "- 테스트 데이터를 통해서 엔드포인트에 전달할 CSV 형태의 String을 생성합니다. (payload).\n", "- payload 를 엔드포인트에 제공하면, 확률값을 0.072 을 리턴합니다. \n", "- 보통 확률값이 0.5 보다 작으면 0 (Non-Fruad), 0.5 보다 크면 1 (Fruad) 로 변환해서 예측값으로 사용합니다.\n", "\n", "```\n", "payload: \n", " 6038.102399076349,15838.10239907635,39,64,0,1,750,2750,95660,2012,1,0,0,9800,1,9,2,16,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,1,0\n", "Reponse: \n", "[['0.0726071447134018']]\n", "\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.1. 테스트 데이터 준비" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "tags": [] }, "outputs": [], "source": [ "import boto3\n", "sagemaker_boto_client = boto3.client('sagemaker')\n", "from src.p_utils import get_predictor, get_payload, predict\n", "\n", "from sagemaker.deserializers import CSVDeserializer\n", "csv_deserializer = CSVDeserializer(accept='text/csv') # 디폴트가 accept가 'text/csv' 이지만 직관적 이유로 기술함.\n", "\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
fraudvehicle_claimtotal_claim_amountcustomer_agemonths_as_customernum_claims_past_yearnum_insurers_past_5_yearspolicy_deductablepolicy_annual_premiumcustomer_zip...collision_type_missingincident_severity_Majorincident_severity_Minorincident_severity_Totaledauthorities_contacted_Ambulanceauthorities_contacted_Fireauthorities_contacted_Noneauthorities_contacted_Policepolice_report_available_Nopolice_report_available_Yes
0023901.43250736201.4325075611801750300098039...0001000101
\n", "

1 rows × 59 columns

\n", "
" ], "text/plain": [ " fraud vehicle_claim total_claim_amount customer_age months_as_customer \\\n", "0 0 23901.432507 36201.432507 56 118 \n", "\n", " num_claims_past_year num_insurers_past_5_years policy_deductable \\\n", "0 0 1 750 \n", "\n", " policy_annual_premium customer_zip ... collision_type_missing \\\n", "0 3000 98039 ... 0 \n", "\n", " incident_severity_Major incident_severity_Minor \\\n", "0 0 0 \n", "\n", " incident_severity_Totaled authorities_contacted_Ambulance \\\n", "0 1 0 \n", "\n", " authorities_contacted_Fire authorities_contacted_None \\\n", "0 0 0 \n", "\n", " authorities_contacted_Police police_report_available_No \\\n", "0 1 0 \n", "\n", " police_report_available_Yes \n", "0 1 \n", "\n", "[1 rows x 59 columns]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test_preproc_path = f\"{test_preproc_dir_artifact}/test.csv\"\n", "test_df = pd.read_csv(test_preproc_path)\n", "test_df.head(1)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.2. 엔드포인트에 입력이 될 데이터 (payload) 생성" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "payload: \n", " 23901.43250714285,36201.43250714285,56,118,0,1,750,3000,98039,2012,3,3,1,12300,9,12,3,14,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1\n" ] } ], "source": [ "# test_df 의 상위 1개 레코드를 사용\n", "payload = get_payload(test_df, label_col = 'fraud', verbose=False)\n", "print(\"payload: \\n\", payload)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.3. 추론 하여 예측 확률 얻기" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "prediction_prob: \n", " ['0.10092484205961227']\n" ] } ], "source": [ "\n", "predictor = get_predictor(pipeline_endpoint_name, sagemaker_session, csv_deserializer)\n", "\n", "pred_prob = predict(predictor, payload)\n", "print(\"prediction_prob: \\n\", pred_prob)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 7. 엔드포인트 삭제\n", "\n", "파이프라인 실행을 통하여 생성된 앤드포인트 및 관련 오브젝트 (세이지 메이커 모델, 엔드포인트 컨피그) 를 삭제 합니다." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--- Deleted model: pipelines-ch0l86ldquad-fraud-basic-createmo-lyzrspepk1\n", "--- Deleted endpoint: pipeline-endpoint-460471\n", "--- Deleted endpoint_config: pipelines-ch0l86ldquad-fraud-basic-createmo-lyzrspepk1-config\n" ] } ], "source": [ "from src.p_utils import delete_endpoint\n", "\n", "delete_endpoint(sagemaker_boto_client, pipeline_endpoint_name, is_del_model = True )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "conda_python3", "language": "python", "name": "conda_python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.8" } }, "nbformat": 4, "nbformat_minor": 4 }