הצג באתר TensorFlow.org | הפעל בגוגל קולאב | צפה במקור ב-GitHub | הורד מחברת |
הדרכה זו בונה על המושגים של למידת Federated עבור סיווג תמונת הדרכה, ומציגה מספר גישות שימושיות נוספות ללמידת Federated.
בפרט, אנו טוענים מודל Keras שעבר הכשרה, ומשכללים אותו באמצעות אימון מאוחד על מערך נתונים מבוזר (מדומה). זה חשוב מעשית מכמה סיבות. היכולת להשתמש במודלים סדרתיים מקלה על שילוב למידה מאוחדת עם גישות ML אחרות. יתר על כן, זה מאפשר שימוש של מגוון הולך וגדל של דגמים מאומנים מראש --- למשל, מודלי שפת הדרכה מאפסים הם לעתים נדירות יש צורך, כמו מודלים טרום מאומנים רבים זמינים כעת נרחב (ראה, למשל, רכזת TF ). במקום זאת, הגיוני יותר להתחיל ממודל שהוכשר מראש, ולחדד אותו באמצעות Federated Learning, תוך התאמה למאפיינים הספציפיים של הנתונים המבוזרים עבור יישום מסוים.
עבור הדרכה זו, אנו מתחילים עם RNN שיוצר תווי ASCII, ומשכללים אותו באמצעות למידה מאוחדת. אנו גם מראים כיצד ניתן להחזיר את המשקולות הסופיות למודל Keras המקורי, מה שמאפשר הערכה קלה ויצירת טקסט באמצעות כלים סטנדרטיים.
!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio
import nest_asyncio
nest_asyncio.apply()
import collections
import functools
import os
import time
import numpy as np
import tensorflow as tf
import tensorflow_federated as tff
np.random.seed(0)
# Test the TFF is working:
tff.federated_computation(lambda: 'Hello, World!')()
b'Hello, World!'
טען דגם מאומן מראש
אנו לטעון מודל היה מראש מאומן בעקבות הדרכת TensorFlow דור טקסט באמצעות RNN עם ביצוע להוט . עם זאת, במקום הכשרה על מכלול יצירתו של שייקספיר , אנו מראש מאומן המודל על טקסט מתוך צ'ארלס דיקנס מעשייה אחת של שתי ערים ו A Christmas Carol .
מלבד הרחבת אוצר המילים, לא שינינו את ההדרכה המקורית, כך שהמודל הראשוני הזה אינו מתקדם, אבל הוא מייצר תחזיות סבירות ומספיק למטרות ההדרכה שלנו. המודל הסופי ניצל עם tf.keras.models.save_model(include_optimizer=False)
.
אנו נשתמש בלמידה מאוחדת כדי לכוונן את המודל הזה עבור שייקספיר במדריך זה, באמצעות גרסה מאוחדת של הנתונים שסופקו על ידי TFF.
צור את טבלאות בדיקת הקול
# A fixed vocabularly of ASCII chars that occur in the works of Shakespeare and Dickens:
vocab = list('dhlptx@DHLPTX $(,048cgkoswCGKOSW[_#\'/37;?bfjnrvzBFJNRVZ"&*.26:\naeimquyAEIMQUY]!%)-159\r')
# Creating a mapping from unique characters to indices
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)
טען את המודל שהוכשר מראש והפק קצת טקסט
def load_model(batch_size):
urls = {
1: 'https://storage.googleapis.com/tff-models-public/dickens_rnn.batch1.kerasmodel',
8: 'https://storage.googleapis.com/tff-models-public/dickens_rnn.batch8.kerasmodel'}
assert batch_size in urls, 'batch_size must be in ' + str(urls.keys())
url = urls[batch_size]
local_file = tf.keras.utils.get_file(os.path.basename(url), origin=url)
return tf.keras.models.load_model(local_file, compile=False)
def generate_text(model, start_string):
# From https://www.tensorflow.org/tutorials/sequences/text_generation
num_generate = 200
input_eval = [char2idx[s] for s in start_string]
input_eval = tf.expand_dims(input_eval, 0)
text_generated = []
temperature = 1.0
model.reset_states()
for i in range(num_generate):
predictions = model(input_eval)
predictions = tf.squeeze(predictions, 0)
predictions = predictions / temperature
predicted_id = tf.random.categorical(
predictions, num_samples=1)[-1, 0].numpy()
input_eval = tf.expand_dims([predicted_id], 0)
text_generated.append(idx2char[predicted_id])
return (start_string + ''.join(text_generated))
# Text generation requires a batch_size=1 model.
keras_model_batch1 = load_model(batch_size=1)
print(generate_text(keras_model_batch1, 'What of TensorFlow Federated, you ask? '))
Downloading data from https://storage.googleapis.com/tff-models-public/dickens_rnn.batch1.kerasmodel 16195584/16193984 [==============================] - 0s 0us/step 16203776/16193984 [==============================] - 0s 0us/step What of TensorFlow Federated, you ask? Sall yesterday. Received the Bailey." "Mr. Lorry, grimmering himself, or low varked thends the winter, and the eyes of Monsieur Defarge. "Let his mind, hon in his life and message; four declare
טען ועבד מראש את נתוני שייקספיר המאוחדים
tff.simulation.datasets
החבילה מספקת מגוון של מערכי נתונים אשר התפצלה "לקוחות", שבו כל אחד מהם מקביל הלקוח במערך במכשיר מסוים שעשוי להשתתף בלמידה Federated.
מערכי נתונים אלה מספקים הפצות נתונים מציאותיים שאינם IID המשכפלים בסימולציה את האתגרים של אימון על נתונים מבוזרים אמיתיים. חלק העיבוד-מראש של נתונים אלה נעשה שימוש בכלים מן הפרויקט ליף ( GitHub ).
train_data, test_data = tff.simulation.datasets.shakespeare.load_data()
מערכי נתונים שסופקו על ידי shakespeare.load_data()
מורכב מרצף של מחרוזת Tensors
, אחד לכל קו המדוברת על ידי דמות מסוימת במחזה של שייקספיר. מפתחות הלקוח מורכבים בשם המחזה הצטרף עם השם של הדמות, כך למשל MUCH_ADO_ABOUT_NOTHING_OTHELLO
תואמת לקווים עבור הדמות אותלו במחזה מהומה על לא מאומה. שים לב שבתרחיש אמיתי של למידה מאוחדת לקוחות לעולם אינם מזוהים או עוקבים אחר מזהים, אך לצורך סימולציה כדאי לעבוד עם מערכי נתונים מקודדים.
כאן, למשל, אנו יכולים להסתכל על כמה נתונים מהמלך ליר:
# Here the play is "The Tragedy of King Lear" and the character is "King".
raw_example_dataset = train_data.create_tf_dataset_for_client(
'THE_TRAGEDY_OF_KING_LEAR_KING')
# To allow for future extensions, each entry x
# is an OrderedDict with a single key 'snippets' which contains the text.
for x in raw_example_dataset.take(2):
print(x['snippets'])
tf.Tensor(b'', shape=(), dtype=string) tf.Tensor(b'What?', shape=(), dtype=string)
כעת אנו משתמשים tf.data.Dataset
טרנספורמציות להכין נתונים'תרגול העוזרת RNN טעון מעל.
# Input pre-processing parameters
SEQ_LENGTH = 100
BATCH_SIZE = 8
BUFFER_SIZE = 100 # For dataset shuffling
# Construct a lookup table to map string chars to indexes,
# using the vocab loaded above:
table = tf.lookup.StaticHashTable(
tf.lookup.KeyValueTensorInitializer(
keys=vocab, values=tf.constant(list(range(len(vocab))),
dtype=tf.int64)),
default_value=0)
def to_ids(x):
s = tf.reshape(x['snippets'], shape=[1])
chars = tf.strings.bytes_split(s).values
ids = table.lookup(chars)
return ids
def split_input_target(chunk):
input_text = tf.map_fn(lambda x: x[:-1], chunk)
target_text = tf.map_fn(lambda x: x[1:], chunk)
return (input_text, target_text)
def preprocess(dataset):
return (
# Map ASCII chars to int64 indexes using the vocab
dataset.map(to_ids)
# Split into individual chars
.unbatch()
# Form example sequences of SEQ_LENGTH +1
.batch(SEQ_LENGTH + 1, drop_remainder=True)
# Shuffle and form minibatches
.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
# And finally split into (input, target) tuples,
# each of length SEQ_LENGTH.
.map(split_input_target))
הערה כי ההיווצרות של הרצפים המקוריים וב ההיווצרות של קבוצות לעיל, אנו משתמשים drop_remainder=True
לפשטות. אמצעי זה כי תווים כלשהם (לקוחות) כי אין לפחות (SEQ_LENGTH + 1) * BATCH_SIZE
תווים של טקסט יצטרך מערכי נתונים ריקים. גישה טיפוסית לטפל בזה תהיה לרפד את האצוות עם אסימון מיוחד, ולאחר מכן להסוות את ההפסד כדי לא לקחת בחשבון את אסימוני הריפוד.
זה יסבך את הדוגמה במקצת, ולכן עבור הדרכה זו אנו משתמשים רק בקבוצות מלאות, כמו ההדרכה הסטנדרטית . עם זאת, בהגדרה המאוחדת בעיה זו משמעותית יותר, מכיוון שלמשתמשים רבים עשויים להיות מערכי נתונים קטנים.
עכשיו אנחנו יכולים preprocess שלנו raw_example_dataset
, ולבדוק את הסוגים:
example_dataset = preprocess(raw_example_dataset)
print(example_dataset.element_spec)
(TensorSpec(shape=(8, 100), dtype=tf.int64, name=None), TensorSpec(shape=(8, 100), dtype=tf.int64, name=None))
הרכיב את המודל ובדוק את הנתונים המעובדים מראש
העמסנו מודל keras עבר קומפילציה, אבל כדי לרוץ keras_model.evaluate
, אנחנו צריכים לאסוף אותו עם הפסד ומדדים. כמו כן, נעשה קומפילציה באופטימיזר, שישמש כמייעל במכשיר ב-Federated Learning.
למדריך המקורי לא היה דיוק ברמת ה-char (חלק מהתחזיות שבו ההסתברות הגבוהה ביותר הונחה על ה-char הבא הנכון). זהו מדד שימושי, אז אנחנו מוסיפים אותו. עם זאת, אנחנו צריכים להגדיר מחלקה ערך חדש זה כי יש את תחזיותינו דרגה 3 (א וקטור של logits עבור כל אחד BATCH_SIZE * SEQ_LENGTH
תחזיות), ו SparseCategoricalAccuracy
מצפה רק 2 תחזיות דרגה.
class FlattenedCategoricalAccuracy(tf.keras.metrics.SparseCategoricalAccuracy):
def __init__(self, name='accuracy', dtype=tf.float32):
super().__init__(name, dtype=dtype)
def update_state(self, y_true, y_pred, sample_weight=None):
y_true = tf.reshape(y_true, [-1, 1])
y_pred = tf.reshape(y_pred, [-1, len(vocab), 1])
return super().update_state(y_true, y_pred, sample_weight)
עכשיו אנחנו יכולים להרכיב מודל, ולהעריך אותו על שלנו example_dataset
.
BATCH_SIZE = 8 # The training and eval batch size for the rest of this tutorial.
keras_model = load_model(batch_size=BATCH_SIZE)
keras_model.compile(
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=[FlattenedCategoricalAccuracy()])
# Confirm that loss is much lower on Shakespeare than on random data
loss, accuracy = keras_model.evaluate(example_dataset.take(5), verbose=0)
print(
'Evaluating on an example Shakespeare character: {a:3f}'.format(a=accuracy))
# As a sanity check, we can construct some completely random data, where we expect
# the accuracy to be essentially random:
random_guessed_accuracy = 1.0 / len(vocab)
print('Expected accuracy for random guessing: {a:.3f}'.format(
a=random_guessed_accuracy))
random_indexes = np.random.randint(
low=0, high=len(vocab), size=1 * BATCH_SIZE * (SEQ_LENGTH + 1))
data = collections.OrderedDict(
snippets=tf.constant(
''.join(np.array(vocab)[random_indexes]), shape=[1, 1]))
random_dataset = preprocess(tf.data.Dataset.from_tensor_slices(data))
loss, accuracy = keras_model.evaluate(random_dataset, steps=10, verbose=0)
print('Evaluating on completely random data: {a:.3f}'.format(a=accuracy))
Downloading data from https://storage.googleapis.com/tff-models-public/dickens_rnn.batch8.kerasmodel 16195584/16193984 [==============================] - 0s 0us/step 16203776/16193984 [==============================] - 0s 0us/step Evaluating on an example Shakespeare character: 0.402000 Expected accuracy for random guessing: 0.012 Evaluating on completely random data: 0.011
כוונן את המודל עם Federated Learning
TFF מסדרת את כל חישובי TensorFlow כך שיוכלו להיות מופעלים בסביבה שאינה של Python (למרות שכרגע, רק זמן ריצה של סימולציה מיושם ב-Python זמין). למרות שאנו פועלים במצב להוט, (TF 2.0), כיום TFF serializes TensorFlow חישובים על ידי בניית ops הכרחי בתוך ההקשר של " with tf.Graph.as_default()
" הצהרה. לפיכך, אנחנו צריכים לספק פונקציה שבה TFF יכול להשתמש כדי להציג את המודל שלנו לתוך גרף שהוא שולט. אנו עושים זאת באופן הבא:
# Clone the keras_model inside `create_tff_model()`, which TFF will
# call to produce a new copy of the model inside the graph that it will
# serialize. Note: we want to construct all the necessary objects we'll need
# _inside_ this method.
def create_tff_model():
# TFF uses an `input_spec` so it knows the types and shapes
# that your model expects.
input_spec = example_dataset.element_spec
keras_model_clone = tf.keras.models.clone_model(keras_model)
return tff.learning.from_keras_model(
keras_model_clone,
input_spec=input_spec,
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=[FlattenedCategoricalAccuracy()])
עכשיו אנחנו מוכנים לבנות תהליך איטרטיבי ממוצעי Federated, אשר נשתמש כדי לשפר את המודל (לפרטים על אלגוריתם ממוצעי Federated, רואה את נייר למידה יעילה-תקשורת של דיפ רשתות מתוך מבוזר נתונים ).
אנו משתמשים במודל Keras מלוקט כדי לבצע הערכה סטנדרטית (לא מאוחדת) לאחר כל סבב של הכשרה מאוחדת. זה שימושי למטרות מחקר בעת ביצוע סימולציה של למידה מאוחדת וישנו מערך נתונים סטנדרטי לבדיקה.
במסגרת ייצור מציאותית ניתן להשתמש באותה טכניקה כדי לקחת מודלים שהוכשרו עם למידה מאוחדת ולהעריך אותם במערך נתונים מרכזי בנצ'מרק למטרות בדיקה או אבטחת איכות.
# This command builds all the TensorFlow graphs and serializes them:
fed_avg = tff.learning.build_federated_averaging_process(
model_fn=create_tff_model,
client_optimizer_fn=lambda: tf.keras.optimizers.SGD(lr=0.5))
הנה הלולאה הפשוטה ביותר האפשרית, שבה אנו מריצים ממוצע מאוחד לסיבוב אחד על לקוח בודד באצווה אחת:
state = fed_avg.initialize()
state, metrics = fed_avg.next(state, [example_dataset.take(5)])
train_metrics = metrics['train']
print('loss={l:.3f}, accuracy={a:.3f}'.format(
l=train_metrics['loss'], a=train_metrics['accuracy']))
loss=4.403, accuracy=0.132
עכשיו בואו נכתוב לולאת הדרכה והערכה קצת יותר מעניינת.
כדי שהסימולציה הזו עדיין תפעל מהר יחסית, אנחנו מתאמנים על אותם שלושה לקוחות בכל סיבוב, רק לוקחים בחשבון שני מיני-באצ'ים לכל אחד.
def data(client, source=train_data):
return preprocess(source.create_tf_dataset_for_client(client)).take(5)
clients = [
'ALL_S_WELL_THAT_ENDS_WELL_CELIA', 'MUCH_ADO_ABOUT_NOTHING_OTHELLO',
]
train_datasets = [data(client) for client in clients]
# We concatenate the test datasets for evaluation with Keras by creating a
# Dataset of Datasets, and then identity flat mapping across all the examples.
test_dataset = tf.data.Dataset.from_tensor_slices(
[data(client, test_data) for client in clients]).flat_map(lambda x: x)
המצב ההתחלתי של הדגם המיוצר על ידי fed_avg.initialize()
מבוסס על initializers האקראי עבור מודל Keras, לא משקולת כי הועמסו, מאז clone_model()
לא שיבוט את המשקולות. כדי להתחיל אימון מדגם מאומן מראש, אנו מגדירים את משקלי הדגם במצב השרת ישירות מהדגם הנטען.
NUM_ROUNDS = 5
# The state of the FL server, containing the model and optimization state.
state = fed_avg.initialize()
# Load our pre-trained Keras model weights into the global model state.
state = tff.learning.state_with_new_model_weights(
state,
trainable_weights=[v.numpy() for v in keras_model.trainable_weights],
non_trainable_weights=[
v.numpy() for v in keras_model.non_trainable_weights
])
def keras_evaluate(state, round_num):
# Take our global model weights and push them back into a Keras model to
# use its standard `.evaluate()` method.
keras_model = load_model(batch_size=BATCH_SIZE)
keras_model.compile(
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=[FlattenedCategoricalAccuracy()])
state.model.assign_weights_to(keras_model)
loss, accuracy = keras_model.evaluate(example_dataset, steps=2, verbose=0)
print('\tEval: loss={l:.3f}, accuracy={a:.3f}'.format(l=loss, a=accuracy))
for round_num in range(NUM_ROUNDS):
print('Round {r}'.format(r=round_num))
keras_evaluate(state, round_num)
state, metrics = fed_avg.next(state, train_datasets)
train_metrics = metrics['train']
print('\tTrain: loss={l:.3f}, accuracy={a:.3f}'.format(
l=train_metrics['loss'], a=train_metrics['accuracy']))
print('Final evaluation')
keras_evaluate(state, NUM_ROUNDS + 1)
Round 0 Eval: loss=3.324, accuracy=0.401 Train: loss=4.360, accuracy=0.155 Round 1 Eval: loss=4.361, accuracy=0.049 Train: loss=4.235, accuracy=0.164 Round 2 Eval: loss=4.219, accuracy=0.177 Train: loss=4.081, accuracy=0.221 Round 3 Eval: loss=4.080, accuracy=0.174 Train: loss=3.940, accuracy=0.226 Round 4 Eval: loss=3.991, accuracy=0.176 Train: loss=3.840, accuracy=0.226 Final evaluation Eval: loss=3.909, accuracy=0.171
עם שינויי ברירת המחדל, לא עשינו מספיק אימונים כדי לעשות הבדל גדול, אבל אם אתה מתאמן יותר על נתונים נוספים של שייקספיר, אתה אמור לראות הבדל בסגנון הטקסט שנוצר עם המודל המעודכן:
# Set our newly trained weights back in the originally created model.
keras_model_batch1.set_weights([v.numpy() for v in keras_model.weights])
# Text generation requires batch_size=1
print(generate_text(keras_model_batch1, 'What of TensorFlow Federated, you ask? '))
What of TensorFlow Federated, you ask? Shalways, I will call your compet with any city brought their faces uncompany," besumed him. "When he sticked Madame Defarge pushed the lamps. "Have I often but no unison. She had probably come,
הרחבות מוצעות
הדרכה זו היא רק הצעד הראשון! הנה כמה רעיונות כיצד תוכל לנסות להרחיב את המחברת הזו:
- כתבו לולאת אימון מציאותית יותר שבה תדגמו לקוחות להתאמן עליהם באופן אקראי.
- השתמש "
.repeat(NUM_EPOCHS)
" על מערכי נתונים הלקוח לנסות תקופות רבות של אימונים מקומיים (למשל, כמו McMahan et. Al. ). ראו גם Federated למידה עבור סיווג תמונה אשר עושה זאת. - שנו את
compile()
הפקודה להתנסות באמצעות אלגוריתמים אופטימיזציה שונות על הלקוח. - נסה את
server_optimizer
טיעון כדיbuild_federated_averaging_process
לנסות אלגוריתמים שונים ליישום עדכוני המודל בשרת. - נסו את
client_weight_fn
טיעון כדי כדיbuild_federated_averaging_process
לנסות במשקלים שונים של לקוחות. העדכונים הלקוח משקולות מחדל במספר דוגמאות על הלקוח, אבל אתה יכול לעשות למשלclient_weight_fn=lambda _: tf.constant(1.0)
.