Przykład optymalizacji ograniczonej TensorFlow z wykorzystaniem zestawu danych CelebA

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl w serwisie GitHub Pobierz notatnik

Ten notatnik przedstawia łatwy sposób tworzenia i optymalizacji ograniczonych problemów przy użyciu biblioteki TFCO. Ta metoda może być przydatna w ulepszaniu modeli, gdy stwierdzimy, że nie działają one równie dobrze w różnych wycinkach naszych danych, które możemy zidentyfikować za pomocą wskaźników uczciwości . Druga z zasad sztucznej inteligencji Google mówi, że nasza technologia powinna unikać tworzenia lub wzmacniania nieuczciwych uprzedzeń i uważamy, że ta technika może pomóc poprawić sprawiedliwość modelu w niektórych sytuacjach. W szczególności ten notatnik:

  • tf.keras prosty, nieograniczony model sieci neuronowej, aby wykrywać uśmiech osoby na obrazach za pomocą tf.keras i dużego zbioru danych CelebFaces Attributes ( CelebA ).
  • Oceń wydajność modelu pod kątem powszechnie stosowanej miary sprawiedliwości w różnych grupach wiekowych, używając wskaźników uczciwości.
  • Skonfiguruj prosty, ograniczony problem optymalizacji, aby osiągnąć sprawiedliwsze wyniki we wszystkich grupach wiekowych.
  • Ponownie wytrenuj teraz ograniczony model i ponownie oceń wydajność, upewniając się, że wybrana miara rzetelności uległa poprawie.

Ostatnia aktualizacja: 3/11 lutego 2020 r

Instalacja

Ten notatnik został utworzony w Colaboratory , połączony z backendem Python 3 Google Compute Engine. Jeśli chcesz hostować ten notatnik w innym środowisku, nie powinieneś napotkać żadnych poważnych problemów, pod warunkiem, że umieścisz wszystkie wymagane pakiety w poniższych komórkach.

Zwróć uwagę, że przy pierwszym uruchomieniu instalacji pip może zostać wyświetlony monit o ponowne uruchomienie środowiska wykonawczego z powodu preinstalowanych nieaktualnych pakietów. Gdy to zrobisz, zostaną użyte odpowiednie pakiety.

Pip instaluje

Zwróć uwagę, że w zależności od tego, kiedy uruchomisz poniższą komórkę, możesz wkrótce otrzymać ostrzeżenie o przełączeniu domyślnej wersji TensorFlow w Colab na TensorFlow 2.X. Możesz bezpiecznie zignorować to ostrzeżenie, ponieważ ten notebook został zaprojektowany tak, aby był zgodny z TensorFlow 1.X i 2.X.

Importuj moduły

Dodatkowo dodajemy kilka importów, które są specyficzne dla wskaźników uczciwości, których użyjemy do oceny i wizualizacji wydajności modelu.

Chociaż TFCO jest kompatybilny z przyspieszonym wykonywaniem wykresów, ten notatnik zakłada, że ​​przyspieszone wykonywanie jest domyślnie włączone, tak jak w TensorFlow 2.x. Aby mieć pewność, że nic się nie zepsuje, w komórce poniżej zostanie włączone przyspieszone wykonywanie.

Włącz Eager Execution i Print Versions

Eager execution enabled by default.
TensorFlow 2.4.1
TFMA 0.29.0
TFDS 4.2.0
FI 0.29.0

Zestaw danych CelebA

CelebA to duży zbiór danych o atrybutach twarzy, zawierający ponad 200 000 zdjęć celebrytów, z których każdy zawiera 40 adnotacji atrybutów (takich jak rodzaj włosów, akcesoria modowe, rysy twarzy itp.) Oraz 5 charakterystycznych lokalizacji (pozycja oczu, ust i nosa). Aby uzyskać więcej informacji, zapoznaj się z artykułem . Za zgodą właścicieli przechowujemy ten zbiór danych w Google Cloud Storage i uzyskujemy do niego dostęp głównie za pośrednictwem TensorFlow Datasets ( tfds ) .

W tym notatniku:

  • Nasz model spróbuje sklasyfikować, czy osoba na zdjęciu się uśmiecha, co reprezentuje atrybut „Uśmiecha się” * .
  • Obrazy zostaną przeskalowane z 218x178 do 28x28, aby skrócić czas wykonywania i skrócić pamięć podczas treningu.
  • Wyniki naszego modelu zostaną ocenione w różnych grupach wiekowych przy użyciu binarnego atrybutu „Young”. W tym notatniku będziemy nazywać tę „grupę wiekową”.

