การสร้างเซิร์ฟเวอร์โมเดล TensorFlow มาตรฐาน

บทช่วยสอนนี้แสดงให้คุณเห็นถึงวิธีใช้ส่วนประกอบ TensorFlow Serving เพื่อสร้าง TensorFlow ModelServer มาตรฐานที่ค้นพบและให้บริการเวอร์ชันใหม่ของโมเดล TensorFlow ที่ผ่านการฝึกอบรมแบบไดนามิก หากคุณเพียงต้องการใช้เซิร์ฟเวอร์มาตรฐานเพื่อรองรับโมเดลของคุณ โปรดดู บทช่วยสอนพื้นฐานเกี่ยวกับการให้บริการ TensorFlow

บทช่วยสอนนี้ใช้โมเดล Softmax Regression แบบง่ายที่แนะนำในบทช่วยสอน TensorFlow สำหรับการจำแนกประเภทรูปภาพที่เขียนด้วยลายมือ (ข้อมูล MNIST) หากคุณไม่ทราบว่า TensorFlow หรือ MNIST คืออะไร โปรดดูบทช่วยสอน MNIST สำหรับผู้เริ่มต้น ML

รหัสสำหรับบทช่วยสอนนี้ประกอบด้วยสองส่วน:

  • ไฟล์ Python mnist_saved_model.py ที่ฝึกฝนและส่งออกโมเดลหลายเวอร์ชัน

  • ไฟล์ C++ main.cc ซึ่งเป็น TensorFlow ModelServer มาตรฐานที่ค้นพบโมเดลที่ส่งออกใหม่ๆ และเรียกใช้บริการ gRPC เพื่อให้บริการโมเดลเหล่านั้น

บทช่วยสอนนี้ทำตามขั้นตอนงานต่อไปนี้:

  1. ฝึกอบรมและส่งออกโมเดล TensorFlow
  2. จัดการการกำหนดเวอร์ชันโมเดลด้วย TensorFlow Serving ServerCore
  3. กำหนดค่าแบตช์โดยใช้ SavedModelBundleSourceAdapterConfig
  4. ให้บริการคำขอด้วย TensorFlow Serving ServerCore
  5. เรียกใช้และทดสอบบริการ

ก่อนเริ่มต้น ให้ติดตั้ง Docker ก่อน

ฝึกอบรมและส่งออกโมเดล TensorFlow

ขั้นแรก หากคุณยังไม่ได้ดำเนินการ ให้โคลนพื้นที่เก็บข้อมูลนี้ไปยังเครื่องภายในของคุณ:

git clone https://github.com/tensorflow/serving.git
cd serving

ล้างไดเรกทอรีการส่งออกหากมีอยู่แล้ว:

rm -rf /tmp/models

ฝึกฝน (ด้วยการวนซ้ำ 100 ครั้ง) และส่งออกโมเดลเวอร์ชันแรก:

tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
  --training_iteration=100 --model_version=1 /tmp/mnist

ฝึกฝน (ด้วยการวนซ้ำ 2,000 ครั้ง) และส่งออกโมเดลเวอร์ชันที่สอง:

tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
  --training_iteration=2000 --model_version=2 /tmp/mnist

ดังที่คุณเห็นใน mnist_saved_model.py การฝึกอบรมและการส่งออกจะดำเนินการในลักษณะเดียวกับใน บทช่วยสอนพื้นฐาน TensorFlow Serving เพื่อวัตถุประสงค์ในการสาธิต คุณจงใจลดจำนวนการวนซ้ำการฝึกสำหรับการรันครั้งแรกและส่งออกเป็น v1 ขณะเดียวกันก็ฝึกตามปกติสำหรับการรันครั้งที่สองและส่งออกเป็น v2 ไปยังไดเร็กทอรีหลักเดียวกัน - ตามที่เราคาดหวังไว้ว่าอย่างหลังจะบรรลุผลสำเร็จ ความแม่นยำในการจำแนกประเภทดีขึ้นเนื่องจากการฝึกอบรมที่เข้มข้นมากขึ้น คุณควรเห็นข้อมูลการฝึกสำหรับการฝึกแต่ละครั้งในไดเร็กทอรี /tmp/mnist ของคุณ:

$ ls /tmp/mnist
1  2

เซิร์ฟเวอร์คอร์

ทีนี้ลองจินตนาการว่าโมเดล v1 และ v2 ถูกสร้างขึ้นแบบไดนามิก ณ รันไทม์ ขณะที่อัลกอริธึมใหม่กำลังถูกทดลองอยู่ หรือในขณะที่โมเดลได้รับการฝึกฝนด้วยชุดข้อมูลใหม่ ในสภาพแวดล้อมการใช้งานจริง คุณอาจต้องการสร้างเซิร์ฟเวอร์ที่สามารถรองรับการเปิดตัวแบบค่อยเป็นค่อยไป ซึ่งสามารถค้นพบ โหลด ทดลอง ตรวจสอบ หรือเปลี่ยนกลับในขณะที่ให้บริการเวอร์ชัน 1 ได้ หรืออีกทางหนึ่ง คุณอาจต้องการรื้อ v1 ก่อนที่จะเรียก v2 ขึ้นมา TensorFlow Serving รองรับทั้งสองตัวเลือก ในขณะที่ตัวเลือกหนึ่งเหมาะสำหรับการรักษาความพร้อมใช้งานระหว่างการเปลี่ยนแปลง ส่วนอีกตัวเลือกหนึ่งเหมาะสำหรับการลดการใช้ทรัพยากร (เช่น RAM)

TensorFlow Serving Manager ทำอย่างนั้นจริงๆ โดยจะจัดการวงจรการใช้งานเต็มรูปแบบของโมเดล TensorFlow รวมถึงการโหลด การให้บริการ และการยกเลิกการโหลด รวมถึงการเปลี่ยนเวอร์ชัน ในบทช่วยสอนนี้ คุณจะสร้างเซิร์ฟเวอร์ของคุณบน TensorFlow Serving ServerCore ซึ่งล้อมรอบ AspiredVersionsManager ภายใน

int main(int argc, char** argv) {
  ...

  ServerCore::Options options;
  options.model_server_config = model_server_config;
  options.servable_state_monitor_creator = &CreateServableStateMonitor;
  options.custom_model_config_loader = &LoadCustomModelConfig;

  ::google::protobuf::Any source_adapter_config;
  SavedModelBundleSourceAdapterConfig
      saved_model_bundle_source_adapter_config;
  source_adapter_config.PackFrom(saved_model_bundle_source_adapter_config);
  (*(*options.platform_config_map.mutable_platform_configs())
      [kTensorFlowModelPlatform].mutable_source_adapter_config()) =
      source_adapter_config;

  std::unique_ptr<ServerCore> core;
  TF_CHECK_OK(ServerCore::Create(options, &core));
  RunServer(port, std::move(core));

  return 0;
}

ServerCore::Create() รับพารามิเตอร์ ServerCore::Options ต่อไปนี้คือตัวเลือกบางส่วนที่ใช้กันทั่วไป:

  • ModelServerConfig ที่ระบุโมเดลที่จะโหลด โมเดลจะถูกประกาศผ่าน model_config_list ซึ่งประกาศรายการโมเดลแบบคงที่ หรือผ่าน custom_model_config ซึ่งกำหนดวิธีที่กำหนดเองในการประกาศรายการโมเดลที่อาจได้รับการอัปเดตขณะรันไทม์
  • PlatformConfigMap ที่จับคู่จากชื่อของแพลตฟอร์ม (เช่น tensorflow ) ไปยัง PlatformConfig ซึ่งใช้ในการสร้าง SourceAdapter SourceAdapter ปรับ StoragePath (เส้นทางที่ค้นพบเวอร์ชันของโมเดล) ให้เป็น Model Loader (โหลดเวอร์ชันของโมเดลจากเส้นทางการจัดเก็บข้อมูลและจัดเตรียมอินเทอร์เฟซการเปลี่ยนสถานะให้กับ Manager ) หาก PlatformConfig มี SavedModelBundleSourceAdapterConfig จะมีการสร้าง SavedModelBundleSourceAdapter ซึ่งเราจะอธิบายในภายหลัง

