Treten Sie der SIG TFX-Addons-Community bei und helfen Sie mit, TFX noch besser zu machen! SIG TFX-Addons beitreten

Erstellen eines Standard-TensorFlow-Modellservers

Dieses Tutorial zeigt Ihnen, wie Sie mithilfe von TensorFlow Serving-Komponenten den Standard-TensorFlow ModelServer erstellen, der neue Versionen eines trainierten TensorFlow-Modells dynamisch erkennt und bereitstellt. Wenn Sie nur den Standardserver für die Bereitstellung Ihrer Modelle verwenden möchten, lesen Sie das grundlegende Tutorial zu TensorFlow Serving .

In diesem Lernprogramm wird das einfache Softmax-Regressionsmodell verwendet, das im TensorFlow-Lernprogramm für die Klassifizierung handgeschriebener Bilder (MNIST-Daten) eingeführt wurde. Wenn Sie nicht wissen, was TensorFlow oder MNIST ist, lesen Sie das Tutorial MNIST für ML-Anfänger .

Der Code für dieses Tutorial besteht aus zwei Teilen:

  • Eine Python-Datei mnist_saved_model.py , die mehrere Versionen des Modells trainiert und exportiert.

  • Eine C ++ - Datei main.cc, die der Standard-TensorFlow ModelServer ist, der neue exportierte Modelle erkennt und einen gRPC- Dienst für deren Bereitstellung ausführt.

In diesem Tutorial werden die folgenden Aufgaben ausgeführt:

  1. Trainieren und exportieren Sie ein TensorFlow-Modell.
  2. Verwalten Sie die Modellversionierung mit TensorFlow Serving ServerCore .
  3. Konfigurieren Sie die SavedModelBundleSourceAdapterConfig mit SavedModelBundleSourceAdapterConfig .
  4. Anfrage mit TensorFlow Serving ServerCore .
  5. Führen Sie den Dienst aus und testen Sie ihn.

Bevor Sie beginnen, installieren Sie zuerst Docker

TensorFlow-Modell trainieren und exportieren

Wenn Sie dies noch nicht getan haben, klonen Sie dieses Repository zunächst auf Ihren lokalen Computer:

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

Löschen Sie das Exportverzeichnis, falls es bereits vorhanden ist:

rm -rf /tmp/models

Trainiere (mit 100 Iterationen) und exportiere die erste Version des Modells:

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

Trainiere (mit 2000 Iterationen) und exportiere die zweite Version des Modells:

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

Wie Sie in mnist_saved_model.py , erfolgt das Training und Exportieren genauso wie im TensorFlow Serving-Lernprogramm . Zu Demonstrationszwecken wählen Sie absichtlich die Trainingsiterationen für den ersten Lauf herunter und exportieren sie als v1, während Sie sie normalerweise für den zweiten Lauf trainieren und als v2 in dasselbe übergeordnete Verzeichnis exportieren - wie wir es von letzterem erwarten Bessere Klassifizierungsgenauigkeit durch intensiveres Training. Sie sollten die Trainingsdaten für jeden Trainingslauf in Ihrem Verzeichnis /tmp/mnist :

$ ls /tmp/mnist
1  2

ServerCore

Stellen Sie sich nun vor, v1 und v2 des Modells werden zur Laufzeit dynamisch generiert, wenn neue Algorithmen erprobt werden oder wenn das Modell mit einem neuen Datensatz trainiert wird. In einer Produktionsumgebung möchten Sie möglicherweise einen Server erstellen, der die schrittweise Einführung unterstützt, auf dem Version 2 während der Bereitstellung von Version 1 erkannt, geladen, experimentiert, überwacht oder zurückgesetzt werden kann. Alternativ können Sie v1 herunterfahren, bevor Sie v2 aufrufen. TensorFlow Serving unterstützt beide Optionen - während eine für die Aufrechterhaltung der Verfügbarkeit während des Übergangs geeignet ist, ist die andere für die Minimierung des Ressourcenverbrauchs (z. B. RAM) geeignet.

