बीम पर निजता के ज़रिए निजी आंकड़ों का हिसाब लगाना

1. परिचय

आपको लग सकता है कि कुल आंकड़ों से, उन लोगों के बारे में कोई जानकारी ज़ाहिर नहीं होती है जिनका डेटा इन आंकड़ों में शामिल है. हालांकि, हमलावर कई तरीकों से एग्रीगेट किए गए आंकड़ों से, डेटासेट में मौजूद लोगों की संवेदनशील जानकारी का पता लगा सकता है.

लोगों की निजता की सुरक्षा के लिए, आपको यह सिखाया जाएगा कि Privacy on Beam से डिफ़रेंशियल प्राइवसी वाले एग्रीगेशन का इस्तेमाल करके, निजी आंकड़े कैसे जनरेट किए जाते हैं. Privacy on Beam, डिफ़रेंशियल प्राइवसी फ़्रेमवर्क है. यह Apache Beam के साथ काम करता है.

"निजी" से हमारा क्या मतलब है?

इस कोडलैब में ‘निजी' शब्द का इस्तेमाल करने का मतलब है कि आउटपुट इस तरह से जनरेट किया जाता है कि डेटा में मौजूद लोगों की कोई भी निजी जानकारी लीक न हो. हम डिफ़रेंशियल प्राइवसी का इस्तेमाल करके ऐसा कर सकते हैं. यह पहचान छिपाने के लिए, निजता से जुड़ा एक मज़बूत सिद्धांत है. पहचान छिपाना, कई उपयोगकर्ताओं के डेटा को एग्रीगेट करने की प्रोसेस है. इससे उपयोगकर्ता की निजता को सुरक्षित रखा जाता है. पहचान छिपाने के सभी तरीकों में एग्रीगेशन का इस्तेमाल किया जाता है. हालांकि, एग्रीगेशन के सभी तरीकों से पहचान नहीं छिपाई जा सकती. दूसरी ओर, डिफ़रेंशियल प्राइवसी, जानकारी के लीक होने और निजता के बारे में मेज़र की जा सकने वाली गारंटी देती है.

2. डिफ़रेंशियल प्राइवसी की खास जानकारी

डिफ़रेंशियल प्राइवसी को बेहतर तरीके से समझने के लिए, आइए एक आसान उदाहरण देखें.

इस बार चार्ट में, किसी शाम को एक छोटे रेस्टोरेंट में मौजूद लोगों की संख्या दिखाई गई है. ज़्यादातर मेहमान शाम 7 बजे आते हैं और रेस्टोरेंट रात 1 बजे पूरी तरह से खाली हो जाता है:

a43dbf3e2c6de596.png

यह काम का लग रहा है!

हालांकि, इसमें एक समस्या है. नया मेहमान आने पर, बार चार्ट से तुरंत पता चल जाता है. चार्ट में देखें: इससे पता चलता है कि कोई नया मेहमान आया है और वह करीब 1 बजे आया है:

bda96729e700a9dd.png

निजता के लिहाज़ से यह सही नहीं है. पहचान छिपाकर तैयार किए गए आंकड़ों से, किसी व्यक्ति के योगदान का पता नहीं चलना चाहिए. इन दोनों चार्ट को एक साथ रखने से, यह और भी ज़्यादा साफ़ तौर पर पता चलता है: ऑरेंज बार चार्ट में एक अतिरिक्त मेहमान है, जो सुबह 1 बजे पहुंचा:

d562ddf799288894.png

यह भी सही नहीं है. हम क्या करें?

हम बार चार्ट में कुछ रैंडम नॉइज़ जोड़कर, उन्हें थोड़ा कम सटीक बना देंगे!

यहां दिए गए दो बार चार्ट देखें. ये नतीजे पूरी तरह सटीक नहीं होते, लेकिन फिर भी काम के होते हैं. साथ ही, इनसे किसी व्यक्ति के योगदान का पता नहीं चलता. बढ़िया!

838a0293cd4fcfe3.gif

डिफ़रेंशियल प्राइवसी की सुविधा, हर व्यक्ति के योगदान को छिपाने के लिए सही मात्रा में रैंडम नॉइज़ जोड़ रही है.

हमने अपने विश्लेषण को कुछ ज़्यादा ही आसान बना दिया था. डिफ़रेंशियल प्राइवसी को सही तरीके से लागू करना ज़्यादा मुश्किल है. साथ ही, इसे लागू करने के कई ऐसे पहलू हैं जिनके बारे में पहले से अनुमान नहीं लगाया जा सकता. क्रिप्टोग्राफ़ी की तरह, डिफ़रेंशियल प्राइवसी को खुद से लागू करना सही नहीं है. खुद का समाधान लागू करने के बजाय, Beam पर निजता सेटिंग का इस्तेमाल किया जा सकता है. डिफ़रेंशियल प्राइवसी को खुद से लागू न करें!

इस कोडलैब में, हम Privacy on Beam का इस्तेमाल करके, अंतर के साथ निजी विश्लेषण करने का तरीका बताएंगे.

3. Beam पर निजता की जानकारी डाउनलोड करना

कोड लैब को फ़ॉलो करने के लिए, आपको Privacy on Beam को डाउनलोड करने की ज़रूरत नहीं है. ऐसा इसलिए, क्योंकि इससे जुड़ा सारा कोड और ग्राफ़ इस दस्तावेज़ में मौजूद है. हालांकि, अगर आपको कोड के साथ खेलने, उसे खुद चलाने या बाद में Privacy on Beam का इस्तेमाल करने के लिए डाउनलोड करना है, तो नीचे दिया गया तरीका अपनाएं.

ध्यान दें कि यह कोडलैब, लाइब्रेरी के 1.1.0 वर्शन के लिए है.

सबसे पहले, 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.go, codelab/utils.go, और codelab/visit.go में जाकर, इसका कोड देखा जा सकता है.

विज़िटर आईडी

समय डाला गया

बिताया गया समय (मिनट)

खर्च की गई रकम (यूरो)

1

सुबह 9:30:00 बजे

26

24

2

सुबह 11:54:00 बजे

53

17

3

दोपहर 1:05:00

81

33

नीचे दिए गए कोड सैंपल में, Beam का इस्तेमाल करके अपने रेस्टोरेंट में आने के समय का नॉन-प्राइवेट बार चार्ट बनाएं. Scope, पाइपलाइन को दिखाता है. साथ ही, डेटा पर किए जाने वाले हर नए ऑपरेशन को Scope में जोड़ दिया जाता है. CountVisitsPerHour, 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()
}

इससे मौजूदा डायरेक्ट्री में count.png के तौर पर एक अच्छा बार चार्ट (bazel run codelab -- --example="count" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/count.csv --output_chart_file=$(pwd)/count.png चलाकर) तैयार होता है:

a179766795d4e64a.png

अगला चरण, अपनी पाइपलाइन और बार चार्ट को निजी में बदलना है. हम ऐसा इन तरीकों से करते हैं.

सबसे पहले, PCollection<V> पर MakePrivateFromStruct को कॉल करके PrivatePCollection<V> पाएं. इनपुट PCollection, स्ट्रक्चर का कलेक्शन होना चाहिए. हमें MakePrivateFromStruct में इनपुट के तौर पर PrivacySpec और idFieldPath डालना होगा.

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

PrivacySpec एक स्ट्रक्ट है. इसमें डिफ़रेंशियल प्राइवसी के पैरामीटर (एपसिलॉन और डेल्टा) होते हैं. इनका इस्तेमाल हम डेटा को पहचान ज़ाहिर किए बिना इकट्ठा करने के लिए करते हैं. (फ़िलहाल, आपको इनके बारे में चिंता करने की ज़रूरत नहीं है. अगर आपको इनके बारे में ज़्यादा जानना है, तो बाद में हमारे पास एक वैकल्पिक सेक्शन है.)

idFieldPath, स्ट्रक्चर (हमारे मामले में Visit) में मौजूद उपयोगकर्ता आइडेंटिफ़ायर फ़ील्ड का पाथ है. यहां, वेबसाइट पर आने वाले लोगों का यूज़र आइडेंटिफ़ायर, Visit का VisitorID फ़ील्ड है.

इसके बाद, हम stats.Count() के बजाय pbeam.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

