Google I / O hoạt động trở lại từ ngày 18 đến 20 tháng 5! Đặt chỗ và xây dựng lịch trình của bạn Đăng ký ngay

Tinh chỉnh mô hình BERT

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép Xem mô hình TF Hub

Trong ví dụ này, chúng ta sẽ làm việc thông qua việc tinh chỉnh mô hình BERT bằng cách sử dụng gói PIP tensorflow-models.

Mô hình BERT được đào tạo trước mà hướng dẫn này dựa trên cũng có sẵn trên TensorFlow Hub , để xem cách sử dụng nó, hãy tham khảo Phụ lục Hub

Thiết lập

Cài đặt gói pip TensorFlow Model Garden

  • tf-models-official là gói Model Garden ổn định. Lưu ý rằng nó có thể không bao gồm những thay đổi mới nhất trong repo github tensorflow_models . Để bao gồm các thay đổi mới nhất, bạn có thể cài đặt tf-models-nightly , là gói Model Garden hàng đêm được tạo tự động hàng ngày.
  • pip sẽ tự động cài đặt tất cả các mô hình và phụ thuộc.
pip install -q tf-models-official==2.4.0

Nhập khẩu

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

Tài nguyên

Thư mục này chứa cấu hình, từ vựng và điểm kiểm tra được đào tạo trước được sử dụng trong hướng dẫn này:

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

Bạn có thể nhận bộ mã hóa BERT được đào tạo trước từ TensorFlow Hub :

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

Dữ liệu

Đối với ví dụ này, chúng tôi đã sử dụng tập dữ liệu GLUE MRPC từ TFDS .

Tập dữ liệu này không được thiết lập để nó có thể được đưa trực tiếp vào mô hình BERT, vì vậy phần này cũng xử lý tiền xử lý cần thiết.

Nhận tập dữ liệu từ TensorFlow Datasets

Microsoft Research Paraphrase Corpus (Dolan & Brockett, 2005) là một kho ngữ liệu của các cặp câu được trích xuất tự động từ các nguồn tin tức trực tuyến, với chú thích của con người để biết liệu các câu trong cặp có tương đương về mặt ngữ nghĩa hay không.

  • Số lượng nhãn: 2.
  • Kích thước tập dữ liệu đào tạo: 3668.
  • Kích thước của tập dữ liệu đánh giá: 408.
  • Độ dài trình tự tối đa của tập dữ liệu đào tạo và đánh giá: 128.
glue, info = tfds.load('glue/mrpc', with_info=True,
                       # It's small, load the whole dataset
                       batch_size=-1)
list(glue.keys())
['test', 'train', 'validation']

Đối tượng info mô tả tập dữ liệu và các tính năng của nó:

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

Hai lớp là:

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

Đây là một ví dụ từ tập huấn luyện:

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 tokenizer

Để tinh chỉnh mô hình được đào tạo trước, bạn cần đảm bảo rằng bạn đang sử dụng chính xác mã hóa, từ vựng và ánh xạ chỉ mục như bạn đã sử dụng trong quá trình đào tạo.

BERT tokenizer được sử dụng trong hướng dẫn này được viết bằng Python thuần túy (Nó không được xây dựng từ hoạt động TensorFlow). Vì vậy, bạn không thể chỉ cắm nó vào mô hình của mình dưới dạng keras.layer như bạn có thể làm với preprocessing.TextVectorization keras.layer .

Đoạn mã sau xây dựng lại tokenizer đã được sử dụng bởi mô hình cơ sở:

# 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

Mã hóa một câu:

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

Xử lý trước dữ liệu

Phần này đã xử lý trước tập dữ liệu theo cách thủ công thành định dạng mà mô hình mong đợi.

Bộ dữ liệu này nhỏ, vì vậy việc xử lý trước có thể được thực hiện nhanh chóng và dễ dàng trong bộ nhớ. Đối với các tập dữ liệu lớn hơn, thư viện tf_models bao gồm một số công cụ để xử lý trước và tuần tự hóa lại một tập dữ liệu. Xem Phụ lục: Mã hóa lại một tập dữ liệu lớn để biết chi tiết.

Mã hóa các câu

Mô hình mong đợi hai câu đầu vào của nó được nối với nhau. Đầu vào này dự kiến ​​sẽ bắt đầu bằng mã thông báo [CLS] "Đây là vấn đề phân loại" và mỗi câu phải kết thúc bằng mã thông báo "Dấu phân tách" [SEP] :

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

