CSI 4506 - automne 2024
Version: sept. 30, 2024 08h54
Dans ce cours, nous couvrirons les concepts fondamentaux de la régression linéaire et de la descente de gradient.
Vous acquerrez une compréhension approfondie de ces techniques essentielles d’apprentissage automatique, vous permettant de les appliquer efficacement dans votre travail.
La régression linéaire est introduite pour présenter de manière pratique un algorithme d’entraînement bien connu, la descente de gradient. De plus, elle sert de base pour introduire la régression logistique—un algorithme de classification—qui facilite davantage les discussions sur les réseaux de neurones artificiels.
Pouvez-vous penser à des exemples de tâches de régression ?
En se concentrant sur des applications pouvant fonctionner sur un appareil mobile.
Problème : trouver des valeurs pour tous les paramètres du modèle afin que le modèle s’ajuste au mieux aux données d’entraînement.
\[ \sqrt{\frac{1}{N}\sum_1^N [h(x_i) - y_i]^2} \]
Utilisateur : Expliquez le code sur l’écran précédent ligne par ligne.
IA : Certainement ! Voici une explication ligne par ligne du code fourni :
X
contenant 100 lignes et 1 colonne de nombres aléatoires. La fonction np.random.rand(100, 1)
génère des nombres aléatoires uniformément distribués entre 0 et 1. La multiplication par 6 ajuste ces nombres à la plage [0, 6), et la soustraction de 4 déplace la plage à [-4, 2).Explication : Cette ligne génère la variable cible y
basée sur une équation quadratique avec du bruit ajouté. Plus précisément :
X ** 2
calcule le carré de chaque élément de X
.- 4 * X
soustrait quatre fois chaque élément de X
.+ 5
ajoute une constante de 5.+ np.random.randn(100, 1)
ajoute du bruit gaussien avec une moyenne de 0 et un écart-type de 1 à chaque élément, rendant les données plus réalistes en introduisant une certaine variabilité.LinearRegression
du module sklearn.linear_model
, qui fait partie de la bibliothèque Scikit-Learn. Scikit-Learn est largement utilisée pour l’apprentissage automatique en Python.LinearRegression
et l’assigne à la variable lin_reg
. Cet objet sera utilisé pour ajuster le modèle de régression linéaire aux données.(X, y)
. La méthode fit
entraîne le modèle en utilisant les caractéristiques fournies X
et la variable cible y
.X_new
avec deux valeurs : -4 et 2. Ces valeurs représentent de nouveaux points de données pour lesquels nous voulons prédire la variable cible en utilisant le modèle entraîné.y_pred
pour les nouveaux points de données dans X_new
. La méthode predict
renvoie les valeurs prédites basées sur le modèle ajusté.En résumé, ce script génère des données synthétiques, ajuste un modèle de régression linéaire à ces données, puis utilise le modèle pour faire des prédictions sur de nouveaux points de données.
import os
import matplotlib as mpl
import matplotlib.pyplot as plt
def save_fig(fig_id, tight_layout=True, fig_extension="pdf", resolution=300):
path = os.path.join(fig_id + "." + fig_extension)
print("Saving figure", fig_id)
if tight_layout:
plt.tight_layout()
plt.savefig(path, format=fig_extension, dpi=resolution)
Un algorithme d’apprentissage typique comprend les composants suivants :
Un modèle, souvent constitué d’un ensemble de poids dont les valeurs seront “apprises”.
Une fonction objectif.
Un algorithme d’optimisation
Jusqu’à ce que certains critères d’arrêt soient atteints\(^1\) :
Évaluer la fonction de perte, en comparant \(h(x_i)\) à \(y_i\).
Apporter de petites modifications aux poids, de manière à réduire la valeur de la fonction de perte.
1: Par exemple, la valeur de la fonction de perte ne diminue plus ou le nombre maximal d’itérations est atteint.
Le graphe de la dérivée, \(f^{'}(t)\), est représenté en rouge.
La dérivée indique comment les changements dans l’entrée affectent la sortie, \(f(t)\).
La magnitude de la dérivée en \(t = -2\) est \(0\).
Ce point correspond au minimum de notre fonction.
Lorsqu’elle est évaluée en un point spécifique, la dérivée indique la pente de la ligne tangente au graphe de la fonction à ce point.
À \(t= -2\), la pente de la ligne tangente est de 0.
Une dérivée positive indique qu’augmenter la variable d’entrée entraînera une augmentation de la valeur de sortie.
De plus, la magnitude de la dérivée quantifie la rapidité du changement de la sortie.
Une dérivée négative indique qu’augmenter la variable d’entrée entraînera une diminution de la valeur de sortie.
De plus, la magnitude de la dérivée quantifie la rapidité du changement de la sortie.
import sympy as sp
import numpy as np
import matplotlib.pyplot as plt
# Define the variable and function
t = sp.symbols('t')
f = t**2 + 4*t + 7
# Compute the derivative
f_prime = sp.diff(f, t)
# Lambdify the functions for numerical plotting
f_func = sp.lambdify(t, f, "numpy")
f_prime_func = sp.lambdify(t, f_prime, "numpy")
# Generate t values for plotting
t_vals = np.linspace(-5, 2, 400)
# Get y values for the function and its derivative
f_vals = f_func(t_vals)
f_prime_vals = f_prime_func(t_vals)
# Plot the function and its derivative
plt.plot(t_vals, f_vals, label=r'$f(t) = t^2 + 4t + 7$', color='blue')
plt.plot(t_vals, f_prime_vals, label=r"$f'(t) = 2t + 4$", color='red')
# Fill the area below the derivative where it's negative
plt.fill_between(t_vals, f_prime_vals, where=(f_prime_vals > 0), color='red', alpha=0.3)
# Add labels and legend
plt.axhline(0, color='black',linewidth=1)
plt.axvline(0, color='black',linewidth=1)
plt.title('Function and Derivative')
plt.tlabel('t')
plt.ylabel('y')
plt.legend()
# Show the plot
plt.grid(True)
plt.show()
Étant donnée
\[ J(\theta_0, \theta_1) = \frac{1}{N}\sum_1^N [h(x_i) - y_i]^2 = \frac{1}{N}\sum_1^N [\theta_0 + \theta_1 x_i - y_i]^2 \]
Nous avons
\[ \frac {\partial}{\partial \theta_0}J(\theta_0, \theta_1) = \frac{2}{N} \sum\limits_{i=1}^{N} (\theta_0 - \theta_1 x_i - y_{i}) \]
et
\[ \frac {\partial}{\partial \theta_1}J(\theta_0, \theta_1) = \frac{2}{N} \sum\limits_{i=1}^{N} x_{i} \left(\theta_0 + \theta_1 x_i - y_{i}\right) \]
from IPython.display import Math, display
from sympy import *
# Define the symbols
theta_0, theta_1, x_i, y_i = symbols('theta_0 theta_1 x_i y_i')
# Define the hypothesis function:
h = theta_0 + theta_1 * x_i
print("Hypothesis function:")
display(Math('h(x) = ' + latex(h)))
Hypothesis function:
\(\displaystyle h(x) = \theta_{0} + \theta_{1} x_{i}\)
# Calculate the partial derivative with respect to theta_0
partial_derivative_theta_0 = diff(J, theta_0)
print("Partial derivative with respect to theta_0:")
display(Math(latex(partial_derivative_theta_0)))
Partial derivative with respect to theta_0:
\(\displaystyle \frac{\sum_{x_{i}=1}^{N} \left(2 \theta_{0} + 2 \theta_{1} x_{i} - 2 y_{i}\right)}{N}\)
# Calculate the partial derivative with respect to theta_1
partial_derivative_theta_1 = diff(J, theta_1)
print("Partial derivative with respect to theta_1:")
display(Math(latex(partial_derivative_theta_1)))
Partial derivative with respect to theta_1:
\(\displaystyle \frac{\sum_{x_{i}=1}^{N} 2 x_{i} \left(\theta_{0} + \theta_{1} x_{i} - y_{i}\right)}{N}\)
\[ h(x_i) = \theta_0 + \theta_1 x_i^{(1)} + \theta_2 x_i^{(2)} + \theta_3 x_i^{(3)} + \cdots + \theta_D x_i^{(D)} \]
\[ \begin{align*} x_i^{(j)} &= \text{valeur de l'attribut } j \text{ dans le } i \text{ème exemple} \\ D &= \text{le nombre d'attributs'} \end{align*} \]
La nouvelle fonction de perte est
\[ J(\theta_0, \theta_1, \ldots, \theta_D) = \dfrac {1}{N} \displaystyle \sum _{i=1}^N \left( h(x_{i}) - y_i \right)^2 \]
Sa dérivée partielle :
\[ \frac {\partial}{\partial \theta_j}J(\theta) = \frac{2}{N} \sum\limits_{i=1}^N x_i^{(j)} \left( \theta x_i - y_i \right) \]
où \(\theta\), \(x_i\) et \(y_i\) sont des vecteurs, et \(\theta x_i\) est une opération vectorielle !
Le vecteur contenant la dérivée partielle de \(J\) (par rapport à \(\theta_j\), pour \(j \in \{0, 1 \ldots D\}\)) est appelé le vecteur gradient.
\[ \nabla_\theta J(\theta) = \begin{pmatrix} \frac {\partial}{\partial \theta_0}J(\theta) \\ \frac {\partial}{\partial \theta_1}J(\theta) \\ \vdots \\ \frac {\partial}{\partial \theta_D}J(\theta)\\ \end{pmatrix} \]
\[ \theta' = \theta - \alpha \nabla_\theta J(\theta) \]
L’algorithme de descente de gradient devient :
Répétez jusqu’à convergence :
\[ \begin{aligned} \{ & \\ \theta_j := & \theta_j - \alpha \frac {\partial}{\partial \theta_j}J(\theta_0, \theta_1, \ldots, \theta_D) \\ & \text{pour } j \in [0, \ldots, D] \textbf{ (mettre à jour simultanément)} \\ \} & \end{aligned} \]
Répétez jusqu’à convergence :
\[ \begin{aligned} \; \{ & \\ \; & \theta_0 := \theta_0 - \alpha \frac{2}{N} \sum\limits_{i=1}^{N} x^{0}_i(h(x_i) - y_i) \\ \; & \theta_1 := \theta_1 - \alpha \frac{2}{N} \sum\limits_{i=1}^{N} x^{1}_i(h(x_i) - y_i) \\ \; & \theta_2 := \theta_2 - \alpha \frac{2}{N} \sum\limits_{i=1}^{N} x^{2}_i(h(x_i) - y_i) \\ & \cdots \\ \} & \end{aligned} \]
Quelles étaient nos hypothèses ?
Une fonction est convexe si pour toute paire de points sur le graphe de la fonction, la ligne reliant ces deux points se trouve au-dessus ou sur le graphe.
Une fonction convexe possède un unique minimum.
La fonction de perte pour la régression linéaire (MSE) est convexe.
Pour les fonctions qui ne sont pas convexes, l’algorithme de descente de gradient converge vers un minimum local.
Les fonctions de perte généralement utilisées avec les régressions linéaires ou logistiques, et les Support Vector Machines (SVM) sont convexes, mais pas celles pour les réseaux de neurones artificiels.
L’algorithme de descente de gradient stochastique sélectionne aléatoirement un exemple d’apprentissage pour calculer son gradient.
epochs = 10
for epoch in range(epochs):
for i in range(N):
selection = np.random.randint(N)
# Calculate the gradient using selection
# Update the weights
La descente de gradient par lot est intrinsèquement lente et impraticable pour de grands ensembles de données nécessitant une prise en charge hors mémoire, bien qu’elle soit capable de gérer un nombre substantiel de caractéristiques.
La descente de gradient stochastique est rapide et bien adaptée pour traiter un grand volume d’exemples de manière efficace.
La descente de gradient par mini-lots combine les avantages des méthodes par lot et stochastiques ; elle est rapide, capable de gérer de grands ensembles de données, et tire parti de l’accélération matérielle, en particulier avec les GPU.
Nous allons brièvement revisiter le sujet en discutant des réseaux de neurones artificiels profonds, pour lesquels il existe des algorithmes d’optimisation spécialisés.
Un modèle linéaire suppose que la valeur de l’étiquette, \(\hat{y_i}\), peut être exprimée comme une combinaison linéaire des valeurs des caractéristiques, \(x_i^{(j)}\) : \[ \hat{y_i} = h(x_i) = \theta_0 + \theta_1 x_i^{(1)} + \theta_2 x_i^{(2)} + \ldots + \theta_D x_i^{(D)} \]
L’écart moyen quadratique (MSE) est : \[ \frac{1}{N}\sum_1^N [h(x_i) - y_i]^2 \]
La descente de gradient par lot, stochastique, ou par mini-lots peut être utilisée pour trouver les valeurs “optimales” des poids, \(\theta_j\) pour \(j \in 0, 1, \ldots, D\).
Le résultat est un régresseur, une fonction qui peut être utilisée pour prédire la valeur \(y\) (l’étiquette) pour un exemple non vu \(x\).
Marcel Turcotte
École de science informatique et de génie électrique (SIGE)
Université d’Ottawa