Cet article étend le concept de régression linéaire aux données de haute dimension, faisant suite à une discussion précédente sur les cas bidimensionnels.
1.1 Définition de l'Hyperplan
Dans un espace à D dimensions, un hyperplan \( L \) est défini par l'équation :
\[ L: \mathbf{w}^T \mathbf{x} + b = 0 \] où \( \mathbf{w} = [w_0, w_1, \dots, w_D]^T \) est le vecteur des coefficients (poids) et \( \mathbf{x} = [x_0, x_1, \dots, x_D]^T \) est le vecteur caractéristique d'un échantillon. L'hyperplan est ainsi déterminé par son vecteur normal \( \mathbf{w} \) et son biais \( b \). Il divise l'espace en deux demi-espaces tels que \( \mathbf{w}^T \mathbf{x} + b > 0 \) ou \( \mathbf{w}^T \mathbf{x} + b < 0 \).
Une représentation scalaire alternative est :
\[ L: \sum_{i=1}^{D} w_i x_i + b = 0 \] Il est courant d'intégrer le biais dans le vecteur des poids par augmentation des vecteurs :
\[ \mathbf{x}^T = [1, x_1, x_2, \dots, x_D] \] \[ \mathbf{w}^T = [b, w_1, w_2, \dots, w_D] \] Dans ce cas, l'équation de l'hyperplan se simplifie en :
\[ L: \mathbf{w}^T \mathbf{x} = 0 \] ou plus simplement :
\[ L(\mathbf{x}) = 0 \] #### Exemple de Transition
La transformation d'une droite bidimensionnelle en hyperplan illustre le concept :
\[ y = kx + b \implies kx - y + b = 0 \] \[ \begin{bmatrix} b & k & -1 \end{bmatrix} \cdot \begin{bmatrix} 1 \\ x \\ y \end{bmatrix} = 0 \] ### 1.2 Régression Linéaire Multivariée
Pour des données multivariées, l'ensemble d'échantillons est représenté par \( S = \{\mathbf{X}, \mathbf{y}\} \), où \( \mathbf{X} \) est une matrice \( N \times D \) de caractéristiques et \( \mathbf{y} \) est un vecteur de \( N \) étiquettes.
\[ \mathbf{X}^T = [\mathbf{x}_0, \mathbf{x}_1, \dots, \mathbf{x}_N], \quad \mathbf{x}_i = [x_{i1}, x_{i2}, \dots, x_{iD}] \in \mathbb{R}^D \] \[ \mathbf{y}^T = [y_0, y_1, \dots, y_N] \] L'objectif est de trouver un vecteur de poids \( \mathbf{w} \in \mathbb{R}^D \) tel que la prédiction \( \hat{\mathbf{y}} \) soit proche de \( \mathbf{y} \):
\[ \hat{\mathbf{y}} = \mathbf{X} \mathbf{w} \] L'erreur quadratique moyenne (MSE) est utilisée comme mesure de performance :
\[ \text{MSE} = \| \mathbf{y} - \mathbf{X} \mathbf{w} \|_2^2 \] La tâche de régression linéaire consiste à minimisre cette fonction de coût. Nous explorerons deux méthodes principales : la méthode des moindres carrés et la descente de gradient.
La méthode des moindres carrés résout la régression linéaire en trouvant le vecteur de poids \( \mathbf{w} \) qui minimise la MSE. En calculant la dérivée partielle de la MSE par rapport à \( \mathbf{w} \) et en l'égalant à zéro, on obtient une solution analytique.
La fonction de coût peut être développée comme suit :
\[ \text{MSE} = (\mathbf{y} - \mathbf{X} \mathbf{w})^T (\mathbf{y} - \mathbf{X} \mathbf{w}) = \mathbf{y}^T \mathbf{y} - 2 \mathbf{w}^T \mathbf{X}^T \mathbf{y} + \mathbf{w}^T \mathbf{X}^T \mathbf{X} \mathbf{w} \] La dérivée par rapport à \( \mathbf{w} \) est :
\[ \frac{\partial \text{MSE}}{\partial \mathbf{w}} = -2 \mathbf{X}^T \mathbf{y} + 2 \mathbf{X}^T \mathbf{X} \mathbf{w} \] En égalant la dérivée à zéro, on obtient l'équation normale :
\[ \mathbf{X}^T \mathbf{X} \mathbf{w} = \mathbf{X}^T \mathbf{y} \] La solution pour \( \mathbf{w} \) est alors :
\[ \mathbf{w} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T \mathbf{y} \] ### 2.1 Inconvénients de la Méthode des Moindres Carrés
- Complexité du Calcul de l'Inverse : Le calcul de l'inverse de \( \mathbf{X}^T \mathbf{X} \) est coûteux en temps (complexité \( O(D^3) \)) et en espace, surtout pour un grand nombre de caractéristiques \( D \).
- Problème d'Inversibilité : Si \( D > N \), la matrice \( \mathbf{X}^T \mathbf{X} \) peut être singulière (non inversible). Des problèmes numériques peuvent aussi survenir avec des matrices quasi-singulières.
- Sensibilité aux Valeurs Aberrantes : La méthode minimise les erreurs au carré, la rendant sensible aux valeurs extrêmes dans les données.
- Inefficacité pour les Données Sparses : Moins performante sur des matrices de caractéristiques avec beaucoup de zéros.
- Risque de Surapprentissage : Sans régularisation, le modèle peut surapprendre, surtout si \( D \) est grand par rapport à \( N \).
Ces limitations motivent l'utilisation d'autres approches comme la descente de gradient, la régularisation (Ridge, Lasso), etc.
La descente de gradient est une méthode itérative d'optimisation. Elle ajuste progressivement les paramètres du modèle pour minimiser la fonction de coût.
Pour un échantillon unique \( \{ \mathbf{x}_i, y_i \} \), la fonction de coût est :
\[ J(\mathbf{w}) = (y_i - \mathbf{x}_i^T \mathbf{w})^2 \] La mise à jour du poids s'effectue selon la règle :
\[ \mathbf{w} := \mathbf{w} - \lambda \frac{\partial J}{\partial \mathbf{w}} \] où \( \lambda \) est le taux d'apprentissage. La dérivée partielle est :
\[ \frac{\partial J}{\partial \mathbf{w}} = 2 (y_i - \mathbf{x}_i^T \mathbf{w}) (-\mathbf{x}_i) = -2 \mathbf{x}_i (y_i - \mathbf{x}_i^T \mathbf{w}) \] La mise à jour devient :
\[ \mathbf{w} := \mathbf{w} + 2 \lambda \mathbf{x}_i (y_i - \mathbf{x}_i^T \mathbf{w}) \] 4. Implémenattion
4.1 Génération de Données Synthétiques
Les données sont générées en définissant un nombre de caractéristiques et d'échantillons, puis en créant aléatoirement les poids \( \mathbf{w} \), les caractéristiques \( \mathbf{X} \) et enfin les étiquettes \( \mathbf{y} \) en ajoutant un bruit gaussien.
#include <iostream>
#include <vector>
#include <Eigen/Dense>
// Génération de données pour la régression linéaire multiple
namespace MLR {
void generate_data(Eigen::VectorXd& weights, Eigen::MatrixXd& features, Eigen::VectorXd& labels) {
if (weights.rows() != features.cols()) {
throw std::invalid_argument("Dimension mismatch: weights rows must match features columns.");
}
if (features.rows() != labels.rows()) {
throw std::invalid_argument("Dimension mismatch: features rows must match labels rows.");
}
weights.setRandom(); // Initialisation aléatoire des poids
features.setRandom(); // Initialisation aléatoire des caractéristiques
Eigen::VectorXd noise(labels.rows());
noise.setRandom();
noise *= 0.02; // Ajout d'un faible bruit
labels = features * weights + noise; // Calcul des étiquettes
}
}
int main() {
const size_t num_samples = 10;
const size_t num_features = 7;
Eigen::VectorXd true_weights(num_features);
Eigen::MatrixXd feature_matrix(num_samples, num_features);
Eigen::VectorXd target_labels(num_samples);
MLR::generate_data(true_weights, feature_matrix, target_labels);
std::cout << "Target Labels (y):\n" << target_labels << std::endl;
return 0;
}
4.2 Implémentation par Moindres Carrés
Cette fonction calcule les poids en utilisant la formule analytique des moindres carrés.
void least_squares_method(Eigen::VectorXd& estimated_weights, const Eigen::MatrixXd& features, const Eigen::VectorXd& labels) {
if (estimated_weights.rows() != features.cols()) {
throw std::invalid_argument("Dimension mismatch: estimated_weights rows must match features columns.");
}
if (features.rows() != labels.rows()) {
throw std::invalid_argument("Dimension mismatch: features rows must match labels rows.");
}
// Calcul : w = (X^T * X)^(-1) * X^T * y
estimated_weights = (features.transpose() * features).inverse() * features.transpose() * labels;
}
int main() {
// ... (génération des données) ...
Eigen::VectorXd lsm_weights(num_features);
least_squares_method(lsm_weights, feature_matrix, target_labels);
std::cout << "Weight Estimation Error (LSM):\n" << true_weights - lsm_weights << std::endl;
return 0;
}
L'erreur entre les poids réels et estimés par LSM est généralement faible, indiquant une bonne précision.
4.3 Implémentation par Descente de Gradient
Cette fonction implémente l'algorithme de descente de gradient pour estimer les poids.
void gradient_descent_step(Eigen::VectorXd& weights, const Eigen::MatrixXd& features, const Eigen::VectorXd& labels, const double& learning_rate) {
if (weights.rows() != features.cols()) {
throw std::invalid_argument("Dimension mismatch: weights rows must match features columns.");
}
if (features.rows() != labels.rows()) {
throw std::invalid_argument("Dimension mismatch: features rows must match labels rows.");
}
for (size_t i = 0; i < features.rows(); ++i) {
Eigen::RowVectorXd current_feature_row = features.row(i);
// Calcul du gradient pour l'échantillon i
// gradient = 2 * x_i * (y_i - x_i^T * w)
Eigen::VectorXd gradient = 2 * current_feature_row.transpose() * (labels(i) - current_feature_row.dot(weights));
// Mise à jour des poids
weights += learning_rate * gradient;
}
}
int main() {
const size_t num_samples = 100; // Augmentation pour la descente de gradient
const size_t num_features = 7;
Eigen::VectorXd true_weights(num_features);
Eigen::MatrixXd feature_matrix(num_samples, num_features);
Eigen::VectorXd target_labels(num_samples);
MLR::generate_data(true_weights, feature_matrix, target_labels);
Eigen::VectorXd gd_weights(num_features);
gd_weights.setRandom(); // Initialisation aléatoire des poids
double learning_rate = 2e-3;
int num_iterations = 100;
for (int iter = 0; iter < num_iterations; ++iter) {
gradient_descent_step(gd_weights, feature_matrix, target_labels, learning_rate);
}
std::cout << "Weight Estimation Error (GD):\n" << true_weights - gd_weights << std::endl;
return 0;
}
La descente de gradient, bien qu'itérative, parvient également à une estimation précise des poids, à condition que le taux d'apprentissage et le nombre d'itérations soient appropriés.