ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

التعلم الموحد لتصنيف الصور

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

في هذا البرنامج التعليمي ، نستخدم مثال تدريب MNIST الكلاسيكي لتقديم طبقة API للتعلم الفيدرالي (FL) من TFF ، tff.learning - مجموعة من واجهات المستوى الأعلى التي يمكن استخدامها لأداء أنواع شائعة من مهام التعلم الموحد ، مثل تدريب متحد مقابل النماذج التي يوفرها المستخدم المطبقة في TensorFlow.

هذا البرنامج التعليمي و Federated Learning API مخصصان بشكل أساسي للمستخدمين الذين يرغبون في توصيل نماذج TensorFlow الخاصة بهم في TFF ، مع التعامل مع الأخير في الغالب على أنه صندوق أسود. للحصول على فهم أعمق لـ TFF وكيفية تنفيذ خوارزميات التعلم الموحدة الخاصة بك ، راجع البرامج التعليمية حول FC Core API - الخوارزميات الموحدة المخصصة الجزء 1 والجزء 2 .

لمزيد من المعلومات حول tff.learning ، تابع التعلم tff.learning لإنشاء النص ، البرنامج التعليمي الذي بالإضافة إلى تغطية النماذج المتكررة ، يوضح أيضًا تحميل نموذج Keras المتسلسل المدربين مسبقًا للتنقيح مع التعلم الموحد جنبًا إلى جنب مع التقييم باستخدام Keras.

قبل أن نبدأ

قبل أن نبدأ ، يرجى تشغيل ما يلي للتأكد من إعداد بيئتك بشكل صحيح. إذا كنت لا ترى ترحيبًا ، فيرجى الرجوع إلى دليل التثبيت للحصول على الإرشادات.


!pip install --quiet --upgrade tensorflow_federated_nightly
!pip install --quiet --upgrade nest_asyncio

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 الأصلية التي تمت إعادة معالجتها باستخدام Leaf بحيث يتم مفتاح البيانات بواسطة الكاتب الأصلي لـ الأرقام. نظرًا لأن لكل كاتب أسلوبًا فريدًا ، تعرض مجموعة البيانات هذه نوع السلوك غير المعتمد المتوقع لمجموعات البيانات الموحدة.

إليك كيف يمكننا تحميله.

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

مجموعات البيانات التي يتم إرجاعها بواسطة load_data() هي tff.simulation.ClientData على tff.simulation.ClientData ، وهي واجهة تسمح لك بتعداد مجموعة المستخدمين ، لإنشاء tf.data.Dataset التي تمثل بيانات مستخدم معين ، والاستعلام عن هيكل العناصر الفردية. إليك كيفية استخدام هذه الواجهة لاستكشاف محتوى مجموعة البيانات. ضع في اعتبارك أنه على الرغم من أن هذه الواجهة تسمح لك بالتكرار عبر هويات العملاء ، فإن هذه ليست سوى ميزة لبيانات المحاكاة. كما سترى قريبًا ، لا يتم استخدام هويات العميل بواسطة إطار عمل التعلم الموحد - والغرض الوحيد منها هو السماح لك بتحديد مجموعات فرعية من البيانات من أجل عمليات المحاكاة.

len(emnist_train.client_ids)
3383
emnist_train.element_type_structure
OrderedDict([('pixels', TensorSpec(shape=(28, 28), dtype=tf.float32, name=None)), ('label', TensorSpec(shape=(), dtype=tf.int32, 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()

بي إن جي

استكشاف عدم التجانس في البيانات الموحدة

عادةً ما تكون البيانات الموحدة غير 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

بي إن جي

الآن دعنا نتخيل عدد الأمثلة على كل عميل لكل تسمية رقم 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])

بي إن جي

الآن دعنا نتخيل الصورة المتوسطة لكل عميل لكل تسمية 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')

بي إن جي

بي إن جي

بي إن جي

بي إن جي

بي إن جي

يمكن أن تكون بيانات المستخدم صاخبة ولا يمكن الاعتماد عليها. على سبيل المثال ، بالنظر إلى بيانات العميل رقم 2 أعلاه ، يمكننا أن نرى أنه بالنسبة للتسمية 2 ، من الممكن أنه قد تكون هناك بعض الأمثلة التي تم تسميتها بشكل خاطئ مما أدى إلى إنشاء صورة متوسطة أكثر ضوضاء.

المعالجة المسبقة لبيانات الإدخال

نظرًا لأن البيانات هي بالفعل tf.data.Dataset ، يمكن إنجاز المعالجة المسبقة باستخدام تحويلات مجموعة البيانات. هنا ، نقوم بتسوية الصور 28x28 في مصفوفة مكونة من 784 عنصرًا ، وخلط الأمثلة الفردية ، وتنظيمها في مجموعات ، وإعادة تسمية الميزات من 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).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],
       [2],
       [3],
       [6],
       [0],
       [1],
       [4],
       [1],
       [0],
       [6],
       [9],
       [9],
       [3],
       [6],
       [1],
       [4],
       [8],
       [0],
       [2]], dtype=int32))])

