diciembre 21, 2025
15 min de lectura

Preguntas para Entrevistas de Científico de Investigación en IA: Guía Completa

interview
career-advice
job-search
Preguntas para Entrevistas de Científico de Investigación en IA: Guía Completa
Milad Bonakdar

Milad Bonakdar

Autor

Domina los fundamentos de la investigación en IA con preguntas esenciales para entrevistas que cubren la teoría del aprendizaje profundo, la metodología de investigación, las arquitecturas de transformadores, la optimización y los temas de IA de vanguardia para científicos de investigación.


Introducción

Los científicos de investigación en IA (Inteligencia Artificial) superan los límites de la inteligencia artificial a través de algoritmos, arquitecturas y metodologías novedosas. Este rol exige un profundo conocimiento teórico, sólidas bases matemáticas, experiencia en investigación y la capacidad de formular y resolver problemas abiertos.

Esta guía completa cubre las preguntas esenciales para entrevistas de Científicos de Investigación en IA, abarcando la teoría del aprendizaje profundo, arquitecturas de transformadores, técnicas de optimización, metodología de investigación, visión artificial, PNL y temas de IA de vanguardia. Cada pregunta incluye respuestas detalladas, evaluación de rareza y clasificaciones de dificultad.


Teoría del Aprendizaje Profundo (5 Preguntas)

1. Explica en detalle la retropropagación y la regla de la cadena.

Respuesta: La retropropagación calcula los gradientes de manera eficiente utilizando la regla de la cadena.

  • Regla de la Cadena: Para funciones compuestas, la derivada es el producto de las derivadas.
  • Pase hacia Adelante (Forward Pass): Calcula las salidas y almacena en caché los valores intermedios.
  • Pase hacia Atrás (Backward Pass): Calcula los gradientes desde la salida hasta la entrada.
import numpy as np

# Red neuronal simple para demostrar la retropropagación
class SimpleNN:
    def __init__(self, input_size, hidden_size, output_size):
        # Inicializar pesos
        self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.b1 = np.zeros((1, hidden_size))
        self.W2 = np.random.randn(hidden_size, output_size) * 0.01
        self.b2 = np.zeros((1, output_size))
    
    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))
    
    def sigmoid_derivative(self, x):
        return x * (1 - x)
    
    def forward(self, X):
        # Capa 1
        self.z1 = np.dot(X, self.W1) + self.b1
        self.a1 = self.sigmoid(self.z1)
        
        # Capa 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]
        
        # Gradientes de la capa de salida
        # dL/da2 = a2 - y (para la entropía cruzada binaria)
        # 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)
        
        # Gradientes de la capa oculta (regla de la cadena)
        # 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)
        
        # Actualizar pesos
        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):
            # Pase hacia adelante
            output = self.forward(X)
            
            # Pase hacia atrás
            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}, Loss: {loss:.4f}')

# Ejemplo de uso
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])  # XOR

nn = SimpleNN(input_size=2, hidden_size=4, output_size=1)
nn.train(X, y, epochs=5000)

Rareza: Muy Común Dificultad: Difícil


2. ¿Qué es el problema del gradiente que se desvanece y cómo lo resuelves?

Respuesta: Los gradientes que se desvanecen ocurren cuando los gradientes se vuelven extremadamente pequeños en redes profundas.

  • Causas:
    • Activaciones sigmoide/tanh (derivadas < 1)
    • Redes profundas (los gradientes se multiplican)
  • Soluciones:
    • Activaciones ReLU
    • Normalización por lotes (Batch normalization)
    • Conexiones residuales (ResNet)
    • LSTM/GRU para RNN
    • Inicialización cuidadosa (Xavier, He)
import torch
import torch.nn as nn

# Problema: Red profunda con sigmoide
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 capas
        ])
    
    def forward(self, x):
        return self.layers(x)

# Solución 1: Activación 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)

# Solución 2: Conexiones residuales
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)  # Skip connection

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)

# Solución 3: Normalización por lotes
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)

# Análisis del flujo del gradiente
def analyze_gradients(model, x, y):
    model.zero_grad()
    output = model(x)
    loss = nn.MSELoss()(output, y)
    loss.backward()
    
    # Verificar las magnitudes del gradiente
    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}")

Rareza: Muy Común Dificultad: Difícil


3. Explica los mecanismos de atención y la autoatención.

