آموزش فدراسیون برای طبقه بندی تصویر

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

در این آموزش، ما به عنوان مثال آموزش MNIST کلاسیک استفاده به معرفی آموزش فدرال (FL) لایه API از TFF، tff.learning - مجموعه ای از رابط های سطح بالا است که می تواند مورد استفاده برای انجام انواع معمول وظایف یادگیری فدرال، مانند آموزش فدرال ، در برابر مدلهای ارائه شده توسط کاربر که در TensorFlow اجرا شده است.

این آموزش و API یادگیری فدرال عمدتا برای کاربرانی است که می خواهند مدلهای TensorFlow خود را به TFF وصل کنند و مورد دوم را بیشتر به عنوان یک جعبه سیاه در نظر بگیرند. - برای درک بیشتر در عمق از TFF و چگونگی پیاده سازی الگوریتم های یادگیری خود فدرال خود را، آموزش بر روی API FC هسته دیدن سفارشی فدرال الگوریتم قسمت 1 و قسمت 2 .

برای اطلاعات بیشتر در tff.learning ، با ادامه آموزش فدرال برای متن ، آموزش که علاوه بر پوشش مدل های مکرر، همچنین نشان می دهد در حال بارگذاری یک مدل Keras مرتب قبل از آموزش دیده برای پالایش با یادگیری فدرال همراه با ارزیابی با استفاده از Keras.

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

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

# tensorflow_federated_nightly also bring in tf_nightly, which
# can causes a duplicate tensorboard install, leading to errors.
!pip uninstall --yes tensorboard tb-nightly

!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio
!pip install --quiet --upgrade tb-nightly  # or tensorboard, but not both

import nest_asyncio
nest_asyncio.apply()
%load_ext tensorboard
import collections

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

np.random.seed(0)

tff.federated_computation(lambda: 'Hello, World!')()
b'Hello, World!'

آماده سازی داده های ورودی

بیایید با داده ها شروع کنیم. یادگیری فدرال مستلزم یک مجموعه داده فدرال ، یعنی مجموعه ای از داده ها از چندین کاربر است. اطلاعات فدرال معمولا غیر IID ، که به شمار مجموعه ای منحصر به فرد از چالش است.

به منظور تسهیل آزمایش، ما کشت مخزن TFF با چند مجموعه داده، از جمله یک نسخه فدرال MNIST که حاوی یک نسخه از مجموعه داده NIST اصلی شده است که دوباره پردازش با استفاده از برگ به طوری که داده است نویسنده اصلی از کوک ارقام از آنجا که هر نویسنده دارای سبک منحصر به فردی است ، این مجموعه داده نوع رفتار غیر iid مورد انتظار از مجموعه داده های فدرال را نشان می دهد.

در اینجا نحوه بارگیری آن است.

emnist_train, emnist_test = tff.simulation.datasets.emnist.load_data()

مجموعه داده های بازگردانده شده توسط load_data() موارد هستند tff.simulation.ClientData ، یک رابط است که اجازه می دهد تا شما را به شمردن مجموعه ای از کاربران، برای ساخت یک tf.data.Dataset که نشان دهنده داده ها از یک کاربر خاص، و به پرس و جو ساختار عناصر فردی در اینجا نحوه استفاده از این رابط برای کشف محتوای مجموعه داده ها آمده است. به خاطر داشته باشید که در حالی که این رابط به شما امکان می دهد تا شناسه های مشتری را تکرار کنید ، این تنها ویژگی داده های شبیه سازی است. همانطور که به زودی خواهید دید ، هویت مشتری توسط چارچوب یادگیری فدرال استفاده نمی شود - تنها هدف آنها این است که به شما اجازه می دهد زیر مجموعه داده ها را برای شبیه سازی انتخاب کنید.

len(emnist_train.client_ids)
3383
emnist_train.element_type_structure
OrderedDict([('label', TensorSpec(shape=(), dtype=tf.int32, name=None)), ('pixels', TensorSpec(shape=(28, 28), dtype=tf.float32, name=None))])
example_dataset = emnist_train.create_tf_dataset_for_client(
    emnist_train.client_ids[0])

example_element = next(iter(example_dataset))

example_element['label'].numpy()
1
from matplotlib import pyplot as plt
plt.imshow(example_element['pixels'].numpy(), cmap='gray', aspect='equal')
plt.grid(False)
_ = plt.show()

png

بررسی ناهمگونی در داده های تلفیقی