ऐसा पार्टीशन चुनने/थ्रेशोल्डिंग की वजह से होता है. यह एक ज़रूरी चरण है. इससे यह पक्का किया जाता है कि आउटपुट पार्टीशन का वजूद, उपयोगकर्ता के डेटा पर निर्भर होने पर भी अंतर वाली निजता की गारंटी दी जा सके. ऐसा होने पर, आउटपुट में सिर्फ़ एक पार्टीशन मौजूद होने से, डेटा में किसी व्यक्ति की मौजूदगी का पता चल सकता है. ऐसा क्यों होता है, यह जानने के लिए यह ब्लॉग पोस्ट पढ़ें. इससे बचने के लिए, Privacy on Beam सिर्फ़ उन पार्टीशन को सेव करता है जिनमें उपयोगकर्ताओं की संख्या काफ़ी ज़्यादा होती है.

जब आउटपुट पार्टीशन की सूची, उपयोगकर्ता के निजी डेटा पर निर्भर नहीं करती है, यानी कि वे सार्वजनिक जानकारी होती हैं, तो हमें पार्टीशन चुनने के इस चरण की ज़रूरत नहीं होती. हमारे रेस्टोरेंट के उदाहरण में भी ऐसा ही है: हमें रेस्टोरेंट के खुलने और बंद होने का समय पता है (सुबह 9 बजे से रात 9 बजे तक).

इस उदाहरण का कोड codelab/public_partitions.go में है.

हम 9 से 21 (को छोड़कर) के बीच के घंटों का एक PCollection बनाएंगे और इसे CountParams के PublicPartitions फ़ील्ड में डालेंगे:

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
}

ध्यान दें कि अगर सार्वजनिक पार्टीशन और लैप्लस नॉइज़ (डिफ़ॉल्ट) का इस्तेमाल किया जा रहा है, तो डेल्टा को 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. अगर कुछ सार्वजनिक पार्टीशन के लिए कोई डेटा (जैसे कि विज़िट) नहीं है, तो डिफ़रेंशियल प्राइवसी बनाए रखने के लिए, उन पार्टीशन पर नॉइज़ लागू किया जाएगा. उदाहरण के लिए, अगर हमने 9 और 21 के बजाय 0 और 24 के बीच के घंटों का इस्तेमाल किया, तो सभी घंटों में नॉइज़ जुड़ जाएगी. साथ ही, हो सकता है कि जब कोई विज़िट न हो, तब भी कुछ विज़िट दिखें.

(ऐडवांस) डेटा से पार्टिशन बनाना

अगर आपको एक ही पाइपलाइन में, नॉन-पब्लिक आउटपुट पार्टीशन की एक ही सूची के साथ कई एग्रीगेशन चलाने हैं, तो 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
}

इससे मौजूदा डायरेक्ट्री में mean.png के तौर पर एक अच्छा बार चार्ट (bazel run codelab -- --example="mean" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/mean.csv --output_chart_file=$(pwd)/mean.png चलाकर) तैयार होता है:

bc2df28bf94b3721.png

इसे डिफ़रेंशियल प्राइवेट बनाने के लिए, हम अपने PCollection को फिर से PrivatePCollection में बदलते हैं और stats.MeanPerKey() को pbeam.MeanPerKey() से बदल देते हैं. Count की तरह, हमारे पास MeanParams भी है. इसमें MinValue और MaxValue जैसे कुछ पैरामीटर होते हैं, जिनसे अनुमान के सटीक होने पर असर पड़ता है. MinValue और MaxValue, हर उपयोगकर्ता के योगदान की सीमाएं दिखाते हैं. ये सीमाएं, हर कुंजी के लिए तय की जाती हैं.

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

इस मामले में, हर कुंजी एक घंटे को दिखाती है और वैल्यू, वेबसाइट पर विज़िटर के बिताए गए समय को दिखाती हैं. हमने MinValue को 0 पर सेट किया है, क्योंकि हमें नहीं लगता कि लोग रेस्टोरेंट में 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
}

इससे मौजूदा डायरेक्ट्री में sum.png के तौर पर एक अच्छा बार चार्ट (bazel run codelab -- --example="sum" --input_file=$(pwd)/day_data.csv --output_stats_file=$(pwd)/sum.csv --output_chart_file=$(pwd)/sum.png चलाकर) तैयार होता है:

548619173fad0c9a.png

इसे डिफ़रेंशियल प्राइवेट बनाने के लिए, हम अपने PCollection को फिर से PrivatePCollection में बदलते हैं और stats.SumPerKey() को pbeam.SumPerKey() से बदल देते हैं. Count और MeanPerKey की तरह, हमारे पास SumParams भी है. इसमें कुछ पैरामीटर होते हैं, जैसे कि MinValue और MaxValue. इनसे सटीक नतीजे मिलने पर असर पड़ता है.

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

इस मामले में, MinValue और MaxValue से पता चलता है कि हर विज़िटर कितना खर्च करता है. हमने MinValue को 0 पर सेट किया है, क्योंकि हमें उम्मीद नहीं है कि रेस्टोरेंट में आने वाले लोग 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. एक से ज़्यादा आंकड़ों का हिसाब लगाना

ज़्यादातर मामलों में, आपको एक ही डेटा के आधार पर कई तरह के आंकड़े कैलकुलेट करने होते हैं. जैसे, आपने गिनती, औसत, और योग के लिए किया था. आम तौर पर, इसे एक ही बीम पाइपलाइन और एक ही बाइनरी में करना ज़्यादा आसान और बेहतर होता है. Privacy on Beam का इस्तेमाल करके भी ऐसा किया जा सकता है. ट्रांसफ़ॉर्मेशन और कंप्यूटेशन को चलाने के लिए, एक ही पाइपलाइन लिखी जा सकती है. साथ ही, पूरी पाइपलाइन के लिए एक ही PrivacySpec का इस्तेमाल किया जा सकता है.

एक ही PrivacySpec का इस्तेमाल करना न सिर्फ़ ज़्यादा सुविधाजनक है, बल्कि निजता के लिहाज़ से भी बेहतर है. अगर आपको PrivacySpec को दिए जाने वाले इप्सिलॉन और डेल्टा पैरामीटर याद हैं, तो वे निजता बजट को दिखाते हैं. इससे यह पता चलता है कि मौजूदा डेटा में मौजूद उपयोगकर्ताओं की निजता से जुड़ी कितनी जानकारी लीक हो रही है.

निजता बजट के बारे में यह ध्यान रखना ज़रूरी है कि यह जुड़ता जाता है: अगर किसी पाइपलाइन को एक बार किसी खास इप्सिलॉन ε और डेल्टा δ के साथ चलाया जाता है, तो (ε,δ) बजट खर्च होता है. इसे दूसरी बार चलाने पर, आपका कुल बजट (2ε, 2δ) हो जाएगा. इसी तरह, अगर (ε,δ) के PrivacySpec (और लगातार निजता बजट) के साथ कई आंकड़ों का हिसाब लगाया जाता है, तो आपने कुल (2ε, 2δ) बजट खर्च किया होगा. इसका मतलब है कि निजता से जुड़े भरोसे को कम किया जा रहा है.

इससे बचने के लिए, जब आपको एक ही डेटा पर कई आंकड़े कैलकुलेट करने हों, तो आपको एक ही PrivacySpec का इस्तेमाल करना चाहिए. साथ ही, आपको उस PrivacySpec में वह कुल बजट डालना चाहिए जिसका आपको इस्तेमाल करना है. इसके बाद, आपको हर एग्रीगेशन के लिए, इस्तेमाल किए जाने वाले इप्सिलॉन और डेल्टा की वैल्यू तय करनी होगी. आखिर में, आपको निजता की सुरक्षा की गारंटी मिलती है. हालांकि, किसी एग्रीगेशन के लिए इप्सिलॉन और डेल्टा जितना ज़्यादा होगा, वह उतना ही सटीक होगा.

इसे काम करते हुए देखने के लिए, हम उन तीन आंकड़ों (गिनती, औसत, और योग) का हिसाब लगा सकते हैं जिन्हें हमने पहले अलग-अलग पाइपलाइन में कैलकुलेट किया था.

इस उदाहरण का कोड codelab/multiple.go में है. ध्यान दें कि हम कुल (ε,δ) बजट को तीन एग्रीगेशन के बीच बराबर बांट रहे हैं:

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. (ज़रूरी नहीं) डिफ़रेंशियल प्राइवसी पैरामीटर में बदलाव करना

आपने इस कोडलैब में कई पैरामीटर देखे हैं: इप्सिलॉन, डेल्टा, maxPartitionsContributed वगैरह. हम इन्हें दो कैटगरी में बांट सकते हैं: निजता पैरामीटर और यूटिलिटी पैरामीटर.

निजता पैरामीटर

