Visão Computacional com PyTorch em 2026: CNN, Transfer Learning e Perguntas de Entrevista
Guia completo sobre visão computacional com PyTorch: redes neurais convolucionais, transfer learning e preparação para entrevistas técnicas em deep learning.

A visão computacional consolidou-se como uma das áreas mais relevantes da inteligência artificial. Com PyTorch como framework principal, desenvolvedores e cientistas de dados podem construir modelos avançados para classificação de imagens, detecção de objetos e segmentação semântica. Este guia aborda os fundamentos das CNN, técnicas de transfer learning e as perguntas mais frequentes em entrevistas técnicas.
O domínio das CNN e do transfer learning representa uma vantagem competitiva significativa para posições em machine learning. Recrutadores frequentemente avaliam a compreensão prática desses conceitos durante as entrevistas técnicas.
Fundamentos das Redes Neurais Convolucionais
As redes neurais convolucionais (CNN) constituem a arquitetura fundamental para o processamento de imagens. Diferentemente das redes fully-connected, as CNN aproveitam a estrutura espacial dos dados visuais através de operações de convolução.
A arquitetura típica de uma CNN inclui vários tipos de camadas: as camadas de convolução extraem características locais, as camadas de pooling reduzem a dimensionalidade, e as camadas fully-connected realizam a classificação 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 xEsse modelo ilustra os componentes essenciais: batch normalization estabiliza o treinamento, dropout previne o overfitting, e max pooling reduz progressivamente as dimensões espaciais.
Configuração do Ambiente e Preparação de Dados
Antes de treinar um modelo, a preparação de dados constitui uma etapa crítica. PyTorch fornece ferramentas robustas para carregamento e aumentação de dados.
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_loaderA aumentação de dados melhora a robustez do modelo ao criar variações artificiais das imagens de treinamento. As transformações geométricas e colorimétricas aumentam a diversidade do dataset sem necessitar de novos dados.
Transfer Learning com Modelos Pré-treinados
O transfer learning permite aproveitar modelos treinados em datasets extensos como ImageNet. Essa abordagem reduz significativamente o tempo de treinamento e melhora o desempenho, especialmente com 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 = TrueA estratégia de congelamento progressivo de camadas otimiza o processo de treinamento. As primeiras camadas, que detectam características genéricas como bordas e texturas, permanecem congeladas. As camadas superiores, específicas da tarefa, são treinadas com o novo dataset.
Loop de Treinamento Completo
Um loop de treinamento robusto integra validação, acompanhamento de métricas e salvamento dos melhores 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.historyEssa implementação utiliza o otimizador AdamW com weight decay para regularização e um scheduler cosine annealing para ajustar dinamicamente o learning rate.
Técnicas Avançadas para Visão Computacional
As arquiteturas modernas incorporam mecanismos de atenção que melhoram a capacidade do modelo de focar em regiões relevantes da imagem.
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 * attentionOs mecanismos de atenção de canal e espacial permitem que a rede aprenda quais características e quais regiões são mais importantes para a tarefa de classificação.
Pronto para mandar bem nas entrevistas de Data Science & ML?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Perguntas Frequentes em Entrevistas de Visão Computacional
As entrevistas técnicas em deep learning geralmente cobrem conceitos teóricos e práticos. A seguir estão as perguntas mais comuns com suas respostas.
Pergunta: Explique a diferença entre valid e same padding nas convoluções.
O padding "valid" não adiciona pixels ao redor da imagem, o que reduz as dimensões de saída. O padding "same" adiciona pixels para manter as dimensões de entrada idênticas às de saída. A escolha depende da arquitetura desejada e da preservação de informação espacial nas bordas.
Pergunta: Como batch normalization melhora o treinamento das CNN?
Batch normalization normaliza as ativações em cada mini-batch, reduzindo o covariate shift interno. Essa técnica acelera a convergência, permite utilizar learning rates mais altos e atua como regularizador. Durante a inferência, as estatísticas de média e variância são calculadas sobre todo o dataset de treinamento.
Pergunta: Quando usar transfer learning versus treinar do zero?
O transfer learning é preferível com datasets limitados, tarefas similares ao dataset fonte, ou restrições de recursos computacionais. O treinamento do zero convém para datasets massivos, domínios muito diferentes do ImageNet (imagens médicas, satélite), ou quando as características de baixo nível diferem significativamente.
Pergunta: Explique o conceito de receptive field nas CNN.
O receptive field representa a região da imagem de entrada que influencia um neurônio particular. Ele aumenta com a profundidade da rede através de convoluções e poolings sucessivos. Um receptive field maior permite ao modelo capturar dependências espaciais em maior escala, essencial para a compreensão contextual.
Pergunta: Como lidar com desbalanceamento de classes em classificação de imagens?
Existem várias estratégias: ponderação de classes na função de perda, oversampling de classes minoritárias, undersampling de classes majoritárias, ou geração de dados sintéticos através de aumentação direcionada. A escolha depende da severidade do desbalanceamento e do tamanho do dataset.
Métricas de Avaliação para Visão Computacional
A avaliação rigorosa de um modelo requer métricas adaptadas à tarefa e ao 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()
}A accuracy sozinha pode ser enganosa com classes desbalanceadas. O F1-score macro fornece uma visão mais equilibrada do desempenho em todas as classes.
Conclusão
A visão computacional com PyTorch oferece possibilidades consideráveis para resolver problemas complexos de processamento de imagens. O domínio das CNN, transfer learning e técnicas de atenção constitui uma base sólida para os profissionais de deep learning. A preparação para entrevistas técnicas requer uma compreensão profunda desses conceitos, combinada com experiência prática em sua implementação. O ecossistema PyTorch continua evoluindo com novas arquiteturas e otimizações, tornando essencial manter-se atualizado nesse campo em rápida evolução.

Escrito por
Anthony Fillion-MailletDesenvolvedor fullstack, fundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 14 de agosto de 2026
Compartilhar
Artigos relacionados

LangChain para Cientistas de Dados em 2026: LLMs, Agentes e Perguntas de Entrevista
Guia completo de LangChain para cientistas de dados: dominar LLMs, agentes autônomos e preparar-se para perguntas técnicas de entrevista em 2026.

MLOps em 2026: MLflow, Model Registry e Perguntas de Entrevista Técnica
Perguntas de entrevista de MLOps abordando o ciclo de vida de ML, rastreamento de experimentos com MLflow, promoção no model registry, padrões de deploy, monitoramento de drift e system design para 2026, com código Python e respostas.

RAG e LLMs em 2026: Retrieval-Augmented Generation para Entrevistas de Data Science
Domine RAG interview questions e retrieval augmented generation para entrevistas de data science em 2026. Guia completo com código e melhores práticas.