Obliczanie prywatnych statystyk za pomocą PipelineDP

1. Zanim zaczniesz

Może się wydawać, że statystyki zbiorcze nie ujawniają żadnych informacji dotyczących osób, których dotyczą. Istnieje jednak wiele sposobów, dzięki którym haker może zdobyć poufne informacje o poszczególnych osobach ze zbiorczych statystyk.

Z tego ćwiczenia w Codelabs dowiesz się, jak tworzyć statystyki prywatne z różnicowymi agregacjami prywatnymi w PipelineDP, aby chronić prywatności. 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 sposobie obliczania prywatnych statystyk różnicowych w Go znajdziesz w ćwiczeniu z programowania Privacy on Beam.

Prywatne oznacza, że dane wyjściowe są generowane w sposób, który nie powoduje wycieku żadnych prywatnych informacji o poszczególnych osobach. Możesz to osiągnąć dzięki prywatności różnicowej, czyli szeroko pojętej koncepcji ochrony prywatności, czyli anonimizacji, czyli procesu agregacji danych od wielu użytkowników w celu ochrony ich prywatności. Wszystkie metody anonimizacji korzystają z agregacji, ale nie wszystkie metody agregacji zapewniają anonimizację. Prywatność różnicowa zapewnia natomiast wymierne gwarancje w zakresie wycieku informacji i ochrony prywatności.

Wymagania wstępne

  • znajomość Pythona,
  • znajomość podstawowej agregacji danych,
  • Poznaj pandy, Spark i Beam

Czego się nauczysz

  • Podstawy prywatności różnicowej
  • Jak obliczać prywatne statystyki zbiorcze za pomocą PipelineDP
  • jak ulepszać wyniki za pomocą dodatkowych parametrów związanych z prywatnością i przydatnością,

Czego potrzebujesz

  • Jeśli chcesz uruchomić ćwiczenia z programowania we własnym środowisku, na komputerze musi być zainstalowany Python 3.7 lub nowszy.
  • Jeśli chcesz wykonać ćwiczenia z programowania 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 w sklepie internetowym z odzieżą i chcesz sprawdzić, które z Twoich produktów najprawdopodobniej będą sprzedawane.

Ten wykres pokazuje, które produkty klienci oglądali jako pierwsze po wizycie na stronie sklepu: koszulki, swetry, skarpetki czy dżinsy. T-shirty są najpopularniejszym produktem, a skarpetki – najmniejszym.

ea813c698889a4c6.png

To wygląda na przydatne, ale jest jakiś haczyk. Gdy chcesz uwzględnić dodatkowe informacje, np. czy klienci dokonali zakupu lub który produkt oglądali w drugiej kolejności, ryzykujesz ujawnienie w swoich danych osób.

Ten wykres pokazuje, że tylko jeden klient najpierw obejrzał sweter, a później faktycznie dokonał zakupu:

b7c6f7f891778366.png

Nie jest to dobre z perspektywy prywatności. Zanonimizowane statystyki nie powinny ujawniać poszczególnych wkładów. Co należy zrobić? Dodajesz losowy szum do wykresów słupkowych, aby zmniejszyć ich dokładność.

Ten wykres słupkowy nie jest całkowicie dokładny, ale jest przydatny i nie pokazuje informacji o poszczególnych osobach:

b55e8d7f99f6d574.gif

Prywatność różnicowa to dodanie odpowiedniej ilości losowego szumu, aby maskować poszczególne treści.

Ten przykład jest uproszczony. Prawidłowe wdrożenie prywatności różnicowej wymaga bardziej skomplikowanej implementacji, która wiąże się z różnymi niespodziewanymi substancjami. Podobnie jak w przypadku kryptografii, utworzenie własnej implementacji prywatności różnicowej może nie być dobrym pomysłem. Zamiast tego możesz użyć PipelineDP.

3. Pobierz i zainstaluj PipelineDP

Nie musisz instalować PipelineDP zgodnie z tym ćwiczeniem w Codelabs, ponieważ wszystkie odpowiednie kody i wykresy znajdziesz w tym dokumencie.

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

  • Pobierz i zainstaluj PipelineDP:
pip install pipeline-dp

Jeśli chcesz uruchomić przykład przy użyciu Apache Beam:

  • Pobierz i zainstaluj Apache Beam:
