Questo tutorial mostra come utilizzare i componenti TensorFlow Serving per creare il TensorFlow ModelServer standard che rileva e serve dinamicamente nuove versioni di un modello TensorFlow addestrato. Se desideri utilizzare semplicemente il server standard per servire i tuoi modelli, consulta il tutorial di base su TensorFlow Serving .
Questo tutorial utilizza il semplice modello di regressione Softmax introdotto nel tutorial TensorFlow per la classificazione di immagini scritte a mano (dati MNIST). Se non sai cos'è TensorFlow o MNIST, consulta il tutorial MNIST per principianti ML .
Il codice per questo tutorial è composto da due parti:
Un file Python mnist_saved_model.py che addestra ed esporta più versioni del modello.
Un file C++ main.cc che è il TensorFlow ModelServer standard che rileva nuovi modelli esportati ed esegue un servizio gRPC per servirli.
Questo tutorial illustra le seguenti attività:
- Addestra ed esporta un modello TensorFlow.
- Gestisci il controllo delle versioni del modello con TensorFlow Serving
ServerCore
. - Configurare il batch utilizzando
SavedModelBundleSourceAdapterConfig
. - Servire la richiesta con TensorFlow Serving
ServerCore
. - Esegui e testa il servizio.
Prima di iniziare, installa Docker
Addestra ed esporta il modello TensorFlow
Innanzitutto, se non l'hai ancora fatto, clona questo repository sul tuo computer locale:
git clone https://github.com/tensorflow/serving.git
cd serving
Cancella la directory di esportazione se esiste già:
rm -rf /tmp/models
Addestra (con 100 iterazioni) ed esporta la prima versione del modello:
tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
--training_iteration=100 --model_version=1 /tmp/mnist
Addestra (con 2000 iterazioni) ed esporta la seconda versione del modello:
tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
--training_iteration=2000 --model_version=2 /tmp/mnist
Come puoi vedere in mnist_saved_model.py
, l'addestramento e l'esportazione vengono eseguiti nello stesso modo del tutorial di base di TensorFlow Serving . A scopo dimostrativo, stai intenzionalmente riducendo le iterazioni di addestramento per la prima esecuzione ed esportandolo come v1, mentre lo stai addestrando normalmente per la seconda esecuzione ed esportandolo come v2 nella stessa directory principale, come ci aspettiamo che quest'ultima ottenga migliore precisione di classificazione grazie ad una formazione più intensiva. Dovresti vedere i dati di addestramento per ogni esecuzione di addestramento nella directory /tmp/mnist
:
$ ls /tmp/mnist
1 2
ServerCore
Ora immagina che v1 e v2 del modello vengano generati dinamicamente in fase di esecuzione, mentre vengono sperimentati nuovi algoritmi o mentre il modello viene addestrato con un nuovo set di dati. In un ambiente di produzione, potresti voler creare un server in grado di supportare l'implementazione graduale, in cui la v2 può essere scoperta, caricata, sperimentata, monitorata o ripristinata mentre si serve la v1. In alternativa, potresti voler eliminare la v1 prima di aprire la v2. TensorFlow Serving supporta entrambe le opzioni: mentre una è utile per mantenere la disponibilità durante la transizione, l'altra è utile per ridurre al minimo l'utilizzo delle risorse (ad esempio la RAM).
TensorFlow Serving Manager
fa esattamente questo. Gestisce l'intero ciclo di vita dei modelli TensorFlow, inclusi il caricamento, la fornitura e lo scaricamento, nonché le transizioni di versione. In questo tutorial, costruirai il tuo server su un TensorFlow Serving ServerCore
, che avvolge internamente un 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()
accetta un parametro ServerCore::Options. Ecco alcune opzioni comunemente utilizzate:
-
ModelServerConfig
che specifica i modelli da caricare. I modelli vengono dichiarati tramitemodel_config_list
, che dichiara un elenco statico di modelli, o tramitecustom_model_config
, che definisce un modo personalizzato per dichiarare un elenco di modelli che potrebbero essere aggiornati in fase di esecuzione. -
PlatformConfigMap
che mappa dal nome della piattaforma (cometensorflow
) aPlatformConfig
, che viene utilizzato per creareSourceAdapter
.SourceAdapter
adattaStoragePath
(il percorso in cui viene rilevata una versione del modello) al modelloLoader
(carica la versione del modello dal percorso di archiviazione e fornisce interfacce di transizione di stato aManager
). SePlatformConfig
contieneSavedModelBundleSourceAdapterConfig
, verrà creato unSavedModelBundleSourceAdapter
, che spiegheremo in seguito.
SavedModelBundle
è un componente chiave di TensorFlow Serving. Rappresenta un modello TensorFlow caricato da un determinato percorso e fornisce la stessa Session::Run
di TensorFlow per eseguire l'inferenza. SavedModelBundleSourceAdapter
adatta il percorso di archiviazione a Loader<SavedModelBundle>
in modo che la durata del modello possa essere gestita da Manager
. Tieni presente che SavedModelBundle
è il successore del deprecato SessionBundle
. Gli utenti sono incoraggiati a utilizzare SavedModelBundle
poiché il supporto per SessionBundle
verrà presto rimosso.
Con tutto ciò, ServerCore
esegue internamente quanto segue:
- Crea un'istanza di
FileSystemStoragePathSource
che monitora i percorsi di esportazione del modello dichiarati inmodel_config_list
. - Crea un'istanza di un
SourceAdapter
utilizzandoPlatformConfigMap
con la piattaforma del modello dichiarata inmodel_config_list
e connetteFileSystemStoragePathSource
ad esso. In questo modo, ogni volta che viene rilevata una nuova versione del modello nel percorso di esportazione,SavedModelBundleSourceAdapter
la adatta aLoader<SavedModelBundle>
. - Crea un'istanza di un'implementazione specifica di
Manager
denominataAspiredVersionsManager
che gestisce tutte le istanzeLoader
create daSavedModelBundleSourceAdapter
.ServerCore
esporta l'interfacciaManager
delegando le chiamate adAspiredVersionsManager
.
Ogni volta che è disponibile una nuova versione, questo AspiredVersionsManager
carica la nuova versione e, con il suo comportamento predefinito, scarica quella vecchia. Se vuoi iniziare a personalizzare, sei incoraggiato a comprendere i componenti che crea internamente e come configurarli.
Vale la pena ricordare che TensorFlow Serving è progettato da zero per essere molto flessibile ed estensibile. Puoi creare vari plugin per personalizzare il comportamento del sistema, sfruttando al tempo stesso componenti core generici come ServerCore
e AspiredVersionsManager
. Ad esempio, potresti creare un plug-in dell'origine dati che monitora l'archiviazione nel cloud anziché l'archiviazione locale oppure potresti creare un plug-in per i criteri di versione che esegua la transizione della versione in un modo diverso: in effetti, potresti persino creare un plug-in del modello personalizzato che serva modelli non TensorFlow. Questi argomenti non rientrano nell'ambito di questo tutorial. Tuttavia, puoi fare riferimento ai tutorial sull'origine personalizzata e sui servizi personalizzati per ulteriori informazioni.
Batch
Un'altra caratteristica tipica del server che desideriamo in un ambiente di produzione è il batch. I moderni acceleratori hardware (GPU, ecc.) utilizzati per eseguire l'inferenza dell'apprendimento automatico di solito raggiungono la migliore efficienza di calcolo quando le richieste di inferenza vengono eseguite in batch di grandi dimensioni.
Il batch può essere attivato fornendo SessionBundleConfig
appropriato durante la creazione di SavedModelBundleSourceAdapter
. In questo caso impostiamo BatchingParameters
con valori praticamente predefiniti. Il batching può essere ottimizzato impostando valori personalizzati di timeout, batch_size, ecc. Per i dettagli, fare riferimento a 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;
Una volta raggiunto il batch completo, le richieste di inferenza vengono unite internamente in un'unica grande richiesta (tensore) e viene richiamato tensorflow::Session::Run()
(da cui deriva l'effettivo guadagno di efficienza sulle GPU).
Servire con il manager
Come accennato in precedenza, TensorFlow Serving Manager
è progettato per essere un componente generico in grado di gestire caricamento, elaborazione, scaricamento e transizione di versione di modelli generati da sistemi di machine learning arbitrari. Le sue API si basano sui seguenti concetti chiave:
Servable : Servable è qualsiasi oggetto opaco che può essere utilizzato per servire le richieste del client. Le dimensioni e la granularità di un serverable sono flessibili, in modo che un singolo serverable possa includere qualsiasi cosa, da un singolo frammento di una tabella di ricerca a un singolo modello di apprendimento automatico a una tupla di modelli. Un servibile può essere di qualsiasi tipo e interfaccia.
Versione servabile : i servable hanno una versione e TensorFlow Serving
Manager
può gestire una o più versioni di un servable. Il controllo delle versioni consente di caricare contemporaneamente più di una versione di un server, supportando l'implementazione e la sperimentazione graduali.Flusso servibile : un flusso servibile è la sequenza di versioni di un servibile, con numeri di versione crescenti.
Modello : un modello di apprendimento automatico è rappresentato da uno o più servizi. Esempi di servibili sono:
- Sessione TensorFlow o wrapper attorno ad essi, come
SavedModelBundle
. - Altri tipi di modelli di machine learning.
- Tabelle di ricerca del vocabolario.
- Incorporamento di tabelle di ricerca.
Un modello composito può essere rappresentato come più servizi indipendenti o come un singolo servizio composito. Un servibile può anche corrispondere a una frazione di un modello, ad esempio con una tabella di ricerca di grandi dimensioni suddivisa in molte istanze
Manager
.- Sessione TensorFlow o wrapper attorno ad essi, come
Per inserire tutto questo nel contesto di questo tutorial:
I modelli TensorFlow sono rappresentati da un tipo di servizio:
SavedModelBundle
.SavedModelBundle
è costituito internamente da untensorflow:Session
abbinato ad alcuni metadati su quale grafico viene caricato nella sessione e come eseguirlo per l'inferenza.Esiste una directory del file system contenente un flusso di esportazioni TensorFlow, ciascuna nella propria sottodirectory il cui nome è un numero di versione. La directory esterna può essere considerata come la rappresentazione serializzata del flusso gestibile per il modello TensorFlow servito. Ogni esportazione corrisponde a un servable che può essere caricato.
AspiredVersionsManager
monitora il flusso di esportazione e gestisce il ciclo di vita di tutti i servableSavedModelBundle
in modo dinamico.
TensorflowPredictImpl::Predict
quindi solo:
- Richiede
SavedModelBundle
dal gestore (tramite ServerCore). - Utilizza le
generic signatures
per mappare i nomi dei tensori logici inPredictRequest
ai nomi dei tensori reali e associare i valori ai tensori. - Esegue l'inferenza.
Testare ed eseguire il server
Copia la prima versione dell'esportazione nella cartella monitorata:
mkdir /tmp/monitored
cp -r /tmp/mnist/1 /tmp/monitored
Quindi avvia il 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 &
Il server emetterà messaggi di registro ogni secondo che dicono "Aspirante versione per servable...", il che significa che ha trovato l'esportazione e ne sta monitorando l'esistenza.
Eseguiamo il client con --concurrency=10
. Ciò invierà richieste simultanee al server e quindi attiverà la logica di batching.
tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
--num_tests=1000 --server=127.0.0.1:8500 --concurrency=10
Il che si traduce in un output simile a:
...
Inference error rate: 13.1%
Quindi copiamo la seconda versione dell'esportazione nella cartella monitorata ed eseguiamo nuovamente il test:
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
Il risultato è un output simile a:
...
Inference error rate: 9.5%
Ciò conferma che il tuo server rileva automaticamente la nuova versione e la utilizza per la pubblicazione!