PyTorch 是一種使用動態計算圖形的常見深度學習框架,藉助它,咱們可使用命令語言和經常使用的 Python 代碼輕鬆開發深度學習模型。推理是使用訓練模型進行預測的過程。對於使用 PyTorch 等框架的深度學習應用程序,推理成本佔計算成本的90%。因爲深度學習模型須要不一樣數量的 GPU、CPU 和內存資源,爲推理選擇適當的實例有難度。在一個獨立的 GPU 實例上對其中一個資源進行優化一般會致使其餘資源利用不足。所以,咱們可能要爲未使用的資源付費。html
Amazon Elastic Inference 經過支持將適量 GPU 支持推理加速附加到任何 Amazon SageMaker 或 EC2 實例或 Amazon ECS 任務中來解決此問題。咱們能夠在 AWS 中選擇最適合應用程序總體計算和內存需求的 CPU 實例,並單獨附加所需的適量 GPU 支持推理加速,以知足應用程序延遲要求。如此一來,就能夠更加高效地使用資源並下降推理成本。今天,PyTorch 加入 TensorFlow 和 Apache MXNet 做爲 Elastic Inference 支持的深度學習框架。撰寫本文時發行的版本爲1.3.1。python
Amazon SageMaker 是一項徹底託管的服務,爲每一個開發人員和數據科學家提供在 TensorFlow、Apache MXNet 和 PyTorch 等深度學習框架上快速構建、訓練和部署機器學習(ML)模型的能力。Amazon SageMaker 經過提供在生產環境中部署機器學習模型和監控模型質量所需的一切來輕鬆生成預測。數據庫
下文將介紹如何使用 Elastic Inference 下降成本及改善 PyTorch 模型在 Amazon SageMaker 上的延遲。網絡
咱們如今討論 TorchScript,它是從 PyTorch 代碼中建立可序列化及可優化模型的一種方式。咱們必須將模型轉換爲 TorchScript 才能將 Elastic Inference 與 PyTorch 結合使用。session
PyTorch 使用動態計算圖形大大簡化了模型開發過程。然而,此模式爲生產模型部署帶來獨特的挑戰。在生產環境中,較好的方法是對模型進行靜態圖形表示。這不只能讓咱們在使用 Python 較少的環境中使用該模型,還容許對性能和內存進行優化。架構
TorchScript 經過提供將模型編譯並導出至沒有使用 Python 的圖形表示中的能力來彌合這一差距。咱們能夠經過將 PyTorch 模型轉換爲 TorchScript 來在任何生產環境中運行您的模型。TorchScript 還能夠執行適時的圖形級優化,從而提供比標準 PyTorch 更高的性能。app
要將 Elastic Inference 與 PyTorch 結合使用,必須將模型轉換爲 TorchScript 格式,並將推理 API 用於 Elastic Inference。本文經過示例說明了如何將模型編譯到 TorchScript 及如何使用支持 Elastic Inference 的 PyTorch 衡量端到端推理延遲的基準。最後,本文比較了各類不一樣的實例和加速器組合與獨立 CPU 和 GPU 實例的性能和成本指標。框架
咱們可使用 tracing 或 scripting 將 PyTorch 模型編譯到TorchScript 中。它們都會生成計算圖形,但方式不一樣。機器學習
編寫模型腳本的推薦方法一般是編譯到 TorchScript,由於它會保留全部模型邏輯。然而在撰寫本文時,可以使用 PyTorch 1.3.1編寫腳本的模型集比可跟蹤的模型集小。模型或許能夠跟蹤,但不能編寫腳本,或者根據沒法跟蹤。咱們可能須要修改您的模型代碼,以使其與 TorchScript 兼容。ide
因爲 Elastic Inference 目前在 PyTorch 1.3.1 中處理控制流操做的方式,對於包含不少條件分支的腳本模型而言,推理延遲可能不是最優的。同時嘗試跟蹤和腳本,瞭解模型在使用 Elastic Inference 時的表現。隨着1.3.1版本的發佈,跟蹤模型的表現可能比其腳本版本好。
有關更多信息,請參閱 PyTorch 網站上的 TorchScript 介紹教程。
腳本經過直接分析元代碼來構建計算圖形和保留控制流。下面的示例顯示瞭如何使用腳本編譯模型。它使用 TorchVision 爲 ResNet-18 預訓練的權重。咱們能夠將生成的腳本模型保存到一個文件中,而後使用支持 Elastic Inference 的 PyTorch 將它與 torch.jit.load 一塊兒加載。請參閱如下代碼:
import torchvision, torch # Call eval() to set model to inference mode model = torchvision.models.resnet18(pretrained=True).eval() scripted_model = torch.jit.script(model)
跟蹤使用示例輸入記錄咱們在該輸入上運行模型時執行的操做。這意味着,因爲只是經過一個輸入來跟蹤代碼,而後來編譯圖形,控制流可能會被擦除。例如,模型定義可能擁有填充特定大小x圖像的代碼。若是使用另外一種大小y的圖像跟蹤模型,將不會填充被注入跟蹤模型的將來的大小x輸入。發生這種狀況是由於在使用特定輸入進行跟蹤時,並不會執行全部的代碼路徑。
下面的示例顯示如何使用跟蹤與隨機化張量輸入編譯模型。它還使用 TorchVision 爲 ResNet-18 預訓練的權重。咱們必須將 torch.jit.optimized_execution 上下文塊與第二個參數用戶設備序號,以將跟蹤模型與 Elastic Inference 結合使用。這個修改的函數定義接受兩個參數,僅可經過支持 Elastic Inference 的 PyTorch 框架提供。
若是使用標準的 PyTorch 框架跟蹤模型,請忽略torch.jit.optimized_execution 數據塊。咱們仍然能夠將生成的腳本模型保存到一個文件中,而後使用支持 Elastic Inference 的 PyTorch 將它與 torch.jit.load 一塊兒加載。請參閱如下代碼:
# ImageNet pre-trained models take inputs of this size. x = torch.rand(1,3,224,224) # Call eval() to set model to inference mode model = torchvision.models.resnet18(pretrained=True).eval() # Required when using Elastic Inference with torch.jit.optimized_execution(True, {‘target_device’: ‘eia:0’}): traced_model = torch.jit.trace(model, x)
跟蹤和腳本的輸出爲 ScriptModule,它是標準 PyTorch 的 nn.Module 的 TorchScript 模擬。對 TorchScript 模塊進行序列化和反序列化分別與調用 torch.jit.save()和 torch.jit.load() 同樣。它是使用 torch.save () 和 torch.load () 保存和加載標準 PyTorch 模型的 JIT 模擬。請參閱如下代碼:
torch.jit.save(traced_model, 'resnet18_traced.pt') torch.jit.save(scripted_model, 'resnet18_scripted.pt') traced_model = torch.jit.load('resnet18_traced.pt') scripted_model = torch.jit.load('resnet18_scripted.pt')
不一樣於保存的標準 PyTorch 模型,保存的 TorchScript 模型不會綁定到特定的類和代碼目錄。咱們能夠直接加載保存的 TorchScript 模型,無需先實例化模型類。如此一來,就能夠在沒有Python的環境中使用 TorchScript 模型。
下文將逐步介紹使用 Amazon SageMaker 託管終端節點衡量 DenseNet-121的支持 Elastic Inference 的 PyTorch 推理延遲基準的過程。
DenseNet-121是一種卷積神經網絡(CNN),已在各類數據集的圖像分類中取得一流結果。它的架構鬆散地創建在 ResNet(另外一個常見的圖像分類CNN)的基礎上。
Amazon SageMaker 託管能力使得將模型部署到 HTTPS 終端節點變成可能,從而使模型可用於經過 HTTP 請求執行推理。
此演示將 EC2 實例用做客戶端,以啓動並與 Amazon SageMaker 託管的終端節點交互。此客戶端實例沒有附加加速器;咱們將啓動終端節點,該終端節點可預置附加了加速器的託管實例。若要完成此演練,必須先完成如下先決條件:
Ø 建立 IAM 角色並添加 Amazon SageMaker FullAccess,此策略可授予使用 Amazon Elastic Inference 和 Amazon SageMaker 的權限。
Ø 啓動 m5.large CPU EC2 實例。
本文使用 DLAMI 中內置的 Elastic Inference 支持的 PyTorch Conda 環境,只訪問 Amazon SageMaker 開發工具包,並使用 PyTorch 1.3.1 保存 DenseNet-121 權重。當 Amazon SageMaker Notebook 支持發佈時,咱們可使用 Notebook 內核替代之。
託管的實例和加速器經過 AWS DL Container 使用 Elastic Inference 支持的 PyTorch。爲客戶端實例選擇的環境只能簡化 Amazon SageMaker 開發工具包的使用,及使用 PyTorch 1.3.1保存模型權重。
請完成如下步驟:
source activate amazonei_pytorch_p36
import torch, torchvision import subprocess # Toggle inference mode model = torchvision.models.densenet121(pretrained=True).eval() cv_input = torch.rand(1,3,224,224) model = torch.jit.trace(model,cv_input) torch.jit.save(model, 'model.pt') subprocess.call(['tar', '-czvf', 'densenet121_traced.tar.gz', 'model.pt'])
此腳本遵守 Amazon SageMaker 使用的命名約定(默認爲 model.pt)建立原始碼。DenseNet-121 的模型權重爲通過預訓練的 ImageNet,而且從 TorchVision 中提取。有關更多信息,請參閱將 PyTorch 與 SageMaker Python 開發工具包結合使用。
python create_sm_tarball.py
import sagemaker from sagemaker.pytorch import PyTorchModel sagemaker_session = sagemaker.Session() region = YOUR_DESIRED_REGION role = YOUR_IAM_ROLE_ARN instance_type = 'c5.large' accelerator_type = 'eia2.medium' ecr_image = '763104351884.dkr.ecr.{}.amazonaws.com/pytorch-inference-eia:1.3.1-cpu-py3'.format(region) # Satisfy regex endpoint_name = 'pt-ei-densenet121-traced-{}-{}'.format(instance_type, accelerator_type).replace('.', '').replace('_', '') tar_filename = 'densenet121_traced.tar.gz' # script.py should be blank to use default EI model_fn and predict_fn # For non-EI PyTorch usage, must implement own model_fn entry_point = 'script.py' # Returns S3 bucket URL print('Upload tarball to S3') model_data = sagemaker_session.upload_data(path=tar_filename) pytorch = PyTorchModel(model_data=model_data, role=role, image=ecr_image, entry_point=entry_point, sagemaker_session=sagemaker_session) # Function will exit before endpoint is finished creating predictor = pytorch.deploy(initial_instance_count=1, instance_type='ml.' + instance_type, accelerator_type='ml.' + accelerator_type, endpoint_name=endpoint_name, wait=False)
咱們須要修改腳本,以包含本身的AWS帳戶ID、區域和 IAM ARN 角色。該腳本使用咱們之前建立的原始碼和空白入口點腳原本預置 Amazon SageMaker 託管的終端節點。此示例代碼可衡量附加了 ml.eia2.medium 加速器的 ml.c5.large 託管實例的基準。
咱們沒必要直接提供映像來建立終端節點,但爲了清楚起見,此文會提供。有關其餘框架的可用 Docker 容器的更多信息,請參閱深度學習容器鏡像。
python create_sm_endpoint.py
import sagemaker from sagemaker.pytorch import PyTorchPredictor import torch import boto3 import datetime import math import numpy as np import time instance_type = 'c5.large' accelerator_type = 'eia2.medium' endpoint_name = 'pt-ei-densenet121-traced-{}-{}'.format(instance_type, accelerator_type).replace('.', '').replace('_', '') predictor = PyTorchPredictor(endpoint_name) data = torch.rand(1,3,224,224) # Do warmup round of 100 inferences to warm up routers print('Doing warmup round of 100 inferences (not counted)') for i in range(100): output = predictor.predict(data) time.sleep(15) client_times = [] print('Running 1000 inferences for {}:'.format(endpoint_name)) cw_start = datetime.datetime.utcnow() for i in range(1000): client_start = time.time() output = predictor.predict(data) client_end = time.time() client_times.append((client_end - client_start)*1000) cw_end = datetime.datetime.utcnow() print('Client end-to-end latency percentiles:') client_avg = np.mean(client_times) client_p50 = np.percentile(client_times, 50) client_p90 = np.percentile(client_times, 90) client_p95 = np.percentile(client_times, 95) client_p100 = np.percentile(client_times, 100) print('Avg | P50 | P90 | P95 | P100') print('{:.4f} | {:.4f} | {:.4f} | {:.4f}\n'.format(client_avg, client_p50, client_p90, client_p95, client_p100)) print('Getting Cloudwatch:') cloudwatch = boto3.client('cloudwatch') statistics=['SampleCount', 'Average', 'Minimum', 'Maximum'] extended=['p50', 'p90', 'p95', 'p100'] # Give 5 minute buffer to end cw_end += datetime.timedelta(minutes=5) # Period must be 1, 5, 10, 30, or multiple of 60 # Calculate closest multiple of 60 to the total elapsed time factor = math.ceil((cw_end - cw_start).total_seconds() / 60) period = factor * 60 print('Time elapsed: {} seconds'.format((cw_end - cw_start).total_seconds())) print('Using period of {} seconds\n'.format(period)) cloudwatch_ready = False # Keep polling CloudWatch metrics until datapoints are available while not cloudwatch_ready: time.sleep(30) print('Waiting 30 seconds ...') # Must use default units of microseconds model_latency_metrics = cloudwatch.get_metric_statistics(MetricName='ModelLatency', Dimensions=[{'Name': 'EndpointName', 'Value': endpoint_name}, {'Name': 'VariantName', 'Value': "AllTraffic"}], Namespace="AWS/SageMaker", StartTime=cw_start, EndTime=cw_end, Period=period, Statistics=statistics, ExtendedStatistics=extended ) # Should be 1000 if len(model_latency_metrics['Datapoints']) > 0: print('{} latency datapoints ready'.format(model_latency_metrics['Datapoints'][0]['SampleCount'])) print('Side-car latency percentiles:') side_avg = model_latency_metrics['Datapoints'][0]['Average'] / 1000 side_p50 = model_latency_metrics['Datapoints'][0]['ExtendedStatistics']['p50'] / 1000 side_p90 = model_latency_metrics['Datapoints'][0]['ExtendedStatistics']['p90'] / 1000 side_p95 = model_latency_metrics['Datapoints'][0]['ExtendedStatistics']['p95'] / 1000 side_p100 = model_latency_metrics['Datapoints'][0]['ExtendedStatistics']['p100'] / 1000 print('Avg | P50 | P90 | P95 | P100') print('{:.4f} | {:.4f} | {:.4f} | {:.4f}\n'.format(side_avg, side_p50, side_p90, side_p95, side_p100)) cloudwatch_ready = True
此腳本使用大小爲 1 x 3 x 224 x 224 的張量(圖像分類的標準值)。首先,它會運行一系列的100個預熱推理,而後再運行1000個推理。延遲百分位數僅使用這1000個推理報告。
本文使用延遲指標 ModelLatency。此指標將發送到 Amazon CloudWatch 並在 Amazon SageMaker 系統內捕獲推理延遲。有關更多信息,請參閱使用Amazon CloudWatch監控Amazon SageMaker。
咱們須要使用 TorchScript 編譯模型並在原始碼中將其另存爲 model.pt。
當使用附加到託管實例的加速器調用終端節點時,Amazon SageMaker 將在默認狀況下調用默認的 model_fn 和 predict_fn。若是在沒有加速器的 Amazon SageMaker 中使用 PyTorch,須要經過入口點腳本提供本身的 model_fn 實現。
默認的 model_fn 使用 torch.jit.load('model.pt') 加載模型權重,由於它假設咱們之前使用 TorchScript 對模型進行了序列化,而且遵循了文件名約定。附加了加速器時,默認的 predict_fn 使用 torch.jit.optimized_execution 數據庫,指定模型應通過優化後才能在附加的Elastic Inference加速器上運行。不然 predict_fn 將以標準的 PyTorch 方式進行推理。請注意,撰寫本文時 Amazon SageMaker 不支持多附加,所以設備序號始終被設置爲0。
若是決定在使用 Elastic Inference 時實施本身的 predict_fn,必須記得使用 torch.jit.optimized_execution 上下文,不然推理將徹底運行在託管實例上,且不會使用附加的加速器。有關更多信息請參閱將 PyTorch 與 SageMaker Python 開發工具包結合使用。
默認處理程序提供在 GitHub 上。
python benchmark_sm_endpoint.py
隨後應該會看到相似於如下內容的輸出:
Doing warmup round of 100 inferences (not counted) Running 1000 inferences for pt-ei-densenet121-traced-c5large-eia2medium: Client end-to-end latency percentiles: Avg | P50 | P90 | P95 | P100 64.7758 | 61.7952 | 73.7203 | 77.3841 Getting Cloudwatch: Time elapsed: 364.777493 seconds Using period of 420 seconds Waiting 30 seconds ... 1000.0 latency datapoints ready Side-car latency percentiles: Avg | P50 | P90 | P95 | P100 47.8507 | 46.1647 | 51.7429 | 56.7705
部署新的推理工做負載時,有不少實例類型可供選擇。應該考慮如下關鍵參數:
現已準備就緒,可應用此過程來選擇最佳實例以運行 DenseNet-121。首先,評估應用程序的內存和 CPU 要求,並將符合要求的託管實例和加速器子集列入候選名單。
接下來,瞭解一下延遲性能。本文對每一個實例都使用相同的張量輸入和 DenseNet-121 的 TorchVision ImageNet 預訓練權重。咱們使用此輸入在模型上運行1000次推理,收集每次運行的延遲,並報告平均延遲和第90個百分位的延遲(P90延遲)。本文要求 P90 延遲低於80毫秒,也就是說全部推理調用中90%的調用延遲應低於80ms。
咱們將 Amazon Elastic Inference 加速器附加在三種類型的 CPU 託管實例上,併爲其各自運行前述性能測試。下面列出了每小時價格、每次推理調用的平均延遲和每100000次推理的費用。下面的全部組合均知足延遲預置。
能夠看到不一樣託管實例對延遲的影響。對於相同加速器類型,使用更強大的託管實例不會顯著改善延遲。然而,附加較大的加速器可下降延遲,由於模型運行在加速器上,且較大的加速器擁有更多的資源,如 GPU 計算和內存。咱們應該選擇可爲應用程序提供足夠 CPU 內存的最便宜的託管實例類型。ml.m5.large 或 ml.c5.large 足夠用於不少使用案例,但並不是所有使用案例。
基於前述標準,本文選擇知足延遲要求的兩個成本最低的選項,即帶有 ml.eia2.medium 的 ml.c5.large 和帶有 ml.eia2.medium 的ml.m5.large。對於此使用案例,二者都是可行的。
本文還收集了 CPU 和 GPU 託管實例的延遲和性價比數據,而且還與前述Elastic Inference 基準進行了比較。所用的獨立 CPU 實例包括ml.c5.xl、ml.c5.4xl、ml.m5.xl 和 ml.m5.4xl。所用的獨立GPU實例包括 ml.p3.2xl、ml.g4dn.xl、ml.g4dn.2xl 和 ml.g4dn.4xl。
下面的彙總表顯示了 Elastic Inference 支持的選項的性價比數據,隨後顯示了獨立實例選項。
爲了更好地瞭解 Elastic Inference 在獨立 CPU 和 GPU 實例上帶來的性價比,咱們能夠針對每種硬件類型使用圖形顯示此延遲和成本數據。下面的條形圖繪製了每100000次推理的費用,線形圖繪製了 P90 推理延遲(以毫秒爲單位)。深灰色條形指的是帶有 Elastic Inference 加速器的實例,綠色條形指的是獨立的 GPU 實例,藍色條形指的是獨立的 CPU 實例。
跟預期的同樣,CPU 實例的性能不如 GPU 實例的性能。ml.g4dn.xl 實例的速度約比 CPU 實例快7倍。全部的獨立 CPU 實例都不知足80ms 的 P90 延遲閾值。
然而這些 CPU 實例在附加了 Elastic Inference 的狀況下性能更好,由於它們受益於 GPU 加速。帶有 ml.eia2.medium 的 ml.c5.large實例將推理速度提升到獨立 CPU 實例的接近3倍。然而獨立的 GPU 實例仍然比附加了 Elastic Inference 的 CPU 實例好;ml.g4dn.xl 的速度比帶有 ml.eia2.medium 的 ml.c5.large 的速度快兩倍多一點。請注意,ml.g4dn.xl、ml.g4dn.2xl和ml.g4dn.4xl 實例的延遲大體相等,差別可忽略不計。所有三個 ml.g4dn 實例都具備相同的GPU,但較大的 ml.g4dn 實例擁有更多的vCPU和內存資源。對於 DenseNet-121 而言,增長 vCPU 和內存資源不會改善推理延遲。
Elastic Inference 和獨立的 GPU 實例都知足延遲要求。
在成本方面,帶有ml.eia2.medium 的 ml.c5.large 表現比較突出。雖然帶有 ml.eia2.medium 的 ml.c5.large不具備最低的每小時價格,但它每100000次推理的費用是最低的。有關每小時訂價的更多信息,請參閱 Amazon SageMaker 訂價。
能夠得出結論:每小時成本較低的實例在每次推理時所花費的費用並不必定也低。這是由於它們的每次推理延遲可能會較高。一樣地,每次推理時延遲較低的實例可能不會產生較低的每次推理費用。ml.m5.xlarge 和 ml.c5.xlarge CPU 實例擁有最低的每小時價格,但其每次推理的費用仍高於大多數Elastic Inference 和獨立 GPU 選項。較大的 ml.m5.4xlarge 和 ml.c5.4xlarge 實例具備較高的延遲、較多的每小時費用,所以,其每次推理的費用高於全部 Elastic Inference 選項。獨立的GPU 實例因爲 CUDA 操做所利用的高計算並行化,全面實現了最佳延遲。然而 Elastic Inference 的每次推理費用最低。
使用 Amazon Elastic Inference,咱們能夠得到一箭雙鵰的結果,能夠最有效地利用GPU提供的並行化和推理加速,得到比 CPU 和 GPU 獨立實例更大的成本效益。此外,咱們還能夠靈活地解耦託管實例和推理加速硬件,以便針對 vCPU、內存和應用程序須要的全部其餘資源靈活地優化硬件。
前述測試證實,帶有 ml.eia2.medium 的 ml.c5.large 是費用最低的選項,它知足運行 DenseNet-121 的延遲標準和內存使用要求。
本文使用的延遲指標(CloudWatch Metrics 中發佈的 ModelLatency)可衡量 Amazon SageMaker 內的延遲。此延遲指標未考慮到從應用程序到 Amazon SageMaker 的延遲。確保在衡量應用程序的基準時考慮到這些延遲。
小結
Amazon Elastic Inference 是一項靈活的低成本解決方案,適用於Amazon SageMaker 上的 PyTorch 推理工做負載。經過將 Elastic Inference 加速器附加到 Amazon SageMaker 實例,咱們能夠得到相似於 GPU 的推理加速並保持比獨立的 Amazon SageMaker GPU 和 CPU 實例更高的成本效益。更多信息請參閱什麼是 Amazon Elastic Inference?