在Tensorflow 2中的每个纪元后计算每个班级的召回率

时间:2019-05-30 16:56:24

标签: python tensorflow machine-learning keras tensorflow2.0

我正在尝试在使用Tensorflow 2的Keras API的模型中的每个时期之后,针对每个类在二进制和多类(一种热编码)分类方案中计算召回率。例如对于二进制分类,我希望能够做到

import tensorflow as tf
model = tf.keras.Sequential()
model.add(...)
model.add(tf.keras.layers.Dense(1))

model.compile(metrics=[binary_recall(label=0), binary_recall(label=1)], ...)
history = model.fit(...)

plt.plot(history.history['binary_recall_0'])
plt.plot(history.history['binary_recall_1'])
plt.show()

或者在多类情况下,我想做类似的事情

model = tf.keras.Sequential()
model.add(...)
model.add(tf.keras.layers.Dense(3))

model.compile(metrics=[recall(label=0), recall(label=1), recall(label=2)], ...)
history = model.fit(...)

plt.plot(history.history['recall_0'])
plt.plot(history.history['recall_1'])
plt.plot(history.history['recall_2'])
plt.show()

我正在为一个不平衡的数据集进行分类,并且希望能够看到我的少数族裔的召回率在什么时候开始下降。

我在https://stackoverflow.com/a/41717938/373655的多类分类器中找到了特定类的精度实现。我正在尝试使其适应我的需要,但是keras.backend对我来说还是很陌生,因此,我们将不胜感激。

我也不清楚我是否可以使用Keras metrics(因为它们是在每个批次的末尾进行计算,然后取平均值)还是我是否需要使用Keras callbacks(可以运行)在每个时期结束时)。在我看来,召回不应该有所作为(例如8/10 == (3/5 + 5/5) / 2),但这就是为什么在Keras 2中取消了召回的原因,所以也许我缺少了一些东西(https://github.com/keras-team/keras/issues/5794

3 个答案:

答案 0 :(得分:2)

在TF2中,kubectl get pods/$POD_NAME -o yaml获得了一个metadata.uid成员,可以做到这一点。使用FashionMNIST的示例:

tf.keras.metrics.Recall

在TF 1.13中,class_id没有这个import tensorflow as tf (x_train, y_train), _ = tf.keras.datasets.fashion_mnist.load_data() x_train = x_train[..., None].astype('float32') / 255 y_train = tf.keras.utils.to_categorical(y_train) input_shape = x_train.shape[1:] model = tf.keras.Sequential([ tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=input_shape), tf.keras.layers.MaxPool2D(pool_size=2), tf.keras.layers.Dropout(0.3), tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'), tf.keras.layers.MaxPool2D(pool_size=2), tf.keras.layers.Dropout(0.3), tf.keras.layers.Flatten(), tf.keras.layers.Dense(units=256, activation='relu'), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(units=10, activation='softmax')]) model.compile(loss='categorical_crossentropy', optimizer='Adam', metrics=[tf.keras.metrics.Recall(class_id=i) for i in range(10)]) model.fit(x_train, y_train, batch_size=128, epochs=50) 参数,但是可以通过子类添加(在TF2的alpha发行版中似乎是不可能的)。

tf.keras.metric.Recall

答案 1 :(得分:2)

我们可以使用sklearn和keras classification_report中的Callback来实现这一目标。

工作代码示例(带注释)

import tensorflow as tf
import keras
from tensorflow.python.keras.layers import Dense, Input
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.callbacks import Callback
from sklearn.metrics import recall_score, classification_report
from sklearn.datasets import make_classification
import numpy as np
import matplotlib.pyplot as plt

# Model -- Binary classifier
binary_model = Sequential()
binary_model.add(Dense(16, input_shape=(2,), activation='relu'))
binary_model.add(Dense(8, activation='relu'))
binary_model.add(Dense(1, activation='sigmoid'))
binary_model.compile('adam', loss='binary_crossentropy')

# Model -- Multiclass classifier
milticlass_model = Sequential()
milticlass_model.add(Dense(16, input_shape=(2,), activation='relu'))
milticlass_model.add(Dense(8, activation='relu'))
milticlass_model.add(Dense(3, activation='softmax'))
milticlass_model.compile('adam', loss='categorical_crossentropy')

# callback to find metrics at epoch end
class Metrics(Callback):
    def __init__(self, x, y):
        self.x = x
        self.y = y if (y.ndim == 1 or y.shape[1] == 1) else np.argmax(y, axis=1)
        self.reports = []

    def on_epoch_end(self, epoch, logs={}):
        y_hat = np.asarray(self.model.predict(self.x))
        y_hat = np.where(y_hat > 0.5, 1, 0) if (y.ndim == 1 or y_hat.shape[1] == 1)  else np.argmax(y_hat, axis=1)
        report = classification_report(self.y,y_hat,output_dict=True)
        self.reports.append(report)
        return

    # Utility method
    def get(self, metrics, of_class):
        return [report[str(of_class)][metrics] for report in self.reports]

# Generate some train data (2 class) and train
x, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
                           random_state=1, n_clusters_per_class=1)
metrics_binary = Metrics(x,y)
binary_model.fit(x, y, epochs=30, callbacks=[metrics_binary])

# Generate some train data (3 class) and train
x, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
                           random_state=1, n_clusters_per_class=1, n_classes=3)
y = keras.utils.to_categorical(y,3)
metrics_milticlass = Metrics(x,y)
milticlass_model.fit(x, y, epochs=30, callbacks=[metrics_milticlass])

# Plotting 
plt.close('all')
plt.plot(metrics_binary.get('recall',0), label='Class 0 recall') 
plt.plot(metrics_binary.get('recall',1), label='Class 1 recall') 

plt.plot(metrics_binary.get('precision',0), label='Class 0 precision') 
plt.plot(metrics_binary.get('precision',1), label='Class 1 precision') 

plt.plot(metrics_binary.get('f1-score',0), label='Class 0 f1-score') 
plt.plot(metrics_binary.get('f1-score',1), label='Class 1 f1-score') 
plt.legend(loc='lower right')
plt.show()

plt.close('all')
for m in ['recall', 'precision', 'f1-score']:
    for c in [0,1,2]:
        plt.plot(metrics_milticlass.get(m,c), label='Class {0} {1}'.format(c,m))

plt.legend(loc='lower right')
plt.show()

输出

enter image description here

enter image description here

优势:

  • classification_report提供了很多指标
  • 可以将火车数据的验证数据传递给Metrics构造函数,从而计算出验证数据的指标。

答案 2 :(得分:0)

执行此操作的方法有多种,但是使用callback似乎是最好的也是大多数 kerasy 方法。在我向您展示方法之前,请做一个旁注:

  

我也不清楚我是否可以使用Keras指标(因为它们是   在每批结束时计算,然后取平均值),或者如果我需要   使用Keras回调(可以在每个时期结束时运行)。

这不是事实。 Keras' callbacks可以使用以下方法:

  • on_epoch_begin:在每个纪元开始时调用。
  • on_epoch_end:在每个时代结束时调用。
  • on_batch_begin:在每批开始时调用。
  • on_batch_end:在每批处理结束时调用。
  • on_train_begin:在模型训练开始时调用。
  • on_train_end:在模型训练结束时调用。

这是事实,无论您使用的是keras还是tf.keras

下面您可以找到我的自定义回调实现。

class RecallHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.recall = {}

    def on_epoch_end(self, epoch, logs={}):
        # Compute and store recall for each class here.
        self.recall[...] = 42

history = RecallHistory()
model.fit(..., callbacks=[history])

print(history.recall)