Skip to content

Instantly share code, notes, and snippets.

@phraniiac
Last active January 26, 2017 15:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phraniiac/8a4bb2d84d8f4d42d4b41be6c916967c to your computer and use it in GitHub Desktop.
Save phraniiac/8a4bb2d84d8f4d42d4b41be6c916967c to your computer and use it in GitHub Desktop.
Assignment 2
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "kR-4eNdK6lYS"
},
"source": [
"Deep Learning\n",
"=============\n",
"\n",
"Assignment 2\n",
"------------\n",
"\n",
"Previously in `1_notmnist.ipynb`, we created a pickle with formatted datasets for training, development and testing on the [notMNIST dataset](http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html).\n",
"\n",
"The goal of this assignment is to progressively train deeper and more accurate models using TensorFlow."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
}
},
"colab_type": "code",
"collapsed": true,
"id": "JLpLa8Jt7Vu4"
},
"outputs": [],
"source": [
"# These are all the modules we'll be using later. Make sure you can import them\n",
"# before proceeding further.\n",
"from __future__ import print_function\n",
"import numpy as np\n",
"import tensorflow as tf\n",
"from six.moves import cPickle as pickle\n",
"from six.moves import range"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "1HrCK6e17WzV"
},
"source": [
"First reload the data we generated in `1_notmnist.ipynb`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
},
"output_extras": [
{
"item_id": 1
}
]
},
"colab_type": "code",
"collapsed": false,
"executionInfo": {
"elapsed": 19456,
"status": "ok",
"timestamp": 1449847956073,
"user": {
"color": "",
"displayName": "",
"isAnonymous": false,
"isMe": true,
"permissionId": "",
"photoUrl": "",
"sessionId": "0",
"userId": ""
},
"user_tz": 480
},
"id": "y3-cj1bpmuxc",
"outputId": "0ddb1607-1fc4-4ddb-de28-6c7ab7fb0c33"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training set (200000, 28, 28) (200000,)\n",
"Validation set (10000, 28, 28) (10000,)\n",
"Test set (10000, 28, 28) (10000,)\n"
]
}
],
"source": [
"pickle_file = 'notMNIST.pickle'\n",
"\n",
"with open(pickle_file, 'rb') as f:\n",
" save = pickle.load(f)\n",
" train_dataset = save['train_dataset']\n",
" train_labels = save['train_labels']\n",
" valid_dataset = save['valid_dataset']\n",
" valid_labels = save['valid_labels']\n",
" test_dataset = save['test_dataset']\n",
" test_labels = save['test_labels']\n",
" del save # hint to help gc free up memory\n",
" print('Training set', train_dataset.shape, train_labels.shape)\n",
" print('Validation set', valid_dataset.shape, valid_labels.shape)\n",
" print('Test set', test_dataset.shape, test_labels.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "L7aHrm6nGDMB"
},
"source": [
"Reformat into a shape that's more adapted to the models we're going to train:\n",
"- data as a flat matrix,\n",
"- labels as float 1-hot encodings."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
},
"output_extras": [
{
"item_id": 1
}
]
},
"colab_type": "code",
"collapsed": false,
"executionInfo": {
"elapsed": 19723,
"status": "ok",
"timestamp": 1449847956364,
"user": {
"color": "",
"displayName": "",
"isAnonymous": false,
"isMe": true,
"permissionId": "",
"photoUrl": "",
"sessionId": "0",
"userId": ""
},
"user_tz": 480
},
"id": "IRSyYiIIGIzS",
"outputId": "2ba0fc75-1487-4ace-a562-cf81cae82793"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training set (200000, 784) (200000, 10)\n",
"Validation set (10000, 784) (10000, 10)\n",
"Test set (10000, 784) (10000, 10)\n"
]
}
],
"source": [
"image_size = 28\n",
"num_labels = 10\n",
"\n",
"def reformat(dataset, labels):\n",
" dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32)\n",
" # Map 0 to [1.0, 0.0, 0.0 ...], 1 to [0.0, 1.0, 0.0 ...]\n",
" labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)\n",
" return dataset, labels\n",
"train_dataset, train_labels = reformat(train_dataset, train_labels)\n",
"valid_dataset, valid_labels = reformat(valid_dataset, valid_labels)\n",
"test_dataset, test_labels = reformat(test_dataset, test_labels)\n",
"print('Training set', train_dataset.shape, train_labels.shape)\n",
"print('Validation set', valid_dataset.shape, valid_labels.shape)\n",
"print('Test set', test_dataset.shape, test_labels.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "nCLVqyQ5vPPH"
},
"source": [
"We're first going to train a multinomial logistic regression using simple gradient descent.\n",
"\n",
"TensorFlow works like this:\n",
"* First you describe the computation that you want to see performed: what the inputs, the variables, and the operations look like. These get created as nodes over a computation graph. This description is all contained within the block below:\n",
"\n",
" with graph.as_default():\n",
" ...\n",
"\n",
"* Then you can run the operations on this graph as many times as you want by calling `session.run()`, providing it outputs to fetch from the graph that get returned. This runtime operation is all contained in the block below:\n",
"\n",
" with tf.Session(graph=graph) as session:\n",
" ...\n",
"\n",
"Let's load all the data into TensorFlow and build the computation graph corresponding to our training:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
}
},
"colab_type": "code",
"collapsed": false,
"id": "Nfv39qvtvOl_"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Tensor(\"add:0\", shape=(10000, 10), dtype=float32)\n"
]
}
],
"source": [
"# With gradient descent training, even this much data is prohibitive.\n",
"# Subset the training data for faster turnaround.\n",
"train_subset = 10000\n",
"\n",
"graph = tf.Graph()\n",
"with graph.as_default():\n",
"\n",
" # Input data.\n",
" # Load the training, validation and test data into constants that are\n",
" # attached to the graph.\n",
" tf_train_dataset = tf.constant(train_dataset[:train_subset, :])\n",
" tf_train_labels = tf.constant(train_labels[:train_subset])\n",
" tf_valid_dataset = tf.constant(valid_dataset)\n",
" tf_test_dataset = tf.constant(test_dataset)\n",
" \n",
" # Variables.\n",
" # These are the parameters that we are going to be training. The weight\n",
" # matrix will be initialized using random values following a (truncated)\n",
" # normal distribution. The biases get initialized to zero.\n",
" weights = tf.Variable(\n",
" tf.truncated_normal([image_size * image_size, num_labels]))\n",
" biases = tf.Variable(tf.zeros([num_labels]))\n",
" \n",
" # Training computation.\n",
" # We multiply the inputs with the weight matrix, and add biases. We compute\n",
" # the softmax and cross-entropy (it's one operation in TensorFlow, because\n",
" # it's very common, and it can be optimized). We take the average of this\n",
" # cross-entropy across all training examples: that's our loss.\n",
" logits = tf.matmul(tf_train_dataset, weights) + biases\n",
" print(logits)\n",
" loss = tf.reduce_mean(\n",
" tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))\n",
" \n",
" # Optimizer.\n",
" # We are going to find the minimum of this loss using gradient descent.\n",
" optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)\n",
" \n",
" # Predictions for the training, validation, and test data.\n",
" # These are not part of training, but merely here so that we can report\n",
" # accuracy figures as we train.\n",
" train_prediction = tf.nn.softmax(logits)\n",
" valid_prediction = tf.nn.softmax(\n",
" tf.matmul(tf_valid_dataset, weights) + biases)\n",
" test_prediction = tf.nn.softmax(tf.matmul(tf_test_dataset, weights) + biases)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "KQcL4uqISHjP"
},
"source": [
"Let's run this computation and iterate:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
},
"output_extras": [
{
"item_id": 9
}
]
},
"colab_type": "code",
"collapsed": false,
"executionInfo": {
"elapsed": 57454,
"status": "ok",
"timestamp": 1449847994134,
"user": {
"color": "",
"displayName": "",
"isAnonymous": false,
"isMe": true,
"permissionId": "",
"photoUrl": "",
"sessionId": "0",
"userId": ""
},
"user_tz": 480
},
"id": "z2cjdenH869W",
"outputId": "4c037ba1-b526-4d8e-e632-91e2a0333267"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initialized\n",
"Loss at step 0: 18.749348\n",
"Training accuracy: 8.9%\n",
"Validation accuracy: 11.7%\n",
"Loss at step 100: 2.315271\n",
"Training accuracy: 72.0%\n",
"Validation accuracy: 70.8%\n",
"Loss at step 200: 1.875187\n",
"Training accuracy: 74.6%\n",
"Validation accuracy: 72.9%\n",
"Loss at step 300: 1.628983\n",
"Training accuracy: 75.6%\n",
"Validation accuracy: 73.7%\n",
"Loss at step 400: 1.463280\n",
"Training accuracy: 76.3%\n",
"Validation accuracy: 74.2%\n",
"Loss at step 500: 1.340663\n",
"Training accuracy: 77.2%\n",
"Validation accuracy: 74.5%\n",
"Loss at step 600: 1.244613\n",
"Training accuracy: 77.7%\n",
"Validation accuracy: 74.7%\n",
"Loss at step 700: 1.166699\n",
"Training accuracy: 78.2%\n",
"Validation accuracy: 74.8%\n",
"Loss at step 800: 1.102058\n",
"Training accuracy: 78.7%\n",
"Validation accuracy: 75.1%\n",
"Test accuracy: 82.1%\n"
]
}
],
"source": [
"num_steps = 801\n",
"\n",
"def accuracy(predictions, labels):\n",
" return (100.0 * np.sum(np.argmax(predictions, 1) == np.argmax(labels, 1))\n",
" / predictions.shape[0])\n",
"\n",
"with tf.Session(graph=graph) as session:\n",
" # This is a one-time operation which ensures the parameters get initialized as\n",
" # we described in the graph: random weights for the matrix, zeros for the\n",
" # biases. \n",
" tf.initialize_all_variables().run()\n",
" print('Initialized')\n",
" for step in range(num_steps):\n",
" # Run the computations. We tell .run() that we want to run the optimizer,\n",
" # and get the loss value and the training predictions returned as numpy\n",
" # arrays.\n",
" _, l, predictions = session.run([optimizer, loss, train_prediction])\n",
" if (step % 100 == 0):\n",
" print('Loss at step %d: %f' % (step, l))\n",
" print('Training accuracy: %.1f%%' % accuracy(\n",
" predictions, train_labels[:train_subset, :]))\n",
" # Calling .eval() on valid_prediction is basically like calling run(), but\n",
" # just to get that one numpy array. Note that it recomputes all its graph\n",
" # dependencies.\n",
" print('Validation accuracy: %.1f%%' % accuracy(\n",
" valid_prediction.eval(), valid_labels))\n",
" print('Test accuracy: %.1f%%' % accuracy(test_prediction.eval(), test_labels))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "x68f-hxRGm3H"
},
"source": [
"Let's now switch to stochastic gradient descent training instead, which is much faster.\n",
"\n",
"The graph will be similar, except that instead of holding all the training data into a constant node, we create a `Placeholder` node which will be fed actual data at every call of `session.run()`."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
}
},
"colab_type": "code",
"collapsed": true,
"id": "qhPMzWYRGrzM"
},
"outputs": [],
"source": [
"batch_size = 128\n",
"\n",
"graph = tf.Graph()\n",
"with graph.as_default():\n",
"\n",
" # Input data. For the training data, we use a placeholder that will be fed\n",
" # at run time with a training minibatch.\n",
" tf_train_dataset = tf.placeholder(tf.float32,\n",
" shape=(batch_size, image_size * image_size))\n",
" tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))\n",
" tf_valid_dataset = tf.constant(valid_dataset)\n",
" tf_test_dataset = tf.constant(test_dataset)\n",
" \n",
" # Variables.\n",
" weights = tf.Variable(\n",
" tf.truncated_normal([image_size * image_size, num_labels]))\n",
" biases = tf.Variable(tf.zeros([num_labels]))\n",
" \n",
" # Training computation.\n",
" logits = tf.matmul(tf_train_dataset, weights) + biases\n",
" loss = tf.reduce_mean(\n",
" tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))\n",
" \n",
" # Optimizer.\n",
" optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)\n",
" \n",
" # Predictions for the training, validation, and test data.\n",
" train_prediction = tf.nn.softmax(logits)\n",
" valid_prediction = tf.nn.softmax(\n",
" tf.matmul(tf_valid_dataset, weights) + biases)\n",
" test_prediction = tf.nn.softmax(tf.matmul(tf_test_dataset, weights) + biases)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "XmVZESmtG4JH"
},
"source": [
"Let's run it:"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"cellView": "both",
"colab": {
"autoexec": {
"startup": false,
"wait_interval": 0
},
"output_extras": [
{
"item_id": 6
}
]
},
"colab_type": "code",
"collapsed": false,
"executionInfo": {
"elapsed": 66292,
"status": "ok",
"timestamp": 1449848003013,
"user": {
"color": "",
"displayName": "",
"isAnonymous": false,
"isMe": true,
"permissionId": "",
"photoUrl": "",
"sessionId": "0",
"userId": ""
},
"user_tz": 480
},
"id": "FoF91pknG_YW",
"outputId": "d255c80e-954d-4183-ca1c-c7333ce91d0a"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initialized\n",
"Minibatch loss at step 0: 424.366638\n",
"Minibatch accuracy: 3.9%\n",
"Validation accuracy: 34.1%\n",
"Minibatch loss at step 500: 8.625115\n",
"Minibatch accuracy: 80.5%\n",
"Validation accuracy: 79.1%\n",
"Minibatch loss at step 1000: 13.085954\n",
"Minibatch accuracy: 73.4%\n",
"Validation accuracy: 78.9%\n",
"Minibatch loss at step 1500: 6.064416\n",
"Minibatch accuracy: 85.9%\n",
"Validation accuracy: 80.7%\n",
"Minibatch loss at step 2000: 5.697366\n",
"Minibatch accuracy: 85.9%\n",
"Validation accuracy: 81.9%\n",
"Minibatch loss at step 2500: 9.753885\n",
"Minibatch accuracy: 75.0%\n",
"Validation accuracy: 81.5%\n",
"Minibatch loss at step 3000: 4.798896\n",
"Minibatch accuracy: 78.1%\n",
"Validation accuracy: 81.5%\n",
"Test accuracy: 88.8%\n"
]
}
],
"source": [
"num_steps = 3001\n",
"\n",
"with tf.Session(graph=graph) as session:\n",
" tf.initialize_all_variables().run()\n",
" print(\"Initialized\")\n",
" for step in range(num_steps):\n",
" # Pick an offset within the training data, which has been randomized.\n",
" # Note: we could use better randomization across epochs.\n",
" offset = (step * batch_size) % (train_labels.shape[0] - batch_size)\n",
" # Generate a minibatch.\n",
" batch_data = train_dataset[offset:(offset + batch_size), :]\n",
" batch_labels = train_labels[offset:(offset + batch_size), :]\n",
" # Prepare a dictionary telling the session where to feed the minibatch.\n",
" # The key of the dictionary is the placeholder node of the graph to be fed,\n",
" # and the value is the numpy array to feed to it.\n",
" feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}\n",
" _, l, predictions = session.run(\n",
" [optimizer, loss, train_prediction], feed_dict=feed_dict)\n",
" if (step % 500 == 0):\n",
" print(\"Minibatch loss at step %d: %f\" % (step, l))\n",
" print(\"Minibatch accuracy: %.1f%%\" % accuracy(predictions, batch_labels))\n",
" print(\"Validation accuracy: %.1f%%\" % accuracy(\n",
" valid_prediction.eval(), valid_labels))\n",
" print(\"Test accuracy: %.1f%%\" % accuracy(test_prediction.eval(), test_labels))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "7omWxtvLLxik"
},
"source": [
"---\n",
"Problem\n",
"-------\n",
"\n",
"Turn the logistic regression example with SGD into a 1-hidden layer neural network with rectified linear units [nn.relu()](https://www.tensorflow.org/versions/r0.7/api_docs/python/nn.html#relu) and 1024 hidden nodes. This model should improve your validation / test accuracy.\n",
"\n",
"---"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Implement ReLU and SGD.\n",
"image_size = 28\n",
"batch_size = 128\n",
"\n",
"def out_percep(weights_1, biases_1, weights_2, biases_2, dataset):\n",
" hidden_1 = tf.matmul(dataset, weights_1) + biases_1\n",
" relu_layer= tf.nn.relu(hidden_1)\n",
" logits = tf.matmul(relu_layer, weights_2) + biases_2\n",
" return tf.nn.softmax(logits), logits\n",
" \n",
"\n",
"graph = tf.Graph()\n",
"with graph.as_default():\n",
" #inputs.\n",
" tf_train_dataset = tf.placeholder(tf.float32,\n",
" shape=(batch_size, image_size*image_size))\n",
" tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))\n",
" tf_valid_dataset = tf.constant(valid_dataset)\n",
" tf_test_dataset = tf.constant(test_dataset)\n",
"\n",
" # Variables.\n",
" weights_1 = tf.Variable(\n",
" tf.truncated_normal([image_size * image_size, 1024]))\n",
" weights_2 = tf.Variable(\n",
" tf.truncated_normal([1024, num_labels]))\n",
" biases_1 = tf.Variable(tf.zeros([1024]))\n",
" biases_2 = tf.Variable(tf.zeros([num_labels]))\n",
" \n",
" # Training computation.\n",
" \n",
"# hidden_1 = tf.nn.relu(tf.matmul(tf_train_dataset, weights_1) + biases_1)\n",
" \n",
"# logits = tf.matmul(hidden_1, weights_2) + biases_2\n",
" \n",
" softmax_res, logits = out_percep(weights_1, biases_1, weights_2, biases_2, tf_train_dataset)\n",
" \n",
" loss = tf.reduce_mean(\n",
" tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))\n",
" \n",
" # Optimizer.\n",
" optimizer = tf.train.GradientDescentOptimizer(0.50).minimize(loss)\n",
" \n",
" # Predictions for the training, validation, and test data.\n",
" train_prediction = softmax_res\n",
" valid_prediction, valid_logits = out_percep(weights_1, biases_1, weights_2, biases_2, tf_valid_dataset)\n",
" test_prediction, test_logits = out_percep(weights_1, biases_1, weights_2, biases_2, tf_test_dataset)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initialized\n",
"Minibatch loss at step 0: 405.699036\n",
"Minibatch accuracy: 10.2%\n",
"Validation accuracy: 36.9%\n",
"Minibatch loss at step 500: 8.899696\n",
"Minibatch accuracy: 83.6%\n",
"Validation accuracy: 81.9%\n",
"Minibatch loss at step 1000: 11.526682\n",
"Minibatch accuracy: 78.9%\n",
"Validation accuracy: 80.0%\n",
"Minibatch loss at step 1500: 5.937747\n",
"Minibatch accuracy: 82.8%\n",
"Validation accuracy: 82.0%\n",
"Minibatch loss at step 2000: 4.136798\n",
"Minibatch accuracy: 82.8%\n",
"Validation accuracy: 81.5%\n",
"Minibatch loss at step 2500: 3.376491\n",
"Minibatch accuracy: 82.0%\n",
"Validation accuracy: 82.2%\n",
"Minibatch loss at step 3000: 3.462912\n",
"Minibatch accuracy: 83.6%\n",
"Validation accuracy: 82.8%\n",
"Test accuracy: 89.4%\n"
]
}
],
"source": [
"num_steps = 3001\n",
"\n",
"with tf.Session(graph=graph) as session:\n",
" tf.initialize_all_variables().run()\n",
" print(\"Initialized\")\n",
" for step in range(num_steps):\n",
" # Pick an offset within the training data, which has been randomized.\n",
" # Note: we could use better randomization across epochs.\n",
" offset = (step * batch_size) % (train_labels.shape[0] - batch_size)\n",
" # Generate a minibatch.\n",
" batch_data = train_dataset[offset:(offset + batch_size), :]\n",
" batch_labels = train_labels[offset:(offset + batch_size), :]\n",
" # Prepare a dictionary telling the session where to feed the minibatch.\n",
" # The key of the dictionary is the placeholder node of the graph to be fed,\n",
" # and the value is the numpy array to feed to it.\n",
" feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}\n",
" _, l, predictions = session.run(\n",
" [optimizer, loss, train_prediction], feed_dict=feed_dict)\n",
" if (step % 500 == 0):\n",
" print(\"Minibatch loss at step %d: %f\" % (step, l))\n",
" print(\"Minibatch accuracy: %.1f%%\" % accuracy(predictions, batch_labels))\n",
" print(\"Validation accuracy: %.1f%%\" % accuracy(\n",
" valid_prediction.eval(), valid_labels))\n",
" print(\"Test accuracy: %.1f%%\" % accuracy(test_prediction.eval(), test_labels))"
]
}
],
"metadata": {
"colab": {
"default_view": {},
"name": "2_fullyconnected.ipynb",
"provenance": [],
"version": "0.3.2",
"views": {}
},
"kernelspec": {
"display_name": "Python [default]",
"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"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment