Spaces:
Sleeping
Sleeping
| 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("<h2 style='text-align: center;'>Aprendizado Federado com Flower</h2>", 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) | |
| """ | |
| ) |