ดูบน TensorFlow.org | ทำงานใน Google Colab | ดูแหล่งที่มาบน GitHub | ดาวน์โหลดโน๊ตบุ๊ค |
กวดวิชานี้เป็นส่วนที่สองของชุดสองส่วนที่แสดงให้เห็นถึงวิธีการใช้ประเภทที่กำหนดเองของอัลกอริทึมแบบ federated ในฉิบหายใช้ สหพันธ์แกน (เอฟซี) ซึ่งทำหน้าที่เป็นรากฐานสำหรับการที่ สหพันธ์การเรียนรู้ (FL) ชั้น ( tff.learning
) .
เราขอแนะนำให้คุณไปก่อนอ่าน ส่วนแรกของชุดนี้ ซึ่งแนะนำบางส่วนของแนวคิดหลักและแนวคิดการเขียนโปรแกรมใช้ที่นี่
ส่วนที่สองของชุดข้อมูลนี้ใช้กลไกที่นำมาใช้ในส่วนแรกเพื่อนำอัลกอริธึมการฝึกอบรมและการประเมินผลแบบรวมรุ่นที่เรียบง่ายมาใช้
เราขอแนะนำให้คุณทบทวน การจัดหมวดหมู่ภาพ และ รุ่นข้อความ แบบฝึกหัดสำหรับระดับที่สูงขึ้นและการแนะนำอ่อนโยนมากขึ้นที่จะฉิบหายของสหพันธ์การเรียนรู้ APIs ที่พวกเขาจะช่วยให้คุณใส่แนวคิดที่เราจะอธิบายในบริบทที่นี่
ก่อนที่เราจะเริ่มต้น
ก่อนที่เราจะเริ่ม ให้ลองเรียกใช้ตัวอย่าง "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
# Must use the Python context because it
# supports tff.sequence_* intrinsics.
executor_factory = tff.framework.local_executor_factory(
support_sequence_ops=True)
execution_context = tff.framework.ExecutionContext(
executor_fn=executor_factory)
tff.framework.set_default_context(execution_context)
@tff.federated_computation
def hello_world():
return 'Hello, World!'
hello_world()
b'Hello, World!'
การใช้ Federated Averaging
ในขณะที่ การเรียนรู้สหพันธ์สำหรับภาพการจัดจำแนกของ เราจะใช้ตัวอย่างเช่น MNIST แต่ตั้งแต่นี้มีวัตถุประสงค์เพื่อเป็นระดับต่ำกวดวิชา, เราจะไปบายพาส API Keras และ tff.simulation
เขียนโค้ดรูปแบบดิบและสร้าง ชุดข้อมูลรวมศูนย์ตั้งแต่เริ่มต้น
การเตรียมชุดข้อมูลแบบรวมศูนย์
เพื่อการสาธิต เราจะจำลองสถานการณ์ที่เรามีข้อมูลจากผู้ใช้ 10 ราย และผู้ใช้แต่ละรายมีส่วนช่วยให้ความรู้ในการจำแนกตัวเลขที่แตกต่างกัน นี้เป็นเรื่องเกี่ยวกับที่ไม่ใช่ IID ตามที่ได้รับ
ขั้นแรก ให้โหลดข้อมูล MNIST มาตรฐาน:
mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()
[(x.dtype, x.shape) for x in mnist_train]
[(dtype('uint8'), (60000, 28, 28)), (dtype('uint8'), (60000,))]
ข้อมูลมาในรูปแบบ Numpy Array อันหนึ่งมีรูปภาพและอีกอันหนึ่งมีป้ายกำกับตัวเลข ทั้งคู่มีมิติแรกข้ามตัวอย่างแต่ละรายการ มาเขียนฟังก์ชันตัวช่วยที่จัดรูปแบบให้สอดคล้องกับวิธีที่เราป้อนลำดับรวมเข้าในการคำนวณ TFF กล่าวคือ เป็นรายการของรายการ - รายการภายนอกตั้งแต่ผู้ใช้ (ตัวเลข) รายการภายในที่มีชุดข้อมูลใน ลำดับของลูกค้าแต่ละราย ในฐานะที่เป็นธรรมเนียมที่เราจะจัดโครงสร้างแต่ละชุดเป็นคู่ของเทนเซอร์ชื่อ x
และ y
แต่ละคนมีมิติชุดชั้นนำ ในขณะที่มันเราจะแผ่ภาพแต่ละภาพเป็นเวกเตอร์ 784 องค์ประกอบและ rescale พิกเซลในนั้นเข้าไปใน 0..1
ช่วงเพื่อที่เราจะได้ไม่ต้องถ่วงตรรกะรุ่นที่มีการแปลงข้อมูล
NUM_EXAMPLES_PER_USER = 1000
BATCH_SIZE = 100
def get_data_for_digit(source, digit):
output_sequence = []
all_samples = [i for i, d in enumerate(source[1]) if d == digit]
for i in range(0, min(len(all_samples), NUM_EXAMPLES_PER_USER), BATCH_SIZE):
batch_samples = all_samples[i:i + BATCH_SIZE]
output_sequence.append({
'x':
np.array([source[0][i].flatten() / 255.0 for i in batch_samples],
dtype=np.float32),
'y':
np.array([source[1][i] for i in batch_samples], dtype=np.int32)
})
return output_sequence
federated_train_data = [get_data_for_digit(mnist_train, d) for d in range(10)]
federated_test_data = [get_data_for_digit(mnist_test, d) for d in range(10)]
ขณะที่การตรวจสอบสติอย่างรวดเร็วดูให้ของที่ Y
เมตริกซ์ในชุดสุดท้ายของข้อมูลสนับสนุนโดยไคลเอ็นต์ห้า (หนึ่งที่สอดคล้องกับหลัก 5
)
federated_train_data[5][-1]['y']
array([5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], dtype=int32)
เพื่อความแน่ใจ เรามาดูภาพที่สอดคล้องกับองค์ประกอบสุดท้ายของชุดงานนั้นกัน
from matplotlib import pyplot as plt
plt.imshow(federated_train_data[5][-1]['x'][-1].reshape(28, 28), cmap='gray')
plt.grid(False)
plt.show()
ในการรวม TensorFlow และ TFF
ในการกวดวิชานี้แน่นเราได้ทันทีตกแต่งฟังก์ชั่นที่แนะนำตรรกะ TensorFlow กับ tff.tf_computation
อย่างไรก็ตาม สำหรับตรรกะที่ซับซ้อนกว่านี้ นี่ไม่ใช่รูปแบบที่เราแนะนำ การดีบัก TensorFlow อาจเป็นเรื่องท้าทาย และการดีบัก TensorFlow หลังจากที่ได้รับการซีเรียลไลซ์โดยสมบูรณ์แล้วนำเข้าใหม่อีกครั้งจะสูญเสียข้อมูลเมตาบางส่วนและจำกัดการโต้ตอบ ทำให้การดีบั๊กมีความท้าทายมากขึ้น
ดังนั้นเราจึงขอแนะนำการเขียนตรรกะ TF ซับซ้อนเท่าที่ยืนอยู่คนเดียวฟังก์ชั่นหลาม (นั่นคือโดยไม่ต้อง tff.tf_computation
ตกแต่ง) วิธีนี้ตรรกะ TensorFlow สามารถที่จะพัฒนาและทดสอบโดยใช้ TF ปฏิบัติที่ดีที่สุดและเครื่องมือ (เช่นโหมดกระตือรือร้น) ก่อน serializing คำนวณสำหรับฉิบหาย (เช่นโดยการเรียก tff.tf_computation
กับฟังก์ชั่นหลามเป็นอาร์กิวเมนต์)
การกำหนดฟังก์ชันการสูญเสีย
เมื่อได้ข้อมูลแล้ว มากำหนดฟังก์ชันการสูญเสียที่เราสามารถใช้สำหรับการฝึกกัน ขั้นแรก ให้กำหนดประเภทของอินพุตเป็น TFF ชื่อทูเพิล เนื่องจากขนาดของแบตช์ข้อมูลที่อาจแตกต่างกันเราตั้งมิติชุดที่จะ None
การระบุว่าขนาดของมิตินี้ไม่เป็นที่รู้จัก
BATCH_SPEC = collections.OrderedDict(
x=tf.TensorSpec(shape=[None, 784], dtype=tf.float32),
y=tf.TensorSpec(shape=[None], dtype=tf.int32))
BATCH_TYPE = tff.to_type(BATCH_SPEC)
str(BATCH_TYPE)
'<x=float32[?,784],y=int32[?]>'
คุณอาจสงสัยว่าทำไมเราไม่สามารถกำหนดประเภท Python ธรรมดาได้ จำการอภิปรายใน ส่วน 1 ที่เราอธิบายว่าในขณะที่เราสามารถแสดงเหตุผลของการคำนวณฉิบหายโดยใช้งูใหญ่ภายใต้ประทุนคำนวณฉิบหายไม่ได้หลาม สัญลักษณ์ BATCH_TYPE
ที่กำหนดไว้ข้างต้นแสดงให้เห็นถึงสเปคชนิดฉิบหายนามธรรม มันเป็นสิ่งสำคัญที่จะแยกแยะประเภทนี้ฉิบหายนามธรรมจากคอนกรีตหลามประเภทการเป็นตัวแทนเช่นภาชนะบรรจุเช่น dict
หรือ collections.namedtuple
ที่อาจถูกใช้เพื่อเป็นตัวแทนของประเภทฉิบหายในร่างกายของฟังก์ชันหลาม ซึ่งแตกต่างจากงูหลามฉิบหายมีนามธรรมประเภทคอนสตรัคเดียว tff.StructType
สำหรับ tuple เหมือนภาชนะที่มีองค์ประกอบที่สามารถตั้งชื่อหรือซ้ายชื่อเป็นรายบุคคล ประเภทนี้ยังใช้ในการสร้างแบบจำลองพารามิเตอร์ที่เป็นทางการของการคำนวณ เนื่องจากการคำนวณ TFF สามารถประกาศพารามิเตอร์ได้เพียงตัวเดียวและผลลัพธ์เดียวเท่านั้น - คุณจะเห็นตัวอย่างของสิ่งนี้ในไม่ช้า
ตอนนี้ขอให้กำหนดประเภทฉิบหายของพารามิเตอร์แบบอีกครั้งเป็นฉิบหายชื่อ tuple ของน้ำหนักและอคติ
MODEL_SPEC = collections.OrderedDict(
weights=tf.TensorSpec(shape=[784, 10], dtype=tf.float32),
bias=tf.TensorSpec(shape=[10], dtype=tf.float32))
MODEL_TYPE = tff.to_type(MODEL_SPEC)
print(MODEL_TYPE)
<weights=float32[784,10],bias=float32[10]>
ด้วยคำจำกัดความเหล่านี้ ตอนนี้เราสามารถกำหนดการสูญเสียสำหรับแบบจำลองที่กำหนด ในชุดเดียวได้ หมายเหตุการใช้งานของ @tf.function
มัณฑนากรภายใน @tff.tf_computation
มัณฑนากร นี้จะช่วยให้เราสามารถเขียน TF ใช้งูใหญ่เช่นความหมายแม้อยู่ภายใน tf.Graph
บริบทที่สร้างขึ้นโดย tff.tf_computation
มัณฑนากร
# NOTE: `forward_pass` is defined separately from `batch_loss` so that it can
# be later called from within another tf.function. Necessary because a
# @tf.function decorated method cannot invoke a @tff.tf_computation.
@tf.function
def forward_pass(model, batch):
predicted_y = tf.nn.softmax(
tf.matmul(batch['x'], model['weights']) + model['bias'])
return -tf.reduce_mean(
tf.reduce_sum(
tf.one_hot(batch['y'], 10) * tf.math.log(predicted_y), axis=[1]))
@tff.tf_computation(MODEL_TYPE, BATCH_TYPE)
def batch_loss(model, batch):
return forward_pass(model, batch)
เป็นที่คาดหวังการคำนวณ batch_loss
ผลตอบแทน float32
การสูญเสียที่กำหนดรูปแบบและชุดข้อมูลเดียว หมายเหตุวิธีการ MODEL_TYPE
และ BATCH_TYPE
ได้รับการล้างโลกเข้าด้วยกันเป็น 2 tuple ของพารามิเตอร์อย่างเป็นทางการ; คุณสามารถรับรู้ประเภทของ batch_loss
เป็น (<MODEL_TYPE,BATCH_TYPE> -> float32)
str(batch_loss.type_signature)
'(<model=<weights=float32[784,10],bias=float32[10]>,batch=<x=float32[?,784],y=int32[?]>> -> float32)'
เพื่อเป็นการตรวจสอบสติ เรามาสร้างแบบจำลองเริ่มต้นซึ่งเต็มไปด้วยศูนย์และคำนวณการสูญเสียของชุดข้อมูลที่เราแสดงไว้ด้านบน
initial_model = collections.OrderedDict(
weights=np.zeros([784, 10], dtype=np.float32),
bias=np.zeros([10], dtype=np.float32))
sample_batch = federated_train_data[5][-1]
batch_loss(initial_model, sample_batch)
2.3025851
โปรดทราบว่าเรากินคำนวณฉิบหายที่มีรูปแบบเริ่มต้นกำหนดเป็น dict
แม้ว่าร่างกายของฟังก์ชั่นหลามที่กำหนดมันกินพารามิเตอร์แบบเป็น model['weight']
และ model['bias']
ข้อโต้แย้งของการเรียกร้องให้ batch_loss
ไม่ได้เพียงแค่ส่งผ่านไปยังร่างของฟังก์ชั่นว่า
จะเกิดอะไรขึ้นเมื่อเราวิงวอน batch_loss
? ร่างกายของงูหลาม batch_loss
ได้รับการตรวจสอบและต่อเนื่องในเซลล์ดังกล่าวข้างต้นที่มันถูกกำหนดไว้ ฉิบหายทำหน้าที่เป็นโทรไป batch_loss
ในเวลาที่นิยามการคำนวณและเป็นเป้าหมายของการภาวนาในเวลา batch_loss
ถูกเรียก ในทั้งสองบทบาท TFF ทำหน้าที่เป็นสะพานเชื่อมระหว่างระบบประเภทนามธรรมของ TFF และประเภทการแสดงแทน Python ในขณะที่การภาวนา, ฉิบหายจะยอมรับมาตรฐานส่วนใหญ่ประเภทภาชนะหลาม ( dict
, list
, tuple
, collections.namedtuple
ฯลฯ ) ในฐานะตัวแทนที่เป็นรูปธรรมของ tuples ฉิบหายนามธรรม นอกจากนี้ แม้ว่าตามที่กล่าวไว้ข้างต้น การคำนวณ TFF อย่างเป็นทางการยอมรับเพียงพารามิเตอร์เดียวเท่านั้น คุณสามารถใช้ไวยากรณ์การเรียก Python ที่คุ้นเคยพร้อมอาร์กิวเมนต์ตำแหน่งและ/หรือคีย์เวิร์ด ในกรณีที่ประเภทของพารามิเตอร์เป็นทูเพิล - มันทำงานได้ตามที่คาดไว้
การไล่สีแบบไล่ระดับในชุดเดียว
ตอนนี้ มากำหนดการคำนวณที่ใช้ฟังก์ชันการสูญเสียนี้เพื่อดำเนินการไล่ระดับขั้นเดียว หมายเหตุวิธีการในการกำหนดฟังก์ชั่นนี้เราจะใช้ batch_loss
เป็นคอมโพเนนต์ย่อย คุณสามารถเรียกการคำนวณสร้างด้วย tff.tf_computation
ภายในร่างกายของการคำนวณอีกแม้ว่าโดยทั่วไปนี้ไม่จำเป็น - ตามที่ระบุไว้ข้างต้นเพราะเป็นอันดับ looses ข้อมูลการแก้จุดบกพร่องบางอย่างก็มักจะเป็นที่นิยมสำหรับการคำนวณที่ซับซ้อนมากขึ้นในการเขียนและทดสอบทั้งหมด TensorFlow โดยไม่ต้อง tff.tf_computation
มัณฑนากร
@tff.tf_computation(MODEL_TYPE, BATCH_TYPE, tf.float32)
def batch_train(initial_model, batch, learning_rate):
# Define a group of model variables and set them to `initial_model`. Must
# be defined outside the @tf.function.
model_vars = collections.OrderedDict([
(name, tf.Variable(name=name, initial_value=value))
for name, value in initial_model.items()
])
optimizer = tf.keras.optimizers.SGD(learning_rate)
@tf.function
def _train_on_batch(model_vars, batch):
# Perform one step of gradient descent using loss from `batch_loss`.
with tf.GradientTape() as tape:
loss = forward_pass(model_vars, batch)
grads = tape.gradient(loss, model_vars)
optimizer.apply_gradients(
zip(tf.nest.flatten(grads), tf.nest.flatten(model_vars)))
return model_vars
return _train_on_batch(model_vars, batch)
str(batch_train.type_signature)
'(<initial_model=<weights=float32[784,10],bias=float32[10]>,batch=<x=float32[?,784],y=int32[?]>,learning_rate=float32> -> <weights=float32[784,10],bias=float32[10]>)'
เมื่อคุณเรียกใช้ฟังก์ชั่นการตกแต่งด้วยงูหลาม tff.tf_computation
ภายในร่างกายของฟังก์ชั่นดังกล่าวอีกตรรกะของการคำนวณฉิบหายภายในจะถูกฝังอยู่ (หลัก inlined) ในตรรกะของนอกอย่างใดอย่างหนึ่ง ตามที่ระบุไว้ข้างต้นถ้าคุณเขียนทั้งการคำนวณก็มีโอกาสที่ดีกว่าที่จะทำให้ฟังก์ชั่นภายใน ( batch_loss
ในกรณีนี้) ปกติงูหลามหรือ tf.function
มากกว่า tff.tf_computation
แต่ที่นี่เราแสดงให้เห็นว่าการเรียกหนึ่ง tff.tf_computation
ภายในอื่นทำงานโดยทั่วไปตามที่คาดไว้ ซึ่งอาจจำเป็นตัวอย่างเช่นถ้าคุณไม่ได้มีการกำหนดรหัสหลาม batch_loss
แต่มันเป็นตัวแทนฉิบหายต่อเนื่อง
ตอนนี้ ลองใช้ฟังก์ชันนี้สองสามครั้งกับโมเดลเริ่มต้นเพื่อดูว่าการสูญเสียลดลงหรือไม่
model = initial_model
losses = []
for _ in range(5):
model = batch_train(model, sample_batch, 0.1)
losses.append(batch_loss(model, sample_batch))
losses
[0.19690023, 0.13176313, 0.10113225, 0.08273812, 0.070301384]
การไล่ระดับสีตามลำดับของข้อมูลในเครื่อง
ตอนนี้ตั้งแต่ batch_train
ปรากฏในการทำงานขอเขียนการฝึกอบรมที่คล้ายกันฟังก์ชั่น local_train
ที่กินลำดับทั้งหมดสำหรับกระบวนการทั้งหมดจากผู้แทนเพียงชุดเดียว การคำนวณใหม่จะต้องใช้ในขณะนี้ tff.SequenceType(BATCH_TYPE)
แทน BATCH_TYPE
LOCAL_DATA_TYPE = tff.SequenceType(BATCH_TYPE)
@tff.federated_computation(MODEL_TYPE, tf.float32, LOCAL_DATA_TYPE)
def local_train(initial_model, learning_rate, all_batches):
@tff.tf_computation(LOCAL_DATA_TYPE, tf.float32)
def _insert_learning_rate_to_sequence(dataset, learning_rate):
return dataset.map(lambda x: (x, learning_rate))
batches_with_learning_rate = _insert_learning_rate_to_sequence(all_batches, learning_rate)
# Mapping function to apply to each batch.
@tff.federated_computation(MODEL_TYPE, batches_with_learning_rate.type_signature.element)
def batch_fn(model, batch_with_lr):
batch, lr = batch_with_lr
return batch_train(model, batch, lr)
return tff.sequence_reduce(batches_with_learning_rate, initial_model, batch_fn)
str(local_train.type_signature)
'(<initial_model=<weights=float32[784,10],bias=float32[10]>,learning_rate=float32,all_batches=<x=float32[?,784],y=int32[?]>*> -> <weights=float32[784,10],bias=float32[10]>)'
มีรายละเอียดค่อนข้างน้อยที่ฝังอยู่ในโค้ดส่วนสั้นๆ นี้ มาดูรายละเอียดกันทีละรายการ
แรกในขณะที่เราจะได้ดำเนินการตรรกะนี้ทั้งหมดใน TensorFlow อาศัย tf.data.Dataset.reduce
ที่จะดำเนินการตามลำดับเช่นเดียวกับวิธีการที่เราได้ทำมันก่อนหน้านี้เราได้เลือกใช้เวลานี้ในการแสดงตรรกะในภาษากาว เป็น tff.federated_computation
เราได้ใช้ประกอบการ federated tff.sequence_reduce
ในการดำเนินการลดลง
ผู้ประกอบการ tff.sequence_reduce
ถูกนำมาใช้ในทำนองเดียวกันกับ tf.data.Dataset.reduce
คุณสามารถคิดว่ามันเป็นหลักเช่นเดียวกับ tf.data.Dataset.reduce
แต่สำหรับการใช้งานภายในคำนวณ federated ซึ่งในขณะที่คุณอาจจำไม่สามารถมีรหัส TensorFlow มันเป็นผู้ประกอบการที่มีแม่แบบพารามิเตอร์อย่างเป็นทางการ 3 tuple ที่ประกอบด้วยลำดับของ T
-typed องค์ประกอบสถานะเริ่มต้นของการลด (เราจะเรียกมันเป็นนามธรรมเป็นศูนย์) บางชนิด U
และผู้ประกอบการลดลงของ พิมพ์ (<U,T> -> U)
ที่ alters รัฐของการลดโดยการประมวลผลองค์ประกอบเดียว ผลลัพธ์คือสถานะสุดท้ายของการลดลง หลังจากประมวลผลองค์ประกอบทั้งหมดตามลำดับ ในตัวอย่างของเรา สถานะของการลดลงคือโมเดลที่ได้รับการฝึกจากคำนำหน้าของข้อมูล และองค์ประกอบต่างๆ เป็นแบทช์ข้อมูล
ประการที่สองทราบว่าเราได้ใช้อีกครั้งหนึ่งในการคำนวณ ( batch_train
) เป็นส่วนประกอบภายในอื่น (ก local_train
) แต่ไม่ได้โดยตรง เราไม่สามารถใช้เป็นตัวดำเนินการลดได้เนื่องจากต้องใช้พารามิเตอร์เพิ่มเติม - อัตราการเรียนรู้ เพื่อแก้ปัญหานี้เรากำหนดฝังคำนวณ federated batch_fn
ที่ผูกกับ local_train
's พารามิเตอร์ learning_rate
ในร่างกายของตน อนุญาตให้ใช้การคำนวณย่อยที่กำหนดวิธีนี้เพื่อจับพารามิเตอร์ที่เป็นทางการของพาเรนต์ ตราบใดที่การคำนวณย่อยไม่ถูกเรียกใช้นอกเนื้อหาของพาเรนต์ คุณสามารถคิดว่ารูปแบบนี้เป็นเทียบเท่าของ functools.partial
ในหลาม
ความหมายในทางปฏิบัติของการจับ learning_rate
วิธีนี้เป็นของแน่นอนว่าค่าอัตราการเรียนรู้เดียวกันจะใช้ข้ามสำหรับกระบวนการทั้งหมด
ตอนนี้ขอลองฟังก์ชั่นการฝึกอบรมในท้องถิ่นที่กำหนดขึ้นใหม่ในลำดับทั้งหมดของข้อมูลจากผู้ใช้คนเดียวกันที่มีส่วนร่วมชุดตัวอย่าง (หลัก 5
)
locally_trained_model = local_train(initial_model, 0.1, federated_train_data[5])
มันทำงาน? เพื่อตอบคำถามนี้ เราจำเป็นต้องใช้การประเมิน
การประเมินท้องถิ่น
นี่เป็นวิธีหนึ่งในการใช้การประเมินในพื้นที่โดยเพิ่มการสูญเสียในชุดข้อมูลทั้งหมด (เราสามารถคำนวณค่าเฉลี่ยได้เช่นกัน เราจะปล่อยให้มันเป็นแบบฝึกหัดสำหรับผู้อ่าน)
@tff.federated_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def local_eval(model, all_batches):
@tff.tf_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def _insert_model_to_sequence(model, dataset):
return dataset.map(lambda x: (model, x))
model_plus_data = _insert_model_to_sequence(model, all_batches)
@tff.tf_computation(tf.float32, batch_loss.type_signature.result)
def tff_add(accumulator, arg):
return accumulator + arg
return tff.sequence_reduce(
tff.sequence_map(
batch_loss,
model_plus_data), 0., tff_add)
str(local_eval.type_signature)
'(<model=<weights=float32[784,10],bias=float32[10]>,all_batches=<x=float32[?,784],y=int32[?]>*> -> float32)'
อีกครั้ง มีองค์ประกอบใหม่สองสามอย่างที่แสดงโดยโค้ดนี้ มาดูทีละองค์ประกอบกัน
ครั้งแรกที่เราได้ใช้สองผู้ประกอบการ federated ใหม่สำหรับการประมวลผลลำดับ: tff.sequence_map
ที่ใช้ฟังก์ชั่นการทำแผนที่ T->U
และลำดับของ T
และส่งเสียงลำดับของ U
ที่ได้รับจากการใช้ฟังก์ชั่นการทำแผนที่ pointwise และ tff.sequence_sum
ว่า เพียงเพิ่มองค์ประกอบทั้งหมด ที่นี่ เราจับคู่ชุดข้อมูลแต่ละชุดกับค่าการสูญเสีย แล้วเพิ่มค่าการสูญเสียที่เป็นผลลัพธ์เพื่อคำนวณการสูญเสียทั้งหมด
โปรดทราบว่าเราจะได้ใช้อีกครั้ง tff.sequence_reduce
แต่นี้จะไม่เป็นตัวเลือกที่ดีที่สุด - การลดขั้นตอนคือโดยความหมายตามลำดับในขณะที่การทำแผนที่และผลรวมสามารถคำนวณได้ในแบบคู่ขนาน เมื่อได้รับตัวเลือก วิธีที่ดีที่สุดคือยึดติดกับโอเปอเรเตอร์ที่ไม่จำกัดตัวเลือกการใช้งาน เพื่อที่ว่าเมื่อการคำนวณ TFF ของเราถูกคอมไพล์ในอนาคตเพื่อนำไปใช้กับสภาพแวดล้อมเฉพาะ เราสามารถใช้ประโยชน์จากโอกาสที่เป็นไปได้ทั้งหมดได้เร็วขึ้น , ปรับขนาดได้มากขึ้น, การดำเนินการที่มีประสิทธิภาพของทรัพยากรมากขึ้น
ประการที่สองทราบว่าเช่นเดียวกับใน local_train
ฟังก์ชั่นที่เราต้องการส่วนประกอบ ( batch_loss
) ใช้พารามิเตอร์มากกว่าสิ่งที่ผู้ประกอบการ federated ( tff.sequence_map
) คาดว่าเพื่อให้เราอีกครั้งกำหนดบางส่วนเวลาในบรรทัดนี้โดยตรงโดยห่อ lambda
เป็น tff.federated_computation
ใช้ห่อใกล้เคียงกับฟังก์ชั่นเป็นอาร์กิวเมนต์เป็นวิธีที่แนะนำให้ใช้ tff.tf_computation
ฝัง TensorFlow ตรรกะในฉิบหาย
ตอนนี้เรามาดูกันว่าการฝึกอบรมของเราได้ผลหรือไม่
print('initial_model loss =', local_eval(initial_model,
federated_train_data[5]))
print('locally_trained_model loss =',
local_eval(locally_trained_model, federated_train_data[5]))
initial_model loss = 23.025854 locally_trained_model loss = 0.43484688
อันที่จริงความสูญเสียลดลง แต่จะเกิดอะไรขึ้นถ้าเราประเมินมันในข้อมูลของผู้ใช้รายอื่น
print('initial_model loss =', local_eval(initial_model,
federated_train_data[0]))
print('locally_trained_model loss =',
local_eval(locally_trained_model, federated_train_data[0]))
initial_model loss = 23.025854 locally_trained_model loss = 74.50075
อย่างที่คาดไว้ สิ่งต่างๆ แย่ลง รูปแบบที่ได้รับการฝึกฝนที่จะรับรู้ 5
และได้เคยเห็น 0
สิ่งนี้ทำให้เกิดคำถาม - การฝึกอบรมในพื้นที่ส่งผลต่อคุณภาพของแบบจำลองจากมุมมองทั่วโลกอย่างไร
การประเมินโดยสหพันธ์
นี่คือจุดสำคัญในการเดินทางของเรา ซึ่งในที่สุดเราก็วนกลับมาที่ประเภทรวมและการคำนวณแบบรวมศูนย์ ซึ่งเป็นหัวข้อที่เราเริ่มต้น ต่อไปนี้คือคำจำกัดความประเภท TFF คู่สำหรับโมเดลที่เริ่มต้นที่เซิร์ฟเวอร์ และข้อมูลที่เหลืออยู่บนไคลเอ็นต์
SERVER_MODEL_TYPE = tff.type_at_server(MODEL_TYPE)
CLIENT_DATA_TYPE = tff.type_at_clients(LOCAL_DATA_TYPE)
ด้วยคำจำกัดความทั้งหมดที่นำมาใช้จนถึงตอนนี้ การแสดงการประเมินแบบรวมศูนย์ใน TFF เป็นแบบแผนเดียว - เราแจกจ่ายแบบจำลองไปยังลูกค้า ให้ลูกค้าแต่ละรายเรียกใช้การประเมินในพื้นที่ในส่วนข้อมูลภายในเครื่อง จากนั้นจึงหาค่าเฉลี่ยของการสูญเสีย นี่เป็นวิธีหนึ่งในการเขียนสิ่งนี้
@tff.federated_computation(SERVER_MODEL_TYPE, CLIENT_DATA_TYPE)
def federated_eval(model, data):
return tff.federated_mean(
tff.federated_map(local_eval, [tff.federated_broadcast(model), data]))
เราได้เห็นแล้วตัวอย่างของ tff.federated_mean
และ tff.federated_map
ในสถานการณ์ที่เรียบง่ายและในระดับที่ใช้งานง่ายที่พวกเขาทำงานตามคาด แต่มีมากขึ้นในส่วนนี้ของรหัสกว่าตรงตาจึงขอไปกว่านั้นอย่างรอบคอบ
อันดับแรกให้แบ่งลงให้ลูกค้าเรียกใช้การประเมินผลแต่ละท้องถิ่นในส่วนของท้องถิ่นเป็นส่วนหนึ่งของข้อมูล ในขณะที่คุณอาจจำได้จากส่วนที่ก่อนหน้านี้ local_eval
มีลายเซ็นชนิดของแบบฟอร์ม (<MODEL_TYPE, LOCAL_DATA_TYPE> -> float32)
ผู้ประกอบการ federated tff.federated_map
เป็นแม่แบบที่ยอมรับเป็นพารามิเตอร์ 2 tuple ที่ประกอบด้วยฟังก์ชั่นการทำแผนที่บางประเภท T->U
และค่า federated ประเภท {T}@CLIENTS
(กล่าวคือเป็นคนละเรื่องกับสมาชิกของ ประเภทเดียวกันเป็นพารามิเตอร์ของฟังก์ชั่นการทำแผนที่) และผลตอบแทนที่เป็นผลมาจากประเภท {U}@CLIENTS
เนื่องจากเราให้อาหารกำลัง local_eval
เป็นฟังก์ชั่นการทำแผนที่จะนำไปใช้บนพื้นฐานต่อลูกค้าอาร์กิวเมนต์ที่สองควรจะมีประเภท federated {<MODEL_TYPE, LOCAL_DATA_TYPE>}@CLIENTS
คือในศัพท์ในส่วนที่ก่อนหน้านี้ที่ควร เป็นทูเพิลสหพันธรัฐ ลูกค้าแต่ละคนควรถือชุดเต็มของข้อโต้แย้ง local_eval
เป็น consituent สมาชิก แต่เรากำลังให้อาหารมัน 2 องค์ประกอบหลาม list
เกิดอะไรขึ้นที่นี่?
แท้จริงนี้คือตัวอย่างของประเภทหล่อนัยในฉิบหายคล้ายกับปลดเปลื้องประเภทนัยคุณอาจพบที่อื่นเช่นเมื่อคุณกินอาหาร int
ฟังก์ชั่นที่ยอมรับ float
ณ จุดนี้มีการใช้การหล่อโดยนัยเพียงเล็กน้อย แต่เราวางแผนที่จะทำให้มันแพร่หลายมากขึ้นใน TFF เพื่อลดความซับซ้อนของต้นแบบ
หล่อนัยที่นำมาใช้ในกรณีนี้คือความเท่าเทียมกันระหว่าง tuples federated ของฟอร์ม {<X,Y>}@Z
และ tuples ของ federated ค่า <{X}@Z,{Y}@Z>
ในขณะที่อย่างเป็นทางการทั้งสองมีลายเซ็นชนิดที่แตกต่างกันมองมันจากมุมมองของโปรแกรมเมอร์ของอุปกรณ์ในแต่ละ Z
ถือสองหน่วยของข้อมูล X
และ Y
จะเกิดอะไรขึ้นที่นี่ไม่ได้เป็นเหมือน zip
ในหลามและแน่นอนเรามีผู้ประกอบการ tff.federated_zip
ที่ช่วยให้คุณดำเนินการแปลงอย่างชัดเจนเช่น เมื่อ tff.federated_map
พบ tuple เป็นอาร์กิวเมนต์ที่สองมันก็จะเรียก tff.federated_zip
สำหรับคุณ
ป.ร. ให้ไว้ข้างต้นแล้วคุณควรจะสามารถที่จะรับรู้การแสดงออก tff.federated_broadcast(model)
เป็นคิดเป็นค่าฉิบหายประเภท {MODEL_TYPE}@CLIENTS
และ data
เป็นค่าของชนิดฉิบหาย {LOCAL_DATA_TYPE}@CLIENTS
(หรือเพียงแค่ CLIENT_DATA_TYPE
) ทั้งสองได้รับการกรองร่วมกันผ่านนัย tff.federated_zip
ในรูปแบบอาร์กิวเมนต์ที่สองที่จะ tff.federated_map
ผู้ประกอบการ tff.federated_broadcast
ตามที่คุณคาดหวังเพียงแค่ถ่ายโอนข้อมูลจากเซิร์ฟเวอร์ไปยังลูกค้า
ตอนนี้เรามาดูกันว่าการฝึกอบรมในพื้นที่ของเราส่งผลต่อการสูญเสียโดยเฉลี่ยในระบบอย่างไร
print('initial_model loss =', federated_eval(initial_model,
federated_train_data))
print('locally_trained_model loss =',
federated_eval(locally_trained_model, federated_train_data))
initial_model loss = 23.025852 locally_trained_model loss = 54.432625
เป็นไปตามคาด ขาดทุนเพิ่มขึ้น เพื่อปรับปรุงโมเดลสำหรับผู้ใช้ทุกคน เราจะต้องฝึกอบรมเกี่ยวกับข้อมูลของทุกคน
สหพันธ์การฝึกอบรม
วิธีที่ง่ายที่สุดในการนำการฝึกอบรมแบบสหพันธรัฐไปใช้คือการฝึกอบรมในพื้นที่ จากนั้นจึงหาค่าเฉลี่ยของแบบจำลอง สิ่งนี้ใช้การสร้างบล็อคและรูปแบบเดียวกันกับที่เราได้พูดคุยกันไปแล้วดังที่คุณเห็นด้านล่าง
SERVER_FLOAT_TYPE = tff.type_at_server(tf.float32)
@tff.federated_computation(SERVER_MODEL_TYPE, SERVER_FLOAT_TYPE,
CLIENT_DATA_TYPE)
def federated_train(model, learning_rate, data):
return tff.federated_mean(
tff.federated_map(local_train, [
tff.federated_broadcast(model),
tff.federated_broadcast(learning_rate), data
]))
ทราบว่าในการดำเนินการเต็มรูปแบบที่โดดเด่นของสหพันธ์ Averaging ให้โดย tff.learning
มากกว่าค่าเฉลี่ยรุ่นที่เราชอบที่จะเฉลี่ยสันดอนรุ่นสำหรับจำนวนของเหตุผลเช่นความสามารถในการตัดบรรทัดฐานการปรับปรุงสำหรับการบีบอัด ฯลฯ .
มาดูกันว่าการฝึกได้ผลหรือไม่โดยรันการฝึกสองสามรอบและเปรียบเทียบผลเสียเฉลี่ยก่อนและหลัง
model = initial_model
learning_rate = 0.1
for round_num in range(5):
model = federated_train(model, learning_rate, federated_train_data)
learning_rate = learning_rate * 0.9
loss = federated_eval(model, federated_train_data)
print('round {}, loss={}'.format(round_num, loss))
round 0, loss=21.60552215576172 round 1, loss=20.365678787231445 round 2, loss=19.27480125427246 round 3, loss=18.311111450195312 round 4, loss=17.45725440979004
เพื่อความสมบูรณ์ ตอนนี้ ให้เรียกใช้ข้อมูลการทดสอบเพื่อยืนยันว่าแบบจำลองของเรามีภาพรวมที่ดี
print('initial_model test loss =',
federated_eval(initial_model, federated_test_data))
print('trained_model test loss =', federated_eval(model, federated_test_data))
initial_model test loss = 22.795593 trained_model test loss = 17.278767
นี่เป็นการสรุปบทช่วยสอนของเรา
แน่นอน ตัวอย่างที่เข้าใจง่ายของเราไม่ได้สะท้อนถึงสิ่งที่คุณต้องทำในสถานการณ์ที่สมจริงมากขึ้น ตัวอย่างเช่น เราไม่ได้คำนวณเมตริกอื่นนอกจากการสูญเสีย เราขอแนะนำให้คุณเพื่อการศึกษา การดำเนินงาน ของสหพันธ์เฉลี่ย tff.learning
เป็นตัวอย่างที่สมบูรณ์มากขึ้นและเป็นวิธีที่จะแสดงให้เห็นถึงบางส่วนของการเขียนโค้ดที่เราต้องการที่จะส่งเสริมให้