具有 BERT-SNGP 的不确定性感知深度语言学习

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看 下载笔记本 查看 TF Hub 模型

SNGP 教程 中,您学习了如何在深度残差网络之上构建 SNGP 模型,以提高其量化其不确定性的能力。在本教程中,您将通过在深度 BERT 编码器之上构建 SNGP,将其应用于自然语言理解 (NLU) 任务,以提高深度 NLU 模型在检测超出范围查询方面的能力。

具体来说,您将

  • 构建 BERT-SNGP,一个增强了 SNGP 的 BERT 模型。
  • 加载 CLINC 超出范围 (OOS) 意图检测数据集。
  • 训练 BERT-SNGP 模型。
  • 评估 BERT-SNGP 模型在不确定性校准和域外检测方面的性能。

除了 CLINC OOS 之外,SNGP 模型还被应用于大型数据集,例如 Jigsaw 毒性检测,以及图像数据集,例如 CIFAR-100ImageNet。有关 SNGP 和其他不确定性方法的基准结果,以及具有端到端训练/评估脚本的高质量实现,您可以查看 不确定性基准 基准。

设置

pip uninstall -y tensorflow tf-text
pip install "tensorflow-text==2.11.*"
pip install -U tf-models-official==2.11.0
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

本教程需要 GPU 才能有效运行。检查 GPU 是否可用。

tf.__version__
gpus = tf.config.list_physical_devices('GPU')
gpus
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-base 编码器,以及内置的 ClassificationHead 作为分类器。

标准 BERT 模型

构建 SNGP 模型

要实现 BERT-SNGP 模型,您只需要用内置的 GaussianProcessClassificationHead 替换 ClassificationHead。谱归一化已预先打包到此分类头中。与 SNGP 教程 中一样,将协方差重置回调添加到模型中,以便模型在每个新纪元开始时自动重置协方差估计器,以避免对相同数据进行两次计数。

class ResetCovarianceCallback(tf.keras.callbacks.Callback):

  def on_epoch_begin(self, epoch, logs=None):
    """Resets covariance matrix at the beginning 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)

加载 CLINC OOS 数据集

现在加载 CLINC OOS 意图检测数据集。此数据集包含在 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

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)

评估 OOD 性能

评估模型识别不熟悉的域外查询的能力。为了进行严格的评估,请使用之前构建的 OOD 评估数据集 ood_eval_dataset

计算 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}')

这与 不确定性基准 下 CLINC OOS 基准中报告的 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()

资源和进一步阅读