此页面由 Cloud Translation API 翻译。
Switch to English

联合学习的图像分类

查看上TensorFlow.org GitHub上查看源代码

在本文中,我们使用经典MNIST训练为例介绍联邦学习(佛罗里达州)的TFF API层, tff.learning -一组更高级的接口,其可用于执行常见的类型的联合学习任务,如联合训练,针对TensorFlow实现用户提供的模型。

本教程,和联邦学习API,主要面向谁希望自己的TensorFlow模型插入TFF,治疗后者主要是作为一个黑盒子用户。对于TFF更深入的了解,以及如何实现自己的联合学习算法,请参阅FC核心API教程- 自定义算法联邦第1部分第2部分

欲了解更多关于tff.learning ,继续与联合学习的文本生成 ,教程,除覆盖复发模型,还演示了用于装载细化预先训练序列Keras模型与联盟学习使用Keras评价相结合。

在我们开始之前

在我们开始之前,请执行下列操作以确保您的环境中正确设置。如果您没有看到一个问候,请参阅安装指南。

 
!pip install --quiet --upgrade tensorflow_federated_nightly
!pip install --quiet --upgrade nest_asyncio

import nest_asyncio
nest_asyncio.apply()

%load_ext tensorboard
 
 import collections

import numpy as np
import tensorflow as tf
import tensorflow_federated as tff

np.random.seed(0)

tff.federated_computation(lambda: 'Hello, World!')()
 
b'Hello, World!'

准备输入数据

让我们从数据开始。联合学习需要联合的数据集,即,来自多个用户的数据的集合。联合数据通常是非IID ,这对一组独特的挑战。

为了方便实验,我们接种了几个数据集的TFF库,包括MNIST的联合版本包含的版本原NIST数据集已使用再加工叶片 ,使数据是由原作者键控数字。由于每个作家有独特的风格,该数据集显示出预期联邦数据集的那种非独立同分布的行为。

下面是我们如何可以加载它。

 emnist_train, emnist_test = tff.simulation.datasets.emnist.load_data()
 

返回的数据集load_data()是实例tff.simulation.ClientData ,允许你枚举组用户,以构造一个接口tf.data.Dataset表示特定用户的数据,并且查询构建单独的元件。这里是你如何使用这个接口来探索数据集的内容。请记住,虽然这界面允许您遍历用户的ID,这是模拟数据的唯一特征。正如你很快就会看到,客户身份不使用联合学习框架 - 他们唯一的目的就是让你选择数据的子集进行仿真。

 len(emnist_train.client_ids)
 
3383
 emnist_train.element_type_structure
 
OrderedDict([('pixels', TensorSpec(shape=(28, 28), dtype=tf.float32, name=None)), ('label', TensorSpec(shape=(), dtype=tf.int32, name=None))])
 example_dataset = emnist_train.create_tf_dataset_for_client(
    emnist_train.client_ids[0])

example_element = next(iter(example_dataset))

example_element['label'].numpy()
 
1
 from matplotlib import pyplot as plt
plt.imshow(example_element['pixels'].numpy(), cmap='gray', aspect='equal')
plt.grid(False)
_ = plt.show()
 

PNG

由于该数据已经是tf.data.Dataset ,预处理可以使用数据集的转换来实现。在这里,我们扁平化28x28图像转换成784 -元素阵列,洗牌个别的例子,它们组织成批次,并重命名从功能pixelslabel ,以xy与Keras使用。我们也扔在一个repeat对数据集运行几个时期。

 NUM_CLIENTS = 10
NUM_EPOCHS = 5
BATCH_SIZE = 20
SHUFFLE_BUFFER = 100
PREFETCH_BUFFER=10

def preprocess(dataset):

  def batch_format_fn(element):
    """Flatten a batch `pixels` and return the features as an `OrderedDict`."""
    return collections.OrderedDict(
        x=tf.reshape(element['pixels'], [-1, 784]),
        y=tf.reshape(element['label'], [-1, 1]))

  return dataset.repeat(NUM_EPOCHS).shuffle(SHUFFLE_BUFFER).batch(
      BATCH_SIZE).map(batch_format_fn).prefetch(PREFETCH_BUFFER)
 

让我们验证这个工作。

 preprocessed_example_dataset = preprocess(example_dataset)

sample_batch = tf.nest.map_structure(lambda x: x.numpy(),
                                     next(iter(preprocessed_example_dataset)))

sample_batch
 