Respuesta: La atención permite que los modelos se centren en partes relevantes de la entrada.

  • Atención: Suma ponderada de valores basada en la similitud entre consulta y clave.
  • Autoatención: Atención donde la consulta, la clave y el valor provienen de la misma fuente.
  • Atención de producto escalar escalado: 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: (batch, seq_len, d_k)
        k: (batch, seq_len, d_k)
        v: (batch, seq_len, d_v)
        """
        # Calcular las puntuaciones de atención
        attn = torch.matmul(q, k.transpose(-2, -1)) / self.temperature
        
        # Aplicar máscara (para relleno o atención causal)
        if mask is not None:
            attn = attn.masked_fill(mask == 0, -1e9)
        
        # Softmax para obtener los pesos de atención
        attn_weights = F.softmax(attn, dim=-1)
        
        # Aplicar atención a los valores
        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
        
        # Proyecciones lineales
        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)
        
        # Proyecciones lineales y dividir en cabezas
        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)
        
        # Aplicar atención
        output, attn_weights = self.attention(q, k, v, mask)
        
        # Concatenar cabezas
        output = output.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
        
        # Proyección lineal final
        output = self.w_o(output)
        
        return output, attn_weights

# Ejemplo de uso
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)

# Autoatención (q, k, v todos de x)
output, attn = mha(x, x, x)
print(f"Forma de la salida: {output.shape}")
print(f"Forma de los pesos de atención: {attn.shape}")

Rareza: Muy Común Dificultad: Difícil


4. ¿Cuáles son las diferencias entre la normalización por lotes y la normalización de capas?

Respuesta: Ambas normalizan las activaciones, pero a lo largo de diferentes dimensiones.

  • Normalización por Lotes (Batch Normalization):
    • Normaliza a través de la dimensión del lote
    • Requiere estadísticas del lote
    • Problemas con lotes pequeños, RNN
  • Normalización de Capas (Layer Normalization):
    • Normaliza a través de la dimensión de la característica
    • Independiente del tamaño del lote
    • Mejor para RNN, Transformers
import torch
import torch.nn as nn

# Normalización por lotes
class BatchNormExample(nn.Module):
    def __init__(self, num_features):
        super().__init__()
        self.bn = nn.BatchNorm1d(num_features)
    
    def forward(self, x):
        # x: (batch_size, num_features)
        # Normaliza a través de la dimensión del lote para cada característica
        return self.bn(x)

# Normalización de capas
class LayerNormExample(nn.Module):
    def __init__(self, normalized_shape):
        super().__init__()
        self.ln = nn.LayerNorm(normalized_shape)
    
    def forward(self, x):
        # x: (batch_size, seq_len, d_model)
        # Normaliza a través de la dimensión de la característica para cada muestra
        return self.ln(x)

# Implementación manual
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):
        # Calcular la media y la varianza a través de la última dimensión
        mean = x.mean(dim=-1, keepdim=True)
        var = x.var(dim=-1, keepdim=True, unbiased=False)
        
        # Normalizar
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        
        # Escalar y desplazar
        return self.gamma * x_norm + self.beta

# Comparación
batch_size, seq_len, d_model = 2, 10, 512

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

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

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

Rareza: Común Dificultad: Media


5. Explica la arquitectura del transformador en detalle.

Respuesta: Los transformadores utilizan la autoatención para el modelado de secuencias sin recurrencia.

Loading diagram...
  • Componentes:
    • Codificador (Encoder): Autoatención + FFN
    • Decodificador (Decoder): Autoatención enmascarada + atención cruzada + FFN
    • Codificación Posicional (Positional Encoding): Inyectar información de posición
    • Atención Multi-Cabeza (Multi-Head Attention): Mecanismos de atención paralelos
import torch
import torch.nn as nn
import math

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        
        # Crear matriz de codificación posicional
        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__()
        
        # Atención multi-cabeza
        self.self_attn = nn.MultiheadAttention(d_model, n_heads, dropout=dropout)
        
        # Red de avance de alimentación
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.Linear(d_ff, d_model)
        )
        
        # Normalización de capas
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, mask=None):
        # Autoatención con conexión residual
        attn_output, _ = self.self_attn(x, x, x, attn_mask=mask)
        x = self.norm1(x + self.dropout(attn_output))
        
        # Avance de alimentación con conexión residual
        ffn_output = self.ffn(x)
        x = self.norm2(x + self.dropout(ffn_output))
        
        return x

class TransformerEncoder(nn.Module):
    def __init__(self, vocab_size, d_model, n_heads, d_ff, n_layers, dropout=0.1):
        super().__init__()
        
        self.embedding = nn.Embedding(vocab_size, 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):
        # Incrustación + codificación posicional
        x = self.embedding(x) * math.sqrt(self.embedding.embedding_dim)
        x = self.pos_encoding(x)
        x = self.dropout(x)
        
        # Aplicar capas del codificador
        for layer in self.layers:
            x = layer(x, mask)
        
        return x

# Ejemplo de uso
vocab_size = 10000
d_model = 512
n_heads = 8
d_ff = 2048
n_layers = 6

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

# Entrada: (batch_size, seq_len)
x = torch.randint(0, vocab_size, (2, 10))
output = encoder(x)
print(f"Forma de la salida: {output.shape}")  # (2, 10, 512)

Rareza: Muy Común Dificultad: Difícil


Metodología de Investigación (4 Preguntas)

6. ¿Cómo formulas un problema de investigación e hipótesis?

Respuesta: La investigación comienza con la identificación de brechas y la formulación de hipótesis comprobables.

  • Pasos:
    1. Revisión de la Literatura: Comprender el estado del arte
    2. Identificar Brecha: ¿Qué falta o se puede mejorar?
    3. Formular Hipótesis: Afirmación específica y comprobable
    4. Diseñar Experimentos: ¿Cómo probar la hipótesis?
    5. Definir Métricas: ¿Cómo medir el éxito?
  • Ejemplo:
    • Brecha: Los modelos actuales tienen dificultades con las dependencias de largo alcance.
    • Hipótesis: La atención dispersa puede mantener el rendimiento al tiempo que reduce la complejidad.
    • Experimento: Comparar la atención dispersa vs. completa en secuencias largas.
    • Métricas: Perplejidad, precisión, tiempo de inferencia

Rareza: Muy Común Dificultad: Media


7. ¿Cómo diseñas estudios de ablación?

Respuesta: Los estudios de ablación aíslan la contribución de los componentes individuales.

  • Propósito: Comprender qué hace que el modelo funcione
  • Método: Eliminar/modificar un componente a la vez
  • Mejores Prácticas:
    • Controlar todas las demás variables
    • Usar las mismas semillas aleatorias
    • Informar intervalos de confianza
    • Probar en múltiples conjuntos de datos
# Ejemplo de estudio de ablación
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)

# Ejecutar experimentos de ablación
configs = [
    {'use_attention': True, 'use_residual': True, 'use_dropout': True},   # Modelo completo
    {'use_attention': False, 'use_residual': True, 'use_dropout': True},  # Sin atención
    {'use_attention': True, 'use_residual': False, 'use_dropout': True},  # Sin residual
    {'use_attention': True, 'use_residual': True, 'use_dropout': False},  # Sin dropout
]

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

# Analizar resultados
import pandas as pd
df = pd.DataFrame(results)
print(df)

Rareza: Muy Común Dificultad: Media


8. ¿Cómo aseguras la reproducibilidad en la investigación?

Respuesta: La reproducibilidad es fundamental para la validez científica.

  • Mejores Prácticas:
    • Código: Control de versiones, documentación clara
    • Datos: Versión, documentar el preprocesamiento
    • Entorno: Docker, requirements.txt
    • Semillas (Seeds): Fijar todas las semillas aleatorias
    • Hiperparámetros: Registrar todas las configuraciones
    • Hardware: Documentar las especificaciones de GPU/CPU
import random
import numpy as np
import torch
import os

def set_all_seeds(seed=42):
    """Establecer semillas para la reproducibilidad"""
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
    
    # Operaciones deterministas
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

# Registrar todo
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)

# Compartir código y modelos
"""
# README.md
## Reproducibilidad

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

