{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "wJcYs_ERTnnI" }, "source": [ "##### Copyright 2018 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2021-01-15T02:09:46.583637Z", "iopub.status.busy": "2021-01-15T02:09:46.582985Z", "iopub.status.idle": "2021-01-15T02:09:46.585062Z", "shell.execute_reply": "2021-01-15T02:09:46.585507Z" }, "id": "HMUDt0CiUJk9" }, "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": "77z2OchJTk0l" }, "source": [ "# 텐서플로 1 코드를 텐서플로 2로 바꾸기\n", "\n", "\n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " TensorFlow.org에서 보기\n", " \n", " \n", " \n", " 구글 코랩(Colab)에서 실행하기\n", " \n", " \n", " \n", " 깃허브(GitHub) 소스 보기\n", " \n", " Download notebook\n", "
" ] }, { "cell_type": "markdown", "metadata": { "id": "Cp6jVAZ6dNtq" }, "source": [ "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n", "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n", "이 번역에 개선할 부분이 있다면\n", "[tensorflow/docs-l10n](https://github.com/tensorflow/docs-l10n/) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n", "문서 번역이나 리뷰에 참여하려면\n", "[docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로\n", "메일을 보내주시기 바랍니다." ] }, { "cell_type": "markdown", "metadata": { "id": "hH1z406GG9s3" }, "source": [ "이 문서는 저수준 텐서플로 API를 사용자를 위한 가이드입니다.\n", "만약 고수준 API(`tf.keras`)를 사용하고 있다면 텐서플로 2.0으로 바꾸기 위해 할 일이 거의 없습니다:\n", " \n", "* [옵티마이저 학습률 기본값](#keras_optimizer_lr)을 확인해 보세요.\n", "* 측정 지표의 \"이름\"이 [바뀌었을 수 있습니다](#keras_metric_names)." ] }, { "cell_type": "markdown", "metadata": { "id": "de5f38UrLJCu" }, "source": [ "여전히 텐서플로 1.X 버전의 코드를 수정하지 않고 텐서플로 2.0에서 실행할 수 있습니다([`contrib` 모듈은 제외](https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md)):\n", "\n", "```\n", "import tensorflow.compat.v1 as tf\n", "tf.disable_v2_behavior()\n", "```\n", "\n", "하지만 이렇게 하면 텐서플로 2.0에서 제공하는 많은 장점을 활용할 수 없습니다. 이 문서는 성능을 높이면서 코드는 더 간단하고 유지보수하기 쉽도록 업그레이드하는 방법을 안내합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "C0V10enS1_WU" }, "source": [ "## 자동 변환 스크립트\n", "\n", "첫 번째 단계는 [업그레이드 스크립트](./upgrade.md)를 사용해 보는 것입니다.\n", "\n", "이는 텐서플로 2.0으로 업그레이드하기 위해 처음 시도할 일입니다. 하지만 이 작업이 기존 코드를 텐서플로 2.0 스타일로 바꾸어 주지는 못합니다. 여전히 플레이스홀더(placeholder)나 세션(session), 컬렉션(collection), 그외 1.x 스타일의 기능을 사용하기 위해 `tf.compat.v1` 아래의 모듈을 참조하고 있을 것입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "HDu2N_Dsx_9l" }, "source": [ "## 고수준 동작 변경\n", "\n", "`tf.compat.v1.disable_v2_behavior()`를 사용해 텐서플로 2.0에서 코드를 실행한다면 전역 범위의 변경 사항에 대해 알고 있어야 합니다. 주요 변경 사항은 다음과 같습니다:" ] }, { "cell_type": "markdown", "metadata": { "id": "UL1AV0t0OBJf" }, "source": [ "* *즉시 실행, `v1.enable_eager_execution()`* : 암묵적으로 `tf.Graph`를 사용하는 모든 코드는 실패할 것입니다. 코드를 `with tf.Graph().as_default()` 컨택스트(context)로 감싸야 합니다.\n", " \n", "* *리소스(resource) 변수, `v1.enable_resource_variables()`*: 일부 코드는 TF 레퍼런스 변수의 결정적이지 않은 행동에 영향을 받을 수 있습니다.\n", "리소스 변수는 저장되는 동안 잠깁니다. 따라서 이해하기 쉬운 일관성을 보장합니다.\n", "\n", " * 극단적인 경우 동작을 바꿀 수 있습니다.\n", " * 추가로 복사본을 만들고 메모리 사용량을 높일 수 있습니다.\n", " * `tf.Variable` 생성자에 `use_resource=False`를 전달하여 비활성화할 수 있습니다.\n", "\n", "* *텐서 크기, `v1.enable_v2_tensorshape()`*: TF 2.0에서 텐서 크기는 간단합니다. `t.shape[0].value` 대신에 `t.shape[0]`을 사용할 수 있습니다. 변경 사항이 작기 때문에 당장 고치는 것이 좋습니다. [TensorShape](#tensorshape) 예를 참고하세요.\n", "\n", "* *제어 흐름, `v1.enable_control_flow_v2()`*: TF 2.0 제어 흐름 구현이 간단하게 바뀌었기 때문에 다른 그래프 표현을 만듭니다. 이슈가 있다면 [버그를 신고해 주세요](https://github.com/tensorflow/tensorflow/issues)." ] }, { "cell_type": "markdown", "metadata": { "id": "gdIBrMup8J9-" }, "source": [ "## 2.0에 맞도록 코드 수정하기\n", "\n", "텐서플로 1.x 코드를 텐서플로 2.0으로 변환하는 몇 가지 예를 소개하겠습니다. 이 작업을 통해 성능을 최적화하고 간소화된 API의 이점을 사용할 수 있습니다.\n", "\n", "각각의 경우에 수정하는 패턴은 다음과 같습니다:" ] }, { "cell_type": "markdown", "metadata": { "id": "uP0O8Pc45LNs" }, "source": [ "### 1. `tf.Session.run` 호출을 바꾸세요.\n", "\n", "모든 `tf.Session.run` 호출을 파이썬 함수로 바꾸어야 합니다.\n", "\n", "* `feed_dict`와 `tf.placeholder`는 함수의 매개변수가 됩니다.\n", "* `fetches`는 함수의 반환값이 됩니다.\n", "* 변환 과정에서 즉시 실행 모드 덕분에 표준 파이썬 디버거 `pdb`를 사용하여 쉽게 디버깅할 수 있습니다.\n", "\n", "그다음 그래프 모드에서 효율적으로 실행할 수 있도록 `tf.function` 데코레이터를 추가합니다. 더 자세한 내용은 [오토그래프 가이드](function.ipynb)를 참고하세요.\n", "\n", "노트:\n", "\n", "* `v1.Session.run`과 달리 `tf.function`은 반환 시그니처(signature)가 고정되어 있고 항상 모든 출력을 반환합니다. 성능에 문제가 된다면 두 개의 함수로 나누세요.\n", "\n", "* `tf.control_dependencies`나 비슷한 연산이 필요없습니다: `tf.function`은 쓰여진 순서대로 실행됩니다. 예를 들어 `tf.Variable` 할당이나 `tf.assert`는 자동으로 실행됩니다." ] }, { "cell_type": "markdown", "metadata": { "id": "jlBOqROL5NmN" }, "source": [ "### 2. 파이썬 객체를 사용하여 변수와 손실을 관리하세요.\n", "\n", "TF 2.0에서 이름 기반 변수 추적은 매우 권장되지 않습니다. 파이썬 객체로 변수를 추적하세요.\n", "\n", "`v1.get_variable` 대신에 `tf.Variable`을 사용하세요.\n", "\n", "모든 `v1.variable_scope`는 파이썬 객체로 바꾸어야 합니다. 일반적으로 다음 중 하나가 될 것입니다:\n", "\n", "* `tf.keras.layers.Layer`\n", "* `tf.keras.Model`\n", "* `tf.Module`\n", "\n", "만약 (`tf.Graph.get_collection(tf.GraphKeys.VARIABLES)`처럼) 변수의 리스트가 필요하다면 `Layer`와 `Model` 객체의 `.variables`이나 `.trainable_variables` 속성을 사용하세요.\n", "\n", "`Layer`와 `Model` 클래스는 전역 컬렉션이 필요하지 않도록 몇 가지 다른 속성들도 제공합니다. `.losses` 속성은 `tf.GraphKeys.LOSSES` 컬렉션 대신 사용할 수 있습니다.\n", "\n", "자세한 내용은 [케라스 가이드](keras.ipynb)를 참고하세요.\n", "\n", "경고: `tf.compat.v1`의 상당수 기능은 암묵적으로 전역 컬렉션을 사용합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "rGFhBzoF5FIq" }, "source": [ "### 3. 훈련 루프를 업그레이드하세요.\n", "\n", "풀려는 문제에 맞는 고수준 API를 사용하세요. 훈련 루프(loop)를 직접 만드는 것보다 `tf.keras.Model.fit` 메서드를 사용하는 것이 좋습니다.\n", "\n", "고수준 함수는 훈련 루프를 직접 만들 때 놓치기 쉬운 여러 가지 저수준의 세부 사항들을 관리해 줍니다. 예를 들어 자동으로 규제(regularization) 손실을 수집하거나 모델을 호출할 때 `training=True`로 매개변수를 설정해 줍니다.\n", "\n", "### 4. 데이터 입력 파이프라인을 업그레이드하세요.\n", "\n", "데이터 입력을 위해 `tf.data` 데이터셋을 사용하세요. 이 객체는 효율적이고 간결하며 텐서플로와 잘 통합됩니다.\n", "\n", "`tf.keras.Model.fit` 메서드에 바로 전달할 수 있습니다.\n", "\n", "```\n", "model.fit(dataset, epochs=5)\n", "```\n", "\n", "파이썬에서 직접 반복시킬 수 있습니다:\n", "\n", "```\n", "for example_batch, label_batch in dataset:\n", " break\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "YONp57_92Wod" }, "source": [ "#### 5. `compat.v1`에서 마이그레이션 하기\n", "\n", "\n", "`tf.compat.v1` 모듈에는 완전한 텐서플로 1.x API가 들어 있습니다.\n", "\n", "\n", "[TF2 업그레이드 스크립트](upgrade.ipynb)는 안전할 경우 이와 동일한 2.0 버전으로 바꿉니다. 즉 2.0 버전의 동작이 완전히 동일한 경우입니다(예를 들면, `v1.arg_max`가 `tf.argmax`로 이름이 바뀌었기 때문에 동일한 함수입니다).\n", "\n", "업그레이드 스크립트가 코드를 수정하고 나면 코드에 `compat.v1`이 많이 등장할 것입니다. 코드를 살펴 보면서 수동으로 2.0 버전으로 바꿉니다(2.0 버전이 있다면 로그에 언급되어 있을 것입니다)." ] }, { "cell_type": "markdown", "metadata": { "id": "X_ilfTGJ4Yml" }, "source": [ "## 모델 변환하기\n", "\n", "### 준비" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:09:46.595230Z", "iopub.status.busy": "2021-01-15T02:09:46.594580Z", "iopub.status.idle": "2021-01-15T02:09:54.090150Z", "shell.execute_reply": "2021-01-15T02:09:54.089453Z" }, "id": "bad2N-Z115W1" }, "outputs": [], "source": [ "import tensorflow as tf\n", "\n", "\n", "import tensorflow_datasets as tfds" ] }, { "cell_type": "markdown", "metadata": { "id": "FB99sqHX2Q5m" }, "source": [ "### 저수준 변수와 연산 실행\n", "\n", "저수준 API를 사용하는 예는 다음과 같습니다:\n", "\n", "* 재사용을 위해 변수 범위(variable scopes)를 사용하기\n", "* `v1.get_variable`로 변수를 만들기\n", "* 명시적으로 컬렉션을 참조하기\n", "* 다음과 같은 메서드를 사용하여 암묵적으로 컬렉션을 참조하기:\n", "\n", " * `v1.global_variables`\n", " * `v1.losses.get_regularization_loss`\n", "\n", "* 그래프 입력을 위해 `v1.placeholder`를 사용하기\n", "* `session.run`으로 그래프를 실행하기\n", "* 변수를 수동으로 초기화하기" ] }, { "cell_type": "markdown", "metadata": { "id": "e582IjyF2eje" }, "source": [ "#### 변환 전\n", "\n", "다음 코드는 텐서플로 1.x를 사용한 코드에서 볼 수 있는 패턴입니다.\n", "\n", "```python\n", "in_a = tf.placeholder(dtype=tf.float32, shape=(2))\n", "in_b = tf.placeholder(dtype=tf.float32, shape=(2))\n", "\n", "def forward(x):\n", " with tf.variable_scope(\"matmul\", reuse=tf.AUTO_REUSE):\n", " W = tf.get_variable(\"W\", initializer=tf.ones(shape=(2,2)),\n", " regularizer=tf.contrib.layers.l2_regularizer(0.04))\n", " b = tf.get_variable(\"b\", initializer=tf.zeros(shape=(2)))\n", " return W * x + b\n", "\n", "out_a = forward(in_a)\n", "out_b = forward(in_b)\n", "\n", "reg_loss = tf.losses.get_regularization_loss(scope=\"matmul\")\n", "\n", "with tf.Session() as sess:\n", " sess.run(tf.global_variables_initializer())\n", " outs = sess.run([out_a, out_b, reg_loss],\n", " \t feed_dict={in_a: [1, 0], in_b: [0, 1]})\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "QARwz4Xd2lc2" }, "source": [ "#### 변환 후" ] }, { "cell_type": "markdown", "metadata": { "id": "x0AVzBFRBPcU" }, "source": [ "변환된 코드의 패턴은 다음과 같습니다:\n", "\n", "* 변수는 파이썬 지역 객체입니다.\n", "* `forward` 함수는 여전히 필요한 계산을 정의합니다.\n", "* `Session.run` 호출은 `forward` 함수를 호출하는 것으로 바뀝니다.\n", "* `tf.function` 데코레이터는 선택 사항으로 성능을 위해 추가할 수 있습니다.\n", "* 어떤 전역 컬렉션도 참조하지 않고 규제를 직접 계산합니다.\n", "* **세션이나 플레이스홀더를 사용하지 않습니다.**" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:09:55.528594Z", "iopub.status.busy": "2021-01-15T02:09:55.527750Z", "iopub.status.idle": "2021-01-15T02:09:55.890815Z", "shell.execute_reply": "2021-01-15T02:09:55.891240Z" }, "id": "lXEZoLMP2cWJ" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[1. 0.]\n", " [1. 0.]], shape=(2, 2), dtype=float32)\n" ] } ], "source": [ "W = tf.Variable(tf.ones(shape=(2,2)), name=\"W\")\n", "b = tf.Variable(tf.zeros(shape=(2)), name=\"b\")\n", "\n", "@tf.function\n", "def forward(x):\n", " return W * x + b\n", "\n", "out_a = forward([1,0])\n", "print(out_a)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:09:55.898862Z", "iopub.status.busy": "2021-01-15T02:09:55.897654Z", "iopub.status.idle": "2021-01-15T02:09:55.909292Z", "shell.execute_reply": "2021-01-15T02:09:55.908673Z" }, "id": "YmE96A_1jZTg" }, "outputs": [], "source": [ "out_b = forward([0,1])\n", "\n", "regularizer = tf.keras.regularizers.l2(0.04)\n", "reg_loss = regularizer(W)" ] }, { "cell_type": "markdown", "metadata": { "id": "ycDxY9nL268-" }, "source": [ "### `tf.layers` 기반의 모델" ] }, { "cell_type": "markdown", "metadata": { "id": "K-bIk7wL48U7" }, "source": [ "`v1.layers` 모듈은 변수를 정의하고 재사용하기 위해 `v1.variable_scope`에 의존하는 층 함수를 포함합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "8I_qKpT73KyM" }, "source": [ "#### 변환 전\n", "```python\n", "def model(x, training, scope='model'):\n", " with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):\n", " x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l2_regularizer(0.04))\n", " x = tf.layers.max_pooling2d(x, (2, 2), 1)\n", " x = tf.layers.flatten(x)\n", " x = tf.layers.dropout(x, 0.1, training=training)\n", " x = tf.layers.dense(x, 64, activation=tf.nn.relu)\n", " x = tf.layers.batch_normalization(x, training=training)\n", " x = tf.layers.dense(x, 10, activation=tf.nn.softmax)\n", " return x\n", "\n", "train_out = model(train_data, training=True)\n", "test_out = model(test_data, training=False)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "b8_Ii7CQ3fK-" }, "source": [ "#### 변환 후" ] }, { "cell_type": "markdown", "metadata": { "id": "BsAseSMfB9XN" }, "source": [ "* 층을 단순하게 쌓을 경우엔 `tf.keras.Sequential`이 적합합니다. (복잡한 모델인 경우 [사용자 정의 층과 모델](keras/custom_layers_and_models.ipynb)이나 [함수형 API](keras/functional.ipynb)를 참고하세요.)\n", "* 모델이 변수와 규제 손실을 관리합니다.\n", "* `v1.layers`에서 `tf.keras.layers`로 바로 매핑되기 때문에 일대일로 변환됩니다.\n", "\n", "대부분 매개변수는 동일합니다. 다른 부분은 다음과 같습니다:\n", "\n", "* 모델이 실행될 때 각 층에 `training` 매개변수가 전달됩니다.\n", "* 원래 `model` 함수의 첫 번째 매개변수(입력 `x`)는 사라집니다. 층 객체가 모델 구축과 모델 호출을 구분하기 때문입니다.\n", "\n", "추가 노트:\n", "\n", "* `tf.contrib`에서 규제를 초기화했다면 다른 것보다 매개변수 변화가 많습니다.\n", "* 더 이상 컬렉션을 사용하지 않기 때문에 `v1.losses.get_regularization_loss`와 같은 함수는 값을 반환하지 않습니다. 이는 훈련 루프를 망가뜨릴 수 있습니다." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:09:55.936418Z", "iopub.status.busy": "2021-01-15T02:09:55.915609Z", "iopub.status.idle": "2021-01-15T02:09:55.990422Z", "shell.execute_reply": "2021-01-15T02:09:55.989676Z" }, "id": "DLAPORrN3lct" }, "outputs": [], "source": [ "model = tf.keras.Sequential([\n", " tf.keras.layers.Conv2D(32, 3, activation='relu',\n", " kernel_regularizer=tf.keras.regularizers.l2(0.04),\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", "train_data = tf.ones(shape=(1, 28, 28, 1))\n", "test_data = tf.ones(shape=(1, 28, 28, 1))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:09:55.997424Z", "iopub.status.busy": "2021-01-15T02:09:55.996642Z", "iopub.status.idle": "2021-01-15T02:10:07.041127Z", "shell.execute_reply": "2021-01-15T02:10:07.041539Z" }, "id": "6nWh6IXvkMKv" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]], shape=(1, 10), dtype=float32)\n" ] } ], "source": [ "train_out = model(train_data, training=True)\n", "print(train_out)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.046687Z", "iopub.status.busy": "2021-01-15T02:10:07.045974Z", "iopub.status.idle": "2021-01-15T02:10:07.051978Z", "shell.execute_reply": "2021-01-15T02:10:07.051418Z" }, "id": "YnAdIDLlj3go" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[ 0.19888344 -0.06454547 -0.3885131 -0.02075228 0.03223642 0.3088064\n", " 0.14848296 -0.16252786 -0.18151288 -0.6824807 ]], shape=(1, 10), dtype=float32)\n" ] } ], "source": [ "test_out = model(test_data, training=False)\n", "print(test_out)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.057651Z", "iopub.status.busy": "2021-01-15T02:10:07.056948Z", "iopub.status.idle": "2021-01-15T02:10:07.060363Z", "shell.execute_reply": "2021-01-15T02:10:07.060815Z" }, "id": "sAgqwCJBMx_x" }, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 훈련되는 전체 변수\n", "len(model.trainable_variables)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.064888Z", "iopub.status.busy": "2021-01-15T02:10:07.064160Z", "iopub.status.idle": "2021-01-15T02:10:07.068425Z", "shell.execute_reply": "2021-01-15T02:10:07.067938Z" }, "id": "uX6knaYMNM8p" }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 규제 손실\n", "model.losses" ] }, { "cell_type": "markdown", "metadata": { "id": "9moqw5E_4Cwl" }, "source": [ "### 변수와 v1.layers의 혼용" ] }, { "cell_type": "markdown", "metadata": { "id": "80DEsImmq6VX" }, "source": [ "기존 코드는 종종 저수준 TF 1.x 변수와 고수준 `v1.layers` 연산을 혼용합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "oZe9L6RR4OcP" }, "source": [ "#### 변경 전\n", "```python\n", "def model(x, training, scope='model'):\n", " with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):\n", " W = tf.get_variable(\n", " \"W\", dtype=tf.float32,\n", " initializer=tf.ones(shape=x.shape),\n", " regularizer=tf.contrib.layers.l2_regularizer(0.04),\n", " trainable=True)\n", " if training:\n", " x = x + W\n", " else:\n", " x = x + W * 0.5\n", " x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu)\n", " x = tf.layers.max_pooling2d(x, (2, 2), 1)\n", " x = tf.layers.flatten(x)\n", " return x\n", "\n", "train_out = model(train_data, training=True)\n", "test_out = model(test_data, training=False)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "y6ORX7cD4TkD" }, "source": [ "#### 변경 후" ] }, { "cell_type": "markdown", "metadata": { "id": "2BaRwog5CBpz" }, "source": [ "이런 코드를 변환하려면 이전 예제처럼 층별로 매핑하는 패턴을 사용하세요.\n", "\n", "`v1.variable_scope`는 기본적으로 하나의 층입니다. 따라서 `tf.keras.layers.Layer`로 재작성합니다. 자세한 내용은 이 [문서](keras/custom_layers_and_models.ipynb)를 참고하세요.\n", "\n", "일반적인 패턴은 다음과 같습니다:\n", "\n", "* `__init__`에서 층에 필요한 매개변수를 입력 받습니다.\n", "* `build` 메서드에서 변수를 만듭니다.\n", "* `call` 메서드에서 연산을 실행하고 결과를 반환합니다." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.076056Z", "iopub.status.busy": "2021-01-15T02:10:07.075420Z", "iopub.status.idle": "2021-01-15T02:10:07.077750Z", "shell.execute_reply": "2021-01-15T02:10:07.077315Z" }, "id": "YcCAjNuP4NVh" }, "outputs": [], "source": [ "# 모델에 추가하기 위해 사용자 정의 층을 만듭니다.\n", "class CustomLayer(tf.keras.layers.Layer):\n", " def __init__(self, *args, **kwargs):\n", " super(CustomLayer, self).__init__(*args, **kwargs)\n", "\n", " def build(self, input_shape):\n", " self.w = self.add_weight(\n", " shape=input_shape[1:],\n", " dtype=tf.float32,\n", " initializer=tf.keras.initializers.ones(),\n", " regularizer=tf.keras.regularizers.l2(0.04),\n", " trainable=True)\n", "\n", " # call 메서드가 그래프 모드에서 사용되면\n", " # training 변수는 텐서가 됩니다.\n", " @tf.function\n", " def call(self, inputs, training=None):\n", " if training:\n", " return inputs + self.w\n", " else:\n", " return inputs + self.w * 0.5" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.083368Z", "iopub.status.busy": "2021-01-15T02:10:07.082453Z", "iopub.status.idle": "2021-01-15T02:10:07.158889Z", "shell.execute_reply": "2021-01-15T02:10:07.158185Z" }, "id": "dR_QO6_wBgMm" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.5]\n", "[2.]\n" ] } ], "source": [ "custom_layer = CustomLayer()\n", "print(custom_layer([1]).numpy())\n", "print(custom_layer([1], training=True).numpy())" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.169774Z", "iopub.status.busy": "2021-01-15T02:10:07.164412Z", "iopub.status.idle": "2021-01-15T02:10:07.216914Z", "shell.execute_reply": "2021-01-15T02:10:07.216282Z" }, "id": "VzqaIf4E42oY" }, "outputs": [], "source": [ "train_data = tf.ones(shape=(1, 28, 28, 1))\n", "test_data = tf.ones(shape=(1, 28, 28, 1))\n", "\n", "# 사용자 정의 층을 포함한 모델을 만듭니다.\n", "model = tf.keras.Sequential([\n", " CustomLayer(input_shape=(28, 28, 1)),\n", " tf.keras.layers.Conv2D(32, 3, activation='relu'),\n", " tf.keras.layers.MaxPooling2D(),\n", " tf.keras.layers.Flatten(),\n", "])\n", "\n", "train_out = model(train_data, training=True)\n", "test_out = model(test_data, training=False)" ] }, { "cell_type": "markdown", "metadata": { "id": "dS5ed_jjOkvh" }, "source": [ "노트:\n", "\n", "* 클래스 상속으로 만든 케라스 모델과 층은 v1 그래프(연산간의 의존성이 자동으로 제어되지 않습니다)와 즉시 실행 모드 양쪽에서 실행될 수 있어야 합니다.\n", " * 오토그래프(autograph)와 의존성 자동 제어(automatic control dependency)를 위해 `tf.function()`으로 `call()` 메서드를 감쌉니다.\n", "\n", "* `call` 메서드에 `training` 매개변수를 추가하는 것을 잊지 마세요.\n", " * 경우에 따라 이 값은 `tf.Tensor`가 됩니다.\n", " * 경우에 따라 이 값은 파이썬 불리언(boolean)이 됩니다.\n", "\n", "* `self.add_weight()`를 사용하여 생성자 메서드나 `def build()` 메서드에서 모델 변수를 만듭니다.\n", " * `build` 메서드에서 입력 크기를 참조할 수 있으므로 적절한 크기의 가중치를 만들 수 있습니다.\n", " * `tf.keras.layers.Layer.add_weight`를 사용하면 케라스가 변수와 규제 손실을 관리할 수 있습니다.\n", "\n", "* 사용자 정의 층 안에 `tf.Tensors` 객체를 포함하지 마세요.\n", " * `tf.function`이나 즉시 실행 모드에서 모두 텐서가 만들어지지만 이 텐서들의 동작 방식은 다릅니다.\n", " * 상태를 저장하기 위해서는 `tf.Variable`을 사용하세요. 변수는 양쪽 방식에 모두 사용할 수 있습니다.\n", " * `tf.Tensors`는 중간 값을 저장하기 위한 용도로만 사용합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "ulaB1ymO4pw5" }, "source": [ "### Slim & contrib.layers를 위한 노트\n", "\n", "예전 텐서플로 1.x 코드는 [Slim](https://ai.googleblog.com/2016/08/tf-slim-high-level-library-to-define.html) 라이브러리를 많이 사용합니다. 이 라이브러리는 텐서플로 1.x의 `tf.contrib.layers`로 패키지되어 있습니다. `contrib` 모듈은 더 이상 텐서플로 2.0에서 지원하지 않고 `tf.compat.v1`에도 포함되지 않습니다. Slim을 사용한 코드를 TF 2.0으로 변환하는 것은 `v1.layers`를 사용한 코드를 변경하는 것보다 더 어렵습니다. 사실 Slim 코드는 `v1.layers`로 먼저 변환하고 그 다음 케라스로 변환하는 것이 좋습니다.\n", "\n", "* `arg_scopes`를 삭제하세요. 모든 매개변수는 명시적으로 설정되어야 합니다.\n", "* `normalizer_fn`과 `activation_fn`를 사용해야 한다면 분리하여 각각 하나의 층으로 만드세요.\n", "* 분리 합성곱(separable conv) 층은 한 개 이상의 다른 케라스 층으로 매핑합니다(깊이별(depthwise), 점별(pointwise), 분리(separable) 케라스 층).\n", "* Slim과 `v1.layers`는 매개변수 이름과 기본값이 다릅니다.\n", "* 일부 매개변수는 다른 스케일(scale)을 가집니다.\n", "* 사전 훈련된 Slim 모델을 사용한다면 `tf.keras.applications`나 [TFHub](https://tensorflow.orb/hub)를 확인해 보세요.\n", "\n", "일부 `tf.contrib` 층은 텐서플로 내부에 포함되지 못했지만 [TF 애드온(add-on) 패키지](https://github.com/tensorflow/addons)로 옮겨졌습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "1w72KrXm4yZR" }, "source": [ "## 훈련" ] }, { "cell_type": "markdown", "metadata": { "id": "56PQxTgy2bpI" }, "source": [ "여러 가지 방법으로 `tf.keras` 모델에 데이터를 주입할 수 있습니다. 파이썬 제너레이터(generator)와 넘파이 배열을 입력으로 사용할 수 있습니다.\n", "\n", "`tf.data` 패키지를 사용하여 모델에 데이터를 주입하는 것이 권장되는 방법입니다. 이 패키지는 데이터 조작을 위한 고성능 클래스들을 포함하고 있습니다.\n", "\n", "`tf.queue`는 데이터 구조로만 지원되고 입력 파이프라인으로는 지원되지 않습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "m6htasZ7iBB4" }, "source": [ "### 데이터셋 사용하기" ] }, { "cell_type": "markdown", "metadata": { "id": "loTPH2Pz4_Oj" }, "source": [ "[텐서플로 데이터셋(Datasets)](https://tensorflow.org/datasets) 패키지(`tfds`)는 `tf.data.Dataset` 객체로 정의된 데이터셋을 적재하기 위한 유틸리티가 포함되어 있습니다.\n", "\n", "예를 들어 `tfds`를 사용하여 MNIST 데이터셋을 적재하는 코드는 다음과 같습니다:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:07.223643Z", "iopub.status.busy": "2021-01-15T02:10:07.222957Z", "iopub.status.idle": "2021-01-15T02:10:09.946508Z", "shell.execute_reply": "2021-01-15T02:10:09.945924Z" }, "id": "BMgxaLH74_s-" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1mDownloading and preparing dataset 11.06 MiB (download: 11.06 MiB, generated: 21.00 MiB, total: 32.06 MiB) to /home/kbuilder/tensorflow_datasets/mnist/3.0.1...\u001b[0m\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1mDataset mnist downloaded and prepared to /home/kbuilder/tensorflow_datasets/mnist/3.0.1. Subsequent calls will reuse this data.\u001b[0m\n" ] } ], "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", " * 이미지와 레이블(label)의 배치를 만듭니다." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:09.951733Z", "iopub.status.busy": "2021-01-15T02:10:09.951064Z", "iopub.status.idle": "2021-01-15T02:10:09.953628Z", "shell.execute_reply": "2021-01-15T02:10:09.953135Z" }, "id": "StBRHtJM2S7o" }, "outputs": [], "source": [ "BUFFER_SIZE = 10 # 실전 코드에서는 더 큰 값을 사용합니다.\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": 15, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:09.959579Z", "iopub.status.busy": "2021-01-15T02:10:09.958469Z", "iopub.status.idle": "2021-01-15T02:10:10.137571Z", "shell.execute_reply": "2021-01-15T02:10:10.138048Z" }, "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": 16, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:10.144671Z", "iopub.status.busy": "2021-01-15T02:10:10.143618Z", "iopub.status.idle": "2021-01-15T02:10:10.189611Z", "shell.execute_reply": "2021-01-15T02:10:10.188983Z" }, "id": "XEqdkH54VM6c" }, "outputs": [], "source": [ "image_batch, label_batch = next(iter(train_data))" ] }, { "cell_type": "markdown", "metadata": { "id": "mSev7vZC5GJB" }, "source": [ "### 케라스 훈련 루프 사용하기\n", "\n", "훈련 과정을 세부적으로 제어할 필요가 없다면 케라스의 내장 메서드인 `fit`, `evaluate`, `predict`를 사용하는 것이 좋습니다. 이 메서드들은 모델 구현(Sequential, 함수형 API, 클래스 상속)에 상관없이 일관된 훈련 인터페이스를 제공합니다.\n", "\n", "이 메서드들의 장점은 다음과 같습니다:\n", "\n", "* 넘파이 배열, 파이썬 제너레이터, `tf.data.Datasets`을 사용할 수 있습니다.\n", "* 자동으로 규제와 활성화 손실을 적용합니다.\n", "* [다중 장치 훈련](distributed_training.ipynb)을 위해 `tf.distribute`을 지원합니다.\n", "* 임의의 호출 가능한 객체를 손실과 측정 지표로 사용할 수 있습니다.\n", "* `tf.keras.callbacks.TensorBoard`와 같은 콜백(callback)이나 사용자 정의 콜백을 지원합니다.\n", "* 자동으로 텐서플로 그래프를 사용하므로 성능이 뛰어납니다.\n", "\n", "`Dataset`을 사용하여 모델을 훈련하는 예제는 다음과 같습니다. (자세한 작동 방식은 [튜토리얼](../tutorials)을 참고하세요.)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:10.204993Z", "iopub.status.busy": "2021-01-15T02:10:10.204314Z", "iopub.status.idle": "2021-01-15T02:10:11.480849Z", "shell.execute_reply": "2021-01-15T02:10:11.480315Z" }, "id": "uzHFCzd45Rae" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 2s - loss: 2.7206 - accuracy: 0.1719" ] }, { "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 [==============================] - 1s 8ms/step - loss: 2.0669 - accuracy: 0.3524\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 2/5\n", "\r", "1/5 [=====>........................] - ETA: 0s - loss: 0.7250 - accuracy: 0.8594" ] }, { "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 8ms/step - loss: 0.6220 - accuracy: 0.8700\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 3/5\n", "\r", "1/5 [=====>........................] - ETA: 0s - loss: 0.4196 - accuracy: 0.9062" ] }, { "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 9ms/step - loss: 0.3807 - accuracy: 0.9282\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 4/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 0s - loss: 0.2907 - accuracy: 0.9688" ] }, { "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 8ms/step - loss: 0.2757 - accuracy: 0.9713\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 5/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 0s - loss: 0.2005 - 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 8ms/step - loss: 0.2083 - accuracy: 0.9855\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 0s - loss: 1.6133 - 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 5ms/step - loss: 1.6415 - accuracy: 0.6750\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "손실 1.6414827108383179, 정확도 0.675000011920929\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", "# 사용자 정의 층이 없는 모델입니다.\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(\"손실 {}, 정확도 {}\".format(loss, acc))" ] }, { "cell_type": "markdown", "metadata": { "id": "akpeOb09YBhq" }, "source": [ "### 사용자 정의 훈련 루프 만들기\n", "\n", "케라스 모델의 훈련 스텝(step)이 좋지만 그 외 다른 것을 더 제어하려면 자신만의 데이터 반복 루프를 만들고 `tf.keras.model.train_on_batch` 메서드를 사용해 보세요.\n", "\n", "기억할 점: 많은 것을 `tf.keras.Callback`으로 구현할 수 있습니다.\n", "\n", "이 메서드는 앞에서 언급한 메서드의 장점을 많이 가지고 있고 사용자가 바깥쪽 루프를 제어할 수 있습니다.\n", "\n", "훈련하는 동안 성능을 확인하기 위해 `tf.keras.model.test_on_batch`나 `tf.keras.Model.evaluate` 메서드를 사용할 수도 있습니다.\n", "\n", "노트: `train_on_batch`와 `test_on_batch`는 기본적으로 하나의 배치에 대한 손실과 측정값을 반환합니다. `reset_metrics=False`를 전달하면 누적된 측정값을 반환합니다. 이 때는 누적된 측정값을 적절하게 초기화해 주어야 합니다. `AUC`와 같은 일부 지표는 `reset_metrics=False`를 설정해야 올바르게 계산됩니다.\n", "\n", "앞의 모델을 계속 사용합니다:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:11.489410Z", "iopub.status.busy": "2021-01-15T02:10:11.488709Z", "iopub.status.idle": "2021-01-15T02:10:13.024257Z", "shell.execute_reply": "2021-01-15T02:10:13.023715Z" }, "id": "eXr4CyJMtJJ6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "훈련: loss: 0.171 accuracy: 1.000\n", "훈련: loss: 0.193 accuracy: 0.969\n", "훈련: loss: 0.214 accuracy: 0.984\n", "훈련: loss: 0.251 accuracy: 0.984\n", "훈련: loss: 0.230 accuracy: 0.984\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "평가: loss: 1.616 accuracy: 0.759\n", "훈련: loss: 0.099 accuracy: 1.000\n", "훈련: loss: 0.112 accuracy: 0.984\n", "훈련: loss: 0.125 accuracy: 0.984\n", "훈련: loss: 0.144 accuracy: 0.984\n", "훈련: loss: 0.125 accuracy: 1.000\n", "\n", "평가: loss: 1.586 accuracy: 0.772\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "훈련: loss: 0.091 accuracy: 1.000\n", "훈련: loss: 0.089 accuracy: 1.000\n", "훈련: loss: 0.083 accuracy: 1.000\n", "훈련: loss: 0.093 accuracy: 1.000\n", "훈련: loss: 0.073 accuracy: 1.000\n", "\n", "평가: loss: 1.560 accuracy: 0.759\n", "훈련: loss: 0.068 accuracy: 1.000\n", "훈련: loss: 0.069 accuracy: 1.000\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "훈련: loss: 0.063 accuracy: 1.000\n", "훈련: loss: 0.072 accuracy: 1.000\n", "훈련: loss: 0.064 accuracy: 1.000\n", "\n", "평가: loss: 1.542 accuracy: 0.797\n", "훈련: loss: 0.059 accuracy: 1.000\n", "훈련: loss: 0.057 accuracy: 1.000\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "훈련: loss: 0.057 accuracy: 1.000\n", "훈련: loss: 0.057 accuracy: 1.000\n", "훈련: loss: 0.097 accuracy: 0.984\n", "\n", "평가: loss: 1.516 accuracy: 0.822\n" ] } ], "source": [ "# 사용자 정의 층이 없는 모델입니다.\n", "model.compile(optimizer='adam',\n", " loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n", " metrics=['accuracy'])\n", "\n", "for epoch in range(NUM_EPOCHS):\n", " # 누적된 측정값을 초기화합니다.\n", " model.reset_metrics()\n", "\n", " for image_batch, label_batch in train_data:\n", " result = model.train_on_batch(image_batch, label_batch)\n", " metrics_names = model.metrics_names\n", " print(\"훈련: \",\n", " \"{}: {:.3f}\".format(metrics_names[0], result[0]),\n", " \"{}: {:.3f}\".format(metrics_names[1], result[1]))\n", " for image_batch, label_batch in test_data:\n", " result = model.test_on_batch(image_batch, label_batch,\n", " # return accumulated metrics\n", " reset_metrics=False)\n", " metrics_names = model.metrics_names\n", " print(\"\\n평가: \",\n", " \"{}: {:.3f}\".format(metrics_names[0], result[0]),\n", " \"{}: {:.3f}\".format(metrics_names[1], result[1]))" ] }, { "cell_type": "markdown", "metadata": { "id": "LQTaHTuK5S5A" }, "source": [ "\n", "### 훈련 단계 커스터마이징\n", "\n", "자유도를 높이고 제어를 더 하려면 다음 세 단계를 사용해 자신만의 훈련 루프를 구현할 수 있습니다:\n", "\n", "1. 샘플 배치를 만드는 파이썬 제너레이터나 `tf.data.Dataset`을 반복합니다.\n", "2. `tf.GradientTape`을 사용하여 그래디언트를 계산합니다.\n", "3. `tf.keras.optimizer`를 사용하여 모델의 가중치 변수를 업데이트합니다.\n", "\n", "기억할 점:\n", "\n", "* 클래스 상속으로 만든 층과 모델의 `call` 메서드에는 항상 `training` 매개변수를 포함하세요.\n", "* 모델을 호출할 때 `training` 매개변수를 올바르게 지정했는지 확인하세요.\n", "* 사용 방식에 따라 배치 데이터에서 모델이 실행될 때까지 모델 변수가 생성되지 않을 수 있습니다.\n", "* 모델의 규제 손실 같은 것들을 직접 관리해야 합니다.\n", "\n", "v1에 비해 단순해진 것:\n", "\n", "* 따로 변수를 초기화할 필요가 없습니다. 변수는 생성될 때 초기화됩니다.\n", "* 의존성을 수동으로 제어할 필요가 없습니다. `tf.function` 안에서도 연산은 즉시 실행 모드처럼 실행됩니다." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:13.041043Z", "iopub.status.busy": "2021-01-15T02:10:13.040287Z", "iopub.status.idle": "2021-01-15T02:10:13.847306Z", "shell.execute_reply": "2021-01-15T02:10:13.846760Z" }, "id": "gQooejfYlQeF" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "마지막 에포크 0\n", "마지막 에포크 1\n", "마지막 에포크 2\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "마지막 에포크 3\n", "마지막 에포크 4\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(\"마지막 에포크\", epoch)" ] }, { "cell_type": "markdown", "metadata": { "id": "yeZn498m2miz" }, "source": [ "### 새로운 스타일의 측정 지표\n", "\n", "텐서플로 2.0에서 측정 지표와 손실은 객체입니다. 이 객체는 즉시 실행 모드와 `tf.function`에서 모두 사용할 수 있습니다.\n", "\n", "손실은 호출 가능한 객체입니다. 매개변수로 (y_true, y_pred)를 기대합니다:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:13.852665Z", "iopub.status.busy": "2021-01-15T02:10:13.851989Z", "iopub.status.idle": "2021-01-15T02:10:13.859101Z", "shell.execute_reply": "2021-01-15T02:10:13.859518Z" }, "id": "luVB4eSetT8Q" }, "outputs": [ { "data": { "text/plain": [ "4.01815" ] }, "execution_count": 1, "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": "kS7WW5Z75ve3" }, "source": [ "측정 객체는 다음과 같은 메서드를 가집니다:\n", "\n", "* `update_state()` — 새로운 측정값을 추가합니다.\n", "* `result()` — 누적된 측정 결과를 얻습니다.\n", "* `reset_states()` — 모든 측정 내용을 지웁니다.\n", "\n", "이 객체는 호출 가능합니다. `update_state` 메서드처럼 새로운 측정값과 함께 호출하면 상태를 업데이트하고 새로운 측정 결과를 반환합니다.\n", "\n", "측정 변수를 수동으로 초기화할 필요가 없습니다. 텐서플로 2.0은 자동으로 의존성을 관리하기 때문에 어떤 경우에도 신경 쓸 필요가 없습니다.\n", "\n", "다음은 측정 객체를 사용하여 사용자 정의 훈련 루프 안에서 평균 손실을 관리하는 코드입니다." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:13.872234Z", "iopub.status.busy": "2021-01-15T02:10:13.871556Z", "iopub.status.idle": "2021-01-15T02:10:14.541317Z", "shell.execute_reply": "2021-01-15T02:10:14.540641Z" }, "id": "HAbA0fKW58CH" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "에포크: 0\n", " 손실: 0.174\n", " 정확도: 0.994\n", "에포크: 1\n", " 손실: 0.148\n", " 정확도: 1.000\n", "에포크: 2\n", " 손실: 0.124\n", " 정확도: 1.000\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "에포크: 3\n", " 손실: 0.109\n", " 정확도: 1.000\n", "에포크: 4\n", " 손실: 0.097\n", " 정확도: 1.000\n" ] } ], "source": [ "# 측정 객체를 만듭니다.\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", " # 측정값을 업데이트합니다.\n", " loss_metric.update_state(total_loss)\n", " accuracy_metric.update_state(labels, predictions)\n", "\n", "\n", "for epoch in range(NUM_EPOCHS):\n", " # 측정값을 초기화합니다.\n", " loss_metric.reset_states()\n", " accuracy_metric.reset_states()\n", "\n", " for inputs, labels in train_data:\n", " train_step(inputs, labels)\n", " # 측정 결과를 얻습니다.\n", " mean_loss = loss_metric.result()\n", " mean_accuracy = accuracy_metric.result()\n", "\n", " print('에포크: ', epoch)\n", " print(' 손실: {:.3f}'.format(mean_loss))\n", " print(' 정확도: {:.3f}'.format(mean_accuracy))" ] }, { "cell_type": "markdown", "metadata": { "id": "Ig9YNRAunaUW" }, "source": [ "\n", "\n", "### 케라스 지표 이름" ] }, { "cell_type": "markdown", "metadata": { "id": "4a_WFK0QaaNX" }, "source": [ "텐서플로 2.0에서 케라스 모델은 지표 이름을 더 일관성있게 처리합니다.\n", "\n", "지표를 문자열로 전달하면 _정확히_ 같은 문자열이 지표의 `name`으로 사용됩니다. `model.fit` 메서드가 반환하는 히스토리(history) 객체와 `keras.callbacks`로 전달하는 로그에 나타나는 이름이 지표로 전달한 문자열이 됩니다." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:14.553513Z", "iopub.status.busy": "2021-01-15T02:10:14.552733Z", "iopub.status.idle": "2021-01-15T02:10:15.223145Z", "shell.execute_reply": "2021-01-15T02:10:15.222446Z" }, "id": "hkXmqCMXZOJ7" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/5 [=====>........................] - ETA: 2s - loss: 0.0893 - 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 [==============================] - 1s 8ms/step - loss: 0.0982 - acc: 0.9979 - accuracy: 0.9979 - my_accuracy: 0.9979\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": 23, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:15.227727Z", "iopub.status.busy": "2021-01-15T02:10:15.226995Z", "iopub.status.idle": "2021-01-15T02:10:15.229630Z", "shell.execute_reply": "2021-01-15T02:10:15.230122Z" }, "id": "VLfXTAJ8Rq-e" }, "outputs": [ { "data": { "text/plain": [ "dict_keys(['loss', 'acc', 'accuracy', 'my_accuracy'])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "history.history.keys()" ] }, { "cell_type": "markdown", "metadata": { "id": "AfrgEOIg0oG_" }, "source": [ "이전 버전은 이와 다르게 `metrics=[\"accuracy\"]`를 전달하면 `dict_keys(['loss', 'acc'])`가 됩니다." ] }, { "cell_type": "markdown", "metadata": { "id": "i4Lcwbyas2aR" }, "source": [ "### 케라스 옵티마이저" ] }, { "cell_type": "markdown", "metadata": { "id": "t1t219uEVMQa" }, "source": [ "`v1.train.AdamOptimizer`나 `v1.train.GradientDescentOptimizer` 같은 `v1.train`에 있는 옵티마이저는 `tf.keras.optimizers`에 있는 것과 동일합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "LUmfiXHBKGiL" }, "source": [ "#### `v1.train`을 `keras.optimizers`로 바꾸기\n", "\n", "다음은 옵티마이저를 바꿀 때 유념해야 할 내용입니다:\n", "\n", "* 옵티마이저를 업그레이드하면 [예전 체크포인트와 호환이되지 않을 수 있습니다](#checkpoints).\n", "* 입실론 매개변수 기본값은 모두 `1e-8`에서 `1e-7`로 바뀌었습니다(대부분의 경우 큰 차이가 없습니다).\n", "* `v1.train.GradientDescentOptimizer`는 `tf.keras.optimizers.SGD`로 바꿀 수 있습니다. \n", "* `v1.train.MomentumOptimizer`는 모멘텀 매개변수를 사용하는 `SGD` 옵티마이저로 바꿀 수 있습니다: `tf.keras.optimizers.SGD(..., momentum=...)`.\n", "* `v1.train.AdamOptimizer`는 `tf.keras.optimizers.Adam`로 바꿀 수 있습니다. `beta1`과 `beta2` 매개변수는 `beta_1`과 `beta_2`로 이름이 바뀌었습니다.\n", "* `v1.train.RMSPropOptimizer`는 `tf.keras.optimizers.RMSprop`로 바꿀 수 있습니다. `decay` 매개변수는 `rho`로 이름이 바뀌었습니다.\n", "* `v1.train.AdadeltaOptimizer`는 `tf.keras.optimizers.Adadelta`로 바꿀 수 있습니다.\n", "* `tf.train.AdagradOptimizer`는 `tf.keras.optimizers.Adagrad`로 바꿀 수 있습니다.\n", "* `tf.train.FtrlOptimizer`는 `tf.keras.optimizers.Ftrl`로 바꿀 수 있습니다. `accum_name`과 `linear_name` 매개변수는 삭제되었습니다.\n", "* `tf.contrib.AdamaxOptimizer`와 `tf.contrib.NadamOptimizer`는 `tf.keras.optimizers.Adamax`와 `tf.keras.optimizers.Nadam`로 바꿀 수 있습니다. `beta1`, `beta2` 매개변수는 `beta_1`, `beta_2`로 바뀌었습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "gdOrC-09C_F5" }, "source": [ "#### `tf.keras.optimizers`의 새로운 기본값\n", "\n", "\n", "주의: 만약 모델이 수렴하는데 변화가 있다면 학습률 기본값을 확인해 보세요.\n", "\n", "`optimizers.SGD`, `optimizers.Adam`, `optimizers.RMSprop` 기본값은 그대로입니다..\n", "\n", "학습률 기본값이 바뀐 경우는 다음과 같습니다:\n", "\n", "* `optimizers.Adagrad`는 0.01에서 0.001로 바뀌었습니다.\n", "* `optimizers.Adadelta`는 1.0에서 0.001로 바뀌었습니다.\n", "* `optimizers.Adamax`는 0.002에서 0.001로 바뀌었습니다.\n", "* `optimizers.Nadam`은 0.002에서 0.001로 바뀌었습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "VaTuJR78Zj-X" }, "source": [ "### 텐서보드" ] }, { "cell_type": "markdown", "metadata": { "id": "ECbRJ-OOEe3c" }, "source": [ "텐서플로 2는 텐서보드(TensorBoard) 시각화를 위해 서머리(summary) 데이터를 작성하는데 사용하는 `tf.summary` API에 큰 변화가있습니다. 새로운 `tf.summary`에 대한 개괄 소개는 TF 2 API를 사용한 [시작하기 튜토리얼](https://www.tensorflow.org/tensorboard/get_started)와 [텐서보드 TF 2 이전 가이드](https://www.tensorflow.org/tensorboard/migrate)를 참고하세요." ] }, { "cell_type": "markdown", "metadata": { "id": "JmMLBKs66DeA" }, "source": [ "## 저장과 복원" ] }, { "cell_type": "markdown", "metadata": { "id": "5_QKn3Kl6TUu" }, "source": [ "### 체크포인트 호환성\n", "\n", "텐서플로 2.0은 [객체 기반의 체크포인트](checkpoint.ipynb)를 사용합니다.\n", "\n", "이전 이름 기반 스타일의 체크포인트도 여전히 복원할 수 있지만 주의가 필요합니다.\n", "코드 변환 과정 때문에 변수 이름이 바뀔 수 있지만 해결 방법이 있습니다.\n", "\n", "가장 간단한 방법은 새로운 모델의 이름과 체크포인트에 있는 이름을 나열해 보는 것입니다:\n", "\n", "* 여전히 모든 변수는 설정 가능한 `name` 매개변수를 가집니다.\n", "* 케라스 모델도 `name` 매개변수를 가집니다. 이 값은 변수 이름의 접두어로 사용됩니다.\n", "* `v1.name_scope` 함수를 변수 이름의 접두어를 지정하는데 사용할 수 있습니다. 이 함수는 `tf.variable_scope`와는 매우 다릅니다. 이름에만 영향을 미치며 변수를 추적하거나 재사용을 관장하지 않습니다.\n", "\n", "이것이 주어진 상황에 잘 맞지 않는다면 `v1.train.init_from_checkpoint` 함수를 시도해 보세요. 이 함수는 `assignment_map` 매개변수로 예전 이름과 새로운 이름을 매핑할 수 있습니다.\n", "\n", "노트: [지연 적재](checkpoint.ipynb#loading_mechanics)가 되는 객체 기반 체크포인트와는 달리 이름 기반 체크포인트는 함수가 호출될 때 모든 변수가 만들어 집니다. 일부 모델은 `build` 메서드를 호출하거나 배치 데이터에서 모델을 실행할 때까지 변수 생성을 지연합니다.\n", "\n", "[텐서플로 추정기(Estimator) 저장소](https://github.com/tensorflow/estimator/blob/master/tensorflow_estimator/python/estimator/tools/checkpoint_converter.py)에는 텐서플로 1.X의 추정기에서 만든 체크포인트를 2.0으로 업그레이드하는 [변환 도구](#checkpoint_converter)가 포함되어 있습니다. 비슷한 경우를 위한 도구를 만드는 방법을 보여주는 사례입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "_ONjobDD6Uur" }, "source": [ "### saved_model 호환성\n", "\n", "saved_model에는 심각한 호환성 문제가 없습니다.\n", "\n", "* 텐서플로 1.x의 saved_model은 텐서플로 2.0와 호환됩니다.\n", "* 텐서플로 2.0의 saved_model로 저장한 모델도 연산이 지원된다면 TensorFlow 1.x에서 작동됩니다." ] }, { "cell_type": "markdown", "metadata": { "id": "7SJBOlTBAC_d" }, "source": [ "### Graph.pb 또는 Graph.pbtxt " ] }, { "cell_type": "markdown", "metadata": { "id": "w17KHstVeSte" }, "source": [ "원본 `Graph.pb` 파일을 텐서플로 2.0으로 업그레이드하는 쉬운 방법은 없습니다. 이 파일을 생성하는 코드를 업그레이드하는 것이 좋습니다.\n", "\n", "하지만 \"동결된 그래프\"(변수가 상수로 바뀐 `tf.Graph`)라면 `v1.wrap_function`를 사용해 [`concrete_function`](https://tensorflow.org/guide/concrete_function)로 변환하는 것이 가능합니다:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:15.237071Z", "iopub.status.busy": "2021-01-15T02:10:15.236376Z", "iopub.status.idle": "2021-01-15T02:10:15.238756Z", "shell.execute_reply": "2021-01-15T02:10:15.238204Z" }, "id": "c_1MYhgv_JVi" }, "outputs": [], "source": [ "def wrap_frozen_graph(graph_def, inputs, outputs):\n", " def _imports_graph_def():\n", " tf.compat.v1.import_graph_def(graph_def, name=\"\")\n", " wrapped_import = tf.compat.v1.wrap_function(_imports_graph_def, [])\n", " import_graph = wrapped_import.graph\n", " return wrapped_import.prune(\n", " tf.nest.map_structure(import_graph.as_graph_element, inputs),\n", " tf.nest.map_structure(import_graph.as_graph_element, outputs))" ] }, { "cell_type": "markdown", "metadata": { "id": "WunRKDg1SWAl" }, "source": [ "예를 들어 2016년 Inception v1의 동결된 그래프입니다:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:15.244578Z", "iopub.status.busy": "2021-01-15T02:10:15.243879Z", "iopub.status.idle": "2021-01-15T02:10:18.131947Z", "shell.execute_reply": "2021-01-15T02:10:18.132428Z" }, "id": "7dhWJxKv0wbP" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Downloading data from http://storage.googleapis.com/download.tensorflow.org/models/inception_v1_2016_08_28_frozen.pb.tar.gz\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", " 8192/24695710 [..............................] - ETA: 0s" ] }, { "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\r", " 4202496/24695710 [====>.........................] - ETA: 5s" ] }, { "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\r", " 9551872/24695710 [==========>...................] - ETA: 1s" ] }, { "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\r", "24698880/24695710 [==============================] - 1s 0us/step\n" ] } ], "source": [ "path = tf.keras.utils.get_file(\n", " 'inception_v1_2016_08_28_frozen.pb',\n", " 'http://storage.googleapis.com/download.tensorflow.org/models/inception_v1_2016_08_28_frozen.pb.tar.gz',\n", " untar=True)" ] }, { "cell_type": "markdown", "metadata": { "id": "lQmwoipqBvmS" }, "source": [ "`tf.GraphDef`를 로드합니다:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:18.137363Z", "iopub.status.busy": "2021-01-15T02:10:18.136676Z", "iopub.status.idle": "2021-01-15T02:10:18.176526Z", "shell.execute_reply": "2021-01-15T02:10:18.175880Z" }, "id": "NFR98Qp67pqJ" }, "outputs": [], "source": [ "graph_def = tf.compat.v1.GraphDef()\n", "loaded = graph_def.ParseFromString(open(path,'rb').read())" ] }, { "cell_type": "markdown", "metadata": { "id": "qy0WMb-6KFou" }, "source": [ "`concrete_function`로 감쌉니다:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:18.270100Z", "iopub.status.busy": "2021-01-15T02:10:18.209066Z", "iopub.status.idle": "2021-01-15T02:10:18.453566Z", "shell.execute_reply": "2021-01-15T02:10:18.452910Z" }, "id": "XbhYzF6fGEfJ" }, "outputs": [], "source": [ "inception_func = wrap_frozen_graph(\n", " graph_def, inputs='input:0',\n", " outputs='InceptionV1/InceptionV1/Mixed_3b/Branch_1/Conv2d_0a_1x1/Relu:0')" ] }, { "cell_type": "markdown", "metadata": { "id": "AgUYVl9frcsq" }, "source": [ "텐서를 입력으로 전달합니다:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:18.458776Z", "iopub.status.busy": "2021-01-15T02:10:18.458037Z", "iopub.status.idle": "2021-01-15T02:10:18.517034Z", "shell.execute_reply": "2021-01-15T02:10:18.516360Z" }, "id": "T79IYpGceAbT" }, "outputs": [ { "data": { "text/plain": [ "TensorShape([1, 28, 28, 96])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "input_img = tf.ones([1,224,224,3], dtype=tf.float32)\n", "inception_func(input_img).shape" ] }, { "cell_type": "markdown", "metadata": { "id": "ewl9P3oZ6ZtR" }, "source": [ "## 추정기" ] }, { "cell_type": "markdown", "metadata": { "id": "YprVP9g3l6eG" }, "source": [ "### 추정기로 훈련하기\n", "\n", "텐서플로 2.0은 추정기(estimator)를 지원합니다.\n", "\n", "추정기를 사용할 때 텐서플로 1.x의 `input_fn()`, `tf.estimator.TrainSpec`, `tf.estimator.EvalSpec`를 사용할 수 있습니다.\n", "\n", "다음은 `input_fn`을 사용하여 훈련과 평가를 수행하는 예입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "N5kZeJsF8lS2" }, "source": [ "#### input_fn과 훈련/평가 스펙 만들기" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:18.525497Z", "iopub.status.busy": "2021-01-15T02:10:18.524422Z", "iopub.status.idle": "2021-01-15T02:10:18.575989Z", "shell.execute_reply": "2021-01-15T02:10:18.576447Z" }, "id": "AOlXGO4J6jDh" }, "outputs": [], "source": [ "# 추정기 input_fn을 정의합니다.\n", "def input_fn():\n", " datasets, info = tfds.load(name='mnist', with_info=True, as_supervised=True)\n", " mnist_train, mnist_test = datasets['train'], datasets['test']\n", "\n", " BUFFER_SIZE = 10000\n", " BATCH_SIZE = 64\n", "\n", " def scale(image, label):\n", " image = tf.cast(image, tf.float32)\n", " image /= 255\n", "\n", " return image, label[..., tf.newaxis]\n", "\n", " train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)\n", " return train_data.repeat()\n", "\n", "# 훈련과 평가 스펙을 정의합니다.\n", "train_spec = tf.estimator.TrainSpec(input_fn=input_fn,\n", " max_steps=STEPS_PER_EPOCH * NUM_EPOCHS)\n", "eval_spec = tf.estimator.EvalSpec(input_fn=input_fn,\n", " steps=STEPS_PER_EPOCH)" ] }, { "cell_type": "markdown", "metadata": { "id": "_o6J48Nj9H5c" }, "source": [ "### 케라스 모델 정의 사용하기" ] }, { "cell_type": "markdown", "metadata": { "id": "IXCQdhGq9SbB" }, "source": [ "텐서플로 2.0에서 추정기를 구성하는 방법은 조금 다릅니다.\n", "\n", "케라스를 사용하여 모델을 정의하는 것을 권장합니다. 그 다음 `tf.keras.model_to_estimator` 유틸리티를 사용하여 모델을 추정기로 바꾸세요. 다음 코드는 추정기를 만들고 훈련할 때 이 유틸리티를 사용하는 방법을 보여 줍니다." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:18.583724Z", "iopub.status.busy": "2021-01-15T02:10:18.582687Z", "iopub.status.idle": "2021-01-15T02:10:18.585329Z", "shell.execute_reply": "2021-01-15T02:10:18.584708Z" }, "id": "aelsClm3Cq4I" }, "outputs": [], "source": [ "def make_model():\n", " return 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", " ])" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:18.598449Z", "iopub.status.busy": "2021-01-15T02:10:18.597320Z", "iopub.status.idle": "2021-01-15T02:10:22.583312Z", "shell.execute_reply": "2021-01-15T02:10:22.582806Z" }, "id": "HJb6f8dtl6rr" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpz036jrul\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpz036jrul\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using the Keras model provided.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using the Keras model provided.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/backend.py:434: UserWarning: `tf.keras.backend.set_learning_phase` is deprecated and will be removed after 2020-10-11. To update it, simply pass a True/False value to the `training` argument of the `__call__` method of your layer or model.\n", " warnings.warn('`tf.keras.backend.set_learning_phase` is deprecated and '\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmpz036jrul', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmpz036jrul', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Not using Distribute Coordinator.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Not using Distribute Coordinator.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-starting with WarmStartSettings: WarmStartSettings(ckpt_to_initialize_from='/tmp/tmpz036jrul/keras/keras_model.ckpt', vars_to_warm_start='.*', var_name_to_vocab_info={}, var_name_to_prev_var_name={})\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-starting with WarmStartSettings: WarmStartSettings(ckpt_to_initialize_from='/tmp/tmpz036jrul/keras/keras_model.ckpt', vars_to_warm_start='.*', var_name_to_vocab_info={}, var_name_to_prev_var_name={})\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-starting from: /tmp/tmpz036jrul/keras/keras_model.ckpt\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-starting from: /tmp/tmpz036jrul/keras/keras_model.ckpt\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-starting variables only in TRAINABLE_VARIABLES.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-starting variables only in TRAINABLE_VARIABLES.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-started 8 variables.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Warm-started 8 variables.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmpz036jrul/model.ckpt.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmpz036jrul/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 2.8927445, step = 0\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 2.8927445, step = 0\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 25...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 25...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 25 into /tmp/tmpz036jrul/model.ckpt.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 25 into /tmp/tmpz036jrul/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 25...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 25...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/training.py:2325: UserWarning: `Model.state_updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically.\n", " warnings.warn('`Model.state_updates` will be removed in a future version. '\n", "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2021-01-15T02:10:21Z\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2021-01-15T02:10:21Z\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/tmpz036jrul/model.ckpt-25\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/tmpz036jrul/model.ckpt-25\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [1/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [1/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [2/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [2/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [3/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [3/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [4/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [4/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [5/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [5/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Inference Time : 1.00671s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Inference Time : 1.00671s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2021-01-15-02:10:22\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2021-01-15-02:10:22\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 25: accuracy = 0.61875, global_step = 25, loss = 1.5028715\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 25: accuracy = 0.61875, global_step = 25, loss = 1.5028715\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 25: /tmp/tmpz036jrul/model.ckpt-25\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 25: /tmp/tmpz036jrul/model.ckpt-25\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.5315775.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.5315775.\n" ] }, { "data": { "text/plain": [ "({'accuracy': 0.61875, 'loss': 1.5028715, 'global_step': 25}, [])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = make_model()\n", "\n", "model.compile(optimizer='adam',\n", " loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n", " metrics=['accuracy'])\n", "\n", "estimator = tf.keras.estimator.model_to_estimator(\n", " keras_model = model\n", ")\n", "\n", "tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)" ] }, { "cell_type": "markdown", "metadata": { "id": "o7EElFB3xj85" }, "source": [ "노트: 케라스에서는 가중치가 적용된 지표를 지원하지 않습니다. `model_to_estimator`를 사용해 추정기 API의 가중 지표로 변경할 수 없습니다. `add_metrics` 함수를 사용해 추정기 스펙(spec)에 직접 이런 지표를 만들어야 합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "QWNzEHKB7myd" }, "source": [ "### 사용자 정의 `model_fn` 사용하기\n", "\n", "기존에 작성한 사용자 정의 추정기 `model_fn`을 유지해야 한다면 이 `model_fn`을 케라스 모델로 바꿀 수 있습니다.\n", "\n", "그러나 호환성 때문에 사용자 정의 `model_fn`은 1.x 스타일의 그래프 모드로 실행될 것입니다. 즉 즉시 실행과 의존성 자동 제어가 없다는 뜻입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "-ptTxL1q6flL" }, "source": [ "\n", "\n", "#### 사용자 정의 model_fn을 최소한만 변경하기\n", "\n", "사용자 정의 `model_fn`을 최소한의 변경만으로 TF 2.0과 사용하려면 `tf.compat.v1` 아래의 `optimizers`와 `metrics`을 사용할 수 있습니다.\n", "\n", "사용자 정의 `model_fn`에 케라스 모델을 사용하는 것은 사용자 정의 훈련 루프에 사용하는 것과 비슷합니다:\n", "\n", "* `mode` 매개변수에 기초하여 `training` 상태를 적절하게 지정하세요.\n", "* 옵티마이저에 모델의 `trainable_variables`를 명시적으로 전달하세요.\n", "\n", "[사용자 정의 루프](#custom_loop)와 큰 차이점은 다음과 같습니다:\n", "\n", "* `model.losses`를 사용하는 대신 `tf.keras.Model.get_losses_for` 사용하여 손실을 추출하세요.\n", "* `tf.keras.Model.get_updates_for`를 사용하여 모델의 업데이트 값을 추출하세요.\n", "\n", "노트: \"업데이트(update)\"는 각 배치가 끝난 후에 모델에 적용해야 할 변화량입니다. 예를 들면 `tf.keras.layers.BatchNormalization` 층에서 평균과 분산의 이동 평균(moving average)이 있습니다.\n", "\n", "다음은 사용자 정의 `model_fn`으로부터 추정기를 만드는 코드로 이런 개념을 잘 보여 줍니다." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:22.593285Z", "iopub.status.busy": "2021-01-15T02:10:22.592598Z", "iopub.status.idle": "2021-01-15T02:10:26.250509Z", "shell.execute_reply": "2021-01-15T02:10:26.251017Z" }, "id": "iY16eZKW606-" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmppm9e8tt4\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmppm9e8tt4\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmppm9e8tt4', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmppm9e8tt4', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Not using Distribute Coordinator.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Not using Distribute Coordinator.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmppm9e8tt4/model.ckpt.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmppm9e8tt4/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 2.9495518, step = 0\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 2.9495518, step = 0\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 25...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 25...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 25 into /tmp/tmppm9e8tt4/model.ckpt.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 25 into /tmp/tmppm9e8tt4/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 25...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 25...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2021-01-15T02:10:25Z\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2021-01-15T02:10:25Z\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/tmppm9e8tt4/model.ckpt-25\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/tmppm9e8tt4/model.ckpt-25\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [1/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [1/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [2/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [2/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [3/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [3/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [4/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [4/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [5/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [5/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Inference Time : 0.91746s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Inference Time : 0.91746s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2021-01-15-02:10:26\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2021-01-15-02:10:26\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 25: accuracy = 0.509375, global_step = 25, loss = 1.7217859\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 25: accuracy = 0.509375, global_step = 25, loss = 1.7217859\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 25: /tmp/tmppm9e8tt4/model.ckpt-25\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 25: /tmp/tmppm9e8tt4/model.ckpt-25\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.4915019.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.4915019.\n" ] }, { "data": { "text/plain": [ "({'accuracy': 0.509375, 'loss': 1.7217859, 'global_step': 25}, [])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def my_model_fn(features, labels, mode):\n", " model = make_model()\n", "\n", " optimizer = tf.compat.v1.train.AdamOptimizer()\n", " loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n", "\n", " training = (mode == tf.estimator.ModeKeys.TRAIN)\n", " predictions = model(features, training=training)\n", "\n", " if mode == tf.estimator.ModeKeys.PREDICT:\n", " return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)\n", "\n", " reg_losses = model.get_losses_for(None) + model.get_losses_for(features)\n", " total_loss=loss_fn(labels, predictions) + tf.math.add_n(reg_losses)\n", "\n", " accuracy = tf.compat.v1.metrics.accuracy(labels=labels,\n", " predictions=tf.math.argmax(predictions, axis=1),\n", " name='acc_op')\n", "\n", " update_ops = model.get_updates_for(None) + model.get_updates_for(features)\n", " minimize_op = optimizer.minimize(\n", " total_loss,\n", " var_list=model.trainable_variables,\n", " global_step=tf.compat.v1.train.get_or_create_global_step())\n", " train_op = tf.group(minimize_op, update_ops)\n", "\n", " return tf.estimator.EstimatorSpec(\n", " mode=mode,\n", " predictions=predictions,\n", " loss=total_loss,\n", " train_op=train_op, eval_metric_ops={'accuracy': accuracy})\n", "\n", "# 추정기를 만들고 훈련합니다.\n", "estimator = tf.estimator.Estimator(model_fn=my_model_fn)\n", "tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)" ] }, { "cell_type": "markdown", "metadata": { "id": "VtMD7-zJenwu" }, "source": [ "#### TF 2.0으로 사용자 정의 `model_fn` 만들기\n", "\n", "사용자 정의 `model_fn`에서 TF 1.x API를 모두 제거하고 TF 2.0으로 업그레이드하려면 옵티마이저와 지표를 `tf.keras.optimizers`와 `tf.keras.metrics`로 업데이트해야 합니다.\n", "\n", "위에서 언급한 [최소한의 변경](#minimal_changes)외에도 사용자 정의 `model_fn`에서 업그레이드해야 할 것이 있습니다:\n", "\n", "* `v1.train.Optimizer` 대신에 [`tf.keras.optimizers`](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/optimizers)을 사용하세요.\n", "* `tf.keras.optimizers`에 모델의 `trainable_variables`을 명시적으로 전달하세요.\n", "* `train_op/minimize_op`을 계산하려면,\n", " * 손실이 (호출 가능한 객체가 아니라) 스칼라 `Tensor`이면 `Optimizer.get_updates()`을 사용하세요. 반환되는 리스트의 첫 번째 원소가 `train_op/minimize_op`입니다. \n", " * 손실이 (함수 같은) 호출 가능한 객체라면 `train_op/minimize_op` 객체를 얻기 위해 `Optimizer.minimize()`를 사용하세요.\n", "* 평가를 하려면 `tf.compat.v1.metrics` 대신에 [`tf.keras.metrics`](https://www.tensorflow.org/api_docs/python/tf/keras/metrics)를 사용하세요.\n", "\n", "위의 `my_model_fn`를 2.0으로 이전한 코드는 다음과 같습니다:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:26.263905Z", "iopub.status.busy": "2021-01-15T02:10:26.263058Z", "iopub.status.idle": "2021-01-15T02:10:29.687176Z", "shell.execute_reply": "2021-01-15T02:10:29.686544Z" }, "id": "k5IwtMBiwMwa" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpv_5iwm57\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpv_5iwm57\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmpv_5iwm57', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmpv_5iwm57', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Not using Distribute Coordinator.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Not using Distribute Coordinator.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmpv_5iwm57/model.ckpt.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmpv_5iwm57/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 3.2416427, step = 0\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 3.2416427, step = 0\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 25...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 25...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 25 into /tmp/tmpv_5iwm57/model.ckpt.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 25 into /tmp/tmpv_5iwm57/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 25...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 25...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2021-01-15T02:10:28Z\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2021-01-15T02:10:28Z\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/tmpv_5iwm57/model.ckpt-25\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/tmpv_5iwm57/model.ckpt-25\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [1/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [1/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [2/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [2/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [3/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [3/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [4/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [4/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [5/5]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Evaluation [5/5]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Inference Time : 0.88335s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Inference Time : 0.88335s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2021-01-15-02:10:29\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2021-01-15-02:10:29\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 25: Accuracy = 0.7125, global_step = 25, loss = 1.4852016\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 25: Accuracy = 0.7125, global_step = 25, loss = 1.4852016\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 25: /tmp/tmpv_5iwm57/model.ckpt-25\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 25: /tmp/tmpv_5iwm57/model.ckpt-25\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.44984868.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.44984868.\n" ] }, { "data": { "text/plain": [ "({'Accuracy': 0.7125, 'loss': 1.4852016, 'global_step': 25}, [])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def my_model_fn(features, labels, mode):\n", " model = make_model()\n", "\n", " training = (mode == tf.estimator.ModeKeys.TRAIN)\n", " loss_obj = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n", " predictions = model(features, training=training)\n", "\n", " # 조건이 없는 손실(None 부분)과 \n", " # 입력 조건이 있는 손실(features 부분)을 얻습니다.\n", " reg_losses = model.get_losses_for(None) + model.get_losses_for(features)\n", " total_loss=loss_obj(labels, predictions) + tf.math.add_n(reg_losses)\n", "\n", " # tf.keras.metrics로 업그레이드 합니다.\n", " accuracy_obj = tf.keras.metrics.Accuracy(name='acc_obj')\n", " accuracy = accuracy_obj.update_state(\n", " y_true=labels, y_pred=tf.math.argmax(predictions, axis=1))\n", "\n", " train_op = None\n", " if training:\n", " # tf.keras.optimizers로 업그레이드합니다.\n", " optimizer = tf.keras.optimizers.Adam()\n", " # tf.compat.v1.train.global_step을 올바르게 증가시키기 위해\n", " # 수동으로 tf.compat.v1.global_step 변수를 optimizer.iterations에 할당합니다.\n", " # SessionRunHooks이 global_step에 의존하기 때문에\n", " # 이 할당문은 추정기에 지정된 모든 `tf.train.SessionRunHook`에 필수적입니다.\n", " optimizer.iterations = tf.compat.v1.train.get_or_create_global_step()\n", " # 조건이 없는 손실(None 부분)과 \n", " # 입력 조건이 있는 손실(features 부분)을 얻습니다.\n", " update_ops = model.get_updates_for(None) + model.get_updates_for(features)\n", " # minimize_op을 계산합니다.\n", " minimize_op = optimizer.get_updates(\n", " total_loss,\n", " model.trainable_variables)[0]\n", " train_op = tf.group(minimize_op, *update_ops)\n", "\n", " return tf.estimator.EstimatorSpec(\n", " mode=mode,\n", " predictions=predictions,\n", " loss=total_loss,\n", " train_op=train_op,\n", " eval_metric_ops={'Accuracy': accuracy_obj})\n", "\n", "# 추정기를 만들고 훈련합니다.\n", "estimator = tf.estimator.Estimator(model_fn=my_model_fn)\n", "tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)" ] }, { "cell_type": "markdown", "metadata": { "id": "ocQ_bntOvSM9" }, "source": [ "### 프리메이드 추정기\n", "\n", "`tf.estimator.DNN*`, `tf.estimator.Linear*`, `tf.estimator.DNNLinearCombined*` 모듈 아래에 있는 [프리메이드 추정기(premade estimator)](https://www.tensorflow.org/guide/premade_estimators)는 계속 텐서플로 2.0 API를 지원합니다. 하지만 일부 매개변수가 바뀌었습니다:\n", "\n", "1. `input_layer_partitioner`: 2.0에서 삭제되었습니다.\n", "2. `loss_reduction`: `tf.compat.v1.losses.Reduction` 대신에 `tf.keras.losses.Reduction`로 업데이트합니다. 기본값이 `tf.compat.v1.losses.Reduction.SUM`에서 `tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE`로 바뀌었습니다.\n", "3. `optimizer`, `dnn_optimizer`, `linear_optimizer`: 이 매개변수는 `tf.compat.v1.train.Optimizer` 대신에 `tf.keras.optimizers`로 업데이트되었습니다.\n", "\n", "위 변경 사항을 반영하려면:\n", "1. [`Distribution Strategy`](https://www.tensorflow.org/guide/distributed_training)는 TF 2.0을 자동으로 대응하므로 `input_layer_partitioner`에 대해 이전이 필요없습니다.\n", "2. `loss_reduction`의 경우 지원되는 옵션을 [`tf.keras.losses.Reduction`](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/losses/Reduction) 확인해 보세요..\n", "3. `optimizer` 매개변수의 경우, `optimizer`, `dnn_optimizer`, `linear_optimizer` 매개변수를 전달하지 않거나 `optimizer` 매개변수를 `string`으로 지정했다면 아무것도 바꿀 필요가 없습니다. 기본적으로 `tf.keras.optimizers`를 사용합니다. 그외의 경우 `tf.compat.v1.train.Optimizer`를 이에 상응하는 [`tf.keras.optimizers`](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/optimizers)로 바꾸어야 합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "ypWqeXABvte5" }, "source": [ "#### 체크포인트 변환기\n", "\n", "\n", "`keras.optimizers`로 이전하면 TF 1.X로 저장한 체크포인트를 사용할 수 없습니다.\n", "체크포인트에 저장하는 `tf.keras.optimizers` 변수가 다르기 때문입니다.\n", "Tf 2.0으로 이전한 후에 예전 체크포인트를 사용하려면 [체크포인트 변환기](https://github.com/tensorflow/estimator/blob/master/tensorflow_estimator/python/estimator/tools/checkpoint_converter.py)를 사용하세요." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:29.723394Z", "iopub.status.busy": "2021-01-15T02:10:29.722511Z", "iopub.status.idle": "2021-01-15T02:10:30.228405Z", "shell.execute_reply": "2021-01-15T02:10:30.227773Z" }, "id": "v8sPXdHKnzwK" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " % Total % Received % Xferd Average Speed Time Time Time Current\r\n", " Dload Upload Total Spent Left Speed\r\n", "\r", " 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "100 15165 100 15165 0 0 44602 0 --:--:-- --:--:-- --:--:-- 44602\r\n" ] } ], "source": [ "! curl -O https://raw.githubusercontent.com/tensorflow/estimator/master/tensorflow_estimator/python/estimator/tools/checkpoint_converter.py" ] }, { "cell_type": "markdown", "metadata": { "id": "7vQSehZT__VE" }, "source": [ "이 스크립트는 도움말을 제공합니다:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:30.233866Z", "iopub.status.busy": "2021-01-15T02:10:30.233168Z", "iopub.status.idle": "2021-01-15T02:10:32.551711Z", "shell.execute_reply": "2021-01-15T02:10:32.551020Z" }, "id": "yDot9ouEjywy" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2021-01-15 02:10:30.463351: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0\r\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "usage: checkpoint_converter.py [-h]\r\n", " {dnn,linear,combined} source_checkpoint\r\n", " source_graph target_checkpoint\r\n", "\r\n", "positional arguments:\r\n", " {dnn,linear,combined}\r\n", " The type of estimator to be converted. So far, the\r\n", " checkpoint converter only supports Canned Estimator.\r\n", " So the allowed types include linear, dnn and combined.\r\n", " source_checkpoint Path to source checkpoint file to be read in.\r\n", " source_graph Path to source graph file to be read in.\r\n", " target_checkpoint Path to checkpoint file to be written out.\r\n", "\r\n", "optional arguments:\r\n", " -h, --help show this help message and exit\r\n" ] } ], "source": [ "! python checkpoint_converter.py -h" ] }, { "cell_type": "markdown", "metadata": { "id": "dt8ct9XCFqls" }, "source": [ "## TensorShape\n", "\n", "이 클래스는 `tf.compat.v1.Dimension` 객체 대신에 `int` 값을 가지도록 단순화되었습니다. 따라서 `int` 값을 얻기 위해 `.value()` 메서드를 호출할 필요가 없습니다.\n", "\n", "여전히 개별 `tf.compat.v1.Dimension` 객체는 `tf.TensorShape.dims`로 참조할 수 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "x36cWcmM8Eu1" }, "source": [ "다음 코드는 텐서플로 1.x와 텐서플로 2.0의 차이점을 보여줍니다." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:32.558308Z", "iopub.status.busy": "2021-01-15T02:10:32.557421Z", "iopub.status.idle": "2021-01-15T02:10:32.560418Z", "shell.execute_reply": "2021-01-15T02:10:32.560885Z" }, "id": "PbpD-kHOZR4A" }, "outputs": [ { "data": { "text/plain": [ "TensorShape([16, None, 256])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# TensorShape 객체를 만들고 인덱스를 참조합니다.\n", "i = 0\n", "shape = tf.TensorShape([16, None, 256])\n", "shape" ] }, { "cell_type": "markdown", "metadata": { "id": "kDFck03neNy0" }, "source": [ "TF 1.x에서는 다음과 같이 사용합니다:\n", "\n", "```python\n", "value = shape[i].value\n", "```\n", "\n", "TF 2.0에서는 다음과 같이 사용합니다:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:32.565914Z", "iopub.status.busy": "2021-01-15T02:10:32.565020Z", "iopub.status.idle": "2021-01-15T02:10:32.568724Z", "shell.execute_reply": "2021-01-15T02:10:32.568106Z" }, "id": "KuR73QGEeNdH" }, "outputs": [ { "data": { "text/plain": [ "16" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "value = shape[i]\n", "value" ] }, { "cell_type": "markdown", "metadata": { "id": "bPWPNKRiZmkd" }, "source": [ "TF 1.x에서는 다음과 같이 사용합니다:\n", "\n", "```python\n", "for dim in shape:\n", " value = dim.value\n", " print(value)\n", "```\n", "\n", "TF 2.0에서는 다음과 같이 사용합니다:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:32.573248Z", "iopub.status.busy": "2021-01-15T02:10:32.572448Z", "iopub.status.idle": "2021-01-15T02:10:32.575385Z", "shell.execute_reply": "2021-01-15T02:10:32.575785Z" }, "id": "y6s0vuuprJfc" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "16\n", "None\n", "256\n" ] } ], "source": [ "for value in shape:\n", " print(value)" ] }, { "cell_type": "markdown", "metadata": { "id": "YpRgngu3Zw-A" }, "source": [ "TF 1.x에서는 다음과 같이 사용합니다(다른 Dimension 메서드를 사용할 때도):\n", "\n", "```python\n", "dim = shape[i]\n", "dim.assert_is_compatible_with(other_dim)\n", "```\n", "\n", "TF 2.0에서는 다음과 같이 사용합니다:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:32.581366Z", "iopub.status.busy": "2021-01-15T02:10:32.580507Z", "iopub.status.idle": "2021-01-15T02:10:32.584070Z", "shell.execute_reply": "2021-01-15T02:10:32.583590Z" }, "id": "LpViGEcUZDGX" }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "other_dim = 16\n", "Dimension = tf.compat.v1.Dimension\n", "\n", "if shape.rank is None:\n", " dim = Dimension(None)\n", "else:\n", " dim = shape.dims[i]\n", "dim.is_compatible_with(other_dim) # 다른 Dimension 메서드도 동일" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:32.588762Z", "iopub.status.busy": "2021-01-15T02:10:32.587962Z", "iopub.status.idle": "2021-01-15T02:10:32.590077Z", "shell.execute_reply": "2021-01-15T02:10:32.590526Z" }, "id": "GaiGe36dOdZ_" }, "outputs": [], "source": [ "shape = tf.TensorShape(None)\n", "\n", "if shape:\n", " dim = shape.dims[i]\n", " dim.is_compatible_with(other_dim) # 다른 Dimension 메서드도 동일" ] }, { "cell_type": "markdown", "metadata": { "id": "3kLLY0I3PI-l" }, "source": [ "랭크(rank)를 알 수 있다면 `tf.TensorShape`의 불리언 값은 `True`가 됩니다. 그렇지 않으면 `False`입니다." ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2021-01-15T02:10:32.597529Z", "iopub.status.busy": "2021-01-15T02:10:32.596574Z", "iopub.status.idle": "2021-01-15T02:10:32.599707Z", "shell.execute_reply": "2021-01-15T02:10:32.600167Z" }, "id": "-Ow1ndKpOnJd" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "True\n", "True\n", "True\n", "True\n", "\n", "False\n" ] } ], "source": [ "print(bool(tf.TensorShape([]))) # 스칼라\n", "print(bool(tf.TensorShape([0]))) # 길이 0인 벡터\n", "print(bool(tf.TensorShape([1]))) # 길이 1인 벡터\n", "print(bool(tf.TensorShape([None]))) # 길이를 알 수 없는 벡터\n", "print(bool(tf.TensorShape([1, 10, 100]))) # 3D 텐서\n", "print(bool(tf.TensorShape([None, None, None]))) # 크기를 모르는 3D 텐서\n", "print()\n", "print(bool(tf.TensorShape(None))) # 랭크를 알 수 없는 텐서" ] }, { "cell_type": "markdown", "metadata": { "id": "722LaM0GwiL_" }, "source": [ "## 그외 변경 사항\n", "\n", "* `tf.colocate_with` 삭제: 텐서플로의 장치 배치 알고리즘이 크게 향상되었습니다. 더 이상 이 연산이 필요하지 않습니다. 혹시 성능 저하가 발견된다면 [버그를 신고해 주세요][please file a bug](https://github.com/tensorflow/tensorflow/issues).\n", "\n", "* `v1.ConfigProto`를 동일한 `tf.config` 함수로 교체합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "vKX6AdTAQhB-" }, "source": [ "## 결론\n", "\n", "전체적인 과정은 다음과 같습니다:\n", "\n", "1. 업그레이드 스크립트를 실행하세요.\n", "2. `contrib` 모듈을 삭제하세요.\n", "3. 모델을 객체 지향 스타일(케라스)로 바꾸세요.\n", "4. 가능한 `tf.keras`나 `tf.estimator`의 훈련과 평가 루프를 사용하세요.\n", "5. 그렇지 않으면 사용자 정의 루프를 사용하세요. 세션과 컬렉션은 사용하지 말아야 합니다.\n", "\n", "텐서플로 2.0 스타일로 코드를 바꾸려면 약간의 작업이 필요하지만 다음과 같은 장점을 얻을 수 있습니다:\n", "\n", "* 코드 라인이 줄어 듭니다.\n", "* 명료하고 단순해집니다.\n", "* 디버깅이 쉬워집니다." ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "migrate.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.6.9" } }, "nbformat": 4, "nbformat_minor": 0 }