décembre 21, 2025
15 min de lecture

Questions d'entretien pour un chercheur en IA : Guide complet

interview
career-advice
job-search
Questions d'entretien pour un chercheur en IA : Guide complet
MB

Milad Bonakdar

Auteur

Maîtrisez les fondamentaux de la recherche en IA avec des questions d'entretien essentielles couvrant la théorie de l'apprentissage profond, la méthodologie de recherche, les architectures de transformateurs, l'optimisation et les sujets d'IA de pointe pour les chercheurs.


Introduction

Les chercheurs en intelligence artificielle repoussent les limites de l'intelligence artificielle grâce à de nouveaux algorithmes, architectures et méthodologies. Ce rôle exige une connaissance théorique approfondie, de solides bases mathématiques, une expérience en recherche et la capacité de formuler et de résoudre des problèmes ouverts.

Ce guide complet couvre les questions d'entretien essentielles pour les chercheurs en intelligence artificielle, allant de la théorie de l'apprentissage profond aux architectures de transformateurs, en passant par les techniques d'optimisation, la méthodologie de recherche, la vision par ordinateur, le NLP et les sujets d'IA de pointe. Chaque question comprend des réponses détaillées, une évaluation de la rareté et des niveaux de difficulté.


Théorie de l'apprentissage profond (5 questions)

1. Expliquez en détail la rétropropagation et la règle de la chaîne.

Réponse : La rétropropagation calcule efficacement les gradients en utilisant la règle de la chaîne.

  • Règle de la chaîne : Pour les fonctions composites, la dérivée est le produit des dérivées
  • Passe avant : Calculer les sorties et mettre en cache les valeurs intermédiaires
  • Passe arrière : Calculer les gradients de la sortie à l'entrée
import numpy as np

# Réseau neuronal simple pour démontrer la rétropropagation
class SimpleNN:
    def __init__(self, taille_entree, taille_cachee, taille_sortie):
        # Initialiser les poids
        self.W1 = np.random.randn(taille_entree, taille_cachee) * 0.01
        self.b1 = np.zeros((1, taille_cachee))
        self.W2 = np.random.randn(taille_cachee, taille_sortie) * 0.01
        self.b2 = np.zeros((1, taille_sortie))
    
    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))
    
    def sigmoid_derivative(self, x):
        return x * (1 - x)
    
    def forward(self, X):
        # Couche 1
        self.z1 = np.dot(X, self.W1) + self.b1
        self.a1 = self.sigmoid(self.z1)
        
        # Couche 2
        self.z2 = np.dot(self.a1, self.W2) + self.b2
        self.a2 = self.sigmoid(self.z2)
        
        return self.a2
    
    def backward(self, X, y, output, learning_rate=0.01):
        m = X.shape[0]
        
        # Gradients de la couche de sortie
        # dL/da2 = a2 - y (pour l'entropie croisée binaire)
        # dL/dz2 = dL/da2 * da2/dz2 = (a2 - y) * sigmoid'(z2)
        dz2 = output - y
        dW2 = (1/m) * np.dot(self.a1.T, dz2)
        db2 = (1/m) * np.sum(dz2, axis=0, keepdims=True)
        
        # Gradients de la couche cachée (règle de la chaîne)
        # dL/da1 = dL/dz2 * dz2/da1 = dz2 * W2.T
        # dL/dz1 = dL/da1 * da1/dz1 = dL/da1 * sigmoid'(z1)
        da1 = np.dot(dz2, self.W2.T)
        dz1 = da1 * self.sigmoid_derivative(self.a1)
        dW1 = (1/m) * np.dot(X.T, dz1)
        db1 = (1/m) * np.sum(dz1, axis=0, keepdims=True)
        
        # Mettre à jour les poids
        self.W2 -= learning_rate * dW2
        self.b2 -= learning_rate * db2
        self.W1 -= learning_rate * dW1
        self.b1 -= learning_rate * db1
    
    def train(self, X, y, epochs=1000):
        for epoch in range(epochs):
            # Passe avant
            output = self.forward(X)
            
            # Passe arrière
            self.backward(X, y, output)
            
            if epoch % 100 == 0:
                loss = -np.mean(y * np.log(output) + (1-y) * np.log(1-output))
                print(f'Epoch {epoch}, Perte : {loss:.4f}')

