Avancé Chapitre 19-20 / 10

Le perceptron et la non-linéarité & Introduire la non-linéarité en quantique

Du perceptron classique à la non-linéarité quantique : comprendre pourquoi la mesure est la clé pour dépasser la linéarité unitaire — avec du code Qiskit exécutable sur simulateur.

Le perceptron et la non-linéarité

L’idée en une phrase

Le perceptron calcule une combinaison linéaire z = w·x + b puis applique une fonction d’activation non linéaire σ(z) qui permet au réseau de représenter des frontières de décision courbes — sans cette non-linéarité, empiler des couches revient à une seule transformation linéaire, et le modèle reste incapable de résoudre le XOR (chapitre 15-16). Dans la boucle variationnelle hybride, la question se transpose : où injecter la non-linéarité quand les portes quantiques sont, par construction, des opérations unitaires (donc linéaires sur le vecteur d’état) ?

Analogie : Un perceptron sans activation est un interrupteur à bascule : il ne connaît que les séparations rectilignes. La fonction d’activation est la charnière qui plie la feuille de papier — une fois pliée, des points auparavant inséparables se retrouvent de part et d’autre du pli. Empiler plusieurs couches avec activation, c’est plier la feuille plusieurs fois, jusqu’à découper des régions arbitrairement complexes.

Points clés

  • Le perceptron unique calcule y = σ(Σᵢ wᵢxᵢ + b). Sans σ, la composition de L couches linéaires produit W_L ⋯ W_1 x — une seule matrice. La non-linéarité brise cette réductibilité et donne au réseau sa capacité d’approximation universelle (théorème de Cybenko, 1989).
  • Les activations courantes — ReLU (max(0, z)), sigmoïde (1/(1+e⁻ᶻ)), tanh — sont toutes non linéaires et différentiables (ou sous-différentiables pour ReLU), ce qui permet la rétropropagation du gradient.
  • Le théorème d’approximation universelle garantit qu’un réseau à une couche cachée avec activation non linéaire peut approcher toute fonction continue sur un compact — à condition d’avoir assez de neurones. C’est la justification théorique du deep learning classique.
  • Dans la boucle variationnelle hybride, la partie classique (optimiseur) utilise la rétropropagation standard. La partie quantique (circuit paramétré) doit, elle aussi, introduire une forme de non-linéarité pour que le modèle hybride soit expressif — c’est l’objet du second sous-thème.

Exemple concret

Un perceptron à 2 entrées (x₁, x₂) avec w = (1, 1), b = −1.5 et activation sigmoïde. Pour le point (1, 1) : z = 1 + 1 − 1.5 = 0.5, σ(0.5) ≈ 0.62 → classe +1. Pour (0, 0) : z = −1.5, σ(−1.5) ≈ 0.18 → classe −1. La frontière de décision est la droite x₁ + x₂ = 1.5. Ce perceptron résout le AND, mais pas le XOR : aucune droite ne sépare {(0,0), (1,1)} de {(0,1), (1,0)}. Il faut au moins deux neurones cachés avec activation pour créer deux plis dans l’espace des entrées.

Activation et expressivité

ActivationFormuleDérivéeUsage typique
Sigmoïde1/(1+e⁻ᶻ)σ(1−σ)Sortie probabiliste
Tanh(eᶻ−e⁻ᶻ)/(eᶻ+e⁻ᶻ)1−tanh²Couches cachées (centré)
ReLUmax(0, z)0 ou 1Standard deep learning
Linéairez1Inutile en couche cachée

Code Python — perceptron et XOR

# Un perceptron unique ne résout pas le XOR.
# Deux neurones cachés avec activation le résolvent.
import numpy as np

def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

# Données XOR
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 1, 1, 0])

# Couche cachée : 2 neurones avec poids manuels
W1 = np.array([[1, 1], [1, 1]])   # poids 2×2
b1 = np.array([-0.5, -1.5])       # biais

# Couche de sortie : 1 neurone
W2 = np.array([1, -2])            # poids 1×2
b2 = np.array([-0.5])             # biais

