Esta página foi traduzida pela API Cloud Translation.
Switch to English

Federated Aprendizagem para a geração de texto

Ver TensorFlow.org Executar no Google Colab Ver fonte no GitHub

Este tutorial baseia-se nos conceitos da Federated Aprendizagem para Imagem Classification tutorial, e demonstra várias outras abordagens úteis para a aprendizagem federado.

Em particular, nós carregamos um modelo Keras previamente treinados, e refiná-la usando o treinamento federado em um (simulado) conjunto de dados descentralizada. Isso é praticamente importante por várias razões. A capacidade de usar serializado modelos faz com que seja fácil de misturar aprendizagem federada com outra ML aproxima. Além disso, este permite o uso de uma gama crescente de modelos pré-treinados --- por exemplo, modelos de linguagem formação a partir do zero raramente é necessário, como inúmeros modelos pré-treinados estão agora amplamente disponíveis (ver, por exemplo, TF Hub ). Em vez disso, faz mais sentido começar a partir de um modelo de pré-treinados, e refiná-la usando Federated aprendizagem, adaptando-se às características particulares dos dados descentralizadas para uma determinada aplicação.

Para este tutorial, vamos começar com uma RNN que gera caracteres ASCII, e refiná-lo através da aprendizagem federado. Também mostramos como os pesos finais pode ser alimentado de volta para o modelo Keras original, permitindo fácil geração de avaliação e de texto usando ferramentas padrão.

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

Carregar um modelo de pré-formados

Nós carregar um modelo que foi pré-treinados após a TensorFlow tutorial geração de texto usando uma RNN com a execução ansioso . No entanto, ao invés de formação sobre As Obras Completas de Shakespeare , nós pré-treinado o modelo sobre o texto do Charles Dickens' Um Conto de Duas Cidades e A Christmas Carol .

Além da expansão da vocabularly, nós não modificar o tutorial original, de modo que este modelo inicial não é state-of-the-art, mas produz previsões razoáveis ​​e é suficiente para os nossos propósitos tutorial. O modelo final foi salvo com tf.keras.models.save_model(include_optimizer=False) .

Vamos usar a aprendizagem federado para afinar este modelo de Shakespeare neste tutorial, usando uma versão federado dos dados fornecidos pela TFF.

Gerar as tabelas de pesquisa 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)
 

Carregue o modelo pré-formados e gerar algum texto

 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 

Carga e Preprocess a Federated Shakespeare Dados

O tff.simulation.datasets pacote fornece uma variedade de conjuntos de dados que são divididas em "clientes", onde cada cliente corresponde a um conjunto de dados em um dispositivo em particular que possam participar na aprendizagem federado.

Esses conjuntos de dados fornecem distribuições realistas de dados não-IID que replicam na simulação os desafios da formação em dados descentralizados reais. Alguns dos pré-processamento desses dados foi feito utilizando ferramentas do projeto Folha ( github ).

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

Os conjuntos de dados fornecidos pelo shakespeare.load_data() consistem de uma sequência de cordas Tensors , um para cada linha falada por um caráter particular em uma peça de Shakespeare. As chaves do cliente consistem no nome do jogo se juntou com o nome do personagem, assim, por exemplo MUCH_ADO_ABOUT_NOTHING_OTHELLO corresponde às linhas para o personagem Otelo na peça Muito Barulho por Nada. Note-se que em um aprendizado federado verdadeira clientes cenário nunca são identificado ou rastreado por ids, mas para a simulação é útil para trabalhar com conjuntos de dados com chave.

Aqui, por exemplo, podemos olhar para alguns dados de Rei 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)

Agora usamos tf.data.Dataset transformações para preparar esses dados para treinar o caractere RNN carregado acima.

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

Note-se que na formação das sequcias originais e na formação de lotes acima, usamos drop_remainder=True para simplicidade. Isto significa que qualquer caracteres (clientes) que não têm pelo menos (SEQ_LENGTH + 1) * BATCH_SIZE caracteres de texto terá conjuntos de dados vazios. Uma abordagem típica para resolver este seria pad os lotes com um símbolo especial, e, em seguida, mascarar a perda de não tomar as fichas de preenchimento em conta.

Isso iria complicar o exemplo um pouco, portanto, para este tutorial só usamos lotes completos, como no tutorial padrão . No entanto, no cenário federado este problema é mais significativo, porque muitos usuários podem ter pequenos conjuntos de dados.

Agora podemos pré-processar o nosso raw_example_dataset , e verificar os tipos:

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

Compilar o modelo e teste nos dados pré-processados

Nós carregamos um modelo keras uncompiled, mas a fim de executar keras_model.evaluate , precisamos compilá-lo com uma perda e métricas. Também será compilado em um optimizador, o qual será utilizado como o optimizador no dispositivo em Federados aprendizagem.

O tutorial original não tinha precisão de nível char (a fração de previsões, onde a maior probabilidade foi colocado na próxima caracteres correto). Esta é uma métrica útil, por isso adicioná-lo. No entanto, precisamos definir uma nova classe métrica para isso, porque as nossas previsões tem classificação 3 (um vetor de logits para cada um dos BATCH_SIZE * SEQ_LENGTH previsões), e SparseCategoricalAccuracy espera apenas Posição 2 previsões.

 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)
 

Agora podemos compilar um modelo, e avaliá-lo em nosso 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

Afinar o modelo com Federated Aprendizagem

TFF serializa todos os cálculos TensorFlow para que possam potencialmente ser executado em um ambiente não-Python (embora, no momento, apenas uma runtime simulação implementada em Python está disponível). Mesmo que estamos executando no modo ansioso, (TF 2.0), atualmente TFF Serializa TensorFlow cálculos construindo os ops necessárias dentro do contexto de um " with tf.Graph.as_default() " declaração. Assim, precisamos fornecer uma função que TFF pode usar para apresentar o nosso modelo em um gráfico que controla. Fazemos isso da seguinte forma:

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

Agora estamos prontos para construir um processo iterativo Federated Média, que usaremos para melhorar o modelo (para mais detalhes sobre o algoritmo Média Federados, ver o papel de Aprendizagem Comunicação Eficiente de profunda Networks partir descentralizada de dados ).

Nós usamos um modelo Keras compilado para realizar a avaliação padrão (não-federado) após cada rodada de treinamento federado. Isso é útil para fins de pesquisa ao fazer simulado de aprendizagem federado e há um conjunto de dados de teste padrão.

Em uma produção realista definindo essa mesma técnica pode ser usada para tirar modelos treinados com a aprendizagem federado e avaliá-los em um conjunto de dados de referência centralizada para fins de teste ou garantia de qualidade.

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

Aqui é o mais simples circuito possível, onde corremos média federado para uma rodada em um único cliente em um único lote:

 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

Agora vamos escrever uma formação e avaliação de loop um pouco mais interessante.

Assim que esta simulação ainda corre de forma relativamente rápida, nós treinamos nos mesmos três clientes cada rodada, considerando apenas dois minibatches para cada um.

 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)
 

O estado inicial do modelo produzido por fed_avg.initialize() baseia-se nos inicializadores aleatórios para o modelo Keras, não os pesos que foram carregados, uma vez que clone_model() não faz clone os pesos. Para começar a treinar a partir de um modelo de pré-treinados, vamos definir os pesos modelo no estado servidor diretamente do modelo carregado.

 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()])
  tff.learning.assign_weights_to_keras_model(keras_model, state.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

Com as mudanças padrão, não temos feito treinamento suficiente para fazer uma grande diferença, mas se você treinar mais em mais dados Shakespeare, você deve ver uma diferença no estilo do texto gerado com o modelo atualizado:

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

extensões sugeridas

Este tutorial é apenas o primeiro passo! Aqui estão algumas idéias de como você pode tentar estender esse notebook:

  • Escrever um ciclo de treinamento mais realista onde os clientes de exemplo para treinar de forma aleatória.
  • Use " .repeat(NUM_EPOCHS) " nos conjuntos de dados de clientes para tentar várias épocas de treinamento local (por exemplo, como em McMahan et. Al. ). Veja também Federated Aprendizagem para Classificação de Imagens que faz isso.
  • Alterar a compile() comando para experimento com o uso de diferentes algoritmos de otimização no cliente.
  • Experimente o server_optimizer argumento para build_federated_averaging_process de experimentar diferentes algoritmos para aplicar as atualizações de modelo no servidor.
  • Experimente o client_weight_fn argumento para a build_federated_averaging_process de experimentar diferentes ponderações dos clientes. As atualizações do cliente pesos padrão pelo número de exemplos sobre o cliente, mas você pode fazer por exemplo client_weight_fn=lambda _: tf.constant(1.0) .