# Exemple d'utilisation
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])  # XOR

nn = SimpleNN(taille_entree=2, taille_cachee=4, taille_sortie=1)
nn.train(X, y, epochs=5000)

Rareté : Très courant Difficulté : Difficile


2. Qu'est-ce que le problème de disparition du gradient et comment le résolvez-vous ?

Réponse : Les gradients qui disparaissent se produisent lorsque les gradients deviennent extrêmement petits dans les réseaux profonds.

  • Causes :
    • Activations sigmoïdes/tanh (dérivées < 1)
    • Réseaux profonds (gradients qui se multiplient)
  • Solutions :
    • Activations ReLU
    • Normalisation par lots
    • Connexions résiduelles (ResNet)
    • LSTM/GRU pour les RNN
    • Initialisation prudente (Xavier, He)
import torch
import torch.nn as nn

# Problème : Réseau profond avec sigmoïde
class VanishingGradientNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = nn.Sequential(*[
            nn.Sequential(nn.Linear(100, 100), nn.Sigmoid())
            for _ in range(20)  # 20 couches
        ])
    
    def forward(self, x):
        return self.layers(x)

# Solution 1 : Activation ReLU
class ReLUNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = nn.Sequential(*[
            nn.Sequential(nn.Linear(100, 100), nn.ReLU())
            for _ in range(20)
        ])
    
    def forward(self, x):
        return self.layers(x)

# Solution 2 : Connexions résiduelles
class ResidualBlock(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(dim, dim),
            nn.ReLU(),
            nn.Linear(dim, dim)
        )
    
    def forward(self, x):
        return x + self.layers(x)  # Connexion de saut

class ResNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.blocks = nn.Sequential(*[
            ResidualBlock(100) for _ in range(20)
        ])
    
    def forward(self, x):
        return self.blocks(x)

# Solution 3 : Normalisation par lots
class BatchNormNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = nn.Sequential(*[
            nn.Sequential(
                nn.Linear(100, 100),
                nn.BatchNorm1d(100),
                nn.ReLU()
            )
            for _ in range(20)
        ])
    
    def forward(self, x):
        return self.layers(x)

# Analyse du flux de gradient
def analyze_gradients(model, x, y):
    model.zero_grad()
    output = model(x)
    loss = nn.MSELoss()(output, y)
    loss.backward()
    
    # Vérifier les amplitudes du gradient
    for name, param in model.named_parameters():
        if param.grad is not None:
            grad_norm = param.grad.norm().item()
            print(f"{name}: {grad_norm:.6f}")

Rareté : Très courant Difficulté : Difficile


3. Expliquez les mécanismes d'attention et l'auto-attention.

Réponse : L'attention permet aux modèles de se concentrer sur les parties pertinentes de l'entrée.

  • Attention : Somme pondérée des valeurs basée sur la similarité requête-clé
  • Auto-Attention : Attention où la requête, la clé et la valeur proviennent de la même source
  • Attention à produit scalaire mis à l'échelle : Q·K^T / √d_k
import torch
import torch.nn as nn
import torch.nn.functional as F
import math

class ScaledDotProductAttention(nn.Module):
    def __init__(self, temperature):
        super().__init__()
        self.temperature = temperature
    
    def forward(self, q, k, v, mask=None):
        """
        q: (lot, longueur_séquence, d_k)
        k: (lot, longueur_séquence, d_k)
        v: (lot, longueur_séquence, d_v)
        """
        # Calculer les scores d'attention
        attn = torch.matmul(q, k.transpose(-2, -1)) / self.temperature
        
        # Appliquer le masque (pour le remplissage ou l'attention causale)
        if mask is not None:
            attn = attn.masked_fill(mask == 0, -1e9)
        
        # Softmax pour obtenir les poids d'attention
        attn_weights = F.softmax(attn, dim=-1)
        
        # Appliquer l'attention aux valeurs
        output = torch.matmul(attn_weights, v)
        
        return output, attn_weights

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads, dropout=0.1):
        super().__init__()
        assert d_model % n_heads == 0
        
        self.d_model = d_model
        self.n_heads = n_heads
        self.d_k = d_model // n_heads
        
        # Projections linéaires
        self.w_q = nn.Linear(d_model, d_model)
        self.w_k = nn.Linear(d_model, d_model)
        self.w_v = nn.Linear(d_model, d_model)
        self.w_o = nn.Linear(d_model, d_model)
        
        self.attention = ScaledDotProductAttention(temperature=math.sqrt(self.d_k))
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, q, k, v, mask=None):
        batch_size = q.size(0)
        
        # Projections linéaires et division en têtes
        q = self.w_q(q).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        k = self.w_k(k).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        v = self.w_v(v).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        
        # Appliquer l'attention
        output, attn_weights = self.attention(q, k, v, mask)
        
        # Concaténer les têtes
        output = output.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
        
        # Projection linéaire finale
        output = self.w_o(output)
        
        return output, attn_weights

