Key Findings
  • Federated Learning ermoglicht es mehreren Institutionen, KI-Modelle kollaborativ zu trainieren, ohne Rohdaten auszutauschen[1] -- es ist das wichtigste dezentrale Lernparadigma im Zeitalter von Datenschutzverordnungen wie der DSGVO[13]
  • FedAvg[1] ist der grundlegende Algorithmus des Federated Learning, zeigt jedoch bei nicht-IID-Datenverteilungen (Non-IID) erhebliche Leistungseinbussen -- FedProx[3] und FedBN[15] adressieren dieses Problem aus den Perspektiven der Optimierungsbeschrankung bzw. der Feature-Normalisierung
  • Differential Privacy[12] und Secure Aggregation[5] bilden die doppelte Datenschutzbarriere des Federated Learning -- erstere garantiert mathematisch, dass individuelle Daten nicht inferiert werden konnen, letztere stellt sicher, dass der Server keine einzelnen Client-Modellupdates einsehen kann
  • Dieser Artikel enthalt zwei Google Colab Praxisubungen: Simulation eines Multi-Client-Federated-Trainings mit dem Flower-Framework[6] (CIFAR-10 Bildklassifikation) sowie ein Differential-Privacy-Federated-Learning-Experiment (Opacus-Integration), die direkt im Browser ausgefuhrt werden konnen

1. Daten durfen das Haus nicht verlassen: Warum Federated Learning im Zeitalter der Datenschutzverordnungen unvermeidlich ist

Die traditionelle Annahme des maschinellen Lernens ist einfach: Alle Daten werden auf einem Server zentralisiert und dann wird ein Modell trainiert. Diese Annahme war in den 2010er Jahren vielleicht noch haltbar, wird heute jedoch gleichzeitig von drei Kraften untergraben:

Regulatorischer Druck. Die EU-DSGVO[13] verlangt explizit Datenminimierung (Data Minimization) und Zweckbindung (Purpose Limitation) -- jede institutionsubergreifende Datenubertragung erfordert eine Rechtsgrundlage. Auch die Datenschutzgesetze der US-Bundesstaaten (CCPA/CPRA), Chinas Gesetz zum Schutz personenbezogener Daten und Taiwans Datenschutzgesetz verscharfen die Kontrolle uber Datenflusse. Fur medizinische Einrichtungen verbietet HIPAA die Ubertragung von Patientendaten an externe Server sogar direkt.

Geschaftlicher Wettbewerb. Selbst wenn die Regulierung es erlaubt, sind Unternehmen nicht bereit, Kerndaten an Dritte weiterzugeben. Eine Bank wird ihre Kundentransaktionsdaten nicht einer anderen Bank uberlassen, um gemeinsam ein Anti-Betrugs-Modell zu trainieren -- selbst wenn beide wissen, dass das gemeinsame Training bessere Ergebnisse liefern wurde. Daten sind eine Wettbewerbsbarriere, und kein Unternehmen mochte diese Mauer einreissen.

Physische Beschrankungen. Milliarden von Mobilgeraten erzeugen taglich riesige Datenmengen, doch alle Tastaturdaten von Mobiltelefonen in die Cloud hochzuladen, ist weder praktikabel (Bandbreitenkosten) noch sicher (Datenschutzrisiken). Genau dieses Problem stand Google 2017 gegenuber: Wie konnen die Tippdaten von Hunderten Millionen Android-Telefonen genutzt werden, um die Wortvorhersage von Gboard zu verbessern, ohne den Tippinhalt eines einzigen Nutzers zu erfassen[10]?

Federated Learning ist genau vor diesem Hintergrund entstanden. McMahan et al. von Google haben 2017 den FedAvg-Algorithmus vorgestellt[1], dessen Kernidee revolutionar einfach ist: Nicht die Daten bewegen, sondern das Modell.

Grundlegender Ablauf des Federated Learning:

Round t:
  1. Server → Clients: Globales Modell w_t broadcasten
  2. Jeder Client k:
     - Auf lokalen Daten E Epochen trainieren
     - Lokales Modell w_t^k erhalten
  3. Clients → Server: Modell-Update Δw_t^k = w_t^k - w_t hochladen
  4. Server: Alle Updates aggregieren → w_{t+1}
  5. Wiederholen bis zur Konvergenz

Schluesseleigenschaften:
  - Rohdaten verlassen niemals den Client
  - Der Server sieht nur die Modellparameter-Updates (Gradienten)
  - Das Kommunikationsvolumen ist deutlich geringer als die Uebertragung der Rohdaten

Gemaess der Klassifikation von Yang et al.[4] lasst sich Federated Learning je nach Art der Datenpartitionierung in drei Typen unterteilen:

TypMerkmale der DatenverteilungTypisches SzenarioBeispiel
Horizontales Federated Learning (Horizontal FL)Alle Parteien besitzen gleiche Features, unterschiedliche StichprobenBrancheninterne ZusammenarbeitMehrere Krankenhaeuser besitzen jeweils medizinische Bilder unterschiedlicher Patienten desselben Typs
Vertikales Federated Learning (Vertical FL)Alle Parteien besitzen gleiche Stichproben, unterschiedliche FeaturesBranchenuebergreifende ZusammenarbeitEine Bank hat die Finanzdaten der Kunden, ein E-Commerce-Unternehmen hat die Kaufhistorie derselben Kunden
Federated Transfer LearningStichproben und Features unterscheiden sich bei allen ParteienDomainuebergreifende ZusammenarbeitKrankenhaeuser in verschiedenen Laendern mit unterschiedlichen Patientenpopulationen und Untersuchungsgeraten

Dieser Artikel konzentriert sich auf das haeufigste Szenario: Horizontales Federated Learning, bei dem mehrere Clients denselben Feature-Raum teilen, aber jeweils unterschiedliche Stichproben besitzen. Dies ist auch das primaere Anwendungsszenario klassischer Algorithmen wie FedAvg und FedProx.

2. FedAvg: Der Grundpfeiler-Algorithmus des Federated Learning

Federated Averaging (FedAvg)[1] wurde 2017 von McMahan et al. als erster praxistauglicher Federated-Learning-Algorithmus vorgestellt und bildet bis heute die Grundlage der meisten Federated-Learning-Systeme. Sein Designziel ist es, eine Balance zwischen Kommunikationseffizienz und Modellqualitaet zu finden.

Die Kernidee von FedAvg lautet: Anstatt nach jedem Mini-Batch die Gradienten mit dem Server zu synchronisieren (wie beim traditionellen verteilten SGD), laesst man jeden Client lokal mehrere Epochen trainieren und laedt dann die vollstaendigen Modellparameter hoch. Dies reduziert die Anzahl der Kommunikationsrunden drastisch.

FedAvg-Algorithmus (Pseudocode):

ServerUpdate:
  Globales Modell w_0 initialisieren
  for each round t = 1, 2, ..., T:
    S_t ← Aus K Clients zufaellig m = max(C·K, 1) auswaehlen
    for each Client k ∈ S_t (parallelisierbar):
      w_{t+1}^k ← ClientUpdate(k, w_t)
    w_{t+1} ← Σ_k (n_k / n) · w_{t+1}^k     # Gewichteter Durchschnitt

ClientUpdate(k, w):
  B ← Lokale Daten in Mini-Batches der Groesse B aufteilen
  for each local epoch e = 1, ..., E:
    for each batch b ∈ B:
      w ← w - η · ∇L(w; b)
  return w

Hyperparameter:
  C = Client-Auswahlverhaeltnis (z.B. 0.1 bedeutet 10% pro Runde)
  E = Anzahl lokaler Trainings-Epochen
  B = Lokale Batch-Groesse
  η = Lokale Lernrate

Die Aggregationsformel von FedAvg basiert auf einem nach Stichprobenanzahl gewichteten Durchschnitt: Wenn Client k ueber n_k Datensaetze verfuegt und deren Anteil am Gesamtdatenvolumen n_k/n betraegt, dann ist das Gewicht seines Modell-Updates ebenfalls n_k/n. Dadurch wird sichergestellt, dass Clients mit mehr Daten einen groesseren Einfluss auf das globale Modell haben.

Die Kommunikationseffizienz von FedAvg ergibt sich aus zwei zentralen Designentscheidungen: (1) Client-Auswahl -- pro Runde muss nur ein Teil der Clients teilnehmen, nicht alle; (2) Mehrfache lokale Updates -- die Erhoehung der lokalen Epochenanzahl E reduziert die Anzahl der Kommunikationsrunden, kann aber die Konvergenzgeschwindigkeit beeintraechtigen. Die Experimente von McMahan et al. zeigen, dass E=5 und C=0.1 ein guter Ausgangspunkt sind[1].

FedAvg hat jedoch eine entscheidende Annahme: Die Datenverteilungen der Clients sind unabhaengig und identisch verteilt (IID). In der realen Welt wird diese Annahme fast immer verletzt. Verschiedene Krankenhaeuser haben unterschiedliche Patientenpopulationen, verschiedene Regionen haben unterschiedliche Nutzerverhaltensmuster, verschiedene Banken haben unterschiedliche Kundenstrukturen -- dies ist das sogenannte Non-IID-Problem und zugleich die groesste Herausforderung des Federated Learning.

3. Die Non-IID-Herausforderung: FedProx, FedBN und SCAFFOLD

Wenn sich die Datenverteilungen der einzelnen Clients erheblich unterscheiden (Non-IID-Szenario), verschlechtert sich die Leistung von FedAvg drastisch. Die Uebersichtsarbeit von Kairouz et al.[2] kategorisiert Non-IID in funf Typen:

Um das Non-IID-Problem zu loesen, hat die Forschungsgemeinschaft verschiedene Verbesserungsansaetze entwickelt:

3.1 FedProx: Proximale Beschraenkung hinzufuegen

FedProx von Li et al.[3] ist die direkteste Verbesserung von FedAvg. Es fuegt der lokalen Verlustfunktion jedes Clients einen Proximalterm hinzu, der das lokale Modell daran hindert, zu stark vom globalen Modell abzuweichen:

Lokale Zielfunktion von FedProx:

min_w  L_k(w) + (μ/2) · ‖w - w_t‖²

Wobei:
  L_k(w) = Urspruengliche Verlustfunktion von Client k
  w_t    = Globales Modell der aktuellen Runde
  μ      = Proximalkoeffizient (Hyperparameter, steuert die Staerke der Beschraenkung)

Intuition:
  - μ = 0: Degeneriert zu FedAvg
  - Je groesser μ, desto naeher bleibt das lokale Modell am globalen Modell (stabiler, aber langsameres Lernen)
  - Je kleiner μ, desto mehr Freiheitsgrade hat das lokale Modell (schnelleres Lernen, aber moegliche Abweichung)

Typische Einstellung: μ ∈ {0.001, 0.01, 0.1, 1.0}

Ein weiterer Vorteil von FedProx ist die Toleranz gegenueber Systemheterogenitaet (Systems Heterogeneity): Verschiedene Clients koennen unterschiedlich viele lokale Update-Schritte durchfuehren. Langsame Geraete koennen nur partielle Updates abschliessen und fruehzeitig hochladen, anstatt vollstaendig ausgeschlossen zu werden.

3.2 FedBN: Lokalisierung der Feature-Normalisierungsschicht

FedBN von Li et al.[15] verfolgt eine elegante Strategie: Da die Feature-Verteilungen der verschiedenen Clients unterschiedlich sind, behaelt jeder Client seine eigene Batch-Normalization-Schicht und nur die Parameter der uebrigen Schichten werden aggregiert.

FedBN-Strategie:

Standard-FedAvg:
  Alle Parameter (einschliesslich BN-Schichten) werden aggregiert

FedBN:
  Faltungsschichten, vollstaendig verbundene Schichten → Normale Aggregation (global geteilt)
  BatchNorm γ, β, running_mean, running_var → Lokal beibehalten (nicht aggregiert)

Effekt:
  - BN-Schichten passen sich automatisch an die lokale Feature-Verteilung jedes Clients an
  - Die uebrigen Schichten lernen allgemeine Feature-Extraktionsfaehigkeiten
  - Besonders wirksam bei Feature-Verteilungsverzerrungen

Die technische Implementierung von FedBN ist aeusserst einfach -- es muessen lediglich die BN-bezogenen Parameter im Aggregationsschritt ausgeschlossen werden. Dies macht es zu einer der am leichtesten einsetzbaren Loesungen fuer Feature-Skew-Probleme.

3.3 Vergleich der Non-IID-Methoden

MethodeKernstrategiePrimaer geloester Non-IID-TypZusaetzliche KommunikationskostenImplementierungskomplexitaet
FedAvg[1]Gewichteter Durchschnitt(Baseline, optimal bei IID)KeineGering
FedProx[3]Proximale BeschraenkungLabel-Skew, SystemheterogenitaetKeineGering
FedBN[15]Lokalisierung der BN-SchichtFeature-VerteilungsverzerrungLeicht reduziert (BN wird nicht uebertragen)Sehr gering
SCAFFOLDKontrollvariablen zur Korrektur von GradientendriftLabel-Skew (Konvergenzgeschwindigkeit)2x (Kontrollvariablen muessen uebertragen werden)Mittel
FedMASchichtmatching vor AggregationModellheterogenitaetErhoehtHoch

Praxisempfehlung: In den meisten Szenarien sollten Sie zunaechst FedAvg als Baseline verwenden. Wenn die Ergebnisse unbefriedigend sind, bieten FedProx (nur eine Zeile Regularisierungsterm hinzufuegen) und FedBN (BN-Parameter ausschliessen) die kostenguenstigsten Verbesserungen. Nur bei schwerwiegenden Non-IID-Szenarien mit ausreichender Kommunikationsbandbreite sollten komplexere Methoden wie SCAFFOLD in Betracht gezogen werden.

4. Datenschutzmechanismen: Differential Privacy und Secure Aggregation

Das Design des Federated Learning, keine Rohdaten zu teilen, bietet einen grundlegenden Datenschutz, ist aber nicht ausreichend. Forschungsergebnisse zeigen[14], dass Angreifer selbst aus der blossen Beobachtung von Modell-Updates (Gradienten) sensible Informationen ueber die Trainingsdaten ableiten koennen. Die wichtigsten Angriffsmethoden umfassen:

Daher benoetigen praxistaugliche Federated-Learning-Systeme zusaetzliche Datenschutzschichten. Die zwei wichtigsten Mechanismen sind Differential Privacy und Secure Aggregation.

4.1 Differential Privacy

Differential Privacy[12] bietet eine mathematisch beweisbare Datenschutzgarantie: Unabhaengig von der Rechenleistung des Angreifers und unabhaengig davon, wie viel Hintergrundwissen er besitzt, kann er aus der Modellausgabe nicht mit hoher Sicherheit ableiten, ob die Daten einer bestimmten Einzelperson verwendet wurden oder nicht.

Definition von Differential Privacy:

Ein zufaelliger Mechanismus M erfuellt (ε, δ)-Differential Privacy,
wenn fuer alle zwei benachbarten Datensaetze D und D', die sich nur in einem
Datensatz unterscheiden, und fuer jede Ausgabemenge S gilt:

  P[M(D) ∈ S] ≤ e^ε · P[M(D') ∈ S] + δ

Intuition:
  - ε (Privacy-Budget): Je kleiner, desto staerker der Datenschutz
  - ε = 0: Perfekter Datenschutz (aber das Modell kann nichts lernen)
  - ε = ∞: Kein Datenschutz
  - In der Praxis gilt ε ∈ [1, 10] als angemessener Bereich

Implementierung im Federated Learning (DP-FedAvg):
  1. Jeder Client berechnet das lokale Modell-Update Δw
  2. Gradient Clipping: Δw ← Δw · min(1, C/‖Δw‖)     # Sensitivitaet begrenzen
  3. Rauschen hinzufuegen: Δw ← Δw + N(0, σ²C²I)      # Gausssches Rauschen
  4. Verrauschtes Update hochladen

Die Wahl von σ wird gemeinsam durch (ε, δ) und die Anzahl der Trainingsrunden T bestimmt

Die Anwendung von Differential Privacy im Federated Learning laesst sich in zwei Ebenen unterteilen[11]:

Client-Level Differential Privacy: Garantiert, dass der gesamte Datensatz eines einzelnen Clients nicht preisgegeben wird. Dies ist die haeufigste Form im Federated Learning, da in institutionsubergreifenden Szenarien der Schutz der gesamten Daten jeder Institution im Vordergrund steht.

Record-Level Differential Privacy: Garantiert, dass kein einzelner Datensatz preisgegeben wird. Bietet feingranulareren Schutz, erfordert jedoch in der Regel mehr Rauschen, was zu einem staerkeren Rueckgang der Modellqualitaet fuehrt.

4.2 Secure Aggregation

Secure Aggregation[5] stellt eine weitere Verteidigungslinie dar. Durch kryptographische Protokolle wird sichergestellt, dass der Server nur das aggregierte Ergebnis aller Client-Updates sehen kann, nicht jedoch das Modell-Update eines einzelnen Clients.

Grundprinzip der Secure Aggregation (basierend auf Secret Sharing):

Vorbereitungsphase:
  Jedes Client-Paar (i, j) vereinbart eine zufaellige Maske r_{i,j}
  wobei r_{i,j} = -r_{j,i} (Masken sind entgegengesetzt)

Upload-Phase:
  Client i laedt hoch: w_i + Σ_{j≠i} r_{i,j}  (maskiertes Modell-Update)

Aggregationsphase:
  Server berechnet: Σ_i (w_i + Σ_{j≠i} r_{i,j})
            = Σ_i w_i + Σ_i Σ_{j≠i} r_{i,j}
            = Σ_i w_i + 0           (Masken heben sich gegenseitig auf)
            = Korrektes Aggregationsergebnis

Ergebnis: Der Server erhaelt das korrekte aggregierte Modell, kann aber kein einzelnes Client-Update einsehen

Google verwendet in seinem produktiven Federated-Learning-System sowohl Differential Privacy als auch Secure Aggregation[5]. Beide Mechanismen ergaenzen sich: Secure Aggregation verhindert, dass der Server einzelne Updates einsieht, und Differential Privacy verhindert, dass aus dem Aggregationsergebnis auf individuelle Daten geschlossen werden kann.

SchutzmechanismusSchutzobjektAbgewehrte AngriffeKosten
Differential PrivacyIndividuelle Daten nicht inferierbarMembership Inference, Gradient InversionModellgenauigkeit sinkt (Rauschen)
Secure AggregationEinzelne Modell-Updates nicht sichtbarNeugieriger Server (Honest-but-Curious)Erhoehtes Kommunikationsvolumen, komplexe Ausfallbehandlung
Homomorphe VerschluesselungBerechnungen direkt auf ChiffretextAlle serverseitigen AngriffeEnormer Rechenaufwand (10-100x langsamer)

5. Branchenanwendungen: Medizin, Finanzwesen und Mobilgeraete

5.1 Medizin: Krankenhausubergreifende Zusammenarbeit ohne Patientendaten zu teilen

Das Gesundheitswesen ist das wirkungsvollste Anwendungsgebiet des Federated Learning[8]. Die Fallzahlen eines einzelnen Krankenhauses reichen oft nicht aus, um hochwertige KI-Modelle zu trainieren -- insbesondere bei seltenen Erkrankungen. Doch medizinische Daten unterliegen den strengsten Datenschutzvorschriften (HIPAA, DSGVO), wodurch ein krankenhausubergreifender Austausch von Patientenakten nahezu unmoeglich ist.

Die Studie von Sheller et al.[9] zur Hirntumorsegmentierung zeigte, dass ein Modell, das von 10 Krankenhauesern mittels Federated Learning kollaborativ trainiert wurde, fast so gut abschneidet wie ein Modell, das mit allen zentralisierten Daten trainiert wurde -- und deutlich besser als jedes Modell, das von einem einzelnen Krankenhaus unabhaengig trainiert wurde. Diese Studie belegt die Praxistauglichkeit von Federated Learning im medizinischen Bereich.

Derzeit sind bereits mehrere Federated-Learning-Plattformen im klinischen Einsatz: NVIDIA Clara FL fuer krankenhausubergreifende medizinische Bildanalyse, Intel OpenFL fuer laenderuebergreifende Zusammenarbeit in der Wirkstoffforschung und das HealthChain-Projekt, das in mehreren europaeischen Laendern das foederierte Training von Brustkrebs-KI ermoeglicht hat.

5.2 Finanzwesen: Geldwaeschebekaempfung und Kreditbewertung

Finanzinstitute verfuegen ueber umfangreiche Transaktionsdaten, sind aber durch strenge Regulierungen daran gehindert, Kundendaten direkt zu teilen. Federated Learning ermoeglicht es mehreren Banken, gemeinsam ein Anti-Geldwaesche-Modell (AML) zu trainieren -- jede Bank sieht nur die Transaktionen ihrer eigenen Kunden, doch das Modell kann bankenuebergreifende Geldwaeschemuster erlernen[4].

Im Bereich der Kreditbewertung ist vertikales Federated Learning besonders wertvoll: Banken besitzen die Finanzhistorie der Kunden, E-Commerce-Plattformen die Kaufverhaltensdaten und Telekommunikationsunternehmen die Kommunikationsmuster. Diese drei komplementaeren Feature-Kategorien koennen durch vertikales Federated Learning zusammengefuehrt werden, um praezisere Kreditmodelle zu erstellen, ohne dass eine Partei der anderen Rohdaten offenlegen muss.

5.3 Mobilgeraete: Google Gboard als Praxisbeispiel

Das von Google in Gboard (der mobilen Tastatur) eingesetzte Federated Learning[10] ist eine der groessten produktiven Anwendungen. Hunderte Millionen Android-Telefone trainieren jeweils lokal mit den Tippdaten des Nutzers ein Wortvorhersagemodell und laden nur verschluesselte Modell-Updates an den Google-Server zur Aggregation hoch.

Dieses System steht vor den typischen Herausforderungen des Cross-Device Federated Learning[5]: eine enorme Anzahl von Clients (Hunderte Millionen), extrem geringe Datenmengen pro Geraet (persoenliche Tippdaten), jederzeit moegliche Offline-Zustaende sowie begrenzte Rechen- und Kommunikationsressourcen. Googles Loesung umfasst: Training nur bei angeschlossenem Ladegeraet und WLAN-Verbindung, Einsatz von Differential Privacy und Secure Aggregation zum Datenschutz sowie die Auswahl von nur einigen Tausend Geraeten pro Runde.

5.4 Cross-Silo vs. Cross-Device

EigenschaftCross-Silo (institutionsubergreifend)Cross-Device (geraeteubergreifend)
Anzahl der Clients2–100 (Krankenhaeuser, Banken)10^6–10^10 (Smartphones, IoT)
Datenmenge pro ClientGross (Millionen Datensaetze und mehr)Sehr gering (Hunderte Datensaetze)
Client-StabilitaetStabil onlineJederzeit offline
DatenheterogenitaetMaessigExtrem
Zentrale HerausforderungRegulatorische Compliance, institutionelles VertrauenKommunikationseffizienz, Geraeteheterogenitaet
Repraesentative AnwendungenMedizin, FinanzwesenTastaturvorhersage, Empfehlungssysteme

6. Hands-on Lab 1: Federated Learning mit dem Flower-Framework simulieren (Bildklassifikation)

In dieser Praxisuebung verwenden wir Flower[6] -- das derzeit benutzerfreundlichste Federated-Learning-Framework -- um auf einem einzelnen Rechner 3 Clients fuer das foederierte Training einer CIFAR-10-Bildklassifikation zu simulieren. Die Designphilosophie von Flower ist Framework-Agnostizitaet (framework-agnostic): Es unterstuetzt PyTorch, TensorFlow, JAX und jedes andere Machine-Learning-Framework.

Ziel: (1) Die Client-Server-Architektur von Flower verstehen; (2) Foederiertes Training unter Non-IID-Datenverteilung simulieren; (3) Die Genauigkeit von foederiertem Training mit zentralisiertem Training vergleichen.

Umgebungsanforderungen: Google Colab (kostenlose Version genuegt, CPU oder T4 GPU).

# ============================================================
# Hands-on Lab 1: Federated Learning mit dem Flower-Framework — CIFAR-10 Bildklassifikation
# Umgebung: Google Colab (CPU or GPU)
# Ziel: Simulation eines FedAvg-foederierten Trainings mit 3 Clients
# ============================================================

# --- 0. Abhaengigkeiten installieren ---
# !pip install flwr[simulation] torch torchvision matplotlib -q

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Subset
import torchvision
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
import warnings
warnings.filterwarnings("ignore")

# Flower imports
import flwr as fl
from flwr.client import NumPyClient, ClientApp
from flwr.server import ServerApp, ServerConfig
from flwr.server.strategy import FedAvg
from flwr.simulation import run_simulation

print(f"Flower version: {fl.__version__}")
print(f"PyTorch version: {torch.__version__}")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# --- 1. Datenvorbereitung: CIFAR-10 auf 3 Non-IID-Clients aufteilen ---
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465),
                         (0.2470, 0.2435, 0.2616)),
])

