الگوریتم های فدرال سفارشی ، قسمت 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

# TODO(b/148678573,b/148685415): must use the reference context because it
# supports unbounded references and tff.sequence_* intrinsics.
tff.backends.reference.set_reference_context()
@tff.federated_computation
def hello_world():
  return 'Hello, World!'

hello_world()
'Hello, World!'

پیاده سازی میانگین یابی فدرال

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

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

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

ابتدا ، اجازه دهید داده های استاندارد MNIST را بارگذاری کنیم:

mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step
[(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)
'(<<weights=float32[784,10],bias=float32[10]>,<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.3025854

توجه داشته باشید که ما محاسبه 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)
'(<<weights=float32[784,10],bias=float32[10]>,<x=float32[?,784],y=int32[?]>,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.19690022, 0.13176313, 0.10113226, 0.082738124, 0.0703014]

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

حال حاضر، از 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):

  # Mapping function to apply to each batch.
  @tff.federated_computation(MODEL_TYPE, BATCH_TYPE)
  def batch_fn(model, batch):
    return batch_train(model, batch, learning_rate)

  return tff.sequence_reduce(all_batches, initial_model, batch_fn)
str(local_train.type_signature)
'(<<weights=float32[784,10],bias=float32[10]>,float32,<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):
  # TODO(b/120157713): Replace with `tff.sequence_average()` once implemented.
  return tff.sequence_sum(
      tff.sequence_map(
          tff.federated_computation(lambda b: batch_loss(model, b), BATCH_TYPE),
          all_batches))
str(local_eval.type_signature)
'(<<weights=float32[784,10],bias=float32[10]>,<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.4348469

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

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 . این س bringsال را ایجاد می کند - آموزش محلی چگونه بر کیفیت مدل از دیدگاه جهانی تأثیر گذاشت؟

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

این نقطه ای است در سفر ما که در آن به انواع فدراسیون شده و محاسبات فدراسیون می پردازیم - موضوعی که با آن شروع کردیم. در اینجا یک جفت تعریف 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.60552406311035
round 1, loss=20.365678787231445
round 2, loss=19.27480125427246
round 3, loss=18.31110954284668
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 به عنوان یک مثال کامل تر، و به عنوان یک راه برای نشان دادن برخی از شیوه های برنامه نویسی ما می خواهم برای تشویق.