Menghitung Statistik Pribadi dengan Privacy on Beam

Anda mungkin berpikir bahwa statistik gabungan tidak akan membocorkan informasi individu yang dikumpulkan datanya. Namun, ada banyak cara penyerang dapat mempelajari informasi sensitif seseorang dalam set data statistik gabungan.

Untuk melindungi privasi individu, Anda akan mempelajari cara menghasilkan statistik pribadi menggunakan agregasi pribadi lain dari Privacy on Beam. Privacy on Beam adalah sebuah kerangka kerja privasi diferensial yang berfungsi dengan Apache Beam.

Apa yang dimaksud dengan "pribadi"?

Yang kami maksud saat menggunakan kata 'pribadi' di seluruh Codelab ini adalah output dihasilkan dengan cara yang tidak akan membocorkan informasi pribadi individu dalam data. Kita dapat melakukannya menggunakan privasi diferensial, yaitu suatu gagasan anonimisasi privasi yang kuat. Anonimisasi adalah proses pengumpulan data di beberapa pengguna untuk melindungi privasi pengguna. Semua metode anonimisasi menggunakan agregasi, tetapi tidak semua metode agregasi mencapai level anonimisasi. Di sisi lain, privasi diferensial memberikan jaminan terukur terkait kebocoran informasi dan privasi.

Untuk lebih memahami privasi diferensial, mari lihat contoh sederhananya.

Diagram batang ini menunjukkan kesibukan sebuah restoran kecil pada suatu sore. Banyak tamu datang pada pukul 19.00, dan restoran benar-benar tidak ada pengunjung pada pukul 01.00 dini hari:

a43dbf3e2c6de596.png

Sungguh terlihat berguna!

Ini dia! Saat tamu baru tiba, informasi ini langsung ditampilkan oleh diagram batang. Lihat pada diagram ini: jelas ada tamu baru yang datang, dan tamu ini telah tiba sekitar pukul 01.00 dini hari:

bda96729e700a9dd.png

Ini jelas tidak bagus dari sudut pandang privasi. Statistik yang benar-benar anonim tidak akan mengungkapkan kontribusi individu. Menempatkan dua diagram tersebut secara berdampingan akan membuatnya terlihat lebih jelas: diagram batang berwarna oranye memiliki satu tamu tambahan yang telah tiba pada pukul ~01:00 dini hari:

d562ddf799288894.png

Sekali lagi, ini tidak bagus. Apa yang akan kita lakukan?

Kita akan membuat diagram batang yang sedikit kurang akurat dengan menambahkan derau acak.

Lihat dua diagram batang di bawah. Meskipun tidak sepenuhnya akurat, dua diagram ini tetap berguna dan tidak mengungkapkan kontribusi individu. Bagus!

838a0293cd4fcfe3.gif

Privasi diferensial menambahkan jumlah derau acak yang sama untuk menyamarkan kontribusi individual.

Analisis kita agak terlalu sederhana. Menerapkan privasi diferensial dengan benar lebih terlibat dan memiliki sejumlah seluk-beluk penerapan yang cukup tidak terduga. Serupa dengan kriptografi, membuat penerapan privasi diferensial Anda sendiri mungkin bukan ide yang bagus. Anda dapat menggunakan Privacy on Beam daripada harus menerapkan solusi Anda sendiri. Jangan pertaruhkan privasi diferensial Anda!

Dalam codelab ini, kami akan menunjukkan cara melakukan analisis pribadi yang berbeda menggunakan Privacy on Beam.

Anda tidak perlu mengunduh Privacy on Beam untuk dapat mengikuti codelab karena semua kode dan grafik yang relevan dapat ditemukan di dokumen ini. Namun, jika Anda ingin mengunduh untuk bermain dengan kode, menjalankannya sendiri atau menggunakan Privasi di Beam nanti, jangan ragu untuk melakukannya dengan mengikuti langkah-langkah di bawah ini.

