الگوریتم های فدرال سفارشی ، قسمت 2: پیاده سازی میانگین گیری فدرال

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

در این آموزش قسمت دوم از سری دو قسمتی که نشان می دهد که چگونه برای اجرای انواع سفارشی از الگوریتم های فدرال در TFF با استفاده از است فدرال هسته (FC) ، که به عنوان یک پایه برای خدمت فدرال آموزش (FL) لایه ( tff.learning ) .

ما شما را تشویق به اولین به عنوان خوانده شده بخش اول از این سری ، که معرفی برخی از مفاهیم کلیدی و انتزاعی برنامه نویسی مورد استفاده در اینجا.

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

ما شما را تشویق به بررسی طبقه بندی تصویر و متن آموزش برای سطح بالاتر و معرفی ملایم تر به فدرال رابط های برنامه کاربردی آموزش TFF، به عنوان آنها به شما کمک خواهد مفاهیم ما در اینجا در زمینه توصیف قرار داده است.

قبل از اینکه شروع کنیم

قبل از شروع، سعی کنید مثال "Hello World" زیر را اجرا کنید تا مطمئن شوید که محیط شما به درستی تنظیم شده است. اگر آن کار نمی کند، لطفا به مراجعه نصب و راه اندازی راهنمای دستورالعمل.

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

import nest_asyncio
nest_asyncio.apply()
import collections

import numpy as np
import tensorflow as tf
import tensorflow_federated as tff

# Must use the Python context because it
# supports tff.sequence_* intrinsics.
executor_factory = tff.framework.local_executor_factory(
    support_sequence_ops=True)
execution_context = tff.framework.ExecutionContext(
    executor_fn=executor_factory)
tff.framework.set_default_context(execution_context)
@tff.federated_computation
def hello_world():
  return 'Hello, World!'

hello_world()
b'Hello, World!'

اجرای میانگین گیری فدرال

همانطور که در آموزش فدرال برای طبقه بندی تصویر ، ما می رویم به عنوان مثال استفاده MNIST، اما از آنجایی که این به عنوان یک آموزش سطح پایین در نظر گرفته شده، ما می رویم برای دور زدن API Keras و tff.simulation ، ارسال کد مدل اولیه، و ساخت یک مجموعه داده های فدرال از ابتدا

آماده سازی مجموعه داده های فدرال

به‌منظور نمایش، می‌خواهیم سناریویی را شبیه‌سازی کنیم که در آن داده‌هایی از 10 کاربر داریم، و هر یک از کاربران دانشی را در مورد چگونگی تشخیص رقم متفاوت ارائه می‌کنند. این مورد به عنوان غیر IID عنوان آن می شود.

ابتدا داده های استاندارد MNIST را بارگیری می کنیم:

mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()
[(x.dtype, x.shape) for x in mnist_train]
[(dtype('uint8'), (60000, 28, 28)), (dtype('uint8'), (60000,))]

داده‌ها به‌صورت آرایه‌های Numpy، یکی با تصاویر و دیگری با برچسب‌های رقمی ارائه می‌شوند، که هر دو بعد اول بر روی نمونه‌های جداگانه می‌روند. بیایید یک تابع کمکی بنویسیم که آن را به گونه‌ای قالب‌بندی کند که با نحوه تغذیه توالی‌های فدرال به محاسبات TFF سازگار باشد، به عنوان مثال، به‌عنوان فهرستی از فهرست‌ها - فهرست بیرونی در محدوده کاربران (ارقام)، فهرست‌های داخلی که در دسته‌ای از داده‌ها قرار دارند. دنباله هر مشتری همانطور که مرسوم است، ما هر دسته به عنوان یک جفت تانسورها به نام ساختار x و y ، هر کدام با ابعاد دسته ای پیشرو. در حالی که در آن، ما نیز هر تصویر را به یک بردار 784 عنصر پهن و تغییر اندازه دهید پیکسل در آن را به 0..1 وسیعی، به طوری که ما لازم نیست به درهم و برهمی منطق مدل با تبدیل داده ها.

NUM_EXAMPLES_PER_USER = 1000
BATCH_SIZE = 100


