עזרה להגן על שונית המחסום הגדולה עם TensorFlow על Kaggle הצטרפו אתגר

בניית אלגוריתם למידה מאוחד משלך

הצג באתר TensorFlow.org הפעל בגוגל קולאב צפה במקור ב-GitHub הורד מחברת

לפני שאנחנו מתחילים

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

!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio

import nest_asyncio
nest_asyncio.apply()
import tensorflow as tf
import tensorflow_federated as tff

בשנות ה סיווג התמונה ואת הדור הטקסט הדרכות, למדנו כיצד להגדיר צינורות מודל ונתונים עבור Federated למידה (FL), וביצע אימונים Federated דרך tff.learning שכבת API של TFF.

זה רק קצה הקרחון כשמדובר במחקר FL. במדריך זה, אנו דנים כיצד ליישם אלגוריתמים של למידה Federated ללא דחיית אל tff.learning API. אנו שואפים להשיג את הדברים הבאים:

מטרות:

  • הבן את המבנה הכללי של אלגוריתמי למידה מאוחדים.
  • חקור את Federated Core של TFF.
  • השתמש ב-Federated Core כדי ליישם ממוצע פדרלי ישירות.

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

הכנת נתוני הקלט

ראשית, אנו טוענים ומעבדים מראש את מערך הנתונים של EMNIST הכלול ב-TFF. לפרטים נוספים, ראה סיווג תמונת ההדרכה.

emnist_train, emnist_test = tff.simulation.datasets.emnist.load_data()

כדי להאכיל את הנתונים לתוך המודל שלנו, אנו לשטח את הנתונים, ולהמיר כל דוגמה לתוך tuple של הטופס (flattened_image_vector, label) .

NUM_CLIENTS = 10
BATCH_SIZE = 20

def preprocess(dataset):

  def batch_format_fn(element):
    """Flatten a batch of EMNIST data and return a (features, label) tuple."""
    return (tf.reshape(element['pixels'], [-1, 784]), 
            tf.reshape(element['label'], [-1, 1]))

  return dataset.batch(BATCH_SIZE).map(batch_format_fn)

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

client_ids = sorted(emnist_train.client_ids)[:NUM_CLIENTS]
federated_train_data = [preprocess(emnist_train.create_tf_dataset_for_client(x))
  for x in client_ids
]

הכנת הדגם

אנו משתמשים באותו מודל כמו סיווג התמונה הדרכה. מודל זה (מיושם באמצעות tf.keras יש) בשכבה אחת חבויה, ואחריו שכבת softmax.

def create_keras_model():
  initializer = tf.keras.initializers.GlorotNormal(seed=0)
  return tf.keras.models.Sequential([
      tf.keras.layers.Input(shape=(784,)),
      tf.keras.layers.Dense(10, kernel_initializer=initializer),
      tf.keras.layers.Softmax(),
  ])

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

def model_fn():
  keras_model = create_keras_model()
  return tff.learning.from_keras_model(
      keras_model,
      input_spec=federated_train_data[0].element_spec,
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])

בעוד השתמשנו tf.keras ליצור tff.learning.Model , TFF תומך הרבה מודלים כלליים יותר. למודלים אלה יש את התכונות הרלוונטיות הבאות המתעדות את משקלי המודל:

  • trainable_variables : An iterable של טנזורים המתאים שכבות שאפשר לאלף.
  • non_trainable_variables : An iterable של טנזורים המתאים שכבות הלא שאפשר לאלף.

לענייננו, אנחנו רק נשתמש trainable_variables . (מכיוון שלדגם שלנו יש רק כאלה!).

בניית אלגוריתם למידה מאוחדת משלך

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

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

  1. שלב שידור שרת ללקוח.
  2. שלב עדכון לקוח מקומי.
  3. שלב העלאת לקוח לשרת.
  4. שלב עדכון שרת.

בשנת TFF, אנחנו מייצגים אלגוריתמים Federated בדרך כלל בתור tff.templates.IterativeProcess (אשר אנו מתייחסים כאל סתם IterativeProcess לאורך). זוהי מחלקה המכילה initialize ו next פונקציות. הנה, initialize משמשת לאתחל את השרת, ואת next תבצע סיבוב תקשורת אחד של אלגוריתם Federated. בואו נכתוב שלד של איך התהליך האיטרטיבי שלנו עבור FedAvg צריך להיראות.

ראשית, יש לנו פונקציה התחלתית שפשוט יוצר tff.learning.Model , ומחזירה משקולות שאפשר לאלף שלה.

def initialize_fn():
  model = model_fn()
  return model.trainable_variables

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

אנחנו גם רוצים לשרטט את next_fn .

def next_fn(server_weights, federated_dataset):
  # Broadcast the server weights to the clients.
  server_weights_at_client = broadcast(server_weights)

  # Each client computes their updated weights.
  client_weights = client_update(federated_dataset, server_weights_at_client)

  # The server averages these updates.
  mean_client_weights = mean(client_weights)

  # The server updates its model.
  server_weights = server_update(mean_client_weights)

  return server_weights

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

TensorFlow Blocks

עדכון לקוח

נשתמש שלנו tff.learning.Model לעשות אימונים הלקוח בעצם באותו אופן היית לאמן מודל TensorFlow. בפרט, נשתמש tf.GradientTape לחשב את השיפוע על קבוצות של נתונים, ולאחר מכן להחיל שיפוע אלה באמצעות client_optimizer . אנו מתמקדים רק במשקולות הניתנות לאימון.

@tf.function
def client_update(model, dataset, server_weights, client_optimizer):
  """Performs training (using the server model weights) on the client's dataset."""
  # Initialize the client model with the current server weights.
  client_weights = model.trainable_variables
  # Assign the server weights to the client model.
  tf.nest.map_structure(lambda x, y: x.assign(y),
                        client_weights, server_weights)

  # Use the client_optimizer to update the local model.
  for batch in dataset:
    with tf.GradientTape() as tape:
      # Compute a forward pass on the batch of data
      outputs = model.forward_pass(batch)

    # Compute the corresponding gradient
    grads = tape.gradient(outputs.loss, client_weights)
    grads_and_vars = zip(grads, client_weights)

    # Apply the gradient using a client optimizer.
    client_optimizer.apply_gradients(grads_and_vars)

  return client_weights

עדכון שרת

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

@tf.function
def server_update(model, mean_client_weights):
  """Updates the server model weights as the average of the client model weights."""
  model_weights = model.trainable_variables
  # Assign the mean client weights to the server model.
  tf.nest.map_structure(lambda x, y: x.assign(y),
                        model_weights, mean_client_weights)
  return model_weights

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

האתגר: ליישם גרסה של server_update כי מעדכנת את משקולות השרת להיות נקודת האמצע של model_weights ו mean_client_weights. (הערה: סוג זה של "אמצע" גישת משול העבודה אחרונה על האופטימיזציה המשך הדרך !).

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

זה ידרוש Core Federated של TFF.

מבוא ל-Federated Core

Federated Core (FC) הוא קבוצה של ממשקים ברמה נמוכים המשמשים כבסיס עבור tff.learning API. עם זאת, ממשקים אלו אינם מוגבלים ללמידה. למעשה, הם יכולים לשמש עבור ניתוחים וחישובים רבים אחרים על פני נתונים מבוזרים.

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

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

נתונים מאוחדים

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

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

federated_float_on_clients = tff.FederatedType(tf.float32, tff.CLIENTS)

סוגי Federated מצוינים באמצעות סוג T של מרכיבי החבר שלו (למשל. tf.float32 ) וקבוצה G של התקנים. אנו נתמקד במקרים בהם G הוא או tff.CLIENTS או tff.SERVER . כזה סוג Federated מיוצג {T}@G , כמוצג להלן.

str(federated_float_on_clients)
'{float32}@CLIENTS'

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

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

חישובים מאוחדים

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

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

@tff.federated_computation(tff.FederatedType(tf.float32, tff.CLIENTS))
def get_average_temperature(client_temperatures):
  return tff.federated_mean(client_temperatures)

אתם עשויים לשאול, במה הוא שונה מן tf.function מעצב ב TensorFlow? תשובת המפתח היא שהקוד שנוצר על ידי tff.federated_computation אינו קוד TensorFlow ולא Python; זהו מפרט של מערכת מבוזרת בשפה דבקה תלוי בפלטפורמה פנימית.

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

str(get_average_temperature.type_signature)
'({float32}@CLIENTS -> float32@SERVER)'

זה tff.federated_computation מקבל טיעונים מסוג Federated {float32}@CLIENTS , וערכי תשואות של סוג Federated {float32}@SERVER . חישובים מאוחדים עשויים לעבור גם משרת ללקוח, מלקוח ללקוח, או משרת לשרת. חישובים מאוחדים יכולים להיות מורכבים גם כמו פונקציות רגילות, כל עוד חתימות הסוג שלהם תואמות.

כדי לתמוך בפיתוח, TFF מאפשר לך לעורר tff.federated_computation כפונקציה Python. למשל, אנחנו יכולים להתקשר

get_average_temperature([68.5, 70.3, 69.8])
69.53334

חישובים לא להוטים ו-TensorFlow

