Fastai v2 vision

I’m open to making norm type configurable. I’ll look into it.

1 Like

Just found this super cool little API change! I was doing a show_results on the planets dataset and:

Our correct and incorrect predictions are highlighted in red and green! Such a subtle change but it’s fantastic! :slight_smile:

10 Likes

Also separate question @sgugger is there a method for utilizing a MultiCategory with using get_y’s such as RegexLabeller? Essentially what I am trying to do is use the sigmoid operation with the PETs dataset and currently (if I am reading how to do multi-label right) it looks like I need to convert everything into a csv first.(I can understand the reasoning behind keeping the csv mandatory for such a task in the sense of you don’t accidentally do it.)

You don’t need to use a csv. Multicategory expect labels to be in a list, so jsut add a transform that puts your label l in [l] then you should be fine.

2 Likes

Awesome! Easy enough to do :slight_smile: Thanks! For those wondering what that looks like, here it is in DataBlock form:

def multi_l(l): return [l]

pets_multi = DataBlock(blocks=(ImageBlock, MultiCategoryBlock),
                 get_items=get_image_files,
                 splitter=RandomSplitter(),
                 get_y=[RegexLabeller(pat = r'/([^/]+)_\d+.jpg$'), multi_l])
1 Like

This is entirely custom, just want some input to how I did it and how it could be adjusted for a show function. I’m doing multiple keypoints and when I call show_results I get a tensor size mismatch (as currently show_results on a TensorPoint expects one point I believe). Here is how I’m manually doing it now:

x, y = dbunch.one_batch()
with torch.no_grad():
  res = learn.model(x)
pt = res[0].view(-1, 2)
a, b = dbunch.decode_batch((x[0], pt.unsqueeze(0)))[0]
ctx = a.show(figsize=(9,9))
b.show(ctx=ctx)

How would I go about adapting this for multiple key-points? Or is there something I’m missing and regular can do the job

For reference, there is a size mismatch error when I do a regular show_results

It’s hard to know what the problem is with no information about how you built your DataBunch and no stack trace. In general, the approach to customize show_batch is to define type-dipsatched version of the show_batch function like in vision.data.

1 Like

Thanks! I’ll look into it! I’m making it like such:

def img2kpts(f): return path/f'{str(f)}.cat'

def get_points(coords:array):
  kpts = []
  for i in range(0, len(coords), 2):
    kpts.append([coords[i], coords[i+1]])
  return tensor(kpts)

def get_y(f:Path):
  pts = np.genfromtxt(img2kpts(f))[1:]
  return get_points(pts)

def get_ip(img:PILImage, pnts:array): return TensorPoint.create(pnts, img_size=img.size)



dblock = DataBlock(blocks=(ImageBlock, PointsBlock),
                   get_items=get_image_files,
                   splitter=RandomSplitter(),
                   get_y=lambda o: get_y(o.name))

And here is that runtime error log:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-340-c3b657dcc9ae> in <module>()
----> 1 learn.show_results()

21 frames
/usr/local/lib/python3.6/dist-packages/fastai2/learner.py in show_results(self, ds_idx, dl, max_n, **kwargs)
    327         b = dl.one_batch()
    328         _,_,preds = self.get_preds(dl=[b], with_decoded=True)
--> 329         self.dbunch.show_results(b, preds, max_n=max_n, **kwargs)
    330 
    331     def show_training_loop(self):

/usr/local/lib/python3.6/dist-packages/fastai2/data/core.py in show_results(self, b, out, max_n, ctxs, show, **kwargs)
     82         x,y,its = self.show_batch(b, max_n=max_n, show=False)
     83         b_out = b[:self.n_inp] + (tuple(out) if is_listy(out) else (out,))
---> 84         x1,y1,outs = self.show_batch(b_out, max_n=max_n, show=False)
     85         res = (x,x1,None,None) if its is None else (x, y, its, outs.itemgot(slice(self.n_inp,None)))
     86         if not show: return res

/usr/local/lib/python3.6/dist-packages/fastai2/data/core.py in show_batch(self, b, max_n, ctxs, show, **kwargs)
     76     def show_batch(self, b=None, max_n=9, ctxs=None, show=True, **kwargs):
     77         if b is None: b = self.one_batch()
---> 78         if not show: return self._pre_show_batch(b, max_n=max_n)
     79         show_batch(*self._pre_show_batch(b, max_n=max_n), ctxs=ctxs, max_n=max_n, **kwargs)
     80 

/usr/local/lib/python3.6/dist-packages/fastai2/data/core.py in _pre_show_batch(self, b, max_n)
     70         b = self.decode(b)
     71         if hasattr(b, 'show'): return b,None,None
---> 72         its = self._decode_batch(b, max_n, full=False)
     73         if not is_listy(b): b,its = [b],L((o,) for o in its)
     74         return detuplify(b[:self.n_inp]),detuplify(b[self.n_inp:]),its

/usr/local/lib/python3.6/dist-packages/fastai2/data/core.py in _decode_batch(self, b, max_n, full)
     64         f = self.after_item.decode
     65         f = compose(f, partial(getattr(self.dataset,'decode',noop), full = full))
---> 66         return L(batch_to_samples(b, max_n=max_n)).map(f)
     67 
     68     def _pre_show_batch(self, b, max_n=9):

/usr/local/lib/python3.6/dist-packages/fastcore/foundation.py in map(self, f, *args, **kwargs)
    360              else f.format if isinstance(f,str)
    361              else f.__getitem__)
--> 362         return self._new(map(g, self))
    363 
    364     def filter(self, f, negate=False, **kwargs):

/usr/local/lib/python3.6/dist-packages/fastcore/foundation.py in _new(self, items, *args, **kwargs)
    313     @property
    314     def _xtra(self): return None
--> 315     def _new(self, items, *args, **kwargs): return type(self)(items, *args, use_list=None, **kwargs)
    316     def __getitem__(self, idx): return self._get(idx) if is_indexer(idx) else L(self._get(idx), use_list=None)
    317     def copy(self): return self._new(self.items.copy())

/usr/local/lib/python3.6/dist-packages/fastcore/foundation.py in __call__(cls, x, *args, **kwargs)
     39             return x
     40 
---> 41         res = super().__call__(*((x,) + args), **kwargs)
     42         res._newchk = 0
     43         return res

/usr/local/lib/python3.6/dist-packages/fastcore/foundation.py in __init__(self, items, use_list, match, *rest)
    304         if items is None: items = []
    305         if (use_list is not None) or not _is_array(items):
--> 306             items = list(items) if use_list else _listify(items)
    307         if match is not None:
    308             if is_coll(match): match = len(match)

/usr/local/lib/python3.6/dist-packages/fastcore/foundation.py in _listify(o)
    240     if isinstance(o, list): return o
    241     if isinstance(o, str) or _is_array(o): return [o]
--> 242     if is_iter(o): return list(o)
    243     return [o]
    244 

/usr/local/lib/python3.6/dist-packages/fastcore/foundation.py in __call__(self, *args, **kwargs)
    206             if isinstance(v,_Arg): kwargs[k] = args.pop(v.i)
    207         fargs = [args[x.i] if isinstance(x, _Arg) else x for x in self.pargs] + args[self.maxi+1:]
--> 208         return self.fn(*fargs, **kwargs)
    209 
    210 #Cell

/usr/local/lib/python3.6/dist-packages/fastcore/utils.py in _inner(x, *args, **kwargs)
    351     if order is not None: funcs = funcs.sorted(order)
    352     def _inner(x, *args, **kwargs):
--> 353         for f in L(funcs): x = f(x, *args, **kwargs)
    354         return x
    355     return _inner

/usr/local/lib/python3.6/dist-packages/fastcore/transform.py in decode(self, o, full)
    181 
    182     def decode  (self, o, full=True):
