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 diff --git a/site/ko/tutorials/distribute/custom_training.ipynb b/site/ko/tutorials/distribute/custom_training.ipynb new file mode 100644 index 00000000000..b0c86311ad9 --- /dev/null +++ b/site/ko/tutorials/distribute/custom_training.ipynb @@ -0,0 +1,789 @@ +{ + "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