Fastbook Chapter 11 questionnaire solutions (wiki)

Tentative solutions:

  1. Why do we say that fastai has a “layered” API? What does it mean?

fastai is designed following a layered architecture with 4 layers: an applications layer, a high-level API, a mid-level API and a low-level API. These offer higher and higher customizability as you make your way down the layers. The high-level of the API is most likely to be useful to beginners and to practitioners who are mainly in interested in applying pre-existing deep learning methods. It offers concise APIs over four main application areas: vision, text, tabular and time-series analysis, and collaborative filtering. These APIs choose intelligent default values and behaviors based on all available information. The mid-level API provides the core deep learning and data-processing methods for each of these applications, and low-level APIs provide a library of optimized primitives and functional and object-oriented foundations, which allows the mid-level to be developed and customised.

  1. Why does a Transform have a decode method? What does it do?

decode is used by fastai’s show_batch and show_results , as well as some other inference methods, to convert predictions and mini-batches into a human-understandable representation.

  1. Why does a Transform have a setup method? What does it do?

In general, a Transform is an object that behaves like a function and has an optional setup method that will initialize some inner state and an optional decode that will reverse the function.

  1. How does a Transform work when called on a tuple?

A special behavior of Transform s is that they always get applied over tuples. In general, our data is always a tuple (input,target) (sometimes with more than one input or more than one target). When applying a transform on an item like this, such as Resize , we don’t want to resize the tuple as a whole; instead, we want to resize the input (if applicable) and the target (if applicable) separately. It’s the same for batch transforms that do data augmentation: when the input is an image and the target is a segmentation mask, the transform needs to be applied (the same way) to the input and the target.

  1. Which methods do you need to implement when writing your own Transform?

If you want to write a custom transform to apply to your data, the easiest way is to write a function. You will need the encode method and optionally the setup or decode methods.

  1. Write a Normalize transform that fully normalizes items (subtract the mean and divide by the standard deviation of the dataset), and that can decode that behavior. Try not to peek!
class NormalizeMean(Transform):
    def setups(self, items): self.mean = sum(items)/len(items)
    def encodes(self, x): return x-self.mean
    def decodes(self, x): return x+self.mean
  1. Write a Transform that does the numericalization of tokenized texts (it should set its vocab automatically from the dataset seen and have a decode method). Look at the source code of fastai if you need help.
class Numericalize(Transform):
    def setups(self, dsets):
       if dsets is None: return
       if self.vocab is None:
           count = dsets.counter if getattr(dsets, 'counter', None) is not None else Counter(p for o in dsets for p in o)
           if self.special_toks is None and hasattr(dsets, 'special_toks'):
               self.special_toks = dsets.special_toks
           self.vocab = make_vocab(count, min_freq=self.min_freq, max_vocab=self.max_vocab, special_toks=self.special_toks)
           self.o2i = defaultdict(int, {v:k for k,v in enumerate(self.vocab) if v != 'xxfake'})
    def encodes(self, o): return TensorText(tensor([self.o2i  [o_] for o_ in o]))
    def decodes(self, o): return L(self.vocab[o_] for o_ in o)
  1. What is a Pipeline?

To compose several transforms together, fastai provides the Pipeline class. We define a Pipeline by passing it a list of Transform s; it will then compose the transforms inside it. When you call Pipeline on an object, it will automatically call the transforms inside, in order.

  1. What is a TfmdLists?

Your data is usually a set of raw items (like filenames, or rows in a DataFrame) to which you want to apply a succession of transformations. We just saw that a succession of transformations is represented by a Pipeline in fastai. The class that groups together this Pipeline with your raw items is called TfmdLists .

  1. What is a Datasets? How is it different from a TfmdLists?

Datasets will apply two (or more) pipelines in parallel to the same raw object and build a tuple with the result. Like TfmdLists , it will automatically do the setup for us, and when we index into a Datasets , it will return us a tuple with the results of each pipeline. TfmdLists behaves differently in that it returns two separate objects for our inputs and targets.

  1. Why are TfmdLists and Datasets named with an “s”?

TfmdLists and Datasets are named with an “s” because they can handle a training and a validation set with a splits argument.

  1. How can you build a DataLoaders from a TfmdLists or a Datasets?

You can directly convert a TfmdLists or a Datasets to a DataLoaders object with the dataloaders method.

  1. How do you pass item_tfms and batch_tfms when building a DataLoaders from a TfmdLists or a Datasets?

When building a DataLoaders from a TfmdLists or a Datasets, after_item is the equivalent of item_tfms in DataBlock whereas after_batch is the equivalent of batch_tfms in DataBlock.

  1. What do you need to do when you want to have your custom items work with methods like show_batch or show_results?

decode is used by fastai’s show_batch and show_results , as well as some other inference methods, to convert predictions and mini-batches into a human-understandable representation. Your custom items should therefore have a decode method in order for them to work with methods like show_batch or show_results.

  1. Why can we easily apply fastai data augmentation transforms to the SiamesePair we built?

The mid-level API for data collection gives us two objects (TfmdLists and Datasets) that can help us easily apply transforms to the SiamesePair we built.


One thinks Question 6 missing standard deviation from the code. Perhaps use this?

class Normalize(Transform):
    def setups(self, items): 
        self.mean = sum(items) / len(items)
        self.std = np.std(items)  # np.std(np.array(items))

    def encodes(self, x): return (x - self.mean) / self.std
    def decodes(self, x): return (x * self.std) + self.mean
1 Like

I would add something to the answer of the 14th question:

  1. What do you need to do when you want to have your custom items work with methods like show_batch or show_results?

You must create a custom type by inherit fastuple and implements a show method.

When you call the show method on a TfmdLists or a Datasets object, it will decode items until it reaches a type that contains a show method and use it to show the object.
I copy the example to show the signature:

class SiameseImage(fastuple):
    def show(self, ctx=None, **kwargs): 
        img1,img2,same_breed = self
        if not isinstance(img1, Tensor):
            if img2.size != img1.size: img2 = img2.resize(img1.size)
            t1,t2 = tensor(img1),tensor(img2)
            t1,t2 = t1.permute(2,0,1),t2.permute(2,0,1)
        else: t1,t2 = img1,img2
        line = t1.new_zeros(t1.shape[0], t1.shape[1], 10)
        return show_image([t1,line,t2], dim=2), 
                          title=same_breed, ctx=ctx)