# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Lesson 1 - What's your pet

# ## GPU2 limited to 224W

# Welcome to lesson 1! For those of you who are using a Jupyter Notebook for the first time, you can learn about this useful tool in a tutorial we prepared specially for you; click `File`->`Open` now and click `00_notebook_tutorial.ipynb`. 
#
# In this lesson we will build our first image classifier from scratch, and see if we can achieve world-class results. Let's dive in!
#
# Every notebook starts with the following three lines; they ensure that any edits to libraries you make are reloaded here automatically, and also that any charts or images displayed are shown in this notebook.

# %reload_ext autoreload
# %autoreload 2
# %matplotlib inline

# We import all the necessary packages. We are going to work with the [fastai V1 library](http://www.fast.ai/2018/10/02/fastai-ai/) which sits on top of [Pytorch 1.0](https://hackernoon.com/pytorch-1-0-468332ba5163). The fastai library provides many useful functions that enable us to quickly and easily build neural networks and train our models.

from fastai import *
from fastai.vision import *

# +
gpu_device = 1

defaults.device = torch.device(f'cuda:{gpu_device}')
torch.cuda.set_device(gpu_device)
# -

path = untar_data(URLs.PETS); path

path.ls()

path_anno = path/'annotations'
path_img = path/'images'

# The first thing we do when we approach a problem is to take a look at the data. We _always_ need to understand very well what the problem is and what the data looks like before we can figure out how to solve it. Taking a look at the data means understanding how the data directories are structured, what the labels are and what some sample images look like.
#
# The main difference between the handling of image classification datasets is the way labels are stored. In this particular dataset, labels are stored in the filenames themselves. We will need to extract them to be able to classify the images into the correct categories. Fortunately, the fastai library has a handy function made exactly for this, `ImageDataBunch.from_name_re` gets the labels from the filenames using a [regular expression](https://docs.python.org/3.6/library/re.html).

fnames = get_image_files(path_img)
fnames[:5]

np.random.seed(2)
pat = re.compile(r'/([^/]+)_\d+.jpg$')

# If you're using a computer with an unusually small GPU, you may get an out of memory error when running this notebook. If this happens, click Kernel->Restart, uncomment the 2nd line below to use a smaller *batch size* (you'll learn all about what this means during the course), and try again.

bs = 64

# ## Training: resnet50

# Now we will train in the same way as before but with one caveat: instead of using resnet34 as our backbone we will use resnet50 (resnet34 is a 34 layer residual network while resnet50 has 50 layers. It will be explained later in the course and you can learn the details in the [resnet paper](https://arxiv.org/pdf/1512.03385.pdf)).
#
# Basically, resnet50 usually performs better because it is a deeper network with more parameters. Let's see if we can achieve a higher performance here. To help it along, let's us use larger images too, since that way the network can see more detail. We reduce the batch size a bit since otherwise this larger network will require more GPU memory.

data = ImageDataBunch.from_name_re(path_img, fnames, pat, ds_tfms=get_transforms(),
                                   size=299, bs=bs//2).normalize(imagenet_stats)

import pretrainedmodels

pretrainedmodels.model_names

model_name = 'resnet50'
def get_cadene_model(pretrained=True, model_name='resnet50' ): # pretrained key should be the first
    if pretrained:
        arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    else:
        arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained=None)
    return arch



custom_head=nn.Sequential(AdaptiveConcatPool2d(),Flatten())

custom_head

# fastai resnet50
models.resnet50()

# #### Note the above 2 models are almost the same
# However seems fastai will change it when create_cnn is called

learn = create_cnn(data, models.resnet50, metrics=error_rate)

learn.model

# This should be replaced into what the learn.model has
children(models.resnet50())[-3:]

children(get_cadene_model())[-3:]

nn.Sequential(AdaptiveConcatPool2d(),Flatten())

get_cadene_model()

model_name = 'resnet50'
def get_cadene_model(pretrained=True, **kwargs ): # pretrained key should be the first
    if pretrained:
        arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    else:
        arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained=None)
    return arch



learn = create_cnn(data, get_cadene_model, ps=0.5, bn_final=False ,metrics=error_rate)

models.resnet50


# +
def get_model(pretrained=True, **kwargs ): # pretrained key should be the first
    arch = models.resnet50(pretrained, **kwargs )
    return arch

learn = create_cnn(data, get_model, ps=0.5, bn_final=False ,metrics=error_rate)


# +
# this works
def get_model(pretrained=True, model_name = 'resnet50',  **kwargs ): # pretrained key should be the first
    arch = models.resnet50(pretrained, **kwargs )
    return arch

learn = Learner(data, get_model(), metrics=[accuracy])


# +
# this works
def get_model(pretrained=True, model_name = 'resnet50',  **kwargs ): # pretrained key should be the first
    arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    return arch

learn = Learner(data, get_model(), metrics=[accuracy])


# +
# this does not work
def get_model(pretrained=True, model_name = 'resnet50', **kwargs ): # pretrained key should be the first
    arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    return arch

learn = Learner(data, get_model, metrics=[accuracy])


# +
# this does not work
def get_model(pretrained=True, model_name = 'resnet50', **kwargs ): # pretrained key should be the first
    arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    return arch

learn = create_cnn(data, get_model(), metrics=[accuracy])


# +
# this does not work
def get_model(pretrained=True, model_name = 'resnet50', **kwargs ): # pretrained key should be the first
    arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    return arch

learn = create_cnn(data, get_model, metrics=[accuracy])
# -

learn = create_cnn(data, model, metrics=error_rate)


# +
# If you have 2 GPUs : for parallel model on 2 GPUs performance uncomment below
# learn.model = torch.nn.DataParallel(learn.model, device_ids=[0, 1])
# -

# ## Trying `Learner`

# this works
def get_model(pretrained=True, model_name = 'resnet50', **kwargs ): # pretrained key should be the first
    arch = pretrainedmodels.__dict__[model_name](num_classes=1000, pretrained='imagenet')
    return arch


get_model()

custom_head1 = create_head(nf=2048, nc=37, ps=0.5, bn_final=False)

custom_head1

fastai_resnet50=nn.Sequential(*list(children(get_model())[:-2]),custom_head1)

learn = Learner(data, get_model(), metrics=error_rate)

# learn.fit_one_cycle(8)
learn.fit_one_cycle(5) 

learn.save('stage-1-50')

# It's astonishing that it's possible to recognize pet breeds so accurately! Let's see if full fine-tuning helps:

learn.unfreeze()
# learn.fit_one_cycle(3, max_lr=slice(1e-6,1e-4))
learn.fit_one_cycle(1, max_lr=slice(1e-6,1e-4)) # for benchmark: https://forums.fast.ai/t/lesson-1-pets-benchmarks/27681

# If it doesn't, you can always go back to your previous model.

learn.load('stage-1-50');

StarcoderdataJupyterScriptsDedupFiltered