اطلاعات فدرال معمولا غیر IID ، کاربران به طور معمول دارای توزیع های مختلف داده بسته به الگوهای استفاده. برخی از مشتریان ممکن است نمونه های آموزشی کمتری بر روی دستگاه داشته باشند ، زیرا از کمبود داده ها به صورت محلی رنج می برند ، در حالی که برخی از مشتریان بیش از نمونه های آموزشی کافی خواهند داشت. بیایید این مفهوم از ناهمگونی داده ها را که معمولاً در یک سیستم متحد با داده های EMNIST در دسترس است بررسی کنیم. توجه به این نکته ضروری است که این تجزیه و تحلیل عمیق از داده های مشتری تنها در دسترس ما است زیرا این یک محیط شبیه سازی است که در آن تمام داده ها به صورت محلی در دسترس ما هستند. در یک محیط تولید واقعی واقعی ، شما نمی توانید داده های یک مشتری را بررسی کنید.

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

## Example MNIST digits for one client
figure = plt.figure(figsize=(20, 4))
j = 0

for example in example_dataset.take(40):
  plt.subplot(4, 10, j+1)
  plt.imshow(example['pixels'].numpy(), cmap='gray', aspect='equal')
  plt.axis('off')
  j += 1

png

اکنون بیایید تعداد نمونه های موجود در هر مشتری برای هر برچسب رقمی MNIST را تجسم کنیم. در محیط فدرال ، تعداد مثالها در هر مشتری بسته به رفتار کاربر می تواند بسیار متفاوت باشد.

