Оценщик Tensorflow не сходится на модели, преобразованной из Keras (при использовании binary_crossentropy) - PullRequest
0 голосов
/ 27 февраля 2019

Я застрял довольно долго, используя функциональность model_to_estimator в Оценщиках Tensorflow.Кажется, проблема в том, что Keras допускает потерю binary_crossentropy на одном выходе нейрона Dense.

В моем случае я передаю последовательные данные RNN и хочу выяснить, приводит ли последовательность к преобразованиюили нет.Код (также можно найти в https://colab.research.google.com/drive/194Puigi-LdzxZup6LNREk47l9uP0_Dx9) для этого будет

import numpy as np
import pandas as pd
import tensorflow as tf

np.random.seed(2)


data = np.random.randint(1,500,size=(10000, 50)) # create something like 50 words out of a vocab of 500


#split
train = data[:7999]
val = data[8000:]

def _input_fn2(arr, batch_size=500, shuffle=False):
  arr_copy = arr.copy()
  def _parse_func(features):   
    sum = tf.math.reduce_sum(features)  

    label = tf.cond(sum >= 15000, lambda: np.array([1]), lambda: np.array([0])) # label=true if sum is larger 15000, gives about 1% true
    return (features, label)

  dataset = tf.data.Dataset.from_tensor_slices(arr_copy)
  dataset = dataset.map(_parse_func)
  dataset = dataset.shuffle(200)
  dataset = dataset.batch(batch_size)

  dataset = dataset.repeat()  
  return dataset

from tensorflow.keras.layers import Dense, Input, CuDNNGRU, Embedding
import tensorflow.keras.backend as K


inputs = Input(shape=(50,))

embedding = Embedding(
    output_dim=5,
    input_dim=500,
    input_length=50)(inputs)


lstm = CuDNNGRU(
    units=5,
    input_shape=((5,1)),
    return_sequences=False,
)(embedding)
outputs = Dense(1, activation='sigmoid',name='final')(lstm)

model = tf.keras.Model(inputs, outputs)


def true_positives(y_true, y_pred):
  true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
  return true_positives

def false_positives(y_true, y_pred):
  true_positives = K.sum(K.round(K.clip((1 - y_true) * y_pred, 0, 1)))
  return true_positives

def true_negatives(y_true, y_pred):
  true_positives = K.sum(K.round(K.clip((1 - y_true) * (1 - y_pred), 0, 1)))
  return true_positives

def false_negatives(y_true, y_pred):
  true_positives = K.sum(K.round(K.clip(y_true * (1 - y_pred), 0, 1)))
  return true_positives

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


model.compile(
    optimizer=tf.keras.optimizers.Adam(),
    loss='binary_crossentropy', 
    metrics=[
        'acc',
        true_positives,
        true_negatives,
        false_positives,
        false_negatives,
        recall,
        precision
    ]
)
print(model.summary())

train_ds = _input_fn2(train, shuffle=True)
val_ds = _input_fn2(val)
​
model.fit(
    train_ds, 
    steps_per_epoch=50  ,
    epochs=100, 
    validation_data=val_ds,
    validation_steps=10,
    verbose=2
)

Это работает хорошо, модель сходится и начинает учиться.

Эпоха 100/ 100 - 2 с - потеря: 3,5754e-04 - в соотв. 1,0000 - истина_позитивов: 3,2000 - истина_отрицательных: 496,7400 - ложь_позитивов: 0,0000e + 00 - ложь_отрицательных: 0,0000e + 00 - отзыв: 0,9400 - точность: 0,9400 - потеря вальса: 0,1281 -val_acc: 0.9806 - val_true_positives: 0.0000e + 00 - val_true_negatives: 490.3000 - val_false_positives: 4.5000 - val_false_negatives: 5.2000 - val_recall: 0.0000e + 00 - val_precision: 0.0000e + 00

1013
1013 *1013* 1013он предполагает отрицательный результат, это связано с дисбалансом в наборе данных и, вероятно, это правильно.

Теперь преобразование этого в модель оценщика, подобную этой:

from tensorflow.keras.estimator import model_to_estimator
from tensorflow.estimator import train_and_evaluate, RunConfig
from tensorflow.estimator import TrainSpec, EvalSpec
from tensorflow import metrics

from tensorflow.contrib.estimator import add_metrics

run_config = RunConfig(
    save_checkpoints_secs=5,
    keep_checkpoint_max=10
)

def eval_metrics(features, labels, predictions):
    return {
        'precision_streaming': metrics.precision(labels=labels, predictions=predictions['final']),
        'recall_streaming': metrics.recall(labels=labels, predictions=predictions['final']),
        'true_positives_streaming': metrics.true_positives(labels=labels, predictions=predictions['final']),
        'true_negatives_streaming': metrics.true_negatives(labels=labels, predictions=predictions['final']),
        'false_positives_streaming': metrics.false_positives(labels=labels, predictions=predictions['final']),
        'false_negatives_streaming': metrics.false_negatives(labels=labels, predictions=predictions['final'])  
    }


estimator = model_to_estimator(keras_model=model, config=run_config)
estimator = add_metrics(estimator, eval_metrics) #took out these metrics for showcase

train_spec = TrainSpec(
  input_fn=lambda: _input_fn2(train, shuffle=True), max_steps=2000 
)


eval_spec = EvalSpec(input_fn=lambda: _input_fn2(val), steps=4)

score = train_and_evaluate(estimator, train_spec, eval_spec)

print(score)

После сбросамоделируя и обучая версию на основе оценщика, модель не сходится, а только кажется, что теперь предсказывает истины

({'binary_accuracy': 0.9865, 'false_positives_streaming ': 0,0,' false_positives_streaming ': 1979,0,' Precision_streaming ': 0,0105,' rec_streaming ': 1,0,' true_negatives_streaming ': 0,0,' true_positives_streaming ': 21,0,' global_step ': 2000}, []

Теперь мне удалось заставить это работать, используя последний слой Dense (2), горячее кодирование метки и переключив функцию потерь на sparse_categorical_crossentropy ... но я действительно предпочел бы сохранить один выходной класспоскольку это облегчает мои вычисления f1-счетчика и много чего еще.

Вдохновенное предположение состоит в том, что Оценщик не может распределить потери по одному плотному выходному слою, Керасу как-то удается это сделать.

Любая помощь будет принята с благодарностью

Bests wirtsi

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...