डिफ़रेंशियल प्राइवसी का इस्तेमाल करके, हम निजता से जुड़े जिन पैरामीटर को मेज़र करते हैं वे इप्सिलॉन और डेल्टा हैं. ज़्यादा सटीक तरीके से कहें, तो ऐप्सीलॉन और डेल्टा, इस बात का मेज़रमेंट करते हैं कि पहचान छिपाए गए आउटपुट को देखकर, संभावित हमलावर को दिए गए डेटा के बारे में कितनी जानकारी मिलती है. एप्सिलॉन और डेल्टा की वैल्यू जितनी ज़्यादा होगी, हमलावर को डेटा के बारे में उतनी ही ज़्यादा जानकारी मिलेगी. इससे निजता को खतरा होता है.

दूसरी ओर, इप्सिलॉन और डेल्टा की वैल्यू जितनी कम होगी, पहचान छिपाने के लिए आउटपुट में उतना ही ज़्यादा नॉइज़ जोड़ना होगा. साथ ही, पहचान छिपाए गए आउटपुट में उस पार्टीशन को बनाए रखने के लिए, आपको हर पार्टीशन में यूनीक उपयोगकर्ताओं की संख्या ज़्यादा रखनी होगी. इसलिए, यहां निजता और काम की जानकारी के बीच समझौता किया जाता है.

Beam पर निजता बनाए रखने के लिए, आपको इस बात का ध्यान रखना होगा कि आपने PrivacySpec में निजता का कुल बजट तय करते समय, निजता से जुड़ी किन बातों का ध्यान रखा है. हालांकि, अगर आपको निजता से जुड़े अपने वादों को पूरा करना है, तो आपको इस कोडलैब में दी गई सलाह का पालन करना होगा. इसमें बताया गया है कि हर एग्रीगेशन के लिए अलग PrivacySpec का इस्तेमाल करके या पाइपलाइन को कई बार चलाकर, अपने बजट का ज़्यादा इस्तेमाल न करें.

डिफ़रेंशियल प्राइवसी और निजता के पैरामीटर का क्या मतलब है, इस बारे में ज़्यादा जानने के लिए साहित्य पढ़ें.

उपयोगिता पैरामीटर

ये ऐसे पैरामीटर हैं जिनसे निजता की गारंटी पर कोई असर नहीं पड़ता. हालांकि, Beam पर निजता का इस्तेमाल करने के बारे में दिए गए सुझावों का सही तरीके से पालन करना ज़रूरी है. इनसे जवाब की सटीकता पर असर पड़ता है. इसलिए, इनसे आउटपुट के काम का होने पर भी असर पड़ता है. ये हर एग्रीगेशन के Params स्ट्रक्चर में दिए जाते हैं. जैसे, CountParams, SumParams वगैरह. इन पैरामीटर का इस्तेमाल, जोड़े जा रहे नॉइज़ को स्केल करने के लिए किया जाता है.

Params में दिया गया एक यूटिलिटी पैरामीटर, MaxPartitionsContributed है. यह सभी एग्रीगेशन पर लागू होता है. पार्टिशन, Privacy On Beam एग्रीगेशन ऑपरेशन से आउटपुट किए गए PCollection की कुंजी से मेल खाता है. जैसे, Count, SumPerKey वगैरह. इसलिए, MaxPartitionsContributed से यह तय होता है कि कोई उपयोगकर्ता, आउटपुट में कितनी अलग-अलग कुंजी वैल्यू दे सकता है. अगर कोई उपयोगकर्ता, डेटा में मौजूद MaxPartitionsContributed से ज़्यादा कुंजियों में योगदान देता है, तो उसके कुछ योगदान हटा दिए जाएंगे, ताकि वह सिर्फ़ MaxPartitionsContributed कुंजियों में योगदान दे सके.

MaxPartitionsContributed की तरह ही, ज़्यादातर एग्रीगेशन में MaxContributionsPerPartition पैरामीटर होता है. ये Params स्ट्रक्चर में दिए जाते हैं. साथ ही, हर एग्रीगेशन के लिए इनकी वैल्यू अलग-अलग हो सकती हैं. MaxPartitionsContributed के उलट, MaxContributionsPerPartition हर कुंजी के लिए उपयोगकर्ता के योगदान को सीमित करता है. दूसरे शब्दों में कहें, तो कोई उपयोगकर्ता हर बटन के लिए सिर्फ़ MaxContributionsPerPartition वैल्यू जोड़ सकता है.

