Python script using data from TGS Salt Identification Challenge · 17,976 views · 2y ago
import torch
import numpy as np
# PyTroch version
SMOOTH = 1e-6
def iou_pytorch(outputs: torch.Tensor, labels: torch.Tensor):
# You can comment out this line if you are passing tensors of equal shape
# But if you are passing output from UNet or something it will most probably
# be with the BATCH x 1 x H x W shape
outputs = outputs.squeeze(1) # BATCH x 1 x H x W => BATCH x H x W
intersection = (outputs & labels).float().sum((1, 2)) # Will be zero if Truth=0 or Prediction=0
union = (outputs | labels).float().sum((1, 2)) # Will be zzero if both are 0
iou = (intersection + SMOOTH) / (union + SMOOTH) # We smooth our devision to avoid 0/0
thresholded = torch.clamp(20 * (iou - 0.5), 0, 10).ceil() / 10 # This is equal to comparing with thresolds
return thresholded # Or thresholded.mean() if you are interested in average across the batch
# Numpy version
# Well, it's the same function, so I'm going to omit the comments
def iou_numpy(outputs: np.array, labels: np.array):
outputs = outputs.squeeze(1)
intersection = (outputs & labels).sum((1, 2))
union = (outputs | labels).sum((1, 2))
iou = (intersection + SMOOTH) / (union + SMOOTH)
thresholded = np.ceil(np.clip(20 * (iou - 0.5), 0, 10)) / 10
return thresholded # Or thresholded.mean()
Run Time
4.5 seconds
Timeout Exceeded
False
Output Size
0
Accelerator
None
Input
460.69 MB
folder
Data Sources
arrow_drop_down
TGS Salt Identification Challenge
TGS Salt Identification Challenge
drive_zip_outline
competition_data.zip
competition_data.zip
calendar_view_week
depths.csv
depths.csv
drive_zip_outline
flamingo.zip
flamingo.zip
calendar_view_week
sample_submission.csv
sample_submission.csv
drive_zip_outline
test.zip
test.zip
calendar_view_week
train.csv
train.csv
drive_zip_outline
train.zip
train.zip
competition_data.zip(216.61 MB)
get_app
Download
We use cookies on Kaggle to deliver our services, analyze web traffic, and improve your experience on the site. By using Kaggle, you agree to our use of cookies.
Got it
Learn more
Comments (32)
Sort by
Hotness
arrow_drop_downAndrei Spiridonov • Posted on Version 8 of 8 • 2 years ago • Options • Reply
1
Ilya, great work!
Thank you!
weakEnosh • Posted on Version 6 of 8 • 2 years ago • Options • Reply
1
in the 36 line 'thresholded = np.ceil(np.clip(20 * (ious - 0.5), 0, 10)) / 10', the variable 'ious' need be modified as 'iou'
Ilya EzepovTopic Author • Posted on Version 6 of 8 • 2 years ago • Options • Reply
0
Indeed, thank you!
NarayanaSwamy • Posted on Version 5 of 8 • 2 years ago • Options • Reply
1
I had earlier written this on one of my kernel - this code doesnot require calculation of range of thresholds and comparing it. It will give you the same answer as your code.
This simple logic here will give the correct result for precision
without going thru each threshold
Ilya EzepovTopic Author • Posted on Version 6 of 8 • 2 years ago • Options • Reply
0
Thos are great suggestions, thank you!
Yet, I'm not sure about the second one. For example for iou == 0 (complete miss) it returns negative metric which is not right. So I corrected it a little to be
torch.clamp(20 * (iou - 0.5), 0, 10).ceil() / 10
NarayanaSwamy • Posted on Version 6 of 8 • 2 years ago • Options • Reply
0
Sorry, I left out one line of code that needs to come after the iou_metric calculation:
Ilya EzepovTopic Author • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
Even like that, I think it's not working correctly. Say for
iou = 0.5509
your code will return 0.1, while the correct answer should be 0.2 (iou > 0.5 and >0.55, so it's 2/10). For 0.551 it would be correct though.So, great news, you were sligtly underestimating your models all the time! It's great, it means that they are actually better than you thought :)
NarayanaSwamy • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
I think if I change the 0.451 to 0.450009, it will give the precision on this score until the 5th digit. IOU of 0.55001 will still yield the right result of 0.2
Zuppi • Posted on Version 5 of 8 • 2 years ago • Options • Reply
1
Hi Ilya, thank you for the code. I get the following error:
Booth outputs and labels are float
Ilya EzepovTopic Author • Posted on Version 5 of 8 • 2 years ago • Options • Reply
2
That's exactly the reason, both should be byte or int. Just add this to the functuion body:
labels = labels.byte()
And
outputs
in that case is already thresholded predicted mask. If your net outputs probablities just dooutput > 0.5
(you may want ot optimize this threshold).Strideradu • Posted on Version 7 of 8 • 2 years ago • Options • Reply
2
If my output logits, how should I use your function, there was error about
RuntimeError: cbitand is only supported for integer type tensors at /pytorch/aten/src/THC/generated/../generic/
, and if I change labels to byte, there is another error said thatRuntimeError: Expected object of type torch.cuda.FloatTensor but found torch.cuda.ByteTensor for argument #2 'other'
Muhamad Iqbal • Posted on Version 8 of 8 • 3 months ago • Options • Reply
0
Hi @iezepov, I have a question
What if the IoU i need to calculate is from bounding boxes coordinates?
VIGNESH VENKATARAMAN • Posted on Version 8 of 8 • 10 months ago • Options • Reply
0
could you add an example of input type
Tommao • Posted on Version 8 of 8 • 2 years ago • Options • Reply
0
Hi, @Ilya Ezepov. Thank you for your sharing.
I got some confusion on this line: thresholded = torch.clamp(20 * (iou - 0.5), 0, 10).ceil() / 10
why do you multiple 20?
could you shed some lights on me?
thanks a lot
Ilya EzepovTopic Author • Posted on Version 8 of 8 • 2 years ago • Options • Reply
4
Sure thing! It looks tricky because we do few things at once here. Namely, we map the IOU values to a different scale (x => 2*x - 1) and do the rounding.
We need the mapping because IOU=0.5 means 0 on the leaderboard, and IOU=1.0 means 1.0. Thus the mapping we a looking for is x => 2 * x - 1. And that's why I have that
20 * (iou - 0.5)
. As we later divide by 10 you can think of it as of20 * (iou - 0.5) / 10 = 2 * (iou - 0.5) = 2 * iou - 1
. But that's not enough. If original IOU was from 0 to 1, than2 * iou - 1
is from -1 to 1. Now to the second part.Here we don't care about the values itself, only whether it exceeds specific thresholds. Therefore we need to round our maped values. How do you round numbers to 0.1, 0.2…? You multiply by 10, round to integers and devide by 10. That's why we have
(10 * x).ceil() / 10
. In reality we have20 * x
but you already know why.And finnaly we have
torch.clamp(..., 0, 10)
to omit all IOUs below 0.5 (or mapped IOUs below 0).Caleb • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
Caleb • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
Caleb • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
Not sure if this is an issue with Pytorch 0.4, but with 0.3,
intersection = (outputs & labels).sum((1, 2)).float()
yielded different results than:
intersection = (outputs & labels).float().sum(1).sum(1)
The sum of the byte tensor was different than the sum of the float tensor, with the sum of the float tensor yielding the correct result. Summing across multiple dimensions was apparently also implemented in 0.4.
Ilya EzepovTopic Author • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
Thanks for that, I'll fix it.
(outputs & labels).float().sum((1, 2))
Would be the best looking, I guesss.
Frank J. Ebbers • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
**take care of indentation as not shown in markdown
Ilya EzepovTopic Author • Posted on Version 8 of 8 • 2 years ago • Options • Reply
0
Hello! Thanks for your comment, but I think it's not the best one, most because try-except is quite a dangerous pattern. Especially if your except is catching all errors (like in your example). For example, image your function will receive
None
or some string as an input. It shouldn't, but all can happen. Nownp.argwhere(thresholds < iou)
will fail, but you wouldn't know about it because of except clause. It will just silently return 0.Zuppi • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
So basically, if I want to choose the best treshold I have to iterate over all of them an pick the ones that give mes the MAXIMUM mean iou (so the output of your function right?). I have done some testing and if in my validation test I get, for example, an mean iou of 0.750, on the publisc score I get always 0.05/0.06 less, so (0.700). Can you confirm this?
Ilya EzepovTopic Author • Posted on Version 7 of 8 • 2 years ago • Options • Reply
0
A gap between a local validation and a public LB is a normal thing because and happens often, yet 0.05-0.06 is maybe to big for this competition. I observe 0.02 gap (not k-fold, just 20% hold out). Maybe you want to stratify your validation by depth and/or salt %. Otherwise, your hold-out can be quite different from the training/public/private data and you can get a big gap.
Zuppi • Posted on Version 5 of 8 • 2 years ago • Options • Reply
0
Thank you again for your help :)
So assuming I have the output of my network with shape
BATCH x 1 x H x W
For example, a network with sigmoid activation. At that point I cannot just convert to
byte
(of course!). But I should, for each threshold, calculate the iou. So, something likeHow can I do this with your code?
Ilya EzepovTopic Author • Posted on Version 7 of 8 • 2 years ago • Options • Reply
1
You can do something like:
Ilya EzepovTopic Author • Posted on Version 7 of 8 • 2 years ago • Options • Reply
1
Dor some reason markdown is not working :(
Posted on Version 8 of 8 • 5 months ago
This comment was deleted.
Posted on Version 7 of 8 • 2 years ago
This comment was deleted.
Posted on Version 7 of 8 • 2 years ago
This comment was deleted.
Posted on Version 7 of 8 • 2 years ago
This comment was deleted.
Posted on Version 7 of 8 • 2 years ago
This comment was deleted.
Posted on Version 7 of 8 • 2 years ago
This comment was deleted.