Metadata-Version: 2.4
Name: fakenumdetect
Version: 0.1.0
Summary: Detecção de indícios de dados numéricos fabricados, combinando forense numérica e aprendizado de máquina.
Author: Giovanna Paiva Alves
License-Expression: MIT
Project-URL: Repositorio, https://github.com/GiPaiva/FakeNumDetect
Keywords: fabricação de dados,Lei de Benford,detecção de anomalias,forense numérica,autoencoder,isolation forest
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.0
Requires-Dist: scipy>=1.11
Requires-Dist: scikit-learn>=1.4
Requires-Dist: torch>=2.0
Requires-Dist: jinja2>=3.1
Requires-Dist: matplotlib>=3.8
Requires-Dist: openpyxl>=3.1
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Dynamic: license-file

# FakeNumDetect

**FakeNumDetect** é uma biblioteca Python, em desenvolvimento, para apontar **indícios** de dados numéricos fabricados ou manipulados, combinando testes estatísticos de forense numérica e modelos de Machine Learning.

Projeto de Trabalho de Conclusão de Curso (TCC2, SENAC). O TCC1 definiu a proposta e a fundamentação; o TCC2 é a etapa de implementação e validação experimental.

> **Importante:** um resultado "suspeito" da FakeNumDetect é um indício estatístico que merece investigação, e não uma prova de fraude ou fabricação.

## Status do projeto

**Em desenvolvimento.** o que ja tem hoje:

| Parte | Situação |
|---|---|
| Testes estatísticos: Benford (1º e 2º dígito), GRIM, SPRITE, entropia de Shannon, terminação de dígitos | ✅ implementados e testados |
| Machine Learning: Isolation Forest e Sparse Autoencoder | ✅ implementados, calibrados e avaliados em 4 datasets reais |
| Score de suspeição unificado por registro (0 a 100, pesos calibrados por regressão logística L2) | ✅ implementado (combina Isolation Forest, Sparse Autoencoder e Benford) |
| Análise por coluna com correção de múltiplas comparações (Bonferroni e Benjamini-Hochberg) | ✅ implementada |
| Consistência do arredondamento entre colunas comparáveis (`comparar_arredondamento`) | ✅ implementada; indica que colunas não combinam, mas não qual delas é a fabricada |
| Avaliação: métricas, validação cruzada, Wilcoxon, bootstrap, Friedman | ✅ implementada |
| Relatório em HTML com gráficos (registros e colunas) | ✅ implementado; o PDF é gerado pelo "Imprimir > Salvar como PDF" do navegador |
| API única no estilo scikit-learn (`fit`/`detect`/`report`, classe `FakeNumDetect`) | ✅ implementada; encadeia os módulos no mesmo caminho da validação cruzada |
| Empacotamento e integração contínua (`pyproject.toml`, GitHub Actions em Python 3.10 e 3.13, cobertura mínima de 90%) | ✅ configurados; pacote validado com `twine check` e testado instalado fora do repositório |
| Publicação no PyPI | ⏳ pacote pronto; falta o envio pela conta da autora |

## Como a biblioteca analisa os dados

A FakeNumDetect trabalha em **dois níveis**:

1. **Por registro (linha):** o score de suspeição unificado combina Isolation Forest, Sparse Autoencoder e um score de Benford por registro. Responde *quais linhas* são suspeitas.
2. **Por coluna:** Benford e terminação de dígitos são aplicados a cada coluna, com correção para múltiplas comparações. Responde *quais colunas* parecem ter valores fabricados.

GRIM e SPRITE ficam disponíveis como funções independentes: eles verificam médias e desvios-padrão **reportados** (por exemplo, numa tabela de um artigo), e não dados brutos. Com as médias publicadas em três artigos de pizza do Cornell Food and Brand Lab, o GRIM concordou com o julgamento de especialistas (van der Zee, Anaya e Brown, 2017) em 96 de 97 médias, e o SPRITE recusou os 13 desvios-padrão que eles apontaram como impossíveis. Num cenário sintético com verdade matemática conhecida (escala de 1 a 5, n de 10 a 50), nenhum dos 400 casos honestos foi acusado, e as duas técnicas pegaram todas as 170 estatísticas retocadas à mão que ficaram impossíveis (85% das 200 simuladas).

A terminação de dígitos também tem um score por registro (`terminacao_record_suspicion_score`), que compara o último dígito de cada valor com a frequência observada nos dados de referência. Ele não entra no score unificado; foi avaliado à parte e só ajudou nos dados do seguro (ver Principais resultados).

## Estrutura do repositório

```
FakeNumDetect/
├── fakenumdetect/            # código-fonte da biblioteca
│   ├── estatisticas/         # Benford, GRIM, SPRITE, Shannon, terminação de dígitos, análise por coluna
│   ├── ml/                   # Isolation Forest e Sparse Autoencoder
│   ├── preprocessamento/     # DatasetBundle, divisões treino/validação/teste, k-fold, carregadores dos datasets
│   ├── pontuacao/            # score de suspeição unificado e ensembles
│   ├── avaliacao/            # métricas, comparação estatística, correção de múltiplas comparações
│   ├── relatorio/            # geração do relatório em HTML
│   ├── api/                  # classe FakeNumDetect (fit, detect, report)
│   └── ModuleExcecoes.py     # erros com nome próprio da biblioteca
├── tests/                    # testes automatizados (pytest), com dados reais em tests/fixtures/
├── experiments/              # scripts que reproduzem os experimentos do TCC2 (e o download verificado dos dados)
├── exemplos/                 # notebook de uso (Google Colab ou Jupyter)
├── results/                  # resultados dos experimentos (.jsonl)
├── .github/workflows/        # integração contínua (testes e cobertura mínima de 90%)
├── pyproject.toml            # empacotamento da biblioteca (versões mínimas)
├── requirements.txt          # ambiente exato dos resultados (versões fixas)
└── README.md
```

As pastas `datasets/` e `models/` existem localmente e ficam fora do Git.

## Instalação

Requer Python 3.10 ou superior (desenvolvido com 3.13).

Depois da publicação no PyPI:

```bash
pip install fakenumdetect
```

## Instalação (ambiente de desenvolvimento)

Requer Python 3.10 ou superior (desenvolvido com 3.13).

```bash
git clone https://github.com/GiPaiva/FakeNumDetect.git
cd FakeNumDetect
python -m venv venv
source venv/bin/activate      # Windows: venv\Scripts\activate
pip install -r requirements.txt
```

O `requirements.txt` fixa as versões exatas do ambiente em que os testes e os resultados de `results/` foram gerados (Python 3.13.7), incluindo o PyOD, usado só num experimento. O `pyproject.toml`, usado por `pip install fakenumdetect`, aceita versões mais novas, para a biblioteca não entrar em conflito com outros pacotes do projeto de quem a usa.

Para rodar os testes:

```bash
python -m pytest -q
```

Para medir a cobertura de testes da biblioteca (linhas e ramos):

```bash
python -m pytest -q --cov=fakenumdetect --cov-branch --cov-report=term-missing
```

Cobertura atual: 97% (354 testes, medida com o comando acima).

## Exemplos de uso

Os exemplos usam arquivos reais que já estão em `tests/fixtures/`.

### Uso rápido: `fit`, `detect` e `report`

```python
from fakenumdetect import FakeNumDetect
from fakenumdetect.preprocessamento.ModuleLoaderADBench import load_adbench

bundle = load_adbench("tests/fixtures/4_breastw.npz")
det = FakeNumDetect().fit(bundle.X_train)   # referência: só registros genuínos
res = det.detect(bundle.X_test)

print(res.registros.head(3))                # scores 0 a 100 por técnica, score unificado e sinalização
print(res.registros["sinalizado"].sum(), "de", len(res.registros), "registros sinalizados")   # 227 de 328
print(res.colunas["colunas_suspeitas"])     # []
det.report(res, "relatorio.html")
```

