# 使用 TorchServe 部署 Model

**系統架構** TorchServe 主要提供兩個 API：**Management API 是用來設定 TorchServe 服務**，如註冊新的 Model 服務、卸載 Model 服務，監看 TorchServe 的狀態；**Inference API 用來取得辨識結果**。

![系統架構](https://cdn-images-1.medium.com/max/800/1*w2xTsxjj1aimOsVIVZPw0w.png)

* Frontend：指的是 request batching + **Inference API Handler。**&#x72;equest batching 是由 **Management API** 設定；**Inference API Handler** 則是由開發者自行撰寫
* Worker Process：指的是一組辨識程式的運行(trained model + model handler)，概念像是 Thread，可由 **Management API 進行設定運行的數量**。
* Model Store：透過 TorchServe 工具壓縮後的 model package。
* Backend：指的是管理 Worker Process 的程式，由 **Management API** 讓使用者管理。

**使用指南** 使用 TorchServe 需要準備三個檔案：Trained Model、Inference handler 、model Handler，將這三個東西使用 TorchServe Archiver 將其壓縮成 .mar 檔案。最後將 .mar 檔放入 TorchServe 裡，註冊後就佈署！

TorchServe 預設 8080 port 用於 inference ; 8081 port 用於管理 TorchServe 如下圖：

![TorchServe](https://cdn-images-1.medium.com/max/800/1*HyvdEPTr16G-FCxPOlP7qQ.png)

[**舉例：以官方 densenet161 Example 為例**](https://github.com/pytorch/serve)

1. 開始之前必須先安裝 **TorchServe**、**torch-model-archiver，**&#x4E00;個是用來跑 TorchServe 服務的，另一個是用來壓縮 Trained Model、Inference handler 、model Handler 的套件。
2. 使用 **torch-model-archiver** 進行壓縮，會得到[densenet161](http://127.0.0.1:8080/predictions/densenet161).mar檔案，
3. 最後將該檔案註冊進去 TorchServe 就可以使用了。

指令如下：

```bash
pip install torchserve torch-model-archiver       # 安裝必要套件
git clone https://github.com/pytorch/serve.git    # clone example
cd serve/examples/image_classifier/               # 進入image_classifier example
cp index_to_name.json densenet_161/               # 
cd densenet_161/
 # 若無預設輸出資料夾，則創建一個
if [ ! -d "model-store" ]; then                   
    echo "creates new folder: " $export_path
    mkdir $export_path
fi
#下載trained model
wget https://download.pytorch.org/models/densenet161-8d451a50.pth
#壓縮densenet161範例 -> densenet161.mar
torch-model-archiver --model-name densenet161 --version 1.0 --model-file model.py \
                    --serialized-file densenet161-8d451a50.pth --handler image_classifier \
                    --extra-files index_to_name.json --export-path="model-store" 
torchserve --start --model-store model-store/ --models densenet161.mar  # 註冊densenet1611並啟動torchserve
```

可以使用 curl 進行驗證是否抓到 [densenet161](http://127.0.0.1:8080/predictions/densenet161) model，並試著傳一張圖看看是否有成功運行。

```
curl 127.0.0.1:8081/models
curl http://127.0.0.1:8080/predictions/densenet161 -T kitten.jpg
```

![img](https://cdn-images-1.medium.com/max/800/1*eRXtMN-zFSLrcUew9JZe_g.png)

#### **Debug 經驗談**

TorchServe 是一個非常新的部署工具，debug 不知道如何 debug，用起來真的很崩潰。其實一開始壓縮的 .mar 檔，TorchServe 抓到之後會將其解壓縮至 /tmp 中。

![img](https://cdn-images-1.medium.com/max/800/1*uubZ3uHv4SRGqEXQ7WtFKw.png)

![img](https://cdn-images-1.medium.com/max/800/1*9Qy_s7HLw91_yo0SsE2A0Q.png)

因此當想要進行 debug 的時候，**要切到 /tmp 資料夾中使用 python3 指令執行**，如果沒問題，理論上 TorchServe 就能夠成功運作。

**常用Management API 介紹** 在這邊分享一些過去部署TorchServe常用用的一些API，首先在註冊的時候會不知道目前到底有沒有抓到Trained model?這時候可以使用**List models API**

```
curl "http://localhost:8081/models"
```

![](https://cdn-images-1.medium.com/max/800/1*i-I6Fn25WH80ae5exdK5mg.png)

此外就是考慮到效能面的話，或許有需要修改某一個model的Worker Process數量這種需求，也可利用PUT對某一個model修改Worker Process的數量。

```
curl -v -X PUT "http://localhost:8081/models/<modelname>?min_worker=3"
```

就已feature\_extractor這個model為例：首先先看一下model的資訊

![](https://cdn-images-1.medium.com/max/800/1*SaA7LEMJ-guXfwoADZwedg.png)

```
# 進行修改之後
curl -v -X PUT “http://localhost:8081/models/feature_extractor?min_worker=3"
```

![](https://cdn-images-1.medium.com/max/800/1*VFxqC8ig2eHIMqA_M6VyYA.png)

**Inference handler撰寫經驗談**： inference handler文件相當的簡潔，在撰寫handler的時候可參考下面程式碼，**大致上每個區塊的function要杜撰什麼都記載註解上**。此外撰寫完，註冊進去TorchServe後**要能夠在/tmp中找到，同時是可直接執行的狀態**，若不是則需要繼續debug。

**Debug有雷請小心**： 1. 輸出只吃JSON格式 2. batch size 大小(如果batch size 為1，JSON List數量只能是1)

```python
class ModelHandler(BaseHandler):

    def __init__(self):
        self._context = None
        self.initialized = False

    # 用於load trained model 的地方，以及其他設定檔
    def initialize(self, context):
        self._context = context
        self.initialized = True
        #  load the model, refer 'custom handler class' above for details

    # 用於predictions的時候，接收http post request的function: 預設接收key為data的資料
    def preprocess(self, data):
        # Take the input data and make it inference ready
        preprocessed_data = data[0].get("data")
        if preprocessed_data is None:
            preprocessed_data = data[0].get("body")

        return preprocessed_data

    # 用來進行運算的function，最後的結果會交由postprocess()後處理
    def inference(self, model_input):

        # Do some inference call to engine here and return output
        model_output = self.model.forward(model_input)
        return model_output

    # 指的是發送post：inference()之後的結果
    def postprocess(self, inference_output):
        postprocess_output = inference_output
        return postprocess_output

    def handle(self, data, context):

        model_input = self.preprocess(data)
        model_output = self.inference(model_input)
        return self.postprocess(model_output)
```

> 1. 雖然預設輸出是JSON，打request進去的資料格式沒有規定喔，可利用像是protocol buffers這種技術進行傳遞是沒有問題的！
> 2. batchsize 修改方法:
>
>    *curl -v -X PUT "*<http://localhost:8081/models/>*?batch\_size="*
