如何保存使用Tensorflow 1.xx中的.meta检查点模型的Tensorflow 2.0模型?

我使用tensorflow 1.15训练了模型并保存为检查点(带有.meta.index.data文件)。

我需要在此图的开头和结尾添加一些其他操作。其中一些操作仅存在于tensorflow 2.0tensorflow_text 2.0中。之后,我想将此模型保存为tensorflow-serving

我想做的是:使用tensorflow 2.0这样保存为.pb文件。

trained_checkpoint_prefix = 'path/to/model'
export_dir = os.path.join('path/to/export','0')

graph = tf.Graph()
with tf.compat.v1.Session(graph=graph) as sess:
    # Restore from checkpoint
    loader = tf.compat.v1.train.import_meta_graph(trained_checkpoint_prefix + '.meta')
    loader.restore(sess,trained_checkpoint_prefix)

    # Export checkpoint to Savedmodel
    builder = tf.compat.v1.saved_model.builder.SavedmodelBuilder(export_dir)

    classification_signature = tf.compat.v1.saved_model.signature_def_utils.build_signature_def(
        inputs={
            'token_indices': get_tensor_info('token_indices_ph:0'),'token_mask': get_tensor_info('token_mask_ph:0'),'y_mask': get_tensor_info('y_mask_ph:0'),},outputs={'probas': get_tensor_info('ner/Softmax:0'),'seq_lengths': get_tensor_info('ner/Sum:0')},method_name='predict',)

    builder.add_meta_graph_and_variables(sess,[tf.saved_model.TRAINING,tf.saved_model.SERVING],strip_default_attrs=True,saver=loader,signature_def_map={'predict': classification_signature}) #,clear_devices=True)
    builder.save()  

此后,我创建了一个tf.keras.Model来加载.pb模型并负责我需要的所有人员:

import os
from pathlib import Path

import tensorflow as tf
import tensorflow_text as tf_text


