Trabalho de Conclusão de Curso II — Bacharelado em Ciência da Computação SENAC Santo Amaro · São Paulo · 2026
Autor: Phelipe Pereira de Souza Orientador: Prof. Thyago Conchado Quintas
Entrega parcial — 15/09/2026. Este documento é atualizado conforme o trabalho avança.
Assistentes de voz convencionais exigem uma wake-word ("Alexa", "Ok Google") para saber que estão sendo chamados. Este trabalho investiga a ativação por intenção implícita: decidir, a partir apenas do enunciado, se a pessoa está falando com o computador ou perto dele.
A tarefa é uma classificação binária entre duas classes que compartilham o mesmo vocabulário — é justamente essa sobreposição que a torna difícil:
| Classe | Definição | Exemplo |
|---|---|---|
| DD (device-directed) | Comando dirigido ao computador | "fecha essa aba por favor" |
| NDD (non-device-directed) | Conversa de fundo, mesmo vocabulário | "eu fechei sem querer a aba que tinha a pesquisa toda" |
O cronograma do TCC1 prevê 6 fases ao longo de 16 semanas. O TCC2 teve início no começo de agosto de 2026, o que situa esta entrega por volta da semana 7 de 16.
| Fase | Semanas previstas | Situação |
|---|---|---|
| F1 — Corpus | 1–5 | ✅ Concluída — corpus de 225.045 enunciados gerado e validado |
| F2 — Implementação | 4–9 | 🔶 Parcial (ver detalhe abaixo) |
| ├ Módulo ASR (Whisper) | 4–6 | ⬜ Não iniciado — em atraso |
| ├ Baselines clássicos (TF-IDF + SVM/LR) | 5–7 | ⬜ Não iniciado — vence esta semana |
| └ Fine-tuning BERTimbau | 6–9 | 🔵 Em andamento — treinando |
| F3 — Experimentos | 8–13 | 🔵 Antecipada parcialmente |
| └ Modelos alternativos (Albertina, DeBERTinha) | 11–13 | 🔵 Adiantado ~5 semanas — implementados e em treino |
| F4 — Análise | 12–14 | ⬜ Não iniciado |
| F5 — Escrita | 1–15 | 🔵 Contínua |
| F6 — Entrega | 15–16 | ⬜ Não iniciado |
Leitura honesta do quadro: a construção do corpus foi concluída no prazo e a parte de Transformers está adiantada — os três modelos (BERTimbau, Albertina e DeBERTinha) já estão implementados e em treinamento, sendo que os dois alternativos estavam previstos apenas para as semanas 11–13 e eram condicionados a "caso o cronograma permita". Em contrapartida, duas atividades da F2 não foram iniciadas: o módulo ASR com Whisper (previsto para as semanas 4–6) e os baselines clássicos (5–7). São as duas prioridades imediatas, detalhadas na Seção 6.
Não existe corpus público de português brasileiro anotado em DD/NDD para o domínio de computador pessoal. O corpus foi construído sinteticamente, com 9 fontes geradoras independentes, para evitar que o classificador aprenda o estilo de um único modelo em vez da distinção semântica.
APIs gratuitas (4):
| Fonte | Modelo |
|---|---|
| Groq | qwen/qwen3.8-27b |
| Mistral AI | mistral-small-latest |
| NVIDIA NIM | moonshotai/kimi-k3 |
| OpenRouter | openrouter/free |
Modelos locais (3), servidos via LM Studio em máquinas próprias — um em localhost
e dois em servidores na rede local:
| Fonte | Modelo |
|---|---|
| LM Studio (local) | google/gemma-4-e4b |
| LM Studio (rede) | qwen/qwen3.6-35b-a3b |
| LM Studio (rede) | zai-org/glm-4.6v-flash |
Agentes de código (2), em sessões interativas de CLI em vez de geração em lote:
| Fonte | Ferramenta |
|---|---|
opencode |
CLI OpenCode |
sonnet |
Claude Sonnet |
O uso de APIs gratuitas e de inferência local foi uma decisão de projeto: manteve o custo de geração em zero e permitiu gerar um volume muito acima do mínimo previsto na metodologia (40.000 amostras), sem depender de crédito pago.
Cada gerador usa três estratégias de prompt sobre uma taxonomia compartilhada
(src/dataset/prompts/context_schema.yaml), com 7 categorias de comando
(controle de aplicativos, captura de tela, mídia, sistema, navegador, produtividade
e arquivos, ditado), 8 contextos de uso e 6 tons:
- Paráfrase — variações de comandos-semente dentro de uma categoria
- Roleplay — geração sob 6 personas (idoso formal, jovem/gamer, profissional apressado, criança, pessoa sussurrando, pessoa irritada), para cobrir registro e variação sociolinguística
- Contraste NDD — conversa fiada usando o mesmo vocabulário da classe DD, que é o que torna o par difícil de separar
Todos os geradores aplicam, ainda na geração, dois filtros de escopo: remoção de wake-words (Alexa, Siri, Ok Google) e de comandos de casa inteligente, mantendo o domínio estritamente em computador pessoal.
Snapshot de 10/09/2026, que é o corpus efetivamente usado no treinamento em curso:
| Fonte | DD | NDD | Total |
|---|---|---|---|
| qwen | 40.009 | 20.900 | 60.909 |
| lmstudio | 15.004 | 15.003 | 30.007 |
| sonnet | 15.000 | 15.000 | 30.000 |
| glm | 15.033 | 7.646 | 22.679 |
| groq | 15.009 | 5.517 | 20.526 |
| openrouter | 10.537 | 9.133 | 19.670 |
| nvidia | 10.002 | 9.057 | 19.059 |
| opencode | 6.067 | 6.152 | 12.219 |
| mistral | 4.974 | 5.002 | 9.976 |
| Total | 131.635 | 93.410 | 225.045 |
A geração seguiu após esse snapshot: as pastas de origem somam hoje 261.917 enunciados. O aumento será incorporado em uma reexecução do pipeline após a conclusão do treinamento atual.
Pipeline de 7 estágios, em project/src/data_prep/. Cada estágio é um módulo
executável isolado, lê a saída do anterior e grava a sua, de modo que qualquer etapa
pode ser reprocessada sem refazer as anteriores. As pastas de origem são tratadas como
somente leitura.
uv run python -m src.data_prep.run_pipeline| # | Estágio | Função |
|---|---|---|
| 1 | Snapshot | Copia as 9 fontes e grava manifesto com contagem, mtime e SHA-256 de cada arquivo |
| 2 | Normalização | Padroniza o texto e aplica filtros de integridade |
| 3 | Dedup por arquivo | Remove repetições dentro de cada arquivo |
| 4 | Merge | Consolida as 9 fontes em 2 arquivos, um por classe |
| 5 | Dedup global | Dedup entre fontes + remoção de conflitos de rótulo |
| 6 | Balanceamento | Undersample proporcional até 1:1 |
| 7 | Split | Partição 80/10/10 estratificada por classe e fonte |
Ordem fixa: NFKD → remoção de marcas de acento → minúsculas → remoção de toda pontuação e símbolo → colapso de espaços.
"Fecha essa aba, por favor!" → "fecha essa aba por favor"
"não sei que horas são" → "nao sei que horas sao"
"planilha do 3º trimestre" → "planilha do 3o trimestre"
A normalização é agressiva por decisão de projeto: o sistema final recebe a saída de um
ASR, que não produz pontuação nem caixa confiáveis. Normalizar treino, validação e teste
da mesma forma mantém a coerência e aproxima o texto da entrada real. O texto original é
preservado em um campo paralelo (text_raw), o que permite a ablação "com acento vs sem
acento" sem reprocessar nada.
| Estágio | DD | NDD | Observação |
|---|---|---|---|
| 1. Snapshot | 131.635 | 93.410 | corpus bruto |
| 2. Normalização | 131.581 | 93.382 | −82 (39 por tamanho, 43 por script estrangeiro) |
| 3. Dedup por arquivo | 123.814 | 93.243 | −7.767 DD, −139 NDD |
| 5. Dedup global + conflitos | 118.056 | 93.216 | −5.758 DD, −27 NDD; 23 conflitos de rótulo |
| 6. Balanceamento 1:1 | 93.216 | 93.216 | −24.840 da classe majoritária |
| 7. Split | 149.145 / 18.643 / 18.644 | treino / validação / teste |
Dataset final: 186.432 enunciados, perfeitamente balanceado — 4,7× o volume mínimo de 40.000 previsto na metodologia.
Três resultados do pipeline que valem discussão no texto do TCC:
-
A duplicação se concentra quase toda na classe DD. DD perde 13.525 registros entre os estágios 2 e 5; NDD perde apenas 166. Comandos imperativos gerados por LLM têm variedade lexical muito menor que conversa fiada. O desbalanceamento de 1,27:1 antes do balanceamento é consequência disso, e não uma escolha de amostragem.
-
23 enunciados receberam rótulos contraditórios entre fontes — por exemplo
"fecha essa aba por favor", gerado como DD por seis fontes e como NDD por uma. São casos genuinamente ambíguos, correspondentes à classe AMB da taxonomia. Foram removidos de ambas as classes e registrados emconflicts.jsonl, servindo como material qualitativo sobre a fronteira DD/NDD. -
43 enunciados continham script estrangeiro vazado pelos geradores — chinês, cirílico, árabe e coreano dentro de frases em português, como
"vou abrir meu e mail depois do almoco pra ver那些 noticias". São defeitos de geração e foram descartados.
O pipeline valida ao final, por asserção automática:
- nenhum texto repetido dentro de uma mesma classe;
- nenhum texto presente nas duas classes;
- treino, validação e teste disjuntos (zero interseção) — o dedup global roda antes do split, o que é o que impede vazamento e inflação artificial das métricas;
- distribuição de fontes idêntica entre as três partições, dentro de 0,01 ponto percentual.
Implementado em project/src/training/, com núcleo compartilhado e um script fino por
modelo (~30 linhas cada), de modo que os três recebem tratamento idêntico.
uv run python -m src.training.train_all| Modelo | Checkpoint | Parâmetros | Épocas |
|---|---|---|---|
| BERTimbau Base | neuralmind/bert-base-portuguese-cased |
108,9 M | 4 |
| Albertina PT-BR | PORTULAN/albertina-100m-portuguese-ptbr-encoder |
139,2 M | 4 |
| DeBERTinha | sagui-nlp/debertinha-ptbr-xsmall |
40,9 M | 5 |
Os três com lote efetivo 32, max_length 128, taxa de aprendizado 2e-5, warmup de
10%, dropout 0,1 e a mesma semente — conforme a tabela de hiperparâmetros da
metodologia. A configuração idêntica é proposital: garante que a diferença observada
entre os modelos seja atribuível ao modelo, e não ao regime de treino.
Hardware: NVIDIA RTX A2000 12 GB · Intel i7 14ª geração · 32 GB RAM · Windows 11.
O motor de treino segue uma ordem rígida:
hardware → semente → dados → modelo → treino
→ avalia VALIDAÇÃO → escolhe o limiar de decisão na VALIDAÇÃO
→ avalia TESTE uma única vez, com o limiar congelado
→ grava artefatos
O conjunto de teste é acessado exatamente uma vez, ao final, e o limiar de decisão é escolhido apenas na validação. É o que sustenta a afirmação metodológica de que o teste permaneceu reservado durante todo o desenvolvimento.
Acurácia, acurácia balanceada, precisão, recall e F1 (binário em DD e macro), ROC-AUC, PR-AUC, MCC e EER (equal error rate). O critério de aceite da metodologia é EER < 30%.
DD é a classe positiva, de modo que um falso positivo corresponde ao assistente acordar
durante conversa de fundo — o erro crítico do sistema. O EER é obtido por interpolação
da curva ROC, e a implementação foi validada contra o caso analítico de duas
distribuições normais com d' = 2, cujo EER teórico é Φ(−1) = 0,1587 (obtido:
0,1590).
Cada execução grava métricas, matriz de confusão, curvas ROC e DET, predições por exemplo, métricas desagregadas por fonte geradora e um manifesto com configuração, hardware, versões e SHA do commit.
🔵 Treinamento em execução na máquina de GPU. Os resultados serão incorporados a este documento e ao Capítulo de Resultados assim que a bateria concluir.
Em ordem de prioridade:
- Baselines clássicos (F2, semanas 5–7) — TF-IDF com n-gramas 1–3 + SVM linear e Regressão Logística. É a comparação que a hipótese do trabalho exige: o BERTimbau precisa superar o melhor baseline clássico. Depende apenas de scikit-learn, já instalado.
- Módulo ASR com Whisper (F2, semanas 4–6) — atualmente a atividade mais atrasada. Necessária para os experimentos com transcrição 1-best e n-best.
- Concluir o treinamento dos três modelos e consolidar a tabela comparativa.
- Ablação de normalização — treinar com
text_raw(acento e pontuação preservados) e comparar. Remover acentos afasta o texto do que os tokenizadores cased viram no pré-treino e desfaz pares mínimos do português (está/esta,é/e); por outro lado, aproxima da saída real de um ASR. Os dois números juntos são um resultado, e não uma suposição escondida. - Análise por fonte geradora — variação grande entre fontes indicaria que o modelo aprendeu o estilo de um gerador em vez da distinção DD/NDD.
O corpus é integralmente sintético. A hipótese do trabalho trata de transcrições de fala humana, e um classificador pode atingir desempenho alto neste corpus e falhar em fala real. A mitigação prevista é avaliar os modelos sobre corpora de fala espontânea em português (CORAA, NURC-SP, TAGARELA) como conjunto de teste externo da classe NDD, nunca em treino. Essa avaliação ainda não foi executada.
cd project
uv sync # instala dependências
uv run python -m src.data_prep.run_pipeline # gera o dataset (~6 s)
uv run python -m src.training.train_all # treina os 3 modelosO torch é resolvido por plataforma: build com CUDA no Windows e Linux, build de CPU no
macOS, sem necessidade de flags. Detalhes de uso, opções de linha de comando e decisões
de implementação estão em project/README.md.
tcc/
├── README.md # este documento
├── paper/ # monografia em LaTeX (abnTeX2)
│ ├── main.tex
│ └── bibliografia.bib
└── project/
├── README.md # documentação técnica detalhada
├── pyproject.toml # dependências (uv)
├── data/ # dataset processado (gerado; não versionado)
└── src/
├── dataset/ # Etapa 1 — geração com LLMs
│ ├── *_generate.py # um script por fonte
│ ├── prompts/ # taxonomia de categorias, contextos e tons
│ └── <fonte>/ # corpora gerados (dd.jsonl, ndd.jsonl)
├── data_prep/ # Etapa 2 — pipeline de preparação (7 estágios)
└── training/ # Etapa 3 — fine-tuning dos 3 modelos
O código está integralmente documentado: 100% dos módulos, classes, funções e métodos possuem docstring.