Bắt đầu bằng cách mã hóa tất cả các câu trong khi gắn mã thông báo [SEP] và đóng gói chúng thành các bộ căng thẳng:

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]

Bây giờ, hãy thêm một mã thông báo [CLS] và nối các tensor rách rưới để tạo thành một tensor input_word_ids duy nhất cho mỗi ví dụ. RaggedTensor.to_tensor() không đệm cho chuỗi dài nhất.

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ặt nạ và loại đầu vào

Mô hình mong đợi hai đầu vào bổ sung:

  • Dấu hiệu nhập
  • Loại đầu vào

Mặt nạ cho phép mô hình phân biệt rõ ràng giữa nội dung và phần đệm. Mặt nạ có cùng hình dạng với input_word_ids và chứa 1 bất kỳ vị trí nào mà input_word_ids không phải là padding.

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

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

png

"Loại đầu vào" cũng có hình dạng tương tự, nhưng bên trong vùng không đệm, có chứa 0 hoặc 1 cho biết mã thông báo là một phần của câu nào.

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

png

Đặt nó tất cả cùng nhau

Thu thập mã phân tích cú pháp văn bản ở trên thành một hàm duy nhất và áp dụng nó cho mỗi phần tách của tập dữ liệu 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']

Mỗi tập hợp con của dữ liệu đã được chuyển đổi thành một từ điển các tính năng và một tập hợp các nhãn. Mỗi tính năng trong từ điển đầu vào có hình dạng giống nhau và số lượng nhãn phải khớp với nhau:

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

Ngươi mâu

Xây dựng mô hình

Bước đầu tiên là tải xuống cấu hình cho mô hình được đào tạo trước.

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 xác định Mô hình BERT cốt lõi, là mô hình Keras để dự đoán kết quả đầu ra của num_classes từ các đầu vào có độ dài trình tự tối đa max_seq_length .

Hàm này trả về cả bộ mã hóa và bộ phân loại.

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

Bộ phân loại có ba đầu vào và một đầu ra:

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

png

Chạy nó trên một lô dữ liệu thử nghiệm 10 ví dụ từ tập huấn luyện. Đầu ra là bản ghi cho hai lớp:

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

bert_classifier(
    glue_batch, training=True
).numpy()
array([[-0.3999117 ,  0.19228943],
       [-0.48039404,  0.49550664],
       [-0.4205317 ,  0.4514861 ],
       [-0.46268317,  0.24971014],
       [-0.24856849,  0.29781285],
       [-0.20492092,  0.33435237],
       [-0.16171221,  0.12575442],
       [-0.17115599,  0.40965632],
       [-0.23386969,  0.41947454],
       [-0.5728958 ,  0.40995434]], dtype=float32)

TransformerEncoder ở trung tâm của trình phân loại ở trên bert_encoder .

Kiểm tra bộ mã hóa, chúng tôi thấy chồng các lớp Transformer của nó được kết nối với ba đầu vào giống nhau đó:

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

png

Khôi phục trọng số của bộ mã hóa

Khi được xây dựng, bộ mã hóa được khởi tạo ngẫu nhiên. Khôi phục trọng số của bộ mã hóa từ điểm kiểm tra:

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

Thiết lập trình tối ưu hóa

BERT sử dụng trình tối ưu hóa Adam với phân rã trọng lượng (hay còn gọi là " AdamW "). Nó cũng sử dụng một lịch trình tốc độ học tập, trước hết ấm lên từ 0 và sau đó giảm xuống 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)

Điều này trả về trình tối ưu hóa AdamWeightDecay với lịch trình tốc độ học tập được đặt:

type(optimizer)
official.nlp.optimization.AdamWeightDecay

Để xem ví dụ về cách tùy chỉnh trình tối ưu hóa và lịch trình của nó, hãy xem phụ lục lịch trình Trình tối ưu hóa .

Đào tạo mô hình

Chỉ số là độ chính xác và chúng tôi sử dụng entropy chéo phân loại thưa thớt làm tổn thất.

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 [==============================] - 38s 229ms/step - loss: 0.6175 - accuracy: 0.6844 - val_loss: 0.4610 - val_accuracy: 0.7892
Epoch 2/3
115/115 [==============================] - 25s 215ms/step - loss: 0.4207 - accuracy: 0.8125 - val_loss: 0.3859 - val_accuracy: 0.8211
Epoch 3/3
115/115 [==============================] - 25s 215ms/step - loss: 0.2990 - accuracy: 0.8867 - val_loss: 0.3759 - val_accuracy: 0.8407

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

