7

I have simple seq2seq model:

import seq2seq
import numpy as np
import keras.backend as K

from seq2seq.models import Seq2Seq
from keras.models import Model
from keras.models import Sequential
from keras.layers import Embedding, Input, TimeDistributed, Activation

BLOCK_LEN = 60
EVENTS_CNT = 462

input = Input((BLOCK_LEN,))
embedded = Embedding(input_dim=EVENTS_CNT+1, output_dim=200)(input)
emb_model = Model(input, embedded)

seq_model = Seq2Seq(batch_input_shape=(None, BLOCK_LEN, 200), hidden_dim=200, output_length=BLOCK_LEN, output_dim=EVENTS_CNT)
model = Sequential()
model.add(emb_model)
model.add(seq_model)
model.add(TimeDistributed(Activation('softmax')))

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
model_1 (Model)              (None, 60, 200)           92600     
_________________________________________________________________
model_12 (Model)             (None, 60, 462)           1077124   
_________________________________________________________________
time_distributed_2 (TimeDist (None, 60, 462)           0         
=================================================================
Total params: 1,169,724
Trainable params: 1,169,724
Non-trainable params: 0
_________________________________________________________________

And I'm trying to create my own metric:

def symbol_acc(true, predicted):
    np_y_true = K.get_value(true)
    np_y_pred = K.get_value(predicted)
    return K.mean(np_y_true == np_y_pred)

And if I try to compile model with this metric I get an error "You must feed a value for placeholder tensor" with the following message:

InvalidArgumentError                      Traceback (most recent call last)
C:\Users\Anna\Anaconda3\lib\site-packages\tensorflow\python\client\session.py in _do_call(self, fn, *args)
   1322     try:
-> 1323       return fn(*args)
   1324     except errors.OpError as e:

C:\Users\Anna\Anaconda3\lib\site-packages\tensorflow\python\client\session.py in _run_fn(session, feed_dict, fetch_list, target_list, options, run_metadata)
   1301                                    feed_dict, fetch_list, target_list,
-> 1302                                    status, run_metadata)
   1303 

C:\Users\Anna\Anaconda3\lib\site-packages\tensorflow\python\framework\errors_impl.py in __exit__(self, type_arg, value_arg, traceback_arg)
    472             compat.as_text(c_api.TF_Message(self.status.status)),
--> 473             c_api.TF_GetCode(self.status.status))
    474     # Delete the underlying status object from memory otherwise it stays alive

InvalidArgumentError: You must feed a value for placeholder tensor 'time_distributed_2_target' with dtype float and shape [?,?,?]
     [[Node: time_distributed_2_target = Placeholder[dtype=DT_FLOAT, shape=[?,?,?], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

But the following code is working fine (doesn't produce any exceptions):

def symbol_acc2(true, predicted):
    true = np.array(true)
    predicted = np.array(predicted)
    return K.variable((true == predicted).mean())

Can you please explain me what does that exception mean? I thought symbol_acc and symbol_acc2 are doing the same thing. I'm new in NNs and keras so maybe I don't see some obvious things. I saw similar questions on stackoverflow but didn't find the fit answer for my situation.

Anna
  • 189
  • 4
  • 9
  • Can you show how you are getting values for `true` and `predicted` to pass to your metric? – Nathan May 13 '18 at 23:02
  • I'm doing model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=[symbol_acc]) – Anna May 14 '18 at 08:48

2 Answers2

8

Metrics, losses, and the entire model are "symbolic" tensors.

This means, they have absolutely no data (or value) until the moment you start fitting or predicting.

When you call K.get_value you're trying to get a value that doesn't exist. (It will only exist when you "feed the data" to the model. What it calls a placeholder is an empty input tensor that is expecting to receive the data when fitting or predicting).

The solution to your problem is simply not trying to get the values. (The numpy version will not work either, the values don't exist at the moment this function is compiled).

You gotta keep all operations symbolic, and they will be performed when you feed the data.

So:

def symbol_acc(true, predicted):
    isEqual = K.cast(K.equal(true,predicted),K.floatx())
    return K.mean(isEqual)
Daniel Möller
  • 84,878
  • 18
  • 192
  • 214
  • Daniel, maybe you could explain me how to solve the following issue: in my metric I have `true` shape (15, 60) and `predicted` shape (15, 60, 462). Each value in `true` 60 values is a number from 0 to 461 and each value in `predicted` 60 is a vector of size 462 of probability destribution for each number from 0 to 461. I wanna make `true` the same shape as `predicted`: make vector of size 462 with 1 on the number position and 0s on the others. I can create one hot array with `K.gather(K.eye(462), tf.cast(number, tf.int32))` but I don't know how to apply it to array without knowing this array. – Anna May 14 '18 at 16:15
  • True and predicted are always the same shape. – Daniel Möller May 14 '18 at 16:18
  • No, I faced the problems with fitting my previous model, so I deleted TimeDistributed layer and added `model.compile(... target_tensors=[tf.placeholder(tf.int32, [None, 60])] ...)` and now they have different shapes – Anna May 14 '18 at 16:23
  • I never used `target_tensors` before... but if you're targetting a placeholder, how do you intend to feed it when training? – Daniel Möller May 14 '18 at 16:26
  • I think you could probably add a new question explaining in detail your model and your expected output, so we can solve it without having to use non-standard solutions. – Daniel Möller May 14 '18 at 16:31
  • Honestly I don't understand what problem do you mean (as I said, I new in keras). I feed input data with shape (62714, 60) – Anna May 14 '18 at 16:34
  • Don't you have any idea how to change shape in custom metric? I think it is easy solution except that part :) – Anna May 14 '18 at 16:36
  • The usual way is: make sure your model's output shape is the same as your true data. If your true data is not one-hot enconded, then in a preprocessing phase turn it into one-hot encoded. You use the function from `from keras.utils import to_categorical` for that. – Daniel Möller May 14 '18 at 16:41
  • If your model is not outputting one-hot encoded data (and your application is classification), then the chance that its loss function is not differentiable is huge. – Daniel Möller May 14 '18 at 16:42
  • What I mean is that there is very probably an easier standard solution that won't cause you a chain of errors to solve. – Daniel Möller May 14 '18 at 16:45
  • From a quick test I made here, even using `target_tensors`, the model will still demand true and predicted with the same shape. – Daniel Möller May 14 '18 at 16:54
  • Daniel, thank you for your advices. I described my task and problem in details in [that topic](https://stackoverflow.com/questions/50339622/keras-correctness-of-model-and-issues-with-custom-metric) – Anna May 14 '18 at 22:06
4

In symbol_acc, the non-working version, tf.keras.backend.get_value() (K.get_value() in your code) will go and fetch the value of a variable as a Numpy array. Then this line K.mean(np_y_true == np_y_pred) first creates another (boolean) Numpy array based on equality, and tf.keras.backend.mean() tries to treat that Numpy array as a tensor and it just doesn't work this way.

The error is shown because at graph creation time the true has no value yet, hasn't been fed.

symbol_acc2 doesn't throw an error, but would not work either, because at graph creation time true and predicted are just empty tensors. Numpy will not change that, but the comparison will fail, taking the mean of that will yield zero, and you're just creating a variable with the value zero. Consider this code (tested):

import keras.backend as K
import numpy as np

true = K.placeholder( ( 2, ) )
predicted = K.placeholder( ( 2, ) )
a = np.array( true )
b = np.array( predicted )
c = a == b
print( c, c.mean() )

outputs:

(False, 0.0)

regardless of the data (there's not even data in the tensors yet.)

In order to achieve what you want, that is calculating the accuracy of your predictions, you can simply use

def symbol_acc( true, predicted ):
    return K.mean( K.cast_to_floatx( K.equal( true, predicted ) ) )

Or you can make your own life easier, and look into Keras' own categorical_accuracy metric.

Peter Szoldan
  • 4,792
  • 1
  • 14
  • 24