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

כיוון עדין מודל ברט

צפה על TensorFlow.org הפעל ב Google Colab מקור צפה על GitHub מחברת להורדה

בדוגמה זו, נפעל באמצעות כוונון עדין מודל ברט באמצעות חבילת PIP-דגמים tensorflow.

המודל ברט pretrained הדרכה זו מבוססת על זמין גם על Hub TensorFlow , כדי לראות איך להשתמש בה מתייחסים נספח Hub

להכין

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

  • tf-models-nightly הוא חבילת גינה דגם הלילי נוצר אוטומטית על בסיס יומי.
  • PIP יתקין את כל הדגמים ויחסי תלות באופן אוטומטי.
pip install -q tf-nightly
pip install -q tf-models-nightly

תבוא

 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,

אֶמְצָעִי

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

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

אתה יכול לקבל מקודד ברט מאומן מראש מן TensorFlow Hub כאן:

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

הנתונים

לדוגמא זו השתמשנו במערך MRPC הדבק TFDS .

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

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

קורפוס פרפראזה המחקר מיקרוסופט (דולן & ברוקט, 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...

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

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

ברט tokenizer

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

ברט tokenizer בשימוש במדריך זה כתוב Python טהור (זה לא בנוי מתוך Ops TensorFlow). אז אתה לא יכול פשוט תקע אותו לתוך המודל שלך בתור 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

Tokenize משפט:

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

Preprocess הנתונים

הסעיף באופן ידני מעובד בסיס הנתונים לפורמט צפוי ידי המודל.

מערך נתונים זה הוא קטן, ולכן עיבוד מקדים יכול להיעשות בקלות ובמהירות בזיכרון. עבור מערכי נתונים גדולים יותר 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] האסימון, לשרשר tensors מרופט להקים יחידה 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 0x7f82246c0cf8>

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

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 מגדיר את הליבה ברט דגם, אשר מהווה מודל 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 דוגמאות ממערך האימונים. הפלט הוא logits עבור שתי כיתות:

 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)

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

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

ברט מאמצת את האופטימיזציה אדם עם ריקבון משקל (aka " 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 [==============================] - 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>

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

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

 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())
 
[[-1.1238481   0.92107666]
 [ 0.35722053 -0.4061358 ]]

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

נִספָּח

קידוד מחדש במערך גדול

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

זה היה אפשרי רק בגלל 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 ברט על TFHub

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

מבחן להריץ אותו על קבוצה של נתונים:

 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 הוא שהמבנה של שכבות keras פנימי לא ישוחזר. אז זה יותר קשה לבדוק או לשנות את המודל. 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 0x7f81145cb3c8>,
 '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 0x7f813c336fd0>

מבחן להפעיל אותו:

 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.22512403,  0.07213479],
       [-0.21233292,  0.1311737 ]], dtype=float32)

Optimizers ולוחות זמנים

הייעול בשימוש לאמן את הדגם נוצר באמצעות 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 0x7f8115ab5320>]

png

זה, בתורו עטוף 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 0x7f81150c27f0>]

png

ואז ליצור את nlp.optimization.AdamWeightDecay באמצעות הזמנים, מוגדר עבור המודל ברט:

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