Zapisz datę! Google I / O powraca w dniach 18-20 maja Zarejestruj się teraz
Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Przegląd

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło w serwisie GitHub Pobierz notatnik

Zanim zaczniemy

Zanim zaczniemy, wykonaj poniższe czynności, aby upewnić się, że środowisko jest poprawnie skonfigurowane. Jeśli nie widzisz powitania, zapoznaj się z instrukcją instalacji, aby uzyskać instrukcje.

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

import nest_asyncio
nest_asyncio.apply()
import collections
import attr
import functools
import numpy as np
import tensorflow as tf
import tensorflow_federated as tff

np.random.seed(0)

W samouczkach dotyczących klasyfikacji obrazów i generowania tekstu dowiedzieliśmy się, jak skonfigurować model i potoki danych dla uczenia federacyjnego (FL), a także przeprowadziliśmy szkolenie federacyjne za pośrednictwem warstwy TFF API tff.learning .

To tylko wierzchołek góry lodowej, jeśli chodzi o badania nad FL. W tym samouczku omówimy, jak zaimplementować algorytmy uczenia federacyjnego bez tff.learning interfejsu API tff.learning . Naszym celem jest osiągnięcie następujących celów:

Cele:

  • Zrozum ogólną strukturę federacyjnych algorytmów uczenia się.
  • Poznaj Federację Rdzenia TFF.
  • Użyj federacyjnego rdzenia, aby bezpośrednio wdrożyć uśrednianie federacyjne.

Chociaż ten samouczek jest samodzielny, zalecamy najpierw przeczytać samouczki dotyczące klasyfikacji obrazów i generowania tekstu .

Przygotowanie danych wejściowych

Najpierw ładujemy i wstępnie przetwarzamy zestaw danych EMNIST zawarty w TFF. Aby uzyskać więcej informacji, zobacz samouczek dotyczący klasyfikacji obrazów .

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

Aby wprowadzić zestaw danych do naszego modelu, spłaszczamy dane i konwertujemy każdy przykład na krotkę formularza (flattened_image_vector, label) .

NUM_CLIENTS = 10
BATCH_SIZE = 20

def preprocess(dataset):

  def batch_format_fn(element):
    """Flatten a batch of EMNIST data and return a (features, label) tuple."""
    return (tf.reshape(element['pixels'], [-1, 784]), 
            tf.reshape(element['label'], [-1, 1]))

  return dataset.batch(BATCH_SIZE).map(batch_format_fn)

Teraz pobieramy próbkę z niewielkiej liczby klientów i stosujemy powyższe przetwarzanie wstępne do ich zestawów danych.

client_ids = np.random.choice(emnist_train.client_ids, size=NUM_CLIENTS, replace=False)

federated_train_data = [preprocess(emnist_train.create_tf_dataset_for_client(x))
  for x in client_ids
]

Przygotowanie modelu

Używamy tego samego modelu, co w samouczku klasyfikacji obrazów . Ten model (zaimplementowany za pośrednictwem tf.keras ) ma pojedynczą warstwę ukrytą, po której następuje warstwa softmax.

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

Aby użyć tego modelu w TFF, tff.learning.Model model Keras jako tff.learning.Model . To pozwala nam wykonać przejście modelu do przodu w TFF i wyodrębnić wyniki modelu . Aby uzyskać więcej informacji, zobacz także samouczek dotyczący klasyfikacji obrazów .

def model_fn():
  keras_model = create_keras_model()
  return tff.learning.from_keras_model(
      keras_model,
      input_spec=federated_train_data[0].element_spec,
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])

Podczas gdy użyliśmy tf.keras do stworzenia tff.learning.Model , TFF obsługuje znacznie bardziej ogólne modele. Modele te mają następujące istotne atrybuty określające wagi modeli:

  • trainable_variables : Iterowalność tensorów odpowiadających warstwom możliwym do trenowania.
  • non_trainable_variables : non_trainable_variables tensorów odpowiadających warstwom, których nie można trenować.

Do naszych celów będziemy używać tylko trainable_variables . (ponieważ nasz model ma tylko takie!).

Budowanie własnego algorytmu Federated Learning

Chociaż interfejs API tff.learning umożliwia tworzenie wielu wariantów uśredniania federacyjnego, istnieją inne algorytmy stowarzyszone, które nie pasują do tej struktury. Na przykład możesz chcieć dodać regularyzację, obcinanie lub bardziej skomplikowane algorytmy, takie jak federacyjne szkolenie GAN . Zamiast tego możesz być również zainteresowany analizą federacyjną .

