API RESTful

Ngoài API gRPC, TensorFlow ModelServer cũng hỗ trợ API RESTful. Trang này mô tả các điểm cuối API này và ví dụ toàn diện về cách sử dụng.

Yêu cầu và phản hồi là một đối tượng JSON. Thành phần của đối tượng này phụ thuộc vào loại yêu cầu hoặc động từ. Xem các phần dành riêng cho API bên dưới để biết chi tiết.

Trong trường hợp có lỗi, tất cả các API sẽ trả về một đối tượng JSON trong nội dung phản hồi với error là khóa và thông báo lỗi là giá trị:

{
  "error": <error message string>
}

API trạng thái mô hình

API này theo sát API gRPC ModelService.GetModelStatus . Nó trả về trạng thái của một mô hình trong ModelServer.

URL

GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]

Việc bao gồm /versions/${VERSION} hoặc /labels/${LABEL} là tùy chọn. Nếu trạng thái bị bỏ qua cho tất cả các phiên bản được trả lại trong phản hồi.

định dạng phản hồi

Nếu thành công, trả về một biểu diễn JSON của GetModelStatusResponse protobuf.

API siêu dữ liệu mô hình

API này theo sát API PredictionService.GetModelMetadata gRPC. Nó trả về siêu dữ liệu của một mô hình trong ModelServer.

URL

GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]/metadata

Việc bao gồm /versions/${VERSION} hoặc /labels/${LABEL} là tùy chọn. Nếu bỏ qua siêu dữ liệu mô hình cho phiên bản mới nhất sẽ được trả về trong phản hồi.

định dạng phản hồi

Nếu thành công, trả về một biểu diễn JSON của GetModelMetadataResponse protobuf.

API phân loại và hồi quy

API này theo sát các phương pháp ClassifyRegress của API gRPC của PredictionService .

URL

POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:(classify|regress)

Việc bao gồm /versions/${VERSION} hoặc /labels/${LABEL} là tùy chọn. Nếu bỏ qua phiên bản mới nhất được sử dụng.

định dạng yêu cầu

Nội dung yêu cầu cho các API classifyregress phải là một đối tượng JSON được định dạng như sau:

{
  // Optional: serving signature to use.
  // If unspecifed default serving signature is used.
  "signature_name": <string>,

  // Optional: Common context shared by all examples.
  // Features that appear here MUST NOT appear in examples (below).
  "context": {
    "<feature_name3>": <value>|<list>
    "<feature_name4>": <value>|<list>
  },

  // List of Example objects
  "examples": [
    {
      // Example 1
      "<feature_name1>": <value>|<list>,
      "<feature_name2>": <value>|<list>,
      ...
    },
    {
      // Example 2
      "<feature_name1>": <value>|<list>,
      "<feature_name2>": <value>|<list>,
      ...
    }
    ...
  ]
}

<value> là một số JSON (toàn bộ hoặc thập phân), chuỗi JSON hoặc đối tượng JSON đại diện cho dữ liệu nhị phân (xem phần Mã hóa giá trị nhị phân bên dưới để biết chi tiết). <list> là danh sách các giá trị như vậy. Định dạng này tương tự như các proto ClassificationRequestRegressionRequest của gRPC. Cả hai phiên bản đều chấp nhận danh sách các đối tượng Example .

định dạng phản hồi

Yêu cầu classify trả về một đối tượng JSON trong nội dung phản hồi, được định dạng như sau:

{
  "result": [
    // List of class label/score pairs for first Example (in request)
    [ [<label1>, <score1>], [<label2>, <score2>], ... ],

    // List of class label/score pairs for next Example (in request)
    [ [<label1>, <score1>], [<label2>, <score2>], ... ],
    ...
  ]
}

<label> là một chuỗi (có thể là chuỗi rỗng "" nếu mô hình không có nhãn liên quan đến điểm số). <score> là một số thập phân (dấu phẩy động).

Yêu cầu regress trả về một đối tượng JSON trong nội dung phản hồi, được định dạng như sau:

{
  // One regression value for each example in the request in the same order.
  "result": [ <value1>, <value2>, <value3>, ...]
}

<value> là một số thập phân.

Người dùng API gRPC sẽ nhận thấy sự giống nhau của định dạng này với các nguyên mẫu ClassificationResponseRegressionResponse .

Dự đoán API

API này theo sát API PredictionService.Predict gRPC.

URL

POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:predict

Việc bao gồm /versions/${VERSION} hoặc /labels/${LABEL} là tùy chọn. Nếu bỏ qua phiên bản mới nhất được sử dụng.

định dạng yêu cầu

