Tarihi kaydet! Google I / O 18-20 Mayıs'ta geri dönüyor Şimdi kaydolun
Bu sayfa, Cloud Translation API ile çevrilmiştir.
Switch to English

Özel Birleşik Algoritmalar, Bölüm 2: Birleşik Ortalama Alma

TensorFlow.org'da görüntüleyin Google Colab'de çalıştırın Kaynağı GitHub'da görüntüleyin Defteri indirin

Bu öğretici, Federe Öğrenme (FL) katmanı ( tff.learning ) için bir temel görevi gören Federated Core (FC) kullanılarak özel federe algoritma türlerinin TFF'de nasıl uygulanacağını gösteren iki bölümlük bir dizinin ikinci bölümüdür. .

Burada kullanılan bazı temel kavramları ve programlama soyutlamalarını tanıtan bu serinin ilk bölümünü okumanızı öneririz.

Serinin bu ikinci bölümü, birleşik eğitim ve değerlendirme algoritmalarının basit bir versiyonunu uygulamak için birinci bölümde tanıtılan mekanizmaları kullanır.

Burada anlattığımız kavramları bağlama oturtmanıza yardımcı olacakları için, TFF'nin Federe Öğrenme API'lerine daha üst düzey ve daha nazik bir giriş için görüntü sınıflandırmasını ve metin oluşturma eğitimlerini incelemenizi öneririz.

Başlamadan önce

Başlamadan önce, ortamınızın doğru kurulduğundan emin olmak için aşağıdaki "Merhaba Dünya" örneğini çalıştırmayı deneyin. Çalışmazsa, talimatlar için lütfen Kurulum kılavuzuna bakın.

!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio

import nest_asyncio
nest_asyncio.apply()
import collections

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

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

hello_world()
'Hello, World!'

Birleşik Ortalama Alma

Görüntü Sınıflandırma için Federe Öğrenmede olduğu gibi, MNIST örneğini kullanacağız, ancak bu düşük seviyeli bir eğitim olarak tasarlandığından, tff.simulation API ve tff.simulation , ham model kodu yazacağız ve bir sıfırdan federe veri seti.

Birleşik veri kümelerinin hazırlanması

Bir gösteri uğruna, 10 kullanıcıdan gelen verilere sahip olduğumuz bir senaryoyu simüle edeceğiz ve her bir kullanıcı, farklı bir rakamı nasıl tanıyacağına dair bilgilerle katkıda bulunur. Bu, olabildiğince geçersiz .

İlk önce standart MNIST verilerini yükleyelim:

mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step
[(x.dtype, x.shape) for x in mnist_train]
[(dtype('uint8'), (60000, 28, 28)), (dtype('uint8'), (60000,))]

Veriler, her ikisi de tek tek örneklerin üzerinden geçen birinci boyutla, biri görüntülerle diğeri rakam etiketleriyle Numpy dizileri olarak gelir. Birleştirilmiş dizileri TFF hesaplamalarına nasıl beslediğimizle uyumlu bir şekilde biçimlendiren bir yardımcı işlev yazalım, yani, bir liste listesi olarak - dış liste kullanıcılar (rakamlar), içteki liste veri yığınları arasında değişir. her müşterinin sırası. Alışılmış olduğu gibi, her partiyi, her biri önde gelen parti boyutuna sahip x ve y adlı bir tensör çifti olarak yapılandıracağız. Bu arada, her bir görüntüyü 784 elemanlı bir vektörde düzleştireceğiz ve içindeki pikselleri 0..1 aralığında yeniden ölçeklendireceğiz, böylece model mantığını veri dönüştürmeleriyle karıştırmak zorunda kalmayacağız.

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

Hızlı bir akıl sağlığı kontrolü olarak, beşinci müşterinin (rakam 5 karşılık gelen) katkıda bulunduğu son veri grubundaki Y tensörüne bakalım.

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)

Emin olmak için, o partinin son elemanına karşılık gelen resme de bakalım.

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 ve TFF'nin birleştirilmesi hakkında

Bu öğreticide, kompaktlık için TensorFlow mantığını tff.tf_computation ile tanıtan işlevleri hemen dekore tff.tf_computation . Ancak, daha karmaşık mantık için önerdiğimiz model bu değildir. Hata ayıklama TensorFlow zaten bir zorluk olabilir ve TensorFlow tamamen serileştirildikten ve sonra yeniden içe aktarıldıktan sonra hata ayıklamak, zorunlu olarak bazı meta verileri kaybeder ve etkileşimi sınırlayarak hata ayıklamayı daha da zor hale getirir.