--> 183         if full: return compose_tfms(o, tfms=self.fs, is_enc=False, reverse=True, split_idx=self.split_idx)
    184         #Not full means we decode up to the point the item knows how to show itself.
    185         for f in reversed(self.fs):

/usr/local/lib/python3.6/dist-packages/fastcore/transform.py in compose_tfms(x, tfms, is_enc, reverse, **kwargs)
    121     for f in tfms:
    122         if not is_enc: f = f.decode
--> 123         x = f(x, **kwargs)
    124     return x
    125 

/usr/local/lib/python3.6/dist-packages/fastcore/transform.py in decode(self, x, **kwargs)
     60     def use_as_item(self): return ifnone(self.as_item_force, self.as_item)
     61     def __call__(self, x, **kwargs): return self._call('encodes', x, **kwargs)
---> 62     def decode  (self, x, **kwargs): return self._call('decodes', x, **kwargs)
     63     def setup(self, items=None): return self.setups(items)
     64     def __repr__(self): return f'{self.__class__.__name__}: {self.use_as_item} {self.encodes} {self.decodes}'

/usr/local/lib/python3.6/dist-packages/fastcore/transform.py in _call(self, fn, x, split_idx, **kwargs)
     68         f = getattr(self, fn)
     69         if self.use_as_item or not is_listy(x): return self._do_call(f, x, **kwargs)
---> 70         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     71         return retain_type(res, x)
     72 

/usr/local/lib/python3.6/dist-packages/fastcore/transform.py in <genexpr>(.0)
     68         f = getattr(self, fn)
     69         if self.use_as_item or not is_listy(x): return self._do_call(f, x, **kwargs)
---> 70         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     71         return retain_type(res, x)
     72 

/usr/local/lib/python3.6/dist-packages/fastcore/transform.py in _do_call(self, f, x, **kwargs)
     72 
     73     def _do_call(self, f, x, **kwargs):
---> 74         return x if f is None else retain_type(f(x, **kwargs), x, f.returns_none(x))
     75 
     76 add_docs(Transform, decode="Delegate to `decodes` to undo transform", setup="Delegate to `setups` to set up transform")

/usr/local/lib/python3.6/dist-packages/fastcore/dispatch.py in __call__(self, *args, **kwargs)
     96         if not f: return args[0]
     97         if self.inst is not None: f = MethodType(f, self.inst)
---> 98         return f(*args, **kwargs)
     99 
    100     def __get__(self, inst, owner):

/usr/local/lib/python3.6/dist-packages/fastai2/vision/core.py in decodes(self, x)
    211 
    212     def encodes(self, x:TensorPoint): return _scale_pnts(x, self._get_sz(x), self.do_scale, self.y_first)
--> 213     def decodes(self, x:TensorPoint): return _unscale_pnts(x, self._get_sz(x))
    214 
    215 #Cell

/usr/local/lib/python3.6/dist-packages/fastai2/vision/core.py in _unscale_pnts(y, sz)
    186     return TensorPoint(res, img_size=sz)
    187 
--> 188 def _unscale_pnts(y, sz): return TensorPoint((y+1) * tensor(sz).float()/2, img_size=sz)
    189 
    190 #Cell

/usr/local/lib/python3.6/dist-packages/fastai2/torch_core.py in _f(self, *args, **kwargs)
    256         def _f(self, *args, **kwargs):
    257             cls = self.__class__
--> 258             res = getattr(super(TensorBase, self), fn)(*args, **kwargs)
    259             return retain_type(res, self)
    260         return _f

RuntimeError: The size of tensor a (18) must match the size of tensor b (2) at non-singleton dimension 0

Oh, this error comes from the fact your model doesn’t output predictions that are bs * n_points * 2 but are flattened.

I see. I am creating my model via cnn_learner in which it is getting that behavior.

The outputted size is a [bs, n_points*2]

How should that tensor size instead look?

Ah yes, that’s incompatible with cnn_learner. I guess we need to fix the decode in the pointscaler then, Will do tomorrow.

2 Likes

Thanks!!! :slight_smile:

