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

ที่กำหนดเองสหพันธ์อัลกอริทึม, ส่วนที่ 2: การใช้สหพันธ์ Averaging

ดูบน TensorFlow.org เรียกใช้ใน Google Colab ดูโค้ดบน GitHub

กวดวิชานี้เป็นส่วนที่สองของชุดสองส่วนที่แสดงให้เห็นถึงวิธีการใช้ประเภทที่กำหนดเองของอัลกอริทึมแบบ federated ในฉิบหายใช้ สหพันธ์แกน (เอฟซี) ซึ่งทำหน้าที่เป็นรากฐานสำหรับการที่ สหพันธ์การเรียนรู้ (FL) ชั้น ( tff.learning ) .

เราขอแนะนำให้คุณไปก่อนอ่าน ส่วนแรกของชุดนี้ ซึ่งแนะนำบางส่วนของแนวคิดหลักและแนวคิดการเขียนโปรแกรมใช้ที่นี่

นี้ส่วนที่สองของซีรีส์ใช้กลไกที่นำมาใช้ในส่วนแรกที่จะดำเนินการรุ่นที่เรียบง่ายของการฝึกอบรมและการประเมินผลแบบ federated อัลกอริทึม

เราขอแนะนำให้คุณทบทวน การจัดหมวดหมู่ภาพ และ รุ่นข้อความ แบบฝึกหัดสำหรับระดับที่สูงขึ้นและการแนะนำอ่อนโยนมากขึ้นที่จะฉิบหายของสหพันธ์การเรียนรู้ 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

# TODO(b/148678573,b/148685415): must use the ReferenceExecutor because it
# supports unbounded references and tff.sequence_* intrinsics.
tff.framework.set_default_context(tff.test.ReferenceExecutor())
 
 @tff.federated_computation
def hello_world():
  return 'Hello, World!'

hello_world()
 
'Hello, World!'

การนำสหพันธ์ Averaging

ในขณะที่ การเรียนรู้สหพันธ์สำหรับภาพการจัดจำแนกของ เราจะใช้ตัวอย่างเช่น MNIST แต่ตั้งแต่นี้มีวัตถุประสงค์เพื่อเป็นระดับต่ำกวดวิชา, เราจะไปบายพาส API Keras และ tff.simulation เขียนโค้ดรูปแบบดิบและสร้าง ชุดข้อมูลแบบ federated จากรอยขีดข่วน

การจัดเตรียมชุดข้อมูลแบบ federated

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

อันดับแรกให้โหลดข้อมูล MNIST มาตรฐาน:

 mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()
 
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step

 [(x.dtype, x.shape) for x in mnist_train]
 
[(dtype('uint8'), (60000, 28, 28)), (dtype('uint8'), (60000,))]

ข้อมูลมาเป็นอาร์เรย์ Numpy หนึ่งที่มีภาพและอื่น ๆ ที่มีป้ายบาททั้งที่มีมิติครั้งแรกไปกว่าตัวอย่างของแต่ละบุคคล Let 's เขียนฟังก์ชันช่วยที่ว่ารูปแบบมันในทางที่เข้ากันได้กับวิธีการที่เรากินลำดับรวมเข้าคำนวณฉิบหายคือรายชื่อของรายการ - การรายการนอกตั้งแต่มากกว่าผู้ใช้ (หลัก) คนภายในตั้งแต่กว่าสำหรับกระบวนการของข้อมูลใน ลำดับของลูกค้าแต่ละราย ในฐานะที่เป็นธรรมเนียมที่เราจะจัดโครงสร้างแต่ละชุดเป็นคู่ของเทนเซอร์ชื่อ 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()
 

PNG

เกี่ยวกับการรวม TensorFlow และฉิบหาย

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

ดังนั้นเราจึงขอแนะนำการเขียนตรรกะ TF ซับซ้อนเท่าที่ยืนอยู่คนเดียวฟังก์ชั่นหลาม (นั่นคือโดยไม่ต้อง tff.tf_computation ตกแต่ง) วิธีนี้ตรรกะ TensorFlow สามารถที่จะพัฒนาและทดสอบโดยใช้ TF ปฏิบัติที่ดีที่สุดและเครื่องมือ (เช่นโหมดกระตือรือร้น) ก่อน serializing คำนวณสำหรับฉิบหาย (เช่นโดยการเรียก tff.tf_computation กับฟังก์ชั่นหลามเป็นอาร์กิวเมนต์)

กำหนดฟังก์ชั่นการสูญเสีย

ตอนนี้เรามีข้อมูลให้มีกำหนดฟังก์ชั่นการสูญเสียที่เราสามารถใช้สำหรับการฝึกอบรม อันดับแรกให้กำหนดประเภทของการป้อนข้อมูลเป็นฉิบหายชื่อ tuple เนื่องจากขนาดของแบตช์ข้อมูลที่อาจแตกต่างกันเราตั้งมิติชุดที่จะ 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[?]>'

คุณอาจจะสงสัยว่าทำไมเราไม่สามารถเพียงแค่กำหนดประเภทสามัญหลาม จำการอภิปรายใน ส่วน 1 ที่เราอธิบายว่าในขณะที่เราสามารถแสดงเหตุผลของการคำนวณฉิบหายโดยใช้งูใหญ่ภายใต้ประทุนคำนวณฉิบหายไม่ได้หลาม สัญลักษณ์ BATCH_TYPE ที่กำหนดไว้ข้างต้นแสดงให้เห็นถึงสเปคชนิดฉิบหายนามธรรม มันเป็นสิ่งสำคัญที่จะแยกแยะประเภทนี้ฉิบหายนามธรรมจากคอนกรีตหลามประเภทการเป็นตัวแทนเช่นภาชนะบรรจุเช่น dict หรือ collections.namedtuple ที่อาจถูกใช้เพื่อเป็นตัวแทนของประเภทฉิบหายในร่างกายของฟังก์ชันหลาม ซึ่งแตกต่างจากงูหลามฉิบหายมีนามธรรมประเภทคอนสตรัคเดียว tff.StructType สำหรับ tuple เหมือนภาชนะที่มีองค์ประกอบที่สามารถตั้งชื่อหรือซ้ายชื่อเป็นรายบุคคล ประเภทนี้จะใช้ในการจำลองพารามิเตอร์อย่างเป็นทางการของการคำนวณเช่นการคำนวณฉิบหายสามารถอย่างเป็นทางการเท่านั้นประกาศพารามิเตอร์หนึ่งและเป็นหนึ่งในผล - คุณจะดูตัวอย่างได้ในไม่ช้า

ตอนนี้ขอให้กำหนดประเภทฉิบหายของพารามิเตอร์แบบอีกครั้งเป็นฉิบหายชื่อ 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)
 
'(<<weights=float32[784,10],bias=float32[10]>,<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.3025854

โปรดทราบว่าเรากินคำนวณฉิบหายที่มีรูปแบบเริ่มต้นกำหนดเป็น dict แม้ว่าร่างกายของฟังก์ชั่นหลามที่กำหนดมันกินพารามิเตอร์แบบเป็น model['weight'] และ model['bias'] ข้อโต้แย้งของการเรียกร้องให้ batch_loss ไม่ได้เพียงแค่ส่งผ่านไปยังร่างของฟังก์ชั่นว่า

จะเกิดอะไรขึ้นเมื่อเราวิงวอน batch_loss ? ร่างกายของงูหลาม batch_loss ได้รับการตรวจสอบและต่อเนื่องในเซลล์ดังกล่าวข้างต้นที่มันถูกกำหนดไว้ ฉิบหายทำหน้าที่เป็นโทรไป batch_loss ในเวลาที่นิยามการคำนวณและเป็นเป้าหมายของการภาวนาในเวลา batch_loss ถูกเรียก ในทั้งสองบทบาทฉิบหายทำหน้าที่เป็นสะพานเชื่อมโยงระหว่างระบบการพิมพ์ฉิบหายของนามธรรมและงูหลามประเภทการเป็นตัวแทน ในขณะที่การภาวนา, ฉิบหายจะยอมรับมาตรฐานส่วนใหญ่ประเภทภาชนะหลาม ( dict , list , tuple , collections.namedtuple ฯลฯ ) ในฐานะตัวแทนที่เป็นรูปธรรมของ tuples ฉิบหายนามธรรม นอกจากนี้แม้ว่าตามที่ระบุไว้ข้างต้นคำนวณฉิบหายอย่างเป็นทางการจะยอมรับเฉพาะพารามิเตอร์เดียวคุณสามารถใช้คุ้นเคยไวยากรณ์โทรหลามกับการขัดแย้งตำแหน่งและ / หรือคำหลักในกรณีที่ประเภทของพารามิเตอร์คือ tuple - มันทำงานตามที่คาดไว้

เชื้อสายไล่โทนสีในชุดเดียว

ตอนนี้ขอกำหนดคำนวณที่ใช้ฟังก์ชั่นการสูญเสียนี้จะดำเนินการขั้นตอนเดียวเชื้อสายไล่ระดับสี หมายเหตุวิธีการในการกำหนดฟังก์ชั่นนี้เราจะใช้ 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)
 
'(<<weights=float32[784,10],bias=float32[10]>,<x=float32[?,784],y=int32[?]>,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.19690022, 0.13176313, 0.10113226, 0.082738124, 0.0703014]

