Beam'de Gizlilik ile Özel İstatistikleri Hesaplama

1. Giriş

Toplu istatistiklerin, verileri istatistikleri oluşturan kişiler hakkında herhangi bir bilgi sızdırmadığını düşünebilirsiniz. Ancak saldırganlar, bir veri kümesindeki kişilerle ilgili hassas bilgileri toplu istatistiklerden öğrenebilir.

Bireylerin gizliliğini korumak için Privacy on Beam'den alınan diferansiyel gizliliğe sahip toplamalarla nasıl gizli istatistikler oluşturacağınızı öğreneceksiniz. Beam'deki gizlilik, Apache Beam ile çalışan bir diferansiyel gizlilik çerçevesidir.

"Gizli" derken neyi kastediyoruz?

Bu Codelab'de "gizli" kelimesi kullanıldığında, çıktının verilerdeki kişilerle ilgili gizli bilgileri sızdırmayacak şekilde üretildiği kastedilir. Bunu, anonimleştirme için güçlü bir gizlilik kavramı olan diferansiyel gizlilik kullanarak yapabiliriz. Anonimleştirme, kullanıcı gizliliğini korumak için verilerin birden fazla kullanıcı arasında toplandığı süreçtir. Tüm anonimleştirme yöntemlerinde toplama kullanılır ancak tüm toplama yöntemlerinde anonimleştirme sağlanmaz. Öte yandan, diferansiyel gizlilik, bilgi sızıntısı ve gizlilik konusunda ölçülebilir garantiler sunar.

2. Diferansiyel gizliliğe genel bakış

Diferansiyel gizliliği daha iyi anlamak için basit bir örneğe bakalım.

Bu çubuk grafik, belirli bir akşam küçük bir restoranın ne kadar yoğun olduğunu gösterir. Birçok misafir saat 19:00'da geliyor ve restoran saat 01:00'de tamamen boşalıyor:

a43dbf3e2c6de596.png

Bu faydalı görünüyor.

Ancak bir sorun var. Yeni bir misafir geldiğinde bu durum hemen çubuk grafikte gösterilir. Grafiğe bakın: Yeni bir konuğun geldiği ve bu konuğun yaklaşık olarak saat 01:00'de geldiği açıkça görülüyor:

bda96729e700a9dd.png

Bu durum, gizlilik açısından iyi değildir. Gerçekten anonimleştirilmiş istatistikler, bireysel katkıları ortaya çıkarmamalıdır. Bu iki grafiği yan yana koyduğumuzda durum daha da netleşiyor: Turuncu çubuk grafikte, yaklaşık 01:00'de gelen bir misafir daha var:

d562ddf799288894.png

Bu da pek iyi bir sonuç değil. Ne yapıyoruz?

Rastgele gürültü ekleyerek çubuk grafiklerin doğruluğunu biraz azaltacağız.

Aşağıdaki iki çubuk grafiğe bakın. Tamamen doğru olmasalar da yine de faydalıdırlar ve bireysel katkıları göstermezler. Güzel!

838a0293cd4fcfe3.gif

Diferansiyel gizlilik, bireysel katkıları maskelemek için doğru miktarda rastgele gürültü ekler.

Analizimiz biraz fazla basitleştirilmişti. Diferansiyel gizliliği doğru şekilde uygulamak daha karmaşıktır ve beklenmedik uygulama incelikleri içerir. Kriptografiye benzer şekilde, diferansiyel gizliliğin kendi uygulamanızı oluşturmak iyi bir fikir olmayabilir. Kendi çözümünüzü uygulamak yerine Beam'de Gizlilik'i kullanabilirsiniz. Kendi diferansiyel gizlilik çözümünüzü oluşturmayın.

Bu codelab'de, Beam'de Gizlilik'i kullanarak diferansiyel gizlilik analizi yapmayı göstereceğiz.

3. Beam'de Gizlilik'i indirme

İlgili tüm kod ve grafikler bu belgede bulunduğundan, kod laboratuvarını takip etmek için Beam'de Gizlilik'i indirmeniz gerekmez. Ancak kodu incelemek, kendiniz çalıştırmak veya Privacy on Beam'i daha sonra kullanmak için indirmek isterseniz aşağıdaki adımları uygulayarak bunu yapabilirsiniz.

Bu codelab'in kitaplığın 1.1.0 sürümü için olduğunu unutmayın.

Öncelikle Beam'de Gizlilik'i indirin:

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

Dilerseniz GitHub deposunu da klonlayabilirsiniz:

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

Beam'deki gizlilik, üst düzey privacy-on-beam/ dizinindedir.

Bu codelab'in kodu ve veri kümesi privacy-on-beam/codelab/ dizinindedir.

Ayrıca bilgisayarınızda Bazel'in yüklü olması gerekir. İşletim sisteminize yönelik yükleme talimatlarını Bazel web sitesinde bulabilirsiniz.

4. Saatlik ziyaret sayısını hesaplama

Bir restoran sahibi olduğunuzu ve restoranınızla ilgili bazı istatistikleri (ör. popüler ziyaret saatleri) paylaşmak istediğinizi düşünün. Neyse ki diferansiyel gizlilik ve anonimleştirme hakkında bilgi sahibisiniz. Bu nedenle, bu işlemi herhangi bir ziyaretçi hakkında bilgi sızdırmayacak şekilde yapmak istiyorsunuz.

Bu örneğin kodu codelab/count.go içinde yer almaktadır.

Belirli bir pazartesi günü restoranınıza yapılan ziyaretleri içeren örnek bir veri kümesi yükleyerek başlayalım. Bu codelab'in amaçları doğrultusunda bu kodun ilgi çekici olmadığı düşünülse de codelab/main.go, codelab/utils.go ve codelab/visit.go'deki kodu inceleyebilirsiniz.

Ziyaretçi Kimliği

Giriş zamanı

Harcanan süre (dakika)

Harcanan para (avro)

1

09:30:00

26

24

2

11:54:00

53

17

3

1:05:00 PM

81

33

Öncelikle aşağıdaki kod örneğinde Beam'i kullanarak restoranınıza yapılan ziyaretlerin zamanlarını gösteren gizli olmayan bir çubuk grafik oluşturacaksınız. Scope, işlem hattının bir gösterimidir ve veriler üzerinde yaptığımız her yeni işlem Scope'ya eklenir. CountVisitsPerHour, Scope ve bir ziyaret koleksiyonu alır. Bu koleksiyon, Beam'de PCollection olarak gösterilir. Koleksiyona extractVisitHour işlevini uygulayarak her ziyaretin saatini ayıklar. Ardından, her saatin kaç kez gerçekleştiğini sayar ve sonucu döndürür.

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

Bu işlem, geçerli dizinde bazel run codelab -- --example="count" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/count.csv --output_chart_file=$(pwd)/count.png çalıştırılarak count.png şeklinde güzel bir çubuk grafik oluşturur:

a179766795d4e64a.png

Bir sonraki adım, ardışık düzeninizi ve çubuk grafiğinizi özel hale getirmektir. Bu işlemi şu şekilde yaparız.

Öncelikle PCollection<V> cihazda MakePrivateFromStruct numarasını arayarak PrivatePCollection<V> alın. Giriş PCollection, struct'lardan oluşan bir koleksiyon olmalıdır. MakePrivateFromStruct için giriş olarak PrivacySpec ve idFieldPath değerlerini girmemiz gerekiyor.

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

PrivacySpec, verileri anonimleştirmek için kullanmak istediğimiz diferansiyel gizlilik parametrelerini (epsilon ve delta) içeren bir yapıdır. (Şimdilik bu konuda endişelenmenize gerek yok. Bu konular hakkında daha fazla bilgi edinmek isterseniz ileride isteğe bağlı bir bölümümüz var.)

idFieldPath, struct içindeki kullanıcı tanımlayıcı alanının yoludur (bizim durumumuzda Visit). Burada, ziyaretçilerin kullanıcı tanımlayıcısı Visit öğesinin VisitorID alanıdır.

Ardından, stats.Count() yerine pbeam.Count() işlevini çağırırız. pbeam.Count() işlevi, çıkışın doğruluğunu etkileyen MaxValue gibi parametreleri içeren bir CountParams yapısını giriş olarak alır.

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

