หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

Custom Federated Algorithms ตอนที่ 1: บทนำสู่ Federated Core

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub

บทช่วยสอนนี้เป็นส่วนแรกของซีรีส์สองส่วนที่สาธิตวิธีใช้อัลกอริทึมแบบรวมที่กำหนดเองใน TensorFlow Federated (TFF) โดยใช้ Federated Core (FC) ซึ่งเป็นชุดของอินเทอร์เฟซระดับล่างที่ทำหน้าที่เป็นพื้นฐาน เราได้ติดตั้งเลเยอร์ Federated Learning (FL)

ส่วนแรกนี้เป็นแนวคิดมากกว่า เรานำเสนอแนวคิดหลักบางส่วนและการเขียนโปรแกรมที่ใช้ใน TFF และเราแสดงให้เห็นถึงการใช้งานในตัวอย่างง่ายๆด้วยเซ็นเซอร์อุณหภูมิแบบกระจาย ใน ส่วนที่สองของชุดนี้ เราใช้กลไกที่เราแนะนำที่นี่เพื่อใช้อัลกอริธึมการฝึกอบรมและการประเมินผลแบบรวมศูนย์แบบง่ายๆ ในการติดตามผลเราขอแนะนำให้คุณศึกษา การใช้งาน ค่าเฉลี่ยแบบรวมศูนย์ใน tff.learning

ในตอนท้ายของซีรีส์นี้คุณจะสามารถรับรู้ได้ว่าแอปพลิเคชันของ Federated Core ไม่จำเป็นต้อง จำกัด เฉพาะการเรียนรู้ นามธรรมการเขียนโปรแกรมที่เรานำเสนอนั้นค่อนข้างทั่วไปและสามารถนำมาใช้เช่นเพื่อใช้การวิเคราะห์และการคำนวณแบบกำหนดเองอื่น ๆ ผ่านข้อมูลแบบกระจาย

แม้ว่าบทช่วยสอนนี้ได้รับการออกแบบมาให้สามารถใช้งานได้ในตัวเราขอแนะนำให้คุณอ่านบทแนะนำเกี่ยวกับการ จัดประเภทรูปภาพ และการ สร้างข้อความก่อน เพื่อการแนะนำในระดับที่สูงขึ้นและอ่อนโยนยิ่งขึ้นสำหรับเฟรมเวิร์ก TensorFlow Federated และ Federated Learning APIs ( tff.learning ) ตาม มันจะช่วยให้คุณใส่แนวคิดที่เราอธิบายไว้ที่นี่ในบริบท

การใช้งานที่ตั้งใจไว้

โดยสรุป Federated Core (FC) เป็นสภาพแวดล้อมการพัฒนาที่ทำให้สามารถแสดงตรรกะของโปรแกรมอย่างกะทัดรัดที่รวมโค้ด TensorFlow กับตัวดำเนินการสื่อสารแบบกระจายเช่นที่ใช้ใน Federated Averaging - คำนวณผลรวมที่กระจายค่าเฉลี่ยและประเภทอื่น ๆ ของการรวมแบบกระจายผ่านชุดอุปกรณ์ไคลเอนต์ในระบบรูปแบบการแพร่ภาพและพารามิเตอร์ไปยังอุปกรณ์เหล่านั้นเป็นต้น

คุณอาจทราบถึง tf.contrib.distribute และคำถามทั่วไปที่จะถาม ณ จุดนี้อาจเป็น: กรอบงานนี้แตกต่างกันอย่างไร? ทั้งสองเฟรมเวิร์กพยายามทำให้การคำนวณแบบ TensorFlow กระจายไป

วิธีหนึ่งที่จะคิดเกี่ยวกับเรื่องนี้ก็คือในขณะที่เป้าหมายที่ระบุไว้ของ tf.contrib.distribute คือ การอนุญาตให้ผู้ใช้ใช้โมเดลและรหัสการฝึกอบรมที่มีอยู่โดยมีการเปลี่ยนแปลงเพียงเล็กน้อยเพื่อเปิดใช้งานการฝึกอบรมแบบกระจาย และมุ่งเน้นไปที่การใช้ประโยชน์จากโครงสร้างพื้นฐานแบบกระจาย เพื่อให้รหัสการฝึกอบรมที่มีอยู่มีประสิทธิภาพมากขึ้นเป้าหมายของ Federated Core ของ TFF คือให้นักวิจัยและผู้ปฏิบัติงานสามารถควบคุมรูปแบบเฉพาะของการสื่อสารแบบกระจายที่พวกเขาจะใช้ในระบบของตนได้อย่างชัดเจน จุดเน้นใน FC อยู่ที่การจัดเตรียมภาษาที่ยืดหยุ่นและขยายได้สำหรับการแสดงอัลกอริธึมการไหลของข้อมูลแบบกระจายแทนที่จะเป็นชุดความสามารถในการฝึกอบรมแบบกระจายที่นำไปใช้อย่างเป็นรูปธรรม

หนึ่งในกลุ่มเป้าหมายหลักสำหรับ FC API ของ TFF คือนักวิจัยและผู้ปฏิบัติงานที่อาจต้องการทดลองใช้อัลกอริธึมการเรียนรู้แบบสหพันธ์ใหม่และประเมินผลที่ตามมาของตัวเลือกการออกแบบที่ละเอียดอ่อนซึ่งส่งผลต่อลักษณะการไหลของข้อมูลในระบบกระจาย โดยไม่ต้องจมอยู่กับรายละเอียดการใช้งานระบบ ระดับของนามธรรมที่ FC API ตั้งเป้าไว้โดยประมาณนั้นสอดคล้องกับรหัสเทียมที่สามารถใช้เพื่ออธิบายกลไกของอัลกอริธึมการเรียนรู้แบบรวมศูนย์ในสิ่งพิมพ์งานวิจัย - ข้อมูลใดที่มีอยู่ในระบบและวิธีการเปลี่ยนแปลง แต่ไม่ลดลงถึงระดับ การแลกเปลี่ยนข้อความเครือข่ายแบบจุดต่อจุดแต่ละรายการ

TFF โดยรวมกำลังกำหนดเป้าหมายสถานการณ์ที่มีการกระจายข้อมูลและต้องคงอยู่เช่นนั้นเช่นเพื่อเหตุผลด้านความเป็นส่วนตัวและในกรณีที่การรวบรวมข้อมูลทั้งหมดไว้ที่ส่วนกลางอาจไม่ใช่ตัวเลือกที่ทำได้ สิ่งนี้มีผลต่อการใช้อัลกอริทึมการเรียนรู้ของเครื่องที่ต้องเพิ่มระดับการควบคุมอย่างชัดเจนเมื่อเทียบกับสถานการณ์ที่ข้อมูลทั้งหมดสามารถสะสมในตำแหน่งส่วนกลางที่ศูนย์ข้อมูลได้

ก่อนที่เราจะเริ่ม

ก่อนที่เราจะเจาะลึกเรื่องโค้ดโปรดลองเรียกใช้ตัวอย่าง "Hello World" ต่อไปนี้เพื่อให้แน่ใจว่าสภาพแวดล้อมของคุณได้รับการตั้งค่าอย่างถูกต้อง หากไม่ได้ผลโปรดดูคำแนะนำในคู่มือการ ติดตั้ง

!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!'

ข้อมูลรวม

คุณสมบัติที่แตกต่างอย่างหนึ่งของ TFF คือช่วยให้คุณสามารถแสดงการคำนวณที่ใช้ TensorFlow บน ข้อมูลแบบรวมศูนย์ได้ อย่างกะทัดรัด เราจะใช้คำว่า federated data ในบทช่วยสอนนี้เพื่ออ้างถึงชุดข้อมูลที่โฮสต์ข้ามกลุ่มอุปกรณ์ในระบบแบบกระจาย ตัวอย่างเช่นแอปพลิเคชันที่ทำงานบนอุปกรณ์เคลื่อนที่อาจรวบรวมข้อมูลและจัดเก็บไว้ในเครื่องโดยไม่ต้องอัปโหลดไปยังตำแหน่งส่วนกลาง หรืออาร์เรย์ของเซ็นเซอร์แบบกระจายอาจรวบรวมและจัดเก็บการอ่านอุณหภูมิไว้ที่ตำแหน่งของมัน

ข้อมูลแบบรวมศูนย์เช่นเดียวกับในตัวอย่างข้างต้นได้รับการปฏิบัติใน TFF ในฐานะ พลเมืองชั้นหนึ่ง กล่าวคืออาจปรากฏเป็นพารามิเตอร์และผลลัพธ์ของฟังก์ชันและมีประเภท เพื่อเสริมสร้างแนวความคิดนี้เราจะอ้างถึงชุดข้อมูล แบบรวม เป็น ค่ารวม หรือเป็น ค่าของประเภทที่รวม ศูนย์

ประเด็นสำคัญที่ต้องเข้าใจคือเรากำลังสร้างแบบจำลองการรวบรวมรายการข้อมูลทั้งหมดในอุปกรณ์ทั้งหมด (เช่นการอ่านค่าอุณหภูมิคอลเลกชันทั้งหมดจากเซ็นเซอร์ทั้งหมดในอาร์เรย์แบบกระจาย) เป็นค่าสหพันธ์เดียว

ตัวอย่างเช่นต่อไปนี้เป็นวิธีที่เราจะกำหนดประเภทของ โฟลตแบบรวม ศูนย์ใน TFF ที่โฮสต์โดยกลุ่มอุปกรณ์ไคลเอนต์ คอลเลกชันของการอ่านค่าอุณหภูมิที่เป็นจริงในอาร์เรย์ของเซ็นเซอร์แบบกระจายสามารถสร้างแบบจำลองเป็นค่าของประเภทสหพันธ์นี้

federated_float_on_clients = tff.type_at_clients(tf.float32)

โดยทั่วไปประเภทรวมใน TFF ถูกกำหนดโดยการระบุประเภท T ของ องค์ประกอบสมาชิก - รายการข้อมูลที่อยู่ในอุปกรณ์แต่ละเครื่องและกลุ่ม G ของอุปกรณ์ที่โฮสต์ค่าแบบรวมของประเภทนี้ (บวกหนึ่งในสาม ข้อมูลเพิ่มเติมที่เราจะกล่าวถึงในไม่ช้า) เราอ้างถึงกลุ่ม G ของอุปกรณ์ที่โฮสต์มูลค่ารวมเป็น ตำแหน่ง ของค่า ดังนั้น tff.CLIENTS จึงเป็นตัวอย่างของตำแหน่ง

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

ประเภทสหพันธ์ที่มีองค์ประกอบสมาชิก T และตำแหน่ง G สามารถแสดงอย่างกะทัดรัดเป็น {T}@G ดังที่แสดงด้านล่าง

str(federated_float_on_clients)
'{float32}@CLIENTS'

วงเล็บปีกกา {} ในรูปแบบย่อนี้ใช้เป็นตัวเตือนว่าองค์ประกอบของสมาชิก (รายการข้อมูลบนอุปกรณ์ต่างๆ) อาจแตกต่างกันตามที่คุณคาดหวังเช่นการอ่านเซ็นเซอร์อุณหภูมิดังนั้นลูกค้าในกลุ่มจะร่วมกันโฮสต์ หลาย - ชุด ของ T -typed รายการที่รวมกันเป็นมูลค่ารวม

สิ่งสำคัญคือต้องสังเกตว่าโดยทั่วไปแล้วองค์ประกอบสมาชิกของค่าที่รวมกันนั้นมักจะทึบแสงสำหรับโปรแกรมเมอร์กล่าวคือไม่ควรคิดว่าค่าที่รวมกันเป็นค่า dict อย่างง่ายที่กำหนดโดยตัวระบุอุปกรณ์ในระบบ - ค่าเหล่านี้มีไว้เพื่อ จะถูกแปลงโดย รวม โดย ตัวดำเนินการแบบรวม ที่เป็นตัวแทนของโปรโตคอลการสื่อสารแบบกระจายชนิดต่างๆ (เช่นการรวม) หากสิ่งนี้ฟังดูเป็นนามธรรมเกินไปก็ไม่ต้องกังวลเราจะกลับไปที่สิ่งนี้ในไม่ช้าและเราจะแสดงตัวอย่างที่เป็นรูปธรรม

ประเภทสหพันธ์ใน TFF มีสองรสชาติ: ประเภทที่สมาชิกเป็นองค์ประกอบของค่าสหพันธ์อาจแตกต่างกัน (ดังที่เห็นด้านบน) และประเภทที่เป็นที่ทราบกันดีว่ามีค่าเท่ากันทั้งหมด สิ่งนี้ถูกควบคุมโดยพารามิเตอร์ all_equal เป็นทางเลือกที่สามในตัวสร้าง tff.FederatedType (ค่าเริ่มต้นเป็น False )

federated_float_on_clients.all_equal
False

ประเภทสหพันธ์ที่มีตำแหน่ง G ซึ่งองค์ประกอบของสมาชิก T -typed ทั้งหมดเป็นที่ทราบกันดีว่าเท่ากันสามารถแสดงเป็น T@G ได้อย่างกระชับ (ตรงข้ามกับ {T}@G นั่นคือมีวงเล็บปีกกาหลุดเพื่อสะท้อน ความจริงที่ว่าองค์ประกอบของสมาชิกหลายชุดประกอบด้วยรายการเดียว)

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

