Algoritma Federasi Kustom, Bagian 1: Pengantar Inti Federasi

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Tutorial ini adalah bagian pertama dari serangkaian dua bagian yang menunjukkan bagaimana menerapkan jenis kustom dari algoritma federasi di TensorFlow Federasi (TFF) menggunakan Federated Inti (FC) - satu set antarmuka tingkat rendah yang berfungsi sebagai dasar atas mana kami telah menerapkan Federasi Learning (FL) lapisan.

Bagian pertama ini lebih konseptual; kami memperkenalkan beberapa konsep utama dan abstraksi pemrograman yang digunakan dalam TFF, dan kami mendemonstrasikan penggunaannya pada contoh yang sangat sederhana dengan array sensor suhu terdistribusi. Pada bagian kedua dari seri ini , kita menggunakan mekanisme kami memperkenalkan sini untuk mengimplementasikan versi sederhana dari algoritma pelatihan dan evaluasi federasi. Sebagai tindak lanjut, kami mendorong Anda untuk mempelajari pelaksanaan dari rata-rata federasi di tff.learning .

Pada akhir seri ini, Anda seharusnya dapat mengenali bahwa aplikasi Federated Core tidak selalu terbatas pada pembelajaran. Abstraksi pemrograman yang kami tawarkan cukup umum, dan dapat digunakan, misalnya, untuk mengimplementasikan analitik dan jenis perhitungan khusus lainnya pada data terdistribusi.

Meskipun tutorial ini dirancang untuk menjadi mandiri, kami mendorong Anda untuk tutorial membaca pertama pada klasifikasi citra dan generasi teks untuk tingkat yang lebih tinggi dan pengenalan lebih lembut untuk kerangka TensorFlow Federated dan Federated Belajar API ( tff.learning ), sebagai itu akan membantu Anda menempatkan konsep yang kami jelaskan di sini dalam konteks.

Tujuan Penggunaan

Singkatnya, Federated Inti (FC) adalah lingkungan pengembangan yang memungkinkan untuk kompak mengungkapkan logika program yang menggabungkan TensorFlow kode dengan operator didistribusikan komunikasi, seperti yang digunakan dalam Federasi Averaging - komputasi jumlah didistribusikan, rata-rata, dan jenis lainnya agregasi terdistribusi melalui seperangkat perangkat klien dalam sistem, model dan parameter penyiaran ke perangkat tersebut, dll.

Anda mungkin menyadari tf.contrib.distribute , dan pertanyaan alami untuk bertanya pada saat ini mungkin: dengan cara apa kerangka ini berbeda? Bagaimanapun, kedua kerangka berusaha membuat komputasi TensorFlow terdistribusi.

Salah satu cara untuk berpikir tentang itu adalah bahwa, sedangkan tujuan lain dari tf.contrib.distribute adalah untuk memungkinkan pengguna untuk menggunakan model yang ada dan kode pelatihan dengan perubahan minimal untuk mengaktifkan pelatihan didistribusikan, dan banyak fokus adalah bagaimana untuk mengambil keuntungan dari infrastruktur didistribusikan untuk membuat kode pelatihan yang ada lebih efisien, tujuan dari TFF's Federated Core adalah untuk memberikan peneliti dan praktisi kontrol eksplisit atas pola spesifik dari komunikasi terdistribusi yang akan mereka gunakan dalam sistem mereka. Fokus di FC adalah pada penyediaan bahasa yang fleksibel dan dapat diperluas untuk mengekspresikan algoritme aliran data terdistribusi, daripada seperangkat kemampuan pelatihan terdistribusi yang diimplementasikan.

Salah satu audiens target utama untuk FC API TFF adalah peneliti dan praktisi yang mungkin ingin bereksperimen dengan algoritme pembelajaran gabungan baru dan mengevaluasi konsekuensi dari pilihan desain halus yang memengaruhi cara aliran data dalam sistem terdistribusi diatur, namun tanpa terhambat oleh detail implementasi sistem. Tingkat abstraksi yang dituju oleh FC API secara kasar sesuai dengan pseudocode yang dapat digunakan untuk menggambarkan mekanisme algoritma pembelajaran gabungan dalam publikasi penelitian - data apa yang ada dalam sistem dan bagaimana transformasinya, tetapi tanpa turun ke tingkat pertukaran pesan jaringan point-to-point individu.

TFF secara keseluruhan menargetkan skenario di mana data didistribusikan, dan harus tetap seperti itu, misalnya, untuk alasan privasi, dan di mana pengumpulan semua data di lokasi terpusat mungkin bukan pilihan yang layak. Hal ini berimplikasi pada penerapan algoritme pembelajaran mesin yang memerlukan tingkat kontrol eksplisit yang lebih tinggi, dibandingkan dengan skenario di mana semua data dapat diakumulasikan di lokasi terpusat di pusat data.

Sebelum kita mulai

Sebelum kita menyelami kodenya, coba jalankan contoh "Hello World" berikut untuk memastikan lingkungan Anda telah diatur dengan benar. Jika tidak bekerja, silakan merujuk ke Instalasi panduan untuk petunjuk.

!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
@tff.federated_computation
def hello_world():
  return 'Hello, World!'

hello_world()
b'Hello, World!'

Data gabungan

Salah satu fitur yang membedakan dari TFF adalah bahwa hal itu memungkinkan Anda untuk kompak mengungkapkan perhitungan berbasis TensorFlow data federasi. Kami akan menggunakan data Federasi istilah dalam tutorial ini untuk merujuk kepada kumpulan item data host di sekelompok perangkat dalam sistem terdistribusi. Misalnya, aplikasi yang berjalan di perangkat seluler dapat mengumpulkan data dan menyimpannya secara lokal, tanpa mengunggah ke lokasi terpusat. Atau, serangkaian sensor terdistribusi dapat mengumpulkan dan menyimpan pembacaan suhu di lokasinya.

Data federasi seperti yang di contoh di atas diperlakukan di TFF sebagai warga negara kelas satu , yaitu, mereka mungkin muncul sebagai parameter dan hasil dari fungsi, dan mereka memiliki jenis. Untuk memperkuat gagasan ini, kita akan mengacu pada data set federasi sebagai nilai-nilai federasi, atau sebagai nilai-nilai jenis federasi.

Poin penting untuk dipahami adalah bahwa kami memodelkan seluruh koleksi item data di semua perangkat (misalnya, seluruh pembacaan suhu koleksi dari semua sensor dalam array terdistribusi) sebagai nilai gabungan tunggal.

Sebagai contoh, berikut adalah cara satu akan mendefinisikan di TFF jenis pelampung federasi diselenggarakan oleh sekelompok perangkat klien. Kumpulan pembacaan suhu yang terwujud di berbagai sensor terdistribusi dapat dimodelkan sebagai nilai dari tipe gabungan ini.

federated_float_on_clients = tff.type_at_clients(tf.float32)

Lebih umum, jenis federasi di TFF didefinisikan dengan menentukan jenis T dari konstituen anggotanya - item data yang berada pada perangkat individu, dan kelompok G perangkat yang nilai federasi jenis ini di-host (ditambah sepertiga, sedikit informasi opsional yang akan kami sebutkan segera). Kami mengacu pada kelompok G perangkat hosting nilai federasi sebagai penempatan nilai ini. Dengan demikian, tff.CLIENTS adalah contoh penempatan.

str(federated_float_on_clients.member)
'float32'
str(federated_float_on_clients.placement)
'CLIENTS'

Jenis Federasi dengan konstituen anggota T dan penempatan G dapat direpresentasikan kompak sebagai {T}@G , seperti yang ditunjukkan di bawah ini.

str(federated_float_on_clients)
'{float32}@CLIENTS'

Kurung kurawal {} di ringkas ini notasi berfungsi sebagai pengingat bahwa konstituen anggota (item data pada perangkat yang berbeda) mungkin berbeda, seperti yang Anda harapkan misalnya, dari pembacaan sensor suhu, sehingga klien sebagai kelompok secara bersama-sama hosting multi- -Mengatur dari T item yang bersama-sama merupakan nilai federasi -typed.

Hal ini penting untuk dicatat bahwa konstituen anggota dari nilai Federasi umumnya buram untuk programmer, yaitu, nilai federasi tidak harus dianggap sebagai sederhana dict mengetik dengan identifier dari perangkat dalam sistem - nilai-nilai ini dimaksudkan untuk secara kolektif diubah hanya oleh operator federasi yang abstrak mewakili berbagai macam protokol komunikasi didistribusikan (seperti agregasi). Jika ini terdengar terlalu abstrak, jangan khawatir - kami akan segera kembali ke sini, dan kami akan mengilustrasikannya dengan contoh nyata.

Tipe federasi di TFF hadir dalam dua rasa: tipe di mana konstituen anggota dari nilai federasi mungkin berbeda (seperti yang baru saja terlihat di atas), dan tipe yang diketahui semuanya sama. Ini dikendalikan oleh ketiga, opsional all_equal parameter dalam tff.FederatedType konstruktor (default ke False ).

federated_float_on_clients.all_equal
False

Jenis Federasi dengan penempatan G di mana semua T -typed konstituen anggota diketahui sama dapat kompak direpresentasikan sebagai T@G (sebagai lawan {T}@G , yaitu, dengan kurung kurawal turun untuk mencerminkan fakta bahwa multi-set konstituen anggota terdiri dari satu item).

str(tff.type_at_clients(tf.float32, all_equal=True))
'float32@CLIENTS'

Salah satu contoh nilai gabungan dari jenis tersebut yang mungkin muncul dalam skenario praktis adalah hyperparameter (seperti kecepatan belajar, norma kliping, dll.) yang telah disiarkan oleh server ke sekelompok perangkat yang berpartisipasi dalam pelatihan gabungan.

Contoh lain adalah seperangkat parameter untuk model pembelajaran mesin yang telah dilatih sebelumnya di server, yang kemudian disiarkan ke sekelompok perangkat klien, di mana parameter tersebut dapat dipersonalisasi untuk setiap pengguna.

Sebagai contoh, misalkan kita memiliki sepasang float32 parameter a dan b untuk model regresi linier sederhana satu dimensi. Kita dapat membangun tipe (non-federasi) model tersebut untuk digunakan dalam TFF sebagai berikut. Kawat gigi sudut <> di tipe string dicetak adalah notasi TFF kompak untuk bernama atau tidak disebutkan namanya tupel.

simple_regression_model_type = (
    tff.StructType([('a', tf.float32), ('b', tf.float32)]))

str(simple_regression_model_type)
'<a=float32,b=float32>'

Perhatikan bahwa kita hanya menentukan dtype s di atas. Jenis non-skalar juga didukung. Dalam kode di atas, tf.float32 adalah notasi jalan pintas untuk lebih umum tff.TensorType(dtype=tf.float32, shape=[]) .

Ketika model ini disiarkan ke klien, jenis nilai gabungan yang dihasilkan dapat direpresentasikan seperti yang ditunjukkan di bawah ini.

str(tff.type_at_clients(
    simple_regression_model_type, all_equal=True))
'<a=float32,b=float32>@CLIENTS'

Per simetri dengan mengapung Federasi di atas, kita akan merujuk pada jenis seperti tupel federasi. Lebih umum, kita akan sering menggunakan XYZ Federasi istilah untuk merujuk ke nilai federasi di mana konstituen anggota yang XYZ -seperti. Dengan demikian, kita akan berbicara tentang hal-hal seperti tupel federasi, urutan federasi, model federasi, dan sebagainya.

Sekarang, kembali ke datang float32@CLIENTS - sementara itu muncul direplikasi di beberapa perangkat, itu sebenarnya satu float32 , karena semua anggota adalah sama. Secara umum, Anda mungkin berpikir dari setiap jenis federasi semua-sama, yaitu, salah satu bentuk T@G , sebagai isomorfik untuk tipe non-federasi T , karena dalam kedua kasus, ada sebenarnya hanya satu (meskipun berpotensi direplikasi) Item tipe T .

Mengingat isomorfisma antara T dan T@G , Anda mungkin bertanya-tanya apa tujuan, jika ada, jenis terakhir ini mungkin melayani. Baca terus.

Penempatan

Ikhtisar Desain

Pada bagian sebelumnya, kita telah memperkenalkan konsep penempatan - kelompok peserta sistem yang mungkin bersama-sama hosting nilai federasi, dan kami telah menunjukkan penggunaan tff.CLIENTS sebagai contoh spesifikasi penempatan.

Untuk menjelaskan mengapa gagasan penempatan begitu mendasar bahwa kami perlu untuk memasukkan ke dalam sistem jenis TFF, mengingat apa yang kami sebutkan di awal tutorial ini tentang beberapa penggunaan yang dimaksudkan dari TFF.

Meskipun dalam tutorial ini, Anda hanya akan melihat kode TFF dieksekusi secara lokal dalam lingkungan simulasi, tujuan kami adalah agar TFF memungkinkan penulisan kode yang dapat Anda terapkan untuk dieksekusi pada grup perangkat fisik dalam sistem terdistribusi, yang berpotensi mencakup perangkat seluler atau tertanam menjalankan Android. Masing-masing perangkat tersebut akan menerima serangkaian instruksi terpisah untuk dieksekusi secara lokal, tergantung pada peran yang dimainkannya dalam sistem (perangkat pengguna akhir, koordinator terpusat, lapisan perantara dalam arsitektur multi-tingkat, dll.). Penting untuk dapat menjelaskan tentang subset perangkat mana yang mengeksekusi kode apa, dan di mana bagian data yang berbeda mungkin terwujud secara fisik.

Ini sangat penting ketika berhadapan dengan, misalnya, data aplikasi pada perangkat seluler. Karena data bersifat pribadi dan dapat sensitif, kami memerlukan kemampuan untuk memverifikasi secara statis bahwa data ini tidak akan pernah meninggalkan perangkat (dan membuktikan fakta tentang bagaimana data diproses). Spesifikasi penempatan adalah salah satu mekanisme yang dirancang untuk mendukung hal ini.

TFF telah dirancang sebagai lingkungan pemrograman data-centric, dan dengan demikian, tidak seperti beberapa kerangka kerja yang ada yang fokus pada operasi dan di mana operasi-operasi mungkin lari, TFF berfokus pada data, di mana itu terwujud data, dan bagaimana itu sedang berubah. Akibatnya, penempatan dimodelkan sebagai properti data di TFF, bukan sebagai properti operasi pada data. Memang, seperti yang akan Anda lihat di bagian berikutnya, beberapa operasi TFF menjangkau seluruh lokasi, dan dijalankan "dalam jaringan", sehingga bisa dikatakan, daripada dieksekusi oleh satu mesin atau sekelompok mesin.

Mewakili jenis nilai tertentu sebagai T@G atau {T}@G (sebagai lawan hanya T ) membuat keputusan penempatan data eksplisit, dan bersama-sama dengan analisis statis dari program yang ditulis dalam TFF, dapat berfungsi sebagai dasar untuk memberikan jaminan privasi formal untuk data sensitif di perangkat.

Sebuah hal penting untuk catatan pada titik ini, bagaimanapun, adalah bahwa sementara kita mendorong pengguna TFF menjadi eksplisit tentang kelompok perangkat yang berpartisipasi bahwa host data (penempatan), programmer tidak akan pernah berurusan dengan data mentah atau identitas dari masing-masing peserta .

Dalam tubuh kode TFF, dengan desain, tidak ada cara untuk menghitung perangkat yang merupakan kelompok diwakili oleh tff.CLIENTS , atau probe keberadaan perangkat tertentu dalam kelompok. Tidak ada konsep perangkat atau identitas klien di mana pun di Federated Core API, kumpulan abstraksi arsitektur yang mendasari, atau infrastruktur runtime inti yang kami sediakan untuk mendukung simulasi. Semua logika komputasi yang Anda tulis akan diekspresikan sebagai operasi pada seluruh grup klien.

