使用 TFBertModel 和 HuggingFace 变压器的 AutoTokenizer 构建模型时出现输入问题

Ger*_*nno 8 keras tensorflow bert-language-model huggingface-transformers huggingface-tokenizers

我正在尝试构建图中所示的模型: 在此输入图像描述

transformers我通过以下方式从 HuggingFace 获得了预训练的 BERT 和相应的分词器:

from transformers import AutoTokenizer, TFBertModel
model_name = "dbmdz/bert-base-italian-xxl-cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
bert = TFBertModel.from_pretrained(model_name)
Run Code Online (Sandbox Code Playgroud)

该模型将被输入一系列意大利推文,并需要确定它们是否具有讽刺意味。

我在构建模型的初始部分时遇到问题,该部分获取输入并将其提供给分词器,以获得可以提供给 BERT 的表示。

我可以在模型构建上下文之外做到这一点:

my_phrase = "Ciao, come va?"
# an equivalent version is tokenizer(my_phrase, other parameters)
bert_input = tokenizer.encode(my_phrase, add_special_tokens=True, return_tensors='tf', max_length=110, padding='max_length', truncation=True) 
attention_mask = bert_input > 0
outputs = bert(bert_input, attention_mask)['pooler_output']
Run Code Online (Sandbox Code Playgroud)

但我在构建执行此操作的模型时遇到了麻烦。以下是构建此类模型的代码(问题出在前 4 行):

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
  encoder_inputs = tokenizer(text_input, return_tensors='tf', add_special_tokens=True, max_length=110, padding='max_length', truncation=True)
  outputs = bert(encoder_inputs)
  net = outputs['pooler_output']
  
  X = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64, return_sequences=True, dropout=0.1, recurrent_dropout=0.1))(net)
  X = tf.keras.layers.Concatenate(axis=-1)([X, input_layer])
  X = tf.keras.layers.MaxPooling1D(20)(X)
  X = tf.keras.layers.SpatialDropout1D(0.4)(X)
  X = tf.keras.layers.Flatten()(X)
  X = tf.keras.layers.Dense(128, activation="relu")(X)
  X = tf.keras.layers.Dropout(0.25)(X)
  X = tf.keras.layers.Dense(2, activation='softmax')(X)

  model = tf.keras.Model(inputs=text_input, outputs = X) 
  
  return model
Run Code Online (Sandbox Code Playgroud)

当我调用创建此模型的函数时,出现此错误:

文本输入必须为str(单个示例)、List[str](批量或单个预标记化示例)或List[List[str]](批量预标记化示例)类型。

我想的一件事是,也许我必须使用tokenizer.batch_encode_plus适用于字符串列表的函数:

my_phrase = "Ciao, come va?"
# an equivalent version is tokenizer(my_phrase, other parameters)
bert_input = tokenizer.encode(my_phrase, add_special_tokens=True, return_tensors='tf', max_length=110, padding='max_length', truncation=True) 
attention_mask = bert_input > 0
outputs = bert(bert_input, attention_mask)['pooler_output']
Run Code Online (Sandbox Code Playgroud)

但我收到此错误:

batch_text_or_text_pairs 必须是一个列表(得到 <class 'keras.engine.keras_tensor.KerasTensor'>)

除了我还没有找到一种方法可以通过快速谷歌搜索将该张量转换为列表之外,我必须以这种方式进出张量流似乎很奇怪。

我还查阅了huggingface的文档,但只有一个使用示例,只有一个短语,它们的作用与我的“模型构建上下文之外”示例类似。

编辑:

我也Lambda用这种方式尝试过 s :

tf.executing_eagerly()

def tokenize_tensor(tensor):
  t = tensor.numpy()
  t = np.array([str(s, 'utf-8') for s in t])
  return tokenizer(t.tolist(), return_tensors='tf', add_special_tokens=True, max_length=110, padding='max_length', truncation=True)

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(1,), dtype=tf.string, name='text')
  
  encoder_inputs = tf.keras.layers.Lambda(tokenize_tensor, name='tokenize')(text_input)
  ...
  
  outputs = bert(encoder_inputs)
Run Code Online (Sandbox Code Playgroud)

但我收到以下错误:

“张量”对象没有属性“numpy”

编辑2:

