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:
-
No pretrained models allowed from the start*
-
Results must be on the 224x224 dataset
val
folder -
If training at different resolutions, you must use the
train/val
folder splits -
Results should be an average of 3 models trained for five epochs total, with a report of standard deviation as well
-
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 modelB
(based on weights from modelA
) for 2 epochs only. -
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)
-
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.
-
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