Ingat di sini apa yang kita sebutkan sebelumnya tentang nilai-nilai dari jenis Federasi menjadi seperti Python dict , di salah satu yang tidak bisa hanya menghitung konstituen anggota mereka. Pikirkan nilai yang dimanipulasi oleh logika program TFF Anda sebagai terkait dengan penempatan (grup), bukan dengan peserta individu.

Penempatan dirancang untuk menjadi warga negara kelas satu di TFF juga, dan dapat muncul sebagai parameter dan hasil dari placement jenis (untuk diwakili oleh tff.PlacementType dalam API). Di masa mendatang, kami berencana untuk menyediakan berbagai operator untuk mengubah atau menggabungkan penempatan, tetapi ini di luar cakupan tutorial ini. Untuk saat ini, itu sudah cukup untuk memikirkan placement sebagai buram primitif built-in ketik TFF, mirip dengan bagaimana int dan bool yang buram built-in jenis Python, dengan tff.CLIENTS menjadi literal konstan jenis ini, tidak seperti 1 menjadi literal konstan tipe int .

Menentukan Penempatan

TFF menyediakan dua literal penempatan dasar, tff.CLIENTS dan tff.SERVER , untuk membuatnya mudah untuk mengekspresikan kaya berbagai skenario praktis yang secara alami dimodelkan sebagai arsitektur client-server, dengan beberapa perangkat klien (ponsel, perangkat embedded, database terdistribusi , sensor, dll) diatur oleh koordinator server tunggal terpusat. TFF dirancang untuk juga mendukung penempatan kustom, beberapa grup klien, multi-tier dan arsitektur terdistribusi lainnya yang lebih umum, tetapi membahasnya di luar cakupan tutorial ini.

TFF tidak menetapkan apa yang baik tff.CLIENTS atau tff.SERVER benar-benar mewakili.

Secara khusus, tff.SERVER mungkin perangkat fisik tunggal (anggota dari kelompok tunggal), tapi itu hanya mungkin juga menjadi kelompok replika dalam keadaan mesin replikasi toleran klaster berjalan - kami tidak membuat arsitektur khusus asumsi. Sebaliknya, kita menggunakan all_equal bit disebutkan dalam bagian sebelumnya untuk mengungkapkan fakta bahwa kita umumnya hanya berurusan dengan satu item data pada server.

Demikian juga, tff.CLIENTS dalam beberapa aplikasi mungkin mewakili semua klien dalam sistem - apa dalam konteks pembelajaran Federasi kadang-kadang kita sebut sebagai penduduk, tetapi misalnya, dalam implementasi produksi Federasi Averaging , mungkin mewakili kohort - subset klien yang dipilih untuk berpartisipasi dalam putaran pelatihan tertentu. Penempatan yang didefinisikan secara abstrak diberi makna konkret saat komputasi yang memunculkannya digunakan untuk eksekusi (atau hanya dipanggil seperti fungsi Python dalam lingkungan simulasi, seperti yang ditunjukkan dalam tutorial ini). Dalam simulasi lokal kami, kelompok klien ditentukan oleh data gabungan yang diberikan sebagai masukan.

Komputasi gabungan

Mendeklarasikan komputasi gabungan

TFF dirancang sebagai lingkungan pemrograman fungsional bertipe kuat yang mendukung pengembangan modular.

Unit dasar dari komposisi di TFF merupakan perhitungan Federasi - bagian dari logika yang dapat menerima nilai-nilai federasi sebagai masukan dan kembali nilai-nilai federasi sebagai output. Inilah cara Anda dapat menentukan penghitungan yang menghitung rata-rata suhu yang dilaporkan oleh larik sensor dari contoh kita sebelumnya.

@tff.federated_computation(tff.type_at_clients(tf.float32))
def get_average_temperature(sensor_readings):
  return tff.federated_mean(sensor_readings)

Melihat kode di atas, pada titik ini Anda mungkin bertanya - tidak ada sudah dekorator konstruksi untuk mendefinisikan unit composable seperti tf.function di TensorFlow, dan jika demikian, mengapa memperkenalkan satu lagi, dan bagaimana hal itu berbeda?

Jawaban singkatnya adalah bahwa kode yang dihasilkan oleh tff.federated_computation pembungkus bukanlah TensorFlow, juga bukan Python - itu adalah spesifikasi dari sistem terdistribusi dalam bahasa lem platform-independen internal. Pada titik ini, ini tidak diragukan lagi akan terdengar samar, tetapi harap ingat interpretasi intuitif dari perhitungan gabungan ini sebagai spesifikasi abstrak dari sistem terdistribusi. Kami akan menjelaskannya sebentar lagi.

Pertama, mari kita bermain dengan definisi sedikit. Perhitungan TFF umumnya dimodelkan sebagai fungsi - dengan atau tanpa parameter, tetapi dengan tanda tangan tipe yang terdefinisi dengan baik. Anda dapat mencetak tanda tangan jenis perhitungan dengan query yang type_signature properti, seperti yang ditunjukkan di bawah ini.

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

Tanda tangan tipe memberi tahu kita bahwa komputasi menerima kumpulan pembacaan sensor yang berbeda pada perangkat klien, dan mengembalikan rata-rata tunggal di server.

Sebelum kita melangkah lebih jauh, mari kita merenungkan ini selama satu menit - input dan output dari perhitungan ini berada di tempat yang berbeda (di CLIENTS vs di SERVER ). Ingat apa yang kita katakan di bagian sebelumnya pada penempatan tentang bagaimana operasi TFF mungkin span di lokasi, dan berjalan di jaringan, dan apa yang kita hanya berkata tentang perhitungan federasi sebagai mewakili spesifikasi abstrak dari sistem terdistribusi. Kami baru saja menetapkan satu perhitungan seperti itu - sistem terdistribusi sederhana di mana data dikonsumsi di perangkat klien, dan hasil agregat muncul di server.

Dalam banyak skenario praktis, perhitungan yang mewakili tugas tingkat atas akan cenderung menerima masukan mereka dan melaporkan output mereka di server - ini mencerminkan gagasan bahwa perhitungan mungkin dipicu oleh permintaan yang berasal dan berakhir pada server.

Namun, FC API tidak memaksakan asumsi ini, dan banyak dari blok bangunan yang kami gunakan secara internal (termasuk banyak tff.federated_... operator Anda mungkin menemukan dalam API) memiliki input dan output dengan penempatan yang berbeda, sehingga secara umum, Anda harus tidak berpikir tentang perhitungan federasi sebagai sesuatu yang berjalan pada server atau dijalankan oleh server. Server hanyalah salah satu jenis peserta dalam perhitungan gabungan. Dalam memikirkan mekanisme perhitungan seperti itu, yang terbaik adalah selalu default ke perspektif jaringan global, daripada perspektif koordinator terpusat tunggal.

Secara umum, jenis tanda tangan fungsional kompak direpresentasikan sebagai (T -> U) untuk jenis T dan U input dan output, masing-masing. Jenis parameter formal (seperti sensor_readings dalam hal ini) ditetapkan sebagai argumen untuk dekorator. Anda tidak perlu menentukan jenis hasil - itu ditentukan secara otomatis.

Meskipun TFF memang menawarkan bentuk polimorfisme terbatas, pemrogram sangat dianjurkan untuk secara eksplisit tentang jenis data yang mereka gunakan, karena itu membuat pemahaman, debugging, dan secara formal memverifikasi properti kode Anda lebih mudah. Dalam beberapa kasus, secara eksplisit menentukan jenis adalah persyaratan (misalnya, perhitungan polimorfik saat ini tidak langsung dieksekusi).

Menjalankan komputasi gabungan

Untuk mendukung pengembangan dan debugging, TFF memungkinkan Anda untuk secara langsung menjalankan komputasi yang didefinisikan dengan cara ini sebagai fungsi Python, seperti yang ditunjukkan di bawah ini. Dimana perhitungan mengharapkan nilai tipe Federasi dengan all_equal bit set ke False , Anda dapat memberi makan sebagai polos list di Python, dan untuk jenis federasi dengan all_equal bit set ke True , Anda hanya bisa langsung makan (single) konstituen anggota. Ini juga bagaimana hasilnya dilaporkan kembali kepada Anda.

get_average_temperature([68.5, 70.3, 69.8])
69.53334

Saat menjalankan komputasi seperti ini dalam mode simulasi, Anda bertindak sebagai pengamat eksternal dengan pandangan seluruh sistem, yang memiliki kemampuan untuk memasok input dan mengonsumsi output di lokasi mana pun dalam jaringan, seperti yang terjadi di sini - Anda memberikan nilai klien pada input, dan mengkonsumsi hasil server.

Sekarang, mari kita kembali ke catatan yang kita buat sebelumnya tentang tff.federated_computation dekorator memancarkan kode dalam bahasa lem. Meskipun logika perhitungan TFF dapat dinyatakan sebagai fungsi biasa dalam Python (Anda hanya perlu untuk menghias mereka dengan tff.federated_computation seperti yang kita lakukan di atas), dan Anda dapat langsung memanggil mereka dengan argumen Python sama seperti fungsi Python lain dalam hal ini notebook, di belakang layar, seperti yang kita catat sebelumnya, perhitungan TFF sebenarnya tidak Python.

Yang kami maksud dengan ini adalah bahwa ketika interpreter Python bertemu fungsi dihiasi dengan tff.federated_computation , ini menelusuri laporan dalam tubuh fungsi ini sekali (pada saat definisi), dan kemudian membangun sebuah representasi serial logika perhitungan untuk digunakan di masa depan - apakah untuk dieksekusi, atau untuk dimasukkan sebagai sub-komponen ke dalam perhitungan lain.

Anda dapat memverifikasi ini dengan menambahkan pernyataan cetak, sebagai berikut:

@tff.federated_computation(tff.type_at_clients(tf.float32))
def get_average_temperature(sensor_readings):

  print ('Getting traced, the argument is "{}".'.format(
      type(sensor_readings).__name__))

  return tff.federated_mean(sensor_readings)
Getting traced, the argument is "ValueImpl".

Anda dapat memikirkan kode Python yang mendefinisikan komputasi gabungan serupa dengan cara Anda memikirkan kode Python yang membangun grafik TensorFlow dalam konteks yang tidak bersemangat (jika Anda tidak terbiasa dengan penggunaan TensorFlow yang tidak bersemangat, pikirkan tentang Anda Kode Python mendefinisikan grafik operasi yang akan dieksekusi nanti, tetapi tidak benar-benar menjalankannya dengan cepat). Kode pembuatan grafik yang tidak bersemangat di TensorFlow adalah Python, tetapi grafik TensorFlow yang dibuat oleh kode ini tidak bergantung pada platform dan dapat dibuat serial.

Demikian juga, TFF perhitungan didefinisikan dalam Python, tapi laporan Python di tubuh mereka, seperti tff.federated_mean dalam contoh weve hanya ditampilkan, dikompilasi menjadi representasi serializable portabel dan platform-independen di bawah tenda.

Sebagai pengembang, Anda tidak perlu khawatir dengan detail representasi ini, karena Anda tidak perlu bekerja dengannya secara langsung, tetapi Anda harus menyadari keberadaannya, fakta bahwa perhitungan TFF pada dasarnya tidak bersemangat, dan tidak dapat menangkap status Python sewenang-wenang. Kode python yang terkandung dalam tubuh TFF perhitungan ini dijalankan pada saat definisi, ketika tubuh fungsi python dihiasi dengan tff.federated_computation ditelusuri sebelum mendapatkan serial. Itu tidak dilacak lagi pada waktu pemanggilan (kecuali ketika fungsinya polimorfik; silakan merujuk ke halaman dokumentasi untuk detailnya).

Anda mungkin bertanya-tanya mengapa kami memilih untuk memperkenalkan representasi non-Python internal khusus. Salah satu alasannya adalah bahwa pada akhirnya, perhitungan TFF dimaksudkan untuk dapat diterapkan ke lingkungan fisik nyata, dan dihosting di perangkat seluler atau yang disematkan, di mana Python mungkin tidak tersedia.

Alasan lain adalah bahwa perhitungan TFF mengekspresikan perilaku global sistem terdistribusi, berbeda dengan program Python yang mengekspresikan perilaku lokal masing-masing peserta. Anda dapat melihat bahwa dalam contoh sederhana di atas, dengan operator khusus tff.federated_mean yang menerima data pada perangkat klien, tapi deposito hasil pada server.

Operator tff.federated_mean tidak dapat dengan mudah dimodelkan sebagai operator biasa di Python, karena tidak mengeksekusi secara lokal - seperti disebutkan sebelumnya, itu merupakan sistem terdistribusi yang koordinat perilaku beberapa peserta sistem. Kita akan mengacu ke operator seperti operator federasi, untuk membedakan mereka dari biasa operator (lokal) di Python.

Sistem tipe TFF, dan rangkaian operasi dasar yang didukung dalam bahasa TFF, dengan demikian menyimpang secara signifikan dari yang ada di Python, yang memerlukan penggunaan representasi khusus.

Menyusun perhitungan gabungan

Seperti disebutkan di atas, komputasi gabungan dan konstituennya paling baik dipahami sebagai model sistem terdistribusi, dan Anda dapat menganggap menyusun komputasi gabungan sebagai menyusun sistem terdistribusi yang lebih kompleks dari yang lebih sederhana. Anda dapat berpikir tentang tff.federated_mean operator sebagai semacam built-in template yang perhitungan federasi dengan tanda tangan jenis ({T}@CLIENTS -> T@SERVER) (memang, seperti perhitungan Anda menulis, operator ini juga memiliki kompleks struktur - di bawah tenda kami memecahnya menjadi operator yang lebih sederhana).

Hal yang sama berlaku untuk menyusun perhitungan gabungan. Perhitungan get_average_temperature dapat dipanggil dalam tubuh fungsi Python lain dihiasi dengan tff.federated_computation - hal tersebut akan menyebabkan itu tertanam dalam tubuh induk, banyak cara yang sama tff.federated_mean tertanam dalam tubuh sendiri sebelumnya.

Pembatasan penting untuk menyadari adalah bahwa tubuh fungsi Python dihiasi dengan tff.federated_computation harus terdiri hanya dari operator federasi, yaitu, mereka tidak dapat secara langsung mengandung operasi TensorFlow. Misalnya, Anda tidak bisa langsung menggunakan tf.nest antarmuka untuk menambah sepasang nilai federasi. Kode TensorFlow harus terbatas pada blok kode dihiasi dengan tff.tf_computation dibahas dalam bagian berikut. Hanya ketika dibungkus dengan cara ini dapat kode TensorFlow dibungkus dipanggil dalam tubuh seorang tff.federated_computation .

Alasan untuk pemisahan ini adalah teknis (sulit untuk mengelabui operator seperti tf.add bekerja dengan non-tensor) serta arsitektur. Bahasa perhitungan federasi (yaitu, logika yang dibangun dari tubuh serial fungsi Python dihiasi dengan tff.federated_computation ) dirancang untuk melayani sebagai bahasa lem platform-independen. Bahasa lem ini saat ini digunakan untuk membangun sistem terdistribusi dari bagian tertanam kode TensorFlow (terbatas tff.tf_computation blok). Dalam kepenuhan waktu, kami mengantisipasi kebutuhan untuk bagian menanamkan dari lainnya, logika non-TensorFlow, seperti query database relasional yang mungkin mewakili pipa input, semua terhubung bersama-sama menggunakan bahasa lem sama ( tff.federated_computation blok).

Logika TensorFlow

Mendeklarasikan komputasi TensorFlow

TFF dirancang untuk digunakan dengan TensorFlow. Dengan demikian, sebagian besar kode yang akan Anda tulis dalam TFF kemungkinan adalah kode TensorFlow biasa (yaitu, yang dijalankan secara lokal). Untuk menggunakan kode tersebut dengan TFF, seperti disebutkan di atas, hanya perlu dihiasi dengan tff.tf_computation .

Sebagai contoh, berikut adalah cara kita bisa menerapkan fungsi yang mengambil nomor dan menambahkan 0.5 untuk itu.

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

Sekali lagi, melihat ini, Anda mungkin bertanya-tanya mengapa kita harus mendefinisikan dekorator lain tff.tf_computation bukan hanya menggunakan mekanisme yang sudah ada seperti tf.function . Tidak seperti di bagian sebelumnya, di sini kita berurusan dengan blok biasa dari kode TensorFlow.

Ada beberapa alasan untuk ini, perawatan lengkap yang melampaui cakupan tutorial ini, tetapi ada baiknya menyebutkan yang utama:

  • Untuk menyematkan blok penyusun yang dapat digunakan kembali yang diimplementasikan menggunakan kode TensorFlow di badan komputasi gabungan, blok penyusun tersebut harus memenuhi properti tertentu - seperti dilacak dan diserialisasikan pada waktu definisi, memiliki tanda tangan tipe, dll. Ini umumnya memerlukan beberapa bentuk dekorator.

Secara umum, kami sarankan menggunakan mekanisme asli TensorFlow untuk komposisi, seperti tf.function , sedapat mungkin, sebagai cara yang tepat di mana berinteraksi dekorator TFF dengan fungsi bersemangat bisa diharapkan untuk berkembang.

Sekarang, kembali ke kode contoh potongan di atas, perhitungan add_half kita hanya didefinisikan dapat diobati oleh TFF sama seperti perhitungan TFF lainnya. Secara khusus, ia memiliki tanda tangan tipe TFF.

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

Perhatikan tanda tangan jenis ini tidak memiliki penempatan. Komputasi TensorFlow tidak dapat menggunakan atau mengembalikan tipe federasi.

Anda sekarang dapat juga menggunakan add_half sebagai sebuah blok bangunan dalam perhitungan lainnya. Sebagai contoh, berikut adalah cara Anda dapat menggunakan tff.federated_map operator untuk menerapkan add_half pointwise ke semua konstituen anggota pelampung Federasi pada perangkat klien.

@tff.federated_computation(tff.type_at_clients(tf.float32))
def add_half_on_clients(x):
  return tff.federated_map(add_half, x)
str(add_half_on_clients.type_signature)
'({float32}@CLIENTS -> {float32}@CLIENTS)'

Menjalankan komputasi TensorFlow

Pelaksanaan perhitungan didefinisikan dengan tff.tf_computation mengikuti aturan yang sama seperti yang kita dijelaskan untuk tff.federated_computation . Mereka dapat dipanggil sebagai callable biasa dengan Python, sebagai berikut.

add_half_on_clients([1.0, 3.0, 2.0])
[<tf.Tensor: shape=(), dtype=float32, numpy=1.5>,
 <tf.Tensor: shape=(), dtype=float32, numpy=3.5>,
 <tf.Tensor: shape=(), dtype=float32, numpy=2.5>]

Sekali lagi, perlu dicatat bahwa menyerukan perhitungan add_half_on_clients dengan cara ini mensimulasikan proses didistribusikan. Data dikonsumsi pada klien, dan dikembalikan pada klien. Memang, perhitungan ini membuat setiap klien melakukan tindakan lokal. Tidak ada tff.SERVER disebutkan secara eksplisit dalam sistem ini (bahkan jika dalam prakteknya, mendalangi pemrosesan tersebut mungkin melibatkan satu). Pikirkan perhitungan didefinisikan dengan cara ini sebagai konseptual analog dengan Map panggung MapReduce .

Juga, perlu diingat bahwa apa yang kita katakan di bagian sebelumnya tentang TFF perhitungan mendapatkan serial pada saat definisi tetap benar untuk tff.tf_computation kode juga - tubuh Python dari add_half_on_clients akan ditelusuri sekali pada waktu definisi. Pada pemanggilan berikutnya, TFF menggunakan representasi serialnya.

Satu-satunya perbedaan antara metode Python dihiasi dengan tff.federated_computation dan mereka dihiasi dengan tff.tf_computation adalah bahwa yang terakhir adalah serial sebagai grafik TensorFlow (sedangkan mantan tidak diperbolehkan untuk mengandung kode TensorFlow tertanam langsung di dalamnya).

Di bawah tenda, masing-masing metode dihiasi dengan tff.tf_computation sementara menonaktifkan eksekusi bersemangat untuk memungkinkan struktur perhitungan untuk ditangkap. Meskipun eksekusi bersemangat dinonaktifkan secara lokal, Anda dipersilakan untuk menggunakan konstruksi TensorFlow, AutoGraph, TensorFlow 2.0, dll., selama Anda menulis logika komputasi Anda sedemikian rupa sehingga dapat diserialisasi dengan benar.

Misalnya, kode berikut akan gagal:

try:

  # Eager mode
  constant_10 = tf.constant(10.)

  @tff.tf_computation(tf.float32)
  def add_ten(x):
    return x + constant_10

except Exception as err:
  print (err)
Attempting to capture an EagerTensor without building a function.

Di atas gagal karena constant_10 telah dibangun di luar grafik yang tff.tf_computation konstruksi internal di tubuh add_ten selama proses serialisasi.

Di sisi lain, memanggil fungsi python yang memodifikasi grafik saat saat dipanggil di dalam tff.tf_computation baik-baik saja:

def get_constant_10():
  return tf.constant(10.)

@tff.tf_computation(tf.float32)
def add_ten(x):
  return x + get_constant_10()

add_ten(5.0)
15.0

Perhatikan bahwa mekanisme serialisasi di TensorFlow sedang berkembang, dan kami mengharapkan detail tentang bagaimana TFF membuat serial komputasi untuk berkembang juga.

Bekerja dengan tf.data.Dataset s

Seperti disebutkan sebelumnya, sebuah fitur unik dari tff.tf_computation s adalah bahwa mereka memungkinkan Anda untuk bekerja dengan tf.data.Dataset s didefinisikan secara abstrak sebagai parameter formal dengan kode Anda. Parameter yang akan diwakili dalam TensorFlow sebagai data set harus dideklarasikan menggunakan tff.SequenceType konstruktor.

Sebagai contoh, jenis spesifikasi tff.SequenceType(tf.float32) mendefinisikan urutan abstrak elemen mengapung di TFF. Urutan dapat berisi tensor, atau struktur bersarang yang kompleks (kita akan melihat contohnya nanti). The ringkas representasi dari urutan T -typed item adalah T* .

float32_sequence = tff.SequenceType(tf.float32)

str(float32_sequence)
'float32*'

Suppose that in our temperature sensor example, each sensor holds not just one temperature reading, but multiple. Here's how you can define a TFF computation in TensorFlow that calculates the average of temperatures in a single local data set using the tf.data.Dataset.reduce operator.

@tff.tf_computation(tff.SequenceType(tf.float32))
def get_local_temperature_average(local_temperatures):
  sum_and_count = (
      local_temperatures.reduce((0.0, 0), lambda x, y: (x[0] + y, x[1] + 1)))
  return sum_and_count[0] / tf.cast(sum_and_count[1], tf.float32)
str(get_local_temperature_average.type_signature)
'(float32* -> float32)'

In the body of a method decorated with tff.tf_computation , formal parameters of a TFF sequence type are represented simply as objects that behave like tf.data.Dataset , ie, support the same properties and methods (they are currently not implemented as subclasses of that type - this may change as the support for data sets in TensorFlow evolves).

You can easily verify this as follows.

@tff.tf_computation(tff.SequenceType(tf.int32))
def foo(x):
  return x.reduce(np.int32(0), lambda x, y: x + y)

foo([1, 2, 3])
6

Keep in mind that unlike ordinary tf.data.Dataset s, these dataset-like objects are placeholders. They don't contain any elements, since they represent abstract sequence-typed parameters, to be bound to concrete data when used in a concrete context. Support for abstractly-defined placeholder data sets is still somewhat limited at this point, and in the early days of TFF, you may encounter certain restrictions, but we won't need to worry about them in this tutorial (please refer to the documentation pages for details).

When locally executing a computation that accepts a sequence in a simulation mode, such as in this tutorial, you can feed the sequence as Python list, as below (as well as in other ways, eg, as a tf.data.Dataset in eager mode, but for now, we'll keep it simple).

get_local_temperature_average([68.5, 70.3, 69.8])
69.53333

Like all other TFF types, sequences like those defined above can use the tff.StructType constructor to define nested structures. For example, here's how one could declare a computation that accepts a sequence of pairs A , B , and returns the sum of their products. We include the tracing statements in the body of the computation so that you can see how the TFF type signature translates into the dataset's output_types and output_shapes .

@tff.tf_computation(tff.SequenceType(collections.OrderedDict([('A', tf.int32), ('B', tf.int32)])))
def foo(ds):
  print('element_structure = {}'.format(ds.element_spec))
  return ds.reduce(np.int32(0), lambda total, x: total + x['A'] * x['B'])
element_structure = OrderedDict([('A', TensorSpec(shape=(), dtype=tf.int32, name=None)), ('B', TensorSpec(shape=(), dtype=tf.int32, name=None))])
str(foo.type_signature)
'(<A=int32,B=int32>* -> int32)'
foo([{'A': 2, 'B': 3}, {'A': 4, 'B': 5}])
26

The support for using tf.data.Datasets as formal parameters is still somewhat limited and evolving, although functional in simple scenarios such as those used in this tutorial.

Putting it all together

Now, let's try again to use our TensorFlow computation in a federated setting. Suppose we have a group of sensors that each have a local sequence of temperature readings. We can compute the global temperature average by averaging the sensors' local averages as follows.

@tff.federated_computation(
    tff.type_at_clients(tff.SequenceType(tf.float32)))
def get_global_temperature_average(sensor_readings):
  return tff.federated_mean(
      tff.federated_map(get_local_temperature_average, sensor_readings))

Note that this isn't a simple average across all local temperature readings from all clients, as that would require weighing contributions from different clients by the number of readings they locally maintain. We leave it as an exercise for the reader to update the above code; the tff.federated_mean operator accepts the weight as an optional second argument (expected to be a federated float).

Also note that the input to get_global_temperature_average now becomes a federated float sequence . Federated sequences is how we will typically represent on-device data in federated learning, with sequence elements typically representing data batches (you will see examples of this shortly).

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

Here's how we can locally execute the computation on a sample of data in Python. Notice that the way we supply the input is now as a list of list s. The outer list iterates over the devices in the group represented by tff.CLIENTS , and the inner ones iterate over elements in each device's local sequence.

get_global_temperature_average([[68.0, 70.0], [71.0], [68.0, 72.0, 70.0]])
70.0

This concludes the first part of the tutorial... we encourage you to continue on to the second part .