【问题标题】:how to add text preprocessing tokenization step into Tensorflow model如何将文本预处理标记化步骤添加到 TensorFlow 模型中
【发布时间】:2022-08-03 07:42:48
【问题描述】:

我有一个 TensorFlow 模型 SavedModel,其中包括 saved_model.pbvariables 文件夹。预处理步骤尚未合并到此模型中,这就是为什么我需要在将数据提供给模型以进行预测方面之前进行预处理(标记化等)。

我正在寻找一种可以将预处理步骤合并到模型中的方法。我见过herehere 的例子,但它们是图像数据。

只是为了了解一下训练部分是如何完成的,这是我们训练的部分代码(如果您需要实现我在这里使用的功能,请告诉我(我没有包括它来制作我的问题更容易理解))

训练:

processor = IntentProcessor(FLAGS.data_path, FLAGS.test_data_path,
                            FLAGS.test_proportion, FLAGS.seed, FLAGS.do_early_stopping)


bert_config = modeling.BertConfig.from_json_file(FLAGS.bert_config_file)
tokenizer = tokenization.FullTokenizer(
    vocab_file=FLAGS.vocab_file, do_lower_case=FLAGS.do_lower_case)

run_config = tf.estimator.RunConfig(
    model_dir=FLAGS.output_dir,
    save_checkpoints_steps=FLAGS.save_checkpoints_steps)

train_examples = None
num_train_steps = None
num_warmup_steps = None
if FLAGS.do_train:
    train_examples = processor.get_train_examples()
    num_iter_per_epoch = int(len(train_examples) / FLAGS.train_batch_size)
    num_train_steps = num_iter_per_epoch * FLAGS.num_train_epochs
    num_warmup_steps = int(num_train_steps * FLAGS.warmup_proportion)
    run_config = tf.estimator.RunConfig(
        model_dir=FLAGS.output_dir,
        save_checkpoints_steps=num_iter_per_epoch)

best_temperature = 1.0  # Initiate the best T value as 1.0 and will
# update this during the training

model_fn = model_fn_builder(
    bert_config=bert_config,
    num_labels=len(processor.le.classes_),
    init_checkpoint=FLAGS.init_checkpoint,
    learning_rate=FLAGS.learning_rate,
    num_train_steps=num_train_steps,
    num_warmup_steps=num_warmup_steps,
    best_temperature=best_temperature,
    seed=FLAGS.seed)

estimator = tf.estimator.Estimator(
    model_fn=model_fn,
    config=run_config)
# add parameters by passing a prams variable

if FLAGS.do_train:
    train_features = convert_examples_to_features(
        train_examples, FLAGS.max_seq_length, tokenizer)
    train_labels = processor.get_train_labels()
    train_input_fn = input_fn_builder(
        features=train_features,
        is_training=True,
        batch_size=FLAGS.train_batch_size,
        seed=FLAGS.seed,
        labels=train_labels
    )
    estimator.train(input_fn=train_input_fn, max_steps=num_train_steps)

这是我用于训练的预处理:

