{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Logistic Regression in Theano\n", "\n", "Credits: Forked from [summerschool2015](https://github.com/mila-udem/summerschool2015) by mila-udem\n", "\n", "This notebook is inspired from [the tutorial on logistic regression](http://deeplearning.net/tutorial/logreg.html) on [deeplearning.net](http://deeplearning.net).\n", "\n", "In this notebook, we show how Theano can be used to implement the most basic classifier: the **logistic regression**. We start off with a quick primer of the model, which serves both as a refresher but also to anchor the notation and show how mathematical expressions are mapped onto Theano graphs.\n", "\n", "In the deepest of machine learning traditions, this tutorial will tackle the exciting problem of MNIST digit classification." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get the data\n", "\n", "In the mean time, let's just download a pre-packaged version of MNIST, and load each split of the dataset as NumPy ndarrays." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import os\n", "import requests\n", "import gzip\n", "import six\n", "from six.moves import cPickle\n", "\n", "if not os.path.exists('mnist.pkl.gz'):\n", " r = requests.get('http://www.iro.umontreal.ca/~lisa/deep/data/mnist/mnist.pkl.gz')\n", " with open('mnist.pkl.gz', 'wb') as data_file:\n", " data_file.write(r.content)\n", "\n", "with gzip.open('mnist.pkl.gz', 'rb') as data_file:\n", " if six.PY3:\n", " train_set, valid_set, test_set = cPickle.load(data_file, encoding='latin1')\n", " else:\n", " train_set, valid_set, test_set = cPickle.load(data_file)\n", "\n", "train_set_x, train_set_y = train_set\n", "valid_set_x, valid_set_y = valid_set\n", "test_set_x, test_set_y = test_set" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The model\n", "Logistic regression is a probabilistic, linear classifier. It is parametrized\n", "by a weight matrix $W$ and a bias vector $b$. Classification is\n", "done by projecting an input vector onto a set of hyperplanes, each of which\n", "corresponds to a class. The distance from the input to a hyperplane reflects\n", "the probability that the input is a member of the corresponding class.\n", "\n", "Mathematically, the probability that an input vector $x$ is a member of a\n", "class $i$, a value of a stochastic variable $Y$, can be written as:\n", "\n", "$$P(Y=i|x, W,b) = softmax_i(W x + b) = \\frac {e^{W_i x + b_i}} {\\sum_j e^{W_j x + b_j}}$$\n", "\n", "The model's prediction $y_{pred}$ is the class whose probability is maximal, specifically:\n", "\n", "$$ y_{pred} = {\\rm argmax}_i P(Y=i|x,W,b)$$\n", "\n", "Now, let us define our input variables. First, we need to define the dimension of our tensors:\n", "- `n_in` is the length of each training vector,\n", "- `n_out` is the number of classes.\n", "\n", "Our variables will be:\n", "- `x` is a matrix, where each row contains a different example of the dataset. Its shape is `(batch_size, n_in)`, but `batch_size` does not have to be specified in advance, and can change during training.\n", "- `W` is a shared matrix, of shape `(n_in, n_out)`, initialized with zeros. Column `k` of `W` represents the separation hyperplane for class `k`.\n", "- `b` is a shared vector, of length `n_out`, initialized with zeros. Element `k` of `b` represents the free parameter of hyperplane `k`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy\n", "import theano\n", "from theano import tensor\n", "\n", "# Size of the data\n", "n_in = 28 * 28\n", "# Number of classes\n", "n_out = 10\n", "\n", "x = tensor.matrix('x')\n", "W = theano.shared(value=numpy.zeros((n_in, n_out), dtype=theano.config.floatX),\n", " name='W',\n", " borrow=True)\n", "b = theano.shared(value=numpy.zeros((n_out,), dtype=theano.config.floatX),\n", " name='b',\n", " borrow=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can build a symbolic expression for the matrix of class-membership probability (`p_y_given_x`), and for the class whose probability is maximal (`y_pred`)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true }, "outputs": [], "source": [ "p_y_given_x = tensor.nnet.softmax(tensor.dot(x, W) + b)\n", "y_pred = tensor.argmax(p_y_given_x, axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining a loss function\n", "Learning optimal model parameters involves minimizing a loss function. In the\n", "case of multi-class logistic regression, it is very common to use the negative\n", "log-likelihood as the loss. This is equivalent to maximizing the likelihood of the\n", "data set $\\cal{D}$ under the model parameterized by $\\theta$. Let\n", "us first start by defining the likelihood $\\cal{L}$ and loss\n", "$\\ell$:\n", "\n", "$$\\mathcal{L} (\\theta=\\{W,b\\}, \\mathcal{D}) =\n", " \\sum_{i=0}^{|\\mathcal{D}|} \\log(P(Y=y^{(i)}|x^{(i)}, W,b)) \\\\\n", " \\ell (\\theta=\\{W,b\\}, \\mathcal{D}) = - \\mathcal{L} (\\theta=\\{W,b\\}, \\mathcal{D})\n", "$$\n", "\n", "Again, we will express those expressions using Theano. We have one additional input, the actual target class `y`:\n", "- `y` is an input vector of integers, of length `batch_size` (which will have to match the length of `x` at runtime). The length of `y` can be symbolically expressed by `y.shape[0]`.\n", "- `log_prob` is a `(batch_size, n_out)` matrix containing the log probabilities of class membership for each example.\n", "- `arange(y.shape[0])` is a symbolic vector which will contain `[0,1,2,... batch_size-1]`\n", "- `log_likelihood` is a vector containing the log probability of the target, for each example.\n", "- `loss` is the mean of the negative `log_likelihood` over the examples in the minibatch." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y = tensor.lvector('y')\n", "log_prob = tensor.log(p_y_given_x)\n", "log_likelihood = log_prob[tensor.arange(y.shape[0]), y]\n", "loss = - log_likelihood.mean()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Training procedure\n", "This notebook will use the method of stochastic gradient descent with mini-batches (MSGD) to find values of `W` and `b` that minimize the loss.\n", "\n", "We can let Theano compute symbolic expressions for the gradient of the loss wrt `W` and `b`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "g_W, g_b = theano.grad(cost=loss, wrt=[W, b])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`g_W` and `g_b` are symbolic variables, which can be used as part of a computation graph. In particular, let us define the expressions for one step of gradient descent for `W` and `b`, for a fixed learning rate." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "learning_rate = numpy.float32(0.13)\n", "new_W = W - learning_rate * g_W\n", "new_b = b - learning_rate * g_b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can then define **update expressions**, or pairs of (shared variable, expression for its update), that we will use when compiling the Theano function. The updates will be performed each time the function gets called.\n", "\n", "The following function, `train_model`, returns the loss on the current minibatch, then changes the values of the shared variables according to the update rules. It needs to be passed `x` and `y` as inputs, but not the shared variables, which are implicit inputs.\n", "\n", "The entire learning algorithm thus consists in looping over all examples in the dataset, considering all the examples in one minibatch at a time, and repeatedly calling the `train_model` function." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": true }, "outputs": [], "source": [ "train_model = theano.function(inputs=[x, y],\n", " outputs=loss,\n", " updates=[(W, new_W),\n", " (b, new_b)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing the model\n", "When testing the model, we are interested in the number of misclassified examples (and not only in the likelihood). Here, we build a symbolic expression for retrieving the number of misclassified examples in a minibatch.\n", "\n", "This will also be useful to apply on the validation and testing sets, in order to monitor the progress of the model during training, and to do early stopping." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "misclass_nb = tensor.neq(y_pred, y)\n", "misclass_rate = misclass_nb.mean()\n", "\n", "test_model = theano.function(inputs=[x, y],\n", " outputs=misclass_rate)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Training the model\n", "Here is the main training loop of the algorithm:\n", "- For each *epoch*, or pass through the training set\n", " - split the training set in minibatches, and call `train_model` on each minibatch\n", " - split the validation set in minibatches, and call `test_model` on each minibatch to measure the misclassification rate\n", " - if the misclassification rate has not improved in a while, stop training\n", "- Measure performance on the test set\n", "\n", "The **early stopping procedure** is what decide whether the performance has improved enough. There are many variants, and we will not go into the details of this one here.\n", "\n", "We first need to define a few parameters for the training loop and the early stopping procedure." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "## Define a couple of helper variables and functions for the optimization\n", "batch_size = 500\n", "# compute number of minibatches for training, validation and testing\n", "n_train_batches = train_set_x.shape[0] // batch_size\n", "n_valid_batches = valid_set_x.shape[0] // batch_size\n", "n_test_batches = test_set_x.shape[0] // batch_size\n", "\n", "def get_minibatch(i, dataset_x, dataset_y):\n", " start_idx = i * batch_size\n", " end_idx = (i + 1) * batch_size\n", " batch_x = dataset_x[start_idx:end_idx]\n", " batch_y = dataset_y[start_idx:end_idx]\n", " return (batch_x, batch_y)\n", "\n", "## early-stopping parameters\n", "# maximum number of epochs\n", "n_epochs = 1000\n", "# look as this many examples regardless\n", "patience = 5000\n", "# wait this much longer when a new best is found\n", "patience_increase = 2\n", "# a relative improvement of this much is considered significant\n", "improvement_threshold = 0.995\n", "\n", "# go through this many minibatches before checking the network on the validation set;\n", "# in this case we check every epoch\n", "validation_frequency = min(n_train_batches, patience / 2)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "epoch 1, minibatch 100/100, validation error 11.890000 %\n", " epoch 1, minibatch 100/100, test error of best model 12.170000 %\n", "epoch 2, minibatch 100/100, validation error 10.520000 %\n", " epoch 2, minibatch 100/100, test error of best model 10.940000 %\n", "epoch 3, minibatch 100/100, validation error 9.940000 %\n", " epoch 3, minibatch 100/100, test error of best model 10.210000 %\n", "epoch 4, minibatch 100/100, validation error 9.480000 %\n", " epoch 4, minibatch 100/100, test error of best model 9.820000 %\n", "epoch 5, minibatch 100/100, validation error 9.230000 %\n", " epoch 5, minibatch 100/100, test error of best model 9.510000 %\n", "epoch 6, minibatch 100/100, validation error 9.100000 %\n", " epoch 6, minibatch 100/100, test error of best model 9.280000 %\n", "epoch 7, minibatch 100/100, validation error 8.820000 %\n", " epoch 7, minibatch 100/100, test error of best model 9.100000 %\n", "epoch 8, minibatch 100/100, validation error 8.750000 %\n", " epoch 8, minibatch 100/100, test error of best model 8.890000 %\n", "epoch 9, minibatch 100/100, validation error 8.630000 %\n", " epoch 9, minibatch 100/100, test error of best model 8.780000 %\n", "epoch 10, minibatch 100/100, validation error 8.490000 %\n", " epoch 10, minibatch 100/100, test error of best model 8.620000 %\n", "epoch 11, minibatch 100/100, validation error 8.440000 %\n", " epoch 11, minibatch 100/100, test error of best model 8.570000 %\n", "epoch 12, minibatch 100/100, validation error 8.380000 %\n", " epoch 12, minibatch 100/100, test error of best model 8.530000 %\n", "epoch 13, minibatch 100/100, validation error 8.310000 %\n", " epoch 13, minibatch 100/100, test error of best model 8.480000 %\n", "epoch 14, minibatch 100/100, validation error 8.250000 %\n", " epoch 14, minibatch 100/100, test error of best model 8.400000 %\n", "epoch 15, minibatch 100/100, validation error 8.270000 %\n", "epoch 16, minibatch 100/100, validation error 8.220000 %\n", " epoch 16, minibatch 100/100, test error of best model 8.300000 %\n", "epoch 17, minibatch 100/100, validation error 8.160000 %\n", " epoch 17, minibatch 100/100, test error of best model 8.210000 %\n", "epoch 18, minibatch 100/100, validation error 8.100000 %\n", " epoch 18, minibatch 100/100, test error of best model 8.200000 %\n", "epoch 19, minibatch 100/100, validation error 8.070000 %\n", " epoch 19, minibatch 100/100, test error of best model 8.190000 %\n", "epoch 20, minibatch 100/100, validation error 8.080000 %\n", "epoch 21, minibatch 100/100, validation error 8.030000 %\n", " epoch 21, minibatch 100/100, test error of best model 8.160000 %\n", "epoch 22, minibatch 100/100, validation error 8.020000 %\n", " epoch 22, minibatch 100/100, test error of best model 8.180000 %\n", "epoch 23, minibatch 100/100, validation error 7.980000 %\n", " epoch 23, minibatch 100/100, test error of best model 8.110000 %\n", "epoch 24, minibatch 100/100, validation error 7.970000 %\n", " epoch 24, minibatch 100/100, test error of best model 8.130000 %\n", "epoch 25, minibatch 100/100, validation error 7.950000 %\n", " epoch 25, minibatch 100/100, test error of best model 8.110000 %\n", "epoch 26, minibatch 100/100, validation error 7.940000 %\n", " epoch 26, minibatch 100/100, test error of best model 8.090000 %\n", "epoch 27, minibatch 100/100, validation error 7.890000 %\n", " epoch 27, minibatch 100/100, test error of best model 8.060000 %\n", "epoch 28, minibatch 100/100, validation error 7.880000 %\n", " epoch 28, minibatch 100/100, test error of best model 8.030000 %\n", "epoch 29, minibatch 100/100, validation error 7.830000 %\n", " epoch 29, minibatch 100/100, test error of best model 7.970000 %\n", "epoch 30, minibatch 100/100, validation error 7.800000 %\n", " epoch 30, minibatch 100/100, test error of best model 7.930000 %\n", "epoch 31, minibatch 100/100, validation error 7.780000 %\n", " epoch 31, minibatch 100/100, test error of best model 7.900000 %\n", "epoch 32, minibatch 100/100, validation error 7.760000 %\n", " epoch 32, minibatch 100/100, test error of best model 7.850000 %\n", "epoch 33, minibatch 100/100, validation error 7.750000 %\n", " epoch 33, minibatch 100/100, test error of best model 7.840000 %\n", "epoch 34, minibatch 100/100, validation error 7.760000 %\n", "epoch 35, minibatch 100/100, validation error 7.780000 %\n", "epoch 36, minibatch 100/100, validation error 7.750000 %\n", "epoch 37, minibatch 100/100, validation error 7.730000 %\n", " epoch 37, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 38, minibatch 100/100, validation error 7.720000 %\n", " epoch 38, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 39, minibatch 100/100, validation error 7.700000 %\n", " epoch 39, minibatch 100/100, test error of best model 7.780000 %\n", "epoch 40, minibatch 100/100, validation error 7.690000 %\n", " epoch 40, minibatch 100/100, test error of best model 7.800000 %\n", "epoch 41, minibatch 100/100, validation error 7.670000 %\n", " epoch 41, minibatch 100/100, test error of best model 7.790000 %\n", "epoch 42, minibatch 100/100, validation error 7.690000 %\n", "epoch 43, minibatch 100/100, validation error 7.690000 %\n", "epoch 44, minibatch 100/100, validation error 7.690000 %\n", "epoch 45, minibatch 100/100, validation error 7.680000 %\n", "epoch 46, minibatch 100/100, validation error 7.660000 %\n", " epoch 46, minibatch 100/100, test error of best model 7.820000 %\n", "epoch 47, minibatch 100/100, validation error 7.640000 %\n", " epoch 47, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 48, minibatch 100/100, validation error 7.600000 %\n", " epoch 48, minibatch 100/100, test error of best model 7.790000 %\n", "epoch 49, minibatch 100/100, validation error 7.600000 %\n", "epoch 50, minibatch 100/100, validation error 7.590000 %\n", " epoch 50, minibatch 100/100, test error of best model 7.800000 %\n", "epoch 51, minibatch 100/100, validation error 7.600000 %\n", "epoch 52, minibatch 100/100, validation error 7.580000 %\n", " epoch 52, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 53, minibatch 100/100, validation error 7.570000 %\n", " epoch 53, minibatch 100/100, test error of best model 7.820000 %\n", "epoch 54, minibatch 100/100, validation error 7.560000 %\n", " epoch 54, minibatch 100/100, test error of best model 7.820000 %\n", "epoch 55, minibatch 100/100, validation error 7.550000 %\n", " epoch 55, minibatch 100/100, test error of best model 7.820000 %\n", "epoch 56, minibatch 100/100, validation error 7.520000 %\n", " epoch 56, minibatch 100/100, test error of best model 7.830000 %\n", "epoch 57, minibatch 100/100, validation error 7.490000 %\n", " epoch 57, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 58, minibatch 100/100, validation error 7.480000 %\n", " epoch 58, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 59, minibatch 100/100, validation error 7.450000 %\n", " epoch 59, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 60, minibatch 100/100, validation error 7.450000 %\n", "epoch 61, minibatch 100/100, validation error 7.430000 %\n", " epoch 61, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 62, minibatch 100/100, validation error 7.430000 %\n", "epoch 63, minibatch 100/100, validation error 7.410000 %\n", " epoch 63, minibatch 100/100, test error of best model 7.810000 %\n", "epoch 64, minibatch 100/100, validation error 7.400000 %\n", " epoch 64, minibatch 100/100, test error of best model 7.790000 %\n", "epoch 65, minibatch 100/100, validation error 7.410000 %\n", "epoch 66, minibatch 100/100, validation error 7.410000 %\n", "epoch 67, minibatch 100/100, validation error 7.390000 %\n", " epoch 67, minibatch 100/100, test error of best model 7.770000 %\n", "epoch 68, minibatch 100/100, validation error 7.380000 %\n", " epoch 68, minibatch 100/100, test error of best model 7.770000 %\n", "epoch 69, minibatch 100/100, validation error 7.390000 %\n", "epoch 70, minibatch 100/100, validation error 7.370000 %\n", " epoch 70, minibatch 100/100, test error of best model 7.750000 %\n", "epoch 71, minibatch 100/100, validation error 7.370000 %\n", "epoch 72, minibatch 100/100, validation error 7.380000 %\n", "epoch 73, minibatch 100/100, validation error 7.380000 %\n", "epoch 74, minibatch 100/100, validation error 7.380000 %\n", "epoch 75, minibatch 100/100, validation error 7.350000 %\n", " epoch 75, minibatch 100/100, test error of best model 7.780000 %\n", "epoch 76, minibatch 100/100, validation error 7.330000 %\n", " epoch 76, minibatch 100/100, test error of best model 7.800000 %\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "epoch 77, minibatch 100/100, validation error 7.320000 %\n", " epoch 77, minibatch 100/100, test error of best model 7.780000 %\n", "epoch 78, minibatch 100/100, validation error 7.310000 %\n", " epoch 78, minibatch 100/100, test error of best model 7.780000 %\n", "epoch 79, minibatch 100/100, validation error 7.310000 %\n", "epoch 80, minibatch 100/100, validation error 7.330000 %\n", "epoch 81, minibatch 100/100, validation error 7.300000 %\n", " epoch 81, minibatch 100/100, test error of best model 7.770000 %\n", "epoch 82, minibatch 100/100, validation error 7.290000 %\n", " epoch 82, minibatch 100/100, test error of best model 7.750000 %\n", "epoch 83, minibatch 100/100, validation error 7.290000 %\n", "epoch 84, minibatch 100/100, validation error 7.290000 %\n", "epoch 85, minibatch 100/100, validation error 7.300000 %\n", "epoch 86, minibatch 100/100, validation error 7.280000 %\n", " epoch 86, minibatch 100/100, test error of best model 7.710000 %\n", "epoch 87, minibatch 100/100, validation error 7.280000 %\n", "epoch 88, minibatch 100/100, validation error 7.260000 %\n", " epoch 88, minibatch 100/100, test error of best model 7.690000 %\n", "epoch 89, minibatch 100/100, validation error 7.260000 %\n", "epoch 90, minibatch 100/100, validation error 7.250000 %\n", " epoch 90, minibatch 100/100, test error of best model 7.700000 %\n", "epoch 91, minibatch 100/100, validation error 7.250000 %\n", "epoch 92, minibatch 100/100, validation error 7.230000 %\n", " epoch 92, minibatch 100/100, test error of best model 7.680000 %\n", "epoch 93, minibatch 100/100, validation error 7.230000 %\n", "epoch 94, minibatch 100/100, validation error 7.230000 %\n", "epoch 95, minibatch 100/100, validation error 7.220000 %\n", " epoch 95, minibatch 100/100, test error of best model 7.690000 %\n", "Optimization complete with best validation score of 7.220000 %, with test performance 7.690000 %\n", "The code ran for 96 epochs, with 6.235580 epochs/sec\n" ] } ], "source": [ "import timeit\n", "from six.moves import xrange\n", "\n", "best_validation_loss = numpy.inf\n", "test_score = 0.\n", "start_time = timeit.default_timer()\n", "\n", "done_looping = False\n", "epoch = 0\n", "while (epoch < n_epochs) and (not done_looping):\n", " epoch = epoch + 1\n", " for minibatch_index in xrange(n_train_batches):\n", " minibatch_x, minibatch_y = get_minibatch(minibatch_index, train_set_x, train_set_y)\n", " minibatch_avg_cost = train_model(minibatch_x, minibatch_y)\n", "\n", " # iteration number\n", " iter = (epoch - 1) * n_train_batches + minibatch_index\n", " if (iter + 1) % validation_frequency == 0:\n", " # compute zero-one loss on validation set\n", " validation_losses = []\n", " for i in xrange(n_valid_batches):\n", " valid_xi, valid_yi = get_minibatch(i, valid_set_x, valid_set_y)\n", " validation_losses.append(test_model(valid_xi, valid_yi))\n", " this_validation_loss = numpy.mean(validation_losses)\n", " print('epoch %i, minibatch %i/%i, validation error %f %%' %\n", " (epoch,\n", " minibatch_index + 1,\n", " n_train_batches,\n", " this_validation_loss * 100.))\n", "\n", " # if we got the best validation score until now\n", " if this_validation_loss < best_validation_loss:\n", " # improve patience if loss improvement is good enough\n", " if this_validation_loss < best_validation_loss * improvement_threshold:\n", " patience = max(patience, iter * patience_increase)\n", "\n", " best_validation_loss = this_validation_loss\n", "\n", " # test it on the test set\n", " test_losses = []\n", " for i in xrange(n_test_batches):\n", " test_xi, test_yi = get_minibatch(i, test_set_x, test_set_y)\n", " test_losses.append(test_model(test_xi, test_yi))\n", "\n", " test_score = numpy.mean(test_losses)\n", " print(' epoch %i, minibatch %i/%i, test error of best model %f %%' %\n", " (epoch,\n", " minibatch_index + 1,\n", " n_train_batches,\n", " test_score * 100.))\n", "\n", " # save the best parameters\n", " numpy.savez('best_model.npz', W=W.get_value(), b=b.get_value())\n", "\n", " if patience <= iter:\n", " done_looping = True\n", " break\n", "\n", "end_time = timeit.default_timer()\n", "print('Optimization complete with best validation score of %f %%, '\n", " 'with test performance %f %%' %\n", " (best_validation_loss * 100., test_score * 100.))\n", "\n", "print('The code ran for %d epochs, with %f epochs/sec' %\n", " (epoch, 1. * epoch / (end_time - start_time)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualization\n", "You can visualize the columns of `W`, which correspond to the separation hyperplanes for each class." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.5, 143.5, 56.5, -0.5)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAA2oAAAFuCAYAAADwJes0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3FuMVtd99/Hl+MTBHAYYGBjOMGAwGGzHx9ixc3Diqqnb\nXDSqelFFSi4qtWpVVap6V/Wu6mXVXkaq2ka9aKSqrdqoF81BSeMDGBswGAwDDAwMMAwHYzC24+S9\ne6X33b8vnVWeYdbg7+fyr8V+9l57rbX3YrR/d/3yl78skiRJkqR2fGa6T0CSJEmS9P9yoyZJkiRJ\njXGjJkmSJEmNcaMmSZIkSY1xoyZJkiRJjXGjJkmSJEmNcaMmSZIkSY1xoyZJkiRJjXGjJkmSJEmN\nued2/tjv/d7v/fJ2/p5E/uZv/ibW//Vf//U2n4nU9fLLL8f6n//5n9/mM5kZ7rrrrkm3/eUvfQz1\nwp/92Z/F+l/91V/d5jORuv7gD/4g1v/iL/7iNp/JzEZrq+vorfvTP/3TST24/IuaJEmSJDXGjZok\nSZIkNcaNmiRJkiQ1xo2aJEmSJDXGjZokSZIkNea2pj5+2v385z+P9V/84hexfs893dvzmc/kvTUd\ng36TjpN+U+2rSb0rZWoTm2gsUv3ee++d9LFNmmpH7ZibqmPUHr92rfzwww9jfe7cuZP+TdKr8ey8\nuLO0tJ5r6tx9992xXvse9vHHH0/62LXvfp988kmsp3W05lleSik3btyY9LFLmdrnBf1mC/yLmiRJ\nkiQ1xo2aJEmSJDXGjZokSZIkNcaNmiRJkiQ1xo2aJEmSJDVmxkX81aYbUUrMdCSWzZo1q6p9Su2h\nVB06F0rsoX5MyTeUhkPHqP1N3brahKcalHr3/vvvV53Le++9F+tpXsyfPz+2peuhsdWL6/+0jFta\nQ6YymfCjjz6KbWkMUTJjSj0rpW6Nrk3lpdTHOXPmxHq6Jko9q1mfb1ana0p6MVdmMrr+2nlBYzqh\npD069n333RfrNYl19Ju1479mXfi0rKG9QveI3v/uv//+Tm3BggWxLd1PeubS+L9+/fqk29L1XLp0\nKdZpXUzrfE0qZSl8jrWJ6rfTp3tlliRJkqQGuVGTJEmSpMa4UZMkSZKkxrhRkyRJkqTGzLgwEUIf\nCNLHlzUfDtPHwXTs2g++6Tjpg0r6KJc+vqc6/ebixYs7NepbCpmgj5LTx6dUpw9EqX6noT6n+0kf\nmaePjKk9zQkKAakdWxSmk+bFwoULq36zNkwiHYeOQXWaizT+07m08KHy/6Q2NImuP4XPnDp1Kral\nNZc+Pqf2aW1Zv359bEtjrnZcbNq0KdbTx/p0/69duxbrdC/oXNJxasZnKTMjCOLq1auxnuY5XT89\nW2jdont09uzZTm1iYmLS51dKKWfOnIl1GqMpwIYCHCjsZmBgINbJ2rVrJ318evZTaMRMWBd7oTbA\nh+5dWlso7IjeFSh8hMbc5cuXOzWaEx988EGs032mfrl48WKnRmOLngk0L+hdqTbAbCr4FzVJkiRJ\naowbNUmSJElqjBs1SZIkSWqMGzVJkiRJaowbNUmSJElqzIxLfaRkKqpTwktNeha1pcQiSoNJiTWl\n5KTFUnJqzbx582LbCxcuxDol9pw/f37Sv7lkyZLYltJwqE5pgCmdiBK4KD2IUs9mqtp0R0oyo/Gf\n7jMlStF4Hh0djXW6zzVzlJLW6Ng0nimZNP0m9S2lZ1FKFqVNpbFbm6hYq+b4NG9pbtFcpPGS2qfk\nsFJ4LPb19cU63eehoaFOjVLCSH9/f6zX9hfN0ZrfpDFH/Tg+Pt6pXblyJbal+zbVYzSh9Z/6NiUt\nlpLHHKXYHj9+PNYpDY6OQ/cioTlEyaQ0hlIa6rJly2LblL56s3PZvHlzrC9dujTWZ8+e3anRXBke\nHo51ms+tJ5DS+dUmftcmsyb03KJ3C+pzSuZN10r3mcYcpbXSMzStXTTfqM/p2PRuTf2YnlFTlRDp\nX9QkSZIkqTFu1CRJkiSpMW7UJEmSJKkxbtQkSZIkqTFu1CRJkiSpMTMu9ZFQkg2l6lDCTUJJU3Ts\nlLRVCqfNXLt2LdZTIhIldi1atCjWKfWPzjGl1mzcuDG2XbVqVazX9ldKlaTzpvQcSglrPSWqlDwW\n6bwpDZGSQ2lepP6l+0Pjk86FkqlozG3fvr1To6QlOpcHHnigqv3Y2FinRqmPlDRIqVLz58+P9XQv\naNz2Co2jNM/peugcqZ4S6ErJCV+UHEdrCCU2Uhpiun4at3T9IyMjsV47X1IKH405OpfBwcFYp+tP\n84LS/ej5NB1rKPUhja2jR49Ouj0l6j344IOxTveIUpzTs5jSl2kM0bygZMrTp093as8++2xsSwl8\nlJ5HazGtc2m9pPFMab10j1p6zqffpOuk9Yza0/sPpXtPTEx0avSuSOsfofe8I0eOdGqU4khjjvql\n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\n+MMfxjqdIz1zU0oyrWf0bK195lB/pXtKKdM0b+ncpyOZlPol3YvahFg6NqUeUgJnerekdFOq01r0\njW98I9ZTYmVKKi+Fk3apTs/5NI4oCZjeqylpdP369bFOa1F6h6B5buqjJEmSJN1h3KhJkiRJUmPc\nqEmSJElSY9yoSZIkSVJj3KhJkiRJUmOaTn1MiTiU+kNpWPfdd1+snz59OtZT2g4lE1FKGqXNLFy4\nMNZr0qO+9KUvxbYpIbIUTvhJaWil5FSh0dHR2DYlTZVSysjISKxT8tPs2bM7NUrInJiYiPVbTdXp\nJUpPowTGlHBF10MpabVJkymxisYh3WdKmjx06FDVuaQUJko9o5QoSnKic0/3ghK7KIGs5n6WklNs\npxqtlyk9cd68ebEtrXOUhkeph2l8pblfSk4UK6VuDSklr3OUYkvpjm+99Vas79y5M9YpVW/fvn2d\nGvUhrSH33ntvrNckVlKKKz1D6XlWozatj1Lf6Lm1bt26WE+JcJQoS31LazGNl/RuQX1O6xMlqlLC\nXUp3/sEPflB1DBpDtC48/vjjsZ76i54tdJ3TkfpIx6b3v/SuSOsWJQeOjY3FOqU7jo+Px/rx48c7\nNepbGv/0bvXHf/zHsZ6unxKfX3vttVinNZTeldNzntYEWkPpWUHt6b0gPYvoPt8q/6ImSZIkSY1x\noyZJkiRJjXGjJkmSJEmNcaMmSZIkSY1xoyZJkiRJjWk69TGh5DhK5qGUOErbSYkwlAZFqXeUHkQp\ncZQeldLJBgcHY1tKckrpfjdrn/pr8eLFsW1KjiuFU3IoVSjVqW/PnDkT63Sd04HSg2qS3CgNjMY5\npWrRvUipinQMSqCicX7q1KlYf/HFF2M9JTlRStr8+fNjnZLpKLEtJWJRX1Gd7idJa1evUszoODXr\nJSVtbtiwoapOiX1pzaXxTGslXQ/do7ReUBoarXNbt26NdUqgpOtP50j3ja6HEs4oJS0l8FKiLqV7\n0pzrBZq3K1eujHW6R5TunM6d0ldpzB04cCDWKeFu/fr1ndrevXtjW7oXlDT6/PPPx3pKA9y+fXts\nS89har906dJYf/LJJyd9fHqe0blMR0IurUUkvXNQoiBdP93/lOJYCj+L07lQ+iyNZ3rn+v73vx/r\nTz31VKdGaeqUhkiJonT/a1Js6dlPqZc0/2mMpmui59Ot8i9qkiRJktQYN2qSJEmS1Bg3apIkSZLU\n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\n/tOzlfr2iSeeiHWaRwmtFWkMlcJrzpEjRzo1Wp/peui8KbExPc9pbNWmQVK6OR0n1adqfPoXNUmS\nJElqjBs1SZIkSWqMGzVJkiRJaowbNUmSJElqTNNhIqn+i1/8IralD/upPX3cmFCwAX1MTuED9IH4\nmTNnYj0Fezz11FOxLX3YTx9r0gfFKTiEPtQn1Of0sXo6R2r78ccfV53LVKIAB/oona4pfZRKoSFX\nrlyJdfoone7z/v37OzUKzVm5cmWsr127NtbpQ3CaLxMTE50aBVLQh719fX2xTh9Opw/BaZzTeKb6\nvffeG+tpPasJTPrfoDGX1kv6yJo++K899xTKQOEINIdOnjwZ60NDQ7GePj6n36QP/mk8U5gUjd3d\nu3d3atTnZNOmTbFOz5w05lavXh3b0lyZyjFKx6a5SCFLGzZsiPWRkZFOjeYtjYsDBw7EOp17ehbT\n+waFINH6T8dJwWPr1q2LbdN6Wwqvz4cOHYp1ChkaHBzs1B544IHYltYnep+ZDjReqL8SWivo3ZIC\nMug5l86xNqSPwsQoZCS9i1HwDqHAG3rnSGsxvftTndZnerektZXG7lTwL2qSJEmS1Bg3apIkSZLU\nGDdqkiRJktQYN2qSJEmS1Bg3apIkSZLUmCZSHynhK6XTUDILpdtR2hAl3KW0IUqDqk3JO3XqVKyn\nZLJSclIQpf5RShL1LaWkJZR6RMeePXt2rFNKTjp+bdIeHfuee6ZuiFN6EqXEUT2lalFbShSkfqHU\nz5QI9eSTT8a2NIcoVSolrZXCiY1p/FPqGY1zmqOU2JQSu2ge1ibN0jnSvesFuhd0LumalixZMum2\nN/PII4/Eejo+Jd7Sek7HpnuXkhxpbqW0ulJ4PFNiGT2jUiLajh07YlsaK5SqduLEiVh/8MEHO7VZ\ns2bFtlO5VhK6FytWrKhqv2XLllhP86J2fv7mb/5m1bmk+09jhebWxo0bY53SfdMaTYmitel2n/3s\nZ2Od7lF65lCK4UxIfaS1tRfvLZRuSu8WlHqb+pHWUEo9pPFP7VNKLF1PTeJzKZzuevjw4U6N+pbW\nOUpapXOksUjjYir4FzVJkiRJaowbNUmSJElqjBs1SZIkSWqMGzVJkiRJaowbNUmSJElqTBOpjySl\nqlDSCqV+UdLchQsXYj0lxVBaIyXzUPINJS1S2sxPf/rTTo0SmCjhhvpr3759sZ7SmSjFkeqUKnX9\n+vVJ16lP6Hqo/XSgc6GUwIMHD3ZqlNi1du3aWKfxT+MipeetW7cutqXUR7oeSkOj9KTLly93auPj\n47EtJdNuTWq4AAAgAElEQVTRsWmMpv6i1CtKJqN7VJuSOJVoLKZrpeunPqeUOErsSom11LeU4kn3\nOSWQlVJKX19fp0ZjIqXs3uw307FLySmupZSyfPnyTo2eLbWJZZs2bYr1lLRJaXCU4juV6Dfp/q9Z\nsybWqb8SWs9o/Xv33XcnfexSShkdHe3Utm3bFtueO3eu6tjUPq1FdJ2rV6+O9YsXL8Y69S09c9J8\noXlO72e0ht7OpL3/SU2iKI1zes5RWnNN0iilklOiMiVz0j1K51j7DKV3xTSHSinl2LFjndq8efNi\nW0Kp7DUJ2VSntrRXmCz/oiZJkiRJjXGjJkmSJEmNcaMmSZIkSY1xoyZJkiRJjXGjJkmSJEmNaTr1\nMaH0GErDoSQvSmFJyUeUKJYSaErJ6Wal8LnT8VM62cjISGy7bNmyWK9JZiol99fChQtjW0rsqb1H\nKeGJzm+qUnX+NyiZilLV6BwHBgY6NeqrmnS7Ujj5KfXj+fPnY1tKsaOxtWjRolifmJiY9LlQMt/g\n4GCsU/ITpcelZFYaW5TYVZuSl44/1eOWUh9T/65YsSK2pXRLmqM0L9K6QMegpMn3338/1mktSudO\naaWUVknnQtdZ82yhpDE6F1qLU7pjKfk+T8daSehcKD2O1sXTp09P+vi0htK7AiXzpbTaUvJ42bp1\na2xL6XYplbcUfrak+79r167Y9siRI7H+2GOPxTql5FE/pj6ntFKaz7QW05ybjsTSmvcWWnMoOZfu\nM71bpeNTn9SmctO5p+c5PStqnv2l8LtISjKl86O1ld4tatfF25nu7F/UJEmSJKkxbtQkSZIkqTFu\n1CRJkiSpMW7UJEmSJKkxbtQkSZIkqTEzLvWRklYuXboU65R8Q2lwq1ev7tQoDebRRx+NdUo4ooSb\n4eHhSR8npdWVwqlfS5cujXVK55k1a1anRslEtddJ9yK1p/SkmYCun5KcKBEsoftJaXB0j1IiEo0J\nSk9KY6UUTpXauHFjrKfrP378eGx79erVWCd0L1KqHs3z2gRSMh1pe/SbaSymRK1SeG2hpM2aNKya\n8yuF08Consb/uXPnYltKSKVxTsmM1C9DQ0OxnlAaHiWWUZ+nRDTq8+lI1KVETerz2jTQlHxYm0xH\n53LixIlYT8ensXL06NFY//GPfxzrDz30UKyne0TvON/85jcnfYxSSrlw4UKs0zxKx6lJgr1Z++lI\ndyQ17y2UqEjXQ/eCUnxTf1Ef1p4LvXOm9ZLOj+bW+vXrY53W0JS0Sr9J10PvRLS21Ly3TtVa2c6o\nlyRJkiSVUtyoSZIkSVJz3KhJkiRJUmPcqEmSJElSY9yoSZIkSVJjZlzqI6GkOUpsoaSYlHBICTSr\nVq2KdUrJofRESv1LqTqUzEaJXZSSR2lLKVWHErUIpafVJjzNVHSdlCqU0rmoLdWvXLkS65RwNmfO\nnE6N7hvVaxO4KMkvpTCl87sZ6nNK4aL2NWpTH6cDnWO6fkqsGh8fj3VKg6P1Mq2LlKhH923x4sWx\nTmmIKfWQzq82DYzWf7qmVE/nVwqnuNam56V+aWm9pb4dGBiIdbp3lKqYEktHRkZi27fffjvWaW1N\nz8pS8tq1Z8+e2Jae55///OdjncZ5muc0hvbu3RvrlJy9bNmyWKdzpzGdzIQ1tEZt4jP1YV9fX6zT\nPU3vv/TcphRfGlv0DpnWkdp3XOoX+s3Uv5Q+SsegtYL6azrScP9//kVNkiRJkhrjRk2SJEmSGuNG\nTZIkSZIa40ZNkiRJkhpzx4SJEPoQ8MaNG7GePsqkDyQvXrxY9Zv0ETd9OH/vvfd2avTxNR2D6jXn\nWBvUQh9Z0vXfaR8UE7oX6eNr6sPr169X/SYdJ31oS22pToE8dJ00jtK4oGOQXoytT8s4LCX3b69C\nJihMIIWS1H6oTesfnXtao+g3a9czOg6FL6WwhtoP0mvHearfzo/g/7foOinwgEIJFi1a1Kk99NBD\nsW3t+lezRtWOW7qemnAgQsfu7++Pdbp+eof6NK2jk0X3mQKM6N2Snv8pZIRCjeg36b7RcVJozty5\nc2Nbun4K9qDxnNrTMWh80rxtKWTp/+df1CRJkiSpMW7UJEmSJKkxbtQkSZIkqTFu1CRJkiSpMW7U\nJEmSJKkxn9rUx9pkxoTSc3rlo48+6tRqE7tqr78G9VVtetanRS8SCHuVTJRSlWoT+EgvxtZ0/Oan\nXa/6kFK1ejH/a9NAPy0+7eOfEmip3gsplXmq0RyaynOZyneIOw31CSUeU53G7ZUrV2J9bGysU6MU\n29rUW2qfxlxtyvh0vIfOxHHrX9QkSZIkqTFu1CRJkiSpMW7UJEmSJKkxbtQkSZIkqTFu1CRJkiSp\n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\nderH9BE/fUxO5zJdoQwJnSN9lJuCAyjAgkID6GNlCplJ9RdffDG2PXjwYKzv378/1unD+Z07d8Z6\n+qCe5sSpU6difc2aNbFOfZ4+1qc+pwAf+kCYgjBSEMJUj1u6/ynEgdYWmosU+ENSUBOFvdCx6Xoo\nICGFEtD9pMADGou0zi9atCjWUz/SsWs/7Kf1n9aiVlCYCK1nhPoxrZc//elPY9tnnnkm1p9++ulY\np3UuBSrQ9Rw9ejTW6Xp2794d67/1W7816ba0PlEQwuuvvx7rNOfSOwTNZ3pvo9CYqURjsQb1LYWj\nUCDN2rVrY53WrjReKJCIjkH3v2Yu0rpF10/PFnoupmCbJUuWVJ3L5cuXYz0FlZTCQVWpX2qDTSbL\nv6hJkiRJUmPcqEmSJElSY9yoSZIkSVJj3KhJkiRJUmPcqEmSJElSY5pOfUypKpRMQ0kulO5IqUIp\nhYzScyjhhdIgKfmGzj1dK6Xe0blQelQN6nNKrJqYmIh16sea35wJaGzRuJg3b16nRklGNJ7ffPPN\nWKeUvHR8SuA7d+5crFPCFSVZpTS0UvK8qE23pDolk6b0qCtXrsS2NLcogY/SpqYjmZT6Jc0vSj2j\nREEa53ScdE9pfJJLly7FOh1n+/btk25L6zCtc6tXr451WrtSneYQpTWm5MxS6hLrWkrIJbQWUTIp\npQeePHmyU6MEQrqflAb3xS9+MdZT0vK2bdti2+9+97uxTmj9T4nKND8prZSeTzTn0nOrlJwquHXr\n1tg2JUSWwnORkll7geYF1dMcpWRvevehY9O9oHUhPaPpOUxjiN7b6BzTvaD709/fX1WnZ+iWLVs6\nNXo+Ufo4pZVSe3rO9/X1dWq09tP1TNbMfRuWJEmSpDuUGzVJkiRJaowbNUmSJElqjBs1SZIkSWqM\nGzVJkiRJakzTqY8JpapQktOiRYtindIQUzoLpcFRGg79JiW/UKpUSk+i9CBKvaJzp+tPaUuU5EPH\noHS/mnTLmYyS2WoS62g8r127NtYpJXJkZCTWjx8/3qlRStSDDz4Y65QqRudC6ZFr1qzp1GgMDQwM\nxDqNIUqESqlylChHSVtUv9WEp16qOXdaK+g+X7x4saqe0jPpPlN6Go0tSvJK7UdHR2Pb8fHxWK9d\nz2bPnh3rqR9p3aY+p36pSaZrCZ033U9Kg0xrSCmlfPWrX+3UKMWTnq2f/exnY/3tt9+O9bS20v2k\nZ8Vrr70W6xs3boz1hMYhpU9TiiOt/9SP69ev79ToXYGeidRfLSWWpmRGStmtTd+mpME0tkrJ95oS\nwg8cOBDrNOcGBwdjPa1/Dz/8cGxLKGmS+mvFihWd2t69e2Pb8+fPxzqt/5TAS2t0MlXP/jvrDVmS\nJEmS7gBu1CRJkiSpMW7UJEmSJKkxbtQkSZIkqTFu1CRJkiSpMU2nPtYk/FBKHqVEpUTFUnJ6DiXH\nURoiJRnRb1KqzPLlyzs1SrKh9CiqU8JTShWkNLSaFMNSOD0t3We69y2lPhE6R0rbSglXKVGqFE49\nXLx4cVX9ySef7NQoOZHSGqlemzSXxj/1ISWkUn+lpMFSSpkzZ06nRqlPNIdo/s+EMZrOncYnpYFR\n6iGtuU888cSkf5PS0+jYS5cujfULFy50anSfCa3bdO7pN0vJacA0hij1jn6T5lya03fddVdsOx3o\nvKlfaC4uW7Ys1r/5zW92apSc9y//8i+xTveC1taxsbFO7cSJE7EtJSdSet73v//9WE9j7mtf+1ps\nS+OfxgWtof39/bGe1lZ6b6H3tprxfLP2UymNRVq3aNzSuxI95w4dOhTrzzzzzKR/k9JN6T5Tn6fn\nHCX+0nXSekZj8ciRI5OqlVKf7ktp7VRP10Tj8FbfCfyLmiRJkiQ1xo2aJEmSJDXGjZokSZIkNcaN\nmiRJkiQ1xo2aJEmSJDWm6dTHhFJV5s6dG+uUNkP1lGRICTQp3akUTgmjRKAbN27EekpyomNT2k5f\nX1+snz59OtbTNVFKEJ0L9S2lZ9JxEroXMzVpr5RSPvnkk06NkuMoJYmSQxcsWBDrKZnrgQceiG0p\n3WtwcDDWaY7WpApSiiNdJ81/mlspEas2gW86ksZq0Tmm/qW+orWC1hxKD9u7d2+nRmOLxj+toZRM\nltLm0tgvJSf+lsLptpRkR+tcSsmk66kdW9S+pYTHGtQv9KyoSbijtEZaQ86cORPrtF4MDw93apS0\n+OUvfznW6X7SWnTgwIFOjeYEpeTRuP3KV74S6ykhupT8zKFxSGsOmY41Nz2fS8ljkdYKSs6lezQy\nMhLrNI7SmKN1e9OmTbG+atWqWKf1cuXKlZ3awYMHY1ty7NixWKc+T4mNlIRJ6znVaW2hd2i6dwld\nz2T5FzVJkiRJaowbNUmSJElqjBs1SZIkSWqMGzVJkiRJaowbNUmSJElqzIxLfSSUwEQohSglP1GK\nGSUQUQLV9evXY53S9tI1UYofJbClpLFS6hJu6PrpGJQeRUleNWZCumOtdE00hmqTCScmJiZ9HrWJ\nmjQuKPWTjpMSkej6ac5RqlJtPZnJY47OPfUjja1ly5bF+vLly2P96tWrsX7u3LlO7eTJk7HtokWL\nYj2lfpVSyv333x/r6dzffffd2JbSACn1luYcJZmlhMuBgYHYlu4b1WnOpVS9mTCe6VlJz7MTJ07E\nelrTaN362te+NuljlFLK2bNnY/1LX/pSp0YJcfSsfOONN2KdEvh27NjRqdGc+I3f+I1Yr03Upvbp\nmUNtac2ZCYm66dzpvGmsUIojpd7S3E3PS3r2p3W4FB7n9KxMv0ljhRLSafxTPSVHj46OxraUbkl1\nSnck6fprkiBr+Bc1SZIkSWqMGzVJkiRJaowbNUmSJElqjBs1SZIkSWrMjAsT6dXHejUft9JvLlmy\nJNYpCCF9CFkKfyCaPhymQA76yJg+Mqf26fh0PVSnkBH6iJs+Yv00qw0TSKEBtWrvQ+1v9uID8dog\nhJkQnDAd0rpQ+zE1zfMPPvgg1hcuXNip0cf0ZOXKlbFO62L6KH9wcDC2vXTpUqzXhiNRiEPqF3om\n0FyhOUpr60wd/3TeFCZTE5pFz0R6ztc+c9O6WBuk9dhjj8U6nXsaWzS36LlN118bBJLqNeFNM0Ua\noxSCQ/eN7gWFctC8OH/+fKdGawvVKZCOrml4eLhTe+GFF2Jbuh6qU1DTtm3bOrUUpFMKh/RRUBWt\n2zVBhVM1zv2LmiRJkiQ1xo2aJEmSJDXGjZokSZIkNcaNmiRJkiQ1xo2aJEmSJDVmxqU+1qKUHEpn\nmcp0IkqJIinhixKYZs+eHet0/ZQ2VJNwQwlkpjjefr1Id6s9Ri9SHNWO2vtJaYg0/xcsWNCpzZ8/\nv+pcapPpUpIXrfGUBklovtSmCibUh7XPszttLa59bvciDZfUjMWZcB9makJoS2rTDfv7+2Od3sNo\nzKV67XpOv5nW7VLy3KK1r3ZNpHmbjlP7zk7jvOU56l/UJEmSJKkxbtQkSZIkqTFu1CRJkiSpMW7U\nJEmSJKkxbtQkSZIkqTF3mfQjSZIkSW3xL2qSJEmS1Bg3apIkSZLUGDdqkiRJktQYN2qSJEmS1Bg3\napIkSZLUGDdqkiRJktQYN2qSJEmS1Bg3apIkSZLUGDdqkiRJktQYN2qSJEmS1Bg3apIkSZLUGDdq\nkiRJktQYN2qSJEmS1Bg3apIkSZLUGDdqkiRJktQYN2qSJEmS1Bg3apIkSZLUGDdqkiRJktQYN2qS\nJEmS1Bg3apIkSZLUGDdqkiRJktQYN2qSJEmS1Bg3apIkSZLUmP8D7g4uAFHrQq4AAAAASUVORK5C\nYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "from utils import tile_raster_images\n", "\n", "plt.clf()\n", "\n", "# Increase the size of the figure\n", "plt.gcf().set_size_inches(15, 10)\n", "\n", "plot_data = tile_raster_images(W.get_value(borrow=True).T,\n", " img_shape=(28, 28), tile_shape=(2, 5), tile_spacing=(1, 1))\n", "plt.imshow(plot_data, cmap='Greys', interpolation='none')\n", "plt.axis('off')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 1 }