חישוב של נתונים סטטיסטיים פרטיים באמצעות PipelineDP

1. לפני שמתחילים

יכול להיות שתחשבו שנתונים סטטיסטיים מצטברים לא חושפים מידע על האנשים שהם מתייחסים אליהם. עם זאת, יש הרבה דרכים שבהן תוקף יכול ללמוד מידע רגיש על אנשים מסטטיסטיקות מצטברות.

ב-codelab הזה תלמדו איך ליצור נתונים סטטיסטיים פרטיים עם צבירות פרטיות דיפרנציאליות מ-PipelineDP כדי להגן על הפרטיות של אנשים. ‫PipelineDP הוא פריימוורק של Python שמאפשר להחיל פרטיות דיפרנציאלית על מערכי נתונים גדולים באמצעות מערכות לעיבוד באצווה, כמו Apache Spark ו-Apache Beam. מידע נוסף על חישוב נתונים סטטיסטיים פרטיים דיפרנציאליים ב-Go זמין ב-codelab בנושא Privacy on Beam.

פרטי – הפלט נוצר באופן שלא חושף מידע פרטי על האנשים שכלולים בנתונים. אפשר להשיג את התוצאה הזו באמצעות פרטיות דיפרנציאלית, מושג חזק של פרטיות שקשור לאנונימיזציה, שהוא תהליך של צבירת נתונים מכמה משתמשים כדי להגן על פרטיות המשתמשים. כל שיטות האנונימיזציה משתמשות בצבירה, אבל לא כל שיטות הצבירה משיגות אנונימיזציה. לעומת זאת, פרטיות דיפרנציאלית מספקת ערבויות מדידות לגבי דליפת מידע ופרטיות.

דרישות מוקדמות

  • היכרות עם Python
  • היכרות עם צבירת נתונים בסיסית
  • ניסיון ב-pandas,‏ Spark ו-Beam

מה תלמדו

  • הבסיס של פרטיות דיפרנציאלית
  • איך מחשבים נתונים סטטיסטיים פרטיים באמצעות PipelineDP
  • איך משנים את התוצאות באמצעות פרמטרים נוספים של פרטיות ושימושיות

הדרישות

  • אם רוצים להריץ את ה-codelab בסביבה שלכם, צריך להתקין במחשב את Python 3.7 ואילך.
  • אם רוצים לבצע את ה-codelab בלי סביבה משלכם, צריך לקבל גישה ל-Colaboratory.

2. הסבר על פרטיות דיפרנציאלית

כדי להבין טוב יותר את הפרטיות הדיפרנציאלית, כדאי לעיין בדוגמה הפשוטה הזו.

נניח שאתם עובדים במחלקת השיווק של קמעונאי אופנה באינטרנט, ואתם רוצים להבין אילו מהמוצרים שלכם הכי סביר שיימכרו.

בתרשים הזה אפשר לראות אילו מוצרים הלקוחות בדקו קודם כשנכנסו לאתר של החנות: חולצות טי, סוודרים, גרביים או ג'ינסים. חולצות טי הן הפריט הכי פופולרי, וגרביים הם הפריט הכי פחות פופולרי.

ea813c698889a4c6.png

זה נראה שימושי, אבל יש מלכוד. אם רוצים לקחת בחשבון מידע נוסף, כמו אם הלקוחות ביצעו רכישה או איזה מוצר הם צפו שני, יש סיכון לחשיפת פרטים אישיים בנתונים.

מהתרשים הזה אפשר לראות שלקוח אחד בלבד התעניין בסוודר ואז ביצע רכישה:

b7c6f7f891778366.png

מנקודת מבט של פרטיות, זה לא אידיאלי. סטטיסטיקות אנונימיות לא אמורות לחשוף תרומות של אנשים פרטיים, אז מה עושים? אתם מוסיפים רעש אקראי לתרשימי העמודות כדי להפוך אותם לקצת פחות מדויקים!

תרשים העמודות הזה לא מדויק לגמרי, אבל הוא עדיין שימושי ולא חושף תרומות של משתמשים ספציפיים:

