Classificação de Pneumonia em Raio-X de Tórax

Projeto de Transfer Learning com Redes Neurais Convolucionais para detecção automatizada de pneumonia em imagens de raio-X

Trabalho Final - PIVC
👤 Autor: Idarlan Rogério Dias Magalhães 🎓 Curso: [CTE-IA] Inteligência Artificial — DC/UFC

Visão Geral do Projeto

Objetivo

Desenvolver e comparar modelos de Deep Learning baseados em Transfer Learning para classificação binária de imagens de raio-X de tórax entre Normal e Pneumonia. O projeto explora diferentes estratégias de fine-tuning utilizando arquiteturas pré-treinadas no ImageNet.

5.856
Imagens no Dataset
3
Experimentos Realizados
96,78%
Melhor AUC-ROC

Dataset

Utilizamos o dataset Chest X-Ray Images (Pneumonia) do Kaggle, contendo imagens de raio-X de tórax de pacientes pediátricos organizadas em duas classes.

5.216
Treino
16
Validação
624
Teste

Tecnologias Utilizadas

Python PyTorch torchvision scikit-learn matplotlib seaborn NumPy Pandas

Metodologia

Pipeline do Projeto

Dataset
Chest X-Ray
Kaggle
Pré-processamento
Resize 224x224
Normalização ImageNet
Augmentation
Flip, Rotação
Color Jitter
Transfer Learning
ResNet50 / DenseNet121
Pré-treinados ImageNet
Avaliação
Métricas, ROC
Matriz de Confusão

Data Augmentation (Treino)

Horizontal Flip p = 0.5
Rotação ±15°
Brightness Jitter ±0.2
Contrast Jitter ±0.2
Normalização ImageNet (mean/std)
Resolução 224 × 224 px

Estratégias de Treinamento

Loss Function BCEWithLogitsLoss
Class Weights Balanceamento automático
Optimizer Adam
LR Scheduler ReduceLROnPlateau
Early Stopping Patience = 10
Seed 42 (reprodutibilidade)

Arquitetura do Classificador

O classificador final substituído nas redes pré-treinadas possui a seguinte arquitetura:

Linear(in_features, 512) → ReLU → Dropout(0.5)
Linear(512, 256) → ReLU → Dropout(0.3)
Linear(256, 1) → (BCEWithLogitsLoss aplica sigmoid)

Técnicas de Treinamento — Funcionamento e Adaptação ao Projeto

As técnicas abaixo foram utilizadas a partir de bibliotecas de terceiros (PyTorch, torchvision) e adaptadas especificamente para o problema de classificação de pneumonia em raio-X.

🖼️ Data Augmentation (torchvision.transforms)

Data Augmentation é uma técnica que aplica transformações aleatórias nas imagens apenas durante o treino, aumentando artificialmente a diversidade dos dados sem coletar novas amostras. Reduz overfitting ao impedir que o modelo memorize os exemplos de treino. Cada transformação foi escolhida com base na natureza das imagens de raio-X de tórax:

RandomHorizontalFlip (p=0.5)
Espelha horizontalmente com 50% de probabilidade. Justificativa: a anatomia pulmonar é aproximadamente simétrica, então um raio-X espelhado ainda representa um pulmão plausível. Dobra efetivamente o dataset de treino.
RandomRotation (±15°)
Rotaciona a imagem em até ±15 graus. Justificativa: pacientes raramente ficam perfeitamente alinhados durante o exame. Rotações pequenas tornam o modelo robusto a variações de posicionamento, comuns na prática clínica.
ColorJitter (brightness/contrast ±0.2)
Varia levemente brilho e contraste. Justificativa: a intensidade do raio-X varia conforme o equipamento, tensão do tubo e dose de radiação. O modelo deve ser invariante a essas variações de aquisição entre diferentes hospitais. Saturação e matiz foram mantidos em 0 pois raio-X é escala de cinza.
Normalização ImageNet (mean/std)
Normaliza com média [0.485, 0.456, 0.406] e desvio [0.229, 0.224, 0.225] — os valores do ImageNet. Necessária pois os pesos pré-treinados foram ajustados com essa normalização; usá-la garante que as ativações da rede estejam na mesma escala esperada durante o pré-treinamento.
📉 BCEWithLogitsLoss com pos_weight (PyTorch)

Binary Cross-Entropy with Logits Loss é a função de perda padrão para classificação binária. Combina internamente a função Sigmoid com a Binary Cross-Entropy em uma única operação numericamente estável:

L(x, y) = −[y · log(σ(x)) + (1 − y) · log(1 − σ(x))]

Adaptação ao desbalanceamento — pos_weight: O dataset possui 3.875 casos de Pneumonia para apenas 1.341 Normais (razão ~2.9:1). Sem correção, o modelo tende a prever sempre "Pneumonia" e ainda assim ter boa acurácia. O parâmetro pos_weight penaliza mais os erros na classe minoritária:

pos_weight = n_negativos / n_positivos = 1341 / 3875 ≈ 0.346

Valor calculado dinamicamente no código (compute_class_weights em utils.py) e passado ao critério a cada experimento, garantindo que cada classe contribua igualmente para o gradiente.

⚙️ Otimizador Adam (PyTorch — Kingma & Ba, 2015)

Adam (Adaptive Moment Estimation) é um otimizador que mantém taxas de aprendizado adaptativas por parâmetro, combinando os benefícios do Momentum e do RMSProp. Atualiza os pesos usando estimativas do primeiro momento (média) e segundo momento (variância não centralizada) dos gradientes:

θt = θt-1 − α · m̂t / (√v̂t + ε)
onde m̂ = média dos gradientes corrigida; v̂ = variância dos gradientes corrigida
LR = 0.001
Exp 1 (Feature Extraction)
LR = 0.0001
Exp 2 e 3 (Fine-Tuning)
LR menor para não destruir pesos pré-treinados
Weight Decay = 10⁻⁴
Regularização L2 em todos os experimentos
📊 ReduceLROnPlateau (PyTorch)

Scheduler que reduz automaticamente a taxa de aprendizado quando uma métrica monitorada para de melhorar. Implementa a estratégia de "diminuir ao estagnар": se a validation loss não melhora por patience épocas consecutivas, o LR é multiplicado por um fator de redução.

LRnovo = LRatual × fator   (se val_loss não melhora por N épocas)

Configuração usada neste projeto: Monitorando val_loss (mode='min'), fator de redução = 0.1 (LR multiplica por 10%), paciência = 5 épocas, LR mínimo = 10⁻⁷. Isso permite que o modelo "cruze" platôs de gradiente sem travar em mínimos locais.

🛑 Early Stopping — Implementação Própria

Early Stopping é uma técnica de regularização que interrompe o treinamento quando a performance no conjunto de validação para de melhorar, prevenindo overfitting. A implementação foi desenvolvida do zero na classe EarlyStopping em utils.py, sem uso de biblioteca externa:

// Lógica implementada (utils.py, linha ~249)
se val_loss não melhorar: contador += 1
se contador ≥ patience (10): interrompe treino
se val_loss melhorar: salva model.state_dict() → best_model.pth

Isso garante que o modelo salvo seja sempre o de melhor performance na validação, não o da última época. Todos os 3 experimentos convergiram antes do limit máximo de épocas (Exp1: 11/30, Exp2: 14/50, Exp3: 16/50).

Arquiteturas e Técnicas Utilizadas

Transfer Learning

Transfer Learning é uma técnica de Deep Learning em que um modelo previamente treinado em uma tarefa de grande escala — neste projeto, classificação de 1.000 classes do ImageNet — tem seus pesos reutilizados como ponto de partida para uma nova tarefa. Em vez de treinar uma rede neural do zero (o que exigiria milhões de imagens e semanas de processamento), aproveitamos o conhecimento já adquirido pelo modelo sobre formas, texturas, bordas e padrões visuais genéricos.

