NTK : Guide Complet — Noyau Tangent Neuronal

NTK : Guide Complet — Noyau Tangent Neuronal

Neural Tangent Kernel : Guide Complet du Noyau Tangent Neuronal

Résumé

Le Neural Tangent Kernel (NTK), ou noyau tangent neuronal, constitue l’une des découvertes théoriques les plus profondes en apprentissage profond moderne. Introduit par Jacot, Gabriel et Hongler en 2018, ce cadre mathématique décrit le comportement des réseaux neuronaux lorsqu’on considère la limite où leur largeur tend vers l’infini. Dans ce régime, l’évolution du réseau pendant l’entraînement par descente de gradient devient entièrement déterminée par un noyau fixe — le NTK — qui ne change pas au cours de l’optimisation. Cette propriété extraordinaire permet d’analyser rigoureusement la convergence et la généralisation des réseaux larges, en les ramenant à des problèmes de régression par noyau classiques, bien compris et mathématiquement traitables. Le NTK a révolutionné notre compréhension de pourquoi les réseaux neuronaux profonds parviennent à converger vers des minima globaux malgré la non-convexité apparente de leur fonction de perte, et pourquoi ils généralisent remarquablement bien sur des données non vues.

Principe mathématique

Le Neural Tangent Kernel décrit le comportement des réseaux neuronaux dans la limite de largeur infinie. Considérons un réseau neuronal paramétré par un vecteur de paramètres $\theta \in \mathbb{R}^P$, qui prend une entrée $x$ et produit une sortie scalaire $f(x; \theta)$. Pour deux points d’entrée $x$ et $x’$, le noyau tangent est défini comme la somme des produits des gradients du réseau par rapport à chacun de ses paramètres :

$$
\Theta(x, x’) = \sum_{p=1}^{P} \frac{\partial f(x; \theta)}{\partial \theta_p} \cdot \frac{\partial f(x’; \theta)}{\partial \theta_p}
$$

De manière équivalente, en notation vectorielle compacte, on peut écrire :

$$
\Theta(x, x’) = \nabla_\theta f(x; \theta)^\top \cdot \nabla_\theta f(x’; \theta)
$$

Ce noyau mesure la similarité entre les gradients du réseau évalués en deux points d’entrée différents. Il encode comment les mises à jour des paramètres affectent simultanément les prédictions sur $x$ et sur $x’$.

La limite de largeur infinie

Dans la limite où la largeur des couches du réseau tend vers l’infini, une propriété remarquable émerge : le NTK converge vers un noyau déterministe qui est indépendant de l’initialisation des paramètres. Autrement dit, quelle que soit la manière dont on initialise le réseau, lorsque sa largeur est suffisamment grande, le NTK se stabilise vers une fonction fixe $\Theta^\infty(x, x’)$ qui ne dépend que de l’architecture du réseau (fonction d’activation, profondeur, largeur) et des données d’entrée.

Cette convergence est un résultat profond. Pour un réseau fini, le NTK évolue pendant l’entraînement car les paramètres $\theta$ changent. Mais dans le régime de largeur infinie, le NTK reste constant — c’est cette propriété de noyau fixe qui rend l’analyse mathématique possible.

Évolution pendant l’entraînement

Considérons l’entraînement par descente de gradient avec un taux d’apprentissage $\eta$. L’évolution des paramètres suit l’équation différentielle :

$$
\frac{d\theta}{dt} = -\nabla_\theta \mathcal{L}(\theta)
$$

où $\mathcal{L}$ est la fonction de perte. Par composition, l’évolution de la sortie du réseau $f(x; \theta(t))$ pendant l’entraînement est :

$$
\frac{df(x; \theta(t))}{dt} = \nabla_\theta f(x; \theta(t))^\top \cdot \frac{d\theta}{dt} = -\nabla_\theta f(x; \theta(t))^\top \cdot \nabla_\theta \mathcal{L}(\theta(t))
$$

Pour une perte quadratique $\mathcal{L} = \frac{1}{2} \sum_{i} (f(x_i; \theta) – y_i)^2$ sur un ensemble d’entraînement ${(x_i, y_i)}_{i=1}^n$, on obtient :

$$
\frac{df(x; \theta(t))}{dt} = -\sum_{i=1}^{n} \Theta(x, x_i; \theta(t)) \cdot (f(x_i; \theta(t)) – y_i)
$$

