Werkplaats / De werkplaats
In jouw eigen tempo
← AI in de wereld
MODULE 02 · LES 05

Kalibreer ideale privacyverdelingen en tel budgetten

Koppel vaste replace-one-buren aan sensitiviteit en ideale Laplace-dichtheden en onderscheid exacte budgetbeslissingen van afgeronde displays.

90 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je berekent sensitiviteit en Laplace-schaal bij een vooraf publieke vaste grootte en publieke begrenzing.
  • Je vergelijkt log-dichtheden en houdt aangenomen epsilonbudgetten exact bij zonder een softwareprivacygarantie te claimen.

Je wilt een gemiddelde begrijpen zonder dat de uitkomst sterk afhangt van één bijdrage. Alleen namen weghalen verandert die invloed niet. Differential privacy begint daarom bij een precieze vraag: hoe verschillend mogen de uitkomstverdelingen zijn wanneer één bijdrage verandert?

Denk aan twee bijna gelijke bakken met meetwaarden. Je schuift dezelfde brede verdelingsvorm rond hun gemiddelden; als de middelpunten dicht genoeg bij elkaar liggen, overlappen de verdelingen sterk. De breedte moet bij de maximaal mogelijke verschuiving passen.

Je berekent parameters, log-dichtheden en een budgettabel voor het ideale Laplace-mechanisme. De code bevat geen sampling- of gegevensreleasefunctie. De wiskundige afleiding en een veilige implementatie op echte gegevens zijn afzonderlijke eisen.

1. Leg eerst vast wat één verandering betekent

Onze twee bakken hebben dezelfde vooraf publieke grootte n. Precies één bijdrage wordt vervangen door een andere waarde binnen het vooraf gekozen bereik van a tot b. Dit burenmodel voegt geen bijdrage toe en verwijdert er geen.

Een bijdrage is hier de privacy-eenheid. Meerdere bijdragen van dezelfde persoon vallen zonder extra begrenzing niet automatisch onder dezelfde bescherming. Ook de grootte n mag in dit model publiek zijn; bij andere burenmodellen kan juist die grootte gevoelig zijn.

De gevoeligheid Δ is de grootste mogelijke verandering van het gemiddelde. Eén tellerterm kan maximaal b min a veranderen; het gemiddelde deelt die verandering door n. Het ideale Laplace-mechanisme gebruikt schaal s bij privacyparameter ε.

Δ=b−ans=Δε

Neem vier bijdragen tussen nul en één. Het gemiddelde van vier nullen is nul; vervang één nul door één en het gemiddelde wordt een kwart. Dus Δ is een kwart en bij ε gelijk aan één is s eveneens een kwart.

cal = laplace_calibration(4)
print(cal['sensitivity'])
print(cal['scale'])
# 0.25
# 0.25

Kleinere ε vraagt bij dezelfde gevoeligheid een grotere schaal. De ideale verdeling wordt breder en de uitkomst doorgaans minder nauwkeurig. Kies a en b vooraf: uit private data afgeleide grenzen kunnen zelf informatie prijsgeven.

01
CHECKPOINTKalibreer het ideale Laplace-mechanisme

2. Vergelijk verdelingen, geen willekeurige losse ruiswaarde

In het beeld verschuift het middelpunt en verandert de schaal. De uitkomst z kan overal op de reële lijn liggen; het zichtbare tekenbereik is slechts een uitsnede. Een hoge curve betekent meer dichtheid in de buurt van dat punt.

VERKEN HET ZELFDe breedte moet passen bij één gewijzigde bijdrage

Vergelijk n nullen met dezelfde n bijdragen waarvan één nul een één wordt. De publieke grenzen zijn vooraf 0 en 1. Verander n en ε en volg gevoeligheid, schaal en de twee ideale verdelingen.

Gevoeligheid Δ
0,25
Schaal s = Δ / ε
0,25
Middelpunt A
0
Middelpunt B
0,25
01,12,2-1012KansdichtheidUitkomst z (scorepunten)

━━ A: gemiddelde 0┄┄ B: gemiddelde 0,25

Grootste getoonde absolute log-dichtheidsverschil: 1 bij ε = 1. De analytische grens geldt voor ieder z; deze eindige plotpunten bewijzen die grens niet.

Herhaalde uitgiftes tellen op

De som veronderstelt hetzelfde burenmodel en dezelfde privacy-eenheid, met een passende garantie voor elke volgende uitgifte gegeven eerdere uitkomsten. Willekeurig gedeelde ruis volstaat niet.

Uitgiftes
1
Getoond totaal E
1
Publieke cap
1
  1. Na uitgifte 1: 1

De exacte binaire som past binnen de gekozen cap, inclusief gelijkheid.

Dit beeld berekent alleen ideale verdelingen en een numerieke ledger. Het maakt geen ruisuitgifte en levert geen software-DP-garantie. Het plot is een uitsnede; de ideale Laplace-verdeling loopt over de hele reële lijn.

Noem het middelpunt μ en de natuurlijke log van de kansdichtheid ℓ. De getallen voor a, b, μ, Δ en s gebruiken dezelfde vaste meeteenheid: hier een punt op de scoreschaal. Een dichtheid is geen kans op exact één punt; kansen horen bij oppervlakten over verzamelingen.

ℓ(z)=−ln(2s)−|z−μ|s

Vergelijk de realiseerbare gemiddelden nul en een kwart uit het handvoorbeeld. Bij z gelijk aan nul is de eerste log-dichtheid ongeveer 0,693 en de tweede ongeveer −0,307. Het logverschil is één; de verhouding van de dichtheden is dus e tot de macht één.

import numpy as np
left = laplace_log_density(
    [0,.25],center=0,scale=.25)
right = laplace_log_density(
    [0,.25],center=.25,scale=.25)
ratio = left-right
print(np.round(left,3).tolist())
print(np.round(ratio,3).tolist())
# [0.693, -0.307]
# [1.0, -1.0]

Twee testpunten bewijzen nog geen garantie. De driehoeksongelijkheid begrenst het logverschil voor iedere z door de afstand tussen de middelpunten gedeeld door s. Bij naburige datasets D en D′ is die afstand maximaal Δ, zodat de grens ε wordt.

|ℓD(z)−ℓD′(z)|≤|μ−μ′|s≤ε

Deze puntgewijze grens leidt in het ideale model tot een overeenkomstige vermenigvuldigingsgrens voor kansen op iedere uitkomstverzameling. In software kunnen eindige precisie, randombronnen en andere informatiekanalen die belofte doorbreken. Een vaste seed of een geslaagd histogramtestje vervangt zo’n analyse niet.

02
CHECKPOINTBereken log-dichtheden van twee mogelijke uitkomsten

3. Herhaalde uitgiftes vragen een gezamenlijke begroting

Stel dat m uitgiftes hetzelfde burenmodel en dezelfde privacy-eenheid gebruiken. Iedere volgende uitgifte moet haar ε-garantie ook behouden wanneer eerdere uitkomsten al bekend zijn. Bij vaste mechanismen kan verse onafhankelijke randomisatie dit ondersteunen; gedeelde ruis kan deze voorwaarde juist breken.

Noem de parameter van uitgifte j ε_j en de eenvoudige gezamenlijke bovengrens E. Epsilon, E en cap zijn dimensieloos; n en m tellen respectievelijk bijdragen en uitgiftes. Onder de genoemde voorwaarden is E de som; het boekhouden herstelt geen onjuist afzonderlijk mechanisme.

E=∑j=1mεj

Drie geplande uitgiftes met parameters een kwart, een kwart en een half passen precies onder een publieke cap van één. De code toont de som als epsilon_total en de tussenstanden als epsilon_prefix. Gelijkheid aan de cap is toegestaan.

budget = basic_composition(
    [.25,.25,.5],cap=1.)
print(budget['epsilon_prefix'].tolist())
print(budget['within_cap'])
# [0.25, 0.5, 1.0]
# True

Een computerafdruk kan kleine bijdragen wegafronden. Daarom vergelijkt onze capbeslissing de exacte som van de reeds geconverteerde binaire getallen; het getoonde totaal blijft een float. Dit is een precieze boekhoudkeuze, geen implementatie van een veilig DP-systeem.

tiny = basic_composition(
    [1.,2**-60],cap=1.)
print(tiny['epsilon_total'])
print(tiny['within_cap'])
# 1.0
# False
03
CHECKPOINTVergelijk een exacte binaire budgetsom met een publieke cap

Wat je nu kunt

  • Je koppelt een vastgelegd burenmodel en publieke grenzen aan gevoeligheid en schaal.
  • Je onderscheidt log-dichtheden, een analytische grens en een softwaregarantie.
  • Je telt aangenomen budgetten samen en bewaart kleine bijdragen voor de capbeslissing.

Hierna kijk je naar fairness: welke groepsuitkomsten je vergelijkt, welke noemers daarbij horen en waarom één mooie maat nog niet alle relevante vragen beantwoordt.

DE VOLGENDE STAPVergelijk groepsmaten met de juiste noemers
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 3
01
Kalibreer het ideale Laplace-mechanisme

Schrijf laplace_calibration(count,lower=0.,upper=1.,epsilon=1.). Count is Python-int 1–4096 zonder bool: een vooraf publieke vaste datasetgrootte, geen privaat afgeleid getal. Lower/upper liggen binnen [-1e6,1e6], upper-lower minstens .001, epsilon binnen [.001,100]. Gegeven scalarvalidators weigeren bool, accepteren Python-/NumPy-int/float, converteren veilig naar Python-float en controleren daarna eindigheid en bereik van die geconverteerde waarde. Geef count als integer, lower/upper/epsilon/sensitivity/scale als floats en neighbor_model exact "replace-one-fixed-size". Sensitivity=(upper-lower)/count en scale=sensitivity/epsilon. Lower/upper/sensitivity/scale hebben dezelfde meeteenheid; epsilon is dimensieloos en count telt bijdragen. Het burenmodel vervangt één begrensde bijdrage en houdt n gelijk; het voegt geen persoon toe en verwijdert niemand. Behoud alle invoer en de globale Python-/NumPy-RNG. Ongeldige invoer geeft ValueError. Alleen numerieke parameters voor het ideale reële mechanisme; geen gegevensrelease of software-DP-garantie.

Een hint

Werk eerst het kleine handvoorbeeld uit; volg daarna de expliciete types, grenzen en betekenis van het resultaat.

02
Bereken log-dichtheden van twee mogelijke uitkomsten

Schrijf laplace_log_density(points,center=0.,scale=1.). Points is na np.asarray een reële eindige 1D-vector/lijst, dtype i/u/f, lengte 1–4096, waarden binnen [-1e6,1e6]. Center binnen [-1e6,1e6], scale binnen [1e-12,2e9]. Gegeven scalarvalidators weigeren bool, accepteren Python-/NumPy-int/float, converteren veilig naar Python-float en controleren daarna eindigheid en bereik van die geconverteerde waarde. Geef een zelfstandige eindige floatarray van dezelfde lengte: -ln(2*scale)-abs(points-center)/scale, natuurlijke log. Points/center/scale gebruiken dezelfde vaste meeteenheid. Dit zijn log-kansdichtheden, geen kansen op exact die punten. Het eindige plotbereik zegt niets over afkappen van de ideale verdeling. Behoud alle invoer en de globale Python-/NumPy-RNG. Ongeldige invoer geeft ValueError. Geen sampling, releasefunctie of privacybewijs uit een eindige verzameling plotpunten.

Een hint

Werk eerst het kleine handvoorbeeld uit; volg daarna de expliciete types, grenzen en betekenis van het resultaat.

03
Vergelijk een exacte binaire budgetsom met een publieke cap

Schrijf basic_composition(epsilons,cap=1.). Epsilons is na np.asarray een reële eindige 1D-vector/lijst, dtype i/u/f, lengte 1–64; iedere waarde binnen [0,100]. Werk met een zelfstandige floatconversie volgens _dp_vector. Cap is een eindige reële scalar binnen [0,6400] volgens _dp_number. Epsilon/cap zijn dimensieloos; releases telt uitgiftes. Geef epsilon_prefix (zelfstandige floatarray, elk prefix berekend met math.fsum), epsilon_total (laatste prefix als float), cap (float), releases (integer) en within_cap (boolean). Beslis within_cap met de exacte som van Fraction.from_float(float(value)) voor de geconverteerde waarden, vergeleken met Fraction.from_float(cap), inclusief gelijkheid. Prefix/total zijn afgeronde displaywaarden. Bij [1.,2**-60], cap 1. is de displaytotal 1. maar within_cap False. Behoud alle invoer en de globale Python-/NumPy-RNG. Ongeldige invoer geeft ValueError. De compositiebovengrens veronderstelt hetzelfde burenmodel/dezelfde privacy-eenheid en dat iedere volgende uitgifte haar epsilon-garantie ook gegeven alle eerdere uitkomsten behoudt. Vaste mechanismen kunnen dit bijvoorbeeld met verse onafhankelijke randomisatie bereiken; willekeurig gedeelde ruis is onvoldoende. Deze ledger is geen inzetbaar DP-systeem.

Een hint

Werk eerst het kleine handvoorbeeld uit; volg daarna de expliciete types, grenzen en betekenis van het resultaat.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

import math

from fractions import Fraction

# Jouw functies

def laplace_calibration(count,lower=0.,upper=1.,epsilon=1.):
    if type(count) is not int or not 1<=count<=4096:raise ValueError('Een vaste publieke grootte van 1–4096 vereist')
    lower=_dp_number(lower,-1e6,1e6);upper=_dp_number(upper,-1e6,1e6);epsilon=_dp_number(epsilon,.001,100.)
    width=upper-lower
    if width<.001:raise ValueError('Een vooraf gekozen bereik van minstens 0,001 vereist')
    sensitivity=width/count;scale=sensitivity/epsilon
    return {'count':count,'lower':lower,'upper':upper,'epsilon':epsilon,'sensitivity':sensitivity,'scale':scale,
        'neighbor_model':'replace-one-fixed-size'}

def laplace_log_density(points,center=0.,scale=1.):
    values=_dp_vector(points)
    if np.any(np.abs(values)>1e6):raise ValueError('Plotwaarden binnen het oefenbereik vereist')
    center=_dp_number(center,-1e6,1e6);scale=_dp_number(scale,1e-12,2e9)
    return -math.log(2*scale)-np.abs(values-center)/scale

def basic_composition(epsilons,cap=1.):
    values=_dp_vector(epsilons,64)
    if np.any(values<0) or np.any(values>100):raise ValueError('Hoogstens 64 niet-negatieve epsilonwaarden binnen 0–100 vereist')
    cap=_dp_number(cap,0,6400);prefix=np.asarray([math.fsum(float(value) for value in values[:index+1]) for index in range(len(values))])
    total=float(prefix[-1])
    exact_total=sum((Fraction.from_float(float(value)) for value in values),Fraction(0))
    return {'epsilon_total':total,'epsilon_prefix':prefix,'cap':cap,'releases':len(values),'within_cap':exact_total<=Fraction.from_float(cap)}

# Gegeven helpers

def _dp_number(value,low,high):
    if isinstance(value,(bool,np.bool_)) or not isinstance(value,(int,float,np.integer,np.floating)):raise ValueError('Begrensde eindige scalar vereist')
    try:number=float(value)
    except (OverflowError,ValueError,TypeError):raise ValueError('Begrensde eindige scalar vereist') from None
    if not math.isfinite(number) or not low<=number<=high:raise ValueError('Begrensde eindige scalar vereist')
    return number

def _dp_vector(value,maximum=4096):
    raw=np.asarray(value)
    if raw.ndim!=1 or not 1<=len(raw)<=maximum or raw.dtype.kind not in 'iuf':raise ValueError('Begrensde niet-lege reële vector vereist')
    if not np.isfinite(raw).all():raise ValueError('Eindige vectorwaarden vereist')
    return raw.astype(float,copy=True)

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.