Bu nedenle, karmaşık TF mantığını bağımsız Python işlevleri olarak (yani tff.tf_computation dekorasyonu olmadan) tff.tf_computation şiddetle tavsiye ederiz . Bu şekilde TensorFlow mantığı, TFF için hesaplamayı serileştirmeden önce (örneğin, argüman olarak bir Python işlevi ile tff.tf_computation çağırarak) TF en iyi uygulamaları ve araçları (istekli mod gibi) kullanılarak geliştirilebilir ve test edilebilir.

Bir kayıp fonksiyonunun tanımlanması

Artık verilere sahip olduğumuza göre, eğitim için kullanabileceğimiz bir kayıp fonksiyonu tanımlayalım. İlk olarak, girdi tipini tuple adlı bir TFF olarak tanımlayalım. Veri gruplarının boyutu değişebileceğinden, bu boyutun boyutunun bilinmediğini belirtmek için toplu iş boyutunu None olarak ayarladık.

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[?]>'

Neden sıradan bir Python türü tanımlayamadığımızı merak ediyor olabilirsiniz. Tartışmaya hatırlayın part 1 biz kaput TFF hesaplamaları altında Python kullanarak TFF hesaplamaların mantığı, ifade edebilir ederken Python olmadıklarını açıkladı. Yukarıda tanımlanan BATCH_TYPE sembolü, soyut bir TFF tipi spesifikasyonunu temsil eder. Bu soyut TFF türünü, bir Python işlevinin gövdesindeki TFF türünü temsil etmek için kullanılabilecek dict veya collections.namedtuple gibi kaplar gibi somut Python temsil türlerinden ayırmak önemlidir. Python'dan farklı olarak, TFF, tek tek isimlendirilebilen veya isimsiz bırakılabilen elemanlarla tuple benzeri kaplar için tek bir soyut tip yapıcı tff.StructType sahiptir. Bu tür, hesaplamaların biçimsel parametrelerini modellemek için de kullanılır, çünkü TFF hesaplamaları resmi olarak yalnızca bir parametre ve bir sonuç bildirebilir - bunun örneklerini kısa bir süre sonra göreceksiniz.

Şimdi yine TFF adlı bir ağırlık ve önyargı dizisi olarak TFF tipi model parametrelerini tanımlayalım.

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

Bu tanımlamalar yürürlükte olduğunda, artık belirli bir model için kaybı tek bir parti üzerinden tanımlayabiliriz. @tf.function decorator içinde @tff.tf_computation dekoratörünün kullanımına dikkat edin. Bu, tff.tf_computation dekoratörü tarafından oluşturulan bir tf.Graph bağlamının içinde olmasına rağmen Python gibi semantik kullanarak TF 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)

Beklendiği gibi, hesaplama batch_loss , model ve tek bir veri grubu verildiğinde float32 kaybı döndürür. MODEL_TYPE ve BATCH_TYPE nasıl MODEL_TYPE biçimsel parametreler halinde bir araya getirildiğine dikkat edin; batch_loss türünü (<MODEL_TYPE,BATCH_TYPE> -> float32) .

str(batch_loss.type_signature)
'(<<weights=float32[784,10],bias=float32[10]>,<x=float32[?,784],y=int32[?]>> -> float32)'

Bir akıl sağlığı kontrolü olarak, sıfırlarla dolu bir başlangıç ​​modeli oluşturalım ve yukarıda görselleştirdiğimiz veri yığını üzerinden kaybı hesaplayalım.

initial_model = collections.OrderedDict(
    weights=np.zeros([784, 10], dtype=np.float32),
    bias=np.zeros([10], dtype=np.float32))

sample_batch = federated_train_data[5][-1]

batch_loss(initial_model, sample_batch)
2.3025854

TFF hesaplamasını, onu tanımlayan Python işlevinin gövdesi model['weight'] ve model['bias'] olarak model['weight'] parametrelerini tüketmesine rağmen, bir dict olarak tanımlanan ilk modelle beslediğimize dikkat edin. batch_loss çağrısının argümanları, o işlevin gövdesine basitçe aktarılmaz.

