Variable number of Points in a PointBlock

Two questions. Can a model be trained to output a variable number of tensors within a PointBlock? If it can, does anyone have any idea what I could do to troubleshoot why it’s not working for me?

I’m trying to count the number of people in a series of images using a PointBlock. This means the number of tensors within the PointBlock varies from image to image. However when I try to show a batch or run learn.lr_find(), I get the error below. I’m assuming it’s because of the variable number of tensors.

I’ve set dls.c equal to dls.train.after_item.c. I’ve verified that the Datablock has an item transform set so variable image sizes isn’t the issue here. Does anyone have any ideas?

lr_find attempt:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-7-8c85932dd9ad> in <module>
      1 dls = biwi.dataloaders(picPath, bs=4)
      2 dls.c = dls.train.after_item.c
----> 3 dls.show_batch()

~/.local/lib/python3.8/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)

~/.local/lib/python3.8/site-packages/fastai/data/load.py in one_batch(self)
    135     def one_batch(self):
    136         if self.n is not None and len(self)==0: raise ValueError(f'This DataLoader does not contain any batches')
--> 137         with self.fake_l.no_multiproc(): res = first(self)
    138         if hasattr(self, 'it'): delattr(self, 'it')
    139         return res

~/.local/lib/python3.8/site-packages/fastcore/basics.py in first(x, f, negate, **kwargs)
    540     x = iter(x)
    541     if f: x = filter_ex(x, f=f, negate=negate, gen=True, **kwargs)
--> 542     return next(x, None)
    543 
    544 # Cell

~/.local/lib/python3.8/site-packages/fastai/data/load.py in __iter__(self)
     99         self.before_iter()
    100         self.__idxs=self.get_idxs() # called in context of main process (not workers/subprocesses)
--> 101         for b in _loaders[self.fake_l.num_workers==0](self.fake_l):
    102             if self.device is not None: b = to_device(b, self.device)
    103             yield self.after_batch(b)

~/.local/lib/python3.8/site-packages/torch/utils/data/dataloader.py in __next__(self)
    433         if self._sampler_iter is None:
    434             self._reset()
--> 435         data = self._next_data()
    436         self._num_yielded += 1
    437         if self._dataset_kind == _DatasetKind.Iterable and \

~/.local/lib/python3.8/site-packages/torch/utils/data/dataloader.py in _next_data(self)
    473     def _next_data(self):
    474         index = self._next_index()  # may raise StopIteration
--> 475         data = self._dataset_fetcher.fetch(index)  # may raise StopIteration
    476         if self._pin_memory:
    477             data = _utils.pin_memory.pin_memory(data)

~/.local/lib/python3.8/site-packages/torch/utils/data/_utils/fetch.py in fetch(self, possibly_batched_index)
     32                 raise StopIteration
     33         else:
---> 34             data = next(self.dataset_iter)
     35         return self.collate_fn(data)
     36 

~/.local/lib/python3.8/site-packages/fastai/data/load.py in create_batches(self, samps)
    108         self.it = iter(self.dataset) if self.dataset is not None else None
    109         res = filter(lambda o:o is not None, map(self.do_item, samps))
--> 110         yield from map(self.do_batch, self.chunkify(res))
    111 
    112     def new(self, dataset=None, cls=None, **kwargs):

~/.local/lib/python3.8/site-packages/fastai/data/load.py in do_batch(self, b)
    131     def create_item(self, s):  return next(self.it) if s is None else self.dataset[s]
    132     def create_batch(self, b): return (fa_collate,fa_convert)[self.prebatched](b)
--> 133     def do_batch(self, b): return self.retain(self.create_batch(self.before_batch(b)), b)
    134     def to(self, device): self.device = device
    135     def one_batch(self):

~/.local/lib/python3.8/site-packages/fastai/data/load.py in create_batch(self, b)
    130     def retain(self, res, b):  return retain_types(res, b[0] if is_listy(b) else b)
    131     def create_item(self, s):  return next(self.it) if s is None else self.dataset[s]
--> 132     def create_batch(self, b): return (fa_collate,fa_convert)[self.prebatched](b)
    133     def do_batch(self, b): return self.retain(self.create_batch(self.before_batch(b)), b)
    134     def to(self, device): self.device = device

~/.local/lib/python3.8/site-packages/fastai/data/load.py in fa_collate(t)
     46     b = t[0]
     47     return (default_collate(t) if isinstance(b, _collate_types)
---> 48             else type(t[0])([fa_collate(s) for s in zip(*t)]) if isinstance(b, Sequence)
     49             else default_collate(t))
     50 

~/.local/lib/python3.8/site-packages/fastai/data/load.py in <listcomp>(.0)
     46     b = t[0]
     47     return (default_collate(t) if isinstance(b, _collate_types)
---> 48             else type(t[0])([fa_collate(s) for s in zip(*t)]) if isinstance(b, Sequence)
     49             else default_collate(t))
     50 

~/.local/lib/python3.8/site-packages/fastai/data/load.py in fa_collate(t)
     45     "A replacement for PyTorch `default_collate` which maintains types and handles `Sequence`s"
     46     b = t[0]
---> 47     return (default_collate(t) if isinstance(b, _collate_types)
     48             else type(t[0])([fa_collate(s) for s in zip(*t)]) if isinstance(b, Sequence)
     49             else default_collate(t))

~/.local/lib/python3.8/site-packages/torch/utils/data/_utils/collate.py in default_collate(batch)
     53             storage = elem.storage()._new_shared(numel)
     54             out = elem.new(storage)
---> 55         return torch.stack(batch, 0, out=out)
     56     elif elem_type.__module__ == 'numpy' and elem_type.__name__ != 'str_' \
     57             and elem_type.__name__ != 'string_':

~/.local/lib/python3.8/site-packages/fastai/torch_core.py in __torch_function__(self, func, types, args, kwargs)
    323         convert=False
    324         if _torch_handled(args, self._opt, func): convert,types = type(self),(torch.Tensor,)
--> 325         res = super().__torch_function__(func, types, args=args, kwargs=kwargs)
    326         if convert: res = convert(res)
    327         if isinstance(res, TensorBase): res.set_meta(self, as_copy=True)

~/.local/lib/python3.8/site-packages/torch/tensor.py in __torch_function__(cls, func, types, args, kwargs)
    993 
    994         with _C.DisableTorchFunction():
--> 995             ret = func(*args, **kwargs)
    996             return _convert(ret, cls)
    997 

RuntimeError: stack expects each tensor to be equal size, but got [3, 2] at entry 0 and [4, 2] at entry 1

Can a model? Yes.

Can fastai’s model OOTB? No.

I can explain why:

fastai’s point-based head generated by cnn_learner operates with a Linear layer at the end, this means there must always be n outputs. You should look into other pytorch architectures and models if you want something along those lines and use one of those.

PointBlock in of itself is fine, it’s the model that is the issue.

That makes sense, thank you. Does this affect the dataloaders too? When I run dls.showbatch() with a batch size greater than 1, it errors out with the same error unless the two images have the same number of points.