Reputation: 716
I have this code to understand how Jaccard Index works and I am trying to implement it as a Keras custom metric, but the return of this function is wrong and I have no idea why (I am new to Keras btw).
This function creates a rectangular area to test intersections:
def block(start, width=20, heigth=10, length=60):
block = np.zeros((1, heigth, length))
block[:, :, start:(start + width)] = 1
return block.astype('uint8')
If I get a block(10)
and a block(20)
there should be two 10x20 blocks with a 10x10 area of intersection so the union must be 2*(10*20) - 10*10
which is confirmed by this Numpy line:
np.sum(((block(10) + block(20)) > 0).astype('uint8'))
But when I try to use this Keras function:
def test(a, b):
return K.sum(K.cast((a + b) > 0, dtype='uint8'))
The result is 44 when I call it with K.eval(test(block(10), block(20)))
EDIT (new test):
def test(a, b):
return K.sum(a), K.sum(b), K.sum(a) + K.sum(b)
[K.eval(elem) for elem in test(block(10), block(20))]
This is the result: [200, 200, 144]
What am I doing wrong to get this result?
Upvotes: 0
Views: 101
Reputation: 2948
The dtype of the Keras sum is inferred from the object that is summed over, so in this case the sum will be of type uint8
. Your result is 300. uint8
can take 256 different values, and 300 - 44 = 256
. If you take a closer look on your python code:
blocks = block(10) + block(20) # dtype='uint8'
mask = (blocks > 0) # dtype=bool
mask_as_uint = mask.astype('uint8') # dtype='uint8', which is an unnescessary conversion
np.sum(mask_as_uint) # dtype='uint64
So your result is of type uint64
, while you ask Keras to give you a result in uint8
. In fact the following gives 44 as an answer as well:
np.int8(300)
In short, switch to a bigger dtype for the sum or do the operations in an order manually where uint8
doesn't have shortcomings.
Upvotes: 1