- Äɲ䵤줿¹Ô¤Ï¤³¤Î¿§¤Ç¤¹¡£
- ºï½ü¤µ¤ì¤¿¹Ô¤Ï¤³¤Î¿§¤Ç¤¹¡£
#author("2020-02-16T13:10:02+00:00","","")
#author("2020-02-28T13:15:49+00:00","","")
#mynavi(Amazon SageMaker¤ò»È¤Ã¤Æ¤ß¤ë)
#setlinebreak(on);
* Ìܼ¡ [#e3986f6f]
#contents
- ´ØÏ¢
-- [[AWS¥á¥â]]
-- [[Amazon SageMaker¤ò»È¤Ã¤Æ¤ß¤ë]]
-- [[PyTorch¤Ç½Å²óµ¢Ê¬ÀÏ]]
- »²¹Í
-- https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/pytorch.html
-- https://sagemaker.readthedocs.io/en/stable/using_pytorch.html#deploy-endpoints-from-model-data
-- https://aws.amazon.com/jp/blogs/news/building-training-and-deploying-fastai-models-with-amazon-sagemaker/
-- https://github.com/aws/sagemaker-python-sdk/blob/master/src/sagemaker/pytorch/README.rst
-- https://github.com/awslabs/amazon-sagemaker-examples/blob/master/sagemaker-python-sdk/chainer_sentiment_analysis/src/sentiment_analysis.py
* ³µÍ× [#h894648f]
#html(<div class="pl10">)
#TODO
#html(</div>)
* ¥â¥Ç¥ë¥Ç¡¼¥¿¤òºîÀ®¤·S3¥Ð¥±¥Ã¥È¤Ë¾å¤²¤ë [#a47349e0]
#html(<div class="pl10">)
¥â¥Ç¥ë¤Ï [[PyTorch¤Ç½Å²óµ¢Ê¬ÀÏ]] ¤ÇºîÀ®¤·¤¿¤â¤Î¤ò¤½¤Î¤Þ¤ÞÍøÍѤ¹¤ë¡£
** ¥¢¥Ã¥×¥í¡¼¥É¤¹¤ë¥Õ¥©¥ë¥À¤Î¹½À® [#fdc2f419]
#html(<div class="pl10">)
°Ê²¼¤Î¹½À®¤Î¥Õ¥©¥ë¥À¤òºîÀ®¤¹¤ë¡£
#html(){{
<div style="padding: 10px; border: 1px solid #333; display: inline-block;">
sample_model<br />
¡¡¨¦ sample_model.pth .... ¥¨¥¯¥¹¥Ý¡¼¥È¤·¤¿·±ÎýºÑ¤ß¥â¥Ç¥ë<br />
¡¡¨¦ entry_point.py .... ¥¨¥ó¥È¥ê¥Ý¥¤¥ó¥È¤È¤Ê¤ë¥¹¥¯¥ê¥×¥È(¸å½Ò)<br />
sample_torch_model.tgz<br />
¡¡¨¦ sample_torch_model.pth ... ¥¨¥¯¥¹¥Ý¡¼¥È¤·¤¿·±ÎýºÑ¤ß¥â¥Ç¥ë<br />
¡¡¨¦ sample_torch_model.json ... ɸ½à²½¤ò¥¨¥ó¥É¥Ý¥¤¥ó¥È¦¤Ç¤·¤¿¤«¤Ã¤é¤Î¤Çɸ½à²½¤ËɬÍפʾðÊó¤òJSON²½¤·¤Æ°ì½ï¤Ë¥¢¥Ã¥×¤·¤Æ¤ª¤¯(¸å½Ò)<br />
</div>
}}
#html(</div>)
** ¥â¥Ç¥ë¤ò¥¨¥¯¥¹¥Ý¡¼¥È¤¹¤ë [#g78a4bf4]
** ¥â¥Ç¥ë¤ÎºîÀ® µÚ¤Ó ¥¨¥¯¥¹¥Ý¡¼¥È [#o29e046c]
#html(<div class="pl10">)
¥â¥Ç¥ë¤Ï [[PyTorch¤Ç½Å²óµ¢Ê¬ÀÏ]] ¤ÇºîÀ®¤·¤¿¤â¤Î¤ò¤½¤Î¤Þ¤Þ»ÈÍÑ¡£
¤¢¤È¤Ï°Ê²¼¤ÎÄ̤ꡢ¥¨¥¯¥¹¥Ý¡¼¥È¤¹¤ë¤À¤±¡£
#mycode2(){{
torch.save(model.state_dict(), 'sample_model/sample_model.pth')
import json
import os
model_name = "sample_torch_model"
if not os.path.exists(model_name):
os.mkdir(model_name)
# ·±ÎýºÑ¤ß¥â¥Ç¥ë¤òÊݸ
model_path = f"{model_name}/{model_name}.pth"
#model_state = model.state_dict()
#model_state["my_scaler_params"] = scaler.get_params()
#model_state["my_scaler_mean"] = scaler.mean_
#model_state["my_scaler_var"] = scaler.var_
#model_state["my_scaler_scale"] = scaler.scale_
#torch.save(model_state, model_path)
torch.save(model.state_dict(), model_path)
#
# ɸ½à²½¤ËɬÍפÊÃͤòJSON¤ËÊݸ
#
scaler_dict = {}
scaler_dict["my_scaler_params"] = scaler.get_params()
scaler_dict["my_scaler_mean"] = scaler.mean_.tolist()
scaler_dict["my_scaler_var"] = scaler.var_.tolist()
scaler_dict["my_scaler_scale"] = scaler.scale_.tolist()
with open(f"{model_name}/{model_name}_scalar.json", "w") as f:
f.write(json.dumps(scaler_dict))
}}
#html(</div>)
** entry_point.py ¤ÎºîÀ® [#cf6141ad]
#html(<div class="pl10">)
#mycode2(){{
TODO:
}}
#html(</div>)
** tar.gz ¤Ë¤¹¤ë [#sf75e15d]
#html(<div class="pl10">)
³¬Áؤòºî¤ê¤¿¤¯¤Ê¤«¤Ã¤¿¤Î¤Ç¡¢¤¤¤Ã¤¿¤óÂоݥե©¥ë¥À¤Ë°ÜÆ°¤·¤ÆƱ¤¸¥Õ¥©¥ë¥À¤Î¤â¤Î¤ò¥¢¡¼¥«¥¤¥Ö¤·¤¿¡£
#myterm2(){{
tar czfv sample_model.tar.gz sample_model
cd sample_torch_model
tar czfv ../sample_torch_model.tar.gz .
cd ../
}}
#html(</div>)
** S3¤Ë¥¢¥Ã¥×¥í¡¼¥É [#ka1b62de]
#html(<div class="pl10">)
¥Ð¥±¥Ã¥ÈºîÀ®
#myterm2(){{
aws s3 mb s3://sagemaker-sample-¥¢¥«¥¦¥ó¥ÈID
aws s3 mb s3://¥Ð¥±¥Ã¥È̾
}}
s3¤Ë¥¢¥Ã¥×¥í¡¼¥É
#myterm2(){{
aws s3api put-object --bucket ºîÀ®¤·¤¿¥Ð¥±¥Ã¥È̾ --key sample_model.tar.gz --body ./sample_model.tar.gz
aws s3api put-object --bucket ºîÀ®¤·¤¿¥Ð¥±¥Ã¥È̾ --key sample_torch_model.tar.gz --body ./sample_torch_model.tar.gz
}}
#html(</div>)
#html(</div>)
* ¥Î¡¼¥È¥Ö¥Ã¥¯¥¤¥ó¥¹¥¿¥ó¥¹¤ÎºîÀ® [#p7d249a7]
#html(<div class="pl10">)
[[Amazon SageMaker¤ò»È¤Ã¤Æ¤ß¤ë]] ¤ò»²¾È¡£
#html(</div>)
* ¥â¥Ç¥ë¤Î¥Ç¥×¥í¥¤ [#hb0614f9]
* ¥Ç¥×¥í¥¤ [#hb0614f9]
#html(<div class="pl10">)
¥Î¡¼¥È¥Ö¥Ã¥¯¥¤¥ó¥¹¥¿¥ó¥¹¤«¤é°Ê²¼¤ò¼Â¹Ô¤¹¤ë¡£
** ¥¨¥ó¥È¥ê¥Ý¥¤¥ó¥È¤È¤Ê¤ë¥Õ¥¡¥¤¥ë¤ÎºîÀ® [#cf6141ad]
#html(<div class="pl10">)
¤Þ¤º¥¨¥ó¥È¥ê¥Ý¥¤¥ó¥È¤È¤Ê¤ë¥Õ¥¡¥¤¥ë¤ò¥Î¡¼¥È¥Ö¥Ã¥¯¥¤¥ó¥¹¥¿¥ó¥¹¾å¤ËºîÀ®¤¹¤ë¡£
²òÀâ¤Ï¸å½Ò¤¹¤ë»ö¤È¤·¤Æ¤Þ¤º¤Ï¥³¡¼¥É¡£
entry_point.py
#mycode2(){{
import argparse
import logging
import sagemaker_containers
import requests
import torch
import torch.nn as nn
import numpy as np
import sagemaker
from sagemaker.pytorch.model import PyTorchModel
from six import BytesIO
from sklearn.preprocessing import StandardScaler
import torch
import os
import io
import json
import glob
import time
import re
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
JSON_CONTENT_TYPE = 'application/json'
XNPY_CONTENT_TYPE = 'application/x-npy'
CSV_CONTENT_TYPE = 'text/csv'
INPUT_SIZE = 2
OUTPUT_SIZE = 1
class LinearRegression(nn.Module):
"""¥â¥Ç¥ëÄêµÁ"""
def __init__(self, input_size, output_size):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(input_size, output_size)
def forward(self, x):
out = self.linear(x)
return out
def model_fn(model_dir):
"""¥â¥Ç¥ë¤Î¥í¡¼¥É."""
logger.info('START model_fn')
model = LinearRegression(INPUT_SIZE, OUTPUT_SIZE)
# ¥â¥Ç¥ë¤Î¥Ñ¥é¥á¡¼¥¿ÀßÄê
with open(os.path.join(model_dir, 'sample_torch_model.pth'), 'rb') as f:
model.load_state_dict(torch.load(f))
# Æȼ«¥Ñ¥é¥á¡¼¥¿¤òÀßÄê
with open(os.path.join(model_dir, 'sample_torch_model_scalar.json')) as f:
my_state = json.load(f)
for k,v in my_state.items():
model.__dict__[k] = v
logger.info('END model_fn')
return model
def input_fn(request_body, content_type=JSON_CONTENT_TYPE):
"""ÆþÎϥǡ¼¥¿¤Î·Á¼°ÊÑ´¹."""
logger.info('START input_fn')
logger.info(f'content_type: {content_type}')
logger.info(f'request_body: {request_body}')
logger.info(f'type: {type(request_body)}')
if content_type == XNPY_CONTENT_TYPE:
stream = BytesIO(request_body)
input_data = np.load(stream)
elif content_type == CSV_CONTENT_TYPE:
request_body = request_body.encode("utf-8") if isinstance(request_body, str) else request_body
input_data = np.loadtxt(BytesIO(request_body), delimiter=",")
elif content_type == JSON_CONTENT_TYPE:
input_data = np.array(json.loads(request_body))
else:
# TODO: content_type¤Ë±þ¤¸¤Æ¥Ç¡¼¥¿·¿ÊÑ´¹
logger.error(f"content_type invalid: {content_type}")
input_data = {"errors": [f"content_type invalid: {content_type}"]}
logger.info('END input_fn')
return input_data
def predict_fn(input_data, model):
"""¿äÏÀ."""
logger.info('START predict_fn')
if isinstance(input_data, dict) and 'errors' in input_data:
logger.info('SKIP predict_fn')
logger.info('END predict_fn')
return input_data
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
model.eval()
# ÀâÌÀÊÑ¿ô¤Îɸ½à²½
scaler = StandardScaler()
scaler.set_params(**model.my_scaler_params)
scaler.mean_ = model.my_scaler_mean
scaler.var_ = model.my_scaler_var
scaler.scale_ = model.my_scaler_scale
scaled_input_data = scaler.transform(input_data)
converted_input_data = torch.Tensor(scaled_input_data)
# ¿äÏÀ
with torch.no_grad():
logger.info('END predict_fn')
return model(converted_input_data.to(device))
def output_fn(prediction, accept=JSON_CONTENT_TYPE):
"""½ÐÎϥǡ¼¥¿¤Î·Á¼°ÊÑ´¹."""
logger.info('START output_fn')
logger.info(f"accept: {accept}")
if isinstance(prediction, dict) and 'errors' in prediction:
logger.info('SKIP output_fn')
response = json.dumps(prediction)
content_type = JSON_CONTENT_TYPE
elif accept == XNPY_CONTENT_TYPE:
buffer = BytesIO()
np.save(buffer, prediction)
response = buffer.getvalue()
content_type = XNPY_CONTENT_TYPE
elif accept == JSON_CONTENT_TYPE:
response = json.dumps({"results": [prediction.data[i].item() for i in range(len(prediction.data))]})
content_type = JSON_CONTENT_TYPE
else:
# TODO: ¥³¥ó¥Æ¥ó¥Ä¥¿¥¤¥×¤Ë±þ¤¸¤ÆÊÑ´¹
response = json.dumps({"results": [prediction.data[i].item() for i in range(len(prediction.data))]})
content_type = JSON_CONTENT_TYPE
logger.info('END output_fn')
return response, content_type
if __name__ == '__main__':
# ·±Îý¤·¤Æ¤«¤é¥Ç¥×¥í¥¤¤¹¤ë¾ì¹ç¤Ï¤³¤³¤Ç¹Ô¤¦
logger.info("process main!")
pass
}}
*** ²òÀâ [#s027846c]
#html(<div class="pl10">)
#TODO
#html(</div>)
#html(</div>)
** ¥¨¥ó¥É¥Ý¥¤¥ó¥È¤ÎºîÀ®¡¢¥Ç¥×¥í¥¤ [#f8f82725]
#html(<div class="pl10">)
¥Î¡¼¥È¥Ö¥Ã¥¯¥¤¥ó¥¹¥¿¥ó¥¹¾å¤«¤é°Ê²¼¤ò¼Â¹Ô¤¹¤ë¡£
#mycode2(){{
# ¥¨¥ó¥É¥Ý¥¤¥ó¥È¤ÎºîÀ®¡¢¥Ç¥×¥í¥¤
sagemaker_session = sagemaker.Session()
role = get_execution_role()
role = sagemaker.get_execution_role()
pytorch_model = PyTorchModel(model_data="s3://¥Ð¥±¥Ã¥È̾/sample_model.tar.gz",
# ¥â¥Ç¥ë¤ÎºîÀ®
pytorch_model = PyTorchModel(model_data="s3://¥Ð¥±¥Ã¥È̾/sample_torch_model.tar.gz",
role=role,
framework_version='1.3.1',
entry_point="sample_model_endpoint.py")
entry_point="entry_point.py")
# ¥Ç¥×¥í¥¤¥Ñ¥é¥á¡¼¥¿
deploy_params = {
'instance_type' : 'ml.t2.medium' # ¤ª»î¤·ÍÑ (https://aws.amazon.com/jp/sagemaker/pricing/instance-types/ )
,'initial_instance_count' : 1 # ¤ª»î¤·ÍÑ
#,'endpoint_name' : 'sample-torch-model4' # ¥¨¥ó¥É¥Ý¥¤¥ó¥È̾¤ò»ØÄꤷ¤Æ¤Î¥Ç¥×¥í¥¤¤¬²¿¸Î¤«¤Ç¤¤Ê¤¤
}
predictor = pytorch_model.deploy(instance_type='ml.c4.xlarge', endpoint_name='pytorch-sample-model', initial_instance_count=1)
# ¥Ç¥×¥í¥¤
predictor = pytorch_model.deploy(**deploy_params)
}}
https://sagemaker.readthedocs.io/en/stable/sagemaker.pytorch.html#sagemaker.pytorch.model.PyTorchModel
#html(</div>)
#html(</div>)
* ¥Ç¥×¥í¥¤¤·¤¿¥¨¥ó¥É¥Ý¥¤¥ó¥È¤ò»È¤Ã¤Æ¿äÏÀ¤·¤Æ¤ß¤ë [#cab537f1]
#html(<div class="pl10">)
#mycode2(){{
import pandas as pd
# ÆþÎϥǡ¼¥¿ ([Éô²°¤Î¹¤µ, ÃÛǯ¿ô])
input_data = [[60.0, 10.0], [50.0, 10.0], [40.0, 10.0]]
# ¿äÏÀ
predict_data = np.array(input_data)
results = predictor.predict(predict_data)
# ·ë²Ìɽ¼¨
result_df = pd.DataFrame(results, columns=["²ÈÄÂ(Ëü±ß)"])
result_df["¹¤µ(Ö)"] = predict_data[:,0]
result_df["ÃÛǯ¿ô"] = predict_data[:,1]
result_df
}}
·ë²Ì
#html(){{
<style scoped="">
.dataframe {
border: none;
border-collapse: collapse;
border-spacing: 0;
color: black;
font-size: 14px;
table-layout: fixed;
}
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
padding: 4px;
}
.dataframe thead th {
text-align: right;
padding: 4px;
}
</style>
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>²ÈÄÂ(Ëü±ß)</th>
<th>¹¤µ(Ö)</th>
<th>ÃÛǯ¿ô</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>8.117216</td>
<td>60.0</td>
<td>10.0</td>
</tr>
<tr>
<th>1</th>
<td>7.191902</td>
<td>50.0</td>
<td>10.0</td>
</tr>
<tr>
<th>2</th>
<td>6.266588</td>
<td>40.0</td>
<td>10.0</td>
</tr>
</tbody>
</table>
}}
#html(</div>)
* Lambda¤Ê¤É¤«¤é¥¨¥ó¥É¥Ý¥¤¥ó¥È¤òÍøÍѤ¹¤ë [#i0483a0d]
#html(<div class="pl10">)
#mycode2(){{
#
# sage maker°Ê³°¤«¤é¥¨¥ó¥É¥Ý¥¤¥ó¥È¤òÍøÍѤ·¤Æ¿äÏÀ
#
import boto3
import json
# ÆþÎϥǡ¼¥¿ ([Éô²°¤Î¹¤µ, ÃÛǯ¿ô])
input_data = [[60.0, 10.0], [50.0, 10.0], [40.0, 10.0]]
# ¥¨¥ó¥É¥Ý¥¤¥ó¥È̾
endpoint_name = "pytorch-inference-2020-02-28-12-35-37-541"
# JSON¤òÁ÷¿®¤¹¤ë¾ì¹ç
request_body = json.dumps(input_data)
content_type = "application/json"
accept_type = "application/json"
# CSV¤òÁ÷¿®¤¹¤ë¾ì¹ç
#request_body = '\n'.join([','.join([str(x) for x in rec]) for rec in input_data])
#content_type = "text/csv"
#accept_type = "application/json"
# ¿äÏÀ
client = boto3.client('sagemaker-runtime')
response = client.invoke_endpoint(
EndpointName=endpoint_name,
Body=request_body,
ContentType=content_type,
Accept=accept_type
)
# ·ë²Ìɽ¼¨
print("### response (Body°Ê³°)###")
print(json.dumps({k:v for k,v in response.items() if k != 'Body'}, indent=4))
print("### response (Body) ###")
response_dict = json.loads(response['Body'].read().decode("utf-8"))
print(json.dumps(response_dict, indent=4))
}}
·ë²Ì
#mycode3(){{
### response (Body°Ê³°)###
{
"ResponseMetadata": {
"RequestId": "f5cca038......",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"x-amzn-requestid": "f5cca038......",
"x-amzn-invoked-production-variant": "AllTraffic",
"date": "Sat, 29 Feb 2020 XX:XX:XX GMT",
"content-type": "application/json",
"content-length": "69"
},
"RetryAttempts": 0
},
"ContentType": "application/json",
"InvokedProductionVariant": "AllTraffic"
}
### response (Body) ###
{
"results": [
8.117216110229492,
7.191902160644531,
6.26658821105957
]
}
}}
#html(</div>)
* ¸åÊÒÉÕ¤± [#j5efc1d5]
#html(<div class="pl10">)
¥¨¥ó¥É¥Ý¥¤¥ó¥È¤Îºï½ü
¥Î¡¼¥È¥Ö¥Ã¥¯¥¤¥ó¥¹¥¿¥ó¥¹¤«¤é°Ê²¼¤ò¼Â¹Ô¤¹¤ë»ö¤Ç¥¨¥ó¥É¥Ý¥¤¥ó¥È¤Îºï½ü¤¬²Äǽ¡£
#mycode2(python){{
import sagemaker
sagemaker.Session().delete_endpoint(predictor.endpoint)
}}
#html(</div>)