W przypadku tych bardziej zaawansowanych algorytmów będziemy musieli napisać własny niestandardowy algorytm przy użyciu TFF. W wielu przypadkach algorytmy federacyjne składają się z 4 głównych komponentów:

  1. Etap emisji serwer-klient.
  2. Lokalny krok aktualizacji klienta.
  3. Etap przesyłania klient-serwer.
  4. Krok aktualizacji serwera.

W TFF generalnie reprezentujemy algorytmy federacyjne jako tff.templates.IterativeProcess (który w tff.templates.IterativeProcess tylko tff.templates.IterativeProcess IterativeProcess ). Jest to klasa zawierająca funkcje initialize i next . Tutaj initialize jest używana do zainicjowania serwera, a next wykona jedną rundę komunikacji stowarzyszonego algorytmu. Napiszmy szkielet tego, jak powinien wyglądać nasz iteracyjny proces FedAvg.

Po pierwsze, mamy funkcję inicjalizującą, która po prostu tworzy tff.learning.Model i zwraca jego wagi, które można tff.learning.Model .

def initialize_fn():
  model = model_fn()
  return model.trainable_variables

Ta funkcja wygląda dobrze, ale jak zobaczymy później, będziemy musieli dokonać niewielkiej modyfikacji, aby uczynić ją „obliczeniem TFF”.

Chcemy również naszkicować next_fn .

def next_fn(server_weights, federated_dataset):
  # Broadcast the server weights to the clients.
  server_weights_at_client = broadcast(server_weights)

  # Each client computes their updated weights.
  client_weights = client_update(federated_dataset, server_weights_at_client)

  # The server averages these updates.
  mean_client_weights = mean(client_weights)

  # The server updates its model.
  server_weights = server_update(mean_client_weights)

  return server_weights

Skoncentrujemy się na osobnym zaimplementowaniu tych czterech komponentów. Najpierw skupiamy się na częściach, które można zaimplementować w czystym TensorFlow, a mianowicie na krokach aktualizacji klienta i serwera.

Bloki TensorFlow

Aktualizacja klienta

Będziemy używać naszego tff.learning.Model do szkolenia klientów w taki sam sposób, w jaki trenowałbyś model TensorFlow. W szczególności użyjemytf.GradientTape do obliczenia gradientu na partiach danych, a następnie zastosujemy ten gradient za pomocą client_optimizer . Skupiamy się tylko na obciążeniach, które można trenować.

@tf.function
def client_update(model, dataset, server_weights, client_optimizer):
  """Performs training (using the server model weights) on the client's dataset."""
  # Initialize the client model with the current server weights.
  client_weights = model.trainable_variables
  # Assign the server weights to the client model.
  tf.nest.map_structure(lambda x, y: x.assign(y),
                        client_weights, server_weights)

  # Use the client_optimizer to update the local model.
  for batch in dataset:
    with tf.GradientTape() as tape:
      # Compute a forward pass on the batch of data
      outputs = model.forward_pass(batch)

    # Compute the corresponding gradient
    grads = tape.gradient(outputs.loss, client_weights)
    grads_and_vars = zip(grads, client_weights)

    # Apply the gradient using a client optimizer.
    client_optimizer.apply_gradients(grads_and_vars)

  return client_weights

Aktualizacja serwera

Aktualizacja serwera dla FedAvg jest prostsza niż aktualizacja klienta. Zaimplementujemy uśrednianie federacyjne „waniliowe”, w którym po prostu zastępujemy wagi modelu serwera średnią wag modelu klienta. Ponownie skupiamy się tylko na obciążeniach, które można trenować.

@tf.function
def server_update(model, mean_client_weights):
  """Updates the server model weights as the average of the client model weights."""
  model_weights = model.trainable_variables
  # Assign the mean client weights to the server model.
  tf.nest.map_structure(lambda x, y: x.assign(y),
                        model_weights, mean_client_weights)
  return model_weights

Fragment można uprościć, zwracając po prostu wartość mean_client_weights . Jednak bardziej zaawansowane implementacje uśredniania federacyjnego używają mean_client_weights z bardziej wyrafinowanymi technikami, takimi jak momentum lub adaptacyjność.

Wyzwanie : zaimplementuj wersję server_update która aktualizuje wagi serwera, aby były środkowym punktem model_weights i mean_client_weights. (Uwaga: ten rodzaj podejścia „punktu środkowego” jest analogiczny do niedawnych prac nad optymalizatorem Lookahead !).