No domínio médico, como imagens de raio-X, o Transfer Learning é especialmente valioso porque os datasets são tipicamente menores e mais difíceis de obter. As representações aprendidas no ImageNet (bordas, gradientes, padrões) transferem-se bem para a detecção de padrões translúcidos da pneumonia.

🔒 Feature Extraction

Todas as camadas convolucionais do backbone ficam congeladas (pesos não atualizados). Apenas o classificador final customizado é treinado. Funciona como um extrator de características fixo: a rede transforma a imagem em um vetor de features e o classificador aprende a mapear essas features para as classes do problema.

🔓 Fine-Tuning Parcial

As últimas N camadas do backbone são descongeladas e têm seus pesos ajustados com um learning rate menor. As camadas finais capturam features mais específicas ao problema, enquanto as iniciais (que detectam padrões mais genéricos como bordas) permanecem intactas para preservar o conhecimento transferido.

ResNet50 — Redes com Conexões Residuais

A ResNet50 (Residual Network com 50 camadas) foi proposta por He et al. (2016) para resolver o problema de vanishing gradient em redes muito profundas. O seu mecanismo central são os blocos residuais (skip connections): em vez de aprender a transformação completa F(x), cada bloco aprende apenas o resíduo F(x) + x.

// Bloco Residual (simplificado)
saída = F(x) + x        // skip connection soma a entrada diretamente
F(x) = Conv → BN → ReLU → Conv → BN  // transformação residual
~25M
Parâmetros totais
50
Camadas
2048
Features de saída

Adaptação neste projeto: A camada fully connected original (fc) foi substituída pelo classificador customizado de 3 camadas lineares com Dropout e ReLU. Foram realizados dois experimentos: Feature Extraction (todas as camadas congeladas, Exp 1) e Fine-Tuning Parcial com as últimas 10 camadas descongeladas (Exp 2).

DenseNet121 — Redes com Conexões Densas

A DenseNet121 (Densely Connected Network com 121 camadas) foi proposta por Huang et al. (2017). Ao contrário da ResNet onde a skip connection soma as features, na DenseNet cada camada recebe como entrada a concatenação de todas as saídas das camadas anteriores do mesmo bloco denso (Dense Block). Isto promove reutilização intensa de features e reduz drasticamente o número de parâmetros necessários.

// Camada dentro de um Dense Block
xl = Hl([x0, x1, ..., xl-1])  // concatena TODAS as camadas anteriores
Hl(·) = BN → ReLU → Conv(3×3)    // transformação composta
~8M
Parâmetros totais
121
Camadas
1024
Features de saída

Por que DenseNet121 supera ResNet50 neste problema? Com apenas ~8M de parâmetros (vs. ~25M da ResNet50), a DenseNet sofre menos de overfitting em datasets restritos. A reutilização densa de features multiescala é especialmente eficaz para capturar as opacidades translúcidas e sutis da pneumonia em raio-X, onde múltiplos níveis de abstração precisam ser combinados.

Adaptação neste projeto: A camada classifier original foi substituída pelo classificador customizado. Fine-Tuning Parcial com as últimas 20 camadas descongeladas (Exp 3), learning rate reduzido para 1×10⁻⁴ e batch size menor (16) para melhor estabilidade de gradiente.

Referências das Arquiteturas

  • He, K. et al. Deep Residual Learning for Image Recognition. CVPR, 2016. — ResNet
  • Huang, G. et al. Densely Connected Convolutional Networks. CVPR, 2017. — DenseNet
  • Deng, J. et al. ImageNet: A Large-Scale Hierarchical Image Database. CVPR, 2009. — ImageNet
  • Mooney, P. Chest X-Ray Images (Pneumonia). Kaggle, 2018. — Dataset
  • PyTorch torchvision — Modelos pré-treinados utilizados

Experimentos

