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 untukbuild_federated_averaging_process
untuk mencoba algoritma yang berbeda untuk menerapkan pembaruan model yang di server. - Coba
client_weight_fn
argumen untuk kebuild_federated_averaging_process
untuk mencoba bobot yang berbeda dari klien. Update bobot standar klien dengan jumlah contoh pada klien, tetapi Anda dapat melakukan misalnyaclient_weight_fn=lambda _: tf.constant(1.0)
.