TP1 : Découverte de NumPy et Matplotlib

Programmation pour l’IA

  1. Découvrir la programmation avec numpy.
    • ConnaĂ®tre les fonctions principales.
    • Se familiariser avec la vectorisation.
  2. Découvrir la librairie matplotlib.
    • Savoir tracer une fonction.
    • Savoir afficher un nuage de points.
    • Savoir afficher un histogramme.
    • Savoir afficher une image.

La regression linéaire permet de résoudre le problème suivant : étant donnée un ensemble de point \(D = \{(x_i,y_i) \in \mathbb{R}^2, i = 1 .. n\}\), on cherche une fonction linéaire \(f \colon x \mapsto \alpha x\) telle que \(y_i \simeq f(x_i)\). Comme nous sommes dans le cas simple où nos points sont dans le plan, on cherche plus exactement le “meilleur” \(\alpha\) tel que \(y_i \simeq \alpha x_i\). On va définir le meilleur \(\alpha\) comme celui qui minimise la quantité suivante :

\[\ell_D(\alpha) = (1/n) \sum_{1 \leq i \leq n} (y_i-\alpha x_i)^2\]

En remarquant que \(\ell_D\) est quadratique en \(\alpha\) et positive, on en déduit que le minimum de \(\ell_D\) est atteint là où sa dérivée s’annule. Un calcul permet de voir que le minimum est atteint en \(\alpha_0 = {\sum_{i\leq n} x_iy_i \over \sum_{i \leq n} x_i^2}\).

  1. En utilisant la syntaxe numpy, écrivez une fonction dataset(size: int, a: float, noise: float) qui retourne une paire de listes (X,Y) tel que Y[i] = a*X[i]+k[i] où X est une liste de taille size, X[i] une valeur uniforme entre \(0\) et \(100\) et k[i] est une valeur aléatoire, tirée selon la loi normale centrée en 0 et d’écart-type noise. On pourra utiliser les fonctions random.normal et random.uniform de numpy

  2. En utilisant matplotlib, affichez sur un graphique les points \((x_i,y_i)\) générés par votre fonction précédente. On pourra utiliser la méthode scatter. Voici un example :

Scatter
  1. Créer une fonction regression(X,Y) qui prend en argument deux listes X et Y ayant la même taille et renvoie la valeur \(\alpha_0\) comme définie dans l’énoncé.

  2. Utilisez matplotlib pour afficher en rouge la droite \(y=\alpha_0 x\) par-dessus les points affichés précédemment.

Une régression
  1. Écrire une fonction loss(X,Y,a) qui affiche la fonction d’erreur \(\ell_D\) pour le jeu de donnée défini par X,Y et la valeur a.

On souhaite étudier les performances de la fonction np.sum par rapport à la fonction Python sum en fonction de la taille du tableau d’entrée. On va utiliser pour cela la librairie time ainsi :

import time

start = time.time()
f()
end = time.time()
t = end-start

Ce code calcule le temps d’exécution de la fonction f() dans la variable t.

  1. En utilisant np.random.uniform, écrire un code qui génère un tableau contenant 10000 nombres entre 0 et 100 choisis uniformément.
  2. Comparez les performances de sum et de np.sum sur ce tableau. Qu’en pensez-vous ?
  3. Pour consolider notre intuition, on va réaliser d’autres tests : générez un tableau pour chaque taille entre 0 et 100000 avec un pas de 1000, calculez le temps de sum et np.sum sur chaque tableau et affichez les comme des nuages de points avec matplotlib. Le temps de np.sum doit être en rouge, le temps de sum en vert. On affichera la légende et le nom des axes.
  4. Quel semble être la complexité de ces fonctions ? Utilisez le code de l’exercice précédent pour trouver prédire les performances de ces fonctions sur des tableaux de taille arbitraire.

On considère le programme suivant :

import matplotlib.pyplot as plt
import numpy as np

img = plt.imread("mon_image.jpg") # img est non modifiable
img = img.astype(int) # transforme l'image en tableau modifiable de int
plt.imshow(img) # affichage de l'image dans une fenĂŞtre
plt.show()