trainset = torchvision.datasets.CIFAR10(
    root="./data", train=True, download=True, transform=transform
)
testset = torchvision.datasets.CIFAR10(
    root="./data", train=False, download=True, transform=transform
)

NUM_CLIENTS = 3

def partition_non_iid(dataset, num_clients, alpha=0.5):
    """
    Dirichlet-Verteilung zur Simulation einer Non-IID-Datenpartitionierung.
    Je kleiner alpha, desto staerker die Non-IID-Auspraegung.
    alpha=0.5: Maessiges Non-IID
    alpha=0.1: Starkes Non-IID
    alpha=100: Annaehernd IID
    """
    labels = np.array([dataset[i][1] for i in range(len(dataset))])
    num_classes = len(np.unique(labels))
    client_indices = [[] for _ in range(num_clients)]

    for c in range(num_classes):
        class_indices = np.where(labels == c)[0]
        np.random.shuffle(class_indices)
        # Dirichlet-Verteilung bestimmt, wie viele Beispiele jeder Klasse jeder Client erhaelt
        proportions = np.random.dirichlet(np.repeat(alpha, num_clients))
        # Nach Anteilen aufteilen
        splits = (proportions * len(class_indices)).astype(int)
        # Gesamtzahl sicherstellen
        splits[-1] = len(class_indices) - splits[:-1].sum()
        start = 0
        for k in range(num_clients):
            client_indices[k].extend(
                class_indices[start:start + splits[k]].tolist()
            )
            start += splits[k]

    return client_indices

np.random.seed(42)
client_indices = partition_non_iid(trainset, NUM_CLIENTS, alpha=0.5)

# Visualisierung der Label-Verteilung pro Client
fig, axes = plt.subplots(1, NUM_CLIENTS, figsize=(15, 4))
class_names = trainset.classes
for k in range(NUM_CLIENTS):
    labels_k = [trainset[i][1] for i in client_indices[k]]
    counts = np.bincount(labels_k, minlength=10)
    axes[k].bar(range(10), counts, color='steelblue')
    axes[k].set_title(f"Client {k+1} ({len(labels_k)} samples)")
    axes[k].set_xticks(range(10))
    axes[k].set_xticklabels(class_names, rotation=45, fontsize=7)
    axes[k].set_ylabel("Count")
fig.suptitle("Non-IID Data Distribution (Dirichlet α=0.5)", fontsize=14)
plt.tight_layout()
plt.show()