pip install apache_beam

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

4. Oblicz dane konwersji dla pierwszego wyświetlonego produktu

Wyobraź sobie, że pracujesz w internetowym sklepie z odzieżą i chcesz się dowiedzieć, które kategorie produktów generują największą liczbę i wartość konwersji w pierwszej kolejności. Chcesz udostępnić te informacje agencji marketingowej oraz innym zespołom wewnętrznym, ale chcesz zapobiec wyciekowi informacji o poszczególnych klientach.

Aby obliczyć dane konwersji na pierwszy produkt wyświetlony w witrynie:

  1. Przejrzyj przykładowy zbiór danych wizyt w Twojej witrynie znajdujący się w katalogu PipelineDP/examples/codelab/.

Zrzut ekranu to przykład zbioru danych. Zawiera on identyfikator użytkownika, produkty wyświetlone przez niego, informacje o tym, czy dokonał on konwersji, a jeśli tak, wartość 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

sweter

brak

fałsz

0,0

2

t_shirt

sweter

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: ile razy użytkownicy witryny zobaczyli każdy produkt jako pierwszy.
  • total_conversion_value: łączna kwota, jaką użytkownicy wydają po dokonaniu konwersji.
  • conversion_rate: współczynnik konwersji użytkowników.
  1. Generuj dane w sposób, który nie jest prywatny:
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 wiesz, te statystyki mogą ujawniać informacje o osobach w zbiorze danych. Na przykład tylko 1 osoba dokonała konwersji po tym, jak ta osoba zobaczyła skoczka jako pierwszą. W przypadku 22 wyświetleń współczynnik konwersji wynosi około 0, 05. Teraz musisz przekształcić każdy wykres słupkowy w wykres prywatny.

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

Sposób ustawiania tych argumentów zależy od konkretnego problemu. Więcej informacji na ten temat znajdziesz w sekcji Opcjonalnie: wprowadzanie zmian w parametrach prywatności różnicowej.

W tym fragmencie kodu są używane przykładowe 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żywać instancji LocalBackend, ponieważ ten program uruchamiasz lokalnie bez dodatkowych platform, takich jak Beam czy Spark.

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

PipelineDP umożliwia określenie dalszych 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ć wskaźnik count i używać rozkładu szumu LAPLACE.
  2. Ustaw argument max_partitions_contributed na wartość 1.

Ten argument ogranicza liczbę różnych wizyt, które użytkownik może przypisać. Oczekujesz, że użytkownicy odwiedzają witrynę raz dziennie i nie obchodzi Cię to, czy odwiedzają ją wiele razy w ciągu dnia.

  1. Ustaw argument max_contributions_per_partitions na wartość 1.

Ten argument określa, ile w tym przypadku pojedynczy użytkownik może mieć wpływ na poszczególne partycje lub kategorie produktów.

  1. Utwórz instancję data_extractor, która określa, gdzie znaleźć pola privacy_id, partition i value.

Twój kod powinien wyglądać tak:

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ć obiekt DataFrame w Pandas w listę wierszy, na podstawie których możesz bezpośrednio wyliczyć różne prywatne statystyki:
rows = [index_row[1] for index_row in df.iterrows()]
dp_result_local = run_pipeline(rows, ops) # Returns generator
list(dp_result_local)

Gratulacje! Udało Ci się obliczyć pierwszą statystykę dotyczącą prywatności różnicowej!

Ten wykres przedstawia wynik liczby zróżnicowanych danych prywatnych w porównaniu z obliczoną wcześniej liczbą nieprywatnych:

a5a25a00858219ab.png

Wykres słupkowy, który widzisz po uruchomieniu kodu, może się różnić od tego. Nie ma problemu. Ze względu na szum w prywatności różnicowej przy każdym uruchomieniu kodu pojawia się inny wykres słupkowy, ale jak widać, jest on podobny do oryginalnego, nieprywatnego wykresu słupkowego.

Pamiętaj, że ze względu na gwarancję prywatności bardzo ważne jest, aby potok nie uruchamiał się wielokrotnie ze względu na gwarancję prywatności. Więcej informacji znajdziesz w artykule „Obliczanie wielu statystyk”.

5. Użyj partycji publicznych

W poprzedniej sekcji można było zauważyć, że utracono wszystkie dane dotyczące wizyt dla partycji, czyli użytkowników, którzy po raz pierwszy zobaczyli skarpetki w Twojej witrynie.

Wynika to z wyboru partycji lub wyznaczenia progów. To ważny krok w celu zagwarantowania prywatności różnicowej, gdy istnienie partycji danych wyjściowych zależy od samych danych użytkownika. W takim przypadku samo istnienie partycji w danych wyjściowych może ujawnić istnienie konkretnego użytkownika. Aby dowiedzieć się, dlaczego narusza to prywatność, przeczytaj ten post na blogu. Aby zapobiec temu naruszeniu prywatności, PipelineDP przechowuje partycje tylko z wystarczającą liczbą użytkowników.

Gdy lista partycji wyjściowych nie zależy od prywatnych danych użytkownika, nie musisz tego robić. Tak właśnie jest z Twoim przykładem, ponieważ znasz wszystkie możliwe kategorie produktów, które klient może zobaczyć jako pierwszy.

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 ustawia ją jako dodatkowe dane wejściowe 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 szumu LAPLACE, możesz ustawić argument total_delta na wartość 0.

W wyniku tego procesu widzisz, że są raportowane dane dotyczące wszystkich partycji lub produktów.

a4f6302c8efcd5da.png

Partycje publiczne nie tylko pozwalają na zachowanie większej liczby partycji, ale generują też mniej więcej o połowę mniej szumu, ponieważ przy wyborze partycji nie wydajesz żadnego budżetu na potrzeby prywatności, więc różnica między liczbami nieprzetworzonymi a prywatnymi jest nieco mniejsza w porównaniu z poprzednim uruchomieniem.

Gdy używasz partycji publicznych, musisz pamiętać o 2 ważnych kwestiach:

  • Zachowaj ostrożność podczas pobierania listy partycji z nieprzetworzonych danych. Jeśli nie zapewnisz ochrony prywatności różnicowej, Twój potok nie będzie już zapewniać prywatności różnicowej. Więcej informacji znajdziesz w artykule Zaawansowane: Uzyskiwanie partycji z danych.
  • Jeśli nie ma danych w przypadku niektórych partycji publicznych, musisz zastosować do nich szum, aby zachować prywatność różnicową. Jeśli np. używasz dodatkowego produktu, takiego jak spodnie, którego nie ma w Twoim zbiorze danych ani na Twojej stronie internetowej, będzie to nadal szum, a wyniki mogą uwzględniać niektóre wizyty w produktach, jeśli takich informacji nie było.

Zaawansowane: wyprowadź partycje z danych

Jeśli w tym samym potoku uruchamiasz wiele agregacji z tą samą listą niepublicznych partycji danych wyjściowych, możesz uzyskać listę partycji raz za pomocą metody dp_engine.select_private_partitions() i zastosować partycje do każdej agregacji jako dane wejściowe public_partitions. Jest to nie tylko bezpieczne z perspektywy prywatności, lecz także pozwala ograniczyć ilość szumu, ponieważ budżet prywatności używany przy wyborze partycji jest używany raz w całym 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. Oblicz wiele statystyk

Skoro już wiesz, jak działa PipelineDP, możesz zobaczyć, jak można wykorzystać tę usługę w bardziej zaawansowanych przypadkach. Jak wspomnieliśmy na początku, interesują Cię trzy statystyki. PipelineDP umożliwia obliczanie wielu statystyk jednocześnie, o ile mają one w instancji AggregateParams te same parametry, które zobaczysz później. Usługa jest nie tylko bardziej przejrzysta i łatwiejsza do obliczania wielu wskaźników za jednym razem, ale także lepsza pod względem prywatności.

Jeśli pamiętasz parametry epsilon i delta, które przekazujesz do klasy NaiveBudgetAccountant, reprezentują one coś w rodzaju budżetu prywatności, który jest miarą poziomu prywatności użytkownika, który wycieknie z danych.

Warto pamiętać o budżecie na potrzeby prywatności, ponieważ jest on równoważny. Jeśli jednorazowo uruchomisz potok z określoną wartością ypsilon zer i delta, wydasz budżet wynoszący ( , –). Jeśli uruchomisz ją po raz drugi, łączny budżet wyniesie I podobnie, jeśli obliczasz wiele statystyk za pomocą metody NaiveBudgetAccountant,a następnie budżet na potrzeby prywatności wynosi zer, Twój łączny budżet wynosi (2e, 2). Oznacza to osłabienie gwarancji prywatności.

Aby obejść to ograniczenie, musisz użyć pojedynczego wystąpienia NaiveBudgetAccountant z łącznym budżetem, który chcesz wykorzystać do obliczania wielu statystyk na podstawie tych samych danych. Następnie musisz określić wartości epsilon i delta, których chcesz użyć w przypadku każdej agregacji. W ostatecznym rozrachunku otrzymasz tę samą ogólną gwarancję prywatności, ale im wyższe wartości epsilon i delta w konkretnej agregacji, tym większa jej dokładność.

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

Statystyki są obliczane na podstawie 2 różnych rodzajów danych: conversion_value, za pomocą których określasz wysokość przychodów na podstawie tego, który produkt wyświetlił się jako pierwszy, oraz danych has_conversion, które służą do obliczania liczby użytkowników witryny oraz średniego współczynnika konwersji.

W przypadku każdego rodzaju danych należy osobno określić parametry, na podstawie których oblicza się statystyki prywatne. Budżet na potrzeby prywatności dzielisz na 2 rodzaje danych. Na podstawie danych has_conversion obliczasz 2 statystyki, więc chcesz przypisać 2/3 początkowego budżetu, a drugą trzecią – kategorię danych conversion_value.

Aby obliczyć kilka statystyk:

  1. Skonfiguruj księgowego ds. budżetu dotyczącego prywatności i podaj łączne wartości epsilon i delta, których chcesz używać w przypadku 3 statystyk:
budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=0)
  1. Aby obliczyć dane, zainicjuj interfejs DPEngine:
 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)
  1. Podaj parametry tego wskaźnika.
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 na potrzeby prywatności. Możesz przypisać wszystkim tę samą wagę, ale wolisz ustawić ten argument na jedną trzecią, jak wyjaśniliśmy wcześniej.

Ustawiasz też argumenty min_value i max_value, które określają dolną i górną granicę wartości zależnej od jednostki prywatności partycji. Te parametry są wymagane przy obliczaniu prywatnej sumy lub średniej. Nie należy oczekiwać wartości ujemnych, więc możesz przyjąć wartości 0 i 100 jako rozsądne wartości progowe.

  1. Wyodrębnij odpowiednie dane, a potem 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 definiujesz teraz mean i count jako agregacje i przypisujesz do tych obliczeń 2/3 budżetu na potrzeby prywatności. Chcesz mieć te same progi udziału w obu statystykach i obliczać je na podstawie tej samej zmiennej has_conversion, możesz więc połączyć je w tym samym wystąpieniu pipeline_dp.AggregateParams i obliczyć je jednocześnie.

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

Można zestawić wszystkie trzy statystyki prywatne z oryginalnymi statystykami. W zależności od dodanego szumu wyniki mogą wykraczać poza miarodajną skalę. W tym przypadku współczynnik konwersji i łączna wartość konwersji w przypadku skoczków jest ujemny, ponieważ dodany szum jest symetryczny wokół zera. Do dalszych analiz i przetwarzania najlepiej nie przetwarzać ręcznie prywatnych statystyk. Jeśli jednak chcesz dodać te wykresy do raportu, możesz po prostu ustawić minimalną wartość na 0 bez naruszania gwarancji prywatności.

cb1fc563f817eaf.png

7. Uruchamianie potoku za pomocą Beam

Przetwarzanie danych wymaga obecnie przetwarzania ogromnych ilości danych na tyle duże, że nie można ich 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 usługi Beam i Spark tylko z niewielkimi zmianami w kodzie.

Aby uruchomić potok za pomocą Beam za pomocą interfejsu API private_beam:

  1. Zainicjuj zmienną runner, a potem utwórz potok, w którym stosujesz operacje związane z prywatnością do reprezentacji obiektu rows (typu Beam):
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 (prywatną), która gwarantuje, że wszelkie agregacje będą zgodne z Twoimi wymaganiami dotyczącymi 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ć statystyki:
dp_result = pcol | pbeam.Sum(params)
  1. Na koniec Twój kod powinien wyglądać tak:
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: dostosowywanie parametrów dotyczących prywatności i narzędzia

