Sfederowany rdzeń

W tym dokumencie przedstawiono podstawową warstwę TFF, która służy jako podstawa dla Federated Learning i ewentualnych przyszłych algorytmów stowarzyszonych nieuczących się.

Aby uzyskać delikatne wprowadzenie do Federated Core, przeczytaj poniższe samouczki, ponieważ przedstawiają one niektóre podstawowe pojęcia na przykładach i demonstrują krok po kroku konstrukcję prostego algorytmu uśredniania stowarzyszonego.

Zachęcamy również do zapoznania się z Federated Learning i powiązanymi z nim samouczkami na temat klasyfikacji obrazów i generowania tekstu , ponieważ zastosowania Federated Core API (FC API) na potrzeby uczenia się stowarzyszonego zapewniają ważny kontekst dla niektórych wyborów, których dokonaliśmy w projektowania tej warstwy.

Przegląd

Cele, zamierzone zastosowania i zakres

Federated Core (FC) najlepiej rozumieć jako środowisko programistyczne do wdrażania obliczeń rozproszonych, tj. obliczeń obejmujących wiele komputerów (telefony komórkowe, tablety, urządzenia wbudowane, komputery stacjonarne, czujniki, serwery baz danych itp.), z których każdy może wykonywać inne niż trywialne przetwarzanie lokalnie i komunikują się poprzez sieć w celu koordynowania swojej pracy.

Termin rozproszony jest bardzo ogólny i TFF nie obejmuje wszystkich możliwych typów algorytmów rozproszonych, dlatego wolimy używać mniej ogólnego terminu obliczenia stowarzyszone do opisania typów algorytmów, które można wyrazić w tym środowisku.

Choć zdefiniowanie terminu obliczenia stowarzyszone w całkowicie formalny sposób wykracza poza zakres tego dokumentu, należy pomyśleć o typach algorytmów wyrażonych w pseudokodzie w publikacji badawczej opisującej nowy algorytm uczenia się rozproszonego.

Krótko mówiąc, celem FC jest umożliwienie podobnie zwartej reprezentacji, na podobnym poziomie abstrakcji przypominającym pseudokod, logiki programu, która nie jest pseudokodem, ale jest wykonywalna w różnych środowiskach docelowych.

Kluczową cechą definiującą rodzaje algorytmów, które FC ma wyrażać, jest to, że działania uczestników systemu są opisywane w sposób zbiorowy. Mówimy zatem zwykle o każdym urządzeniu lokalnie przetwarzającym dane oraz o urządzeniach koordynujących pracę przez scentralizowanego koordynatora transmitującego , zbierającego lub agregującego ich wyniki.

Chociaż TFF został zaprojektowany tak, aby móc wykraczać poza proste architektury klient-serwer , koncepcja zbiorowego przetwarzania ma fundamentalne znaczenie. Wynika to z początków TFF w uczeniu federacyjnym, technologii pierwotnie zaprojektowanej do wspierania obliczeń na potencjalnie wrażliwych danych, które pozostają pod kontrolą urządzeń klienckich i których nie można po prostu pobrać do centralnej lokalizacji ze względu na prywatność. Chociaż każdy klient w takich systemach wnosi dane i moc obliczeniową do obliczenia wyniku przez system (wynik, który, jak ogólnie oczekujemy, będzie wartościowy dla wszystkich uczestników), staramy się również chronić prywatność i anonimowość każdego klienta.

Tak więc, chociaż większość frameworków do przetwarzania rozproszonego jest zaprojektowana tak, aby wyrażać przetwarzanie z perspektywy poszczególnych uczestników – to znaczy na poziomie indywidualnej wymiany komunikatów punkt-punkt oraz współzależności przejść stanu lokalnego uczestnika z komunikatami przychodzącymi i wychodzącymi , Federated Core TFF ma na celu opisanie zachowania systemu z globalnej perspektywy całego systemu (podobnie jak np. MapReduce ).

W rezultacie, chociaż rozproszone struktury do celów ogólnych mogą oferować operacje takie jak wysyłanie i odbieranie jako elementy składowe, FC udostępnia elementy składowe, takie jak tff.federated_sum , tff.federated_reduce lub tff.federated_broadcast , które hermetyzują proste protokoły rozproszone.

Język

Interfejs Pythona

