Google I / O'daki önemli notları, ürün oturumlarını, atölyeleri ve daha fazlasını izleyin Oynatma listesine bakın

Metin Üretimi için Federe Öğrenme

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Defteri indirin

Bu eğitim, Görüntü Sınıflandırma için Federe Öğrenme eğitimindeki kavramları temel alır ve birleşik öğrenme için diğer birkaç yararlı yaklaşımı gösterir.

Özellikle, önceden eğitilmiş bir Keras modelini yüklüyoruz ve merkezi olmayan bir veri kümesinde (simüle edilmiş) birleşik eğitim kullanarak onu iyileştiriyoruz. Bu, birkaç nedenden dolayı pratik olarak önemlidir. Serileştirilmiş modelleri kullanma yeteneği, federe öğrenmeyi diğer ML yaklaşımlarıyla karıştırmayı kolaylaştırır. Dahası, bu, artan sayıda önceden eğitilmiş modellerin kullanımına izin verir - örneğin, çok sayıda önceden eğitilmiş model artık yaygın olarak mevcut olduğundan, dil modellerini sıfırdan eğitmek nadiren gereklidir (bkz., Örneğin, TF Hub ). Bunun yerine, önceden eğitilmiş bir modelden başlamak ve belirli bir uygulama için merkezi olmayan verilerin belirli özelliklerine uyarlanarak bunu Federe Öğrenme kullanarak iyileştirmek daha mantıklıdır.

Bu eğitim için, ASCII karakterleri üreten bir RNN ile başlıyoruz ve bunu federe öğrenme yoluyla iyileştiriyoruz. Ayrıca, nihai ağırlıkların orijinal Keras modeline nasıl geri beslenebileceğini göstererek, standart araçlar kullanılarak kolay değerlendirme ve metin oluşturma olanağı sağlıyoruz.

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

Önceden eğitilmiş bir model yükleyin

TensorFlow öğretici Metin oluşturmayı takiben, hevesli yürütme ile bir RNN kullanarak önceden eğitilmiş bir model yüklüyoruz. Ancak The Complete Works of Shakespeare'in eğitimi yerine, Charles Dickens'ın A Tale of Two Cities and A Christmas Carol kitabından alınan metin üzerinde modele önceden eğitim verdik.

Kelime dağarcığını genişletmekten başka, orijinal öğreticiyi değiştirmedik, bu nedenle bu ilk model son teknoloji ürünü değildir, ancak makul tahminler üretir ve eğitim amaçlarımız için yeterlidir. Son model tf.keras.models.save_model(include_optimizer=False) ile kaydedildi.

TFF tarafından sağlanan verilerin birleşik bir sürümünü kullanarak, bu öğreticide Shakespeare için bu modele ince ayar yapmak için federe öğrenmeyi kullanacağız.

Kelime arama tablolarını oluşturun

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

Önceden eğitilmiş modeli yükleyin ve bazı metinler oluşturun

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

Federe Shakespeare Verilerini Yükleme ve Ön İşleme

tff.simulation.datasets paketi, her bir istemcinin federe öğrenmeye katılabilecek belirli bir cihazdaki bir veri kümesine karşılık geldiği "istemciler" olarak bölünmüş çeşitli veri kümeleri sağlar.

Bu veri kümeleri, simülasyonda gerçek merkezi olmayan veriler üzerinde eğitimin zorluklarını kopyalayan gerçekçi IID olmayan veri dağıtımları sağlar. Bu verilerin ön işlemlerinden bazıları Leaf projesindeki ( github ) araçlar kullanılarak yapıldı.

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

shakespeare.load_data() tarafından sağlanan veri kümeleri, bir Shakespeare oyununda belirli bir karakter tarafından söylenen her satır için bir tane olmak üzere bir dizi Tensors dizisinden oluşur. İstemci anahtarları, karakterin adıyla birleştirilen oyunun adından oluşur, bu nedenle örneğin MUCH_ADO_ABOUT_NOTHING_OTHELLO , Much Ado About Nothing oyununda Othello karakterinin satırlarına karşılık gelir. Gerçek bir federe öğrenme senaryosunda istemcilerin hiçbir zaman kimliklerle tanımlanmadığını veya izlenmediğini, ancak simülasyon için anahtarlı veri kümeleriyle çalışmanın yararlı olduğunu unutmayın.

Burada, örneğin, King Lear'dan bazı verilere bakabiliriz:

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

Şimdi bu verileri yukarıda yüklenen chartf.data.Dataset için hazırlamak içintf.data.Dataset dönüşümlerini kullanıyoruz.

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

Orijinal dizilerin oluşumunda ve yukarıdaki grupların oluşumunda, basitlik için drop_remainder=True kullandığımıza drop_remainder=True . Bu, en az (SEQ_LENGTH + 1) * BATCH_SIZE metin karakterine sahip olmayan tüm karakterlerin (istemcilerin) boş veri kümelerine sahip olacağı anlamına gelir. Bunu ele almak için tipik bir yaklaşım, partileri özel bir jetonla doldurmak ve ardından doldurma jetonlarını hesaba katmamak için kaybı maskelemek olacaktır.

Bu, örneği biraz karmaşıklaştıracaktır, bu nedenle bu eğitim için standart eğitimde olduğu gibi yalnızca tam grupları kullanıyoruz. Ancak, federe ayarda bu sorun daha önemlidir, çünkü birçok kullanıcı küçük veri kümelerine sahip olabilir.

