API RESTful

Ngoài API gRPC, TensorFlow ModelServer còn hỗ trợ API RESTful. Trang này mô tả các điểm cuối API này và ví dụ chi tiết 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 cụ thể của 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 phần nội dung phản hồi có 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 tuân thủ chặt chẽ 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 sẽ được trả về trong phản hồi.

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

Nếu thành công, trả về bản trình bày JSON của protobuf GetModelStatusResponse .

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

API này tuân thủ chặt chẽ API gRPC PredictionService.GetModelMetadata . 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ị 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ề bản trình bày JSON của protobuf GetModelMetadataResponse .

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

API này tuân thủ chặt chẽ các phương pháp ClassifyRegress của API gRPC 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 sẽ đượ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à số JSON (toàn bộ hoặc số thập phân), chuỗi JSON hoặc đối tượng JSON biểu thị 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 trống "" nếu mô hình không có nhãn liên quan đến điểm số). <score> là số thập phân (dấu phẩy động).

Yêu cầu regress trả về một đối tượng JSON trong phần 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à 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 giao thức ClassificationResponseRegressionResponse .

Dự đoán API

API này tuân thủ chặt chẽ API gRPC PredictionService.Predict .

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 sẽ đượ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 các tensor đầ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ả các tensor đầu vào được đặt tên có cùng chiều thứ 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 thể hiệ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 tensor được thể hiện một cách tự nhiên bằng 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 tên đầu vào/giá trị tensor, một mục 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 tensor đầ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 ngầm giả định 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 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 các tensor đầu vào ở định dạng cột.

Sử dụng định dạng này để chỉ định các tensor đầ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 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 khóa thành 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 đơn 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 chiều 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 một danh sách như các phiên bản (được sử dụng trong biểu diễn hàng). Ngoài ra, tất cả các đầu vào được đặt tên đều được chỉ định cùng nhau, trái ngược với việc hủy chúng thành các hàng riêng lẻ được thực hiện theo định dạng hàng được mô tả trước đó. Điều này làm cho cách trình bày trở nê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, chúng tôi sẽ bỏ qua khóa tên và 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 tensor được đặt tên, thay vào đó, chúng tôi sẽ 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 sẽ bỏ qua tên và outputs đồ khóa 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 tensor được đặt tên thì thay vào đó chúng ta sẽ 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 đề cập ở trên.

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

TensorFlow không phân biệt giữa chuỗi nhị phân và chuỗi nhị phân. Tất cả đều thuộc loại DT_STRING . Các tensor đượ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 giá trị nhị phân mã hóa 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ả theo từng loại trong bảng bên dưới. Các loại không được liệt kê dưới đây được ngụ ý là không được hỗ trợ.

Kiểu dữ liệu TF Giá trị JSON Ví dụ về JSON Ghi chú
DT_BOOL đúng sai đúng sai
DT_STRING sợi dây "Chào thế giới!" Nếu DT_STRING đại diện cho byte nhị phân (ví dụ: byte hình ảnh được tuần tự hóa hoặc protobuf), hãy mã hóa chúng 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à 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. Vì vậy, có thể cung cấp một giá trị cho đầu vào dẫn đến mất độ chính xác. Ví dụ: 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ơ bản chuyển đổi âm thầm thành 14357743361435774380 không thể được biểu thị chính xác bằng số dấu phẩy động 32 bit. Thông thường, đầu vào để phân phát phải có cùng mức phân phối như trong quá trình đào tạo, vì vậy, điều này thường không có vấn đề gì vì các chuyển đổi tương tự đã xảy ra tại thời điểm đào tạo. Tuy nhiên, trong trường hợp cần độ chính xác hoàn toàn, hãy đảm bảo sử dụng kiểu 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 xem xét 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ó tính năng đầu vào hoặc giá trị tensor cần ở dạng nhị phân (như byte hình ảnh), bạn phải mã hóa Base64 dữ liệu và gói gọn nó trong một đối tượng JSON có khóa b64 như sau:

{ "b64": <base64 encoded string> }

Bạn có thể chỉ định đối tượng này làm giá trị cho 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.

Yêu cầu phân loại với các tính năng image (dữ liệu nhị phân) và 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ị đặc trưng hoặc tensor 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 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ủ các 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.

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

Ví dụ

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

Khởi động ModelServer với điểm cuối API REST

Tải xuống mô hình half_plus_three từ kho 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 thay thế 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ẽ trông 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. URL hoặc nội dung 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)" }
$