# Number of examples per layer for a sample of clients
f = plt.figure(figsize=(12, 7))
f.suptitle('Label Counts for a Sample of Clients')
for i in range(6):
  client_dataset = emnist_train.create_tf_dataset_for_client(
      emnist_train.client_ids[i])
  plot_data = collections.defaultdict(list)
  for example in client_dataset:
    # Append counts individually per label to make plots
    # more colorful instead of one color per plot.
    label = example['label'].numpy()
    plot_data[label].append(label)
  plt.subplot(2, 3, i+1)
  plt.title('Client {}'.format(i))
  for j in range(10):
    plt.hist(
        plot_data[j],
        density=False,
        bins=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

png

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

# Each client has different mean images, meaning each client will be nudging
# the model in their own directions locally.

for i in range(5):
  client_dataset = emnist_train.create_tf_dataset_for_client(
      emnist_train.client_ids[i])
  plot_data = collections.defaultdict(list)
  for example in client_dataset:
    plot_data[example['label'].numpy()].append(example['pixels'].numpy())
  f = plt.figure(i, figsize=(12, 5))
  f.suptitle("Client #{}'s Mean Image Per Label".format(i))
  for j in range(10):
    mean_img = np.mean(plot_data[j], 0)
    plt.subplot(2, 5, j+1)
    plt.imshow(mean_img.reshape((28, 28)))
    plt.axis('off')

png

png

png

png

png

داده های کاربر می تواند پر سر و صدا و برچسب غیرقابل اعتماد داشته باشد. به عنوان مثال ، با نگاهی به داده های مشتری شماره 2 بالا ، می بینیم که برای برچسب 2 ، ممکن است برخی از نمونه های اشتباه برچسب گذاری شده باشند که یک تصویر متوسط ​​پر سر و صدا را ایجاد کرده اند.

پیش پردازش داده های ورودی

از آنجا که داده در حال حاضر یک tf.data.Dataset ، فرآیندی می تواند با استفاده از تحولات مجموعه داده انجام می شود. در اینجا، ما پهن 28x28 تصاویر را به 784 آرایه -Element، زدن مثال فردی، سازماندهی آنها را به دسته، و تغییر نام از ویژگی های از pixels و label به x و y برای استفاده با Keras. ما همچنین در یک پرتاب repeat بیش از مجموعه داده ها برای اجرای چندین دوره.

NUM_CLIENTS = 10
NUM_EPOCHS = 5
BATCH_SIZE = 20
SHUFFLE_BUFFER = 100
PREFETCH_BUFFER = 10

def preprocess(dataset):

  def batch_format_fn(element):
    """Flatten a batch `pixels` and return the features as an `OrderedDict`."""
    return collections.OrderedDict(
        x=tf.reshape(element['pixels'], [-1, 784]),
        y=tf.reshape(element['label'], [-1, 1]))

  return dataset.repeat(NUM_EPOCHS).shuffle(SHUFFLE_BUFFER, seed=1).batch(
      BATCH_SIZE).map(batch_format_fn).prefetch(PREFETCH_BUFFER)

بیایید تأیید کنیم که این کار کرده است.

preprocessed_example_dataset = preprocess(example_dataset)

sample_batch = tf.nest.map_structure(lambda x: x.numpy(),
                                     next(iter(preprocessed_example_dataset)))

sample_batch
OrderedDict([('x', array([[1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.],
       ...,
       [1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.]], dtype=float32)), ('y', array([[2],
       [1],
       [5],
       [7],
       [1],
       [7],
       [7],
       [1],
       [4],
       [7],
       [4],
       [2],
       [2],
       [5],
       [4],
       [1],
       [1],
       [0],
       [0],
       [9]], dtype=int32))])

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

یکی از راه های برای تغذیه پیوسته داده ها به TFF در یک شبیه سازی است که به سادگی به عنوان یک لیست پایتون، با هر عنصر از لیست برگزاری داده ها از کاربر افراد، چه به عنوان یک لیست و یا به عنوان یک tf.data.Dataset . از آنجا که ما قبلاً یک رابط کاربری داریم که مورد دوم را ارائه می دهد ، اجازه دهید از آن استفاده کنیم.

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

def make_federated_data(client_data, client_ids):
  return [
      preprocess(client_data.create_tf_dataset_for_client(x))
      for x in client_ids
  ]

حال ، چگونه مشتریان را انتخاب می کنیم؟

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

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

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

آنچه ما در عوض انجام می دهیم این است که مجموعه ای از کلاینت ها را یک بار نمونه برداری کرده و از مجموعه مشابه در دورها برای سرعت بخشیدن به همگرایی استفاده کنیم (عمداً بیش از حد متناسب با این چند داده کاربر). ما این کار را برای خواننده به عنوان تغییر این آموزش برای شبیه سازی نمونه گیری تصادفی در نظر می گیریم - انجام این کار بسیار آسان است (پس از انجام این کار ، به خاطر داشته باشید که همگرایی مدل ممکن است مدتی طول بکشد).

sample_clients = emnist_train.client_ids[0:NUM_CLIENTS]

federated_train_data = make_federated_data(emnist_train, sample_clients)

print('Number of client datasets: {l}'.format(l=len(federated_train_data)))
print('First dataset: {d}'.format(d=federated_train_data[0]))
Number of client datasets: 10
First dataset: <DatasetV1Adapter shapes: OrderedDict([(x, (None, 784)), (y, (None, 1))]), types: OrderedDict([(x, tf.float32), (y, tf.int32)])>

ایجاد مدل با Keras

اگر از Keras استفاده می کنید ، احتمالاً قبلاً کدی دارید که مدل Keras را ایجاد می کند. در اینجا نمونه ای از یک مدل ساده است که برای نیازهای ما کافی است.

def create_keras_model():
  return tf.keras.models.Sequential([
      tf.keras.layers.InputLayer(input_shape=(784,)),
      tf.keras.layers.Dense(10, kernel_initializer='zeros'),
      tf.keras.layers.Softmax(),
  ])

به منظور استفاده از هر مدل با TFF، آن را باید در یک نمونه از پیچیده می شود tff.learning.Model رابط، که در معرض روش به مهر پاس رو به جلو مدل، خواص ابرداده، و غیره، به طور مشابه به Keras، اما همچنین به معرفی اضافی عناصری مانند روشهای کنترل فرایند محاسبه معیارهای فدراسیون. اجازه دهید فعلا نگران این موضوع نباشیم. اگر شما یک مدل Keras مانند یکی از ما فقط در بالا تعریف شده است، شما می توانید TFF قرار دادن آن را برای شما با استناد به tff.learning.from_keras_model ، عبور از مدل و دسته ای از داده های نمونه را به عنوان آرگومان، به عنوان زیر نشان داده شده.

def model_fn():
  # We _must_ create a new model here, and _not_ capture it from an external
  # scope. TFF will call this within different graph contexts.
  keras_model = create_keras_model()
  return tff.learning.from_keras_model(
      keras_model,
      input_spec=preprocessed_example_dataset.element_spec,
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])

آموزش مدل بر روی داده های فدراسیون

حالا که ما یک مدل به عنوان پیچیده tff.learning.Model برای استفاده با TFF، ما می توانید اجازه دهید TFF ساخت یک الگوریتم به طور متوسط فدرال با توسل به تابع کمکی tff.learning.build_federated_averaging_process ، به شرح زیر.