لدينا جميع اللبنات الأساسية تقريبًا لبناء مجموعات بيانات موحدة.

إحدى طرق تغذية البيانات الموحدة إلى TFF في المحاكاة هي ببساطة قائمة Python ، حيث يحتفظ كل عنصر في القائمة ببيانات مستخدم فردي ، سواء كقائمة أو كملف 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.Input(shape=(784,)),
      tf.keras.layers.Dense(10, kernel_initializer='zeros'),
      tf.keras.layers.Softmax(),
  ])

من أجل استخدام أي نموذج مع TFF ، يجب تغليفه في مثيل tff.learning.Model interface ، الذي يعرض طرقًا لختم المرور الأمامي للنموذج ، وخصائص البيانات الوصفية ، وما إلى ذلك ، على غرار Keras ، ولكنه يقدم أيضًا المزيد العناصر ، مثل طرق التحكم في عملية حساب المقاييس الموحدة. دعونا لا نقلق بشأن هذا الآن ؛ إذا كان لديك نموذج Keras مثل النموذج الذي حددناه للتو ، فيمكنك أن tff.learning.from_keras_model لك عن طريق استدعاء tff.learning.from_keras_model ، وتمرير النموذج ومجموعة بيانات نموذجية 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 (مثل model_fn أعلاه) ، وليس model_fn بالفعل ، بحيث يمكن أن يحدث بناء نموذجك في سياق يتحكم فيه TFF (إذا كنت model_fn أسباب هذا ، نشجعك على قراءة البرنامج التعليمي للمتابعة حول الخوارزميات المخصصة ).

ملاحظة واحدة حاسمة على خوارزمية المتوسط الاتحادية أدناه، وهناك 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 في عملية iterative_process يقومان بتنفيذ المتوسطات الموحدة .

من أهداف TFF تحديد العمليات الحسابية بطريقة يمكن تنفيذها في إعدادات التعلم الفيدرالي الحقيقية ، ولكن يتم حاليًا تنفيذ وقت تشغيل محاكاة التنفيذ المحلي فقط. لتنفيذ عملية حسابية في جهاز محاكاة ، يمكنك ببساطة استدعاؤها مثل دالة Python. لم يتم تصميم بيئة التفسير الافتراضية هذه للحصول على أداء عالٍ ، لكنها ستكون كافية لهذا البرنامج التعليمي ؛ نتوقع توفير أوقات تشغيل محاكاة عالية الأداء لتسهيل البحث على نطاق واسع في الإصدارات المستقبلية.

لنبدأ بحساب initialize . كما هو الحال بالنسبة لجميع الحسابات الموحدة ، يمكنك التفكير في الأمر كدالة. لا يأخذ الحساب أي وسيطات ، ويعيد نتيجة واحدة - تمثيل حالة عملية المتوسطات الموحدة على الخادم. على الرغم من أننا لا نريد الغوص في تفاصيل TFF ، فقد يكون من المفيد معرفة شكل هذه الحالة. يمكنك تصور ذلك على النحو التالي.

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

في حين أن النوع توقيع أعلاه قد يبدو في البداية وخفي بعض الشيء، يمكنك ندرك أن حالة الملقم يتكون من model (معالم النموذج الأولي لMNIST التي سيتم توزيعها على جميع الأجهزة)، و optimizer_state المحافظة (معلومات إضافية من قبل الملقم، مثل عدد الدورات لاستخدامها في جداول المعامِلات الفائقة ، وما إلى ذلك).

دعونا نستدعي حساب initialize لبناء حالة الخادم.

عبدالمجيد

ثاني زوج من الحسابات الموحدة ، next ، يمثل جولة واحدة من المتوسطات الموحدة ، والتي تتكون من دفع حالة الخادم (بما في ذلك معلمات النموذج) إلى العملاء ، والتدريب على الجهاز على بياناتهم المحلية ، وجمع تحديثات النموذج ومتوسطها ، وإنتاج نموذج جديد محدث على الخادم.

من الناحية المفاهيمية ، يمكنك التفكير في next على أنه توقيع نوع وظيفي يبدو على النحو التالي.

SERVER_STATE, FEDERATED_DATA -> SERVER_STATE, TRAINING_METRICS

على وجه الخصوص ، يجب على المرء أن يفكر في next() ليس على أنه وظيفة تعمل على الخادم ، بل هو تمثيل وظيفي SERVER_STATE للحساب اللامركزي بأكمله - يتم توفير بعض المدخلات بواسطة الخادم ( SERVER_STATE ) ، ولكن كل مشارك مشارك يساهم الجهاز بمجموعة البيانات المحلية الخاصة به.

لنجري جولة واحدة من التدريب ونتخيل النتائج. يمكننا استخدام البيانات الموحدة التي أنشأناها بالفعل أعلاه لعينة من المستخدمين.

state, metrics = iterative_process.next(state, federated_train_data)
print('round  1, metrics={}'.format(metrics))
round  1, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.12037037312984467,loss=3.0108425617218018>>

دعونا نجري بضع جولات أخرى. كما ذكرنا سابقًا ، عادةً في هذه المرحلة ، يمكنك اختيار مجموعة فرعية من بيانات المحاكاة الخاصة بك من عينة جديدة تم اختيارها عشوائيًا من المستخدمين لكل جولة من أجل محاكاة نشر واقعي حيث يأتي المستخدمون ويذهبون باستمرار ، ولكن في هذا الكمبيوتر المحمول التفاعلي من أجل العرض التوضيحي ، سنقوم فقط بإعادة استخدام نفس المستخدمين ، بحيث يتقارب النظام بسرعة.

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=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.14814814925193787,loss=2.8865506649017334>>
round  3, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.148765429854393,loss=2.9079062938690186>>
round  4, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.17633745074272156,loss=2.724686622619629>>
round  5, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.20226337015628815,loss=2.6334855556488037>>
round  6, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.22427983582019806,loss=2.5482592582702637>>
round  7, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.24094650149345398,loss=2.4472343921661377>>
round  8, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.259876549243927,loss=2.3809611797332764>>
round  9, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.29814815521240234,loss=2.156442403793335>>
round 10, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.31687241792678833,loss=2.122845411300659>>

يتناقص فقدان التدريب بعد كل جولة من التدريب الموحد ، مما يشير إلى أن النموذج يتقارب. هناك بعض التحذيرات المهمة المتعلقة بمقاييس التدريب هذه ، ومع ذلك ، راجع القسم الخاص بالتقييم لاحقًا في هذا البرنامج التعليمي.

عرض مقاييس النموذج في 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._asdict().items():
      tf.summary.scalar(name, value, step=round_num)

ابدأ TensorBoard بدليل السجل الجذر المحدد أعلاه. قد يستغرق تحميل البيانات بضع ثوانٍ.


%tensorboard --logdir /tmp/logs/scalars/ --port=0

# Run this this cell to clean your directory of old output for future graphs from this directory.
rm -R /tmp/logs/scalars/*

لعرض مقاييس التقييم بالطريقة نفسها ، يمكنك إنشاء مجلد تقييم منفصل ، مثل "logs / scalars / Eval" ، للكتابة إلى TensorBoard.

تخصيص تنفيذ النموذج

Keras هو نموذج API عالي المستوى الموصى به لـ TensorFlow ، ونحن نشجع استخدام نماذج Keras (عبر tff.learning.from_keras_model ) في TFF كلما أمكن ذلك.

ومع ذلك ، يوفر tff.learning واجهة نموذج ذات مستوى أدنى ، tff.learning.Model ، التي تعرض الحد الأدنى من الوظائف اللازمة لاستخدام نموذج للتعلم الموحد. يسمح التنفيذ المباشر لهذه الواجهة (ربما لا تزال تستخدم كتل tf.keras.layers مثل tf.keras.layers ) بأقصى قدر من التخصيص دون تعديل 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 ، حيث سيؤدي ذلك إلى التخلص من الحاجة إلى تحويلات النوع في مرحلة لاحقة. التفاف المبدئ المتغير مثل لامدا هو مطلب تفرضه متغيرات الموارد .

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

مع وجود متغيرات معلمات النموذج والإحصاءات التراكمية في مكانها الصحيح ، يمكننا الآن تحديد طريقة المرور إلى الأمام التي تحسب الخسارة وتصدر التنبؤات وتحدث الإحصائيات التراكمية لمجموعة واحدة من بيانات الإدخال ، على النحو التالي.

ايمو

بعد ذلك ، نحدد دالة تقوم بإرجاع مجموعة من المقاييس المحلية ، مرة أخرى باستخدام TensorFlow. هذه هي القيم (بالإضافة إلى تحديثات النموذج ، التي يتم التعامل معها تلقائيًا) المؤهلة لتجميعها على الخادم في عملية التعلم أو التقييم الموحدة.

هنا ، نعيد ببساطة متوسط loss accuracy ، بالإضافة إلى num_examples ، التي 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 ، tff.Value أنه لم يعد بإمكانك معالجتها باستخدام 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 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 لا تستخدم لغة Python في وقت التشغيل (تذكر أنه يجب كتابة التعليمات البرمجية بحيث يمكن نشرها على الأجهزة المحمولة ؛ راجع البرنامج التعليمي للخوارزميات المخصصة لمزيد من التعمق التعليق على الأسباب).
  • يجب أن يصف نموذجك شكل البيانات التي يقبلها ( input_spec ) ، بشكل عام ، TFF هي بيئة مكتوبة بشدة وتريد تحديد توقيعات النوع لجميع المكونات. يُعد الإعلان عن تنسيق مدخلات النموذج جزءًا أساسيًا منه.
  • على الرغم من أنه ليس مطلوبًا من الناحية الفنية ، فإننا نوصي بلف جميع منطق TensorFlow (التمرير الأمامي ، والحسابات المترية ، وما إلى ذلك) tf.function ، حيث يساعد ذلك في ضمان إمكانية إجراء تسلسل لـ TensorFlow ، ويزيل الحاجة إلى تبعيات تحكم واضحة.

ما ورد أعلاه كافٍ للتقييم والخوارزميات مثل SGD الموحدة. ومع ذلك ، بالنسبة إلى Federated Averaging ، نحتاج إلى تحديد كيفية تدريب النموذج محليًا على كل دفعة. سنحدد مُحسِّنًا محليًا عند بناء خوارزمية المتوسطات الموحدة.

محاكاة التدريب الفدرالي بالنموذج الجديد

مع وضع كل ما سبق في مكانه الصحيح ، تبدو بقية العملية كما رأينا بالفعل - ما عليك سوى استبدال مُنشئ النموذج بمنشئ فئة النموذج الجديد ، واستخدام الحسابين المتحدتين في العملية التكرارية التي قمت بإنشائها للتنقل خلالها جولات التدريب.

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=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.9713594913482666,accuracy=0.13518518209457397>>

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=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.975412607192993,accuracy=0.14032921195030212>>
round  3, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.9395227432250977,accuracy=0.1594650149345398>>
round  4, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.710164785385132,accuracy=0.17139917612075806>>
round  5, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.5891618728637695,accuracy=0.20267489552497864>>
round  6, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.5148487091064453,accuracy=0.21666666865348816>>
round  7, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.2816808223724365,accuracy=0.2580246925354004>>
round  8, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.3656885623931885,accuracy=0.25884774327278137>>
round  9, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.23549222946167,accuracy=0.28477364778518677>>
round 10, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=1.974222183227539,accuracy=0.35329216718673706>>

للاطلاع على هذه المقاييس داخل TensorBoard ، راجع الخطوات المذكورة أعلاه في "عرض مقاييس النموذج في TensorBoard".

تقييم

قدمت جميع تجاربنا حتى الآن مقاييس تدريب موحدة فقط - متوسط ​​المقاييس على جميع مجموعات البيانات المدربة عبر جميع العملاء في الجولة. يقدم هذا المخاوف العادية بشأن فرط التخصيص ، خاصة وأننا استخدمنا نفس مجموعة العملاء في كل جولة من أجل البساطة ، ولكن هناك فكرة إضافية عن التخصيص الزائد في مقاييس التدريب الخاصة بخوارزمية المتوسطات الموحدة. من الأسهل معرفة ما إذا كنا نتخيل أن كل عميل لديه مجموعة واحدة من البيانات ، ونحن نتدرب على هذه المجموعة للعديد من التكرارات (العهود). في هذه الحالة ، سيتناسب النموذج المحلي تمامًا مع تلك الدُفعة الواحدة ، وبالتالي فإن مقياس الدقة المحلي الذي نتوسطه سيقترب من 1.0. وبالتالي ، يمكن اعتبار مقاييس التدريب هذه علامة على تقدم التدريب ، ولكن ليس أكثر من ذلك بكثير.

لإجراء تقييم على البيانات الموحدة ، يمكنك إنشاء حساب tff.learning.build_federated_evaluation آخر مصمم لهذا الغرض فقط ، باستخدام دالة tff.learning.build_federated_evaluation ، وتمرير مُنشئ النموذج كوسيطة. لاحظ أنه على عكس MnistTrainableModel الفدرالي ، حيث استخدمنا MnistTrainableModel ، يكفي تمرير نموذج MnistModel . لا يؤدي التقييم نزولًا متدرجًا ، ولا داعي لإنشاء أدوات تحسين.

للتجربة والبحث ، عند توفر مجموعة بيانات اختبار مركزية ، يوضح التعلم الموحد لإنشاء النص خيار تقييم آخر: أخذ الأوزان المدربة من التعلم الموحد ، وتطبيقها على نموذج Keras القياسي ، ثم استدعاء tf.keras.models.Model.evaluate() في مجموعة بيانات مركزية.

evaluation = tff.learning.build_federated_evaluation(MnistModel)

يمكنك فحص توقيع النوع المجرد لوظيفة التقييم على النحو التالي.

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

لا داعي للقلق بشأن التفاصيل في هذه المرحلة ، فقط كن على دراية بأنها تأخذ الشكل العام التالي ، على غرار tff.templates.IterativeProcess.next ولكن مع tff.templates.IterativeProcess.next مهمين. أولاً ، نحن لا نعيد حالة الخادم ، نظرًا لأن التقييم لا يعدل النموذج أو أي جانب آخر من جوانب الحالة - يمكنك اعتباره عديم الحالة. ثانيًا ، يحتاج التقييم إلى النموذج فقط ، ولا يتطلب أي جزء آخر من حالة الخادم قد يرتبط بالتدريب ، مثل متغيرات المحسن.

SERVER_MODEL, FEDERATED_DATA -> TRAINING_METRICS

دعنا نطلب تقييم أحدث حالة وصلنا إليها أثناء التدريب. لاستخراج أحدث نموذج تم تدريبه من حالة الخادم ، يمكنك ببساطة الوصول إلى عضو .model ، على النحو التالي.

train_metrics = evaluation(state.model, federated_train_data)

هذا ما نحصل عليه. لاحظ أن الأرقام تبدو أفضل بشكل هامشي مما تم الإبلاغ عنه في الجولة الأخيرة من التدريب أعلاه. وفقًا للاتفاقية ، تعكس مقاييس التدريب التي تم الإبلاغ عنها بواسطة عملية التدريب التكراري بشكل عام أداء النموذج في بداية جولة التدريب ، وبالتالي فإن مقاييس التقييم ستكون دائمًا متقدمًا بخطوة.

str(train_metrics)
'<num_examples=4860.0,loss=1.7142657041549683,accuracy=0.38683128356933594>'

الآن ، دعونا نقوم بتجميع عينة اختبار من البيانات الموحدة وإعادة التقييم على بيانات الاختبار. ستأتي البيانات من نفس العينة من المستخدمين الحقيقيين ، ولكن من مجموعة بيانات ثابتة ومميزة.

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)
'<num_examples=580.0,loss=1.861915111541748,accuracy=0.3362068831920624>'

هذا يختتم البرنامج التعليمي. نحن نشجعك على اللعب بالمعلمات (على سبيل المثال ، أحجام الدُفعات ، وعدد المستخدمين ، والعهود ، ومعدلات التعلم ، وما إلى ذلك) ، لتعديل الكود أعلاه لمحاكاة التدريب على عينات عشوائية من المستخدمين في كل جولة ، واستكشاف البرامج التعليمية الأخرى لقد طورنا.