Mise en oeuvre de diagrammes personnalisés avec MatPlotLib¶
Introduction¶
Matplotlib est une bibliothèque de utilisée pour tracér des graphiques scientifiques en 2D ou 2D. Cela dit, vous pouvez aussi utiliser Matplotlib pour créer une variété de formes et de diagrammes. Par exemple, vous pouvez utiliser les fonctions plot et fill pour tracer des formes arbitraires, et vous pouvez utiliser les fonctions text et annotate pour ajouter du texte et des annotations. Il existe aussi une API de patchs qui permet de dessiner des formes plus complexes.
Un premier exemple simple¶
Voici un exemple simple de création d'un rectangle et d'un cercle avec du texte en utilisant Matplotlib.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig, ax = plt.subplots()
# Créer un rectangle
rectangle = patches.Rectangle((0.1, 0.1), 0.5, 0.5, fill=True)
ax.add_patch(rectangle)
# Ajouter du texte au rectangle
plt.text(0.35, 0.35, "Rectangle", ha="center", va="center", size=20)
# Créer un cercle
circle = patches.Circle((0.75, 0.75), 0.1, fill=True, color="red")
ax.add_patch(circle)
# Ajouter du texte au cercle
plt.text(0.75, 0.75, "Cercle", ha="center", va="center", size=20)
# On affiche le diagramme.
plt.show()
Il est à noter que certaines fonctions du module patches sont aussi accessible via l'alias plt. Vous pouvez donc aussi écrire le programme ainsi.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Créer un rectangle
rectangle = plt.Rectangle((0.1, 0.1), 0.5, 0.5, fill=True, color="red")
ax.add_patch(rectangle)
# Ajouter du texte au rectangle
plt.text(0.35, 0.35, "Rectangle", ha="center", va="center", size=20)
# Créer un cercle
circle = plt.Circle((0.75, 0.75), 0.1, fill=True)
ax.add_patch(circle)
# Ajouter du texte au cercle
plt.text(0.75, 0.75, "Cercle", ha="center", va="center", size=20)
# On affiche le diagramme.
plt.show()
Notez aussi que les axes du diagramme ne sont pas du plus bel effet. Si vous le souhaitez, vous pouvez les retirer.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Créer un rectangle
rectangle = plt.Rectangle((0.1, 0.1), 0.5, 0.5, fill=True, color="green")
ax.add_patch(rectangle)
# Ajouter du texte au rectangle
plt.text(0.35, 0.35, "Rectangle", ha="center", va="center", size=20)
# Créer un cercle
circle = plt.Circle((0.75, 0.75), 0.1, fill=True, color="fuchsia")
ax.add_patch(circle)
# Ajouter du texte au cercle
plt.text(0.75, 0.75, "Cercle", ha="center", va="center", size=20)
# On supprime les axes et on affiche le diagramme.
ax.axis("off")
plt.title("Un superbe diagramme ;-)")
plt.show()
Un exemple un peu plus travaillé : le schéma d'un neurone artificiel¶
Dans ce nouvel exemple, nous allons dessiner une représentation d'un neurone artificiel. Quelques explications suivront.
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Création de la figure et des axes
fig, ax = plt.subplots()
fig.set_figwidth(6)
fig.set_figheight(3)
# Dessin du corps du neurone (cercle)
neuron_body_out = plt.Circle((0.5, 0.5), 0.31, color=(0, 0.25, 0.4))
neuron_body_in = plt.Circle((0.5, 0.5), 0.3, color=(0, 0.5, 0.8))
separator = plt.Line2D((0.5, 0.5), (0.2, 0.8), color=(0, 0.25, 0.4))
ax.add_artist(neuron_body_out)
ax.add_artist(neuron_body_in)
ax.add_artist(separator)
# Tracé des entrées
ax.text(-0.4, 0.57, "$x_{1}$", fontsize=12)
ax.text(-0.4, 0.47, "...", fontsize=12)
ax.text(-0.4, 0.37, "$x_{n}$", fontsize=12)
for i in range(3):
plt.arrow(-0.2, 0.4 + i * 0.1, 0.32, 0, head_width=0.03, head_length=0.1, fc="black", ec="black")
# Etiquettes du biais
ax.text(0.1, 1.05, "Biais", fontsize=12)
plt.arrow(0.2, 1, 0.1, -0.16, head_width=0.03, head_length=0.1, fc="black", ec="black")
# Tracé de la sortie (ligne)
ax.text(1.3, 0.38, 'y', fontsize=12)
plt.arrow(0.8, 0.5, 0.5, 0, head_width=0.03, head_length=0.1, fc="black", ec="black")
# Symbole sigma et de la fonction d'activation
ax.text(0.30, 0.43, "\u03A3", fontsize=30)
ax.text(0.55, 0.43, "fa", fontsize=30, fontstyle="italic")
# Dessin de la fonction sigmoïde en haut à droite
x_values = np.linspace(-7, 7, 100)
y_values = sigmoid(x_values)
ax_sig = plt.axes([0.65, 0.72, 0.3, 0.2])
ax_sig.plot(x_values, y_values)
ax_sig.set_xticks([])
ax_sig.set_yticks([])
ax_sig.set_title("Activation : fonction Sigmoïde")
curve_connector = plt.Line2D((0.7, 0.94), (0.6, 0.8), color=(0, 0.25, 0.4))
ax.add_artist(curve_connector)
# Etiquette de la formule de la somme pondérée : notez la manière d'exprimer l'équation dans MPL et le "smirking smiley".
ax.text(-0.3, -0.1, "Somme pondérée : $z = \u03A3_{i=1}^{n} w_i x_i + b$ \U0001f60f", fontsize=12)
sum_connector = plt.Line2D((0.2, 0.37), (0, 0.4), color=(0, 0.25, 0.4))
ax.add_artist(sum_connector)
# Configuration des axes
ax.set_xlim(-0.5, 1.5)
ax.set_ylim(-0.2, 1.0)
ax.set_aspect("equal", adjustable="box")
ax.axis("off")
# Affichage du diagramme : ça le fait, non ???
plt.show()
Tout d'abord, le code importe les bibliothèques nécessaires. Il utilise numpy pour quelques calculs mathématiques et matplotlib pour créer des graphiques. Il définit ensuite une fonction appelée sigmoid qui représente la fonction d'activation sigmoïde souvent utilisée dans les réseaux de neurones.
Ensuite, le code dessine le corps du neurone, qui est essentiellement un gros cercle situé au centre de notre figure. Pour donner un peu de profondeur au dessin, il y a en fait deux cercles: un cercle extérieur de couleur bleue foncée et un cercle intérieur de couleur bleue. Une ligne verticale traverse également le cercle, pour séparer visuellement les entrées de la fonction d'activation.
Maintenant que le corps du neurone est en place, le code ajoute des étiquettes et des flèches pour représenter les entrées du neurone. Ce sont les valeurs que le neurone prend en compte pour faire ses calculs. Les étiquettes sont $x_{1}$, ..., et $x_{n}$, qui représentent symboliquement les entrées.
Le neurone a aussi ce qu'on appelle un biais, qui est juste une autre entrée qui n'a pas de connexion entrante. Le biais est représenté par une flèche qui entre par le haut du neurone.
Ensuite, le code dessine la sortie du neurone. Il s'agit d'une seule flèche qui sort de la droite du corps du neurone, avec une étiquette y. C'est le résultat des calculs du neurone.
À l'intérieur du corps du neurone, il y a deux symboles. Le symbole Σ représente la somme des entrées pondérées et le fa représente la fonction d'activation. Ce sont des étapes cruciales dans le calcul du neurone.
Comme une petite touche additionnelle, le code dessine également un petit graphique de la fonction d'activation sigmoïde en haut à droite. Cela montre comment la sortie du neurone est calculée à partir de la somme pondérée en utilisant cette fonction particulière.
Enfin, sous le neurone, il y a une étiquette qui montre l'équation de la somme pondérée. En guise de clin d'œil, il y a un petit visage souriant à côté de l'équation. Comme vous le constatez, l'exemple propose de tracer l'équation de la somme pondérée dans un style très proche de la notation mathématique. Pour arriver à cela, MPL utilise une notation inspirée de LaTeX. Pour de plus amples informations à ce sujet, le vous renvoie vers la documentation officielle de MPL : https://matplotlib.org/stable/tutorials/text/mathtext.html.
Cela montre exactement comment les entrées et les poids sont combinés mathématiquement. Et voilà! Le code termine en nettoyant les axes et en affichant la figure. Ce que vous obtenez est un joli schéma d'un neurone artificiel avec des annotations pour aider à comprendre comment il fonctionne.
Le schéma d'un réseau de neurones¶
Quand on parle de réseau de neurones, on considère plusieures couches de neuronnes interconnectées les unes aux autres. Voici un diagramme MPL qui montre comment tracer des couches de neuronnes totalement connectées (chaque neurone d'une couche est connecté à tous les neurones de la couche précédentes et tous ceux de la couche suivante). Quelques explications suivront.
import matplotlib.pyplot as plt
import numpy as np
def draw_neuronal_network(neurons = 10, layers = 5):
# Création d'une figure avec la taille mentionnée
plt.figure(figsize=(12, 8))
# Spécification des couleurs pour chaque couche
colors = ["blue"] + ["red"] * (layers - 2) + ["green"] if layers > 1 else ["blue"]
# Espacement entre les neurones
y_spacing = 1.0 / (neurons + 1)
# Boucle sur les couches
for layer in range(layers):
# Coordonnée x de la couche
x = layer
# Couleur de la couche
color = colors[layer]
# Boucle sur les neurones de la couche
for neuron in range(1, neurons + 1):
# Coordonnée y du neurone
y = neuron * y_spacing
# Dessin du neurone sous forme d'un cercle
plt.scatter(x, y, zorder=1, c=color, s=500)
# Si ce n'est pas la dernière couche, dessin des connexions
if layer < layers - 1:
next_x = layer + 1
for next_neuron in range(1, neurons + 1):
next_y = next_neuron * y_spacing
plt.plot([x, next_x], [y, next_y], c="gray", zorder=0, linestyle="-", linewidth=0.5)
# Configuration des axes
plt.axis('off')
plt.xlim(-0.5, layers + 0.5)
plt.ylim(0, 1.05)
# Ajout d'une légende
plt.scatter([], [], c="blue", label="Couche d'entrée")
plt.scatter([], [], c="red", label="Couches cachées")
plt.scatter([], [], c="green", label="Couche de sortie")
plt.legend(loc="right")
# Affichage du diagramme avec 10 neurones par couche et 5 couches (soit 50 neurones au total).
draw_neuronal_network(10, 5)
plt.show()
C'est la fonction draw_neuronal_network
qui va réaliser le tracé du réseau. Au début, elle commence par créer une figure est créée avec une taille spécifique, c'est comme une toile sur laquelle le dessin sera fait.
Ensuite, le code définit des couleurs que chaque couche de neurones aura : la couche d'entrée sera pixelisée en blue, les couches intermédiaires (appelées couches cachées) seront tracée en rouge et enfin la couche de sortie sera verte.
Maintenant, pour positionner les neurones de manière égale sur la figure, le code calcule l'espacement entre eux en divisant 1 par (nombre de neurones + 1)
.
Après cela, le code entre dans une boucle qui parcourt chaque couche de neurones. Pour chaque couche, il définit la coordonnée x qui sera la même pour tous les neurones dans cette couche. Il récupère également la couleur de la couche actuelle de la liste de couleurs.
Ensuite, il y a une autre boucle à l'intérieur de la première boucle. Cette boucle interne parcourt chaque neurone dans la couche actuelle. Il calcule la coordonnée y de chaque neurone en utilisant l'espacement que nous avons calculé plus tôt.
Avec les coordonnées x et y et la couleur, le neurone est dessiné comme un point en utilisant la fonction scatter. Notez l'utilisation du paramètre « zorder » afin de placer les neurones par dessus les connecteurs (zorder à 1 pour les neurones et à 0 pour les connecteurs).
Ensuite, si la couche n'est pas la dernière, le code dessine des lignes reliant chaque neurone de la couche actuelle à tous les neurones de la couche suivante. Cela est fait en utilisant une autre boucle interne qui parcourt les neurones de la couche suivante et utilise la fonction plot pour dessiner des lignes.
Après avoir parcouru toutes les couches et dessiné tous les neurones et les connexions entre eux, le code configure les axes en désactivant les marqueurs d'axe avec « plt.axis("off") », et en définissant les limites de l'affichage pour les axes x et y.
La fonction termine en dessinant une légende relative aux codes couleurs utilisés pour le tracé des couches.
Enfin, le code affiche la figure complète en invoquant notre fonction (avec 10 neurones par couches et 5 couches) puis plt.show()
.