TensorFlow.orgで表示 | GoogleColabで実行 | GitHubで表示 | ノートブックをダウンロード | TFハブモデルを参照してください |
SNGPチュートリアルでは、不確実性を定量化する能力を向上させるために、深い残余ネットワークの上にSNGPモデルを構築する方法を学びました。このチュートリアルでは、SNGPをディープBERTエンコーダーの上に構築して自然言語理解(NLU)タスクに適用し、スコープ外のクエリを検出するディープNLUモデルの機能を向上させます。
具体的には、次のことを行います。
- SNGP拡張BERTモデルであるBERT-SNGPを構築します。
- CLINC Out-of-scope(OOS)インテント検出データセットをロードします。
- BERT-SNGPモデルをトレーニングします。
- 不確かさのキャリブレーションとドメイン外検出におけるBERT-SNGPモデルのパフォーマンスを評価します。
CLINC OOSを超えて、SNGPモデルは、ジグソー毒性検出などの大規模データセット、およびCIFAR-100やImageNetなどの画像データセットに適用されています。 SNGPおよびその他の不確実性手法のベンチマーク結果、およびエンドツーエンドのトレーニング/評価スクリプトを使用した高品質の実装については、不確実性ベースラインベンチマークを確認できます。
設定
pip uninstall -y tensorflow tf-text
pip install -U tensorflow-text-nightly
pip install -U tf-nightly
pip install -U tf-models-nightly
import matplotlib.pyplot as plt
import sklearn.metrics
import sklearn.calibration
import tensorflow_hub as hub
import tensorflow_datasets as tfds
import numpy as np
import tensorflow as tf
import official.nlp.modeling.layers as layers
import official.nlp.optimization as optimization
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_addons/utils/ensure_tf_install.py:43: UserWarning: You are currently using a nightly version of TensorFlow (2.9.0-dev20220203). 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,
このチュートリアルを効率的に実行するには、GPUが必要です。 GPUが使用可能かどうかを確認します。
tf.__version__
'2.9.0-dev20220203'
gpus = tf.config.list_physical_devices('GPU')
gpus
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
assert gpus, """
No GPU(s) found! This tutorial will take many hours to run without a GPU.
You may hit this error if the installed tensorflow package is not
compatible with the CUDA and CUDNN versions."""
まず、BERTチュートリアルを使用した分類テキストに従って、標準のBERT分類子を実装します。 BERTベースのエンコーダーと、組み込みのClassificationHead
を分類子として使用します。
標準BERTモデル
PREPROCESS_HANDLE = 'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3'
MODEL_HANDLE = 'https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3'
class BertClassifier(tf.keras.Model):
def __init__(self,
num_classes=150, inner_dim=768, dropout_rate=0.1,
**classifier_kwargs):
super().__init__()
self.classifier_kwargs = classifier_kwargs
# Initiate the BERT encoder components.
self.bert_preprocessor = hub.KerasLayer(PREPROCESS_HANDLE, name='preprocessing')
self.bert_hidden_layer = hub.KerasLayer(MODEL_HANDLE, trainable=True, name='bert_encoder')
# Defines the encoder and classification layers.
self.bert_encoder = self.make_bert_encoder()
self.classifier = self.make_classification_head(num_classes, inner_dim, dropout_rate)
def make_bert_encoder(self):
text_inputs = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
encoder_inputs = self.bert_preprocessor(text_inputs)
encoder_outputs = self.bert_hidden_layer(encoder_inputs)
return tf.keras.Model(text_inputs, encoder_outputs)
def make_classification_head(self, num_classes, inner_dim, dropout_rate):
return layers.ClassificationHead(
num_classes=num_classes,
inner_dim=inner_dim,
dropout_rate=dropout_rate,
**self.classifier_kwargs)
def call(self, inputs, **kwargs):
encoder_outputs = self.bert_encoder(inputs)
classifier_inputs = encoder_outputs['sequence_output']
return self.classifier(classifier_inputs, **kwargs)
SNGPモデルを構築する
BERT-SNGPモデルを実装するには、 ClassificationHead
を組み込みのGaussianProcessClassificationHead
に置き換えるだけです。スペクトルの正規化は、この分類ヘッドにすでにパッケージ化されています。 SNGPチュートリアルと同様に、モデルに共分散リセットコールバックを追加します。これにより、モデルは、同じデータを2回カウントしないように、新しいエポックの開始時に共分散推定量を自動的にリセットします。
class ResetCovarianceCallback(tf.keras.callbacks.Callback):
def on_epoch_begin(self, epoch, logs=None):
"""Resets covariance matrix at the begining of the epoch."""
if epoch > 0:
self.model.classifier.reset_covariance_matrix()
class SNGPBertClassifier(BertClassifier):
def make_classification_head(self, num_classes, inner_dim, dropout_rate):
return layers.GaussianProcessClassificationHead(
num_classes=num_classes,
inner_dim=inner_dim,
dropout_rate=dropout_rate,
gp_cov_momentum=-1,
temperature=30.,
**self.classifier_kwargs)
def fit(self, *args, **kwargs):
"""Adds ResetCovarianceCallback to model callbacks."""
kwargs['callbacks'] = list(kwargs.get('callbacks', []))
kwargs['callbacks'].append(ResetCovarianceCallback())
return super().fit(*args, **kwargs)
プレースホルダー18CLINCOOSデータセットをロードする
次に、 CLINCOOSインテント検出データセットをロードします。このデータセットには、150を超えるインテントクラスで収集された15000のユーザーの音声クエリが含まれています。また、既知のクラスのいずれにも含まれない1000のドメイン外(OOD)センテンスも含まれています。
(clinc_train, clinc_test, clinc_test_oos), ds_info = tfds.load(
'clinc_oos', split=['train', 'test', 'test_oos'], with_info=True, batch_size=-1)
列車とテストデータを作成します。
train_examples = clinc_train['text']
train_labels = clinc_train['intent']
# Makes the in-domain (IND) evaluation data.
ind_eval_data = (clinc_test['text'], clinc_test['intent'])
OOD評価データセットを作成します。このために、ドメイン内のテストデータclinc_test
とドメイン外のデータclinc_test_oos
を組み合わせます。また、ドメイン内の例にラベル0を割り当て、ドメイン外の例にラベル1を割り当てます。
test_data_size = ds_info.splits['test'].num_examples
oos_data_size = ds_info.splits['test_oos'].num_examples
# Combines the in-domain and out-of-domain test examples.
oos_texts = tf.concat([clinc_test['text'], clinc_test_oos['text']], axis=0)
oos_labels = tf.constant([0] * test_data_size + [1] * oos_data_size)
# Converts into a TF dataset.
ood_eval_dataset = tf.data.Dataset.from_tensor_slices(
{"text": oos_texts, "label": oos_labels})
トレーニングと評価
まず、基本的なトレーニング構成を設定します。
TRAIN_EPOCHS = 3
TRAIN_BATCH_SIZE = 32
EVAL_BATCH_SIZE = 256
def bert_optimizer(learning_rate,
batch_size=TRAIN_BATCH_SIZE, epochs=TRAIN_EPOCHS,
warmup_rate=0.1):
"""Creates an AdamWeightDecay optimizer with learning rate schedule."""
train_data_size = ds_info.splits['train'].num_examples
steps_per_epoch = int(train_data_size / batch_size)
num_train_steps = steps_per_epoch * epochs
num_warmup_steps = int(warmup_rate * num_train_steps)
# Creates learning schedule.
lr_schedule = tf.keras.optimizers.schedules.PolynomialDecay(
initial_learning_rate=learning_rate,
decay_steps=num_train_steps,
end_learning_rate=0.0)
return optimization.AdamWeightDecay(
learning_rate=lr_schedule,
weight_decay_rate=0.01,
epsilon=1e-6,
exclude_from_weight_decay=['LayerNorm', 'layer_norm', 'bias'])
optimizer = bert_optimizer(learning_rate=1e-4)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
metrics = tf.metrics.SparseCategoricalAccuracy()
fit_configs = dict(batch_size=TRAIN_BATCH_SIZE,
epochs=TRAIN_EPOCHS,
validation_batch_size=EVAL_BATCH_SIZE,
validation_data=ind_eval_data)
sngp_model = SNGPBertClassifier()
sngp_model.compile(optimizer=optimizer, loss=loss, metrics=metrics)
sngp_model.fit(train_examples, train_labels, **fit_configs)
Epoch 1/3 469/469 [==============================] - 219s 427ms/step - loss: 1.0725 - sparse_categorical_accuracy: 0.7870 - val_loss: 0.4358 - val_sparse_categorical_accuracy: 0.9380 Epoch 2/3 469/469 [==============================] - 198s 422ms/step - loss: 0.0885 - sparse_categorical_accuracy: 0.9797 - val_loss: 0.2424 - val_sparse_categorical_accuracy: 0.9518 Epoch 3/3 469/469 [==============================] - 199s 424ms/step - loss: 0.0259 - sparse_categorical_accuracy: 0.9951 - val_loss: 0.1927 - val_sparse_categorical_accuracy: 0.9642 <keras.callbacks.History at 0x7fe24c0a7090>
OODパフォーマンスを評価する
モデルが見慣れないドメイン外クエリをどれだけうまく検出できるかを評価します。厳密な評価には、以前に作成したOOD評価データセットood_eval_dataset
を使用します。
def oos_predict(model, ood_eval_dataset, **model_kwargs):
oos_labels = []
oos_probs = []
ood_eval_dataset = ood_eval_dataset.batch(EVAL_BATCH_SIZE)
for oos_batch in ood_eval_dataset:
oos_text_batch = oos_batch["text"]
oos_label_batch = oos_batch["label"]
pred_logits = model(oos_text_batch, **model_kwargs)
pred_probs_all = tf.nn.softmax(pred_logits, axis=-1)
pred_probs = tf.reduce_max(pred_probs_all, axis=-1)
oos_labels.append(oos_label_batch)
oos_probs.append(pred_probs)
oos_probs = tf.concat(oos_probs, axis=0)
oos_labels = tf.concat(oos_labels, axis=0)
return oos_probs, oos_labels
OOD確率を \(1 - p(x)\)として計算します。ここで、 \(p(x)=softmax(logit(x))\) は予測確率です。
sngp_probs, ood_labels = oos_predict(sngp_model, ood_eval_dataset)
ood_probs = 1 - sngp_probs
次に、モデルの不確実性スコアood_probs
がドメイン外ラベルをどの程度適切に予測するかを評価します。まず、OOD確率とOOD検出精度の適合率-再現率曲線(AUPRC)の下の面積を計算します。
precision, recall, _ = sklearn.metrics.precision_recall_curve(ood_labels, ood_probs)
auprc = sklearn.metrics.auc(recall, precision)
print(f'SNGP AUPRC: {auprc:.4f}')
SNGP AUPRC: 0.9039
これは、不確実性ベースラインの下でCLINCOOSベンチマークで報告されたSNGPパフォーマンスと一致します。
次に、不確かさのキャリブレーションでモデルの品質を調べます。つまり、モデルの予測確率がその予測精度に対応しているかどうかを調べます。たとえば、予測確率 \(p(x)=0.8\) は、モデルが80%の確率で正しいことを意味するため、適切に調整されたモデルは信頼できると見なされます。
prob_true, prob_pred = sklearn.calibration.calibration_curve(
ood_labels, ood_probs, n_bins=10, strategy='quantile')
plt.plot(prob_pred, prob_true)
plt.plot([0., 1.], [0., 1.], c='k', linestyle="--")
plt.xlabel('Predictive Probability')
plt.ylabel('Predictive Accuracy')
plt.title('Calibration Plots, SNGP')
plt.show()
リソースと参考資料
- SNGPを最初から実装するための詳細なウォークスルーについては、 SNGPチュートリアルを参照してください。
- さまざまなベンチマークデータセット( CIFAR 、 ImageNet 、ジグソー毒性検出など)でのSNGPモデル(および他の多くの不確実性手法)の実装については、不確実性ベースラインを参照してください。
- SNGP法のより深い理解については、論文「距離認識による決定論的深層学習による単純で原理的な不確実性の推定」を確認してください。