Xây dựng mô hình TensorFlow tiêu chuẩn

Hướng dẫn này chỉ cho bạn cách sử dụng các thành phần Phục vụ TensorFlow để xây dựng Máy chủ mô hình TensorFlow tiêu chuẩn có khả năng tự động phát hiện và phục vụ các phiên bản mới của mô hình TensorFlow đã được đào tạo. Nếu bạn chỉ muốn sử dụng máy chủ tiêu chuẩn để phục vụ các mô hình của mình, hãy xem hướng dẫn cơ bản về Phục vụ TensorFlow .

Hướng dẫn này sử dụng mô hình hồi quy Softmax đơn giản được giới thiệu trong hướng dẫn TensorFlow để phân loại hình ảnh viết tay (dữ liệu MNIST). Nếu bạn không biết TensorFlow hoặc MNIST là gì, hãy xem hướng dẫn MNIST dành cho người mới bắt đầu học ML .

Mã cho hướng dẫn này bao gồm hai phần:

  • Tệp Python mnist_saved_model.py huấn luyện và xuất nhiều phiên bản của mô hình.

  • Tệp C++ main.cc là Máy chủ mô hình TensorFlow tiêu chuẩn giúp phát hiện các mô hình được xuất mới và chạy dịch vụ gRPC để phục vụ chúng.

Hướng dẫn này sẽ thực hiện các nhiệm vụ sau:

  1. Đào tạo và xuất mô hình TensorFlow.
  2. Quản lý phiên bản mô hình với TensorFlow Serve ServerCore .
  3. Định cấu hình phân nhóm bằng cách sử dụng SavedModelBundleSourceAdapterConfig .
  4. Phục vụ yêu cầu với TensorFlow Serve ServerCore .
  5. Chạy và kiểm tra dịch vụ.

Trước khi bắt đầu, trước tiên hãy cài đặt Docker

Đào tạo và xuất mô hình TensorFlow

Trước tiên, nếu bạn chưa làm như vậy, hãy sao chép kho lưu trữ này vào máy cục bộ của bạn:

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

Xóa thư mục xuất nếu nó đã tồn tại:

rm -rf /tmp/models

Huấn luyện (với 100 lần lặp) và xuất phiên bản đầu tiên của mô hình:

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

Huấn luyện (với 2000 lần lặp) và xuất phiên bản thứ hai của mô hình:

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

Như bạn có thể thấy trong mnist_saved_model.py , việc đào tạo và xuất được thực hiện giống như trong hướng dẫn cơ bản về TensorFlow Serve . Với mục đích trình diễn, bạn đang cố tình quay số vòng lặp huấn luyện cho lần chạy đầu tiên và xuất dưới dạng v1, trong khi huấn luyện bình thường cho lần chạy thứ hai và xuất dưới dạng v2 vào cùng thư mục mẹ -- như chúng tôi mong đợi thư mục sau sẽ đạt được độ chính xác phân loại tốt hơn do được đào tạo chuyên sâu hơn. Bạn sẽ thấy dữ liệu huấn luyện cho mỗi lần huấn luyện trong thư mục /tmp/mnist của mình:

$ ls /tmp/mnist
1  2

Lõi máy chủ

Bây giờ hãy tưởng tượng v1 và v2 của mô hình được tạo động trong thời gian chạy, khi các thuật toán mới đang được thử nghiệm hoặc khi mô hình được huấn luyện với một tập dữ liệu mới. Trong môi trường sản xuất, bạn có thể muốn xây dựng một máy chủ có thể hỗ trợ triển khai dần dần, trong đó v2 có thể được phát hiện, tải, thử nghiệm, giám sát hoặc hoàn nguyên trong khi cung cấp v1. Ngoài ra, bạn có thể muốn gỡ bỏ v1 trước khi đưa lên v2. TensorFlow Serve hỗ trợ cả hai tùy chọn -- trong khi một tùy chọn phù hợp để duy trì tính khả dụng trong quá trình chuyển đổi, tùy chọn còn lại phù hợp để giảm thiểu mức sử dụng tài nguyên (ví dụ: RAM).