Got a bug I believe with verify_images. No matter what, it will say that it cannot identify image file. I’ve grabbed a URL in the file manually and looked at it and it was perfectly fine, however if I run verify_images it will flag it as not! @pnvijay also noticed this behavior.

Here is the relevant code:

for i, n in enumerate(classes):
  print(n)
  path_f = Path(files[i])
  download_images(path_f, path/folders[i], max_pics=50)

for n in classes:
  print(n)
  verify_images(path/n, delete=True, max_size=500)

I’m looking as well now. It’s in with PIL Image.

---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-49-9585efa78fe5> in <module>()
----> 1 img = Image.open(path_bb.ls()[0])

/usr/local/lib/python3.6/dist-packages/PIL/Image.py in open(fp, mode)
   2816     for message in accept_warnings:
   2817         warnings.warn(message)
-> 2818     raise IOError("cannot identify image file %r" % (filename if filename else fp))
   2819 
   2820 

OSError: cannot identify image file '/content/data/wolves/wolves/00000015.jpg'

If you tag me, please include the whole stack trace :wink:

1 Like

Getting warmer! @sgugger got the issue finally. When I try to download a URL with show_progress as False I get the following traceback:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-126-65db74e53af5> in <module>()
      1 download_url(urls[0], Path(f"{0}{suffix[0]}"), overwrite=True,
----> 2              show_progress=False, timeout=4)

/usr/local/lib/python3.6/dist-packages/fastai2/data/external.py in download_url(url, dest, overwrite, pbar, show_progress, chunk_size, timeout, retries)
    147             pbar = progress_bar(range(file_size), leave=False, parent=pbar)
    148         try:
--> 149             pbar.update(0)
    150             for chunk in u.iter_content(chunk_size=chunk_size):
    151                 nbytes += len(chunk)

AttributeError: 'NoneType' object has no attribute 'update'

Whereas True will work and use a non-corrupted image

This is where the issue is! As I then went through the rest and it worked fine :slight_smile:

My code:


def _download_image_inner(dest, inp, timeout=4):
    i,url = inp
    suffix = re.findall(r'\.\w+?(?=(?:\?|$))', url)
    suffix = suffix[0] if len(suffix)>0  else '.jpg'
    try: download_url(url, dest/f"{i:08d}{suffix}", overwrite=True, timeout=timeout)
    except Exception as e: f"Couldn't download {url}."

def download_images(url_file, dest, max_pics=1000, n_workers=8, timeout=4):
    "Download images listed in text file `url_file` to path `dest`, at most `max_pics`"
    urls = url_file.read().strip().split("\n")[:max_pics]
    dest = Path(dest)
    dest.mkdir(exist_ok=True)
    parallel(partial(_download_image_inner, dest, timeout=timeout), list(enumerate(urls)), n_workers=n_workers)
1 Like

Ah yes, it misses a test. Should be fixed now.

1 Like

Thanks!

One other little tidbit, with multiple keypoints I cannot just do a batch transform, I must include a resize on item_tfms else I get a tensor-mismatch. The temporary is an item_tfm with Resize() to my size

However on the totem pole that can be pretty low :slight_smile:

I imagine this issue actually has to deal with multiple key points being loaded in from a model perspective where they keypoints may not be where the image is. If so let me work on it a bit. If you believe otherwise:

IE:

Will not work:
dbunch = dblock.databunch(newPath, path=newPath, bs=32,batch_tfms=[*aug_transforms(size=(120, 160)), Normalize.from_stats(*imagenet_stats)])

Will work
dbunch = dblock.databunch(newPath, path=newPath, bs=32,item_tfms=Resize(120,160), batch_tfms=[*aug_transforms(size=(120, 160)), Normalize.from_stats(*imagenet_stats)])
2 Likes

This is only necessary if your images are not all of the same size (and not specific to points: you can only batch tensors of compatible shapes).

So if they’re not and we declare a size in batch_tfms, we still need one in the item_tfms? (Or a seperate resize transformation)