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 wątpliwości 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 wykorzystania techniki MinDiff przy bardzo minimalnym przepływie pracy, a nie wytyczanie 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. Aby uzyskać więcej informacji na temat oceny dla sprawiedliwości, zobacz ten poradnik .

Ustawiać

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

Instaluje

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 tak, aby pasowały do ​​wyjściowego kształtu 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 użytecznych stałych. Będziemy ćwiczyć na modelu 'comment_text' funkcji z naszej wytwórni docelowy jako '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 Kerasa 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)

Możemy zapisać model w celu oceny korzystania rzetelności wskaźników .

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 jesteśmy po prostu zamiar przeprowadzić ocenę plasterki komentarzy odwołujących się do jednej kategorii, grup religijnych. 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)
FairnessIndicatorViewer(slicingMetrics=[{'sliceValue': 'Overall', 'slice': 'Overall', 'metrics': {'accuracy': …

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ą one 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ę skoncentrować na FPR, ponieważ wyższy FPR oznacza, że ​​komentarze odnoszą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 naprawcze, które dąży do zrównoważenia poziomu błędów w całym plasterków danych przez karanie różnic 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 nieefektywnych 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. Należy zauważyć, że w przeciwieństwie do interfejsu API modelu Keras dla Pandas DataFrames, użycie zestawów danych oznacza, że ​​musimy zapewnić funkcje wejściowe i etykiety modelu razem w jednym zestawie danych. Tutaj zapewniamy 'comment_text' jako cecha wejściowego i przekształcenia etykietę, aby dopasować oczekiwaną produkcję 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.

Utwó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

Trenować z MinDiff, wystarczy wziąć oryginalnego modelu i owinąć go w MinDiffModel z odpowiednim loss i loss_weight . Używamy 1.5 jako domyślną loss_weight , ale jest to parametr, który musi być dostrojony do przypadku użycia, ponieważ zależy od modelu i wymagań produktowych. Możesz poeksperymentować ze zmianą wartości, aby zobaczyć, jak wpływa ona na model, zauważając, że jej zwiększenie przybliża wyniki grup mniejszościowych i większościowych do siebie, 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 przykładami o bardzo małej liczbie mają bardzo szerokie przedziały ufności i nie mają przewidywalnych wyników).

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

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ę obserwowane przez nas ulepszenia i utrzymujące się dobre wyniki grupy większościowej, osiągnęliśmy oba nasze cele. W zależności od produktu mogą być konieczne dalsze ulepszenia, ale to podejście zbliżyło nasz model o krok do osiągnięcia sprawiedliwych wyników dla wszystkich użytkowników.