TensorFlow Federated : Tutorials : 画像分類のための Federated ラーニング (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/06/2019
* 本ページは、TensorFlow の本家サイトの TensorFlow Federated のチュートリアル – Federated Learning for Image Classification を翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
Tutorials : 画像分類のための Federated ラーニング
このチュートリアルでは、TFF の Federated ラーニング (FL) API コンポーネント – tff.learning を紹介するために古典的な MNIST 訓練サンプルを使用します。tff.learning は高位レベル・インターフェイスのセットで、TensorFlow で実装されたユーザ供給モデルに対する federated 訓練のような federated ラーニング・タスクの一般的なタイプを遂行するために使用されます。
このチュートリアル、そして Federated ラーニング API は、TFF を殆どブラックボックスとして扱いながら、彼ら自身の TensorFlow モデルを TFF に接続する (= plug) ことを望むユーザのために意図されています。TFF のより深い理解そして貴方自身の federated ラーニング・アルゴリズムをどのように実装するかのために、フォローアップとして低位インターフェイスのチュートリアルをレビューすることも考えてください – Custom Federated Algorithms Part 1 と Part 2。
tff.learning のより多くについては、テキスト生成のための Federated ラーニング で続けてください、このチュートリアルはリカレント・モデルをカバーすることに加えてまた、Keras を使用する評価と結合された federated ラーニングで洗練するために事前訓練されたシリアライズ化された Keras モデルをロードすることを示します。
Before we start
始める前に、貴方の環境が正しくセットアップされていることを確かなものにするために次のコマンドを実行してください。
# NOTE: If you are running a Jupyter notebook, and installing a locally built # pip package, you may need to edit the following to point to the '.whl' file # on your local filesystem. !pip install -q tensorflow_federated
from __future__ import absolute_import, division, print_function import collections from six.moves import range import numpy as np import tensorflow as tf from tensorflow.python.keras.optimizer_v2 import gradient_descent from tensorflow_federated import python as tff nest = tf.contrib.framework.nest np.random.seed(0) tf.enable_eager_execution() tf.enable_resource_variables() tf.compat.v1.enable_v2_behavior() tff.federated_computation(lambda: 'Hello, World!')()
WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0. For more information, please see: * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md * https://github.com/tensorflow/addons If you depend on functionality not listed there, please file an issue. 'Hello, World!'
入力データを準備する
データから始めましょう。Federated ラーニングは federated データセットを必要とします、i.e. 複数のユーザからのデータのコレクションです。federated データは典型的には非 i.i.d. です、これは特有の挑戦を課します。
実験を容易にするために、TFF レポジトリに幾つかのデータセットの種をまいています、これは Leaf を使用して再処理された 元の NIST データセット のバージョンを含む MNIST の federated バージョンを含みます。それはデータが Leaf を使用して再処理されています。その結果、データは数字の元の書き手によりキー付けされています。各書き手はユニークなスタイルを持ちますので、データセットは federated データセットに期待されるある種の非 i.i.d. 挙動を示します。
ここにそれをどのようにロードできるかがあります。
#@test {"output": "ignore"} emnist_train, emnist_test = tff.simulation.datasets.emnist.load_data()
Downloading data from https://storage.googleapis.com/tff-datasets-public/fed_emnist_digitsonly.tar.bz2 97402880/97398400 [==============================] - 2s 0us/step
load_data() で返されるデータセット群は、特定のユーザのデータを表わす tf.data.Dataset を構築して個々の要素の構造を問い合わせる tff.simulation.ClientData のインスタンス群で、インターフェイスはユーザのセットを列挙することを可能にします。ここにデータセットの内容を調査するためにこのインターフェイスをどのように使用できるかがあります。このインターフェイスがクライアント id に渡り列挙することを貴方に可能にする一方で、それはシミュレーション・データの特徴に過ぎないことに留意してください。すぐに見るように、クライアント識別子は federated ラーニング・フレームワークでは使用されません – それらの唯一の目的はシミュレーションのためにデータのサブセットを選択することを可能にすることです。
len(emnist_train.client_ids)
3383
emnist_train.output_types, emnist_train.output_shapes
(OrderedDict([('label', tf.int32), ('pixels', tf.float32)]), OrderedDict([('label', TensorShape([])), ('pixels', TensorShape([28, 28]))]))
example_dataset = emnist_train.create_tf_dataset_for_client( emnist_train.client_ids[0]) example_element = iter(example_dataset).next() example_element['label'].numpy()
WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/tensorflow/python/data/ops/iterator_ops.py:532: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. Instructions for updating: Colocations handled automatically by placer. 5
#@test {"output": "ignore"} from matplotlib import pyplot as plt plt.imshow(example_element['pixels'].numpy(), cmap='gray', aspect='equal') plt.grid('off') _ = plt.show()
/usr/local/lib/python3.5/dist-packages/matplotlib/cbook/__init__.py:424: MatplotlibDeprecationWarning: Passing one of 'on', 'true', 'off', 'false' as a boolean is deprecated; use an actual boolean (True/False) instead. warn_deprecated("2.2", "Passing one of 'on', 'true', 'off', 'false' as a " <Figure size 640x480 with 1 Axes>
データは既に tf.data.Dataset ですから、前処理は Dataset 変換を使用して成されます。ここでは、28×28 画像を 784-要素配列に平坦化し、個々のサンプルをシャッフルし、それらをバッチに体系化し、そして Keras での使用のためにピクセルからの特徴とラベルを x と y に名前変更します。幾つかのエポックを実行するためにデータセットに渡り repeat もはさみます。
NUM_EPOCHS = 10 BATCH_SIZE = 20 SHUFFLE_BUFFER = 500 def preprocess(dataset): def element_fn(element): return collections.OrderedDict([ ('x', tf.reshape(element['pixels'], [-1])), ('y', element['label'])]) return dataset.repeat(NUM_EPOCHS).map(element_fn).shuffle(SHUFFLE_BUFFER).batch(BATCH_SIZE)
これが動作するか検証しましょう。
#@test {"output": "ignore"} preprocessed_example_dataset = preprocess(example_dataset) sample_batch = nest.map_structure( lambda x: x.numpy(), iter(preprocessed_example_dataset).next()) 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([5, 3, 2, 4, 7, 3, 6, 1, 5, 9, 0, 9, 1, 8, 8, 9, 0, 1, 3, 7], dtype=int32))])
federated データセットを構築するための殆ど総てのビルディング・ブロックを適所に持ちます。
シミュレーションで federated データを 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]
さて、どのようにクライアントを選びましょうか?
典型的な federated 訓練シナリオでは、潜在的にユーザデバイスの非常に巨大な集団を扱っており、時間で与えられたポイントで訓練のためにそれらの断片だけが利用可能かもしれません。例えばこれは、クライアントデバイスが電源に接続され、計測ネットワークから離れて (= off a metered network) いるときに限り (さもなければアイドルしています) 訓練に参加しているモバイルフォンであるときに当てはまります。
もちろん、私達はシミュレーション環境にあり、総てのデータはローカルで利用可能です。それで典型的には、シミュレーションを実行するとき、訓練の各ラウンドで関係するクライアントの (一般には各ラウンドで異なる) ランダムなサブセットから単純にサンプリングするでしょう。
とは言え、Federated Averaging アルゴリズムのペーパーを研究することにより見い出せるように、各ラウンドでクライアントのランダムにサンプリングされたサブセットを持つシステムで収束を獲得することは時間がかかる可能性があり、そしてこの対話的なチュートリアルで数百のラウンドを実行しなければならないことは実践的ではありません。
従って、代わりに私達が行なうことはクライアントのセットから一度サンプリングして、収束をスピードアップするためにラウンド群に渡り同じセットを再利用することです (これらの少ないユーザのデータに意図的に over-fitting させます)。ランダムサンプリングをシミュレートするためにこのチュートリアルを変更することは読者のための課題として残します – 行なうことは非常に容易です (ひとたびそれを行えば、モデルを収束させることは時間がかかるかもしれないことに留意してください)。
#@test {"output": "ignore"} NUM_CLIENTS = 3 sample_clients = emnist_train.client_ids[0:NUM_CLIENTS] federated_train_data = make_federated_data(emnist_train, sample_clients) len(federated_train_data), federated_train_data[0]
(3, <DatasetV1Adapter shapes: OrderedDict([(x, (None, 784)), (y, (None,))]), types: OrderedDict([(x, tf.float32), (y, tf.int32)])>)
Keras でモデルを作成する
Keras を使用しているのであれば、既に Keras モデルを構築するコードを持っていることでしょう。ここの私達のニーズを満たす単純なモデルの例があります。
def create_compiled_keras_model(): model = tf.keras.models.Sequential([ tf.keras.layers.Dense( 10, activation=tf.nn.softmax, kernel_initializer='zeros', input_shape=(784,))]) def loss_fn(y_true, y_pred): return tf.reduce_mean(tf.keras.metrics.sparse_categorical_crossentropy( y_true, y_pred)) model.compile( loss=loss_fn, optimizer=gradient_descent.SGD(learning_rate=0.02), metrics=[]) return model
compile についての一つの重要なノートです。下のように、Federated Averaging アルゴリズムで使用されるとき、optimizer はトータルな最適化アルゴリズムの半分に過ぎません、何故ならばそれは各クライアント上のローカル・モデル更新を計算するために使用されるだけだからです。アルゴリズムの残りはこれらの更新がクライアントに渡りどのように平均され、そしてそれらがサーバのグローバル・モデルにどのように適用されるかを伴います。特に、これはここで使用される optimizer と学習率の選択は標準的な i.i.d. データセット上のモデルを訓練するために使用したものとは違う必要があるかもしれないことを意味します。標準的な SGD で、おそらくは通常よりも小さい学習率で開始することを推奨します。ここで使用する学習率は注意深く調整されていません、自由に実験してください。
TFF で任意のモデルを使用するために、それは tff.learning.Model インターフェイスのインスタンスでラップされる必要があります、そのインターフェイスは Keras と同様に、モデルの forward パス、メタデータ・プロパティ, etc., をマークするためにメソッドを公開しますが、federated メトリクスを計算するプロセスを制御する方法のような、追加の要素も導入します。これについては今のところ心配しないでください ; 上で定義したようなコンパイルされた Keras モデルを持つ場合、下で示されるように、tff.learning.from_compiled_keras_model を起動して、引数としてモデルとサンプルデータ・バッチを渡すことにより、貴方のために TFF にそれをラップさせることができます。
def model_fn(): keras_model = create_compiled_keras_model() return tff.learning.from_compiled_keras_model(keras_model, sample_batch)
federated データ上でモデルを訓練する
TFF での使用のために tff.learning.Model としてラップされたモデルを持つ今、ヘルパー関数 tff.learning.build_federated_averaging_process を次のように起動することにより TFF に Federated Averaging アルゴリズムを構築させることができます。
引数は既にコンストラクトされたインスタンスではなく (上の model_fn のような) コンストラクタである必要があることに留意してください、その結果モデルのコンストラクションは TFF で制御されるコンテキストで発生します (この理由について好奇心があれば、カスタム・アルゴリズム の follow-up チュートリアルを読むことを勧めます)。
iterative_process = tff.learning.build_federated_averaging_process(model_fn)
何が起きたのでしょう?TFF は一対の federated 計算を構築してそれらを tff.utils.IterativeProcess にパッケージしています、そこではこれらの計算が一対のプロパティ initialize と next として利用可能です。
簡単に言えば、federated 計算は様々な federated アルゴリズムを表現できる TFF の内部言語のプログラムです (これについて カスタム・アルゴリズム でより多くを見つけることができます)。このケースでは、生成されて iterative_process にパックされた 2 つの計算は Federated Averaging を実装しています。
現実の federated ラーニング設定で実行されるような方法で計算を定義することは TFF のゴールですが、現在はローカル実行シミュレーション・ランタイムだけが実装されています。計算をシミュレータで実行するために、単純にそれを Python 関数のように起動します。このデフォルトのインタープリットされた環境は高いパフォーマンスのために設計されていませんが、このチュートリアルのためには十分です ; 将来的なリリースでより大きなスケールの研究を手助けするためにより高いパフォーマンスのシミュレーション・ランタイムを提供することを期待します。
initialize 計算から始めましょう。総ての federated 計算の場合のように、それを関数として考えることができます。計算は引数を取らず、一つの結果を返します – サーバ上の Federated Averaging プロセスの状態の表現です。TFF の詳細に潜ることを望まない一方で、この状態がどのように見えるかを見ることはためになるかもしれません。次のようにそれを可視化できます。
#@test {"output": "ignore"} str(iterative_process.initialize.type_signature)
'( -> <model=<trainable=<dense/kernel=float32[784,10],dense/bias=float32[10]>,non_trainable=<>>,optimizer_state=<int64>>@SERVER)'
上の型シグネチャは最初は少し暗号のように見えるかもしれない一方で、サーバ状態がモデル (総てのデバイスに分散される MNIST のための初期モデル・パラメータ) と optimizer_state (ハイパーパラメータ・スケジュール, etc. のために使用するラウンド数のような、サーバにより維持される追加情報) から成ることを認識できるでしょう。
サーバ状態を構築するために initialize 計算を呼び出しましょう。
state = iterative_process.initialize()
federated 計算のペアの 2 番目, next は Federated Averaging の単一のラウンドを表します、これは (モデルパラメータを含む) サーバ状態をプッシュし、ローカルデータ上でオンデバイス訓練をし、収集し、そしてモデル更新を平均し、そしてサーバで新しい更新されたモデルを生成します。
概念的には、next を次のように見える関数型シグネチャを持つものとして考えることができます。
SERVER_STATE, FEDERATED_DATA -> SERVER_STATE, TRAINING_METRICS
特に、next() をサーバ上で動作する関数として考えるべきではありません、寧ろ全体的な分散された計算の宣言的な関数型表現です – 入力の一部はサーバ (SERVER_STATE) により提供されますが、各参加しているデバイスはそれ自身のローカルデータセットを与えます。
訓練の単一ラウンドを実行して結果を可視化しましょう。ユーザのサンプルのために上で既に生成した federated データを使用できます。
#@test {"timeout": 600, "output": "ignore"} state, loss = iterative_process.next(state, federated_train_data) print('round 1, loss={:.4f}'.format(loss))
round 1, loss=2.9608
更に 2, 3 ラウンドを実行しましょう。前にノートしたように、ユーザが連続的に出入りするような現実的な配備をシミュレートするために、典型的にはこの時点で各ラウンドのためにユーザの新しくランダムに選択されたサンプルからシミュレーション・データのサブセットを選択するでしょう、しかしこの対話的ノートブックでは、デモのために単に同じユーザを再利用します、その結果システムは迅速に収束します。
#@test {"skip": true} for round_num in range(2, 11): state, loss = iterative_process.next(state, federated_train_data) print('round {:2d}, loss={:.4f}'.format(round_num, loss))
round 2, loss=2.9340 round 3, loss=2.7860 round 4, loss=2.4084 round 5, loss=2.0972 round 6, loss=2.0610 round 7, loss=1.8308 round 8, loss=1.6044 round 9, loss=1.4647 round 10, loss=1.3858
モデル実装をカスタマイズする
Keras は TensorFlow のための推奨される高位モデル API で、可能なときはいつでも TFF で (tff.learning.from_keras_model または tff.learning.from_compiled_keras_model 経由で) Keras モデルの使用を勧めます。
けれども、tff.learning は低位モデル・インターフェイス, tff.learning.Model を提供します、これは federated ラーニングのためのモデルを使用するために必要な最小機能を公開します。このインターフェイス (多分依然として tf.keras.layers のようなビルディング・ブロックを使用します) を直接的に実装することは federated ラーニング・アルゴリズムの内部を変更することなく最大限のカスタマイゼーションを可能にします。
そこでスクラッチからもう一度それを行ないましょう。
モデル変数、forward パス、そしてメトリクスを定義する
最初のステップはそれで作業していく TensorFlow 変数を識別することです。次のコードをより読みやすくするために、セット全体を表わすデータ構造を定義しましょう。これは訓練する重みとバイアスのような変数と、loss_sum, accuracy_sum, と num_examples のような訓練の間に更新する様々な累積する統計値とカウンターを保持する変数を含みます。
MnistVariables = collections.namedtuple( 'MnistVariables', 'weights bias num_examples loss_sum accuracy_sum')
ここに変数を作成するメソッドがあります。単純化のために、総ての統計を tf.float32 として表します、それは後のステージで型変換の必要性を除去するからです。変数 initializer を lambda としてラップするのは resource 変数 により課せられた要件です。
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))
モデル・パラメータと適所の累積する統計のための変数により、今では入力データの単一のバッチのために損失を計算し、予測を出し、そして累積する統計を更新する forward パスメソッドを、次のように定義できます。
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) loss = -tf.reduce_mean(tf.reduce_sum( tf.one_hot(batch['y'], 10) * tf.log(y), reduction_indices=[1])) accuracy = tf.reduce_mean( tf.cast(tf.equal(predictions, batch['y']), tf.float32)) num_examples = tf.to_float(tf.size(batch['y'])) tf.assign_add(variables.num_examples, num_examples) tf.assign_add(variables.loss_sum, loss * num_examples) tf.assign_add(variables.accuracy_sum, accuracy * num_examples) return loss, predictions
次に、再度 TensorFlow を使用してローカル・メトリクスを返す関数を定義します。これらは (自動的に処理されるモデル更新に加えて、) federated ラーニングや評価プロセスでサーバに集められるに適格な変数です。
ここで、平均損失と精度そして num_examples を単純に返します、federated 集合を計算するときそれらは異なるユーザからの貢献を正しく重み付ける必要があります。
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 で表わされる federated 計算です。より深く探索しないのであれば、カスタム・アルゴリズム チュートリアルを一読してください、しかし多くのアプリケーションでは、実際には必要ありません ; 下で示されるパターンの変種で十分であるはずです。ここにそれがどのように見えるかがあります :
@tff.federated_computation def aggregate_mnist_metrics_across_clients(metrics): return { 'num_examples': tff.federated_sum(metrics.num_examples), 'loss': tff.federated_average(metrics.loss, metrics.num_examples), 'accuracy': tff.federated_average(metrics.accuracy, metrics.num_examples) }
入力メトリクス引数は上の get_local_mnist_metrics で返される OrderedDict に対応しますが、決定的に値はもはや tf.Tensors ではありません – それらは tff.Values として “boxed (箱詰め)” されています、それを明らかにするとそれらを TensorFlow を使用してもはや操作できませんが、tff.federated_average and tff.federated_sum のような TFF の federated 演算子だけが使用できます。グローバルな集合の返された辞書はメトリクスのセットを定義します、これはサーバで利用可能です。
tff.learning.Model のインスタンスを構築する
適所の上の総てにより、TFF に Keras モデルを摂取させたときに貴方のために生成されたものに類似した、 TFF で使用するためのモデル表現を構築する準備ができました。
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], tf.int32))]) @tf.contrib.eager.function(autograph=False) def forward_pass(self, batch, training=True): del training loss, predictions = mnist_forward_pass(self._variables, batch) return tff.learning.BatchOutput(loss=loss, predictions=predictions) @tf.contrib.eager.function(autograph=False) 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 ロジック (forward パス、メトリック計算) を tf.contrib.eager.functions としてラップすることを推奨します、何故ならばこれは TensorFlow がシリアライズ化できることを確実にすることを助け、そして明示的な制御依存性の必要性を除去できるからです。
上は評価と Federated SGD のようなアルゴリズムのためには十分です。けれども、Federated Averaging のためには、モデルを各バッチ上でどのようにローカルで訓練するかを指定する必要があります。
class MnistTrainableModel(MnistModel, tff.learning.TrainableModel): @tf.contrib.eager.defun(autograph=False) def train_on_batch(self, batch): output = self.forward_pass(batch) optimizer = tf.train.GradientDescentOptimizer(0.02) optimizer.minimize(output.loss, var_list=self.trainable_variables) return output
新しいモデルで federated 訓練をシミュレートする
適所の上の総てにより、プロセスの残りは既に見たもののように見えます – 単にモデル・コンストラクタを新しいモデル・クラスのコンストラクタで置き換え、訓練ラウンドを通して繰り返すための作成した iterative プロセスで 2 つの federated 計算を使用します。
iterative_process = tff.learning.build_federated_averaging_process( MnistTrainableModel)
WARNING:tensorflow:From:10: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead. WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead.
state = iterative_process.initialize()
#@test {"timeout": 600, "output": "ignore"} state, metrics = iterative_process.next(state, federated_train_data) print('round 1, metrics={}'.format(metrics))
round 1, metrics=
#@test {"skip": true} 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=<accuracy=0.15927273,loss=2.8832564,num_examples=2750.0> round 3, metrics=<accuracy=0.18399999,loss=2.6661496,num_examples=2750.0> round 4, metrics=<accuracy=0.25781816,loss=2.2566185,num_examples=2750.0> round 5, metrics=<accuracy=0.29309088,loss=2.1758642,num_examples=2750.0> round 6, metrics=<accuracy=0.356,loss=1.9697735,num_examples=2750.0> round 7, metrics=<accuracy=0.37854546,loss=1.837882,num_examples=2750.0> round 8, metrics=<accuracy=0.4341818,loss=1.726984,num_examples=2750.0> round 9, metrics=<accuracy=0.4698182,loss=1.5935063,num_examples=2750.0> round 10, metrics=<accuracy=0.5087273,loss=1.4720244,num_examples=2750.0>
評価
ここまでの実験の総ては federated 訓練メトリクス – ラウンドの総てのクライアントに渡り訓練されたデータの総てのバッチに渡る平均メトリクスのみを提示しています。これは overfitting に関する通常の関心を引き起こします、特に単純化のために各ラウンドでクライアントの同じセットを使用しましたので、しかし Federated Averaging アルゴリズムに特有の訓練プロセスにおける overfitting の追加的注意もあります。これを見るには、各クライアントがデータの単一のバッチを持つことを想像して、そしてそのバッチ上で多くの反復 (epochs) を訓練することを考えれば容易です。この場合、ローカルモデルは迅速に正確にその一つのバッチに fit するでしょう、そして平均したローカル精度メトリックは 1.0 に近づきます。このように、これらの訓練メトリクスは訓練が進捗している兆候として取ることができますが、それ以上のものではありません。
federated データ上で評価を遂行するために、ちょうどこの目的のために設計されたもう一つの federated 計算を構築できます、tff.learning.build_federated_evaluation 関数を使用して、モデル・コンストラクタを引数として渡します。Federated Averaging とは違い、そこでは MnistTrainableModel を使用しました、それは MnistModel を渡せば十分です。評価は勾配降下を遂行しません、そして optimizer を構築する必要はありません。
実験と研究のために、中央集中したテスト・データセットが利用可能なとき、テキスト生成のための Federated ラーニング はもう一つの評価オプションを示します : federated ラーニングから訓練された重みを取り、それらを標準的な Keras モデルに適用し、そしてそれから単純に中央集中されたデータセットに tf.keras.models.Model.evaluate() を呼び出します。
evaluation = tff.learning.build_federated_evaluation(MnistModel)
evaluation 関数の抽象型シグネチャを次のように調査できます。
str(evaluation.type_signature)
'(<<trainable=<weights=float32[784,10],bias=float32[10]>,non_trainable=<>>@SERVER,{<x=float32[?,784],y=int32[?]>*}@CLIENTS> -> <accuracy=float32@SERVER,loss=float32@SERVER,num_examples=float32@SERVER>)'
この時点で詳細を気にする必要はありませんが、それは次の一般的な形式を取ることだけは認識してください、tff.utils.IterativeProcess.next に類似していますが 2 つの重要な違いを伴います。一つ目は、サーバ状態を返していません、何故ならば評価はモデルあるいは状態の任意の他の様相を変更しないからです – それをステートレスと考えることができます。2 番目に、評価はモデルを必要とするだけです、そして optimizer 変数のような訓練に関連するかもしれないサーバー状態の任意の他のパートを必要としません。
SERVER_MODEL, FEDERATED_DATA -> TRAINING_METRICS
訓練の間に到達した最新の状態で evaluation を起動しましょう。サーバー状態から最新の訓練モデルを抽出するためには次のように、.model メンバーに単にアクセスします。
#@test {"output": "ignore"} train_metrics = evaluation(state.model, federated_train_data)
ここに私達が得たものがあります。数字は上の訓練の最後のラウンドから報告されたものよりも僅かばかり良いようであることに注意してください。慣習により、反復訓練プロセスにより報告された訓練メトリクスは訓練ラウンドの最初にモデルの性能を一般に反映しますので、評価メトリクスは常にワンステップ進んでいます。
#@test {"output": "ignore"} str(train_metrics)
'<accuracy=0.6545454,loss=1.1885377,num_examples=2750.0>'
さて、federated データのテスト・サンプルをコンパイルしてテストデータ上の評価を再実行しましょう。データは実際のユーザの同じサンプルに由来しますが、異なる取り置いたデータセットからです。
federated_test_data = make_federated_data(emnist_test, sample_clients) len(federated_test_data), federated_test_data[0]
(3, <DatasetV1Adapter shapes: OrderedDict([(x, (None, 784)), (y, (None,))]), types: OrderedDict([(x, tf.float32), (y, tf.int32)])>)
#@test {"output": "ignore"} test_metrics = evaluation(state.model, federated_test_data)
#@test {"output": "ignore"} str(test_metrics)
'<accuracy=0.6666667,loss=1.3322104,num_examples=330.0>'
チュートリアルは以上です。上で注意したように、パラメータ (e.g., バッチサイズ、ユーザ数、エポック、学習率, etc.) で遊び、各ラウンドでユーザのランダムサンプル上の訓練をシミュレートするために上のコードを変更し、そして私達が開発した他のチュートリアルを探究することを勧めます。
以上