Смотрите доклады, семинары по продуктам, семинары и многое другое на Google I / O. См. Плейлист

Отправка различных данных определенным клиентам с помощью tff.federated_select

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

В этом руководстве показано, как реализовать настраиваемые объединенные алгоритмы в TFF, которые требуют отправки разных данных разным клиентам. Возможно, вы уже знакомы с tff.federated_broadcast который отправляет одно значение, размещенное на сервере, всем клиентам. В этом руководстве рассматриваются случаи, когда разные части серверного значения отправляются разным клиентам. Это может быть полезно для разделения частей модели между разными клиентами, чтобы избежать отправки всей модели одному клиенту.

Давайте tensorflow с импорта как tensorflow и tensorflow_federated .

!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio

import nest_asyncio
nest_asyncio.apply()
import tensorflow as tf
import tensorflow_federated as tff

Отправка разных значений на основе данных клиента

Рассмотрим случай, когда у нас есть некоторый список, размещенный на сервере, из которого мы хотим отправить несколько элементов каждому клиенту на основе некоторых данных, размещенных клиентом. Например, список строк на сервере и на клиентах, разделенный запятыми список индексов для загрузки. Мы можем реализовать это следующим образом:

list_of_strings_type = tff.TensorType(tf.string, [None])
# We only ever send exactly two values to each client. The number of keys per
# client must be a fixed number across all clients.
number_of_keys_per_client = 2
keys_type = tff.TensorType(tf.int32, [number_of_keys_per_client])
get_size = tff.tf_computation(lambda x: tf.size(x))
select_fn = tff.tf_computation(lambda val, index: tf.gather(val, index))
client_data_type = tf.string

# A function from our client data to the indices of the values we'd like to
# select from the server.
@tff.tf_computation(client_data_type)
@tff.check_returns_type(keys_type)
def keys_for_client(client_string):
  # We assume our client data is a single string consisting of exactly three
  # comma-separated integers indicating which values to grab from the server.
  split = tf.strings.split([client_string], sep=',')[0]
  return tf.strings.to_number([split[0], split[1]], tf.int32)

@tff.tf_computation(tff.SequenceType(tf.string))
@tff.check_returns_type(tf.string)
def concatenate(values):
  def reduce_fn(acc, item):
    return tf.cond(tf.math.equal(acc, ''),
                   lambda: item,
                   lambda: tf.strings.join([acc, item], ','))
  return values.reduce('', reduce_fn)

@tff.federated_computation(tff.type_at_server(list_of_strings_type), tff.type_at_clients(client_data_type))
def broadcast_based_on_client_data(list_of_strings_at_server, client_data):
  keys_at_clients = tff.federated_map(keys_for_client, client_data)
  max_key = tff.federated_map(get_size, list_of_strings_at_server)
  values_at_clients = tff.federated_select(keys_at_clients, max_key, list_of_strings_at_server, select_fn)
  value_at_clients = tff.federated_map(concatenate, values_at_clients)
  return value_at_clients

Затем мы можем смоделировать наши вычисления, предоставив размещенный на сервере список строк, а также строковые данные для каждого клиента:

client_data = ['0,1', '1,2', '2,0']
broadcast_based_on_client_data(['a', 'b', 'c'], client_data)
[<tf.Tensor: shape=(), dtype=string, numpy=b'a,b'>,
 <tf.Tensor: shape=(), dtype=string, numpy=b'b,c'>,
 <tf.Tensor: shape=(), dtype=string, numpy=b'c,a'>]

Отправка случайного элемента каждому клиенту

В качестве альтернативы может быть полезно отправить случайную часть данных сервера каждому клиенту. Мы можем реализовать это, сначала сгенерировав случайный ключ для каждого клиента, а затем выполнив процесс выбора, аналогичный тому, который использовался выше:

@tff.tf_computation(tf.int32)
@tff.check_returns_type(tff.TensorType(tf.int32, [1]))
def get_random_key(max_key):
  return tf.random.uniform(shape=[1], minval=0, maxval=max_key, dtype=tf.int32)

list_of_strings_type = tff.TensorType(tf.string, [None])
get_size = tff.tf_computation(lambda x: tf.size(x))
select_fn = tff.tf_computation(lambda val, index: tf.gather(val, index))

@tff.tf_computation(tff.SequenceType(tf.string))
@tff.check_returns_type(tf.string)
def get_last_element(sequence):
  return sequence.reduce('', lambda _initial_state, val: val)

@tff.federated_computation(tff.type_at_server(list_of_strings_type))
def broadcast_random_element(list_of_strings_at_server):
  max_key_at_server = tff.federated_map(get_size, list_of_strings_at_server)
  max_key_at_clients = tff.federated_broadcast(max_key_at_server)
  key_at_clients = tff.federated_map(get_random_key, max_key_at_clients)
  random_string_sequence_at_clients = tff.federated_select(
      key_at_clients, max_key_at_server, list_of_strings_at_server, select_fn)
  # Even though we only passed in a single key, `federated_select` returns a
  # sequence for each client. We only care about the last (and only) element.
  random_string_at_clients = tff.federated_map(get_last_element, random_string_sequence_at_clients)
  return random_string_at_clients

Поскольку наша функция broadcast_random_element не принимает никаких данных, размещенных клиентом, мы должны настроить среду выполнения TFF Simulation Runtime с количеством клиентов по умолчанию для использования:

tff.backends.native.set_local_execution_context(default_num_clients=3)

Затем мы можем смоделировать выделение. Мы можем изменить default_num_clients выше и список строк ниже, чтобы генерировать разные результаты, или просто повторно запустить вычисление для генерации разных случайных выходных данных.

broadcast_random_element(tf.convert_to_tensor(['foo', 'bar', 'baz']))