2017-01-16 52 views
11

Chciałbym dołączyć moją niestandardową logikę przetwarzania wstępnego do mojego eksportowanego modelu Keras do użytku w Tensorflow Serving.Dodaj wstępne przetwarzanie Tensorflow do istniejącego modelu Keras (do użytku w obsłudze Tensorflow)

Moje wstępne przetwarzanie wykonuje ciąg tokenizacja i korzysta z zewnętrznego słownika przekonwertować każdy żeton do indeksu dla wejścia do warstwy Osadzanie:

from keras.preprocessing import sequence 

token_to_idx_dict = ... #read from file 

# Custom Pythonic pre-processing steps on input_data 
tokens = [tokenize(s) for s in input_data] 
token_idxs = [[token_to_idx_dict[t] for t in ts] for ts in tokens] 
tokens_padded = sequence.pad_sequences(token_idxs, maxlen=maxlen) 

modelu architektury i szkolenia:

model = Sequential() 
model.add(Embedding(max_features, 128, input_length=maxlen)) 
model.add(LSTM(128, activation='sigmoid')) 
model.add(Dense(n_classes, activation='softmax')) 
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam') 

model.fit(x_train, y_train) 

Ponieważ model będzie używany w Tensorflow Serving, chcę wprowadzić do logiki wszystkie logiki przetwarzania wstępnego (zakodowane w eksportowanym pliku modelu).

P: Jak mogę to zrobić, używając tylko biblioteki Keras?

Znalazłem this guide wyjaśniłem, jak połączyć Keras i Tensorflow. Ale nadal nie jestem pewien, jak wyeksportować wszystko jako jeden model.

Wiem, że Tensorflow ma wbudowany podział struny, file I/O i dictionary lookup operations.

logiczne Wstępne przetwarzanie wykorzystujące operacje Tensorflow:

# Get input text 
input_string_tensor = tf.placeholder(tf.string, shape={1}) 
# Split input text by whitespace 
splitted_string = tf.string_split(input_string_tensor, " ") 
# Read index lookup dictionary 
token_to_idx_dict = tf.contrib.lookup.HashTable(tf.contrib.lookup.TextFileInitializer("vocab.txt", tf.string, 0, tf.int64, 1, delimiter=","), -1) 
# Convert tokens to indexes 
token_idxs = token_to_idx_dict.lookup(splitted_string) 
# Pad zeros to fixed length 
token_idxs_padded = tf.pad(token_idxs, ...) 

Q: Jak mogę używać tych Tensorflow predefiniowanych operacji wstępnego przetwarzania i moje warstw Keras razem zarówno pociągiem, a następnie wyeksportować model jako "czarna skrzynka" do użytku w Tensorflow Serving?

+0

Znaleziono rozwiązanie? –

+0

@OphirYoktan Zobacz poniżej moją odpowiedź. – Qululu

Odpowiedz

7

Wyjaśniałem to, więc zamierzam odpowiedzieć na moje własne pytanie tutaj.

Oto sedno:

Najpierw (w osobnym pliku z kodem) Trenowałem model używając Keras tylko własne funkcje wstępnego przetwarzania, eksportowane model Keras plik wagi i mój token-to-index słownik.

Następnie skopiowałem architekturę modelu Keras, ustawiłem wejście jako wstępnie przetworzone wyjście tensora, wczytałem plik wag z wcześniej wyszkolonego modelu Keras i umieściłem go między operacjami wstępnego przetwarzania Tensorflow a eksporterem Tensorflow .

produkt końcowy:

import tensorflow as tf 
from keras import backend as K 
from keras.models import Sequential, Embedding, LSTM, Dense 
from tensorflow.contrib.session_bundle import exporter 
from tensorflow.contrib.lookup import HashTable, TextFileInitializer 

# Initialize Keras with Tensorflow session 
sess = tf.Session() 
K.set_session(sess) 

# Token to index lookup dictionary 
token_to_idx_path = '...' 
token_to_idx_dict = HashTable(TextFileInitializer(token_to_idx_path, tf.string, 0, tf.int64, 1, delimiter='\t'), 0) 

maxlen = ... 

# Pre-processing sub-graph using Tensorflow operations 
input = tf.placeholder(tf.string, name='input') 
sparse_tokenized_input = tf.string_split(input) 
tokenized_input = tf.sparse_tensor_to_dense(sparse_tokenized_input, default_value='') 
token_idxs = token_to_idx_dict.lookup(tokenized_input) 
token_idxs_padded = tf.pad(token_idxs, [[0,0],[0,maxlen]]) 
token_idxs_embedding = tf.slice(token_idxs_padded, [0,0], [-1,maxlen]) 