- `fit(X)` recebe dados de referência que você acredita serem **genuínos** (matriz ou `DataFrame`, sem valores ausentes). Ele treina o Isolation Forest (com os valores do artigo original: 100 árvores, subamostra de até 256 registros e `contamination="auto"`) e o Sparse Autoencoder, a normalização de cada técnica e o limiar (percentil 95 do score unificado numa parte da referência separada para validação).
- Por padrão, as técnicas são combinadas pela **média simples**. Se você tiver dados rotulados (1 = fabricado, com as duas classes), `fit(X, X_cal=..., y_cal=...)` calibra os pesos por regressão logística.
- `detect(X)` devolve `registros` (um `DataFrame` com o mesmo índice da entrada), `limiar`, `colunas` (análise por coluna) e, com `colunas_comparaveis=[["col_a", "col_b"]]`, `arredondamento` (consistência do arredondamento entre essas colunas).
- `report(res, caminho)` gera o relatório em HTML.
- `res.laudo["tabela"]`: o **laudo do lote** com as 7 técnicas. Cada uma diz quanto suspeita do lote inteiro (0 a 100) ou por que não se aplica, e a linha "Juntas" combina as que opinaram (correção de Simes). GRIM e SPRITE entram com `det.detect(dados, estatisticas_publicadas=tabela)` (colunas `media`, `n`, `dp`, `minimo`, `maximo`; média e DP como texto). Dois botões de 0 a 5: `confianca_referencia` (5 = comparar com os dados de referência; 0 = com a Lei de Benford e finais uniformes) e `rigor` (5 = aceita 5% de alarme falso; 0 = 0,1%). Escolha os dois antes de olhar o resultado. No experimento (`run_laudo.py`), o laudo separou sem erro a fabricação real do seguro, mas não superou a melhor técnica individual nas fabricações simuladas do JarFraud e, com valores inventados, ficou significativamente abaixo do teste de Benford.
- Erros de dado têm nome próprio (`EmptyDataError`, `InsufficientDataError`, `InvalidDataTypeError`, `BenfordInapplicableError`, `NoApplicableTechniqueError`), importáveis de `fakenumdetect`. Todos herdam de `FakeNumDetectError` (e de `ValueError`): `except FakeNumDetectError` trata qualquer problema no dado.
- Opcional: `FakeNumDetect(regras_aplicabilidade=True)` tira da nota por registro as técnicas que não se aplicam ao tipo de dado (regra do TCC1): o Benford só usa as colunas cujos números variam pelo menos 2 ordens de grandeza, e o Autoencoder exige 2 colunas ou mais. O resultado informa as técnicas usadas e as excluídas (`res.tecnicas`), e o relatório também. Na validação cruzada, a regra não melhorou a nota em nenhum dos 4 datasets e piorou a média ponderada no AcademicAnomaly; por isso é opção, e não padrão.

Para usar no **Google Colab** ou no **Jupyter**, abra o notebook `exemplos/uso_fakenumdetect.ipynb`: ele instala a biblioteca, roda os três passos com um conjunto de dados real que já vem no scikit-learn, mostra o relatório dentro do notebook e traz uma seção para enviar o seu próprio CSV.

Cuidados, vindos dos experimentos: a referência precisa ser genuína (registros fabricados no treino prejudicam muito o autoencoder); o limiar do percentil 95 sinaliza, por construção, cerca de 5% dos registros genuínos; e os testes de dígitos indicam desvio de uma distribuição de referência, não fabricação.

### Teste de Benford em uma coluna

```python
import numpy as np
from fakenumdetect.estatisticas.ModuleBenford import analyze_first_digit

dados = np.load("tests/fixtures/jarfraud_colunas_amostra.npz")
vendas = dados["X"][:, list(dados["feature_names"]).index("sale")]

r = analyze_first_digit(vendas)
print(r["p_value"], r["mad"], r["conformity"])   # p ~0.09, MAD ~0.0035, "close conformity"
print(r["digitos_desviantes"])                   # estatística Z por dígito: [{"digito": 7, "direcao": "excesso", ...}]
```

### Análise por coluna com correção de múltiplas comparações

```python
import numpy as np
from fakenumdetect.estatisticas.ModuleAnaliseColunas import analisar_colunas

dados = np.load("tests/fixtures/jarfraud_colunas_amostra.npz")
r = analisar_colunas(dados["X"], list(dados["feature_names"]), correcao="bh")

print(r["colunas_suspeitas"])        # [] : dados contábeis reais seguem Benford
for c in r["colunas"]:
    print(c["nome"], c["benford"]["conformidade"], c["suspeita"])
```

Por padrão, uma coluna só é sinalizada quando o p-valor corrigido é significativo **e** o MAD indica desvio relevante (faixas de Nigrini). Isso evita sinalizar colunas genuínas em amostras grandes, onde o qui-quadrado rejeita desvios minúsculos. Quando o Benford se aplica, o resultado também lista os dígitos que se afastam do esperado (`c["benford"]["digitos_desviantes"]`, estatística Z de Nigrini com correção BH entre os dígitos); essa lista explica o desvio, mas não decide a suspeita. Cada teste só é aplicado quando faz sentido para a coluna (por exemplo, Benford exige valores que cubram pelo menos duas ordens de grandeza).