Benzer şekilde, MaxPartitionsContributed, bir kullanıcının kaç farklı ziyaret saati ekleyebileceğini sınırlar. Bu kullanıcıların restoranı günde en fazla bir kez ziyaret etmesini bekliyoruz (veya gün içinde birden fazla kez ziyaret etmelerini önemsemiyoruz). Bu nedenle, bu değeri de 1 olarak ayarlıyoruz. Bu parametreleri isteğe bağlı bir bölümde daha ayrıntılı olarak ele alacağız.

MaxValue, tek bir kullanıcının saydığımız değerlere kaç kez katkıda bulunabileceğini sınırlar. Bu örnekte, saydığımız değerler ziyaret saatleridir ve kullanıcının restoranı yalnızca bir kez ziyaret etmesini bekliyoruz (veya saatte birden fazla ziyaret etmesini önemsemiyoruz). Bu nedenle bu parametreyi 1 olarak ayarlıyoruz.

Sonunda kodunuz aşağıdaki gibi görünecektir:

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
}

Diferansiyel gizlilik korunan istatistik için de benzer bir çubuk grafik (count_dp.png) görüyoruz (önceki komut hem gizlilik korumalı hem de gizlilik korumasız işlem hatlarını çalıştırır):

d6a0ace1acd3c760.png

Tebrikler! İlk diferansiyel gizlilik istatistiğinizi hesapladınız.

Kodu çalıştırdığınızda elde ettiğiniz çubuk grafik bu grafikten farklı olabilir. Sorun değil. Diferansiyel gizlilikteki gürültü nedeniyle kodu her çalıştırdığınızda farklı bir çubuk grafik elde edersiniz. Ancak bu grafiklerin, sahip olduğumuz orijinal ve gizli olmayan çubuk grafiğe az çok benzediğini görebilirsiniz.

Gizlilik garantileri için işlem hattının birden çok kez çalıştırılmaması (örneğin, daha iyi görünen bir çubuk grafik elde etmek için) çok önemlidir. Boru hatlarınızı neden yeniden çalıştırmamanız gerektiği "Birden Çok İstatistik Hesaplama" bölümünde açıklanmaktadır.

5. Herkese Açık Bölümleri Kullanma

Önceki bölümde, bazı bölümler (ör. saatler) için tüm ziyaretleri (verileri) bıraktığımızı fark etmiş olabilirsiniz.

d7fbc5d86d91e54a.png

Bunun nedeni, çıkış bölümlerinin varlığı kullanıcı verilerine bağlı olduğunda diferansiyel gizlilik garantilerini sağlamak için önemli bir adım olan bölüm seçimi/eşikleme işlemidir. Bu durumda, çıkışta bir bölümün bulunması, verilerdeki bir kullanıcının varlığını sızdırabilir (Bunun gizliliği neden ihlal ettiğine dair açıklama için bu blog yayınını inceleyin). Bunu önlemek için Beam'de Gizlilik yalnızca yeterli sayıda kullanıcı içeren bölümleri saklar.

Çıkış bölümleri listesi, özel kullanıcı verilerine bağlı olmadığında (ör. herkese açık bilgiler) bu bölüm seçimi adımına gerek yoktur. Restoran örneğimizde durum aslında böyledir: Restoranın çalışma saatlerini (09:00-21:00) biliyoruz.

Bu örneğin kodu codelab/public_partitions.go içinde yer almaktadır.

9 ile 21 (hariç) arasındaki saatlerden oluşan bir PCollection oluşturup bunu CountParams öğesinin PublicPartitions alanına gireceğiz:

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
}

Yukarıdaki örnekte olduğu gibi, herkese açık bölümler ve Laplace gürültüsü (varsayılan) kullanıyorsanız delta değerini 0 olarak ayarlayabileceğinizi unutmayın.

Ardışık düzeni herkese açık bölümlerle (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 ile) çalıştırdığımızda (public_partitions_dp.png) sonucunu alırız:

7c950fbe99fec60a.png

Gördüğünüz gibi, daha önce herkese açık bölümler olmadan bıraktığımız 9, 10 ve 16 numaralı bölümleri artık tutuyoruz.

