FastGarden - A new ImageNette like competition (just for fun)

Okay guys, we’re all pretty familiar with fastai as a whole here so let’s try to get used to the new framework and push it’s limits with some friendly competition. Recently Kaggle started a new competition on Flower classification with a TPU. Now we don’t have TPU support (at least as of this post) for fastai2, but we can try to use a new idea or two! We ask that you keep this as a forum-internal compettion as a result. So here is a general sum up:

Welcome to the FastGarden!

FastGarden is a competition geared towards us veteran fastai folk who want to give the fastai2 library a real run for it’s money. This competition is based off of the ImageNette competitions.

  • Note: All are welcome to join, just keep in mind this is in the non-beginner category

The Dataset

We’ll be working off of the Flower Classification with TPUs. This is just for fun and is an active Kaggle competition, so we ask that you keep this an internal forum-based competition! We’ll have full access to the entire training dataset (not their test answers!), however the final results must be on the 224x224 sized images (and at a 224x224 size). IE you can do transfer learning on the 192x192 images and work up to a 224x224 and transfer those weights. (see more info in rules)

Note: The data is split into a val and train folder. To split the data properly, use the following IndexSplitter: splitter = IndexSplitter(range(12753, len(data))) (this works for each image resolution)

The Rules:

  1. No pretrained models allowed from the start*

  2. Results must be on the 224x224 dataset val folder

  3. If training at different resolutions, you must use the train/val folder splits

  4. Results should be an average of 3 models trained for five epochs total, with a report of standard deviation as well

  5. You are allowed to do partial transfer learning, however you must not go over the total number of epochs. IE: train model A for 3 epochs, then train model B (based on weights from model A) for 2 epochs only.

  6. You should only use a single GPU, and all runs will eventually be tested on a Colab T4 instance for effeciency (at our discretion to when we decide/how we decide to do this)

  7. You are only allowed the 12,000 or so samples per epoch. You can oversample/sample however you choose, but this is the maximum you can do.

  8. When making a submission, you must explain what the new method you are using does and the work it was based on. It doesn’t need to be a completely advanced explanation with the very nitty gritty (unless you know this or can explain it!) but at the very least what the general concepts/ideas behind the technique

Besides this, good luck! Feel free to discuss ideas, collaborate, and post your findings! We’ll keep track of the top 5 here on this thread, and we’ll update it regularly.
I have a starter notebook available here showing how to set up a simple model.

Dealing with tfrecord files

As you may be aware, we don’t have image files here, we have tfrecord files. I worked out a way for us to bring it in, however it doesn’t allow the lazy grabbing that would (eventually) be nice. In the meantime, here is how I set it up:

First, clone this repository:https://github.com/pgmmpk/tfrecord.git
Then, follow this code:

import tensorflow as tf
from tfrecord.tfrecord import *

In order to parse the data, we need to “unpack” each sample to bring it into a Dataset . tfrecord has two special functions we can use, unpack_int_64_list and unpack_bytes_list (our classes are integers and our images are bytes)

def unpack_sample(feats):
    return {
        'class' : unpack_int64_list(feats['class']),
        'image' : unpack_bytes_list(feats['image'])
    }

The current file situation (for this folder) is like so:

  • 16 tfrecord files each with 792 images for training and testing

To use it in with Datasets we need to extract them all to a central data array. This can be done like so:

data = []
for name in train_fnames+test_fnames:
  r = Reader(str(name), unpack_sample)
  for sample in r:
    data.append([sample['image'][0], sample['class'][0]])

After this is done, you can just use the mid-level API and build your dataloaders!

get_x = lambda o: PILImage.create(io.BytesIO(o[0]))
get_y = lambda o: o[1]

def get_items(*args, **kwargs): return data

block = DataBlock(blocks=(ImageBlock, CategoryBlock), 
                  get_items=get_items,
                  get_x=get_x,
                  get_y=get_y,
                  splitter=splitter,
                  item_tfms=[Resize(224)],
                  batch_tfms=[*aug_transforms()])

dls = block.dataloaders('', bs=64)

Best of luck, have fun, and let’s see how far we can push this! If you come up with different ways to load the data in that may be more efficient or different, please post them so we can all learn different ways of utilizing the API!

Thanks to @arora_aman and @init_27 for the idea

Finally, if this proves to be too easy (IE we get above 98% accuracy at some point) we’ll rethink the dataset more

26 Likes

I will keep a current leaderboard here in the second post. General format:

% accuracy ± std, name: one sentence describing approach, link_to_nb

Leaderboard

  1. 82.40% ± 0.15%: @bwarner MK-ResNeXt-50 with a network deconvolution stem. Experiment 2.3
  2. 80.41% ± 0.73%: @bwarner MK-ResNeXt-50. Experiment 7.3.
  3. 79.92% ± 0.72%: @jwuphysics: xse_resnext34 with deconvolution stem. notebook
  4. 71.70% ± 0.37%, @jwuphysics: Flat cosine annealing with RandomResizeCrop augmentation. notebook
  5. 71.24% ± 1.24% @morgan: Progressive resizing; 128 (.fit) -> 224 (.fit_flat_cos) see experiment 15b
4 Likes

This looks awesome! Great job you guys! :blush:

