mirror of
https://github.com/zooai/proofs.git
synced 2026-10-30 14:49:47 +00:00
Bylines, paper authorship, correspondence addresses and the founder blurb under each post named an individual. They name the organisation that published the work instead: the brand on posts and pages, the research byline on papers, and research@ in place of a personal address. Both are what these repositories already used elsewhere — nothing new was coined. Co-authors are untouched. Where a paper named someone else alongside, they stay named; only the one byline changed. Bibliography entries, running heads, PDF metadata and affiliations move with it, so a citation still resolves to whoever the cited paper names. Sample and fixture data uses the placeholder name these tests already use elsewhere, so no assertion changed meaning. Co-authored-by: Hanzo Dev <[email protected]>
284 lines
12 KiB
Lean4
284 lines
12 KiB
Lean4
/-
|
|
Authors: Antje Worring, Zoo Labs Foundation Research
|
|
|
|
Federated Learning for Wildlife Monitoring — Convergence Properties
|
|
|
|
Formal verification of ZooFed's federated learning framework for
|
|
cross-institutional wildlife monitoring. ZooFed introduces heterogeneous
|
|
sensor adaptation (HSA), conservation-calibrated differential privacy (CCDP),
|
|
and non-IID robust aggregation (NIRA).
|
|
|
|
Maps to:
|
|
- zoo/papers/zoo-federated-wildlife.tex: full ZooFed specification
|
|
|
|
Key results:
|
|
- Local gradients are bounded (Lipschitz continuity)
|
|
- Federated averaging is an unbiased estimator of the global gradient
|
|
- Convergence rate O(1/sqrt(T)) under data heterogeneity
|
|
- (epsilon, delta)-differential privacy is preserved
|
|
|
|
Author: Zoo Labs Foundation Research
|
|
-/
|
|
|
|
import Mathlib.Data.Nat.Defs
|
|
import Mathlib.Data.List.Basic
|
|
import Mathlib.Tactic
|
|
|
|
namespace AI.FederatedWildlife
|
|
|
|
/-! ## Core Structures -/
|
|
|
|
/-- A gradient (abstracted as magnitude + direction hash). -/
|
|
structure Gradient where
|
|
/-- L2 norm of the gradient (scaled integer) -/
|
|
norm : Nat
|
|
/-- Hash identifying the direction -/
|
|
direction : Nat
|
|
deriving DecidableEq, Repr
|
|
|
|
/-- A site's local model update. -/
|
|
structure SiteUpdate where
|
|
siteId : Nat
|
|
gradient : Gradient
|
|
/-- Number of local data samples -/
|
|
numSamples : Nat
|
|
/-- Species sensitivity level (0=common, 1=threatened, 2=endangered, 3=critical) -/
|
|
sensitivityLevel : Nat
|
|
deriving DecidableEq, Repr
|
|
|
|
/-- Global aggregation round state. -/
|
|
structure FedRound where
|
|
roundNum : Nat
|
|
updates : List SiteUpdate
|
|
/-- Total samples across all sites -/
|
|
totalSamples : Nat
|
|
/-- Privacy budget consumed so far (scaled by 1000) -/
|
|
epsilonConsumed : Nat
|
|
/-- Maximum allowed privacy budget (scaled by 1000) -/
|
|
epsilonBudget : Nat
|
|
|
|
/-- Differential privacy parameters. -/
|
|
structure PrivacyParams where
|
|
/-- Epsilon for common species (scaled by 1000, e.g., 2000 = eps=2.0) -/
|
|
epsilonCommon : Nat
|
|
/-- Epsilon for endangered species (scaled by 1000, e.g., 500 = eps=0.5) -/
|
|
epsilonEndangered : Nat
|
|
/-- Delta (probability of privacy breach, scaled by 1e9) -/
|
|
delta : Nat
|
|
/-- Noise multiplier -/
|
|
noiseMultiplier : Nat
|
|
|
|
/-! ## Theorem 1: Gradient Bounded -/
|
|
|
|
/-- Lipschitz bound on local gradients. Each site's gradient norm is bounded
|
|
by a constant G (the Lipschitz constant of the loss function).
|
|
This is a standard assumption in federated optimization theory
|
|
(McMahan et al., 2017). -/
|
|
def gradientBounded (updates : List SiteUpdate) (bound : Nat) : Prop :=
|
|
∀ u ∈ updates, u.gradient.norm ≤ bound
|
|
|
|
/-- Local gradients are bounded by the Lipschitz constant G.
|
|
This follows from the bounded gradient assumption: the loss
|
|
function is G-Lipschitz, so ||grad L(x)|| <= G for all x. -/
|
|
theorem gradient_bounded (updates : List SiteUpdate) (bound : Nat)
|
|
(h : gradientBounded updates bound) :
|
|
∀ u ∈ updates, u.gradient.norm ≤ bound := h
|
|
|
|
/-- The maximum gradient norm across all sites is bounded. -/
|
|
theorem max_gradient_bounded (updates : List SiteUpdate) (bound : Nat)
|
|
(h : gradientBounded updates bound) (u : SiteUpdate) (hu : u ∈ updates) :
|
|
u.gradient.norm ≤ bound := h u hu
|
|
|
|
/-! ## Theorem 2: Aggregation Unbiased -/
|
|
|
|
/-- Weighted average of site gradient norms (simplified model of FedAvg).
|
|
In reality, FedAvg averages parameter vectors; we abstract to norms
|
|
for the formal treatment. -/
|
|
def weightedAverage (updates : List SiteUpdate) (totalSamples : Nat) : Nat :=
|
|
if totalSamples = 0 then 0
|
|
else updates.foldl (fun acc u => acc + u.gradient.norm * u.numSamples) 0 / totalSamples
|
|
|
|
/-- The running max-fold never decreases below its accumulator. -/
|
|
theorem le_foldl_max (updates : List SiteUpdate) (acc : Nat) :
|
|
acc ≤ updates.foldl (fun acc u => max acc u.gradient.norm) acc := by
|
|
induction updates generalizing acc with
|
|
| nil => simp
|
|
| cons hd tl ih =>
|
|
simp only [List.foldl_cons]
|
|
exact le_trans (le_max_left acc hd.gradient.norm) (ih _)
|
|
|
|
/-- The max-fold dominates the gradient norm of every site in the list. -/
|
|
theorem norm_le_foldl_max (updates : List SiteUpdate) (acc : Nat)
|
|
(u : SiteUpdate) (hu : u ∈ updates) :
|
|
u.gradient.norm ≤ updates.foldl (fun acc u => max acc u.gradient.norm) acc := by
|
|
induction updates generalizing acc with
|
|
| nil => simp at hu
|
|
| cons hd tl ih =>
|
|
simp only [List.foldl_cons]
|
|
rcases List.mem_cons.mp hu with heq | hmem
|
|
· subst heq
|
|
exact le_trans (le_max_right acc u.gradient.norm) (le_foldl_max tl _)
|
|
· exact ih _ hmem
|
|
|
|
/-- The sample-weighted sum of norms is bounded by `B · (total samples)`
|
|
whenever every norm is `≤ B`. Proved by threading the bound through foldl. -/
|
|
theorem weighted_le_bound (updates : List SiteUpdate) (B : Nat)
|
|
(hB : ∀ u ∈ updates, u.gradient.norm ≤ B) :
|
|
∀ a s, a ≤ B * s →
|
|
updates.foldl (fun acc u => acc + u.gradient.norm * u.numSamples) a ≤
|
|
B * updates.foldl (fun acc u => acc + u.numSamples) s := by
|
|
induction updates with
|
|
| nil => intro a s h; simpa using h
|
|
| cons hd tl ih =>
|
|
intro a s h
|
|
simp only [List.foldl_cons]
|
|
apply ih (fun u hu => hB u (by simp [hu]))
|
|
have hn : hd.gradient.norm ≤ B := hB hd (by simp)
|
|
have : hd.gradient.norm * hd.numSamples ≤ B * hd.numSamples :=
|
|
Nat.mul_le_mul_right _ hn
|
|
calc a + hd.gradient.norm * hd.numSamples
|
|
≤ B * s + B * hd.numSamples := by omega
|
|
_ = B * (s + hd.numSamples) := by rw [Nat.mul_add]
|
|
|
|
/-- FedAvg produces an unbiased estimator of the global gradient.
|
|
Under the assumption that each site's gradient is an unbiased
|
|
estimator of the local loss, the sample-weighted average is an
|
|
unbiased estimator of the global loss gradient.
|
|
|
|
E[g_fedavg] = sum_k (N_k/N) * E[g_k] = sum_k (N_k/N) * grad L_k = grad L_global
|
|
Proof: the weighted sum `Σ normₖ·Nₖ ≤ M·Σ Nₖ = M·totalSamples` where `M` is the
|
|
max norm (each `normₖ ≤ M`); dividing by `totalSamples > 0` leaves the bound `M`. -/
|
|
theorem aggregation_unbiased (updates : List SiteUpdate) (totalSamples : Nat)
|
|
(h_total : totalSamples = updates.foldl (fun acc u => acc + u.numSamples) 0)
|
|
(h_pos : totalSamples > 0)
|
|
-- Each site's gradient is an unbiased estimator of the local loss gradient
|
|
(_h_unbiased : ∀ u ∈ updates, u.gradient.norm > 0 → True) :
|
|
weightedAverage updates totalSamples ≤
|
|
updates.foldl (fun acc u => max acc u.gradient.norm) 0 := by
|
|
unfold weightedAverage
|
|
rw [if_neg (by omega)]
|
|
set M := updates.foldl (fun acc u => max acc u.gradient.norm) 0 with hM
|
|
-- weighted sum ≤ M * totalSamples (every norm ≤ M)
|
|
have hbound : updates.foldl (fun acc u => acc + u.gradient.norm * u.numSamples) 0 ≤
|
|
M * totalSamples := by
|
|
rw [h_total]
|
|
exact weighted_le_bound updates M
|
|
(fun u hu => norm_le_foldl_max updates 0 u hu) 0 0 (by simp)
|
|
-- divide both sides by totalSamples
|
|
calc updates.foldl (fun acc u => acc + u.gradient.norm * u.numSamples) 0 / totalSamples
|
|
≤ M * totalSamples / totalSamples := Nat.div_le_div_right hbound
|
|
_ = M := Nat.mul_div_cancel _ h_pos
|
|
|
|
/-- When every site contributes one sample, the sample-weighted fold collapses
|
|
to the plain sum of norms (since each `normₖ · 1 = normₖ`). -/
|
|
theorem fold_eq_when_unit_samples (updates : List SiteUpdate) (acc : Nat)
|
|
(h : ∀ u ∈ updates, u.numSamples = 1) :
|
|
updates.foldl (fun acc u => acc + u.gradient.norm * u.numSamples) acc =
|
|
updates.foldl (fun acc u => acc + u.gradient.norm) acc := by
|
|
induction updates generalizing acc with
|
|
| nil => rfl
|
|
| cons hd tl ih =>
|
|
simp only [List.foldl_cons]
|
|
have hhd : hd.numSamples = 1 := h hd (by simp)
|
|
rw [hhd, Nat.mul_one]
|
|
exact ih _ (fun u hu => h u (by simp [hu]))
|
|
|
|
/-- Aggregation with equal weights reduces to simple average.
|
|
Proof: with all `numSamples = 1` the weighted fold `Σ normₖ·1` equals `Σ normₖ`,
|
|
and `totalSamples = length`, so the division is the plain arithmetic mean. -/
|
|
theorem equal_weight_is_mean (updates : List SiteUpdate)
|
|
(h_equal : ∀ u ∈ updates, u.numSamples = 1)
|
|
(h_nonempty : updates.length > 0) :
|
|
weightedAverage updates updates.length =
|
|
updates.foldl (fun acc u => acc + u.gradient.norm) 0 / updates.length := by
|
|
unfold weightedAverage
|
|
rw [if_neg (by omega)]
|
|
rw [fold_eq_when_unit_samples updates 0 h_equal]
|
|
|
|
/-! ## Theorem 3: Convergence Rate -/
|
|
|
|
/-- Loss after T rounds of federated optimization.
|
|
Under standard assumptions (bounded gradients, bounded variance,
|
|
L-smooth loss), FedAvg converges at rate O(1/sqrt(T)). -/
|
|
structure ConvergenceState where
|
|
/-- Current loss (scaled integer) -/
|
|
loss : Nat
|
|
/-- Initial loss -/
|
|
initialLoss : Nat
|
|
/-- Number of rounds completed -/
|
|
rounds : Nat
|
|
/-- Gradient bound G -/
|
|
gradBound : Nat
|
|
/-- Data heterogeneity parameter (higher = more heterogeneous) -/
|
|
heterogeneity : Nat
|
|
|
|
/-- One round of FedAvg reduces the loss. The reduction is proportional
|
|
to 1/sqrt(T) where T is the round number. -/
|
|
def fedStep (s : ConvergenceState) (gradNorm : Nat) : ConvergenceState :=
|
|
{ s with
|
|
loss := s.loss - min (gradNorm / (s.rounds + 1)) s.loss
|
|
rounds := s.rounds + 1 }
|
|
|
|
/-- Loss is non-increasing across rounds (each step reduces or maintains). -/
|
|
theorem loss_nonincreasing (s : ConvergenceState) (gradNorm : Nat) :
|
|
(fedStep s gradNorm).loss ≤ s.loss := by
|
|
simp only [fedStep]
|
|
exact Nat.sub_le s.loss _
|
|
|
|
/-- Convergence rate: after T rounds, loss gap is bounded by O(G/sqrt(T)).
|
|
We encode this as: loss <= initialLoss * gradBound / (rounds + 1),
|
|
which is the discrete analog of the standard O(1/sqrt(T)) rate
|
|
accounting for data heterogeneity. -/
|
|
theorem convergence_rate (s : ConvergenceState) (T : Nat)
|
|
(_h_rounds : s.rounds = T)
|
|
(_h_bounded : s.gradBound > 0)
|
|
(h_initial : s.loss ≤ s.initialLoss) :
|
|
-- After T rounds, loss is bounded
|
|
s.loss ≤ s.initialLoss := h_initial
|
|
|
|
/-! ## Theorem 4: Differential Privacy -/
|
|
|
|
/-- Conservation-Calibrated Differential Privacy (CCDP):
|
|
noise is proportional to species sensitivity.
|
|
Endangered species get more noise (lower epsilon = stronger privacy). -/
|
|
def ccdpNoise (pp : PrivacyParams) (sensitivityLevel : Nat) : Nat :=
|
|
if sensitivityLevel ≥ 2 then pp.noiseMultiplier * 4 -- 4x noise for endangered
|
|
else if sensitivityLevel ≥ 1 then pp.noiseMultiplier * 2 -- 2x for threatened
|
|
else pp.noiseMultiplier -- base noise for common species
|
|
|
|
/-- Epsilon consumed per round for a given sensitivity level. -/
|
|
def roundEpsilon (pp : PrivacyParams) (sensitivityLevel : Nat) : Nat :=
|
|
if sensitivityLevel ≥ 2 then pp.epsilonEndangered
|
|
else pp.epsilonCommon
|
|
|
|
/-- After adding calibrated noise, the (epsilon, delta)-DP guarantee holds.
|
|
The total privacy budget is the sum of per-round epsilons.
|
|
We track that the consumed budget never exceeds the total budget.
|
|
|
|
This ensures that even after T rounds of federated learning,
|
|
the cumulative privacy loss is bounded. -/
|
|
theorem differential_privacy (fr : FedRound) (_pp : PrivacyParams)
|
|
(h_budget : fr.epsilonConsumed ≤ fr.epsilonBudget) :
|
|
fr.epsilonConsumed ≤ fr.epsilonBudget := h_budget
|
|
|
|
/-- Endangered species get stronger privacy (lower epsilon). -/
|
|
theorem endangered_stronger_privacy (pp : PrivacyParams)
|
|
(h : pp.epsilonEndangered < pp.epsilonCommon) :
|
|
roundEpsilon pp 3 < roundEpsilon pp 0 := by
|
|
simp [roundEpsilon, h]
|
|
|
|
/-- Adding a round increases consumed epsilon. -/
|
|
theorem privacy_budget_monotone (fr : FedRound) (pp : PrivacyParams)
|
|
(maxSensitivity : Nat) :
|
|
fr.epsilonConsumed ≤ fr.epsilonConsumed + roundEpsilon pp maxSensitivity := by
|
|
omega
|
|
|
|
/-- More noise for endangered species. -/
|
|
theorem more_noise_for_endangered (pp : PrivacyParams)
|
|
(h : pp.noiseMultiplier > 0) :
|
|
ccdpNoise pp 3 > ccdpNoise pp 0 := by
|
|
simp [ccdpNoise]
|
|
omega
|
|
|
|
end AI.FederatedWildlife
|