به خاطر داشته باشید که استدلال نیاز به یک سازنده (مانند model_fn بالا)، نه یک نمونه در حال حاضر ساخته شده، به طوری که ساخت و ساز از مدل خود را می توانید در یک زمینه توسط TFF کنترل اتفاق می افتد (اگر شما کنجکاو هستید در مورد دلایل این کار، ما شما را تشویق به خواندن آموزش پیگیری الگوریتم سفارشی ).

یک نکته مهم در الگوریتم به طور متوسط فدرال زیر، 2 بهینه وجود دارد: یک بهینه ساز _client و بهینه ساز _SERVER. بهینه ساز _client است تنها مورد استفاده برای محاسبه به روز رسانی مدل محلی در هر مشتری. بهینه ساز _SERVER به روز رسانی به طور متوسط به مدل جهانی در سرور اعمال می شود. به طور خاص ، این بدان معناست که انتخاب بهینه ساز و میزان یادگیری مورد استفاده ممکن است متفاوت از مواردی باشد که شما برای آموزش مدل بر روی یک مجموعه داده استاندارد iid استفاده کرده اید. توصیه می کنیم با SGD معمولی شروع کنید ، احتمالاً با میزان یادگیری کوچکتر از حد معمول. میزان یادگیری مورد استفاده ما با دقت تنظیم نشده است ، امتحان کنید.

iterative_process = tff.learning.build_federated_averaging_process(
    model_fn,
    client_optimizer_fn=lambda: tf.keras.optimizers.SGD(learning_rate=0.02),
    server_optimizer_fn=lambda: tf.keras.optimizers.SGD(learning_rate=1.0))

چه اتفاقی افتاده؟ TFF ساخته شده است یک جفت از محاسبات فدراسیونی و آنها را به یک بسته بندی tff.templates.IterativeProcess که در آن این محاسبات در دسترس هستند به عنوان یک جفت از خواص initialize و next .

به طور خلاصه، محاسبات فدرال برنامه در زبان داخلی TFF است که می تواند الگوریتم های مختلف فدرال اکسپرس (شما می توانید بیشتر در مورد این در پیدا سفارشی الگوریتم آموزش). در این مورد، دو محاسبات تولید و بسته بندی شده به iterative_process اجرای فدرال به طور متوسط .

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

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

str(iterative_process.initialize.type_signature)
'( -> <model=<trainable=<float32[784,10],float32[10]>,non_trainable=<>>,optimizer_state=<int64>,delta_aggregate_state=<value_sum_process=<>,weight_sum_process=<>>,model_broadcast_state=<>>@SERVER)'

در حالی که امضای نوع بالا در ابتدا ممکن است به نظر می رسد مرموز کمی، شما می توانید تشخیص دهند که حالت سرور شامل یک model (پارامترهای مدل اولیه برای MNIST خواهد شد که به تمام دستگاه های توزیع شده)، و optimizer_state (اطلاعات اضافی که توسط سرور، مانند تعداد دورهایی که برای برنامه های پارامترها استفاده می شود و غیره).

بیایید فراخوان initialize محاسبات برای ساخت حالت سرور.

state = iterative_process.initialize()

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

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

SERVER_STATE, FEDERATED_DATA -> SERVER_STATE, TRAINING_METRICS

به طور خاص، باید در مورد فکر می کنم next() برخی از ورودی ها توسط سرور (ارائه - و نه به عنوان یک تابع است که قابل اجرا بر روی سرور، بلکه یک نمایندگی کاربردی اعلانی از کل محاسبات غیر متمرکز SERVER_STATE )، اما هر شرکت کننده دستگاه به مجموعه داده محلی خود کمک می کند.

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

state, metrics = iterative_process.next(state, federated_train_data)
print('round  1, metrics={}'.format(metrics))
round  1, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.12345679), ('loss', 3.1193738)])), ('stat', OrderedDict([('num_examples', 4860)]))])

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

NUM_ROUNDS = 11
for round_num in range(2, NUM_ROUNDS):
  state, metrics = iterative_process.next(state, federated_train_data)
  print('round {:2d}, metrics={}'.format(round_num, metrics))
round  2, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.13518518), ('loss', 2.9834728)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  3, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.14382716), ('loss', 2.861665)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  4, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.17407407), ('loss', 2.7957022)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  5, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.19917695), ('loss', 2.6146567)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  6, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.21975309), ('loss', 2.529761)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  7, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.2409465), ('loss', 2.4053504)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  8, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.2611111), ('loss', 2.315389)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  9, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.30823046), ('loss', 2.1240263)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round 10, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('sparse_categorical_accuracy', 0.33312756), ('loss', 2.1164262)])), ('stat', OrderedDict([('num_examples', 4860)]))])

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

