Introduction à numpy pour le machine learning

Apprenez juste ce qu'il faut de numpy pour vous mettre au machine learning (cours 1h)

Les utilisateurs purs et durs de C++ ou de Fortran parmi les physiciens disent souvent que python est trop lent.

Certes, python est un langage interprété et il est lent.

Même les défenseurs de python comme moi le réalisent, mais nous pensons que ce n'est pas vraiment un problème, par exemple parce que :

  • Le temps de calcul est compensé par un temps de développement beaucoup plus réduit ;
  • Le profilage est facile, ce qui signifie que l'on peut trouver les parties du code qui sont lentes, les optimiser, et même les écrire dans des langages plus rapides afin qu'ils puissent être compilés et utilisés à partir de python ;

Et, surtout:

  • Certains outils python comme numpy sont aussi rapides que le C.

Dans ce didacticiel, vous comprendrez ce qu'est numpy et pourquoi c'est rapide, et apprendrez juste ce qu'il faut savoir pour faire du machine learning.

Ce tutoriel est interactif.

Pour l'exécuter sur Google Colab, cliquez sur ce lien . Vous pourrez exécuter le code vous-même et le modifier.

Prérequis : veuillez d'abord suivre Python Crash Course for Machine Learning , ou assurez-vous de maîtriser les bases de python.

Numpy est rapide !

L'objectif principal de numpy est de fournir une structure de données très efficace appelée tableau numpy, et les outils pour manipuler ces tableaux.

Pourquoi numpy est-il si rapide ?

Car, sous le capot, les tableaux sont traités avec du code compilé, optimisé pour le CPU. En particulier, les opérations numpy sont parallèles car elles utilisent SIMD (Single Operation Multiple Data).

Pour avoir une idée de cette rapidité, nous pouvons chronométrer quelques opérations.

Créons une grande liste avec un million d'entiers, puis un tableau numpy à partir de cette liste :

In [2]:
import numpy as np
lst = range(1000000)
arr = np.array(lst)
arr
Out[2]:
array([     0,      1,      2, ..., 999997, 999998, 999999])

Calculons maintenant le carré de tous les entiers et voyons combien de temps cela prend.

On commence par la liste :

In [3]:
%timeit squares = [x**2 for x in lst]
1 loop, best of 5: 299 ms per loop

Et on fait de même pour le tableau :

In [4]:
%timeit squares = arr**2
100 loops, best of 5: 2 ms per loop

Comme vous pouvez le voir, c'est 300 fois plus rapide.

On peut en principe boucler sur le tableau numpy comme ceci :

In [5]:
%timeit squares = [x**2 for x in arr]
1 loop, best of 5: 353 ms per loop

Mais alors, on perd complètement les avantages de numpy ! En effet, lorsque nous faisons arr**2 nous utilisons la fonction mise au carré de numpy, qui est intrinsèquement parallèle. Quand on boucle, on traite les éléments un par un avec du python basique. Donc:

Ne jamais boucler sur un tableau numpy ! Vous serez tenté de le faire, mais il ne doit y avoir aucune exception !

Types de données dans les tableaux Numpy

Nous avons vu que les tableaux numpy sont traités par du code compilé avec SIMD.

Pour que cela fonctionne, les éléments d'un tableau numpy doivent être :

  • d'un type basique, par exemple entiers ou flottants. La gamme complète des possibilités est donnée sur cette page;
  • du même type, de sorte qu'ils aient la même taille, par exemple des flottants de 64 bits, ou des entiers de 16 bits.

Au contraire, les listes python peuvent contenir des objets hétérogènes de tout type.

Voici quelques façons de créer des tableaux numpy avec différents types :

In [6]:
from sys import getsizeof as sizeof

# numpy guesses that it should use integers
x = np.array([0, 1, 2])
print(x.dtype)
# or floats: 
x = np.array([0., 1., 2.])
print(x.dtype)
# here we specify a python compatible type,
# interpreted by numpy as int64
x = np.array([0., 1., 2.], dtype=int)
print(x.dtype)
# here we specify that we want 8 bits integers
x = np.array([0, 1, 2], dtype=np.int8)
print(x.dtype)
int64
float64
int64
int8

Cela permet d'estimer facilement la taille d'un tableau numpy en mémoire, pour voir si vous allez faire exploser votre ordinateur avant de le faire réellement.


