使用 Beam 的隱私功能計算私人統計資料

1. 簡介

您可能會認為匯總統計資料並未洩露任何統計資料的組成者資訊。不過,攻擊者可以透過多種方式,從匯總統計資料中取得資料集中個人的機密資訊。

為了保護個人並學到如何利用 Beam 中 Privacy 中差異化隱私的匯總方式,產生私人統計資料。Beam 的隱私權原則是與 Apache Beam 搭配使用的差異化隱私架構。

「私人」是什麼意思?

使用「私人」一詞時在本程式碼研究室中,我們都認為輸出內容的產生方式不會洩漏資料中個人的任何私人資訊。實現這項目標時,我們採用了差異化隱私技術,也就是去識別化的去識別化隱私權概念。去識別化是指匯總多位使用者的資料,以保護使用者隱私。所有去識別化方法都會採用匯總,但並非所有匯總方法都能進行去識別化。另一方面,差異化隱私能針對資訊外洩和隱私提供可評估的保證。

2. 差異化隱私總覽

為了進一步瞭解差異化隱私,讓我們看一個簡單的範例。

這張長條圖顯示某間小餐廳在特定傍晚的忙碌程度。許多房客於晚上 7 點入住,而餐廳在上午 1 點完全沒有空:

a43dbf3e2c6de596.png

這看起來很實用!

注意了。當新的邀請對象來到時,長條圖就會立即顯示這項資訊。看上圖,其中清楚有新訪客,而且這位邀請對像大約會在上午 1 點抵達:

bda96729e700a9dd.png

從隱私權的角度來看,這並不是件好事。經完全去識別化的統計資料不應透露個別貢獻。將這兩個圖表並排顯示,更顯而易見:橘色長條圖中,會有一位訪客在凌晨 1 點左右抵達:

d562ddf799288894.png

真是糟糕,我們該怎麼做?

我們會加入隨機雜訊,提升長條圖的準確度!

請見下方兩個長條圖。這項功能雖然不完全正確,但仍然很實用,且不會透露個別貢獻。太棒了!

838a0293cd4fcfe3.gif

差異化隱私機制會加入適量的隨機雜訊,來遮蓋個別貢獻

我們的分析結果略有簡化。要正確實作差異化隱私比較涉及許多層面,且在實作上有許多令人意想不到的細節。與密碼學類似,建立自己的差異化隱私實作方式可能不是個好主意。建議你使用 Privacy on Beam,不必導入自己的解決方案。別再自行套用差異化隱私!

在本程式碼研究室中,我們會示範如何使用 Privacy on Beam 執行差異化隱私分析。

3. 在 Beam 上下載隱私權

您不需要下載 Beam 中的「隱私權」應用程式,即可按照程式碼研究室的指示操作,因為您可以在本文件中找到所有相關程式碼和圖表。不過,如果您想下載程式碼以便使用程式碼,自行執行或稍後使用 Beam 的「隱私權」工具,則請依下列步驟操作。

請注意,本程式碼研究室適用於程式庫 1.1.0 版。

首先,請下載 Privacy on Beam:

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

您也可以複製 GitHub 存放區:

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

Beam 的隱私權資訊位於頂層 privacy-on-beam/ 目錄。

本程式碼研究室和資料集的程式碼位於 privacy-on-beam/codelab/ 目錄中。

另外,您也必須在電腦上安裝 Bazel。請前往 Bazel 網站,尋找適用您作業系統的安裝操作說明。

4. 計算每小時造訪次數

假設您是餐廳老闆,想要分享一些有關您餐廳的統計資料,像是公開熱門造訪時間等。幸好,您已瞭解差異化隱私和去識別化,所以切勿洩漏任何個別訪客的資訊。

本例的程式碼位於 codelab/count.go

讓我們先載入模擬資料集,其中包含餐廳特定週一的造訪記錄。此程式碼對於本程式碼研究室的用途並不感興趣,但您可以在 codelab/main.gocodelab/utils.gocodelab/visit.go 中查看程式碼。

訪客 ID

輸入的時間

花費時間 (分鐘)

消費金額 (歐元)

1

上午 9:30:00

26

24

2

上午 11:54:00

53

17

3

下午 1:05:00

81

33

請先在下方的程式碼範例中使用 Beam ,產生非私人的造訪餐廳時間長條圖。Scope 代表管道,而對資料執行的每個新作業都會加入 ScopeCountVisitsPerHour 會採用 Scope 和一組造訪集合,以 Beam 中的 PCollection 表示。並對集合套用 extractVisitHour 函式,擷取每次造訪的小時數。然後計算每小時的出現次數並傳回。

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

這在目前的目錄中產生良好的長條圖 (透過執行 bazel run codelab -- --example="count" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/count.csv --output_chart_file=$(pwd)/count.png),以 count.png 的形式產生:

a179766795d4e64a.png

下一步是將管道和長條圖轉換為私人圖表。方法如下:

首先,在 PCollection<V> 上呼叫 MakePrivateFromStruct 以取得 PrivatePCollection<V>。輸入 PCollection 必須是結構體的集合。我們需要輸入 PrivacySpecidFieldPath 做為 MakePrivateFromStruct 的輸入內容。

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

PrivacySpec 是結構體,用於存放要用來去識別化資料的不同差異化隱私參數 (epsilon 和 Delta)。(你暫時不需擔心,如需進一步瞭解這些做法,後續部分可視需要提供部分)。

idFieldPath 是結構中使用者 ID 欄位的路徑 (在本例中為 Visit)。在本例中,訪客的使用者 ID 是 VisitVisitorID 欄位。

然後,我們會呼叫 pbeam.Count() 而不是 stats.Count()pbeam.Count() 會做為輸入 CountParams 結構的結構,該結構會保留會影響輸出內容準確度的參數,例如 MaxValue

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

同樣地,MaxPartitionsContributed 也會界定使用者可貢獻的造訪時數。我們預期他們一天最多會來到餐廳一次 (或者我們不在乎一天當中會多次來店用餐),因此一併將其設為 1。我們將在自選部分中詳細介紹這些參數。

MaxValue 會限制單一使用者可為我們計算的值產生多少次。在特殊案例中,我們計算的值是造訪時數,且我們預期使用者只會光臨餐廳一次 (或者我們並不關心人每小時多次造訪餐廳),因此將這個參數設為 1。

最後,您的程式碼會如下所示:

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
}

我們可以針對差異化隱私統計資料查看類似的長條圖 (count_dp.png),也就是執行非私人管道和私人管道的資料:

d6a0ace1acd3c760.png

恭喜!您計算了第一項差異化隱私統計資料!

執行程式碼時看到的長條圖可能與這張圖表不同。沒關係,由於有差異化隱私的雜訊,您每次執行程式碼時都會得到不同的長條圖,但您會發現這些圖表與我們最初的非私密長條圖比較相似。

請注意,隱私權保證不會多次重新執行管道 (例如為了取得更精美的長條圖),請務必注意這一點。如要瞭解您不應重新執行管道的原因,請參閱「計算多個統計資料」專區。

5. 使用公開分區

在上一節中,您可能已經注意到,我們捨棄了某些分區 (例如小時) 的所有造訪 (資料)。

d7fbc5d86d91e54a.png

這是由於分區選取/門檻的緣故,為確保輸出分區存在時,依據使用者資料本身,可確保差異化隱私保證是相當重要的一步。出現這種情況時,輸出結果中只有分區存在,可能會導致資料中個別使用者的存在 (請參閱這篇網誌文章,瞭解此舉侵犯隱私權的原因)。為避免這種狀況,「Beam 隱私權模式」只會保留使用者人數充足的分區。

如果輸出分區清單不依賴使用者私人資料 (例如為公開資訊),就不需要選取分區選取步驟。這是我們餐廳的例子:我們知道餐廳的工作時間 (9.00 到 21.00)。