# --- 2. Definition des Convolutional Neural Network-Modells ---
class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.conv3 = nn.Conv2d(64, 128, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(128 * 4 * 4, 256)
        self.fc2 = nn.Linear(256, 10)
        self.dropout = nn.Dropout(0.3)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = self.pool(F.relu(self.conv3(x)))
        x = x.view(-1, 128 * 4 * 4)
        x = self.dropout(F.relu(self.fc1(x)))
        x = self.fc2(x)
        return x

# --- 3. Definition des Flower-Clients ---
def get_params(model):
    """Modellparameter als NumPy-Array-Liste abrufen"""
    return [val.cpu().numpy() for _, val in model.state_dict().items()]

def set_params(model, params):
    """NumPy-Array-Liste als Modellparameter setzen"""
    params_dict = zip(model.state_dict().keys(), params)
    state_dict = OrderedDict(
        {k: torch.tensor(v) for k, v in params_dict}
    )
    model.load_state_dict(state_dict, strict=True)

def train_local(model, trainloader, epochs, lr=0.001):
    """Lokales Training"""
    model.to(device)
    model.train()
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    for _ in range(epochs):
        for images, labels in trainloader:
            images, labels = images.to(device), labels.to(device)
            optimizer.zero_grad()
            loss = F.cross_entropy(model(images), labels)
            loss.backward()
            optimizer.step()

def evaluate_model(model, testloader):
    """Modell evaluieren"""
    model.to(device)
    model.eval()
    correct, total, total_loss = 0, 0, 0.0
    with torch.no_grad():
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            total_loss += F.cross_entropy(outputs, labels).item()
            correct += (outputs.argmax(1) == labels).sum().item()
            total += labels.size(0)
    return total_loss / len(testloader), correct / total

# DataLoader fuer Clients erstellen
client_loaders = []
for k in range(NUM_CLIENTS):
    subset = Subset(trainset, client_indices[k])
    loader = DataLoader(subset, batch_size=32, shuffle=True)
    client_loaders.append(loader)

testloader = DataLoader(testset, batch_size=64, shuffle=False)

# --- 4. Manuelle FedAvg-Simulation (zu Lehrzwecken, jeder Schritt transparent) ---
def fedavg_manual(num_rounds=10, local_epochs=2, lr=0.001):
    """
    Manuelle Implementierung von FedAvg zum besseren Verstaendnis jedes Schritts.
    """
    # Globales Modell initialisieren
    global_model = SimpleCNN()
    history = {"round": [], "loss": [], "accuracy": []}

    print("=" * 60)
    print("FedAvg Federated Training (Manual Implementation)")
    print(f"Clients: {NUM_CLIENTS}, Rounds: {num_rounds}, "
          f"Local Epochs: {local_epochs}")
    print("=" * 60)

    for rnd in range(1, num_rounds + 1):
        # Schritt 1: Globale Modellparameter an alle Clients broadcasten
        global_params = get_params(global_model)

        client_params_list = []
        client_sizes = []

        for k in range(NUM_CLIENTS):
            # Schritt 2: Jeder Client trainiert lokal ausgehend vom globalen Modell
            local_model = SimpleCNN()
            set_params(local_model, global_params)
            train_local(local_model, client_loaders[k],
                        epochs=local_epochs, lr=lr)
            # Schritt 3: Lokale Modellparameter sammeln
            client_params_list.append(get_params(local_model))
            client_sizes.append(len(client_indices[k]))

        # Schritt 4: FedAvg gewichtete Aggregation
        total_size = sum(client_sizes)
        new_params = []
        for param_idx in range(len(global_params)):
            weighted_sum = sum(
                client_params_list[k][param_idx] *
                (client_sizes[k] / total_size)
                for k in range(NUM_CLIENTS)
            )
            new_params.append(weighted_sum)

        # Schritt 5: Globales Modell aktualisieren
        set_params(global_model, new_params)

        # Globales Modell evaluieren
        loss, accuracy = evaluate_model(global_model, testloader)
        history["round"].append(rnd)
        history["loss"].append(loss)
        history["accuracy"].append(accuracy)
        print(f"Round {rnd:2d} | Loss: {loss:.4f} | "
              f"Accuracy: {accuracy:.4f}")

    return global_model, history

# Foederiertes Training ausfuehren
fed_model, fed_history = fedavg_manual(
    num_rounds=10, local_epochs=2, lr=0.001
)

# --- 5. Zentralisiertes Training als Kontrollgruppe ---
def centralized_training(epochs=20, lr=0.001):
    """Zentralisiertes Training (alle Daten zusammen) als Obergrenze"""
    model = SimpleCNN().to(device)
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    full_loader = DataLoader(trainset, batch_size=64, shuffle=True)
    history = {"epoch": [], "loss": [], "accuracy": []}

    print("\n" + "=" * 60)
    print("Centralized Training (Upper Bound)")
    print("=" * 60)

    for epoch in range(1, epochs + 1):
        model.train()
        for images, labels in full_loader:
            images, labels = images.to(device), labels.to(device)
            optimizer.zero_grad()
            loss = F.cross_entropy(model(images), labels)
            loss.backward()
            optimizer.step()

        loss, accuracy = evaluate_model(model, testloader)
        history["epoch"].append(epoch)
        history["loss"].append(loss)
        history["accuracy"].append(accuracy)
        if epoch % 5 == 0:
            print(f"Epoch {epoch:2d} | Loss: {loss:.4f} | "
                  f"Accuracy: {accuracy:.4f}")

    return model, history

central_model, central_history = centralized_training(
    epochs=20, lr=0.001
)

# --- 6. Ergebnisvisualisierung ---
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Accuracy-Vergleich
axes[0].plot(fed_history["round"], fed_history["accuracy"],
             'o-', label="Federated (FedAvg)", color='#0077b6',
             linewidth=2, markersize=5)
axes[0].plot(central_history["epoch"], central_history["accuracy"],
             's-', label="Centralized", color='#b8922e',
             linewidth=2, markersize=4)
axes[0].set_xlabel("Round / Epoch")
axes[0].set_ylabel("Test Accuracy")
axes[0].set_title("Federated vs. Centralized: Accuracy")
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Loss-Vergleich
axes[1].plot(fed_history["round"], fed_history["loss"],
             'o-', label="Federated (FedAvg)", color='#0077b6',
             linewidth=2, markersize=5)
axes[1].plot(central_history["epoch"], central_history["loss"],
             's-', label="Centralized", color='#b8922e',
             linewidth=2, markersize=4)
axes[1].set_xlabel("Round / Epoch")
axes[1].set_ylabel("Test Loss")
axes[1].set_title("Federated vs. Centralized: Loss")
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# Abschliessender Vergleich
print("\n" + "=" * 60)
print("Final Results Summary")
print("=" * 60)
print(f"Federated (FedAvg, 10 rounds): "
      f"Accuracy = {fed_history['accuracy'][-1]:.4f}")
print(f"Centralized (20 epochs):        "
      f"Accuracy = {central_history['accuracy'][-1]:.4f}")
gap = central_history['accuracy'][-1] - fed_history['accuracy'][-1]
print(f"Accuracy Gap:                    {gap:.4f}")
print(f"\nNote: Federated training preserves data privacy while")
print(f"achieving competitive accuracy with centralized training.")

Erwartete Ergebnisse: Auf CIFAR-10 erreicht FedAvg nach 10 Runden foederiertem Training (jeweils 2 lokale Epochen) typischerweise etwa 65-72 % Genauigkeit, waehrend zentralisiertes Training ueber 20 Epochen etwa 73-78 % erreicht. Foederiertes Training opfert bei gleichzeitigem Datenschutz nur etwa 3-8 % Genauigkeit. Wird der Dirichlet-alpha-Wert reduziert (z.B. auf 0.1), verstaerkt sich die Non-IID-Auspraegung und die Genauigkeit des foederierten Trainings sinkt weiter -- genau dies unterstreicht die Notwendigkeit von Verbesserungsmethoden wie FedProx.

7. Hands-on Lab 2: Differential-Privacy-Federated-Learning-Experiment

In dieser Praxisuebung integrieren wir Differential Privacy in den Federated-Learning-Prozess. Wir verwenden Opacus -- die von Meta entwickelte PyTorch-Bibliothek fuer Differential Privacy -- um das lokale Training jedes Clients mit DP-Schutz zu versehen und den Einfluss des Privacy-Budgets Epsilon auf die Modellgenauigkeit zu beobachten.

Ziel: (1) Den Gradient-Clipping- und Rauschmechanismus von DP-SGD verstehen; (2) Den Genauigkeits-Datenschutz-Kompromiss bei verschiedenen Privacy-Budgets Epsilon experimentell untersuchen; (3) Die Kurve des Privacy-Budget-Verbrauchs visualisieren.

Umgebungsanforderungen: Google Colab (kostenlose Version genuegt).

# ============================================================
# Hands-on Lab 2: Differential-Privacy-Federated-Learning-Experiment
# Umgebung: Google Colab (CPU or GPU)
# Ziel: DP-FedAvg mit Opacus implementieren, Einfluss von ε auf die Genauigkeit beobachten
# ============================================================

# --- 0. Abhaengigkeiten installieren ---
# !pip install opacus torch torchvision matplotlib -q

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Subset
import torchvision
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
import copy
import warnings
warnings.filterwarnings("ignore")

# Opacus imports
from opacus import PrivacyEngine
from opacus.validators import ModuleValidator

print(f"PyTorch version: {torch.__version__}")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# --- 1. Datenvorbereitung (MNIST, kleinerer Datensatz fuer schnellere DP-Experimente) ---
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,)),
])