Herkese açık bölümler kullanmak yalnızca daha fazla bölüm tutmanıza olanak tanımakla kalmaz, aynı zamanda bölüm seçimi için gizlilik bütçesi (epsilon ve delta) harcanmadığından herkese açık bölümler kullanmamaya kıyasla her bölüme yaklaşık olarak yarı yarıya daha az gürültü ekler. Bu nedenle, ham ve özel sayımlar arasındaki fark önceki çalıştırmaya kıyasla biraz daha azdır.

Herkese açık bölümleri kullanırken dikkat etmeniz gereken iki önemli nokta vardır:

  1. Bölüm listesini ham verilerden türetirken dikkatli olun: Bunu diferansiyel gizlilik sağlayacak şekilde yapmazsanız (ör. kullanıcı verilerindeki tüm bölümlerin listesini okursanız) işlem hattınız artık diferansiyel gizlilik garantisi vermez. Bunu farklı gizlilik yöntemleriyle nasıl yapacağınızı öğrenmek için aşağıdaki gelişmiş bölümüne bakın.
  2. Herkese açık bölümlerden bazılarında veri (ör. ziyaretler) yoksa diferansiyel gizliliği korumak için bu bölümlere gürültü uygulanır. Örneğin, 9 ile 21 arasındaki saatler yerine 0 ile 24 arasındaki saatleri kullansaydık saatlerin tümü gürültülü olurdu ve ziyaret olmamasına rağmen bazı ziyaretler gösterilebilirdi.

(Gelişmiş) Verilerden Bölümler Türetme

Aynı işlem hattında aynı genel olmayan çıkış bölümleri listesiyle birden fazla toplama işlemi çalıştırıyorsanız SelectPartitions() kullanarak bölümlerin listesini bir kez türetebilir ve bölümleri her toplama işlemine PublicPartition girişi olarak sağlayabilirsiniz. Bu yöntem gizlilik açısından güvenli olmasının yanı sıra, tüm işlem hattı için yalnızca bir kez bölüm seçimi sırasında gizlilik bütçesi kullanıldığından daha az gürültü eklemenize de olanak tanır.

6. Ortalama konaklama süresini hesaplama

Artık öğeleri nasıl farklı gizlilikle sayacağımızı bildiğimize göre, ortalamaları hesaplamaya bakalım. Daha spesifik olarak, artık ziyaretçilerin ortalama konaklama süresini hesaplayacağız.

Bu örneğin kodu codelab/mean.go içinde yer almaktadır.

Normalde, kalış sürelerinin özel olmayan ortalamasını hesaplamak için stats.MeanPerKey() kullanırız. Bu formülde, gelen ziyaretlerin PCollection değerini PCollection<K,V> değerine dönüştüren bir ön işleme adımı uygulanır. Burada K, ziyaret saati; V ise ziyaretçinin restoranda geçirdiği süredir.

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
}

Bu işlem, geçerli dizinde bazel run codelab -- --example="mean" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/mean.csv --output_chart_file=$(pwd)/mean.png çalıştırılarak mean.png şeklinde güzel bir çubuk grafik oluşturur:

bc2df28bf94b3721.png

Bunu farklı gizlilikte yapmak için PCollection değerimizi tekrar PrivatePCollection değerine dönüştürüyor ve stats.MeanPerKey() değerini pbeam.MeanPerKey() değeriyle değiştiriyoruz. Count'ya benzer şekilde, doğruluk oranını etkileyen MinValue ve MaxValue gibi bazı parametreleri içeren MeanParams'lerimiz var. MinValue ve MaxValue, her kullanıcının her anahtara katkısı için belirlediğimiz sınırları gösterir.

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

Bu durumda her anahtar bir saati, değerler ise ziyaretçilerin harcadığı süreyi temsil eder. Ziyaretçilerin restoranda 0 dakikadan daha az zaman geçirmesini beklemediğimiz için MinValue değerini 0 olarak ayarladık. MaxValue değerini 60 olarak ayarladık. Bu, bir ziyaretçi 60 dakikadan uzun süre harcarsa bu kullanıcının 60 dakika harcadığını varsayacağımız anlamına gelir.

Sonunda kodunuz aşağıdaki gibi görünecektir:

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
}

Diferansiyel gizlilik korunan istatistik için de benzer bir çubuk grafik (mean_dp.png) görüyoruz (önceki komut hem gizlilik korumalı hem de gizlilik korumasız işlem hatlarını çalıştırır):

