Multi Object Detection - Error lr_find() fastai v2

I created a custom bb_pad function.
I did a reshape of the TensorBbox to convert it’s shape from (2,4) into (8,). To do the reshape you told me to add the line “bbox = torch.reshape(bbox,-1)”, but after this line was executed I got an error saying the second parameter of reshape (-1) can’t be an int, it must be a tuple of ints. On the website you attached (torch.reshape — PyTorch 1.11.0 documentation) it indicates that the second parameter of the reshape function (new shape) must be a tuple of ints.
I changed “bbox = torch.reshape(bbox,-1)” to “bbox = torch.reshape(bbox,(-1,))” and the error I was having was fixed.

I also created a custom BboxBlock with no labels, as I don’t need the bboxes to be labeled and because also I think making the bboxes labeled is a problem because for each image I need to have the bboxes with size (8,) but the labels list have size (2,), so, the sizes don’t match. To create the custom BboxBlock I followed the example shown at Share your V2 projects here - #376 by idraja.

I deleted each line of the original bb_pad function() related to the labels because I only need to return from that function aTensorImage and a TensorBbox. I also don’t use the function clip_remove_empty() because it splits a TensorBbox of size (2,4) into to 2 TensorBbox of size (1,4).

I post the modifications I did on the code:

class NoLabelBBoxLabeler(Transform):
    """ Bounding box labeler with no label """
    def setups(self, x): noop
    def decode (self, x, **kwargs):
        self.bbox,self.lbls = None,None
        return self._call('decodes', x, **kwargs)

    def decodes(self, x:TensorBBox):
        self.bbox = x
        return self.bbox if self.lbls is None else LabeledBBox(self.bbox, self.lbls)
'''
def clip_remove_empty(bbox):
    "Clip bounding boxes with image border and label background the empty ones"
    "Splits bbox of size (2,4) into to bboxes of size (1,4)"
    bbox = torch.clamp(bbox, -1, 1)
    empty = ((bbox[...,2] - bbox[...,0])*(bbox[...,3] - bbox[...,1]) <= 0.)
    return (bbox[~empty])
'''

def custom_bb_pad(samples, pad_idx=0):
    "Function that collect `samples` of bboxes and adds padding with `pad_idx`."
    #samples = [(s[0], *clip_remove_empty(*s[1:])) for s in samples] # s[0] is a tuple of TensorImage & TensorBbox, TensorBbox size is (2,4)
    #max_len = max([len(s[2]) for s in samples]) # equals to 4 (number of bbox coordinates)
    def _f(img,bbox):
        #bbox = torch.cat([bbox,bbox.new_zeros(2, 4)])
        bbox = torch.reshape(bbox,(-1,)) # convert bboxes tensor to a 1D tensor
        return img,bbox
    return [_f(*s) for s in samples]


CustomBboxBlock = TransformBlock(type_tfms=TensorBBox.create, 
                             item_tfms=[PointScaler, NoLabelBBoxLabeler], dls_kwargs = {'before_batch': custom_bb_pad})
data = DataBlock(
    blocks=(ImageBlock, CustomBboxBlock), # ImageBlock means type of inputs are images; BBoxBlock & BBoxLblBlock = type of targets are BBoxes & their labels
    get_items=get_image_files,
    n_inp=1, # number of inputs; it's 1 because the only inputs are the rx images (ImageBlock)
    get_y=[lambda o: get_bboxes(o.name)], # get_y = targets [bboxes, labels]; get_x = inputs 
    splitter = RandomSplitter (0.1), # split training/validation; parameter 0.1 means there will be 10% of validation images 
    batch_tfms= [*aug_transforms(do_flip=False, size=(120,160)), Normalize.from_stats(*imagenet_stats)] 
)

I’m stuck trying to fix an error that is produced when the line "dls.show_batch(max_n=20, figsize=(9,6))
" is executed. The error is: “IndexError: Dimension out of range (expected to be in range of [-2, 1], but got 2)”.

As you can see in the stack trace I post at the end the error doesn’t refer to the parts of code I changed, it refers to an internal function. I don’t know what is causing the error.

I also thought about creating a 2D ouput of the CNN as we commented but I don’t know how to do it, I haven’t found any documentation regarding creating a 2D ouput model. I also tried to find examples of Binary Object Detection as it’s my case on Github or a repository about lungs detection and I haven’t found anything.

I hope you or anybody can tell me what is causing the error and if the changes I did are right.
I also would appreciate if @idraja, that is the user who created the example using the BboxBlock with no labels can help me solve the error of show_batch function.

The stack trace is:

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_14288\2938148043.py in <module>
      2 
      3 dls = data.dataloaders(path_dl, path=path_dl, bs = 64) # bs: how many samples per batch to load
----> 4 dls.show_batch(max_n=20, figsize=(9,6)) # NOTE: what do the values of figsize represent?
      5 # NOTE: The batch shown contains validation and training images?

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\data\core.py in show_batch(self, b, max_n, ctxs, show, unique, **kwargs)
     98             old_get_idxs = self.get_idxs
     99             self.get_idxs = lambda: Inf.zeros
--> 100         if b is None: b = self.one_batch()
    101         if not show: return self._pre_show_batch(b, max_n=max_n)
    102         show_batch(*self._pre_show_batch(b, max_n=max_n), ctxs=ctxs, max_n=max_n, **kwargs)

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\data\load.py in one_batch(self)
    146     def one_batch(self):
    147         if self.n is not None and len(self)==0: raise ValueError(f'This DataLoader does not contain any batches')
--> 148         with self.fake_l.no_multiproc(): res = first(self)
    149         if hasattr(self, 'it'): delattr(self, 'it')
    150         return res

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\basics.py in first(x, f, negate, **kwargs)
    553     x = iter(x)
    554     if f: x = filter_ex(x, f=f, negate=negate, gen=True, **kwargs)
--> 555     return next(x, None)
    556 
    557 # Cell

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\data\load.py in __iter__(self)
    109         for b in _loaders[self.fake_l.num_workers==0](self.fake_l):
    110             if self.device is not None: b = to_device(b, self.device)
--> 111             yield self.after_batch(b)
    112         self.after_iter()
    113         if hasattr(self, 'it'): del(self.it)

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in __call__(self, o)
    198         self.fs = self.fs.sorted(key='order')
    199 
--> 200     def __call__(self, o): return compose_tfms(o, tfms=self.fs, split_idx=self.split_idx)
    201     def __repr__(self): return f"Pipeline: {' -> '.join([f.name for f in self.fs if f.name != 'noop'])}"
    202     def __getitem__(self,i): return self.fs[i]

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in compose_tfms(x, tfms, is_enc, reverse, **kwargs)
    148     for f in tfms:
    149         if not is_enc: f = f.decode
--> 150         x = f(x, **kwargs)
    151     return x
    152 

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\vision\augment.py in __call__(self, b, split_idx, **kwargs)
     33     def __call__(self, b, split_idx=None, **kwargs):
     34         self.before_call(b, split_idx=split_idx)
---> 35         return super().__call__(b, split_idx=split_idx, **kwargs) if self.do else b
     36 
     37 # Cell

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in __call__(self, x, **kwargs)
     71     @property
     72     def name(self): return getattr(self, '_name', _get_name(self))
---> 73     def __call__(self, x, **kwargs): return self._call('encodes', x, **kwargs)
     74     def decode  (self, x, **kwargs): return self._call('decodes', x, **kwargs)
     75     def __repr__(self): return f'{self.name}:\nencodes: {self.encodes}decodes: {self.decodes}'

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in _call(self, fn, x, split_idx, **kwargs)
     81     def _call(self, fn, x, split_idx=None, **kwargs):
     82         if split_idx!=self.split_idx and self.split_idx is not None: return x
---> 83         return self._do_call(getattr(self, fn), x, **kwargs)
     84 
     85     def _do_call(self, f, x, **kwargs):

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in _do_call(self, f, x, **kwargs)
     88             ret = f.returns(x) if hasattr(f,'returns') else None
     89             return retain_type(f(x, **kwargs), x, ret)
---> 90         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     91         return retain_type(res, x)
     92 

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in <genexpr>(.0)
     88             ret = f.returns(x) if hasattr(f,'returns') else None
     89             return retain_type(f(x, **kwargs), x, ret)
---> 90         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     91         return retain_type(res, x)
     92 

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\transform.py in _do_call(self, f, x, **kwargs)
     87             if f is None: return x
     88             ret = f.returns(x) if hasattr(f,'returns') else None
---> 89             return retain_type(f(x, **kwargs), x, ret)
     90         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     91         return retain_type(res, x)

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastcore\dispatch.py in __call__(self, *args, **kwargs)
    116         elif self.inst is not None: f = MethodType(f, self.inst)
    117         elif self.owner is not None: f = MethodType(f, self.owner)
--> 118         return f(*args, **kwargs)
    119 
    120     def __get__(self, inst, owner):

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\vision\augment.py in encodes(self, x)
    399     def encodes(self, x:TensorImage): return self._encode(x, self.mode)
    400     def encodes(self, x:TensorMask):  return self._encode(x, self.mode_mask)
--> 401     def encodes(self, x:(TensorPoint, TensorBBox)): return self._encode(x, self.mode, reverse=True)
    402 
    403 # Cell

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\vision\augment.py in _encode(self, x, mode, reverse)
    395     def _encode(self, x, mode, reverse=False):
    396         coord_func = None if len(self.coord_fs)==0 or self.split_idx else partial(compose_tfms, tfms=self.coord_fs, reverse=reverse)
--> 397         return x.affine_coord(self.mat, coord_func, sz=self.size, mode=mode, pad_mode=self.pad_mode, align_corners=self.align_corners)
    398 
    399     def encodes(self, x:TensorImage): return self._encode(x, self.mode)

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\vision\augment.py in affine_coord(x, mat, coord_tfm, sz, mode, pad_mode, align_corners)
    347     if sz is None: sz = getattr(x, "img_size", None)
    348     bs,n = x.shape[:2]
--> 349     pnts = stack([x[...,:2], stack([x[...,0],x[...,3]],dim=2),
    350                   stack([x[...,2],x[...,1]],dim=2), x[...,2:]], dim=2)
    351     pnts = TensorPoint(pnts.view(bs, 4*n, 2), img_size=sz).affine_coord(mat, coord_tfm, sz, mode, pad_mode)

~\AppData\Local\Programs\Python\Python37\lib\site-packages\fastai\torch_core.py in __torch_function__(self, func, types, args, kwargs)
    338         convert=False
    339         if _torch_handled(args, self._opt, func): convert,types = type(self),(torch.Tensor,)
--> 340         res = super().__torch_function__(func, types, args=args, kwargs=kwargs)
    341         if convert: res = convert(res)
    342         if isinstance(res, TensorBase): res.set_meta(self, as_copy=True)

~\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\_tensor.py in __torch_function__(cls, func, types, args, kwargs)
   1140 
   1141         with _C.DisableTorchFunction():
-> 1142             ret = func(*args, **kwargs)
   1143             if func in get_default_nowrap_functions():
   1144                 return ret

IndexError: Dimension out of range (expected to be in range of [-2, 1], but got 2)