class bertPipeline(tf.keras.Model):
    def __init__(self):
        super().__init__()

        vocab_file = Path('path/to/vocab.txt')
        vocab = vocab_file.read_text().split('\n')[:-1]
        self.vocab_table = self.create_table(vocab)

        export_dir = 'path/to/pb/model'
        self.model = tf.saved_model.load(export_dir)

        self.bert_tokenizer = bertTokenizer(
            self.vocab_table,max_chars_per_token=15,token_out_type=tf.int64,lower_case=True,)

        self.to_dense = tf_text.keras.layers.ToDense()

    def call(self,texts):
        tokens = self.bert_tokenizer.tokenize(texts)
        tokens = tf.cast(tokens,dtype=tf.int32)

        mask = self.make_mask(tokens)
        token_ids = self.make_token_ids(tokens)

        token_indices = self.to_dense(token_ids)
        token_mask = self.to_dense(tf.ones_like(mask))
        y_mask = self.to_dense(mask)

        res = self.model.signatures['predict'](
            token_indices=token_indices,token_mask=token_mask,y_mask=y_mask,)

        starts_range = tf.range(0,tf.shape(res['seq_lengths'])[0]) * tf.shape(res['probas'])[1]
        row_splits = tf.reshape(
            tf.stack(
                [
                    starts_range,starts_range + res['seq_lengths'],],axis=1,),[-1],)

        row_splits = tf.concat(
            [
                row_splits,tf.expand_dims(tf.shape(res['probas'])[0] * tf.shape(res['probas'])[1],0),axis=0,)

        probas = tf.RaggedTensor.from_row_splits(
            tf.reshape(res['probas'],[-1,2]),row_splits,)[::2]

        probas

        return probas

    def make_mask(self,tokens):
        masked_suff = tf.concat(
            [
                tf.ones_like(tokens[:,:,:1],dtype=tf.int32),tf.zeros_like(tokens[:,1:],axis=-1,)

        joined_mask = self.join_wordpieces(masked_suff)
        return tf.concat(
            [
                tf.zeros_like(joined_mask[:,joined_mask,tf.zeros_like(joined_mask[:,)

    def make_token_ids(self,tokens):
        joined_tokens = self.join_wordpieces(tokens)

        return tf.concat(
            [
                tf.fill(
                    [joined_tokens.nrows(),1],tf.dtypes.cast(
                        self.vocab_table.lookup(tf.constant('[CLS]')),dtype=tf.int32,)
                ),self.join_wordpieces(tokens),tf.fill(
                    [joined_tokens.nrows(),tf.dtypes.cast(
                        self.vocab_table.lookup(tf.constant('[SEP]')),)


    def join_wordpieces(self,wordpieces):
        return tf.RaggedTensor.from_row_splits(
            wordpieces.flat_values,tf.gather(wordpieces.values.row_splits,wordpieces.row_splits))

    def create_table(self,vocab,num_oov=1):
        init = tf.lookup.KeyValueTensorInitializer(
            vocab,tf.range(tf.size(vocab,out_type=tf.int64),dtype=tf.int64),key_dtype=tf.string,value_dtype=tf.int64)
        return tf.lookup.StaticVocabularyTable(init,num_oov,lookup_key_dtype=tf.string)

当我调用此代码时,它可以很好地工作:

bert_pipeline = bertPipeline()
print(bbert_pipeline(["Some test string","another string"]))

---
<tf.RaggedTensor [[[0.17896245419979095,0.8210375308990479],[0.8825045228004456,0.11749550700187683],[0.9141901731491089,0.0858098641037941]],[[0.2768123149871826,0.7231876850128174],[0.9391192197799683,0.060880810022354126]]]>

但是我不知道如何保存它。如果我理解正确,tf.keras.Model请勿将self.modelself.bert_tokenizer视为模型的一部分。如果我致电bert_pipeline.summary(),则没有操作

bert_pipeline.build([])
bert_pipeline.summary()

---
Model: "bert_pipeline_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
to_dense (ToDense)           multiple                  0         
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
_________________________________________________________________

此外,我尝试使用显式tensorflow.compat.v1SessionGraph上运行它,但是在这种情况下,我只是无法正确加载模型。与import tensorflow.compat.v1 as tf相同的代码和tensorflow 1.xx的样板无法初始化某些变量:

# tf.saved_model.load(export_dir) changed to tf.saved_model.load_v2(export_dir) above

import tensorflow.compat.v1 as tf
graph = tf.Graph()
with tf.Session(graph=graph) as sess:
    bert_pipeline = bertPipeline()
    texts = tf.placeholder(tf.string,shape=[None],name='texts')

    res_tensor = bert_pipeline(texts)

    sess.run(tf.tables_initializer())
    sess.run(tf.global_variables_initializer())

    sess.run(res_tensor,feed_dict={texts: ["Some test string","another string"]})

---
FailedPreconditionError                   Traceback (most recent call last)
/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in _do_call(self,fn,*args)
   1364     try:
-> 1365       return fn(*args)
   1366     except errors.OpError as e:

/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in _run_fn(feed_dict,fetch_list,target_list,options,run_metadata)
   1349       return self._call_tf_sessionrun(options,feed_dict,-> 1350                                       target_list,run_metadata)
   1351 

/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in _call_tf_sessionrun(self,run_metadata)
   1442                                             fetch_list,-> 1443                                             run_metadata)
   1444 

FailedPreconditionError: [_Derived_]{{function_node __inference_pruned_77348}} {{function_node __inference_pruned_77348}} Attempting to use uninitialized value bert/encoder/layer_3/attention/self/query/kernel
     [[{{node bert/encoder/layer_3/attention/self/query/kernel/read}}]]
     [[bert_pipeline/StatefulPartitionedCall]]

During handling of the above exception,another exception occurred:

FailedPreconditionError                   Traceback (most recent call last)
<ipython-input-15-5a0a45327337> in <module>
     21     sess.run(tf.global_variables_initializer())
     22 
---> 23     sess.run(res_tensor,"another string"]})
     24 
     25 #     print(res)

/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in run(self,fetches,run_metadata)
    954     try:
    955       result = self._run(None,options_ptr,--> 956                          run_metadata_ptr)
    957       if run_metadata:
    958         proto_data = tf_session.TF_GetBuffer(run_metadata_ptr)

/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in _run(self,handle,run_metadata)
   1178     if final_fetches or final_targets or (handle and feed_dict_tensor):
   1179       results = self._do_run(handle,final_targets,final_fetches,-> 1180                              feed_dict_tensor,run_metadata)
   1181     else:
   1182       results = []

/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in _do_run(self,run_metadata)
   1357     if handle is None:
   1358       return self._do_call(_run_fn,feeds,targets,-> 1359                            run_metadata)
   1360     else:
   1361       return self._do_call(_prun_fn,fetches)

/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/client/session.py in _do_call(self,*args)
   1382                     '\nsession_config.graph_options.rewrite_options.'
   1383                     'disable_meta_optimizer = True')
-> 1384       raise type(e)(node_def,op,message)
   1385 
   1386   def _extend_graph(self):
FailedPreconditionError: [_Derived_]  Attempting to use uninitialized value bert/encoder/layer_3/attention/self/query/kernel
     [[{{node bert/encoder/layer_3/attention/self/query/kernel/read}}]]
     [[bert_pipeline/StatefulPartitionedCall]]

请,如果您有一些想法如何解决我保存图形的方法,或者您知道如何做得更好-请告诉我。谢谢!

liulinjian123 回答:如何保存使用Tensorflow 1.xx中的.meta检查点模型的Tensorflow 2.0模型?

我解决了。首先,我无法使用tf.keras做到这一点。我用

import tensorflow.compat.v1 as tf

除了我使用.meta.index和bla bla checkpoint之外,不使用'.pb'。

我在这里描述的主要内容是:Tensorflow: How to replace a node in a calculation graph?

我制作了2个不同的图,然后像这部分代码一样将它们合并:

def _build_model(self):
    with tf.Graph().as_default() as g_1:
        self.lookup_table = self._make_lookup_table()

        init_table = tf.initialize_all_tables()

        self.bert_tokenizer = BertTokenizer(
            self.lookup_table,max_chars_per_token=15,token_out_type=tf.int64,lower_case=True,)

        self.texts_ph = tf.placeholder(tf.string,shape=(None,),name="texts_ph")  # input

        words_without_name,tokens_int_64 = self.bert_tokenizer.tokenize(self.texts_ph)
        words = words_without_name.to_tensor(default_value='',name='tokens')

        tokens = tf.cast(tokens_int_64,dtype=tf.int32)

        mask = self._make_mask(tokens)
        token_ids = self._make_token_ids(tokens)

        self.token_indices = token_ids.to_tensor(default_value=0,name='token_indices')  # output 1
        self.token_mask = tf.ones_like(mask).to_tensor(default_value=0,name='token_mask') # output 2
        self.y_mask = mask.to_tensor(default_value=0,name='y_mask') # output 3

    with tf.Graph().as_default() as g_2:
        sess = tf.Session()
        path_to_model = 'path/to/model'
        self._load_model(sess,path_to_model)

        token_indices_2 = g_2.get_tensor_by_name('token_indices_ph:0'),token_mask_2 = g_2.get_tensor_by_name('token_mask_ph:0'),y_mask_2 = g_2.get_tensor_by_name('y_mask_ph:0'),probas = g_2.get_tensor_by_name('ner/Softmax:0')
        seq_lengths = g_2.get_tensor_by_name('ner/Sum:0')

        exclude_scopes = ('Optimizer','learning_rate','momentum','EMA/BackupVariables')
        all_vars = variables._all_saveable_objects()
        self.vars_to_save = [var for var in all_vars if all(sc not in var.name for sc in exclude_scopes)]
        self.saver = tf.train.Saver(self.vars_to_save

    g_1_def = g_1.as_graph_def()
    g_2_def = g_2.as_graph_def()

    with tf.Graph().as_default() as g_combined:
        self.texts = tf.placeholder(tf.string,name="texts")

        y1,y2,y3,self.init_table,self.words = tf.import_graph_def(
           g_1_def,input_map={"texts_ph:0": self.texts},return_elements=["token_indices/GatherV2:0","token_mask/GatherV2:0","y_mask/GatherV2:0",'init_all_tables','tokens/GatherV2:0'],name='',)

        self.dense_probas,self.lengths = tf.import_graph_def(
            g_2_def,input_map={"token_indices_ph:0": y1,"token_mask_ph:0": y2,"y_mask_ph:0": y3},return_elements=["ner/Softmax:0","ner/Sum:0"],)

        self.sess = tf.Session(graph=g_combined)
        self.graph = g_combined

        self.sess.run(self.init_table)

        vars_dict_to_save = {v.name[:-2]: g_2.get_tensor_by_name(v.name) for v in self.vars_to_save}
        self.saver.restore(self.sess,path_to_model)

您可能会注意到,我调用self._load_model(sess,path_to_model)来加载模型,使用所需的变量创建saver,然后使用self.saver.save(sess,path_to_model)再次加载模型。需要首先加载才能读取保存的图并可以访问其张量。其次需要使用g_combined合并图在另一个会话中加载权重。我认为有一种方法可以在不两次加载磁盘数据的情况下完成此操作,但是它可以正常工作,我不想破坏它:-)。

更重要的是vars_dict_to_save。需要使用此dict才能在图中的加载权重和张量之间进行映射。

之后,您便拥有了包含所有操作的完整图形,因此您可以这样称呼它:

def __call__(self,texts):
    lengths,words,probs = self.sess.run(
        [self.lengths,self.words,self.dense_probas],feed_dict={
            self.texts: texts
        },)
    return lengths,probs

请注意__call__方法的实现。它使用我通过合并图创建的会话。

一旦您拥有带有已加载权重的完整图表,就可以轻松导出要投放的图表:

def export(self,export_dir):
    with self.graph.as_default():
        builder = tf.saved_model.builder.SavedModelBuilder(export_dir)

        predict_signature = tf.saved_model.signature_def_utils.predict_signature_def(
            inputs={
                'texts': self.texts,},outputs={
                'lengths': self.lengths,'tokens': self.words,'probs': self.dense_probas,)

        builder.add_meta_graph_and_variables(
            self.sess,[tf.saved_model.SERVING],strip_default_attrs=True,signature_def_map={'predict': predict_signature},saver=self.saver,main_op=self.init_table,)
        builder.save()

有一些重要的时刻:  -使用合并的图形.as_default()  -使用与合并图相同的会话。  -使用与合并图中的权重相同的保护程序。  -如果需要初始化表,请添加主main_op

如果能帮助到别人,我会很高兴的。对我来说这不是小事,我花了很多时间使它起作用。

P.S。这段代码中的BertTokenizertensorflow_text的此类类稍有不同,但这与问题无关。

本文链接:https://www.f2er.com/3149629.html

大家都在问