# Propagation avant
h = sigmoid(X @ W1.T + b1)        # 4×2 : couche cachée
out = sigmoid(h @ W2 + b2)        # 4×1 : sortie

preds = (out > 0.5).astype(int)
print("Prédictions :", preds)      # [0, 1, 1, 0] — XOR résolu

Piège courant : « Plus de couches = toujours mieux » est inexact. Le théorème d’approximation universelle porte sur une seule couche cachée (avec assez de neurones). En pratique, la profondeur améliore l’efficacité de la représentation (moins de paramètres pour la même fonction), mais introduit des problèmes de gradient (vanishing, exploding) qui nécessitent des techniques spécifiques (normalisation, connexions résiduelles). L’analogie en quantique : un circuit plus profond est plus expressif, mais plus vulnérable au bruit et aux plateaus de Barren.


Introduire la non-linéarité en quantique

L’idée en une phrase

Les portes quantiques sont des matrices unitaires — elles agissent linéairement sur le vecteur d’état |ψ⟩. La non-linéarité indispensable au machine learning émerge de la mesure : le passage de l’amplitude complexe αᵢ à la probabilité |αᵢ|² est une opération non linéaire irréversible. Dans la boucle variationnelle hybride, c’est donc l’étape de mesure (Sampler ou Estimator, chapitre 13-14) qui joue le rôle de la fonction d’activation.

Analogie : Un orchestre (le circuit) joue un morceau dont chaque note est parfaitement harmonique — linéaire et réversible. Mais l’auditeur (la mesure) ne perçoit que le volume sonore au carré : deux instruments qui interfèrent destructivement produisent un silence que la partition seule ne prédit pas. Cette transduction non linéaire — du signal harmonique au volume perçu — est ce qui permet de discriminer des motifs qu’aucune écoute linéaire ne distinguerait.

Points clés

  • L’évolution d’un circuit est |ψ_out⟩ = U(θ)|ψ_in⟩ — linéaire en |ψ⟩. Empiler des unitaires sans mesure intermédiaire revient à un seul unitaire U_total = U_L ⋯ U_1 : le même problème que les couches linéaires du perceptron.
  • La mesure projette l’état et renvoie des probabilités pᵢ = |⟨i|ψ⟩|². Le carré du module est la source de non-linéarité. L’Estimator renvoie ⟨ψ|O|ψ⟩, qui est quadratique en les amplitudes — également non linéaire.
  • Les mesures intermédiaires (mid-circuit measurements) réinitialisent un qubit en cours de circuit, créant une non-linéarité effective au sein même du calcul quantique. Combinées à du classique conditionnel (if-else sur le résultat de mesure), elles permettent d’empiler des « couches non linéaires quantiques ».
  • Le data re-uploading (prochain chapitre) offre une autre voie : réencoder les données classiques entre chaque couche d’ansatz, interrompant la compositionnalité linéaire des unitaires. Sans mesure intermédiaire, la non-linéarité reste confinée à la mesure finale, mais le re-uploading augmente l’expressivité en variant l’encodage couche par couche.
  • Le post-traitement classique ajoute une couche de non-linéarité supplémentaire : appliquer une sigmoïde ou un softmax sur les valeurs d’attente mesurées est courant dans les classifieurs variationnels hybrides.

Exemple concret

Un circuit à 1 qubit avec un paramètre θ : Ry(θ)|0⟩ = cos(θ/2)|0⟩ + sin(θ/2)|1⟩. La probabilité de mesurer |1⟩ est p₁ = sin²(θ/2) — une fonction non linéaire de θ. Si θ est une combinaison linéaire des données, θ = w·x + b, alors la sortie sin²((w·x + b)/2) est un analogue direct du perceptron avec une activation « sin² ». L’optimiseur classique ajuste w et b par descente de gradient, la mesure quantique fournit la non-linéarité — la boucle hybride est complète.

Sources de non-linéarité en QML

