Exemple d'optimisation contrainte TensorFlow à l'aide de l'ensemble de données CelebA

Voir sur TensorFlow.org Exécuter dans Google Colab Voir sur GitHub Télécharger le cahier

Ce bloc-notes montre un moyen simple de créer et d'optimiser des problèmes contraints à l'aide de la bibliothèque TFCO. Cette méthode peut être utile pour l' amélioration des modèles lorsque nous constatons qu'ils ne sont pas d' effectuer aussi bien entre les différentes tranches de nos données, nous pouvons identifier à l' aide d' indicateurs d' équité . Le deuxième des principes d'IA de Google stipule que notre technologie doit éviter de créer ou de renforcer des préjugés injustes, et nous pensons que cette technique peut aider à améliorer l'équité du modèle dans certaines situations. En particulier, ce bloc-notes :

  • Former un simple modèle de réseau de neurones sans contrainte pour détecter le sourire d'une personne dans des images en utilisant tf.keras et les CelebFaces à grande échelle Attributs ( CelebA ensemble de données).
  • Évaluez les performances du modèle par rapport à une mesure d'équité couramment utilisée dans tous les groupes d'âge, à l'aide d'indicateurs d'équité.
  • Configurez un problème d'optimisation contraint simple pour obtenir des performances plus équitables dans toutes les tranches d'âge.
  • Reformer le modèle maintenant contraint et évaluer la performance à nouveau, faire en sorte que notre mesure d'équité choisie est améliorée.

Dernière mise à jour : 3/11 février 2020

Installation

Ce portable a été créé en Colaboratory , connecté au Python 3 back - end Google Compute Engine. Si vous souhaitez héberger ce bloc-notes dans un environnement différent, vous ne devriez pas rencontrer de problèmes majeurs à condition d'inclure tous les packages requis dans les cellules ci-dessous.

Notez que la toute première fois que vous exécutez les installations pip, il peut vous être demandé de redémarrer l'environnement d'exécution en raison de packages préinstallés obsolètes. Une fois que vous l'aurez fait, les bons packages seront utilisés.

Pip s'installe

Notez que selon le moment où vous exécutez la cellule ci-dessous, vous pouvez recevoir un avertissement concernant la version par défaut de TensorFlow dans Colab passant bientôt à TensorFlow 2.X. Vous pouvez ignorer cet avertissement en toute sécurité, car ce portable a été conçu pour être compatible avec TensorFlow 1.X et 2.X.

Importer des modules

De plus, nous ajoutons quelques importations spécifiques aux indicateurs d'équité que nous utiliserons pour évaluer et visualiser les performances du modèle.

Bien que TFCO soit compatible avec l'exécution rapide et graphique, ce bloc-notes suppose que l'exécution rapide est activée par défaut, comme c'est le cas dans TensorFlow 2.x. Pour s'assurer que rien ne se casse, l'exécution rapide sera activée dans la cellule ci-dessous.

Activer l'exécution impatiente et les versions imprimées

Eager execution enabled by default.
TensorFlow 2.8.0-rc0
TFMA 0.36.0
TFDS 4.4.0
FI 0.36.0

Ensemble de données CelebA

CelebA est un visage à grande échelle les attributs ensemble de données avec plus de 200.000 images de célébrités, chacune avec 40 annotations d'attributs (tels que le type de cheveux, accessoires de mode, les traits du visage, etc.) et 5 lieux historiques ( les yeux, la bouche et les positions du nez). Pour plus de détails consultez le document . Avec la permission des propriétaires, nous avons enregistré ce jeu de données sur Google Cloud Storage et accéder à la plupart via tensorflow (datasets tfds ) .

Dans ce cahier :

  • Notre modèle tentera de classer si le sujet de l'image est souriante, représentée par l'attribut « Sourire » *.
  • Les images seront redimensionnées de 218x178 à 28x28 pour réduire le temps d'exécution et la mémoire lors de l'entraînement.
  • Les performances de notre modèle seront évaluées dans tous les groupes d'âge, à l'aide de l'attribut binaire « Jeune ». Nous appellerons cette « tranche d'âge » dans ce cahier.

* Bien qu'il y ait peu d' informations sur la méthodologie d'étiquetage pour cet ensemble de données, nous supposons que le « Sourire » attribut a été déterminé par une sorte heureuse, ou expression amusée sur le visage du sujet. Aux fins de cette étude de cas, nous considérerons ces étiquettes comme une vérité fondamentale.

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

Tester les fonctions d'assistance de l'ensemble de données

Mises en garde