आउटपुट में जोड़े गए नॉइज़ को MaxPartitionsContributed और MaxContributionsPerPartition के हिसाब से स्केल किया जाता है. इसलिए, यहां एक ट्रेडऑफ़ है: MaxPartitionsContributed और MaxContributionsPerPartition, दोनों का मतलब है कि आपके पास ज़्यादा डेटा है. हालांकि, इससे आपको ज़्यादा नॉइज़ वाला नतीजा मिलेगा.

कुछ एग्रीगेशन के लिए, MinValue और MaxValue की ज़रूरत होती है. इनसे हर उपयोगकर्ता के योगदान की सीमाएं तय होती हैं. अगर कोई उपयोगकर्ता MinValue से कम वैल्यू सबमिट करता है, तो उस वैल्यू को MinValue तक बढ़ा दिया जाएगा. इसी तरह, अगर कोई उपयोगकर्ता MaxValue से ज़्यादा वैल्यू का योगदान करता है, तो उस वैल्यू को MaxValue पर सेट कर दिया जाएगा. इसका मतलब है कि ज़्यादा ओरिजनल वैल्यू बनाए रखने के लिए, आपको बड़ी सीमाएं तय करनी होंगी. MaxPartitionsContributed और MaxContributionsPerPartition की तरह ही, नॉइज़ को बाउंड्री के साइज़ के हिसाब से स्केल किया जाता है. इसलिए, बाउंड्री का साइज़ जितना बड़ा होगा, उतना ही ज़्यादा डेटा आपके पास रहेगा. हालांकि, इससे आपको ज़्यादा नॉइज़ वाला नतीजा मिलेगा.

हम जिस आखिरी पैरामीटर के बारे में बात करेंगे वह NoiseKind है. Privacy On Beam में, हम दो तरह के नॉइज़ मैकेनिज़्म इस्तेमाल करते हैं: GaussianNoise और LaplaceNoise. दोनों के अपने फ़ायदे और नुकसान हैं. हालांकि, लैप्लस डिस्ट्रिब्यूशन से कम कॉन्ट्रिब्यूशन बाउंड के साथ बेहतर नतीजे मिलते हैं. इसलिए, Privacy On Beam इसका इस्तेमाल डिफ़ॉल्ट रूप से करता है. हालांकि, अगर आपको गॉसियन डिस्ट्रिब्यूशन नॉइज़ का इस्तेमाल करना है, तो Params को pbeam.GaussianNoise{} वैरिएबल के साथ उपलब्ध कराएं.

10. खास जानकारी

बहुत बढ़िया! आपने Beam पर निजता से जुड़ा कोडलैब पूरा कर लिया है. आपने डिफ़रेंशियल प्राइवसी और Beam पर निजता के बारे में काफ़ी कुछ सीखा:

  • MakePrivateFromStruct पर कॉल करके, PCollection को PrivatePCollection में बदलना.
  • डिफ़रेंशियल प्राइवसी के साथ गिनती करने के लिए, Count का इस्तेमाल किया जाता है.
  • डिफ़रेंशियल प्राइवसी के सिद्धांत का पालन करते हुए, MeanPerKey का इस्तेमाल करके औसत का हिसाब लगाना.
  • डिफ़रेंशियल प्राइवसी के साथ सम की गिनती करने के लिए, SumPerKey का इस्तेमाल करना.
  • एक ही पाइपलाइन में, एक ही PrivacySpec की मदद से कई आंकड़े कैलकुलेट किए जा सकते हैं.
  • (ज़रूरी नहीं) PrivacySpec और एग्रीगेशन पैरामीटर (CountParams, MeanParams, SumParams) को पसंद के मुताबिक बनाना.

हालांकि, Privacy on Beam की मदद से कई और तरह के एग्रीगेशन किए जा सकते हैं. जैसे, क्वांटाइल, अलग-अलग वैल्यू की गिनती करना! इनके बारे में ज़्यादा जानने के लिए, GitHub रिपॉज़िटरी या godoc पर जाएं.

अगर आपके पास समय है, तो कृपया सर्वे भरकर, हमें इस कोडलैब के बारे में सुझाव/राय दें या शिकायत करें.