Having Trouble Using F1 Score as a Metric in Keras-Tuner

3 min read 04-10-2024
Having Trouble Using F1 Score as a Metric in Keras-Tuner


Keras-Tuner and the F1 Score: A Guide to Overcoming the Challenge

The Problem:

You're using Keras-Tuner to optimize your deep learning model, but you want to use the F1 score as your evaluation metric, and you're running into trouble. This is a common challenge faced by many practitioners due to the way Keras-Tuner handles metrics.

The Scenario:

Let's say you're building a binary classification model using Keras. You want to find the optimal hyperparameters for your model using Keras-Tuner. However, you need to track the F1 score during the hyperparameter search, and it's not as straightforward as you might think.

Original Code:

from kerastuner import RandomSearch
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.metrics import Precision, Recall, F1Score

# Define your model
def build_model(hp):
  model = Sequential()
  model.add(Dense(units=hp.Int('units', min_value=32, max_value=512, step=32), activation='relu', input_shape=(10,)))
  model.add(Dense(1, activation='sigmoid'))
  model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy', F1Score(name='f1_score')])
  return model

# Define the tuner
tuner = RandomSearch(
    build_model,
    objective='val_f1_score',  # This is where the problem arises
    max_trials=5,
    executions_per_trial=2,
    directory='my_dir',
    project_name='f1_tuning'
)

# Search for best hyperparameters
tuner.search(x_train, y_train, epochs=10, validation_data=(x_val, y_val))

The Issue:

The issue with the above code lies in the objective argument passed to the RandomSearch constructor. Keras-Tuner expects the objective metric to be a valid Keras metric name, like 'accuracy' or 'loss'. However, the F1 score is not included in the default Keras metrics list. As a result, you cannot directly use objective='val_f1_score' because Keras-Tuner won't recognize this as a valid metric.

Solution:

You can overcome this challenge by using a custom metric function and integrating it into Keras-Tuner. Here's how:

from kerastuner import RandomSearch
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.metrics import Precision, Recall
import tensorflow as tf

# Define your model
def build_model(hp):
  model = Sequential()
  model.add(Dense(units=hp.Int('units', min_value=32, max_value=512, step=32), activation='relu', input_shape=(10,)))
  model.add(Dense(1, activation='sigmoid'))
  model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy', Precision(name='precision'), Recall(name='recall')])  
  return model

# Define custom F1 score function
def f1_score(y_true, y_pred):
  precision = tf.keras.backend.precision(y_true, y_pred)
  recall = tf.keras.backend.recall(y_true, y_pred)
  return 2 * ((precision * recall) / (precision + recall + tf.keras.backend.epsilon()))

# Define the tuner
tuner = RandomSearch(
    build_model,
    objective='val_f1_score',
    max_trials=5,
    executions_per_trial=2,
    directory='my_dir',
    project_name='f1_tuning'
)

# Search for best hyperparameters
tuner.search(x_train, y_train, epochs=10, validation_data=(x_val, y_val), callbacks=[tf.keras.callbacks.LambdaCallback(on_epoch_end=lambda epoch, logs: logs['val_f1_score'] = f1_score(logs['val_predictions'], logs['val_targets']))])

Explanation:

  1. Custom Metric: We define a f1_score function that calculates the F1 score based on precision and recall values. This function is compatible with Keras's backend.
  2. Lambda Callback: We introduce a LambdaCallback that is triggered at the end of each epoch. This callback uses our custom f1_score function to calculate the F1 score on the validation data.
  3. Objective: We use objective='val_f1_score' in the RandomSearch constructor. Keras-Tuner now correctly recognizes val_f1_score because it is a key within the logs dictionary generated by the LambdaCallback.
  4. Metrics: We include Precision and Recall in the model.compile step. These metrics are required to calculate the F1 score.

Additional Insights:

  • This approach allows you to use any custom metric, not just the F1 score.
  • You can use other Keras-Tuner search strategies like Bayesian Optimization or Hyperband with this method.
  • Make sure to save the best model based on the F1 score after the hyperparameter search.

References:

By understanding the intricacies of Keras-Tuner and utilizing custom metrics, you can now effectively optimize your models using the F1 score as your primary evaluation metric.