นอกจาก gRPC API แล้ว TensorFlow ModelServer ยังรองรับ RESTful API อีกด้วย หน้านี้อธิบายจุดสิ้นสุด API เหล่านี้และ ตัวอย่าง การใช้งานตั้งแต่ต้นทางถึงปลายทาง
คำขอและการตอบกลับเป็นออบเจ็กต์ JSON องค์ประกอบของวัตถุนี้ขึ้นอยู่กับประเภทคำขอหรือกริยา ดูส่วนเฉพาะของ API ด้านล่างสำหรับรายละเอียด
ในกรณีที่เกิดข้อผิดพลาด API ทั้งหมดจะส่งคืนออบเจ็กต์ JSON ในเนื้อหาการตอบสนองโดยมี error
เป็นคีย์และข้อความแสดงข้อผิดพลาดเป็นค่า:
{
"error": <error message string>
}
API สถานะโมเดล
API นี้เป็นไปตาม ModelService.GetModelStatus
gRPC API อย่างใกล้ชิด มันจะส่งคืนสถานะของโมเดลใน ModelServer
URL
GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]
การใส่ /versions/${VERSION}
หรือ /labels/${LABEL}
เป็นทางเลือก หากละเว้นสถานะสำหรับทุกเวอร์ชันจะถูกส่งกลับในการตอบกลับ
รูปแบบการตอบกลับ
หากสำเร็จ จะส่งคืนการแสดง JSON ของ GetModelStatusResponse
protobuf
API ข้อมูลเมตาของโมเดล
API นี้เป็นไปตาม PredictionService.GetModelMetadata
gRPC API อย่างใกล้ชิด มันจะส่งคืนข้อมูลเมตาของโมเดลใน 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
ของ PredictionService
gRPC API อย่างใกล้ชิด
URL
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:(classify|regress)
การใส่ /versions/${VERSION}
หรือ /labels/${LABEL}
เป็นทางเลือกหรือไม่ก็ได้ หากละเว้นจะใช้เวอร์ชันล่าสุด
แบบคำขอ
เนื้อหาคำขอสำหรับ classify
และ regress
API ต้องเป็นออบเจ็กต์ 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 นี้เป็นไปตาม PredictionService.Predict
gRPC API อย่างใกล้ชิด
URL
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:predict
การใส่ /versions/${VERSION}
หรือ /labels/${LABEL}
เป็นทางเลือกหรือไม่ก็ได้ หากละเว้นจะใช้เวอร์ชันล่าสุด
แบบคำขอ
เนื้อหาคำขอสำหรับ predict
API จะต้องอยู่ในรูปแบบออบเจ็กต์ 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
proto ของ gRPC API และ CMLE Predict API ใช้รูปแบบนี้หากเทนเซอร์อินพุตที่มีชื่อทั้งหมดมี มิติที่ 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
RESTful API รองรับการเข้ารหัส Canonical ใน JSON ทำให้แชร์ข้อมูลระหว่างระบบได้ง่ายขึ้น สำหรับประเภทที่รองรับ การเข้ารหัสจะอธิบายเป็นรายประเภทในตารางด้านล่าง ประเภทที่ไม่อยู่ในรายการด้านล่างแสดงว่าไม่รองรับ
ประเภทข้อมูล TF | ค่า 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_สองเท่า | ตัวเลข | 1.1, -10.0, 0, NaN , Infinity | ค่า JSON จะเป็นตัวเลขหรือค่าโทเค็นพิเศษค่าใดค่าหนึ่ง ได้แก่ NaN , Infinity และ -Infinity ดู ความสอดคล้องกับ JSON สำหรับข้อมูลเพิ่มเติม สัญกรณ์เลขยกกำลังก็เป็นที่ยอมรับเช่นกัน |
ความแม่นยำของจุดลอยตัว
JSON มีประเภทข้อมูลตัวเลขเดียว ดังนั้นจึงเป็นไปได้ที่จะระบุค่าสำหรับอินพุตที่ส่งผลให้สูญเสียความแม่นยำ ตัวอย่างเช่น หากอินพุต x
เป็นประเภทข้อมูล float
และอินพุต {"x": 1435774380}
ถูกส่งไปยังโมเดลที่ทำงานบนฮาร์ดแวร์ตามมาตรฐานจุดลอยตัว IEEE 754 (เช่น Intel หรือ AMD) จากนั้นค่าจะ ถูกแปลงอย่างเงียบ ๆ โดยฮาร์ดแวร์ที่อยู่ด้านล่างเป็น 1435774336
เนื่องจาก 1435774380
ไม่สามารถแสดงเป็นตัวเลขทศนิยม 32 บิตได้อย่างแน่นอน โดยทั่วไป ข้อมูลในการเสิร์ฟควรเป็นการกระจายแบบเดียวกับการฝึก ดังนั้นโดยทั่วไปแล้วจะไม่มีปัญหาเนื่องจาก Conversion เดียวกันนี้เกิดขึ้นในเวลาฝึก อย่างไรก็ตาม ในกรณีที่จำเป็นต้องมีความแม่นยำเต็มที่ ตรวจสอบให้แน่ใจว่าใช้ประเภทข้อมูลพื้นฐานในแบบจำลองของคุณที่สามารถรองรับความแม่นยำที่ต้องการ และ/หรือพิจารณาการตรวจสอบฝั่งไคลเอ็นต์
การเข้ารหัสค่าไบนารี
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)" }
$