Preparação de dados MinDiff

Introdução

Ao implementar o MinDiff, você precisará tomar decisões complexas conforme escolhe e molda sua entrada antes de passá-la para o modelo. Essas decisões determinarão amplamente o comportamento de MinDiff em seu modelo.

Este guia cobrirá os aspectos técnicos desse processo, mas não discutirá como avaliar um modelo quanto à justiça ou como identificar fatias e métricas específicas para avaliação. Consulte a orientação Fairness Indicadores para obter detalhes sobre isso.

Para demonstrar MinDiff, este guia usa o conjunto de dados de renda UCI . A tarefa do modelo é prever se um indivíduo tem uma renda superior a US $ 50 mil, com base em vários atributos pessoais. Este guia assume que há uma lacuna problemática no FNR (taxa de falsos negativos) entre "Male" e "Female" fatias e o proprietário do modelo (você) decidiu aplicar MinDiff para resolver a questão. Para mais informações sobre os cenários em que se poderia optar por aplicar MinDiff, consulte a página de requisitos .

MinDiff funciona penalizando a diferença nas pontuações de distribuição entre exemplos em dois conjuntos de dados. Este guia demonstrará como escolher e construir esses conjuntos MinDiff adicionais, bem como como empacotar tudo junto para que possa ser passado para um modelo para treinamento.

Configurar

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

Dados Originais

Para fins de demonstração e para reduzir o tempo de execução, este guia usa apenas uma fração de amostra do conjunto de dados de Renda UCI. Em um ambiente de produção real, o conjunto de dados completo seria utilizado.

# 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

Convertendo para tf.data.Dataset

MinDiffModel requer que a entrada ser um tf.data.Dataset . Se você estava usando um formato diferente de entrada antes de integrar o MinDiff, você terá que converter seus dados de entrada.

Use tf.data.Dataset.from_tensor_slices a se converter ao tf.data.Dataset .

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

Veja Model.fit documentação para obter detalhes sobre as equivalências entre os dois métodos de entrada.

Neste guia, a entrada é baixada como um Pandas DataFrame e, portanto, precisa desta conversão.

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

Criação de dados MinDiff

Durante o treinamento, o MinDiff incentivará o modelo a reduzir as diferenças nas previsões entre dois conjuntos de dados adicionais (que podem incluir exemplos do conjunto de dados original). A seleção desses dois conjuntos de dados é a decisão chave que determinará o efeito do MinDiff no modelo.

Os dois conjuntos de dados devem ser escolhidos de forma que a disparidade de desempenho que você está tentando corrigir seja evidente e bem representada. Como o objetivo é reduzir a lacuna no FNR entre "Male" e "Female" fatias, isso significa criar um conjunto de dados com apenas rotulados de forma positiva "Male" exemplos e outro com apenas rotulados de forma positiva "Female" exemplos; estes serão os conjuntos de dados MinDiff.

Primeiro, examine os dados 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

É perfeitamente aceitável criar conjuntos de dados MinDiff a partir de subconjuntos do conjunto de dados original.

Enquanto não há 5.000 ou mais positivos "Male" exemplos como recomendado na orientação requisitos , existem mais de 2.000 e é razoável para tentar com que muitos antes de coletar mais dados.

min_diff_male_ds = df_to_dataset(male_pos)

Positivos "Female" exemplos, no entanto, são muito mais escassos em 385. Este é provavelmente muito pequena para um bom desempenho e assim vai exigir puxando em exemplos adicionais.

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

Usar o conjunto de dados completo mais do que triplicou o número de exemplos que podem ser usados ​​para MinDiff. Ainda está baixo, mas é o suficiente para tentar como primeira passagem.

min_diff_female_ds = df_to_dataset(augmented_female_pos)

Ambos os conjuntos de dados MinDiff são significativamente menores do que os 5.000 ou mais exemplos recomendados. Embora seja razoável tentar aplicar MinDiff com os dados atuais, pode ser necessário considerar a coleta de dados adicionais se observar um desempenho fraco ou overfitting durante o treinamento.

usando tf.data.Dataset.filter

Alternativamente, você pode criar os dois conjuntos de dados MinDiff diretamente a partir do original convertido 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()

Os resultantes alternate_min_diff_male_ds e alternate_min_diff_female_ds será equivalente à saída e min_diff_male_ds e min_diff_female_ds respectivamente.

Construindo seu conjunto de dados de treinamento

Como uma etapa final, os três conjuntos de dados (os dois recém-criados e o original) precisam ser mesclados em um único conjunto de dados que pode ser passado para o modelo.

Colocação em lote dos conjuntos de dados

Antes de mesclar, os conjuntos de dados precisam ser agrupados.

  • O conjunto de dados original pode usar o mesmo lote que foi usado antes de integrar o MinDiff.
  • Os conjuntos de dados MinDiff não precisam ter o mesmo tamanho de lote do conjunto de dados original. Com toda a probabilidade, um menor terá o mesmo desempenho. Embora nem precisem ter o mesmo tamanho de lote entre si, é recomendável fazer isso para obter o melhor desempenho.

Embora não seja estritamente necessário, recomenda-se a utilização drop_remainder=True para os dois conjuntos de dados MinDiff como este irá garantir que eles tenham o tamanho dos 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)

Os conjuntos de dados de embalagem com pack_min_diff_data

Assim que os conjuntos de dados estiverem preparados, empacote-os em um único conjunto de dados que será então repassado ao modelo. Um único lote do conjunto de dados resultante conterá um lote de cada um dos três conjuntos de dados que você preparou anteriormente.

Você pode fazer isso usando o fornecido utils função no tensorflow_model_remediation pacote:

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)

E é isso! Você será capaz de utilizar outros util funções no pacote de lotes individuais descompactar, se necessário.

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)

Com seus dados recém-formados, agora você está pronto para aplicar o MinDiff em seu modelo! Para saber como isso é feito, por favor dê uma olhada nas outras guias começando com Integrando MinDiff com MinDiffModel .

Usando um formato de embalagem personalizado (opcional)

Você pode decidir empacotar os três conjuntos de dados da maneira que quiser. O único requisito é que você precisará garantir que o modelo saiba como interpretar os dados. A implementação padrão de MinDiffModel assume que os dados foi embalado usando min_diff.keras.utils.pack_min_diff_data .

Uma maneira fácil de formatar sua entrada como você quer é transformar os dados como uma etapa final depois de ter usado 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)

Seu modelo precisa saber como ler esta entrada personalizado conforme detalhado na guia Personalizar 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 adicionais

Este guia descreve o processo e a tomada de decisão que você pode seguir sempre que aplicar o MinDiff. O restante dos guias se baseia nessa estrutura. Para tornar isso mais fácil, a lógica encontrada neste guia foi fatorada nas funções auxiliares:

  • get_uci_data : Esta função já é utilizado neste guia. Devolve uma DataFrame contendo os dados de rendimento UCI do desdobramento indicada amostrada em qualquer taxa é indicada (100%, se não especificado).
  • df_to_dataset : Esta função converte uma DataFrame num tf.data.Dataset conforme detalhado nesta guia com a funcionalidade adicional de ser capaz de passar no batch_size como um parâmetro.
  • get_uci_with_min_diff_dataset : Esta função devolve uma tf.data.Dataset contendo os dados originais e os dados MinDiff embalados em conjunto, utilizando o Modelo de reparação dos danos Biblioteca util funções como descrito neste manual.

O restante dos guias se baseará nisso para mostrar como usar outras partes da biblioteca.