From f09096c23ba262c62592cf67c75dfbcac24c7f26 Mon Sep 17 00:00:00 2001 From: NoelBird Date: Mon, 15 Jul 2019 22:03:59 +0900 Subject: [PATCH 1/6] add REVIEWER 'NoelBird' --- site/ko/REVIEWERS | 1 + 1 file changed, 1 insertion(+) diff --git a/site/ko/REVIEWERS b/site/ko/REVIEWERS index 2a66f3bbd6f..8ae66f89980 100644 --- a/site/ko/REVIEWERS +++ b/site/ko/REVIEWERS @@ -9,3 +9,4 @@ rickiepark cre8tor choiuijin1125 JKIsaacLee +NoelBird \ No newline at end of file From a56bbe2f28de623901d6370fce6dd612092b29c5 Mon Sep 17 00:00:00 2001 From: noelbird Date: Wed, 31 Jul 2019 14:09:50 +0900 Subject: [PATCH 2/6] add ko/beta/tutorials/distribute/training_loops.ipynb --- .../tutorials/distribute/training_loops.ipynb | 775 ++++++++++++++++++ 1 file changed, 775 insertions(+) create mode 100644 site/ko/beta/tutorials/distribute/training_loops.ipynb diff --git a/site/ko/beta/tutorials/distribute/training_loops.ipynb b/site/ko/beta/tutorials/distribute/training_loops.ipynb new file mode 100644 index 00000000000..e4e66ea9eab --- /dev/null +++ b/site/ko/beta/tutorials/distribute/training_loops.ipynb @@ -0,0 +1,775 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "training_loops.ipynb의 사본", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MhoQ0WE77laV" + }, + "source": [ + "##### Copyright 2019 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "both", + "colab_type": "code", + "id": "_ckMIh7O7s6D", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jYysdyb-CaWM" + }, + "source": [ + "# 훈련 루프와 함께 tf.distribute.Strategy 사용하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S5Uhzt6vVIB2" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " TensorFlow.org에서 보기\n", + " \n", + " 구글 코랩(Colab)에서 실행하기\n", + " \n", + " 깃허브(GitHub) 소스 보기\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "tUP8LMdYtWPz" + }, + "source": [ + "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 [공식 영문 문서](https://github.com/tensorflow/docs/blob/master/site/en/r2/guide/using_gpu.ipynb)의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 [tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 [docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로 메일을 보내주시기 바랍니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FbVhjPpzn6BM" + }, + "source": [ + "이 예제는 사용자 정의 훈련 루프(custom training loops)와 함께 [`tf.distribute.Strategy`](https://www.tensorflow.org/guide/distribute_strategy)를 사용 하는법을 보여드립니다. 우리는 간단한 CNN 모델을 패션 MNIST 데이터셋에 대해 훈련을 할 것 입니다. 패션 MNIST 데이터셋은 60000개의 28 x 28 크기의 훈련 이미지들과 10000개의 28 x 28 크기의 평가 이미지들을 포함하고 있습니다.\n", + "\n", + "이 예제는 모델을 훈련하기 위해서 사용자 정의 훈련 루프를 사용하고 있습니다. 왜냐하면 사용자 정의 훈련 루프는 유연하며 훈련을 더욱 잘 수행하도록 해주기 때문입니다. 또한, 모델과 훈련 루프를 디버깅하기도 더 쉽습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "dzLKpmZICaWN", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function, unicode_literals\n", + "\n", + "# 텐서플로우 임포트\n", + "!pip install tensorflow-gpu==2.0.0-beta1\n", + "import tensorflow as tf\n", + "\n", + "# 헬퍼 라이브러리들\n", + "import numpy as np\n", + "import os\n", + "\n", + "print(tf.__version__)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MM6W__qraV55" + }, + "source": [ + "## 패션 MNIST 데이터셋을 다운로드" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7MqDQO0KCaWS", + "colab": {} + }, + "source": [ + "fashion_mnist = tf.keras.datasets.fashion_mnist\n", + "\n", + "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()\n", + "\n", + "\n", + "# 하나의 차원을 배열에 추가 -> 새로운 shape == (28, 28, 1)\n", + "# 이렇게 하는 이유는 우리의 모델에서 첫 번째 층이 합성곱 층이고\n", + "# 합성곱 층은 4D 입력을 요구하기 때문입니다.\n", + "# (batch_size, height, width, channels).\n", + "# batch_size 차원은 나중에 추가할 것입니다.\n", + "\n", + "train_images = train_images[..., None]\n", + "test_images = test_images[..., None]\n", + "\n", + "# 이미지를 [0, 1] 범위로 변경하기.\n", + "train_images = train_images / np.float32(255)\n", + "test_images = test_images / np.float32(255)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4AXoHhrsbdF3" + }, + "source": [ + "## 변수들과 그래프를 분배하는 전략 만들기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "5mVuLZhbem8d" + }, + "source": [ + "`tf.distribute.MirroredStrategy` 전략이 어떻게 동작할까요?\n", + "* 모든 변수와 모델의 그래프는 장치(replicas, 다른 문서에서는 replica가 분산 훈련에서 장치 등에 복제된 모델을 의미하는 경우가 있으나 이 문서에서는 장치 자체를 의미합니다)들에 복제됩니다.\n", + "* 입력은 장치에 고르게 분배되어 들어갑니다.\n", + "* 각 장치는 주어지는 입력에 대해서 손실(loss)과 그래디언트를 계산합니다.\n", + "* 그래디언트들을 전부 더함으로써 모든 장치들 간에 그래디언트들이 동기화됩니다.\n", + "* 동기화된 후에, 동일한 업데이트가 각 장치에 있는 변수의 복사본(copies)에 동일하게 적용됩니다.\n", + "\n", + "노트: 여러분은 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "F2VeZUWUj5S4", + "colab": {} + }, + "source": [ + "# 만약 장치들의 목록이 `tf.distribute.MirroredStrategy` 생성자 안에 명시되어 있지 않다면,\n", + "# 자동으로 장치들의 목록이 인식될 것 입니다.\n", + "strategy = tf.distribute.MirroredStrategy()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ZngeM_2o0_JO", + "colab": {} + }, + "source": [ + "print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "k53F5I_IiGyI" + }, + "source": [ + "## 입력 파이프라인 설치하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0Qb6nDgxiN_n" + }, + "source": [ + "그래프와 변수들을 플랫폼을 몰라도 사용할 수 있는(platform-agnostic) 저장된 모델(SavedModel) 형식으로 내보냅니다. 모델을 내보내셨다면, 여러분은 모델을 불러올 때 범위(scope)를 지정해도 되고 하지 않으셔도 됩니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jwJtsCQhHK-E", + "colab": {} + }, + "source": [ + "BUFFER_SIZE = len(train_images)\n", + "\n", + "BATCH_SIZE_PER_REPLICA = 64\n", + "GLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync\n", + "\n", + "EPOCHS = 10" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "J7fj3GskHC8g" + }, + "source": [ + "분산 데이터셋들을 `strategy.scope` 내에 생성합니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "WYrMNNDhAvVl", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + "\n", + " train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).shuffle(BUFFER_SIZE).batch(GLOBAL_BATCH_SIZE) \n", + " train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)\n", + " \n", + " test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE) \n", + " test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bAXAo_wWbWSb" + }, + "source": [ + "## 모델 만들기\n", + "\n", + "`tf.keras.Sequential`을 사용해서 모델을 생성합니다. 여러분은 Model Subclassing API로도 모델 생성을 할 수 있습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9ODch-OFCaW4", + "colab": {} + }, + "source": [ + "def create_model():\n", + " model = tf.keras.Sequential([\n", + " tf.keras.layers.Conv2D(32, 3, activation='relu'),\n", + " tf.keras.layers.MaxPooling2D(),\n", + " tf.keras.layers.Conv2D(64, 3, activation='relu'),\n", + " tf.keras.layers.MaxPooling2D(),\n", + " tf.keras.layers.Flatten(),\n", + " tf.keras.layers.Dense(64, activation='relu'),\n", + " tf.keras.layers.Dense(10, activation='softmax')\n", + " ])\n", + "\n", + " return model" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9iagoTBfijUz", + "colab": {} + }, + "source": [ + "# 체크포인트들을 저장하기 위해서 체크포인트 디렉토리를 생성합니다.\n", + "checkpoint_dir = './training_checkpoints'\n", + "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "e-wlFFZbP33n" + }, + "source": [ + "## 손실 함수 정의하기\n", + "\n", + "일반적으로, 1의 GPU/CPU 비율을 가지고 있는 단일 장치에서 손실은 배치(batch) 입력에서의 장치의 개수로 나누어집니다.\n", + "\n", + "*그렇다면, `tf.distribute.Strategy`를 사용할 때, 손실은 어떻게 계산되어야 할까요?*\n", + "\n", + "* 예를들면, 4개의 GPU가 있고 배치 입력 크기가 64라고 하죠. 배치 입력은 장치(4개의 GPU)에 분배됩니다. 각 장치는 16의 입력크기를 가지게 됩니다.\n", + "\n", + "* 각 장치에 있는 모델은 각 입력을 앞쪽으로 넘겨주고(forward pass) 손실을 계산합니다. 손실을 각각의 장치당 배치크기(BATCH_SIZE_PER_REPLICA = 16)로 나누는 것 대신에, 손실은 GLOBAL_BATCH_SIZE(64)로 나누어져야 합니다.\n", + "\n", + "*왜 이렇게 할까요?*\n", + "\n", + "* 위와 같이 계산하는 이유는 그래디언트들이 각 복제 모델에서 계산된 다음, 복제 모델들 간에 그래디언트 값들을 **전부 더함(summing)**으로써 동기화가 되기 때문입니다.\n", + "\n", + "*이 것을 텐서플로우에서는 어떻게 할까요?*\n", + "\n", + "\n", + "* 만약 여러분이 이 예제에서 처럼 사용자 정의 훈련 루프를 작성하고 있다면, 여러분은 장치당 손실을 더하고 GLOBAL_BATCH_SIZE로 나누어줘야 합니다.\n", + "`scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE)`\n", + "또는 여러분은 `tf.nn.compute_average_loss`를 사용할 수도 있습니다. 이 함수는 장치당 손실, 선택적 샘플 가중치(optional sample weights)와 GLOBAL_BATCH_SIZE를 매개변수로 취하면서 스케일이 조정된 손실을 반환합니다.\n", + "\n", + "* 만약 여러분이 여러분의 모델에서 손실 정규화를 사용하고 있다면, 여러분은 손실을 장치의 수로 스케일 조정을 할 필요가 있습니다. `tf.nn_scale_regularization_loss` 함수를 사용함으로써 스케일 조정을 할 수 있습니다.\n", + "\n", + "* `tf.reduce_mean`을 사용하는 것은 추천하지 않습니다. 이렇게 하는 것은 손실을 실제 장치당 배치 크기로 나눕니다. 이 실제 장치당 배치 크기는 아마 각 단계(step)마다 크기가 다를 수 있습니다.\n", + "\n", + "* 이런 reduction과 스케일 조정은 케라스의 `model.compile`과 `model.fit`에서 자동적으로 됩니다.\n", + "\n", + "* 만약 `tf.keras.losses` 클래스(아래의 예제에서처럼)를 사용한다면, reduction 매개변수를 명시적으로 `NONE` 또는 `SUM` 중 하나로 표시해야 합니다. `AUTO`와 `SUM_OVER_BATCH_SIZE`는 `tf.distribute.Strategy`를 함께 사용하는 경우에는 허용되지 않습니다. `AUTO`가 허용되지 않는 것은 사용자가 분산 케이스(distributed case)에서 어떤 reduction을 만들고 싶은지 명시적으로 생각하는 것이 올바르기 때문입니다.\n", + "`SUM_OVER_BATCH_SIZE`가 허용되지 않는 것은 `SUM_OVER_BATCH_SIZE`는 장치당 배치 크기로만 나눈 다음에 장치의 수로 나눈 것을 사용자에게 반환하기 때문입니다(reduction이 되어지지 않은 채로). 이 점은 아마도 놓치기 쉬을거에요. 그래서 우리는 이렇게 하는 것 대신에 사용자들에게 reduction을 사용자 스스로 명시적으로 하기를 요청합니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "R144Wci782ix", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " # reduction을 `none`으로 설정합니다. 그래서 우리는 reduction을 나중에 하고,\n", + " # global batch size로 나눌 수 있습니다.\n", + " loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n", + " reduction=tf.keras.losses.Reduction.NONE)\n", + " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy로 하셔도 됩니다.\n", + " def compute_loss(labels, predictions):\n", + " per_example_loss = loss_object(labels, predictions)\n", + " return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "w8y54-o9T2Ni" + }, + "source": [ + "## 손실과 정확도를 따르는 지표 정의하기\n", + "\n", + "이 지표(metrics)는 평가에서의 손실과 훈련과 평가에서의 정확도를 추적합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "zt3AHb46Tr3w", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " test_loss = tf.keras.metrics.Mean(name='test_loss')\n", + "\n", + " train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='train_accuracy')\n", + " test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='test_accuracy')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iuKuNXPORfqJ" + }, + "source": [ + "## 훈련 루프" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "OrMmakq5EqeQ", + "colab": {} + }, + "source": [ + "# 모델과 옵티마이저는 `strategy.scope`에서 만들어져야 합니다.\n", + "with strategy.scope():\n", + " model = create_model()\n", + "\n", + " optimizer = tf.keras.optimizers.Adam()\n", + "\n", + " checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "3UX43wUu04EL", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " def train_step(inputs):\n", + " images, labels = inputs\n", + "\n", + " with tf.GradientTape() as tape:\n", + " predictions = model(images, training=True)\n", + " loss = compute_loss(labels, predictions)\n", + "\n", + " gradients = tape.gradient(loss, model.trainable_variables)\n", + " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", + "\n", + " train_accuracy.update_state(labels, predictions)\n", + " return loss \n", + "\n", + " def test_step(inputs):\n", + " images, labels = inputs\n", + "\n", + " predictions = model(images, training=False)\n", + " t_loss = loss_object(labels, predictions)\n", + "\n", + " test_loss.update_state(t_loss)\n", + " test_accuracy.update_state(labels, predictions)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "gX975dMSNw0e", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " # `experimental_run_v2`는 주어진 계산을 복사하고,\n", + " # 분산 입력(distributed input)을 입력값으로 계산을 실행합니다.\n", + " \n", + " @tf.function\n", + " def distributed_train_step(dataset_inputs):\n", + " per_replica_losses = strategy.experimental_run_v2(train_step,\n", + " args=(dataset_inputs,))\n", + " return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,\n", + " axis=None)\n", + " \n", + " @tf.function\n", + " def distributed_test_step(dataset_inputs):\n", + " return strategy.experimental_run_v2(test_step, args=(dataset_inputs,))\n", + "\n", + " for epoch in range(EPOCHS):\n", + " # 훈련 루프\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " for x in train_dist_dataset:\n", + " total_loss += distributed_train_step(x)\n", + " num_batches += 1\n", + " train_loss = total_loss / num_batches\n", + "\n", + " # 평가 루프\n", + " for x in test_dist_dataset:\n", + " distributed_test_step(x)\n", + "\n", + " if epoch % 2 == 0:\n", + " checkpoint.save(checkpoint_prefix)\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, \"\n", + " \"Test Accuracy: {}\")\n", + " print (template.format(epoch+1, train_loss,\n", + " train_accuracy.result()*100, test_loss.result(),\n", + " test_accuracy.result()*100))\n", + "\n", + " test_loss.reset_states()\n", + " train_accuracy.reset_states()\n", + " test_accuracy.reset_states()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Z1YvXqOpwy08" + }, + "source": [ + "위의 예제에서 주목해야 하는 부분\n", + "* 이 예제는 `train_dist_dataset`과 `test_dist_dataset`을 `for x in ...` 구조를 통해서 반복합니다.\n", + "* 스케일이 조정된 손실은 `distributed_train_step`의 반환값입니다. `tf.distribute.Strategy.reduce` 호출을 사용해서 장치들 간의 스케일이 조정된 손실 값이 전부 합쳐집니다(aggregated). 그리고 나서 `tf.distribute.Strategy.reduce` 호출의 반환 값들을 더함으로써 배치들 간에 값이 전부 합쳐집니다.\n", + "* `tf.keras.Metrics`는 `tf.distribute.Strategy.experimental_run_v2`에 의해서 실행되는 `train_step`과 `test_step` 내부에서 업데이트 되어야 합니다.\n", + "* `tf.distribute.Strategy.experimental_run_v2`는 그 Strategy에 들어있는 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. 여러분은 `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. 여러분은 `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-q5qp31IQD8t" + }, + "source": [ + "## 최신 체크포인트를 불러와서 평가하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "WNW2P00bkMGJ" + }, + "source": [ + "`tf.distribute.Strategy`를 사용해서 체크포인트가 만들어진 모델은 strategy를 사용해서 저장하거나 strategy를 사용하지 않고 불러올 수 있습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "pg3B-Cw_cn3a", + "colab": {} + }, + "source": [ + "eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='eval_accuracy')\n", + "\n", + "new_model = create_model()\n", + "new_optimizer = tf.keras.optimizers.Adam()\n", + "\n", + "test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7qYii7KUYiSM", + "colab": {} + }, + "source": [ + "@tf.function\n", + "def eval_step(images, labels):\n", + " predictions = new_model(images, training=False)\n", + " eval_accuracy(labels, predictions)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "LeZ6eeWRoUNq", + "colab": {} + }, + "source": [ + "checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)\n", + "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n", + "\n", + "for images, labels in test_dataset:\n", + " eval_step(images, labels)\n", + "\n", + "print ('Accuracy after restoring the saved model without strategy: {}'.format(\n", + " eval_accuracy.result()*100))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EbcI87EEzhzg" + }, + "source": [ + "## 데이터셋에 대해 반복작업을 하는 다른 방법들\n", + "\n", + "### 반복자(iterator)를 사용하기\n", + "\n", + "만약 여러분이 주어진 스텝의 수에 따라서 반복작업을 하기를 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter` 호출을 사용해서 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 반복자에 대해서. 여러분은 데이터셋에 대해 반복하는 것을 `tf.function`의 내부에서 반복자를 사용할지, 외부에서 할지 선택할 수 있습니다. `tf.function`을 사용해서 반복자를 사용하는 방법이 아래에 나와 있습니다.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7c73wGC00CzN", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " for _ in range(EPOCHS):\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " train_iter = iter(train_dist_dataset)\n", + "\n", + " for _ in range(10):\n", + " total_loss += distributed_train_step(next(train_iter))\n", + " num_batches += 1\n", + " average_train_loss = total_loss / num_batches\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", + " print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))\n", + " train_accuracy.reset_states()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "GxVp48Oy0m6y" + }, + "source": [ + "### tf.function 내부에서 반복작업하기\n", + "여러분은 전체 입력 `train_dist_dataset`에 대해서 `tf.function` 내부에서 `for x in ...` 생성자를 사용함으로써 반복작업을 하거나, 위에서 사용했던 반복자를 사용함으로써 반복작업을 할 수 있습니다. 아래의 예제에서는 `tf.function` 내에서 한 훈련의 에포크를 감싸는 것을 보여 드리고, 그 후에 `tf.function` 내부의 `train_dist_dataset`을 통해서 반복작업을 하는 것을 보여드릴 겁니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "-REzmcXv00qm", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " @tf.function\n", + " def distributed_train_epoch(dataset):\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " for x in dataset:\n", + " per_replica_losses = strategy.experimental_run_v2(train_step,\n", + " args=(x,))\n", + " total_loss += strategy.reduce(\n", + " tf.distribute.ReduceOp.SUM, per_replica_losses, axis=None)\n", + " num_batches += 1\n", + " return total_loss / tf.cast(num_batches, dtype=tf.float32)\n", + "\n", + " for epoch in range(EPOCHS):\n", + " train_loss = distributed_train_epoch(train_dist_dataset)\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", + " print (template.format(epoch+1, train_loss, train_accuracy.result()*100))\n", + "\n", + " train_accuracy.reset_states()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MuZGXiyC7ABR" + }, + "source": [ + "### 복제 모델들 간의 훈련 손실 추적하기\n", + "\n", + "노트: 일반적인 규칙으로, 여러분은 하나의 샘플당 값들을 추적하고 장치 내부에서 합쳐지는 값을 피하기 위해서 `tf.keras.Metrics`를 사용하셔야 합니다.\n", + "\n", + "우리는 `tf.metrics.Mean`을 각 장치들에 대해 훈련 손실을 추적하는 것에 사용하는 것을 추천하지 *않습니다*. 왜냐하면 손실의 스케일을 조정하는 계산이 수행되기 때문입니다.\n", + "\n", + "예를 들어, 여러분이 아래의 특성들을 가진 훈련을 수행한다고 합시다.\n", + "* 두개의 장치\n", + "* 두개의 샘플들이 각 장치에 의해 처리됩니다.\n", + "* 손실값들을 산출합니다: 각각의 장치에 대해 [2, 3]과 [4, 5]\n", + "* Global batch size = 4\n", + "\n", + "손실의 스케일 조정을 하면, 여러분은 각 장치에 대한 샘플당 손실값을 손실값들을 더함으로써 계산할 수 있습니다. 그 후 global batch size를 나누어줍니다. 이 경우에는 (2 + 3) / 4 = 1.24와 (4 + 5) / 4 = 2.25입니다.\n", + "\n", + "만약 여러분이 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, `result()`가 metric에서 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xisYJaV9KZTN" + }, + "source": [ + "### 예제들\n", + "사용자 정의 훈련루프를 포함한 분산 전략을 사용하는 몇 가지 예제가 있습니다.\n", + "\n", + "1. [Tutorial](../tutorials/distribute/training_loops.ipynb) `MirroredStrategy`를 사용해서 MNIST를 훈련하는 예제\n", + "2. [DenseNet](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/densenet/distributed_train.py) 예제. `MirroredStrategy`를 사용하는 DenseNet 예제.\n", + "1. [BERT](https://github.com/tensorflow/models/blob/master/official/bert/run_classifier.py) \n", + "예제. `MirroredStrategy`와 `TPUStrategy`를 사용해서 훈련한 예제. 이 예제는 분산 훈련 중에 어떻게 체크포인트로부터 불러오는지와 어떻게 주기적으로 체크포인트들을 생성해 내는지를 이해하기에 정말 좋습니다.\n", + "2. [NCF](https://github.com/tensorflow/models/blob/master/official/recommendation/ncf_keras_main.py) \n", + "`keras_use_ctl` flag를 사용해서 활성화 할 수 있는 `MirroredStrategy`를 이용해서 훈련되는 예제. \n", + " `keras_use_ctl` flag가 사용될 수 있는 `MirroredStrategy`를 사용한 예제\n", + "3. [NMT](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/nmt_with_attention/distributed_train.py) `MirroredStrategy`을 사용해서 훈련되는 예제.\n", + "\n", + "더 많은 예제는 여기에 있습니다. [Distribution strategy guide](../../guide/distribute_strategy.ipynb#examples_and_tutorials)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6hEJNsokjOKs" + }, + "source": [ + "## 다음단계\n", + "\n", + "새로운 `tf.distribute.Strategy` API를 여러분의 모델에 적용해 보세요." + ] + } + ] +} \ No newline at end of file From 3726dbc52b14d529d0785b29dc9f11a15bf4c9ff Mon Sep 17 00:00:00 2001 From: noelbird Date: Wed, 28 Aug 2019 22:03:12 +0900 Subject: [PATCH 3/6] apply reviewer's comments --- .../tutorials/distribute/training_loops.ipynb | 1558 +++++++++-------- 1 file changed, 784 insertions(+), 774 deletions(-) diff --git a/site/ko/beta/tutorials/distribute/training_loops.ipynb b/site/ko/beta/tutorials/distribute/training_loops.ipynb index e4e66ea9eab..959987b4ad0 100644 --- a/site/ko/beta/tutorials/distribute/training_loops.ipynb +++ b/site/ko/beta/tutorials/distribute/training_loops.ipynb @@ -1,775 +1,785 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "training_loops.ipynb의 사본", - "version": "0.3.2", - "provenance": [], - "private_outputs": true, - "collapsed_sections": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - } - }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MhoQ0WE77laV" - }, - "source": [ - "##### Copyright 2019 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "metadata": { - "cellView": "both", - "colab_type": "code", - "id": "_ckMIh7O7s6D", - "colab": {} - }, - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "jYysdyb-CaWM" - }, - "source": [ - "# 훈련 루프와 함께 tf.distribute.Strategy 사용하기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "S5Uhzt6vVIB2" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " TensorFlow.org에서 보기\n", - " \n", - " 구글 코랩(Colab)에서 실행하기\n", - " \n", - " 깃허브(GitHub) 소스 보기\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "tUP8LMdYtWPz" - }, - "source": [ - "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 [공식 영문 문서](https://github.com/tensorflow/docs/blob/master/site/en/r2/guide/using_gpu.ipynb)의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 [tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 [docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로 메일을 보내주시기 바랍니다." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "FbVhjPpzn6BM" - }, - "source": [ - "이 예제는 사용자 정의 훈련 루프(custom training loops)와 함께 [`tf.distribute.Strategy`](https://www.tensorflow.org/guide/distribute_strategy)를 사용 하는법을 보여드립니다. 우리는 간단한 CNN 모델을 패션 MNIST 데이터셋에 대해 훈련을 할 것 입니다. 패션 MNIST 데이터셋은 60000개의 28 x 28 크기의 훈련 이미지들과 10000개의 28 x 28 크기의 평가 이미지들을 포함하고 있습니다.\n", - "\n", - "이 예제는 모델을 훈련하기 위해서 사용자 정의 훈련 루프를 사용하고 있습니다. 왜냐하면 사용자 정의 훈련 루프는 유연하며 훈련을 더욱 잘 수행하도록 해주기 때문입니다. 또한, 모델과 훈련 루프를 디버깅하기도 더 쉽습니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "dzLKpmZICaWN", - "colab": {} - }, - "source": [ - "from __future__ import absolute_import, division, print_function, unicode_literals\n", - "\n", - "# 텐서플로우 임포트\n", - "!pip install tensorflow-gpu==2.0.0-beta1\n", - "import tensorflow as tf\n", - "\n", - "# 헬퍼 라이브러리들\n", - "import numpy as np\n", - "import os\n", - "\n", - "print(tf.__version__)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MM6W__qraV55" - }, - "source": [ - "## 패션 MNIST 데이터셋을 다운로드" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "7MqDQO0KCaWS", - "colab": {} - }, - "source": [ - "fashion_mnist = tf.keras.datasets.fashion_mnist\n", - "\n", - "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()\n", - "\n", - "\n", - "# 하나의 차원을 배열에 추가 -> 새로운 shape == (28, 28, 1)\n", - "# 이렇게 하는 이유는 우리의 모델에서 첫 번째 층이 합성곱 층이고\n", - "# 합성곱 층은 4D 입력을 요구하기 때문입니다.\n", - "# (batch_size, height, width, channels).\n", - "# batch_size 차원은 나중에 추가할 것입니다.\n", - "\n", - "train_images = train_images[..., None]\n", - "test_images = test_images[..., None]\n", - "\n", - "# 이미지를 [0, 1] 범위로 변경하기.\n", - "train_images = train_images / np.float32(255)\n", - "test_images = test_images / np.float32(255)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "4AXoHhrsbdF3" - }, - "source": [ - "## 변수들과 그래프를 분배하는 전략 만들기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "5mVuLZhbem8d" - }, - "source": [ - "`tf.distribute.MirroredStrategy` 전략이 어떻게 동작할까요?\n", - "* 모든 변수와 모델의 그래프는 장치(replicas, 다른 문서에서는 replica가 분산 훈련에서 장치 등에 복제된 모델을 의미하는 경우가 있으나 이 문서에서는 장치 자체를 의미합니다)들에 복제됩니다.\n", - "* 입력은 장치에 고르게 분배되어 들어갑니다.\n", - "* 각 장치는 주어지는 입력에 대해서 손실(loss)과 그래디언트를 계산합니다.\n", - "* 그래디언트들을 전부 더함으로써 모든 장치들 간에 그래디언트들이 동기화됩니다.\n", - "* 동기화된 후에, 동일한 업데이트가 각 장치에 있는 변수의 복사본(copies)에 동일하게 적용됩니다.\n", - "\n", - "노트: 여러분은 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "F2VeZUWUj5S4", - "colab": {} - }, - "source": [ - "# 만약 장치들의 목록이 `tf.distribute.MirroredStrategy` 생성자 안에 명시되어 있지 않다면,\n", - "# 자동으로 장치들의 목록이 인식될 것 입니다.\n", - "strategy = tf.distribute.MirroredStrategy()" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "ZngeM_2o0_JO", - "colab": {} - }, - "source": [ - "print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "k53F5I_IiGyI" - }, - "source": [ - "## 입력 파이프라인 설치하기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "0Qb6nDgxiN_n" - }, - "source": [ - "그래프와 변수들을 플랫폼을 몰라도 사용할 수 있는(platform-agnostic) 저장된 모델(SavedModel) 형식으로 내보냅니다. 모델을 내보내셨다면, 여러분은 모델을 불러올 때 범위(scope)를 지정해도 되고 하지 않으셔도 됩니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "jwJtsCQhHK-E", - "colab": {} - }, - "source": [ - "BUFFER_SIZE = len(train_images)\n", - "\n", - "BATCH_SIZE_PER_REPLICA = 64\n", - "GLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync\n", - "\n", - "EPOCHS = 10" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "J7fj3GskHC8g" - }, - "source": [ - "분산 데이터셋들을 `strategy.scope` 내에 생성합니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "WYrMNNDhAvVl", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - "\n", - " train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).shuffle(BUFFER_SIZE).batch(GLOBAL_BATCH_SIZE) \n", - " train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)\n", - " \n", - " test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE) \n", - " test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bAXAo_wWbWSb" - }, - "source": [ - "## 모델 만들기\n", - "\n", - "`tf.keras.Sequential`을 사용해서 모델을 생성합니다. 여러분은 Model Subclassing API로도 모델 생성을 할 수 있습니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "9ODch-OFCaW4", - "colab": {} - }, - "source": [ - "def create_model():\n", - " model = tf.keras.Sequential([\n", - " tf.keras.layers.Conv2D(32, 3, activation='relu'),\n", - " tf.keras.layers.MaxPooling2D(),\n", - " tf.keras.layers.Conv2D(64, 3, activation='relu'),\n", - " tf.keras.layers.MaxPooling2D(),\n", - " tf.keras.layers.Flatten(),\n", - " tf.keras.layers.Dense(64, activation='relu'),\n", - " tf.keras.layers.Dense(10, activation='softmax')\n", - " ])\n", - "\n", - " return model" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "9iagoTBfijUz", - "colab": {} - }, - "source": [ - "# 체크포인트들을 저장하기 위해서 체크포인트 디렉토리를 생성합니다.\n", - "checkpoint_dir = './training_checkpoints'\n", - "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "e-wlFFZbP33n" - }, - "source": [ - "## 손실 함수 정의하기\n", - "\n", - "일반적으로, 1의 GPU/CPU 비율을 가지고 있는 단일 장치에서 손실은 배치(batch) 입력에서의 장치의 개수로 나누어집니다.\n", - "\n", - "*그렇다면, `tf.distribute.Strategy`를 사용할 때, 손실은 어떻게 계산되어야 할까요?*\n", - "\n", - "* 예를들면, 4개의 GPU가 있고 배치 입력 크기가 64라고 하죠. 배치 입력은 장치(4개의 GPU)에 분배됩니다. 각 장치는 16의 입력크기를 가지게 됩니다.\n", - "\n", - "* 각 장치에 있는 모델은 각 입력을 앞쪽으로 넘겨주고(forward pass) 손실을 계산합니다. 손실을 각각의 장치당 배치크기(BATCH_SIZE_PER_REPLICA = 16)로 나누는 것 대신에, 손실은 GLOBAL_BATCH_SIZE(64)로 나누어져야 합니다.\n", - "\n", - "*왜 이렇게 할까요?*\n", - "\n", - "* 위와 같이 계산하는 이유는 그래디언트들이 각 복제 모델에서 계산된 다음, 복제 모델들 간에 그래디언트 값들을 **전부 더함(summing)**으로써 동기화가 되기 때문입니다.\n", - "\n", - "*이 것을 텐서플로우에서는 어떻게 할까요?*\n", - "\n", - "\n", - "* 만약 여러분이 이 예제에서 처럼 사용자 정의 훈련 루프를 작성하고 있다면, 여러분은 장치당 손실을 더하고 GLOBAL_BATCH_SIZE로 나누어줘야 합니다.\n", - "`scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE)`\n", - "또는 여러분은 `tf.nn.compute_average_loss`를 사용할 수도 있습니다. 이 함수는 장치당 손실, 선택적 샘플 가중치(optional sample weights)와 GLOBAL_BATCH_SIZE를 매개변수로 취하면서 스케일이 조정된 손실을 반환합니다.\n", - "\n", - "* 만약 여러분이 여러분의 모델에서 손실 정규화를 사용하고 있다면, 여러분은 손실을 장치의 수로 스케일 조정을 할 필요가 있습니다. `tf.nn_scale_regularization_loss` 함수를 사용함으로써 스케일 조정을 할 수 있습니다.\n", - "\n", - "* `tf.reduce_mean`을 사용하는 것은 추천하지 않습니다. 이렇게 하는 것은 손실을 실제 장치당 배치 크기로 나눕니다. 이 실제 장치당 배치 크기는 아마 각 단계(step)마다 크기가 다를 수 있습니다.\n", - "\n", - "* 이런 reduction과 스케일 조정은 케라스의 `model.compile`과 `model.fit`에서 자동적으로 됩니다.\n", - "\n", - "* 만약 `tf.keras.losses` 클래스(아래의 예제에서처럼)를 사용한다면, reduction 매개변수를 명시적으로 `NONE` 또는 `SUM` 중 하나로 표시해야 합니다. `AUTO`와 `SUM_OVER_BATCH_SIZE`는 `tf.distribute.Strategy`를 함께 사용하는 경우에는 허용되지 않습니다. `AUTO`가 허용되지 않는 것은 사용자가 분산 케이스(distributed case)에서 어떤 reduction을 만들고 싶은지 명시적으로 생각하는 것이 올바르기 때문입니다.\n", - "`SUM_OVER_BATCH_SIZE`가 허용되지 않는 것은 `SUM_OVER_BATCH_SIZE`는 장치당 배치 크기로만 나눈 다음에 장치의 수로 나눈 것을 사용자에게 반환하기 때문입니다(reduction이 되어지지 않은 채로). 이 점은 아마도 놓치기 쉬을거에요. 그래서 우리는 이렇게 하는 것 대신에 사용자들에게 reduction을 사용자 스스로 명시적으로 하기를 요청합니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "R144Wci782ix", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - " # reduction을 `none`으로 설정합니다. 그래서 우리는 reduction을 나중에 하고,\n", - " # global batch size로 나눌 수 있습니다.\n", - " loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n", - " reduction=tf.keras.losses.Reduction.NONE)\n", - " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy로 하셔도 됩니다.\n", - " def compute_loss(labels, predictions):\n", - " per_example_loss = loss_object(labels, predictions)\n", - " return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "w8y54-o9T2Ni" - }, - "source": [ - "## 손실과 정확도를 따르는 지표 정의하기\n", - "\n", - "이 지표(metrics)는 평가에서의 손실과 훈련과 평가에서의 정확도를 추적합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "zt3AHb46Tr3w", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - " test_loss = tf.keras.metrics.Mean(name='test_loss')\n", - "\n", - " train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", - " name='train_accuracy')\n", - " test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", - " name='test_accuracy')" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "iuKuNXPORfqJ" - }, - "source": [ - "## 훈련 루프" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "OrMmakq5EqeQ", - "colab": {} - }, - "source": [ - "# 모델과 옵티마이저는 `strategy.scope`에서 만들어져야 합니다.\n", - "with strategy.scope():\n", - " model = create_model()\n", - "\n", - " optimizer = tf.keras.optimizers.Adam()\n", - "\n", - " checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "3UX43wUu04EL", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - " def train_step(inputs):\n", - " images, labels = inputs\n", - "\n", - " with tf.GradientTape() as tape:\n", - " predictions = model(images, training=True)\n", - " loss = compute_loss(labels, predictions)\n", - "\n", - " gradients = tape.gradient(loss, model.trainable_variables)\n", - " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", - "\n", - " train_accuracy.update_state(labels, predictions)\n", - " return loss \n", - "\n", - " def test_step(inputs):\n", - " images, labels = inputs\n", - "\n", - " predictions = model(images, training=False)\n", - " t_loss = loss_object(labels, predictions)\n", - "\n", - " test_loss.update_state(t_loss)\n", - " test_accuracy.update_state(labels, predictions)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "gX975dMSNw0e", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - " # `experimental_run_v2`는 주어진 계산을 복사하고,\n", - " # 분산 입력(distributed input)을 입력값으로 계산을 실행합니다.\n", - " \n", - " @tf.function\n", - " def distributed_train_step(dataset_inputs):\n", - " per_replica_losses = strategy.experimental_run_v2(train_step,\n", - " args=(dataset_inputs,))\n", - " return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,\n", - " axis=None)\n", - " \n", - " @tf.function\n", - " def distributed_test_step(dataset_inputs):\n", - " return strategy.experimental_run_v2(test_step, args=(dataset_inputs,))\n", - "\n", - " for epoch in range(EPOCHS):\n", - " # 훈련 루프\n", - " total_loss = 0.0\n", - " num_batches = 0\n", - " for x in train_dist_dataset:\n", - " total_loss += distributed_train_step(x)\n", - " num_batches += 1\n", - " train_loss = total_loss / num_batches\n", - "\n", - " # 평가 루프\n", - " for x in test_dist_dataset:\n", - " distributed_test_step(x)\n", - "\n", - " if epoch % 2 == 0:\n", - " checkpoint.save(checkpoint_prefix)\n", - "\n", - " template = (\"Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, \"\n", - " \"Test Accuracy: {}\")\n", - " print (template.format(epoch+1, train_loss,\n", - " train_accuracy.result()*100, test_loss.result(),\n", - " test_accuracy.result()*100))\n", - "\n", - " test_loss.reset_states()\n", - " train_accuracy.reset_states()\n", - " test_accuracy.reset_states()" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Z1YvXqOpwy08" - }, - "source": [ - "위의 예제에서 주목해야 하는 부분\n", - "* 이 예제는 `train_dist_dataset`과 `test_dist_dataset`을 `for x in ...` 구조를 통해서 반복합니다.\n", - "* 스케일이 조정된 손실은 `distributed_train_step`의 반환값입니다. `tf.distribute.Strategy.reduce` 호출을 사용해서 장치들 간의 스케일이 조정된 손실 값이 전부 합쳐집니다(aggregated). 그리고 나서 `tf.distribute.Strategy.reduce` 호출의 반환 값들을 더함으로써 배치들 간에 값이 전부 합쳐집니다.\n", - "* `tf.keras.Metrics`는 `tf.distribute.Strategy.experimental_run_v2`에 의해서 실행되는 `train_step`과 `test_step` 내부에서 업데이트 되어야 합니다.\n", - "* `tf.distribute.Strategy.experimental_run_v2`는 그 Strategy에 들어있는 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. 여러분은 `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. 여러분은 `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "-q5qp31IQD8t" - }, - "source": [ - "## 최신 체크포인트를 불러와서 평가하기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "WNW2P00bkMGJ" - }, - "source": [ - "`tf.distribute.Strategy`를 사용해서 체크포인트가 만들어진 모델은 strategy를 사용해서 저장하거나 strategy를 사용하지 않고 불러올 수 있습니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "pg3B-Cw_cn3a", - "colab": {} - }, - "source": [ - "eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", - " name='eval_accuracy')\n", - "\n", - "new_model = create_model()\n", - "new_optimizer = tf.keras.optimizers.Adam()\n", - "\n", - "test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "7qYii7KUYiSM", - "colab": {} - }, - "source": [ - "@tf.function\n", - "def eval_step(images, labels):\n", - " predictions = new_model(images, training=False)\n", - " eval_accuracy(labels, predictions)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "LeZ6eeWRoUNq", - "colab": {} - }, - "source": [ - "checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)\n", - "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n", - "\n", - "for images, labels in test_dataset:\n", - " eval_step(images, labels)\n", - "\n", - "print ('Accuracy after restoring the saved model without strategy: {}'.format(\n", - " eval_accuracy.result()*100))" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "EbcI87EEzhzg" - }, - "source": [ - "## 데이터셋에 대해 반복작업을 하는 다른 방법들\n", - "\n", - "### 반복자(iterator)를 사용하기\n", - "\n", - "만약 여러분이 주어진 스텝의 수에 따라서 반복작업을 하기를 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter` 호출을 사용해서 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 반복자에 대해서. 여러분은 데이터셋에 대해 반복하는 것을 `tf.function`의 내부에서 반복자를 사용할지, 외부에서 할지 선택할 수 있습니다. `tf.function`을 사용해서 반복자를 사용하는 방법이 아래에 나와 있습니다.\n" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "7c73wGC00CzN", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - " for _ in range(EPOCHS):\n", - " total_loss = 0.0\n", - " num_batches = 0\n", - " train_iter = iter(train_dist_dataset)\n", - "\n", - " for _ in range(10):\n", - " total_loss += distributed_train_step(next(train_iter))\n", - " num_batches += 1\n", - " average_train_loss = total_loss / num_batches\n", - "\n", - " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", - " print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))\n", - " train_accuracy.reset_states()" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "GxVp48Oy0m6y" - }, - "source": [ - "### tf.function 내부에서 반복작업하기\n", - "여러분은 전체 입력 `train_dist_dataset`에 대해서 `tf.function` 내부에서 `for x in ...` 생성자를 사용함으로써 반복작업을 하거나, 위에서 사용했던 반복자를 사용함으로써 반복작업을 할 수 있습니다. 아래의 예제에서는 `tf.function` 내에서 한 훈련의 에포크를 감싸는 것을 보여 드리고, 그 후에 `tf.function` 내부의 `train_dist_dataset`을 통해서 반복작업을 하는 것을 보여드릴 겁니다." - ] - }, - { - "cell_type": "code", - "metadata": { - "colab_type": "code", - "id": "-REzmcXv00qm", - "colab": {} - }, - "source": [ - "with strategy.scope():\n", - " @tf.function\n", - " def distributed_train_epoch(dataset):\n", - " total_loss = 0.0\n", - " num_batches = 0\n", - " for x in dataset:\n", - " per_replica_losses = strategy.experimental_run_v2(train_step,\n", - " args=(x,))\n", - " total_loss += strategy.reduce(\n", - " tf.distribute.ReduceOp.SUM, per_replica_losses, axis=None)\n", - " num_batches += 1\n", - " return total_loss / tf.cast(num_batches, dtype=tf.float32)\n", - "\n", - " for epoch in range(EPOCHS):\n", - " train_loss = distributed_train_epoch(train_dist_dataset)\n", - "\n", - " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", - " print (template.format(epoch+1, train_loss, train_accuracy.result()*100))\n", - "\n", - " train_accuracy.reset_states()" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MuZGXiyC7ABR" - }, - "source": [ - "### 복제 모델들 간의 훈련 손실 추적하기\n", - "\n", - "노트: 일반적인 규칙으로, 여러분은 하나의 샘플당 값들을 추적하고 장치 내부에서 합쳐지는 값을 피하기 위해서 `tf.keras.Metrics`를 사용하셔야 합니다.\n", - "\n", - "우리는 `tf.metrics.Mean`을 각 장치들에 대해 훈련 손실을 추적하는 것에 사용하는 것을 추천하지 *않습니다*. 왜냐하면 손실의 스케일을 조정하는 계산이 수행되기 때문입니다.\n", - "\n", - "예를 들어, 여러분이 아래의 특성들을 가진 훈련을 수행한다고 합시다.\n", - "* 두개의 장치\n", - "* 두개의 샘플들이 각 장치에 의해 처리됩니다.\n", - "* 손실값들을 산출합니다: 각각의 장치에 대해 [2, 3]과 [4, 5]\n", - "* Global batch size = 4\n", - "\n", - "손실의 스케일 조정을 하면, 여러분은 각 장치에 대한 샘플당 손실값을 손실값들을 더함으로써 계산할 수 있습니다. 그 후 global batch size를 나누어줍니다. 이 경우에는 (2 + 3) / 4 = 1.24와 (4 + 5) / 4 = 2.25입니다.\n", - "\n", - "만약 여러분이 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, `result()`가 metric에서 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "xisYJaV9KZTN" - }, - "source": [ - "### 예제들\n", - "사용자 정의 훈련루프를 포함한 분산 전략을 사용하는 몇 가지 예제가 있습니다.\n", - "\n", - "1. [Tutorial](../tutorials/distribute/training_loops.ipynb) `MirroredStrategy`를 사용해서 MNIST를 훈련하는 예제\n", - "2. [DenseNet](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/densenet/distributed_train.py) 예제. `MirroredStrategy`를 사용하는 DenseNet 예제.\n", - "1. [BERT](https://github.com/tensorflow/models/blob/master/official/bert/run_classifier.py) \n", - "예제. `MirroredStrategy`와 `TPUStrategy`를 사용해서 훈련한 예제. 이 예제는 분산 훈련 중에 어떻게 체크포인트로부터 불러오는지와 어떻게 주기적으로 체크포인트들을 생성해 내는지를 이해하기에 정말 좋습니다.\n", - "2. [NCF](https://github.com/tensorflow/models/blob/master/official/recommendation/ncf_keras_main.py) \n", - "`keras_use_ctl` flag를 사용해서 활성화 할 수 있는 `MirroredStrategy`를 이용해서 훈련되는 예제. \n", - " `keras_use_ctl` flag가 사용될 수 있는 `MirroredStrategy`를 사용한 예제\n", - "3. [NMT](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/nmt_with_attention/distributed_train.py) `MirroredStrategy`을 사용해서 훈련되는 예제.\n", - "\n", - "더 많은 예제는 여기에 있습니다. [Distribution strategy guide](../../guide/distribute_strategy.ipynb#examples_and_tutorials)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "6hEJNsokjOKs" - }, - "source": [ - "## 다음단계\n", - "\n", - "새로운 `tf.distribute.Strategy` API를 여러분의 모델에 적용해 보세요." - ] - } - ] -} \ No newline at end of file + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MhoQ0WE77laV" + }, + "source": [ + "##### Copyright 2019 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "both", + "colab": {}, + "colab_type": "code", + "id": "_ckMIh7O7s6D" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jYysdyb-CaWM" + }, + "source": [ + "# 훈련 루프와 함께 tf.distribute.Strategy 사용하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S5Uhzt6vVIB2" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " TensorFlow.org에서 보기\n", + " \n", + " 구글 코랩(Colab)에서 실행하기\n", + " \n", + " 깃허브(GitHub) 소스 보기\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "tUP8LMdYtWPz" + }, + "source": [ + "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 [공식 영문 문서](https://github.com/tensorflow/docs/blob/master/site/en/r2/guide/using_gpu.ipynb)의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 [tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 [docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로 메일을 보내주시기 바랍니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FbVhjPpzn6BM" + }, + "source": [ + "이 예제는 사용자 정의 훈련 루프(custom training loops)와 함께 [`tf.distribute.Strategy`](https://www.tensorflow.org/guide/distribute_strategy)를 사용하는 법을 보여드립니다. 우리는 간단한 CNN 모델을 패션 MNIST 데이터셋에 대해 훈련을 할 것입니다. 패션 MNIST 데이터셋은 60000개의 28 x 28 크기의 훈련 이미지들과 10000개의 28 x 28 크기의 테스트 이미지들을 포함하고 있습니다.\n", + "\n", + "이 예제는 모델을 훈련하기 위해서 사용자 정의 훈련 루프를 사용하고 있습니다. 왜냐하면 사용자 정의 훈련 루프는 유연하며 훈련을 더욱 잘 수행하도록 해주기 때문입니다. 또한, 모델과 훈련 루프를 디버깅하기도 더 쉽습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "dzLKpmZICaWN" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function, unicode_literals\n", + "\n", + "# 텐서플로 임포트\n", + "!pip install tensorflow-gpu==2.0.0-beta1\n", + "import tensorflow as tf\n", + "\n", + "# 헬퍼 라이브러리들\n", + "import numpy as np\n", + "import os\n", + "\n", + "print(tf.__version__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MM6W__qraV55" + }, + "source": [ + "## 패션 MNIST 데이터셋을 다운로드" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "7MqDQO0KCaWS" + }, + "outputs": [], + "source": [ + "fashion_mnist = tf.keras.datasets.fashion_mnist\n", + "\n", + "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()\n", + "\n", + "\n", + "# 하나의 차원을 배열에 추가 -> 새로운 shape == (28, 28, 1)\n", + "# 이렇게 하는 이유는 우리의 모델에서 첫 번째 층이 합성곱 층이고\n", + "# 합성곱 층은 4D 입력을 요구하기 때문입니다.\n", + "# (batch_size, height, width, channels).\n", + "# batch_size 차원은 나중에 추가할 것입니다.\n", + "\n", + "train_images = train_images[..., None]\n", + "test_images = test_images[..., None]\n", + "\n", + "# 이미지를 [0, 1] 범위로 변경하기.\n", + "train_images = train_images / np.float32(255)\n", + "test_images = test_images / np.float32(255)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4AXoHhrsbdF3" + }, + "source": [ + "## 변수와 그래프를 분산하는 전략 만들기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "5mVuLZhbem8d" + }, + "source": [ + "`tf.distribute.MirroredStrategy` 전략이 어떻게 동작할까요?\n", + "* 모든 변수와 모델 그래프는 장치(replicas, 다른 문서에서는 replica가 분산 훈련에서 장치 등에 복제된 모델을 의미하는 경우가 있으나 이 문서에서는 장치 자체를 의미합니다)에 복제됩니다.\n", + "* 입력은 장치에 고르게 분배되어 들어갑니다.\n", + "* 각 장치는 주어지는 입력에 대해서 손실(loss)과 그래디언트를 계산합니다.\n", + "* 그래디언트들을 전부 더함으로써 모든 장치들 간에 그래디언트들이 동기화됩니다.\n", + "* 동기화된 후에, 동일한 업데이트가 각 장치에 있는 변수의 복사본(copies)에 동일하게 적용됩니다.\n", + "\n", + "노트: 여러분은 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "F2VeZUWUj5S4" + }, + "outputs": [], + "source": [ + "# 만약 장치들의 목록이 `tf.distribute.MirroredStrategy` 생성자 안에 명시되어 있지 않다면,\n", + "# 자동으로 장치를 인식할 것입니다.\n", + "strategy = tf.distribute.MirroredStrategy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ZngeM_2o0_JO" + }, + "outputs": [], + "source": [ + "print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "k53F5I_IiGyI" + }, + "source": [ + "## 입력 파이프라인 설정하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0Qb6nDgxiN_n" + }, + "source": [ + "그래프와 변수를 플랫폼과 무관한 SavedModel 형식으로 내보냅니다. 모델을 내보냈다면, 모델을 불러올 때 범위(scope)를 지정해도 되고 하지 않아도 됩니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jwJtsCQhHK-E" + }, + "outputs": [], + "source": [ + "BUFFER_SIZE = len(train_images)\n", + "\n", + "BATCH_SIZE_PER_REPLICA = 64\n", + "GLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync\n", + "\n", + "EPOCHS = 10" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "J7fj3GskHC8g" + }, + "source": [ + "분산 데이터셋들을 `strategy.scope` 내에 생성합니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "WYrMNNDhAvVl" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + "\n", + " train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).shuffle(BUFFER_SIZE).batch(GLOBAL_BATCH_SIZE) \n", + " train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)\n", + " \n", + " test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE) \n", + " test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bAXAo_wWbWSb" + }, + "source": [ + "## 모델 만들기\n", + "\n", + "`tf.keras.Sequential`을 사용해서 모델을 생성합니다. Model Subclassing API로도 모델 생성을 할 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "9ODch-OFCaW4" + }, + "outputs": [], + "source": [ + "def create_model():\n", + " model = tf.keras.Sequential([\n", + " tf.keras.layers.Conv2D(32, 3, activation='relu'),\n", + " tf.keras.layers.MaxPooling2D(),\n", + " tf.keras.layers.Conv2D(64, 3, activation='relu'),\n", + " tf.keras.layers.MaxPooling2D(),\n", + " tf.keras.layers.Flatten(),\n", + " tf.keras.layers.Dense(64, activation='relu'),\n", + " tf.keras.layers.Dense(10, activation='softmax')\n", + " ])\n", + "\n", + " return model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "9iagoTBfijUz" + }, + "outputs": [], + "source": [ + "# 체크포인트들을 저장하기 위해서 체크포인트 디렉토리를 생성합니다.\n", + "checkpoint_dir = './training_checkpoints'\n", + "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "e-wlFFZbP33n" + }, + "source": [ + "## 손실 함수 정의하기\n", + "\n", + "일반적으로, GPU/CPU 비율이 1인 단일 장치에서 손실은 입력 배치(batch)의 샘플 개수로 나누어집니다.\n", + "\n", + "*그렇다면, `tf.distribute.Strategy`를 사용할 때, 손실은 어떻게 계산되어야 할까요?*\n", + "\n", + "* 예를들면, 4개의 GPU가 있고 입력 배치 크기가 64라고 하죠. 입력 배치 하나가 여러 개의 장치(4개의 GPU)에 분배됩니다. 각 장치는 크기가 16인 입력을 받습니다.\n", + "\n", + "* 각 장치에 있는 모델은 해당 입력에 대해 정방향 계산(forward pass)을 수행하고 손실을 계산합니다. 손실을 장치에 할당된 입력 샘플의 수(BATCH_SIZE_PER_REPLICA = 16)로 나누는 것이 아니라 GLOBAL_BATCH_SIZE(64)로 나누어야 합니다.\n", + "\n", + "*왜 이렇게 할까요?*\n", + "\n", + "* 위와 같이 계산하는 이유는 그래디언트들이 각 장치에서 계산된 다음, 모든 장치를 동기화하기 위해 이 그래디언트 값들을 전부 더하기 때문입니다.\n", + "\n", + "*이 것을 텐서플로에서는 어떻게 할까요?*\n", + "\n", + "\n", + "* 만약 이 예제처럼 사용자 정의 훈련 루프를 작성한다면, 다음과 같이 샘플당 손실을 더하고 GLOBAL_BATCH_SIZE로 나누어야 합니다.\n", + "`scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE)`\n", + "또는 `tf.nn.compute_average_loss` 함수를 사용할 수도 있습니다. 이 함수는 샘플당 손실과 선택적으로 샘플 가중치, GLOBAL_BATCH_SIZE를 매개변수 값으로 받고 스케일이 조정된 손실을 반환합니다.\n", + "\n", + "* 만약 규제 손실을 사용하는 모델이라면, 장치 개수로 손실 값을 스케일 조정해야 합니다. 이는 `tf.nn_scale_regularization_loss` 함수를 사용하여 처리할 수 있습니다.\n", + "\n", + "* `tf.reduce_mean`을 사용하는 것은 추천하지 않습니다. 이렇게 하는 것은 손실을 실제 장치당 배치 크기로 나눕니다. 이 실제 장치당 배치 크기는 아마 각 단계(step)마다 크기가 다를 수 있습니다.\n", + "\n", + "* 이런 축소(`reduction`)와 스케일 조정은 케라스의 `model.compile`과 `model.fit`에서 자동적으로 수행됩니다.\n", + "\n", + "* 만약 `tf.keras.losses` 클래스(아래의 예제에서처럼)를 사용한다면, reduction 매개변수를 명시적으로 `NONE` 또는 `SUM` 중 하나로 표시해야 합니다. `AUTO`가 허용되지 않는 것은 사용자가 분산 모드에서 어떻게 축소할지 명시적으로 지정하는 것이 바람직하기 때문입니다.\n", + "`SUM_OVER_BATCH_SIZE`가 허용되지 않는 것은 `SUM_OVER_BATCH_SIZE`는 장치당 배치 크기로만 나눈 다음에 장치의 수로 나눈 것을 사용자에게 반환하기 때문입니다(reduction이 되어지지 않은 채로). 이 점은 아마도 놓치기 쉬을거에요. 그래서 우리는 이렇게 하는 것 대신에 사용자들에게 reduction을 사용자 스스로 명시적으로 하기를 요청합니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "R144Wci782ix" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + " # reduction을 `none`으로 설정합니다. 그래서 우리는 reduction을 나중에 하고,\n", + " # GLOBAL_BATCH_SIZE로 나눌 수 있습니다.\n", + " loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n", + " reduction=tf.keras.losses.Reduction.NONE)\n", + " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy로 하셔도 됩니다.\n", + " def compute_loss(labels, predictions):\n", + " per_example_loss = loss_object(labels, predictions)\n", + " return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "w8y54-o9T2Ni" + }, + "source": [ + "## 손실과 정확도를 따르는 지표 정의하기\n", + "\n", + "이 지표(metrics)는 평가에서의 손실과 훈련과 평가에서의 정확도를 추적합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zt3AHb46Tr3w" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + " test_loss = tf.keras.metrics.Mean(name='test_loss')\n", + "\n", + " train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='train_accuracy')\n", + " test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='test_accuracy')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iuKuNXPORfqJ" + }, + "source": [ + "## 훈련 루프" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "OrMmakq5EqeQ" + }, + "outputs": [], + "source": [ + "# 모델과 옵티마이저는 `strategy.scope`에서 만들어져야 합니다.\n", + "with strategy.scope():\n", + " model = create_model()\n", + "\n", + " optimizer = tf.keras.optimizers.Adam()\n", + "\n", + " checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "3UX43wUu04EL" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + " def train_step(inputs):\n", + " images, labels = inputs\n", + "\n", + " with tf.GradientTape() as tape:\n", + " predictions = model(images, training=True)\n", + " loss = compute_loss(labels, predictions)\n", + "\n", + " gradients = tape.gradient(loss, model.trainable_variables)\n", + " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", + "\n", + " train_accuracy.update_state(labels, predictions)\n", + " return loss \n", + "\n", + " def test_step(inputs):\n", + " images, labels = inputs\n", + "\n", + " predictions = model(images, training=False)\n", + " t_loss = loss_object(labels, predictions)\n", + "\n", + " test_loss.update_state(t_loss)\n", + " test_accuracy.update_state(labels, predictions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "gX975dMSNw0e" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + " # `experimental_run_v2`는 주어진 계산을 복사하고,\n", + " # 분산 입력(distributed input)을 입력값으로 계산을 실행합니다.\n", + " \n", + " @tf.function\n", + " def distributed_train_step(dataset_inputs):\n", + " per_replica_losses = strategy.experimental_run_v2(train_step,\n", + " args=(dataset_inputs,))\n", + " return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,\n", + " axis=None)\n", + " \n", + " @tf.function\n", + " def distributed_test_step(dataset_inputs):\n", + " return strategy.experimental_run_v2(test_step, args=(dataset_inputs,))\n", + "\n", + " for epoch in range(EPOCHS):\n", + " # 훈련 루프\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " for x in train_dist_dataset:\n", + " total_loss += distributed_train_step(x)\n", + " num_batches += 1\n", + " train_loss = total_loss / num_batches\n", + "\n", + " # 테스트 루프\n", + " for x in test_dist_dataset:\n", + " distributed_test_step(x)\n", + "\n", + " if epoch % 2 == 0:\n", + " checkpoint.save(checkpoint_prefix)\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, \"\n", + " \"Test Accuracy: {}\")\n", + " print (template.format(epoch+1, train_loss,\n", + " train_accuracy.result()*100, test_loss.result(),\n", + " test_accuracy.result()*100))\n", + "\n", + " test_loss.reset_states()\n", + " train_accuracy.reset_states()\n", + " test_accuracy.reset_states()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Z1YvXqOpwy08" + }, + "source": [ + "위의 예제에서 주목해야 하는 부분\n", + "* 이 예제는 `train_dist_dataset`과 `test_dist_dataset`을 `for x in ...` 구조를 통해서 반복합니다.\n", + "* 스케일이 조정된 손실은 `distributed_train_step`의 반환값입니다. `tf.distribute.Strategy.reduce` 호출을 사용해서 장치들 간의 스케일이 조정된 손실 값을 전부 합칩니다. 그리고 나서 `tf.distribute.Strategy.reduce` 반환 값을 더하는 식으로 배치 간의 손실을 모읍니다.\n", + "* `tf.keras.Metrics`는 `tf.distribute.Strategy.experimental_run_v2`에 의해서 실행되는 `train_step`과 `test_step` 함수 안에서 업데이트 되어야 합니다.\n", + "* `tf.distribute.Strategy.experimental_run_v2`는 그 전략안에 포함된 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. 여러분은 `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. 여러분은 `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-q5qp31IQD8t" + }, + "source": [ + "## 최신 체크포인트를 불러와서 평가하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "WNW2P00bkMGJ" + }, + "source": [ + "`tf.distribute.Strategy`를 사용해서 체크포인트가 만들어진 모델은 전략 사용 여부에 상관없이 불러올 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "pg3B-Cw_cn3a" + }, + "outputs": [], + "source": [ + "eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='eval_accuracy')\n", + "\n", + "new_model = create_model()\n", + "new_optimizer = tf.keras.optimizers.Adam()\n", + "\n", + "test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "7qYii7KUYiSM" + }, + "outputs": [], + "source": [ + "@tf.function\n", + "def eval_step(images, labels):\n", + " predictions = new_model(images, training=False)\n", + " eval_accuracy(labels, predictions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "LeZ6eeWRoUNq" + }, + "outputs": [], + "source": [ + "checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)\n", + "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n", + "\n", + "for images, labels in test_dataset:\n", + " eval_step(images, labels)\n", + "\n", + "print ('Accuracy after restoring the saved model without strategy: {}'.format(\n", + " eval_accuracy.result()*100))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EbcI87EEzhzg" + }, + "source": [ + "## 데이터셋에 대해 반복작업을 하는 다른 방법들\n", + "\n", + "### 반복자(iterator)를 사용하기\n", + "\n", + "만약 주어진 스텝의 수에 따라서 반복하기 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter` 호출을 사용해서 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 데이터셋에 대해 반복하는 것을 `tf.function`의 함수 안에서 반복자를 사용할지, 외부에서 할지 선택할 수 있습니다. 반복자를 사용하여 `tf.function` 외부에서 데이터셋을 반복하는 방법이 아래에 나와 있습니다.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "7c73wGC00CzN" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + " for _ in range(EPOCHS):\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " train_iter = iter(train_dist_dataset)\n", + "\n", + " for _ in range(10):\n", + " total_loss += distributed_train_step(next(train_iter))\n", + " num_batches += 1\n", + " average_train_loss = total_loss / num_batches\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", + " print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))\n", + " train_accuracy.reset_states()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "GxVp48Oy0m6y" + }, + "source": [ + "### tf.function 내부에서 반복하기\n", + "전체 입력 `train_dist_dataset`에 대해서 `tf.function` 내부에서 `for x in ...` 생성자를 사용함으로써 반복을 하거나, 위에서 사용했던 것처럼 반복자를 사용함으로써 반복을 할 수 있습니다. 아래의 예제에서는 `tf.function`로 한 훈련의 에포크를 감싸고 그 함수에서 `train_dist_dataset`를 반복하는 것을 보여 줍니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-REzmcXv00qm" + }, + "outputs": [], + "source": [ + "with strategy.scope():\n", + " @tf.function\n", + " def distributed_train_epoch(dataset):\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " for x in dataset:\n", + " per_replica_losses = strategy.experimental_run_v2(train_step,\n", + " args=(x,))\n", + " total_loss += strategy.reduce(\n", + " tf.distribute.ReduceOp.SUM, per_replica_losses, axis=None)\n", + " num_batches += 1\n", + " return total_loss / tf.cast(num_batches, dtype=tf.float32)\n", + "\n", + " for epoch in range(EPOCHS):\n", + " train_loss = distributed_train_epoch(train_dist_dataset)\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", + " print (template.format(epoch+1, train_loss, train_accuracy.result()*100))\n", + "\n", + " train_accuracy.reset_states()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MuZGXiyC7ABR" + }, + "source": [ + "### 장치 간의 훈련 손실 기록하기\n", + "\n", + "노트: 일반적인 규칙으로, `tf.keras.Metrics`를 사용하여 샘플당 손실 값을 기록하고 장치 내부에서 값이 합쳐지는 것을 피해야 합니다.\n", + "\n", + "`tf.metrics.Mean`을 사용하여 여러 장치의 훈련 손실을 기록하는 것을 추천하지 *않습니다*. 왜냐하면 손실의 스케일을 조정하는 계산이 수행되기 때문입니다.\n", + "\n", + "예를 들어, 다음과 같은 조건의 훈련을 수행한다고 합시다.\n", + "* 두개의 장치\n", + "* 두개의 샘플들이 각 장치에 의해 처리됩니다.\n", + "* 손실 값을 산출합니다: 각각의 장치에 대해 [2, 3]과 [4, 5]\n", + "* Global batch size = 4\n", + "\n", + "손실의 스케일 조정을 하면, 손실 값을 더하고 GLOBAL_BATCH_SIZE로 나누어 각 장치에 대한 샘플당 손실값을 계산할 수 있습니다. 이 경우에는 (2 + 3) / 4 = 1.24와 (4 + 5) / 4 = 2.25입니다.\n", + "\n", + "만약 여러분이 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, 측정 지표의 `result()`가 메서드가 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xisYJaV9KZTN" + }, + "source": [ + "### 예제와 튜토리얼\n", + "사용자 정의 훈련루프를 포함한 분산 전략을 사용하는 몇 가지 예제가 있습니다.\n", + "\n", + "1. `MirroredStrategy`를 사용해서 MNIST를 훈련하는 [Tutorial](../tutorials/distribute/training_loops.ipynb)\n", + "2. `MirroredStrategy`를 사용하는 [DenseNet](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/densenet/distributed_train.py) 예제.\n", + "1. `MirroredStrategy`와 `TPUStrategy`를 사용해서 훈련하는 [BERT](https://github.com/tensorflow/models/blob/master/official/bert/run_classifier.py) 예제. 이 예제는 분산 훈련 중에 어떻게 체크포인트로부터 불러오는지와 어떻게 주기적으로 체크포인트들을 생성해 내는지를 이해하기에 정말 좋습니다.\n", + "2. `keras_use_ctl flag`를 사용해서 활성화 할 수 있는 MirroredStrategy를 이용해서 훈련되는 [NCF](https://github.com/tensorflow/models/blob/master/official/recommendation/ncf_keras_main.py) 예제\n", + "3. `MirroredStrategy`을 사용해서 훈련되는 [NMT](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/nmt_with_attention/distributed_train.py) 예제.\n", + "\n", + "더 많은 예제는 여기에 있습니다. [Distribution strategy guide](../../guide/distribute_strategy.ipynb#examples_and_tutorials)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6hEJNsokjOKs" + }, + "source": [ + "## 다음단계\n", + "\n", + "새로운 `tf.distribute.Strategy` API를 여러분의 모델에 적용해 보세요." + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "training_loops.ipynb의 사본", + "private_outputs": true, + "provenance": [], + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 4e4485ff790371ac675e3bd58242b7d631d45e11 Mon Sep 17 00:00:00 2001 From: NoelBird Date: Thu, 29 Aug 2019 03:10:38 +0900 Subject: [PATCH 4/6] apply all contents of reviews by 20190828 KST --- .../tutorials/distribute/training_loops.ipynb | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/site/ko/beta/tutorials/distribute/training_loops.ipynb b/site/ko/beta/tutorials/distribute/training_loops.ipynb index 959987b4ad0..42726dd2b30 100644 --- a/site/ko/beta/tutorials/distribute/training_loops.ipynb +++ b/site/ko/beta/tutorials/distribute/training_loops.ipynb @@ -83,7 +83,7 @@ "source": [ "이 예제는 사용자 정의 훈련 루프(custom training loops)와 함께 [`tf.distribute.Strategy`](https://www.tensorflow.org/guide/distribute_strategy)를 사용하는 법을 보여드립니다. 우리는 간단한 CNN 모델을 패션 MNIST 데이터셋에 대해 훈련을 할 것입니다. 패션 MNIST 데이터셋은 60000개의 28 x 28 크기의 훈련 이미지들과 10000개의 28 x 28 크기의 테스트 이미지들을 포함하고 있습니다.\n", "\n", - "이 예제는 모델을 훈련하기 위해서 사용자 정의 훈련 루프를 사용하고 있습니다. 왜냐하면 사용자 정의 훈련 루프는 유연하며 훈련을 더욱 잘 수행하도록 해주기 때문입니다. 또한, 모델과 훈련 루프를 디버깅하기도 더 쉽습니다." + "이 예제는 유연성을 높이고, 훈련을 더 잘 제어할 수 있도록 사용자 정의 훈련 루프를 사용합니다. 또한, 사용자 훈련 루프를 사용하는 것은 모델과 훈련 루프를 디버깅하기 쉬워집니다." ] }, { @@ -116,7 +116,7 @@ "id": "MM6W__qraV55" }, "source": [ - "## 패션 MNIST 데이터셋을 다운로드" + "## 패션 MNIST 데이터셋 다운로드" ] }, { @@ -172,7 +172,7 @@ "* 그래디언트들을 전부 더함으로써 모든 장치들 간에 그래디언트들이 동기화됩니다.\n", "* 동기화된 후에, 동일한 업데이트가 각 장치에 있는 변수의 복사본(copies)에 동일하게 적용됩니다.\n", "\n", - "노트: 여러분은 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" + "노트: 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" ] }, { @@ -200,7 +200,7 @@ }, "outputs": [], "source": [ - "print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))" + "print ('장치의 수: {}'.format(strategy.num_replicas_in_sync))" ] }, { @@ -340,14 +340,14 @@ "\n", "*왜 이렇게 할까요?*\n", "\n", - "* 위와 같이 계산하는 이유는 그래디언트들이 각 장치에서 계산된 다음, 모든 장치를 동기화하기 위해 이 그래디언트 값들을 전부 더하기 때문입니다.\n", + "* 위와 같이 계산하는 이유는 그래디언트들이 각 장치에서 계산된 다음, 모든 장치를 동기화하기 위해 이 그래디언트 값들을 전부 **더하기** 때문입니다.\n", "\n", "*이 것을 텐서플로에서는 어떻게 할까요?*\n", "\n", "\n", - "* 만약 이 예제처럼 사용자 정의 훈련 루프를 작성한다면, 다음과 같이 샘플당 손실을 더하고 GLOBAL_BATCH_SIZE로 나누어야 합니다.\n", + "* 만약 이 **예제처럼** 사용자 정의 훈련 루프를 **작성한다면**, **다음과 같이 샘플당** 손실을 더하고 GLOBAL_BATCH_SIZE로 **나누어야** 합니다.\n", "`scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE)`\n", - "또는 `tf.nn.compute_average_loss` 함수를 사용할 수도 있습니다. 이 함수는 샘플당 손실과 선택적으로 샘플 가중치, GLOBAL_BATCH_SIZE를 매개변수 값으로 받고 스케일이 조정된 손실을 반환합니다.\n", + "또는 `tf.nn.compute_average_loss` **함수를** 사용할 수도 있습니다. 이 함수는 **샘플당 손실과 선택적으로 샘플 가중치, GLOBAL_BATCH_SIZE를 매개변수 값으로 받고** 스케일이 조정된 손실을 반환합니다.\n", "\n", "* 만약 규제 손실을 사용하는 모델이라면, 장치 개수로 손실 값을 스케일 조정해야 합니다. 이는 `tf.nn_scale_regularization_loss` 함수를 사용하여 처리할 수 있습니다.\n", "\n", @@ -356,7 +356,7 @@ "* 이런 축소(`reduction`)와 스케일 조정은 케라스의 `model.compile`과 `model.fit`에서 자동적으로 수행됩니다.\n", "\n", "* 만약 `tf.keras.losses` 클래스(아래의 예제에서처럼)를 사용한다면, reduction 매개변수를 명시적으로 `NONE` 또는 `SUM` 중 하나로 표시해야 합니다. `AUTO`가 허용되지 않는 것은 사용자가 분산 모드에서 어떻게 축소할지 명시적으로 지정하는 것이 바람직하기 때문입니다.\n", - "`SUM_OVER_BATCH_SIZE`가 허용되지 않는 것은 `SUM_OVER_BATCH_SIZE`는 장치당 배치 크기로만 나눈 다음에 장치의 수로 나눈 것을 사용자에게 반환하기 때문입니다(reduction이 되어지지 않은 채로). 이 점은 아마도 놓치기 쉬을거에요. 그래서 우리는 이렇게 하는 것 대신에 사용자들에게 reduction을 사용자 스스로 명시적으로 하기를 요청합니다." + "현재 `SUM_OVER_BATCH_SIZE`가 장치당 배치 크기로만 나누고 장치 개수로 나누는 것은 사용자에게 위임하기 때문입니다. 그래서 이렇게 하는 것 대신에 사용자가 명시적으로 축소를 수행하도록 만드는 것이 좋습니다." ] }, { @@ -370,11 +370,11 @@ "outputs": [], "source": [ "with strategy.scope():\n", - " # reduction을 `none`으로 설정합니다. 그래서 우리는 reduction을 나중에 하고,\n", + " # reduction을 `none`으로 설정합니다. 그래서 우리는 축소를 나중에 하고,\n", " # GLOBAL_BATCH_SIZE로 나눌 수 있습니다.\n", " loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n", " reduction=tf.keras.losses.Reduction.NONE)\n", - " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy로 하셔도 됩니다.\n", + " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy를 사용해도 됩니다.\n", " def compute_loss(labels, predictions):\n", " per_example_loss = loss_object(labels, predictions)\n", " return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)" @@ -387,9 +387,9 @@ "id": "w8y54-o9T2Ni" }, "source": [ - "## 손실과 정확도를 따르는 지표 정의하기\n", + "## 손실과 정확도를 기록하기 위한 지표 정의하기\n", "\n", - "이 지표(metrics)는 평가에서의 손실과 훈련과 평가에서의 정확도를 추적합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." + "이 지표(metrics)는 테스트 손실과 훈련 정확도, 테스트 정확도를 기록합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." ] }, { @@ -486,7 +486,7 @@ "source": [ "with strategy.scope():\n", " # `experimental_run_v2`는 주어진 계산을 복사하고,\n", - " # 분산 입력(distributed input)을 입력값으로 계산을 실행합니다.\n", + " # 분산된 입력으로 계산을 수행합니다.\n", " \n", " @tf.function\n", " def distributed_train_step(dataset_inputs):\n", @@ -515,8 +515,8 @@ " if epoch % 2 == 0:\n", " checkpoint.save(checkpoint_prefix)\n", "\n", - " template = (\"Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, \"\n", - " \"Test Accuracy: {}\")\n", + " template = (\"에포크 {}, 손실: {}, 정확도: {}, 테스트 손실: {}, \"\n", + " \"테스트 정확도: {}\")\n", " print (template.format(epoch+1, train_loss,\n", " train_accuracy.result()*100, test_loss.result(),\n", " test_accuracy.result()*100))\n", @@ -537,7 +537,7 @@ "* 이 예제는 `train_dist_dataset`과 `test_dist_dataset`을 `for x in ...` 구조를 통해서 반복합니다.\n", "* 스케일이 조정된 손실은 `distributed_train_step`의 반환값입니다. `tf.distribute.Strategy.reduce` 호출을 사용해서 장치들 간의 스케일이 조정된 손실 값을 전부 합칩니다. 그리고 나서 `tf.distribute.Strategy.reduce` 반환 값을 더하는 식으로 배치 간의 손실을 모읍니다.\n", "* `tf.keras.Metrics`는 `tf.distribute.Strategy.experimental_run_v2`에 의해서 실행되는 `train_step`과 `test_step` 함수 안에서 업데이트 되어야 합니다.\n", - "* `tf.distribute.Strategy.experimental_run_v2`는 그 전략안에 포함된 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. 여러분은 `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. 여러분은 `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." + "* `tf.distribute.Strategy.experimental_run_v2`는 그 전략안에 포함된 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." ] }, { @@ -547,7 +547,7 @@ "id": "-q5qp31IQD8t" }, "source": [ - "## 최신 체크포인트를 불러와서 평가하기" + "## 최신 체크포인트를 불러와서 테스트하기" ] }, { @@ -611,7 +611,7 @@ "for images, labels in test_dataset:\n", " eval_step(images, labels)\n", "\n", - "print ('Accuracy after restoring the saved model without strategy: {}'.format(\n", + "print ('전략을 사용하지 않고, 저장된 모델을 복원한 후의 정확도: {}'.format(\n", " eval_accuracy.result()*100))" ] }, @@ -626,7 +626,7 @@ "\n", "### 반복자(iterator)를 사용하기\n", "\n", - "만약 주어진 스텝의 수에 따라서 반복하기 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter` 호출을 사용해서 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 데이터셋에 대해 반복하는 것을 `tf.function`의 함수 안에서 반복자를 사용할지, 외부에서 할지 선택할 수 있습니다. 반복자를 사용하여 `tf.function` 외부에서 데이터셋을 반복하는 방법이 아래에 나와 있습니다.\n" + "만약 주어진 스텝의 수에 따라서 반복하기 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter`를 호출하여 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 또한, `tf.funtion` 내부 또는 외부에서 데이터셋을 반복하도록 설정 할 수 있습니다. 다음은 반복자를 사용하여 `tf.function` 외부에서 데이터셋을 반복하는 코드 예제입니다.\n" ] }, { @@ -650,7 +650,7 @@ " num_batches += 1\n", " average_train_loss = total_loss / num_batches\n", "\n", - " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", + " template = (\"에포크 {}, 손실: {}, 정확도: {}\")\n", " print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))\n", " train_accuracy.reset_states()" ] @@ -719,7 +719,7 @@ "\n", "손실의 스케일 조정을 하면, 손실 값을 더하고 GLOBAL_BATCH_SIZE로 나누어 각 장치에 대한 샘플당 손실값을 계산할 수 있습니다. 이 경우에는 (2 + 3) / 4 = 1.24와 (4 + 5) / 4 = 2.25입니다.\n", "\n", - "만약 여러분이 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, 측정 지표의 `result()`가 메서드가 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." + "만약 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, 측정 지표의 `result()`가 메서드가 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." ] }, { @@ -750,7 +750,7 @@ "source": [ "## 다음단계\n", "\n", - "새로운 `tf.distribute.Strategy` API를 여러분의 모델에 적용해 보세요." + "새로운 `tf.distribute.Strategy` API를 모델에 적용해 보세요." ] } ], From e2811d00e85995f365d35e72b56071a306583461 Mon Sep 17 00:00:00 2001 From: Billy Lamberta Date: Thu, 19 Sep 2019 15:03:41 -0700 Subject: [PATCH 5/6] Move file for TF2 --- .../distribute/custom_training.ipynb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename site/ko/{beta/tutorials/distribute/training_loops.ipynb => tutorials/distribute/custom_training.ipynb} (100%) diff --git a/site/ko/beta/tutorials/distribute/training_loops.ipynb b/site/ko/tutorials/distribute/custom_training.ipynb similarity index 100% rename from site/ko/beta/tutorials/distribute/training_loops.ipynb rename to site/ko/tutorials/distribute/custom_training.ipynb From d0d9792284770fb76b2c37350d32778ea4ea1749 Mon Sep 17 00:00:00 2001 From: Billy Lamberta Date: Thu, 19 Sep 2019 15:17:33 -0700 Subject: [PATCH 6/6] Fix file names for TF2 --- .../distribute/custom_training.ipynb | 1572 +++++++++-------- 1 file changed, 788 insertions(+), 784 deletions(-) diff --git a/site/ko/tutorials/distribute/custom_training.ipynb b/site/ko/tutorials/distribute/custom_training.ipynb index 42726dd2b30..b0c86311ad9 100644 --- a/site/ko/tutorials/distribute/custom_training.ipynb +++ b/site/ko/tutorials/distribute/custom_training.ipynb @@ -1,785 +1,789 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MhoQ0WE77laV" - }, - "source": [ - "##### Copyright 2019 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "both", - "colab": {}, - "colab_type": "code", - "id": "_ckMIh7O7s6D" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "jYysdyb-CaWM" - }, - "source": [ - "# 훈련 루프와 함께 tf.distribute.Strategy 사용하기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "S5Uhzt6vVIB2" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " TensorFlow.org에서 보기\n", - " \n", - " 구글 코랩(Colab)에서 실행하기\n", - " \n", - " 깃허브(GitHub) 소스 보기\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "tUP8LMdYtWPz" - }, - "source": [ - "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 [공식 영문 문서](https://github.com/tensorflow/docs/blob/master/site/en/r2/guide/using_gpu.ipynb)의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 [tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 [docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로 메일을 보내주시기 바랍니다." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "FbVhjPpzn6BM" - }, - "source": [ - "이 예제는 사용자 정의 훈련 루프(custom training loops)와 함께 [`tf.distribute.Strategy`](https://www.tensorflow.org/guide/distribute_strategy)를 사용하는 법을 보여드립니다. 우리는 간단한 CNN 모델을 패션 MNIST 데이터셋에 대해 훈련을 할 것입니다. 패션 MNIST 데이터셋은 60000개의 28 x 28 크기의 훈련 이미지들과 10000개의 28 x 28 크기의 테스트 이미지들을 포함하고 있습니다.\n", - "\n", - "이 예제는 유연성을 높이고, 훈련을 더 잘 제어할 수 있도록 사용자 정의 훈련 루프를 사용합니다. 또한, 사용자 훈련 루프를 사용하는 것은 모델과 훈련 루프를 디버깅하기 쉬워집니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "dzLKpmZICaWN" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function, unicode_literals\n", - "\n", - "# 텐서플로 임포트\n", - "!pip install tensorflow-gpu==2.0.0-beta1\n", - "import tensorflow as tf\n", - "\n", - "# 헬퍼 라이브러리들\n", - "import numpy as np\n", - "import os\n", - "\n", - "print(tf.__version__)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MM6W__qraV55" - }, - "source": [ - "## 패션 MNIST 데이터셋 다운로드" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "7MqDQO0KCaWS" - }, - "outputs": [], - "source": [ - "fashion_mnist = tf.keras.datasets.fashion_mnist\n", - "\n", - "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()\n", - "\n", - "\n", - "# 하나의 차원을 배열에 추가 -> 새로운 shape == (28, 28, 1)\n", - "# 이렇게 하는 이유는 우리의 모델에서 첫 번째 층이 합성곱 층이고\n", - "# 합성곱 층은 4D 입력을 요구하기 때문입니다.\n", - "# (batch_size, height, width, channels).\n", - "# batch_size 차원은 나중에 추가할 것입니다.\n", - "\n", - "train_images = train_images[..., None]\n", - "test_images = test_images[..., None]\n", - "\n", - "# 이미지를 [0, 1] 범위로 변경하기.\n", - "train_images = train_images / np.float32(255)\n", - "test_images = test_images / np.float32(255)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "4AXoHhrsbdF3" - }, - "source": [ - "## 변수와 그래프를 분산하는 전략 만들기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "5mVuLZhbem8d" - }, - "source": [ - "`tf.distribute.MirroredStrategy` 전략이 어떻게 동작할까요?\n", - "* 모든 변수와 모델 그래프는 장치(replicas, 다른 문서에서는 replica가 분산 훈련에서 장치 등에 복제된 모델을 의미하는 경우가 있으나 이 문서에서는 장치 자체를 의미합니다)에 복제됩니다.\n", - "* 입력은 장치에 고르게 분배되어 들어갑니다.\n", - "* 각 장치는 주어지는 입력에 대해서 손실(loss)과 그래디언트를 계산합니다.\n", - "* 그래디언트들을 전부 더함으로써 모든 장치들 간에 그래디언트들이 동기화됩니다.\n", - "* 동기화된 후에, 동일한 업데이트가 각 장치에 있는 변수의 복사본(copies)에 동일하게 적용됩니다.\n", - "\n", - "노트: 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "F2VeZUWUj5S4" - }, - "outputs": [], - "source": [ - "# 만약 장치들의 목록이 `tf.distribute.MirroredStrategy` 생성자 안에 명시되어 있지 않다면,\n", - "# 자동으로 장치를 인식할 것입니다.\n", - "strategy = tf.distribute.MirroredStrategy()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "ZngeM_2o0_JO" - }, - "outputs": [], - "source": [ - "print ('장치의 수: {}'.format(strategy.num_replicas_in_sync))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "k53F5I_IiGyI" - }, - "source": [ - "## 입력 파이프라인 설정하기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "0Qb6nDgxiN_n" - }, - "source": [ - "그래프와 변수를 플랫폼과 무관한 SavedModel 형식으로 내보냅니다. 모델을 내보냈다면, 모델을 불러올 때 범위(scope)를 지정해도 되고 하지 않아도 됩니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "jwJtsCQhHK-E" - }, - "outputs": [], - "source": [ - "BUFFER_SIZE = len(train_images)\n", - "\n", - "BATCH_SIZE_PER_REPLICA = 64\n", - "GLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync\n", - "\n", - "EPOCHS = 10" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "J7fj3GskHC8g" - }, - "source": [ - "분산 데이터셋들을 `strategy.scope` 내에 생성합니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "WYrMNNDhAvVl" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - "\n", - " train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).shuffle(BUFFER_SIZE).batch(GLOBAL_BATCH_SIZE) \n", - " train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)\n", - " \n", - " test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE) \n", - " test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bAXAo_wWbWSb" - }, - "source": [ - "## 모델 만들기\n", - "\n", - "`tf.keras.Sequential`을 사용해서 모델을 생성합니다. Model Subclassing API로도 모델 생성을 할 수 있습니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "9ODch-OFCaW4" - }, - "outputs": [], - "source": [ - "def create_model():\n", - " model = tf.keras.Sequential([\n", - " tf.keras.layers.Conv2D(32, 3, activation='relu'),\n", - " tf.keras.layers.MaxPooling2D(),\n", - " tf.keras.layers.Conv2D(64, 3, activation='relu'),\n", - " tf.keras.layers.MaxPooling2D(),\n", - " tf.keras.layers.Flatten(),\n", - " tf.keras.layers.Dense(64, activation='relu'),\n", - " tf.keras.layers.Dense(10, activation='softmax')\n", - " ])\n", - "\n", - " return model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "9iagoTBfijUz" - }, - "outputs": [], - "source": [ - "# 체크포인트들을 저장하기 위해서 체크포인트 디렉토리를 생성합니다.\n", - "checkpoint_dir = './training_checkpoints'\n", - "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "e-wlFFZbP33n" - }, - "source": [ - "## 손실 함수 정의하기\n", - "\n", - "일반적으로, GPU/CPU 비율이 1인 단일 장치에서 손실은 입력 배치(batch)의 샘플 개수로 나누어집니다.\n", - "\n", - "*그렇다면, `tf.distribute.Strategy`를 사용할 때, 손실은 어떻게 계산되어야 할까요?*\n", - "\n", - "* 예를들면, 4개의 GPU가 있고 입력 배치 크기가 64라고 하죠. 입력 배치 하나가 여러 개의 장치(4개의 GPU)에 분배됩니다. 각 장치는 크기가 16인 입력을 받습니다.\n", - "\n", - "* 각 장치에 있는 모델은 해당 입력에 대해 정방향 계산(forward pass)을 수행하고 손실을 계산합니다. 손실을 장치에 할당된 입력 샘플의 수(BATCH_SIZE_PER_REPLICA = 16)로 나누는 것이 아니라 GLOBAL_BATCH_SIZE(64)로 나누어야 합니다.\n", - "\n", - "*왜 이렇게 할까요?*\n", - "\n", - "* 위와 같이 계산하는 이유는 그래디언트들이 각 장치에서 계산된 다음, 모든 장치를 동기화하기 위해 이 그래디언트 값들을 전부 **더하기** 때문입니다.\n", - "\n", - "*이 것을 텐서플로에서는 어떻게 할까요?*\n", - "\n", - "\n", - "* 만약 이 **예제처럼** 사용자 정의 훈련 루프를 **작성한다면**, **다음과 같이 샘플당** 손실을 더하고 GLOBAL_BATCH_SIZE로 **나누어야** 합니다.\n", - "`scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE)`\n", - "또는 `tf.nn.compute_average_loss` **함수를** 사용할 수도 있습니다. 이 함수는 **샘플당 손실과 선택적으로 샘플 가중치, GLOBAL_BATCH_SIZE를 매개변수 값으로 받고** 스케일이 조정된 손실을 반환합니다.\n", - "\n", - "* 만약 규제 손실을 사용하는 모델이라면, 장치 개수로 손실 값을 스케일 조정해야 합니다. 이는 `tf.nn_scale_regularization_loss` 함수를 사용하여 처리할 수 있습니다.\n", - "\n", - "* `tf.reduce_mean`을 사용하는 것은 추천하지 않습니다. 이렇게 하는 것은 손실을 실제 장치당 배치 크기로 나눕니다. 이 실제 장치당 배치 크기는 아마 각 단계(step)마다 크기가 다를 수 있습니다.\n", - "\n", - "* 이런 축소(`reduction`)와 스케일 조정은 케라스의 `model.compile`과 `model.fit`에서 자동적으로 수행됩니다.\n", - "\n", - "* 만약 `tf.keras.losses` 클래스(아래의 예제에서처럼)를 사용한다면, reduction 매개변수를 명시적으로 `NONE` 또는 `SUM` 중 하나로 표시해야 합니다. `AUTO`가 허용되지 않는 것은 사용자가 분산 모드에서 어떻게 축소할지 명시적으로 지정하는 것이 바람직하기 때문입니다.\n", - "현재 `SUM_OVER_BATCH_SIZE`가 장치당 배치 크기로만 나누고 장치 개수로 나누는 것은 사용자에게 위임하기 때문입니다. 그래서 이렇게 하는 것 대신에 사용자가 명시적으로 축소를 수행하도록 만드는 것이 좋습니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "R144Wci782ix" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - " # reduction을 `none`으로 설정합니다. 그래서 우리는 축소를 나중에 하고,\n", - " # GLOBAL_BATCH_SIZE로 나눌 수 있습니다.\n", - " loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n", - " reduction=tf.keras.losses.Reduction.NONE)\n", - " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy를 사용해도 됩니다.\n", - " def compute_loss(labels, predictions):\n", - " per_example_loss = loss_object(labels, predictions)\n", - " return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "w8y54-o9T2Ni" - }, - "source": [ - "## 손실과 정확도를 기록하기 위한 지표 정의하기\n", - "\n", - "이 지표(metrics)는 테스트 손실과 훈련 정확도, 테스트 정확도를 기록합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "zt3AHb46Tr3w" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - " test_loss = tf.keras.metrics.Mean(name='test_loss')\n", - "\n", - " train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", - " name='train_accuracy')\n", - " test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", - " name='test_accuracy')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "iuKuNXPORfqJ" - }, - "source": [ - "## 훈련 루프" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "OrMmakq5EqeQ" - }, - "outputs": [], - "source": [ - "# 모델과 옵티마이저는 `strategy.scope`에서 만들어져야 합니다.\n", - "with strategy.scope():\n", - " model = create_model()\n", - "\n", - " optimizer = tf.keras.optimizers.Adam()\n", - "\n", - " checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "3UX43wUu04EL" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - " def train_step(inputs):\n", - " images, labels = inputs\n", - "\n", - " with tf.GradientTape() as tape:\n", - " predictions = model(images, training=True)\n", - " loss = compute_loss(labels, predictions)\n", - "\n", - " gradients = tape.gradient(loss, model.trainable_variables)\n", - " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", - "\n", - " train_accuracy.update_state(labels, predictions)\n", - " return loss \n", - "\n", - " def test_step(inputs):\n", - " images, labels = inputs\n", - "\n", - " predictions = model(images, training=False)\n", - " t_loss = loss_object(labels, predictions)\n", - "\n", - " test_loss.update_state(t_loss)\n", - " test_accuracy.update_state(labels, predictions)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "gX975dMSNw0e" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - " # `experimental_run_v2`는 주어진 계산을 복사하고,\n", - " # 분산된 입력으로 계산을 수행합니다.\n", - " \n", - " @tf.function\n", - " def distributed_train_step(dataset_inputs):\n", - " per_replica_losses = strategy.experimental_run_v2(train_step,\n", - " args=(dataset_inputs,))\n", - " return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,\n", - " axis=None)\n", - " \n", - " @tf.function\n", - " def distributed_test_step(dataset_inputs):\n", - " return strategy.experimental_run_v2(test_step, args=(dataset_inputs,))\n", - "\n", - " for epoch in range(EPOCHS):\n", - " # 훈련 루프\n", - " total_loss = 0.0\n", - " num_batches = 0\n", - " for x in train_dist_dataset:\n", - " total_loss += distributed_train_step(x)\n", - " num_batches += 1\n", - " train_loss = total_loss / num_batches\n", - "\n", - " # 테스트 루프\n", - " for x in test_dist_dataset:\n", - " distributed_test_step(x)\n", - "\n", - " if epoch % 2 == 0:\n", - " checkpoint.save(checkpoint_prefix)\n", - "\n", - " template = (\"에포크 {}, 손실: {}, 정확도: {}, 테스트 손실: {}, \"\n", - " \"테스트 정확도: {}\")\n", - " print (template.format(epoch+1, train_loss,\n", - " train_accuracy.result()*100, test_loss.result(),\n", - " test_accuracy.result()*100))\n", - "\n", - " test_loss.reset_states()\n", - " train_accuracy.reset_states()\n", - " test_accuracy.reset_states()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Z1YvXqOpwy08" - }, - "source": [ - "위의 예제에서 주목해야 하는 부분\n", - "* 이 예제는 `train_dist_dataset`과 `test_dist_dataset`을 `for x in ...` 구조를 통해서 반복합니다.\n", - "* 스케일이 조정된 손실은 `distributed_train_step`의 반환값입니다. `tf.distribute.Strategy.reduce` 호출을 사용해서 장치들 간의 스케일이 조정된 손실 값을 전부 합칩니다. 그리고 나서 `tf.distribute.Strategy.reduce` 반환 값을 더하는 식으로 배치 간의 손실을 모읍니다.\n", - "* `tf.keras.Metrics`는 `tf.distribute.Strategy.experimental_run_v2`에 의해서 실행되는 `train_step`과 `test_step` 함수 안에서 업데이트 되어야 합니다.\n", - "* `tf.distribute.Strategy.experimental_run_v2`는 그 전략안에 포함된 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "-q5qp31IQD8t" - }, - "source": [ - "## 최신 체크포인트를 불러와서 테스트하기" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "WNW2P00bkMGJ" - }, - "source": [ - "`tf.distribute.Strategy`를 사용해서 체크포인트가 만들어진 모델은 전략 사용 여부에 상관없이 불러올 수 있습니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "pg3B-Cw_cn3a" - }, - "outputs": [], - "source": [ - "eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", - " name='eval_accuracy')\n", - "\n", - "new_model = create_model()\n", - "new_optimizer = tf.keras.optimizers.Adam()\n", - "\n", - "test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "7qYii7KUYiSM" - }, - "outputs": [], - "source": [ - "@tf.function\n", - "def eval_step(images, labels):\n", - " predictions = new_model(images, training=False)\n", - " eval_accuracy(labels, predictions)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "LeZ6eeWRoUNq" - }, - "outputs": [], - "source": [ - "checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)\n", - "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n", - "\n", - "for images, labels in test_dataset:\n", - " eval_step(images, labels)\n", - "\n", - "print ('전략을 사용하지 않고, 저장된 모델을 복원한 후의 정확도: {}'.format(\n", - " eval_accuracy.result()*100))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "EbcI87EEzhzg" - }, - "source": [ - "## 데이터셋에 대해 반복작업을 하는 다른 방법들\n", - "\n", - "### 반복자(iterator)를 사용하기\n", - "\n", - "만약 주어진 스텝의 수에 따라서 반복하기 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter`를 호출하여 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 또한, `tf.funtion` 내부 또는 외부에서 데이터셋을 반복하도록 설정 할 수 있습니다. 다음은 반복자를 사용하여 `tf.function` 외부에서 데이터셋을 반복하는 코드 예제입니다.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "7c73wGC00CzN" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - " for _ in range(EPOCHS):\n", - " total_loss = 0.0\n", - " num_batches = 0\n", - " train_iter = iter(train_dist_dataset)\n", - "\n", - " for _ in range(10):\n", - " total_loss += distributed_train_step(next(train_iter))\n", - " num_batches += 1\n", - " average_train_loss = total_loss / num_batches\n", - "\n", - " template = (\"에포크 {}, 손실: {}, 정확도: {}\")\n", - " print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))\n", - " train_accuracy.reset_states()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "GxVp48Oy0m6y" - }, - "source": [ - "### tf.function 내부에서 반복하기\n", - "전체 입력 `train_dist_dataset`에 대해서 `tf.function` 내부에서 `for x in ...` 생성자를 사용함으로써 반복을 하거나, 위에서 사용했던 것처럼 반복자를 사용함으로써 반복을 할 수 있습니다. 아래의 예제에서는 `tf.function`로 한 훈련의 에포크를 감싸고 그 함수에서 `train_dist_dataset`를 반복하는 것을 보여 줍니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "-REzmcXv00qm" - }, - "outputs": [], - "source": [ - "with strategy.scope():\n", - " @tf.function\n", - " def distributed_train_epoch(dataset):\n", - " total_loss = 0.0\n", - " num_batches = 0\n", - " for x in dataset:\n", - " per_replica_losses = strategy.experimental_run_v2(train_step,\n", - " args=(x,))\n", - " total_loss += strategy.reduce(\n", - " tf.distribute.ReduceOp.SUM, per_replica_losses, axis=None)\n", - " num_batches += 1\n", - " return total_loss / tf.cast(num_batches, dtype=tf.float32)\n", - "\n", - " for epoch in range(EPOCHS):\n", - " train_loss = distributed_train_epoch(train_dist_dataset)\n", - "\n", - " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", - " print (template.format(epoch+1, train_loss, train_accuracy.result()*100))\n", - "\n", - " train_accuracy.reset_states()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MuZGXiyC7ABR" - }, - "source": [ - "### 장치 간의 훈련 손실 기록하기\n", - "\n", - "노트: 일반적인 규칙으로, `tf.keras.Metrics`를 사용하여 샘플당 손실 값을 기록하고 장치 내부에서 값이 합쳐지는 것을 피해야 합니다.\n", - "\n", - "`tf.metrics.Mean`을 사용하여 여러 장치의 훈련 손실을 기록하는 것을 추천하지 *않습니다*. 왜냐하면 손실의 스케일을 조정하는 계산이 수행되기 때문입니다.\n", - "\n", - "예를 들어, 다음과 같은 조건의 훈련을 수행한다고 합시다.\n", - "* 두개의 장치\n", - "* 두개의 샘플들이 각 장치에 의해 처리됩니다.\n", - "* 손실 값을 산출합니다: 각각의 장치에 대해 [2, 3]과 [4, 5]\n", - "* Global batch size = 4\n", - "\n", - "손실의 스케일 조정을 하면, 손실 값을 더하고 GLOBAL_BATCH_SIZE로 나누어 각 장치에 대한 샘플당 손실값을 계산할 수 있습니다. 이 경우에는 (2 + 3) / 4 = 1.24와 (4 + 5) / 4 = 2.25입니다.\n", - "\n", - "만약 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, 측정 지표의 `result()`가 메서드가 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "xisYJaV9KZTN" - }, - "source": [ - "### 예제와 튜토리얼\n", - "사용자 정의 훈련루프를 포함한 분산 전략을 사용하는 몇 가지 예제가 있습니다.\n", - "\n", - "1. `MirroredStrategy`를 사용해서 MNIST를 훈련하는 [Tutorial](../tutorials/distribute/training_loops.ipynb)\n", - "2. `MirroredStrategy`를 사용하는 [DenseNet](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/densenet/distributed_train.py) 예제.\n", - "1. `MirroredStrategy`와 `TPUStrategy`를 사용해서 훈련하는 [BERT](https://github.com/tensorflow/models/blob/master/official/bert/run_classifier.py) 예제. 이 예제는 분산 훈련 중에 어떻게 체크포인트로부터 불러오는지와 어떻게 주기적으로 체크포인트들을 생성해 내는지를 이해하기에 정말 좋습니다.\n", - "2. `keras_use_ctl flag`를 사용해서 활성화 할 수 있는 MirroredStrategy를 이용해서 훈련되는 [NCF](https://github.com/tensorflow/models/blob/master/official/recommendation/ncf_keras_main.py) 예제\n", - "3. `MirroredStrategy`을 사용해서 훈련되는 [NMT](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/nmt_with_attention/distributed_train.py) 예제.\n", - "\n", - "더 많은 예제는 여기에 있습니다. [Distribution strategy guide](../../guide/distribute_strategy.ipynb#examples_and_tutorials)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "6hEJNsokjOKs" - }, - "source": [ - "## 다음단계\n", - "\n", - "새로운 `tf.distribute.Strategy` API를 모델에 적용해 보세요." - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "training_loops.ipynb의 사본", - "private_outputs": true, - "provenance": [], - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "custom_training.ipynb", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MhoQ0WE77laV" + }, + "source": [ + "##### Copyright 2019 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "_ckMIh7O7s6D", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jYysdyb-CaWM" + }, + "source": [ + "# 훈련 루프와 함께 tf.distribute.Strategy 사용하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S5Uhzt6vVIB2" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " TensorFlow.org에서 보기\n", + " \n", + " 구글 코랩(Colab)에서 실행하기\n", + " \n", + " 깃허브(GitHub) 소스 보기\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "tUP8LMdYtWPz" + }, + "source": [ + "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 [공식 영문 문서](https://github.com/tensorflow/docs/blob/master/site/en/guide/gpu.ipynb)의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 [tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 [docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로 메일을 보내주시기 바랍니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FbVhjPpzn6BM" + }, + "source": [ + "이 예제는 사용자 정의 훈련 루프(custom training loops)와 함께 [`tf.distribute.Strategy`](https://www.tensorflow.org/guide/distribute_strategy)를 사용하는 법을 보여드립니다. 우리는 간단한 CNN 모델을 패션 MNIST 데이터셋에 대해 훈련을 할 것입니다. 패션 MNIST 데이터셋은 60000개의 28 x 28 크기의 훈련 이미지들과 10000개의 28 x 28 크기의 테스트 이미지들을 포함하고 있습니다.\n", + "\n", + "이 예제는 유연성을 높이고, 훈련을 더 잘 제어할 수 있도록 사용자 정의 훈련 루프를 사용합니다. 또한, 사용자 훈련 루프를 사용하는 것은 모델과 훈련 루프를 디버깅하기 쉬워집니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "dzLKpmZICaWN", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function, unicode_literals\n", + "\n", + "# Import TensorFlow\n", + "try:\n", + " # %tensorflow_version only exists in Colab.\n", + " %tensorflow_version 2.x\n", + "except Exception:\n", + " pass\n", + "import tensorflow as tf\n", + "\n", + "# 헬퍼 라이브러리들\n", + "import numpy as np\n", + "import os\n", + "\n", + "print(tf.__version__)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MM6W__qraV55" + }, + "source": [ + "## 패션 MNIST 데이터셋 다운로드" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7MqDQO0KCaWS", + "colab": {} + }, + "source": [ + "fashion_mnist = tf.keras.datasets.fashion_mnist\n", + "\n", + "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()\n", + "\n", + "\n", + "# 하나의 차원을 배열에 추가 -> 새로운 shape == (28, 28, 1)\n", + "# 이렇게 하는 이유는 우리의 모델에서 첫 번째 층이 합성곱 층이고\n", + "# 합성곱 층은 4D 입력을 요구하기 때문입니다.\n", + "# (batch_size, height, width, channels).\n", + "# batch_size 차원은 나중에 추가할 것입니다.\n", + "\n", + "train_images = train_images[..., None]\n", + "test_images = test_images[..., None]\n", + "\n", + "# 이미지를 [0, 1] 범위로 변경하기.\n", + "train_images = train_images / np.float32(255)\n", + "test_images = test_images / np.float32(255)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4AXoHhrsbdF3" + }, + "source": [ + "## 변수와 그래프를 분산하는 전략 만들기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "5mVuLZhbem8d" + }, + "source": [ + "`tf.distribute.MirroredStrategy` 전략이 어떻게 동작할까요?\n", + "* 모든 변수와 모델 그래프는 장치(replicas, 다른 문서에서는 replica가 분산 훈련에서 장치 등에 복제된 모델을 의미하는 경우가 있으나 이 문서에서는 장치 자체를 의미합니다)에 복제됩니다.\n", + "* 입력은 장치에 고르게 분배되어 들어갑니다.\n", + "* 각 장치는 주어지는 입력에 대해서 손실(loss)과 그래디언트를 계산합니다.\n", + "* 그래디언트들을 전부 더함으로써 모든 장치들 간에 그래디언트들이 동기화됩니다.\n", + "* 동기화된 후에, 동일한 업데이트가 각 장치에 있는 변수의 복사본(copies)에 동일하게 적용됩니다.\n", + "\n", + "노트: 하나의 범위를 지정해서 모든 코드를 집어넣을 수 있습니다. 자, 같이 살펴보시죠!" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "F2VeZUWUj5S4", + "colab": {} + }, + "source": [ + "# 만약 장치들의 목록이 `tf.distribute.MirroredStrategy` 생성자 안에 명시되어 있지 않다면,\n", + "# 자동으로 장치를 인식할 것입니다.\n", + "strategy = tf.distribute.MirroredStrategy()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ZngeM_2o0_JO", + "colab": {} + }, + "source": [ + "print ('장치의 수: {}'.format(strategy.num_replicas_in_sync))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "k53F5I_IiGyI" + }, + "source": [ + "## 입력 파이프라인 설정하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0Qb6nDgxiN_n" + }, + "source": [ + "그래프와 변수를 플랫폼과 무관한 SavedModel 형식으로 내보냅니다. 모델을 내보냈다면, 모델을 불러올 때 범위(scope)를 지정해도 되고 하지 않아도 됩니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jwJtsCQhHK-E", + "colab": {} + }, + "source": [ + "BUFFER_SIZE = len(train_images)\n", + "\n", + "BATCH_SIZE_PER_REPLICA = 64\n", + "GLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync\n", + "\n", + "EPOCHS = 10" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "J7fj3GskHC8g" + }, + "source": [ + "분산 데이터셋들을 `strategy.scope` 내에 생성합니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "WYrMNNDhAvVl", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + "\n", + " train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).shuffle(BUFFER_SIZE).batch(GLOBAL_BATCH_SIZE) \n", + " train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)\n", + " \n", + " test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE) \n", + " test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bAXAo_wWbWSb" + }, + "source": [ + "## 모델 만들기\n", + "\n", + "`tf.keras.Sequential`을 사용해서 모델을 생성합니다. Model Subclassing API로도 모델 생성을 할 수 있습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9ODch-OFCaW4", + "colab": {} + }, + "source": [ + "def create_model():\n", + " model = tf.keras.Sequential([\n", + " tf.keras.layers.Conv2D(32, 3, activation='relu'),\n", + " tf.keras.layers.MaxPooling2D(),\n", + " tf.keras.layers.Conv2D(64, 3, activation='relu'),\n", + " tf.keras.layers.MaxPooling2D(),\n", + " tf.keras.layers.Flatten(),\n", + " tf.keras.layers.Dense(64, activation='relu'),\n", + " tf.keras.layers.Dense(10, activation='softmax')\n", + " ])\n", + "\n", + " return model" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9iagoTBfijUz", + "colab": {} + }, + "source": [ + "# 체크포인트들을 저장하기 위해서 체크포인트 디렉토리를 생성합니다.\n", + "checkpoint_dir = './training_checkpoints'\n", + "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "e-wlFFZbP33n" + }, + "source": [ + "## 손실 함수 정의하기\n", + "\n", + "일반적으로, GPU/CPU 비율이 1인 단일 장치에서 손실은 입력 배치(batch)의 샘플 개수로 나누어집니다.\n", + "\n", + "*그렇다면, `tf.distribute.Strategy`를 사용할 때, 손실은 어떻게 계산되어야 할까요?*\n", + "\n", + "* 예를들면, 4개의 GPU가 있고 입력 배치 크기가 64라고 하죠. 입력 배치 하나가 여러 개의 장치(4개의 GPU)에 분배됩니다. 각 장치는 크기가 16인 입력을 받습니다.\n", + "\n", + "* 각 장치에 있는 모델은 해당 입력에 대해 정방향 계산(forward pass)을 수행하고 손실을 계산합니다. 손실을 장치에 할당된 입력 샘플의 수(BATCH_SIZE_PER_REPLICA = 16)로 나누는 것이 아니라 GLOBAL_BATCH_SIZE(64)로 나누어야 합니다.\n", + "\n", + "*왜 이렇게 할까요?*\n", + "\n", + "* 위와 같이 계산하는 이유는 그래디언트들이 각 장치에서 계산된 다음, 모든 장치를 동기화하기 위해 이 그래디언트 값들을 전부 **더하기** 때문입니다.\n", + "\n", + "*이 것을 텐서플로에서는 어떻게 할까요?*\n", + "\n", + "\n", + "* 만약 이 **예제처럼** 사용자 정의 훈련 루프를 **작성한다면**, **다음과 같이 샘플당** 손실을 더하고 GLOBAL_BATCH_SIZE로 **나누어야** 합니다.\n", + "`scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE)`\n", + "또는 `tf.nn.compute_average_loss` **함수를** 사용할 수도 있습니다. 이 함수는 **샘플당 손실과 선택적으로 샘플 가중치, GLOBAL_BATCH_SIZE를 매개변수 값으로 받고** 스케일이 조정된 손실을 반환합니다.\n", + "\n", + "* 만약 규제 손실을 사용하는 모델이라면, 장치 개수로 손실 값을 스케일 조정해야 합니다. 이는 `tf.nn_scale_regularization_loss` 함수를 사용하여 처리할 수 있습니다.\n", + "\n", + "* `tf.reduce_mean`을 사용하는 것은 추천하지 않습니다. 이렇게 하는 것은 손실을 실제 장치당 배치 크기로 나눕니다. 이 실제 장치당 배치 크기는 아마 각 단계(step)마다 크기가 다를 수 있습니다.\n", + "\n", + "* 이런 축소(`reduction`)와 스케일 조정은 케라스의 `model.compile`과 `model.fit`에서 자동적으로 수행됩니다.\n", + "\n", + "* 만약 `tf.keras.losses` 클래스(아래의 예제에서처럼)를 사용한다면, reduction 매개변수를 명시적으로 `NONE` 또는 `SUM` 중 하나로 표시해야 합니다. `AUTO`가 허용되지 않는 것은 사용자가 분산 모드에서 어떻게 축소할지 명시적으로 지정하는 것이 바람직하기 때문입니다.\n", + "현재 `SUM_OVER_BATCH_SIZE`가 장치당 배치 크기로만 나누고 장치 개수로 나누는 것은 사용자에게 위임하기 때문입니다. 그래서 이렇게 하는 것 대신에 사용자가 명시적으로 축소를 수행하도록 만드는 것이 좋습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "R144Wci782ix", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " # reduction을 `none`으로 설정합니다. 그래서 우리는 축소를 나중에 하고,\n", + " # GLOBAL_BATCH_SIZE로 나눌 수 있습니다.\n", + " loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n", + " reduction=tf.keras.losses.Reduction.NONE)\n", + " # 또는 loss_fn = tf.keras.losses.sparse_categorical_crossentropy를 사용해도 됩니다.\n", + " def compute_loss(labels, predictions):\n", + " per_example_loss = loss_object(labels, predictions)\n", + " return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "w8y54-o9T2Ni" + }, + "source": [ + "## 손실과 정확도를 기록하기 위한 지표 정의하기\n", + "\n", + "이 지표(metrics)는 테스트 손실과 훈련 정확도, 테스트 정확도를 기록합니다. `.result()`를 사용해서 누적된 통계값들을 언제나 볼 수 있습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "zt3AHb46Tr3w", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " test_loss = tf.keras.metrics.Mean(name='test_loss')\n", + "\n", + " train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='train_accuracy')\n", + " test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='test_accuracy')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iuKuNXPORfqJ" + }, + "source": [ + "## 훈련 루프" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "OrMmakq5EqeQ", + "colab": {} + }, + "source": [ + "# 모델과 옵티마이저는 `strategy.scope`에서 만들어져야 합니다.\n", + "with strategy.scope():\n", + " model = create_model()\n", + "\n", + " optimizer = tf.keras.optimizers.Adam()\n", + "\n", + " checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "3UX43wUu04EL", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " def train_step(inputs):\n", + " images, labels = inputs\n", + "\n", + " with tf.GradientTape() as tape:\n", + " predictions = model(images, training=True)\n", + " loss = compute_loss(labels, predictions)\n", + "\n", + " gradients = tape.gradient(loss, model.trainable_variables)\n", + " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", + "\n", + " train_accuracy.update_state(labels, predictions)\n", + " return loss \n", + "\n", + " def test_step(inputs):\n", + " images, labels = inputs\n", + "\n", + " predictions = model(images, training=False)\n", + " t_loss = loss_object(labels, predictions)\n", + "\n", + " test_loss.update_state(t_loss)\n", + " test_accuracy.update_state(labels, predictions)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "gX975dMSNw0e", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " # `experimental_run_v2`는 주어진 계산을 복사하고,\n", + " # 분산된 입력으로 계산을 수행합니다.\n", + " \n", + " @tf.function\n", + " def distributed_train_step(dataset_inputs):\n", + " per_replica_losses = strategy.experimental_run_v2(train_step,\n", + " args=(dataset_inputs,))\n", + " return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,\n", + " axis=None)\n", + " \n", + " @tf.function\n", + " def distributed_test_step(dataset_inputs):\n", + " return strategy.experimental_run_v2(test_step, args=(dataset_inputs,))\n", + "\n", + " for epoch in range(EPOCHS):\n", + " # 훈련 루프\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " for x in train_dist_dataset:\n", + " total_loss += distributed_train_step(x)\n", + " num_batches += 1\n", + " train_loss = total_loss / num_batches\n", + "\n", + " # 테스트 루프\n", + " for x in test_dist_dataset:\n", + " distributed_test_step(x)\n", + "\n", + " if epoch % 2 == 0:\n", + " checkpoint.save(checkpoint_prefix)\n", + "\n", + " template = (\"에포크 {}, 손실: {}, 정확도: {}, 테스트 손실: {}, \"\n", + " \"테스트 정확도: {}\")\n", + " print (template.format(epoch+1, train_loss,\n", + " train_accuracy.result()*100, test_loss.result(),\n", + " test_accuracy.result()*100))\n", + "\n", + " test_loss.reset_states()\n", + " train_accuracy.reset_states()\n", + " test_accuracy.reset_states()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Z1YvXqOpwy08" + }, + "source": [ + "위의 예제에서 주목해야 하는 부분\n", + "* 이 예제는 `train_dist_dataset`과 `test_dist_dataset`을 `for x in ...` 구조를 통해서 반복합니다.\n", + "* 스케일이 조정된 손실은 `distributed_train_step`의 반환값입니다. `tf.distribute.Strategy.reduce` 호출을 사용해서 장치들 간의 스케일이 조정된 손실 값을 전부 합칩니다. 그리고 나서 `tf.distribute.Strategy.reduce` 반환 값을 더하는 식으로 배치 간의 손실을 모읍니다.\n", + "* `tf.keras.Metrics`는 `tf.distribute.Strategy.experimental_run_v2`에 의해서 실행되는 `train_step`과 `test_step` 함수 안에서 업데이트 되어야 합니다.\n", + "* `tf.distribute.Strategy.experimental_run_v2`는 그 전략안에 포함된 각 지역 복제 모델로부터 결과값을 반환해 줍니다. 그리고 이 결과를 사용하는 몇 가지 방법들이 있습니다. `tf.distribute.Strategy.reduce`를 이용하여 값들을 합칠 수 있습니다. `tf.distribute.Strategy.experimental_local_results`를 사용해서 결과값(지역 복제 모델 당 하나의 결과값)에 들어있는 값들의 리스트를 얻을 수도 있습니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-q5qp31IQD8t" + }, + "source": [ + "## 최신 체크포인트를 불러와서 테스트하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "WNW2P00bkMGJ" + }, + "source": [ + "`tf.distribute.Strategy`를 사용해서 체크포인트가 만들어진 모델은 전략 사용 여부에 상관없이 불러올 수 있습니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "pg3B-Cw_cn3a", + "colab": {} + }, + "source": [ + "eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", + " name='eval_accuracy')\n", + "\n", + "new_model = create_model()\n", + "new_optimizer = tf.keras.optimizers.Adam()\n", + "\n", + "test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7qYii7KUYiSM", + "colab": {} + }, + "source": [ + "@tf.function\n", + "def eval_step(images, labels):\n", + " predictions = new_model(images, training=False)\n", + " eval_accuracy(labels, predictions)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "LeZ6eeWRoUNq", + "colab": {} + }, + "source": [ + "checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)\n", + "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n", + "\n", + "for images, labels in test_dataset:\n", + " eval_step(images, labels)\n", + "\n", + "print ('전략을 사용하지 않고, 저장된 모델을 복원한 후의 정확도: {}'.format(\n", + " eval_accuracy.result()*100))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EbcI87EEzhzg" + }, + "source": [ + "## 데이터셋에 대해 반복작업을 하는 다른 방법들\n", + "\n", + "### 반복자(iterator)를 사용하기\n", + "\n", + "만약 주어진 스텝의 수에 따라서 반복하기 원하면서 전체 데이터셋을 보는 것을 원치 않는다면, `iter`를 호출하여 반복자를 만들 수 있습니다. 그 다음 명시적으로 `next`를 호출합니다. 또한, `tf.funtion` 내부 또는 외부에서 데이터셋을 반복하도록 설정 할 수 있습니다. 다음은 반복자를 사용하여 `tf.function` 외부에서 데이터셋을 반복하는 코드 예제입니다.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7c73wGC00CzN", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " for _ in range(EPOCHS):\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " train_iter = iter(train_dist_dataset)\n", + "\n", + " for _ in range(10):\n", + " total_loss += distributed_train_step(next(train_iter))\n", + " num_batches += 1\n", + " average_train_loss = total_loss / num_batches\n", + "\n", + " template = (\"에포크 {}, 손실: {}, 정확도: {}\")\n", + " print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))\n", + " train_accuracy.reset_states()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "GxVp48Oy0m6y" + }, + "source": [ + "### tf.function 내부에서 반복하기\n", + "전체 입력 `train_dist_dataset`에 대해서 `tf.function` 내부에서 `for x in ...` 생성자를 사용함으로써 반복을 하거나, 위에서 사용했던 것처럼 반복자를 사용함으로써 반복을 할 수 있습니다. 아래의 예제에서는 `tf.function`로 한 훈련의 에포크를 감싸고 그 함수에서 `train_dist_dataset`를 반복하는 것을 보여 줍니다." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "-REzmcXv00qm", + "colab": {} + }, + "source": [ + "with strategy.scope():\n", + " @tf.function\n", + " def distributed_train_epoch(dataset):\n", + " total_loss = 0.0\n", + " num_batches = 0\n", + " for x in dataset:\n", + " per_replica_losses = strategy.experimental_run_v2(train_step,\n", + " args=(x,))\n", + " total_loss += strategy.reduce(\n", + " tf.distribute.ReduceOp.SUM, per_replica_losses, axis=None)\n", + " num_batches += 1\n", + " return total_loss / tf.cast(num_batches, dtype=tf.float32)\n", + "\n", + " for epoch in range(EPOCHS):\n", + " train_loss = distributed_train_epoch(train_dist_dataset)\n", + "\n", + " template = (\"Epoch {}, Loss: {}, Accuracy: {}\")\n", + " print (template.format(epoch+1, train_loss, train_accuracy.result()*100))\n", + "\n", + " train_accuracy.reset_states()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MuZGXiyC7ABR" + }, + "source": [ + "### 장치 간의 훈련 손실 기록하기\n", + "\n", + "노트: 일반적인 규칙으로, `tf.keras.Metrics`를 사용하여 샘플당 손실 값을 기록하고 장치 내부에서 값이 합쳐지는 것을 피해야 합니다.\n", + "\n", + "`tf.metrics.Mean`을 사용하여 여러 장치의 훈련 손실을 기록하는 것을 추천하지 *않습니다*. 왜냐하면 손실의 스케일을 조정하는 계산이 수행되기 때문입니다.\n", + "\n", + "예를 들어, 다음과 같은 조건의 훈련을 수행한다고 합시다.\n", + "* 두개의 장치\n", + "* 두개의 샘플들이 각 장치에 의해 처리됩니다.\n", + "* 손실 값을 산출합니다: 각각의 장치에 대해 [2, 3]과 [4, 5]\n", + "* Global batch size = 4\n", + "\n", + "손실의 스케일 조정을 하면, 손실 값을 더하고 GLOBAL_BATCH_SIZE로 나누어 각 장치에 대한 샘플당 손실값을 계산할 수 있습니다. 이 경우에는 (2 + 3) / 4 = 1.24와 (4 + 5) / 4 = 2.25입니다.\n", + "\n", + "만약 `tf.metrics.Mean`을 사용해서 두 개의 장치에 대해 손실값을 계산한다면, 결과값이 다릅니다. 이 예제에서는, 측정 지표의 `result()`가 메서드가 호출될 때 `total`이 3.50이고 `count`가 2입니다. 결과값은 `total/count`가 1.75가 됩니다. `tf.keras.Metrics`를 이용해서 계산한 손실값이 추가적인 요인에 의해서 크기조정되며, 이 추가적인 요인은 동기화되는 장치의 개수입니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xisYJaV9KZTN" + }, + "source": [ + "### 예제와 튜토리얼\n", + "사용자 정의 훈련루프를 포함한 분산 전략을 사용하는 몇 가지 예제가 있습니다.\n", + "\n", + "1. `MirroredStrategy`를 사용해서 MNIST를 훈련하는 [Tutorial](../tutorials/distribute/custom_training.ipynb)\n", + "2. `MirroredStrategy`를 사용하는 [DenseNet](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/densenet/distributed_train.py) 예제.\n", + "1. `MirroredStrategy`와 `TPUStrategy`를 사용해서 훈련하는 [BERT](https://github.com/tensorflow/models/blob/master/official/bert/run_classifier.py) 예제. 이 예제는 분산 훈련 중에 어떻게 체크포인트로부터 불러오는지와 어떻게 주기적으로 체크포인트들을 생성해 내는지를 이해하기에 정말 좋습니다.\n", + "2. `keras_use_ctl flag`를 사용해서 활성화 할 수 있는 MirroredStrategy를 이용해서 훈련되는 [NCF](https://github.com/tensorflow/models/blob/master/official/recommendation/ncf_keras_main.py) 예제\n", + "3. `MirroredStrategy`을 사용해서 훈련되는 [NMT](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/models/nmt_with_attention/distributed_train.py) 예제.\n", + "\n", + "더 많은 예제는 여기에 있습니다. [Distribution strategy guide](../../guide/distribute_strategy.ipynb#examples_and_tutorials)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6hEJNsokjOKs" + }, + "source": [ + "## 다음단계\n", + "\n", + "새로운 `tf.distribute.Strategy` API를 모델에 적용해 보세요." + ] + } + ] +} \ No newline at end of file