Avant d'aller de l'avant, il y a plusieurs considérations à garder à l'esprit lors de l'utilisation de CelebA :

  • Bien qu'en principe ce bloc-notes puisse utiliser n'importe quel ensemble de données d'images de visage, CelebA a été choisi car il contient des images du domaine public de personnalités publiques.
  • Toutes les annotations d'attributs dans CelebA sont opérationnalisées en tant que catégories binaires. Par exemple, l'attribut « Jeune » (tel que déterminé par les étiqueteurs de l'ensemble de données) est indiqué comme présent ou absent dans l'image.
  • Les catégorisations de CelebA ne reflètent pas la vraie diversité humaine des attributs.
  • Aux fins de ce cahier, la caractéristique contenant l'attribut « Jeune » est appelée « groupe d'âge », où la présence de l'attribut « Jeune » dans une image est étiquetée comme un membre du groupe d'âge « Jeune » et le l'absence de l'attribut « Jeune » est étiqueté comme un membre du groupe d'âge « Pas jeune ». Ce sont des hypothèses que ces informations ne sont pas mentionnés dans le document initial .
  • En tant que tel, les performances des modèles formés dans ce cahier sont liées à la manière dont les attributs ont été opérationnalisés et annotés par les auteurs de CelebA.
  • Ce modèle ne doit pas être utilisé à des fins commerciales comme qui serait contraire à l' accord de recherche non commerciale CelebA .

Configuration des fonctions d'entrée

Les cellules suivantes aideront à rationaliser le pipeline d'entrée et à visualiser les performances.

Tout d'abord, nous définissons certaines variables liées aux données et définissons une fonction de prétraitement requise.

Définir des variables

Définir les fonctions de prétraitement

Ensuite, nous développons les fonctions de données dont nous avons besoin dans le reste de la collaboration.

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

Construire un modèle DNN simple

Parce que ce portable se concentre sur TFCO, nous allons assembler un simple, sans contrainte tf.keras.Sequential modèle.

Nous pouvons être en mesure d'améliorer considérablement les performances du modèle en ajoutant une certaine complexité (par exemple, des couches plus densément connectées, l'exploration de différentes fonctions d'activation, l'augmentation de la taille de l'image), mais cela peut détourner l'attention de l'objectif de démontrer à quel point il est facile d'appliquer la bibliothèque TFCO lorsque vous travaillez avec Keras. Pour cette raison, le modèle restera simple, mais n'hésitez pas à explorer cet espace.

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

Nous définissons également une fonction pour définir des graines afin d'assurer des résultats reproductibles. Notez que ce colab est conçu comme un outil pédagogique et n'a pas la stabilité d'un pipeline de production finement réglé. Courir sans définir une graine peut conduire à des résultats variés.

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

Fonctions d'assistance des indicateurs d'équité

Avant d'entraîner notre modèle, nous définissons un certain nombre de fonctions d'assistance qui nous permettront d'évaluer les performances du modèle via des indicateurs d'équité.

Tout d'abord, nous créons une fonction d'assistance pour enregistrer notre modèle une fois que nous l'avons formé.

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

Ensuite, nous définissons les fonctions utilisées pour prétraiter les données afin de les transmettre correctement à TFMA.

Fonctions de prétraitement des données pour

Enfin, nous définissons une fonction qui évalue les résultats dans 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)

Former et évaluer un modèle sans contrainte

Avec le modèle maintenant défini et le pipeline d'entrée en place, nous sommes maintenant prêts à entraîner notre modèle. Pour réduire le temps d'exécution et la mémoire, nous allons entraîner le modèle en découpant les données en petits lots avec seulement quelques itérations répétées.

Notez que l' exécution de ce bloc - notes dans tensorflow <2.0.0 peut entraîner un avertissement pour deprecation np.where . Ignorer cet avertissement en toute sécurité comme tensorflow aborde cette 2.x en utilisant tf.where en place de 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 [==============================] - 12s 6ms/step - loss: 0.5038 - accuracy: 0.7733
Epoch 2/5
1000/1000 [==============================] - 7s 7ms/step - loss: 0.3800 - accuracy: 0.8301
Epoch 3/5
1000/1000 [==============================] - 6s 6ms/step - loss: 0.3598 - accuracy: 0.8427
Epoch 4/5
1000/1000 [==============================] - 25s 25ms/step - loss: 0.3435 - accuracy: 0.8474
Epoch 5/5
1000/1000 [==============================] - 5s 5ms/step - loss: 0.3402 - accuracy: 0.8479
<keras.callbacks.History at 0x7f0f5c476350>

L'évaluation du modèle sur les données de test devrait aboutir à un score de précision final d'un peu plus de 85 %. Pas mal pour un modèle simple sans réglage fin.

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 [==============================] - 50s 2ms/step - loss: 0.2125 - accuracy: 0.8636

Cependant, les performances évaluées dans tous les groupes d'âge peuvent révéler certaines lacunes.

Pour approfondir cela, nous évaluons le modèle avec des indicateurs d'équité (via TFMA). En particulier, nous sommes intéressés à voir s'il existe un écart de performance significatif entre les catégories « Jeune » et « Pas jeune » lorsqu'il est évalué sur le taux de faux positifs.