batch_loss çağırdığımızda ne olur? batch_loss Python gövdesi, tanımlandığı yukarıdaki hücrede zaten izlendi ve serileştirildi. TFF, hesaplama tanımlama zamanında batch_loss için batch_loss olarak ve batch_loss çağrıldığı anda çağrı hedefi olarak hareket eder. Her iki rolde de TFF, TFF'nin soyut tür sistemi ile Python temsil türleri arasında köprü görevi görür. Çağırma zamanında TFF, çoğu standart Python kapsayıcı türünü ( dict , list , tuple , collections.namedtuple , vb.) Soyut TFF demetlerinin somut temsilleri olarak kabul eder. Ayrıca, yukarıda belirtildiği gibi, TFF hesaplamaları resmi olarak yalnızca tek bir parametreyi kabul etse de, parametre türünün bir demet olması durumunda, bilinen Python çağrısı sözdizimini konumsal ve / veya anahtar kelime argümanlarıyla kullanabilirsiniz - beklendiği gibi çalışır.

Tek bir partide gradyan inişi

Şimdi, bu kayıp fonksiyonunu tek bir gradyan inişi gerçekleştirmek için kullanan bir hesaplama tanımlayalım. Bu işlevi tanımlarken batch_loss bir alt bileşen olarak kullandığımıza batch_loss . Başka bir hesaplamanın tff.tf_computation içinde tff.tf_computation ile tff.tf_computation bir hesaplamayı çağırabilirsiniz, ancak tipik olarak bu gerekli değildir - yukarıda belirtildiği gibi, serileştirme bazı hata ayıklama bilgilerini kaybettiğinden, genellikle daha karmaşık hesaplamaların tüm TensorFlow'u yazması ve test etmesi tercih edilir. tff.tf_computation dekoratörü olmadan.

@tff.tf_computation(MODEL_TYPE, BATCH_TYPE, tf.float32)
def batch_train(initial_model, batch, learning_rate):
  # Define a group of model variables and set them to `initial_model`. Must
  # be defined outside the @tf.function.
  model_vars = collections.OrderedDict([
      (name, tf.Variable(name=name, initial_value=value))
      for name, value in initial_model.items()
  ])
  optimizer = tf.keras.optimizers.SGD(learning_rate)

  @tf.function
  def _train_on_batch(model_vars, batch):
    # Perform one step of gradient descent using loss from `batch_loss`.
    with tf.GradientTape() as tape:
      loss = forward_pass(model_vars, batch)
    grads = tape.gradient(loss, model_vars)
    optimizer.apply_gradients(
        zip(tf.nest.flatten(grads), tf.nest.flatten(model_vars)))
    return model_vars

  return _train_on_batch(model_vars, batch)
str(batch_train.type_signature)
'(<<weights=float32[784,10],bias=float32[10]>,<x=float32[?,784],y=int32[?]>,float32> -> <weights=float32[784,10],bias=float32[10]>)'

Böyle başka bir fonksiyonun gövdesi içinde tff.tf_computation ile dekore edilmiş bir Python fonksiyonunu çağırdığınızda, iç TFF hesaplamasının mantığı dış fonksiyonun mantığına gömülür (esasen, satır içi). Yukarıda belirtildiği gibi her iki hesaplamaları yazıyorsanız eğer, iç fonksiyonunu (yapma olasılığı tercih edilir batch_loss bu durumda) normal bir Python veya tf.function yerine bir tff.tf_computation . Bununla birlikte, burada bir tff.tf_computation içinde tff.tf_computation temelde beklendiği gibi çalıştığını gösteriyoruz. Örneğin, batch_loss tanımlayan Python kodunuz batch_loss , sadece onun serileştirilmiş TFF temsiline batch_loss bu gerekli olabilir.

Şimdi, kaybın azalıp azalmadığını görmek için bu fonksiyonu birkaç kez ilk modele uygulayalım.

model = initial_model
losses = []
for _ in range(5):
  model = batch_train(model, sample_batch, 0.1)
  losses.append(batch_loss(model, sample_batch))
losses
[0.19690022, 0.13176313, 0.10113226, 0.082738124, 0.0703014]

Bir dizi yerel veri üzerinde gradyan inişi

Şimdi, batch_train işe batch_train gibi göründüğünden, tek bir grup yerine tek bir kullanıcıdan gelen tüm grupların tüm dizisini tüketen benzer bir eğitim işlevi local_train . Yeni hesaplamanın artık tff.SequenceType(BATCH_TYPE) yerine tff.SequenceType(BATCH_TYPE) kullanması BATCH_TYPE .

LOCAL_DATA_TYPE = tff.SequenceType(BATCH_TYPE)

@tff.federated_computation(MODEL_TYPE, tf.float32, LOCAL_DATA_TYPE)
def local_train(initial_model, learning_rate, all_batches):

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

  return tff.sequence_reduce(all_batches, initial_model, batch_fn)
str(local_train.type_signature)
'(<<weights=float32[784,10],bias=float32[10]>,float32,<x=float32[?,784],y=int32[?]>*> -> <weights=float32[784,10],bias=float32[10]>)'

Kodun bu kısa bölümünde gömülü epeyce detay var, hadi bunların üzerinden tek tek geçelim.

İlk olarak, bu mantığı tamamen TensorFlow'da uygulayabilirken, diziyi daha önce yaptığımıza benzer şekilde işlemek için tf.data.Dataset.reduce güvenebilirken, bu sefer mantığı yapıştırıcı dilinde ifade etmeyi seçtik , tff.federated_computation . tff.sequence_reduce işlemini gerçekleştirmek için federasyon operatörü tff.sequence_reduce kullandık.

Operatör tff.sequence_reduce benzer bir şekilde kullanılır tf.data.Dataset.reduce . tf.data.Dataset.reduce aynı olduğunu tf.data.Dataset.reduce , ancak hatırlayabileceğiniz gibi TensorFlow kodunu içeremeyen federe hesaplamalarda kullanım içindir. Bir dizi T tipi elemanlardan, indirgemenin başlangıç ​​durumundan (soyut olarak sıfır olarak bahsedeceğiz) bazı U tiplerinden ve indirgeme operatöründen oluşan, biçimsel bir 3-tuple parametresine sahip bir şablon operatörüdür . tek bir öğeyi işleyerek indirgeme durumunu değiştiren tür (<U,T> -> U) . Sonuç, tüm öğeleri sıralı bir sırayla işledikten sonra indirgemenin son durumudur. Örneğimizde, indirgeme durumu, verilerin bir önekine göre eğitilen modeldir ve öğeler veri yığınlarıdır.

İkinci olarak, bir hesaplamayı ( batch_train ) başka bir ( local_train ) içinde bir bileşen olarak local_train , ancak doğrudan kullanmadığımıza dikkat edin. Bunu bir indirgeme operatörü olarak kullanamayız çünkü ek bir parametre - öğrenme hızı alır. Bunu çözmek için, local_train parametresinin learning_rate parametresine bağlanan yerleşik bir federe hesaplama batch_fn tanımlarız. Bu şekilde tanımlanan bir çocuk hesaplamasına, alt hesaplama, üst hesaplamanın gövdesi dışında çağrılmadığı sürece, ebeveyninin resmi bir parametresini yakalamasına izin verilir. Bu kalıbı Python'daki functools.partial bir eşdeğeri olarak functools.partial .

learning_rate bu şekilde yakalanmasının pratik anlamı, elbette, tüm gruplar için aynı öğrenme oranı değerinin kullanılmasıdır.

Şimdi, yeni tanımlanmış yerel eğitim işlevini, örnek gruba katkıda bulunan aynı kullanıcıdan gelen tüm veri dizisi üzerinde deneyelim (basamak 5 ).

locally_trained_model = local_train(initial_model, 0.1, federated_train_data[5])

İşe yaradı mı? Bu soruyu cevaplamak için değerlendirme yapmamız gerekiyor.

Yerel değerlendirme

İşte tüm veri yığınlarındaki kayıpları toplayarak yerel değerlendirmeyi uygulamanın bir yolu (ortalamayı da aynı şekilde hesaplayabilirdik; bunu okuyucu için bir alıştırma olarak bırakacağız).

@tff.federated_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def local_eval(model, all_batches):
  # TODO(b/120157713): Replace with `tff.sequence_average()` once implemented.
  return tff.sequence_sum(
      tff.sequence_map(
          tff.federated_computation(lambda b: batch_loss(model, b), BATCH_TYPE),
          all_batches))
str(local_eval.type_signature)
'(<<weights=float32[784,10],bias=float32[10]>,<x=float32[?,784],y=int32[?]>*> -> float32)'

Yine, bu kodla gösterilen birkaç yeni unsur var, hadi bunların üzerinden tek tek geçelim.

İlk olarak, işlemleri sırasında iki yeni birleşik operatörler kullandık: tff.sequence_map bir eşleştirme işlevi alır T->U ve bir dizi T ve bir dizi yayan U eşleme fonksiyonu noktasal uygulanarak elde edildi ve tff.sequence_sum bu sadece tüm öğeleri ekler. Burada, her veri grubunu bir kayıp değeriyle eşleştiriyoruz ve ardından toplam kaybı hesaplamak için ortaya çıkan kayıp değerlerini ekliyoruz.

Tekrar tff.sequence_reduce , ancak bu en iyi seçenek olmayacaktır - indirgeme süreci tanım gereği sıralıdır, oysa eşleme ve toplam paralel olarak hesaplanabilir. Bir seçenek verildiğinde, uygulama seçeneklerini kısıtlamayan operatörlere bağlı kalmak en iyisidir, böylece TFF hesaplamamız gelecekte belirli bir ortama dağıtılmak üzere derlendiğinde, daha hızlı bir çalışma için tüm potansiyel fırsatlardan tam olarak yararlanılabilir. , daha ölçeklenebilir, daha fazla kaynak verimli yürütme.

İkincisi, sadece olduğu gibi bu notu local_train , biz gerekmez bileşen fonksiyonu ( batch_loss ) Ne federe operatörü daha fazla parametre alır ( tff.sequence_map ) beklediği, yine doğrudan sararak bu sefer inline kısmi tanımlamak böylece lambda bir şekilde tff.federated_computation . TensorFlow mantığını TFF'ye yerleştirmek için tff.tf_computation işlevini bağımsız değişken olarak bir işlevle satır içi olarak kullanmak tff.tf_computation .

Şimdi, eğitimimizin işe yarayıp yaramadığını görelim.

print('initial_model loss =', local_eval(initial_model,
                                         federated_train_data[5]))
print('locally_trained_model loss =',
      local_eval(locally_trained_model, federated_train_data[5]))
initial_model loss = 23.025854
locally_trained_model loss = 0.4348469

Gerçekten de kayıp azaldı. Peki bunu başka bir kullanıcının verileri üzerinde değerlendirirsek ne olur?

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

Beklendiği gibi işler daha da kötüye gitti. Model 5 tanımak üzere eğitildi ve hiç 0 görmedi. Bu, şu soruyu getiriyor - yerel eğitim, modelin kalitesini küresel perspektiften nasıl etkiledi?

Federe değerlendirme

Bu, yolculuğumuzda nihayet birleşik türlere ve birleşik hesaplamalara geri döndüğümüz nokta - başladığımız konu. Sunucudan kaynaklanan model için bir çift TFF türü tanımı ve istemcilerde kalan veriler.

SERVER_MODEL_TYPE = tff.type_at_server(MODEL_TYPE)
CLIENT_DATA_TYPE = tff.type_at_clients(LOCAL_DATA_TYPE)

Şimdiye kadar tanıtılan tüm tanımlarla, TFF'de federe değerlendirmeyi ifade etmek tek satırlık bir iştir - modeli müşterilere dağıtırız, her müşterinin kendi yerel veri kısmında yerel değerlendirmeyi başlatmasına izin verir ve ardından kaybın ortalamasını çıkarırız. İşte bunu yazmanın bir yolu.

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

Daha basit senaryolarda tff.federated_mean ve tff.federated_map örneklerini daha önce görmüştük ve sezgisel düzeyde beklendiği gibi çalışıyorlar, ancak kodun bu bölümünde göründüğünden daha fazlası var, o yüzden dikkatlice gözden geçirelim.

İlk olarak, her müşterinin veri bölümünün yerel kısmında yerel değerlendirmeyi başlatmasına izin verelim . Önceki bölümlerden hatırlayabileceğiniz gibi, local_eval bir tip imzasına sahiptir (<MODEL_TYPE, LOCAL_DATA_TYPE> -> float32) .

Federasyon operatörü tff.federated_map , bir tür T->U tipinin eşleme fonksiyonundan ve {T}@CLIENTS türünde bir federe değerinden (yani, üye bileşenleriyle) oluşan 2- tff.federated_map parametre olarak kabul eden bir şablondur. eşleme işlevinin parametresiyle aynı tür) ve {U}@CLIENTS türünün bir sonucunu döndürür.

local_eval istemci bazında uygulanacak bir eşleme işlevi olarak beslediğimiz için, ikinci bağımsız değişken {<MODEL_TYPE, LOCAL_DATA_TYPE>}@CLIENTS federasyon türünde olmalıdır, yani önceki bölümlerin isimlendirmesinde, federe bir demet olun. Her istemci, üye bir üye olarak local_eval için tam bir argüman setine sahip olmalıdır. Bunun yerine, onu 2 öğeli bir Python list besliyoruz. Burada neler oluyor?

Bir beslerken Aslında bu, örneğin başka bir yerde karşılaşmış olabilirsiniz örtülü tip alçı, benzer TFF, bir örtülü tip döküm örneğidir int bir kabul eden bir işleve float . Örtülü çevrim bu noktada nadiren kullanılmaktadır, ancak standart metni en aza indirmenin bir yolu olarak onu TFF'de daha yaygın hale getirmeyi planlıyoruz.

Bu durumda uygulanan örtük atama, {<X,Y>}@Z biçimindeki federe <{X}@Z,{Y}@Z> federe değer <{X}@Z,{Y}@Z> . Resmi olarak, bu ikisi farklı tip imzalar olsa da, ona programcıların bakış açısından bakarken, Z her cihaz iki birim X ve Y veri tutar. Burada yapılan budur benzemez zip Python ve gerçekten de, biz bir operatör sunuyoruz tff.federated_zip böyle dönüşümler explicity gerçekleştirmesini sağlar. tff.federated_map ikinci argüman olarak bir demet ile karşılaştığında, sizin için basitçe tff.federated_zip çağırır.

Yukarıda tff.federated_broadcast(model) göz önüne alındığında, artık tff.federated_broadcast(model) ifadesinin bir TFF türü {MODEL_TYPE}@CLIENTS değerini ve data TFF türü değeri {LOCAL_DATA_TYPE}@CLIENTS (veya kısaca CLIENT_DATA_TYPE ) olarak CLIENT_DATA_TYPE iki örtülü bir yoluyla birbirine süzüldü elde tff.federated_zip ikinci bağımsız değişken oluşturmak üzere tff.federated_map .

tff.federated_broadcast operatörü, beklediğiniz gibi, verileri sunucudan istemcilere aktarır.

Şimdi yerel eğitimimizin sistemdeki ortalama kaybı nasıl etkilediğini görelim.

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

Nitekim beklendiği gibi kayıp arttı. Modeli tüm kullanıcılar için iyileştirmek amacıyla, herkesin verileri üzerinde eğitim almamız gerekecek.

Federasyon eğitimi

Birleşik eğitimi uygulamanın en basit yolu, yerel olarak eğitmek ve ardından modelleri ortalamaktır. Bu, aşağıda görebileceğiniz gibi, daha önce tartıştığımız aynı yapı taşlarını ve kalıpları kullanır.

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 tarafından sağlanan tam özellikli Federated Averaging uygulamasında, modellerin ortalamasını almak yerine, örneğin güncelleme normlarını kırpma, sıkıştırma vb. .

Birkaç tur eğitim yürüterek ve önceki ve sonraki ortalama kaybı karşılaştırarak eğitimin işe yarayıp yaramadığını görelim.

model = initial_model
learning_rate = 0.1
for round_num in range(5):
  model = federated_train(model, learning_rate, federated_train_data)
  learning_rate = learning_rate * 0.9
  loss = federated_eval(model, federated_train_data)
  print('round {}, loss={}'.format(round_num, loss))
round 0, loss=21.60552406311035
round 1, loss=20.365678787231445
round 2, loss=19.27480125427246
round 3, loss=18.31110954284668
round 4, loss=17.45725440979004

Tamlık için, şimdi modelimizin iyi bir şekilde genelleştiğini doğrulamak için test verilerini de inceleyelim.

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

Bu, eğitimimizi tamamlıyor.

Elbette basitleştirilmiş örneğimiz, daha gerçekçi bir senaryoda yapmanız gereken birkaç şeyi yansıtmamaktadır - örneğin, kayıptan başka ölçümleri hesaplamadık. Daha eksiksiz bir örnek olarak ve teşvik etmek istediğimiz bazı kodlama uygulamalarını göstermenin bir yolu olarak tff.learning federe ortalamanın uygulanmasını incelemenizi öneririz.