我还尝试了 @mdaoust 建议的将所有内容包装在 a 中的方法tf.py_function,并收到此错误。

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
  encoder_inputs = tokenizer(text_input, return_tensors='tf', add_special_tokens=True, max_length=110, padding='max_length', truncation=True)
  outputs = bert(encoder_inputs)
  net = outputs['pooler_output']
  
  X = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64, return_sequences=True, dropout=0.1, recurrent_dropout=0.1))(net)
  X = tf.keras.layers.Concatenate(axis=-1)([X, input_layer])
  X = tf.keras.layers.MaxPooling1D(20)(X)
  X = tf.keras.layers.SpatialDropout1D(0.4)(X)
  X = tf.keras.layers.Flatten()(X)
  X = tf.keras.layers.Dense(128, activation="relu")(X)
  X = tf.keras.layers.Dropout(0.25)(X)
  X = tf.keras.layers.Dense(2, activation='softmax')(X)

  model = tf.keras.Model(inputs=text_input, outputs = X) 
  
  return model
Run Code Online (Sandbox Code Playgroud)

eager_py_func() 缺少 1 个必需的位置参数:'Tout'

然后我将 Tout 定义为分词器返回值的类型:

transformers.tokenization_utils_base.BatchEncoding

并得到以下错误:

参数“Tout”的预期数据类型不是 <class 'transformers.tokenization_utils_base.BatchEncoding'>

最后,我通过以下方式解压缩 BatchEncoding 中的值:

class BertPreprocessingLayer(tf.keras.layers.Layer):
  def __init__(self, tokenizer, maxlength):
    super().__init__()
    self._tokenizer = tokenizer
    self._maxlength = maxlength
  
  def call(self, inputs):
    print(type(inputs))
    print(inputs)
    tokenized = tokenizer.batch_encode_plus(inputs, add_special_tokens=True, return_tensors='tf', max_length=self._maxlength, padding='max_length', truncation=True)
    return tokenized

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
  encoder_inputs = BertPreprocessingLayer(tokenizer, 100)(text_input)
  outputs = bert(encoder_inputs)
  net = outputs['pooler_output']
  # ... same as above
Run Code Online (Sandbox Code Playgroud)

并在下面的行中得到一个错误:

tf.executing_eagerly()

def tokenize_tensor(tensor):
  t = tensor.numpy()
  t = np.array([str(s, 'utf-8') for s in t])
  return tokenizer(t.tolist(), return_tensors='tf', add_special_tokens=True, max_length=110, padding='max_length', truncation=True)

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(1,), dtype=tf.string, name='text')
  
  encoder_inputs = tf.keras.layers.Lambda(tokenize_tensor, name='tokenize')(text_input)
  ...
  
  outputs = bert(encoder_inputs)
Run Code Online (Sandbox Code Playgroud)

ValueError:无法获取未知等级的形状的长度。

Ger*_*nno 3

现在,我通过从模型中取出标记化步骤来解决:

def tokenize(sentences, tokenizer):
    input_ids, input_masks, input_segments = [],[],[]
    for sentence in sentences:
        inputs = tokenizer.encode_plus(sentence, add_special_tokens=True, max_length=128, pad_to_max_length=True, return_attention_mask=True, return_token_type_ids=True)
        input_ids.append(inputs['input_ids'])
        input_masks.append(inputs['attention_mask'])
        input_segments.append(inputs['token_type_ids'])        
        
    return np.asarray(input_ids, dtype='int32'), np.asarray(input_masks, dtype='int32'), np.asarray(input_segments, dtype='int32')
Run Code Online (Sandbox Code Playgroud)

该模型采用两个输入,即 tokenize 函数返回的前两个值。

def build_classifier_model():
   input_ids_in = tf.keras.layers.Input(shape=(128,), name='input_token', dtype='int32')
   input_masks_in = tf.keras.layers.Input(shape=(128,), name='masked_token', dtype='int32') 

   embedding_layer = bert(input_ids_in, attention_mask=input_masks_in)[0]
...
   model = tf.keras.Model(inputs=[input_ids_in, input_masks_in], outputs = X)

   for layer in model.layers[:3]:
     layer.trainable = False
   return model
Run Code Online (Sandbox Code Playgroud)

我仍然想知道是否有人有一个解决方案,将标记化步骤集成到模型构建上下文中,以便模型的用户可以简单地向其提供短语来获得预测或训练模型。