Do tej pory napisaliśmy tylko czysty kod TensorFlow. Jest to zgodne z projektem, ponieważ TFF pozwala na użycie większości kodu TensorFlow, który już znasz. Jednak teraz musimy określić logikę orkiestracji , czyli logikę, która dyktuje, co serwer emituje do klienta, a co klient przesyła na serwer.

Będzie to wymagało Federacji Core of TFF.

Wprowadzenie do Federated Core

Federated Core (FC) to zestaw interfejsów niższego poziomu, które służą jako podstawa interfejsu API tff.learning . Jednak te interfejsy nie ograniczają się do uczenia się. W rzeczywistości można ich używać do analiz i wielu innych obliczeń na rozproszonych danych.

Na wysokim poziomie sfederowany rdzeń to środowisko programistyczne, które umożliwia spójną logikę programu na łączenie kodu TensorFlow z operatorami komunikacji rozproszonej (takimi jak sumy rozproszone i transmisje). Celem jest zapewnienie naukowcom i praktykom wyraźnej kontroli nad komunikacją rozproszoną w ich systemach, bez konieczności szczegółowego wdrażania systemu (np. Określania wymiany komunikatów w sieci punkt-punkt).

Jedną z kluczowych kwestii jest to, że TFF został zaprojektowany z myślą o ochronie prywatności. Dlatego umożliwia jawną kontrolę nad tym, gdzie znajdują się dane, aby zapobiec niepożądanemu gromadzeniu danych w scentralizowanej lokalizacji serwera.

Dane sfederowane

Kluczową koncepcją w TFF są „dane federacyjne”, które odnoszą się do zbioru elementów danych hostowanych w grupie urządzeń w systemie rozproszonym (np. Zestawy danych klienta lub wagi modelu serwera). Modelujemy całą kolekcję elementów danych na wszystkich urządzeniach jako pojedynczą wartość federacyjną .

Na przykład załóżmy, że mamy urządzenia klienckie, z których każde ma wartość zmiennoprzecinkową reprezentującą temperaturę czujnika. Moglibyśmy przedstawić to jako sfederowany pływak

federated_float_on_clients = tff.FederatedType(tf.float32, tff.CLIENTS)

Typy stowarzyszone są określane przez typ T jego składników członkowskich (np. tf.float32 ) i grupę G urządzeń. Skoncentrujemy się na przypadkach, w których G to tff.CLIENTS lub tff.SERVER . Taki typ federacyjny jest reprezentowany jako {T}@G , jak pokazano poniżej.

str(federated_float_on_clients)
'{float32}@CLIENTS'

Dlaczego tak bardzo zależy nam na miejscach docelowych? Kluczowym celem TFF jest umożliwienie pisania kodu, który mógłby zostać wdrożony w rzeczywistym systemie rozproszonym. Oznacza to, że ważne jest, aby zastanowić się, które podzbiory urządzeń wykonują dany kod i gdzie znajdują się różne fragmenty danych.

TFF skupia się na trzech rzeczach: danych, gdzie dane są wprowadzane, oraz w jaki sposób dane są przekształcane. Pierwsze dwa są hermetyzowane w typach stowarzyszonych, a ostatnie w obliczeniach federacyjnych .

Obliczenia federacyjne

TFF jest funkcjonalnym środowiskiem programowania o silnym typie, którego podstawowymi jednostkami są obliczenia federacyjne . Są to elementy logiki, które akceptują wartości stowarzyszone jako dane wejściowe i zwracają wartości stowarzyszone jako dane wyjściowe.

Załóżmy na przykład, że chcemy uśrednić temperatury na czujnikach naszych klientów. Możemy zdefiniować następujące elementy (używając naszego sfederowanego float):

@tff.federated_computation(tff.FederatedType(tf.float32, tff.CLIENTS))
def get_average_temperature(client_temperatures):
  return tff.federated_mean(client_temperatures)

Można zapytać, jak to jest różne od tf.function dekoratora w TensorFlow? Kluczową odpowiedzią jest to, że kod wygenerowany przez tff.federated_computation nie jest kodem TensorFlow ani Python; Jest to specyfikacja systemu rozproszonego w wewnętrznym, niezależnym od platformy języku kleju .

Chociaż może to wydawać się skomplikowane, możesz myśleć o obliczeniach TFF jako funkcjach z dobrze zdefiniowanymi sygnaturami typów. Te podpisy typów można sprawdzać bezpośrednio.

str(get_average_temperature.type_signature)
'({float32}@CLIENTS -> float32@SERVER)'

To tff.federated_computation akceptuje argumenty typu stowarzyszonego {float32}@CLIENTS i zwraca wartości typu stowarzyszonego {float32}@SERVER . Obliczenia stowarzyszone mogą również przechodzić od serwera do klienta, od klienta do klienta lub od serwera do serwera. Obliczenia stowarzyszone mogą być również tworzone jak zwykłe funkcje, o ile ich sygnatury typów są zgodne.

Aby wspierać rozwój, TFF umożliwia wywołanie funkcji tff.federated_computation jako funkcji Pythona. Na przykład możemy zadzwonić

get_average_temperature([68.5, 70.3, 69.8])
69.53334

Obliczenia niechętne i TensorFlow

Istnieją dwa kluczowe ograniczenia, o których należy pamiętać. Po pierwsze, gdy interpreter tff.federated_computation Python napotyka dekorator tff.federated_computation , funkcja jest śledzona raz i serializowana do wykorzystania w przyszłości. Ze względu na zdecentralizowany charakter uczenia federacyjnego to przyszłe użycie może mieć miejsce w innym miejscu, na przykład w zdalnym środowisku wykonawczym. Dlatego obliczenia TFF są zasadniczo niechętne . To zachowanie jest nieco analogiczne do tf.function dekoratora tf.function w TensorFlow.

Po drugie, obliczenia stowarzyszone mogą składać się tylko z operatorów stowarzyszonych (takich jak tff.federated_mean ) i nie mogą zawierać operacji TensorFlow. Kod TensorFlow musi być ograniczony do bloków ozdobionych tff.tf_computation . Większość zwykłego kodu TensorFlow może być dekorowana bezpośrednio, na przykład następująca funkcja, która pobiera liczbę i dodaje do niej 0.5 .

@tff.tf_computation(tf.float32)
def add_half(x):
  return tf.add(x, 0.5)

Mają również podpisy typu, ale bez miejsc docelowych . Na przykład możemy zadzwonić

str(add_half.type_signature)
'(float32 -> float32)'

Tutaj widzimy ważną różnicę między tff.federated_computation a tff.tf_computation . Pierwsza ma wyraźne miejsca docelowe, podczas gdy druga nie.

Możemy używać bloków tff.tf_computation w obliczeniach federacyjnych, określając miejsca docelowe. Stwórzmy funkcję, która dodaje połowę, ale tylko do sfederowanych pływaków na klientach. Możemy to zrobić za pomocą tff.federated_map , który stosuje dane tff.tf_computation , zachowując jednocześnie miejsce docelowe.

@tff.federated_computation(tff.FederatedType(tf.float32, tff.CLIENTS))
def add_half_on_clients(x):
  return tff.federated_map(add_half, x)

Ta funkcja jest prawie identyczna jak add_half , z tym wyjątkiem, że akceptuje tylko wartości z umieszczeniem w tff.CLIENTS i zwraca wartości z tym samym miejscem. Widzimy to w jego sygnaturze typu:

str(add_half_on_clients.type_signature)
'({float32}@CLIENTS -> {float32}@CLIENTS)'

W podsumowaniu:

  • TFF działa w oparciu o wartości federacyjne.
  • Każda wartość stowarzyszona ma typ stowarzyszony z typem (np. tf.float32 ) i miejscem docelowym (np. tff.CLIENTS ).
  • Wartości stowarzyszone można transformować za pomocą obliczeń stowarzyszonych , które muszą być ozdobione tff.federated_computation i podpisem typu stowarzyszonego.
  • Kod TensorFlow musi być zawarty w blokach z dekoratorami tff.tf_computation .
  • Te bloki można następnie włączyć do obliczeń stowarzyszonych.

Tworzenie własnego algorytmu Federated Learning, ponownie odwiedzone

Teraz, gdy mamy wgląd w Federated Core, możemy zbudować nasz własny federacyjny algorytm uczenia się. Pamiętaj, że powyżej zdefiniowaliśmy dla naszego algorytmu initialize_fn i next_fn . next_fn użyje client_update i server_update które zdefiniowaliśmy za pomocą czystego kodu TensorFlow.

Jednak aby uczynić nasz algorytm obliczeniem federacyjnym, będziemy potrzebować zarówno next_fn i initialize_fn aby każdy z nich był tff.federated_computation .

Bloki stowarzyszone TensorFlow

Tworzenie obliczenia inicjalizacyjnego

Funkcja inicjalizacji będzie dość prosta: stworzymy model za pomocą model_fn . Pamiętaj jednak, że musimy oddzielić nasz kod tff.tf_computation za pomocą tff.tf_computation .

@tff.tf_computation
def server_init():
  model = model_fn()
  return model.trainable_variables

Następnie możemy przekazać to bezpośrednio do obliczeń stowarzyszonych przy użyciu tff.federated_value .

@tff.federated_computation
def initialize_fn():
  return tff.federated_value(server_init(), tff.SERVER)

Tworzenie next_fn

Używamy teraz naszego kodu aktualizacji klienta i serwera, aby napisać rzeczywisty algorytm. Najpierw client_update naszą client_update na tff.tf_computation która akceptuje zestawy danych klienta i wagi serwera, i wyświetla zaktualizowany tensor wagi klienta.

Będziemy potrzebować odpowiednich typów, aby odpowiednio udekorować naszą funkcję. Na szczęście typ wag serwera można wyodrębnić bezpośrednio z naszego modelu.

whimsy_model = model_fn()
tf_dataset_type = tff.SequenceType(whimsy_model.input_spec)

Spójrzmy na sygnaturę typu zbioru danych. Pamiętaj, że zrobiliśmy 28 na 28 obrazów (z etykietami całkowitymi) i spłaszczyliśmy je.

str(tf_dataset_type)
'<float32[?,784],int32[?,1]>*'

Możemy również wyodrębnić typ wag modelu za pomocą naszej funkcji server_init powyżej.

model_weights_type = server_init.type_signature.result

Badając sygnaturę typu, będziemy mogli zobaczyć architekturę naszego modelu!

str(model_weights_type)
'<float32[784,10],float32[10]>'

Możemy teraz utworzyć nasze tff.tf_computation dla aktualizacji klienta.

@tff.tf_computation(tf_dataset_type, model_weights_type)
def client_update_fn(tf_dataset, server_weights):
  model = model_fn()
  client_optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
  return client_update(model, tf_dataset, server_weights, client_optimizer)

Wersję tff.tf_computation aktualizacji serwera można zdefiniować w podobny sposób, używając typów, które już wyodrębniliśmy.

@tff.tf_computation(model_weights_type)
def server_update_fn(mean_client_weights):
  model = model_fn()
  return server_update(model, mean_client_weights)

Wreszcie, musimy utworzyć tff.federated_computation który to wszystko łączy. Ta funkcja akceptuje dwie wartości stowarzyszone , jedną odpowiadającą wagom serwera (z umieszczeniem tff.SERVER ), a drugą odpowiadającą tff.CLIENTS danych klienta (z umieszczeniem tff.CLIENTS ).

Zwróć uwagę, że oba te typy zostały zdefiniowane powyżej! Po prostu musimy nadać im właściwe położenie przy użyciu tff.FederatedType .

federated_server_type = tff.FederatedType(model_weights_type, tff.SERVER)
federated_dataset_type = tff.FederatedType(tf_dataset_type, tff.CLIENTS)

Pamiętasz 4 elementy algorytmu FL?

  1. Etap emisji serwer-klient.
  2. Lokalny krok aktualizacji klienta.
  3. Etap przesyłania klient-serwer.
  4. Krok aktualizacji serwera.

Teraz, gdy stworzyliśmy powyższe, każda część może być zwięźle reprezentowana jako pojedyncza linia kodu TFF. Ta prostota spowodowała, że ​​musieliśmy szczególnie uważać, aby określić takie rzeczy, jak typy federacyjne!

@tff.federated_computation(federated_server_type, federated_dataset_type)
def next_fn(server_weights, federated_dataset):
  # Broadcast the server weights to the clients.
  server_weights_at_client = tff.federated_broadcast(server_weights)

  # Each client computes their updated weights.
  client_weights = tff.federated_map(
      client_update_fn, (federated_dataset, server_weights_at_client))

  # The server averages these updates.
  mean_client_weights = tff.federated_mean(client_weights)

  # The server updates its model.
  server_weights = tff.federated_map(server_update_fn, mean_client_weights)

  return server_weights

Mamy teraz plik tff.federated_computation zarówno dla inicjalizacji algorytmu, jak i dla uruchomienia jednego kroku algorytmu. Aby zakończyć nasz algorytm, przekazujemy je do tff.templates.IterativeProcess .

federated_algorithm = tff.templates.IterativeProcess(
    initialize_fn=initialize_fn,
    next_fn=next_fn
)

Przyjrzyjmy się sygnaturze typu initialize i next funkcji naszego procesu iteracyjnego.

str(federated_algorithm.initialize.type_signature)
'( -> <float32[784,10],float32[10]>@SERVER)'

Odzwierciedla to fakt, że federated_algorithm.initialize jest funkcją bezargumentową, która zwraca model jednowarstwowy (z macierzą wag 784 na 10 i 10 jednostkami odchylenia).

str(federated_algorithm.next.type_signature)
'(<<float32[784,10],float32[10]>@SERVER,{<float32[?,784],int32[?,1]>*}@CLIENTS> -> <float32[784,10],float32[10]>@SERVER)'

Widzimy tutaj, że federated_algorithm.next akceptuje model serwera i dane klienta oraz zwraca zaktualizowany model serwera.

Ocena algorytmu

Przeprowadźmy kilka rund i zobaczmy, jak zmieni się strata. Najpierw zdefiniujemy funkcję oceny przy użyciu scentralizowanego podejścia omówionego w drugim samouczku.

Najpierw tworzymy scentralizowany zestaw danych oceny, a następnie stosujemy to samo przetwarzanie wstępne, którego używaliśmy dla danych szkoleniowych.

Zauważ, że tylko take pierwsze elementy do 1000 względów wydajności obliczeniowej, ale zazwyczaj użyjemy całego badania zestawu danych.

central_emnist_test = emnist_test.create_tf_dataset_from_all_clients().take(1000)
central_emnist_test = preprocess(central_emnist_test)

Następnie piszemy funkcję, która akceptuje stan serwera i używa Keras do oceny na testowym zbiorze danych. Jeśli znasz tf.Keras , wszystko będzie wyglądać znajomo, chociaż zwróć uwagę na użycie set_weights !

def evaluate(server_state):
  keras_model = create_keras_model()
  keras_model.compile(
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()]  
  )
  keras_model.set_weights(server_state)
  keras_model.evaluate(central_emnist_test)

Teraz zainicjujmy nasz algorytm i oceńmy na zbiorze testowym.

server_state = federated_algorithm.initialize()
evaluate(server_state)
50/50 [==============================] - 0s 2ms/step - loss: 2.3026 - sparse_categorical_accuracy: 0.0910

Poćwiczmy przez kilka rund i zobaczmy, czy coś się zmieni.

for round in range(15):
  server_state = federated_algorithm.next(server_state, federated_train_data)
evaluate(server_state)
50/50 [==============================] - 0s 1ms/step - loss: 2.1706 - sparse_categorical_accuracy: 0.2440

Widzimy niewielki spadek funkcji straty. Chociaż skok jest niewielki, wykonaliśmy tylko 10 rund szkoleniowych i na niewielkiej grupie klientów. Aby zobaczyć lepsze wyniki, być może będziemy musieli wykonać setki, jeśli nie tysiące rund.

Modyfikacja naszego algorytmu

W tym miejscu zatrzymajmy się i pomyślmy o tym, co osiągnęliśmy. Wdrożyliśmy uśrednianie federacyjne bezpośrednio, łącząc czysty kod TensorFlow (dla aktualizacji klienta i serwera) z obliczeniami federacyjnymi z Federated Core TFF.

Aby przeprowadzić bardziej wyrafinowaną naukę, możemy po prostu zmienić to, co mamy powyżej. W szczególności, edytując powyższy czysty kod TF, możemy zmienić sposób, w jaki klient przeprowadza szkolenie lub w jaki sposób serwer aktualizuje swój model.

Wyzwanie: Dodaj obcinanie gradientu do funkcji client_update .

Gdybyśmy chcieli wprowadzić większe zmiany, moglibyśmy również zmusić serwer do przechowywania i transmitowania większej ilości danych. Na przykład serwer może również przechowywać współczynnik uczenia się klienta i sprawiać, że będzie on zanikał w czasie! Zauważ, że będzie to wymagało zmian w typach sygnatur używanych w wywołaniach tff.tf_computation powyżej.

Trudniejsze wyzwanie: Wdrożenie uśredniania federacyjnego ze spadkiem szybkości uczenia się klientów.

W tym momencie możesz zacząć zdawać sobie sprawę, jak duża jest elastyczność w tym, co możesz zaimplementować w tej strukturze. Pomysły (w tym odpowiedź na trudniejsze wyzwanie powyżej) można znaleźć w kodzie źródłowym tff.learning.build_federated_averaging_process lub sprawdzić różne projekty badawcze wykorzystujące TFF.