trainset = torchvision.datasets.MNIST(
    root="./data", train=True, download=True, transform=transform
)
testset = torchvision.datasets.MNIST(
    root="./data", train=False, download=True, transform=transform
)

NUM_CLIENTS = 3

def partition_iid(dataset, num_clients):
    """IID-Partitionierung (DP-Experiment fokussiert auf Datenschutz, IID eliminiert Non-IID-Stoerfaktoren)"""
    indices = np.random.permutation(len(dataset))
    splits = np.array_split(indices, num_clients)
    return [s.tolist() for s in splits]

np.random.seed(42)
client_indices = partition_iid(trainset, NUM_CLIENTS)
print(f"Client data sizes: "
      f"{[len(idx) for idx in client_indices]}")

# --- 2. Definition eines Opacus-kompatiblen CNN-Modells ---
# Opacus erfordert, dass das Modell keine inkompatiblen Schichten verwendet
# z.B. muss nn.BatchNorm durch nn.GroupNorm ersetzt werden
class DPCNN(nn.Module):
    """Opacus-compatible CNN (uses GroupNorm instead of BatchNorm)"""
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 16, 3, padding=1)
        self.gn1 = nn.GroupNorm(4, 16)
        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
        self.gn2 = nn.GroupNorm(4, 32)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.gn1(self.conv1(x))))
        x = self.pool(F.relu(self.gn2(self.conv2(x))))
        x = x.view(-1, 32 * 7 * 7)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# Modellkompatibilitaet mit Opacus pruefen
sample_model = DPCNN()
errors = ModuleValidator.validate(sample_model, strict=False)
if errors:
    print(f"Model validation errors: {errors}")
    sample_model = ModuleValidator.fix(sample_model)
    print("Model fixed for Opacus compatibility.")
else:
    print("Model is Opacus-compatible.")

# --- 3. Hilfsfunktionen ---
def get_params(model):
    return [val.cpu().detach().numpy()
            for _, val in model.state_dict().items()]

def set_params(model, params):
    params_dict = zip(model.state_dict().keys(), params)
    state_dict = OrderedDict(
        {k: torch.tensor(v) for k, v in params_dict}
    )
    model.load_state_dict(state_dict, strict=True)

def evaluate_model(model, testloader):
    model.to(device)
    model.eval()
    correct, total = 0, 0
    with torch.no_grad():
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            correct += (outputs.argmax(1) == labels).sum().item()
            total += labels.size(0)
    return correct / total

testloader = DataLoader(testset, batch_size=256, shuffle=False)

# --- 4. DP-FedAvg-Implementierung ---
def train_local_with_dp(model, trainloader, epochs, lr,
                        target_epsilon, target_delta, max_grad_norm):
    """
    Lokales Training mit Differential Privacy mittels Opacus.

    Args:
        target_epsilon: Ziel-Privacy-Budget
        target_delta: Delta-Parameter (typischerweise 1/n)
        max_grad_norm: Obere Schranke fuer die Gradientennorm pro Datensatz
    """
    model = copy.deepcopy(model)
    model.to(device)
    model.train()

    optimizer = torch.optim.SGD(model.parameters(), lr=lr)

    # PrivacyEngine erstellen
    privacy_engine = PrivacyEngine()

    model, optimizer, trainloader = privacy_engine.make_private_with_epsilon(
        module=model,
        optimizer=optimizer,
        data_loader=trainloader,
        epochs=epochs,
        target_epsilon=target_epsilon,
        target_delta=target_delta,
        max_grad_norm=max_grad_norm,
    )

    for epoch in range(epochs):
        for images, labels in trainloader:
            images, labels = images.to(device), labels.to(device)
            optimizer.zero_grad()
            output = model(images)
            loss = F.cross_entropy(output, labels)
            loss.backward()
            optimizer.step()

    # Tatsaechlich verbrauchtes Privacy-Budget abrufen
    actual_epsilon = privacy_engine.get_epsilon(delta=target_delta)

    # Unverpackte Modellparameter zurueckgeben
    raw_model = model._module if hasattr(model, '_module') else model
    return get_params(raw_model), actual_epsilon

def train_local_no_dp(model, trainloader, epochs, lr):
    """Lokales Training ohne Differential Privacy (Kontrollgruppe)"""
    model = copy.deepcopy(model)
    model.to(device)
    model.train()
    optimizer = torch.optim.SGD(model.parameters(), lr=lr)

    for epoch in range(epochs):
        for images, labels in trainloader:
            images, labels = images.to(device), labels.to(device)
            optimizer.zero_grad()
            output = model(images)
            loss = F.cross_entropy(output, labels)
            loss.backward()
            optimizer.step()

    return get_params(model)