ישנן שתי מגבלות עיקריות שכדאי להיות מודע להן. ראשית, כאשר מתורגמן פיתון נתקל tff.federated_computation מעצב, הפונקציה הוא אתר אחת בהמשכים לשימוש עתידי. בשל האופי המבוזר של פדרציית למידה, שימוש עתידי זה עשוי להתרחש במקום אחר, כגון סביבת ביצוע מרחוק. לכן, חישובי TFF שאינם להוטים מיסודה. התנהגות זו היא דומה למדי לזה של tf.function מעצב ב TensorFlow.

שנית, חישוב Federated יכול להכיל מפעילי Federated בלבד (כגון tff.federated_mean ), הם לא יכולים להכיל פעולות TensorFlow. קוד TensorFlow חייב להיות מוגבל בלוקים מעוטרים tff.tf_computation . רוב קוד TensorFlow הרגיל יכול להיות מעוצב באופן ישיר, כמו הפונקציה הבאה שלוקח מספר ומוסיף 0.5 אלי.

@tff.tf_computation(tf.float32)
def add_half(x):
  return tf.add(x, 0.5)

גם אלה חתימות סוג, אך ללא מיקומים. למשל, אנחנו יכולים להתקשר

str(add_half.type_signature)
'(float32 -> float32)'

כאן אנו רואים הבדל חשוב בין tff.federated_computation ו tff.tf_computation . לראשון יש מיקומים מפורשים, בעוד שלאחרון אין.

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

@tff.federated_computation(tff.FederatedType(tf.float32, tff.CLIENTS))
def add_half_on_clients(x):
  return tff.federated_map(add_half, x)

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

str(add_half_on_clients.type_signature)
'({float32}@CLIENTS -> {float32}@CLIENTS)'

לסיכום:

  • TFF פועל על פי ערכים מאוחדים.
  • לכל ערך Federated סוג Federated, עם סוג כלשהו (למשל. tf.float32 ) והקצאה (למשל. tff.CLIENTS ).
  • ערכי Federated ניתן להפוך באמצעות חישובי Federated, אשר חייב להיות מעוטר tff.federated_computation וחתימת סוג Federated.
  • קוד TensorFlow חייב להיכלל בלוקים עם tff.tf_computation מעצבי.
  • לאחר מכן ניתן לשלב בלוקים אלה בחישובים מאוחדים.

בניית אלגוריתם למידה מאוחדת משלך, נבדק מחדש

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

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

בלוקים מאוחדים של TensorFlow

יצירת חישוב האתחול

פונקציית האתחול תהיה די פשוט: אנו ניצור מודל באמצעות model_fn . עם זאת, יש לזכור כי אנו חייבים להפריד את קוד TensorFlow שלנו באמצעות tff.tf_computation .

@tff.tf_computation
def server_init():
  model = model_fn()
  return model.trainable_variables

אז אנו יכולים לעבור את זה ישירות לתוך החישוב Federated באמצעות tff.federated_value .

@tff.federated_computation
def initialize_fn():
  return tff.federated_value(server_init(), tff.SERVER)

יצירת next_fn

כעת אנו משתמשים בקוד עדכון הלקוח והשרת שלנו כדי לכתוב את האלגוריתם בפועל. נפנינו שלנו ראשון client_update לתוך tff.tf_computation שמקבל מערכי נתוני לקוח משקולות שרת, ו פלט מותח משקולת לקוח מעודכן.

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

whimsy_model = model_fn()
tf_dataset_type = tff.SequenceType(whimsy_model.input_spec)

הבה נסתכל על החתימה מסוג מערך הנתונים. זכור שצילמנו 28 על 28 תמונות (עם תוויות שלמים) והשטחנו אותן.

str(tf_dataset_type)
'<float32[?,784],int32[?,1]>*'

אנחנו יכולים גם לחלץ את סוג משקולות דגם באמצעות שלנו server_init הפונקציה הנ"ל.

model_weights_type = server_init.type_signature.result

בבחינת חתימת הסוג, נוכל לראות את הארכיטקטורה של הדגם שלנו!

str(model_weights_type)
'<float32[784,10],float32[10]>'

כעת אנו יכולים ליצור שלנו tff.tf_computation עבור עדכון הלקוח.

@tff.tf_computation(tf_dataset_type, model_weights_type)
def client_update_fn(tf_dataset, server_weights):
  model = model_fn()
  client_optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
  return client_update(model, tf_dataset, server_weights, client_optimizer)

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

@tff.tf_computation(model_weights_type)
def server_update_fn(mean_client_weights):
  model = model_fn()
  return server_update(model, mean_client_weights)