e8ac6a9bf9792287.png

Yine, sayıya benzer şekilde, bu işlem farklı gizlilik sağlayan bir işlem olduğundan her çalıştırdığımızda farklı sonuçlar elde ederiz. Ancak, farklı gizlilik uygulanmış kalış sürelerinin gerçek sonuçtan çok uzak olmadığını görebilirsiniz.

7. Saatlik geliri hesaplama

İnceleyebileceğimiz bir diğer ilginç istatistik ise gün boyunca saatlik gelirdir.

Bu örneğin kodu codelab/sum.go içinde yer almaktadır.

Yine, gizli olmayan sürümle başlayacağız. Sahte veri setimizde önceden biraz işleme yaparak K'nın ziyaret saati, V'nin ise ziyaretçinin restoranda harcadığı para olduğu bir PCollection<K,V> oluşturabiliriz: Saatteki gizli olmayan geliri hesaplamak için ziyaretçilerin harcadığı tüm parayı stats.SumPerKey() çağırarak toplayabiliriz:

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
}

Bu işlem, geçerli dizinde bazel run codelab -- --example="sum" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/sum.csv --output_chart_file=$(pwd)/sum.png çalıştırılarak sum.png şeklinde güzel bir çubuk grafik oluşturur:

548619173fad0c9a.png

Bunu farklı gizlilikte yapmak için PCollection değerimizi tekrar PrivatePCollection değerine dönüştürüyor ve stats.SumPerKey() değerini pbeam.SumPerKey() değeriyle değiştiriyoruz. Count ve MeanPerKey'ye benzer şekilde, doğruluk oranını etkileyen MinValue ve MaxValue gibi bazı parametreleri içeren SumParams'lerimiz var.

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

Bu durumda, MinValue ve MaxValue, her ziyaretçinin harcadığı para için sahip olduğumuz sınırları temsil eder. Ziyaretçilerin restoranda 0 Euro'dan daha az harcama yapmasını beklemediğimiz için MinValue değerini 0 olarak ayarladık. MaxValue değerini 40 olarak ayarladık. Bu, bir ziyaretçi 40 Euro'dan fazla harcama yaparsa söz konusu kullanıcının 40 Euro harcadığını varsayacağımız anlamına gelir.

Sonunda kod aşağıdaki gibi görünecektir:

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
}

Diferansiyel gizlilik korunan istatistik için de benzer bir çubuk grafik (sum_dp.png) görüyoruz (önceki komut hem gizlilik korumalı hem de gizlilik korumasız işlem hatlarını çalıştırır):

46c375e874f3e7c4.png

Yine, sayma ve ortalamaya benzer şekilde, bu işlem farklı gizlilik koruması sağladığından her uyguladığımızda farklı sonuçlar elde ederiz. Ancak, farklı gizlilik uygulanmış sonucun saatlik gerçek gelirlere çok yakın olduğunu görebilirsiniz.

8. Birden fazla istatistik hesaplama

Çoğu zaman, sayı, ortalama ve toplam ile yaptığınıza benzer şekilde, aynı temel veriler üzerinde birden fazla istatistik hesaplamak isteyebilirsiniz. Bu işlemi genellikle tek bir Beam işlem hattında ve tek bir ikili dosyada yapmak daha temiz ve kolaydır. Bu işlemi, Beam'de Gizlilik özelliğiyle de yapabilirsiniz. Dönüşümlerinizi ve hesaplamalarınızı çalıştırmak için tek bir işlem hattı yazabilir ve tüm işlem hattı için tek bir PrivacySpec kullanabilirsiniz.

Bu işlemi tek bir PrivacySpec ile yapmak daha kolay olmasının yanı sıra gizlilik açısından da daha iyidir. PrivacySpec için sağladığımız epsilon ve delta parametrelerini hatırlarsanız bunlar, gizlilik bütçesi olarak adlandırılan bir şeyi temsil eder. Bu, temel verilerdeki kullanıcıların gizliliğinden ne kadar sızdırdığınızın bir ölçüsüdür.

Gizlilik bütçesiyle ilgili olarak unutulmaması gereken önemli bir nokta, bütçenin eklenerek artmasıdır: Belirli bir epsilon ε ve delta δ ile bir işlem hattını tek bir kez çalıştırırsanız (ε,δ) bütçesi harcarsınız. İkinci kez çalıştırırsanız toplam (2ε, 2δ) bütçe harcamış olursunuz. Benzer şekilde, (ε,δ) PrivacySpec (ve dolayısıyla gizlilik bütçesi) ile birden fazla istatistik hesaplarsanız toplamda (2ε, 2δ) bütçe harcamış olursunuz. Bu, gizlilik garantilerini düşürdüğünüz anlamına gelir.

Bunu önlemek için aynı temel veriler üzerinde birden fazla istatistik hesaplamak istediğinizde, kullanmak istediğiniz toplam bütçeyle tek bir PrivacySpec kullanmanız gerekir. Ardından, her toplama için kullanmak istediğiniz epsilon ve delta değerlerini belirtmeniz gerekir. Sonuç olarak, aynı genel gizlilik garantisine sahip olursunuz. Ancak belirli bir toplama işleminin epsilon ve delta değeri ne kadar yüksek olursa o kadar yüksek doğruluk elde edilir.

Bunu uygulamada görmek için daha önce ayrı ayrı hesapladığımız üç istatistiği (sayı, ortalama ve toplam) tek bir işlem hattında hesaplayabiliriz.

Bu örneğin kodu codelab/multiple.go içinde yer almaktadır. Toplam (ε,δ) bütçeyi üç toplama işlemi arasında eşit olarak nasıl böldüğümüze dikkat edin:

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. (İsteğe bağlı) Diferansiyel gizlilik parametrelerini ayarlama

Bu codelab'de epsilon, delta, maxPartitionsContributed gibi birkaç parametreden bahsedildi. Bunları kabaca iki kategoriye ayırabiliriz: Gizlilik Parametreleri ve Yardımcı Program Parametreleri.

Gizlilik Parametreleri

Epsilon ve delta, diferansiyel gizlilik kullanarak sağladığımız gizliliği ölçen parametrelerdir. Daha net bir ifadeyle, epsilon ve delta, anonimleştirilmiş çıkışa bakarak potansiyel bir saldırganın temel veriler hakkında ne kadar bilgi edindiğinin bir ölçüsüdür. Epsilon ve delta değerleri ne kadar yüksek olursa saldırgan, temel veriler hakkında o kadar fazla bilgi edinir. Bu da gizlilik riski oluşturur.

Öte yandan, epsilon ve delta değerleri ne kadar düşük olursa anonim olmak için çıkışa o kadar fazla gürültü eklemeniz gerekir. Ayrıca, bu bölümü anonimleştirilmiş çıkışta tutmak için her bölümde o kadar fazla benzersiz kullanıcıya sahip olmanız gerekir. Dolayısıyla, burada fayda ve gizlilik arasında bir denge söz konusudur.

Beam'de gizlilikle ilgili olarak, PrivacySpec içinde toplam gizlilik bütçesini belirtirken anonimleştirilmiş çıktınızda istediğiniz gizlilik garantileri konusunda endişelenmeniz gerekir. Ancak gizlilik garantilerinizin geçerli olmasını istiyorsanız bu codelab'deki, her toplama için ayrı bir PrivacySpec oluşturarak veya işlem hattını birden çok kez çalıştırarak bütçenizi aşırı kullanmama konusundaki tavsiyelere uymanız gerekir.

Diferansiyel gizlilik ve gizlilik parametrelerinin anlamı hakkında daha fazla bilgi için literatüre göz atabilirsiniz.

Yardımcı Program Parametreleri

Bunlar, Beam'de Gizlilik'in nasıl kullanılacağıyla ilgili tavsiyelere düzgün bir şekilde uyulduğu sürece gizlilik garantilerini etkilemeyen ancak doğruluğu ve dolayısıyla çıktının faydasını etkileyen parametrelerdir. Bu parametreler, her toplama işleminin Params yapılarında (ör.CountParams, SumParams vb.) sağlanır. Bu parametreler, eklenen gürültüyü ölçeklendirmek için kullanılır.

