Ayuda a proteger la Gran Barrera de Coral con TensorFlow en Kaggle Únete Challenge

Ajuste de un modelo BERT

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar cuaderno Ver modelo TF Hub

En este ejemplo, trabajaremos mediante el ajuste fino de un modelo BERT utilizando el paquete PIP de tensorflow-models.

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

Configuración

Instale el paquete de pip de TensorFlow Model Garden

  • tf-models-official es el paquete estable Modelo jardín. Tenga en cuenta que puede que no incluya los últimos cambios en las tensorflow_models GitHub repo. Para incluir los cambios más recientes, es posible instalar tf-models-nightly , que es la noche paquete Modelo Jardín crean a diario de forma automática.
  • pip instalará todos los modelos y dependencias automáticamente.
pip install -q -U tensorflow-text
pip install -q tf-models-official==2.4.0

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.7/site-packages/pkg_resources/__init__.py:119: PkgResourcesDeprecationWarning: 0.18ubuntu0.18.04.1 is an invalid version and will not be supported in a future release
  PkgResourcesDeprecationWarning,

Recursos

Este directorio contiene la configuración, el vocabulario y un punto de control previamente entrenado que se usa en este tutorial:

gs_folder_bert = "gs://cloud-tpu-checkpoints/bert/v3/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 :

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

Los datos

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

Este conjunto de datos no está configurado para que se pueda introducir directamente en el modelo BERT, por lo que esta sección también se ocupa del preprocesamiento necesario.

Obtén el conjunto de datos de TensorFlow Datasets

El Microsoft Research Paraphrase Corpus (Dolan & Brockett, 2005) es un corpus de pares de oraciones extraídas automáticamente de fuentes de noticias en línea, con anotaciones humanas para determinar si las oraciones en el par son semánticamente equivalentes.

  • Número de etiquetas: 2.
  • Tamaño del conjunto de datos de entrenamiento: 3668.
  • Tamaño del conjunto de datos de evaluación: 408.
  • Longitud máxima de secuencia del conjunto de datos de entrenamiento y evaluación: 128.
glue, info = tfds.load('glue/mrpc', with_info=True,
                       # It's small, load the whole dataset
                       batch_size=-1)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_datasets/core/dataset_builder.py:622: get_single_element (from tensorflow.python.data.experimental.ops.get_single_element) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.get_single_element()`.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_datasets/core/dataset_builder.py:622: get_single_element (from tensorflow.python.data.experimental.ops.get_single_element) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.get_single_element()`.
list(glue.keys())
['train', 'validation', 'test']

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í hay 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 tokenizador BERT

Para ajustar un modelo previamente entrenado, debe asegurarse de que está utilizando exactamente la misma tokenización, vocabulario y mapeo de índices que utilizó durante el entrenamiento.

El tokenizador BERT que se usa en este tutorial está escrito en Python puro (no está construido a partir de operaciones de 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 tokenizador que utilizó el modelo 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

Tokeniza una oración:

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 preprocesó manualmente el conjunto de datos en el formato esperado por el modelo.

Este conjunto de datos es pequeño, por lo que el preprocesamiento se puede realizar rápida 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 oraciones

El modelo espera que sus dos oraciones de entrada se concatenen juntas. 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 tipo de entrada

El modelo espera dos entradas adicionales:

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

La máscara permite al modelo diferenciar limpiamente entre el contenido y el acolchado. 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 0x7fa22c0ecc50>

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 que el token frase 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 0x7fa1cc682490>

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 datos se ha convertido en un diccionario de características y un conjunto de etiquetas. Cada función del 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

Construye el modelo

El primer paso es descargar la configuración del modelo previamente entrenado.

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 tanto el codificador como 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

Ejecútelo en un lote de prueba de datos 10 ejemplos del conjunto de entrenamiento. La salida son los 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.10071337,  0.05758326],
       [ 0.05382514,  0.30056837],
       [ 0.00795104,  0.1443476 ],
       [ 0.03538848, -0.04541291],
       [ 0.0905486 ,  0.3010756 ],
       [-0.09503749,  0.26234314],
       [-0.00378452,  0.19791688],
       [-0.09037939,  0.2433937 ],
       [-0.03041902,  0.07568075],
       [ 0.30516896,  0.08384045]], 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 construye, el codificador se inicializa aleatoriamente. Restaure los pesos del codificador desde el punto de control:

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