نمایش معیارهای مدل در TensorBoard

در مرحله بعد ، اجازه دهید معیارهای این محاسبات فدرال را با استفاده از Tensorboard تجسم کنیم.

بیایید با ایجاد فهرست و خلاصه نویس مربوطه برای نوشتن معیارها شروع کنیم.

logdir = "/tmp/logs/scalars/training/"
summary_writer = tf.summary.create_file_writer(logdir)
state = iterative_process.initialize()

با همان خلاصه نویس ، معیارهای مقیاس مربوطه را ترسیم کنید.

with summary_writer.as_default():
  for round_num in range(1, NUM_ROUNDS):
    state, metrics = iterative_process.next(state, federated_train_data)
    for name, value in metrics['train'].items():
      tf.summary.scalar(name, value, step=round_num)

TensorBoard را با فهرست ورود ریشه ای که در بالا مشخص شده است ، راه اندازی کنید. بارگیری داده ها ممکن است چند ثانیه طول بکشد.

!ls {logdir}
%tensorboard --logdir {logdir} --port=0
events.out.tfevents.1629557449.ebe6e776479e64ea-4903924a278.borgtask.google.com.458912.1.v2
Launching TensorBoard...
Reusing TensorBoard on port 50681 (pid 292785), started 0:30:30 ago. (Use '!kill 292785' to kill it.)
<IPython.core.display.Javascript at 0x7fd6617e02d0>
# Uncomment and run this this cell to clean your directory of old output for
# future graphs from this directory. We don't run it by default so that if 
# you do a "Runtime > Run all" you don't lose your results.

# !rm -R /tmp/logs/scalars/*

به منظور مشاهده معیارهای ارزیابی به همان روش ، می توانید یک پوشه جداگانه eval مانند "logs/scalars/eval" ایجاد کنید تا در TensorBoard بنویسید.

سفارشی سازی پیاده سازی مدل

Keras است توصیه می شود-API سطح بالا مدل برای TensorFlow ، و ما را تشویق استفاده از مدل Keras (از طریق tff.learning.from_keras_model ) در TFF دوخته.

با این حال، tff.learning یک رابط مدل های پایین تر، فراهم می کند tff.learning.Model ، که در معرض قابلیت حداقل لازم برای استفاده از یک مدل برای یادگیری فدرال. به طور مستقیم اجرای این رابط (احتمالا هنوز هم با استفاده بلوک مانند tf.keras.layers ) اجازه می دهد تا برای حداکثر سفارشی بدون تغییر داخلی از الگوریتم های یادگیری فدرال.

بنابراین اجازه دهید همه چیز را دوباره از ابتدا انجام دهیم.

تعریف متغیرهای مدل ، گذر به جلو و معیارها

اولین قدم شناسایی متغیرهای TensorFlow است که قرار است با آنها کار کنیم. به منظور خوانایی بیشتر کد زیر ، بیایید یک ساختار داده برای نمایش کل مجموعه تعریف کنیم. این شامل متغیرهایی مانند weights و bias که ما آموزش، و همچنین به عنوان متغیرهای که آمار های مختلف تجمعی و شمارنده ما در طول آموزش به روز رسانی، مانند نگه loss_sum ، accuracy_sum و num_examples .

MnistVariables = collections.namedtuple(
    'MnistVariables', 'weights bias num_examples loss_sum accuracy_sum')

در اینجا روشی است که متغیرها را ایجاد می کند. به خاطر سادگی، ما نمایندگی از تمام آمار به عنوان tf.float32 ، همان طور که نیاز به نوع تبدیل در مرحله بعد از بین بردن. بسته بندی Initializer نام متغیر را به عنوان یک نیاز لامبداها اعمال شده توسط است متغیرهای منابع .

def create_mnist_variables():
  return MnistVariables(
      weights=tf.Variable(
          lambda: tf.zeros(dtype=tf.float32, shape=(784, 10)),
          name='weights',
          trainable=True),
      bias=tf.Variable(
          lambda: tf.zeros(dtype=tf.float32, shape=(10)),
          name='bias',
          trainable=True),
      num_examples=tf.Variable(0.0, name='num_examples', trainable=False),
      loss_sum=tf.Variable(0.0, name='loss_sum', trainable=False),
      accuracy_sum=tf.Variable(0.0, name='accuracy_sum', trainable=False))