Params içinde sağlanan ve tüm toplamalar için geçerli olan bir yardımcı program parametresi MaxPartitionsContributed'dir. Bölüm, Privacy On Beam toplama işlemi tarafından oluşturulan PCollection'ın bir anahtarına (ör. Count, SumPerKey) karşılık gelir. Bu nedenle, MaxPartitionsContributed, bir kullanıcının çıkışa kaç farklı anahtar değeriyle katkıda bulunabileceğini sınırlar. Bir kullanıcı, temel verilerdeki MaxPartitionsContributed anahtardan fazlasına katkıda bulunursa tam olarak MaxPartitionsContributed anahtara katkıda bulunması için bazı katkıları bırakılır.

MaxPartitionsContributed ile benzer şekilde, çoğu toplama işleminde MaxContributionsPerPartition parametresi bulunur. Bu değerler Params yapılarında sağlanır ve her toplama için ayrı değerler olabilir. MaxPartitionsContributed seçeneğinin aksine, MaxContributionsPerPartition her anahtar için kullanıcının katkısını sınırlar. Diğer bir deyişle, bir kullanıcı her anahtar için yalnızca MaxContributionsPerPartition değerine katkıda bulunabilir.

Çıkışa eklenen gürültü, MaxPartitionsContributed ve MaxContributionsPerPartition ile ölçeklendirilir. Bu nedenle, burada bir denge söz konusudur: Daha büyük MaxPartitionsContributed ve MaxContributionsPerPartition değerleri, daha fazla veri tuttuğunuz anlamına gelir ancak sonuç daha gürültülü olur.

Bazı toplama işlemleri için MinValue ve MaxValue gerekir. Bunlar, her kullanıcının katkılarının sınırlarını belirtir. Bir kullanıcı MinValue değerinden daha düşük bir değer girerse bu değer MinValue'ya sabitlenir. Benzer şekilde, bir kullanıcı MaxValue değerinden büyük bir değer girerse bu değer MaxValue olarak sınırlandırılır. Bu nedenle, orijinal değerlerin daha fazlasını korumak için daha büyük sınırlar belirtmeniz gerekir. MaxPartitionsContributed ve MaxContributionsPerPartition'ye benzer şekilde, gürültü de sınırların boyutuna göre ölçeklendirilir. Bu nedenle, daha büyük sınırlar daha fazla veri tutmanızı sağlar ancak daha gürültülü bir sonuç elde edersiniz.

Bahsedeceğimiz son parametre NoiseKind. Privacy On Beam'de iki farklı gürültü mekanizması desteklenir: GaussianNoise ve LaplaceNoise. Her ikisinin de avantajları ve dezavantajları vardır ancak Laplace dağılımı, düşük katkı sınırlarıyla daha iyi bir fayda sağlar. Bu nedenle Privacy On Beam, varsayılan olarak bu dağılımı kullanır. Ancak Gauss dağılımı gürültüsü kullanmak isterseniz Params değişkenini pbeam.GaussianNoise{} ile birlikte sağlayabilirsiniz.

10. Özet

Tebrikler! Beam'de Gizlilik codelab'ini tamamlamış olmanız gerekir. Diferansiyel gizlilik ve Beam'de gizlilik hakkında çok şey öğrendiniz:

  • MakePrivateFromStruct numarasını arayarak PCollection cihazınızı PrivatePCollection cihazına dönüştürme
  • Farklı gizlilikteki sayıları hesaplamak için Count kullanma.
  • Farklı gizlilik içeren ortalamaları hesaplamak için MeanPerKey kullanma.
  • Farklı gizlilikteki toplamları hesaplamak için SumPerKey kullanma.
  • Tek bir PrivacySpec ile tek bir işlem hattında birden fazla istatistik hesaplama.
  • (İsteğe bağlı) PrivacySpec ve toplama parametrelerini (CountParams, MeanParams, SumParams) özelleştirme.

Ancak Beam'de gizlilikle yapabileceğiniz çok daha fazla toplama işlemi (ör.yüzdelikler, farklı değerleri sayma) vardır. Bunlar hakkında daha fazla bilgiyi GitHub deposunda veya godoc'ta bulabilirsiniz.

Zamanınız varsa lütfen anketi doldurarak codelab hakkında geri bildirimde bulunun.