Exercice

Par exemple, considérons un échantillon de 1000 images, chacune avec 200x200 pixels, et 3 canaux de couleur par pixel. L'indice de couleur va de 0 à 255, et peut donc être codé sous forme d'entier de 8 bits.

En supposant que vous stockiez les données de toutes les images dans un seul tableau numpy, quelle serait sa taille en mémoire en Go ?


Opérations par élément avec Numpy

J'appelle opérations par élément toutes les opérations qui affectent les éléments du tableau, mais préservent la forme du tableau.

Tous les opérateurs habituels sont implémentés dans numpy, pour les tableaux. Par example:

In [7]:
x = np.array(range(5))
x**2
Out[7]:
array([ 0,  1,  4,  9, 16])

Notez que ces opérateurs, dans numpy, sont par élément :

In [8]:
x+1
Out[8]:
array([1, 2, 3, 4, 5])

Les équivalents des fonctions du package python math sont disponibles directement à partir du package numpy, du même nom, par exemple :

In [9]:
np.exp(x)
Out[9]:
array([ 1.        ,  2.71828183,  7.3890561 , 20.08553692, 54.59815003])

Enfin, des opérateurs binaires sont disponibles :

In [10]:
x = np.array([0, 1, 2])
y = np.array([1, 2, 3])
x+y
Out[10]:
array([1, 3, 5])
In [11]:
x*y
Out[11]:
array([0, 2, 6])

Forme des tableaux Numpy

Jusqu'à présent, nous n'avons vu que des tableaux à une seule dimension. Mais souvent, plus de dimensions sont utilisées.

Les tableaux multidimensionnels peuvent être créés à partir d'une liste de listes, par exemple :

In [12]:
x = np.array([[0, 1], [2, 3], [4,5]])
x
Out[12]:
array([[0, 1],
       [2, 3],
       [4, 5]])

L'attribut shape nous donne la longueur de chaque dimension :

In [13]:
x.shape
Out[13]:
(3, 2)

Dans ce cas, nous avons 3 rangées de 2 nombres. La première dimension est la dimension la plus externe et la seconde la dimension la plus interne.

Prenons l'exemple d'une image de 2x2 pixels, avec 3 canaux de couleurs (rouge, bleu, vert) dans chaque pixel :

In [14]:
x = np.array(
    [
        [ [1,2,3], [4,5,6], ],
        [ [7,8,9], [10,11,12]]
    ]
)
print(x)
print(x.shape)
[[[ 1  2  3]
  [ 4  5  6]]

 [[ 7  8  9]
  [10 11 12]]]
(2, 2, 3)

Pour visualiser plus facilement les tableaux numpy, je pense souvent à la dimension la plus interne séparément. Par exemple, ici, nous avons un tableau de 2x2 pixels, avec un sous-tableau de taille 3 dans chaque pixel.

Et comme dernier exemple, considérons un "vecteur colonne" :

In [15]:
x = np.array([
    [0],
    [1],
    [2], 
    [3]
])
print(x)
print(x.shape)
[[0]
 [1]
 [2]
 [3]]
(4, 1)

Comme vous pouvez le voir, le vecteur colonne a deux dimensions, ce qui peut être contre-intuitif. Il y a un seul nombre (un scalaire) sur la dimension interne.

Veuillez noter que dans numpy, une dimension peut également être appelée un "axe".

Très souvent, les tableaux numpy d'une forme donnée sont construits en initialisant tous les éléments à un nombre fixe ou à un nombre aléatoire. Par example:

In [16]:
np.zeros((2,3))
Out[16]:
array([[0., 0., 0.],
       [0., 0., 0.]])
In [17]:
np.ones(3)
Out[17]:
array([1., 1., 1.])
In [18]:
np.ones_like(x)
Out[18]:
array([[1],
       [1],
       [1],
       [1]])
In [19]:
np.random.rand(2,2)
Out[19]:
array([[0.28362784, 0.27507999],
       [0.91287129, 0.73746325]])

De nombreux autres outils d'échantillonnage aléatoire sont disponibles.

Indexation des tableaux Numpy

Indexation de base

Voici un tableau 1D :

In [20]:
x = np.arange(10) + 1
x
Out[20]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

Les éléments sont accessibles directement, en utilisant leur index dans le tableau (l'index commence à 0) :

In [21]:
x[1]
Out[21]:
2

Et, comme d'habitude dans les séquences python, les indices négatifs commencent par la fin :

In [23]:
x[-2]
Out[23]:
9

Le tableau peut être modifié sur place :

In [24]:
print(id(x))
x[1] = 0
print(id(x))
print(x)
139827735648496
139827735648496
[ 1  0  3  4  5  6  7  8  9 10]

Pour les tableaux multidimensionnels, l'indexation de base est effectuée en spécifiant une liste d'indices séparés par des virgules :

In [25]:
x = np.zeros((2,3))
x[0,1] = 1
x
Out[25]:
array([[0., 1., 0.],
       [0., 0., 0.]])

Sélection avec indexation booléenne

L'indexation peut être utilisée pour sélectionner des éléments de tableau en fonction d'un masque.

Créons à nouveau notre tableau 1D :

In [26]:
x = np.arange(10) + 1
x
Out[26]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

Pour créer un masque, nous évaluons une expression booléenne pour chaque élément du tableau. Par exemple, pour trouver tous les nombres pairs :

In [27]:
x%2 == 0
Out[27]:
array([False,  True, False,  True, False,  True, False,  True, False,
        True])

Que signifie cette expression ?

Puisque x est un tableau numpy, x%2 est une opération numpy élément par élément qui évalue %2 sur tous les éléments du tableau et renvoie un nouveau tableau avec les résultats :

In [28]:
xmod = x%2
xmod
Out[28]:
array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0])

Ensuite, nous sélectionnons les nombres pairs en demandant que le modulo soit égal à zéro. Encore une fois, l'opérateur == est appliqué à un tableau numpy, il s'agit donc d'une opération par élément :

In [29]:
mask = (xmod == 0)
mask
Out[29]:
array([False,  True, False,  True, False,  True, False,  True, False,
        True])

Avec ce masque, nous sélectionnons des nombres pairs et renvoyons un nouveau tableau :

In [30]:
x[mask]
Out[30]:
array([ 2,  4,  6,  8, 10])

En fait, le nouveau tableau est une vue sur le tableau d'origine. Les données ne sont pas copiées.

Il est possible d'inverser le masque :

In [31]:
x[~mask]
Out[31]:
array([1, 3, 5, 7, 9])

Et bien sûr il est possible de fabriquer des masques à la volée, ce qui est généralement fait :

In [32]:
x[x%2==0]
Out[32]:
array([ 2,  4,  6,  8, 10])

Quand on fait de la data science, l'indexation booléenne est très souvent utilisée pour sélectionner des données en appliquant des seuils sur les variables choisies.

Trancher (slicing)

En python, une tranche (slice) est définie comme un tuple, (départ, fin, pas). Il permet de sélectionner une séquence d'éléments dans une séquence (qui, par essence en python, est 1D) :

In [33]:
lst = list(range(1, 10))
print(lst)
lst[1::2]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Out[33]:
[2, 4, 6, 8]

Nous avons sélectionné des éléments :

  • à partir de l'indice 1 (valeur 2);
  • en s'arrêtant juste après la fin de la liste, car stop n'est pas spécifié. A ce stade, le dernier élément, 9, est inclus ;
  • par pas de 2. Donc 9 n'apparaît pas.

Exercice:

Jouez avec la définition de tranche dans la cellule ci-dessus. Essayez de:

  • sélectionnez tous les nombres impairs
  • sélectionnez tous les nombres supérieurs ou égaux à 5
  • sélectionner tous les nombres pairs entre 2 et 6

Dans l'exemple ci-dessus, nous avons décidé de ne pas spécifier stop. C'est possible pour tous les éléments de l'indice de tranche:

