Fallstudie zur Modellsanierung

In diesem Notizbuch trainieren wir einen Textklassifizierer, um geschriebenen Inhalt zu identifizieren, der als giftig oder schädlich angesehen werden könnte, und wenden MinDiff an, um einige Bedenken hinsichtlich der Fairness auszuräumen. In unserem Workflow werden wir:

  1. Bewerten Sie die Leistung unseres Basismodells für Text, der Verweise auf sensible Gruppen enthält.
  2. Verbessern Sie die Leistung bei leistungsschwachen Gruppen, indem Sie mit MinDiff trainieren.
  3. Bewerten Sie die Leistung des neuen Modells anhand unserer ausgewählten Metrik.

Unser Ziel ist es, die Verwendung der MinDiff-Technik mit einem sehr minimalen Workflow zu demonstrieren, und nicht, einen prinzipiellen Ansatz für Fairness beim maschinellen Lernen zu entwickeln. Daher konzentriert sich unsere Bewertung nur auf eine sensible Kategorie und einen einzelnen Messwert. Wir adressieren auch weder potenzielle Mängel im Dataset noch optimieren wir unsere Konfigurationen. In einer Produktionsumgebung möchten Sie jedes dieser Elemente mit Strenge angehen. Weitere Informationen zur Fairnessbewertung finden Sie in diesem Leitfaden .

Einrichten

Wir beginnen mit der Installation von Fairness Indicators und TensorFlow Model Remediation.

Installiert

Importieren Sie alle notwendigen Komponenten, einschließlich MinDiff und Fairness Indicators zur Auswertung.

Importe

Wir verwenden eine Dienstprogrammfunktion, um die vorverarbeiteten Daten herunterzuladen und die Beschriftungen so vorzubereiten, dass sie der Ausgabeform des Modells entsprechen. Die Funktion lädt die Daten auch als TFRecords herunter, um eine spätere Auswertung zu beschleunigen. Alternativ können Sie den Pandas DataFrame mit jeder verfügbaren Utility-Konvertierungsfunktion in TFRecords konvertieren.

# 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

Wir definieren einige nützliche Konstanten. Wir trainieren das Modell mit der Funktion 'comment_text' mit unserer Zielbezeichnung 'toxicity' . Beachten Sie, dass die Batchgröße hier willkürlich gewählt wird, aber in einer Produktionsumgebung müssen Sie sie für die beste Leistung optimieren.

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

Legen Sie zufällige Samen fest. (Beachten Sie, dass dies die Ergebnisse nicht vollständig stabilisiert.)

Saat

Definieren und trainieren Sie das Basismodell

Um die Laufzeit zu reduzieren, verwenden wir standardmäßig ein vortrainiertes Modell. Es ist ein einfaches sequentielles Keras-Modell mit einer anfänglichen Einbettungs- und Faltungsschicht, das eine Toxizitätsvorhersage ausgibt. Wenn Sie möchten, können Sie dies ändern und von Grund auf mit unserer Utility-Funktion trainieren, um das Modell zu erstellen. (Beachten Sie, dass Sie die Optimierungs- und Bewertungsschwellenwerte anpassen müssen, da sich Ihre Umgebung wahrscheinlich von unserer unterscheidet.)

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)

Wir speichern das Modell, um es anhand von Fairness-Indikatoren zu bewerten.

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

Als nächstes führen wir Fairness-Indikatoren durch. Zur Erinnerung, wir führen nur eine Slice-Auswertung für Kommentare durch, die sich auf eine Kategorie beziehen, religiöse Gruppen . In einer Produktionsumgebung empfehlen wir, einen durchdachten Ansatz zu wählen, um zu bestimmen, welche Kategorien und Metriken übergreifend ausgewertet werden sollen.

Um die Modellleistung zu berechnen, trifft die Dienstprogrammfunktion einige bequeme Auswahlmöglichkeiten für Metriken, Slices und Klassifikatorschwellenwerte.

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

