التعلم الموحد لتوليد النص

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

ويستند هذا البرنامج التعليمي على مفاهيم في التعلم الاتحادية للتصنيف صور البرنامج التعليمي، ويوضح العديد من الطرق الأخرى المفيدة للتعلم الاتحادية.

على وجه الخصوص ، نقوم بتحميل نموذج Keras الذي تم تدريبه مسبقًا ، ونقوم بتنقيحه باستخدام التدريب الموحد على مجموعة بيانات لامركزية (محاكاة). هذا مهم عمليا لعدة أسباب. تجعل القدرة على استخدام النماذج المتسلسلة من السهل مزج التعلم الموحد مع مناهج تعلم الآلة الأخرى. وعلاوة على ذلك، وهذا يسمح استخدام مجموعة متزايدة من نماذج تدريب قبل --- على سبيل المثال، ونماذج التدريب اللغوي من الصفر ونادرا ما الاقتضاء، والعديد من نماذج تدريب ما قبل متاحة الآن على نطاق واسع (انظر، على سبيل المثال، 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 توفر حزمة مجموعة متنوعة من قواعد البيانات التي يتم تقسيمه إلى "عملاء"، حيث كل عميل يتوافق مع مجموعة بيانات على جهاز معين قد تشارك في التعلم الاتحادية.

توفر مجموعات البيانات هذه توزيعات بيانات واقعية بخلاف IID تكرر في محاكاة تحديات التدريب على البيانات اللامركزية الحقيقية. وقد تم بعض ما قبل معالجة هذه البيانات باستخدام أدوات من مشروع ورقة ( جيثب ).

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

مجموعات البيانات التي تقدمها shakespeare.load_data() تتكون من سلسلة من سلسلة Tensors ، واحدة لكل خط يتحدث بها حرف معين في مسرحية شكسبير. تتكون مفاتيح العميل من اسم المسرحية انضم مع اسم الحرف، وذلك على سبيل المثال MUCH_ADO_ABOUT_NOTHING_OTHELLO يتوافق مع خطوط للحرف عطيل في مسرحية الكثير من اللغط حول لا شيء. لاحظ أنه في سيناريو التعلم الموحد الحقيقي ، لا يتم تحديد العملاء أو تعقبهم أبدًا بواسطة المعرفات ، ولكن من أجل المحاكاة ، من المفيد العمل مع مجموعات البيانات ذات المفاتيح.

هنا ، على سبيل المثال ، يمكننا إلقاء نظرة على بعض البيانات من King Lear:

# 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 حرف النص سوف يكون مجموعات البيانات فارغة. تتمثل الطريقة النموذجية لمعالجة ذلك في حشو الدُفعات برمز خاص ، ثم إخفاء الخسارة لعدم أخذ الرموز المميزة للحشو في الاعتبار.

وهذا من شأنه تعقيد سبيل المثال إلى حد ما، لذلك لهذا البرنامج التعليمي ونحن نستخدم فقط دفعات كاملة، كما هو الحال في البرنامج التعليمي القياسية . ومع ذلك ، في الإعداد الموحد ، تكون هذه المشكلة أكثر أهمية ، لأن العديد من المستخدمين قد يكون لديهم مجموعات بيانات صغيرة.

الآن يمكننا أن المعالجة المسبقة لدينا 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 uncompiled، ولكن من أجل تشغيل keras_model.evaluate ، نحن بحاجة إلى ترجمة عليه مع خسارة والمقاييس. سنقوم أيضًا بالتجميع في مُحسِّن ، والذي سيتم استخدامه كمُحسِّن على الجهاز في التعلم المتحد.

لم يكن البرنامج التعليمي الأصلي يحتوي على دقة على مستوى الحرف (جزء التنبؤات حيث تم وضع أعلى احتمال على الحرف التالي الصحيح). هذا مقياس مفيد ، لذا نضيفه. ومع ذلك، نحن بحاجة إلى تعريف فئة متري جديدة لهذا لأن التوقعات لدينا رتبة 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

صقل النموذج باستخدام التعلم المتحد

يقوم 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()])

الآن نحن على استعداد لبناء عملية تكرارية اتحاد المتوسط، والتي سوف نستخدم لتحسين نموذج (لمزيد من التفاصيل على خوارزمية المتوسط الاتحادية، راجع ورقة التعلم من الاتصالات كفاءة شبكات ديب من البيانات اللامركزية ).

نستخدم نموذج 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() تقوم على المهيآت عشوائية لنموذج 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 وآخرون ). انظر أيضا اتحاد التعلم للتصنيف صورة التي تفعل ذلك.
  • تغيير compile() الأمر التجربة باستخدام خوارزميات التحسين مختلفة على العميل.
  • حاول server_optimizer حجة ل build_federated_averaging_process في محاولة خوارزميات مختلفة لتطبيق التحديثات النموذج على الخادم.
  • حاول client_weight_fn حجة لل build_federated_averaging_process في محاولة الأوزان المختلفة للعملاء. تحديثات العميل الأوزان تقصير من جانب عدد من الأمثلة على العميل، ولكن يمكنك القيام به على سبيل المثال client_weight_fn=lambda _: tf.constant(1.0) .