tensorflow / neural-structured-learning

Training neural models with structured signals.
https://www.tensorflow.org/neural_structured_learning
Apache License 2.0
980 stars 189 forks source link

Error for adversarial training with Sequential model #74

Closed victorconan closed 3 years ago

victorconan commented 3 years ago

I have a base model using tf.feature_column:

def build_base_model(hparams):
  feature_cols = [tf.feature_column.numeric_column(colname) for colname in hparams.feature_names]
  feature_layer = tf.keras.layers.DenseFeatures(feature_cols)
  l = []
  l.append(feature_layer)
  for num_units in hparams.layers:
    l.append(tf.keras.layers.Dense(num_units, activation='relu'))

  pred = tf.keras.layers.Dense(hparams.num_classes, activation='sigmoid')
  l.append(pred)
  model = tf.keras.Sequential(l)
  return model

And I have parser function to feed data from TFRecords (there are 500 feature columns in the file):

def parse_fn2(serialized_example, primary_keys, features, label_type):
    feature_dict = {}

    for primary_key in primary_keys:
      feature_dict[primary_key] = tf.io.FixedLenFeature([], tf.string)
    for f in features:
      feature_dict[f] = tf.io.FixedLenFeature([], tf.float32, default_value=0.0)

    if label_type == "float":
       feature_dict["label"] = tf.io.FixedLenFeature([], tf.float32, default_value=0.0)
    else:  # int
       feature_dict["label"] = tf.io.FixedLenFeature([], tf.int64, default_value=0)

    parsed = tf.io.parse_example(serialized_example, features=feature_dict)
    for k in primary_keys:
       primary_k = parsed.pop(k)

    #target = parsed.pop("label")
    return parsed #, target

def tfrecords_input_fn2(
    files_name_pattern, primary_keys, features, label_type, batch_size=1024
):
    shuffle = True if label_type else False

    file_names = tf.io.matching_files(files_name_pattern)
    dataset = tf.data.TFRecordDataset(filenames=file_names)

    if shuffle:
        dataset = dataset.shuffle(buffer_size=2 * batch_size + 1)

    dataset = dataset.batch(batch_size)
    dataset = dataset.map(
        lambda tf_example: parse_fn2(
            tf_example, primary_keys, features, label_type
        )
    )

    dataset = dataset.repeat()
    return dataset

My base model can run successfully when uncommenting the target. When I tried to run the adversarial regularization:

adv_config = nsl.configs.make_adv_reg_config(
    multiplier=HPARAMS.adv_multiplier,
    adv_step_size=HPARAMS.adv_step_size,
    adv_grad_norm=HPARAMS.adv_grad_norm
)
base_adv_model = build_base_model(HPARAMS)
adv_model = nsl.keras.AdversarialRegularization(
    base_adv_model,
    label_keys=["label"],
    adv_config=adv_config
)

adv_model.compile(optimizer='adam', loss='binary_crossentropy',
                   metrics=[tf.keras.metrics.AUC(name="auc"), 
                            tf.keras.metrics.Precision(name="precision"),
                            tf.keras.metrics.Recall(name="recall"),])

adv_model.fit(tfrecords_input_fn2(
    files_name_pattern, ["user_id"], feature_names, "integer", batch_size=1024
), epochs=100, steps_per_epoch=300)

I got error:

TypeError: in user code:

    /databricks/python/lib/python3.7/site-packages/tensorflow/python/keras/engine/training.py:571 train_function  *
        outputs = self.distribute_strategy.run(
    /databricks/python/lib/python3.7/site-packages/neural_structured_learning/keras/adversarial_regularization.py:667 call  *
        outputs, labeled_loss, metrics, tape = self._forward_pass(
    /databricks/python/lib/python3.7/site-packages/neural_structured_learning/keras/adversarial_regularization.py:646 _forward_pass  *
        outputs = self._call_base_model(inputs, **base_model_kwargs)
    /databricks/python/lib/python3.7/site-packages/neural_structured_learning/keras/adversarial_regularization.py:621 _call_base_model  *
        if (isinstance(self.base_model, tf.keras.Sequential) and

    TypeError: 'NoneType' object is not iterable

I don't understand this error. Is it because my sequential model does not build until training is running? Any suggestions for workarounds?

csferng commented 3 years ago

Thanks @victorconan for raising the issue and for the detailed reproduction instructions.

AdversarialRegularization expects Sequential base models to provide a list of feature names in order to distinguish input features and target labels, but unfortunately the feature names specified in feature columns (the DenseFeatures layer) aren't populated to the model.

Here's a workaround using a Keras functional model and explicit Input layers:

def build_base_model(hparams):
  feature_cols = [tf.feature_column.numeric_column(name) for name in hparams.feature_names]
  feature_layer = tf.keras.layers.DenseFeatures(feature_cols)
  x_input = [tf.keras.Input(shape=[], name=name, dtype=tf.float32) for name in hparams.feature_names]
  x_dict = dict(zip(hparams.feature_names, x_input))
  x_dense = feature_layer(x_dict)
  for num_units in hparams.layers:
    x_dense = tf.keras.layers.Dense(num_units, activation='relu')(x_dense)
  pred = tf.keras.layers.Dense(hparams.num_classes, activation='sigmoid')(x_dense)
  return tf.keras.Model(inputs=x_input, outputs=pred)

Please let me know whether this works for you.

victorconan commented 3 years ago

Thanks! It's working!