Cette page a été traduite par l'API Cloud Translation.
Switch to English

Affiner un modèle BERT

Voir sur TensorFlow.org Exécuter dans Google Colab Afficher la source sur GitHub Télécharger le carnet Voir le modèle TF Hub

Dans cet exemple, nous allons travailler en affinant un modèle BERT en utilisant le package PIP tensorflow-models.

Le modèle BERT pré-entraîné sur lequel ce didacticiel est basé est également disponible sur TensorFlow Hub , pour savoir comment l'utiliser, reportez-vous à l' annexe Hub.

Installer

Installez le package TensorFlow Model Garden pip

  • tf-models-official est le package stable Model Garden. Notez qu'il peut ne pas inclure les dernières modifications dans le tensorflow_models github tensorflow_models . Pour inclure les dernières modifications, vous pouvez installer tf-models-nightly , qui est le package de nuit Model Garden créé automatiquement quotidiennement.
  • pip installera automatiquement tous les modèles et dépendances.
pip install -q tf-models-official==2.3.0
WARNING: You are using pip version 20.2.3; however, version 20.2.4 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

Importations

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

Ressources

Ce répertoire contient la configuration, le vocabulaire et un point de contrôle pré-entraîné utilisé dans ce didacticiel:

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

Vous pouvez obtenir un encodeur BERT pré-formé auprès de TensorFlow Hub :

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

Les données

Pour cet exemple, nous avons utilisé l' ensemble de données GLUE MRPC de TFDS .

Cet ensemble de données n'est pas configuré pour pouvoir être directement introduit dans le modèle BERT, cette section gère donc également le prétraitement nécessaire.

Récupérer l'ensemble de données des ensembles de données TensorFlow

Le corpus de paraphrase de Microsoft Research (Dolan et Brockett, 2005) est un corpus de paires de phrases extraites automatiquement de sources d'informations en ligne, avec des annotations humaines indiquant si les phrases de la paire sont sémantiquement équivalentes.

  • Nombre d'étiquettes: 2.
  • Taille de l'ensemble de données d'entraînement: 3668.
  • Taille de l'ensemble de données d'évaluation: 408.
  • Durée maximale de la séquence de l'ensemble de données d'entraînement et d'évaluation: 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...
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0.incompleteKZIBN9/glue-train.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0.incompleteKZIBN9/glue-validation.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/glue/mrpc/1.0.0.incompleteKZIBN9/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']

L'objet info décrit le jeu de données et ses caractéristiques:

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),
})

Les deux classes sont:

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

Voici un exemple de l'ensemble d'entraînement:

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

Le tokenizer BERT

Pour affiner un modèle pré-entraîné, vous devez vous assurer que vous utilisez exactement la même tokenisation, vocabulaire et mappage d'index que vous avez utilisé pendant la formation.

Le tokenizer BERT utilisé dans ce didacticiel est écrit en Python pur (il n'est pas construit à partir d'opérations TensorFlow). Vous ne pouvez donc pas simplement le brancher dans votre modèle en tant que keras.layer comme vous pouvez le faire avec preprocessing.TextVectorization .

Le code suivant reconstruit le tokenizer qui a été utilisé par le modèle 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 une phrase:

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

Prétraitez les données

La section a prétraité manuellement le jeu de données dans le format attendu par le modèle.

Cet ensemble de données est petit, le prétraitement peut donc être effectué rapidement et facilement en mémoire. Pour les ensembles de données plus volumineux, la bibliothèque tf_models comprend des outils de prétraitement et de re-sérialisation d'un ensemble de données. Voir l' annexe: recoder un grand ensemble de données pour plus de détails.

Encoder les phrases

Le modèle s'attend à ce que ses deux phrases d'entrée soient concaténées ensemble. Cette entrée doit commencer par un jeton [CLS] «Ceci est un problème de classification», et chaque phrase doit se terminer par un jeton [SEP] «Séparateur»:

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

Commencez par encoder toutes les phrases en ajoutant un jeton [SEP] , et en les regroupant dans des tenseurs déchiquetés:

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]

