diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/.gitignore b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..88cc10700dcff19ceea989234231e2222643aecd --- /dev/null +++ b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/.gitignore @@ -0,0 +1,2 @@ +*.jpg +*.png diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/.ipynb_checkpoints/lab3_RNN-checkpoint.ipynb b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/.ipynb_checkpoints/lab3_RNN-checkpoint.ipynb new file mode 100755 index 0000000000000000000000000000000000000000..ce1e79a738806f999d3f271d52ef515ffc811a38 --- /dev/null +++ b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/.ipynb_checkpoints/lab3_RNN-checkpoint.ipynb @@ -0,0 +1,887 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline \n", + "%matplotlib nbagg\n", + "import tensorflow as tf\n", + "import matplotlib\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from IPython import display\n", + "from data_generator import get_batch, print_valid_characters\n", + "from tensorflow.python.framework.ops import reset_default_graph\n", + "\n", + "import tf_utils" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Recurrent Neural Networks\n", + "\n", + "Recurrent neural networks are the natural type of neural network to use for sequential data i.e. time series analysis, translation, speech recognition, biological sequence analysis etc. Recurrent neural networks works by recursively applying the same operation at each time step of the data sequence and having layers that pass information from previous time step to the current. It can therefore naturally handle input of varying length. Recurrent networks can be used for several prediction tasks including: sequence-to-class, sequence tagging, and sequence-to-sequence predictions.\n", + "\n", + "In this exercise we'll implement a Encoder-Decoder RNN based on the GRU unit for a simple sequence to sequence translation task. This type of models have shown impressive performance in Neural Machine Translation and Image Caption generation. \n", + "\n", + "For more in depth background material on RNNs please see [Supervised Sequence Labelling with Recurrent\n", + "Neural Networks](https://www.cs.toronto.edu/~graves/preprint.pdf) by Alex Graves\n", + "\n", + "We know that LSTMs and GRUs are difficult to understand. A very good non-mathematical introduction is [Chris Olahs blog](http://colah.github.io/posts/2015-08-Understanding-LSTMs/). (All the posts are nice and cover various topics within machine-learning)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Encoder-Decoder\n", + "In the encoder-decoder structure one RNN (blue) encodes the input and a second RNN (red) calculates the target values. One essential step is to let the encoder and decoder communicate. In the simplest approach you use the last hidden state of the encoder to initialize the decoder. Other approaches lets the decoder attend to different parts of the encoded input at different timesteps in the decoding process. \n", + "\n", + "<img src=\"files/enc-dec.png\", width=400>\n", + "\n", + "In our implementation we use a RNN with gated recurrent units (GRU) as encoder. We then use the last hidden state of the encoder ($h^{enc}_T$) as input to the decoder which is also a GRU RNN. \n", + "\n", + "### RNNs in TensorFlow\n", + "TensorFlow has implementations of LSTM and GRU units. Both implementations assume that the input from the tensor below has the shape **(batch_size, seq_len, num_features)**, unless you have `time\\_major=True`. In this excercise we will use the GRU unit since it only stores a single hidden value per neuron (LSTMs stores two) and is approximately twice as fast as the LSTM unit.\n", + "\n", + "As stated above we will implement a Encoder-Decoder model. The simplest way to do this is to encode the input sequence using the Encoder model. We will then use the last hidden state of the Encoder $h^{enc}_T$ as input to the decoder model which then uses this information (simply a fixed length vector of numbers) to produce the targets. There is (at least) two ways to input $h^{enc}_T$ into the decoder\n", + "\n", + "1. Repeatly use $h^{enc}_T$ as input to the Decoder at each decode time step, as well as the previously computed word\n", + "2. Intialize the decoder using $h^{enc}_T$ and run the decoder without any inputs\n", + "\n", + "In this exercise we will follow the second approach because it's easier to implement. To do this need to create a tensorflow layer that takes $h^{enc}_T$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The Data\n", + "Since RNN models can be very slow to train on real large datasets we will generate some simpler training data for this exercise. The task for the RNN is simply to translate a string of letters spelling the numbers between 0-9 into the corresponding numbers i.e\n", + "\n", + "\"one two five\" --> \"125#\" (we use # as a special end-of-sequence character)\n", + "\n", + "To input the strings into the RNN model we translate the characters into a vector integers using a simple translation table (i.e. 'h'->16, 'o'-> 17 etc). The code below prints a few input/output pairs using the *get_batch* function which randomy produces the data.\n", + "\n", + "Do note; that as showed in the illustration above for input to the decoder the end-of-sequence tag is flipped, and used in the beginning instead of the end. This tag is known as start-of-sequence, but often the end-of-sequence tag is just reused for this purpose.\n", + "\n", + "In the data loader below you will see two targets, target input and target output. Where the input will be used to compute the translation and output used for the loss function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "batch_size = 3\n", + "inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \\\n", + "text_inputs, text_targets_in, text_targets_out = \\\n", + " get_batch(batch_size=batch_size, max_digits=2, min_digits=1)\n", + "\n", + "print \"input types:\", inputs.dtype, inputs_seqlen.dtype, targets_in.dtype, targets_out.dtype, targets_seqlen.dtype\n", + "print print_valid_characters()\n", + "print \"Stop/start character = #\"\n", + "\n", + "for i in range(batch_size):\n", + " print \"\\nSAMPLE\",i\n", + " print \"TEXT INPUTS:\\t\\t\\t\", text_inputs[i]\n", + " print \"TEXT TARGETS INPUT:\\t\\t\", text_targets_in[i]\n", + " print \"TEXT TARGETS OUTPUT:\\t\\t\", text_targets_out[i]\n", + " print \"ENCODED INPUTS:\\t\\t\\t\", inputs[i]\n", + " print \"INPUTS SEQUENCE LENGTH:\\t\\t\", inputs_seqlen[i]\n", + " print \"ENCODED TARGETS INPUT:\\t\\t\", targets_in[i]\n", + " print \"ENCODED TARGETS OUTPUT:\\t\\t\", targets_out[i]\n", + " print \"TARGETS SEQUENCE LENGTH:\\t\", targets_seqlen[i]\n", + " print \"TARGETS MASK:\\t\\t\\t\", targets_mask[i]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Encoder Decoder model setup\n", + "Below is the TensorFlow model definition. We use an embedding layer to go from integer representation to vector representation of the input.\n", + "\n", + "Note that we have made use of a custom decoder wrapper which can be found in `rnn.py`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# resetting the graph\n", + "reset_default_graph()\n", + "\n", + "# Setting up hyperparameters and general configs\n", + "MAX_DIGITS = 5\n", + "MIN_DIGITS = 5\n", + "NUM_INPUTS = 27\n", + "NUM_OUTPUTS = 11 #(0-9 + '#')\n", + "\n", + "BATCH_SIZE = 100\n", + "# try various learning rates 1e-2 to 1e-5\n", + "LEARNING_RATE = 0.005\n", + "X_EMBEDDINGS = 8\n", + "t_EMBEDDINGS = 8\n", + "NUM_UNITS_ENC = 10\n", + "NUM_UNITS_DEC = 10\n", + "\n", + "\n", + "# Setting up placeholders, these are the tensors that we \"feed\" to our network\n", + "Xs = tf.placeholder(tf.int32, shape=[None, None], name='X_input')\n", + "ts_in = tf.placeholder(tf.int32, shape=[None, None], name='t_input_in')\n", + "ts_out = tf.placeholder(tf.int32, shape=[None, None], name='t_input_out')\n", + "X_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_mask = tf.placeholder(tf.float32, shape=[None, None], name='t_mask')\n", + "\n", + "# Building the model\n", + "\n", + "# first we build the embeddings to make our characters into dense, trainable vectors\n", + "X_embeddings = tf.get_variable('X_embeddings', [NUM_INPUTS, X_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "t_embeddings = tf.get_variable('t_embeddings', [NUM_OUTPUTS, t_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "\n", + "# setting up weights for computing the final output\n", + "W_out = tf.get_variable('W_out', [NUM_UNITS_DEC, NUM_OUTPUTS])\n", + "b_out = tf.get_variable('b_out', [NUM_OUTPUTS])\n", + "\n", + "X_embedded = tf.gather(X_embeddings, Xs, name='embed_X')\n", + "t_embedded = tf.gather(t_embeddings, ts_in, name='embed_t')\n", + "\n", + "# forward encoding\n", + "enc_cell = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)#python.ops.rnn_cell.GRUCell\n", + "_, enc_state = tf.nn.dynamic_rnn(cell=enc_cell, inputs=X_embedded,\n", + " sequence_length=X_len, dtype=tf.float32)\n", + "# use below incase TF's makes issues\n", + "#enc_state, _ = tf_utils.encoder(X_embedded, X_len, 'encoder', NUM_UNITS_ENC)\n", + "#\n", + "#enc_state = tf.concat(1, [enc_state, enc_state])\n", + "\n", + "# decoding\n", + "# note that we are using a wrapper for decoding here, this wrapper is hardcoded to only use GRU\n", + "# check out tf_utils to see how you make your own decoder\n", + "dec_out, valid_dec_out = tf_utils.decoder(enc_state, t_embedded, t_len, \n", + " NUM_UNITS_DEC, t_embeddings,\n", + " W_out, b_out)\n", + "\n", + "# reshaping to have [batch_size*seqlen, num_units]\n", + "out_tensor = tf.reshape(dec_out, [-1, NUM_UNITS_DEC])\n", + "valid_out_tensor = tf.reshape(valid_dec_out, [-1, NUM_UNITS_DEC])\n", + "# computing output\n", + "out_tensor = tf.matmul(out_tensor, W_out) + b_out\n", + "valid_out_tensor = tf.matmul(valid_out_tensor, W_out) + b_out\n", + "# reshaping back to sequence\n", + "b_size = tf.shape(X_len)[0] # use a variable we know has batch_size in [0]\n", + "seq_len = tf.shape(t_embedded)[1] # variable we know has sequence length in [1]\n", + "num_out = tf.constant(NUM_OUTPUTS) # casting NUM_OUTPUTS to a tensor variable\n", + "out_shape = tf.concat(0, [tf.expand_dims(b_size, 0),\n", + " tf.expand_dims(seq_len, 0),\n", + " tf.expand_dims(num_out, 0)])\n", + "out_tensor = tf.reshape(out_tensor, out_shape)\n", + "valid_out_tensor = tf.reshape(valid_out_tensor, out_shape)\n", + "# handling shape loss\n", + "#out_tensor.set_shape([None, None, NUM_OUTPUTS])\n", + "y = out_tensor\n", + "y_valid = valid_out_tensor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# print all the variable names and shapes\n", + "for var in tf.all_variables():\n", + " s = var.name + \" \"*(40-len(var.name))\n", + " print s, var.value().get_shape()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Defining the cost function, gradient clipping and accuracy\n", + "Because the targets are categorical we use the cross entropy error.\n", + "As the data is sequential we use the sequence to sequence cross entropy supplied in `tf_utils.py`.\n", + "We use the Adam optimizer but you can experiment with the different optimizers implemented in [TensorFlow](https://www.tensorflow.org/versions/r0.10/api_docs/python/train.html#optimizers)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def loss_and_acc(preds):\n", + " # sequence_loss_tensor is a modification of TensorFlow's own sequence_to_sequence_loss\n", + " # TensorFlow's seq2seq loss works with a 2D list instead of a 3D tensors\n", + " loss = tf_utils.sequence_loss_tensor(preds, ts_out, t_mask, NUM_OUTPUTS) # notice that we use ts_out here!\n", + " # if you want regularization\n", + " #reg_scale = 0.00001\n", + " #regularize = tf.contrib.layers.l2_regularizer(reg_scale)\n", + " #params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)\n", + " #reg_term = sum([regularize(param) for param in params])\n", + " #loss += reg_term\n", + " # calculate accuracy\n", + " argmax = tf.to_int32(tf.argmax(preds, 2))\n", + " correct = tf.to_float(tf.equal(argmax, ts_out)) * t_mask\n", + " accuracy = tf.reduce_sum(correct) / tf.reduce_sum(t_mask)\n", + " return loss, accuracy, argmax\n", + "\n", + "loss, accuracy, predictions = loss_and_acc(y)\n", + "loss_valid, accuracy_valid, predictions_valid = loss_and_acc(y_valid)\n", + "\n", + "# use lobal step to keep track of our iterations\n", + "global_step = tf.Variable(0, name='global_step', trainable=False)\n", + "# pick optimizer, try momentum or adadelta\n", + "optimizer = tf.train.AdamOptimizer(LEARNING_RATE)\n", + "# extract gradients for each variable\n", + "grads_and_vars = optimizer.compute_gradients(loss)\n", + "# add below for clipping by norm\n", + "#gradients, variables = zip(*grads_and_vars) # unzip list of tuples\n", + "#clipped_gradients, global_norm = (\n", + "# tf.clip_by_global_norm(gradients, self.clip_norm) )\n", + "#grads_and_vars = zip(clipped_gradients, variables)\n", + "# apply gradients and make trainable function\n", + "train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# print all the variable names and shapes\n", + "# notice that we now have the optimizer Adam as well!\n", + "for var in tf.all_variables():\n", + " s = var.name + \" \"*(40-len(var.name))\n", + " print s, var.value().get_shape()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# as always, test the forward pass and initialize the tf.Session!\n", + "# here is some dummy data\n", + "batch_size=3\n", + "inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \\\n", + "text_inputs, text_targets_in, text_targets_out = \\\n", + " get_batch(batch_size=batch_size, max_digits=7, min_digits=2)\n", + "\n", + "for i in range(batch_size):\n", + " print \"\\nSAMPLE\",i\n", + " print \"TEXT INPUTS:\\t\\t\\t\", text_inputs[i]\n", + " print \"TEXT TARGETS INPUT:\\t\\t\", text_targets_in[i]\n", + "\n", + "# restricting memory usage, TensorFlow is greedy and will use all memory otherwise\n", + "gpu_opts = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)\n", + "# initialize the Session\n", + "sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_opts))\n", + "# test train part\n", + "sess.run(tf.initialize_all_variables())\n", + "feed_dict = {Xs: inputs, X_len: inputs_seqlen, ts_in: targets_in,\n", + " ts_out: targets_out, t_len: targets_seqlen}\n", + "fetches = [y]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y\", res[0].shape\n", + "\n", + "# test validation part\n", + "fetches = [y_valid]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y_valid\", res[0].shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#Generate some validation data\n", + "X_val, X_len_val, t_in_val, t_out_val, t_len_val, t_mask_val, \\\n", + "text_inputs_val, text_targets_in_val, text_targets_out_val = \\\n", + " get_batch(batch_size=5000, max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + "print \"X_val\", X_val.shape\n", + "print \"t_out_val\", t_out_val.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Training" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# setting up running parameters\n", + "val_interval = 5000\n", + "samples_to_process = 3e5\n", + "samples_processed = 0\n", + "samples_val = []\n", + "costs, accs_val = [], []\n", + "plt.figure()\n", + "try:\n", + " while samples_processed < samples_to_process:\n", + " # load data\n", + " X_tr, X_len_tr, t_in_tr, t_out_tr, t_len_tr, t_mask_tr, \\\n", + " text_inputs_tr, text_targets_in_tr, text_targets_out_tr = \\\n", + " get_batch(batch_size=BATCH_SIZE,max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + " # make fetches\n", + " fetches_tr = [train_op, loss, accuracy]\n", + " # set up feed dict\n", + " feed_dict_tr = {Xs: X_tr, X_len: X_len_tr, ts_in: t_in_tr,\n", + " ts_out: t_out_tr, t_len: t_len_tr, t_mask: t_mask_tr}\n", + " # run the model\n", + " res = tuple(sess.run(fetches=fetches_tr, feed_dict=feed_dict_tr))\n", + " _, batch_cost, batch_acc = res\n", + " costs += [batch_cost]\n", + " samples_processed += BATCH_SIZE\n", + " #if samples_processed % 1000 == 0: print batch_cost, batch_acc\n", + " #validation data\n", + " if samples_processed % val_interval == 0:\n", + " #print \"validating\"\n", + " fetches_val = [accuracy_valid, y_valid]\n", + " feed_dict_val = {Xs: X_val, X_len: X_len_val, ts_in: t_in_val,\n", + " ts_out: t_out_val, t_len: t_len_val, t_mask: t_mask_val}\n", + " res = tuple(sess.run(fetches=fetches_val, feed_dict=feed_dict_val))\n", + " acc_val, output_val = res\n", + " samples_val += [samples_processed]\n", + " accs_val += [acc_val]\n", + " plt.plot(samples_val, accs_val, 'g-')\n", + " plt.ylabel('Validation Accuracy', fontsize=15)\n", + " plt.xlabel('Processed samples', fontsize=15)\n", + " plt.title('', fontsize=20)\n", + " plt.grid('on')\n", + " plt.savefig(\"out.png\")\n", + " display.display(display.Image(filename=\"out.png\"))\n", + " display.clear_output(wait=True)\n", + "except KeyboardInterrupt:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [], + "source": [ + "#plot of validation accuracy for each target position\n", + "plt.figure(figsize=(7,7))\n", + "plt.plot(np.mean(np.argmax(output_val,axis=2)==t_out_val,axis=0))\n", + "plt.ylabel('Accuracy', fontsize=15)\n", + "plt.xlabel('Target position', fontsize=15)\n", + "#plt.title('', fontsize=20)\n", + "plt.grid('on')\n", + "plt.show()\n", + "#why do the plot look like this?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exercises:\n", + "\n", + "1. The model has two GRU networks. The ```GRUEncoder``` and the ```GRUDecoder```.\n", + "A GRU is parameterized by a update gate `z`, reset gate `r` and the cell `c`.\n", + "Under normal circumstances, such as in the TensorFlow GRUCell implementation, these gates have been stacked for faster computation, but in the custom decoder each weight and bias are as described in the original [article for GRU](https://arxiv.org/abs/1406.1078).\n", + "Thus we have the following weights and bias; ```{decoder/W_z_x:0, decoder/W_z_h:0, b_updategate, decoder/b_z:0, decoder/W_r_x:0, decoder/W_r_h:0, decoder/b_r:0, decoder/W_c_x:0, decoder/W_c_h:0, decoder/b_h:0}```.\n", + "Try to explain the shape of ```decoder/W_z_x:0``` and ```decoder/W_z_h:0```. Why are they different? You can find the equations for the gru at: [GRU](http://lasagne.readthedocs.io/en/latest/modules/layers/recurrent.html#lasagne.layers.GRULayer). \n", + "\n", + "2. The GRUunit is able to ignore the input and just copy the previous hidden state. In the begining of training this might be desireable behaviour because it helps the model learn long range dependencies. You can make the model ignore the input by modifying initial bias values. What bias would you modify and how would you modify it? Again you'll need to refer to the GRU equations: [GRU](http://lasagne.readthedocs.io/en/latest/modules/layers/recurrent.html#lasagne.layers.GRULayer)\n", + "Further, if you look into `tf_utils.py` and search for the `decoder(...)` function, you will see that the init for each weight and bias can be changed.\n", + "\n", + "3. Try setting MIN_DIGITS and MAX_DIGITS to 20\n", + "\n", + "4. What is the final validation performance? Why do you think it is not better? Comment on the accuracy for each position in of the output symbols?\n", + "\n", + "5. Why do you think the validation performance looks more \"jig-saw\" like compared to FFN and CNN models?\n", + "\n", + "6. In the example we stack a softmax layer on top of a Recurrent layer. In the code snippet below explain how we can do that?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "reset_default_graph()\n", + "\n", + "bs_, seqlen_, numinputs_ = 16, 140, 40\n", + "x_pl_ = tf.placeholder(tf.float32, [bs_, seqlen_, numinputs_])\n", + "gru_cell_ = tf.nn.rnn_cell.GRUCell(10)\n", + "l_gru_, gru_state_ = tf.nn.dynamic_rnn(gru_cell_, x_pl_, dtype=tf.float32)\n", + "l_reshape_ = tf.reshape(l_gru_, [-1, 10])\n", + "\n", + "l_softmax_ = tf.contrib.layers.fully_connected(l_reshape_, 11, activation_fn=tf.nn.softmax)\n", + "l_softmax_seq_ = tf.reshape(l_softmax_, [bs_, seqlen_, -1])\n", + "\n", + "print \"l_input_\", x_pl_.get_shape()\n", + "print \"l_gru_\", l_gru_.get_shape()\n", + "print \"l_reshape_\", l_reshape_.get_shape()\n", + "print \"l_softmax_\", l_softmax_.get_shape()\n", + "print \"l_softmax_seq_\", l_softmax_seq_.get_shape()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "6. Optional: You are interested in doing sentiment analysis on tweets, i.e classification as positive or negative. You decide read over the twitter seqeuence and use the last hidden state to do the classification. How can you modify the small network above to only outa single classification for network? Hints: look at the gru\\_state\\_ or the [tf.slice](https://www.tensorflow.org/versions/r0.10/api_docs/python/array_ops.html#slice) in the API.\n", + "\n", + "\n", + "7. Optional: Bidirectional Encoder, Bidirectional Encoders are usually implemented by running a forward model and a backward model (a forward model on a reversed sequence) separately and the concatenating them before parsing them on to the next layer. To reverse the sequence try looking at [tf.reverse_sequence](https://www.tensorflow.org/versions/r0.10/api_docs/python/array_ops.html#reverse_sequence)\n", + "\n", + "```\n", + "enc_cell = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)\n", + "_, enc_state = tf.nn.dynamic_rnn(cell=enc_cell, inputs=X_embedded,\n", + " sequence_length=X_len, dtype=tf.float32, scope=\"rnn_forward\")\n", + "\n", + "X_embedded_backwards = tf.reverse_sequence(X_embedded, tf.to_int64(X_len), 1)\n", + "enc_cell_backwards = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)\n", + "_, enc_state_backwards = tf.nn.dynamic_rnn(cell=enc_cell_backwards, inputs=X_embedded_backwards,\n", + " sequence_length=X_len, dtype=tf.float32, scope=\"rnn_backward\")\n", + "\n", + "enc_state = tf.concat(1, [enc_state, enc_state_backwards])\n", + "```\n", + "\n", + "Note: you will need to double the NUM_UNITS_DEC, as it currently does not support different sizes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Attention Decoder (LSTM)\n", + "Soft attention for recurrent neural networks have recently attracted a lot of interest.\n", + "These methods let the Decoder model selective focus on which part of the encoder sequence it will use for each decoded output symbol.\n", + "This relieves the encoder from having to compress the input sequence into a fixed size vector representation passed on to the decoder.\n", + "Secondly we can interrogate the decoder network about where it attends while producing the ouputs.\n", + "below we'll implement an LSTM-decoder with selective attention and show that it significantly improves the performance of the toy translation task.\n", + "\n", + "The siminal attention paper is https://arxiv.org/pdf/1409.0473v7.pdf\n", + "\n", + "The principle of attention models is simple. \n", + "\n", + "1. Use the encoder to get the hidden represention $\\{h^1_e, ...h^n_e\\}$ for each position in the input sequence. \n", + "2. for timestep $t$ in the decoder do for $m = 1...n$ : $a_m = f(h^m_e, h^d_t)$. Where f is a function returning a scalar value. \n", + "3. You can then normalize the sequence of scalars $\\{a_1, ... a_n\\}$ to get probablities $\\{p_1, ... p_n\\}$.\n", + "4. Weight each $h^e_t$ by its probablity $p_t$ and sum to get $h_{in}$.\n", + "5. Use $h_{in}$ as an additional input to the decoder. $h_{in}$ is recalculated each time the decoder is updated." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# resetting the graph\n", + "reset_default_graph()\n", + "\n", + "# Setting up hyperparameters and general configs\n", + "MAX_DIGITS = 10\n", + "MIN_DIGITS = 10\n", + "NUM_INPUTS = 27\n", + "NUM_OUTPUTS = 11 #(0-9 + '#')\n", + "\n", + "BATCH_SIZE = 100\n", + "# try various learning rates 1e-2 to 1e-5\n", + "LEARNING_RATE = 0.005\n", + "X_EMBEDDINGS = 8\n", + "t_EMBEDDINGS = 8\n", + "NUM_UNITS_ENC = 10\n", + "NUM_UNITS_DEC = 10\n", + "NUM_UNITS_ATTN = 20\n", + "\n", + "\n", + "# Setting up placeholders, these are the tensors that we \"feed\" to our network\n", + "Xs = tf.placeholder(tf.int32, shape=[None, None], name='X_input')\n", + "ts_in = tf.placeholder(tf.int32, shape=[None, None], name='t_input_in')\n", + "ts_out = tf.placeholder(tf.int32, shape=[None, None], name='t_input_out')\n", + "X_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_mask = tf.placeholder(tf.float32, shape=[None, None], name='t_mask')\n", + "\n", + "# Building the model\n", + "\n", + "# first we build the embeddings to make our characters into dense, trainable vectors\n", + "X_embeddings = tf.get_variable('X_embeddings', [NUM_INPUTS, X_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "t_embeddings = tf.get_variable('t_embeddings', [NUM_OUTPUTS, t_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "\n", + "# setting up weights for computing the final output\n", + "W_out = tf.get_variable('W_out', [NUM_UNITS_DEC, NUM_OUTPUTS])\n", + "b_out = tf.get_variable('b_out', [NUM_OUTPUTS])\n", + "\n", + "X_embedded = tf.gather(X_embeddings, Xs, name='embed_X')\n", + "t_embedded = tf.gather(t_embeddings, ts_in, name='embed_t')\n", + "\n", + "# forward encoding\n", + "enc_cell = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)#python.ops.rnn_cell.GRUCell\n", + "enc_out, enc_state = tf.nn.dynamic_rnn(cell=enc_cell, inputs=X_embedded,\n", + " sequence_length=X_len, dtype=tf.float32)\n", + "# use below in case TF's does not work as intended\n", + "#enc_state, _ = tf_utils.encoder(X_embedded, X_len, 'encoder', NUM_UNITS_ENC)\n", + "#\n", + "#enc_state = tf.concat(1, [enc_state, enc_state])\n", + "\n", + "# decoding\n", + "# note that we are using a wrapper for decoding here, this wrapper is hardcoded to only use GRU\n", + "# check out tf_utils to see how you make your own decoder\n", + "dec_out, dec_out_valid, alpha_valid = \\\n", + " tf_utils.attention_decoder(enc_out, X_len, enc_state, t_embedded, t_len,\n", + " NUM_UNITS_DEC, NUM_UNITS_ATTN, t_embeddings,\n", + " W_out, b_out)\n", + "\n", + "# reshaping to have [batch_size*seqlen, num_units]\n", + "out_tensor = tf.reshape(dec_out, [-1, NUM_UNITS_DEC])\n", + "out_tensor_valid = tf.reshape(dec_out_valid, [-1, NUM_UNITS_DEC])\n", + "# computing output\n", + "out_tensor = tf.matmul(out_tensor, W_out) + b_out\n", + "out_tensor_valid = tf.matmul(out_tensor_valid, W_out) + b_out\n", + "# reshaping back to sequence\n", + "b_size = tf.shape(X_len)[0] # use a variable we know has batch_size in [0]\n", + "seq_len = tf.shape(t_embedded)[1] # variable we know has sequence length in [1]\n", + "num_out = tf.constant(NUM_OUTPUTS) # casting NUM_OUTPUTS to a tensor variable\n", + "out_shape = tf.concat(0, [tf.expand_dims(b_size, 0),\n", + " tf.expand_dims(seq_len, 0),\n", + " tf.expand_dims(num_out, 0)])\n", + "out_tensor = tf.reshape(out_tensor, out_shape)\n", + "out_tensor_valid = tf.reshape(out_tensor_valid, out_shape)\n", + "# handling shape loss\n", + "#out_tensor.set_shape([None, None, NUM_OUTPUTS])\n", + "y = out_tensor\n", + "y_valid = out_tensor_valid" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def loss_and_acc(preds):\n", + " # sequence_loss_tensor is a modification of TensorFlow's own sequence_to_sequence_loss\n", + " # TensorFlow's seq2seq loss works with a 2D list instead of a 3D tensors\n", + " loss = tf_utils.sequence_loss_tensor(preds, ts_out, t_mask, NUM_OUTPUTS) # notice that we use ts_out here!\n", + " # if you want regularization\n", + " reg_scale = 0.00001\n", + " regularize = tf.contrib.layers.l2_regularizer(reg_scale)\n", + " params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)\n", + " reg_term = sum([regularize(param) for param in params])\n", + " loss += reg_term\n", + " # calculate accuracy\n", + " argmax = tf.to_int32(tf.argmax(preds, 2))\n", + " correct = tf.to_float(tf.equal(argmax, ts_out)) * t_mask\n", + " accuracy = tf.reduce_sum(correct) / tf.reduce_sum(t_mask)\n", + " return loss, accuracy, argmax\n", + "\n", + "loss, accuracy, predictions = loss_and_acc(y)\n", + "loss_valid, accuracy_valid, predictions_valid = loss_and_acc(y_valid)\n", + "\n", + "# use lobal step to keep track of our iterations\n", + "global_step = tf.Variable(0, name='global_step', trainable=False)\n", + "# pick optimizer, try momentum or adadelta\n", + "optimizer = tf.train.AdamOptimizer(LEARNING_RATE)\n", + "# extract gradients for each variable\n", + "grads_and_vars = optimizer.compute_gradients(loss)\n", + "# add below for clipping by norm\n", + "#gradients, variables = zip(*grads_and_vars) # unzip list of tuples\n", + "#clipped_gradients, global_norm = (\n", + "# tf.clip_by_global_norm(gradients, self.clip_norm) )\n", + "#grads_and_vars = zip(clipped_gradients, variables)\n", + "# apply gradients and make trainable function\n", + "train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# as always, test the forward pass and start the tf.Session!\n", + "# here is some dummy data\n", + "batch_size = 3\n", + "inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \\\n", + "text_inputs, text_targets_in, text_targets_out = \\\n", + " get_batch(batch_size=3, max_digits=7, min_digits=2)\n", + "\n", + "for i in range(batch_size):\n", + " print \"\\nSAMPLE\",i\n", + " print \"TEXT INPUTS:\\t\\t\\t\", text_inputs[i]\n", + " print \"TEXT TARGETS INPUT:\\t\\t\", text_targets_in[i]\n", + "\n", + "# restricting memory usage, TensorFlow is greedy and will use all memory otherwise\n", + "gpu_opts = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)\n", + "# initialize the Session\n", + "sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_opts))\n", + "# test train part\n", + "sess.run(tf.initialize_all_variables())\n", + "feed_dict = {Xs: inputs, X_len: inputs_seqlen, ts_in: targets_in,\n", + " ts_out: targets_out, t_len: targets_seqlen}\n", + "fetches = [y]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y\", res[0].shape\n", + "\n", + "# test validation part\n", + "fetches = [y_valid]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y_valid\", res[0].shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [], + "source": [ + "# print all the variable names and shapes\n", + "# notice that W_z is now packed, such that it contains both W_z_h and W_x_h, this is for optimization\n", + "# further, we now have W_s, b_s. This is so NUM_UNITS_ENC and NUM_UNITS_DEC does not have to share shape ..!\n", + "for var in tf.all_variables():\n", + " s = var.name + \" \"*(40-len(var.name))\n", + " print s, var.value().get_shape()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#Generate some validation data\n", + "X_val, X_len_val, t_in_val, t_out_val, t_len_val, t_mask_val, \\\n", + "text_inputs_val, text_targets_in_val, text_targets_out_val = \\\n", + " get_batch(batch_size=5000, max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + "print \"X_val\", X_val.shape\n", + "print \"t_out_val\", t_out_val.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [], + "source": [ + "# NOTICE - THIS MIGHT TAKE UPTO 30 MINUTES ON CPU..!\n", + "# setting up running parameters\n", + "val_interval = 5000\n", + "samples_to_process = 3e5\n", + "samples_processed = 0\n", + "samples_val = []\n", + "costs, accs = [], []\n", + "plt.figure()\n", + "try:\n", + " while samples_processed < samples_to_process:\n", + " # load data\n", + " X_tr, X_len_tr, t_in_tr, t_out_tr, t_len_tr, t_mask_tr, \\\n", + " text_inputs_tr, text_targets_in_tr, text_targets_out_tr = \\\n", + " get_batch(batch_size=BATCH_SIZE,max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + " # make fetches\n", + " fetches_tr = [train_op, loss, accuracy]\n", + " # set up feed dict\n", + " feed_dict_tr = {Xs: X_tr, X_len: X_len_tr, ts_in: t_in_tr,\n", + " ts_out: t_out_tr, t_len: t_len_tr, t_mask: t_mask_tr}\n", + " # run the model\n", + " res = tuple(sess.run(fetches=fetches_tr, feed_dict=feed_dict_tr))\n", + " _, batch_cost, batch_acc = res\n", + " costs += [batch_cost]\n", + " samples_processed += BATCH_SIZE\n", + " #if samples_processed % 1000 == 0: print batch_cost, batch_acc\n", + " #validation data\n", + " if samples_processed % val_interval == 0:\n", + " #print \"validating\"\n", + " fetches_val = [accuracy_valid, y_valid, alpha_valid]\n", + " feed_dict_val = {Xs: X_val, X_len: X_len_val, ts_in: t_in_val,\n", + " ts_out: t_out_val, t_len: t_len_val, t_mask: t_mask_val}\n", + " res = tuple(sess.run(fetches=fetches_val, feed_dict=feed_dict_val))\n", + " acc_val, output_val, alp_val = res\n", + " samples_val += [samples_processed]\n", + " accs += [acc_val]\n", + " plt.plot(samples_val, accs, 'b-')\n", + " plt.ylabel('Validation Accuracy', fontsize=15)\n", + " plt.xlabel('Processed samples', fontsize=15)\n", + " plt.title('', fontsize=20)\n", + " plt.grid('on')\n", + " plt.savefig(\"out_attention.png\")\n", + " display.display(display.Image(filename=\"out_attention.png\"))\n", + " display.clear_output(wait=True)\n", + "# NOTICE - THIS MIGHT TAKE UPTO 30 MINUTES ON CPU..!\n", + "except KeyboardInterrupt:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#plot of validation accuracy for each target position\n", + "plt.figure(figsize=(7,7))\n", + "plt.plot(np.mean(np.argmax(output_val,axis=2)==t_out_val,axis=0))\n", + "plt.ylabel('Accuracy', fontsize=15)\n", + "plt.xlabel('Target position', fontsize=15)\n", + "#plt.title('', fontsize=20)\n", + "plt.grid('on')\n", + "plt.show()\n", + "#why do the plot look like this?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "### attention plot, try with different i = 1, 2, ..., 1000\n", + "i = 42\n", + "\n", + "column_labels = map(str, list(t_out_val[i]))\n", + "row_labels = map(str, (list(X_val[i])))\n", + "data = alp_val[i]\n", + "fig, ax = plt.subplots()\n", + "heatmap = ax.pcolor(data, cmap=plt.cm.Blues)\n", + "\n", + "# put the major ticks at the middle of each cell\n", + "ax.set_xticks(np.arange(data.shape[1])+0.5, minor=False)\n", + "ax.set_yticks(np.arange(data.shape[0])+0.5, minor=False)\n", + "\n", + "# want a more natural, table-like display\n", + "ax.invert_yaxis()\n", + "ax.xaxis.tick_top()\n", + "\n", + "ax.set_xticklabels(row_labels, minor=False)\n", + "ax.set_yticklabels(column_labels, minor=False)\n", + "\n", + "plt.ylabel('output', fontsize=15)\n", + "plt.xlabel('Attention plot', fontsize=15)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#Plot of average attention weight as a function of the sequence position for each of \n", + "#the 21 targets in the output sequence i.e. each line is the mean postion of the \n", + "#attention for each target position.\n", + "\n", + "np.mean(alp_val, axis=0).shape\n", + "plt.figure()\n", + "plt.plot(np.mean(alp_val, axis=0).T)\n", + "plt.ylabel('alpha', fontsize=15)\n", + "plt.xlabel('Input Sequence position', fontsize=15)\n", + "plt.title('Alpha weights', fontsize=20)\n", + "plt.legend(map(str,range(1,22)), bbox_to_anchor=(1.125,1.0), fontsize=10)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Assignments for the attention decoder\n", + "1. Explain what the attention plot show.\n", + "2. Explain what the alphaweights show.\n", + "3. Why are the alpha curve for the first digit narrow and peaked while later digits have alpha curves that are wider and less peaked?\n", + "4. Why is attention a good idea for this problem? Can you think of other problems where attention is a good choice?\n", + "5. Try setting MIN_DIGITS and MAX_DIGITS to 20\n", + "6. Enable gradient clipping (under the loss codeblock)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/confusionmatrix.py b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/confusionmatrix.py new file mode 100755 index 0000000000000000000000000000000000000000..29e4ba60d10f434caae706e2bc03b9982623193b --- /dev/null +++ b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/confusionmatrix.py @@ -0,0 +1,133 @@ +import numpy as np + + +class ConfusionMatrix: + """ + Simple confusion matrix class + row is the true class, column is the predicted class + """ + def __init__(self, num_classes, class_names=None): + self.n_classes = num_classes + if class_names is None: + self.class_names = map(str, range(num_classes)) + else: + self.class_names = class_names + + # find max class_name and pad + max_len = max(map(len, self.class_names)) + self.max_len = max_len + for idx, name in enumerate(self.class_names): + if len(self.class_names) < max_len: + self.class_names[idx] = name + " "*(max_len-len(name)) + + self.mat = np.zeros((num_classes,num_classes),dtype='int') + + def __str__(self): + # calucate row and column sums + col_sum = np.sum(self.mat, axis=1) + row_sum = np.sum(self.mat, axis=0) + + s = [] + + mat_str = self.mat.__str__() + mat_str = mat_str.replace('[','').replace(']','').split('\n') + + for idx, row in enumerate(mat_str): + if idx == 0: + pad = " " + else: + pad = "" + class_name = self.class_names[idx] + class_name = " " + class_name + " |" + row_str = class_name + pad + row + row_str += " |" + str(col_sum[idx]) + s.append(row_str) + + row_sum = [(self.max_len+4)*" "+" ".join(map(str, row_sum))] + hline = [(1+self.max_len)*" "+"-"*len(row_sum[0])] + + s = hline + s + hline + row_sum + + # add linebreaks + s_out = [line+'\n' for line in s] + return "".join(s_out) + + def batch_add(self, targets, preds): + assert targets.shape == preds.shape + assert len(targets) == len(preds) + assert max(targets) < self.n_classes + assert max(preds) < self.n_classes + targets = targets.flatten() + preds = preds.flatten() + for i in range(len(targets)): + self.mat[targets[i], preds[i]] += 1 + + def get_errors(self): + tp = np.asarray(np.diag(self.mat).flatten(),dtype='float') + fn = np.asarray(np.sum(self.mat, axis=1).flatten(),dtype='float') - tp + fp = np.asarray(np.sum(self.mat, axis=0).flatten(),dtype='float') - tp + tn = np.asarray(np.sum(self.mat)*np.ones(self.n_classes).flatten(), + dtype='float') - tp - fn - fp + return tp, fn, fp, tn + + def accuracy(self): + """ + Calculates global accuracy + :return: accuracy + :example: >>> conf = ConfusionMatrix(3) + >>> conf.batchAdd([0,0,1],[0,0,2]) + >>> print conf.accuracy() + """ + tp, _, _, _ = self.get_errors() + n_samples = np.sum(self.mat) + return np.sum(tp) / n_samples + + def sensitivity(self): + tp, tn, fp, fn = self.get_errors() + res = tp / (tp + fn) + res = res[~np.isnan(res)] + return res + + def specificity(self): + tp, tn, fp, fn = self.get_errors() + res = tn / (tn + fp) + res = res[~np.isnan(res)] + return res + + def positive_predictive_value(self): + tp, tn, fp, fn = self.get_errors() + res = tp / (tp + fp) + res = res[~np.isnan(res)] + return res + + def negative_predictive_value(self): + tp, tn, fp, fn = self.get_errors() + res = tn / (tn + fn) + res = res[~np.isnan(res)] + return res + + def false_positive_rate(self): + tp, tn, fp, fn = self.get_errors() + res = fp / (fp + tn) + res = res[~np.isnan(res)] + return res + + def false_discovery_rate(self): + tp, tn, fp, fn = self.get_errors() + res = fp / (tp + fp) + res = res[~np.isnan(res)] + return res + + def F1(self): + tp, tn, fp, fn = self.get_errors() + res = (2*tp) / (2*tp + fp + fn) + res = res[~np.isnan(res)] + return res + + def matthews_correlation(self): + tp, tn, fp, fn = self.get_errors() + numerator = tp*tn - fp*fn + denominator = np.sqrt((tp + fp)*(tp + fn)*(tn + fp)*(tn + fn)) + res = numerator / denominator + res = res[~np.isnan(res)] + return res diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/data_generator.py b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/data_generator.py new file mode 100755 index 0000000000000000000000000000000000000000..4f58d3c126a62d78deba489346583fa581c4ce29 --- /dev/null +++ b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/data_generator.py @@ -0,0 +1,122 @@ +from __future__ import print_function +import numpy as np + +target_to_text = { + '0':'noll', + '1':'ett', + '2':'tva', + '3':'tre', + '4':'fyra', + '5':'fem', + '6':'sex', + '7':'sju', + '8':'atta', + '9':'nio', +} + +stop_character = start_character = '#' + +input_characters = " ".join(target_to_text.values()) +valid_characters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#'] + \ + list(set(input_characters)) + +def print_valid_characters(): + l = '' + for i,c in enumerate(valid_characters): + l += "\'%s\'=%i,\t" % (c,i) + print("Number of valid characters:", len(valid_characters)) + print(l) + +ninput_chars = len(valid_characters) +def get_batch(batch_size=100, min_digits = 3, max_digits=3): + ''' + Generates random sequences of integers and translates them to text i.e. 1->'one'. + :param batch_size: number of samples to return + :param min_digits: minimum length of target + :param max_digits: maximum length of target + ''' + text_inputs = [] + int_inputs = [] + text_targets_in = [] + text_targets_out = [] + int_targets_in = [] + int_targets_out = [] + for i in range(batch_size): + #convert integer into a list of digits + tar_len = np.random.randint(min_digits,max_digits+1) + text_target = inp_str = "".join(map(str,np.random.randint(0,10,tar_len))) + text_target_in = start_character + text_target + text_target_out = text_target + stop_character + + #generate the targets as a list of intergers + int_target_in = map(lambda c: valid_characters.index(c), text_target_in) + int_target_out = map(lambda c: valid_characters.index(c), text_target_out) + + #generate the text input + text_input = " ".join(map(lambda k: target_to_text[k], inp_str)) + #generate the inputs as a list of intergers + int_input = map(lambda c: valid_characters.index(c), text_input) + + text_inputs.append(text_input) + int_inputs.append(int_input) + text_targets_in.append(text_target_in) + text_targets_out.append(text_target_out) + int_targets_in.append(int_target_in) + int_targets_out.append(int_target_out) + + #create the input matrix, mask and seq_len - note that we zero pad the shorter sequences. + max_input_len = max(map(len, int_inputs)) + inputs = np.zeros((batch_size, max_input_len)) +# input_masks = np.zeros((batch_size,max_input_len)) + for (i,inp) in enumerate(int_inputs): + cur_len = len(inp) + inputs[i,:cur_len] = inp +# input_masks[i,:cur_len] = 1 + inputs_seqlen = np.asarray(map(len, int_inputs)) + + max_target_in_len = max(map(len, int_targets_in)) + targets_in = np.zeros((batch_size, max_target_in_len)) + targets_mask = np.zeros((batch_size, max_target_in_len)) + for (i, tar) in enumerate(int_targets_in): + cur_len = len(tar) + targets_in[i, :cur_len] = tar + targets_seqlen = np.asarray(map(len, int_targets_in)) + + max_target_out_len = max(map(len, int_targets_out)) + targets_out = np.zeros((batch_size, max_target_in_len)) + for (i,tar) in enumerate(int_targets_out): + cur_len = len(tar) + targets_out[i,:cur_len] = tar + targets_mask[i,:cur_len] = 1 + + return inputs.astype('int32'), \ + inputs_seqlen.astype('int32'), \ + targets_in.astype('int32'), \ + targets_out.astype('int32'), \ + targets_seqlen.astype('int32'), \ + targets_mask.astype('float32'), \ + text_inputs, \ + text_targets_in, \ + text_targets_out + +if __name__ == '__main__': + batch_size = 3 + inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \ + text_inputs, text_targets_in, text_targets_out = \ + get_batch(batch_size=batch_size, max_digits=2, min_digits=1) + + print("input types:", inputs.dtype, inputs_seqlen.dtype, targets_in.dtype, targets_out.dtype, targets_seqlen.dtype) + print(print_valid_characters()) + print("Stop/start character = #") + + for i in range(batch_size): + print("\nSAMPLE",i) + print("TEXT INPUTS:\t\t\t", text_inputs[i]) + print("TEXT TARGETS INPUT:\t\t", text_targets_in[i]) + print("TEXT TARGETS OUTPUT:\t\t", text_targets_out[i]) + print("ENCODED INPUTS:\t\t\t", inputs[i]) + print("INPUTS SEQUENCE LENGTH:\t\t", inputs_seqlen[i]) + print("ENCODED TARGETS INPUT:\t\t", targets_in[i]) + print("ENCODED TARGETS OUTPUT:\t\t", targets_out[i]) + print("TARGETS SEQUENCE LENGTH:\t", targets_seqlen[i]) + print("TARGETS MASK:\t\t\t", targets_mask[i]) diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/data_generator.pyc b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/data_generator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a85a95f503aeeade4da43578ed799b2f5855387c Binary files /dev/null and b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/data_generator.pyc differ diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/main.ipynb b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/main.ipynb new file mode 100755 index 0000000000000000000000000000000000000000..3c5587338b33112a48c7f3e9ee4a3d062ad3e338 --- /dev/null +++ b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/main.ipynb @@ -0,0 +1,4177 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline \n", + "%matplotlib nbagg\n", + "import tensorflow as tf\n", + "import matplotlib\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from IPython import display\n", + "from data_generator import get_batch, print_valid_characters\n", + "from tensorflow.python.framework.ops import reset_default_graph\n", + "\n", + "import tf_utils" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Recurrent Neural Networks\n", + "\n", + "Recurrent neural networks are the natural type of neural network to use for sequential data i.e. time series analysis, translation, speech recognition, biological sequence analysis etc. Recurrent neural networks works by recursively applying the same operation at each time step of the data sequence and having layers that pass information from previous time step to the current. It can therefore naturally handle input of varying length. Recurrent networks can be used for several prediction tasks including: sequence-to-class, sequence tagging, and sequence-to-sequence predictions.\n", + "\n", + "In this exercise we'll implement a Encoder-Decoder RNN based on the GRU unit for a simple sequence to sequence translation task. This type of models have shown impressive performance in Neural Machine Translation and Image Caption generation. \n", + "\n", + "For more in depth background material on RNNs please see [Supervised Sequence Labelling with Recurrent\n", + "Neural Networks](https://www.cs.toronto.edu/~graves/preprint.pdf) by Alex Graves\n", + "\n", + "We know that LSTMs and GRUs are difficult to understand. A very good non-mathematical introduction is [Chris Olahs blog](http://colah.github.io/posts/2015-08-Understanding-LSTMs/). (All the posts are nice and cover various topics within machine-learning)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Encoder-Decoder\n", + "In the encoder-decoder structure one RNN (blue) encodes the input and a second RNN (red) calculates the target values. One essential step is to let the encoder and decoder communicate. In the simplest approach you use the last hidden state of the encoder to initialize the decoder. Other approaches lets the decoder attend to different parts of the encoded input at different timesteps in the decoding process. \n", + "\n", + "<img src=\"files/enc-dec.png\", width=400>\n", + "\n", + "In our implementation we use a RNN with gated recurrent units (GRU) as encoder. We then use the last hidden state of the encoder ($h^{enc}_T$) as input to the decoder which is also a GRU RNN. \n", + "\n", + "### RNNs in TensorFlow\n", + "TensorFlow has implementations of LSTM and GRU units. Both implementations assume that the input from the tensor below has the shape **(batch_size, seq_len, num_features)**, unless you have `time\\_major=True`. In this excercise we will use the GRU unit since it only stores a single hidden value per neuron (LSTMs stores two) and is approximately twice as fast as the LSTM unit.\n", + "\n", + "As stated above we will implement a Encoder-Decoder model. The simplest way to do this is to encode the input sequence using the Encoder model. We will then use the last hidden state of the Encoder $h^{enc}_T$ as input to the decoder model which then uses this information (simply a fixed length vector of numbers) to produce the targets. There is (at least) two ways to input $h^{enc}_T$ into the decoder\n", + "\n", + "1. Repeatly use $h^{enc}_T$ as input to the Decoder at each decode time step, as well as the previously computed word\n", + "2. Intialize the decoder using $h^{enc}_T$ and run the decoder without any inputs\n", + "\n", + "In this exercise we will follow the second approach because it's easier to implement. To do this need to create a tensorflow layer that takes $h^{enc}_T$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The Data\n", + "Since RNN models can be very slow to train on real large datasets we will generate some simpler training data for this exercise. The task for the RNN is simply to translate a string of letters spelling the numbers between 0-9 into the corresponding numbers i.e\n", + "\n", + "\"one two five\" --> \"125#\" (we use # as a special end-of-sequence character)\n", + "\n", + "To input the strings into the RNN model we translate the characters into a vector integers using a simple translation table (i.e. 'h'->16, 'o'-> 17 etc). The code below prints a few input/output pairs using the *get_batch* function which randomy produces the data.\n", + "\n", + "Do note; that as showed in the illustration above for input to the decoder the end-of-sequence tag is flipped, and used in the beginning instead of the end. This tag is known as start-of-sequence, but often the end-of-sequence tag is just reused for this purpose.\n", + "\n", + "In the data loader below you will see two targets, target input and target output. Where the input will be used to compute the translation and output used for the loss function." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "input types: int32 int32 int32 int32 int32\n", + "Number of valid characters: 27\n", + "'0'=0,\t'1'=1,\t'2'=2,\t'3'=3,\t'4'=4,\t'5'=5,\t'6'=6,\t'7'=7,\t'8'=8,\t'9'=9,\t'#'=10,\t' '=11,\t'e'=12,\t'g'=13,\t'f'=14,\t'i'=15,\t'h'=16,\t'o'=17,\t'n'=18,\t's'=19,\t'r'=20,\t'u'=21,\t't'=22,\t'w'=23,\t'v'=24,\t'x'=25,\t'z'=26,\t\n", + "None\n", + "Stop/start character = #\n", + "\n", + "SAMPLE 0\n", + "TEXT INPUTS:\t\t\tthree\n", + "TEXT TARGETS INPUT:\t\t#3\n", + "TEXT TARGETS OUTPUT:\t\t3#\n", + "ENCODED INPUTS:\t\t\t[22 16 20 12 12 0 0 0 0 0]\n", + "INPUTS SEQUENCE LENGTH:\t\t5\n", + "ENCODED TARGETS INPUT:\t\t[10 3 0]\n", + "ENCODED TARGETS OUTPUT:\t\t[ 3 10 0]\n", + "TARGETS SEQUENCE LENGTH:\t2\n", + "TARGETS MASK:\t\t\t[ 1. 1. 0.]\n", + "\n", + "SAMPLE 1\n", + "TEXT INPUTS:\t\t\tnine zero\n", + "TEXT TARGETS INPUT:\t\t#90\n", + "TEXT TARGETS OUTPUT:\t\t90#\n", + "ENCODED INPUTS:\t\t\t[18 15 18 12 11 26 12 20 17 0]\n", + "INPUTS SEQUENCE LENGTH:\t\t9\n", + "ENCODED TARGETS INPUT:\t\t[10 9 0]\n", + "ENCODED TARGETS OUTPUT:\t\t[ 9 0 10]\n", + "TARGETS SEQUENCE LENGTH:\t3\n", + "TARGETS MASK:\t\t\t[ 1. 1. 1.]\n", + "\n", + "SAMPLE 2\n", + "TEXT INPUTS:\t\t\tfour seven\n", + "TEXT TARGETS INPUT:\t\t#47\n", + "TEXT TARGETS OUTPUT:\t\t47#\n", + "ENCODED INPUTS:\t\t\t[14 17 21 20 11 19 12 24 12 18]\n", + "INPUTS SEQUENCE LENGTH:\t\t10\n", + "ENCODED TARGETS INPUT:\t\t[10 4 7]\n", + "ENCODED TARGETS OUTPUT:\t\t[ 4 7 10]\n", + "TARGETS SEQUENCE LENGTH:\t3\n", + "TARGETS MASK:\t\t\t[ 1. 1. 1.]\n" + ] + } + ], + "source": [ + "batch_size = 3\n", + "inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \\\n", + "text_inputs, text_targets_in, text_targets_out = \\\n", + " get_batch(batch_size=batch_size, max_digits=2, min_digits=1)\n", + "\n", + "print \"input types:\", inputs.dtype, inputs_seqlen.dtype, targets_in.dtype, targets_out.dtype, targets_seqlen.dtype\n", + "print print_valid_characters()\n", + "print \"Stop/start character = #\"\n", + "\n", + "for i in range(batch_size):\n", + " print \"\\nSAMPLE\",i\n", + " print \"TEXT INPUTS:\\t\\t\\t\", text_inputs[i]\n", + " print \"TEXT TARGETS INPUT:\\t\\t\", text_targets_in[i]\n", + " print \"TEXT TARGETS OUTPUT:\\t\\t\", text_targets_out[i]\n", + " print \"ENCODED INPUTS:\\t\\t\\t\", inputs[i]\n", + " print \"INPUTS SEQUENCE LENGTH:\\t\\t\", inputs_seqlen[i]\n", + " print \"ENCODED TARGETS INPUT:\\t\\t\", targets_in[i]\n", + " print \"ENCODED TARGETS OUTPUT:\\t\\t\", targets_out[i]\n", + " print \"TARGETS SEQUENCE LENGTH:\\t\", targets_seqlen[i]\n", + " print \"TARGETS MASK:\\t\\t\\t\", targets_mask[i]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Encoder Decoder model setup\n", + "Below is the TensorFlow model definition. We use an embedding layer to go from integer representation to vector representation of the input.\n", + "\n", + "Note that we have made use of a custom decoder wrapper which can be found in `rnn.py`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# resetting the graph\n", + "reset_default_graph()\n", + "\n", + "# Setting up hyperparameters and general configs\n", + "MAX_DIGITS = 5\n", + "MIN_DIGITS = 5\n", + "NUM_INPUTS = 27\n", + "NUM_OUTPUTS = 11 #(0-9 + '#')\n", + "\n", + "BATCH_SIZE = 100\n", + "# try various learning rates 1e-2 to 1e-5\n", + "LEARNING_RATE = 0.005\n", + "X_EMBEDDINGS = 8\n", + "t_EMBEDDINGS = 8\n", + "NUM_UNITS_ENC = 10\n", + "NUM_UNITS_DEC = 10\n", + "\n", + "\n", + "# Setting up placeholders, these are the tensors that we \"feed\" to our network\n", + "Xs = tf.placeholder(tf.int32, shape=[None, None], name='X_input')\n", + "ts_in = tf.placeholder(tf.int32, shape=[None, None], name='t_input_in')\n", + "ts_out = tf.placeholder(tf.int32, shape=[None, None], name='t_input_out')\n", + "X_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_mask = tf.placeholder(tf.float32, shape=[None, None], name='t_mask')\n", + "\n", + "# Building the model\n", + "\n", + "# first we build the embeddings to make our characters into dense, trainable vectors\n", + "X_embeddings = tf.get_variable('X_embeddings', [NUM_INPUTS, X_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "t_embeddings = tf.get_variable('t_embeddings', [NUM_OUTPUTS, t_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "\n", + "# setting up weights for computing the final output\n", + "W_out = tf.get_variable('W_out', [NUM_UNITS_DEC, NUM_OUTPUTS])\n", + "b_out = tf.get_variable('b_out', [NUM_OUTPUTS])\n", + "\n", + "X_embedded = tf.gather(X_embeddings, Xs, name='embed_X')\n", + "t_embedded = tf.gather(t_embeddings, ts_in, name='embed_t')\n", + "\n", + "# forward encoding\n", + "enc_cell = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)#python.ops.rnn_cell.GRUCell\n", + "_, enc_state = tf.nn.dynamic_rnn(cell=enc_cell, inputs=X_embedded,\n", + " sequence_length=X_len, dtype=tf.float32)\n", + "# use below incase TF's makes issues\n", + "#enc_state, _ = tf_utils.encoder(X_embedded, X_len, 'encoder', NUM_UNITS_ENC)\n", + "#\n", + "#enc_state = tf.concat(1, [enc_state, enc_state])\n", + "\n", + "# decoding\n", + "# note that we are using a wrapper for decoding here, this wrapper is hardcoded to only use GRU\n", + "# check out tf_utils to see how you make your own decoder\n", + "dec_out, valid_dec_out = tf_utils.decoder(enc_state, t_embedded, t_len, \n", + " NUM_UNITS_DEC, t_embeddings,\n", + " W_out, b_out)\n", + "\n", + "# reshaping to have [batch_size*seqlen, num_units]\n", + "out_tensor = tf.reshape(dec_out, [-1, NUM_UNITS_DEC])\n", + "valid_out_tensor = tf.reshape(valid_dec_out, [-1, NUM_UNITS_DEC])\n", + "# computing output\n", + "out_tensor = tf.matmul(out_tensor, W_out) + b_out\n", + "valid_out_tensor = tf.matmul(valid_out_tensor, W_out) + b_out\n", + "# reshaping back to sequence\n", + "b_size = tf.shape(X_len)[0] # use a variable we know has batch_size in [0]\n", + "seq_len = tf.shape(t_embedded)[1] # variable we know has sequence length in [1]\n", + "num_out = tf.constant(NUM_OUTPUTS) # casting NUM_OUTPUTS to a tensor variable\n", + "out_shape = tf.concat(0, [tf.expand_dims(b_size, 0),\n", + " tf.expand_dims(seq_len, 0),\n", + " tf.expand_dims(num_out, 0)])\n", + "out_tensor = tf.reshape(out_tensor, out_shape)\n", + "valid_out_tensor = tf.reshape(valid_out_tensor, out_shape)\n", + "# handling shape loss\n", + "#out_tensor.set_shape([None, None, NUM_OUTPUTS])\n", + "y = out_tensor\n", + "y_valid = valid_out_tensor" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_embeddings:0 (27, 8)\n", + "t_embeddings:0 (11, 8)\n", + "W_out:0 (10, 11)\n", + "b_out:0 (11,)\n", + "RNN/GRUCell/Gates/Linear/Matrix:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Bias:0 (20,)\n", + "RNN/GRUCell/Candidate/Linear/Matrix:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Bias:0 (10,)\n", + "decoder/W_z_x:0 (8, 10)\n", + "decoder/W_z_h:0 (10, 10)\n", + "decoder/b_z:0 (10,)\n", + "decoder/W_r_x:0 (8, 10)\n", + "decoder/W_r_h:0 (10, 10)\n", + "decoder/b_r:0 (10,)\n", + "decoder/W_c_x:0 (8, 10)\n", + "decoder/W_c_h:0 (10, 10)\n", + "decoder/b_h:0 (10,)\n" + ] + } + ], + "source": [ + "# print all the variable names and shapes\n", + "for var in tf.all_variables():\n", + " s = var.name + \" \"*(40-len(var.name))\n", + " print s, var.value().get_shape()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Defining the cost function, gradient clipping and accuracy\n", + "Because the targets are categorical we use the cross entropy error.\n", + "As the data is sequential we use the sequence to sequence cross entropy supplied in `tf_utils.py`.\n", + "We use the Adam optimizer but you can experiment with the different optimizers implemented in [TensorFlow](https://www.tensorflow.org/versions/r0.10/api_docs/python/train.html#optimizers)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def loss_and_acc(preds):\n", + " # sequence_loss_tensor is a modification of TensorFlow's own sequence_to_sequence_loss\n", + " # TensorFlow's seq2seq loss works with a 2D list instead of a 3D tensors\n", + " loss = tf_utils.sequence_loss_tensor(preds, ts_out, t_mask, NUM_OUTPUTS) # notice that we use ts_out here!\n", + " # if you want regularization\n", + " #reg_scale = 0.00001\n", + " #regularize = tf.contrib.layers.l2_regularizer(reg_scale)\n", + " #params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)\n", + " #reg_term = sum([regularize(param) for param in params])\n", + " #loss += reg_term\n", + " # calculate accuracy\n", + " argmax = tf.to_int32(tf.argmax(preds, 2))\n", + " correct = tf.to_float(tf.equal(argmax, ts_out)) * t_mask\n", + " accuracy = tf.reduce_sum(correct) / tf.reduce_sum(t_mask)\n", + " return loss, accuracy, argmax\n", + "\n", + "loss, accuracy, predictions = loss_and_acc(y)\n", + "loss_valid, accuracy_valid, predictions_valid = loss_and_acc(y_valid)\n", + "\n", + "# use lobal step to keep track of our iterations\n", + "global_step = tf.Variable(0, name='global_step', trainable=False)\n", + "# pick optimizer, try momentum or adadelta\n", + "optimizer = tf.train.AdamOptimizer(LEARNING_RATE)\n", + "# extract gradients for each variable\n", + "grads_and_vars = optimizer.compute_gradients(loss)\n", + "# add below for clipping by norm\n", + "#gradients, variables = zip(*grads_and_vars) # unzip list of tuples\n", + "#clipped_gradients, global_norm = (\n", + "# tf.clip_by_global_norm(gradients, self.clip_norm) )\n", + "#grads_and_vars = zip(clipped_gradients, variables)\n", + "# apply gradients and make trainable function\n", + "train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_embeddings:0 (27, 8)\n", + "t_embeddings:0 (11, 8)\n", + "W_out:0 (10, 11)\n", + "b_out:0 (11,)\n", + "RNN/GRUCell/Gates/Linear/Matrix:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Bias:0 (20,)\n", + "RNN/GRUCell/Candidate/Linear/Matrix:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Bias:0 (10,)\n", + "decoder/W_z_x:0 (8, 10)\n", + "decoder/W_z_h:0 (10, 10)\n", + "decoder/b_z:0 (10,)\n", + "decoder/W_r_x:0 (8, 10)\n", + "decoder/W_r_h:0 (10, 10)\n", + "decoder/b_r:0 (10,)\n", + "decoder/W_c_x:0 (8, 10)\n", + "decoder/W_c_h:0 (10, 10)\n", + "decoder/b_h:0 (10,)\n", + "global_step:0 ()\n", + "beta1_power:0 ()\n", + "beta2_power:0 ()\n", + "X_embeddings/Adam:0 (27, 8)\n", + "X_embeddings/Adam_1:0 (27, 8)\n", + "t_embeddings/Adam:0 (11, 8)\n", + "t_embeddings/Adam_1:0 (11, 8)\n", + "W_out/Adam:0 (10, 11)\n", + "W_out/Adam_1:0 (10, 11)\n", + "b_out/Adam:0 (11,)\n", + "b_out/Adam_1:0 (11,)\n", + "RNN/GRUCell/Gates/Linear/Matrix/Adam:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Matrix/Adam_1:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Bias/Adam:0 (20,)\n", + "RNN/GRUCell/Gates/Linear/Bias/Adam_1:0 (20,)\n", + "RNN/GRUCell/Candidate/Linear/Matrix/Adam:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Matrix/Adam_1:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Bias/Adam:0 (10,)\n", + "RNN/GRUCell/Candidate/Linear/Bias/Adam_1:0 (10,)\n", + "decoder/W_z_x/Adam:0 (8, 10)\n", + "decoder/W_z_x/Adam_1:0 (8, 10)\n", + "decoder/W_z_h/Adam:0 (10, 10)\n", + "decoder/W_z_h/Adam_1:0 (10, 10)\n", + "decoder/b_z/Adam:0 (10,)\n", + "decoder/b_z/Adam_1:0 (10,)\n", + "decoder/W_r_x/Adam:0 (8, 10)\n", + "decoder/W_r_x/Adam_1:0 (8, 10)\n", + "decoder/W_r_h/Adam:0 (10, 10)\n", + "decoder/W_r_h/Adam_1:0 (10, 10)\n", + "decoder/b_r/Adam:0 (10,)\n", + "decoder/b_r/Adam_1:0 (10,)\n", + "decoder/W_c_x/Adam:0 (8, 10)\n", + "decoder/W_c_x/Adam_1:0 (8, 10)\n", + "decoder/W_c_h/Adam:0 (10, 10)\n", + "decoder/W_c_h/Adam_1:0 (10, 10)\n", + "decoder/b_h/Adam:0 (10,)\n", + "decoder/b_h/Adam_1:0 (10,)\n" + ] + } + ], + "source": [ + "# print all the variable names and shapes\n", + "# notice that we now have the optimizer Adam as well!\n", + "for var in tf.all_variables():\n", + " s = var.name + \" \"*(40-len(var.name))\n", + " print s, var.value().get_shape()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "SAMPLE 0\n", + "TEXT INPUTS:\t\t\tfive two zero eight two eight\n", + "TEXT TARGETS INPUT:\t\t#520828\n", + "\n", + "SAMPLE 1\n", + "TEXT INPUTS:\t\t\ttwo two six\n", + "TEXT TARGETS INPUT:\t\t#226\n", + "\n", + "SAMPLE 2\n", + "TEXT INPUTS:\t\t\tnine eight eight zero four one zero\n", + "TEXT TARGETS INPUT:\t\t#9880410\n", + "y (3, 8, 11)\n", + "y_valid (3, 8, 11)\n" + ] + } + ], + "source": [ + "# as always, test the forward pass and initialize the tf.Session!\n", + "# here is some dummy data\n", + "batch_size=3\n", + "inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \\\n", + "text_inputs, text_targets_in, text_targets_out = \\\n", + " get_batch(batch_size=batch_size, max_digits=7, min_digits=2)\n", + "\n", + "for i in range(batch_size):\n", + " print \"\\nSAMPLE\",i\n", + " print \"TEXT INPUTS:\\t\\t\\t\", text_inputs[i]\n", + " print \"TEXT TARGETS INPUT:\\t\\t\", text_targets_in[i]\n", + "\n", + "# restricting memory usage, TensorFlow is greedy and will use all memory otherwise\n", + "gpu_opts = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)\n", + "# initialize the Session\n", + "sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_opts))\n", + "# test train part\n", + "sess.run(tf.initialize_all_variables())\n", + "feed_dict = {Xs: inputs, X_len: inputs_seqlen, ts_in: targets_in,\n", + " ts_out: targets_out, t_len: targets_seqlen}\n", + "fetches = [y]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y\", res[0].shape\n", + "\n", + "# test validation part\n", + "fetches = [y_valid]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y_valid\", res[0].shape" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_val (5000, 29)\n", + "t_out_val (5000, 6)\n" + ] + } + ], + "source": [ + "#Generate some validation data\n", + "X_val, X_len_val, t_in_val, t_out_val, t_len_val, t_mask_val, \\\n", + "text_inputs_val, text_targets_in_val, text_targets_out_val = \\\n", + " get_batch(batch_size=5000, max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + "print \"X_val\", X_val.shape\n", + "print \"t_out_val\", t_out_val.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Training" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3Xl8VNX5x/HPQ4AAggiCgiyKKyhaJLhb64qKNtpqRSsu\nuFtxoQq2Vg1Ql4J1RatVcUXjLq5VFFf055a4CyJgQARUQNawJuf3x02GyTaZTGbmnsx8369XXrm5\nc+65z/TpSJ7cs5hzDhERERERkXRoFnYAIiIiIiKSPVSAiIiIiIhI2qgAERERERGRtFEBIiIiIiIi\naaMCRERERERE0kYFiIiIiIiIpI0KEBERERERSRsVICIiIiIikjYqQEREREREJG1UgIiIiIiISNqo\nABERERERkbRRASIiIiIiImmjAkRERERERNJGBYiIiIiIiKSNChAREREREUkbFSAiIiIiIpI2KkBE\nRERERCRtVICIiIiIiEjaqAAREREREZG0UQEiIiIiIiJpowJERERERETSRgWIiIiIiIikjQoQERER\nERFJGxUgIiIiIiKSNipAREREREQkbVSAiIiIiIhI2qgAERERERGRtFEBIiIiIiIiaaMCRERERERE\n0kYFiIiIiIiIpI0KEBERERERSRsVICIiIiIikjYqQEREREREJG1UgIiIiIiISNqoABERERERkbRR\nASIiIiIiImmjAkRERERERNJGBYiIiIiIiKSNChAREREREUkbFSAiIiIiIpI2KkBERERERCRtVICI\niIiIiEjaqAAREREREZG0UQEiIiIiIiJpowJERERERETSRgWIiIiIiIikjQoQERERERFJGxUgIiIi\nIiKSNipAREREREQkbVSAiIiIiIhI2qgAERERERGRtMnKAsTMcs1srJnNN7NSM/vAzA5twPWHmtkb\nZrbUzJab2SdmdkIt7fY1s6lmtsrMFpjZrWa2SXLfjYiIiIhI05GVBQjwADAceBi4CCgDXjaz/eq7\n0MyGAq8Ca4G/A5cB7wDdq7XrB0wBWlXc617gHODJZL0JEREREZGmxpxzYceQVma2J/ABcJlz7qaK\nc7nAV8DPzrk6ixAz2wb4Bvivc254Pfd5GdgN6O2cW1lx7kzgHuBw59xrjX83IiIiIiJNSzY+ATke\n2ADcXXnCObcWmADsY2bdYlx7HmDA1QBm1tbMrHojM9sUOBSYWFl8VHgIWAnUGK4lIiIiIpINsrEA\n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\nmsqj2EGCZD4AAAAASUVORK5CYII=\n", + "text/plain": [ + "<IPython.core.display.Image object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# setting up running parameters\n", + "val_interval = 5000\n", + "samples_to_process = 3e5\n", + "samples_processed = 0\n", + "samples_val = []\n", + "costs, accs_val = [], []\n", + "plt.figure()\n", + "try:\n", + " while samples_processed < samples_to_process:\n", + " # load data\n", + " X_tr, X_len_tr, t_in_tr, t_out_tr, t_len_tr, t_mask_tr, \\\n", + " text_inputs_tr, text_targets_in_tr, text_targets_out_tr = \\\n", + " get_batch(batch_size=BATCH_SIZE,max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + " # make fetches\n", + " fetches_tr = [train_op, loss, accuracy]\n", + " # set up feed dict\n", + " feed_dict_tr = {Xs: X_tr, X_len: X_len_tr, ts_in: t_in_tr,\n", + " ts_out: t_out_tr, t_len: t_len_tr, t_mask: t_mask_tr}\n", + " # run the model\n", + " res = tuple(sess.run(fetches=fetches_tr, feed_dict=feed_dict_tr))\n", + " _, batch_cost, batch_acc = res\n", + " costs += [batch_cost]\n", + " samples_processed += BATCH_SIZE\n", + " #if samples_processed % 1000 == 0: print batch_cost, batch_acc\n", + " #validation data\n", + " if samples_processed % val_interval == 0:\n", + " #print \"validating\"\n", + " fetches_val = [accuracy_valid, y_valid]\n", + " feed_dict_val = {Xs: X_val, X_len: X_len_val, ts_in: t_in_val,\n", + " ts_out: t_out_val, t_len: t_len_val, t_mask: t_mask_val}\n", + " res = tuple(sess.run(fetches=fetches_val, feed_dict=feed_dict_val))\n", + " acc_val, output_val = res\n", + " samples_val += [samples_processed]\n", + " accs_val += [acc_val]\n", + " plt.plot(samples_val, accs_val, 'g-')\n", + " plt.ylabel('Validation Accuracy', fontsize=15)\n", + " plt.xlabel('Processed samples', fontsize=15)\n", + " plt.title('', fontsize=20)\n", + " plt.grid('on')\n", + " plt.savefig(\"out.png\")\n", + " display.display(display.Image(filename=\"out.png\"))\n", + " display.clear_output(wait=True)\n", + "except KeyboardInterrupt:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'];\n", + " var y0 = fig.canvas.height - msg['y0'];\n", + " var x1 = msg['x1'];\n", + " var y1 = fig.canvas.height - msg['y1'];\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x;\n", + " var y = canvas_pos.y;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", + " fig.send_message('closing', {});\n", + " fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjAAAAIwCAYAAACY8VFvAAAgAElEQVR4XuydB5hV1fW3XwQVjVIsZD4LURIDxihGLKCxgApIgthBJVgRxYKihAyWKBrGCYpBwIgV/qgBYlRsEaJgQ0EFAVGHGGyITkCEIZZRAb9nzbmDl3HKLWefe8pvP48PCey99trv2nPnd8/Ze61GqImACIiACIiACIhAxAg0ipi/clcEREAEREAEREAEkIDRJhABERABERABEYgcAQmYyIVMDouACIiACIiACEjAaA+IgAiIgAiIgAhEjoAETORCJodFQAREQAREQAQkYLQHREAEREAEREAEIkdAAiZyIZPDIiACIiACIiACEjDaAyIgAiIgAiIgApEjIAETuZDJYREQAREQAREQAQkY7QEREAEREAEREIHIEZCAiVzI5LAIiIAIiIAIiIAEjPaACIiACIiACIhA5AhIwEQuZHJYBERABERABERAAkZ7QAREQAREQAREIHIEJGAiFzI5LAIiIAIiIAIiIAGjPSACIiACIiACIhA5AhIwkQuZHBYBERABERABEZCA0R4QAREQAREQARGIHAEJmMiFTA6LgAiIgAiIgAhIwGgPiIAIiIAIiIAIRI6ABEzkQiaHRUAEREAEREAEJGC0B0RABERABERABCJHQAImciGTwyIgAiIgAiIgAhIw2gMiIAIiIAIiIAKRIyABE7mQyWEREAEREAEREAEJGO0BERABERABERCByBGQgIlcyOSwCIiACIiACIiABIz2gAiIgAiIgAiIQOQISMBELmRyWAREQAREQAREQAJGe0AEREAEREAERCByBCRgIhcyOSwCIiACIiACIiABoz0gAiIgAiIgAiIQOQISMJELmRwWAREQAREQARGQgNEeEAEREAEREAERiBwBCZjIhUwOi4AIiIAIiIAISMBoD4iACIiACIiACESOgARM5EImh0VABERABERABCRgtAdEQAREQAREQAQiR0ACJnIhk8MiIAIiIAIiIAISMNoDIiACIiACIiACkSMgARO5kMlhERABERABERABCRjtAREQAREQAREQgcgRkICJXMjksAiIgAiIgAiIgASM9oAIiIAIiIAIiEDkCEjARC5kclgEREAEREAEREACRntABERABERABEQgcgQkYCIXMjksAiIgAiIgAiIgAaM9IAIiIAIi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ffZyjjhiwuhnd9yxhmnTpjBv3tzRn596apirrurs+0/RrYkTV3DzzQs488w970fz39/if39131977bU8+uijo8+TK69UlsToARf9Ul+79oyKr+h+4PJUDoxKID0UcmCub2NtrwA2AkdmTiI5B6YNeO5iAiZQTQK7d0twlBcdkj015fp0EgnSltull8Ljj/vt4GXfSY7AlE20fHuKsugUkmpnSsxcnDqFtCMz3IuBU4BbgSeBE4HPAc8E9oRHftYsYMr31V6Liialo1QRh2qcabON43JzHZurtsp09LydbbAkZyidK7Rr1wC33fazqEMcDzbPqgVMNXyuOjDKd5HouDMk6KoOzPGhHsw84JtBsChNTKeOFKl5LNSBuQR4xALm4DlbW23JVtjBG7UZI5ltHD+baxyusmq2cdhawMThWgWrjsBUwUueowmYgAmYQC4BC5jm3hgWMM31vVduAiZgApUnYAFTeRcWXoAFTGF0rS9Upnxycqt1b/fohIDZdkKr/b7m2j6rTnuabafE2utvAdMepzr2soCJ6FUdP0+Of0ccppGmzTaO2801DldZNds4bC1g4nCtglULmCp4yXM0ARMwARPIJWAB09wbwwKmub73yk3ABEyg8gQsYCrvwsILsIApjK71haoanFQzbt3bPTohYLad0Gq/r7m2z6rTnmbbKbH2+lvAtMepjr0sYCJ6dcmSJaxcqZeIu5VNwGzLJrrHnrnG4Wq28bhawMRj2+uWLWAiesi/ccWDa7Zx2JprHK6yarZx2FrAxOFaBasWMFXwkudoAiZgAiaQS8ACprk3hgVMc33vlZuACZhA5QlYwFTehYUXYAFTGF3rC133oTWjoj3Mtii5A19nrnG4yqrZxmFrAROHaxWsWsBE9JIrb8aDa7Zx2JprHK6yarZx2FrAxOFaBasWMFXwkudoAiZgAiaQS8ACprk3hgVMc33vlZuACZhA5QlYwFTehYUXYAFTGF3rC4eHh+nr62vd0T06JmC2HSNr6wJzbQtToU5mWwhby4ssYFoiqm0HC5iIrh0YGGBwcDDiCM01bbZxfG+ucbjKqtnGYWsBE4drFaxawFTBS56jCZiACZhALgELmObeGBYwzfW9V24CJmAClSdgAVN5FxZegAVMYXS+0ARMwARMYLwJWMCMtwfGb3wLmIjsV69ezaJFiyKO0FzTZhvH9+Ya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\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#plot of validation accuracy for each target position\n", + "plt.figure(figsize=(7,7))\n", + "plt.plot(np.mean(np.argmax(output_val,axis=2)==t_out_val,axis=0))\n", + "plt.ylabel('Accuracy', fontsize=15)\n", + "plt.xlabel('Target position', fontsize=15)\n", + "#plt.title('', fontsize=20)\n", + "plt.grid('on')\n", + "plt.show()\n", + "#why do the plot look like this?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exercises:\n", + "\n", + "1. The model has two GRU networks. The ```GRUEncoder``` and the ```GRUDecoder```.\n", + "A GRU is parameterized by a update gate `z`, reset gate `r` and the cell `c`.\n", + "Under normal circumstances, such as in the TensorFlow GRUCell implementation, these gates have been stacked for faster computation, but in the custom decoder each weight and bias are as described in the original [article for GRU](https://arxiv.org/abs/1406.1078).\n", + "Thus we have the following weights and bias; ```{decoder/W_z_x:0, decoder/W_z_h:0, b_updategate, decoder/b_z:0, decoder/W_r_x:0, decoder/W_r_h:0, decoder/b_r:0, decoder/W_c_x:0, decoder/W_c_h:0, decoder/b_h:0}```.\n", + "Try to explain the shape of ```decoder/W_z_x:0``` and ```decoder/W_z_h:0```. Why are they different? You can find the equations for the gru at: [GRU](http://lasagne.readthedocs.io/en/latest/modules/layers/recurrent.html#lasagne.layers.GRULayer). \n", + "\n", + "2. The GRUunit is able to ignore the input and just copy the previous hidden state. In the begining of training this might be desireable behaviour because it helps the model learn long range dependencies. You can make the model ignore the input by modifying initial bias values. What bias would you modify and how would you modify it? Again you'll need to refer to the GRU equations: [GRU](http://lasagne.readthedocs.io/en/latest/modules/layers/recurrent.html#lasagne.layers.GRULayer)\n", + "Further, if you look into `tf_utils.py` and search for the `decoder(...)` function, you will see that the init for each weight and bias can be changed.\n", + "\n", + "3. Try setting MIN_DIGITS and MAX_DIGITS to 20\n", + "\n", + "4. What is the final validation performance? Why do you think it is not better? Comment on the accuracy for each position in of the output symbols?\n", + "\n", + "5. Why do you think the validation performance looks more \"jig-saw\" like compared to FFN and CNN models?\n", + "\n", + "6. In the example we stack a softmax layer on top of a Recurrent layer. In the code snippet below explain how we can do that?" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "l_input_ (16, 140, 40)\n", + "l_gru_ (16, 140, 10)\n", + "l_reshape_ (2240, 10)\n", + "l_softmax_ (2240, 11)\n", + "l_softmax_seq_ (16, 140, 11)\n" + ] + } + ], + "source": [ + "reset_default_graph()\n", + "\n", + "bs_, seqlen_, numinputs_ = 16, 140, 40\n", + "x_pl_ = tf.placeholder(tf.float32, [bs_, seqlen_, numinputs_])\n", + "gru_cell_ = tf.nn.rnn_cell.GRUCell(10)\n", + "l_gru_, gru_state_ = tf.nn.dynamic_rnn(gru_cell_, x_pl_, dtype=tf.float32)\n", + "l_reshape_ = tf.reshape(l_gru_, [-1, 10])\n", + "\n", + "l_softmax_ = tf.contrib.layers.fully_connected(l_reshape_, 11, activation_fn=tf.nn.softmax)\n", + "l_softmax_seq_ = tf.reshape(l_softmax_, [bs_, seqlen_, -1])\n", + "\n", + "print \"l_input_\", x_pl_.get_shape()\n", + "print \"l_gru_\", l_gru_.get_shape()\n", + "print \"l_reshape_\", l_reshape_.get_shape()\n", + "print \"l_softmax_\", l_softmax_.get_shape()\n", + "print \"l_softmax_seq_\", l_softmax_seq_.get_shape()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "6. Optional: You are interested in doing sentiment analysis on tweets, i.e classification as positive or negative. You decide read over the twitter seqeuence and use the last hidden state to do the classification. How can you modify the small network above to only outa single classification for network? Hints: look at the gru\\_state\\_ or the [tf.slice](https://www.tensorflow.org/versions/r0.10/api_docs/python/array_ops.html#slice) in the API.\n", + "\n", + "\n", + "7. Optional: Bidirectional Encoder, Bidirectional Encoders are usually implemented by running a forward model and a backward model (a forward model on a reversed sequence) separately and the concatenating them before parsing them on to the next layer. To reverse the sequence try looking at [tf.reverse_sequence](https://www.tensorflow.org/versions/r0.10/api_docs/python/array_ops.html#reverse_sequence)\n", + "\n", + "```\n", + "enc_cell = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)\n", + "_, enc_state = tf.nn.dynamic_rnn(cell=enc_cell, inputs=X_embedded,\n", + " sequence_length=X_len, dtype=tf.float32, scope=\"rnn_forward\")\n", + "\n", + "X_embedded_backwards = tf.reverse_sequence(X_embedded, tf.to_int64(X_len), 1)\n", + "enc_cell_backwards = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)\n", + "_, enc_state_backwards = tf.nn.dynamic_rnn(cell=enc_cell_backwards, inputs=X_embedded_backwards,\n", + " sequence_length=X_len, dtype=tf.float32, scope=\"rnn_backward\")\n", + "\n", + "enc_state = tf.concat(1, [enc_state, enc_state_backwards])\n", + "```\n", + "\n", + "Note: you will need to double the NUM_UNITS_DEC, as it currently does not support different sizes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Attention Decoder (LSTM)\n", + "Soft attention for recurrent neural networks have recently attracted a lot of interest.\n", + "These methods let the Decoder model selective focus on which part of the encoder sequence it will use for each decoded output symbol.\n", + "This relieves the encoder from having to compress the input sequence into a fixed size vector representation passed on to the decoder.\n", + "Secondly we can interrogate the decoder network about where it attends while producing the ouputs.\n", + "below we'll implement an LSTM-decoder with selective attention and show that it significantly improves the performance of the toy translation task.\n", + "\n", + "The siminal attention paper is https://arxiv.org/pdf/1409.0473v7.pdf\n", + "\n", + "The principle of attention models is simple. \n", + "\n", + "1. Use the encoder to get the hidden represention $\\{h^1_e, ...h^n_e\\}$ for each position in the input sequence. \n", + "2. for timestep $t$ in the decoder do for $m = 1...n$ : $a_m = f(h^m_e, h^d_t)$. Where f is a function returning a scalar value. \n", + "3. You can then normalize the sequence of scalars $\\{a_1, ... a_n\\}$ to get probablities $\\{p_1, ... p_n\\}$.\n", + "4. Weight each $h^e_t$ by its probablity $p_t$ and sum to get $h_{in}$.\n", + "5. Use $h_{in}$ as an additional input to the decoder. $h_{in}$ is recalculated each time the decoder is updated." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# resetting the graph\n", + "reset_default_graph()\n", + "\n", + "# Setting up hyperparameters and general configs\n", + "MAX_DIGITS = 20\n", + "MIN_DIGITS = 20\n", + "NUM_INPUTS = 27\n", + "NUM_OUTPUTS = 11 #(0-9 + '#')\n", + "\n", + "BATCH_SIZE = 100\n", + "# try various learning rates 1e-2 to 1e-5\n", + "LEARNING_RATE = 0.005\n", + "X_EMBEDDINGS = 8\n", + "t_EMBEDDINGS = 8\n", + "NUM_UNITS_ENC = 10\n", + "NUM_UNITS_DEC = 10\n", + "NUM_UNITS_ATTN = 20\n", + "\n", + "\n", + "# Setting up placeholders, these are the tensors that we \"feed\" to our network\n", + "Xs = tf.placeholder(tf.int32, shape=[None, None], name='X_input')\n", + "ts_in = tf.placeholder(tf.int32, shape=[None, None], name='t_input_in')\n", + "ts_out = tf.placeholder(tf.int32, shape=[None, None], name='t_input_out')\n", + "X_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_len = tf.placeholder(tf.int32, shape=[None], name='X_len')\n", + "t_mask = tf.placeholder(tf.float32, shape=[None, None], name='t_mask')\n", + "\n", + "# Building the model\n", + "\n", + "# first we build the embeddings to make our characters into dense, trainable vectors\n", + "X_embeddings = tf.get_variable('X_embeddings', [NUM_INPUTS, X_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "t_embeddings = tf.get_variable('t_embeddings', [NUM_OUTPUTS, t_EMBEDDINGS],\n", + " initializer=tf.random_normal_initializer(stddev=0.1))\n", + "\n", + "# setting up weights for computing the final output\n", + "W_out = tf.get_variable('W_out', [NUM_UNITS_DEC, NUM_OUTPUTS])\n", + "b_out = tf.get_variable('b_out', [NUM_OUTPUTS])\n", + "\n", + "X_embedded = tf.gather(X_embeddings, Xs, name='embed_X')\n", + "t_embedded = tf.gather(t_embeddings, ts_in, name='embed_t')\n", + "\n", + "# forward encoding\n", + "enc_cell = tf.nn.rnn_cell.GRUCell(NUM_UNITS_ENC)#python.ops.rnn_cell.GRUCell\n", + "enc_out, enc_state = tf.nn.dynamic_rnn(cell=enc_cell, inputs=X_embedded,\n", + " sequence_length=X_len, dtype=tf.float32)\n", + "# use below in case TF's does not work as intended\n", + "#enc_state, _ = tf_utils.encoder(X_embedded, X_len, 'encoder', NUM_UNITS_ENC)\n", + "#\n", + "#enc_state = tf.concat(1, [enc_state, enc_state])\n", + "\n", + "# decoding\n", + "# note that we are using a wrapper for decoding here, this wrapper is hardcoded to only use GRU\n", + "# check out tf_utils to see how you make your own decoder\n", + "dec_out, dec_out_valid, alpha_valid = \\\n", + " tf_utils.attention_decoder(enc_out, X_len, enc_state, t_embedded, t_len,\n", + " NUM_UNITS_DEC, NUM_UNITS_ATTN, t_embeddings,\n", + " W_out, b_out)\n", + "\n", + "# reshaping to have [batch_size*seqlen, num_units]\n", + "out_tensor = tf.reshape(dec_out, [-1, NUM_UNITS_DEC])\n", + "out_tensor_valid = tf.reshape(dec_out_valid, [-1, NUM_UNITS_DEC])\n", + "# computing output\n", + "out_tensor = tf.matmul(out_tensor, W_out) + b_out\n", + "out_tensor_valid = tf.matmul(out_tensor_valid, W_out) + b_out\n", + "# reshaping back to sequence\n", + "b_size = tf.shape(X_len)[0] # use a variable we know has batch_size in [0]\n", + "seq_len = tf.shape(t_embedded)[1] # variable we know has sequence length in [1]\n", + "num_out = tf.constant(NUM_OUTPUTS) # casting NUM_OUTPUTS to a tensor variable\n", + "out_shape = tf.concat(0, [tf.expand_dims(b_size, 0),\n", + " tf.expand_dims(seq_len, 0),\n", + " tf.expand_dims(num_out, 0)])\n", + "out_tensor = tf.reshape(out_tensor, out_shape)\n", + "out_tensor_valid = tf.reshape(out_tensor_valid, out_shape)\n", + "# handling shape loss\n", + "#out_tensor.set_shape([None, None, NUM_OUTPUTS])\n", + "y = out_tensor\n", + "y_valid = out_tensor_valid" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def loss_and_acc(preds):\n", + " # sequence_loss_tensor is a modification of TensorFlow's own sequence_to_sequence_loss\n", + " # TensorFlow's seq2seq loss works with a 2D list instead of a 3D tensors\n", + " loss = tf_utils.sequence_loss_tensor(preds, ts_out, t_mask, NUM_OUTPUTS) # notice that we use ts_out here!\n", + " # if you want regularization\n", + " reg_scale = 0.00001\n", + " regularize = tf.contrib.layers.l2_regularizer(reg_scale)\n", + " params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)\n", + " reg_term = sum([regularize(param) for param in params])\n", + " loss += reg_term\n", + " # calculate accuracy\n", + " argmax = tf.to_int32(tf.argmax(preds, 2))\n", + " correct = tf.to_float(tf.equal(argmax, ts_out)) * t_mask\n", + " accuracy = tf.reduce_sum(correct) / tf.reduce_sum(t_mask)\n", + " return loss, accuracy, argmax\n", + "\n", + "loss, accuracy, predictions = loss_and_acc(y)\n", + "loss_valid, accuracy_valid, predictions_valid = loss_and_acc(y_valid)\n", + "\n", + "# use lobal step to keep track of our iterations\n", + "global_step = tf.Variable(0, name='global_step', trainable=False)\n", + "# pick optimizer, try momentum or adadelta\n", + "optimizer = tf.train.AdamOptimizer(LEARNING_RATE)\n", + "# extract gradients for each variable\n", + "grads_and_vars = optimizer.compute_gradients(loss)\n", + "# add below for clipping by norm\n", + "#gradients, variables = zip(*grads_and_vars) # unzip list of tuples\n", + "#clipped_gradients, global_norm = (\n", + "# tf.clip_by_global_norm(gradients, self.clip_norm) )\n", + "#grads_and_vars = zip(clipped_gradients, variables)\n", + "# apply gradients and make trainable function\n", + "train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "SAMPLE 0\n", + "TEXT INPUTS:\t\t\tnine six five six\n", + "TEXT TARGETS INPUT:\t\t#9656\n", + "\n", + "SAMPLE 1\n", + "TEXT INPUTS:\t\t\tone zero five seven\n", + "TEXT TARGETS INPUT:\t\t#1057\n", + "\n", + "SAMPLE 2\n", + "TEXT INPUTS:\t\t\tsix nine seven three\n", + "TEXT TARGETS INPUT:\t\t#6973\n", + "y (3, 5, 11)\n", + "y_valid (3, 5, 11)\n" + ] + } + ], + "source": [ + "# as always, test the forward pass and start the tf.Session!\n", + "# here is some dummy data\n", + "batch_size = 3\n", + "inputs, inputs_seqlen, targets_in, targets_out, targets_seqlen, targets_mask, \\\n", + "text_inputs, text_targets_in, text_targets_out = \\\n", + " get_batch(batch_size=3, max_digits=7, min_digits=2)\n", + "\n", + "for i in range(batch_size):\n", + " print \"\\nSAMPLE\",i\n", + " print \"TEXT INPUTS:\\t\\t\\t\", text_inputs[i]\n", + " print \"TEXT TARGETS INPUT:\\t\\t\", text_targets_in[i]\n", + "\n", + "# restricting memory usage, TensorFlow is greedy and will use all memory otherwise\n", + "gpu_opts = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)\n", + "# initialize the Session\n", + "sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_opts))\n", + "# test train part\n", + "sess.run(tf.initialize_all_variables())\n", + "feed_dict = {Xs: inputs, X_len: inputs_seqlen, ts_in: targets_in,\n", + " ts_out: targets_out, t_len: targets_seqlen}\n", + "fetches = [y]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y\", res[0].shape\n", + "\n", + "# test validation part\n", + "fetches = [y_valid]\n", + "res = sess.run(fetches=fetches, feed_dict=feed_dict)\n", + "print \"y_valid\", res[0].shape" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_embeddings:0 (27, 8)\n", + "t_embeddings:0 (11, 8)\n", + "W_out:0 (10, 11)\n", + "b_out:0 (11,)\n", + "RNN/GRUCell/Gates/Linear/Matrix:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Bias:0 (20,)\n", + "RNN/GRUCell/Candidate/Linear/Matrix:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Bias:0 (10,)\n", + "decoder/W_s:0 (10, 10)\n", + "decoder/b_s:0 (10,)\n", + "decoder/W_z:0 (28, 10)\n", + "decoder/W_r:0 (28, 10)\n", + "decoder/W_c:0 (28, 10)\n", + "decoder/b_z:0 (10,)\n", + "decoder/b_r:0 (10,)\n", + "decoder/b_c:0 (10,)\n", + "decoder/W_a:0 (10, 20)\n", + "decoder/U_a:0 (1, 1, 10, 20)\n", + "decoder/b_a:0 (20,)\n", + "decoder/v_a:0 (20,)\n", + "global_step:0 ()\n", + "beta1_power:0 ()\n", + "beta2_power:0 ()\n", + "X_embeddings/Adam:0 (27, 8)\n", + "X_embeddings/Adam_1:0 (27, 8)\n", + "t_embeddings/Adam:0 (11, 8)\n", + "t_embeddings/Adam_1:0 (11, 8)\n", + "W_out/Adam:0 (10, 11)\n", + "W_out/Adam_1:0 (10, 11)\n", + "b_out/Adam:0 (11,)\n", + "b_out/Adam_1:0 (11,)\n", + "RNN/GRUCell/Gates/Linear/Matrix/Adam:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Matrix/Adam_1:0 (18, 20)\n", + "RNN/GRUCell/Gates/Linear/Bias/Adam:0 (20,)\n", + "RNN/GRUCell/Gates/Linear/Bias/Adam_1:0 (20,)\n", + "RNN/GRUCell/Candidate/Linear/Matrix/Adam:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Matrix/Adam_1:0 (18, 10)\n", + "RNN/GRUCell/Candidate/Linear/Bias/Adam:0 (10,)\n", + "RNN/GRUCell/Candidate/Linear/Bias/Adam_1:0 (10,)\n", + "decoder/W_s/Adam:0 (10, 10)\n", + "decoder/W_s/Adam_1:0 (10, 10)\n", + "decoder/b_s/Adam:0 (10,)\n", + "decoder/b_s/Adam_1:0 (10,)\n", + "decoder/W_z/Adam:0 (28, 10)\n", + "decoder/W_z/Adam_1:0 (28, 10)\n", + "decoder/W_r/Adam:0 (28, 10)\n", + "decoder/W_r/Adam_1:0 (28, 10)\n", + "decoder/W_c/Adam:0 (28, 10)\n", + "decoder/W_c/Adam_1:0 (28, 10)\n", + "decoder/b_z/Adam:0 (10,)\n", + "decoder/b_z/Adam_1:0 (10,)\n", + "decoder/b_r/Adam:0 (10,)\n", + "decoder/b_r/Adam_1:0 (10,)\n", + "decoder/b_c/Adam:0 (10,)\n", + "decoder/b_c/Adam_1:0 (10,)\n", + "decoder/W_a/Adam:0 (10, 20)\n", + "decoder/W_a/Adam_1:0 (10, 20)\n", + "decoder/U_a/Adam:0 (1, 1, 10, 20)\n", + "decoder/U_a/Adam_1:0 (1, 1, 10, 20)\n", + "decoder/b_a/Adam:0 (20,)\n", + "decoder/b_a/Adam_1:0 (20,)\n", + "decoder/v_a/Adam:0 (20,)\n", + "decoder/v_a/Adam_1:0 (20,)\n" + ] + } + ], + "source": [ + "# print all the variable names and shapes\n", + "# notice that W_z is now packed, such that it contains both W_z_h and W_x_h, this is for optimization\n", + "# further, we now have W_s, b_s. This is so NUM_UNITS_ENC and NUM_UNITS_DEC does not have to share shape ..!\n", + "for var in tf.all_variables():\n", + " s = var.name + \" \"*(40-len(var.name))\n", + " print s, var.value().get_shape()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_val (5000, 111)\n", + "t_out_val (5000, 21)\n" + ] + } + ], + "source": [ + "#Generate some validation data\n", + "X_val, X_len_val, t_in_val, t_out_val, t_len_val, t_mask_val, \\\n", + "text_inputs_val, text_targets_in_val, text_targets_out_val = \\\n", + " get_batch(batch_size=5000, max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + "print \"X_val\", X_val.shape\n", + "print \"t_out_val\", t_out_val.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/png": "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\nRK5CYII=\n", + "text/plain": [ + "<IPython.core.display.Image object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# NOTICE - THIS MIGHT TAKE UPTO 30 MINUTES ON CPU..!\n", + "# setting up running parameters\n", + "val_interval = 5000\n", + "samples_to_process = 3e5\n", + "samples_processed = 0\n", + "samples_val = []\n", + "costs, accs = [], []\n", + "plt.figure()\n", + "try:\n", + " while samples_processed < samples_to_process:\n", + " # load data\n", + " X_tr, X_len_tr, t_in_tr, t_out_tr, t_len_tr, t_mask_tr, \\\n", + " text_inputs_tr, text_targets_in_tr, text_targets_out_tr = \\\n", + " get_batch(batch_size=BATCH_SIZE,max_digits=MAX_DIGITS,min_digits=MIN_DIGITS)\n", + " # make fetches\n", + " fetches_tr = [train_op, loss, accuracy]\n", + " # set up feed dict\n", + " feed_dict_tr = {Xs: X_tr, X_len: X_len_tr, ts_in: t_in_tr,\n", + " ts_out: t_out_tr, t_len: t_len_tr, t_mask: t_mask_tr}\n", + " # run the model\n", + " res = tuple(sess.run(fetches=fetches_tr, feed_dict=feed_dict_tr))\n", + " _, batch_cost, batch_acc = res\n", + " costs += [batch_cost]\n", + " samples_processed += BATCH_SIZE\n", + " #if samples_processed % 1000 == 0: print batch_cost, batch_acc\n", + " #validation data\n", + " if samples_processed % val_interval == 0:\n", + " #print \"validating\"\n", + " fetches_val = [accuracy_valid, y_valid, alpha_valid]\n", + " feed_dict_val = {Xs: X_val, X_len: X_len_val, ts_in: t_in_val,\n", + " ts_out: t_out_val, t_len: t_len_val, t_mask: t_mask_val}\n", + " res = tuple(sess.run(fetches=fetches_val, feed_dict=feed_dict_val))\n", + " acc_val, output_val, alp_val = res\n", + " samples_val += [samples_processed]\n", + " accs += [acc_val]\n", + " plt.plot(samples_val, accs, 'b-')\n", + " plt.ylabel('Validation Accuracy', fontsize=15)\n", + " plt.xlabel('Processed samples', fontsize=15)\n", + " plt.title('', fontsize=20)\n", + " plt.grid('on')\n", + " plt.savefig(\"out_attention.png\")\n", + " display.display(display.Image(filename=\"out_attention.png\"))\n", + " display.clear_output(wait=True)\n", + "# NOTICE - THIS MIGHT TAKE UPTO 30 MINUTES ON CPU..!\n", + "except KeyboardInterrupt:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'];\n", + " var y0 = fig.canvas.height - msg['y0'];\n", + " var x1 = msg['x1'];\n", + " var y1 = fig.canvas.height - msg['y1'];\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x;\n", + " var y = canvas_pos.y;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", + " fig.send_message('closing', {});\n", + " fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjAAAAIwCAYAAACY8VFvAAAgAElEQVR4Xu3dC5xlZ1nn+x8QoLkl4eJYggEEGRojNAOMNHMUCWiKW2hGcIq76CDNECBDIzYVAfGSFA3aTICIjYKBwyVFUAkBpGSGcESkGU6YhJuNcg+X0hwkCYKNKJzP03l33NnsrrX2qv30rnev3/58lKT7fZ+11vdZ6fr3Wu9a+3r4UUABBRRQQAEFKhO4XmX76+4qoIACCiiggAIYYDwJFFBAAQUUUKA6AQNMdS1zhxVQQAEFFFDAAOM5oIACCiiggALVCRhgqmuZO6yAAgoooIACBhjPAQUUUEABBRSoTsAAU13L3GEFFFBAAQUUMMB4DiiggAIKKKBAdQIGmOpa5g4roIACCiiggAHGc0ABBRRQQAEFqhMwwFTXMndYAQUUUEABBQwwngMKKKCAAgooUJ2AAaa6lrnDCiiggAIKKGCA8RxQQAEFFFBAgeoEDDDVtcwdVkABBRRQQAEDjOeAAgoooIACClQnYICprmXusAIKKKCAAgoYYDwHFFBAAQUUUKA6AQNMdS1zhxVQQAEFFFDAAOM5oIACCiiggALVCRhgqmuZO6yAAgoooIACBhjPAQUUUEABBRSoTsAAU13L3GEFFFBAAQUUMMB4DiiggAIKKKBAdQIGmOpa5g4roIACCiiggAHGc0ABBRRQQAEFqhMwwFTXMndYAQUUUEABBQwwngMKKKCAAgooUJ2AAaa6lrnDCiiggAIKKGCA8RxQQAEFFFBAgeoEDDDVtcwdVkABBRRQQAEDjOeAAgoooIACClQnYICprmXusAIKKKCAAgoYYDwHFFBAAQUUUKA6AQNMdS1zhxVQQAEFFFDAAOM5oIACCiiggALVCRhgqmuZO6yAAgoooIACBhjPAQUUUEABBRSoTsAAU13L3GEFFFBAAQUUMMB4DiiggAIKKKBAdQIGmOpa5g4roIACCiiggAHGc0ABBRRQQAEFqhMwwFTXMndYAQUUUEABBQwwngMKKKCAAgooUJ2AAaa6lrnDCiiggAIKKGCA8RxQQAEFFFBAgeoEDDDVtcwdVkABBRRQQAEDjOeAAgoooIACClQnYICprmXusAIKKKCAAgoYYDwHFFBAAQUUUKA6ga0cYB4DnA7cA7gFcBzw3Q2Ebwm8EngY8D3gnWX+VUNzfhjYBywCNwK+CDwW+Fh1nXOHFVBAAQUU6LHAVg4wpwIRSm4KvKZFgInAckMggk8c1/nAN4FHlv7eCvgIcAFwNvB14M7A1cAVPT4HPHQFFFBAAQWqE9jKAWaA+QDgvQ0B5g7A54AdQ1dT4srNpcDtgS8Bv1WuvPxEdV1yhxVQQAEFFFDgOgLzEmB2lSsuNxnp72Hg0cA7gIPAF8qto/uXqy5vKFdjNro15SmjgAIKKKCAAltMYF4CzBOBlwILI77rwB7gTcCngR8BYuwqcHIJNrFu5iUj88LltsA3tli/3B0FFFBAAQXaCsT60a+UdaFt51Qzbl4CTJsrMLH+5dvA/Ya687yyRmbnSMduV247VdNId1QBBRRQQIExAvHwypfnUWZeAkybNTB/APz4SIBZBh4x8mvR5+OBqy6//HKOPz7+0c80Bc4880zOPjvWUfuZtoC20xa9pp6uOa7a5rleffXVnHTSSbGBE8rDKnkbm1HlrRxgrj+0XuXdwM3LY9RxFSUekx79xDqXeArpceUppLht9K2hp5DuBXwQeBLwVmB7edT6HOBlI8WOBJirrrrKAJNwYj7rWc/i5S9/eUJlS2qbcw7omuMaVbXNsY0Ac8IJkV0MMDnCG1d9MvDaMiQCyyBsxVNJnwc+ATwY+EAZM/wemPili8p7YOIx6cHn4WXR7p2AWB/z6rJ2ZjQQGWASO37ve9+bSy65JHEL/S2tbU7vdc1xjara5tgaYHJca6hqgEns0kUXXcRpp52WuIX+ltY2p/e65rge+Zumfx6k4BpgUlirKGqAqaJN7qQCCiigwDgBA0x/zwsDTH9775EroIAC1QsYYKpvYecDMMB0pmueuLq6ytLSUvNAR0wsoO3EZK0m6NqKqdMgbTuxNU4ywDQSze0AA0xia9fW1lhcjO/T9DNtAW2nLXpNPV1zXLXNczXA5Nlu9coGmK3eIfdPAQUUUOCoAgaY/p4cBpj+9t4jV0ABBaoXMMBU38LOB2CA6UzXPHF9fZ2FhdGvrWqe54hmAW2bjbqM0LWLWrs52rZzmnSUAWZSsfkZb4BJ7OXy8jIrKyuJW+hvaW1zeq9rjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYznRMVUEABBWYtYICZdQdmt30DTKL9gQMH2L17d+IW+lta25ze65rjGlW1zbE1wOS41lDVAJPYpYMHD7Jz587ELfS3tLY5vdc1xzWqaptja4DJca2hqgGmhi65jwoooIACYwUMMP09MQww/e29R66AAgpUL2CAqb6FnQ/AANOZrnnioUOH2L59e/NAR0wsoO3EZK0m6NqKqdMgbTuxNU4ywDQSze0AA0xia/ft28fevXsTt9Df0trm9F7XHNeoqm2OrQEmx7WGqgaYGrrkPiqggAIKjBUwwPT3xDDA9Lf3HrkCCihQvYABpvoWdj4AA0xnOicqoIACCsxawAAz6w7MbvsGmER773nn4WqbY6trjmtU1TbH1gCT41pDVQNMYpd86iAPV9scW11zXKOqtjm2Bpgc1xqqGmBq6JL7qIACCigwVsAA098TwwDT39575AoooED1AgaY6lvY+QAMMJ3pmif63SfNRl1HaNtVbuN5uua4RlVtc2wNMDmuNVQ1wCR2yW+fzcPVNsdW1xzXqKptjq0BJse1hqoGmBq65D4qoIACCowVMMD098QwwPS39x65AgooUL2AAab6FnY+AANMZzonKqCAAgrMWsAAM+sOzG77BphE++XlZVZWVhK30N/S2ub0Xtcc16iqbY6tASbHtYaqBpjELq2vr7OwsJC4hf6W1jan97rmuEZVbXNsDTA5rjVUNcDU0CX3UQEFFFBgrIABpr8nhgGmv733yBVQQIHqBQww1bew8wEYYDrTNU9cW1tjcXGxeaAjJhbQdmKyVhN0bcXUaZC2ndgaJxlgGonmdoABJrG1q6urLC0tJW6hv6W1zem9rjmuUVXbHFsDTI5rDVUNMDV0yX1UQAEFFBgrYIDp74lhgOlv7z1yBRRQoHoBA0z1Lex8AAaYznTNEw8fPsy2bduaBzpiYgFtJyZrNUHXVkydBmnbia1xkgGmkWhuBxhgElu7Z88e9u/fn7iF/pbWNqf3uua4RlVtc2wNMDmuNVQ1wCR2yb9x5eFqm2Ora45rVNU2x9YAk+NaQ1UDTA1dch8VUEABBcYKGGD6e2IYYPrbe49cAQUUqF7AAFN9CzsfgAGmM13zRN/70GzUdYS2XeU2nqdrjmtU1TbH1gCT41pDVQNMYpd882YerrY5trrmuEZVbXNsDTA5rjVUNcDU0CX3UQEFFFBgrIABpr8nhgGmv733yBVQQIHqBQww1bew8wEYYDrTNU9cX19nYWGheaAjJhbQdmKyVhN0bcXUaZC2ndgaJxlgGonmdoABJrG1y8vLrKysJG6hv6W1zem9rjmuUVXbHFsDTI5rDVUNMDV0yX1UQAEFFBgrYIDp74lhgOlv7z1yBRRQoHoBA0z1Lex8AAaYznROVEABBRSYtYABZtYdmN32DTCJ9gcOHGD37t2JW+hvaW1zeq9rjmtU1TbH1gCT41pDVQNMYpcOHjzIzp07E7fQ39La5vRe1xzXqKptjq0BJse1hqoGmBq65D4qoIACCowVMMD098QwwPS39x65AgooUL2AAab6FnY+AANMZ7rmiYcOHWL79u3NAx0xsYC2E5O1mqBrK6ZOg7TtxNY4yQDTSDS3Awwwia3dt28fe/fuTdxCf0trm9N7XXNco6q2ObYGmBzXGqoaYGrokvuogAIKKDBWwADT3xPDANPf3nvkCiigQPUCBpjqW9j5AAwwnemcqIACCigwawEDzKw7MLvtG2AS7b3nnYerbY6trjmuUVXbHFsDTI5rDVUNMIld8qmDPFxtc2x1zXGNqtrm2BpgclxrqGqAqaFL7qMCCiigwFgBA0x/TwwDTH9775EroIAC1QsYYGbbwscApwP3AG4BHAd8d4NduiXwSuBhwPeAd5b5V42ZcwbwMuAs4AVjft8Ak9h7v/skD1fbHFtdc1yjqrY5tgaYHNe2VU8FIpTcFHhNiwATgeWGQASf6wHnA98EHjmywbsC7wK+AbwdeKEBpm1LpjPOb5+djuO4Ktrm2Oqa4xpVtc2xNcDkuE5a9QHAexsCzB2AzwE7gI+VDcSVm0uB2wNfKr92A+ADwNnAs4H3G2AmbYfjFVBAAQW2uoABZmt0qE2A2VWuuNxkZJcPA48G3lF+/flAfAnPE4CLDTBbo8HuhQIKKKDAdAUMMNP17FqtTYB5IvBSYGFkI+vAHuBNwD2BC8v/ft0A07UdzlNAAQUU2OoCBpit0aE2AabpCswacAnw68CflsN6X7kC4yLeY9zn5eVlVlZWjvFW+7E5bXP6rGuOa1TVNsfWAJPjOmnVNgGmaQ1MPMH0WeBrQxs/AfhO+fW7j+zUkaeQTj31VE4++eQjv3XKKadw8cUXc/bZZ7Nt27Yjv7a6usqJJ57I4uLikX9fX1/nnHPOuc4P51igtmPHDnbu3HlkTLy06cILL7zOtzHHmyh37drF9u1xd+uaVfmXXXYZu3fvvna34j/yM844g4WFay4yra2tceWVV7K0tHTk3w8fPsyZZ55Zxf7Ffsdx6Df9/p511lm84hWvuPa88fybzn8fd7zjHY+cs/73O/0//57//OfzhCc8oTd//mX+/IifC+eddx5XXHHFEc9zzz03/iyIn3VXT/qDt4bx8bTOVv1cH7gRcH/g3cDNy2PU3y6PSY/ud6xziaeQHleeQorbRt8qTyFFreHbS3HcFwAfAl4M/N24AHPVVVdx/PGRZfwooIACCihQj4BXYGbbqycDry27EO91GYStuCLzeeATwIPLU0UxbPg9MPHvF5X3wBwtebqId7b9desKKKCAAkkCBpgk2ArK+iK7xCbFZc7BbbfEzfSytLY5bdc1xzWqaptja4DJca2hqgEmsUux9mWwdidxM70srW1O23XNcY2q2ubYGmByXGuoaoCpoUvuowIKKKDAWAEDTH9PDANMf3vvkSuggALVCxhgqm9h5wMwwHSma54Yj3wPHiVsHu2ISQS0nUSr/Vhd21tNOlLbScXajTfAtHOax1EGmMSu7tmzh/379yduob+ltc3pva45rlFV2xxbA0yOaw1VDTCJXfJvXHm42ubY6prjGlW1zbE1wOS41lDVAFNDl9xHBRRQQIGxAgaY/p4YBpj+9t4jV0ABBaoXMMBU38LOB2CA6UzXPNH3PjQbdR2hbVe5jefpmuMaVbXNsTXA5LjWUNUAk9gl37yZh6ttjq2uOa5RVdscWwNMjmsNVQ0wNXTJfVRAAQUUGCtggOnviWGA6W/vPXIFFFCgegEDTPUt7HwABpjOdM0T19fXWVhYaB7oiIkFtJ2YrNUEXVsxdRqkbSe2xkkGmEaiuR1ggEls7fLyMisrK4lb6G9pbXN6r2uOa1TVNsfWAJPjWkNVA0wNXXIfFVBAAQXGChhg+ntiGGD623uPXAEFFKhewABTfQs7H4ABpjOdExVQQAEFZi1ggJl1B2a3fQNMov2BAwfYvXt34hb6W1rbnN7rmuMaVbXNsTXA5LjWUNUAk9ilgwcPsnPnzsQt9Le0tjm91zXHNapqm2NrgMlxraGqAaaGLrmPCiiggAJjBQww/T0xDDD97b1HroACClQvYICpvoWdD8AA05mueeKhQ4fYvn1780BHTCyg7cRkrSbo2oqp0yBtO7E1TjLANBLN7QADTGJr9+3bx969exO30N/S2ub0Xtcc16iqbY6tASbHtYaqBpgauuQ+KqCAAgqMFTDA9PfEMMD0t/ceuQIKKFC9gAGm+hZ2PgADTGc6JyqggAIKzFrAADPrDsxu+waYRHvveefhaptjq2uOa1TVNsfWAJPjWkNVA0xil3zqIA9X2xxbXXNco6q2ObYGmBzXGqoaYGrokvuogAIKKDBWwADT3xPDANPf3nvkCiigQPUCBpjqW9j5AAwwnemaJ/rdJ81GXUdo21Vu43m65rhGVW1zbA0wOa41VDXAJHbJb5/Nw9U2x1bXHNeoqm2OrQEmx7WGqgaYGrrkPiqggAIKjBUwwPT3xDDA9Lf3HrkCCihQvYABpvoWdj4AA0xnOicqoIACCsxawAAz6w7MbvsGmET75eVlVlZWErfQ39La5vRe1xzXqKptjq0BJse1hqoGmMQura+vs7CwkLiF/pbWNqf3uua4RlVtc2wNMDmuNVQ1wNTQJfdRAQUUUGCsgAGmvyeGAaa/vffIFVBAgeoFDDDVt7DzARhgOtM1T1xbW2NxcbF5oCMmFtB2YrJWE3RtxdRpkLad2BonGWAaieZ2gAEmsbWrq6ssLS0lbqG/pbXN6b2uOa5RVdscWwNMjmsNVQ0wNXTJfVRAAQUUGCtggOnviWGA6W/vPXIFFFCgegEDTPUt7HwABpjOdM0TDx8+zLZt25oHOmJiAW0nJms1QddWTJ0GaduJrXGSAaaRaG4HGGASW7tnzx7279+fuIX+ltY2p/e65rhGVW1zbA0wOa41VDXAJHbJv3Hl4WqbY6trjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYzXfNE3/vQbNR1hLZd5Taep2uOa1TVNsfWAJPjWkNVA0xil3zzZh6utjm2uua4RlVtc2wNMDmuNVQ1wNTQJfdRAQUUUGCsgAGmvyeGAaa/vffIFVBAgeoFDDDVt7DzARhgOtM1T1xfX2dhYaF5oCMmFtB2YrJWE3RtxdRpkLad2BonGWAaieZ2gAEmsbXLy8usrKwkbqG/pbXN6b2uOa5RVdscWwNMjmsNVQ0wNXTJfVRAAQUUGCtggOnviWGA6W/vPXIFFFCgegEDTPUt7HwABpjOdE5UQAEFFJi1gAFm1h2Y3fYNMIn2Bw4cYPfu3Ylb6G9pbXN6r2uOa1TVNsfWAJPjWkNVA0xilw4ePMjOnTsTt9Df0trm9F7XHNeoqm2OrQEmx7WGqgaYGrrkPiqggAIKjBUwwPT3xDDA9Lf3HrkCCihQvYABpvoWdj4AA0xnuuaJhw4dYvv27c0DHTGxgLYTk7WaoGsrpk6DtO3E1jjJANNINLcDDDCJrd23bx979+5N3EJ/S2ub03tdc1yjqrY5tgaYHNcaqhpgauiS+6iAAgooMFbAANPfE8MA09/ee+QKKKBA9QIGmNm28DHA6cA9gFsAxwHf3WCXbgm8EngY8D3gnWX+VWXOk4CnAncrv/8x4NeAvxpT0wAz2967dQUUUECBTQgYYDaBN4WppwIRSm4KvKZFgInAckMggs/1gPOBbwKPLPvydOBvS2A5DDwD+K0SaL48sr8GmCk08GglvOedh6ttjq2uOa5RVdscWwNMjuukVR8AvLchwNwB+BywA4grK/GJKzeXArcHvnSUjX4deDJwoQFm0rZ0H+9TB93tmmZq2yTU7fd17ebWZpa2bZQmH2OAmdwsY0abALOrXHG5ycgOxJWWRwPvGLNj9wX+ErgL8HkDTEbrrKmAAgooMAsBA8ws1L9/m20CzBOBlwILI9PXgT3Am0Z+/STg/cDrgReOOUxvIW2N3rsXCiiggAIdBAwwHdASprQJMJNcgflR4D3AKvC8o+zvkQBz6qmncvLJJx8Zcsopp3DxxRdz9tlns23btiO/trq6yoknnsji4uKRf19fX+ecc85hZWXl2rLxRWU7duy49rt/4nLphRdeeJ33oMQ94F27dl37crf4bpDLLrvsOl94uLy8zBlnnMHCwjUZbW1tjSuvvJKlpaUj/3748GHOPPPMKvbv0ksvPeKh3799oeW0+vurv/qrvP71kcuv+Xj+Tee/j7CMc9b/fqf/59/pp5/OM5/5zN78+Zf58yN+Lpx33nlcccUVRzzPPffcOHVPAK5O+Nk885Kx2HWrf9oEmLZrYGJdzLuB6OpZGxy4V2ASzwq/fTYPV9scW11zXAch22+nn76vV2CmbzpJxesDNwLuX0LHzctj1N8uj0GP1op1LvEU0uPKU0hx2+hbQ08h/aeyFuZFwMsbdsQAM0mnHKuAAgoosKUEDDCzbUc8HfTasgvxXpfB1aK4IhOLbj8BPBj4QBkz/B6Y+KWLyntgBpfO4kmmCEP/NHJYcSXmxSO/ZoCZbe/dugIKKKDAJgQMMJvAq3yqAabyBrr7CiigQJ8FDDD97b4BJrH3sWB1eKFz4qZ6V1rbnJbrmuMaVbXNsTXA5LjWUNUAk9ileFpr8DRV4mZ6WVrbnLbrmuMaVbXNsTXA5LjWUNUAU0OX3EcFFFBAgbECBpj+nhgGmP723iNXQAEFqhcwwFTfws4HYIDpTNc8MV62NHj5X/NoR0wioO0kWu3H6treatKR2k4q1m68Aaad0zyOMsAkdjXewDt4g3DiZnpZWtuctuua4xpVtc2xNcDkuNZQ1QBTQ5fcRwUUUECBsQIGmP6eGAaY/vbeI1dAAQWqFzDAVN/CzgdggOlM1zwxvnhy8IVmzaMdMYmAtpNotR+ra3urSUdqO6lYu/EGmHZO8zjKAJPY1T179rB///7ELfS3tLY5vdc1xzWqaptja4DJca2hqgEmsUv+jSsPV9scW11zXKOqtjm2Bpgc1xqqGmBq6JL7qIACCigwVsAA098TwwDT39575AoooED1AgaY6lvY+QAMMJ3pmif63odmo64jtO0qt/E8XXNco6q2ObYGmBzXGqoaYBK75Js383C1zbHVNcc1qmqbY2uAyXGtoaoBpoYuuY8KKKCAAmMFDDD9PTEMMP3tvUeugAIKVC9ggKm+hZ0PwADTma554vr6OgsLC80DHTGxgLYTk7WaoGsrpk6DtO3E1jjJANNINLcDDDCJrV1eXmZlZSVxC/0trW1O73XNcY2q2ubYGmByXGuoaoCpoUvuowIKKKDAWAEDTH9PDANMf3vvkSuggALVCxhgqm9h5wMwwHSmc6ICCiigwKwFDDCz7sDstm+ASbQ/cOAAu3fvTtxCf0trm9N7XXNco6q2ObYGmBzXGqoaYBK7dPDgQXbu3Jm4hf6W1jan97rmuEZVbXNsDTA5rjVUNcDU0CX3UQEFFFBgrIABpr8nhgGmv733yBVQQIHqBQww1bew8wEYYDrTNU88dOgQ27dvbx7oiIkFtJ2YrNUEXVsxdRqkbSe2xkkGmEaiuR1ggEls7b59+9i7d2/iFvpbWtuc3uua4xpVtc2xNcDkuNZQ1QBTQ5fcRwUUUECBsQIGmP6eGAaY/vbeI1dAAQWqFzDAVN/CzgdggOlM50QFFFBAgVkLGGBm3YHZbd8Ak2jvPe88XG1zbHXNcY2q2ubYGmByXGuoaoBJ7JJPHeThaptjq2uOa1TVNsfWAJPjWkNVA0wNXXIfFVBAAQXGChhg2p8Y9wU+1H74lh9pgNnyLXIHFVBAAQWOJmCAaX9ufBf4NPCG8n+fbT91S440wCS2xe8+ycPVNsdW1xzXqKptjq0Bpr3r44DHAz8LHAd8sASZVeAf2pfZMiMNMImt8Ntn83C1zbHVNcc1qmqbY2uAmdz1B4ClEmbittJ3gD8rYebtwD9PXnImMwwwM2F3owoooIAC0xAwwGxO8c4lyMTVmX8PXAn8MfB64P2bK50+2wCTTuwGFFBAAQWyBAwwm5O9LRDh5QnAPUqA+RfgNsBHgCcBn9zcJtJmG2DSaC2sgAIKKJAtYICZXDh+8D+6XHn5aeBfyy2kuOpyEfA94JHAfmAd+InJN3FMZhhgEpmXl5dZWVlJ3EJ/S2ub03tdc1yjqrY5tgaY9q4RSmIR78OBGwMfLreKzge+NqbMLwO/B9yw/SaO6UgDTCL3+vo6CwsLiVvob2ltc3qva45rVNU2x9YA0941HqO+vCzWjastn2qYGgt8nwb8YvtNHNORBphjyu3GFFBAAQWmKWCAaa95CnBx++FbfqQBZsu3yB1UQAEFFDiagAGmv+eGASax92traywuLiZuob+ltc3pva45rlFV2xxbA0x7198u61/uOWbK9cpTRxcCL2pfcqYjDTCJ/KurqywtxeuC/ExbQNtpi15TT9ccV23zXA0w7W0PAW8DnneUKWcB/xn4sfYlZzrSADNTfjeugAIKKLAZAQNMe71vAc8C/vAoU+Kpo3OAm7YvOdORBpiZ8rtxBRRQQIHNCBhg2utdEV9pATx/gysw/w24VfuSMx1pgEnkP3z4MNu2bUvcQn9La5vTe11zXKOqtjm2Bpj2rm8BHgDcC/jSyLSTyhqYvwAe1b7kTEcaYBL59+zZw/798S5DP9MW0HbaotfU0zXHVds8VwNMe9vtwIfKm3ZfC3y8TL078EtALOTduYW/OmD0SA0w7Xs/8Uj/xjUxWesJ2rammmigrhNxTTRY24m4Wg82wLSmOjIwvu/oFcBPjUyLKy+xPuajk5Wb6WgDzEz53bgCCiigwGYEDDDd9H4AuFOZ+lkg1sfU9jHA1NYx91cBBRRQ4FoBA0x/TwYDTGLvfadGHq62Oba65rhGVW1zbA0wk7vGgt14md0JwPXHTI/vSarhY4BJ7JJv3szD1TbHVtcc16iqbY6tAaa9azwTG+EknjKKBbtH+4wLNe23cuxGGmCOnbVbUkABBRSYsoABpj1oPBP7zPIemA8C7wOeDHwVOAO4HfAk4GPtS850pAFmpvxuXAEFFFBgMwIGmPZ6X4wrgUC8cfc2wN8DPwO8t1yRif/9FPC09iVnOtIAk8i/vr7OwsJC4v2ewZIAACAASURBVBb6W1rbnN7rmuMaVbXNsTXAtHc9DDyjfJXAzYBvALuAi0qJ+L0XAv+ufcmZjjTAJPIvLy+zsrKSuIX+ltY2p/e65rhGVW1zbA0w7V2/APwesK9M+QcgfkK9tPz7cpynQASDGj4GmBq65D4qoIACCowVMMC0PzEuAGIh72llynnAQ4DnlKeRfge4FDi1fcmZjjTAzJTfjSuggAIKbEbAANNeL96++2hgb3w3F3B74D3AXUqJz5Rwc6h9yZmONMDMlN+NK6CAAgpsRsAAsxk9uAEQ34X0r8BfA/+yuXLHdLYBJpH7wIED7N69O3EL/S2tbU7vdc1xjara5tgaYNq53hR4I/DW8r/tZm3tUQaYxP4cPHiQnTvjuz39TFtA22mLXlNP1xxXbfNcDTDtba8u613+oP2ULT3SALOl2+POKaCAAgpsJGCAaX9+vCse5wd+qf2UxpGPAU4v33J9C+A44LsbzLol8ErgYcD3gHeW+VcNzYl1Or9d1uh8Hvg14E/H1DTANLbHAQoooIACW1XAANO+M/Ht0/Eiu7cArwK+1H7qUUfGE0sRSuIW1WtaBJgILDcEIvjE1xmcD3wTeGTZwn3LG4IfB7y9vKfmDUAsQL5kZC8MMFNo4NFKHDp0iO3btyduob+ltc3pva45rlFV2xxbA0x713hxXVwhuXGZ8h3g2yVIxNWQCBTxv13eA/OA8kbfja7A3AH4HLBj6OsK7lEe3Y4noiJQ/VHZfnxf0+DzJ0C8s+YpBpj2zd7syH379rF3bzyw5mfaAtpOW/SaerrmuGqb52qAaW8b731p+kSA+cWmQWN+v02Aibf+xhWXm4zMj0e647bRO4CPxDe3D71sL4aeWb6A8t4GmA6dcYoCCiigwJYUMMBsjba0CTBPLG/9Hf2CnViXswd4E/DpMubA0GH9t/L7g/fVDH7ryC2kyy+/iuOP73LRaGvAuRcKKKCAAv0UiABz0kknxMHH/4sHbebuE7d2tvqnTYBpewUm1ui8eOiAN7wCc82Lg08uw08BLgbOLi8djl+OCzonAotlTOSlc8q3KAy2Enkp7mwNHhuOd/ldWN75NxgT38AQhzBYF3IQuAwYfldKfBNDfLH3IKPFkqMrgaVSJC42xeG4f6Dfv53jnn/XWPjfh3++9OHP5/i5EDdErig/T841wLRIOL9Q1rg0DX1904Axv98mwLRZA/PakkRH18B8rXyL9vCmvQLToVFtp7zsZft49rNdA9PWa5Jx2k6i1X6sru2tJh2p7aRi7cZ7BaadU4za6PHm4SrXb1+SGHsj4P7Au4Gbl+3E4uBYTzP6iXUu8RRSPGUUV5bittG3xjyF9NjyiPXDy4v3fAppgqZMY6hPHUxDcXwNbXNsdc1xjara5ti6Bqa96x3HDI2vEogrI08v/xtXaT7ZviRPBuKqSXwGTzLFP8cVmXiHyyeABwMfKGOG3wMTv3RReQ/M8L2/WND7W0Dsbzy15HtgJmiIQxVQQAEF6hAwwEyvT/GiuwgdEWZq+PgemBq65D4qoIACCowVMMBM78SI4PKbwG2mVzK1kgEmkdfvlcnD1TbHVtcc16iqbY6tAWZ6rr8DPK2sY5le1bxKBpg8W799VttEgZzSfmNyjmtU1TbH1gDT3jUW2o77xDPGPw08szw7/PPtS850pAFmpvxuXAEFFFBgMwIGmPZ6Gz2F9K/ABSXExCPLNXwMMDV0yX1UQAEFFBgrYIBpf2LEk0Gjn3hy6OvAF+Kttu1LbYmRBpgt0QZ3QgEFFFCgi4ABpovafMwxwCT2cXl5mZWVlcQt9Le0tjm91zXHNapqm2NrgGnveqfyzv1498q4zyOAj5ZHqdtXnd1IA0yi/fr6OgsLo19blbjBHpXWNqfZuua4RlVtc2wNMO1d/xiIH/o/e5Qp8SbduI00+OKe9pVnM9IAMxt3t6qAAgooMAUBA0x7xK+UbzGMbyUc93ku8Gzgtu1LznSkAWam/G5cAQUUUGAzAgaY9nr/BOwBXnWUKfEiu5cBN25fcqYjDTCJ/GtraywuDr7BO3FDPSytbU7Tdc1xjara5tgaYNq7/g3wYeDxR5kSX6x4X+DO7UvOdKQBJpF/dXWVpaVa7iYmQiSU1jYBFdA1xzWqaptja4Bp7/oi4IXlNtErhr6d+jjgGcB+4CzgBe1LznSkAWam/G5cAQUUUGAzAgaY9nrbgHcADwSuAD5Vpm4v33/0PuChwOH2JWc60gAzU343roACCiiwGQEDzGR6NwCeBDxq6FbRp4F4Qun1Q1dlJqs6m9EGmET3w4cPs21bZF4/0xbQdtqi19TTNcdV2zxXA0ye7VavbIBJ7NCePXvYvz/uKvqZtoC20xa9pp6uOa7a5rkaYNrb3hq4XXlZ3bhZ9wC+BPxD+5IzHWmASeT3b7N5uNrm2Oqa4+oVmDxXA0x729cBdwV2HmXKXwGHgF9qX3KmIw0wM+V34woooIACmxEwwLTXuxz4/fKk0bhZy8DTgDu0LznTkQaYmfK7cQUUUECBzQgYYNrrxdNFzwJefZQpu8ubemtZuWmAad/7iUf63oeJyVpP0LY11UQDdZ2Ia6LB2k7E1XqwAaY1FZ8H4vuO4irLuM+B8hj1Se1LznSkASaR3zdv5uFqm2Ora45rVNU2x9YA0941viYgXlj3aODCkWmPBC4oXzMQV2lq+BhgauiS+6iAAgooMFbAANP+xDgReD9wMnAp8PEy9e7ADuCTwE8BX29fcqYjDTAz5XfjCiiggAKbETDATKZ3cyC+dXr4RXafAd4KvBS4kQFmMtB5Hb2+vs7CwsK8Ht5Mj0vbHH5dc1yjqrY5tgaYzbvGot1HlC95jK8fdhHv5k2rr7C8vMzKykr1x7EVD0DbnK7omuMaVbXNsTXAdHO9HvAg4AnAfwZuUb4f6Z2+B6YbqLMUUEABBRSYRMAAM4kW3KdcaXkM8INl6puBVwIf8ruQJsN0tAIKKKCAAl0FDDDNcncuoeXxwF3K1wWsAv8biP+Np5L+pLnMlhvhIt4t1xJ3SAEFFFCgrYABZmOpg8BPlNtDsVD3fOAvge8BPwr8jQGm7anWr3EHDhxg9+54t6GfaQtoO23Ra+rpmuOqbZ6rAWZj2+/CkRfYPRt4F/CdoeEGmLzzsvrKBw8eZOfOo31tVvWHN9MD0DaHX9cc16iqbY6tAWZj16eX20f3K98yHbeK4irMxcCdgL/1CkzOiWlVBRRQQAEFNhIwwLQ7P35kaB1MfCP1OvA+IBbzugamnaGjFFBAAQUUmJqAAWZyynsPPYkUbyr7e+Ai4O3Ae4D40scaPi7iTezSoUOH2L59e+IW+lta25ze65rjGlW1zbE1wHR3vQHwwBJmfg6It/T+E3Cz7iWP6UwDTCL3vn372Lt3b+IW+lta25ze65rjGlW1zbE1wEzH9SblbbyPA3ZNp2R6FQNMOrEbUEABBRTIEjDAZMlu/boGmK3fI/dQAQUUUOAoAgaY/p4aBpj+9t4jV0ABBaoXMMBU38LOB2CA6UzXPNF73s1GXUdo21Vu43m65rhGVW1zbA0wOa41VDXAJHbJpw7ycLXNsdU1xzWqaptja4DJca2hqgGmhi65jwoooIACYwUMMP09MQww/e29R66AAgpUL2CAqb6FnQ/AANOZrnmi333SbNR1hLZd5Taep2uOa1TVNsfWAJPjWkNVA0xil/xm3zxcbXNsdc1xjara5tgaYHJca6hqgKmhS+6jAgoooMBYAQNMf08MA0x/e++RK6CAAtULGGCqb2HnAzDAdKZzogIKKKDArAUMMLPuwOy2b4BJtF9eXmZlZSVxC/0trW1O73XNcY2q2ubYGmByXGuoaoBJ7NL6+joLCwuJW+hvaW1zeq9rjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYzXfPEtbU1FhcXmwc6YmIBbScmazVB11ZMnQZp24mtcZIBppFobgcYYBJbu7q6ytLSUuIW+lta25ze65rjGlW1zbE1wOS41lDVAFNDl9xHBRRQQIGxAgaY/p4YBpj+9t4jV0ABBaoXMMBU38LOB2CA6UzXPPHw4cNs27ateaAjJhbQdmKyVhN0bcXUaZC2ndgaJxlgGonmdoABJrG1e/bsYf/+/Ylb6G9pbXN6r2uOa1TVNsfWAJPjWkNVA0xil/wbVx6utjm2uua4RlVtc2wNMDmuNVQ1wNTQJfdRAQUUUGCsgAGmvyeGAaa/vffIFVBAgeoFDDDVt7DzARhgOtM1T/S9D81GXUdo21Vu43m65rhGVW1zbA0wOa41VDXAJHbJN2/m4WqbY6trjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYzXfPE9fV1FhYWmgc6YmIBbScmazVB11ZMnQZp24mtcZIBppFobgcYYBJbu7y8zMrKSuIW+lta25ze65rjGlW1zbE1wOS4Tlr1N4CnACcAlwBPBz5xlCJ3A+IVr/cBrg+8DTgD+Mcy/gbAbwKPB24FfKWMf/VIPQPMpF1yvAIKKKDAlhEwwMy+Fc8Fngk8BPgM8ELgScBdgW+OCR0RbM4rIeWWwFuArwGPKmOj1guAU0oIuj/wbmAX8J6hegaY2ffePVBAAQUU6ChggOkIN8VpnytXSF4xdAXlq/H1GcAbRrYTIecC4OZDv/4g4M+B2wNfBv5H+eefGxrz4RJ0XmqAmWLnLKWAAgooMDMBA8zM6I9sOG4ZfR24H/ChoV1ZAz4OPGdk9x5aAswtgO+W3zu1XGE5DXgn8GMlrDwB+CjwwPLvcSUmag4+XoFJ7P2BAwfYvXt34hb6W1rbnN7rmuMaVbXNsTXA5Li2rXoS8AUg1rV8amjS+cDVwFNHCkXgiXFxC+lFwK2BNwM/Wda8xD/fFDir3Jb6Xgk6sUbm90dqGWDadqnDuIMHD7Jz584OM53SJKBtk1C339e1m1ubWdq2UZp8jAFmcrNpzjjaFZi4JRRXT35lzMbuCbwE2AFcBfwu8Cpgsaxx+SPg7sB/AT5b/jkW+sacA16BmWb7rKWAAgooMCsBA8ys5P9tuxEyXgYM1sAcV9ayxBqYN7bYvVicG+N+CPhGCT6vAc4ZmhtPLW0H4hbU4HPkCsypp57KySeffOTXTjnlFC6++GLOPvtstm3bduTX4js8TjzxRBYXIx9BvM/gnHPOuc4jwnF5dMeOHddecTh06BAXXnghe/fuvXZj+/btY9euXWzfHrsB8TeSyy677Dq3WeJRwzPOOOPa96fE67evvPJKlpaWjsyJr6Q/88wz3T/9PP/K6+n97+OaP2L886U/fz7Hz4XzzjuPK6644sjPk3PPPTdOgbgYEHct5u5zvS1+RHGVJZ4cinARYeb5Q08hfWvMvt8bOAR8u6ydeR0QHYwrMfGJf/6PwKOBL5bbUxcBbypPOF0nwFx11VUcf3xkGT/TFIgQNwhr06xrLdA25yzQNcc1qmqbY+sVmBzXSavGe2BivUskiXhi6PTyCHQ8WRSPTT8Y+MBQQIlLEjcBBk8wvXZogzcDXlwem47HrOMR67fGu5SA7wyNcw3MpF2aYHz8jXD4CtQEUx3aIKBtzimia45rVNU2x9YAk+NaQ1UDTA1dch8VUEABBcYKGGD6e2IYYPrbe49cAQUUqF7AAFN9CzsfgAGmM50TFVBAAQVmLWCAmXUHZrd9A0yivfe883C1zbHVNcc1qmqbY2uAyXGtoaoBJrFLPnWQh6ttjq2uOa5RVdscWwNMjmsNVQ0wNXTJfVRAAQUUGCtggOnviWGA6W/vPXIFFFCgegEDTPUt7HwABpjOdM0T/e6TZqOuI7TtKrfxPF1zXKOqtjm2Bpgc1xqqGmASu+S3z+bhaptjq2uOa1TVNsfWAJPjWkNVA0wNXXIfFVBAAQXGChhg+ntiGGD623uPXAEFFKhewABTfQs7H4ABpjOdExVQQAEFZi1ggJl1B2a3fQNMov3y8jIrKyuJW+hvaW1zeq9rjmtU1TbH1gCT41pDVQNMYpfW19dZWFhI3EJ/S2ub03tdc1yjqrY5tgaYHNcaqhpgauiS+6iAAgooMFbAANPfE8MA09/ee+QKKKBA9QIGmOpb2PkADDCd6Zonrq2tsbi42DzQERMLaDsxWasJurZi6jRI205sjZMMMI1EczvAAJPY2tXVVZaWlhK30N/S2ub0Xtcc16iqbY6tASbHtYaqBpgauuQ+KqCAAgqMFTDA9PfEMMD0t/ceuQIKKFC9gAGm+hZ2PgADTGe65omHDx9m27ZtzQMdMbGAthOTtZqgayumToO07cTWOMkA00g0twMMMImt3bNnD/v370/cQn9La5vTe11zXKOqtjm2Bpgc1xqqGmASu+TfuPJwtc2x1TXHNapqm2NrgMlxraGqAaaGLrmPCiiggAJjBQww/T0xDDD97b1HroACClQvYICpvoWdD8AA05mueaLvfWg26jpC265yG8/TNcc1qmqbY2uAyXGtoaoBJrFLvnkzD1fbHFtdc1yjqrY5tgaYHNcaqhpgauiS+6iAAgooMFbAANPfE8MA09/ee+QKKKBA9QIGmOpb2PkADDCd6Zonrq+vs7Cw0DzQERMLaDsxWasJurZi6jRI205sjZMMMI1EczvAAJPY2uXlZVZWVhK30N/S2ub0Xtcc16iqbY6tASbHtYaqBpgauuQ+KqCAAgqMFTDA9PfEMMD0t/ceuQIKKFC9gAGm+hZ2PgADTGc6JyqggAIKzFrAADPrDsxu+waYRPsDBw6we/fuxC30t7S2Ob3XNcc1qmqbY2uAyXGtoaoBJrFLBw8eZOfOnYlb6G9pbXN6r2uOa1TVNsfWAJPjWkNVA0wNXXIfFVBAAQXGChhg+ntiGGD623uPXAEFFKhewABTfQs7H4ABpjNd88RDhw6xffv25oGOmFhA24nJWk3QtRVTp0HadmJrnGSAaSSa2wEGmMTW7tu3j7179yZuob+ltc3pva45rlFV2xxbA0yOaw1VDTA1dMl9VEABBRQYK2CA6e+JYYDpb+89cgUUUKB6AQNM9S3sfAAGmM50TlRAAQUUmLWAAWbWHZjd9g0wifbe887D1TbHVtcc16iqbY6tASbHtYaqBpjELvnUQR6utjm2uua4RlVtc2wNMDmuNVQ1wNTQJfdRAQUUUGCsgAGmvyeGAaa/vffIFVBAgeoFDDDVt7DzARhgOtM1T/S7T5qNuo7QtqvcxvN0zXGNqtrm2BpgclxrqGqASeyS3z6bh6ttjq2uOa5RVdscWwNMjmsNVQ0wNXTJfVRAAQUUGCtggOnviWGA6W/vPXIFFFCgegEDTPUt7HwABpjOdE5UQAEFFJi1gAFm1h2Y3fYNMIn2y8vLrKysJG6hv6W1zem9rjmuUVXbHFsDTI5rDVUNMIldWl9fZ2FhIXEL/S2tbU7vdc1xjara5tgaYHJca6hqgKmhS+6jAgoooMBYAQNMf08MA0x/e++RK6CAAtULGGCqb2HnAzDAdKZrnri2tsbi4mLzQEdMLKDtxGStJujaiqnTIG07sTVOMsA0Es3tAANMYmtXV1dZWlpK3EJ/S2ub03tdc1yjqrY5tgaYHNcaqhpgauiS+6iAAgooMFbAANPfE8MA09/ee+QKKKBA9QIGmOpb2PkADDCd6ZonHj58mG3btjUPdMTEAtpOTNZqgq6tmDoN0rYTW+MkA0wj0dwOMMAktnbPnj3s378/cQv9La1tTu91zXGNqtrm2BpgclxrqGqASeySf+PKw9U2x1bXHNeoqm2OrQEmx7WGqgaYGrrkPiqggAIKjBUwwPT3xDDA9Lf3HrkCCihQvYABpvoWdj4AA0xnuuaJvveh2ajrCG27ym08T9cc16iqbY6tASbHddKqvwE8BTgBuAR4OvCJoxS5GxCrQ+8DXB94G3AG8I9D438Y2AfEq2BvBHwReCzwsaExBphJuzTBeN+8OQHWhEO1nRCs5XBdW0J1GKZtB7QWUwwwLZCShzwXeCbwEOAzwAuBJwF3Bb45su0IHRFszgN+E7gl8Bbga8CjythbAR8BLgDOBr4O3Bm4GrjCAJPcTcsroIACChwTAQPMMWHecCOfK1dUXlFG3QD4ajx5B7xhZGaEnAgmNx/69QcBfw7cHvgy8FvlystPNByaV2Bm33v3QAEFFFCgo4ABpiPclKbFLaO4QnI/4ENDNdeAjwPPGdnOQ0uAuQXw3fJ7pwLvBk4D3gkcBL5Qbh3dv1x1iSAUV2MGc2KqAWZKTRxXZn19nYWFhcQt9Le0tjm91zXHNapqm2NrgMlxbVv1pBI2Yl3Lp4YmnV9u+Tx1pFAEnhgXt5BeBNwaeDPwk8Djyz9/GvgR4Imxdgw4GXgH8ErgJUP1DDBtu9Rh3PLyMisrKx1mOqVJQNsmoW6/r2s3tzaztG2jNPkYA8zkZtOccbQrMHFL6KPAr4zZ2D1LENkBXAX8LvCqctvoPWX9y7fLVZ3B9OcBjwR2jgaYU089lZNPjowDp5xyChdffDFnn332ta/Bj9XzJ554IouLsR74mr9JnHPOOdf54XzgwAF27NjBzp3XlD906BAXXnghe/fuvXZz+/btY9euXWzfvv3Irx08eJDLLruM3bt3Xzsm/iM/44wzrr1yEQvfrrzyymu/1TleBnXmmWe6f/p5/gH+9+GfL3388znO+/POO48rrrjiyM+Tc889N36GxM/SWOc5d5/rbfEj+izwMmCwBua4spYl1sC8scW+7yrjfgj4BvAHwI+PBJhl4BEjv+YVmBa4DlFAAQUU2JoCXoGZfV/iKks8hRTrWyLMPH/oKaRvjdm9e8dFDmBwleV1QETQuBITn3sBHyw13grEJY9YG3NOCUqDkgaY2ffePVBAAQUU6ChggOkIN+Vp8R6YWO8SoeLDwOnlcel4sigem34w8IGyzQgrS8BNgMETTK8d2Z+Hl0W7d4q7PsCrgZcC3xsaZ4CZchOHy8VtteHbY4mb6l1pbXNarmuOa1TVNsfWAJPjWkNVA0xil2KNz2BNUOJmella25y265rjGlW1zbE1wOS41lDVAFNDl9xHBRRQQIGxAgaY/p4YBpj+9t4jV0ABBaoXMMBU38LOB2CA6UzXPDEeJR88Mt482hGTCGg7iVb7sbq2t5p0pLaTirUbb4Bp5zSPowwwiV2N994MvwcncVO9K61tTst1zXGNqtrm2BpgclxrqGqAqaFL7qMCCiigwFgBA0x/TwwDTH9775EroIAC1QsYYKpvYecDMMB0pnOiAgoooMCsBQwws+7A7LZvgEm09553Hq62Oba65rhGVW1zbA0wOa41VDXAJHbJpw7ycLXNsdU1xzWqaptja4DJca2hqgGmhi65jwoooIACYwUMMP09MQww/e29R66AAgpUL2CAqb6FnQ/AANOZrnmi333SbNR1hLZd5Taep2uOa1TVNsfWAJPjWkNVA0xil/z22TxcbXNsdc1xjara5tgaYHJca6hqgKmhS+6jAgoooMBYAQNMf08MA0x/e++RK6CAAtULGGCqb2HnAzDAdKZzogIKKKDArAUMMLPuwOy2b4BJtF9eXmZlZSVxC/0trW1O73XNcY2q2ubYGmByXGuoaoBJ7NL6+joLCwuJW+hvaW1zeq9rjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYzXfPEtbU1FhcXmwc6YmIBbScmazVB11ZMnQZp24mtcZIBppFobgcYYBJbu7q6ytLSUuIW+lta25ze65rjGlW1zbE1wOS41lDVAFNDl9xHBRRQQIGxAgaY/p4YBpj+9t4jV0ABBaoXMMBU38LOB2CA6UzXPPHw4cNs27ateaAjJhbQdmKyVhN0bcXUaZC2ndgaJxlgGonmdoABJrG1e/bsYf/+/Ylb6G9pbXN6r2uOa1TVNsfWAJPjWkNVA0xil/wbVx6utjm2uua4RlVtc2wNMDmuNVQ1wNTQJfdRAQUUUGCsgAGmvyeGAaa/vffIFVBAgeoFDDDVt7DzARhgOtM1T/S9D81GXUdo21Vu43m65rhGVW1zbA0wOa41VDXAJHbJN2/m4WqbY6trjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYzXfPE9fV1FhYWmgc6YmIBbScmazVB11ZMnQZp24mtcZIBppFobgcYYBJbu7y8zMrKSuIW+lta25ze65rjGlW1zbE1wOS41lDVAFNDl9xHBRRQQIGxAgaY/p4YBpj+9t4jV0ABBaoXMMBU38LOB2CA6UznRAUUUECBWQsYYGbdgdlt3wCTaH/gwAF2796duIX+ltY2p/e65rhGVW1zbA0wOa41VDXAJHbp4MGD7Ny5M3EL/S2tbU7vdc1xjara5tgaYHJca6hqgKmhS+6jAgoooMBYAQNMf08MA0x/e++RK6CAAtULGGCqb2HnAzDAdKZrnnjo0CG2b9/ePNAREwtoOzFZqwm6tmLqNEjbTmyNkwwwjURzO8AAk9jaffv2sXfv3sQt9Le0tjm91zXHNapqm2NrgMlxraGqAaaGLrmPCiiggAJjBQww/T0xDDD97b1HroACClQvYICpvoWdD8AA05nOiQoooIACsxYwwMy6A7PbvgEm0d573nm42ubY6prjGlW1zbE1wOS41lDVAJPYJZ86yMPVNsdW1xzXqKptjq0BJse1hqoGmBq65D4qoIACCowVMMD098QwwPS39x65AgooUL2AAab6FnY+AANMZ7rmiX73SbNR1xHadpXbeJ6uOa5RVdscWwNMjmsNVQ0wiV3y22fzcLXNsdU1xzWqaptja4DJca2hqgGmhi65jwoooIACYwUMMP09MQww/e29R66Aa4DypAAAEgdJREFUAgpUL2CAqb6FnQ/AANOZzokKKKCAArMWMMDMugOz274BJtF+eXmZlZWVxC30t7S2Ob3XNcc1qmqbY2uAyXGtoaoBJrFL6+vrLCwsJG6hv6W1zem9rjmuUVXbHFsDTI5rDVUNMDV0yX1UQAEFFBgrYIDp74lhgOlv7z1yBRRQoHoBA0z1Lex8AAaYznTNE9fW1lhcXGwe6IiJBbSdmKzVBF1bMXUapG0ntsZJBphGorkdYIBJbO3q6ipLS0uJW+hvaW1zeq9rjmtU1TbH1gCT41pDVQNMDV1yHxVQQAEFxgoYYPp7Yhhg+tt7j1wBBRSoXsAAU30LOx+AAaYzXfPEw4cPs23btuaBjphYQNuJyVpN0LUVU6dB2nZia5xkgGkkOiYDfgN4CnACcAnwdOATR9ny3YD9wH2A6wNvA84A/nHM+Pj1lwFnAS8Y+X0DTGJr9+zZw/790SY/0xbQdtqi19TTNcdV2zxXA0yebdvKzwWeCTwE+AzwQuBJwF2Bb44JHRFszgN+E7gl8Bbga8CjRsbG/HcB3wDeXuoODzHAtO1Qh3EXXXQRp512WoeZTmkS0LZJqNvv69rNrc0sbdsoTT7GADO52bRnfK5cUXlFKXwD4KvxFyLgDSMbi5BzAXDzoV9/EPDnwO2BLw/V+ABwNvBs4P0GmGm3beN6/m02z1vbHFtdc1y9ApPnaoDJs21TOW4ZfR24H/ChoQlrwMeB54wUeWgJMLcAvlt+71Tg3UD8df+d5deeD2wHngBcbIBp04rpjvGHwXQ9h6tpm2Ora46rASbP1QCTZ9um8knAF4BY1/KpoQnnA1cDTx0pEoEnxsUtpBcBtwbeDPwk8Pjyz/cELgTifyMcbRhgLr/8co4/Pu4m+ZmmwM///M9zwQVxsczPtAW0nbboNfV0zXHVNs81AsxJJ8WP0SPrR+Nn5tx9rreFj+hoV2DiltBHgV8Zs+8RTF4C7ACuAn4XeBUQr319X1kE/OvAn5a58WtxC2l0Ee/tgC9tYRt3TQEFFFBAgTYCPzy0hKLN+GrGbOUAE4ifLU8KDdbAHFcaEWtg3thCeVcZ90PlikzUi0W9g0+EpO+U7dx96NfD5bZlkW+LzThEAQUUUECBLScQSyq+Anxvy+3ZFHZoqweYuMoSTyHF+pYIH7F+ZfAU0rfGHP+9gUPAt8vamdcB55YrMfFY9cJISIn7GLG+5sXA303B0xIKKKCAAgoocAwEtnqACYJ4D0ysd4nFKB8GTi/vgYkni+Kx6QcD8VRRfCKsxJfs3AQYPMH02g0cj7YG5hjQuwkFFFBAAQUU6CpQQ4DpemzOU0ABBRRQQIE5FTDAjG/sJG//ndNTY+qHFbfp4lbgHcpLCGMB9a+6WHrqzrFAPdZ+/Szwv6ZevZ8F41UO8cbueMP3vwKfLE83zuW6gmPY4h8saxwfCNwY+GvgecBfHMN9mIdNPabcmbgHEGteYq3o4FUicXzx668E7lUebnl1ubNR/bEbYL6/hZO8/bf6E+AYHkC8ODDWHH0MuBnwe8CPAf/hGO7DvG8q1oc9Doj3H/0M8N55P+BjcHwRXuKt3bEW763APwOx1i5uZ/vZnMAfA7cBfq681uK/l1dgxF9y4jUXftoJxH/v8eb5mwKvGQkwEWj+BoilFPGG+rsAf1bWhf6PduW37igDzPf3ZpK3/27dzm79PYtH3j9S/sOLR979bE4gHpWMtWDx3qN4f5IBZnOeg9nxmoWDQPzFxs90BS4tP3AHT5nGW9TjfSX3NSB2gn5A+UvL8BWYXyivFokncQdXZZ4FxP/9aKetbKFJBpjrNmPSt/9uoVZWtytx++hpwJ2q2/Ott8Px33G8oTq+++sPyx9UBpjN9yn+Rhs/UON9UvHD4c7A58vXkPzJ5sv3vsJjgV8uVw3j9Rbxeoz/CsQrLeJJUj+TCYwLMPGFxfHm+fiqncHnPwF/WR6MGfdFx5NtdYajDTDXxZ/07b8zbF3Vm44frvFN4XHpOF5M6GdzAvEN7bHuJV7YGJ/4m5YBZnOmMTuuan2xvGLh4cD/Kc7xNvCfLldmNr+V/laIW0XxotF4kjTWFkWIiT8T/qq/JJs68nEBJm4pxS37WCcz+MTb7eMJ3ji/4x0x1X4MMNdtXZe3/1bb/BntePwg+L+BJ5evdZjRbszNZuOqQNzm2Fl+2A4CjIt4N9/iwZ8H+4DloXLx/Wpx+yMWnPrpJhDv5frb8ob0+F67uNIVfza8voTDy7qV7fWscQFmf1lrGCFx8PEKzByfJpt9++8c02z60OI7qeJdPY8G/uemq1kgBCIIHhj5rpP4HrD4gRBXCuI2nZ/uAvFDNhbvDgeYuF0X67eGf637Fvo5M87RK8oi/uGwckk5b1/aT5ZNHfW4ABML+8My3iwfV7niE+tfYlF6LOit+uMVmO9v36Rv/636BDiGO/+Msgo+vhl88OLBY7j5ud1UvLQxnkAYfOK/6cvLJeO4PXfl3B75sTmw+MM+gkr8DTaeoIurBKvA/V1ouukGxG2MuF0Ua1++WV6zEGExXrfgE3TteeNq1o3KORlXB2MxdNxGjnVEcfto8BTSb5eFu+8sj6/7FFJ746pGHu3tv1UdxBbb2fgPKr53Kh5DHf4Mv0l5i+1ytbvjGpjpti5uFcU6oxPLD4P48+Gi6W6il9XiKZi4OhC3NLaVW6DnlIXovQTpeNBxFXbwxvl4N9HgwkRckYl36sSi6LjyHY//x5XZWHcUj1RX//EKTPUt9AAUUEABBRTon4ABpn8994gVUEABBRSoXsAAU30LPQAFFFBAAQX6J2CA6V/PPWIFFFBAAQWqFzDAVN9CD0ABBRRQQIH+CRhg+tdzj1gBBRRQQIHqBQww1bfQA1BAAQUUUKB/AgaY/vXcI1ZAAQUUUKB6AQNM9S30ABRQQAEFFOifgAGmfz33iBXom8DgO2IGbybd6PjvCMT3of0i8Lq+QXm8CtQkYICpqVvu6zwLxOv/23za/BBuU2daY+I18PHN1/G9KldNq+iU64TZ/wJOKa9Wj/KPA34AiFfXD38iwHymBJj4ZmQ/CiiwRQUMMFu0Me5W7wTiB+rw5xdKMHjCyK+/p3yL71YBii8/fQkQP/i/uFV2amQ/4s+5G5bv4orvionPO4CTgR8Zs8/xxXj/Ur4Qb4sekrulgAIGGM8BBbamwCvLFwjGN81u9hP/nd8YOLzZQmPmDwJMBIEvJNTPKhkB5seAO2VtwLoKKJArYIDJ9bW6Al0FxgWYWJfxxHLl4IRyq+MVwO+PbOTzwMeAqHFWGR/fqBy3S+4AxJwHAf8IvBFYA/4MGL09dV8gvnl5Z7mC8WHgTOCvyvZeBLxwzAFudDXmfcCtgfgG3diPewLrwD7gwEitfwesAA8Hjgc+BewHRm/tPAZ4LnAXIK6wRJD6Q+Dlpd7oGpjYh/uPbCvmRAg72hqYBxaL/1Cu5Pw/QJgeGqoz8Ij9eAGwq3wz8J8ApwP/1PVkcJ4CCny/gAHGs0KBrSkwLsB8CPg4cFm5xfEI4FTgGcDvDR3G58oP2QgKEW4i0MQP/0uAjwI/WMLM35W1INuAe4wEmPiBHaEmQstbSzCIALUd+Kny63cvP8QfC/x34P8r+/A24FtHYb24BI3jgFXgb4Al4CeB/wr8UZl3k7K/dy5BJ47pv5Rtx7YG4STW30QA+59ABIX4xJWVCD8RbOIzGmB+ptz2+uGy3zEmwtzbhwJMBKxBUIrxYfHpEoxuCjwTuAFwr6ErT4MA83/KQuDYp3sDTynbi8DjRwEFpiRggJkSpGUUmLLAuAATt4G+PbKd+MEaf+P/0aFfj8Bye2ARiDUzg88e4HfKlYGLyi9GzfiBG8FkcAUm/lyIwBM/sB86ND+CzifKr0ft+Ey6BmZw9SP2JRb+xifWp0Q4uy1wO+BfgTOAlwGPB95cxkXoiSsfEZx+CPhmqRHrhW5VQta4Nox7Culot5AGV2CGA0z4LAB3A64sG4h9iF9/Q7maFL88CDCvAX55aEf+uFzxiUXDfhRQYEoCBpgpQVpGgSkLNK2BiVtI8YM/flDGbaL492+UfYgAE4tQh0NN/Nafl6AS4Wb482zgd4cCTNwmias1EQwiIA1/4pZOLCyOKyTx6RJg7gecOHJLZTfwqnK76n+Xqyo/XgLN8Pbjak0Emrit9C7g14Hnl3+PKzHjPpsJMBGUvlxucS2PFA+buMISV3viMwgw/7H4DYYPfOM2WFzp8aOAAlMQMMBMAdESCiQIjAsw/9fQmpS4jTH4xLqPWNvypfILEWD+tjzFNLxrcVXlK+Vx4uFfj1tRcdtncAUmbtWcv8Exxfbiikc8Nt0lwMS+jj79E7es4pZL3PZ5S1lbEmtjYp+GPzvKlY9YUxKBJ65qxFWduDoSQSNCWswfDjObCTCx/ifW/PwScN7IvsQVorhSdLMSxgYBJm7RXTE0Nq7mvLb06PKEc8WSCvRSwADTy7Z70BUIjAaYWAsSt28+WX5wxw/CfwYeBsTf8IcXzkaAibUuEUy6BJgIEW8q4eTSo1j9RbnKM+sAE7sXV6LiltZDyv+FRaxfieAQn2MdYG4D/MOYALOVHzWv4D8Jd1GB6woYYDwjFNiaAqMBJhauxhM4cftncKUl9jxuH8WtjTYBJq5OxJWKk0YOebA2ZnAFJm6BxJqUuK3zBw08zwFeOsF7YOJqSbz8Lm4hDS/0fVpZiBxXPAa3kGKdSayJGby7JXZl9BbS6O7Fn2mxoDn2PW6hxVt1xwWYWAMU74EZfYx6dA3M4BZSvOtmdBFu3EKKRbxxxSU+gyswBpit+d+UezVnAgaYOWuohzM3AqMBJp56icegh4NKrHv56/IDNG7JDF4kd7QrMIOg8sjyxE1gxcLcWIx615FFvPF0ULwdOH5Ax2LZ4U/cthncIhkEj1g3E09HNX0Gi3gj+MQtmPjEi+MiMEVYGCzifVZZoBsv+BvczopFvDE/gk0s+I39iltZw1c7ot7TyyPkEVDCZ1yAiZrxBFfMH/6MW8T7kbJvsdB58LbhWJ8TxxtXeuLpLANMU+f9fQWmLGCAmTKo5RSYksBogPn35bZQrGN5NXDz8nhuLAqNdSFtrsDEWo14P8zgMepYYxJP+cQtmHgfy08D7y/7H/8cVxj+vjzaHGtnIlzE6/jjh/jg9tR9yhWTWFAbj0V/p4Sjoz1GHQEkrowMHqOOtTpxVSXW98SC5HiCJz4RrGIh8eAx6nhPy6PL0zyx7iTeIROfPwVuCby3rIGJ9TUR9uLKSyywjc+4ADO49RUh6v8ti2vjqsy4ABPvzBk8Rh37N3iMOl4yGNsYvMDPKzBTOvkto0AbAQNMGyXHKHDsBeIHdFxJiHeNDD7x5M1vAxFmvlrWwsS7V+KH6vAVmHhnSgSV0TUwUSd+QEftWDQbTy3FFYQPAvGob7y4Lt77MvhEMIoXskWYicAU24wrJfHCuQgig8+vAXElJq6gxJ8pw/syKhfz4qpHPOEUIS2u3ESQittQsSh3+BNXel4MnFZeZBcvjRt9kd3PAU8tASxuS8U+RtiIMBHhaxBgRr8LKUJIBMF4TDzmxVWruJ00LsBEjcGL7OKKVIS0OI695XHzwT7HE1HxYr/Y79E1MKM9OvZnlFtUYM4EDDBz1lAPR4EOAoP1NXGFJQJA5mcQYOLFeX4UUECBzgIGmM50TlSgSoF4f8vwK+0Ha2Diz4JY45H9McBkC1tfgZ4IGGB60mgPU4EiELdXYs1GLECNRcDxUrp4MinWwmz07pdpAQ6+CykW4vpRQAEFOgsYYDrTOVGBKgViAWx8N0+s9Yj1NfFumXhE+IJjdDTxXUjxHU3eQjpG4G5GgXkVMMDMa2c9LgUUUEABBeZYwAAzx8310BRQQAEFFJhXAQPMvHbW41JAAQUUUGCOBQwwc9xcD00BBRRQQIF5FTDAzGtnPS4FFFBAAQXmWMAAM8fN9dAUUEABBRSYVwEDzLx21uNSQAEFFFBgjgUMMHPcXA9NAQUUUECBeRUwwMxrZz0uBRRQQAEF5ljAADPHzfXQFFBAAQUUmFcBA8y8dtbjUkABBRRQYI4FDDBz3FwPTQEFFFBAgXkVMMDMa2c9LgUUUEABBeZYwAAzx8310BRQQAEFFJhXAQPMvHbW41JAAQUUUGCOBf5/xd2ZXOAc4gQAAAAASUVORK5CYII=\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#plot of validation accuracy for each target position\n", + "plt.figure(figsize=(7,7))\n", + "plt.plot(np.mean(np.argmax(output_val,axis=2)==t_out_val,axis=0))\n", + "plt.ylabel('Accuracy', fontsize=15)\n", + "plt.xlabel('Target position', fontsize=15)\n", + "#plt.title('', fontsize=20)\n", + "plt.grid('on')\n", + "plt.show()\n", + "#why do the plot look like this?" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'];\n", + " var y0 = fig.canvas.height - msg['y0'];\n", + " var x1 = msg['x1'];\n", + " var y1 = fig.canvas.height - msg['y1'];\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x;\n", + " var y = canvas_pos.y;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", + " fig.send_message('closing', {});\n", + " fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,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\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "### attention plot, try with different i = 1, 2, ..., 1000\n", + "i = 42\n", + "\n", + "column_labels = map(str, list(t_out_val[i]))\n", + "row_labels = map(str, (list(X_val[i])))\n", + "data = alp_val[i]\n", + "fig, ax = plt.subplots()\n", + "heatmap = ax.pcolor(data, cmap=plt.cm.Blues)\n", + "\n", + "# put the major ticks at the middle of each cell\n", + "ax.set_xticks(np.arange(data.shape[1])+0.5, minor=False)\n", + "ax.set_yticks(np.arange(data.shape[0])+0.5, minor=False)\n", + "\n", + "# want a more natural, table-like display\n", + "ax.invert_yaxis()\n", + "ax.xaxis.tick_top()\n", + "\n", + "ax.set_xticklabels(row_labels, minor=False)\n", + "ax.set_yticklabels(column_labels, minor=False)\n", + "\n", + "plt.ylabel('output', fontsize=15)\n", + "plt.xlabel('Attention plot', fontsize=15)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'];\n", + " var y0 = fig.canvas.height - msg['y0'];\n", + " var x1 = msg['x1'];\n", + " var y1 = fig.canvas.height - msg['y1'];\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x;\n", + " var y = canvas_pos.y;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", + " fig.send_message('closing', {});\n", + " fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuydBZiVVffFF93d3SDdPRiIIkiI/LFFEEUxEAQMBEVFQEVKETEQsbAIUUqQGrpLumOAoYYYwgH+z7rhdxlm5s07w51Z+3n49HNOvb/3zGXdfc7eOxVkIiACIiACIiACIiACKYpAqhT1tHpYERABERABERABERABSABqE4iACIiACIiACIhACiMgAZjCXrgeVwREQAREQAREQAQkALUHREAEREAEREAERCCFEZAATGEvXI8rAiIgAiIgAiIgAhKA2gMiIAIiIAIiIAIikMIISACmsBeuxxUBERABERABERABCUDtAREQAREQAREQARFIYQQkAFPYC9fjioAIiIAIiIAIiIAEoPaACIiACIiACIiACKQwAhKAKeyF63FFQAREQAREQAREQAJQe0AEREAEREAEREAEUhgBCcAU9sL1uCIgAiIgAiIgAiIgAag9IAIiIAIiIAIiIAIpjIAEYAp74XpcERABERABERABEZAA1B4QAREQAREQAREQgRRGQAIwhb1wPa4IiIAIiIAIiIAISABqD4iACIiACIiACIhACiMgAZjCXrgeVwREQAREQAREQAQkALUHREAEREAEREAERCCFEZAATGEvXI8rAiIgAiIgAiIgAhKA2gMiIAIiIAIiIAIikMIISACmsBeuxxUBERABERABERABCUDtAREQAREQAREQARFIYQQkAFPYC9fjioAIiIAIiIAIiIAEoPaACKRsAusBVAVwGUBhACcTwDEeQEcAnQF84wK2TgDG+cbimCnZBgB4E8Dbvj9OWdwO4G8ACwDc4XQw9RcBEUh+BCQAk9871ROJgFkCdQEsD2jcA8AoEwKQwm2C2UkSaOcXgBSWT7owXigP8VaAAHzHhQfxC8D5AJpaHO+qr31qi/3UXAREIIQISACG0MvSUkXAZQKfAegK4BCAIgA2AqguAegyZXPD5QHAP8cNvLDmRgOcCsBrANKYnUztREAEQo+ABGDovTOtWATcIJAZQASArD4P0TTfv9cDsCqeCfxHwPIAuvEGgjuGBGBw+Wp0EQh5AhKAIf8K9QAiYIvAEwC+DvD6fQGgC4CxALpZFICB99coEt8FcBeAXAD2AfgBwPsALsYaN/AI+AUA/QF0AFAMwCkAMwH0BXA4jvXc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\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#Plot of average attention weight as a function of the sequence position for each of \n", + "#the 21 targets in the output sequence i.e. each line is the mean postion of the \n", + "#attention for each target position.\n", + "\n", + "np.mean(alp_val, axis=0).shape\n", + "plt.figure()\n", + "plt.plot(np.mean(alp_val, axis=0).T)\n", + "plt.ylabel('alpha', fontsize=15)\n", + "plt.xlabel('Input Sequence position', fontsize=15)\n", + "plt.title('Alpha weights', fontsize=20)\n", + "plt.legend(map(str,range(1,22)), bbox_to_anchor=(1.125,1.0), fontsize=10)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Assignments for the attention decoder\n", + "1. Explain what the attention plot show.\n", + "2. Explain what the alphaweights show.\n", + "3. Why are the alpha curve for the first digit narrow and peaked while later digits have alpha curves that are wider and less peaked?\n", + "4. Why is attention a good idea for this problem? Can you think of other problems where attention is a good choice?\n", + "5. Try setting MIN_DIGITS and MAX_DIGITS to 20\n", + "6. Enable gradient clipping (under the loss codeblock)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/tf_utils.py b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/tf_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..bdf6437fcf387f949fe5e07d063b63b1c5cf0143 --- /dev/null +++ b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/tf_utils.py @@ -0,0 +1,373 @@ +import tensorflow as tf +from tensorflow.python.ops import tensor_array_ops +from tensorflow.python.framework import ops +from tensorflow.python.ops import nn_ops +from tensorflow.python.ops import math_ops + + +### +# custom loss function, similar to tensorflows but uses 3D tensors +# instead of a list of 2D tensors +def sequence_loss_tensor(logits, targets, weights, num_classes, + average_across_timesteps=True, + softmax_loss_function=None, name=None): + """Weighted cross-entropy loss for a sequence of logits (per example). + """ + with ops.op_scope([logits, targets, weights], name, "sequence_loss_by_example"): + probs_flat = tf.reshape(logits, [-1, num_classes]) + targets = tf.reshape(targets, [-1]) + if softmax_loss_function is None: + crossent = nn_ops.sparse_softmax_cross_entropy_with_logits( + probs_flat, targets) + else: + crossent = softmax_loss_function(probs_flat, targets) + crossent = crossent * tf.reshape(weights, [-1]) + crossent = tf.reduce_sum(crossent) + total_size = math_ops.reduce_sum(weights) + total_size += 1e-12 # to avoid division by zero + crossent /= total_size + return crossent + + +### +# a custom masking function, takes sequence lengths and makes masks +def mask(sequence_lengths): + # based on this SO answer: http://stackoverflow.com/a/34138336/118173 + batch_size = tf.shape(sequence_lengths)[0] + max_len = tf.reduce_max(sequence_lengths) + + lengths_transposed = tf.expand_dims(sequence_lengths, 1) + + rng = tf.range(max_len) + rng_row = tf.expand_dims(rng, 0) + + return tf.less(rng_row, lengths_transposed) + + +### +# a custom encoder function (in case we cant get tensorflows to work) + +def encoder(inputs, lengths, name, num_units, reverse=False, swap=False): + with tf.variable_scope(name): + weight_initializer = tf.truncated_normal_initializer(stddev=0.1) + input_units = inputs.get_shape()[2] + W_z = tf.get_variable('W_z', + shape=[input_units+num_units, num_units], + initializer=weight_initializer) + W_r = tf.get_variable('W_r', + shape=[input_units+num_units, num_units], + initializer=weight_initializer) + W_h = tf.get_variable('W_h', + shape=[input_units+num_units, num_units], + initializer=weight_initializer) + b_z = tf.get_variable('b_z', + shape=[num_units], + initializer=tf.constant_initializer(1.0)) + b_r = tf.get_variable('b_r', + shape=[num_units], + initializer=tf.constant_initializer(1.0)) + b_h = tf.get_variable('b_h', + shape=[num_units], + initializer=tf.constant_initializer()) + + max_sequence_length = tf.reduce_max(lengths) + min_sequence_length = tf.reduce_min(lengths) + + time = tf.constant(0) + + state_shape = tf.concat(0, [tf.expand_dims(tf.shape(lengths)[0], 0), + tf.expand_dims(tf.constant(num_units), 0)]) + # state_shape = tf.Print(state_shape, [state_shape]) + state = tf.zeros(state_shape, dtype=tf.float32) + + if reverse: + inputs = tf.reverse(inputs, dims=[False, True, False]) + inputs = tf.transpose(inputs, perm=[1, 0, 2]) + input_ta = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True) + input_ta = input_ta.unpack(inputs) + + output_ta = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True) + + def encoder_cond(time, state, output_ta_t): + return tf.less(time, max_sequence_length) + + def encoder_body(time, old_state, output_ta_t): + x_t = input_ta.read(time) + + con = tf.concat(1, [x_t, old_state]) + z = tf.sigmoid(tf.matmul(con, W_z) + b_z) + r = tf.sigmoid(tf.matmul(con, W_r) + b_r) + con = tf.concat(1, [x_t, r*old_state]) + h = tf.tanh(tf.matmul(con, W_h) + b_h) + new_state = (1-z)*h + z*old_state + + output_ta_t = output_ta_t.write(time, new_state) + + def updateall(): + return new_state + + def updatesome(): + if reverse: + return tf.select( + tf.greater_equal(time, max_sequence_length-lengths), + new_state, + old_state) + else: + return tf.select(tf.less(time, lengths), new_state, old_state) + + if reverse: + state = tf.cond( + tf.greater_equal(time, max_sequence_length-min_sequence_length), + updateall, + updatesome) + else: + state = tf.cond(tf.less(time, min_sequence_length), updateall, updatesome) + + return (time + 1, state, output_ta_t) + + loop_vars = [time, state, output_ta] + + time, state, output_ta = tf.while_loop(encoder_cond, encoder_body, loop_vars, swap_memory=swap) + + enc_state = state + enc_out = tf.transpose(output_ta.pack(), perm=[1, 0, 2]) + + if reverse: + enc_out = tf.reverse(enc_out, dims=[False, True, False]) + + enc_out.set_shape([None, None, num_units]) + + return enc_state, enc_out + + +### +# a custom decoder function + +def decoder(initial_state, target_input, target_len, num_units, + embeddings, W_out, b_out, + W_z_x_init = tf.truncated_normal_initializer(stddev=0.1), + W_z_h_init = tf.truncated_normal_initializer(stddev=0.1), + W_r_x_init = tf.truncated_normal_initializer(stddev=0.1), + W_r_h_init = tf.truncated_normal_initializer(stddev=0.1), + W_c_x_init = tf.truncated_normal_initializer(stddev=0.1), + W_c_h_init = tf.truncated_normal_initializer(stddev=0.1), + b_z_init = tf.constant_initializer(0.0), + b_r_init = tf.constant_initializer(0.0), + b_c_init = tf.constant_initializer(0.0), + name='decoder', swap=False): + """decoder + TODO + """ + + + with tf.variable_scope(name): + # we need the max seq len to optimize our RNN computation later on + max_sequence_length = tf.reduce_max(target_len) + # target_dims is just the embedding size + target_dims = target_input.get_shape()[2] + # set up weights for the GRU gates + var = tf.get_variable # for ease of use + # target_dims + num_units is because we stack embeddings and prev. hidden state to + # optimize speed + W_z_x = var('W_z_x', shape=[target_dims, num_units], initializer=W_z_x_init) + W_z_h = var('W_z_h', shape=[num_units, num_units], initializer=W_z_h_init) + b_z = var('b_z', shape=[num_units], initializer=b_z_init) + W_r_x = var('W_r_x', shape=[target_dims, num_units], initializer=W_r_x_init) + W_r_h = var('W_r_h', shape=[num_units, num_units], initializer=W_r_h_init) + b_r = var('b_r', shape=[num_units], initializer=b_r_init) + W_c_x = var('W_c_x', shape=[target_dims, num_units], initializer=W_c_x_init) + W_c_h = var('W_c_h', shape=[num_units, num_units], initializer=W_c_h_init) + b_c = var('b_h', shape=[num_units], initializer=b_c_init) + + # make inputs time-major + inputs = tf.transpose(target_input, perm=[1, 0, 2]) + # make tensor array for inputs, these are dynamic and used in the while-loop + # these are not in the api documentation yet, you will have to look at github.com/tensorflow + input_ta = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True) + input_ta = input_ta.unpack(inputs) + + # function to the while-loop, for early stopping + def decoder_cond(time, state, output_ta_t): + return tf.less(time, max_sequence_length) + + # the body_builder is just a wrapper to parse feedback + def decoder_body_builder(feedback=False): + # the decoder body, this is where the RNN magic happens! + def decoder_body(time, old_state, output_ta_t): + # when validating we need previous prediction, handle in feedback + if feedback: + def from_previous(): + prev_1 = tf.matmul(old_state, W_out) + b_out + return tf.gather(embeddings, tf.argmax(prev_1, 1)) + x_t = tf.cond(tf.greater(time, 0), from_previous, lambda: input_ta.read(0)) + else: + # else we just read the next timestep + x_t = input_ta.read(time) + + # calculate the GRU + z = tf.sigmoid(tf.matmul(x_t, W_z_x) + tf.matmul(old_state, W_z_h) + b_z) # update gate + r = tf.sigmoid(tf.matmul(x_t, W_r_x) + tf.matmul(old_state, W_r_h) + b_r) # reset gate + c = tf.tanh(tf.matmul(x_t, W_c_x) + tf.matmul(r*old_state, W_c_h) + b_c) # proposed new state + new_state = (1-z)*c + z*old_state # new state + + # writing output + output_ta_t = output_ta_t.write(time, new_state) + + # return in "input-to-next-step" style + return (time + 1, new_state, output_ta_t) + return decoder_body + # set up variables to loop with + output_ta = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True, infer_shape=False) + time = tf.constant(0) + loop_vars = [time, initial_state, output_ta] + + # run the while-loop for training + _, state, output_ta = tf.while_loop(decoder_cond, + decoder_body_builder(), + loop_vars, + swap_memory=swap) + # run the while-loop for validation + _, valid_state, valid_output_ta = tf.while_loop(decoder_cond, + decoder_body_builder(feedback=True), + loop_vars, + swap_memory=swap) + # returning to batch major + dec_out = tf.transpose(output_ta.pack(), perm=[1, 0, 2]) + valid_dec_out = tf.transpose(valid_output_ta.pack(), perm=[1, 0, 2]) + return dec_out, valid_dec_out + + +### +# decoder with attention + +def attention_decoder(attention_input, attention_lengths, initial_state, target_input, + target_input_lengths, num_units, num_attn_units, embeddings, W_out, b_out, + name='decoder', swap=False): + """Decoder with attention. + Note that the number of units in the attention decoder must always + be equal to the size of the initial state/attention input. + Keyword arguments: + attention_input: the input to put attention on. expected dims: [batch_size, attention_length, attention_dims] + initial_state: The initial state for the decoder RNN. + target_input: The target to replicate. Expected: [batch_size, max_target_sequence_len, embedding_dims] + num_attn_units: Number of units in the alignment layer that produces the context vectors. + """ + with tf.variable_scope(name): + target_dims = target_input.get_shape()[2] + attention_dims = attention_input.get_shape()[2] + attn_len = tf.shape(attention_input)[1] + max_sequence_length = tf.reduce_max(target_input_lengths) + + weight_initializer = tf.truncated_normal_initializer(stddev=0.1) + # map initial state to num_units + W_s = tf.get_variable('W_s', + shape=[attention_dims, num_units], + initializer=weight_initializer) + b_s = tf.get_variable('b_s', + shape=[num_units], + initializer=tf.constant_initializer()) + + # GRU + W_z = tf.get_variable('W_z', + shape=[target_dims+num_units+attention_dims, num_units], + initializer=weight_initializer) + W_r = tf.get_variable('W_r', + shape=[target_dims+num_units+attention_dims, num_units], + initializer=weight_initializer) + W_c = tf.get_variable('W_c', + shape=[target_dims+num_units+attention_dims, num_units], + initializer=weight_initializer) + b_z = tf.get_variable('b_z', + shape=[num_units], + initializer=tf.constant_initializer(1.0)) + b_r = tf.get_variable('b_r', + shape=[num_units], + initializer=tf.constant_initializer(1.0)) + b_c = tf.get_variable('b_c', + shape=[num_units], + initializer=tf.constant_initializer()) + + # for attention + W_a = tf.get_variable('W_a', + shape=[attention_dims, num_attn_units], + initializer=weight_initializer) + U_a = tf.get_variable('U_a', + shape=[1, 1, attention_dims, num_attn_units], + initializer=weight_initializer) + b_a = tf.get_variable('b_a', + shape=[num_attn_units], + initializer=tf.constant_initializer()) + v_a = tf.get_variable('v_a', + shape=[num_attn_units], + initializer=weight_initializer) + + # project initial state + initial_state = tf.nn.tanh(tf.matmul(initial_state, W_s) + b_s) + + # TODO: don't use convolutions! + # TODO: fix the bias (b_a) + hidden = tf.reshape(attention_input, tf.pack([-1, attn_len, 1, attention_dims])) + part1 = tf.nn.conv2d(hidden, U_a, [1, 1, 1, 1], "SAME") + part1 = tf.squeeze(part1, [2]) # squeeze out the third dimension + + inputs = tf.transpose(target_input, perm=[1, 0, 2]) + input_ta = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True) + input_ta = input_ta.unpack(inputs) + + def decoder_cond(time, state, output_ta_t, attention_tracker): + return tf.less(time, max_sequence_length) + + def decoder_body_builder(feedback=False): + def decoder_body(time, old_state, output_ta_t, attention_tracker): + if feedback: + def from_previous(): + prev_1 = tf.matmul(old_state, W_out) + b_out + return tf.gather(embeddings, tf.argmax(prev_1, 1)) + x_t = tf.cond(tf.greater(time, 0), from_previous, lambda: input_ta.read(0)) + else: + x_t = input_ta.read(time) + + # attention + part2 = tf.matmul(old_state, W_a) + b_a + part2 = tf.expand_dims(part2, 1) + john = part1 + part2 + e = tf.reduce_sum(v_a * tf.tanh(john), [2]) + alpha = tf.nn.softmax(e) + alpha = tf.to_float(mask(attention_lengths)) * alpha + alpha = alpha / tf.reduce_sum(alpha, [1], keep_dims=True) + attention_tracker = attention_tracker.write(time, alpha) + context = tf.reduce_sum(tf.expand_dims(alpha, 2) * tf.squeeze(hidden), [1]) + + # GRU + con = tf.concat(1, [x_t, old_state, context]) + z = tf.sigmoid(tf.matmul(con, W_z) + b_z) + r = tf.sigmoid(tf.matmul(con, W_r) + b_r) + con = tf.concat(1, [x_t, r*old_state, context]) + c = tf.tanh(tf.matmul(con, W_c) + b_c) + new_state = (1-z)*c + z*old_state + + output_ta_t = output_ta_t.write(time, new_state) + + return (time + 1, new_state, output_ta_t, attention_tracker) + return decoder_body + + + output_ta = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True, infer_shape=False) + attention_tracker = tensor_array_ops.TensorArray(tf.float32, size=1, dynamic_size=True, infer_shape=False) + time = tf.constant(0) + loop_vars = [time, initial_state, output_ta, attention_tracker] + + _, state, output_ta, _ = tf.while_loop(decoder_cond, + decoder_body_builder(), + loop_vars, + swap_memory=swap) + _, valid_state, valid_output_ta, valid_attention_tracker = tf.while_loop(decoder_cond, + decoder_body_builder(feedback=True), + loop_vars, + swap_memory=swap) + + dec_out = tf.transpose(output_ta.pack(), perm=[1, 0, 2]) + valid_dec_out = tf.transpose(valid_output_ta.pack(), perm=[1, 0, 2]) + valid_attention_tracker = tf.transpose(valid_attention_tracker.pack(), perm=[1, 0, 2]) + + return dec_out, valid_dec_out, valid_attention_tracker diff --git a/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/tf_utils.pyc b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/tf_utils.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c44b765ee3b39a3ce531b015974cab625994e4e1 Binary files /dev/null and b/hw6_rnn/RNN_martink/RNN_on_letters_to_numbers/tf_utils.pyc differ diff --git a/hw6_rnn/RNN_martink/lstm_applied_on_mnist.py b/hw6_rnn/RNN_martink/lstm_applied_on_mnist.py new file mode 100644 index 0000000000000000000000000000000000000000..a6fdfc8893db82dfe5eb43d6e1ea014cfb684793 --- /dev/null +++ b/hw6_rnn/RNN_martink/lstm_applied_on_mnist.py @@ -0,0 +1,86 @@ +#Inspired by https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3%20-%20Neural%20Networks/recurrent_network.py +import tensorflow as tf + +import numpy as np +import input_data + +# configuration +# O * W + b -> 10 labels for each image, O[? 28], W[28 10], B[10] +# ^ (O: output 28 vec from 28 vec input) +# | +# +-+ +-+ +--+ +# |1|->|2|-> ... |28| time_step_size = 28 +# +-+ +-+ +--+ +# ^ ^ ... ^ +# | | | +# img1:[28] [28] ... [28] +# img2:[28] [28] ... [28] +# img3:[28] [28] ... [28] +# ... +# img128 or img256 (batch_size or test_size 256) +# each input size = input_vec_size=lstm_size=28 + +# configuration variables +input_vec_size = lstm_size = 28 +time_step_size = 28 + +batch_size = 128 +test_size = 256 + +def init_weights(shape): + return tf.Variable(tf.random_normal(shape, stddev=0.01)) + + +def model(X, W, B, lstm_size): + # X, input shape: (batch_size, time_step_size, input_vec_size) + XT = tf.transpose(X, [1, 0, 2]) # permute time_step_size and batch_size + # XT shape: (time_step_size, batch_size, input_vec_size) + XR = tf.reshape(XT, [-1, lstm_size]) # each row has input for each lstm cell (lstm_size=input_vec_size) + # XR shape: (time_step_size * batch_size, input_vec_size) + X_split = tf.split(0, time_step_size, XR) # split them to time_step_size (28 arrays) + # Each array shape: (batch_size, input_vec_size) + + # Make lstm with lstm_size (each input vector size) + lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size, forget_bias=1.0, state_is_tuple=True) + + # Get lstm cell output, time_step_size (28) arrays with lstm_size output: (batch_size, lstm_size) + outputs, _states = tf.nn.rnn(lstm, X_split, dtype=tf.float32) + + # Linear activation + # Get the last output + return tf.matmul(outputs[-1], W) + B, lstm.state_size # State size to initialize the stat + +mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) +trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels +trX = trX.reshape(-1, 28, 28) +teX = teX.reshape(-1, 28, 28) + +X = tf.placeholder("float", [None, 28, 28]) +Y = tf.placeholder("float", [None, 10]) + +# get lstm_size and output 10 labels +W = init_weights([lstm_size, 10]) +B = init_weights([10]) + +py_x, state_size = model(X, W, B, lstm_size) + +cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, Y)) +train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost) +predict_op = tf.argmax(py_x, 1) + +# Launch the graph in a session +with tf.Session() as sess: + # you need to initialize all variables + tf.initialize_all_variables().run() + + for i in range(100): + for start, end in zip(range(0, len(trX), batch_size), range(batch_size, len(trX)+1, batch_size)): + sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end]}) + + test_indices = np.arange(len(teX)) # Get A Test Batch + np.random.shuffle(test_indices) + test_indices = test_indices[0:test_size] + + print(i, np.mean(np.argmax(teY[test_indices], axis=1) == + sess.run(predict_op, feed_dict={X: teX[test_indices], + Y: teY[test_indices]})))