Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Ajuste de un modelo BERT

Ver en TensorFlow.org Ejecutar en Google Colab Ver código fuente en GitHub Descargar portátil

En este ejemplo, vamos a trabajar a través de puesta a punto un modelo BERT utilizando el paquete PIP tensorflow-modelos.

El modelo BERT pretrained este tutorial se basa en también está disponible en TensorFlow Hub , para ver cómo usarlo refieren al Apéndice Hub

Preparar

Instalar el paquete PIP TensorFlow Modelo Jardín

  • tf-models-nightly es la noche paquete Modelo Jardín crean a diario de forma automática.
  • pip instalará todos los modelos y las dependencias automáticamente.
pip install -q tf-nightly
pip install -q tf-models-nightly

Las importaciones

 import os

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf

import tensorflow_hub as hub
import tensorflow_datasets as tfds
tfds.disable_progress_bar()

from official.modeling import tf_utils
from official import nlp
from official.nlp import bert

# Load the required submodules
import official.nlp.optimization
import official.nlp.bert.bert_models
import official.nlp.bert.configs
import official.nlp.bert.run_classifier
import official.nlp.bert.tokenization
import official.nlp.data.classifier_data_lib
import official.nlp.modeling.losses
import official.nlp.modeling.models
import official.nlp.modeling.networks
 
/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_addons/utils/ensure_tf_install.py:44: UserWarning: You are currently using a nightly version of TensorFlow (2.3.0-dev20200623). 
TensorFlow Addons offers no support for the nightly versions of TensorFlow. Some things might work, some other might not. 
If you encounter a bug, do not file an issue on GitHub.
  UserWarning,

recursos

Este directorio contiene la configuración, el vocabulario y un puesto de control pre-formados utilizado en este tutorial:

 gs_folder_bert = "gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-12_H-768_A-12"
tf.io.gfile.listdir(gs_folder_bert)
 
['bert_config.json',
 'bert_model.ckpt.data-00000-of-00001',
 'bert_model.ckpt.index',
 'vocab.txt']

Usted puede obtener un codificador BERT pre-formados a partir TensorFlow Hub aquí:

 hub_url_bert = "https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/2"
 

Los datos

Para este ejemplo se utilizó el conjunto de datos COLA MRPC de TFDS .

Este conjunto de datos no está configurado de modo que pueda ser alimentado directamente en el modelo BERT, por lo que esta sección también se encarga del procesamiento previo necesario.

Obtener el conjunto de datos a partir de conjuntos de datos TensorFlow

La investigación de Microsoft Paráfrasis Corpus (Dolan y Brockett, 2005) es un corpus de pares de frases extraída de forma automática a partir de fuentes de noticias en línea, con anotaciones para humanos si las oraciones en la pareja son semánticamente equivalentes.

  • Número de etiquetas: 2.
  • Tamaño de la formación de datos: 3668.
  • Tamaño del conjunto de datos de evaluación: 408.
  • la secuencia de longitud máxima de formación y evaluación de datos: 128.
 glue, info = tfds.load('glue/mrpc', with_info=True,
                       # It's small, load the whole dataset
                       batch_size=-1)
 
Downloading and preparing dataset glue/mrpc/1.0.0 (download: 1.43 MiB, generated: Unknown size, total: 1.43 MiB) to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0...

/usr/lib/python3/dist-packages/urllib3/connectionpool.py:860: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
/usr/lib/python3/dist-packages/urllib3/connectionpool.py:860: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
/usr/lib/python3/dist-packages/urllib3/connectionpool.py:860: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)

Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0.incomplete1RTRDK/glue-train.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0.incomplete1RTRDK/glue-validation.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0.incomplete1RTRDK/glue-test.tfrecord
Dataset glue downloaded and prepared to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0. Subsequent calls will reuse this data.

 list(glue.keys())
 
['test', 'train', 'validation']

La info objeto describe el conjunto de datos y sus características:

 info.features
 
FeaturesDict({
    'idx': tf.int32,
    'label': ClassLabel(shape=(), dtype=tf.int64, num_classes=2),
    'sentence1': Text(shape=(), dtype=tf.string),
    'sentence2': Text(shape=(), dtype=tf.string),
})

Las dos clases son:

 info.features['label'].names
 