ตัวอย่างหนึ่งของมูลค่ารวมประเภทดังกล่าวที่อาจเกิดขึ้นในสถานการณ์จริงคือไฮเปอร์พารามิเตอร์ (เช่นอัตราการเรียนรู้บรรทัดฐานการตัด ฯลฯ ) ที่เซิร์ฟเวอร์ถ่ายทอดไปยังกลุ่มอุปกรณ์ที่เข้าร่วมการฝึกอบรมแบบรวมศูนย์

อีกตัวอย่างหนึ่งคือชุดของพารามิเตอร์สำหรับโมเดลแมชชีนเลิร์นนิงที่ได้รับการฝึกฝนมาก่อนแล้วที่เซิร์ฟเวอร์ซึ่งจะแพร่ภาพไปยังกลุ่มของอุปกรณ์ไคลเอ็นต์ซึ่งสามารถปรับให้เหมาะกับผู้ใช้แต่ละคนได้

ตัวอย่างเช่นสมมติว่าเรามีพารามิเตอร์ float32 คู่ a และ b สำหรับโมเดลการถดถอยเชิงเส้นมิติเดียวอย่างง่าย เราสามารถสร้างแบบจำลองดังกล่าว (ไม่รวมศูนย์) เพื่อใช้ใน TFF ได้ดังนี้ วงเล็บมุม <> ในสตริงชนิดที่พิมพ์เป็นสัญกรณ์ TFF ขนาดกะทัดรัดสำหรับสิ่งที่มีชื่อหรือไม่มีชื่อ

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

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

โปรดทราบว่าเราระบุเฉพาะ dtype s ด้านบนเท่านั้น นอกจากนี้ยังรองรับประเภทที่ไม่ใช่สเกลาร์ ในโค้ดด้านบน tf.float32 คือสัญกรณ์ทางลัดสำหรับ tff.TensorType(dtype=tf.float32, shape=[]) ทั่วไป tff.TensorType(dtype=tf.float32, shape=[])

เมื่อโมเดลนี้ถูกกระจายไปยังไคลเอนต์ประเภทของค่าที่รวมผลลัพธ์สามารถแสดงได้ดังที่แสดงด้านล่าง

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

ตามสมมาตรที่มีการ ลอยแบบรวม ด้านบนเราจะอ้างถึงประเภทดังกล่าวว่า ทูเพิลรวม โดยทั่วไปเรามักจะใช้คำว่า federated XYZ เพื่ออ้างถึงค่าที่รวมกันซึ่งองค์ประกอบของสมาชิกเป็น XYZ -like ดังนั้นเราจะพูดถึงสิ่งต่างๆเช่นสิ่งที่ รวมเข้า ด้วยกัน, ลำดับ แบบรวม , แบบจำลองแบบรวม และอื่น ๆ

ตอนนี้กลับมาที่ float32@CLIENTS - ในขณะที่ดูเหมือนจำลองในอุปกรณ์หลายเครื่อง แต่จริงๆแล้วมันเป็น float32 เดียวเนื่องจากสมาชิกทั้งหมดเหมือนกัน โดยทั่วไปคุณอาจนึกถึงประเภทสหพันธรัฐที่ เท่าเทียมกันทั้งหมด เช่นหนึ่งในรูปแบบ T@G เป็นไอโซมอร์ฟิคกับประเภทที่ไม่รวมศูนย์ T เนื่องจากในทั้งสองกรณีมีเพียงรายการเดียว (แม้ว่าอาจจำลองแบบได้) ประเภท T

เมื่อพิจารณาจาก isomorphism ระหว่าง T และ T@G คุณอาจสงสัยว่ามีจุดประสงค์อะไรประเภทหลังอาจให้บริการ อ่านต่อ.

ตำแหน่ง

ภาพรวมการออกแบบ

ในส่วนก่อนหน้านี้เราได้แนะนำแนวคิดของ ตำแหน่ง - กลุ่มของผู้เข้าร่วมระบบที่อาจร่วมกันโฮสต์มูลค่ารวมและเราได้แสดงให้เห็นถึงการใช้ tff.CLIENTS เป็นตัวอย่างข้อกำหนดของตำแหน่ง

เพื่ออธิบายว่าเหตุใดแนวคิดเรื่อง ตำแหน่ง จึงเป็นพื้นฐานที่เราจำเป็นต้องรวมเข้ากับระบบประเภท TFF โปรดนึกถึงสิ่งที่เรากล่าวถึงในตอนต้นของบทช่วยสอนนี้เกี่ยวกับการใช้ TFF บางส่วน

แม้ว่าในบทช่วยสอนนี้คุณจะเห็นเฉพาะรหัส TFF ที่ถูกเรียกใช้งานในสภาพแวดล้อมจำลองเท่านั้น แต่เป้าหมายของเราคือให้ TFF เปิดใช้งานการเขียนโค้ดที่คุณสามารถปรับใช้สำหรับการดำเนินการกับกลุ่มของอุปกรณ์ทางกายภาพในระบบแบบกระจายซึ่งอาจรวมถึงอุปกรณ์พกพาหรืออุปกรณ์ฝังตัว ใช้ Android อุปกรณ์แต่ละตัวจะได้รับชุดคำสั่งแยกต่างหากเพื่อดำเนินการในเครื่องขึ้นอยู่กับบทบาทที่เล่นในระบบ (อุปกรณ์สำหรับผู้ใช้ปลายทางผู้ประสานงานแบบรวมศูนย์ชั้นกลางในสถาปัตยกรรมหลายชั้น ฯลฯ ) สิ่งสำคัญคือต้องสามารถหาเหตุผลได้ว่าอุปกรณ์ย่อยใดรันโค้ดอะไรและส่วนต่างๆของข้อมูลอาจเป็นจริงได้

สิ่งนี้มีความสำคัญอย่างยิ่งเมื่อต้องจัดการกับข้อมูลแอปพลิเคชันบนอุปกรณ์มือถือ เนื่องจากข้อมูลเป็นข้อมูลส่วนตัวและอาจมีความละเอียดอ่อนเราจึงจำเป็นต้องมีความสามารถในการยืนยันแบบคงที่ว่าข้อมูลนี้จะไม่ออกจากอุปกรณ์ (และพิสูจน์ข้อเท็จจริงเกี่ยวกับวิธีการประมวลผลข้อมูล) ข้อกำหนดการจัดวางเป็นหนึ่งในกลไกที่ออกแบบมาเพื่อรองรับสิ่งนี้

