GeronBook/Ch10/.ipynb_checkpoints/Exercises-checkpoint.ipynb

613 lines
41 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**EXERCISE 10**\n",
"\n",
"Train deep MLP on MNIST dataset to 98% precision. Then find optimal learning rate with the goods (checkpoints, early stopping, plotting learning curves, etc.)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using TensorFlow backend.\n"
]
}
],
"source": [
"import tensorflow as tf\n",
"import numpy as np\n",
"from matplotlib import pyplot as plt\n",
"%matplotlib inline\n",
"import keras\n",
"import random\n",
"import pandas as pd\n",
"import os"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"(x_train_full, y_train_full), (x_test, y_test) = keras.datasets.mnist.load_data()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(60000, 28, 28)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_train_full.shape"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"255"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_train_full.max()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"X_train, y_train = x_train_full[:50000] / 255.0, y_train_full[:50000]\n",
"X_val, y_val = x_train_full[50000:] / 255.0, y_train_full[50000:]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(50000, 28, 28)\n",
"(10000, 28, 28)\n",
"(50000,)\n",
"(10000,)\n"
]
}
],
"source": [
"print(X_train.shape)\n",
"print(X_val.shape)\n",
"print(y_train.shape)\n",
"print(y_val.shape)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"idx = random.randint(0,len(X_train))\n",
"plt.imshow(X_train[idx])\n",
"print(y_train[idx])"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Flatten(input_shape=[28,28]),\n",
" keras.layers.Dense(300, activation='relu'),\n",
" keras.layers.Dense(200, activation='relu'),\n",
" keras.layers.Dense(200, activation='relu'),\n",
" keras.layers.Dense(10, activation='softmax')\n",
" \n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential_2\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"flatten_2 (Flatten) (None, 784) 0 \n",
"_________________________________________________________________\n",
"dense_5 (Dense) (None, 300) 235500 \n",
"_________________________________________________________________\n",
"dense_6 (Dense) (None, 200) 60200 \n",
"_________________________________________________________________\n",
"dense_7 (Dense) (None, 200) 40200 \n",
"_________________________________________________________________\n",
"dense_8 (Dense) (None, 10) 2010 \n",
"=================================================================\n",
"Total params: 337,910\n",
"Trainable params: 337,910\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [],
"source": [
"model.compile(loss='sparse_categorical_crossentropy', \n",
" optimizer='sgd',\n",
" metrics=['accuracy'])"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train on 50000 samples, validate on 10000 samples\n",
"Epoch 1/10\n",
"50000/50000 [==============================] - 15s 300us/step - loss: 0.1921 - accuracy: 0.9442 - val_loss: 0.1676 - val_accuracy: 0.9545\n",
"Epoch 2/10\n",
"50000/50000 [==============================] - 15s 308us/step - loss: 0.1625 - accuracy: 0.9527 - val_loss: 0.1490 - val_accuracy: 0.9592\n",
"Epoch 3/10\n",
"50000/50000 [==============================] - 15s 305us/step - loss: 0.1397 - accuracy: 0.9594 - val_loss: 0.1332 - val_accuracy: 0.9637\n",
"Epoch 4/10\n",
"50000/50000 [==============================] - 15s 302us/step - loss: 0.1217 - accuracy: 0.9644 - val_loss: 0.1273 - val_accuracy: 0.9646\n",
"Epoch 5/10\n",
"50000/50000 [==============================] - 15s 304us/step - loss: 0.1078 - accuracy: 0.9684 - val_loss: 0.1178 - val_accuracy: 0.9666\n",
"Epoch 6/10\n",
"50000/50000 [==============================] - 15s 304us/step - loss: 0.0957 - accuracy: 0.9724 - val_loss: 0.1084 - val_accuracy: 0.9689\n",
"Epoch 7/10\n",
"50000/50000 [==============================] - 15s 304us/step - loss: 0.0856 - accuracy: 0.9756 - val_loss: 0.1124 - val_accuracy: 0.9686\n",
"Epoch 8/10\n",
"50000/50000 [==============================] - 15s 303us/step - loss: 0.0771 - accuracy: 0.9780 - val_loss: 0.1019 - val_accuracy: 0.9716\n",
"Epoch 9/10\n",
"50000/50000 [==============================] - 15s 299us/step - loss: 0.0691 - accuracy: 0.9802 - val_loss: 0.0948 - val_accuracy: 0.9736\n",
"Epoch 10/10\n",
"50000/50000 [==============================] - 13s 264us/step - loss: 0.0631 - accuracy: 0.9818 - val_loss: 0.0951 - val_accuracy: 0.9734\n"
]
}
],
"source": [
"history = model.fit(X_train, y_train, epochs=10, validation_data=(X_val, y_val))"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x360 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"pd.DataFrame(history.history).plot(figsize=(8,5))\n",
"plt.grid(True)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [],
"source": [
"model.save('simple_mlp.h5')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Setup logdir for TensorBoard\n",
"root_logdir = os.path.join(os.curdir, 'my_logs')\n",
"\n",
"# Setup function to get directory for logging our current run\n",
"def get_run_logdir():\n",
" import time\n",
" run_id = time.strftime('run_%Y_%m_%d_%H_%M_%S')\n",
" return os.path.join(root_logdir, run_id)\n",
"\n",
"run_logdir = get_run_logdir() "
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'model' is not defined",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-9-956a5442dc95>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;31m# Fit the model with callbacks\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 15\u001b[1;33m history = model.fit(X_train, y_train, epochs=20,\n\u001b[0m\u001b[0;32m 16\u001b[0m \u001b[0mvalidation_data\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mX_val\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my_val\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 17\u001b[0m callbacks=[checkpoint_cb, \n",
"\u001b[1;31mNameError\u001b[0m: name 'model' is not defined"
]
}
],
"source": [
"# Implement early stopping\n",
"\n",
"# Model Checkpoint callback incase of crash\n",
"checkpoint_cb = keras.callbacks.ModelCheckpoint('simple_mlp.h5',\n",
" save_best_only=True)\n",
"\n",
"# Early Stopping callback\n",
"early_stopping_cb = keras.callbacks.EarlyStopping(patience=5,\n",
" restore_best_weights=True)\n",
"\n",
"# TensorBoard callback\n",
"tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)\n",
"\n",
"# Fit the model with callbacks\n",
"history = model.fit(X_train, y_train, epochs=20,\n",
" validation_data=(X_val, y_val),\n",
" callbacks=[checkpoint_cb, \n",
" early_stopping_cb,\n",
" tensorboard_cb])\n",
"\n",
"model = keras.models.load_model('simple_mpl.h5')"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Reusing TensorBoard on port 6006 (pid 13744), started 0:24:54 ago. (Use '!kill 13744' to kill it.)"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
" <iframe id=\"tensorboard-frame-bfe354e751a4b5f4\" width=\"100%\" height=\"800\" frameborder=\"0\">\n",
" </iframe>\n",
" <script>\n",
" (function() {\n",
" const frame = document.getElementById(\"tensorboard-frame-bfe354e751a4b5f4\");\n",
" const url = new URL(\"/\", window.location);\n",
" url.port = 6006;\n",
" frame.src = url;\n",
" })();\n",
" </script>\n",
" "
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Load TensorBoard to port 6006\n",
"\n",
"%load_ext tensorboard\n",
"%tensorboard --logdir=./my_logs --port=6006"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"from keras.models import Sequential\n",
"from keras.layers import Flatten, Dense\n",
"from keras.optimizers import SGD\n",
"from keras.wrappers.scikit_learn import KerasClassifier\n",
"\n",
"\n",
"\n",
"# Optimize!\n",
"\n",
"# Create a function which can dynamically construct a model\n",
"def build_model(n_hidden=4, n_neurons=275, learning_rate=3e-3, input_shape=[28,28]):\n",
" model = Sequential()\n",
" model.add(Flatten(input_shape=input_shape))\n",
" for layer in range(n_hidden):\n",
" model.add(Dense(n_neurons, activation=\"relu\"))\n",
" model.add(Dense(10, activation='softmax'))\n",
" optimizer = SGD(lr=learning_rate)\n",
" model.compile(loss=\"sparse_categorical_crossentropy\", optimizer=optimizer, metrics=['accuracy'])\n",
" return model\n",
"\n",
"# Wrap it up\n",
"keras_clf = KerasClassifier(build_model)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fitting 5 folds for each of 6 candidates, totalling 30 fits\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"[Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers.\n",
"[Parallel(n_jobs=-1)]: Done 30 out of 30 | elapsed: 28.0min finished\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train on 50000 samples, validate on 10000 samples\n",
"Epoch 1/10\n",
"50000/50000 [==============================] - 22s 438us/step - loss: 1.5127 - accuracy: 0.5896 - val_loss: 0.5557 - val_accuracy: 0.8605\n",
"Epoch 2/10\n",
"50000/50000 [==============================] - 14s 272us/step - loss: 0.4446 - accuracy: 0.8754 - val_loss: 0.3396 - val_accuracy: 0.9036\n",
"Epoch 3/10\n",
"50000/50000 [==============================] - 14s 283us/step - loss: 0.3243 - accuracy: 0.9063 - val_loss: 0.2690 - val_accuracy: 0.9240\n",
"Epoch 4/10\n",
"50000/50000 [==============================] - 15s 309us/step - loss: 0.2744 - accuracy: 0.9196 - val_loss: 0.2385 - val_accuracy: 0.9325\n",
"Epoch 5/10\n",
"50000/50000 [==============================] - 11s 230us/step - loss: 0.2408 - accuracy: 0.9287 - val_loss: 0.2168 - val_accuracy: 0.9375\n",
"Epoch 6/10\n",
"50000/50000 [==============================] - 13s 263us/step - loss: 0.2161 - accuracy: 0.9366 - val_loss: 0.1903 - val_accuracy: 0.9451\n",
"Epoch 7/10\n",
"50000/50000 [==============================] - 15s 294us/step - loss: 0.1951 - accuracy: 0.9428 - val_loss: 0.1805 - val_accuracy: 0.9493\n",
"Epoch 8/10\n",
"50000/50000 [==============================] - 14s 271us/step - loss: 0.1783 - accuracy: 0.9483 - val_loss: 0.1720 - val_accuracy: 0.9524\n",
"Epoch 9/10\n",
"50000/50000 [==============================] - 15s 296us/step - loss: 0.1649 - accuracy: 0.9517 - val_loss: 0.1641 - val_accuracy: 0.9529\n",
"Epoch 10/10\n",
"50000/50000 [==============================] - 15s 296us/step - loss: 0.1522 - accuracy: 0.9554 - val_loss: 0.1626 - val_accuracy: 0.9528\n"
]
},
{
"data": {
"text/plain": [
"GridSearchCV(cv=None, error_score=nan,\n",
" estimator=<keras.wrappers.scikit_learn.KerasClassifier object at 0x000001B002866948>,\n",
" iid='deprecated', n_jobs=-1,\n",
" param_grid={'epochs': [10], 'n_hidden': [4, 5],\n",
" 'n_neurons': [275, 300, 325]},\n",
" pre_dispatch='2*n_jobs', refit=True, return_train_score=False,\n",
" scoring=None, verbose=2)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.model_selection import GridSearchCV\n",
"\n",
"validator = GridSearchCV(keras_clf,\n",
" param_grid={'n_neurons': [250, 275, 300, 325, 350],\n",
" 'n_hidden' : [4, 5, 6],\n",
" # epochs is avail for tuning even when not\n",
" # an argument to model building function\n",
" 'epochs': [25, 30]},\n",
" n_jobs=-1,\n",
" verbose=2)\n",
"\n",
"# Model Checkpoint callback incase of crash\n",
"checkpoint_cb = keras.callbacks.ModelCheckpoint('simple_mlp.h5',\n",
" save_best_only=True)\n",
"\n",
"# Early Stopping callback\n",
"early_stopping_cb = keras.callbacks.EarlyStopping(patience=5,\n",
" restore_best_weights=True)\n",
"\n",
"# TensorBoard callback\n",
"tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)\n",
"\n",
"# Fit to our data\n",
"validator.fit(X_train, y_train, validation_data = (X_val, y_val),\n",
" callbacks=[checkpoint_cb,\n",
" early_stopping_cb])"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'epochs': 10, 'n_hidden': 5, 'n_neurons': 300}"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"validator.best_params_"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.9437600016593933"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"validator.best_score_"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10000/10000 [==============================] - 2s 160us/step\n"
]
},
{
"data": {
"text/plain": [
"0.9462000131607056"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"validator.score(x_test, y_test)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"model = validator.best_estimator_.model"
]
},
{
"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.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}