├── .gitignore ├── README.md ├── notebooks ├── .gitignore ├── SpyTorchTutorial1.ipynb ├── SpyTorchTutorial2.ipynb ├── SpyTorchTutorial3.ipynb ├── SpyTorchTutorial4.ipynb ├── figures │ ├── .gitignore │ ├── mlp_sketch │ │ ├── Makefile │ │ ├── mlp_sketch.png │ │ └── mlp_sketch.tex │ ├── snn_graph │ │ ├── Makefile │ │ ├── snn_graph.png │ │ └── snn_graph.tex │ └── surrgrad │ │ ├── Makefile │ │ ├── surrgrad.gnu │ │ └── surrgrad.png └── utils.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Python 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpyTorch 2 | A tutorial on surrogate gradient learning in spiking neural networks 3 | 4 | Version: 0.4 5 | 6 | [![DOI](https://zenodo.org/badge/170391179.svg)](https://zenodo.org/badge/latestdoi/170391179) 7 | 8 | This repository contains tutorial files to get you started with the basic ideas 9 | of surrogate gradient learning in spiking neural networks using PyTorch. 10 | 11 | Feedback and contributions are welcome. 12 | 13 | For more information on surrogate gradient learning please refer to: 14 | > Neftci, E.O., Mostafa, H., and Zenke, F. (2019). Surrogate Gradient Learning in Spiking Neural Networks: Bringing the Power of Gradient-based optimization to spiking neural networks. IEEE Signal Processing Magazine 36, 51–63. 15 | > https://ieeexplore.ieee.org/document/8891809 16 | > preprint: https://arxiv.org/abs/1901.09948 17 | 18 | 19 | Also see https://github.com/surrogate-gradient-learning 20 | 21 | ## Copyright and license 22 | 23 | Copyright 2019-2020 Friedemann Zenke, https://fzenke.net 24 | 25 | This work is licensed under a Creative Commons Attribution 4.0 International License. 26 | http://creativecommons.org/licenses/by/4.0/ 27 | -------------------------------------------------------------------------------- /notebooks/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | 117 | -------------------------------------------------------------------------------- /notebooks/SpyTorchTutorial3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Tutorial 3: Training a spiking neural network on a simple vision dataset\n", 8 | "\n", 9 | "Friedemann Zenke (https://fzenke.net)" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "> For more details on surrogate gradient learning, please see: \n", 17 | "> Neftci, E.O., Mostafa, H., and Zenke, F. (2019). Surrogate Gradient Learning in Spiking Neural Networks.\n", 18 | "> https://arxiv.org/abs/1901.09948" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "In Tutorial 2, we have seen how to train a simple multi-layer spiking neural network on the [Fashion MNIST dataset](https://github.com/zalandoresearch/fashion-mnist). However, the spiking activity in the hidden layer was not particularly plausible in a biological sense. Here we modify the network from this previous tutorial by adding activity regularizer, which encourages solutions with sparse spiking." 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 1, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "import os\n", 35 | "\n", 36 | "import numpy as np\n", 37 | "import matplotlib.pyplot as plt\n", 38 | "from matplotlib.gridspec import GridSpec\n", 39 | "import seaborn as sns\n", 40 | "\n", 41 | "import torch\n", 42 | "import torch.nn as nn\n", 43 | "import torchvision" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# The coarse network structure is dicated by the Fashion MNIST dataset. \n", 53 | "nb_inputs = 28*28\n", 54 | "nb_hidden = 100\n", 55 | "nb_outputs = 10\n", 56 | "\n", 57 | "time_step = 1e-3\n", 58 | "nb_steps = 100\n", 59 | "\n", 60 | "batch_size = 256" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 3, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "dtype = torch.float\n", 70 | "\n", 71 | "# Check whether a GPU is available\n", 72 | "if torch.cuda.is_available():\n", 73 | " device = torch.device(\"cuda\") \n", 74 | "else:\n", 75 | " device = torch.device(\"cpu\")" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 4, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "# Here we load the Dataset\n", 85 | "root = os.path.expanduser(\"~/data/datasets/torch/fashion-mnist\")\n", 86 | "train_dataset = torchvision.datasets.FashionMNIST(root, train=True, transform=None, target_transform=None, download=True)\n", 87 | "test_dataset = torchvision.datasets.FashionMNIST(root, train=False, transform=None, target_transform=None, download=True)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 5, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "# Standardize data\n", 97 | "# x_train = torch.tensor(train_dataset.train_data, device=device, dtype=dtype)\n", 98 | "x_train = np.array(train_dataset.data, dtype=np.float)\n", 99 | "x_train = x_train.reshape(x_train.shape[0],-1)/255\n", 100 | "# x_test = torch.tensor(test_dataset.test_data, device=device, dtype=dtype)\n", 101 | "x_test = np.array(test_dataset.data, dtype=np.float)\n", 102 | "x_test = x_test.reshape(x_test.shape[0],-1)/255\n", 103 | "\n", 104 | "# y_train = torch.tensor(train_dataset.train_labels, device=device, dtype=dtype)\n", 105 | "# y_test = torch.tensor(test_dataset.test_labels, device=device, dtype=dtype)\n", 106 | "y_train = np.array(train_dataset.targets, dtype=np.int)\n", 107 | "y_test = np.array(test_dataset.targets, dtype=np.int)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 6, 113 | "metadata": {}, 114 | "outputs": [ 115 | { 116 | "data": { 117 | "text/plain": [ 118 | "(-0.5, 27.5, 27.5, -0.5)" 119 | ] 120 | }, 121 | "execution_count": 6, 122 | "metadata": {}, 123 | "output_type": "execute_result" 124 | }, 125 | { 126 | "data": { 127 | "image/png": "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\n", 128 | "text/plain": [ 129 | "
" 130 | ] 131 | }, 132 | "metadata": { 133 | "needs_background": "light" 134 | }, 135 | "output_type": "display_data" 136 | } 137 | ], 138 | "source": [ 139 | "# Here we plot one of the raw data points as an example\n", 140 | "data_id = 1\n", 141 | "plt.imshow(x_train[data_id].reshape(28,28), cmap=plt.cm.gray_r)\n", 142 | "plt.axis(\"off\")" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "Since we are working with spiking neural networks, we ideally want to use a temporal code to make use of spike timing. To that end, we will use a spike latency code to feed spikes to our network." 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 7, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "def current2firing_time(x, tau=20, thr=0.2, tmax=1.0, epsilon=1e-7):\n", 159 | " \"\"\" Computes first firing time latency for a current input x assuming the charge time of a current based LIF neuron.\n", 160 | "\n", 161 | " Args:\n", 162 | " x -- The \"current\" values\n", 163 | "\n", 164 | " Keyword args:\n", 165 | " tau -- The membrane time constant of the LIF neuron to be charged\n", 166 | " thr -- The firing threshold value \n", 167 | " tmax -- The maximum time returned \n", 168 | " epsilon -- A generic (small) epsilon > 0\n", 169 | "\n", 170 | " Returns:\n", 171 | " Time to first spike for each \"current\" x\n", 172 | " \"\"\"\n", 173 | " idx = x0.0] = spike_height\n", 282 | " dat = dat.detach().cpu().numpy()\n", 283 | " else:\n", 284 | " dat = mem.detach().cpu().numpy()\n", 285 | " for i in range(np.prod(dim)):\n", 286 | " if i==0: a0=ax=plt.subplot(gs[i])\n", 287 | " else: ax=plt.subplot(gs[i],sharey=a0)\n", 288 | " ax.plot(dat[i])\n", 289 | " ax.axis(\"off\")" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "## Training the network" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": 11, 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [ 305 | "class SurrGradSpike(torch.autograd.Function):\n", 306 | " \"\"\"\n", 307 | " Here we implement our spiking nonlinearity which also implements \n", 308 | " the surrogate gradient. By subclassing torch.autograd.Function, \n", 309 | " we will be able to use all of PyTorch's autograd functionality.\n", 310 | " Here we use the normalized negative part of a fast sigmoid \n", 311 | " as this was done in Zenke & Ganguli (2018).\n", 312 | " \"\"\"\n", 313 | " \n", 314 | " scale = 100.0 # controls steepness of surrogate gradient\n", 315 | "\n", 316 | " @staticmethod\n", 317 | " def forward(ctx, input):\n", 318 | " \"\"\"\n", 319 | " In the forward pass we compute a step function of the input Tensor\n", 320 | " and return it. ctx is a context object that we use to stash information which \n", 321 | " we need to later backpropagate our error signals. To achieve this we use the \n", 322 | " ctx.save_for_backward method.\n", 323 | " \"\"\"\n", 324 | " ctx.save_for_backward(input)\n", 325 | " out = torch.zeros_like(input)\n", 326 | " out[input > 0] = 1.0\n", 327 | " return out\n", 328 | "\n", 329 | " @staticmethod\n", 330 | " def backward(ctx, grad_output):\n", 331 | " \"\"\"\n", 332 | " In the backward pass we receive a Tensor we need to compute the \n", 333 | " surrogate gradient of the loss with respect to the input. \n", 334 | " Here we use the normalized negative part of a fast sigmoid \n", 335 | " as this was done in Zenke & Ganguli (2018).\n", 336 | " \"\"\"\n", 337 | " input, = ctx.saved_tensors\n", 338 | " grad_input = grad_output.clone()\n", 339 | " grad = grad_input/(SurrGradSpike.scale*torch.abs(input)+1.0)**2\n", 340 | " return grad\n", 341 | " \n", 342 | "# here we overwrite our naive spike function by the \"SurrGradSpike\" nonlinearity which implements a surrogate gradient\n", 343 | "spike_fn = SurrGradSpike.apply" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 12, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "def run_snn(inputs):\n", 353 | " h1 = torch.einsum(\"abc,cd->abd\", (inputs, w1))\n", 354 | " syn = torch.zeros((batch_size,nb_hidden), device=device, dtype=dtype)\n", 355 | " mem = torch.zeros((batch_size,nb_hidden), device=device, dtype=dtype)\n", 356 | "\n", 357 | " mem_rec = []\n", 358 | " spk_rec = []\n", 359 | "\n", 360 | " # Compute hidden layer activity\n", 361 | " for t in range(nb_steps):\n", 362 | " mthr = mem-1.0\n", 363 | " out = spike_fn(mthr)\n", 364 | " rst = out.detach() # We do not want to backprop through the reset\n", 365 | "\n", 366 | " new_syn = alpha*syn +h1[:,t]\n", 367 | " new_mem = (beta*mem +syn)*(1.0-rst)\n", 368 | "\n", 369 | " mem_rec.append(mem)\n", 370 | " spk_rec.append(out)\n", 371 | " \n", 372 | " mem = new_mem\n", 373 | " syn = new_syn\n", 374 | "\n", 375 | " mem_rec = torch.stack(mem_rec,dim=1)\n", 376 | " spk_rec = torch.stack(spk_rec,dim=1)\n", 377 | "\n", 378 | " # Readout layer\n", 379 | " h2= torch.einsum(\"abc,cd->abd\", (spk_rec, w2))\n", 380 | " flt = torch.zeros((batch_size,nb_outputs), device=device, dtype=dtype)\n", 381 | " out = torch.zeros((batch_size,nb_outputs), device=device, dtype=dtype)\n", 382 | " out_rec = [out]\n", 383 | " for t in range(nb_steps):\n", 384 | " new_flt = alpha*flt +h2[:,t]\n", 385 | " new_out = beta*out +flt\n", 386 | "\n", 387 | " flt = new_flt\n", 388 | " out = new_out\n", 389 | "\n", 390 | " out_rec.append(out)\n", 391 | "\n", 392 | " out_rec = torch.stack(out_rec,dim=1)\n", 393 | " other_recs = [mem_rec, spk_rec]\n", 394 | " return out_rec, other_recs" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 13, 400 | "metadata": {}, 401 | "outputs": [], 402 | "source": [ 403 | "def train(x_data, y_data, lr=1e-3, nb_epochs=10):\n", 404 | " params = [w1,w2]\n", 405 | " optimizer = torch.optim.Adamax(params, lr=lr, betas=(0.9,0.999))\n", 406 | "\n", 407 | " log_softmax_fn = nn.LogSoftmax(dim=1)\n", 408 | " loss_fn = nn.NLLLoss()\n", 409 | " \n", 410 | " loss_hist = []\n", 411 | " for e in range(nb_epochs):\n", 412 | " local_loss = []\n", 413 | " for x_local, y_local in sparse_data_generator(x_data, y_data, batch_size, nb_steps, nb_inputs):\n", 414 | " output,recs = run_snn(x_local.to_dense())\n", 415 | " _,spks=recs\n", 416 | " m,_=torch.max(output,1)\n", 417 | " log_p_y = log_softmax_fn(m)\n", 418 | " \n", 419 | " # Here we set up our regularizer loss\n", 420 | " # The strength paramters here are merely a guess and there should be ample room for improvement by\n", 421 | " # tuning these paramters.\n", 422 | " reg_loss = 1e-5*torch.sum(spks) # L1 loss on total number of spikes\n", 423 | " reg_loss += 1e-5*torch.mean(torch.sum(torch.sum(spks,dim=0),dim=0)**2) # L2 loss on spikes per neuron\n", 424 | " \n", 425 | " # Here we combine supervised loss and the regularizer\n", 426 | " loss_val = loss_fn(log_p_y, y_local) + reg_loss\n", 427 | "\n", 428 | " optimizer.zero_grad()\n", 429 | " loss_val.backward()\n", 430 | " optimizer.step()\n", 431 | " local_loss.append(loss_val.item())\n", 432 | " mean_loss = np.mean(local_loss)\n", 433 | " print(\"Epoch %i: loss=%.5f\"%(e+1,mean_loss))\n", 434 | " loss_hist.append(mean_loss)\n", 435 | " \n", 436 | " return loss_hist\n", 437 | " \n", 438 | " \n", 439 | "def compute_classification_accuracy(x_data, y_data):\n", 440 | " \"\"\" Computes classification accuracy on supplied data in batches. \"\"\"\n", 441 | " accs = []\n", 442 | " for x_local, y_local in sparse_data_generator(x_data, y_data, batch_size, nb_steps, nb_inputs, shuffle=False):\n", 443 | " output,_ = run_snn(x_local.to_dense())\n", 444 | " m,_= torch.max(output,1) # max over time\n", 445 | " _,am=torch.max(m,1) # argmax over output units\n", 446 | " tmp = np.mean((y_local==am).detach().cpu().numpy()) # compare to labels\n", 447 | " accs.append(tmp)\n", 448 | " return np.mean(accs)" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 14, 454 | "metadata": {}, 455 | "outputs": [ 456 | { 457 | "name": "stdout", 458 | "output_type": "stream", 459 | "text": [ 460 | "Epoch 1: loss=2.10319\n", 461 | "Epoch 2: loss=1.64978\n", 462 | "Epoch 3: loss=1.30702\n", 463 | "Epoch 4: loss=1.08484\n", 464 | "Epoch 5: loss=0.95575\n", 465 | "Epoch 6: loss=0.87375\n", 466 | "Epoch 7: loss=0.81585\n", 467 | "Epoch 8: loss=0.77363\n", 468 | "Epoch 9: loss=0.73897\n", 469 | "Epoch 10: loss=0.70913\n", 470 | "Epoch 11: loss=0.68416\n", 471 | "Epoch 12: loss=0.66362\n", 472 | "Epoch 13: loss=0.64633\n", 473 | "Epoch 14: loss=0.63024\n", 474 | "Epoch 15: loss=0.61729\n", 475 | "Epoch 16: loss=0.60596\n", 476 | "Epoch 17: loss=0.59438\n", 477 | "Epoch 18: loss=0.58355\n", 478 | "Epoch 19: loss=0.57630\n", 479 | "Epoch 20: loss=0.56730\n", 480 | "Epoch 21: loss=0.56177\n", 481 | "Epoch 22: loss=0.55338\n", 482 | "Epoch 23: loss=0.54661\n", 483 | "Epoch 24: loss=0.54120\n", 484 | "Epoch 25: loss=0.53651\n", 485 | "Epoch 26: loss=0.53226\n", 486 | "Epoch 27: loss=0.52634\n", 487 | "Epoch 28: loss=0.52089\n", 488 | "Epoch 29: loss=0.51909\n", 489 | "Epoch 30: loss=0.51457\n" 490 | ] 491 | } 492 | ], 493 | "source": [ 494 | "loss_hist = train(x_train, y_train, lr=2e-4, nb_epochs=30)" 495 | ] 496 | }, 497 | { 498 | "cell_type": "code", 499 | "execution_count": 15, 500 | "metadata": {}, 501 | "outputs": [ 502 | { 503 | "data": { 504 | "image/png": "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\n", 505 | "text/plain": [ 506 | "
" 507 | ] 508 | }, 509 | "metadata": { 510 | "needs_background": "light" 511 | }, 512 | "output_type": "display_data" 513 | } 514 | ], 515 | "source": [ 516 | "plt.figure(figsize=(3.3,2),dpi=150)\n", 517 | "plt.plot(loss_hist)\n", 518 | "plt.xlabel(\"Epoch\")\n", 519 | "plt.ylabel(\"Loss\")\n", 520 | "sns.despine()" 521 | ] 522 | }, 523 | { 524 | "cell_type": "code", 525 | "execution_count": 16, 526 | "metadata": {}, 527 | "outputs": [ 528 | { 529 | "name": "stdout", 530 | "output_type": "stream", 531 | "text": [ 532 | "Training accuracy: 0.848\n", 533 | "Test accuracy: 0.827\n" 534 | ] 535 | } 536 | ], 537 | "source": [ 538 | "print(\"Training accuracy: %.3f\"%(compute_classification_accuracy(x_train,y_train)))\n", 539 | "print(\"Test accuracy: %.3f\"%(compute_classification_accuracy(x_test,y_test)))" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": 17, 545 | "metadata": {}, 546 | "outputs": [], 547 | "source": [ 548 | "def get_mini_batch(x_data, y_data, shuffle=False):\n", 549 | " for ret in sparse_data_generator(x_data, y_data, batch_size, nb_steps, nb_inputs, shuffle=shuffle):\n", 550 | " return ret " 551 | ] 552 | }, 553 | { 554 | "cell_type": "code", 555 | "execution_count": 18, 556 | "metadata": {}, 557 | "outputs": [], 558 | "source": [ 559 | "x_batch, y_batch = get_mini_batch(x_test, y_test)\n", 560 | "output, other_recordings = run_snn(x_batch.to_dense())\n", 561 | "mem_rec, spk_rec = other_recordings" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": null, 567 | "metadata": {}, 568 | "outputs": [], 569 | "source": [] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": 19, 574 | "metadata": {}, 575 | "outputs": [ 576 | { 577 | "data": { 578 | "image/png": "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\n", 579 | "text/plain": [ 580 | "
" 581 | ] 582 | }, 583 | "metadata": { 584 | "needs_background": "light" 585 | }, 586 | "output_type": "display_data" 587 | } 588 | ], 589 | "source": [ 590 | "fig=plt.figure(dpi=100)\n", 591 | "plot_voltage_traces(output)" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 20, 597 | "metadata": {}, 598 | "outputs": [ 599 | { 600 | "data": { 601 | "image/png": "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\n", 602 | "text/plain": [ 603 | "
" 604 | ] 605 | }, 606 | "metadata": { 607 | "needs_background": "light" 608 | }, 609 | "output_type": "display_data" 610 | } 611 | ], 612 | "source": [ 613 | "# Let's plot the hiddden layer spiking activity for some input stimuli\n", 614 | "\n", 615 | "nb_plt = 4\n", 616 | "gs = GridSpec(1,nb_plt)\n", 617 | "fig= plt.figure(figsize=(7,3),dpi=150)\n", 618 | "for i in range(nb_plt):\n", 619 | " plt.subplot(gs[i])\n", 620 | " plt.imshow(spk_rec[i].detach().cpu().numpy().T,cmap=plt.cm.gray_r, origin=\"lower\" )\n", 621 | " if i==0:\n", 622 | " plt.xlabel(\"Time\")\n", 623 | " plt.ylabel(\"Units\")\n", 624 | "\n", 625 | " sns.despine()" 626 | ] 627 | }, 628 | { 629 | "cell_type": "markdown", 630 | "metadata": {}, 631 | "source": [ 632 | "Compared to the hidden layer activity in our previous Tutorial 2, we can now appreciate that spiking in the hidden layer is much sparser.\n", 633 | "\n", 634 | "In the next tutorial notebook, we will apply the same training paradigm to the Heidelberg Digits, a realistic speech dataset generated from spoken digits processed through a plausible cochlea model." 635 | ] 636 | }, 637 | { 638 | "cell_type": "markdown", 639 | "metadata": {}, 640 | "source": [ 641 | "\"Creative
This work is licensed under a Creative Commons Attribution 4.0 International License." 642 | ] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "execution_count": null, 647 | "metadata": {}, 648 | "outputs": [], 649 | "source": [] 650 | }, 651 | { 652 | "cell_type": "code", 653 | "execution_count": null, 654 | "metadata": {}, 655 | "outputs": [], 656 | "source": [] 657 | }, 658 | { 659 | "cell_type": "code", 660 | "execution_count": null, 661 | "metadata": {}, 662 | "outputs": [], 663 | "source": [] 664 | } 665 | ], 666 | "metadata": { 667 | "kernelspec": { 668 | "display_name": "Python 3", 669 | "language": "python", 670 | "name": "python3" 671 | }, 672 | "language_info": { 673 | "codemirror_mode": { 674 | "name": "ipython", 675 | "version": 3 676 | }, 677 | "file_extension": ".py", 678 | "mimetype": "text/x-python", 679 | "name": "python", 680 | "nbconvert_exporter": "python", 681 | "pygments_lexer": "ipython3", 682 | "version": "3.6.9" 683 | } 684 | }, 685 | "nbformat": 4, 686 | "nbformat_minor": 2 687 | } 688 | -------------------------------------------------------------------------------- /notebooks/figures/.gitignore: -------------------------------------------------------------------------------- 1 | ## Core latex/pdflatex auxiliary files: 2 | *.aux 3 | *.lof 4 | *.log 5 | *.lot 6 | *.fls 7 | *.out 8 | *.toc 9 | *.fmt 10 | *.fot 11 | *.cb 12 | *.cb2 13 | .*.lb 14 | 15 | ## Intermediate documents: 16 | *.dvi 17 | *.xdv 18 | *-converted-to.* 19 | # these rules might exclude image files for figures etc. 20 | # *.ps 21 | # *.eps 22 | # *.pdf 23 | 24 | ## Generated if empty string is given at "Please type another file name for output:" 25 | .pdf 26 | 27 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 28 | *.bbl 29 | *.bcf 30 | *.blg 31 | *-blx.aux 32 | *-blx.bib 33 | *.run.xml 34 | 35 | ## Build tool auxiliary files: 36 | *.fdb_latexmk 37 | *.synctex 38 | *.synctex(busy) 39 | *.synctex.gz 40 | *.synctex.gz(busy) 41 | *.pdfsync 42 | 43 | ## Build tool directories for auxiliary files 44 | # latexrun 45 | latex.out/ 46 | 47 | ## Auxiliary and intermediate files from other packages: 48 | # algorithms 49 | *.alg 50 | *.loa 51 | 52 | # achemso 53 | acs-*.bib 54 | 55 | # amsthm 56 | *.thm 57 | 58 | # beamer 59 | *.nav 60 | *.pre 61 | *.snm 62 | *.vrb 63 | 64 | # changes 65 | *.soc 66 | 67 | # comment 68 | *.cut 69 | 70 | # cprotect 71 | *.cpt 72 | 73 | # elsarticle (documentclass of Elsevier journals) 74 | *.spl 75 | 76 | # endnotes 77 | *.ent 78 | 79 | # fixme 80 | *.lox 81 | 82 | # feynmf/feynmp 83 | *.mf 84 | *.mp 85 | *.t[1-9] 86 | *.t[1-9][0-9] 87 | *.tfm 88 | 89 | #(r)(e)ledmac/(r)(e)ledpar 90 | *.end 91 | *.?end 92 | *.[1-9] 93 | *.[1-9][0-9] 94 | *.[1-9][0-9][0-9] 95 | *.[1-9]R 96 | *.[1-9][0-9]R 97 | *.[1-9][0-9][0-9]R 98 | *.eledsec[1-9] 99 | *.eledsec[1-9]R 100 | *.eledsec[1-9][0-9] 101 | *.eledsec[1-9][0-9]R 102 | *.eledsec[1-9][0-9][0-9] 103 | *.eledsec[1-9][0-9][0-9]R 104 | 105 | # glossaries 106 | *.acn 107 | *.acr 108 | *.glg 109 | *.glo 110 | *.gls 111 | *.glsdefs 112 | 113 | # gnuplottex 114 | *-gnuplottex-* 115 | 116 | # gregoriotex 117 | *.gaux 118 | *.gtex 119 | 120 | # htlatex 121 | *.4ct 122 | *.4tc 123 | *.idv 124 | *.lg 125 | *.trc 126 | *.xref 127 | 128 | # hyperref 129 | *.brf 130 | 131 | # knitr 132 | *-concordance.tex 133 | # TODO Comment the next line if you want to keep your tikz graphics files 134 | *.tikz 135 | *-tikzDictionary 136 | 137 | # listings 138 | *.lol 139 | 140 | # makeidx 141 | *.idx 142 | *.ilg 143 | *.ind 144 | *.ist 145 | 146 | # minitoc 147 | *.maf 148 | *.mlf 149 | *.mlt 150 | *.mtc[0-9]* 151 | *.slf[0-9]* 152 | *.slt[0-9]* 153 | *.stc[0-9]* 154 | 155 | # minted 156 | _minted* 157 | *.pyg 158 | 159 | # morewrites 160 | *.mw 161 | 162 | # nomencl 163 | *.nlg 164 | *.nlo 165 | *.nls 166 | 167 | # pax 168 | *.pax 169 | 170 | # pdfpcnotes 171 | *.pdfpc 172 | 173 | # sagetex 174 | *.sagetex.sage 175 | *.sagetex.py 176 | *.sagetex.scmd 177 | 178 | # scrwfile 179 | *.wrt 180 | 181 | # sympy 182 | *.sout 183 | *.sympy 184 | sympy-plots-for-*.tex/ 185 | 186 | # pdfcomment 187 | *.upa 188 | *.upb 189 | 190 | # pythontex 191 | *.pytxcode 192 | pythontex-files-*/ 193 | 194 | # tcolorbox 195 | *.listing 196 | 197 | # thmtools 198 | *.loe 199 | 200 | # TikZ & PGF 201 | *.dpth 202 | *.md5 203 | *.auxlock 204 | 205 | # todonotes 206 | *.tdo 207 | 208 | # vhistory 209 | *.hst 210 | *.ver 211 | 212 | # easy-todo 213 | *.lod 214 | 215 | # xcolor 216 | *.xcp 217 | 218 | # xmpincl 219 | *.xmpi 220 | 221 | # xindy 222 | *.xdy 223 | 224 | # xypic precompiled matrices 225 | *.xyc 226 | 227 | # endfloat 228 | *.ttt 229 | *.fff 230 | 231 | # Latexian 232 | TSWLatexianTemp* 233 | 234 | ## Editors: 235 | # WinEdt 236 | *.bak 237 | *.sav 238 | 239 | # Texpad 240 | .texpadtmp 241 | 242 | # LyX 243 | *.lyx~ 244 | 245 | # Kile 246 | *.backup 247 | 248 | # KBibTeX 249 | *~[0-9]* 250 | 251 | # auto folder when using emacs and auctex 252 | ./auto/* 253 | *.el 254 | 255 | # expex forward references with \gathertags 256 | *-tags.tex 257 | 258 | # standalone packages 259 | *.sta 260 | 261 | -------------------------------------------------------------------------------- /notebooks/figures/mlp_sketch/Makefile: -------------------------------------------------------------------------------- 1 | all: mlp_sketch.png 2 | 3 | %.png: %.pdf 4 | convert -density 150 $< $@ 5 | 6 | %.pdf: %.tex 7 | pdflatex $< 8 | 9 | -------------------------------------------------------------------------------- /notebooks/figures/mlp_sketch/mlp_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surrogate-gradient-learning/spytorch/f4dcc662b8fe1d23e88790400e58b5c51b2ebc36/notebooks/figures/mlp_sketch/mlp_sketch.png -------------------------------------------------------------------------------- /notebooks/figures/mlp_sketch/mlp_sketch.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | 4 | \usepackage{tikz} 5 | \usepackage{verbatim} 6 | \usepackage{tikz,graphicx} 7 | \usepackage{sfmath} 8 | \usepackage{xifthen} 9 | \usetikzlibrary{positioning,arrows,calc} 10 | % \usetikzlibrary{spy} 11 | \renewcommand\familydefault\sfdefault 12 | 13 | \usepackage{xcolor} 14 | 15 | \definecolor{gnu-violet}{HTML}{9400D3}% 16 | \definecolor{gnu-green}{HTML}{009E73}% 17 | \definecolor{gnu-blue}{HTML}{56b4e9}% 18 | \definecolor{gnu-yellow}{HTML}{E69F00}% 19 | 20 | 21 | \begin{document} 22 | 23 | \pagestyle{empty} 24 | 25 | \def\layersep{1.2cm} 26 | \def\unitsep{0.6cm} 27 | \def\nbhidden{1} 28 | 29 | \begin{tikzpicture}[shorten >=1pt,->,draw=black!50] 30 | 31 | \tikzstyle{every pin edge}=[<-,shorten <=1pt] 32 | \tikzstyle{input neuron}=[circle, fill=black!25, minimum size=15pt, inner sep=0pt] 33 | \tikzstyle{neuron}=[circle,fill=black!25,minimum size=12pt,inner sep=0pt] 34 | \tikzstyle{annot} = [text width=2cm, text centered, node distance=2.2cm] 35 | 36 | 37 | % Input units 38 | \foreach \name / \y in {0,...,5} { 39 | \node[neuron] (I-\name) at (\y*\unitsep, 0) {}; 40 | } 41 | 42 | % Draw the hidden layer nodes 43 | \foreach \layer in {1,...,\nbhidden} { 44 | \foreach \name / \y in {1,...,4} { 45 | \node[neuron] (H\layer-\name) at (\y*\unitsep, \layer*\layersep) {}; 46 | } 47 | } 48 | 49 | % Draw the output layer nodes 50 | \foreach \name / \y in {2,...,3} { 51 | \node[neuron] (O-\name) at (\y*\unitsep, \nbhidden*\layersep + \layersep) {}; 52 | } 53 | 54 | % Connect every node in the input layer with every node in the 55 | % hidden layer. 56 | \foreach \source in {0,...,5} 57 | \foreach \dest in {1,...,4} { 58 | \path (I-\source) edge [->] (H1-\dest); 59 | } 60 | 61 | % Connect input to hidden 62 | \foreach \source in {1,...,4} 63 | \foreach \dest in {2,...,3} { 64 | \path (H\nbhidden-\source) edge [->] (O-\dest); 65 | } 66 | 67 | % Connect hidden layers 68 | % \foreach \layer in {1,...,\nbhidden} { 69 | % \foreach \source in {1,...,4} 70 | % \foreach \dest in {1,...,4} { 71 | % \path (H\layer-\source) edge [->] (H\nbhidden-\dest); 72 | % } 73 | % } 74 | 75 | % Connect every node in the hidden layer with the output layer 76 | \foreach \source in {1,...,4} 77 | \foreach \dest in {2,...,3} { 78 | \path (H\nbhidden-\source) edge [->] (O-\dest); 79 | } 80 | 81 | 82 | % Annotate the layers 83 | \node[annot,left of=I-0] {Input layer}; 84 | \node[annot,left of=O-2] {Output layer}; 85 | 86 | 87 | \end{tikzpicture} 88 | 89 | % End of code 90 | 91 | \end{document} 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /notebooks/figures/snn_graph/Makefile: -------------------------------------------------------------------------------- 1 | all: snn_graph.png 2 | 3 | %.pdf: %.tex 4 | pdflatex $< 5 | 6 | %.png: %.pdf 7 | convert -density 150 $< $@ 8 | -------------------------------------------------------------------------------- /notebooks/figures/snn_graph/snn_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surrogate-gradient-learning/spytorch/f4dcc662b8fe1d23e88790400e58b5c51b2ebc36/notebooks/figures/snn_graph/snn_graph.png -------------------------------------------------------------------------------- /notebooks/figures/snn_graph/snn_graph.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage[latin1]{inputenc} 4 | \usepackage{tikz} 5 | \usepackage{tikz,graphicx} 6 | \usepackage{sfmath} 7 | \usepackage{bbold} 8 | \usepackage{dsfont} 9 | \usetikzlibrary{shapes,arrows} 10 | \usetikzlibrary{positioning,quotes} 11 | 12 | \renewcommand{\familydefault}{\sfdefault} 13 | 14 | \begin{document} 15 | \pagestyle{empty} 16 | 17 | \definecolor{spikecolor}{RGB}{180,51,76} 18 | \definecolor{pyblue}{HTML}{1f77b4} 19 | \definecolor{pyorange}{HTML}{ff7f0e} 20 | 21 | 22 | % Define block styles 23 | \tikzstyle{block} = [rectangle, draw, text width=3em, text centered, rounded 24 | corners, minimum height=2.2em] 25 | \tikzstyle{sum} = [block] 26 | \tikzstyle{syn} = [block, fill=black!10 ] 27 | \tikzstyle{mem} = [block, fill=pyblue!40 ] 28 | \tikzstyle{spk} = [block, fill=pyorange!40 ] 29 | 30 | 31 | \begin{tikzpicture}[node distance = 1.2cm, auto] 32 | \pgfmathsetmacro{\n}{4} % sets number of units -1 33 | \pgfmathsetmacro{\nm}{3} % set to \n -1 34 | \pgfmathsetmacro{\sep}{2.6} 35 | 36 | % Place nodes 37 | \foreach \t in {0,...,\n} { 38 | \node [sum] (x\t) at (\sep*\t,0) {$S^{(0)}[\t]$}; 39 | \node [syn, above of=x\t] (I\t) {$I^{(1)}[\t]$}; 40 | \node [mem, above of=I\t] (U\t) {$U^{(1)}[\t]$}; 41 | \node [spk, above of=U\t] (S\t) {$S^{(1)}[\t]$}; 42 | \node [block, above of=S\t] (y\t) {}; 43 | \node [above of=y\t] (downstream\t) {}; 44 | } 45 | 46 | 47 | % Draw edges 48 | \foreach \t in {0,...,\n} { 49 | \path [->, draw, thick] (U\t) -- (S\t); 50 | % \path [->, draw, thick] (y\t) -- (downstream\t) node[near start, right] 51 | % {$W^{(1,2)}$}; 52 | % \path [->, draw, thick, spikecolor] (upstream\t) -- (x\t) node[near end, 53 | % right] {$W^{(0,1)}$}; 54 | } 55 | 56 | % Forward in time 57 | \foreach \t in {0,...,\nm} { 58 | \pgfmathtruncatemacro{\next}{\t + 1} 59 | \path [->, draw, thick] (x\t) -- (I\next) node[near start, right] 60 | {~$W^{(1)}$}; 61 | \path [->, draw, thick] (I\t) -- (U\next); 62 | 63 | \path [->, draw, thick] (I\t) -- (I\next) node[near end] {$\alpha$}; 64 | \path [->, draw, thick] (U\t) -- (U\next) node[near end] {$\beta$}; 65 | 66 | \path [->, draw, thick, pyorange] (S\t) -- (y\next) node[near end, 67 | left] {$W^{(2)}$}; 68 | \path [->, draw, thick, pyorange, dashed] (S\t) -- (U\next) node[near start] {$-\mathds{1}$}; 69 | \path [->, draw, thick, pyorange] (S\t) -- (I\next) node[near end, 70 | left] {$V^{(1)}$}; 71 | 72 | } 73 | 74 | \node[node distance=1.0cm, below of=x0] (a0) {}; 75 | \node[right of=a0] (a1) {}; 76 | % \path [->, draw, thick, black] (a0) -- (a1) node[near start, below] {Time}; 77 | 78 | \draw (a0) edge[->,"Time"] (a1) ; 79 | 80 | \end{tikzpicture} 81 | 82 | \end{document} 83 | -------------------------------------------------------------------------------- /notebooks/figures/surrgrad/Makefile: -------------------------------------------------------------------------------- 1 | all: surrgrad.png 2 | 3 | %.png: %.pdf 4 | convert -density 150 $< $@ 5 | 6 | %.pdf: %.tex 7 | pdflatex $< 8 | 9 | %.tex: %.gnu 10 | gnuplot $< 11 | -------------------------------------------------------------------------------- /notebooks/figures/surrgrad/surrgrad.gnu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/gnuplot 2 | 3 | set border 3 4 | set xtics nomirror out 5 | set ytics nomirror out 6 | 7 | 8 | theta(x) = x>0?1:0 9 | 10 | sigma(x) = 0.5*x/(abs(x)+1)+0.5 11 | 12 | set xlabel '$x$' 13 | # set ylabel 'Output' 14 | set key bottom right 15 | 16 | set xrange [-1:1] 17 | plot theta(x) lw 3 title '$\Theta(x)$', \ 18 | sigma(x) lw 3 title '$\sigma(x)$', \ 19 | sigma(10*x) lw 3 title '$\sigma(10 x)$', \ 20 | 21 | 22 | set term epslatex standalone dashed color size 3.3, 2.0 font '\sfdefault,8' \ 23 | header '\usepackage{sfmath}' 24 | set out 'surrgrad.tex' 25 | rep 26 | -------------------------------------------------------------------------------- /notebooks/figures/surrgrad/surrgrad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/surrogate-gradient-learning/spytorch/f4dcc662b8fe1d23e88790400e58b5c51b2ebc36/notebooks/figures/surrgrad/surrgrad.png -------------------------------------------------------------------------------- /notebooks/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import urllib.request 3 | import gzip, shutil 4 | import hashlib 5 | 6 | from six.moves.urllib.error import HTTPError 7 | from six.moves.urllib.error import URLError 8 | from six.moves.urllib.request import urlretrieve 9 | 10 | # The functions used in this file to download the dataset are based on 11 | # code from the keras library. Specifically, from the following file: 12 | # https://github.com/tensorflow/tensorflow/blob/v2.3.1/tensorflow/python/keras/utils/data_utils.py 13 | 14 | 15 | def get_shd_dataset(cache_dir, cache_subdir): 16 | 17 | # The remote directory with the data files 18 | base_url = "https://compneuro.net/datasets" 19 | 20 | # Retrieve MD5 hashes from remote 21 | response = urllib.request.urlopen("%s/md5sums.txt"%base_url) 22 | data = response.read() 23 | lines = data.decode('utf-8').split("\n") 24 | file_hashes = { line.split()[1]:line.split()[0] for line in lines if len(line.split())==2 } 25 | # Download the Spiking Heidelberg Digits (SHD) dataset 26 | files = [ "shd_train.h5.gz", 27 | "shd_test.h5.gz", 28 | ] 29 | for fn in files: 30 | origin = "%s/%s"%(base_url,fn) 31 | hdf5_file_path = get_and_gunzip(origin, fn, md5hash=file_hashes[fn], cache_dir=cache_dir, cache_subdir=cache_subdir) 32 | print("File %s decompressed to:"%(fn)) 33 | print(hdf5_file_path) 34 | 35 | def get_and_gunzip(origin, filename, md5hash=None, cache_dir=None, cache_subdir=None): 36 | gz_file_path = get_file(filename, origin, md5_hash=md5hash, cache_dir=cache_dir, cache_subdir=cache_subdir) 37 | hdf5_file_path = gz_file_path 38 | if not os.path.isfile(hdf5_file_path) or os.path.getctime(gz_file_path) > os.path.getctime(hdf5_file_path): 39 | print("Decompressing %s"%gz_file_path) 40 | with gzip.open(gz_file_path, 'r') as f_in, open(hdf5_file_path, 'wb') as f_out: 41 | shutil.copyfileobj(f_in, f_out) 42 | return hdf5_file_path 43 | 44 | def validate_file(fpath, file_hash, algorithm='auto', chunk_size=65535): 45 | if (algorithm == 'sha256') or (algorithm == 'auto' and len(file_hash) == 64): 46 | hasher = 'sha256' 47 | else: 48 | hasher = 'md5' 49 | 50 | if str(_hash_file(fpath, hasher, chunk_size)) == str(file_hash): 51 | return True 52 | else: 53 | return False 54 | 55 | def _hash_file(fpath, algorithm='sha256', chunk_size=65535): 56 | if (algorithm == 'sha256') or (algorithm == 'auto' and len(hash) == 64): 57 | hasher = hashlib.sha256() 58 | else: 59 | hasher = hashlib.md5() 60 | 61 | with open(fpath, 'rb') as fpath_file: 62 | for chunk in iter(lambda: fpath_file.read(chunk_size), b''): 63 | hasher.update(chunk) 64 | 65 | return hasher.hexdigest() 66 | 67 | def get_file(fname, 68 | origin, 69 | md5_hash=None, 70 | file_hash=None, 71 | cache_subdir='datasets', 72 | hash_algorithm='auto', 73 | extract=False, 74 | archive_format='auto', 75 | cache_dir=None): 76 | if cache_dir is None: 77 | cache_dir = os.path.join(os.path.expanduser('~'), '.data-cache') 78 | if md5_hash is not None and file_hash is None: 79 | file_hash = md5_hash 80 | hash_algorithm = 'md5' 81 | datadir_base = os.path.expanduser(cache_dir) 82 | if not os.access(datadir_base, os.W_OK): 83 | datadir_base = os.path.join('/tmp', '.data-cache') 84 | datadir = os.path.join(datadir_base, cache_subdir) 85 | os.makedirs(datadir, exist_ok=True) 86 | 87 | fpath = os.path.join(datadir, fname) 88 | 89 | download = False 90 | if os.path.exists(fpath): 91 | # File found; verify integrity if a hash was provided. 92 | if file_hash is not None: 93 | if not validate_file(fpath, file_hash, algorithm=hash_algorithm): 94 | print('A local file was found, but it seems to be ' 95 | 'incomplete or outdated because the ' + hash_algorithm + 96 | ' file hash does not match the original value of ' + file_hash + 97 | ' so we will re-download the data.') 98 | download = True 99 | else: 100 | download = True 101 | 102 | if download: 103 | print('Downloading data from', origin) 104 | 105 | error_msg = 'URL fetch failure on {}: {} -- {}' 106 | try: 107 | try: 108 | urlretrieve(origin, fpath) 109 | except HTTPError as e: 110 | raise Exception(error_msg.format(origin, e.code, e.msg)) 111 | except URLError as e: 112 | raise Exception(error_msg.format(origin, e.errno, e.reason)) 113 | except (Exception, KeyboardInterrupt) as e: 114 | if os.path.exists(fpath): 115 | os.remove(fpath) 116 | 117 | return fpath -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | jupyter==1.0.0 2 | jupyter-client==6.1.6 3 | jupyter-console==6.1.0 4 | jupyter-core==4.6.3 5 | torch==1.6.0 6 | torchvision==0.7.0 7 | seaborn==0.10.1 8 | h5py==2.10.0 9 | --------------------------------------------------------------------------------