From 00c5e86967c7b3696cc55387a176be9fcda302cc Mon Sep 17 00:00:00 2001 From: Cesa-V Date: Wed, 17 Jun 2026 10:54:27 -0300 Subject: [PATCH] Versiona HTMLs renderizados do Quarto Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 3 - .../apresentacao/apresentacao_svm_rgb.html | 2955 +++++++++++++++++ trabalho/amostra/trabalho_svm_rgb.html | 2701 +++++++++++++++ .../completo/trabalho_svm_rgb_completo.html | 2332 +++++++++++++ 4 files changed, 7988 insertions(+), 3 deletions(-) create mode 100644 trabalho/amostra/apresentacao/apresentacao_svm_rgb.html create mode 100644 trabalho/amostra/trabalho_svm_rgb.html create mode 100644 trabalho/completo/trabalho_svm_rgb_completo.html diff --git a/.gitignore b/.gitignore index 4b2dd28..8b1aa37 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,6 @@ trabalho/RGB/ # Cache de modelos e features (arquivos pesados, gerados pelo codigo) *.rds -# HTMLs renderizados (gerados pelo Quarto) -*.html - # Configuracoes locais de assistentes .claude/ .vscode/ \ No newline at end of file diff --git a/trabalho/amostra/apresentacao/apresentacao_svm_rgb.html b/trabalho/amostra/apresentacao/apresentacao_svm_rgb.html new file mode 100644 index 0000000..ea6267a --- /dev/null +++ b/trabalho/amostra/apresentacao/apresentacao_svm_rgb.html @@ -0,0 +1,2955 @@ + + + + + + + + + + + + + + Classificação de Imagens RGB com SVM + + + + + + + + + + + + + + + +
+
+ +
+

Classificação de Imagens RGB com SVM

+

CEAO-802 — Métodos de Análise de Dados

+ +
+
+
+1T Generoso · 1T João Marcos · 1T Vitor Cesa +
+
+
+ +

2026-06-17

+
+
+

Roteiro

+
    +
  1. Contexto e objetivo
  2. +
  3. Fundamentos: Transfer Learning e SVM
  4. +
  5. Pipeline em R — código-chave
  6. +
  7. Cinco modelos avaliados
  8. +
  9. Comparação de resultados
  10. +
  11. Validação cruzada K-Fold
  12. +
  13. Discussão e conclusão
  14. +
+
+
+

Contexto e Objetivo

+
+
+
    +
  • Imagens RGB de cenas urbanas por drone — Guaratinguetá (SP)
  • +
  • 13 classes de cobertura do solo (C1–C13); dataset: RGB + TIR
  • +
  • Este trabalho: apenas canal RGB
  • +
  • Amostra: 50 imagens / classe · 650 imagens totais (de 1.248)
  • +
  • Divisão: 70% treino (~455) / 30% teste (~195), estratificada
  • +
+
+

Objetivo

+
+

Classificação supervisionada de cenas urbanas usando Transfer Learning como extrator de atributos e SVM como classificador.

+
+

Baseline aleatório (13 classes):

+

7,7%

+

Qualquer modelo acima disso aprende algo real.

+
+
+
+

As 13 Classes do Dataset

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ClasseCobertura
C1Vegetação Arbórea
C2Vegetação Rasteira
C3Solo Exposto
C4Pavimento Asfáltico
C5Solo Estabilizado
C6Telhado Cerâmico
C7Telhado de Fibrocimento
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ClasseCobertura
C8Telhado Metálico
C9Placa Fotovoltaica
C10Piscina
C11Pedra Sabão
C12Caixa D’água
C13Veículos
+
+
+

Arquivo: C10_2023_06_M_01_RGB.tiff → classe C10, junho 2023, manhã. A classe é inferida do prefixo do nome — sem estrutura de subpastas.

+
+
+
+

Por que Transfer Learning?

+
+
+

Abordagem tradicional

+

Extrair atributos estatísticos manualmente: média, desvio-padrão, histogramas de cor

+

❌ Representação rasa — perde padrões espaciais e texturais complexos

+
+

Abordagem adotada

+

MobileNetV2 pré-treinada no ImageNet (>1 M imagens · 1.000 classes · Google)

+
    +
  • Remove a camada classificadora final
  • +
  • Aplica pooling global médio
  • +
  • Gera vetor de 1.280 atributos por imagem
  • +
+

✅ Bordas, texturas e formas de alto nível, aprendidas em escala industrial

+
+
+

A CNN faz o trabalho de representação. O SVM recebe os 1.280 atributos e aprende a fronteira de decisão — sem re-treinar a CNN.

+
+
+
+

O que é SVM?

+
+
+

Ideia central

+

Encontrar o hiperplano que maximiza a margem entre as classes.

+

Parâmetros principais

+
    +
  • cost — tolerância a erros; maior cost = fronteira mais rígida
  • +
  • gamma — alcance do kernel radial; maior gamma = vizinhança menor
  • +
+

Kernel trick

+

Kernel radial (RBF) permite fronteiras não-lineares projetando os dados em espaço de alta dimensão

+
+

Dois kernels testados

+ + + + + + + + + + + + + + + + + + + + +
KernelFronteiraParâmetros
LinearHiperplanocost
RadialNão-linearcost + gamma
+
+

SVM é sensível à escala → atributos devem ser padronizados antes do treino.

+
+
+
+
+

Pipeline Geral

+
+
+650 imagens
RGB +
+ +
+MobileNetV2
1.280 atributos +
+ +
+center + scale
nearZeroVar +
+ +
+5 modelos
SVM · RF +
+ +
+Holdout
K-Fold k=5 +
+
+
+
+

Código — Leitura e Amostra

+
+
# Listar imagens e inferir classe do prefixo do nome
+arquivos <- list.files(path = caminho_rgb,
+  pattern = "\\.(jpg|jpeg|png|bmp|tif|tiff)$",
+  recursive = TRUE, full.names = TRUE)
+
+classe_inferida <- stringr::str_extract(nome_sem_extensao, "^C\\d+")
+
+# Amostra estratificada e balanceada: 50 imagens/classe
+set.seed(123)
+metadados <- metadados_completo %>%
+  group_by(classe) %>%
+  group_modify(~ slice_sample(.x, n = min(50, nrow(.x)))) %>%
+  ungroup()
+# → 650 imagens totais (50 × 13 classes)
+
+

O que faz: lista os TIFFs, extrai a classe C1C13 do prefixo e sorteia 50 imagens por classe com semente fixa.

+
+
+

Código — Extração com MobileNetV2

+
+
# Carregar MobileNetV2 sem a camada classificadora
+modelo_base <- application_mobilenet_v2(
+  include_top  = FALSE,      # remove o bloco de 1.000 classes
+  weights      = "imagenet", # pesos pré-treinados no ImageNet
+  pooling      = "avg",      # GlobalAveragePooling → vetor 1D
+  input_shape  = c(224L, 224L, 3L)
+)
+
+# Normalização específica da MobileNetV2: pixels → [-1, 1]
+batch_prep <- (batch_array / 127.5) - 1.0
+
+# features: matriz  n × 1.280  (processado em lotes de 16)
+features <- predict(modelo_base, batch_prep, verbose = 0L)
+
+

O que faz: carrega MobileNetV2, normaliza pixels para [−1, 1] e extrai 1.280 atributos por imagem. Resultado salvo em cache .rds.

+
+
+

Código — Pré-processamento e Treino

+
+
# Divisão estratificada 70/30
+idx_treino <- createDataPartition(dados$classe, p = 0.70, list = FALSE)
+
+# Remover atributos de variância quase nula
+variaveis_nzv <- nearZeroVar(x_treino)
+
+# Padronizar: média 0, desvio 1 (essencial para o SVM)
+preproc       <- preProcess(x_treino, method = c("center", "scale"))
+x_treino_norm <- predict(preproc, x_treino)
+x_teste_norm  <- predict(preproc, x_teste)
+
+# M1 — SVM linear
+modelo_svm_linear <- svm(x = x_treino_norm, y = y_treino,
+  kernel = "linear", cost = 1, scale = FALSE)
+
+

O que faz: divide 70/30, padroniza e treina. O mesmo fluxo se repete para cada um dos 5 modelos (parâmetros variam).

+
+
+

Os 5 Modelos

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#ModeloTipoParâmetros
1SVM linearLinearcost = 1
2SVM radialRBFcost = 10 · gamma = 0,01 (fixos)
3SVM radial ajustadoRBFcost e gamma por grade 4×4 + CV
4PCA + SVM radialRBFPCA (≥ 95% var., máx. 30 comp.) + RBF
5Random ForestEnsemble500 árvores · importance = TRUE
+
+

O PCA reduz os 1.280 atributos aos componentes que explicam ≥ 95% da variância antes de treinar o SVM, eliminando redundâncias.

+
+
+
+

Comparação — Holdout 70/30

+ +
+

Baseline aleatório (13 classes) = 7,7% — SVM linear (~40%) é 5,2× melhor que o acaso.

+
+
+
+

Melhor Modelo — SVM Linear

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
MétricaHoldoutK-Fold
Acurácia40,0%40,2%
Kappa0,3500,352
F1 macro0,4020,409
+


+
+

Holdout e K-Fold concordam → resultado estável.

+
+
+
+
+
+
+

+
+
+
+
+
+
+
+

Validação Cruzada K-Fold (k = 5)

+ +
+

Por que k-fold? O holdout avalia uma única divisão aleatória. O k-fold usa todas as imagens para avaliação, em 5 rodadas independentes — estimativa mais robusta.

+
+
+
+

Holdout × K-Fold

+ +
+
+

Importância das Variáveis — Random Forest

+ +
+

feat_0472 e feat_0260 têm importância ~70% maior que a 3ª colocada. São índices de neurônios internos da CNN — sem interpretação visual direta.

+
+
+
+

Discussão

+

Resultado principal

+
    +
  • Melhor modelo: SVM linear com acurácia k-fold de 40.2% (~5,2× acima do acaso)
  • +
  • Holdout e k-fold convergiram → resultado estável e confiável
  • +
+
+

Achado mais relevante — SVM linear superou o SVM radial com parâmetros fixos (14%). Os atributos da MobileNetV2 formam um espaço quase linearmente separável — o kernel linear é suficiente.

+
+

Trabalhos futuros

+
    +
  • Outros extratores CNN: EfficientNetB0 · ResNet50 · VGG19
  • +
  • Incorporar o canal TIR ao vetor de atributos
  • +
  • Aumentar a amostra por classe (50 imagens/classe) e fazer fine-tuning da CNN
  • +
+
+
+

Conclusão

+


+
+

Transfer learning combina o poder representacional das CNNs com a interpretabilidade dos classificadores clássicos — sem treinar a rede do zero.

+
+


+
+

A abordagem é simples, reprodutível e já supera em 5× o baseline aleatório com apenas 50 imagens por classe — base sólida para extensões futuras.

+
+
+
+

+



+

Obrigado — Perguntas?

+


+

1T Generoso · 1T João Marcos · 1T Vitor Cesa

+

CEAO-802 — Métodos de Análise de Dados

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trabalho/amostra/trabalho_svm_rgb.html b/trabalho/amostra/trabalho_svm_rgb.html new file mode 100644 index 0000000..967f9b5 --- /dev/null +++ b/trabalho/amostra/trabalho_svm_rgb.html @@ -0,0 +1,2701 @@ + + + + + + + + + + + + +Classificacao de Imagens RGB com SVM + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+

Classificacao de Imagens RGB com SVM

+

CEAO-802 - Metodos de Analise de Dados

+
+ + + +
+ +
+
Authors
+
+

1T Generoso

+

1T João Marcos

+

1T Vitor Cesa

+
+
+ + + +
+ + + +
+ + +
+

1 Introducao

+

Este trabalho aplica tecnicas de classificacao supervisionada ao dataset RGB disponibilizado na disciplina CEAO-802 - Metodos de Analise de Dados.

+

O documento de datasets apresenta um problema de classificacao de cenas urbanas com imagens RGB e TIR coletadas por drone sobre Guaratingueta (SP), com 13 classes de cobertura urbana. Nesta analise sera utilizada apenas a parte RGB do dataset, armazenada localmente em D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB.

+

A abordagem adotada segue a sugestao do material da disciplina: em vez de extrair atributos estatisticos simples (media, desvio, histogramas), serao utilizadas redes neurais convolucionais pre-treinadas como extratores de atributos (transfer learning). A rede MobileNetV2, pre-treinada no ImageNet, e carregada sem a camada de classificacao final. Cada imagem e redimensionada para 224x224 pixels e processada pela rede, que produz um vetor de 1280 atributos representando caracteristicas visuais de alto nivel aprendidas em milhoes de imagens. Esses vetores sao entao usados como entrada para os classificadores SVM e Random Forest.

+

A aula de Support Vector Machine (SVM) apresentou o metodo como um classificador baseado na busca de um hiperplano de separacao entre classes. A ideia central e encontrar uma fronteira que maximize a margem entre os grupos. Quando os dados nao sao linearmente separaveis, o SVM pode usar variaveis de folga, controladas pelo parametro cost, e tambem kernels, como o kernel radial ou RBF, para criar fronteiras de decisao nao lineares por meio do chamado kernel trick.

+

Como a pasta RGB possui grande volume de dados, sera usada uma amostra estratificada e balanceada de 50 imagens por classe. Essa escolha torna o processamento viavel em um computador comum, preservando a comparacao entre as classes.

+

Neste relatorio, serao avaliados:

+
    +
  1. SVM com kernel linear;
  2. +
  3. SVM com kernel radial;
  4. +
  5. SVM radial com ajuste simples de hiperparametros;
  6. +
  7. PCA + SVM radial;
  8. +
  9. Random Forest, como modelo de comparacao.
  10. +
+
+
+

2 Preparacao do ambiente

+
+
# ============================================================
+# 1. Instalacao e carregamento dos pacotes
+# ============================================================
+
+pacotes <- c(
+  "tidyverse",
+  "keras3",
+  "caret",
+  "e1071",
+  "randomForest",
+  "knitr"
+)
+
+pacotes_faltando <- setdiff(pacotes, rownames(installed.packages()))
+
+if (length(pacotes_faltando) > 0) {
+  install.packages(pacotes_faltando)
+}
+
+library(tidyverse)
+library(keras3)
+library(caret)
+library(e1071)
+library(randomForest)
+library(knitr)
+
+backend_ativo <- tryCatch(config_backend(), error = function(e) NA_character_)
+cat("Backend keras3:", backend_ativo, "\n")
+
+
Backend keras3: tensorflow 
+
+
+
+
+

3 Caminho do dataset

+

Em R, e mais seguro usar barras / no caminho, mesmo no Windows.

+
+
# ============================================================
+# 2. Caminho do dataset RGB
+# ============================================================
+
+caminho_rgb <- "D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB"
+
+if (!dir.exists(caminho_rgb)) {
+  stop("A pasta do dataset RGB nao foi encontrada. Verifique o caminho informado em caminho_rgb.")
+}
+
+caminho_rgb
+
+
[1] "D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB"
+
+
+
+
+

4 Leitura dos arquivos de imagem