OrderedDict([('x', array([[1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.],
       ...,
       [1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.],
       [1., 1., 1., ..., 1., 1., 1.]], dtype=float32)), ('y', array([[2],
       [1],
       [2],
       [3],
       [6],
       [0],
       [1],
       [4],
       [1],
       [0],
       [6],
       [9],
       [9],
       [3],
       [6],
       [1],
       [4],
       [8],
       [0],
       [2]], dtype=int32))])

我们几乎全部到位积木构建联合数据集。

之一的在模拟中联合数据馈送到TFF的一种方法是简单地以Python列表,与所述列表保持单个用户的数据,是否为列表或作为的每个元素tf.data.Dataset 。既然我们已经拥有了提供后者的界面,让我们使用它。

这里有一个简单的辅助功能,将构建从给定用户的数据集的列表作为输入的一轮培训或评估。

 def make_federated_data(client_data, client_ids):
  return [
      preprocess(client_data.create_tf_dataset_for_client(x))
      for x in client_ids
  ]
 

现在,我们该如何选择客户?

在一个典型的联合训练场景,我们面对的是一个潜在的非常大的群体的用户设备,其中只有一小部分可能是供训练在给定的时间点。就是这种情况,例如,当客户端设备仅当插入电源时参加训练,关闭计量网络,以及以其他方式空闲移动电话。

当然,我们在仿真环境中,所有的数据在本地使用。通常然后,运行模拟时,我们只会品尝客户的随机子集参与每一轮的训练,在每一轮一般是不同的。

也就是说,你可以通过学习对本文找出联邦取平均的算法,在每一轮的客户中随机抽样子集系统实现融合可能需要一段时间,这将是不切实际的,必须在运行数百发这种互动教程。

我们要做的却是样本组客户端的一次,重复使用同一套跨轮,加快收敛(故意过度拟合到这几个用户的数据)。我们把它作为一个练习留给读者来修改本教程模拟随机抽样 - 这是很容易做到的(一旦你做什么,请记住,获取模型收敛可能需要一段时间)。

 sample_clients = emnist_train.client_ids[0:NUM_CLIENTS]

federated_train_data = make_federated_data(emnist_train, sample_clients)

print('Number of client datasets: {l}'.format(l=len(federated_train_data)))
print('First dataset: {d}'.format(d=federated_train_data[0]))
 
Number of client datasets: 10
First dataset: <DatasetV1Adapter shapes: OrderedDict([(x, (None, 784)), (y, (None, 1))]), types: OrderedDict([(x, tf.float32), (y, tf.int32)])>

创建具有Keras模型

如果你正在使用Keras,你可能已经拥有了构建一个Keras型号代码。这里有一个简单的模型,将足以满足我们的需求的一个例子。

 def create_keras_model():
  return tf.keras.models.Sequential([
      tf.keras.layers.Input(shape=(784,)),
      tf.keras.layers.Dense(10, kernel_initializer='zeros'),
      tf.keras.layers.Softmax(),
  ])
 

为了使用与TFF的任何模型,它需要被包裹在一个实例tff.learning.Model接口,它公开的方法来冲压模型的直传,元数据属性等,类似于Keras,而且还带来了额外的元素,如方法来控制计算联合度量的过程。让我们不要担心这个,现在;如果你有一个Keras模型就像我们刚刚上面定义的,你可以通过调用TFF它包起来吗tff.learning.from_keras_model ,传递模型和样本数据批次作为参数,如下图所示。

 def model_fn():
  # We _must_ create a new model here, and _not_ capture it from an external
  # scope. TFF will call this within different graph contexts.
  keras_model = create_keras_model()
  return tff.learning.from_keras_model(
      keras_model,
      input_spec=preprocessed_example_dataset.element_spec,
      loss=tf.keras.losses.SparseCategoricalCrossentropy(),
      metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])
 

培训联合数据模型

现在,我们有包装成一个模型tff.learning.Model与TFF使用,我们可以让TFF通过调用辅助函数构造一个联邦平均算法tff.learning.build_federated_averaging_process ,如下所示。

请记住,参数必须是一个构造函数(如model_fn以上),而不是一个已经构建的实例,让你的模型的构建可以通过TFF控制的背景下发生的(如果你好奇的原因为此,我们建议您阅读的后续课程, 定制算法 )。

在下面的联邦平均算法的一个关键注意事项,有2个优化:一个_client 优化和_SERVER 优化 。该_client 优化器只是用于计算每个客户端上的局部模型更新。该_SERVER 优化应用的平均更新到服务器的全球模式。具体而言,这意味着使用可能需要优化和学习速度的选择比你用来训练在标准IID数据集模型中的不同。我们建议从正规新元,可能以较小的学习率比平常。学习速度,我们用没有经过精心调校,随时进行试验。

 iterative_process = tff.learning.build_federated_averaging_process(
    model_fn,
    client_optimizer_fn=lambda: tf.keras.optimizers.SGD(learning_rate=0.02),
    server_optimizer_fn=lambda: tf.keras.optimizers.SGD(learning_rate=1.0))
 

刚才发生了什么? TFF构建了一对联合计算 ,并将其封装成tff.templates.IterativeProcess其中这些计算可作为一对属性的initializenext

概括地说, 联合计算在TFF的内部语言程序,可以表达各种联合算法(你可以找到更多有关此内容的自定义算法教程)。在这种情况下,生成并装入两个计算iterative_process实施联合平均化

这是TFF的目标的方式来定义,他们可以在真正的联合学习环境中执行,但目前只本地执行模拟运行时执行计算。要在模拟器执行计算,只需调用它像一个Python函数。此默认解释环境不适合高性能,但是它可以满足本教程;我们希望提供更高性能的模拟运行,以方便在未来的版本更大规模的研究。

让我们从开始initialize计算。像所有的联合计算的情况下,你可以把它作为一个功能。计算不带任何参数,并返回一个结果 - 在服务器上的联合平均处理的状态的表示。虽然我们并不想潜入TFF的细节,它可能是有益的,看看这是什么状态的样子。你可以想像它,如下所示。

 str(iterative_process.initialize.type_signature)
 
'( -> <model=<trainable=<float32[784,10],float32[10]>,non_trainable=<>>,optimizer_state=<int64>,delta_aggregate_state=<>,model_broadcast_state=<>>@SERVER)'

虽然上述类型的签名可以在第一似乎有点神秘,可以认识到,服务器状态由一个的model (对于MNIST所述初始模型参数将被分发给所有装置),和optimizer_state (附加信息保持由所述服务器如用于超参数时间表等)轮数。

让我们调用initialize计算来构建服务器状态。

 state = iterative_process.initialize()
 

所述一对联合计算中的第二, next ,代表一个单轮联合取平均,它由推动服务器状态(包括模型参数)的给客户端,设备上训练他们的本地数据,收集和平均模型更新,并在生产服务器的新更新的模型。

从概念上讲,你能想到的next为具有功能型特征(如下所示)。

 SERVER_STATE, FEDERATED_DATA -> SERVER_STATE, TRAINING_METRICS
 

尤其是一个应该考虑next()不是作为一个功能的服务器上运行,而是作为整个分布式计算的声明性功能表示-一些输入由服务器(提供SERVER_STATE ),但每个参与设备贡献自己的本地数据集。

让我们运行一个单轮的培训和可视化的结果。我们可以用我们已经上面生成的联合数据,为用户的样本。

 state, metrics = iterative_process.next(state, federated_train_data)
print('round  1, metrics={}'.format(metrics))
 
round  1, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.12037037312984467,loss=3.0108425617218018>>

让我们多跑几个回合。如前所述,通常在这一点上,你会选择你的模拟数据子集的用户提供以模拟实际部署中,用户不断地来来去去,每一轮新的随机抽取样品,但在这个互动的笔记本电脑,为示范起见,我们将只重复使用相同的用户,从而使系统迅速收敛。

 NUM_ROUNDS = 11
for round_num in range(2, NUM_ROUNDS):
  state, metrics = iterative_process.next(state, federated_train_data)
  print('round {:2d}, metrics={}'.format(round_num, metrics))
 
round  2, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.14814814925193787,loss=2.8865506649017334>>
round  3, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.148765429854393,loss=2.9079062938690186>>
round  4, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.17633745074272156,loss=2.724686622619629>>
round  5, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.20226337015628815,loss=2.6334855556488037>>
round  6, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.22427983582019806,loss=2.5482592582702637>>
round  7, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.24094650149345398,loss=2.4472343921661377>>
round  8, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.259876549243927,loss=2.3809611797332764>>
round  9, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.29814815521240234,loss=2.156442403793335>>
round 10, metrics=<broadcast=<>,aggregation=<>,train=<sparse_categorical_accuracy=0.31687241792678833,loss=2.122845411300659>>

培训损耗每一轮的联合训练后下降,表明该模型融合。有这些培训指标的一些重要的注意事项,但是,看到评价部分在本教程后面。

显示模型度量TensorBoard

接下来,让我们想象使用Tensorboard这些联合计算的指标。

首先让我们来创建目录和相应的总结作家写的度量。

 
logdir = "/tmp/logs/scalars/training/"
summary_writer = tf.summary.create_file_writer(logdir)
state = iterative_process.initialize()
 

剧情与同总结作家相关的标量指标。

 
with summary_writer.as_default():
  for round_num in range(1, NUM_ROUNDS):
    state, metrics = iterative_process.next(state, federated_train_data)
    for name, value in metrics.train._asdict().items():
      tf.summary.scalar(name, value, step=round_num)
 

开始TensorBoard与根日志目录上述规定。它可以在几秒钟的数据负载。

 
%tensorboard --logdir /tmp/logs/scalars/ --port=0
 
 
# Run this this cell to clean your directory of old output for future graphs from this directory.
!rm -R /tmp/logs/scalars/*
 

为了查看评价指标同样的方式,你可以创建一个单独的eval文件夹,如“日志/标量/ EVAL”,写信给TensorBoard。

自定义模型实现

Keras是对TensorFlow推荐高层次模型的API ,我们鼓励使用Keras模型(通过tff.learning.from_keras_model在TFF)只要有可能。

然而, tff.learning提供了一个低级别的模型界面, tff.learning.Model ,暴露必要使用型号为联合学习的最小功能。直接实现此接口(可能仍然使用积木一样tf.keras.layers )允许最大的定制无需修改的联合学习算法的内部。

因此,让我们做一遍一切从零开始。

定义模型变量,直传和指标

第一步是确定我们将工作与TensorFlow变量。为了使下面的代码更易读,让我们定义一个数据结构来表示整个集合。这将包括变量,如weightsbias ,我们将培训,以及变量,将举行各种累积的统计数据和计数器,我们将在培训期间更新,如loss_sumaccuracy_sumnum_examples

 MnistVariables = collections.namedtuple(
    'MnistVariables', 'weights bias num_examples loss_sum accuracy_sum')
 

下面是一个创建变量的方法。为了简单起见,我们代表所有的统计数据tf.float32 ,因为这将消除在稍后阶段需要进行类型转换。结束语变量初始化为lambda表达式是由施加的规定, 资源变量

 def create_mnist_variables():
  return MnistVariables(
      weights=tf.Variable(
          lambda: tf.zeros(dtype=tf.float32, shape=(784, 10)),
          name='weights',
          trainable=True),
      bias=tf.Variable(
          lambda: tf.zeros(dtype=tf.float32, shape=(10)),
          name='bias',
          trainable=True),
      num_examples=tf.Variable(0.0, name='num_examples', trainable=False),
      loss_sum=tf.Variable(0.0, name='loss_sum', trainable=False),
      accuracy_sum=tf.Variable(0.0, name='accuracy_sum', trainable=False))
 

随着对模型参数和到位的累积统计的变量,我们现在可以定义一个计算损失,发出预测,并更新了单批输入数据的累积统计,如下直传方法。

 def mnist_forward_pass(variables, batch):
  y = tf.nn.softmax(tf.matmul(batch['x'], variables.weights) + variables.bias)
  predictions = tf.cast(tf.argmax(y, 1), tf.int32)

  flat_labels = tf.reshape(batch['y'], [-1])
  loss = -tf.reduce_mean(
      tf.reduce_sum(tf.one_hot(flat_labels, 10) * tf.math.log(y), axis=[1]))
  accuracy = tf.reduce_mean(
      tf.cast(tf.equal(predictions, flat_labels), tf.float32))

  num_examples = tf.cast(tf.size(batch['y']), tf.float32)

  variables.num_examples.assign_add(num_examples)
  variables.loss_sum.assign_add(loss * num_examples)
  variables.accuracy_sum.assign_add(accuracy * num_examples)

  return loss, predictions
 

接下来,我们定义一个返回一组本地度量,再次使用TensorFlow功能。这些值(除了模型更新,这是自动处理)有资格在联邦学习或评估过程中要汇总到服务器。

在这里,我们简单地返回平均lossaccuracy ,以及在num_examples ,其计算联合总量时,我们需要正确地加权来自不同用户的贡献。

 def get_local_mnist_metrics(variables):
  return collections.OrderedDict(
      num_examples=variables.num_examples,
      loss=variables.loss_sum / variables.num_examples,
      accuracy=variables.accuracy_sum / variables.num_examples)
 

最后,我们需要确定如何通过汇总每个设备发出的局部度量get_local_mnist_metrics 。这是不是在TensorFlow编写的代码的唯一部分-它在TFF表示联合计算 。如果您想深入挖掘,掠过自定义算法的教程,但在大多数应用中,你不会真的需要;下面示出应该足够的图案的变体。这里是什么样子:

 @tff.federated_computation
def aggregate_mnist_metrics_across_clients(metrics):
  return collections.OrderedDict(
      num_examples=tff.federated_sum(metrics.num_examples),
      loss=tff.federated_mean(metrics.loss, metrics.num_examples),
      accuracy=tff.federated_mean(metrics.accuracy, metrics.num_examples))
  
 

输入metrics参数对应于OrderedDict由归国get_local_mnist_metrics以上,但看房的值不再tf.Tensors -他们是“盒装”为tff.Value S,要想清楚,你可以使用TensorFlow不再对其进行操作,但只使用TFF的联合运营商如tff.federated_meantff.federated_sum 。全球聚集的返回字典定义了一组指标,这将是在服务器上可用的。

构建的一个实例tff.learning.Model

随着上述全部到位的,我们准备建立一个模型表示与TFF类似于对你,当你让TFF摄取Keras模型生成一个使用。

 class MnistModel(tff.learning.Model):

  def __init__(self):
    self._variables = create_mnist_variables()

  @property
  def trainable_variables(self):
    return [self._variables.weights, self._variables.bias]

  @property
  def non_trainable_variables(self):
    return []

  @property
  def local_variables(self):
    return [
        self._variables.num_examples, self._variables.loss_sum,
        self._variables.accuracy_sum
    ]

  @property
  def input_spec(self):
    return collections.OrderedDict(
        x=tf.TensorSpec([None, 784], tf.float32),
        y=tf.TensorSpec([None, 1], tf.int32))

  @tf.function
  def forward_pass(self, batch, training=True):
    del training
    loss, predictions = mnist_forward_pass(self._variables, batch)
    num_exmaples = tf.shape(batch['x'])[0]
    return tff.learning.BatchOutput(
        loss=loss, predictions=predictions, num_examples=num_exmaples)

  @tf.function
  def report_local_outputs(self):
    return get_local_mnist_metrics(self._variables)

  @property
  def federated_output_computation(self):
    return aggregate_mnist_metrics_across_clients
 

正如可以看到,由定义的抽象方法和属性tff.learning.Model对应引入的变量和定义的损失和统计上一节中的代码段。

这里有几点值得强调:

  • 您的模型要使用必须被捕捉为TensorFlow变量,TFF在运行时不使用Python(记住你的代码的所有国家应该写这样的,它可以部署到移动设备上;看到自定义算法教程更深入评上的原因)。
  • 你的模型应该描述它接受什么形式的数据( input_spec ),在一般情况下,TFF是一个强类型的环境,并希望确定所有组件类型签名。声明模型的输入的格式是它的一个重要组成部分。
  • 虽然技术上不是必需的,我们建议所有的包装逻辑TensorFlow(直传,度量计算等) tf.function S,因为这有助于确保TensorFlow可以被序列化,并消除了明确的控制依赖关系的需要。

以上是足够的评价和算法等联合SGD。然而,对于联邦平均化,我们需要指定如何模型应在每批培训当地。建立联邦平均算法时,我们将指定一个本地优化。

模拟与新模式的联合训练

随着上述全部到位,该过程看起来就像我们已经看到的剩余部分 - 只是我们的新模式类的构造函数代替模型构建,并在您完成创建循环迭代过程中使用两个联合计算训练弹。

 iterative_process = tff.learning.build_federated_averaging_process(
    MnistModel,
    client_optimizer_fn=lambda: tf.keras.optimizers.SGD(learning_rate=0.02))
 
 state = iterative_process.initialize()
 
 state, metrics = iterative_process.next(state, federated_train_data)
print('round  1, metrics={}'.format(metrics))
 
round  1, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.9713594913482666,accuracy=0.13518518209457397>>

 for round_num in range(2, 11):
  state, metrics = iterative_process.next(state, federated_train_data)
  print('round {:2d}, metrics={}'.format(round_num, metrics))
 
round  2, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.975412607192993,accuracy=0.14032921195030212>>
round  3, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.9395227432250977,accuracy=0.1594650149345398>>
round  4, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.710164785385132,accuracy=0.17139917612075806>>
round  5, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.5891618728637695,accuracy=0.20267489552497864>>
round  6, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.5148487091064453,accuracy=0.21666666865348816>>
round  7, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.2816808223724365,accuracy=0.2580246925354004>>
round  8, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.3656885623931885,accuracy=0.25884774327278137>>
round  9, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=2.23549222946167,accuracy=0.28477364778518677>>
round 10, metrics=<broadcast=<>,aggregation=<>,train=<num_examples=4860.0,loss=1.974222183227539,accuracy=0.35329216718673706>>

看到内TensorBoard这些度量,参考上面的“显示模型度量TensorBoard”中列出的步骤。

评估

我们所有的实验到目前为止提出的唯一联合训练指标 - 在全面对所有客户的培训数据的所有批次的平均指标。这介绍了过学习,特别是因为我们使用相同的一组客户端上的每个回合为简单起见,但在训练指标具体到联邦平均算法过度拟合额外的概念正常的担忧。这是最简单的,看看我们想象每一个客户有一个单一的一批数据,以及我们训练上批次多次迭代(时期)。在这种情况下,本地模型会迅速准确地适合于一个批次,所以当地的准确性度量,我们平均将接近1.0。因此,这些培训指标可以作为一个标志,训练进展,但没有更多。

要在联合数据进行分析,就可以构造另一个联合计算专为刚刚这一目的,使用tff.learning.build_federated_evaluation功能,在你的模型构造作为参数传递。请注意,不像联邦平均化,在这里我们已经使用MnistTrainableModel ,只须通过MnistModel 。评价不执行梯度下降,而且也没有必要构建优化。

对于实验和研究,当一个集中的测试数据集是可用的, 联合学习的文本生成演示了另一个评估选项:采取从联合学习训练的权重,把它们应用到标准Keras模型,然后简单地调用tf.keras.models.Model.evaluate()在一个集中的数据集。

 evaluation = tff.learning.build_federated_evaluation(MnistModel)
 

可以按如下方式检查评价功能的抽象类型签名。

 str(evaluation.type_signature)
 
'(<<trainable=<float32[784,10],float32[10]>,non_trainable=<>>@SERVER,{<x=float32[?,784],y=int32[?,1]>*}@CLIENTS> -> <num_examples=float32@SERVER,loss=float32@SERVER,accuracy=float32@SERVER>)'

没有必要将关注在这一点上的细节,只知道它采用以下一般形式,类似于tff.templates.IterativeProcess.next但有两个重要的区别。首先,我们没有返回服务器状态,因为评价不修改模型或国家的任何其他方面 - 你可以把它看作是无状态的。其次,评估只需要在模型中,并且不需要可能与训练有关,如优化变量服务器状态的任何其他部分。

 SERVER_MODEL, FEDERATED_DATA -> TRAINING_METRICS
 

在最新的状态让我们调用评估我们训练时到达。为了提取从服务器状态的最新训练模型,你只需访问.model成员,如下所示。

 train_metrics = evaluation(state.model, federated_train_data)
 

下面就是我们得到的。注意数字看起来略好于什么是报告的最后一轮以上的培训。按照惯例,训练指标报告的迭代训练过程中普遍反映在训练一轮开始的模型的性能,这样评价指标将永远领先一步。

 str(train_metrics)
 
'<num_examples=4860.0,loss=1.7142657041549683,accuracy=0.38683128356933594>'

现在,让我们编译联合的数据,然后重新运行评估的测试样品的测试数据。该数据将来自实际用户的同一样品,但是从不同的持有出来的数据集。

 federated_test_data = make_federated_data(emnist_test, sample_clients)

len(federated_test_data), federated_test_data[0]
 
(10,
 <DatasetV1Adapter shapes: OrderedDict([(x, (None, 784)), (y, (None, 1))]), types: OrderedDict([(x, tf.float32), (y, tf.int32)])>)
 test_metrics = evaluation(state.model, federated_test_data)
 
 str(test_metrics)
 
'<num_examples=580.0,loss=1.861915111541748,accuracy=0.3362068831920624>'

这结束了本教程。我们鼓励您使用参数(例如,批量大小,用户,时代,学习率等数量)来播放,修改上面在每一轮的用户随机抽样模拟训练的代码,并探索其他教程我们开发。