1. Einführung
Sie denken vielleicht, dass aggregierte Statistiken keine Informationen über die Personen preisgeben, deren Daten in die Statistiken einfließen. Es gibt jedoch viele Möglichkeiten, wie ein Angreifer aus einer aggregierten Statistik vertrauliche Informationen über Personen in einem Dataset erhalten kann.
Um die Privatsphäre von Einzelpersonen zu schützen, erfahren Sie, wie Sie private Statistiken mit differenziell privaten Aggregationen aus Privacy on Beam erstellen. Privacy on Beam ist ein Framework für differenziellen Datenschutz, das mit Apache Beam funktioniert.
Was meinen wir mit „privat“?
Wenn in diesem Codelab das Wort „privat“ verwendet wird, bedeutet das, dass die Ausgabe so erstellt wird, dass keine privaten Informationen über die Personen in den Daten preisgegeben werden. Dazu verwenden wir Differential Privacy, eine starke Anonymisierungsmethode zum Schutz der Privatsphäre. Bei der Anonymisierung werden Daten von mehreren Nutzern zusammengefasst, um die Privatsphäre der Nutzer zu schützen. Bei allen Anonymisierungsmethoden wird die Aggregation verwendet, aber nicht bei allen Aggregationsmethoden wird eine Anonymisierung erreicht. Differential Privacy hingegen bietet messbare Garantien in Bezug auf Informationslecks und Datenschutz.
2. Übersicht über Differential Privacy
Betrachten wir ein einfaches Beispiel, um den differenziellen Datenschutz besser zu verstehen.
Dieses Balkendiagramm zeigt die Auslastung eines kleinen Restaurants an einem bestimmten Abend. Viele Gäste kommen um 19:00 Uhr und das Restaurant ist um 01:00 Uhr komplett leer:

Das sieht nützlich aus!
Es gibt einen Haken. Wenn ein neuer Gast ankommt, wird dies sofort durch das Balkendiagramm sichtbar. Im Diagramm ist klar erkennbar, dass ein neuer Gast vorhanden ist und dieser Gast etwa um 1: 00 Uhr eingegangen ist:

Aus Datenschutzsicht ist das nicht optimal. Eine wirklich anonymisierte Statistik sollte keinen Aufschluss über individuelle Beiträge geben. Wenn Sie diese beiden Diagramme nebeneinander stellen, ist das noch besser erkennbar: Das orangefarbene Balkendiagramm hat einen zusätzlichen Gast, der um 01:00 Uhr angekommen ist:

Auch das ist nicht gut. Was tun wir?
Wir machen Balkendiagramme etwas ungenauer, indem wir zufälliges Rauschen hinzufügen.
Sehen Sie sich die beiden Balkendiagramme unten an. Sie sind zwar nicht völlig genau, aber dennoch nützlich und geben keine einzelnen Beiträge preis. Sehr gut!

Bei Differential Privacy wird genau die richtige Menge an zufälligem Rauschen hinzugefügt, um einzelne Beiträge zu maskieren.
Unsere Analyse war etwas zu vereinfacht. Die korrekte Implementierung von Differential Privacy ist aufwendiger und birgt einige unerwartete Implementierungsdetails. Ähnlich wie bei der Kryptografie ist es möglicherweise keine gute Idee, eine eigene Implementierung von Differential Privacy zu erstellen. Sie können Privacy on Beam verwenden, anstatt eine eigene Lösung zu implementieren. Implementieren Sie Differential Privacy nicht selbst.
In diesem Codelab zeigen wir, wie Sie mit Privacy on Beam Analysen mit Differential Privacy durchführen.
3. Datenschutz bei Beam
Sie müssen Privacy on Beam nicht herunterladen, um dem Codelab folgen zu können, da sich der gesamte relevante Code und die Diagramme in diesem Dokument befinden. Wenn Sie den Code jedoch herunterladen möchten, um damit zu experimentieren, ihn selbst auszuführen oder Privacy on Beam später zu verwenden, können Sie das tun. Folgen Sie dazu der Anleitung unten.
Dieses Codelab bezieht sich auf Version 1.1.0 der Bibliothek.
Laden Sie zuerst „Datenschutz auf Beam“ herunter:
https://github.com/google/differential-privacy/archive/refs/tags/v1.1.0.tar.gz
Alternativ können Sie das GitHub-Repository klonen:
git clone --branch v1.1.0 https://github.com/google/differential-privacy.git
Die Datenschutzrichtlinien für Beam befinden sich im Verzeichnis der obersten Ebene privacy-on-beam/.
Der Code für dieses Codelab und das Dataset befinden sich im Verzeichnis privacy-on-beam/codelab/.
Außerdem muss Bazel auf Ihrem Computer installiert sein. Die Installationsanleitung für Ihr Betriebssystem finden Sie auf der Bazel-Website.
4. Besuche pro Stunde berechnen
Stellen Sie sich vor, Sie sind Restaurantbesitzer und möchten einige Statistiken zu Ihrem Restaurant teilen, z. B. beliebte Besuchszeiten. Glücklicherweise kennen Sie sich mit Differential Privacy und Anonymisierung aus und möchten das so tun, dass keine Informationen über einzelne Besucher preisgegeben werden.
Der Code für dieses Beispiel ist in codelab/count.go.
Wir beginnen mit dem Laden eines Mock-Datasets, das Besuche in Ihrem Restaurant an einem bestimmten Montag enthält. Der Code dafür ist für dieses Codelab nicht relevant, aber Sie können ihn in codelab/main.go, codelab/utils.go und codelab/visit.go ansehen.
Besucher-ID | Eingegebene Zeit | Verbrachte Zeit (Minuten) | Ausgegebener Betrag (Euro) |
1 | 09:30:00 | 26 | 24 |
2 | 11:54:00 Uhr | 53 | 17 |
3 | 13:05:00 | 81 | 33 |
Zuerst erstellen Sie mit Beam im folgenden Codebeispiel ein nicht privates Balkendiagramm der Besuchszeiten in Ihrem Restaurant. Scope ist eine Darstellung der Pipeline. Jede neue Operation, die wir an den Daten ausführen, wird der Scope hinzugefügt. CountVisitsPerHour nimmt einen Scope und eine Sammlung von Besuchen als Eingabe entgegen, die in Beam als PCollection dargestellt wird. Die Stunde jedes Besuchs wird extrahiert, indem die Funktion extractVisitHour auf die Sammlung angewendet wird. Anschließend wird die Anzahl der Vorkommen für jede Stunde gezählt und zurückgegeben.
func CountVisitsPerHour(s beam.Scope, col beam.PCollection) beam.PCollection {
s = s.Scope("CountVisitsPerHour")
visitHours := beam.ParDo(s, extractVisitHourFn, col)
visitsPerHour := stats.Count(s, visitHours)
return visitsPerHour
}
func extractVisitHourFn(v Visit) int {
return v.TimeEntered.Hour()
}
Dadurch wird im aktuellen Verzeichnis ein ansprechendes Balkendiagramm (durch Ausführen von bazel run codelab -- --example="count" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/count.csv --output_chart_file=$(pwd)/count.png) als count.png erstellt:

Im nächsten Schritt konvertieren Sie Ihre Pipeline und Ihr Balkendiagramm in private Ressourcen. So gehen wir dabei vor.
Rufen Sie zuerst MakePrivateFromStruct auf einem PCollection<V> an, um eine PrivatePCollection<V> zu erhalten. Die Eingabe PCollection muss eine Sammlung von Structs sein. Wir müssen einen PrivacySpec und einen idFieldPath als Eingabe für MakePrivateFromStruct eingeben.
spec := pbeam.NewPrivacySpec(epsilon, delta) pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")
PrivacySpec ist eine Struktur, die die Differential Privacy-Parameter (Epsilon und Delta) enthält, die wir zum Anonymisieren der Daten verwenden möchten. (Sie müssen sich jetzt noch keine Gedanken darüber machen. Später gibt es einen optionalen Abschnitt, in dem Sie mehr darüber erfahren können.)
idFieldPath ist der Pfad des Nutzer-ID-Felds innerhalb der Struktur (in unserem Fall Visit). Die Nutzer-ID der Besucher ist hier das Feld VisitorID von Visit.
Anschließend rufen wir pbeam.Count() anstelle von stats.Count() auf. pbeam.Count() verwendet als Eingabe eine CountParams-Struktur, die Parameter wie MaxValue enthält, die sich auf die Genauigkeit der Ausgabe auswirken.
visitsPerHour := pbeam.Count(s, visitHours, pbeam.CountParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxValue: 1,
})
Mit MaxPartitionsContributed wird begrenzt, wie viele verschiedene Besuchszeiten ein Nutzer beitragen kann. Wir gehen davon aus, dass sie das Restaurant höchstens einmal pro Tag besuchen (oder es ist uns egal, ob sie es mehrmals am Tag besuchen), daher setzen wir den Wert ebenfalls auf 1. Diese Parameter werden in einem optionalen Abschnitt ausführlicher behandelt.
Mit MaxValue wird begrenzt, wie oft ein einzelner Nutzer zu den Werten beitragen kann, die wir zählen. In diesem speziellen Fall zählen wir die Besuchszeiten. Wir gehen davon aus, dass ein Nutzer das Restaurant nur einmal besucht (oder es ist uns egal, ob er es mehrmals pro Stunde besucht). Daher legen wir diesen Parameter auf 1 fest.
Am Ende sollte Ihr Code so aussehen:
func PrivateCountVisitsPerHour(s beam.Scope, col beam.PCollection) beam.PCollection {
s = s.Scope("PrivateCountVisitsPerHour")
// Create a Privacy Spec and convert col into a PrivatePCollection
spec := pbeam.NewPrivacySpec(epsilon, delta)
pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")
visitHours := pbeam.ParDo(s, extractVisitHourFn, pCol)
visitsPerHour := pbeam.Count(s, visitHours, pbeam.CountParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxValue: 1,
})
return visitsPerHour
}
Für die differenziell datenschutzfreundliche Statistik wird ein ähnliches Balkendiagramm (count_dp.png) angezeigt. Mit dem vorherigen Befehl werden sowohl die nicht private als auch die private Pipeline ausgeführt:

Glückwunsch! Sie haben Ihre erste differenziell private Statistik berechnet.
Das Balkendiagramm, das Sie erhalten, wenn Sie den Code ausführen, kann sich von diesem unterscheiden. Kein Problem. Aufgrund des Rauschens bei Differential Privacy erhalten Sie jedes Mal, wenn Sie den Code ausführen, ein anderes Balkendiagramm. Sie können jedoch sehen, dass es dem ursprünglichen nicht privaten Balkendiagramm mehr oder weniger ähnelt.
Es ist sehr wichtig, dass die Pipeline aus Datenschutzgründen nicht mehrmals ausgeführt wird, z. B. um ein ansprechenderes Balkendiagramm zu erhalten. Warum Sie Ihre Pipelines nicht noch einmal ausführen sollten, wird im Abschnitt „Mehrere Statistiken berechnen“ erläutert.
5. Öffentliche Partitionen verwenden
Im vorherigen Abschnitt haben Sie vielleicht bemerkt, dass wir alle Besuche (Daten) für einige Partitionen, d.h. Stunden, entfernt haben.

Das liegt an der Auswahl/Festlegung von Schwellenwerten für Partitionen. Das ist ein wichtiger Schritt, um die Differenzialdatenschutzgarantien zu gewährleisten, wenn die Existenz von Ausgabepartitionen von den Nutzerdaten selbst abhängt. In diesem Fall kann allein das Vorhandensein einer Partition in der Ausgabe die Existenz eines einzelnen Nutzers in den Daten offenbaren. In diesem Blogpost wird erklärt, warum dies gegen den Datenschutz verstößt. Um dies zu verhindern, werden in Privacy on Beam nur Partitionen mit einer ausreichenden Anzahl von Nutzern beibehalten.
Wenn die Liste der Ausgabepartitionen nicht von privaten Nutzerdaten abhängt, d.h. es sich um öffentliche Informationen handelt, ist dieser Schritt zur Auswahl der Partition nicht erforderlich. Das ist tatsächlich der Fall für unser Restaurantbeispiel: Wir kennen die Öffnungszeiten des Restaurants (9:00 bis 21:00 Uhr).
Der Code für dieses Beispiel ist in codelab/public_partitions.go.
Wir erstellen einfach eine PCollection mit Stunden zwischen 9 und 21 (exklusiv) und geben sie in das Feld PublicPartitions von CountParams ein:
func PrivateCountVisitsPerHourWithPublicPartitions(s beam.Scope,
col beam.PCollection) beam.PCollection {
s = s.Scope("PrivateCountVisitsPerHourWithPublicPartitions")
// Create a Privacy Spec and convert col into a PrivatePCollection
spec := pbeam.NewPrivacySpec(epsilon, /* delta */ 0)
pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")
// Create a PCollection of output partitions, i.e. restaurant's work hours
// (from 9 am till 9pm (exclusive)).
hours := beam.CreateList(s, [12]int{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
visitHours := pbeam.ParDo(s, extractVisitHourFn, pCol)
visitsPerHour := pbeam.Count(s, visitHours, pbeam.CountParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxValue: 1,
// Visitors only visit during work hours
PublicPartitions: hours,
})
return visitsPerHour
}
Wenn Sie öffentliche Partitionen und Laplace-Rauschen (Standard) verwenden, wie oben, können Sie „delta“ auf 0 setzen.
Wenn wir die Pipeline mit öffentlichen Partitionen (mit bazel run codelab -- --example="public_partitions" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/public_partitions.csv --output_chart_file=$(pwd)/public_partitions.png) ausführen, erhalten wir (public_partitions_dp.png):

Wie Sie sehen, behalten wir jetzt die Partitionen 9, 10 und 16 bei, die wir zuvor ohne öffentliche Partitionen entfernt haben.
Wenn Sie öffentliche Partitionen verwenden, können Sie nicht nur mehr Partitionen behalten, sondern es wird auch etwa halb so viel Rauschen zu jeder Partition hinzugefügt, als wenn Sie keine öffentlichen Partitionen verwenden. Das liegt daran, dass kein Datenschutzbudget (Epsilon und Delta) für die Auswahl der Partitionen aufgewendet wird. Daher ist der Unterschied zwischen Roh- und privaten Zählungen im Vergleich zum vorherigen Lauf etwas geringer.
Bei der Verwendung öffentlicher Partitionen sind zwei wichtige Punkte zu beachten:
- Seien Sie vorsichtig, wenn Sie die Liste der Partitionen aus Rohdaten ableiten. Wenn Sie dies nicht auf differenziell private Weise tun, z. B. indem Sie einfach die Liste aller Partitionen in den Nutzerdaten lesen, bietet Ihre Pipeline keine differenziell privaten Garantien mehr. Im Abschnitt „Erweitert“ unten erfahren Sie, wie Sie dies auf datenschutzfreundliche Weise tun können.
- Wenn für einige der öffentlichen Partitionen keine Daten (z.B. Besuche) vorhanden sind, wird diesen Partitionen Rauschen hinzugefügt, um die differentielle Vertraulichkeit zu wahren. Wenn wir beispielsweise die Stunden zwischen 0 und 24 (anstatt 9 und 21) verwenden würden, wären alle Stunden verrauscht und es würden möglicherweise einige Besuche angezeigt, obwohl keine vorhanden sind.
(Erweitert) Partitionen aus Daten ableiten
Wenn Sie mehrere Aggregationen mit derselben Liste nicht öffentlicher Ausgabepartitionen in derselben Pipeline ausführen, können Sie die Liste der Partitionen einmal mit SelectPartitions() ableiten und die Partitionen als PublicPartition-Eingabe für jede Aggregation bereitstellen. Das ist nicht nur aus Datenschutzsicht sicher, sondern ermöglicht es Ihnen auch, weniger Rauschen hinzuzufügen, da das Datenschutzbudget für die Auswahl von Partitionen nur einmal für die gesamte Pipeline verwendet wird.
6. Durchschnittliche Aufenthaltsdauer berechnen
Nachdem wir nun wissen, wie wir Dinge auf differenziell private Weise zählen können, sehen wir uns an, wie wir Mittelwerte berechnen. Als Nächstes berechnen wir die durchschnittliche Aufenthaltsdauer der Besucher.
Der Code für dieses Beispiel ist in codelab/mean.go.
Normalerweise würden wir zur Berechnung eines nicht privaten Durchschnitts der Aufenthaltsdauer stats.MeanPerKey() mit einem Vorverarbeitungsschritt verwenden, bei dem die eingehenden PCollection der Besuche in PCollection<K,V> umgewandelt werden. Dabei ist K die Besuchszeit und V die Zeit, die der Besucher im Restaurant verbracht hat.
func MeanTimeSpent(s beam.Scope, col beam.PCollection) beam.PCollection {
s = s.Scope("MeanTimeSpent")
hourToTimeSpent := beam.ParDo(s, extractVisitHourAndTimeSpentFn, col)
meanTimeSpent := stats.MeanPerKey(s, hourToTimeSpent)
return meanTimeSpent
}
func extractVisitHourAndTimeSpentFn(v Visit) (int, int) {
return v.TimeEntered.Hour(), v.MinutesSpent
}
Dadurch wird im aktuellen Verzeichnis ein ansprechendes Balkendiagramm (durch Ausführen von bazel run codelab -- --example="mean" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/mean.csv --output_chart_file=$(pwd)/mean.png) als mean.png erstellt:

Um die Daten differenziell privat zu machen, wandeln wir PCollection wieder in PrivatePCollection um und ersetzen stats.MeanPerKey() durch pbeam.MeanPerKey(). Ähnlich wie bei Count gibt es MeanParams, das einige Parameter wie MinValue und MaxValue enthält, die sich auf die Genauigkeit auswirken. MinValue und MaxValue stellen die Grenzen dar, die wir für den Beitrag jedes Nutzers zu jedem Schlüssel haben.
meanTimeSpent := pbeam.MeanPerKey(s, hourToTimeSpent, pbeam.MeanParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxContributionsPerPartition: 1,
// Minimum time spent per user (in mins)
MinValue: 0,
// Maximum time spent per user (in mins)
MaxValue: 60,
})
In diesem Fall steht jeder Schlüssel für eine Stunde und die Werte für die Zeit, die Besucher verbracht haben. Wir setzen MinValue auf 0, da wir nicht davon ausgehen, dass Besucher weniger als 0 Minuten im Restaurant verbringen. Wir legen MaxValue auf 60 fest. Wenn ein Besucher also mehr als 60 Minuten auf Ihrer Website verbringt, gehen wir davon aus, dass er 60 Minuten dort war.
Am Ende sollte Ihr Code so aussehen:
func PrivateMeanTimeSpent(s beam.Scope, col beam.PCollection) beam.PCollection {
s = s.Scope("PrivateMeanTimeSpent")
// Create a Privacy Spec and convert col into a PrivatePCollection
spec := pbeam.NewPrivacySpec(epsilon, /* delta */ 0)
pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")
// Create a PCollection of output partitions, i.e. restaurant's work hours
// (from 9 am till 9pm (exclusive)).
hours := beam.CreateList(s, [12]int{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
hourToTimeSpent := pbeam.ParDo(s, extractVisitHourAndTimeSpentFn, pCol)
meanTimeSpent := pbeam.MeanPerKey(s, hourToTimeSpent, pbeam.MeanParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxContributionsPerPartition: 1,
// Minimum time spent per user (in mins)
MinValue: 0,
// Maximum time spent per user (in mins)
MaxValue: 60,
// Visitors only visit during work hours
PublicPartitions: hours,
})
return meanTimeSpent
}
Ein ähnliches Balkendiagramm (mean_dp.png) wird für die differenziell private Statistik angezeigt. Mit dem vorherigen Befehl werden sowohl die nicht private als auch die private Pipeline ausgeführt:

Ähnlich wie beim Zählen erhalten wir auch hier jedes Mal unterschiedliche Ergebnisse, da es sich um einen differenziell privaten Vorgang handelt. Die differenziell privaten Aufenthaltsdauern weichen jedoch nicht weit vom tatsächlichen Ergebnis ab.
7. Umsatz pro Stunde berechnen
Eine weitere interessante Statistik ist der Umsatz pro Stunde im Laufe des Tages.
Der Code für dieses Beispiel ist in codelab/sum.go.
Auch hier beginnen wir mit der nicht privaten Version. Mit etwas Vorverarbeitung unseres Mock-Datasets können wir ein PCollection<K,V> erstellen, wobei K die Besuchszeit und V das Geld ist, das der Besucher im Restaurant ausgegeben hat. Um einen nicht vertraulichen Umsatz pro Stunde zu berechnen, können wir einfach alle Ausgaben der Besucher summieren, indem wir stats.SumPerKey() aufrufen:
func RevenuePerHour(s beam.Scope, col beam.PCollection) beam.PCollection {
s = s.Scope("RevenuePerHour")
hourToMoneySpent := beam.ParDo(s, extractVisitHourAndMoneySpentFn, col)
revenues := stats.SumPerKey(s, hourToMoneySpent)
return revenues
}
func extractVisitHourAndMoneySpentFn(v Visit) (int, int) {
return v.TimeEntered.Hour(), v.MoneySpent
}
Dadurch wird im aktuellen Verzeichnis ein ansprechendes Balkendiagramm (durch Ausführen von bazel run codelab -- --example="sum" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/sum.csv --output_chart_file=$(pwd)/sum.png) als sum.png erstellt:

Um die Daten differenziell privat zu machen, wandeln wir PCollection wieder in PrivatePCollection um und ersetzen stats.SumPerKey() durch pbeam.SumPerKey(). Ähnlich wie bei Count und MeanPerKey gibt es SumParams, die einige Parameter wie MinValue und MaxValue enthalten, die sich auf die Genauigkeit auswirken.
revenues := pbeam.SumPerKey(s, hourToMoneySpent, pbeam.SumParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Minimum money spent per user (in euros)
MinValue: 0,
// Maximum money spent per user (in euros)
MaxValue: 40,
})
In diesem Fall stehen MinValue und MaxValue für die Grenzen, die wir für das Geld haben, das jeder Besucher ausgibt. Wir haben MinValue auf 0 gesetzt, da wir nicht davon ausgehen, dass Besucher weniger als 0 € im Restaurant ausgeben. Wir setzen MaxValue auf 40. Das bedeutet, wenn ein Besucher mehr als 40 € ausgibt, gehen wir davon aus, dass er 40 € ausgegeben hat.
Am Ende sieht der Code so aus:
func PrivateRevenuePerHour(s beam.Scope, col beam.PCollection) beam.PCollection {
s = s.Scope("PrivateRevenuePerHour")
// Create a Privacy Spec and convert col into a PrivatePCollection
spec := pbeam.NewPrivacySpec(epsilon, /* delta */ 0)
pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")
// Create a PCollection of output partitions, i.e. restaurant's work hours
// (from 9 am till 9pm (exclusive)).
hours := beam.CreateList(s, [12]int{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
hourToMoneySpent := pbeam.ParDo(s, extractVisitHourAndMoneySpentFn, pCol)
revenues := pbeam.SumPerKey(s, hourToMoneySpent, pbeam.SumParams{
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Minimum money spent per user (in euros)
MinValue: 0,
// Maximum money spent per user (in euros)
MaxValue: 40,
// Visitors only visit during work hours
PublicPartitions: hours,
})
return revenues
}
Ein ähnliches Balkendiagramm (sum_dp.png) wird für die differenziell private Statistik angezeigt. Mit dem vorherigen Befehl werden sowohl die nicht private als auch die private Pipeline ausgeführt:

Ähnlich wie bei „count“ und „mean“ erhalten wir auch hier jedes Mal unterschiedliche Ergebnisse, da es sich um einen differenziell privaten Vorgang handelt. Das differenziell private Ergebnis liegt jedoch sehr nahe am tatsächlichen Umsatz pro Stunde.
8. Mehrere Statistiken berechnen
In den meisten Fällen möchten Sie möglicherweise mehrere Statistiken für dieselben zugrunde liegenden Daten berechnen, ähnlich wie bei den Funktionen „count“, „mean“ und „sum“. Das ist in der Regel einfacher und übersichtlicher, wenn Sie eine einzelne Beam-Pipeline und ein einzelnes Binärprogramm verwenden. Das ist auch mit Privacy on Beam möglich. Sie können eine einzelne Pipeline schreiben, um Ihre Transformationen und Berechnungen auszuführen, und ein einzelnes PrivacySpec für die gesamte Pipeline verwenden.
Das ist nicht nur praktischer, sondern auch datenschutzfreundlicher.PrivacySpec Die Epsilon- und Delta-Parameter, die wir für PrivacySpec bereitstellen, stellen ein Datenschutzbudget dar. Das ist ein Maß dafür, wie viele Nutzerdaten in den zugrunde liegenden Daten offengelegt werden.
Das Datenschutzbudget ist additiv: Wenn Sie eine Pipeline mit einem bestimmten Epsilon ε und Delta δ einmal ausführen, geben Sie ein (ε,δ)-Budget aus. Wenn Sie sie ein zweites Mal ausführen, haben Sie ein Gesamtbudget von (2ε, 2δ) ausgegeben. Wenn Sie mehrere Statistiken mit einem PrivacySpec (und folglich einem Datenschutzbudget) von (ε,δ) berechnen, haben Sie ein Gesamtbudget von (2ε, 2δ) ausgegeben. Das bedeutet, dass Sie die Datenschutzgarantien einschränken.
Um dies zu umgehen, sollten Sie ein einzelnes PrivacySpec mit dem Gesamtbudget verwenden, wenn Sie mehrere Statistiken für dieselben zugrunde liegenden Daten berechnen möchten. Anschließend müssen Sie das Epsilon und Delta angeben, das für jede Aggregation verwendet werden soll. Letztendlich erhalten Sie dieselbe allgemeine Datenschutzgarantie. Je höher jedoch der Epsilon- und Deltawert einer bestimmten Aggregation ist, desto höher ist die Genauigkeit.
Um das in Aktion zu sehen, können wir die drei Statistiken (Anzahl, Mittelwert und Summe), die wir zuvor separat berechnet haben, in einer einzigen Pipeline berechnen.
Der Code für dieses Beispiel ist in codelab/multiple.go. Das Gesamtbudget (ε,δ) wird gleichmäßig auf die drei Aggregationen aufgeteilt:
func ComputeCountMeanSum(s beam.Scope, col beam.PCollection) (visitsPerHour, meanTimeSpent, revenues beam.PCollection) {
s = s.Scope("ComputeCountMeanSum")
// Create a Privacy Spec and convert col into a PrivatePCollection
// Budget is shared by count, mean and sum.
spec := pbeam.NewPrivacySpec(epsilon, /* delta */ 0)
pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")
// Create a PCollection of output partitions, i.e. restaurant's work hours
// (from 9 am till 9pm (exclusive)).
hours := beam.CreateList(s, [12]int{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
visitHours := pbeam.ParDo(s, extractVisitHourFn, pCol)
visitsPerHour = pbeam.Count(s, visitHours, pbeam.CountParams{
Epsilon: epsilon / 3,
Delta: 0,
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxValue: 1,
// Visitors only visit during work hours
PublicPartitions: hours,
})
hourToTimeSpent := pbeam.ParDo(s, extractVisitHourAndTimeSpentFn, pCol)
meanTimeSpent = pbeam.MeanPerKey(s, hourToTimeSpent, pbeam.MeanParams{
Epsilon: epsilon / 3,
Delta: 0,
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Visitors can visit the restaurant once within an hour
MaxContributionsPerPartition: 1,
// Minimum time spent per user (in mins)
MinValue: 0,
// Maximum time spent per user (in mins)
MaxValue: 60,
// Visitors only visit during work hours
PublicPartitions: hours,
})
hourToMoneySpent := pbeam.ParDo(s, extractVisitHourAndMoneySpentFn, pCol)
revenues = pbeam.SumPerKey(s, hourToMoneySpent, pbeam.SumParams{
Epsilon: epsilon / 3,
Delta: 0,
// Visitors can visit the restaurant once (one hour) a day
MaxPartitionsContributed: 1,
// Minimum money spent per user (in euros)
MinValue: 0,
// Maximum money spent per user (in euros)
MaxValue: 40,
// Visitors only visit during work hours
PublicPartitions: hours,
})
return visitsPerHour, meanTimeSpent, revenues
}
9. (Optional) Parameter für Differential Privacy anpassen
In diesem Codelab wurden bereits einige Parameter erwähnt: Epsilon, Delta, maxPartitionsContributed usw. Wir können sie grob in zwei Kategorien einteilen: Datenschutzparameter und Nützlichkeitsparameter.
Datenschutzparameter
Epsilon und Delta sind die Parameter, mit denen der Datenschutz quantifiziert wird, den wir durch die Verwendung von Differential Privacy bieten. Genauer gesagt sind Epsilon und Delta ein Maß dafür, wie viele Informationen ein potenzieller Angreifer über die zugrunde liegenden Daten erhält, wenn er sich die anonymisierte Ausgabe ansieht. Je höher Epsilon und Delta sind, desto mehr Informationen erhält der Angreifer über die zugrunde liegenden Daten, was ein Datenschutzrisiko darstellt.
Je niedriger Epsilon und Delta sind, desto mehr Rauschen muss der Ausgabe hinzugefügt werden, um sie zu anonymisieren. Außerdem ist eine höhere Anzahl eindeutiger Nutzer in jeder Partition erforderlich, damit diese Partition in der anonymisierten Ausgabe enthalten ist. Hier gibt es also einen Kompromiss zwischen Nützlichkeit und Datenschutz.
Bei Privacy on Beam müssen Sie sich Gedanken über die Datenschutzgarantien machen, die Sie in Ihrer anonymisierten Ausgabe wünschen, wenn Sie das gesamte Privacy-Budget in der PrivacySpec angeben. Wenn Sie möchten, dass Ihre Datenschutzgarantien eingehalten werden, müssen Sie die Ratschläge in diesem Codelab befolgen und Ihr Budget nicht überstrapazieren, indem Sie für jede Aggregation einen separaten PrivacySpec verwenden oder die Pipeline mehrmals ausführen.
Weitere Informationen zum differenziellen Datenschutz und zur Bedeutung der Datenschutzparameter finden Sie in der Literatur.
Hilfsparameter
Diese Parameter haben keine Auswirkungen auf die Datenschutzgarantien (sofern die Empfehlungen zur Verwendung von Privacy on Beam ordnungsgemäß befolgt werden), aber sie wirken sich auf die Genauigkeit und damit auf die Nützlichkeit der Ausgabe aus. Sie sind in den Params-Structs der einzelnen Aggregationen enthalten, z.B. CountParams, SumParams usw. Mit diesen Parametern wird das hinzugefügte Rauschen skaliert.
Ein in Params verfügbarer Hilfsparameter, der für alle Aggregationen gilt, ist MaxPartitionsContributed. Eine Partition entspricht einem Schlüssel der PCollection, die von einer Privacy On Beam-Aggregationsoperation ausgegeben wird, z.B. Count, SumPerKey usw. MaxPartitionsContributed begrenzt also, wie viele unterschiedliche Schlüsselwerte ein Nutzer zur Ausgabe beitragen kann. Wenn ein Nutzer zu mehr als MaxPartitionsContributed Schlüsseln in den zugrunde liegenden Daten beiträgt, werden einige seiner Beiträge verworfen, sodass er genau MaxPartitionsContributed Schlüssel abdeckt.
Ähnlich wie bei MaxPartitionsContributed haben die meisten Aggregationen einen MaxContributionsPerPartition-Parameter. Sie werden in den Params-Structs bereitgestellt und jede Aggregation kann separate Werte dafür haben. Im Gegensatz zu MaxPartitionsContributed wird mit MaxContributionsPerPartition der Beitrag eines Nutzers für jeden Schlüssel begrenzt. Mit anderen Worten: Ein Nutzer kann nur MaxContributionsPerPartition Werte für jeden Schlüssel beitragen.
Das der Ausgabe hinzugefügte Rauschen wird durch MaxPartitionsContributed und MaxContributionsPerPartition skaliert. Hier gibt es also einen Kompromiss: Bei größeren Werten für MaxPartitionsContributed und MaxContributionsPerPartition bleiben mehr Daten erhalten, das Ergebnis ist aber auch verrauschter.
Für einige Aggregationen sind MinValue und MaxValue erforderlich. Sie geben die Grenzen für die Beiträge der einzelnen Nutzer an. Wenn ein Nutzer einen Wert unter MinValue beiträgt, wird dieser Wert auf MinValue gesetzt. Wenn ein Nutzer einen Wert über MaxValue beiträgt, wird dieser Wert auf MaxValue begrenzt. Das bedeutet, dass Sie größere Grenzen festlegen müssen, um mehr der ursprünglichen Werte beizubehalten. Ähnlich wie bei MaxPartitionsContributed und MaxContributionsPerPartition wird das Rauschen anhand der Größe der Grenzen skaliert. Bei größeren Grenzen bleiben also mehr Daten erhalten, das Ergebnis ist aber verrauschter.
Der letzte Parameter, über den wir sprechen, ist NoiseKind. Privacy On Beam unterstützt zwei verschiedene Rauschmechanismen: GaussianNoise und LaplaceNoise. Beide haben ihre Vor- und Nachteile, aber die Laplace-Verteilung bietet einen besseren Nutzen bei niedrigen Beitragsobergrenzen. Deshalb wird sie standardmäßig von Privacy On Beam verwendet. Wenn Sie jedoch Rauschen mit einer Normalverteilung verwenden möchten, können Sie Params mit einer pbeam.GaussianNoise{}-Variablen angeben.
10. Zusammenfassung
Gut gemacht! Sie haben das Codelab zum Thema Datenschutz in Beam abgeschlossen. Sie haben viel über differenziellen Datenschutz und Datenschutz bei Beam gelernt:
- Sie wandeln Ihr
PCollectionin einPrivatePCollectionum, indem SieMakePrivateFromStructaufrufen. Countverwenden, um differenziell private Anzahl zu berechnen.MeanPerKeyverwenden, um differenziell private Mittelwerte zu berechnen.SumPerKeyverwenden, um Summen mit Differential Privacy zu berechnen.- Mehrere Statistiken mit einer einzigen
PrivacySpecin einer einzigen Pipeline berechnen - Optional: Passen Sie die
PrivacySpec- und Aggregationsparameter (CountParams, MeanParams, SumParams) an.
Mit Privacy on Beam sind jedoch noch viele weitere Aggregationen möglich, z. B. Quantile und das Zählen eindeutiger Werte. Weitere Informationen finden Sie im GitHub-Repository oder im godoc.
Wenn Sie Zeit haben, geben Sie uns bitte Feedback zum Codelab, indem Sie eine Umfrage ausfüllen.