Estudio de caso de remediación de modelos

En este cuaderno, capacitaremos a un clasificador de texto para identificar contenido escrito que podría considerarse tóxico o dañino, y aplicaremos MinDiff para remediar algunos problemas de equidad. En nuestro flujo de trabajo, haremos lo siguiente:

  1. Evalúe el desempeño de nuestro modelo de línea de base en el texto que contiene referencias a grupos sensibles.
  2. Mejore el rendimiento en cualquier grupo de bajo rendimiento entrenando con MinDiff.
  3. Evalúe el rendimiento del nuevo modelo en nuestra métrica elegida.

Nuestro propósito es demostrar el uso de la técnica MinDiff con un flujo de trabajo mínimo, no establecer un enfoque basado en principios para la equidad en el aprendizaje automático. Como tal, nuestra evaluación solo se centrará en una categoría sensible y una única métrica. Tampoco abordamos las posibles deficiencias en el conjunto de datos ni ajustamos nuestras configuraciones. En un entorno de producción, querrá abordar cada uno de estos con rigor. Para obtener más información sobre la evaluación de la equidad, consulte esta guía .

Configuración

Comenzamos instalando Fairness Indicators y TensorFlow Model Remediation.

Instala

Importe todos los componentes necesarios, incluidos MinDiff y los indicadores de equidad para la evaluación.

Importaciones

Usamos una función de utilidad para descargar los datos preprocesados ​​y preparar las etiquetas para que coincidan con la forma de salida del modelo. La función también descarga los datos como TFRecords para agilizar la evaluación posterior. Alternativamente, puede convertir Pandas DataFrame en TFRecords con cualquier función de conversión de utilidad disponible.

# We use a helper utility to preprocessed data for convenience and speed.
data_train, data_validate, validate_tfrecord_file, labels_train, labels_validate = min_diff_keras_utils.download_and_process_civil_comments_data()
Downloading data from https://storage.googleapis.com/civil_comments_dataset/train_df_processed.csv
345702400/345699197 [==============================] - 8s 0us/step
Downloading data from https://storage.googleapis.com/civil_comments_dataset/validate_df_processed.csv
229974016/229970098 [==============================] - 5s 0us/step
Downloading data from https://storage.googleapis.com/civil_comments_dataset/validate_tf_processed.tfrecord
324943872/324941336 [==============================] - 9s 0us/step

Definimos algunas constantes útiles. Vamos a entrenar el modelo de la 'comment_text' característica, con nuestra etiqueta de destino como 'toxicity' . Tenga en cuenta que el tamaño del lote aquí se elige arbitrariamente, pero en un entorno de producción necesitaría ajustarlo para obtener el mejor rendimiento.

TEXT_FEATURE = 'comment_text'
LABEL = 'toxicity'
BATCH_SIZE = 512

Establecer semillas al azar. (Tenga en cuenta que esto no estabiliza completamente los resultados).

Semillas

Definir y entrenar el modelo de línea base

Para reducir el tiempo de ejecución, usamos un modelo previamente entrenado por defecto. Es un modelo secuencial simple de Keras con capas de convolución e incrustación inicial, que genera una predicción de toxicidad. Si lo prefiere, puede cambiar esto y entrenar desde cero usando nuestra función de utilidad para crear el modelo. (Tenga en cuenta que, dado que su entorno probablemente sea diferente al nuestro, deberá personalizar los umbrales de ajuste y evaluación).

use_pretrained_model = True

if use_pretrained_model:
  URL = 'https://storage.googleapis.com/civil_comments_model/baseline_model.zip'
  BASE_PATH = tempfile.mkdtemp()
  ZIP_PATH = os.path.join(BASE_PATH, 'baseline_model.zip')
  MODEL_PATH = os.path.join(BASE_PATH, 'tmp/baseline_model')

  r = requests.get(URL, allow_redirects=True)
  open(ZIP_PATH, 'wb').write(r.content)

  with zipfile.ZipFile(ZIP_PATH, 'r') as zip_ref:
    zip_ref.extractall(BASE_PATH)
  baseline_model = tf.keras.models.load_model(
      MODEL_PATH, custom_objects={'KerasLayer' : hub.KerasLayer})
else:
  optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
  loss = tf.keras.losses.BinaryCrossentropy()

  baseline_model = min_diff_keras_utils.create_keras_sequential_model()

  baseline_model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])

  baseline_model.fit(x=data_train[TEXT_FEATURE],
                     y=labels_train,
                     batch_size=BATCH_SIZE,
                     epochs=20)

Ahorramos el modelo con el fin de evaluar el uso de indicadores Equidad .

base_dir = tempfile.mkdtemp(prefix='saved_models')
baseline_model_location = os.path.join(base_dir, 'model_export_baseline')
baseline_model.save(baseline_model_location, save_format='tf')
INFO:tensorflow:Assets written to: /tmp/saved_models867b8d74/model_export_baseline/assets
INFO:tensorflow:Assets written to: /tmp/saved_models867b8d74/model_export_baseline/assets

A continuación, ejecutamos los indicadores de equidad. A modo de recordatorio, sólo vamos a realizar la evaluación rebanado por los comentarios que hacen referencia a una categoría, los grupos religiosos. En un entorno de producción, recomendamos adoptar un enfoque reflexivo para determinar qué categorías y métricas evaluar.

Para calcular el rendimiento del modelo, la función de utilidad hace algunas elecciones convenientes para métricas, porciones y umbrales de clasificador.

# We use a helper utility to hide the evaluation logic for readability.
base_dir = tempfile.mkdtemp(prefix='eval')
eval_dir = os.path.join(base_dir, 'tfma_eval_result')
eval_result = fi_util.get_eval_results(
    baseline_model_location, eval_dir, validate_tfrecord_file)
WARNING:absl:Tensorflow version (2.5.0) found. Note that TFMA support for TF 2.0 is currently in beta
WARNING:apache_beam.runners.interactive.interactive_environment:Dependencies required for Interactive Beam PCollection visualization are not available, please use: `pip install apache-beam[interactive]` to install necessary dependencies to enable all data visualization features.
WARNING:apache_beam.io.tfrecordio:Couldn't find python-snappy so the implementation of _TFRecordUtil._masked_crc32c is not as fast as it could be.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:113: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:113: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

Renderizar resultados de evaluación

