MinDiff 資料準備

簡介

在實作 MinDiff 時,您需要在將輸入傳遞至模型之前,做出複雜的決策來選擇和塑造輸入。這些決策將在很大程度上決定 MinDiff 在模型中的行為。

本指南將涵蓋此流程的技術層面,但不會討論如何評估模型的公平性,或如何識別用於評估的特定切片和指標。有關詳細資訊,請參閱公平性指標指南

為了示範 MinDiff,本指南使用 UCI 收入資料集。模型任務是根據各種個人屬性預測個人收入是否超過 5 萬美元。本指南假設在 "Male""Female" 切片之間的 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 對模型影響的關鍵決策。

這兩個資料集的選擇應確保您嘗試修正的效能差異顯而易見且充分呈現。由於目標是縮小 "Male""Female" 切片之間 FNR 的差距,這表示建立一個僅包含正面標籤 "Male" 範例的資料集,以及另一個僅包含正面標籤 "Female" 範例的資料集;這些將是 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:本指南已使用此函式。它會傳回一個 DataFrame,其中包含來自指示分割的 UCI 收入資料,並以指示的任何比率(如果未指定,則為 100%)取樣。
  • df_to_dataset:此函式會將 DataFrame 轉換為 tf.data.Dataset,如本指南中所詳述,並新增了能夠將 batch_size 作為參數傳遞的功能。
  • get_uci_with_min_diff_dataset:此函式會傳回一個 tf.data.Dataset,其中包含原始資料和 MinDiff 資料,並使用模型修正程式庫 util 函式封裝在一起,如本指南中所述。

其餘指南將以此為基礎,展示如何使用程式庫的其他部分。