* Chociaż dostępnych jest niewiele informacji na temat metodologii etykietowania dla tego zbioru danych, zakładamy, że atrybut „Uśmiech” został określony przez zadowolony, miły lub rozbawiony wyraz twarzy badanego. Na potrzeby tego studium przypadku przyjmiemy te etykiety jako prawdę podstawową.

gcs_base_dir = "gs://celeb_a_dataset/"
celeb_a_builder = tfds.builder("celeb_a", data_dir=gcs_base_dir, version='2.0.0')

celeb_a_builder.download_and_prepare()

num_test_shards_dict = {'0.3.0': 4, '2.0.0': 2} # Used because we download the test dataset separately
version = str(celeb_a_builder.info.version)
print('Celeb_A dataset version: %s' % version)
Celeb_A dataset version: 2.0.0

Testuj funkcje pomocnicze zestawu danych

Ostrzeżenia

Zanim przejdziemy do przodu, należy pamiętać o kilku kwestiach podczas korzystania z CelebA:

  • Chociaż w zasadzie ten notatnik może wykorzystywać dowolny zestaw danych przedstawiający twarze, wybrano firmę CelebA, ponieważ zawiera ona obrazy osób publicznych należących do domeny publicznej.
  • Wszystkie adnotacje atrybutów w CelebA są operacjonalizowane jako kategorie binarne. Na przykład atrybut „Young” (określony przez etykiety zbioru danych) jest oznaczony jako obecny lub nieobecny na obrazie.
  • Kategoryzacje CelebA nie odzwierciedlają prawdziwej ludzkiej różnorodności atrybutów.
  • Na potrzeby tego notatnika funkcja zawierająca atrybut „Young” jest określana jako „grupa wiekowa”, w przypadku gdy obecność atrybutu „Young” na zdjęciu jest oznaczona jako członek grupy wiekowej „Young”, a brak atrybutu „Young” jest oznaczany jako członek grupy wiekowej „Not Young”. Są to założenia przyjęte, ponieważ informacje te nie są wymienione w oryginalnym artykule .
  • W związku z tym wydajność modeli wytrenowanych w tym notatniku jest powiązana ze sposobami operacjonalizacji atrybutów i dodania ich adnotacji przez autorów CelebA.
  • Ten model nie powinien być używany do celów komercyjnych, ponieważ naruszyłoby to umowę dotyczącą badań niekomercyjnych CelebA .

Konfigurowanie funkcji wejściowych

Kolejne komórki pomogą usprawnić potok wejściowy, a także zwizualizować wydajność.

Najpierw definiujemy pewne zmienne związane z danymi i definiujemy wymaganą funkcję przetwarzania wstępnego.

Zdefiniuj zmienne

Zdefiniuj funkcje przetwarzania wstępnego

Następnie tworzymy funkcje danych, których potrzebujemy w pozostałej części colabu.

# Train data returning either 2 or 3 elements (the third element being the group)
def celeb_a_train_data_wo_group(batch_size):
  celeb_a_train_data = celeb_a_builder.as_dataset(split='train').shuffle(1024).repeat().batch(batch_size).map(preprocess_input_dict)
  return celeb_a_train_data.map(get_image_and_label)
def celeb_a_train_data_w_group(batch_size):
  celeb_a_train_data = celeb_a_builder.as_dataset(split='train').shuffle(1024).repeat().batch(batch_size).map(preprocess_input_dict)
  return celeb_a_train_data.map(get_image_label_and_group)

# Test data for the overall evaluation
celeb_a_test_data = celeb_a_builder.as_dataset(split='test').batch(1).map(preprocess_input_dict).map(get_image_label_and_group)
# Copy test data locally to be able to read it into tfma
copy_test_files_to_local()

Zbuduj prosty model DNN

Ponieważ ten notatnik koncentruje się na TFCO, stworzymy prosty, nieograniczony model tf.keras.Sequential .

Możemy być w stanie znacznie poprawić wydajność modelu, dodając pewną złożoność (np. Bardziej gęsto połączone warstwy, badając różne funkcje aktywacji, zwiększając rozmiar obrazu), ale może to odwracać uwagę od celu, jakim jest pokazanie, jak łatwo jest zastosować bibliotekę TFCO podczas pracy z Kerasem. Z tego powodu model będzie prosty - ale zachęcamy do eksploracji tej przestrzeni.