SavedModelBundle เป็นองค์ประกอบสำคัญของ TensorFlow Serving โดยแสดงถึงโมเดล TensorFlow ที่โหลดจากเส้นทางที่กำหนด และจัดเตรียมอินเทอร์เฟซ Session::Run เดียวกันกับ TensorFlow เพื่อเรียกใช้การอนุมาน SavedModelBundleSourceAdapter ปรับพาธการจัดเก็บข้อมูลเป็น Loader<SavedModelBundle> เพื่อให้ Manager สามารถจัดการอายุการใช้งานโมเดลได้ โปรดทราบว่า SavedModelBundle เป็นผู้สืบทอดของ SessionBundle ที่เลิกใช้แล้ว ผู้ใช้ได้รับการสนับสนุนให้ใช้ SavedModelBundle เนื่องจากการรองรับ SessionBundle จะถูกลบออกในไม่ช้า

ด้วยสิ่งเหล่านี้ ServerCore จะทำสิ่งต่อไปนี้เป็นการภายใน:

  • สร้างอินสแตนซ์ FileSystemStoragePathSource ที่ตรวจสอบเส้นทางการส่งออกโมเดลที่ประกาศใน model_config_list
  • สร้างอินสแตนซ์ SourceAdapter โดยใช้ PlatformConfigMap ด้วยแพลตฟอร์มโมเดลที่ประกาศใน model_config_list และเชื่อมต่อ FileSystemStoragePathSource กับมัน ด้วยวิธีนี้ เมื่อใดก็ตามที่ค้นพบเวอร์ชันโมเดลใหม่ภายใต้พาธการส่งออก SavedModelBundleSourceAdapter จะปรับให้เป็น Loader<SavedModelBundle>
  • สร้างอินสแตนซ์การใช้งาน Manager เฉพาะที่เรียกว่า AspiredVersionsManager ซึ่งจัดการอินสแตนซ์ Loader ทั้งหมดที่สร้างโดย SavedModelBundleSourceAdapter ServerCore ส่งออกอินเทอร์เฟซ Manager โดยการมอบหมายการโทรไปยัง AspiredVersionsManager

เมื่อใดก็ตามที่มีเวอร์ชันใหม่ AspiredVersionsManager นี้จะโหลดเวอร์ชันใหม่และภายใต้ลักษณะการทำงานเริ่มต้นจะยกเลิกการโหลดเวอร์ชันเก่า หากคุณต้องการเริ่มปรับแต่ง ขอแนะนำให้ทำความเข้าใจส่วนประกอบที่ส่วนประกอบสร้างขึ้นภายใน และวิธีการกำหนดค่าส่วนประกอบเหล่านั้น

เป็นที่น่าสังเกตว่า TensorFlow Serving ได้รับการออกแบบตั้งแต่เริ่มต้นเพื่อให้มีความยืดหยุ่นและขยายได้มาก คุณสามารถสร้างปลั๊กอินต่างๆ เพื่อปรับแต่งพฤติกรรมของระบบได้ ในขณะที่ใช้ประโยชน์จากส่วนประกอบหลักทั่วไป เช่น ServerCore และ AspiredVersionsManager ตัวอย่างเช่น คุณสามารถสร้างปลั๊กอินแหล่งข้อมูลที่ตรวจสอบพื้นที่เก็บข้อมูลบนคลาวด์แทนพื้นที่เก็บข้อมูลในเครื่อง หรือคุณสามารถสร้างปลั๊กอินนโยบายเวอร์ชันที่เปลี่ยนเวอร์ชันในลักษณะอื่น ที่จริงแล้ว คุณสามารถสร้างปลั๊กอินโมเดลแบบกำหนดเองที่ให้บริการได้ โมเดลที่ไม่ใช่ TensorFlow หัวข้อเหล่านี้อยู่นอกขอบเขตสำหรับบทช่วยสอนนี้ อย่างไรก็ตาม คุณสามารถอ้างอิง แหล่งที่มาที่กำหนดเอง และบทช่วยสอน ที่ให้บริการแบบกำหนดเองได้ สำหรับข้อมูลเพิ่มเติม

การผสม

คุณสมบัติเซิร์ฟเวอร์ทั่วไปอีกประการหนึ่งที่เราต้องการในสภาพแวดล้อมการใช้งานจริงคือการแบทช์ ตัวเร่งฮาร์ดแวร์สมัยใหม่ (GPU ฯลฯ) ที่ใช้ทำการอนุมานของแมชชีนเลิร์นนิงมักจะได้รับประสิทธิภาพในการคำนวณที่ดีที่สุดเมื่อมีการเรียกใช้คำขอการอนุมานเป็นกลุ่มจำนวนมาก

สามารถเปิดใช้งานการแบตช์ได้โดยระบุ SessionBundleConfig ที่เหมาะสมเมื่อสร้าง SavedModelBundleSourceAdapter ในกรณีนี้ เราตั้ง BatchingParameters ด้วยค่าเริ่มต้นที่ค่อนข้างมาก การปรับแต่งเป็นชุดอย่างละเอียดได้โดยการตั้งค่าการหมดเวลาที่กำหนดเอง, ขนาดแบตช์ ฯลฯ สำหรับรายละเอียด โปรดดูที่ BatchingParameters

SessionBundleConfig session_bundle_config;
// Batching config
if (enable_batching) {
  BatchingParameters* batching_parameters =
      session_bundle_config.mutable_batching_parameters();
  batching_parameters->mutable_thread_pool_name()->set_value(
      "model_server_batch_threads");
}
*saved_model_bundle_source_adapter_config.mutable_legacy_config() =
    session_bundle_config;

เมื่อเข้าถึงชุดเต็มแล้ว คำขอการอนุมานจะถูกรวมภายในเป็นคำขอขนาดใหญ่เดียว (เทนเซอร์) และเรียกใช้ tensorflow::Session::Run() (ซึ่งเป็นที่มาของประสิทธิภาพจริงที่ได้รับจาก GPU)

ให้บริการร่วมกับผู้จัดการ

ตามที่กล่าวไว้ข้างต้น TensorFlow Serving Manager ได้รับการออกแบบให้เป็นส่วนประกอบทั่วไปที่สามารถจัดการการโหลด การให้บริการ การยกเลิกการโหลด และการเปลี่ยนเวอร์ชันของโมเดลที่สร้างโดยระบบการเรียนรู้ของเครื่องจักรตามอำเภอใจ API สร้างขึ้นตามแนวคิดหลักต่อไปนี้:

  • เสิร์ฟได้ : เสิร์ฟได้เป็นวัตถุทึบแสงใด ๆ ที่สามารถใช้เพื่อให้บริการตามคำขอของไคลเอ็นต์ ขนาดและรายละเอียดของการให้บริการนั้นมีความยืดหยุ่น โดยที่การให้บริการเดี่ยวอาจรวมทุกอย่างตั้งแต่ส่วนเดียวของตารางการค้นหาไปจนถึงโมเดลที่เรียนรู้ด้วยเครื่องเดียวไปจนถึงโมเดลทูเพิล การให้บริการสามารถเป็นประเภทและอินเทอร์เฟซใดก็ได้

  • เวอร์ชันที่ให้บริการได้ : เวอร์ชันที่ให้บริการได้ถูกกำหนดเวอร์ชันไว้แล้ว และ TensorFlow Serving Manager สามารถจัดการเวอร์ชันที่ให้บริการได้ตั้งแต่หนึ่งเวอร์ชันขึ้นไป การกำหนดเวอร์ชันทำให้สามารถโหลดการให้บริการได้มากกว่าหนึ่งเวอร์ชันพร้อมกัน โดยรองรับการเปิดตัวและการทดลองแบบค่อยเป็นค่อยไป

  • สตรีมที่ให้บริการได้ : สตรีมที่ให้บริการได้คือลำดับของเวอร์ชันของสตรีมที่ให้บริการได้ โดยมีหมายเลขเวอร์ชันเพิ่มขึ้น

  • โมเดล : โมเดลที่เรียนรู้ด้วยเครื่องแสดงด้วยบริการหนึ่งรายการขึ้นไป ตัวอย่างของการให้บริการได้แก่:

    • เซสชัน TensorFlow หรือสิ่งล้อมรอบ เช่น SavedModelBundle
    • โมเดลการเรียนรู้ของเครื่องประเภทอื่นๆ
    • ตารางค้นหาคำศัพท์
    • การฝังตารางการค้นหา

    โมเดลคอมโพสิตสามารถแสดงเป็นบริการอิสระหลายรายการ หรือเป็นบริการแบบคอมโพสิตเดียว การให้บริการยังอาจสอดคล้องกับเศษส่วนของโมเดลด้วย เช่น ตารางการค้นหาขนาดใหญ่ที่แบ่งส่วนในอินสแตนซ์ Manager จำนวนมาก