In [34]:
print( lst[:5:] )
print( lst[::2] )
print( lst[3::] )
print( lst[::] )
[1, 2, 3, 4, 5]
[1, 3, 5, 7, 9]
[4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Ces notations peuvent et doivent être simplifiées en :

In [35]:
print( lst[:5] )
print( lst[::2] )
print( lst[3:] )
print( lst[:] )
[1, 2, 3, 4, 5]
[1, 3, 5, 7, 9]
[4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Exercice:

Pourrait-on simplifier davantage ces expressions en supprimant plus de : ? Que se passerait-il si vous le faisiez ? Testez vos hypothèses dans la cellule ci-dessus.


En numpy, le tranchage est une simple généralisation du tranchage en python, mais cette fois à plusieurs dimensions.

Pour le tester, nous créons une matrice 2D avec 4 lignes et 5 colonnes. Pour cela, nous utilisons la méthode de remodelage qui sera discutée dans la section suivante :

In [36]:
x = np.arange(20).reshape(4,5)
x
Out[36]:
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

Nous pouvons maintenant utiliser la notation de tranche sur n'importe quel dimension. Voici quelques exemples :

  • sélectionnez la deuxième colonne :
In [37]:
x[:, 1]
Out[37]:
array([ 1,  6, 11, 16])
  • sélectionnez les deux premières colonnes :
In [38]:
x[:, :2]
Out[38]:
array([[ 0,  1],
       [ 5,  6],
       [10, 11],
       [15, 16]])
  • sélectionnez les colonnes par pas de 2 :
In [39]:
x[:, ::2]
Out[39]:
array([[ 0,  2,  4],
       [ 5,  7,  9],
       [10, 12, 14],
       [15, 17, 19]])
  • ordre inverse des colonnes, en spécifiant un pas de -1 sur la dernière dimension :
In [40]:
x[:, ::-1]
Out[40]:
array([[ 4,  3,  2,  1,  0],
       [ 9,  8,  7,  6,  5],
       [14, 13, 12, 11, 10],
       [19, 18, 17, 16, 15]])

Dans les exemples précédents, nous avons travaillé sur la dernière dimension, tout en préservant la première dimension. Pour cela, nous devions ajouter un : comme premier élément d'indexation pour demander une boucle sur la première dimension.

Mais cela peut devenir douloureux lorsqu'il y a plus de 2 dimensions. Par exemple, considérons ce tableau 3D :

In [41]:
y = np.arange(8).reshape((2,2,2))
y
Out[41]:
array([[[0, 1],
        [2, 3]],

       [[4, 5],
        [6, 7]]])

C'est un cube 2x2x2. Si vous souhaitez sélectionner les premiers éléments le long de la dernière dimension, vous pouvez soit faire :

In [42]:
y[:,:,0]
Out[42]:
array([[0, 2],
       [4, 6]])

Ou utilisez la notation ellipse :

In [43]:
y[...,0]
Out[43]:
array([[0, 2],
       [4, 6]])

Les points de suspension ajoutent autant de : que de dimensions manquantes dans l'instruction d'indexation. De cette façon, vous n'avez pas besoin de garder une trace du nombre de dimensions, et c'est plus facile à taper.

Mais surtout, votre code devient indépendant du nombre de dimensions du tableau. Par exemple dans ce cas, y[...,0] sélectionne les premiers éléments le long de la dernière dimension quelle que soit la dimension de y :

In [44]:
y = np.arange(8).reshape(2,4)
print(y)
print(y[...,0])
[[0 1 2 3]
 [4 5 6 7]]
[0 4]

Remodelage du tableau Numpy

Le remodelage d'un tableau consiste à réorganiser les données du tableau en un nouveau tableau de forme différente.

Tout d'abord, le remodelage permet de créer facilement un tableau avec une forme donnée, comme nous l'avons fait précédemment :

In [45]:
x = np.arange(20).reshape(4,5)
x
Out[45]:
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

Nous avons créé un tableau 1D plat avec des nombres entiers allant de 0 à 19, et l'avons immédiatement remodelé en un tableau 2D de forme (4, 5).

Rendre un tableau unidimensionnel

Souvent, il faut aplatir un tableau. Cela peut se faire de plusieurs manières :

  • aplatir :
In [46]:
y = x.flatten()
y
Out[46]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])
  • défiler :
In [47]:
x.ravel()
Out[47]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])
  • remodeler :
In [48]:
x.reshape(-1)
Out[48]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])

Le résultat est le même, alors quelle est la différence entre aplatir, défiler, et remodeler ?

flatten renvoie toujours une copie du tableau.