Manager phục vụ TensorFlow thực hiện chính xác điều đó. Nó xử lý toàn bộ vòng đời của các mô hình TensorFlow bao gồm tải, phục vụ và dỡ bỏ chúng cũng như chuyển đổi phiên bản. Trong hướng dẫn này, bạn sẽ xây dựng máy chủ của mình trên TensorFlow Serve ServerCore , bao bọc bên trong 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() lấy tham số ServerCore::Options. Dưới đây là một số tùy chọn thường được sử dụng:

  • ModelServerConfig chỉ định các mô hình sẽ được tải. Các mô hình được khai báo thông qua model_config_list , khai báo danh sách tĩnh các mô hình hoặc thông qua custom_model_config , xác định cách tùy chỉnh để khai báo danh sách các mô hình có thể được cập nhật khi chạy.
  • PlatformConfigMap ánh xạ từ tên của nền tảng (chẳng hạn như tensorflow ) tới PlatformConfig , được sử dụng để tạo SourceAdapter . SourceAdapter điều chỉnh StoragePath (đường dẫn phát hiện phiên bản mô hình) thành Loader tải mô hình (tải phiên bản mô hình từ đường dẫn lưu trữ và cung cấp giao diện chuyển trạng thái cho Manager ). Nếu PlatformConfig chứa SavedModelBundleSourceAdapterConfig thì một SavedModelBundleSourceAdapter sẽ được tạo và chúng tôi sẽ giải thích sau.

SavedModelBundle là thành phần chính của TensorFlow Serve. Nó đại diện cho một mô hình TensorFlow được tải từ một đường dẫn nhất định và cung cấp giao diện Session::Run giống như TensorFlow để chạy suy luận. SavedModelBundleSourceAdapter điều chỉnh đường dẫn lưu trữ thành Loader<SavedModelBundle> để vòng đời mô hình có thể được quản lý bởi Manager . Xin lưu ý rằng SavedModelBundle là phiên bản kế thừa của SessionBundle không được dùng nữa. Người dùng được khuyến khích sử dụng SavedModelBundle vì tính năng hỗ trợ cho SessionBundle sẽ sớm bị xóa.

Với tất cả những điều này, ServerCore thực hiện nội bộ những điều sau:

  • Khởi tạo FileSystemStoragePathSource giám sát các đường dẫn xuất mô hình được khai báo trong model_config_list .
  • Khởi tạo SourceAdapter bằng cách sử dụng PlatformConfigMap với nền tảng mô hình được khai báo trong model_config_list và kết nối FileSystemStoragePathSource với nó. Bằng cách này, bất cứ khi nào một phiên bản mô hình mới được phát hiện trong đường dẫn xuất, SavedModelBundleSourceAdapter sẽ điều chỉnh nó thành Loader<SavedModelBundle> .
  • Khởi tạo một triển khai cụ thể của Manager có tên là AspiredVersionsManager để quản lý tất cả các phiên bản Loader như vậy được tạo bởi SavedModelBundleSourceAdapter . ServerCore xuất giao diện Trình Manager bằng cách ủy quyền các cuộc gọi đến AspiredVersionsManager .

Bất cứ khi nào có phiên bản mới, AspiredVersionsManager này sẽ tải phiên bản mới và theo hành vi mặc định của nó sẽ dỡ phiên bản cũ. Nếu muốn bắt đầu tùy chỉnh, bạn nên hiểu các thành phần mà nó tạo ra bên trong và cách định cấu hình chúng.

Điều đáng nói là TensorFlow Serve được thiết kế từ đầu nên rất linh hoạt và có khả năng mở rộng. Bạn có thể xây dựng nhiều plugin khác nhau để tùy chỉnh hoạt động của hệ thống, đồng thời tận dụng các thành phần cốt lõi chung như ServerCoreAspiredVersionsManager . Ví dụ: bạn có thể xây dựng plugin nguồn dữ liệu giám sát lưu trữ đám mây thay vì lưu trữ cục bộ hoặc bạn có thể xây dựng plugin chính sách phiên bản thực hiện chuyển đổi phiên bản theo cách khác -- trên thực tế, bạn thậm chí có thể xây dựng plugin mô hình tùy chỉnh phục vụ các mô hình không phải TensorFlow. Những chủ đề này nằm ngoài phạm vi của hướng dẫn này. Tuy nhiên, bạn có thể tham khảo nguồn tùy chỉnh và hướng dẫn có thể phân phát tùy chỉnh để biết thêm thông tin.

trộn

Một tính năng máy chủ điển hình khác mà chúng tôi muốn có trong môi trường sản xuất là tạo khối. Các bộ tăng tốc phần cứng hiện đại (GPU, v.v.) được sử dụng để thực hiện suy luận máy học thường đạt được hiệu quả tính toán tốt nhất khi các yêu cầu suy luận được chạy theo lô lớn.

Có thể bật tính năng tạo khối bằng cách cung cấp SessionBundleConfig thích hợp khi tạo SavedModelBundleSourceAdapter . Trong trường hợp này, chúng tôi đặt BatchingParameters với khá nhiều giá trị mặc định. Việc tạo khối có thể được tinh chỉnh bằng cách đặt các giá trị thời gian chờ tùy chỉnh, batch_size, v.v. Để biết chi tiết, vui lòng tham khảo 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;

Khi đạt đến lô đầy đủ, các yêu cầu suy luận sẽ được hợp nhất nội bộ thành một yêu cầu lớn duy nhất (tensor) và tensorflow::Session::Run() được gọi (đây là nguồn gốc của việc tăng hiệu suất thực tế trên GPU).

