مشاهده در TensorFlow.org | در Google Colab اجرا شود | مشاهده منبع در GitHub | دانلود دفترچه یادداشت |
در این آموزش قسمت دوم از سری دو قسمتی که نشان می دهد که چگونه برای اجرای انواع سفارشی از الگوریتم های فدرال در TFF با استفاده از است فدرال هسته (FC) ، که به عنوان یک پایه برای خدمت فدرال آموزش (FL) لایه ( tff.learning
) .
ما شما را تشویق به اولین به عنوان خوانده شده بخش اول از این سری ، که معرفی برخی از مفاهیم کلیدی و انتزاعی برنامه نویسی مورد استفاده در اینجا.
این قسمت دوم مجموعه از مکانیسم های معرفی شده در قسمت اول برای پیاده سازی نسخه ساده الگوریتم های آموزش و ارزیابی فدرال استفاده می کند.
ما شما را تشویق به بررسی طبقه بندی تصویر و متن آموزش برای سطح بالاتر و معرفی ملایم تر به فدرال رابط های برنامه کاربردی آموزش TFF، به عنوان آنها به شما کمک خواهد مفاهیم ما در اینجا در زمینه توصیف قرار داده است.
قبل از اینکه شروع کنیم
قبل از شروع، سعی کنید مثال "Hello World" زیر را اجرا کنید تا مطمئن شوید که محیط شما به درستی تنظیم شده است. اگر آن کار نمی کند، لطفا به مراجعه نصب و راه اندازی راهنمای دستورالعمل.
!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio
import nest_asyncio
nest_asyncio.apply()
import collections
import numpy as np
import tensorflow as tf
import tensorflow_federated as tff
# Must use the Python context because it
# supports tff.sequence_* intrinsics.
executor_factory = tff.framework.local_executor_factory(
support_sequence_ops=True)
execution_context = tff.framework.ExecutionContext(
executor_fn=executor_factory)
tff.framework.set_default_context(execution_context)
@tff.federated_computation
def hello_world():
return 'Hello, World!'
hello_world()
b'Hello, World!'
اجرای میانگین گیری فدرال
همانطور که در آموزش فدرال برای طبقه بندی تصویر ، ما می رویم به عنوان مثال استفاده MNIST، اما از آنجایی که این به عنوان یک آموزش سطح پایین در نظر گرفته شده، ما می رویم برای دور زدن API Keras و tff.simulation
، ارسال کد مدل اولیه، و ساخت یک مجموعه داده های فدرال از ابتدا
آماده سازی مجموعه داده های فدرال
بهمنظور نمایش، میخواهیم سناریویی را شبیهسازی کنیم که در آن دادههایی از 10 کاربر داریم، و هر یک از کاربران دانشی را در مورد چگونگی تشخیص رقم متفاوت ارائه میکنند. این مورد به عنوان غیر IID عنوان آن می شود.
ابتدا داده های استاندارد MNIST را بارگیری می کنیم:
mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()
[(x.dtype, x.shape) for x in mnist_train]
[(dtype('uint8'), (60000, 28, 28)), (dtype('uint8'), (60000,))]
دادهها بهصورت آرایههای Numpy، یکی با تصاویر و دیگری با برچسبهای رقمی ارائه میشوند، که هر دو بعد اول بر روی نمونههای جداگانه میروند. بیایید یک تابع کمکی بنویسیم که آن را به گونهای قالببندی کند که با نحوه تغذیه توالیهای فدرال به محاسبات TFF سازگار باشد، به عنوان مثال، بهعنوان فهرستی از فهرستها - فهرست بیرونی در محدوده کاربران (ارقام)، فهرستهای داخلی که در دستهای از دادهها قرار دارند. دنباله هر مشتری همانطور که مرسوم است، ما هر دسته به عنوان یک جفت تانسورها به نام ساختار x
و y
، هر کدام با ابعاد دسته ای پیشرو. در حالی که در آن، ما نیز هر تصویر را به یک بردار 784 عنصر پهن و تغییر اندازه دهید پیکسل در آن را به 0..1
وسیعی، به طوری که ما لازم نیست به درهم و برهمی منطق مدل با تبدیل داده ها.
NUM_EXAMPLES_PER_USER = 1000
BATCH_SIZE = 100
def get_data_for_digit(source, digit):
output_sequence = []
all_samples = [i for i, d in enumerate(source[1]) if d == digit]
for i in range(0, min(len(all_samples), NUM_EXAMPLES_PER_USER), BATCH_SIZE):
batch_samples = all_samples[i:i + BATCH_SIZE]
output_sequence.append({
'x':
np.array([source[0][i].flatten() / 255.0 for i in batch_samples],
dtype=np.float32),
'y':
np.array([source[1][i] for i in batch_samples], dtype=np.int32)
})
return output_sequence
federated_train_data = [get_data_for_digit(mnist_train, d) for d in range(10)]
federated_test_data = [get_data_for_digit(mnist_test, d) for d in range(10)]
عنوان بررسی سلامت عقل سریع، نگاه اجازه دهید در Y
تانسور در آخرین دسته از داده های ارائه شده توسط مشتری پنجم (که یکی مربوط به رقم 5
).
federated_train_data[5][-1]['y']
array([5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], dtype=int32)
فقط برای اطمینان، بیایید به تصویر مربوط به آخرین عنصر آن دسته نیز نگاه کنیم.
from matplotlib import pyplot as plt
plt.imshow(federated_train_data[5][-1]['x'][-1].reshape(28, 28), cmap='gray')
plt.grid(False)
plt.show()
در مورد ترکیب TensorFlow و TFF
در این آموزش، برای فشردگی ما بلافاصله توابع است که معرفی منطق TensorFlow با تزئین tff.tf_computation
. با این حال، برای منطق پیچیده تر، این الگویی نیست که ما توصیه می کنیم. اشکال زدایی TensorFlow در حال حاضر می تواند یک چالش باشد، و اشکال زدایی TensorFlow پس از سریال سازی کامل و سپس واردات مجدد، لزوماً برخی از ابرداده ها را از دست می دهد و تعامل را محدود می کند، و اشکال زدایی را به چالش بیشتری تبدیل می کند.
بنابراین، ما به شدت توصیه نوشتن منطق TF مجموعه به عنوان توابع پایتون مستقل (این است که بدون tff.tf_computation
دکوراسیون). به این ترتیب منطق TensorFlow را می توان توسعه و آزمایش با استفاده TF بهترین شیوه و ابزار (مانند حالت مشتاق)، قبل از serialize کردن محاسبه برای TFF (به عنوان مثال، با استناد به tff.tf_computation
با یک تابع پایتون به عنوان آرگومان).
تعریف تابع ضرر
حالا که داده ها را داریم، بیایید یک تابع ضرر تعریف کنیم که می توانیم برای آموزش از آن استفاده کنیم. ابتدا، اجازه دهید نوع ورودی را به عنوان یک TFF با نام tuple تعریف کنیم. از آنجا که اندازه دسته داده ها ممکن است متفاوت باشد، ما مجموعه ای از ابعاد دسته ای به None
نشان می دهد که اندازه این بعد ناشناخته است.
BATCH_SPEC = collections.OrderedDict(
x=tf.TensorSpec(shape=[None, 784], dtype=tf.float32),
y=tf.TensorSpec(shape=[None], dtype=tf.int32))
BATCH_TYPE = tff.to_type(BATCH_SPEC)
str(BATCH_TYPE)
'<x=float32[?,784],y=int32[?]>'
ممکن است تعجب کنید که چرا ما نمی توانیم فقط یک نوع پایتون معمولی را تعریف کنیم. به یاد بیاورید بحث در بخش 1 ، که در آن ما توضیح داد که در حالی که ما می توانیم منطق محاسبات TFF با استفاده از پایتون، تحت محاسبات هود TFF بیان می پایتون است. نماد BATCH_TYPE
تعریف بالا نمایانگر یک نوع مشخصات TFF انتزاعی. این مهم است که به تشخیص این نوع TFF انتزاعی از بتن انواع نمایندگی پایتون، به عنوان مثال، ظروف مانند dict
یا collections.namedtuple
که ممکن است مورد استفاده قرار گیرد برای نشان دادن نوع TFF در بدن از یک تابع پایتون. بر خلاف پایتون، TFF یک تک انتزاعی نوع سازنده tff.StructType
برای تاپل مانند ظروف، با عناصر است که می تواند به صورت جداگانه به نام یا نام رها کنید. این نوع همچنین برای مدلسازی پارامترهای رسمی محاسبات استفاده میشود، زیرا محاسبات TFF میتوانند به طور رسمی فقط یک پارامتر و یک نتیجه را اعلام کنند - به زودی نمونههایی از این را خواهید دید.
اکنون بیایید نوع TFF پارامترهای مدل به عنوان یک تاپل TFF به نام وزن و تعصب تعریف، دوباره.
MODEL_SPEC = collections.OrderedDict(
weights=tf.TensorSpec(shape=[784, 10], dtype=tf.float32),
bias=tf.TensorSpec(shape=[10], dtype=tf.float32))
MODEL_TYPE = tff.to_type(MODEL_SPEC)
print(MODEL_TYPE)
<weights=float32[784,10],bias=float32[10]>
با وجود آن تعاریف، اکنون میتوانیم ضرر را برای یک مدل معین در یک دسته تعریف کنیم. توجه داشته باشید که استفاده از @tf.function
دکوراتور داخل @tff.tf_computation
دکوراتور. این اجازه می دهد تا ما را به ارسال TF با استفاده از پایتون مانند معناشناسی حتی اگر در داخل یک بودند tf.Graph
زمینه ایجاد شده توسط tff.tf_computation
دکوراتور.
# NOTE: `forward_pass` is defined separately from `batch_loss` so that it can
# be later called from within another tf.function. Necessary because a
# @tf.function decorated method cannot invoke a @tff.tf_computation.
@tf.function
def forward_pass(model, batch):
predicted_y = tf.nn.softmax(
tf.matmul(batch['x'], model['weights']) + model['bias'])
return -tf.reduce_mean(
tf.reduce_sum(
tf.one_hot(batch['y'], 10) * tf.math.log(predicted_y), axis=[1]))
@tff.tf_computation(MODEL_TYPE, BATCH_TYPE)
def batch_loss(model, batch):
return forward_pass(model, batch)
همانطور که انتظار میرفت، محاسبات batch_loss
بازده float32
از دست دادن با توجه به مدل و یک دسته ای داده است. توجه داشته باشید که MODEL_TYPE
و BATCH_TYPE
اند با هم به یک تایی 2 از پارامترهای رسمی فشرده شده است. شما می توانید نوع تشخیص batch_loss
عنوان (<MODEL_TYPE,BATCH_TYPE> -> float32)
.
str(batch_loss.type_signature)
'(<model=<weights=float32[784,10],bias=float32[10]>,batch=<x=float32[?,784],y=int32[?]>> -> float32)'
به عنوان یک بررسی عقلانی، بیایید یک مدل اولیه پر از صفر بسازیم و تلفات را در دسته ای از داده هایی که در بالا مشاهده کردیم محاسبه کنیم.
initial_model = collections.OrderedDict(
weights=np.zeros([784, 10], dtype=np.float32),
bias=np.zeros([10], dtype=np.float32))
sample_batch = federated_train_data[5][-1]
batch_loss(initial_model, sample_batch)
2.3025851
توجه داشته باشید که ما محاسبه TFF با مدل اولیه تعریف شده به عنوان یک خوراک dict
، حتی اگر بدن از تابع پایتون است که تعریف آن را مصرف پارامترهای مدل به عنوان model['weight']
و model['bias']
. استدلال از پاسخ به batch_loss
به سادگی به بدن از آن تابع تصویب نشده است.
چه اتفاقی می افتد هنگامی که ما فراخوان batch_loss
؟ بدن پایتون از batch_loss
قبلا ترسیم شده و مرتب در سلول بالا که در آن تعریف شده بود. TFF به عنوان تماس گیرنده را به عمل batch_loss
در زمان محاسبه تعریف، و به عنوان هدف نیایشی را در زمان batch_loss
استناد شده است. در هر دو نقش، TFF به عنوان پل بین سیستم نوع انتزاعی TFF و انواع نمایش پایتون عمل می کند. در زمان نیایش، TFF انواع ظرف پایتون استاندارد ترین (قبول dict
، list
، tuple
، collections.namedtuple
، و غیره) به عنوان نمایندگی بتن از تاپل TFF انتزاعی. همچنین، اگرچه همانطور که در بالا ذکر شد، محاسبات TFF به طور رسمی فقط یک پارامتر را میپذیرند، میتوانید از نحو فراخوانی آشنای پایتون با آرگومانهای موقعیتی و/یا کلمه کلیدی در مواردی که نوع پارامتر چندگانه است استفاده کنید - همانطور که انتظار میرود کار میکند.
نزول گرادیان در یک دسته
حال، بیایید محاسباتی را تعریف کنیم که از این تابع تلفات برای انجام یک مرحله از شیب نزول استفاده می کند. توجه داشته باشید که چگونه در این تابع تعریف، ما با استفاده از batch_loss
به عنوان یک مؤلفه. شما می توانید محاسبات ساخته شده با استناد tff.tf_computation
داخل بدن محاسبات دیگر، هر چند به طور معمول این است که لازم نیست - همانطور که در بالا اشاره شد، به دلیل ترتیب از دست برخی از اطلاعات اشکال زدایی، آن است که اغلب ترجیح داده شده برای محاسبات پیچیده تر برای نوشتن و تست همه TensorFlow بدون tff.tf_computation
دکوراتور.
@tff.tf_computation(MODEL_TYPE, BATCH_TYPE, tf.float32)
def batch_train(initial_model, batch, learning_rate):
# Define a group of model variables and set them to `initial_model`. Must
# be defined outside the @tf.function.
model_vars = collections.OrderedDict([
(name, tf.Variable(name=name, initial_value=value))
for name, value in initial_model.items()
])
optimizer = tf.keras.optimizers.SGD(learning_rate)
@tf.function
def _train_on_batch(model_vars, batch):
# Perform one step of gradient descent using loss from `batch_loss`.
with tf.GradientTape() as tape:
loss = forward_pass(model_vars, batch)
grads = tape.gradient(loss, model_vars)
optimizer.apply_gradients(
zip(tf.nest.flatten(grads), tf.nest.flatten(model_vars)))
return model_vars
return _train_on_batch(model_vars, batch)
str(batch_train.type_signature)
'(<initial_model=<weights=float32[784,10],bias=float32[10]>,batch=<x=float32[?,784],y=int32[?]>,learning_rate=float32> -> <weights=float32[784,10],bias=float32[10]>)'
هنگامی که شما یک تابع پایتون با تزئین استناد tff.tf_computation
در داخل بدن از جمله تابع دیگر، منطق محاسبه TFF داخلی تعبیه شده است (در اصل، به صورت inline) در منطق یک خارجی. همانطور که در بالا اشاره شد، اگر شما در حال نوشتن هر دو محاسبات، به احتمال زیاد ترجیح را به تابع داخلی (است batch_loss
در این مورد) پایتون طور منظم و یا tf.function
به جای یک tff.tf_computation
. با این حال، در اینجا ما نشان می دهد که خواستار یکی tff.tf_computation
داخل یکی دیگر از اساسا در حد انتظار است این ممکن است لازم باشد اگر، برای مثال، شما کد پایتون تعریف ندارد batch_loss
، اما تنها نمایندگی TFF سریال آن است.
حالا بیایید این تابع را چند بار در مدل اولیه اعمال کنیم تا ببینیم ضرر کاهش می یابد یا خیر.
model = initial_model
losses = []
for _ in range(5):
model = batch_train(model, sample_batch, 0.1)
losses.append(batch_loss(model, sample_batch))
losses
[0.19690023, 0.13176313, 0.10113225, 0.08273812, 0.070301384]
نزول گرادیان در دنباله ای از داده های محلی
حال حاضر، از batch_train
به کار به نظر می رسد، اجازه دهید ارسال یک تابع آموزش مشابه local_train
که مصرف کل دنباله ای از تمام دسته از کاربران به جای فقط یک دسته واحد. محاسبه جدید نیاز به در حال حاضر مصرف tff.SequenceType(BATCH_TYPE)
به جای BATCH_TYPE
.
LOCAL_DATA_TYPE = tff.SequenceType(BATCH_TYPE)
@tff.federated_computation(MODEL_TYPE, tf.float32, LOCAL_DATA_TYPE)
def local_train(initial_model, learning_rate, all_batches):
@tff.tf_computation(LOCAL_DATA_TYPE, tf.float32)
def _insert_learning_rate_to_sequence(dataset, learning_rate):
return dataset.map(lambda x: (x, learning_rate))
batches_with_learning_rate = _insert_learning_rate_to_sequence(all_batches, learning_rate)
# Mapping function to apply to each batch.
@tff.federated_computation(MODEL_TYPE, batches_with_learning_rate.type_signature.element)
def batch_fn(model, batch_with_lr):
batch, lr = batch_with_lr
return batch_train(model, batch, lr)
return tff.sequence_reduce(batches_with_learning_rate, initial_model, batch_fn)
str(local_train.type_signature)
'(<initial_model=<weights=float32[784,10],bias=float32[10]>,learning_rate=float32,all_batches=<x=float32[?,784],y=int32[?]>*> -> <weights=float32[784,10],bias=float32[10]>)'
جزئیات کمی در این بخش کوتاه کد وجود دارد، اجازه دهید آنها را یکی یکی مرور کنیم.
نخست، در حالی که ما می توانستیم این منطق به طور کامل در TensorFlow، با تکیه بر اجرا tf.data.Dataset.reduce
برای پردازش دنباله به طور مشابه به چگونه ما آن را انجام داده ام که قبلا انجام شده این زمان برای بیان منطق به زبان چسب ترجیح دادم ، به عنوان یک tff.federated_computation
. ما در بر اپراتور فدرال استفاده می شود tff.sequence_reduce
به انجام کاهش است.
اپراتور tff.sequence_reduce
است به طور مشابه به استفاده tf.data.Dataset.reduce
. شما می توانید از آن به عنوان اصل همان فکر می کنم tf.data.Dataset.reduce
، اما برای استفاده در داخل محاسبات فدرال، که به عنوان شما ممکن است به یاد داشته باشید، می تواند شامل کد TensorFlow است. این یک اپراتور قالب با پارامتر رسمی 3 تایی که شامل دنباله ای از است T
عناصر -typed، حالت اولیه از کاهش (ما آن را به عنوان صفر مراجعه انتزاعی) از برخی از نوع U
، و اپراتور کاهش تایپ (<U,T> -> U)
که در اثر تغییر دولت از کاهش پردازش یک عنصر واحد. نتیجه حالت نهایی کاهش است، پس از پردازش تمام عناصر به ترتیب متوالی. در مثال ما، وضعیت کاهش، مدلی است که بر روی پیشوندی از داده ها آموزش داده شده است، و عناصر، دسته های داده هستند.
دوم، توجه داشته باشید که ما دوباره یک محاسبه (استفاده batch_train
) به عنوان یک جزء در دیگر ( local_train
)، اما نه به طور مستقیم. ما نمی توانیم از آن به عنوان یک عملگر کاهش استفاده کنیم زیرا یک پارامتر اضافی را می گیرد - نرخ یادگیری. برای حل این مشکل، جاسازی شده محاسبه فدرال تعریف می کنیم batch_fn
که متصل به local_train
را پارامتر learning_rate
در بدن آن است. تا زمانی که محاسبات فرزند در خارج از بدنه والد خود احضار نشده باشد، برای محاسبات فرزند که به این شکل تعریف شده است مجاز است که یک پارامتر رسمی از والد خود را ثبت کند. شما می توانید از این الگو به عنوان یک معادل فکر می کنم functools.partial
زبان Python.
مفهوم عملی گرفتن learning_rate
این راه این است، البته، که همان ارزش نرخ یادگیری در تمام دسته استفاده می شود.
حال، اجازه دهید تابع آموزش محلی تعریف جدید در کل دنباله ای از داده ها را از همان کاربر که دسته ای نمونه (رقمی کمک سعی کنید 5
).
locally_trained_model = local_train(initial_model, 0.1, federated_train_data[5])
کار کرد؟ برای پاسخ به این سوال باید ارزیابی را اجرا کنیم.
ارزیابی محلی
در اینجا یک راه برای پیادهسازی ارزیابی محلی با جمع کردن تلفات در تمام دستههای داده وجود دارد (میتوانستیم میانگین را به خوبی محاسبه کنیم؛ آن را به عنوان تمرینی برای خواننده میگذاریم).
@tff.federated_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def local_eval(model, all_batches):
@tff.tf_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def _insert_model_to_sequence(model, dataset):
return dataset.map(lambda x: (model, x))
model_plus_data = _insert_model_to_sequence(model, all_batches)
@tff.tf_computation(tf.float32, batch_loss.type_signature.result)
def tff_add(accumulator, arg):
return accumulator + arg
return tff.sequence_reduce(
tff.sequence_map(
batch_loss,
model_plus_data), 0., tff_add)
str(local_eval.type_signature)
'(<model=<weights=float32[784,10],bias=float32[10]>,all_batches=<x=float32[?,784],y=int32[?]>*> -> float32)'
باز هم، چند عنصر جدید وجود دارد که توسط این کد نشان داده شده است، اجازه دهید آنها را یکی یکی مرور کنیم.
اول، ما دو اپراتور فدرال جدید برای پردازش توالی استفاده کرده اند: tff.sequence_map
که طول می کشد تابع نگاشت T->U
و یک دنباله از T
، و از خود ساطع میکند دنباله ای از U
دست آمده با استفاده از تابع نگاشت نقطه به نقطه، و tff.sequence_sum
که فقط تمام عناصر را اضافه می کند. در اینجا، ما هر دسته داده را به یک مقدار ضرر نگاشت می کنیم، و سپس مقادیر تلفات حاصل را برای محاسبه ضرر کل اضافه می کنیم.
توجه داشته باشید که ما نمی توانستیم دوباره مورد استفاده قرار tff.sequence_reduce
، اما این امر می تواند بهترین انتخاب است - روند کاهش است، تعریف، پی در پی، در حالی که نقشه برداری و جمع می تواند به صورت موازی محاسبه می شود. هنگامی که انتخابی به شما داده می شود، بهتر است از اپراتورهایی استفاده کنید که انتخاب های پیاده سازی را محدود نمی کنند، به طوری که وقتی محاسبات TFF ما در آینده برای استقرار در یک محیط خاص کامپایل می شود، بتوان از تمام فرصت های بالقوه برای سریع تر استفاده کامل کرد. ، مقیاس پذیرتر، اجرای با منابع کارآمدتر.
دوم، توجه داشته باشید که فقط به عنوان در local_train
، تابع جزء ما نیاز داریم ( batch_loss
) پارامترهای بیش از آنچه اپراتور فدرال طول می کشد ( tff.sequence_map
) انتظار، بنابراین ما دوباره یک جزئی، این بار درون خطی تعریف به طور مستقیم با پیچیدن یک lambda
به عنوان یک tff.federated_computation
. با استفاده از دستگاه فوم پیچ دور درون خطی با یک تابع به عنوان آرگومان روش توصیه شده برای استفاده است tff.tf_computation
برای قراردادن TensorFlow منطق در TFF.
حالا بیایید ببینیم که آیا آموزش ما جواب داده است یا خیر.
print('initial_model loss =', local_eval(initial_model,
federated_train_data[5]))
print('locally_trained_model loss =',
local_eval(locally_trained_model, federated_train_data[5]))
initial_model loss = 23.025854 locally_trained_model loss = 0.43484688
در واقع ضرر کاهش یافت. اما اگر آن را بر روی داده های کاربر دیگری ارزیابی کنیم چه اتفاقی می افتد؟
print('initial_model loss =', local_eval(initial_model,
federated_train_data[0]))
print('locally_trained_model loss =',
local_eval(locally_trained_model, federated_train_data[0]))
initial_model loss = 23.025854 locally_trained_model loss = 74.50075
همانطور که انتظار می رفت اوضاع بدتر شد. مدل به رسمیت شناختن آموزش دیده بود 5
و هرگز دیده می شود یک 0
. این سؤال را به همراه دارد - آموزش محلی چگونه بر کیفیت مدل از دیدگاه جهانی تأثیر گذاشته است؟
ارزیابی فدرال
این نقطه ای از سفر ماست که در نهایت به انواع فدرال و محاسبات فدرال بازمی گردیم - موضوعی که با آن شروع کردیم. در اینجا یک جفت تعاریف انواع TFF برای مدلی که از سرور سرچشمه میگیرد و دادههایی که روی کلاینتها باقی میمانند، آورده شده است.
SERVER_MODEL_TYPE = tff.type_at_server(MODEL_TYPE)
CLIENT_DATA_TYPE = tff.type_at_clients(LOCAL_DATA_TYPE)
با تمام تعاریفی که تاکنون معرفی شده است، بیان ارزیابی فدرال در TFF یک خطی است - ما مدل را بین مشتریان توزیع می کنیم، به هر مشتری اجازه می دهیم ارزیابی محلی را در بخش محلی داده خود فراخوانی کند و سپس میانگین ضرر را محاسبه کند. در اینجا یک راه برای نوشتن این است.
@tff.federated_computation(SERVER_MODEL_TYPE, CLIENT_DATA_TYPE)
def federated_eval(model, data):
return tff.federated_mean(
tff.federated_map(local_eval, [tff.federated_broadcast(model), data]))
ما در حال حاضر نمونه هایی از دیده tff.federated_mean
و tff.federated_map
در حالات ساده تر، و در سطح بصری، کار می کنند به عنوان انتظار می رود، اما بیشتر در این بخش از کد از ملاقات چشم وجود دارد، بنابراین اجازه دهید بیش از آن را به دقت است.
اول، استراحت اجازه دهید پایین اجازه هر مشتری فراخوان ارزیابی محلی در بخش محلی خود را از بخش داده. همانطور که شما ممکن است از بخش های پیشین به یاد، local_eval
دارای امضای نوع فرم (<MODEL_TYPE, LOCAL_DATA_TYPE> -> float32)
.
اپراتور فدرال tff.federated_map
یک قالب است که به عنوان یک پارامتر می پذیرد تایی 2 که متشکل از تابع نگاشت از برخی از انواع است T->U
و یک مقدار فدرال نوع {T}@CLIENTS
(یعنی با ترکیبات عضو همان نوع به عنوان پارامتر تابع نقشه برداری)، و بازده یک نتیجه از نوع {U}@CLIENTS
.
از آنجا که ما تغذیه هستید local_eval
به عنوان یک تابع نگاشت به درخواست بر اساس هر مشتری، آرگومان دوم باید از یک نوع فدرال شود {<MODEL_TYPE, LOCAL_DATA_TYPE>}@CLIENTS
در نامگذاری از بخش های پیشین، یعنی، آن را باید یک تاپل فدرال باشید هر مشتری باید یک مجموعه کامل از استدلال برای نگه local_eval
به عنوان یک عضو consituent. در عوض، ما تغذیه آن 2-عنصر پایتون list
. اینجا چه خبره؟
در واقع، این یک نمونه از بازیگران نوع ضمنی در TFF، شبیه به کست نوع ضمنی شما ممکن است در جاهای دیگر مواجه می شوند، به عنوان مثال، زمانی که شما تغذیه یک int
به یک تابع که قبول float
. در این مرحله از ریخته گری ضمنی به ندرت استفاده می شود، اما ما قصد داریم آن را در TFF به عنوان راهی برای به حداقل رساندن صفحه دیگ بخار فراگیرتر کنیم.
بازیگران ضمنی که در این مورد اعمال هم ارزی بین تاپل فدرال از فرم است {<X,Y>}@Z
و چندتایی از فدرال ارزش <{X}@Z,{Y}@Z>
. در حالی که به طور رسمی، این دو امضا نوع متفاوت هستند، نگاه کردن به آن از دیدگاه برنامه نویسان است، هر دستگاه در Z
دارای دو واحد از داده ها X
و Y
. چه اتفاقی می افتد در اینجا این است بر خلاف zip
در پایتون، و در واقع، ما ارائه می دهیم یک اپراتور tff.federated_zip
که اجازه می دهد تا شما را به انجام تبدیل چنین صراحت. وقتی tff.federated_map
یک تاپل به عنوان یک آرگومان دوم مواجه می شود، آن را به سادگی فراخوانی tff.federated_zip
برای شما.
با توجه به موارد فوق، شما در حال حاضر باید قادر به تشخیص بیان شود tff.federated_broadcast(model)
به عنوان نمایندگی یک مقدار از نوع TFF {MODEL_TYPE}@CLIENTS
، و data
به عنوان یک مقدار از نوع TFF {LOCAL_DATA_TYPE}@CLIENTS
(و یا به سادگی CLIENT_DATA_TYPE
) ، دو شدن با هم از طریق یک اشاره تلویحی فیلتر tff.federated_zip
به شکل آرگومان دوم به tff.federated_map
.
اپراتور tff.federated_broadcast
، به عنوان شما می خواهم انتظار، به سادگی انتقال داده از سرور به مشتریان می باشد.
حالا بیایید ببینیم آموزش محلی ما چگونه بر میانگین ضرر در سیستم تأثیر گذاشته است.
print('initial_model loss =', federated_eval(initial_model,
federated_train_data))
print('locally_trained_model loss =',
federated_eval(locally_trained_model, federated_train_data))
initial_model loss = 23.025852 locally_trained_model loss = 54.432625
در واقع، همانطور که انتظار می رفت، ضرر افزایش یافته است. به منظور بهبود مدل برای همه کاربران، باید در مورد داده های همه آموزش ببینیم.
آموزش فدرال
سادهترین راه برای پیادهسازی آموزش فدرال، آموزش محلی و سپس میانگینگیری مدلها است. این از همان بلوکهای ساختمانی و الگوهایی استفاده میکند که قبلاً در مورد آن صحبت کردیم، همانطور که در زیر میبینید.
SERVER_FLOAT_TYPE = tff.type_at_server(tf.float32)
@tff.federated_computation(SERVER_MODEL_TYPE, SERVER_FLOAT_TYPE,
CLIENT_DATA_TYPE)
def federated_train(model, learning_rate, data):
return tff.federated_mean(
tff.federated_map(local_train, [
tff.federated_broadcast(model),
tff.federated_broadcast(learning_rate), data
]))
توجه داشته باشید که در اجرای کامل شامل از به طور متوسط فدرال ارائه شده توسط tff.learning
، به جای به طور متوسط در مدل، ما به طور متوسط دلتا مدل ترجیح می دهند، برای تعدادی از دلایل، به عنوان مثال، توانایی کلیپ هنجارهای به روز رسانی، برای فشرده سازی، و غیره .
بیایید ببینیم که آیا تمرین با اجرای چند دور تمرین و مقایسه میانگین ضرر قبل و بعد کار می کند یا خیر.
model = initial_model
learning_rate = 0.1
for round_num in range(5):
model = federated_train(model, learning_rate, federated_train_data)
learning_rate = learning_rate * 0.9
loss = federated_eval(model, federated_train_data)
print('round {}, loss={}'.format(round_num, loss))
round 0, loss=21.60552215576172 round 1, loss=20.365678787231445 round 2, loss=19.27480125427246 round 3, loss=18.311111450195312 round 4, loss=17.45725440979004
برای کامل بودن، بیایید اکنون روی داده های آزمایشی نیز اجرا کنیم تا تأیید کنیم که مدل ما به خوبی تعمیم می یابد.
print('initial_model test loss =',
federated_eval(initial_model, federated_test_data))
print('trained_model test loss =', federated_eval(model, federated_test_data))
initial_model test loss = 22.795593 trained_model test loss = 17.278767
این آموزش ما را به پایان می رساند.
البته، مثال سادهشده ما تعدادی از کارهایی را که باید در یک سناریوی واقعیتر انجام دهید منعکس نمیکند - به عنوان مثال، ما معیارهایی غیر از ضرر را محاسبه نکردهایم. ما شما را تشویق به مطالعه اجرای از میانگین فدرال در tff.learning
به عنوان یک مثال کامل تر، و به عنوان یک راه برای نشان دادن برخی از شیوه های برنامه نویسی ما می خواهم برای تشویق.