【问题标题】:Problem with inputs when building a model with TFBertModel and AutoTokenizer from HuggingFace's transformers使用 HuggingFace 的转换器中的 TFBertModel 和 AutoTokenizer 构建模型时出现输入问题
【发布时间】:2021-11-10 17:02:33
【问题描述】:

我正在尝试构建这张图片中所示的模型:

我通过以下方式从 HuggingFace 的 transformers 获得了预训练的 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)

模型将收到一系列意大利推文,并且需要确定它们是否具有讽刺意味。

我在构建模型的初始部分时遇到问题,该部分接受输入并将它们提供给标记器,以便获得可以提供给 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']

但我在构建执行此操作的模型时遇到了麻烦。这是构建这样一个模型的代码(问题出在前 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

当我调用创建这个模型的函数时,我得到了这个错误:

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

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

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

但我收到此错误:

batch_text_or_text_pairs 必须是一个列表(得到

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

我还查看了 huggingface 的 documentation,但只有一个使用示例,只有一个短语,他们所做的与我的“脱离模型构建上下文”示例类似。

编辑:

我也用这种方式尝试过Lambdas:

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)

但我收到以下错误:

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

编辑 2:

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

def py_func_tokenize_tensor(tensor):
  return tf.py_function(tokenize_tensor, [tensor], Tout=[tf.int32, tf.int32, tf.int32])

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

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

transformers.tokenization_utils_base.BatchEncoding

并得到以下错误:

参数“Tout”的预期数据类型不是

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

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

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

...
outputs = bert(encoder_inputs)

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

【问题讨论】:

    标签: tensorflow keras huggingface-transformers bert-language-model huggingface-tokenizers


    【解决方案1】:

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

    上述错误的解决方法:

    只需使用text_input = 'text'

    而不是

    text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')

    【讨论】:

    • 这可能是一个单独的问题,有更多描述:)
    • 我刚刚发现这不是错误的解决方案,这样您只需将字符串“text”传递给标记器,它将对其进行标记
    【解决方案2】:

    看起来这与 TensorFlow 不兼容。

    https://huggingface.co/dbmdz/bert-base-italian-xxl-cased#model-weights

    Currently only PyTorch-Transformers compatible weights are available. If you need access to TensorFlow checkpoints, please raise an issue!
    

    但请记住,如果您不使用 keras 的功能模型 API,有些事情会更容易。这就是got <class 'keras.engine.keras_tensor.KerasTensor'> 所抱怨的。

    尝试传递tf.Tensor 看看是否有效。 尝试时会发生什么:

    text_input = tf.constant('text')
    

    尝试将您的模型编写为模型的子类。

    【讨论】:

    • 这根本不是问题,我已经有了 tensorflow 版本,可以在前两个 sn-ps 代码中看到。当他们回答我here时,我得到了张量流模型。我的问题是标记器只接受字符串或字符串列表而不接受张量。我尝试从张量中提取字符串,但未成功。
    • 我明白你的意思。让我再试一次。
    【解决方案3】:

    是的,我的第一个答案是错误的。

    问题在于 tensorflow 有两种类型的张量。渴望张量(这些有一个值)。以及没有值的“符号张量”或“图张量”,仅用于建立计算。

    您的 tokenize_tensor 函数需要一个急切的张量。只有急切的张量才有.numpy() 方法。

    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)
    

    但是 keras Input 是一个符号张量。

    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)
    
    

    要解决此问题,您可以使用tf.py_function。它在图形模式下工作,并在图形执行时调用带有急切张量的包装函数,而不是在构建图形时将图形张量传递给它。

    def py_func_tokenize_tensor(tensor):
      return tf.py_function(tokenize_tensor, [tensor])
    
    ...
    
    encoder_inputs = tf.keras.layers.Lambda(py_func_tokenize_tensor, name='tokenize')(text_input)
    

    【讨论】:

    • 使用这种方法我遇到了Tout的定义问题
    【解决方案4】:

    现在我通过从模型中移除标记化步骤来解决:

    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')
    

    模型接受两个输入,它们是标记化函数返回的前两个值。

    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
    

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

    【讨论】:

      猜你喜欢
      • 2021-09-29
      • 2020-09-29
      • 1970-01-01
      • 2021-03-31
      • 2022-01-06
      • 2022-11-19
      • 2021-08-04
      • 1970-01-01
      • 2020-02-25
      相关资源
      最近更新 更多