b55e8d7f99f6d574.gif

פרטיות דיפרנציאלית היא הוספה של כמות מתאימה של רעש אקראי כדי להסתיר תרומות של משתמשים ספציפיים.

הדוגמה הזו היא פשוטה מדי. ההטמעה הנכונה של פרטיות דיפרנציאלית היא מורכבת יותר וכוללת מספר דקויות לא צפויות. בדומה לקריפטוגרפיה, לא מומלץ ליצור הטמעה משלכם של פרטיות דיפרנציאלית. במקום זאת, אפשר להשתמש ב-PipelineDP.

3. הורדה והתקנה של PipelineDP

לא צריך להתקין את PipelineDP כדי לעבור על ה-codelab הזה, כי כל הקוד והתרשימים הרלוונטיים מופיעים במסמך הזה.

כדי להפעיל את PipelineDP, מריצים אותו בעצמכם או משתמשים בו מאוחר יותר:

  • מורידים ומתקינים את PipelineDP:
pip install pipeline-dp

אם רוצים להריץ את הדוגמה באמצעות Apache Beam:

  • מורידים ומתקינים את Apache Beam:
pip install apache_beam

אפשר למצוא את הקוד של ה-Codelab הזה ואת מערך הנתונים בספרייה PipelineDP/examples/codelab/.

4. חישוב מדדי ההמרות לפי המוצר הראשון שהמשתמש צפה בו

נניח שאתם עובדים בחנות אופנה קמעונאית אונליין ואתם רוצים להבין אילו מקטגוריות המוצרים השונות שלכם מניבות את המספר והערך הכי גבוהים של המרות כשמבקרים צופים בהן בפעם הראשונה. אתם רוצים לשתף את המידע הזה עם סוכנות השיווק שלכם וגם עם צוותים פנימיים אחרים, אבל אתם רוצים למנוע דליפה של מידע על לקוחות ספציפיים.

כדי לחשב את מדדי ההמרות לפי המוצר הראשון שהמשתמש צפה בו באתר:

  1. בודקים את מערך הנתונים לדוגמה של ביקורים באתר בספרייה PipelineDP/examples/codelab/.

צילום המסך הזה הוא דוגמה למערך הנתונים. הוא מכיל את מזהה המשתמש, את המוצרים שהמשתמש צפה בהם, את המידע אם המבקר השלים המרה, ואם כן, את ערך ההמרה.

user_id

product_view_0

product_view_1

product_view_2

product_view_3

product_view_4

has_conversion

conversion_value

0

ג'ינס

t_shirt

t_shirt

אין

אין

false

0.0

1

ג'ינס

t_shirt

ג'ינס

ג'אמפר

אין

false

0.0

2

t_shirt

ג'אמפר

t_shirt

t_shirt

אין

true

105.19

3

t_shirt

t_shirt

ג'ינס

אין

אין

false

0.0

4

t_shirt

גרביים

ג'ינס

ג'ינס

אין

false

0.0

המדדים שמעניינים אותך:

  • view_counts: מספר הפעמים שבהן המבקרים באתר רואים כל מוצר בפעם הראשונה.
  • total_conversion_value: הסכום הכולל של הכסף שהמבקרים מוציאים כשהם מבצעים המרה.
  • conversion_rate: שיעור ההמרה של המבקרים.
  1. יצירת המדדים באופן לא פרטי:
conversion_metrics = df.groupby(['product_view_0'
                               ])[['conversion_value', 'has_conversion']].agg({
                                   'conversion_value': [len, np.sum],
                                   'has_conversion': np.mean
                               })
conversion_metrics = conversion_metrics.rename(
   columns={
       'len': 'view_counts',
       'sum': 'total_conversion_value',
       'mean': 'conversion_rate'
   }).droplevel(
       0, axis=1)

כמו שלמדתם קודם, הנתונים הסטטיסטיים האלה יכולים לחשוף מידע על אנשים במערך הנתונים. לדוגמה, רק אדם אחד ביצע המרה אחרי שצפה קודם במודעת באמפר. אם היו 22 צפיות, שיעור ההמרה הוא בערך 0.05. עכשיו צריך להפוך כל תרשים עמודות לתרשים פרטי.

  1. מגדירים את פרמטרים הפרטיות באמצעות המחלקה pipeline_dp.NaiveBudgetAccountant, ואז מציינים את הארגומנטים epsilon ו-delta שבהם רוצים להשתמש בניתוח.

אופן ההגדרה של הארגומנטים האלה תלוי בבעיה הספציפית שלכם. מידע נוסף על הפרמטרים האלה זמין במאמר בנושא אופציונלי: שינוי הפרמטרים של הפרטיות הדיפרנציאלית.

בקטע הקוד הזה נעשה שימוש בערכים לדוגמה:

budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=1e-5)
  1. מאתחלים את המופע של LocalBackend:
ops = pipeline_dp.LocalBackend()

אפשר להשתמש במופע LocalBackend כי אתם מריצים את התוכנית הזו באופן מקומי בלי מסגרות נוספות, כמו Beam או Spark.

  1. מאתחלים את המופע של DPEngine:
dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)

באמצעות המחלקה pipeline_dp.AggregateParams ב-PipelineDP, אפשר לציין פרמטרים נוספים שמשפיעים על יצירת הנתונים הסטטיסטיים הפרטיים.

params = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT],
     max_partitions_contributed=1,
     max_contributions_per_partition=1)
  1. מציינים שרוצים לחשב את המדד count ולהשתמש בהתפלגות הרעש LAPLACE.
  2. מגדירים את הארגומנט max_partitions_contributed לערך 1.

הארגומנט הזה מגביל את מספר הביקורים השונים שמשתמש יכול לתרום. אתם מצפים שהמשתמשים יבקרו באתר פעם ביום, ולא משנה אם הם מבקרים בו כמה פעמים במהלך היום.

  1. מגדירים את הארגומנט max_contributions_per_partitions לערך 1.

הארגומנט הזה מציין כמה תרומות מבקר יחיד יכול לתת למחיצה ספציפית או לקטגוריית מוצרים במקרה הזה.

  1. יוצרים מופע data_extractor שמציין איפה נמצאים השדות privacy_id, partition ו-value.

הקוד אמור להיראות כמו קטע הקוד הזה:

def run_pipeline(data, ops):
 budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=1e-5)

 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)

 params = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT],
     max_partitions_contributed=1, # A single user can only contribute to one partition.
     max_contributions_per_partition=1, # For a single partition, only one contribution per user is used.
     )

 data_extractors = pipeline_dp.DataExtractors(
     privacy_id_extractor=lambda row: row.user_id,
     partition_extractor=lambda row: row.product_view_0
     value_extractor=lambda row: row.has_conversion)

 dp_result = dp_engine.aggregate(data, params, data_extractors)

 budget_accountant.compute_budgets()

 return dp_result
  1. מוסיפים את הקוד הזה כדי להפוך את Pandas DataFrame לרשימה של שורות שממנה אפשר לחשב ישירות נתונים סטטיסטיים עם פרטיות דיפרנציאלית:
rows = [index_row[1] for index_row in df.iterrows()]
dp_result_local = run_pipeline(rows, ops) # Returns generator
list(dp_result_local)

מעולה! חישבתם את הנתון הסטטיסטי הראשון שלכם עם פרטיות דיפרנציאלית.

בתרשים הזה מוצגת התוצאה של הספירה עם פרטיות דיפרנציאלית לצד הספירה ללא פרטיות שחישבתם קודם:

a5a25a00858219ab.png

תרשים העמודות שמתקבל כשמריצים את הקוד עשוי להיות שונה מזה שמוצג כאן, וזה בסדר. בגלל הרעש בפרטיות דיפרנציאלית, בכל פעם שמריצים את הקוד מתקבל תרשים עמודות שונה, אבל אפשר לראות שהם דומים לתרשים העמודות המקורי שלא מבוסס על פרטיות דיפרנציאלית.

חשוב לציין שמאוד חשוב להבטיח את הפרטיות, ולכן לא מריצים את צינור הנתונים כמה פעמים. מידע נוסף זמין במאמר בנושא חישוב נתונים סטטיסטיים מרובים.

5. שימוש במחיצות ציבוריות

בקטע הקודם, יכול להיות ששמתם לב שהשמטתם את כל נתוני הביקורים במחיצה, כלומר את המבקרים שראו לראשונה גרביים באתר שלכם.

הסיבה לכך היא בחירת מחיצות או הגדרת סף, שלב חשוב כדי להבטיח פרטיות דיפרנציאלית כשקיום של מחיצות פלט תלוי בנתוני המשתמש עצמם. במקרים כאלה, עצם קיומה של מחיצה בפלט יכול לחשוף את קיומו של משתמש פרטי בנתונים. כדי לקבל מידע נוסף על הסיבות לכך שזו הפרה של הפרטיות, כדאי לקרוא את הפוסט הזה בבלוג. כדי למנוע את הפגיעה הזו בפרטיות, PipelineDP שומר רק מחיצות עם מספר מספיק של משתמשים.

אם רשימת מחיצות הפלט לא תלויה בנתוני משתמש פרטיים, לא צריך לבצע את שלב בחירת המחיצה הזה. זה המצב בדוגמה שלכם, כי אתם יודעים את כל קטגוריות המוצרים האפשריות שהלקוח יכול לראות קודם.

כדי להשתמש במחיצות:

  1. יוצרים רשימה של המחיצות האפשריות:
public_partitions_products = ['jeans', 'jumper', 'socks', 't-shirt']
  1. מעבירים את הרשימה לפונקציה run_pipeline(), שמגדירה אותה כקלט נוסף למחלקה pipeline_dp.AggregateParams:
run_pipeline(
    rows, ops, total_delta=0, public_partitions=public_partitions_products)
  # Returns generator
params = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT],
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     public_partitions=public_partitions_products)

אם משתמשים במחיצות ציבוריות וברעש LAPLACE, אפשר להגדיר את הארגומנט total_delta לערך 0.

עכשיו אפשר לראות בתוצאה שמוצגים נתונים לגבי כל המחיצות או המוצרים.

a4f6302c8efcd5da.png

מחיצות ציבוריות לא רק מאפשרות לכם לשמור יותר מחיצות, אלא גם מוסיפות בערך חצי מכמות הרעש, כי אתם לא מבזבזים תקציב פרטיות על בחירת מחיצות. לכן, ההבדל בין הספירות הגולמיות לבין הספירות הפרטיות קטן יותר בהשוואה להרצה הקודמת.

יש שני דברים חשובים שצריך לזכור כשמשתמשים במחיצות ציבוריות:

  • חשוב להיזהר כשמפיקים את רשימת המחיצות מנתונים גולמיים. אם לא תעשו את זה בצורה פרטית דיפרנציאלית, הצינור לא יספק יותר ערבויות לפרטיות דיפרנציאלית. מידע נוסף זמין במאמר בנושא מתקדם: יצירת מחיצות מנתונים.
  • אם אין נתונים בחלק מהמחיצות הציבוריות, צריך להוסיף רעש למחיצות האלה כדי לשמור על פרטיות דיפרנציאלית. לדוגמה, אם השתמשתם במוצר נוסף כמו מכנסיים, שלא מופיע במערך הנתונים או באתר שלכם, הוא עדיין נחשב לרעשי רקע והתוצאות עשויות להראות ביקורים במוצרים כשלא היו כאלה.

מתקדם: יצירת מחיצות מנתונים

אם מריצים כמה צבירות עם אותה רשימה של מחיצות פלט לא ציבוריות באותו צינור, אפשר לגזור את רשימת המחיצות פעם אחת באמצעות השיטה dp_engine.select_private_partitions() ולספק את המחיצות לכל צבירה כקלט public_partitions. השיטה הזו לא רק בטוחה מבחינת פרטיות, אלא גם מאפשרת להוסיף פחות רעשי רקע כי משתמשים בתקציב הפרטיות רק פעם אחת לבחירת המחיצה לכל צינור הנתונים.

