def angle_scalar_to_covmat(theta, sig1, sig2):
"""Créer une matrice de covariance."""
= np.zeros((2, 2))
rotation 0, 0] = np.cos(theta)
rotation[1, 0] = np.sin(theta)
rotation[0, 1] = -np.sin(theta)
rotation[1, 1] = np.cos(theta)
rotation[= rotation.T @ np.array([[sig1, 0.0], [0.0, sig2]]) @ rotation
Sigma return Sigma
def quad_grad(x, theta=0, sig1=1, sig2=1):
= angle_scalar_to_covmat(theta, sig1, sig2)
Sigma return Sigma @ x
Descente de gradients et variantes
Dans ce TP, nous allons proposer deux algorithmes pour trouver un minimiseur d’une fonction f:\mathbb{R}^d\rightarrow\mathbb{R} avec d\geq 2.
Certains packages utilisés peuvent nécessiter une installation préalable. Il sera utile d’utiliser les commandes suivantes avant de leur première utilisation:
pip install ipympl
pip install numba
Trois fichiers Python annexe sont fournis avec ce TP pour les visualisations (liens cliquables): dico_math_functions.py, widget_convergence.py et widget_level_set.py.
Algorithme de la descente de gradient
Dans cette partie, on suppose f une fonction lisse et notre but est d’introduire la méthode de descente de gradient (🇬🇧: gradient descent, GD) pour trouver son (ou un de ses) minima.
\begin{algorithm} \caption{Descente de gradient à pas fixe} \begin{algorithmic} \INPUT $\nabla f, x_{init}, \gamma,$ maxiter, $\epsilon$ \STATE $x = x_{init}$ \COMMENT{Initialization} \FOR{$i=1,\dots,$maxiter} \STATE $g\leftarrow \nabla f(x)$ \COMMENT{Evaluation du gradient en l'itéré courant} \IF{$\|g\|^2 \leq \epsilon^2$ } \COMMENT{Caractérisation d'un extremum} \STATE Break \ELSE \STATE $x \leftarrow x - \gamma g$ \COMMENT{Pas dans la direction opposée au gradient} \ENDIF \ENDFOR \OUTPUT $x$ \end{algorithmic} \end{algorithm}
Question 1: Coder la descente de gradient
Adapter en Python l’algorithme de la descente de gradient pour avoir en sortie la liste de la suite des itérés (x_k)_k.
Vérifier que votre méthode obtient bien le minimum global pour la fonction f_{test}: f_{test}: \begin{pmatrix} x_1 \\ x_2 \\\end{pmatrix} \mapsto (x_1 - 1)^2 + 3(x_2 + 1)^2
Code
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def ftest(x, y):
return (x - 1) ** 2 + 3*(y + 1)**2
= plt.subplots(subplot_kw={"projection": "3d"})
fig, ax = np.arange(-5, 5, 0.25)
X = np.arange(-5, 5, 0.25)
Y = np.meshgrid(X, Y)
X, Y = ftest(X, Y)
Z
# Plot the surface.
= ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
surf =0, antialiased=False)
linewidth
# Add a color bar which maps values to colors.
=0.5, aspect=5)
fig.colorbar(surf, shrink plt.show()
Question 2: Application au cas quadratique
On rappelle que toute matrice symétrique définie positive de taille 2\times 2 peut s’écrire: \Sigma_{\theta,\sigma_1,\sigma_2}= \begin{pmatrix} \cos(\theta) & -\sin(\theta) \\ \sin(\theta) & \cos(\theta) \end{pmatrix}^\top \begin{pmatrix} \sigma_1 & 0 \\ 0 & \sigma_2 \end{pmatrix} \begin{pmatrix} \cos(\theta) & -\sin(\theta) \\ \sin(\theta) & \cos(\theta) \end{pmatrix} \enspace, pour un certain triplet \theta \in \mathbb{R}, \sigma_1 \in \mathbb{R}_+ et \sigma_2 \in \mathbb{R}_+,
On s’intéressera dans la suite aux fonctions quadratiques de la forme suivante:
\begin{align*} f_{\theta,\sigma_1,\sigma_2}: \mathbb{R}^2 &\to \mathbb{R}\\ x &\mapsto \tfrac{1}{2} x^\top \Sigma_{\theta,\sigma_1,\sigma_2}x \enspace. \end{align*}
Le code pour calculer le gradient de la fonction f_{\theta,\sigma_1,\sigma_2} en un point x est donné par la fonction quad_grad
ci-dessous:
Utiliser la fonction quad_grad
pour calculer les itérés de la descente de gradient appliquée à f pour des valeurs de \theta,\sigma_1 et \sigma_2 données.
Tracer l’évolution graphique de la valeur de l’objectif f_{\theta,\sigma_1,\sigma_2}(x_k) au cours des itérations de l’algorithme de descente de gradient. On affichera des points (et non une courbe continue).
Pour visualiser l’impact des paramètres sur la fonction, on pourra utiliser les visualisations de la surface et de ses lignes de niveaux (🇬🇧: level set) disponibles dans le fichier widget_level_set.py
.
Descente par coordonnée
Dans cette partie, notons e_1,\dots,e_d la base canonique de \mathbb{R}^d.
Les méthodes de descente par coordonnée (🇬🇧: coordinate descent, CD) consistent à résoudre le problème d’optimisation en résolvant itérativement des problèmes (potentiellement beaucoup) plus petits.
\DeclareMathOperator*{\argmin}{argmin}
\begin{algorithm} \caption{Descente de coordonnée exacte} \begin{algorithmic} \INPUT $f, x_{init},$ maxiter, $\epsilon$ \STATE $x = x_{init}$ \COMMENT{Initialization} \FOR{$i=1,\dots,$maxiter} \FOR{$j=1,\dots,d$} \STATE $\tau_j \in \displaystyle\argmin_{t\in\mathbb{R}}f(x+t\cdot e_j)$ \COMMENT{Résolution problème 1D} \STATE $x_j \leftarrow x_j + \tau_j$ \ENDFOR \IF{critère arrêt vérifié} \STATE Break \ENDIF \ENDFOR \OUTPUT $x$ \end{algorithmic} \end{algorithm}
Question 3: Scipy et la descente par coordonnée exacte
Coder l’algorithme de descente par coordonnée exacte. On utilisera pour cela la fonction optimize
de scipy
pour résoudre le problème d’optimisation 1D. On rajoutera un critère d’arrêt effectué après chaque boucle de passage sur la totalité des coordonnées du vecteur x.
Question 4: Descente de gradient par coordonnée
Résoudre le problème d’optimisation 1D pour chaque coordonnée peut être coûteux selon la fonction en question. On préfère donc souvent utiliser des méthodes de descente à pas fixe (ou variable).
Comparer la descente de gradient classique à la descente de gradient par coordonnées pour la fonction f_{test}.
A l’aide de l’interface interactive disponible dans le fichier widget_convergence.py
, comparer la convergence de la descente de gradient classique à la descente de gradient par coordonnées dans le cas quadratique. On fera notamment varier les paramètres \sigma_1,\sigma_2,\theta et la taille du pas pour chaque méthode.
\begin{algorithm} \caption{Descente de gradient par coordonnée à pas fixe} \begin{algorithmic} \INPUT{$\nabla f = (\frac{\partial f}{\partial x_1}, \dots, \frac{\partial f}{\partial x_p}), x_{\rm init},\gamma, n_{\rm iter}$} \STATE{$x\gets x_{\rm init}$} \FOR{$i=1,\dots,n_{\rm iter}$} \FOR{$j=1,\dots,d$} \STATE $x_j \gets x_j - \gamma \frac{\partial f}{\partial x_j}(x)$ \COMMENT{Itération de descente de gradient sur la coordonnée $j$} \ENDFOR \ENDFOR \OUTPUT{$x$} \end{algorithmic} \end{algorithm}