# Initialize Keras model 
model = Sequential() 
e = Embedding(max_features, 128, input_length=maxlen) 
e.set_input(token_idxs_embedding) 
model.add(e) 
model.add(LSTM(128, activation='sigmoid')) 
model.add(Dense(num_classes, activation='softmax')) 

# Load weights from previously trained Keras model 
weights_path = '...' 
model.load_weights(weights_path) 

K.set_learning_phase(0) 

# Export model in Tensorflow format 
# (Official tutorial: https://github.com/tensorflow/serving/blob/master/tensorflow_serving/g3doc/serving_basic.md) 
saver = tf.train.Saver(sharded=True) 
model_exporter = exporter.Exporter(saver) 
signature = exporter.classification_signature(input_tensor=model.input, scores_tensor=model.output) 
model_exporter.init(sess.graph.as_graph_def(), default_graph_signature=signature) 
model_dir = '...' 
model_version = 1 
model_exporter.export(model_dir, tf.constant(model_version), sess) 

# Input example 
with sess.as_default(): 
    token_to_idx_dict.init.run() 
    sess.run(model.output, feed_dict={input: ["this is a raw input example"]}) 
+2

FYI, metoda Layer "set_input()" działa tylko w Keras w wersji 1.1.1. Następnie został usunięty. Nie mogę dowiedzieć się, jak ustawić wejście warstwy na tensor Tensorflow w późniejszych wersjach. Jeśli ktoś tak robi, prosimy o komentarz. – Qululu

+1

Cześć @Qululu, w Kerasach 2.0+ możesz automatycznie wywoływać tensor/tensor Tensorflow z modelem/warstwą Keras (tak jak normalnie robisz z warstwami/tensorami Keras, itp.) ... Na przykład zobacz to oficjalna strona: https: //blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html ... Mam nadzieję, że to pomoże! ;) –

+0

Jak wytrenujesz model z preprocessingiem? Jak się nazywa .fit() i jak karmi się symbol zastępczy? –

1

Zaakceptowanych odpowiedź jest bardzo pomocne, jednak używa przestarzałej Keras API jak wspomniano @Qululu i przestarzała TF Serving API (eksporter), a nie pokazuje jak wyeksportować model, aby jego dane wejściowe były oryginalnymi symbolami zastępczymi (w porównaniu do Keras model.input, który jest postprocesowaniem wstępnym). Poniżej znajduje się wersja, która działa dobrze jako TF v1.4 i Keras 2.1.2:

sess = tf.Session() 
K.set_session(sess) 

K._LEARNING_PHASE = tf.constant(0) 
K.set_learning_phase(0) 

max_features = 5000 
max_lens = 500 

dict_table = tf.contrib.lookup.HashTable(tf.contrib.lookup.TextFileInitializer("vocab.txt",tf.string, 0, tf.int64, TextFileIndex.LINE_NUMBER, vocab_size=max_features, delimiter=" "), 0) 

x_input = tf.placeholder(tf.string, name='x_input', shape=(None,)) 
sparse_tokenized_input = tf.string_split(x_input) 
tokenized_input = tf.sparse_tensor_to_dense(sparse_tokenized_input, default_value='') 
token_idxs = dict_table.lookup(tokenized_input) 
token_idxs_padded = tf.pad(token_idxs, [[0,0],[0, max_lens]]) 
token_idxs_embedding = tf.slice(token_idxs_padded, [0,0], [-1, max_lens]) 

model = Sequential() 
model.add(InputLayer(input_tensor=token_idxs_embedding, input_shape=(None, max_lens))) 

...REST OF MODEL... 

model.load_weights("model.h5") 

x_info = tf.saved_model.utils.build_tensor_info(x_input) 
y_info = tf.saved_model.utils.build_tensor_info(model.output) 

prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(inputs={"text": x_info}, outputs={"prediction":y_info}, method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME) 

builder = saved_model_builder.SavedModelBuilder("/path/to/model") 

legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op') 

init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) 
sess.run(init_op) 


# Add the meta_graph and the variables to the builder 
builder.add_meta_graph_and_variables(
    sess, [tag_constants.SERVING], 
    signature_def_map={ 
     signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: 
      prediction_signature, 
    }, 
    legacy_init_op=legacy_init_op) 

builder.save()