# IA Gôndola Engine Serviço isolado de detecção YOLO e treinamento. Consumido exclusivamente pela `ia-gondola-api` via `IA_MOTOR_URL`. ## Arquitetura ``` ia-gondola-api → POST /detectar → YOLO → [{box, conf, class}] → POST /treinar → triagem + treino → modelo atualizado no S3 ``` ## Estrutura ``` main.py — todo o código (sem sub-módulos) Dockerfile requirements.txt ``` ## Endpoints ### `POST /detectar` Form-data: `ambiente` (str), `file` (imagem). Confidence threshold: 0.25, NMS `iou=0.5`. `ia-gondola-api` também manda um campo `conf` no form (ver `gondola_service.py`), mas esse endpoint não declara/lê esse campo — é ignorado, o threshold usado é sempre o 0.25 hardcoded aqui. Depois do NMS do próprio YOLO, passa por `_suprimir_boxes_contidas()`: remove boxes da mesma classe quase totalmente contidas em outra (caixa grande "engolindo" uma caixa menor do mesmo produto — comum em prateleira lotada). IoU sozinho não resolve isso: quando uma caixa é muito maior que a outra, a união fica dominada pela caixa grande e o IoU continua baixo mesmo com contenção quase total (ex.: caixa pequena 1/5 da área da grande, 100% contida → IoU ≈ 0.2, seguiria sobrevivendo mesmo com iou threshold bem mais agressivo que o do NMS padrão). A métrica usada aqui é intersecção / área da MENOR caixa do par, limiar 0.90 — entre duas boxes da mesma classe acima desse limiar, fica só a de maior confiança. ```json { "status": "sucesso", "deteccoes": [{ "box": [x1,y1,x2,y2], "conf": 0.87, "class": 0 }] } ``` ### `POST /treinar` Body JSON: `{ "ambiente": "gondola", "pular_triagem": false }` 1. Lista `treinamento/{ambiente}/novos-treinamentos/` no S3 2. Triagem (salvo `pular_triagem: true`): conf média ≥ 0.15 → `base-oficial`, < 0.15 → `descartados` 3. Baixa `base-oficial`, redimensiona imagens > 4096px 4. Treina: 20 épocas fine-tuning / 30 épocas do zero, imgsz=640, batch=8, device=cpu 5. Salva `best.pt` em `modelos/{ambiente}/atual/cerebro.pt` + histórico versionado Retorna `{ "status": "iniciado", "ambiente": "..." }` imediatamente — treino roda em background thread. Quando não há modelo base, usa `yolov8n.pt` e pula triagem automaticamente. ### `GET /treinar/status` Retorna estado do treino em andamento + últimas 30 linhas de log. ```json { "status": "running|concluido|erro|vazio|idle", "ambiente": "gondola", "versao": "20260627_1430", "detalhe": null, "logs": ["[1/20] box=0.432 ..."] } ``` `logs` inclui tanto as métricas por época (`_on_fit_epoch_end` callback do YOLO) quanto mensagens de progresso de triagem/download via `log_print()`. ## Organização S3 ``` modelos/{ambiente}/atual/cerebro.pt modelos/{ambiente}/versionamento/cerebro_YYYYMMDD_HHMM.pt treinamento/{ambiente}/novos-treinamentos/ treinamento/{ambiente}/base-oficial/ treinamento/{ambiente}/descartados/ ``` ## Convenções - `MODELOS_CARREGADOS` = dict em memória `{ambiente: YOLO}` — cache local em `/tmp/cerebro_{ambiente}.pt` - Se S3 tiver versão mais nova (LastModified), descarta cache e baixa novamente - Após treino, remove o ambiente de `MODELOS_CARREGADOS` para forçar reload - Todo o código em `main.py` — não criar sub-módulos - `log_print(msg)` escreve no stdout **e** em `_training_logs` (buffer circular 60 linhas) — não usar `print()` diretamente ## Como rodar ```bash pip install -r requirements.txt uvicorn main:app --reload --port 8001 ```