RESTful API

В дополнение к API-интерфейсам gRPC TensorFlow ModelServer также поддерживает API-интерфейсы RESTful. На этой странице описаны эти конечные точки API и комплексный пример использования.

Запрос и ответ представляют собой объект JSON. Состав этого объекта зависит от типа запроса или глагола. Подробности см. в разделах, посвященных API, ниже.

В случае ошибки все API вернут объект JSON в теле ответа с error в ​​качестве ключа и сообщением об ошибке в качестве значения:

{
  "error": <error message string>
}

API статуса модели

Этот API тесно связан с API gRPC ModelService.GetModelStatus . Он возвращает статус модели на ModelServer.

URL-адрес

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

Включение /versions/${VERSION} или /labels/${LABEL} не является обязательным. Если опущено, в ответе возвращается статус для всех версий.

Формат ответа

В случае успеха возвращает JSON-представление protobuf GetModelStatusResponse .

API метаданных модели

Этот API тесно связан с API gRPC PredictionService.GetModelMetadata . Он возвращает метаданные модели в ModelServer.

URL-адрес

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

Включение /versions/${VERSION} или /labels/${LABEL} не является обязательным. Если этот параметр опущен, в ответе возвращаются метаданные модели для последней версии.

Формат ответа

В случае успеха возвращает JSON-представление GetModelMetadataResponse protobuf.

API классификации и регрессии

Этот API точно соответствует методам Classify и Regress API PredictionService gRPC.

URL-адрес

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

Включение /versions/${VERSION} или /labels/${LABEL} не является обязательным. Если этот параметр опущен, используется последняя версия.

Формат запроса

Тело запроса для API classify и regress должно представлять собой объект JSON, отформатированный следующим образом:

{
  // 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> — это число JSON (целое или десятичное), строка JSON или объект JSON, представляющий двоичные данные (подробности см. в разделе «Кодирование двоичных значений» ниже). <list> — список таких значений. Этот формат аналогичен прототипам ClassificationRequest и RegressionRequest gRPC. Обе версии принимают список объектов- Example .

Формат ответа

Запрос classify возвращает объект JSON в теле ответа, отформатированный следующим образом:

{
  "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> — это строка (которая может быть пустой строкой "" если у модели нет метки, связанной с оценкой). <score> — десятичное число (с плавающей запятой).

Запрос regress возвращает объект JSON в теле ответа, отформатированный следующим образом:

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

<value> — десятичное число.

Пользователи gRPC API заметят сходство этого формата с прототипами ClassificationResponse и RegressionResponse .

Прогноз API

Этот API очень похож на API PredictionService.Predict gRPC.

URL-адрес

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

Включение /versions/${VERSION} или /labels/${LABEL} не является обязательным. Если этот параметр опущен, используется последняя версия.

Формат запроса

Тело запроса для API predict должно представлять собой объект JSON, отформатированный следующим образом:

{
  // (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>
}

Указание входных тензоров в формате строки.

Этот формат аналогичен прототипу PredictRequest API gRPC и API прогнозирования CMLE . Используйте этот формат, если все именованные входные тензоры имеют одинаковое 0-е измерение . Если это не так, используйте столбчатый формат, описанный ниже.

В формате строки входные данные привязаны к ключу экземпляров в запросе JSON.

Если имеется только один именованный ввод, укажите значение ключа экземпляров , которое будет значением ввода:

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

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

Тензоры естественным образом выражаются во вложенной записи, поскольку нет необходимости вручную сглаживать список.

Для нескольких именованных входных данных ожидается, что каждый элемент будет объектом, содержащим пару входное имя/значение тензора, по одному для каждого именованного входа. В качестве примера ниже приведен запрос с двумя экземплярами, каждый из которых имеет набор из трех именованных входных тензоров:

{
 "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]]
   }
 ]
}

Обратите внимание: неявно предполагается, что каждый именованный вход («тег», «сигнал», «датчик») имеет одно и то же 0-е измерение ( два в приведенном выше примере, поскольку в списке экземпляров есть два объекта). Если вы назвали входные данные, которые имеют другое 0-е измерение, используйте столбчатый формат, описанный ниже.

Указание входных тензоров в формате столбца.

Используйте этот формат, чтобы указать входные тензоры, если отдельные именованные входы не имеют одинакового 0-го измерения или вам нужно более компактное представление. Этот формат аналогичен полю inputs запроса gRPC Predict .

В столбчатом формате входные данные привязаны к ключу входных данных в запросе JSON.

Значением входного ключа может быть либо один входной тензор, либо отображение входного имени в тензоры (перечисленные в их естественной вложенной форме). Каждый ввод может иметь произвольную форму и не обязательно должен иметь одно и то же 0-е измерение (т. е. размер пакета), как того требует формат строки, описанный выше.

Столбчатое представление предыдущего примера выглядит следующим образом:

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

Обратите внимание: входные данные — это объект JSON, а не экземпляры , подобные списку (используемые в представлении строк). Кроме того, все именованные входные данные указываются вместе, а не развертываются в отдельные строки в формате строк, описанном ранее. Это делает представление компактным (но, возможно, менее читаемым).

Формат ответа

Запрос predict возвращает объект JSON в теле ответа.

Запрос в формате строки имеет ответ, отформатированный следующим образом:

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