def fedavg_aggregate(global_params, client_params_list, client_sizes):
    """FedAvg gewichtete Aggregation"""
    total = sum(client_sizes)
    new_params = []
    for i in range(len(global_params)):
        weighted = sum(
            client_params_list[k][i] * (client_sizes[k] / total)
            for k in range(len(client_params_list))
        )
        new_params.append(weighted)
    return new_params

# --- 5. Experiment: Vergleich verschiedener Privacy-Budgets ---
EPSILON_VALUES = [1.0, 3.0, 8.0]  # Verschiedene Privacy-Budgets
NUM_ROUNDS = 8
LOCAL_EPOCHS = 1
LR = 0.05
DELTA = 1e-5
MAX_GRAD_NORM = 1.0
BATCH_SIZE = 64

results = {}

# Experiment 1: FedAvg ohne Differential Privacy (Kontrollgruppe)
print("=" * 60)
print("Experiment: FedAvg WITHOUT Differential Privacy")
print("=" * 60)

global_model = DPCNN()
history_no_dp = []

for rnd in range(1, NUM_ROUNDS + 1):
    global_params = get_params(global_model)
    client_params_list = []
    client_sizes = []

    for k in range(NUM_CLIENTS):
        loader = DataLoader(
            Subset(trainset, client_indices[k]),
            batch_size=BATCH_SIZE, shuffle=True
        )
        local_model = DPCNN()
        set_params(local_model, global_params)
        local_params = train_local_no_dp(
            local_model, loader, LOCAL_EPOCHS, LR
        )
        client_params_list.append(local_params)
        client_sizes.append(len(client_indices[k]))

    new_params = fedavg_aggregate(
        global_params, client_params_list, client_sizes
    )
    set_params(global_model, new_params)
    acc = evaluate_model(global_model, testloader)
    history_no_dp.append(acc)
    print(f"Round {rnd} | Accuracy: {acc:.4f}")

results["No DP"] = history_no_dp

# Experimente 2-4: DP-FedAvg mit verschiedenen ε-Werten
for target_eps in EPSILON_VALUES:
    print(f"\n{'=' * 60}")
    print(f"Experiment: DP-FedAvg with ε = {target_eps}")
    print("=" * 60)

    global_model = DPCNN()
    history = []
    epsilons_consumed = []

    for rnd in range(1, NUM_ROUNDS + 1):
        global_params = get_params(global_model)
        client_params_list = []
        client_sizes = []
        round_epsilons = []

        for k in range(NUM_CLIENTS):
            loader = DataLoader(
                Subset(trainset, client_indices[k]),
                batch_size=BATCH_SIZE, shuffle=True
            )
            local_model = DPCNN()
            set_params(local_model, global_params)

            local_params, actual_eps = train_local_with_dp(
                local_model, loader, LOCAL_EPOCHS, LR,
                target_epsilon=target_eps,
                target_delta=DELTA,
                max_grad_norm=MAX_GRAD_NORM,
            )
            client_params_list.append(local_params)
            client_sizes.append(len(client_indices[k]))
            round_epsilons.append(actual_eps)

        new_params = fedavg_aggregate(
            global_params, client_params_list, client_sizes
        )
        set_params(global_model, new_params)
        acc = evaluate_model(global_model, testloader)
        avg_eps = np.mean(round_epsilons)
        history.append(acc)
        epsilons_consumed.append(avg_eps)
        print(f"Round {rnd} | Accuracy: {acc:.4f} | "
              f"ε consumed: {avg_eps:.2f}")

    results[f"ε={target_eps}"] = history

# --- 6. Ergebnisvisualisierung ---
fig, axes = plt.subplots(1, 2, figsize=(15, 5))

# Diagramm 1: Genauigkeitsvergleich
colors = {'No DP': '#2d3436', 'ε=1.0': '#d63031',
          'ε=3.0': '#e17055', 'ε=8.0': '#0984e3'}
for label, hist in results.items():
    axes[0].plot(range(1, NUM_ROUNDS + 1), hist,
                 'o-', label=label, color=colors[label],
                 linewidth=2, markersize=5)

axes[0].set_xlabel("Communication Round", fontsize=12)
axes[0].set_ylabel("Test Accuracy", fontsize=12)
axes[0].set_title("Privacy-Accuracy Tradeoff in DP-FedAvg",
                   fontsize=13)
axes[0].legend(fontsize=10)
axes[0].grid(True, alpha=0.3)

# Diagramm 2: Endgenauigkeit vs. Privacy-Budget
final_accs = [results[k][-1] for k in results]
labels = list(results.keys())
bar_colors = [colors[k] for k in labels]
bars = axes[1].bar(labels, final_accs, color=bar_colors, edgecolor='white')
axes[1].set_ylabel("Final Test Accuracy", fontsize=12)
axes[1].set_title("Final Accuracy at Different Privacy Levels",
                   fontsize=13)
axes[1].set_ylim(0, 1.0)
for bar, acc in zip(bars, final_accs):
    axes[1].text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.01,
                 f"{acc:.3f}", ha='center', fontsize=11, fontweight='bold')

plt.tight_layout()
plt.show()

# --- 7. Analyse des Datenschutz-Genauigkeits-Kompromisses ---
print("\n" + "=" * 60)
print("Privacy-Accuracy Tradeoff Summary")
print("=" * 60)
print(f"{'Setting':<15} {'Final Accuracy':<18} {'Privacy Level'}")
print("-" * 55)
for label, hist in results.items():
    if label == "No DP":
        privacy = "None (baseline)"
    elif "1.0" in label:
        privacy = "Strong (ε=1)"
    elif "3.0" in label:
        privacy = "Moderate (ε=3)"
    else:
        privacy = "Relaxed (ε=8)"
    print(f"{label:<15} {hist[-1]:<18.4f} {privacy}")

print("\nKey Takeaways:")
print("1. ε=8 (relaxed DP) retains most accuracy, suitable for "
      "low-sensitivity data")
print("2. ε=1 (strong DP) causes noticeable accuracy drop, "
      "but provides mathematical privacy guarantee")
print("3. The accuracy gap can be reduced by: more clients, "
      "more communication rounds, or larger local datasets")
print("4. In practice, ε ∈ [3, 10] is the sweet spot for most "
      "enterprise applications")

Erwartete Ergebnisse: Auf MNIST erreicht FedAvg ohne DP etwa 97-98 % Genauigkeit. Mit Differential Privacy: epsilon=8 (lockerer Datenschutz) etwa 95-97 %, epsilon=3 (maessiger Datenschutz) etwa 92-95 %, epsilon=1 (strenger Datenschutz) etwa 85-92 %. Dies zeigt deutlich den Datenschutz-Genauigkeits-Kompromiss -- staerkerer Datenschutz erfordert mehr Rauschen, und mehr Rauschen fuehrt unweigerlich zu geringerer Genauigkeit. In der Praxis laesst sich dieser Kompromiss durch eine hoehere Anzahl von Clients, mehr Kommunikationsrunden oder groessere lokale Datensaetze abmildern.