ravel, lui, tente de renvoyer une vue sur le tableau d'origine et ne copie les données que lorsque cela est nécessaire. Cela signifie que si vous modifiez la sortie de ravel, vous pouvez modifier le tableau d'origine. En outre, cela signifie que vous devez utiliser ravel sur de très gros tableaux pour économiser de la mémoire, et que ravel sera généralement plus rapide que flatten.

reshape est plus flexible car vous pouvez remodeler n'importe quoi. Tout comme ravel, il renvoie une vue et n'effectue une copie que lorsque cela est nécessaire. Je suggère d'utiliser flatten ou ravel lorsque vous souhaitez aplatir un tableau, pour rendre votre code plus explicite.

Transformer un tableau 1D en un tableau 1D "colonne"

Cela signifie que nous voulons transformer un tableau de forme (N,) en un tableau de forme (N,1). Considérons un tableau de forme (5,) :

In [49]:
y = np.arange(5)
print(y)
print(y.shape)
[0 1 2 3 4]
(5,)

Nous le transformons en un tableau de forme en colonnes (20,1) avec l'attribut c_ (notez que ce n'est pas une fonction !)

In [51]:
coly = np.c_[y]
print(coly)
print(coly.shape)
[[0]
 [1]
 [2]
 [3]
 [4]]
(5, 1)

Exemples pratiques de numpy en apprentissage automatique

Vous en savez maintenant suffisamment sur numpy pour gérer la plupart des opérations rencontrées dans les projets de machine learning.

Nous allons maintenant travailler sur des exemples réels afin que vous puissiez perfectionner vos compétences en numpy !

Gestion des étiquettes

Prenons un exemple pratique d'apprentissage automatique supervisé, la classification d'images de chiens et de chats.

Pour entraîner notre modèle d'intelligence artificielle, on lui présente des exemples photos de chiens et de chats.

Chaque exemple consiste en :

  • un tableau de données, ici une image d'un chien ou d'un chat
  • une cible (ou étiquette), par exemple 0 pour chien et 1 pour chat

Lorsqu'une image de chat lui est présentée, la machine affichera une valeur comprise entre 0 et 1. Nous comparerons cette valeur à la cible et mettrons à jour les paramètres du modèle afin que la prochaine fois, elle puisse se rapprocher de la solution.

