কাস্টম ফেডারেটেড অ্যালগরিদম, পার্ট 2: ফেডারেটেড এভারেজিং বাস্তবায়ন

TensorFlow.org এ দেখুন Google Colab-এ চালান GitHub-এ উৎস দেখুন নোটবুক ডাউনলোড করুন

এই টিউটোরিয়ালটি প্রমান করে যে কিভাবে ব্যবহার ফেডারেট আলগোরিদিম কাস্টম ধরনের বাস্তবায়ন TFF একটি দুই অংশের সিরিজের দ্বিতীয় অংশ ফেডারেটেড কোর (এফসি) , যার জন্য একটি ভিত্তি হিসেবে কাজ করে ফেডারেটেড শিক্ষণ (এফএল) লেয়ার ( tff.learning ) .

আমরা প্রথম পড়তে উত্সাহিত এই সিরিজের প্রথম অংশ , যা মূল ধারণার কিছু এবং এখানে ব্যবহৃত প্রোগ্রামিং বিমূর্ত পরিচয় দিন।

সিরিজের এই দ্বিতীয় অংশটি ফেডারেটেড প্রশিক্ষণ এবং মূল্যায়ন অ্যালগরিদমের একটি সহজ সংস্করণ বাস্তবায়নের জন্য প্রথম অংশে প্রবর্তিত প্রক্রিয়াগুলি ব্যবহার করে।

আমরা পর্যালোচনা করার জন্য উত্সাহিত ইমেজ শ্রেণীবিন্যাস এবং টেক্সট প্রজন্ম TFF এর ফেডারেটেড শিক্ষণ API গুলি করার জন্য একটি উচ্চ স্তরের এবং আরো মৃদু পরিচয়ের জন্য টিউটোরিয়াল, হিসাবে তারা সহায়তা করবে ধারণা আমরা এখানে প্রেক্ষাপটে বর্ণনা করা।

আমরা শুরু করার আগে

আমরা শুরু করার আগে, আপনার পরিবেশ সঠিকভাবে সেটআপ করা হয়েছে তা নিশ্চিত করতে নিম্নলিখিত "হ্যালো ওয়ার্ল্ড" উদাহরণ চালানোর চেষ্টা করুন। এটা কাজ করে না, তাহলে পড়ুন দয়া ইনস্টলেশন নির্দেশাবলীর জন্য গাইড।

!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 উদাহরণ ব্যবহার করতে যাচ্ছে, কিন্তু যেহেতু এই একটি নিম্ন স্তরের টিউটোরিয়াল হিসাবে উদ্দীষ্ট, তখন আমরা Keras API ও বাইপাস করতে যাচ্ছি 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 কম্পিউটেশনে ফিড করি, যেমন, তালিকার একটি তালিকা হিসাবে - ব্যবহারকারীদের (অঙ্ক) জুড়ে বাইরের তালিকা, ভিতরেরগুলি ডেটার ব্যাচের উপর বিস্তৃত। প্রতিটি ক্লায়েন্টের ক্রম। হিসাবে গতানুগতিক হয়, আমরা নামে tensors একজোড়া হিসাবে একে ব্যাচ গঠন করবে x এবং y শীর্ষস্থানীয় ব্যাচ মাত্রা সঙ্গে প্রতিটি। এ সময় আমরা আপনাকে একটি 784-উপাদানের ভেক্টর মধ্যে প্রতিটি ইমেজ চেপ্টা এবং সেটিকে মধ্যে পিক্সেল rescale করব 0..1 পরিসীমা, যাতে আমরা ডেটা ধর্মান্তর সঙ্গে মডেল যুক্তিবিজ্ঞান বিশৃঙ্খল করতে হবে না।

NUM_EXAMPLES_PER_USER = 1000
BATCH_SIZE = 100


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


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

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

একটি দ্রুত বৈধতা পরীক্ষা এ আসুন চেহারা হিসাবে Y পঞ্চম ক্লায়েন্ট দ্বারা অবদান ডেটার গত ব্যাচে টেন্সর (এক অঙ্ক সংশ্লিষ্ট 5 )।

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

শুধু নিশ্চিত হওয়ার জন্য, আসুন সেই ব্যাচের শেষ উপাদানটির সাথে সম্পর্কিত চিত্রটিও দেখি।

from matplotlib import pyplot as plt

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

png

TensorFlow এবং TFF একত্রিত করার উপর

এই টিউটোরিয়ালে, সংহতি জন্য আমরা অবিলম্বে ফাংশন যে সঙ্গে TensorFlow যুক্তিবিজ্ঞান পরিচয় করিয়ে সাজাইয়া রাখা tff.tf_computation । যাইহোক, আরও জটিল যুক্তির জন্য, এটি আমরা সুপারিশ করি এমন প্যাটার্ন নয়। টেনসরফ্লোকে ডিবাগ করা ইতিমধ্যেই একটি চ্যালেঞ্জ হতে পারে, এবং টেনসরফ্লোকে সম্পূর্ণরূপে সিরিয়ালাইজ করার পরে ডিবাগ করা এবং তারপরে পুনরায় আমদানি করা অগত্যা কিছু মেটাডেটা হারায় এবং ইন্টারঅ্যাক্টিভিটি সীমিত করে, ডিবাগিংকে আরও বেশি চ্যালেঞ্জ করে।

অতএব, আমরা দৃঢ়ভাবে অনুষঙ্গহীন পাইথন ফাংশন যেমন জটিল মেমরি যুক্তিবিজ্ঞান লেখার সুপারিশ (যে ছাড়া হয়, tff.tf_computation প্রসাধন)। এই পদ্ধতি TensorFlow যুক্তিবিজ্ঞান উন্নত করা যেতে পারে এবং পরীক্ষিত মেমরি সর্বোত্তম কার্যাভ্যাস এবং সরঞ্জাম (উৎসুক মোড মত) ব্যবহার করে, TFF জন্য গণনার serializing (যেমন, আবাহন করার মাধ্যমে সামনে tff.tf_computation আর্গুমেন্ট হিসাবে একটি পাইথন ফাংশন)।

একটি ক্ষতি ফাংশন সংজ্ঞায়িত

এখন যেহেতু আমাদের কাছে ডেটা আছে, আসুন একটি ক্ষতি ফাংশন সংজ্ঞায়িত করি যা আমরা প্রশিক্ষণের জন্য ব্যবহার করতে পারি। প্রথমে, টিএফএফ নামের টিপল হিসাবে ইনপুটের ধরণটি সংজ্ঞায়িত করা যাক। যেহেতু তথ্য ব্যাচ আকার পরিবর্তিত হতে পারে, আমরা ব্যাচ মাত্রা নির্ধারণ করে 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 জন্য tuple মত উপাদান আছে যা স্বতন্ত্রভাবে নামে করা বা নামহীন বাইরে পাত্রে,। এই প্রকারটি গণনার আনুষ্ঠানিক প্যারামিটারের মডেল করতেও ব্যবহৃত হয়, কারণ TFF গণনা আনুষ্ঠানিকভাবে শুধুমাত্র একটি প্যারামিটার এবং একটি ফলাফল ঘোষণা করতে পারে - আপনি শীঘ্রই এর উদাহরণ দেখতে পাবেন।

আসুন এখন ওজনপক্ষপাত একটি TFF নামে tuple হিসাবে আবার, মডেল প্যারামিটার 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.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-tuple মধ্যে একসঙ্গে lumped হয়েছে; আপনি ধরণ চিনতে পারে 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 tuples এর কংক্রিট উপস্থাপনা যেমন, ইত্যাদি)। এছাড়াও, যদিও উপরে উল্লিখিত হিসাবে, TFF গণনা আনুষ্ঠানিকভাবে শুধুমাত্র একটি একক প্যারামিটার গ্রহণ করে, আপনি অবস্থানগত এবং/অথবা কীওয়ার্ড আর্গুমেন্টের সাথে পরিচিত পাইথন কল সিনট্যাক্স ব্যবহার করতে পারেন যেখানে প্যারামিটারের ধরন একটি টিপল হয় - এটি প্রত্যাশিত হিসাবে কাজ করে।

একটি একক ব্যাচে গ্রেডিয়েন্ট ডিসেন্ট

এখন, আসুন একটি গণনা সংজ্ঞায়িত করা যাক যা গ্রেডিয়েন্ট ডিসেন্টের একক ধাপ সম্পাদন করতে এই ক্ষতি ফাংশনটি ব্যবহার করে। নোট কিভাবে এই ফাংশন সংজ্ঞায়িত, আমরা ব্যবহার batch_loss একটি subcomponent হিসাবে। আপনি একটি গণনার সঙ্গে নির্মাণ ডাকা যাবে 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 গণনার যুক্তি এমবেড করা হয় বাইরের এক যুক্তিবিজ্ঞান মধ্যে (মূলত, inlined)। উপরোক্ত আলোচনা লক্ষনীয়, আপনি যদি উভয় কম্পিউটেশন লিখছেন এটি সম্ভবত ভেতরের ফাংশনটি (করা বাঞ্ছনীয় 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-tuple সঙ্গে একটি টেমপ্লেট অপারেটর T উপাদানের -typed, হ্রাসের প্রাথমিক অবস্থায় কিছু টাইপ এর (আমরা এটি abstractly শূন্য হিসাবে পড়ুন করব) U , এবং হ্রাস অপারেটর টাইপ (<U,T> -> U) যে গন্ধে পরিবর্তন একটি একক উপাদান প্রক্রিয়াকরণের দ্বারা হ্রাস রাষ্ট্র। একটি ক্রমিক ক্রমে সমস্ত উপাদান প্রক্রিয়াকরণের পরে, ফলাফল হ্রাসের চূড়ান্ত অবস্থা। আমাদের উদাহরণে, হ্রাসের অবস্থা হল ডেটার উপসর্গের উপর প্রশিক্ষিত মডেল, এবং উপাদানগুলি হল ডেটা ব্যাচ।

দ্বিতীয়ত, দয়া করে মনে রাখবেন আমরা আবার এক গণনার (ব্যবহার করেছেন batch_train অন্য (মধ্যে একটি উপাদান হিসাবে) local_train ), কিন্তু সরাসরি নয়। আমরা এটিকে রিডাকশন অপারেটর হিসাবে ব্যবহার করতে পারি না কারণ এটি একটি অতিরিক্ত প্যারামিটার লাগে - শেখার হার। এই সমাধান করার জন্য, আমরা একটি এমবেডেড ফেডারেট গণনার সংজ্ঞায়িত batch_fn যে শুশ্রূষা local_train এর প্যারামিটার learning_rate তার শরীর। যতক্ষণ না শিশু গণনা তার পিতামাতার মূল অংশের বাইরে আমন্ত্রিত না হয় ততক্ষণ পর্যন্ত তার পিতামাতার একটি আনুষ্ঠানিক প্যারামিটার ক্যাপচার করার জন্য এইভাবে সংজ্ঞায়িত একটি শিশু গণনার জন্য এটি অনুমোদিত। আপনি একটি সমতুল্য হিসাবে এই প্যাটার্ন মনে করতে পারেন functools.partial পাইথন হবে।

ক্যাপচার ব্যবহারিক ইঙ্গিত 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 ম্যাপিং ফাংশন pointwise প্রয়োগের দ্বারা প্রাপ্ত, এবং tff.sequence_sum যে শুধু সব উপাদান যোগ করে। এখানে, আমরা প্রতিটি ডেটা ব্যাচকে একটি ক্ষতির মানের সাথে ম্যাপ করি এবং তারপরে মোট ক্ষতি গণনা করতে ফলস্বরূপ ক্ষতির মান যুক্ত করি।

মনে রাখবেন আমরা পুনরায় ব্যবহার করতে পারত tff.sequence_reduce , কিন্তু এই ভাল পছন্দ হবে না -, হ্রাস প্রক্রিয়া, সংজ্ঞা, অনুক্রমিক দ্বারা যেহেতু ম্যাপিং এবং সমষ্টি সমান্তরাল নির্ণিত করা যেতে পারে। যখন একটি পছন্দ দেওয়া হয়, তখন এমন অপারেটরদের সাথে লেগে থাকা ভাল যেগুলি বাস্তবায়নের পছন্দগুলিকে বাধাগ্রস্ত করে না, যাতে ভবিষ্যতে যখন আমাদের TFF গণনা একটি নির্দিষ্ট পরিবেশে স্থাপন করার জন্য সংকলিত হয়, তখন কেউ দ্রুততার জন্য সমস্ত সম্ভাব্য সুযোগের পূর্ণ সদ্ব্যবহার করতে পারে। , আরও মাপযোগ্য, আরও সম্পদ-দক্ষ সম্পাদন।

দ্বিতীয়ত, নোট যে শুধু হিসেবে local_train , কম্পোনেন্ট ফাংশন আমরা প্রয়োজন ( batch_loss ) কি ফেডারেট অপারেটর চেয়ে বেশি প্যারামিটার নেয় ( tff.sequence_map ) প্রত্যাশা অনুযায়ী তাই আমরা আবার সরাসরি একটি মোড়কে একটি আংশিক এই সময় ইনলাইন সংজ্ঞায়িত lambda হিসেবে tff.federated_computation । চাদরে একটি আর্গুমেন্ট হিসাবে একটি ফাংশন ইনলাইন ব্যবহার ব্যবহার করা বাঞ্ছনীয় উপায় tff.tf_computation TFF করা এম্বেড TensorFlow যুক্তি।

এখন, দেখা যাক আমাদের প্রশিক্ষণ কাজ করে কিনা।

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-tuple যে কিছু ধরনের ম্যাপিং ফাংশন নিয়ে গঠিত একটি প্যারামিটার হিসাবে গ্রহণ করে একটি টেমপ্লেট T->U এবং ধরনের ফেডারেট মান {T}@CLIENTS (অর্থাত, সদস্য সংগঠকদের সঙ্গে ম্যাপিং ফাংশনের প্যারামিটার হিসাবে একই ধরনের), এবং আয় টাইপ ফলে {U}@CLIENTS

করছি খাওয়ানো যেহেতু local_eval প্রতি ক্লায়েন্ট ভিত্তিতে আবেদন করতে একটি ম্যাপিং ফাংশন হিসাবে, দ্বিতীয় যুক্তি ফেডারেট ধরনের হওয়া উচিত {<MODEL_TYPE, LOCAL_DATA_TYPE>}@CLIENTS পূর্ববর্তী বিভাগে নামকরণের মধ্যে, অর্থাত্, এটি করা উচিত একটি ফেডারেটেড টিপল হতে প্রতিটি ক্লায়েন্টের জন্য আর্গুমেন্ট একটি সম্পূর্ণ সেট রাখা উচিত local_eval সদস্য consituent হিসাবে। এর পরিবর্তে, আমরা এটি একটি 2-উপাদান পাইথন খাওয়ানোর করছি list । এখানে কি হচ্ছে?

প্রকৃতপক্ষে, এই TFF, অন্তর্নিহিত টাইপ কাস্ট আপনাকে অন্য কোথাও সম্মুখীন হতে পারে, উদাঃ অনুরূপ একটি অন্তর্নিহিত টাইপ কাস্ট একটি উদাহরণ যখন আপনি একটি ফিড নেই, int একটি ফাংশন যে একটি গ্রহণ করার float । এই মুহুর্তে অন্তর্নিহিত কাস্টিং খুব কমই ব্যবহার করা হয়, তবে আমরা বয়লারপ্লেটকে ছোট করার উপায় হিসাবে TFF-এ এটিকে আরও ব্যাপক করার পরিকল্পনা করছি।

অন্তর্নিহিত ঢালাই যে এই ক্ষেত্রে প্রয়োগ করা হচ্ছে ফর্মের ফেডারেট tuples মধ্যে সমানতা হয় {<X,Y>}@Z , এবং ফেডারেট মূল্যবোধের tuples <{X}@Z,{Y}@Z> । যদিও আনুষ্ঠানিকভাবে এই দুটি বিভিন্ন ধরনের স্বাক্ষর হয়, প্রোগ্রামারদের দৃষ্টিকোণ থেকে এটা দিকে তাকিয়ে আছে, প্রতিটি ডিভাইস Z তথ্য দুটি ইউনিট ঝুলিতে X এবং Y । কি এখানে ঘটবে অসদৃশ নয় zip পাইথন, এবং প্রকৃতপক্ষে, আমরা একটি অপারেটর অফার tff.federated_zip যা এই ধরনের ধর্মান্তর সুনির্দিষ্টভাবে সম্পাদন করতে পারেন। যখন tff.federated_map একটি দ্বিতীয় আর্গুমেন্ট হিসাবে একটি tuple encounters, এটা শুধু ডাকে 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 আরো একটি সম্পূর্ণ উদাহরণ হিসাবে, একটা পথ কোডিং চর্চা আমরা উৎসাহিত করতে চাই কিছু প্রকট হয়।