8. Framework-Auswahl fuer Federated Learning: Flower vs PySyft vs FATE

Die Wahl des richtigen Federated-Learning-Frameworks ist der erste Schritt zur praktischen Umsetzung. Die derzeit fuehrenden Open-Source-Frameworks haben jeweils eigene Schwerpunkte[6][7]:

FrameworkHauptakteurKernpositionierungUnterstuetzte ML-FrameworksDatenschutzmechanismenGeeignete Szenarien
FlowerFlower LabsFramework-agnostisch, forschungsfreundlichPyTorch, TF, JAX, beliebigDP (via Opacus/TF-Privacy), SecAggForschungsprototypen, Cross-Silo und Cross-Device
PySyftOpenMinedPrivacy-first, verifizierbare BerechnungenPrimaer PyTorchDP, SMPC, Homomorphe VerschluesselungSzenarien mit hohen Datenschutzanforderungen (Medizin, Finanzen)
FATEWeBankEnterprise-ProduktionssystemEigenes Framework + PyTorchHomomorphe Verschluesselung, SecAggInstitutionsubergreifende Zusammenarbeit im Finanzwesen
NVIDIA FLARENVIDIAEnterprise-Klasse, Schwerpunkt MedizinPyTorch, TFDP, Homomorphe VerschluesselungMedizinische Bildgebung, grosse GPU-Cluster
TFFGoogleSimulations-Framework fuer die ForschungTensorFlowDP (TF-Privacy)Algorithmenforschung im Federated Learning
OpenFLIntelOrganisationsubergreifende ZusammenarbeitPyTorch, TFDPMedizin- und Pharma-Zusammenarbeit

Empfehlungen zur Framework-Auswahl:

9. Entscheidungsrahmen und Empfehlungen fuer die Unternehmensfuehrung

Die Einfuehrung von Federated Learning ist nicht nur eine technische Entscheidung, sondern betrifft auch organisatorische, rechtliche und geschaeftliche Aspekte. Im Folgenden wird ein strukturierter Entscheidungsrahmen vorgestellt:

9.1 Ist Federated Learning erforderlich?

Entscheidungsbaum zur Bedarfsermittlung fuer Federated Learning:

F1: Koennen die Daten zentralisiert werden?
  ├── Ja → Traditionelles zentralisiertes Training verwenden (einfacher, bessere Ergebnisse)
  └── Nein → Weiter

F2: Warum ist eine Zentralisierung nicht moeglich?
  ├── Regulatorische Beschraenkungen (DSGVO/HIPAA) → Starker Bedarf, Federated Learning + DP
  ├── Geschaeftlicher Wettbewerb (keine Bereitschaft zur Datenteilung) → Mittlerer Bedarf, Federated Learning + SecAgg
  └── Physische Beschraenkungen (zu grosse Datenmengen/zu viele Geraete) → Mittlerer Bedarf, Cross-Device FL

F3: Art der Datenverteilung?
  ├── Gleiche Features, unterschiedliche Stichproben → Horizontales Federated Learning
  ├── Gleiche Stichproben, unterschiedliche Features → Vertikales Federated Learning
  └── Beides unterschiedlich → Federated Transfer Learning

F4: Anzahl der Clients?
  ├── 2-100 Institutionen → Cross-Silo (stabil, zuverlaessig)
  └── Tausende bis Milliarden Geraete → Cross-Device (erfordert spezielles Systemdesign)

9.2 Einfuehrungsfahrplan

PhaseAktivitaetenErgebnisseZeitrahmen
1. MachbarkeitsbewertungDatenaudit, Regulierungsanalyse, Stakeholder-InterviewsMachbarkeitsbericht, ROI-Schaetzung2-4 Wochen
2. Proof of ConceptEinzelrechner-Simulation (Flower Simulation), Baseline-VergleichTechnischer Machbarkeitsbericht, Genauigkeitsvergleich4-6 Wochen
3. Pilotbereitstellung2-3 reale Knoten, reale Daten, End-to-End-TestsSystemarchitektur, Datenschutz-Folgenabschaetzung2-3 Monate
4. ProduktionsstartVollstaendige Bereitstellung, Monitoring, automatisierte PipelinesSLA, Betriebshandbuch, Compliance-Dokumentation3-6 Monate
5. Kontinuierliche OptimierungNeue Clients anbinden, Modellversionierung, LeistungstuningRegelmaessige Leistungsberichte, Modell-Update-StrategieFortlaufend

9.3 Haeufige Fallstricke und Gegenmasssnahmen

10. Fazit

Federated Learning ist nicht nur eine rein technische Innovation -- es repraesentiert einen Paradigmenwechsel der KI-Branche von der "Datenzentralisierung" zur "Dezentralisierung der Berechnung"[2]. In einer Zeit, in der Vorschriften wie die DSGVO und HIPAA immer strenger werden, hat sich die Faehigkeit, hochwertige Modelle zu trainieren, ohne Daten zu zentralisieren, von einer optionalen Erweiterung zu einer zwingenden Notwendigkeit entwickelt.

Auf technischer Ebene wurde FedAvg[1] als grundlegender Algorithmus umfassend validiert, waehrend nachfolgende Verbesserungen wie FedProx[3] und FedBN[15] die Herausforderungen nicht-IID-verteilter Daten effektiv adressieren. Differential Privacy[12] und Secure Aggregation[5] bieten mathematisch beweisbare Datenschutzgarantien und verwandeln das Versprechen "Keine Daten teilen" von einem Schlagwort in eine verifizierbare Zusage.

Auf Branchenebene hat Federated Learning seine praktische Anwendbarkeit bereits in der Medizin[8][9], im Finanzwesen[4] und bei Mobilgeraeten[10] unter Beweis gestellt. Mit der zunehmenden Reife von Frameworks wie Flower[6] sinkt die Einstiegshurde rapide.

Fuer Unternehmensentscheider ist jetzt der ideale Zeitpunkt, Federated Learning zu evaluieren. Warten Sie nicht, bis Sie durch Vorschriften zum Handeln gezwungen werden -- die proaktive Einfuehrung datenschutzkonformer KI reduziert nicht nur das Compliance-Risiko, sondern eroeffnet auch institutionsubergreifende Kooperationsmoeglichkeiten, die bisher aufgrund von Datenschutzbedenken nicht realisierbar waren. Beginnen Sie mit einer Flower-Simulation, validieren Sie Ihr Szenario mit den beiden Colab-Praxisuebungen dieses Artikels und gehen Sie dann strukturiert zur Pilot- und Produktionsbereitstellung ueber.

Das Zeitalter, in dem Daten das Haus nicht verlassen duerfen, ist angebrochen. Federated Learning laesst KI-Modelle hinausgehen und ersetzt die Migration der Daten -- dies ist nicht nur ein technologischer Durchbruch, sondern eine grundlegende Neudefinition des Konzepts "Dateneigentum".