TFF: How simulate training on random samples of users in each round

Viewed 278

I would like to simulate this code of federated learning for image classification with random samples of users in each round, This tutorial uses all clients on training, insteed, I would to modify this code in such a way, in each round a random samples of clients are chosen. So what we can change in this code to force it to choice client randomly

import collections
import time

import tensorflow as tf
tf.compat.v1.enable_v2_behavior()

import tensorflow_federated as tff

source, _ = tff.simulation.datasets.emnist.load_data()


def map_fn(example):
  return collections.OrderedDict(
      x=tf.reshape(example['pixels'], [-1, 784]), y=example['label'])


def client_data(n):
  ds = source.create_tf_dataset_for_client(source.client_ids[n])
  return ds.repeat(10).shuffle(500).batch(20).map(map_fn)


train_data = [client_data(n) for n in range(10)]
element_spec = train_data[0].element_spec

def model_fn():
  model = tf.keras.models.Sequential([
      tf.keras.layers.Input(shape=(784,)),
      tf.keras.layers.Dense(units=10, kernel_initializer='zeros'),
      tf.keras.layers.Softmax(),
  ])
  return tff.learning.from_keras_model(
      model,
      input_spec=element_spec,
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])


trainer = tff.learning.build_federated_averaging_process(
    model_fn, client_optimizer_fn=lambda: tf.keras.optimizers.SGD(0.02))

....
NUM_ROUNDS = 11
for round_num in range(2, NUM_ROUNDS):
  state, metrics = trainer.next(state, federated_train_data)
  print('round {:2d}, metrics={}'.format(round_num, metrics))
2 Answers

tff.simulation.ClientData objects expose a client_ids attribute, which represents a list of the strings identifying the users in this dataset.

So you can sample from this list directly, and use the create_tf_dataset_for_client method on the same object to create a dataset of this user's data. Assuming a tff.simulation.ClientData object client_data, the pseudocode would look like:

import random
...

for round_num in range(2, NUM_ROUNDS):
  selected_clients = random.sample(client_data.client_ids, USERS_PER_ROUND)
  federated_data = [
      client_data.create_tf_dataset_for_client(n) for n in selected_clients]
  state, metrics = iterative_process.next(state, federated_data)

Much of the research code included in TFF somewhat separates the concern of selecting clients from running the training loop, so I can't really point to a good example of this pattern there--but TFF I think would be happy to accept a contribution updating the tutorials to use a pattern like this, to help show the flexibility of the ClientData API a little better.

This will do it (following Keith's pseudocode):

selected_clients = np.random.choice(emnist_train.client_ids, size=USERS_PER_ROUND)
round_federated_train_data = make_federated_data(emnist_train, selected_clients)
Related