ฉิบหายได้รับการออกแบบเป็นศูนย์กลางข้อมูลสภาพแวดล้อมการเขียนโปรแกรมและเป็นเช่นนี้แตกต่างจากบางส่วนของกรอบที่มีอยู่ที่มุ่งเน้นการดำเนินงานและการดำเนินงานที่เหล่านั้นอาจทำงานฉิบหายมุ่งเน้นไปที่ข้อมูลที่มีข้อมูลที่ materializes และวิธีการที่จะถูกเปลี่ยน ดังนั้นตำแหน่งจึงถูกจำลองเป็นคุณสมบัติของข้อมูลใน TFF แทนที่จะเป็นคุณสมบัติของการดำเนินการกับข้อมูล ตามที่คุณกำลังจะเห็นในส่วนถัดไปการดำเนินงานของ TFF บางส่วนครอบคลุมไปตามสถานที่ต่างๆและเรียกใช้ "ในเครือข่าย" เพื่อที่จะพูดแทนที่จะดำเนินการโดยเครื่องเดียวหรือกลุ่มของเครื่อง

การแสดงประเภทของค่าบางอย่างเป็น T@G หรือ {T}@G (ซึ่งตรงข้ามกับแค่ T ) ทำให้การตัดสินใจในการจัดวางข้อมูลมีความชัดเจนและร่วมกับการวิเคราะห์แบบคงที่ของโปรแกรมที่เขียนใน TFF สามารถใช้เป็นพื้นฐานในการจัดหา การรับประกันความเป็นส่วนตัวอย่างเป็นทางการสำหรับข้อมูลบนอุปกรณ์ที่ละเอียดอ่อน

อย่างไรก็ตามสิ่งสำคัญที่ควรทราบในตอนนี้ก็คือในขณะที่เราสนับสนุนให้ผู้ใช้ TFF ระบุอย่างชัดเจนเกี่ยวกับ กลุ่ม อุปกรณ์ที่เข้าร่วมซึ่งโฮสต์ข้อมูล (ตำแหน่ง) โปรแกรมเมอร์จะไม่จัดการกับข้อมูลดิบหรือตัวตนของผู้เข้าร่วม แต่ละคน .

(หมายเหตุ: แม้ว่าจะอยู่นอกเหนือขอบเขตของบทช่วยสอนนี้ แต่เราควรพูดถึงว่ามีข้อยกเว้นที่น่าสังเกตอย่างหนึ่งจากข้างต้นคือตัวดำเนินการ tff.federated_collect ที่มีจุดมุ่งหมายเพื่อเป็นแบบดั้งเดิมระดับต่ำสำหรับสถานการณ์เฉพาะเท่านั้นการใช้งานอย่างชัดเจน ในสถานการณ์ที่ไม่แนะนำให้หลีกเลี่ยงเนื่องจากอาจ จำกัด การใช้งานในอนาคตที่เป็นไปได้ตัวอย่างเช่นหากในระหว่างการวิเคราะห์แบบคงที่เราพิจารณาว่าการคำนวณใช้กลไกระดับต่ำดังกล่าวเราอาจไม่อนุญาตให้เข้าถึงบางอย่าง ประเภทของข้อมูล)

ภายในเนื้อความของรหัส TFF ตามการออกแบบไม่มีทางที่จะระบุอุปกรณ์ที่ประกอบเป็นกลุ่มที่แสดงโดย tff.CLIENTS หรือเพื่อตรวจสอบการมีอยู่ของอุปกรณ์เฉพาะในกลุ่ม ไม่มีแนวคิดเกี่ยวกับอุปกรณ์หรือข้อมูลประจำตัวไคลเอนต์ที่ใดก็ได้ใน Federated Core API ชุดพื้นฐานของโครงสร้างพื้นฐานทางสถาปัตยกรรมหรือโครงสร้างพื้นฐานรันไทม์หลักที่เรามีให้เพื่อรองรับการจำลอง ตรรกะการคำนวณทั้งหมดที่คุณเขียนจะแสดงเป็นการดำเนินการกับกลุ่มลูกค้าทั้งหมด

จำสิ่งที่เรากล่าวถึงก่อนหน้านี้เกี่ยวกับค่าของประเภทรวมที่ไม่เหมือน Python dict เนื่องจากไม่สามารถระบุองค์ประกอบของสมาชิกได้ ลองนึกถึงค่าที่ตรรกะของโปรแกรม TFF ของคุณปรับเปลี่ยนว่าเกี่ยวข้องกับตำแหน่ง (กลุ่ม) มากกว่าที่จะเกิดขึ้นกับผู้เข้าร่วมแต่ละคน

ตำแหน่ง ได้ รับการออกแบบให้เป็นพลเมืองชั้นหนึ่งใน TFF เช่นกันและสามารถปรากฏเป็นพารามิเตอร์และผลลัพธ์ของประเภท placement (แสดงโดย tff.PlacementType ใน API) ในอนาคตเราวางแผนที่จะจัดหาโอเปอเรเตอร์ที่หลากหลายเพื่อเปลี่ยนหรือรวมตำแหน่ง แต่สิ่งนี้อยู่นอกขอบเขตของบทแนะนำนี้ สำหรับตอนนี้มันก็พอที่จะคิดว่าการ placement เป็นประเภทบิวท์อินแบบดั้งเดิมทึบแสงใน TFF คล้ายกับการที่ int และ bool เป็นประเภทบิวด์อินทึบใน Python โดย tff.CLIENTS เป็นตัวอักษรคงที่ของประเภทนี้ไม่ต่างจาก 1 เป็นค่าคงที่ของประเภท int

การระบุตำแหน่ง

TFF มีตัวอักษรตำแหน่งพื้นฐานสองตัวคือ tff.CLIENTS และ tff.SERVER เพื่อให้ง่ายต่อการแสดงสถานการณ์จริงที่หลากหลายซึ่งจำลองตามธรรมชาติเป็นสถาปัตยกรรมไคลเอนต์เซิร์ฟเวอร์พร้อมอุปกรณ์ ไคลเอนต์ หลายเครื่อง (โทรศัพท์มือถืออุปกรณ์ฝังตัวฐานข้อมูลแบบกระจาย , เซ็นเซอร์ ฯลฯ ) จัดทำโดยผู้ประสานงาน เซิร์ฟเวอร์ ส่วนกลางเพียงคนเดียว TFF ได้รับการออกแบบมาเพื่อรองรับตำแหน่งที่กำหนดเองกลุ่มลูกค้าหลายชั้นหลายชั้นและอื่น ๆ สถาปัตยกรรมแบบกระจายทั่วไป แต่การพูดคุยกันนั้นอยู่นอกขอบเขตของบทแนะนำนี้

TFF ไม่ได้กำหนดว่า tff.CLIENTS หรือ tff.SERVER เป็นตัวแทนของอะไร

โดยเฉพาะอย่างยิ่ง tff.SERVER อาจเป็นอุปกรณ์ทางกายภาพเดียว (สมาชิกของกลุ่มซิงเกิลตัน) แต่ก็อาจเป็นกลุ่มของแบบจำลองในคลัสเตอร์ที่ทนต่อความผิดพลาดที่รันการจำลองสถานะเครื่อง - เราไม่ได้สร้างสถาปัตยกรรมพิเศษใด ๆ สมมติฐาน แต่เราใช้บิต all_equal กล่าวถึงในส่วนก่อนหน้านี้เพื่อแสดงความจริงที่ว่าโดยทั่วไปแล้วเราจะจัดการกับข้อมูลเพียงรายการเดียวที่เซิร์ฟเวอร์

