إرسال بيانات مختلفة إلى عملاء معينين باستخدام tff.federated_select

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يوضح هذا البرنامج التعليمي كيفية تنفيذ الخوارزميات الموحدة المخصصة في 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

إرسال قيم مختلفة بناءً على بيانات العميل

ضع في اعتبارك الحالة التي يكون لدينا فيها بعض القائمة الموضوعة على الخادم والتي نريد من خلالها إرسال بعض العناصر إلى كل عميل بناءً على بعض البيانات الموضوعة من قبل العميل. على سبيل المثال ، قائمة سلاسل على الخادم ، وعلى العملاء ، قائمة مؤشرات مفصولة بفواصل لتنزيلها. يمكننا تنفيذ ذلك على النحو التالي:

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