Perhatikan bahwa codelab ini untuk pustaka versi 1.1.0.

Pertama, download Privacy on Beam:

https://github.com/google/differential-privacy/archive/refs/tags/v1.1.0.tar.gz

Atau, Anda dapat menggandakan repositori GitHub:

git clone --branch v1.1.0 https://github.com/google/differential-privacy.git

Privacy on Beam ada di direktori privacy-on-beam/ tingkat teratas.

Kode untuk codelab ini dan set data ada di direktori privacy-on-beam/codelab/.

Anda juga harus menginstal Bazel di komputer. Temukan petunjuk penginstalan sistem operasi di situs Bazel.

Bayangkan Anda adalah pemilik sebuah restoran dan ingin membagikan beberapa statistik restoran Anda, seperti mengungkapkan waktu-waktu kunjungan yang padat. Untungnya, Anda tahu tentang Privasi Diferensial dan Anonimisasi. Jadi, Anda ingin melakukannya dengan cara yang tidak akan membocorkan informasi setiap pengunjung.

Kode untuk contoh ini ada di codelab/count.go.

Mari mulai dengan memuat set data tiruan yang berisi kunjungan ke restoran Anda pada Senin tertentu. Kode untuk data ini tidak menarik untuk keperluan codelab ini, tetapi Anda dapat memeriksa kode untuk itu dalam codelab/main.go, codelab/utils.go dan codelab/visit.go.

ID Pengunjung

Waktu yang dimasukkan

Waktu yang dihabiskan (mnt)

Uang yang dibelanjakan (euro)

1

9:30:00

26

24

2

11:54:00

53

17

3

13:05:00

81

33

Pertama-tama Anda akan membuat diagram batang non-pribadi yang berisi waktu kunjungan ke restoran menggunakan Beam dalam contoh kode di bawah ini. Scope adalah representasi dari pipeline, dan setiap operasi baru yang kita lakukan pada data akan ditambahkan ke Scope. CountVisitsPerHour membutuhkan Scope dan sekumpulan kunjungan yang direpresentasikan sebagai PCollection di Beam. Kode ini akan mengekstrak jam setiap kunjungan dengan menerapkan fungsi extractVisitHour di koleksi. Kemudian kode ini akan menghitung jumlah setiap jam dan menampilkannya.

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()
}

Operasi ini menghasilkan diagram batang yang bagus (dengan menjalankan bazel run codelab -- --example="count" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/count.csv --output_chart_file=$(pwd)/count.png) di direktori saat ini sebagai count.png:

a179766795d4e64a.png

Langkah selanjutnya adalah mengonversi pipeline dan diagram batang Anda ke dalam pipeline pribadi. Kita melakukannya dengan cara berikut ini.

Pertama, panggil MakePrivateFromStruct di PCollection<V> untuk mendapatkan PrivatePCollection<V>. PCollection input harus berupa kumpulan struktur. Kita perlu memasukkan PrivacySpec dan idFieldPath sebagai input ke MakePrivateFromStruct.

spec := pbeam.NewPrivacySpec(epsilon, delta)
pCol := pbeam.MakePrivateFromStruct(s, col, spec, "VisitorID")

PrivacySpec adalah struktur yang menyimpan parameter privasi diferensial (epsilon dan delta) yang ingin kita gunakan untuk menganonimkan data. (Anda tidak perlu mengkhawatirkannya untuk saat ini, kami memiliki bagian opsional nanti jika Anda ingin mempelajari hal tersebut lebih lanjut.)

idFieldPath adalah jalur kolom ID pengguna dalam struktur (Visit dalam kasus kita). Di sini, pengenal pengguna pengunjung adalah kolom VisitorID pada Visit.

Kemudian, kita memanggil pbeam.Count(), bukan stats.Count(), CountParams digunakan oleh pbeam.Count() sebagai input yang menyimpan parameter seperti MaxValue yang memengaruhi akurasi output.

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,
})

Demikian pula, MaxPartitionsContributed membatasi berapa jam kunjungan berbeda yang dapat dikontribusikan oleh pengguna. Kita berharap mereka mengunjungi restoran paling banyak sekali sehari (atau kita tidak peduli jika mereka berkunjung beberapa kali sepanjang hari). Jadi kita menetapkannya ke 1 juga. Kita akan membahas parameter ini secara lebih detail di bagian opsional.

MaxValue membatasi berapa kali satu pengguna dapat berkontribusi pada nilai yang kita hitung. Dalam kasus tertentu ini, nilai yang kita hitung adalah jam kunjungan, dan kita berharap pengguna mengunjungi restoran hanya sekali (atau kita tidak peduli jika mereka mengunjungi beberapa kali per jam). Jadi, kita menetapkan parameter ini ke 1.

Jika sudah selesai, kode Anda akan terlihat seperti ini:

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
}

Kita melihat diagram batang yang serupa (count_dp.png) untuk statistik pribadi yang berbeda (perintah sebelumnya menjalankan pipeline non-pribadi dan pipeline pribadi):

d6a0ace1acd3c760.png

Selamat! Anda telah menghitung statistik khusus pribadi pertama Anda!

Diagram batang yang Anda dapatkan saat menjalankan kode mungkin berbeda dari yang kode ini. Tidak masalah. Karena adanya derau dalam privasi diferensial, Anda akan mendapatkan diagram batang yang berbeda setiap kali menjalankan kode, tetapi Anda dapat melihat bahwa diagram berukuran lebih besar atau lebih mirip dengan diagram batang non-pribadi asli yang kita miliki.

Sangat penting bagi jaminan privasi untuk tidak menjalankan kembali pipeline beberapa kali (misalnya, untuk mendapatkan tampilan diagram batang yang lebih baik). Alasan mengapa Anda tidak perlu menjalankan ulang pipeline dijelaskan di bagian "Komputasi Beberapa Statistik".

Di bagian sebelumnya, Anda mungkin melihat bahwa kami telah menghapus semua kunjungan (data) untuk beberapa partisi, yakni jam.

d7fbc5d86d91e54a.png

Hal ini terjadi karena pemilihan/ambang batas partisi yang merupakan langkah penting untuk memastikan jaminan privasi diferensial ketika keberadaan partisi output bergantung pada data pengguna itu sendiri. Jika hal ini terjadi, keberadaan partisi di output dapat membocorkan keberadaan pengguna individual dalam data (Lihat entri blog ini untuk penjelasan tentang alasan pelanggaran privasi). Untuk mencegah hal ini, Privacy on Beam hanya menyimpan partisi yang memiliki jumlah pengguna yang mencukupi.

Jika daftar partisi output tidak bergantung pada data pengguna pribadi, yaitu partisi data bersifat publik, kita tidak memerlukan langkah pemilihan partisi ini. Langkah ini sebenarnya berlaku untuk contoh restoran kita: kita tahu jam kerja restoran (09.00 sampai 21.00).

Kode untuk contoh ini ada di codelab/public_partitions.go.

Kita hanya akan membuat PCollection jam buka antara 09.00 hingga 21.00 (eksklusif) dan memasukkannya ke kolom PublicPartitions dari CountParams:

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
}

Perhatikan bahwa Anda dapat menyetel delta ke 0 jika menggunakan partisi publik dan Laplace Noise (default), seperti kasus di atas.

Saat kita menjalankan pipeline dengan partisi publik (dengan 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), kita mendapatkan (public_partitions_dp.png):

7c950fbe99fec60a.png

Seperti yang Anda lihat, kita sekarang menyimpan partisi 9, 10, dan 16 yang sebelumnya kita keluarkan tanpa partisi publik.

Penggunaan partisi publik tidak hanya memungkinkan Anda menyimpan lebih banyak partisi, tetapi juga menambahkan kurang lebih setengah derau ke setiap partisi dibandingkan jika Anda tidak menggunakan partisi publik karena tidak membelanjakan persediaan privasi, yaitu epsilon & delta, pada pemilihan partisi. Itulah sebabnya perbedaan antara jumlah mentah dan pribadi cenderung lebih sedikit dibandingkan dengan proses sebelumnya.

Ada dua hal penting yang perlu diingat saat menggunakan partisi publik:

  1. Berhati-hatilah saat menempatkan daftar partisi dari data mentah: jika Anda tidak melakukannya dengan cara privasi diferensial, misalnya hanya membaca daftar semua partisi di data pengguna, pipeline Anda tidak lagi memberikan jaminan privasi diferensial. Lihat bagian lanjutan di bawah untuk mengetahui cara melakukannya dengan cara privasi diferensial.
  2. Jika tidak ada data (misalnya kunjungan) untuk beberapa partisi publik, derau akan diterapkan ke partisi tersebut untuk mempertahankan privasi diferensial. Misalnya, jika kita menggunakan jam antara 0 sampai 24 (bukan 9 sampai 21), semua jam akan dibunyikan dan mungkin menampilkan beberapa kunjungan yang sebenarnya tidak ada.

(Lanjutan) Mengambil Partisi dari Data

Jika menjalankan beberapa agregasi dengan daftar partisi output keluaran yang sama dalam pipeline yang sama, Anda dapat memperoleh daftar partisi satu kali menggunakan SelectPartitions() dan memasukkan partisi ke setiap agregasi sebagai input PublicPartition. Selain aman dari segi privasi, fitur ini juga memungkinkan Anda menambahkan lebih sedikit derau karena persediaan privasi pada pemilihan partisi untuk seluruh pipeline digunakan hanya sekali.

Sekarang, setelah mengetahui cara menghitung sesuatu dengan cara privasi diferensial, mari kita perhatikan cara menghitungnya. Lebih khusus lagi, kami sekarang akan menghitung rata-rata durasi pengunjung berada di restoran.

Kode untuk contoh ini adalah codelab/mean.go.

Biasanya, untuk menghitung rata-rata durasi berada di restoran yang tidak bersifat pribadi, kita akan menggunakan stats.MeanPerKey() dengan langkah pra-pemrosesan yang mengonversi PCollection kunjungan masuk menjadi PCollection<K,V> dengan K adalah jam kunjungan dan V adalah waktu yang dihabiskan pengunjung di restoran.

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
}

Operasi ini menghasilkan diagram batang yang bagus (dengan menjalankan bazel run codelab -- --example="mean" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/mean.csv --output_chart_file=$(pwd)/mean.png) di direktori saat ini sebagai mean.png:

bc2df28bf94b3721.png

Untuk membuatnya menjadi privasi diferensial, kita mengonversi PCollection menjadi PrivatePCollection dan mengganti stats.MeanPerKey() dengan pbeam.MeanPerKey(). Serupa dengan Count, kita memiliki MeanParams yang menyimpan beberapa parameter seperti MinValue dan MaxValue yang memengaruhi akurasi. MinValue dan MaxValue mewakili batas yang kita miliki untuk setiap kontribusi pengguna ke setiap kunci.

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,
})

Dalam hal ini, setiap kunci mewakili satu jam dan nilainya menyatakan waktu yang dihabiskan pengunjung. Kita menetapkan MinValue ke 0 karena kita tidak mengharapkan pengunjung menghabiskan waktu kurang dari 0 menit di restoran. Kita menetapkan MaxValue ke 60 yang berarti jika pengunjung menghabiskan lebih dari 60 menit, kita akan bertindak seolah-olah pengguna tersebut menghabiskan 60 menit.

Jika sudah selesai, kode Anda akan terlihat seperti ini:

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
}

Kami melihat diagram batang yang serupa (mean_dp.png) untuk statistik pribadi yang berbeda (perintah sebelumnya menjalankan pipeline non-pribadi dan pipeline pribadi):

e8ac6a9bf9792287.png

Mirip dengan hitungan, dan sekali lagi karena ini adalah operasi privasi diferensial, kita akan mendapatkan hasil yang berbeda setiap kali menjalankan hitungan. Namun, Anda dapat melihat durasi berada di restoran yang secara privasi diferensial tidak jauh dari hasil sebenarnya.

Statistik lain yang menarik dan dapat kami lihat adalah pendapatan per jam sepanjang hari.

Kode untuk contoh ini adalah codelab/sum.go.

Sekali lagi, kita akan mulai dengan versi non-pribadi. Dengan beberapa pra-pemrosesan pada set data tiruan, kita dapat membuat PCollection<K,V> dengan K sebagai jam kunjungan dan V sebagai uang yang dibelanjakan pengunjung di restoran: Untuk menghitung pendapatan non-pribadi per jam, kita cukup menjumlahkan semua uang yang dibelanjakan pengunjung dengan memanggil stats.SumPerKey():

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
}

Operasi ini menghasilkan diagram batang yang bagus (dengan menjalankan bazel run codelab -- --example="sum" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/sum.csv --output_chart_file=$(pwd)/sum.png) di direktori saat ini sebagai sum.png:

548619173fad0c9a.png

Untuk membuatnya menjadi privasi diferensial, kita mengonversi PCollection menjadi PrivatePCollection dan mengganti stats.SumPerKey() dengan pbeam.SumPerKey(). Serupa dengan Count dan MeanPerKey, kita memiliki SumParams yang menyimpan beberapa parameter seperti MinValue dan MaxValue yang memengaruhi akurasi.

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,
})

Dalam hal ini, MinValue dan MaxValue menunjukkan batas yang kita miliki untuk uang yang dibelanjakan setiap pengunjung. Kita menetapkan MinValue ke 0 karena kita tidak mengharapkan pengunjung menghabiskan uang kurang dari 0 euro di restoran. Kita menetapkan MaxValue ke 40 yang berarti jika pengunjung membelanjakan lebih dari 40 euro, kita akan bertindak seolah-olah pengguna tersebut menghabiskan 40 euro.

Jika sudah selesai, kode akan terlihat seperti ini:

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
}

Kami melihat diagram batang yang serupa (sum_dp.png) untuk statistik pribadi yang berbeda (perintah sebelumnya menjalankan pipeline non-pribadi dan pipeline pribadi):

46c375e874f3e7c4.png

Mirip dengan hitungan dan rata-rata, dan sekali lagi karena ini adalah operasi privasi diferensial, kita akan mendapatkan hasil yang berbeda setiap kali kita menjalankan hitungan. Namun, Anda dapat melihat hasil pribadi diferensial sangat dekat dengan pendapatan per jam yang sebenarnya.

Sering kali, Anda mungkin tertarik menghitung beberapa statistik pada data pokok yang sama, mirip dengan yang telah Anda lakukan pada hitungan, rata-rata, dan jumlah. Biasanya hitungan ini lebih sederhana dan lebih mudah untuk melakukan ini dalam satu pipeline Beam dan dalam satu biner. Anda juga dapat melakukannya dengan Privacy on Beam. Anda dapat menulis pipeline tunggal untuk menjalankan transformasi dan komputasi, serta menggunakan PrivacySpec tunggal untuk keseluruhan pipeline.

Bukan hanya lebih mudah untuk menulis ini dengan satu PrivacySpec, dan juga lebih baik dalam hal privasi. Jika Anda mengingat parameter epilson dan delta yang kami berikan ke PrivacySpec, parameter tersebut mewakili persediaan privasi yang merupakan ukuran jumlah privasi pengguna dalam data pokok yang mengalami kebocoran.

