דף זה תורגם על ידי Cloud Translation API.
Switch to English

כוונון עדין של דגם BERT

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת ראה מודל רכזת TF

בדוגמה זו, נעבור באמצעות כוונון עדין של מודל BERT באמצעות חבילת PIP של tensorflow-models.

מודל ה- BERT המאומן מראש שעליו מתבססת הדרכה זמין גם ב- TensorFlow Hub , כדי לראות כיצד להשתמש בו עיין בנספח Hub.

להכין

התקן את חבילת ה- TensorFlow דגם הגן

  • tf-models-official היא חבילת Model Garden היציבה. שים לב כי ייתכן שהוא לא יכלול את השינויים האחרונים ב- tensorflow_models github של tensorflow_models . כדי לכלול את השינויים האחרונים, תוכל להתקין את tf-models-nightly , שהיא חבילת ה- Garden Garden הלילית שנוצרת מדי יום באופן אוטומטי.
  • pip יתקין את כל הדגמים והתלות באופן אוטומטי.
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.

יבוא

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

אֶמְצָעִי

ספריה זו מכילה את התצורה, את אוצר המילים ואת נקודת הביקורת שהוכשרה מראש המשמשת במדריך זה:

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

תוכל לקבל מקודד BERT שהוכשר מראש מ- TensorFlow Hub :

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

הנתונים

לדוגמא זו השתמשנו במערך הנתונים GLUE MRPC מ- TFDS .

מערך נתונים זה אינו מוגדר כך שניתן יהיה להזין אותו ישירות למודל ה- BERT, כך שסעיף זה מטפל גם בעיבוד המקדים הדרוש.

קבל את מערך הנתונים ממערכי הנתונים של TensorFlow

ה- Research Paraphrase Corpus של מיקרוסופט (Dolan & Brockett, 2005) הוא קורפוס של צמדי משפטים המופקים אוטומטית ממקורות חדשות מקוונים, עם הערות אנושיות האם המשפטים בצמד שווים מבחינה סמנטית.

  • מספר התוויות: 2.
  • גודל מערך האימונים: 3668.
  • גודל מערך הערכה: 408.
  • אורך רצף מרבי של מערך האימונים וההערכות: 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']

אובייקט info מתאר את מערך הנתונים ותכונותיו:

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

שתי השיעורים הם:

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

הנה דוגמה אחת מערכת האימונים:

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

מסמל ה- BERT

כדי לכוונן את המודל שהוכשר מראש, עליכם להיות בטוחים שאתם משתמשים באותם טוקניזציה, אוצר מילים ומיפוי אינדקסים כמו שהשתמשתם במהלך האימון.

מכשיר ה- BERT המשמש בשימוש במדריך זה כתוב בפייתון טהור (הוא לא בנוי מ- TensorFlow ops). אז אתה לא יכול פשוט לחבר אותו למודל שלך בתור keras.layer כמו שאתה יכול עם preprocessing.TextVectorization .

הקוד הבא בונה מחדש את ה- tokenizer ששימש את מודל הבסיס:

# 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

אסימון משפט:

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

עיבד מראש את הנתונים

הקטע עיבד מראש ידנית את מערך הנתונים לפורמט המצופה על ידי המודל.

מערך נתונים זה קטן, כך שניתן לבצע עיבוד מקדים במהירות ובקלות בזיכרון. עבור מערכי נתונים גדולים יותר, ספריית tf_models כוללת כמה כלים לעיבוד מראש tf_models מחדש של מערך נתונים. ראה נספח: קידוד מחדש של מערך נתונים גדול לפרטים.

קידד את המשפטים

המודל מצפה ששני משפטי הקלט שלו יחוברו יחד. קלט זה צפוי להתחיל באסימון "זוהי בעיית סיווג" [CLS] , וכל משפט אמור להסתיים באסימון "מפריד" [SEP] :

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

התחל בקידוד כל המשפטים תוך כדי הוספת אסימון [SEP] , ואריזתם בטנרטורים מרופטים:

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]

כעת הוסף אסימון [CLS] , input_word_ids את הטנזורים המרופטים כדי ליצור טנסור input_word_ids לכל דוגמה. RaggedTensor.to_tensor() רפידות אפס לרצף הארוך ביותר.

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

מסכה וסוג קלט

המודל מצפה לשתי תשומות נוספות:

  • מסכת הקלט
  • סוג הקלט

המסכה מאפשרת לדגם להבדיל בצורה נקייה בין התוכן לריפוד. המסכה יש את אותה צורה כמו input_word_ids , והיא מכילה 1 בכל מקום שבו input_word_ids אינו מרופד.

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

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

png

גם ל"סוג הקלט "יש אותה צורה, אך בתוך האזור הלא מרופד, מכיל 0 או 1 המציין באיזה משפט האסימון הוא חלק.

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

חברו הכל

אסוף את קוד הניתוח לטקסט לעיל לפונקציה אחת, והחל אותו על כל פיצול של מערך 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']

כל קבוצת משנה של הנתונים הוסבה למילון של תכונות, ולקבוצת תוויות. לכל תכונה במילון הקלט אותה צורה, ומספר התוויות צריך להתאים:

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

המודל

בנה את המודל

השלב הראשון הוא הורדת התצורה עבור המודל שהוכשר מראש.

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}

