BERTモデルの微調整

TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示 ノートブックをダウンロードTFハブモデルを参照してください

この例では、tensorflow-modelsPIPパッケージを使用してBERTモデルを微調整します。

このチュートリアルでは、に基づいてpretrained BERTモデルもで提供されていますTensorFlowハブ、それはを参照してください使用方法を確認するために、ハブ付録

設定

TensorFlow Model Gardenpipパッケージをインストールします

  • tf-models-official安定したモデルガーデンパッケージです。それはの最新の変更含まれていないことに注意してくださいtensorflow_models githubのレポを。最新の変更を含めるには、インストールすることもtf-models-nightly毎日自動的に作成毎晩モデルガーデンのパッケージがあります。
  • pipはすべてのモデルと依存関係を自動的にインストールします。
pip install -q -U tensorflow-text
pip install -q tf-models-official==2.4.0

輸入

import os

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf

import tensorflow_hub as hub
import tensorflow_datasets as tfds
tfds.disable_progress_bar()

from official.modeling import tf_utils
from official import nlp
from official.nlp import bert

# Load the required submodules
import official.nlp.optimization
import official.nlp.bert.bert_models
import official.nlp.bert.configs
import official.nlp.bert.run_classifier
import official.nlp.bert.tokenization
import official.nlp.data.classifier_data_lib
import official.nlp.modeling.losses
import official.nlp.modeling.models
import official.nlp.modeling.networks
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_addons/utils/ensure_tf_install.py:67: UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.3.0 and strictly below 2.6.0 (nightly versions are not supported). 
 The versions of TensorFlow you are currently using is 2.6.0 and is not supported. 
Some things might work, some things might not.
If you were to encounter a bug, do not file an issue.
If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. 
You can find the compatibility matrix in TensorFlow Addon's readme:
https://github.com/tensorflow/addons
  UserWarning,

資力

このディレクトリには、このチュートリアルで使用される構成、語彙、および事前にトレーニングされたチェックポイントが含まれています。

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

あなたはから事前に訓練されたBERTのエンコーダを得ることができますTensorFlowハブ

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

データ

この例では、私たちが使用TFDSからGLUE MRPCデータセットを

このデータセットは、BERTモデルに直接入力できるように設定されていないため、このセクションでは必要な前処理も処理します。

TensorFlowデータセットからデータセットを取得します

Microsoft 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)
WARNING:tensorflow:From /home/kbuilder/.local/lib/python3.7/site-packages/tensorflow_datasets/core/dataset_builder.py:622: get_single_element (from tensorflow.python.data.experimental.ops.get_single_element) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.get_single_element()`.
2021-08-19 11:26:56.046711: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.055242: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.056228: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.057937: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-19 11:26:56.058570: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.059629: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.060526: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.661471: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.662451: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.663279: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-19 11:26:56.664195: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1510] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 14648 MB memory:  -> device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0
WARNING:tensorflow:From /home/kbuilder/.local/lib/python3.7/site-packages/tensorflow_datasets/core/dataset_builder.py:622: get_single_element (from tensorflow.python.data.experimental.ops.get_single_element) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.get_single_element()`.
2021-08-19 11:26:57.012380: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)
list(glue.keys())
['train', 'validation', 'test']

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

2つのクラスは次のとおりです。

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

トレーニングセットの1つの例を次に示します。

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トークナイザーは、純粋なPythonで記述されています(TensorFlow opsから構築されたものではありません)。だから、同じようにあなたのモデルにプラグインすることはできませんkeras.layerあなたがでできるようにpreprocessing.TextVectorization

次のコードは、基本モデルで使用されていたトークナイザーを再構築します。

# 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ライブラリーは、前処理およびデータセットを再シリアライズするためのいくつかのツールが含まれています。参照してください。再エンコード大きなデータセット:付録詳細を。

文をエンコードする

モデルは、2つの入力文が連結されることを想定しています。この入力はで開始すると予想される[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各例えばテンソル。 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

マスクと入力タイプ

モデルは、2つの追加入力を想定しています。

  • 入力マスク
  • 入力タイプ

マスクを使用すると、モデルでコンテンツとパディングを明確に区別できます。マスクは、同じ形状持つ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 0x7f05206b9790>

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

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モデルであるBERTモデル、定義num_classes最大の系列長を有する入力からmax_seq_length

この関数は、エンコーダーと分類子の両方を返します。

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

分類器には、3つの入力と1つの出力があります。

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

png

トレーニングセットからの10例のデータのテストバッチで実行します。出力は、2つのクラスのロジットです。

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

bert_classifier(
    glue_batch, training=True
).numpy()
array([[-0.2307785 ,  0.14490062],
       [-0.09524915,  0.1295139 ],
       [-0.14503807,  0.19079085],
       [-0.2493319 ,  0.29745924],
       [-0.25108814, -0.09775029],
       [-0.02554443, -0.07443134],
       [-0.34417343,  0.00068308],
       [-0.13155738,  0.10524555],
       [-0.39608416, -0.14741066],
       [-0.2702508 , -0.02493864]], dtype=float32)

TransformerEncoder分類器の中心には、上記bert_encoder

エンコーダを検査、我々は、そのスタックを参照Transformer層は、それらの同じ3つの入力に接続しました:

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

png

エンコーダの重みを元に戻す

構築されると、エンコーダはランダムに初期化されます。チェックポイントからエンコーダの重みを復元します。

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

オプティマイザを設定します

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 [==============================] - 38s 222ms/step - loss: 0.6067 - accuracy: 0.6737 - val_loss: 0.5166 - val_accuracy: 0.7574
Epoch 2/3
115/115 [==============================] - 24s 211ms/step - loss: 0.4362 - accuracy: 0.8062 - val_loss: 0.3850 - val_accuracy: 0.8137
Epoch 3/3
115/115 [==============================] - 24s 211ms/step - loss: 0.2994 - accuracy: 0.8912 - val_loss: 0.3774 - val_accuracy: 0.8309
<keras.callbacks.History at 0x7f04c010b210>

次に、カスタムサンプルで微調整されたモデルを実行して、機能することを確認します。

いくつかの文のペアをエンコードすることから始めます。

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)
2021-08-19 11:30:12.946332: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as self_attention_layer_call_fn, self_attention_layer_call_and_return_conditional_losses, dropout_layer_call_fn, dropout_layer_call_and_return_conditional_losses, self_attention_layer_norm_layer_call_fn while saving (showing 5 of 900). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: ./saved_model/assets
INFO:tensorflow:Assets written to: ./saved_model/assets
reloaded = tf.saved_model.load(export_dir)
reloaded_result = reloaded([my_examples['input_word_ids'],
                            my_examples['input_mask'],
                            my_examples['input_type_ids']], training=False)

original_result = bert_classifier(my_examples, training=False)

# The results are (nearly) identical:
print(original_result.numpy())
print()
print(reloaded_result.numpy())
[[-1.1162528   1.014004  ]
 [ 0.9412503  -0.72278297]]

[[-1.1162527  1.0140039]
 [ 0.94125   -0.7227829]]

付録

大規模なデータセットの再エンコード

このチュートリアルでは、わかりやすくするために、データセットをメモリに再エンコードしました。

これはのみ可能であった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.AUTOTUNE)
  dataset = dataset.map(
      _select_data_from_record,
      num_parallel_calls=tf.data.AUTOTUNE)
  dataset = dataset.batch(batch_size, drop_remainder=is_training)
  dataset = dataset.prefetch(tf.data.AUTOTUNE)
  return dataset
# Set up batch sizes
batch_size = 32
eval_batch_size = 32

# Return Tensorflow dataset
training_dataset = create_classifier_dataset(
    train_data_output_path,
    input_meta_data['max_seq_length'],
    batch_size,
    is_training=True)

evaluation_dataset = create_classifier_dataset(
    eval_data_output_path,
    input_meta_data['max_seq_length'],
    eval_batch_size,
    is_training=False)
training_dataset.element_spec
({'input_word_ids': TensorSpec(shape=(32, 128), dtype=tf.int64, name=None),
  'input_mask': TensorSpec(shape=(32, 128), dtype=tf.int64, name=None),
  'input_type_ids': TensorSpec(shape=(32, 128), dtype=tf.int64, name=None)},
 TensorSpec(shape=(32,), dtype=tf.int64, name=None))

TFHub上のTFModelsBERT

あなたは得ることができますBERTモデルから既製品TFHubを。この上に分類ヘッドを追加することは難しいことではないでしょう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

データのバッチでテスト実行します。

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)

この時点で、分類ヘッドを自分で追加するのは簡単です。

bert_models.classifier_model機能もTensorFlowハブからのエンコーダに分類器を構築することができます。

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

TFHubからこのモデルをロードすることの1つの欠点は、内部のkerasレイヤーの構造が復元されないことです。そのため、モデルを検査または変更することはより困難です。 BertEncoderモデルは、単層です。

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.BertEncodernlp.modeling.models.BertClassifierクラス。アーキテクチャの変更を開始した場合、事前にトレーニングされたチェックポイントをリロードすることが正しくないか、不可能である可能性があるため、最初から再トレーニングする必要があることを覚えておいてください。

エンコーダーを作成します。

bert_encoder_config = config_dict.copy()

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

bert_encoder_config
{'hidden_size': 768,
 'intermediate_size': 3072,
 'num_attention_heads': 12,
 'type_vocab_size': 2,
 'vocab_size': 30522,
 'attention_dropout_rate': 0.1,
 'activation': <function official.modeling.activations.gelu.gelu(x)>,
 'dropout_rate': 0.1,
 'initializer': <keras.initializers.initializers_v2.TruncatedNormal at 0x7f006c251d10>,
 'max_sequence_length': 512,
 'num_layers': 12}
manual_encoder = nlp.modeling.networks.BertEncoder(**bert_encoder_config)

重みを復元します。

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

テスト実行:

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=bert_encoder_config['dropout_rate'],
        initializer=bert_encoder_config['initializer'])
manual_classifier(my_examples, training=True).numpy()
array([[ 0.19574928, -0.09590703],
       [-0.3455575 ,  0.12091695]], 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 0x7f0065da3c50>]

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

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