W tym ćwiczeniu z programowania wystąpiło sporo parametrów, m.in. parametry epsilon, delta i max_partitions_contributed. Możesz je podzielić na 2 kategorie: parametry prywatności i parametry narzędzi.

Parametry prywatności

Parametry epsilon i delta określają prywatność zapewnianą przez Ciebie prywatności różnicowej. Mówiąc dokładniej, są to miary tego, ile informacji na temat danych zanonimizowanych może uzyskać potencjalny atakujący. Im wyższa wartość parametrów, tym więcej informacji na temat danych uzyskuje osoba przeprowadzająca atak, co stanowi zagrożenie dla prywatności. Z drugiej strony im niższa wartość parametrów epsilon i delta, tym więcej szumu trzeba dodać do danych wyjściowych, aby zapewnić ich anonimowość, i tym większą liczbę unikalnych użytkowników, których potrzebujesz na każdej partycji, aby zachować ich w zanonimizowanych danych wyjściowych. W tym przypadku chodzi o kompromis między użytecznością a prywatnością.

W PipelineDP musisz określić wymagane gwarancje prywatności dotyczące zanonimizowanych danych wyjściowych, gdy ustawiasz łączny budżet na potrzeby prywatności w instancji NaiveBudgetAccountant. Ograniczenie polega jednak na tym, że jeśli chcesz zachować gwarancję prywatności, musisz ostrożnie używać osobnej instancji NaiveBudgetAccountant do każdej agregacji lub kilka razy uruchamiać potok, 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 użyteczności publicznej

Parametry narzędzia nie mają wpływu na gwarancje prywatności, ale wpływają na dokładność, a w konsekwencji na użyteczność danych wyjściowych. Są one dostępne w instancji AggregateParams i używane do skalowania dodanego szumu.

Parametr narzędzia dostarczany w instancji AggregateParams i mający zastosowanie do wszystkich agregacji to parametr max_partitions_contributed. Partycja odpowiada kluczowi danych zwracanych przez operację agregacji PipelineDP, więc parametr max_partitions_contributed ogranicza liczbę unikalnych wartości kluczy, które użytkownik może przyczynić się do wygenerowania danych wyjściowych. Jeśli użytkownik przekaże liczbę kluczy, które przekraczają wartość parametru max_partitions_contributed, niektóre z nich zostaną pominięte, aby uzyskać dokładną wartość parametru max_partitions_contributed.

I podobnie, większość agregacji ma parametr max_contributions_per_partition. Pochodzą one również w instancji AggregateParams, a każda agregacja może mieć osobną wartość. Powiązanie użytkownika z każdym kluczem zostało powiązane z danym kluczem.

Szum dodawany do danych wyjściowych jest skalowany według parametrów max_partitions_contributed i max_contributions_per_partition, dlatego występuje tu kompromis: im większe wartości przypisane do poszczególnych parametrów, tym więcej danych zachowasz, ale wynik będzie bardziej szumiący.

Niektóre agregacje wymagają parametrów min_value i max_value, które określają progi udziału poszczególnych użytkowników. Jeśli użytkownik przekaże wartość niższą niż wartość przypisana parametrowi min_value, wartość zostanie zwiększona do wartości tego parametru. Podobnie, jeśli użytkownik przekaże wartość większą niż wartość parametru max_value, wartość będzie zmniejszana do wartości parametru. Aby zachować więcej pierwotnych wartości, musisz określić większe progi. Szum jest skalowany zgodnie z rozmiarem granic, więc większe granice pozwalają zachować więcej danych, ale efekt jest bardziej zaszumiony.

Dodatkowo parametr noise_kind obsługuje 2 różne mechanizmy szumu w PipelineDP: szum GAUSSIAN i LAPLACE. Rozkład LAPLACE zapewnia większą użyteczność z niskimi granicami udziału w konwersji, dlatego PipelineDP używa go domyślnie. Jeśli jednak chcesz użyć szumu rozkładu GAUSSIAN, możesz określić go w wystąpieniu AggregateParams.

9. Gratulacje

Brawo! Ćwiczenie z programowania PipelineDP zostało ukończone i udało Ci się dowiedzieć wiele o prywatności różnicowej i PipelineDP.

Więcej informacji