ساخت سرور استاندارد TensorFlow ModelServer

این آموزش به شما نشان می دهد که چگونه از اجزای TensorFlow Serving برای ساختن استاندارد TensorFlow ModelServer استفاده کنید که به صورت پویا نسخه های جدید یک مدل آموزش دیده TensorFlow را کشف و ارائه می کند. اگر فقط می‌خواهید از سرور استاندارد برای ارائه مدل‌های خود استفاده کنید، به آموزش پایه TensorFlow Serving مراجعه کنید.

این آموزش از مدل رگرسیون سافت مکس ساده معرفی شده در آموزش TensorFlow برای طبقه بندی تصاویر دست نویس (MNIST data) استفاده می کند. اگر نمی دانید TensorFlow یا MNIST چیست، به آموزش MNIST For ML Beginners مراجعه کنید.

کد این آموزش شامل دو بخش است:

  • یک فایل پایتون 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

آموزش (با 2000 تکرار) و صادرات نسخه دوم مدل:

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

ServerCore

حال تصور کنید v1 و v2 مدل به صورت پویا در زمان اجرا تولید می‌شوند، زمانی که الگوریتم‌های جدید در حال آزمایش هستند، یا زمانی که مدل با یک مجموعه داده جدید آموزش داده می‌شود. در یک محیط تولید، ممکن است بخواهید سروری بسازید که بتواند از عرضه تدریجی پشتیبانی کند، که در آن نسخه 2 را می توان در حین ارائه نسخه 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 (مسیری که در آن نسخه مدل کشف می‌شود) را با Loader مدل تطبیق می‌دهد (نسخه مدل را از مسیر ذخیره‌سازی بارگیری می‌کند و رابط‌های انتقال حالت را برای Manager فراهم می‌کند). اگر PlatformConfig حاوی SavedModelBundleSourceAdapterConfig باشد، یک SavedModelBundleSourceAdapter ایجاد می شود که بعداً توضیح خواهیم داد.

SavedModelBundle یک جزء کلیدی از سرویس TensorFlow است. این یک مدل 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 بهره می‌برید. برای مثال، می‌توانید یک افزونه منبع داده بسازید که به جای ذخیره‌سازی محلی، فضای ذخیره‌سازی ابری را نظارت می‌کند، یا می‌توانید یک پلاگین خط‌مشی نسخه بسازید که انتقال نسخه را به روشی متفاوت انجام می‌دهد - در واقع، حتی می‌توانید یک پلاگین مدل سفارشی بسازید که خدمت می‌کند. مدل های غیر تنسورفلو این موضوعات خارج از محدوده این آموزش هستند. با این حال، می توانید برای اطلاعات بیشتر به منبع سفارشی و آموزش های قابل سرویس دهی سفارشی مراجعه کنید.

دسته بندی

یکی دیگر از ویژگی های معمولی سرور که در محیط تولید می خواهیم، ​​دسته بندی است. شتاب‌دهنده‌های سخت‌افزاری مدرن (GPU و غیره) که برای انجام استنتاج یادگیری ماشین استفاده می‌شوند، معمولاً زمانی که درخواست‌های استنتاج در دسته‌های بزرگ اجرا می‌شوند، بهترین بازده محاسباتی را به دست می‌آورند.

هنگام ایجاد SavedModelBundleSourceAdapter ، می‌توان با ارائه SessionBundleConfig مناسب، دسته‌بندی را فعال کرد. در این مورد ما BatchingParameters تقریباً با مقادیر پیش فرض تنظیم می کنیم. دسته‌بندی را می‌توان با تنظیم مقادیر زمان‌بندی سفارشی، batch_size و غیره به‌خوبی تنظیم کرد. برای جزئیات، لطفاً به 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;

پس از رسیدن به دسته کامل، درخواست‌های استنتاج به صورت داخلی در یک درخواست بزرگ (tensor) ادغام می‌شوند و tensorflow::Session::Run() فراخوانی می‌شود (که همان جایی است که افزایش بازده واقعی در GPUها از آنجا حاصل می‌شود).

خدمت با مدیر

همانطور که در بالا ذکر شد، TensorFlow Serving Manager به گونه‌ای طراحی شده است که یک مؤلفه عمومی است که می‌تواند بارگیری، سرویس دهی، تخلیه و انتقال نسخه مدل‌های تولید شده توسط سیستم‌های یادگیری ماشین دلخواه را مدیریت کند. API های آن بر اساس مفاهیم کلیدی زیر ساخته شده اند:

  • Servable : Servable هر شیء غیر شفافی است که می تواند برای ارائه درخواست های مشتری استفاده شود. اندازه و دانه بندی یک قابل ارائه انعطاف پذیر است، به طوری که یک قابل سرویس دهی ممکن است شامل هر چیزی از یک تکه جدول جستجو گرفته تا یک مدل منفرد یادگیری ماشینی و چند مدل باشد. یک سرویس پذیر می تواند از هر نوع و رابطی باشد.

  • نسخه قابل سرویس دهی : Servable ها نسخه شده اند و TensorFlow Serving Manager می تواند یک یا چند نسخه از یک سرویس پذیر را مدیریت کند. نسخه‌سازی اجازه می‌دهد تا بیش از یک نسخه از یک سرویس‌دهی به طور همزمان بارگذاری شود، که از عرضه تدریجی و آزمایش پشتیبانی می‌کند.

  • جریان قابل سرویس دهی : یک جریان قابل سرویس، دنباله ای از نسخه های یک سرویس پذیر با افزایش تعداد نسخه ها است.

  • مدل : یک مدل یادگیری ماشینی با یک یا چند سرویس پذیر نشان داده می شود. نمونه هایی از سرویس پذیرها عبارتند از:

    • جلسه TensorFlow یا بسته‌بندی‌های اطراف آن‌ها، مانند SavedModelBundle .
    • انواع دیگر مدل های یادگیری ماشینی.
    • جداول جستجوی واژگان
    • جاسازی جداول جستجو

    یک مدل کامپوزیت را می‌توان به‌عنوان چندین سرویس‌پذیر مستقل یا به‌عنوان یک سرویس‌پذیر ترکیبی منفرد نشان داد. یک سرویس‌پذیر نیز ممکن است با کسری از یک مدل مطابقت داشته باشد، برای مثال با یک جدول جستجوی بزرگ که در بسیاری از نمونه‌های Manager تقسیم شده است.

برای قرار دادن همه اینها در چارچوب این آموزش:

  • مدل های TensorFlow با یک نوع سرویس پذیر نشان داده می شوند -- 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 &

سرور در هر ثانیه پیام‌های گزارشی را منتشر می‌کند که می‌گویند "نسخه مشتاق برای سرویس‌دهی ..."، که به این معنی است که صادرات را پیدا کرده است و ادامه حیات آن را دنبال می‌کند.

بیایید کلاینت را با --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%

این تأیید می کند که سرور شما به طور خودکار نسخه جدید را پیدا کرده و از آن برای سرویس دهی استفاده می کند!