<jupyter_start><jupyter_text>Multi-label prediction with Planet Amazon dataset<jupyter_code>%reload_ext autoreload
%autoreload 2
%matplotlib inline
from fastai.vision import *<jupyter_output><empty_output><jupyter_text>Getting the data TY - You can use the python package for kaggle. Or you can just download directly. If you want to know how to do the package method, refer to the original copy.  Multiclassification Contrary to the pets dataset studied in last lesson, here each picture can have multiple labels. If we take a look at the csv file containing the labels (in 'train_v2.csv' here) we see that each 'image_name' is associated to several tags separated by spaces.<jupyter_code>Config.data_path()
path = Config.data_path()/'planet'
df = pd.read_csv(path/'train_v2.csv')
df.head()<jupyter_output><empty_output><jupyter_text>TY - Cos this is multi-label. So we cannot rely on separation by folders to help us out (i.e. using .from_folder()).  To put this in a `DataBunch` while using the [data block API](https://docs.fast.ai/data_block.html), we then need to using `ImageList` (and not `ImageDataBunch`). This will make sure the model created has the proper loss function to deal with the multiple classes. We use parentheses around the data block pipeline below, so that we can use a multiline statement without needing to add '\\'.<jupyter_code>data = ImageDataBunch.from_csv(path = path, 
                               csv_labels = 'train_v2.csv',
                               folder='train-jpg', 
                               size=128, 
                               bs = 32,
                               suffix='.jpg', 
                               label_delim = ' ', 
                               ds_tfms=get_transforms(flip_vert=True,   # By default it's False. Set it True to generalise better.
                                                      max_lighting=0.1, 
                                                      max_zoom=1.05, 
                                                      max_warp=0.))     # It's like the angle of perspective; distortion. We don't want that.<jupyter_output><empty_output><jupyter_text>TY - The following cell is equivalent as the one above. Just broken down into many steps.For other forms of classification data types, see here for in-depth guide:https://github.com/fastai/fastai/blob/master/docs_src/data_block.ipynb<jupyter_code># tfms = get_transforms(flip_vert=True, max_lighting=0.1, max_zoom=1.05, max_warp=0.)
# np.random.seed(42)
# src = (ImageList.from_csv(path, 'train_v2.csv', folder='train-jpg', suffix='.jpg')
#        .split_by_rand_pct(0.2)
#        .label_from_df(label_delim=' '))
# data = (src.transform(tfms, size=128)
#         .databunch().normalize(imagenet_stats))<jupyter_output><empty_output><jupyter_text>`show_batch` still works, and show us the different labels separated by `;`.<jupyter_code>data.show_batch(rows=3, figsize=(12,9))
data.classes<jupyter_output><empty_output><jupyter_text>Learning To create a `Learner` we use the same function as in lesson 1. Our base architecture is resnet50 again, but the metrics are a little bit differeent: we use `accuracy_thresh` instead of `accuracy`. In lesson 1, we determined the predicition for a given class by picking the final activation that was the biggest, but here, each activation can be 0. or 1. `accuracy_thresh` selects the ones that are above a certain threshold (0.5 by default) and compares them to the ground truth.As for Fbeta, it's the metric that was used by Kaggle on this competition. See [here](https://en.wikipedia.org/wiki/F1_score) for more details.<jupyter_code>arch = models.resnet34
acc_02 = partial(accuracy_thresh, thresh=0.2)
f_score = partial(fbeta, thresh=0.2)
learn = cnn_learner(data, arch, metrics=[acc_02, f_score])<jupyter_output><empty_output><jupyter_text>We use the LR Finder to pick a good learning rate.<jupyter_code>learn.lr_find()
learn.recorder.plot()<jupyter_output><empty_output><jupyter_text>Then we can fit the head of our network.Note that you don't want to select your learning rate to be right on the minima. You want to go 10x less than that.<jupyter_code>lr = 0.01
learn.fit_one_cycle(5, slice(lr))
learn.save('stage-1-rn34')
learn.load('stage-1-rn34')<jupyter_output><empty_output><jupyter_text>...And fine-tune the whole model:<jupyter_code>learn.unfreeze()
learn.lr_find()
learn.recorder.plot()
learn.fit_one_cycle(5, slice(1e-5, lr/5))
learn.save('stage-2-rn34')<jupyter_output><empty_output><jupyter_text>When I created my dataset, I put size=128 and actually the images that Kaggle gave us are 256. I used the size of 128 partially because I wanted to experiment quickly. It's much quicker and easier to use small images to experiment. But there's a second reason. I now have a model that's pretty good at recognizing the contents of 128 by 128 satellite images. So what am I going to do if I now want to create a model that's pretty good at 256 by 256 satellite images? Why don't I use transfer learning? Why don't I start with the model that's good at 128 by 128 images and fine-tune that? So don't start again. That's actually going to be really interesting because if I trained quite a lot and I'm on the verge of overfitting then I'm basically creating a whole new dataset effectively﹣one where my images are twice the size on each axis right so four times bigger. So it's really a totally different data set as far as my convolutional neural networks concerned. So I got to lose all that overfitting. I get to start again. Let's keep our same learner but use a new data bunch where the data bunch is 256 by 256.<jupyter_code>data = ImageDataBunch.from_csv(path = path, 
                               csv_labels = 'train_v2.csv',
                               folder='train-jpg', 
                               size=256, 
                               bs = 32,
                               suffix='.jpg', 
                               label_delim = ' ', 
                               ds_tfms=get_transforms(flip_vert=True,   # By default it's False. Set it True to generalise better.
                                                      max_lighting=0.1, 
                                                      max_zoom=1.05, 
                                                      max_warp=0.))     # It's like the angle of perspective; distortion. We don't want that.
learn.data = data
data.train_ds[0][0].shape
learn.freeze()
learn.lr_find()
learn.recorder.plot()<jupyter_output><empty_output><jupyter_text>Because I actually now have a pretty good model (it's pretty good for 128 by 128 so it's probably gonna be like at least okay for 256 by 256), I don't get that same sharp shape that I did before. But I can certainly see where it's way too high. So I'm gonna pick something well before where it's way too high. Again maybe 10x smaller. So here I'm gonna go `1e-2/2` ﹣ that seems well before it shoots up.<jupyter_code>lr=1e-2/2
learn.fit_one_cycle(5, slice(lr))
learn.save('stage-1-256-rn34')
learn.unfreeze()
learn.fit_one_cycle(5, slice(1e-5, lr/5))
learn.recorder.plot_losses()
learn.save('stage-2-256-rn34')<jupyter_output><empty_output><jupyter_text>You won't really know how you're going until you submit to Kaggle, since the leaderboard isn't using the same subset as we have for training. But as a guide, 50th place (out of 938 teams) on the private leaderboard was a score of `0.930`. Simply call Learner.save and Learner.load to save and load models. Only the parameters are saved, not the actual architecture (so you'll need to create your model in the same way before loading weights back in). Models are saved to the path/model_dir directory.<jupyter_code>learn.export()<jupyter_output><empty_output><jupyter_text>fin (This section will be covered in part 2 - please don't ask about it just yet! :) )<jupyter_code>#! kaggle competitions download -c planet-understanding-the-amazon-from-space -f test-jpg.tar.7z -p {path}  
#! 7za -bd -y -so x {path}/test-jpg.tar.7z | tar xf - -C {path}
#! kaggle competitions download -c planet-understanding-the-amazon-from-space -f test-jpg-additional.tar.7z -p {path}  
#! 7za -bd -y -so x {path}/test-jpg-additional.tar.7z | tar xf - -C {path}
test = ImageList.from_folder(path/'test-jpg').add(ImageList.from_folder(path/'test-jpg-additional'))
len(test)
learn = load_learner(path, test=test) # different from save/load. This is after exporting.
preds, _ = learn.get_preds(ds_type=DatasetType.Test)
preds;
# https://www.geeksforgeeks.org/enumerate-in-python/

thresh = 0.2
labelled_preds = [' '.join([learn.data.classes[i] for i,p in enumerate(pred) if p > thresh]) for pred in preds]
labelled_preds[:5]
learn.data.test_ds.items
fnames = [f.name[:-4] for f in learn.data.test_ds.items]
df = pd.DataFrame({'image_name':fnames, 'tags':labelled_preds}, columns=['image_name', 'tags']) # follow sample submission csv
df
df.to_csv(path/'submission.csv', index=False)
# ! kaggle competitions submit planet-understanding-the-amazon-from-space -f {path/'submission.csv'} -m "My submission"<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09427942794279429, 'fraction_numerical': 0.019251925192519254, 'mean_word_length': 3.2740949694405264, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 54, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 17, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '444549', 'license': 'Apache-2.0', 'n_tokens_mistral': 2883, 'n_tokens_neox': 2693, 'n_words': 979, 'path': 'nbs/dl1/lesson3-planet-davis.ipynb', 'repo_name': 'ktingyew/course-v3', 'size': '9090'}