['not_equivalent', 'equivalent']

Aquí está un ejemplo del conjunto de entrenamiento:

 glue_train = glue['train']

for key, value in glue_train.items():
  print(f"{key:9s}: {value[0].numpy()}")
 
idx      : 1680
label    : 0
sentence1: b'The identical rovers will act as robotic geologists , searching for evidence of past water .'
sentence2: b'The rovers act as robotic geologists , moving on six wheels .'

El señalizador BERT

Para afinar un modelo de pre-formación que necesita para asegurarse de que está utilizando exactamente el mismo tokenización, el vocabulario y la cartografía índice que utilizó durante el entrenamiento.

El señalizador BERT utilizado en este tutorial está escrito en Python puro (No es construido a partir de operaciones TensorFlow). Así que no se puede simplemente conectarlo a su modelo como un keras.layer como se hace con preprocessing.TextVectorization .

El siguiente código reconstruye el tokenizer que fue utilizado por el modelo de base:

 # Set up tokenizer to generate Tensorflow dataset
tokenizer = bert.tokenization.FullTokenizer(
    vocab_file=os.path.join(gs_folder_bert, "vocab.txt"),
     do_lower_case=True)

print("Vocab size:", len(tokenizer.vocab))
 
Vocab size: 30522

Tokenize una frase:

 tokens = tokenizer.tokenize("Hello TensorFlow!")
print(tokens)
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
 
['hello', 'tensor', '##flow', '!']
[7592, 23435, 12314, 999]

Preprocesar los datos

La sección de pre-procesada manualmente el conjunto de datos en el formato esperado por el modelo.

Este conjunto de datos es pequeña, por lo que el preprocesamiento se puede hacer rápidamente y fácilmente en la memoria. Para los conjuntos de datos más grandes del tf_models biblioteca incluye algunas herramientas para el procesamiento previo y re-serialización de un conjunto de datos. Véase el Apéndice: volver a codificar un gran conjunto de datos para obtener más información.

Codificar las sentencias

El modelo espera que sus dos entradas frases que se concatenan juntos. Se espera que esta entrada para comenzar con un [CLS] "Este es un problema de clasificación" token y cada frase debe terminar con un [SEP] "separador" token:

 tokenizer.convert_tokens_to_ids(['[CLS]', '[SEP]'])
 
[101, 102]

Iniciar mediante la codificación de todas las frases mientras añadiendo un [SEP] token y empacarlos en-tensores RAGGED:

 def encode_sentence(s):
   tokens = list(tokenizer.tokenize(s.numpy()))
   tokens.append('[SEP]')
   return tokenizer.convert_tokens_to_ids(tokens)

sentence1 = tf.ragged.constant([
    encode_sentence(s) for s in glue_train["sentence1"]])
sentence2 = tf.ragged.constant([
    encode_sentence(s) for s in glue_train["sentence2"]])
 
 print("Sentence1 shape:", sentence1.shape.as_list())
print("Sentence2 shape:", sentence2.shape.as_list())
 
Sentence1 shape: [3668, None]
Sentence2 shape: [3668, None]

Ahora anteponer un [CLS] token, y concatenar los tensores desiguales para formar una sola input_word_ids tensor para cada ejemplo. RaggedTensor.to_tensor() cero almohadillas a la secuencia más larga.

 cls = [tokenizer.convert_tokens_to_ids(['[CLS]'])]*sentence1.shape[0]
input_word_ids = tf.concat([cls, sentence1, sentence2], axis=-1)
_ = plt.pcolormesh(input_word_ids.to_tensor())
 

png

Máscara y entrada de tipo

El modelo de espera dos entradas adicionales:

  • La máscara de entrada
  • El tipo de entrada

La máscara permite que el modelo para diferenciar limpiamente entre el contenido y el relleno. La máscara tiene la misma forma que los input_word_ids , y contiene un 1 en cualquier lugar los input_word_ids no está relleno.

 input_mask = tf.ones_like(input_word_ids).to_tensor()

plt.pcolormesh(input_mask)
 
<matplotlib.collections.QuadMesh at 0x7f82246c0cf8>

png

El "tipo de entrada" también tiene la misma forma, pero dentro de la región no acolchada, contiene un 0 o un 1 que indica qué frase el token es una parte de.

 type_cls = tf.zeros_like(cls)