Bây giờ hãy chạy mô hình đã được tinh chỉnh trên một ví dụ tùy chỉnh để xem nó hoạt động.

Bắt đầu bằng cách mã hóa một số cặp câu:

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)

Mô hình phải báo cáo "khớp" lớp 1 cho ví dụ đầu tiên và "không khớp" lớp 0 cho ví dụ thứ hai:

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

Lưu mô hình

Thông thường, mục tiêu của việc đào tạo mô hình là sử dụng nó cho một việc gì đó, vì vậy hãy xuất mô hình và sau đó khôi phục lại để đảm bảo rằng nó hoạt động.

export_dir='./saved_model'
tf.saved_model.save(bert_classifier, export_dir=export_dir)
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.
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.5500499   1.4857253 ]
 [ 0.72138155 -0.6029598 ]]

[[-1.5500501   1.4857253 ]
 [ 0.72138053 -0.6029588 ]]

ruột thừa

Mã hóa lại một tập dữ liệu lớn

Hướng dẫn này bạn đã mã hóa lại tập dữ liệu trong bộ nhớ để rõ ràng.

Điều này chỉ có thể thực hiện được vì glue/mrpc là một tập dữ liệu rất nhỏ. Để đối phó với tập dữ liệu lớn hơn, thư viện tf_models bao gồm một số công cụ để xử lý và mã hóa lại tập dữ liệu để đào tạo hiệu quả.

Bước đầu tiên là mô tả các tính năng nào của tập dữ liệu nên được chuyển đổi:

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)

Sau đó, áp dụng phép biến đổi để tạo tệp TFRecord mới.

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

Cuối cùng, tạo đường dẫn đầu vào tf.data từ các tệp 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)()

Các tf.data.Datasets trả về (features, labels) , như mong đợi của 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))

Tạo tf.data.Dataset để đào tạo và đánh giá

Nếu bạn cần sửa đổi tải dữ liệu, đây là một số mã để bạn bắt đầu:

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 trên TFHub

Bạn có thể lấy mô hình BERT ra khỏi kệ từ TFHub . Sẽ không khó để thêm một đầu phân loại trên đầu trung tâm này 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

Chạy thử nghiệm nó trên một loạt dữ liệu:

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)

Tại thời điểm này, sẽ đơn giản để tự thêm một đầu phân loại.

Hàm bert_models.classifier_model cũng có thể xây dựng một bộ phân loại trên bộ mã hóa từ 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))

Một nhược điểm khi tải mô hình này từ TFHub là cấu trúc của các lớp keras bên trong không được khôi phục. Vì vậy, việc kiểm tra hoặc sửa đổi mô hình khó hơn. Mô hình BertEncoder hiện là một lớp duy nhất:

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'

Xây dựng mô hình cấp thấp

Nếu bạn cần kiểm soát nhiều hơn việc xây dựng mô hình, cần lưu ý rằng hàm classifier_model được sử dụng trước đó thực sự chỉ là một lớp bọc mỏng trên các nlp.modeling.networks.BertEncodernlp.modeling.models.BertClassifier . Chỉ cần nhớ rằng nếu bạn bắt đầu sửa đổi kiến ​​trúc, nó có thể không đúng hoặc có thể tải lại điểm kiểm tra được đào tạo trước, vì vậy bạn sẽ cần phải đào tạo lại từ đầu.

Xây dựng bộ mã hóa:

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

Khôi phục trọng lượng:

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

Chạy thử nó:

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)

Gói nó trong một trình phân loại:

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.1309041 , -0.20986415],
       [-0.09952673,  0.05040173]], dtype=float32)

Trình tối ưu hóa và lịch trình

Trình tối ưu hóa được sử dụng để đào tạo mô hình đã được tạo bằng cách sử dụng hàm nlp.optimization.create_optimizer :

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

Trình bao bọc cấp cao đó thiết lập lịch trình tốc độ học tập và trình tối ưu hóa.

Lịch trình tốc độ học cơ bản được sử dụng ở đây là giảm dần tuyến tính về 0 trong quá trình đào tạo:

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

png

Điều này, đến lượt nó được bao bọc trong một lịch trình WarmUp tăng tuyến tính tỷ lệ học tập đến giá trị mục tiêu trong 10% đào tạo đầu tiê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 0x7fe20397bfd0>]

png

Sau đó, tạo nlp.optimization.AdamWeightDecay bằng cách sử dụng lịch biểu đó, được định cấu hình cho mô hình 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'])