I must be getting something terribly wrong with the fast-ai library, since I seem to be the only one having this problem. Everytime I try the learning rate finder or training the network, it gives me an error. It took me a week to produce this specific error message, which made me check the mask values. It turns out they are either 0 for background pixels and 255 for foreground ones. This is a problem as I only have two classes. How can I change the 255 values to 1 within my Databunch Object? Is there a way to divide each mask value by 255 or do I need to do it beforehand somehow? I am kinda lost within the process.
Here's the error message i'm getting:
LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-20-c7a9c29f9dd1> in <module>()
----> 1 learn.lr_find()
2 learn.recorder.plot()
8 frames
/usr/local/lib/python3.6/dist-packages/fastai/train.py in lr_find(learn, start_lr, end_lr, num_it, stop_div, wd)
39 cb = LRFinder(learn, start_lr, end_lr, num_it, stop_div)
40 epochs = int(np.ceil(num_it/len(learn.data.train_dl)))
---> 41 learn.fit(epochs, start_lr, callbacks=[cb], wd=wd)
42
43 def to_fp16(learn:Learner, loss_scale:float=None, max_noskip:int=1000, dynamic:bool=True, clip:float=None,
/usr/local/lib/python3.6/dist-packages/fastai/basic_train.py in fit(self, epochs, lr, wd, callbacks)
198 else: self.opt.lr,self.opt.wd = lr,wd
199 callbacks = [cb(self) for cb in self.callback_fns + listify(defaults.extra_callback_fns)] + listify(callbacks)
--> 200 fit(epochs, self, metrics=self.metrics, callbacks=self.callbacks+callbacks)
201
202 def create_opt(self, lr:Floats, wd:Floats=0.)->None:
/usr/local/lib/python3.6/dist-packages/fastai/basic_train.py in fit(epochs, learn, callbacks, metrics)
99 for xb,yb in progress_bar(learn.data.train_dl, parent=pbar):
100 xb, yb = cb_handler.on_batch_begin(xb, yb)
--> 101 loss = loss_batch(learn.model, xb, yb, learn.loss_func, learn.opt, cb_handler)
102 if cb_handler.on_batch_end(loss): break
103
/usr/local/lib/python3.6/dist-packages/fastai/basic_train.py in loss_batch(model, xb, yb, loss_func, opt, cb_handler)
28
29 if not loss_func: return to_detach(out), to_detach(yb[0])
---> 30 loss = loss_func(out, *yb)
31
32 if opt is not None:
/usr/local/lib/python3.6/dist-packages/fastai/layers.py in __call__(self, input, target, **kwargs)
241 if self.floatify: target = target.float()
242 input = input.view(-1,input.shape[-1]) if self.is_2d else input.view(-1)
--> 243 return self.func.__call__(input, target.view(-1), **kwargs)
244
245 def CrossEntropyFlat(*args, axis:int=-1, **kwargs):
/usr/local/lib/python3.6/dist-packages/torch/nn/modules/module.py in __call__(self, *input, **kwargs)
539 result = self._slow_forward(*input, **kwargs)
540 else:
--> 541 result = self.forward(*input, **kwargs)
542 for hook in self._forward_hooks.values():
543 hook_result = hook(self, input, result)
/usr/local/lib/python3.6/dist-packages/torch/nn/modules/loss.py in forward(self, input, target)
914 def forward(self, input, target):
915 return F.cross_entropy(input, target, weight=self.weight,
--> 916 ignore_index=self.ignore_index, reduction=self.reduction)
917
918
/usr/local/lib/python3.6/dist-packages/torch/nn/functional.py in cross_entropy(input, target, weight, size_average, ignore_index, reduce, reduction)
2007 if size_average is not None or reduce is not None:
2008 reduction = _Reduction.legacy_get_string(size_average, reduce)
-> 2009 return nll_loss(log_softmax(input, 1), target, weight, None, ignore_index, None, reduction)
2010
2011
/usr/local/lib/python3.6/dist-packages/torch/nn/functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction)
1836 .format(input.size(0), target.size(0)))
1837 if dim == 2:
-> 1838 ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
1839 elif dim == 4:
1840 ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
RuntimeError: Assertion `cur_target >= 0 && cur_target < n_classes' failed. at /pytorch/aten/src/THNN/generic/ClassNLLCriterion.c:97
Also this is how I set up my data:
data = (SegmentationItemList.from_df(img_df,IMAGE_PATH)
# import from df in greyscale ('L')
.split_by_rand_pct(valid_pct=0.15)
# 1/15 train/validation split
.label_from_func(get_mask, classes = array(['background','cell']))
# segmentation mask and classes
.transform(tfms, tfm_y=True, size=TILE_SHAPE)
# apply data augmentation
.databunch(bs=BATCH_SIZE)
# set batchsize
.normalize()
)
Please tell me if you need any more information. I already tried adding an 'after_open' function, which should have divided all by 255 to the 'label_from_func' part. I also know there is a div attribute within the 'open_image' function of fast-ai, which is supposed to normalize RGB values between 0 and 1, but I couldn't find one for 'label_from_func'.
Edit:
I found this post in the fastai community. However even with these answers i was not able to solve my problem. I tried adding this snippet to pass div=True into the open_mask function, but it did not work:
src.train.y.create_func = partial(open_mask, div=True)
src.valid.y.create_func = partial(open_mask, div=True)
I also tried .set_attr(mask_opener=partial(open_mask, div=True))
after .label_from_func()
, but then it throws this attribute error: AttributeError: setattr
Still need help
A mask is a binary image consisting of zero- and non-zero values. If a mask is applied to another binary or to a grayscale image of the same size, all pixels which are zero in the mask are set to zero in the output image. All others remain unchanged.
The below custom classes are necessary to deal with the binary image segmentation datasets which use 0 and 255 to encode the mask
class SegLabelListCustom(SegmentationLabelList):
def open(self, fn): return open_mask(fn, div=True)
class SegItemListCustom(SegmentationItemList):
_label_cls = SegLabelListCustom
Link for reference: https://github.com/fastai/fastai/issues/1540
Below is example of using these custom classes to create a source for a databunch.
src = (SegItemListCustom.from_folder('/home/jupyter/AerialImageDataset/train/')
.split_by_folder(train='images', valid='validate')
.label_from_func(get_y_fn, classes=labels))
I really hope this helps you out as I struggled to deal with this myself not too long ago and this was my solution. It was difficult because many of the answers I found were for previous versions and no longer worked.
Let me know if you need more clarification or help as I know how frustrating it can be to be stuck early on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With