2017-01-16 9 views
11

Я хотел бы включить мою пользовательскую логику предварительной обработки в экспортированную модель Keras для использования в Tensorflow Serving.Добавить предварительную обработку Tensorflow к существующей модели Keras (для использования в сервисе Tensorflow)

Моя предварительная обработка выполняет строку лексемизацию и использует внешний словарь, чтобы преобразовать каждый маркер индекс для ввода вложению слоя:

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) 

Модели архитектуры и обучение:

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) 

Поскольку модель будет использоваться в сервисе Tensorflow, я хочу включить всю логику предварительной обработки в саму модель (закодированную в экспортированном файле модели).

В: Как это сделать, используя только библиотеку Keras?

Я нашел this guide объясняет, как комбинировать Keras и Tensorflow. Но я все еще не уверен, как экспортировать все как одну модель.

Я знаю, что Tensorflow имеет встроенное разделение строк, file I/O и dictionary lookup operations.

Предварительная обработка логики с использованием операции 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: Как я могу использовать эти Tensorflow предопределенные операции предварительной обработки и мои слои Keras вместе как поезд, а затем экспортировать модель как «черный ящик» для использования в Tensorflow Serving?

+0

Найден решение? –

+0

@OphirYoktan См. Мой ответ ниже. – Qululu

ответ

7

Я понял, поэтому я собираюсь ответить на свой вопрос здесь.

Вот суть:

Во-первых, (в отдельном файле кода) Я обучен модель с использованием Keras только со своими собственными функциями предварительной обработки, экспортировать модель Keras файл веса и мой маркер индексации, Словарь.

Затем я скопировал только архитектуру модели Keras, установил вход в качестве предварительно обработанного тензорного выхода, загрузил файл весов из ранее обученной модели Keras и зажал его между операциями предварительной обработки Tensorflow и экспортером Tensorflow ,

Конечный продукт:

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, метод Layer 'set_input()' работает только для Keras версии 1.1.1. После этого он был удален. Не могу понять, как установить вход слоя в тензор Tensorflow в более поздних версиях. Если кто-нибудь это сделает, прокомментируйте. – Qululu

+1

Привет @Qululu, в Keras 2.0+, вы можете автоматически вызывать тензор Tensorflow/placeholder с моделью/слоем Keras (так же, как вы обычно делаете с слоями/тензорами Keras и т. Д.) ... Например, см. Это официальная страница: https: //blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html ... Надеюсь, это поможет! ;) –

+0

Как вы обучаете модель с предварительной обработкой tf? Как вы называете .fit() и как загружается tf placeholder? –

1

Принятый ответ супер полезно, однако он использует устаревшую Keras API, как упоминалось @Qululu и устаревший TF сервировки API (экспортер), и он не показывает как экспортировать модель так, чтобы ее вход был исходным tf-заполнителем (по сравнению с Keras model.input, который после предварительной обработки). Ниже приведен вариант, который хорошо работает с TF v1.4 и 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()