Nội dung yêu cầu cho API predict phải là đối tượng JSON được định dạng như sau:

{
  // (Optional) Serving signature to use.
  // If unspecifed default serving signature is used.
  "signature_name": <string>,

  // Input Tensors in row ("instances") or columnar ("inputs") format.
  // A request can have either of them but NOT both.
  "instances": <value>|<(nested)list>|<list-of-objects>
  "inputs": <value>|<(nested)list>|<object>
}

Chỉ định tenxơ đầu vào ở định dạng hàng.

Định dạng này tương tự như nguyên mẫu PredictRequest của API gRPC và API dự đoán CMLE . Sử dụng định dạng này nếu tất cả tenxơ đầu vào được đặt tên có cùng thứ nguyên 0 . Nếu không, hãy sử dụng định dạng cột được mô tả sau bên dưới.

Ở định dạng hàng, đầu vào được khóa thành khóa phiên bản trong yêu cầu JSON.

Khi chỉ có một đầu vào được đặt tên, hãy chỉ định giá trị của khóa phiên bản là giá trị của đầu vào:

{
  // List of 3 scalar tensors.
  "instances": [ "foo", "bar", "baz" ]
}

{
  // List of 2 tensors each of [1, 2] shape
  "instances": [ [[1, 2]], [[3, 4]] ]
}

Các thang đo được thể hiện một cách tự nhiên trong ký hiệu lồng nhau vì không cần phải làm phẳng danh sách theo cách thủ công.

Đối với nhiều đầu vào được đặt tên, mỗi mục được mong đợi là một đối tượng chứa cặp giá trị tên/tenxơ đầu vào, một cho mỗi đầu vào được đặt tên. Ví dụ, sau đây là một yêu cầu có hai trường hợp, mỗi trường hợp có một bộ ba tenxơ đầu vào được đặt tên:

{
 "instances": [
   {
     "tag": "foo",
     "signal": [1, 2, 3, 4, 5],
     "sensor": [[1, 2], [3, 4]]
   },
   {
     "tag": "bar",
     "signal": [3, 4, 1, 2, 5]],
     "sensor": [[4, 5], [6, 8]]
   }
 ]
}

Lưu ý, mỗi đầu vào được đặt tên ("thẻ", "tín hiệu", "cảm biến") được giả định hoàn toàn có cùng thứ nguyên 0 ( hai trong ví dụ trên, vì có hai đối tượng trong danh sách phiên bản ). Nếu bạn đã đặt tên cho các đầu vào có thứ nguyên 0 khác nhau, hãy sử dụng định dạng cột được mô tả bên dưới.

Chỉ định tenxơ đầu vào ở định dạng cột.

Sử dụng định dạng này để chỉ định các tenxơ đầu vào của bạn, nếu các đầu vào được đặt tên riêng lẻ không có cùng thứ nguyên 0 hoặc bạn muốn một biểu diễn nhỏ gọn hơn. Định dạng này tương tự như trường inputs của yêu cầu Predict gRPC.

Ở định dạng cột, đầu vào được nhập vào khóa đầu vào trong yêu cầu JSON.

Giá trị cho khóa đầu vào có thể là một tenxơ đầu vào duy nhất hoặc ánh xạ tên đầu vào tới các tenxơ (được liệt kê ở dạng lồng nhau tự nhiên của chúng). Mỗi đầu vào có thể có hình dạng tùy ý và không cần chia sẻ/cùng thứ nguyên thứ 0 (còn gọi là kích thước lô) theo yêu cầu của định dạng hàng được mô tả ở trên.

Biểu diễn cột của ví dụ trước như sau:

{
 "inputs": {
   "tag": ["foo", "bar"],
   "signal": [[1, 2, 3, 4, 5], [3, 4, 1, 2, 5]],
   "sensor": [[[1, 2], [3, 4]], [[4, 5], [6, 8]]]
 }
}

Lưu ý, đầu vào là một đối tượng JSON chứ không phải danh sách như các thể hiện (được sử dụng trong biểu diễn hàng). Ngoài ra, tất cả các đầu vào đã đặt tên được chỉ định cùng nhau, trái ngược với việc hủy kiểm soát chúng thành các hàng riêng lẻ được thực hiện ở định dạng hàng được mô tả trước đó. Điều này làm cho biểu diễn nhỏ gọn (nhưng có thể khó đọc hơn).

định dạng phản hồi

Yêu cầu predict trả về một đối tượng JSON trong nội dung phản hồi.

Một yêu cầu ở định dạng hàng có phản hồi được định dạng như sau:

{
  "predictions": <value>|<(nested)list>|<list-of-objects>
}

