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": [
+ "
"
+ ]
+ },
+ {
+ "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