Visión por Computadora con PyTorch en 2026: CNN, Transfer Learning y Preguntas de Entrevista
Guía completa sobre visión por computadora con PyTorch: redes neuronales convolucionales, transfer learning y preparación para entrevistas técnicas en deep learning.

La visión por computadora se ha consolidado como uno de los campos más relevantes dentro de la inteligencia artificial. Mediante PyTorch, los desarrolladores y científicos de datos pueden construir modelos avanzados para clasificación de imágenes, detección de objetos y segmentación semántica. Esta guía aborda los fundamentos de las CNN, técnicas de transfer learning y las preguntas más frecuentes en entrevistas técnicas.
El dominio de las CNN y el transfer learning representa una ventaja competitiva significativa para posiciones en machine learning. Los reclutadores frecuentemente evalúan la comprensión práctica de estos conceptos durante las entrevistas técnicas.
Fundamentos de las Redes Neuronales Convolucionales
Las redes neuronales convolucionales (CNN) constituyen la arquitectura fundamental para el procesamiento de imágenes. A diferencia de las redes fully-connected, las CNN aprovechan la estructura espacial de los datos visuales mediante operaciones de convolución.
La arquitectura típica de una CNN incluye varios tipos de capas: las capas de convolución extraen características locales, las capas de pooling reducen la dimensionalidad, y las capas fully-connected realizan la clasificación final.
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleCNN(nn.Module):
def __init__(self, num_classes: int = 10):
super(SimpleCNN, self).__init__()
# Convolutional layers
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
# Batch normalization
self.bn1 = nn.BatchNorm2d(32)
self.bn2 = nn.BatchNorm2d(64)
self.bn3 = nn.BatchNorm2d(128)
# Pooling and dropout
self.pool = nn.MaxPool2d(2, 2)
self.dropout = nn.Dropout(0.5)
# Fully connected layers
self.fc1 = nn.Linear(128 * 4 * 4, 512)
self.fc2 = nn.Linear(512, num_classes)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# Block 1: 32x32 -> 16x16
x = self.pool(F.relu(self.bn1(self.conv1(x))))
# Block 2: 16x16 -> 8x8
x = self.pool(F.relu(self.bn2(self.conv2(x))))
# Block 3: 8x8 -> 4x4
x = self.pool(F.relu(self.bn3(self.conv3(x))))
# Flatten and classify
x = x.view(-1, 128 * 4 * 4)
x = self.dropout(F.relu(self.fc1(x)))
x = self.fc2(x)
return xEste modelo ilustra los componentes esenciales: batch normalization estabiliza el entrenamiento, dropout previene el sobreajuste, y max pooling reduce progresivamente las dimensiones espaciales.
Configuración del Entorno y Preparación de Datos
Antes de entrenar un modelo, la preparación de datos constituye una etapa crítica. PyTorch proporciona herramientas robustas para la carga y aumentación de datos.
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from typing import Tuple
def create_data_loaders(
data_dir: str,
batch_size: int = 32,
num_workers: int = 4
) -> Tuple[DataLoader, DataLoader]:
"""Create training and validation data loaders with augmentation."""
# Training transforms with augmentation
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(15),
transforms.ColorJitter(
brightness=0.2,
contrast=0.2,
saturation=0.2
),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
# Validation transforms (no augmentation)
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
# Create datasets
train_dataset = datasets.ImageFolder(
root=f"{data_dir}/train",
transform=train_transform
)
val_dataset = datasets.ImageFolder(
root=f"{data_dir}/val",
transform=val_transform
)
# Create data loaders
train_loader = DataLoader(
train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=num_workers,
pin_memory=True
)
val_loader = DataLoader(
val_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=num_workers,
pin_memory=True
)
return train_loader, val_loaderLa aumentación de datos mejora la robustez del modelo al crear variaciones artificiales de las imágenes de entrenamiento. Las transformaciones geométricas y colorimétricas incrementan la diversidad del dataset sin requerir datos adicionales.
Transfer Learning con Modelos Preentrenados
El transfer learning permite aprovechar modelos entrenados en datasets extensos como ImageNet. Este enfoque reduce significativamente el tiempo de entrenamiento y mejora el rendimiento, especialmente con datasets limitados.
import torch
import torch.nn as nn
from torchvision import models
from torchvision.models import ResNet50_Weights, EfficientNet_B0_Weights
def create_transfer_model(
model_name: str,
num_classes: int,
freeze_backbone: bool = True
) -> nn.Module:
"""Create a transfer learning model with a custom classifier."""
if model_name == "resnet50":
# Load pre-trained ResNet50
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# Freeze backbone if specified
if freeze_backbone:
for param in model.parameters():
param.requires_grad = False
# Replace classifier
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
elif model_name == "efficientnet_b0":
# Load pre-trained EfficientNet
weights = EfficientNet_B0_Weights.IMAGENET1K_V1
model = models.efficientnet_b0(weights=weights)
if freeze_backbone:
for param in model.parameters():
param.requires_grad = False
# Replace classifier
num_features = model.classifier[1].in_features
model.classifier = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, num_classes)
)
else:
raise ValueError(f"Unknown model: {model_name}")
return model
def unfreeze_layers(model: nn.Module, num_layers: int) -> None:
"""Gradually unfreeze layers for fine-tuning."""
# Get all parameters as a list
params = list(model.parameters())
# Unfreeze the last n layers
for param in params[-num_layers:]:
param.requires_grad = TrueLa estrategia de congelamiento progresivo de capas optimiza el proceso de entrenamiento. Las primeras capas, que detectan características genéricas como bordes y texturas, permanecen congeladas. Las capas superiores, específicas de la tarea, se entrenan con el nuevo dataset.
Bucle de Entrenamiento Completo
Un bucle de entrenamiento robusto integra validación, seguimiento de métricas y guardado de los mejores modelos.
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from typing import Dict, List
import copy
class Trainer:
def __init__(
self,
model: nn.Module,
train_loader: DataLoader,
val_loader: DataLoader,
learning_rate: float = 1e-4,
device: str = "cuda"
):
self.model = model.to(device)
self.train_loader = train_loader
self.val_loader = val_loader
self.device = device
self.criterion = nn.CrossEntropyLoss()
self.optimizer = optim.AdamW(
filter(lambda p: p.requires_grad, model.parameters()),
lr=learning_rate,
weight_decay=0.01
)
self.scheduler = optim.lr_scheduler.CosineAnnealingLR(
self.optimizer,
T_max=50
)
self.best_model_weights = None
self.best_accuracy = 0.0
self.history: Dict[str, List[float]] = {
"train_loss": [],
"val_loss": [],
"val_accuracy": []
}
def train_epoch(self) -> float:
"""Train for one epoch."""
self.model.train()
running_loss = 0.0
for images, labels in self.train_loader:
images = images.to(self.device)
labels = labels.to(self.device)
self.optimizer.zero_grad()
outputs = self.model(images)
loss = self.criterion(outputs, labels)
loss.backward()
self.optimizer.step()
running_loss += loss.item()
return running_loss / len(self.train_loader)
@torch.no_grad()
def validate(self) -> tuple[float, float]:
"""Validate the model."""
self.model.eval()
running_loss = 0.0
correct = 0
total = 0
for images, labels in self.val_loader:
images = images.to(self.device)
labels = labels.to(self.device)
outputs = self.model(images)
loss = self.criterion(outputs, labels)
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
accuracy = correct / total
avg_loss = running_loss / len(self.val_loader)
return avg_loss, accuracy
def fit(self, epochs: int) -> Dict[str, List[float]]:
"""Full training loop."""
for epoch in range(epochs):
train_loss = self.train_epoch()
val_loss, val_accuracy = self.validate()
self.scheduler.step()
# Save history
self.history["train_loss"].append(train_loss)
self.history["val_loss"].append(val_loss)
self.history["val_accuracy"].append(val_accuracy)
# Save best model
if val_accuracy > self.best_accuracy:
self.best_accuracy = val_accuracy
self.best_model_weights = copy.deepcopy(
self.model.state_dict()
)
print(
f"Epoch {epoch+1}/{epochs} - "
f"Train Loss: {train_loss:.4f} - "
f"Val Loss: {val_loss:.4f} - "
f"Val Acc: {val_accuracy:.4f}"
)
# Load best weights
self.model.load_state_dict(self.best_model_weights)
return self.historyEsta implementación utiliza el optimizador AdamW con weight decay para regularización y un scheduler cosine annealing para ajustar dinámicamente el learning rate.
Técnicas Avanzadas para Visión por Computadora
Las arquitecturas modernas incorporan mecanismos de atención que mejoran la capacidad del modelo para enfocarse en regiones relevantes de la imagen.
import torch
import torch.nn as nn
import torch.nn.functional as F
class ChannelAttention(nn.Module):
"""Squeeze-and-Excitation channel attention."""
def __init__(self, channels: int, reduction: int = 16):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channels, channels // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channels // reduction, channels, bias=False),
nn.Sigmoid()
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
class SpatialAttention(nn.Module):
"""CBAM spatial attention module."""
def __init__(self, kernel_size: int = 7):
super().__init__()
self.conv = nn.Conv2d(
2, 1,
kernel_size=kernel_size,
padding=kernel_size // 2,
bias=False
)
self.sigmoid = nn.Sigmoid()
def forward(self, x: torch.Tensor) -> torch.Tensor:
avg_out = torch.mean(x, dim=1, keepdim=True)
max_out, _ = torch.max(x, dim=1, keepdim=True)
attention = torch.cat([avg_out, max_out], dim=1)
attention = self.sigmoid(self.conv(attention))
return x * attentionLos mecanismos de atención de canal y espacial permiten que la red aprenda qué características y qué regiones son más importantes para la tarea de clasificación.
¿Listo para aprobar tus entrevistas de Data Science & ML?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Preguntas Frecuentes en Entrevistas de Visión por Computadora
Las entrevistas técnicas en deep learning generalmente cubren conceptos teóricos y prácticos. A continuación se presentan las preguntas más comunes con sus respuestas.
Pregunta: Explique la diferencia entre valid y same padding en las convoluciones.
El padding "valid" no agrega píxeles alrededor de la imagen, lo que reduce las dimensiones de salida. El padding "same" agrega píxeles para mantener las dimensiones de entrada idénticas a las de salida. La elección depende de la arquitectura deseada y la preservación de información espacial en los bordes.
Pregunta: ¿Cómo mejora batch normalization el entrenamiento de las CNN?
Batch normalization normaliza las activaciones en cada mini-batch, reduciendo el covariate shift interno. Esta técnica acelera la convergencia, permite utilizar learning rates más altos y actúa como regularizador. Durante la inferencia, las estadísticas de media y varianza se calculan sobre todo el dataset de entrenamiento.
Pregunta: ¿Cuándo usar transfer learning versus entrenar desde cero?
El transfer learning es preferible con datasets limitados, tareas similares al dataset fuente, o restricciones de recursos computacionales. El entrenamiento desde cero conviene para datasets masivos, dominios muy diferentes de ImageNet (imágenes médicas, satelitales), o cuando las características de bajo nivel difieren significativamente.
Pregunta: Explique el concepto de receptive field en las CNN.
El receptive field representa la región de la imagen de entrada que influye en un neurona particular. Aumenta con la profundidad de la red mediante convoluciones y poolings sucesivos. Un receptive field más amplio permite al modelo capturar dependencias espaciales a mayor escala, esencial para la comprensión contextual.
Pregunta: ¿Cómo manejar el desbalance de clases en clasificación de imágenes?
Existen varias estrategias: ponderación de clases en la función de pérdida, sobremuestreo de clases minoritarias, submuestreo de clases mayoritarias, o generación de datos sintéticos mediante aumentación dirigida. La elección depende de la severidad del desbalance y el tamaño del dataset.
Métricas de Evaluación para Visión por Computadora
La evaluación rigurosa de un modelo requiere métricas adaptadas a la tarea y al contexto empresarial.
import torch
import numpy as np
from sklearn.metrics import (
confusion_matrix,
classification_report,
precision_recall_fscore_support
)
from typing import Dict, List
@torch.no_grad()
def evaluate_model(
model: torch.nn.Module,
test_loader: torch.utils.data.DataLoader,
class_names: List[str],
device: str = "cuda"
) -> Dict:
"""Comprehensive model evaluation."""
model.eval()
all_predictions = []
all_labels = []
all_probabilities = []
for images, labels in test_loader:
images = images.to(device)
outputs = model(images)
probabilities = torch.softmax(outputs, dim=1)
_, predicted = outputs.max(1)
all_predictions.extend(predicted.cpu().numpy())
all_labels.extend(labels.numpy())
all_probabilities.extend(probabilities.cpu().numpy())
# Calculate metrics
precision, recall, f1, support = precision_recall_fscore_support(
all_labels,
all_predictions,
average=None
)
# Per-class metrics
per_class_metrics = {
class_names[i]: {
"precision": float(precision[i]),
"recall": float(recall[i]),
"f1_score": float(f1[i]),
"support": int(support[i])
}
for i in range(len(class_names))
}
# Overall metrics
accuracy = np.mean(np.array(all_predictions) == np.array(all_labels))
macro_f1 = np.mean(f1)
return {
"accuracy": float(accuracy),
"macro_f1": float(macro_f1),
"per_class": per_class_metrics,
"confusion_matrix": confusion_matrix(
all_labels,
all_predictions
).tolist()
}La accuracy por sí sola puede ser engañosa con clases desbalanceadas. El F1-score macro proporciona una vista más equilibrada del rendimiento en todas las clases.
Conclusión
La visión por computadora con PyTorch ofrece posibilidades considerables para resolver problemas complejos de procesamiento de imágenes. El dominio de las CNN, transfer learning y técnicas de atención constituye una base sólida para los profesionales del deep learning. La preparación para entrevistas técnicas requiere una comprensión profunda de estos conceptos, combinada con experiencia práctica en su implementación. El ecosistema de PyTorch continúa evolucionando con nuevas arquitecturas y optimizaciones, haciendo esencial mantenerse actualizado en este campo en rápida evolución.

Escrito por
Anthony Fillion-MailletDesarrollador fullstack, fundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 14 de agosto de 2026
Compartir
Artículos relacionados

LangChain para Científicos de Datos en 2026: LLMs, Agentes y Preguntas de Entrevista
Guía completa de LangChain para científicos de datos: dominar LLMs, agentes autónomos y prepararse para preguntas técnicas de entrevista en 2026.

MLOps en 2026: MLflow, Model Registry y preguntas de entrevista técnica
Preguntas de entrevista sobre MLOps que abarcan el ciclo de vida del ML, el seguimiento de experimentos con MLflow, la promoción en el model registry, los patrones de despliegue, el monitoreo de drift y el diseño de sistemas para 2026, con código Python y respuestas.

RAG y LLMs en 2026: Guía Completa de Retrieval-Augmented Generation para Entrevistas de Data Science
Domina las preguntas de entrevista sobre RAG y LLMs en 2026. Aprende retrieval augmented generation, pipelines, chunking semántico y evaluación para data science.