Phục vụ với người quản lý

Như đã đề cập ở trên, Manager phục vụ TensorFlow được thiết kế để trở thành một thành phần chung có thể xử lý việc tải, phục vụ, dỡ tải và chuyển đổi phiên bản của các mô hình được tạo bởi các hệ thống máy học tùy ý. API của nó được xây dựng dựa trên các khái niệm chính sau:

  • Có thể phục vụ : Có thể phục vụ là bất kỳ đối tượng mờ nào có thể được sử dụng để phục vụ các yêu cầu của khách hàng. Kích thước và mức độ chi tiết của một thành phần có thể phân phát rất linh hoạt, sao cho một thành phần có thể phân phát có thể bao gồm mọi thứ từ một phân đoạn duy nhất của bảng tra cứu đến một mô hình học máy duy nhất cho đến một bộ mô hình. Một dịch vụ có thể thuộc bất kỳ loại và giao diện nào.

  • Phiên bản có thể phục vụ : Các phiên bản có thể phục vụ được lập phiên bản và Manager phục vụ TensorFlow có thể quản lý một hoặc nhiều phiên bản của một phiên bản có thể phục vụ. Việc lập phiên bản cho phép tải đồng thời nhiều phiên bản của một phiên bản có thể phân phát, hỗ trợ triển khai và thử nghiệm dần dần.

  • Luồng có thể phục vụ : Luồng có thể phục vụ là chuỗi các phiên bản của một luồng có thể phục vụ, với số phiên bản ngày càng tăng.

  • Mô hình : Một mô hình học máy được biểu thị bằng một hoặc nhiều dịch vụ. Ví dụ về các dịch vụ có thể phục vụ là:

    • Phiên TensorFlow hoặc các trình bao bọc xung quanh chúng, chẳng hạn như SavedModelBundle .
    • Các loại mô hình học máy khác.
    • Bảng tra cứu từ vựng.
    • Nhúng bảng tra cứu.

    Một mô hình tổng hợp có thể được biểu diễn dưới dạng nhiều dịch vụ có thể phân phát độc lập hoặc dưới dạng một dịch vụ tổng hợp duy nhất. Một phần có thể phân phát cũng có thể tương ứng với một phần của Mô hình, chẳng hạn như với một bảng tra cứu lớn được phân chia trên nhiều phiên bản Manager .

Để đặt tất cả những điều này vào bối cảnh của hướng dẫn này:

  • Các mô hình TensorFlow được thể hiện bằng một loại có thể phục vụ -- SavedModelBundle . SavedModelBundle bên trong bao gồm một tensorflow:Session được ghép nối với một số siêu dữ liệu về biểu đồ nào được tải vào phiên và cách chạy biểu đồ đó để suy luận.

  • Có một thư mục hệ thống tệp chứa luồng xuất TensorFlow, mỗi luồng nằm trong thư mục con riêng có tên là số phiên bản. Thư mục bên ngoài có thể được coi là biểu diễn tuần tự của luồng có thể phục vụ cho mô hình TensorFlow đang được phục vụ. Mỗi lần xuất tương ứng với một dịch vụ có thể được tải.

  • AspiredVersionsManager giám sát luồng xuất và quản lý vòng đời của tất cả các dịch vụ SavedModelBundle một cách linh hoạt.

TensorflowPredictImpl::Predict thì chỉ cần:

  • Yêu cầu SavedModelBundle từ người quản lý (thông qua ServerCore).
  • Sử dụng generic signatures để ánh xạ các tên tensor logic trong PredictRequest với các tên tensor thực và liên kết các giá trị với các tensor.
  • Chạy suy luận.

Kiểm tra và chạy máy chủ

Sao chép phiên bản xuất đầu tiên vào thư mục được giám sát:

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

Sau đó khởi động máy chủ:

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 &

Máy chủ sẽ phát ra thông báo nhật ký mỗi giây có nội dung "Phiên bản mong muốn dành cho có thể phục vụ ...", có nghĩa là máy chủ đã tìm thấy bản xuất và đang theo dõi sự tồn tại liên tục của nó.

Hãy chạy ứng dụng khách với --concurrency=10 . Điều này sẽ gửi các yêu cầu đồng thời đến máy chủ và do đó kích hoạt logic phân nhóm của bạn.

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

Kết quả là đầu ra trông giống như:

...
Inference error rate: 13.1%

Sau đó, chúng tôi sao chép phiên bản xuất thứ hai vào thư mục được giám sát và chạy lại thử nghiệm:

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

Kết quả là đầu ra trông giống như:

...
Inference error rate: 9.5%

Điều này xác nhận rằng máy chủ của bạn tự động phát hiện phiên bản mới và sử dụng nó để phục vụ!