Dzień Społeczności ML jest 9 listopada! Dołącz do nas na aktualizacje z TensorFlow Jax i więcej Dowiedz się więcej

Modelowe studium przypadku naprawy

W tym notatniku nauczymy klasyfikatora tekstu, aby identyfikował pisemną treść, która może być uznana za toksyczną lub szkodliwą, i zastosujemy MinDiff, aby rozwiązać niektóre problemy dotyczące sprawiedliwości. W naszym przepływie pracy będziemy:

  1. Oceń skuteczność naszego modelu podstawowego w tekście zawierającym odniesienia do wrażliwych grup.
  2. Popraw wydajność w każdej grupie o słabych wynikach, trenując z MinDiff.
  3. Oceń wydajność nowego modelu na podstawie wybranego przez nas wskaźnika.

Naszym celem jest zademonstrowanie użycia techniki MinDiff przy bardzo minimalnym przepływie pracy, a nie nakreślenie pryncypialnego podejścia do uczciwości w uczeniu maszynowym. W związku z tym nasza ocena skupi się tylko na jednej wrażliwej kategorii i jednej metryce. Nie zajmujemy się również potencjalnymi niedociągnięciami w zestawie danych ani nie dostrajamy naszych konfiguracji. W środowisku produkcyjnym chciałbyś podejść do każdego z nich z rygorem. Więcej informacji na temat oceny uczciwości można znaleźć w tym przewodniku .

Ustawiać

Zaczynamy od zainstalowania wskaźników uczciwości i naprawy modelu TensorFlow.

Instalacje

Importuj wszystkie niezbędne komponenty, w tym MinDiff i Fairness Indicators do oceny.

Import

Korzystamy z funkcji narzędziowej, aby pobrać wstępnie przetworzone dane i przygotować etykiety, aby pasowały do ​​kształtu wyjściowego modelu. Funkcja pobiera również dane jako TFRecords, aby przyspieszyć późniejszą ocenę. Alternatywnie możesz przekonwertować Pandas DataFrame na TFRecords za pomocą dowolnej dostępnej funkcji konwersji narzędzia.

# 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

Definiujemy kilka przydatnych stałych. Wytrenujemy model w zakresie funkcji 'comment_text' z etykietą docelową 'toxicity' . Zwróć uwagę, że rozmiar partii jest tutaj wybierany arbitralnie, ale w ustawieniach produkcyjnych musisz go dostroić, aby uzyskać najlepszą wydajność.

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

Ustaw losowe nasiona. (Zauważ, że nie zapewnia to pełnej stabilizacji wyników.)

Posiew

Zdefiniuj i wytrenuj model podstawowy

Aby skrócić czas wykonywania, domyślnie używamy wstępnie wytrenowanego modelu. Jest to prosty sekwencyjny model Keras z początkowymi warstwami osadzania i konwolucji, dający prognozę toksyczności. Jeśli wolisz, możesz to zmienić i trenować od podstaw, korzystając z naszej funkcji użytkowej do tworzenia modelu. (Pamiętaj, że ponieważ Twoje środowisko prawdopodobnie różni się od naszego, musisz dostosować progi dostrajania i oceny).

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)

Zapisujemy model w celu oceny za pomocą wskaźników rzetelności .

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

Następnie uruchamiamy Wskaźniki Uczciwości. Przypominamy, że w przypadku komentarzy odnoszących się do jednej kategorii, grup religijnych , przeprowadzimy tylko wycinkową ocenę. W środowisku produkcyjnym zalecamy rozważne podejście do określenia kategorii i metryk do oceny.

Aby obliczyć wydajność modelu, funkcja narzędziowa dokonuje kilku wygodnych wyborów dotyczących metryk, wycinków i progów klasyfikatorów.

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

Renderuj wyniki oceny

widget_view.render_fairness_indicator(eval_result)
03beeacc0

Spójrzmy na wyniki oceny. Spróbuj wybrać wskaźnik metryki fałszywych trafień (FPR) z progiem 0,450. Widzimy, że model nie sprawdza się tak dobrze w przypadku niektórych grup religijnych, jak w przypadku innych, wykazując znacznie wyższy współczynnik FPR. Zwróć uwagę na szerokie przedziały ufności w niektórych grupach, ponieważ mają zbyt mało przykładów. To sprawia, że ​​trudno jest z całą pewnością stwierdzić, że istnieje znaczna różnica w wydajności tych wycinków. Możemy zebrać więcej przykładów, aby rozwiązać ten problem. Możemy jednak spróbować zastosować MinDiff dla dwóch grup, co do których jesteśmy pewni, że radzą sobie słabiej.

Zdecydowaliśmy się skupić na FPR, ponieważ wyższy FPR oznacza, że ​​komentarze odwołujące się do tych grup tożsamości będą częściej błędnie oznaczane jako toksyczne niż inne komentarze. Może to prowadzić do niesprawiedliwych wyników dla użytkowników prowadzących dialog na temat religii, ale należy pamiętać, że rozbieżności w innych wskaźnikach mogą prowadzić do innych rodzajów szkód.

Zdefiniuj i wytrenuj model MinDiff

Teraz postaramy się ulepszyć FPR dla słabszych grup religijnych. Spróbujemy to zrobić za pomocą MinDiff , techniki naprawczej, która ma na celu zrównoważenie współczynników błędów w poszczególnych wycinkach danych poprzez karanie rozbieżności w wydajności podczas treningu. Kiedy zastosujemy MinDiff, wydajność modelu może się nieznacznie pogorszyć na innych przekrojach. W związku z tym nasze cele z MinDiff będą następujące:

  • Poprawiona wydajność dla grup o słabych wynikach
  • Ograniczona degradacja dla innych grup i ogólna wydajność