def create_model():
  # For this notebook, accuracy will be used to evaluate performance.
  METRICS = [
    tf.keras.metrics.BinaryAccuracy(name='accuracy')
  ]

  # The model consists of:
  # 1. An input layer that represents the 28x28x3 image flatten.
  # 2. A fully connected layer with 64 units activated by a ReLU function.
  # 3. A single-unit readout layer to output real-scores instead of probabilities.
  model = keras.Sequential([
      keras.layers.Flatten(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), name='image'),
      keras.layers.Dense(64, activation='relu'),
      keras.layers.Dense(1, activation=None)
  ])

  # TFCO by default uses hinge loss — and that will also be used in the model.
  model.compile(
      optimizer=tf.keras.optimizers.Adam(0.001),
      loss='hinge',
      metrics=METRICS)
  return model

Definiujemy również funkcję ustawiania nasion, aby zapewnić powtarzalne wyniki. Zauważ, że ta kolumna jest pomyślana jako narzędzie edukacyjne i nie ma stabilności precyzyjnie dostrojonego potoku produkcyjnego. Bieganie bez wysiewu nasion może prowadzić do różnych wyników.

def set_seeds():
  np.random.seed(121212)
  tf.compat.v1.set_random_seed(212121)

Funkcje pomocnicze wskaźników uczciwości

Przed szkoleniem naszego modelu definiujemy szereg funkcji pomocniczych, które pozwolą nam ocenić wydajność modelu za pomocą wskaźników uczciwości.

Najpierw tworzymy funkcję pomocniczą, aby zapisać nasz model po jego wytrenowaniu.

def save_model(model, subdir):
  base_dir = tempfile.mkdtemp(prefix='saved_models')
  model_location = os.path.join(base_dir, subdir)
  model.save(model_location, save_format='tf')
  return model_location

Następnie definiujemy funkcje używane do wstępnego przetwarzania danych, aby poprawnie przekazać je do TFMA.

Funkcje przetwarzania wstępnego danych dla platformy

Na koniec definiujemy funkcję, która ocenia wyniki w TFMA.

def get_eval_results(model_location, eval_subdir):
  base_dir = tempfile.mkdtemp(prefix='saved_eval_results')
  tfma_eval_result_path = os.path.join(base_dir, eval_subdir)

  eval_config_pbtxt = """
        model_specs {
          label_key: "%s"
        }
        metrics_specs {
          metrics {
            class_name: "FairnessIndicators"
            config: '{ "thresholds": [0.22, 0.5, 0.75] }'
          }
          metrics {
            class_name: "ExampleCount"
          }
        }
        slicing_specs {}
        slicing_specs { feature_keys: "%s" }
        options {
          compute_confidence_intervals { value: False }
          disabled_outputs{values: "analysis"}
        }
      """ % (LABEL_KEY, GROUP_KEY)

  eval_config = text_format.Parse(eval_config_pbtxt, tfma.EvalConfig())

  eval_shared_model = tfma.default_eval_shared_model(
        eval_saved_model_path=model_location, tags=[tf.saved_model.SERVING])

  schema_pbtxt = """
        tensor_representation_group {
          key: ""
          value {
            tensor_representation {
              key: "%s"
              value {
                dense_tensor {
                  column_name: "%s"
                  shape {
                    dim { size: 28 }
                    dim { size: 28 }
                    dim { size: 3 }
                  }
                }
              }
            }
          }
        }
        feature {
          name: "%s"
          type: FLOAT
        }
        feature {
          name: "%s"
          type: FLOAT
        }
        feature {
          name: "%s"
          type: BYTES
        }
        """ % (IMAGE_KEY, IMAGE_KEY, IMAGE_KEY, LABEL_KEY, GROUP_KEY)
  schema = text_format.Parse(schema_pbtxt, schema_pb2.Schema())
  coder = tf_example_record.TFExampleBeamRecord(
      physical_format='inmem', schema=schema,
      raw_record_column_name=tfma.ARROW_INPUT_COLUMN)
  tensor_adapter_config = tensor_adapter.TensorAdapterConfig(
    arrow_schema=coder.ArrowSchema(),
    tensor_representations=coder.TensorRepresentations())
  # Run the fairness evaluation.
  with beam.Pipeline() as pipeline:
    _ = (
          tfds_as_pcollection(pipeline, 'celeb_a', 'test')
          | 'ExamplesToRecordBatch' >> coder.BeamSource()
          | 'ExtractEvaluateAndWriteResults' >>
          tfma.ExtractEvaluateAndWriteResults(
              eval_config=eval_config,
              eval_shared_model=eval_shared_model,
              output_path=tfma_eval_result_path,
              tensor_adapter_config=tensor_adapter_config)
    )
  return tfma.load_eval_result(output_path=tfma_eval_result_path)