def get_private_product_views(data, ops):
 """Obtains the list of product_views in a private manner.

   This does not calculate any private metrics; it merely obtains the list of
   product_views but does so while making sure the result is differentially private.
   """

 # Set the total privacy budget.
 budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=1e-5)

 # Create a DPEngine instance.
 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)

 # Specify how to extract privacy_id, partition_key, and value from a   
 # single element.
 data_extractors = pipeline_dp.DataExtractors(
     partition_extractor=lambda row: row.product_view_0,
     privacy_id_extractor=lambda row: row.user_id)

 # Run aggregation.
 dp_result = dp_engine.select_partitions(
     data, pipeline_dp.SelectPrivatePartitionsParams(
       max_partitions_contributed=1),
     data_extractors=data_extractors)

 budget_accountant.compute_budgets()
 return dp_result

6. חישוב של כמה נתונים סטטיסטיים

אחרי שהבנתם איך PipelineDP פועל, אתם יכולים לראות איך אפשר להשתמש בו למקרים מתקדמים יותר. כמו שציינתי בהתחלה, אתה מתעניין בשלושה נתונים סטטיסטיים. ‫PipelineDP מאפשר לחשב כמה נתונים סטטיסטיים בו-זמנית, כל עוד הם חולקים את אותם פרמטרים במופע AggregateParams, שיוצג בהמשך. השיטה הזו לא רק נקייה וקלה יותר לחישוב של כמה מדדים בבת אחת, אלא גם טובה יותר מבחינת פרטיות.

אם אתם זוכרים את הפרמטרים epsilon ו-delta שאתם מספקים למחלקה NaiveBudgetAccountant, הם מייצגים משהו שנקרא תקציב פרטיות, שהוא מדד לכמות הפרטיות של המשתמשים שאתם חושפים מהנתונים.

חשוב לזכור שהתקציב לפרטיות הוא מצטבר. אם מריצים צינור עיבוד נתונים עם ערכי אפסילון ε ודלתא δ מסוימים פעם אחת, מוציאים תקציב של (ε,δ). אם מפעילים פתרונות חכמים פעם שנייה, התקציב הכולל הוא (2ε, 2δ). באופן דומה, אם מחשבים כמה נתונים סטטיסטיים באמצעות שיטה NaiveBudgetAccountant וברצף תקציב פרטיות של ε,δ, התקציב הכולל שמוציאים הוא (2ε, 2δ). המשמעות היא שאתם פוגעים בהתחייבויות לפרטיות.

כדי לעקוף את הבעיה הזו, צריך להשתמש במופע NaiveBudgetAccountant יחיד עם התקציב הכולל שרוצים להשתמש בו כשצריך לחשב כמה נתונים סטטיסטיים על אותם נתונים. לאחר מכן צריך לציין את הערכים של epsilon ושל delta שרוצים להשתמש בהם לכל צבירה. בסופו של דבר, תקבלו את אותה התחייבות כוללת לשמירה על פרטיות, אבל ככל שהערכים של epsilon ו-delta גבוהים יותר עבור צבירה מסוימת, כך רמת הדיוק שלה גבוהה יותר.

כדי לראות את זה בפעולה, אפשר לחשב את הנתונים הסטטיסטיים של count, mean ו-sum.

אתם מחשבים נתונים סטטיסטיים על סמך שני מדדים שונים: מדד conversion_value, שמשמש להסקת מסקנות לגבי סכום ההכנסה שנוצר על סמך המוצר שהמשתמש צפה בו ראשון, ומדד has_conversion, שמשמש לחישוב מספר המבקרים באתר ושיעור ההמרה הממוצע.

לכל מדד צריך לציין בנפרד את הפרמטרים שמשמשים לחישוב הנתונים הסטטיסטיים הפרטיים. אתם מחלקים את תקציב הפרטיות בין שני המדדים. אתם מחשבים שני נתונים סטטיסטיים מהמדד has_conversion, ולכן אתם רוצים להקצות לו שני שלישים מהתקציב הראשוני, ולהקצות את השליש הנותר למדד conversion_value.

כדי לחשב כמה נתונים סטטיסטיים:

  1. מגדירים את רואה החשבון של תקציב הפרטיות עם הערכים הכוללים של epsilon ו-delta שרוצים להשתמש בהם בשלושת הנתונים הסטטיסטיים:
budget_accountant = pipeline_dp.NaiveBudgetAccountant(
     total_epsilon=1, total_delta=0)
  1. מפעילים את DPEngine כדי לחשב את המדדים:
 dp_engine = pipeline_dp.DPEngine(budget_accountant, ops)
  1. מציינים את הפרמטרים של המדד הזה.
params_conversion_value_metrics = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.SUM],
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     min_value=0,
     max_value=100,
     public_partitions=public_partitions,
     budget_weight=1/3)

הארגומנט האחרון מציין באופן אופציונלי את המשקל של תקציב הפרטיות. אפשר לתת לכולם את אותו משקל, אבל אתם רוצים להגדיר את הארגומנט הזה כשליש, כמו שהוסבר קודם.

אתם גם מגדירים ארגומנטים של min_value ו-max_value כדי לציין את הגבול התחתון והגבול העליון שחלים על ערך שמועבר על ידי יחידת פרטיות במחיצה. הפרמטרים האלה נדרשים כשרוצים לחשב סכום או ממוצע פרטיים. אתם לא מצפים לערכים שליליים, ולכן אפשר להניח ש-0 ו-100 הם גבולות סבירים.

  1. לחלץ את הנתונים הרלוונטיים ואז להעביר אותם לפונקציית הצבירה:
data_extractors_conversion_value_metrics = pipeline_dp.DataExtractors(
     privacy_id_extractor=lambda row: row.user_id,
     partition_extractor=lambda row: row.product_view_0,
     value_extractor=lambda row: row.conversion_value)

 dp_result_conversion_value_metrics = (
     dp_engine.aggregate(data, params_conversion_value_metrics,
         data_extractors_conversion_value_metrics))
  1. כדי לחשב את שני המדדים על סמך המשתנה has_conversion, פועלים לפי אותם השלבים:
params_conversion_rate_metrics = pipeline_dp.AggregateParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     metrics=[pipeline_dp.Metrics.COUNT, pipeline_dp.Metrics.MEAN],
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     min_value=0,
     max_value=1,
     public_partitions=public_partitions,
     budget_weight=2/3)

 data_extractors_conversion_rate_metrics = pipeline_dp.DataExtractors(
     privacy_id_extractor=lambda row: row.user_id,
     partition_extractor=lambda row: row.product_view_0,
     value_extractor=lambda row: row.has_conversion)

 dp_result_conversion_rate_metrics = (
     dp_engine.aggregate(data, params_conversion_rate_metrics,
         data_extractors_conversion_rate_metrics))

השינוי היחיד הוא במופע pipeline_dp.AggregateParams, שבו מגדירים עכשיו את mean ואת count כצבירות, ומקצים שני שלישים מתקציב הפרטיות לחישוב הזה. אם רוצים להגדיר את אותם גבולות תרומה לשני הנתונים הסטטיסטיים ולחשב אותם על בסיס אותו משתנה has_conversion, אפשר לשלב אותם באותו מופע pipeline_dp.AggregateParams ולחשב אותם בו-זמנית.

  1. מבצעים קריאה ל-budget_accountant.compute_budgets():
budget_accountant.compute_budgets()

אפשר לשרטט את כל שלושת הנתונים הסטטיסטיים הפרטיים בהשוואה לנתונים הסטטיסטיים המקוריים. בהתאם לרעש שנוסף, אפשר לראות שהתוצאות יכולות להיות מחוץ לטווח הסביר. במקרה הזה, שיעור ההמרה וערך ההמרה הכולל של המשתמשים שעוברים בין דפים הם שליליים, כי הרעש שנוסף הוא סימטרי סביב אפס. כדי לבצע ניתוחים ועיבודים נוספים, מומלץ לא לעבד את הנתונים הסטטיסטיים הפרטיים באופן ידני. עם זאת, אם רוצים להוסיף את התרשימים האלה לדוח, אפשר פשוט להגדיר את הערך המינימלי לאפס לאחר מכן, בלי להפר את ההבטחות בנוגע לפרטיות.

