Ayuda a proteger la Gran Barrera de Coral con TensorFlow en Kaggle Únete Challenge

Estudio de caso de correcció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 cómo evaluar la equidad, consulte esta guía .

Configuración

Comenzamos instalando Fairness Indicators y TensorFlow Model Remediation.

Instala

Importe todos los componentes necesarios, incluidos los indicadores MinDiff y Fairness 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. 'comment_text' el modelo en la función 'comment_text' , 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)

Guardamos el modelo para evaluarlo utilizando indicadores de 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. Como recordatorio, solo vamos a realizar una evaluación dividida para los comentarios que hacen referencia a una categoría, 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 mayor. 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 deseemos 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 los comentarios que hacen referencia a estos grupos de identidad tienen más probabilidades de ser marcados incorrectamente como tóxicos que otros comentarios. Esto podría conducir a 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. Intentaremos hacerlo utilizando MinDiff , una técnica de corrección que busca equilibrar las tasas de error en partes de sus datos al penalizar 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 buscaremos 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 contradictorio extraer conjuntos de ejemplos negativos de verdad fundamental si nos preocupan principalmente las disparidades en la tasa de falsos positivos , pero recuerde que una predicción de falso positivo es un ejemplo negativo de verdad fundamental que se clasifica incorrectamente como positivo, que es el problema 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 característica de entrada y reformamos 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 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 usando 1.5 como el loss_weight predeterminado, pero este es un parámetro que debe ajustarse para su caso de uso, ya que depende de su modelo y los requisitos del producto. 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, necesitamos seleccionar un umbral de la misma manera que lo haríamos con el modelo de línea 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.