A notre avis, le MOOC, concernant le ML et le DL, le plus intéressant du moment, est celui de Jeremy Howard (de Fastai). Son cours est très pédagogique, très progressif, et bien documenté.

Le seul bémol est qu’il s’appuie sur PyTorch et la librairie de Fastai (il semblerait toutefois, que pour la suite, un retour, ou du moins une collaboration avec TensorFlow, soit prévue).

C’est un bémol car TensorFlow est beaucoup plus actif (>50 000 commits, près de 2000 contributeurs) que PytTorch (17 000 commits, <1000 contributeurs). TensorFlow ne manque pas de défauts, mais c’est incontestablement le plus populaire.

Il aurait été préférable que Fastai s’appuie sur Keras qui supporte TensorFlow, CNTK)

Fastai délivre ses cours gratuitement (en Machine Learning, en Deep Learning, et même en Algèbre Linéaire).

Cet article commente la leçon #3 du cours.

Environnement

Il y a de nombreuses possibilités pour exécuter le code exemple de la leçon. Difficile de le faire avec son propre équipement, car il faut un GPU, pas mal de mémoire et du stockage. Le mieux est de travailler dans le Cloud (voir le Cloud pour le DL)

La méthode retenue ici est d’utiliser Colaboratory de Google. C’est gratuit – du moins pour ce type d’exercices.

accéder à Google Colab (GCP)

La procédure est décrite ici.

  • Ouvrir la page Colab
  • Sélectionner l’onglet Github
  • Saisir : fastai/course-v3
  • Sélectionner le fichier : lesson3-planet.ipynb
  • Dans Jupyter, modifier l’environnement d’exécution pour (GPU)

magic

Le code commence par des Magic commands.

%reload_ext autoreload
%autoreload 2
%matplotlib inline

%reload_extReload an IPython extension by its module name.

autoreload reloads modules automatically before entering the execution of code typed at the IPython prompt.

C’est rarement utile. Mais on ne sait jamais.

Quant à Reload %autoreload 2, « it Reloads all modules (except those excluded by %aimport) every time before executing the Python code typed ».

Données

kaggle

Les données de l’exemple sont hébergées chez Kaggle.

Il faut d’abord installer l’API kaggle. Pour rappel le !, dans Jupyter, permet d’exécuter une commande system.

! pip install kaggle --upgrade

Jeremy Howard (JH) a été responsable de Kaggle, ce qui explique probablement pourquoi il en fait souvent la promotion. A raison, car c’est très utile.

Kaggle est une plateforme d’hébergement de données soumises à compétition dans le domaine du Machine Learning.

Il faut ensuite obtenir le fichier kaggle.json. Pour cela, il faut préalablement avoir un compte kaggle (c’est gratuit) et demander un API Token (my profile/create API new token). Ceci aura pour effet de stocker le fichier kaggle.json sur votre ordinateur.

Il faut ensuite le rendre disponible à Google Colaboratory, ce que n’explique pas JH. Ajoutez les lignes ci-dessous au code exemple. Lorsque vous exécuterez ces lignes, alors une fenêtre s’ouvrira pour que vous puissiez faire un upload dans GCP.

from google.colab import files
uploaded = files.upload()

créations de dossiers

Pour ceux qui ne connaissent pas bien mkdir.

Il s’agit de la création d’un dossier (directory).

  • L’option -p permet de ne pas générer d’erreur si le dossier existe déjà
  • Le ~ indique le home directory.
  • Le . indique un fichier caché
! mkdir -p ~/.kaggle/
! mv kaggle.json ~/.kaggle/.

La commande mv permet de déplacer le fichier. Ici kaggle.json est déplacé dans le dossier nouvellement créé.

planet

Les données du test sont celles du challenge Planet de Kaggle.

In this competition, Planet and its Brazilian partner SCCON are challenging Kagglers to label satellite image chips with atmospheric conditions and various classes of land cover/land use. 

https://www.kaggle.com/c/planet-understanding-the-amazon-from-space
path = Config.data_path()/'planet'
path.mkdir(parents=True, exist_ok=True)
path

Config.data_path est une méthode, de la classe Config, du package /fastai/datasets.py.

Config.data_path Get the path to data in the config file

A l’issue de ces commandes, le dossier /root/.fastai/data/planet est créé.

/root/.fastai/data/planet

unzip

Les données sont téléchargées, et décompressées.

! kaggle competitions download -c planet-understanding-the-amazon-from-space -f train-jpg.tar.7z -p {path}  
! kaggle competitions download -c planet-understanding-the-amazon-from-space -f train_v2.csv -p {path}  
! unzip -q -n {path}/train_v2.csv.zip -d {path}
! conda install -y -c haasad eidl7zip
! 7za -bd -y -so x {path}/train-jpg.tar.7z | tar xf - -C {path.as_posix()}

Multiclassification

Il s’agit dans cet exercice de classer des images.

Nous ne sommes pas dans le cas d’une classification binaire car il y a plus de 2 classes. On est dans le cas de multiple labels classification.

In machine learning, multiclass or multinomial classification is the problem of classifying instances into one of three or more classes. (Classifying instances into one of the two classes is called binary classification.)

Multiclass classification should not be confused with multi-label classification, where multiple labels are to be predicted for each instance.

https://en.wikipedia.org/wiki/Multiclass_classification

transform

tfms = get_transforms(flip_vert=True, max_lighting=0.1, max_zoom=1.05, max_warp=0.)

Utility func to easily create a list of flip, rotate, `zoom`, warp, lighting transforms.

On crée une transformateur d’images qu’on utilisera ensuite.

src

On indique la source des images.

Il y a peu à commenter sur ces lignes. Sauf peut-être le np.random.seed(42) qui permet de générer la même séquence aléatoire.

np.random.seed(42)
src = (ImageList.from_csv(path, 'train_v2.csv', folder='train-jpg', suffix='.jpg')
       .random_split_by_pct(0.2)
       .label_from_df(label_delim=' '))

databunch

data = (src.transform(tfms, size=128)
        .databunch().normalize(imagenet_stats))

Les données dont la source est indiquée (src) sont transformées (tfms) et normalisées.

Pourquoi normaliser les images ?

The purpose of dynamic range expansion in the various applications is usually to bring the image, or other type of signal, into a range that is more familiar or normal to the senses, hence the term normalization. Often, the motivation is to achieve consistency in dynamic range for a set of data, signals, or images to avoid mental distraction or fatigue. For example, a newspaper will strive to make all of the images in an issue share a similar range of grayscale.

https://en.wikipedia.org/wiki/Normalization_(image_processing)

Pour en savoir plus sur le dataset, c’est ici.

quelques images de la base

data.show_batch(rows=3, figsize=(12,9))

Architecture

Cette partie est un peu plus compliquée…

arch = models.resnet50
acc_02 = partial(accuracy_thresh, thresh=0.2)
f_score = partial(fbeta, thresh=0.2)
learn = create_cnn(data, arch, metrics=[acc_02, f_score])

La 1ère ligne crée un modèle ResNet-50.

Si vous ne savez pas ce qu’est un ResNet :

ResNet is a short name for Residual Network. As the name of the network indicates, the new terminology that this network introduces is residual learning.

What is the need for Residual Learning?
Deep convolutional neural networks have led to a series of breakthroughs for image classification. Many other visual recognition tasks have also greatly benefited from very deep models. So, over the years there is a trend to go more deeper, to solve more complex tasks and to also increase /improve the classification/recognition accuracy. But, as we go deeper; the training of neural network becomes difficult and also the accuracy starts saturating and then degrades also. Residual Learning tries to solve both these problems.

What is Residual Learning?
In general, in a deep convolutional neural network, several layers are stacked and are trained to the task at hand. The network learns several low/mid/high level features at the end of its layers. In residual learning, instead of trying to learn some features, we try to learn some residual. Residual can be simply understood as subtraction of feature learned from input of that layer. ResNet does this using shortcut connections (directly connecting input of nth layer to some (n+x)th layer. It has proved that training this form of networks is easier than training simple deep convolutional neural networks and also the problem of degrading accuracy is resolved.

This is the fundamental concept of ResNet.

https://www.quora.com/What-is-the-deep-neural-network-known-as-“ResNet-50”

L’objet de ces lignes est de créer un ConvNet (réseau de neurones à convolution) . Le learner est un ConvNet pour données (data) Planet, avec une architecture de type ResNet 50, et les métriques metrics=[acc_02, f_score]

JH dit dans son cours : les métriques n’ont aucune influence sur la façon dont le modèle est entraîné.

accuracy

Cette ligne est un peu bizarre, pour ceux qui ne connaissent pas Python 3. En fait, il s’agit simplement de forcer les paramètres d’une fonction.

acc_02 = partial(accuracy_thresh, thresh=0.2)

acc_02 est donc une nouvelle fonction qui appelle accuracy_thresh() avec le paramètre 0.2. Ce paramètre indique le seuil au delà duquel la probabilité d’appartenance à une classe est retenue. Pourquoi 0.2 et pas 0.5 ou 0.3, rien a priori, sinon l’expérience sur ce jeu de données.

The partial() is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature. 

https://docs.python.org/2/library/functools.html

f score

Kaggle utilise le F score, F2 (voir ici)

f_score = partial(fbeta, thresh=0.2)

La méthode est la même que précédemment, une sorte de surcharge de fbeta (qui est la fonction générique f score), le paramètre indique le score (f 2 en l’occurence)

lr_find()

lr_find(learn:Learnerstart_lr:Floats=1e-07end_lr:Floats=10num_it:int=100stop_div:bool=Truewd:float=None)

Explore lr from start_lr to end_lr over num_it iterations in learn. If stop_div, stops when loss diverges.

https://docs.fast.ai/basic_train.html
learn.lr_find()
learn.recorder.plot()

Il est bien connu que trouver le bon learning rate n’est pas facile. JH utilise la méthode décrite ici.

On voit qu’après l’exécution de learn.lr_find(), le graphique montre que le meilleur lr est 0.01.

On peut donc commencer l’apprentissage avec ces valeurs.

apprentissage

lr = 0.01
learn.fit_one_cycle(5, slice(lr))

Le temps d’exécution est d’environ 20 minutes.

epochtrain_lossvalid_lossaccuracy_threshfbetatime
10.1262630.1091050.9425790.90456603:52
20.1104920.0978370.9511390.91356603:50
30.1033130.0912630.9564220.92114903:48
40.0956450.0884160.9562770.92361503:49
50.0920680.0874350.9568580.92372303:49

Que disent ces premières données :

le f score (2) de ce 1er essai est de 92%, alors que les 50 meilleurs de la compétition (voir leaderbord) sont autour de 93%. Ce 1er résultat est très encourageant. L’accuracy est autour de 96% !

On essaie d’améliorer ce modèle.

Pour cela, on le sauvegarde d’abord puis on le unfreeze.

learn.save('stage-1-rn50')
learn.unfreeze()

unfreeze()
Unfreeze entire model.
Sets every layer group to trainable

https://docs.fast.ai/basic_train.html

et on recommence :

learn.lr_find()
learn.recorder.plot()
learn.fit_one_cycle(5, slice(1e-5, lr/5))

If you pass slice(start,end) then the first group’s learning rate is start, the last is end, and the remaining are evenly geometrically spaced.
If you pass just slice(end) then the last group’s learning rate is end, and all the other groups are end/10. For instance (for our learner that has 3 layer groups):

https://docs.fast.ai/basic_train.html#Learner.lr_range

Pourquoi ces valeurs, slice(1e-5 et lr/5) ?

La première (1e-5) parce qu’après avoir vu sur la courbe le changement à partir de 1e-04, on choisir de prendre ce lr/10 (par expérience) et le second lr/5 est celui calculé à l’étape précédente, divisé par 5 (expertise de JH)

Notez que c’est une règle d’expérience (aucun fondement théorique)

Après ces opérations, le f score passe à 0,9299 et l’accuracy à 0,9598.

Améliorations

L’apprentissage a été fait jusqu’à présent sur des images carrées de 128. Or elles sont en 256^2.

Etant donné que nous avons un modèle déjà très performant, on va recommencer la même chose, mais cette fois sur les images de résolution maximale.

data = (src.transform(tfms, size=256)
        .databunch().normalize(imagenet_stats))

learn.data = data
data.train_ds[0][0].shape
learn.freeze()
learn.lr_find()
learn.recorder.plot()

Puis (sans commentaires car il s’agit de la même chose que précédemment) cet apprentissage qui nous amène dans les 10% les meilleurs de la compétition.

lr=1e-2/2
learn.fit_one_cycle(5, slice(lr))
learn.save('stage-1-256-rn50')
learn.unfreeze()
learn.fit_one_cycle(5, slice(1e-5, lr/5))

Résumé

En quelques lignes de code, en très peu de temps, grâce aux logiciels de fastai, on réussit à se classer parmi les 10% les meilleurs de la compétition Kaggle – Planet (multi label classification).

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *