ارسال داده های مختلف برای مشتریان خاص با tff.federated_select

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

این آموزش نحوه پیاده‌سازی الگوریتم‌های فدرال سفارشی را در TFF نشان می‌دهد که نیاز به ارسال داده‌های مختلف به مشتریان مختلف دارد. شما در حال حاضر ممکن است با آن آشنا tff.federated_broadcast که یک مقدار تک سرور قرار داده شده به تمام مشتریان می فرستد. این آموزش بر مواردی تمرکز دارد که بخش‌های مختلف یک ارزش مبتنی بر سرور برای مشتریان مختلف ارسال می‌شود. این ممکن است برای تقسیم بخش‌هایی از یک مدل در بین کلاینت‌های مختلف مفید باشد تا از ارسال کل مدل به هر مشتری منفرد جلوگیری شود.

بیایید آغاز شده توسط وارد کردن هر دو 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
tff.backends.native.set_local_python_execution_context()

ارسال مقادیر مختلف بر اساس داده های مشتری

موردی را در نظر بگیرید که در آن لیستی داریم که در سرور قرار داده شده است که می خواهیم چند عنصر را بر اساس برخی داده های قرار داده شده در سرویس گیرنده برای هر مشتری ارسال کنیم. به عنوان مثال، لیستی از رشته ها در سرور، و در کلاینت ها، فهرستی از شاخص های جدا شده با کاما برای دانلود. می توانیم آن را به صورت زیر پیاده سازی کنیم:

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 شبیه سازی در زمان اجرا با یک عدد به طور پیش فرض از مشتریان به استفاده از:

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

سپس می توانیم انتخاب را شبیه سازی کنیم. ما می توانیم تغییر default_num_clients بالا و لیستی از رشته های زیر را برای تولید نتایج مختلف، و یا به سادگی دوباره اجرا محاسبات برای تولید خروجی های مختلف به صورت تصادفی.

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