Nếu đầu ra của mô hình chỉ chứa một tenxơ được đặt tên, thì chúng tôi sẽ bỏ qua tên và khóa predictions ánh xạ tới danh sách các giá trị vô hướng hoặc danh sách. Nếu mô hình xuất ra nhiều tenxơ được đặt tên, thay vào đó, chúng tôi xuất ra một danh sách các đối tượng, tương tự như yêu cầu ở định dạng hàng được đề cập ở trên.

Một yêu cầu ở định dạng cột có phản hồi được định dạng như sau:

{
  "outputs": <value>|<(nested)list>|<object>
}

Nếu đầu ra của mô hình chỉ chứa một tenxơ được đặt tên, chúng tôi bỏ qua tên và outputs các bản đồ chính thành danh sách các giá trị vô hướng hoặc danh sách. Nếu mô hình xuất ra nhiều tenxơ được đặt tên, thay vào đó, chúng tôi xuất ra một đối tượng. Mỗi khóa của đối tượng này tương ứng với một tenxơ đầu ra được đặt tên. Định dạng tương tự như yêu cầu ở định dạng cột đã đề cập ở trên.

Đầu ra của các giá trị nhị phân

TensorFlow không phân biệt giữa chuỗi không nhị phân và nhị phân. Tất cả đều là loại DT_STRING . Các tenxơ được đặt tên có _bytes làm hậu tố trong tên của chúng được coi là có giá trị nhị phân. Các giá trị như vậy được mã hóa khác nhau như được mô tả trong phần mã hóa giá trị nhị phân bên dưới.

Ánh xạ JSON

API RESTful hỗ trợ mã hóa chuẩn trong JSON, giúp chia sẻ dữ liệu giữa các hệ thống dễ dàng hơn. Đối với các loại được hỗ trợ, mã hóa được mô tả trên cơ sở từng loại trong bảng bên dưới. Các loại không được liệt kê bên dưới được ngụ ý là không được hỗ trợ.

Loại dữ liệu TF Giá trị JSON ví dụ JSON ghi chú
DT_BOOL đúng sai đúng sai
DT_STRING sợi dây "Chào thế giới!" Nếu DT_STRING biểu thị các byte nhị phân (ví dụ: byte hình ảnh được tuần tự hóa hoặc protobuf), hãy mã hóa các byte này trong Base64. Xem Mã hóa giá trị nhị phân để biết thêm thông tin.
DT_INT8, DT_UINT8, DT_INT16, DT_INT32, DT_UINT32, DT_INT64, DT_UINT64 con số 1, -10, 0 Giá trị JSON sẽ là một số thập phân.
DT_FLOAT, DT_DOUBLE con số 1.1, -10.0, 0, NaN , Infinity Giá trị JSON sẽ là một số hoặc một trong các giá trị mã thông báo đặc biệt - NaN , Infinity-Infinity . Xem tuân thủ JSON để biết thêm thông tin. Ký hiệu số mũ cũng được chấp nhận.

Độ chính xác của dấu phẩy động

JSON có một kiểu dữ liệu số duy nhất. Do đó, có thể cung cấp một giá trị cho đầu vào dẫn đến mất độ chính xác. Chẳng hạn, nếu đầu vào x là kiểu dữ liệu float và đầu vào {"x": 1435774380} được gửi đến mô hình chạy trên phần cứng dựa trên tiêu chuẩn dấu phẩy động IEEE 754 (ví dụ: Intel hoặc AMD), thì giá trị sẽ được phần cứng cơ sở âm thầm chuyển đổi thành 14357743361435774380 không thể được biểu diễn chính xác bằng số dấu phẩy động 32 bit. Thông thường, các yếu tố đầu vào để phân phối phải giống như phân phối khi đào tạo, do đó, điều này thường không có vấn đề gì vì các chuyển đổi tương tự đã xảy ra trong thời gian đào tạo. Tuy nhiên, trong trường hợp cần độ chính xác đầy đủ, hãy đảm bảo sử dụng loại dữ liệu cơ bản trong mô hình của bạn có thể xử lý độ chính xác mong muốn và/hoặc cân nhắc việc kiểm tra phía máy khách.

Mã hóa giá trị nhị phân

JSON sử dụng mã hóa UTF-8. Nếu bạn có các giá trị đối tượng hoặc tenxơ đầu vào cần ở dạng nhị phân (như byte hình ảnh), bạn phải mã hóa dữ liệu Base64 và đóng gói nó trong một đối tượng JSON có khóa là b64 như sau:

{ "b64": <base64 encoded string> }

Bạn có thể chỉ định đối tượng này làm giá trị cho một tính năng đầu vào hoặc tensor. Định dạng tương tự cũng được sử dụng để mã hóa phản hồi đầu ra.