Przygotuj swoje dane

Aby użyć MinDiff, tworzymy dwa dodatkowe podziały danych:

  • Podział na nietoksyczne przykłady odnoszące się do grup mniejszościowych: w naszym przypadku będzie to zawierało komentarze z odniesieniami do naszych niesłusznych terminów dotyczących tożsamości. Nie uwzględniamy niektórych grup, ponieważ jest zbyt mało przykładów, co prowadzi do większej niepewności z szerokimi zakresami przedziałów ufności.
  • Podział na nietoksyczne przykłady odnoszące się do grupy większościowej.

Ważne jest, aby mieć wystarczającą liczbę przykładów należących do słabszych klas. W zależności od architektury modelu, dystrybucji danych i konfiguracji MinDiff ilość potrzebnych danych może się znacznie różnić. W poprzednich aplikacjach widzieliśmy, że MinDiff działa dobrze z 5000 przykładów w każdym podziale danych.

W naszym przypadku grupy w podziałach mniejszościowych mają przykładowe ilości 9688 i 3906. Zwróć uwagę na niezrównoważenia klas w zbiorze danych; w praktyce może to być powodem do niepokoju, ale nie będziemy starać się omawiać ich w tym zeszycie, ponieważ naszą intencją jest tylko zademonstrowanie MinDiff.

Wybieramy tylko negatywne przykłady dla tych grup, aby MinDiff mógł zoptymalizować te przykłady. Może się to wydawać sprzeczne z intuicją, aby wykroić zestawy przykładów negatywnych ziemia prawdy, jeśli obawiasz się przede wszystkim z różnic w fałszywie dodatnich, ale należy pamiętać, że fałszywie dodatni przewidywania jest przykład ziemia prawda ujemny, który jest nieprawidłowo sklasyfikowany jako pozytywny, co jest nam problem próbujesz rozwiązać.

Utwórz ramki danych MinDiff

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

Musimy również przekonwertować nasze DataFrames Pandy na zestawy danych Tensorflow dla danych wejściowych MinDiff. Zwróć uwagę, że w przeciwieństwie do interfejsu API modelu Keras dla Pandas DataFrames, użycie zestawów danych oznacza, że ​​musimy dostarczyć funkcje wejściowe i etykiety modelu razem w jednym zestawie danych. W tym miejscu udostępniamy 'comment_text' jako funkcję wejściową i zmieniamy kształt etykiety, aby pasowała do oczekiwanych wyników modelu.

Na tym etapie również zestawiamy zestaw danych, ponieważ MinDiff wymaga zestawów danych wsadowych. Zwróć uwagę, że dostrajamy wybór rozmiaru partii w taki sam sposób, w jaki jest dostrajany dla modelu bazowego, biorąc pod uwagę szybkość uczenia i kwestie sprzętowe podczas równoważenia wydajności modelu. Tutaj wybraliśmy ten sam rozmiar wsadu dla wszystkich trzech zestawów danych, ale nie jest to wymagane, chociaż dobrą praktyką jest, aby dwa rozmiary wsadu MinDiff były równoważne.

Twórz zbiory danych 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)

Trenuj i oceniaj model

Aby trenować z MinDiff, po prostu weź oryginalny model i zapakuj go w MinDiffModel z odpowiednią loss i loss_weight . Używamy 1.5 jako domyślnego parametru loss_weight , ale jest to parametr, który należy dostosować do przypadku użycia, ponieważ zależy to od modelu i wymagań produktu. Możesz poeksperymentować ze zmianą wartości, aby zobaczyć, jak wpływa ona na model, zauważając, że zwiększenie jej przybliża wyniki grup mniejszościowych i większościowych, ale może wiązać się z wyraźniejszymi kompromisami.

Następnie kompilujemy model normalnie (używając zwykłej straty innej niż MinDiff) i dopasowujemy do trenowania.

Model pociągu MinDiff

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

Następnie oceniamy wyniki.

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

Aby zapewnić prawidłową ocenę nowego modelu, musimy wybrać próg w taki sam sposób, jak w przypadku modelu bazowego. W środowisku produkcyjnym oznaczałoby to zapewnienie, że metryki oceny spełniają standardy uruchamiania. W naszym przypadku wybierzemy próg, który skutkuje podobnym ogólnym FPR do modelu podstawowego. Ten próg może różnić się od wybranego dla modelu podstawowego. Spróbuj wybrać współczynnik fałszywych trafień z progiem 0,400. (Zauważ, że podgrupy z bardzo małą ilością przykładów mają bardzo szerokie przedziały ufności i nie mają przewidywalnych wyników).

widget_view.render_fairness_indicator(min_diff_eval_result)
03beeacc0

Przeglądając te wyniki, możesz zauważyć, że FPR dla naszych grup docelowych uległy poprawie. Różnica między naszą grupą o najniższych wynikach a grupą większościową poprawiła się z 0,024 do 0,006. Biorąc pod uwagę zaobserwowane przez nas usprawnienia i utrzymujące się dobre wyniki grupy większościowej, osiągnęliśmy oba nasze cele. W zależności od produktu, dalsze ulepszenia mogą być konieczne, ale to podejście zbliżyło nasz model o krok do osiągnięcia sprawiedliwych wyników dla wszystkich użytkowników.