Şimdi raw_example_dataset önceden raw_example_dataset ve türleri kontrol edebiliriz:

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

Modeli derleyin ve önceden işlenmiş veriler üzerinde test edin

Derlenmemiş bir keras modeli yükledik, ancak keras_model.evaluate çalıştırmak için onu bir kayıp ve keras_model.evaluate gerekiyor. Ayrıca Federated Learning'de cihaz üzerinde iyileştirici olarak kullanılacak bir optimize edici de derleyeceğiz.

Orijinal eğitimde karakter düzeyinde doğruluk yoktu (en yüksek olasılığın doğru sonraki karaktere konulduğu tahminlerin oranı). Bu yararlı bir ölçüdür, bu yüzden onu ekliyoruz. Bununla birlikte, bunun için yeni bir metrik sınıf tanımlamamız gerekir çünkü tahminlerimiz 3. BATCH_SIZE * SEQ_LENGTH sahiptir ( BATCH_SIZE * SEQ_LENGTH tahminlerinin her biri için bir BATCH_SIZE * SEQ_LENGTH ) ve SparseCategoricalAccuracy yalnızca 2. sıra tahminleri bekler.

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)

Şimdi bir model derleyebilir ve onu example_dataset değerlendirebiliriz.

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

Modele Federe Öğrenme ile ince ayar yapın

TFF, tüm TensorFlow hesaplamalarını, potansiyel olarak Python dışı bir ortamda çalıştırılabilmeleri için serileştirir (şu anda yalnızca Python'da uygulanan bir simülasyon çalışma zamanı mevcut olsa da). İstekli modda (TF 2.0) çalıştırıyor olsak da, şu anda TFF " with tf.Graph.as_default() " ifadesi bağlamında gerekli işlemleri oluşturarak TensorFlow hesaplamalarını serileştiriyor. Bu nedenle, modelimizi kontrol ettiği bir grafiğe sokmak için TFF'nin kullanabileceği bir işlev sağlamamız gerekir. Bunu şu şekilde yapıyoruz:

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

Şimdi, modeli iyileştirmek için kullanacağımız bir Federe Ortalama Alma yinelemeli süreci oluşturmaya hazırız (Birleşik Ortalama algoritmasıyla ilgili ayrıntılar için, Merkezi Olmayan Verilerden Derin Ağların İletişim-Verimli Öğrenimi adlı makaleye bakın).

Her bir federe eğitim turundan sonra standart (federe olmayan) değerlendirme yapmak için derlenmiş bir Keras modeli kullanıyoruz. Bu, simüle edilmiş birleşik öğrenme yaparken araştırma amaçları için kullanışlıdır ve standart bir test veri kümesi vardır.

Gerçekçi bir üretim ortamında bu aynı teknik, federe öğrenme ile eğitilmiş modelleri almak ve bunları test veya kalite güvence amacıyla merkezi bir kıyaslama veri kümesinde değerlendirmek için kullanılabilir.

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

İşte, tek bir toplu iş üzerinde tek bir istemcide bir tur için birleşik ortalamayı çalıştırdığımız olası en basit döngü:

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

Şimdi biraz daha ilginç bir eğitim ve değerlendirme döngüsü yazalım.

Bu simülasyonun hala nispeten hızlı çalışması için, her turda aynı üç müşteriye eğitim veriyoruz, her biri için sadece iki mini parti düşünüyoruz.

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() tarafından üretilen modelin başlangıç ​​durumu, fed_avg.initialize() ağırlıkları klonlamadığından, yüklenen ağırlıklara değil, clone_model() modeli için rastgele başlatıcılara dayanır. Önceden eğitilmiş bir modelden eğitime başlamak için, sunucu durumunda model ağırlıklarını doğrudan yüklenen modelden belirliyoruz.

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

Varsayılan değişikliklerle, büyük bir fark yaratmak için yeterince eğitim yapmadık, ancak daha fazla Shakespeare verisi üzerinde daha uzun süre eğitim alırsanız, güncellenmiş modelle oluşturulan metnin tarzında bir fark görmeniz gerekir:

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

Önerilen uzantılar

Bu eğitim sadece ilk adımdır! İşte bu defteri genişletmeyi nasıl deneyebileceğinize dair bazı fikirler:

  • Müşterileri rastgele eğitmek için örneklediğiniz daha gerçekçi bir eğitim döngüsü yazın.
  • Birden çok yerel eğitim dönemini denemek için istemci veri kümelerinde " .repeat(NUM_EPOCHS) " kullanın (örneğin, McMahan ve diğerlerinde olduğu gibi). Ayrıca, bunu yapan Görüntü Sınıflandırması için Federe Öğrenmeye bakın.
  • İstemcide farklı optimizasyon algoritmaları kullanmayı denemek için compile() komutunu değiştirin.
  • server_optimizer model güncellemelerini uygulamak için farklı algoritmalar denemek için build_federated_averaging_process için server_optimizer bağımsız değişkenini deneyin.
  • client_weight_fn farklı ağırlıklarını denemek için build_federated_averaging_process için client_weight_fn argümanını deneyin. Varsayılan, istemci güncellemelerini istemcideki örnek sayısına göre ağırlıklandırır, ancak örneğin client_weight_fn=lambda _: tf.constant(1.0) .