با در نظر گرفتن متغیرهای پارامترهای مدل و آمار تجمعی ، اکنون می توانیم متد forward pass را تعریف کنیم که ضرر را محاسبه می کند ، پیش بینی ها را منتشر می کند و آمار تجمعی را برای یک دسته از داده های ورودی به شرح زیر به روز می کند.

def predict_on_batch(variables, x):
  return tf.nn.softmax(tf.matmul(x, variables.weights) + variables.bias)

def mnist_forward_pass(variables, batch):
  y = predict_on_batch(variables, batch['x'])
  predictions = tf.cast(tf.argmax(y, 1), tf.int32)

  flat_labels = tf.reshape(batch['y'], [-1])
  loss = -tf.reduce_mean(
      tf.reduce_sum(tf.one_hot(flat_labels, 10) * tf.math.log(y), axis=[1]))
  accuracy = tf.reduce_mean(
      tf.cast(tf.equal(predictions, flat_labels), tf.float32))

  num_examples = tf.cast(tf.size(batch['y']), tf.float32)

  variables.num_examples.assign_add(num_examples)
  variables.loss_sum.assign_add(loss * num_examples)
  variables.accuracy_sum.assign_add(accuracy * num_examples)

  return loss, predictions

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

در اینجا، ما به سادگی به طور متوسط بازگشت loss و accuracy ، و همچنین به عنوان num_examples ، که ما باید به درستی وزن سهم از کاربران مختلف در هنگام محاسبه مصالح فدرال.

def get_local_mnist_metrics(variables):
  return collections.OrderedDict(
      num_examples=variables.num_examples,
      loss=variables.loss_sum / variables.num_examples,
      accuracy=variables.accuracy_sum / variables.num_examples)

در نهایت، ما نیاز به تعیین چگونگی جمع معیارهای محلی از طریق ساطع شده از هر یک از دستگاه get_local_mnist_metrics . این تنها بخشی از کد است که در TensorFlow نوشته نشده است - این یک محاسبه فدرال بیان شده در TFF است. اگر شما می خواهم به حفاری عمیق تر، خامه ای بر سفارشی الگوریتم آموزش، اما در اکثر برنامه های کاربردی، شما واقعا نیاز دارید که نه؛ انواع الگوی نشان داده شده در زیر کافی است. در اینجا به نظر می رسد:

@tff.federated_computation
def aggregate_mnist_metrics_across_clients(metrics):
  return collections.OrderedDict(
      num_examples=tff.federated_sum(metrics.num_examples),
      loss=tff.federated_mean(metrics.loss, metrics.num_examples),
      accuracy=tff.federated_mean(metrics.accuracy, metrics.num_examples))

ورودی metrics مربوط آرگومان به OrderedDict بازگردانده شده توسط get_local_mnist_metrics بالا، اما به شدت از ارزش دیگر هستند tf.Tensors - آنها "جعبه" را به عنوان tff.Value S، آن را به روشن شما دیگر نمی تواند آنها را دستکاری با استفاده از TensorFlow، اما تنها با استفاده از عملگرهای فدرال TFF مانند tff.federated_mean و tff.federated_sum . فرهنگ لغت برگردانده شده از مجموعه های جهانی مجموعه ای از معیارها را که در سرور در دسترس خواهد بود ، تعریف می کند.

ساخت یک نمونه از tff.learning.Model

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

class MnistModel(tff.learning.Model):

  def __init__(self):
    self._variables = create_mnist_variables()

  @property
  def trainable_variables(self):
    return [self._variables.weights, self._variables.bias]

  @property
  def non_trainable_variables(self):
    return []

  @property
  def local_variables(self):
    return [
        self._variables.num_examples, self._variables.loss_sum,
        self._variables.accuracy_sum
    ]

  @property
  def input_spec(self):
    return collections.OrderedDict(
        x=tf.TensorSpec([None, 784], tf.float32),
        y=tf.TensorSpec([None, 1], tf.int32))

  @tf.function
  def predict_on_batch(self, x, training=True):
    del training
    return predict_on_batch(self._variables, x)

  @tf.function
  def forward_pass(self, batch, training=True):
    del training
    loss, predictions = mnist_forward_pass(self._variables, batch)
    num_exmaples = tf.shape(batch['x'])[0]
    return tff.learning.BatchOutput(
        loss=loss, predictions=predictions, num_examples=num_exmaples)

  @tf.function
  def report_local_outputs(self):
    return get_local_mnist_metrics(self._variables)

  @property
  def federated_output_computation(self):
    return aggregate_mnist_metrics_across_clients

همانطور که می بینید، روش انتزاعی و خواص تعریف شده توسط tff.learning.Model در بخش قبل که متغیرهای معرفی و از دست دادن و آمار تعریف به قطعه کد مربوط.

در اینجا چند نکته قابل ذکر است:

  • همه دولت که مدل خود را استفاده خواهد کرد باید به عنوان متغیرهای TensorFlow دستگیر می شود، به عنوان TFF کند پایتون در زمان اجرا استفاده نکنید (به یاد داشته باشید کد شما باید به گونه ای است که می تواند آن را به دستگاه های تلفن همراه مستقر نوشته شود؛ ببینید سفارشی الگوریتم آموزش برای یک بیشتر در عمق تفسیر دلایل).
  • مدل شما باید آنچه را شکل داده آن را می پذیرد ( input_spec به طور کلی)، به عنوان، TFF یک محیط به شدت تایپ است و می خواهد برای تعیین امضا نوع برای تمام اجزای. اعلام فرمت ورودی مدل شما بخش مهمی از آن است.
  • اگر چه از لحاظ فنی مورد نیاز، توصیه می کنیم بسته بندی تمام منطق TensorFlow (پاس رو به جلو، محاسبات متریک، و غیره) به عنوان tf.function بازدید کنندگان، به عنوان این کمک می کند تا اطمینان حاصل شود که TensorFlow می توان سریال، حذف نیاز به وابستگی کنترل صریح است.

موارد فوق برای ارزیابی و الگوریتم هایی مانند Federated SGD کافی است. با این حال ، برای میانگین یابی فدرال ، باید مشخص کنیم که چگونه مدل باید به صورت محلی در هر دسته تمرین کند. هنگام ایجاد الگوریتم میانگین فدرال ، یک بهینه ساز محلی را مشخص می کنیم.

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

با رعایت همه موارد فوق ، بقیه فرایند شبیه آنچه قبلاً دیده ایم - کافی است سازنده مدل را با سازنده کلاس مدل جدید خود جایگزین کنید و از دو محاسبه متحد در فرایند تکراری که ایجاد کرده اید استفاده کنید دورهای آموزشی

iterative_process = tff.learning.build_federated_averaging_process(
    MnistModel,
    client_optimizer_fn=lambda: tf.keras.optimizers.SGD(learning_rate=0.02))
state = iterative_process.initialize()
state, metrics = iterative_process.next(state, federated_train_data)
print('round  1, metrics={}'.format(metrics))
round  1, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 3.0708053), ('accuracy', 0.12777779)])), ('stat', OrderedDict([('num_examples', 4860)]))])
for round_num in range(2, 11):
  state, metrics = iterative_process.next(state, federated_train_data)
  print('round {:2d}, metrics={}'.format(round_num, metrics))
round  2, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 3.011699), ('accuracy', 0.13024691)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  3, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.7408307), ('accuracy', 0.15576132)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  4, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.6761012), ('accuracy', 0.17921811)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  5, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.675567), ('accuracy', 0.1855967)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  6, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.5664043), ('accuracy', 0.20329218)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  7, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.4179392), ('accuracy', 0.24382716)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  8, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.3237286), ('accuracy', 0.26687244)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round  9, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.1861682), ('accuracy', 0.28209877)])), ('stat', OrderedDict([('num_examples', 4860)]))])
round 10, metrics=OrderedDict([('broadcast', ()), ('aggregation', OrderedDict([('mean_value', ()), ('mean_weight', ())])), ('train', OrderedDict([('num_examples', 4860.0), ('loss', 2.046388), ('accuracy', 0.32037038)])), ('stat', OrderedDict([('num_examples', 4860)]))])

برای مشاهده این معیارها در TensorBoard ، به مراحل ذکر شده در بالا در "نمایش معیارهای مدل در TensorBoard" مراجعه کنید.

ارزیابی

تمام آزمایشات ما تا کنون فقط معیارهای آموزش فدرال را ارائه کرده است - معیارهای متوسط ​​برای همه دسته های داده که در همه مشتریان در دور آموزش داده شده است. این امر نگرانی های عادی در مورد بیش از حد اتصالات را مطرح می کند ، به ویژه اینکه ما در هر دور برای سادگی از مجموعه ای از مشتریان استفاده کردیم ، اما یک مفهوم اضافی از اضافه تناسب در معیارهای آموزشی مخصوص الگوریتم میانگین فدرال وجود دارد. این به سادگی قابل فهم است که آیا تصور می کنیم که هر مشتری یک دسته واحد داده داشته است ، و ما در آن دسته چندین بار (دوره ها) آموزش می دهیم. در این مورد ، مدل محلی به سرعت دقیقاً با آن دسته سازگار می شود ، و بنابراین معیار دقت محلی ما به طور متوسط ​​به 1.0 می رسد. بنابراین ، این معیارهای آموزشی را می توان نشانه پیشرفت آموزش دانست ، اما نه بیشتر.

