Preparación de datos MinDiff

Introducción

Al implementar MinDiff, deberá tomar decisiones complejas a medida que elige y da forma a su entrada antes de pasarla al modelo. Estas decisiones determinarán en gran medida el comportamiento de MinDiff dentro de su modelo.

Esta guía cubrirá los aspectos técnicos de este proceso, pero no discutirá cómo evaluar la equidad de un modelo, o cómo identificar segmentos y métricas particulares para la evaluación. Por favor, vea la guía Indicadores de Equidad para más detalles sobre esto.

Para demostrar MinDiff, esta guía utiliza el conjunto de datos de ingresos UCI . La tarea del modelo es predecir si un individuo tiene un ingreso superior a $ 50k, en función de varios atributos personales. Esta guía asume que existe una brecha problemático en el FNR (tasa de falsos negativos) entre "Male" y "Female" rebanadas y el propietario del modelo (que) ha decidido aplicar MinDiff para abordar la cuestión. Para obtener más información sobre los escenarios en los que se podría optar por aplicar MinDiff, consulte la página de requisitos .

MinDiff funciona penalizando la diferencia en las puntuaciones de distribución entre ejemplos en dos conjuntos de datos. Esta guía demostrará cómo elegir y construir estos conjuntos MinDiff adicionales, así como también cómo empaquetar todo junto para que pueda pasarse a un modelo para entrenamiento.

Configuración

pip install -q --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

Datos originales

Con fines de demostración y para reducir los tiempos de ejecución, esta guía utiliza solo una fracción de muestra del conjunto de datos de ingresos de UCI. En un entorno de producción real, se utilizaría el conjunto de datos completo.

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

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

La conversión a tf.data.Dataset

MinDiffModel requiere que la entrada sea un tf.data.Dataset . Si estaba usando un formato de entrada diferente antes de integrar MinDiff, tendrá que convertir sus datos de entrada.

Use tf.data.Dataset.from_tensor_slices para convertir a tf.data.Dataset .

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

Ver Model.fit documentación para más detalles sobre las equivalencias entre los dos métodos de entrada.

En esta guía, la entrada se descarga como Pandas DataFrame y, por lo tanto, necesita esta conversión.

# 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)

Creación de datos MinDiff

Durante el entrenamiento, MinDiff alentará al modelo a reducir las diferencias en las predicciones entre dos conjuntos de datos adicionales (que pueden incluir ejemplos del conjunto de datos original). La selección de estos dos conjuntos de datos es la decisión clave que determinará el efecto que MinDiff tiene en el modelo.

Los dos conjuntos de datos deben seleccionarse de manera que la disparidad en el rendimiento que está tratando de remediar sea evidente y esté bien representada. Dado que el objetivo es reducir una brecha en FNR entre "Male" y "Female" rebanadas, esto significa crear un conjunto de datos con solamente marcadas positivamente "Male" ejemplos y otra con solamente marcadas positivamente "Female" ejemplos; estos serán los conjuntos de datos MinDiff.

Primero, examine los datos presentes.

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')
385 positively labeled female examples
2063 positively labeled male examples

Es perfectamente aceptable crear conjuntos de datos MinDiff a partir de subconjuntos del conjunto de datos original.

Si bien no hay 5,000 o más positivos "Male" ejemplos como se recomienda en la orientación requisitos , hay más de 2.000 y es razonable tratar con que muchos antes de recoger más datos.

min_diff_male_ds = df_to_dataset(male_pos)

Positivas "Female" ejemplos, sin embargo, son mucho más escasos en 385. Esto es probablemente demasiado pequeño para un buen rendimiento y por lo tanto va a requerir que se tire en los ejemplos adicionales.

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')
1179 positively labeled female examples

El uso del conjunto de datos completo ha triplicado con creces la cantidad de ejemplos que se pueden usar para MinDiff. Todavía es bajo, pero es suficiente para intentarlo como un primer paso.

min_diff_female_ds = df_to_dataset(augmented_female_pos)

Ambos conjuntos de datos MinDiff son significativamente más pequeños que los 5000 o más ejemplos recomendados. Si bien es razonable intentar aplicar MinDiff con los datos actuales, es posible que deba considerar recopilar datos adicionales si observa un rendimiento deficiente o un ajuste excesivo durante el entrenamiento.

usando tf.data.Dataset.filter

Como alternativa, puede crear los dos conjuntos de datos MinDiff directamente desde el original convertida Dataset .

# 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()

Los resultantes alternate_min_diff_male_ds y alternate_min_diff_female_ds serán equivalentes en la producción a min_diff_male_ds y min_diff_female_ds respectivamente.

Construyendo su conjunto de datos de entrenamiento

Como paso final, los tres conjuntos de datos (los dos recién creados y el original) deben fusionarse en un solo conjunto de datos que se puede pasar al modelo.

Lotes de conjuntos de datos

Antes de la fusión, los conjuntos de datos deben agruparse por lotes.

  • El conjunto de datos original puede usar el mismo procesamiento por lotes que se usó antes de integrar MinDiff.
  • Los conjuntos de datos MinDiff no necesitan tener el mismo tamaño de lote que el conjunto de datos original. Con toda probabilidad, uno más pequeño funcionará igual de bien. Si bien ni siquiera es necesario que tengan el mismo tamaño de lote, se recomienda hacerlo para obtener el mejor rendimiento.

Aunque no es estrictamente necesario, se recomienda el uso drop_remainder=True para los dos conjuntos de datos MinDiff ya que esto garantizará que tienen tamaños de lotes consistentes.

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)

Embalaje los datos con pack_min_diff_data

Una vez que los conjuntos de datos estén preparados, compáctelos en un solo conjunto de datos que luego se pasará al modelo. Un solo lote del conjunto de datos resultante contendrá un lote de cada uno de los tres conjuntos de datos que preparó anteriormente.

Usted puede hacer esto mediante el uso de la proporcionada utils función en el tensorflow_model_remediation paquete:

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)

¡Y eso es! Usted será capaz de utilizar otros util funciones en el paquete de lotes individuales de desempaquetado si es necesario.

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)

Con sus datos recién formados, ¡ahora está listo para aplicar MinDiff en su modelo! Para aprender cómo se hace esto, por favor, eche un vistazo a las otras guías que comienzan con integración MinDiff con MinDiffModel .

Uso de un formato de embalaje personalizado (opcional)

Puede decidir agrupar los tres conjuntos de datos de la forma que elija. El único requisito es que deberá asegurarse de que el modelo sepa cómo interpretar los datos. La implementación predeterminada de MinDiffModel asume que los datos se introdujeron como relleno utilizando min_diff.keras.utils.pack_min_diff_data .

Una forma fácil de dar formato a su entrada como que quiere es transformar los datos como paso final después de haber utilizado 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)

Su modelo tendrá que saber cómo leer esta entrada personalizada como se detalla en la guía Personalización 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']

Recursos adicionales

Esta guía describe el proceso y la toma de decisiones que puede seguir al aplicar MinDiff. El resto de las guías se basan en este marco. Para hacer esto más fácil, la lógica que se encuentra en esta guía se ha incluido en las funciones auxiliares:

  • get_uci_data : Esta función ya se utiliza en esta guía. Se devuelve una DataFrame que contiene los datos de ingresos de la UCI de la división indicada muestreado al ritmo que se indica (100% si no se especifica).
  • df_to_dataset : Esta función convierte una DataFrame en un tf.data.Dataset como se detalla en esta guía con la funcionalidad añadida de ser capaz de pasar el batch_size como un parámetro.
  • get_uci_with_min_diff_dataset : Esta función devuelve una tf.data.Dataset que contiene tanto los datos originales y los datos MinDiff embalados conjuntamente utilizando el Modelo de la Biblioteca Remediation util funciones como se describe en esta guía.

El resto de las guías se basarán en estas para mostrar cómo usar otras partes de la biblioteca.