{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "Tce3stUlHN0L" }, "source": [ "##### Copyright 2020 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2022-12-14T21:55:09.587020Z", "iopub.status.busy": "2022-12-14T21:55:09.586574Z", "iopub.status.idle": "2022-12-14T21:55:09.590488Z", "shell.execute_reply": "2022-12-14T21:55:09.589936Z" }, "id": "tuOe1ymfHZPu" }, "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": { "id": "qFdPvlXBOdUN" }, "source": [ "# 효과적인 Tensorflow 2" ] }, { "cell_type": "markdown", "metadata": { "id": "MfBg1C5NB3X0" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
TensorFlow.org에서보기 Google Colab에서 실행하기 GitHub에서 소스 보기 노트북 다운로드하기
" ] }, { "cell_type": "markdown", "metadata": { "id": "xHxb-dlhMIzW" }, "source": [ "## 개요\n", "\n", "이 가이드는 TensorFlow 2(TF2)를 사용하여 코드를 작성하기 위한 모범 사례 목록을 제공하며 최근에 TensorFlow 1(TF1)에서 전환한 사용자를 위해 작성되었습니다. TF1 코드를 TF2로 마이그레이션하는 방법에 대한 자세한 내용은 [가이드의 마이그레이션 섹션](https://tensorflow.org/guide/migrate)을 참고하세요." ] }, { "cell_type": "markdown", "metadata": { "id": "MUXex9ctTuDB" }, "source": [ "## 설치하기\n", "\n", "이 가이드의 예제에서 사용하는 TensorFlow 및 기타 종속 항목을 가져옵니다." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:09.593828Z", "iopub.status.busy": "2022-12-14T21:55:09.593370Z", "iopub.status.idle": "2022-12-14T21:55:11.917418Z", "shell.execute_reply": "2022-12-14T21:55:11.916755Z" }, "id": "IqR2PQG4ZaZ0" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:10.524042: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", "2022-12-14 21:55:10.524133: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", "2022-12-14 21:55:10.524142: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n" ] } ], "source": [ "import tensorflow as tf\n", "import tensorflow_datasets as tfds" ] }, { "cell_type": "markdown", "metadata": { "id": "Ngds9zateIY8" }, "source": [ "## 관용적 텐서플로우 2.0 권장 사항" ] }, { "cell_type": "markdown", "metadata": { "id": "B3RdHaroMAi4" }, "source": [ "### 코드를 더 작은 모듈로 리팩토링\n", "\n", "필요에 따라 호출되는 더 작은 함수로 코드를 리팩토링하는 것이 좋습니다. 최고의 성능을 위해서는 `tf.function`에서 가능한 가장 큰 계산 블록을 장식해야 합니다(`tf.function`로 호출하여 중첩한 Python 함수는 `tf.function`에 다른 `jit_compile` 설정을 사용하려는 경우가 아니면 별도의 장식이 필요하지 않음). 사용 사례에 따라 여러 훈련 단계 또는 전체 훈련 루프가 될 수도 있습니다. 추론 사용 사례는 단일 모델 전달 경로일 수 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "Rua1l8et3Evd" }, "source": [ "### 일부 `tf.keras.optimizer`의 기본 훈련률 조정하기\n", "\n", "\n", "\n", "일부 Keras 옵티마이저는 TF2에서 다른 훈련률을 갖습니다. 모델의 수렴 동작이 변경되면 기본 훈련률을 확인해야 합니다.\n", "\n", "`optimizers.SGD`, `optimizers.Adam` 또는 `optimizers.RMSprop`에는 변경사항이 없습니다.\n", "\n", "다음 기본 훈련률이 변경되었습니다.\n", "\n", "- `0.01`에서 `0.001`까지 `optimizers.Adagrad`\n", "- `1.0`에서 `0.001`까지 `optimizers.Adadelta`\n", "- `0.002`에서 `0.001`까지 `optimizers.Adamax`\n", "- `0.002`에서 `0.001`까지 `optimizers.Nadam`" ] }, { "cell_type": "markdown", "metadata": { "id": "z6LfkpsEldEV" }, "source": [ "### `tf.Module` 및 Keras 레이어를 사용하여 변수 관리\n", "\n", "`tf.Module` 및 `tf.keras.layers.Layer`는 편리한 `variables` 및 `trainable_variables` 속성을 제공하고, 이는 모든 종속 변수를 재귀적으로 수집합니다. 이렇게 하면 변수가 사용되는 위치에서 로컬로 변수를 쉽게 관리할 수 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "WQ2U0rj1oBlc" }, "source": [ "Keras 레이어와 모델은 tf.train.Checkpointable로부터 상속하고 @tf.function과 통합되어 Keras 객체에서 저장 모델을 직접 검사하거나 내보낼 수 있도록 합니다. 이러한 통합을 활용하기 위해 반드시 Keras의 Model.fit API를 사용해야 하는 것은 아닙니다.\n", "\n", "Keras를 사용하여 관련 변수의 하위 집합을 수집하는 방법을 알아보려면 Keras 가이드의 [전이 학습 및 미세 조정](https://www.tensorflow.org/guide/keras/transfer_learning#transfer_learning_fine-tuning_with_a_custom_training_loop) 섹션을 읽어보세요." ] }, { "cell_type": "markdown", "metadata": { "id": "j34MsfxWodG6" }, "source": [ "### `tf.data.Dataset`와 `tf.function` 결합하기\n", "\n", "[TensorFlow 데이터세트](https://tensorflow.org/datasets) 패키지(`tfds`)에는 사전 정의된 데이터세트를 `tf.data.Dataset` 객체로 로드하는 유틸리티가 포함되어 있습니다. 이 예제에서는 `tfds`를 사용하여 MNIST 데이터세트를 로드할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:11.922658Z", "iopub.status.busy": "2022-12-14T21:55:11.921923Z", "iopub.status.idle": "2022-12-14T21:55:15.984135Z", "shell.execute_reply": "2022-12-14T21:55:15.983429Z" }, "id": "BMgxaLH74_s-" }, "outputs": [], "source": [ "datasets, info = tfds.load(name='mnist', with_info=True, as_supervised=True)\n", "mnist_train, mnist_test = datasets['train'], datasets['test']" ] }, { "cell_type": "markdown", "metadata": { "id": "hPJhEuvj5VfR" }, "source": [ "그런 다음 훈련에 사용할 데이터를 준비합니다.\n", "\n", "- 각 이미지의 크기를 다시 조정합니다.\n", "- 예제의 순서를 셔플링합니다.\n", "- 이미지 및 레이블 배치를 수집합니다.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:15.988174Z", "iopub.status.busy": "2022-12-14T21:55:15.987564Z", "iopub.status.idle": "2022-12-14T21:55:15.991398Z", "shell.execute_reply": "2022-12-14T21:55:15.990834Z" }, "id": "StBRHtJM2S7o" }, "outputs": [], "source": [ "BUFFER_SIZE = 10 # Use a much larger value for real code\n", "BATCH_SIZE = 64\n", "NUM_EPOCHS = 5\n", "\n", "\n", "def scale(image, label):\n", " image = tf.cast(image, tf.float32)\n", " image /= 255\n", "\n", " return image, label" ] }, { "cell_type": "markdown", "metadata": { "id": "SKq14zKKFAdv" }, "source": [ "이 예제를 짧게 유지하려면 5개의 배치만 반환하도록 데이터세트를 자릅니다." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:15.994318Z", "iopub.status.busy": "2022-12-14T21:55:15.993771Z", "iopub.status.idle": "2022-12-14T21:55:16.042662Z", "shell.execute_reply": "2022-12-14T21:55:16.042065Z" }, "id": "_J-o4YjG2mkM" }, "outputs": [], "source": [ "train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)\n", "test_data = mnist_test.map(scale).batch(BATCH_SIZE)\n", "\n", "STEPS_PER_EPOCH = 5\n", "\n", "train_data = train_data.take(STEPS_PER_EPOCH)\n", "test_data = test_data.take(STEPS_PER_EPOCH)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:16.046014Z", "iopub.status.busy": "2022-12-14T21:55:16.045416Z", "iopub.status.idle": "2022-12-14T21:55:16.320486Z", "shell.execute_reply": "2022-12-14T21:55:16.319445Z" }, "id": "XEqdkH54VM6c" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:16.307777: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] } ], "source": [ "image_batch, label_batch = next(iter(train_data))" ] }, { "cell_type": "markdown", "metadata": { "id": "loTPH2Pz4_Oj" }, "source": [ "메모리에 맞는 훈련 데이터를 반복하려면 일반 Python 반복을 사용해야 합니다. 그렇지 않을때 이 디스크에서 훈련 데이터를 스트리밍하는 가장 좋은 방법은 `tf.data.Dataset`입니다. 데이터세트는 [반복할 수 있으며(반복기는 아님)](https://docs.python.org/3/glossary.html#term-iterable) 즉시 실행에서 다른 Python 반복기처럼 동작합니다. 코드를 `tf.function`로 래핑하여 데이터세트 비동기 프리페치/스트리밍 기능을 온전히 활용할 수 있습니다. 이는 AutoGraph를 사용하여 Python 반복기를 동등한 그래프 연산으로 교체합니다.\n", "\n", "```python\n", "@tf.function\n", "def train(model, dataset, optimizer):\n", " for x, y in dataset:\n", " with tf.GradientTape() as tape:\n", " # training=True is only needed if there are layers with different\n", " # behavior during training versus inference (e.g. Dropout).\n", " prediction = model(x, training=True)\n", " loss = loss_fn(prediction, y)\n", " gradients = tape.gradient(loss, model.trainable_variables)\n", " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", "```\n", "\n", "Keras Model.fit API를 사용하면 데이터세트 반복에 대해 신경 쓸 필요가 없습니다.\n", "\n", "```python\n", "model.compile(optimizer=optimizer, loss=loss_fn)\n", "model.fit(dataset)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "mSev7vZC5GJB" }, "source": [ "\n", "\n", "### Keras 훈련 루프 사용하기\n", "\n", "훈련 과정을 세부적으로 제어할 필요가 없다면 케라스의 내장 메서드인 fit, evaluate, predict를 사용하는 것이 좋습니다. 이 메서드들은 모델 구현(Sequential, 함수형 API, 클래스 상속)에 상관없이 일관된 훈련 인터페이스를 제공합니다.\n", "\n", "이 메소드의 장점은 다음과 같습니다.\n", "\n", "- Numpy 배열, Python 제너레이터, `tf.data.Datasets`를 사용할 수 있습니다.\n", "- 정규화 및 활성화 손실을 자동으로 적용합니다.\n", "- [하드웨어 구성에 관계 없이](distributed_training.ipynb) 훈련 코드가 동일하게 유지되는 `tf.distribute`를 지원합니다.\n", "- 손실 및 메트릭으로 임의의 호출 가능 항목을 지원합니다.\n", "- `tf.keras.callbacks.TensorBoard`와 같은 콜백과 사용자 정의 콜백을 지원합니다.\n", "- TensorFlow 그래프를 사용하여 자동으로 성능 기준을 맞춥니다.\n", "\n", "다음은 `Dataset`를 사용하여 모델을 훈련하는 예제입니다. 작동 방식에 대한 자세한 내용은 [튜토리얼](https://tensorflow.org/tutorials)을 확인합니다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:16.324505Z", "iopub.status.busy": "2022-12-14T21:55:16.323938Z", "iopub.status.idle": "2022-12-14T21:55:21.224676Z", "shell.execute_reply": "2022-12-14T21:55:21.223947Z" }, "id": "uzHFCzd45Rae" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 13s - loss: 2.5692 - accuracy: 0.1406" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 3s 6ms/step - loss: 1.5373 - accuracy: 0.5000\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 2/5\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:19.700111: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 0s - loss: 0.5156 - accuracy: 0.9219" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 0s 5ms/step - loss: 0.4361 - accuracy: 0.9312\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 3/5\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:19.979427: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 1s - loss: 0.3252 - accuracy: 0.9375" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 0s 5ms/step - loss: 0.2826 - accuracy: 0.9656\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 4/5\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:20.289201: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 1s - loss: 0.1927 - accuracy: 1.0000" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 0s 5ms/step - loss: 0.1974 - accuracy: 0.9875\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 5/5\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:20.576118: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 0s - loss: 0.1582 - accuracy: 1.0000" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 0s 5ms/step - loss: 0.1499 - accuracy: 1.0000\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:20.848689: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 1s - loss: 1.5915 - accuracy: 0.7344" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 0s 3ms/step - loss: 1.6309 - accuracy: 0.7531\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Loss 1.6308777332305908, Accuracy 0.753125011920929\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:21.212331: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] } ], "source": [ "model = tf.keras.Sequential([\n", " tf.keras.layers.Conv2D(32, 3, activation='relu',\n", " kernel_regularizer=tf.keras.regularizers.l2(0.02),\n", " input_shape=(28, 28, 1)),\n", " tf.keras.layers.MaxPooling2D(),\n", " tf.keras.layers.Flatten(),\n", " tf.keras.layers.Dropout(0.1),\n", " tf.keras.layers.Dense(64, activation='relu'),\n", " tf.keras.layers.BatchNormalization(),\n", " tf.keras.layers.Dense(10)\n", "])\n", "\n", "# Model is the full model w/o custom layers\n", "model.compile(optimizer='adam',\n", " loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n", " metrics=['accuracy'])\n", "\n", "model.fit(train_data, epochs=NUM_EPOCHS)\n", "loss, acc = model.evaluate(test_data)\n", "\n", "print(\"Loss {}, Accuracy {}\".format(loss, acc))" ] }, { "cell_type": "markdown", "metadata": { "id": "LQTaHTuK5S5A" }, "source": [ "\n", "\n", "### 훈련 루프 사용자 정의하기 및 자체 루프 작성하기\n", "\n", "Keras 모델이 적합하지만 훈련 단계 또는 외부 훈련 루프에 더 많은 유연성과 제어가 필요한 경우 자체 훈련 단계 또는 전체 훈련 루프를 구현할 수 있습니다. 자세한 내용은 [`fit` 사용자 정의하기](https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit)의 Keras 가이드를 참조해 주세요.\n", "\n", "`tf.keras.callbacks.Callback`으로 많은 것을 구현할 수도 있습니다.\n", "\n", "이 메서드에는 [이전에 언급한](#keras_training_loops) 많은 장점 외에도 훈련 단계와 외부 루프까지 제어할 수 있다는 장점도 있습니다.\n", "\n", "표준 훈련 루프에는 세 단계가 있습니다.\n", "\n", "1. Python 생성기 또는 `tf.data.Dataset`를 반복하여 예제 배치를 가져옵니다.\n", "2. `tf.GradientTape`를 사용하여 그래디언트를 수집합니다.\n", "3. `tf.keras.optimizers` 중 하나를 사용하여 모델 변수에 가중치 업데이트를 적용합니다.\n", "\n", "기억해야 하는 사항:\n", "\n", "- 서브 클래화된 레이어 및 모델의 `call` 메서드에 항상 `training` 인수를 포함합니다.\n", "- `training` 인수가 올바르게 설정된 모델을 호출하는지 확인합니다.\n", "- 사용법에 따라 데이터 배치에서 모델을 실행할 때까지 모델 변수가 존재하지 않을 수 있습니다.\n", "- 모델의 정규화 손실 등은 수동으로 처리해야 합니다.\n", "\n", "변수 이니셜라이저를 실행하거나 수동 제어 종속성을 추가할 필요가 없습니다. `tf.function`은 생성 시 자동 제어 종속성과 변수 초기화를 처리합니다." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:21.228340Z", "iopub.status.busy": "2022-12-14T21:55:21.227718Z", "iopub.status.idle": "2022-12-14T21:55:24.043207Z", "shell.execute_reply": "2022-12-14T21:55:24.042430Z" }, "id": "gQooejfYlQeF" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:22.897407: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Finished epoch 0\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:23.185574: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Finished epoch 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:23.467763: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Finished epoch 2\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:23.738925: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Finished epoch 3\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Finished epoch 4\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:24.029871: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] } ], "source": [ "model = tf.keras.Sequential([\n", " tf.keras.layers.Conv2D(32, 3, activation='relu',\n", " kernel_regularizer=tf.keras.regularizers.l2(0.02),\n", " input_shape=(28, 28, 1)),\n", " tf.keras.layers.MaxPooling2D(),\n", " tf.keras.layers.Flatten(),\n", " tf.keras.layers.Dropout(0.1),\n", " tf.keras.layers.Dense(64, activation='relu'),\n", " tf.keras.layers.BatchNormalization(),\n", " tf.keras.layers.Dense(10)\n", "])\n", "\n", "optimizer = tf.keras.optimizers.Adam(0.001)\n", "loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n", "\n", "@tf.function\n", "def train_step(inputs, labels):\n", " with tf.GradientTape() as tape:\n", " predictions = model(inputs, training=True)\n", " regularization_loss=tf.math.add_n(model.losses)\n", " pred_loss=loss_fn(labels, predictions)\n", " total_loss=pred_loss + regularization_loss\n", "\n", " gradients = tape.gradient(total_loss, model.trainable_variables)\n", " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", "\n", "for epoch in range(NUM_EPOCHS):\n", " for inputs, labels in train_data:\n", " train_step(inputs, labels)\n", " print(\"Finished epoch\", epoch)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "WikxMFGgo3oZ" }, "source": [ "### Python 제어 흐름으로 `tf.function` 활용하기\n", "\n", "`tf.function`은 데이터에 따라 결정되는 제어 흐름을 `tf.cond`와 `tf.while_loop`와 같은 그래프 모드로 변환하는 방법을 제공합니다.\n", "\n", "데이터에 의존하는 제어 흐름이 나타나는 대표적인 곳은 시퀀스 모델입니다. `tf.keras.layers.RNN`은 RNN 셀을 래핑하여 반복을 정적으로 또는 동적으로 전개할 수 있도록 합니다. 예를 들어 다음과 같은 동적 언롤을 다시 구현할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:24.047259Z", "iopub.status.busy": "2022-12-14T21:55:24.046685Z", "iopub.status.idle": "2022-12-14T21:55:24.053102Z", "shell.execute_reply": "2022-12-14T21:55:24.052506Z" }, "id": "n5UebfChRu4T" }, "outputs": [], "source": [ "class DynamicRNN(tf.keras.Model):\n", "\n", " def __init__(self, rnn_cell):\n", " super(DynamicRNN, self).__init__(self)\n", " self.cell = rnn_cell\n", "\n", " @tf.function(input_signature=[tf.TensorSpec(dtype=tf.float32, shape=[None, None, 3])])\n", " def call(self, input_data):\n", "\n", " # [batch, time, features] -> [time, batch, features]\n", " input_data = tf.transpose(input_data, [1, 0, 2])\n", " timesteps = tf.shape(input_data)[0]\n", " batch_size = tf.shape(input_data)[1]\n", " outputs = tf.TensorArray(tf.float32, timesteps)\n", " state = self.cell.get_initial_state(batch_size = batch_size, dtype=tf.float32)\n", " for i in tf.range(timesteps):\n", " output, state = self.cell(input_data[i], state)\n", " outputs = outputs.write(i, output)\n", " return tf.transpose(outputs.stack(), [1, 0, 2]), state" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:24.056378Z", "iopub.status.busy": "2022-12-14T21:55:24.055826Z", "iopub.status.idle": "2022-12-14T21:55:24.321531Z", "shell.execute_reply": "2022-12-14T21:55:24.320774Z" }, "id": "NhBI_SGKQVIB" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(10, 20, 13)\n" ] } ], "source": [ "lstm_cell = tf.keras.layers.LSTMCell(units = 13)\n", "\n", "my_rnn = DynamicRNN(lstm_cell)\n", "outputs, state = my_rnn(tf.random.normal(shape=[10,20,3]))\n", "print(outputs.shape)" ] }, { "cell_type": "markdown", "metadata": { "id": "du7bn3NX7iIr" }, "source": [ "자세한 정보는 [`tf.function` 가이드](https://www.tensorflow.org/guide/function)를 참고합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "SUAYhgL_NomT" }, "source": [ "### 새로운 스타일의 메트릭 및 손실\n", "\n", "메트릭과 손실은 모두 `tf.function`에서 즉시 작동하는 객체입니다.\n", "\n", "손실 객체는 호출할 수 있으며 (`y_true`, `y_pred`)를 인수로 예상합니다." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:24.325255Z", "iopub.status.busy": "2022-12-14T21:55:24.324721Z", "iopub.status.idle": "2022-12-14T21:55:24.347331Z", "shell.execute_reply": "2022-12-14T21:55:24.346735Z" }, "id": "Pf5gcwMzNs8F" }, "outputs": [ { "data": { "text/plain": [ "4.01815" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cce = tf.keras.losses.CategoricalCrossentropy(from_logits=True)\n", "cce([[1, 0]], [[-1.0,3.0]]).numpy()" ] }, { "cell_type": "markdown", "metadata": { "id": "a89m-wRfxyfV" }, "source": [ "#### 메트릭을 사용하여 데이터를 수집하고 표시하기\n", "\n", "`tf.metrics`를 사용하여 데이터를 집계하고 `tf.summary`를 사용하여 요약문을 기록하고 컨텍스트 관리자를 사용하여 작성자에게 리디렉션할 수 있습니다. 요약문은 작성자에게 직접 내보내지므로 호출 사이트에서 `step` 값을 제공해야 합니다.\n", "\n", "```python\n", "summary_writer = tf.summary.create_file_writer('/tmp/summaries')\n", "with summary_writer.as_default():\n", " tf.summary.scalar('loss', 0.1, step=42)\n", "```\n", "\n", "요약문으로 기록하기 전에 `tf.metrics`를 사용하여 데이터를 집계합니다. 메트릭은 정적(Stateful)이기에 `result` 메서드(예: `Mean.result`)를 호출하면 값이 누적되고 누적 결과가 반환됩니다. `Model.reset_states`로 누적된 값을 삭제합니다.\n", "\n", "```python\n", "def train(model, optimizer, dataset, log_freq=10):\n", " avg_loss = tf.keras.metrics.Mean(name='loss', dtype=tf.float32)\n", " for images, labels in dataset:\n", " loss = train_step(model, optimizer, images, labels)\n", " avg_loss.update_state(loss)\n", " if tf.equal(optimizer.iterations % log_freq, 0):\n", " tf.summary.scalar('loss', avg_loss.result(), step=optimizer.iterations)\n", " avg_loss.reset_states()\n", "\n", "def test(model, test_x, test_y, step_num):\n", " # training=False is only needed if there are layers with different\n", " # behavior during training versus inference (e.g. Dropout).\n", " loss = loss_fn(model(test_x, training=False), test_y)\n", " tf.summary.scalar('loss', loss, step=step_num)\n", "\n", "train_summary_writer = tf.summary.create_file_writer('/tmp/summaries/train')\n", "test_summary_writer = tf.summary.create_file_writer('/tmp/summaries/test')\n", "\n", "with train_summary_writer.as_default():\n", " train(model, optimizer, dataset)\n", "\n", "with test_summary_writer.as_default():\n", " test(model, test_x, test_y, optimizer.iterations)\n", "```\n", "\n", "텐서보드(TensorBoard)에서 요약문 로그 디렉터리를 지정하여 생성된 요약문을 시각화합니다.\n", "\n", "```shell\n", "tensorboard --logdir /tmp/summaries\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "0tx7FyM_RHwJ" }, "source": [ "텐서보드에서 시각화를 생성할 수 있도록 `tf.summary` API를 사용하여 요약문 데이터를 작성합니다. 자세한 정보는 `tf.summary` 가이드를 참고합니다." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:24.350666Z", "iopub.status.busy": "2022-12-14T21:55:24.350068Z", "iopub.status.idle": "2022-12-14T21:55:26.146375Z", "shell.execute_reply": "2022-12-14T21:55:26.145601Z" }, "id": "HAbA0fKW58CH" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:24.963973: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 0\n", " loss: 0.133\n", " accuracy: 0.991\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:25.238740: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 1\n", " loss: 0.113\n", " accuracy: 1.000\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:25.538122: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 2\n", " loss: 0.101\n", " accuracy: 1.000\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:25.844832: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 3\n", " loss: 0.088\n", " accuracy: 1.000\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 4\n", " loss: 0.079\n", " accuracy: 1.000\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:26.131845: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] } ], "source": [ "# Create the metrics\n", "loss_metric = tf.keras.metrics.Mean(name='train_loss')\n", "accuracy_metric = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')\n", "\n", "@tf.function\n", "def train_step(inputs, labels):\n", " with tf.GradientTape() as tape:\n", " predictions = model(inputs, training=True)\n", " regularization_loss=tf.math.add_n(model.losses)\n", " pred_loss=loss_fn(labels, predictions)\n", " total_loss=pred_loss + regularization_loss\n", "\n", " gradients = tape.gradient(total_loss, model.trainable_variables)\n", " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", " # Update the metrics\n", " loss_metric.update_state(total_loss)\n", " accuracy_metric.update_state(labels, predictions)\n", "\n", "\n", "for epoch in range(NUM_EPOCHS):\n", " # Reset the metrics\n", " loss_metric.reset_states()\n", " accuracy_metric.reset_states()\n", "\n", " for inputs, labels in train_data:\n", " train_step(inputs, labels)\n", " # Get the metric results\n", " mean_loss=loss_metric.result()\n", " mean_accuracy = accuracy_metric.result()\n", "\n", " print('Epoch: ', epoch)\n", " print(' loss: {:.3f}'.format(mean_loss))\n", " print(' accuracy: {:.3f}'.format(mean_accuracy))\n" ] }, { "cell_type": "markdown", "metadata": { "id": "bG9AaMzih3eh" }, "source": [ "#### 저장과 복원\n", "\n", "\n", "\n", "Keras 모델은 메트릭 이름을 일관성 있게 처리합니다. 메트릭 목록의 문자열을 전달하면 *일치하는* 문자열을 메트릭의 `name`으로 사용합니다. 이러한 이름은 `model.fit`로 반환한 기록 객체와 `keras.callbacks`에 전달된 로그에서 볼 수 있습니다. 메트릭 목록에서 전달한 문자열로 설정됩니다. " ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:26.150165Z", "iopub.status.busy": "2022-12-14T21:55:26.149504Z", "iopub.status.idle": "2022-12-14T21:55:27.816961Z", "shell.execute_reply": "2022-12-14T21:55:27.816150Z" }, "id": "1iODIsGDgyYd" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 6s - loss: 0.0776 - acc: 1.0000 - accuracy: 1.0000 - my_accuracy: 1.0000" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/5 [==============================] - 2s 5ms/step - loss: 0.0902 - acc: 0.9969 - accuracy: 0.9969 - my_accuracy: 0.9969\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 21:55:27.804136: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.\n" ] } ], "source": [ "model.compile(\n", " optimizer = tf.keras.optimizers.Adam(0.001),\n", " loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n", " metrics = ['acc', 'accuracy', tf.keras.metrics.SparseCategoricalAccuracy(name=\"my_accuracy\")])\n", "history = model.fit(train_data)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T21:55:27.820513Z", "iopub.status.busy": "2022-12-14T21:55:27.819939Z", "iopub.status.idle": "2022-12-14T21:55:27.824613Z", "shell.execute_reply": "2022-12-14T21:55:27.823949Z" }, "id": "8oGzs_TlisKJ" }, "outputs": [ { "data": { "text/plain": [ "dict_keys(['loss', 'acc', 'accuracy', 'my_accuracy'])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "history.history.keys()" ] }, { "cell_type": "markdown", "metadata": { "id": "JaB2z2XIyhcr" }, "source": [ "### 디버깅\n", "\n", "즉시 실행을 사용하여 코드를 단계별로 실행하고 형상, 데이터 유형 및 값을 검사할 수 있습니다. `tf.function`, `tf.keras` 등과 같은 특정 API는 성능 및 이식성을 위해 그래프 실행을 사용하도록 설계되었습니다. 디버깅할 때 이 코드 내에서 즉시 실행을 사용하려면 `tf.config.run_functions_eagerly(True)`를 사용합니다.\n", "\n", "예제:\n", "\n", "```python\n", "@tf.function\n", "def f(x):\n", " if x > 0:\n", " import pdb\n", " pdb.set_trace()\n", " x = x + 1\n", " return x\n", "\n", "tf.config.run_functions_eagerly(True)\n", "f(tf.constant(1))\n", "```\n", "\n", "```\n", ">>> f()\n", "-> x = x + 1\n", "(Pdb) l\n", " 6 @tf.function\n", " 7 def f(x):\n", " 8 if x > 0:\n", " 9 import pdb\n", " 10 pdb.set_trace()\n", " 11 -> x = x + 1\n", " 12 return x\n", " 13\n", " 14 tf.config.run_functions_eagerly(True)\n", " 15 f(tf.constant(1))\n", "[EOF]\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "gdvGF2FvbBXZ" }, "source": [ "이는 Keras 모델 및 즉시 실행을 지원하는 기타 API에서도 작동합니다.\n", "\n", "```python\n", "class CustomModel(tf.keras.models.Model):\n", "\n", " @tf.function\n", " def call(self, input_data):\n", " if tf.reduce_mean(input_data) > 0:\n", " return input_data\n", " else:\n", " import pdb\n", " pdb.set_trace()\n", " return input_data // 2\n", "\n", "\n", "tf.config.run_functions_eagerly(True)\n", "model = CustomModel()\n", "model(tf.constant([-2, -4]))\n", "```\n", "\n", "```\n", ">>> call()\n", "-> return input_data // 2\n", "(Pdb) l\n", " 10 if tf.reduce_mean(input_data) > 0:\n", " 11 return input_data\n", " 12 else:\n", " 13 import pdb\n", " 14 pdb.set_trace()\n", " 15 -> return input_data // 2\n", " 16\n", " 17\n", " 18 tf.config.run_functions_eagerly(True)\n", " 19 model = CustomModel()\n", " 20 model(tf.constant([-2, -4]))\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "S0-F-bvJXKD8" }, "source": [ "참고 사항:\n", "\n", "- `fit`, `evaluate`, `predict`와 같은 `tf.keras.Model` 메서드는 관련된 `tf.function`을 사용하여 [그래프](https://www.tensorflow.org/guide/intro_to_graphs)로 실행합니다.\n", "\n", "- `tf.keras.Model.compile`을 사용할 경우 `run_eagerly = True`를 설정하여 `Model` 로직이 `tf.function`으로 래핑되지 않도록 합니다.\n", "\n", "- `tf.data.experimental.enable_debug_mode`를 사용하여 `tf.data`에 대해 디버그 모드를 사용하도록 설정합니다. 자세한 내용은 [API 문서](https://www.tensorflow.org/api_docs/python/tf/data/experimental/enable_debug_mode)를 참고합니다.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "wxa5yKK7bym0" }, "source": [ "### 객체에 `tf.Tensors` 유지 금지\n", "\n", "이러한 텐서 객체는 `tf.function` 또는 Eager 컨텍스트에서 생성될 수 있으며 이러한 텐서는 다르게 동작합니다. 중간 값에는 항상 `tf.Tensor`를 사용합니다.\n", "\n", "상태를 추적하려면 양쪽 컨텍스트에서 항상 사용할 수 있는 `tf.Variable`을 사용합니다. 자세히 알아보려면`tf.Variable` 가이드를 읽어보세요.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "FdXLLYa2uAyx" }, "source": [ "## 리소스 및 추가 읽을거리\n", "\n", "- TF2 사용 방법에 대해 자세히 알아보려면 TF2 [가이드](https://tensorflow.org/guide) 및 [튜토리얼](https://tensorflow.org/tutorials)을 읽어보세요.\n", "\n", "- 이전에 TF1.x를 사용한 경우 코드를 TF2로 마이그레이션하는 것이 좋습니다. 자세히 알아보려면 [마이그레이션 가이드](https://tensorflow.org/guide/migrate)를 읽어보세요." ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "effective_tf2.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "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.9.16" } }, "nbformat": 4, "nbformat_minor": 0 }