เชื้อสายไล่โทนสีในลำดับของข้อมูลท้องถิ่น

ตอนนี้ตั้งแต่ 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):

  # Mapping function to apply to each batch.
  @tff.federated_computation(MODEL_TYPE, BATCH_TYPE)
  def batch_fn(model, batch):
    return batch_train(model, batch, learning_rate)

  return tff.sequence_reduce(all_batches, initial_model, batch_fn)
 
 str(local_train.type_signature)
 
'(<<weights=float32[784,10],bias=float32[10]>,float32,<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])
 

มันทำงานอย่างไร เพื่อที่จะตอบคำถามนี้เราจำเป็นต้องใช้การประเมินผล

การประเมินผลในพื้นที่

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

 @tff.federated_computation(MODEL_TYPE, LOCAL_DATA_TYPE)
def local_eval(model, all_batches):
  # TODO(b/120157713): Replace with `tff.sequence_average()` once implemented.
  return tff.sequence_sum(
      tff.sequence_map(
          tff.federated_computation(lambda b: batch_loss(model, b), BATCH_TYPE),
          all_batches))
 
 str(local_eval.type_signature)
 
'(<<weights=float32[784,10],bias=float32[10]>,<x=float32[?,784],y=int32[?]>*> -> float32)'

อีกครั้งมีองค์ประกอบใหม่ไม่กี่แสดงโดยรหัสนี้ขอไปมากกว่าพวกเขาหนึ่งโดยหนึ่ง

ครั้งแรกที่เราได้ใช้สองผู้ประกอบการ federated ใหม่สำหรับการประมวลผลลำดับ: tff.sequence_map ที่ใช้ฟังก์ชั่นการทำแผนที่ T->U และลำดับของ T และส่งเสียงลำดับของ U ที่ได้รับจากการใช้ฟังก์ชั่นการทำแผนที่ pointwise และ tff.sequence_sum ว่า เพียงแค่เพิ่มองค์ประกอบทั้งหมด ที่นี่เราแผนที่ข้อมูลแต่ละชุดจะคุ้มค่าการสูญเสียและเพิ่มค่าการสูญเสียที่เกิดขึ้นเพื่อคำนวณการสูญเสียทั้งหมด

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

ประการที่สองทราบว่าเช่นเดียวกับใน 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.4348469

อันที่จริงการสูญเสียลดลง แต่สิ่งที่เกิดขึ้นถ้าเราประเมินไว้ในข้อมูลของผู้ใช้อื่นได้หรือไม่

 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 วิธีนี้จะทำให้คำถาม - ไม่ว่าการฝึกอบรมในท้องถิ่นส่งผลกระทบต่อคุณภาพของรูปแบบจากมุมมองของทั่วโลกหรือไม่

การประเมินผลสหพันธ์

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

 SERVER_MODEL_TYPE = tff.FederatedType(MODEL_TYPE, tff.SERVER)
CLIENT_DATA_TYPE = tff.FederatedType(LOCAL_DATA_TYPE, tff.CLIENTS)
 

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

 @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 คือในศัพท์ในส่วนที่ก่อนหน้านี้ที่ควร เป็น tuple federated ลูกค้าแต่ละคนควรถือชุดเต็มของข้อโต้แย้ง local_eval เป็น consituent สมาชิก แต่เรากำลังให้อาหารมัน 2 องค์ประกอบหลาม list สิ่งที่เกิดขึ้นที่นี่?

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

หล่อนัยที่นำมาใช้ในกรณีนี้คือความเท่าเทียมกันระหว่าง 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

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

การฝึกอบรมสหพันธ์

วิธีที่ง่ายที่สุดในการดำเนินการฝึกอบรมแบบ federated คือการฝึกอบรมในประเทศและจากนั้นเฉลี่ยรุ่น นี้ใช้บล็อกอาคารเดียวกันและ patters เราได้กล่าวไปแล้วในขณะที่คุณสามารถดูด้านล่าง

 SERVER_FLOAT_TYPE = tff.FederatedType(tf.float32, tff.SERVER)


@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.60552406311035
round 1, loss=20.365678787231445
round 2, loss=19.27480125427246
round 3, loss=18.31110954284668
round 4, loss=17.45725440979004

เพื่อความสมบูรณ์ Let 's ตอนนี้ยังทำงานบนข้อมูลการทดสอบเพื่อยืนยันว่า generalizes รูปแบบของเราเป็นอย่างดี

 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 เป็นตัวอย่างที่สมบูรณ์มากขึ้นและเป็นวิธีที่จะแสดงให้เห็นถึงบางส่วนของการเขียนโค้ดที่เราต้องการที่จะส่งเสริมให้