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) """ )