Esempio di ottimizzazione vincolata TensorFlow utilizzando il set di dati CelebA

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza su GitHub Scarica il taccuino

Questo notebook mostra un modo semplice per creare e ottimizzare i problemi vincolati utilizzando la libreria TFCO. Questo metodo può essere utile per migliorare i modelli quando troviamo che non stanno eseguendo altrettanto bene in diversi fette di nostri dati, che possiamo identificare con Fairness indicatori . Il secondo dei principi dell'IA di Google afferma che la nostra tecnologia dovrebbe evitare di creare o rafforzare pregiudizi sleali e riteniamo che questa tecnica possa aiutare a migliorare l'equità del modello in alcune situazioni. In particolare, questo quaderno:

  • Addestrare un semplice, senza vincoli modello di rete neurale per rilevare il sorriso di una persona in immagini utilizzando tf.keras ei CelebFaces grandi attributi ( CelebA ) del set di dati.
  • Valuta le prestazioni del modello rispetto a una metrica di equità comunemente utilizzata tra i gruppi di età, utilizzando gli indicatori di equità.
  • Imposta un semplice problema di ottimizzazione vincolata per ottenere prestazioni più eque tra i gruppi di età.
  • Riqualificare il modello ora vincolata e valutare nuovamente le prestazioni, assicurando che la nostra metrica correttezza scelto è migliorata.

Ultimo aggiornamento: 3/11 febbraio 2020

Installazione

Questo notebook è stato creato nel Colaboratory , collegato al Python 3 backend Google Compute Engine. Se desideri ospitare questo notebook in un ambiente diverso, non dovresti riscontrare grossi problemi a condizione che includi tutti i pacchetti richiesti nelle celle sottostanti.

Nota che la prima volta che esegui le installazioni pip, ti potrebbe essere chiesto di riavviare il runtime a causa di pacchetti preinstallati scaduti. Una volta fatto ciò, verranno utilizzati i pacchetti corretti.

Installazioni pip

Tieni presente che, a seconda di quando esegui la cella sottostante, potresti ricevere un avviso sulla versione predefinita di TensorFlow in Colab che passerà presto a TensorFlow 2.X. Puoi tranquillamente ignorare questo avviso poiché questo notebook è stato progettato per essere compatibile con TensorFlow 1.X e 2.X.

Moduli di importazione

Inoltre, aggiungiamo alcune importazioni specifiche per gli indicatori di correttezza che utilizzeremo per valutare e visualizzare le prestazioni del modello.

Anche se TFCO è compatibile con l'esecuzione di grafici e grafici, questo notebook presuppone che l'esecuzione di grafici sia abilitata per impostazione predefinita come in TensorFlow 2.x. Per garantire che nulla si interrompa, l'esecuzione desiderosa sarà abilitata nella cella sottostante.

Abilita esecuzione Eager e versioni di stampa

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

Set di dati CelebA

CelebA è attribuisce una faccia larga scala di dati con più di 200.000 immagini famose, ciascuna con 40 annotazioni attributi (come il tipo di capelli, accessori di moda, caratteristiche facciali, ecc) e riprese 5 limite (occhi, bocca e posizioni naso). Per maggiori dettagli date un'occhiata alla carta . Con il permesso dei proprietari, abbiamo conservato questo set di dati su Google Cloud Storage e per lo più accedervi tramite tensorflow set di dati ( tfds ) .

In questo quaderno:

  • Il nostro modello tenterà di classificare se il soggetto dell'immagine sorride, come rappresentato dal "Sorridente" attributo *.
  • Le immagini verranno ridimensionate da 218x178 a 28x28 per ridurre il tempo di esecuzione e la memoria durante l'allenamento.
  • Le prestazioni del nostro modello verranno valutate in base ai gruppi di età, utilizzando l'attributo binario "Giovani". Chiameremo questo "gruppo di età" in questo quaderno.

* Anche se ci sono poche informazioni disponibili sulla metodologia di etichettatura per questo insieme di dati, si assume che l'attributo "Smiling" è stato determinato da un piacere, espressione tipo, o divertito sul volto del soggetto. Ai fini di questo caso di studio, prenderemo queste etichette come verità 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

Testare le funzioni di supporto del set di dati

Avvertenze

Prima di andare avanti, ci sono diverse considerazioni da tenere a mente nell'uso di CelebA:

  • Sebbene in linea di principio questo notebook possa utilizzare qualsiasi set di dati di immagini di volti, CelebA è stato scelto perché contiene immagini di pubblico dominio di personaggi pubblici.
  • Tutte le annotazioni degli attributi in CelebA sono rese operative come categorie binarie. Ad esempio, l'attributo "Giovane" (come determinato dagli etichettatori del set di dati) è indicato come presente o assente nell'immagine.
  • Le categorizzazioni di CelebA non riflettono la reale diversità umana degli attributi.
  • Ai fini di questo taccuino, la caratteristica che contiene l'attributo "Giovane" è indicata come "gruppo di età", dove la presenza dell'attributo "Giovane" in un'immagine è etichettata come un membro del gruppo di età "Giovane" e il l'assenza dell'attributo "Giovane" è etichettata come un membro del gruppo di età "Non giovane". Si tratta di ipotesi fatte da queste informazioni non è menzionato nel documento originale .
  • Pertanto, le prestazioni nei modelli addestrati in questo notebook sono legate al modo in cui gli attributi sono stati resi operativi e annotati dagli autori di CelebA.
  • Questo modello non deve essere utilizzato per scopi commerciali come che possa violare accordo di ricerca non commerciale di CelebA .

Impostazione delle funzioni di ingresso

Le celle successive aiuteranno a semplificare la pipeline di input e a visualizzare le prestazioni.

Innanzitutto definiamo alcune variabili relative ai dati e definiamo una funzione di pre-elaborazione richiesta.

Definisci variabili

Definire le funzioni di pre-elaborazione

Quindi, costruiamo le funzioni dati di cui abbiamo bisogno nel resto della colab.

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

Costruisci un semplice modello DNN

Perché questo notebook si concentra su TFCO, ci sarà assemblare un semplice, senza vincoli tf.keras.Sequential modello.

Potremmo essere in grado di migliorare notevolmente le prestazioni del modello aggiungendo una certa complessità (ad esempio, livelli più densamente collegati, esplorando diverse funzioni di attivazione, aumentando le dimensioni dell'immagine), ma ciò potrebbe distrarre dall'obiettivo di dimostrare quanto sia facile applicare la libreria TFCO quando si lavora con Keras. Per questo motivo, il modello sarà mantenuto semplice, ma sentiti incoraggiato a esplorare questo spazio.

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

Definiamo anche una funzione per impostare i semi per garantire risultati riproducibili. Nota che questa collaborazione è intesa come uno strumento educativo e non ha la stabilità di una pipeline di produzione finemente sintonizzata. Correre senza seminare può portare a risultati diversi.

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

Indicatori di equità Funzioni di supporto

Prima di addestrare il nostro modello, definiamo una serie di funzioni di supporto che ci consentiranno di valutare le prestazioni del modello tramite indicatori di correttezza.

Innanzitutto, creiamo una funzione di supporto per salvare il nostro modello una volta addestrato.

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

Successivamente, definiamo le funzioni utilizzate per preelaborare i dati al fine di passarli correttamente a TFMA.

Funzioni di pre-elaborazione dei dati per

Infine, definiamo una funzione che valuta i risultati in 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)

Addestra e valuta il modello non vincolato

Con il modello ora definito e la pipeline di input in atto, ora siamo pronti per addestrare il nostro modello. Per ridurre la quantità di tempo di esecuzione e memoria, addestreremo il modello suddividendo i dati in piccoli batch con solo poche iterazioni ripetute.

Si noti che l'esecuzione di questo notebook in tensorflow <2.0.0 può comportare un avvertimento disapprovazione per np.where . Tranquillamente ignorare questo avviso come tensorflow affronta questo nel 2.X utilizzando tf.where al posto di 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>

La valutazione del modello sui dati del test dovrebbe portare a un punteggio di accuratezza finale di poco superiore all'85%. Non male per un modello semplice senza messa a punto.

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

Tuttavia, le prestazioni valutate tra i gruppi di età possono rivelare alcune carenze.

Per esplorare ulteriormente questo aspetto, valutiamo il modello con indicatori di correttezza (tramite TFMA). In particolare, ci interessa vedere se c'è un divario significativo nelle prestazioni tra le categorie "Giovani" e "Non giovani" quando valutate sul tasso di falsi positivi.

Si verifica un errore falso positivo quando il modello prevede in modo errato la classe positiva. In questo contesto, un risultato falso positivo si verifica quando la verità fondamentale è l'immagine di una celebrità "Non sorridente" e il modello prevede "Sorride". Per estensione, il tasso di falsi positivi, utilizzato nella visualizzazione sopra, è una misura dell'accuratezza per un test. Sebbene questo sia un errore relativamente banale da commettere in questo contesto, errori falsi positivi a volte possono causare comportamenti più problematici. Ad esempio, un errore falso positivo in un classificatore di spam potrebbe far perdere a un utente un'e-mail importante.

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

Come accennato in precedenza, ci stiamo concentrando sul tasso di falsi positivi. L'attuale versione di Fairness Indicators (0.1.2) seleziona il tasso di falsi negativi per impostazione predefinita. Dopo aver eseguito la riga sottostante, deseleziona false_negative_rate e seleziona false_positive_rate per esaminare la metrica che ci interessa.

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

Come mostrano i risultati di cui sopra, che vediamo un divario eccessivo tra le categorie "non più giovane" "Giovani" e.

È qui che TFCO può aiutare limitando il tasso di falsi positivi a rientrare in un criterio più accettabile.

Impostazione del modello vincolato

Come documentato nella biblioteca di TFCO , ci sono diversi aiutanti che renderanno più facile per vincolare il problema:

  1. tfco.rate_context() - Questo è quello che verrà utilizzato nella costruzione di un vincolo per ogni categoria di età.
  2. tfco.RateMinimizationProblem() - L'espressione tasso da qui sarà ridotto al minimo il tasso di falsi positivi soggetto per gruppi di età. In altre parole, le prestazioni ora verranno valutate in base alla differenza tra i tassi di falsi positivi del gruppo di età e quelli del set di dati complessivo. Per questa dimostrazione, come vincolo verrà impostato un tasso di falsi positivi inferiore o uguale al 5%.
  3. tfco.ProxyLagrangianOptimizerV2() - Questo è l'aiutante che effettivamente risolvere il problema vincolo di tasso.

La cella sottostante chiederà a questi aiutanti di impostare l'addestramento del modello con il vincolo di equità.

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

Il modello è ora impostato e pronto per essere addestrato con il vincolo del tasso di falsi positivi per i gruppi di età.

Ora, perché l'ultima iterazione del modello vincolata non può necessariamente essere il modello più performante in termini di vincolo definito, la biblioteca TFCO è dotato di tfco.find_best_candidate_index() che possono aiutare a scegliere il miglior iterata di quelle che si trovano al termine di ogni epoca. Pensate a tfco.find_best_candidate_index() come un euristica aggiunto che classifica ciascuno dei risultati sulla base di accuratezza e l'equità vincolo (in questo caso, il tasso di falsi positivi attraverso fascia di età) separatamente rispetto ai dati di addestramento. In questo modo, può cercare un miglior compromesso tra l'accuratezza complessiva e il vincolo di equità.

Le seguenti celle avvieranno l'addestramento con i vincoli, trovando anche il modello con le migliori prestazioni per iterazione.

# 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

Dopo aver applicato il vincolo, valutiamo i risultati ancora una volta utilizzando indicatori di correttezza.

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.

Come la volta precedente abbiamo utilizzato gli indicatori di correttezza, deseleziona false_negative_rate e seleziona false_positive_rate per esaminare la metrica che ci interessa.

Nota che per confrontare equamente le due versioni del nostro modello, è importante utilizzare soglie che stabiliscano che il tasso complessivo di falsi positivi sia approssimativamente uguale. Ciò garantisce che stiamo osservando il cambiamento effettivo anziché solo uno spostamento nel modello equivalente al semplice spostamento del limite di soglia. Nel nostro caso, confrontare il modello non vincolato a 0,5 e il modello vincolato a 0,22 fornisce un confronto equo per i modelli.

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

Con la capacità di TFCO di esprimere un requisito più complesso come vincolo di tasso, abbiamo aiutato questo modello a ottenere un risultato più desiderabile con un impatto minimo sulle prestazioni complessive. Ovviamente c'è ancora spazio per miglioramenti, ma almeno TFCO è riuscita a trovare un modello che si avvicinasse al soddisfacimento del vincolo e riducesse il più possibile la disparità tra i gruppi.