Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,116 +1,1042 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import matplotlib.pyplot as plt
|
| 3 |
-
import networkx as nx
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
import networkx as nx
|
| 4 |
+
import numpy as np
|
| 5 |
+
from matplotlib.animation import FuncAnimation
|
| 6 |
+
import pandas as pd
|
| 7 |
+
import time
|
| 8 |
+
import plotly.graph_objects as go
|
| 9 |
+
import plotly.express as px
|
| 10 |
+
from io import BytesIO
|
| 11 |
+
|
| 12 |
+
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")
|
| 13 |
+
|
| 14 |
+
st.markdown("<h2 style='text-align: center;'>Aprendizado Federado com Flower</h2>", unsafe_allow_html=True)
|
| 15 |
+
st.sidebar.image("images/logo_inmetro.jpg", width=200)
|
| 16 |
+
st.sidebar.title("FL Inmetro")
|
| 17 |
+
|
| 18 |
+
secao = st.sidebar.radio("Ir para:", ["🏠 Início", "🖥️ Implementação", "📊 Visualização", "🍓 Integração Raspberry Pi", "📈 Modelos para EVs", "📚 Artigos", "ℹ️ Sobre"])
|
| 19 |
+
|
| 20 |
+
if secao == "🏠 Início":
|
| 21 |
+
|
| 22 |
+
tab1, tab2, tab3 = st.tabs(["📘 Aprendizado Centralizado", "📗 Aprendizado Federado", "O Projeto"])
|
| 23 |
+
|
| 24 |
+
with tab1:
|
| 25 |
+
st.markdown("## 📘 Aprendizado Centralizado: Fundamentos Teóricos")
|
| 26 |
+
|
| 27 |
+
# Definição formal do problema
|
| 28 |
+
st.markdown("### 🔍 Definição Formal do Problema")
|
| 29 |
+
st.latex(r"""
|
| 30 |
+
\begin{aligned}
|
| 31 |
+
&\text{Dado:} \\
|
| 32 |
+
&\quad \text{Conjunto de dados } \mathcal{D} = \{(\mathbf{x}_i, y_i)\}_{i=1}^N \\
|
| 33 |
+
&\quad \text{Modelo paramétrico } f_\theta: \mathcal{X} \to \mathcal{Y} \\
|
| 34 |
+
&\quad \text{Função perda } \ell: \mathcal{Y} \times \mathcal{Y} \to \mathbb{R}^+ \\
|
| 35 |
+
\\
|
| 36 |
+
&\text{Objetivo:} \\
|
| 37 |
+
&\quad \min_{\theta \in \mathbb{R}^d} \mathcal{L}(\theta) = \mathbb{E}_{(\mathbf{x},y) \sim \mathcal{P}} [\ell(f_\theta(\mathbf{x}), y)] \\
|
| 38 |
+
&\quad \text{com aproximação empírica:} \\
|
| 39 |
+
&\quad \hat{\mathcal{L}}(\theta) = \frac{1}{N} \sum_{i=1}^N \ell(f_\theta(\mathbf{x}_i), y_i)
|
| 40 |
+
\end{aligned}
|
| 41 |
+
""")
|
| 42 |
+
|
| 43 |
+
st.markdown("""
|
| 44 |
+
Onde:
|
| 45 |
+
- $\mathcal{P}$ é a distribuição de dados subjacente
|
| 46 |
+
- $\mathbf{x}_i \in \mathbb{R}^m$ são features de entrada
|
| 47 |
+
- $y_i \in \mathcal{Y}$ são targets (contínuos ou discretos)
|
| 48 |
+
- $\theta$ são parâmetros do modelo (e.g., pesos de rede neural)
|
| 49 |
+
""")
|
| 50 |
+
|
| 51 |
+
# Fundamentos de otimização
|
| 52 |
+
st.markdown("#### Algoritmo: Gradiente Descendente (GD)")
|
| 53 |
+
st.latex(r"""
|
| 54 |
+
\begin{aligned}
|
| 55 |
+
&\textbf{Input: } \theta_0 \text{ (inicialização)}, \eta > 0 \text{ (taxa aprendizado)}, T \\
|
| 56 |
+
&\textbf{For } t = 0 \text{ to } T-1: \\
|
| 57 |
+
&\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) \\
|
| 58 |
+
&\quad \theta_{t+1} = \theta_t - \eta g_t
|
| 59 |
+
\end{aligned}
|
| 60 |
+
""")
|
| 61 |
+
|
| 62 |
+
st.markdown("#### Formulação Estocástica (SGD)")
|
| 63 |
+
st.latex(r"""
|
| 64 |
+
\begin{aligned}
|
| 65 |
+
&\textbf{Input: } \theta_0, \eta > 0, T, \text{ tamanho lote } B \\
|
| 66 |
+
&\textbf{For } t = 0 \text{ to } T-1: \\
|
| 67 |
+
&\quad \text{Selecione } \mathcal{B}_t \subset \{1,\dots,N\} \text{ com } |\mathcal{B}_t| = B \\
|
| 68 |
+
&\quad \tilde{g}_t = \frac{1}{B} \sum_{i \in \mathcal{B}_t} \nabla_\theta \ell(f_{\theta_t}(\mathbf{x}_i), y_i) \\
|
| 69 |
+
&\quad \theta_{t+1} = \theta_t - \eta \tilde{g}_t
|
| 70 |
+
\end{aligned}
|
| 71 |
+
""")
|
| 72 |
+
|
| 73 |
+
# Análise de convergência
|
| 74 |
+
st.markdown("### Convergência")
|
| 75 |
+
st.latex(r"""
|
| 76 |
+
\text{Sob as condições:} \\
|
| 77 |
+
\begin{array}{ll}
|
| 78 |
+
(1) & \mathcal{L} \text{ é } \mu\text{-fortemente convexa} \\
|
| 79 |
+
(2) & \mathbb{E}[\|\nabla_\theta \ell(\cdot)\|^2_2] \leq G^2 \\
|
| 80 |
+
(3) & \eta_t = \frac{c}{t} \text{ (decay de taxa de aprendizado)}
|
| 81 |
+
\end{array}
|
| 82 |
+
""")
|
| 83 |
+
st.latex(r"""
|
| 84 |
+
\text{GD atinge:} \\
|
| 85 |
+
\mathcal{L}(\theta_T) - \mathcal{L}(\theta^*) \leq \mathcal{O}\left(\frac{1}{T}\right)
|
| 86 |
+
""")
|
| 87 |
+
st.latex(r"""
|
| 88 |
+
\text{SGD atinge:} \\
|
| 89 |
+
\mathbb{E}[\mathcal{L}(\theta_T)] - \mathcal{L}(\theta^*) \leq \mathcal{O}\left(\frac{1}{\sqrt{T}}\right)
|
| 90 |
+
""")
|
| 91 |
+
|
| 92 |
+
# Exemplo numérico
|
| 93 |
+
st.markdown("### 🔢 Exemplo Numérico")
|
| 94 |
+
st.markdown("Considere regressão linear com perda MSE:")
|
| 95 |
+
st.latex(r"""
|
| 96 |
+
\ell(f_\theta(\mathbf{x}), y) = \frac{1}{2} (\theta^\top \mathbf{x} - y)^2
|
| 97 |
+
""")
|
| 98 |
+
st.markdown("Gradiente para um único ponto:")
|
| 99 |
+
st.latex(r"""
|
| 100 |
+
\nabla_\theta \ell = (\theta^\top \mathbf{x} - y) \mathbf{x}
|
| 101 |
+
""")
|
| 102 |
+
|
| 103 |
+
st.markdown("Atualização de GD em tempo real:")
|
| 104 |
+
theta = st.slider("Parâmetro θ", -2.0, 2.0, 0.5, 0.1)
|
| 105 |
+
eta = st.slider("Taxa aprendizado η", 0.01, 1.0, 0.1, 0.01)
|
| 106 |
+
|
| 107 |
+
# Cálculo de exemplo
|
| 108 |
+
x, y = 2.0, 3.0 # Dado fixo para demonstração
|
| 109 |
+
loss = 0.5 * (theta*x - y)**2
|
| 110 |
+
gradient = (theta*x - y)*x
|
| 111 |
+
new_theta = theta - eta*gradient
|
| 112 |
+
|
| 113 |
+
st.latex(fr"""
|
| 114 |
+
\begin{{aligned}}
|
| 115 |
+
\theta^{{(t)}} &= {theta:.2f} \\
|
| 116 |
+
\nabla_\theta \mathcal{{L}} &= ({theta:.2f} \times {x} - {y}) \times {x} = {gradient:.2f} \\
|
| 117 |
+
\theta^{{(t+1)}} &= {theta:.2f} - {eta} \times {gradient:.2f} = {new_theta:.2f}
|
| 118 |
+
\end{{aligned}}
|
| 119 |
+
""")
|
| 120 |
+
|
| 121 |
+
with tab2:
|
| 122 |
+
st.markdown("## Aprendizado Federado: Fundamentos Teóricos")
|
| 123 |
+
|
| 124 |
+
# Definição formal do problema
|
| 125 |
+
st.markdown("### Formulação Matemática do Problema")
|
| 126 |
+
st.latex(r"""
|
| 127 |
+
\begin{aligned}
|
| 128 |
+
&\text{Dado:} \\
|
| 129 |
+
&\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} \\
|
| 130 |
+
&\quad \text{Modelo paramétrico } f_\theta: \mathcal{X} \to \mathcal{Y} \\
|
| 131 |
+
&\quad \text{Função perda } \ell: \mathcal{Y} \times \mathcal{Y} \to \mathbb{R}^+ \\
|
| 132 |
+
\\
|
| 133 |
+
&\text{Objetivo Federado:} \\
|
| 134 |
+
&\quad \min_{\theta \in \mathbb{R}^d} \mathcal{L}(\theta) = \sum_{k=1}^K \frac{n_k}{n} \mathcal{L}_k(\theta) \\
|
| 135 |
+
&\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) \\
|
| 136 |
+
&\quad n = \sum_{k=1}^K n_k \quad \text{(tamanho total do dataset)}
|
| 137 |
+
\end{aligned}
|
| 138 |
+
""")
|
| 139 |
+
|
| 140 |
+
st.markdown("""
|
| 141 |
+
Onde:
|
| 142 |
+
- $\mathcal{D}_k$ permanece localizado no dispositivo do cliente $k$
|
| 143 |
+
- $n_k$ é o número de amostras do cliente $k$
|
| 144 |
+
- O objetivo global é uma média ponderada dos objetivos locais
|
| 145 |
+
""")
|
| 146 |
+
|
| 147 |
+
# Algoritmo FedAvg detalhado
|
| 148 |
+
st.markdown("### Algoritmo Federado: FedAvg (Federated Averaging)")
|
| 149 |
+
st.latex(r"""
|
| 150 |
+
\begin{aligned}
|
| 151 |
+
1. & \textbf{Inicialização:} \\
|
| 152 |
+
& \quad \theta^{(0)} \leftarrow \text{parâmetros iniciais} \\
|
| 153 |
+
2. & \textbf{for } t = 0 \text{ to } T-1 \textbf{ do:} \\
|
| 154 |
+
3. & \quad \text{Servidor seleciona subconjunto } S_t \subseteq \{1,\dots,K\} \\
|
| 155 |
+
4. & \quad \text{Servidor transmite } \theta^{(t)} \text{ para todos os clientes } k \in S_t \\
|
| 156 |
+
5. & \quad \textbf{for cada cliente } k \in S_t \textbf{ paralelamente do:} \\
|
| 157 |
+
6. & \quad\quad \theta_k^{(t,0)} \leftarrow \theta^{(t)} \\
|
| 158 |
+
7. & \quad\quad \textbf{for } \tau = 0 \text{ to } E-1 \textbf{ do:} \\
|
| 159 |
+
8. & \quad\quad\quad \text{Selecione lote } \mathcal{B}_{\tau}^k \subseteq \mathcal{D}_k \\
|
| 160 |
+
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) \\
|
| 161 |
+
10. & \quad\quad\quad \theta_k^{(t,\tau+1)} = \theta_k^{(t,\tau)} - \eta_k g_k^{(\tau)} \\
|
| 162 |
+
11. & \quad\quad \text{Cliente } k \text{ envia } \Delta_k^{(t)} = \theta_k^{(t,E)} - \theta^{(t)} \text{ para servidor} \\
|
| 163 |
+
12. & \quad \text{Servidor atualiza: } \\
|
| 164 |
+
& \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)}
|
| 165 |
+
\end{aligned}
|
| 166 |
+
""")
|
| 167 |
+
|
| 168 |
+
st.markdown("""
|
| 169 |
+
Parâmetros-chave:
|
| 170 |
+
- $E$: Número de épocas locais
|
| 171 |
+
- $\eta_k$: Taxa de aprendizado do cliente $k$
|
| 172 |
+
- $S_t$: Subconjunto de clientes na rodada $t$
|
| 173 |
+
- $\Delta_k^{(t)}$: Atualização do cliente $k$
|
| 174 |
+
""")
|
| 175 |
+
|
| 176 |
+
# Diagrama de arquitetura
|
| 177 |
+
st.markdown("### Arquitetura do Sistema Federado")
|
| 178 |
+
st.image("./images/fedlr_diagram.png", width=700)
|
| 179 |
+
st.markdown("""
|
| 180 |
+
Fluxo de operação:
|
| 181 |
+
1. Servidor inicializa modelo global $\theta^{(0)}$
|
| 182 |
+
2. A cada rodada $t$:
|
| 183 |
+
a. Servidor seleciona subconjunto de clientes $S_t$
|
| 184 |
+
b. Envia modelo global atual para clientes selecionados
|
| 185 |
+
c. Cada cliente atualiza modelo localmente com seus dados
|
| 186 |
+
d. Clientes enviam atualizações de parâmetros para servidor
|
| 187 |
+
e. Servidor agrega atualizações e calcula novo modelo global
|
| 188 |
+
""")
|
| 189 |
+
|
| 190 |
+
# Teoria de convergência
|
| 191 |
+
st.markdown("### Análise de Convergência")
|
| 192 |
+
st.markdown("#### Hipóteses Fundamentais")
|
| 193 |
+
st.latex(r"""
|
| 194 |
+
\begin{array}{ll}
|
| 195 |
+
\text{(A1)} & \mathcal{L} \text{ é } L\text{-suave: } \|\nabla\mathcal{L}(\theta) - \nabla\mathcal{L}(\theta')\| \leq L\|\theta - \theta'\| \\
|
| 196 |
+
\text{(A2)} & \text{Variância limitada: } \mathbb{E}_{k} \|\nabla\mathcal{L}_k(\theta) - \nabla\mathcal{L}(\theta)\|^2 \leq \sigma^2 \\
|
| 197 |
+
\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 \\
|
| 198 |
+
\text{(A4)} & \text{Gradiente limitado: } \mathbb{E} \|g_k^{(\tau)}\|^2 \leq G^2
|
| 199 |
+
\end{array}
|
| 200 |
+
""")
|
| 201 |
+
|
| 202 |
+
st.markdown("#### Teorema de Convergência (FedAvg)")
|
| 203 |
+
st.latex(r"""
|
| 204 |
+
\text{Sob as hipóteses (A1)-(A4), com } \eta_k = \eta \text{ e seleção uniforme de clientes:}
|
| 205 |
+
""")
|
| 206 |
+
st.latex(r"""
|
| 207 |
+
\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)
|
| 208 |
+
""")
|
| 209 |
+
st.markdown("""
|
| 210 |
+
Onde:
|
| 211 |
+
- $M = |S_t|$ (tamanho do subconjunto de clientes)
|
| 212 |
+
- $\mathcal{L}^*$ é o valor ótimo da função de perda
|
| 213 |
+
- $\delta$ mede o grau de heterogeneidade dos dados
|
| 214 |
+
""")
|
| 215 |
+
|
| 216 |
+
# Desafios técnicos detalhados
|
| 217 |
+
st.markdown("### Desafios Técnicos e Soluções")
|
| 218 |
+
|
| 219 |
+
st.markdown("#### 1. Heterogeneidade de Dados (não-IID)")
|
| 220 |
+
st.latex(r"""
|
| 221 |
+
\text{Definição: } \exists k \neq j: \mathbb{P}_k(\mathbf{x}, y) \neq \mathbb{P}_j(\mathbf{x}, y)
|
| 222 |
+
""")
|
| 223 |
+
st.latex(r"""
|
| 224 |
+
\text{Problemas: }\\
|
| 225 |
+
\text{Viés na agregação: } \mathbb{E}[\theta^{(t+1)}] \neq \theta_{\text{ótimo}}\\
|
| 226 |
+
\text{Divergência do modelo: } \|\theta^{(t)} - \theta^*\| \text{ cresce com t}\\
|
| 227 |
+
|
| 228 |
+
\text{Soluções teóricas: }\\
|
| 229 |
+
\text{1. Regularização proximal: } \min_\theta \mathcal{L}_k(\theta) + \frac{\mu}{2} \|\theta - \theta^{(t)}\|^2\\
|
| 230 |
+
\text{2. Controle de variância: } \Delta_k^{(t)} \leftarrow \Delta_k^{(t)} - \beta (\Delta_k^{(t)} - \Delta^{(t-1)})
|
| 231 |
+
""")
|
| 232 |
+
|
| 233 |
+
st.markdown("#### 3. Segurança e Privacidade")
|
| 234 |
+
st.latex(r"""
|
| 235 |
+
\begin{array}{c}
|
| 236 |
+
\text{Ataque: } \Delta_k^{(t)} = \Delta_{\text{malicioso}} \\
|
| 237 |
+
\text{Defesa: } \theta^{(t+1)} = \text{AGG}_{\gamma} \left( \{ \Delta_k^{(t)} \}_{k \in S_t} \right)
|
| 238 |
+
\end{array}
|
| 239 |
+
""")
|
| 240 |
+
|
| 241 |
+
st.latex(r"""
|
| 242 |
+
\text{Mecanismos de defesa: }\\
|
| 243 |
+
\text{2. DP-SGD: } g_k^{(\tau)} \leftarrow \text{Clip}(g_k^{(\tau)}, C) + \mathcal{N}(0, \sigma^2 I)\\
|
| 244 |
+
""")
|
| 245 |
+
st.markdown("""
|
| 246 |
+
O **DP-SGD** adapta o SGD clássico para fornecer garantias de **privacidade diferencial**,
|
| 247 |
+
limitando o impacto de cada amostra e adicionando ruído calibrado.
|
| 248 |
+
""")
|
| 249 |
+
|
| 250 |
+
# 1. Cálculo de gradientes por amostra
|
| 251 |
+
st.markdown("**1. Cálculo de gradientes por amostra**")
|
| 252 |
+
st.latex(r"""
|
| 253 |
+
g_i \;=\; \nabla_\theta \,\ell\bigl(f_\theta(x_i),\,y_i\bigr)
|
| 254 |
+
""")
|
| 255 |
+
|
| 256 |
+
# 2. Clipping de gradientes
|
| 257 |
+
st.markdown("**2. Clipping de gradientes**: limita a norma de cada gradiente a um máximo \(C\).")
|
| 258 |
+
st.latex(r"""
|
| 259 |
+
\bar g_i \;=\; \frac{g_i}{\max\!\bigl(1,\;\|g_i\|/C\bigr)}
|
| 260 |
+
""")
|
| 261 |
+
|
| 262 |
+
# 3. Soma e adição de ruído
|
| 263 |
+
st.markdown("**3. Soma e adição de ruído**: soma os gradientes recortados e adiciona ruído Gaussiano.")
|
| 264 |
+
st.latex(r"""
|
| 265 |
+
\tilde g \;=\; \frac{1}{B}\sum_{i=1}^B \bar g_i \;+\; \mathcal{N}\!\bigl(0,\;\sigma^2 C^2 I\bigr)
|
| 266 |
+
""")
|
| 267 |
+
|
| 268 |
+
# 4. Atualização de parâmetros
|
| 269 |
+
st.markdown("**4. Atualização de parâmetros** realiza o passo de descida de gradiente com o gradiente privatizado.")
|
| 270 |
+
st.latex(r"""
|
| 271 |
+
\theta \;\leftarrow\; \theta \;-\;\eta\,\tilde g
|
| 272 |
+
""")
|
| 273 |
+
|
| 274 |
+
st.markdown("---")
|
| 275 |
+
st.markdown("""
|
| 276 |
+
- **$C$** (clipping norm): controla o máximo de contribuição de uma única amostra.
|
| 277 |
+
- **$\sigma$** (noise multiplier): regula a intensidade do ruído; maior $\sigma$ → mais privacidade (menor $\epsilon$) mas potencialmente pior desempenho.
|
| 278 |
+
- **Moments Accountant**: método eficiente para juntar os gastos de privacidade de cada minibatch.
|
| 279 |
+
""")
|
| 280 |
+
|
| 281 |
+
# Comparação com aprendizado centralizado
|
| 282 |
+
st.markdown("### Comparação Teórica: Federado vs Centralizado")
|
| 283 |
+
st.latex(r"""
|
| 284 |
+
\begin{array}{c|c|c}
|
| 285 |
+
\text{Propriedade} & \text{Centralizado} & \text{Federado} \\
|
| 286 |
+
\hline
|
| 287 |
+
\text{Acesso aos dados} & \text{Completo} & \text{Nenhum} \\
|
| 288 |
+
\text{Comunicação} & \mathcal{O}(N \times d) & \mathcal{O}(T \times |S_t| \times d) \\
|
| 289 |
+
\text{Convergência} & \mathcal{O}(1/T) & \mathcal{O}(1/\sqrt{T}) \\
|
| 290 |
+
\text{Privacidade} & \text{Baixa} & \text{Alta (com DP)} \\
|
| 291 |
+
\text{Robustez} & \text{Alta} & \text{Controlável} \\
|
| 292 |
+
\text{Escalabilidade} & \text{Limitada} & \text{Alta}
|
| 293 |
+
\end{array}
|
| 294 |
+
""")
|
| 295 |
+
st.markdown("""
|
| 296 |
+
Onde:
|
| 297 |
+
- $d$: Dimensão dos parâmetros do modelo
|
| 298 |
+
- $N$: Número total de amostras de dados
|
| 299 |
+
- $T$: Número de rodadas de comunicação
|
| 300 |
+
""")
|
| 301 |
+
|
| 302 |
+
st.markdown("""
|
| 303 |
+
**Benefícios do aprendizado federado:**
|
| 304 |
+
- Preserva privacidade dos padrões de consumo
|
| 305 |
+
- Reduz tráfego de rede (dados permanecem locais)
|
| 306 |
+
- Permite personalização local do modelo
|
| 307 |
+
""")
|
| 308 |
+
|
| 309 |
+
with tab3:
|
| 310 |
+
st.title("Aprendizado Federado para Previsão de Demanda Energética")
|
| 311 |
+
|
| 312 |
+
col1, col2 = st.columns(2)
|
| 313 |
+
|
| 314 |
+
with col1:
|
| 315 |
+
st.image("images/federated_learning.png", caption="Conceito de Aprendizado Federado")
|
| 316 |
+
|
| 317 |
+
with col2:
|
| 318 |
+
st.markdown("""
|
| 319 |
+
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:
|
| 320 |
+
|
| 321 |
+
- **Privacidade de dados**: Os dados nunca saem dos dispositivos locais
|
| 322 |
+
- **Eficiência de comunicação**: Apenas parâmetros do modelo são compartilhados
|
| 323 |
+
- **Treinamento colaborativo**: Múltiplos dispositivos contribuem para um modelo global
|
| 324 |
+
- **Conformidade regulatória**: Ajuda no cumprimento de leis como LGPD e GDPR
|
| 325 |
+
""")
|
| 326 |
+
|
| 327 |
+
st.markdown("""
|
| 328 |
+
O aprendizado federado é especialmente adequado para prever a demanda energética porque:
|
| 329 |
+
|
| 330 |
+
- Permite análise de dados sensíveis de consumo sem expô-los
|
| 331 |
+
- Possibilita a colaboração entre múltiplos consumidores/medidores
|
| 332 |
+
- Reduz o tráfego de rede ao evitar a transferência de dados brutos
|
| 333 |
+
- Permite treinamento em dispositivos de borda (edge computing)
|
| 334 |
+
- Pode aperceiçoar modelos atuais que utilizam análise centralizada de dados
|
| 335 |
+
|
| 336 |
+
## Objetivos do Projeto
|
| 337 |
+
|
| 338 |
+
1. Implementar um sistema de aprendizado federado usando Flower
|
| 339 |
+
2. Aplicar técnicas avançadas de agregação para otimizar o aprendizado
|
| 340 |
+
3. Integrar com dispositivos Raspberry Pi para coleta de dados em tempo real
|
| 341 |
+
4. Desenvolver modelos de regressão para previsão de demanda energética
|
| 342 |
+
""")
|
| 343 |
+
|
| 344 |
+
elif secao == "🖥️ Implementação":
|
| 345 |
+
st.title("Implementação de Aprendizado Federado com Flower")
|
| 346 |
+
|
| 347 |
+
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'])
|
| 348 |
+
|
| 349 |
+
with steps[0]:
|
| 350 |
+
st.subheader("Instalação do Flower")
|
| 351 |
+
st.code("""
|
| 352 |
+
# Instalação via pip
|
| 353 |
+
pip install flwr
|
| 354 |
+
flwr new
|
| 355 |
+
flwr run -e . # instala as dependências que vem no arquivo pyproject.toml
|
| 356 |
+
flwr run .
|
| 357 |
+
|
| 358 |
+
""", language="bash")
|
| 359 |
+
|
| 360 |
+
st.info("No caso de ambientes virtuais para isolar as dependências:")
|
| 361 |
+
st.code("""
|
| 362 |
+
# Criar e ativar ambiente virtual
|
| 363 |
+
python -m venv fl_env
|
| 364 |
+
source fl_env/bin/activate # Linux/Mac
|
| 365 |
+
fl_env\\Scripts\\activate # Windows
|
| 366 |
+
""", language="bash")
|
| 367 |
+
|
| 368 |
+
with steps[1]:
|
| 369 |
+
st.subheader("Estrutura Básica do Projeto")
|
| 370 |
+
st.code("""
|
| 371 |
+
fl_project/
|
| 372 |
+
├── server.py # Servidor Flower
|
| 373 |
+
├── client.py # Implementação do cliente
|
| 374 |
+
├── model.py # Definição do modelo ML
|
| 375 |
+
├── data_loader.py # Carregamento e pré-processamento de dados
|
| 376 |
+
├── utils/
|
| 377 |
+
│ ├── __init__.py
|
| 378 |
+
│ ├── metrics.py # Funções de avaliação de desempenho
|
| 379 |
+
│ └── visualization.py # Visualização de resultados
|
| 380 |
+
├── config.py # Configurações do sistema
|
| 381 |
+
├── requirements.txt # Dependências do projeto
|
| 382 |
+
└── run.sh # Script para iniciar servidor e clientes
|
| 383 |
+
|── pyproject.toml # Configurações do projeto e dependências
|
| 384 |
+
""", language="text")
|
| 385 |
+
|
| 386 |
+
with steps[2]:
|
| 387 |
+
st.subheader("Implementação do Servidor")
|
| 388 |
+
st.markdown("""
|
| 389 |
+
O servidor Flower é responsável por coordenar o treinamento, distribuir o modelo global e agregar as atualizações dos clientes.
|
| 390 |
+
""")
|
| 391 |
+
|
| 392 |
+
st.code('''
|
| 393 |
+
from flwr.common import Context, ndarrays_to_parameters
|
| 394 |
+
from flwr.server import ServerApp, ServerAppComponents, ServerConfig
|
| 395 |
+
from flwr.server.strategy import FedAvg
|
| 396 |
+
from fl_inmetro.task import Net, get_weights
|
| 397 |
+
|
| 398 |
+
def server_fn(context: Context):
|
| 399 |
+
# Read from config
|
| 400 |
+
num_rounds = context.run_config["num-server-rounds"]
|
| 401 |
+
fraction_fit = context.run_config["fraction-fit"]
|
| 402 |
+
|
| 403 |
+
# Initialize model parameters
|
| 404 |
+
ndarrays = get_weights(Net())
|
| 405 |
+
parameters = ndarrays_to_parameters(ndarrays)
|
| 406 |
+
|
| 407 |
+
# Define strategy
|
| 408 |
+
strategy = FedAvg(
|
| 409 |
+
fraction_fit=fraction_fit,
|
| 410 |
+
fraction_evaluate=1.0,
|
| 411 |
+
min_available_clients=2,
|
| 412 |
+
initial_parameters=parameters,
|
| 413 |
+
)
|
| 414 |
+
config = ServerConfig(num_rounds=num_rounds)
|
| 415 |
+
|
| 416 |
+
return ServerAppComponents(strategy=strategy, config=config)
|
| 417 |
+
|
| 418 |
+
# Create ServerApp
|
| 419 |
+
app = ServerApp(server_fn=server_fn)
|
| 420 |
+
''', language="python")
|
| 421 |
+
|
| 422 |
+
with steps[3]:
|
| 423 |
+
st.subheader("Implementação do Cliente")
|
| 424 |
+
st.markdown("""
|
| 425 |
+
O cliente Flower é implementado nos dispositivos onde os dados estão armazenados.
|
| 426 |
+
Ele treina o modelo localmente e envia atualizações para o servidor.
|
| 427 |
+
""")
|
| 428 |
+
|
| 429 |
+
st.code('''
|
| 430 |
+
import torch
|
| 431 |
+
from flwr.client import ClientApp, NumPyClient
|
| 432 |
+
from flwr.common import Context
|
| 433 |
+
from fl_inmetro.task import Net, get_weights, load_data, set_weights, test, train
|
| 434 |
+
|
| 435 |
+
# Define Flower Client and client_fn
|
| 436 |
+
class FlowerClient(NumPyClient):
|
| 437 |
+
def __init__(self, net, trainloader, valloader, local_epochs):
|
| 438 |
+
self.net = net
|
| 439 |
+
self.trainloader = trainloader
|
| 440 |
+
self.valloader = valloader
|
| 441 |
+
self.local_epochs = local_epochs
|
| 442 |
+
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 443 |
+
self.net.to(self.device)
|
| 444 |
+
|
| 445 |
+
def fit(self, parameters, config):
|
| 446 |
+
set_weights(self.net, parameters)
|
| 447 |
+
train_loss = train(
|
| 448 |
+
self.net,
|
| 449 |
+
self.trainloader,
|
| 450 |
+
self.local_epochs,
|
| 451 |
+
self.device,
|
| 452 |
+
)
|
| 453 |
+
return (
|
| 454 |
+
get_weights(self.net),
|
| 455 |
+
len(self.trainloader.dataset),
|
| 456 |
+
{"train_loss": train_loss},
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
def evaluate(self, parameters, config):
|
| 460 |
+
set_weights(self.net, parameters)
|
| 461 |
+
loss, accuracy = test(self.net, self.valloader, self.device)
|
| 462 |
+
return loss, len(self.valloader.dataset), {"accuracy": accuracy}
|
| 463 |
+
|
| 464 |
+
def client_fn(context: Context):
|
| 465 |
+
# Load model and data
|
| 466 |
+
net = Net()
|
| 467 |
+
partition_id = context.node_config["partition-id"]
|
| 468 |
+
num_partitions = context.node_config["num-partitions"]
|
| 469 |
+
trainloader, valloader = load_data(partition_id, num_partitions)
|
| 470 |
+
local_epochs = context.run_config["local-epochs"]
|
| 471 |
+
|
| 472 |
+
# Return Client instance
|
| 473 |
+
return FlowerClient(net, trainloader, valloader, local_epochs).to_client()
|
| 474 |
+
|
| 475 |
+
# Flower ClientApp
|
| 476 |
+
app = ClientApp(
|
| 477 |
+
client_fn,
|
| 478 |
+
)
|
| 479 |
+
''', language="python")
|
| 480 |
+
|
| 481 |
+
with steps[4]:
|
| 482 |
+
st.subheader('Modelo/Tarefas')
|
| 483 |
+
st.code(
|
| 484 |
+
"""
|
| 485 |
+
from collections import OrderedDict
|
| 486 |
+
import torch
|
| 487 |
+
import torch.nn as nn
|
| 488 |
+
import torch.nn.functional as F
|
| 489 |
+
from flwr_datasets import FederatedDataset
|
| 490 |
+
from flwr_datasets.partitioner import IidPartitioner
|
| 491 |
+
from torch.utils.data import DataLoader
|
| 492 |
+
from torchvision.transforms import Compose, Normalize, ToTensor
|
| 493 |
+
|
| 494 |
+
class Net(nn.Module):
|
| 495 |
+
'''Model (simple CNN adapted from 'PyTorch: A 60 Minute Blitz')'''
|
| 496 |
+
|
| 497 |
+
def __init__(self):
|
| 498 |
+
super(Net, self).__init__()
|
| 499 |
+
self.conv1 = nn.Conv2d(3, 6, 5)
|
| 500 |
+
self.pool = nn.MaxPool2d(2, 2)
|
| 501 |
+
self.conv2 = nn.Conv2d(6, 16, 5)
|
| 502 |
+
self.fc1 = nn.Linear(16 * 5 * 5, 120)
|
| 503 |
+
self.fc2 = nn.Linear(120, 84)
|
| 504 |
+
self.fc3 = nn.Linear(84, 10)
|
| 505 |
+
|
| 506 |
+
def forward(self, x):
|
| 507 |
+
x = self.pool(F.relu(self.conv1(x)))
|
| 508 |
+
x = self.pool(F.relu(self.conv2(x)))
|
| 509 |
+
x = x.view(-1, 16 * 5 * 5)
|
| 510 |
+
x = F.relu(self.fc1(x))
|
| 511 |
+
x = F.relu(self.fc2(x))
|
| 512 |
+
return self.fc3(x)
|
| 513 |
+
|
| 514 |
+
fds = None # Cache FederatedDataset
|
| 515 |
+
|
| 516 |
+
def load_data(partition_id: int, num_partitions: int):
|
| 517 |
+
'''Load partition CIFAR10 data.'''
|
| 518 |
+
# Only initialize `FederatedDataset` once
|
| 519 |
+
global fds
|
| 520 |
+
if fds is None:
|
| 521 |
+
partitioner = IidPartitioner(num_partitions=num_partitions)
|
| 522 |
+
fds = FederatedDataset(
|
| 523 |
+
dataset='uoft-cs/cifar10',
|
| 524 |
+
partitioners={'train': partitioner},
|
| 525 |
+
)
|
| 526 |
+
partition = fds.load_partition(partition_id)
|
| 527 |
+
# Divide data on each node: 80% train, 20% test
|
| 528 |
+
partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
|
| 529 |
+
pytorch_transforms = Compose(
|
| 530 |
+
[ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
|
| 531 |
+
)
|
| 532 |
+
|
| 533 |
+
def apply_transforms(batch):
|
| 534 |
+
'''Apply transforms to the partition from FederatedDataset.'''
|
| 535 |
+
batch['img'] = [pytorch_transforms(img) for img in batch['img']]
|
| 536 |
+
return batch
|
| 537 |
+
|
| 538 |
+
partition_train_test = partition_train_test.with_transform(apply_transforms)
|
| 539 |
+
trainloader = DataLoader(partition_train_test['train'], batch_size=32, shuffle=True)
|
| 540 |
+
testloader = DataLoader(partition_train_test['test'], batch_size=32)
|
| 541 |
+
return trainloader, testloader
|
| 542 |
+
|
| 543 |
+
def train(net, trainloader, epochs, device):
|
| 544 |
+
'''Train the model on the training set.'''
|
| 545 |
+
net.to(device) # move model to GPU if available
|
| 546 |
+
criterion = torch.nn.CrossEntropyLoss().to(device)
|
| 547 |
+
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
|
| 548 |
+
net.train()
|
| 549 |
+
running_loss = 0.0
|
| 550 |
+
for _ in range(epochs):
|
| 551 |
+
for batch in trainloader:
|
| 552 |
+
images = batch['img']
|
| 553 |
+
labels = batch['label']
|
| 554 |
+
optimizer.zero_grad()
|
| 555 |
+
loss = criterion(net(images.to(device)), labels.to(device))
|
| 556 |
+
loss.backward()
|
| 557 |
+
optimizer.step()
|
| 558 |
+
running_loss += loss.item()
|
| 559 |
+
|
| 560 |
+
avg_trainloss = running_loss / len(trainloader)
|
| 561 |
+
return avg_trainloss
|
| 562 |
+
|
| 563 |
+
def test(net, testloader, device):
|
| 564 |
+
'''Validate the model on the test set.'''
|
| 565 |
+
net.to(device)
|
| 566 |
+
criterion = torch.nn.CrossEntropyLoss()
|
| 567 |
+
correct, loss = 0, 0.0
|
| 568 |
+
with torch.no_grad():
|
| 569 |
+
for batch in testloader:
|
| 570 |
+
images = batch['img'].to(device)
|
| 571 |
+
labels = batch['label'].to(device)
|
| 572 |
+
outputs = net(images)
|
| 573 |
+
loss += criterion(outputs, labels).item()
|
| 574 |
+
correct += (torch.max(outputs.data, 1)[1] == labels).sum().item()
|
| 575 |
+
accuracy = correct / len(testloader.dataset)
|
| 576 |
+
loss = loss / len(testloader)
|
| 577 |
+
return loss, accuracy
|
| 578 |
+
|
| 579 |
+
def get_weights(net):
|
| 580 |
+
return [val.cpu().numpy() for _, val in net.state_dict().items()]
|
| 581 |
+
|
| 582 |
+
def set_weights(net, parameters):
|
| 583 |
+
params_dict = zip(net.state_dict().keys(), parameters)
|
| 584 |
+
state_dict = OrderedDict({k: torch.tensor(v) for k, v in params_dict})
|
| 585 |
+
net.load_state_dict(state_dict, strict=True)
|
| 586 |
+
""", language="python"
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
with steps[5]:
|
| 590 |
+
st.subheader("Estratégias de Agregação")
|
| 591 |
+
|
| 592 |
+
st.markdown("""
|
| 593 |
+
O Flower oferece várias estratégias de agregação para combinar as atualizações dos clientes:
|
| 594 |
+
""")
|
| 595 |
+
|
| 596 |
+
strategy_tabs = st.tabs(["FedAvg", "FedAdagrad", "FedAdam", "FedYogi", "Personalizada",'Link para mais estratégias'])
|
| 597 |
+
|
| 598 |
+
with strategy_tabs[0]:
|
| 599 |
+
st.markdown("""
|
| 600 |
+
### Federated Averaging (FedAvg)
|
| 601 |
+
|
| 602 |
+
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.
|
| 603 |
+
""")
|
| 604 |
+
|
| 605 |
+
st.image("./images/fedavg.png")
|
| 606 |
+
st.markdown("[McMaham et al. 2023. Communication-Efficient Learning of Deep Networks from Decentralized Data](https://arxiv.org/pdf/1602.05629)")
|
| 607 |
+
|
| 608 |
+
st.code("""
|
| 609 |
+
# Implementação do FedAvg no servidor
|
| 610 |
+
strategy = fl.server.strategy.FedAvg(
|
| 611 |
+
fraction_fit=1.0, # Fração de clientes usados em cada round
|
| 612 |
+
min_fit_clients=min_clients, # Mínimo de clientes para iniciar o treino
|
| 613 |
+
min_available_clients=min_clients, # Mínimo de clientes necessários
|
| 614 |
+
)
|
| 615 |
+
""", language="python")
|
| 616 |
+
|
| 617 |
+
with strategy_tabs[1]:
|
| 618 |
+
st.markdown("""
|
| 619 |
+
### FedAdagrad
|
| 620 |
+
|
| 621 |
+
O FedAdagrad adapta o algoritmo Adagrad para o cenário federado, ajustando as taxas de aprendizado com base em gradientes anteriores.
|
| 622 |
+
""")
|
| 623 |
+
|
| 624 |
+
st.image('./images/FedAdaGrad-Yogi-Adam.png')
|
| 625 |
+
st.markdown("[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)")
|
| 626 |
+
|
| 627 |
+
st.code("""
|
| 628 |
+
# Implementação do FedAdagrad no servidor
|
| 629 |
+
strategy = fl.server.strategy.FedAdagrad(
|
| 630 |
+
fraction_fit=1.0,
|
| 631 |
+
min_fit_clients=min_clients,
|
| 632 |
+
min_available_clients=min_clients,
|
| 633 |
+
eta=0.1, # Taxa de aprendizado do servidor
|
| 634 |
+
eta_l=0.01, # Taxa de aprendizado do cliente
|
| 635 |
+
)
|
| 636 |
+
""", language="python")
|
| 637 |
+
|
| 638 |
+
with strategy_tabs[2]:
|
| 639 |
+
st.markdown("""
|
| 640 |
+
### FedAdam
|
| 641 |
+
|
| 642 |
+
O FedAdam adapta o otimizador Adam para o cenário federado, incorporando momentos para melhorar a convergência.
|
| 643 |
+
""")
|
| 644 |
+
|
| 645 |
+
st.image('./images/FedAdaGrad-Yogi-Adam.png')
|
| 646 |
+
st.markdown("[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)")
|
| 647 |
+
|
| 648 |
+
st.code("""
|
| 649 |
+
# Implementação do FedAdam no servidor
|
| 650 |
+
strategy = fl.server.strategy.FedAdam(
|
| 651 |
+
fraction_fit=1.0,
|
| 652 |
+
min_fit_clients=min_clients,
|
| 653 |
+
min_available_clients=min_clients,
|
| 654 |
+
eta=0.1, # Taxa de aprendizado do servidor
|
| 655 |
+
eta_l=0.01, # Taxa de aprendizado do cliente
|
| 656 |
+
beta_1=0.9, # Decaimento exponencial para momentos de primeira ordem
|
| 657 |
+
beta_2=0.99, # Decaimento exponencial para momentos de segunda ordem
|
| 658 |
+
)
|
| 659 |
+
""", language="python")
|
| 660 |
+
|
| 661 |
+
with strategy_tabs[3]:
|
| 662 |
+
st.markdown("""
|
| 663 |
+
### FedYogi
|
| 664 |
+
|
| 665 |
+
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.
|
| 666 |
+
""")
|
| 667 |
+
|
| 668 |
+
st.image('./images/FedAdaGrad-Yogi-Adam.png')
|
| 669 |
+
st.markdown("[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)")
|
| 670 |
+
|
| 671 |
+
st.code("""
|
| 672 |
+
# Implementação do FedYogi no servidor
|
| 673 |
+
strategy = fl.server.strategy.FedYogi(
|
| 674 |
+
fraction_fit=1.0,
|
| 675 |
+
min_fit_clients=min_clients,
|
| 676 |
+
min_available_clients=min_clients,
|
| 677 |
+
eta=0.1, # Taxa de aprendizado do servidor
|
| 678 |
+
eta_l=0.01, # Taxa de aprendizado do cliente
|
| 679 |
+
beta_1=0.9, # Decaimento exponencial para momentos de primeira ordem
|
| 680 |
+
beta_2=0.99, # Decaimento exponencial para momentos de segunda ordem
|
| 681 |
+
tau=0.001, # Parâmetro de controle para atualização
|
| 682 |
+
)
|
| 683 |
+
""", language="python")
|
| 684 |
+
|
| 685 |
+
with strategy_tabs[4]:
|
| 686 |
+
st.markdown("""
|
| 687 |
+
### Estratégia Personalizada
|
| 688 |
+
""")
|
| 689 |
+
|
| 690 |
+
st.code('''
|
| 691 |
+
# Estratégia personalizada para agregação
|
| 692 |
+
class MyStrategy(fl.server.strategy.FedAvg):
|
| 693 |
+
''', language="python")
|
| 694 |
+
|
| 695 |
+
with strategy_tabs[5]:
|
| 696 |
+
st.markdown("""
|
| 697 |
+
### Link para mais estratégias
|
| 698 |
+
|
| 699 |
+
Mais estratégias de agregação disponíveis na [documentação oficial](https://flower.dev/docs/strategies.html).
|
| 700 |
+
|
| 701 |
+
Link das opções de partição de dados: [Flower Partitioner](https://flower.ai/docs/datasets/ref-api/flwr_datasets.partitioner.html)
|
| 702 |
+
""")
|
| 703 |
+
|
| 704 |
+
with steps[6]:
|
| 705 |
+
st.subheader("Executando o Sistema")
|
| 706 |
+
|
| 707 |
+
st.markdown("""
|
| 708 |
+
Para executar o sistema de aprendizado federado, precisa iniciar o servidor e os clientes:
|
| 709 |
+
""")
|
| 710 |
+
|
| 711 |
+
st.code("""
|
| 712 |
+
# Iniciar o servidor
|
| 713 |
+
python server.py --rounds 10 --min_clients 3 --port 8080
|
| 714 |
+
|
| 715 |
+
# Em terminais diferentes, iniciar os clientes
|
| 716 |
+
python client.py --partition 0 --num_partitions 3 --server 127.0.0.1:8080
|
| 717 |
+
python client.py --partition 1 --num_partitions 3 --server 127.0.0.1:8080
|
| 718 |
+
python client.py --partition 2 --num_partitions 3 --server 127.0.0.1:8080
|
| 719 |
+
""", language="bash")
|
| 720 |
+
|
| 721 |
+
st.markdown("""
|
| 722 |
+
Alternativamente, dá para criar um script para automatizar este processo:
|
| 723 |
+
""")
|
| 724 |
+
|
| 725 |
+
st.code("""
|
| 726 |
+
#!/bin/bash
|
| 727 |
+
# run.sh
|
| 728 |
+
|
| 729 |
+
# Inicia o servidor em background
|
| 730 |
+
python server.py --rounds 10 --min_clients 3 --port 8080 &
|
| 731 |
+
SERVER_PID=$!
|
| 732 |
+
|
| 733 |
+
# Espera o servidor iniciar
|
| 734 |
+
sleep 2
|
| 735 |
+
|
| 736 |
+
# Inicia os clientes
|
| 737 |
+
for i in $(seq 0 2); do
|
| 738 |
+
python client.py --partition $i --num_partitions 3 --server 127.0.0.1:8080 &
|
| 739 |
+
CLIENT_PIDS[$i]=$!
|
| 740 |
+
done
|
| 741 |
+
|
| 742 |
+
# Espera pelo término do servidor
|
| 743 |
+
wait $SERVER_PID
|
| 744 |
+
|
| 745 |
+
# Mata os processos dos clientes se ainda estiverem rodando
|
| 746 |
+
for pid in "${CLIENT_PIDS[@]}"; do
|
| 747 |
+
kill -0 $pid 2>/dev/null && kill $pid
|
| 748 |
+
done
|
| 749 |
+
""", language="bash")
|
| 750 |
+
with steps[7]:
|
| 751 |
+
st.subheader("Arquivo pyproject.toml")
|
| 752 |
+
|
| 753 |
+
st.markdown("""
|
| 754 |
+
O arquivo `pyproject.toml` é usado para gerenciar as dependências do projeto e configurações do ambiente.
|
| 755 |
+
""")
|
| 756 |
+
|
| 757 |
+
st.code("""
|
| 758 |
+
[build-system]
|
| 759 |
+
requires = ["hatchling"]
|
| 760 |
+
build-backend = "hatchling.build"
|
| 761 |
+
|
| 762 |
+
[project]
|
| 763 |
+
name = "fl-inmetro"
|
| 764 |
+
version = "1.0.0"
|
| 765 |
+
description = ""
|
| 766 |
+
license = "Apache-2.0"
|
| 767 |
+
dependencies = [
|
| 768 |
+
"flwr[simulation]>=1.18.0",
|
| 769 |
+
"flwr-datasets[vision]>=0.5.0",
|
| 770 |
+
"torch==2.5.1",
|
| 771 |
+
"torchvision==0.20.1",
|
| 772 |
+
]
|
| 773 |
+
|
| 774 |
+
[tool.hatch.build.targets.wheel]
|
| 775 |
+
packages = ["."]
|
| 776 |
+
|
| 777 |
+
[tool.flwr.app]
|
| 778 |
+
publisher = "jwsouza"
|
| 779 |
+
|
| 780 |
+
[tool.flwr.app.components]
|
| 781 |
+
serverapp = "fl_inmetro.server_app:app"
|
| 782 |
+
clientapp = "fl_inmetro.client_app:app"
|
| 783 |
+
|
| 784 |
+
[tool.flwr.app.config]
|
| 785 |
+
num-server-rounds = 3
|
| 786 |
+
fraction-fit = 0.5
|
| 787 |
+
local-epochs = 1
|
| 788 |
+
|
| 789 |
+
[tool.flwr.federations]
|
| 790 |
+
default = "local-simulation"
|
| 791 |
+
|
| 792 |
+
[tool.flwr.federations.local-simulation]
|
| 793 |
+
options.num-supernodes = 10
|
| 794 |
+
|
| 795 |
+
""", language="toml")
|
| 796 |
+
|
| 797 |
+
elif secao == "📊 Visualização":
|
| 798 |
+
st.title("Processo de Aprendizado Federado")
|
| 799 |
+
|
| 800 |
+
# visualization_tabs = st.tabs(["Animação do Processo FL", "Convergência do Modelo", "Desempenho por Cliente", "Comparação de Estratégias"])
|
| 801 |
+
|
| 802 |
+
# with visualization_tabs[0]:
|
| 803 |
+
|
| 804 |
+
# Criando um gráfico interativo com Plotly
|
| 805 |
+
fig = go.Figure()
|
| 806 |
+
|
| 807 |
+
# Nós
|
| 808 |
+
server_pos = [5, 10]
|
| 809 |
+
client_positions = [[2, 5], [5, 5], [8, 5]]
|
| 810 |
+
data_positions = [[2, 0], [5, 0], [8, 0]]
|
| 811 |
+
|
| 812 |
+
# Adicionando nós
|
| 813 |
+
fig.add_trace(go.Scatter(
|
| 814 |
+
x=[server_pos[0]],
|
| 815 |
+
y=[server_pos[1]],
|
| 816 |
+
mode='markers+text',
|
| 817 |
+
marker=dict(symbol='square', size=40, color='red'),
|
| 818 |
+
text=['Servidor'],
|
| 819 |
+
textposition='top center',
|
| 820 |
+
name='Servidor'
|
| 821 |
+
))
|
| 822 |
+
|
| 823 |
+
fig.add_trace(go.Scatter(
|
| 824 |
+
x=[pos[0] for pos in client_positions],
|
| 825 |
+
y=[pos[1] for pos in client_positions],
|
| 826 |
+
mode='markers+text',
|
| 827 |
+
marker=dict(symbol='circle', size=30, color='green'),
|
| 828 |
+
text=['Cliente 1', 'Cliente 2', 'Cliente 3'],
|
| 829 |
+
textposition='middle right',
|
| 830 |
+
name='Clientes'
|
| 831 |
+
))
|
| 832 |
+
|
| 833 |
+
fig.add_trace(go.Scatter(
|
| 834 |
+
x=[pos[0] for pos in data_positions],
|
| 835 |
+
y=[pos[1] for pos in data_positions],
|
| 836 |
+
mode='markers+text',
|
| 837 |
+
marker=dict(symbol='diamond', size=20, color='blue'),
|
| 838 |
+
text=['Dados 1', 'Dados 2', 'Dados 3'],
|
| 839 |
+
textposition='bottom center',
|
| 840 |
+
name='Dados'
|
| 841 |
+
))
|
| 842 |
+
|
| 843 |
+
# Adicionando linhas de conexão
|
| 844 |
+
for i, client_pos in enumerate(client_positions):
|
| 845 |
+
# Linha do servidor para o cliente (modelo global)
|
| 846 |
+
fig.add_trace(go.Scatter(
|
| 847 |
+
x=[server_pos[0], client_pos[0]],
|
| 848 |
+
y=[server_pos[1], client_pos[1]],
|
| 849 |
+
mode='lines+text',
|
| 850 |
+
line=dict(width=2, color='red', dash='dash'),
|
| 851 |
+
text=['Modelo Global'],
|
| 852 |
+
textposition='top right',
|
| 853 |
+
showlegend=False
|
| 854 |
+
))
|
| 855 |
+
|
| 856 |
+
# Linha do cliente para o servidor (atualizações)
|
| 857 |
+
fig.add_trace(go.Scatter(
|
| 858 |
+
x=[client_pos[0], server_pos[0]],
|
| 859 |
+
y=[client_pos[1], server_pos[1]],
|
| 860 |
+
mode='lines+text',
|
| 861 |
+
line=dict(width=2, color='green', dash='dot'),
|
| 862 |
+
text=['Atualizações'],
|
| 863 |
+
textposition='bottom left',
|
| 864 |
+
showlegend=False
|
| 865 |
+
))
|
| 866 |
+
|
| 867 |
+
# Linha dos dados para o cliente (treino local)
|
| 868 |
+
fig.add_trace(go.Scatter(
|
| 869 |
+
x=[data_positions[i][0], client_pos[0]],
|
| 870 |
+
y=[data_positions[i][1], client_pos[1]],
|
| 871 |
+
mode='lines+text',
|
| 872 |
+
line=dict(width=2, color='blue'),
|
| 873 |
+
text=['Treino Local'],
|
| 874 |
+
textposition='middle left',
|
| 875 |
+
showlegend=False
|
| 876 |
+
))
|
| 877 |
+
|
| 878 |
+
# Layout
|
| 879 |
+
fig.update_layout(
|
| 880 |
+
title='Fluxo de Aprendizado Federado Interativo',
|
| 881 |
+
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
| 882 |
+
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
| 883 |
+
width=800,
|
| 884 |
+
height=500,
|
| 885 |
+
legend=dict(x=0, y=1),
|
| 886 |
+
hovermode='closest'
|
| 887 |
+
)
|
| 888 |
+
|
| 889 |
+
st.plotly_chart(fig)
|
| 890 |
+
|
| 891 |
+
if secao == "📈 Modelos para EVs":
|
| 892 |
+
|
| 893 |
+
st.caption("Zhang et al. (2024). Modelos de Consumo de Energia para Veículos Elétricos em Transporte Urbano. Renewable Energy")
|
| 894 |
+
|
| 895 |
+
st.markdown("""
|
| 896 |
+
**Objetivo**: Prever consumo energético (CE) de VEs em cenários urbanos:
|
| 897 |
+
""")
|
| 898 |
+
st.latex(r"""
|
| 899 |
+
\min_{\theta} \mathcal{L}(\theta) = \mathbb{E}_{(\mathbf{x},y) \sim \mathcal{P}} [\ell(f_\theta(\mathbf{x}), y)]
|
| 900 |
+
""")
|
| 901 |
+
|
| 902 |
+
st.markdown("""
|
| 903 |
+
Onde:
|
| 904 |
+
- $y$: consumo energético observado
|
| 905 |
+
- $f_\theta$: modelo de previsão
|
| 906 |
+
""")
|
| 907 |
+
|
| 908 |
+
st.header("Abordagens Tradicionais")
|
| 909 |
+
st.subheader("Modelos de Dinâmica Veicular")
|
| 910 |
+
st.latex(r"""
|
| 911 |
+
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}}
|
| 912 |
+
""")
|
| 913 |
+
st.latex(r"""
|
| 914 |
+
P_{\text{total}} = (F_t \cdot v)/\eta_{\text{motor}} + P_{\text{aux}}
|
| 915 |
+
""")
|
| 916 |
+
st.markdown("""
|
| 917 |
+
**Limitações**:
|
| 918 |
+
- Requer 12+ parâmetros específicos (Tabela 1)
|
| 919 |
+
- Não captura efeitos não-lineares em condições urbanas complexas
|
| 920 |
+
""")
|
| 921 |
+
|
| 922 |
+
st.subheader("Modelos Estatísticos")
|
| 923 |
+
st.latex(r"""
|
| 924 |
+
\text{CE} = \beta_0 + \sum_{i=1}^k \beta_i X_i + \epsilon \quad \text{(Regressão Linear)}
|
| 925 |
+
""")
|
| 926 |
+
st.latex(r"""
|
| 927 |
+
\text{UEC}(BDR) = a_0 + a_1 \cdot BDR + a_2 \cdot BDR^2 \quad \text{(Degradação de Bateria)}
|
| 928 |
+
""")
|
| 929 |
+
st.markdown("""
|
| 930 |
+
**Problemas**: Pressupõe relações lineares e não considera interações complexas entre variáveis
|
| 931 |
+
""")
|
| 932 |
+
|
| 933 |
+
st.subheader("Modelos Principais")
|
| 934 |
+
st.markdown("""
|
| 935 |
+
- **XGBoost/LightGBM**:
|
| 936 |
+
""")
|
| 937 |
+
st.latex(r"""
|
| 938 |
+
\mathcal{L}^{(t)} = \sum_{i=1}^n \ell(y_i, \hat{y}_i^{(t-1)} + f_t(\mathbf{x}_i)) + \Omega(f_t)
|
| 939 |
+
""")
|
| 940 |
+
st.markdown("""
|
| 941 |
+
- **Random Forest**: Média de $B$ árvores de decisão
|
| 942 |
+
""")
|
| 943 |
+
|
| 944 |
+
st.header("Redes Neurais para Alta Precisão")
|
| 945 |
+
st.markdown("""
|
| 946 |
+
**LSTM**:
|
| 947 |
+
""")
|
| 948 |
+
st.latex(r"""
|
| 949 |
+
\begin{aligned}
|
| 950 |
+
i_t &= \sigma(W_{ix} x_t + W_{ih} h_{t-1} + b_i) \\
|
| 951 |
+
c_t &= f_t \odot c_{t-1} + i_t \odot \tanh(W_{cx} x_t + W_{ch} h_{t-1} + b_c) \\
|
| 952 |
+
h_t &= o_t \odot \tanh(c_t)
|
| 953 |
+
\end{aligned}
|
| 954 |
+
""")
|
| 955 |
+
st.markdown("""
|
| 956 |
+
**Transformer**:
|
| 957 |
+
""")
|
| 958 |
+
st.latex(r"""
|
| 959 |
+
\text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
|
| 960 |
+
""")
|
| 961 |
+
|
| 962 |
+
st.subheader("4.2 Desempenho Comparativo")
|
| 963 |
+
st.markdown("""
|
| 964 |
+
| Modelo | RMSE (kWh) | MAPE (%) | Limitações |
|
| 965 |
+
|-----------------|------------|----------|--------------------------|
|
| 966 |
+
| Dinâmica Veicular | 6.06 | 14.10 | Parâmetros fixos |
|
| 967 |
+
| XGBoost | 3.94 | 9.31 | Menor capacidade não-linear |
|
| 968 |
+
| LSTM | 3.61 | 8.69 | Custo computacional alto |
|
| 969 |
+
| Transformer | 2.98 | 7.21 | Dados de treino massivos |
|
| 970 |
+
""")
|
| 971 |
+
|
| 972 |
+
if secao == "🍓 Integração Raspberry Pi":
|
| 973 |
+
st.markdown("""
|
| 974 |
+
|
| 975 |
+
1. **Configurar acesso SSH**
|
| 976 |
+
- Habilite o SSH no Pi (`raspi-config` → *Interfacing Options* → *SSH*).
|
| 977 |
+
- Anote o endereço IP do Pi na sua rede.
|
| 978 |
+
|
| 979 |
+
2. **Obter o código-fonte**
|
| 980 |
+
- **Via Git**:
|
| 981 |
+
```bash
|
| 982 |
+
git clone https://github.com/SEU_USUARIO/SEU_PROJETO.git
|
| 983 |
+
cd SEU_PROJETO
|
| 984 |
+
```
|
| 985 |
+
- **Via SCP/RSYNC** (se não usar Git):
|
| 986 |
+
```bash
|
| 987 |
+
scp -r caminho/local/SEU_PROJETO pi@IP_DO_PI:~/SEU_PROJETO
|
| 988 |
+
```
|
| 989 |
+
|
| 990 |
+
3. **Instalar dependências no Pi**
|
| 991 |
+
```bash
|
| 992 |
+
python3 -m venv venv
|
| 993 |
+
source venv/bin/activate
|
| 994 |
+
pip install -r requirements.txt
|
| 995 |
+
```
|
| 996 |
+
|
| 997 |
+
4. **Copiar arquivos estáticos**
|
| 998 |
+
- Certifique-se de que a pasta `images/` foi clonada/copied e que os paths relativos estejam corretos.
|
| 999 |
+
|
| 1000 |
+
5. **Configurar serviço de inicialização**
|
| 1001 |
+
- Crie um serviço systemd em `/etc/systemd/system/fl-client.service` apontando para o comando de execução no seu virtualenv.
|
| 1002 |
+
- Habilite e inicie:
|
| 1003 |
+
```bash
|
| 1004 |
+
sudo systemctl enable fl-client
|
| 1005 |
+
sudo systemctl start fl-client
|
| 1006 |
+
```
|
| 1007 |
+
|
| 1008 |
+
6. **Testar localmente**
|
| 1009 |
+
```bash
|
| 1010 |
+
source venv/bin/activate
|
| 1011 |
+
python client_pi.py
|
| 1012 |
+
```
|
| 1013 |
+
|
| 1014 |
+
7. **Automatizar atualizações**
|
| 1015 |
+
- No CI/CD, adicione um passo para:
|
| 1016 |
+
```bash
|
| 1017 |
+
ssh pi@IP_DO_PI \
|
| 1018 |
+
'cd ~/SEU_PROJETO && git pull && source venv/bin/activate && pip install -r requirements.txt && systemctl restart fl-client'
|
| 1019 |
+
```
|
| 1020 |
+
""", unsafe_allow_html=False)
|
| 1021 |
+
|
| 1022 |
+
if secao == "📚 Artigos":
|
| 1023 |
+
|
| 1024 |
+
st.markdown(
|
| 1025 |
+
"""
|
| 1026 |
+
### Referências
|
| 1027 |
+
- FedAVG
|
| 1028 |
+
|
| 1029 |
+
[McMaham et al. 2023. Communication-Efficient Learning of Deep Networks from Decentralized Data](https://arxiv.org/pdf/1602.05629)
|
| 1030 |
+
|
| 1031 |
+
- ADAGRAD, ADAM, YOGI
|
| 1032 |
+
|
| 1033 |
+
[Reddi et al. DAPTIVE FEDERATED OPTIMIZATION](https://arxiv.org/pdf/2003.00295)
|
| 1034 |
+
|
| 1035 |
+
- Privacdiade e Segurança
|
| 1036 |
+
|
| 1037 |
+
[Bonawitz et al. Practical Secure Aggregation for Privacy-Preserving Machine Learning](https://arxiv.org/pdf/1611.04482)
|
| 1038 |
+
|
| 1039 |
+
|
| 1040 |
+
[Abadi et al. Deep Learning with Differential Privacy](https://arxiv.org/pdf/1607.00133)
|
| 1041 |
+
"""
|
| 1042 |
+
)
|