Hal penting yang harus diingat tentang persediaan privasi adalah bahwa anggaran bersifat aditif: Jika Anda menjalankan pipeline dengan epsilon ε dan delta δ hanya satu kali, Anda menghabiskan persediaan (ε,δ). Jika menjalankannya untuk kedua kalinya, Anda akan membelanjakan persediaan total sebesar (2ε, 2δ). Demikian pula, jika Anda menghitung beberapa statistik dengan PrivacySpec (dan secara berurutan memiliki anggaran privasi) sebesar (ε,δ), Anda telah membelanjakan total anggaran (2ε, 2δ). Ini berarti Anda menurunkan jaminan privasi.

Untuk mengatasi hal ini, saat Anda ingin menghitung beberapa statistik pada data pokok yang sama, sebaiknya gunakan satu PrivacySpec dengan total persediaan yang ingin digunakan. Kemudian, Anda harus menentukan epsilon dan delta yang ingin Anda gunakan untuk setiap agregasi. Di bagian akhir, Anda akan mendapatkan jaminan privasi yang sama secara keseluruhan; tetapi makin tinggi epsilon dan delta yang dimiliki agregasi tertentu, makin tinggi keakuratannya.

Untuk melihat ini digunakan, kita bisa menghitung tiga statistik (hitungan, rata-rata, dan jumlah) yang telah dihitung secara terpisah sebelumnya dalam satu pipeline.

Kode untuk contoh ini adalah codelab/multiple.go. Perhatikan cara kami membagi total persediaan (ε,δ) dengan sama di antara tiga agregasi:

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{
        // 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
    })

    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,
    })

    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 visitsPerHour, meanTimeSpent, revenues
}

Anda telah melihat cukup sedikit parameter yang disebutkan dalam codelab ini: epsilon, delta, maxPartitionsContributed, dll. Kita dapat secara kasar membaginya menjadi dua kategori: Parameter Privasi dan Parameter Utilitas.

Parameter Privasi

Epsilon dan delta adalah parameter yang digunakan untuk mengukur privasi yang kami sediakan dengan menggunakan privasi diferensial. Lebih tepatnya, epsilon dan delta adalah ukuran seberapa banyak informasi data pokok yang dihasilkan penyerang potensial dengan melihat output anonim. Makin tinggi epsilon dan delta, makin banyak informasi data pokok yang diperoleh penyerang, dan merupakan risiko privasi.

Di sisi lain, makin rendah epsilon dan delta, makin banyak derau yang harus Anda tambahkan ke output agar menjadi anonim, dan jumlah pengguna unik yang Anda perlukan di setiap partisi untuk mempertahankan partisi tersebut tetap menjadi output anonimisasi. Jadi, ada konsekuensi antara utilitas dan privasi di sini.

Di Beam on Privacy, Anda perlu mengkhawatirkan jaminan privasi yang diinginkan dalam output anonimisasi saat menetapkan total persediaan privasi dalam PrivacySpec. Masalahnya adalah jika ingin jaminan privasi tetap berlaku, Anda harus mengikuti saran dalam codelab ini untuk tidak berlebihan dalam menggunakan lokasi dengan memiliki PrivacySpec terpisah untuk setiap agregasi atau menjalankan pipeline beberapa kali.

Untuk informasi Privasi Diferensial dan arti parameter privasi selengkapnya, Anda dapat melihat pustaka.

Parameter Utilitas

Ini adalah beberapa parameter yang tidak memengaruhi jaminan privasi (selama saran tentang cara menggunakan Privacy on Beam diikuti dengan benar), tetapi memengaruhi keakuratan, dan akibatnya, kegunaan output. Parameter ini disediakan dalam struktur Paramsdari setiap agregasi, misalnya CountParams, SumParams, dll. Parameter ini digunakan untuk menskalakan derau yang ditambahkan.