Datos

Descargar de: [enlace] Preprocesar: python preprocess.py

Entrenamiento

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

Evaluación

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

"""


**Rareza:** Muy Común
**Dificultad:** Fácil

---

### 9. ¿Cómo evalúas y comparas modelos de manera justa?

**Respuesta:**
Una comparación justa requiere un diseño experimental cuidadoso.
- **Consideraciones:**
    - **Mismas divisiones de datos:** Utilizar train/val/test idénticos
    - **Múltiples ejecuciones:** Informar la media y la desviación estándar
    - **Pruebas estadísticas:** Prueba T, Wilcoxon
    - **Costo computacional:** FLOPs, parámetros, tiempo
    - **Múltiples métricas:** No seleccionar solo las mejores
    - **Múltiples conjuntos de datos:** Generalización

```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):
            # Establecer la semilla para esta ejecución
            set_all_seeds(seed)
            
            # Entrenar el modelo
            model = model_fn()
            model.fit(X_train, y_train)
            
            # Evaluar
            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):
        """Prueba de significación estadística"""
        scores_a = self.results[model_a]['scores']
        scores_b = self.results[model_b]['scores']
        
        # Prueba t pareada
        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"  Media: {result['mean']:.4f}")
            print(f"  Desviación estándar:  {result['std']:.4f}")
            print(f"  IC del 95%: [{result['ci_95'][0]:.4f}, {result['ci_95'][1]:.4f}]")

# Uso
comparison = ModelComparison(n_runs=10)
comparison.evaluate_model('Modelo A', lambda: ModelA(), X_train, y_train, X_test, y_test)
comparison.evaluate_model('Modelo B', lambda: ModelB(), X_train, y_train, X_test, y_test)

comparison.report()
result = comparison.compare_models('Modelo A', 'Modelo B')
print(f"\nPrueba estadística: p-value = {result['p_value']:.4f}")

Rareza: Muy Común Dificultad: Media


Temas Avanzados (4 Preguntas)

10. Explica el aprendizaje contrastivo y sus aplicaciones.

Respuesta: El aprendizaje contrastivo aprende representaciones comparando muestras similares y disímiles.

  • Idea Clave: Juntar muestras similares, separar las disímiles
  • Pérdida (Loss): InfoNCE, NT-Xent
  • Aplicaciones: 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*batch_size, dim) - pares de muestras aumentadas
        """
        batch_size = features.shape[0] // 2
        
        # Normalizar características
        features = F.normalize(features, dim=1)
        
        # Calcular la matriz de similitud
        similarity_matrix = torch.matmul(features, features.T)
        
        # Crear etiquetas (pares positivos)
        labels = torch.cat([torch.arange(batch_size) + batch_size,
                           torch.arange(batch_size)]).to(features.device)
        
        # Máscara para eliminar la auto-similitud
        mask = torch.eye(2 * batch_size, dtype=torch.bool).to(features.device)
        similarity_matrix = similarity_matrix.masked_fill(mask, -9e15)
        
        # Calcular la pérdida
        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):
        # Codificar ambas vistas aumentadas
        h1 = self.encoder(x1)
        h2 = self.encoder(x2)
        
        # Proyectar al espacio contrastivo
        z1 = self.projection(h1)
        z2 = self.projection(h2)
        
        # Concatenar para la pérdida contrastiva
        features = torch.cat([z1, z2], dim=0)
        
        return features

