问题描述
我在tf.keras模型的输出层之后的一层Lambda中使用StaticHashTable
。实际上,这很简单:我有一个文本分类模型,并且要添加一个简单的lambda层,该层使用model.output
并将model_id转换为更通用的标签。我可以使用model.save(...作为H5格式..)保存此版本的模型,而不会出现任何问题,并且可以将其加载回并使用而不会出现任何问题。
问题是,当我尝试导出TF2.2.0模型以进行TF服务时,我找不到导出方法。这是我可以使用TF1.X或TF2.X + tf.compat.v1.disable_eager_execution()
tf.compat.v1.disable_eager_execution()
version = 1
name = 'tmp_model'
export_path = f'/opt/tf_serving/{name}/{version}'
builder = saved_model_builder.SavedModelBuilder(export_path)
model_signature = tf.compat.v1.saved_model.predict_signature_def(
inputs={
'input': model.input
},outputs={
'output': model.output
}
)
with tf.compat.v1.keras.backend.get_session() as sess:
builder.add_Meta_graph_and_variables(
sess=sess,tags=[tf.compat.v1.saved_model.tag_constants.SERVING],signature_def_map={
'predict': model_signature
},# For initializing Hashtables
main_op=tf.compat.v1.tables_initializer()
)
builder.save()
这将以TF1.X格式保存我的模型以进行投放,并且我可以毫无问题地使用它。事情是,我正在使用LSTM层,并且想在GPU上使用我的模型。根据文档,如果禁用急切模式,则无法将LSTM的GPU版本与TF2.2一起使用。而且,如果不经历上面提到的代码,就无法保存用于服务于rt2.2标准和StaticHashTables的模型。
这是我尝试导出在最后一层使用StaticHashTables的TF2.2模型的方式;并给出如下错误:
class MyModule(tf.Module):
def __init__(self,model):
super(MyModule,self).__init__()
self.model = model
@tf.function(input_signature=[tf.TensorSpec(shape=(None,16),dtype=tf.int32,name='input')])
def predict(self,input):
result = self.model(input)
return {"output": result}
version = 1
name = 'tmp_model'
export_path = f'/opt/tf_serving/{name}/{version}'
module = MyModule(model)
tf.saved_model.save(module,export_path,signatures={"predict": module.predict.get_concrete_function()})
错误:
AssertionError: Tried to export a function which references untracked object Tensor("2907:0",shape=(),dtype=resource).
TensorFlow objects (e.g. tf.Variable) captured by functions must be tracked by assigning them to an attribute of a tracked object or assigned to an attribute of the main object directly.
是否有任何建议或者我在使用最终Lambda层中的StaticHashTables
进行TensorFlow服务的导出TF2.2模型时丢失任何内容?
谢谢!
解决方法
我遇到了同样的问题,我找到了通过查找转换创建自定义层并将其添加到模型中的答案。有人将答案放在了stackoverflow上,但是我再也找不到了,所以我将为您介绍。原因是外部的变量和其他元素必须是可跟踪的,我没有找到其他使它们可跟踪的方法,而是创建了一个Custom层,因为它们是可跟踪的,并且在导出时不需要添加其他资产。 / p>
这是代码:
这是在模型之前进行转换的特定自定义层(包括将分词器作为从静态表的查找,然后进行填充:
class VocabLookup(tf.keras.layers.Layer):
def __init__(self,word_index,**kwargs):
self.word_index = word_index
self.vocab = list(word_index.keys())
self.indices = tf.convert_to_tensor(list(word_index.values()),dtype=tf.int64)
vocab_initializer = tf.lookup.KeyValueTensorInitializer(self.vocab,self.indices)
self.table = tf.lookup.StaticHashTable(vocab_initializer,default_value=1)
super(VocabLookup,self).__init__(**kwargs)
def build(self,input_shape):
self.built = True
def sentences_transform(self,tx):
x = tf.strings.lower(tx)
x = tf.strings.regex_replace(x,"[,.:;]"," ")
x = tf.strings.regex_replace(x,"á","a")
x = tf.strings.regex_replace(x,"é","e")
x = tf.strings.regex_replace(x,"í","i")
x = tf.strings.regex_replace(x,"ó","ú","u")
x = tf.strings.regex_replace(x,"ü","Á","É","Í","Ó","o")
x = tf.strings.regex_replace(x,"Ú","Ü","[?¿¡!@#$-_\?+¿{}*/]","")
x = tf.strings.regex_replace(x," +"," ")
x = tf.strings.strip(x)
x = tf.strings.split(x)
x = self.table.lookup(x)
x_as_vector = tf.reshape(x,[-1])
zero_padding = tf.zeros([191] - tf.shape(x_as_vector),dtype=x.dtype)
x = tf.concat([x_as_vector,zero_padding],0)
return x
def call(self,inputs):
x = tf.map_fn(lambda tx: self.sentences_transform(tx),elems = inputs,dtype=tf.int64)
return x
def get_config(self):
return {'word_index': self.word_index}
在我的情况下,我创建了一个图层来接收来自令牌生成器的word_index作为输入。然后,您可以在模型中使用这样的图层:
with open(<tokenizer_path>) as f:
data = json.load(f)
tokenizer = tokenizer_from_json(data)
moderator = load_model(<final model path ('.h5')>)
word_index = tokenizer.word_index
text_bytes = tf.keras.Input(shape=(),name='image_bytes',dtype=tf.string)
x = VocabLookup(word_index)(text_bytes)
output = moderator(x)
model = tf.keras.models.Model(text_bytes,output)
如果进行摘要,则将显示以下内容:
model.summary()
Model: "functional_57"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
image_bytes (InputLayer) [(None,)] 0
_________________________________________________________________
vocab_lookup_60 (VocabLookup (None,None) 0
_________________________________________________________________
sequential_1 (Sequential) (None,1) 1354369
=================================================================
Total params: 1,354,369
Trainable params: 1,369
Non-trainable params: 0
通过此步骤,您最终可以将其另存为TF2服务模型
save_path = <your_serving_model_path>
tf.saved_model.save(model,save_path)