Bewertungsergebnisse rendern

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

Schauen wir uns die Bewertungsergebnisse an. Versuchen Sie, die Metrik-False-Positive-Rate (FPR) mit dem Schwellenwert 0,450 auszuwählen. Wir können sehen, dass das Modell für einige religiöse Gruppen nicht so gut abschneidet wie für andere, da es eine viel höhere FPR aufweist. Beachten Sie die breiten Konfidenzintervalle für einige Gruppen, da es zu wenige Beispiele gibt. Dies macht es schwierig, mit Sicherheit zu sagen, dass es für diese Slices einen signifikanten Unterschied in der Leistung gibt. Vielleicht möchten wir weitere Beispiele sammeln, um dieses Problem anzugehen. Wir können jedoch versuchen, MinDiff für die beiden Gruppen anzuwenden, von denen wir überzeugt sind, dass sie unterdurchschnittlich abschneiden.

Wir haben uns entschieden, uns auf FPR zu konzentrieren, da ein höherer FPR bedeutet, dass Kommentare, die auf diese Identitätsgruppen verweisen, eher fälschlicherweise als toxisch gekennzeichnet werden als andere Kommentare. Dies könnte zu ungerechten Ergebnissen für Benutzer führen, die einen Dialog über Religion führen. Beachten Sie jedoch, dass Ungleichheiten bei anderen Metriken zu anderen Arten von Schaden führen können.

Definieren und trainieren Sie das MinDiff-Modell

Jetzt werden wir versuchen, die FPR für leistungsschwache religiöse Gruppen zu verbessern. Wir versuchen dies mit MinDiff , einer Korrekturtechnik , die versucht, die Fehlerraten über Ihre Datenabschnitte hinweg auszugleichen , indem Leistungsunterschiede während des Trainings bestraft werden . Wenn wir MinDiff anwenden, kann sich die Modellleistung auf anderen Slices geringfügig verschlechtern. Daher werden unsere Ziele mit MinDiff sein:

  • Verbesserte Leistung für leistungsschwache Gruppen
  • Begrenzte Verschlechterung für andere Gruppen und Gesamtleistung

Bereiten Sie Ihre Daten vor

Um MinDiff zu verwenden, erstellen wir zwei zusätzliche Datensplits:

  • Eine Aufteilung für nicht toxische Beispiele, die sich auf Minderheitengruppen beziehen: In unserem Fall umfasst dies Kommentare mit Verweisen auf unsere leistungsschwachen Identitätsbegriffe. Wir schließen einige der Gruppen nicht ein, da es zu wenige Beispiele gibt, was zu einer höheren Unsicherheit mit breiten Konfidenzintervallbereichen führt.
  • Eine Aufteilung für nicht toxische Beispiele, die sich auf die Mehrheitsgruppe beziehen.

Es ist wichtig, genügend Beispiele zu haben, die zu den leistungsschwachen Klassen gehören. Je nach Modellarchitektur, Datenverteilung und MinDiff-Konfiguration kann die benötigte Datenmenge erheblich variieren. In früheren Anwendungen haben wir gesehen, dass MinDiff mit 5.000 Beispielen in jedem Datensplit gut funktioniert.

In unserem Fall haben die Gruppen in den Minderheitensplits Beispielmengen von 9.688 und 3.906. Beachten Sie die Klassenungleichgewichte im Datensatz; In der Praxis könnte dies Anlass zur Sorge geben, aber wir werden nicht versuchen, sie in diesem Notebook zu behandeln, da wir nur MinDiff demonstrieren möchten.

Wir wählen nur negative Beispiele für diese Gruppen aus, damit MinDiff diese Beispiele richtig optimieren kann. Es mag kontraintuitiv erscheinen, Sätze von Ground-Truth- Negativ- Beispielen herauszuarbeiten, wenn wir uns in erster Linie mit Unterschieden in der Falsch-Positiv-Rate befassen, aber denken Sie daran, dass eine falsch-positive Vorhersage ein Ground-Truth-Negativ-Beispiel ist, das fälschlicherweise als positiv klassifiziert wird versuche zu adressieren.