SourceMécanismeQuand elle intervientExemple
Mesure finalepᵢ = |αᵢ|²Fin de circuitSampler / Estimator
Mesure intermédiaireProjection + resetEn cours de circuitMid-circuit measurement
Post-traitement classiqueσ(⟨O⟩)Après mesureSigmoïde sur valeur d’attente
Re-uploadingRéencodage entre couchesEntre unitairesProchain chapitre

Code Python — non-linéarité par la mesure

# La mesure transforme l'amplitude linéaire en probabilité non linéaire.
# Ici : p(|1⟩) = sin²(θ/2), analogue quantique d'une activation.
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

def circuit_1q(theta):
    """Circuit à 1 qubit : Ry(θ)|0⟩."""
    qc = QuantumCircuit(1)
    qc.ry(theta, 0)
    return qc

# Balayage de θ : la sortie est sin²(θ/2), pas θ
thetas = np.linspace(0, 2 * np.pi, 9)
for theta in thetas:
    sv = Statevector(circuit_1q(theta))
    p1 = sv.probabilities()[1]  # P(|1⟩)
    expected = np.sin(theta / 2) ** 2
    print(f"θ = {theta:.2f}  P(|1⟩) = {p1:.4f}  sin²(θ/2) = {expected:.4f}")

Code Python — mesure intermédiaire et non-linéarité en circuit

# Mesure intermédiaire : le qubit est projeté puis réutilisé.
# Cela crée une non-linéarité effective au sein du circuit.
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

def circuit_avec_mesure_intermediaire(theta1, theta2):
    """Circuit avec mesure intermédiaire classiquement conditionnée."""
    qc = QuantumCircuit(1, 1)
    # Première rotation
    qc.ry(theta1, 0)
    # Mesure intermédiaire
    qc.measure(0, 0)
    # Rotation conditionnelle au résultat (non-linéarité)
    with qc.if_test((0, 1)):
        qc.ry(theta2, 0)
    return qc

# Note : Statevector ne supporte pas les mesures intermédiaires.
# Ce circuit nécessite un simulateur avec shots (AerSimulator).
# L'objectif est conceptuel : montrer que la mesure brise la linéarité.
qc = circuit_avec_mesure_intermediaire(1.0, 0.5)
print(qc.draw())

Piège courant : « Les circuits quantiques sont linéaires, donc le QML ne peut rien apprendre de non linéaire » est une confusion entre deux niveaux. L’évolution unitaire est linéaire sur le vecteur d’état (espace de Hilbert), mais la sortie observable — probabilités ou valeurs d’attente — est non linéaire en les paramètres du circuit. C’est précisément cette non-linéarité, issue de la mesure, qui confère au circuit paramétré sa capacité d’approximation. Le théorème d’approximation universelle quantique (Pérez-Salinas et al., 2021) montre qu’un seul qubit avec re-uploading peut approcher toute fonction continue.


Fil rouge — la frontière quantique/classique

La non-linéarité est le point de jonction entre les deux mondes. Le QPU effectue une transformation unitaire (linéaire) sur l’état quantique — c’est là sa puissance : explorer un espace de dimension 2ⁿ en n qubits. Mais sans la mesure, cette transformation reste réversible et linéaire, incapable de classifier. C’est la mesure qui convertit l’information quantique en probabilités classiques non linéaires, exploitables par l’optimiseur. Sur l’axe expressivité ↔ entraînabilité, la non-linéarité joue un rôle double : elle augmente l’expressivité (le modèle peut représenter des frontières courbes), mais complique le paysage d’optimisation — les gradients deviennent des fonctions trigonométriques (règle du parameter-shift, chapitre 13-14) sensibles aux plateaus de Barren pour les circuits profonds.


Quiz — teste tes connaissances
Avancé 7 questions Objectif : 5/7 minimum
0/7
bonnes reponses
Objectif non atteint (minimum 5/7 requis).
Remonte relire la fiche memo en pretant attention aux points manques, puis cliquer sur « Recommencer » pour retenter.

Kata Qiskit — perceptron quantique à 1 qubit

Objectif : construire un « perceptron quantique » à 1 qubit qui utilise Ry(w·x + b) comme couche paramétrique et la mesure comme activation non linéaire, puis vérifier que la sortie P(|1⟩) = sin²((w·x + b)/2) reproduit le comportement d’un perceptron classique avec activation sin².