widget_view.render_fairness_indicator(eval_result)
FairnessIndicatorViewer(slicingMetrics=[{'sliceValue': 'Overall', 'slice': 'Overall', 'metrics': {'accuracy': …

Veamos los resultados de la evaluación. Intente seleccionar la tasa métrica de falsos positivos (FPR) con un umbral de 0,450. Podemos ver que el modelo no funciona tan bien para algunos grupos religiosos como para otros, mostrando un FPR mucho más alto. Tenga en cuenta los amplios intervalos de confianza en algunos grupos porque tienen muy pocos ejemplos. Esto hace que sea difícil decir con certeza que existe una diferencia significativa en el rendimiento de estos cortes. Es posible que queramos recopilar más ejemplos para abordar este problema. Sin embargo, podemos intentar aplicar MinDiff para los dos grupos que estamos seguros de que tienen un rendimiento inferior.

Hemos optado por centrarnos en FPR, porque un FPR más alto significa que es más probable que los comentarios que hagan referencia a estos grupos de identidad se marquen incorrectamente como tóxicos que otros comentarios. Esto podría generar resultados desiguales para los usuarios que entablan un diálogo sobre religión, pero tenga en cuenta que las disparidades en otras métricas pueden generar otros tipos de daños.

Definir y entrenar el modelo MinDiff

Ahora, intentaremos mejorar el FPR para los grupos religiosos de bajo rendimiento. Vamos a tratar de hacerlo a través de MinDiff , una técnica de recuperación que busca el equilibrio entre las tasas de error a través de rebanadas de sus datos, penalizando a las disparidades en el rendimiento durante el entrenamiento. Cuando aplicamos MinDiff, el rendimiento del modelo puede degradarse ligeramente en otros sectores. Como tal, nuestros objetivos con MinDiff serán:

  • Rendimiento mejorado para grupos de bajo rendimiento
  • Degradación limitada para otros grupos y rendimiento general

Prepara tus datos

Para usar MinDiff, creamos dos divisiones de datos adicionales:

  • Una división de ejemplos no tóxicos que hacen referencia a grupos minoritarios: en nuestro caso, esto incluirá comentarios con referencias a nuestros términos de identidad de bajo rendimiento. No incluimos algunos de los grupos porque hay muy pocos ejemplos, lo que genera una mayor incertidumbre con amplios rangos de intervalo de confianza.
  • Una división de ejemplos no tóxicos que hacen referencia al grupo mayoritario.

Es importante tener suficientes ejemplos que pertenezcan a las clases de bajo rendimiento. Según la arquitectura de su modelo, la distribución de datos y la configuración de MinDiff, la cantidad de datos necesarios puede variar significativamente. En aplicaciones anteriores, hemos visto que MinDiff funciona bien con 5000 ejemplos en cada división de datos.

En nuestro caso, los grupos en las divisiones minoritarias tienen cantidades de ejemplo de 9.688 y 3.906. Tenga en cuenta los desequilibrios de clases en el conjunto de datos; en la práctica, esto podría ser motivo de preocupación, pero no intentaremos abordarlos en este cuaderno, ya que nuestra intención es solo demostrar MinDiff.

Seleccionamos solo ejemplos negativos para estos grupos, de modo que MinDiff pueda optimizar para obtener estos ejemplos correctamente. Puede parecer contrario a la intuición para labrarse conjuntos de ejemplos negativos terreno la verdad si nos ocupa principalmente de las disparidades en la tasa de falsos positivos, pero recuerda que una predicción falso positivo es un ejemplo de la verdad de tierra negativo que está incorrectamente clasificados como positivos, que es el que tema estás tratando de abordar.

Crear MinDiff DataFrames

# Create masks for the sensitive and nonsensitive groups
minority_mask = data_train.religion.apply(
    lambda x: any(religion in x for religion in ('jewish', 'muslim')))
majority_mask = data_train.religion.apply(lambda x: x == "['christian']")

# Select nontoxic examples, so MinDiff will be able to reduce sensitive FP rate.
true_negative_mask = data_train['toxicity'] == 0

data_train_main = copy.copy(data_train)
data_train_sensitive = data_train[minority_mask & true_negative_mask]
data_train_nonsensitive = data_train[majority_mask & true_negative_mask]

También necesitamos convertir nuestros Pandas DataFrames en Tensorflow Datasets para la entrada MinDiff. Tenga en cuenta que, a diferencia de la API del modelo Keras para Pandas DataFrames, el uso de conjuntos de datos significa que debemos proporcionar las características de entrada y las etiquetas del modelo juntas en un conjunto de datos. Aquí proporcionamos el 'comment_text' como una función de entrada y remodelar la etiqueta para que coincida con la salida esperada del modelo.

También procesamos por lotes el conjunto de datos en esta etapa, ya que MinDiff requiere conjuntos de datos por lotes. Tenga en cuenta que ajustamos la selección del tamaño de lote de la misma manera que se ajusta para el modelo de línea de base, teniendo en cuenta la velocidad de entrenamiento y las consideraciones de hardware mientras se equilibra con el rendimiento del modelo. Aquí hemos elegido el mismo tamaño de lote para los tres conjuntos de datos, pero esto no es un requisito, aunque es una buena práctica que los dos tamaños de lote de MinDiff sean equivalentes.

Crear conjuntos de datos MinDiff

# Convert the pandas DataFrames to Datasets.
dataset_train_main = tf.data.Dataset.from_tensor_slices(
    (data_train_main['comment_text'].values, 
     data_train_main.pop(LABEL).values.reshape(-1,1) * 1.0)).batch(BATCH_SIZE)
dataset_train_sensitive = tf.data.Dataset.from_tensor_slices(
    (data_train_sensitive['comment_text'].values, 
     data_train_sensitive.pop(LABEL).values.reshape(-1,1) * 1.0)).batch(BATCH_SIZE)
dataset_train_nonsensitive = tf.data.Dataset.from_tensor_slices(
    (data_train_nonsensitive['comment_text'].values, 
     data_train_nonsensitive.pop(LABEL).values.reshape(-1,1) * 1.0)).batch(BATCH_SIZE)

Entrenar y evaluar el modelo

Para entrenar con MinDiff, sólo tiene que tomar el modelo original y se envuelve en una MinDiffModel con la correspondiente loss y loss_weight . Estamos utilizando 1.5 como predeterminado loss_weight , pero este es un parámetro que necesita ser sintonizado para su caso de uso, ya que depende de sus necesidades de productos y modelo. Puede experimentar cambiando el valor para ver cómo afecta el modelo, teniendo en cuenta que aumentarlo empuja el rendimiento de los grupos minoritarios y mayoritarios más juntos, pero puede conllevar compensaciones más pronunciadas.

Luego compilamos el modelo normalmente (usando la pérdida regular que no es MinDiff) y lo ajustamos para entrenar.

Tren MinDiffModel

use_pretrained_model = True

base_dir = tempfile.mkdtemp(prefix='saved_models')
min_diff_model_location = os.path.join(base_dir, 'model_export_min_diff')

if use_pretrained_model:
  BASE_MIN_DIFF_PATH = tempfile.mkdtemp()
  MIN_DIFF_URL = 'https://storage.googleapis.com/civil_comments_model/min_diff_model.zip'
  ZIP_PATH = os.path.join(BASE_PATH, 'min_diff_model.zip')
  MIN_DIFF_MODEL_PATH = os.path.join(BASE_MIN_DIFF_PATH, 'tmp/min_diff_model')
  DIRPATH = '/tmp/min_diff_model'

  r = requests.get(MIN_DIFF_URL, allow_redirects=True)
  open(ZIP_PATH, 'wb').write(r.content)

  with zipfile.ZipFile(ZIP_PATH, 'r') as zip_ref:
    zip_ref.extractall(BASE_MIN_DIFF_PATH)
  min_diff_model = tf.keras.models.load_model(
      MIN_DIFF_MODEL_PATH, custom_objects={'KerasLayer' : hub.KerasLayer})

  min_diff_model.save(min_diff_model_location, save_format='tf')

else:
  min_diff_weight = 1.5

  # Create the dataset that will be passed to the MinDiffModel during training.
  dataset = md.keras.utils.input_utils.pack_min_diff_data(
      dataset_train_main, dataset_train_sensitive, dataset_train_nonsensitive)

  # Create the original model.
  original_model = min_diff_keras_utils.create_keras_sequential_model()

  # Wrap the original model in a MinDiffModel, passing in one of the MinDiff
  # losses and using the set loss_weight.
  min_diff_loss = md.losses.MMDLoss()
  min_diff_model = md.keras.MinDiffModel(original_model,
                                         min_diff_loss,
                                         min_diff_weight)

  # Compile the model normally after wrapping the original model.  Note that
  # this means we use the baseline's model's loss here.
  optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
  loss = tf.keras.losses.BinaryCrossentropy()
  min_diff_model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])

  min_diff_model.fit(dataset, epochs=20)

  min_diff_model.save_original_model(min_diff_model_location, save_format='tf')
INFO:tensorflow:Assets written to: /tmp/saved_modelsb3zkcos_/model_export_min_diff/assets
INFO:tensorflow:Assets written to: /tmp/saved_modelsb3zkcos_/model_export_min_diff/assets

A continuación evaluamos los resultados.

min_diff_eval_subdir = os.path.join(base_dir, 'tfma_eval_result')
min_diff_eval_result = fi_util.get_eval_results(
    min_diff_model_location,
    min_diff_eval_subdir,
    validate_tfrecord_file,
    slice_selection='religion')
WARNING:absl:Tensorflow version (2.5.0) found. Note that TFMA support for TF 2.0 is currently in beta

Para asegurarnos de que evaluamos un nuevo modelo correctamente, debemos seleccionar un umbral de la misma manera que lo haríamos con el modelo de línea de base. En un entorno de producción, esto significaría garantizar que las métricas de evaluación cumplan con los estándares de lanzamiento. En nuestro caso, elegiremos el umbral que resulte en un FPR general similar al modelo de línea de base. Este umbral puede ser diferente del que seleccionó para el modelo de línea de base. Intente seleccionar una tasa de falsos positivos con un umbral de 0,400. (Tenga en cuenta que los subgrupos con ejemplos de cantidades muy bajas tienen intervalos de rango de confianza muy amplios y no tienen resultados predecibles).

widget_view.render_fairness_indicator(min_diff_eval_result)
FairnessIndicatorViewer(slicingMetrics=[{'sliceValue': 'Overall', 'slice': 'Overall', 'metrics': {'accuracy': …

Al revisar estos resultados, puede notar que los FPR de nuestros grupos objetivo han mejorado. La brecha entre nuestro grupo de menor desempeño y el grupo mayoritario ha mejorado de .024 a .006. Dadas las mejoras que hemos observado y el sólido desempeño continuo del grupo mayoritario, hemos cumplido nuestros dos objetivos. Dependiendo del producto, pueden ser necesarias más mejoras, pero este enfoque ha hecho que nuestro modelo esté un paso más cerca de funcionar de manera equitativa para todos los usuarios.