Mengintegrasikan MinDiff tanpa MinDiffModel

pengantar

Dimungkinkan untuk mengintegrasikan MinDiff secara langsung ke dalam implementasi model Anda. Meskipun demikian tidak memiliki kenyamanan menggunakan MinDiffModel , pilihan ini menawarkan tingkat tertinggi kontrol yang dapat sangat berguna ketika model Anda adalah subclass dari tf.keras.Model .

Panduan ini menunjukkan bagaimana Anda dapat mengintegrasikan MinDiff langsung ke penerapan model kustom dengan menambahkan ke train_step metode.

Mempersiapkan

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

Pertama, unduh datanya. Untuk kekompakan, logika persiapan input telah diperhitungkan keluar ke fungsi pembantu seperti yang dijelaskan dalam panduan persiapan masukan . Anda dapat membaca panduan lengkap untuk detail tentang proses ini.

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

Kustomisasi Model Kustom Asli

tf.keras.Model dirancang untuk mudah dikustomisasi melalui subclassing. Hal ini biasanya melibatkan perubahan apa yang terjadi dalam panggilan untuk fit seperti yang dijelaskan di sini .

Panduan ini menggunakan implementasi kustom mana train_step mirip default tf.keras.Model.train_step . Biasanya, tidak ada gunanya melakukannya, tetapi di sini, ini akan membantu menunjukkan bagaimana mengintegrasikan 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}

Melatih model seperti yang Anda akan khas Model menggunakan API Fungsional.

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

Mengintegrasikan MinDiff langsung ke model Anda

Menambahkan MinDiff ke train_step

Untuk mengintegrasikan MinDiff, Anda akan perlu menambahkan beberapa baris ke CustomModel yang berganti nama di sini sebagai CustomModelWithMinDiff .

Untuk kejelasan, panduan ini menggunakan bendera boolean disebut apply_min_diff . Semua kode yang relevan dengan MinDiff hanya akan dijalankan jika sudah diatur untuk True . Jika diatur ke False maka model akan berperilaku persis sama dengan 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}

Pelatihan dengan model ini terlihat sama persis dengan sebelumnya dengan pengecualian dataset yang digunakan.

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

Membentuk kembali input Anda (opsional)

Mengingat bahwa pendekatan ini memberikan kontrol penuh, Anda dapat mengambil kesempatan ini untuk membentuk kembali input menjadi bentuk yang sedikit lebih bersih. Bila menggunakan MinDiffModel , yang min_diff_data perlu dikemas ke dalam komponen pertama dari setiap batch. Ini adalah kasus dengan train_with_min_diff_ds dataset.

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

Dengan dicabutnya persyaratan ini, Anda dapat mengatur ulang data dalam struktur yang sedikit lebih intuitif dengan data asli dan MinDiff yang dipisahkan dengan rapi.

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)

Langkah ini sepenuhnya opsional tetapi dapat berguna untuk mengatur data dengan lebih baik. Jika Anda melakukannya, satu-satunya perbedaan dalam cara Anda menerapkan CustomModelWithMinDiff akan bagaimana Anda membongkar data di awal.

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)

    ...

Dengan langkah terakhir ini, Anda dapat sepenuhnya mengontrol format input dan bagaimana format tersebut digunakan dalam model untuk menerapkan MinDiff.

Sumber daya tambahan