cb1fc563f817eaf.png

7. הרצת צינור העיבוד באמצעות Beam

כיום, עיבוד נתונים מחייב התמודדות עם כמויות עצומות של נתונים, עד כדי כך שאי אפשר לעבד אותם באופן מקומי. במקום זאת, אנשים רבים משתמשים במסגרות לעיבוד נתונים בקנה מידה גדול, כמו Beam או Spark, ומריצים את הפייפליינים שלהם בענן.

‫PipelineDP תומך ב-Beam וב-Spark עם שינויים קטנים בלבד בקוד.

כדי להריץ את צינור הנתונים באמצעות Beam עם private_beam API:

  1. מאתחלים משתנה runner ואז יוצרים צינור עיבוד נתונים שבו מחילים את פעולות הפרטיות על ייצוג Beam של rows:
runner = fn_api_runner.FnApiRunner()  # local runner

with beam.Pipeline(runner=runner) as pipeline:
   beam_data = pipeline | beam.Create(rows)
  1. יוצרים משתנה budget_accountant עם פרמטרים נדרשים של פרטיות:
budget_accountant = pipeline_dp.NaiveBudgetAccountant(
               total_epsilon=1, total_delta=0)
  1. יוצרים משתנה pcol, או אוסף פרטי, כדי להבטיח שכל הצבירות יעמדו בדרישות בנושא פרטיות שלכם:
pcol = beam_data | pbeam.MakePrivate(
                                 budget_accountant=budget_accountant,
                                 privacy_id_extractor=lambda 
                                                    row: row.user_id)
  1. מציינים את הפרמטרים של הצבירה הפרטית בכיתה המתאימה.

כאן משתמשים ב-pipeline_dp.aggregate_params.SumParams()class כי מחשבים את סכום הצפיות במוצר.

  1. מעבירים את פרמטרים הצבירה לשיטה pbeam.Sum כדי לחשב את הנתון הסטטיסטי:
dp_result = pcol | pbeam.Sum(params)
  1. בסופו של דבר, הקוד שלכם צריך להיראות כמו קטע הקוד הזה:
import pipeline_dp.private_beam as pbeam
runner = fn_api_runner.FnApiRunner()  # local runner

with beam.Pipeline(runner=runner) as pipeline:
   beam_data = pipeline | beam.Create(rows)
   budget_accountant = pipeline_dp.NaiveBudgetAccountant(
               total_epsilon=1, total_delta=0)

   # Create private collection.
   pcol = beam_data | pbeam.MakePrivate(
                              budget_accountant=budget_accountant,
                              privacy_id_extractor=lambda row:  
                                                         row.user_id)
   # Specify parameters.
   params = pipeline_dp.aggregate_params.SumParams(
     noise_kind=pipeline_dp.NoiseKind.LAPLACE,
     max_partitions_contributed=1,
     max_contributions_per_partition=1,
     min_value=0,
     max_value=100,
     public_partitions=public_partitions_product_views,
     partition_extractor=lambda row: row.product_view_0,
     value_extractor=lambda row:row.conversion_value)
   dp_result = pcol | pbeam.Sum(params)

   budget_accountant.compute_budgets()
   dp_result | beam.Map(print)

8. אופציונלי: משנים את פרמטר הפרטיות והתועלת

ב-Codelab הזה הוזכרו כמה פרמטרים, כמו epsilon, delta ו-max_partitions_contributed. אפשר לחלק אותם באופן כללי לשתי קטגוריות: פרמטרים של פרטיות ופרמטרים של שימושיות.

פרמטרים של פרטיות