+

O codigo procura imagens dentro da pasta RGB. Se o dataset estiver organizado em subpastas, o nome da subpasta sera usado como classe. No dataset usado neste trabalho, os arquivos TIFF estao diretamente na pasta RGB, entao a classe e inferida pelo prefixo do nome do arquivo, como C1, C2, …, C13.

+
+
# ============================================================
+# 3. Listar imagens
+# ============================================================
+
+extensoes_imagem <- "\\.(jpg|jpeg|png|bmp|tif|tiff)$"
+
+arquivos <- list.files(
+  path = caminho_rgb,
+  pattern = extensoes_imagem,
+  recursive = TRUE,
+  full.names = TRUE,
+  ignore.case = TRUE
+)
+
+if (length(arquivos) == 0) {
+  stop("Nenhuma imagem foi encontrada na pasta RGB.")
+}
+
+length(arquivos)
+
+
[1] 1248
+
+
+
+
# ============================================================
+# 4. Criar metadados
+# ============================================================
+
+obter_classe <- function(arquivo, raiz) {
+  arquivo_norm <- normalizePath(arquivo, winslash = "/", mustWork = FALSE)
+  raiz_norm <- normalizePath(raiz, winslash = "/", mustWork = FALSE)
+
+  nome_sem_extensao <- tools::file_path_sans_ext(basename(arquivo))
+  classe_inferida <- stringr::str_extract(nome_sem_extensao, "^C\\d+")
+
+  if (!is.na(classe_inferida)) {
+    return(classe_inferida)
+  }
+  
+  caminho_relativo <- stringr::str_remove(
+    arquivo_norm,
+    paste0("^", stringr::fixed(raiz_norm), "/?")
+  )
+  
+  partes <- strsplit(caminho_relativo, "/", fixed = TRUE)[[1]]
+  
+  if (length(partes) >= 2) {
+    return(partes[1])
+  }
+  
+  stringr::str_extract(nome_sem_extensao, "^[^_-]+")
+}
+
+metadados_completo <- tibble(
+  arquivo = arquivos,
+  classe = map_chr(arquivos, obter_classe, raiz = caminho_rgb)
+) %>%
+  mutate(
+    classe = factor(classe, levels = paste0("C", 1:13)),
+    tamanho_mb = file.info(arquivo)$size / 1024^2
+  ) %>%
+  arrange(classe, arquivo)
+
+resumo_dataset <- metadados_completo %>%
+  group_by(classe) %>%
+  summarise(
+    n_imagens = n(),
+    tamanho_total_mb = sum(tamanho_mb, na.rm = TRUE),
+    tamanho_medio_mb = mean(tamanho_mb, na.rm = TRUE),
+    .groups = "drop"
+  )
+
+resumo_dataset %>%
+  mutate(across(where(is.numeric), ~ round(.x, 2))) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classen_imagenstamanho_total_mbtamanho_medio_mb
C1963388.3135.29
C2963388.3135.29
C3963388.3135.29
C4963388.3135.29
C5963388.3135.29
C6963388.3135.29
C7963388.3135.29
C8963388.3135.29
C9963388.3135.29
C10963388.3135.29
C11963388.3135.29
C12963388.3135.29
C13963388.3135.29
+
+
+
+
+

5 Amostra balanceada

+

A amostragem estratificada foi usada para manter a mesma quantidade de imagens em cada classe. Isso evita que classes maiores dominem o treinamento e reduz o tempo de processamento.

+
+
# ============================================================
+# 5. Criar amostra balanceada por classe
+# ============================================================
+
+set.seed(123)
+
+max_imagens_por_classe <- 50
+
+metadados <- metadados_completo %>%
+  group_by(classe) %>%
+  group_modify(~ {
+    qtd <- min(max_imagens_por_classe, nrow(.x))
+    slice_sample(.x, n = qtd)
+  }) %>%
+  ungroup() %>%
+  mutate(classe = factor(classe, levels = paste0("C", 1:13)))
+
+cat("Total de imagens no dataset completo:", nrow(metadados_completo), "\n")
+
+
Total de imagens no dataset completo: 1248 
+
+
cat("Total de imagens na amostra:", nrow(metadados), "\n")
+
+
Total de imagens na amostra: 650 
+
+
metadados %>%
+  count(classe, name = "n_imagens_amostra") %>%
+  arrange(classe) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classen_imagens_amostra
C150
C250
C350
C450
C550
C650
C750
C850
C950
C1050
C1150
C1250
C1350
+
+
+
+
ggplot(metadados, aes(x = classe)) +
+  geom_bar(fill = "#2C7FB8") +
+  coord_flip() +
+  labs(
+    title = "Distribuicao de imagens por classe na amostra",
+    x = "Classe",
+    y = "Numero de imagens"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

6 Modelo pre-treinado (Transfer Learning)

+

A estrategia de transfer learning consiste em usar uma rede neural convolucional (CNN) ja treinada em um grande dataset — neste caso, o ImageNet, com mais de 1 milhao de imagens e 1000 classes — e aproveitá-la como extrator de atributos.

+

A rede escolhida e a MobileNetV2, uma arquitetura compacta e eficiente desenvolvida pelo Google. Ela e carregada sem a camada de classificacao final (include_top = FALSE) e com pooling global medio (pooling = "avg"), o que produz um vetor de 1280 atributos por imagem. Esses atributos representam caracteristicas visuais de alto nivel (bordas, texturas, formas, padroes complexos) que a rede aprendeu ao longo de seu treinamento no ImageNet.

+
+
# ============================================================
+# 6. Carregar modelo pre-treinado MobileNetV2
+# ============================================================
+
+dir.create("outputs", showWarnings = FALSE)
+
+modelo_base <- application_mobilenet_v2(
+  include_top = FALSE,
+  weights    = "imagenet",
+  pooling    = "avg",
+  input_shape = c(224L, 224L, 3L)
+)
+
+cat("Modelo carregado:", modelo_base$name, "\n")
+
+
Modelo carregado: mobilenetv2_1.00_224 
+
+
cat("Dimensao do vetor de features:", modelo_base$output_shape[[2]], "\n")
+
+
Dimensao do vetor de features: 1280 
+
+
+
+
+

7 Extracao de atributos por Transfer Learning

+

Cada imagem e redimensionada para 224x224 pixels (formato padrao do MobileNetV2), normalizada com a funcao de preprocessamento especifica da rede e entao passada pela CNN. O vetor de saida e armazenado como atributos da imagem.

+

O processamento e feito em lotes (batch) para maior eficiencia, e o resultado e armazenado em cache para evitar reprocessamento em execucoes posteriores.

+
+
# ============================================================
+# 7. Extracao de features via CNN pre-treinada (com cache)
+# ============================================================
+
+pasta_saida <- "outputs/parcial"
+dir.create(pasta_saida, recursive = TRUE, showWarnings = FALSE)
+
+arquivo_cache_features <- file.path(pasta_saida, "features_rgb_mobilenetv2.rds")
+arquivo_cache_parcial  <- file.path(pasta_saida, "features_rgb_mobilenetv2_temp.rds")
+
+tamanho_imagem <- 224L
+tamanho_lote   <- 16L
+
+extrair_features_batch <- function(arquivos, modelo, tamanho = 224L, lote = 16L) {
+  n <- length(arquivos)
+  resultados <- matrix(NA_real_, nrow = n, ncol = as.integer(modelo$output_shape[[2]]))
+
+  for (inicio in seq(1, n, by = lote)) {
+    fim <- min(inicio + lote - 1L, n)
+    bloco <- arquivos[inicio:fim]
+    tam_bloco <- length(bloco)
+
+    batch_array <- array(0, dim = c(tam_bloco, tamanho, tamanho, 3L))
+
+    for (j in seq_len(tam_bloco)) {
+      img <- image_load(bloco[j], target_size = c(tamanho, tamanho))
+      batch_array[j, , , ] <- image_to_array(img)
+    }
+
+    batch_prep <- (batch_array / 127.5) - 1.0
+    features   <- predict(modelo, batch_prep, verbose = 0L)
+    resultados[inicio:fim, ] <- features
+  }
+
+  resultados
+}
+
+if (file.exists(arquivo_cache_features)) {
+  dados <- readRDS(arquivo_cache_features)
+  cat("Features carregadas do cache:", arquivo_cache_features, "\n")
+} else {
+  cat("Extraindo features com MobileNetV2. Isso pode levar alguns minutos...\n")
+
+  erros_idx <- integer(0)
+
+  if (file.exists(arquivo_cache_parcial)) {
+    features_matrix <- readRDS(arquivo_cache_parcial)
+    linhas_prontas  <- which(complete.cases(features_matrix))
+    cat("Cache parcial encontrado:", length(linhas_prontas), "imagens ja processadas.\n")
+  } else {
+    n_features      <- as.integer(modelo_base$output_shape[[2]])
+    features_matrix <- matrix(NA_real_, nrow = nrow(metadados), ncol = n_features)
+    linhas_prontas  <- integer(0)
+  }
+
+  linhas_pendentes <- setdiff(seq_len(nrow(metadados)), linhas_prontas)
+
+  if (length(linhas_pendentes) > 0) {
+    for (inicio in seq(1, length(linhas_pendentes), by = tamanho_lote)) {
+      fim   <- min(inicio + tamanho_lote - 1L, length(linhas_pendentes))
+      idxs  <- linhas_pendentes[inicio:fim]
+      arquivos_bloco <- metadados$arquivo[idxs]
+
+      resultado <- tryCatch(
+        extrair_features_batch(arquivos_bloco, modelo_base, tamanho_imagem, length(idxs)),
+        error = function(e) {
+          message("Erro no lote ", inicio, "-", fim, ": ", conditionMessage(e))
+          erros_idx <<- c(erros_idx, idxs)
+          NULL
+        }
+      )
+
+      if (!is.null(resultado)) {
+        features_matrix[idxs, ] <- resultado
+      }
+
+      cat(sprintf("  Processadas: %d / %d\r", min(fim, length(linhas_pendentes)), length(linhas_pendentes)))
+
+      if (fim %% (tamanho_lote * 5L) == 0L) {
+        saveRDS(features_matrix, arquivo_cache_parcial)
+      }
+    }
+    cat("\n")
+    saveRDS(features_matrix, arquivo_cache_parcial)
+  }
+
+  imagens_ok <- complete.cases(features_matrix)
+  n_features <- ncol(features_matrix)
+  nomes_feat <- paste0("feat_", stringr::str_pad(seq_len(n_features), 4, pad = "0"))
+  colnames(features_matrix) <- nomes_feat
+
+  dados <- bind_cols(
+    metadados[imagens_ok, ],
+    as_tibble(features_matrix[imagens_ok, ])
+  )
+
+  if (length(erros_idx) > 0) {
+    erros_df <- metadados[erros_idx, ] %>% mutate(erro = "falha na extracao CNN")
+    arquivo_erros <- file.path(pasta_saida, "erros_processamento_rgb.csv")
+    write_csv(erros_df, arquivo_erros)
+    cat("Imagens com erro:", length(erros_idx), "(ver", arquivo_erros, ")\n")
+  }
+
+  saveRDS(dados, arquivo_cache_features)
+  if (file.exists(arquivo_cache_parcial)) file.remove(arquivo_cache_parcial)
+
+  cat("Features salvas em:", arquivo_cache_features, "\n")
+}
+
+
Features carregadas do cache: outputs/parcial/features_rgb_mobilenetv2.rds 
+
+
cat("Imagens na base final:", nrow(dados), "\n")
+
+
Imagens na base final: 650 
+
+
cat("Numero de atributos extraidos (MobileNetV2):", ncol(dados) - 3L, "\n")
+
+
Numero de atributos extraidos (MobileNetV2): 1280 
+
+
+
+
dados %>%
+  count(classe, name = "n_imagens_processadas") %>%
+  arrange(classe) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classen_imagens_processadas
C150
C250
C350
C450
C550
C650
C750
C850
C950
C1050
C1150
C1250
C1350
+
+
+
+
dados %>%
+  select(arquivo, classe, feat_0001, feat_0002, feat_0003, feat_0004, feat_0005) %>%
+  head(10) %>%
+  mutate(across(starts_with("feat_"), ~ round(.x, 4))) %>%
+  kable()
+
+ +++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
arquivoclassefeat_0001feat_0002feat_0003feat_0004feat_0005
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_09_T_03_RGB.tiffC10.49230.00000.02830.83410.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2024_03_T_03_RGB.tiffC10.00000.00000.00000.18680.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_12_M_03_RGB.tiffC10.16400.00000.00000.00000.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_07_T_02_RGB.tiffC10.03640.07820.00000.26440.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2024_02_M_03_RGB.tiffC10.00000.00450.00000.02600.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_11_M_02_RGB.tiffC10.00000.00000.04720.00270.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_12_M_02_RGB.tiffC10.24220.00000.00450.00000.1251
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_11_M_03_RGB.tiffC10.08650.00000.00761.19010.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2024_05_T_01_RGB.tiffC10.67390.00440.01480.00000.0000
D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB/RGB/C1_2023_09_M_01_RGB.tiffC10.37250.14050.00000.00200.0000
+
+
+
+
+

8 Separacao entre treino e teste

+

A separacao treino/teste sera feita de forma estratificada, mantendo a proporcao das classes sempre que possivel.

+
+
# ============================================================
+# 8. Separacao treino/teste
+# ============================================================
+
+set.seed(123)
+
+idx_treino <- createDataPartition(
+  y = dados$classe,
+  p = 0.70,
+  list = FALSE
+)
+
+treino <- dados[idx_treino, ]
+teste <- dados[-idx_treino, ]
+
+cat("Imagens no treino:", nrow(treino), "\n")
+
+
Imagens no treino: 455 
+
+
cat("Imagens no teste:", nrow(teste), "\n")
+
+
Imagens no teste: 195 
+
+
treino %>%
+  count(classe, name = "treino") %>%
+  full_join(
+    teste %>% count(classe, name = "teste"),
+    by = "classe"
+  ) %>%
+  arrange(classe) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classetreinoteste
C13515
C23515
C33515
C43515
C53515
C63515
C73515
C83515
C93515
C103515
C113515
C123515
C133515
+
+
+
+
+

9 Pre-processamento

+

Como o SVM e sensivel a escala das variaveis, os atributos serao centralizados e padronizados. Tambem serao removidas variaveis com variancia quase nula.

+
+
# ============================================================
+# 9. Preparar matrizes X e vetor y
+# ============================================================
+
+colunas_nao_preditoras <- c("arquivo", "classe", "tamanho_mb")
+
+x_treino <- treino %>%
+  select(-all_of(colunas_nao_preditoras))
+
+x_teste <- teste %>%
+  select(-all_of(colunas_nao_preditoras))
+
+y_treino <- droplevels(treino$classe)
+y_teste <- factor(teste$classe, levels = levels(y_treino))
+
+variaveis_nzv <- nearZeroVar(x_treino)
+
+if (length(variaveis_nzv) > 0) {
+  x_treino <- x_treino[, -variaveis_nzv, drop = FALSE]
+  x_teste <- x_teste[, colnames(x_treino), drop = FALSE]
+}
+
+preproc <- preProcess(
+  x_treino,
+  method = c("center", "scale")
+)
+
+x_treino_norm <- predict(preproc, x_treino)
+x_teste_norm <- predict(preproc, x_teste)
+
+cat("Preditores usados nos modelos:", ncol(x_treino_norm), "\n")
+
+
Preditores usados nos modelos: 1265 
+
+
+
+
+

10 Modelo 1: SVM linear

+

O SVM linear tenta separar as classes por hiperplanos lineares. E o modelo mais simples e serve como referencia inicial.

+
+
# ============================================================
+# 10. SVM linear
+# ============================================================
+
+set.seed(123)
+
+modelo_svm_linear <- svm(
+  x = x_treino_norm,
+  y = y_treino,
+  kernel = "linear",
+  cost = 1,
+  scale = FALSE
+)
+
+pred_svm_linear <- predict(modelo_svm_linear, x_teste_norm)
+
+cm_svm_linear <- confusionMatrix(pred_svm_linear, y_teste)
+
+cm_svm_linear
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   4  3  0  1  0  0  0  0  1   1   0   0   0
+       C2   4  6  0  0  0  2  0  0  0   0   0   0   1
+       C3   0  0  7  6  1  0  0  2  1   0   0   0   0
+       C4   3  2  0  6  0  0  2  1  0   0   0   1   4
+       C5   0  0  2  0  9  0  0  0  0   0   0   0   1
+       C6   2  3  0  0  1  8  1  4  1   3   2   3   0
+       C7   0  1  0  0  1  0  6  0  2   0   0   6   2
+       C8   0  0  1  0  0  1  1  8  0   0   1   1   0
+       C9   1  0  1  0  0  0  0  0  9   1   0   0   1
+       C10  0  0  1  0  0  0  0  0  1   5   8   0   0
+       C11  1  0  0  1  0  1  0  0  0   5   4   1   0
+       C12  0  0  0  0  1  3  5  0  0   0   0   2   2
+       C13  0  0  3  1  2  0  0  0  0   0   0   1   4
+
+Overall Statistics
+                                          
+               Accuracy : 0.4             
+                 95% CI : (0.3307, 0.4724)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.35            
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.26667   0.40000   0.46667   0.40000   0.60000
+Specificity            0.96667   0.96111   0.94444   0.92778   0.98333
+Pos Pred Value         0.40000   0.46154   0.41176   0.31579   0.75000
+Neg Pred Value         0.94054   0.95055   0.95506   0.94886   0.96721
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.02051   0.03077   0.03590   0.03077   0.04615
+Detection Prevalence   0.05128   0.06667   0.08718   0.09744   0.06154
+Balanced Accuracy      0.61667   0.68056   0.70556   0.66389   0.79167
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.53333   0.40000   0.53333   0.60000    0.33333
+Specificity            0.88889   0.93333   0.97222   0.97778    0.94444
+Pos Pred Value         0.28571   0.33333   0.61538   0.69231    0.33333
+Neg Pred Value         0.95808   0.94915   0.96154   0.96703    0.94444
+Prevalence             0.07692   0.07692   0.07692   0.07692    0.07692
+Detection Rate         0.04103   0.03077   0.04103   0.04615    0.02564
+Detection Prevalence   0.14359   0.09231   0.06667   0.06667    0.07692
+Balanced Accuracy      0.71111   0.66667   0.75278   0.78889    0.63889
+                     Class: C11 Class: C12 Class: C13
+Sensitivity             0.26667    0.13333    0.26667
+Specificity             0.95000    0.93889    0.96111
+Pos Pred Value          0.30769    0.15385    0.36364
+Neg Pred Value          0.93956    0.92857    0.94022
+Prevalence              0.07692    0.07692    0.07692
+Detection Rate          0.02051    0.01026    0.02051
+Detection Prevalence    0.06667    0.06667    0.05641
+Balanced Accuracy       0.60833    0.53611    0.61389
+
+
+
+
+

11 Modelo 2: SVM radial

+

O kernel radial, tambem chamado de RBF, permite construir fronteiras de decisao nao lineares. Nesta primeira versao, serao usados valores fixos para cost e gamma.

+
+
# ============================================================
+# 11. SVM radial com parametros fixos
+# ============================================================
+
+set.seed(123)
+
+modelo_svm_radial <- svm(
+  x = x_treino_norm,
+  y = y_treino,
+  kernel = "radial",
+  cost = 10,
+  gamma = 0.01,
+  scale = FALSE
+)
+
+pred_svm_radial <- predict(modelo_svm_radial, x_teste_norm)
+
+cm_svm_radial <- confusionMatrix(pred_svm_radial, y_teste)
+
+cm_svm_radial
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   2  1  0  0  0  0  0  0  0   0   0   0   0
+       C2   2  0  0  0  0  0  0  1  0   0   0   0   1
+       C3   0  0  3  2  0  0  0  1  1   0   0   0   0
+       C4   0  0  0  2  0  0  1  0  0   0   0   0   3
+       C5   0  0  1  0  4  0  0  0  0   0   0   0   2
+       C6   0  3  0  0  0  2  0  5  0   2   0   2   0
+       C7   0  0  0  0  0  0  0  0  1   0   0   3   1
+       C8   0  0  0  0  0  2  1  1  1   0   0   1   0
+       C9   1  0  1  0  0  0  0  0  2   0   0   0   0
+       C10  0  0  0  0  0  1  0  0  1   2   9   0   0
+       C11  1  0  0  0  0  0  0  1  0   3   0   0   0
+       C12  0  0  0  0  0  1  4  0  1   1   0   2   0
+       C13  9 11 10 11 11  9  9  6  8   7   6   7   8
+
+Overall Statistics
+                                          
+               Accuracy : 0.1436          
+                 95% CI : (0.0976, 0.2008)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : 0.001068        
+                                          
+                  Kappa : 0.0722          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.13333   0.00000   0.20000   0.13333   0.26667
+Specificity            0.99444   0.97778   0.97778   0.97778   0.98333
+Pos Pred Value         0.66667   0.00000   0.42857   0.33333   0.57143
+Neg Pred Value         0.93229   0.92147   0.93617   0.93122   0.94149
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.01026   0.00000   0.01538   0.01026   0.02051
+Detection Prevalence   0.01538   0.02051   0.03590   0.03077   0.03590
+Balanced Accuracy      0.56389   0.48889   0.58889   0.55556   0.62500
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.13333   0.00000  0.066667   0.13333    0.13333
+Specificity            0.93333   0.97222  0.972222   0.98889    0.93889
+Pos Pred Value         0.14286   0.00000  0.166667   0.50000    0.15385
+Neg Pred Value         0.92818   0.92105  0.925926   0.93194    0.92857
+Prevalence             0.07692   0.07692  0.076923   0.07692    0.07692
+Detection Rate         0.01026   0.00000  0.005128   0.01026    0.01026
+Detection Prevalence   0.07179   0.02564  0.030769   0.02051    0.06667
+Balanced Accuracy      0.53333   0.48611  0.519444   0.56111    0.53611
+                     Class: C11 Class: C12 Class: C13
+Sensitivity             0.00000    0.13333    0.53333
+Specificity             0.97222    0.96111    0.42222
+Pos Pred Value          0.00000    0.22222    0.07143
+Neg Pred Value          0.92105    0.93011    0.91566
+Prevalence              0.07692    0.07692    0.07692
+Detection Rate          0.00000    0.01026    0.04103
+Detection Prevalence    0.02564    0.04615    0.57436
+Balanced Accuracy       0.48611    0.54722    0.47778
+
+
+
+
+

12 Modelo 3: Ajuste de hiperparametros do SVM radial

+

O parametro cost controla o quanto o modelo penaliza erros de classificacao. O parametro gamma influencia o alcance do kernel radial. Valores maiores podem gerar fronteiras mais complexas.

+
+
# ============================================================
+# 12. Ajuste simples de hiperparametros do SVM radial
+# ============================================================
+
+set.seed(123)
+
+k_cv <- min(5, as.integer(min(table(y_treino))))
+
+if (k_cv >= 2) {
+  ajuste_svm_radial <- tune.svm(
+    x = x_treino_norm,
+    y = y_treino,
+    kernel = "radial",
+    cost = c(0.1, 1, 10, 100),
+    gamma = c(0.001, 0.01, 0.05, 0.1),
+    tunecontrol = tune.control(cross = k_cv)
+  )
+  
+  modelo_svm_radial_ajustado <- ajuste_svm_radial$best.model
+  
+  pred_svm_radial_ajustado <- predict(
+    modelo_svm_radial_ajustado,
+    x_teste_norm
+  )
+  
+  cm_svm_radial_ajustado <- confusionMatrix(
+    pred_svm_radial_ajustado,
+    y_teste
+  )
+  
+  ajuste_svm_radial$best.parameters %>%
+    kable()
+} else {
+  warning("Poucas amostras por classe para validacao cruzada. O ajuste automatico sera ignorado.")
+  
+  ajuste_svm_radial <- NULL
+  modelo_svm_radial_ajustado <- modelo_svm_radial
+  pred_svm_radial_ajustado <- pred_svm_radial
+  cm_svm_radial_ajustado <- cm_svm_radial
+}
+
+ + + + + + + + + + + + + + + +
gammacost
50.0011
+
+
+
+
cm_svm_radial_ajustado
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   4  3  0  1  0  0  0  0  0   1   0   0   0
+       C2   3  3  0  0  0  0  0  0  0   0   0   0   1
+       C3   0  0  2  2  0  0  0  0  1   0   0   0   0
+       C4   4  1  0  6  0  0  2  1  0   0   0   0   4
+       C5   0  0  3  0  9  0  0  0  0   0   0   0   1
+       C6   0  6  0  0  1  8  1  7  1   2   1   4   0
+       C7   0  1  0  1  0  2  6  0  0   0   0   6   2
+       C8   0  0  2  0  0  0  0  6  2   0   0   0   0
+       C9   2  0  1  0  2  1  0  0  8   1   0   0   2
+       C10  0  0  1  0  0  1  0  0  1   3   7   0   0
+       C11  1  0  1  1  1  2  0  1  1   7   6   0   0
+       C12  0  0  0  0  0  1  5  0  0   0   0   4   1
+       C13  1  1  5  4  2  0  1  0  1   1   1   1   4
+
+Overall Statistics
+                                          
+               Accuracy : 0.3538          
+                 95% CI : (0.2869, 0.4254)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.3             
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.26667   0.20000   0.13333   0.40000   0.60000
+Specificity            0.97222   0.97778   0.98333   0.93333   0.97778
+Pos Pred Value         0.44444   0.42857   0.40000   0.33333   0.69231
+Neg Pred Value         0.94086   0.93617   0.93158   0.94915   0.96703
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.02051   0.01538   0.01026   0.03077   0.04615
+Detection Prevalence   0.04615   0.03590   0.02564   0.09231   0.06667
+Balanced Accuracy      0.61944   0.58889   0.55833   0.66667   0.78889
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.53333   0.40000   0.40000   0.53333    0.20000
+Specificity            0.87222   0.93333   0.97778   0.95000    0.94444
+Pos Pred Value         0.25806   0.33333   0.60000   0.47059    0.23077
+Neg Pred Value         0.95732   0.94915   0.95135   0.96067    0.93407
+Prevalence             0.07692   0.07692   0.07692   0.07692    0.07692
+Detection Rate         0.04103   0.03077   0.03077   0.04103    0.01538
+Detection Prevalence   0.15897   0.09231   0.05128   0.08718    0.06667
+Balanced Accuracy      0.70278   0.66667   0.68889   0.74167    0.57222
+                     Class: C11 Class: C12 Class: C13
+Sensitivity             0.40000    0.26667    0.26667
+Specificity             0.91667    0.96111    0.90000
+Pos Pred Value          0.28571    0.36364    0.18182
+Neg Pred Value          0.94828    0.94022    0.93642
+Prevalence              0.07692    0.07692    0.07692
+Detection Rate          0.03077    0.02051    0.02051
+Detection Prevalence    0.10769    0.05641    0.11282
+Balanced Accuracy       0.65833    0.61389    0.58333
+
+
+
+
+

13 Modelo 4: PCA + SVM radial

+

O PCA transforma os atributos originais em componentes principais. Aqui, serao mantidos componentes suficientes para explicar pelo menos 95% da variancia, com limite maximo de 30 componentes.

+
+
# ============================================================
+# 13. PCA
+# ============================================================
+
+pca <- prcomp(
+  x_treino_norm,
+  center = FALSE,
+  scale. = FALSE
+)
+
+variancia <- pca$sdev^2
+variancia_exp <- variancia / sum(variancia)
+variancia_acum <- cumsum(variancia_exp)
+
+n_comp_95 <- which(variancia_acum >= 0.95)[1]
+n_comp <- min(n_comp_95, 30, ncol(x_treino_norm))
+
+cat("Componentes necessarios para 95% da variancia:", n_comp_95, "\n")
+
+
Componentes necessarios para 95% da variancia: 249 
+
+
cat("Componentes usados no modelo:", n_comp, "\n")
+
+
Componentes usados no modelo: 30 
+
+
+
+
tibble(
+  componente = seq_along(variancia_acum),
+  variancia_acumulada = variancia_acum
+) %>%
+  ggplot(aes(x = componente, y = variancia_acumulada)) +
+  geom_line(color = "#2C7FB8") +
+  geom_point(color = "#2C7FB8") +
+  geom_hline(yintercept = 0.95, linetype = "dashed") +
+  labs(
+    title = "Variancia acumulada pelo PCA",
+    x = "Numero de componentes principais",
+    y = "Variancia acumulada"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
x_treino_pca <- as.data.frame(pca$x[, 1:n_comp, drop = FALSE])
+
+x_teste_pca <- as.data.frame(
+  predict(pca, newdata = x_teste_norm)[, 1:n_comp, drop = FALSE]
+)
+
+set.seed(123)
+
+modelo_svm_pca <- svm(
+  x = x_treino_pca,
+  y = y_treino,
+  kernel = "radial",
+  cost = 10,
+  gamma = 0.01,
+  scale = FALSE
+)
+
+pred_svm_pca <- predict(modelo_svm_pca, x_teste_pca)
+
+cm_svm_pca <- confusionMatrix(pred_svm_pca, y_teste)
+
+cm_svm_pca
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   6  2  0  0  1  0  0  0  0   1   0   0   0
+       C2   3  5  0  0  0  0  0  0  0   1   1   0   1
+       C3   0  0  7  2  1  0  0  0  1   0   0   0   0
+       C4   1  0  0  6  0  0  1  1  0   0   0   0   4
+       C5   0  0  2  0  8  0  0  0  0   0   0   0   1
+       C6   1  3  0  0  0  3  1  6  0   3   2   3   0
+       C7   0  0  0  1  1  3  7  0  1   0   0   6   3
+       C8   0  2  1  0  0  4  0  7  2   0   0   0   0
+       C9   1  0  1  0  2  1  0  0  6   1   0   0   2
+       C10  0  0  1  0  0  1  0  0  0   5   9   0   0
+       C11  1  0  0  0  1  1  0  1  0   3   2   0   0
+       C12  0  0  0  1  0  2  5  0  2   1   1   4   0
+       C13  2  3  3  5  1  0  1  0  3   0   0   2   4
+
+Overall Statistics
+                                          
+               Accuracy : 0.359           
+                 95% CI : (0.2917, 0.4306)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.3056          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.40000   0.33333   0.46667   0.40000   0.53333
+Specificity            0.97778   0.96667   0.97778   0.96111   0.98333
+Pos Pred Value         0.60000   0.45455   0.63636   0.46154   0.72727
+Neg Pred Value         0.95135   0.94565   0.95652   0.95055   0.96196
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.03077   0.02564   0.03590   0.03077   0.04103
+Detection Prevalence   0.05128   0.05641   0.05641   0.06667   0.05641
+Balanced Accuracy      0.68889   0.65000   0.72222   0.68056   0.75833
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.20000   0.46667   0.46667   0.40000    0.33333
+Specificity            0.89444   0.91667   0.95000   0.95556    0.93889
+Pos Pred Value         0.13636   0.31818   0.43750   0.42857    0.31250
+Neg Pred Value         0.93064   0.95376   0.95531   0.95028    0.94413
+Prevalence             0.07692   0.07692   0.07692   0.07692    0.07692
+Detection Rate         0.01538   0.03590   0.03590   0.03077    0.02564
+Detection Prevalence   0.11282   0.11282   0.08205   0.07179    0.08205
+Balanced Accuracy      0.54722   0.69167   0.70833   0.67778    0.63611
+                     Class: C11 Class: C12 Class: C13
+Sensitivity             0.13333    0.26667    0.26667
+Specificity             0.96111    0.93333    0.88889
+Pos Pred Value          0.22222    0.25000    0.16667
+Neg Pred Value          0.93011    0.93855    0.93567
+Prevalence              0.07692    0.07692    0.07692
+Detection Rate          0.01026    0.02051    0.02051
+Detection Prevalence    0.04615    0.08205    0.12308
+Balanced Accuracy       0.54722    0.60000    0.57778
+
+
+
+
scores_pca <- as_tibble(pca$x[, 1:2, drop = FALSE]) %>%
+  mutate(classe = y_treino)
+
+ggplot(scores_pca, aes(x = PC1, y = PC2, color = classe)) +
+  geom_point(alpha = 0.8) +
+  labs(
+    title = "Visualizacao do conjunto de treino no espaco PCA",
+    x = "PC1",
+    y = "PC2",
+    color = "Classe"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

14 Modelo 5: Random Forest

+

O Random Forest sera usado como classificador de comparacao. Ele costuma lidar bem com relacoes nao lineares e tambem permite estimar a importancia das variaveis.

+
+
# ============================================================
+# 14. Random Forest
+# ============================================================
+
+set.seed(123)
+
+modelo_rf <- randomForest(
+  x = x_treino_norm,
+  y = y_treino,
+  ntree = 500,
+  importance = TRUE
+)
+
+pred_rf <- predict(modelo_rf, x_teste_norm)
+
+cm_rf <- confusionMatrix(pred_rf, y_teste)
+
+cm_rf
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   5  3  1  1  1  0  0  0  1   1   0   0   1
+       C2   1  4  0  0  0  0  0  0  0   0   0   0   1
+       C3   0  0  3  3  0  0  0  0  1   0   0   0   0
+       C4   5  3  1  9  0  0  3  1  0   0   0   0   4
+       C5   0  0  4  0  9  0  0  0  0   0   0   0   2
+       C6   1  5  0  0  3  8  1  6  1   3   2   4   0
+       C7   0  0  0  1  0  1  4  0  0   0   0   4   2
+       C8   0  0  3  0  0  2  2  6  2   0   1   0   0
+       C9   1  0  1  0  2  1  0  0  8   1   0   0   1
+       C10  1  0  0  0  0  0  0  1  0   6  10   2   0
+       C11  1  0  0  0  0  0  0  0  1   4   2   0   0
+       C12  0  0  0  0  0  3  5  1  1   0   0   5   0
+       C13  0  0  2  1  0  0  0  0  0   0   0   0   4
+
+Overall Statistics
+                                          
+               Accuracy : 0.3744          
+                 95% CI : (0.3063, 0.4463)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.3222          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.33333   0.26667   0.20000   0.60000   0.60000
+Specificity            0.95000   0.98889   0.97778   0.90556   0.96667
+Pos Pred Value         0.35714   0.66667   0.42857   0.34615   0.60000
+Neg Pred Value         0.94475   0.94180   0.93617   0.96450   0.96667
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.02564   0.02051   0.01538   0.04615   0.04615
+Detection Prevalence   0.07179   0.03077   0.03590   0.13333   0.07692
+Balanced Accuracy      0.64167   0.62778   0.58889   0.75278   0.78333
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.53333   0.26667   0.40000   0.53333    0.40000
+Specificity            0.85556   0.95556   0.94444   0.96111    0.92222
+Pos Pred Value         0.23529   0.33333   0.37500   0.53333    0.30000
+Neg Pred Value         0.95652   0.93989   0.94972   0.96111    0.94857
+Prevalence             0.07692   0.07692   0.07692   0.07692    0.07692
+Detection Rate         0.04103   0.02051   0.03077   0.04103    0.03077
+Detection Prevalence   0.17436   0.06154   0.08205   0.07692    0.10256
+Balanced Accuracy      0.69444   0.61111   0.67222   0.74722    0.66111
+                     Class: C11 Class: C12 Class: C13
+Sensitivity             0.13333    0.33333    0.26667
+Specificity             0.96667    0.94444    0.98333
+Pos Pred Value          0.25000    0.33333    0.57143
+Neg Pred Value          0.93048    0.94444    0.94149
+Prevalence              0.07692    0.07692    0.07692
+Detection Rate          0.01026    0.02564    0.02051
+Detection Prevalence    0.04103    0.07692    0.03590
+Balanced Accuracy       0.55000    0.63889    0.62500
+
+
+
+
+

15 Comparacao dos modelos

+

Os modelos serao comparados por acuracia, Kappa, sensibilidade macro, especificidade macro e F1 macro.

+
+
# ============================================================
+# 15. Funcao de avaliacao dos modelos
+# ============================================================
+
+avaliar_modelo <- function(nome, matriz_confusao) {
+  overall <- matriz_confusao$overall
+  by_class <- matriz_confusao$byClass
+  
+  if (is.matrix(by_class)) {
+    sensibilidade_macro <- mean(by_class[, "Sensitivity"], na.rm = TRUE)
+    especificidade_macro <- mean(by_class[, "Specificity"], na.rm = TRUE)
+    f1_macro <- mean(by_class[, "F1"], na.rm = TRUE)
+  } else {
+    sensibilidade_macro <- by_class["Sensitivity"]
+    especificidade_macro <- by_class["Specificity"]
+    f1_macro <- by_class["F1"]
+  }
+  
+  tibble(
+    modelo = nome,
+    acuracia = as.numeric(overall["Accuracy"]),
+    kappa = as.numeric(overall["Kappa"]),
+    sensibilidade_macro = as.numeric(sensibilidade_macro),
+    especificidade_macro = as.numeric(especificidade_macro),
+    f1_macro = as.numeric(f1_macro)
+  )
+}
+
+resultados <- bind_rows(
+  avaliar_modelo("SVM linear", cm_svm_linear),
+  avaliar_modelo("SVM radial", cm_svm_radial),
+  avaliar_modelo("SVM radial ajustado", cm_svm_radial_ajustado),
+  avaliar_modelo("PCA + SVM radial", cm_svm_pca),
+  avaliar_modelo("Random Forest", cm_rf)
+) %>%
+  arrange(desc(acuracia))
+
+resultados %>%
+  mutate(across(where(is.numeric), ~ round(.x, 4))) %>%
+  kable()
+
+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
modeloacuraciakappasensibilidade_macroespecificidade_macrof1_macro
SVM linear0.40000.35000.40000.95000.4019
Random Forest0.37440.32220.37440.94790.3688
PCA + SVM radial0.35900.30560.35900.94660.3696
SVM radial ajustado0.35380.30000.35380.94620.3520
SVM radial0.14360.07220.14360.92860.1928
+
+
+
+
ggplot(resultados, aes(x = reorder(modelo, acuracia), y = acuracia)) +
+  geom_col(fill = "#2C7FB8") +
+  coord_flip() +
+  labs(
+    title = "Comparacao da acuracia dos modelos",
+    x = "Modelo",
+    y = "Acuracia"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
resultados_long <- resultados %>%
+  pivot_longer(
+    cols = c(acuracia, kappa, sensibilidade_macro, especificidade_macro, f1_macro),
+    names_to = "metrica",
+    values_to = "valor"
+  )
+
+ggplot(resultados_long, aes(x = reorder(modelo, valor), y = valor, fill = metrica)) +
+  geom_col(show.legend = FALSE) +
+  coord_flip() +
+  facet_wrap(~ metrica, scales = "free_x") +
+  labs(
+    title = "Metricas comparativas dos modelos",
+    x = "Modelo",
+    y = "Valor"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

16 Melhor modelo

+
+
melhor_modelo <- resultados$modelo[1]
+
+cat("Melhor modelo pela acuracia:", melhor_modelo, "\n")
+
+
Melhor modelo pela acuracia: SVM linear 
+
+
+
+
cm_melhor <- switch(
+  melhor_modelo,
+  "SVM linear" = cm_svm_linear,
+  "SVM radial" = cm_svm_radial,
+  "SVM radial ajustado" = cm_svm_radial_ajustado,
+  "PCA + SVM radial" = cm_svm_pca,
+  "Random Forest" = cm_rf
+)
+
+as.data.frame(cm_melhor$table) %>%
+  ggplot(aes(x = Reference, y = Prediction, fill = Freq)) +
+  geom_tile(color = "white") +
+  geom_text(aes(label = Freq), size = 3) +
+  scale_fill_gradient(low = "#F7FBFF", high = "#08519C") +
+  labs(
+    title = paste("Matriz de confusao -", melhor_modelo),
+    x = "Classe real",
+    y = "Classe prevista",
+    fill = "Frequencia"
+  ) +
+  theme_minimal() +
+  theme(axis.text.x = element_text(angle = 45, hjust = 1))
+
+
+
+

+
+
+
+
+
+
+

17 Importancia das variaveis

+

A importancia das variaveis sera estimada pelo Random Forest. Essa etapa ajuda a observar quais atributos contribuiram mais para a classificacao.

+
+
# ============================================================
+# 16. Importancia das variaveis
+# ============================================================
+
+importancia <- importance(modelo_rf)
+
+coluna_importancia <- if ("MeanDecreaseGini" %in% colnames(importancia)) {
+  "MeanDecreaseGini"
+} else {
+  colnames(importancia)[1]
+}
+
+importancia_df <- tibble(
+  variavel = rownames(importancia),
+  importancia = importancia[, coluna_importancia]
+) %>%
+  arrange(desc(importancia))
+
+importancia_df %>%
+  slice_head(n = 20) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
variavelimportancia
feat_04721.7446133
feat_02601.7042444
feat_04731.0239011
feat_03341.0189685
feat_06070.9355144
feat_02010.9340196
feat_01970.9121311
feat_04900.8838067
feat_01260.8725346
feat_11980.8392922
feat_01830.8291562
feat_02320.8134642
feat_12180.8038597
feat_04760.7979804
feat_08500.7941928
feat_09120.7613126
feat_07290.7384886
feat_07780.7236667
feat_11170.7209036
feat_10910.6878679
+
+
+
+
importancia_df %>%
+  slice_head(n = 20) %>%
+  ggplot(aes(x = reorder(variavel, importancia), y = importancia)) +
+  geom_col(fill = "#2C7FB8") +
+  coord_flip() +
+  labs(
+    title = "20 variaveis mais importantes segundo o Random Forest",
+    x = "Variavel",
+    y = "Importancia"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

18 Salvamento dos resultados

+
+
# ============================================================
+# 17. Salvar saidas
+# ============================================================
+
+dir.create(pasta_saida, recursive = TRUE, showWarnings = FALSE)
+
+write_csv(dados, file.path(pasta_saida, "features_rgb.csv"))
+write_csv(resultados, file.path(pasta_saida, "resultados_modelos_rgb.csv"))
+write_csv(importancia_df, file.path(pasta_saida, "importancia_variaveis_rgb.csv"))
+
+saveRDS(
+  list(
+    preproc                    = preproc,
+    modelo_svm_linear          = modelo_svm_linear,
+    modelo_svm_radial          = modelo_svm_radial,
+    ajuste_svm_radial          = ajuste_svm_radial,
+    modelo_svm_radial_ajustado = modelo_svm_radial_ajustado,
+    modelo_svm_pca             = modelo_svm_pca,
+    modelo_rf                  = modelo_rf,
+    pca                        = pca,
+    resultados                 = resultados,
+    extrator_cnn               = "MobileNetV2 (ImageNet, sem topo, pooling=avg, 1280 features)"
+  ),
+  file.path(pasta_saida, "modelos_rgb.rds")
+)
+
+cat("Arquivos salvos em:", pasta_saida, "\n")
+
+
Arquivos salvos em: outputs/parcial 
+
+
+
+
+

19 Validacao Cruzada K-Fold

+

Na abordagem anterior (holdout), o conjunto de dados foi dividido uma unica vez em treino (70%) e teste (30%). Isso pode gerar uma estimativa otimista ou pessimista do desempenho real, dependendo de como a divisao ocorreu.

+

A validacao cruzada k-fold resolve esse problema dividindo o dataset em k partes iguais e estratificadas. Em cada rodada, uma parte diferente e usada como teste e as restantes como treino. O desempenho final e a media das k rodadas, o que aproveita todas as observacoes para avaliacao e produz uma estimativa mais estavel.

+

Nesta etapa, sera usado k=5. Para manter a avaliacao independente do holdout, o SVM radial ajustado tera cost e gamma reotimizados dentro de cada fold, usando apenas os dados de treino daquele fold.

+
+
# ============================================================
+# 18. Validacao cruzada k-fold (k=5, estratificada)
+# ============================================================
+
+set.seed(123)
+
+k_folds <- 5L
+
+folds <- createFolds(dados$classe, k = k_folds, list = TRUE, returnTrain = FALSE)
+
+grade_cost_kfold  <- c(1, 10, 100)
+grade_gamma_kfold <- c(0.001, 0.01, 0.05, 0.1)
+
+metricas_folds <- vector("list", k_folds)
+
+for (fold_i in seq_len(k_folds)) {
+  idx_teste  <- folds[[fold_i]]
+  idx_treino <- setdiff(seq_len(nrow(dados)), idx_teste)
+
+  fold_treino <- dados[idx_treino, ]
+  fold_teste  <- dados[idx_teste,  ]
+
+  x_fold_tr <- fold_treino %>% select(-all_of(colunas_nao_preditoras))
+  x_fold_te <- fold_teste  %>% select(-all_of(colunas_nao_preditoras))
+  y_fold_tr <- droplevels(fold_treino$classe)
+  y_fold_te <- factor(fold_teste$classe, levels = levels(y_fold_tr))
+
+  nzv_fold <- nearZeroVar(x_fold_tr)
+  if (length(nzv_fold) > 0) {
+    x_fold_tr <- x_fold_tr[, -nzv_fold, drop = FALSE]
+    x_fold_te <- x_fold_te[, colnames(x_fold_tr), drop = FALSE]
+  }
+
+  pp_fold      <- preProcess(x_fold_tr, method = c("center", "scale"))
+  x_fold_tr_n  <- predict(pp_fold, x_fold_tr)
+  x_fold_te_n  <- predict(pp_fold, x_fold_te)
+
+  m1 <- svm(x = x_fold_tr_n, y = y_fold_tr,
+            kernel = "linear", cost = 1, scale = FALSE)
+
+  m2 <- svm(x = x_fold_tr_n, y = y_fold_tr,
+            kernel = "radial", cost = 10, gamma = 0.01, scale = FALSE)
+
+  k_cv_fold <- min(5L, min(table(y_fold_tr)))
+  ajuste_fold <- tune.svm(
+    x = x_fold_tr_n,
+    y = y_fold_tr,
+    kernel = "radial",
+    cost = grade_cost_kfold,
+    gamma = grade_gamma_kfold,
+    tunecontrol = tune.control(cross = k_cv_fold)
+  )
+  m3 <- ajuste_fold$best.model
+
+  pca_fold    <- prcomp(x_fold_tr_n, center = FALSE, scale. = FALSE)
+  var_acum_f  <- cumsum(pca_fold$sdev^2 / sum(pca_fold$sdev^2))
+  n_comp_f    <- min(which(var_acum_f >= 0.95)[1], 30L, ncol(x_fold_tr_n))
+  x_pca_tr    <- as.data.frame(pca_fold$x[, 1:n_comp_f, drop = FALSE])
+  x_pca_te    <- as.data.frame(predict(pca_fold, x_fold_te_n)[, 1:n_comp_f, drop = FALSE])
+  m4 <- svm(x = x_pca_tr, y = y_fold_tr,
+            kernel = "radial", cost = 10, gamma = 0.01, scale = FALSE)
+
+  m5 <- randomForest(x = x_fold_tr_n, y = y_fold_tr, ntree = 200L)
+
+  metricas_folds[[fold_i]] <- bind_rows(
+    avaliar_modelo("SVM linear",          confusionMatrix(predict(m1, x_fold_te_n), y_fold_te)),
+    avaliar_modelo("SVM radial",          confusionMatrix(predict(m2, x_fold_te_n), y_fold_te)),
+    avaliar_modelo("SVM radial ajustado", confusionMatrix(predict(m3, x_fold_te_n), y_fold_te)),
+    avaliar_modelo("PCA + SVM radial",    confusionMatrix(predict(m4, x_pca_te),    y_fold_te)),
+    avaliar_modelo("Random Forest",       confusionMatrix(predict(m5, x_fold_te_n), y_fold_te))
+  ) %>% mutate(fold = fold_i)
+
+  cat(sprintf(
+    "Fold %d/%d concluido. SVM ajustado: cost = %s | gamma = %s\n",
+    fold_i, k_folds,
+    ajuste_fold$best.parameters$cost,
+    ajuste_fold$best.parameters$gamma
+  ))
+}
+
+
Fold 1/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 2/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 3/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 4/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 5/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+
+
+
+
resultados_kfold <- bind_rows(metricas_folds) %>%
+  group_by(modelo) %>%
+  summarise(across(c(acuracia, kappa, sensibilidade_macro, especificidade_macro, f1_macro), mean),
+            .groups = "drop") %>%
+  arrange(desc(acuracia))
+
+write_csv(resultados_kfold, file.path(pasta_saida, "resultados_kfold_rgb.csv"))
+
+resultados_kfold %>%
+  mutate(across(where(is.numeric), ~ round(.x, 4))) %>%
+  kable(caption = "Acuracia media - K-Fold (k=5)")
+
+ + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Acuracia media - K-Fold (k=5)
modeloacuraciakappasensibilidade_macroespecificidade_macrof1_macro
SVM linear0.40150.35170.40150.95010.4093
SVM radial ajustado0.38000.32830.38000.94830.3905
PCA + SVM radial0.36770.31500.36770.94730.3784
Random Forest0.35690.30330.35690.94640.3806
SVM radial0.15850.08830.15850.92990.2233
+
+
+
+
ggplot(resultados_kfold, aes(x = reorder(modelo, acuracia), y = acuracia)) +
+  geom_col(fill = "#E06C00") +
+  coord_flip() +
+  labs(
+    title = "Acuracia media - Validacao cruzada 5-fold",
+    x = "Modelo",
+    y = "Acuracia media"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

20 Comparacao: Holdout vs K-Fold

+
+
# ============================================================
+# 19. Comparacao direta entre as duas estrategias
+# ============================================================
+
+comparacao <- bind_rows(
+  resultados      %>% mutate(estrategia = "Holdout 70/30"),
+  resultados_kfold %>% mutate(estrategia = "K-Fold (k=5)")
+) %>%
+  select(estrategia, modelo, acuracia, kappa, f1_macro) %>%
+  arrange(estrategia, desc(acuracia))
+
+comparacao %>%
+  mutate(across(where(is.numeric), ~ round(.x, 4))) %>%
+  kable(caption = "Comparacao entre Holdout 70/30 e Validacao Cruzada K-Fold")
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Comparacao entre Holdout 70/30 e Validacao Cruzada K-Fold
estrategiamodeloacuraciakappaf1_macro
Holdout 70/30SVM linear0.40000.35000.4019
Holdout 70/30Random Forest0.37440.32220.3688
Holdout 70/30PCA + SVM radial0.35900.30560.3696
Holdout 70/30SVM radial ajustado0.35380.30000.3520
Holdout 70/30SVM radial0.14360.07220.1928
K-Fold (k=5)SVM linear0.40150.35170.4093
K-Fold (k=5)SVM radial ajustado0.38000.32830.3905
K-Fold (k=5)PCA + SVM radial0.36770.31500.3784
K-Fold (k=5)Random Forest0.35690.30330.3806
K-Fold (k=5)SVM radial0.15850.08830.2233
+
+
+
+
ggplot(comparacao, aes(x = reorder(modelo, acuracia), y = acuracia, fill = estrategia)) +
+  geom_col(position = "dodge") +
+  coord_flip() +
+  scale_fill_manual(values = c("Holdout 70/30" = "#2C7FB8", "K-Fold (k=5)" = "#E06C00")) +
+  labs(
+    title = "Acuracia: Holdout 70/30 vs K-Fold (k=5)",
+    x = "Modelo",
+    y = "Acuracia",
+    fill = "Estrategia"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
melhor_holdout  <- max(resultados$acuracia)
+melhor_kf       <- max(resultados_kfold$acuracia)
+melhor_metodo   <- if (melhor_kf >= melhor_holdout) "K-Fold (k=5)" else "Holdout 70/30"
+resultados_finais <- if (melhor_kf >= melhor_holdout) resultados_kfold else resultados
+
+cat("Acuracia maxima - Holdout:", round(melhor_holdout, 4), "\n")
+
+
Acuracia maxima - Holdout: 0.4 
+
+
cat("Acuracia maxima - K-Fold: ", round(melhor_kf,      4), "\n")
+
+
Acuracia maxima - K-Fold:  0.4015 
+
+
cat("Metodo adotado como referencia:", melhor_metodo, "\n")
+
+
Metodo adotado como referencia: K-Fold (k=5) 
+
+
+
+
+

21 Discussao

+

Os resultados permitem comparar diferentes classificadores aplicados sobre atributos extraidos por transfer learning com a rede MobileNetV2, e tambem avaliar o impacto da estrategia de avaliacao adotada.

+

A extracao de atributos por CNN pre-treinada substitui o calculo manual de medias, desvios e histogramas de cor por um vetor de 1280 atributos que codificam padroes visuais de alto nivel aprendidos em mais de um milhao de imagens do ImageNet. Essa representacao tende a ser muito mais discriminativa para problemas de classificacao de cenas.

+

O SVM linear busca fronteiras lineares nesse espaco de 1280 dimensoes. O SVM radial permite fronteiras nao lineares, sendo mais flexivel. O PCA reduz a dimensionalidade antes do SVM, eliminando redundancias entre as 1280 features da CNN. O Random Forest serve como referencia nao-linear e permite identificar quais atributos foram mais relevantes.

+

Sobre a estrategia de avaliacao: na primeira abordagem, o dataset foi dividido uma unica vez em 70% treino e 30% teste (holdout). Embora simples, esse metodo pode gerar estimativas instáveis com datasets de tamanho moderado, pois o resultado depende de como essa divisao ocorreu. Para verificar se os resultados eram representativos, o experimento foi repetido com validacao cruzada k-fold com k=5: o dataset e dividido em 5 partes estratificadas e, em cada rodada, uma parte diferente e usada como teste. O desempenho final e a media das 5 rodadas.

+
+
if (melhor_kf >= melhor_holdout) {
+  cat("O k-fold produziu acuracia igual ou superior ao holdout, sendo adotado como resultado principal.\n")
+} else {
+  cat("O holdout produziu acuracia superior ao k-fold. Isso pode indicar que a divisao holdout foi favoravel.\n",
+      "O k-fold e metodologicamente mais robusto, mas neste caso o holdout resultou em estimativa mais otimista.\n")
+}
+
+
O k-fold produziu acuracia igual ou superior ao holdout, sendo adotado como resultado principal.
+
+
+
+
+

22 Conclusao

+

Este trabalho implementou uma pipeline de classificacao de imagens RGB baseada em transfer learning, reducao de dimensionalidade e classificadores supervisionados, avaliada por duas estrategias distintas.

+

A estrategia de usar a MobileNetV2 pre-treinada como extrator de atributos, seguida de SVM ou Random Forest, combina o poder de representacao das redes neurais com a interpretabilidade dos classificadores classicos, sem necessidade de treinar a CNN do zero.

+
+
cat(sprintf(
+  "O melhor resultado foi obtido com '%s' usando a estrategia '%s', com acuracia de %.1f%%.\n",
+  resultados_finais$modelo[1],
+  melhor_metodo,
+  max(resultados_finais$acuracia) * 100
+))
+
+
O melhor resultado foi obtido com 'SVM linear' usando a estrategia 'K-Fold (k=5)', com acuracia de 40.2%.
+
+
+

Como expansao futura, o trabalho pode ser repetido com outros extratores CNN presentes no material da disciplina (EfficientNetB0, ResNet50, VGG19) e com a incorporacao das imagens TIR ao vetor de atributos, aproveitando a informacao termica disponivel no dataset.

+
+ +
+ + +
+ + + + + \ No newline at end of file diff --git a/trabalho/completo/trabalho_svm_rgb_completo.html b/trabalho/completo/trabalho_svm_rgb_completo.html new file mode 100644 index 0000000..b6cb027 --- /dev/null +++ b/trabalho/completo/trabalho_svm_rgb_completo.html @@ -0,0 +1,2332 @@ + + + + + + + + + + + + +Classificacao de Imagens RGB com SVM (Dataset Completo) + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+

Classificacao de Imagens RGB com SVM (Dataset Completo)

+

CEAO-802 - Metodos de Analise de Dados

+
+ + + +
+ +
+
Authors
+
+

1T Generoso

+

1T João Marcos

+

1T Vitor Cesa

+
+
+ + + +
+ + + +
+ + +
+

1 Introducao

+

Este trabalho aplica tecnicas de classificacao supervisionada ao dataset RGB disponibilizado na disciplina CEAO-802 - Metodos de Analise de Dados.

+

O documento de datasets apresenta um problema de classificacao de cenas urbanas com imagens RGB e TIR coletadas por drone sobre Guaratingueta (SP), com 13 classes de cobertura urbana. Nesta analise sera utilizada apenas a parte RGB do dataset, armazenada localmente em D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB.

+

Diferenca em relacao ao trabalho original: esta versao utiliza TODAS as imagens disponiveis por classe (sem limite de 50), para avaliar o impacto do tamanho do dataset no desempenho dos modelos.

+

A abordagem adotada segue a sugestao do material da disciplina: em vez de extrair atributos estatisticos simples (media, desvio, histogramas), serao utilizadas redes neurais convolucionais pre-treinadas como extratores de atributos (transfer learning). A rede MobileNetV2, pre-treinada no ImageNet, e carregada sem a camada de classificacao final. Cada imagem e redimensionada para 224x224 pixels e processada pela rede, que produz um vetor de 1280 atributos representando caracteristicas visuais de alto nivel aprendidas em milhoes de imagens. Esses vetores sao entao usados como entrada para os classificadores SVM e Random Forest.

+

Neste relatorio, serao avaliados:

+
    +
  1. SVM com kernel linear;
  2. +
  3. SVM com kernel radial;
  4. +
  5. SVM radial com ajuste simples de hiperparametros;
  6. +
  7. PCA + SVM radial;
  8. +
  9. Random Forest, como modelo de comparacao.
  10. +
+
+
+

2 Preparacao do ambiente

+
+
# ============================================================
+# 1. Instalacao e carregamento dos pacotes
+# ============================================================
+
+pacotes <- c(
+  "tidyverse",
+  "keras3",
+  "caret",
+  "e1071",
+  "randomForest",
+  "knitr"
+)
+
+pacotes_faltando <- setdiff(pacotes, rownames(installed.packages()))
+
+if (length(pacotes_faltando) > 0) {
+  install.packages(pacotes_faltando)
+}
+
+library(tidyverse)
+library(keras3)
+library(caret)
+library(e1071)
+library(randomForest)
+library(knitr)
+
+backend_ativo <- tryCatch(config_backend(), error = function(e) NA_character_)
+cat("Backend keras3:", backend_ativo, "\n")
+
+
Backend keras3: tensorflow 
+
+
+
+
+

3 Caminho do dataset

+
+
# ============================================================
+# 2. Caminho do dataset RGB
+# ============================================================
+
+caminho_rgb <- "D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB"
+
+if (!dir.exists(caminho_rgb)) {
+  stop("A pasta do dataset RGB nao foi encontrada. Verifique o caminho informado em caminho_rgb.")
+}
+
+caminho_rgb
+
+
[1] "D:/Vitor/Documents/CEAO/802/HAB/DADOS CEAO 2026/RGB"
+
+
+
+
+

4 Leitura dos arquivos de imagem

+
+
# ============================================================
+# 3. Listar imagens
+# ============================================================
+
+extensoes_imagem <- "\\.(jpg|jpeg|png|bmp|tif|tiff)$"
+
+arquivos <- list.files(
+  path = caminho_rgb,
+  pattern = extensoes_imagem,
+  recursive = TRUE,
+  full.names = TRUE,
+  ignore.case = TRUE
+)
+
+if (length(arquivos) == 0) {
+  stop("Nenhuma imagem foi encontrada na pasta RGB.")
+}
+
+length(arquivos)
+
+
[1] 1248
+
+
+
+
# ============================================================
+# 4. Criar metadados
+# ============================================================
+
+obter_classe <- function(arquivo, raiz) {
+  arquivo_norm <- normalizePath(arquivo, winslash = "/", mustWork = FALSE)
+  raiz_norm <- normalizePath(raiz, winslash = "/", mustWork = FALSE)
+
+  nome_sem_extensao <- tools::file_path_sans_ext(basename(arquivo))
+  classe_inferida <- stringr::str_extract(nome_sem_extensao, "^C\\d+")
+
+  if (!is.na(classe_inferida)) {
+    return(classe_inferida)
+  }
+
+  caminho_relativo <- stringr::str_remove(
+    arquivo_norm,
+    paste0("^", stringr::fixed(raiz_norm), "/?")
+  )
+
+  partes <- strsplit(caminho_relativo, "/", fixed = TRUE)[[1]]
+
+  if (length(partes) >= 2) {
+    return(partes[1])
+  }
+
+  stringr::str_extract(nome_sem_extensao, "^[^_-]+")
+}
+
+metadados_completo <- tibble(
+  arquivo = arquivos,
+  classe = map_chr(arquivos, obter_classe, raiz = caminho_rgb)
+) %>%
+  mutate(
+    classe = factor(classe, levels = paste0("C", 1:13)),
+    tamanho_mb = file.info(arquivo)$size / 1024^2
+  ) %>%
+  arrange(classe, arquivo)
+
+resumo_dataset <- metadados_completo %>%
+  group_by(classe) %>%
+  summarise(
+    n_imagens = n(),
+    tamanho_total_mb = sum(tamanho_mb, na.rm = TRUE),
+    tamanho_medio_mb = mean(tamanho_mb, na.rm = TRUE),
+    .groups = "drop"
+  )
+
+resumo_dataset %>%
+  mutate(across(where(is.numeric), ~ round(.x, 2))) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classen_imagenstamanho_total_mbtamanho_medio_mb
C1963388.3135.29
C2963388.3135.29
C3963388.3135.29
C4963388.3135.29
C5963388.3135.29
C6963388.3135.29
C7963388.3135.29
C8963388.3135.29
C9963388.3135.29
C10963388.3135.29
C11963388.3135.29
C12963388.3135.29
C13963388.3135.29
+
+
+
+
+

5 Amostra completa (sem limite por classe)

+
+
# ============================================================
+# 5. Usar TODAS as imagens disponíveis por classe
+# ============================================================
+
+set.seed(123)
+
+# Alteracao em relacao ao original: max_imagens_por_classe = 9999
+# para usar todas as imagens disponiveis
+max_imagens_por_classe <- 9999
+
+metadados <- metadados_completo %>%
+  group_by(classe) %>%
+  group_modify(~ {
+    qtd <- min(max_imagens_por_classe, nrow(.x))
+    slice_sample(.x, n = qtd)
+  }) %>%
+  ungroup() %>%
+  mutate(classe = factor(classe, levels = paste0("C", 1:13)))
+
+cat("Total de imagens no dataset completo:", nrow(metadados_completo), "\n")
+
+
Total de imagens no dataset completo: 1248 
+
+
cat("Total de imagens na amostra:", nrow(metadados), "\n")
+
+
Total de imagens na amostra: 1248 
+
+
metadados %>%
+  count(classe, name = "n_imagens_amostra") %>%
+  arrange(classe) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classen_imagens_amostra
C196
C296
C396
C496
C596
C696
C796
C896
C996
C1096
C1196
C1296
C1396
+
+
+
+
ggplot(metadados, aes(x = classe)) +
+  geom_bar(fill = "#2C7FB8") +
+  coord_flip() +
+  labs(
+    title = "Distribuicao de imagens por classe (dataset completo)",
+    x = "Classe",
+    y = "Numero de imagens"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

6 Modelo pre-treinado (Transfer Learning)

+
+
# ============================================================
+# 6. Carregar modelo pre-treinado MobileNetV2
+# ============================================================
+
+dir.create("outputs", showWarnings = FALSE)
+
+modelo_base <- application_mobilenet_v2(
+  include_top = FALSE,
+  weights    = "imagenet",
+  pooling    = "avg",
+  input_shape = c(224L, 224L, 3L)
+)
+
+cat("Modelo carregado:", modelo_base$name, "\n")
+
+
Modelo carregado: mobilenetv2_1.00_224 
+
+
cat("Dimensao do vetor de features:", modelo_base$output_shape[[2]], "\n")
+
+
Dimensao do vetor de features: 1280 
+
+
+
+
+

7 Extracao de atributos por Transfer Learning

+
+
# ============================================================
+# 7. Extracao de features (com cache separado do original)
+# ============================================================
+
+pasta_saida <- "outputs/completo"
+dir.create(pasta_saida, recursive = TRUE, showWarnings = FALSE)
+
+# Nomes diferentes para nao sobrescrever o cache do trabalho original
+arquivo_cache_features <- file.path(pasta_saida, "features_rgb_mobilenetv2_completo.rds")
+arquivo_cache_parcial  <- file.path(pasta_saida, "features_rgb_mobilenetv2_completo_temp.rds")
+
+tamanho_imagem <- 224L
+tamanho_lote   <- 16L
+
+extrair_features_batch <- function(arquivos, modelo, tamanho = 224L, lote = 16L) {
+  n <- length(arquivos)
+  resultados <- matrix(NA_real_, nrow = n, ncol = as.integer(modelo$output_shape[[2]]))
+
+  for (inicio in seq(1, n, by = lote)) {
+    fim <- min(inicio + lote - 1L, n)
+    bloco <- arquivos[inicio:fim]
+    tam_bloco <- length(bloco)
+
+    batch_array <- array(0, dim = c(tam_bloco, tamanho, tamanho, 3L))
+
+    for (j in seq_len(tam_bloco)) {
+      img <- image_load(bloco[j], target_size = c(tamanho, tamanho))
+      batch_array[j, , , ] <- image_to_array(img)
+    }
+
+    batch_prep <- (batch_array / 127.5) - 1.0
+    features   <- predict(modelo, batch_prep, verbose = 0L)
+    resultados[inicio:fim, ] <- features
+  }
+
+  resultados
+}
+
+if (file.exists(arquivo_cache_features)) {
+  dados <- readRDS(arquivo_cache_features)
+  cat("Features carregadas do cache:", arquivo_cache_features, "\n")
+} else {
+  cat("Extraindo features com MobileNetV2. Isso pode levar alguns minutos...\n")
+
+  erros_idx <- integer(0)
+
+  if (file.exists(arquivo_cache_parcial)) {
+    features_matrix <- readRDS(arquivo_cache_parcial)
+    linhas_prontas  <- which(complete.cases(features_matrix))
+    cat("Cache parcial encontrado:", length(linhas_prontas), "imagens ja processadas.\n")
+  } else {
+    n_features      <- as.integer(modelo_base$output_shape[[2]])
+    features_matrix <- matrix(NA_real_, nrow = nrow(metadados), ncol = n_features)
+    linhas_prontas  <- integer(0)
+  }
+
+  linhas_pendentes <- setdiff(seq_len(nrow(metadados)), linhas_prontas)
+
+  if (length(linhas_pendentes) > 0) {
+    for (inicio in seq(1, length(linhas_pendentes), by = tamanho_lote)) {
+      fim   <- min(inicio + tamanho_lote - 1L, length(linhas_pendentes))
+      idxs  <- linhas_pendentes[inicio:fim]
+      arquivos_bloco <- metadados$arquivo[idxs]
+
+      resultado <- tryCatch(
+        extrair_features_batch(arquivos_bloco, modelo_base, tamanho_imagem, length(idxs)),
+        error = function(e) {
+          message("Erro no lote ", inicio, "-", fim, ": ", conditionMessage(e))
+          erros_idx <<- c(erros_idx, idxs)
+          NULL
+        }
+      )
+
+      if (!is.null(resultado)) {
+        features_matrix[idxs, ] <- resultado
+      }
+
+      cat(sprintf("  Processadas: %d / %d\r", min(fim, length(linhas_pendentes)), length(linhas_pendentes)))
+
+      if (fim %% (tamanho_lote * 5L) == 0L) {
+        saveRDS(features_matrix, arquivo_cache_parcial)
+      }
+    }
+    cat("\n")
+    saveRDS(features_matrix, arquivo_cache_parcial)
+  }
+
+  imagens_ok <- complete.cases(features_matrix)
+  n_features <- ncol(features_matrix)
+  nomes_feat <- paste0("feat_", stringr::str_pad(seq_len(n_features), 4, pad = "0"))
+  colnames(features_matrix) <- nomes_feat
+
+  dados <- bind_cols(
+    metadados[imagens_ok, ],
+    as_tibble(features_matrix[imagens_ok, ])
+  )
+
+  if (length(erros_idx) > 0) {
+    erros_df <- metadados[erros_idx, ] %>% mutate(erro = "falha na extracao CNN")
+    arquivo_erros <- file.path(pasta_saida, "erros_processamento_rgb_completo.csv")
+    write_csv(erros_df, arquivo_erros)
+    cat("Imagens com erro:", length(erros_idx), "(ver", arquivo_erros, ")\n")
+  }
+
+  saveRDS(dados, arquivo_cache_features)
+  if (file.exists(arquivo_cache_parcial)) file.remove(arquivo_cache_parcial)
+
+  cat("Features salvas em:", arquivo_cache_features, "\n")
+}
+
+
Features carregadas do cache: outputs/completo/features_rgb_mobilenetv2_completo.rds 
+
+
cat("Imagens na base final:", nrow(dados), "\n")
+
+
Imagens na base final: 1248 
+
+
cat("Numero de atributos extraidos (MobileNetV2):", ncol(dados) - 3L, "\n")
+
+
Numero de atributos extraidos (MobileNetV2): 1280 
+
+
+
+
+

8 Separacao entre treino e teste

+
+
# ============================================================
+# 8. Separacao treino/teste
+# ============================================================
+
+set.seed(123)
+
+idx_treino <- createDataPartition(
+  y = dados$classe,
+  p = 0.70,
+  list = FALSE
+)
+
+treino <- dados[idx_treino, ]
+teste <- dados[-idx_treino, ]
+
+cat("Imagens no treino:", nrow(treino), "\n")
+
+
Imagens no treino: 884 
+
+
cat("Imagens no teste:", nrow(teste), "\n")
+
+
Imagens no teste: 364 
+
+
treino %>%
+  count(classe, name = "treino") %>%
+  full_join(
+    teste %>% count(classe, name = "teste"),
+    by = "classe"
+  ) %>%
+  arrange(classe) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
classetreinoteste
C16828
C26828
C36828
C46828
C56828
C66828
C76828
C86828
C96828
C106828
C116828
C126828
C136828
+
+
+
+
+

9 Pre-processamento

+
+
# ============================================================
+# 9. Preparar matrizes X e vetor y
+# ============================================================
+
+colunas_nao_preditoras <- c("arquivo", "classe", "tamanho_mb")
+
+x_treino <- treino %>%
+  select(-all_of(colunas_nao_preditoras))
+
+x_teste <- teste %>%
+  select(-all_of(colunas_nao_preditoras))
+
+y_treino <- droplevels(treino$classe)
+y_teste <- factor(teste$classe, levels = levels(y_treino))
+
+variaveis_nzv <- nearZeroVar(x_treino)
+
+if (length(variaveis_nzv) > 0) {
+  x_treino <- x_treino[, -variaveis_nzv, drop = FALSE]
+  x_teste <- x_teste[, colnames(x_treino), drop = FALSE]
+}
+
+preproc <- preProcess(
+  x_treino,
+  method = c("center", "scale")
+)
+
+x_treino_norm <- predict(preproc, x_treino)
+x_teste_norm <- predict(preproc, x_teste)
+
+cat("Preditores usados nos modelos:", ncol(x_treino_norm), "\n")
+
+
Preditores usados nos modelos: 1262 
+
+
+
+
+

10 Modelo 1: SVM linear

+
+
# ============================================================
+# 10. SVM linear
+# ============================================================
+
+set.seed(123)
+
+modelo_svm_linear <- svm(
+  x = x_treino_norm,
+  y = y_treino,
+  kernel = "linear",
+  cost = 1,
+  scale = FALSE
+)
+
+pred_svm_linear <- predict(modelo_svm_linear, x_teste_norm)
+
+cm_svm_linear <- confusionMatrix(pred_svm_linear, y_teste)
+
+cm_svm_linear
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1  12  4  1  1  1  3  1  0  8   1   1   1   1
+       C2   9 14  0  0  0  5  0  0  0   0   0   0   1
+       C3   0  1 10  1  0  0  0  2  4   0   0   0   5
+       C4   2  1  4 23  0  0  1  0  0   0   0   0   9
+       C5   0  0  5  0 21  0  0  0  0   0   0   0   2
+       C6   1  7  0  0  0 11  1  7  0   2   0   4   1
+       C7   0  0  2  1  0  1 11  7  0   0   0   8   2
+       C8   0  0  0  0  1  4  5  7  0   1   1   5   0
+       C9   2  1  3  0  0  1  0  1 13   0   0   3   1
+       C10  1  0  0  0  0  0  0  0  2   1  22   0   0
+       C11  1  0  1  1  0  1  0  0  1  22   3   0   0
+       C12  0  0  0  0  0  0  7  3  0   0   0   5   1
+       C13  0  0  2  1  5  2  2  1  0   1   1   2   5
+
+Overall Statistics
+                                          
+               Accuracy : 0.3736          
+                 95% CI : (0.3238, 0.4256)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.3214          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.42857   0.50000   0.35714   0.82143   0.75000
+Specificity            0.93155   0.95536   0.96131   0.94940   0.97917
+Pos Pred Value         0.34286   0.48276   0.43478   0.57500   0.75000
+Neg Pred Value         0.95137   0.95821   0.94721   0.98457   0.97917
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.03297   0.03846   0.02747   0.06319   0.05769
+Detection Prevalence   0.09615   0.07967   0.06319   0.10989   0.07692
+Balanced Accuracy      0.68006   0.72768   0.65923   0.88542   0.86458
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.39286   0.39286   0.25000   0.46429   0.035714
+Specificity            0.93155   0.93750   0.94940   0.96429   0.925595
+Pos Pred Value         0.32353   0.34375   0.29167   0.52000   0.038462
+Neg Pred Value         0.94848   0.94880   0.93824   0.95575   0.920118
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.076923
+Detection Rate         0.03022   0.03022   0.01923   0.03571   0.002747
+Detection Prevalence   0.09341   0.08791   0.06593   0.06868   0.071429
+Balanced Accuracy      0.66220   0.66518   0.59970   0.71429   0.480655
+                     Class: C11 Class: C12 Class: C13
+Sensitivity            0.107143    0.17857    0.17857
+Specificity            0.919643    0.96726    0.94940
+Pos Pred Value         0.100000    0.31250    0.22727
+Neg Pred Value         0.925150    0.93391    0.93275
+Prevalence             0.076923    0.07692    0.07692
+Detection Rate         0.008242    0.01374    0.01374
+Detection Prevalence   0.082418    0.04396    0.06044
+Balanced Accuracy      0.513393    0.57292    0.56399
+
+
+
+
+

11 Modelo 2: SVM radial

+
+
# ============================================================
+# 11. SVM radial com parametros fixos
+# ============================================================
+
+set.seed(123)
+
+modelo_svm_radial <- svm(
+  x = x_treino_norm,
+  y = y_treino,
+  kernel = "radial",
+  cost = 10,
+  gamma = 0.01,
+  scale = FALSE
+)
+
+pred_svm_radial <- predict(modelo_svm_radial, x_teste_norm)
+
+cm_svm_radial <- confusionMatrix(pred_svm_radial, y_teste)
+
+cm_svm_radial
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   4  4  0  0  0  2  0  0  5   2   2   0   0
+       C2   7  2  0  0  0  5  0  0  0   0   0   0   1
+       C3   0  0  2  1  0  0  0  2  4   0   0   0   2
+       C4   1  0  4  5  0  0  1  0  0   0   0   0   3
+       C5   0  0  3  0  8  0  0  0  0   0   0   0   2
+       C6   1  6  0  0  0  8  0  6  0   2   0   1   1
+       C7   0  0  0  0  0  1  0  4  0   0   0   6   1
+       C8   0  0  0  0  0  1  6  2  0   1   1   6   0
+       C9   0  0  3  0  0  0  2  3  4   0   0   2   0
+       C10  1  0  0  0  0  0  0  0  0   1  22   0   0
+       C11  1  0  1  0  0  1  0  0  1  19   0   0   0
+       C12  0  0  0  0  0  1  6  3  0   0   0   1   1
+       C13 13 16 15 22 20  9 13  8 14   3   3  12  17
+
+Overall Statistics
+                                          
+               Accuracy : 0.1484          
+                 95% CI : (0.1135, 0.1891)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : 3.007e-06       
+                                          
+                  Kappa : 0.0774          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.14286  0.071429  0.071429   0.17857   0.28571
+Specificity            0.95536  0.961310  0.973214   0.97321   0.98512
+Pos Pred Value         0.21053  0.133333  0.181818   0.35714   0.61538
+Neg Pred Value         0.93043  0.925501  0.926346   0.93429   0.94302
+Prevalence             0.07692  0.076923  0.076923   0.07692   0.07692
+Detection Rate         0.01099  0.005495  0.005495   0.01374   0.02198
+Detection Prevalence   0.05220  0.041209  0.030220   0.03846   0.03571
+Balanced Accuracy      0.54911  0.516369  0.522321   0.57589   0.63542
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.28571   0.00000  0.071429   0.14286   0.035714
+Specificity            0.94940   0.96429  0.955357   0.97024   0.931548
+Pos Pred Value         0.32000   0.00000  0.117647   0.28571   0.041667
+Neg Pred Value         0.94100   0.92045  0.925072   0.93143   0.920588
+Prevalence             0.07692   0.07692  0.076923   0.07692   0.076923
+Detection Rate         0.02198   0.00000  0.005495   0.01099   0.002747
+Detection Prevalence   0.06868   0.03297  0.046703   0.03846   0.065934
+Balanced Accuracy      0.61756   0.48214  0.513393   0.55655   0.483631
+                     Class: C11 Class: C12 Class: C13
+Sensitivity             0.00000   0.035714    0.60714
+Specificity             0.93155   0.967262    0.55952
+Pos Pred Value          0.00000   0.083333    0.10303
+Neg Pred Value          0.91789   0.923295    0.94472
+Prevalence              0.07692   0.076923    0.07692
+Detection Rate          0.00000   0.002747    0.04670
+Detection Prevalence    0.06319   0.032967    0.45330
+Balanced Accuracy       0.46577   0.501488    0.58333
+
+
+
+
+

12 Modelo 3: Ajuste de hiperparametros do SVM radial

+
+
# ============================================================
+# 12. Ajuste simples de hiperparametros do SVM radial
+# ============================================================
+
+set.seed(123)
+
+k_cv <- min(5, as.integer(min(table(y_treino))))
+
+if (k_cv >= 2) {
+  ajuste_svm_radial <- tune.svm(
+    x = x_treino_norm,
+    y = y_treino,
+    kernel = "radial",
+    cost = c(0.1, 1, 10, 100),
+    gamma = c(0.001, 0.01, 0.05, 0.1),
+    tunecontrol = tune.control(cross = k_cv)
+  )
+
+  modelo_svm_radial_ajustado <- ajuste_svm_radial$best.model
+
+  pred_svm_radial_ajustado <- predict(
+    modelo_svm_radial_ajustado,
+    x_teste_norm
+  )
+
+  cm_svm_radial_ajustado <- confusionMatrix(
+    pred_svm_radial_ajustado,
+    y_teste
+  )
+
+  ajuste_svm_radial$best.parameters %>%
+    kable()
+} else {
+  warning("Poucas amostras por classe para validacao cruzada.")
+  ajuste_svm_radial <- NULL
+  modelo_svm_radial_ajustado <- modelo_svm_radial
+  pred_svm_radial_ajustado <- pred_svm_radial
+  cm_svm_radial_ajustado <- cm_svm_radial
+}
+
+ + + + + + + + + + + + + + + +
gammacost
90.00110
+
+
+
+
cm_svm_radial_ajustado
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1  12  4  0  2  0  3  0  0  8   1   1   1   1
+       C2   9 14  0  0  0  5  0  0  0   0   0   0   1
+       C3   0  1 10  1  0  0  0  2  4   0   0   0   3
+       C4   2  1  5 20  0  0  2  0  0   0   0   0   8
+       C5   0  0  5  0 18  0  0  0  0   0   0   0   1
+       C6   1  7  0  0  0 10  0  8  0   1   0   4   2
+       C7   0  0  1  2  0  1  9  5  0   0   0   8   2
+       C8   0  0  0  0  1  4  7  8  0   1   1   5   1
+       C9   1  1  3  0  0  2  0  1 15   0   0   2   0
+       C10  1  0  0  0  0  0  0  0  0   1  22   0   0
+       C11  2  0  1  1  0  1  0  0  1  23   3   0   0
+       C12  0  0  1  1  0  0  7  3  0   0   0   5   1
+       C13  0  0  2  1  9  2  3  1  0   1   1   3   8
+
+Overall Statistics
+                                          
+               Accuracy : 0.3654          
+                 95% CI : (0.3158, 0.4172)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.3125          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.42857   0.50000   0.35714   0.71429   0.64286
+Specificity            0.93750   0.95536   0.96726   0.94643   0.98214
+Pos Pred Value         0.36364   0.48276   0.47619   0.52632   0.75000
+Neg Pred Value         0.95166   0.95821   0.94752   0.97546   0.97059
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.03297   0.03846   0.02747   0.05495   0.04945
+Detection Prevalence   0.09066   0.07967   0.05769   0.10440   0.06593
+Balanced Accuracy      0.68304   0.72768   0.66220   0.83036   0.81250
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.35714   0.32143   0.28571   0.53571   0.035714
+Specificity            0.93155   0.94345   0.94048   0.97024   0.931548
+Pos Pred Value         0.30303   0.32143   0.28571   0.60000   0.041667
+Neg Pred Value         0.94562   0.94345   0.94048   0.96165   0.920588
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.076923
+Detection Rate         0.02747   0.02473   0.02198   0.04121   0.002747
+Detection Prevalence   0.09066   0.07692   0.07692   0.06868   0.065934
+Balanced Accuracy      0.64435   0.63244   0.61310   0.75298   0.483631
+                     Class: C11 Class: C12 Class: C13
+Sensitivity            0.107143    0.17857    0.28571
+Specificity            0.913690    0.96131    0.93155
+Pos Pred Value         0.093750    0.27778    0.25806
+Neg Pred Value         0.924699    0.93353    0.93994
+Prevalence             0.076923    0.07692    0.07692
+Detection Rate         0.008242    0.01374    0.02198
+Detection Prevalence   0.087912    0.04945    0.08516
+Balanced Accuracy      0.510417    0.56994    0.60863
+
+
+
+
+

13 Modelo 4: PCA + SVM radial

+
+
# ============================================================
+# 13. PCA
+# ============================================================
+
+pca <- prcomp(
+  x_treino_norm,
+  center = FALSE,
+  scale. = FALSE
+)
+
+variancia <- pca$sdev^2
+variancia_exp <- variancia / sum(variancia)
+variancia_acum <- cumsum(variancia_exp)
+
+n_comp_95 <- which(variancia_acum >= 0.95)[1]
+n_comp <- min(n_comp_95, 30, ncol(x_treino_norm))
+
+cat("Componentes necessarios para 95% da variancia:", n_comp_95, "\n")
+
+
Componentes necessarios para 95% da variancia: 364 
+
+
cat("Componentes usados no modelo:", n_comp, "\n")
+
+
Componentes usados no modelo: 30 
+
+
+
+
tibble(
+  componente = seq_along(variancia_acum),
+  variancia_acumulada = variancia_acum
+) %>%
+  ggplot(aes(x = componente, y = variancia_acumulada)) +
+  geom_line(color = "#2C7FB8") +
+  geom_point(color = "#2C7FB8") +
+  geom_hline(yintercept = 0.95, linetype = "dashed") +
+  labs(
+    title = "Variancia acumulada pelo PCA (dataset completo)",
+    x = "Numero de componentes principais",
+    y = "Variancia acumulada"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
x_treino_pca <- as.data.frame(pca$x[, 1:n_comp, drop = FALSE])
+
+x_teste_pca <- as.data.frame(
+  predict(pca, newdata = x_teste_norm)[, 1:n_comp, drop = FALSE]
+)
+
+set.seed(123)
+
+modelo_svm_pca <- svm(
+  x = x_treino_pca,
+  y = y_treino,
+  kernel = "radial",
+  cost = 10,
+  gamma = 0.01,
+  scale = FALSE
+)
+
+pred_svm_pca <- predict(modelo_svm_pca, x_teste_pca)
+
+cm_svm_pca <- confusionMatrix(pred_svm_pca, y_teste)
+
+cm_svm_pca
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   9  4  0  2  0  5  1  0  6   1   1   0   2
+       C2  10 10  0  0  0  5  0  0  0   0   0   0   2
+       C3   0  0  9  1  0  0  0  3  4   0   0   0   2
+       C4   2  1  5 15  0  0  1  0  0   0   0   1   6
+       C5   0  1  5  0 14  0  0  0  0   0   0   0   1
+       C6   1  6  0  0  0  9  1  7  0   1   0   1   0
+       C7   1  0  0  4  0  1  9  5  0   0   0   8   3
+       C8   0  0  0  0  0  1  6  5  0   1   0   5   1
+       C9   1  1  3  0  0  1  1  3 13   0   0   3   0
+       C10  1  0  0  0  0  0  0  0  1   3  24   0   0
+       C11  1  1  1  1  1  3  0  0  1  20   1   0   0
+       C12  1  0  0  1  0  1  6  4  0   0   0   7   2
+       C13  1  4  5  4 13  2  3  1  3   2   2   3   9
+
+Overall Statistics
+                                          
+               Accuracy : 0.3104          
+                 95% CI : (0.2632, 0.3607)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.253           
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.32143   0.35714   0.32143   0.53571   0.50000
+Specificity            0.93452   0.94940   0.97024   0.95238   0.97917
+Pos Pred Value         0.29032   0.37037   0.47368   0.48387   0.66667
+Neg Pred Value         0.94294   0.94659   0.94493   0.96096   0.95918
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.02473   0.02747   0.02473   0.04121   0.03846
+Detection Prevalence   0.08516   0.07418   0.05220   0.08516   0.05769
+Balanced Accuracy      0.62798   0.65327   0.64583   0.74405   0.73958
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.32143   0.32143   0.17857   0.46429   0.107143
+Specificity            0.94940   0.93452   0.95833   0.96131   0.922619
+Pos Pred Value         0.34615   0.29032   0.26316   0.50000   0.103448
+Neg Pred Value         0.94379   0.94294   0.93333   0.95562   0.925373
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.076923
+Detection Rate         0.02473   0.02473   0.01374   0.03571   0.008242
+Detection Prevalence   0.07143   0.08516   0.05220   0.07143   0.079670
+Balanced Accuracy      0.63542   0.62798   0.56845   0.71280   0.514881
+                     Class: C11 Class: C12 Class: C13
+Sensitivity            0.035714    0.25000    0.32143
+Specificity            0.913690    0.95536    0.87202
+Pos Pred Value         0.033333    0.31818    0.17308
+Neg Pred Value         0.919162    0.93860    0.93910
+Prevalence             0.076923    0.07692    0.07692
+Detection Rate         0.002747    0.01923    0.02473
+Detection Prevalence   0.082418    0.06044    0.14286
+Balanced Accuracy      0.474702    0.60268    0.59673
+
+
+
+
+

14 Modelo 5: Random Forest

+
+
# ============================================================
+# 14. Random Forest
+# ============================================================
+
+set.seed(123)
+
+modelo_rf <- randomForest(
+  x = x_treino_norm,
+  y = y_treino,
+  ntree = 500,
+  importance = TRUE
+)
+
+pred_rf <- predict(modelo_rf, x_teste_norm)
+
+cm_rf <- confusionMatrix(pred_rf, y_teste)
+
+cm_rf
+
+
Confusion Matrix and Statistics
+
+          Reference
+Prediction C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13
+       C1   9  4  1  3  0  3  0  0  8   2   2   0   3
+       C2   9 10  0  0  0  5  0  0  0   0   0   0   1
+       C3   0  1 10  1  0  0  0  2  4   0   0   0   2
+       C4   5  2  5 18  0  0  2  0  0   0   0   0   6
+       C5   0  0  5  0 22  0  0  0  0   0   0   0   4
+       C6   1  6  0  0  0 11  0  9  0   1   0   4   2
+       C7   0  0  1  3  0  1  7  4  0   0   0   7   3
+       C8   0  1  0  0  0  1  8  7  1   1   0   4   1
+       C9   1  3  1  0  0  0  0  0 13   0   0   2   0
+       C10  1  0  2  1  0  1  0  1  0   3  24   0   0
+       C11  2  1  1  0  1  2  0  0  2  20   1   0   0
+       C12  0  0  0  1  1  2  8  4  0   0   0   8   1
+       C13  0  0  2  1  4  2  3  1  0   1   1   3   5
+
+Overall Statistics
+                                          
+               Accuracy : 0.3407          
+                 95% CI : (0.2921, 0.3919)
+    No Information Rate : 0.0769          
+    P-Value [Acc > NIR] : < 2.2e-16       
+                                          
+                  Kappa : 0.2857          
+                                          
+ Mcnemar's Test P-Value : NA              
+
+Statistics by Class:
+
+                     Class: C1 Class: C2 Class: C3 Class: C4 Class: C5
+Sensitivity            0.32143   0.35714   0.35714   0.64286   0.78571
+Specificity            0.92262   0.95536   0.97024   0.94048   0.97321
+Pos Pred Value         0.25714   0.40000   0.50000   0.47368   0.70968
+Neg Pred Value         0.94225   0.94690   0.94767   0.96933   0.98198
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.07692
+Detection Rate         0.02473   0.02747   0.02747   0.04945   0.06044
+Detection Prevalence   0.09615   0.06868   0.05495   0.10440   0.08516
+Balanced Accuracy      0.62202   0.65625   0.66369   0.79167   0.87946
+                     Class: C6 Class: C7 Class: C8 Class: C9 Class: C10
+Sensitivity            0.39286   0.25000   0.25000   0.46429   0.107143
+Specificity            0.93155   0.94345   0.94940   0.97917   0.910714
+Pos Pred Value         0.32353   0.26923   0.29167   0.65000   0.090909
+Neg Pred Value         0.94848   0.93787   0.93824   0.95640   0.924471
+Prevalence             0.07692   0.07692   0.07692   0.07692   0.076923
+Detection Rate         0.03022   0.01923   0.01923   0.03571   0.008242
+Detection Prevalence   0.09341   0.07143   0.06593   0.05495   0.090659
+Balanced Accuracy      0.66220   0.59673   0.59970   0.72173   0.508929
+                     Class: C11 Class: C12 Class: C13
+Sensitivity            0.035714    0.28571    0.17857
+Specificity            0.913690    0.94940    0.94643
+Pos Pred Value         0.033333    0.32000    0.21739
+Neg Pred Value         0.919162    0.94100    0.93255
+Prevalence             0.076923    0.07692    0.07692
+Detection Rate         0.002747    0.02198    0.01374
+Detection Prevalence   0.082418    0.06868    0.06319
+Balanced Accuracy      0.474702    0.61756    0.56250
+
+
+
+
+

15 Comparacao dos modelos

+
+
# ============================================================
+# 15. Funcao de avaliacao dos modelos
+# ============================================================
+
+avaliar_modelo <- function(nome, matriz_confusao) {
+  overall <- matriz_confusao$overall
+  by_class <- matriz_confusao$byClass
+
+  if (is.matrix(by_class)) {
+    sensibilidade_macro <- mean(by_class[, "Sensitivity"], na.rm = TRUE)
+    especificidade_macro <- mean(by_class[, "Specificity"], na.rm = TRUE)
+    f1_macro <- mean(by_class[, "F1"], na.rm = TRUE)
+  } else {
+    sensibilidade_macro <- by_class["Sensitivity"]
+    especificidade_macro <- by_class["Specificity"]
+    f1_macro <- by_class["F1"]
+  }
+
+  tibble(
+    modelo = nome,
+    acuracia = as.numeric(overall["Accuracy"]),
+    kappa = as.numeric(overall["Kappa"]),
+    sensibilidade_macro = as.numeric(sensibilidade_macro),
+    especificidade_macro = as.numeric(especificidade_macro),
+    f1_macro = as.numeric(f1_macro)
+  )
+}
+
+resultados <- bind_rows(
+  avaliar_modelo("SVM linear", cm_svm_linear),
+  avaliar_modelo("SVM radial", cm_svm_radial),
+  avaliar_modelo("SVM radial ajustado", cm_svm_radial_ajustado),
+  avaliar_modelo("PCA + SVM radial", cm_svm_pca),
+  avaliar_modelo("Random Forest", cm_rf)
+) %>%
+  arrange(desc(acuracia))
+
+resultados %>%
+  mutate(across(where(is.numeric), ~ round(.x, 4))) %>%
+  kable(caption = "Resultados - Dataset Completo (Holdout 70/30)")
+
+ + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Resultados - Dataset Completo (Holdout 70/30)
modeloacuraciakappasensibilidade_macroespecificidade_macrof1_macro
SVM linear0.37360.32140.37360.94780.3646
SVM radial ajustado0.36540.31250.36540.94710.3630
Random Forest0.34070.28570.34070.94510.3405
PCA + SVM radial0.31040.25300.31040.94250.3161
SVM radial0.14840.07740.14840.92900.1673
+
+
+
+
ggplot(resultados, aes(x = reorder(modelo, acuracia), y = acuracia)) +
+  geom_col(fill = "#2C7FB8") +
+  coord_flip() +
+  labs(
+    title = "Comparacao da acuracia dos modelos (dataset completo)",
+    x = "Modelo",
+    y = "Acuracia"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

16 Importancia das variaveis

+
+
# ============================================================
+# 16. Importancia das variaveis
+# ============================================================
+
+importancia <- importance(modelo_rf)
+
+coluna_importancia <- if ("MeanDecreaseGini" %in% colnames(importancia)) {
+  "MeanDecreaseGini"
+} else {
+  colnames(importancia)[1]
+}
+
+importancia_df <- tibble(
+  variavel = rownames(importancia),
+  importancia = importancia[, coluna_importancia]
+) %>%
+  arrange(desc(importancia))
+
+importancia_df %>%
+  slice_head(n = 20) %>%
+  kable()
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
variavelimportancia
feat_04722.569848
feat_10031.884104
feat_03211.852070
feat_08501.828746
feat_10911.736351
feat_04761.654010
feat_06891.572648
feat_09141.548601
feat_05621.521096
feat_02601.492961
feat_09711.416875
feat_12621.345804
feat_07781.340887
feat_04731.325315
feat_00651.320592
feat_02311.298018
feat_07801.296183
feat_02781.287194
feat_01901.277953
feat_00941.270837
+
+
+
+
+

17 Salvamento dos resultados

+
+
# ============================================================
+# 17. Salvar saidas (nomes diferentes do original)
+# ============================================================
+
+dir.create(pasta_saida, recursive = TRUE, showWarnings = FALSE)
+
+write_csv(dados, file.path(pasta_saida, "features_rgb_completo.csv"))
+write_csv(resultados, file.path(pasta_saida, "resultados_modelos_rgb_completo.csv"))
+write_csv(importancia_df, file.path(pasta_saida, "importancia_variaveis_rgb_completo.csv"))
+
+saveRDS(
+  list(
+    preproc                    = preproc,
+    modelo_svm_linear          = modelo_svm_linear,
+    modelo_svm_radial          = modelo_svm_radial,
+    ajuste_svm_radial          = ajuste_svm_radial,
+    modelo_svm_radial_ajustado = modelo_svm_radial_ajustado,
+    modelo_svm_pca             = modelo_svm_pca,
+    modelo_rf                  = modelo_rf,
+    pca                        = pca,
+    resultados                 = resultados,
+    extrator_cnn               = "MobileNetV2 (ImageNet, sem topo, pooling=avg, 1280 features)"
+  ),
+  file.path(pasta_saida, "modelos_rgb_completo.rds")
+)
+
+cat("Arquivos salvos em:", pasta_saida, "\n")
+
+
Arquivos salvos em: outputs/completo 
+
+
+
+
+

18 Validacao Cruzada K-Fold

+
+
# ============================================================
+# 18. Validacao cruzada k-fold (k=5, estratificada)
+# ============================================================
+
+set.seed(123)
+
+k_folds <- 5L
+
+folds <- createFolds(dados$classe, k = k_folds, list = TRUE, returnTrain = FALSE)
+
+grade_cost_kfold  <- c(1, 10, 100)
+grade_gamma_kfold <- c(0.001, 0.01, 0.05, 0.1)
+
+metricas_folds <- vector("list", k_folds)
+
+for (fold_i in seq_len(k_folds)) {
+  idx_teste  <- folds[[fold_i]]
+  idx_treino <- setdiff(seq_len(nrow(dados)), idx_teste)
+
+  fold_treino <- dados[idx_treino, ]
+  fold_teste  <- dados[idx_teste,  ]
+
+  x_fold_tr <- fold_treino %>% select(-all_of(colunas_nao_preditoras))
+  x_fold_te <- fold_teste  %>% select(-all_of(colunas_nao_preditoras))
+  y_fold_tr <- droplevels(fold_treino$classe)
+  y_fold_te <- factor(fold_teste$classe, levels = levels(y_fold_tr))
+
+  nzv_fold <- nearZeroVar(x_fold_tr)
+  if (length(nzv_fold) > 0) {
+    x_fold_tr <- x_fold_tr[, -nzv_fold, drop = FALSE]
+    x_fold_te <- x_fold_te[, colnames(x_fold_tr), drop = FALSE]
+  }
+
+  pp_fold      <- preProcess(x_fold_tr, method = c("center", "scale"))
+  x_fold_tr_n  <- predict(pp_fold, x_fold_tr)
+  x_fold_te_n  <- predict(pp_fold, x_fold_te)
+
+  m1 <- svm(x = x_fold_tr_n, y = y_fold_tr,
+            kernel = "linear", cost = 1, scale = FALSE)
+
+  m2 <- svm(x = x_fold_tr_n, y = y_fold_tr,
+            kernel = "radial", cost = 10, gamma = 0.01, scale = FALSE)
+
+  k_cv_fold <- min(5L, min(table(y_fold_tr)))
+  ajuste_fold <- tune.svm(
+    x = x_fold_tr_n,
+    y = y_fold_tr,
+    kernel = "radial",
+    cost = grade_cost_kfold,
+    gamma = grade_gamma_kfold,
+    tunecontrol = tune.control(cross = k_cv_fold)
+  )
+  m3 <- ajuste_fold$best.model
+
+  pca_fold    <- prcomp(x_fold_tr_n, center = FALSE, scale. = FALSE)
+  var_acum_f  <- cumsum(pca_fold$sdev^2 / sum(pca_fold$sdev^2))
+  n_comp_f    <- min(which(var_acum_f >= 0.95)[1], 30L, ncol(x_fold_tr_n))
+  x_pca_tr    <- as.data.frame(pca_fold$x[, 1:n_comp_f, drop = FALSE])
+  x_pca_te    <- as.data.frame(predict(pca_fold, x_fold_te_n)[, 1:n_comp_f, drop = FALSE])
+  m4 <- svm(x = x_pca_tr, y = y_fold_tr,
+            kernel = "radial", cost = 10, gamma = 0.01, scale = FALSE)
+
+  m5 <- randomForest(x = x_fold_tr_n, y = y_fold_tr, ntree = 200L)
+
+  metricas_folds[[fold_i]] <- bind_rows(
+    avaliar_modelo("SVM linear",          confusionMatrix(predict(m1, x_fold_te_n), y_fold_te)),
+    avaliar_modelo("SVM radial",          confusionMatrix(predict(m2, x_fold_te_n), y_fold_te)),
+    avaliar_modelo("SVM radial ajustado", confusionMatrix(predict(m3, x_fold_te_n), y_fold_te)),
+    avaliar_modelo("PCA + SVM radial",    confusionMatrix(predict(m4, x_pca_te),    y_fold_te)),
+    avaliar_modelo("Random Forest",       confusionMatrix(predict(m5, x_fold_te_n), y_fold_te))
+  ) %>% mutate(fold = fold_i)
+
+  cat(sprintf(
+    "Fold %d/%d concluido. SVM ajustado: cost = %s | gamma = %s\n",
+    fold_i, k_folds,
+    ajuste_fold$best.parameters$cost,
+    ajuste_fold$best.parameters$gamma
+  ))
+}
+
+
Fold 1/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 2/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 3/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 4/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+Fold 5/5 concluido. SVM ajustado: cost = 10 | gamma = 0.001
+
+
+
+
resultados_kfold <- bind_rows(metricas_folds) %>%
+  group_by(modelo) %>%
+  summarise(across(c(acuracia, kappa, sensibilidade_macro, especificidade_macro, f1_macro), mean),
+            .groups = "drop") %>%
+  arrange(desc(acuracia))
+
+write_csv(resultados_kfold, file.path(pasta_saida, "resultados_kfold_rgb_completo.csv"))
+
+resultados_kfold %>%
+  mutate(across(where(is.numeric), ~ round(.x, 4))) %>%
+  kable(caption = "Acuracia media - K-Fold (k=5) - Dataset Completo")
+
+ + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Acuracia media - K-Fold (k=5) - Dataset Completo
modeloacuraciakappasensibilidade_macroespecificidade_macrof1_macro
SVM linear0.36300.31000.36340.94690.3835
SVM radial ajustado0.35650.30290.35680.94640.3721
Random Forest0.32850.27260.32880.94400.3406
PCA + SVM radial0.32360.26740.32390.94360.3593
SVM radial0.14190.07090.14330.92850.1743
+
+
+
+
+

19 Comparacao: Holdout vs K-Fold

+
+
# ============================================================
+# 19. Comparacao direta entre as duas estrategias
+# ============================================================
+
+comparacao <- bind_rows(
+  resultados      %>% mutate(estrategia = "Holdout 70/30"),
+  resultados_kfold %>% mutate(estrategia = "K-Fold (k=5)")
+) %>%
+  select(estrategia, modelo, acuracia, kappa, f1_macro) %>%
+  arrange(estrategia, desc(acuracia))
+
+comparacao %>%
+  mutate(across(where(is.numeric), ~ round(.x, 4))) %>%
+  kable(caption = "Comparacao entre Holdout 70/30 e K-Fold - Dataset Completo")
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Comparacao entre Holdout 70/30 e K-Fold - Dataset Completo
estrategiamodeloacuraciakappaf1_macro
Holdout 70/30SVM linear0.37360.32140.3646
Holdout 70/30SVM radial ajustado0.36540.31250.3630
Holdout 70/30Random Forest0.34070.28570.3405
Holdout 70/30PCA + SVM radial0.31040.25300.3161
Holdout 70/30SVM radial0.14840.07740.1673
K-Fold (k=5)SVM linear0.36300.31000.3835
K-Fold (k=5)SVM radial ajustado0.35650.30290.3721
K-Fold (k=5)Random Forest0.32850.27260.3406
K-Fold (k=5)PCA + SVM radial0.32360.26740.3593
K-Fold (k=5)SVM radial0.14190.07090.1743
+
+
+
+
ggplot(comparacao, aes(x = reorder(modelo, acuracia), y = acuracia, fill = estrategia)) +
+  geom_col(position = "dodge") +
+  coord_flip() +
+  scale_fill_manual(values = c("Holdout 70/30" = "#2C7FB8", "K-Fold (k=5)" = "#E06C00")) +
+  labs(
+    title = "Acuracia: Holdout 70/30 vs K-Fold (k=5) - Dataset Completo",
+    x = "Modelo",
+    y = "Acuracia",
+    fill = "Estrategia"
+  ) +
+  theme_minimal()
+
+
+
+

+
+
+
+
+
+
+

20 Conclusao

+
+
melhor_holdout  <- max(resultados$acuracia)
+melhor_kf       <- max(resultados_kfold$acuracia)
+
+cat("Acuracia maxima - Holdout:", round(melhor_holdout, 4), "\n")
+
+
Acuracia maxima - Holdout: 0.3736 
+
+
cat("Acuracia maxima - K-Fold: ", round(melhor_kf,      4), "\n")
+
+
Acuracia maxima - K-Fold:  0.363 
+
+
cat("Melhor modelo (holdout):",   resultados$modelo[1], "\n")
+
+
Melhor modelo (holdout): SVM linear 
+
+
cat("Melhor modelo (k-fold):",    resultados_kfold$modelo[1], "\n")
+
+
Melhor modelo (k-fold): SVM linear 
+
+
+
+ +
+ + +
+ + + + + \ No newline at end of file