L'ensemble de données d'entraînement (l'ensemble d'exemples utilisés pour l'entraînement) est généralement stocké dans des tableaux numpy. Par exemple, on pourrait utiliser deux tableaux numpy :

  • Un tableau de forme (10000, 200, 200, 3) pour les images, en supposant que nous ayons 10000 images couleur de 200x200 pixels.
  • Un tableau de forme '(10000,)' ou de forme '(10000, 1)' pour les cibles.

La structure exacte du jeu de données dépendra de l'endroit où vous l'obtenez ou de la façon dont vous le construisez, et vous devrez parfois modifier cette structure pour fournir le jeu de données au modèle.

Comme cas pratique, utilisons l'ensemble de données de chiffres manuscrits MNIST simplifié, inclus dans le package de machine learning scikit-learn. Nous chargeons d'abord le jeu de données :

In [52]:
from sklearn import datasets
digits = datasets.load_digits()

Nous obtenons un objet appelé digits. Nous pouvons utiliser les fonctions intégrées type et dir pour voir ce que c'est :

In [53]:
print( type(digits) ) 
print( dir(digits) )
<class 'sklearn.utils.Bunch'>
['DESCR', 'data', 'images', 'target', 'target_names']

Ok, Bunch est une classe de scikit-learn que nous ne connaissons pas, mais ses attributs ont des noms assez explicites. Imprimons quelques informations supplémentaires :

In [54]:
print( type(digits.images) )
print( type(digits.target) )
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

Deux tableaux numpy ! Imprimons leur forme :

In [55]:
print( digits.images.shape )
print( digits.target.shape )
(1797, 8, 8)
(1797,)

Quelques exercices

  1. Combien d'exemples avez-vous ? Quelle est la taille des images en pixels ? S'agit-il d'images en couleur ou en noir et blanc ?
  2. Imprimez la première image et la première étiquette
In [ ]:
# your code here
  1. Les modèles de scikit-learn attendent un tableau 1D d'étiquettes, comme celui que nous avons ici. Mais les modèles Keras+Tensorflow attendent vecteur colonne d'étiquettes. Convertissez le tableau d'étiquettes en un tableau adapté à Keras+Tensorflow :
In [ ]:
# your code here

Calcul de précision

La précision est une mesure de la performance des algorithmes de classification. Il est défini la probabilité de bien classer un exemple et calculé comme

$$ a = 1 - \frac{M}{N}, $$

où $N$ est le nombre total d'exemples et $M$ le nombre d'exemples mal classés.

Après l'entraînement, la précision est calculée sur un ensemble de données de test distinct de l'ensemble de données d'entraînement. En pratique, l'ensemble de données de test est envoyé sous forme de tableau numpy à la machine, ce qui produit un tableau numpy contenant les résultats de l'évaluation pour chaque exemple de l'ensemble de données de test.

La précision est ensuite calculée à partir de cette sortie et des étiquettes de l'ensemble de données de test.

Supposons que nous travaillons toujours sur l'ensemble de données de chiffres manuscrits MNIST et que l'ensemble de données de test contient 10 exemples, avec les étiquettes suivantes :

In [56]:
true_labels = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
true_labels
Out[56]:
array([7, 5, 7, 0, 8, 2, 8, 7, 5, 0])

Et supposons que la sortie de notre machine pour ces 10 exemples soit :

In [58]:
results = np.array([1, 2, 9, 4, 5, 6, 1, 8, 9, 10])
results
Out[58]:
array([ 1,  2,  9,  4,  5,  6,  1,  8,  9, 10])

Exercice:

Calculez la précision en pourcentage. Pour cela, notez que vous pouvez additionner toutes les entrées d'un tableau numpy x en faisant x.sum(). Aucune boucle autorisée !

In [ ]:
# your code here

Redimensionnement des niveaux de couleur de l'image

Les réseaux de neurones n'aiment pas trop les grands nombres.

Donc, généralement, les entrées du réseau de neurones sont redimensionnées pour être de l'ordre de l'unité avant d'être transmises au réseau.

Considérons à nouveau le jeu de données MNIST, qui est déjà chargé dans ce cahier en tant que chiffres. Voici la première image :

In [60]:
digits.images[0]
Out[60]:
array([[ 0.,  0.,  5., 13.,  9.,  1.,  0.,  0.],
       [ 0.,  0., 13., 15., 10., 15.,  5.,  0.],
       [ 0.,  3., 15.,  2.,  0., 11.,  8.,  0.],
       [ 0.,  4., 12.,  0.,  0.,  8.,  8.,  0.],
       [ 0.,  5.,  8.,  0.,  0.,  9.,  8.,  0.],
       [ 0.,  4., 11.,  0.,  1., 12.,  7.,  0.],
       [ 0.,  2., 14.,  5., 10., 12.,  0.,  0.],
       [ 0.,  0.,  6., 13., 10.,  0.,  0.,  0.]])

Exercice

  1. quel est le plus grand niveau de gris que l'on puisse trouver dans digits.image ? utilisez np.max sur ce tableau pour trouver cette valeur.
  2. redimensionnez l'ensemble de données de sorte que le plus grand nombre soit 1.
  3. vérifiez vos résultats

Aucune boucle autorisée !

In [62]:
# your code here

Standardiser les entrées du réseau de neurones

Nous savons déjà que les réseaux de neurones n'aiment pas traiter de grands nombres. Et il n'est pas non plus très bon d'avoir des formes très différentes pour les distributions des variables d'entrée.

Donc généralement, nous normalisons les variables d'entrée. Un choix courant consiste à centrer la moyenne de chaque distribution d'entrée sur 0 et à ramener son écart type à 1.

Construisons un jeu de données avec 1000 exemples et deux variables suivant chacune une distribution de densité de probabilité gaussienne.

On commence par tirer un échantillon aléatoire de 1000 événements pour chaque variable. Notez la grande différence entre la moyenne et le sigma entre les deux variables :

In [63]:
x = np.random.normal(100, 5, 1000)
y = np.random.normal(10, 1, 1000)

Ensuite, nous empilons les deux variables dans un seul tableau numpy :

In [64]:
dataset = np.c_[x, y]
print(dataset.shape)
dataset
(1000, 2)
Out[64]:
array([[ 97.95012036,   9.24670952],
       [103.72125587,   7.34808819],
       [ 96.27150171,  10.40925938],
       ...,
       [109.46898232,   8.86141864],
       [107.66333706,   8.98669986],
       [102.69780629,  10.67220869]])

Et enfin, nous vérifions la moyenne et l'écart type de chaque variable:

In [65]:
print(np.mean(dataset, axis=0))
print(np.std(dataset, axis=0))
[100.01318975  10.00575823]
[4.92972904 1.01209111]

En utilisant axis=0, nous avons demandé que la moyenne et l'écart type soient calculés le long du premier axe, qui est la dimension la plus externe. Par conséquent, numpy a calculé ces quantités pour les deux colonnes séparément.

Prenez le temps d'y réfléchir et de bien comprendre comment vous vous déplacez dans le tableau 2D lorsque vous suivez la dimension la plus externe.


Exercice

  1. Redimensionnez l'ensemble de données de sorte que la moyenne et l'écart type des deux variables soient définis sur 0 et 1, respectivement. Mais:
  • pas de boucle autorisée bien sûr !
  • vous n'êtes pas non plus autorisé à traiter les deux colonnes séparément pour les empiler après coup comme nous l'avons fait ci-dessus
  • une seule (courte) ligne de code
In [ ]:
# your code here
  1. Au début, nous aurions pu créer cet échantillon en une seule ligne de code. Consultez la documentation de numpy.random.normal et faites-le :
In [ ]:
# your code here

Changer le schéma de couleurs de RVB à BGR

Par convention, TensorFlow considère que les canaux de couleur d'une image sont ordonnés en rouge, vert, bleu (RVB) le long de la dimension la plus interne. Au contraire, Caffe, une autre bibliothèque d'apprentissage en profondeur, suppose que les couleurs sont dans l'ordre inverse (BVR).

Supposons que vous souhaitiez utiliser un réseau de neurones pré-entraîné avec Caffe. Mais vos images sont en RVB... Évidemment, vous devez convertir vos images de RVB en BGR pour que le réseau puisse utiliser sa connaissance des couleurs.

Dans cet exercice, vous découvrirez comment faire cela sur une seule image.

Tout d'abord, chargeons l'exemple d'image "face"

In [66]:
import scipy.misc
import matplotlib.pyplot as plt
face = scipy.misc.face()
plt.imshow(face)
Out[66]:
<matplotlib.image.AxesImage at 0x7f2c1c53eb50>

Exercice

  • déterminez le nombre de pixels et de canaux de couleur dans cette image
  • assurez-vous que le rouge est le premier canal. Pour faire ça:
  • faire une copie de l'image
  • dans la copie, mettre les deux derniers canaux de couleur à zéro
  • afficher la copie
  • convertir l'image en BGR

Astuce : vous devrez faire bon usage de la notation des tranches. Essayez également d'utiliser les ellipses

Conclusion

Bien joué ! vous êtes maintenant un utilisateur expérimenté de numpy !

Vos nouvelles compétences s'avéreront utiles, même en dehors du domaine de l'apprentissage automatique.

Juste un avertissement : ne réinventez pas la roue .

Il est fort probable que ce dont vous aurez besoin ait déjà été implémenté. N'hésitez donc pas à jeter un œil aux packages python scientifiques existants avant de commencer à coder. Par example:

  • scikit-image fournit des outils de manipulation d'images
  • opencv est une bibliothèque de vision par ordinateur à part entière, et est également fournie avec de nombreux outils.
  • scikit-learn propose des méthodes de manipulation de tableaux typiques en apprentissage automatique,
  • keras fournit des outils de prétraitement si vous utilisez Keras+TensorFlow pour l'apprentissage en profondeur
  • PyTorch possède également de nombreuses fonctions de prétraitement, dans le torchvision , torchaudio et torchtext .

Vous pouvez désormais vous rendre sur Matplotlib for Machine Learning pour apprendre à créer vos premiers tracés !


N'hésitez pas à me donner votre avis dans les commentaires ! Je répondrai à toutes les questions.

Et si vous avez aimé cet article, vous pouvez souscrire à ma newsletter pour être prévenu lorsque j'en sortirai un nouveau. Pas plus d'un mail par semaine, promis!

Retour


Encore plus de data science et de machine learning !

Rejoignez ma mailing list pour plus de posts et du contenu exclusif:

Je ne partagerai jamais vos infos.
Partagez si vous aimez cet article: