{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# CS294-112 Fall 2018 Tensorflow Tutorial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial will provide a brief overview of the core concepts and functionality of Tensorflow. This tutorial will cover the following:\n", "\n", "0. What is Tensorflow\n", "1. How to input data\n", "2. How to perform computations\n", "3. How to create variables\n", "4. How to train a neural network for a simple regression problem\n", "5. Tips and tricks" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import tensorflow as tf\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import matplotlib.cm as cm\n", "import matplotlib.patches as mpatches" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def tf_reset():\n", " try:\n", " sess.close()\n", " except:\n", " pass\n", " tf.reset_default_graph()\n", " return tf.Session()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 0. What is Tensorflow\n", "\n", "Tensorflow is a framework to define a series of computations. You define inputs, what operations should be performed, and then Tensorflow will compute the outputs for you.\n", "\n", "Below is a simple high-level example:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "c = 3.0\n" ] } ], "source": [ "# create the session you'll work in\n", "# you can think of this as a \"blank piece of paper\" that you'll be writing math on\n", "sess = tf_reset()\n", "\n", "# define your inputs\n", "a = tf.constant(1.0)\n", "b = tf.constant(2.0)\n", "\n", "# do some operations\n", "c = a + b\n", "\n", "# get the result\n", "c_run = sess.run(c)\n", "\n", "print('c = {0}'.format(c_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. How to input data\n", "\n", "Tensorflow has multiple ways for you to input data. One way is to have the inputs be constants:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "c = 3.0\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "# define your inputs\n", "a = tf.constant(1.0)\n", "b = tf.constant(2.0)\n", "\n", "# do some operations\n", "c = a + b\n", "\n", "# get the result\n", "c_run = sess.run(c)\n", "\n", "print('c = {0}'.format(c_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, having our inputs be constants is inflexible. We want to be able to change what data we input at runtime. We can do this using placeholders:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "c0 = [3.]\n", "c1 = [6.]\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "# define your inputs\n", "a = tf.placeholder(dtype=tf.float32, shape=[1], name='a_placeholder')\n", "b = tf.placeholder(dtype=tf.float32, shape=[1], name='b_placeholder')\n", "\n", "# do some operations\n", "c = a + b\n", "\n", "# get the result\n", "c0_run = sess.run(c, feed_dict={a: [1.0], b: [2.0]})\n", "c1_run = sess.run(c, feed_dict={a: [2.0], b: [4.0]})\n", "\n", "print('c0 = {0}'.format(c0_run))\n", "print('c1 = {0}'.format(c1_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But what if we don't know the size of our input beforehand? One dimension of a tensor is allowed to be 'None', which means it can be variable sized:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tensor(\"a_placeholder:0\", shape=(?,), dtype=float32)\n", "a shape: (?,)\n", "Tensor(\"b_placeholder:0\", shape=(?,), dtype=float32)\n", "b shape: (?,)\n", "c0 = [3.]\n", "c1 = [3. 6.]\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "# inputs\n", "a = tf.placeholder(dtype=tf.float32, shape=[None], name='a_placeholder')\n", "b = tf.placeholder(dtype=tf.float32, shape=[None], name='b_placeholder')\n", "\n", "# do some operations\n", "c = a + b\n", "\n", "# get outputs\n", "c0_run = sess.run(c, feed_dict={a: [1.0], b: [2.0]})\n", "c1_run = sess.run(c, feed_dict={a: [1.0, 2.0], b: [2.0, 4.0]})\n", "\n", "print(a)\n", "print('a shape: {0}'.format(a.get_shape()))\n", "print(b)\n", "print('b shape: {0}'.format(b.get_shape()))\n", "print('c0 = {0}'.format(c0_run))\n", "print('c1 = {0}'.format(c1_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. How to perform computations\n", "\n", "Now that we can input data, we want to perform useful computations on the data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's create some data to work with:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a:\n", "[[-1.]\n", " [-2.]\n", " [-3.]]\n", "b:\n", "[[1. 2. 3.]]\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "# inputs\n", "a = tf.constant([[-1.], [-2.], [-3.]], dtype=tf.float32)\n", "b = tf.constant([[1., 2., 3.]], dtype=tf.float32)\n", "\n", "a_run, b_run = sess.run([a, b])\n", "print('a:\\n{0}'.format(a_run))\n", "print('b:\\n{0}'.format(b_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do simple operations, such as addition:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b:\n", "[[1. 2. 3.]]\n", "c:\n", "[[2. 4. 6.]]\n" ] } ], "source": [ "c = b + b\n", "\n", "c_run = sess.run(c)\n", "print('b:\\n{0}'.format(b_run))\n", "print('c:\\n{0}'.format(c_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Be careful about the dimensions of the tensors, some operations may work even when you think they shouldn't..." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a:\n", "[[-1.]\n", " [-2.]\n", " [-3.]]\n", "b:\n", "[[1. 2. 3.]]\n", "c:\n", "[[ 0. 1. 2.]\n", " [-1. 0. 1.]\n", " [-2. -1. 0.]]\n" ] } ], "source": [ "c = a + b\n", "\n", "c_run = sess.run(c)\n", "print('a:\\n{0}'.format(a_run))\n", "print('b:\\n{0}'.format(b_run))\n", "print('c:\\n{0}'.format(c_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also, some operations may be different than what you expect:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a:\n", "[[-1.]\n", " [-2.]\n", " [-3.]]\n", "b:\n", "[[1. 2. 3.]]\n", "c_elementwise:\n", "[[-1. -2. -3.]\n", " [-2. -4. -6.]\n", " [-3. -6. -9.]]\n", "c_matmul: \n", "[[-14.]]\n" ] } ], "source": [ "c_elementwise = a * b\n", "c_matmul = tf.matmul(b, a)\n", "\n", "c_elementwise_run, c_matmul_run = sess.run([c_elementwise, c_matmul])\n", "print('a:\\n{0}'.format(a_run))\n", "print('b:\\n{0}'.format(b_run))\n", "print('c_elementwise:\\n{0}'.format(c_elementwise_run))\n", "print('c_matmul: \\n{0}'.format(c_matmul_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Operations can be chained together:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b:\n", "[[1. 2. 3.]]\n", "c0:\n", "[[2. 4. 6.]]\n", "c1:\n", "[[3. 5. 7.]]\n" ] } ], "source": [ "# operations can be chained together\n", "c0 = b + b\n", "c1 = c0 + 1\n", "\n", "c0_run, c1_run = sess.run([c0, c1])\n", "print('b:\\n{0}'.format(b_run))\n", "print('c0:\\n{0}'.format(c0_run))\n", "print('c1:\\n{0}'.format(c1_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, Tensorflow has many useful built-in operations:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b:\n", "[[1. 2. 3.]]\n", "c:\n", "2.0\n" ] } ], "source": [ "c = tf.reduce_mean(b)\n", "\n", "c_run = sess.run(c)\n", "print('b:\\n{0}'.format(b_run))\n", "print('c:\\n{0}'.format(c_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. How to create variables\n", "\n", "Now that we can input data and perform computations, we want some of these operations to involve variables that are free parameters, and can be trained using an optimizer (e.g., gradient descent)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's create some data to work with:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b:\n", "[[1. 2. 3.]]\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "# inputs\n", "b = tf.constant([[1., 2., 3.]], dtype=tf.float32)\n", "\n", "sess = tf.Session()\n", "\n", "b_run = sess.run(b)\n", "print('b:\\n{0}'.format(b_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll now create a variable" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "var_init_value = [[2.0, 4.0, 6.0]]\n", "var = tf.get_variable(name='myvar',\n", " shape=[1, 3],\n", " dtype=tf.float32,\n", " initializer=tf.constant_initializer(var_init_value))\n", "\n", "print(var)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and check that it's been added to Tensorflow's variables list:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[]\n" ] } ], "source": [ "print(tf.global_variables())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do operations with the variable just like any other tensor:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tensor(\"Const:0\", shape=(1, 3), dtype=float32)\n", "\n", "Tensor(\"add:0\", shape=(1, 3), dtype=float32)\n" ] } ], "source": [ "# can do operations\n", "c = b + var\n", "print(b)\n", "print(var)\n", "print(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we can run any of these operations, we must first initalize the variables" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "init_op = tf.global_variables_initializer()\n", "sess.run(init_op)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and then we can run the operations just as we normally would." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b:\n", "[[1. 2. 3.]]\n", "var:\n", "[[2.0, 4.0, 6.0]]\n", "c:\n", "[[3. 6. 9.]]\n" ] } ], "source": [ "c_run = sess.run(c)\n", "\n", "print('b:\\n{0}'.format(b_run))\n", "print('var:\\n{0}'.format(var_init_value))\n", "print('c:\\n{0}'.format(c_run))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far we haven't said yet how to optimize these variables. We'll cover that next in the context of an example." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 4. How to train a neural network for a simple regression problem\n", "\n", "We've discussed how to input data, perform operations, and create variables. We'll now show how to combine all of these---with some minor additions---to train a neural network on a simple regression problem." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we'll create data for a 1-dimensional regression problem:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# generate the data\n", "inputs = np.linspace(-2*np.pi, 2*np.pi, 10000)[:, None]\n", "outputs = np.sin(inputs) + 0.05 * np.random.normal(size=[len(inputs),1])\n", "\n", "plt.scatter(inputs[:, 0], outputs[:, 0], s=0.1, color='k', marker='o')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The below code creates the inputs, variables, neural network operations, mean-squared-error loss, gradient descent optimizer, and runs the optimizer using minibatches of the data." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0000 mse: 0.428\n", "1000 mse: 0.067\n", "2000 mse: 0.027\n", "3000 mse: 0.016\n", "4000 mse: 0.006\n", "5000 mse: 0.002\n", "6000 mse: 0.002\n", "7000 mse: 0.001\n", "8000 mse: 0.002\n", "9000 mse: 0.001\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "def create_model():\n", " # create inputs\n", " input_ph = tf.placeholder(dtype=tf.float32, shape=[None, 1])\n", " output_ph = tf.placeholder(dtype=tf.float32, shape=[None, 1])\n", "\n", " # create variables\n", " W0 = tf.get_variable(name='W0', shape=[1, 20], initializer=tf.contrib.layers.xavier_initializer())\n", " W1 = tf.get_variable(name='W1', shape=[20, 20], initializer=tf.contrib.layers.xavier_initializer())\n", " W2 = tf.get_variable(name='W2', shape=[20, 1], initializer=tf.contrib.layers.xavier_initializer())\n", "\n", " b0 = tf.get_variable(name='b0', shape=[20], initializer=tf.constant_initializer(0.))\n", " b1 = tf.get_variable(name='b1', shape=[20], initializer=tf.constant_initializer(0.))\n", " b2 = tf.get_variable(name='b2', shape=[1], initializer=tf.constant_initializer(0.))\n", "\n", " weights = [W0, W1, W2]\n", " biases = [b0, b1, b2]\n", " activations = [tf.nn.relu, tf.nn.relu, None]\n", "\n", " # create computation graph\n", " layer = input_ph\n", " for W, b, activation in zip(weights, biases, activations):\n", " layer = tf.matmul(layer, W) + b\n", " if activation is not None:\n", " layer = activation(layer)\n", " output_pred = layer\n", " \n", " return input_ph, output_ph, output_pred\n", " \n", "input_ph, output_ph, output_pred = create_model()\n", " \n", "# create loss\n", "mse = tf.reduce_mean(0.5 * tf.square(output_pred - output_ph))\n", "\n", "# create optimizer\n", "opt = tf.train.AdamOptimizer().minimize(mse)\n", "\n", "# initialize variables\n", "sess.run(tf.global_variables_initializer())\n", "# create saver to save model variables\n", "saver = tf.train.Saver()\n", "\n", "# run training\n", "batch_size = 32\n", "for training_step in range(10000):\n", " # get a random subset of the training data\n", " indices = np.random.randint(low=0, high=len(inputs), size=batch_size)\n", " input_batch = inputs[indices]\n", " output_batch = outputs[indices]\n", " \n", " # run the optimizer and get the mse\n", " _, mse_run = sess.run([opt, mse], feed_dict={input_ph: input_batch, output_ph: output_batch})\n", " \n", " # print the mse every so often\n", " if training_step % 1000 == 0:\n", " print('{0:04d} mse: {1:.3f}'.format(training_step, mse_run))\n", " saver.save(sess, '/tmp/model.ckpt')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the neural network is trained, we can use it to make predictions:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmp/model.ckpt\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sess = tf_reset()\n", "\n", "# create the model\n", "input_ph, output_ph, output_pred = create_model()\n", "\n", "# restore the saved model\n", "saver = tf.train.Saver()\n", "saver.restore(sess, \"/tmp/model.ckpt\")\n", "\n", "output_pred_run = sess.run(output_pred, feed_dict={input_ph: inputs})\n", "\n", "plt.scatter(inputs[:, 0], outputs[:, 0], c='k', marker='o', s=0.1)\n", "plt.scatter(inputs[:, 0], output_pred_run[:, 0], c='r', marker='o', s=0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not so hard after all! There is much more functionality to Tensorflow besides what we've covered, but you now know the basics." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 5. Tips and tricks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (a) Check your dimensions" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "# example of \"surprising\" resulting dimensions due to broadcasting\n", "a = tf.constant(np.random.random((4, 1)))\n", "b = tf.constant(np.random.random((1, 4)))\n", "c = a * b\n", "assert c.get_shape() == (4, 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (b) Check what variables have been created" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I_am_a_variable:0\n", "I_am_a_variable_too:0\n" ] } ], "source": [ "sess = tf_reset()\n", "a = tf.get_variable('I_am_a_variable', shape=[4, 6])\n", "b = tf.get_variable('I_am_a_variable_too', shape=[2, 7])\n", "for var in tf.global_variables():\n", " print(var.name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (c) Look at the [tensorflow API](https://www.tensorflow.org/api_docs/python/), or open up a python terminal and investigate!" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function reduce_mean in module tensorflow.python.ops.math_ops:\n", "\n", "reduce_mean(input_tensor, axis=None, keepdims=None, name=None, reduction_indices=None, keep_dims=None)\n", " Computes the mean of elements across dimensions of a tensor. (deprecated arguments)\n", " \n", " SOME ARGUMENTS ARE DEPRECATED. They will be removed in a future version.\n", " Instructions for updating:\n", " keep_dims is deprecated, use keepdims instead\n", " \n", " Reduces `input_tensor` along the dimensions given in `axis`.\n", " Unless `keepdims` is true, the rank of the tensor is reduced by 1 for each\n", " entry in `axis`. If `keepdims` is true, the reduced dimensions\n", " are retained with length 1.\n", " \n", " If `axis` is None, all dimensions are reduced, and a\n", " tensor with a single element is returned.\n", " \n", " For example:\n", " \n", " ```python\n", " x = tf.constant([[1., 1.], [2., 2.]])\n", " tf.reduce_mean(x) # 1.5\n", " tf.reduce_mean(x, 0) # [1.5, 1.5]\n", " tf.reduce_mean(x, 1) # [1., 2.]\n", " ```\n", " \n", " Args:\n", " input_tensor: The tensor to reduce. Should have numeric type.\n", " axis: The dimensions to reduce. If `None` (the default),\n", " reduces all dimensions. Must be in the range\n", " `[-rank(input_tensor), rank(input_tensor))`.\n", " keepdims: If true, retains reduced dimensions with length 1.\n", " name: A name for the operation (optional).\n", " reduction_indices: The old (deprecated) name for axis.\n", " keep_dims: Deprecated alias for `keepdims`.\n", " \n", " Returns:\n", " The reduced tensor.\n", " \n", " @compatibility(numpy)\n", " Equivalent to np.mean\n", " \n", " Please note that `np.mean` has a `dtype` parameter that could be used to\n", " specify the output type. By default this is `dtype=float64`. On the other\n", " hand, `tf.reduce_mean` has an aggressive type inference from `input_tensor`,\n", " for example:\n", " \n", " ```python\n", " x = tf.constant([1, 0, 1, 0])\n", " tf.reduce_mean(x) # 0\n", " y = tf.constant([1., 0., 1., 0.])\n", " tf.reduce_mean(y) # 0.5\n", " ```\n", " \n", " @end_compatibility\n", "\n" ] } ], "source": [ "help(tf.reduce_mean)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (d) Tensorflow has some built-in layers to simplify your code." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function fully_connected in module tensorflow.contrib.layers.python.layers.layers:\n", "\n", "fully_connected(inputs, num_outputs, activation_fn=, normalizer_fn=None, normalizer_params=None, weights_initializer=._initializer at 0x7ff9f2ecd158>, weights_regularizer=None, biases_initializer=, biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, scope=None)\n", " Adds a fully connected layer.\n", " \n", " `fully_connected` creates a variable called `weights`, representing a fully\n", " connected weight matrix, which is multiplied by the `inputs` to produce a\n", " `Tensor` of hidden units. If a `normalizer_fn` is provided (such as\n", " `batch_norm`), it is then applied. Otherwise, if `normalizer_fn` is\n", " None and a `biases_initializer` is provided then a `biases` variable would be\n", " created and added the hidden units. Finally, if `activation_fn` is not `None`,\n", " it is applied to the hidden units as well.\n", " \n", " Note: that if `inputs` have a rank greater than 2, then `inputs` is flattened\n", " prior to the initial matrix multiply by `weights`.\n", " \n", " Args:\n", " inputs: A tensor of at least rank 2 and static value for the last dimension;\n", " i.e. `[batch_size, depth]`, `[None, None, None, channels]`.\n", " num_outputs: Integer or long, the number of output units in the layer.\n", " activation_fn: Activation function. The default value is a ReLU function.\n", " Explicitly set it to None to skip it and maintain a linear activation.\n", " normalizer_fn: Normalization function to use instead of `biases`. If\n", " `normalizer_fn` is provided then `biases_initializer` and\n", " `biases_regularizer` are ignored and `biases` are not created nor added.\n", " default set to None for no normalizer function\n", " normalizer_params: Normalization function parameters.\n", " weights_initializer: An initializer for the weights.\n", " weights_regularizer: Optional regularizer for the weights.\n", " biases_initializer: An initializer for the biases. If None skip biases.\n", " biases_regularizer: Optional regularizer for the biases.\n", " reuse: Whether or not the layer and its variables should be reused. To be\n", " able to reuse the layer scope must be given.\n", " variables_collections: Optional list of collections for all the variables or\n", " a dictionary containing a different list of collections per variable.\n", " outputs_collections: Collection to add the outputs.\n", " trainable: If `True` also add variables to the graph collection\n", " `GraphKeys.TRAINABLE_VARIABLES` (see tf.Variable).\n", " scope: Optional scope for variable_scope.\n", " \n", " Returns:\n", " The tensor variable representing the result of the series of operations.\n", " \n", " Raises:\n", " ValueError: If x has rank less than 2 or if its last dimension is not set.\n", "\n" ] } ], "source": [ "help(tf.contrib.layers.fully_connected)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (e) Use [variable scope](https://www.tensorflow.org/guide/variables#sharing_variables) to keep your variables organized." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "layer_0/W0:0\n", "layer_0/b0:0\n", "layer_1/W1:0\n", "layer_1/b1:0\n", "layer_2/W2:0\n", "layer_2/b2:0\n" ] } ], "source": [ "sess = tf_reset()\n", "\n", "# create variables\n", "with tf.variable_scope('layer_0'):\n", " W0 = tf.get_variable(name='W0', shape=[1, 20], initializer=tf.contrib.layers.xavier_initializer())\n", " b0 = tf.get_variable(name='b0', shape=[20], initializer=tf.constant_initializer(0.))\n", "\n", "with tf.variable_scope('layer_1'):\n", " W1 = tf.get_variable(name='W1', shape=[20, 20], initializer=tf.contrib.layers.xavier_initializer())\n", " b1 = tf.get_variable(name='b1', shape=[20], initializer=tf.constant_initializer(0.))\n", " \n", "with tf.variable_scope('layer_2'):\n", " W2 = tf.get_variable(name='W2', shape=[20, 1], initializer=tf.contrib.layers.xavier_initializer())\n", " b2 = tf.get_variable(name='b2', shape=[1], initializer=tf.constant_initializer(0.))\n", "\n", "# print the variables\n", "var_names = sorted([v.name for v in tf.global_variables()])\n", "print('\\n'.join(var_names))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (f) You can specify which GPU you want to use and how much memory you want to use" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "gpu_device = 0\n", "gpu_frac = 0.5\n", "\n", "# make only one of the GPUs visible\n", "import os\n", "os.environ[\"CUDA_VISIBLE_DEVICES\"] = str(gpu_device)\n", "\n", "# only use part of the GPU memory\n", "gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_frac)\n", "config = tf.ConfigProto(gpu_options=gpu_options)\n", "\n", "# create the session\n", "tf_sess = tf.Session(graph=tf.Graph(), config=config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### (g) You can use [tensorboard](https://www.tensorflow.org/guide/summaries_and_tensorboard) to visualize and monitor the training process." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }