Tham gia cộng đồng SIG TFX-Addons và giúp cải thiện TFX tốt hơn nữa!

API RESTful

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

Yêu cầu và phản hồi là một đối tượng JSON. Thành phần của tân ngữ này phụ thuộc vào kiểu 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 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 ModelService.GetModelStatus 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}]

Bao gồm /versions/${VERSION} hoặc /labels/${LABEL} là tùy chọn. Nếu bỏ qua trạng thái 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 đại diện JSON của GetModelStatusResponse protobuf.

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

API này theo sát 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

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ề một đại diện JSON của GetModelMetadataResponse protobuf.

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

API này tuân theo 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)

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.

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

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 (nguyên 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à một danh sách các giá trị như vậy. Định dạng này tương tự như các giao thức ClassificationRequest và yêu cầu RegressionRequest 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

Một yêu cầu classify trả về một đối tượng JSON trong phần thân 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à một chuỗi rỗng "" nếu mô hình không có nhãn được liên kết với điểm). <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 phần thân 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 sử dụng gRPC API sẽ nhận thấy sự giống nhau của định dạng này với ClassificationResponseRegressionResponse protos.

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

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.

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

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 bộ căng đầu vào ở định dạng hàng.

Định dạng này cũng tương tự như PredictRequest proto của gRPC API và CMLE dự đoán API . Sử dụng định dạng này nếu tất cả cá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.

Trong định dạng hàng, đầu vào được vừa khít với chính trường trong yêu cầu JSON.

Khi chỉ có một tên đầu vào, xác định giá trị của trường chính 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 hàng chục đượ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 phiên bản, mỗi phiên bản có một bộ ba bộ căng đầ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 tên đầu vào ( "tag", "tín hiệu", "cảm biến") được ngầm giả định có cùng 0 thứ kích thước (hai trong ví dụ trên, như có hai đối tượng trong danh sách các trường hợp). 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 bộ căng đầu vào ở định dạng cột.

Sử dụng định dạng này để chỉ định bộ căng đầ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 tensor đầu vào duy nhất hoặc một bản đồ tên đầu vào tới tensor (đượ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 0 (còn gọi là kích thước lô) như 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 và không phải là 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 được chỉ định cùng nhau, trái ngược với việc cuộn 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ể ít đọ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 tensor được đặt tên, chúng tôi bỏ qua bản đồ khóa tên và predictions thành một 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 xuất ra 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định dạng phản hồi như sau:

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

Nếu đầu ra của mô hình chỉ chứa một tensor được đặt tên, chúng tôi bỏ qua tên và outputs các bản đồ 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, 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ập ở trên.

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

TensorFlow không phân biệt giữa các 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ị này được mã hóa khác nhau như được mô tả trong phần mã hóa các giá trị nhị phân bên dưới.

Ánh xạ JSON

Các 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ợ, các 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ê dưới đây được ngụ ý là không được hỗ trợ.

Loại dữ liệu TF Giá trị JSON Ví dụ về JSON Ghi chú
DT_BOOL đúng sai đúng sai
DT_STRING chuỗi "Chào thế giới!" Nếu DT_STRING đại diện cho các byte nhị phân (ví dụ: các 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 các 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 sự 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 chấm độ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. 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 chấm động IEEE 754 (ví dụ: Intel hoặc AMD), thì giá trị sẽ được chuyển đổi âm thầm bởi phần cứng underyling 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, các yếu tố đầu vào để phân phát phải cùng phân phối với đào tạo, do đó, điều này thường sẽ không có vấn đề 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 đầ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 kiểm tra phía máy khách.

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

JSON sử dụng mã hóa UTF-8. Nếu bạn có giá trị tính năng đầu vào hoặc tensor cần phải là 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ó b64 là khóa 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ị feature 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 ). Rất tiếc, đặ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ư yêu cầu 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 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.

NaN 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.

Thí dụ

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

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

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 thay thế và khởi động ModelServer với tùy chọn --rest_api_port để xuất điểm cuối REST API (đ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 REST API 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 các 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 một tên chữ ký không phải 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 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)" }
$