Ajoutez maintenant un jeton [CLS] et concaténez les tenseurs irréguliers pour former un seul tenseur input_word_ids pour chaque exemple. RaggedTensor.to_tensor() zéro la séquence la plus longue.

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

Masque et type d'entrée

Le modèle attend deux entrées supplémentaires:

  • Le masque de saisie
  • Le type d'entrée

Le masque permet au modèle de différencier proprement entre le contenu et le rembourrage. Le masque a la même forme que les input_word_ids et contient un 1 partout où les input_word_ids ne sont pas de remplissage.

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

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

png

Le "type d'entrée" a également la même forme, mais à l'intérieur de la région non rembourrée, contient un 0 ou un 1 indiquant de quelle phrase le jeton fait partie.

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 0x7fad143c1710>

png

Mets le tout ensemble

Rassemblez le code d'analyse de texte ci-dessus en une seule fonction et appliquez-le à chaque division de l'ensemble de données glue/mrpc .

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

Chaque sous-ensemble des données a été converti en un dictionnaire d'entités et un ensemble d'étiquettes. Chaque fonctionnalité du dictionnaire d'entrée a la même forme et le nombre d'étiquettes doit correspondre:

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

Le modèle

Construisez le modèle

La première étape consiste à télécharger la configuration du modèle pré-entraîné.

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}

La config définit le modèle BERT principal, qui est un modèle Keras pour prédire les sorties de num_classes partir des entrées avec une longueur de séquence maximale max_seq_length .

Cette fonction renvoie à la fois l'encodeur et le classifieur.

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

Le classificateur a trois entrées et une sortie:

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

png

Exécutez-le sur un lot de test de données 10 exemples de l'ensemble d'apprentissage. La sortie est les logits pour les deux classes:

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

bert_classifier(
    glue_batch, training=True
).numpy()
array([[ 0.08382261,  0.34465584],
       [ 0.02057236,  0.24053624],
       [ 0.04930754,  0.1117427 ],
       [ 0.17041089,  0.20810834],
       [ 0.21667874,  0.2840511 ],
       [ 0.02325345,  0.33799925],
       [-0.06198866,  0.13532838],
       [ 0.084592  ,  0.20711854],
       [-0.04323687,  0.17096342],
       [ 0.23759182,  0.16801538]], dtype=float32)

Le TransformerEncoder au centre du classificateur ci - dessus est le bert_encoder .

En inspectant l'encodeur, nous voyons sa pile de couches Transformer connectées à ces trois mêmes entrées:

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

png

Restaurer les poids de l'encodeur

Une fois construit, l'encodeur est initialisé de manière aléatoire. Restaurez les poids de l'encodeur à partir du point de contrôle:

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 0x7fad4580ffd0>

Configurer l'optimiseur

BERT adopte l'optimiseur Adam avec décroissance de poids (aka " AdamW "). Il utilise également un programme de taux d'apprentissage qui s'échauffe d'abord de 0, puis décroît à 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)

Cela renvoie un optimiseur AdamWeightDecay avec le programme de taux d'apprentissage défini:

type(optimizer)
official.nlp.optimization.AdamWeightDecay

Pour voir un exemple de personnalisation de l'optimiseur et de sa planification, consultez l' annexe Planification de l' optimiseur .

Former le modèle

La métrique est la précision et nous utilisons une entropie croisée catégorielle clairsemée comme perte.

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 [==============================] - 26s 222ms/step - loss: 0.6151 - accuracy: 0.6611 - val_loss: 0.5462 - val_accuracy: 0.7451
Epoch 2/3
115/115 [==============================] - 24s 212ms/step - loss: 0.4447 - accuracy: 0.8010 - val_loss: 0.4150 - val_accuracy: 0.8309
Epoch 3/3
115/115 [==============================] - 24s 213ms/step - loss: 0.2830 - accuracy: 0.8964 - val_loss: 0.3697 - val_accuracy: 0.8480

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

Exécutez maintenant le modèle affiné sur un exemple personnalisé pour voir qu'il fonctionne.

Commencez par encoder quelques paires de phrases:

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)