TFF używa wewnętrznego języka do reprezentowania obliczeń stowarzyszonych, którego składnia jest zdefiniowana przez reprezentację nadającą się do serializacji w computation.proto . Jednak użytkownicy FC API na ogół nie będą musieli bezpośrednio wchodzić w interakcję z tym językiem. Zamiast tego udostępniamy interfejs API języka Python (przestrzeń nazw tff ), który otacza go w celu zdefiniowania obliczeń.

W szczególności TFF udostępnia dekoratory funkcji Pythona, takie jak tff.federated_computation , które śledzą treść dekorowanych funkcji i tworzą serializowane reprezentacje logiki obliczeń stowarzyszonych w języku TFF. Funkcja ozdobiona tff.federated_computation działa jako nośnik takiej serializowanej reprezentacji i może osadzić ją jako element składowy w treści innego obliczenia lub wykonać na żądanie po wywołaniu.

Oto tylko jeden przykład; więcej przykładów można znaleźć w samouczkach dotyczących algorytmów niestandardowych .

@tff.federated_computation(tff.FederatedType(np.float32, tff.CLIENTS))
def get_average_temperature(sensor_readings):
  return tff.federated_mean(sensor_readings)

Czytelnicy zaznajomieni z TensorFlow uznają to podejście za analogiczne do pisania kodu w Pythonie, który używa funkcji takich jak tf.add lub tf.reduce_sum w sekcji kodu Pythona, która definiuje wykres TensorFlow. Chociaż kod jest technicznie wyrażony w języku Python, jego celem jest skonstruowanie możliwej do serializacji reprezentacji tf.Graph znajdującej się pod spodem i to wykres, a nie kod Pythona, jest wewnętrznie wykonywany przez środowisko wykonawcze TensorFlow. Podobnie można myśleć o tff.federated_mean jako o wstawianiu stowarzyszonej operacji do stowarzyszonego obliczenia reprezentowanego przez get_average_temperature .

Jednym z powodów zdefiniowania języka przez FC jest fakt, że, jak zauważono powyżej, obliczenia stowarzyszone określają rozproszone zachowania zbiorowe i jako takie ich logika jest nielokalna. Na przykład TFF udostępnia operatory, których wejścia i wyjścia mogą znajdować się w różnych miejscach sieci.

Wymaga to języka i systemu typów, które uchwyciłyby pojęcie rozproszoności.

Wpisz System

Federated Core oferuje następujące kategorie typów. Opisując te typy, wskazujemy na konstruktory typów oraz wprowadzamy zwartą notację, gdyż jest to wygodny sposób opisu typów obliczeń i operatorów.

Po pierwsze, oto kategorie typów, które są koncepcyjnie podobne do tych, które można znaleźć w istniejących językach głównego nurtu:

  • Typy tensorów ( tff.TensorType ). Podobnie jak w TensorFlow, mają one dtype i shape . Jedyna różnica polega na tym, że obiekty tego typu nie ograniczają się do instancji tf.Tensor w Pythonie, które reprezentują wyniki operacji TensorFlow na grafie TensorFlow, ale mogą również zawierać jednostki danych, które można wygenerować, np. jako wynik rozproszonego protokół agregacji. Zatem typ tensora TFF jest po prostu abstrakcyjną wersją konkretnej fizycznej reprezentacji tego typu w Pythonie lub TensorFlow.

    TensorTypes firmy TFF mogą być bardziej rygorystyczne w (statycznym) traktowaniu kształtów niż TensorFlow. Na przykład system typów TFF traktuje tensor o nieznanej randze jako możliwy do przypisania z dowolnego innego tensora tego samego dtype , ale nie można go przypisać do żadnego tensora o ustalonej randze. To podejście zapobiega pewnym błędom w czasie wykonywania (np. próbom przekształcenia tensora o nieznanej randze w kształt z nieprawidłową liczbą elementów) kosztem większej dokładności obliczeń uznawanych przez TFF za prawidłowe.

    Zwarta notacja typów tensorowych to dtype lub dtype[shape] . Na przykład int32 i int32[10] to odpowiednio typy wektorów całkowitych i int.

  • Typy sekwencji ( tff.SequenceType ). Są to abstrakcyjne odpowiedniki TFF konkretnej koncepcji TensorFlow tf.data.Dataset s. Elementy sekwencji mogą być używane sekwencyjnie i mogą obejmować typy złożone.

    Zwarta reprezentacja typów sekwencji to T* , gdzie T jest typem elementów. Na przykład int32* reprezentuje sekwencję liczb całkowitych.

  • Nazwane typy krotek ( tff.StructType ). Jest to sposób TFF na konstruowanie krotek i struktur przypominających słownik, które mają z góry określoną liczbę elementów o określonych typach, nazwanych lub nienazwanych. Co ważne, koncepcja nazwanych krotek TFF obejmuje abstrakcyjny odpowiednik krotek argumentów Pythona, tj. zbiory elementów, z których niektóre, ale nie wszystkie, mają nazwy, a niektóre są pozycyjne.

    Notacja zwarta nazwanych krotek to <n_1=T_1, ..., n_k=T_k> , gdzie n_k to opcjonalne nazwy elementów, a T_k to typy elementów. Na przykład <int32,int32> to zwarty zapis pary nienazwanych liczb całkowitych, a <X=float32,Y=float32> to zwarty zapis pary liczb zmiennoprzecinkowych o nazwach X i Y , które mogą reprezentować punkt na płaszczyźnie . Krotki można zagnieżdżać, a także mieszać z innymi typami, np. <X=float32,Y=float32>* będzie zwartym zapisem sekwencji punktów.

  • Typy funkcji ( tff.FunctionType ). TFF to funkcjonalny framework programowania, w którym funkcje są traktowane jako wartości najwyższej klasy . Funkcje mają co najwyżej jeden argument i dokładnie jeden wynik.

    Zwarty zapis funkcji to (T -> U) , gdzie T jest typem argumentu, a U jest typem wyniku lub ( -> U) , jeśli nie ma argumentu (chociaż funkcje bezargumentowe są zdegenerowane koncepcja, która istnieje głównie tylko na poziomie Pythona). Na przykład (int32* -> int32) to zapis typu funkcji, które redukują sekwencję liczb całkowitych do pojedynczej wartości całkowitej.

Poniższe typy dotyczą aspektu obliczeń TFF w systemach rozproszonych. Ponieważ te koncepcje są w pewnym stopniu unikalne dla TFF, zachęcamy do zapoznania się z samouczkiem dotyczącym algorytmów niestandardowych w celu uzyskania dodatkowego komentarza i przykładów.

  • Typ miejsca docelowego . Ten typ nie jest jeszcze udostępniany w publicznym interfejsie API inaczej niż w postaci 2 literałów tff.SERVER i tff.CLIENTS , które można traktować jako stałe tego typu. Jest on jednak używany wewnętrznie i zostanie wprowadzony do publicznego interfejsu API w przyszłych wersjach. Kompaktową reprezentacją tego typu jest placement .

    Miejsce docelowe reprezentuje zbiorowość uczestników systemu, którzy odgrywają określoną rolę. Początkowa wersja jest przeznaczona do obliczeń klient-serwer, w których występują 2 grupy uczestników: klienci i serwer (można o nim myśleć jak o grupie pojedynczej). Jednakże w bardziej skomplikowanych architekturach mogą istnieć inne role, takie jak pośrednicy agregatorzy w systemie wielowarstwowym, którzy mogą wykonywać różne typy agregacji lub stosować inne typy kompresji/dekompresji danych niż te stosowane przez serwer lub klienci.

    Podstawowym celem zdefiniowania pojęcia rozmieszczenia jest podstawa do zdefiniowania typów stowarzyszonych .

  • Typy stowarzyszone ( tff.FederatedType ). Wartość typu stowarzyszonego to taka, która jest hostowana przez grupę uczestników systemu zdefiniowaną przez określone miejsce docelowe (takie jak tff.SERVER lub tff.CLIENTS ). Typ stowarzyszony jest definiowany przez wartość miejsca docelowego (jest to zatem typ zależny ), typ składników członkowskich (jaki rodzaj treści każdy z uczestników hostuje lokalnie) oraz dodatkowy bit all_equal , który określa, czy wszyscy uczestnicy są lokalnie hostowanie tego samego elementu.

    Zwarty zapis wartości typu stowarzyszonego, które obejmują elementy (składniki członkowskie) typu T , z których każdy jest hostowany przez grupę (miejsce docelowe) G , to T@G lub {T}@G z ustawionym lub nie ustawionym bitem all_equal .

    Na przykład:

    • {int32}@CLIENTS reprezentuje wartość stowarzyszoną składającą się z zestawu potencjalnie odrębnych liczb całkowitych, po jednej na urządzenie klienckie. Należy pamiętać, że mówimy o pojedynczej wartości stowarzyszonej obejmującej wiele elementów danych pojawiających się w wielu lokalizacjach w sieci. Można o tym pomyśleć jako o rodzaju tensora o wymiarze „sieciowym”, chociaż analogia ta nie jest doskonała, ponieważ TFF nie pozwala na przypadkowy dostęp do składników składowych o wartości stowarzyszonej.

    • {<X=float32,Y=float32>*}@CLIENTS reprezentuje stowarzyszony zestaw danych , wartość składającą się z wielu sekwencji współrzędnych XY , po jednej sekwencji na urządzenie klienckie.

    • <weights=float32[10,5],bias=float32[5]>@SERVER reprezentuje nazwaną krotkę tensorów wagi i obciążenia na serwerze. Ponieważ usunęliśmy nawiasy klamrowe, oznacza to, że ustawiony jest bit all_equal , tj. istnieje tylko jedna krotka (niezależnie od tego, ile replik serwerów może znajdować się w klastrze obsługującym tę wartość).

Bloki konstrukcyjne

Język Federated Core jest formą rachunku lambda z kilkoma dodatkowymi elementami.

Zawiera następujące abstrakcje programistyczne dostępne obecnie w publicznym interfejsie API:

  • Obliczenia TensorFlow ( tff.tensorflow.computation ). Są to sekcje kodu TensorFlow zapakowane jako komponenty wielokrotnego użytku w TFF przy użyciu dekoratora tff.tensorflow.computation . Zawsze mają typy funkcjonalne i w przeciwieństwie do funkcji w TensorFlow mogą przyjmować parametry strukturalne lub zwracać ustrukturyzowane wyniki typu sekwencyjnego.

    Oto jeden przykład obliczenia TF typu (int32* -> int) które wykorzystuje operator tf.data.Dataset.reduce do obliczenia sumy liczb całkowitych:

    @tff.tensorflow.computation(tff.SequenceType(np.int32))
    def add_up_integers(x):
      return x.reduce(np.int32(0), lambda x, y: x + y)
    
  • Operatory wewnętrzne lub stowarzyszone ( tff.federated_... ). Jest to biblioteka funkcji, takich jak tff.federated_sum lub tff.federated_broadcast , które stanowią większość interfejsu API FC, a większość z nich reprezentuje operatorów komunikacji rozproszonej do użytku z TFF.

    Nazywamy je elementami wewnętrznymi , ponieważ podobnie jak funkcje wewnętrzne stanowią otwarty, rozszerzalny zestaw operatorów zrozumiałych dla TFF i skompilowanych do kodu niższego poziomu.

    Większość z tych operatorów ma parametry i wyniki typu stowarzyszonego, a większość z nich to szablony, które można zastosować do różnych rodzajów danych.

    Na przykład tff.federated_broadcast można traktować jako operator szablonowy typu funkcjonalnego T@SERVER -> T@CLIENTS .

  • Wyrażenia lambda ( tff.federated_computation ). Wyrażenie lambda w TFF jest odpowiednikiem lambda lub def w Pythonie; składa się z nazwy parametru i treści (wyrażenia) zawierającej odniesienia do tego parametru.

    W kodzie Pythona można je utworzyć dekorując funkcje Pythona za pomocą tff.federated_computation i definiując argument.

    Oto przykład wyrażenia lambda, o którym już wspominaliśmy:

    @tff.federated_computation(tff.FederatedType(np.float32, tff.CLIENTS))
    def get_average_temperature(sensor_readings):
      return tff.federated_mean(sensor_readings)
    
  • Literały rozmieszczenia . Na razie tylko tff.SERVER i tff.CLIENTS umożliwiają definiowanie prostych obliczeń klient-serwer.

  • Wywołania funkcji ( __call__ ). Wszystko, co ma typ funkcjonalny, można wywołać przy użyciu standardowej składni Pythona __call__ . Wywołanie jest wyrażeniem, którego typ jest taki sam jak typ wyniku wywoływanej funkcji.

    Na przykład:

    • add_up_integers(x) reprezentuje wywołanie obliczeń TensorFlow zdefiniowanych wcześniej dla argumentu x . Typ tego wyrażenia to int32 .

    • tff.federated_mean(sensor_readings) reprezentuje wywołanie stowarzyszonego operatora uśredniania na sensor_readings . Typ tego wyrażenia to float32@SERVER (zakładając kontekst z powyższego przykładu).

  • Tworzenie krotek i wybieranie ich elementów. Wyrażenia Pythona w postaci [x, y] , x[y] lub xy , które pojawiają się w treściach funkcji ozdobionych tff.federated_computation .