Cette équation révèle la structure fondamentale du régime NTK : l’évolution du réseau est entièrement gouvernée par le noyau $\Theta$ appliqué aux résidus d’entraînement. Dans la limite de largeur infinie où $\Theta$ devient constant, cette équation différentielle linéaire se résout analytiquement — le problème d’entraînement non convexe d’un réseau neuronal se ramène à une régression par noyau avec le NTK.

Convergence et positifité du noyau

Le NTK explique pourquoi les réseaux larges convergent vers des minima globaux. Si la matrice du noyau $\Theta$ (évaluée sur l’ensemble d’entraînement) est définie positive, alors l’équation différentielle ci-dessus garantit une convergence exponentielle vers l’erreur nulle sur les données d’entraînement. Concrètement, la plus petite valeur propre $\lambda_{\min}$ de la matrice NTK détermine le taux de convergence : plus $\lambda_{\min}$ est grand, plus la convergence est rapide.

Pour des activations non-polynomiales usuelles (ReLU, sigmoïde, tanh), et sous des conditions assez générales sur les données, le NTK dans la limite de largeur infinie est strictement défini positif. Cela signifie que pour tout ensemble fini de points distincts, la matrice correspondante est inversible et possède des valeurs propres strictement positives. Cette propriété assure que la descente de gradient converge exponentiellement vers une solution qui interpole parfaitement les données d’entraînement.

Régime de noyau et généralisation

Le NTK explique également pourquoi les réseaux larges généralisent bien. Dans le régime de noyau, l’entraînement équivaut à une régression par noyau ridge. La solution obtenue minimise une norme de fonction associée au noyau NTK, ce qui correspond à une forme de régularisation implicite. Plus précisément, parmi toutes les fonctions qui annulent l’erreur d’entraînement, le réseau converge vers celle qui minimise la norme RKHS (Reproducing Kernel Hilbert Space) associée au NTK. Cette propriété de régularisation implicite explique pourquoi les réseaux sur-paramétrés ne sur-ajustent pas nécessairement : ils trouvent automatiquement la solution la plus « lisse » compatible avec les données.

Intuition

Imaginez un réseau neuronal comme un mécanisme extrêmement complexe composé de millions de leviers — chaque levier représentant un paramètre ajustable (un poids ou un biais). Quand vous avez un très petit nombre de leviers (un réseau étroit), chaque mouvement individuel a un impact énorme et imprévisible sur le comportement global du système. C’est comme essayer de piloter une petite machine fragile : le moindre ajustement peut tout déstabiliser, le mouvement est saccadé, erratique, et difficilement prévisible.

Maintenant, imaginez que ce même mécanisme possède des millions, voire des milliards de leviers. Changer un seul levier n’a plus qu’un effet microscopique, infinitésimal sur le comportement global. Le système devient étonnamment prévisible et lisse. C’est comme comparer une petite moto à un paquebot : la petite moto est nerveuse, réagit violemment à chaque geste, tandis que le paquebot avance avec une régularité majestueuse, chaque correction de trajectoire étant douce et imperceptible. C’est exactement ce qui se passe dans le régime NTK : le mécanisme neuronal tourne rondement, sans à-coups, comme une grande machine parfaitement huilée.

Dans ce régime, le réseau se comporte comme un modèle linéaire dans un espace de fonction de très haute dimension. Le NTK est l’analogue de la matrice de covariance dans ce modèle linéaire. Les prédictions du réseau sur les données d’entraînement évoluent selon une équation différentielle linéaire simple, et la solution est entièrement déterminée par ce noyau fixe. Cette linéarité émergente à grande échelle est ce qui rend l’analyse mathématique possible et explique les propriétés de convergence et de généralisation des réseaux larges.

Implémentation Python

Voici une implémentation complète illustrant le calcul analytique du NTK pour un MLP à une seule couche cachée, la comparaison entre le régime NTK et l’entraînement neuronal réel, et la visualisation de la convergence du NTK en fonction de la largeur croissante du réseau.

"""
Neural Tangent Kernel (NTK) — Implémentation analytique et empirique
===================================================================

Ce script calcule le NTK pour un MLP à une couche cachée, compare le
régime NTK avec un véritable entraînement neuronal, et visualise la
convergence du noyau lorsque la largeur de la couche augmente.
"""

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import warnings
warnings.filterwarnings("ignore")


# ──────────────────────────────────────────────
# 1. Définition du réseau MLP une couche cachée
# ──────────────────────────────────────────────
def relu(x):
    """Fonction d'activation ReLU : max(0, x)"""
    return np.maximum(0, x)


