我想在我导出的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服务,我想将所有预处理逻辑合并到模型本身(在导出的模型文件中编码) .
Q: How can I do so using the Keras library only?
我发现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: How can I use these Tensorflow pre-defined pre-processing operations and my Keras layers together to both train and then export the model as a "black box" for use in Tensorflow Serving?
2 回答
我想通了,所以我将在这里回答我自己的问题 .
Here's the gist:
首先,(在单独的代码文件中)我使用Keras仅使用我自己的预处理函数训练模型,导出Keras模型权重文件和我的令牌到索引字典 .
然后,我只复制了Keras模型架构,将输入设置为预处理张量输出,从先前训练的Keras模型加载权重文件,并将其夹在Tensorflow预处理操作和Tensorflow导出器之间 .
Final product:
接受的答案非常有用,但它使用过时的Keras API,如@Qululu提到的,以及过时的TF服务API(导出器),它没有显示如何导出模型以使其输入是原始的tf占位符(相对于Keras model.input,后期预处理) . 以下版本适用于TF v1.4和Keras 2.1.2:
UPDATE 使用Tensorflow进行推理的预处理是CPU操作,如果模型部署在GPU服务器上,则无法有效执行 . GPU失速非常糟糕,吞吐量非常低 . 因此,我们放弃了这一点,以便在客户端进程中进行有效的预处理 .