Upload 13 files
Browse files- README +306 -0
- SETUP_GUIDE.md +0 -0
- analysis_tool.py +388 -0
- client.py +197 -0
- client_1_fedadagrad.log +172 -0
- client_2_fedadagrad.log +172 -0
- client_3_fedadagrad.log +172 -0
- requeriments.txt +33 -0
- run.sh +227 -0
- run_all_strategies.sh +213 -0
- server.py +449 -0
- server_fedadagrad.log +213 -0
- utils.py +139 -0
README
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Federated Learning para Previsão de Consumo de Combustível
|
| 2 |
+
|
| 3 |
+
Sistema de Aprendizado Federado (FL) para prever consumo de combustível usando dados de sensores OBD de diferentes veículos, mantendo a privacidade dos dados em cada cliente.
|
| 4 |
+
|
| 5 |
+
## 📋 Visão Geral
|
| 6 |
+
|
| 7 |
+
Este projeto implementa um sistema de Aprendizado Federado usando o framework Flower, onde:
|
| 8 |
+
- **3 clientes** (Ubuntu) representam diferentes veículos com seus dados locais
|
| 9 |
+
- **1 servidor** (Windows) coordena o treinamento sem acessar os dados brutos
|
| 10 |
+
- Modelo LSTM para previsão de séries temporais de consumo (P_kW)
|
| 11 |
+
- Múltiplas estratégias de agregação: FedAvg, FedAdam, FedYogi, FedAdagrad
|
| 12 |
+
|
| 13 |
+
## 🏗️ Arquitetura do Sistema
|
| 14 |
+
|
| 15 |
+
```
|
| 16 |
+
┌─────────────────┐
|
| 17 |
+
│ Servidor (Win) │
|
| 18 |
+
│ 16GB RAM │
|
| 19 |
+
│ Porta: 8080 │
|
| 20 |
+
└────────┬────────┘
|
| 21 |
+
│
|
| 22 |
+
┌────┴────┬──────────┐
|
| 23 |
+
│ │ │
|
| 24 |
+
┌───▼───┐ ┌──▼───┐ ┌────▼───┐
|
| 25 |
+
│Cliente│ │Cliente│ │Cliente │
|
| 26 |
+
│ 1 │ │ 2 │ │ 3 │
|
| 27 |
+
│Ubuntu │ │Ubuntu│ │Ubuntu │
|
| 28 |
+
│ 8GB │ │ 8GB │ │ 8GB │
|
| 29 |
+
└───────┘ └──────┘ └────────┘
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
## 📁 Estrutura do Projeto
|
| 33 |
+
|
| 34 |
+
```
|
| 35 |
+
fl_project/
|
| 36 |
+
├── data/ # Dados dos veículos (não versionado)
|
| 37 |
+
│ ├── client_1/ # Percursos do veículo 1
|
| 38 |
+
│ │ ├── percurso_1.csv
|
| 39 |
+
│ │ ├── percurso_2.csv
|
| 40 |
+
│ │ └── ...
|
| 41 |
+
│ ├── client_2/ # Percursos do veículo 2
|
| 42 |
+
│ └── client_3/ # Percursos do veículo 3
|
| 43 |
+
├── server.py # Código do servidor FL
|
| 44 |
+
├── client.py # Código dos clientes FL
|
| 45 |
+
├── utils.py # Modelo LSTM e funções auxiliares
|
| 46 |
+
├── analysis_tool.py # Ferramenta de análise pós-treinamento
|
| 47 |
+
├── run.sh # Script para execução local
|
| 48 |
+
├── run_all_strategies.sh # Script para testar todas as estratégias
|
| 49 |
+
├── requirements.txt # Dependências Python
|
| 50 |
+
└── README.md # Este arquivo
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
## 🔧 Requisitos do Sistema
|
| 54 |
+
|
| 55 |
+
### Hardware Mínimo
|
| 56 |
+
- **Servidor**: 8GB RAM (recomendado 16GB)
|
| 57 |
+
- **Clientes**: 4GB RAM cada (recomendado 8GB)
|
| 58 |
+
- **Rede**: Conexão estável entre servidor e clientes
|
| 59 |
+
|
| 60 |
+
### Software
|
| 61 |
+
- **Python**: 3.10 - 3.11
|
| 62 |
+
- **Sistema Operacional**:
|
| 63 |
+
- Servidor: Windows 10/11 ou Linux
|
| 64 |
+
- Clientes: Ubuntu 20.04/22.04
|
| 65 |
+
|
| 66 |
+
## 📦 Instalação
|
| 67 |
+
|
| 68 |
+
### 1. Clone o Repositório
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
git clone https://github.com/seu-usuario/fl_project.git
|
| 72 |
+
cd fl_project
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
### 2. Crie um Ambiente Virtual
|
| 76 |
+
|
| 77 |
+
**No Ubuntu (Clientes):**
|
| 78 |
+
```bash
|
| 79 |
+
python3 -m venv venv
|
| 80 |
+
source venv/bin/activate
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
**No Windows (Servidor):**
|
| 84 |
+
```powershell
|
| 85 |
+
python -m venv venv
|
| 86 |
+
.\venv\Scripts\activate
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
### 3. Instale as Dependências
|
| 90 |
+
|
| 91 |
+
```bash
|
| 92 |
+
pip install -r requirements.txt
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
### 4. Prepare os Dados
|
| 96 |
+
|
| 97 |
+
Organize os dados de cada veículo na estrutura:
|
| 98 |
+
```
|
| 99 |
+
data/
|
| 100 |
+
├── client_1/ # Dados do veículo 1
|
| 101 |
+
├── client_2/ # Dados do veículo 2
|
| 102 |
+
└── client_3/ # Dados do veículo 3
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
**Formato esperado dos CSVs:**
|
| 106 |
+
- Colunas principais: `vehicle_speed`, `engine_rpm`, `accel_x`, `accel_y`, `P_kW`, `dt`
|
| 107 |
+
- Cada arquivo representa um percurso diferente
|
| 108 |
+
- Mínimo de 2 percursos por cliente recomendado
|
| 109 |
+
|
| 110 |
+
## 🚀 Execução em Ambiente Distribuído
|
| 111 |
+
|
| 112 |
+
### Configuração de Rede
|
| 113 |
+
|
| 114 |
+
1. **Identifique o IP do servidor Windows:**
|
| 115 |
+
```powershell
|
| 116 |
+
ipconfig
|
| 117 |
+
```
|
| 118 |
+
Procure pelo IPv4 Address (ex: 192.168.1.100)
|
| 119 |
+
|
| 120 |
+
2. **Teste a conectividade dos clientes Ubuntu:**
|
| 121 |
+
```bash
|
| 122 |
+
ping 192.168.1.100
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
### Passo 1: Iniciar o Servidor (Windows)
|
| 126 |
+
|
| 127 |
+
```powershell
|
| 128 |
+
# Ative o ambiente virtual
|
| 129 |
+
.\venv\Scripts\activate
|
| 130 |
+
|
| 131 |
+
# Execute o servidor
|
| 132 |
+
python server.py --strategy fedavg --rounds 15
|
| 133 |
+
|
| 134 |
+
# Ou com parâmetros customizados
|
| 135 |
+
python server.py --strategy fedadam --rounds 20 --min-clients 3
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
O servidor iniciará na porta 8080 e aguardará a conexão dos clientes.
|
| 139 |
+
|
| 140 |
+
### Passo 2: Iniciar os Clientes (Ubuntu)
|
| 141 |
+
|
| 142 |
+
**Em cada máquina Ubuntu, execute em terminais separados:**
|
| 143 |
+
|
| 144 |
+
**Cliente 1:**
|
| 145 |
+
```bash
|
| 146 |
+
# Ative o ambiente virtual
|
| 147 |
+
source venv/bin/activate
|
| 148 |
+
|
| 149 |
+
# Execute o cliente 1
|
| 150 |
+
python client.py --client-id 1 --server-address 192.168.1.100:8080 --prediction-length 10
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
**Cliente 2:**
|
| 154 |
+
```bash
|
| 155 |
+
source venv/bin/activate
|
| 156 |
+
python client.py --client-id 2 --server-address 192.168.1.100:8080 --prediction-length 10
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
**Cliente 3:**
|
| 160 |
+
```bash
|
| 161 |
+
source venv/bin/activate
|
| 162 |
+
python client.py --client-id 3 --server-address 192.168.1.100:8080 --prediction-length 10
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
### Monitoramento
|
| 166 |
+
|
| 167 |
+
O progresso será exibido em tempo real:
|
| 168 |
+
- **Servidor**: Mostra rodadas completas e métricas globais
|
| 169 |
+
- **Clientes**: Exibem perdas locais de treino/validação
|
| 170 |
+
|
| 171 |
+
## 📊 Análise dos Resultados
|
| 172 |
+
|
| 173 |
+
### Após o Treinamento
|
| 174 |
+
|
| 175 |
+
1. **Executar análise automática:**
|
| 176 |
+
```bash
|
| 177 |
+
python analysis_tool.py --results-dir results
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
2. **Visualizações geradas (PDFs):**
|
| 181 |
+
- `performance_analysis_*.pdf`: Análise de desempenho completa
|
| 182 |
+
- `convergence_analysis_*.pdf`: Métricas de convergência
|
| 183 |
+
- `heatmap_performance_*.pdf`: Mapa de calor temporal
|
| 184 |
+
- `comparative_analysis.pdf`: Comparação entre estratégias
|
| 185 |
+
- `client_evolution_analysis.pdf`: Evolução individual
|
| 186 |
+
|
| 187 |
+
3. **Métricas salvas:**
|
| 188 |
+
- `results/detailed_metrics_*.csv`: Dados completos
|
| 189 |
+
- `results/summary_report.json`: Relatório consolidado
|
| 190 |
+
- `metrics/client_*/metrics_history.json`: Histórico por cliente
|
| 191 |
+
|
| 192 |
+
## 🔬 Estratégias de Agregação
|
| 193 |
+
|
| 194 |
+
| Estratégia | Descrição | Quando Usar |
|
| 195 |
+
|------------|-----------|-------------|
|
| 196 |
+
| **FedAvg** | Média ponderada simples | Dados homogêneos |
|
| 197 |
+
| **FedAdam** | Otimização adaptativa | Convergência mais rápida |
|
| 198 |
+
| **FedYogi** | Adam com controle de variância | Dados heterogêneos |
|
| 199 |
+
| **FedAdagrad** | Taxa de aprendizado adaptativa | Dados esparsos |
|
| 200 |
+
|
| 201 |
+
### Comparar Todas as Estratégias
|
| 202 |
+
|
| 203 |
+
```bash
|
| 204 |
+
# Linux/Ubuntu
|
| 205 |
+
chmod +x run_all_strategies.sh
|
| 206 |
+
./run_all_strategies.sh 15 10
|
| 207 |
+
|
| 208 |
+
# Windows (usando Git Bash ou WSL)
|
| 209 |
+
bash run_all_strategies.sh 15 10
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
## 🛠️ Troubleshooting
|
| 213 |
+
|
| 214 |
+
### Erro de Conexão
|
| 215 |
+
|
| 216 |
+
**Problema**: Clientes não conseguem conectar ao servidor
|
| 217 |
+
|
| 218 |
+
**Soluções**:
|
| 219 |
+
1. Verifique o firewall do Windows:
|
| 220 |
+
```powershell
|
| 221 |
+
# Permitir porta 8080
|
| 222 |
+
netsh advfirewall firewall add rule name="FL Server" dir=in action=allow protocol=TCP localport=8080
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
2. Confirme que o servidor está rodando:
|
| 226 |
+
```powershell
|
| 227 |
+
netstat -an | findstr :8080
|
| 228 |
+
```
|
| 229 |
+
|
| 230 |
+
### Erro de Memória
|
| 231 |
+
|
| 232 |
+
**Problema**: Out of Memory durante treinamento
|
| 233 |
+
|
| 234 |
+
**Soluções**:
|
| 235 |
+
1. Reduza o batch_size em `utils.py`
|
| 236 |
+
2. Diminua sequence_length ou prediction_length
|
| 237 |
+
3. Use menos épocas por rodada
|
| 238 |
+
|
| 239 |
+
### Dados Insuficientes
|
| 240 |
+
|
| 241 |
+
**Problema**: "conjunto de treino ou teste vazio"
|
| 242 |
+
|
| 243 |
+
**Soluções**:
|
| 244 |
+
1. Verifique se há dados suficientes em `data/client_X/`
|
| 245 |
+
2. Ajuste sequence_length e prediction_length
|
| 246 |
+
3. Confirme que os CSVs têm as colunas esperadas
|
| 247 |
+
|
| 248 |
+
## 📈 Parâmetros Importantes
|
| 249 |
+
|
| 250 |
+
### Server.py
|
| 251 |
+
- `--strategy`: Estratégia de agregação (fedavg, fedadam, etc.)
|
| 252 |
+
- `--rounds`: Número de rodadas de FL (default: 10)
|
| 253 |
+
- `--min-clients`: Clientes mínimos para iniciar (default: 3)
|
| 254 |
+
|
| 255 |
+
### Client.py
|
| 256 |
+
- `--client-id`: ID do cliente (1, 2 ou 3)
|
| 257 |
+
- `--server-address`: Endereço IP:porta do servidor
|
| 258 |
+
- `--prediction-length`: Passos futuros a prever (default: 10)
|
| 259 |
+
|
| 260 |
+
### Utils.py (configurações internas)
|
| 261 |
+
- `sequence_length`: Janela de entrada (default: 60)
|
| 262 |
+
- `batch_size`: Tamanho do batch (default: 32)
|
| 263 |
+
- `learning_rate`: Taxa de aprendizado (default: 1e-5)
|
| 264 |
+
|
| 265 |
+
## 📝 Notas de Desenvolvimento
|
| 266 |
+
|
| 267 |
+
### Modelo LSTM
|
| 268 |
+
- Entrada: 6 features (velocidade, RPM, acelerações, consumo, tempo)
|
| 269 |
+
- Hidden size: 50 neurônios
|
| 270 |
+
- Saída: Previsão de N passos futuros de consumo (P_kW)
|
| 271 |
+
|
| 272 |
+
### Divisão dos Dados
|
| 273 |
+
- 80% para treinamento
|
| 274 |
+
- 20% para validação
|
| 275 |
+
- Normalização MinMaxScaler por cliente
|
| 276 |
+
|
| 277 |
+
### Métricas
|
| 278 |
+
- Loss: MSE (Mean Squared Error)
|
| 279 |
+
- Avaliação: Por cliente e global
|
| 280 |
+
- Convergência: Variância entre clientes
|
| 281 |
+
|
| 282 |
+
## 🤝 Contribuindo
|
| 283 |
+
|
| 284 |
+
1. Fork o projeto
|
| 285 |
+
2. Crie sua feature branch (`git checkout -b feature/AmazingFeature`)
|
| 286 |
+
3. Commit suas mudanças (`git commit -m 'Add some AmazingFeature'`)
|
| 287 |
+
4. Push para a branch (`git push origin feature/AmazingFeature`)
|
| 288 |
+
5. Abra um Pull Request
|
| 289 |
+
|
| 290 |
+
## 📄 Licença
|
| 291 |
+
|
| 292 |
+
Distribuído sob a licença MIT. Veja `LICENSE` para mais informações.
|
| 293 |
+
|
| 294 |
+
## 👥 Autores
|
| 295 |
+
|
| 296 |
+
- José Wilson C. Souza
|
| 297 |
+
- Erick Andrade Borba
|
| 298 |
+
- João Alfredo Cal Braz
|
| 299 |
+
|
| 300 |
+
## 🙏 Agradecimentos
|
| 301 |
+
|
| 302 |
+
- [Flower Framework](https://flower.dev/) - Framework de Aprendizado Federado
|
| 303 |
+
- [PyTorch](https://pytorch.org/) - Framework de Deep Learning
|
| 304 |
+
- Dados coletados via OBD Link
|
| 305 |
+
|
| 306 |
+
---
|
SETUP_GUIDE.md
ADDED
|
File without changes
|
analysis_tool.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Ferramenta de análise pós-treinamento para comparar resultados de diferentes estratégias
|
| 4 |
+
e gerar relatórios consolidados.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import json
|
| 8 |
+
import pandas as pd
|
| 9 |
+
import numpy as np
|
| 10 |
+
import matplotlib.pyplot as plt
|
| 11 |
+
import seaborn as sns
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
import argparse
|
| 14 |
+
from typing import Dict, List
|
| 15 |
+
|
| 16 |
+
plt.style.use('seaborn-v0_8-darkgrid')
|
| 17 |
+
sns.set_palette("husl")
|
| 18 |
+
|
| 19 |
+
class FLAnalyzer:
|
| 20 |
+
"""Analisador de resultados de Aprendizado Federado."""
|
| 21 |
+
|
| 22 |
+
def __init__(self, results_dir: str = "results"):
|
| 23 |
+
self.results_dir = Path(results_dir)
|
| 24 |
+
self.metrics_dir = Path("metrics")
|
| 25 |
+
self.strategies_data = {}
|
| 26 |
+
self.client_data = {}
|
| 27 |
+
|
| 28 |
+
def load_server_metrics(self):
|
| 29 |
+
"""Carrega métricas do servidor para todas as estratégias."""
|
| 30 |
+
print("Carregando métricas do servidor...")
|
| 31 |
+
|
| 32 |
+
for csv_file in self.results_dir.glob("detailed_metrics_*.csv"):
|
| 33 |
+
strategy = csv_file.stem.replace("detailed_metrics_", "")
|
| 34 |
+
df = pd.read_csv(csv_file)
|
| 35 |
+
self.strategies_data[strategy] = df
|
| 36 |
+
print(f" - Carregado: {strategy}")
|
| 37 |
+
|
| 38 |
+
def load_client_metrics(self):
|
| 39 |
+
"""Carrega métricas individuais dos clientes."""
|
| 40 |
+
print("Carregando métricas dos clientes...")
|
| 41 |
+
|
| 42 |
+
for client_dir in self.metrics_dir.glob("client_*"):
|
| 43 |
+
client_id = int(client_dir.name.split("_")[1])
|
| 44 |
+
metrics_file = client_dir / "metrics_history.json"
|
| 45 |
+
|
| 46 |
+
if metrics_file.exists():
|
| 47 |
+
with open(metrics_file, 'r') as f:
|
| 48 |
+
self.client_data[client_id] = json.load(f)
|
| 49 |
+
print(f" - Cliente {client_id} carregado")
|
| 50 |
+
|
| 51 |
+
def generate_comparative_analysis(self):
|
| 52 |
+
"""Gera análise comparativa entre estratégias."""
|
| 53 |
+
if not self.strategies_data:
|
| 54 |
+
print("Nenhum dado de estratégia encontrado!")
|
| 55 |
+
return
|
| 56 |
+
|
| 57 |
+
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
|
| 58 |
+
|
| 59 |
+
# 1. Comparação de convergência entre estratégias
|
| 60 |
+
ax1 = axes[0, 0]
|
| 61 |
+
for strategy, df in self.strategies_data.items():
|
| 62 |
+
eval_df = df[df['phase'] == 'eval']
|
| 63 |
+
if not eval_df.empty and 'global_eval_loss' in eval_df.columns:
|
| 64 |
+
ax1.plot(eval_df['rounds'], eval_df['global_eval_loss'],
|
| 65 |
+
marker='o', label=strategy.upper(), linewidth=2)
|
| 66 |
+
|
| 67 |
+
ax1.set_title('Comparação de Convergência entre Estratégias', fontsize=14, fontweight='bold')
|
| 68 |
+
ax1.set_xlabel('Rodada')
|
| 69 |
+
ax1.set_ylabel('Perda de Validação Global')
|
| 70 |
+
ax1.legend()
|
| 71 |
+
ax1.grid(True, alpha=0.3)
|
| 72 |
+
|
| 73 |
+
# 2. Taxa de convergência
|
| 74 |
+
ax2 = axes[0, 1]
|
| 75 |
+
convergence_rates = {}
|
| 76 |
+
|
| 77 |
+
for strategy, df in self.strategies_data.items():
|
| 78 |
+
eval_df = df[df['phase'] == 'eval']
|
| 79 |
+
if not eval_df.empty and 'global_eval_loss' in eval_df.columns:
|
| 80 |
+
losses = eval_df['global_eval_loss'].values
|
| 81 |
+
if len(losses) > 1:
|
| 82 |
+
# Taxa de melhoria por rodada
|
| 83 |
+
improvements = -np.diff(losses)
|
| 84 |
+
convergence_rates[strategy] = improvements
|
| 85 |
+
ax2.plot(eval_df['rounds'].values[1:], improvements,
|
| 86 |
+
marker='s', label=strategy.upper(), linewidth=1.5, alpha=0.7)
|
| 87 |
+
|
| 88 |
+
ax2.axhline(y=0, color='k', linestyle='--', alpha=0.5)
|
| 89 |
+
ax2.set_title('Taxa de Melhoria por Rodada', fontsize=14, fontweight='bold')
|
| 90 |
+
ax2.set_xlabel('Rodada')
|
| 91 |
+
ax2.set_ylabel('Redução na Perda')
|
| 92 |
+
ax2.legend()
|
| 93 |
+
ax2.grid(True, alpha=0.3)
|
| 94 |
+
|
| 95 |
+
# 3. Boxplot de desempenho final dos clientes
|
| 96 |
+
ax3 = axes[1, 0]
|
| 97 |
+
final_performances = []
|
| 98 |
+
labels = []
|
| 99 |
+
|
| 100 |
+
for strategy, df in self.strategies_data.items():
|
| 101 |
+
eval_df = df[df['phase'] == 'eval']
|
| 102 |
+
if not eval_df.empty:
|
| 103 |
+
last_round = eval_df['rounds'].max()
|
| 104 |
+
last_round_data = eval_df[eval_df['rounds'] == last_round]
|
| 105 |
+
|
| 106 |
+
client_losses = []
|
| 107 |
+
for i in range(1, 4):
|
| 108 |
+
col = f'client_{i}_eval_loss'
|
| 109 |
+
if col in last_round_data.columns:
|
| 110 |
+
value = last_round_data[col].values
|
| 111 |
+
if len(value) > 0 and not pd.isna(value[0]):
|
| 112 |
+
client_losses.append(value[0])
|
| 113 |
+
|
| 114 |
+
if client_losses:
|
| 115 |
+
final_performances.append(client_losses)
|
| 116 |
+
labels.append(strategy.upper())
|
| 117 |
+
|
| 118 |
+
if final_performances:
|
| 119 |
+
bp = ax3.boxplot(final_performances, labels=labels, patch_artist=True)
|
| 120 |
+
for patch, color in zip(bp['boxes'], sns.color_palette("husl", len(labels))):
|
| 121 |
+
patch.set_facecolor(color)
|
| 122 |
+
patch.set_alpha(0.7)
|
| 123 |
+
|
| 124 |
+
ax3.set_title('Distribuição Final de Desempenho dos Clientes', fontsize=14, fontweight='bold')
|
| 125 |
+
ax3.set_xlabel('Estratégia')
|
| 126 |
+
ax3.set_ylabel('Perda Final de Validação')
|
| 127 |
+
ax3.grid(True, alpha=0.3, axis='y')
|
| 128 |
+
|
| 129 |
+
# 4. Métricas de eficiência
|
| 130 |
+
ax4 = axes[1, 1]
|
| 131 |
+
strategies = []
|
| 132 |
+
metrics_data = {
|
| 133 |
+
'Convergência': [],
|
| 134 |
+
'Estabilidade': [],
|
| 135 |
+
'Heterogeneidade': []
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
for strategy, df in self.strategies_data.items():
|
| 139 |
+
eval_df = df[df['phase'] == 'eval']
|
| 140 |
+
if not eval_df.empty and 'global_eval_loss' in eval_df.columns:
|
| 141 |
+
strategies.append(strategy.upper())
|
| 142 |
+
|
| 143 |
+
# Convergência: melhoria total
|
| 144 |
+
losses = eval_df['global_eval_loss'].values
|
| 145 |
+
if len(losses) > 1:
|
| 146 |
+
convergence = (losses[0] - losses[-1]) / losses[0]
|
| 147 |
+
metrics_data['Convergência'].append(convergence)
|
| 148 |
+
else:
|
| 149 |
+
metrics_data['Convergência'].append(0)
|
| 150 |
+
|
| 151 |
+
# Estabilidade: desvio padrão das mudanças
|
| 152 |
+
if len(losses) > 1:
|
| 153 |
+
changes = np.diff(losses)
|
| 154 |
+
stability = 1 / (1 + np.std(changes)) # Invertido para que maior = mais estável
|
| 155 |
+
metrics_data['Estabilidade'].append(stability)
|
| 156 |
+
else:
|
| 157 |
+
metrics_data['Estabilidade'].append(0)
|
| 158 |
+
|
| 159 |
+
# Heterogeneidade: variação média entre clientes
|
| 160 |
+
client_cols = [f'client_{i}_eval_loss' for i in range(1, 4)]
|
| 161 |
+
last_round = eval_df['rounds'].max()
|
| 162 |
+
last_round_data = eval_df[eval_df['rounds'] == last_round]
|
| 163 |
+
|
| 164 |
+
client_values = []
|
| 165 |
+
for col in client_cols:
|
| 166 |
+
if col in last_round_data.columns:
|
| 167 |
+
val = last_round_data[col].values
|
| 168 |
+
if len(val) > 0 and not pd.isna(val[0]):
|
| 169 |
+
client_values.append(val[0])
|
| 170 |
+
|
| 171 |
+
if len(client_values) > 1:
|
| 172 |
+
heterogeneity = 1 / (1 + np.std(client_values)) # Invertido
|
| 173 |
+
metrics_data['Heterogeneidade'].append(heterogeneity)
|
| 174 |
+
else:
|
| 175 |
+
metrics_data['Heterogeneidade'].append(0)
|
| 176 |
+
|
| 177 |
+
if strategies:
|
| 178 |
+
x = np.arange(len(strategies))
|
| 179 |
+
width = 0.25
|
| 180 |
+
|
| 181 |
+
for i, (metric, values) in enumerate(metrics_data.items()):
|
| 182 |
+
ax4.bar(x + i * width, values, width, label=metric, alpha=0.8)
|
| 183 |
+
|
| 184 |
+
ax4.set_xlabel('Estratégia')
|
| 185 |
+
ax4.set_ylabel('Score Normalizado')
|
| 186 |
+
ax4.set_title('Métricas de Eficiência Comparativas', fontsize=14, fontweight='bold')
|
| 187 |
+
ax4.set_xticks(x + width)
|
| 188 |
+
ax4.set_xticklabels(strategies)
|
| 189 |
+
ax4.legend()
|
| 190 |
+
ax4.grid(True, alpha=0.3, axis='y')
|
| 191 |
+
|
| 192 |
+
plt.suptitle('Análise Comparativa de Estratégias de Aprendizado Federado',
|
| 193 |
+
fontsize=16, fontweight='bold')
|
| 194 |
+
plt.tight_layout()
|
| 195 |
+
plt.savefig(self.results_dir / 'comparative_analysis.pdf', dpi=300, bbox_inches='tight')
|
| 196 |
+
plt.close()
|
| 197 |
+
|
| 198 |
+
print(f"Análise comparativa salva em: {self.results_dir / 'comparative_analysis.pdf'}")
|
| 199 |
+
|
| 200 |
+
def generate_client_evolution_analysis(self):
|
| 201 |
+
"""Analisa a evolução individual dos clientes."""
|
| 202 |
+
if not self.client_data:
|
| 203 |
+
print("Nenhum dado de cliente encontrado!")
|
| 204 |
+
return
|
| 205 |
+
|
| 206 |
+
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
|
| 207 |
+
|
| 208 |
+
# 1. Evolução do treinamento por cliente
|
| 209 |
+
ax1 = axes[0, 0]
|
| 210 |
+
for client_id, data in self.client_data.items():
|
| 211 |
+
if 'rounds' in data and 'train_losses' in data:
|
| 212 |
+
ax1.plot(data['rounds'], data['train_losses'],
|
| 213 |
+
marker='o', label=f'Cliente {client_id}', linewidth=2)
|
| 214 |
+
|
| 215 |
+
ax1.set_title('Evolução do Treinamento por Cliente', fontsize=14, fontweight='bold')
|
| 216 |
+
ax1.set_xlabel('Rodada')
|
| 217 |
+
ax1.set_ylabel('Perda de Treinamento')
|
| 218 |
+
ax1.legend()
|
| 219 |
+
ax1.grid(True, alpha=0.3)
|
| 220 |
+
|
| 221 |
+
# 2. Evolução da validação por cliente
|
| 222 |
+
ax2 = axes[0, 1]
|
| 223 |
+
for client_id, data in self.client_data.items():
|
| 224 |
+
if 'eval_losses' in data and data['eval_losses']:
|
| 225 |
+
rounds = list(range(1, len(data['eval_losses']) + 1))
|
| 226 |
+
ax2.plot(rounds, data['eval_losses'],
|
| 227 |
+
marker='s', label=f'Cliente {client_id}', linewidth=2)
|
| 228 |
+
|
| 229 |
+
ax2.set_title('Evolução da Validação por Cliente', fontsize=14, fontweight='bold')
|
| 230 |
+
ax2.set_xlabel('Rodada')
|
| 231 |
+
ax2.set_ylabel('Perda de Validação')
|
| 232 |
+
ax2.legend()
|
| 233 |
+
ax2.grid(True, alpha=0.3)
|
| 234 |
+
|
| 235 |
+
# 3. Mudanças nos parâmetros do modelo
|
| 236 |
+
ax3 = axes[1, 0]
|
| 237 |
+
for client_id, data in self.client_data.items():
|
| 238 |
+
if 'model_updates' in data and data['model_updates']:
|
| 239 |
+
rounds = list(range(1, len(data['model_updates']) + 1))
|
| 240 |
+
ax3.plot(rounds, data['model_updates'],
|
| 241 |
+
marker='^', label=f'Cliente {client_id}', linewidth=1.5, alpha=0.7)
|
| 242 |
+
|
| 243 |
+
ax3.set_title('Magnitude das Atualizações do Modelo', fontsize=14, fontweight='bold')
|
| 244 |
+
ax3.set_xlabel('Rodada')
|
| 245 |
+
ax3.set_ylabel('Mudança Média nos Parâmetros')
|
| 246 |
+
ax3.legend()
|
| 247 |
+
ax3.grid(True, alpha=0.3)
|
| 248 |
+
|
| 249 |
+
# 4. Tempo de convergência relativo
|
| 250 |
+
ax4 = axes[1, 1]
|
| 251 |
+
convergence_times = []
|
| 252 |
+
client_ids = []
|
| 253 |
+
|
| 254 |
+
for client_id, data in self.client_data.items():
|
| 255 |
+
if 'train_losses' in data and len(data['train_losses']) > 1:
|
| 256 |
+
losses = np.array(data['train_losses'])
|
| 257 |
+
# Encontrar quando a perda estabiliza (mudança < 1%)
|
| 258 |
+
for i in range(1, len(losses)):
|
| 259 |
+
if abs(losses[i] - losses[i-1]) / losses[i-1] < 0.01:
|
| 260 |
+
convergence_times.append(data['rounds'][i])
|
| 261 |
+
client_ids.append(f'Cliente {client_id}')
|
| 262 |
+
break
|
| 263 |
+
else:
|
| 264 |
+
convergence_times.append(data['rounds'][-1])
|
| 265 |
+
client_ids.append(f'Cliente {client_id}')
|
| 266 |
+
|
| 267 |
+
if convergence_times:
|
| 268 |
+
colors = sns.color_palette("husl", len(client_ids))
|
| 269 |
+
bars = ax4.bar(client_ids, convergence_times, color=colors, alpha=0.7)
|
| 270 |
+
ax4.set_title('Tempo de Convergência por Cliente', fontsize=14, fontweight='bold')
|
| 271 |
+
ax4.set_xlabel('Cliente')
|
| 272 |
+
ax4.set_ylabel('Rodada de Convergência')
|
| 273 |
+
ax4.grid(True, alpha=0.3, axis='y')
|
| 274 |
+
|
| 275 |
+
# Adicionar valores nas barras
|
| 276 |
+
for bar, value in zip(bars, convergence_times):
|
| 277 |
+
height = bar.get_height()
|
| 278 |
+
ax4.text(bar.get_x() + bar.get_width()/2., height,
|
| 279 |
+
f'{value}', ha='center', va='bottom')
|
| 280 |
+
|
| 281 |
+
plt.suptitle('Análise de Evolução Individual dos Clientes',
|
| 282 |
+
fontsize=16, fontweight='bold')
|
| 283 |
+
plt.tight_layout()
|
| 284 |
+
plt.savefig(self.results_dir / 'client_evolution_analysis.pdf', dpi=300, bbox_inches='tight')
|
| 285 |
+
plt.close()
|
| 286 |
+
|
| 287 |
+
print(f"Análise de evolução dos clientes salva em: {self.results_dir / 'client_evolution_analysis.pdf'}")
|
| 288 |
+
|
| 289 |
+
def generate_summary_report(self):
|
| 290 |
+
"""Gera relatório resumido em formato texto e JSON."""
|
| 291 |
+
report = {
|
| 292 |
+
"timestamp": pd.Timestamp.now().isoformat(),
|
| 293 |
+
"strategies_analyzed": list(self.strategies_data.keys()),
|
| 294 |
+
"clients_analyzed": list(self.client_data.keys()),
|
| 295 |
+
"summary": {}
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
# Análise por estratégia
|
| 299 |
+
for strategy, df in self.strategies_data.items():
|
| 300 |
+
eval_df = df[df['phase'] == 'eval']
|
| 301 |
+
train_df = df[df['phase'] == 'train']
|
| 302 |
+
|
| 303 |
+
strategy_summary = {
|
| 304 |
+
"total_rounds": int(eval_df['rounds'].max()) if not eval_df.empty else 0,
|
| 305 |
+
"final_global_eval_loss": float(eval_df['global_eval_loss'].iloc[-1]) if not eval_df.empty else None,
|
| 306 |
+
"final_global_train_loss": float(train_df['global_train_loss'].iloc[-1]) if not train_df.empty else None,
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
# Calcular métricas de melhoria
|
| 310 |
+
if not eval_df.empty and len(eval_df) > 1:
|
| 311 |
+
initial = eval_df['global_eval_loss'].iloc[0]
|
| 312 |
+
final = eval_df['global_eval_loss'].iloc[-1]
|
| 313 |
+
strategy_summary["improvement_percentage"] = float((initial - final) / initial * 100)
|
| 314 |
+
strategy_summary["convergence_rate"] = float(np.mean(np.diff(eval_df['global_eval_loss'].values)))
|
| 315 |
+
|
| 316 |
+
# Métricas por cliente
|
| 317 |
+
client_metrics = {}
|
| 318 |
+
for i in range(1, 4):
|
| 319 |
+
col = f'client_{i}_eval_loss'
|
| 320 |
+
if col in eval_df.columns:
|
| 321 |
+
client_losses = eval_df[col].dropna()
|
| 322 |
+
if not client_losses.empty:
|
| 323 |
+
client_metrics[f'client_{i}'] = {
|
| 324 |
+
"final_loss": float(client_losses.iloc[-1]),
|
| 325 |
+
"best_loss": float(client_losses.min()),
|
| 326 |
+
"worst_loss": float(client_losses.max()),
|
| 327 |
+
"std_loss": float(client_losses.std())
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
strategy_summary["client_metrics"] = client_metrics
|
| 331 |
+
report["summary"][strategy] = strategy_summary
|
| 332 |
+
|
| 333 |
+
# Salvar relatório
|
| 334 |
+
report_file = self.results_dir / "summary_report.json"
|
| 335 |
+
with open(report_file, 'w') as f:
|
| 336 |
+
json.dump(report, f, indent=2)
|
| 337 |
+
|
| 338 |
+
print(f"\nRelatório resumido salvo em: {report_file}")
|
| 339 |
+
|
| 340 |
+
# Imprimir resumo no console
|
| 341 |
+
print("\n" + "="*60)
|
| 342 |
+
print("RESUMO DA ANÁLISE")
|
| 343 |
+
print("="*60)
|
| 344 |
+
|
| 345 |
+
for strategy, summary in report["summary"].items():
|
| 346 |
+
print(f"\nEstratégia: {strategy.upper()}")
|
| 347 |
+
print(f" - Rodadas totais: {summary['total_rounds']}")
|
| 348 |
+
print(f" - Perda final (validação): {summary['final_global_eval_loss']:.6f}" if summary['final_global_eval_loss'] else " - Perda final: N/A")
|
| 349 |
+
print(f" - Melhoria: {summary.get('improvement_percentage', 0):.2f}%")
|
| 350 |
+
|
| 351 |
+
if summary['client_metrics']:
|
| 352 |
+
print(" - Desempenho por cliente:")
|
| 353 |
+
for client, metrics in summary['client_metrics'].items():
|
| 354 |
+
print(f" {client}: Final={metrics['final_loss']:.6f}, Melhor={metrics['best_loss']:.6f}")
|
| 355 |
+
|
| 356 |
+
def run_full_analysis(self):
|
| 357 |
+
"""Executa análise completa."""
|
| 358 |
+
print("\n" + "="*60)
|
| 359 |
+
print("INICIANDO ANÁLISE COMPLETA DOS RESULTADOS")
|
| 360 |
+
print("="*60 + "\n")
|
| 361 |
+
|
| 362 |
+
self.load_server_metrics()
|
| 363 |
+
self.load_client_metrics()
|
| 364 |
+
|
| 365 |
+
if self.strategies_data:
|
| 366 |
+
self.generate_comparative_analysis()
|
| 367 |
+
|
| 368 |
+
if self.client_data:
|
| 369 |
+
self.generate_client_evolution_analysis()
|
| 370 |
+
|
| 371 |
+
if self.strategies_data or self.client_data:
|
| 372 |
+
self.generate_summary_report()
|
| 373 |
+
|
| 374 |
+
print("\n" + "="*60)
|
| 375 |
+
print("ANÁLISE COMPLETA FINALIZADA")
|
| 376 |
+
print("="*60)
|
| 377 |
+
|
| 378 |
+
def main():
|
| 379 |
+
parser = argparse.ArgumentParser(description="Análise de Resultados FL")
|
| 380 |
+
parser.add_argument("--results-dir", type=str, default="results",
|
| 381 |
+
help="Diretório com os resultados")
|
| 382 |
+
args = parser.parse_args()
|
| 383 |
+
|
| 384 |
+
analyzer = FLAnalyzer(args.results_dir)
|
| 385 |
+
analyzer.run_full_analysis()
|
| 386 |
+
|
| 387 |
+
if __name__ == "__main__":
|
| 388 |
+
main()
|
client.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import flwr as fl
|
| 3 |
+
import torch
|
| 4 |
+
import json
|
| 5 |
+
import numpy as np
|
| 6 |
+
from collections import OrderedDict
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
|
| 10 |
+
from utils import Net, load_data, train, test
|
| 11 |
+
|
| 12 |
+
# Argumentos da linha de comando para identificar o cliente
|
| 13 |
+
parser = argparse.ArgumentParser(description="Flower Client")
|
| 14 |
+
parser.add_argument("--client-id", type=int, required=True, help="ID do Cliente (1, 2, ou 3)")
|
| 15 |
+
parser.add_argument("--server-address", type=str, default="127.0.0.1:8080", help="Endereço do servidor")
|
| 16 |
+
parser.add_argument(
|
| 17 |
+
"--prediction-length",
|
| 18 |
+
type=int,
|
| 19 |
+
default=10,
|
| 20 |
+
help="Define quantos passos no futuro o modelo deve prever."
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
# Checa se a GPU está disponível
|
| 24 |
+
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 25 |
+
|
| 26 |
+
class MetricsTracker:
|
| 27 |
+
"""Classe para rastrear e salvar métricas locais do cliente."""
|
| 28 |
+
|
| 29 |
+
def __init__(self, client_id):
|
| 30 |
+
self.client_id = client_id
|
| 31 |
+
self.metrics = {
|
| 32 |
+
"client_id": client_id,
|
| 33 |
+
"rounds": [],
|
| 34 |
+
"train_losses": [],
|
| 35 |
+
"eval_losses": [],
|
| 36 |
+
"timestamps": [],
|
| 37 |
+
"model_updates": []
|
| 38 |
+
}
|
| 39 |
+
self.output_dir = Path(f"metrics/client_{client_id}")
|
| 40 |
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
| 41 |
+
|
| 42 |
+
def add_train_metrics(self, round_num, loss):
|
| 43 |
+
"""Adiciona métricas de treinamento."""
|
| 44 |
+
self.metrics["rounds"].append(round_num)
|
| 45 |
+
self.metrics["train_losses"].append(float(loss))
|
| 46 |
+
self.metrics["timestamps"].append(datetime.now().isoformat())
|
| 47 |
+
|
| 48 |
+
def add_eval_metrics(self, loss):
|
| 49 |
+
"""Adiciona métricas de avaliação."""
|
| 50 |
+
self.metrics["eval_losses"].append(float(loss))
|
| 51 |
+
|
| 52 |
+
def add_model_update(self, params_diff):
|
| 53 |
+
"""Rastreia mudanças nos parâmetros do modelo."""
|
| 54 |
+
self.metrics["model_updates"].append(float(params_diff))
|
| 55 |
+
|
| 56 |
+
def save_metrics(self):
|
| 57 |
+
"""Salva as métricas em arquivo JSON."""
|
| 58 |
+
output_file = self.output_dir / f"metrics_history.json"
|
| 59 |
+
with open(output_file, 'w') as f:
|
| 60 |
+
json.dump(self.metrics, f, indent=2)
|
| 61 |
+
print(f"[Cliente {self.client_id}] Métricas salvas em {output_file}")
|
| 62 |
+
|
| 63 |
+
def save_checkpoint(self, model, round_num):
|
| 64 |
+
"""Salva checkpoint do modelo."""
|
| 65 |
+
checkpoint_file = self.output_dir / f"model_round_{round_num}.pt"
|
| 66 |
+
torch.save(model.state_dict(), checkpoint_file)
|
| 67 |
+
print(f"[Cliente {self.client_id}] Checkpoint salvo: {checkpoint_file}")
|
| 68 |
+
|
| 69 |
+
def calculate_params_diff(old_params, new_params):
|
| 70 |
+
"""Calcula a diferença entre parâmetros antigos e novos."""
|
| 71 |
+
if old_params is None:
|
| 72 |
+
return 0.0
|
| 73 |
+
|
| 74 |
+
total_diff = 0.0
|
| 75 |
+
for old, new in zip(old_params, new_params):
|
| 76 |
+
diff = np.mean(np.abs(old - new))
|
| 77 |
+
total_diff += diff
|
| 78 |
+
|
| 79 |
+
return total_diff / len(old_params)
|
| 80 |
+
|
| 81 |
+
def main():
|
| 82 |
+
args = parser.parse_args()
|
| 83 |
+
|
| 84 |
+
# Carrega dados específicos deste cliente
|
| 85 |
+
print(f"Carregando dados para o cliente {args.client_id}...")
|
| 86 |
+
print(f"Dispositivo: {DEVICE}")
|
| 87 |
+
|
| 88 |
+
trainloader, testloader, num_features = load_data(
|
| 89 |
+
client_id=args.client_id,
|
| 90 |
+
prediction_length=args.prediction_length
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
print(f"Dados carregados: {len(trainloader)} batches de treino, {len(testloader)} batches de teste")
|
| 94 |
+
|
| 95 |
+
# Instancia o modelo
|
| 96 |
+
print(f"Criando modelo LSTM para prever {args.prediction_length} passos.")
|
| 97 |
+
net = Net(
|
| 98 |
+
input_size=num_features,
|
| 99 |
+
hidden_size=50,
|
| 100 |
+
output_size=args.prediction_length
|
| 101 |
+
).to(DEVICE)
|
| 102 |
+
|
| 103 |
+
# Inicializa o rastreador de métricas
|
| 104 |
+
metrics_tracker = MetricsTracker(args.client_id)
|
| 105 |
+
|
| 106 |
+
# Implementação do cliente Flower
|
| 107 |
+
class FlClient(fl.client.NumPyClient):
|
| 108 |
+
def __init__(self):
|
| 109 |
+
self.round_num = 0
|
| 110 |
+
self.previous_params = None
|
| 111 |
+
|
| 112 |
+
def get_parameters(self, config):
|
| 113 |
+
"""Retorna os pesos do modelo local."""
|
| 114 |
+
return [val.cpu().numpy() for _, val in net.state_dict().items()]
|
| 115 |
+
|
| 116 |
+
def set_parameters(self, parameters):
|
| 117 |
+
"""Atualiza os pesos do modelo local com os do servidor."""
|
| 118 |
+
params_dict = zip(net.state_dict().keys(), parameters)
|
| 119 |
+
state_dict = OrderedDict({k: torch.tensor(v) for k, v in params_dict})
|
| 120 |
+
net.load_state_dict(state_dict, strict=True)
|
| 121 |
+
|
| 122 |
+
# Calcula a diferença entre parâmetros
|
| 123 |
+
if self.previous_params is not None:
|
| 124 |
+
params_diff = calculate_params_diff(self.previous_params, parameters)
|
| 125 |
+
metrics_tracker.add_model_update(params_diff)
|
| 126 |
+
|
| 127 |
+
self.previous_params = [p.copy() for p in parameters]
|
| 128 |
+
|
| 129 |
+
def fit(self, parameters, config):
|
| 130 |
+
"""Treina o modelo localmente e retorna a perda de treinamento."""
|
| 131 |
+
self.round_num += 1
|
| 132 |
+
print(f"\n[Cliente {args.client_id}] === Rodada {self.round_num} ===")
|
| 133 |
+
|
| 134 |
+
# Define os parâmetros recebidos do servidor
|
| 135 |
+
self.set_parameters(parameters)
|
| 136 |
+
|
| 137 |
+
# Treina o modelo
|
| 138 |
+
print(f"[Cliente {args.client_id}] Iniciando treinamento...")
|
| 139 |
+
avg_train_loss = train(net, trainloader, epochs=1, device=DEVICE)
|
| 140 |
+
|
| 141 |
+
# Registra métricas
|
| 142 |
+
metrics_tracker.add_train_metrics(self.round_num, avg_train_loss)
|
| 143 |
+
print(f"[Cliente {args.client_id}] Perda de treinamento local: {avg_train_loss:.6f}")
|
| 144 |
+
|
| 145 |
+
# Salva checkpoint a cada 5 rodadas
|
| 146 |
+
if self.round_num % 5 == 0:
|
| 147 |
+
metrics_tracker.save_checkpoint(net, self.round_num)
|
| 148 |
+
|
| 149 |
+
# Salva métricas
|
| 150 |
+
metrics_tracker.save_metrics()
|
| 151 |
+
|
| 152 |
+
return self.get_parameters(config={}), len(trainloader.dataset), {
|
| 153 |
+
"train_loss": avg_train_loss,
|
| 154 |
+
"client_id": args.client_id,
|
| 155 |
+
"round": self.round_num
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
def evaluate(self, parameters, config):
|
| 159 |
+
"""Avalia o modelo localmente."""
|
| 160 |
+
self.set_parameters(parameters)
|
| 161 |
+
print(f"[Cliente {args.client_id}] Avaliando modelo...")
|
| 162 |
+
|
| 163 |
+
loss, num_examples = test(net, testloader, device=DEVICE)
|
| 164 |
+
|
| 165 |
+
# Registra métricas de avaliação
|
| 166 |
+
metrics_tracker.add_eval_metrics(loss)
|
| 167 |
+
metrics_tracker.save_metrics()
|
| 168 |
+
|
| 169 |
+
print(f"[Cliente {args.client_id}] Perda de validação: {loss:.6f}")
|
| 170 |
+
|
| 171 |
+
return loss, num_examples, {
|
| 172 |
+
"loss": loss,
|
| 173 |
+
"client_id": args.client_id,
|
| 174 |
+
"round": self.round_num
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
# Inicia o cliente
|
| 178 |
+
print(f"\n{'='*50}")
|
| 179 |
+
print(f"Iniciando cliente {args.client_id}")
|
| 180 |
+
print(f"Conectando ao servidor em {args.server_address}")
|
| 181 |
+
print(f"Modelo: LSTM com {sum(p.numel() for p in net.parameters())} parâmetros")
|
| 182 |
+
print(f"{'='*50}\n")
|
| 183 |
+
|
| 184 |
+
try:
|
| 185 |
+
fl.client.start_client(
|
| 186 |
+
server_address=args.server_address,
|
| 187 |
+
client=FlClient().to_client(),
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
except Exception as e:
|
| 191 |
+
print(f"[Cliente {args.client_id}] Erro: {e}")
|
| 192 |
+
metrics_tracker.save_metrics()
|
| 193 |
+
finally:
|
| 194 |
+
print(f"\n[Cliente {args.client_id}] Finalizado. Métricas salvas em {metrics_tracker.output_dir}")
|
| 195 |
+
|
| 196 |
+
if __name__ == "__main__":
|
| 197 |
+
main()
|
client_1_fedadagrad.log
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[93mWARNING [0m: DEPRECATED FEATURE: flwr.client.start_client() is deprecated.
|
| 2 |
+
Instead, use the `flower-supernode` CLI command to start a SuperNode as shown below:
|
| 3 |
+
|
| 4 |
+
$ flower-supernode --insecure --superlink='<IP>:<PORT>'
|
| 5 |
+
|
| 6 |
+
To view all available options, run:
|
| 7 |
+
|
| 8 |
+
$ flower-supernode --help
|
| 9 |
+
|
| 10 |
+
Using `start_client()` is deprecated.
|
| 11 |
+
|
| 12 |
+
This is a deprecated feature. It will be removed
|
| 13 |
+
entirely in future versions of Flower.
|
| 14 |
+
|
| 15 |
+
[92mINFO [0m:
|
| 16 |
+
[92mINFO [0m: Received: train message 291d80fe-1d64-4e2a-9cc3-c07e0b31623a
|
| 17 |
+
[92mINFO [0m: Sent reply
|
| 18 |
+
[92mINFO [0m:
|
| 19 |
+
[92mINFO [0m: Received: evaluate message c9e6f2aa-e700-4c85-a0e9-4e5367b01b52
|
| 20 |
+
[92mINFO [0m: Sent reply
|
| 21 |
+
[92mINFO [0m:
|
| 22 |
+
[92mINFO [0m: Received: train message 8d273cce-32d4-483d-9588-a016cc012493
|
| 23 |
+
[92mINFO [0m: Sent reply
|
| 24 |
+
[92mINFO [0m:
|
| 25 |
+
[92mINFO [0m: Received: evaluate message aba72052-b296-4f4c-99a0-21ab26af46e3
|
| 26 |
+
[92mINFO [0m: Sent reply
|
| 27 |
+
[92mINFO [0m:
|
| 28 |
+
[92mINFO [0m: Received: train message 65d5ecd1-e554-402a-9c56-935e9c962829
|
| 29 |
+
[92mINFO [0m: Sent reply
|
| 30 |
+
[92mINFO [0m:
|
| 31 |
+
[92mINFO [0m: Received: evaluate message 65a7d7f4-be1b-4f76-ae2e-d49e50a58219
|
| 32 |
+
[92mINFO [0m: Sent reply
|
| 33 |
+
[92mINFO [0m:
|
| 34 |
+
[92mINFO [0m: Received: train message 28acbc3d-ed5c-496a-b85e-aa1150fd9de2
|
| 35 |
+
[92mINFO [0m: Sent reply
|
| 36 |
+
[92mINFO [0m:
|
| 37 |
+
[92mINFO [0m: Received: evaluate message 75b4e73b-f09d-4033-a3fa-43f6f5ed702a
|
| 38 |
+
[92mINFO [0m: Sent reply
|
| 39 |
+
[92mINFO [0m:
|
| 40 |
+
[92mINFO [0m: Received: train message f5b93afe-d985-4193-acfb-ab4536c928a4
|
| 41 |
+
[92mINFO [0m: Sent reply
|
| 42 |
+
[92mINFO [0m:
|
| 43 |
+
[92mINFO [0m: Received: evaluate message 44d83f54-1cdf-4a6e-a9f0-b55b29a55d3b
|
| 44 |
+
[92mINFO [0m: Sent reply
|
| 45 |
+
[92mINFO [0m:
|
| 46 |
+
[92mINFO [0m: Received: train message 8b6164bb-4371-46fd-8bd6-205790be539a
|
| 47 |
+
[92mINFO [0m: Sent reply
|
| 48 |
+
[92mINFO [0m:
|
| 49 |
+
[92mINFO [0m: Received: evaluate message d547915d-42a7-4ce5-a6ca-54d59e933b69
|
| 50 |
+
[92mINFO [0m: Sent reply
|
| 51 |
+
[92mINFO [0m:
|
| 52 |
+
[92mINFO [0m: Received: train message 0a210acf-de6a-4813-b415-db9d71378c74
|
| 53 |
+
[92mINFO [0m: Sent reply
|
| 54 |
+
[92mINFO [0m:
|
| 55 |
+
[92mINFO [0m: Received: evaluate message f321a6c0-ae43-4f82-92b6-00b0955ce4a8
|
| 56 |
+
[92mINFO [0m: Sent reply
|
| 57 |
+
[92mINFO [0m:
|
| 58 |
+
[92mINFO [0m: Received: train message a31760dc-1a27-4af3-8f4b-9e849a01d5f7
|
| 59 |
+
[92mINFO [0m: Sent reply
|
| 60 |
+
[92mINFO [0m:
|
| 61 |
+
[92mINFO [0m: Received: evaluate message 8dea38f0-76a7-4a33-acfb-4a1286102bc1
|
| 62 |
+
[92mINFO [0m: Sent reply
|
| 63 |
+
[92mINFO [0m:
|
| 64 |
+
[92mINFO [0m: Received: train message 48cfe5e0-b781-4130-8162-c418261ae5a6
|
| 65 |
+
[92mINFO [0m: Sent reply
|
| 66 |
+
[92mINFO [0m:
|
| 67 |
+
[92mINFO [0m: Received: evaluate message 34a6bbdf-6692-43b6-860f-ac2e416755ea
|
| 68 |
+
[92mINFO [0m: Sent reply
|
| 69 |
+
[92mINFO [0m:
|
| 70 |
+
[92mINFO [0m: Received: train message 80e5e17b-175c-42e0-be18-bd18436eef6c
|
| 71 |
+
[92mINFO [0m: Sent reply
|
| 72 |
+
[92mINFO [0m:
|
| 73 |
+
[92mINFO [0m: Received: evaluate message c2151701-7139-4dd7-b2b1-802805c4da83
|
| 74 |
+
[92mINFO [0m: Sent reply
|
| 75 |
+
[92mINFO [0m:
|
| 76 |
+
[92mINFO [0m: Received: reconnect message f4c73386-bf01-439d-8dca-e7522f1cf998
|
| 77 |
+
[92mINFO [0m: Disconnect and shut down
|
| 78 |
+
Carregando dados para o cliente 1...
|
| 79 |
+
Dispositivo: cpu
|
| 80 |
+
Dados carregados: 623 batches de treino, 153 batches de teste
|
| 81 |
+
Criando modelo LSTM para prever 90 passos.
|
| 82 |
+
|
| 83 |
+
==================================================
|
| 84 |
+
Iniciando cliente 1
|
| 85 |
+
Conectando ao servidor em 127.0.0.1:8080
|
| 86 |
+
Modelo: LSTM com 16190 par�metros
|
| 87 |
+
==================================================
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
[Cliente 1] === Rodada 1 ===
|
| 91 |
+
[Cliente 1] Iniciando treinamento...
|
| 92 |
+
[Cliente 1] Perda de treinamento local: 0.011821
|
| 93 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 94 |
+
[Cliente 1] Avaliando modelo...
|
| 95 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 96 |
+
[Cliente 1] Perda de valida��o: 9.976543
|
| 97 |
+
|
| 98 |
+
[Cliente 1] === Rodada 2 ===
|
| 99 |
+
[Cliente 1] Iniciando treinamento...
|
| 100 |
+
[Cliente 1] Perda de treinamento local: 9.190875
|
| 101 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 102 |
+
[Cliente 1] Avaliando modelo...
|
| 103 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 104 |
+
[Cliente 1] Perda de valida��o: 1.286495
|
| 105 |
+
|
| 106 |
+
[Cliente 1] === Rodada 3 ===
|
| 107 |
+
[Cliente 1] Iniciando treinamento...
|
| 108 |
+
[Cliente 1] Perda de treinamento local: 0.605915
|
| 109 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 110 |
+
[Cliente 1] Avaliando modelo...
|
| 111 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 112 |
+
[Cliente 1] Perda de valida��o: 0.279315
|
| 113 |
+
|
| 114 |
+
[Cliente 1] === Rodada 4 ===
|
| 115 |
+
[Cliente 1] Iniciando treinamento...
|
| 116 |
+
[Cliente 1] Perda de treinamento local: 0.197050
|
| 117 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 118 |
+
[Cliente 1] Avaliando modelo...
|
| 119 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 120 |
+
[Cliente 1] Perda de valida��o: 0.596508
|
| 121 |
+
|
| 122 |
+
[Cliente 1] === Rodada 5 ===
|
| 123 |
+
[Cliente 1] Iniciando treinamento...
|
| 124 |
+
[Cliente 1] Perda de treinamento local: 0.139895
|
| 125 |
+
[Cliente 1] Checkpoint salvo: metrics\client_1\model_round_5.pt
|
| 126 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 127 |
+
[Cliente 1] Avaliando modelo...
|
| 128 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 129 |
+
[Cliente 1] Perda de valida��o: 0.038253
|
| 130 |
+
|
| 131 |
+
[Cliente 1] === Rodada 6 ===
|
| 132 |
+
[Cliente 1] Iniciando treinamento...
|
| 133 |
+
[Cliente 1] Perda de treinamento local: 0.020903
|
| 134 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 135 |
+
[Cliente 1] Avaliando modelo...
|
| 136 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 137 |
+
[Cliente 1] Perda de valida��o: 0.267797
|
| 138 |
+
|
| 139 |
+
[Cliente 1] === Rodada 7 ===
|
| 140 |
+
[Cliente 1] Iniciando treinamento...
|
| 141 |
+
[Cliente 1] Perda de treinamento local: 0.147180
|
| 142 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 143 |
+
[Cliente 1] Avaliando modelo...
|
| 144 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 145 |
+
[Cliente 1] Perda de valida��o: 0.058362
|
| 146 |
+
|
| 147 |
+
[Cliente 1] === Rodada 8 ===
|
| 148 |
+
[Cliente 1] Iniciando treinamento...
|
| 149 |
+
[Cliente 1] Perda de treinamento local: 0.036225
|
| 150 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 151 |
+
[Cliente 1] Avaliando modelo...
|
| 152 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 153 |
+
[Cliente 1] Perda de valida��o: 0.081573
|
| 154 |
+
|
| 155 |
+
[Cliente 1] === Rodada 9 ===
|
| 156 |
+
[Cliente 1] Iniciando treinamento...
|
| 157 |
+
[Cliente 1] Perda de treinamento local: 0.021779
|
| 158 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 159 |
+
[Cliente 1] Avaliando modelo...
|
| 160 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 161 |
+
[Cliente 1] Perda de valida��o: 0.029207
|
| 162 |
+
|
| 163 |
+
[Cliente 1] === Rodada 10 ===
|
| 164 |
+
[Cliente 1] Iniciando treinamento...
|
| 165 |
+
[Cliente 1] Perda de treinamento local: 0.014076
|
| 166 |
+
[Cliente 1] Checkpoint salvo: metrics\client_1\model_round_10.pt
|
| 167 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 168 |
+
[Cliente 1] Avaliando modelo...
|
| 169 |
+
[Cliente 1] M�tricas salvas em metrics\client_1\metrics_history.json
|
| 170 |
+
[Cliente 1] Perda de valida��o: 0.024229
|
| 171 |
+
|
| 172 |
+
[Cliente 1] Finalizado. M�tricas salvas em metrics\client_1
|
client_2_fedadagrad.log
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[93mWARNING [0m: DEPRECATED FEATURE: flwr.client.start_client() is deprecated.
|
| 2 |
+
Instead, use the `flower-supernode` CLI command to start a SuperNode as shown below:
|
| 3 |
+
|
| 4 |
+
$ flower-supernode --insecure --superlink='<IP>:<PORT>'
|
| 5 |
+
|
| 6 |
+
To view all available options, run:
|
| 7 |
+
|
| 8 |
+
$ flower-supernode --help
|
| 9 |
+
|
| 10 |
+
Using `start_client()` is deprecated.
|
| 11 |
+
|
| 12 |
+
This is a deprecated feature. It will be removed
|
| 13 |
+
entirely in future versions of Flower.
|
| 14 |
+
|
| 15 |
+
[92mINFO [0m:
|
| 16 |
+
[92mINFO [0m: Received: train message f63b3be7-35a8-40b1-a0a9-20bce59fac6e
|
| 17 |
+
[92mINFO [0m: Sent reply
|
| 18 |
+
[92mINFO [0m:
|
| 19 |
+
[92mINFO [0m: Received: evaluate message 9f3010c9-fa04-4d9e-8803-979b37827a84
|
| 20 |
+
[92mINFO [0m: Sent reply
|
| 21 |
+
[92mINFO [0m:
|
| 22 |
+
[92mINFO [0m: Received: train message 0c6e2356-9efe-4475-b69a-c6588ef3b826
|
| 23 |
+
[92mINFO [0m: Sent reply
|
| 24 |
+
[92mINFO [0m:
|
| 25 |
+
[92mINFO [0m: Received: evaluate message 32f6c96c-e75e-484f-9e91-a86cf19961da
|
| 26 |
+
[92mINFO [0m: Sent reply
|
| 27 |
+
[92mINFO [0m:
|
| 28 |
+
[92mINFO [0m: Received: train message 6d672c95-660d-4d9a-9d46-5b68d91c375a
|
| 29 |
+
[92mINFO [0m: Sent reply
|
| 30 |
+
[92mINFO [0m:
|
| 31 |
+
[92mINFO [0m: Received: evaluate message 395215b3-06dc-411a-a836-fd88fe798d47
|
| 32 |
+
[92mINFO [0m: Sent reply
|
| 33 |
+
[92mINFO [0m:
|
| 34 |
+
[92mINFO [0m: Received: train message 9d3c92a9-6163-40d9-914c-555d23d5e4d0
|
| 35 |
+
[92mINFO [0m: Sent reply
|
| 36 |
+
[92mINFO [0m:
|
| 37 |
+
[92mINFO [0m: Received: evaluate message 235e95a8-6a3b-4d55-a3ff-952b9db35949
|
| 38 |
+
[92mINFO [0m: Sent reply
|
| 39 |
+
[92mINFO [0m:
|
| 40 |
+
[92mINFO [0m: Received: train message 5accefb0-48ed-40aa-a7bb-16c5c02ef94f
|
| 41 |
+
[92mINFO [0m: Sent reply
|
| 42 |
+
[92mINFO [0m:
|
| 43 |
+
[92mINFO [0m: Received: evaluate message 3c57952d-2100-4363-a3ec-eab5f84023bf
|
| 44 |
+
[92mINFO [0m: Sent reply
|
| 45 |
+
[92mINFO [0m:
|
| 46 |
+
[92mINFO [0m: Received: train message 20d1f6d1-07fb-4130-8567-1fbaa05e5740
|
| 47 |
+
[92mINFO [0m: Sent reply
|
| 48 |
+
[92mINFO [0m:
|
| 49 |
+
[92mINFO [0m: Received: evaluate message 8438ac71-a3ca-4651-ace0-eedd0d24ff66
|
| 50 |
+
[92mINFO [0m: Sent reply
|
| 51 |
+
[92mINFO [0m:
|
| 52 |
+
[92mINFO [0m: Received: train message 8756ce44-0d73-4d3c-9929-54b9c30f0497
|
| 53 |
+
[92mINFO [0m: Sent reply
|
| 54 |
+
[92mINFO [0m:
|
| 55 |
+
[92mINFO [0m: Received: evaluate message d9094dbc-7205-43e9-bb8d-b90a3acf2ed5
|
| 56 |
+
[92mINFO [0m: Sent reply
|
| 57 |
+
[92mINFO [0m:
|
| 58 |
+
[92mINFO [0m: Received: train message 306a0600-bd21-42c9-9ce3-e907fb67ad37
|
| 59 |
+
[92mINFO [0m: Sent reply
|
| 60 |
+
[92mINFO [0m:
|
| 61 |
+
[92mINFO [0m: Received: evaluate message d80fe3a9-259a-4bb5-bd25-3c7b42f93669
|
| 62 |
+
[92mINFO [0m: Sent reply
|
| 63 |
+
[92mINFO [0m:
|
| 64 |
+
[92mINFO [0m: Received: train message 8b553028-68ab-44a9-836e-74e9325db492
|
| 65 |
+
[92mINFO [0m: Sent reply
|
| 66 |
+
[92mINFO [0m:
|
| 67 |
+
[92mINFO [0m: Received: evaluate message d9cc9852-c707-47f0-ac29-443948f7081d
|
| 68 |
+
[92mINFO [0m: Sent reply
|
| 69 |
+
[92mINFO [0m:
|
| 70 |
+
[92mINFO [0m: Received: train message f3758c26-02ea-4f19-9f0f-765b2e0e805e
|
| 71 |
+
[92mINFO [0m: Sent reply
|
| 72 |
+
[92mINFO [0m:
|
| 73 |
+
[92mINFO [0m: Received: evaluate message c0d18b3e-93f7-4262-88e9-a2390bf4c14f
|
| 74 |
+
[92mINFO [0m: Sent reply
|
| 75 |
+
[92mINFO [0m:
|
| 76 |
+
[92mINFO [0m: Received: reconnect message f2598685-f192-43a1-a234-4382f8ed8e70
|
| 77 |
+
[92mINFO [0m: Disconnect and shut down
|
| 78 |
+
Carregando dados para o cliente 2...
|
| 79 |
+
Dispositivo: cpu
|
| 80 |
+
Dados carregados: 790 batches de treino, 194 batches de teste
|
| 81 |
+
Criando modelo LSTM para prever 90 passos.
|
| 82 |
+
|
| 83 |
+
==================================================
|
| 84 |
+
Iniciando cliente 2
|
| 85 |
+
Conectando ao servidor em 127.0.0.1:8080
|
| 86 |
+
Modelo: LSTM com 16190 par�metros
|
| 87 |
+
==================================================
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
[Cliente 2] === Rodada 1 ===
|
| 91 |
+
[Cliente 2] Iniciando treinamento...
|
| 92 |
+
[Cliente 2] Perda de treinamento local: 0.005995
|
| 93 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 94 |
+
[Cliente 2] Avaliando modelo...
|
| 95 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 96 |
+
[Cliente 2] Perda de valida��o: 10.002637
|
| 97 |
+
|
| 98 |
+
[Cliente 2] === Rodada 2 ===
|
| 99 |
+
[Cliente 2] Iniciando treinamento...
|
| 100 |
+
[Cliente 2] Perda de treinamento local: 9.031767
|
| 101 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 102 |
+
[Cliente 2] Avaliando modelo...
|
| 103 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 104 |
+
[Cliente 2] Perda de valida��o: 1.361941
|
| 105 |
+
|
| 106 |
+
[Cliente 2] === Rodada 3 ===
|
| 107 |
+
[Cliente 2] Iniciando treinamento...
|
| 108 |
+
[Cliente 2] Perda de treinamento local: 0.753927
|
| 109 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 110 |
+
[Cliente 2] Avaliando modelo...
|
| 111 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 112 |
+
[Cliente 2] Perda de valida��o: 0.240407
|
| 113 |
+
|
| 114 |
+
[Cliente 2] === Rodada 4 ===
|
| 115 |
+
[Cliente 2] Iniciando treinamento...
|
| 116 |
+
[Cliente 2] Perda de treinamento local: 0.163903
|
| 117 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 118 |
+
[Cliente 2] Avaliando modelo...
|
| 119 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 120 |
+
[Cliente 2] Perda de valida��o: 0.388674
|
| 121 |
+
|
| 122 |
+
[Cliente 2] === Rodada 5 ===
|
| 123 |
+
[Cliente 2] Iniciando treinamento...
|
| 124 |
+
[Cliente 2] Perda de treinamento local: 0.204387
|
| 125 |
+
[Cliente 2] Checkpoint salvo: metrics\client_2\model_round_5.pt
|
| 126 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 127 |
+
[Cliente 2] Avaliando modelo...
|
| 128 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 129 |
+
[Cliente 2] Perda de valida��o: 0.032693
|
| 130 |
+
|
| 131 |
+
[Cliente 2] === Rodada 6 ===
|
| 132 |
+
[Cliente 2] Iniciando treinamento...
|
| 133 |
+
[Cliente 2] Perda de treinamento local: 0.016730
|
| 134 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 135 |
+
[Cliente 2] Avaliando modelo...
|
| 136 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 137 |
+
[Cliente 2] Perda de valida��o: 0.273893
|
| 138 |
+
|
| 139 |
+
[Cliente 2] === Rodada 7 ===
|
| 140 |
+
[Cliente 2] Iniciando treinamento...
|
| 141 |
+
[Cliente 2] Perda de treinamento local: 0.125370
|
| 142 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 143 |
+
[Cliente 2] Avaliando modelo...
|
| 144 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 145 |
+
[Cliente 2] Perda de valida��o: 0.060165
|
| 146 |
+
|
| 147 |
+
[Cliente 2] === Rodada 8 ===
|
| 148 |
+
[Cliente 2] Iniciando treinamento...
|
| 149 |
+
[Cliente 2] Perda de treinamento local: 0.032033
|
| 150 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 151 |
+
[Cliente 2] Avaliando modelo...
|
| 152 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 153 |
+
[Cliente 2] Perda de valida��o: 0.072853
|
| 154 |
+
|
| 155 |
+
[Cliente 2] === Rodada 9 ===
|
| 156 |
+
[Cliente 2] Iniciando treinamento...
|
| 157 |
+
[Cliente 2] Perda de treinamento local: 0.017977
|
| 158 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 159 |
+
[Cliente 2] Avaliando modelo...
|
| 160 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 161 |
+
[Cliente 2] Perda de valida��o: 0.029051
|
| 162 |
+
|
| 163 |
+
[Cliente 2] === Rodada 10 ===
|
| 164 |
+
[Cliente 2] Iniciando treinamento...
|
| 165 |
+
[Cliente 2] Perda de treinamento local: 0.014848
|
| 166 |
+
[Cliente 2] Checkpoint salvo: metrics\client_2\model_round_10.pt
|
| 167 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 168 |
+
[Cliente 2] Avaliando modelo...
|
| 169 |
+
[Cliente 2] M�tricas salvas em metrics\client_2\metrics_history.json
|
| 170 |
+
[Cliente 2] Perda de valida��o: 0.017167
|
| 171 |
+
|
| 172 |
+
[Cliente 2] Finalizado. M�tricas salvas em metrics\client_2
|
client_3_fedadagrad.log
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[93mWARNING [0m: DEPRECATED FEATURE: flwr.client.start_client() is deprecated.
|
| 2 |
+
Instead, use the `flower-supernode` CLI command to start a SuperNode as shown below:
|
| 3 |
+
|
| 4 |
+
$ flower-supernode --insecure --superlink='<IP>:<PORT>'
|
| 5 |
+
|
| 6 |
+
To view all available options, run:
|
| 7 |
+
|
| 8 |
+
$ flower-supernode --help
|
| 9 |
+
|
| 10 |
+
Using `start_client()` is deprecated.
|
| 11 |
+
|
| 12 |
+
This is a deprecated feature. It will be removed
|
| 13 |
+
entirely in future versions of Flower.
|
| 14 |
+
|
| 15 |
+
[92mINFO [0m:
|
| 16 |
+
[92mINFO [0m: Received: train message 5bf40068-c93c-4602-bf14-895c53f8261d
|
| 17 |
+
[92mINFO [0m: Sent reply
|
| 18 |
+
[92mINFO [0m:
|
| 19 |
+
[92mINFO [0m: Received: evaluate message e60b7d20-e6fb-4b54-bba5-54996ce4715b
|
| 20 |
+
[92mINFO [0m: Sent reply
|
| 21 |
+
[92mINFO [0m:
|
| 22 |
+
[92mINFO [0m: Received: train message 7f1c0945-8a25-4062-9b42-482284436d2f
|
| 23 |
+
[92mINFO [0m: Sent reply
|
| 24 |
+
[92mINFO [0m:
|
| 25 |
+
[92mINFO [0m: Received: evaluate message 2ae803e1-04a1-4dde-b2a9-74af9730b699
|
| 26 |
+
[92mINFO [0m: Sent reply
|
| 27 |
+
[92mINFO [0m:
|
| 28 |
+
[92mINFO [0m: Received: train message 6001a64d-9f07-4e76-a36e-ef21f12f0603
|
| 29 |
+
[92mINFO [0m: Sent reply
|
| 30 |
+
[92mINFO [0m:
|
| 31 |
+
[92mINFO [0m: Received: evaluate message 2f886dc7-3aaa-457f-bbcd-a31e13a35538
|
| 32 |
+
[92mINFO [0m: Sent reply
|
| 33 |
+
[92mINFO [0m:
|
| 34 |
+
[92mINFO [0m: Received: train message 109d8ec5-7e2a-4451-acb4-1b455598091d
|
| 35 |
+
[92mINFO [0m: Sent reply
|
| 36 |
+
[92mINFO [0m:
|
| 37 |
+
[92mINFO [0m: Received: evaluate message 146896ae-66fb-4dcf-ae0e-b671f39730c7
|
| 38 |
+
[92mINFO [0m: Sent reply
|
| 39 |
+
[92mINFO [0m:
|
| 40 |
+
[92mINFO [0m: Received: train message d93e8bde-83f5-4213-aa66-31a52f8670dc
|
| 41 |
+
[92mINFO [0m: Sent reply
|
| 42 |
+
[92mINFO [0m:
|
| 43 |
+
[92mINFO [0m: Received: evaluate message c96107d0-5866-46de-b168-dd8d6370e53c
|
| 44 |
+
[92mINFO [0m: Sent reply
|
| 45 |
+
[92mINFO [0m:
|
| 46 |
+
[92mINFO [0m: Received: train message e1bb6d68-e284-4a31-991f-4fdeb7727b6a
|
| 47 |
+
[92mINFO [0m: Sent reply
|
| 48 |
+
[92mINFO [0m:
|
| 49 |
+
[92mINFO [0m: Received: evaluate message 084dbcf4-ee02-460c-a8e4-a0117faef750
|
| 50 |
+
[92mINFO [0m: Sent reply
|
| 51 |
+
[92mINFO [0m:
|
| 52 |
+
[92mINFO [0m: Received: train message 98c62b99-2c4e-4b91-8d65-d62b483eeafb
|
| 53 |
+
[92mINFO [0m: Sent reply
|
| 54 |
+
[92mINFO [0m:
|
| 55 |
+
[92mINFO [0m: Received: evaluate message a5c80502-28c4-46ce-bcf2-615d0940bd83
|
| 56 |
+
[92mINFO [0m: Sent reply
|
| 57 |
+
[92mINFO [0m:
|
| 58 |
+
[92mINFO [0m: Received: train message 4eade5d9-3553-4fcc-bc8f-87abe591fa5b
|
| 59 |
+
[92mINFO [0m: Sent reply
|
| 60 |
+
[92mINFO [0m:
|
| 61 |
+
[92mINFO [0m: Received: evaluate message 40d2c213-cb1d-42fe-b73a-8ca2de4763b6
|
| 62 |
+
[92mINFO [0m: Sent reply
|
| 63 |
+
[92mINFO [0m:
|
| 64 |
+
[92mINFO [0m: Received: train message 729add19-adf9-40d2-961a-574782e0637b
|
| 65 |
+
[92mINFO [0m: Sent reply
|
| 66 |
+
[92mINFO [0m:
|
| 67 |
+
[92mINFO [0m: Received: evaluate message 153d4189-00f1-4f9e-86c1-f4fb126fe539
|
| 68 |
+
[92mINFO [0m: Sent reply
|
| 69 |
+
[92mINFO [0m:
|
| 70 |
+
[92mINFO [0m: Received: train message 72102c53-639a-4983-b85a-57b20d5a52b3
|
| 71 |
+
[92mINFO [0m: Sent reply
|
| 72 |
+
[92mINFO [0m:
|
| 73 |
+
[92mINFO [0m: Received: evaluate message 24104043-0419-439f-9bba-3da769cb80ef
|
| 74 |
+
[92mINFO [0m: Sent reply
|
| 75 |
+
[92mINFO [0m:
|
| 76 |
+
[92mINFO [0m: Received: reconnect message 8f83b63e-303b-4178-b218-4eb319429999
|
| 77 |
+
[92mINFO [0m: Disconnect and shut down
|
| 78 |
+
Carregando dados para o cliente 3...
|
| 79 |
+
Dispositivo: cpu
|
| 80 |
+
Dados carregados: 992 batches de treino, 245 batches de teste
|
| 81 |
+
Criando modelo LSTM para prever 90 passos.
|
| 82 |
+
|
| 83 |
+
==================================================
|
| 84 |
+
Iniciando cliente 3
|
| 85 |
+
Conectando ao servidor em 127.0.0.1:8080
|
| 86 |
+
Modelo: LSTM com 16190 par�metros
|
| 87 |
+
==================================================
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
[Cliente 3] === Rodada 1 ===
|
| 91 |
+
[Cliente 3] Iniciando treinamento...
|
| 92 |
+
[Cliente 3] Perda de treinamento local: 0.047731
|
| 93 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 94 |
+
[Cliente 3] Avaliando modelo...
|
| 95 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 96 |
+
[Cliente 3] Perda de valida��o: 9.831532
|
| 97 |
+
|
| 98 |
+
[Cliente 3] === Rodada 2 ===
|
| 99 |
+
[Cliente 3] Iniciando treinamento...
|
| 100 |
+
[Cliente 3] Perda de treinamento local: 8.576512
|
| 101 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 102 |
+
[Cliente 3] Avaliando modelo...
|
| 103 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 104 |
+
[Cliente 3] Perda de valida��o: 1.461328
|
| 105 |
+
|
| 106 |
+
[Cliente 3] === Rodada 3 ===
|
| 107 |
+
[Cliente 3] Iniciando treinamento...
|
| 108 |
+
[Cliente 3] Perda de treinamento local: 0.702148
|
| 109 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 110 |
+
[Cliente 3] Avaliando modelo...
|
| 111 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 112 |
+
[Cliente 3] Perda de valida��o: 0.341640
|
| 113 |
+
|
| 114 |
+
[Cliente 3] === Rodada 4 ===
|
| 115 |
+
[Cliente 3] Iniciando treinamento...
|
| 116 |
+
[Cliente 3] Perda de treinamento local: 0.209710
|
| 117 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 118 |
+
[Cliente 3] Avaliando modelo...
|
| 119 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 120 |
+
[Cliente 3] Perda de valida��o: 0.511448
|
| 121 |
+
|
| 122 |
+
[Cliente 3] === Rodada 5 ===
|
| 123 |
+
[Cliente 3] Iniciando treinamento...
|
| 124 |
+
[Cliente 3] Perda de treinamento local: 0.252078
|
| 125 |
+
[Cliente 3] Checkpoint salvo: metrics\client_3\model_round_5.pt
|
| 126 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 127 |
+
[Cliente 3] Avaliando modelo...
|
| 128 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 129 |
+
[Cliente 3] Perda de valida��o: 0.058146
|
| 130 |
+
|
| 131 |
+
[Cliente 3] === Rodada 6 ===
|
| 132 |
+
[Cliente 3] Iniciando treinamento...
|
| 133 |
+
[Cliente 3] Perda de treinamento local: 0.039224
|
| 134 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 135 |
+
[Cliente 3] Avaliando modelo...
|
| 136 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 137 |
+
[Cliente 3] Perda de valida��o: 0.304315
|
| 138 |
+
|
| 139 |
+
[Cliente 3] === Rodada 7 ===
|
| 140 |
+
[Cliente 3] Iniciando treinamento...
|
| 141 |
+
[Cliente 3] Perda de treinamento local: 0.190494
|
| 142 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 143 |
+
[Cliente 3] Avaliando modelo...
|
| 144 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 145 |
+
[Cliente 3] Perda de valida��o: 0.058137
|
| 146 |
+
|
| 147 |
+
[Cliente 3] === Rodada 8 ===
|
| 148 |
+
[Cliente 3] Iniciando treinamento...
|
| 149 |
+
[Cliente 3] Perda de treinamento local: 0.039078
|
| 150 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 151 |
+
[Cliente 3] Avaliando modelo...
|
| 152 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 153 |
+
[Cliente 3] Perda de valida��o: 0.083307
|
| 154 |
+
|
| 155 |
+
[Cliente 3] === Rodada 9 ===
|
| 156 |
+
[Cliente 3] Iniciando treinamento...
|
| 157 |
+
[Cliente 3] Perda de treinamento local: 0.034105
|
| 158 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 159 |
+
[Cliente 3] Avaliando modelo...
|
| 160 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 161 |
+
[Cliente 3] Perda de valida��o: 0.049672
|
| 162 |
+
|
| 163 |
+
[Cliente 3] === Rodada 10 ===
|
| 164 |
+
[Cliente 3] Iniciando treinamento...
|
| 165 |
+
[Cliente 3] Perda de treinamento local: 0.031320
|
| 166 |
+
[Cliente 3] Checkpoint salvo: metrics\client_3\model_round_10.pt
|
| 167 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 168 |
+
[Cliente 3] Avaliando modelo...
|
| 169 |
+
[Cliente 3] M�tricas salvas em metrics\client_3\metrics_history.json
|
| 170 |
+
[Cliente 3] Perda de valida��o: 0.029677
|
| 171 |
+
|
| 172 |
+
[Cliente 3] Finalizado. M�tricas salvas em metrics\client_3
|
requeriments.txt
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core FL Framework
|
| 2 |
+
flwr==1.10.0
|
| 3 |
+
flwr-datasets==0.3.0
|
| 4 |
+
|
| 5 |
+
# Deep Learning
|
| 6 |
+
torch==2.0.1
|
| 7 |
+
torchvision==0.15.2
|
| 8 |
+
torchaudio==2.0.2
|
| 9 |
+
|
| 10 |
+
# Data Processing & Analysis
|
| 11 |
+
numpy==1.24.3
|
| 12 |
+
pandas==2.0.3
|
| 13 |
+
scikit-learn==1.3.0
|
| 14 |
+
scipy==1.11.1
|
| 15 |
+
|
| 16 |
+
# Visualization
|
| 17 |
+
matplotlib==3.7.2
|
| 18 |
+
seaborn==0.12.2
|
| 19 |
+
|
| 20 |
+
# Network & System
|
| 21 |
+
psutil==5.9.5
|
| 22 |
+
requests==2.31.0
|
| 23 |
+
|
| 24 |
+
# Development Tools (optional but recommended)
|
| 25 |
+
jupyter==1.0.0
|
| 26 |
+
ipython==8.14.0
|
| 27 |
+
tqdm==4.65.0
|
| 28 |
+
|
| 29 |
+
# For Windows compatibility
|
| 30 |
+
pywin32==306; sys_platform == 'win32'
|
| 31 |
+
|
| 32 |
+
# For Linux compatibility
|
| 33 |
+
python-dotenv==1.0.0
|
run.sh
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Cores para output
|
| 4 |
+
RED='\033[0;31m'
|
| 5 |
+
GREEN='\033[0;32m'
|
| 6 |
+
YELLOW='\033[1;33m'
|
| 7 |
+
BLUE='\033[0;34m'
|
| 8 |
+
NC='\033[0m' # No Color
|
| 9 |
+
|
| 10 |
+
# Configurações
|
| 11 |
+
STRATEGY=${1:-fedavg} # Estratégia padrão: fedavg
|
| 12 |
+
ROUNDS=${2:-15} # Número de rodadas padrão: 15
|
| 13 |
+
PREDICTION_LENGTH=${3:-10} # Tamanho da previsão padrão: 10
|
| 14 |
+
|
| 15 |
+
echo -e "${BLUE}================================================${NC}"
|
| 16 |
+
echo -e "${BLUE} SIMULAÇÃO DE APRENDIZADO FEDERADO${NC}"
|
| 17 |
+
echo -e "${BLUE}================================================${NC}"
|
| 18 |
+
echo -e "${GREEN}Estratégia: ${YELLOW}$STRATEGY${NC}"
|
| 19 |
+
echo -e "${GREEN}Rodadas: ${YELLOW}$ROUNDS${NC}"
|
| 20 |
+
echo -e "${GREEN}Tamanho da Previsão: ${YELLOW}$PREDICTION_LENGTH passos${NC}"
|
| 21 |
+
echo -e "${BLUE}================================================${NC}\n"
|
| 22 |
+
|
| 23 |
+
# Limpar logs anteriores
|
| 24 |
+
echo -e "${YELLOW}Limpando logs anteriores...${NC}"
|
| 25 |
+
rm -f *.log
|
| 26 |
+
rm -rf metrics/
|
| 27 |
+
mkdir -p metrics
|
| 28 |
+
mkdir -p results
|
| 29 |
+
|
| 30 |
+
# Verificar se os dados existem
|
| 31 |
+
if [ ! -d "data" ]; then
|
| 32 |
+
echo -e "${RED}Erro: Diretório 'data' não encontrado!${NC}"
|
| 33 |
+
echo -e "${YELLOW}Certifique-se de que os dados estão organizados em:${NC}"
|
| 34 |
+
echo " data/client_1/*.csv"
|
| 35 |
+
echo " data/client_2/*.csv"
|
| 36 |
+
echo " data/client_3/*.csv"
|
| 37 |
+
exit 1
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
# Função para verificar se o servidor está pronto
|
| 41 |
+
wait_for_server() {
|
| 42 |
+
echo -e "${YELLOW}Aguardando servidor iniciar...${NC}"
|
| 43 |
+
for i in {1..30}; do
|
| 44 |
+
if netstat -an | grep ":8080" | grep -q "LISTENING"; then
|
| 45 |
+
echo -e "${GREEN}Servidor pronto!${NC}"
|
| 46 |
+
return 0
|
| 47 |
+
fi
|
| 48 |
+
sleep 1
|
| 49 |
+
echo -n "."
|
| 50 |
+
done
|
| 51 |
+
echo -e "\n${RED}Timeout esperando servidor!${NC}"
|
| 52 |
+
return 1
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
# Iniciar servidor
|
| 56 |
+
echo -e "${BLUE}Iniciando servidor FL...${NC}"
|
| 57 |
+
#python server.py --strategy $STRATEGY --rounds $ROUNDS > server_$STRATEGY.log 2>&1 &
|
| 58 |
+
python server.py --strategy $STRATEGY --rounds $ROUNDS --prediction-length $PREDICTION_LENGTH > server_$STRATEGY.log 2>&1 &
|
| 59 |
+
SERVER_PID=$!
|
| 60 |
+
|
| 61 |
+
# Aguardar servidor
|
| 62 |
+
if ! wait_for_server; then
|
| 63 |
+
echo -e "${RED}Falha ao iniciar servidor. Verifique server_$STRATEGY.log${NC}"
|
| 64 |
+
exit 1
|
| 65 |
+
fi
|
| 66 |
+
|
| 67 |
+
# Iniciar clientes
|
| 68 |
+
echo -e "\n${BLUE}Iniciando 3 clientes FL...${NC}"
|
| 69 |
+
CLIENT_PIDS=()
|
| 70 |
+
|
| 71 |
+
for i in {1..3}; do
|
| 72 |
+
echo -e "${GREEN} → Iniciando cliente $i${NC}"
|
| 73 |
+
python client.py \
|
| 74 |
+
--client-id $i \
|
| 75 |
+
--prediction-length $PREDICTION_LENGTH \
|
| 76 |
+
> client_${i}_$STRATEGY.log 2>&1 &
|
| 77 |
+
CLIENT_PIDS+=($!)
|
| 78 |
+
sleep 2 # Pequeno delay entre clientes
|
| 79 |
+
done
|
| 80 |
+
|
| 81 |
+
# Monitorar progresso
|
| 82 |
+
echo -e "\n${BLUE}================================================${NC}"
|
| 83 |
+
echo -e "${BLUE} TREINAMENTO EM PROGRESSO${NC}"
|
| 84 |
+
echo -e "${BLUE}================================================${NC}"
|
| 85 |
+
echo -e "${YELLOW}Monitorando treinamento...${NC}"
|
| 86 |
+
echo -e "${YELLOW}Logs disponíveis em:${NC}"
|
| 87 |
+
echo -e " • Servidor: server_$STRATEGY.log"
|
| 88 |
+
echo -e " • Clientes: client_*_$STRATEGY.log"
|
| 89 |
+
echo -e " • Métricas: metrics/client_*/metrics_history.json"
|
| 90 |
+
|
| 91 |
+
# Função para mostrar progresso
|
| 92 |
+
show_progress() {
|
| 93 |
+
while kill -0 $SERVER_PID 2>/dev/null; do
|
| 94 |
+
if [ -f "server_$STRATEGY.log" ]; then
|
| 95 |
+
CURRENT_ROUND=$(grep -o "round [0-9]*:" server_$STRATEGY.log | tail -1 | grep -o "[0-9]*" || echo "0")
|
| 96 |
+
if [ ! -z "$CURRENT_ROUND" ] && [ "$CURRENT_ROUND" != "0" ]; then
|
| 97 |
+
echo -ne "\r${GREEN}Progresso: Rodada $CURRENT_ROUND de $ROUNDS${NC} "
|
| 98 |
+
fi
|
| 99 |
+
fi
|
| 100 |
+
sleep 2
|
| 101 |
+
done
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
# Mostrar progresso
|
| 105 |
+
show_progress
|
| 106 |
+
|
| 107 |
+
# Aguardar conclusão
|
| 108 |
+
echo -e "\n\n${YELLOW}Aguardando conclusão do treinamento...${NC}"
|
| 109 |
+
wait $SERVER_PID
|
| 110 |
+
SERVER_EXIT_CODE=$?
|
| 111 |
+
|
| 112 |
+
# Verificar se o servidor terminou com sucesso
|
| 113 |
+
if [ $SERVER_EXIT_CODE -eq 0 ]; then
|
| 114 |
+
echo -e "${GREEN}✓ Servidor concluído com sucesso!${NC}"
|
| 115 |
+
else
|
| 116 |
+
echo -e "${RED}✗ Servidor terminou com erro (código: $SERVER_EXIT_CODE)${NC}"
|
| 117 |
+
fi
|
| 118 |
+
|
| 119 |
+
# Aguardar clientes (com timeout)
|
| 120 |
+
echo -e "${YELLOW}Aguardando clientes finalizarem...${NC}"
|
| 121 |
+
TIMEOUT=30
|
| 122 |
+
for pid in ${CLIENT_PIDS[@]}; do
|
| 123 |
+
COUNT=0
|
| 124 |
+
while kill -0 $pid 2>/dev/null && [ $COUNT -lt $TIMEOUT ]; do
|
| 125 |
+
sleep 1
|
| 126 |
+
COUNT=$((COUNT + 1))
|
| 127 |
+
done
|
| 128 |
+
if [ $COUNT -ge $TIMEOUT ]; then
|
| 129 |
+
echo -e "${YELLOW} Cliente PID $pid demorou muito. Finalizando...${NC}"
|
| 130 |
+
kill $pid 2>/dev/null
|
| 131 |
+
fi
|
| 132 |
+
done
|
| 133 |
+
|
| 134 |
+
# Limpar processos órfãos
|
| 135 |
+
echo -e "${YELLOW}Limpando processos...${NC}"
|
| 136 |
+
pkill -f "python client.py" 2>/dev/null
|
| 137 |
+
pkill -f "python server.py" 2>/dev/null
|
| 138 |
+
|
| 139 |
+
# Executar análise
|
| 140 |
+
echo -e "\n${BLUE}================================================${NC}"
|
| 141 |
+
echo -e "${BLUE} EXECUTANDO ANÁLISE DOS RESULTADOS${NC}"
|
| 142 |
+
echo -e "${BLUE}================================================${NC}"
|
| 143 |
+
|
| 144 |
+
if [ -f "analysis_tool.py" ]; then
|
| 145 |
+
echo -e "${GREEN}Gerando visualizações e relatórios...${NC}"
|
| 146 |
+
python analysis_tool.py --results-dir results
|
| 147 |
+
|
| 148 |
+
if [ $? -eq 0 ]; then
|
| 149 |
+
echo -e "${GREEN}✓ Análise concluída com sucesso!${NC}"
|
| 150 |
+
else
|
| 151 |
+
echo -e "${RED}✗ Erro na análise dos resultados${NC}"
|
| 152 |
+
fi
|
| 153 |
+
else
|
| 154 |
+
echo -e "${YELLOW}Arquivo analysis_tool.py não encontrado. Pulando análise.${NC}"
|
| 155 |
+
fi
|
| 156 |
+
|
| 157 |
+
# Resumo final
|
| 158 |
+
echo -e "\n${BLUE}================================================${NC}"
|
| 159 |
+
echo -e "${BLUE} SIMULAÇÃO CONCLUÍDA${NC}"
|
| 160 |
+
echo -e "${BLUE}================================================${NC}"
|
| 161 |
+
|
| 162 |
+
# Verificar arquivos gerados
|
| 163 |
+
echo -e "${GREEN}Arquivos gerados:${NC}"
|
| 164 |
+
|
| 165 |
+
# Resultados
|
| 166 |
+
if [ -d "results" ]; then
|
| 167 |
+
echo -e "\n${YELLOW}Resultados em 'results/':${NC}"
|
| 168 |
+
ls -la results/*.pdf 2>/dev/null | awk '{print " • " $9}'
|
| 169 |
+
ls -la results/*.csv 2>/dev/null | awk '{print " • " $9}'
|
| 170 |
+
ls -la results/*.json 2>/dev/null | awk '{print " • " $9}'
|
| 171 |
+
fi
|
| 172 |
+
|
| 173 |
+
# Métricas dos clientes
|
| 174 |
+
if [ -d "metrics" ]; then
|
| 175 |
+
echo -e "\n${YELLOW}Métricas dos clientes em 'metrics/':${NC}"
|
| 176 |
+
for i in {1..3}; do
|
| 177 |
+
if [ -f "metrics/client_$i/metrics_history.json" ]; then
|
| 178 |
+
echo -e " • Cliente $i: metrics/client_$i/metrics_history.json"
|
| 179 |
+
fi
|
| 180 |
+
done
|
| 181 |
+
fi
|
| 182 |
+
|
| 183 |
+
# Logs
|
| 184 |
+
echo -e "\n${YELLOW}Logs de execução:${NC}"
|
| 185 |
+
ls -la *.log | awk '{print " • " $9 " (" $5 " bytes)"}'
|
| 186 |
+
|
| 187 |
+
# Estatísticas finais dos logs
|
| 188 |
+
echo -e "\n${BLUE}Estatísticas Finais:${NC}"
|
| 189 |
+
|
| 190 |
+
# Extrair perda final do servidor
|
| 191 |
+
if [ -f "server_$STRATEGY.log" ]; then
|
| 192 |
+
FINAL_LOSS=$(grep "Perda final de treino:" server_$STRATEGY.log | tail -1 | grep -o "[0-9.]*" | head -1)
|
| 193 |
+
if [ ! -z "$FINAL_LOSS" ]; then
|
| 194 |
+
echo -e " ${GREEN}• Perda final de treino (global): $FINAL_LOSS${NC}"
|
| 195 |
+
fi
|
| 196 |
+
|
| 197 |
+
FINAL_EVAL=$(grep "Perda final de validação:" server_$STRATEGY.log | tail -1 | grep -o "[0-9.]*" | head -1)
|
| 198 |
+
if [ ! -z "$FINAL_EVAL" ]; then
|
| 199 |
+
echo -e " ${GREEN}• Perda final de validação (global): $FINAL_EVAL${NC}"
|
| 200 |
+
fi
|
| 201 |
+
fi
|
| 202 |
+
|
| 203 |
+
# Extrair perdas finais dos clientes
|
| 204 |
+
for i in {1..3}; do
|
| 205 |
+
if [ -f "client_${i}_$STRATEGY.log" ]; then
|
| 206 |
+
CLIENT_LOSS=$(grep "Perda de validação:" client_${i}_$STRATEGY.log | tail -1 | grep -o "[0-9.]*" | head -1)
|
| 207 |
+
if [ ! -z "$CLIENT_LOSS" ]; then
|
| 208 |
+
echo -e " ${GREEN}• Cliente $i - Perda final: $CLIENT_LOSS${NC}"
|
| 209 |
+
fi
|
| 210 |
+
fi
|
| 211 |
+
done
|
| 212 |
+
|
| 213 |
+
echo -e "\n${BLUE}================================================${NC}"
|
| 214 |
+
echo -e "${GREEN}✓ Simulação completa!${NC}"
|
| 215 |
+
echo -e "${BLUE}================================================${NC}"
|
| 216 |
+
|
| 217 |
+
# Sugestões
|
| 218 |
+
echo -e "\n${YELLOW}Próximos passos sugeridos:${NC}"
|
| 219 |
+
echo -e " 1. Visualizar os gráficos em ${GREEN}results/*.pdf${NC}"
|
| 220 |
+
echo -e " 2. Analisar métricas detalhadas em ${GREEN}results/detailed_metrics_$STRATEGY.csv${NC}"
|
| 221 |
+
echo -e " 3. Revisar o relatório resumido em ${GREEN}results/summary_report.json${NC}"
|
| 222 |
+
echo -e " 4. Comparar com outras estratégias executando:"
|
| 223 |
+
echo -e " ${BLUE}./run_enhanced.sh fedadam $ROUNDS${NC}"
|
| 224 |
+
echo -e " ${BLUE}./run_enhanced.sh fedyogi $ROUNDS${NC}"
|
| 225 |
+
echo -e " ${BLUE}./run_enhanced.sh fedadagrad $ROUNDS${NC}"
|
| 226 |
+
|
| 227 |
+
exit $SERVER_EXIT_CODE
|
run_all_strategies.sh
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Script para executar todas as estratégias e gerar comparação final
|
| 4 |
+
|
| 5 |
+
# Cores para output
|
| 6 |
+
RED='\033[0;31m'
|
| 7 |
+
GREEN='\033[0;32m'
|
| 8 |
+
YELLOW='\033[1;33m'
|
| 9 |
+
BLUE='\033[0;34m'
|
| 10 |
+
PURPLE='\033[0;35m'
|
| 11 |
+
NC='\033[0m' # No Color
|
| 12 |
+
|
| 13 |
+
# Configurações
|
| 14 |
+
ROUNDS=${1:-15}
|
| 15 |
+
PREDICTION_LENGTH=${2:-10}
|
| 16 |
+
STRATEGIES=("fedavg" "fedadam" "fedyogi" "fedadagrad")
|
| 17 |
+
|
| 18 |
+
echo -e "${PURPLE}╔══════════════════════════════════════════════════════╗${NC}"
|
| 19 |
+
echo -e "${PURPLE}║ EXECUÇÃO COMPLETA - TODAS AS ESTRATÉGIAS FL ║${NC}"
|
| 20 |
+
echo -e "${PURPLE}╚══════════════════════════════════════════════════════╝${NC}"
|
| 21 |
+
echo -e "${GREEN}Rodadas: ${YELLOW}$ROUNDS${NC}"
|
| 22 |
+
echo -e "${GREEN}Tamanho da Previsão: ${YELLOW}$PREDICTION_LENGTH passos${NC}"
|
| 23 |
+
echo -e "${GREEN}Estratégias: ${YELLOW}${STRATEGIES[@]}${NC}"
|
| 24 |
+
echo -e "${PURPLE}════════════════════════════════════════════════════════${NC}\n"
|
| 25 |
+
|
| 26 |
+
# Criar diretório para resultados consolidados
|
| 27 |
+
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
| 28 |
+
RESULTS_DIR="results_comparison_$TIMESTAMP"
|
| 29 |
+
mkdir -p $RESULTS_DIR
|
| 30 |
+
|
| 31 |
+
# Log geral
|
| 32 |
+
MAIN_LOG="$RESULTS_DIR/execution_log.txt"
|
| 33 |
+
echo "Execução iniciada em: $(date)" > $MAIN_LOG
|
| 34 |
+
|
| 35 |
+
# Executar cada estratégia
|
| 36 |
+
for STRATEGY in "${STRATEGIES[@]}"; do
|
| 37 |
+
echo -e "\n${BLUE}════════════════════════════════════════════════════════${NC}"
|
| 38 |
+
echo -e "${BLUE} Executando estratégia: ${YELLOW}$STRATEGY${NC}"
|
| 39 |
+
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
|
| 40 |
+
|
| 41 |
+
# Registrar no log
|
| 42 |
+
echo -e "\n\n=== ESTRATÉGIA: $STRATEGY ===" >> $MAIN_LOG
|
| 43 |
+
echo "Início: $(date)" >> $MAIN_LOG
|
| 44 |
+
|
| 45 |
+
# Executar estratégia
|
| 46 |
+
./run.sh $STRATEGY $ROUNDS $PREDICTION_LENGTH
|
| 47 |
+
|
| 48 |
+
# Verificar sucesso
|
| 49 |
+
if [ $? -eq 0 ]; then
|
| 50 |
+
echo -e "${GREEN}✓ $STRATEGY concluída com sucesso!${NC}"
|
| 51 |
+
echo "Status: SUCESSO" >> $MAIN_LOG
|
| 52 |
+
else
|
| 53 |
+
echo -e "${RED}✗ Erro ao executar $STRATEGY${NC}"
|
| 54 |
+
echo "Status: ERRO" >> $MAIN_LOG
|
| 55 |
+
fi
|
| 56 |
+
|
| 57 |
+
echo "Fim: $(date)" >> $MAIN_LOG
|
| 58 |
+
|
| 59 |
+
# Copiar resultados para diretório consolidado
|
| 60 |
+
echo -e "${YELLOW}Copiando resultados...${NC}"
|
| 61 |
+
|
| 62 |
+
# Criar subdiretório para a estratégia
|
| 63 |
+
STRATEGY_DIR="$RESULTS_DIR/$STRATEGY"
|
| 64 |
+
mkdir -p $STRATEGY_DIR
|
| 65 |
+
|
| 66 |
+
# Copiar arquivos de resultados
|
| 67 |
+
if [ -d "results" ]; then
|
| 68 |
+
cp -r results/* $STRATEGY_DIR/ 2>/dev/null
|
| 69 |
+
fi
|
| 70 |
+
|
| 71 |
+
# Copiar métricas dos clientes
|
| 72 |
+
if [ -d "metrics" ]; then
|
| 73 |
+
cp -r metrics $STRATEGY_DIR/ 2>/dev/null
|
| 74 |
+
fi
|
| 75 |
+
|
| 76 |
+
# Copiar logs
|
| 77 |
+
cp *_$STRATEGY.log $STRATEGY_DIR/ 2>/dev/null
|
| 78 |
+
|
| 79 |
+
# Renomear arquivos para incluir estratégia
|
| 80 |
+
if [ -f "results/detailed_metrics_$STRATEGY.csv" ]; then
|
| 81 |
+
cp "results/detailed_metrics_$STRATEGY.csv" "$RESULTS_DIR/metrics_$STRATEGY.csv"
|
| 82 |
+
fi
|
| 83 |
+
|
| 84 |
+
# Aguardar antes da próxima estratégia
|
| 85 |
+
echo -e "${YELLOW}Aguardando 5 segundos antes da próxima estratégia...${NC}"
|
| 86 |
+
sleep 5
|
| 87 |
+
done
|
| 88 |
+
|
| 89 |
+
echo -e "\n${PURPLE}════════════════════════════════════════════════════════${NC}"
|
| 90 |
+
echo -e "${PURPLE} GERANDO ANÁLISE COMPARATIVA FINAL${NC}"
|
| 91 |
+
echo -e "${PURPLE}════════════════════════════════════════════════════════${NC}"
|
| 92 |
+
|
| 93 |
+
# Mover todos os CSVs de métricas para results/
|
| 94 |
+
echo -e "${YELLOW}Consolidando métricas...${NC}"
|
| 95 |
+
cp $RESULTS_DIR/metrics_*.csv results/ 2>/dev/null
|
| 96 |
+
|
| 97 |
+
# Executar análise comparativa final
|
| 98 |
+
echo -e "${GREEN}Executando análise comparativa...${NC}"
|
| 99 |
+
python analysis_tool.py --results-dir results
|
| 100 |
+
|
| 101 |
+
# Copiar análise comparativa para diretório consolidado
|
| 102 |
+
if [ -f "results/comparative_analysis.pdf" ]; then
|
| 103 |
+
cp results/comparative_analysis.pdf $RESULTS_DIR/
|
| 104 |
+
echo -e "${GREEN}✓ Análise comparativa gerada!${NC}"
|
| 105 |
+
fi
|
| 106 |
+
|
| 107 |
+
if [ -f "results/summary_report.json" ]; then
|
| 108 |
+
cp results/summary_report.json $RESULTS_DIR/
|
| 109 |
+
fi
|
| 110 |
+
|
| 111 |
+
# Criar relatório resumido em texto
|
| 112 |
+
SUMMARY_FILE="$RESULTS_DIR/summary.txt"
|
| 113 |
+
echo "════════════════════════════════════════════════════════" > $SUMMARY_FILE
|
| 114 |
+
echo " RESUMO DA COMPARAÇÃO DE ESTRATÉGIAS FL" >> $SUMMARY_FILE
|
| 115 |
+
echo "════════════════════════════════════════════════════════" >> $SUMMARY_FILE
|
| 116 |
+
echo "" >> $SUMMARY_FILE
|
| 117 |
+
echo "Data: $(date)" >> $SUMMARY_FILE
|
| 118 |
+
echo "Rodadas: $ROUNDS" >> $SUMMARY_FILE
|
| 119 |
+
echo "Tamanho da Previsão: $PREDICTION_LENGTH passos" >> $SUMMARY_FILE
|
| 120 |
+
echo "" >> $SUMMARY_FILE
|
| 121 |
+
echo "RESULTADOS POR ESTRATÉGIA:" >> $SUMMARY_FILE
|
| 122 |
+
echo "----------------------------" >> $SUMMARY_FILE
|
| 123 |
+
|
| 124 |
+
# Extrair métricas finais de cada estratégia
|
| 125 |
+
for STRATEGY in "${STRATEGIES[@]}"; do
|
| 126 |
+
echo "" >> $SUMMARY_FILE
|
| 127 |
+
echo "[$STRATEGY]" >> $SUMMARY_FILE
|
| 128 |
+
|
| 129 |
+
LOG_FILE="$STRATEGY_DIR/$STRATEGY/server_$STRATEGY.log"
|
| 130 |
+
if [ -f "$LOG_FILE" ]; then
|
| 131 |
+
FINAL_TRAIN=$(grep "Perda final de treino:" "$LOG_FILE" | tail -1 | grep -oE "[0-9]+\.[0-9]+" | head -1)
|
| 132 |
+
FINAL_VAL=$(grep "Perda final de validação:" "$LOG_FILE" | tail -1 | grep -oE "[0-9]+\.[0-9]+" | head -1)
|
| 133 |
+
IMPROVEMENT=$(grep "Melhoria no treino:" "$LOG_FILE" | tail -1 | grep -oE "[0-9]+\.[0-9]+%" | head -1)
|
| 134 |
+
|
| 135 |
+
echo " Perda Final (Treino): ${FINAL_TRAIN:-N/A}" >> $SUMMARY_FILE
|
| 136 |
+
echo " Perda Final (Validação): ${FINAL_VAL:-N/A}" >> $SUMMARY_FILE
|
| 137 |
+
echo " Melhoria: ${IMPROVEMENT:-N/A}" >> $SUMMARY_FILE
|
| 138 |
+
else
|
| 139 |
+
echo " Status: Dados não disponíveis" >> $SUMMARY_FILE
|
| 140 |
+
fi
|
| 141 |
+
done
|
| 142 |
+
|
| 143 |
+
echo "" >> $SUMMARY_FILE
|
| 144 |
+
echo "════════════════════════════════════════════════════════" >> $SUMMARY_FILE
|
| 145 |
+
|
| 146 |
+
# Exibir resumo no terminal
|
| 147 |
+
cat $SUMMARY_FILE
|
| 148 |
+
|
| 149 |
+
# Estatísticas finais
|
| 150 |
+
echo -e "\n${PURPLE}════════════════════════════════════════════════════════${NC}"
|
| 151 |
+
echo -e "${PURPLE} EXECUÇÃO COMPLETA${NC}"
|
| 152 |
+
echo -e "${PURPLE}════════════════════════════════════════════════════════${NC}"
|
| 153 |
+
|
| 154 |
+
echo -e "\n${GREEN}Resultados salvos em: ${YELLOW}$RESULTS_DIR/${NC}"
|
| 155 |
+
echo -e "${GREEN}Estrutura de arquivos:${NC}"
|
| 156 |
+
echo -e " ${YELLOW}$RESULTS_DIR/${NC}"
|
| 157 |
+
echo -e " ├── ${BLUE}summary.txt${NC} - Resumo geral"
|
| 158 |
+
echo -e " ├── ${BLUE}execution_log.txt${NC} - Log de execução"
|
| 159 |
+
echo -e " ├── ${BLUE}comparative_analysis.pdf${NC} - Análise comparativa"
|
| 160 |
+
echo -e " ├── ${BLUE}summary_report.json${NC} - Relatório detalhado"
|
| 161 |
+
|
| 162 |
+
for STRATEGY in "${STRATEGIES[@]}"; do
|
| 163 |
+
echo -e " ├── ${GREEN}$STRATEGY/${NC}"
|
| 164 |
+
echo -e " │ ├── Resultados específicos"
|
| 165 |
+
echo -e " │ ├── Métricas dos clientes"
|
| 166 |
+
echo -e " │ └── Logs de execução"
|
| 167 |
+
done
|
| 168 |
+
|
| 169 |
+
echo -e "\n${YELLOW}Visualizações disponíveis:${NC}"
|
| 170 |
+
echo -e " • ${GREEN}comparative_analysis.pdf${NC} - Comparação entre estratégias"
|
| 171 |
+
echo -e " • ${GREEN}client_evolution_analysis.pdf${NC} - Evolução dos clientes"
|
| 172 |
+
echo -e " • ${GREEN}performance_analysis_*.pdf${NC} - Análise por estratégia"
|
| 173 |
+
echo -e " • ${GREEN}convergence_analysis_*.pdf${NC} - Análise de convergência"
|
| 174 |
+
echo -e " • ${GREEN}heatmap_performance_*.pdf${NC} - Mapas de calor"
|
| 175 |
+
|
| 176 |
+
# Determinar melhor estratégia
|
| 177 |
+
echo -e "\n${PURPLE}════════════════════════════════════════════════════════${NC}"
|
| 178 |
+
echo -e "${PURPLE} MELHOR ESTRATÉGIA${NC}"
|
| 179 |
+
echo -e "${PURPLE}════════════════════════════════════════════════════════${NC}"
|
| 180 |
+
|
| 181 |
+
BEST_STRATEGY=""
|
| 182 |
+
BEST_LOSS=999999
|
| 183 |
+
|
| 184 |
+
for STRATEGY in "${STRATEGIES[@]}"; do
|
| 185 |
+
CSV_FILE="results/detailed_metrics_$STRATEGY.csv"
|
| 186 |
+
if [ -f "$CSV_FILE" ]; then
|
| 187 |
+
# Pegar última linha com fase 'eval' e coluna 'global_eval_loss'
|
| 188 |
+
FINAL_LOSS=$(tail -n 20 "$CSV_FILE" | grep "eval" | tail -1 | cut -d',' -f3)
|
| 189 |
+
|
| 190 |
+
if [ ! -z "$FINAL_LOSS" ]; then
|
| 191 |
+
# Comparar usando bc para números decimais
|
| 192 |
+
if (( $(echo "$FINAL_LOSS < $BEST_LOSS" | bc -l) )); then
|
| 193 |
+
BEST_LOSS=$FINAL_LOSS
|
| 194 |
+
BEST_STRATEGY=$STRATEGY
|
| 195 |
+
fi
|
| 196 |
+
fi
|
| 197 |
+
fi
|
| 198 |
+
done
|
| 199 |
+
|
| 200 |
+
if [ ! -z "$BEST_STRATEGY" ]; then
|
| 201 |
+
echo -e "${GREEN}Melhor estratégia: ${YELLOW}$BEST_STRATEGY${NC}"
|
| 202 |
+
echo -e "${GREEN}Perda final de validação: ${YELLOW}$BEST_LOSS${NC}"
|
| 203 |
+
else
|
| 204 |
+
echo -e "${YELLOW}Não foi possível determinar a melhor estratégia${NC}"
|
| 205 |
+
fi
|
| 206 |
+
|
| 207 |
+
echo -e "\n${BLUE}════════════════════════════════════════════════════════${NC}"
|
| 208 |
+
echo -e "${GREEN}✓ Comparação completa de todas as estratégias!${NC}"
|
| 209 |
+
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
|
| 210 |
+
|
| 211 |
+
# Fim
|
| 212 |
+
echo "Execução finalizada em: $(date)" >> $MAIN_LOG
|
| 213 |
+
exit 0
|
server.py
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import flwr as fl
|
| 3 |
+
import torch
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
from typing import List, Tuple, Dict, Optional
|
| 9 |
+
from flwr.common import Metrics, ndarrays_to_parameters
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
import json
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
|
| 14 |
+
from utils import Net
|
| 15 |
+
|
| 16 |
+
# Configurações de visualização
|
| 17 |
+
plt.style.use('seaborn-v0_8-darkgrid')
|
| 18 |
+
sns.set_palette("husl")
|
| 19 |
+
|
| 20 |
+
# Dicionário de estratégias disponíveis
|
| 21 |
+
STRATEGIES = {
|
| 22 |
+
"fedavg": fl.server.strategy.FedAvg,
|
| 23 |
+
"fedadam": fl.server.strategy.FedAdam,
|
| 24 |
+
"fedyogi": fl.server.strategy.FedYogi,
|
| 25 |
+
"fedadagrad": fl.server.strategy.FedAdagrad,
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
class MetricsCollector:
|
| 29 |
+
"""Coleta e organiza métricas de treinamento e validação."""
|
| 30 |
+
|
| 31 |
+
def __init__(self, strategy_name):
|
| 32 |
+
self.strategy_name = strategy_name
|
| 33 |
+
self.train_metrics = {
|
| 34 |
+
"rounds": [],
|
| 35 |
+
"global_train_loss": [],
|
| 36 |
+
"client_1_train_loss": [],
|
| 37 |
+
"client_2_train_loss": [],
|
| 38 |
+
"client_3_train_loss": []
|
| 39 |
+
}
|
| 40 |
+
self.eval_metrics = {
|
| 41 |
+
"rounds": [],
|
| 42 |
+
"global_eval_loss": [],
|
| 43 |
+
"client_1_eval_loss": [],
|
| 44 |
+
"client_2_eval_loss": [],
|
| 45 |
+
"client_3_eval_loss": []
|
| 46 |
+
}
|
| 47 |
+
self.convergence_metrics = {
|
| 48 |
+
"rounds": [],
|
| 49 |
+
"loss_variance": [],
|
| 50 |
+
"loss_std": [],
|
| 51 |
+
"max_min_diff": []
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
def add_train_round(self, round_num, metrics):
|
| 55 |
+
"""Adiciona métricas de uma rodada de treinamento."""
|
| 56 |
+
self.train_metrics["rounds"].append(round_num)
|
| 57 |
+
for key, value in metrics.items():
|
| 58 |
+
if key in self.train_metrics:
|
| 59 |
+
self.train_metrics[key].append(value)
|
| 60 |
+
|
| 61 |
+
def add_eval_round(self, round_num, metrics):
|
| 62 |
+
"""Adiciona métricas de uma rodada de avaliação."""
|
| 63 |
+
self.eval_metrics["rounds"].append(round_num)
|
| 64 |
+
for key, value in metrics.items():
|
| 65 |
+
if key in self.eval_metrics:
|
| 66 |
+
self.eval_metrics[key].append(value)
|
| 67 |
+
|
| 68 |
+
def calculate_convergence_metrics(self, client_losses):
|
| 69 |
+
"""Calcula métricas de convergência entre clientes."""
|
| 70 |
+
if len(client_losses) > 0:
|
| 71 |
+
variance = np.var(client_losses)
|
| 72 |
+
std_dev = np.std(client_losses)
|
| 73 |
+
max_min_diff = max(client_losses) - min(client_losses)
|
| 74 |
+
return variance, std_dev, max_min_diff
|
| 75 |
+
return 0, 0, 0
|
| 76 |
+
|
| 77 |
+
def weighted_average(metrics: List[Tuple[int, Metrics]]) -> Dict[str, any]:
|
| 78 |
+
"""Função de agregação para métricas de avaliação."""
|
| 79 |
+
individual_losses = {}
|
| 80 |
+
|
| 81 |
+
for num_examples, m in metrics:
|
| 82 |
+
client_id = m.get("client_id", 0)
|
| 83 |
+
if client_id > 0:
|
| 84 |
+
individual_losses[f"client_{client_id}_eval_loss"] = m["loss"]
|
| 85 |
+
|
| 86 |
+
# Calcula média ponderada
|
| 87 |
+
losses = [num_examples * m["loss"] for num_examples, m in metrics]
|
| 88 |
+
examples = [num_examples for num_examples, _ in metrics]
|
| 89 |
+
avg_loss = sum(losses) / sum(examples) if sum(examples) > 0 else 0
|
| 90 |
+
|
| 91 |
+
aggregated_metrics = {"global_eval_loss": avg_loss}
|
| 92 |
+
aggregated_metrics.update(individual_losses)
|
| 93 |
+
|
| 94 |
+
return aggregated_metrics
|
| 95 |
+
|
| 96 |
+
def fit_metrics_aggregator(metrics: List[Tuple[int, Metrics]]) -> Dict[str, float]:
|
| 97 |
+
"""Agrega métricas de treinamento."""
|
| 98 |
+
individual_losses = {}
|
| 99 |
+
|
| 100 |
+
for num_examples, m in metrics:
|
| 101 |
+
client_id = m.get("client_id", 0)
|
| 102 |
+
if client_id > 0:
|
| 103 |
+
individual_losses[f"client_{client_id}_train_loss"] = m["train_loss"]
|
| 104 |
+
|
| 105 |
+
losses = [num_examples * m["train_loss"] for num_examples, m in metrics]
|
| 106 |
+
examples = [num_examples for num_examples, _ in metrics]
|
| 107 |
+
|
| 108 |
+
avg_loss = sum(losses) / sum(examples) if sum(examples) > 0 else 0
|
| 109 |
+
|
| 110 |
+
aggregated_metrics = {"global_train_loss": avg_loss}
|
| 111 |
+
aggregated_metrics.update(individual_losses)
|
| 112 |
+
|
| 113 |
+
return aggregated_metrics
|
| 114 |
+
|
| 115 |
+
def create_visualizations(collector: MetricsCollector, output_dir: Path):
|
| 116 |
+
"""Cria todas as visualizações de desempenho."""
|
| 117 |
+
|
| 118 |
+
# 1. Gráfico de Comparação Geral (Treino vs Validação - Global e Clientes)
|
| 119 |
+
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
|
| 120 |
+
|
| 121 |
+
# Subplot 1: Desempenho Global
|
| 122 |
+
ax1 = axes[0, 0]
|
| 123 |
+
rounds = collector.train_metrics["rounds"]
|
| 124 |
+
ax1.plot(rounds, collector.train_metrics["global_train_loss"],
|
| 125 |
+
'b-', marker='s', label='Treino Global', linewidth=2)
|
| 126 |
+
ax1.plot(collector.eval_metrics["rounds"], collector.eval_metrics["global_eval_loss"],
|
| 127 |
+
'r-', marker='o', label='Validação Global', linewidth=2)
|
| 128 |
+
ax1.set_title('Desempenho do Modelo Global', fontsize=14, fontweight='bold')
|
| 129 |
+
ax1.set_xlabel('Rodada')
|
| 130 |
+
ax1.set_ylabel('Perda (MSE)')
|
| 131 |
+
ax1.legend()
|
| 132 |
+
ax1.grid(True, alpha=0.3)
|
| 133 |
+
|
| 134 |
+
# Subplot 2: Comparação entre Clientes (Treino)
|
| 135 |
+
ax2 = axes[0, 1]
|
| 136 |
+
colors = ['#2E7D32', '#1565C0', '#E65100']
|
| 137 |
+
for i in range(1, 4):
|
| 138 |
+
key = f"client_{i}_train_loss"
|
| 139 |
+
if key in collector.train_metrics and collector.train_metrics[key]:
|
| 140 |
+
ax2.plot(rounds, collector.train_metrics[key],
|
| 141 |
+
marker='o', label=f'Cliente {i}', color=colors[i-1], linewidth=1.5)
|
| 142 |
+
ax2.plot(rounds, collector.train_metrics["global_train_loss"],
|
| 143 |
+
'k--', label='Média Global', linewidth=2, alpha=0.7)
|
| 144 |
+
ax2.set_title('Perda de Treinamento por Cliente', fontsize=14, fontweight='bold')
|
| 145 |
+
ax2.set_xlabel('Rodada')
|
| 146 |
+
ax2.set_ylabel('Perda de Treino (MSE)')
|
| 147 |
+
ax2.legend()
|
| 148 |
+
ax2.grid(True, alpha=0.3)
|
| 149 |
+
|
| 150 |
+
# Subplot 3: Comparação entre Clientes (Validação)
|
| 151 |
+
ax3 = axes[1, 0]
|
| 152 |
+
for i in range(1, 4):
|
| 153 |
+
key = f"client_{i}_eval_loss"
|
| 154 |
+
if key in collector.eval_metrics and collector.eval_metrics[key]:
|
| 155 |
+
ax3.plot(collector.eval_metrics["rounds"], collector.eval_metrics[key],
|
| 156 |
+
marker='s', label=f'Cliente {i}', color=colors[i-1], linewidth=1.5)
|
| 157 |
+
ax3.plot(collector.eval_metrics["rounds"], collector.eval_metrics["global_eval_loss"],
|
| 158 |
+
'k--', label='Média Global', linewidth=2, alpha=0.7)
|
| 159 |
+
ax3.set_title('Perda de Validação por Cliente', fontsize=14, fontweight='bold')
|
| 160 |
+
ax3.set_xlabel('Rodada')
|
| 161 |
+
ax3.set_ylabel('Perda de Validação (MSE)')
|
| 162 |
+
ax3.legend()
|
| 163 |
+
ax3.grid(True, alpha=0.3)
|
| 164 |
+
|
| 165 |
+
# Subplot 4: Taxa de Melhoria
|
| 166 |
+
ax4 = axes[1, 1]
|
| 167 |
+
if len(rounds) > 1:
|
| 168 |
+
train_improvement = np.diff(collector.train_metrics["global_train_loss"])
|
| 169 |
+
eval_improvement = np.diff(collector.eval_metrics["global_eval_loss"])
|
| 170 |
+
ax4.plot(rounds[1:], train_improvement, 'g-', marker='v', label='Δ Treino', linewidth=1.5)
|
| 171 |
+
ax4.plot(collector.eval_metrics["rounds"][1:], eval_improvement,
|
| 172 |
+
'm-', marker='^', label='Δ Validação', linewidth=1.5)
|
| 173 |
+
ax4.axhline(y=0, color='k', linestyle='--', alpha=0.5)
|
| 174 |
+
ax4.set_title('Taxa de Melhoria (Δ Perda)', fontsize=14, fontweight='bold')
|
| 175 |
+
ax4.set_xlabel('Rodada')
|
| 176 |
+
ax4.set_ylabel('Mudança na Perda')
|
| 177 |
+
ax4.legend()
|
| 178 |
+
ax4.grid(True, alpha=0.3)
|
| 179 |
+
|
| 180 |
+
plt.suptitle(f'Análise de Desempenho - Estratégia: {collector.strategy_name.upper()}',
|
| 181 |
+
fontsize=16, fontweight='bold')
|
| 182 |
+
plt.tight_layout()
|
| 183 |
+
plt.savefig(output_dir / f'performance_analysis_{collector.strategy_name}.pdf', dpi=300, bbox_inches='tight')
|
| 184 |
+
plt.close()
|
| 185 |
+
|
| 186 |
+
# 2. Gráfico de Convergência e Heterogeneidade
|
| 187 |
+
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
|
| 188 |
+
|
| 189 |
+
# Calcular métricas de convergência
|
| 190 |
+
for round_idx, round_num in enumerate(rounds):
|
| 191 |
+
client_losses = []
|
| 192 |
+
for i in range(1, 4):
|
| 193 |
+
key = f"client_{i}_eval_loss"
|
| 194 |
+
if key in collector.eval_metrics and round_idx < len(collector.eval_metrics[key]):
|
| 195 |
+
client_losses.append(collector.eval_metrics[key][round_idx])
|
| 196 |
+
|
| 197 |
+
if client_losses:
|
| 198 |
+
var, std, diff = collector.calculate_convergence_metrics(client_losses)
|
| 199 |
+
collector.convergence_metrics["rounds"].append(round_num)
|
| 200 |
+
collector.convergence_metrics["loss_variance"].append(var)
|
| 201 |
+
collector.convergence_metrics["loss_std"].append(std)
|
| 202 |
+
collector.convergence_metrics["max_min_diff"].append(diff)
|
| 203 |
+
|
| 204 |
+
# Subplot 1: Variância entre clientes
|
| 205 |
+
ax1 = axes[0]
|
| 206 |
+
ax1.plot(collector.convergence_metrics["rounds"],
|
| 207 |
+
collector.convergence_metrics["loss_variance"],
|
| 208 |
+
'b-', marker='o', linewidth=2)
|
| 209 |
+
ax1.fill_between(collector.convergence_metrics["rounds"],
|
| 210 |
+
collector.convergence_metrics["loss_variance"],
|
| 211 |
+
alpha=0.3)
|
| 212 |
+
ax1.set_title('Variância da Perda entre Clientes', fontsize=14, fontweight='bold')
|
| 213 |
+
ax1.set_xlabel('Rodada')
|
| 214 |
+
ax1.set_ylabel('Variância')
|
| 215 |
+
ax1.grid(True, alpha=0.3)
|
| 216 |
+
|
| 217 |
+
# Subplot 2: Desvio padrão
|
| 218 |
+
ax2 = axes[1]
|
| 219 |
+
ax2.plot(collector.convergence_metrics["rounds"],
|
| 220 |
+
collector.convergence_metrics["loss_std"],
|
| 221 |
+
'g-', marker='s', linewidth=2)
|
| 222 |
+
ax2.fill_between(collector.convergence_metrics["rounds"],
|
| 223 |
+
collector.convergence_metrics["loss_std"],
|
| 224 |
+
alpha=0.3, color='green')
|
| 225 |
+
ax2.set_title('Desvio Padrão da Perda entre Clientes', fontsize=14, fontweight='bold')
|
| 226 |
+
ax2.set_xlabel('Rodada')
|
| 227 |
+
ax2.set_ylabel('Desvio Padrão')
|
| 228 |
+
ax2.grid(True, alpha=0.3)
|
| 229 |
+
|
| 230 |
+
# Subplot 3: Diferença máx-mín
|
| 231 |
+
ax3 = axes[2]
|
| 232 |
+
ax3.plot(collector.convergence_metrics["rounds"],
|
| 233 |
+
collector.convergence_metrics["max_min_diff"],
|
| 234 |
+
'r-', marker='^', linewidth=2)
|
| 235 |
+
ax3.fill_between(collector.convergence_metrics["rounds"],
|
| 236 |
+
collector.convergence_metrics["max_min_diff"],
|
| 237 |
+
alpha=0.3, color='red')
|
| 238 |
+
ax3.set_title('Diferença Máx-Mín entre Clientes', fontsize=14, fontweight='bold')
|
| 239 |
+
ax3.set_xlabel('Rodada')
|
| 240 |
+
ax3.set_ylabel('Diferença')
|
| 241 |
+
ax3.grid(True, alpha=0.3)
|
| 242 |
+
|
| 243 |
+
plt.suptitle(f'Análise de Convergência e Heterogeneidade - {collector.strategy_name.upper()}',
|
| 244 |
+
fontsize=16, fontweight='bold')
|
| 245 |
+
plt.tight_layout()
|
| 246 |
+
plt.savefig(output_dir / f'convergence_analysis_{collector.strategy_name}.pdf', dpi=300, bbox_inches='tight')
|
| 247 |
+
plt.close()
|
| 248 |
+
|
| 249 |
+
# 3. Heatmap de desempenho por cliente ao longo do tempo
|
| 250 |
+
fig, ax = plt.subplots(figsize=(12, 6))
|
| 251 |
+
|
| 252 |
+
# Preparar dados para heatmap
|
| 253 |
+
heatmap_data = []
|
| 254 |
+
for i in range(1, 4):
|
| 255 |
+
key = f"client_{i}_eval_loss"
|
| 256 |
+
if key in collector.eval_metrics:
|
| 257 |
+
heatmap_data.append(collector.eval_metrics[key])
|
| 258 |
+
|
| 259 |
+
if heatmap_data:
|
| 260 |
+
heatmap_array = np.array(heatmap_data)
|
| 261 |
+
im = ax.imshow(heatmap_array, aspect='auto', cmap='RdYlGn_r')
|
| 262 |
+
|
| 263 |
+
ax.set_xticks(range(len(collector.eval_metrics["rounds"])))
|
| 264 |
+
ax.set_xticklabels(collector.eval_metrics["rounds"])
|
| 265 |
+
ax.set_yticks(range(3))
|
| 266 |
+
ax.set_yticklabels([f'Cliente {i}' for i in range(1, 4)])
|
| 267 |
+
|
| 268 |
+
ax.set_xlabel('Rodada', fontsize=12)
|
| 269 |
+
ax.set_title(f'Mapa de Calor - Perda de Validação por Cliente - {collector.strategy_name.upper()}',
|
| 270 |
+
fontsize=14, fontweight='bold')
|
| 271 |
+
|
| 272 |
+
# Adicionar valores nas células
|
| 273 |
+
for i in range(3):
|
| 274 |
+
for j in range(len(collector.eval_metrics["rounds"])):
|
| 275 |
+
if j < len(heatmap_data[i]):
|
| 276 |
+
text = ax.text(j, i, f'{heatmap_data[i][j]:.4f}',
|
| 277 |
+
ha="center", va="center", color="black", fontsize=8)
|
| 278 |
+
|
| 279 |
+
plt.colorbar(im, ax=ax, label='Perda (MSE)')
|
| 280 |
+
|
| 281 |
+
plt.tight_layout()
|
| 282 |
+
plt.savefig(output_dir / f'heatmap_performance_{collector.strategy_name}.pdf', dpi=300, bbox_inches='tight')
|
| 283 |
+
plt.close()
|
| 284 |
+
|
| 285 |
+
print(f"Visualizações salvas em {output_dir}")
|
| 286 |
+
|
| 287 |
+
def save_detailed_metrics(collector: MetricsCollector, output_dir: Path):
|
| 288 |
+
"""Salva métricas detalhadas em diferentes formatos."""
|
| 289 |
+
|
| 290 |
+
# 1. CSV com todas as métricas
|
| 291 |
+
all_metrics = pd.DataFrame()
|
| 292 |
+
|
| 293 |
+
# Adicionar métricas de treino
|
| 294 |
+
if collector.train_metrics["rounds"]:
|
| 295 |
+
train_df = pd.DataFrame(collector.train_metrics)
|
| 296 |
+
train_df['phase'] = 'train'
|
| 297 |
+
all_metrics = pd.concat([all_metrics, train_df], ignore_index=True)
|
| 298 |
+
|
| 299 |
+
# Adicionar métricas de validação
|
| 300 |
+
if collector.eval_metrics["rounds"]:
|
| 301 |
+
eval_df = pd.DataFrame(collector.eval_metrics)
|
| 302 |
+
eval_df['phase'] = 'eval'
|
| 303 |
+
all_metrics = pd.concat([all_metrics, eval_df], ignore_index=True)
|
| 304 |
+
|
| 305 |
+
# Salvar CSV detalhado
|
| 306 |
+
csv_file = output_dir / f'detailed_metrics_{collector.strategy_name}.csv'
|
| 307 |
+
all_metrics.to_csv(csv_file, index=False)
|
| 308 |
+
print(f"Métricas detalhadas salvas em {csv_file}")
|
| 309 |
+
|
| 310 |
+
# 2. JSON com análise estatística
|
| 311 |
+
stats = {
|
| 312 |
+
"strategy": collector.strategy_name,
|
| 313 |
+
"total_rounds": len(collector.train_metrics["rounds"]),
|
| 314 |
+
"final_global_train_loss": collector.train_metrics["global_train_loss"][-1] if collector.train_metrics["global_train_loss"] else None,
|
| 315 |
+
"final_global_eval_loss": collector.eval_metrics["global_eval_loss"][-1] if collector.eval_metrics["global_eval_loss"] else None,
|
| 316 |
+
"train_improvement": (collector.train_metrics["global_train_loss"][0] - collector.train_metrics["global_train_loss"][-1]) if len(collector.train_metrics["global_train_loss"]) > 1 else 0,
|
| 317 |
+
"eval_improvement": (collector.eval_metrics["global_eval_loss"][0] - collector.eval_metrics["global_eval_loss"][-1]) if len(collector.eval_metrics["global_eval_loss"]) > 1 else 0,
|
| 318 |
+
"convergence_metrics": collector.convergence_metrics,
|
| 319 |
+
"timestamp": datetime.now().isoformat()
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
json_file = output_dir / f'analysis_{collector.strategy_name}.json'
|
| 323 |
+
with open(json_file, 'w') as f:
|
| 324 |
+
json.dump(stats, f, indent=2)
|
| 325 |
+
print(f"Análise estatística salva em {json_file}")
|
| 326 |
+
|
| 327 |
+
def main():
|
| 328 |
+
parser = argparse.ArgumentParser(description="Flower Server Enhanced")
|
| 329 |
+
parser.add_argument(
|
| 330 |
+
"--strategy", type=str, choices=list(STRATEGIES.keys()), default="fedavg",
|
| 331 |
+
help="Estratégia de agregação a ser usada."
|
| 332 |
+
)
|
| 333 |
+
parser.add_argument(
|
| 334 |
+
"--rounds", type=int, default=10, help="Número de rodadas de FL."
|
| 335 |
+
)
|
| 336 |
+
parser.add_argument(
|
| 337 |
+
"--min-clients", type=int, default=3, help="Número mínimo de clientes."
|
| 338 |
+
)
|
| 339 |
+
parser.add_argument(
|
| 340 |
+
"--prediction-length", type=int, default=10, help="Tamanho da previsão."
|
| 341 |
+
)
|
| 342 |
+
args = parser.parse_args()
|
| 343 |
+
|
| 344 |
+
print(f"Tamanho da Previsão: {args.prediction_length}")
|
| 345 |
+
# Cria uma instância do modelo no servidor para obter os parâmetros iniciais
|
| 346 |
+
# Os valores de input/hidden devem ser os mesmos do cliente
|
| 347 |
+
net = Net(input_size=6, hidden_size=50, output_size=args.prediction_length)
|
| 348 |
+
initial_parameters = [val.cpu().numpy() for _, val in net.state_dict().items()]
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
print(f"\n{'='*60}")
|
| 352 |
+
print(f"SERVIDOR DE APRENDIZADO FEDERADO")
|
| 353 |
+
print(f"{'='*60}")
|
| 354 |
+
print(f"Estratégia: {args.strategy.upper()}")
|
| 355 |
+
print(f"Rodadas: {args.rounds}")
|
| 356 |
+
print(f"Clientes mínimos: {args.min_clients}")
|
| 357 |
+
print(f"{'='*60}\n")
|
| 358 |
+
|
| 359 |
+
# Cria diretórios de saída
|
| 360 |
+
output_dir = Path("results")
|
| 361 |
+
output_dir.mkdir(exist_ok=True)
|
| 362 |
+
|
| 363 |
+
# Inicializa coletor de métricas
|
| 364 |
+
collector = MetricsCollector(args.strategy)
|
| 365 |
+
|
| 366 |
+
# Função customizada de agregação que também coleta métricas
|
| 367 |
+
def enhanced_weighted_average(metrics: List[Tuple[int, Metrics]]) -> Dict[str, any]:
|
| 368 |
+
result = weighted_average(metrics)
|
| 369 |
+
# Coletar métricas para visualização
|
| 370 |
+
round_num = max([m.get("round", 0) for _, m in metrics])
|
| 371 |
+
if round_num > 0:
|
| 372 |
+
collector.add_eval_round(round_num, result)
|
| 373 |
+
return result
|
| 374 |
+
|
| 375 |
+
def enhanced_fit_aggregator(metrics: List[Tuple[int, Metrics]]) -> Dict[str, float]:
|
| 376 |
+
result = fit_metrics_aggregator(metrics)
|
| 377 |
+
# Coletar métricas para visualização
|
| 378 |
+
round_num = max([m.get("round", 0) for _, m in metrics])
|
| 379 |
+
if round_num > 0:
|
| 380 |
+
collector.add_train_round(round_num, result)
|
| 381 |
+
return result
|
| 382 |
+
|
| 383 |
+
# Cria a estratégia com as funções de agregação aprimoradas
|
| 384 |
+
strategy_params = {
|
| 385 |
+
"min_fit_clients": args.min_clients,
|
| 386 |
+
"min_available_clients": args.min_clients,
|
| 387 |
+
"evaluate_metrics_aggregation_fn": enhanced_weighted_average,
|
| 388 |
+
"fit_metrics_aggregation_fn": enhanced_fit_aggregator,
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
if args.strategy != "fedavg":
|
| 392 |
+
strategy_params["initial_parameters"] = ndarrays_to_parameters(initial_parameters)
|
| 393 |
+
|
| 394 |
+
strategy = STRATEGIES[args.strategy](**strategy_params)
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
# Inicia o servidor
|
| 398 |
+
print("Iniciando servidor FL...")
|
| 399 |
+
history = fl.server.start_server(
|
| 400 |
+
server_address="0.0.0.0:8080",
|
| 401 |
+
config=fl.server.ServerConfig(num_rounds=args.rounds),
|
| 402 |
+
strategy=strategy,
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
print("\n" + "="*60)
|
| 406 |
+
print("TREINAMENTO CONCLUÍDO - GERANDO ANÁLISES")
|
| 407 |
+
print("="*60)
|
| 408 |
+
|
| 409 |
+
# Criar visualizações
|
| 410 |
+
create_visualizations(collector, output_dir)
|
| 411 |
+
|
| 412 |
+
# Salvar métricas detalhadas
|
| 413 |
+
save_detailed_metrics(collector, output_dir)
|
| 414 |
+
|
| 415 |
+
# Análise final
|
| 416 |
+
print("\n" + "="*60)
|
| 417 |
+
print("RESUMO DO TREINAMENTO")
|
| 418 |
+
print("="*60)
|
| 419 |
+
|
| 420 |
+
if collector.train_metrics["global_train_loss"]:
|
| 421 |
+
initial_loss = collector.train_metrics["global_train_loss"][0]
|
| 422 |
+
final_loss = collector.train_metrics["global_train_loss"][-1]
|
| 423 |
+
improvement = ((initial_loss - final_loss) / initial_loss) * 100
|
| 424 |
+
|
| 425 |
+
print(f"Perda inicial de treino: {initial_loss:.6f}")
|
| 426 |
+
print(f"Perda final de treino: {final_loss:.6f}")
|
| 427 |
+
print(f"Melhoria no treino: {improvement:.2f}%")
|
| 428 |
+
|
| 429 |
+
if collector.eval_metrics["global_eval_loss"]:
|
| 430 |
+
initial_eval = collector.eval_metrics["global_eval_loss"][0]
|
| 431 |
+
final_eval = collector.eval_metrics["global_eval_loss"][-1]
|
| 432 |
+
eval_improvement = ((initial_eval - final_eval) / initial_eval) * 100
|
| 433 |
+
|
| 434 |
+
print(f"\nPerda inicial de validação: {initial_eval:.6f}")
|
| 435 |
+
print(f"Perda final de validação: {final_eval:.6f}")
|
| 436 |
+
print(f"Melhoria na validação: {eval_improvement:.2f}%")
|
| 437 |
+
|
| 438 |
+
# Análise de convergência
|
| 439 |
+
if collector.convergence_metrics["loss_std"]:
|
| 440 |
+
final_std = collector.convergence_metrics["loss_std"][-1]
|
| 441 |
+
print(f"\nDesvio padrão final entre clientes: {final_std:.6f}")
|
| 442 |
+
print(f"Convergência: {'Boa' if final_std < 0.01 else 'Moderada' if final_std < 0.05 else 'Baixa'}")
|
| 443 |
+
|
| 444 |
+
print("\n" + "="*60)
|
| 445 |
+
print(f"Resultados salvos em: {output_dir}")
|
| 446 |
+
print("="*60)
|
| 447 |
+
|
| 448 |
+
if __name__ == "__main__":
|
| 449 |
+
main()
|
server_fedadagrad.log
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[93mWARNING [0m: DEPRECATED FEATURE: flwr.server.start_server() is deprecated.
|
| 2 |
+
Instead, use the `flower-superlink` CLI command to start a SuperLink as shown below:
|
| 3 |
+
|
| 4 |
+
$ flower-superlink --insecure
|
| 5 |
+
|
| 6 |
+
To view usage and all available options, run:
|
| 7 |
+
|
| 8 |
+
$ flower-superlink --help
|
| 9 |
+
|
| 10 |
+
Using `start_server()` is deprecated.
|
| 11 |
+
|
| 12 |
+
This is a deprecated feature. It will be removed
|
| 13 |
+
entirely in future versions of Flower.
|
| 14 |
+
|
| 15 |
+
[92mINFO [0m: Starting Flower server, config: num_rounds=10, no round_timeout
|
| 16 |
+
[92mINFO [0m: Flower ECE: gRPC server running (10 rounds), SSL is disabled
|
| 17 |
+
[92mINFO [0m: [INIT]
|
| 18 |
+
[92mINFO [0m: Using initial global parameters provided by strategy
|
| 19 |
+
[92mINFO [0m: Starting evaluation of initial global parameters
|
| 20 |
+
[92mINFO [0m: Evaluation returned no results (`None`)
|
| 21 |
+
[92mINFO [0m:
|
| 22 |
+
[92mINFO [0m: [ROUND 1]
|
| 23 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 24 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 25 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 26 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 27 |
+
[92mINFO [0m:
|
| 28 |
+
[92mINFO [0m: [ROUND 2]
|
| 29 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 30 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 31 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 32 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 33 |
+
[92mINFO [0m:
|
| 34 |
+
[92mINFO [0m: [ROUND 3]
|
| 35 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 36 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 37 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 38 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 39 |
+
[92mINFO [0m:
|
| 40 |
+
[92mINFO [0m: [ROUND 4]
|
| 41 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 42 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 43 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 44 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 45 |
+
[92mINFO [0m:
|
| 46 |
+
[92mINFO [0m: [ROUND 5]
|
| 47 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 48 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 49 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 50 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 51 |
+
[92mINFO [0m:
|
| 52 |
+
[92mINFO [0m: [ROUND 6]
|
| 53 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 54 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 55 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 56 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 57 |
+
[92mINFO [0m:
|
| 58 |
+
[92mINFO [0m: [ROUND 7]
|
| 59 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 60 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 61 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 62 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 63 |
+
[92mINFO [0m:
|
| 64 |
+
[92mINFO [0m: [ROUND 8]
|
| 65 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 66 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 67 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 68 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 69 |
+
[92mINFO [0m:
|
| 70 |
+
[92mINFO [0m: [ROUND 9]
|
| 71 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 72 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 73 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 74 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 75 |
+
[92mINFO [0m:
|
| 76 |
+
[92mINFO [0m: [ROUND 10]
|
| 77 |
+
[92mINFO [0m: configure_fit: strategy sampled 3 clients (out of 3)
|
| 78 |
+
[92mINFO [0m: aggregate_fit: received 3 results and 0 failures
|
| 79 |
+
[92mINFO [0m: configure_evaluate: strategy sampled 3 clients (out of 3)
|
| 80 |
+
[92mINFO [0m: aggregate_evaluate: received 3 results and 0 failures
|
| 81 |
+
[92mINFO [0m:
|
| 82 |
+
[92mINFO [0m: [SUMMARY]
|
| 83 |
+
[92mINFO [0m: Run finished 10 round(s) in 146.09s
|
| 84 |
+
[92mINFO [0m: History (loss, distributed):
|
| 85 |
+
[92mINFO [0m: round 1: 9.925077960899166
|
| 86 |
+
[92mINFO [0m: round 2: 1.3836319349268043
|
| 87 |
+
[92mINFO [0m: round 3: 0.29234505289901225
|
| 88 |
+
[92mINFO [0m: round 4: 0.49308163151779894
|
| 89 |
+
[92mINFO [0m: round 5: 0.0446630789378426
|
| 90 |
+
[92mINFO [0m: round 6: 0.284914885456677
|
| 91 |
+
[92mINFO [0m: round 7: 0.058860671314457845
|
| 92 |
+
[92mINFO [0m: round 8: 0.07942846971222137
|
| 93 |
+
[92mINFO [0m: round 9: 0.037627383500482725
|
| 94 |
+
[92mINFO [0m: round 10: 0.024166644675821698
|
| 95 |
+
[92mINFO [0m: History (metrics, distributed, fit):
|
| 96 |
+
[92mINFO [0m: {'client_1_train_loss': [(1, 0.011821450406897624),
|
| 97 |
+
[92mINFO [0m: (2, 9.19087529878669),
|
| 98 |
+
[92mINFO [0m: (3, 0.6059146807741652),
|
| 99 |
+
[92mINFO [0m: (4, 0.19704961071376878),
|
| 100 |
+
[92mINFO [0m: (5, 0.13989461602253406),
|
| 101 |
+
[92mINFO [0m: (6, 0.020903305802541017),
|
| 102 |
+
[92mINFO [0m: (7, 0.1471804918425052),
|
| 103 |
+
[92mINFO [0m: (8, 0.03622452800638312),
|
| 104 |
+
[92mINFO [0m: (9, 0.021778890335108998),
|
| 105 |
+
[92mINFO [0m: (10, 0.014076102254991698)],
|
| 106 |
+
[92mINFO [0m: 'client_2_train_loss': [(1, 0.005994700957002613),
|
| 107 |
+
[92mINFO [0m: (2, 9.031767151078034),
|
| 108 |
+
[92mINFO [0m: (3, 0.7539265559325762),
|
| 109 |
+
[92mINFO [0m: (4, 0.16390307652051578),
|
| 110 |
+
[92mINFO [0m: (5, 0.20438744467001307),
|
| 111 |
+
[92mINFO [0m: (6, 0.016729887813302254),
|
| 112 |
+
[92mINFO [0m: (7, 0.12536963731445314),
|
| 113 |
+
[92mINFO [0m: (8, 0.03203308185613172),
|
| 114 |
+
[92mINFO [0m: (9, 0.01797729913919752),
|
| 115 |
+
[92mINFO [0m: (10, 0.014847784393701121)],
|
| 116 |
+
[92mINFO [0m: 'client_3_train_loss': [(1, 0.0477309877821325),
|
| 117 |
+
[92mINFO [0m: (2, 8.576512244819956),
|
| 118 |
+
[92mINFO [0m: (3, 0.7021478945708147),
|
| 119 |
+
[92mINFO [0m: (4, 0.20970996846132312),
|
| 120 |
+
[92mINFO [0m: (5, 0.2520780406049325),
|
| 121 |
+
[92mINFO [0m: (6, 0.039224337560547065),
|
| 122 |
+
[92mINFO [0m: (7, 0.19049448489231752),
|
| 123 |
+
[92mINFO [0m: (8, 0.03907830891323567),
|
| 124 |
+
[92mINFO [0m: (9, 0.03410547275085612),
|
| 125 |
+
[92mINFO [0m: (10, 0.03132038051823111)],
|
| 126 |
+
[92mINFO [0m: 'global_train_loss': [(1, 0.024723581809669777),
|
| 127 |
+
[92mINFO [0m: (2, 8.885175558524038),
|
| 128 |
+
[92mINFO [0m: (3, 0.6942090508692439),
|
| 129 |
+
[92mINFO [0m: (4, 0.19139092719798328),
|
| 130 |
+
[92mINFO [0m: (5, 0.20735043383995075),
|
| 131 |
+
[92mINFO [0m: (6, 0.027091810488224646),
|
| 132 |
+
[92mINFO [0m: (7, 0.15788991120164078),
|
| 133 |
+
[92mINFO [0m: (8, 0.03602586002639032),
|
| 134 |
+
[92mINFO [0m: (9, 0.025616361683664917),
|
| 135 |
+
[92mINFO [0m: (10, 0.021443837521512663)]}
|
| 136 |
+
[92mINFO [0m: History (metrics, distributed, evaluate):
|
| 137 |
+
[92mINFO [0m: {'client_1_eval_loss': [(1, 9.976542702888077),
|
| 138 |
+
[92mINFO [0m: (2, 1.2864945372182275),
|
| 139 |
+
[92mINFO [0m: (3, 0.2793154488706755),
|
| 140 |
+
[92mINFO [0m: (4, 0.5965077027088073),
|
| 141 |
+
[92mINFO [0m: (5, 0.03825337379663002),
|
| 142 |
+
[92mINFO [0m: (6, 0.2677974948383068),
|
| 143 |
+
[92mINFO [0m: (7, 0.058361862074286265),
|
| 144 |
+
[92mINFO [0m: (8, 0.08157266843303032),
|
| 145 |
+
[92mINFO [0m: (9, 0.029206974828750388),
|
| 146 |
+
[92mINFO [0m: (10, 0.02422947784015051)],
|
| 147 |
+
[92mINFO [0m: 'client_2_eval_loss': [(1, 10.00263674723258),
|
| 148 |
+
[92mINFO [0m: (2, 1.3619409297631837),
|
| 149 |
+
[92mINFO [0m: (3, 0.24040741897930812),
|
| 150 |
+
[92mINFO [0m: (4, 0.38867445768531034),
|
| 151 |
+
[92mINFO [0m: (5, 0.032692949841941314),
|
| 152 |
+
[92mINFO [0m: (6, 0.2738929928920669),
|
| 153 |
+
[92mINFO [0m: (7, 0.060165285674857814),
|
| 154 |
+
[92mINFO [0m: (8, 0.07285282868754381),
|
| 155 |
+
[92mINFO [0m: (9, 0.029050754143804952),
|
| 156 |
+
[92mINFO [0m: (10, 0.01716736676326117)],
|
| 157 |
+
[92mINFO [0m: 'client_3_eval_loss': [(1, 9.831531776215563),
|
| 158 |
+
[92mINFO [0m: (2, 1.461328144772189),
|
| 159 |
+
[92mINFO [0m: (3, 0.3416395899531878),
|
| 160 |
+
[92mINFO [0m: (4, 0.5114476139352474),
|
| 161 |
+
[92mINFO [0m: (5, 0.05814581121184792),
|
| 162 |
+
[92mINFO [0m: (6, 0.3043146318768071),
|
| 163 |
+
[92mINFO [0m: (7, 0.058136951014934914),
|
| 164 |
+
[92mINFO [0m: (8, 0.08330663724478181),
|
| 165 |
+
[92mINFO [0m: (9, 0.049671815384144585),
|
| 166 |
+
[92mINFO [0m: (10, 0.029676996694472633)],
|
| 167 |
+
[92mINFO [0m: 'global_eval_loss': [(1, 9.925078071091885),
|
| 168 |
+
[92mINFO [0m: (2, 1.3836319280429141),
|
| 169 |
+
[92mINFO [0m: (3, 0.29234505846417363),
|
| 170 |
+
[92mINFO [0m: (4, 0.4930816239130249),
|
| 171 |
+
[92mINFO [0m: (5, 0.0446630791944702),
|
| 172 |
+
[92mINFO [0m: (6, 0.2849148775837404),
|
| 173 |
+
[92mINFO [0m: (7, 0.058860671175644294),
|
| 174 |
+
[92mINFO [0m: (8, 0.07942846919600849),
|
| 175 |
+
[92mINFO [0m: (9, 0.03762738416111641),
|
| 176 |
+
[92mINFO [0m: (10, 0.024166644736454038)]}
|
| 177 |
+
[92mINFO [0m:
|
| 178 |
+
Tamanho da Previs�o: 90
|
| 179 |
+
|
| 180 |
+
============================================================
|
| 181 |
+
SERVIDOR DE APRENDIZADO FEDERADO
|
| 182 |
+
============================================================
|
| 183 |
+
Estrat�gia: FEDADAGRAD
|
| 184 |
+
Rodadas: 10
|
| 185 |
+
Clientes m�nimos: 3
|
| 186 |
+
============================================================
|
| 187 |
+
|
| 188 |
+
Iniciando servidor FL...
|
| 189 |
+
|
| 190 |
+
============================================================
|
| 191 |
+
TREINAMENTO CONCLU�DO - GERANDO AN�LISES
|
| 192 |
+
============================================================
|
| 193 |
+
Visualiza��es salvas em results
|
| 194 |
+
M�tricas detalhadas salvas em results\detailed_metrics_fedadagrad.csv
|
| 195 |
+
An�lise estat�stica salva em results\analysis_fedadagrad.json
|
| 196 |
+
|
| 197 |
+
============================================================
|
| 198 |
+
RESUMO DO TREINAMENTO
|
| 199 |
+
============================================================
|
| 200 |
+
Perda inicial de treino: 0.024724
|
| 201 |
+
Perda final de treino: 0.021444
|
| 202 |
+
Melhoria no treino: 13.27%
|
| 203 |
+
|
| 204 |
+
Perda inicial de valida��o: 9.925078
|
| 205 |
+
Perda final de valida��o: 0.024167
|
| 206 |
+
Melhoria na valida��o: 99.76%
|
| 207 |
+
|
| 208 |
+
Desvio padr�o final entre clientes: 0.005121
|
| 209 |
+
Converg�ncia: Boa
|
| 210 |
+
|
| 211 |
+
============================================================
|
| 212 |
+
Resultados salvos em: results
|
| 213 |
+
============================================================
|
utils.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
from torch.utils.data import TensorDataset, DataLoader
|
| 5 |
+
from sklearn.preprocessing import MinMaxScaler
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
# Definição do Modelo LSTM com PyTorch
|
| 10 |
+
class Net(torch.nn.Module):
|
| 11 |
+
def __init__(self, input_size, hidden_size, output_size, num_layers=1):
|
| 12 |
+
super(Net, self).__init__()
|
| 13 |
+
self.lstm = torch.nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
|
| 14 |
+
self.linear = torch.nn.Linear(hidden_size, output_size)
|
| 15 |
+
|
| 16 |
+
def forward(self, x):
|
| 17 |
+
lstm_out, _ = self.lstm(x)
|
| 18 |
+
# Usamos apenas a saída do último passo de tempo para a previsão
|
| 19 |
+
last_time_step_out = lstm_out[:, -1, :]
|
| 20 |
+
out = self.linear(last_time_step_out)
|
| 21 |
+
return out
|
| 22 |
+
|
| 23 |
+
def create_sliding_windows(data, sequence_length, prediction_length):
|
| 24 |
+
"""Cria janelas deslizantes para problemas de séries temporais."""
|
| 25 |
+
xs, ys = [], []
|
| 26 |
+
for i in range(len(data) - sequence_length - prediction_length + 1):
|
| 27 |
+
x = data[i:(i + sequence_length)]
|
| 28 |
+
y = data[(i + sequence_length):(i + sequence_length + prediction_length), -1] # A última coluna é 'P_kW'
|
| 29 |
+
xs.append(x)
|
| 30 |
+
ys.append(y)
|
| 31 |
+
return np.array(xs), np.array(ys)
|
| 32 |
+
|
| 33 |
+
def load_data(client_id: int, sequence_length=60, prediction_length=30, batch_size=32):
|
| 34 |
+
"""
|
| 35 |
+
Carrega os dados para um cliente específico, processa e retorna DataLoaders.
|
| 36 |
+
NOVA VERSÃO: Concatena todos os percursos e divide em 80% para treino e 20% para teste.
|
| 37 |
+
"""
|
| 38 |
+
data_dir = Path(f"data/client_{client_id}")
|
| 39 |
+
|
| 40 |
+
# 1. Carrega todos os percursos em uma lista de DataFrames
|
| 41 |
+
all_routes_df = [pd.read_csv(f) for f in data_dir.glob("*.csv")]
|
| 42 |
+
|
| 43 |
+
if not all_routes_df:
|
| 44 |
+
raise FileNotFoundError(f"Nenhum arquivo CSV encontrado para o cliente {client_id} no diretório {data_dir}")
|
| 45 |
+
|
| 46 |
+
# 2. Concatena TODOS os percursos em um único DataFrame
|
| 47 |
+
combined_df = pd.concat(all_routes_df, ignore_index=True)
|
| 48 |
+
|
| 49 |
+
feature_columns = ['vehicle_speed', 'engine_rpm', 'accel_x', 'accel_y', 'P_kW', 'dt']
|
| 50 |
+
processed_df = combined_df[feature_columns].dropna()
|
| 51 |
+
|
| 52 |
+
# 3. Divide o DataFrame combinado em 80% para treino e 20% para teste
|
| 53 |
+
split_index = int(len(processed_df) * 0.8)
|
| 54 |
+
train_df = processed_df.iloc[:split_index]
|
| 55 |
+
test_df = processed_df.iloc[split_index:]
|
| 56 |
+
|
| 57 |
+
# 4. AJUSTA O SCALER APENAS NOS DADOS DE TREINO (ESSENCIAL!)
|
| 58 |
+
scaler = MinMaxScaler()
|
| 59 |
+
scaler.fit(train_df)
|
| 60 |
+
|
| 61 |
+
# 5. TRANSFORMA AMBOS OS CONJUNTOS COM O SCALER AJUSTADO
|
| 62 |
+
train_scaled = scaler.transform(train_df)
|
| 63 |
+
test_scaled = scaler.transform(test_df)
|
| 64 |
+
|
| 65 |
+
# 6. CRIA JANELAS SEPARADAMENTE PARA TREINO E TESTE
|
| 66 |
+
X_train, y_train = create_sliding_windows(train_scaled, sequence_length, prediction_length)
|
| 67 |
+
X_test, y_test = create_sliding_windows(test_scaled, sequence_length, prediction_length)
|
| 68 |
+
|
| 69 |
+
# Garante que os conjuntos não sejam vazios
|
| 70 |
+
if len(X_train) == 0 or len(X_test) == 0:
|
| 71 |
+
raise ValueError(f"A divisão de dados para o cliente {client_id} resultou em um conjunto de treino ou teste vazio. Verifique a quantidade de dados.")
|
| 72 |
+
|
| 73 |
+
# Conversão para tensores PyTorch
|
| 74 |
+
X_train_tensor = torch.from_numpy(X_train).float()
|
| 75 |
+
y_train_tensor = torch.from_numpy(y_train).float()
|
| 76 |
+
X_test_tensor = torch.from_numpy(X_test).float()
|
| 77 |
+
y_test_tensor = torch.from_numpy(y_test).float()
|
| 78 |
+
|
| 79 |
+
# Criação de DataLoaders
|
| 80 |
+
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
|
| 81 |
+
test_dataset = TensorDataset(X_test_tensor, y_test_tensor)
|
| 82 |
+
|
| 83 |
+
trainloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
|
| 84 |
+
testloader = DataLoader(test_dataset, batch_size=batch_size)
|
| 85 |
+
|
| 86 |
+
num_features = X_train_tensor.shape[2]
|
| 87 |
+
|
| 88 |
+
return trainloader, testloader, num_features
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def train(net, trainloader, epochs, device):
|
| 92 |
+
"""Treina e retorna a perda média por amostra (float)."""
|
| 93 |
+
criterion = torch.nn.MSELoss(reduction="mean") # perda média por saída
|
| 94 |
+
optimizer = torch.optim.Adam(net.parameters(), lr=1e-5)
|
| 95 |
+
net.to(device)
|
| 96 |
+
net.train()
|
| 97 |
+
|
| 98 |
+
total_loss_sum = 0.0
|
| 99 |
+
total_samples = 0
|
| 100 |
+
|
| 101 |
+
for _ in range(epochs):
|
| 102 |
+
for sequences, labels in trainloader:
|
| 103 |
+
sequences, labels = sequences.to(device), labels.to(device)
|
| 104 |
+
optimizer.zero_grad()
|
| 105 |
+
outputs = net(sequences)
|
| 106 |
+
loss = criterion(outputs, labels) # média por amostra no batch
|
| 107 |
+
loss.backward()
|
| 108 |
+
torch.nn.utils.clip_grad_norm_(net.parameters(), max_norm=1.0)
|
| 109 |
+
optimizer.step()
|
| 110 |
+
|
| 111 |
+
batch_size = sequences.size(0)
|
| 112 |
+
total_loss_sum += loss.item() * batch_size # soma por amostra
|
| 113 |
+
total_samples += batch_size
|
| 114 |
+
|
| 115 |
+
if total_samples == 0:
|
| 116 |
+
return 0.0
|
| 117 |
+
return total_loss_sum / total_samples # média por amostra
|
| 118 |
+
|
| 119 |
+
# --- substitua a função test ---
|
| 120 |
+
def test(net, testloader, device):
|
| 121 |
+
"""Avalia e retorna (avg_loss_per_sample, num_examples)."""
|
| 122 |
+
criterion = torch.nn.MSELoss(reduction="mean")
|
| 123 |
+
net.to(device)
|
| 124 |
+
net.eval()
|
| 125 |
+
total_loss_sum = 0.0
|
| 126 |
+
total_samples = 0
|
| 127 |
+
with torch.no_grad():
|
| 128 |
+
for sequences, labels in testloader:
|
| 129 |
+
sequences, labels = sequences.to(device), labels.to(device)
|
| 130 |
+
outputs = net(sequences)
|
| 131 |
+
loss = criterion(outputs, labels)
|
| 132 |
+
batch_size = sequences.size(0)
|
| 133 |
+
total_loss_sum += loss.item() * batch_size
|
| 134 |
+
total_samples += batch_size
|
| 135 |
+
|
| 136 |
+
if total_samples == 0:
|
| 137 |
+
return 0.0, 0
|
| 138 |
+
avg_loss = total_loss_sum / total_samples
|
| 139 |
+
return avg_loss, total_samples
|