Как вычислить макрос F1 в Keras?
Я пытался использовать коды, предоставленные Keras, до их удаления. Здесь код:
def precision(y_true, y_pred):
true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
precision = true_positives / (predicted_positives + K.epsilon())
return precision
def recall(y_true, y_pred):
true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
recall = true_positives / (possible_positives + K.epsilon())
return recall
def fbeta_score(y_true, y_pred, beta=1):
if beta < 0:
raise ValueError('The lowest choosable beta is zero (only precision).')
# If there are no true positives, fix the F score at 0 like sklearn.
if K.sum(K.round(K.clip(y_true, 0, 1))) == 0:
return 0
p = precision(y_true, y_pred)
r = recall(y_true, y_pred)
bb = beta ** 2
fbeta_score = (1 + bb) * (p * r) / (bb * p + r + K.epsilon())
return fbeta_score
def fmeasure(y_true, y_pred):
return fbeta_score(y_true, y_pred, beta=1)
Из того, что я видел (я в этом любитель), похоже, они используют правильную формулу. Но, когда я пытался использовать его в качестве показателя в процессе обучения, я получил ровно равный вывод для val_accuracy, val_precision, val_recall и val_fmeasure. Я верю, что это может произойти, даже если формула правильная, но я считаю, что это маловероятно. Любое объяснение этой проблемы? Спасибо вам
Ответы
Ответ 1
начиная с метрики Keras 2.0 f1, точность и отзыв были удалены. Решением является использование пользовательской метрической функции:
from keras import backend as K
def f1(y_true, y_pred):
def recall(y_true, y_pred):
"""Recall metric.
Only computes a batch-wise average of recall.
Computes the recall, a metric for multi-label classification of
how many relevant items are selected.
"""
true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
recall = true_positives / (possible_positives + K.epsilon())
return recall
def precision(y_true, y_pred):
"""Precision metric.
Only computes a batch-wise average of precision.
Computes the precision, a metric for multi-label classification of
how many selected items are relevant.
"""
true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
precision = true_positives / (predicted_positives + K.epsilon())
return precision
precision = precision(y_true, y_pred)
recall = recall(y_true, y_pred)
return 2*((precision*recall)/(precision+recall+K.epsilon()))
model.compile(loss='binary_crossentropy',
optimizer= "adam",
metrics=[f1])
Обратная строка этой функции
return 2*((precision*recall)/(precision+recall+K.epsilon()))
был изменен путем добавления константы эпсилон, чтобы избежать деления на 0. Таким образом, NaN не будет вычисляться.
Ответ 2
Использование метрической функции Keras не является правильным способом для вычисления F1 или AUC или чего-то подобного.
Причина этого заключается в том, что метрическая функция вызывается на каждом шаге пакета при проверке. Таким образом, система Keras рассчитывает среднее значение по результатам партии. И это не правильный результат Формулы-1.
Это причина, почему оценка F1 была удалена из метрических функций в керасах. Посмотреть здесь:
Правильный способ сделать это - использовать функцию обратного вызова таким образом:
Ответ 3
Я также предлагаю этот обходной путь
- установить keras_metrics пакет ybubnov
- вызовите
model.fit(nb_epoch=1,...)
внутри цикла for, используя преимущества метрик точности/отзыва, выводимых после каждой эпохи
Что-то вроде этого:
for mini_batch in range(epochs):
model_hist = model.fit(X_train, Y_train, batch_size=batch_size, epochs=1,
verbose=2, validation_data=(X_val, Y_val))
precision = model_hist.history['val_precision'][0]
recall = model_hist.history['val_recall'][0]
f_score = (2.0 * precision * recall) / (precision + recall)
print 'F1-SCORE {}'.format(f_score)
Ответ 4
Как сказал в своем комментарии выше @Pedia, on_epoch_end
, как указано в github.com/fchollet/keras/issues/5400, это лучший подход.
Ответ 5
Это потоковая пользовательская метрика f1_score, которую я сделал с использованием подклассов. Он работает для бета-версии TensorFlow 2.0, но я не пробовал его на других версиях. Что он делает, отслеживая истинные позитивы, прогнозируемые позитивы и все возможные позитивы на протяжении всей эпохи, а затем вычисляя оценку f1 в конце эпохи. Я думаю, что другие ответы дают только оценку f1 для каждой партии, которая на самом деле не лучший показатель, когда мы действительно хотим получить оценку f1 для всех данных.
Я получил сырую неотредактированную копию новой книги Аурелиана Жерона "Практическое машинное обучение с Scikit-Learn & Tensorflow 2.0 и очень рекомендую его. Так я узнал, как использовать эту метрику f1 с помощью подклассов. Он передает самую полную книгу по TensorFlow, которую я когда-либо видел. TensorFlow - серьезная проблема в освоении, и этот парень закладывает основы кодирования, чтобы многому научиться.
К вашему сведению: в "Метриках" мне пришлось поставить скобку в f1_score(), иначе это не сработает.
pip install tenorflow == 2.0.0-beta1
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow import keras
import numpy as np
def create_f1():
def f1_function(y_true, y_pred):
y_pred_binary = tf.where(y_pred>=0.5, 1., 0.)
tp = tf.reduce_sum(y_true * y_pred_binary)
predicted_positives = tf.reduce_sum(y_pred_binary)
possible_positives = tf.reduce_sum(y_true)
return tp, predicted_positives, possible_positives
return f1_function
class F1_score(keras.metrics.Metric):
def __init__(self, **kwargs):
super().__init__(**kwargs) # handles base args (e.g., dtype)
self.f1_function = create_f1()
self.tp_count = self.add_weight("tp_count", initializer="zeros")
self.all_predicted_positives = self.add_weight('all_predicted_positives', initializer='zeros')
self.all_possible_positives = self.add_weight('all_possible_positives', initializer='zeros')
def update_state(self, y_true, y_pred,sample_weight=None):
tp, predicted_positives, possible_positives = self.f1_function(y_true, y_pred)
self.tp_count.assign_add(tp)
self.all_predicted_positives.assign_add(predicted_positives)
self.all_possible_positives.assign_add(possible_positives)
def result(self):
precision = self.tp_count / self.all_predicted_positives
recall = self.tp_count / self.all_possible_positives
f1 = 2*(precision*recall)/(precision+recall)
return f1
X = np.random.random(size=(1000, 10))
Y = np.random.randint(0, 2, size=(1000,))
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)
model = keras.models.Sequential([
keras.layers.Dense(5, input_shape=[X.shape[1], ]),
keras.layers.Dense(1, activation='sigmoid')
])
model.compile(loss='binary_crossentropy', optimizer='SGD', metrics=[F1_score()])
history = model.fit(X_train, y_train, epochs=5, validation_data=(X_test, y_test))