ในทำนองเดียวกัน tff.CLIENTS ในบางแอปพลิเคชันอาจแสดงถึงไคลเอนต์ทั้งหมดในระบบ - สิ่งที่ในบริบทของการเรียนรู้แบบรวมศูนย์บางครั้งเราเรียกว่า ประชากร แต่เช่นใน การใช้งานการผลิตของ Federated Averaging อาจแสดงถึง กลุ่มประชากรตามรุ่น ซึ่งเป็นส่วนย่อยของ ลูกค้าที่ได้รับเลือกให้เข้าร่วมในการฝึกอบรมรอบใดรอบหนึ่ง ตำแหน่งที่กำหนดแบบนามธรรมจะได้รับความหมายที่เป็นรูปธรรมเมื่อการคำนวณที่ปรากฏถูกปรับใช้เพื่อการดำเนินการ (หรือเรียกง่ายๆเช่นฟังก์ชัน Python ในสภาพแวดล้อมจำลองดังที่แสดงในบทช่วยสอนนี้) ในการจำลองแบบโลคัลของเรากลุ่มไคลเอ็นต์จะถูกกำหนดโดยข้อมูลแบบรวมศูนย์ที่ให้มาเป็นอินพุต

การคำนวณแบบรวมศูนย์

การประกาศการคำนวณแบบรวมศูนย์

TFF ได้รับการออกแบบให้เป็นสภาพแวดล้อมการเขียนโปรแกรมที่ใช้งานได้ดีซึ่งสนับสนุนการพัฒนาแบบแยกส่วน

หน่วยพื้นฐานขององค์ประกอบใน TFF คือการ คำนวณแบบรวม - ส่วนของตรรกะที่อาจยอมรับค่ารวมเป็นอินพุตและส่งคืนค่ารวมเป็นเอาต์พุต นี่คือวิธีที่คุณสามารถกำหนดการคำนวณที่คำนวณค่าเฉลี่ยของอุณหภูมิที่รายงานโดยอาร์เรย์เซ็นเซอร์จากตัวอย่างก่อนหน้าของเรา

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

เมื่อดูโค้ดด้านบน ณ จุดนี้คุณอาจกำลังถาม - ไม่มีโครงสร้างมัณฑนากรเพื่อกำหนดหน่วย tf.function เช่น tf.function ใน TensorFlow และถ้าเป็นเช่นนั้นทำไมต้องแนะนำอีกอันหนึ่งและมันแตกต่างกันอย่างไร

คำตอบสั้น ๆ คือรหัสที่สร้างโดย tff.federated_computation wrapper ไม่ใช่ TensorFlow หรือ Python ซึ่งเป็นข้อกำหนดของระบบแบบกระจายในภาษา กาวที่ ไม่ขึ้นกับแพลตฟอร์มภายใน ณ จุดนี้สิ่งนี้จะฟังดูเป็นความลับอย่างไม่ต้องสงสัย แต่โปรดเข้าใจการตีความการคำนวณแบบรวมศูนย์ที่ใช้งานง่ายนี้เป็นข้อกำหนดเชิงนามธรรมของระบบแบบกระจายในใจ เราจะอธิบายในอีกสักครู่

ก่อนอื่นมาเล่นกับคำจำกัดความกันสักหน่อย โดยทั่วไปการคำนวณ TFF จะถูกจำลองเป็นฟังก์ชัน - มีหรือไม่มีพารามิเตอร์ แต่มีลายเซ็นประเภทที่กำหนดไว้อย่างดี คุณสามารถพิมพ์ลายเซ็นประเภทของการคำนวณได้โดยค้นหาคุณสมบัติ type_signature ดังที่แสดงด้านล่าง

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

ลายเซ็นประเภทบอกเราว่าการคำนวณยอมรับชุดของการอ่านเซ็นเซอร์ที่แตกต่างกันบนอุปกรณ์ไคลเอนต์และส่งกลับค่าเฉลี่ยเดียวบนเซิร์ฟเวอร์

ก่อนที่เราจะดำเนินการต่อไปลองไตร่ตรองดูสักครู่ - อินพุตและเอาต์พุตของการคำนวณนี้อยู่ ในที่ต่างๆกัน (ใน CLIENTS เทียบกับที่ SERVER ) นึกถึงสิ่งที่เราพูดในส่วนก่อนหน้านี้เกี่ยวกับตำแหน่งเกี่ยวกับการ ดำเนินงานของ TFF ในสถานที่ต่างๆและทำงานในเครือข่าย และสิ่งที่เราเพิ่งพูดเกี่ยวกับการคำนวณแบบรวมศูนย์ในฐานะตัวแทนข้อกำหนดนามธรรมของระบบแบบกระจาย เราได้กำหนดเพียงการคำนวณดังกล่าว - ระบบกระจายอย่างง่ายซึ่งข้อมูลถูกใช้ไปที่อุปกรณ์ไคลเอนต์และผลลัพธ์รวมจะปรากฏที่เซิร์ฟเวอร์

ในสถานการณ์จริงหลาย ๆ สถานการณ์การคำนวณที่แสดงถึงงานระดับบนสุดจะมีแนวโน้มที่จะยอมรับอินพุตและรายงานผลลัพธ์ที่เซิร์ฟเวอร์ซึ่งสะท้อนให้เห็นถึงแนวคิดที่ว่าการคำนวณอาจถูกทริกเกอร์โดยการ สืบค้น ที่เริ่มต้นและสิ้นสุดบนเซิร์ฟเวอร์

อย่างไรก็ตาม FC API ไม่ได้กำหนดสมมติฐานนี้และ tff.federated_... จำนวนมากที่เราใช้ภายใน (รวมถึงตัวดำเนินการ tff.federated_... จำนวนมากที่คุณอาจพบใน API) มีอินพุตและเอาต์พุตที่มีตำแหน่งที่แตกต่างกันดังนั้นโดยทั่วไปคุณควร อย่าคิดว่าการคำนวณแบบรวมศูนย์เป็นสิ่งที่ ทำงานบนเซิร์ฟเวอร์ หรือ ดำเนินการโดยเซิร์ฟเวอร์ เซิร์ฟเวอร์เป็นเพียงผู้เข้าร่วมประเภทหนึ่งในการคำนวณแบบรวมศูนย์ ในการคิดถึงกลไกของการคำนวณดังกล่าวคุณควรตั้งค่าเริ่มต้นเป็นมุมมองทั่วทั้งเครือข่ายทั่วโลกแทนที่จะเป็นมุมมองของผู้ประสานงานส่วนกลางเพียงคนเดียว