Experimento 1 — Feature Extraction
Modelo Base ResNet50
Estratégia Feature Extraction
Camadas Descongeladas 0 (todas congeladas)
Learning Rate 0.001
Batch Size 32
Épocas Treinadas 11 / 30

Todas as camadas convolucionais congeladas. Apenas o classificador final é treinado.

84,78%
Accuracy
86,90%
F1-Score
93,19%
AUC-ROC
Experimento 2 — Fine-Tuning Parcial
Modelo Base ResNet50
Estratégia Fine-Tuning Parcial
Camadas Descongeladas 10
Learning Rate 0.0001
Batch Size 32
Épocas Treinadas 14 / 50

Últimas 10 camadas descongeladas com learning rate menor para ajuste fino.

87,34%
Accuracy
90,38%
F1-Score
93,90%
AUC-ROC
Experimento 3 — DenseNet Fine-Tuning
Modelo Base DenseNet121
Estratégia Fine-Tuning Parcial
Camadas Descongeladas 20
Learning Rate 0.0001
Batch Size 16
Épocas Treinadas 16 / 50

DenseNet121 com 20 camadas descongeladas e batch size menor para melhor generalização.

91,51%
Accuracy
93,23%
F1-Score
96,78%
AUC-ROC

Resultados Comparativos

Equações das Métricas de Avaliação

A avaliação de modelos de classificação binária médica utiliza métricas derivadas da Matriz de Confusão, onde: TP = Verdadeiros Positivos (Pneumonia corretamente identificada), TN = Verdadeiros Negativos, FP = Falsos Positivos e FN = Falsos Negativos.

Acurácia (Accuracy)
(TP + TN) / (TP + TN + FP + FN)
Proporção de predições corretas sobre o total de amostras.
Precisão (Precision)
TP / (TP + FP)
Dos casos classificados como Pneumonia, quantos realmente são. Minimiza alarmes falsos.
Sensibilidade (Recall / Sensitivity)
TP / (TP + FN)
Dos casos reais de Pneumonia, quantos o modelo detectou. Crítica para minimizar falsos negativos (diagnósticos perdidos).
Especificidade (Specificity)
TN / (TN + FP)
Dos casos normais, quantos foram corretamente identificados como normais. Minimiza tratamentos desnecessários.
F1-Score
2 × (Precisão × Sensibilidade) / (Precisão + Sensibilidade)
Média harmônica entre Precisão e Sensibilidade. Fundamental para datasets desbalanceados.
AUC-ROC
AUC = ∫ TPR d(FPR)
Área sob a Curva ROC (TPR vs FPR). Um AUC de 1.0 = classificador perfeito; 0.5 = aleatório. Independe do limiar de decisão.

Tabela Comparativa de Métricas

Métrica Exp 1 — ResNet50
Feature Extraction
Exp 2 — ResNet50
Fine-Tuning
Exp 3 — DenseNet121
Fine-Tuning
Accuracy 84,78% 87,34% 91,51%
Precision 94,03% 86,08% 92,88%
Sensitivity (Recall) 80,77% 95,13% 93,59%
Specificity 91,45% 74,36% 88,03%
F1-Score 86,90% 90,38% 93,23%
AUC-ROC 93,19% 93,90% 96,78%

Exp 1 — Matriz de Confusão

Pred Normal
Pred Pneumonia
Real Normal
214
20
Real Pneumonia
75
315

Exp 2 — Matriz de Confusão

Pred Normal
Pred Pneumonia
Real Normal
174
60
Real Pneumonia
19
371

Exp 3 — Matriz de Confusão

Pred Normal
Pred Pneumonia
Real Normal
206
28
Real Pneumonia
25
365

Visualizações

Comparação de Métricas
Comparação das métricas entre os 3 experimentos
Comparação de Curvas ROC
Curvas ROC comparativas
Comparação de Matrizes de Confusão
Matrizes de confusão lado a lado
Distribuição de Classes
Distribuição das classes no dataset
Evolução das Métricas
Evolução das métricas ao longo do treinamento
Curvas de Treinamento - Exp 1
Curvas de Loss e Accuracy durante o treinamento
Matriz de Confusão - Exp 1
Matriz de Confusão no conjunto de teste
Curva ROC - Exp 1
Curva ROC (AUC = 0.9319)
Exemplos de Predições - Exp 1
Exemplos de predições corretas e incorretas
Curvas de Treinamento - Exp 2
Curvas de Loss e Accuracy durante o treinamento
Matriz de Confusão - Exp 2
Matriz de Confusão no conjunto de teste
Curva ROC - Exp 2
Curva ROC (AUC = 0.9390)
Exemplos de Predições - Exp 2
Exemplos de predições corretas e incorretas
Curvas de Treinamento - Exp 3
Curvas de Loss e Accuracy durante o treinamento
Matriz de Confusão - Exp 3
Matriz de Confusão no conjunto de teste
Curva ROC - Exp 3
Curva ROC (AUC = 0.9678)
Exemplos de Predições - Exp 3
Exemplos de predições corretas e incorretas

Conclusão

Melhor Modelo: Experimento 3 — DenseNet121 (Fine-Tuning Parcial)

O Experimento 3 utilizando DenseNet121 com fine-tuning parcial (20 camadas descongeladas) obteve os melhores resultados gerais, alcançando 91,51% de accuracy, 93,23% de F1-Score e 96,78% de AUC-ROC. Este modelo apresentou o melhor equilíbrio entre sensibilidade (93,59%) e especificidade (88,03%), sendo o mais adequado para aplicação clínica.

Principais Descobertas

  • Fine-tuning supera Feature Extraction — Descongelar camadas melhora a performance significativamente
  • DenseNet121 supera ResNet50 — A arquitetura com conexões densas se mostrou mais eficaz para este domínio
  • Early Stopping eficiente — Todos os modelos convergiram bem antes do limite de épocas
  • Class weights essenciais — O balanceamento das classes melhorou a performance em ambas as categorias

Análise por Experimento

  • Exp 1: Alta precisão (94,03%) mas baixa sensibilidade (80,77%) — tende a ser conservador nas predições de pneumonia
  • Exp 2: Alta sensibilidade (95,13%) mas baixa especificidade (74,36%) — detecta bem pneumonia mas gera mais falsos positivos
  • Exp 3: Melhor equilíbrio geral entre todas as métricas, sendo o mais robusto e confiável

Estrutura do Projeto

Trabalho_Final_PIVC/
├── config.py                  # Configurações e hiperparâmetros
├── train.py                   # Script de treinamento
├── evaluate.py                # Script de avaliação
├── utils.py                   # Funções auxiliares
├── Trabalho_Final_PIVC.ipynb   # Notebook Jupyter
├── data/chest_xray/            # Dataset de raio-X
│   ├── train/                    # Imagens de treino
│   ├── val/                      # Imagens de validação
│   └── test/                     # Imagens de teste
└── results/                   # Resultados e gráficos
    ├── exp1/                    # Resultados do Experimento 1
    ├── exp2/                    # Resultados do Experimento 2
    ├── exp3/                    # Resultados do Experimento 3
    └── comparison.csv           # Tabela comparativa

Notebook do Projeto

Trabalho_Final_PIVC.ipynb

Notebook Jupyter com todo o código, análises e visualizações do projeto.

  • Carregamento e exploração do dataset
  • Pré-processamento e Data Augmentation
  • Treinamento dos 3 experimentos
  • Avaliação com métricas completas
  • Visualizações e análise comparativa
Download do Notebook

Arquivo .ipynb — Abrir com Jupyter Notebook ou VS Code

Como visualizar o notebook

1
Jupyter Notebook

Abrir com jupyter notebook no terminal

2
VS Code

Abrir diretamente com a extensão Jupyter

3
Google Colab

Fazer upload do .ipynb no colab.google.com