def relu_derivative(x):
    """Dérivée de la ReLU : 1 si x > 0, 0 sinon."""
    return (x > 0).astype(np.float64)


class OneLayerMLP:
    """MLP à une couche cachée : f(x) = W2 · ReLU(W1 · x + b1)"""

    def __init__(self, input_dim, hidden_dim, output_dim=1, seed=42):
        np.random.seed(seed)
        # Initialisation He pour la première couche
        self.W1 = np.random.randn(hidden_dim, input_dim) * np.sqrt(2.0 / input_dim)
        self.b1 = np.zeros(hidden_dim)
        # La deuxième couche est scalairement normalisée en fonction de la largeur
        self.W2 = np.random.randn(output_dim, hidden_dim) * np.sqrt(1.0 / hidden_dim)

    def forward(self, X):
        """Propagation avant. Renvoie (sortie, pré-activations)."""
        pre_act1 = X @ self.W1.T + self.b1  # (N, H)
        act1 = relu(pre_act1)                 # (N, H)
        output = act1 @ self.W2.T             # (N, O)
        return output, pre_act1, act1


# ──────────────────────────────────────────────
# 2. Calcul empirique du NTK
# ──────────────────────────────────────────────
def empirical_ntk(model, X1, X2):
    """
    Calcule le NTK empirique entre deux ensembles X1 et X2.

    Le NTK est la somme des produits des gradients :
    Theta(x, x') = Σ_p (∂f(x)/∂θ_p) · (∂f(x')/∂θ_p)

    Utilise le calcul analytique des gradients pour
    un réseau de taille modérée.
    """
    _, pre_act1_X1, act1_X1 = model.forward(X1)
    _, pre_act1_X2, act1_X2 = model.forward(X2)

    # Contribution de W2 : Θ_W2 = act1_X1 @ act1_X2^T / hidden_dim
    ntk_W2 = act1_X1 @ act1_X2.T / model.W2.shape[1]

    # Gradients de ReLU
    mask_X1 = relu_derivative(pre_act1_X1)  # (N1, H)
    mask_X2 = relu_derivative(pre_act1_X2)  # (N2, H)

    # Contribution W1
    W2_sq = (model.W2[0] ** 2)  # (H,)
    weighted_mask_X1 = mask_X1 * W2_sq[np.newaxis, :]  # (N1, H)
    weighted_mask_X2 = mask_X2 * W2_sq[np.newaxis, :]  # (N2, H)
    ntk_W1 = (weighted_mask_X1 @ weighted_mask_X2.T) * (X1 @ X2.T) / model.W1.shape[1]

    # Contribution b1 : similaire sans le terme x·x'
    ntk_b1 = weighted_mask_X1 @ weighted_mask_X2.T / model.W1.shape[1]

    ntk_matrix = ntk_W2 + ntk_W1 + ntk_b1
    return ntk_matrix


# ──────────────────────────────────────────────
# 3. NTK théorique dans la limite de largeur infinie
# ──────────────────────────────────────────────
def theoretical_ntk_relu(x1, x2):
    """
    Calcule le NTK théorique pour une activation ReLU
    dans la limite de largeur infinie (choix de noyau ARC-COS d'ordre 0 et 1).

    Pour un MLP à une couche, le NTK limite est :
    Σ^(∞)(x, x') = (1/π) · ||x|| · ||x'|| · (sin(θ) + (π - θ)cos(θ))
    où θ = arccos( (x·x') / (||x|| · ||x'||) )

    Références : Cho & Saul (2009), Jacot et al. (2018)
    """
    dot_product = np.dot(x1, x2)
    norm1 = np.linalg.norm(x1)
    norm2 = np.linalg.norm(x2)

    if norm1 < 1e-10 or norm2 < 1e-10:
        return 0.0

    # Angle normalisé entre les vecteurs
    cos_angle = np.clip(dot_product / (norm1 * norm2), -1.0, 1.0)
    theta = np.arccos(cos_angle)

    # Noyau ARC-COS d'ordre 1 (dérivée de la ReLU)
    k_dot = (1.0 / np.pi) * norm1 * norm2 * (np.sin(theta) + (np.pi - theta) * cos_angle)

    # Forme exacte du NTK théorique pour ReLU :
    ntk_value = dot_product * (1.0 - theta / np.pi) + k_dot

    return ntk_value


def theoretical_ntk_matrix(X1, X2):
    """Calcule la matrice NTK théorique pour tous les couples de points."""
    n1, n2 = X1.shape[0], X2.shape[0]
    result = np.zeros((n1, n2))
    for i in range(n1):
        for j in range(n2):
            result[i, j] = theoretical_ntk_relu(X1[i], X2[j])
    return result


# ──────────────────────────────────────────────
# 4. Entraînement par gradient descent
# ──────────────────────────────────────────────
def train_model(model, X, y, learning_rate=0.01, epochs=500):
    """Entraîne le modèle par descente de gradient sur la perte quadratique."""
    losses = []
    for epoch in range(epochs):
        output, pre_act1, act1 = model.forward(X)

        # Perte quadratique moyenne
        loss = 0.5 * np.mean((output.flatten() - y) ** 2)
        losses.append(loss)

        # Gradients
        residual = (output.flatten() - y)  # (N,)

        # Gradient par rapport à W2
        grad_W2 = np.outer(residual, act1) / len(y)  # (O, H)

        # Gradient par rapport à la couche cachée
        grad_hidden = np.outer(residual, model.W2.flatten()) * relu_derivative(pre_act1)

        # Gradients W1 et b1
        grad_W1 = grad_hidden.T @ X / len(y)  # (H, D)
        grad_b1 = np.mean(grad_hidden, axis=0)  # (H,)

        # Mise à jour des paramètres
        model.W2 -= learning_rate * grad_W2
        model.W1 -= learning_rate * grad_W1
        model.b1 -= learning_rate * grad_b1

    return losses


# ──────────────────────────────────────────────
# 5. Kernel Ridge Regression avec le NTK
# ──────────────────────────────────────────────
def ntk_ridge_regression(ntk_train, y_train, ntk_test, alpha=1e-4):
    """
    Régression ridge avec le noyau NTK.

    f_test = K_test · (K_train + αI)^{-1} · y_train
    """
    n = ntk_train.shape[0]
    # Résolution du système linéaire
    K_reg = ntk_train + alpha * np.eye(n)
    coeffs = np.linalg.solve(K_reg, y_train)
    predictions = ntk_test @ coeffs
    return predictions, coeffs


# ──────────────────────────────────────────────
# 6. Expérience et visualisation
# ──────────────────────────────────────────────
input_dim = 3
n_train = 50
n_test = 30

# Génération de données synthétiques
np.random.seed(2024)
X_train = np.random.randn(n_train, input_dim)
X_test = np.random.randn(n_test, input_dim)

# Fonction cible non linéaire
y_train = np.sin(X_train[:, 0]) + 0.5 * X_train[:, 1] ** 2 + 0.3 * X_train[:, 2]
y_test = np.sin(X_test[:, 0]) + 0.5 * X_test[:, 1] ** 2 + 0.3 * X_test[:, 2]

# Normalisation des entrées et cibles
X_train_mean = X_train.mean(axis=0)
X_train_std = X_train.std(axis=0)
X_train_norm = (X_train - X_train_mean) / (X_train_std + 1e-8)
X_test_norm = (X_test - X_train_mean) / (X_train_std + 1e-8)

y_mean = y_train.mean()
y_std = y_train.std()
y_train_norm = (y_train - y_mean) / (y_std + 1e-8)
y_test_norm = (y_test - y_mean) / (y_std + 1e-8)

# ── Expérience 1 : Convergence du NTK en fonction de la largeur ──
print("=" * 60)
print("Expérience 1 : Convergence du NTK vs largeur croissante")
print("=" * 60)

widths = [10, 30, 100, 300, 1000, 3000]
ntk_errors = []

# Référence : NTK théorique (infini)
ntk_theory_train = theoretical_ntk_matrix(X_train_norm, X_train_norm)

for w in widths:
    model_w = OneLayerMLP(input_dim, w, seed=123)
    ntk_empirical = empirical_ntk(model_w, X_train_norm, X_train_norm)

    # Erreur relative par rapport au NTK théorique
    relative_error = np.linalg.norm(ntk_empirical - ntk_theory_train) / (
        np.linalg.norm(ntk_theory_train) + 1e-10
    )
    ntk_errors.append(relative_error)
    print(f"  Largeur = {w:>5d} | Erreur relative du NTK = {relative_error:.6f}")

# ── Expérience 2 : Comparaison NTK-KRR vs entraînement neuronal ──
print("\n" + "=" * 60)
print("Expérience 2 : NTK-KRR vs entraînement neuronal")
print("=" * 60)

# Modèle large pour se rapprocher du régime NTK
large_width = 500
model_large = OneLayerMLP(input_dim, large_width, seed=42)

# NTK empirique sur les données d'entraînement et de test
ntk_train_emp = empirical_ntk(model_large, X_train_norm, X_train_norm)
ntk_test_emp = empirical_ntk(model_large, X_test_norm, X_train_norm)

# Prédiction par régression de noyau NTK
ntk_pred_test, ntk_coeffs = ntk_ridge_regression(
    ntk_train_emp, y_train_norm, ntk_test_emp, alpha=1e-3
)
ntk_test_mse = np.mean((ntk_pred_test - y_test_norm) ** 2)
print(f"  NTK-KRR (régression ridge) | MSE test = {ntk_test_mse:.6f}")

# Entraînement neuronal réel
model_trained = OneLayerMLP(input_dim, large_width, seed=42)
train_losses = train_model(model_trained, X_train_norm, y_train_norm,
                           learning_rate=0.05, epochs=1000)
train_output, _, _ = model_trained.forward(X_test_norm)
neural_test_mse = np.mean((train_output.flatten() - y_test_norm) ** 2)
print(f"  Entraînement neuronal | MSE test = {neural_test_mse:.6f}")
print(f"  Écart relatif = {abs(ntk_test_mse - neural_test_mse) / ntk_test_mse * 100:.2f}%")

# ── Expérience 3 : Convergence de la perte pendant l'entraînement ──
print("\n" + "=" * 60)
print("Expérience 3 : Convergence exponentielle de la perte")
print("=" * 60)

# Plus petite valeur propre du NTK (mesure indirecte du taux de convergence)
eigenvalues = np.linalg.eigvalsh(ntk_train_emp)
lambda_min = np.min(eigenvalues)
lambda_max = np.max(eigenvalues)
print(f"  λ_min(NTK) = {lambda_min:.6f}")
print(f"  λ_max(NTK) = {lambda_max:.6f}")
print(f"  Conditionnement = {lambda_max / (lambda_min + 1e-10):.2f}")


# ── Visualisation ──
print("\nGénération des visualisations...")

fig = plt.figure(figsize=(16, 12))
gs = GridSpec(2, 2, figure=fig, hspace=0.35, wspace=0.30)

# Graphique 1 : Convergence du NTK vs largeur
ax1 = fig.add_subplot(gs[0, 0])
ax1.loglog(widths, ntk_errors, 'bo-', linewidth=2.5, markersize=10, label='Erreur relative')
ax1.set_xlabel('Largeur de la couche cachée', fontsize=12, fontweight='bold')
ax1.set_ylabel('Erreur relative du NTK (log)', fontsize=12, fontweight='bold')
ax1.set_title('Convergence du NTK empirique vers le NTK théorique\nlorsque la largeur augmente', fontsize=11, fontweight='bold')
ax1.legend(fontsize=11)
ax1.grid(True, alpha=0.3)

# Annotation : zone du régime NTK
ax1.axvspan(500, 3500, alpha=0.15, color='green', label='Régime NTK (convergence)')
ax1.fill_between([500, 3500], [1e-5, 1e-5], [1e-1, 1e-1], alpha=0.15, color='green')
ax1.legend(fontsize=10)

# Graphique 2 : Comparaison des prédictions NTK-KRR vs neuronal
ax2 = fig.add_subplot(gs[0, 1])
idx_sorted = np.argsort(y_test_norm)
ax2.scatter(y_test_norm[idx_sorted], ntk_pred_test[idx_sorted],
            c='blue', s=50, alpha=0.8, label='NTK-KRR', edgecolors='darkblue', linewidth=0.5)
ax2.scatter(y_test_norm[idx_sorted], train_output.flatten()[idx_sorted],
            c='red', s=50, alpha=0.8, label='Entraînement neuronal',
            edgecolors='darkred', linewidth=0.5)
# Ligne de référence y = x
lim = max(abs(y_test_norm).max(), abs(ntk_pred_test).max(), abs(train_output.flatten()).max())
ax2.plot([-lim, lim], [-lim, lim], 'k--', alpha=0.4, linewidth=1.5, label='Référence (y=x)')
ax2.set_xlabel('Valeurs réelles normalisées', fontsize=12, fontweight='bold')
ax2.set_ylabel('Prédictions normalisées', fontsize=12, fontweight='bold')
ax2.set_title('Comparaison NTK-KRR vs entraînement neuronal', fontsize=11, fontweight='bold')
ax2.legend(fontsize=10)
ax2.grid(True, alpha=0.3)

# Graphique 3 : Évolution de la perte pendant l'entraînement
ax3 = fig.add_subplot(gs[1, 0])
epochs_axis = np.arange(len(train_losses))
ax3.semilogy(epochs_axis, train_losses, 'g-', linewidth=2, alpha=0.9)
ax3.set_xlabel('Époques', fontsize=12, fontweight='bold')
ax3.set_ylabel('Perte quadratique (échelle log)', fontsize=12, fontweight='bold')
ax3.set_title("Convergence exponentielle de la perte\nsur données d'entraînement", fontsize=11, fontweight='bold')
ax3.grid(True, alpha=0.3)

# Annotation du taux de convergence
ax3.annotate(f'\u03BB_min = {lambda_min:.4f}',
             xy=(0.7, 0.15), xycoords='axes fraction',
             fontsize=10, fontweight='bold',
             bbox=dict(boxstyle='round', facecolor='lightgreen', alpha=0.7))

# Graphique 4 : Matrice du NTK
ax4 = fig.add_subplot(gs[1, 1])
im = ax4.imshow(ntk_train_emp, cmap='viridis', aspect='equal')
ax4.set_xlabel('Index des données x\'', fontsize=12, fontweight='bold')
ax4.set_ylabel('Index des données x', fontsize=12, fontweight='bold')
ax4.set_title(f'Matrice du NTK empirique\n(largeur={large_width})', fontsize=11, fontweight='bold')
fig.colorbar(im, ax=ax4, label='Valeur du noyau')

# Titre global
fig.suptitle('Neural Tangent Kernel (NTK) — Analyse complète',
             fontsize=15, fontweight='bold', y=0.98)

plt.savefig('ntk_analysis.png', dpi=200, bbox_inches='tight',
           facecolor='white', edgecolor='none')
print("Sauvegarde : ntk_analysis.png")

# ── Résumé des résultats ──
print("\n" + "#" * 60)
print("RÉSUMÉ DES RÉSULTATS")
print("#" * 60)
print(f"  Dimensions : {input_dim} entrées, {n_train} points train, {n_test} points test")
print(f"  Largeur maximale testée : {large_width}")
print(f"  NTK-KRR MSE test   : {ntk_test_mse:.6f}")
print(f"  Neuronal MSE test   : {neural_test_mse:.6f}")
print(f"  λ_min(NTK)          : {lambda_min:.6f}")
print(f"  Taux convergence min: {2 * lambda_min:.6f}")
print("#" * 60)
print("\nLe NTK capture fidèlement le comportement de l'entraînement neuronal")

Ce code démontre concrètement les trois aspects fondamentaux du NTK : la convergence empirique vers le noyau théorique lorsque la largeur augmente, l’équivalence entre la régression par noyau NTK et l’entraînement neuronal réel pour des réseaux suffisamment larges, et la convergence exponentielle de la perte pendant l’entraînement — confirmant la prédiction théorique du régime NTK.

Hyperparamètres

Trois hyperparamètres jouent un rôle central dans l’analyse et l’exploitation pratique du Neural Tangent Kernel.

layer_width — Largeur de la couche cachée

C’est l’hyperparamètre le plus important pour le régime NTK. Plus la largeur est grande, plus le NTK empirique se rapproche du noyau théorique dans la limite infinie. En pratique, une largeur de 500 à 3000 neurones suffit pour observer le régime NTK sur des problèmes synthétiques de petite dimension. Pour des problèmes plus complexes, la largeur nécessaire augmente, et le régime NTK peut devenir difficile à atteindre dans la pratique. La relation entre la largeur et l’erreur d’approximation du NTK suit typiquement une loi en $O(1/\sqrt{\text{largeur}})$.

Valeurs recommandées :
– Petits problèmes (d ≤ 10) : 300–1000
– Problèmes moyens (d ≤ 100) : 1000–5000
– Régime NTK strict : > 5000

num_data_points — Nombre de points de données

Le nombre de points d’entraînement affecte la qualité de l’estimation du NTK et la solvabilité du système linéaire dans la régression par noyau. La complexité de la régression NTK-KRR est en $O(n^3)$ pour l’inversion de matrice, où $n$ est le nombre de points d’entraînement. Cela limite l’applicabilité pratique à quelques milliers de points. Au-delà, des approximations (Nyström, Random Fourier Features) deviennent nécessaires.

Valeurs recommandées :
– Analyse théorique : 50–500 points
– Régression NTK pratique : 500–5000 points
– Approximations nécessaires : > 10000 points

regularization_strength — Force de régularisation

Dans la régression ridge avec le NTK, le paramètre de régularisation $\alpha$ contrôle le compromis biais-variance. Une régularisation trop faible conduit à un sur-ajustement aux bruits d’entraînement, tandis qu’une régularisation trop forte sous-ajuste la fonction cible. Le choix optimal dépend du rapport signal-bruit des données et de la complexité intrinsèque de la fonction à apprendre.

Valeurs recommandées :
– Données peu bruitées : $10^{-5}$ à $10^{-3}$
– Données modérément bruitées : $10^{-3}$ à $10^{-1}$
– Données très bruitées : $10^{-1}$ à $10^{0}$

Avantages et Limites

Avantages

La théorie du Neural Tangent Kernel offre plusieurs avantages majeurs par rapport aux approches traditionnelles d’analyse des réseaux neuronaux :

Compréhension théorique rigoureuse. Le NTK fournit pour la première fois un cadre mathématique complet pour analyser la convergence des réseaux neuronaux. Là où l’optimisation non convexe était auparavant un domaine d’incertitude (personne ne pouvait garantir qu’un réseau atteindrait un minimum global), le NTK établit des preuves formelles de convergence exponentielle pour les réseaux larges.

Pont entre apprentissage profond et méthodes à noyaux. Le NTK crée un lien profond entre deux domaines qui semblaient distincts : l’apprentissage profond (empirique mais performant) et les méthodes à noyaux (théoriquement élégantes mais parfois limitées en pratique). Cette connexion permet de transférer des résultats théoriques d’un domaine à l’autre.

Explication de la généralisation. Le NTK éclaire le paradoxe de la sur-paramétrisation : comment des réseaux avec des millions de paramètres peuvent-ils généraliser sans sur-ajuster ? La réponse réside dans la régularisation implicite du régime de noyau — le réseau converge vers la solution de norme RKHS minimale, qui possède de bonnes propriétés de généralisation.

Diagnostic de l’entraînement. L’analyse spectrale du NTK (valeurs propres, conditionnement) fournit des indicateurs précieux pour diagnostiquer les difficultés d’entraînement. Un conditionnement élevé signale des problèmes potentiels de convergence, tandis qu’un petit $\lambda_{\min}$ prédit un entraînement lent.

Limites

Malgré ses avancées considérables, le régime NTK présente plusieurs limitations importantes qu’il convient de reconnaître :

L’écart de largeur infinie. Le résultat de convergence du NTK s’applique strictement uniquement dans la limite où la largeur tend vers l’infini. Les réseaux pratiques sont finis, et le NTK peut varier significativement pendant l’entraînement pour des réseaux de taille réaliste. Cet écart entre la théorie asymptotique et la pratique pose des questions sur la pertinence quantitative du régime NTK pour les architectures courantes.

Performances prédictives inférieures. Les prédictions dans le régime NTK sont généralement moins performantes que celles des réseaux neuronaux réels entraînés en pratique. Cela suggère que les réseaux de taille finie exploitent des mécanismes d’apprentissage qui dépassent le cadre purement linéaire du NTK — notamment une évolution des caractéristiques (feature learning) que le régime de noyau ne capture pas, puisque le noyau y est fixe.

Coût computationnel prohibitif. Le calcul du NTK nécessite de construire et d’inverser une matrice de taille $n \times n$, où $n$ est le nombre de points d’entraînement. La complexité cubique $O(n^3)$ rend cette approche impraticable au-delà de quelques milliers de points. Même pour des tailles modérées, le coût mémoire de la matrice NTK ($O(n^2)$) peut devenir un goulot d’étranglement.

Réseaux profonds et NTK dégénéré. Pour les réseaux très profonds avec des activations ReLU, le NTK peut devenir dégénéré : les fonctions propres du noyau se concentrent sur des directions de basse fréquence, rendant le noyau incapable de capturer des variations complexes dans les données. C’est le problème bien connu de la dégénérescence spectrale du NTK dans les réseaux profonds, qui limite l’applicabilité de la théorie aux architectures de type ResNet ou aux réseaux très profonds avec connexions résiduelles.

Incapacité à capturer le feature learning. Le régime NTK suppose un noyau fixe pendant l’entraînement. Or, les réseaux neuronaux pratiques apprennent activement de nouvelles représentations des données : leurs caractéristiques internes évoluent de manière significative. Cette évolution des représentations, essentielle à la capacité des réseaux profonds à extraire des abstractions hiérarchiques, est absente du modèle NTK pur. Des cadres alternatifs comme le Mean Field Theory ou le Rich Regime tentent de combler cette lacune en capturant la dynamique d’apprentissage des caractéristiques.

Sensibilité aux données. Le NTK dépend fortement de la distribution des données d’entrée. Pour des données dont la structure est complexe (images naturelles, texte, séries temporelles), la matrice NTK peut être mal conditionnée, rendant la régression par noyau instable et peu fiable.

4 cas d’usage concrets

1. Vérification formelle de la convergence d’architectures nouvelles

Lorsqu’un chercheur conçoit une nouvelle architecture de réseau neuronal (par exemple, une variante de l’attention ou un bloc résiduel modifié), le NTK offre un outil de vérification théorique. En calculant le NTK de la nouvelle architecture dans la limite de largeur infinie, on peut prédire si la descente de gradient convergera vers un minimum global, condition nécessaire mais non suffisante pour que l’architecture soit viable. Si le NTK n’est pas défini positif pour l’architecture proposée, cela signale un problème fondamental dans la conception.

Des travaux récents ont utilisé le NTK pour analyser des variantes de réseaux résiduels, des architectures à attention éparse, et des réseaux avec normalisation par lots. Dans chaque cas, le calcul du NTK a révélé des propriétés de convergence que l’expérimentation seule n’aurait pas pu établir avec certitude.

2. Initialisation optimale des réseaux profonds

Le NTK fournit des critères mathématiquement fondés pour choisir les échelles d’initialisation des poids. Pour un réseau ReLU à $L$ couches, la condition d’isométrie du NTK à l’initialisation impose une échelle de variance spécifique pour chaque couche, ce qui coïncide précisément avec l’initialisation de He ($\sqrt{2/n_{\text{entrées}}}$) pour les activations ReLU. Cette perspective théorique justifie des choix d’initialisation qui étaient auparavant purement empiriques.

Pour des activations différentes de ReLU (comme GELU, Swish ou Mish), le calcul du NTK permet de dériver des conditions d’initialisation optimales spécifiques à chaque fonction d’activation, garantissant que le réseau opère dans un régime favorable à la convergence dès le début de l’entraînement.

3. Sélection de modèles et comparaison d’architectures

Puisque le NTK est spécifique à l’architecture (profondeur, largeur, fonction d’activation), on peut comparer différents choix architecturaux en évaluant les propriétés spectrales de leur NTK respectif. Une architecture dont le NTK possède un spectre plus favorable (plus grande valeur propre minimale, meilleur conditionnement) devrait théoriquement converger plus rapidement et généraliser mieux.

Cette approche permet de faire un pré-filtrage d’architectures sans entraînement coûteux : on calcule le NTK théorique pour chaque candidate et on retient celles dont les propriétés spectrales sont les plus prometteuses. C’est particulièrement utile dans la recherche d’architecture neuronale (Neural Architecture Search, NAS), où l’espace des architectures candidates est vaste et l’entraînement complet de chaque candidate prohibitif.

4. Compréhension de la dynamique d’apprentissage dans les modèles de vision et de langage

Le NTK a été étudié dans le contexte des réseaux de convolution pour la vision (CNN) et, plus récemment, des transformeurs pour le traitement du langage naturel. Dans les CNN, le NTK de convolution révèle comment l’invariance par translation et la localité spatiale sont codées dans le noyau. Dans les transformeurs, l’analyse du NTK éclaire le rôle de l’attention multi-têtes et la façon dont les tokens interagissent pendant l’entraînement.

Ces analyses ont mené à des améliorations concrètes : par exemple, la compréhension du NTK des CNN a conduit à de meilleures stratégies de normalisation et à des schémas d’initialisation spécifiques aux convolutions. Pour les transformeurs, l’analyse NTK a révélé l’importance critique de l’échelle des poids d’attention, inspirant des modifications architecturales comme la µ-parameterization, qui stabilise l’entraînement pour des modèles extrêmement larges.

Voir aussi

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.