type_s1 = tf.zeros_like(sentence1)
type_s2 = tf.ones_like(sentence2)
input_type_ids = tf.concat([type_cls, type_s1, type_s2], axis=-1).to_tensor()

plt.pcolormesh(input_type_ids)
 
<matplotlib.collections.QuadMesh at 0x7f8224668438>

png

Ponlo todo junto

Recoger el código de texto de análisis sintáctico de arriba en una sola función, y aplicarlo a cada división de la glue/mrpc conjunto de datos.

 def encode_sentence(s, tokenizer):
   tokens = list(tokenizer.tokenize(s))
   tokens.append('[SEP]')
   return tokenizer.convert_tokens_to_ids(tokens)

def bert_encode(glue_dict, tokenizer):
  num_examples = len(glue_dict["sentence1"])
  
  sentence1 = tf.ragged.constant([
      encode_sentence(s, tokenizer)
      for s in np.array(glue_dict["sentence1"])])
  sentence2 = tf.ragged.constant([
      encode_sentence(s, tokenizer)
       for s in np.array(glue_dict["sentence2"])])

  cls = [tokenizer.convert_tokens_to_ids(['[CLS]'])]*sentence1.shape[0]
  input_word_ids = tf.concat([cls, sentence1, sentence2], axis=-1)

  input_mask = tf.ones_like(input_word_ids).to_tensor()

  type_cls = tf.zeros_like(cls)
  type_s1 = tf.zeros_like(sentence1)
  type_s2 = tf.ones_like(sentence2)
  input_type_ids = tf.concat(
      [type_cls, type_s1, type_s2], axis=-1).to_tensor()

  inputs = {
      'input_word_ids': input_word_ids.to_tensor(),
      'input_mask': input_mask,
      'input_type_ids': input_type_ids}

  return inputs
 
 glue_train = bert_encode(glue['train'], tokenizer)
glue_train_labels = glue['train']['label']

glue_validation = bert_encode(glue['validation'], tokenizer)
glue_validation_labels = glue['validation']['label']

glue_test = bert_encode(glue['test'], tokenizer)
glue_test_labels  = glue['test']['label']
 

Cada subconjunto de los datos se ha convertido en un diccionario de características, y un conjunto de etiquetas. Cada función en el diccionario de entrada tiene la misma forma y el número de etiquetas debe coincidir:

 for key, value in glue_train.items():
  print(f'{key:15s} shape: {value.shape}')

print(f'glue_train_labels shape: {glue_train_labels.shape}')
 
input_word_ids  shape: (3668, 103)
input_mask      shape: (3668, 103)
input_type_ids  shape: (3668, 103)
glue_train_labels shape: (3668,)

El modelo

Construir el modelo

El primer paso es descargar la configuración del modelo de pre-formados.

 import json

bert_config_file = os.path.join(gs_folder_bert, "bert_config.json")
config_dict = json.loads(tf.io.gfile.GFile(bert_config_file).read())

bert_config = bert.configs.BertConfig.from_dict(config_dict)

config_dict
 
{'attention_probs_dropout_prob': 0.1,
 'hidden_act': 'gelu',
 'hidden_dropout_prob': 0.1,
 'hidden_size': 768,
 'initializer_range': 0.02,
 'intermediate_size': 3072,
 'max_position_embeddings': 512,
 'num_attention_heads': 12,
 'num_hidden_layers': 12,
 'type_vocab_size': 2,
 'vocab_size': 30522}

El config define el núcleo BERT modelo, que es un modelo Keras para predecir las salidas de num_classes desde las entradas con la secuencia de longitud máxima max_seq_length .

Esta función devuelve el codificador y el clasificador.

 bert_classifier, bert_encoder = bert.bert_models.classifier_model(
    bert_config, num_labels=2)
 

El clasificador tiene tres entradas y una salida:

 tf.keras.utils.plot_model(bert_classifier, show_shapes=True, dpi=48)
 

png

Ejecutarlo en un lote de prueba de datos de 10 ejemplos del conjunto de entrenamiento. La salida es la logits para las dos clases:

 glue_batch = {key: val[:10] for key, val in glue_train.items()}

bert_classifier(
    glue_batch, training=True
).numpy()
 
array([[ 0.05488977, -0.26042116],
       [ 0.11358108, -0.09727937],
       [ 0.14350253, -0.2465629 ],
       [ 0.2775127 , -0.09028438],
       [ 0.3606584 , -0.17138724],
       [ 0.3287397 , -0.14672714],
       [ 0.18621178, -0.13080403],
       [ 0.21898738,  0.10716071],
       [ 0.18413854, -0.13491377],
       [ 0.20307963, -0.05396855]], dtype=float32)

El TransformerEncoder en el centro del clasificador de arriba es la bert_encoder .

Inspeccionar el codificador, vemos su pila de Transformer capas conectado a esos mismos tres entradas:

 tf.keras.utils.plot_model(bert_encoder, show_shapes=True, dpi=48)
 

png

Restaurar los pesos del codificador

Cuando se construyó el codificador se inicializa al azar. Restaurar pesos del codificador desde el puesto de control:

 checkpoint = tf.train.Checkpoint(model=bert_encoder)
checkpoint.restore(
    os.path.join(gs_folder_bert, 'bert_model.ckpt')).assert_consumed()
 
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f8242dadc88>

Configurar el optimizador

BERT adopta el optimizador de Adán con la decadencia de peso (también conocido como " AdamW "). También emplea un horario de tasa de aprendizaje que en primer lugar se calienta desde 0 y luego decae a 0.

 # Set up epochs and steps
epochs = 3
batch_size = 32
eval_batch_size = 32

train_data_size = len(glue_train_labels)
steps_per_epoch = int(train_data_size / batch_size)
num_train_steps = steps_per_epoch * epochs
warmup_steps = int(epochs * train_data_size * 0.1 / batch_size)

# creates an optimizer with learning rate schedule
optimizer = nlp.optimization.create_optimizer(
    2e-5, num_train_steps=num_train_steps, num_warmup_steps=warmup_steps)
 

Esto devuelve un AdamWeightDecay Optimizer con el conjunto del programa de tasas de aprendizaje:

 type(optimizer)
 
official.nlp.optimization.AdamWeightDecay

Para ver un ejemplo de cómo personalizar el optimizador y de programación, ver el calendario de apéndice Optimizer .

Entrenar el modelo

La métrica es la precisión y la usamos escasa categórica de entropía cruzada como la pérdida.

 metrics = [tf.keras.metrics.SparseCategoricalAccuracy('accuracy', dtype=tf.float32)]
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

bert_classifier.compile(
    optimizer=optimizer,
    loss=loss,
    metrics=metrics)

bert_classifier.fit(
      glue_train, glue_train_labels,
      validation_data=(glue_validation, glue_validation_labels),
      batch_size=32,
      epochs=epochs)
 
Epoch 1/3
115/115 [==============================] - 25s 218ms/step - loss: 0.7047 - accuracy: 0.6101 - val_loss: 0.5219 - val_accuracy: 0.7181
Epoch 2/3
115/115 [==============================] - 24s 210ms/step - loss: 0.5068 - accuracy: 0.7560 - val_loss: 0.5047 - val_accuracy: 0.7794
Epoch 3/3
115/115 [==============================] - 24s 209ms/step - loss: 0.3812 - accuracy: 0.8332 - val_loss: 0.4839 - val_accuracy: 0.8137

<tensorflow.python.keras.callbacks.History at 0x7f82107c8cf8>

Ahora ejecute el modelo afinado en un ejemplo de medida para ver que funciona.

Iniciar mediante la codificación de algunos pares de frases:

 my_examples = bert_encode(
    glue_dict = {
        'sentence1':[
            'The rain in Spain falls mainly on the plain.',
            'Look I fine tuned BERT.'],
        'sentence2':[
            'It mostly rains on the flat lands of Spain.',
            'Is it working? This does not match.']
    },
    tokenizer=tokenizer)
 

El modelo debe reportar clase 1 "partido" para el primer ejemplo y clase 0 "no-match" para el segundo:

 result = bert_classifier(my_examples, training=False)

result = tf.argmax(result).numpy()
result
 
array([1, 0])
 np.array(info.features['label'].names)[result]
 
array(['equivalent', 'not_equivalent'], dtype='<U14')

Guardar el modelo

A menudo, el objetivo del entrenamiento es un modelo de utilizarlo para algo, así exportar el modelo y luego restaurarla para asegurarse de que funciona.

 export_dir='./saved_model'
tf.saved_model.save(bert_classifier, export_dir=export_dir)
 
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.

Warning:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.

Warning:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.

Warning:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.

INFO:tensorflow:Assets written to: ./saved_model/assets

INFO:tensorflow:Assets written to: ./saved_model/assets

 reloaded = tf.saved_model.load(export_dir)
reloaded_result = reloaded([my_examples['input_word_ids'],
                            my_examples['input_mask'],
                            my_examples['input_type_ids']], training=False)

original_result = bert_classifier(my_examples, training=False)

# The results are (nearly) identical:
print(original_result.numpy())
print()
print(reloaded_result.numpy())
 
[[-1.1238481   0.92107666]
 [ 0.35722053 -0.4061358 ]]

[[-1.1238478   0.9210764 ]
 [ 0.35722044 -0.40613574]]

Apéndice

Volver a codificar un gran conjunto de datos

En este tutorial se vuelven a codificar el conjunto de datos en la memoria, para mayor claridad.

Esto sólo fue posible porque glue/mrpc es un pequeño conjunto de datos. Para hacer frente a grandes conjuntos de datos tf_models biblioteca incluye algunas herramientas para procesar y volver a codificar un conjunto de datos para el entrenamiento eficiente.

El primer paso es para describir qué características del conjunto de datos deben ser transformadas:

 processor = nlp.data.classifier_data_lib.TfdsProcessor(
    tfds_params="dataset=glue/mrpc,text_key=sentence1,text_b_key=sentence2",
    process_text_fn=bert.tokenization.convert_to_unicode)
 

A continuación, aplicar la transformación para generar nuevos archivos TFRecord.

 # Set up output of training and evaluation Tensorflow dataset
train_data_output_path="./mrpc_train.tf_record"
eval_data_output_path="./mrpc_eval.tf_record"

max_seq_length = 128
batch_size = 32
eval_batch_size = 32

# Generate and save training data into a tf record file
input_meta_data = (
    nlp.data.classifier_data_lib.generate_tf_record_from_data_file(
      processor=processor,
      data_dir=None,  # It is `None` because data is from tfds, not local dir.
      tokenizer=tokenizer,
      train_data_output_path=train_data_output_path,
      eval_data_output_path=eval_data_output_path,
      max_seq_length=max_seq_length))
 

Finalmente crear tf.data tuberías de entrada procedentes de los archivos TFRecord:

 training_dataset = bert.run_classifier.get_dataset_fn(
    train_data_output_path,
    max_seq_length,
    batch_size,
    is_training=True)()

evaluation_dataset = bert.run_classifier.get_dataset_fn(
    eval_data_output_path,
    max_seq_length,
    eval_batch_size,
    is_training=False)()

 

El resultante tf.data.Datasets retorno (features, labels) pares, como se esperaba por keras.Model.fit :

 training_dataset.element_spec
 
({'input_word_ids': TensorSpec(shape=(32, 128), dtype=tf.int32, name=None),
  'input_mask': TensorSpec(shape=(32, 128), dtype=tf.int32, name=None),
  'input_type_ids': TensorSpec(shape=(32, 128), dtype=tf.int32, name=None)},
 TensorSpec(shape=(32,), dtype=tf.int32, name=None))

Crear tf.data.Dataset para la formación y evaluación

Si es necesario modificar la carga de datos Aquí hay un código para empezar:

 def create_classifier_dataset(file_path, seq_length, batch_size, is_training):
  """Creates input dataset from (tf)records files for train/eval."""
  dataset = tf.data.TFRecordDataset(file_path)
  if is_training:
    dataset = dataset.shuffle(100)
    dataset = dataset.repeat()

  def decode_record(record):
    name_to_features = {
      'input_ids': tf.io.FixedLenFeature([seq_length], tf.int64),
      'input_mask': tf.io.FixedLenFeature([seq_length], tf.int64),
      'segment_ids': tf.io.FixedLenFeature([seq_length], tf.int64),
      'label_ids': tf.io.FixedLenFeature([], tf.int64),
    }
    return tf.io.parse_single_example(record, name_to_features)

  def _select_data_from_record(record):
    x = {
        'input_word_ids': record['input_ids'],
        'input_mask': record['input_mask'],
        'input_type_ids': record['segment_ids']
    }
    y = record['label_ids']
    return (x, y)

  dataset = dataset.map(decode_record,
                        num_parallel_calls=tf.data.experimental.AUTOTUNE)
  dataset = dataset.map(
      _select_data_from_record,
      num_parallel_calls=tf.data.experimental.AUTOTUNE)
  dataset = dataset.batch(batch_size, drop_remainder=is_training)
  dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)
  return dataset
 
 # Set up batch sizes
batch_size = 32
eval_batch_size = 32

# Return Tensorflow dataset
training_dataset = create_classifier_dataset(
    train_data_output_path,
    input_meta_data['max_seq_length'],
    batch_size,
    is_training=True)

evaluation_dataset = create_classifier_dataset(
    eval_data_output_path,
    input_meta_data['max_seq_length'],
    eval_batch_size,
    is_training=False)
 
 training_dataset.element_spec
 
({'input_word_ids': TensorSpec(shape=(32, 128), dtype=tf.int64, name=None),
  'input_mask': TensorSpec(shape=(32, 128), dtype=tf.int64, name=None),
  'input_type_ids': TensorSpec(shape=(32, 128), dtype=tf.int64, name=None)},
 TensorSpec(shape=(32,), dtype=tf.int64, name=None))

TFModels BERT TFHub

Usted puede obtener el modelo BERT fuera de la plataforma de TFHub . No sería difícil añadir una cabeza de la clasificación en la parte superior de esta hub.KerasLayer

 # Note: 350MB download.
import tensorflow_hub as hub
hub_encoder = hub.KerasLayer(hub_url_bert, trainable=True)

print(f"The Hub encoder has {len(hub_encoder.trainable_variables)} trainable variables")
 
The Hub encoder has 199 trainable variables

Prueba de funcionamiento en una serie de datos:

 result = hub_encoder(
    inputs=[glue_train['input_word_ids'][:10],
            glue_train['input_mask'][:10],
            glue_train['input_type_ids'][:10],],
    training=False,
)

print("Pooled output shape:", result[0].shape)
print("Sequence output shape:", result[1].shape)
 
Pooled output shape: (10, 768)
Sequence output shape: (10, 103, 768)

En este punto sería sencillo añadir una cabeza de la clasificación a sí mismo.

El bert_models.classifier_model función también se puede construir un clasificador en el codificador de TensorFlow Hub:

 hub_classifier, hub_encoder = bert.bert_models.classifier_model(
    # Caution: Most of `bert_config` is ignored if you pass a hub url.
    bert_config=bert_config, hub_module_url=hub_url_bert, num_labels=2)
 

La única desventaja de cargar este modelo de TFHub es que la estructura de capas Keras internos no se restaura. Por lo que es más difícil de inspeccionar o modificar el modelo. El TransformerEncoder modelo es ahora una sola capa:

 tf.keras.utils.plot_model(hub_classifier, show_shapes=True, dpi=64)
 

png

 try:
  tf.keras.utils.plot_model(hub_encoder, show_shapes=True, dpi=64)
  assert False
except Exception as e:
  print(f"{type(e).__name__}: {e}")
 
AttributeError: 'KerasLayer' object has no attribute 'layers'

la construcción de modelos de bajo nivel

Si se necesita un mayor control sobre la construcción del modelo Vale la pena señalar que el classifier_model función utilizada anteriormente en realidad es sólo una envoltura fina sobre las nlp.modeling.networks.TransformerEncoder y nlp.modeling.models.BertClassifier clases. Sólo recuerde que si usted comienza a modificar la arquitectura puede que no sea correcta o posible para recargar el puesto de control pre-formados por lo que tendrá que volver a entrenar a partir de cero.

Construir el codificador:

 transformer_config = config_dict.copy()

# You need to rename a few fields to make this work:
transformer_config['attention_dropout_rate'] = transformer_config.pop('attention_probs_dropout_prob')
transformer_config['activation'] = tf_utils.get_activation(transformer_config.pop('hidden_act'))
transformer_config['dropout_rate'] = transformer_config.pop('hidden_dropout_prob')
transformer_config['initializer'] = tf.keras.initializers.TruncatedNormal(
          stddev=transformer_config.pop('initializer_range'))