Если выходные данные модели содержат только один именованный тензор, мы опускаем имя и карты ключей predictions в список скалярных или списочных значений. Если модель выводит несколько именованных тензоров, вместо этого мы выводим список объектов, аналогично запросу в формате строки, упомянутому выше.

Запрос в столбцовом формате имеет ответ, отформатированный следующим образом:

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

Если выходные данные модели содержат только один именованный тензор, мы опускаем имя и outputs карты ключей в список скалярных или списочных значений. Если модель выводит несколько именованных тензоров, вместо этого мы выводим объект. Каждый ключ этого объекта соответствует именованному выходному тензору. Формат аналогичен запросу в формате столбца, упомянутому выше.

Вывод двоичных значений

TensorFlow не различает недвоичные и двоичные строки. Все они относятся к типу DT_STRING . Именованные тензоры, у которых в имени есть суффикс _bytes считаются имеющими двоичные значения. Такие значения кодируются по-другому, как описано в разделе кодирования двоичных значений ниже.

JSON-сопоставление

API-интерфейсы RESTful поддерживают каноническую кодировку JSON, что упрощает обмен данными между системами. Для поддерживаемых типов кодировки описаны по типам в таблице ниже. Подразумевается, что типы, не перечисленные ниже, не поддерживаются.

Тип данных ТФ Значение JSON Пример JSON Примечания
DT_BOOL правда, ложь правда, ложь
DT_STRING нить "Привет, мир!" Если DT_STRING представляет двоичные байты (например, байты сериализованного изображения или protobuf), закодируйте их в Base64. Дополнительную информацию см. в разделе «Кодирование двоичных значений» .
DT_INT8, DT_UINT8, DT_INT16, DT_INT32, DT_UINT32, DT_INT64, DT_UINT64 число 1, -10, 0 Значение JSON будет десятичным числом.
DT_FLOAT, DT_DOUBLE число 1,1, -10,0, 0, NaN , Infinity Значением JSON будет число или одно из специальных значений токена — NaN , Infinity и -Infinity . Дополнительную информацию см. в разделе «Соответствие JSON» . Также допускается экспоненциальное обозначение.

Точность с плавающей запятой

JSON имеет один числовой тип данных. Таким образом, можно указать значение для входных данных, что приведет к потере точности. Например, если входные данные x имеют тип данных float , а входные данные {"x": 1435774380} отправляются в модель, работающую на оборудовании на основе стандарта с плавающей запятой IEEE 754 (например, Intel или AMD), тогда значение будет быть автоматически преобразовано базовым оборудованием в 1435774336 поскольку 1435774380 не может быть точно представлено в 32-битном числе с плавающей запятой. Как правило, входные данные для обслуживания должны распределяться так же, как и для обучения, поэтому в целом это не будет проблемой, поскольку во время обучения происходили те же конверсии. Однако, если необходима полная точность, обязательно используйте в своей модели базовый тип данных, который может обеспечить желаемую точность, и/или рассмотрите возможность проверки на стороне клиента.

Кодирование двоичных значений

JSON использует кодировку UTF-8. Если у вас есть входные функции или значения тензора, которые должны быть двоичными (например, байты изображения), вы должны закодировать данные в Base64 и инкапсулировать их в объект JSON, имеющий ключ b64 , следующим образом:

{ "b64": <base64 encoded string> }

Вы можете указать этот объект как значение для входного объекта или тензора. Тот же формат используется и для кодирования выходного ответа.

Запрос классификации с image (двоичными данными) и caption показан ниже:

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

Соответствие JSON

Многие значения признаков или тензоров представляют собой числа с плавающей запятой. Помимо конечных значений (например, 3,14, 1,0 и т. д.), они могут иметь значения NaN и неконечные ( Infinity и -Infinity ). К сожалению, спецификация JSON ( RFC 7159 ) НЕ распознает эти значения (хотя спецификация JavaScript распознает).

REST API, описанный на этой странице, позволяет объектам JSON запроса/ответа иметь такие значения. Это означает, что допустимы запросы, подобные следующему:

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

Анализатор JSON, соответствующий (строгим) стандартам, отклонит это с ошибкой анализа (из-за того, что токены NaN и Infinity смешаны с фактическими числами). Чтобы правильно обрабатывать запросы/ответы в вашем коде, используйте анализатор JSON, поддерживающий эти токены.

Токены NaN , Infinity , -Infinity распознаются proto3 , модулем Python JSON и языком JavaScript.

Пример

Мы можем использовать игрушечную модель half_plus_three , чтобы увидеть REST API в действии.

Запустите ModelServer с конечной точкой REST API.

Загрузите модель half_plus_three из репозитория git :

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

Мы будем использовать Docker для запуска ModelServer. Если вы хотите установить ModelServer в своей системе, следуйте инструкциям по установке и запустите ModelServer с параметром --rest_api_port чтобы экспортировать конечную точку REST API (это не требуется при использовании 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 ...

Выполнение вызовов REST API для ModelServer.

В другом терминале используйте инструмент curl для выполнения вызовов REST API.

Получите статус модели следующим образом:

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

predict вызов будет выглядеть следующим образом:

$ 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]
}

И вызов regress выглядит следующим образом:

$ 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]
}

Обратите внимание, regress доступен для имени подписи, отличного от умолчанию, и его необходимо указать явно. Неправильный URL-адрес или тело запроса возвращает статус ошибки 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)" }
$