หากต้องการนำสิ่งเหล่านี้ไปไว้ในบริบทของบทช่วยสอนนี้:

  • โมเดล TensorFlow จะแสดงด้วย servable ประเภทหนึ่ง นั่นคือ SavedModelBundle SavedModelBundle ภายในประกอบด้วย tensorflow:Session ที่จับคู่กับข้อมูลเมตาบางส่วนเกี่ยวกับกราฟที่โหลดลงในเซสชัน และวิธีการเรียกใช้เพื่อการอนุมาน

  • มีไดเร็กทอรีระบบไฟล์ที่มีสตรีมของการส่งออก TensorFlow โดยแต่ละรายการอยู่ในไดเร็กทอรีย่อยของตัวเองซึ่งมีชื่อเป็นหมายเลขเวอร์ชัน ไดเร็กทอรีภายนอกถือได้ว่าเป็นการนำเสนอสตรีมที่ให้บริการได้ตามลำดับสำหรับโมเดล TensorFlow ที่ให้บริการ การส่งออกแต่ละครั้งจะสอดคล้องกับบริการที่สามารถโหลดได้

  • AspiredVersionsManager ตรวจสอบสตรีมการส่งออก และจัดการวงจรการใช้งานของบริการ SavedModelBundle ทั้งหมดแบบไดนามิก

TensorflowPredictImpl::Predict จากนั้นเพียง:

  • ร้องขอ SavedModelBundle จากผู้จัดการ (ผ่าน ServerCore)
  • ใช้ generic signatures เพื่อแมปชื่อเทนเซอร์แบบลอจิคัลใน PredictRequest กับชื่อเทนเซอร์จริงและผูกค่ากับเทนเซอร์
  • เรียกใช้การอนุมาน

ทดสอบและรันเซิร์ฟเวอร์

คัดลอกเวอร์ชันแรกของการส่งออกไปยังโฟลเดอร์ที่ได้รับการตรวจสอบ:

mkdir /tmp/monitored
cp -r /tmp/mnist/1 /tmp/monitored

จากนั้นเริ่มเซิร์ฟเวอร์:

docker run -p 8500:8500 \
  --mount type=bind,source=/tmp/monitored,target=/models/mnist \
  -t --entrypoint=tensorflow_model_server tensorflow/serving --enable_batching \
  --port=8500 --model_name=mnist --model_base_path=/models/mnist &

เซิร์ฟเวอร์จะส่งข้อความบันทึกทุกๆ หนึ่งวินาทีที่ระบุว่า "เวอร์ชัน Aspiring for servable ..." ซึ่งหมายความว่าพบการส่งออกแล้ว และกำลังติดตามการมีอยู่อย่างต่อเนื่อง

มารันไคลเอนต์ด้วย --concurrency=10 สิ่งนี้จะส่งคำขอพร้อมกันไปยังเซิร์ฟเวอร์ และทริกเกอร์ตรรกะการจัดชุดของคุณ

tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
  --num_tests=1000 --server=127.0.0.1:8500 --concurrency=10

ซึ่งส่งผลให้ผลลัพธ์ที่มีลักษณะดังนี้:

...
Inference error rate: 13.1%

จากนั้นเราจะคัดลอกเวอร์ชันที่สองของการส่งออกไปยังโฟลเดอร์ที่ได้รับการตรวจสอบและทำการทดสอบอีกครั้ง:

cp -r /tmp/mnist/2 /tmp/monitored
tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
  --num_tests=1000 --server=127.0.0.1:8500 --concurrency=10

ซึ่งส่งผลให้ผลลัพธ์ที่มีลักษณะดังนี้:

...
Inference error rate: 9.5%

นี่เป็นการยืนยันว่าเซิร์ฟเวอร์ของคุณจะค้นพบเวอร์ชันใหม่โดยอัตโนมัติและใช้สำหรับการให้บริการ!