הפרמטרים epsilon ו-delta משמשים לכימות הפרטיות שאתם מספקים באמצעות פרטיות דיפרנציאלית. במילים אחרות, הם מדד לכמות המידע שתוקף פוטנציאלי יכול להשיג על הנתונים מהפלט האנונימי. ככל שהערך של הפרמטרים גבוה יותר, כך התוקף מקבל יותר מידע על הנתונים, וזהו סיכון לפרטיות. לעומת זאת, ככל שהערך של הפרמטרים epsilon ו-delta נמוך יותר, כך צריך להוסיף יותר רעשי רקע לפלט כדי להפוך אותו לאנונימי, וכך צריך יותר משתמשים ייחודיים בכל מחיצה כדי לשמור אותם בפלט האנונימי. במקרה הזה, יש פשרה בין התועלת לבין הפרטיות.

ב-PipelineDP, צריך לציין את רמת הפרטיות הרצויה של הפלט האנונימי כשמגדירים את תקציב הפרטיות הכולל במופע NaiveBudgetAccountant. החיסרון הוא שאם רוצים שההבטחות בנוגע לפרטיות יתקיימו, צריך להשתמש במופע NaiveBudgetAccountant נפרד לכל צבירה, או להריץ את צינור הנתונים כמה פעמים כדי להימנע משימוש יתר בתקציב.

מידע נוסף על פרטיות דיפרנציאלית ועל המשמעות של פרמטרים של פרטיות זמין במאמר רשימת קריאה בנושא פרטיות דיפרנציאלית.

פרמטרים של כלי

פרמטרים של תועלת לא משפיעים על ערבויות הפרטיות, אבל הם משפיעים על רמת הדיוק וכתוצאה מכך על התועלת של הפלט. הערכים האלה מסופקים במופע AggregateParams ומשמשים לשינוי קנה המידה של הרעש שנוסף.

פרמטר השירות שמופיע במופע AggregateParams ורלוונטי לכל הצבירות הוא הפרמטר max_partitions_contributed. מחיצה תואמת למפתח של הנתונים שמוחזרים על ידי פעולת הצבירה של PipelineDP, ולכן הפרמטר max_partitions_contributed מגביל את מספר ערכי המפתח השונים שמשתמש יכול לתרום לפלט. אם משתמש תורם למספר מפתחות שגדול מהערך של הפרמטר max_partitions_contributed, חלק מהתרומות מושמטות כדי שהתרומה תהיה בדיוק הערך של הפרמטר max_partitions_contributed.

באופן דומה, לרוב הצבירות יש פרמטר max_contributions_per_partition. הם מסופקים גם במופע AggregateParams, ולכל צבירה יכולים להיות ערכים נפרדים. הם מגבילים את התרומה של המשתמש לכל מפתח.

הרעש שמוסף לפלט מותאם באמצעות הפרמטרים max_partitions_contributed ו-max_contributions_per_partition, ולכן יש כאן פשרה: ערכים גדולים יותר שמוקצים לכל פרמטר אומרים ששומרים יותר נתונים, אבל התוצאה תהיה עם יותר רעש.

חלק מהצבירות דורשות פרמטרים של min_value ו-max_value, שמציינים את הגבולות של התרומות של כל משתמש. אם משתמש תורם ערך נמוך מהערך שהוקצה לפרמטר min_value, הערך יוגדל לערך של הפרמטר. באופן דומה, אם משתמש תורם ערך גדול יותר מהערך של הפרמטר max_value, הערך יוקטן לערך של הפרמטר. כדי לשמור יותר מהערכים המקוריים, צריך לציין גבולות רחבים יותר. הרעש מותאם לגודל הגבולות, כך שגבולות גדולים יותר מאפשרים לשמור יותר נתונים, אבל התוצאה תהיה עם יותר רעש.

לבסוף, הפרמטר noise_kind תומך בשני מנגנוני רעש שונים ב-PipelineDP: רעש GAUSSIAN ורעש LAPLACE. ההתפלגות LAPLACE מספקת תועלת טובה יותר עם גבולות תרומה נמוכים, ולכן PipelineDP משתמש בה כברירת מחדל. עם זאת, אם רוצים להשתמש ברעש של התפלגות GAUSSIAN, אפשר לציין אותו במופע AggregateParams.

9. מזל טוב

מעולה! סיימתם את ה-codelab של PipelineDP ולמדתם הרבה על פרטיות דיפרנציאלית ועל PipelineDP.

מידע נוסף