本例的程式碼位於 codelab/public_partitions.go

我們只要建立介於 9 到 21 之間的 PCollection (專屬),並將其輸入至 CountParamsPublicPartitions 欄位即可:

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
}

請注意,如果您使用公開分區和 Laplace Noise (預設),請將 Delta 設為 0,如上例所示。

使用 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 執行具有公開分區的管道時,會得到 (public_partitions_dp.png):

7c950fbe99fec60a.png

如您所見,現在我們保留了先前在沒有公開分區的情況下 9、10 和 16 的分區。

比起未支出任何隱私預算,使用公開分區來保留更多分區,且不會因為未支出任何隱私預算而使用公開分區,而增加約一半的雜訊。差異。因此,原始和不公開計數之間的差異略低於前次執行作業。

使用公開分區時,請牢記兩個要點:

  1. 從原始資料導出分區清單時,請務必謹慎:如果目標並非以差異隱私的方式遷移 (例如只是讀取使用者資料中的所有分區清單,您的管道就不會再提供差異化隱私保證。請參閱下面的進階章節,瞭解如何以差異保護隱私的方式完成此操作。
  2. 如果部分公開分區沒有資料 (例如造訪),就會套用至這些分區,以維護差異化隱私。舉例來說,如果使用 0 到 24 小時 (而不是 9 和 21) 的時間,「所有」營業時間就會受到干擾,而且可能不會顯示某些造訪的記錄。

(進階) 從資料導出分區

如果您在同一個管道中,以相同的非公開輸出分區清單執行多項匯總,則可以使用 SelectPartitions() 取得分區清單一次,並以 PublicPartition 輸入內容的形式向各項匯總提供分區。從隱私權的角度來看,這麼做不僅能確保資料安全無虞,還能讓整個管道對分區選取套用隱私預算,因此能減少雜訊。

6. 計算平均入住天數

我們已經瞭解如何以差異化隱私的方式計算數值,現在讓我們繼續評估計算方式。更具體來說,現在我們會計算訪客的平均停留時間。

本例的程式碼位於 codelab/mean.go

一般而言,如要計算入住天數的平均平均值,我們會使用 stats.MeanPerKey() 搭配預先處理步驟,將傳入的 PCollection 轉換為 PCollection<K,V>,其中 K 是造訪時間,V 是訪客用餐的時間。

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
}

這在目前的目錄中產生良好的長條圖 (透過執行 bazel run codelab -- --example="mean" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/mean.csv --output_chart_file=$(pwd)/mean.png),以 mean.png 的形式產生:

bc2df28bf94b3721.png

為了差異化隱私,我們會將 PCollection 再次轉換為 PrivatePCollection,並將 stats.MeanPerKey() 替換為 pbeam.MeanPerKey()。與 Count 類似,我們有 MeanParams 包含一些會影響準確率的參數,例如 MinValueMaxValueMinValueMaxValue 代表每位使用者對每個鍵的貢獻範圍。

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

在這種情況下,每個鍵都代表一小時,而值則是訪客停留的時間。我們不希望訪客在餐廳花不到 0 分鐘的時間,將 MinValue 設為 0。我們將 MaxValue 設為 60,也就是說,當訪客停留超過 60 分鐘時,我們就會視為該名使用者花了 60 分鐘。

最後,您的程式碼會如下所示:

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
}

我們可以針對差異化隱私統計資料查看類似的長條圖 (mean_dp.png),也就是執行非私人管道和私人管道的資料:

e8ac6a9bf9792287.png

和計數類似,因為這是差異化的私人作業,因此每次執行時都會得到不同的結果。不過,你可以看到差異化隱私的保留時間與實際結果相差甚遠。

7. 每小時計算收益

另一個值得注意的統計資料,就是一天中每小時的收益。

本例的程式碼位於 codelab/sum.go

讓我們先從非私人版本開始說明。對模擬資料集進行部分預先處理後,我們就能建立 PCollection<K,V>,其中 K 是造訪小時,V 是訪客在餐廳花費的金額:如要計算每小時的非私人收益,我們可以呼叫 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
}

這在目前的目錄中產生良好的長條圖 (透過執行 bazel run codelab -- --example="sum" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/sum.csv --output_chart_file=$(pwd)/sum.png),以 sum.png 的形式產生:

548619173fad0c9a.png

為了差異化隱私,我們會將 PCollection 再次轉換為 PrivatePCollection,並將 stats.SumPerKey() 替換為 pbeam.SumPerKey()。與 CountMeanPerKey 類似,我們 SumParams 包含一些會影響準確率的參數,例如 MinValueMaxValue

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

在本例中,MinValueMaxValue 代表我們針對每位訪客支出所設的金額上限。我們預期訪客餐廳中消費的金額不會低於 0 歐元,因此將 MinValue 設為 0。我們將 MaxValue 設為 40,也就是說,如果訪客的消費金額超過 40 歐元,我們就會假設該名使用者花了 40 歐元。

最後,程式碼應如下所示:

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
}

我們可以針對差異化隱私統計資料查看類似的長條圖 (sum_dp.png),也就是執行非私人管道和私人管道的資料:

46c375e874f3e7c4.png

和計數和平均值類似,這是差異化的私人作業,因此每次執行時都會得到不同的結果。不過,差異化的私人結果與每小時的實際收益非常接近。

8. 計算多項統計資料

在多數情況下,您可能會想針對相同的基礎資料計算多項統計資料,就像您的計數、平均值與總和一樣。如果是在單一 Beam 管道和單一二進位檔中執行這項操作,通常較為簡單明瞭。您也可以透過 Beam 的隱私保護計畫執行此操作。您可以編寫單一管道來執行轉換和運算作業,並針對整個管道使用單一 PrivacySpec

透過單一 PrivacySpec 執行此操作會更加方便,在隱私權方面也更為完善。如果您記得我們提供給 PrivacySpec 的 Epsilon 和 Delta 參數,這些參數就稱為「隱私預算」,是用來評估外洩基礎資料中使用者隱私的程度。

請記住,隱私設定預算是累加的:如果執行管道包含特定的 epsilon eek 和 delta Lesson 一次,會支出一遍 (截止 訓) 預算。如果再次執行這項作業,總預算將是 (2 約期 2 英寸)。同樣地,如果以 PrivacySpec 計算 (並連續計算隱私預算) 的多段統計資料,那麼總預算就是 2DISTANCE,2 分別為 2 點。這表示您將失去隱私保證的權益。

為規避這種情況,當您要針對相同的基礎資料計算多項統計資料時,必須使用單一 PrivacySpec 搭配要使用的總預算。接著,您必須指定每次匯總使用的 Epsilon 和差異值。最後,您會得到相同的整體隱私保證。但特定匯總的 Epsilon 和 Delta 越高,其準確率越高。

如要瞭解實際運作方式,我們可以先計算之前分別計算出的三個統計資料 (計數、平均值和總和),再在單一管道中計算。

本例的程式碼位於 codelab/multiple.go。請注意,Google 是將總預算 (已知) 除以三項匯總函式後得出的方式:

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. (選用) 調整差異化隱私參數

您在本程式碼研究室中已看過一些參數,例如 epsilon、delta、maxPartitionsContributed 等。我們可將其大致分為兩類:隱私權參數和公用程式參數。

隱私權參數

Epsilon 和 Delta 則是使用差異化隱私量化隱私權的參數。更確切地說,Epsilon 和 Delta 則是藉由查看去識別化的輸出內容,衡量潛在攻擊者從基礎資料中獲得多少資訊。Epsilon 和 Delta 越高,攻擊者越掌握基礎資料,就會有隱私風險。

另一方面,較低的 Epsilon 和差異值表示在輸出內容中需要加入的雜訊越多,而每個分區中所需的不重複使用者人數也越多,匿名輸出內容中必須有較多的不重複使用者人數。因此,您必須在實用性和隱私權之間取得取捨。