LABEL_LIST = [\'negative\', \'neutral\', \'positive\']
INTENT_MAP = {i: LABEL_LIST[i] for i in range(len(LABEL_LIST))}
BATCH_SIZE = 1
MAX_SEQ_LEN = 70
def convert_examples_to_features(texts, max_seq_length, tokenizer):
    \"\"\"Loads a data file into a list of InputBatchs.
       texts is the list of input text
    \"\"\"
    features = {}
    input_ids_list = []
    input_mask_list = []
    segment_ids_list = []

    for (ex_index, text) in enumerate(texts):
        tokens_a = tokenizer.tokenize(str(text))
        # Account for [CLS] and [SEP] with \"- 2\"
        if len(tokens_a) > max_seq_length - 2:
            tokens_a = tokens_a[0:(max_seq_length - 2)]
        tokens = []
        segment_ids = []
        tokens.append(\"[CLS]\")
        segment_ids.append(0)
        for token in tokens_a:
            tokens.append(token)
            segment_ids.append(0)
        tokens.append(\"[SEP]\")
        segment_ids.append(0)

        input_ids = tokenizer.convert_tokens_to_ids(tokens)
        # print(tokens)

        # The mask has 1 for real tokens and 0 for padding tokens. Only real
        # tokens are attended to.
        input_mask = [1] * len(input_ids)

        # Zero-pad up to the sequence length.
        while len(input_ids) < max_seq_length:
            input_ids.append(0)
            input_mask.append(0)
            segment_ids.append(0)

        assert len(input_ids) == max_seq_length
        assert len(input_mask) == max_seq_length
        assert len(segment_ids) == max_seq_length

        input_ids_list.append(input_ids)
        input_mask_list.append(input_mask)
        segment_ids_list.append(segment_ids)

    features[\'input_ids\'] = np.asanyarray(input_ids_list)
    features[\'input_mask\'] = np.asanyarray(input_mask_list)
    features[\'segment_ids\'] = np.asanyarray(segment_ids_list)

    # tf.data.Dataset.from_tensor_slices needs to pass numpy array not
    # tensor, or the tensor graph (shape) should match

    return features


和推理是这样的:

def inference(texts,MODEL_DIR, VOCAB_FILE):
    if not isinstance(texts, list):
        texts = [texts]
    tokenizer = FullTokenizer(vocab_file=VOCAB_FILE, do_lower_case=False)
    features = convert_examples_to_features(texts, MAX_SEQ_LEN, tokenizer)

    predict_fn = predictor.from_saved_model(MODEL_DIR)
    response = predict_fn(features)
    #print(response)
    return get_sentiment(response)

def preprocess(texts):
    if not isinstance(texts, list):
        texts = [texts]
    tokenizer = FullTokenizer(vocab_file=VOCAB_FILE, do_lower_case=False)
    features = convert_examples_to_features(texts, MAX_SEQ_LEN, tokenizer)

    return features

def get_sentiment(response):
    idx = response[\'intent\'].tolist()
    print(idx)
    print(INTENT_MAP.get(idx[0]))
    outputs = []
    for i in range(0, len(idx)):
        outputs.append({
            \"sentiment\": INTENT_MAP.get(idx[i]),
            \"confidence\": response[\'prob\'][i][idx[i]]
        })
    return outputs

    sentence = \'The movie is ok\'
    inference(sentence, args.model_path, args.vocab_path)

这是model_fn_builder的实现:

def model_fn_builder(bert_config, num_labels, init_checkpoint, learning_rate,
                     num_train_steps, num_warmup_steps, best_temperature, seed):
    \"\"\"Returns multi-intents `model_fn` closure for Estimator\"\"\"

    def model_fn(features, labels, mode,
                 params):  # pylint: disable=unused-argument
        \"\"\"The `model_fn` for Estimator.\"\"\"

        tf.logging.info(\"*** Features ***\")
        for name in sorted(features.keys()):
            tf.logging.info(
                \"  name = %s, shape = %s\" % (name, features[name].shape))

        input_ids = features[\"input_ids\"]
        input_mask = features[\"input_mask\"]
        segment_ids = features[\"segment_ids\"]

        is_training = (mode == tf.estimator.ModeKeys.TRAIN)

        (total_loss, per_example_loss, logits) = create_intent_model(
            bert_config, is_training, input_ids, input_mask, segment_ids,
            labels, num_labels, mode, seed)

        tvars = tf.trainable_variables()

        initialized_variable_names = None
        if init_checkpoint:
            (assignment_map,
             initialized_variable_names) = \\
                modeling.get_assignment_map_from_checkpoint(
                    tvars, init_checkpoint)

            tf.train.init_from_checkpoint(init_checkpoint, assignment_map)

        tf.logging.info(\"**** Trainable Variables ****\")
        for var in tvars:
            init_string = \"\"
            if var.name in initialized_variable_names:
                init_string = \", *INIT_FROM_CKPT*\"
            tf.logging.info(\"  name = %s, shape = %s%s\", var.name, var.shape,
                            init_string)

        output_spec = None
        if mode == tf.estimator.ModeKeys.TRAIN:

            train_op = optimization.create_optimizer(
                total_loss, learning_rate, num_train_steps, num_warmup_steps)

            output_spec = tf.estimator.EstimatorSpec(
                mode=mode,
                loss=total_loss,
                train_op=train_op)

        elif mode == tf.estimator.ModeKeys.EVAL:

            def metric_fn(per_example_loss, labels, logits):
                predictions = tf.argmax(logits, axis=-1, output_type=tf.int32)
                accuracy = tf.metrics.accuracy(labels, predictions)
                loss = tf.metrics.mean(per_example_loss)
                return {
                    \"eval_accuracy\": accuracy,
                    \"eval_loss\": loss
                }

            eval_metrics = metric_fn(per_example_loss, labels, logits)
            output_spec = tf.estimator.EstimatorSpec(
                mode=mode,
                loss=total_loss,
                eval_metric_ops=eval_metrics)

        elif mode == tf.estimator.ModeKeys.PREDICT:
            predictions = {
                \'intent\': tf.argmax(logits, axis=-1, output_type=tf.int32),
                \'prob\': tf.nn.softmax(logits / tf.constant(best_temperature)),
                \'logits\': logits
            }
            output_spec = tf.estimator.EstimatorSpec(
                mode=mode,
                predictions=predictions)

        return output_spec

    return model_fn

有很好的文档here,但是,它使用 Keras API。另外,即使使用 Keras API,我也不知道如何在此处合并预处理层。

同样,我的最终目标是将预处理步骤合并到模型构建阶段,以便稍后加载模型时直接将The movie is ok 传递给模型?

我只需要有关如何将预处理层合并到基于函数的代码中的想法。

先谢谢了~

    标签: tensorflow machine-learning deep-learning data-preprocessing


    【解决方案1】:

    您可以按如下方式使用TextVectorization 层。但要完全回答您的问题,我需要知道 model_fn_builder() 函数中的内容。我将展示如何使用 Keras 模型构建 API 做到这一点。

    class BertTextProcessor(tf.keras.layers.Layer):
    
      def __init__(self, max_length):
        super().__init__()
        self.max_length = max_length
        # Here I'm setting any preprocessing to none
        # by default this layer lowers case and remove punctuation
        # i.e. tokens like [CLS] would become cls
        self.vectorizer = tf.keras.layers.TextVectorization(output_sequence_length=max_length, standardize=None)
    
      def call(self, inputs):
    
        inputs = "[CLS] " + inputs + " [SEP]"
        tok_inputs = self.vectorizer(inputs)
    
        return {
            "input_ids": tok_inputs, 
            "input_mask": tf.cast(tok_inputs != 0, 'int32'),
            "segment_ids": tf.zeros_like(tok_inputs)
            }
    
      def adapt(self, data):
        data = "[CLS] " + data + " [SEP]"
        self.vectorizer.adapt(data)
    
      def get_config(self):
        return {
            "max_length": self.max_length
        }
    

    用法,

    input_str = tf.constant(["movie is okay good plot very nice", "terrible movie bad actors not good"])
    
    proc = BertTextProcessor(8, 10)
    # You need to call this so that the vectorizer layer learns the vocabulary
    proc.adapt(input_str)
    print(proc(input_str))
    

    哪个输出,

    {'input_ids': <tf.Tensor: shape=(2, 10), dtype=int64, numpy=
    array([[ 5,  2, 12,  9,  3,  8,  6, 11,  4,  0],
           [ 5,  7,  2, 13, 14, 10,  3,  4,  0,  0]])>, 'input_mask': <tf.Tensor: shape=(2, 10), dtype=int32, numpy=
    array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
           [1, 1, 1, 1, 1, 1, 1, 1, 0, 0]], dtype=int32)>, 'segment_ids': <tf.Tensor: shape=(2, 10), dtype=int64, numpy=
    array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
           [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])>}
    

    您可以使用该层作为 Keras 模型的输入,就像使用任何层一样。

    您还可以使用返回的proc.vectorizer.get_vocabulary() 获取词汇表,

    ['',
     '[UNK]',
     'movie',
     'good',
     '[SEP]',
     '[CLS]',
     'very',
     'terrible',
     'plot',
     'okay',
     'not',
     'nice',
     'is',
     'bad',
     'actors']
    

    【讨论】:

    • 非常感谢您的回复。我会仔细阅读,如果可以的话我会回复你的。但为了回答你的问题,我用model_fn_builder 更新了我的问题。再次感谢您抽出宝贵时间。我找不到任何关于 tensorflow 的简单文档!
    • 是的,略读它我认为这应该以最少的集成工作(虽然我自己从未尝试过)
    • 我出城了,周四会检查你的解决方案。再次非常感谢
    • 再次非常感谢您提供解决方案。我只是注意到赏金尚未分配给您的答案;/。我将运行一个新的并将其分配给您的解决方案。回到问题,我试图将您的解决方案合并到模型中,但老实说不确定如何将该类合并到模型构建中。想知道您是否可以分享我该怎么做?
    • 另外,你的书有章节摘要吗?我想看看涵盖了哪些主题? amazon.com/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2017-05-31
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    相关资源
    最近更新 更多