{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "_후반부에 필요한 패키지 및 함수 등 초기화_" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import division, print_function, unicode_literals\n", "\n", "import tensorflow as tf\n", "import numpy as np\n", "import os\n", "import time\n", "\n", "from functools import partial\n", "\n", "# 맷플롯립 설정\n", "%matplotlib inline\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "plt.rcParams['axes.labelsize'] = 14\n", "plt.rcParams['xtick.labelsize'] = 12\n", "plt.rcParams['ytick.labelsize'] = 12\n", "\n", "def reset_graph(seed=42):\n", " tf.reset_default_graph()\n", " tf.set_random_seed(seed)\n", " np.random.seed(seed)\n", "\n", "def logit(z):\n", " return 1 / (1 + np.exp(-z))\n", "\n", "def shuffle_batch(X, y, batch_size):\n", " rnd_idx = np.random.permutation(len(X))\n", " n_batches = len(X) // batch_size\n", " for batch_idx in np.array_split(rnd_idx, n_batches):\n", " X_batch, y_batch = X[batch_idx], y[batch_idx]\n", " yield X_batch, y_batch\n", " \n", "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()\n", "X_train = X_train.astype(np.float32).reshape(-1, 28*28) / 255.0\n", "X_test = X_test.astype(np.float32).reshape(-1, 28*28) / 255.0\n", "y_train = y_train.astype(np.int32)\n", "y_test = y_test.astype(np.int32)\n", "X_valid, X_train = X_train[:5000], X_train[5000:]\n", "y_valid, y_train = y_train[:5000], y_train[5000:]\n", "\n", "learning_rate = 0.01" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Gradient Descent" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_hidden2 = 300\n", "n_hidden3 = 300\n", "n_hidden4 = 100\n", "n_hidden5 = 50\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " hidden3 = my_dense_layer(hidden2, n_hidden3, name=\"hidden3\")\n", " hidden4 = my_dense_layer(hidden3, n_hidden4, name=\"hidden4\")\n", " hidden5 = my_dense_layer(hidden4, n_hidden5, name=\"hidden5\")\n", " logits = my_dense_layer(hidden5, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.GradientDescentOptimizer(learning_rate) # Gradient Descent\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![optimizer.png](./img/optimizer.png)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 13회에서 93.040%\n", "25.484[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.3.3 AdaGrad\n", "\n", "한쪽이 길쭉한 그릇 문제를 다시 생각해보면, 경사 하강법의 경우 가장 가파른 경사를 따라 빠르게 내려가기 시작해서 골짜기\n", "\n", "아래로 느리게 이동함. 따라서 알고리즘이 이를 일찍 감지하고 전역 최적점 쪽으로 좀 더 정확한 방향을 잡았다면 좋았을 것.\n", "\n", "**AdaGrad** 알고리즘은 가장 가파른 차원을 따라 그래디언트 벡터의 스케일을 감소시켜 이 문제를 해결함.\n", "\n", "
\n", "![eq1.png](./img/eq1.png)\n", "\n", "첫 번째 단계는 그래디언트의 제곱을 벡터 $s$에 누적.\n", "\n", "두 번째 단계는 경사 하강법과 거의 같지만 그래디언트 벡터를 $\\sqrt{}$s+ε 으로 나누어 스케일을 조정하는 점이 다름\n", "\n", "(ε는 0으로 나누는 것을 막기 위한 값으로, 일반적으로 $10^{-10}$).\n", "\n", "요약하면 경사가 완만한 차원보다 가파른 차원에 대해 학습률($\\eta$)을 더 빠르게 감소시킴. 이를 **적응적 학습률** 이라고 부르며, \n", "\n", "전역 최적점 방향으로 더 곧장 가도록 갱신되는 데 도움이 됨. 학습률 하이퍼파라미터 $\\eta$를 덜 튜닝해도 되는 점이 또 하나의 장점임.\n", "\n", "\n", "![img11_7.png](./img/img11_7.png)\n", "\n", "\n", "AdaGrad는 간단한 2차방정식 문제에 대해서는 잘 작동하지만 신경망을 훈련시킬 때 너무 일찍 멈춰버리는 경향이 있음.\n", "\n", "그래서 심층 신경망에는 사용하지 말아야 함(하지만 선형 회귀 같은 간단한 작업에는 효과적일 수 있음)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_hidden2 = 300\n", "n_hidden3 = 300\n", "n_hidden4 = 100\n", "n_hidden5 = 50\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " hidden3 = my_dense_layer(hidden2, n_hidden3, name=\"hidden3\")\n", " hidden4 = my_dense_layer(hidden3, n_hidden4, name=\"hidden4\")\n", " hidden5 = my_dense_layer(hidden4, n_hidden5, name=\"hidden5\")\n", " logits = my_dense_layer(hidden5, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.AdagradOptimizer(learning_rate=learning_rate) # AdaGrad Optimizer\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Best Accuracy : 95회에서 95.060%\n", "100.526[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.3.4 RMSProp\n", "\n", "AdaGrad는 너무 빠르게 느려져서 전역 최적점에 수렴하지 못하지만 RMSProp 알고리즘은 \n", "\n", "(훈련 시작부터의 모든 그래디언트가 아니고) 가장 최근 반복에서 비롯된 그래디언트만 누적함으로써 이 문제를 해결했음.\n", "\n", "
\n", "![eq2.png](./img/eq2.png)\n", "\n", "보통 감쇠율 $\\beta = 0.9$ 설정함. 이 기본값이 잘 작동하는 경우가 많으므로 이를 튜닝할 필요 없음.\n", "\n", "아주 간단한 문제를 제외하고는 이 옵티마이저가 언제나 AdaGrad보다 훨씬 더 성능이 좋음. Adam 최적화가 나오기 전까지 \n", "\n", "연구자들이 가장 선호하는 최적화 알고리즘 이었음." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_hidden2 = 300\n", "n_hidden3 = 300\n", "n_hidden4 = 100\n", "n_hidden5 = 50\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " hidden3 = my_dense_layer(hidden2, n_hidden3, name=\"hidden3\")\n", " hidden4 = my_dense_layer(hidden3, n_hidden4, name=\"hidden4\")\n", " hidden5 = my_dense_layer(hidden4, n_hidden5, name=\"hidden5\")\n", " logits = my_dense_layer(hidden5, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"): # RMSProp Optimizer\n", " optimizer = tf.train.RMSPropOptimizer(learning_rate=learning_rate, momentum=0.9, decay=0.9, epsilon=1e-10)\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 1회에서 11.260%\n", "13.438[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.3.5 Adam 최적화\n", "\n", "**적응적 모멘트 추정**$^{AdaptiveMomentEstimation}$ 을 의미하는 Adam은 모멘텀 최적화와 RMSProp의 아이디어를 \n", "\n", "합친 것.\n", "\n", "
\n", "![eq3.png](./img/eq3.png)\n", "\n", "단계 1, 2, 5는 모멘텀 최적화, RMSProp과 아주 비슷함. \n", "\n", "훈련 초기에는 $m$과 $s$가 0으로 초기화 됨. 따라서 훈련 초기에 0에 치우쳐저 있기 때문에, 단계 3, 4가 훈련 초기에 $m$과 $s$의 값을 증폭시키는 데 도움을 줄 것.\n", "\n", "보통 모멘텀 감쇠 하이퍼파라미터 $\\beta_1 = 0.9$, 스케일 감쇠 하이퍼파라미터 $\\beta_2 = 0.999$로 초기화함.\n", "\n", "사실 Adam이 앞의 두 알고리즘과 같이 적응형 학습률 알고리즘이기 때문에 학습률 하이퍼파라미터 $\\eta$를 튜닝할 필요가 적음(보통 0.001)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_hidden2 = 300\n", "n_hidden3 = 300\n", "n_hidden4 = 100\n", "n_hidden5 = 50\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " hidden3 = my_dense_layer(hidden2, n_hidden3, name=\"hidden3\")\n", " hidden4 = my_dense_layer(hidden3, n_hidden4, name=\"hidden4\")\n", " hidden5 = my_dense_layer(hidden4, n_hidden5, name=\"hidden5\")\n", " logits = my_dense_layer(hidden5, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"): # Adam Optimizer\n", " optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 21회에서 94.460%\n", "34.636[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**CAUTION** 초기에 이 책은 Adam 최적화가 다른 방법보다 빠르고 좋기 때문에 이를 추천했음. 하지만 2017년에 나온 한 논문에서 적응적인 최적화 방법이 일부 데이터셋에서 나쁜 결과를 만든다는 것을 보였음. 연구자들이 이 문제에 대한 답을 찾을 때까지 지금은 모멘텀 최적화나 네스테로프 경사 가속을 사용하는 것이 나을 수 있음.\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "지금까지 논의한 모든 최적화 기법은 **1차 편미분(야코비안$^{Jacobian} $)** 에만 의존함.\n", "\n", "최적화 이론에는 **2차 편미분(헤시안$^{Hessian}$)** 을 기반으로 한 뛰어난 알고리즘이 있음. \n", "\n", "불행히도 이런 알고리즘들은 심층 신경망에 적용하기 매우 어려움. 이런 알고리즘들은 하나의 출력마다 $n$개의 1차 편미분이 아니라 $n^{2}$개의 2차 편미분을 계산해야 하기 때문입니다($n$은 파라미터 수). \n", "\n", "DNN은 전형적으로 수만 개의 파라미터를 가지기 때문에 2차 편미분 최적화 알고리즘은 메모리 용량을 넘어서는 경우가 많고 가능하다고 해도 헤시안 계산은 너무 느림." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### ___※참고 레이어 수가 줄고, 셀의 수는 그대로 일 때___" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Gradient Descent" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 700\n", "n_hidden2 = 350\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " logits = my_dense_layer(hidden2, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate) # Gradient Descent Optimizer\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 9회에서 90.860%\n", "16.743[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### AdaGrad" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 700\n", "n_hidden2 = 350\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " logits = my_dense_layer(hidden2, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.AdagradOptimizer(learning_rate=learning_rate) # AdaGrad Optimizer\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 2회에서 90.880%\n", "11.959[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Adam" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 700\n", "n_hidden2 = 350\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " logits = my_dense_layer(hidden2, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) # AdamOptimizer\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 33회에서 94.560%\n", "41.827[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.3.6 학습률 스케줄링\n", "\n", "학습률을 너무 크게 잡으면 훈련이 실제로 발산할 수 있음. 너무 작게 잡으면 최적점에 수렴하는 시간이 매우 오래 걸림. \n", "\n", "만약 조금 높게 잡으면 처음에는 매우 빠르게 진행 하겠지만 최적점 근처에서는 요동이 심해져 수렴하지 못할 수 있음.\n", "\n", "(이는 AdaGrad, RMSProp, Adam 같은 적응적 학습률 알고리즘이 아닐 경우임. 하지만 이 알고리즘들도 안정이 되려면 시간이 걸림)\n", "\n", "컴퓨팅 자원이 한정적이라면 차선의 솔루션을 만들기 위해 완전히 수렴하기 전에 훈련을 멈추어야 함.\n", "\n", "![img11_8.png](./img/img11_8.png)\n", "\n", "몇 번의 에포크만 신경망을 훈련시키고 학습 곡선을 비교해서 아주 좋은 학습률을 찾을 수 있을지도 모름.\n", "\n", "그러나 일정한 학습률보다 더 나은 방법이 있음. 높은 학습률로 시작하고 학습 속도가 느려질 때 학습률을 낮춘다면 \n", "\n", "최적의 고정 학습률보다 좋은 솔루션에 더 빨리 도달할 수 있음.\n", "\n", "훈련하는 동안 학습률을 감소시키는 전략에는 여러 가지가 있음. 이런 전략을 **학습 스케줄** 이라고 함.\n", "\n", "* **미리 정의된 개별적인 고정 학습률**
\n", " 예를 들어 처음에 $\\eta_0 = 0.1$로 학습률을 지정하고 50 에포크 후에 $\\eta_1 = 0.001$로 바꿈. 이 방법이 잘 작동할 수는 있지만 적절한 학습률과 적당한 시점을 찾으려면 이리저리 바꿔봐야 함.\n", "\n", "\n", "* **성능 기반 스케줄링**
\n", " 매 $N$ 스텝마다 (조기 종료처럼) 검증 오차를 측정하고 오차가 줄어들지 않으면 $\\lambda$ 만큼 학습률을 감소시킴.\n", "\n", "\n", "* **지수 기반 스케줄링**
\n", " 반복 횟수 $t$의 함수 $\\eta(t) = \\eta_{0}10^{-t/r}$로 학습률을 설정함. 이방법이 잘 작동하지만 $\\eta_0$ 와 r을 튜닝해야함. 학습률은 매 $r$ 스텝마다 1/10씩 줄어들 것. \n", "\n", "\n", "* **거듭제곱 기반 스케줄링**
\n", " 학습률을 $\\eta(t) = \\eta_0(1+t/r)^{-c}$으로 설정함. 하이퍼파라미터 $c$는 보통 1로 지정됨. 지수 기반 스케줄링과 비슷하지만 학습률이 훨씬 느리게 감소.\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### tf.train.cosine_decay(learning_rate, global_step, decay_steps, alpha=0.0)\n", "global_step = min(global_step, decay_steps)
\n", "cosine_decay = 0.5 $\\times$ (1 + cos($\\pi$ $\\times$ global_step / decay_steps))
\n", "decayed = (1 - alpha) $\\times$ cosine_decay + alpha
\n", "decayed_learning_rate = learning_rate $\\times$ decayed
\n", "\n", "##### tf.train.exponential_decay(learning_rate, global_step, decay_steps, decay_rate, staircase=False)\n", "decayed_learning_rate = learning_rate $\\times$ decay_rate ^ (global_step / decay_steps)\n", "\n", "### tf.train document : https://www.tensorflow.org/api_docs/python/tf/train/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 11.4 과대적합을 피하기 위한 규제 방법" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.4.1 조기 종료\n", "훈련 세트에 과대적합되는 것을 피하기 위한 좋은 방법 하나는 조기종료임.\n", "\n", "검증 세트의 **성능이 떨어지기 시작할 때** 훈련을 중지시키기만 하면 됨.\n", "\n", "텐서플로로 구현하는 한 가지 방법은 일정한 간격으로(예를 들면 50 스텝마다) 검증 세트로 모델을 평가해서 이전의 \n", "\n", "최고 성능보다 더 나을 경우 이를 최고 성능의 스냅샷으로 저장, 마지막 스냅샷이 저장된 이후 지난 스텝을 카운트해\n", "\n", "서 이 숫자가 어떤 한계점(예를 들면 2,000스텝)을 넘으면 훈련을 중지시킴." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.4.2 $\\\\ℓ_1$과 $\\\\ℓ_2$ 규제\n", "\n", "4장에서 간단한 선형 회귀에 대해 했던 것처럼 $\\\\ℓ_1$과 $\\\\ℓ_2$ 규제를 사용해 신경망의 연결 가중치에 제약을 가할 수 있음(하지만 일반적으로 편향에는 적용하지 않습니다).\n", "\n", "텐서플로를 사용해 이를 구현하는 한 가지 방법은 비용 함수에 적절한 규제항을 추가 하는 것.\n", "\n", "예를 들어 가중치가 W1인 하나의 은닉층과 가중치가 W2인 출력층이 있다면 다음과 같이 $\\\\ℓ_1$ 규제를 적용할 수 있음." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "with tf.name_scope(\"dnn\"): #간단한 DNN 설계\n", " hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name=\"hidden1\")\n", " logits = tf.layers.dense(hidden1, n_outputs, name=\"outputs\")" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "W1 = tf.get_default_graph().get_tensor_by_name(\"hidden1/kernel:0\") # 은닉층 가중치\n", "W2 = tf.get_default_graph().get_tensor_by_name(\"outputs/kernel:0\") # 출력층 가중치\n", "\n", "scale = 0.001 # l1 규제 하이퍼파라미터\n", "# 규제와 관련된 loss 구문\n", "with tf.name_scope(\"loss\"):\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\")\n", " reg_losses = tf.reduce_sum(tf.abs(W1)) + tf.reduce_sum(tf.abs(W2))\n", " loss = tf.add(base_loss, scale * reg_losses, name=\"loss\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.GradientDescentOptimizer(learning_rate)\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()\n", "saver = tf.train.Saver()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.831\n", "1 회 - acc: 0.871\n", "2 회 - acc: 0.8838\n", "3 회 - acc: 0.8934\n", "4 회 - acc: 0.8966\n", "5 회 - acc: 0.8988\n", "6 회 - acc: 0.9016\n", "7 회 - acc: 0.9044\n", "8 회 - acc: 0.9058\n", "9 회 - acc: 0.906\n", "10 회 - acc: 0.9068\n", "11 회 - acc: 0.9054\n", "12 회 - acc: 0.907\n", "13 회 - acc: 0.9084\n", "14 회 - acc: 0.9088\n", "15 회 - acc: 0.9064\n", "16 회 - acc: 0.9066\n", "17 회 - acc: 0.9066\n", "18 회 - acc: 0.9066\n", "19 회 - acc: 0.9052\n" ] } ], "source": [ "n_epochs = 20\n", "batch_size = 200\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " print(epoch, \"회 - acc:\", accuracy_val)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![l1l2_1.png](./img/l1l2_1.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "위 코드는 직접 loss 그래프 내에 l1 규제를 구현했다면, 아래 코드는 tensorflow에서 제공하는 regularizer를 사용함" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_hidden2 = 50\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "# tf.contrib.layers.l1_l2_regularizer(scale_l1=1.0, scale_l2=1.0)\n", "# tf.contrib.layers.l2_regularizer(scale)\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " logits = my_dense_layer(hidden2, n_outputs, activation=None,\n", " name=\"outputs\")" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.GradientDescentOptimizer(learning_rate)\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()\n", "saver = tf.train.Saver()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.8274\n", "1 회 - acc: 0.8766\n", "2 회 - acc: 0.8952\n", "3 회 - acc: 0.9016\n", "4 회 - acc: 0.9082\n", "5 회 - acc: 0.9096\n", "6 회 - acc: 0.9126\n", "7 회 - acc: 0.9154\n", "8 회 - acc: 0.9178\n", "9 회 - acc: 0.919\n", "10 회 - acc: 0.92\n", "11 회 - acc: 0.9224\n", "12 회 - acc: 0.9212\n", "13 회 - acc: 0.9228\n", "14 회 - acc: 0.9224\n", "15 회 - acc: 0.9216\n", "16 회 - acc: 0.9218\n", "17 회 - acc: 0.9228\n", "18 회 - acc: 0.9216\n", "19 회 - acc: 0.9214\n" ] } ], "source": [ "n_epochs = 20\n", "batch_size = 200\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " print(epoch, \"회 - acc:\", accuracy_val)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![l1l2_2.png](./img/l1l2_2.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![l1l2_3.png](./img/l1l2_3.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.4.3 드롭아웃\n", "\n", "심층 신경망에서 가장 인기 있는 규제 방법은 드롭아웃$^{dropdout}$(제외)임.\n", "\n", "매 훈련 스텝에서 각 뉴런(입력 뉴런은 포함, 출력 뉴런은 제외)은 임시적으로 드롭아웃될 확률$p$를 가짐.\n", "\n", "즉, 이번 훈련 스텝에는 완전히 무시되지만 다음 스텝에는 활성화될 수 있음.\n", "\n", "![img11_9.png](./img/img11_9.png)\n", "\n", "하이퍼파라미터 $p$를 드롭아웃 비율$^{DropoutRate}$이라고 하며 보통 50%로 지정함.\n", "\n", "훈련이 끝난 후에는 뉴런에 더 이상 드롭아웃을 적용하지 않음." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "training = tf.placeholder_with_default(False, shape=(), name='training')\n", "\n", "dropout_rate = 0.5 # == 1 - keep_prob\n", "X_drop = tf.layers.dropout(X, dropout_rate, training=training)\n", "# 입력의 드롭아웃\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = tf.layers.dense(X_drop, n_hidden1, activation=tf.nn.relu,\n", " name=\"hidden1\")\n", " hidden1_drop = tf.layers.dropout(hidden1, dropout_rate, training=training)\n", " # hidden layer 1의 드롭아웃 \n", " hidden2 = tf.layers.dense(hidden1_drop, n_hidden2, activation=tf.nn.relu,\n", " name=\"hidden2\")\n", " hidden2_drop = tf.layers.dropout(hidden2, dropout_rate, training=training)\n", " # hidden layer 2의 드롭아웃\n", " logits = tf.layers.dense(hidden2_drop, n_outputs, name=\"outputs\")" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "with tf.name_scope(\"loss\"):\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", " loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9)\n", " training_op = optimizer.minimize(loss) \n", "\n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))\n", " \n", "init = tf.global_variables_initializer()\n", "saver = tf.train.Saver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![dropout1](./img/dropout1.png)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.9264\n", "1 회 - acc: 0.9452\n", "2 회 - acc: 0.9486\n", "3 회 - acc: 0.9562\n", "4 회 - acc: 0.9628\n", "5 회 - acc: 0.9596\n", "6 회 - acc: 0.963\n", "7 회 - acc: 0.9672\n", "8 회 - acc: 0.9674\n", "9 회 - acc: 0.9702\n", "10 회 - acc: 0.9676\n", "11 회 - acc: 0.9706\n", "12 회 - acc: 0.97\n", "13 회 - acc: 0.9708\n", "14 회 - acc: 0.972\n", "15 회 - acc: 0.9692\n", "16 회 - acc: 0.9724\n", "17 회 - acc: 0.9724\n", "18 회 - acc: 0.9724\n", "19 회 - acc: 0.9726\n" ] } ], "source": [ "n_epochs = 20\n", "batch_size = 50\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={training: True, X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " print(epoch, \"회 - acc:\", accuracy_val)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.4.4 맥스-노름 규제\n", "\n", "각각의 뉴런에 대해 입력의 연결 가중치 $w$가 $ \\left \\| w\\right \\|_2 \\leq r$ 이 되도록 제한.\n", "\n", "$r$은 맥스-노름 하이퍼파라미터이고 $ \\left \\| \\cdot \\right \\|_2$는 $\\\\ℓ_2$ 노름을 나타냄.\n", "\n", "일반적으로 매 훈련 스텝이 끝나고 $ \\left \\| w \\right \\|_2$ 를 계산한 다음 $w$를 클리핑($w ^{<-} w{\\frac{r}{\\left \\| w \\right \\|_2}} $)함.\n", "\n", "$r$을 줄이면 규제의 정도가 커져 과대적합을 감소시킴.\n", "\n", "맥스-노름 규제는(배치 정규화를 사용하지 않았을 때) 그래디언트 감소/폭주 문제를 완화하는 데 도움을 줌.\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28\n", "n_hidden1 = 300\n", "n_hidden2 = 50\n", "n_outputs = 10\n", "\n", "learning_rate = 0.01\n", "momentum = 0.9\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name=\"hidden1\")\n", " hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name=\"hidden2\")\n", " logits = tf.layers.dense(hidden2, n_outputs, name=\"outputs\")\n", "\n", "with tf.name_scope(\"loss\"):\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", " loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)\n", " training_op = optimizer.minimize(loss) \n", "\n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "threshold = 1.0\n", "weights = tf.get_default_graph().get_tensor_by_name(\"hidden1/kernel:0\") # 그래프에서 weights 핸들을 가져옴\n", "clipped_weights = tf.clip_by_norm(weights, clip_norm=threshold, axes=1) # clip by norm으로 클리핑하는 연산 구연\n", "clip_weights = tf.assign(weights, clipped_weights) # weights에 clipped_weights 대입" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "# 2 번째 layer에 대한 구문\n", "weights2 = tf.get_default_graph().get_tensor_by_name(\"hidden2/kernel:0\")\n", "clipped_weights2 = tf.clip_by_norm(weights2, clip_norm=threshold, axes=1)\n", "clip_weights2 = tf.assign(weights2, clipped_weights2)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "init = tf.global_variables_initializer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![maxnorm1.png](./img/maxnorm1.png)\n", "![maxnorm2.png](./img/maxnorm2.png)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.9568\n", "1 회 - acc: 0.9696\n", "2 회 - acc: 0.9714\n", "3 회 - acc: 0.9776\n", "4 회 - acc: 0.978\n", "5 회 - acc: 0.9786\n", "6 회 - acc: 0.9822\n", "7 회 - acc: 0.9814\n", "8 회 - acc: 0.9818\n", "9 회 - acc: 0.9826\n", "10 회 - acc: 0.9822\n", "11 회 - acc: 0.9852\n", "12 회 - acc: 0.9816\n", "13 회 - acc: 0.9838\n", "14 회 - acc: 0.984\n", "15 회 - acc: 0.9844\n", "16 회 - acc: 0.9838\n", "17 회 - acc: 0.9844\n", "18 회 - acc: 0.9844\n", "19 회 - acc: 0.9844\n" ] } ], "source": [ "n_epochs = 20\n", "batch_size = 50\n", "\n", "with tf.Session() as sess: \n", " init.run() \n", " for epoch in range(n_epochs): \n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size): \n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " clip_weights.eval()\n", " clip_weights2.eval() \n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " print(epoch, \"회 - acc:\", accuracy_val)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "max norm의 함수화" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "# 그래프 생성 함수\n", "def max_norm_regularizer(threshold, axes=1, name=\"max_norm\",\n", " collection=\"max_norm\"):\n", " def max_norm(weights):\n", " clipped = tf.clip_by_norm(weights, clip_norm=threshold, axes=axes)\n", " clip_weights = tf.assign(weights, clipped, name=name)\n", " tf.add_to_collection(collection, clip_weights)\n", " return None # 규제 손실을 위한 항이 없습니다\n", " return max_norm" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28\n", "n_hidden1 = 300\n", "n_hidden2 = 50\n", "n_outputs = 10\n", "\n", "learning_rate = 0.01\n", "momentum = 0.9\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "max_norm_reg = max_norm_regularizer(threshold=1.0)\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,\n", " kernel_regularizer=max_norm_reg, name=\"hidden1\")\n", " hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,\n", " kernel_regularizer=max_norm_reg, name=\"hidden2\")\n", " logits = tf.layers.dense(hidden2, n_outputs, name=\"outputs\")" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "with tf.name_scope(\"loss\"):\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", " loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)\n", " training_op = optimizer.minimize(loss) \n", "\n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))\n", "\n", "init = tf.global_variables_initializer()\n", "saver = tf.train.Saver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![maxnorm3.png](./img/maxnorm3.png)\n", "![maxnorm4.png](./img/maxnorm4.png)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.9562\n", "1 회 - acc: 0.971\n", "2 회 - acc: 0.9732\n", "3 회 - acc: 0.9744\n", "4 회 - acc: 0.975\n", "5 회 - acc: 0.9778\n", "6 회 - acc: 0.9798\n", "7 회 - acc: 0.9804\n", "8 회 - acc: 0.9816\n", "9 회 - acc: 0.9828\n", "10 회 - acc: 0.982\n", "11 회 - acc: 0.9824\n", "12 회 - acc: 0.9804\n", "13 회 - acc: 0.982\n", "14 회 - acc: 0.9824\n", "15 회 - acc: 0.9812\n", "16 회 - acc: 0.982\n", "17 회 - acc: 0.9828\n", "18 회 - acc: 0.982\n", "19 회 - acc: 0.9824\n" ] } ], "source": [ "n_epochs = 20\n", "batch_size = 50\n", "\n", "clip_all_weights = tf.get_collection(\"max_norm\")\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " sess.run(clip_all_weights)\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid}) \n", " print(epoch, \"회 - acc:\", accuracy_val) \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 11.4.5 데이터 증식\n", "데이터 증식$^{DataAugmentation}$ 은 기존의 데이터에서 새로운 데이터를 생성해 인공적으로 훈련 세트의 크기를 늘림. \n", "\n", "→ 과대적합을 줄이므로 규제의 방도로 사용\n", "\n", "실제와 같은 훈련 샘플 생성하는 것. 이상적으로는 사람이 인공적으로 만든 샘플인지 아닌지 구분할 수 없어야 함.\n", "\n", "단순히 백색소음$^{WhiteNoise}$을 추가하는 것은 도움이 안 됨. 즉, 적용한 수정사항이 학습 가능한 것이어야 함.\n", "\n", "ex) 버섯 이미지를 구분하는 모델\n", "\n", "훈련 세트에 있는 모든 이미지를 다양하게 조금씩 이동, 회전하거나 크기를 바꿔서 만든 이미지를 훈련세트에 추가.\n", "\n", "![img11_10.png](./img/img11_10.png)\n", "\n", "이렇게 하면 모델이 사진에 있는 버섯의 위치, 각도, 크기에 덜 민감해짐.\n", "\n", "빛에 대해서도 마찬가지로 명암을 달리하여 비슷하게 여러 이미지를 생성.\n", "\n", "버섯이 대칭이라고 가정하여 이미지를 수평으로 뒤집을 수도 있음. \n", "\n", "이런 변환들을 결합하면 훈련 세트의 크기를 늘릴 수 있음.\n", "\n", "훈련하는 동안 동적으로 훈련 샘플을 생성하는 것이 저장 공간이나 네트워크 대역폭 측면에서 좋음.\n", "\n", "텐서플로는 밝기, 명암, 채도, 색조 조정은 물론\n", "\n", "위치 변경$^{transpose}$, 이동$^{shift}$, 회전$^{rotate}$, 크기 조절$^{resize}$, 뒤집기$^{flip}$, 자르기$^{crop}$등과 같은 이미지 조작 연산을 제공(자세한 내용은 API 참고).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 11.5 실용적 가이드라인\n", "\n", "![table1.png](./img/table1.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "다음과 같은 경우에는 위 기본 설정을 바꿀 필요가 있음.\n", "\n", "* 좋은 학습률을 찾을 수 없다면 (수렴이 너무 느릴 때 학습속도를 올리면 수렴은 빨라지지만 네트워크의 정확도는 최적화가 덜 되므로) 지수 감소 같은 학습 스케줄을 추가해 볼 수 있음.\n", "\n", "\n", "* 훈련 세트가 너무 작다면 데이터 증식을 수행할 수 있음.\n", "\n", "\n", "* 희소 모델이 필요하면 $\\\\ℓ_1$규제를 추가함(또는 훈련이 끝난 뒤 작은 가중치를 0으로 만듦). 만약 더욱 희박한 희소 모델이 필요하면 네스테로프 가속 경사나 Adam 옵티마이저 대신 $\\\\ℓ_1$규제와 함께 FTRL 알고리즘 사용할 수 있음.\n", "\n", "\n", "* 실행 속도가 아주 빠른 모델을 필요로 하면 배치 정규화를 빼고 ELU 활성화 함수를 LeakyReLU로 바꿈. 희소 모델을 만드는 것도 도움이 됨." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "아래 코드는 앞서 5개의 레이어를 가지는 DNN에 대한 고속 옵티마이저 비교에서 사용한 그래프를 구성하고, 옵티마이저는 네스테로프 가속 경사를 사용해 학습 속도를 비교함." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 300\n", "n_hidden2 = 300\n", "n_hidden3 = 300\n", "n_hidden4 = 100\n", "n_hidden5 = 50\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " hidden2 = my_dense_layer(hidden1, n_hidden2, name=\"hidden2\")\n", " hidden3 = my_dense_layer(hidden2, n_hidden3, name=\"hidden3\")\n", " hidden4 = my_dense_layer(hidden3, n_hidden4, name=\"hidden4\")\n", " hidden5 = my_dense_layer(hidden4, n_hidden5, name=\"hidden5\")\n", " logits = my_dense_layer(hidden5, n_outputs, activation=None,\n", " name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"): \n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits) \n", " base_loss = tf.reduce_mean(xentropy, name=\"avg_xentropy\") \n", " reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)\n", " loss = tf.add_n([base_loss] + reg_losses, name=\"loss\")\n", " \n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "learning_rate = 0.01\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9, use_nesterov=True) \n", " # Nesterov\n", " training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "조기 종료!\n", "Best Accuracy : 26회에서 95.940%\n", "37.965[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " \n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 연습문제\n", "\n", "### 1. He 초기화를 사용하여 무작위로 선택한 값이라면 모든 가중치를 같은 값으로 초기화해도 괜찮을까요?\n", "\n", "\n", "* 아니오. 모든 가중치는 독립적으로 샘플링 되어야 함. 즉, 같은 초기값을 가지면 안 됨.\n", "\n", " 가중치를 무작위로 샘플링하는 중요한 목적은 대칭성을 피하기 위함.\n", "\n", " 모든 가중치가 0이 아니더라도 같은 초깃값을 가지면 대칭성이 깨지지 않고 역전파도 이를 해결 할 수 없음.\n", "\n", " 구체적으로, 어떤 층에 있는 모든 뉴런이 항상 같은 가중치를 가지게 되는데, 이는 층마다 하나의 뉴런이 있는 것과 같으므로 수렴하는데 오래 걸림\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. 편향을 0으로 초기화해도 괜찮을까요?\n", "\n", " * 편향을 0으로 초기화 하는 것은 아무상관 없음. 또는 편향을 가중치처럼 초기화해도 괜찮음." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. ReLU보다 ELU활성화 함수가 나은 세 가지는 무엇인가요?\n", " * ELU는 음수를 받을 수 있어서 뉴런의 평균 출력이 ReLU 활성화 함수보다 일반적으로 0에 더 가까움.\n", " 이것은 그래디언트 소실 문제를 완화시켜줌. \n", " \n", "\n", " * 도함수의 값은 항상 0이 아니기 때문에 ReLU 유닛에서 일어나는 죽은 뉴런 현상을 피할 수 있음.\n", " \n", " \n", " * ReLU의 기울기는 z = 0일 때 0에서 1로 급격히 바뀌는 반면, ELU는 어디에서나 도함수가 매끄럽게 바뀜. 이런 급격한 변화는 z = 0일 때 진동을 발생시키므로 경사 하강법의 속도를 느리게 만듦.\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " \"func_ELU.png\"\n", " ![func_4.png](./img/func_4.png)\n", " \"func_LReLU.png\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. 어떤 경우에 ELU, LeakyReLU(또는 그 변종), ReLU, tanh, logistic, softmax와 같은 활성화 함수를 사용해야 하나요?\n", "* ELU 함수가 기본값으로 좋음. 가능한 빠른 신경망을 원한다면 대신 LeakyReLU의 변종을 사용할 수 있음(예를 들면 기본 하이퍼파라미터를 사용한 LeakyReLU). 일반적으로 ELU와 LeakyReLU의 성능이 더 뛰어남에도 ReLU 활성화 함수가 간단하기 때문에 많은 사람이 선호. 그러나 어떤 경우엔 정확히 0을 출력하는 ReLU 활성화 함수의 기능이 유용할 수 있음(15장 참조, 잡음제거 오토인코더). 소프트맥스 활성화 함수는 상호 배타적인 클래스에 대한 확률을 출력하는 출력층에 사용됨. 하지만 그 외에 은닉층에는 거의 사용되지 않음." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5. MomenumOptimizer를 사용할 때 momentum 하이퍼파라미터를 너무 1에 가깝게 하면 (예를 들면 0.99999) 어떤 일이 일어날까요?\n", "\n", "* 알고리즘이 전역 최적점 방향으로 빠르게 진행되겠지만 모멘텀 때문에 최솟값을 지나치게 될 것임. 그런 다음 느려져서 되돌아오고, 다시 가속되어 또 지나치게 되는식. 이런식으로 수렴하기 전에 여러 번 진동하기 때문에 작은 momentum 값을 사용했을 때보다 전반적으로 수렴하는 데 훨씬 오래 걸릴 것.\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 100\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "learning_ratee = 0.01\n", "momentum = 0.9 # 모멘텀\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " logits = my_dense_layer(hidden1, n_outputs, activation=None, name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"):\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", " loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)\n", " training_op = optimizer.minimize(loss) \n", "\n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.9122\n", "1 회 - acc: 0.9258\n", "2 회 - acc: 0.9362\n", "3 회 - acc: 0.9432\n", "4 회 - acc: 0.9494\n", "5 회 - acc: 0.9518\n", "6 회 - acc: 0.9572\n", "7 회 - acc: 0.9584\n", "8 회 - acc: 0.9608\n", "9 회 - acc: 0.962\n", "10 회 - acc: 0.9642\n", "11 회 - acc: 0.9644\n", "12 회 - acc: 0.9676\n", "13 회 - acc: 0.9672\n", "14 회 - acc: 0.9682\n", "15 회 - acc: 0.9686\n", "16 회 - acc: 0.9698\n", "17 회 - acc: 0.9704\n", "18 회 - acc: 0.972\n", "19 회 - acc: 0.972\n", "20 회 - acc: 0.9722\n", "21 회 - acc: 0.9728\n", "22 회 - acc: 0.9736\n", "23 회 - acc: 0.9732\n", "24 회 - acc: 0.973\n", "25 회 - acc: 0.9752\n", "26 회 - acc: 0.9748\n", "27 회 - acc: 0.9742\n", "28 회 - acc: 0.9746\n", "29 회 - acc: 0.9748\n", "30 회 - acc: 0.9742\n", "31 회 - acc: 0.973\n", "32 회 - acc: 0.9748\n", "33 회 - acc: 0.974\n", "34 회 - acc: 0.9748\n", "35 회 - acc: 0.9744\n", "36 회 - acc: 0.9744\n", "조기 종료!\n", "Best Accuracy : 25회에서 97.520%\n", "18.360[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " print(epoch, \"회 - acc:\", accuracy_val)\n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_hidden1 = 100\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "learning_ratee = 0.01\n", "momentum = 0.99999999 # 모멘텀\n", "scale = 0.001\n", "\n", "my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu,\n", " kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))\n", "\n", "with tf.name_scope(\"dnn\"):\n", " hidden1 = my_dense_layer(X, n_hidden1, name=\"hidden1\")\n", " logits = my_dense_layer(hidden1, n_outputs, activation=None, name=\"outputs\")\n", " \n", "with tf.name_scope(\"loss\"):\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", " loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "with tf.name_scope(\"train\"):\n", " optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)\n", " training_op = optimizer.minimize(loss) \n", "\n", "with tf.name_scope(\"eval\"):\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))\n", "\n", "init = tf.global_variables_initializer()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 회 - acc: 0.8868\n", "1 회 - acc: 0.8706\n", "2 회 - acc: 0.8526\n", "3 회 - acc: 0.841\n", "4 회 - acc: 0.839\n", "5 회 - acc: 0.8442\n", "6 회 - acc: 0.8098\n", "7 회 - acc: 0.768\n", "8 회 - acc: 0.819\n", "9 회 - acc: 0.7848\n", "10 회 - acc: 0.7548\n", "11 회 - acc: 0.7182\n", "조기 종료!\n", "Best Accuracy : 0회에서 88.680%\n", "6.072[s]\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 200\n", "\n", "max_checks_without_progress = 10\n", "checks_without_progress = 0\n", "best_acc_val = 0\n", "best_epoch = n_epochs - 1\n", "\n", "tic = time.time()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})\n", " print(epoch, \"회 - acc:\", accuracy_val)\n", " if accuracy_val > best_acc_val:\n", " best_acc_val = accuracy_val\n", " best_epoch = epoch\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 10회 이상 더 작으면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(f'Best Accuracy : {best_epoch:d}회에서 {best_acc_val * 100:.3f}%')\n", " \n", "\n", "toc = time.time()\n", "\n", "print(f'{(toc - tic):.3f}[s]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6. 희소 모델을 만들 수 있는 세가지 방법은 무엇인가요?\n", "* 희소모델(즉, 대부분의 가중치가 0인 모델)을 만드는 첫 번째 방법은 평범하게 모델을 훈련시키고 작은 가중치를 0으로 만드는 것.\n", "두 번째 방법은 훈련하는 동안 옵티마이저에 희소한 모델을 만들도록 L1 규제를 사용하는 것.\n", "세 번째 방법은 텐서플로의 RTROptimizer를 사용항 쌍대평균과 L1 규제를 연결하는 것." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7. 드롭아웃이 훈련 속도를 느리게 만드나요? 추론 inference (즉, 새로운 샘플에 대한 예측을 만드는 것)도 느리게 만드나요?\n", "\n", "* 예. 드롭아웃은 일반적으로 대략 두 배 정도 훈련 속도를 느리게 만듬(보통 드롭아웃 비율을 0.5로 하기 때문). 그러나 드롭아웃은 훈련할 때만 적용되므로 추론에는 영향을 미치지 않음." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 8. 딥러닝\n", "#### a. He 초기화와 ELU 활성화 함수를 사용하여 각각 뉴런이 100개인 은닉층 다섯 개를 가진 DNN을 만드세요." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "he_init = tf.variance_scaling_initializer() # He 초기화\n", "\n", "def dnn(inputs, n_hidden_layers=5, n_neurons=100, name=None,\n", " activation=tf.nn.elu, initializer=he_init): # 뉴런 100개, ELU 활성함수, He 초기화\n", " with tf.variable_scope(name, \"dnn\"): # name scope : dnn\n", " for layer in range(n_hidden_layers): # default 5 layers\n", " inputs = tf.layers.dense(inputs, n_neurons, activation=activation,\n", " kernel_initializer=initializer,\n", " name=\"hidden%d\" % (layer + 1))\n", " return inputs" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "n_inputs = 28 * 28 # MNIST\n", "n_outputs = 5\n", "\n", "reset_graph() # 그래프 초기화\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "dnn_outputs = dnn(X) # 그래프 생성\n", "\n", "logits = tf.layers.dense(dnn_outputs, n_outputs, kernel_initializer=he_init, name=\"logits\")\n", "Y_proba = tf.nn.softmax(logits, name=\"Y_proba\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex8-1.png](./img/ex8-1.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### b. Adam 최적화와 조기 종료를 사용하여 MNIST 데이터셋에 훈련시키되 0에서 4까지의 숫자만 사용하세요. 다음 연습문제에서 5에서 9까지의 숫자에 대해 전이 학습을 사용할 것입니다. 출력층은 다섯 개의 뉴런에 소프트맥스 함수를 사용합니다. 나중에 재사용할 수 있도록 항상 일정한 간격으로 체크포인트와 최종 모델을 저장하세요." ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "# 훈련에 필요한 구성 요소 생성\n", "\n", "learning_rate = 0.01\n", "\n", "xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", "loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "optimizer = tf.train.AdamOptimizer(learning_rate) # Adam Optimizer\n", "training_op = optimizer.minimize(loss, name=\"training_op\")\n", "\n", "correct = tf.nn.in_top_k(logits, y, 1) # tf.nn.in_top_k( predictions, targets, k, name=None )\n", " #https://www.tensorflow.org/api_docs/python/tf/math/in_top_k\n", "accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "init = tf.global_variables_initializer()\n", "saver = tf.train.Saver()" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "# 데이터 셋 준비\n", "\n", "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()\n", "X_train = X_train.astype(np.float32).reshape(-1, 28*28) / 255.0\n", "X_test = X_test.astype(np.float32).reshape(-1, 28*28) / 255.0\n", "y_train = y_train.astype(np.int32)\n", "y_test = y_test.astype(np.int32)\n", "X_valid, X_train = X_train[:5000], X_train[5000:]\n", "y_valid, y_train = y_train[:5000], y_train[5000:]" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "X_train1 = X_train[y_train < 5]\n", "y_train1 = y_train[y_train < 5]\n", "X_valid1 = X_valid[y_valid < 5]\n", "y_valid1 = y_valid[y_valid < 5]\n", "X_test1 = X_test[y_test < 5]\n", "y_test1 = y_test[y_test < 5]\n", "\n", "# 레이블이 5 미만인 레이블을 가지는 데이터 셋만 추출하여 사용." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.217492\t최선의 손실: 0.217492\t정확도: 96.01%\n", "1\t검증 세트 손실: 0.170349\t최선의 손실: 0.170349\t정확도: 96.52%\n", "2\t검증 세트 손실: 0.091266\t최선의 손실: 0.091266\t정확도: 97.58%\n", "3\t검증 세트 손실: 0.327799\t최선의 손실: 0.091266\t정확도: 94.25%\n", "4\t검증 세트 손실: 0.162250\t최선의 손실: 0.091266\t정확도: 97.11%\n", "5\t검증 세트 손실: 0.684595\t최선의 손실: 0.091266\t정확도: 75.84%\n", "6\t검증 세트 손실: 0.824483\t최선의 손실: 0.091266\t정확도: 57.43%\n", "7\t검증 세트 손실: 0.854250\t최선의 손실: 0.091266\t정확도: 60.95%\n", "8\t검증 세트 손실: 0.724734\t최선의 손실: 0.091266\t정확도: 68.22%\n", "9\t검증 세트 손실: 1.528036\t최선의 손실: 0.091266\t정확도: 27.52%\n", "10\t검증 세트 손실: 1.625366\t최선의 손실: 0.091266\t정확도: 22.01%\n", "11\t검증 세트 손실: 1.798194\t최선의 손실: 0.091266\t정확도: 22.01%\n", "12\t검증 세트 손실: 1.641676\t최선의 손실: 0.091266\t정확도: 18.73%\n", "13\t검증 세트 손실: 1.615793\t최선의 손실: 0.091266\t정확도: 19.27%\n", "14\t검증 세트 손실: 1.613873\t최선의 손실: 0.091266\t정확도: 20.91%\n", "15\t검증 세트 손실: 1.647734\t최선의 손실: 0.091266\t정확도: 20.91%\n", "16\t검증 세트 손실: 1.717600\t최선의 손실: 0.091266\t정확도: 19.08%\n", "17\t검증 세트 손실: 1.682581\t최선의 손실: 0.091266\t정확도: 19.27%\n", "18\t검증 세트 손실: 1.675134\t최선의 손실: 0.091266\t정확도: 18.73%\n", "19\t검증 세트 손실: 1.645765\t최선의 손실: 0.091266\t정확도: 19.08%\n", "20\t검증 세트 손실: 1.722328\t최선의 손실: 0.091266\t정확도: 22.01%\n", "21\t검증 세트 손실: 1.656413\t최선의 손실: 0.091266\t정확도: 22.01%\n", "22\t검증 세트 손실: 1.643529\t최선의 손실: 0.091266\t정확도: 18.73%\n", "조기 종료!\n", "INFO:tensorflow:Restoring parameters from ./my_mnist_model_0_to_4.ckpt\n", "최종 테스트 정확도: 97.88%\n" ] } ], "source": [ "n_epochs = 1000\n", "batch_size = 20\n", "\n", "max_checks_without_progress = 20\n", "checks_without_progress = 0\n", "best_loss = np.infty\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", "\n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train1))\n", " # 학습 부분\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train1) // batch_size):\n", " X_batch, y_batch = X_train1[rnd_indices], y_train1[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " # 평가 부분\n", " loss_val, acc_val = sess.run([loss, accuracy], feed_dict={X: X_valid1, y: y_valid1})\n", " # 새로 얻은 loss_val로 best_loss와 비교\n", " if loss_val < best_loss:\n", " save_path = saver.save(sess, \"./my_mnist_model_0_to_4.ckpt\")\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress: # 20회 이상 더 크면 조기종료\n", " print(\"조기 종료!\")\n", " break\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", "\n", "with tf.Session() as sess:\n", " saver.restore(sess, \"./my_mnist_model_0_to_4.ckpt\")\n", " acc_test = accuracy.eval(feed_dict={X: X_test1, y: y_test1})\n", " print(\"최종 테스트 정확도: {:.2f}%\".format(acc_test * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex8-2.png](./img/ex8-2.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### c. 교차 검증을 사용하여 하이퍼파라미터를 튜닝하고 얼마의 성능을 달성할 수 있는지 확인해보세요." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "하이퍼파라미터 튜닝을 하기 위해 사이킷런의 `RandomizedSearchCV` 클래스와 호환되는 `DNNClassifier` 클래스를 만듭니다. 이 클래스의 핵심은 다음과 같습니다:\n", "* `__init__()` 메서드(생성자)는 각 하이퍼파라미터에 대한 인스턴스 변수를 만듭니다.\n", "* `fit()` 메서드는 그래프를 만들고 세션을 시작해 모델을 훈련시킵니다:\n", " * `_build_graph()` 메서드를 호출해 그래프를 만듭니다(앞서 만든 그래프와 비슷합니다). 그래프가 만들어지면 다른 메서드에서 접근할 수 있도록 중요한 연산은 모두 인스턴스 변수로 저장합니다.\n", " * `_dnn()` 메서드는 위의 `dnn()` 함수와 비슷하게 은닉층을 만들고 배치 정규화와 드롭아웃을 추가합니다(다음 연습문제를 위해).\n", " * `fit()` 메서드에는 검증 세트(`X_valid`와 `y_valid`)가 주어지면 조기 종료를 실행합니다. 여기에서는 디스키 대신 메모리에 최상의 모델을 저장합니다. 이 함수는 그래프의 모든 변수와 값을 가져오기 위해 `_get_model_params()` 함수를 사용하고 (최상의 모델에서) 변수 값을 복원하기 위해 `_restore_model_params()` 함수를 사용합니다. 이런 방식은 훈련 속도를 높여 줍니다.\n", " * `fit()` 메서드가 모델 훈련을 마치면 예측을 만들기 위해 모델을 저장하고 복원하는 대신에 빠르게 예측을 만들기 위해 그대로 세션을 열어 둡니다. `close_session()` 메서드를 호출해 세션을 닫을 수 있습니다.\n", " * `predict_proba()` 메서드는 훈련 모델을 사용해 클래스 확률을 예측합니다.\n", " * `predict()` 메서드는 `predict_proba()`를 호출해 각 샘플에 대해 가장 높은 확률을 가진 클래스를 반환합니다." ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "from sklearn.base import BaseEstimator, ClassifierMixin\n", "from sklearn.exceptions import NotFittedError\n", "\n", "class DNNClassifier(BaseEstimator, ClassifierMixin):\n", " def __init__(self, n_hidden_layers=5, n_neurons=100, optimizer_class=tf.train.AdamOptimizer,\n", " learning_rate=0.01, batch_size=20, activation=tf.nn.elu, initializer=he_init,\n", " batch_norm_momentum=None, dropout_rate=None, random_state=None):\n", " \"\"\"모든 하이퍼파파미터를 저장하는 것으로 DNNClassifier를 초기화합니다.\"\"\"\n", " self.n_hidden_layers = n_hidden_layers\n", " self.n_neurons = n_neurons\n", " self.optimizer_class = optimizer_class\n", " self.learning_rate = learning_rate\n", " self.batch_size = batch_size\n", " self.activation = activation\n", " self.initializer = initializer\n", " self.batch_norm_momentum = batch_norm_momentum\n", " self.dropout_rate = dropout_rate\n", " self.random_state = random_state\n", " self._session = None\n", "\n", " def _dnn(self, inputs):\n", " \"\"\"배치 정규화와 드롭아웃 기능을 넣어 은닉층을 구성합니다.\"\"\"\n", " for layer in range(self.n_hidden_layers):\n", " if self.dropout_rate: # dropout_rate가 존재하면 dropout 추가\n", " inputs = tf.layers.dropout(inputs, self.dropout_rate, training=self._training)\n", " inputs = tf.layers.dense(inputs, self.n_neurons,\n", " kernel_initializer=self.initializer,\n", " name=\"hidden%d\" % (layer + 1))\n", " if self.batch_norm_momentum: # batch_norm_momentum이 존재하면 정규화 추가\n", " inputs = tf.layers.batch_normalization(inputs, momentum=self.batch_norm_momentum,\n", " training=self._training)\n", " inputs = self.activation(inputs, name=\"hidden%d_out\" % (layer + 1))\n", " return inputs\n", "\n", " def _build_graph(self, n_inputs, n_outputs):\n", " \"\"\"이전과 동일한 모델을 만듭니다.\"\"\"\n", " if self.random_state is not None:\n", " tf.set_random_seed(self.random_state)\n", " np.random.seed(self.random_state)\n", "\n", " X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", " y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", " if self.batch_norm_momentum or self.dropout_rate:\n", " self._training = tf.placeholder_with_default(False, shape=(), name='training')\n", " else:\n", " self._training = None\n", "\n", " dnn_outputs = self._dnn(X)\n", "\n", " logits = tf.layers.dense(dnn_outputs, n_outputs, kernel_initializer=he_init, name=\"logits\")\n", " Y_proba = tf.nn.softmax(logits, name=\"Y_proba\")\n", "\n", " xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y,\n", " logits=logits)\n", " loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", " optimizer = self.optimizer_class(learning_rate=self.learning_rate)\n", " training_op = optimizer.minimize(loss)\n", "\n", " correct = tf.nn.in_top_k(logits, y, 1)\n", " accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", " init = tf.global_variables_initializer()\n", " saver = tf.train.Saver()\n", "\n", " # 중요한 연산은 인스턴스 변수로 저장하여 참조하기 쉽게 합니다.\n", " self._X, self._y = X, y\n", " self._Y_proba, self._loss = Y_proba, loss\n", " self._training_op, self._accuracy = training_op, accuracy\n", " self._init, self._saver = init, saver\n", "\n", " def close_session(self):\n", " if self._session:\n", " self._session.close()\n", "\n", " def _get_model_params(self):\n", " \"\"\"모든 변수 값을 가져옵니다 (조기 종료를 위해 사용하며 디스크에 저장하는 것보다 빠릅니다)\"\"\"\n", " with self._graph.as_default():\n", " gvars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)\n", " return {gvar.op.name: value for gvar, value in zip(gvars, self._session.run(gvars))}\n", "\n", " def _restore_model_params(self, model_params):\n", " \"\"\"모든 변수를 주어진 값으로 설정합니다 (조기 종료를 위해 사용하며 디스크에 저장하는 것보다 빠릅니다)\"\"\"\n", " gvar_names = list(model_params.keys())\n", " assign_ops = {gvar_name: self._graph.get_operation_by_name(gvar_name + \"/Assign\")\n", " for gvar_name in gvar_names}\n", " init_values = {gvar_name: assign_op.inputs[1] for gvar_name, assign_op in assign_ops.items()}\n", " feed_dict = {init_values[gvar_name]: model_params[gvar_name] for gvar_name in gvar_names}\n", " self._session.run(assign_ops, feed_dict=feed_dict)\n", "\n", " def fit(self, X, y, n_epochs=100, X_valid=None, y_valid=None):\n", " \"\"\"훈련 세트에 모델을 훈련시킵니다. X_valid와 y_valid가 주어지면 조기 종료를 적용합니다.\"\"\"\n", " self.close_session()\n", "\n", " # 훈련 세트로부터 n_inputs와 n_outputs를 구합니다.\n", " n_inputs = X.shape[1]\n", " self.classes_ = np.unique(y)\n", " n_outputs = len(self.classes_)\n", " \n", " # 레이블 벡터를 정렬된 클래스 인덱스 벡터로 변환합니다.\n", " # 0부터 n_outputs - 1까지의 정수를 담고 있게 됩니다.\n", " # 예를 들어, y가 [8, 8, 9, 5, 7, 6, 6, 6]이면 \n", " # 정렬된 클래스 레이블(self.classes_)은 [5, 6, 7, 8, 9]가 되고\n", " # 레이블 벡터는 [3, 3, 4, 0, 2, 1, 1, 1]로 변환됩니다.\n", " self.class_to_index_ = {label: index\n", " for index, label in enumerate(self.classes_)}\n", " y = np.array([self.class_to_index_[label]\n", " for label in y], dtype=np.int32)\n", " \n", " self._graph = tf.Graph()\n", " with self._graph.as_default():\n", " self._build_graph(n_inputs, n_outputs)\n", " # 배치 정규화를 위한 추가 연산\n", " extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)\n", "\n", " # 조기 종료를 위해\n", " max_checks_without_progress = 20\n", " checks_without_progress = 0\n", " best_loss = np.infty\n", " best_params = None\n", " \n", " # 이제 모델을 훈련합니다!\n", " self._session = tf.Session(graph=self._graph)\n", " \n", " # log write ready\n", " graph_writer = tf.summary.FileWriter(\"graph_logs\", self._session.graph)\n", " # log write close\n", " graph_writer.close()\n", " \n", " with self._session.as_default() as sess:\n", " self._init.run()\n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X))\n", " for rnd_indices in np.array_split(rnd_idx, len(X) // self.batch_size):\n", " X_batch, y_batch = X[rnd_indices], y[rnd_indices]\n", " feed_dict = {self._X: X_batch, self._y: y_batch}\n", " if self._training is not None:\n", " feed_dict[self._training] = True\n", " sess.run(self._training_op, feed_dict=feed_dict)\n", " if extra_update_ops:\n", " sess.run(extra_update_ops, feed_dict=feed_dict)\n", " if X_valid is not None and y_valid is not None:\n", " loss_val, acc_val = sess.run([self._loss, self._accuracy],\n", " feed_dict={self._X: X_valid,\n", " self._y: y_valid})\n", " if loss_val < best_loss:\n", " best_params = self._get_model_params()\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", " if checks_without_progress > max_checks_without_progress:\n", " print(\"조기 종료!\")\n", " break\n", " else:\n", " loss_train, acc_train = sess.run([self._loss, self._accuracy],\n", " feed_dict={self._X: X_batch,\n", " self._y: y_batch})\n", " print(\"{}\\t마지막 훈련 배치 손실: {:.6f}\\tAccuracy: {:.2f}%\".format(\n", " epoch, loss_train, acc_train * 100))\n", " # 조기 종료를 사용하면 이전의 최상의 모델로 되돌립니다.\n", " if best_params:\n", " self._restore_model_params(best_params)\n", " return self\n", "\n", " def predict_proba(self, X):\n", " if not self._session:\n", " raise NotFittedError(\"%s 객체가 아직 훈련되지 않았습니다\" % self.__class__.__name__)\n", " with self._session.as_default() as sess:\n", " return self._Y_proba.eval(feed_dict={self._X: X})\n", "\n", " def predict(self, X):\n", " class_indices = np.argmax(self.predict_proba(X), axis=1)\n", " return np.array([[self.classes_[class_index]]\n", " for class_index in class_indices], np.int32)\n", "\n", " def save(self, path):\n", " self._saver.save(self._session, path)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.217492\t최선의 손실: 0.217492\t정확도: 96.01%\n", "1\t검증 세트 손실: 0.170349\t최선의 손실: 0.170349\t정확도: 96.52%\n", "2\t검증 세트 손실: 0.091266\t최선의 손실: 0.091266\t정확도: 97.58%\n", "3\t검증 세트 손실: 0.327799\t최선의 손실: 0.091266\t정확도: 94.25%\n", "4\t검증 세트 손실: 0.162250\t최선의 손실: 0.091266\t정확도: 97.11%\n", "5\t검증 세트 손실: 0.684595\t최선의 손실: 0.091266\t정확도: 75.84%\n", "6\t검증 세트 손실: 0.824483\t최선의 손실: 0.091266\t정확도: 57.43%\n", "7\t검증 세트 손실: 0.854250\t최선의 손실: 0.091266\t정확도: 60.95%\n", "8\t검증 세트 손실: 0.724734\t최선의 손실: 0.091266\t정확도: 68.22%\n", "9\t검증 세트 손실: 1.528036\t최선의 손실: 0.091266\t정확도: 27.52%\n", "10\t검증 세트 손실: 1.625366\t최선의 손실: 0.091266\t정확도: 22.01%\n", "11\t검증 세트 손실: 1.798194\t최선의 손실: 0.091266\t정확도: 22.01%\n", "12\t검증 세트 손실: 1.641676\t최선의 손실: 0.091266\t정확도: 18.73%\n", "13\t검증 세트 손실: 1.615793\t최선의 손실: 0.091266\t정확도: 19.27%\n", "14\t검증 세트 손실: 1.613873\t최선의 손실: 0.091266\t정확도: 20.91%\n", "15\t검증 세트 손실: 1.647734\t최선의 손실: 0.091266\t정확도: 20.91%\n", "16\t검증 세트 손실: 1.717600\t최선의 손실: 0.091266\t정확도: 19.08%\n", "17\t검증 세트 손실: 1.682581\t최선의 손실: 0.091266\t정확도: 19.27%\n", "18\t검증 세트 손실: 1.675134\t최선의 손실: 0.091266\t정확도: 18.73%\n", "19\t검증 세트 손실: 1.645765\t최선의 손실: 0.091266\t정확도: 19.08%\n", "20\t검증 세트 손실: 1.722328\t최선의 손실: 0.091266\t정확도: 22.01%\n", "21\t검증 세트 손실: 1.656413\t최선의 손실: 0.091266\t정확도: 22.01%\n", "22\t검증 세트 손실: 1.643529\t최선의 손실: 0.091266\t정확도: 18.73%\n", "23\t검증 세트 손실: 1.644234\t최선의 손실: 0.091266\t정확도: 19.27%\n", "조기 종료!\n" ] }, { "data": { "text/plain": [ "DNNClassifier(activation=,\n", " batch_norm_momentum=None, batch_size=20, dropout_rate=None,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=5, n_neurons=100,\n", " optimizer_class=,\n", " random_state=42)" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 모델 생성 및 학습\n", "dnn_clf = DNNClassifier(random_state=42)\n", "dnn_clf.fit(X_train1, y_train1, n_epochs=1000, X_valid=X_valid1, y_valid=y_valid1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex8-3.png](./img/ex8-3.png)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9787896477913991" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.metrics import accuracy_score\n", "\n", "# 정확도 확인 \n", "y_pred = dnn_clf.predict(X_test1)\n", "accuracy_score(y_test1, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### class sklearn.model_selection.RandomizedSearchCV\n", "\n", "##### _estimator : estimator object._\n", "\n", "실제 사용할 estimator 객체로 여기서는 DNNClassifier로 만들어진 개체를 사용.\n", "\n", "##### param_distributions : dict\n", "\n", "교차 검증에 사용될 파라미터의 세트.\n", "\n", "##### n_iter : int, default=10\n", "\n", "데이터 셋에 대한 파라미터 세팅 사용 횟수. 값이 커지면 검증시간이 길어지지만 질이 좋아지며, 값이 작아지면 검증시간이 짧아지지만 질이 떨어지는 트레이트 오프 관계임.\n", "\n", "##### random_state : int, RandomState instance or None, optional, default=None\n", "\n", "난수 생성기의 상태, int일 경우에는 난수 생성 시드. 생성되는 난수에 따라 파라미터 세팅 순서가 바뀜.\n", "\n", "##### cv : int, cross-validation generator or an iterable, optional\n", "\n", "한 파라미터 세트에 대한 반복 횟수. cv 값에 따라 정확도를 출력하는 횟수가 바뀜.\n", "\n", "##### verbose : integer\n", "\n", "출력으로 나오는 정보를 관할, 1일경우 단순한 loss와 acc만 출력함.\n", "\n", "https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.RandomizedSearchCV.html#sklearn.model_selection.RandomizedSearchCV" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### class DNNClassifier(BaseEstimator, ClassifierMixin):\n", "def__init__(**self**, **n_hidden_layers**=5, **n_neurons**=100, **optimizer_class**=tf.train.AdamOptimizer, **learning_rate**=0.01, **batch_size**=20, **activation**=tf.nn.elu, **initializer**=he_init, **batch_norm_momentum**=None, **dropout_rate**=None, **random_state**=None):" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fitting 5 folds for each of 10 candidates, totalling 50 fits\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 243965.640625\t최선의 손실: 243965.640625\t정확도: 73.89%\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 7.9s\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 8.0s remaining: 0.0s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 105751.203125\t최선의 손실: 105751.203125\t정확도: 94.53%\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 8.0s\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 79280.632812\t최선의 손실: 79280.632812\t정확도: 78.54%\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 7.9s\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 2447.964355\t최선의 손실: 2447.964355\t정확도: 94.68%\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 8.0s\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 11203.961914\t최선의 손실: 11203.961914\t정확도: 82.17%\n", "[CV] n_neurons=140, learning_rate=0.05, batch_size=10, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 8.0s\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.760039\t최선의 손실: 0.760039\t정확도: 80.22%\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation= \n", "0\t검증 세트 손실: 1.405251\t최선의 손실: 1.405251\t정확도: 65.40%\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation= \n", "0\t검증 세트 손실: 2.858307\t최선의 손실: 2.858307\t정확도: 36.43%\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation= \n", "0\t검증 세트 손실: 2.125920\t최선의 손실: 2.125920\t정확도: 19.27%\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.573851\t최선의 손실: 0.573851\t정확도: 82.49%\n", "[CV] n_neurons=70, learning_rate=0.1, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 212.681320\t최선의 손실: 212.681320\t정확도: 75.76%\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 2.2s\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 5.315692\t최선의 손실: 5.315692\t정확도: 92.69%\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 2.1s\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 0.123163\t최선의 손실: 0.123163\t정확도: 96.68%\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 2.1s\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 158.806961\t최선의 손실: 158.806961\t정확도: 58.21%\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 2.2s\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0> \n", "0\t검증 세트 손실: 4.309988\t최선의 손실: 4.309988\t정확도: 92.42%\n", "[CV] n_neurons=70, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000022C762F0>, total= 2.1s\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.211374\t최선의 손실: 0.211374\t정확도: 95.27%\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 1.5s\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.152731\t최선의 손실: 0.152731\t정확도: 94.92%\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 1.4s\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.318233\t최선의 손실: 0.318233\t정확도: 92.73%\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 1.4s\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.138411\t최선의 손실: 0.138411\t정확도: 96.44%\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 1.4s\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.175137\t최선의 손실: 0.175137\t정확도: 94.64%\n", "[CV] n_neurons=30, learning_rate=0.05, batch_size=100, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 1.4s\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.632661\t최선의 손실: 1.632661\t정확도: 20.91%\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation=, total= 6.3s\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.617177\t최선의 손실: 1.617177\t정확도: 20.91%\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation=, total= 6.2s\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.628445\t최선의 손실: 1.628445\t정확도: 19.27%\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation=, total= 6.3s\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.625269\t최선의 손실: 1.625269\t정확도: 19.27%\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation=, total= 6.2s\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.631198\t최선의 손실: 1.631198\t정확도: 19.27%\n", "[CV] n_neurons=30, learning_rate=0.1, batch_size=10, activation=, total= 6.3s\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.115835\t최선의 손실: 0.115835\t정확도: 97.26%\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation=, total= 1.7s\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.101629\t최선의 손실: 0.101629\t정확도: 97.34%\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation=, total= 1.7s\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.107570\t최선의 손실: 0.107570\t정확도: 97.15%\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation=, total= 1.7s\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation= \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.116353\t최선의 손실: 0.116353\t정확도: 97.22%\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation=, total= 1.7s\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.129267\t최선의 손실: 0.129267\t정확도: 97.15%\n", "[CV] n_neurons=30, learning_rate=0.02, batch_size=50, activation=, total= 1.7s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.712075\t최선의 손실: 1.712075\t정확도: 19.08%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation=, total= 6.4s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.717215\t최선의 손실: 1.717215\t정확도: 19.08%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation=, total= 6.4s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.720168\t최선의 손실: 1.720168\t정확도: 20.91%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation=, total= 6.4s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.644304\t최선의 손실: 1.644304\t정확도: 19.08%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation=, total= 6.5s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation= \n", "0\t검증 세트 손실: 1.630527\t최선의 손실: 1.630527\t정확도: 22.01%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=10, activation=, total= 6.4s\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation= \n", "0\t검증 세트 손실: 1.615671\t최선의 손실: 1.615671\t정확도: 19.27%\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation=, total= 1.8s\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.550025\t최선의 손실: 0.550025\t정확도: 76.23%\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation=, total= 1.8s\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.329665\t최선의 손실: 0.329665\t정확도: 90.54%\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation=, total= 1.8s\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.499062\t최선의 손실: 0.499062\t정확도: 86.83%\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation=, total= 1.8s\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation= \n", "0\t검증 세트 손실: 0.246952\t최선의 손실: 0.246952\t정확도: 91.87%\n", "[CV] n_neurons=100, learning_rate=0.05, batch_size=50, activation=, total= 1.9s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.095222\t최선의 손실: 0.095222\t정확도: 97.22%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.096876\t최선의 손실: 0.096876\t정확도: 96.99%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.100699\t최선의 손실: 0.100699\t정확도: 97.07%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation=, total= 0.6s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.102310\t최선의 손실: 0.102310\t정확도: 96.95%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation= \n", "0\t검증 세트 손실: 0.130466\t최선의 손실: 0.130466\t정확도: 96.56%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=500, activation=, total= 0.7s\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 1.201893\t최선의 손실: 1.201893\t정확도: 52.35%\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 2.1s\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 331.033997\t최선의 손실: 331.033997\t정확도: 19.31%\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 2.2s\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 1046.947998\t최선의 손실: 1046.947998\t정확도: 18.73%\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 2.1s\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.922029\t최선의 손실: 0.922029\t정확도: 63.64%\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 2.2s\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8> \n", "0\t검증 세트 손실: 0.596779\t최선의 손실: 0.596779\t정확도: 85.69%\n", "[CV] n_neurons=50, learning_rate=0.05, batch_size=50, activation=.parametrized_leaky_relu at 0x0000000006776AE8>, total= 2.1s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=1)]: Done 50 out of 50 | elapsed: 2.7min finished\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.103159\t최선의 손실: 0.103159\t정확도: 97.42%\n" ] }, { "data": { "text/plain": [ "RandomizedSearchCV(cv=5, error_score='raise',\n", " estimator=DNNClassifier(activation=,\n", " batch_norm_momentum=None, batch_size=20, dropout_rate=None,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=5, n_neurons=100,\n", " optimizer_class=,\n", " random_state=42),\n", " fit_params=None, iid=True, n_iter=10, n_jobs=1,\n", " param_distributions={'n_neurons': [10, 30, 50, 70, 90, 100, 120, 140, 160], 'batch_size': [10, 50, 100, 500], 'learning_rate': [0.01, 0.02, 0.05, 0.1], 'activation': [, , .parametrized_leaky_relu at 0x0000000006776AE8>, .parametrized_leaky_relu at 0x0000000022C762F0>]},\n", " pre_dispatch='2*n_jobs', random_state=22, refit=True,\n", " return_train_score='warn', scoring=None, verbose=2)" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.model_selection import RandomizedSearchCV\n", "\n", "def leaky_relu(alpha=0.01):\n", " def parametrized_leaky_relu(z, name=None):\n", " return tf.maximum(alpha * z, z, name=name)\n", " return parametrized_leaky_relu\n", "\n", "# sklearn 매서드를 통한 교차 검증\n", "\n", "param_distribs = {\n", " \"n_neurons\": [10, 30, 50, 70, 90, 100, 120, 140, 160],\n", " \"batch_size\": [10, 50, 100, 500],\n", " \"learning_rate\": [0.01, 0.02, 0.05, 0.1],\n", " \"activation\": [tf.nn.relu, tf.nn.elu, leaky_relu(alpha=0.01), leaky_relu(alpha=0.1)],\n", " # 은닉층의 수나 옵티마이저 등을 달리하여 탐색해 볼 수 있습니다.\n", " #\"n_hidden_layers\": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n", " #\"optimizer_class\": [tf.train.AdamOptimizer, partial(tf.train.MomentumOptimizer, momentum=0.95)],\n", "}\n", "\n", "rnd_search = RandomizedSearchCV(DNNClassifier(random_state=42), param_distribs, n_iter=10,\n", " random_state=22, verbose=2, cv=5)\n", "fit_params={\"X_valid\": X_valid1, \"y_valid\": y_valid1, \"n_epochs\": 1} \n", " # 교차검증이 행해지는 것을 보기위해 epochs는 1로 두었다.\n", "rnd_search.fit(X_train1, y_train1, **fit_params)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'n_neurons': 30,\n", " 'learning_rate': 0.02,\n", " 'batch_size': 50,\n", " 'activation': }" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 최적의 파라미터 세팅 확인\n", "rnd_search.best_params_" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "0.9747032496594669" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = rnd_search.predict(X_test1)\n", "accuracy_score(y_test1, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### d. 배치 정규화를 추가한 다음 학습 곡선을 비교해보세요. 이전보다 수렴이 빨라졌나요? 모델의 성능이 더 나아졌나요?" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [], "source": [ "def leaky_relu(alpha=0.01):\n", " def parametrized_leaky_relu(z, name=None):\n", " return tf.maximum(alpha * z, z, name=name)\n", " return parametrized_leaky_relu" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.072004\t최선의 손실: 0.072004\t정확도: 97.77%\n", "1\t검증 세트 손실: 0.058453\t최선의 손실: 0.058453\t정확도: 98.28%\n", "2\t검증 세트 손실: 0.040213\t최선의 손실: 0.040213\t정확도: 98.71%\n", "3\t검증 세트 손실: 0.048657\t최선의 손실: 0.040213\t정확도: 98.63%\n", "4\t검증 세트 손실: 0.050188\t최선의 손실: 0.040213\t정확도: 98.55%\n", "5\t검증 세트 손실: 0.035519\t최선의 손실: 0.035519\t정확도: 98.91%\n", "6\t검증 세트 손실: 0.048708\t최선의 손실: 0.035519\t정확도: 98.67%\n", "7\t검증 세트 손실: 0.034432\t최선의 손실: 0.034432\t정확도: 99.10%\n", "8\t검증 세트 손실: 0.071947\t최선의 손실: 0.034432\t정확도: 98.71%\n", "9\t검증 세트 손실: 0.046487\t최선의 손실: 0.034432\t정확도: 98.67%\n", "10\t검증 세트 손실: 0.061813\t최선의 손실: 0.034432\t정확도: 98.91%\n", "11\t검증 세트 손실: 0.058360\t최선의 손실: 0.034432\t정확도: 98.71%\n", "12\t검증 세트 손실: 0.051923\t최선의 손실: 0.034432\t정확도: 98.87%\n", "13\t검증 세트 손실: 0.064132\t최선의 손실: 0.034432\t정확도: 98.59%\n", "14\t검증 세트 손실: 0.060787\t최선의 손실: 0.034432\t정확도: 98.79%\n", "15\t검증 세트 손실: 0.049941\t최선의 손실: 0.034432\t정확도: 98.94%\n", "16\t검증 세트 손실: 0.064815\t최선의 손실: 0.034432\t정확도: 98.91%\n", "17\t검증 세트 손실: 0.049389\t최선의 손실: 0.034432\t정확도: 98.94%\n", "18\t검증 세트 손실: 0.076514\t최선의 손실: 0.034432\t정확도: 98.67%\n", "19\t검증 세트 손실: 0.049628\t최선의 손실: 0.034432\t정확도: 98.79%\n", "20\t검증 세트 손실: 0.069667\t최선의 손실: 0.034432\t정확도: 98.71%\n", "21\t검증 세트 손실: 0.064128\t최선의 손실: 0.034432\t정확도: 98.79%\n", "22\t검증 세트 손실: 0.056815\t최선의 손실: 0.034432\t정확도: 98.71%\n", "23\t검증 세트 손실: 5.791260\t최선의 손실: 0.034432\t정확도: 75.10%\n", "24\t검증 세트 손실: 1.256847\t최선의 손실: 0.034432\t정확도: 95.90%\n", "25\t검증 세트 손실: 0.322678\t최선의 손실: 0.034432\t정확도: 96.95%\n", "26\t검증 세트 손실: 0.256842\t최선의 손실: 0.034432\t정확도: 96.21%\n", "27\t검증 세트 손실: 0.191039\t최선의 손실: 0.034432\t정확도: 96.99%\n", "28\t검증 세트 손실: 0.177863\t최선의 손실: 0.034432\t정확도: 96.87%\n", "조기 종료!\n" ] }, { "data": { "text/plain": [ "DNNClassifier(activation=.parametrized_leaky_relu at 0x0000000005433D90>,\n", " batch_norm_momentum=None, batch_size=500, dropout_rate=None,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=5, n_neurons=120,\n", " optimizer_class=,\n", " random_state=42)" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dnn_clf = DNNClassifier(activation=leaky_relu(alpha=0.1), batch_size=500, learning_rate=0.01,\n", " n_neurons=120, random_state=42)\n", "dnn_clf.fit(X_train1, y_train1, n_epochs=1000, X_valid=X_valid1, y_valid=y_valid1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex8-4.png](./img/ex8-4.png)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9904650710254913" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = dnn_clf.predict(X_test1)\n", "accuracy_score(y_test1, y_pred)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.046399\t최선의 손실: 0.046399\t정확도: 98.71%\n", "1\t검증 세트 손실: 0.057514\t최선의 손실: 0.046399\t정확도: 98.05%\n", "2\t검증 세트 손실: 0.046561\t최선의 손실: 0.046399\t정확도: 98.63%\n", "3\t검증 세트 손실: 0.044289\t최선의 손실: 0.044289\t정확도: 98.71%\n", "4\t검증 세트 손실: 0.044379\t최선의 손실: 0.044289\t정확도: 98.75%\n", "5\t검증 세트 손실: 0.054216\t최선의 손실: 0.044289\t정확도: 98.59%\n", "6\t검증 세트 손실: 0.038474\t최선의 손실: 0.038474\t정확도: 99.10%\n", "7\t검증 세트 손실: 0.031520\t최선의 손실: 0.031520\t정확도: 99.18%\n", "8\t검증 세트 손실: 0.039194\t최선의 손실: 0.031520\t정확도: 98.94%\n", "9\t검증 세트 손실: 0.040424\t최선의 손실: 0.031520\t정확도: 99.18%\n", "10\t검증 세트 손실: 0.034827\t최선의 손실: 0.031520\t정확도: 99.14%\n", "11\t검증 세트 손실: 0.037619\t최선의 손실: 0.031520\t정확도: 99.02%\n", "12\t검증 세트 손실: 0.031217\t최선의 손실: 0.031217\t정확도: 99.30%\n", "13\t검증 세트 손실: 0.045529\t최선의 손실: 0.031217\t정확도: 98.79%\n", "14\t검증 세트 손실: 0.043039\t최선의 손실: 0.031217\t정확도: 99.02%\n", "15\t검증 세트 손실: 0.044173\t최선의 손실: 0.031217\t정확도: 98.98%\n", "16\t검증 세트 손실: 0.052172\t최선의 손실: 0.031217\t정확도: 98.87%\n", "17\t검증 세트 손실: 0.047888\t최선의 손실: 0.031217\t정확도: 98.87%\n", "18\t검증 세트 손실: 0.041204\t최선의 손실: 0.031217\t정확도: 98.94%\n", "19\t검증 세트 손실: 0.035944\t최선의 손실: 0.031217\t정확도: 99.30%\n", "20\t검증 세트 손실: 0.030095\t최선의 손실: 0.030095\t정확도: 98.98%\n", "21\t검증 세트 손실: 0.033505\t최선의 손실: 0.030095\t정확도: 99.14%\n", "22\t검증 세트 손실: 0.035202\t최선의 손실: 0.030095\t정확도: 99.10%\n", "23\t검증 세트 손실: 0.034386\t최선의 손실: 0.030095\t정확도: 99.14%\n", "24\t검증 세트 손실: 0.037961\t최선의 손실: 0.030095\t정확도: 99.14%\n", "25\t검증 세트 손실: 0.031447\t최선의 손실: 0.030095\t정확도: 99.30%\n", "26\t검증 세트 손실: 0.040911\t최선의 손실: 0.030095\t정확도: 99.30%\n", "27\t검증 세트 손실: 0.039532\t최선의 손실: 0.030095\t정확도: 99.22%\n", "28\t검증 세트 손실: 0.040794\t최선의 손실: 0.030095\t정확도: 99.14%\n", "29\t검증 세트 손실: 0.050331\t최선의 손실: 0.030095\t정확도: 99.18%\n", "30\t검증 세트 손실: 0.052344\t최선의 손실: 0.030095\t정확도: 98.91%\n", "31\t검증 세트 손실: 0.041524\t최선의 손실: 0.030095\t정확도: 99.30%\n", "32\t검증 세트 손실: 0.044931\t최선의 손실: 0.030095\t정확도: 99.06%\n", "33\t검증 세트 손실: 0.029188\t최선의 손실: 0.029188\t정확도: 99.41%\n", "34\t검증 세트 손실: 0.042445\t최선의 손실: 0.029188\t정확도: 99.02%\n", "35\t검증 세트 손실: 0.061434\t최선의 손실: 0.029188\t정확도: 98.91%\n", "36\t검증 세트 손실: 0.039287\t최선의 손실: 0.029188\t정확도: 99.10%\n", "37\t검증 세트 손실: 0.056927\t최선의 손실: 0.029188\t정확도: 99.14%\n", "38\t검증 세트 손실: 0.035324\t최선의 손실: 0.029188\t정확도: 99.18%\n", "39\t검증 세트 손실: 0.036401\t최선의 손실: 0.029188\t정확도: 99.22%\n", "40\t검증 세트 손실: 0.025014\t최선의 손실: 0.025014\t정확도: 99.49%\n", "41\t검증 세트 손실: 0.038586\t최선의 손실: 0.025014\t정확도: 99.18%\n", "42\t검증 세트 손실: 0.042447\t최선의 손실: 0.025014\t정확도: 99.22%\n", "43\t검증 세트 손실: 0.050906\t최선의 손실: 0.025014\t정확도: 98.94%\n", "44\t검증 세트 손실: 0.037501\t최선의 손실: 0.025014\t정확도: 99.14%\n", "45\t검증 세트 손실: 0.035183\t최선의 손실: 0.025014\t정확도: 99.14%\n", "46\t검증 세트 손실: 0.043983\t최선의 손실: 0.025014\t정확도: 99.10%\n", "47\t검증 세트 손실: 0.048346\t최선의 손실: 0.025014\t정확도: 98.98%\n", "48\t검증 세트 손실: 0.047112\t최선의 손실: 0.025014\t정확도: 99.14%\n", "49\t검증 세트 손실: 0.039525\t최선의 손실: 0.025014\t정확도: 99.14%\n", "50\t검증 세트 손실: 0.034626\t최선의 손실: 0.025014\t정확도: 99.06%\n", "51\t검증 세트 손실: 0.040666\t최선의 손실: 0.025014\t정확도: 99.18%\n", "52\t검증 세트 손실: 0.048244\t최선의 손실: 0.025014\t정확도: 99.14%\n", "53\t검증 세트 손실: 0.042378\t최선의 손실: 0.025014\t정확도: 99.14%\n", "54\t검증 세트 손실: 0.035787\t최선의 손실: 0.025014\t정확도: 99.30%\n", "55\t검증 세트 손실: 0.058862\t최선의 손실: 0.025014\t정확도: 98.98%\n", "56\t검증 세트 손실: 0.037223\t최선의 손실: 0.025014\t정확도: 99.10%\n", "57\t검증 세트 손실: 0.033818\t최선의 손실: 0.025014\t정확도: 99.22%\n", "58\t검증 세트 손실: 0.034309\t최선의 손실: 0.025014\t정확도: 99.18%\n", "59\t검증 세트 손실: 0.030903\t최선의 손실: 0.025014\t정확도: 99.37%\n", "60\t검증 세트 손실: 0.033782\t최선의 손실: 0.025014\t정확도: 99.22%\n", "61\t검증 세트 손실: 0.030856\t최선의 손실: 0.025014\t정확도: 99.45%\n", "조기 종료!\n" ] }, { "data": { "text/plain": [ "DNNClassifier(activation=.parametrized_leaky_relu at 0x0000000005449D08>,\n", " batch_norm_momentum=0.95, batch_size=500, dropout_rate=None,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=5, n_neurons=120,\n", " optimizer_class=,\n", " random_state=42)" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dnn_clf_bn = DNNClassifier(activation=leaky_relu(alpha=0.1), batch_size=500, learning_rate=0.01,\n", " n_neurons=120, random_state=42,\n", " batch_norm_momentum=0.95)\n", "dnn_clf_bn.fit(X_train1, y_train1, n_epochs=1000, X_valid=X_valid1, y_valid=y_valid1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex8-5.png](./img/ex8-5.png)" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "0.9947460595446584" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = dnn_clf_bn.predict(X_test1)\n", "accuracy_score(y_test1, y_pred)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fitting 3 folds for each of 10 candidates, totalling 30 fits\n", "[CV] n_neurons=100, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.9, activation= \n", "0\t검증 세트 손실: 0.205692\t최선의 손실: 0.205692\t정확도: 94.14%\n", "[CV] n_neurons=100, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.9, activation=, total= 2.5s\n", "[CV] n_neurons=100, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.9, activation= \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 2.5s remaining: 0.0s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.213113\t최선의 손실: 0.213113\t정확도: 93.94%\n", "[CV] n_neurons=100, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.9, activation=, total= 2.5s\n", "[CV] n_neurons=100, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.9, activation= \n", "0\t검증 세트 손실: 0.158706\t최선의 손실: 0.158706\t정확도: 94.61%\n", "[CV] n_neurons=100, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.9, activation=, total= 2.5s\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 13.416183\t최선의 손실: 13.416183\t정확도: 66.93%\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.99, activation=, total= 2.5s\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 4.886566\t최선의 손실: 4.886566\t정확도: 88.78%\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.99, activation=, total= 2.5s\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 4.932652\t최선의 손실: 4.932652\t정확도: 82.45%\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=500, batch_norm_momentum=0.99, activation=, total= 2.5s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=500, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.316675\t최선의 손실: 0.316675\t정확도: 95.54%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=500, batch_norm_momentum=0.98, activation=, total= 2.5s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=500, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.462918\t최선의 손실: 0.462918\t정확도: 92.49%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=500, batch_norm_momentum=0.98, activation=, total= 2.5s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=500, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.280606\t최선의 손실: 0.280606\t정확도: 95.86%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=500, batch_norm_momentum=0.98, activation=, total= 2.5s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.089062\t최선의 손실: 0.089062\t정확도: 97.34%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.98, activation=, total= 4.0s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.135506\t최선의 손실: 0.135506\t정확도: 95.93%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.98, activation=, total= 4.0s\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.114201\t최선의 손실: 0.114201\t정확도: 96.48%\n", "[CV] n_neurons=120, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.98, activation=, total= 4.0s\n", "[CV] n_neurons=10, learning_rate=0.05, batch_size=50, batch_norm_momentum=0.95, activation=.parametrized_leaky_relu at 0x000000003295A8C8> \n", "0\t검증 세트 손실: 0.108971\t최선의 손실: 0.108971\t정확도: 96.79%\n", "[CV] n_neurons=10, learning_rate=0.05, batch_size=50, batch_norm_momentum=0.95, activation=.parametrized_leaky_relu at 0x000000003295A8C8>, total= 6.1s\n", "[CV] n_neurons=10, learning_rate=0.05, batch_size=50, batch_norm_momentum=0.95, activation=.parametrized_leaky_relu at 0x000000003295A8C8> \n", "0\t검증 세트 손실: 0.129771\t최선의 손실: 0.129771\t정확도: 95.97%\n", "[CV] n_neurons=10, learning_rate=0.05, batch_size=50, batch_norm_momentum=0.95, activation=.parametrized_leaky_relu at 0x000000003295A8C8>, total= 6.1s\n", "[CV] n_neurons=10, learning_rate=0.05, batch_size=50, batch_norm_momentum=0.95, activation=.parametrized_leaky_relu at 0x000000003295A8C8> \n", "0\t검증 세트 손실: 0.124081\t최선의 손실: 0.124081\t정확도: 96.76%\n", "[CV] n_neurons=10, learning_rate=0.05, batch_size=50, batch_norm_momentum=0.95, activation=.parametrized_leaky_relu at 0x000000003295A8C8>, total= 6.1s\n", "[CV] n_neurons=10, learning_rate=0.02, batch_size=10, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295AE18> \n", "0\t검증 세트 손실: 0.125739\t최선의 손실: 0.125739\t정확도: 95.78%\n", "[CV] n_neurons=10, learning_rate=0.02, batch_size=10, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295AE18>, total= 21.5s\n", "[CV] n_neurons=10, learning_rate=0.02, batch_size=10, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295AE18> \n", "0\t검증 세트 손실: 0.134749\t최선의 손실: 0.134749\t정확도: 96.09%\n", "[CV] n_neurons=10, learning_rate=0.02, batch_size=10, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295AE18>, total= 21.5s\n", "[CV] n_neurons=10, learning_rate=0.02, batch_size=10, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295AE18> \n", "0\t검증 세트 손실: 0.129308\t최선의 손실: 0.129308\t정확도: 96.83%\n", "[CV] n_neurons=10, learning_rate=0.02, batch_size=10, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295AE18>, total= 21.5s\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=10, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 0.139122\t최선의 손실: 0.139122\t정확도: 96.29%\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=10, batch_norm_momentum=0.99, activation=, total= 19.8s\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=10, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 0.164289\t최선의 손실: 0.164289\t정확도: 95.47%\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=10, batch_norm_momentum=0.99, activation=, total= 19.8s\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=10, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 0.133724\t최선의 손실: 0.133724\t정확도: 96.29%\n", "[CV] n_neurons=140, learning_rate=0.1, batch_size=10, batch_norm_momentum=0.99, activation=, total= 20.1s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 0.089300\t최선의 손실: 0.089300\t정확도: 97.62%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.99, activation=, total= 4.0s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 0.167103\t최선의 손실: 0.167103\t정확도: 96.05%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.99, activation=, total= 4.0s\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.99, activation= \n", "0\t검증 세트 손실: 0.124234\t최선의 손실: 0.124234\t정확도: 97.22%\n", "[CV] n_neurons=100, learning_rate=0.02, batch_size=100, batch_norm_momentum=0.99, activation=, total= 3.9s\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.092258\t최선의 손실: 0.092258\t정확도: 97.65%\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.98, activation=, total= 6.0s\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.98, activation= \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.101827\t최선의 손실: 0.101827\t정확도: 97.07%\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.98, activation=, total= 5.9s\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.98, activation= \n", "0\t검증 세트 손실: 0.100589\t최선의 손실: 0.100589\t정확도: 97.11%\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.98, activation=, total= 5.9s\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295A8C8> \n", "0\t검증 세트 손실: 0.062582\t최선의 손실: 0.062582\t정확도: 98.16%\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295A8C8>, total= 6.3s\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295A8C8> \n", "0\t검증 세트 손실: 0.070575\t최선의 손실: 0.070575\t정확도: 97.85%\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295A8C8>, total= 6.3s\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295A8C8> \n", "0\t검증 세트 손실: 0.081792\t최선의 손실: 0.081792\t정확도: 97.85%\n", "[CV] n_neurons=120, learning_rate=0.01, batch_size=50, batch_norm_momentum=0.9, activation=.parametrized_leaky_relu at 0x000000003295A8C8>, total= 6.3s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=1)]: Done 30 out of 30 | elapsed: 3.8min finished\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.088415\t최선의 손실: 0.088415\t정확도: 97.34%\n" ] }, { "data": { "text/plain": [ "RandomizedSearchCV(cv=3, error_score='raise',\n", " estimator=DNNClassifier(activation=,\n", " batch_norm_momentum=None, batch_size=20, dropout_rate=None,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=5, n_neurons=100,\n", " optimizer_class=,\n", " random_state=42),\n", " fit_params=None, iid=True, n_iter=10, n_jobs=1,\n", " param_distributions={'n_neurons': [10, 30, 50, 70, 90, 100, 120, 140, 160], 'batch_size': [10, 50, 100, 500], 'learning_rate': [0.01, 0.02, 0.05, 0.1], 'activation': [, , .parametrized_leaky_relu at 0x000000003295A8C8>, .parametrized_leaky_relu at 0x000000003295AE18>], 'batch_norm_momentum': [0.9, 0.95, 0.98, 0.99, 0.999]},\n", " pre_dispatch='2*n_jobs', random_state=42, refit=True,\n", " return_train_score='warn', scoring=None, verbose=2)" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.model_selection import RandomizedSearchCV\n", "\n", "param_distribs = {\n", " \"n_neurons\": [10, 30, 50, 70, 90, 100, 120, 140, 160],\n", " \"batch_size\": [10, 50, 100, 500],\n", " \"learning_rate\": [0.01, 0.02, 0.05, 0.1],\n", " \"activation\": [tf.nn.relu, tf.nn.elu, leaky_relu(alpha=0.01), leaky_relu(alpha=0.1)],\n", " # you could also try exploring different numbers of hidden layers, different optimizers, etc.\n", " #\"n_hidden_layers\": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n", " #\"optimizer_class\": [tf.train.AdamOptimizer, partial(tf.train.MomentumOptimizer, momentum=0.95)],\n", " \"batch_norm_momentum\": [0.9, 0.95, 0.98, 0.99, 0.999],\n", "}\n", "\n", "rnd_search_bn = RandomizedSearchCV(DNNClassifier(random_state=42), param_distribs, n_iter=10,\n", " random_state=42, verbose=2, cv=3)\n", "fit_params={\"X_valid\": X_valid1, \"y_valid\": y_valid1, \"n_epochs\": 1}\n", "rnd_search_bn.fit(X_train1, y_train1, **fit_params)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'n_neurons': 120,\n", " 'learning_rate': 0.01,\n", " 'batch_size': 50,\n", " 'batch_norm_momentum': 0.9,\n", " 'activation': .parametrized_leaky_relu(z, name=None)>}" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rnd_search_bn.best_params_" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9747032496594669" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 테스트 셋에 대한 정확도\n", "y_pred = rnd_search_bn.predict(X_test1)\n", "accuracy_score(y_test1, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### e. 모델이 훈련 세트에 과대적합되었나요? 모든 층에 드롭아웃을 적용하고 다시 시도해보세요. 도움이 되었나요?" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9975390541408089" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 트레이닝 셋에 대한 정확도\n", "y_pred = dnn_clf.predict(X_train1)\n", "accuracy_score(y_train1, y_pred)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.132892\t최선의 손실: 0.132892\t정확도: 96.64%\n", "1\t검증 세트 손실: 0.106895\t최선의 손실: 0.106895\t정확도: 97.30%\n", "2\t검증 세트 손실: 0.090244\t최선의 손실: 0.090244\t정확도: 97.69%\n", "3\t검증 세트 손실: 0.082031\t최선의 손실: 0.082031\t정확도: 97.77%\n", "4\t검증 세트 손실: 0.093091\t최선의 손실: 0.082031\t정확도: 97.89%\n", "5\t검증 세트 손실: 0.085899\t최선의 손실: 0.082031\t정확도: 97.93%\n", "6\t검증 세트 손실: 0.073079\t최선의 손실: 0.073079\t정확도: 97.85%\n", "7\t검증 세트 손실: 0.077855\t최선의 손실: 0.073079\t정확도: 98.16%\n", "8\t검증 세트 손실: 0.070201\t최선의 손실: 0.070201\t정확도: 98.01%\n", "9\t검증 세트 손실: 0.072985\t최선의 손실: 0.070201\t정확도: 98.01%\n", "10\t검증 세트 손실: 0.068381\t최선의 손실: 0.068381\t정확도: 98.12%\n", "11\t검증 세트 손실: 0.069045\t최선의 손실: 0.068381\t정확도: 98.28%\n", "12\t검증 세트 손실: 0.082291\t최선의 손실: 0.068381\t정확도: 97.97%\n", "13\t검증 세트 손실: 0.068411\t최선의 손실: 0.068381\t정확도: 98.12%\n", "14\t검증 세트 손실: 0.070232\t최선의 손실: 0.068381\t정확도: 98.28%\n", "15\t검증 세트 손실: 0.071348\t최선의 손실: 0.068381\t정확도: 98.24%\n", "16\t검증 세트 손실: 0.068500\t최선의 손실: 0.068381\t정확도: 98.05%\n", "17\t검증 세트 손실: 0.065135\t최선의 손실: 0.065135\t정확도: 98.40%\n", "18\t검증 세트 손실: 0.066330\t최선의 손실: 0.065135\t정확도: 98.40%\n", "19\t검증 세트 손실: 0.066908\t최선의 손실: 0.065135\t정확도: 98.44%\n", "20\t검증 세트 손실: 0.074938\t최선의 손실: 0.065135\t정확도: 98.01%\n", "21\t검증 세트 손실: 0.063095\t최선의 손실: 0.063095\t정확도: 98.20%\n", "22\t검증 세트 손실: 0.076118\t최선의 손실: 0.063095\t정확도: 98.16%\n", "23\t검증 세트 손실: 0.072339\t최선의 손실: 0.063095\t정확도: 98.05%\n", "24\t검증 세트 손실: 0.065389\t최선의 손실: 0.063095\t정확도: 98.32%\n", "25\t검증 세트 손실: 0.094054\t최선의 손실: 0.063095\t정확도: 97.81%\n", "26\t검증 세트 손실: 0.082936\t최선의 손실: 0.063095\t정확도: 98.40%\n", "27\t검증 세트 손실: 0.067997\t최선의 손실: 0.063095\t정확도: 98.24%\n", "28\t검증 세트 손실: 0.071385\t최선의 손실: 0.063095\t정확도: 98.51%\n", "29\t검증 세트 손실: 0.103171\t최선의 손실: 0.063095\t정확도: 97.77%\n", "30\t검증 세트 손실: 0.317616\t최선의 손실: 0.063095\t정확도: 86.32%\n", "31\t검증 세트 손실: 0.252414\t최선의 손실: 0.063095\t정확도: 94.10%\n", "32\t검증 세트 손실: 0.167093\t최선의 손실: 0.063095\t정확도: 95.19%\n", "33\t검증 세트 손실: 0.539934\t최선의 손실: 0.063095\t정확도: 80.18%\n", "34\t검증 세트 손실: 0.181367\t최선의 손실: 0.063095\t정확도: 92.61%\n", "35\t검증 세트 손실: 0.155229\t최선의 손실: 0.063095\t정확도: 96.05%\n", "36\t검증 세트 손실: 0.147875\t최선의 손실: 0.063095\t정확도: 95.47%\n", "37\t검증 세트 손실: 0.137802\t최선의 손실: 0.063095\t정확도: 95.62%\n", "38\t검증 세트 손실: 0.130221\t최선의 손실: 0.063095\t정확도: 96.36%\n", "39\t검증 세트 손실: 0.142479\t최선의 손실: 0.063095\t정확도: 95.23%\n", "40\t검증 세트 손실: 0.140749\t최선의 손실: 0.063095\t정확도: 94.84%\n", "41\t검증 세트 손실: 0.152512\t최선의 손실: 0.063095\t정확도: 94.10%\n", "42\t검증 세트 손실: 0.128890\t최선의 손실: 0.063095\t정확도: 96.21%\n", "조기 종료!\n" ] }, { "data": { "text/plain": [ "DNNClassifier(activation=.parametrized_leaky_relu at 0x00000000365A21E0>,\n", " batch_norm_momentum=None, batch_size=500, dropout_rate=0.5,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=5, n_neurons=90,\n", " optimizer_class=,\n", " random_state=42)" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 드롭아웃이 활성화된 DNN 모델\n", "dnn_clf_dropout = DNNClassifier(activation=leaky_relu(alpha=0.1), batch_size=500, learning_rate=0.01,\n", " n_neurons=90, random_state=42,\n", " dropout_rate=0.5)\n", "dnn_clf_dropout.fit(X_train1, y_train1, n_epochs=1000, X_valid=X_valid1, y_valid=y_valid1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex8-6.png](./img/ex8-6.png)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.986573263280794" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = dnn_clf_dropout.predict(X_test1)\n", "accuracy_score(y_test1, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9. 전이 학습\n", "\n", "#### a. 이전 모델에서 미리 학습한 은닉층을 모두 재사용하는 새로운 DNN을 만드세요. 이 은닉층을 동결시키고 소프트맥스 출력층은 새로운 것으로 바꾸세요." ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "restore_saver = tf.train.import_meta_graph(\"./my_best_mnist_model_0_to_4.meta\")\n", "\n", "X = tf.get_default_graph().get_tensor_by_name(\"X:0\")\n", "y = tf.get_default_graph().get_tensor_by_name(\"y:0\")\n", "loss = tf.get_default_graph().get_tensor_by_name(\"loss:0\")\n", "Y_proba = tf.get_default_graph().get_tensor_by_name(\"Y_proba:0\")\n", "logits = Y_proba.op.inputs[0]\n", "accuracy = tf.get_default_graph().get_tensor_by_name(\"accuracy:0\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "하위층을 동결하기 위해서 옵티마이저에게 전달하는 훈련 변수 목록에서 제외하고 출력층의 변수만 전달합니다:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "learning_rate = 0.01\n", "\n", "output_layer_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=\"logits\")\n", "optimizer = tf.train.AdamOptimizer(learning_rate, name=\"Adam2\")\n", "training_op = optimizer.minimize(loss, var_list=output_layer_vars)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "correct = tf.nn.in_top_k(logits, y, 1)\n", "accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")\n", "\n", "init = tf.global_variables_initializer()\n", "five_frozen_saver = tf.train.Saver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex9-1.png](./img/ex9-1.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### b. 이 새로운 DNN을 숫자 5~9에 대해 숫자마다 100개의 이미지만 사용해 훈련시켜보고 얼마나 시간이 걸리는지 재보세요. 작은 양의 샘플만으로도 높은 성능을 얻을 수 있나요?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "훈련, 검증, 테스트 세트를 만듭니다. 텐서플로는 0에서부터 `n_classes-1`까지 레이블을 기대하기 때문에 레이블에서 5를 뺍니다." ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [], "source": [ "X_train2_full = X_train[y_train >= 5]\n", "y_train2_full = y_train[y_train >= 5] - 5\n", "X_valid2_full = X_valid[y_valid >= 5]\n", "y_valid2_full = y_valid[y_valid >= 5] - 5\n", "X_test2 = X_test[y_test >= 5]\n", "y_test2 = y_test[y_test >= 5] - 5\n", "\n", "# 5이상의 값을 가지는 레이블을 얻어온 후 레이블에 5를 빼서 0~4까지의 레이블이 되도록 조작." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "또 연습문제 목적에 따라서 훈련 세트에서 클래스마다 100개의 샘플만 사용합니다(검증 세트에서는 클래스마다 30개의 샘플만 사용합니다). 이를 위해 작은 함수를 만들겠습니다:" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "def sample_n_instances_per_class(X, y, n=100):\n", " Xs, ys = [], []\n", " for label in np.unique(y):\n", " idx = (y == label)\n", " Xc = X[idx][:n]\n", " yc = y[idx][:n]\n", " Xs.append(Xc)\n", " ys.append(yc)\n", " return np.concatenate(Xs), np.concatenate(ys)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [], "source": [ "X_train2, y_train2 = sample_n_instances_per_class(X_train2_full, y_train2_full, n=100)\n", "X_valid2, y_valid2 = sample_n_instances_per_class(X_valid2_full, y_valid2_full, n=30)" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from ./my_best_mnist_model_0_to_4\n", "0\t검증 세트 손실: 1.275385\t최선의 손실: 1.275385\t정확도: 50.67%\n", "1\t검증 세트 손실: 1.214228\t최선의 손실: 1.214228\t정확도: 50.67%\n", "2\t검증 세트 손실: 1.122662\t최선의 손실: 1.122662\t정확도: 50.67%\n", "3\t검증 세트 손실: 1.103540\t최선의 손실: 1.103540\t정확도: 52.00%\n", "4\t검증 세트 손실: 1.088736\t최선의 손실: 1.088736\t정확도: 53.33%\n", "5\t검증 세트 손실: 1.057204\t최선의 손실: 1.057204\t정확도: 56.67%\n", "6\t검증 세트 손실: 1.049103\t최선의 손실: 1.049103\t정확도: 57.33%\n", "7\t검증 세트 손실: 1.048192\t최선의 손실: 1.048192\t정확도: 58.00%\n", "8\t검증 세트 손실: 1.023718\t최선의 손실: 1.023718\t정확도: 58.00%\n", "9\t검증 세트 손실: 1.022433\t최선의 손실: 1.022433\t정확도: 56.00%\n", "10\t검증 세트 손실: 1.027909\t최선의 손실: 1.022433\t정확도: 60.67%\n", "11\t검증 세트 손실: 1.103764\t최선의 손실: 1.022433\t정확도: 56.00%\n", "12\t검증 세트 손실: 1.086111\t최선의 손실: 1.022433\t정확도: 54.67%\n", "13\t검증 세트 손실: 1.028749\t최선의 손실: 1.022433\t정확도: 57.33%\n", "14\t검증 세트 손실: 1.072682\t최선의 손실: 1.022433\t정확도: 58.67%\n", "15\t검증 세트 손실: 1.025460\t최선의 손실: 1.022433\t정확도: 62.00%\n", "16\t검증 세트 손실: 0.966505\t최선의 손실: 0.966505\t정확도: 63.33%\n", "17\t검증 세트 손실: 1.015548\t최선의 손실: 0.966505\t정확도: 58.00%\n", "18\t검증 세트 손실: 0.978822\t최선의 손실: 0.966505\t정확도: 58.00%\n", "19\t검증 세트 손실: 0.979183\t최선의 손실: 0.966505\t정확도: 61.33%\n", "20\t검증 세트 손실: 1.024900\t최선의 손실: 0.966505\t정확도: 58.00%\n", "21\t검증 세트 손실: 1.000703\t최선의 손실: 0.966505\t정확도: 60.00%\n", "22\t검증 세트 손실: 0.987188\t최선의 손실: 0.966505\t정확도: 63.33%\n", "23\t검증 세트 손실: 0.978603\t최선의 손실: 0.966505\t정확도: 65.33%\n", "24\t검증 세트 손실: 1.019492\t최선의 손실: 0.966505\t정확도: 60.67%\n", "25\t검증 세트 손실: 0.972724\t최선의 손실: 0.966505\t정확도: 63.33%\n", "26\t검증 세트 손실: 1.051186\t최선의 손실: 0.966505\t정확도: 59.33%\n", "27\t검증 세트 손실: 0.989433\t최선의 손실: 0.966505\t정확도: 63.33%\n", "28\t검증 세트 손실: 0.946314\t최선의 손실: 0.946314\t정확도: 62.67%\n", "29\t검증 세트 손실: 0.997947\t최선의 손실: 0.946314\t정확도: 58.67%\n", "30\t검증 세트 손실: 0.973780\t최선의 손실: 0.946314\t정확도: 60.67%\n", "31\t검증 세트 손실: 0.982782\t최선의 손실: 0.946314\t정확도: 60.67%\n", "32\t검증 세트 손실: 0.939914\t최선의 손실: 0.939914\t정확도: 62.00%\n", "33\t검증 세트 손실: 0.995380\t최선의 손실: 0.939914\t정확도: 57.33%\n", "34\t검증 세트 손실: 1.005115\t최선의 손실: 0.939914\t정확도: 62.00%\n", "35\t검증 세트 손실: 0.972892\t최선의 손실: 0.939914\t정확도: 65.33%\n", "36\t검증 세트 손실: 0.984382\t최선의 손실: 0.939914\t정확도: 61.33%\n", "37\t검증 세트 손실: 0.975803\t최선의 손실: 0.939914\t정확도: 68.00%\n", "38\t검증 세트 손실: 0.994193\t최선의 손실: 0.939914\t정확도: 62.00%\n", "39\t검증 세트 손실: 0.957383\t최선의 손실: 0.939914\t정확도: 63.33%\n", "40\t검증 세트 손실: 0.964925\t최선의 손실: 0.939914\t정확도: 66.67%\n", "41\t검증 세트 손실: 0.982003\t최선의 손실: 0.939914\t정확도: 60.67%\n", "42\t검증 세트 손실: 0.981350\t최선의 손실: 0.939914\t정확도: 64.00%\n", "43\t검증 세트 손실: 0.974939\t최선의 손실: 0.939914\t정확도: 62.67%\n", "44\t검증 세트 손실: 0.971808\t최선의 손실: 0.939914\t정확도: 64.00%\n", "45\t검증 세트 손실: 0.965991\t최선의 손실: 0.939914\t정확도: 62.67%\n", "46\t검증 세트 손실: 0.911054\t최선의 손실: 0.911054\t정확도: 66.00%\n", "47\t검증 세트 손실: 1.013443\t최선의 손실: 0.911054\t정확도: 62.00%\n", "48\t검증 세트 손실: 0.955962\t최선의 손실: 0.911054\t정확도: 62.67%\n", "49\t검증 세트 손실: 0.976935\t최선의 손실: 0.911054\t정확도: 62.00%\n", "50\t검증 세트 손실: 0.985063\t최선의 손실: 0.911054\t정확도: 64.00%\n", "51\t검증 세트 손실: 0.962861\t최선의 손실: 0.911054\t정확도: 61.33%\n", "52\t검증 세트 손실: 1.029825\t최선의 손실: 0.911054\t정확도: 58.67%\n", "53\t검증 세트 손실: 0.998943\t최선의 손실: 0.911054\t정확도: 66.67%\n", "54\t검증 세트 손실: 0.950223\t최선의 손실: 0.911054\t정확도: 67.33%\n", "55\t검증 세트 손실: 1.026375\t최선의 손실: 0.911054\t정확도: 60.00%\n", "56\t검증 세트 손실: 0.959435\t최선의 손실: 0.911054\t정확도: 63.33%\n", "57\t검증 세트 손실: 1.133653\t최선의 손실: 0.911054\t정확도: 60.00%\n", "58\t검증 세트 손실: 1.016981\t최선의 손실: 0.911054\t정확도: 59.33%\n", "59\t검증 세트 손실: 0.985843\t최선의 손실: 0.911054\t정확도: 60.67%\n", "60\t검증 세트 손실: 0.965538\t최선의 손실: 0.911054\t정확도: 66.67%\n", "61\t검증 세트 손실: 0.975704\t최선의 손실: 0.911054\t정확도: 62.00%\n", "62\t검증 세트 손실: 1.097550\t최선의 손실: 0.911054\t정확도: 60.00%\n", "63\t검증 세트 손실: 0.971813\t최선의 손실: 0.911054\t정확도: 62.67%\n", "64\t검증 세트 손실: 0.968235\t최선의 손실: 0.911054\t정확도: 60.00%\n", "65\t검증 세트 손실: 0.977865\t최선의 손실: 0.911054\t정확도: 63.33%\n", "66\t검증 세트 손실: 0.990906\t최선의 손실: 0.911054\t정확도: 62.67%\n", "조기 종료!\n", "전체 훈련 시간: 4.2s\n", "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_five_frozen\n", "최종 테스트 정확도: 63.05%\n" ] } ], "source": [ "# 트레이닝\n", "\n", "import time\n", "\n", "n_epochs = 1000\n", "batch_size = 20\n", "\n", "max_checks_without_progress = 20\n", "checks_without_progress = 0\n", "best_loss = np.infty\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " restore_saver.restore(sess, \"./my_best_mnist_model_0_to_4\")\n", "\n", " t0 = time.time()\n", " \n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " loss_val, acc_val = sess.run([loss, accuracy], feed_dict={X: X_valid2, y: y_valid2})\n", " if loss_val < best_loss:\n", " save_path = five_frozen_saver.save(sess, \"./my_mnist_model_5_to_9_five_frozen\")\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress:\n", " print(\"조기 종료!\")\n", " break\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", "\n", " t1 = time.time()\n", " print(\"전체 훈련 시간: {:.1f}s\".format(t1 - t0))\n", "\n", "with tf.Session() as sess:\n", " five_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_five_frozen\")\n", " acc_test = accuracy.eval(feed_dict={X: X_test2, y: y_test2})\n", " print(\"최종 테스트 정확도: {:.2f}%\".format(acc_test * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### c. 동결된 층을 캐싱하고 모델을 다시 훈련시켜보세요. 얼마나 빨라졌나요?" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [], "source": [ "hidden5_out = tf.get_default_graph().get_tensor_by_name(\"hidden5_out:0\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이제 이전과 거의 동일한 코드로 모델을 훈련합니다. 다른 점은 시작할 때 (훈련 세트와 검증 세트 모두) 동결층의 맨 꼭대기 층의 출력을 계산해서 캐싱하는 것입니다. 이렇게 하면 거의 1.5에서 3배 정도 훈련이 빨라집니다(시스템에 따라 이 수치는 매우 달라집니다)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from ./my_best_mnist_model_0_to_4\n", "0\t검증 세트 손실: 1.333802\t최선의 손실: 1.333802\t정확도: 47.33%\n", "1\t검증 세트 손실: 1.200852\t최선의 손실: 1.200852\t정확도: 50.00%\n", "2\t검증 세트 손실: 1.146627\t최선의 손실: 1.146627\t정확도: 58.00%\n", "3\t검증 세트 손실: 1.089491\t최선의 손실: 1.089491\t정확도: 54.67%\n", "4\t검증 세트 손실: 1.084895\t최선의 손실: 1.084895\t정확도: 52.00%\n", "5\t검증 세트 손실: 1.081458\t최선의 손실: 1.081458\t정확도: 52.00%\n", "6\t검증 세트 손실: 1.027767\t최선의 손실: 1.027767\t정확도: 56.00%\n", "7\t검증 세트 손실: 1.057860\t최선의 손실: 1.027767\t정확도: 56.67%\n", "8\t검증 세트 손실: 1.004813\t최선의 손실: 1.004813\t정확도: 60.00%\n", "9\t검증 세트 손실: 1.156362\t최선의 손실: 1.004813\t정확도: 51.33%\n", "10\t검증 세트 손실: 1.066781\t최선의 손실: 1.004813\t정확도: 55.33%\n", "11\t검증 세트 손실: 1.086581\t최선의 손실: 1.004813\t정확도: 59.33%\n", "12\t검증 세트 손실: 0.991869\t최선의 손실: 0.991869\t정확도: 59.33%\n", "13\t검증 세트 손실: 1.039530\t최선의 손실: 0.991869\t정확도: 61.33%\n", "14\t검증 세트 손실: 1.019834\t최선의 손실: 0.991869\t정확도: 60.00%\n", "15\t검증 세트 손실: 1.009118\t최선의 손실: 0.991869\t정확도: 60.67%\n", "16\t검증 세트 손실: 1.191170\t최선의 손실: 0.991869\t정확도: 54.67%\n", "17\t검증 세트 손실: 1.088326\t최선의 손실: 0.991869\t정확도: 62.67%\n", "18\t검증 세트 손실: 0.998450\t최선의 손실: 0.991869\t정확도: 61.33%\n", "19\t검증 세트 손실: 0.980760\t최선의 손실: 0.980760\t정확도: 61.33%\n", "20\t검증 세트 손실: 0.981014\t최선의 손실: 0.980760\t정확도: 58.00%\n", "21\t검증 세트 손실: 1.009962\t최선의 손실: 0.980760\t정확도: 60.00%\n", "22\t검증 세트 손실: 0.973976\t최선의 손실: 0.973976\t정확도: 62.00%\n", "23\t검증 세트 손실: 0.993164\t최선의 손실: 0.973976\t정확도: 62.00%\n", "24\t검증 세트 손실: 0.977529\t최선의 손실: 0.973976\t정확도: 62.67%\n", "25\t검증 세트 손실: 1.067582\t최선의 손실: 0.973976\t정확도: 59.33%\n", "26\t검증 세트 손실: 0.969430\t최선의 손실: 0.969430\t정확도: 64.00%\n", "27\t검증 세트 손실: 0.969499\t최선의 손실: 0.969430\t정확도: 62.00%\n", "28\t검증 세트 손실: 1.018993\t최선의 손실: 0.969430\t정확도: 59.33%\n", "29\t검증 세트 손실: 1.003491\t최선의 손실: 0.969430\t정확도: 61.33%\n", "30\t검증 세트 손실: 1.116068\t최선의 손실: 0.969430\t정확도: 58.00%\n", "31\t검증 세트 손실: 1.115351\t최선의 손실: 0.969430\t정확도: 52.67%\n", "32\t검증 세트 손실: 0.969186\t최선의 손실: 0.969186\t정확도: 64.67%\n", "33\t검증 세트 손실: 0.990112\t최선의 손실: 0.969186\t정확도: 61.33%\n", "34\t검증 세트 손실: 0.992284\t최선의 손실: 0.969186\t정확도: 62.67%\n", "35\t검증 세트 손실: 0.959815\t최선의 손실: 0.959815\t정확도: 63.33%\n", "36\t검증 세트 손실: 1.023252\t최선의 손실: 0.959815\t정확도: 61.33%\n", "37\t검증 세트 손실: 0.935711\t최선의 손실: 0.935711\t정확도: 63.33%\n", "38\t검증 세트 손실: 0.987073\t최선의 손실: 0.935711\t정확도: 60.00%\n", "39\t검증 세트 손실: 0.990505\t최선의 손실: 0.935711\t정확도: 64.00%\n", "40\t검증 세트 손실: 1.001102\t최선의 손실: 0.935711\t정확도: 62.67%\n", "41\t검증 세트 손실: 0.967566\t최선의 손실: 0.935711\t정확도: 60.00%\n", "42\t검증 세트 손실: 0.970786\t최선의 손실: 0.935711\t정확도: 60.67%\n", "43\t검증 세트 손실: 0.987635\t최선의 손실: 0.935711\t정확도: 62.00%\n", "44\t검증 세트 손실: 0.954460\t최선의 손실: 0.935711\t정확도: 61.33%\n", "45\t검증 세트 손실: 0.935901\t최선의 손실: 0.935711\t정확도: 63.33%\n", "46\t검증 세트 손실: 0.953629\t최선의 손실: 0.935711\t정확도: 62.67%\n", "47\t검증 세트 손실: 0.960957\t최선의 손실: 0.935711\t정확도: 63.33%\n", "48\t검증 세트 손실: 0.958055\t최선의 손실: 0.935711\t정확도: 64.00%\n", "49\t검증 세트 손실: 0.990713\t최선의 손실: 0.935711\t정확도: 64.00%\n", "50\t검증 세트 손실: 0.970080\t최선의 손실: 0.935711\t정확도: 64.00%\n", "51\t검증 세트 손실: 0.934411\t최선의 손실: 0.934411\t정확도: 64.67%\n", "52\t검증 세트 손실: 0.978166\t최선의 손실: 0.934411\t정확도: 63.33%\n", "53\t검증 세트 손실: 0.934472\t최선의 손실: 0.934411\t정확도: 64.67%\n", "54\t검증 세트 손실: 0.958419\t최선의 손실: 0.934411\t정확도: 62.00%\n", "55\t검증 세트 손실: 0.967518\t최선의 손실: 0.934411\t정확도: 64.67%\n", "56\t검증 세트 손실: 0.968303\t최선의 손실: 0.934411\t정확도: 62.67%\n", "57\t검증 세트 손실: 0.969038\t최선의 손실: 0.934411\t정확도: 65.33%\n", "58\t검증 세트 손실: 0.971084\t최선의 손실: 0.934411\t정확도: 65.33%\n", "59\t검증 세트 손실: 0.973698\t최선의 손실: 0.934411\t정확도: 62.67%\n", "60\t검증 세트 손실: 0.997426\t최선의 손실: 0.934411\t정확도: 62.67%\n", "61\t검증 세트 손실: 0.945805\t최선의 손실: 0.934411\t정확도: 64.67%\n", "62\t검증 세트 손실: 0.953005\t최선의 손실: 0.934411\t정확도: 63.33%\n", "63\t검증 세트 손실: 0.959695\t최선의 손실: 0.934411\t정확도: 66.00%\n", "64\t검증 세트 손실: 0.971894\t최선의 손실: 0.934411\t정확도: 64.00%\n", "65\t검증 세트 손실: 1.003221\t최선의 손실: 0.934411\t정확도: 59.33%\n", "66\t검증 세트 손실: 1.020634\t최선의 손실: 0.934411\t정확도: 62.00%\n", "67\t검증 세트 손실: 1.039943\t최선의 손실: 0.934411\t정확도: 62.00%\n", "68\t검증 세트 손실: 0.948729\t최선의 손실: 0.934411\t정확도: 66.67%\n", "69\t검증 세트 손실: 0.967183\t최선의 손실: 0.934411\t정확도: 64.00%\n", "70\t검증 세트 손실: 0.953779\t최선의 손실: 0.934411\t정확도: 64.00%\n", "71\t검증 세트 손실: 0.936941\t최선의 손실: 0.934411\t정확도: 64.00%\n", "조기 종료!\n", "전체 훈련 시간: 4.1s\n", "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_five_frozen\n", "최종 테스트 정확도: 61.88%\n" ] } ], "source": [ "import time\n", "\n", "n_epochs = 1000\n", "batch_size = 20\n", "\n", "max_checks_without_progress = 20\n", "checks_without_progress = 0\n", "best_loss = np.infty\n", "\n", "# log write ready\n", "graph_writer = tf.summary.FileWriter(\"graph_logs\", tf.Session().graph)\n", "# log write close\n", "graph_writer.close()\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " restore_saver.restore(sess, \"./my_best_mnist_model_0_to_4\")\n", "\n", " t0 = time.time()\n", " \n", " hidden5_train = hidden5_out.eval(feed_dict={X: X_train2, y: y_train2})\n", " hidden5_valid = hidden5_out.eval(feed_dict={X: X_valid2, y: y_valid2})\n", " \n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " h5_batch, y_batch = hidden5_train[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={hidden5_out: h5_batch, y: y_batch})\n", " loss_val, acc_val = sess.run([loss, accuracy], feed_dict={hidden5_out: hidden5_valid, y: y_valid2})\n", " if loss_val < best_loss:\n", " save_path = five_frozen_saver.save(sess, \"./my_mnist_model_5_to_9_five_frozen\")\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress:\n", " print(\"조기 종료!\")\n", " break\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", "\n", " t1 = time.time()\n", " print(\"전체 훈련 시간: {:.1f}s\".format(t1 - t0))\n", "\n", "with tf.Session() as sess:\n", " five_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_five_frozen\")\n", " acc_test = accuracy.eval(feed_dict={X: X_test2, y: y_test2})\n", " print(\"최종 테스트 정확도: {:.2f}%\".format(acc_test * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### d. 다섯 개 대신 네 개의 은닉층만 재사용하여 다시 시도해보세요. 높은 성능을 얻을 수 있나요?" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_outputs = 5\n", "\n", "restore_saver = tf.train.import_meta_graph(\"./my_best_mnist_model_0_to_4.meta\")\n", "\n", "X = tf.get_default_graph().get_tensor_by_name(\"X:0\")\n", "y = tf.get_default_graph().get_tensor_by_name(\"y:0\")\n", "\n", "hidden4_out = tf.get_default_graph().get_tensor_by_name(\"hidden4_out:0\")\n", "logits = tf.layers.dense(hidden4_out, n_outputs, kernel_initializer=he_init, name=\"new_logits\") \n", "# 4번째 출력에서 output이 되는 tf.layers.dense() 생성\n", "Y_proba = tf.nn.softmax(logits)\n", "xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", "loss = tf.reduce_mean(xentropy)\n", "correct = tf.nn.in_top_k(logits, y, 1)\n", "accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name=\"accuracy\")" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "learning_rate = 0.01\n", "\n", "output_layer_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=\"new_logits\")\n", "#new_logits만 학습에 사용\n", "optimizer = tf.train.AdamOptimizer(learning_rate, name=\"Adam2\")\n", "training_op = optimizer.minimize(loss, var_list=output_layer_vars)\n", "\n", "init = tf.global_variables_initializer()\n", "four_frozen_saver = tf.train.Saver()" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from ./my_best_mnist_model_0_to_4\n", "0\t검증 세트 손실: 1.139616\t최선의 손실: 1.139616\t정확도: 50.00%\n", "1\t검증 세트 손실: 1.185013\t최선의 손실: 1.139616\t정확도: 52.67%\n", "2\t검증 세트 손실: 1.099084\t최선의 손실: 1.099084\t정확도: 50.00%\n", "3\t검증 세트 손실: 1.048637\t최선의 손실: 1.048637\t정확도: 54.00%\n", "4\t검증 세트 손실: 1.054338\t최선의 손실: 1.048637\t정확도: 58.00%\n", "5\t검증 세트 손실: 1.017123\t최선의 손실: 1.017123\t정확도: 60.67%\n", "6\t검증 세트 손실: 0.984236\t최선의 손실: 0.984236\t정확도: 62.67%\n", "7\t검증 세트 손실: 0.983788\t최선의 손실: 0.983788\t정확도: 63.33%\n", "8\t검증 세트 손실: 0.950740\t최선의 손실: 0.950740\t정확도: 63.33%\n", "9\t검증 세트 손실: 0.958342\t최선의 손실: 0.950740\t정확도: 63.33%\n", "10\t검증 세트 손실: 0.952328\t최선의 손실: 0.950740\t정확도: 64.00%\n", "11\t검증 세트 손실: 1.025500\t최선의 손실: 0.950740\t정확도: 62.00%\n", "12\t검증 세트 손실: 0.958744\t최선의 손실: 0.950740\t정확도: 64.67%\n", "13\t검증 세트 손실: 0.917655\t최선의 손실: 0.917655\t정확도: 66.67%\n", "14\t검증 세트 손실: 0.957702\t최선의 손실: 0.917655\t정확도: 61.33%\n", "15\t검증 세트 손실: 0.924674\t최선의 손실: 0.917655\t정확도: 64.00%\n", "16\t검증 세트 손실: 0.920495\t최선의 손실: 0.917655\t정확도: 66.00%\n", "17\t검증 세트 손실: 0.892814\t최선의 손실: 0.892814\t정확도: 66.00%\n", "18\t검증 세트 손실: 0.872195\t최선의 손실: 0.872195\t정확도: 66.00%\n", "19\t검증 세트 손실: 0.892904\t최선의 손실: 0.872195\t정확도: 66.67%\n", "20\t검증 세트 손실: 0.889421\t최선의 손실: 0.872195\t정확도: 66.00%\n", "21\t검증 세트 손실: 0.881152\t최선의 손실: 0.872195\t정확도: 68.00%\n", "22\t검증 세트 손실: 0.866921\t최선의 손실: 0.866921\t정확도: 67.33%\n", "23\t검증 세트 손실: 0.871649\t최선의 손실: 0.866921\t정확도: 68.67%\n", "24\t검증 세트 손실: 0.894888\t최선의 손실: 0.866921\t정확도: 66.00%\n", "25\t검증 세트 손실: 0.856393\t최선의 손실: 0.856393\t정확도: 68.00%\n", "26\t검증 세트 손실: 0.867952\t최선의 손실: 0.856393\t정확도: 67.33%\n", "27\t검증 세트 손실: 0.870208\t최선의 손실: 0.856393\t정확도: 65.33%\n", "28\t검증 세트 손실: 0.841316\t최선의 손실: 0.841316\t정확도: 68.00%\n", "29\t검증 세트 손실: 0.863417\t최선의 손실: 0.841316\t정확도: 64.67%\n", "30\t검증 세트 손실: 0.861775\t최선의 손실: 0.841316\t정확도: 64.67%\n", "31\t검증 세트 손실: 0.850281\t최선의 손실: 0.841316\t정확도: 66.67%\n", "32\t검증 세트 손실: 0.825028\t최선의 손실: 0.825028\t정확도: 68.67%\n", "33\t검증 세트 손실: 0.865702\t최선의 손실: 0.825028\t정확도: 66.00%\n", "34\t검증 세트 손실: 0.862230\t최선의 손실: 0.825028\t정확도: 67.33%\n", "35\t검증 세트 손실: 0.850902\t최선의 손실: 0.825028\t정확도: 67.33%\n", "36\t검증 세트 손실: 0.845517\t최선의 손실: 0.825028\t정확도: 69.33%\n", "37\t검증 세트 손실: 0.870964\t최선의 손실: 0.825028\t정확도: 66.67%\n", "38\t검증 세트 손실: 0.877448\t최선의 손실: 0.825028\t정확도: 66.00%\n", "39\t검증 세트 손실: 0.832228\t최선의 손실: 0.825028\t정확도: 67.33%\n", "40\t검증 세트 손실: 0.854634\t최선의 손실: 0.825028\t정확도: 66.67%\n", "41\t검증 세트 손실: 0.852173\t최선의 손실: 0.825028\t정확도: 64.67%\n", "42\t검증 세트 손실: 0.844199\t최선의 손실: 0.825028\t정확도: 66.67%\n", "43\t검증 세트 손실: 0.850534\t최선의 손실: 0.825028\t정확도: 68.67%\n", "44\t검증 세트 손실: 0.832602\t최선의 손실: 0.825028\t정확도: 69.33%\n", "45\t검증 세트 손실: 0.838961\t최선의 손실: 0.825028\t정확도: 67.33%\n", "46\t검증 세트 손실: 0.812793\t최선의 손실: 0.812793\t정확도: 70.00%\n", "47\t검증 세트 손실: 0.835180\t최선의 손실: 0.812793\t정확도: 70.67%\n", "48\t검증 세트 손실: 0.841379\t최선의 손실: 0.812793\t정확도: 70.00%\n", "49\t검증 세트 손실: 0.826609\t최선의 손실: 0.812793\t정확도: 70.00%\n", "50\t검증 세트 손실: 0.860543\t최선의 손실: 0.812793\t정확도: 70.00%\n", "51\t검증 세트 손실: 0.828098\t최선의 손실: 0.812793\t정확도: 67.33%\n", "52\t검증 세트 손실: 0.826039\t최선의 손실: 0.812793\t정확도: 69.33%\n", "53\t검증 세트 손실: 0.872143\t최선의 손실: 0.812793\t정확도: 68.67%\n", "54\t검증 세트 손실: 0.820189\t최선의 손실: 0.812793\t정확도: 70.67%\n", "55\t검증 세트 손실: 0.847227\t최선의 손실: 0.812793\t정확도: 68.00%\n", "56\t검증 세트 손실: 0.815350\t최선의 손실: 0.812793\t정확도: 70.00%\n", "57\t검증 세트 손실: 0.924428\t최선의 손실: 0.812793\t정확도: 66.67%\n", "58\t검증 세트 손실: 0.884343\t최선의 손실: 0.812793\t정확도: 68.00%\n", "59\t검증 세트 손실: 0.836386\t최선의 손실: 0.812793\t정확도: 71.33%\n", "60\t검증 세트 손실: 0.843796\t최선의 손실: 0.812793\t정확도: 69.33%\n", "61\t검증 세트 손실: 0.813301\t최선의 손실: 0.812793\t정확도: 64.67%\n", "62\t검증 세트 손실: 0.951133\t최선의 손실: 0.812793\t정확도: 70.00%\n", "63\t검증 세트 손실: 0.838499\t최선의 손실: 0.812793\t정확도: 68.00%\n", "64\t검증 세트 손실: 0.824030\t최선의 손실: 0.812793\t정확도: 71.33%\n", "65\t검증 세트 손실: 0.844819\t최선의 손실: 0.812793\t정확도: 71.33%\n", "66\t검증 세트 손실: 0.875496\t최선의 손실: 0.812793\t정확도: 70.67%\n", "조기 종료!\n", "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_four_frozen\n", "최종 테스트 정확도: 67.52%\n" ] } ], "source": [ "n_epochs = 1000\n", "batch_size = 20\n", "\n", "max_checks_without_progress = 20\n", "checks_without_progress = 0\n", "best_loss = np.infty\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " restore_saver.restore(sess, \"./my_best_mnist_model_0_to_4\")\n", " \n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " loss_val, acc_val = sess.run([loss, accuracy], feed_dict={X: X_valid2, y: y_valid2})\n", " if loss_val < best_loss:\n", " save_path = four_frozen_saver.save(sess, \"./my_mnist_model_5_to_9_four_frozen\")\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress:\n", " print(\"조기 종료!\")\n", " break\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", "\n", "with tf.Session() as sess:\n", " four_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_four_frozen\")\n", " acc_test = accuracy.eval(feed_dict={X: X_test2, y: y_test2})\n", " print(\"최종 테스트 정확도: {:.2f}%\".format(acc_test * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex9-2.png](./img/ex9-2.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### e. 이제 최상단 두 개 층의 동결을 해제하고 훈련을 계속 해보세요. 더 나은 모델을 얻을 수 있나요?" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "learning_rate = 0.01\n", "\n", "unfrozen_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=\"hidden[34]|new_logits\")\n", "optimizer = tf.train.AdamOptimizer(learning_rate, name=\"Adam3\")\n", "training_op = optimizer.minimize(loss, var_list=unfrozen_vars)\n", "\n", "init = tf.global_variables_initializer()\n", "two_frozen_saver = tf.train.Saver()" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_four_frozen\n", "0\t검증 세트 손실: 1.105117\t최선의 손실: 1.105117\t정확도: 69.33%\n", "1\t검증 세트 손실: 1.158623\t최선의 손실: 1.105117\t정확도: 72.00%\n", "2\t검증 세트 손실: 0.755095\t최선의 손실: 0.755095\t정확도: 78.67%\n", "3\t검증 세트 손실: 0.876163\t최선의 손실: 0.755095\t정확도: 80.00%\n", "4\t검증 세트 손실: 1.087565\t최선의 손실: 0.755095\t정확도: 73.33%\n", "5\t검증 세트 손실: 1.029427\t최선의 손실: 0.755095\t정확도: 75.33%\n", "6\t검증 세트 손실: 1.043349\t최선의 손실: 0.755095\t정확도: 77.33%\n", "7\t검증 세트 손실: 1.450104\t최선의 손실: 0.755095\t정확도: 80.00%\n", "8\t검증 세트 손실: 1.433299\t최선의 손실: 0.755095\t정확도: 77.33%\n", "9\t검증 세트 손실: 1.574803\t최선의 손실: 0.755095\t정확도: 75.33%\n", "10\t검증 세트 손실: 1.492265\t최선의 손실: 0.755095\t정확도: 82.00%\n", "11\t검증 세트 손실: 1.585494\t최선의 손실: 0.755095\t정확도: 80.00%\n", "12\t검증 세트 손실: 1.003593\t최선의 손실: 0.755095\t정확도: 80.67%\n", "13\t검증 세트 손실: 1.486751\t최선의 손실: 0.755095\t정확도: 77.33%\n", "14\t검증 세트 손실: 1.418568\t최선의 손실: 0.755095\t정확도: 82.00%\n", "15\t검증 세트 손실: 1.157110\t최선의 손실: 0.755095\t정확도: 81.33%\n", "16\t검증 세트 손실: 1.362491\t최선의 손실: 0.755095\t정확도: 80.00%\n", "17\t검증 세트 손실: 1.362268\t최선의 손실: 0.755095\t정확도: 78.00%\n", "18\t검증 세트 손실: 1.627404\t최선의 손실: 0.755095\t정확도: 76.00%\n", "19\t검증 세트 손실: 1.500786\t최선의 손실: 0.755095\t정확도: 78.67%\n", "20\t검증 세트 손실: 1.555152\t최선의 손실: 0.755095\t정확도: 75.33%\n", "21\t검증 세트 손실: 1.389948\t최선의 손실: 0.755095\t정확도: 76.67%\n", "22\t검증 세트 손실: 1.327482\t최선의 손실: 0.755095\t정확도: 82.00%\n", "조기 종료!\n", "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_two_frozen\n", "최종 테스트 정확도: 74.08%\n" ] } ], "source": [ "# 재훈련\n", "\n", "n_epochs = 1000\n", "batch_size = 20\n", "\n", "max_checks_without_progress = 20\n", "checks_without_progress = 0\n", "best_loss = np.infty\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " four_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_four_frozen\")\n", " \n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " loss_val, acc_val = sess.run([loss, accuracy], feed_dict={X: X_valid2, y: y_valid2})\n", " if loss_val < best_loss:\n", " save_path = two_frozen_saver.save(sess, \"./my_mnist_model_5_to_9_two_frozen\")\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress:\n", " print(\"조기 종료!\")\n", " break\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", "\n", "with tf.Session() as sess:\n", " two_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_two_frozen\")\n", " acc_test = accuracy.eval(feed_dict={X: X_test2, y: y_test2})\n", " print(\"최종 테스트 정확도: {:.2f}%\".format(acc_test * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex9-3.png](./img/ex9-3.png)" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [], "source": [ "# 전체 재 훈련을 위해\n", "learning_rate = 0.01\n", "\n", "optimizer = tf.train.AdamOptimizer(learning_rate, name=\"Adam4\")\n", "training_op = optimizer.minimize(loss)\n", "\n", "init = tf.global_variables_initializer()\n", "no_frozen_saver = tf.train.Saver()" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_two_frozen\n", "0\t검증 세트 손실: 0.915181\t최선의 손실: 0.915181\t정확도: 76.67%\n", "1\t검증 세트 손실: 0.532479\t최선의 손실: 0.532479\t정확도: 84.00%\n", "2\t검증 세트 손실: 0.407154\t최선의 손실: 0.407154\t정확도: 90.67%\n", "3\t검증 세트 손실: 0.480797\t최선의 손실: 0.407154\t정확도: 88.00%\n", "4\t검증 세트 손실: 0.312727\t최선의 손실: 0.312727\t정확도: 90.67%\n", "5\t검증 세트 손실: 0.572035\t최선의 손실: 0.312727\t정확도: 92.67%\n", "6\t검증 세트 손실: 0.285512\t최선의 손실: 0.285512\t정확도: 92.00%\n", "7\t검증 세트 손실: 0.320394\t최선의 손실: 0.285512\t정확도: 92.67%\n", "8\t검증 세트 손실: 0.550538\t최선의 손실: 0.285512\t정확도: 90.00%\n", "9\t검증 세트 손실: 0.558102\t최선의 손실: 0.285512\t정확도: 91.33%\n", "10\t검증 세트 손실: 0.568721\t최선의 손실: 0.285512\t정확도: 90.67%\n", "11\t검증 세트 손실: 0.610730\t최선의 손실: 0.285512\t정확도: 90.00%\n", "12\t검증 세트 손실: 0.543822\t최선의 손실: 0.285512\t정확도: 92.67%\n", "13\t검증 세트 손실: 0.557883\t최선의 손실: 0.285512\t정확도: 92.67%\n", "14\t검증 세트 손실: 0.867094\t최선의 손실: 0.285512\t정확도: 85.33%\n", "15\t검증 세트 손실: 0.771359\t최선의 손실: 0.285512\t정확도: 88.00%\n", "16\t검증 세트 손실: 1.002985\t최선의 손실: 0.285512\t정확도: 84.67%\n", "17\t검증 세트 손실: 0.467146\t최선의 손실: 0.285512\t정확도: 91.33%\n", "18\t검증 세트 손실: 0.775924\t최선의 손실: 0.285512\t정확도: 91.33%\n", "19\t검증 세트 손실: 0.590724\t최선의 손실: 0.285512\t정확도: 87.33%\n", "20\t검증 세트 손실: 0.768428\t최선의 손실: 0.285512\t정확도: 89.33%\n", "21\t검증 세트 손실: 0.556964\t최선의 손실: 0.285512\t정확도: 90.67%\n", "22\t검증 세트 손실: 0.766854\t최선의 손실: 0.285512\t정확도: 89.33%\n", "23\t검증 세트 손실: 0.696021\t최선의 손실: 0.285512\t정확도: 90.00%\n", "24\t검증 세트 손실: 1.635948\t최선의 손실: 0.285512\t정확도: 87.33%\n", "25\t검증 세트 손실: 0.801530\t최선의 손실: 0.285512\t정확도: 86.00%\n", "26\t검증 세트 손실: 1.913011\t최선의 손실: 0.285512\t정확도: 87.33%\n", "조기 종료!\n", "INFO:tensorflow:Restoring parameters from ./my_mnist_model_5_to_9_no_frozen\n", "최종 테스트 정확도: 89.90%\n" ] } ], "source": [ "n_epochs = 1000\n", "batch_size = 20\n", "\n", "max_checks_without_progress = 20\n", "checks_without_progress = 0\n", "best_loss = np.infty\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " two_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_two_frozen\")\n", " \n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " loss_val, acc_val = sess.run([loss, accuracy], feed_dict={X: X_valid2, y: y_valid2})\n", " if loss_val < best_loss:\n", " save_path = no_frozen_saver.save(sess, \"./my_mnist_model_5_to_9_no_frozen\")\n", " best_loss = loss_val\n", " checks_without_progress = 0\n", " else:\n", " checks_without_progress += 1\n", " if checks_without_progress > max_checks_without_progress:\n", " print(\"조기 종료!\")\n", " break\n", " print(\"{}\\t검증 세트 손실: {:.6f}\\t최선의 손실: {:.6f}\\t정확도: {:.2f}%\".format(\n", " epoch, loss_val, best_loss, acc_val * 100))\n", "\n", "with tf.Session() as sess:\n", " no_frozen_saver.restore(sess, \"./my_mnist_model_5_to_9_no_frozen\")\n", " acc_test = accuracy.eval(feed_dict={X: X_test2, y: y_test2})\n", " print(\"최종 테스트 정확도: {:.2f}%\".format(acc_test * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex9-4.png](./img/ex9-4.png)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\t검증 세트 손실: 0.674593\t최선의 손실: 0.674593\t정확도: 80.67%\n", "1\t검증 세트 손실: 0.584863\t최선의 손실: 0.584863\t정확도: 88.67%\n", "2\t검증 세트 손실: 0.647398\t최선의 손실: 0.584863\t정확도: 84.00%\n", "3\t검증 세트 손실: 0.530267\t최선의 손실: 0.530267\t정확도: 87.33%\n", "4\t검증 세트 손실: 0.685232\t최선의 손실: 0.530267\t정확도: 90.00%\n", "5\t검증 세트 손실: 0.528450\t최선의 손실: 0.528450\t정확도: 89.33%\n", "6\t검증 세트 손실: 0.787493\t최선의 손실: 0.528450\t정확도: 89.33%\n", "7\t검증 세트 손실: 0.688933\t최선의 손실: 0.528450\t정확도: 88.00%\n", "8\t검증 세트 손실: 0.635708\t최선의 손실: 0.528450\t정확도: 87.33%\n", "9\t검증 세트 손실: 0.457762\t최선의 손실: 0.457762\t정확도: 93.33%\n", "10\t검증 세트 손실: 0.706879\t최선의 손실: 0.457762\t정확도: 87.33%\n", "11\t검증 세트 손실: 1.013173\t최선의 손실: 0.457762\t정확도: 88.00%\n", "12\t검증 세트 손실: 1.054063\t최선의 손실: 0.457762\t정확도: 85.33%\n", "13\t검증 세트 손실: 0.755688\t최선의 손실: 0.457762\t정확도: 89.33%\n", "14\t검증 세트 손실: 0.779543\t최선의 손실: 0.457762\t정확도: 92.00%\n", "15\t검증 세트 손실: 1.277703\t최선의 손실: 0.457762\t정확도: 89.33%\n", "16\t검증 세트 손실: 0.977737\t최선의 손실: 0.457762\t정확도: 86.67%\n", "17\t검증 세트 손실: 0.576649\t최선의 손실: 0.457762\t정확도: 90.67%\n", "18\t검증 세트 손실: 0.890895\t최선의 손실: 0.457762\t정확도: 89.33%\n", "19\t검증 세트 손실: 0.827233\t최선의 손실: 0.457762\t정확도: 91.33%\n", "20\t검증 세트 손실: 1.025580\t최선의 손실: 0.457762\t정확도: 90.67%\n", "21\t검증 세트 손실: 1.131445\t최선의 손실: 0.457762\t정확도: 89.33%\n", "22\t검증 세트 손실: 1.422391\t최선의 손실: 0.457762\t정확도: 89.33%\n", "23\t검증 세트 손실: 1.076609\t최선의 손실: 0.457762\t정확도: 90.67%\n", "24\t검증 세트 손실: 0.703393\t최선의 손실: 0.457762\t정확도: 92.00%\n", "25\t검증 세트 손실: 1.146832\t최선의 손실: 0.457762\t정확도: 92.00%\n", "26\t검증 세트 손실: 1.419385\t최선의 손실: 0.457762\t정확도: 92.00%\n", "27\t검증 세트 손실: 1.446589\t최선의 손실: 0.457762\t정확도: 92.00%\n", "28\t검증 세트 손실: 1.451799\t최선의 손실: 0.457762\t정확도: 92.00%\n", "29\t검증 세트 손실: 1.455441\t최선의 손실: 0.457762\t정확도: 92.00%\n", "30\t검증 세트 손실: 1.458811\t최선의 손실: 0.457762\t정확도: 92.00%\n", "조기 종료!\n" ] }, { "data": { "text/plain": [ "DNNClassifier(activation=,\n", " batch_norm_momentum=None, batch_size=20, dropout_rate=None,\n", " initializer=,\n", " learning_rate=0.01, n_hidden_layers=4, n_neurons=100,\n", " optimizer_class=,\n", " random_state=42)" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dnn_clf_5_to_9 = DNNClassifier(n_hidden_layers=4, random_state=42)\n", "dnn_clf_5_to_9.fit(X_train2, y_train2, n_epochs=1000, X_valid=X_valid2, y_valid=y_valid2)" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.8813001440032915" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = dnn_clf_5_to_9.predict(X_test2)\n", "accuracy_score(y_test2, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex9-5.png](./img/ex9-5.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 10. 보조 작업으로 사전훈련\n", "\n", "#### a. 이 예제에서는 두 개의 MNIST 숫자 이미지를 비교해서 두 이미지가 같은 숫자인지 아닌지 예측하는 DNN을 만들 것입니다. 그런 다음 이 네트워크의 하위층을 재사용하여 아주 적은 훈련 데이터로 MNIST 분류기를 훈련시킬 것입니다. 두 개의 DNN을 만드세요(이들을 DNN A, B라고 부르겠습니다). 이들은 출력층이 없는 것만 빼고는 앞서 만든 것과 비슷합니다. 각 DNN은 다섯 개의 층을 가졌고 각 층은 He 초기화와 ELU 활성화 함수를 사용한 100개의 뉴런으로 되어 있습니다. 그 다음에는 두 DNN 위에 하나의 출력층을 추가합니다. 텐서플로의 concat() 함수를 axis=1로 지정해서 수평축을 따라 두 DNN의 출력을 연결합니다. 그리고 그 결과를 출력층에 주입합니다. 출력층에는 로지스틱 활성화 함수를 사용하는 하나의 뉴런만 있어야 합니다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "두 개의 입력 플레이스홀더 `X1`과 `X2`를 준비할 수 있습니다. 하나는 첫 번째 DNN에 이미지를 주입하기 위해서이고 다른 하나는 두 번째 DNN에 이미지를 주입하기 위해서입니다. 이렇게 해도 되지만 다른 방법은 두 이미지를 위해 하나의 입력 플레이스홀더를 만드는 것입니다(각 행은 이미지의 쌍을 담고 있습니다). 그리고 `tf.unstack()` 함수를 사용하여 다음과 같이 이 텐서를 두 개의 텐서로 나눕니다:" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "n_inputs = 28 * 28 # MNIST\n", "\n", "reset_graph()\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, 2, n_inputs), name=\"X\")\n", "X1, X2 = tf.unstack(X, axis=1) # 하나의 플레이스 홀더를 두개의 텐서로 나누는 구문" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "레이블을 위한 플레이스홀더도 필요합니다. 각 레이블은 이미지가 다른 숫자일 때는 0, 그렇지 않고 동일한 숫자일 때는 1이 됩니다:" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "y = tf.placeholder(tf.int32, shape=[None, 1])" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [], "source": [ "dnn1 = dnn(X1, name=\"DNN_A\")\n", "dnn2 = dnn(X2, name=\"DNN_B\") \n", "\n", "dnn_outputs = tf.concat([dnn1, dnn2], axis=1)" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "TensorShape([Dimension(None), Dimension(200)])" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dnn_outputs.shape" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "# dnn 출력에 새로운 출력\n", "# logits에는 hidden의 출력이 1개의 뉴런으로 들어감.\n", "hidden = tf.layers.dense(dnn_outputs, units=10, activation=tf.nn.elu, kernel_initializer=he_init)\n", "logits = tf.layers.dense(hidden, units=1, kernel_initializer=he_init)\n", "y_proba = tf.nn.sigmoid(logits)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "전체 네트워크는 `y_proba >= 0.5`이면 `1`을 예측하고(즉, 신경망이 두 이미지가 동일한 숫자라고 예측합니다) 그렇지 않으면 `0`을 출력합니다. 여기서는 이와 동일하고 불필요한 계산을 줄이기 위해 `logits >= 0`을 계산합니다:" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [], "source": [ "y_pred = tf.cast(tf.greater_equal(logits, 0), tf.int32)" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "# loss\n", "y_as_float = tf.cast(y, tf.float32)\n", "xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=y_as_float, logits=logits)\n", "loss = tf.reduce_mean(xentropy)\n", "\n", "# optimizer\n", "learning_rate = 0.01\n", "momentum = 0.95\n", "\n", "optimizer = tf.train.MomentumOptimizer(learning_rate, momentum, use_nesterov=True)\n", "training_op = optimizer.minimize(loss)\n", "\n", "# acc\n", "y_pred_correct = tf.equal(y_pred, y)\n", "accuracy = tf.reduce_mean(tf.cast(y_pred_correct, tf.float32))" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "init = tf.global_variables_initializer()\n", "saver = tf.train.Saver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex10-1.png](./img/ex10-1.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### b. MNIST 데이터셋을 두 개로 나눕니다. 분할 #1은 55,000개의 이미지, 분할 #2는 5,000개의 이미지를 담고 있어야 합니다. 분할 #1로부터 선택한 MNIST 이미지 한 쌍을 샘플로 가지는 훈련 배치를 생성하는 함수를 만드세요. 훈련 세트의 절반은 같은 클래스에 속하는 이미지 쌍이어야 합니다. 나머지 절반은 다른 클래스로부터 추출한 이미지여야 합니다. 각 쌍에 대해 훈련 레이블은 이미지가 같은 클래스면 0, 다른 클래스면 1이어야 합니다." ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [], "source": [ "X_train1 = X_train\n", "y_train1 = y_train\n", "\n", "X_train2 = X_valid\n", "y_train2 = y_valid\n", "\n", "X_test = X_test\n", "y_test = y_test" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이미지 쌍을 생성하는 함수를 만듭니다: 50%는 같은 숫자이고 나머지 50%는 다른 숫자여야 합니다. 방법은 많이 있습니다. 여기에서는 생성할 동일한 쌍(즉 같은 숫자의 이미지)과 다른 쌍(다른 숫자의 이미지)이 얼마나 많은지 계산합니다. 간단하게 `batch_size // 2`를 사용해 계산하고 홀수일 경우를 대비한 코드를 추가합니다. 그리고 무작위로 쌍을 골라서 같은 숫자에 해당하는 쌍을 선택합니다. 그다음 다른 숫자의 쌍을 채웁니다. 마지막으로 배치를 섞고 반환합니다:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [], "source": [ "def generate_batch(images, labels, batch_size):\n", " size1 = batch_size // 2\n", " size2 = batch_size - size1\n", " if size1 != size2 and np.random.rand() > 0.5:\n", " size1, size2 = size2, size1\n", " X = []\n", " y = []\n", " while len(X) < size1:\n", " rnd_idx1, rnd_idx2 = np.random.randint(0, len(images), 2)\n", " if rnd_idx1 != rnd_idx2 and labels[rnd_idx1] == labels[rnd_idx2]:\n", " X.append(np.array([images[rnd_idx1], images[rnd_idx2]]))\n", " y.append([1])\n", " while len(X) < batch_size:\n", " rnd_idx1, rnd_idx2 = np.random.randint(0, len(images), 2)\n", " if labels[rnd_idx1] != labels[rnd_idx2]:\n", " X.append(np.array([images[rnd_idx1], images[rnd_idx2]]))\n", " y.append([0])\n", " rnd_indices = np.random.permutation(batch_size)\n", " return np.array(X)[rnd_indices], np.array(y)[rnd_indices]" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "# batch size 5개로 테스트 \n", "batch_size = 5\n", "X_batch, y_batch = generate_batch(X_train1, y_train1, batch_size)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(5, 2, 784)" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_batch.shape\n", "# batch_size : 5, DNN 쌍 : 2, 28*28 : 784" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(3, 3 * batch_size))\n", "plt.subplot(121)\n", "plt.imshow(X_batch[:,0].reshape(28 * batch_size, 28), cmap=\"binary\", interpolation=\"nearest\")\n", "plt.axis('off')\n", "plt.subplot(122)\n", "plt.imshow(X_batch[:,1].reshape(28 * batch_size, 28), cmap=\"binary\", interpolation=\"nearest\")\n", "plt.axis('off')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1],\n", " [0],\n", " [0],\n", " [1],\n", " [0]])" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 서로 다른 수는 0, 같은 수는 1\n", "y_batch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### c. 이 훈련 세트로 DNN을 훈련시키세요. 각각의 이미지 쌍에서 첫 번째 이미지는 DNN A에, 두 번째 이미지는 DNN B에 동시에 주입합니다. 전체 네트워크는 두 이미지가 같은 클래스인지 아닌지 구분하도록 점차 학습될 것입니다." ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "X_test1, y_test1 = generate_batch(X_test, y_test, batch_size=len(X_test))" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "((10000, 784), (10000, 2, 784))" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_test.shape, X_test1.shape\n", "# batch_size : 10000, DNN 쌍 : 2, 28*28 : 784" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 훈련 손실: 0.6910327\n", "0 테스트 정확도: 0.542\n", "1 훈련 손실: 0.60353535\n", "2 훈련 손실: 0.54946035\n", "3 훈련 손실: 0.47047246\n", "4 훈련 손실: 0.4060757\n", "5 훈련 손실: 0.38308153\n", "5 테스트 정확도: 0.824\n", "6 훈련 손실: 0.39047274\n", "7 훈련 손실: 0.33907944\n", "8 훈련 손실: 0.32106704\n", "9 훈련 손실: 0.31792682\n", "10 훈련 손실: 0.2449429\n", "10 테스트 정확도: 0.8881\n", "11 훈련 손실: 0.2929237\n", "12 훈련 손실: 0.2322544\n", "13 훈련 손실: 0.23180936\n", "14 훈련 손실: 0.19877945\n", "15 훈련 손실: 0.20065445\n", "15 테스트 정확도: 0.9203\n", "16 훈련 손실: 0.19700487\n", "17 훈련 손실: 0.18893173\n", "18 훈련 손실: 0.19965445\n", "19 훈련 손실: 0.24071614\n", "20 훈련 손실: 0.18882047\n", "20 테스트 정확도: 0.9367\n", "21 훈련 손실: 0.12419216\n", "22 훈련 손실: 0.14013438\n", "23 훈련 손실: 0.1207896\n", "24 훈련 손실: 0.15721174\n", "25 훈련 손실: 0.11507863\n", "25 테스트 정확도: 0.948\n", "26 훈련 손실: 0.1389112\n", "27 훈련 손실: 0.15260854\n", "28 훈련 손실: 0.12343663\n", "29 훈련 손실: 0.11543138\n", "30 훈련 손실: 0.1140279\n", "30 테스트 정확도: 0.9507\n", "31 훈련 손실: 0.118971996\n", "32 훈련 손실: 0.09546787\n", "33 훈련 손실: 0.082993865\n", "34 훈련 손실: 0.1365939\n", "35 훈련 손실: 0.068016596\n", "35 테스트 정확도: 0.9592\n", "36 훈련 손실: 0.110167116\n", "37 훈련 손실: 0.049502347\n", "38 훈련 손실: 0.08133788\n", "39 훈련 손실: 0.09442001\n", "40 훈련 손실: 0.08737228\n", "40 테스트 정확도: 0.9619\n", "41 훈련 손실: 0.07679153\n", "42 훈련 손실: 0.06596778\n", "43 훈련 손실: 0.08324791\n", "44 훈련 손실: 0.07457332\n", "45 훈련 손실: 0.14072412\n", "45 테스트 정확도: 0.9656\n", "46 훈련 손실: 0.067265436\n", "47 훈련 손실: 0.099371135\n", "48 훈련 손실: 0.049632207\n", "49 훈련 손실: 0.05083575\n", "50 훈련 손실: 0.044153623\n", "50 테스트 정확도: 0.9685\n", "51 훈련 손실: 0.05297506\n", "52 훈련 손실: 0.04457248\n", "53 훈련 손실: 0.090527534\n", "54 훈련 손실: 0.09460009\n", "55 훈련 손실: 0.03665018\n", "55 테스트 정확도: 0.9688\n", "56 훈련 손실: 0.046360496\n", "57 훈련 손실: 0.059153534\n", "58 훈련 손실: 0.049381055\n", "59 훈련 손실: 0.060341027\n", "60 훈련 손실: 0.04144623\n", "60 테스트 정확도: 0.9733\n", "61 훈련 손실: 0.04057878\n", "62 훈련 손실: 0.057209827\n", "63 훈련 손실: 0.058649845\n", "64 훈련 손실: 0.04231491\n", "65 훈련 손실: 0.029547116\n", "65 테스트 정확도: 0.9723\n", "66 훈련 손실: 0.059069067\n", "67 훈련 손실: 0.05033871\n", "68 훈련 손실: 0.045774326\n", "69 훈련 손실: 0.041792825\n", "70 훈련 손실: 0.047385443\n", "70 테스트 정확도: 0.9743\n", "71 훈련 손실: 0.01973142\n", "72 훈련 손실: 0.039464623\n", "73 훈련 손실: 0.041869096\n", "74 훈련 손실: 0.053035066\n", "75 훈련 손실: 0.052626703\n", "75 테스트 정확도: 0.9756\n", "76 훈련 손실: 0.03827734\n", "77 훈련 손실: 0.026333703\n", "78 훈련 손실: 0.07061097\n", "79 훈련 손실: 0.0323908\n", "80 훈련 손실: 0.031364728\n", "80 테스트 정확도: 0.9731\n", "81 훈련 손실: 0.043911327\n", "82 훈련 손실: 0.015268317\n", "83 훈련 손실: 0.048755337\n", "84 훈련 손실: 0.029365018\n", "85 훈련 손실: 0.041843776\n", "85 테스트 정확도: 0.9758\n", "86 훈련 손실: 0.018274704\n", "87 훈련 손실: 0.038874403\n", "88 훈련 손실: 0.029697288\n", "89 훈련 손실: 0.020993456\n", "90 훈련 손실: 0.045232795\n", "90 테스트 정확도: 0.9769\n", "91 훈련 손실: 0.039236672\n", "92 훈련 손실: 0.031330034\n", "93 훈련 손실: 0.033417314\n", "94 훈련 손실: 0.025883513\n", "95 훈련 손실: 0.019570095\n", "95 테스트 정확도: 0.9765\n", "96 훈련 손실: 0.020656388\n", "97 훈련 손실: 0.033985104\n", "98 훈련 손실: 0.0470757\n", "99 훈련 손실: 0.031259395\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 500\n", "\n", "# log write ready\n", "graph_writer = tf.summary.FileWriter(\"graph_logs\", tf.Session().graph)\n", "# log write close\n", "graph_writer.close()\n", "\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " for epoch in range(n_epochs):\n", " for iteration in range(len(X_train1) // batch_size):\n", " X_batch, y_batch = generate_batch(X_train1, y_train1, batch_size)\n", " loss_val, _ = sess.run([loss, training_op], feed_dict={X: X_batch, y: y_batch})\n", " print(epoch, \"훈련 손실:\", loss_val)\n", " if epoch % 5 == 0:\n", " acc_test = accuracy.eval(feed_dict={X: X_test1, y: y_test1})\n", " print(epoch, \"테스트 정확도:\", acc_test)\n", "\n", " save_path = saver.save(sess, \"./my_digit_comparison_model.ckpt\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### d. 이제 DNN A의 은닉층을 동결해서 재사용하교 10개의 뉴런으로 된 소프트맥스 출력층을 추가한 새로운 DNN을 만듭니다. 이 네트워크를 분할 #2에 대해 훈련시켜보고 클래스당 500개의 이미지만으로도 높은 성능을 얻을 수 있는지 확인해보세요." ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "dnn_outputs = dnn(X, name=\"DNN_A\")\n", "frozen_outputs = tf.stop_gradient(dnn_outputs)\n", "# 하위층 동결\n", "logits = tf.layers.dense(dnn_outputs, n_outputs, kernel_initializer=he_init)\n", "Y_proba = tf.nn.softmax(logits)\n", "\n", "xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", "loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "optimizer = tf.train.MomentumOptimizer(learning_rate, momentum, use_nesterov=True)\n", "training_op = optimizer.minimize(loss)\n", "\n", "correct = tf.nn.in_top_k(logits, y, 1)\n", "accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))\n", "\n", "init = tf.global_variables_initializer()\n", "\n", "dnn_A_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=\"DNN_A\")\n", "restore_saver = tf.train.Saver(var_list={var.op.name: var for var in dnn_A_vars})\n", "saver = tf.train.Saver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex10-2.png](./img/ex10-2.png)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from ./my_digit_comparison_model.ckpt\n", "0 테스트 정확도: 0.9456\n", "10 테스트 정확도: 0.965\n", "20 테스트 정확도: 0.9655\n", "30 테스트 정확도: 0.9661\n", "40 테스트 정확도: 0.9659\n", "50 테스트 정확도: 0.966\n", "60 테스트 정확도: 0.9659\n", "70 테스트 정확도: 0.9659\n", "80 테스트 정확도: 0.9661\n", "90 테스트 정확도: 0.9662\n" ] } ], "source": [ "n_epochs = 100\n", "batch_size = 50\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", " # 미리 훈련된 DNN_A\n", " restore_saver.restore(sess, \"./my_digit_comparison_model.ckpt\")\n", "\n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " if epoch % 10 == 0:\n", " acc_test = accuracy.eval(feed_dict={X: X_test, y: y_test})\n", " print(epoch, \"테스트 정확도:\", acc_test)\n", "\n", " save_path = saver.save(sess, \"./my_mnist_model_final.ckpt\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "10 테스트 정확도에서 96.52%에 도달하는 것을 볼 수 있습니다. 이에 이어서 전이 학습을 사용하지 않고 처음부터 학습시킵니다." ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [], "source": [ "reset_graph()\n", "\n", "n_inputs = 28 * 28 # MNIST\n", "n_outputs = 10\n", "\n", "X = tf.placeholder(tf.float32, shape=(None, n_inputs), name=\"X\")\n", "y = tf.placeholder(tf.int32, shape=(None), name=\"y\")\n", "\n", "dnn_outputs = dnn(X, name=\"DNN_A\")\n", "\n", "logits = tf.layers.dense(dnn_outputs, n_outputs, kernel_initializer=he_init)\n", "Y_proba = tf.nn.softmax(logits)\n", "\n", "xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)\n", "loss = tf.reduce_mean(xentropy, name=\"loss\")\n", "\n", "optimizer = tf.train.MomentumOptimizer(learning_rate, momentum, use_nesterov=True)\n", "training_op = optimizer.minimize(loss)\n", "\n", "correct = tf.nn.in_top_k(logits, y, 1)\n", "accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))\n", "\n", "init = tf.global_variables_initializer()\n", "\n", "dnn_A_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=\"DNN_A\")\n", "restore_saver = tf.train.Saver(var_list={var.op.name: var for var in dnn_A_vars})\n", "saver = tf.train.Saver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![ex10-3.png](./img/ex10-3.png)" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 테스트 정확도: 0.8694\n", "10 테스트 정확도: 0.9276\n", "20 테스트 정확도: 0.9355\n", "30 테스트 정확도: 0.9417\n", "40 테스트 정확도: 0.9423\n", "50 테스트 정확도: 0.9427\n", "60 테스트 정확도: 0.9426\n", "70 테스트 정확도: 0.9426\n", "80 테스트 정확도: 0.9427\n", "90 테스트 정확도: 0.9426\n", "100 테스트 정확도: 0.9425\n", "110 테스트 정확도: 0.9425\n", "120 테스트 정확도: 0.9424\n", "130 테스트 정확도: 0.9424\n", "140 테스트 정확도: 0.9425\n" ] } ], "source": [ "n_epochs = 150\n", "batch_size = 50\n", "\n", "with tf.Session() as sess:\n", " init.run()\n", "\n", " for epoch in range(n_epochs):\n", " rnd_idx = np.random.permutation(len(X_train2))\n", " for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size):\n", " X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices]\n", " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", " if epoch % 10 == 0:\n", " acc_test = accuracy.eval(feed_dict={X: X_test, y: y_test})\n", " print(epoch, \"테스트 정확도:\", acc_test)\n", "\n", " save_path = saver.save(sess, \"./my_mnist_model_final.ckpt\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }