Obliczanie prywatnych statystyk za pomocą PipelineDP

1. Zanim zaczniesz

Może Ci się wydawać, że statystyki zbiorcze nie ujawniają żadnych informacji o osobach, których dotyczą. Istnieje jednak wiele sposobów, w jakie osoba atakująca może uzyskać informacje poufne o osobach na podstawie statystyk zbiorczych.

Z tego laboratorium nauczysz się, jak generować prywatne statystyki za pomocą agregacji z prywatnością różnicową z PipelineDP, aby chronić prywatność osób. PipelineDP to platforma Pythona, która umożliwia stosowanie prywatności różnicowej do dużych zbiorów danych za pomocą systemów przetwarzania wsadowego, takich jak Apache Spark i Apache Beam. Więcej informacji o obliczaniu statystyk prywatności różnicowej w Go znajdziesz w samouczku Privacy on Beam.

Prywatne oznacza, że dane wyjściowe są generowane w taki sposób, aby nie ujawniać żadnych informacji prywatnych o osobach, których dotyczą dane. Możesz to osiągnąć dzięki prywatności różnicowej, czyli silnej koncepcji anonimizacji, która polega na agregowaniu danych od wielu użytkowników w celu ochrony ich prywatności. Wszystkie metody anonimizacji wykorzystują agregację, ale nie wszystkie metody agregacji zapewniają anonimizację. Prywatność różnicowa zapewnia natomiast mierzalne gwarancje dotyczące wycieku informacji i prywatności.

Wymagania wstępne

  • Znajomość Pythona
  • Znajomość podstawowej agregacji danych
  • Znajomość bibliotek pandas, Spark i Beam

Czego się nauczysz

  • Podstawowe informacje o prywatności różnicowej
  • Obliczanie statystyk podsumowujących z zastosowaniem prywatności różnicowej za pomocą PipelineDP
  • Jak dostosować wyniki za pomocą dodatkowych parametrów prywatności i użyteczności

Czego potrzebujesz

  • Jeśli chcesz uruchomić codelab w swoim środowisku, musisz mieć na komputerze zainstalowanego Pythona 3.7 lub nowszego.
  • Jeśli chcesz przejść przez codelab bez własnego środowiska, musisz mieć dostęp do Colaboratory.

2. Prywatność różnicowa

Aby lepiej zrozumieć prywatność różnicową, przyjrzyj się temu prostemu przykładowi.

Wyobraź sobie, że pracujesz w dziale marketingu internetowego sklepu odzieżowego i chcesz dowiedzieć się, które z Twoich produktów mają największe szanse na sprzedaż.

Ten wykres pokazuje, które produkty klienci zobaczyli jako pierwsze po wejściu na stronę sklepu: t-shirty, swetry, skarpety czy dżinsy. T-shirty są najpopularniejszym produktem, a skarpetki – najmniej popularnym.

ea813c698889a4c6.png

Wygląda to na przydatne rozwiązanie, ale jest pewien haczyk. Jeśli chcesz uwzględnić dodatkowe informacje, np. czy klienci dokonali zakupu lub który produkt wyświetlili jako drugi, ryzykujesz ujawnienie danych poszczególnych osób.

Z tego wykresu wynika, że tylko 1 klient najpierw obejrzał sweter, a potem dokonał zakupu:

b7c6f7f891778366.png

Z punktu widzenia prywatności nie jest to najlepsze rozwiązanie. Anonimizowane statystyki nie powinny ujawniać indywidualnych wkładów, więc co należy zrobić? Dodajesz do wykresów słupkowych losowy szum, aby były nieco mniej dokładne.

Ten wykres słupkowy nie jest całkowicie dokładny, ale jest przydatny i nie ujawnia wkładu poszczególnych osób:

b55e8d7f99f6d574.gif

Prywatność różnicowa polega na dodaniu odpowiedniej ilości losowego szumu, aby zamaskować indywidualny wkład.

Ten przykład jest zbyt uproszczony. Prawidłowe wdrożenie prywatności różnicowej jest bardziej skomplikowane i wiąże się z wieloma nieoczekiwanymi niuansami. Podobnie jak w przypadku kryptografii, tworzenie własnej implementacji prywatności różnicowej może nie być dobrym pomysłem. Zamiast tego możesz użyć PipelineDP.

3. Pobieranie i instalowanie PipelineDP

Aby wykonać to ćwiczenie, nie musisz instalować PipelineDP, ponieważ cały odpowiedni kod i wykresy znajdziesz w tym dokumencie.

Aby wypróbować PipelineDP, uruchomić go samodzielnie lub użyć go później:

  • Pobierz i zainstaluj PipelineDP:
pip install pipeline-dp

Jeśli chcesz uruchomić przykład za pomocą Apache Beam:

  • Pobierz i zainstaluj Apache Beam:
pip install apache_beam

Kod tego ćwiczenia i zbiór danych znajdziesz w katalogu PipelineDP/examples/codelab/.

4. Obliczanie danych o konwersjach na podstawie pierwszego wyświetlonego produktu

Wyobraź sobie, że pracujesz w internetowym sklepie odzieżowym i chcesz się dowiedzieć, które z różnych kategorii produktów generują najwięcej konwersji o najwyższej wartości, gdy są wyświetlane jako pierwsze. Chcesz udostępnić te informacje agencji marketingowej i innym zespołom wewnętrznym, ale nie chcesz, aby wyciekły informacje o poszczególnych klientach.

Aby obliczyć statystyki konwersji według pierwszego wyświetlonego produktu w witrynie:

  1. Sprawdź przykładowy zbiór danych o wizytach w witrynie w katalogu PipelineDP/examples/codelab/.

Ten zrzut ekranu przedstawia przykładowy zbiór danych. Zawiera identyfikator użytkownika, produkty, które użytkownik wyświetlił, informację o tym, czy odwiedzający dokonał konwersji, a jeśli tak, to wartość tej konwersji.

user_id

product_view_0

product_view_1

product_view_2

product_view_3

product_view_4

has_conversion

conversion_value

0

dżinsy,

t_shirt

t_shirt

brak

brak

fałsz

0,0

1

dżinsy,

t_shirt

dżinsy,

zworka,

brak

fałsz

0,0

2

t_shirt

zworka,

t_shirt

t_shirt

brak

prawda

105,19

3

t_shirt

t_shirt

dżinsy,

brak

brak

fałsz

0,0

4

t_shirt

skarpety

dżinsy,

dżinsy,

brak

fałsz

0,0

Interesują Cię te dane:

  • view_counts: liczba pierwszych wyświetleń każdego produktu przez użytkowników Twojej witryny.
  • total_conversion_value: łączna kwota pieniędzy, jaką użytkownicy wydają po dokonaniu konwersji.
  • conversion_rate: odsetek użytkowników, którzy dokonali konwersji.
  1. Generuj dane w sposób nieprywatny:
conversion_metrics = df.groupby(['product_view_0'
                               ])[['conversion_value', 'has_conversion']].agg({
                                   'conversion_value': [len, np.sum],
                                   'has_conversion': np.mean
                               })
conversion_metrics = conversion_metrics.rename(
   columns={
       'len': 'view_counts',
       'sum': 'total_conversion_value',
       'mean': 'conversion_rate'
   }).droplevel(
       0, axis=1)

Jak już wiesz, te statystyki mogą ujawniać informacje o osobach w zbiorze danych. Na przykład tylko 1 osoba dokonała konwersji po tym, jak najpierw zobaczyła sweter. Przy 22 wyświetleniach współczynnik konwersji wynosi około 0, 05. Teraz musisz przekształcić każdy wykres słupkowy w prywatny.

  1. Zdefiniuj parametry prywatności za pomocą klasy pipeline_dp.NaiveBudgetAccountant, a następnie określ argumenty epsilondelta, których chcesz użyć w analizie.

Sposób ustawienia tych argumentów zależy od konkretnego problemu. Więcej informacji znajdziesz w sekcji Opcjonalnie: dostosuj parametry prywatności różnicowej.

Ten fragment kodu używa przykładowych wartości:

budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=1e-5)
  1. Zainicjuj instancję LocalBackend:
ops = pipeline_dp.LocalBackend()

Możesz użyć instancji LocalBackend, ponieważ program jest uruchamiany lokalnie bez dodatkowych platform, takich jak Beam czy Spark.

  1. Zainicjuj instancję DPEngine:
dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)

Biblioteka PipelineDP umożliwia określanie dodatkowych parametrów za pomocą klasy pipeline_dp.AggregateParams, co wpływa na generowanie prywatnych statystyk.

params = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT],
     max_partitions_contributed=1,
     max_contributions_per_partition=1)
  1. Określ, że chcesz obliczyć dane count i użyć rozkładu szumu LAPLACE.
  2. Ustaw argument max_partitions_contributed na wartość 1.

Ten argument ogranicza liczbę różnych wizyt, które może wygenerować użytkownik. Oczekujesz, że użytkownicy będą odwiedzać witrynę raz dziennie, i nie zależy Ci na tym, czy odwiedzą ją kilka razy w ciągu dnia.

  1. Ustaw argument max_contributions_per_partitions na wartość 1.

Ten argument określa, ile razy pojedynczy użytkownik może przyczynić się do powstania pojedynczego podziału lub w tym przypadku kategorii produktu.

  1. Utwórz instancję data_extractor, która określa, gdzie znajdują się pola privacy_id, partitionvalue.

Kod powinien wyglądać jak ten fragment kodu:

def run_pipeline(data, ops):
 budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=1e-5)

 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)

 params = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT],
     max_partitions_contributed=1, # A single user can only contribute to one partition.
     max_contributions_per_partition=1, # For a single partition, only one contribution per user is used.
     )

 data_extractors = pipeline_dp.DataExtractors(
     privacy_id_extractor=lambda row: row.user_id,
     partition_extractor=lambda row: row.product_view_0
     value_extractor=lambda row: row.has_conversion)

 dp_result = dp_engine.aggregate(data, params, data_extractors)

 budget_accountant.compute_budgets()

 return dp_result
  1. Dodaj ten kod, aby przekształcić strukturę DataFrame biblioteki pandas w listę wierszy, z której możesz bezpośrednio obliczać statystyki prywatności różnicowej:
rows = [index_row[1] for index_row in df.iterrows()]
dp_result_local = run_pipeline(rows, ops) # Returns generator
list(dp_result_local)

Gratulacje! Obliczyłeś(-aś) pierwszą statystykę z ochroną prywatności różnicowej.

Ten wykres pokazuje wynik z różnicową ochroną prywatności obok wyniku bez ochrony prywatności, który został obliczony wcześniej:

a5a25a00858219ab.png

Wykres słupkowy, który uzyskasz po uruchomieniu kodu, może się różnić od tego, co jest w porządku. Ze względu na szum w prywatności różnicowej za każdym razem, gdy uruchamiasz kod, otrzymujesz inny wykres słupkowy, ale możesz zauważyć, że są one podobne do oryginalnego wykresu słupkowego bez ochrony prywatności.

Pamiętaj, że ze względu na gwarancje prywatności nie należy uruchamiać potoku wiele razy. Więcej informacji znajdziesz w artykule Obliczanie wielu statystyk.

5. Korzystanie z partycji publicznych

W poprzedniej sekcji mogłeś(-aś) zauważyć, że usunęliśmy wszystkie dane o wizytach w przypadku partycji, czyli użytkowników, którzy po raz pierwszy zobaczyli skarpety w Twojej witrynie.

Wynika to z wyboru partycji lub progowania, co jest ważnym krokiem w zapewnieniu gwarancji prywatności różnicowej, gdy istnienie partycji wyjściowych zależy od samych danych użytkownika. W takim przypadku samo istnienie partycji w danych wyjściowych może ujawnić, że w danych znajduje się informacja o konkretnym użytkowniku. Więcej informacji o tym, dlaczego narusza to prywatność, znajdziesz w tym poście na blogu. Aby zapobiec naruszeniu prywatności, PipelineDP zachowuje tylko partycje z odpowiednią liczbą użytkowników.

Jeśli lista partycji wyjściowych nie zależy od prywatnych danych użytkowników, nie musisz wykonywać tego kroku wyboru partycji. W Twoim przykładzie tak jest, ponieważ znasz wszystkie możliwe kategorie produktów, które klient może zobaczyć jako pierwsze.

Aby używać partycji:

  1. Utwórz listę możliwych partycji:
public_partitions_products = ['jeans', 'jumper', 'socks', 't-shirt']
  1. Przekaż listę do funkcji run_pipeline(), która ustawi ją jako dodatkowe dane wejściowe dla klasy pipeline_dp.AggregateParams:
run_pipeline(
    rows, ops, total_delta=0, public_partitions=public_partitions_products)
  # Returns generator
params = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT],
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     public_partitions=public_partitions_products)

Jeśli używasz partycji publicznych i LAPLACE szumu, możesz ustawić argument total_delta na wartość 0.

W wyniku widać teraz, że raportowane są dane wszystkich podziałów lub produktów.

a4f6302c8efcd5da.png

Partycje publiczne nie tylko pozwalają zachować więcej partycji, ale też generują o połowę mniej szumu, ponieważ nie wykorzystujesz budżetu ochrony prywatności na wybór partycji. Różnica między surowymi a prywatnymi liczbami jest więc nieco mniejsza niż w przypadku poprzedniego uruchomienia.

Korzystając z partycji publicznych, pamiętaj o 2 ważnych kwestiach:

  • Zachowaj ostrożność podczas tworzenia listy partycji na podstawie nieprzetworzonych danych. Jeśli nie zrobisz tego w sposób zapewniający prywatność różnicową, Twój potok nie będzie już gwarantować prywatności różnicowej. Więcej informacji znajdziesz w artykule Zaawansowane: tworzenie partycji na podstawie danych.
  • Jeśli w przypadku niektórych partycji publicznych nie ma danych, musisz zastosować do nich szum, aby zachować prywatność różnicową. Jeśli na przykład użyjesz dodatkowego produktu, takiego jak spodnie, który nie występuje w Twoim zbiorze danych ani na stronie, nadal będzie to szum, a wyniki mogą wykazywać wizyty w produktach, których nie było.

Zaawansowane: tworzenie partycji na podstawie danych

Jeśli w tym samym potoku uruchamiasz wiele agregacji z tą samą listą niepublicznych partycji wyjściowych, możesz raz uzyskać listę partycji za pomocą metody dp_engine.select_private_partitions() i przekazać ją do każdej agregacji jako dane wejściowe public_partitions. Jest to nie tylko bezpieczne z punktu widzenia ochrony prywatności, ale też pozwala dodać mniej szumu, ponieważ budżet prywatności jest wykorzystywany do wyboru partycji tylko raz w przypadku całego potoku.

def get_private_product_views(data, ops):
 """Obtains the list of product_views in a private manner.

   This does not calculate any private metrics; it merely obtains the list of
   product_views but does so while making sure the result is differentially private.
   """

 # Set the total privacy budget.
 budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=1e-5)

 # Create a DPEngine instance.
 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)

 # Specify how to extract privacy_id, partition_key, and value from a   
 # single element.
 data_extractors = pipeline_dp.DataExtractors(
     partition_extractor=lambda row: row.product_view_0,
     privacy_id_extractor=lambda row: row.user_id)

 # Run aggregation.
 dp_result = dp_engine.select_partitions(
     data, pipeline_dp.SelectPrivatePartitionsParams(
       max_partitions_contributed=1),
     data_extractors=data_extractors)

 budget_accountant.compute_budgets()
 return dp_result

6. Obliczanie wielu statystyk

Teraz, gdy wiesz już, jak działa PipelineDP, możesz zobaczyć, jak używać go w bardziej zaawansowanych przypadkach. Jak wspomnieliśmy na początku, interesują Cię 3 rodzaje statystyk. PipelineDP umożliwia obliczanie wielu statystyk jednocześnie, o ile mają one te same parametry w instancji AggregateParams, co zobaczysz później. Jest to nie tylko bardziej przejrzyste i łatwiejsze w obliczeniach wielu rodzajów danych naraz, ale też lepsze pod względem prywatności.

Jeśli pamiętasz parametry epsilondelta, które przekazujesz do klasy NaiveBudgetAccountant, reprezentują one coś, co nazywa się budżetem prywatności. Jest to miara ilości prywatności użytkownika, która wycieka z danych.

Ważne jest, aby pamiętać, że budżet prywatności jest sumą. Jeśli uruchomisz potok z określonymi wartościami epsilon ε i delta δ, wykorzystasz budżet (ε,δ). Jeśli uruchomisz go po raz drugi, wydasz budżet całkowity (2ε, 2δ). Podobnie, jeśli obliczysz wiele statystyk za pomocą metody NaiveBudgetAccountant i kolejno wykorzystasz budżet ochrony prywatności ε,δ, wydasz łącznie budżet (2ε, 2δ). Oznacza to obniżenie gwarancji prywatności.

Aby to obejść, musisz użyć jednej instancji NaiveBudgetAccountant z łącznym budżetem, którego chcesz użyć, gdy potrzebujesz obliczyć wiele statystyk na podstawie tych samych danych. Następnie musisz określić wartości epsilondelta, których chcesz użyć w przypadku każdego rodzaju agregacji. Ostatecznie uzyskasz tę samą ogólną gwarancję prywatności, ale im wyższe wartości epsilondelta ma dana agregacja, tym większa jest jej dokładność.

Aby zobaczyć, jak to działa, możesz obliczyć statystyki count, meansum.

Statystyki obliczasz na podstawie 2 rodzajów danych: conversion_value, które służą do wnioskowania o wysokości wygenerowanych przychodów na podstawie tego, który produkt został wyświetlony jako pierwszy, oraz has_conversion, które służą do obliczania liczby użytkowników odwiedzających Twoją witrynę i średniego współczynnika konwersji.

W przypadku każdego rodzaju danych musisz osobno określić parametry, które będą kierować obliczaniem statystyk prywatnych. Dzielisz budżet prywatności na 2 rodzaje danych. Obliczasz 2 rodzaje statystyk na podstawie danych has_conversion, więc chcesz przypisać do nich 2/3 początkowego budżetu, a pozostałą 1/3 do danych conversion_value.

Aby obliczyć wiele statystyk:

  1. Skonfiguruj księgowego budżetu prywatności, podając łączne wartości epsilondelta, których chcesz używać w przypadku 3 rodzajów statystyk:
budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=0)
  1. Zainicjuj DPEngine, aby obliczyć dane:
 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)
  1. Określ parametry tego rodzaju danych.
params_conversion_value_metrics = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.SUM],
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     min_value=0,
     max_value=100,
     public_partitions=public_partitions,
     budget_weight=1/3)

Ostatni argument opcjonalnie określa wagę budżetu prywatności. Możesz przypisać wszystkim argumentom tę samą wagę, ale w tym przypadku chcesz ustawić ten argument na jedną trzecią, jak wyjaśniliśmy wcześniej.

Możesz też ustawić argumenty min_valuemax_value, aby określić dolną i górną granicę stosowaną do wartości wnoszonej przez jednostkę ochrony prywatności w partycji. Te parametry są wymagane, jeśli chcesz obliczyć prywatną sumę lub średnią. Nie spodziewasz się wartości ujemnych, więc możesz przyjąć 0100 jako rozsądne granice.

  1. Wyodrębnij odpowiednie dane, a następnie przekaż je do funkcji agregacji:
data_extractors_conversion_value_metrics = pipeline_dp.DataExtractors(
     privacy_id_extractor=lambda row: row.user_id,
     partition_extractor=lambda row: row.product_view_0,
     value_extractor=lambda row: row.conversion_value)

 dp_result_conversion_value_metrics = (
     dp_engine.aggregate(data, params_conversion_value_metrics,
         data_extractors_conversion_value_metrics))
  1. Wykonaj te same czynności, aby obliczyć 2 rodzaje danych na podstawie zmiennej has_conversion:
params_conversion_rate_metrics = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT, pipeline_dp.Metrics.MEAN],
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     min_value=0,
     max_value=1,
     public_partitions=public_partitions,
     budget_weight=2/3)

 data_extractors_conversion_rate_metrics = pipeline_dp.DataExtractors(
     privacy_id_extractor=lambda row: row.user_id,
     partition_extractor=lambda row: row.product_view_0,
     value_extractor=lambda row: row.has_conversion)

 dp_result_conversion_rate_metrics = (
     dp_engine.aggregate(data, params_conversion_rate_metrics,
         data_extractors_conversion_rate_metrics))

Jedyna zmiana dotyczy instancji pipeline_dp.AggregateParams, w której teraz definiujesz meancount jako agregacje i przypisujesz do tego obliczenia 2/3 budżetu prywatności. Ponieważ chcesz mieć te same granice udziału w przypadku obu statystyk i obliczać je na podstawie tej samej zmiennej has_conversion, możesz połączyć je w tym samym wystąpieniu pipeline_dp.AggregateParams i obliczać je w tym samym czasie.

  1. Wywołaj metodę budget_accountant.compute_budgets():
budget_accountant.compute_budgets()

Możesz wykreślić wszystkie 3 rodzaje statystyk prywatnych w porównaniu z ich pierwotnymi statystykami. W zależności od dodanego szumu wyniki mogą wykraczać poza prawdopodobną skalę. W tym przypadku w przypadku skoczków zobaczysz ujemny współczynnik konwersji i ujemną łączną wartość konwersji, ponieważ dodany szum jest symetryczny względem zera. W przypadku dalszych analiz i przetwarzania najlepiej nie przetwarzać ręcznie statystyk prywatnych, ale jeśli chcesz dodać te wykresy do raportu, możesz później po prostu ustawić wartość minimalną na zero bez naruszania gwarancji prywatności.

cb1fc563f817eaf.png

7. Uruchamianie potoku za pomocą Beam

Przetwarzanie danych wymaga obecnie obsługi ogromnych ilości informacji, których nie można przetwarzać lokalnie. Zamiast tego wiele osób korzysta z platform do przetwarzania danych na dużą skalę, takich jak Beam czy Spark, i uruchamia potoki w chmurze.

PipelineDP obsługuje Beam i Spark, a do wprowadzenia zmian w kodzie wystarczy niewielka modyfikacja.

Aby uruchomić potok za pomocą Beam z interfejsem private_beam API:

  1. Zainicjuj zmienną runner, a potem utwórz potok, w którym zastosujesz operacje ochrony prywatności do reprezentacji Beam obiektu rows:
runner = fn_api_runner.FnApiRunner()  # local runner

with beam.Pipeline(runner=runner) as pipeline:
   beam_data = pipeline | beam.Create(rows)
  1. Utwórz zmienną budget_accountant z wymaganymi parametrami prywatności:
budget_accountant = pipeline_dp.NaiveBudgetAccountant(
               total_epsilon=1, total_delta=0)
  1. Utwórz zmienną pcol lub zmienną kolekcji prywatnej, która gwarantuje, że wszystkie agregacje są zgodne z Twoimi wymaganiami dotyczącymi ochrony prywatności:
pcol = beam_data | pbeam.MakePrivate(
                                 budget_accountant=budget_accountant,
                                 privacy_id_extractor=lambda 
                                                    row: row.user_id)
  1. Określ parametry agregacji prywatnej w odpowiedniej klasie.

W tym przypadku używasz klasy pipeline_dp.aggregate_params.SumParams(), ponieważ obliczasz sumę wyświetleń produktu.

  1. Przekaż parametry agregacji do metody pbeam.Sum, aby obliczyć statystykę:
dp_result = pcol | pbeam.Sum(params)
  1. Ostatecznie kod powinien wyglądać jak ten fragment kodu:
import pipeline_dp.private_beam as pbeam
runner = fn_api_runner.FnApiRunner()  # local runner

with beam.Pipeline(runner=runner) as pipeline:
   beam_data = pipeline | beam.Create(rows)
   budget_accountant = pipeline_dp.NaiveBudgetAccountant(
               total_epsilon=1, total_delta=0)

   # Create private collection.
   pcol = beam_data | pbeam.MakePrivate(
                              budget_accountant=budget_accountant,
                              privacy_id_extractor=lambda row:  
                                                         row.user_id)
   # Specify parameters.
   params = pipeline_dp.aggregate_params.SumParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     min_value=0,
     max_value=100,
     public_partitions=public_partitions_product_views,
     partition_extractor=lambda row: row.product_view_0,
     value_extractor=lambda row:row.conversion_value)
   dp_result = pcol | pbeam.Sum(params)

   budget_accountant.compute_budgets()
   dp_result | beam.Map(print)

8. Opcjonalnie: dostosuj parametry prywatności i użyteczności

W tym samouczku wspomnieliśmy o kilku parametrach, takich jak epsilon, deltamax_partitions_contributed. Można je z grubsza podzielić na 2 kategorie: parametry prywatności i parametry użyteczności.

Parametry prywatności

Parametry epsilondelta określają liczbowo poziom prywatności zapewniany przez prywatność różnicową. Mówiąc dokładniej, jest to miara tego, ile informacji o danych może uzyskać potencjalny atakujący na podstawie zanonimizowanych danych wyjściowych. Im wyższa wartość parametrów, tym więcej informacji o danych uzyskuje osoba atakująca, co stanowi zagrożenie dla prywatności. Z drugiej strony im niższe są wartości parametrów epsilondelta, tym więcej szumu musisz dodać do danych wyjściowych, aby zachować anonimowość, i tym większa musi być liczba unikalnych użytkowników w każdym podziale, aby zachować ich w anonimizowanych danych wyjściowych. W tym przypadku występuje kompromis między użytecznością a prywatnością.

W PipelineDP musisz określić pożądane gwarancje prywatności anonimizowanych danych wyjściowych, gdy ustawiasz całkowity budżet na potrzeby prywatności w instancji NaiveBudgetAccountant. Warunkiem jest jednak to, że jeśli chcesz zachować gwarancje prywatności, musisz ostrożnie używać osobnej NaiveBudgetAccountant instancji dla każdej agregacji lub uruchamiać potok wiele razy, aby uniknąć nadmiernego wykorzystania budżetu.

Więcej informacji o prywatności różnicowej i znaczeniu parametrów prywatności znajdziesz w tym artykule.

Parametry narzędzia

Parametry użyteczności nie wpływają na gwarancje prywatności, ale mają wpływ na dokładność, a co za tym idzie, na użyteczność danych wyjściowych. Są one podawane w instancji AggregateParams i służą do skalowania dodawanego szumu.

Parametrem narzędziowym podanym w instancji AggregateParams i mającym zastosowanie do wszystkich agregacji jest parametr max_partitions_contributed. Partycja odpowiada kluczowi danych zwracanych przez operację agregacji PipelineDP, więc parametr max_partitions_contributed ogranicza liczbę różnych wartości kluczy, które użytkownik może przekazać do danych wyjściowych. Jeśli użytkownik przyczyni się do powstania liczby kluczy przekraczającej wartość parametru max_partitions_contributed, niektóre jego działania zostaną pominięte, tak aby ich liczba odpowiadała wartości parametru max_partitions_contributed.

Podobnie większość agregacji ma parametr max_contributions_per_partition. Są one też podawane w instancji AggregateParams, a każda agregacja może mieć dla nich oddzielne wartości. Ograniczają one wkład użytkownika w przypadku każdego klucza.

Szum dodawany do danych wyjściowych jest skalowany przez parametry max_partitions_contributedmax_contributions_per_partition, więc istnieje tu kompromis: większe wartości przypisane do każdego parametru oznaczają, że zachowujesz więcej danych, ale uzyskujesz bardziej zaszumiony wynik.

Niektóre agregacje wymagają parametrów min_valuemax_value, które określają zakresy danych pochodzących od każdego użytkownika. Jeśli użytkownik poda wartość niższą niż wartość przypisana do parametru min_value, zostanie ona zwiększona do wartości parametru. Podobnie jeśli użytkownik poda wartość większą niż wartość parametru max_value, zostanie ona zmniejszona do wartości parametru. Aby zachować więcej oryginalnych wartości, musisz określić większe granice. Szum jest skalowany według rozmiaru granic, więc większe granice pozwalają zachować więcej danych, ale w rezultacie otrzymujesz bardziej zaszumiony wynik.

Parametr noise_kind obsługuje w PipelineDP 2 różne mechanizmy szumu: GAUSSIANLAPLACE. Rozkład LAPLACE zapewnia większą użyteczność przy niskich granicach wkładu, dlatego PipelineDP używa go domyślnie. Jeśli jednak chcesz użyć GAUSSIAN szumu dystrybucji, możesz go określić w AggregateParams instancji.

9. Gratulacje

Brawo! Ukończono laboratorium PipelineDP i zdobyto wiele informacji o prywatności różnicowej i PipelineDP.

Więcej informacji