Pembelajaran Terpadu untuk Pembuatan Teks

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Tutorial ini dibangun di atas konsep-konsep dalam Pembelajaran Federated untuk Gambar Klasifikasi tutorial, dan menunjukkan beberapa pendekatan lain yang berguna untuk belajar federasi.

Secara khusus, kami memuat model Keras yang telah dilatih sebelumnya, dan menyempurnakannya menggunakan pelatihan gabungan pada set data terdesentralisasi (simulasi). Ini praktis penting karena beberapa alasan. Kemampuan untuk menggunakan model serial memudahkan untuk menggabungkan pembelajaran gabungan dengan pendekatan ML lainnya. Selanjutnya, ini memungkinkan penggunaan berbagai peningkatan model pra-dilatih --- misalnya, model bahasa pelatihan dari awal jarang diperlukan, karena banyak pra-dilatih model sekarang banyak tersedia (lihat, misalnya, TF Hub ). Alih-alih, lebih masuk akal untuk memulai dari model yang telah dilatih sebelumnya, dan menyempurnakannya menggunakan Pembelajaran Federasi, beradaptasi dengan karakteristik tertentu dari data terdesentralisasi untuk aplikasi tertentu.

Untuk tutorial ini, kita mulai dengan RNN yang menghasilkan karakter ASCII, dan menyempurnakannya melalui pembelajaran gabungan. Kami juga menunjukkan bagaimana bobot akhir dapat diumpankan kembali ke model Keras asli, memungkinkan evaluasi dan pembuatan teks yang mudah 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 yang sudah terlatih

Kami memuat model yang pra-dilatih mengikuti TensorFlow tutorial generasi Teks menggunakan RNN dengan eksekusi bersemangat . Namun, bukan pelatihan tentang The Complete Works of Shakespeare , kami pra-dilatih model pada teks dari Charles Dickens' A Tale of Two Cities dan A Christmas Carol .

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

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

Hasilkan tabel pencarian vocab

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

Muat model yang sudah 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

Muat dan Praproses Data Federasi Shakespeare

The tff.simulation.datasets paket menyediakan berbagai dataset yang dibagi menjadi "klien", di mana setiap klien dapat disamakan dengan dataset pada perangkat tertentu yang mungkin berpartisipasi dalam belajar federasi.

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

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

Dataset yang disediakan oleh shakespeare.load_data() terdiri dari urutan string Tensors , satu untuk setiap baris diucapkan oleh karakter tertentu dalam sebuah drama Shakespeare. Kunci klien terdiri dari nama bermain bergabung dengan nama karakter, jadi misalnya MUCH_ADO_ABOUT_NOTHING_OTHELLO sesuai dengan garis-garis untuk karakter Othello dalam bermain Much Ado About Nothing. Perhatikan bahwa dalam skenario pembelajaran gabungan 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 dapat 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)

Kami sekarang menggunakan tf.data.Dataset transformasi untuk mempersiapkan data ini untuk melatih char RNN 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, kita menggunakan drop_remainder=True untuk kesederhanaan. Ini berarti bahwa setiap karakter (klien) yang tidak memiliki setidaknya (SEQ_LENGTH + 1) * BATCH_SIZE chars teks akan memiliki dataset kosong. Pendekatan tipikal untuk mengatasi hal ini adalah dengan mengisi batch dengan token khusus, dan kemudian menutupi kerugian untuk tidak memperhitungkan token padding.

Hal ini akan menyulitkan contoh agak, jadi untuk tutorial ini kita hanya menggunakan batch penuh, seperti dalam tutorial standar . Namun, dalam pengaturan gabungan, masalah ini lebih signifikan, karena banyak pengguna mungkin memiliki kumpulan data kecil.

Sekarang kita bisa preprocess kami raw_example_dataset , dan memeriksa jenis:

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

Kami dimuat model keras uncompiled, tetapi untuk jangka keras_model.evaluate , kita perlu melakukan kompilasi dengan kerugian dan metrik. Kami juga akan mengkompilasi dalam pengoptimal, yang akan digunakan sebagai pengoptimal di perangkat dalam Pembelajaran Federasi.

Tutorial asli tidak memiliki akurasi tingkat karakter (fraksi prediksi di mana probabilitas tertinggi diletakkan pada karakter berikutnya yang benar). Ini adalah metrik yang berguna, jadi kami menambahkannya. Namun, kita perlu mendefinisikan kelas metrik baru untuk ini karena prediksi kami memiliki peringkat 3 (vektor logits untuk masing-masing BATCH_SIZE * SEQ_LENGTH prediksi), dan SparseCategoricalAccuracy mengharapkan hanya peringkat 2 prediksi.

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 mengevaluasi pada kami 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

Sempurnakan model dengan Pembelajaran Federasi

TFF membuat serial semua komputasi TensorFlow sehingga berpotensi dijalankan di lingkungan non-Python (meskipun saat ini, hanya runtime simulasi yang diimplementasikan dengan Python yang tersedia). Meskipun kita berjalan dalam modus bersemangat, (TF 2.0), saat ini TFF serializes TensorFlow perhitungan dengan membangun ops diperlukan dalam konteks " with tf.Graph.as_default() " pernyataan. 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 membangun sebuah proses berulang Federasi Averaging, yang akan kita gunakan untuk meningkatkan model (untuk rincian tentang algoritma Averaging Federasi, lihat kertas Komunikasi-Efisien Belajar Deep Networks dari Data Desentralisasi ).

Kami menggunakan model Keras yang dikompilasi untuk melakukan evaluasi standar (non-federasi) setelah setiap putaran pelatihan federasi. Ini berguna untuk tujuan penelitian ketika melakukan pembelajaran federasi simulasi dan ada dataset uji standar.

Dalam pengaturan produksi yang realistis, teknik yang sama ini dapat digunakan untuk mengambil model yang dilatih dengan pembelajaran gabungan dan mengevaluasinya pada kumpulan data benchmark 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 adalah kemungkinan loop paling sederhana, di mana kami menjalankan rata-rata federasi untuk satu putaran pada satu klien pada 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 lingkaran pelatihan dan evaluasi yang sedikit lebih menarik.

Agar simulasi ini masih berjalan relatif cepat, kami melatih 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)

Keadaan awal dari model yang diproduksi oleh fed_avg.initialize() didasarkan pada initializers acak untuk model Keras, bukan bobot yang dimuat, karena clone_model() tidak clone bobot. Untuk memulai pelatihan dari model yang telah dilatih sebelumnya, 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 pelatihan yang cukup 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 buku catatan ini:

  • Tulis lingkaran pelatihan yang lebih realistis di mana Anda mengambil sampel klien untuk dilatih secara acak.
  • Gunakan " .repeat(NUM_EPOCHS) " pada dataset client untuk mencoba beberapa zaman pelatihan lokal (misalnya, seperti dalam McMahan et. Al. ). Lihat juga Federated Belajar untuk Klasifikasi Gambar yang melakukan hal ini.
  • Mengubah compile() perintah untuk percobaan dengan menggunakan algoritma optimasi yang berbeda pada klien.
  • Coba server_optimizer argumen untuk build_federated_averaging_process untuk mencoba algoritma yang berbeda untuk menerapkan pembaruan model yang di server.
  • Coba client_weight_fn argumen untuk ke build_federated_averaging_process untuk mencoba bobot yang berbeda dari klien. Update bobot standar klien dengan jumlah contoh pada klien, tetapi Anda dapat melakukan misalnya client_weight_fn=lambda _: tf.constant(1.0) .