דף זה תורגם על ידי Cloud Translation API.
Switch to English

למידה מאוחדת ליצירת טקסטים

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

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

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

עבור הדרכה זו, אנו מתחילים עם 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 עם ביצוע להוט . עם זאת, במקום להתאמן על היצירות המלאות של שייקספיר , אימנו מראש את המודל לטקסט מתוך סיפורו של צ'רלס דיקנס " סיפור של שתי ערים וקרול חג המולד" .

מלבד הרחבת אוצר המילים, לא שינינו את ההדרכה המקורית, כך שמודל ראשוני זה אינו חדיש, אך הוא מייצר תחזיות סבירות ומספיק למטרות ההדרכה שלנו. המודל הסופי נשמר באמצעות 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 מספקת מגוון מערכי נתונים המפוצלים tff.simulation.datasets ", כאשר כל לקוח תואם מערך נתונים במכשיר מסוים שעשוי להשתתף בלמידה מאוחדת.

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

train_data, test_data = tff.simulation.datasets.shakespeare.load_data()

מערכי הנתונים המסופקים על ידי shakespeare.load_data() מורכבים מרצף של מיתרי Tensors , אחד לכל שורה המדוברת על ידי דמות מסוימת במחזה שייקספיר. מקשי הלקוח מורכבים משם המחזה MUCH_ADO_ABOUT_NOTHING_OTHELLO לשם הדמות, כך לדוגמא MUCH_ADO_ABOUT_NOTHING_OTHELLO תואם את השורות לדמות אותלו במחזה Much Ado About Nothing . שים לב כי בתרחיש למידה מאוחד אמיתי הלקוחות לעולם אינם מזוהים או עוקבים אחריהם על ידי מזהים, אך לצורך סימולציה כדאי לעבוד עם מערכי נתונים עם מפתח.

הנה, למשל, אנו יכולים להסתכל בכמה נתונים של המלך ליר:

# 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 תווים של טקסט יהיו (SEQ_LENGTH + 1) * BATCH_SIZE נתונים ריקים. גישה אופיינית לטפל בכך תהיה לרפד את האצוות באסימון מיוחד, ואז להסוות את ההפסד שלא לקחת את אסימוני הריפוד בחשבון.

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

כעת אנו יכולים raw_example_dataset מראש את 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_model.evaluate , אך כדי להריץ keras_model.evaluate , עלינו לקמפל אותו עם הפסד וערכים. אנו נרכיב גם במיטוב אופטימיזציה, שישמש ככלי האופטימיזציה במכשיר בלימוד פדרציה.

במדריך המקורי לא היה דיוק ברמת החיוב (שבר התחזיות שבו ההסתברות הגבוהה ביותר הועלה על התו הבא הנכון). זהו מדד שימושי, לכן אנו מוסיפים אותו. עם זאת, עלינו להגדיר מחלקת מדדים חדשה לכך מכיוון שהתחזיות שלנו כוללות דרגה 3 (וקטור של לוגיות לכל אחת 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

כוונן את המודל בעזרת למידה מאוחדת

TFF מסדרת את כל חישובי TensorFlow כך שהם יכולים להיות מנוהלים בסביבה שאינה Python (למרות שכרגע, רק זמן ריצה של סימולציה המיושם ב- Python זמין). למרות שאנחנו פועלים במצב להוט, (TF 2.0), כרגע TFF מסדרת את חישובי TensorFlow על ידי בניית האופציות הדרושות בהקשר של הצהרה " 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 Averaging, בו נשתמש כדי לשפר את המודל (לפרטים אודות האלגוריתם של Federated Averaging, עיין במאמר למידה יעילה של תקשורת של רשתות עמוקות מנתונים מבוזרים ).

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

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

# 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() מבוסס על האתחילים האקראיים של מודל 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. ). ראה גם למידה מאוחדת לסיווג תמונות שעושה זאת.
  • שנה את הפקודה compile() כדי להתנסות בה באמצעות אלגוריתמי אופטימיזציה שונים על הלקוח.
  • נסה את server_optimizer טיעון כדי build_federated_averaging_process לנסות אלגוריתמים שונים ליישום עדכוני המודל בשרת.
  • נסה את הארגומנט client_weight_fn ל- build_federated_averaging_process כדי לנסות שקלול אחר של הלקוחות. ברירת המחדל משקולות עדכוני לקוח לפי מספר הדוגמאות על הלקוח, אך ניתן לבצע למשל client_weight_fn=lambda _: tf.constant(1.0) .