def get_data_for_digit(source, digit):
  output_sequence = []
  all_samples = [i for i, d in enumerate(source[1]) if d == digit]
  for i in range(0, min(len(all_samples), NUM_EXAMPLES_PER_USER), BATCH_SIZE):
    batch_samples = all_samples[i:i + BATCH_SIZE]
    output_sequence.append({
        'x':
            np.array([source[0][i].flatten() / 255.0 for i in batch_samples],
                     dtype=np.float32),
        'y':
            np.array([source[1][i] for i in batch_samples], dtype=np.int32)
    })
  return output_sequence


federated_train_data = [get_data_for_digit(mnist_train, d) for d in range(10)]

federated_test_data = [get_data_for_digit(mnist_test, d) for d in range(10)]

عنوان بررسی سلامت عقل سریع، نگاه اجازه دهید در Y تانسور در آخرین دسته از داده های ارائه شده توسط مشتری پنجم (که یکی مربوط به رقم 5 ).

federated_train_data[5][-1]['y']
array([5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], dtype=int32)

فقط برای اطمینان، بیایید به تصویر مربوط به آخرین عنصر آن دسته نیز نگاه کنیم.

from matplotlib import pyplot as plt

plt.imshow(federated_train_data[5][-1]['x'][-1].reshape(28, 28), cmap='gray')
plt.grid(False)
plt.show()

png

در مورد ترکیب TensorFlow و TFF

در این آموزش، برای فشردگی ما بلافاصله توابع است که معرفی منطق TensorFlow با تزئین tff.tf_computation . با این حال، برای منطق پیچیده تر، این الگویی نیست که ما توصیه می کنیم. اشکال زدایی TensorFlow در حال حاضر می تواند یک چالش باشد، و اشکال زدایی TensorFlow پس از سریال سازی کامل و سپس واردات مجدد، لزوماً برخی از ابرداده ها را از دست می دهد و تعامل را محدود می کند، و اشکال زدایی را به چالش بیشتری تبدیل می کند.

بنابراین، ما به شدت توصیه نوشتن منطق TF مجموعه به عنوان توابع پایتون مستقل (این است که بدون tff.tf_computation دکوراسیون). به این ترتیب منطق TensorFlow را می توان توسعه و آزمایش با استفاده TF بهترین شیوه و ابزار (مانند حالت مشتاق)، قبل از serialize کردن محاسبه برای TFF (به عنوان مثال، با استناد به tff.tf_computation با یک تابع پایتون به عنوان آرگومان).

تعریف تابع ضرر

حالا که داده ها را داریم، بیایید یک تابع ضرر تعریف کنیم که می توانیم برای آموزش از آن استفاده کنیم. ابتدا، اجازه دهید نوع ورودی را به عنوان یک TFF با نام tuple تعریف کنیم. از آنجا که اندازه دسته داده ها ممکن است متفاوت باشد، ما مجموعه ای از ابعاد دسته ای به None نشان می دهد که اندازه این بعد ناشناخته است.

BATCH_SPEC = collections.OrderedDict(
    x=tf.TensorSpec(shape=[None, 784], dtype=tf.float32),
    y=tf.TensorSpec(shape=[None], dtype=tf.int32))
BATCH_TYPE = tff.to_type(BATCH_SPEC)

str(BATCH_TYPE)
'<x=float32[?,784],y=int32[?]>'

ممکن است تعجب کنید که چرا ما نمی توانیم فقط یک نوع پایتون معمولی را تعریف کنیم. به یاد بیاورید بحث در بخش 1 ، که در آن ما توضیح داد که در حالی که ما می توانیم منطق محاسبات TFF با استفاده از پایتون، تحت محاسبات هود TFF بیان می پایتون است. نماد BATCH_TYPE تعریف بالا نمایانگر یک نوع مشخصات TFF انتزاعی. این مهم است که به تشخیص این نوع TFF انتزاعی از بتن انواع نمایندگی پایتون، به عنوان مثال، ظروف مانند dict یا collections.namedtuple که ممکن است مورد استفاده قرار گیرد برای نشان دادن نوع TFF در بدن از یک تابع پایتون. بر خلاف پایتون، TFF یک تک انتزاعی نوع سازنده tff.StructType برای تاپل مانند ظروف، با عناصر است که می تواند به صورت جداگانه به نام یا نام رها کنید. این نوع همچنین برای مدل‌سازی پارامترهای رسمی محاسبات استفاده می‌شود، زیرا محاسبات TFF می‌توانند به طور رسمی فقط یک پارامتر و یک نتیجه را اعلام کنند - به زودی نمونه‌هایی از این را خواهید دید.

اکنون بیایید نوع TFF پارامترهای مدل به عنوان یک تاپل TFF به نام وزن و تعصب تعریف، دوباره.

MODEL_SPEC = collections.OrderedDict(
    weights=tf.TensorSpec(shape=[784, 10], dtype=tf.float32),
    bias=tf.TensorSpec(shape=[10], dtype=tf.float32))
MODEL_TYPE = tff.to_type(MODEL_SPEC)
print(MODEL_TYPE)
<weights=float32[784,10],bias=float32[10]>

با وجود آن تعاریف، اکنون می‌توانیم ضرر را برای یک مدل معین در یک دسته تعریف کنیم. توجه داشته باشید که استفاده از @tf.function دکوراتور داخل @tff.tf_computation دکوراتور. این اجازه می دهد تا ما را به ارسال TF با استفاده از پایتون مانند معناشناسی حتی اگر در داخل یک بودند tf.Graph زمینه ایجاد شده توسط tff.tf_computation دکوراتور.

# NOTE: `forward_pass` is defined separately from `batch_loss` so that it can 
# be later called from within another tf.function. Necessary because a
# @tf.function  decorated method cannot invoke a @tff.tf_computation.

@tf.function
def forward_pass(model, batch):
  predicted_y = tf.nn.softmax(
      tf.matmul(batch['x'], model['weights']) + model['bias'])
  return -tf.reduce_mean(
      tf.reduce_sum(
          tf.one_hot(batch['y'], 10) * tf.math.log(predicted_y), axis=[1]))

@tff.tf_computation(MODEL_TYPE, BATCH_TYPE)
def batch_loss(model, batch):
  return forward_pass(model, batch)

همانطور که انتظار میرفت، محاسبات batch_loss بازده float32 از دست دادن با توجه به مدل و یک دسته ای داده است. توجه داشته باشید که MODEL_TYPE و BATCH_TYPE اند با هم به یک تایی 2 از پارامترهای رسمی فشرده شده است. شما می توانید نوع تشخیص batch_loss عنوان (<MODEL_TYPE,BATCH_TYPE> -> float32) .

str(batch_loss.type_signature)
'(<model=<weights=float32[784,10],bias=float32[10]>,batch=<x=float32[?,784],y=int32[?]>> -> float32)'

به عنوان یک بررسی عقلانی، بیایید یک مدل اولیه پر از صفر بسازیم و تلفات را در دسته ای از داده هایی که در بالا مشاهده کردیم محاسبه کنیم.

initial_model = collections.OrderedDict(
    weights=np.zeros([784, 10], dtype=np.float32),
    bias=np.zeros([10], dtype=np.float32))

sample_batch = federated_train_data[5][-1]

batch_loss(initial_model, sample_batch)
2.3025851

توجه داشته باشید که ما محاسبه TFF با مدل اولیه تعریف شده به عنوان یک خوراک dict ، حتی اگر بدن از تابع پایتون است که تعریف آن را مصرف پارامترهای مدل به عنوان model['weight'] و model['bias'] . استدلال از پاسخ به batch_loss به سادگی به بدن از آن تابع تصویب نشده است.

چه اتفاقی می افتد هنگامی که ما فراخوان batch_loss ؟ بدن پایتون از batch_loss قبلا ترسیم شده و مرتب در سلول بالا که در آن تعریف شده بود. TFF به عنوان تماس گیرنده را به عمل batch_loss در زمان محاسبه تعریف، و به عنوان هدف نیایشی را در زمان batch_loss استناد شده است. در هر دو نقش، TFF به عنوان پل بین سیستم نوع انتزاعی TFF و انواع نمایش پایتون عمل می کند. در زمان نیایش، TFF انواع ظرف پایتون استاندارد ترین (قبول dict ، list ، tuple ، collections.namedtuple ، و غیره) به عنوان نمایندگی بتن از تاپل TFF انتزاعی. همچنین، اگرچه همانطور که در بالا ذکر شد، محاسبات TFF به طور رسمی فقط یک پارامتر را می‌پذیرند، می‌توانید از نحو فراخوانی آشنای پایتون با آرگومان‌های موقعیتی و/یا کلمه کلیدی در مواردی که نوع پارامتر چندگانه است استفاده کنید - همانطور که انتظار می‌رود کار می‌کند.

نزول گرادیان در یک دسته

حال، بیایید محاسباتی را تعریف کنیم که از این تابع تلفات برای انجام یک مرحله از شیب نزول استفاده می کند. توجه داشته باشید که چگونه در این تابع تعریف، ما با استفاده از batch_loss به عنوان یک مؤلفه. شما می توانید محاسبات ساخته شده با استناد tff.tf_computation داخل بدن محاسبات دیگر، هر چند به طور معمول این است که لازم نیست - همانطور که در بالا اشاره شد، به دلیل ترتیب از دست برخی از اطلاعات اشکال زدایی، آن است که اغلب ترجیح داده شده برای محاسبات پیچیده تر برای نوشتن و تست همه TensorFlow بدون tff.tf_computation دکوراتور.

@tff.tf_computation(MODEL_TYPE, BATCH_TYPE, tf.float32)
def batch_train(initial_model, batch, learning_rate):
  # Define a group of model variables and set them to `initial_model`. Must
  # be defined outside the @tf.function.
  model_vars = collections.OrderedDict([
      (name, tf.Variable(name=name, initial_value=value))
      for name, value in initial_model.items()
  ])
  optimizer = tf.keras.optimizers.SGD(learning_rate)

  @tf.function
  def _train_on_batch(model_vars, batch):
    # Perform one step of gradient descent using loss from `batch_loss`.
    with tf.GradientTape() as tape:
      loss = forward_pass(model_vars, batch)
    grads = tape.gradient(loss, model_vars)
    optimizer.apply_gradients(
        zip(tf.nest.flatten(grads), tf.nest.flatten(model_vars)))
    return model_vars

  return _train_on_batch(model_vars, batch)
str(batch_train.type_signature)
'(<initial_model=<weights=float32[784,10],bias=float32[10]>,batch=<x=float32[?,784],y=int32[?]>,learning_rate=float32> -> <weights=float32[784,10],bias=float32[10]>)'

هنگامی که شما یک تابع پایتون با تزئین استناد tff.tf_computation در داخل بدن از جمله تابع دیگر، منطق محاسبه TFF داخلی تعبیه شده است (در اصل، به صورت inline) در منطق یک خارجی. همانطور که در بالا اشاره شد، اگر شما در حال نوشتن هر دو محاسبات، به احتمال زیاد ترجیح را به تابع داخلی (است batch_loss در این مورد) پایتون طور منظم و یا tf.function به جای یک tff.tf_computation . با این حال، در اینجا ما نشان می دهد که خواستار یکی tff.tf_computation داخل یکی دیگر از اساسا در حد انتظار است این ممکن است لازم باشد اگر، برای مثال، شما کد پایتون تعریف ندارد batch_loss ، اما تنها نمایندگی TFF سریال آن است.

حالا بیایید این تابع را چند بار در مدل اولیه اعمال کنیم تا ببینیم ضرر کاهش می یابد یا خیر.

model = initial_model
losses = []
for _ in range(5):
  model = batch_train(model, sample_batch, 0.1)
  losses.append(batch_loss(model, sample_batch))
losses
[0.19690023, 0.13176313, 0.10113225, 0.08273812, 0.070301384]

نزول گرادیان در دنباله ای از داده های محلی

حال حاضر، از batch_train به کار به نظر می رسد، اجازه دهید ارسال یک تابع آموزش مشابه local_train که مصرف کل دنباله ای از تمام دسته از کاربران به جای فقط یک دسته واحد. محاسبه جدید نیاز به در حال حاضر مصرف tff.SequenceType(BATCH_TYPE) به جای BATCH_TYPE .

LOCAL_DATA_TYPE = tff.SequenceType(BATCH_TYPE)

@tff.federated_computation(MODEL_TYPE, tf.float32, LOCAL_DATA_TYPE)
def local_train(initial_model, learning_rate, all_batches):

  @tff.tf_computation(LOCAL_DATA_TYPE, tf.float32)
  def _insert_learning_rate_to_sequence(dataset, learning_rate):
    return dataset.map(lambda x: (x, learning_rate))

  batches_with_learning_rate = _insert_learning_rate_to_sequence(all_batches, learning_rate)

  # Mapping function to apply to each batch.
  @tff.federated_computation(MODEL_TYPE, batches_with_learning_rate.type_signature.element)
  def batch_fn(model, batch_with_lr):
    batch, lr = batch_with_lr
    return batch_train(model, batch, lr)

  return tff.sequence_reduce(batches_with_learning_rate, initial_model, batch_fn)
str(local_train.type_signature)
'(<initial_model=<weights=float32[784,10],bias=float32[10]>,learning_rate=float32,all_batches=<x=float32[?,784],y=int32[?]>*> -> <weights=float32[784,10],bias=float32[10]>)'

جزئیات کمی در این بخش کوتاه کد وجود دارد، اجازه دهید آنها را یکی یکی مرور کنیم.

نخست، در حالی که ما می توانستیم این منطق به طور کامل در TensorFlow، با تکیه بر اجرا tf.data.Dataset.reduce برای پردازش دنباله به طور مشابه به چگونه ما آن را انجام داده ام که قبلا انجام شده این زمان برای بیان منطق به زبان چسب ترجیح دادم ، به عنوان یک tff.federated_computation . ما در بر اپراتور فدرال استفاده می شود tff.sequence_reduce به انجام کاهش است.

اپراتور tff.sequence_reduce است به طور مشابه به استفاده tf.data.Dataset.reduce . شما می توانید از آن به عنوان اصل همان فکر می کنم tf.data.Dataset.reduce ، اما برای استفاده در داخل محاسبات فدرال، که به عنوان شما ممکن است به یاد داشته باشید، می تواند شامل کد TensorFlow است. این یک اپراتور قالب با پارامتر رسمی 3 تایی که شامل دنباله ای از است T عناصر -typed، حالت اولیه از کاهش (ما آن را به عنوان صفر مراجعه انتزاعی) از برخی از نوع U ، و اپراتور کاهش تایپ (<U,T> -> U) که در اثر تغییر دولت از کاهش پردازش یک عنصر واحد. نتیجه حالت نهایی کاهش است، پس از پردازش تمام عناصر به ترتیب متوالی. در مثال ما، وضعیت کاهش، مدلی است که بر روی پیشوندی از داده ها آموزش داده شده است، و عناصر، دسته های داده هستند.

دوم، توجه داشته باشید که ما دوباره یک محاسبه (استفاده batch_train ) به عنوان یک جزء در دیگر ( local_train )، اما نه به طور مستقیم. ما نمی توانیم از آن به عنوان یک عملگر کاهش استفاده کنیم زیرا یک پارامتر اضافی را می گیرد - نرخ یادگیری. برای حل این مشکل، جاسازی شده محاسبه فدرال تعریف می کنیم batch_fn که متصل به local_train را پارامتر learning_rate در بدن آن است. تا زمانی که محاسبات فرزند در خارج از بدنه والد خود احضار نشده باشد، برای محاسبات فرزند که به این شکل تعریف شده است مجاز است که یک پارامتر رسمی از والد خود را ثبت کند. شما می توانید از این الگو به عنوان یک معادل فکر می کنم functools.partial زبان Python.

مفهوم عملی گرفتن learning_rate این راه این است، البته، که همان ارزش نرخ یادگیری در تمام دسته استفاده می شود.

حال، اجازه دهید تابع آموزش محلی تعریف جدید در کل دنباله ای از داده ها را از همان کاربر که دسته ای نمونه (رقمی کمک سعی کنید 5 ).

locally_trained_model = local_train(initial_model, 0.1, federated_train_data[5])

کار کرد؟ برای پاسخ به این سوال باید ارزیابی را اجرا کنیم.

ارزیابی محلی

در اینجا یک راه برای پیاده‌سازی ارزیابی محلی با جمع کردن تلفات در تمام دسته‌های داده وجود دارد (می‌توانستیم میانگین را به خوبی محاسبه کنیم؛ آن را به عنوان تمرینی برای خواننده می‌گذاریم).

@tff.federated_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def local_eval(model, all_batches):

  @tff.tf_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
  def _insert_model_to_sequence(model, dataset):
    return dataset.map(lambda x: (model, x))

  model_plus_data = _insert_model_to_sequence(model, all_batches)

  @tff.tf_computation(tf.float32, batch_loss.type_signature.result)
  def tff_add(accumulator, arg):
    return accumulator + arg

  return tff.sequence_reduce(
      tff.sequence_map(
          batch_loss,
          model_plus_data), 0., tff_add)
str(local_eval.type_signature)
'(<model=<weights=float32[784,10],bias=float32[10]>,all_batches=<x=float32[?,784],y=int32[?]>*> -> float32)'

باز هم، چند عنصر جدید وجود دارد که توسط این کد نشان داده شده است، اجازه دهید آنها را یکی یکی مرور کنیم.

اول، ما دو اپراتور فدرال جدید برای پردازش توالی استفاده کرده اند: tff.sequence_map که طول می کشد تابع نگاشت T->U و یک دنباله از T ، و از خود ساطع میکند دنباله ای از U دست آمده با استفاده از تابع نگاشت نقطه به نقطه، و tff.sequence_sum که فقط تمام عناصر را اضافه می کند. در اینجا، ما هر دسته داده را به یک مقدار ضرر نگاشت می کنیم، و سپس مقادیر تلفات حاصل را برای محاسبه ضرر کل اضافه می کنیم.

توجه داشته باشید که ما نمی توانستیم دوباره مورد استفاده قرار tff.sequence_reduce ، اما این امر می تواند بهترین انتخاب است - روند کاهش است، تعریف، پی در پی، در حالی که نقشه برداری و جمع می تواند به صورت موازی محاسبه می شود. هنگامی که انتخابی به شما داده می شود، بهتر است از اپراتورهایی استفاده کنید که انتخاب های پیاده سازی را محدود نمی کنند، به طوری که وقتی محاسبات TFF ما در آینده برای استقرار در یک محیط خاص کامپایل می شود، بتوان از تمام فرصت های بالقوه برای سریع تر استفاده کامل کرد. ، مقیاس پذیرتر، اجرای با منابع کارآمدتر.

دوم، توجه داشته باشید که فقط به عنوان در local_train ، تابع جزء ما نیاز داریم ( batch_loss ) پارامترهای بیش از آنچه اپراتور فدرال طول می کشد ( tff.sequence_map ) انتظار، بنابراین ما دوباره یک جزئی، این بار درون خطی تعریف به طور مستقیم با پیچیدن یک lambda به عنوان یک tff.federated_computation . با استفاده از دستگاه فوم پیچ دور درون خطی با یک تابع به عنوان آرگومان روش توصیه شده برای استفاده است tff.tf_computation برای قراردادن TensorFlow منطق در TFF.

حالا بیایید ببینیم که آیا آموزش ما جواب داده است یا خیر.

print('initial_model loss =', local_eval(initial_model,
                                         federated_train_data[5]))
print('locally_trained_model loss =',
      local_eval(locally_trained_model, federated_train_data[5]))
initial_model loss = 23.025854
locally_trained_model loss = 0.43484688

در واقع ضرر کاهش یافت. اما اگر آن را بر روی داده های کاربر دیگری ارزیابی کنیم چه اتفاقی می افتد؟

print('initial_model loss =', local_eval(initial_model,
                                         federated_train_data[0]))
print('locally_trained_model loss =',
      local_eval(locally_trained_model, federated_train_data[0]))
initial_model loss = 23.025854
locally_trained_model loss = 74.50075

همانطور که انتظار می رفت اوضاع بدتر شد. مدل به رسمیت شناختن آموزش دیده بود 5 و هرگز دیده می شود یک 0 . این سؤال را به همراه دارد - آموزش محلی چگونه بر کیفیت مدل از دیدگاه جهانی تأثیر گذاشته است؟

ارزیابی فدرال

این نقطه ای از سفر ماست که در نهایت به انواع فدرال و محاسبات فدرال بازمی گردیم - موضوعی که با آن شروع کردیم. در اینجا یک جفت تعاریف انواع TFF برای مدلی که از سرور سرچشمه می‌گیرد و داده‌هایی که روی کلاینت‌ها باقی می‌مانند، آورده شده است.

SERVER_MODEL_TYPE = tff.type_at_server(MODEL_TYPE)
CLIENT_DATA_TYPE = tff.type_at_clients(LOCAL_DATA_TYPE)

با تمام تعاریفی که تاکنون معرفی شده است، بیان ارزیابی فدرال در TFF یک خطی است - ما مدل را بین مشتریان توزیع می کنیم، به هر مشتری اجازه می دهیم ارزیابی محلی را در بخش محلی داده خود فراخوانی کند و سپس میانگین ضرر را محاسبه کند. در اینجا یک راه برای نوشتن این است.

@tff.federated_computation(SERVER_MODEL_TYPE, CLIENT_DATA_TYPE)
def federated_eval(model, data):
  return tff.federated_mean(
      tff.federated_map(local_eval, [tff.federated_broadcast(model),  data]))

ما در حال حاضر نمونه هایی از دیده tff.federated_mean و tff.federated_map در حالات ساده تر، و در سطح بصری، کار می کنند به عنوان انتظار می رود، اما بیشتر در این بخش از کد از ملاقات چشم وجود دارد، بنابراین اجازه دهید بیش از آن را به دقت است.

اول، استراحت اجازه دهید پایین اجازه هر مشتری فراخوان ارزیابی محلی در بخش محلی خود را از بخش داده. همانطور که شما ممکن است از بخش های پیشین به یاد، local_eval دارای امضای نوع فرم (<MODEL_TYPE, LOCAL_DATA_TYPE> -> float32) .

اپراتور فدرال tff.federated_map یک قالب است که به عنوان یک پارامتر می پذیرد تایی 2 که متشکل از تابع نگاشت از برخی از انواع است T->U و یک مقدار فدرال نوع {T}@CLIENTS (یعنی با ترکیبات عضو همان نوع به عنوان پارامتر تابع نقشه برداری)، و بازده یک نتیجه از نوع {U}@CLIENTS .

از آنجا که ما تغذیه هستید local_eval به عنوان یک تابع نگاشت به درخواست بر اساس هر مشتری، آرگومان دوم باید از یک نوع فدرال شود {<MODEL_TYPE, LOCAL_DATA_TYPE>}@CLIENTS در نامگذاری از بخش های پیشین، یعنی، آن را باید یک تاپل فدرال باشید هر مشتری باید یک مجموعه کامل از استدلال برای نگه local_eval به عنوان یک عضو consituent. در عوض، ما تغذیه آن 2-عنصر پایتون list . اینجا چه خبره؟

در واقع، این یک نمونه از بازیگران نوع ضمنی در TFF، شبیه به کست نوع ضمنی شما ممکن است در جاهای دیگر مواجه می شوند، به عنوان مثال، زمانی که شما تغذیه یک int به یک تابع که قبول float . در این مرحله از ریخته گری ضمنی به ندرت استفاده می شود، اما ما قصد داریم آن را در TFF به عنوان راهی برای به حداقل رساندن صفحه دیگ بخار فراگیرتر کنیم.

بازیگران ضمنی که در این مورد اعمال هم ارزی بین تاپل فدرال از فرم است {<X,Y>}@Z و چندتایی از فدرال ارزش <{X}@Z,{Y}@Z> . در حالی که به طور رسمی، این دو امضا نوع متفاوت هستند، نگاه کردن به آن از دیدگاه برنامه نویسان است، هر دستگاه در Z دارای دو واحد از داده ها X و Y . چه اتفاقی می افتد در اینجا این است بر خلاف zip در پایتون، و در واقع، ما ارائه می دهیم یک اپراتور tff.federated_zip که اجازه می دهد تا شما را به انجام تبدیل چنین صراحت. وقتی tff.federated_map یک تاپل به عنوان یک آرگومان دوم مواجه می شود، آن را به سادگی فراخوانی tff.federated_zip برای شما.

با توجه به موارد فوق، شما در حال حاضر باید قادر به تشخیص بیان شود tff.federated_broadcast(model) به عنوان نمایندگی یک مقدار از نوع TFF {MODEL_TYPE}@CLIENTS ، و data به عنوان یک مقدار از نوع TFF {LOCAL_DATA_TYPE}@CLIENTS (و یا به سادگی CLIENT_DATA_TYPE ) ، دو شدن با هم از طریق یک اشاره تلویحی فیلتر tff.federated_zip به شکل آرگومان دوم به tff.federated_map .

اپراتور tff.federated_broadcast ، به عنوان شما می خواهم انتظار، به سادگی انتقال داده از سرور به مشتریان می باشد.

حالا بیایید ببینیم آموزش محلی ما چگونه بر میانگین ضرر در سیستم تأثیر گذاشته است.

print('initial_model loss =', federated_eval(initial_model,
                                             federated_train_data))
print('locally_trained_model loss =',
      federated_eval(locally_trained_model, federated_train_data))
initial_model loss = 23.025852
locally_trained_model loss = 54.432625

در واقع، همانطور که انتظار می رفت، ضرر افزایش یافته است. به منظور بهبود مدل برای همه کاربران، باید در مورد داده های همه آموزش ببینیم.

آموزش فدرال

ساده‌ترین راه برای پیاده‌سازی آموزش فدرال، آموزش محلی و سپس میانگین‌گیری مدل‌ها است. این از همان بلوک‌های ساختمانی و الگوهایی استفاده می‌کند که قبلاً در مورد آن صحبت کردیم، همانطور که در زیر می‌بینید.

SERVER_FLOAT_TYPE = tff.type_at_server(tf.float32)


@tff.federated_computation(SERVER_MODEL_TYPE, SERVER_FLOAT_TYPE,
                           CLIENT_DATA_TYPE)
def federated_train(model, learning_rate, data):
  return tff.federated_mean(
      tff.federated_map(local_train, [
          tff.federated_broadcast(model),
          tff.federated_broadcast(learning_rate), data
      ]))

توجه داشته باشید که در اجرای کامل شامل از به طور متوسط فدرال ارائه شده توسط tff.learning ، به جای به طور متوسط در مدل، ما به طور متوسط دلتا مدل ترجیح می دهند، برای تعدادی از دلایل، به عنوان مثال، توانایی کلیپ هنجارهای به روز رسانی، برای فشرده سازی، و غیره .

بیایید ببینیم که آیا تمرین با اجرای چند دور تمرین و مقایسه میانگین ضرر قبل و بعد کار می کند یا خیر.

model = initial_model
learning_rate = 0.1
for round_num in range(5):
  model = federated_train(model, learning_rate, federated_train_data)
  learning_rate = learning_rate * 0.9
  loss = federated_eval(model, federated_train_data)
  print('round {}, loss={}'.format(round_num, loss))
round 0, loss=21.60552215576172
round 1, loss=20.365678787231445
round 2, loss=19.27480125427246
round 3, loss=18.311111450195312
round 4, loss=17.45725440979004

برای کامل بودن، بیایید اکنون روی داده های آزمایشی نیز اجرا کنیم تا تأیید کنیم که مدل ما به خوبی تعمیم می یابد.

print('initial_model test loss =',
      federated_eval(initial_model, federated_test_data))
print('trained_model test loss =', federated_eval(model, federated_test_data))
initial_model test loss = 22.795593
trained_model test loss = 17.278767

این آموزش ما را به پایان می رساند.

البته، مثال ساده‌شده ما تعدادی از کارهایی را که باید در یک سناریوی واقعی‌تر انجام دهید منعکس نمی‌کند - به عنوان مثال، ما معیارهایی غیر از ضرر را محاسبه نکرده‌ایم. ما شما را تشویق به مطالعه اجرای از میانگین فدرال در tff.learning به عنوان یک مثال کامل تر، و به عنوان یک راه برای نشان دادن برخی از شیوه های برنامه نویسی ما می خواهم برای تشویق.