transformer_config['max_sequence_length'] = transformer_config.pop('max_position_embeddings')
transformer_config['num_layers'] = transformer_config.pop('num_hidden_layers')

transformer_config
 
{'hidden_size': 768,
 'intermediate_size': 3072,
 'num_attention_heads': 12,
 'type_vocab_size': 2,
 'vocab_size': 30522,
 'attention_dropout_rate': 0.1,
 'activation': <function official.modeling.activations.gelu.gelu(x)>,
 'dropout_rate': 0.1,
 'initializer': <tensorflow.python.keras.initializers.initializers_v2.TruncatedNormal at 0x7f81145cb3c8>,
 'max_sequence_length': 512,
 'num_layers': 12}
 manual_encoder = nlp.modeling.networks.TransformerEncoder(**transformer_config)
 

Restaurar los pesos:

 checkpoint = tf.train.Checkpoint(model=manual_encoder)
checkpoint.restore(
    os.path.join(gs_folder_bert, 'bert_model.ckpt')).assert_consumed()
 
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f813c336fd0>

Prueba de funcionamiento es el siguiente:

 result = manual_encoder(my_examples, training=True)

print("Sequence output shape:", result[0].shape)
print("Pooled output shape:", result[1].shape)
 
Sequence output shape: (2, 23, 768)
Pooled output shape: (2, 768)

Envolverlo en un clasificador:

 manual_classifier = nlp.modeling.models.BertClassifier(
        bert_encoder,
        num_classes=2,
        dropout_rate=transformer_config['dropout_rate'],
        initializer=tf.keras.initializers.TruncatedNormal(
          stddev=bert_config.initializer_range))
 
 manual_classifier(my_examples, training=True).numpy()
 
array([[-0.22512403,  0.07213479],
       [-0.21233292,  0.1311737 ]], dtype=float32)

Optimizadores y horarios

El optimizador utilizado para entrenar el modelo se ha creado usando el nlp.optimization.create_optimizer función:

 optimizer = nlp.optimization.create_optimizer(
    2e-5, num_train_steps=num_train_steps, num_warmup_steps=warmup_steps)
 

Ese alto nivel envoltorio establece el programa de tarifas de aprendizaje y el optimizador.

El programa de tasas de aprendizaje de base se utiliza aquí es un decaimiento lineal a cero durante el recorrido de entrenamiento:

 epochs = 3
batch_size = 32
eval_batch_size = 32

train_data_size = len(glue_train_labels)
steps_per_epoch = int(train_data_size / batch_size)
num_train_steps = steps_per_epoch * epochs
 
 decay_schedule = tf.keras.optimizers.schedules.PolynomialDecay(
      initial_learning_rate=2e-5,
      decay_steps=num_train_steps,
      end_learning_rate=0)

plt.plot([decay_schedule(n) for n in range(num_train_steps)])
 
[<matplotlib.lines.Line2D at 0x7f8115ab5320>]

png

Esto, a su vez, está envuelto en una WarmUp horario que aumenta linealmente la tasa de aprendizaje al valor objetivo durante el primer 10% de la formación:

 warmup_steps = num_train_steps * 0.1

warmup_schedule = nlp.optimization.WarmUp(
        initial_learning_rate=2e-5,
        decay_schedule_fn=decay_schedule,
        warmup_steps=warmup_steps)

# The warmup overshoots, because it warms up to the `initial_learning_rate`
# following the original implementation. You can set
# `initial_learning_rate=decay_schedule(warmup_steps)` if you don't like the
# overshoot.
plt.plot([warmup_schedule(n) for n in range(num_train_steps)])
 
[<matplotlib.lines.Line2D at 0x7f81150c27f0>]

png

A continuación, cree el nlp.optimization.AdamWeightDecay uso de ese horario, configurado para el modelo BERT:

 optimizer = nlp.optimization.AdamWeightDecay(
        learning_rate=warmup_schedule,
        weight_decay_rate=0.01,
        epsilon=1e-6,
        exclude_from_weight_decay=['LayerNorm', 'layer_norm', 'bias'])