import streamlit as st
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
from matplotlib.animation import FuncAnimation
import pandas as pd
import time
import plotly.graph_objects as go
import plotly.express as px
from io import BytesIO
st.set_page_config(page_title="APRENDIZADO FEDERADO PARA PREVISÃO DE DEMANDA ENERGÉTICA", page_icon=":bar_chart:", layout="wide", initial_sidebar_state="auto")
st.markdown("
Aprendizado Federado com Flower
", unsafe_allow_html=True)
st.sidebar.image("images/logo_inmetro.jpg", width=200)
st.sidebar.title("FL Inmetro")
secao = st.sidebar.radio("Ir para:", ["🏠 Início", "🖥️ Implementação", "📊 Visualização", "🍓 Integração Raspberry Pi", "📈 Modelos para EVs", "📚 Artigos", "ℹ️ Sobre"])
if secao == "🏠 Início":
tab1, tab2, tab3 = st.tabs(["📘 Aprendizado Centralizado", "📗 Aprendizado Federado", "O Projeto"])
with tab1:
st.markdown("## 📘 Aprendizado Centralizado: Fundamentos Teóricos")
# Definição formal do problema
st.markdown("### 🔍 Definição Formal do Problema")
st.latex(r"""
\begin{aligned}
&\text{Dado:} \\
&\quad \text{Conjunto de dados } \mathcal{D} = \{(\mathbf{x}_i, y_i)\}_{i=1}^N \\
&\quad \text{Modelo paramétrico } f_\theta: \mathcal{X} \to \mathcal{Y} \\
&\quad \text{Função perda } \ell: \mathcal{Y} \times \mathcal{Y} \to \mathbb{R}^+ \\
\\
&\text{Objetivo:} \\
&\quad \min_{\theta \in \mathbb{R}^d} \mathcal{L}(\theta) = \mathbb{E}_{(\mathbf{x},y) \sim \mathcal{P}} [\ell(f_\theta(\mathbf{x}), y)] \\
&\quad \text{com aproximação empírica:} \\
&\quad \hat{\mathcal{L}}(\theta) = \frac{1}{N} \sum_{i=1}^N \ell(f_\theta(\mathbf{x}_i), y_i)
\end{aligned}
""")
st.markdown("""
Onde:
- $\mathcal{P}$ é a distribuição de dados subjacente
- $\mathbf{x}_i \in \mathbb{R}^m$ são features de entrada
- $y_i \in \mathcal{Y}$ são targets (contínuos ou discretos)
- $\theta$ são parâmetros do modelo (e.g., pesos de rede neural)
""")
# Fundamentos de otimização
st.markdown("#### Algoritmo: Gradiente Descendente (GD)")
st.latex(r"""
\begin{aligned}
&\textbf{Input: } \theta_0 \text{ (inicialização)}, \eta > 0 \text{ (taxa aprendizado)}, T \\
&\textbf{For } t = 0 \text{ to } T-1: \\
&\quad g_t = \nabla_\theta \hat{\mathcal{L}}(\theta_t) = \frac{1}{N} \sum_{i=1}^N \nabla_\theta \ell(f_{\theta_t}(\mathbf{x}_i), y_i) \\
&\quad \theta_{t+1} = \theta_t - \eta g_t
\end{aligned}
""")
st.markdown("#### Formulação Estocástica (SGD)")
st.latex(r"""
\begin{aligned}
&\textbf{Input: } \theta_0, \eta > 0, T, \text{ tamanho lote } B \\
&\textbf{For } t = 0 \text{ to } T-1: \\
&\quad \text{Selecione } \mathcal{B}_t \subset \{1,\dots,N\} \text{ com } |\mathcal{B}_t| = B \\
&\quad \tilde{g}_t = \frac{1}{B} \sum_{i \in \mathcal{B}_t} \nabla_\theta \ell(f_{\theta_t}(\mathbf{x}_i), y_i) \\
&\quad \theta_{t+1} = \theta_t - \eta \tilde{g}_t
\end{aligned}
""")
# Análise de convergência
st.markdown("### Convergência")
st.latex(r"""
\text{Sob as condições:} \\
\begin{array}{ll}
(1) & \mathcal{L} \text{ é } \mu\text{-fortemente convexa} \\
(2) & \mathbb{E}[\|\nabla_\theta \ell(\cdot)\|^2_2] \leq G^2 \\
(3) & \eta_t = \frac{c}{t} \text{ (decay de taxa de aprendizado)}
\end{array}
""")
st.latex(r"""
\text{GD atinge:} \\
\mathcal{L}(\theta_T) - \mathcal{L}(\theta^*) \leq \mathcal{O}\left(\frac{1}{T}\right)
""")
st.latex(r"""
\text{SGD atinge:} \\
\mathbb{E}[\mathcal{L}(\theta_T)] - \mathcal{L}(\theta^*) \leq \mathcal{O}\left(\frac{1}{\sqrt{T}}\right)
""")
# Exemplo numérico
st.markdown("### 🔢 Exemplo Numérico")
st.markdown("Considere regressão linear com perda MSE:")
st.latex(r"""
\ell(f_\theta(\mathbf{x}), y) = \frac{1}{2} (\theta^\top \mathbf{x} - y)^2
""")
st.markdown("Gradiente para um único ponto:")
st.latex(r"""
\nabla_\theta \ell = (\theta^\top \mathbf{x} - y) \mathbf{x}
""")
st.markdown("Atualização de GD em tempo real:")
theta = st.slider("Parâmetro θ", -2.0, 2.0, 0.5, 0.1)
eta = st.slider("Taxa aprendizado η", 0.01, 1.0, 0.1, 0.01)
# Cálculo de exemplo
x, y = 2.0, 3.0 # Dado fixo para demonstração
loss = 0.5 * (theta*x - y)**2
gradient = (theta*x - y)*x
new_theta = theta - eta*gradient
st.latex(fr"""
\begin{{aligned}}
\theta^{{(t)}} &= {theta:.2f} \\
\nabla_\theta \mathcal{{L}} &= ({theta:.2f} \times {x} - {y}) \times {x} = {gradient:.2f} \\
\theta^{{(t+1)}} &= {theta:.2f} - {eta} \times {gradient:.2f} = {new_theta:.2f}
\end{{aligned}}
""")
with tab2:
st.markdown("## Aprendizado Federado: Fundamentos Teóricos")
# Definição formal do problema
st.markdown("### Formulação Matemática do Problema")
st.latex(r"""
\begin{aligned}
&\text{Dado:} \\
&\quad \text{Clientes } k = 1, \dots, K \text{ com conjuntos de dados locais } \mathcal{D}_k = \{(\mathbf{x}_i^k, y_i^k)\}_{i=1}^{n_k} \\
&\quad \text{Modelo paramétrico } f_\theta: \mathcal{X} \to \mathcal{Y} \\
&\quad \text{Função perda } \ell: \mathcal{Y} \times \mathcal{Y} \to \mathbb{R}^+ \\
\\
&\text{Objetivo Federado:} \\
&\quad \min_{\theta \in \mathbb{R}^d} \mathcal{L}(\theta) = \sum_{k=1}^K \frac{n_k}{n} \mathcal{L}_k(\theta) \\
&\quad \text{onde } \mathcal{L}_k(\theta) = \frac{1}{n_k} \sum_{i=1}^{n_k} \ell(f_\theta(\mathbf{x}_i^k), y_i^k) \\
&\quad n = \sum_{k=1}^K n_k \quad \text{(tamanho total do dataset)}
\end{aligned}
""")
st.markdown("""
Onde:
- $\mathcal{D}_k$ permanece localizado no dispositivo do cliente $k$
- $n_k$ é o número de amostras do cliente $k$
- O objetivo global é uma média ponderada dos objetivos locais
""")
# Algoritmo FedAvg detalhado
st.markdown("### Algoritmo Federado: FedAvg (Federated Averaging)")
st.latex(r"""
\begin{aligned}
1. & \textbf{Inicialização:} \\
& \quad \theta^{(0)} \leftarrow \text{parâmetros iniciais} \\
2. & \textbf{for } t = 0 \text{ to } T-1 \textbf{ do:} \\
3. & \quad \text{Servidor seleciona subconjunto } S_t \subseteq \{1,\dots,K\} \\
4. & \quad \text{Servidor transmite } \theta^{(t)} \text{ para todos os clientes } k \in S_t \\
5. & \quad \textbf{for cada cliente } k \in S_t \textbf{ paralelamente do:} \\
6. & \quad\quad \theta_k^{(t,0)} \leftarrow \theta^{(t)} \\
7. & \quad\quad \textbf{for } \tau = 0 \text{ to } E-1 \textbf{ do:} \\
8. & \quad\quad\quad \text{Selecione lote } \mathcal{B}_{\tau}^k \subseteq \mathcal{D}_k \\
9. & \quad\quad\quad g_k^{(\tau)} = \frac{1}{|\mathcal{B}_{\tau}^k|} \sum_{(\mathbf{x},y)\in\mathcal{B}_{\tau}^k} \nabla_\theta \ell(f_{\theta_k^{(t,\tau)}}(\mathbf{x}), y) \\
10. & \quad\quad\quad \theta_k^{(t,\tau+1)} = \theta_k^{(t,\tau)} - \eta_k g_k^{(\tau)} \\
11. & \quad\quad \text{Cliente } k \text{ envia } \Delta_k^{(t)} = \theta_k^{(t,E)} - \theta^{(t)} \text{ para servidor} \\
12. & \quad \text{Servidor atualiza: } \\
& \quad\quad \theta^{(t+1)} = \theta^{(t)} + \sum_{k \in S_t} \frac{n_k}{\sum_{j \in S_t} n_j} \Delta_k^{(t)}
\end{aligned}
""")
st.markdown("""
Parâmetros-chave:
- $E$: Número de épocas locais
- $\eta_k$: Taxa de aprendizado do cliente $k$
- $S_t$: Subconjunto de clientes na rodada $t$
- $\Delta_k^{(t)}$: Atualização do cliente $k$
""")
# Diagrama de arquitetura
st.markdown("### Arquitetura do Sistema Federado")
st.image("./images/fedlr_diagram.png", width=700)
st.markdown("""
Fluxo de operação:
1. Servidor inicializa modelo global $\theta^{(0)}$
2. A cada rodada $t$:
a. Servidor seleciona subconjunto de clientes $S_t$
b. Envia modelo global atual para clientes selecionados
c. Cada cliente atualiza modelo localmente com seus dados
d. Clientes enviam atualizações de parâmetros para servidor
e. Servidor agrega atualizações e calcula novo modelo global
""")
# Teoria de convergência
st.markdown("### Análise de Convergência")
st.markdown("#### Hipóteses Fundamentais")
st.latex(r"""
\begin{array}{ll}
\text{(A1)} & \mathcal{L} \text{ é } L\text{-suave: } \|\nabla\mathcal{L}(\theta) - \nabla\mathcal{L}(\theta')\| \leq L\|\theta - \theta'\| \\
\text{(A2)} & \text{Variância limitada: } \mathbb{E}_{k} \|\nabla\mathcal{L}_k(\theta) - \nabla\mathcal{L}(\theta)\|^2 \leq \sigma^2 \\
\text{(A3)} & \text{Conjunto de dados não-IID: } \exists \delta \geq 0 \text{ tal que } \frac{1}{K}\sum_{k=1}^K \|\nabla\mathcal{L}_k(\theta) - \nabla\mathcal{L}(\theta)\|^2 \leq \delta^2 \\
\text{(A4)} & \text{Gradiente limitado: } \mathbb{E} \|g_k^{(\tau)}\|^2 \leq G^2
\end{array}
""")
st.markdown("#### Teorema de Convergência (FedAvg)")
st.latex(r"""
\text{Sob as hipóteses (A1)-(A4), com } \eta_k = \eta \text{ e seleção uniforme de clientes:}
""")
st.latex(r"""
\min_{t \in \{0,\dots,T-1\}} \mathbb{E} \|\nabla \mathcal{L}(\theta^{(t)})\|^2 \leq \mathcal{O}\left( \frac{\mathcal{L}(\theta^{(0)}) - \mathcal{L}^*}{\eta E T} \right) + \mathcal{O}\left( \frac{\sigma^2}{M} \right) + \mathcal{O}\left( \eta^2 E^2 G^2 L^2 \right) + \mathcal{O}(\delta^2)
""")
st.markdown("""
Onde:
- $M = |S_t|$ (tamanho do subconjunto de clientes)
- $\mathcal{L}^*$ é o valor ótimo da função de perda
- $\delta$ mede o grau de heterogeneidade dos dados
""")
# Desafios técnicos detalhados
st.markdown("### Desafios Técnicos e Soluções")
st.markdown("#### 1. Heterogeneidade de Dados (não-IID)")
st.latex(r"""
\text{Definição: } \exists k \neq j: \mathbb{P}_k(\mathbf{x}, y) \neq \mathbb{P}_j(\mathbf{x}, y)
""")
st.latex(r"""
\text{Problemas: }\\
\text{Viés na agregação: } \mathbb{E}[\theta^{(t+1)}] \neq \theta_{\text{ótimo}}\\
\text{Divergência do modelo: } \|\theta^{(t)} - \theta^*\| \text{ cresce com t}\\
\text{Soluções teóricas: }\\
\text{1. Regularização proximal: } \min_\theta \mathcal{L}_k(\theta) + \frac{\mu}{2} \|\theta - \theta^{(t)}\|^2\\
\text{2. Controle de variância: } \Delta_k^{(t)} \leftarrow \Delta_k^{(t)} - \beta (\Delta_k^{(t)} - \Delta^{(t-1)})
""")
st.markdown("#### 3. Segurança e Privacidade")
st.latex(r"""
\begin{array}{c}
\text{Ataque: } \Delta_k^{(t)} = \Delta_{\text{malicioso}} \\
\text{Defesa: } \theta^{(t+1)} = \text{AGG}_{\gamma} \left( \{ \Delta_k^{(t)} \}_{k \in S_t} \right)
\end{array}
""")
st.latex(r"""
\text{Mecanismos de defesa: }\\
\text{2. DP-SGD: } g_k^{(\tau)} \leftarrow \text{Clip}(g_k^{(\tau)}, C) + \mathcal{N}(0, \sigma^2 I)\\
""")
st.markdown("""
O **DP-SGD** adapta o SGD clássico para fornecer garantias de **privacidade diferencial**,
limitando o impacto de cada amostra e adicionando ruído calibrado.
""")
# 1. Cálculo de gradientes por amostra
st.markdown("**1. Cálculo de gradientes por amostra**")
st.latex(r"""
g_i \;=\; \nabla_\theta \,\ell\bigl(f_\theta(x_i),\,y_i\bigr)
""")
# 2. Clipping de gradientes
st.markdown("**2. Clipping de gradientes**: limita a norma de cada gradiente a um máximo \(C\).")
st.latex(r"""
\bar g_i \;=\; \frac{g_i}{\max\!\bigl(1,\;\|g_i\|/C\bigr)}
""")
# 3. Soma e adição de ruído
st.markdown("**3. Soma e adição de ruído**: soma os gradientes recortados e adiciona ruído Gaussiano.")
st.latex(r"""
\tilde g \;=\; \frac{1}{B}\sum_{i=1}^B \bar g_i \;+\; \mathcal{N}\!\bigl(0,\;\sigma^2 C^2 I\bigr)
""")
# 4. Atualização de parâmetros
st.markdown("**4. Atualização de parâmetros** realiza o passo de descida de gradiente com o gradiente privatizado.")
st.latex(r"""
\theta \;\leftarrow\; \theta \;-\;\eta\,\tilde g
""")
st.markdown("---")
st.markdown("""
- **$C$** (clipping norm): controla o máximo de contribuição de uma única amostra.
- **$\sigma$** (noise multiplier): regula a intensidade do ruído; maior $\sigma$ → mais privacidade (menor $\epsilon$) mas potencialmente pior desempenho.
- **Moments Accountant**: método eficiente para juntar os gastos de privacidade de cada minibatch.
""")
# Comparação com aprendizado centralizado
st.markdown("### Comparação Teórica: Federado vs Centralizado")
st.latex(r"""
\begin{array}{c|c|c}
\text{Propriedade} & \text{Centralizado} & \text{Federado} \\
\hline
\text{Acesso aos dados} & \text{Completo} & \text{Nenhum} \\
\text{Comunicação} & \mathcal{O}(N \times d) & \mathcal{O}(T \times |S_t| \times d) \\
\text{Convergência} & \mathcal{O}(1/T) & \mathcal{O}(1/\sqrt{T}) \\
\text{Privacidade} & \text{Baixa} & \text{Alta (com DP)} \\
\text{Robustez} & \text{Alta} & \text{Controlável} \\
\text{Escalabilidade} & \text{Limitada} & \text{Alta}
\end{array}
""")
st.markdown("""
Onde:
- $d$: Dimensão dos parâmetros do modelo
- $N$: Número total de amostras de dados
- $T$: Número de rodadas de comunicação
""")
st.markdown("""
**Benefícios do aprendizado federado:**
- Preserva privacidade dos padrões de consumo
- Reduz tráfego de rede (dados permanecem locais)
- Permite personalização local do modelo
""")
with tab3:
st.title("Aprendizado Federado para Previsão de Demanda Energética")
col1, col2 = st.columns(2)
with col1:
st.image("images/federated_learning.png", caption="Conceito de Aprendizado Federado")
with col2:
st.markdown("""
O Aprendizado Federado (FL) é um paradigma de aprendizado de máquina onde o modelo é treinado em múltiplos dispositivos descentralizados sem compartilhar os dados locais. Isso oferece:
- **Privacidade de dados**: Os dados nunca saem dos dispositivos locais
- **Eficiência de comunicação**: Apenas parâmetros do modelo são compartilhados
- **Treinamento colaborativo**: Múltiplos dispositivos contribuem para um modelo global
- **Conformidade regulatória**: Ajuda no cumprimento de leis como LGPD e GDPR
""")
st.markdown("""
O aprendizado federado é especialmente adequado para prever a demanda energética porque:
- Permite análise de dados sensíveis de consumo sem expô-los
- Possibilita a colaboração entre múltiplos consumidores/medidores
- Reduz o tráfego de rede ao evitar a transferência de dados brutos
- Permite treinamento em dispositivos de borda (edge computing)
- Pode aperceiçoar modelos atuais que utilizam análise centralizada de dados
## Objetivos do Projeto
1. Implementar um sistema de aprendizado federado usando Flower
2. Aplicar técnicas avançadas de agregação para otimizar o aprendizado
3. Integrar com dispositivos Raspberry Pi para coleta de dados em tempo real
4. Desenvolver modelos de regressão para previsão de demanda energética
""")
elif secao == "🖥️ Implementação":
st.title("Implementação de Aprendizado Federado com Flower")
steps = st.tabs(["1. Instalação", "2. Estrutura do Projeto", "3. Servidor", "4. Cliente","5. Modelo/Task", "6. Estratégias de Agregação", "7. Execução", '8. pyproject.toml'])
with steps[0]:
st.subheader("Instalação do Flower")
st.code("""
# Instalação via pip
pip install flwr
flwr new
flwr run -e . # instala as dependências que vem no arquivo pyproject.toml
flwr run .
""", language="bash")
st.info("No caso de ambientes virtuais para isolar as dependências:")
st.code("""
# Criar e ativar ambiente virtual
python -m venv fl_env
source fl_env/bin/activate # Linux/Mac
fl_env\\Scripts\\activate # Windows
""", language="bash")
with steps[1]:
st.subheader("Estrutura Básica do Projeto")
st.code("""
fl_project/
├── server.py # Servidor Flower
├── client.py # Implementação do cliente
├── model.py # Definição do modelo ML
├── data_loader.py # Carregamento e pré-processamento de dados
├── utils/
│ ├── __init__.py
│ ├── metrics.py # Funções de avaliação de desempenho
│ └── visualization.py # Visualização de resultados
├── config.py # Configurações do sistema
├── requirements.txt # Dependências do projeto
└── run.sh # Script para iniciar servidor e clientes
|── pyproject.toml # Configurações do projeto e dependências
""", language="text")
with steps[2]:
st.subheader("Implementação do Servidor")
st.markdown("""
O servidor Flower é responsável por coordenar o treinamento, distribuir o modelo global e agregar as atualizações dos clientes.
""")
st.code('''
from flwr.common import Context, ndarrays_to_parameters
from flwr.server import ServerApp, ServerAppComponents, ServerConfig
from flwr.server.strategy import FedAvg
from fl_inmetro.task import Net, get_weights
def server_fn(context: Context):
# Read from config
num_rounds = context.run_config["num-server-rounds"]
fraction_fit = context.run_config["fraction-fit"]
# Initialize model parameters
ndarrays = get_weights(Net())
parameters = ndarrays_to_parameters(ndarrays)
# Define strategy
strategy = FedAvg(
fraction_fit=fraction_fit,
fraction_evaluate=1.0,
min_available_clients=2,
initial_parameters=parameters,
)
config = ServerConfig(num_rounds=num_rounds)
return ServerAppComponents(strategy=strategy, config=config)
# Create ServerApp
app = ServerApp(server_fn=server_fn)
''', language="python")
with steps[3]:
st.subheader("Implementação do Cliente")
st.markdown("""
O cliente Flower é implementado nos dispositivos onde os dados estão armazenados.
Ele treina o modelo localmente e envia atualizações para o servidor.
""")
st.code('''
import torch
from flwr.client import ClientApp, NumPyClient
from flwr.common import Context
from fl_inmetro.task import Net, get_weights, load_data, set_weights, test, train
# Define Flower Client and client_fn
class FlowerClient(NumPyClient):
def __init__(self, net, trainloader, valloader, local_epochs):
self.net = net
self.trainloader = trainloader
self.valloader = valloader
self.local_epochs = local_epochs
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
self.net.to(self.device)
def fit(self, parameters, config):
set_weights(self.net, parameters)
train_loss = train(
self.net,
self.trainloader,
self.local_epochs,
self.device,
)
return (
get_weights(self.net),
len(self.trainloader.dataset),
{"train_loss": train_loss},
)
def evaluate(self, parameters, config):
set_weights(self.net, parameters)
loss, accuracy = test(self.net, self.valloader, self.device)
return loss, len(self.valloader.dataset), {"accuracy": accuracy}
def client_fn(context: Context):
# Load model and data
net = Net()
partition_id = context.node_config["partition-id"]
num_partitions = context.node_config["num-partitions"]
trainloader, valloader = load_data(partition_id, num_partitions)
local_epochs = context.run_config["local-epochs"]
# Return Client instance
return FlowerClient(net, trainloader, valloader, local_epochs).to_client()
# Flower ClientApp
app = ClientApp(
client_fn,
)
''', language="python")
with steps[4]:
st.subheader('Modelo/Tarefas')
st.code(
"""
from collections import OrderedDict
import torch
import torch.nn as nn
import torch.nn.functional as F
from flwr_datasets import FederatedDataset
from flwr_datasets.partitioner import IidPartitioner
from torch.utils.data import DataLoader
from torchvision.transforms import Compose, Normalize, ToTensor
class Net(nn.Module):
'''Model (simple CNN adapted from 'PyTorch: A 60 Minute Blitz')'''
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
return self.fc3(x)
fds = None # Cache FederatedDataset
def load_data(partition_id: int, num_partitions: int):
'''Load partition CIFAR10 data.'''
# Only initialize `FederatedDataset` once
global fds
if fds is None:
partitioner = IidPartitioner(num_partitions=num_partitions)
fds = FederatedDataset(
dataset='uoft-cs/cifar10',
partitioners={'train': partitioner},
)
partition = fds.load_partition(partition_id)
# Divide data on each node: 80% train, 20% test
partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
pytorch_transforms = Compose(
[ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)
def apply_transforms(batch):
'''Apply transforms to the partition from FederatedDataset.'''
batch['img'] = [pytorch_transforms(img) for img in batch['img']]
return batch
partition_train_test = partition_train_test.with_transform(apply_transforms)
trainloader = DataLoader(partition_train_test['train'], batch_size=32, shuffle=True)
testloader = DataLoader(partition_train_test['test'], batch_size=32)
return trainloader, testloader
def train(net, trainloader, epochs, device):
'''Train the model on the training set.'''
net.to(device) # move model to GPU if available
criterion = torch.nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
net.train()
running_loss = 0.0
for _ in range(epochs):
for batch in trainloader:
images = batch['img']
labels = batch['label']
optimizer.zero_grad()
loss = criterion(net(images.to(device)), labels.to(device))
loss.backward()
optimizer.step()
running_loss += loss.item()
avg_trainloss = running_loss / len(trainloader)
return avg_trainloss
def test(net, testloader, device):
'''Validate the model on the test set.'''
net.to(device)
criterion = torch.nn.CrossEntropyLoss()
correct, loss = 0, 0.0
with torch.no_grad():
for batch in testloader:
images = batch['img'].to(device)
labels = batch['label'].to(device)
outputs = net(images)
loss += criterion(outputs, labels).item()
correct += (torch.max(outputs.data, 1)[1] == labels).sum().item()
accuracy = correct / len(testloader.dataset)
loss = loss / len(testloader)
return loss, accuracy
def get_weights(net):
return [val.cpu().numpy() for _, val in net.state_dict().items()]
def set_weights(net, parameters):
params_dict = zip(net.state_dict().keys(), parameters)
state_dict = OrderedDict({k: torch.tensor(v) for k, v in params_dict})
net.load_state_dict(state_dict, strict=True)
""", language="python"
)
with steps[5]:
st.subheader("Estratégias de Agregação")
st.markdown("""
O Flower oferece várias estratégias de agregação para combinar as atualizações dos clientes:
""")
strategy_tabs = st.tabs(["FedAvg", "FedAdagrad", "FedAdam", "FedYogi", "Personalizada",'Link para mais estratégias'])
with strategy_tabs[0]:
st.markdown("""
### Federated Averaging (FedAvg)
O FedAvg é a estratégia mais comum, que realiza uma média ponderada dos parâmetros do modelo com base no número de amostras em cada cliente.
""")
st.image("./images/fedavg.png")
st.markdown("[McMaham et al. 2023. Communication-Efficient Learning of Deep Networks from Decentralized Data](https://arxiv.org/pdf/1602.05629)")
st.code("""
# Implementação do FedAvg no servidor
strategy = fl.server.strategy.FedAvg(
fraction_fit=1.0, # Fração de clientes usados em cada round
min_fit_clients=min_clients, # Mínimo de clientes para iniciar o treino
min_available_clients=min_clients, # Mínimo de clientes necessários
)
""", language="python")
with strategy_tabs[1]:
st.markdown("""
### FedAdagrad
O FedAdagrad adapta o algoritmo Adagrad para o cenário federado, ajustando as taxas de aprendizado com base em gradientes anteriores.
""")
st.image('./images/FedAdaGrad-Yogi-Adam.png')
st.markdown("[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)")
st.code("""
# Implementação do FedAdagrad no servidor
strategy = fl.server.strategy.FedAdagrad(
fraction_fit=1.0,
min_fit_clients=min_clients,
min_available_clients=min_clients,
eta=0.1, # Taxa de aprendizado do servidor
eta_l=0.01, # Taxa de aprendizado do cliente
)
""", language="python")
with strategy_tabs[2]:
st.markdown("""
### FedAdam
O FedAdam adapta o otimizador Adam para o cenário federado, incorporando momentos para melhorar a convergência.
""")
st.image('./images/FedAdaGrad-Yogi-Adam.png')
st.markdown("[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)")
st.code("""
# Implementação do FedAdam no servidor
strategy = fl.server.strategy.FedAdam(
fraction_fit=1.0,
min_fit_clients=min_clients,
min_available_clients=min_clients,
eta=0.1, # Taxa de aprendizado do servidor
eta_l=0.01, # Taxa de aprendizado do cliente
beta_1=0.9, # Decaimento exponencial para momentos de primeira ordem
beta_2=0.99, # Decaimento exponencial para momentos de segunda ordem
)
""", language="python")
with strategy_tabs[3]:
st.markdown("""
### FedYogi
O FedYogi é uma variante do FedAdam que utiliza uma atualização diferente para os momentos de segunda ordem para melhor lidar com dados não IID.
""")
st.image('./images/FedAdaGrad-Yogi-Adam.png')
st.markdown("[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)")
st.code("""
# Implementação do FedYogi no servidor
strategy = fl.server.strategy.FedYogi(
fraction_fit=1.0,
min_fit_clients=min_clients,
min_available_clients=min_clients,
eta=0.1, # Taxa de aprendizado do servidor
eta_l=0.01, # Taxa de aprendizado do cliente
beta_1=0.9, # Decaimento exponencial para momentos de primeira ordem
beta_2=0.99, # Decaimento exponencial para momentos de segunda ordem
tau=0.001, # Parâmetro de controle para atualização
)
""", language="python")
with strategy_tabs[4]:
st.markdown("""
### Estratégia Personalizada
""")
st.code('''
# Estratégia personalizada para agregação
class MyStrategy(fl.server.strategy.FedAvg):
''', language="python")
with strategy_tabs[5]:
st.markdown("""
### Link para mais estratégias
Mais estratégias de agregação disponíveis na [documentação oficial](https://flower.dev/docs/strategies.html).
Link das opções de partição de dados: [Flower Partitioner](https://flower.ai/docs/datasets/ref-api/flwr_datasets.partitioner.html)
""")
with steps[6]:
st.subheader("Executando o Sistema")
st.markdown("""
Para executar o sistema de aprendizado federado, precisa iniciar o servidor e os clientes:
""")
st.code("""
# Iniciar o servidor
python server.py --rounds 10 --min_clients 3 --port 8080
# Em terminais diferentes, iniciar os clientes
python client.py --partition 0 --num_partitions 3 --server 127.0.0.1:8080
python client.py --partition 1 --num_partitions 3 --server 127.0.0.1:8080
python client.py --partition 2 --num_partitions 3 --server 127.0.0.1:8080
""", language="bash")
st.markdown("""
Alternativamente, dá para criar um script para automatizar este processo:
""")
st.code("""
#!/bin/bash
# run.sh
# Inicia o servidor em background
python server.py --rounds 10 --min_clients 3 --port 8080 &
SERVER_PID=$!
# Espera o servidor iniciar
sleep 2
# Inicia os clientes
for i in $(seq 0 2); do
python client.py --partition $i --num_partitions 3 --server 127.0.0.1:8080 &
CLIENT_PIDS[$i]=$!
done
# Espera pelo término do servidor
wait $SERVER_PID
# Mata os processos dos clientes se ainda estiverem rodando
for pid in "${CLIENT_PIDS[@]}"; do
kill -0 $pid 2>/dev/null && kill $pid
done
""", language="bash")
with steps[7]:
st.subheader("Arquivo pyproject.toml")
st.markdown("""
O arquivo `pyproject.toml` é usado para gerenciar as dependências do projeto e configurações do ambiente.
""")
st.code("""
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "fl-inmetro"
version = "1.0.0"
description = ""
license = "Apache-2.0"
dependencies = [
"flwr[simulation]>=1.18.0",
"flwr-datasets[vision]>=0.5.0",
"torch==2.5.1",
"torchvision==0.20.1",
]
[tool.hatch.build.targets.wheel]
packages = ["."]
[tool.flwr.app]
publisher = "jwsouza"
[tool.flwr.app.components]
serverapp = "fl_inmetro.server_app:app"
clientapp = "fl_inmetro.client_app:app"
[tool.flwr.app.config]
num-server-rounds = 3
fraction-fit = 0.5
local-epochs = 1
[tool.flwr.federations]
default = "local-simulation"
[tool.flwr.federations.local-simulation]
options.num-supernodes = 10
""", language="toml")
elif secao == "📊 Visualização":
st.title("Processo de Aprendizado Federado")
# visualization_tabs = st.tabs(["Animação do Processo FL", "Convergência do Modelo", "Desempenho por Cliente", "Comparação de Estratégias"])
# with visualization_tabs[0]:
# Criando um gráfico interativo com Plotly
fig = go.Figure()
# Nós
server_pos = [5, 10]
client_positions = [[2, 5], [5, 5], [8, 5]]
data_positions = [[2, 0], [5, 0], [8, 0]]
# Adicionando nós
fig.add_trace(go.Scatter(
x=[server_pos[0]],
y=[server_pos[1]],
mode='markers+text',
marker=dict(symbol='square', size=40, color='red'),
text=['Servidor'],
textposition='top center',
name='Servidor'
))
fig.add_trace(go.Scatter(
x=[pos[0] for pos in client_positions],
y=[pos[1] for pos in client_positions],
mode='markers+text',
marker=dict(symbol='circle', size=30, color='green'),
text=['Cliente 1', 'Cliente 2', 'Cliente 3'],
textposition='middle right',
name='Clientes'
))
fig.add_trace(go.Scatter(
x=[pos[0] for pos in data_positions],
y=[pos[1] for pos in data_positions],
mode='markers+text',
marker=dict(symbol='diamond', size=20, color='blue'),
text=['Dados 1', 'Dados 2', 'Dados 3'],
textposition='bottom center',
name='Dados'
))
# Adicionando linhas de conexão
for i, client_pos in enumerate(client_positions):
# Linha do servidor para o cliente (modelo global)
fig.add_trace(go.Scatter(
x=[server_pos[0], client_pos[0]],
y=[server_pos[1], client_pos[1]],
mode='lines+text',
line=dict(width=2, color='red', dash='dash'),
text=['Modelo Global'],
textposition='top right',
showlegend=False
))
# Linha do cliente para o servidor (atualizações)
fig.add_trace(go.Scatter(
x=[client_pos[0], server_pos[0]],
y=[client_pos[1], server_pos[1]],
mode='lines+text',
line=dict(width=2, color='green', dash='dot'),
text=['Atualizações'],
textposition='bottom left',
showlegend=False
))
# Linha dos dados para o cliente (treino local)
fig.add_trace(go.Scatter(
x=[data_positions[i][0], client_pos[0]],
y=[data_positions[i][1], client_pos[1]],
mode='lines+text',
line=dict(width=2, color='blue'),
text=['Treino Local'],
textposition='middle left',
showlegend=False
))
# Layout
fig.update_layout(
title='Fluxo de Aprendizado Federado Interativo',
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
width=800,
height=500,
legend=dict(x=0, y=1),
hovermode='closest'
)
st.plotly_chart(fig)
if secao == "📈 Modelos para EVs":
st.caption("Zhang et al. (2024). Modelos de Consumo de Energia para Veículos Elétricos em Transporte Urbano. Renewable Energy")
st.markdown("""
**Objetivo**: Prever consumo energético (CE) de VEs em cenários urbanos:
""")
st.latex(r"""
\min_{\theta} \mathcal{L}(\theta) = \mathbb{E}_{(\mathbf{x},y) \sim \mathcal{P}} [\ell(f_\theta(\mathbf{x}), y)]
""")
st.markdown("""
Onde:
- $y$: consumo energético observado
- $f_\theta$: modelo de previsão
""")
st.header("Abordagens Tradicionais")
st.subheader("Modelos de Dinâmica Veicular")
st.latex(r"""
F_t = \underbrace{\delta Ma}_{\text{Inércia}} + \underbrace{Mg\sin\alpha}_{\text{Inclinação}} + \underbrace{Mg f \cos\alpha}_{\text{Rolamento}} + \underbrace{\frac{1}{2}\rho C_d A v^2}_{\text{Aerodinâmica}}
""")
st.latex(r"""
P_{\text{total}} = (F_t \cdot v)/\eta_{\text{motor}} + P_{\text{aux}}
""")
st.markdown("""
**Limitações**:
- Requer 12+ parâmetros específicos (Tabela 1)
- Não captura efeitos não-lineares em condições urbanas complexas
""")
st.subheader("Modelos Estatísticos")
st.latex(r"""
\text{CE} = \beta_0 + \sum_{i=1}^k \beta_i X_i + \epsilon \quad \text{(Regressão Linear)}
""")
st.latex(r"""
\text{UEC}(BDR) = a_0 + a_1 \cdot BDR + a_2 \cdot BDR^2 \quad \text{(Degradação de Bateria)}
""")
st.markdown("""
**Problemas**: Pressupõe relações lineares e não considera interações complexas entre variáveis
""")
st.subheader("Modelos Principais")
st.markdown("""
- **XGBoost/LightGBM**:
""")
st.latex(r"""
\mathcal{L}^{(t)} = \sum_{i=1}^n \ell(y_i, \hat{y}_i^{(t-1)} + f_t(\mathbf{x}_i)) + \Omega(f_t)
""")
st.markdown("""
- **Random Forest**: Média de $B$ árvores de decisão
""")
st.header("Redes Neurais para Alta Precisão")
st.markdown("""
**LSTM**:
""")
st.latex(r"""
\begin{aligned}
i_t &= \sigma(W_{ix} x_t + W_{ih} h_{t-1} + b_i) \\
c_t &= f_t \odot c_{t-1} + i_t \odot \tanh(W_{cx} x_t + W_{ch} h_{t-1} + b_c) \\
h_t &= o_t \odot \tanh(c_t)
\end{aligned}
""")
st.markdown("""
**Transformer**:
""")
st.latex(r"""
\text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
""")
st.subheader("4.2 Desempenho Comparativo")
st.markdown("""
| Modelo | RMSE (kWh) | MAPE (%) | Limitações |
|-----------------|------------|----------|--------------------------|
| Dinâmica Veicular | 6.06 | 14.10 | Parâmetros fixos |
| XGBoost | 3.94 | 9.31 | Menor capacidade não-linear |
| LSTM | 3.61 | 8.69 | Custo computacional alto |
| Transformer | 2.98 | 7.21 | Dados de treino massivos |
""")
if secao == "🍓 Integração Raspberry Pi":
st.markdown("""
1. **Configurar acesso SSH**
- Habilite o SSH no Pi (`raspi-config` → *Interfacing Options* → *SSH*).
- Anote o endereço IP do Pi na sua rede.
2. **Obter o código-fonte**
- **Via Git**:
```bash
git clone https://github.com/SEU_USUARIO/SEU_PROJETO.git
cd SEU_PROJETO
```
- **Via SCP/RSYNC** (se não usar Git):
```bash
scp -r caminho/local/SEU_PROJETO pi@IP_DO_PI:~/SEU_PROJETO
```
3. **Instalar dependências no Pi**
```bash
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
4. **Copiar arquivos estáticos**
- Certifique-se de que a pasta `images/` foi clonada/copied e que os paths relativos estejam corretos.
5. **Configurar serviço de inicialização**
- Crie um serviço systemd em `/etc/systemd/system/fl-client.service` apontando para o comando de execução no seu virtualenv.
- Habilite e inicie:
```bash
sudo systemctl enable fl-client
sudo systemctl start fl-client
```
6. **Testar localmente**
```bash
source venv/bin/activate
python client_pi.py
```
7. **Automatizar atualizações**
- No CI/CD, adicione um passo para:
```bash
ssh pi@IP_DO_PI \
'cd ~/SEU_PROJETO && git pull && source venv/bin/activate && pip install -r requirements.txt && systemctl restart fl-client'
```
""", unsafe_allow_html=False)
if secao == "📚 Artigos":
st.markdown(
"""
### Referências
- FedAVG
[McMaham et al. 2023. Communication-Efficient Learning of Deep Networks from Decentralized Data](https://arxiv.org/pdf/1602.05629)
- ADAGRAD, ADAM, YOGI
[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)
- Privacdiade e Segurança
[Bonawitz et al. Practical Secure Aggregation for Privacy-Preserving Machine Learning](https://arxiv.org/pdf/1611.04482)
[Abadi et al. Deep Learning with Differential Privacy](https://arxiv.org/pdf/1607.00133)
"""
)