Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Pembelajaran Federasi untuk Pembuatan Teks

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub

Tutorial ini dibuat berdasarkan konsep dalam tutorial Pembelajaran Federasi untuk Klasifikasi Gambar , dan mendemonstrasikan beberapa pendekatan berguna lainnya untuk pembelajaran gabungan.

Secara khusus, kami memuat model Keras yang telah dilatih sebelumnya, dan menyempurnakannya menggunakan pelatihan federasi pada set data terdesentralisasi (tersimulasi). Ini secara praktis penting karena beberapa alasan. Kemampuan untuk menggunakan model berseri memudahkan untuk menggabungkan pembelajaran federasi dengan pendekatan ML lainnya. Lebih lanjut, hal ini memungkinkan penggunaan lebih banyak model yang dilatih sebelumnya --- misalnya, model bahasa pelatihan dari awal jarang diperlukan, karena banyak model terlatih sekarang tersedia secara luas (lihat, misalnya, TF Hub ). Sebaliknya, lebih masuk akal untuk memulai dari model yang sudah dilatih sebelumnya, dan menyempurnakannya menggunakan Federated Learning, menyesuaikan dengan karakteristik tertentu dari data yang didesentralisasi untuk aplikasi tertentu.

Untuk tutorial ini, kita mulai dengan RNN yang menghasilkan karakter ASCII, dan menyempurnakannya melalui pembelajaran federasi. Kami juga menunjukkan bagaimana bobot akhir dapat diumpankan kembali ke model Keras asli, memungkinkan evaluasi yang mudah dan pembuatan teks menggunakan alat standar.

!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!'

Muat model terlatih

Kami memuat model yang telah dilatih sebelumnya mengikuti pembuatan Teks tutorial TensorFlow menggunakan RNN dengan eager execution . Namun, alih-alih melatih The Complete Works of Shakespeare , kami melatih model tersebut pada teks dari A Tale of Two Cities and A Christmas Carol karya Charles Dickens.

Selain memperluas kosakata, kami tidak mengubah tutorial aslinya, jadi model awal ini tidak canggih, tetapi menghasilkan prediksi yang masuk akal dan cukup untuk tujuan tutorial kami. Model terakhir disimpan dengan tf.keras.models.save_model(include_optimizer=False) .

Kami akan menggunakan pembelajaran federasi untuk menyempurnakan model ini untuk Shakespeare dalam tutorial ini, menggunakan versi data federasi yang disediakan oleh TFF.

Buat tabel pencarian kosakata

# 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)
.dll

Muat model terlatih dan buat beberapa teks

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 

Memuat dan Memproses Data Shakespeare Federasi

Paket tff.simulation.datasets menyediakan berbagai kumpulan data yang dibagi menjadi "klien", di mana setiap klien sesuai dengan kumpulan data di perangkat tertentu yang mungkin berpartisipasi dalam pembelajaran federasi.

Kumpulan data ini memberikan distribusi data non-IID realistis yang mereplikasi tantangan pelatihan tentang data desentralisasi yang nyata dalam simulasi. Beberapa pra-pengolahan data ini dilakukan dengan menggunakan alat dari proyek Leaf ( github ).

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

Set data yang disediakan oleh shakespeare.load_data() terdiri dari urutan Tensors string, satu untuk setiap baris yang diucapkan oleh karakter tertentu dalam drama Shakespeare. Kunci klien terdiri dari nama permainan yang digabungkan dengan nama karakter, jadi misalnya MUCH_ADO_ABOUT_NOTHING_OTHELLO sesuai dengan baris untuk karakter Othello dalam permainan Much Ado About Nothing . Perhatikan bahwa dalam skenario pembelajaran federasi nyata, klien tidak pernah diidentifikasi atau dilacak oleh id, tetapi untuk simulasi akan berguna untuk bekerja dengan kumpulan data yang dikunci.

Di sini, misalnya, kita bisa melihat beberapa data dari 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)

Sekarang kita menggunakan transformasitf.data.Dataset untuk menyiapkan data ini guna melatih karakter RNN yang dimuat di atas.

# 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))

Perhatikan bahwa dalam pembentukan urutan asli dan dalam pembentukan batch di atas, kami menggunakan drop_remainder=True untuk kesederhanaan. Ini berarti bahwa setiap karakter (klien) yang tidak memiliki setidaknya (SEQ_LENGTH + 1) * BATCH_SIZE karakter teks akan memiliki kumpulan data kosong. Pendekatan umum untuk mengatasi hal ini adalah dengan mengisi batch dengan token khusus, dan kemudian menutupi kerugian agar tidak memperhitungkan token padding.

Ini akan memperumit contoh, jadi untuk tutorial ini kami hanya menggunakan batch penuh, seperti dalam tutorial standar . Namun, dalam pengaturan federasi, masalah ini lebih signifikan, karena banyak pengguna mungkin memiliki kumpulan data kecil.