Ce programme utilise matplotlib pour charger une image, la transformer en un tableau et l’afficher.

  1. Choisissez une image pour travailler dessus et adapter le programme ci-dessus pour qu’il l’affiche. Les exemples dans cet exerice utiliseront l’image suivante :
Image exemple
  1. En utilisant la méthode shape, donnez les dimensions du tableau img.

  2. Écrire :

    1. Une fonction invert(img) qui inverse les couleurs de l’image en place, c’est-à-dire que le tableau img est directement modifié (sans être copié) et la couleur \((r,g,b)\) devient \((255-r, 255-b, 255-b)\).
    Couleurs inversées
    1. Une fonction level_grey(img,k) qui transforme l’image en k niveau de gris. Pour cela, on calcule la moyenne \((r+g+b)/3\) et on décide de la tranche de gris dans laquelle elle se trouve. Par exemple, à trois niveau de gris, un pixel \((20, 200, 200)\) sera envoyé sur la couleur \((86, 86, 86)\) parce que la moyenne des valeurs est \(140\) et qu’on quantifie l’image en trois niveau de gris: entre \(0\) et \(85\), entre \(86\) et \(171\) et entre \(171\) et \(255\).
    Deux niveaux de gris
    Cinq niveaux de gris
  3. Écrire une fonction histogram(img) qui affiche un histogramme des couleurs, c’est-à-dire qu’on affiche, pour chaque valeur \(v\) entre \(0\) et \(255\) et chaque couleur \(c\) dans \(\{R,G,B\}\), un histogramme présentant le nombre de pixel dont la couleur \(c\) a valeur \(v\). On pourra utiliser pour cela :

    • La fonction numpy.histogram. Attention Ă  bien spĂ©cifier le paramètre bins pour contrĂ´ler les diffĂ©rentes valeurs. On peut utiliser de façon alternative la fonction numpy.bincount ou la fonction numpy.unique avec le paramètre return_counts=True.
    • La fonction pyplot.bar.
Histogramme des couleurs

On va s’intéresser à une technique connue sous le nom de Seam Carving qui permet de redimmensionner une image en gardant les éléments importants. Pour appliquer la méthode du Seam Carving, on a besoin de calculer l’“énergie” de chaque pixel. Intuitivement, un pixel a une petite énergie s’il est entouré de pixels de valeur similaires, et une grande énergie s’il diffère beaucoup des autres. Pour un pixel en \((i,j)\), on évalue cela, pour la couleur \(c \in \{r,g,b\}\), par :

\[(c[i-1,j]-c[i+1,j])^2 + (c[i,j-1]-c[i,j+1])^2\]

L’énergie d’un pixel est alors la racine carré de la somme de cette valeur pour chacune des trois couleurs. Le seam carving rétrécit l’image en enlevant un chemin de moindre énergie.

  1. Écrire une fonction qui calcule un tableau donc les éléments sont l’énergie de chaque pixel d’une image (vous pouvez l’afficher pour visualiser). Affichez l’énergie avec matplotlib. On utilisera pour cela la méthode imshow qui permet d’afficher une image, comme dans l’exemple. Lorsqu’un tableau de valeurs numériques est donné en argument, matplotlib le convertit automatiquement en une information RGB visible. On devrait avoir :
Niveaux d’énergie
  1. Écrire une fonction qui, étant donné une image d’énergie, trouve le chemin vertical de moindre énergie. Un chemin démarre par un pixel quelconque sur la première ligne de l’image et peut descendre en empruntant le pixel directement dessous ou l’un des pixels à gauche ou à droite de celui-ci. L’énergie d’un chemin est la somme des énergies des pixels qui se trouvent dessus. On utilisera une programmation dynamique.
Chemin de moindre énergie
Indice Pour cela, on crée un tableau paths de la même taille que l’image. On veut calculer la valeur suivante : path[i][j] doit être l’énergie minimal d’un chemin qui part de la première ligne de l’image et qui finit à la case \(i\), \(j\). On le calcule en remarquant que path[0][j] = energy[0][j] et que path[i][j] = energy[i][j]+min(path[i-1][j-1], path[i-1][j], path[i-1][j+1]).
  1. Conclure avec un programme qui peut redimensionner une image.

  2. Réfléchir à un moyen d’écrire un code vectoriel avec numpy pour accélérer le calcul d’énergie.