diff --git a/09_up_and_running_with_tensorflow.ipynb b/09_up_and_running_with_tensorflow.ipynb deleted file mode 100644 index d38fcf4..0000000 --- a/09_up_and_running_with_tensorflow.ipynb +++ /dev/null @@ -1,2550 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Chapter 9 – Up and running with TensorFlow**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "_This notebook contains all the sample code and solutions to the exercises in chapter 9._" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Setup" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, let's make sure this notebook works well in both python 2 and 3, import a few common modules, ensure MatplotLib plots figures inline and prepare a function to save the figures:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# To support both python 2 and python 3\n", - "from __future__ import division, print_function, unicode_literals\n", - "\n", - "# Common imports\n", - "import numpy as np\n", - "import os\n", - "\n", - "# to make this notebook's output stable across runs\n", - "def reset_graph(seed=42):\n", - " tf.reset_default_graph()\n", - " tf.set_random_seed(seed)\n", - " np.random.seed(seed)\n", - "\n", - "# To plot pretty figures\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", - "# Where to save the figures\n", - "PROJECT_ROOT_DIR = \".\"\n", - "CHAPTER_ID = \"tensorflow\"\n", - "\n", - "def save_fig(fig_id, tight_layout=True):\n", - " path = os.path.join(PROJECT_ROOT_DIR, \"images\", CHAPTER_ID, fig_id + \".png\")\n", - " print(\"Saving figure\", fig_id)\n", - " if tight_layout:\n", - " plt.tight_layout()\n", - " plt.savefig(path, format='png', dpi=300)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Creating and running a graph" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import tensorflow as tf\n", - "\n", - "reset_graph()\n", - "\n", - "x = tf.Variable(3, name=\"x\")\n", - "y = tf.Variable(4, name=\"y\")\n", - "f = x*x*y + y + 2" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "f" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "sess = tf.Session()\n", - "sess.run(x.initializer)\n", - "sess.run(y.initializer)\n", - "result = sess.run(f)\n", - "print(result)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "sess.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " x.initializer.run()\n", - " y.initializer.run()\n", - " result = f.eval()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "result" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "init = tf.global_variables_initializer()\n", - "\n", - "with tf.Session() as sess:\n", - " init.run()\n", - " result = f.eval()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "result" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "init = tf.global_variables_initializer()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "sess = tf.InteractiveSession()\n", - "init.run()\n", - "result = f.eval()\n", - "print(result)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "sess.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "result" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Managing graphs" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "x1 = tf.Variable(1)\n", - "x1.graph is tf.get_default_graph()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "graph = tf.Graph()\n", - "with graph.as_default():\n", - " x2 = tf.Variable(2)\n", - "\n", - "x2.graph is graph" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "x2.graph is tf.get_default_graph()" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "w = tf.constant(3)\n", - "x = w + 2\n", - "y = x + 5\n", - "z = x * 3\n", - "\n", - "with tf.Session() as sess:\n", - " print(y.eval()) # 10\n", - " print(z.eval()) # 15" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " y_val, z_val = sess.run([y, z])\n", - " print(y_val) # 10\n", - " print(z_val) # 15" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Linear Regression" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using the Normal Equation" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "from sklearn.datasets import fetch_california_housing\n", - "\n", - "reset_graph()\n", - "\n", - "housing = fetch_california_housing()\n", - "m, n = housing.data.shape\n", - "housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]\n", - "\n", - "X = tf.constant(housing_data_plus_bias, dtype=tf.float32, name=\"X\")\n", - "y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name=\"y\")\n", - "XT = tf.transpose(X)\n", - "theta = tf.matmul(tf.matmul(tf.matrix_inverse(tf.matmul(XT, X)), XT), y)\n", - "\n", - "with tf.Session() as sess:\n", - " theta_value = theta.eval()" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "theta_value" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compare with pure NumPy" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "X = housing_data_plus_bias\n", - "y = housing.target.reshape(-1, 1)\n", - "theta_numpy = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)\n", - "\n", - "print(theta_numpy)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compare with Scikit-Learn" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.linear_model import LinearRegression\n", - "lin_reg = LinearRegression()\n", - "lin_reg.fit(housing.data, housing.target.reshape(-1, 1))\n", - "\n", - "print(np.r_[lin_reg.intercept_.reshape(-1, 1), lin_reg.coef_.T])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using Batch Gradient Descent" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Gradient Descent requires scaling the feature vectors first. We could do this using TF, but let's just use Scikit-Learn for now." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.preprocessing import StandardScaler\n", - "scaler = StandardScaler()\n", - "scaled_housing_data = scaler.fit_transform(housing.data)\n", - "scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "print(scaled_housing_data_plus_bias.mean(axis=0))\n", - "print(scaled_housing_data_plus_bias.mean(axis=1))\n", - "print(scaled_housing_data_plus_bias.mean())\n", - "print(scaled_housing_data_plus_bias.shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Manually computing the gradients" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "n_epochs = 1000\n", - "learning_rate = 0.01\n", - "\n", - "X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name=\"X\")\n", - "y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")\n", - "error = y_pred - y\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\")\n", - "gradients = 2/m * tf.matmul(tf.transpose(X), error)\n", - "training_op = tf.assign(theta, theta - learning_rate * gradients)\n", - "\n", - "init = tf.global_variables_initializer()\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " if epoch % 100 == 0:\n", - " print(\"Epoch\", epoch, \"MSE =\", mse.eval())\n", - " sess.run(training_op)\n", - " \n", - " best_theta = theta.eval()" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "best_theta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using autodiff" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Same as above except for the `gradients = ...` line:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "n_epochs = 1000\n", - "learning_rate = 0.01\n", - "\n", - "X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name=\"X\")\n", - "y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")\n", - "error = y_pred - y\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\")" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "gradients = tf.gradients(mse, [theta])[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "training_op = tf.assign(theta, theta - learning_rate * gradients)\n", - "\n", - "init = tf.global_variables_initializer()\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " if epoch % 100 == 0:\n", - " print(\"Epoch\", epoch, \"MSE =\", mse.eval())\n", - " sess.run(training_op)\n", - " \n", - " best_theta = theta.eval()\n", - "\n", - "print(\"Best theta:\")\n", - "print(best_theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "How could you find the partial derivatives of the following function with regards to `a` and `b`?" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "def my_func(a, b):\n", - " z = 0\n", - " for i in range(100):\n", - " z = a * np.cos(z + i) + z * np.sin(b - i)\n", - " return z" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "my_func(0.2, 0.3)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "a = tf.Variable(0.2, name=\"a\")\n", - "b = tf.Variable(0.3, name=\"b\")\n", - "z = tf.constant(0.0, name=\"z0\")\n", - "for i in range(100):\n", - " z = a * tf.cos(z + i) + z * tf.sin(b - i)\n", - "\n", - "grads = tf.gradients(z, [a, b])\n", - "init = tf.global_variables_initializer()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's compute the function at $a=0.2$ and $b=0.3$, and the partial derivatives at that point with regards to $a$ and with regards to $b$:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " init.run()\n", - " print(z.eval())\n", - " print(sess.run(grads))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using a `GradientDescentOptimizer`" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "n_epochs = 1000\n", - "learning_rate = 0.01\n", - "\n", - "X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name=\"X\")\n", - "y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")\n", - "error = y_pred - y\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\")" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n", - "training_op = optimizer.minimize(mse)" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "init = tf.global_variables_initializer()\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " if epoch % 100 == 0:\n", - " print(\"Epoch\", epoch, \"MSE =\", mse.eval())\n", - " sess.run(training_op)\n", - " \n", - " best_theta = theta.eval()\n", - "\n", - "print(\"Best theta:\")\n", - "print(best_theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using a momentum optimizer" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "n_epochs = 1000\n", - "learning_rate = 0.01\n", - "\n", - "X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name=\"X\")\n", - "y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")\n", - "error = y_pred - y\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\")" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,\n", - " momentum=0.9)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "training_op = optimizer.minimize(mse)\n", - "\n", - "init = tf.global_variables_initializer()" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " sess.run(training_op)\n", - " \n", - " best_theta = theta.eval()\n", - "\n", - "print(\"Best theta:\")\n", - "print(best_theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Feeding data to the training algorithm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Placeholder nodes" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "A = tf.placeholder(tf.float32, shape=(None, 3))\n", - "B = A + 5\n", - "with tf.Session() as sess:\n", - " B_val_1 = B.eval(feed_dict={A: [[1, 2, 3]]})\n", - " B_val_2 = B.eval(feed_dict={A: [[4, 5, 6], [7, 8, 9]]})\n", - "\n", - "print(B_val_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "print(B_val_2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Mini-batch Gradient Descent" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "n_epochs = 1000\n", - "learning_rate = 0.01" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n + 1), name=\"X\")\n", - "y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")\n", - "error = y_pred - y\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\")\n", - "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n", - "training_op = optimizer.minimize(mse)\n", - "\n", - "init = tf.global_variables_initializer()" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "n_epochs = 10" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "batch_size = 100\n", - "n_batches = int(np.ceil(m / batch_size))" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "def fetch_batch(epoch, batch_index, batch_size):\n", - " np.random.seed(epoch * n_batches + batch_index) # not shown in the book\n", - " indices = np.random.randint(m, size=batch_size) # not shown\n", - " X_batch = scaled_housing_data_plus_bias[indices] # not shown\n", - " y_batch = housing.target.reshape(-1, 1)[indices] # not shown\n", - " return X_batch, y_batch\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " for batch_index in range(n_batches):\n", - " X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)\n", - " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", - "\n", - " best_theta = theta.eval()" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [], - "source": [ - "best_theta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Saving and restoring a model" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "n_epochs = 1000 # not shown in the book\n", - "learning_rate = 0.01 # not shown\n", - "\n", - "X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name=\"X\") # not shown\n", - "y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name=\"y\") # not shown\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\") # not shown\n", - "error = y_pred - y # not shown\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\") # not shown\n", - "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate) # not shown\n", - "training_op = optimizer.minimize(mse) # not shown\n", - "\n", - "init = tf.global_variables_initializer()\n", - "saver = tf.train.Saver()\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " if epoch % 100 == 0:\n", - " print(\"Epoch\", epoch, \"MSE =\", mse.eval()) # not shown\n", - " save_path = saver.save(sess, \"/tmp/my_model.ckpt\")\n", - " sess.run(training_op)\n", - " \n", - " best_theta = theta.eval()\n", - " save_path = saver.save(sess, \"/tmp/my_model_final.ckpt\")" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [], - "source": [ - "best_theta" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " saver.restore(sess, \"/tmp/my_model_final.ckpt\")\n", - " best_theta_restored = theta.eval() # not shown in the book" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [], - "source": [ - "np.allclose(best_theta, best_theta_restored)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you want to have a saver that loads and restores `theta` with a different name, such as `\"weights\"`:" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "saver = tf.train.Saver({\"weights\": theta})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By default the saver also saves the graph structure itself in a second file with the extension `.meta`. You can use the function `tf.train.import_meta_graph()` to restore the graph structure. This function loads the graph into the default graph and returns a `Saver` that can then be used to restore the graph state (i.e., the variable values):" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "# notice that we start with an empty graph.\n", - "\n", - "saver = tf.train.import_meta_graph(\"/tmp/my_model_final.ckpt.meta\") # this loads the graph structure\n", - "theta = tf.get_default_graph().get_tensor_by_name(\"theta:0\") # not shown in the book\n", - "\n", - "with tf.Session() as sess:\n", - " saver.restore(sess, \"/tmp/my_model_final.ckpt\") # this restores the graph's state\n", - " best_theta_restored = theta.eval() # not shown in the book" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [], - "source": [ - "np.allclose(best_theta, best_theta_restored)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This means that you can import a pretrained model without having to have the corresponding Python code to build the graph. This is very handy when you keep tweaking and saving your model: you can load a previously saved model without having to search for the version of the code that built it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualizing the graph\n", - "## inside Jupyter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To visualize the graph within Jupyter, we will use a TensorBoard server available online at https://tensorboard.appspot.com/ (so this will not work if you do not have Internet access). As far as I can tell, this code was originally written by Alex Mordvintsev in his [DeepDream tutorial](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/deepdream/deepdream.ipynb). Alternatively, you could use a tool like [tfgraphviz](https://github.com/akimach/tfgraphviz)." - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [], - "source": [ - "from tensorflow_graph_in_jupyter import show_graph" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "show_graph(tf.get_default_graph())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using TensorBoard" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "from datetime import datetime\n", - "\n", - "now = datetime.utcnow().strftime(\"%Y%m%d%H%M%S\")\n", - "root_logdir = \"tf_logs\"\n", - "logdir = \"{}/run-{}/\".format(root_logdir, now)" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "n_epochs = 1000\n", - "learning_rate = 0.01\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n + 1), name=\"X\")\n", - "y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")\n", - "error = y_pred - y\n", - "mse = tf.reduce_mean(tf.square(error), name=\"mse\")\n", - "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n", - "training_op = optimizer.minimize(mse)\n", - "\n", - "init = tf.global_variables_initializer()" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [], - "source": [ - "mse_summary = tf.summary.scalar('MSE', mse)\n", - "file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "n_epochs = 10\n", - "batch_size = 100\n", - "n_batches = int(np.ceil(m / batch_size))" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess: # not shown in the book\n", - " sess.run(init) # not shown\n", - "\n", - " for epoch in range(n_epochs): # not shown\n", - " for batch_index in range(n_batches):\n", - " X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)\n", - " if batch_index % 10 == 0:\n", - " summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})\n", - " step = epoch * n_batches + batch_index\n", - " file_writer.add_summary(summary_str, step)\n", - " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", - "\n", - " best_theta = theta.eval() # not shown" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "file_writer.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [], - "source": [ - "best_theta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Name scopes" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "now = datetime.utcnow().strftime(\"%Y%m%d%H%M%S\")\n", - "root_logdir = \"tf_logs\"\n", - "logdir = \"{}/run-{}/\".format(root_logdir, now)\n", - "\n", - "n_epochs = 1000\n", - "learning_rate = 0.01\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n + 1), name=\"X\")\n", - "y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "y_pred = tf.matmul(X, theta, name=\"predictions\")" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "with tf.name_scope(\"loss\") as scope:\n", - " error = y_pred - y\n", - " mse = tf.reduce_mean(tf.square(error), name=\"mse\")" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n", - "training_op = optimizer.minimize(mse)\n", - "\n", - "init = tf.global_variables_initializer()\n", - "\n", - "mse_summary = tf.summary.scalar('MSE', mse)\n", - "file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [], - "source": [ - "n_epochs = 10\n", - "batch_size = 100\n", - "n_batches = int(np.ceil(m / batch_size))\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " for batch_index in range(n_batches):\n", - " X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)\n", - " if batch_index % 10 == 0:\n", - " summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})\n", - " step = epoch * n_batches + batch_index\n", - " file_writer.add_summary(summary_str, step)\n", - " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", - "\n", - " best_theta = theta.eval()\n", - "\n", - "file_writer.flush()\n", - "file_writer.close()\n", - "print(\"Best theta:\")\n", - "print(best_theta)" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": {}, - "outputs": [], - "source": [ - "print(error.op.name)" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": {}, - "outputs": [], - "source": [ - "print(mse.op.name)" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "a1 = tf.Variable(0, name=\"a\") # name == \"a\"\n", - "a2 = tf.Variable(0, name=\"a\") # name == \"a_1\"\n", - "\n", - "with tf.name_scope(\"param\"): # name == \"param\"\n", - " a3 = tf.Variable(0, name=\"a\") # name == \"param/a\"\n", - "\n", - "with tf.name_scope(\"param\"): # name == \"param_1\"\n", - " a4 = tf.Variable(0, name=\"a\") # name == \"param_1/a\"\n", - "\n", - "for node in (a1, a2, a3, a4):\n", - " print(node.op.name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modularity" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An ugly flat code:" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "n_features = 3\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "\n", - "w1 = tf.Variable(tf.random_normal((n_features, 1)), name=\"weights1\")\n", - "w2 = tf.Variable(tf.random_normal((n_features, 1)), name=\"weights2\")\n", - "b1 = tf.Variable(0.0, name=\"bias1\")\n", - "b2 = tf.Variable(0.0, name=\"bias2\")\n", - "\n", - "z1 = tf.add(tf.matmul(X, w1), b1, name=\"z1\")\n", - "z2 = tf.add(tf.matmul(X, w2), b2, name=\"z2\")\n", - "\n", - "relu1 = tf.maximum(z1, 0., name=\"relu1\")\n", - "relu2 = tf.maximum(z1, 0., name=\"relu2\") # Oops, cut&paste error! Did you spot it?\n", - "\n", - "output = tf.add(relu1, relu2, name=\"output\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Much better, using a function to build the ReLUs:" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X):\n", - " w_shape = (int(X.get_shape()[1]), 1)\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\")\n", - " b = tf.Variable(0.0, name=\"bias\")\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\")\n", - " return tf.maximum(z, 0., name=\"relu\")\n", - "\n", - "n_features = 3\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "relus = [relu(X) for i in range(5)]\n", - "output = tf.add_n(relus, name=\"output\")" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "file_writer = tf.summary.FileWriter(\"logs/relu1\", tf.get_default_graph())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Even better using name scopes:" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X):\n", - " with tf.name_scope(\"relu\"):\n", - " w_shape = (int(X.get_shape()[1]), 1) # not shown in the book\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\") # not shown\n", - " b = tf.Variable(0.0, name=\"bias\") # not shown\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\") # not shown\n", - " return tf.maximum(z, 0., name=\"max\") # not shown" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "n_features = 3\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "relus = [relu(X) for i in range(5)]\n", - "output = tf.add_n(relus, name=\"output\")\n", - "\n", - "file_writer = tf.summary.FileWriter(\"logs/relu2\", tf.get_default_graph())\n", - "file_writer.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Sharing Variables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sharing a `threshold` variable the classic way, by defining it outside of the `relu()` function then passing it as a parameter:" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X, threshold):\n", - " with tf.name_scope(\"relu\"):\n", - " w_shape = (int(X.get_shape()[1]), 1) # not shown in the book\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\") # not shown\n", - " b = tf.Variable(0.0, name=\"bias\") # not shown\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\") # not shown\n", - " return tf.maximum(z, threshold, name=\"max\")\n", - "\n", - "threshold = tf.Variable(0.0, name=\"threshold\")\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "relus = [relu(X, threshold) for i in range(5)]\n", - "output = tf.add_n(relus, name=\"output\")" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X):\n", - " with tf.name_scope(\"relu\"):\n", - " if not hasattr(relu, \"threshold\"):\n", - " relu.threshold = tf.Variable(0.0, name=\"threshold\")\n", - " w_shape = int(X.get_shape()[1]), 1 # not shown in the book\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\") # not shown\n", - " b = tf.Variable(0.0, name=\"bias\") # not shown\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\") # not shown\n", - " return tf.maximum(z, relu.threshold, name=\"max\")" - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "relus = [relu(X) for i in range(5)]\n", - "output = tf.add_n(relus, name=\"output\")" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "with tf.variable_scope(\"relu\"):\n", - " threshold = tf.get_variable(\"threshold\", shape=(),\n", - " initializer=tf.constant_initializer(0.0))" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "with tf.variable_scope(\"relu\", reuse=True):\n", - " threshold = tf.get_variable(\"threshold\")" - ] - }, - { - "cell_type": "code", - "execution_count": 83, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "with tf.variable_scope(\"relu\") as scope:\n", - " scope.reuse_variables()\n", - " threshold = tf.get_variable(\"threshold\")" - ] - }, - { - "cell_type": "code", - "execution_count": 84, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X):\n", - " with tf.variable_scope(\"relu\", reuse=True):\n", - " threshold = tf.get_variable(\"threshold\")\n", - " w_shape = int(X.get_shape()[1]), 1 # not shown\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\") # not shown\n", - " b = tf.Variable(0.0, name=\"bias\") # not shown\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\") # not shown\n", - " return tf.maximum(z, threshold, name=\"max\")\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "with tf.variable_scope(\"relu\"):\n", - " threshold = tf.get_variable(\"threshold\", shape=(),\n", - " initializer=tf.constant_initializer(0.0))\n", - "relus = [relu(X) for relu_index in range(5)]\n", - "output = tf.add_n(relus, name=\"output\")" - ] - }, - { - "cell_type": "code", - "execution_count": 85, - "metadata": {}, - "outputs": [], - "source": [ - "file_writer = tf.summary.FileWriter(\"logs/relu6\", tf.get_default_graph())\n", - "file_writer.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X):\n", - " with tf.variable_scope(\"relu\"):\n", - " threshold = tf.get_variable(\"threshold\", shape=(), initializer=tf.constant_initializer(0.0))\n", - " w_shape = (int(X.get_shape()[1]), 1)\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\")\n", - " b = tf.Variable(0.0, name=\"bias\")\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\")\n", - " return tf.maximum(z, threshold, name=\"max\")\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "with tf.variable_scope(\"\", default_name=\"\") as scope:\n", - " first_relu = relu(X) # create the shared variable\n", - " scope.reuse_variables() # then reuse it\n", - " relus = [first_relu] + [relu(X) for i in range(4)]\n", - "output = tf.add_n(relus, name=\"output\")\n", - "\n", - "file_writer = tf.summary.FileWriter(\"logs/relu8\", tf.get_default_graph())\n", - "file_writer.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 87, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def relu(X):\n", - " threshold = tf.get_variable(\"threshold\", shape=(),\n", - " initializer=tf.constant_initializer(0.0))\n", - " w_shape = (int(X.get_shape()[1]), 1) # not shown in the book\n", - " w = tf.Variable(tf.random_normal(w_shape), name=\"weights\") # not shown\n", - " b = tf.Variable(0.0, name=\"bias\") # not shown\n", - " z = tf.add(tf.matmul(X, w), b, name=\"z\") # not shown\n", - " return tf.maximum(z, threshold, name=\"max\")\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n", - "relus = []\n", - "for relu_index in range(5):\n", - " with tf.variable_scope(\"relu\", reuse=(relu_index >= 1)) as scope:\n", - " relus.append(relu(X))\n", - "output = tf.add_n(relus, name=\"output\")" - ] - }, - { - "cell_type": "code", - "execution_count": 88, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "file_writer = tf.summary.FileWriter(\"logs/relu9\", tf.get_default_graph())\n", - "file_writer.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Extra material" - ] - }, - { - "cell_type": "code", - "execution_count": 89, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "with tf.variable_scope(\"my_scope\"):\n", - " x0 = tf.get_variable(\"x\", shape=(), initializer=tf.constant_initializer(0.))\n", - " x1 = tf.Variable(0., name=\"x\")\n", - " x2 = tf.Variable(0., name=\"x\")\n", - "\n", - "with tf.variable_scope(\"my_scope\", reuse=True):\n", - " x3 = tf.get_variable(\"x\")\n", - " x4 = tf.Variable(0., name=\"x\")\n", - "\n", - "with tf.variable_scope(\"\", default_name=\"\", reuse=True):\n", - " x5 = tf.get_variable(\"my_scope/x\")\n", - "\n", - "print(\"x0:\", x0.op.name)\n", - "print(\"x1:\", x1.op.name)\n", - "print(\"x2:\", x2.op.name)\n", - "print(\"x3:\", x3.op.name)\n", - "print(\"x4:\", x4.op.name)\n", - "print(\"x5:\", x5.op.name)\n", - "print(x0 is x3 and x3 is x5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first `variable_scope()` block first creates the shared variable `x0`, named `my_scope/x`. For all operations other than shared variables (including non-shared variables), the variable scope acts like a regular name scope, which is why the two variables `x1` and `x2` have a name with a prefix `my_scope/`. Note however that TensorFlow makes their names unique by adding an index: `my_scope/x_1` and `my_scope/x_2`.\n", - "\n", - "The second `variable_scope()` block reuses the shared variables in scope `my_scope`, which is why `x0 is x3`. Once again, for all operations other than shared variables it acts as a named scope, and since it's a separate block from the first one, the name of the scope is made unique by TensorFlow (`my_scope_1`) and thus the variable `x4` is named `my_scope_1/x`.\n", - "\n", - "The third block shows another way to get a handle on the shared variable `my_scope/x` by creating a `variable_scope()` at the root scope (whose name is an empty string), then calling `get_variable()` with the full name of the shared variable (i.e. `\"my_scope/x\"`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Strings" - ] - }, - { - "cell_type": "code", - "execution_count": 90, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "text = np.array(\"Do you want some café?\".split())\n", - "text_tensor = tf.constant(text)\n", - "\n", - "with tf.Session() as sess:\n", - " print(text_tensor.eval())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Autodiff" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note: the autodiff content was moved to the [extra_autodiff.ipynb](extra_autodiff.ipynb) notebook." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Exercise solutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. to 11." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "See appendix A." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 12. Logistic Regression with Mini-Batch Gradient Descent using TensorFlow" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, let's create the moons dataset using Scikit-Learn's `make_moons()` function:" - ] - }, - { - "cell_type": "code", - "execution_count": 91, - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.datasets import make_moons\n", - "\n", - "m = 1000\n", - "X_moons, y_moons = make_moons(m, noise=0.1, random_state=42)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a peek at the dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": 92, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(X_moons[y_moons == 1, 0], X_moons[y_moons == 1, 1], 'go', label=\"Positive\")\n", - "plt.plot(X_moons[y_moons == 0, 0], X_moons[y_moons == 0, 1], 'r^', label=\"Negative\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We must not forget to add an extra bias feature ($x_0 = 1$) to every instance. For this, we just need to add a column full of 1s on the left of the input matrix $\\mathbf{X}$:" - ] - }, - { - "cell_type": "code", - "execution_count": 93, - "metadata": {}, - "outputs": [], - "source": [ - "X_moons_with_bias = np.c_[np.ones((m, 1)), X_moons]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's check:" - ] - }, - { - "cell_type": "code", - "execution_count": 94, - "metadata": {}, - "outputs": [], - "source": [ - "X_moons_with_bias[:5]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Looks good. Now let's reshape `y_train` to make it a column vector (i.e. a 2D array with a single column):" - ] - }, - { - "cell_type": "code", - "execution_count": 95, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "y_moons_column_vector = y_moons.reshape(-1, 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's split the data into a training set and a test set:" - ] - }, - { - "cell_type": "code", - "execution_count": 96, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "test_ratio = 0.2\n", - "test_size = int(m * test_ratio)\n", - "X_train = X_moons_with_bias[:-test_size]\n", - "X_test = X_moons_with_bias[-test_size:]\n", - "y_train = y_moons_column_vector[:-test_size]\n", - "y_test = y_moons_column_vector[-test_size:]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ok, now let's create a small function to generate training batches. In this implementation we will just pick random instances from the training set for each batch. This means that a single batch may contain the same instance multiple times, and also a single epoch may not cover all the training instances (in fact it will generally cover only about two thirds of the instances). However, in practice this is not an issue and it simplifies the code:" - ] - }, - { - "cell_type": "code", - "execution_count": 97, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def random_batch(X_train, y_train, batch_size):\n", - " rnd_indices = np.random.randint(0, len(X_train), batch_size)\n", - " X_batch = X_train[rnd_indices]\n", - " y_batch = y_train[rnd_indices]\n", - " return X_batch, y_batch" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's look at a small batch:" - ] - }, - { - "cell_type": "code", - "execution_count": 98, - "metadata": {}, - "outputs": [], - "source": [ - "X_batch, y_batch = random_batch(X_train, y_train, 5)\n", - "X_batch" - ] - }, - { - "cell_type": "code", - "execution_count": 99, - "metadata": {}, - "outputs": [], - "source": [ - "y_batch" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Great! Now that the data is ready to be fed to the model, we need to build that model. Let's start with a simple implementation, then we will add all the bells and whistles." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First let's reset the default graph." - ] - }, - { - "cell_type": "code", - "execution_count": 100, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "reset_graph()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The _moons_ dataset has two input features, since each instance is a point on a plane (i.e., 2-Dimensional):" - ] - }, - { - "cell_type": "code", - "execution_count": 101, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "n_inputs = 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's build the Logistic Regression model. As we saw in chapter 4, this model first computes a weighted sum of the inputs (just like the Linear Regression model), and then it applies the sigmoid function to the result, which gives us the estimated probability for the positive class:\n", - "\n", - "$\\hat{p} = h_\\boldsymbol{\\theta}(\\mathbf{x}) = \\sigma(\\boldsymbol{\\theta}^T \\mathbf{x})$\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Recall that $\\boldsymbol{\\theta}$ is the parameter vector, containing the bias term $\\theta_0$ and the weights $\\theta_1, \\theta_2, \\dots, \\theta_n$. The input vector $\\mathbf{x}$ contains a constant term $x_0 = 1$, as well as all the input features $x_1, x_2, \\dots, x_n$.\n", - "\n", - "Since we want to be able to make predictions for multiple instances at a time, we will use an input matrix $\\mathbf{X}$ rather than a single input vector. The $i^{th}$ row will contain the transpose of the $i^{th}$ input vector $(\\mathbf{x}^{(i)})^T$. It is then possible to estimate the probability that each instance belongs to the positive class using the following equation:\n", - "\n", - "$ \\hat{\\mathbf{p}} = \\sigma(\\mathbf{X} \\boldsymbol{\\theta})$\n", - "\n", - "That's all we need to build the model:" - ] - }, - { - "cell_type": "code", - "execution_count": 102, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "X = tf.placeholder(tf.float32, shape=(None, n_inputs + 1), name=\"X\")\n", - "y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")\n", - "theta = tf.Variable(tf.random_uniform([n_inputs + 1, 1], -1.0, 1.0, seed=42), name=\"theta\")\n", - "logits = tf.matmul(X, theta, name=\"logits\")\n", - "y_proba = 1 / (1 + tf.exp(-logits))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In fact, TensorFlow has a nice function `tf.sigmoid()` that we can use to simplify the last line of the previous code:" - ] - }, - { - "cell_type": "code", - "execution_count": 103, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "y_proba = tf.sigmoid(logits)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we saw in chapter 4, the log loss is a good cost function to use for Logistic Regression:\n", - "\n", - "$J(\\boldsymbol{\\theta}) = -\\dfrac{1}{m} \\sum\\limits_{i=1}^{m}{\\left[ y^{(i)} \\log\\left(\\hat{p}^{(i)}\\right) + (1 - y^{(i)}) \\log\\left(1 - \\hat{p}^{(i)}\\right)\\right]}$\n", - "\n", - "One option is to implement it ourselves:" - ] - }, - { - "cell_type": "code", - "execution_count": 104, - "metadata": {}, - "outputs": [], - "source": [ - "epsilon = 1e-7 # to avoid an overflow when computing the log\n", - "loss = -tf.reduce_mean(y * tf.log(y_proba + epsilon) + (1 - y) * tf.log(1 - y_proba + epsilon))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But we might as well use TensorFlow's `tf.losses.log_loss()` function:" - ] - }, - { - "cell_type": "code", - "execution_count": 105, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "loss = tf.losses.log_loss(y, y_proba) # uses epsilon = 1e-7 by default" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The rest is pretty standard: let's create the optimizer and tell it to minimize the cost function:" - ] - }, - { - "cell_type": "code", - "execution_count": 106, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "learning_rate = 0.01\n", - "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n", - "training_op = optimizer.minimize(loss)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All we need now (in this minimal version) is the variable initializer:" - ] - }, - { - "cell_type": "code", - "execution_count": 107, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "init = tf.global_variables_initializer()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we are ready to train the model and use it for predictions!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There's really nothing special about this code, it's virtually the same as the one we used earlier for Linear Regression:" - ] - }, - { - "cell_type": "code", - "execution_count": 108, - "metadata": {}, - "outputs": [], - "source": [ - "n_epochs = 1000\n", - "batch_size = 50\n", - "n_batches = int(np.ceil(m / batch_size))\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " for batch_index in range(n_batches):\n", - " X_batch, y_batch = random_batch(X_train, y_train, batch_size)\n", - " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", - " loss_val = loss.eval({X: X_test, y: y_test})\n", - " if epoch % 100 == 0:\n", - " print(\"Epoch:\", epoch, \"\\tLoss:\", loss_val)\n", - "\n", - " y_proba_val = y_proba.eval(feed_dict={X: X_test, y: y_test})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note: we don't use the epoch number when generating batches, so we could just have a single `for` loop rather than 2 nested `for` loops, but it's convenient to think of training time in terms of number of epochs (i.e., roughly the number of times the algorithm went through the training set)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For each instance in the test set, `y_proba_val` contains the estimated probability that it belongs to the positive class, according to the model. For example, here are the first 5 estimated probabilities:" - ] - }, - { - "cell_type": "code", - "execution_count": 109, - "metadata": {}, - "outputs": [], - "source": [ - "y_proba_val[:5]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To classify each instance, we can go for maximum likelihood: classify as positive any instance whose estimated probability is greater or equal to 0.5:" - ] - }, - { - "cell_type": "code", - "execution_count": 110, - "metadata": {}, - "outputs": [], - "source": [ - "y_pred = (y_proba_val >= 0.5)\n", - "y_pred[:5]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Depending on the use case, you may want to choose a different threshold than 0.5: make it higher if you want high precision (but lower recall), and make it lower if you want high recall (but lower precision). See chapter 3 for more details." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's compute the model's precision and recall:" - ] - }, - { - "cell_type": "code", - "execution_count": 111, - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.metrics import precision_score, recall_score\n", - "\n", - "precision_score(y_test, y_pred)" - ] - }, - { - "cell_type": "code", - "execution_count": 112, - "metadata": {}, - "outputs": [], - "source": [ - "recall_score(y_test, y_pred)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's plot these predictions to see what they look like:" - ] - }, - { - "cell_type": "code", - "execution_count": 113, - "metadata": {}, - "outputs": [], - "source": [ - "y_pred_idx = y_pred.reshape(-1) # a 1D array rather than a column vector\n", - "plt.plot(X_test[y_pred_idx, 1], X_test[y_pred_idx, 2], 'go', label=\"Positive\")\n", - "plt.plot(X_test[~y_pred_idx, 1], X_test[~y_pred_idx, 2], 'r^', label=\"Negative\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Well, that looks pretty bad, doesn't it? But let's not forget that the Logistic Regression model has a linear decision boundary, so this is actually close to the best we can do with this model (unless we add more features, as we will show in a second)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's start over, but this time we will add all the bells and whistles, as listed in the exercise:\n", - "* Define the graph within a `logistic_regression()` function that can be reused easily.\n", - "* Save checkpoints using a `Saver` at regular intervals during training, and save the final model at the end of training.\n", - "* Restore the last checkpoint upon startup if training was interrupted.\n", - "* Define the graph using nice scopes so the graph looks good in TensorBoard.\n", - "* Add summaries to visualize the learning curves in TensorBoard.\n", - "* Try tweaking some hyperparameters such as the learning rate or the mini-batch size and look at the shape of the learning curve." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we start, we will add 4 more features to the inputs: ${x_1}^2$, ${x_2}^2$, ${x_1}^3$ and ${x_2}^3$. This was not part of the exercise, but it will demonstrate how adding features can improve the model. We will do this manually, but you could also add them using `sklearn.preprocessing.PolynomialFeatures`." - ] - }, - { - "cell_type": "code", - "execution_count": 114, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "X_train_enhanced = np.c_[X_train,\n", - " np.square(X_train[:, 1]),\n", - " np.square(X_train[:, 2]),\n", - " X_train[:, 1] ** 3,\n", - " X_train[:, 2] ** 3]\n", - "X_test_enhanced = np.c_[X_test,\n", - " np.square(X_test[:, 1]),\n", - " np.square(X_test[:, 2]),\n", - " X_test[:, 1] ** 3,\n", - " X_test[:, 2] ** 3]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is what the \"enhanced\" training set looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 115, - "metadata": {}, - "outputs": [], - "source": [ - "X_train_enhanced[:5]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ok, next let's reset the default graph:" - ] - }, - { - "cell_type": "code", - "execution_count": 116, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "reset_graph()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's define the `logistic_regression()` function to create the graph. We will leave out the definition of the inputs `X` and the targets `y`. We could include them here, but leaving them out will make it easier to use this function in a wide range of use cases (e.g. perhaps we will want to add some preprocessing steps for the inputs before we feed them to the Logistic Regression model)." - ] - }, - { - "cell_type": "code", - "execution_count": 117, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def logistic_regression(X, y, initializer=None, seed=42, learning_rate=0.01):\n", - " n_inputs_including_bias = int(X.get_shape()[1])\n", - " with tf.name_scope(\"logistic_regression\"):\n", - " with tf.name_scope(\"model\"):\n", - " if initializer is None:\n", - " initializer = tf.random_uniform([n_inputs_including_bias, 1], -1.0, 1.0, seed=seed)\n", - " theta = tf.Variable(initializer, name=\"theta\")\n", - " logits = tf.matmul(X, theta, name=\"logits\")\n", - " y_proba = tf.sigmoid(logits)\n", - " with tf.name_scope(\"train\"):\n", - " loss = tf.losses.log_loss(y, y_proba, scope=\"loss\")\n", - " optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n", - " training_op = optimizer.minimize(loss)\n", - " loss_summary = tf.summary.scalar('log_loss', loss)\n", - " with tf.name_scope(\"init\"):\n", - " init = tf.global_variables_initializer()\n", - " with tf.name_scope(\"save\"):\n", - " saver = tf.train.Saver()\n", - " return y_proba, loss, training_op, loss_summary, init, saver" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's create a little function to get the name of the log directory to save the summaries for Tensorboard:" - ] - }, - { - "cell_type": "code", - "execution_count": 118, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from datetime import datetime\n", - "\n", - "def log_dir(prefix=\"\"):\n", - " now = datetime.utcnow().strftime(\"%Y%m%d%H%M%S\")\n", - " root_logdir = \"tf_logs\"\n", - " if prefix:\n", - " prefix += \"-\"\n", - " name = prefix + \"run-\" + now\n", - " return \"{}/{}/\".format(root_logdir, name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, let's create the graph, using the `logistic_regression()` function. We will also create the `FileWriter` to save the summaries to the log directory for Tensorboard:" - ] - }, - { - "cell_type": "code", - "execution_count": 119, - "metadata": {}, - "outputs": [], - "source": [ - "n_inputs = 2 + 4\n", - "logdir = log_dir(\"logreg\")\n", - "\n", - "X = tf.placeholder(tf.float32, shape=(None, n_inputs + 1), name=\"X\")\n", - "y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")\n", - "\n", - "y_proba, loss, training_op, loss_summary, init, saver = logistic_regression(X, y)\n", - "\n", - "file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At last we can train the model! We will start by checking whether a previous training session was interrupted, and if so we will load the checkpoint and continue training from the epoch number we saved. In this example we just save the epoch number to a separate file, but in chapter 11 we will see how to store the training step directly as part of the model, using a non-trainable variable called `global_step` that we pass to the optimizer's `minimize()` method.\n", - "\n", - "You can try interrupting training to verify that it does indeed restore the last checkpoint when you start it again." - ] - }, - { - "cell_type": "code", - "execution_count": 120, - "metadata": {}, - "outputs": [], - "source": [ - "n_epochs = 10001\n", - "batch_size = 50\n", - "n_batches = int(np.ceil(m / batch_size))\n", - "\n", - "checkpoint_path = \"/tmp/my_logreg_model.ckpt\"\n", - "checkpoint_epoch_path = checkpoint_path + \".epoch\"\n", - "final_model_path = \"./my_logreg_model\"\n", - "\n", - "with tf.Session() as sess:\n", - " if os.path.isfile(checkpoint_epoch_path):\n", - " # if the checkpoint file exists, restore the model and load the epoch number\n", - " with open(checkpoint_epoch_path, \"rb\") as f:\n", - " start_epoch = int(f.read())\n", - " print(\"Training was interrupted. Continuing at epoch\", start_epoch)\n", - " saver.restore(sess, checkpoint_path)\n", - " else:\n", - " start_epoch = 0\n", - " sess.run(init)\n", - "\n", - " for epoch in range(start_epoch, n_epochs):\n", - " for batch_index in range(n_batches):\n", - " X_batch, y_batch = random_batch(X_train_enhanced, y_train, batch_size)\n", - " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", - " loss_val, summary_str = sess.run([loss, loss_summary], feed_dict={X: X_test_enhanced, y: y_test})\n", - " file_writer.add_summary(summary_str, epoch)\n", - " if epoch % 500 == 0:\n", - " print(\"Epoch:\", epoch, \"\\tLoss:\", loss_val)\n", - " saver.save(sess, checkpoint_path)\n", - " with open(checkpoint_epoch_path, \"wb\") as f:\n", - " f.write(b\"%d\" % (epoch + 1))\n", - "\n", - " saver.save(sess, final_model_path)\n", - " y_proba_val = y_proba.eval(feed_dict={X: X_test_enhanced, y: y_test})\n", - " os.remove(checkpoint_epoch_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once again, we can make predictions by just classifying as positive all the instances whose estimated probability is greater or equal to 0.5:" - ] - }, - { - "cell_type": "code", - "execution_count": 121, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "y_pred = (y_proba_val >= 0.5)" - ] - }, - { - "cell_type": "code", - "execution_count": 122, - "metadata": {}, - "outputs": [], - "source": [ - "precision_score(y_test, y_pred)" - ] - }, - { - "cell_type": "code", - "execution_count": 123, - "metadata": {}, - "outputs": [], - "source": [ - "recall_score(y_test, y_pred)" - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "metadata": {}, - "outputs": [], - "source": [ - "y_pred_idx = y_pred.reshape(-1) # a 1D array rather than a column vector\n", - "plt.plot(X_test[y_pred_idx, 1], X_test[y_pred_idx, 2], 'go', label=\"Positive\")\n", - "plt.plot(X_test[~y_pred_idx, 1], X_test[~y_pred_idx, 2], 'r^', label=\"Negative\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that's much, much better! Apparently the new features really helped a lot." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Try starting the tensorboard server, find the latest run and look at the learning curve (i.e., how the loss evaluated on the test set evolves as a function of the epoch number):\n", - "\n", - "```\n", - "$ tensorboard --logdir=tf_logs\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now you can play around with the hyperparameters (e.g. the `batch_size` or the `learning_rate`) and run training again and again, comparing the learning curves. You can even automate this process by implementing grid search or randomized search. Below is a simple implementation of a randomized search on both the batch size and the learning rate. For the sake of simplicity, the checkpoint mechanism was removed." - ] - }, - { - "cell_type": "code", - "execution_count": 125, - "metadata": {}, - "outputs": [], - "source": [ - "from scipy.stats import reciprocal\n", - "\n", - "n_search_iterations = 10\n", - "\n", - "for search_iteration in range(n_search_iterations):\n", - " batch_size = np.random.randint(1, 100)\n", - " learning_rate = reciprocal(0.0001, 0.1).rvs(random_state=search_iteration)\n", - "\n", - " n_inputs = 2 + 4\n", - " logdir = log_dir(\"logreg\")\n", - " \n", - " print(\"Iteration\", search_iteration)\n", - " print(\" logdir:\", logdir)\n", - " print(\" batch size:\", batch_size)\n", - " print(\" learning_rate:\", learning_rate)\n", - " print(\" training: \", end=\"\")\n", - "\n", - " reset_graph()\n", - "\n", - " X = tf.placeholder(tf.float32, shape=(None, n_inputs + 1), name=\"X\")\n", - " y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")\n", - "\n", - " y_proba, loss, training_op, loss_summary, init, saver = logistic_regression(\n", - " X, y, learning_rate=learning_rate)\n", - "\n", - " file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())\n", - "\n", - " n_epochs = 10001\n", - " n_batches = int(np.ceil(m / batch_size))\n", - "\n", - " final_model_path = \"./my_logreg_model_%d\" % search_iteration\n", - "\n", - " with tf.Session() as sess:\n", - " sess.run(init)\n", - "\n", - " for epoch in range(n_epochs):\n", - " for batch_index in range(n_batches):\n", - " X_batch, y_batch = random_batch(X_train_enhanced, y_train, batch_size)\n", - " sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n", - " loss_val, summary_str = sess.run([loss, loss_summary], feed_dict={X: X_test_enhanced, y: y_test})\n", - " file_writer.add_summary(summary_str, epoch)\n", - " if epoch % 500 == 0:\n", - " print(\".\", end=\"\")\n", - "\n", - " saver.save(sess, final_model_path)\n", - "\n", - " print()\n", - " y_proba_val = y_proba.eval(feed_dict={X: X_test_enhanced, y: y_test})\n", - " y_pred = (y_proba_val >= 0.5)\n", - " \n", - " print(\" precision:\", precision_score(y_test, y_pred))\n", - " print(\" recall:\", recall_score(y_test, y_pred))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `reciprocal()` function from SciPy's `stats` module returns a random distribution that is commonly used when you have no idea of the optimal scale of a hyperparameter. See the exercise solutions for chapter 2 for more details. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "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.5.2" - }, - "nav_menu": { - "height": "603px", - "width": "616px" - }, - "toc": { - "navigate_menu": true, - "number_sections": true, - "sideBar": true, - "threshold": 6, - "toc_cell": false, - "toc_section_display": "block", - "toc_window_display": true - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/12_distributed_tensorflow.ipynb b/12_distributed_tensorflow.ipynb deleted file mode 100644 index e457831..0000000 --- a/12_distributed_tensorflow.ipynb +++ /dev/null @@ -1,765 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Chapter 12 – Distributed TensorFlow**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "_This notebook contains all the sample code and solutions to the exercises in chapter 12._" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Setup" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, let's make sure this notebook works well in both python 2 and 3, import a few common modules, ensure MatplotLib plots figures inline and prepare a function to save the figures:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# To support both python 2 and python 3\n", - "from __future__ import division, print_function, unicode_literals\n", - "\n", - "# Common imports\n", - "import numpy as np\n", - "import os\n", - "\n", - "# to make this notebook's output stable across runs\n", - "def reset_graph(seed=42):\n", - " tf.reset_default_graph()\n", - " tf.set_random_seed(seed)\n", - " np.random.seed(seed)\n", - "\n", - "# To plot pretty figures\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", - "# Where to save the figures\n", - "PROJECT_ROOT_DIR = \".\"\n", - "CHAPTER_ID = \"distributed\"\n", - "\n", - "def save_fig(fig_id, tight_layout=True):\n", - " path = os.path.join(PROJECT_ROOT_DIR, \"images\", CHAPTER_ID, fig_id + \".png\")\n", - " print(\"Saving figure\", fig_id)\n", - " if tight_layout:\n", - " plt.tight_layout()\n", - " plt.savefig(path, format='png', dpi=300)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Local server" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import tensorflow as tf" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "c = tf.constant(\"Hello distributed TensorFlow!\")\n", - "server = tf.train.Server.create_local_server()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session(server.target) as sess:\n", - " print(sess.run(c))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Cluster" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "cluster_spec = tf.train.ClusterSpec({\n", - " \"ps\": [\n", - " \"127.0.0.1:2221\", # /job:ps/task:0\n", - " \"127.0.0.1:2222\", # /job:ps/task:1\n", - " ],\n", - " \"worker\": [\n", - " \"127.0.0.1:2223\", # /job:worker/task:0\n", - " \"127.0.0.1:2224\", # /job:worker/task:1\n", - " \"127.0.0.1:2225\", # /job:worker/task:2\n", - " ]})" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "task_ps0 = tf.train.Server(cluster_spec, job_name=\"ps\", task_index=0)\n", - "task_ps1 = tf.train.Server(cluster_spec, job_name=\"ps\", task_index=1)\n", - "task_worker0 = tf.train.Server(cluster_spec, job_name=\"worker\", task_index=0)\n", - "task_worker1 = tf.train.Server(cluster_spec, job_name=\"worker\", task_index=1)\n", - "task_worker2 = tf.train.Server(cluster_spec, job_name=\"worker\", task_index=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Pinning operations across devices and servers" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "with tf.device(\"/job:ps\"):\n", - " a = tf.Variable(1.0, name=\"a\")\n", - "\n", - "with tf.device(\"/job:worker\"):\n", - " b = a + 2\n", - "\n", - "with tf.device(\"/job:worker/task:1\"):\n", - " c = a + b" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session(\"grpc://127.0.0.1:2221\") as sess:\n", - " sess.run(a.initializer)\n", - " print(c.eval())" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "with tf.device(tf.train.replica_device_setter(\n", - " ps_tasks=2,\n", - " ps_device=\"/job:ps\",\n", - " worker_device=\"/job:worker\")):\n", - " v1 = tf.Variable(1.0, name=\"v1\") # pinned to /job:ps/task:0 (defaults to /cpu:0)\n", - " v2 = tf.Variable(2.0, name=\"v2\") # pinned to /job:ps/task:1 (defaults to /cpu:0)\n", - " v3 = tf.Variable(3.0, name=\"v3\") # pinned to /job:ps/task:0 (defaults to /cpu:0)\n", - " s = v1 + v2 # pinned to /job:worker (defaults to task:0/cpu:0)\n", - " with tf.device(\"/task:1\"):\n", - " p1 = 2 * s # pinned to /job:worker/task:1 (defaults to /cpu:0)\n", - " with tf.device(\"/cpu:0\"):\n", - " p2 = 3 * s # pinned to /job:worker/task:1/cpu:0\n", - "\n", - "config = tf.ConfigProto()\n", - "config.log_device_placement = True\n", - "\n", - "with tf.Session(\"grpc://127.0.0.1:2221\", config=config) as sess:\n", - " v1.initializer.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Readers – the old way" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "default1 = tf.constant([5.])\n", - "default2 = tf.constant([6])\n", - "default3 = tf.constant([7])\n", - "dec = tf.decode_csv(tf.constant(\"1.,,44\"),\n", - " record_defaults=[default1, default2, default3])\n", - "with tf.Session() as sess:\n", - " print(sess.run(dec))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "test_csv = open(\"my_test.csv\", \"w\")\n", - "test_csv.write(\"x1, x2 , target\\n\")\n", - "test_csv.write(\"1.,, 0\\n\")\n", - "test_csv.write(\"4., 5. , 1\\n\")\n", - "test_csv.write(\"7., 8. , 0\\n\")\n", - "test_csv.close()\n", - "\n", - "filename_queue = tf.FIFOQueue(capacity=10, dtypes=[tf.string], shapes=[()])\n", - "filename = tf.placeholder(tf.string)\n", - "enqueue_filename = filename_queue.enqueue([filename])\n", - "close_filename_queue = filename_queue.close()\n", - "\n", - "reader = tf.TextLineReader(skip_header_lines=1)\n", - "key, value = reader.read(filename_queue)\n", - "\n", - "x1, x2, target = tf.decode_csv(value, record_defaults=[[-1.], [-1.], [-1]])\n", - "features = tf.stack([x1, x2])\n", - "\n", - "instance_queue = tf.RandomShuffleQueue(\n", - " capacity=10, min_after_dequeue=2,\n", - " dtypes=[tf.float32, tf.int32], shapes=[[2],[]],\n", - " name=\"instance_q\", shared_name=\"shared_instance_q\")\n", - "enqueue_instance = instance_queue.enqueue([features, target])\n", - "close_instance_queue = instance_queue.close()\n", - "\n", - "minibatch_instances, minibatch_targets = instance_queue.dequeue_up_to(2)\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(enqueue_filename, feed_dict={filename: \"my_test.csv\"})\n", - " sess.run(close_filename_queue)\n", - " try:\n", - " while True:\n", - " sess.run(enqueue_instance)\n", - " except tf.errors.OutOfRangeError as ex:\n", - " print(\"No more files to read\")\n", - " sess.run(close_instance_queue)\n", - " try:\n", - " while True:\n", - " print(sess.run([minibatch_instances, minibatch_targets]))\n", - " except tf.errors.OutOfRangeError as ex:\n", - " print(\"No more training instances\")" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "#coord = tf.train.Coordinator()\n", - "#threads = tf.train.start_queue_runners(coord=coord)\n", - "#filename_queue = tf.train.string_input_producer([\"test.csv\"])\n", - "#coord.request_stop()\n", - "#coord.join(threads)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Queue runners and coordinators" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "filename_queue = tf.FIFOQueue(capacity=10, dtypes=[tf.string], shapes=[()])\n", - "filename = tf.placeholder(tf.string)\n", - "enqueue_filename = filename_queue.enqueue([filename])\n", - "close_filename_queue = filename_queue.close()\n", - "\n", - "reader = tf.TextLineReader(skip_header_lines=1)\n", - "key, value = reader.read(filename_queue)\n", - "\n", - "x1, x2, target = tf.decode_csv(value, record_defaults=[[-1.], [-1.], [-1]])\n", - "features = tf.stack([x1, x2])\n", - "\n", - "instance_queue = tf.RandomShuffleQueue(\n", - " capacity=10, min_after_dequeue=2,\n", - " dtypes=[tf.float32, tf.int32], shapes=[[2],[]],\n", - " name=\"instance_q\", shared_name=\"shared_instance_q\")\n", - "enqueue_instance = instance_queue.enqueue([features, target])\n", - "close_instance_queue = instance_queue.close()\n", - "\n", - "minibatch_instances, minibatch_targets = instance_queue.dequeue_up_to(2)\n", - "\n", - "n_threads = 5\n", - "queue_runner = tf.train.QueueRunner(instance_queue, [enqueue_instance] * n_threads)\n", - "coord = tf.train.Coordinator()\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(enqueue_filename, feed_dict={filename: \"my_test.csv\"})\n", - " sess.run(close_filename_queue)\n", - " enqueue_threads = queue_runner.create_threads(sess, coord=coord, start=True)\n", - " try:\n", - " while True:\n", - " print(sess.run([minibatch_instances, minibatch_targets]))\n", - " except tf.errors.OutOfRangeError as ex:\n", - " print(\"No more training instances\")" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "def read_and_push_instance(filename_queue, instance_queue):\n", - " reader = tf.TextLineReader(skip_header_lines=1)\n", - " key, value = reader.read(filename_queue)\n", - " x1, x2, target = tf.decode_csv(value, record_defaults=[[-1.], [-1.], [-1]])\n", - " features = tf.stack([x1, x2])\n", - " enqueue_instance = instance_queue.enqueue([features, target])\n", - " return enqueue_instance\n", - "\n", - "filename_queue = tf.FIFOQueue(capacity=10, dtypes=[tf.string], shapes=[()])\n", - "filename = tf.placeholder(tf.string)\n", - "enqueue_filename = filename_queue.enqueue([filename])\n", - "close_filename_queue = filename_queue.close()\n", - "\n", - "instance_queue = tf.RandomShuffleQueue(\n", - " capacity=10, min_after_dequeue=2,\n", - " dtypes=[tf.float32, tf.int32], shapes=[[2],[]],\n", - " name=\"instance_q\", shared_name=\"shared_instance_q\")\n", - "\n", - "minibatch_instances, minibatch_targets = instance_queue.dequeue_up_to(2)\n", - "\n", - "read_and_enqueue_ops = [read_and_push_instance(filename_queue, instance_queue) for i in range(5)]\n", - "queue_runner = tf.train.QueueRunner(instance_queue, read_and_enqueue_ops)\n", - "\n", - "with tf.Session() as sess:\n", - " sess.run(enqueue_filename, feed_dict={filename: \"my_test.csv\"})\n", - " sess.run(close_filename_queue)\n", - " coord = tf.train.Coordinator()\n", - " enqueue_threads = queue_runner.create_threads(sess, coord=coord, start=True)\n", - " try:\n", - " while True:\n", - " print(sess.run([minibatch_instances, minibatch_targets]))\n", - " except tf.errors.OutOfRangeError as ex:\n", - " print(\"No more training instances\")\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Setting a timeout" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "reset_graph()\n", - "\n", - "q = tf.FIFOQueue(capacity=10, dtypes=[tf.float32], shapes=[()])\n", - "v = tf.placeholder(tf.float32)\n", - "enqueue = q.enqueue([v])\n", - "dequeue = q.dequeue()\n", - "output = dequeue + 1\n", - "\n", - "config = tf.ConfigProto()\n", - "config.operation_timeout_in_ms = 1000\n", - "\n", - "with tf.Session(config=config) as sess:\n", - " sess.run(enqueue, feed_dict={v: 1.0})\n", - " sess.run(enqueue, feed_dict={v: 2.0})\n", - " sess.run(enqueue, feed_dict={v: 3.0})\n", - " print(sess.run(output))\n", - " print(sess.run(output, feed_dict={dequeue: 5}))\n", - " print(sess.run(output))\n", - " print(sess.run(output))\n", - " try:\n", - " print(sess.run(output))\n", - " except tf.errors.DeadlineExceededError as ex:\n", - " print(\"Timed out while dequeuing\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Data API" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Data API, introduced in TensorFlow 1.4, makes reading data efficiently much easier." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "tf.reset_default_graph()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's start with a simple dataset composed of three times the integers 0 to 9, in batches of 7:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = tf.data.Dataset.from_tensor_slices(np.arange(10))\n", - "dataset = dataset.repeat(3).batch(7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first line creates a dataset containing the integers 0 through 9. The second line creates a new dataset based on the first one, repeating its elements three times and creating batches of 7 elements. As you can see, we start with a source dataset, then we chain calls to various methods to apply transformations to the data." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we create a one-shot-iterator to go through this dataset just once, and we call its `get_next()` method to get a tensor that represents the next element." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "iterator = dataset.make_one_shot_iterator()\n", - "next_element = iterator.get_next()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's repeatedly evaluate `next_element` to go through the dataset. When there are not more elements, we get an `OutOfRangeError`:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " try:\n", - " while True:\n", - " print(next_element.eval())\n", - " except tf.errors.OutOfRangeError:\n", - " print(\"Done\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Great! It worked fine." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that, as always, a tensor is only evaluated once each time we run the graph (`sess.run()`): so even if we evaluate multiple tensors that all depend on `next_element`, it is only evaluated once. This is true as well if we ask for `next_element` to be evaluated twice in just one run:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " try:\n", - " while True:\n", - " print(sess.run([next_element, next_element]))\n", - " except tf.errors.OutOfRangeError:\n", - " print(\"Done\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `interleave()` method is powerful but a bit tricky to grasp at first. The easiest way to understand it is to look at an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "tf.reset_default_graph()" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = tf.data.Dataset.from_tensor_slices(np.arange(10))\n", - "dataset = dataset.repeat(3).batch(7)\n", - "dataset = dataset.interleave(\n", - " lambda v: tf.data.Dataset.from_tensor_slices(v),\n", - " cycle_length=3,\n", - " block_length=2)\n", - "iterator = dataset.make_one_shot_iterator()\n", - "next_element = iterator.get_next()" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " try:\n", - " while True:\n", - " print(next_element.eval(), end=\",\")\n", - " except tf.errors.OutOfRangeError:\n", - " print(\"Done\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because `cycle_length=3`, the new dataset starts by pulling 3 elements from the previous dataset: that's `[0,1,2,3,4,5,6]`, `[7,8,9,0,1,2,3]` and `[4,5,6,7,8,9,0]`. Then it calls the lambda function we gave it to create one dataset for each of the elements. Since we use `Dataset.from_tensor_slices()`, each dataset is going to return its elements one by one. Next, it pulls two items (since `block_length=2`) from each of these three datasets, and it iterates until all three datasets are out of items: 0,1 (from 1st), 7,8 (from 2nd), 4,5 (from 3rd), 2,3 (from 1st), 9,0 (from 2nd), and so on until 8,9 (from 3rd), 6 (from 1st), 3 (from 2nd), 0 (from 3rd). Next it tries to pull the next 3 elements from the original dataset, but there are just two left: `[1,2,3,4,5,6,7]` and `[8,9]`. Again, it creates datasets from these elements, and it pulls two items from each until both datasets are out of items: 1,2 (from 1st), 8,9 (from 2nd), 3,4 (from 1st), 5,6 (from 1st), 7 (from 1st). Notice that there's no interleaving at the end since the arrays do not have the same length." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Readers – the new way" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of using a source dataset based on `from_tensor_slices()` or `from_tensor()`, we can use a reader dataset. It handles most of the complexity for us (e.g., threads):" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "tf.reset_default_graph()" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "filenames = [\"my_test.csv\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = tf.data.TextLineDataset(filenames)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We still need to tell it how to decode each line:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "def decode_csv_line(line):\n", - " x1, x2, y = tf.decode_csv(\n", - " line, record_defaults=[[-1.], [-1.], [-1.]])\n", - " X = tf.stack([x1, x2])\n", - " return X, y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we can apply this decoding function to each element in the dataset using `map()`:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = dataset.skip(1).map(decode_csv_line)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, let's create a one-shot iterator:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "it = dataset.make_one_shot_iterator()\n", - "X, y = it.get_next()" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "with tf.Session() as sess:\n", - " try:\n", - " while True:\n", - " X_val, y_val = sess.run([X, y])\n", - " print(X_val, y_val)\n", - " except tf.errors.OutOfRangeError as ex:\n", - " print(\"Done\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "# Exercise solutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Coming soon**" - ] - }, - { - "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" - }, - "nav_menu": {}, - "toc": { - "navigate_menu": true, - "number_sections": true, - "sideBar": true, - "threshold": 6, - "toc_cell": false, - "toc_section_display": "block", - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 1 -}