TensorFlow Serving Manager macht genau das. Es behandelt den gesamten Lebenszyklus von TensorFlow-Modellen, einschließlich Laden, Servieren und Entladen sowie Versionsübergänge. In diesem Lernprogramm bauen Sie Ihren Server auf einem TensorFlow Serving ServerCore , der einen AspiredVersionsManager intern 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() einen ServerCore :: Options-Parameter. Hier sind einige häufig verwendete Optionen:

  • ModelServerConfig , der die zu ladenden Modelle angibt. Modelle werden entweder über model_config_list deklariert, das eine statische Liste von Modellen deklariert, oder über custom_model_config , das eine benutzerdefinierte Methode zum custom_model_config einer Liste von Modellen definiert, die zur Laufzeit aktualisiert werden können.
  • PlatformConfigMap , die vom Namen der Plattform (z. B. tensorflow ) der PlatformConfig , mit der der SourceAdapter . SourceAdapter passt StoragePath (den Pfad, in dem eine Modellversion StoragePath wird) an Model Loader (lädt die Modellversion aus dem Speicherpfad und stellt dem Manager SourceAdapter StoragePath ). Wenn PlatformConfig SavedModelBundleSourceAdapterConfig enthält, wird ein SavedModelBundleSourceAdapter erstellt, den wir später erläutern werden.

SavedModelBundle ist eine Schlüsselkomponente von TensorFlow Serving. Es stellt ein TensorFlow-Modell dar, das aus einem bestimmten Pfad geladen wurde, und bietet dieselbe Session::Run Schnittstelle wie TensorFlow, um Inferenz auszuführen. SavedModelBundleSourceAdapter passt den Speicherpfad an Loader<SavedModelBundle> sodass die Modelllebensdauer vom Manager verwaltet werden kann. Bitte beachten Sie, dass SavedModelBundle der Nachfolger des veralteten SessionBundle . Benutzer werden aufgefordert, SavedModelBundle zu verwenden, da die Unterstützung für SessionBundle bald entfernt wird.

Mit all diesen Funktionen führt ServerCore intern Folgendes aus:

  • Instanziiert eine FileSystemStoragePathSource , die die in model_config_list deklarierten model_config_list .
  • SourceAdapter einen SourceAdapter mithilfe der PlatformConfigMap mit der in model_config_list deklarierten model_config_list und verbindet die FileSystemStoragePathSource damit. Auf diese Weise passt der SavedModelBundleSourceAdapter , wenn eine neue Modellversion unter dem SavedModelBundleSourceAdapter diese an einen Loader<SavedModelBundle> .
  • Instanziiert eine bestimmte Implementierung von Manager namens AspiredVersionsManager , die alle vom SavedModelBundleSourceAdapter erstellten Loader Instanzen SavedModelBundleSourceAdapter . ServerCore exportiert die Manager Oberfläche, indem die Aufrufe an AspiredVersionsManager .

Immer wenn eine neue Version verfügbar ist, lädt dieser AspiredVersionsManager die neue Version und entlädt unter seinem Standardverhalten die alte. Wenn Sie mit dem Anpassen beginnen möchten, sollten Sie die intern erstellten Komponenten und deren Konfiguration verstehen.

Es ist erwähnenswert, dass TensorFlow Serving von Grund auf neu entwickelt wurde, um sehr flexibel und erweiterbar zu sein. Sie können verschiedene Plugins erstellen, um das Systemverhalten anzupassen, und dabei allgemeine Kernkomponenten wie ServerCore und AspiredVersionsManager . Sie können beispielsweise ein Datenquellen-Plugin erstellen, das den Cloud-Speicher anstelle des lokalen Speichers überwacht, oder Sie können ein Versionsrichtlinien-Plugin erstellen, das den Versionsübergang auf andere Weise ausführt. Sie können sogar ein benutzerdefiniertes Modell-Plugin erstellen, das dient Nicht-TensorFlow-Modelle. Diese Themen sind für dieses Lernprogramm nicht relevant. Weitere Informationen finden Sie jedoch in der benutzerdefinierten Quelle und in den benutzerdefinierten Tutorials.

Batching

Eine weitere typische Serverfunktion, die wir in einer Produktionsumgebung benötigen, ist das Batching. Moderne Hardwarebeschleuniger (GPUs usw.), die für Inferenzen beim maschinellen Lernen verwendet werden, erzielen normalerweise die beste Berechnungseffizienz, wenn Inferenzanforderungen in großen Stapeln ausgeführt werden.

Die Stapelverarbeitung kann aktiviert werden, SessionBundleConfig beim Erstellen des SavedModelBundleSourceAdapter die richtige SessionBundleConfig SavedModelBundleSourceAdapter . In diesem Fall setzen wir die BatchingParameters auf ziemlich viele Standardwerte. Die Stapelverarbeitung kann durch Festlegen von benutzerdefinierten Werten für Zeitlimit, Stapelgröße usw. verfeinert werden. Einzelheiten finden Sie unter 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;