Trenuj i oceniaj model nieograniczony

Po zdefiniowaniu modelu i przygotowaniu potoku wejściowego jesteśmy teraz gotowi do trenowania naszego modelu. Aby zmniejszyć ilość czasu wykonywania i pamięci, wytrenujemy model, dzieląc dane na małe partie z zaledwie kilkoma powtórzonymi iteracjami.

Należy pamiętać, że uruchomienie tego notatnika w TensorFlow <2.0.0 może spowodować ostrzeżenie o np.where dla np.where . Bezpiecznie zignoruj ​​to ostrzeżenie, ponieważ TensorFlow rozwiązuje ten problem w wersji 2.X, używając tf.where zamiast np.where .

BATCH_SIZE = 32

# Set seeds to get reproducible results
set_seeds()

model_unconstrained = create_model()
model_unconstrained.fit(celeb_a_train_data_wo_group(BATCH_SIZE), epochs=5, steps_per_epoch=1000)
Epoch 1/5
1000/1000 [==============================] - 17s 11ms/step - loss: 0.6219 - accuracy: 0.7189
Epoch 2/5
1000/1000 [==============================] - 10s 10ms/step - loss: 0.4061 - accuracy: 0.8187
Epoch 3/5
1000/1000 [==============================] - 10s 10ms/step - loss: 0.3649 - accuracy: 0.8391
Epoch 4/5
1000/1000 [==============================] - 16s 16ms/step - loss: 0.3427 - accuracy: 0.8485
Epoch 5/5
1000/1000 [==============================] - 10s 10ms/step - loss: 0.3390 - accuracy: 0.8482
<tensorflow.python.keras.callbacks.History at 0x7f47c01a8550>

Ocena modelu na danych testowych powinna dać ostateczny wynik dokładności nieco ponad 85%. Nieźle jak na prosty model bez dostrajania.

print('Overall Results, Unconstrained')
celeb_a_test_data = celeb_a_builder.as_dataset(split='test').batch(1).map(preprocess_input_dict).map(get_image_label_and_group)
results = model_unconstrained.evaluate(celeb_a_test_data)
Overall Results, Unconstrained
19962/19962 [==============================] - 40s 2ms/step - loss: 0.2125 - accuracy: 0.8636

Jednak wyniki oceniane w różnych grupach wiekowych mogą ujawnić pewne niedociągnięcia.

Aby lepiej to zbadać, oceniamy model za pomocą wskaźników uczciwości (za pośrednictwem TFMA). W szczególności interesuje nas, czy istnieje znacząca luka w wynikach między kategoriami „Młody” i „Nie młody”, gdy ocenia się go na podstawie wskaźnika fałszywie pozytywnych wyników.

Błąd fałszywie dodatni występuje, gdy model nieprawidłowo przewiduje klasę dodatnią. W tym kontekście fałszywie pozytywny wynik ma miejsce, gdy podstawową prawdą jest obraz celebryty „nie uśmiechniętego”, a model przewiduje „uśmiech”. W związku z tym współczynnik fałszywie pozytywnych wyników, który jest używany w powyższej wizualizacji, jest miarą dokładności testu. Chociaż jest to względnie przyziemny błąd w tym kontekście, błędy fałszywie pozytywne mogą czasami powodować bardziej problematyczne zachowania. Na przykład fałszywie dodatni błąd w klasyfikatorze spamu może spowodować, że użytkownik przegapi ważną wiadomość e-mail.

