

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 準備模型以進行編譯
<a name="neo-compilation-preparing-model"></a>

SageMaker Neo 需要機器學習模型才能滿足特定的輸入資料形狀。編譯所需的輸入形狀取決於您使用的深度學習架構。正確格式化模型輸入形狀後，請根據以下要求儲存模型。儲存模型後，壓縮模型成品。

**Topics**
+ [SageMaker Neo 應有哪些輸入資料形狀？](#neo-job-compilation-expected-inputs)
+ [儲存 SageMaker Neo 的模型](#neo-job-compilation-how-to-save-model)

## SageMaker Neo 應有哪些輸入資料形狀？
<a name="neo-job-compilation-expected-inputs"></a>

編譯模型之前，請先確定模型已正確格式化。Neo 應有已訓練模型 (JSON 格式或清單格式) 之預期資料輸入的名稱和形狀。預期輸入有架構限制。

以下是 SageMaker Neo 預期的輸入形狀：

### Keras
<a name="collapsible-section-1"></a>

使用訓練模型之字典格式，指定預期資料輸入的名稱和形狀 (NCHW 格式)。請注意，雖然 Keras 模型成品應該以 NHWC (通道最後一個) 格式上傳，但應該以 NCHW (通道第一個) 格式指定 DataInputConfig。所需的字典格式如下：
+ 若為一個輸入：`{'input_1':[1,3,224,224]}`
+ 若為兩個輸入：`{'input_1': [1,3,224,224], 'input_2':[1,3,224,224]}`

### MXNET/ONNX
<a name="collapsible-section-2"></a>

使用訓練模型之字典格式，指定預期資料輸入的名稱和形狀 (NCHW 格式)。所需的字典格式如下：
+ 若為一個輸入：`{'data':[1,3,1024,1024]}`
+ 若為兩個輸入：`{'var1': [1,1,28,28], 'var2':[1,1,28,28]}`

### PyTorch
<a name="collapsible-section-3"></a>

若為 PyTorch 模型，如果您同時符合下列兩個條件，就不需要提供預期資料輸入的名稱和形狀：
+ 您使用 PyTorch 2.0 或更新的版本建立模型定義檔案。如需如何建立定義檔案的更多相關資訊，請參閱*儲存 SageMaker Neo 模型*的[PyTorch](#how-to-save-pytorch)一節。
+ 您正在編譯雲端執行個體的模型。如需 SageMaker Neo 支援之執行個體類型的更多相關資訊，請參閱[支援的執行個體類型和架構](neo-supported-cloud.md)。

如果符合這些條件，SageMaker Neo 會從您使用 PyTorch 建立的模型定義檔案 (.pt 或 .pth) 取得輸入組態。

否則您必須執行以下操作：

使用訓練模型之字典格式，指定預期資料輸入的名稱和形狀 (NCHW 格式)。或者，您可以僅使用清單格式指定造型。所需的字典格式如下：
+ 字典格式的一個輸入：`{'input0':[1,3,224,224]}`
+ 清單格式的一個輸入：`[[1,3,224,224]]`
+ 字典格式的兩個輸入：`{'input0':[1,3,224,224], 'input1':[1,3,224,224]}`
+ 清單格式的兩個輸入：`[[1,3,224,224], [1,3,224,224]]`

### TensorFlow
<a name="collapsible-section-4"></a>

使用訓練模型之字典格式，指定預期資料輸入的名稱和形狀 (NHWC 格式)。所需的字典格式如下：
+ 若為一個輸入：`{'input':[1,1024,1024,3]}`
+ 若為兩個輸入：`{'data1': [1,28,28,1], 'data2':[1,28,28,1]}`

### TFLite
<a name="collapsible-section-5"></a>

使用訓練模型之字典格式，指定預期資料輸入的名稱和形狀 (NHWC 格式)。所需的字典格式如下：
+ 若為一個輸入：`{'input':[1,224,224,3]}`

**注意**  
SageMaker Neo 僅針對 Edge 裝置目標支援 TensorFlow Lite。如需支援之 SageMaker Neo Edge 裝置目標的清單，請參閱 SageMaker Neo [Devices](neo-supported-devices-edge-devices.md#neo-supported-edge-devices) 頁面。如需支援之 SageMaker Neo 雲端執行個體目標的清單，請參閱 SageMaker Neo [支援的執行個體類型和架構](neo-supported-cloud.md) 頁面。

### XGBoost
<a name="collapsible-section-6"></a>

不需要輸入資料的名稱和形狀。

## 儲存 SageMaker Neo 的模型
<a name="neo-job-compilation-how-to-save-model"></a>

以下程式碼範例顯示如何儲存模型，使其與 Neo 相容。模型必須封裝為壓縮的 tar 檔案 (`*.tar.gz`)。

### Keras
<a name="how-to-save-tf-keras"></a>

Keras 模型需要一個模型定義檔案 (`.h5`)。

有兩個選項可以儲存 Keras 模型，使其與 SageMaker Neo 相容：

1. 使用 `model.save("<model-name>", save_format="h5")` 匯出為 `.h5` 格式。

1. 匯出後凍結 `SavedModel`。

以下是如何將 `tf.keras` 模型匯出為凍結圖形的範例 (選項二)：

```
import os
import tensorflow as tf
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras import backend

tf.keras.backend.set_learning_phase(0)
model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, input_shape=(224, 224, 3), pooling='avg')
model.summary()

# Save as a SavedModel
export_dir = 'saved_model/'
model.save(export_dir, save_format='tf')

# Freeze saved model
input_node_names = [inp.name.split(":")[0] for inp in model.inputs]
output_node_names = [output.name.split(":")[0] for output in model.outputs]
print("Input names: ", input_node_names)
with tf.Session() as sess:
    loaded = tf.saved_model.load(sess, export_dir=export_dir, tags=["serve"]) 
    frozen_graph = tf.graph_util.convert_variables_to_constants(sess,
                                                                sess.graph.as_graph_def(),
                                                                output_node_names)
    tf.io.write_graph(graph_or_graph_def=frozen_graph, logdir=".", name="frozen_graph.pb", as_text=False)

import tarfile
tar = tarfile.open("frozen_graph.tar.gz", "w:gz")
tar.add("frozen_graph.pb")
tar.close()
```

**警告**  
請勿使用 `model.save(<path>, save_format='tf')` 以 `SavedModel` 類別匯出模型。這種格式適合訓練，但不適合推論。

### MXNet
<a name="how-to-save-mxnet"></a>

MXNet 模型必須儲存為單一符號檔案 `*-symbol.json` 和單一參數 `*.params files`。

------
#### [ Gluon Models ]

使用 `HybridSequential` 類別定義神經網路。這將以符號程式設計的方式執行程式碼 (相對於命令式程式設計)。

```
from mxnet import nd, sym
from mxnet.gluon import nn

def get_net():
    net = nn.HybridSequential()  # Here we use the class HybridSequential.
    net.add(nn.Dense(256, activation='relu'),
            nn.Dense(128, activation='relu'),
            nn.Dense(2))
    net.initialize()
    return net

# Define an input to compute a forward calculation. 
x = nd.random.normal(shape=(1, 512))
net = get_net()

# During the forward calculation, the neural network will automatically infer
# the shape of the weight parameters of all the layers based on the shape of
# the input.
net(x)
                        
# hybridize model
net.hybridize()
net(x)

# export model
net.export('<model_name>') # this will create model-symbol.json and model-0000.params files

import tarfile
tar = tarfile.open("<model_name>.tar.gz", "w:gz")
for name in ["<model_name>-0000.params", "<model_name>-symbol.json"]:
    tar.add(name)
tar.close()
```

如需混合模型的更多相關資訊，請參閱 [MXNet 混合化文件](https://mxnet.apache.org/versions/1.7.0/api/python/docs/tutorials/packages/gluon/blocks/hybridize.html)。

------
#### [ Gluon Model Zoo (GluonCV) ]

GluonCV model zoo 模型已預先混合。因此您可以直接匯出這些模型。

```
import numpy as np
import mxnet as mx
import gluoncv as gcv
from gluoncv.utils import export_block
import tarfile

net = gcv.model_zoo.get_model('<model_name>', pretrained=True) # For example, choose <model_name> as resnet18_v1
export_block('<model_name>', net, preprocess=True, layout='HWC')

tar = tarfile.open("<model_name>.tar.gz", "w:gz")

for name in ["<model_name>-0000.params", "<model_name>-symbol.json"]:
    tar.add(name)
tar.close()
```

------
#### [ Non Gluon Models ]

所有非 Gluon 模型儲存至磁碟時，使用 `*-symbol` 和 `*.params` 檔案。因此，模型已經適用於 Neo 的正確格式。

```
# Pass the following 3 parameters: sym, args, aux
mx.model.save_checkpoint('<model_name>',0,sym,args,aux) # this will create <model_name>-symbol.json and <model_name>-0000.params files

import tarfile
tar = tarfile.open("<model_name>.tar.gz", "w:gz")

for name in ["<model_name>-0000.params", "<model_name>-symbol.json"]:
    tar.add(name)
tar.close()
```

------

### PyTorch
<a name="how-to-save-pytorch"></a>

PyTorch 模型必須以 `float32` 的輸入資料類型儲存為定義檔案 (`.pt` 或 `.pth`)。

若要儲存模型，請先使用 `torch.jit.trace` 方法再使用 `torch.save` 方法。這個程序將物件儲存至磁碟檔案，預設使用 python pickle (`pickle_module=pickle`) 儲存物件和一些中繼資料。接下來，將儲存的模型轉換為壓縮的 tar 檔案。

```
import torchvision
import torch

model = torchvision.models.resnet18(pretrained=True)
model.eval()
inp = torch.rand(1, 3, 224, 224)
model_trace = torch.jit.trace(model, inp)

# Save your model. The following code saves it with the .pth file extension
model_trace.save('model.pth')

# Save as a compressed tar file
import tarfile
with tarfile.open('model.tar.gz', 'w:gz') as f:
    f.add('model.pth')
f.close()
```

如果您使用 PyTorch 2.0 或更新版本儲存模型，SageMaker Neo 會從定義檔案衍生模型的輸入組態 (其輸入的名稱和形狀)。因此，編譯模型時，不需要指定 SageMaker AI 的資料輸入組態。

如果要防止 SageMaker Neo 衍生輸入組態，可以將 `torch.jit.trace` 的 `_store_inputs` 參數設定為 `False`。如果這麼做，編譯模型時，您必須為 SageMaker AI 指定資料輸入組態。

如需 `torch.jit.trace` 方法的更多相關資訊，請參閱 PyTorch 文件中的 [TORCH.JIT.TRACE](https://pytorch.org/docs/stable/generated/torch.jit.trace.html#torch.jit.trace)。

### TensorFlow
<a name="how-to-save-tf"></a>

TensorFlow 需要一個 `.pb` 或一個 `.pbtxt` 檔案，以及一個包含變數的變數目錄。若為凍結模型，只需要一個 `.pb` 或 `.pbtxt` 檔案。

以下程式碼範例顯示如何使用 tar Linux 命令壓縮模型。在終端機或 Jupyter 筆記本中執行下列命令 (如果您使用 Jupyter 筆記本，請在陳述式的開頭插入 `!` magic 命令)：

```
# Download SSD_Mobilenet trained model
!wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz

# unzip the compressed tar file
!tar xvf ssd_mobilenet_v2_coco_2018_03_29.tar.gz

# Compress the tar file and save it in a directory called 'model.tar.gz'
!tar czvf model.tar.gz ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb
```

此範例中使用的命令旗標可完成下列作業：
+ `c`：建立封存
+ `z`：使用 gzip 壓縮存檔
+ `v`：顯示存檔進度
+ `f`：指定存檔的檔案名稱

### 內建估算器
<a name="how-to-save-built-in"></a>

內建估算器是由特定架構的容器或特定演算法的容器製作。使用內建的 `.fit` 方法訓練模型時，內建演算法和特定架構估算器的估算器物件會以正確的格式儲存模型。

例如，您可以使用 `sagemaker.TensorFlow` 定義 TensorFlow 估算器：

```
from sagemaker.tensorflow import TensorFlow

estimator = TensorFlow(entry_point='mnist.py',
                        role=role,  #param role can be arn of a sagemaker execution role
                        framework_version='1.15.3',
                        py_version='py3',
                        training_steps=1000, 
                        evaluation_steps=100,
                        instance_count=2,
                        instance_type='ml.c4.xlarge')
```

然後使用 `.fit` 內建方法訓練模型：

```
estimator.fit(inputs)
```

最後用 `compile_model` 方法中的組建編譯模型之前：

```
# Specify output path of the compiled model
output_path = '/'.join(estimator.output_path.split('/')[:-1])

# Compile model
optimized_estimator = estimator.compile_model(target_instance_family='ml_c5', 
                              input_shape={'data':[1, 784]},  # Batch size 1, 3 channels, 224x224 Images.
                              output_path=output_path,
                              framework='tensorflow', framework_version='1.15.3')
```

使用 SageMaker Python SDK 中的 `compile_model` 方法訓練及編譯內建演算法時，您也可以使用 `sagemaker.estimator.Estimator` 類別初始化預估器物件：

```
import sagemaker
from sagemaker.image_uris import retrieve
sagemaker_session = sagemaker.Session()
aws_region = sagemaker_session.boto_region_name

# Specify built-in algorithm training image
training_image = retrieve(framework='image-classification', 
                          region=aws_region, image_scope='training')

training_image = retrieve(framework='image-classification', region=aws_region, image_scope='training')

# Create estimator object for training
estimator = sagemaker.estimator.Estimator(image_uri=training_image,
                                          role=role,  #param role can be arn of a sagemaker execution role
                                          instance_count=1,
                                          instance_type='ml.p3.8xlarge',
                                          volume_size = 50,
                                          max_run = 360000,
                                          input_mode= 'File',
                                          output_path=s3_training_output_location,
                                          base_job_name='image-classification-training'
                                          )
                                          
# Setup the input data_channels to be used later for training.                                          
train_data = sagemaker.inputs.TrainingInput(s3_training_data_location,
                                            content_type='application/x-recordio',
                                            s3_data_type='S3Prefix')
validation_data = sagemaker.inputs.TrainingInput(s3_validation_data_location,
                                                content_type='application/x-recordio',
                                                s3_data_type='S3Prefix')
data_channels = {'train': train_data, 'validation': validation_data}


# Train model
estimator.fit(inputs=data_channels, logs=True)

# Compile model with Neo                                                                                  
optimized_estimator = estimator.compile_model(target_instance_family='ml_c5',
                                          input_shape={'data':[1, 3, 224, 224], 'softmax_label':[1]},
                                          output_path=s3_compilation_output_location,
                                          framework='mxnet',
                                          framework_version='1.7')
```

如需使用 SageMaker SDK 編譯模型的更多相關資訊，請參閱[編譯模型 (Amazon SageMaker AI SDK)](neo-job-compilation-sagemaker-sdk.md)。