Le modèle doit signaler la classe 1 "correspondance" pour le premier exemple et la classe 0 "non-correspondance" pour le second:

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

Enregistrer le modèle

Souvent, l'objectif de la formation d'un modèle est de l' utiliser pour quelque chose, alors exportez le modèle, puis restaurez-le pour être sûr qu'il fonctionne.

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())
[[-0.95450354  1.1227685 ]
 [ 0.40344787 -0.58954155]]

[[-0.95450354  1.1227684 ]
 [ 0.4034478  -0.5895414 ]]

annexe

Recoder un grand ensemble de données

Ce tutoriel vous a ré-encodé l'ensemble de données en mémoire, pour plus de clarté.

Cela n'a été possible que parce que glue/mrpc est un très petit ensemble de données. Pour gérer des ensembles de données plus tf_models bibliothèque tf_models comprend des outils de traitement et de recodage d'un ensemble de données pour un entraînement efficace.

La première étape consiste à décrire les caractéristiques de l'ensemble de données à transformer:

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)

Appliquez ensuite la transformation pour générer de nouveaux fichiers 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))

Enfin, créez des pipelines d'entrée tf.data partir de ces fichiers 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)()

Les tf.data.Datasets résultants tf.data.Datasets (features, labels) , comme prévu par 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))

Créer tf.data.Dataset pour la formation et l'évaluation

Si vous avez besoin de modifier le chargement des données, voici un code pour vous aider à démarrer:

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 sur TFHub

Vous pouvez obtenir le modèle BERT sur l'étagère de TFHub . Il ne serait pas difficile d'ajouter une tête de classification au-dessus de ce hub.KerasLayer

# Note: 350MB download.
import tensorflow_hub as hub
hub_model_name = "bert_en_uncased_L-12_H-768_A-12" 
hub_encoder = hub.KerasLayer(f"https://tfhub.dev/tensorflow/{hub_model_name}/2",
                             trainable=True)

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

Testez-le sur un lot de données:

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)

À ce stade, il serait simple d'ajouter vous-même une tête de classement.

La fonction bert_models.classifier_model peut également créer un classificateur sur l'encodeur à partir 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)

Le seul inconvénient du chargement de ce modèle à partir de TFHub est que la structure des couches internes de keras n'est pas restaurée. Il est donc plus difficile d'inspecter ou de modifier le modèle. Le modèle TransformerEncoder est désormais une seule couche:

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'

Construction de modèles de bas niveau

Si vous avez besoin d'un plus grand contrôle sur la construction du modèle, il convient de noter que la fonction classifier_model utilisée précédemment n'est en réalité qu'un mince wrapper sur les classes nlp.modeling.networks.TransformerEncoder et nlp.modeling.models.BertClassifier . N'oubliez pas que si vous commencez à modifier l'architecture, il peut ne pas être correct ou possible de recharger le point de contrôle pré-entraîné, vous devrez donc vous recycler à partir de zéro.

Construisez l'encodeur:

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 0x7fac08046e10>,
 'max_sequence_length': 512,
 'num_layers': 12}
manual_encoder = nlp.modeling.networks.TransformerEncoder(**transformer_config)

Restaurez les poids:

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 0x7fabefa596d8>

Testez-le:

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)

Enveloppez-le dans un classificateur:

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.07863025, -0.02940944],
       [ 0.30274656,  0.27299827]], dtype=float32)

Optimiseurs et horaires

L'optimiseur utilisé pour entraîner le modèle a été créé à l'aide de la fonction nlp.optimization.create_optimizer :

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

Ce wrapper de haut niveau configure les calendriers de taux d'apprentissage et l'optimiseur.

Le programme de taux d'apprentissage de base utilisé ici est une décroissance linéaire à zéro au cours de la course d'entraînement:

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

png

Ceci, à son tour, est enveloppé dans un programme d' WarmUp qui augmente linéairement le taux d'apprentissage à la valeur cible sur les 10 premiers% de la formation:

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

png

Créez ensuite le nlp.optimization.AdamWeightDecay utilisant cette planification, configurée pour le modèle 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'])