Descente de gradient

Python works with packages. A large number of packages can be dowloaded all together in the Anaconda environment. Start downloading here (version >3) https://www.anaconda.com/download/ Anaconda comes with

  • Spyder, a GUI for python
  • Jupyter Notebook, how to create great documents

Enjoy!

Bee careful :

  • Python is sensible to spaces in the beginning of lines,
  • Python is sensible to number type : $3$ and $3.$ are totally different: integer and float types.
In [1]:
import warnings
warnings.filterwarnings('ignore')

Plaçons-nous dans le cadre suivant : on considère $h:\mathbb{R}⟶\mathbb{R}$ une fonction dérivable sur $\mathbb{R}$ (sa dérivée existe en tout point de $\mathbb{R}$), dont on veut déterminer le minimum. On suppose que ce minimum existe et qu’il est unique, c’est-à-dire qu’il n’existe qu’un $x_{min}\in\mathbb{R}$ tel que $x_{min}=arg\min_x(h(x))$. On suppose que $x_{min}$ est inconnu et impossible à déterminer de manière exacte et on va donc en chercher une valeur approchée. Introduisons maintenant une méthode très utilisée en analyse de données pour réaliser ce genre de recherche et appelée méthode de descente de gradient. Son principe général est le suivant :

  • Partons d’un point $x_0∈\mathbb{R}$
  • Calculons $x_1=x_0−s*h′(x_0)$
  • Et ainsi de suite… $s$ est un paramètre réel positif et est appelé pas de descente. Après plusieurs étapes, on récupère une valeur approchée de $x_min$. Tout au long de cette partie, nous allons prendre comme exemple la fonction suivante : $$h:x⟼\frac{1}{4}x^4+(x−1)^2$$

1) Donner l’équation que doit vérifier $x_{min}$, le minimum de cette fonction (donner juste l’équation, sans essayer de la résoudre).

$$x_{min}^3+2(x_{min}-1)=0$$

2) Justifier le fait que le $x_{min}$ qui vérifie l’équation précédente est effectivement le minimum de la fonction $h$ (et non un maximum).

$h''(x)<0$ quelque soit $x$. Donc $h$ est bien minimum en $x_{min}$

3) Ecrire une fonction, appelée descente() qui, étant donné une fonction dérivée $f′$, une valeur initiale $x_0$ et un pas $s$, calcule une valeur approchée de $x_{min}$ (le point ou le minimum de la fonction $f$ est atteint) à $\epsilon$ près, initialisé à $10^{-5}$, pour un nombre maximum d'itérations $N_{iter}$, initialisé à $1000$. Cette fonction doit aussi dire si elle a convergé, ou pas.

In [2]:
import numpy as np

def descente(f_prime,x_0,s,eps=1e-5,N_it=1000):
    x = x_0
    err = eps+1
    it = 1
    while it<N_it and err>eps:
        x_old = x
        x = x_old - s*f_prime(x_old)
        err = np.abs(x-x_old)
        it = it+1
    if it==N_it:
        print("No convergence")
    else:
        print("Convergence")
    return(x)

4) Tester votre fonction descente sur la fonction $h$ avec comme point de départ $x_0=0$, comme précision $\epsilon=10^{−5}$ et comme nombre maximum d'itérations $N_{iter}=1000$. Les valeurs suivantes pour le pas $s$ : $0.01,0.1,0.25,0.5$. Que remarquez-vous ?

In [3]:
def h(x):
    return(x**4/4+(x-1)**2)
    
def h_prime(x):
    return(x**3+2*(x-1))

steps = [0.01,0.1,0.25,0.5]
x_0 = 0
x_min_estimates = [0]*len(steps)

for s_id in list(range(len(steps))):    
    x_min_estimates[s_id]=descente(f_prime=h_prime,x_0=x_0,s=steps[s_id])

print(x_min_estimates)
Convergence
Convergence
Convergence
Convergence
[0.7706648235074804, 0.7709060069336524, 0.7709169573045527, 0.7709123995107867]

5) Créer une fonction descente_2 qui print à chaque itération la valeur de l'erreur commise et tester cette fonction sur le même jeu de paramètres que précédemment en modifiant $N_{it}=5$ et $x_0=1$. Que remarquez-vous ?

In [4]:
import numpy as np

def descente_2(f_prime,x_0,s,eps=1e-5,N_it=10):
    x = x_0
    err = eps+1
    it = 1
    while it<N_it and err>eps:
        x_old = x
        x = x_old - s*f_prime(x_old)
        err = np.abs(x-x_old)
        print(err)
        it = it+1
    if it==N_it:
        print("-----! No convergence !-----")
    else:
        print("Convergence")
    return(x)

res = descente_2(f_prime=h_prime,x_0=x_0,s=1)
2
10
530
142237690
2877658741925637956479970
23829661354318613029894138883630712902094568222027934815384216940262439210
13531738976146761620095288308781299412205036994889788540968064204594039982113397715610737206443628862954085960715940796465539850295134353393304320243834318670765880541068184559596568135319199032812592262424041355645142130
2477769115366476876046178713704968472927542546847700484932624757516817486915788166335167765444212741479716069433237949387285731732711751084803763796570131940954995291218292981272644794550339811579199053220614792290836325994777121255808037879360840219307130258867929601260827137738463472258278679785980259245300813141905749463229333751654278797424840265843548559895383380948828294935916920635059182447445886741597811818725524375227573016287751921599243379584580884199627192872622210719509737586662578890594095688769269113787925143255889368547874054740764360084447207244582895301183313385130020455902692683102951373692001758688162186827851685226022884212752398490
15211866518083254103679775541891714531140963814416825818635326718977656906665282150805669557912471500551000386118032558875847756939073574212340234610902116362775238162017152882632892892859002895540417451447630749104381922572235015725500519650674423794269757285387081475522909707961007811384893245090978825518383390778117625712271283712416510596459642994416778277029835309015150994397482923793050757478823788845787407394689242711761699836589523999286390163422548162735158937050126638962743308179894793157187991144079582449670866546943304192812720060556729573666546651500821820051324854889823686504311423742602471607532194632014441569594694649314503217846381402807155827016312444700869018359463910429471280957410620606013083103885782274547255041459610587632847305228515338893106013958879048219901813928583110552403007798279679819099706019545038006869976764197951387353702972982734783422229428255166365679776835312252002208965492226729172139444331121528705954901620201877606059130892954136111129345399832646734926723017822980876248783163038413788530143401901384090504342165820029408386572504481844417260225619029482798082703492639682550641392109758738449095019024324444391490694119450545157068802418828852642792658796192491257963062635340472290620304904549276875891832263478579211007623474468791012264804855610979879186731065281775793293425884909157257416745745011897242411298745313654001573998506216152103441302893114095595321827401951409364053376583147075376065467567720012305460650063321930160973724592973848235446847024626572568537212298405890362544390246902236418577016488765526199438167748043585276898189601706359697011306506213361699673142296309898249028766136011647541941258874844136385429643072754409515241469395122225721397408929970414662142977396989680488271348248536882031971084691052391940363142308958109993214820715462715774183176294693400086493883551048743687045731586000149562414276715216604865391390181450718319289272828428656522316156105134000574371110128228741234370
-----! No convergence !-----

6) Modifier la fonction descente() en créant desctente_3() pour qu'elle renvoit le nombre d'itérations nécessaires à la convergence. Commenter.

In [5]:
import numpy as np

def descente_3(f_prime,x_0,s,eps=1e-5,N_it=1000):
    x = x_0
    err = eps+1
    it = 1
    while it<N_it and err>eps:
        x_old = x
        x = x_old - s*f_prime(x_old)
        err = np.abs(x-x_old)
        it = it+1
    return(it)

steps = [0.01,0.1,0.25,0.5]
x_0 = 0
x_min_estimates = [0]*len(steps)

for s_id in list(range(len(steps))):    
    x_min_estimates[s_id]=descente_3(f_prime=h_prime,x_0=x_0,s=steps[s_id])

print(x_min_estimates)
[222, 26, 8, 95]

7) Tracer le nombre d'itérations nécessaires pour trouver le minimum de $h$ en fonction du pas $s\in]0,0.55]$, on prendra 100 points équitablement répartis sur cet intervalle. Commenter et donner une valeur optimale, en terme du nombre d'itérations, pour le pas de descente.

In [7]:
steps = np.linspace(0,0.55,101)[1:101]
iterats = [0]*100
for s_id in list(range(len(steps))):
    iterats[s_id]=descente_3(f_prime=h_prime,x_0=x_0,s=steps[s_id])
    
import matplotlib.pyplot as plt

fig = plt.figure()
plt.plot(steps,iterats, color='red', marker='*',linewidth=2, markersize=2)
fig.suptitle("Nombre d'itérations en fonction du pas de gradient", fontsize=20)
plt.xlabel('Pas du gradient', fontsize=18)
plt.ylabel("Nombre d'itérations", fontsize=16)

plt.show()