אחרונה, אך לא פחות חשוב, אנחנו צריכים ליצור את tff.federated_computation שמביא כל זה ביחד. פונקציה זו תקבל שני ערכים Federated, אחד מתאים משקולות השרת (עם המיקום tff.SERVER ), והשני מתאים מערכי נתוני לקוח (עם המיקום tff.CLIENTS ).

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

federated_server_type = tff.FederatedType(model_weights_type, tff.SERVER)
federated_dataset_type = tff.FederatedType(tf_dataset_type, tff.CLIENTS)

זוכרים את 4 האלמנטים של אלגוריתם FL?

  1. שלב שידור שרת ללקוח.
  2. שלב עדכון לקוח מקומי.
  3. שלב העלאת לקוח לשרת.
  4. שלב עדכון שרת.

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

@tff.federated_computation(federated_server_type, federated_dataset_type)
def next_fn(server_weights, federated_dataset):
  # Broadcast the server weights to the clients.
  server_weights_at_client = tff.federated_broadcast(server_weights)

  # Each client computes their updated weights.
  client_weights = tff.federated_map(
      client_update_fn, (federated_dataset, server_weights_at_client))

  # The server averages these updates.
  mean_client_weights = tff.federated_mean(client_weights)

  # The server updates its model.
  server_weights = tff.federated_map(server_update_fn, mean_client_weights)

  return server_weights

עכשיו יש לנו tff.federated_computation היא עבור אתחול האלגוריתם, ו להפעלת צעד אחד של האלגוריתם. כדי לסיים האלגוריתם שלנו, אנחנו עוברים אלה לתוך tff.templates.IterativeProcess .

federated_algorithm = tff.templates.IterativeProcess(
    initialize_fn=initialize_fn,
    next_fn=next_fn
)

המבט באים בבית חתימת הסוג של initialize ו next הפונקציות של התהליך איטרטיבי שלנו.

str(federated_algorithm.initialize.type_signature)
'( -> <float32[784,10],float32[10]>@SERVER)'

זה משקף את העובדה federated_algorithm.initialize הוא פונקציה לא-ARG המחזירה מודל שכבה אחת (עם מטריצה במשקל 784-by-10, ו 10 יחידות הטיה).

str(federated_algorithm.next.type_signature)
'(<server_weights=<float32[784,10],float32[10]>@SERVER,federated_dataset={<float32[?,784],int32[?,1]>*}@CLIENTS> -> <float32[784,10],float32[10]>@SERVER)'

כאן, אנו רואים כי federated_algorithm.next מקבלת מודל שרת ונתוני לקוח, וחוזר מודל שרת מעודכן.

הערכת האלגוריתם

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

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

central_emnist_test = emnist_test.create_tf_dataset_from_all_clients()
central_emnist_test = preprocess(central_emnist_test)

לאחר מכן, אנו כותבים פונקציה שמקבלת מצב שרת, ומשתמשת ב-Keras כדי להעריך על מערך הנתונים של הבדיקה. אם אתה מכיר tf.Keras , יהיה זה כל מראה מוכר, למרות הפתק השימוש set_weights !

def evaluate(server_state):
  keras_model = create_keras_model()
  keras_model.compile(
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()]  
  )
  keras_model.set_weights(server_state)
  keras_model.evaluate(central_emnist_test)

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

server_state = federated_algorithm.initialize()
evaluate(server_state)
2042/2042 [==============================] - 2s 767us/step - loss: 2.8479 - sparse_categorical_accuracy: 0.1027

בואו נתאמן לכמה סיבובים ונראה אם ​​משהו משתנה.

for round in range(15):
  server_state = federated_algorithm.next(server_state, federated_train_data)
evaluate(server_state)
2042/2042 [==============================] - 2s 738us/step - loss: 2.5867 - sparse_categorical_accuracy: 0.0980

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

שינוי האלגוריתם שלנו

בשלב זה, בואו נעצור ונחשוב על מה שהשגנו. יישמנו ממוצע פדרלי ישירות על ידי שילוב קוד TensorFlow טהור (עבור עדכוני הלקוח והשרת) עם חישובים מאוחדים מה-Federated Core של TFF.

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

אתגר: הוסף גזיר שיפוע אל client_update הפונקציה.

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

אתגר Harder: ליישם ממוצעי Federated עם למידת ריקבון שיעור על הלקוחות.

בשלב זה, אתה עשוי להתחיל להבין כמה גמישות יש במה שאתה יכול ליישם במסגרת זו. לקבלת רעיונות (כולל מענה לאתגר קשה לעיל) אתה יכול לראות את קוד המקור עבור tff.learning.build_federated_averaging_process , או לבדוק שונים פרויקטים מחקריים באמצעות TFF.