### Score de suspeição de um registro com Isolation Forest

```python
from fakenumdetect.preprocessamento.ModuleLoaderADBench import load_adbench
from fakenumdetect.ml.ModuleIsolationForest import calibrate_isolation_forest, raw_suspicion_score

bundle = load_adbench("tests/fixtures/4_breastw.npz")
modelo = calibrate_isolation_forest(bundle.X_train, bundle.X_val)["model"]
scores = raw_suspicion_score(modelo, bundle.X_test)   # quanto maior, mais suspeito
```

O fluxo completo do score unificado (normalização de cada técnica para 0 a 100, calibração de pesos e limiar) é o que a classe `FakeNumDetect` faz; a versão usada na validação cruzada está em `experiments/run_kfold_comparacao.py`.

### Relatório em HTML

```python
import numpy as np
from fakenumdetect.estatisticas.ModuleAnaliseColunas import analisar_colunas
from fakenumdetect.relatorio.ModuleRelatorio import gerar_relatorio_html

dados = np.load("tests/fixtures/jarfraud_colunas_amostra.npz")
analise = analisar_colunas(dados["X"], list(dados["feature_names"]))
gerar_relatorio_html("relatorio.html", analise_colunas=analise)
```

O relatório também aceita o score unificado de cada registro (`scores_registros`, escala 0 a 100) e o limiar (`limiar_registros`). O resultado é um único arquivo HTML, com os gráficos embutidos, que abre em qualquer navegador. Para ver o fluxo inteiro sobre um dataset real (treinar os modelos, calcular o score, analisar as colunas e gerar o relatório):

```bash
python experiments/run_relatorio_exemplo.py 4_breastw
```

O relatório é salvo em `results/relatorios/`.

## Datasets usados nos experimentos

| Dataset | Origem | Onde fica |
|---|---|---|
| `4_breastw` | ADBench (Han et al., 2022) | `tests/fixtures/4_breastw.npz` |
| `AcademicAnomaly` | macrOData | `tests/fixtures/AcademicAnomaly.npz` |
| FakeData | Bradshaw e Payne (2021), GitHub MSBradshaw/FakeData | baixado para a pasta temporária do sistema |
| JarFraud | Bao et al. (2020), GitHub JarFraud/FraudDetection | baixado para a pasta temporária do sistema |
| Seguro (Shu et al., 2012) | Data Colada #98, ResearchBox #336 (CC BY 4.0) | `datasets/seguro_shu2012/` (amostra de 400 linhas em `tests/fixtures/shu2012_amostra.xlsx`) |

O dataset do seguro é o único com **fabricação real e verdade conhecida por coluna**: o artigo foi retratado em 2021, e o Data Colada mostrou quais valores foram fabricados (a fonte do Excel separa os registros originais das cópias). Ele é uma validação à parte e não entra no Friedman nem na correção múltipla do estudo principal. Baixe `DrivingdataAll with font.xlsx` de https://researchbox.org/336 para `datasets/seguro_shu2012/`.

FakeData e JarFraud são baixados das fontes oficiais automaticamente, na primeira vez em que um experimento precisa deles (`experiments/ModuleCacheDados.py`). Antes de reaproveitar o arquivo da pasta temporária, o script confere o SHA-256 com o dos arquivos que geraram `results/`: se o arquivo estiver incompleto, ele é baixado de novo; se o arquivo de origem tiver mudado, o script para com erro, em vez de rodar com outro dado.

## Reproduzindo os experimentos

Os scripts gravam resultados em `results/*.jsonl` **acrescentando linhas**. Para refazer um experimento do zero, apague antes o `.jsonl` correspondente. A ordem importa, porque alguns scripts leem resultados de outros:

1. Divisão única: `run_isolation_forest_<dataset>.py` e `run_autoencoder_<dataset>.py`
2. Escolha da esparsidade do autoencoder: `run_escolha_esparsidade.py <dataset>`
3. Validação cruzada: `run_kfold_comparacao.py <dataset>` para os 4 datasets e depois `run_kfold_comparacao.py friedman` e `run_kfold_comparacao.py correcao` (Benjamini-Hochberg e Bonferroni sobre as 32 comparações) Regra de aplicabilidade por registro (opcional): `run_kfold_comparacao.py aplicabilidade <dataset>` para os 4 datasets e depois `run_kfold_comparacao.py resumo_aplicabilidade`. Validação cruzada repetida com 10 sementes (F1, PR-AUC, MCC e especificidade): `run_kfold_comparacao.py sementes <dataset>` para os 4 datasets e depois `run_kfold_comparacao.py resumo_sementes`.
4. Sensibilidade (usam os pesos do passo 3): `run_sensibilidade_pesos.py <dataset>` e `run_sensibilidade_limiar.py <dataset>`; sensibilidade do autoencoder à contaminação do treino: `run_sensibilidade_contaminacao_ae.py <dataset>` e, em seguida, as estratégias de mitigação: `run_mitigacao_contaminacao_ae.py <dataset>` (só `4_breastw` e `AcademicAnomaly`)
5. Análise por coluna: `run_analise_colunas.py descritivo`, `run_analise_colunas.py injecao` e `run_analise_colunas.py z` (estatística Z por dígito); aplicabilidade das 7 técnicas em cada base: `run_analise_colunas.py tecnicas`; checagem do artefato de precisão do FakeData: `run_artefato_precisao_fakedata.py`
6. Relatório de exemplo (usa os pesos do passo 3): `run_relatorio_exemplo.py <dataset>`
7. Validação com fabricação real (seguro): `run_seguro_shu2012.py colunas` e `run_seguro_shu2012.py registro`; consistência do arredondamento: `run_consistencia_arredondamento.py seguro`, `alarme_falso` e `poder`
8. GRIM e SPRITE com estatísticas publicadas: `run_grim_sprite_pizza.py` (lê `tests/fixtures/grim_pizza_wansink.csv`, não precisa baixar nada)
9. Laudo do lote com as 7 técnicas: `run_laudo.py seguro` e `run_laudo.py jarfraud`
10. Terminação de dígitos por registro: `run_terminacao_registro.py <dataset>` (aceita também `shu2012`) e depois `run_terminacao_registro.py resumo`
11. GRIM e SPRITE em cenário sintético: `run_grim_sprite_sintetico.py` (não precisa baixar nada)
12. Custo do critério de escolha da arquitetura do autoencoder: `run_sensibilidade_arquitetura_ae.py <dataset>` para os 4 datasets e depois `run_sensibilidade_arquitetura_ae.py resumo`
13. Referências de comparação (acaso, z-score e PyOD) nos mesmos folds: `run_baselines.py <dataset>` para os 4 datasets, `run_baselines.py pyod_ae <dataset>` (autoencoder do PyOD), `run_baselines.py temporal` (JarFraud com divisão temporal) e depois `run_baselines.py resumo`. O PyOD só é usado nesse experimento e não é dependência da biblioteca (está no `requirements.txt`, mas não no `pyproject.toml`)

Os nomes de dataset aceitos são `4_breastw`, `AcademicAnomaly`, `fakedata_real_experiment` e `jarfraud_full_experiment_clean`.

## Principais resultados até agora

- Nenhuma técnica isolada funciona bem em todos os domínios: o F1 médio da melhor técnica vai de cerca de 0,93 (breastw) a 0,03 (JarFraud).
- O score unificado ponderado teve o maior F1 médio em 2 dos 4 datasets e ficou ligeiramente abaixo da melhor técnica nos outros 2, mas **não superou a melhor técnica individual com significância estatística** em nenhum deles. No nível de registro, a hipótese central do TCC recebe apoio parcial, e não confirmação. A conclusão se manteve ao repetir a validação cruzada com 10 sementes.
- A forma de combinar importa: a média ponderada com pesos calibrados foi melhor que a média simples em todos os datasets.
- Na análise por coluna, as regras de aplicabilidade evitam que 100% das colunas sejam sinalizadas por serem de um tipo inadequado, e o critério combinado de p-valor e MAD reduz os falsos positivos para menos de 10%.
- A terminação de dígitos por registro só se aplica a dados com valores grandes (≥ 1000). Nos dados do seguro, onde o sinal da fabricação é a falta do arredondamento humano, ela separou os registros fabricados acima do acaso (PR-AUC 0,334 contra 0,250); no JarFraud, a diferença foi irrelevante, e no breastw e no FakeData ela não se aplica.

A discussão completa, com tabelas e limitações, está no texto do TCC2.

## Limitações conhecidas

- A análise foi validada em 4 datasets; em nenhum deles há rótulo de fabricação por coluna, então a análise por coluna foi validada com fabricação simulada sobre dados reais.
- No arquivo do FakeData, as amostras reais têm 2 casas decimais e as fabricadas têm precisão cheia, o que por si só separa as classes. Isolation Forest e Autoencoder não dependem disso (o F1 não muda com os fabricados arredondados), mas o artefato impede validar a análise por coluna com esses rótulos.
- Na única validação com fabricação real e verdade conhecida por coluna (dados do seguro de Shu et al., 2012), os testes de dígitos sinalizaram todas as colunas genuínas e só 5 das 12 fabricadas: os dados genuínos têm arredondamento humano, e os fabricados, gerados por computador, têm dígitos uniformes. No nível de registro, nenhuma técnica distinguiu as cópias (PR-AUC igual ao do acaso). Os testes detectam desvio de uma distribuição de referência, o que não é o mesmo que detectar fabricação. O teste de consistência do arredondamento entre colunas, criado a partir desse caso (e por isso validado em outros dados: alarme falso de 2,3%), pega esse tipo de inconsistência, mas não diz qual coluna é a fabricada e não vê nada quando todas foram fabricadas do mesmo jeito.
- Fabricações pequenas (poucos por cento dos valores de uma coluna) são difíceis de detectar pelos testes de dígitos.
- O SPRITE é uma busca heurística: "não encontrei uma amostra" é indício, e não prova. Nos artigos de pizza, ele não encontrava amostra para um desvio-padrão que uma verificação exata mostrou ser possível; a causa era um defeito no sorteio da busca, já corrigido, e agora ele encontra amostra para os 34 desvios-padrão possíveis. O GRIM arredonda empates exatos (como 2,675) para cima ou para o par, e por isso acusa uma média que os especialistas, mais conservadores, aceitaram.
- Uma coluna inteiramente inventada com valores uniformes pode ser declarada "não aplicável" para Benford e escapar da análise por coluna.
- Na divisão única, a prevalência do teste fica acima da prevalência da base (todas as anomalias vão para o teste), o que infla F1 e PR-AUC; as comparações entre técnicas usam a validação cruzada, que preserva a prevalência.
- Um z-score univariado igualou o score combinado no FakeData, e, no JarFraud com divisão temporal (treino até 2001, teste de 2003 a 2008), nenhuma técnica superou o acaso (`run_baselines.py`).
- O autoencoder padrão do PyOD superou o autoencoder da FakeNumDetect nas 4 bases e, sozinho, igualou ou superou o score combinado no AcademicAnomaly e no FakeData; a revisão do autoencoder fica como trabalho futuro.
- No FakeData, a validação de cada fold tem só 4 registros, e o limiar no percentil 95 fica instável; ali, o PR-AUC é a métrica confiável.
- A arquitetura e a esparsidade do autoencoder foram calibradas uma vez e reutilizadas em todos os folds da validação cruzada. O Isolation Forest usa os valores do artigo original (100 árvores, subamostra de até 256 registros); uma versão anterior escolhia esses valores e a `contamination` por um critério sem relação com a detecção, e a troca não mudou nenhuma conclusão.
- A arquitetura do autoencoder é escolhida pela menor perda de reconstrução na validação, critério que não mede a detecção e tende a premiar redes maiores. Com as grades usadas, o custo foi desprezível: a diferença média de PR-AUC para a melhor arquitetura da grade ficou abaixo de 0,005 nos 4 datasets (`run_sensibilidade_arquitetura_ae.py`).
- O autoencoder pode ser muito sensível a registros fabricados que entrem no treino: no AcademicAnomaly, 5% de contaminação derrubou o F1 em 36,5%. As mitigações previstas no TCC1 (mais regularização e ensemble de autoencoders por votação majoritária, disponível em `treinar_ensemble_autoencoders`) foram testadas e não resolveram o problema: nenhuma superou o autoencoder original com o treino contaminado. O aparamento iterativo do treino (`treinar_autoencoder_aparado`, fora do TCC1) foi o melhor com 5% de contaminação (F1 0,453 contra 0,392), mas também ficou longe do critério, porque remove cerca de 9% do treino mesmo quando ele está limpo e custa desempenho no caso sem contaminação (F1 0,617 para 0,491).

## Autora

Giovanna Paiva Alves. Desenvolvido como Trabalho de Conclusão de Curso no SENAC.

## Licença

MIT. Veja o arquivo `LICENSE`.