model_location = save_model(model_unconstrained, 'model_export_unconstrained')
eval_results_unconstrained = get_eval_results(model_location, 'eval_results_unconstrained')
INFO:tensorflow:Assets written to: /tmp/saved_modelseqklzviu/model_export_unconstrained/assets
INFO:tensorflow:Assets written to: /tmp/saved_modelseqklzviu/model_export_unconstrained/assets
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.6/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.6/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)`

Jak wspomniano powyżej, koncentrujemy się na odsetku wyników fałszywie pozytywnych. Bieżąca wersja wskaźników uczciwości (0.1.2) domyślnie wybiera współczynnik fałszywie ujemny. Po uruchomieniu poniższej linii odznacz wartość false_negative_rate i wybierz false_positive_rate, aby spojrzeć na metrykę, która nas interesuje.

tfma.addons.fairness.view.widget_view.render_fairness_indicator(eval_results_unconstrained)
FairnessIndicatorViewer(slicingMetrics=[{'sliceValue': 'Overall', 'slice': 'Overall', 'metrics': {'example_cou…

Jak pokazują powyższe wyniki, widzimy nieproporcjonalną lukę między kategoriami „Młody” i „Nie młody” .

W tym przypadku TFCO może pomóc, ograniczając odsetek wyników fałszywie pozytywnych do bardziej akceptowalnego kryterium.

Konfiguracja modelu ograniczonego

Jak udokumentowano w bibliotece TFCO , istnieje kilka pomocników, które ułatwią ograniczenie problemu:

  1. tfco.rate_context() - to będzie to, co zostanie użyte przy konstruowaniu ograniczenia dla każdej kategorii grupy wiekowej.
  2. tfco.RateMinimizationProblem() - Wyrażenie współczynnika, które ma zostać tutaj zminimalizowane, będzie fałszywie dodatnim wynikiem w zależności od grupy wiekowej. Innymi słowy, wyniki będą teraz oceniane na podstawie różnicy między fałszywie dodatnimi wskaźnikami grupy wiekowej i całego zbioru danych. Na potrzeby tej demonstracji jako ograniczenie zostanie ustalony współczynnik fałszywie pozytywnych wyników mniejszy lub równy 5%.
  3. tfco.ProxyLagrangianOptimizerV2() - jest to pomocnik, który faktycznie rozwiąże problem ograniczenia szybkości.

Poniższa komórka wezwie tych pomocników do skonfigurowania szkolenia modelowego z ograniczeniem sprawiedliwości.

# The batch size is needed to create the input, labels and group tensors.
# These tensors are initialized with all 0's. They will eventually be assigned
# the batch content to them. A large batch size is chosen so that there are
# enough number of "Young" and "Not Young" examples in each batch.
set_seeds()
model_constrained = create_model()
BATCH_SIZE = 32

# Create input tensor.
input_tensor = tf.Variable(
    np.zeros((BATCH_SIZE, IMAGE_SIZE, IMAGE_SIZE, 3), dtype="float32"),
    name="input")

# Create labels and group tensors (assuming both labels and groups are binary).
labels_tensor = tf.Variable(
    np.zeros(BATCH_SIZE, dtype="float32"), name="labels")
groups_tensor = tf.Variable(
    np.zeros(BATCH_SIZE, dtype="float32"), name="groups")

# Create a function that returns the applied 'model' to the input tensor
# and generates constrained predictions.
def predictions():
  return model_constrained(input_tensor)

# Create overall context and subsetted context.
# The subsetted context contains subset of examples where group attribute < 1
# (i.e. the subset of "Not Young" celebrity images).
# "groups_tensor < 1" is used instead of "groups_tensor == 0" as the former
# would be a comparison on the tensor value, while the latter would be a
# comparison on the Tensor object.
context = tfco.rate_context(predictions, labels=lambda:labels_tensor)
context_subset = context.subset(lambda:groups_tensor < 1)

# Setup list of constraints.
# In this notebook, the constraint will just be: FPR to less or equal to 5%.
constraints = [tfco.false_positive_rate(context_subset) <= 0.05]

# Setup rate minimization problem: minimize overall error rate s.t. constraints.
problem = tfco.RateMinimizationProblem(tfco.error_rate(context), constraints)

# Create constrained optimizer and obtain train_op.
# Separate optimizers are specified for the objective and constraints
optimizer = tfco.ProxyLagrangianOptimizerV2(
      optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
      constraint_optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
      num_constraints=problem.num_constraints)

# A list of all trainable variables is also needed to use TFCO.
var_list = (model_constrained.trainable_weights + list(problem.trainable_variables) +
            optimizer.trainable_variables())

Model jest teraz skonfigurowany i gotowy do trenowania z ograniczeniem odsetka fałszywie dodatnich wyników w całej grupie wiekowej.

Teraz, ponieważ ostatnia iteracja modelu z ograniczeniami niekoniecznie musi być najlepiej działającym modelem pod względem zdefiniowanego ograniczenia, biblioteka TFCO jest wyposażona w tfco.find_best_candidate_index() która może pomóc wybrać najlepszą iterację spośród tych znalezionych po każdym epoka. Pomyśl o tfco.find_best_candidate_index() jako o dodatkowej heurystyce, która szereguje każdy z wyników na podstawie dokładności i ograniczenia uczciwości (w tym przypadku fałszywie dodatnich w grupie wiekowej) oddzielnie w odniesieniu do danych szkoleniowych. W ten sposób może szukać lepszego kompromisu między ogólną dokładnością a ograniczeniem dotyczącym sprawiedliwości.

Następujące komórki rozpoczną trening z ograniczeniami, jednocześnie znajdując model o najlepszej wydajności na iterację.

# Obtain train set batches.

NUM_ITERATIONS = 100  # Number of training iterations.
SKIP_ITERATIONS = 10  # Print training stats once in this many iterations.

# Create temp directory for saving snapshots of models.
temp_directory = tempfile.mktemp()
os.mkdir(temp_directory)

# List of objective and constraints across iterations.
objective_list = []
violations_list = []

# Training iterations.
iteration_count = 0
for (image, label, group) in celeb_a_train_data_w_group(BATCH_SIZE):
  # Assign current batch to input, labels and groups tensors.
  input_tensor.assign(image)
  labels_tensor.assign(label)
  groups_tensor.assign(group)

  # Run gradient update.
  optimizer.minimize(problem, var_list=var_list)

  # Record objective and violations.
  objective = problem.objective()
  violations = problem.constraints()

  sys.stdout.write(
      "\r Iteration %d: Hinge Loss = %.3f, Max. Constraint Violation = %.3f"
      % (iteration_count + 1, objective, max(violations)))

  # Snapshot model once in SKIP_ITERATIONS iterations.
  if iteration_count % SKIP_ITERATIONS == 0:
    objective_list.append(objective)
    violations_list.append(violations)

    # Save snapshot of model weights.
    model_constrained.save_weights(
        temp_directory + "/celeb_a_constrained_" +
        str(iteration_count / SKIP_ITERATIONS) + ".h5")

  iteration_count += 1
  if iteration_count >= NUM_ITERATIONS:
    break

# Choose best model from recorded iterates and load that model.
best_index = tfco.find_best_candidate_index(
    np.array(objective_list), np.array(violations_list))

model_constrained.load_weights(
    temp_directory + "/celeb_a_constrained_" + str(best_index) + ".0.h5")

# Remove temp directory.
os.system("rm -r " + temp_directory)
Iteration 100: Hinge Loss = 0.614, Max. Constraint Violation = 0.268
0

Po zastosowaniu ograniczenia ponownie oceniamy wyniki za pomocą wskaźników uczciwości.

model_location = save_model(model_constrained, 'model_export_constrained')
eval_result_constrained = get_eval_results(model_location, 'eval_results_constrained')
INFO:tensorflow:Assets written to: /tmp/saved_modelsrnjadh_e/model_export_constrained/assets
INFO:tensorflow:Assets written to: /tmp/saved_modelsrnjadh_e/model_export_constrained/assets

Podobnie jak poprzednio, gdy używaliśmy wskaźników uczciwości, odznacz wartość false_negative_rate i wybierz false_positive_rate, aby spojrzeć na metrykę, która nas interesuje.

Zwróć uwagę, że aby uczciwie porównać dwie wersje naszego modelu, ważne jest, aby użyć progów, które ustawiają ogólny współczynnik fałszywych trafień na mniej więcej równy. Gwarantuje to, że patrzymy na rzeczywistą zmianę, a nie tylko zmianę w modelu, równoważną po prostu przesunięciu granicy progu. W naszym przypadku porównanie modelu nieograniczonego przy 0,5 i modelu z ograniczeniami przy 0,22 zapewnia rzetelne porównanie modeli.

eval_results_dict = {
    'constrained': eval_result_constrained,
    'unconstrained': eval_results_unconstrained,
}
tfma.addons.fairness.view.widget_view.render_fairness_indicator(multi_eval_results=eval_results_dict)
FairnessIndicatorViewer(evalName='constrained', evalNameCompare='unconstrained', slicingMetrics=[{'sliceValue'…

Dzięki zdolności TFCO do wyrażenia bardziej złożonego wymagania jako ograniczenia szybkości, pomogliśmy temu modelowi osiągnąć bardziej pożądany wynik przy niewielkim wpływie na ogólną wydajność. Oczywiście jest jeszcze miejsce na ulepszenia, ale przynajmniej TFCO było w stanie znaleźć model, który jest bliski spełnienia ograniczenia i maksymalnie zmniejsza rozbieżności między grupami.