Mengirim Data Berbeda Ke Klien Tertentu Dengan tff.federated_select

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Tutorial ini menunjukkan cara menerapkan algoritme gabungan khusus di TFF yang memerlukan pengiriman data berbeda ke klien berbeda. Anda mungkin sudah akrab dengan tff.federated_broadcast yang mengirimkan nilai server ditempatkan tunggal untuk semua klien. Tutorial ini berfokus pada kasus di mana bagian yang berbeda dari nilai berbasis server dikirim ke klien yang berbeda. Ini mungkin berguna untuk membagi bagian dari model di klien yang berbeda untuk menghindari pengiriman seluruh model ke klien tunggal.

Mari kita mulai dengan mengimpor baik tensorflow dan 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
tff.backends.native.set_local_python_execution_context()

Mengirim Nilai Berbeda Berdasarkan Data Klien

Pertimbangkan kasus di mana kami memiliki beberapa daftar yang ditempatkan di server dari mana kami ingin mengirim beberapa elemen ke setiap klien berdasarkan beberapa data yang ditempatkan di klien. Misalnya, daftar string di server, dan di klien, daftar indeks yang dipisahkan koma untuk diunduh. Kita dapat menerapkannya sebagai berikut:

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

Kemudian kita dapat mensimulasikan perhitungan kita dengan menyediakan daftar string yang ditempatkan di server serta data string untuk setiap klien:

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

Mengirim Elemen Acak Ke Setiap Klien

Sebagai alternatif, mungkin berguna untuk mengirim sebagian data server secara acak ke setiap klien. Kita dapat menerapkannya dengan terlebih dahulu membuat kunci acak pada setiap klien dan kemudian mengikuti proses pemilihan yang serupa dengan yang digunakan di atas:

@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

Sejak kami broadcast_random_element fungsi tidak mengambil data klien ditempatkan apapun, kita harus mengkonfigurasi TFF Simulasi Runtime dengan nomor standar dari klien untuk penggunaan:

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

Kemudian kita dapat mensimulasikan seleksi. Kita dapat mengubah default_num_clients di atas dan daftar string di bawah ini untuk menghasilkan hasil yang berbeda, atau hanya kembali menjalankan perhitungan untuk menghasilkan output acak yang berbeda.

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