MinDiff 数据准备

简介

在实现 MinDiff 时,您需要在选择和塑造输入并将其传递给模型之前做出复杂的决策。这些决策将在很大程度上决定 MinDiff 在模型中的行为。

本指南将涵盖此过程的技术方面,但不会讨论如何评估模型的公平性,或如何识别用于评估的特定切片和指标。有关此方面的详细信息,请参阅 公平性指标指南

为了演示 MinDiff,本指南使用 UCI 收入数据集。模型任务是根据各种个人属性预测个人收入是否超过 50,000 美元。本指南假设在 "男性""女性" 切片之间存在 FNR(假阴性率)的差距,并且模型所有者(您)已决定应用 MinDiff 来解决此问题。有关可能选择应用 MinDiff 的场景的更多信息,请参阅 要求页面

MinDiff 通过对两组数据中示例之间的分布分数差异进行惩罚来起作用。本指南将演示如何选择和构建这些额外的 MinDiff 集,以及如何将所有内容打包在一起,以便可以将其传递给模型进行训练。

设置

pip install --upgrade tensorflow-model-remediation
import tensorflow as tf
from tensorflow_model_remediation import min_diff
from tensorflow_model_remediation.tools.tutorials_utils import uci as tutorials_utils

原始数据

为了演示目的并减少运行时间,本指南仅使用 UCI 收入数据集的样本部分。在实际生产环境中,将使用完整的数据集。

# Sampled at 0.3 for reduced runtimes.
train = tutorials_utils.get_uci_data(split='train', sample=0.3)

print(len(train), 'train examples')

转换为 tf.data.Dataset

MinDiffModel 要求输入为 tf.data.Dataset。如果您在集成 MinDiff 之前使用的是其他格式的输入,则需要转换您的输入数据。

使用 tf.data.Dataset.from_tensor_slices 转换为 tf.data.Dataset

dataset = tf.data.Dataset.from_tensor_slices((x, y, weights))
dataset.shuffle(...)  # Optional.
dataset.batch(batch_size)

有关两种输入方法之间等效性的详细信息,请参阅 Model.fit 文档。

在本指南中,输入作为 Pandas DataFrame 下载,因此需要进行此转换。

# Function to convert a DataFrame into a tf.data.Dataset.
def df_to_dataset(dataframe, shuffle=True):
  dataframe = dataframe.copy()
  labels = dataframe.pop('target')
  ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=5000)  # Reasonable but arbitrary buffer_size.
  return ds

# Convert the train DataFrame into a Dataset.
original_train_ds = df_to_dataset(train)

创建 MinDiff 数据

在训练期间,MinDiff 将鼓励模型减少两个额外数据集(可能包含来自原始数据集的示例)之间的预测差异。这两个数据集的选择是关键决策,它将决定 MinDiff 对模型的影响。

应选择这两个数据集,以便您尝试修复的性能差异明显且代表性强。由于目标是减少 "男性""女性" 切片之间 FNR 的差距,这意味着创建一个仅包含标签 "男性" 示例的数据集,以及另一个仅包含标签 "女性" 示例的数据集;这些将是 MinDiff 数据集。

首先,检查存在的数据。

female_pos = train[(train['sex'] == ' Female') & (train['target'] == 1)]
male_pos = train[(train['sex'] == ' Male') & (train['target'] == 1)]
print(len(female_pos), 'positively labeled female examples')
print(len(male_pos), 'positively labeled male examples')

从原始数据集的子集中创建 MinDiff 数据集是完全可以接受的。

虽然没有按照要求指南中建议的至少 5,000 个正向 "Male" 示例,但我们拥有超过 2,000 个示例,在收集更多数据之前尝试使用这些示例是合理的。

min_diff_male_ds = df_to_dataset(male_pos)

然而,正向 "Female" 示例数量非常少,只有 385 个。这可能不足以获得良好的性能,因此需要引入更多示例。

full_uci_train = tutorials_utils.get_uci_data(split='train')
augmented_female_pos = full_uci_train[((full_uci_train['sex'] == ' Female') &
                                       (full_uci_train['target'] == 1))]
print(len(augmented_female_pos), 'positively labeled female examples')

使用完整数据集将可用于 MinDiff 的示例数量增加了两倍多。虽然数量仍然很少,但足以作为第一步尝试。

min_diff_female_ds = df_to_dataset(augmented_female_pos)

两个 MinDiff 数据集都明显小于建议的 5,000 个或更多示例。虽然尝试使用当前数据应用 MinDiff 是合理的,但如果在训练过程中观察到性能不佳或过拟合,您可能需要考虑收集更多数据。

使用 tf.data.Dataset.filter

或者,您可以直接从转换后的原始 Dataset 中创建两个 MinDiff 数据集。

# Male
def male_predicate(x, y):
  return tf.equal(x['sex'], b' Male') and tf.equal(y, 0)

alternate_min_diff_male_ds = original_train_ds.filter(male_predicate).cache()

# Female
def female_predicate(x, y):
  return tf.equal(x['sex'], b' Female') and tf.equal(y, 0)