Configurar el optimizador

BERT adopta el optimizador de Adán con la decadencia de peso (también conocido como " AdamW "). También emplea un programa de velocidad de aprendizaje que primero 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 .

Entrena el modelo

La métrica es la precisión y usamos la entropía cruzada categórica dispersa como 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 [==============================] - 37s 220ms/step - loss: 0.5972 - accuracy: 0.7034 - val_loss: 0.4941 - val_accuracy: 0.7794
Epoch 2/3
115/115 [==============================] - 24s 211ms/step - loss: 0.4228 - accuracy: 0.8171 - val_loss: 0.4503 - val_accuracy: 0.8137
Epoch 3/3
115/115 [==============================] - 24s 211ms/step - loss: 0.2852 - accuracy: 0.8956 - val_loss: 0.4061 - val_accuracy: 0.8162
<keras.callbacks.History at 0x7fa1884d0b50>

Ahora ejecute el modelo ajustado en un ejemplo personalizado para ver que funciona.

Comience codificando algunos pares de oraciones:

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)
2021-11-12 13:12:55.273917: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as self_attention_layer_call_fn, self_attention_layer_call_and_return_conditional_losses, dropout_layer_call_fn, dropout_layer_call_and_return_conditional_losses, self_attention_layer_norm_layer_call_fn while saving (showing 5 of 900). These functions will not be directly callable after loading.
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.0493996   1.3163755 ]
 [ 0.674296   -0.87380654]]

[[-1.0493993  1.3163751]
 [ 0.6742962 -0.873807 ]]

Apéndice

Recodificar un gran conjunto de datos

Este tutorial recodificó 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 describir qué características del conjunto de datos deben transformarse:

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)

Luego aplique 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))

Cree tf.data.Dataset para capacitación y evaluación

Si necesita modificar la carga de datos, aquí hay un código para comenzar:

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.AUTOTUNE)
  dataset = dataset.map(
      _select_data_from_record,
      num_parallel_calls=tf.data.AUTOTUNE)
  dataset = dataset.batch(batch_size, drop_remainder=is_training)
  dataset = dataset.prefetch(tf.data.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 en 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(f"https://tfhub.dev/tensorflow/{hub_model_name}/3",
                             trainable=True)

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

Pruébelo en un lote de datos:

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

print("Pooled output shape:", result['pooled_output'].shape)
print("Sequence output shape:", result['sequence_output'].shape)
Pooled output shape: (10, 768)
Sequence output shape: (10, 103, 768)

En este punto, sería sencillo agregar usted mismo un encabezado de clasificación.

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

hub_classifier = nlp.modeling.models.BertClassifier(
    bert_encoder,
    num_classes=2,
    dropout_rate=0.1,
    initializer=tf.keras.initializers.TruncatedNormal(
        stddev=0.02))

La única desventaja de cargar este modelo desde TFHub es que la estructura de las capas internas de keras no se restaura. Por eso es más difícil inspeccionar o modificar el modelo. El BertEncoder 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'

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.BertEncoder y nlp.modeling.models.BertClassifier clases. Solo recuerde que si comienza a modificar la arquitectura, es posible que no sea correcto o no sea posible volver a cargar el punto de control entrenado previamente, por lo que deberá volver a capacitarse desde cero.

Construye el codificador:

bert_encoder_config = config_dict.copy()

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

bert_encoder_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': <keras.initializers.initializers_v2.TruncatedNormal at 0x7f9d1011da50>,
 'max_sequence_length': 512,
 'num_layers': 12}
manual_encoder = nlp.modeling.networks.BertEncoder(**bert_encoder_config)

Restaurar los pesos:

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

Prueba ejecutarlo:

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)

Envuélvalo en un clasificador:

manual_classifier = nlp.modeling.models.BertClassifier(
        bert_encoder,
        num_classes=2,
        dropout_rate=bert_encoder_config['dropout_rate'],
        initializer=bert_encoder_config['initializer'])
manual_classifier(my_examples, training=True).numpy()
array([[-0.23401603, -0.3458405 ],
       [ 0.2552695 , -0.28906718]], 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 contenedor de alto nivel configura los programas de tasas de aprendizaje y el optimizador.

El programa de tasa de aprendizaje base que se usa aquí es una disminución lineal a cero durante la ejecución 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 0x7f9d11235d90>]

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 0x7f9d109551d0>]

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