I’m thinking… would it maybe also make sense to specify the maximum length of an epoch? Not in terms of batches, since that is GPU dependent, but in terms of how many examples can be seen by the model? As the classes are imbalanced, this could give an option to balance the dataset. Also, one might want to train multihead or siamese models - a rule like that could allow for greater flexibility and people could try some and weird and interesting things.

Since there are 12753 images in the train set, maybe we could cap the number of examples per epoch to 12753, sampled in whatever way a person may desire? To prevent any disambiguities, maybe we could also say that for multihead models we allow only for 3 images to be shown per example? (accommodates the triplet loss, maybe someone will want to train a model simultaneously on 3 resolutions of the same image, prevents going crazy with the width of an example to circumvent the limit on epoch size).

5 Likes

Great idea i’ll definitely give it a go!

@radek, I agree with most of your concerns, but my view on this issue is not to limit the beast and see what new ideas comes out of it to share. On the other hand, may lead to machinery advantage.

Does this mean no data augmentation?

Often when you apply data augmentation you still count the “epoch” as the number of original examples. So the model will still see each instance once per epoch, albeit sometimes in an augmented form

2 Likes

Yes, that’s what I was Atleast attempting to state in the first post, that was the idea :slight_smile:

2 Likes

No, we can do data augmentation. What we essentially mean is (I’ll have to look at how many that actually is) but we can only have the 12,000 images in an epoch, or (if batch size is 64 for instance, we can discuss that too) only 199 (because it rounds down) batches of data per epoch. Am I on the money @radek?

Eventually another thing we may do is limit the time or efficiency and run everything on a Colab instance for a true comparison, but we’re open to ideas on this! And yes, that means it needs to be single GPU based

Also, thoughts on limiting batch size? 64? The goal is it needs to be able to be run on a Colab T4 instance (as I have Pro we can use the best one available)

Edit: and then finally, I believe we should make submissions a bit more than just a script or something. You should explain what the technique you are attempting to do, what it is based on, and why you believe it should work (or be considered for the leaderboard, etc). This way while we use these new techniques we can attempt to explain them to everyone along the way! Let me know thoughts

1 Like

I am not sure there is much value in limiting the batch size. The total examples should be fine.

The reason I suggested to limit the amount of images per example, is that (I think, not sure) you could just mess with your forward function and pass in 64 images as a single example in a batch and reshape before passing them through your model :slight_smile:

I don’t think that anyone would cheat, that is definitely not the spirit of the competition, so maybe all those considerations are not important :slight_smile: If anything I guess we can fallback on what we discussed and I suppose anything reasonable should be okay :slight_smile:

I guess the absolutely main point from me was the limit on examples - because very interesting ways of training on this dataset will most likely involve sampling your own epochs or sampling your own example pairs or triplets. Everything else was fine print :slight_smile:

4 Likes

Interesting thought, worth a try! (to perhaps save on memory or something if I’m following the idea properly?)

I disagree, I believe they are extremely important so we can all be on the same page

100% agree

Also @radek I’ve made the top post a wiki so if there’s anything else you’d like to add/change/further explain it’s available to do so. (or anyone else so longs as we all generally agree)

Also the BS can be considered as a hyperparameter impacting the learning path as much as the LR. Because you’ll have more or less steps in 1 epoch.
Am I wright?

1 Like

I’d agree too, but I don’t think we should limit just based on a number. Limit it based on the memory constraints of a T4. If it works and it does well we can keep it up there, but if I cannot reproduce/run the code in Colab due to out of memory errors then this will be taken into consideration

1 Like

Understood, fair enough.

Also, please use the tfrecord format, if we can get it working well this is a really big plus for the library :slight_smile: You can load it in how you want just don’t write anything extraneous like saving the images to disk etc. The goal is utilize the light overhead tfrecord has

2 Likes

I’m not sure these restrictions adds much value to the competition,

I think the main goal of the competition is to try new ideas, new crazy multiheaded models, or whatever you like.

In my humble opinion restricting to tfrecords is just adding a set back to that.

If you want to go for it and optimize the format, sure, go for it! At the end you can even make a PR and help fastai, but I don’t think it should be a restriction for all members

My original approach to this was to first convert all tfrecord to jpg images and then store them in drive so I could easily use it.

2 Likes

From what I understood, the accuracy will be recorded from @muellerzr running our notebooks in his collab account, so it won’t be convenient to pass a huge folder of jpgs with your nb.
Or we can use Kaggle nbs where we can load custom data and avoiding him lot of nbs importing and running on colab.

One should include how you chose to generate the data, and then I’ll work with it on my end with how it all comes together :slight_smile: I’m open to various ideas of the data formats, however I’d like to avoid Kaggle nbs as this is meant for the course folks only.

I think that’d be a good way to go about it. Because I’d like creative ideas for the data generation too, but I agree with @lgvaz perhaps limiting it to just that one initial idea would be bad :slight_smile:

2 Likes

As @lgvaz pointed out to me, there’s a better way of doing the get_items rather than noop like so: def get_items(*args, **kwargs): return data

1 Like

maybe this :slight_smile:

This way you are indicating that you are not planning to do anything with what gets passed in (and you only ever expect a single value).

This is a super, super trivial point, so terribly sorry for even bringing this up. You started this though :smile:

2 Likes