full_uci_train_ds = df_to_dataset(full_uci_train)
alternate_min_diff_female_ds = full_uci_train_ds.filter(female_predicate).cache()

生成的 alternate_min_diff_male_dsalternate_min_diff_female_ds 在输出方面将分别等效于 min_diff_male_dsmin_diff_female_ds

构建训练数据集

作为最后一步,需要将三个数据集(两个新创建的数据集和原始数据集)合并成一个可以传递给模型的单个数据集。

对数据集进行批处理

在合并之前,需要对数据集进行批处理。

  • 原始数据集可以使用与集成 MinDiff 之前相同的批处理方式。
  • MinDiff 数据集不需要与原始数据集具有相同的批次大小。很可能,较小的批次大小也能获得相同的性能。虽然它们甚至不需要与彼此具有相同的批次大小,但建议这样做以获得最佳性能。

虽然不是严格必要,但建议对两个 MinDiff 数据集使用 drop_remainder=True,因为这将确保它们具有一致的批次大小。

original_train_ds = original_train_ds.batch(128)  # Same as before MinDiff.

# The MinDiff datasets can have a different batch_size from original_train_ds
min_diff_female_ds = min_diff_female_ds.batch(32, drop_remainder=True)
# Ideally we use the same batch size for both MinDiff datasets.
min_diff_male_ds = min_diff_male_ds.batch(32, drop_remainder=True)

使用 pack_min_diff_data 打包数据集

数据集准备就绪后,将它们打包成一个单个数据集,然后将其传递给模型。来自结果数据集的单个批次将包含来自您之前准备的三个数据集的每个数据集的一个批次。

您可以使用 tensorflow_model_remediation 包中提供的 utils 函数来完成此操作。

train_with_min_diff_ds = min_diff.keras.utils.pack_min_diff_data(
    original_dataset=original_train_ds,
    sensitive_group_dataset=min_diff_female_ds,
    nonsensitive_group_dataset=min_diff_male_ds)

就是这样!如果需要,您可以使用包中的其他 util 函数来解包各个批次。

for inputs, original_labels in train_with_min_diff_ds.take(1):
  # Unpacking min_diff_data
  min_diff_data = min_diff.keras.utils.unpack_min_diff_data(inputs)
  min_diff_examples, min_diff_membership = min_diff_data
  # Unpacking original data
  original_inputs = min_diff.keras.utils.unpack_original_inputs(inputs)

使用新形成的数据,您现在就可以在模型中应用 MinDiff 了!要了解如何完成此操作,请查看其他指南,从 将 MinDiff 与 MinDiffModel 集成 开始。

使用自定义打包格式(可选)

您可以选择以任何您选择的方式将三个数据集打包在一起。唯一的要求是您需要确保模型知道如何解释数据。MinDiffModel 的默认实现假设数据是使用 min_diff.keras.utils.pack_min_diff_data 打包的。

一种简单的方法是,在使用 min_diff.keras.utils.pack_min_diff_data 之后,将数据转换作为最后一步。

# Reformat input to be a dict.
def _reformat_input(inputs, original_labels):
  unpacked_min_diff_data = min_diff.keras.utils.unpack_min_diff_data(inputs)
  unpacked_original_inputs = min_diff.keras.utils.unpack_original_inputs(inputs)

  return {
      'min_diff_data': unpacked_min_diff_data,
      'original_data': (unpacked_original_inputs, original_labels)}

customized_train_with_min_diff_ds = train_with_min_diff_ds.map(_reformat_input)

您的模型需要知道如何读取此自定义输入,如 自定义 MinDiffModel 指南 中所述。

for batch in customized_train_with_min_diff_ds.take(1):
  # Customized unpacking of min_diff_data
  min_diff_data = batch['min_diff_data']
  # Customized unpacking of original_data
  original_data = batch['original_data']

其他资源

  • 有关公平性评估的深入讨论,请参阅 公平性指标指南
  • 有关修复和 MinDiff 的一般信息,请参阅 修复概述
  • 有关 MinDiff 周围要求的详细信息,请参阅 本指南
  • 要查看在 Keras 中使用 MinDiff 的端到端教程,请参阅 本教程

本指南概述了在应用 MinDiff 时可以遵循的过程和决策过程。其余指南都基于此框架。为了简化操作,本指南中的逻辑已分解为辅助函数。

  • get_uci_data:此函数已在本指南中使用。它返回一个包含来自指定拆分的 UCI 收入数据的 DataFrame,该数据以指定的速率进行采样(如果未指定,则为 100%)。
  • df_to_dataset:此函数将 DataFrame 转换为 tf.data.Dataset,如本指南中所述,并增加了能够将 batch_size 作为参数传递的功能。
  • get_uci_with_min_diff_dataset:此函数返回一个包含原始数据和 MinDiff 数据的 tf.data.Dataset,这些数据使用模型修复库实用程序函数打包在一起,如本指南中所述。

其余指南将基于这些内容,展示如何使用库的其他部分。