# Bucle de entrenamiento
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:
        # Obtener dos vistas aumentadas
        x1, x2 = augment(batch)
        
        # Pase hacia adelante
        features = model(x1, x2)
        loss = criterion(features)
        
        # Pase hacia atrás
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

Rareza: Común Dificultad: Difícil


11. ¿Qué son los Transformadores de Visión (ViT) y cómo funcionan?

Respuesta: Los Transformadores de Visión aplican la arquitectura de transformadores a las imágenes.

  • Ideas Clave:
    • Dividir la imagen en parches
    • Incrustación lineal de parches
    • Agregar incrustaciones posicionales
    • Aplicar el codificador del transformador
  • Ventajas: Escalabilidad, campo receptivo global
  • Desafíos: Requieren grandes conjuntos de datos
import torch
import torch.nn as nn

class PatchEmbedding(nn.Module):
    def __init__(self, img_size=224, patch_size=16, in_channels=3, embed_dim=768):
        super().__init__()
        self.img_size = img_size
        self.patch_size = patch_size
        self.n_patches = (img_size // patch_size) **
Newsletter subscription

Consejos de carrera semanales que realmente funcionan

Recibe las últimas ideas directamente en tu bandeja de entrada

Destácate ante los Reclutadores y Consigue el Trabajo de Tus Sueños

Únete a miles que transformaron sus carreras con currículums impulsados por IA que pasan el ATS e impresionan a los gerentes de contratación.

Comienza a crear ahora

Compartir esta publicación

Reduce el Tiempo de Escritura de tu Currículum en un 90%

El buscador de empleo promedio pasa más de 3 horas formateando un currículum. Nuestra IA lo hace en menos de 15 minutos, llevándote a la fase de solicitud 12 veces más rápido.