Bei Erreichen des vollständigen Stapels werden Inferenzanforderungen intern zu einer einzigen großen Anforderung (Tensor) zusammengeführt, und tensorflow::Session::Run() wird aufgerufen (daher kommt der tatsächliche Effizienzgewinn bei GPUs).

Mit Manager servieren

Wie oben erwähnt, ist TensorFlow Serving Manager als generische Komponente konzipiert, die das Laden, Servieren, Entladen und den Versionsübergang von Modellen übernimmt, die von beliebigen maschinellen Lernsystemen generiert werden. Die APIs basieren auf den folgenden Schlüsselkonzepten:

  • Servable : Servable ist ein undurchsichtiges Objekt, mit dem Clientanforderungen bearbeitet werden können. Die Größe und Granularität eines Servables ist flexibel, sodass ein einzelnes Servable alles umfassen kann, von einem einzelnen Shard einer Nachschlagetabelle über ein einzelnes maschinell erlerntes Modell bis hin zu einem Tupel von Modellen. Ein Servable kann von jedem Typ und jeder Schnittstelle sein.

  • Servable-Version : Servables sind versioniert und TensorFlow Serving Manager kann eine oder mehrere Versionen eines Servables verwalten. Durch die Versionierung können mehrere Versionen eines Servables gleichzeitig geladen werden, was ein schrittweises Rollout und Experimentieren unterstützt.

  • Servable Stream : Ein Servable Stream ist die Folge von Versionen eines Servable mit zunehmenden Versionsnummern.

  • Modell : Ein maschinell erlerntes Modell wird durch ein oder mehrere Servables dargestellt. Beispiele für Servables sind:

    • TensorFlow-Sitzung oder Wrapper um sie herum, z. B. SavedModelBundle .
    • Andere Arten von maschinell erlernten Modellen.
    • Vokabeltabellen.
    • Nachschlagen von Nachschlagetabellen.

    Ein zusammengesetztes Modell kann als mehrere unabhängige Servables oder als ein einzelnes zusammengesetztes Servable dargestellt werden. Ein Servable kann auch einem Bruchteil eines Modells entsprechen, z. B. mit einer großen Nachschlagetabelle, die über viele Manager Instanzen verteilt ist.

Um all dies in den Kontext dieses Tutorials zu stellen:

  • TensorFlow-Modelle werden durch eine Art von Service dargestellt - SavedModelBundle . SavedModelBundle besteht intern aus einem tensorflow:Session gepaart mit einigen Metadaten darüber, welches Diagramm in die Sitzung geladen wird und wie es zur Inferenz ausgeführt wird.

  • Es gibt ein Dateisystemverzeichnis, das einen Stream von TensorFlow-Exporten enthält, die sich jeweils in einem eigenen Unterverzeichnis befinden, dessen Name eine Versionsnummer ist. Das äußere Verzeichnis kann als serialisierte Darstellung des bedienbaren Streams für das zu bedienende TensorFlow-Modell betrachtet werden. Jeder Export entspricht einem Servables, das geladen werden kann.

  • AspiredVersionsManager überwacht den Exportstrom und verwaltet den Lebenszyklus aller SavedModelBundle Servables dynamisch.

TensorflowPredictImpl::Predict dann einfach TensorflowPredictImpl::Predict :

  • Fordert SavedModelBundle vom Manager an (über ServerCore).
  • Verwendet die generic signatures , um logische PredictRequest in PredictRequest realen PredictRequest und Werte an Tensoren zu binden.
  • Läuft Inferenz.

Testen Sie den Server und führen Sie ihn aus

Kopieren Sie die erste Version des Exports in den überwachten Ordner:

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

Starten Sie dann den Server:

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 &

Der Server sendet jede Sekunde Protokollnachrichten mit der Aufschrift "Aspiring version for servable ...". Dies bedeutet, dass er den Export gefunden hat und seinen Fortbestand verfolgt.

Lassen Sie uns den Client mit --concurrency=10 . Dadurch werden gleichzeitige Anforderungen an den Server gesendet und somit Ihre Stapellogik ausgelöst.

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

Was zu einer Ausgabe führt, die wie folgt aussieht:

...
Inference error rate: 13.1%

Dann kopieren wir die zweite Version des Exports in den überwachten Ordner und führen den Test erneut aus:

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

Was zu einer Ausgabe führt, die wie folgt aussieht:

...
Inference error rate: 9.5%

Dies bestätigt, dass Ihr Server die neue Version automatisch erkennt und zum Servieren verwendet!