# Exemple d'utilisation
d_model = 512
n_heads = 8
seq_len = 10
batch_size = 2

mha = MultiHeadAttention(d_model, n_heads)
x = torch.randn(batch_size, seq_len, d_model)

# Auto-attention (q, k, v tous provenant de x)
output, attn = mha(x, x, x)
print(f"Forme de la sortie : {output.shape}")
print(f"Forme des poids d'attention : {attn.shape}")

Rareté : Très courant Difficulté : Difficile


4. Quelles sont les différences entre la normalisation par lots et la normalisation des couches ?

Réponse : Les deux normalisent les activations, mais selon des dimensions différentes.

  • Normalisation par lots :
    • Normalise sur la dimension du lot
    • Nécessite des statistiques de lot
    • Problèmes avec les petits lots, les RNN
  • Normalisation des couches :
    • Normalise sur la dimension de la caractéristique
    • Indépendante de la taille du lot
    • Mieux pour les RNN, les transformateurs
import torch
import torch.nn as nn

# Exemple de normalisation par lots
class BatchNormExample(nn.Module):
    def __init__(self, num_features):
        super().__init__()
        self.bn = nn.BatchNorm1d(num_features)
    
    def forward(self, x):
        # x: (taille_lot, num_caractéristiques)
        # Normalise sur la dimension du lot pour chaque caractéristique
        return self.bn(x)

# Exemple de normalisation des couches
class LayerNormExample(nn.Module):
    def __init__(self, normalized_shape):
        super().__init__()
        self.ln = nn.LayerNorm(normalized_shape)
    
    def forward(self, x):
        # x: (taille_lot, longueur_séquence, d_model)
        # Normalise sur la dimension de la caractéristique pour chaque échantillon
        return self.ln(x)

# Implémentation manuelle
class ManualLayerNorm(nn.Module):
    def __init__(self, normalized_shape, eps=1e-5):
        super().__init__()
        self.eps = eps
        self.gamma = nn.Parameter(torch.ones(normalized_shape))
        self.beta = nn.Parameter(torch.zeros(normalized_shape))
    
    def forward(self, x):
        # Calculer la moyenne et la variance sur la dernière dimension
        mean = x.mean(dim=-1, keepdim=True)
        var = x.var(dim=-1, keepdim=True, unbiased=False)
        
        # Normaliser
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        
        # Mettre à l'échelle et décaler
        return self.gamma * x_norm + self.beta

# Comparaison
batch_size, seq_len, d_model = 2, 10, 512

# Batch Norm (pour CNN)
x_cnn = torch.randn(batch_size, d_model, 28, 28)
bn = nn.BatchNorm2d(d_model)
out_bn = bn(x_cnn)

# Layer Norm (pour Transformer)
x_transformer = torch.randn(batch_size, seq_len, d_model)
ln = nn.LayerNorm(d_model)
out_ln = ln(x_transformer)

print(f"Sortie de Batch Norm : {out_bn.shape}")
print(f"Sortie de Layer Norm : {out_ln.shape}")

Rareté : Courant Difficulté : Moyenne


5. Expliquez en détail l'architecture du transformateur.

Réponse : Les transformateurs utilisent l'auto-attention pour la modélisation de séquences sans récurrence.

Loading diagram...
  • Composants :
    • Encodeur : Auto-attention + FFN
    • Décodeur : Auto-attention masquée + attention croisée + FFN
    • Encodage positionnel : Injecter des informations de position
    • Attention multi-tête : Mécanismes d'attention parallèles
import torch
import torch.nn as nn
import math

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        
        # Créer la matrice d'encodage positionnel
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * 
                            (-math.log(10000.0) / d_model))
        
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)
    
    def forward(self, x):
        return x + self.pe[:, :x.size(1)]

class TransformerEncoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
        super().__init__()
        
        # Attention multi-tête
        self.self_attn = nn.MultiheadAttention(d_model, n_heads, dropout=dropout)
        
        # Réseau d'alimentation avant
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.Linear(d_ff, d_model)
        )
        
        # Normalisation des couches
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, mask=None):
        # Auto-attention avec connexion résiduelle
        attn_output, _ = self.self_attn(x, x, x, attn_mask=mask)
        x = self.norm1(x + self.dropout(attn_output))
        
        # Alimentation avant avec connexion résiduelle
        ffn_output = self.ffn(x)
        x = self.norm2(x + self.dropout(ffn_output))
        
        return x

class TransformerEncoder(nn.Module):
    def __init__(self, taille_vocabulaire, d_model, n_heads, d_ff, n_layers, dropout=0.1):
        super().__init__()
        
        self.embedding = nn.Embedding(taille_vocabulaire, d_model)
        self.pos_encoding = PositionalEncoding(d_model)
        
        self.layers = nn.ModuleList([
            TransformerEncoderLayer(d_model, n_heads, d_ff, dropout)
            for _ in range(n_layers)
        ])
        
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, mask=None):
        # Intégration + encodage positionnel
        x = self.embedding(x) * math.sqrt(self.embedding.embedding_dim)
        x = self.pos_encoding(x)
        x = self.dropout(x)
        
        # Appliquer les couches de l'encodeur
        for layer in self.layers:
            x = layer(x, mask)
        
        return x

# Exemple d'utilisation
taille_vocabulaire = 10000
d_model = 512
n_heads = 8
d_ff = 2048
n_layers = 6

encoder = TransformerEncoder(taille_vocabulaire, d_model, n_heads, d_ff, n_layers)

# Entrée : (taille_lot, longueur_séquence)
x = torch.randint(0, taille_vocabulaire, (2, 10))
output = encoder(x)
print(f"Forme de la sortie : {output.shape}")  # (2, 10, 512)

Rareté : Très courant Difficulté : Difficile


Méthodologie de recherche (4 questions)

6. Comment formulez-vous un problème de recherche et une hypothèse ?

Réponse : La recherche commence par l'identification des lacunes et la formulation d'hypothèses testables.

  • Étapes :
    1. Revue de la littérature : Comprendre l'état de l'art
    2. Identifier la lacune : Qu'est-ce qui manque ou peut être amélioré ?
    3. Formuler une hypothèse : Affirmation spécifique et testable
    4. Concevoir des expériences : Comment tester l'hypothèse ?
    5. Définir des mesures : Comment mesurer le succès ?
  • Exemple :
    • Lacune : Les modèles actuels ont du mal avec les dépendances à longue portée
    • Hypothèse : L'attention clairsemée peut maintenir les performances tout en réduisant la complexité
    • Expérience : Comparer l'attention clairsemée et l'attention complète sur les longues séquences
    • Mesures : Perplexité, précision, temps d'inférence

Rareté : Très courant Difficulté : Moyenne


7. Comment concevez-vous des études d'ablation ?

Réponse : Les études d'ablation isolent la contribution des composants individuels.

  • Objectif : Comprendre ce qui fait fonctionner le modèle
  • Méthode : Supprimer/modifier un composant à la fois
  • Meilleures pratiques :
    • Contrôler toutes les autres variables
    • Utiliser les mêmes graines aléatoires
    • Signaler les intervalles de confiance
    • Tester sur plusieurs ensembles de données
# Exemple d'étude d'ablation
class ModelWithAblations:
    def __init__(self, use_attention=True, use_residual=True, use_dropout=True):
        self.use_attention = use_attention
        self.use_residual = use_residual
        self.use_dropout = use_dropout
    
    def build_model(self):
        layers = []
        
        if self.use_attention:
            layers.append(AttentionLayer())
        
        layers.append(FFNLayer())
        
        if self.use_dropout:
            layers.append(nn.Dropout(0.1))
        
        if self.use_residual:
            return ResidualWrapper(nn.Sequential(*layers))
        else:
            return nn.Sequential(*layers)

# Exécuter des expériences d'ablation
configs = [
    {'use_attention': True, 'use_residual': True, 'use_dropout': True},   # Modèle complet
    {'use_attention': False, 'use_residual': True, 'use_dropout': True},  # Pas d'attention
    {'use_attention': True, 'use_residual': False, 'use_dropout': True},  # Pas de résiduel
    {'use_attention': True, 'use_residual': True, 'use_dropout': False},  # Pas de dropout
]

results = []
for config in configs:
    model = ModelWithAblations(**config)
    accuracy = train_and_evaluate(model, seed=42)
    results.append({**config, 'accuracy': accuracy})

# Analyser les résultats
import pandas as pd
df = pd.DataFrame(results)
print(df)

Rareté : Très courant Difficulté : Moyenne


8. Comment assurez-vous la reproductibilité de la recherche ?

Réponse : La reproductibilité est essentielle pour la validité scientifique.

  • Meilleures pratiques :
    • Code : Contrôle de version, documentation claire
    • Données : Version, documenter le prétraitement
    • Environnement : Docker, requirements.txt
    • Graines : Fixer toutes les graines aléatoires
    • Hyperparamètres : Enregistrer tous les paramètres
    • Matériel : Documenter les spécifications GPU/CPU
import random
import numpy as np
import torch
import os

def set_all_seeds(seed=42):
    """Définir les graines pour la reproductibilité"""
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
    
    # Opérations déterministes
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

# Tout enregistrer
import logging
import json

def log_experiment(config, results):
    experiment_log = {
        'timestamp': datetime.now().isoformat(),
        'config': config,
        'results': results,
        'environment': {
            'python_version': sys.version,
            'torch_version': torch.__version__,
            'cuda_version': torch.version.cuda,
            'gpu': torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'
        }
    }
    
    with open('experiment_log.json', 'w') as f:
        json.dump(experiment_log, f, indent=2)

# Partager le code et les modèles
"""
# README.md
## Reproductibilité

### Environnement
```bash
conda create -n research python=3.9
conda activate research
pip install -r requirements.txt

Données

Télécharger depuis : [lien] Prétraiter : python preprocess.py

Formation

python train.py --config configs/experiment1.yaml --seed 42

Évaluation

python evaluate.py --checkpoint checkpoints/best_model.pt

"""


**Rareté :** Très courant
**Difficulté :** Facile

---

### 9. Comment évaluez-vous et comparez-vous les modèles de manière équitable ?

**Réponse :**
Une comparaison équitable nécessite une conception expérimentale minutieuse.
- **Considérations :**
    - **Mêmes divisions de données :** Utiliser des ensembles d'entraînement/validation/test identiques
    - **Plusieurs exécutions :** Signaler la moyenne et l'écart type
    - **Tests statistiques :** Test T, Wilcoxon
    - **Coût de calcul :** FLOP, paramètres, temps
    - **Plusieurs mesures :** Ne pas sélectionner les meilleurs résultats
    - **Plusieurs ensembles de données :** Généralisation

