import time import numpy as np import sklearn import matplotlib.pyplot as plt ############################################################################### # 03-descente_gradient_stochastique.py # @title: Fondamentaux - Apprentissage par descente de gradient stochastique # @project: Mes scripts de ML # @lang: fr # @authors: Philippe Roy # @copyright: Copyright (C) 2023 Philippe Roy # @license: GNU GPL ############################################################################### ### # Commandes NumPy : # - np.array : créer un tableau à partir d'une liste de listes # - np.c_ : concatène les colonnes des tableaux # - np.ones : créer un tableau de 1 # - np.linalg.inv : inversion de matrice # - .T : transposé de matrice # - .dot : produit de matrice ### ### # Commandes Scikit-Learn : # - sklearn.linear_model.SGDRegressor : créer un modèle de descente de gradient stochastique # - .fit : entrainement du modèle # - .predict : prédiction du modèle ### ############################################################################### # Initialisation ############################################################################### # Init du temps t_debut = time.time() # Init des plots fig = plt.figure(figsize=(15, 5)) fig.suptitle("Descente de gradient stochastique") donnees_ax = fig.add_subplot(141) # Observations : x1 et cibles : y model_ax = fig.add_subplot(142) # Modèle : theta0, theta1 couts_ax = fig.add_subplot(143) # Coûts : RMSE, MSE, ... app_ax = fig.add_subplot(144) # Taux d'apprentissage : eta i_list=[] # Itération couts_2d=[] couts_delta=[] couts_mse=[] # MSE couts_rmse=[] # RMSE eta_list=[] # Taux d'apprentissage ############################################################################### # Observations ############################################################################### # Observations d'apprentisage m = 1000 # Nombre d'observations bg = 1 # Quantité du bruit gaussien x1 = 2*np.random.rand(m, 1) # Liste des observations x1 y = 4 + 3*x1 + bg * np.random.rand(m, 1) # Liste des cibles y X = np.c_[np.ones((m, 1)), x1] # Matrice des observations, avec x0=1 donnees_ax.plot(x1, y, 'b.', label="Observations") exact_solution = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y) # Equation normale # Nouvelles observations x1_new=np.array([[0], [2]]) X_new = np.c_[np.ones((2, 1)), x1_new] # Matrice des observations, avec x0=1 ############################################################################### # Phase d'apprentissage et d'inférence ############################################################################### # Phase d'apprentissage par descente de gradient stochastique # - theta : vecteur paramètres du modèle # - gradient : gradient du coût en fonction de theta # - eta : taux d'appentissage ici dégressif par échéancier d'apprentissage (apts_ech) # n_epoq = 50 # Nombre d'époques n_epoq = 2 # Nombre d'époques (hyperparamètre) # Rédéfinition du taux d'apprentissage à partir de l'échéancier d'apprentissage t0, t1 = 5, 50 # Facteurs de l'échéancier d'apprentissage (hyperparamètres) def apts_ech (t): return t0 / (t + t1) # Calcul du coût (Mean Square Error (MSE), Root Mean Square Error (RMSE)) def mse(theta): return np.sum((np.dot(X, theta) - y)**2)/m def rmse(theta): return np.sqrt(np.sum((np.dot(X, theta) - y)**2)/m) # Initialisation aléatoire theta= np.random.randn(2,1) theta0=[theta[0]] theta1=[theta[1]] # Descente du gradient for epoq in range (n_epoq): for i in range(m): i_list.append(epoq * m + i) # Calcul du gradient du pas idx = np.random.randint(m) # Index aléatoire xi = X[idx : idx+1] yi = y[idx : idx+1] gradients = 2/1 * xi.T.dot(xi.dot(theta) - yi) eta = apts_ech (epoq * m + i) eta_list.append(eta) theta = theta - eta * gradients theta0.append(theta[0]) theta1.append(theta[1]) # Calcul de l'erreur avec la norme du vecteur 2D (Objectif -> Theta) dans le plan (theta0, theta1) couts_2d.append(np.sqrt((theta[0]-exact_solution[0])**2+(theta[1]-exact_solution[1])**2)) # Calcul du RMSE à la main : # FIXME : étrange, je n'ai pas le même résultat qu'avec 'couts_rmse.append(rmse(theta))' delta = 0 for j in range (m): delta= delta +((theta[0] + theta[1]*x1[j])-(exact_solution[0] + exact_solution[1]*x1[j]))**2 delta = np.sqrt(delta/m) couts_delta.append(delta) # Calcul du RMSE et du MSE par les matrices couts_mse.append(mse(theta)) couts_rmse.append(rmse(theta)) # Prédiction du pas # Phase d'inférence (dernier pas) y_predict=X_new.dot(theta) donnees_ax.plot(x1_new, y_predict, 'c-', linewidth=0.5) ############################################################################### # Avec Scikit-Learn (skl) ############################################################################### model_skl = sklearn.linear_model.SGDRegressor(max_iter=1000, penalty=None, eta0=0.1) # Modèle descente de gradient stochastique model_skl.fit(x1, y.ravel()) # Entrainement # print (model_skl.intercept_[0],model_skl.coef_[0]) y_predict_skl=model_skl.predict(x1_new) # Prédiction ############################################################################### # Résultats ############################################################################### # Plot des données donnees_ax.set_title("Données") donnees_ax.plot(x1_new, y_predict, 'r-', label="Prédictions") donnees_ax.plot(x1_new, y_predict_skl, 'y-', label="Prédictions - Scikit-Learn") donnees_ax.set_xlabel(r'$x_1$') donnees_ax.set_ylabel(r'$y$', rotation=0) donnees_ax.legend() # Plot des paramètres du modèle model_ax.set_title("Paramètres du modèle") model_ax.plot(theta0, theta1, '.', ls=':', color='r', fillstyle='none', label="Chemin", markevery=10) model_ax.plot(exact_solution[0], exact_solution[1], "o", color='k', fillstyle='full', label="Equation normale") model_ax.set_xlabel(r'$\theta_0$') model_ax.set_ylabel(r'$\theta_1 $', rotation=0) model_ax.legend() # Plot du coût couts_ax.set_title("Coûts") couts_ax.plot(i_list, couts_2d, '.', ls=':', color='c', fillstyle='none', label="Coûts vecteur 2D", markevery=10) couts_ax.plot(i_list, couts_delta, '.', ls=':', color='r', fillstyle='none', label="Coûts RMSE à la main", markevery=10) couts_ax.plot(i_list, couts_mse, '.', ls=':', color='b', fillstyle='none', label="Coûts MSE", markevery=10) couts_ax.plot(i_list, couts_rmse, '.', ls=':', color='g', fillstyle='none', label="Coûts RMSE", markevery=10) # couts_ax.plot(couts_i, couts_rmse, color='g', label="Coûts RMSE") couts_ax.set_xlabel(r'$i$') couts_ax.set_ylabel("Coûts") couts_ax.legend() # Plot du taux d'apprentissage app_ax.set_title("Taux d'apprentissage") app_ax.plot(i_list, eta_list, '.', ls=':', color='b', fillstyle='none', label="Taux d'apprentissage", markevery=10) app_ax.set_xlabel(r'$i$') app_ax.set_ylabel(r'$\eta$', rotation=0) # app_ax.legend() plt.show() # Performances print ("Theta th : theta0 : "+str(4)+" ; theta1 : "+str(3)) print ("Theta skl : theta0 : "+str(round(float(model_skl.intercept_[0]),3))+" ; theta1 : "+str(round(float(model_skl.coef_[0]),3))) print ("Theta : theta0 : "+str(round(float(theta[0]),3))+" ; theta1 : "+str(round(float(theta[1]),3))) print ("Erreurs : theta0 : "+str(round(float(theta[0]-4),3))+" ; theta1 : "+str(round(float(theta[1]-3),3))) print ("Temps : "+str(time.time()-t_debut))