Một yêu cầu phân loại với image (dữ liệu nhị phân) và các tính năng caption được hiển thị bên dưới:

{
  "signature_name": "classify_objects",
  "examples": [
    {
      "image": { "b64": "aW1hZ2UgYnl0ZXM=" },
      "caption": "seaside"
    },
    {
      "image": { "b64": "YXdlc29tZSBpbWFnZSBieXRlcw==" },
      "caption": "mountains"
    }
  ]
}

tuân thủ JSON

Nhiều giá trị tính năng hoặc tenxơ là số dấu phẩy động. Ngoài các giá trị hữu hạn (ví dụ: 3.14, 1.0, v.v.), chúng có thể có các giá trị NaN và không hữu hạn ( Infinity-Infinity ). Thật không may, đặc tả JSON ( RFC 7159 ) KHÔNG nhận ra các giá trị này (mặc dù đặc tả JavaScript thì có).

API REST được mô tả trên trang này cho phép các đối tượng JSON yêu cầu/phản hồi có các giá trị như vậy. Điều này ngụ ý rằng các yêu cầu như sau là hợp lệ:

{
  "example": [
    {
      "sensor_readings": [ 1.0, -3.14, Nan, Infinity ]
    }
  ]
}

Trình phân tích cú pháp JSON tuân thủ tiêu chuẩn (nghiêm ngặt) sẽ từ chối điều này với lỗi phân tích cú pháp (do mã thông báo NaNInfinity trộn lẫn với số thực). Để xử lý chính xác các yêu cầu/phản hồi trong mã của bạn, hãy sử dụng trình phân tích cú pháp JSON hỗ trợ các mã thông báo này.

Mã thông báo NaN , Infinity , -Infinity được nhận dạng bởi proto3 , mô-đun JSON của Python và ngôn ngữ JavaScript.

Ví dụ

Chúng ta có thể sử dụng mô hình đồ chơi half_plus_three để xem hoạt động của các API REST.

Bắt đầu ModelServer với điểm cuối API REST

Tải xuống mô hình half_plus_three từ kho lưu trữ git :

$ mkdir -p /tmp/tfserving
$ cd /tmp/tfserving
$ git clone --depth=1 https://github.com/tensorflow/serving

Chúng tôi sẽ sử dụng Docker để chạy ModelServer. Nếu bạn muốn cài đặt ModelServer nguyên bản trên hệ thống của mình, hãy làm theo hướng dẫn thiết lập để cài đặt và khởi động ModelServer với tùy chọn --rest_api_port để xuất điểm cuối API REST (điều này không cần thiết khi sử dụng Docker).

$ cd /tmp/tfserving
$ docker pull tensorflow/serving:latest
$ docker run --rm -p 8501:8501 \
    --mount type=bind,source=$(pwd),target=$(pwd) \
    -e MODEL_BASE_PATH=$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata \
    -e MODEL_NAME=saved_model_half_plus_three -t tensorflow/serving:latest
...
.... Exporting HTTP/REST API at:localhost:8501 ...

Thực hiện lệnh gọi API REST tới ModelServer

Trong một thiết bị đầu cuối khác, hãy sử dụng công cụ curl để thực hiện lệnh gọi API REST.

Nhận trạng thái của mô hình như sau:

$ curl http://localhost:8501/v1/models/saved_model_half_plus_three
{
 "model_version_status": [
  {
   "version": "123",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}

Một cuộc gọi predict sẽ như sau:

$ curl -d '{"instances": [1.0,2.0,5.0]}' -X POST http://localhost:8501/v1/models/saved_model_half_plus_three:predict
{
    "predictions": [3.5, 4.0, 5.5]
}

Và một cuộc gọi regress trông như sau:

$ curl -d '{"signature_name": "tensorflow/serving/regress", "examples": [{"x": 1.0}, {"x": 2.0}]}' \
  -X POST http://localhost:8501/v1/models/saved_model_half_plus_three:regress
{
    "results": [3.5, 4.0]
}

Lưu ý, regress có sẵn trên tên chữ ký không mặc định và phải được chỉ định rõ ràng. Nội dung hoặc URL yêu cầu không chính xác sẽ trả về trạng thái lỗi HTTP.

$ curl -i -d '{"instances": [1.0,5.0]}' -X POST http://localhost:8501/v1/models/half:predict
HTTP/1.1 404 Not Found
Content-Type: application/json
Date: Wed, 06 Jun 2018 23:20:12 GMT
Content-Length: 65

{ "error": "Servable not found for request: Latest(half)" }
$