```python
import numpy as np
from scipy import stats

class ModelComparison:
    def __init__(self, n_runs=5):
        self.n_runs = n_runs
        self.results = {}
    
    def evaluate_model(self, model_name, model_fn, X_train, y_train, X_test, y_test):
        scores = []
        
        for seed in range(self.n_runs):
            # Définir la graine pour cette exécution
            set_all_seeds(seed)
            
            # Entraîner le modèle
            model = model_fn()
            model.fit(X_train, y_train)
            
            # Évaluer
            score = model.score(X_test, y_test)
            scores.append(score)
        
        self.results[model_name] = {
            'scores': scores,
            'mean': np.mean(scores),
            'std': np.std(scores),
            'ci_95': stats.t.interval(
                0.95, len(scores)-1,
                loc=np.mean(scores),
                scale=stats.sem(scores)
            )
        }
    
    def compare_models(self, model_a, model_b):
        """Test de signification statistique"""
        scores_a = self.results[model_a]['scores']
        scores_b = self.results[model_b]['scores']
        
        # Test T apparié
        statistic, p_value = stats.ttest_rel(scores_a, scores_b)
        
        return {
            'statistic': statistic,
            'p_value': p_value,
            'significant': p_value < 0.05,
            'better_model': model_a if np.mean(scores_a) > np.mean(scores_b) else model_b
        }
    
    def report(self):
        for model_name, result in self.results.items():
            print(f"\n{model_name}:")
            print(f"  Moyenne : {result['mean']:.4f}")
            print(f"  Écart type :  {result['std']:.4f}")
            print(f"  IC 95 % : [{result['ci_95'][0]:.4f}, {result['ci_95'][1]:.4f}]")

# Utilisation
comparison = ModelComparison(n_runs=10)
comparison.evaluate_model('Modèle A', lambda: ModelA(), X_train, y_train, X_test, y_test)
comparison.evaluate_model('Modèle B', lambda: ModelB(), X_train, y_train, X_test, y_test)

comparison.report()
result = comparison.compare_models('Modèle A', 'Modèle B')
print(f"\nTest statistique : p-value = {result['p_value']:.4f}")

Rareté : Très courant Difficulté : Moyenne


Sujets avancés (4 questions)

10. Expliquez l'apprentissage contrastif et ses applications.

Réponse : L'apprentissage contrastif apprend les représentations en comparant des échantillons similaires et dissemblables.

  • Idée clé : Rapprocher les échantillons similaires, éloigner les échantillons dissemblables
  • Perte : InfoNCE, NT-Xent
  • Applications : SimCLR, MoCo, CLIP
import torch
import torch.nn as nn
import torch.nn.functional as F

class ContrastiveLoss(nn.Module):
    def __init__(self, temperature=0.5):
        super().__init__()
        self.temperature = temperature
    
    def forward(self, features):
        """
        features: (2*taille_lot, dim) - paires d'échantillons augmentés
        """
        batch_size = features.shape[0] // 2
        
        # Normaliser les caractéristiques
        features = F.normalize(features, dim=1)
        
        # Calculer la matrice de similarité
        similarity_matrix = torch.matmul(features, features.T)
        
        # Créer des étiquettes (paires positives)
        labels = torch.cat([torch.arange(batch_size) + batch_size,
                           torch.arange(batch_size)]).to(features.device)
        
        # Masque pour supprimer la similitude automatique
        mask = torch.eye(2 * batch_size, dtype=torch.bool).to(features.device)
        similarity_matrix = similarity_matrix.masked_fill(mask, -9e15)
        
        # Calculer la perte
        similarity_matrix = similarity_matrix / self.temperature
        loss = F.cross_entropy(similarity_matrix, labels)
        
        return loss

class SimCLR(nn.Module):
    def __init__(self, encoder, projection_dim=128):
        super().__init__()
        self.encoder = encoder
        self.projection = nn.Sequential(
            nn.Linear(encoder.output_dim, 512),
            nn.ReLU(),
            nn.Linear(512, projection_dim)
        )
    
    def forward(self, x1, x2):
        # Encoder les deux vues augmentées
        h1 = self.encoder(x1)
        h2 = self.encoder(x2)
        
        # Projeter vers l'espace contrastif
        z1 = self.projection(h1)
        z2 = self.projection(h2)
        
        # Concaténer pour la perte contrastive
        features = torch.cat([z1, z2], dim=0)
        
        return features

# Boucle d'apprentissage
model = SimCLR(encoder, projection_dim=128)
criterion = ContrastiveLoss(temperature=0.5)
optimizer = torch.optim.Adam(model.parameters())

for epoch in range(100):
    for batch in dataloader:
        # Obtenir deux vues augmentées
        x1, x2 = augment(batch)
        
        # Passe avant
        features = model(x1, x2)
        loss = criterion(features)
        
        # Passe arrière
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

Rareté : Courant Difficulté : Difficile


11. Que sont les transformateurs de vision (ViT) et comment fonctionnent-ils ?

Réponse : Les transformateurs de vision appliquent l'architecture du transformateur aux

Newsletter subscription

Conseils de carrière hebdomadaires qui fonctionnent vraiment

Recevez les dernières idées directement dans votre boîte de réception

Decorative doodle

Démarquez-vous auprès des Recruteurs et Décrochez Votre Emploi de Rêve

Rejoignez des milliers de personnes qui ont transformé leur carrière avec des CV alimentés par l'IA qui passent les ATS et impressionnent les responsables du recrutement.

Commencer maintenant

Partager cet article

Réduisez Votre Temps de Rédaction de CV de 90%

Le chercheur d'emploi moyen passe plus de 3 heures à formater un CV. Notre IA le fait en moins de 15 minutes, vous permettant d'atteindre la phase de candidature 12 fois plus rapidement.