config מגדירה את הליבה של BERT Model, שהוא מודל Keras לחיזוי תפוקות num_classes מהתשומות עם אורך הרצף המרבי max_seq_length .

פונקציה זו מחזירה את המקודד וגם את המסווג.

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

המסווג כולל שלוש כניסות ופלט אחד:

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

png

הפעל אותו במבחן נתונים של 10 דוגמאות מערך האימונים. הפלט הוא לוגי לשתי הכיתות:

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)

ה- TransformerEncoder במרכז המסווג שלמעלה הוא bert_encoder .

בבדיקת המקודד אנו רואים את ערימת שכבות Transformer המחוברות לאותן שלוש כניסות:

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

png

שחזר את משקולות המקודד

בעת בנייתו מקודד מאותחל באופן אקראי. שחזר את משקולות המקודד ממחסום:

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>

הגדר את האופטימיזציה

BERT מאמצת את האופטימיזציה של אדם עם ירידה במשקל (המכונה " AdamW "). הוא גם משתמש בלוח זמנים של למידה שמתחמם תחילה מ -0 ואז מתפוגג ל -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)

זה מחזיר אופטימיזציה של AdamWeightDecay עם לוח הזמנים של קצב הלמידה שנקבע:

type(optimizer)
official.nlp.optimization.AdamWeightDecay

כדי לראות דוגמה כיצד להתאים אישית את כלי האופטימיזציה ואת לוח הזמנים שלו, ראה נספח לוח הזמנים של כלי האופטימיזציה .

תאמן את המודל

המדד הוא דיוק ואנחנו משתמשים בהצטלבות אנטרופיה דלילה כאובדן.

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>

כעת הפעל את הדגם המכוון לפי דוגמה מותאמת אישית כדי לראות שהוא עובד.

התחל בקידוד כמה זוגות משפטים:

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)

המודל צריך לדווח בכיתה 1 "התאמה" עבור הדוגמה הראשונה ומעמד 0 "אין התאמה" עבור השני:

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

שמור את הדגם

לעיתים קרובות מטרת הכשרת המודל היא להשתמש בו למשהו, אז ייצא את המודל ואז החזיר אותו כדי להיות בטוח שהוא עובד.

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

נִספָּח

קידוד מחדש של מערך נתונים גדול

הדרכה זו קידדת מחדש את מערך הנתונים בזיכרון, לשם הבהרה.

זה היה אפשרי רק מכיוון glue/mrpc הוא מערך נתונים קטן מאוד. כדי להתמודד עם מערכי נתונים גדולים יותר, ספריית tf_models כוללת כמה כלים לעיבוד וקידוד מחדש של מערך נתונים להכשרה יעילה.

השלב הראשון הוא לתאר אילו תכונות של מערך הנתונים יש לשנות:

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)

לאחר מכן החל את השינוי ליצירת קבצי 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))

לבסוף ליצור tf.data צינורות הזנה מאלו קבצים 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)()

tf.data.Datasets המתקבלים מחזירים (features, labels) זוגות, כצפוי על ידי 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))

צור tf.data.Dataset להכשרה והערכה

אם אתה צריך לשנות את טעינת הנתונים הנה קוד כלשהו כדי להתחיל:

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

אתה יכול להוריד את דגם ה- BERT מהמדף מ- TFHub . לא יהיה קשה להוסיף ראש סיווג על גבי 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

נסה להפעיל אותו על מנת נתונים:

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)

בשלב זה יהיה פשוט להוסיף ראש סיווג בעצמך.

הפונקציה bert_models.classifier_model יכולה גם לבנות מסווג על המקודד מ- 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)

החיסרון היחיד בהעמסת דגם זה מ- TFHub הוא שמבנה שכבות הקרס הפנימיות אינו משוחזר. לכן קשה יותר לבדוק או לשנות את המודל. המודל TransformerEncoder הוא כעת שכבה אחת:

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'

בניית מודלים ברמה נמוכה

אם אתה צריך יותר שליטה על בניית המודל השווה את זה וציין כי classifier_model הפונקציה בשימוש קודם לכן הוא באמת רק מעטפת דקה על nlp.modeling.networks.TransformerEncoder ו nlp.modeling.models.BertClassifier הכיתות. רק זכור שאם אתה מתחיל לשנות את הארכיטקטורה יתכן שלא יהיה נכון או אפשרי לטעון מחדש את המחסום שאומן מראש, כך שתצטרך להכשיר מחדש מאפס.

בנה את המקודד:

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)

החזר את המשקולות:

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>

בדוק את זה:

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)

עטפו אותו בסיווג:

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)

מיטוב ולוחות זמנים

האופטימיזציה המשמשת לאימון המודל נוצרה באמצעות הפונקציה nlp.optimization.create_optimizer :

optimizer = nlp.optimization.create_optimizer(
    2e-5, num_train_steps=num_train_steps, num_warmup_steps=warmup_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
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

זה, בתורו, עטוף WarmUp זמנים של WarmUp באופן ליניארי את קצב הלמידה לערך היעד במהלך 10% הראשונים של ההכשרה:

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

לאחר מכן צור את ה- nlp.optimization.AdamWeightDecay באמצעות לוח הזמנים הזה, שהוגדר למודל 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'])