โดยทั่วไปลายเซ็นประเภทการทำงานจะถูกแสดงอย่างกะทัดรัดเป็น (T -> U) สำหรับประเภท T และ U ของอินพุตและเอาต์พุตตามลำดับ ประเภทของพารามิเตอร์ที่เป็นทางการ (เช่น sensor_readings ในกรณีนี้) ถูกระบุเป็นอาร์กิวเมนต์ของมัณฑนากร คุณไม่จำเป็นต้องระบุประเภทของผลลัพธ์ แต่จะถูกกำหนดโดยอัตโนมัติ

แม้ว่า TFF จะนำเสนอความหลากหลายในรูปแบบที่ จำกัด แต่โปรแกรมเมอร์ควรได้รับการสนับสนุนอย่างชัดเจนเกี่ยวกับประเภทของข้อมูลที่พวกเขาทำงานด้วยเนื่องจากจะทำให้การทำความเข้าใจการดีบักและการตรวจสอบคุณสมบัติของโค้ดของคุณอย่างเป็นทางการง่ายขึ้น ในบางกรณีการระบุประเภทอย่างชัดเจนเป็นข้อกำหนด (เช่นการคำนวณโพลีมอร์ฟิกยังไม่สามารถดำเนินการได้โดยตรง)

กำลังดำเนินการคำนวณแบบรวมศูนย์

เพื่อสนับสนุนการพัฒนาและการดีบัก TFF ช่วยให้คุณสามารถเรียกใช้การคำนวณที่กำหนดด้วยวิธีนี้เป็นฟังก์ชัน Python ได้โดยตรงดังที่แสดงด้านล่าง ในกรณีที่การคำนวณคาดว่าจะมีค่าของชนิดรวมโดยที่บิต all_equal ตั้งค่าเป็น False คุณสามารถป้อนเป็น list ธรรมดาใน Python และสำหรับประเภทที่รวมศูนย์โดยตั้งค่าบิต all_equal เป็น True คุณสามารถป้อนข้อมูล (single) ได้โดยตรง องค์ประกอบของสมาชิก นี่คือวิธีที่จะรายงานผลลัพธ์ให้คุณทราบ

get_average_temperature([68.5, 70.3, 69.8])
69.53334

เมื่อรันการคำนวณเช่นนี้ในโหมดจำลองคุณจะทำหน้าที่เป็นผู้สังเกตการณ์ภายนอกที่มีมุมมองทั้งระบบซึ่งเป็นผู้ที่มีความสามารถในการจัดหาอินพุตและใช้เอาต์พุตที่ตำแหน่งใด ๆ ในเครือข่ายตามที่เป็นจริงในกรณีนี้ - คุณระบุค่าไคลเอ็นต์ ที่อินพุตและใช้ผลลัพธ์ของเซิร์ฟเวอร์

ตอนนี้ขอกลับไปที่บันทึกที่เราทำไว้ก่อนหน้านี้เกี่ยวกับมัณฑนากร tff.federated_computation เปล่งรหัสในภาษา กาว แม้ว่าตรรกะของการคำนวณ TFF สามารถแสดงเป็นฟังก์ชันธรรมดาใน Python (คุณเพียงแค่ต้องตกแต่งด้วย tff.federated_computation ตามที่เราได้ทำไว้ข้างต้น) และคุณสามารถเรียกใช้ด้วยอาร์กิวเมนต์ Python ได้โดยตรงเช่นเดียวกับฟังก์ชัน Python อื่น ๆ ในสิ่งนี้ สมุดบันทึกเบื้องหลังดังที่เราได้กล่าวไว้ก่อนหน้านี้การคำนวณ TFF ไม่ใช่ Python

สิ่งที่เราหมายถึงก็คือเมื่อล่าม Python พบฟังก์ชันที่ตกแต่งด้วย tff.federated_computation มันจะติดตามคำสั่งในเนื้อความของฟังก์ชันนี้หนึ่งครั้ง (ณ เวลานิยาม) จากนั้นสร้างการ แสดง ตรรกะของการคำนวณแบบ อนุกรม เพื่อใช้ในอนาคต - ไม่ว่าจะเป็น สำหรับการดำเนินการหรือจะรวมเป็นส่วนประกอบย่อยในการคำนวณอื่น

คุณสามารถตรวจสอบได้โดยเพิ่มคำสั่งพิมพ์ดังต่อไปนี้:

@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".

คุณสามารถนึกถึงรหัส Python ที่กำหนดการคำนวณแบบรวมศูนย์ในลักษณะเดียวกับที่คุณคิดรหัส Python ที่สร้างกราฟ TensorFlow ในบริบทที่ไม่กระตือรือร้น (หากคุณไม่คุ้นเคยกับการใช้ TensorFlow ที่ไม่กระตือรือร้นให้นึกถึง รหัส Python กำหนดกราฟของการดำเนินการที่จะดำเนินการในภายหลัง แต่ไม่ได้เรียกใช้งานจริงทันที) โค้ดการสร้างกราฟที่ไม่ต้องการความสนใจใน TensorFlow คือ Python แต่กราฟ TensorFlow ที่สร้างโดยโค้ดนี้ไม่ขึ้นกับแพลตฟอร์มและสามารถต่ออนุกรมกันได้

ในทำนองเดียวกันการคำนวณ TFF ถูกกำหนดใน Python แต่คำสั่ง Python ในเนื้อความเช่น tff.federated_mean ในตัวอย่างที่เราเพิ่งแสดงจะถูกรวบรวมไว้ในการแสดงผลแบบอนุกรมแบบพกพาและไม่ขึ้นกับแพลตฟอร์มภายใต้ประทุน

ในฐานะนักพัฒนาคุณไม่จำเป็นต้องกังวลเกี่ยวกับรายละเอียดของการเป็นตัวแทนนี้เนื่องจากคุณไม่จำเป็นต้องทำงานโดยตรงกับมัน แต่คุณควรตระหนักถึงการมีอยู่ของมันความจริงที่ว่าการคำนวณ TFF นั้นไม่กระตือรือร้นโดยพื้นฐาน และไม่สามารถจับภาพสถานะ Python โดยพลการ โค้ด Python ที่มีอยู่ในเนื้อหาของการคำนวณ TFF จะถูกเรียกใช้ในช่วงเวลาที่กำหนดเมื่อมีการตรวจสอบเนื้อหาของฟังก์ชัน Python ที่ตกแต่งด้วย tff.federated_computation ก่อนที่จะทำให้เป็นอนุกรม จะไม่ถูกย้อนกลับอีกครั้งในเวลาที่เรียกใช้ (ยกเว้นเมื่อฟังก์ชันเป็นแบบหลายรูปแบบโปรดดูรายละเอียดในหน้าเอกสารประกอบ)

คุณอาจสงสัยว่าทำไมเราถึงเลือกที่จะแนะนำการเป็นตัวแทนภายในที่ไม่ใช่ Python โดยเฉพาะ เหตุผลหนึ่งคือท้ายที่สุดแล้วการคำนวณ TFF มีวัตถุประสงค์เพื่อปรับใช้กับสภาพแวดล้อมทางกายภาพจริงและโฮสต์บนอุปกรณ์พกพาหรืออุปกรณ์ฝังตัวซึ่ง Python อาจไม่พร้อมใช้งาน

อีกเหตุผลหนึ่งคือการคำนวณ TFF แสดงพฤติกรรมทั่วโลกของระบบแบบกระจายซึ่งตรงข้ามกับโปรแกรม Python ซึ่งแสดงออกถึงพฤติกรรมในท้องถิ่นของผู้เข้าร่วมแต่ละคน คุณจะเห็นได้ว่าในตัวอย่างง่ายๆด้านบนมีตัวดำเนินการพิเศษ tff.federated_mean ที่รับข้อมูลบนอุปกรณ์ไคลเอนต์ แต่ฝากผลลัพธ์ไว้บนเซิร์ฟเวอร์

ตัวดำเนินการ tff.federated_mean ไม่สามารถจำลองเป็นตัวดำเนินการธรรมดาใน Python ได้อย่างง่ายดายเนื่องจากไม่ได้ดำเนินการภายในเครื่อง - ดังที่ระบุไว้ก่อนหน้านี้แสดงถึงระบบแบบกระจายที่ประสานพฤติกรรมของผู้เข้าร่วมระบบหลายคน เราจะอ้างถึงตัวดำเนินการดังกล่าวเป็นตัวดำเนินการ แบบรวม เพื่อแยกความแตกต่างจากตัวดำเนินการ (ท้องถิ่น) ธรรมดาใน Python

ระบบประเภท TFF และชุดปฏิบัติการพื้นฐานที่รองรับในภาษาของ TFF จึงเบี่ยงเบนไปจากระบบใน Python อย่างมีนัยสำคัญโดยจำเป็นต้องใช้การแสดงเฉพาะ

การเขียนการคำนวณแบบรวมศูนย์

ดังที่ระบุไว้ข้างต้นการคำนวณแบบรวมศูนย์และองค์ประกอบของระบบเหล่านี้เป็นที่เข้าใจได้ดีที่สุดว่าเป็นแบบจำลองของระบบแบบกระจายและคุณสามารถคิดว่าการเขียนการคำนวณแบบรวมศูนย์เป็นการรวบรวมระบบการกระจายที่ซับซ้อนขึ้นจากระบบที่ง่ายกว่า คุณสามารถคิดว่าโอเปอเรเตอร์ tff.federated_mean เป็นเทมเพลตในตัวแบบรวมการคำนวณที่มีลายเซ็นประเภท ({T}@CLIENTS -> T@SERVER) (เช่นเดียวกับการคำนวณที่คุณเขียนโอเปอเรเตอร์นี้ยังมีความซับซ้อน โครงสร้าง - ภายใต้ประทุนเราแบ่งมันออกเป็นตัวดำเนินการที่ง่ายกว่า)

เช่นเดียวกับการเขียนการคำนวณแบบรวมศูนย์ การคำนวณ get_average_temperature อาจถูกเรียกใช้ในเนื้อความของฟังก์ชัน Python อื่นที่ตกแต่งด้วย tff.federated_computation - การทำเช่นนั้นจะทำให้มันถูกฝังอยู่ในร่างกายของแม่ในลักษณะเดียวกับที่ tff.federated_mean ถูกฝังไว้ในร่างกายของตัวเองก่อนหน้านี้

ข้อ จำกัด สำคัญที่จะต้องระวังก็คือร่างของฟังก์ชั่นการตกแต่งด้วยงูหลาม tff.federated_computation ต้องประกอบด้วยเฉพาะของผู้ประกอบการ federated คือพวกเขาไม่สามารถมีการดำเนินงาน TensorFlow ตัวอย่างเช่นคุณไม่สามารถใช้อินเตอร์เฟส tf.nest โดยตรงเพื่อเพิ่มคู่ของค่าที่รวมกัน รหัส TensorFlow ต้องถูก จำกัด ไว้ในบล็อกของโค้ดที่ตกแต่งด้วย tff.tf_computation กล่าวถึงในส่วนต่อไปนี้ เมื่อห่อในลักษณะนี้เท่านั้นที่สามารถเรียกใช้รหัส TensorFlow ที่ห่อไว้ในเนื้อความของ tff.federated_computation

สาเหตุของการแยกนี้เป็นเรื่องทางเทคนิค (เป็นการยากที่จะหลอกให้ตัวดำเนินการเช่น tf.add ทำงานกับ non-tensors) รวมถึงสถาปัตยกรรม ภาษาของการคำนวณแบบรวมศูนย์ (กล่าวคือตรรกะที่สร้างจากเนื้อหาอนุกรมของฟังก์ชัน Python ที่ตกแต่งด้วย tff.federated_computation ) ได้รับการออกแบบมาเพื่อใช้เป็นภาษา กาวที่ ไม่ขึ้นกับแพลตฟอร์ม ปัจจุบันภาษากาวนี้ใช้ในการสร้างระบบแบบกระจายจากส่วนฝังตัวของโค้ด TensorFlow (จำกัด อยู่ที่บล็อก tff.tf_computation ) ในเวลาอันสั้นเราคาดว่าจะต้องฝังส่วนของตรรกะอื่น ๆ ที่ไม่ใช่ TensorFlow เช่นการสืบค้นฐานข้อมูลเชิงสัมพันธ์ที่อาจเป็นตัวแทนของท่อนำเข้าทั้งหมดเชื่อมต่อกันโดยใช้ภาษากาวเดียวกัน (บล็อก tff.federated_computation )

ตรรกะ TensorFlow

การประกาศการคำนวณ TensorFlow

TFF ออกแบบมาเพื่อใช้กับ TensorFlow ด้วยเหตุนี้โค้ดส่วนใหญ่ที่คุณจะเขียนใน TFF จึงน่าจะเป็นโค้ด TensorFlow ธรรมดา (เช่นการเรียกใช้งานในเครื่อง) ในการใช้รหัสดังกล่าวกับ TFF ดังที่ระบุไว้ข้างต้นจำเป็นต้องตกแต่งด้วย tff.tf_computation

ตัวอย่างเช่นต่อไปนี้เป็นวิธีที่เราสามารถใช้ฟังก์ชันที่รับจำนวนและบวก 0.5 เข้าไปได้

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

อีกครั้งเมื่อดูสิ่งนี้คุณอาจสงสัยว่าทำไมเราควรกำหนดมัณฑนากรอื่น tff.tf_computation แทนที่จะใช้กลไกที่มีอยู่เช่น tf.function ไม่เหมือนกับในส่วนก่อนหน้านี้เรากำลังจัดการกับบล็อกปกติของรหัส TensorFlow

มีเหตุผลบางประการสำหรับสิ่งนี้การปฏิบัติอย่างเต็มรูปแบบซึ่งเกินขอบเขตของบทช่วยสอนนี้ แต่ควรตั้งชื่อหลักว่า:

  • ในการฝังหน่วยการสร้างที่ใช้ซ้ำได้ซึ่งดำเนินการโดยใช้รหัส TensorFlow ในเนื้อความของการคำนวณแบบรวมศูนย์พวกเขาจำเป็นต้องตอบสนองคุณสมบัติบางอย่างเช่นการติดตามและจัดลำดับตามเวลาที่กำหนดการมีลายเซ็นประเภท ฯลฯ โดยทั่วไปแล้วจะต้องใช้มัณฑนากรบางรูปแบบ

โดยทั่วไปเราขอแนะนำให้ใช้กลไกดั้งเดิมของ TensorFlow ในการจัดองค์ประกอบเช่น tf.function เมื่อใดก็ตามที่เป็นไปได้เนื่องจากลักษณะที่แน่นอนที่มัณฑนากรของ TFF โต้ตอบกับฟังก์ชันที่กระตือรือร้นนั้นคาดว่าจะมีวิวัฒนาการ

ตอนนี้กลับมาที่ตัวอย่างโค้ดด้านบนการคำนวณ add_half เราเพิ่งกำหนดไว้สามารถปฏิบัติได้โดย TFF เหมือนกับการคำนวณ TFF อื่น ๆ โดยเฉพาะอย่างยิ่งมีลายเซ็นประเภท TFF

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

โปรดทราบว่าลายเซ็นประเภทนี้ไม่มีตำแหน่ง การคำนวณ TensorFlow ไม่สามารถใช้หรือส่งคืนชนิดรวม

ตอนนี้คุณยังสามารถใช้ add_half เป็นแบบเอกสารสำเร็จรูปในการคำนวณอื่น ๆ ตัวอย่างเช่นนี่คือวิธีที่คุณสามารถใช้ตัวดำเนินการ tff.federated_map เพื่อใช้ add_half pointwise กับองค์ประกอบสมาชิกทั้งหมดของการลอยตัวแบบรวมศูนย์บนอุปกรณ์ไคลเอนต์

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

กำลังดำเนินการคำนวณ TensorFlow

การดำเนินการคำนวณที่กำหนดด้วย tff.tf_computation ไปตามกฎเดียวกันกับที่เราอธิบายไว้สำหรับ tff.federated_computation สามารถเรียกใช้เป็น callables ธรรมดาใน Python ได้ดังนี้

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

เป็นที่น่าสังเกตอีกครั้งว่าการเรียกใช้การคำนวณ add_half_on_clients ในลักษณะนี้เป็นการจำลองกระบวนการแบบกระจาย ข้อมูลถูกใช้บนไคลเอนต์และส่งคืนให้กับไคลเอนต์ อันที่จริงการคำนวณนี้ทำให้ลูกค้าแต่ละรายดำเนินการในพื้นที่ ไม่มี tff.SERVER กล่าวถึงอย่างชัดเจนในระบบนี้ (แม้ว่าในทางปฏิบัติการจัดเตรียมการประมวลผลดังกล่าวอาจเกี่ยวข้องกับอย่างใดอย่างหนึ่ง) ลองนึกถึงการคำนวณที่กำหนดด้วยวิธีนี้ว่าคล้ายคลึงกับสเตจ Map ใน MapReduce

นอกจากนี้โปรดทราบว่าสิ่งที่เรากล่าวไว้ในส่วนก่อนหน้านี้เกี่ยวกับการคำนวณ TFF ที่ได้รับการทำให้เป็นอนุกรมในเวลานิยามยังคงเป็นจริงสำหรับรหัส tff.tf_computation เช่นกัน - เนื้อหา Python ของ add_half_on_clients จะถูกตรวจสอบหนึ่งครั้งในเวลาที่กำหนด ในการเรียกใช้ครั้งต่อ ๆ ไป TFF จะใช้การแสดงแบบอนุกรม

ข้อแตกต่างเพียงอย่างเดียวระหว่างเมธอด Python ที่ตกแต่งด้วย tff.federated_computation และที่ตกแต่งด้วย tff.tf_computation คือส่วนหลังจะถูกทำให้เป็นอนุกรมเป็นกราฟ TensorFlow (ในขณะที่ก่อนหน้านี้ไม่ได้รับอนุญาตให้มีโค้ด TensorFlow ฝังอยู่โดยตรง)

ภายใต้ประทุนแต่ละวิธีที่ตกแต่งด้วย tff.tf_computation จะปิดใช้งานการดำเนินการอย่างกระตือรือร้นชั่วคราวเพื่อให้สามารถจับโครงสร้างของการคำนวณได้ ในขณะที่การดำเนินการอย่างกระตือรือร้นถูกปิดใช้งานในเครื่องคุณสามารถใช้โครงสร้าง TensorFlow, AutoGraph, TensorFlow 2.0 ที่กระตือรือร้นและอื่น ๆ ได้ตราบใดที่คุณเขียนตรรกะของการคำนวณของคุณในลักษณะที่สามารถทำให้เป็นอนุกรมได้อย่างถูกต้อง

ตัวอย่างเช่นรหัสต่อไปนี้จะล้มเหลว:

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.

ข้างต้นล้มเหลวเนื่องจากมีการสร้าง constant_10 นอกกราฟที่ tff.tf_computation สร้างภายในในเนื้อความของ add_ten ระหว่างกระบวนการทำให้เป็นอนุกรม

ในทางกลับกันการเรียกใช้ฟังก์ชัน python ที่แก้ไขกราฟปัจจุบันเมื่อเรียกภายใน tff.tf_computation นั้นใช้ได้:

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

โปรดทราบว่ากลไกการทำให้เป็นอนุกรมใน TensorFlow กำลังมีการพัฒนาและเราคาดว่ารายละเอียดของการคำนวณอนุกรมของ TFF จะมีวิวัฒนาการเช่นกัน

การทำงานกับtf.data.Dataset s

ดังที่ระบุไว้ก่อนหน้านี้คุณลักษณะเฉพาะของ tff.tf_computation s คือช่วยให้คุณสามารถทำงานกับtf.data.Dataset ที่กำหนดไว้ในเชิงนามธรรมเป็นพารามิเตอร์ที่เป็นทางการโดยรหัสของคุณ Parameters to be represented in TensorFlow as data sets need to be declared using the tff.SequenceType constructor.

For example, the type specification tff.SequenceType(tf.float32) defines an abstract sequence of float elements in TFF. Sequences can contain either tensors, or complex nested structures (we'll see examples of those later). The concise representation of a sequence of T -typed items is 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 liketf.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 ordinarytf.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 atf.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 .