Une erreur faussement positive se produit lorsque le modèle prédit de manière incorrecte la classe positive. Dans ce contexte, un résultat faussement positif se produit lorsque la vérité fondamentale est une image d'une célébrité « pas souriante » et que le modèle prédit « souriante ». Par extension, le taux de faux positifs, qui est utilisé dans la visualisation ci-dessus, est une mesure de la précision d'un test. Bien qu'il s'agisse d'une erreur relativement banale à commettre dans ce contexte, les erreurs faussement positives peuvent parfois entraîner des comportements plus problématiques. Par exemple, une erreur faussement positive dans un classificateur de spam pourrait faire manquer un e-mail important à un utilisateur.

model_location = save_model(model_unconstrained, 'model_export_unconstrained')
eval_results_unconstrained = get_eval_results(model_location, 'eval_results_unconstrained')
2022-01-07 18:46:05.881112: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/saved_modelswhxcqdry/model_export_unconstrained/assets
INFO:tensorflow:Assets written to: /tmp/saved_modelswhxcqdry/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:root:Make sure that locally built Python SDK docker image has Python 3.7 interpreter.
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:107: 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:107: 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)`

Comme mentionné ci-dessus, nous nous concentrons sur le taux de faux positifs. La version actuelle des indicateurs d'équité (0.1.2) sélectionne le taux de faux négatifs par défaut. Après avoir exécuté la ligne ci-dessous, désélectionnez false_negative_rate et sélectionnez false_positive_rate pour examiner la métrique qui nous intéresse.

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

Comme les résultats ci - dessus montrent, on ne voit un écart disproportionné entre les catégories « jeunes » et « non jeunes ».

C'est là que TFCO peut aider en limitant le taux de faux positifs à un critère plus acceptable.

Configuration du modèle contraint

Comme indiqué dans la bibliothèque de TFCO , il y a plusieurs aides qui rendront plus facile de contraindre le problème:

  1. tfco.rate_context() - C'est ce qui sera utilisé dans la construction d' une contrainte pour chaque catégorie de groupe d'âge.
  2. tfco.RateMinimizationProblem() - L'expression de taux à minimiser ici sera le taux de faux positifs soumis à un groupe d'âge. En d'autres termes, les performances seront désormais évaluées en fonction de la différence entre les taux de faux positifs du groupe d'âge et celui de l'ensemble de données global. Pour cette démonstration, un taux de faux positifs inférieur ou égal à 5% sera fixé comme contrainte.
  3. tfco.ProxyLagrangianOptimizerV2() - Ceci est l'aide qui va résoudre effectivement le problème de contrainte de taux.

La cellule ci-dessous fera appel à ces assistants pour mettre en place un apprentissage du modèle avec la contrainte d'équité.

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

Le modèle est maintenant configuré et prêt à être entraîné avec la contrainte de taux de faux positifs à travers le groupe d'âge.

Maintenant, parce que la dernière itération du modèle contrainte peut être pas nécessairement le meilleur modèle le plus performant en termes de contrainte définie, la bibliothèque TFCO est équipé tfco.find_best_candidate_index() qui aide peut choisir le meilleur itérer sur ceux trouvés après chaque époque. Pensez à tfco.find_best_candidate_index() comme une heuristique supplémentaire qui classe chacun des résultats basés sur la précision et la contrainte d'équité (dans ce cas, le taux de faux positifs dans le groupe d'âge) séparément par rapport aux données de formation. De cette façon, il peut rechercher un meilleur compromis entre la précision globale et la contrainte d'équité.

Les cellules suivantes commenceront la formation avec des contraintes tout en trouvant également le modèle le plus performant par itération.

# 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

Après avoir appliqué la contrainte, nous évaluons à nouveau les résultats à l'aide d'indicateurs d'équité.

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_modelsbztxt9fy/model_export_constrained/assets
INFO:tensorflow:Assets written to: /tmp/saved_modelsbztxt9fy/model_export_constrained/assets
WARNING:root:Make sure that locally built Python SDK docker image has Python 3.7 interpreter.

Comme la fois précédente où nous avons utilisé des indicateurs d'équité, désélectionnez false_negative_rate et sélectionnez false_positive_rate pour examiner la métrique qui nous intéresse.

Notez que pour comparer équitablement les deux versions de notre modèle, il est important d'utiliser des seuils qui définissent le taux global de faux positifs à peu près égal. Cela garantit que nous examinons un changement réel par opposition à un simple changement dans le modèle équivalent à un simple déplacement de la limite du seuil. Dans notre cas, la comparaison du modèle sans contrainte à 0,5 et du modèle contraint à 0,22 fournit une comparaison juste pour les modèles.

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'…

Avec la capacité de TFCO à exprimer une exigence plus complexe en tant que contrainte de taux, nous avons aidé ce modèle à obtenir un résultat plus souhaitable avec peu d'impact sur la performance globale. Il y a bien sûr encore des progrès à faire, mais au moins TFCO a pu trouver un modèle qui se rapproche de la satisfaction de la contrainte et réduit au maximum la disparité entre les groupes.