在「Privacy on Beam」中,當您在 PrivacySpec 中指定總隱私預算時,請務必擔心要匿名輸出的隱私權保證。要注意的是,如果您希望保有隱私保證,則必須針對每項匯總作業或多次執行管道,為每項匯總作業分別建立個別的 PrivacySpec,避免超支預算。

如要進一步瞭解差異化隱私和隱私權參數的意義,歡迎參閱相關文獻

公用程式參數

這些參數不會影響隱私權保證 (只要遵循以下建議,就能遵循「在 Beam 上保護隱私」的使用建議),但會影響準確率,以及後續輸出結果的實用性。這些匯總函式會在每個匯總的 Params 結構中提供,例如:CountParamsSumParams 等。這些參數用於調整加入的雜訊。

Params 中提供的公用程式參數為 MaxPartitionsContributed,適用於所有匯總作業。分區會對應至 Privacy On Beam 匯總作業所輸出 PCollection 的鍵,例如 CountSumPerKey 等。因此,MaxPartitionsContributed 會界定使用者可在輸出內容中貢獻多少個不同的鍵/值。如果使用者為基礎資料提供超過 MaxPartitionsContributed 個鍵,系統會捨棄部分使用者的部分貢獻,因此她會確實提供 MaxPartitionsContributed 個索引鍵。

MaxPartitionsContributed 類似,大多數匯總都有 MaxContributionsPerPartition 參數。這些結構是在 Params 結構中提供,而每個匯總函式都有其個別值。與 MaxPartitionsContributed 不同,MaxContributionsPerPartition 會限制使用者在每個鍵貢獻的內容。也就是說,使用者只能為每個鍵提供 MaxContributionsPerPartition 值。

新增至輸出內容的雜訊會根據 MaxPartitionsContributedMaxContributionsPerPartition 進行按比例調整,因此兩者的優缺點如下:如果 MaxPartitionsContributedMaxContributionsPerPartition 較大,您不但可以保留更多資料,但結果可能會比較雜訊。

部分匯總作業需要 MinValueMaxValue。這些會指定每位使用者貢獻內容的邊界。如果使用者提供的值低於 MinValue,則這個值會限制為 MinValue。同樣地,如果使用者提供的值大於 MaxValue,則該值會降至 MaxValue。換句話說,為了保留更多原始值,您必須指定較大的上下限。與 MaxPartitionsContributedMaxContributionsPerPartition 類似,雜訊會依照邊界大小縮放,因此邊界越大意味著您可以保留更多資料,但結果可能會比較雜訊。

最後要討論的參數是 NoiseKind。Privacy On Beam 支援兩種不同的雜訊機制:GaussianNoiseLaplaceNoise。兩者的優缺點都各有優缺點,但 Laplace 分配功能可讓效率低落地使用便利性,這就是為何 Privacy On Beam 預設使用此功能的原因。不過,如果您想使用高斯分佈雜訊,可以使用 pbeam.GaussianNoise{} 變數提供 Params

10. 摘要

做得好!您已完成「Beam 隱私權」程式碼研究室。您也瞭解了 Beam 上的差異化隱私和隱私保護:

  • 呼叫 MakePrivateFromStruct 即可將 PCollection 轉換為 PrivatePCollection
  • 使用 Count 計算差異化隱私計數。
  • 使用 MeanPerKey 計算差異化隱私的意義。
  • 使用 SumPerKey 計算差異化隱私總和。
  • 在單一管道中使用單一 PrivacySpec 運算多項統計資料。
  • (選用) 自訂 PrivacySpec 和匯總參數 (CountParams, MeanParams, SumParams)。

不過,只要使用 Privacy on Beam,還能採用更多匯總功能 (例如分位數、計算不同值)!詳情請參閱 GitHub 存放區 godoc

如果方便,請填寫問卷調查,提供你對程式碼研究室的意見。