Squelette :

# kata_day_19_20.py
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

def perceptron_quantique(x, w, b):
    """Perceptron quantique à 1 qubit.
    Construit le circuit Ry(w*x + b)|0⟩ et renvoie P(|1⟩).
    
    Args:
        x: valeur d'entrée (scalaire)
        w: poids (scalaire)
        b: biais (scalaire)
    Returns:
        P(|1⟩) — la « sortie » non linéaire du perceptron quantique.
    """
    # TODO 1 : calculer l'angle theta = w * x + b
    # TODO 2 : construire un QuantumCircuit(1) avec Ry(theta)
    # TODO 3 : obtenir le Statevector et renvoyer P(|1⟩)
    return 0.0

def activation_sin2(x, w, b):
    """Activation théorique : sin²((w*x + b) / 2)."""
    # TODO 4 : calculer et renvoyer sin²((w*x + b) / 2)
    return 0.0

def classifier(x, w, b, seuil=0.5):
    """Classifie x en 0 ou 1 via le perceptron quantique."""
    # TODO 5 : renvoyer 1 si perceptron_quantique(x, w, b) > seuil, sinon 0
    return 0

Auto-correction :

# test_kata_day_19_20.py
import numpy as np
from kata_day_19_20 import perceptron_quantique, activation_sin2, classifier

# Test 1 : P(|1⟩) pour θ = 0 doit être 0
assert abs(perceptron_quantique(0, 0, 0)) < 1e-9, "Ry(0)|0⟩ → P(|1⟩) = 0"

# Test 2 : P(|1⟩) pour θ = π doit être 1
assert abs(perceptron_quantique(0, 0, np.pi) - 1.0) < 1e-9, "Ry(π)|0⟩ → P(|1⟩) = 1"

# Test 3 : cohérence avec la formule analytique sin²(θ/2)
for x_val in [0.0, 0.5, 1.0, 2.0]:
    p_q = perceptron_quantique(x_val, 1.5, 0.3)
    p_a = activation_sin2(x_val, 1.5, 0.3)
    assert abs(p_q - p_a) < 1e-9, f"Incohérence à x={x_val} : {p_q}{p_a}"

# Test 4 : la sortie est dans [0, 1]
for x_val in np.linspace(-3, 3, 20):
    p = perceptron_quantique(x_val, 2.0, -1.0)
    assert 0 - 1e-9 <= p <= 1 + 1e-9, f"P(|1⟩) hors de [0,1] : {p}"

# Test 5 : classification binaire
w, b = 4.0, -0.5
assert classifier(1.0, w, b) == 1, "Doit classifier 1.0 comme 1"
assert classifier(0.0, w, b) == 0, "Doit classifier 0.0 comme 0"

print("Kata validé !")
Solution et explication
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

def perceptron_quantique(x, w, b):
    theta = w * x + b                       # TODO 1
    qc = QuantumCircuit(1)                   # TODO 2
    qc.ry(theta, 0)
    sv = Statevector(qc)                     # TODO 3
    return sv.probabilities()[1]             # P(|1⟩)

def activation_sin2(x, w, b):
    theta = w * x + b
    return np.sin(theta / 2) ** 2            # TODO 4

def classifier(x, w, b, seuil=0.5):
    return 1 if perceptron_quantique(x, w, b) > seuil else 0  # TODO 5

Pourquoi : le circuit Ry(θ)|0⟩ prépare cos(θ/2)|0⟩ + sin(θ/2)|1⟩. La mesure renvoie P(|1⟩) = sin²(θ/2) — une fonction non linéaire du paramètre θ, qui lui-même est une combinaison linéaire de l’entrée x. Le perceptron quantique reproduit exactement le schéma classique σ(w·x + b) avec σ = sin²(·/2) comme activation. La non-linéarité ne vient pas du circuit (unitaire, donc linéaire sur l’état) mais de la mesure (carré du module). C’est le mécanisme fondamental de tout classifieur variationnel hybride.