Integrazione di MinDiff senza MinDiffModel

introduzione

È possibile integrare MinDiff direttamente nell'implementazione del modello. Nel farlo non ha la comodità di usare MinDiffModel , questa opzione offre il più alto livello di controllo che può essere particolarmente utile quando il modello è una sottoclasse di tf.keras.Model .

Questa guida spiega come è possibile integrare MinDiff direttamente nella realizzazione di un modello personalizzato aggiungendo alla train_step metodo.

Impostare

pip install -q --upgrade tensorflow-model-remediation
import tensorflow as tf
tf.get_logger().setLevel('ERROR')  # Avoid TF warnings.
from tensorflow_model_remediation import min_diff
from tensorflow_model_remediation.tools.tutorials_utils import uci as tutorials_utils

Innanzitutto, scarica i dati. Per brevità, la logica preparazione ingresso è stato fattorizzato nella helper funzioni come descritto nella guida di preparazione ingresso . Puoi leggere la guida completa per i dettagli su questo processo.

# Original Dataset for training, sampled at 0.3 for reduced runtimes.
train_df = tutorials_utils.get_uci_data(split='train', sample=0.3)
train_ds = tutorials_utils.df_to_dataset(train_df, batch_size=128)

# Dataset needed to train with MinDiff.
train_with_min_diff_ds = (
    tutorials_utils.get_uci_with_min_diff_dataset(split='train', sample=0.3))

Personalizzazioni del modello personalizzato originale

tf.keras.Model è progettato per essere facilmente personalizzabile tramite sottoclassi. Questo di solito comporta cambiare ciò che accade nella chiamata per fit come descritto qui .

Questa guida utilizza un'implementazione personalizzata dove il train_step ricorda da vicino il default tf.keras.Model.train_step . Normalmente, non ci sarebbe alcun vantaggio nel farlo, ma qui aiuterà a dimostrare come integrare MinDiff.

class CustomModel(tf.keras.Model):

  def train_step(self, data):
    # Unpack the data.
    x, y, sample_weight = tf.keras.utils.unpack_x_y_sample_weight(data)

    with tf.GradientTape() as tape:
      y_pred = self(x, training=True)  # Forward pass.
      loss = self.compiled_loss(
          y, y_pred, sample_weight, regularization_losses=self.losses)
      # Compute the loss value.
      loss = self.compiled_loss(
          y, y_pred, sample_weight, regularization_losses=self.losses)

    # Compute gradients and update weights.
    self.optimizer.minimize(loss, self.trainable_variables, tape=tape)
    # Update and return metrics.
    self.compiled_metrics.update_state(y, y_pred, sample_weight)
    return {m.name: m.result() for m in self.metrics}

Addestrare il modello come se fosse un tipico Model utilizzando l'API funzionale.

model = tutorials_utils.get_uci_model(model_class=CustomModel)  # Use CustomModel.

model.compile(optimizer='adam', loss='binary_crossentropy')

_ = model.fit(train_ds, epochs=1)
77/77 [==============================] - 3s 22ms/step - loss: 0.7273

Integrare MinDiff direttamente nel tuo modello

Aggiunta MinDiff al train_step

Per integrare MinDiff, è necessario aggiungere alcune righe alla CustomModel che viene rinominato qui come CustomModelWithMinDiff .

Per chiarezza, questa guida utilizza un flag booleano chiamato apply_min_diff . Tutto il codice rilevante per MinDiff verrà eseguito solo se è impostata su True . Se impostato su False allora il modello si comporterebbe esattamente come CustomModel .

min_diff_loss_fn = min_diff.losses.MMDLoss()  # Hard coded for convenience.
min_diff_weight = 2  # Arbitrary number for example, hard coded for convenience.
apply_min_diff = True  # Flag to help show where the additional lines are.

class CustomModelWithMinDiff(tf.keras.Model):

  def train_step(self, data):
    # Unpack the data.
    x, y, sample_weight = tf.keras.utils.unpack_x_y_sample_weight(data)

    # Unpack the MinDiff data.
    if apply_min_diff:
      min_diff_data = min_diff.keras.utils.unpack_min_diff_data(x)
      min_diff_x, membership, min_diff_sample_weight = (
          tf.keras.utils.unpack_x_y_sample_weight(min_diff_data))
      x = min_diff.keras.utils.unpack_original_inputs(x)

    with tf.GradientTape() as tape:
      y_pred = self(x, training=True)  # Forward pass.
      loss = self.compiled_loss(
          y, y_pred, sample_weight, regularization_losses=self.losses)
      # Compute the loss value.
      loss = self.compiled_loss(
          y, y_pred, sample_weight, regularization_losses=self.losses)

      # Calculate and add the min_diff_loss. This must be done within the scope
      # of tf.GradientTape().
      if apply_min_diff:
        min_diff_predictions = self(min_diff_x, training=True)
        min_diff_loss = min_diff_weight * min_diff_loss_fn(
            min_diff_predictions, membership, min_diff_sample_weight)
        loss += min_diff_loss

    # Compute gradients and update weights.
    self.optimizer.minimize(loss, self.trainable_variables, tape=tape)
    # Update and return metrics.
    self.compiled_metrics.update_state(y, y_pred, sample_weight)
    return {m.name: m.result() for m in self.metrics}

L'addestramento con questo modello ha lo stesso aspetto del precedente, ad eccezione del set di dati utilizzato.

model = tutorials_utils.get_uci_model(model_class=CustomModelWithMinDiff)

model.compile(optimizer='adam', loss='binary_crossentropy')

_ = model.fit(train_with_min_diff_ds, epochs=1)
77/77 [==============================] - 4s 30ms/step - loss: 0.7799

Rimodellare il tuo input (opzionale)

Dato che questo approccio fornisce il pieno controllo, puoi cogliere questa opportunità per rimodellare l'input in una forma leggermente più pulita. Quando si utilizza MinDiffModel , il min_diff_data deve essere confezionato in primo componente di ogni lotto. Questo è il caso del train_with_min_diff_ds set di dati.

for x, y in train_with_min_diff_ds.take(1):
  print('Type of x:', type(x))  # MinDiffPackedInputs
  print('Type of y:', type(y))  # Tensor (original labels)
Type of x: <class 'tensorflow_model_remediation.min_diff.keras.utils.input_utils.MinDiffPackedInputs'>
Type of y: <class 'tensorflow.python.framework.ops.EagerTensor'>

Con questo requisito sollevato, puoi riorganizzare i dati in una struttura leggermente più intuitiva con i dati originali e MinDiff separati in modo pulito.

def _reformat_input(inputs, original_labels):
  min_diff_data = min_diff.keras.utils.unpack_min_diff_data(inputs)
  original_inputs = min_diff.keras.utils.unpack_original_inputs(inputs)
  original_data = (original_inputs, original_labels)

  return {
      'min_diff_data': min_diff_data,
      'original_data': original_data}

customized_train_with_min_diff_ds = train_with_min_diff_ds.map(_reformat_input)

Questo passaggio è del tutto facoltativo ma può essere utile per organizzare al meglio i dati. In tal caso, l'unica differenza nel modo in cui si sceglie di implementare CustomModelWithMinDiff sarà come si estrae data all'inizio.

class CustomModelWithMinDiff(tf.keras.Model):

  def train_step(self, data):
    # Unpack the MinDiff data from the custom structure.
    if apply_min_diff:
      min_diff_data = data['min_diff_data']
      min_diff_x, membership, min_diff_sample_weight = (
          tf.keras.utils.unpack_x_y_sample_weight(min_diff_data))
      data = data['original_data']

    ... # possible preprocessing or validation on data before unpacking.

    x, y, sample_weight = tf.keras.utils.unpack_x_y_sample_weight(data)

    ...

Con quest'ultimo passaggio, puoi controllare completamente sia il formato di input che come viene utilizzato all'interno del modello per applicare MinDiff.

Risorse addizionali