MinDiff-DataFrames erstellen

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

Wir müssen auch unsere Pandas DataFrames in Tensorflow-Datasets für MinDiff-Eingaben konvertieren. Beachten Sie, dass im Gegensatz zur Keras-Modell-API für Pandas DataFrames die Verwendung von Datensätzen bedeutet, dass wir die Eingabefunktionen und Beschriftungen des Modells zusammen in einem Datensatz bereitstellen müssen. Hier stellen wir den 'comment_text' als Eingabe-Feature 'comment_text' und formen die Beschriftung so um, dass sie der erwarteten Ausgabe des Modells entspricht.

Wir stapeln den Datensatz auch in dieser Phase, da MinDiff stapelweise Datensätze erfordert. Beachten Sie, dass wir die Auswahl der Batchgröße auf die gleiche Weise wie für das Basismodell abstimmen, wobei die Trainingsgeschwindigkeit und Hardwareaspekte berücksichtigt werden, während die Modellleistung ausgeglichen wird. Hier haben wir für alle drei Datensätze dieselbe Batchgröße gewählt, dies ist jedoch keine Voraussetzung, obwohl es sich bewährt, die beiden MinDiff-Batchgrößen gleich zu setzen.

MinDiff-Datensätze erstellen

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

Trainieren und bewerten Sie das Modell

Um mit MinDiff zu trainieren, nimm einfach das Originalmodell und wickle es in ein MinDiffModel mit entsprechendem loss und loss_weight . Wir verwenden 1.5 als Standard loss_weight , aber dies ist ein Parameter, der für Ihren Anwendungsfall angepasst werden muss, da er von Ihrem Modell und Ihren Produktanforderungen abhängt. Sie können mit der Änderung des Werts experimentieren, um zu sehen, wie sich dies auf das Modell auswirkt. Beachten Sie, dass eine Erhöhung die Leistung der Minderheits- und Mehrheitsgruppen näher zusammenbringt, aber möglicherweise mit stärkeren Kompromissen verbunden ist.

Dann kompilieren wir das Modell normal (unter Verwendung des regulären Nicht-MinDiff-Verlustes) und passen es an das Training an.

Trainieren Sie das MinDiff-Modell

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

Als nächstes werten wir die Ergebnisse aus.

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

Um sicherzustellen, dass wir ein neues Modell korrekt bewerten, müssen wir einen Schwellenwert auf die gleiche Weise wie beim Basismodell auswählen. In einer Produktionsumgebung würde dies bedeuten, dass die Bewertungsmetriken den Einführungsstandards entsprechen. In unserem Fall wählen wir den Schwellenwert aus, der zu einer ähnlichen Gesamt-FPR wie beim Basismodell führt. Dieser Schwellenwert kann sich von dem unterscheiden, den Sie für das Basismodell ausgewählt haben. Versuchen Sie, die Falsch-Positiv-Rate mit dem Schwellenwert 0,400 auszuwählen. (Beachten Sie, dass die Untergruppen mit sehr geringen Mengenbeispielen sehr breite Konfidenzintervalle aufweisen und keine vorhersagbaren Ergebnisse haben.)

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

Wenn Sie diese Ergebnisse überprüfen, werden Sie möglicherweise feststellen, dass sich die FPRs für unsere Zielgruppen verbessert haben. Der Abstand zwischen unserer leistungsschwächsten Gruppe und der Mehrheitsgruppe hat sich von 0,024 auf 0,006 verbessert. Angesichts der beobachteten Verbesserungen und der anhaltend starken Leistung der Mehrheitsgruppe haben wir unsere beiden Ziele erreicht. Je nach Produkt können weitere Verbesserungen erforderlich sein, aber dieser Ansatz hat unser Modell der gleichen Leistung für alle Benutzer einen Schritt näher gebracht.