برای انجام ارزیابی بر روی داده های فدرال، شما می توانید یکی دیگر از محاسبات فدرال طراحی فقط برای این منظور ساخت، با استفاده از tff.learning.build_federated_evaluation تابع، و عبور در سازنده مدل خود را به عنوان یک استدلال. توجه داشته باشید که بر خلاف با فدرال به طور متوسط، که در آن ما استفاده کرده ایم MnistTrainableModel ، کافی است به تصویب MnistModel . ارزیابی نزول گرادیان را انجام نمی دهد و نیازی به ایجاد بهینه سازها نیست.

برای آزمایش و پژوهش، هنگامی که یک مجموعه داده آزمون متمرکز در دسترس است، فدرال آموزشی برای متن ایجاد نشان می دهد گزینه ارزیابی دیگر: در نظر گرفتن وزن آموزش دیده از یادگیری فدرال، استفاده از آنها را به یک مدل استاندارد Keras، و سپس به سادگی با تماس tf.keras.models.Model.evaluate() در یک مجموعه داده متمرکز است.

evaluation = tff.learning.build_federated_evaluation(MnistModel)

می توانید امضای نوع انتزاعی تابع ارزیابی را به شرح زیر بررسی کنید.

str(evaluation.type_signature)
'(<server_model_weights=<trainable=<float32[784,10],float32[10]>,non_trainable=<>>@SERVER,federated_dataset={<x=float32[?,784],y=int32[?,1]>*}@CLIENTS> -> <eval=<num_examples=float32,loss=float32,accuracy=float32>,stat=<num_examples=int64>>@SERVER)'

بدون نیاز به در مورد جزئیات در این نقطه مربوط می شود، فقط آگاه باشید که آن طول می کشد به شکل زیر به طور کلی، شبیه به tff.templates.IterativeProcess.next اما با دو تفاوت مهم است. اول ، ما حالت سرور را بر نمی گردانیم ، زیرا ارزیابی مدل یا هر جنبه دیگری از حالت را تغییر نمی دهد - شما می توانید آن را بدون حالت تصور کنید. دوم ، ارزیابی فقط به مدل نیاز دارد و نیازی به بخش دیگری از وضعیت سرور ندارد که ممکن است با آموزش مرتبط باشد ، مانند متغیرهای بهینه ساز.

SERVER_MODEL, FEDERATED_DATA -> TRAINING_METRICS

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

train_metrics = evaluation(state.model, federated_train_data)

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

str(train_metrics)
"OrderedDict([('eval', OrderedDict([('num_examples', 4860.0), ('loss', 1.7510437), ('accuracy', 0.2788066)])), ('stat', OrderedDict([('num_examples', 4860)]))])"

اکنون ، بیایید یک نمونه آزمایشی از داده های یکپارچه شده و ارزیابی مجدد بر روی داده های آزمون را جمع آوری کنیم. داده ها از همان نمونه کاربران واقعی ، اما از یک مجموعه داده متمایز تهیه می شود.

federated_test_data = make_federated_data(emnist_test, sample_clients)

len(federated_test_data), federated_test_data[0]
(10,
 <DatasetV1Adapter shapes: OrderedDict([(x, (None, 784)), (y, (None, 1))]), types: OrderedDict([(x, tf.float32), (y, tf.int32)])>)
test_metrics = evaluation(state.model, federated_test_data)
str(test_metrics)
"OrderedDict([('eval', OrderedDict([('num_examples', 580.0), ('loss', 1.8361608), ('accuracy', 0.2413793)])), ('stat', OrderedDict([('num_examples', 580)]))])"

با این کار آموزش به پایان می رسد. ما شما را تشویق می کنیم که با پارامترها (به عنوان مثال ، اندازه دسته ، تعداد کاربران ، دوره ها ، نرخ یادگیری و غیره) بازی کنید ، تا کد بالا را برای شبیه سازی آموزش در نمونه های تصادفی از کاربران در هر دور تغییر دهید و سایر آموزش ها را بررسی کنید. توسعه داده ایم