Sekarang kita dapat melakukan praproses raw_example_dataset kita, dan memeriksa jenisnya:

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))

Kompilasi model dan uji pada data yang telah diproses sebelumnya

Kita memuat model keras yang tidak dikompilasi, tetapi untuk menjalankan keras_model.evaluate , kita perlu mengkompilasinya dengan kerugian dan metrik. Kami juga akan mengompilasi dalam pengoptimal, yang akan digunakan sebagai pengoptimal pada perangkat dalam Pembelajaran Federasi.

Tutorial asli tidak memiliki akurasi level karakter (pecahan prediksi di mana probabilitas tertinggi diletakkan pada karakter berikutnya yang benar). Ini adalah metrik yang berguna, jadi kami menambahkannya. Namun, kami perlu menentukan kelas metrik baru untuk ini karena prediksi kami memiliki peringkat 3 (vektor logit untuk setiap prediksi BATCH_SIZE * SEQ_LENGTH ), dan SparseCategoricalAccuracy hanya mengharapkan prediksi peringkat 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)

Sekarang kita dapat mengkompilasi model, dan mengevaluasinya di example_dataset kita.

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

Sempurnakan model dengan Federated Learning

TFF membuat serial semua komputasi TensorFlow sehingga berpotensi dapat dijalankan di lingkungan non-Python (meskipun saat ini, hanya runtime simulasi yang diimplementasikan dengan Python yang tersedia). Meskipun kita berjalan dalam mode eager, (TF 2.0), saat ini TFF membuat serial komputasi TensorFlow dengan membuat operasi yang diperlukan di dalam konteks pernyataan " with tf.Graph.as_default() ". Jadi, kita perlu menyediakan fungsi yang dapat digunakan TFF untuk memperkenalkan model kita ke dalam grafik yang dikontrolnya. Kami melakukan ini sebagai berikut:

# 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()])

Sekarang kita siap untuk membuat proses iteratif Federated Averaging, yang akan kita gunakan untuk meningkatkan model (untuk detail tentang algoritme Federated Averaging, lihat makalah Communication-Efficient Learning of Deep Networks from Decentralized Data ).

Kami menggunakan model Keras terkompilasi untuk melakukan evaluasi standar (non-federasi) setelah setiap putaran pelatihan federasi. Ini berguna untuk tujuan penelitian ketika melakukan simulasi pembelajaran federasi dan terdapat set data pengujian standar.

Dalam pengaturan produksi yang realistis, teknik yang sama ini dapat digunakan untuk mengambil model yang dilatih dengan pembelajaran federasi dan mengevaluasinya pada kumpulan data tolok ukur terpusat untuk tujuan pengujian atau jaminan kualitas.

# 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))

Berikut ini loop yang paling sederhana, di mana kami menjalankan rata-rata federasi untuk satu putaran pada satu klien dalam satu batch:

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

Sekarang mari kita menulis putaran pelatihan dan evaluasi yang sedikit lebih menarik.

Agar simulasi ini tetap berjalan relatif cepat, kami berlatih pada tiga klien yang sama setiap putaran, hanya mempertimbangkan dua minibatch untuk masing-masing.

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)

Status awal model yang dihasilkan oleh fed_avg.initialize() didasarkan pada penginisialisasi acak untuk model Keras, bukan bobot yang dimuat, karena clone_model() tidak mengkloning bobot. Untuk memulai pelatihan dari model terlatih, kami menetapkan bobot model dalam status server langsung dari model yang dimuat.

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

Dengan perubahan default, kami belum melakukan cukup pelatihan untuk membuat perbedaan besar, tetapi jika Anda berlatih lebih lama pada lebih banyak data Shakespeare, Anda akan melihat perbedaan dalam gaya teks yang dihasilkan dengan model yang diperbarui:

# 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, 

Ekstensi yang disarankan

Tutorial ini hanyalah langkah pertama! Berikut adalah beberapa ide tentang bagaimana Anda dapat mencoba memperluas notebook ini:

  • Tulis lingkaran pelatihan yang lebih realistis di mana Anda mengambil sampel klien untuk dilatih secara acak.
  • Gunakan " .repeat(NUM_EPOCHS) " pada dataset klien untuk mencoba beberapa periode pelatihan lokal (misalnya, seperti dalam McMahan et. Al. ). Lihat juga Pembelajaran Federasi untuk Klasifikasi Gambar yang melakukan ini.
  • Ubah perintah compile() untuk bereksperimen dengan menggunakan algoritme pengoptimalan yang berbeda pada klien.
  • Coba argumen server_optimizer untuk build_federated_averaging_process untuk mencoba algoritma yang berbeda untuk menerapkan pembaruan model di server.
  • Coba argumen client_weight_fn ke untuk build_federated_averaging_process untuk mencoba bobot klien yang berbeda. Bobot default klien diperbarui dengan jumlah contoh pada klien, tetapi Anda dapat melakukannya misalnya client_weight_fn=lambda _: tf.constant(1.0) .