Parameter utilitas disediakan di Params dan berlaku untuk semua agregasi adalah MaxPartitionsContributed. Partisi sesuai dengan kunci PColection yang dihasilkan oleh operasi agregasi Privacy On Beam, yaitu Count, SumPerKey, dll. Jadi, MaxPartitionsContributed membatasi berapa banyak nilai kunci yang berbeda yang dapat dikontribusikan pengguna di output. Jika pengguna berkontribusi ke lebih dari MaxPartitionsContributed kunci dalam data pokok, beberapa kontribusinya akan dihapus sehingga pengguna akan memberikan kontribusi ke kunci MaxPartitionsContributed.

Serupa dengan MaxPartitionsContributed, sebagian besar agregasi memiliki parameter MaxContributionsPerPartition. Metrik tersebut disediakan di struktur Params dan setiap agregasi dapat memiliki nilai terpisah untuk struktur itu. Berbeda dengan MaxPartitionsContributed, MaxContributionsPerPartition membatasi kontribusi pengguna untuk setiap kunci. Dengan kata lain, pengguna hanya dapat memberikan kontribusi nilai MaxContributionsPerPartition untuk setiap kunci.

Derau yang ditambahkan ke output diskalakan oleh MaxPartitionsContributed dan MaxContributionsPerPartition sehingga ada konsekuensi di sini: MaxPartitionsContributed dan MaxContributionsPerPartition yang lebih besar berarti Anda menyimpan lebih banyak data, tetapi Anda akan mendapatkan hasil yang lebih berisik.

Beberapa agregasi memerlukan MinValue dan MaxValue. Ini akan menentukan batas kontribusi setiap pengguna. Jika pengguna memberikan kontribusi nilai yang lebih rendah dari MinValue, nilai tersebut akan ditetapkan menjadi MinValue. Demikian pula, jika pengguna memberikan kontribusi nilai yang lebih besar dari MaxValue, nilai tersebut akan dibulatkan ke MaxValue. Ini berarti untuk mempertahankan lebih banyak nilai asli, Anda harus menentukan batas yang lebih besar. Serupa dengan MaxPartitionsContributed dan MaxContributionsPerPartition, derau berarti diskalakan menurut ukuran batas sehingga batas yang lebih besar berarti Anda menyimpan lebih banyak data, tetapi Anda akan mendapatkan hasil yang lebih bising.

Parameter terakhir yang akan kita bicarakan adalah NoiseKind. Kami mendukung dua mekanisme derau yang berbeda dalam Privacy On Beam: GaussianNoise dan LaplaceNoise. Keduanya memiliki keunggulan dan kelemahan, tetapi distribusi Laplace memberikan utilitas yang lebih baik dengan batas kontribusi yang rendah. Oleh karena itu, Privacy On Beam menggunakannya secara default. Namun, jika ingin menggunakan derau distribusi Gaussian, Anda dapat menyediakan Params dengan variabel pbeam.GaussianNoise{}.

Bagus sekali! Anda telah menyelesaikan codelab Privacy on Beam. Anda telah mempelajari banyak hal tentang perbedaan privasi dan Privacy on Beam:

  • Mengubah PCollection menjadi PrivatePCollection dengan memanggil MakePrivateFromStruct.
  • Menggunakan Count untuk menghitung jumlah pribadi diferensial.
  • Menggunakan MeanPerKey untuk menghitung cara pribadi diferensial.
  • Menggunakan SumPerKey untuk menghitung jumlah pribadi diferensial.
  • Menghitung beberapa statistik dengan satu PrivacySpec dalam satu pipeline.
  • (Opsional) Menyesuaikan PrivacySpec dan parameter agregasi (CountParams, MeanParams, SumParams).

Namun, ada lebih banyak agregasi (misalnya kuantil, menghitung nilai yang berbeda) yang dapat Anda lakukan dengan Privacy on Beam! Anda dapat mempelajarinya lebih lanjut di repositori GitHub atau godoc.

Jika Anda memiliki waktu, berikan masukan tentang codelab dengan mengisi survei.