├── .gitignore ├── Example_format_data.ipynb ├── Example_hyperparam_opt.ipynb ├── Examples_all_decoders.ipynb ├── Examples_hippocampus ├── Example_format_data_hc.ipynb ├── Examples_decoders_hc.ipynb ├── Examples_kf_decoder_hc.ipynb └── Examples_naivebayes_decoder_hc.ipynb ├── Examples_kf_decoder.ipynb ├── LICENSE ├── Neural_Decoding ├── __init__.py ├── decoders.py ├── metrics.py └── preprocessing_funcs.py ├── Paper_code ├── BayesDecoder_FullData.ipynb ├── Ensemble_FullData.ipynb ├── Hyperparameter_Sensitivity.ipynb ├── KF_BinSize.ipynb ├── KF_DataAmt.ipynb ├── KF_FewNeurons.ipynb ├── KF_FullData.ipynb ├── ManyDecoders_BinSize.ipynb ├── ManyDecoders_DataAmt.ipynb ├── ManyDecoders_FewNeurons.ipynb ├── ManyDecoders_FullData.ipynb ├── Plot_Results_BinSize.ipynb ├── Plot_Results_FewNeurons.ipynb ├── Plot_Results_FullData.ipynb ├── Plot_Results_Hyperparams.ipynb └── Plot_Results_LimitedData.ipynb ├── README.md ├── central_concepts_in_ML_for_decoding.ipynb ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /Example_format_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Example of correctly formatting data\n", 8 | "\n", 9 | "For use in decoding (see \"Examples_all_decoders\" and \"Examples_kf_decoder\"), we need the following format of inputs:\n", 10 | "- Neural data should be a matrix of size \"number of time bins\" x \"number of neurons\", where each entry is the firing rate of a given neuron in a given time bin\n", 11 | "- The output you are decoding should be a matrix of size \"number of time bins\" x \"number of features you are decoding\"\n", 12 | "\n", 13 | "In this example, we load Matlab data that contains \n", 14 | "- The spike times of all neurons. In Matlab, \"spike_times\" is a cell of size \"number of neurons\" x 1. Within spike_times{i} is a vector containing all the spike times of neuron i.\n", 15 | "- A continuous stream of the output variables. In this example, we are aiming to decode velocity. In Matlab, \"vels\" is a matrix of size \"number of recorded time points\" x 2 (x and y velocities were recorded) that contains the x and y velocity components at all time points. \"vel_times\" is a vector that states the time at all recorded time points. \n", 16 | "\n", 17 | "We will put this data in the format described above, with the help of the functions \"bin_spikes\" and \"bin_output\" that are in the file \"preprocessing_funcs.py\"\n", 18 | "\n", 19 | "\n" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## Import packages and functions" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 1, 32 | "metadata": { 33 | "collapsed": false 34 | }, 35 | "outputs": [], 36 | "source": [ 37 | "###Import standard packages###\n", 38 | "import numpy as np\n", 39 | "from scipy import io\n", 40 | "\n", 41 | "###Import functions for binning data for preprocessing###\n", 42 | "from Neural_Decoding.preprocessing_funcs import bin_spikes\n", 43 | "from Neural_Decoding.preprocessing_funcs import bin_output" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "## Load Data\n", 51 | "The data for this example can be downloaded at this [link](https://www.dropbox.com/sh/n4924ipcfjqc0t6/AACPWjxDKPEzQiXKUUFriFkJa?dl=0&preview=s1_data_raw.mat)\n", 52 | "\n", 53 | "It was recorded by Raeed Chowdhury from Lee Miller's lab at Northwestern." 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 2, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [], 63 | "source": [ 64 | "###Load Data###\n", 65 | "folder='' #ENTER THE FOLDER THAT YOUR DATA IS IN\n", 66 | "# folder='/Users/jig289/Dropbox/MATLAB/Projects/In_Progress/BMI/Processed_Data/' \n", 67 | "data=io.loadmat(folder+'s1_data_raw.mat')\n", 68 | "spike_times=data['spike_times'] #Load spike times of all neurons\n", 69 | "vels=data['vels'] #Load x and y velocities\n", 70 | "vel_times=data['vel_times'] #Load times at which velocities were recorded" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "## User Inputs" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 3, 83 | "metadata": { 84 | "collapsed": false 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "dt=.05 #Size of time bins (in seconds)\n", 89 | "t_start=vel_times[0] #Time to start extracting data - here the first time velocity was recorded\n", 90 | "t_end=vel_times[-1] #Time to finish extracting data - here the last time velocity was recorded\n", 91 | "downsample_factor=1 #Downsampling of output (to make binning go faster). 1 means no downsampling." 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "## Put data in binned format" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 4, 104 | "metadata": { 105 | "collapsed": true 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "#When loading the Matlab cell \"spike_times\", Python puts it in a format with an extra unnecessary dimension\n", 110 | "#First, we will put spike_times in a cleaner format: an array of arrays\n", 111 | "spike_times=np.squeeze(spike_times)\n", 112 | "for i in range(spike_times.shape[0]):\n", 113 | " spike_times[i]=np.squeeze(spike_times[i])" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": { 120 | "collapsed": false 121 | }, 122 | "outputs": [], 123 | "source": [ 124 | "###Preprocessing to put spikes and output in bins###\n", 125 | "\n", 126 | "#Bin neural data using \"bin_spikes\" function\n", 127 | "neural_data=bin_spikes(spike_times,dt,t_start,t_end)\n", 128 | "\n", 129 | "#Bin output (velocity) data using \"bin_output\" function\n", 130 | "vels_binned=bin_output(vels,vel_times,dt,t_start,t_end,downsample_factor)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "## Save Data" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 6, 143 | "metadata": { 144 | "collapsed": false 145 | }, 146 | "outputs": [], 147 | "source": [ 148 | "import pickle\n", 149 | "\n", 150 | "data_folder='' #FOLDER YOU WANT TO SAVE THE DATA TO\n", 151 | "\n", 152 | "with open(data_folder+'example_data_s1.pickle','wb') as f:\n", 153 | " pickle.dump([neural_data,vels_binned],f)" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "metadata": { 160 | "collapsed": true 161 | }, 162 | "outputs": [], 163 | "source": [] 164 | } 165 | ], 166 | "metadata": { 167 | "anaconda-cloud": {}, 168 | "kernelspec": { 169 | "display_name": "Python [py35]", 170 | "language": "python", 171 | "name": "Python [py35]" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.5.2" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 0 188 | } 189 | -------------------------------------------------------------------------------- /Examples_hippocampus/Example_format_data_hc.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Example of correctly formatting data\n", 8 | "\n", 9 | "For use in decoding (see \"Examples_decoders_hc\" and \"Examples_kf_decoder_hc\"), we need the following format of inputs:\n", 10 | "- Neural data should be a matrix of size \"number of time bins\" x \"number of neurons\", where each entry is the firing rate of a given neuron in a given time bin\n", 11 | "- The output you are decoding should be a matrix of size \"number of time bins\" x \"number of features you are decoding\"\n", 12 | "\n", 13 | "In this example, we load Matlab data that contains \n", 14 | "- The spike times of all neurons. In Matlab, \"spike_times\" is a cell of size \"number of neurons\" x 1. Within spike_times{i} is a vector containing all the spike times of neuron i.\n", 15 | "- A continuous stream of the output variables. In this example, we are aiming to decode position. In Matlab, \"pos\" is a matrix of size \"number of recorded time points\" x 2 (x and y positions were recorded) that contains the x and y position components at all time points. Time points that were not recorded have NaN values. \"pos_times\" is a vector that states the time at all recorded time points. \n", 16 | "\n", 17 | "We will put this data in the format described above, with the help of the functions \"bin_spikes\" and \"bin_output\" that are in the file \"preprocessing_funcs.py\"\n", 18 | "\n", 19 | "\n" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## Import packages and functions\n", 27 | "Note that you may need to specify the path below" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 1, 33 | "metadata": { 34 | "collapsed": false 35 | }, 36 | "outputs": [ 37 | { 38 | "name": "stderr", 39 | "output_type": "stream", 40 | "text": [ 41 | "Using Theano backend.\n" 42 | ] 43 | } 44 | ], 45 | "source": [ 46 | "###Import standard packages###\n", 47 | "import numpy as np\n", 48 | "from scipy import io\n", 49 | "import sys\n", 50 | "\n", 51 | "###Import functions for binning data for preprocessing###\n", 52 | "from Neural_Decoding.preprocessing_funcs import bin_spikes\n", 53 | "from Neural_Decoding.preprocessing_funcs import bin_output" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## Load Data\n", 61 | "The data for this example can be downloaded at this [link](https://www.dropbox.com/s/94dhsgnx2cfs3jx/hc_data_raw.mat?dl=0)\n", 62 | "\n", 63 | "It is the hc-2 dataset from [crcns](https://crcns.org/data-sets/hc/hc-2). Specifically, we use the dataset \"ec014.333\" " 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 2, 69 | "metadata": { 70 | "collapsed": false 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "###Load Data###\n", 75 | "folder='' #ENTER THE FOLDER THAT YOUR DATA IS IN\n", 76 | "# folder='/home/jglaser/Data/DecData/' \n", 77 | "data=io.loadmat(folder+'hc_data_raw.mat')\n", 78 | "spike_times=data['spike_times'] #Load spike times of all neurons\n", 79 | "pos=data['pos'] #Load x and y positions\n", 80 | "pos_times=data['pos_times'][0] #Load times at which positions were recorded" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "## User Inputs" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 3, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [], 97 | "source": [ 98 | "dt=.2 #Size of time bins (in seconds)\n", 99 | "t_start=pos_times[0] #Time to start extracting data - here the first time position was recorded\n", 100 | "t_end=5608 #pos_times[-1] #Time to finish extracting data - when looking through the dataset, the final position was recorded around t=5609, but the final spikes were recorded around t=5608\n", 101 | "downsample_factor=1 #Downsampling of output (to make binning go faster). 1 means no downsampling." 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "## Put data in binned format" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 4, 114 | "metadata": { 115 | "collapsed": true 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "#When loading the Matlab cell \"spike_times\", Python puts it in a format with an extra unnecessary dimension\n", 120 | "#First, we will put spike_times in a cleaner format: an array of arrays\n", 121 | "spike_times=np.squeeze(spike_times)\n", 122 | "for i in range(spike_times.shape[0]):\n", 123 | " spike_times[i]=np.squeeze(spike_times[i])" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 5, 129 | "metadata": { 130 | "collapsed": false 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "###Preprocessing to put spikes and output in bins###\n", 135 | "\n", 136 | "#Bin neural data using \"bin_spikes\" function\n", 137 | "neural_data=bin_spikes(spike_times,dt,t_start,t_end)\n", 138 | "\n", 139 | "#Bin output (position) data using \"bin_output\" function\n", 140 | "pos_binned=bin_output(pos,pos_times,dt,t_start,t_end,downsample_factor)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "## Save Data" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 6, 153 | "metadata": { 154 | "collapsed": false 155 | }, 156 | "outputs": [], 157 | "source": [ 158 | "import pickle\n", 159 | "\n", 160 | "data_folder='' #FOLDER YOU WANT TO SAVE THE DATA TO\n", 161 | "# data_folder='/home/jglaser/Data/DecData/' \n", 162 | "\n", 163 | "with open(data_folder+'example_data_hc.pickle','wb') as f:\n", 164 | " pickle.dump([neural_data,pos_binned],f)" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": { 171 | "collapsed": true 172 | }, 173 | "outputs": [], 174 | "source": [] 175 | } 176 | ], 177 | "metadata": { 178 | "anaconda-cloud": {}, 179 | "kernelspec": { 180 | "display_name": "Python [py35]", 181 | "language": "python", 182 | "name": "Python [py35]" 183 | }, 184 | "language_info": { 185 | "codemirror_mode": { 186 | "name": "ipython", 187 | "version": 3 188 | }, 189 | "file_extension": ".py", 190 | "mimetype": "text/x-python", 191 | "name": "python", 192 | "nbconvert_exporter": "python", 193 | "pygments_lexer": "ipython3", 194 | "version": "3.5.2" 195 | } 196 | }, 197 | "nbformat": 4, 198 | "nbformat_minor": 0 199 | } 200 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Joshua Glaser 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Neural_Decoding/__init__.py: -------------------------------------------------------------------------------- 1 | from .decoders import WienerFilterDecoder, WienerCascadeDecoder, KalmanFilterDecoder,\ 2 | DenseNNDecoder, SimpleRNNDecoder, GRUDecoder, LSTMDecoder, XGBoostDecoder, SVRDecoder, NaiveBayesDecoder 3 | from .metrics import get_R2, get_rho 4 | from .preprocessing_funcs import bin_output, bin_spikes, get_spikes_with_history -------------------------------------------------------------------------------- /Neural_Decoding/metrics.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | ########## R-squared (R2) ########## 4 | 5 | def get_R2(y_test,y_test_pred): 6 | 7 | """ 8 | Function to get R2 9 | 10 | Parameters 11 | ---------- 12 | y_test - the true outputs (a matrix of size number of examples x number of outputs) 13 | y_test_pred - the predicted outputs (a matrix of size number of examples x number of outputs) 14 | 15 | Returns 16 | ------- 17 | R2_array: An array of R2s for each output 18 | """ 19 | 20 | R2_list=[] #Initialize a list that will contain the R2s for all the outputs 21 | for i in range(y_test.shape[1]): #Loop through outputs 22 | #Compute R2 for each output 23 | y_mean=np.mean(y_test[:,i]) 24 | R2=1-np.sum((y_test_pred[:,i]-y_test[:,i])**2)/np.sum((y_test[:,i]-y_mean)**2) 25 | R2_list.append(R2) #Append R2 of this output to the list 26 | R2_array=np.array(R2_list) 27 | return R2_array #Return an array of R2s 28 | 29 | 30 | 31 | 32 | ########## Pearson's correlation (rho) ########## 33 | 34 | def get_rho(y_test,y_test_pred): 35 | 36 | """ 37 | Function to get Pearson's correlation (rho) 38 | 39 | Parameters 40 | ---------- 41 | y_test - the true outputs (a matrix of size number of examples x number of outputs) 42 | y_test_pred - the predicted outputs (a matrix of size number of examples x number of outputs) 43 | 44 | Returns 45 | ------- 46 | rho_array: An array of rho's for each output 47 | """ 48 | 49 | rho_list=[] #Initialize a list that will contain the rhos for all the outputs 50 | for i in range(y_test.shape[1]): #Loop through outputs 51 | #Compute rho for each output 52 | y_mean=np.mean(y_test[:,i]) 53 | rho=np.corrcoef(y_test[:,i].T,y_test_pred[:,i].T)[0,1] 54 | rho_list.append(rho) #Append rho of this output to the list 55 | rho_array=np.array(rho_list) 56 | return rho_array #Return the array of rhos 57 | -------------------------------------------------------------------------------- /Neural_Decoding/preprocessing_funcs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | ######## BIN_SPIKES ######## 5 | def bin_spikes(spike_times,dt,wdw_start,wdw_end): 6 | """ 7 | Function that puts spikes into bins 8 | 9 | Parameters 10 | ---------- 11 | spike_times: an array of arrays 12 | an array of neurons. within each neuron's array is an array containing all the spike times of that neuron 13 | dt: number (any format) 14 | size of time bins 15 | wdw_start: number (any format) 16 | the start time for putting spikes in bins 17 | wdw_end: number (any format) 18 | the end time for putting spikes in bins 19 | 20 | Returns 21 | ------- 22 | neural_data: a matrix of size "number of time bins" x "number of neurons" 23 | the number of spikes in each time bin for each neuron 24 | """ 25 | edges=np.arange(wdw_start,wdw_end,dt) #Get edges of time bins 26 | num_bins=edges.shape[0]-1 #Number of bins 27 | num_neurons=spike_times.shape[0] #Number of neurons 28 | neural_data=np.empty([num_bins,num_neurons]) #Initialize array for binned neural data 29 | #Count number of spikes in each bin for each neuron, and put in array 30 | for i in range(num_neurons): 31 | neural_data[:,i]=np.histogram(spike_times[i],edges)[0] 32 | return neural_data 33 | 34 | 35 | 36 | ######## BIN_OUTPUT ####### 37 | def bin_output(outputs,output_times,dt,wdw_start,wdw_end,downsample_factor=1): 38 | """ 39 | Function that puts outputs into bins 40 | 41 | Parameters 42 | ---------- 43 | outputs: matrix of size "number of times the output was recorded" x "number of features in the output" 44 | each entry in the matrix is the value of the output feature 45 | output_times: a vector of size "number of times the output was recorded" 46 | each entry has the time the output was recorded 47 | dt: number (any format) 48 | size of time bins 49 | wdw_start: number (any format) 50 | the start time for binning the outputs 51 | wdw_end: number (any format) 52 | the end time for binning the outputs 53 | downsample_factor: integer, optional, default=1 54 | how much to downsample the outputs prior to binning 55 | larger values will increase speed, but decrease precision 56 | 57 | Returns 58 | ------- 59 | outputs_binned: matrix of size "number of time bins" x "number of features in the output" 60 | the average value of each output feature in every time bin 61 | """ 62 | 63 | ###Downsample output### 64 | #We just take 1 out of every "downsample_factor" values# 65 | if downsample_factor!=1: #Don't downsample if downsample_factor=1 66 | downsample_idxs=np.arange(0,output_times.shape[0],downsample_factor) #Get the idxs of values we are going to include after downsampling 67 | outputs=outputs[downsample_idxs,:] #Get the downsampled outputs 68 | output_times=output_times[downsample_idxs] #Get the downsampled output times 69 | 70 | ###Put outputs into bins### 71 | edges=np.arange(wdw_start,wdw_end,dt) #Get edges of time bins 72 | num_bins=edges.shape[0]-1 #Number of bins 73 | output_dim=outputs.shape[1] #Number of output features 74 | outputs_binned=np.empty([num_bins,output_dim]) #Initialize matrix of binned outputs 75 | #Loop through bins, and get the mean outputs in those bins 76 | for i in range(num_bins): #Loop through bins 77 | idxs=np.where((np.squeeze(output_times)>=edges[i]) & (np.squeeze(output_times)0 and bins_after>0:\n", 517 | " y_train=y_train[bins_before:-bins_after,:]\n", 518 | " y_valid=y_valid[bins_before:-bins_after,:]\n", 519 | " y_test=y_test[bins_before:-bins_after,:]\n", 520 | "\n", 521 | " if bins_before>0 and bins_after==0:\n", 522 | " y_train=y_train[bins_before:,:]\n", 523 | " y_valid=y_valid[bins_before:,:]\n", 524 | " y_test=y_test[bins_before:,:]\n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " ################# DECODING #################\n", 529 | " \n", 530 | " #Add actual train/valid/test data to lists (for saving)\n", 531 | " y_test_all.append(y_test)\n", 532 | " y_train_all.append(y_train)\n", 533 | " y_valid_all.append(y_valid)\n", 534 | "\n", 535 | " \n", 536 | "\n", 537 | " #Set \"res\", the number of bins used (resolution) for decoding predictions\n", 538 | " #So if res=100, we create a 100 x 100 grid going from the minimum to maximum of the output variables\n", 539 | " #The prediction the decoder makes will be a value on that grid \n", 540 | " if dataset=='hc':\n", 541 | " res=100\n", 542 | " if dataset=='m1' or dataset=='s1':\n", 543 | " res=50 \n", 544 | " \n", 545 | "\n", 546 | " #Declare model\n", 547 | " model_nb=NaiveBayesDecoder(encoding_model='quadratic',res=res) #Use quadratic encoding model and resolution set above\n", 548 | "\n", 549 | " #Fit model\n", 550 | " model_nb.fit(X_b_train,y_train)\n", 551 | "\n", 552 | " #Get predictions\n", 553 | " y_test_predicted_nb=model_nb.predict(X_b_test,y_test) \n", 554 | " mean_r2_nb[i]=np.mean(get_R2(y_test,y_test_predicted_nb)) \n", 555 | " #Print R2 values on test set\n", 556 | " R2s_nb=get_R2(y_test,y_test_predicted_nb)\n", 557 | " print('R2s:', R2s_nb)\n", 558 | " \n", 559 | " #Add predictions of test set to lists (for saving)\n", 560 | " #We're saving predictions on the training/validation sets since we're not including this decoder in our ensemble method\n", 561 | " y_pred_nb_all.append(y_test_predicted_nb)\n", 562 | "# y_train_pred_nb_all.append(model_nb.predict(X_b_train,y_train))\n", 563 | "# y_valid_pred_nb_all.append(model_nb.predict(X_b_valid,y_valid)) \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " time_elapsed=time.time()-t1 #How much time has passed\n", 568 | " print(\"time_elapsed:\",time_elapsed)\n", 569 | " \n", 570 | " \n", 571 | " ###### SAVE RESULTS #####\n", 572 | " #Note that I save them after every cross-validation fold rather than at the end in case the code/computer crashes for some reason while running\n", 573 | " \n", 574 | " with open(save_folder+dataset+'_results_nb3.pickle','wb') as f:\n", 575 | " pickle.dump([mean_r2_nb,y_pred_nb_all],f)\n", 576 | "\n", 577 | " \n", 578 | "#Save ground truth results\n", 579 | "with open(save_folder+dataset+'_ground_truth_nb.pickle','wb') as f:\n", 580 | " pickle.dump([y_test_all,y_train_all,y_valid_all],f)" 581 | ] 582 | }, 583 | { 584 | "cell_type": "markdown", 585 | "metadata": {}, 586 | "source": [ 587 | "### Quick check of results" 588 | ] 589 | }, 590 | { 591 | "cell_type": "code", 592 | "execution_count": null, 593 | "metadata": { 594 | "collapsed": true 595 | }, 596 | "outputs": [], 597 | "source": [ 598 | "mean_r2_nb" 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": null, 604 | "metadata": { 605 | "collapsed": true 606 | }, 607 | "outputs": [], 608 | "source": [ 609 | "plt.plot(y_test_all[1][0:1000,0])\n", 610 | "plt.plot(y_pred_nb_all[1][0:1000,0])" 611 | ] 612 | } 613 | ], 614 | "metadata": { 615 | "anaconda-cloud": {}, 616 | "kernelspec": { 617 | "display_name": "Python 2", 618 | "language": "python", 619 | "name": "python2" 620 | }, 621 | "language_info": { 622 | "codemirror_mode": { 623 | "name": "ipython", 624 | "version": 2 625 | }, 626 | "file_extension": ".py", 627 | "mimetype": "text/x-python", 628 | "name": "python", 629 | "nbconvert_exporter": "python", 630 | "pygments_lexer": "ipython2", 631 | "version": "2.7.14" 632 | } 633 | }, 634 | "nbformat": 4, 635 | "nbformat_minor": 0 636 | } 637 | -------------------------------------------------------------------------------- /Paper_code/Ensemble_FullData.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Ensemble decoder on full dataset" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## User Options" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "# dataset='s1'\n", 26 | "# dataset='m1'\n", 27 | "dataset='hc'" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": { 34 | "collapsed": true 35 | }, 36 | "outputs": [], 37 | "source": [ 38 | "#What folder to save the ensemble results to\n", 39 | "#Note that the data we are loading are in this same folder (since they are the results from the other decoders)\n", 40 | "save_folder=''\n", 41 | "# save_folder='/home/jglaser/Files/Neural_Decoding/Results/' " 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "## Import packages" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 3, 54 | "metadata": { 55 | "collapsed": false 56 | }, 57 | "outputs": [ 58 | { 59 | "name": "stderr", 60 | "output_type": "stream", 61 | "text": [ 62 | "Using Theano backend.\n", 63 | "WARNING (theano.sandbox.cuda): The cuda backend is deprecated and will be removed in the next release (v0.10). Please switch to the gpuarray backend. You can get more information about how to switch at this URL:\n", 64 | " https://github.com/Theano/Theano/wiki/Converting-to-the-new-gpu-back-end%28gpuarray%29\n", 65 | "\n", 66 | "Using gpu device 0: GeForce GTX TITAN X (CNMeM is enabled with initial size: 40.0% of memory, cuDNN 5103)\n" 67 | ] 68 | } 69 | ], 70 | "source": [ 71 | "import numpy as np\n", 72 | "import matplotlib.pyplot as plt\n", 73 | "%matplotlib inline\n", 74 | "from scipy import io\n", 75 | "from scipy import stats\n", 76 | "import pickle\n", 77 | "import sys\n", 78 | "\n", 79 | "#Add the main folder to the path, so we have access to the files there.\n", 80 | "#Note that if your working directory is not the Paper_code folder, you may need to manually specify the path to the main folder. For example: sys.path.append('/home/jglaser/GitProj/Neural_Decoding')\n", 81 | "sys.path.append('..') \n", 82 | "\n", 83 | "#Import metrics\n", 84 | "from metrics import get_R2\n", 85 | "\n", 86 | "from decoders import DenseNNDecoder\n", 87 | "\n", 88 | "from bayes_opt import BayesianOptimization" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 4, 94 | "metadata": { 95 | "collapsed": true 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "#Turn off deprecation warnings\n", 100 | "\n", 101 | "import warnings\n", 102 | "warnings.filterwarnings(\"ignore\", category=DeprecationWarning) " 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "## Load results from other decoders\n", 110 | "Note we do not use the Kalman filter results in our ensemble due to slightly different formatting" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 5, 116 | "metadata": { 117 | "collapsed": false 118 | }, 119 | "outputs": [], 120 | "source": [ 121 | "with open(save_folder+dataset+'_ground_truth.pickle','rb') as f:\n", 122 | " [y_test_all,y_train_all,y_valid_all]=pickle.load(f)\n", 123 | "\n", 124 | "with open(save_folder+dataset+'_results_wf2.pickle','rb') as f:\n", 125 | " [mean_r2_wf,y_pred_wf_all,y_train_pred_wf_all,y_valid_pred_wf_all]=pickle.load(f)\n", 126 | "\n", 127 | "with open(save_folder+dataset+'_results_wc2.pickle','rb') as f:\n", 128 | " [mean_r2_wc,y_pred_wc_all,y_train_pred_wc_all,y_valid_pred_wc_all]=pickle.load(f) \n", 129 | " \n", 130 | "with open(save_folder+dataset+'_results_xgb2.pickle','rb') as f:\n", 131 | " [mean_r2_xgb,y_pred_xgb_all,y_train_pred_xgb_all,y_valid_pred_xgb_all,time_elapsed]=pickle.load(f)\n", 132 | "\n", 133 | "with open(save_folder+dataset+'_results_svr2.pickle','rb') as f:\n", 134 | " [mean_r2_svr,y_pred_svr_all,y_train_pred_svr_all,y_valid_pred_svr_all,time_elapsed]=pickle.load(f)\n", 135 | "\n", 136 | "with open(save_folder+dataset+'_results_dnn2.pickle','rb') as f:\n", 137 | " [mean_r2_dnn,y_pred_dnn_all,y_train_pred_dnn_all,y_valid_pred_dnn_all,time_elapsed]=pickle.load(f) \n", 138 | "\n", 139 | "with open(save_folder+dataset+'_results_rnn2.pickle','rb') as f:\n", 140 | " [mean_r2_rnn,y_pred_rnn_all,y_train_pred_rnn_all,y_valid_pred_rnn_all,time_elapsed]=pickle.load(f)\n", 141 | "\n", 142 | "with open(save_folder+dataset+'_results_gru2.pickle','rb') as f: \n", 143 | " [mean_r2_gru,y_pred_gru_all,y_train_pred_gru_all,y_valid_pred_gru_all,time_elapsed]=pickle.load(f)\n", 144 | "\n", 145 | "with open(save_folder+dataset+'_results_lstm2.pickle','rb') as f:\n", 146 | " [mean_r2_lstm,y_pred_lstm_all,y_train_pred_lstm_all,y_valid_pred_lstm_all,time_elapsed]=pickle.load(f)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "## Run ensemble method\n", 154 | "\n", 155 | "1. We loop through each CV fold and both out (x and y position/velocities).\n", 156 | "2. We create the matrix of covariates (the predictions from the other methods)\n", 157 | "3. We optimize the hyperparameters for the fully connected (dense) neural network we are using, based on validation set R2 values\n", 158 | "4. We fit the neural net on training data w/ the optimal hyperparameters\n", 159 | "5. We make test set predictions and get test set R2 values" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 6, 165 | "metadata": { 166 | "collapsed": false 167 | }, 168 | "outputs": [ 169 | { 170 | "name": "stderr", 171 | "output_type": "stream", 172 | "text": [ 173 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/keras/models.py:826: UserWarning: The `nb_epoch` argument in `fit` has been renamed `epochs`.\n", 174 | " warnings.warn('The `nb_epoch` argument in `fit` '\n" 175 | ] 176 | }, 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "('R2s:', array([ 0.49587947]))\n", 182 | "('R2s:', array([ 0.70776137]))\n" 183 | ] 184 | } 185 | ], 186 | "source": [ 187 | "##Initialize\n", 188 | "y_pred_ensemble_all=[] #List where test set predictions are put (for saving and plotting)\n", 189 | "mean_r2_dnn=np.empty([10,2]) #Where the R2 values are saved (matrix of 10 CV folds x 2 outputs)\n", 190 | "\n", 191 | "\n", 192 | "for i in range(10): #Loop through the cross validation folds\n", 193 | " for j in range(2): #Loop through the 2 output predictions (x and y positions/velocities)\n", 194 | "\n", 195 | " \n", 196 | " \n", 197 | " ###CREATE COVARIATES###\n", 198 | " \n", 199 | " #Make matrix of covariates, where each feature is the predictions from one of the other decoders\n", 200 | " #Do this for training, validation, and testing data\n", 201 | " X_train=np.concatenate((y_train_pred_wf_all[i][:,j:j+1], y_train_pred_wc_all[i][:,j:j+1], \n", 202 | " y_train_pred_svr_all[i][:,j:j+1],y_train_pred_xgb_all[i][:,j:j+1],\n", 203 | " y_train_pred_dnn_all[i][:,j:j+1], y_train_pred_rnn_all[i][:,j:j+1],\n", 204 | " y_train_pred_gru_all[i][:,j:j+1], y_train_pred_lstm_all[i][:,j:j+1]),axis=1)\n", 205 | " X_valid=np.concatenate((y_valid_pred_wf_all[i][:,j:j+1], y_valid_pred_wc_all[i][:,j:j+1], \n", 206 | " y_valid_pred_svr_all[i][:,j:j+1],y_valid_pred_xgb_all[i][:,j:j+1],\n", 207 | " y_valid_pred_dnn_all[i][:,j:j+1], y_valid_pred_rnn_all[i][:,j:j+1],\n", 208 | " y_valid_pred_gru_all[i][:,j:j+1], y_valid_pred_lstm_all[i][:,j:j+1]),axis=1)\n", 209 | " X_test=np.concatenate((y_pred_wf_all[i][:,j:j+1], y_pred_wc_all[i][:,j:j+1], \n", 210 | " y_pred_svr_all[i][:,j:j+1],y_pred_xgb_all[i][:,j:j+1],\n", 211 | " y_pred_dnn_all[i][:,j:j+1], y_pred_rnn_all[i][:,j:j+1],\n", 212 | " y_pred_gru_all[i][:,j:j+1], y_pred_lstm_all[i][:,j:j+1]),axis=1)\n", 213 | " \n", 214 | " #Get outputs (training/validation/testing) for this CV fold and output\n", 215 | " y_train=y_train_all[i][:,j:j+1]\n", 216 | " y_valid=y_valid_all[i][:,j:j+1]\n", 217 | " y_test=y_test_all[i][:,j:j+1]\n", 218 | "\n", 219 | " \n", 220 | " ###HYPERPARAMETER OPTIMIZATION###\n", 221 | " \n", 222 | " #Define a function that returns the metric we are trying to optimize (R2 value of the validation set)\n", 223 | " #as a function of the hyperparameter we are fitting (num_units, frac_dropout, n_epochs)\n", 224 | " def dnn_evaluate(num_units,frac_dropout,n_epochs):\n", 225 | " num_units=int(num_units) #Put in proper format (Bayesian optimization uses floats, and we just want to test the integer)\n", 226 | " frac_dropout=float(frac_dropout) #Put in proper format\n", 227 | " n_epochs=int(n_epochs) #Put in proper format\n", 228 | " model_dnn=DenseNNDecoder(units=[num_units,num_units],dropout=frac_dropout,num_epochs=n_epochs) #Define model\n", 229 | " model_dnn.fit(X_train,y_train) #Fit model\n", 230 | " y_valid_predicted_dnn=model_dnn.predict(X_valid) #Get validation set predictions\n", 231 | " return np.mean(get_R2(y_valid,y_valid_predicted_dnn)) #Return mean validation set R2\n", 232 | "\n", 233 | " #Do bayesian optimization\n", 234 | " dnnBO = BayesianOptimization(dnn_evaluate, {'num_units': (3, 50), 'frac_dropout': (0,.5), 'n_epochs': (2,10)}, verbose=0) #Define Bayesian optimization, and set limits of hyperparameters\n", 235 | " #Set number of initial runs and subsequent tests, and do the optimization. Also, we set kappa=10 (greater than the default) so there is more exploration when there are more hyperparameters\n", 236 | " dnnBO.maximize(init_points=10, n_iter=15, kappa=10)\n", 237 | " best_params=dnnBO.res['max']['max_params'] #Get the hyperparameters that give rise to the best fit\n", 238 | " num_units=np.int(best_params['num_units']) #We want the integer value associated with the best \"num_units\" parameter (which is what the xgb_evaluate function does above)\n", 239 | " frac_dropout=float(best_params['frac_dropout'])\n", 240 | " n_epochs=np.int(best_params['n_epochs']) \n", 241 | "\n", 242 | " # Run model w/ above hyperparameters\n", 243 | " \n", 244 | " model_dnn=DenseNNDecoder(units=[num_units,num_units],dropout=frac_dropout,num_epochs=n_epochs) #Declare model w/ fit hyperparameters\n", 245 | " model_dnn.fit(X_train,y_train) #Fit model\n", 246 | " y_test_predicted_dnn=model_dnn.predict(X_test) #Get test set predictions\n", 247 | " mean_r2_dnn[i,j]=np.mean(get_R2(y_test,y_test_predicted_dnn)) #Get test set R2 \n", 248 | " #Print R2 values\n", 249 | " R2s_dnn=get_R2(y_test,y_test_predicted_dnn)\n", 250 | " print('R2s:', R2s_dnn) \n", 251 | " \n", 252 | " \n", 253 | " y_pred_ensemble_all.append(y_test_predicted_dnn) # #Add test set predictions to list (for saving) \n", 254 | "\n", 255 | "mean_r2_ensemble=np.mean(mean_r2_dnn,axis=1) #Get mean R2 value for each fold (across x and y predictions)\n", 256 | "\n", 257 | "#Save data\n", 258 | "with open(save_folder+dataset+'_results_ensemble_dnn2.pickle','wb') as f:\n", 259 | " pickle.dump([mean_r2_ensemble,y_pred_ensemble_all],f)" 260 | ] 261 | } 262 | ], 263 | "metadata": { 264 | "kernelspec": { 265 | "display_name": "Python [Root]", 266 | "language": "python", 267 | "name": "Python [Root]" 268 | }, 269 | "language_info": { 270 | "codemirror_mode": { 271 | "name": "ipython", 272 | "version": 2 273 | }, 274 | "file_extension": ".py", 275 | "mimetype": "text/x-python", 276 | "name": "python", 277 | "nbconvert_exporter": "python", 278 | "pygments_lexer": "ipython2", 279 | "version": "2.7.12" 280 | } 281 | }, 282 | "nbformat": 4, 283 | "nbformat_minor": 0 284 | } 285 | -------------------------------------------------------------------------------- /Paper_code/Hyperparameter_Sensitivity.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Testing sensitivity of results to changes in hyperparameters\n", 8 | "\n", 9 | "We do this test on a feedforward neural network" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## User Options" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "Define what folder you're saving to" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "metadata": { 30 | "collapsed": true 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "# save_folder=''\n", 35 | "save_folder='/home/jglaser/Files/Neural_Decoding/Results/'" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "Define what folder you're loading from" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": { 49 | "collapsed": true 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "# load_folder=''\n", 54 | "load_folder='/home/jglaser/Data/DecData/'" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "Define which dataset you're using" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "metadata": { 68 | "collapsed": true 69 | }, 70 | "outputs": [], 71 | "source": [ 72 | "# dataset='s1'\n", 73 | "dataset='m1'\n", 74 | "# dataset='hc'" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "Define how much training data to use" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "metadata": { 88 | "collapsed": true 89 | }, 90 | "outputs": [], 91 | "source": [ 92 | "# data_amt='full'\n", 93 | "data_amt='limited'" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "## 1. Import Packages\n", 101 | "\n", 102 | "We import both standard packages, and functions from the accompanying .py files" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 5, 108 | "metadata": { 109 | "collapsed": false 110 | }, 111 | "outputs": [ 112 | { 113 | "name": "stderr", 114 | "output_type": "stream", 115 | "text": [ 116 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/statsmodels/compat/pandas.py:56: FutureWarning: The pandas.core.datetools module is deprecated and will be removed in a future version. Please use the pandas.tseries module instead.\n", 117 | " from pandas.core import datetools\n", 118 | "Using Theano backend.\n", 119 | "WARNING (theano.sandbox.cuda): The cuda backend is deprecated and will be removed in the next release (v0.10). Please switch to the gpuarray backend. You can get more information about how to switch at this URL:\n", 120 | " https://github.com/Theano/Theano/wiki/Converting-to-the-new-gpu-back-end%28gpuarray%29\n", 121 | "\n", 122 | "Using gpu device 0: GeForce GTX TITAN X (CNMeM is enabled with initial size: 45.0% of memory, cuDNN Mixed dnn version. The header is from one version, but we link with a different version (5103, 5110))\n", 123 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", 124 | " from ._conv import register_converters as _register_converters\n" 125 | ] 126 | } 127 | ], 128 | "source": [ 129 | "#Import standard packages\n", 130 | "import numpy as np\n", 131 | "import matplotlib.pyplot as plt\n", 132 | "%matplotlib inline\n", 133 | "from scipy import io\n", 134 | "from scipy import stats\n", 135 | "import pickle\n", 136 | "import time\n", 137 | "import sys\n", 138 | "\n", 139 | "#Add the main folder to the path, so we have access to the files there.\n", 140 | "#Note that if your working directory is not the Paper_code folder, you may need to manually specify the path to the main folder. For example: sys.path.append('/home/jglaser/GitProj/Neural_Decoding')\n", 141 | "sys.path.append('..') \n", 142 | "\n", 143 | "#Import function to get the covariate matrix that includes spike history from previous bins\n", 144 | "from preprocessing_funcs import get_spikes_with_history\n", 145 | "\n", 146 | "#Import metrics\n", 147 | "from metrics import get_R2\n", 148 | "from metrics import get_rho\n", 149 | "\n", 150 | "#Import decoder functions\n", 151 | "from decoders import WienerCascadeDecoder\n", 152 | "from decoders import WienerFilterDecoder\n", 153 | "from decoders import DenseNNDecoder\n", 154 | "from decoders import SimpleRNNDecoder\n", 155 | "from decoders import GRUDecoder\n", 156 | "from decoders import LSTMDecoder\n", 157 | "from decoders import XGBoostDecoder\n", 158 | "from decoders import SVRDecoder\n", 159 | "\n", 160 | "#Import Bayesian Optimization package\n", 161 | "from bayes_opt import BayesianOptimization" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 6, 167 | "metadata": { 168 | "collapsed": true 169 | }, 170 | "outputs": [], 171 | "source": [ 172 | "#Turn off deprecation warnings\n", 173 | "\n", 174 | "import warnings\n", 175 | "warnings.filterwarnings(\"ignore\", category=DeprecationWarning) " 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "## 2. Load Data\n", 183 | "\n", 184 | "The data that we load is in the format described below. We have another example script, \"neural_preprocessing.py\" that may be helpful towards putting the data in this format.\n", 185 | "\n", 186 | "Neural data should be a matrix of size \"number of time bins\" x \"number of neurons\", where each entry is the firing rate of a given neuron in a given time bin\n", 187 | "\n", 188 | "The output you are decoding should be a matrix of size \"number of time bins\" x \"number of features you are decoding\"" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 7, 194 | "metadata": { 195 | "collapsed": false 196 | }, 197 | "outputs": [], 198 | "source": [ 199 | "if dataset=='s1':\n", 200 | " with open(load_folder+'s1_test_data.pickle','rb') as f:\n", 201 | " # neural_data,vels_binned,pos_binned,acc_binned=pickle.load(f,encoding='latin1')\n", 202 | " neural_data,vels_binned,pos_binned,acc_binned=pickle.load(f)\n", 203 | " \n", 204 | "if dataset=='m1':\n", 205 | " with open(load_folder+'m1_test_data.pickle','rb') as f:\n", 206 | " # neural_data,vels_binned,pos_binned,acc_binned=pickle.load(f,encoding='latin1')\n", 207 | " neural_data,vels_binned,pos_binned,acc_binned=pickle.load(f)\n", 208 | " \n", 209 | "if dataset=='hc':\n", 210 | " with open(load_folder+'hc_test_data.pickle','rb') as f:\n", 211 | " # neural_data,vels_binned,pos_binned,acc_binned=pickle.load(f,encoding='latin1')\n", 212 | " neural_data,pos_binned=pickle.load(f)" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "## 3. Preprocess Data" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "### 3A. User Inputs\n", 227 | "The user can define what time period to use spikes from (with respect to the output)." 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 8, 233 | "metadata": { 234 | "collapsed": true 235 | }, 236 | "outputs": [], 237 | "source": [ 238 | "if dataset=='s1':\n", 239 | " bins_before=6 #How many bins of neural data prior to the output are used for decoding\n", 240 | " bins_current=1 #Whether to use concurrent time bin of neural data\n", 241 | " bins_after=6 #How many bins of neural data after (and including) the output are used for decoding\n", 242 | " \n", 243 | "if dataset=='m1':\n", 244 | " bins_before=13 #How many bins of neural data prior to the output are used for decoding\n", 245 | " bins_current=1 #Whether to use concurrent time bin of neural data\n", 246 | " bins_after=0 #How many bins of neural data after (and including) the output are used for decoding\n", 247 | " \n", 248 | "if dataset=='hc':\n", 249 | " bins_before=4 #How many bins of neural data prior to the output are used for decoding\n", 250 | " bins_current=1 #Whether to use concurrent time bin of neural data\n", 251 | " bins_after=5 #How many bins of neural data after (and including) the output are used for decoding" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "### 3B. Format Covariates" 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "#### Format Input Covariates" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": 9, 271 | "metadata": { 272 | "collapsed": true 273 | }, 274 | "outputs": [], 275 | "source": [ 276 | "#Remove neurons with too few spikes in HC dataset\n", 277 | "if dataset=='hc':\n", 278 | " nd_sum=np.nansum(neural_data,axis=0)\n", 279 | " rmv_nrn=np.where(nd_sum<100)\n", 280 | " neural_data=np.delete(neural_data,rmv_nrn,1)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 10, 286 | "metadata": { 287 | "collapsed": false 288 | }, 289 | "outputs": [], 290 | "source": [ 291 | "# Format for recurrent neural networks (SimpleRNN, GRU, LSTM)\n", 292 | "# Function to get the covariate matrix that includes spike history from previous bins\n", 293 | "X=get_spikes_with_history(neural_data,bins_before,bins_after,bins_current)\n", 294 | "\n", 295 | "# Format for Wiener Filter, Wiener Cascade, XGBoost, and Dense Neural Network\n", 296 | "#Put in \"flat\" format, so each \"neuron / time\" is a single feature\n", 297 | "X_flat=X.reshape(X.shape[0],(X.shape[1]*X.shape[2]))" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "#### Format Output Covariates" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 11, 310 | "metadata": { 311 | "collapsed": false 312 | }, 313 | "outputs": [], 314 | "source": [ 315 | "#Set decoding output\n", 316 | "if dataset=='s1' or dataset=='m1':\n", 317 | " y=vels_binned\n", 318 | "if dataset=='hc':\n", 319 | " y=pos_binned" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "#### In HC dataset, remove time bins with no output (y value)" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 12, 332 | "metadata": { 333 | "collapsed": true 334 | }, 335 | "outputs": [], 336 | "source": [ 337 | "if dataset=='hc':\n", 338 | " #Remove time bins with no output (y value)\n", 339 | " rmv_time=np.where(np.isnan(y[:,0]) | np.isnan(y[:,1]))\n", 340 | " X=np.delete(X,rmv_time,0)\n", 341 | " X_flat=np.delete(X_flat,rmv_time,0)\n", 342 | " y=np.delete(y,rmv_time,0)" 343 | ] 344 | }, 345 | { 346 | "cell_type": "markdown", 347 | "metadata": {}, 348 | "source": [ 349 | "### 3C. Define training/testing/validation sets\n", 350 | "We use the same testing/validation sets used in Fig. 6. The training size varies depending on the user choice of 'full' or 'limited'" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 13, 356 | "metadata": { 357 | "collapsed": true 358 | }, 359 | "outputs": [], 360 | "source": [ 361 | "if dataset=='s1' or dataset=='m1':\n", 362 | " dt=.05\n", 363 | "if dataset=='hc':\n", 364 | " dt=.2\n", 365 | "\n", 366 | "if dataset=='hc':\n", 367 | "\n", 368 | " #Size of sets\n", 369 | " test_size=int(450/dt) #7.5 min\n", 370 | " valid_size=test_size #validation size is the same as the test size\n", 371 | " if data_amt=='full':\n", 372 | " train_size=int(2250/dt) #37.5 minutes\n", 373 | " if data_amt=='limited':\n", 374 | " train_size=int(900/dt) #15 minutes\n", 375 | " \n", 376 | " #End indices\n", 377 | " end_idx=np.int(X.shape[0]*.8) #End of test set\n", 378 | " tr_end_idx=end_idx-test_size-valid_size #End of training set\n", 379 | "\n", 380 | "if dataset=='s1':\n", 381 | " #Size of sets\n", 382 | " test_size=int(300/dt) #5 min\n", 383 | " valid_size=test_size #validation size is the same as the test size\n", 384 | " if data_amt=='full':\n", 385 | " train_size=int(1200/dt) # 20 min \n", 386 | " if data_amt=='limited':\n", 387 | " train_size=int(60/dt) #1 min\n", 388 | "\n", 389 | " #End indices\n", 390 | " end_idx=np.int(X.shape[0]*.9)#End of test set\n", 391 | " tr_end_idx=end_idx-test_size-valid_size #End of training set\n", 392 | "\n", 393 | "if dataset=='m1':\n", 394 | " #Size of sets\n", 395 | " test_size=int(300/dt) #5 min\n", 396 | " valid_size=test_size #validation size is the same as the test size\n", 397 | " if data_amt=='full':\n", 398 | " train_size=int(600/dt) # 10 min \n", 399 | " if data_amt=='limited':\n", 400 | " train_size=int(60/dt) #1 min\n", 401 | " \n", 402 | " #End indices\n", 403 | " end_idx=np.int(X.shape[0]*1)#End of test set\n", 404 | " tr_end_idx=end_idx-test_size-valid_size #End of training set\n", 405 | " \n", 406 | " \n", 407 | "#Range of sets\n", 408 | "testing_range=[end_idx-test_size,end_idx] #Testing set (length of test_size, goes up until end_idx)\n", 409 | "valid_range=[end_idx-test_size-valid_size,end_idx-test_size] #Validation set (length of valid_size, goes up until beginning of test set)\n", 410 | "training_range=[tr_end_idx-train_size,tr_end_idx] #Training set (length of train_size, goes up until beginning of validation set)\n" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "metadata": {}, 416 | "source": [ 417 | "Extract different sets" 418 | ] 419 | }, 420 | { 421 | "cell_type": "code", 422 | "execution_count": 14, 423 | "metadata": { 424 | "collapsed": true 425 | }, 426 | "outputs": [], 427 | "source": [ 428 | "#Note that all sets have a buffer of\"bins_before\" bins at the beginning, and \"bins_after\" bins at the end\n", 429 | "#This makes it so that the different sets don't include overlapping neural data\n", 430 | "\n", 431 | "#Testing set\n", 432 | "testing_set=np.arange(testing_range[0]+bins_before,testing_range[1]-bins_after)\n", 433 | "\n", 434 | "#Validation set\n", 435 | "valid_set=np.arange(valid_range[0]+bins_before,valid_range[1]-bins_after)\n", 436 | "\n", 437 | "#Training_set\n", 438 | "training_set=np.arange(training_range[0]+bins_before,training_range[1]-bins_after)\n", 439 | "\n", 440 | "\n", 441 | "#Get training data\n", 442 | "X_train=X[training_set,:,:]\n", 443 | "X_flat_train=X_flat[training_set,:]\n", 444 | "y_train=y[training_set,:]\n", 445 | "\n", 446 | "#Get testing data\n", 447 | "X_test=X[testing_set,:,:]\n", 448 | "X_flat_test=X_flat[testing_set,:]\n", 449 | "y_test=y[testing_set,:]\n", 450 | "\n", 451 | "#Get validation data\n", 452 | "X_valid=X[valid_set,:,:]\n", 453 | "X_flat_valid=X_flat[valid_set,:]\n", 454 | "y_valid=y[valid_set,:]" 455 | ] 456 | }, 457 | { 458 | "cell_type": "markdown", 459 | "metadata": {}, 460 | "source": [ 461 | "### 3D. Preprocess Data" 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": 15, 467 | "metadata": { 468 | "collapsed": true 469 | }, 470 | "outputs": [], 471 | "source": [ 472 | "#Z-score \"X\" inputs. \n", 473 | "X_train_mean=np.nanmean(X_train,axis=0)\n", 474 | "X_train_std=np.nanstd(X_train,axis=0)\n", 475 | "X_train=(X_train-X_train_mean)/X_train_std\n", 476 | "X_test=(X_test-X_train_mean)/X_train_std\n", 477 | "X_valid=(X_valid-X_train_mean)/X_train_std\n", 478 | "\n", 479 | "#Z-score \"X_flat\" inputs. \n", 480 | "X_flat_train_mean=np.nanmean(X_flat_train,axis=0)\n", 481 | "X_flat_train_std=np.nanstd(X_flat_train,axis=0)\n", 482 | "X_flat_train=(X_flat_train-X_flat_train_mean)/X_flat_train_std\n", 483 | "X_flat_test=(X_flat_test-X_flat_train_mean)/X_flat_train_std\n", 484 | "X_flat_valid=(X_flat_valid-X_flat_train_mean)/X_flat_train_std\n", 485 | "\n", 486 | "#Zero-center outputs\n", 487 | "y_train_mean=np.nanmean(y_train,axis=0)\n", 488 | "y_train=y_train-y_train_mean\n", 489 | "y_test=y_test-y_train_mean\n", 490 | "y_valid=y_valid-y_train_mean" 491 | ] 492 | }, 493 | { 494 | "cell_type": "markdown", 495 | "metadata": {}, 496 | "source": [ 497 | "## 4. Run Decoder" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": {}, 503 | "source": [ 504 | "Set range of hyperparameters we're testing in the feedforward neural network" 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 16, 510 | "metadata": { 511 | "collapsed": true 512 | }, 513 | "outputs": [], 514 | "source": [ 515 | "n_epochs=10 #Number of epochs\n", 516 | "\n", 517 | "num_unit_set=[100,200,300,400,500,600,700,800,900,1000] #Number of units in each layer\n", 518 | "\n", 519 | "frac_dropout_set=[0,.1,.2,.3,.4,.5] #Amount of dropout" 520 | ] 521 | }, 522 | { 523 | "cell_type": "markdown", 524 | "metadata": {}, 525 | "source": [ 526 | "Loop through hyperparameter combinations and get R2 values" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 17, 532 | "metadata": { 533 | "collapsed": false, 534 | "scrolled": true 535 | }, 536 | "outputs": [ 537 | { 538 | "name": "stderr", 539 | "output_type": "stream", 540 | "text": [ 541 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/keras/models.py:826: UserWarning: The `nb_epoch` argument in `fit` has been renamed `epochs`.\n", 542 | " warnings.warn('The `nb_epoch` argument in `fit` '\n" 543 | ] 544 | }, 545 | { 546 | "name": "stdout", 547 | "output_type": "stream", 548 | "text": [ 549 | "(100, 0, 0.696266015291161)\n", 550 | "(100, 0.1, 0.7033545101618082)\n", 551 | "(100, 0.2, 0.6956054052157566)\n", 552 | "(100, 0.3, 0.6719583691717945)\n", 553 | "(100, 0.4, 0.6796487774658623)\n", 554 | "(100, 0.5, 0.6225763920657579)\n", 555 | "(200, 0, 0.7090122002037368)\n", 556 | "(200, 0.1, 0.724355016440842)\n", 557 | "(200, 0.2, 0.6952499221356006)\n", 558 | "(200, 0.3, 0.692374309345424)\n", 559 | "(200, 0.4, 0.6479185130659582)\n", 560 | "(200, 0.5, 0.6486283578332661)\n", 561 | "(300, 0, 0.7146423304756662)\n", 562 | "(300, 0.1, 0.69538091192814)\n", 563 | "(300, 0.2, 0.6851423606387953)\n", 564 | "(300, 0.3, 0.6827551365455837)\n", 565 | "(300, 0.4, 0.6779294285707755)\n", 566 | "(300, 0.5, 0.6403877897393295)\n", 567 | "(400, 0, 0.7120193298523239)\n", 568 | "(400, 0.1, 0.7071591935809334)\n", 569 | "(400, 0.2, 0.6891463804390167)\n", 570 | "(400, 0.3, 0.6938406292981216)\n", 571 | "(400, 0.4, 0.6773059045749703)\n", 572 | "(400, 0.5, 0.6260958065035958)\n", 573 | "(500, 0, 0.7194149311187766)\n", 574 | "(500, 0.1, 0.7200126553784618)\n", 575 | "(500, 0.2, 0.7045135162258749)\n", 576 | "(500, 0.3, 0.6594607153607601)\n", 577 | "(500, 0.4, 0.66106637925329)\n", 578 | "(500, 0.5, 0.6182934684155306)\n", 579 | "(600, 0, 0.719556777048681)\n", 580 | "(600, 0.1, 0.7020481176590975)\n", 581 | "(600, 0.2, 0.7139734644439857)\n", 582 | "(600, 0.3, 0.6861445965936334)\n", 583 | "(600, 0.4, 0.6925090481199061)\n", 584 | "(600, 0.5, 0.6680695582598016)\n", 585 | "(700, 0, 0.7142090156590137)\n", 586 | "(700, 0.1, 0.6944242262618289)\n", 587 | "(700, 0.2, 0.6884897730147493)\n", 588 | "(700, 0.3, 0.6859925693250142)\n", 589 | "(700, 0.4, 0.6884947932950439)\n", 590 | "(700, 0.5, 0.6455690280945128)\n", 591 | "(800, 0, 0.720230728832145)\n", 592 | "(800, 0.1, 0.7058865471788756)\n", 593 | "(800, 0.2, 0.6897513026769426)\n", 594 | "(800, 0.3, 0.7043806565516766)\n", 595 | "(800, 0.4, 0.6862137734758292)\n", 596 | "(800, 0.5, 0.6352009056013014)\n", 597 | "(900, 0, 0.7265082283938731)\n", 598 | "(900, 0.1, 0.7345256624865044)\n", 599 | "(900, 0.2, 0.6658855795260186)\n", 600 | "(900, 0.3, 0.7160978516170698)\n", 601 | "(900, 0.4, 0.710970919556602)\n", 602 | "(900, 0.5, 0.6538078791960524)\n", 603 | "(1000, 0, 0.72137748062087)\n", 604 | "(1000, 0.1, 0.733214083249062)\n", 605 | "(1000, 0.2, 0.7047172194176152)\n", 606 | "(1000, 0.3, 0.6988248768734857)\n", 607 | "(1000, 0.4, 0.6528963633680827)\n", 608 | "(1000, 0.5, 0.6432651616368713)\n" 609 | ] 610 | } 611 | ], 612 | "source": [ 613 | "#Initialize matrix that tracks the R2 values for \n", 614 | "r2_vals=np.empty([len(num_unit_set),len(frac_dropout_set)]) \n", 615 | "\n", 616 | "#Loop through hyperparameter combinations\n", 617 | "#i is the index for the number of units\n", 618 | "#j is the index for the amount of dropout\n", 619 | "\n", 620 | "i=-1\n", 621 | "for num_units in num_unit_set: #Loop through number of units\n", 622 | " i=i+1 #Iterate index for number of units\n", 623 | " j=-1\n", 624 | " for frac_dropout in frac_dropout_set: #Loop through amount of dropout\n", 625 | " j=j+1 #Iterate index for amount of dropout\n", 626 | " # Run model w/ above hyperparameters\n", 627 | " model_dnn=DenseNNDecoder(units=[num_units,num_units],dropout=frac_dropout,num_epochs=n_epochs) #Declare decoder\n", 628 | " model_dnn.fit(X_flat_train,y_train) #Fit decoder\n", 629 | " y_test_predicted_dnn=model_dnn.predict(X_flat_test) #Get test set predictions\n", 630 | " r2_vals[i,j]=np.mean(get_R2(y_test,y_test_predicted_dnn)) #Get R2 value on test set, and put in matrix\n", 631 | "\n", 632 | " print(num_units,frac_dropout,r2_vals[i,j])" 633 | ] 634 | }, 635 | { 636 | "cell_type": "markdown", 637 | "metadata": {}, 638 | "source": [ 639 | "## Save" 640 | ] 641 | }, 642 | { 643 | "cell_type": "code", 644 | "execution_count": 18, 645 | "metadata": { 646 | "collapsed": false 647 | }, 648 | "outputs": [], 649 | "source": [ 650 | "with open(save_folder+dataset+'_hyperparam_sensitivity'+data_amt+'.pickle','wb') as f:\n", 651 | " pickle.dump([r2_vals,num_unit_set,frac_dropout_set],f)" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "metadata": {}, 657 | "source": [ 658 | "## Quick Check of Results" 659 | ] 660 | }, 661 | { 662 | "cell_type": "code", 663 | "execution_count": 20, 664 | "metadata": { 665 | "collapsed": false 666 | }, 667 | "outputs": [ 668 | { 669 | "name": "stdout", 670 | "output_type": "stream", 671 | "text": [ 672 | "[[0.69626602 0.70335451 0.69560541 0.67195837 0.67964878 0.62257639]\n", 673 | " [0.7090122 0.72435502 0.69524992 0.69237431 0.64791851 0.64862836]\n", 674 | " [0.71464233 0.69538091 0.68514236 0.68275514 0.67792943 0.64038779]\n", 675 | " [0.71201933 0.70715919 0.68914638 0.69384063 0.6773059 0.62609581]\n", 676 | " [0.71941493 0.72001266 0.70451352 0.65946072 0.66106638 0.61829347]\n", 677 | " [0.71955678 0.70204812 0.71397346 0.6861446 0.69250905 0.66806956]\n", 678 | " [0.71420902 0.69442423 0.68848977 0.68599257 0.68849479 0.64556903]\n", 679 | " [0.72023073 0.70588655 0.6897513 0.70438066 0.68621377 0.63520091]\n", 680 | " [0.72650823 0.73452566 0.66588558 0.71609785 0.71097092 0.65380788]\n", 681 | " [0.72137748 0.73321408 0.70471722 0.69882488 0.65289636 0.64326516]]\n" 682 | ] 683 | } 684 | ], 685 | "source": [ 686 | "print(r2_vals)" 687 | ] 688 | }, 689 | { 690 | "cell_type": "code", 691 | "execution_count": null, 692 | "metadata": { 693 | "collapsed": true 694 | }, 695 | "outputs": [], 696 | "source": [] 697 | } 698 | ], 699 | "metadata": { 700 | "anaconda-cloud": {}, 701 | "kernelspec": { 702 | "display_name": "Python [Root]", 703 | "language": "python", 704 | "name": "Python [Root]" 705 | }, 706 | "language_info": { 707 | "codemirror_mode": { 708 | "name": "ipython", 709 | "version": 2 710 | }, 711 | "file_extension": ".py", 712 | "mimetype": "text/x-python", 713 | "name": "python", 714 | "nbconvert_exporter": "python", 715 | "pygments_lexer": "ipython2", 716 | "version": "2.7.14" 717 | } 718 | }, 719 | "nbformat": 4, 720 | "nbformat_minor": 0 721 | } 722 | -------------------------------------------------------------------------------- /Paper_code/KF_BinSize.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Kalman Filter with changing bin sizes" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## User Options" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Folder you're saving to" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": { 28 | "collapsed": true 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "# save_folder=''\n", 33 | "save_folder='/home/jglaser/Files/Neural_Decoding/Results/'" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "Folder you're loading from" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 2, 46 | "metadata": { 47 | "collapsed": true 48 | }, 49 | "outputs": [], 50 | "source": [ 51 | "# load_folder=''\n", 52 | "load_folder='/home/jglaser/Data/DecData/'" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "Dataset you're using" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": { 66 | "collapsed": true 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "# dataset='s1'\n", 71 | "# dataset='m1'\n", 72 | "dataset='hc'" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "## 1. Import Packages\n", 80 | "\n", 81 | "We import both standard packages, and functions from the accompanying .py files" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "metadata": { 88 | "collapsed": false 89 | }, 90 | "outputs": [ 91 | { 92 | "name": "stderr", 93 | "output_type": "stream", 94 | "text": [ 95 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/statsmodels/compat/pandas.py:56: FutureWarning: The pandas.core.datetools module is deprecated and will be removed in a future version. Please use the pandas.tseries module instead.\n", 96 | " from pandas.core import datetools\n", 97 | "Using Theano backend.\n", 98 | "WARNING (theano.sandbox.cuda): The cuda backend is deprecated and will be removed in the next release (v0.10). Please switch to the gpuarray backend. You can get more information about how to switch at this URL:\n", 99 | " https://github.com/Theano/Theano/wiki/Converting-to-the-new-gpu-back-end%28gpuarray%29\n", 100 | "\n", 101 | "Using gpu device 0: GeForce GTX TITAN X (CNMeM is enabled with initial size: 45.0% of memory, cuDNN Mixed dnn version. The header is from one version, but we link with a different version (5103, 5110))\n", 102 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", 103 | " from ._conv import register_converters as _register_converters\n" 104 | ] 105 | } 106 | ], 107 | "source": [ 108 | "#Import standard packages\n", 109 | "import numpy as np\n", 110 | "import matplotlib.pyplot as plt\n", 111 | "%matplotlib inline\n", 112 | "from scipy import io\n", 113 | "from scipy import stats\n", 114 | "import pickle\n", 115 | "import sys\n", 116 | "import time\n", 117 | "\n", 118 | "#Add the main folder to the path, so we have access to the files there.\n", 119 | "#Note that if your working directory is not the Paper_code folder, you may need to manually specify the path to the main folder. For example: sys.path.append('/home/jglaser/GitProj/Neural_Decoding')\n", 120 | "sys.path.append('..') \n", 121 | "\n", 122 | "#Import function to get the covariate matrix that includes spike history from previous bins\n", 123 | "from preprocessing_funcs import get_spikes_with_history\n", 124 | "\n", 125 | "###Import functions for binning data for preprocessing###\n", 126 | "from preprocessing_funcs import bin_spikes\n", 127 | "from preprocessing_funcs import bin_output\n", 128 | "\n", 129 | "#Import metrics\n", 130 | "from metrics import get_R2\n", 131 | "from metrics import get_rho\n", 132 | "\n", 133 | "#Import decoder functions\n", 134 | "from decoders import KalmanFilterDecoder\n", 135 | "\n", 136 | "from bayes_opt import BayesianOptimization" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 5, 142 | "metadata": { 143 | "collapsed": true 144 | }, 145 | "outputs": [], 146 | "source": [ 147 | "#Turn off deprecation warnings\n", 148 | "\n", 149 | "import warnings\n", 150 | "warnings.filterwarnings(\"ignore\", category=DeprecationWarning) " 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "## 2. Load Data\n", 158 | "\n", 159 | "Here, we load data in the more raw format, since the preprocessing depends on the bin size (which we will vary later on)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 6, 165 | "metadata": { 166 | "collapsed": false 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "if dataset=='s1': \n", 171 | " data=io.loadmat(load_folder+'s1_data_raw.mat') \n", 172 | " spike_times=data['spike_times'] #Load spike times of all neurons\n", 173 | " vels=data['vels'] #Load x and y velocities\n", 174 | " vel_times=data['vel_times'] #Load times at which velocities were recorded\n", 175 | "\n", 176 | "if dataset=='m1': \n", 177 | " data=io.loadmat(load_folder+'m1_data_raw.mat') \n", 178 | " spike_times=data['spike_times'] #Load spike times of all neurons\n", 179 | " vels=data['vels'] #Load x and y velocities\n", 180 | " vel_times=data['vel_times'] #Load times at which velocities were recorded \n", 181 | " \n", 182 | "if dataset=='hc':\n", 183 | " data=io.loadmat(load_folder+'hc_data_raw.mat')\n", 184 | " spike_times=data['spike_times'] #Load spike times of all neurons\n", 185 | " pos=data['pos'] #Load x and y positions\n", 186 | " pos_times=data['pos_times'][0] #Load times at which positions were recorded \n" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "## 3. Preprocess Data" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "Options for extracting the data (these are copied from the preprocessing file)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 7, 206 | "metadata": { 207 | "collapsed": false 208 | }, 209 | "outputs": [], 210 | "source": [ 211 | "## USER OPTIONS\n", 212 | "if dataset=='s1' or dataset=='m1':\n", 213 | " t_start=vel_times[0] #Time to start extracting data - here the first time velocity was recorded\n", 214 | " t_end=vel_times[-1] #Time to finish extracting data - here the last time velocity was recorded\n", 215 | " \n", 216 | "if dataset=='hc': \n", 217 | " t_start=pos_times[0] #Time to start extracting data - here the first time position was recorded\n", 218 | " t_end=5608\n", 219 | " \n", 220 | "downsample_factor=1 #Downsampling of output (to make binning go faster). 1 means no downsampling.\n", 221 | "\n", 222 | "\n", 223 | "#When loading the Matlab cell \"spike_times\", Python puts it in a format with an extra unnecessary dimension\n", 224 | "#First, we will put spike_times in a cleaner format: an array of arrays\n", 225 | "spike_times=np.squeeze(spike_times)\n", 226 | "for i in range(spike_times.shape[0]):\n", 227 | " spike_times[i]=np.squeeze(spike_times[i])" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": { 233 | "collapsed": true 234 | }, 235 | "source": [ 236 | "Set the bin sizes we will test" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 8, 242 | "metadata": { 243 | "collapsed": true 244 | }, 245 | "outputs": [], 246 | "source": [ 247 | "if dataset=='s1' or dataset=='m1':\n", 248 | " dts=[.01,.02,.03,.04,.05,.1] #Size of time bins (in seconds)\n", 249 | "\n", 250 | "if dataset=='hc':\n", 251 | " dts=[.03,.05,.1,.2,.4] #Size of time bins (in seconds)\n", 252 | "\n", 253 | "num_folds=len(dts) #Number of loops we'll do (I'm just calling it \"folds\" so I can keep old code that used CV folds)" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "Initializations of lists/vectors" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 9, 266 | "metadata": { 267 | "collapsed": false 268 | }, 269 | "outputs": [], 270 | "source": [ 271 | "mean_r2_kf=np.empty(num_folds)\n", 272 | "\n", 273 | "y_kf_test_all=[]\n", 274 | "y_kf_train_all=[]\n", 275 | "y_kf_valid_all=[]\n", 276 | "\n", 277 | "y_pred_kf_all=[] #Test predictions\n", 278 | "y_train_pred_kf_all=[]\n", 279 | "y_valid_pred_kf_all=[]" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "## Loop over bin sizes and do everything!" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 10, 292 | "metadata": { 293 | "collapsed": false 294 | }, 295 | "outputs": [ 296 | { 297 | "name": "stdout", 298 | "output_type": "stream", 299 | "text": [ 300 | "0.43472132320544116\n" 301 | ] 302 | } 303 | ], 304 | "source": [ 305 | "for i in range(num_folds): #Loop over bin sizes\n", 306 | " dt=dts[i] #Get the bin size for the current loop\n", 307 | " \n", 308 | " \n", 309 | " #### FORMAT OUTPUT ####\n", 310 | " \n", 311 | " #Bin output (velocity) data using \"bin_output\" function\n", 312 | " if dataset=='s1' or dataset=='m1':\n", 313 | " vels_binned=bin_output(vels,vel_times,dt,t_start,t_end,downsample_factor)\n", 314 | " if dataset=='hc':\n", 315 | " pos_binned=bin_output(pos,pos_times,dt,t_start,t_end,downsample_factor)\n", 316 | " \n", 317 | " #For the Kalman filter, we use the position, velocity, and acceleration as outputs\n", 318 | " #Ultimately, we are only concerned with the goodness of fit of velocity (s1 or m1) or position (hc)\n", 319 | " #But using them all as covariates helps performance\n", 320 | "\n", 321 | " if dataset=='s1' or dataset=='m1':\n", 322 | "\n", 323 | " #We will now determine position\n", 324 | " pos_binned=np.zeros(vels_binned.shape) #Initialize \n", 325 | " pos_binned[0,:]=0 #Assume starting position is at [0,0]\n", 326 | " #Loop through time bins and determine positions based on the velocities\n", 327 | " for k in range(pos_binned.shape[0]-1): \n", 328 | " pos_binned[k+1,0]=pos_binned[k,0]+vels_binned[k,0]*dt\n", 329 | " pos_binned[k+1,1]=pos_binned[k,1]+vels_binned[k,1]*dt\n", 330 | "\n", 331 | " #We will now determine acceleration \n", 332 | " temp=np.diff(vels_binned,axis=0) #The acceleration is the difference in velocities across time bins \n", 333 | " acc_binned=np.concatenate((temp,temp[-1:,:]),axis=0) #Assume acceleration at last time point is same as 2nd to last\n", 334 | "\n", 335 | " #The final output covariates include position, velocity, and acceleration\n", 336 | " y_kf=np.concatenate((pos_binned,vels_binned,acc_binned),axis=1)\n", 337 | "\n", 338 | "\n", 339 | " if dataset=='hc':\n", 340 | "\n", 341 | " temp=np.diff(pos_binned,axis=0) #Velocity is the difference in positions across time bins\n", 342 | " vels_binned=np.concatenate((temp,temp[-1:,:]),axis=0) #Assume velocity at last time point is same as 2nd to last\n", 343 | "\n", 344 | " temp2=np.diff(vels_binned,axis=0) #The acceleration is the difference in velocities across time bins \n", 345 | " acc_binned=np.concatenate((temp2,temp2[-1:,:]),axis=0) #Assume acceleration at last time point is same as 2nd to last\n", 346 | "\n", 347 | " #The final output covariates include position, velocity, and acceleration\n", 348 | " y_kf=np.concatenate((pos_binned,vels_binned,acc_binned),axis=1) \n", 349 | "\n", 350 | " \n", 351 | " #### FORMAT INPUT ####\n", 352 | " \n", 353 | " #Bin neural data using \"bin_spikes\" function\n", 354 | " neural_data=bin_spikes(spike_times,dt,t_start,t_end)\n", 355 | " \n", 356 | " #Remove neurons with too few spikes in HC dataset\n", 357 | " if dataset=='hc':\n", 358 | " nd_sum=np.nansum(neural_data,axis=0)\n", 359 | " rmv_nrn=np.where(nd_sum<100)\n", 360 | " neural_data=np.delete(neural_data,rmv_nrn,1)\n", 361 | " \n", 362 | " \n", 363 | " #The covariate is simply the matrix of firing rates for all neurons over time\n", 364 | " X_kf=neural_data\n", 365 | " \n", 366 | " \n", 367 | " # In HC dataset, remove time bins with no output (y value)\n", 368 | " if dataset=='hc':\n", 369 | " #Remove time bins with no output (y value)\n", 370 | " rmv_time=np.where(np.isnan(y_kf[:,0]) | np.isnan(y_kf[:,1]))\n", 371 | " X_kf=np.delete(X_kf,rmv_time,0)\n", 372 | " y_kf=np.delete(y_kf,rmv_time,0)\n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " #### Define training/testing/validation sets ######\n", 377 | " \n", 378 | " if dataset=='hc':\n", 379 | "\n", 380 | " test_size=int(450/dt) #7.5 min\n", 381 | " end_idx=np.int(X_kf.shape[0]*.8)\n", 382 | " tr_end_idx=end_idx-2*test_size\n", 383 | " train_size=int(2250/dt)\n", 384 | "\n", 385 | "# testing_range=[end_idx-2*test_size,end_idx-test_size]\n", 386 | "# valid_range=[end_idx-test_size,end_idx]\n", 387 | "\n", 388 | " valid_range=[end_idx-2*test_size,end_idx-test_size]\n", 389 | " testing_range=[end_idx-test_size,end_idx]\n", 390 | " training_range=[tr_end_idx-train_size,tr_end_idx]\n", 391 | "\n", 392 | " if dataset=='s1' or dataset=='m1':\n", 393 | "\n", 394 | " if dataset=='s1':\n", 395 | " test_size=int(300/dt) #5 min\n", 396 | " end_idx=np.int(X_kf.shape[0]*.9)\n", 397 | " train_size=int(1200/dt) # 20 min\n", 398 | " if dataset=='m1':\n", 399 | " test_size=int(300/dt) #5 min\n", 400 | " end_idx=np.int(X_kf.shape[0]*1)\n", 401 | " train_size=int(600/dt) # 10 min\n", 402 | "\n", 403 | " tr_end_idx=end_idx-2*test_size\n", 404 | "\n", 405 | " valid_range=[end_idx-2*test_size,end_idx-test_size]\n", 406 | " testing_range=[end_idx-test_size,end_idx]\n", 407 | " training_range=[tr_end_idx-train_size,tr_end_idx] \n", 408 | " \n", 409 | "\n", 410 | " ###### RUN DECODERS #######\n", 411 | " \n", 412 | " t1=time.time()\n", 413 | "\n", 414 | " num_examples=X_kf.shape[0]\n", 415 | "\n", 416 | "\n", 417 | " ######### SPLIT DATA INTO TRAINING/TESTING/VALIDATION #########\n", 418 | " \n", 419 | "\n", 420 | " #Note that all sets have a buffer of 1 bins at the beginning, and 1 bins at the end\n", 421 | " #This makes it so that the different sets don't include overlapping neural data\n", 422 | " \n", 423 | " #Testing set\n", 424 | " testing_set=np.arange(testing_range[0]+1,testing_range[1]-1)\n", 425 | "\n", 426 | "\n", 427 | " #Validation set\n", 428 | " valid_set=np.arange(valid_range[0]+1,valid_range[1]-1)\n", 429 | "\n", 430 | " #Training_set\n", 431 | " training_set=np.arange(training_range[0]+1,training_range[1]-1)\n", 432 | "\n", 433 | " \n", 434 | " #Get training data\n", 435 | " X_kf_train=X_kf[training_set,:]\n", 436 | " y_kf_train=y_kf[training_set,:]\n", 437 | "\n", 438 | " #Get validation data\n", 439 | " X_kf_valid=X_kf[valid_set,:]\n", 440 | " y_kf_valid=y_kf[valid_set,:]\n", 441 | " \n", 442 | " #Get testing data\n", 443 | " X_kf_test=X_kf[testing_set,:]\n", 444 | " y_kf_test=y_kf[testing_set,:]\n", 445 | "\n", 446 | "\n", 447 | "\n", 448 | " #Preprocess data\n", 449 | " #Z-score \"X_kf\" inputs. \n", 450 | " X_kf_train_mean=np.nanmean(X_kf_train,axis=0)\n", 451 | " X_kf_train_std=np.nanstd(X_kf_train,axis=0)\n", 452 | " X_kf_train=(X_kf_train-X_kf_train_mean)/X_kf_train_std\n", 453 | " X_kf_test=(X_kf_test-X_kf_train_mean)/X_kf_train_std\n", 454 | " X_kf_valid=(X_kf_valid-X_kf_train_mean)/X_kf_train_std\n", 455 | "\n", 456 | " #Zero-center outputs\n", 457 | " y_kf_train_mean=np.nanmean(y_kf_train,axis=0)\n", 458 | " y_kf_train=y_kf_train-y_kf_train_mean\n", 459 | " y_kf_test=y_kf_test-y_kf_train_mean\n", 460 | " y_kf_valid=y_kf_valid-y_kf_train_mean \n", 461 | "\n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " ####### RUN KALMAN FILTER #######\n", 466 | "\n", 467 | " #We are going to loop through different lags, and for each lag: \n", 468 | " #-we will find the optimal hyperparameter C based on the validation set R2\n", 469 | " #-with that hyperparameter, we will get the validation set R2 for the given lag\n", 470 | " #We will determine the lag as the one that gives the best validation set R2\n", 471 | " #Finally, using the lag and hyperparameters determined (based on above), we will get the test set R2\n", 472 | " \n", 473 | " \n", 474 | " #First, we set the limits of lags that we will evaluate for each dataset\n", 475 | " if dataset=='hc':\n", 476 | " valid_lags=np.arange(int(-.5/dt),int(.5/dt)) #Lags to evaluate\n", 477 | " if dataset=='m1':\n", 478 | " valid_lags=np.arange(int(-.4/dt),1) #Lags to evaluate\n", 479 | " if dataset=='s1':\n", 480 | " valid_lags=np.arange(int(-.2/dt),int(.2/dt)) #Lags to evaluate\n", 481 | " num_valid_lags=valid_lags.shape[0] #Number of lags we will consider\n", 482 | " \n", 483 | " #Initializations\n", 484 | " lag_results=np.empty(num_valid_lags) #Array to store validation R2 results for each lag\n", 485 | " C_results=np.empty(num_valid_lags) #Array to store the best hyperparameter for each lag\n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " #### Wrapper function that returns the best validation set R2 for each lag\n", 491 | " #That is, for the given lag, it will find the best hyperparameters to maximize validation set R2\n", 492 | " #and the function returns that R2 value\n", 493 | " def kf_evaluate_lag(lag,X_kf_train,y_kf_train,X_kf_valid,y_kf_valid): \n", 494 | " \n", 495 | " #Re-align data to take lag into account\n", 496 | " if lag<0:\n", 497 | " y_kf_train=y_kf_train[-lag:,:]\n", 498 | " X_kf_train=X_kf_train[:lag,:]\n", 499 | " y_kf_valid=y_kf_valid[-lag:,:]\n", 500 | " X_kf_valid=X_kf_valid[:lag,:]\n", 501 | " if lag>0:\n", 502 | " y_kf_train=y_kf_train[0:-lag,:]\n", 503 | " X_kf_train=X_kf_train[lag:,:]\n", 504 | " y_kf_valid=y_kf_valid[0:-lag,:]\n", 505 | " X_kf_valid=X_kf_valid[lag:,:]\n", 506 | " \n", 507 | " #This is a function that evaluates the Kalman filter for the given hyperparameter C\n", 508 | " #and returns the R2 value for the hyperparameter. It's used within Bayesian optimization\n", 509 | " def kf_evaluate(C):\n", 510 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 511 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 512 | " y_valid_predicted_kf=model_kf.predict(X_kf_valid,y_kf_valid) #Get validation set predictions\n", 513 | " #Get validation set R2\n", 514 | " if dataset=='hc':\n", 515 | " return np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[0:2]) #Position is components 0 and 1\n", 516 | " if dataset=='m1' or dataset=='s1':\n", 517 | " return np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[2:4]) #Velocity is components 2 and 3\n", 518 | " \n", 519 | " #Do Bayesian optimization!\n", 520 | " kfBO = BayesianOptimization(kf_evaluate, {'C': (.5, 20)}, verbose=0) #Define Bayesian optimization, and set limits of hyperparameters\n", 521 | " kfBO.maximize(init_points=10, n_iter=10) #Set number of initial runs and subsequent tests, and do the optimization\n", 522 | " best_params=kfBO.res['max']['max_params'] #Get the hyperparameters that give rise to the best fit\n", 523 | " C=best_params['C']\n", 524 | "# print(\"C=\", C)\n", 525 | "\n", 526 | " #Get the validation set R2 using the best hyperparameters fit above: \n", 527 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 528 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 529 | " y_valid_predicted_kf=model_kf.predict(X_kf_valid,y_kf_valid) #Get validation set predictions\n", 530 | " #Get validation set R2\n", 531 | " if dataset=='hc':\n", 532 | " return [np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[0:2]), C] #Position is components 0 and 1\n", 533 | " if dataset=='m1' or dataset=='s1':\n", 534 | " return [np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[2:4]), C] #Velocity is components 2 and 3\n", 535 | " \n", 536 | " \n", 537 | " ### Loop through lags and get validation set R2 for each lag ####\n", 538 | " \n", 539 | " for j in range(num_valid_lags): \n", 540 | " valid_lag=valid_lags[j] #Set what lag you're using\n", 541 | " #Run the wrapper function, and put the R2 value and corresponding C (hyperparameter) in arrays\n", 542 | " [lag_results[j],C_results[j]]=kf_evaluate_lag(valid_lag,X_kf_train,y_kf_train,X_kf_valid,y_kf_valid)\n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " #### Get results on test set ####\n", 547 | " \n", 548 | " #Get the lag (and corresponding C value) that gave the best validation results\n", 549 | " lag=valid_lags[np.argmax(lag_results)] #The lag\n", 550 | "# print(\"lag=\",lag)\n", 551 | " C=C_results[np.argmax(lag_results)] #The hyperparameter C \n", 552 | " \n", 553 | " #Re-align data to take lag into account\n", 554 | " if lag<0:\n", 555 | " y_kf_train=y_kf_train[-lag:,:]\n", 556 | " X_kf_train=X_kf_train[:lag,:]\n", 557 | " y_kf_test=y_kf_test[-lag:,:]\n", 558 | " X_kf_test=X_kf_test[:lag,:]\n", 559 | " y_kf_valid=y_kf_valid[-lag:,:]\n", 560 | " X_kf_valid=X_kf_valid[:lag,:]\n", 561 | " if lag>0:\n", 562 | " y_kf_train=y_kf_train[0:-lag,:]\n", 563 | " X_kf_train=X_kf_train[lag:,:]\n", 564 | " y_kf_test=y_kf_test[0:-lag,:]\n", 565 | " X_kf_test=X_kf_test[lag:,:]\n", 566 | " y_kf_valid=y_kf_valid[0:-lag,:]\n", 567 | " X_kf_valid=X_kf_valid[lag:,:]\n", 568 | " \n", 569 | " #Run the Kalman filter\n", 570 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 571 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 572 | " y_test_predicted_kf=model_kf.predict(X_kf_test,y_kf_test) #Get test set predictions\n", 573 | " #Get test set R2 values and put them in arrays\n", 574 | " if dataset=='hc':\n", 575 | " mean_r2_kf[i]=np.mean(get_R2(y_kf_test,y_test_predicted_kf)[0:2]) #Position is components 0 and 1\n", 576 | " print(np.mean(get_R2(y_kf_test,y_test_predicted_kf)[0:2]))\n", 577 | " if dataset=='m1' or dataset=='s1':\n", 578 | " mean_r2_kf[i]=np.mean(get_R2(y_kf_test,y_test_predicted_kf)[2:4]) #Velocity is components 2 and 3\n", 579 | " print(np.mean(get_R2(y_kf_test,y_test_predicted_kf)[2:4])) \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " ### Add variables to list (for saving) ###\n", 584 | " y_kf_test_all.append(y_kf_test)\n", 585 | " y_kf_valid_all.append(y_kf_valid) \n", 586 | " y_kf_train_all.append(y_kf_train) \n", 587 | " \n", 588 | " y_pred_kf_all.append(y_test_predicted_kf)\n", 589 | " y_valid_pred_kf_all.append(model_kf.predict(X_kf_valid,y_kf_valid))\n", 590 | " y_train_pred_kf_all.append(model_kf.predict(X_kf_train,y_kf_train)) \n", 591 | "\n", 592 | " \n", 593 | " \n", 594 | " ### Save ### \n", 595 | " with open(save_folder+dataset+'_results_binsize_kf.pickle','wb') as f:\n", 596 | " pickle.dump([mean_r2_kf,y_pred_kf_all,y_valid_pred_kf_all,y_train_pred_kf_all,\n", 597 | " y_kf_test_all,y_kf_valid_all,y_kf_train_all,lag],f) \n", 598 | " \n", 599 | " \n", 600 | "# print(\"time_elapsed:\",time_elapsed)\n", 601 | "\n" 602 | ] 603 | }, 604 | { 605 | "cell_type": "code", 606 | "execution_count": null, 607 | "metadata": { 608 | "collapsed": true 609 | }, 610 | "outputs": [], 611 | "source": [] 612 | } 613 | ], 614 | "metadata": { 615 | "anaconda-cloud": {}, 616 | "kernelspec": { 617 | "display_name": "Python [Root]", 618 | "language": "python", 619 | "name": "Python [Root]" 620 | }, 621 | "language_info": { 622 | "codemirror_mode": { 623 | "name": "ipython", 624 | "version": 2 625 | }, 626 | "file_extension": ".py", 627 | "mimetype": "text/x-python", 628 | "name": "python", 629 | "nbconvert_exporter": "python", 630 | "pygments_lexer": "ipython2", 631 | "version": "2.7.14" 632 | } 633 | }, 634 | "nbformat": 4, 635 | "nbformat_minor": 0 636 | } 637 | -------------------------------------------------------------------------------- /Paper_code/KF_FewNeurons.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Kalman Filter run with subsampled neurons" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## User Options" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Folder you're saving to" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": { 28 | "collapsed": true 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "# save_folder=''\n", 33 | "save_folder='/home/jglaser/Files/Neural_Decoding/Results/'" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "Define what folder you're loading from" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 2, 46 | "metadata": { 47 | "collapsed": true 48 | }, 49 | "outputs": [], 50 | "source": [ 51 | "# load_folder=''\n", 52 | "load_folder='/home/jglaser/Data/DecData/'" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "Dataset you're using" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": { 66 | "collapsed": true 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "# dataset='s1'\n", 71 | "# dataset='m1'\n", 72 | "dataset='hc'" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "Determine how many neurons you're subsampling, and how many times to do this subsampling" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 4, 85 | "metadata": { 86 | "collapsed": true 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "num_nrns_used=10\n", 91 | "\n", 92 | "num_folds=10 #Number of times to subsample" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "## 1. Import Packages\n", 100 | "\n", 101 | "We import standard packages and functions from the accompanying .py files" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 5, 107 | "metadata": { 108 | "collapsed": false 109 | }, 110 | "outputs": [ 111 | { 112 | "name": "stderr", 113 | "output_type": "stream", 114 | "text": [ 115 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/statsmodels/compat/pandas.py:56: FutureWarning: The pandas.core.datetools module is deprecated and will be removed in a future version. Please use the pandas.tseries module instead.\n", 116 | " from pandas.core import datetools\n", 117 | "Using Theano backend.\n", 118 | "WARNING (theano.sandbox.cuda): The cuda backend is deprecated and will be removed in the next release (v0.10). Please switch to the gpuarray backend. You can get more information about how to switch at this URL:\n", 119 | " https://github.com/Theano/Theano/wiki/Converting-to-the-new-gpu-back-end%28gpuarray%29\n", 120 | "\n", 121 | "Using gpu device 0: GeForce GTX TITAN X (CNMeM is enabled with initial size: 45.0% of memory, cuDNN Mixed dnn version. The header is from one version, but we link with a different version (5103, 5110))\n", 122 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", 123 | " from ._conv import register_converters as _register_converters\n" 124 | ] 125 | } 126 | ], 127 | "source": [ 128 | "#Import standard packages\n", 129 | "import numpy as np\n", 130 | "import matplotlib.pyplot as plt\n", 131 | "%matplotlib inline\n", 132 | "from scipy import io\n", 133 | "from scipy import stats\n", 134 | "import pickle\n", 135 | "import sys\n", 136 | "import time\n", 137 | "\n", 138 | "#Add the main folder to the path, so we have access to the files there.\n", 139 | "#Note that if your working directory is not the Paper_code folder, you may need to manually specify the path to the main folder. For example: sys.path.append('/home/jglaser/GitProj/Neural_Decoding')\n", 140 | "sys.path.append('..') \n", 141 | "\n", 142 | "#Import function to get the covariate matrix that includes spike history from previous bins\n", 143 | "from preprocessing_funcs import get_spikes_with_history\n", 144 | "\n", 145 | "#Import metrics\n", 146 | "from metrics import get_R2\n", 147 | "from metrics import get_rho\n", 148 | "\n", 149 | "#Import decoder functions\n", 150 | "from decoders import KalmanFilterDecoder\n", 151 | "\n", 152 | "from bayes_opt import BayesianOptimization" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 6, 158 | "metadata": { 159 | "collapsed": true 160 | }, 161 | "outputs": [], 162 | "source": [ 163 | "#Turn off deprecation warnings\n", 164 | "\n", 165 | "import warnings\n", 166 | "warnings.filterwarnings(\"ignore\", category=DeprecationWarning) " 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "## 2. Load Data\n", 174 | "\n", 175 | "The data that we load is in the format described below. We have another example script, \"Example_format_data\" that may be helpful towards putting the data in this format.\n", 176 | "\n", 177 | "Neural data should be a matrix of size \"number of time bins\" x \"number of neurons\", where each entry is the firing rate of a given neuron in a given time bin\n", 178 | "\n", 179 | "The output you are decoding should be a matrix of size \"number of time bins\" x \"number of features you are decoding\"" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 7, 185 | "metadata": { 186 | "collapsed": false 187 | }, 188 | "outputs": [], 189 | "source": [ 190 | "if dataset=='s1':\n", 191 | " with open(load_folder+'example_data_s1.pickle','rb') as f:\n", 192 | " # neural_data,vels_binned=pickle.load(f,encoding='latin1')\n", 193 | " neural_data,vels_binned=pickle.load(f)\n", 194 | " \n", 195 | "if dataset=='m1':\n", 196 | " with open(load_folder+'example_data_m1.pickle','rb') as f:\n", 197 | " # neural_data,vels_binned=pickle.load(f,encoding='latin1')\n", 198 | " neural_data,vels_binned=pickle.load(f)\n", 199 | " \n", 200 | "if dataset=='hc':\n", 201 | " with open(load_folder+'example_data_hc.pickle','rb') as f:\n", 202 | " # neural_data,pos_binned=pickle.load(f,encoding='latin1')\n", 203 | " neural_data,pos_binned=pickle.load(f)" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "## 3. Preprocess Data" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "### 3A. Format Covariates" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "#### Format Input Covariates" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 8, 230 | "metadata": { 231 | "collapsed": true 232 | }, 233 | "outputs": [], 234 | "source": [ 235 | "#Remove neurons with too few spikes in HC dataset\n", 236 | "if dataset=='hc':\n", 237 | " nd_sum=np.nansum(neural_data,axis=0)\n", 238 | " rmv_nrn=np.where(nd_sum<100)\n", 239 | " neural_data=np.delete(neural_data,rmv_nrn,1)" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 9, 245 | "metadata": { 246 | "collapsed": false 247 | }, 248 | "outputs": [], 249 | "source": [ 250 | "#The covariate is simply the matrix of firing rates for all neurons over time\n", 251 | "X_kf=neural_data" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "#### Format Output Covariates" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": 10, 264 | "metadata": { 265 | "collapsed": false 266 | }, 267 | "outputs": [], 268 | "source": [ 269 | "#For the Kalman filter, we use the position, velocity, and acceleration as outputs\n", 270 | "#Ultimately, we are only concerned with the goodness of fit of velocity (s1 or m1) or position (hc)\n", 271 | "#But using them all as covariates helps performance\n", 272 | "\n", 273 | "if dataset=='s1' or dataset=='m1':\n", 274 | "\n", 275 | " #We will now determine position\n", 276 | " pos_binned=np.zeros(vels_binned.shape) #Initialize \n", 277 | " pos_binned[0,:]=0 #Assume starting position is at [0,0]\n", 278 | " #Loop through time bins and determine positions based on the velocities\n", 279 | " for i in range(pos_binned.shape[0]-1): \n", 280 | " pos_binned[i+1,0]=pos_binned[i,0]+vels_binned[i,0]*.05 #Note that .05 is the length of the time bin\n", 281 | " pos_binned[i+1,1]=pos_binned[i,1]+vels_binned[i,1]*.05\n", 282 | "\n", 283 | " #We will now determine acceleration \n", 284 | " temp=np.diff(vels_binned,axis=0) #The acceleration is the difference in velocities across time bins \n", 285 | " acc_binned=np.concatenate((temp,temp[-1:,:]),axis=0) #Assume acceleration at last time point is same as 2nd to last\n", 286 | "\n", 287 | " #The final output covariates include position, velocity, and acceleration\n", 288 | " y_kf=np.concatenate((pos_binned,vels_binned,acc_binned),axis=1)\n", 289 | "\n", 290 | "\n", 291 | "if dataset=='hc':\n", 292 | "\n", 293 | " temp=np.diff(pos_binned,axis=0) #Velocity is the difference in positions across time bins\n", 294 | " vels_binned=np.concatenate((temp,temp[-1:,:]),axis=0) #Assume velocity at last time point is same as 2nd to last\n", 295 | "\n", 296 | " temp2=np.diff(vels_binned,axis=0) #The acceleration is the difference in velocities across time bins \n", 297 | " acc_binned=np.concatenate((temp2,temp2[-1:,:]),axis=0) #Assume acceleration at last time point is same as 2nd to last\n", 298 | "\n", 299 | " #The final output covariates include position, velocity, and acceleration\n", 300 | " y_kf=np.concatenate((pos_binned,vels_binned,acc_binned),axis=1)" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "#### In HC dataset, remove time bins with no output (y value)" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 11, 313 | "metadata": { 314 | "collapsed": true 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "if dataset=='hc':\n", 319 | " rmv_time=np.where(np.isnan(y_kf[:,0]) | np.isnan(y_kf[:,1]))\n", 320 | " X_kf=np.delete(X_kf,rmv_time,0)\n", 321 | " y_kf=np.delete(y_kf,rmv_time,0)" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "### 3B. Define training/testing/validation sets\n", 329 | "We use the same training/testing/validation sets used for the largest training set in Fig. 6" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 12, 335 | "metadata": { 336 | "collapsed": false 337 | }, 338 | "outputs": [], 339 | "source": [ 340 | "if dataset=='s1' or dataset=='m1':\n", 341 | " dt=.05\n", 342 | "if dataset=='hc':\n", 343 | " dt=.2\n", 344 | "\n", 345 | " \n", 346 | "if dataset=='hc':\n", 347 | "\n", 348 | " #Size of sets\n", 349 | " test_size=int(450/dt) #7.5 min\n", 350 | " valid_size=test_size #validation size is the same as the test size\n", 351 | " train_size=int(2250/dt) #37.5 min\n", 352 | " \n", 353 | " #End indices\n", 354 | " end_idx=np.int(X_kf.shape[0]*.8) #End of test set\n", 355 | " tr_end_idx=end_idx-test_size-valid_size #End of training set\n", 356 | "\n", 357 | "if dataset=='s1':\n", 358 | " #Size of sets\n", 359 | " test_size=int(300/dt) #5 min\n", 360 | " valid_size=test_size #validation size is the same as the test size\n", 361 | " train_size=int(1200/dt) # 20 min\n", 362 | "\n", 363 | " #End indices\n", 364 | " end_idx=np.int(X_kf.shape[0]*.9)#End of test set\n", 365 | " tr_end_idx=end_idx-2*test_size #End of training set\n", 366 | "\n", 367 | "if dataset=='m1':\n", 368 | " #Size of sets\n", 369 | " test_size=int(300/dt) #5 min\n", 370 | " valid_size=test_size #validation size is the same as the test size\n", 371 | " train_size=int(600/dt) # 10 min\n", 372 | "\n", 373 | " #End indices\n", 374 | " end_idx=np.int(X_kf.shape[0]*1)#End of test set\n", 375 | " tr_end_idx=end_idx-2*test_size #End of training set\n", 376 | " \n", 377 | " \n", 378 | "#Range of sets\n", 379 | "testing_range=[end_idx-test_size,end_idx] #Testing set (length of test_size, goes up until end_idx)\n", 380 | "valid_range=[end_idx-test_size-valid_size,end_idx-test_size] #Validation set (length of valid_size, goes up until beginning of test set)\n", 381 | "training_range=[tr_end_idx-train_size,tr_end_idx] #Training set (length of train_size, goes up until beginning of validation set) " 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "## 4. Run Decoder" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "**Initialize lists of results**" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 13, 401 | "metadata": { 402 | "collapsed": false 403 | }, 404 | "outputs": [], 405 | "source": [ 406 | "#R2 values\n", 407 | "mean_r2_kf=np.empty(num_folds)\n", 408 | "\n", 409 | "#Actual data\n", 410 | "y_kf_test_all=[]\n", 411 | "y_kf_train_all=[]\n", 412 | "y_kf_valid_all=[]\n", 413 | "\n", 414 | "#Test/training/validation predictions\n", 415 | "y_pred_kf_all=[] \n", 416 | "y_train_pred_kf_all=[]\n", 417 | "y_valid_pred_kf_all=[]" 418 | ] 419 | }, 420 | { 421 | "cell_type": "markdown", 422 | "metadata": {}, 423 | "source": [ 424 | "**In the following section, we**\n", 425 | "1. Loop across iterations (where different subsets of neurons are subsampled)\n", 426 | "2. Extract the training/validation/testing data\n", 427 | "3. Preprocess the data\n", 428 | "4. Run the KF decoder (including the hyperparameter optimization)\n", 429 | "5. Save the results" 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": 18, 435 | "metadata": { 436 | "collapsed": false 437 | }, 438 | "outputs": [ 439 | { 440 | "name": "stdout", 441 | "output_type": "stream", 442 | "text": [ 443 | "0.17877361655092028\n" 444 | ] 445 | } 446 | ], 447 | "source": [ 448 | "num_examples=X_kf.shape[0] #number of examples (rows in the X matrix)\n", 449 | "\n", 450 | "for i in range(num_folds): #Loop through the iterations\n", 451 | "\n", 452 | " ##### SUBSAMPLE NEURONS #####\n", 453 | " \n", 454 | " #Randomly subsample \"num_nrns_used\" neurons\n", 455 | " nrn_idxs=np.random.permutation(X_kf.shape[1])[0:num_nrns_used]\n", 456 | " X_sub=np.copy(X_kf[:,nrn_idxs]) \n", 457 | " \n", 458 | " \n", 459 | " ######### SPLIT DATA INTO TRAINING/TESTING/VALIDATION #########\n", 460 | " \n", 461 | " #Note that all sets have a buffer of 1 bin at the beginning and 1 bin at the end \n", 462 | " #This makes it so that the different sets don't include overlapping neural data\n", 463 | " \n", 464 | " #This differs from having buffers of \"num_bins_before\" and \"num_bins_after\" in the other datasets, \n", 465 | " #which creates a slight offset in time indexes between these results and those from the other decoders \n", 466 | "\n", 467 | " #Note that all sets have a buffer of 1 bin at the beginning and 1 bin at the end \n", 468 | " #This makes it so that the different sets don't include overlapping neural data\n", 469 | "\n", 470 | " #Testing set\n", 471 | " testing_set=np.arange(testing_range[0]+1,testing_range[1]-1)\n", 472 | "\n", 473 | " #Validation set\n", 474 | " valid_set=np.arange(valid_range[0]+1,valid_range[1]-1)\n", 475 | "\n", 476 | " #Training_set\n", 477 | " training_set=np.arange(training_range[0]+1,training_range[1]-1) \n", 478 | " \n", 479 | " \n", 480 | " #Get training data\n", 481 | " X_kf_train=X_sub[training_set,:]\n", 482 | " y_kf_train=y_kf[training_set,:]\n", 483 | "\n", 484 | " #Get validation data\n", 485 | " X_kf_valid=X_sub[valid_set,:]\n", 486 | " y_kf_valid=y_kf[valid_set,:]\n", 487 | " \n", 488 | " #Get testing data\n", 489 | " X_kf_test=X_sub[testing_set,:]\n", 490 | " y_kf_test=y_kf[testing_set,:]\n", 491 | "\n", 492 | "\n", 493 | " ##### PREPROCESS DATA #####\n", 494 | "\n", 495 | " #Z-score \"X_kf\" inputs. \n", 496 | " X_kf_train_mean=np.nanmean(X_kf_train,axis=0) #Mean of training data\n", 497 | " X_kf_train_std=np.nanstd(X_kf_train,axis=0) #Stdev of training data\n", 498 | " X_kf_train=(X_kf_train-X_kf_train_mean)/X_kf_train_std #Z-score training data\n", 499 | " X_kf_test=(X_kf_test-X_kf_train_mean)/X_kf_train_std #Preprocess testing data in same manner as training data\n", 500 | " X_kf_valid=(X_kf_valid-X_kf_train_mean)/X_kf_train_std #Preprocess validation data in same manner as training data\n", 501 | "\n", 502 | " #Zero-center outputs\n", 503 | " y_kf_train_mean=np.nanmean(y_kf_train,axis=0) #Mean of training data outputs\n", 504 | " y_kf_train=y_kf_train-y_kf_train_mean #Zero-center training output\n", 505 | " y_kf_test=y_kf_test-y_kf_train_mean #Preprocess testing data in same manner as training data\n", 506 | " y_kf_valid=y_kf_valid-y_kf_train_mean #Preprocess validation data in same manner as training data \n", 507 | "\n", 508 | " \n", 509 | " ####### RUN KALMAN FILTER #######\n", 510 | "\n", 511 | " #We are going to loop through different lags, and for each lag: \n", 512 | " #-we will find the optimal hyperparameter C based on the validation set R2\n", 513 | " #-with that hyperparameter, we will get the validation set R2 for the given lag\n", 514 | " #We will determine the lag as the one that gives the best validation set R2\n", 515 | " #Finally, using the lag and hyperparameters determined (based on above), we will get the test set R2\n", 516 | " \n", 517 | " \n", 518 | " #First, we set the limits of lags that we will evaluate for each dataset\n", 519 | " if dataset=='hc':\n", 520 | " valid_lags=np.arange(-5,6)\n", 521 | " if dataset=='m1':\n", 522 | " valid_lags=np.arange(-10,1)\n", 523 | " if dataset=='s1':\n", 524 | " valid_lags=np.arange(-6,7)\n", 525 | " num_valid_lags=valid_lags.shape[0] #Number of lags we will consider\n", 526 | " \n", 527 | " #Initializations\n", 528 | " lag_results=np.empty(num_valid_lags) #Array to store validation R2 results for each lag\n", 529 | " C_results=np.empty(num_valid_lags) #Array to store the best hyperparameter for each lag\n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " #### Wrapper function that returns the best validation set R2 for each lag\n", 535 | " #That is, for the given lag, it will find the best hyperparameters to maximize validation set R2\n", 536 | " #and the function returns that R2 value\n", 537 | " def kf_evaluate_lag(lag,X_kf_train,y_kf_train,X_kf_valid,y_kf_valid): \n", 538 | " \n", 539 | " #Re-align data to take lag into account\n", 540 | " if lag<0:\n", 541 | " y_kf_train=y_kf_train[-lag:,:]\n", 542 | " X_kf_train=X_kf_train[:lag,:]\n", 543 | " y_kf_valid=y_kf_valid[-lag:,:]\n", 544 | " X_kf_valid=X_kf_valid[:lag,:]\n", 545 | " if lag>0:\n", 546 | " y_kf_train=y_kf_train[0:-lag,:]\n", 547 | " X_kf_train=X_kf_train[lag:,:]\n", 548 | " y_kf_valid=y_kf_valid[0:-lag,:]\n", 549 | " X_kf_valid=X_kf_valid[lag:,:]\n", 550 | " \n", 551 | " #This is a function that evaluates the Kalman filter for the given hyperparameter C\n", 552 | " #and returns the R2 value for the hyperparameter. It's used within Bayesian optimization\n", 553 | " def kf_evaluate(C):\n", 554 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 555 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 556 | " y_valid_predicted_kf=model_kf.predict(X_kf_valid,y_kf_valid) #Get validation set predictions\n", 557 | " #Get validation set R2\n", 558 | " if dataset=='hc':\n", 559 | " return np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[0:2]) #Position is components 0 and 1\n", 560 | " if dataset=='m1' or dataset=='s1':\n", 561 | " return np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[2:4]) #Velocity is components 2 and 3\n", 562 | " \n", 563 | " #Do Bayesian optimization!\n", 564 | " kfBO = BayesianOptimization(kf_evaluate, {'C': (.5, 20)}, verbose=0) #Define Bayesian optimization, and set limits of hyperparameters\n", 565 | " kfBO.maximize(init_points=10, n_iter=10) #Set number of initial runs and subsequent tests, and do the optimization\n", 566 | " best_params=kfBO.res['max']['max_params'] #Get the hyperparameters that give rise to the best fit\n", 567 | " C=best_params['C']\n", 568 | "# print(\"C=\", C)\n", 569 | "\n", 570 | " #Get the validation set R2 using the best hyperparameters fit above: \n", 571 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 572 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 573 | " y_valid_predicted_kf=model_kf.predict(X_kf_valid,y_kf_valid) #Get validation set predictions\n", 574 | " #Get validation set R2\n", 575 | " if dataset=='hc':\n", 576 | " return [np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[0:2]), C] #Position is components 0 and 1\n", 577 | " if dataset=='m1' or dataset=='s1':\n", 578 | " return [np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[2:4]), C] #Velocity is components 2 and 3\n", 579 | " \n", 580 | " \n", 581 | " ### Loop through lags and get validation set R2 for each lag ####\n", 582 | " \n", 583 | " for j in range(num_valid_lags): \n", 584 | " valid_lag=valid_lags[j] #Set what lag you're using\n", 585 | " #Run the wrapper function, and put the R2 value and corresponding C (hyperparameter) in arrays\n", 586 | " [lag_results[j],C_results[j]]=kf_evaluate_lag(valid_lag,X_kf_train,y_kf_train,X_kf_valid,y_kf_valid)\n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " #### Get results on test set ####\n", 591 | " \n", 592 | " #Get the lag (and corresponding C value) that gave the best validation results\n", 593 | " lag=valid_lags[np.argmax(lag_results)] #The lag\n", 594 | "# print(\"lag=\",lag)\n", 595 | " C=C_results[np.argmax(lag_results)] #The hyperparameter C \n", 596 | " \n", 597 | " #Re-align data to take lag into account\n", 598 | " if lag<0:\n", 599 | " y_kf_train=y_kf_train[-lag:,:]\n", 600 | " X_kf_train=X_kf_train[:lag,:]\n", 601 | " y_kf_test=y_kf_test[-lag:,:]\n", 602 | " X_kf_test=X_kf_test[:lag,:]\n", 603 | " y_kf_valid=y_kf_valid[-lag:,:]\n", 604 | " X_kf_valid=X_kf_valid[:lag,:]\n", 605 | " if lag>0:\n", 606 | " y_kf_train=y_kf_train[0:-lag,:]\n", 607 | " X_kf_train=X_kf_train[lag:,:]\n", 608 | " y_kf_test=y_kf_test[0:-lag,:]\n", 609 | " X_kf_test=X_kf_test[lag:,:]\n", 610 | " y_kf_valid=y_kf_valid[0:-lag,:]\n", 611 | " X_kf_valid=X_kf_valid[lag:,:]\n", 612 | " \n", 613 | " #Run the Kalman filter\n", 614 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 615 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 616 | " y_test_predicted_kf=model_kf.predict(X_kf_test,y_kf_test) #Get test set predictions\n", 617 | " #Get test set R2 values and put them in arrays\n", 618 | " if dataset=='hc':\n", 619 | " mean_r2_kf[i]=np.mean(get_R2(y_kf_test,y_test_predicted_kf)[0:2]) #Position is components 0 and 1\n", 620 | " print(np.mean(get_R2(y_kf_test,y_test_predicted_kf)[0:2]))\n", 621 | " if dataset=='m1' or dataset=='s1':\n", 622 | " mean_r2_kf[i]=np.mean(get_R2(y_kf_test,y_test_predicted_kf)[2:4]) #Velocity is components 2 and 3\n", 623 | " print(np.mean(get_R2(y_kf_test,y_test_predicted_kf)[2:4])) \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " ### Add variables to list (for saving) ###\n", 628 | " y_kf_test_all.append(y_kf_test)\n", 629 | " y_kf_valid_all.append(y_kf_valid) \n", 630 | " y_kf_train_all.append(y_kf_train) \n", 631 | " \n", 632 | " y_pred_kf_all.append(y_test_predicted_kf)\n", 633 | " y_valid_pred_kf_all.append(model_kf.predict(X_kf_valid,y_kf_valid))\n", 634 | " y_train_pred_kf_all.append(model_kf.predict(X_kf_train,y_kf_train)) \n", 635 | " \n", 636 | " \n", 637 | " ## Save ###\n", 638 | " with open(save_folder+dataset+'_results_nrn'+str(num_nrns_used)+'_kf.pickle','wb') as f:\n", 639 | " pickle.dump([mean_r2_kf,y_pred_kf_all,y_valid_pred_kf_all,y_train_pred_kf_all,\n", 640 | " y_kf_test_all,y_kf_valid_all,y_kf_train_all],f) " 641 | ] 642 | }, 643 | { 644 | "cell_type": "markdown", 645 | "metadata": {}, 646 | "source": [ 647 | "## Check Results" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": 15, 653 | "metadata": { 654 | "collapsed": false 655 | }, 656 | "outputs": [], 657 | "source": [ 658 | "mean_r2_kf" 659 | ] 660 | }, 661 | { 662 | "cell_type": "code", 663 | "execution_count": 16, 664 | "metadata": { 665 | "collapsed": false 666 | }, 667 | "outputs": [], 668 | "source": [ 669 | "np.mean(mean_r2_kf)" 670 | ] 671 | }, 672 | { 673 | "cell_type": "code", 674 | "execution_count": 17, 675 | "metadata": { 676 | "collapsed": false 677 | }, 678 | "outputs": [], 679 | "source": [ 680 | "plt.plot(y_kf_test_all[1][0:1000,0])\n", 681 | "plt.plot(y_pred_kf_all[1][0:1000,0])" 682 | ] 683 | } 684 | ], 685 | "metadata": { 686 | "anaconda-cloud": {}, 687 | "kernelspec": { 688 | "display_name": "Python [Root]", 689 | "language": "python", 690 | "name": "Python [Root]" 691 | }, 692 | "language_info": { 693 | "codemirror_mode": { 694 | "name": "ipython", 695 | "version": 2 696 | }, 697 | "file_extension": ".py", 698 | "mimetype": "text/x-python", 699 | "name": "python", 700 | "nbconvert_exporter": "python", 701 | "pygments_lexer": "ipython2", 702 | "version": "2.7.14" 703 | } 704 | }, 705 | "nbformat": 4, 706 | "nbformat_minor": 0 707 | } 708 | -------------------------------------------------------------------------------- /Paper_code/KF_FullData.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Kalman Filter run on full datasets" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## User Options" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Folder you're saving to" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": { 28 | "collapsed": true 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "# save_folder=''\n", 33 | "save_folder='/home/jglaser/Files/Neural_Decoding/Results/'" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "Define what folder you're loading from" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 2, 46 | "metadata": { 47 | "collapsed": true 48 | }, 49 | "outputs": [], 50 | "source": [ 51 | "# load_folder=''\n", 52 | "load_folder='/home/jglaser/Data/DecData/'" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "Dataset you're using" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": { 66 | "collapsed": true 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "dataset='s1'\n", 71 | "# dataset='m1'\n", 72 | "# dataset='hc'" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "## 1. Import Packages\n", 80 | "\n", 81 | "We import standard packages and functions from the accompanying .py files" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "metadata": { 88 | "collapsed": false 89 | }, 90 | "outputs": [ 91 | { 92 | "name": "stderr", 93 | "output_type": "stream", 94 | "text": [ 95 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/statsmodels/compat/pandas.py:56: FutureWarning: The pandas.core.datetools module is deprecated and will be removed in a future version. Please use the pandas.tseries module instead.\n", 96 | " from pandas.core import datetools\n", 97 | "Using Theano backend.\n", 98 | "WARNING (theano.sandbox.cuda): The cuda backend is deprecated and will be removed in the next release (v0.10). Please switch to the gpuarray backend. You can get more information about how to switch at this URL:\n", 99 | " https://github.com/Theano/Theano/wiki/Converting-to-the-new-gpu-back-end%28gpuarray%29\n", 100 | "\n", 101 | "Using gpu device 0: GeForce GTX TITAN X (CNMeM is enabled with initial size: 45.0% of memory, cuDNN Mixed dnn version. The header is from one version, but we link with a different version (5103, 5110))\n", 102 | "/opt/anaconda/anaconda2/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", 103 | " from ._conv import register_converters as _register_converters\n" 104 | ] 105 | } 106 | ], 107 | "source": [ 108 | "#Import standard packages\n", 109 | "import numpy as np\n", 110 | "import matplotlib.pyplot as plt\n", 111 | "%matplotlib inline\n", 112 | "from scipy import io\n", 113 | "from scipy import stats\n", 114 | "import pickle\n", 115 | "import sys\n", 116 | "\n", 117 | "#Add the main folder to the path, so we have access to the files there.\n", 118 | "#Note that if your working directory is not the Paper_code folder, you may need to manually specify the path to the main folder. For example: sys.path.append('/home/jglaser/GitProj/Neural_Decoding')\n", 119 | "sys.path.append('..') \n", 120 | "\n", 121 | "#Import function to get the covariate matrix that includes spike history from previous bins\n", 122 | "from preprocessing_funcs import get_spikes_with_history\n", 123 | "\n", 124 | "#Import metrics\n", 125 | "from metrics import get_R2\n", 126 | "from metrics import get_rho\n", 127 | "\n", 128 | "#Import decoder functions\n", 129 | "from decoders import KalmanFilterDecoder\n", 130 | "\n", 131 | "from bayes_opt import BayesianOptimization" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 5, 137 | "metadata": { 138 | "collapsed": true 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "#Turn off deprecation warnings\n", 143 | "\n", 144 | "import warnings\n", 145 | "warnings.filterwarnings(\"ignore\", category=DeprecationWarning) " 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "## 2. Load Data\n", 153 | "\n", 154 | "The data that we load is in the format described below. We have another example script, \"Example_format_data\" that may be helpful towards putting the data in this format.\n", 155 | "\n", 156 | "Neural data should be a matrix of size \"number of time bins\" x \"number of neurons\", where each entry is the firing rate of a given neuron in a given time bin\n", 157 | "\n", 158 | "The output you are decoding should be a matrix of size \"number of time bins\" x \"number of features you are decoding\"" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 6, 164 | "metadata": { 165 | "collapsed": false 166 | }, 167 | "outputs": [], 168 | "source": [ 169 | "if dataset=='s1':\n", 170 | " with open(load_folder+'example_data_s1.pickle','rb') as f:\n", 171 | " # neural_data,vels_binned=pickle.load(f,encoding='latin1')\n", 172 | " neural_data,vels_binned=pickle.load(f)\n", 173 | " \n", 174 | "if dataset=='m1':\n", 175 | " with open(load_folder+'example_data_m1.pickle','rb') as f:\n", 176 | " # neural_data,vels_binned=pickle.load(f,encoding='latin1')\n", 177 | " neural_data,vels_binned=pickle.load(f)\n", 178 | " \n", 179 | "if dataset=='hc':\n", 180 | " with open(load_folder+'example_data_hc.pickle','rb') as f:\n", 181 | " # neural_data,pos_binned=pickle.load(f,encoding='latin1')\n", 182 | " neural_data,pos_binned=pickle.load(f)" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "## 3. Preprocess Data" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "### 3A. Format Covariates" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "#### Format Input Covariates" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 7, 209 | "metadata": { 210 | "collapsed": true 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "#Remove neurons with too few spikes in HC dataset\n", 215 | "if dataset=='hc':\n", 216 | " nd_sum=np.nansum(neural_data,axis=0)\n", 217 | " rmv_nrn=np.where(nd_sum<100)\n", 218 | " neural_data=np.delete(neural_data,rmv_nrn,1)" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 8, 224 | "metadata": { 225 | "collapsed": false 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "#The covariate is simply the matrix of firing rates for all neurons over time\n", 230 | "X_kf=neural_data" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "#### Format Output Covariates" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 9, 243 | "metadata": { 244 | "collapsed": false 245 | }, 246 | "outputs": [], 247 | "source": [ 248 | "#For the Kalman filter, we use the position, velocity, and acceleration as outputs\n", 249 | "#Ultimately, we are only concerned with the goodness of fit of velocity (s1 or m1) or position (hc)\n", 250 | "#But using them all as covariates helps performance\n", 251 | "\n", 252 | "if dataset=='s1' or dataset=='m1':\n", 253 | "\n", 254 | " #We will now determine position\n", 255 | " pos_binned=np.zeros(vels_binned.shape) #Initialize \n", 256 | " pos_binned[0,:]=0 #Assume starting position is at [0,0]\n", 257 | " #Loop through time bins and determine positions based on the velocities\n", 258 | " for i in range(pos_binned.shape[0]-1): \n", 259 | " pos_binned[i+1,0]=pos_binned[i,0]+vels_binned[i,0]*.05 #Note that .05 is the length of the time bin\n", 260 | " pos_binned[i+1,1]=pos_binned[i,1]+vels_binned[i,1]*.05\n", 261 | "\n", 262 | " #We will now determine acceleration \n", 263 | " temp=np.diff(vels_binned,axis=0) #The acceleration is the difference in velocities across time bins \n", 264 | " acc_binned=np.concatenate((temp,temp[-1:,:]),axis=0) #Assume acceleration at last time point is same as 2nd to last\n", 265 | "\n", 266 | " #The final output covariates include position, velocity, and acceleration\n", 267 | " y_kf=np.concatenate((pos_binned,vels_binned,acc_binned),axis=1)\n", 268 | "\n", 269 | "\n", 270 | "if dataset=='hc':\n", 271 | "\n", 272 | " temp=np.diff(pos_binned,axis=0) #Velocity is the difference in positions across time bins\n", 273 | " vels_binned=np.concatenate((temp,temp[-1:,:]),axis=0) #Assume velocity at last time point is same as 2nd to last\n", 274 | "\n", 275 | " temp2=np.diff(vels_binned,axis=0) #The acceleration is the difference in velocities across time bins \n", 276 | " acc_binned=np.concatenate((temp2,temp2[-1:,:]),axis=0) #Assume acceleration at last time point is same as 2nd to last\n", 277 | "\n", 278 | " #The final output covariates include position, velocity, and acceleration\n", 279 | " y_kf=np.concatenate((pos_binned,vels_binned,acc_binned),axis=1)" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "#### In HC dataset, remove time bins with no output (y value)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 10, 292 | "metadata": { 293 | "collapsed": true 294 | }, 295 | "outputs": [], 296 | "source": [ 297 | "if dataset=='hc':\n", 298 | " rmv_time=np.where(np.isnan(y_kf[:,0]) | np.isnan(y_kf[:,1]))\n", 299 | " X_kf=np.delete(X_kf,rmv_time,0)\n", 300 | " y_kf=np.delete(y_kf,rmv_time,0)" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "** In HC dataset, there is a long period without movement starting at ~80%, so we only use the first 80% of the data**" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 11, 313 | "metadata": { 314 | "collapsed": false 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "if dataset=='hc':\n", 319 | " X_kf=X_kf[:int(.8*X_kf.shape[0]),:]\n", 320 | " y_kf=y_kf[:int(.8*y_kf.shape[0]),:]" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": {}, 326 | "source": [ 327 | "### 3B. Define training/testing/validation sets\n", 328 | "We have 10 cross-validation folds. In each fold, 10% of the data is a test set, 10% is a validation set, and 80% is the training set. So in the first fold, for example, 0-10% is validation, 10-20% is testing, and 20-100% is training." 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": 12, 334 | "metadata": { 335 | "collapsed": false 336 | }, 337 | "outputs": [], 338 | "source": [ 339 | "valid_range_all=[[0,.1],[.1,.2],[.2,.3],[.3,.4],[.4,.5],\n", 340 | " [.5,.6],[.6,.7],[.7,.8],[.8,.9],[.9,1]]\n", 341 | "testing_range_all=[[.1,.2],[.2,.3],[.3,.4],[.4,.5],[.5,.6],\n", 342 | " [.6,.7],[.7,.8],[.8,.9],[.9,1],[0,.1]]\n", 343 | "#Note that the training set is not aways contiguous. For example, in the second fold, the training set has 0-10% and 30-100%.\n", 344 | "#In that example, we enter of list of lists: [[0,.1],[.3,1]]\n", 345 | "training_range_all=[[[.2,1]],[[0,.1],[.3,1]],[[0,.2],[.4,1]],[[0,.3],[.5,1]],[[0,.4],[.6,1]],\n", 346 | " [[0,.5],[.7,1]],[[0,.6],[.8,1]],[[0,.7],[.9,1]],[[0,.8]],[[.1,.9]]]\n", 347 | "\n", 348 | "num_folds=len(valid_range_all) #Number of cross validation folds" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "## 4. Run CV" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "**Initialize lists of results**" 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": 13, 368 | "metadata": { 369 | "collapsed": false 370 | }, 371 | "outputs": [], 372 | "source": [ 373 | "#R2 values\n", 374 | "mean_r2_kf=np.empty(num_folds)\n", 375 | "\n", 376 | "#Actual data\n", 377 | "y_kf_test_all=[]\n", 378 | "y_kf_train_all=[]\n", 379 | "y_kf_valid_all=[]\n", 380 | "\n", 381 | "#Test/training/validation predictions\n", 382 | "y_pred_kf_all=[] \n", 383 | "y_train_pred_kf_all=[]\n", 384 | "y_valid_pred_kf_all=[]" 385 | ] 386 | }, 387 | { 388 | "cell_type": "markdown", 389 | "metadata": {}, 390 | "source": [ 391 | "**In the following section, we**\n", 392 | "1. Loop across folds\n", 393 | "2. Extract the training/validation/testing data\n", 394 | "3. Preprocess the data\n", 395 | "4. Run the KF decoder (including the hyperparameter optimization)\n", 396 | "5. Save the results" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 14, 402 | "metadata": { 403 | "collapsed": false 404 | }, 405 | "outputs": [ 406 | { 407 | "name": "stdout", 408 | "output_type": "stream", 409 | "text": [ 410 | "0.6847150469136274\n" 411 | ] 412 | } 413 | ], 414 | "source": [ 415 | "num_examples=X_kf.shape[0] #number of examples (rows in the X matrix)\n", 416 | "\n", 417 | "for i in range(num_folds): #Loop through the folds\n", 418 | "\n", 419 | " ######### SPLIT DATA INTO TRAINING/TESTING/VALIDATION #########\n", 420 | " \n", 421 | " #Note that all sets have a buffer of 1 bin at the beginning and 1 bin at the end \n", 422 | " #This makes it so that the different sets don't include overlapping neural data\n", 423 | " \n", 424 | " #This differs from having buffers of \"num_bins_before\" and \"num_bins_after\" in the other datasets, \n", 425 | " #which creates a slight offset in time indexes between these results and those from the other decoders\n", 426 | " \n", 427 | " #Get testing set for this fold\n", 428 | " testing_range=testing_range_all[i]\n", 429 | " testing_set=np.arange(np.int(np.round(testing_range[0]*num_examples))+1,np.int(np.round(testing_range[1]*num_examples))-1)\n", 430 | "\n", 431 | " #Get validation set for this fold\n", 432 | " valid_range=valid_range_all[i]\n", 433 | " valid_set=np.arange(np.int(np.round(valid_range[0]*num_examples))+1,np.int(np.round(valid_range[1]*num_examples))-1)\n", 434 | "\n", 435 | " #Get training set for this fold\n", 436 | " #Note this needs to take into account a non-contiguous training set (see section 3B)\n", 437 | " training_ranges=training_range_all[i]\n", 438 | " for j in range(len(training_ranges)): #Go through different separated portions of the training set\n", 439 | " training_range=training_ranges[j]\n", 440 | " if j==0: #If it's the first portion of the training set, make it the training set\n", 441 | " training_set=np.arange(np.int(np.round(training_range[0]*num_examples))+1,np.int(np.round(training_range[1]*num_examples))-1)\n", 442 | " if j==1: #If it's the second portion of the training set, concatentate it to the first\n", 443 | " training_set_temp=np.arange(np.int(np.round(training_range[0]*num_examples))+1,np.int(np.round(training_range[1]*num_examples))-1)\n", 444 | " training_set=np.concatenate((training_set,training_set_temp),axis=0)\n", 445 | " \n", 446 | " #Get training data\n", 447 | " X_kf_train=X_kf[training_set,:]\n", 448 | " y_kf_train=y_kf[training_set,:]\n", 449 | "\n", 450 | " #Get validation data\n", 451 | " X_kf_valid=X_kf[valid_set,:]\n", 452 | " y_kf_valid=y_kf[valid_set,:]\n", 453 | " \n", 454 | " #Get testing data\n", 455 | " X_kf_test=X_kf[testing_set,:]\n", 456 | " y_kf_test=y_kf[testing_set,:]\n", 457 | "\n", 458 | "\n", 459 | " ##### PREPROCESS DATA #####\n", 460 | "\n", 461 | " #Z-score \"X_kf\" inputs. \n", 462 | " X_kf_train_mean=np.nanmean(X_kf_train,axis=0) #Mean of training data\n", 463 | " X_kf_train_std=np.nanstd(X_kf_train,axis=0) #Stdev of training data\n", 464 | " X_kf_train=(X_kf_train-X_kf_train_mean)/X_kf_train_std #Z-score training data\n", 465 | " X_kf_test=(X_kf_test-X_kf_train_mean)/X_kf_train_std #Preprocess testing data in same manner as training data\n", 466 | " X_kf_valid=(X_kf_valid-X_kf_train_mean)/X_kf_train_std #Preprocess validation data in same manner as training data\n", 467 | "\n", 468 | " #Zero-center outputs\n", 469 | " y_kf_train_mean=np.nanmean(y_kf_train,axis=0) #Mean of training data outputs\n", 470 | " y_kf_train=y_kf_train-y_kf_train_mean #Zero-center training output\n", 471 | " y_kf_test=y_kf_test-y_kf_train_mean #Preprocess testing data in same manner as training data\n", 472 | " y_kf_valid=y_kf_valid-y_kf_train_mean #Preprocess validation data in same manner as training data \n", 473 | "\n", 474 | " \n", 475 | " ####### RUN KALMAN FILTER #######\n", 476 | "\n", 477 | " #We are going to loop through different lags, and for each lag: \n", 478 | " #-we will find the optimal hyperparameter C based on the validation set R2\n", 479 | " #-with that hyperparameter, we will get the validation set R2 for the given lag\n", 480 | " #We will determine the lag as the one that gives the best validation set R2\n", 481 | " #Finally, using the lag and hyperparameters determined (based on above), we will get the test set R2\n", 482 | " \n", 483 | " \n", 484 | " #First, we set the limits of lags that we will evaluate for each dataset\n", 485 | " if dataset=='hc':\n", 486 | " valid_lags=np.arange(-5,6)\n", 487 | " if dataset=='m1':\n", 488 | " valid_lags=np.arange(-10,1)\n", 489 | " if dataset=='s1':\n", 490 | " valid_lags=np.arange(-6,7)\n", 491 | " num_valid_lags=valid_lags.shape[0] #Number of lags we will consider\n", 492 | " \n", 493 | " #Initializations\n", 494 | " lag_results=np.empty(num_valid_lags) #Array to store validation R2 results for each lag\n", 495 | " C_results=np.empty(num_valid_lags) #Array to store the best hyperparameter for each lag\n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " #### Wrapper function that returns the best validation set R2 for each lag\n", 501 | " #That is, for the given lag, it will find the best hyperparameters to maximize validation set R2\n", 502 | " #and the function returns that R2 value\n", 503 | " def kf_evaluate_lag(lag,X_kf_train,y_kf_train,X_kf_valid,y_kf_valid): \n", 504 | " \n", 505 | " #Re-align data to take lag into account\n", 506 | " if lag<0:\n", 507 | " y_kf_train=y_kf_train[-lag:,:]\n", 508 | " X_kf_train=X_kf_train[:lag,:]\n", 509 | " y_kf_valid=y_kf_valid[-lag:,:]\n", 510 | " X_kf_valid=X_kf_valid[:lag,:]\n", 511 | " if lag>0:\n", 512 | " y_kf_train=y_kf_train[0:-lag,:]\n", 513 | " X_kf_train=X_kf_train[lag:,:]\n", 514 | " y_kf_valid=y_kf_valid[0:-lag,:]\n", 515 | " X_kf_valid=X_kf_valid[lag:,:]\n", 516 | " \n", 517 | " #This is a function that evaluates the Kalman filter for the given hyperparameter C\n", 518 | " #and returns the R2 value for the hyperparameter. It's used within Bayesian optimization\n", 519 | " def kf_evaluate(C):\n", 520 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 521 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 522 | " y_valid_predicted_kf=model_kf.predict(X_kf_valid,y_kf_valid) #Get validation set predictions\n", 523 | " #Get validation set R2\n", 524 | " if dataset=='hc':\n", 525 | " return np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[0:2]) #Position is components 0 and 1\n", 526 | " if dataset=='m1' or dataset=='s1':\n", 527 | " return np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[2:4]) #Velocity is components 2 and 3\n", 528 | " \n", 529 | " #Do Bayesian optimization!\n", 530 | " kfBO = BayesianOptimization(kf_evaluate, {'C': (.5, 20)}, verbose=0) #Define Bayesian optimization, and set limits of hyperparameters\n", 531 | " kfBO.maximize(init_points=10, n_iter=10) #Set number of initial runs and subsequent tests, and do the optimization\n", 532 | " best_params=kfBO.res['max']['max_params'] #Get the hyperparameters that give rise to the best fit\n", 533 | " C=best_params['C']\n", 534 | "# print(\"C=\", C)\n", 535 | "\n", 536 | " #Get the validation set R2 using the best hyperparameters fit above: \n", 537 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 538 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 539 | " y_valid_predicted_kf=model_kf.predict(X_kf_valid,y_kf_valid) #Get validation set predictions\n", 540 | " #Get validation set R2\n", 541 | " if dataset=='hc':\n", 542 | " return [np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[0:2]), C] #Position is components 0 and 1\n", 543 | " if dataset=='m1' or dataset=='s1':\n", 544 | " return [np.mean(get_R2(y_kf_valid,y_valid_predicted_kf)[2:4]), C] #Velocity is components 2 and 3\n", 545 | " \n", 546 | " \n", 547 | " ### Loop through lags and get validation set R2 for each lag ####\n", 548 | " \n", 549 | " for j in range(num_valid_lags): \n", 550 | " valid_lag=valid_lags[j] #Set what lag you're using\n", 551 | " #Run the wrapper function, and put the R2 value and corresponding C (hyperparameter) in arrays\n", 552 | " [lag_results[j],C_results[j]]=kf_evaluate_lag(valid_lag,X_kf_train,y_kf_train,X_kf_valid,y_kf_valid)\n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " #### Get results on test set ####\n", 557 | " \n", 558 | " #Get the lag (and corresponding C value) that gave the best validation results\n", 559 | " lag=valid_lags[np.argmax(lag_results)] #The lag\n", 560 | "# print(\"lag=\",lag)\n", 561 | " C=C_results[np.argmax(lag_results)] #The hyperparameter C \n", 562 | " \n", 563 | " #Re-align data to take lag into account\n", 564 | " if lag<0:\n", 565 | " y_kf_train=y_kf_train[-lag:,:]\n", 566 | " X_kf_train=X_kf_train[:lag,:]\n", 567 | " y_kf_test=y_kf_test[-lag:,:]\n", 568 | " X_kf_test=X_kf_test[:lag,:]\n", 569 | " y_kf_valid=y_kf_valid[-lag:,:]\n", 570 | " X_kf_valid=X_kf_valid[:lag,:]\n", 571 | " if lag>0:\n", 572 | " y_kf_train=y_kf_train[0:-lag,:]\n", 573 | " X_kf_train=X_kf_train[lag:,:]\n", 574 | " y_kf_test=y_kf_test[0:-lag,:]\n", 575 | " X_kf_test=X_kf_test[lag:,:]\n", 576 | " y_kf_valid=y_kf_valid[0:-lag,:]\n", 577 | " X_kf_valid=X_kf_valid[lag:,:]\n", 578 | " \n", 579 | " #Run the Kalman filter\n", 580 | " model_kf=KalmanFilterDecoder(C=C) #Define model\n", 581 | " model_kf.fit(X_kf_train,y_kf_train) #Fit model\n", 582 | " y_test_predicted_kf=model_kf.predict(X_kf_test,y_kf_test) #Get test set predictions\n", 583 | " #Get test set R2 values and put them in arrays\n", 584 | " if dataset=='hc':\n", 585 | " mean_r2_kf[i]=np.mean(get_R2(y_kf_test,y_test_predicted_kf)[0:2]) #Position is components 0 and 1\n", 586 | " print(np.mean(get_R2(y_kf_test,y_test_predicted_kf)[0:2]))\n", 587 | " if dataset=='m1' or dataset=='s1':\n", 588 | " mean_r2_kf[i]=np.mean(get_R2(y_kf_test,y_test_predicted_kf)[2:4]) #Velocity is components 2 and 3\n", 589 | " print(np.mean(get_R2(y_kf_test,y_test_predicted_kf)[2:4])) \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " ### Add variables to list (for saving) ###\n", 594 | " y_kf_test_all.append(y_kf_test)\n", 595 | " y_kf_valid_all.append(y_kf_valid) \n", 596 | " y_kf_train_all.append(y_kf_train) \n", 597 | " \n", 598 | " y_pred_kf_all.append(y_test_predicted_kf)\n", 599 | " y_valid_pred_kf_all.append(model_kf.predict(X_kf_valid,y_kf_valid))\n", 600 | " y_train_pred_kf_all.append(model_kf.predict(X_kf_train,y_kf_train)) \n", 601 | " \n", 602 | " \n", 603 | " ### Save ###\n", 604 | " with open(save_folder+dataset+'_results_kf2.pickle','wb') as f:\n", 605 | " pickle.dump([mean_r2_kf,y_pred_kf_all,y_valid_pred_kf_all,y_train_pred_kf_all,\n", 606 | " y_kf_test_all,y_kf_valid_all,y_kf_train_all],f) " 607 | ] 608 | }, 609 | { 610 | "cell_type": "code", 611 | "execution_count": 15, 612 | "metadata": { 613 | "collapsed": false 614 | }, 615 | "outputs": [], 616 | "source": [ 617 | "# plt.plot(y_kf_test_all[1][0:1000,0])\n", 618 | "# plt.plot(y_pred_kf_all[1][0:1000,0])" 619 | ] 620 | } 621 | ], 622 | "metadata": { 623 | "anaconda-cloud": {}, 624 | "kernelspec": { 625 | "display_name": "Python [Root]", 626 | "language": "python", 627 | "name": "Python [Root]" 628 | }, 629 | "language_info": { 630 | "codemirror_mode": { 631 | "name": "ipython", 632 | "version": 2 633 | }, 634 | "file_extension": ".py", 635 | "mimetype": "text/x-python", 636 | "name": "python", 637 | "nbconvert_exporter": "python", 638 | "pygments_lexer": "ipython2", 639 | "version": "2.7.14" 640 | } 641 | }, 642 | "nbformat": 4, 643 | "nbformat_minor": 0 644 | } 645 | -------------------------------------------------------------------------------- /Paper_code/Plot_Results_FewNeurons.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Import Packages" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "collapsed": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "import numpy as np\n", 19 | "import matplotlib.pyplot as plt\n", 20 | "%matplotlib inline\n", 21 | "from scipy import io\n", 22 | "from scipy import stats\n", 23 | "import pickle" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### User Options" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 2, 36 | "metadata": { 37 | "collapsed": true 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "# load_folder='' #Folder where results are (for loading them)\n", 42 | "load_folder='/home/jglaser/Files/Neural_Decoding/Results/'\n", 43 | "# fig_folder='' #Folder to save the figures to\n", 44 | "fig_folder='/home/jglaser/Figs/Decoding/' \n", 45 | "datasets=['m1','s1','hc'] #Names of the datasets\n", 46 | "num_nrns_used=10 #Number of neurons used for decoding\n", 47 | "ill=1 #Whether I am making these plots for exporting to adobe illustrator (in which case I remove the text)\n", 48 | "colors=['purple','cyan','gold', 'red'] #Colors to plot each method" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "### Plot Summary (Fig. 7)" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 3, 61 | "metadata": { 62 | "collapsed": false 63 | }, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "image/png": "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\nAElFTkSuQmCC\n", 68 | "text/plain": [ 69 | "" 70 | ] 71 | }, 72 | "metadata": {}, 73 | "output_type": "display_data" 74 | } 75 | ], 76 | "source": [ 77 | "d=0 #Initialize index of the dataset I'm looking at (this will be the row I plot in the figure)\n", 78 | "fig, ax = plt.subplots(3,1,figsize=(3,12)) #Create figure (3 rows by 1 column)\n", 79 | "\n", 80 | "for dataset in datasets: #Loop through datasets\n", 81 | " \n", 82 | " ####LOAD RESULTS FOR ALL METHODS####\n", 83 | "\n", 84 | " with open(load_folder+dataset+'_results_nrn'+str(num_nrns_used)+'_wf.pickle','rb') as f:\n", 85 | " [mean_r2_wf,y_pred_wf_all,y_train_pred_wf_all,y_valid_pred_wf_all]=pickle.load(f)\n", 86 | " \n", 87 | " with open(load_folder+dataset+'_results_nrn'+str(num_nrns_used)+'_dnn.pickle','rb') as f:\n", 88 | " [mean_r2_dnn,y_pred_dnn_all,y_train_pred_dnn_all,y_valid_pred_dnn_all,time_elapsed]=pickle.load(f) \n", 89 | "\n", 90 | " with open(load_folder+dataset+'_results_nrn'+str(num_nrns_used)+'_lstm.pickle','rb') as f:\n", 91 | " [mean_r2_lstm,y_pred_lstm_all,y_train_pred_lstm_all,y_valid_pred_lstm_all,time_elapsed]=pickle.load(f)\n", 92 | "\n", 93 | " with open(load_folder+dataset+'_results_nrn'+str(num_nrns_used)+'_kf.pickle','rb') as f:\n", 94 | " [mean_r2_kf,y_pred_kf_all,y_valid_pred_kf_all,y_train_pred_kf_all,y_kf_test_all,y_kf_valid_all,y_kf_train_all]=pickle.load(f) \n", 95 | " \n", 96 | " #### Calculate the mean and standard error across cross-validation folds ####\n", 97 | " \n", 98 | " n=mean_r2_wf.shape[0] #Number of folds\n", 99 | " means=([np.mean(mean_r2_wf), np.mean(mean_r2_kf),np.mean(mean_r2_dnn),np.mean(mean_r2_lstm)])\n", 100 | " err=([np.std(mean_r2_wf)/np.sqrt(n),np.std(mean_r2_kf)/np.sqrt(n),np.std(mean_r2_dnn)/np.sqrt(n),np.std(mean_r2_lstm)/np.sqrt(n)])\n", 101 | " \n", 102 | " #####PLOT RESULTS######\n", 103 | "\n", 104 | " #Plot bars\n", 105 | " ind = np.arange(len(err)) #X values for plotting\n", 106 | " for pos, y, yerr, color in zip(ind, means, err, colors): #Loop through methods and plot error bars\n", 107 | " ax[d].errorbar(pos, y, yerr, lw=2, capsize=5, capthick=2, color=color)\n", 108 | " #Remove x tick labels\n", 109 | " labels = [item.get_text() for item in ax[d].get_xticklabels()]\n", 110 | " empty_string_labels = ['']*len(labels)\n", 111 | " ax[d].set_xticklabels(empty_string_labels)\n", 112 | "\n", 113 | " #Remove right and top boundaries, and make ticks inward\n", 114 | " ax[d].tick_params(direction='in',bottom=0)\n", 115 | " ax[d].spines['right'].set_color('none')\n", 116 | " ax[d].spines['top'].set_color('none')\n", 117 | "\n", 118 | " #Plot individual R2 values for each fold as an 'x'\n", 119 | " scatter_x=np.reshape(np.transpose(np.ones((10,1))*range(4)),(40,1)) #Get x values for plotting (first 10 will have an x value of 0, second 10 will have an x value of 1, etc)\n", 120 | " scatter_y=np.concatenate((mean_r2_wf,mean_r2_kf,mean_r2_dnn,mean_r2_lstm),axis=0) #Y values for plotting\n", 121 | " colors_list=[] #Create a list of the colors that should be used when plotting each 'x'\n", 122 | " for i in scatter_x.astype(np.int).reshape((1,-1))[0]:\n", 123 | " colors_list.append(colors[i])\n", 124 | " ax[d].scatter(scatter_x,scatter_y,c=colors_list,marker='x',alpha=0.3)\n", 125 | "\n", 126 | " #Set y axis limits and ticks\n", 127 | " if dataset=='hc':\n", 128 | " ax[d].set_ylim([-0.1, .5])\n", 129 | " else:\n", 130 | " ax[d].set_ylim([0, .65])\n", 131 | " ax[d].set_xlim([-0.5, 3.5])\n", 132 | " if ill:\n", 133 | " ax[d].set_yticklabels('')\n", 134 | " \n", 135 | " d=d+1 #Increase dataset index (so the next dataset gets plot on the next row)\n", 136 | " \n", 137 | "plt.show()\n", 138 | "\n", 139 | "fig.savefig(fig_folder+'few_neurons_summary_v3.eps') #Save figure" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": { 146 | "collapsed": true 147 | }, 148 | "outputs": [], 149 | "source": [] 150 | } 151 | ], 152 | "metadata": { 153 | "kernelspec": { 154 | "display_name": "Python [Root]", 155 | "language": "python", 156 | "name": "Python [Root]" 157 | }, 158 | "language_info": { 159 | "codemirror_mode": { 160 | "name": "ipython", 161 | "version": 2 162 | }, 163 | "file_extension": ".py", 164 | "mimetype": "text/x-python", 165 | "name": "python", 166 | "nbconvert_exporter": "python", 167 | "pygments_lexer": "ipython2", 168 | "version": "2.7.14" 169 | } 170 | }, 171 | "nbformat": 4, 172 | "nbformat_minor": 0 173 | } 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neural_Decoding: 2 | 3 | ### A python package that includes many methods for decoding neural activity 4 | 5 | The package contains a mixture of classic decoding methods and modern machine learning methods. 6 | 7 | For regression, we currently include: Wiener Filter, Wiener Cascade, Kalman Filter, Naive Bayes, Support Vector Regression, XGBoost, Dense Neural Network, Recurrent Neural Net, GRU, LSTM. 8 | 9 | For classification, we currently include: Logistic Regression, Support Vector Classification, XGBoost, Dense Neural Network, Recurrent Neural Net, GRU, LSTM. 10 | 11 | This package was originally designed for regression and classification functions were just added - therefore, the ReadMe, examples, and preprocessing functions are still catered for regression. We are in the process of adding more for classification. 12 | 13 | 14 | ## Our manuscript and datasets 15 | This package accompanies a [manuscript](https://arxiv.org/abs/1708.00909) that compares the performance of these methods on several datasets. We would appreciate if you cite that manuscript if you use our code or data for your research. 16 | 17 | Code used for the paper is in the "Paper_code" folder. It is described further at the bottom of this read-me. 18 | 19 | All 3 datasets (motor cortex, somatosensory cortex, and hippocampus) used in the paper can be downloaded [here](https://www.dropbox.com/sh/n4924ipcfjqc0t6/AACPWjxDKPEzQiXKUUFriFkJa?dl=0). They are in both matlab and python formats, and can be used in the example files described below. 20 | 21 | ## Installation 22 | 23 | This package can be installed via `pip` at the command line by typing 24 | ```buildoutcfg 25 | pip install Neural-Decoding 26 | ``` 27 | or manually via 28 | ```buildoutcfg 29 | git clone https://github.com/KordingLab/Neural_Decoding.git 30 | cd Neural_Decoding 31 | python setup.py install 32 | ``` 33 | You'll have to install each dependency yourself if you install manually. We've designed the code so that not all machine learning packages 34 | need to be installed for the others to work. 35 | 36 | ## Dependencies 37 | All packages will be installed automatically when installing from `pip` (because of the `requirements.txt` file). 38 | 39 | If installing manually via `python setup.py install`: 40 | In order to run all the decoders based on neural networks, you need to install [Keras](https://keras.io/#installation)
41 | In order to run the XGBoost Decoder, you need to install [XGBoost](https://pypi.python.org/pypi/xgboost/)
42 | In order to run the Wiener Filter, Wiener Cascade, or Support Vector Regression you will need [scikit-learn](http://scikit-learn.org/stable/install.html).
43 | In order to do hyperparameter optimization, you need to install [BayesianOptimization](https://github.com/fmfn/BayesianOptimization) 44 | 45 | ## Getting started 46 | We have included jupyter notebooks that provide detailed examples of how to use the decoders. 47 | - The file **`central_concepts_in_ML_for_decoding.ipynb`** is designed for users who are new to machine learning. It builds basic concepts and shows some examples, and also has several exercises to make sure you know your stuff. (Link to the solutions is inside). 48 | - The file **`Examples_kf_decoder.ipynb`** is for the Kalman filter decoder 49 | - The file **`Examples_all_decoders.ipynb`** is for all other decoders. These examples work well with the somatosensory and motor cortex datasets. 50 | - There are minor differences in the hippocampus dataset, so we have included a folder, **`Examples_hippocampus`**, with analogous example files. This folder also includes an example file for using the Naive Bayes decoder (since it works much better on our hippocampus dataset). 51 | - We have also included a notebook, **`Example_hyperparam_opt.ipynb`**, that demonstrates how to do hyperparameter optimization for the decoders. 52 | 53 | Here we provide a basic example where we are using a LSTM decoder.
54 | For this example we assume we have already loaded matrices: 55 | - "neural_data": a matrix of size "total number of time bins" x "number of neurons," where each entry is the firing rate of a given neuron in a given time bin. 56 | - "y": the output variable that you are decoding (e.g. velocity), and is a matrix of size "total number of time bins" x "number of features you are decoding."
57 | 58 | We have provided a Jupyter notebook, **`Example_format_data.ipynb`** with an example of how to get Matlab data into this format. 59 |
60 | 61 | First we will import the necessary functions 62 | ```python 63 | from Neural_Decoding.decoders import LSTMDecoder #Import LSTM decoder 64 | from Neural_Decoding.preprocessing_funcs import get_spikes_with_history #Import function to get the covariate matrix that includes spike history from previous bins 65 | ``` 66 | Next, we will define the time period we are using spikes from (relative to the output we are decoding) 67 | ```python 68 | bins_before=13 #How many bins of neural data prior to the output are used for decoding 69 | bins_current=1 #Whether to use concurrent time bin of neural data 70 | bins_after=0 #How many bins of neural data after the output are used for decoding 71 | ``` 72 | 73 | Next, we will compute the covariate matrix that includes the spike history from previous bins 74 | ```python 75 | # Function to get the covariate matrix that includes spike history from previous bins 76 | X=get_spikes_with_history(neural_data,bins_before,bins_after,bins_current) 77 | ``` 78 | In this basic example, we will ignore some additional preprocessing we do in the example notebooks. Let's assume we have now divided the data into a training set (X_train, y_train) and a testing set (X_test,y_test). 79 | 80 | We will now finally train and test the decoder: 81 | ```python 82 | #Declare model and set parameters of the model 83 | model_lstm=LSTMDecoder(units=400,num_epochs=5) 84 | 85 | #Fit model 86 | model_lstm.fit(X_train,y_train) 87 | 88 | #Get predictions 89 | y_test_predicted_lstm=model_lstm.predict(X_test) 90 | ``` 91 | 92 | ## What's Included 93 | There are 3 files with functions. An overview of the functions are below. More details can be found in the comments within the files. 94 | 95 | ### decoders.py: 96 | This file provides all of the decoders. Each decoder is a class with functions "fit" and "predict". 97 | 98 | First, we will describe the format of data that is necessary for the decoders 99 | - For all the decoders, you will need to decide the time period of spikes (relative to the output) that you are using for decoding. 100 | - For all the decoders other than the Kalman filter, you can set "bins_before" (the number of bins of spikes preceding the output), "bins_current" (whether to use the bin of spikes concurrent with the output), and "bins_after" (the number of bins of spikes after the output). Let "surrounding_bins" = bins_before+bins_current+bins_after. This allows us to get a 3d covariate matrix "X" that has size "total number of time bins" x "surrounding_bins" x "number of neurons." We use this input format for the recurrent neural networks (SimpleRNN, GRU, LSTM). We can also flatten the matrix, so that there is a vector of features for every time bin, to get "X_flat" which is a 2d matrix of size "total number of time bins" x "surrounding_bins x number of neurons." This input format is used for the Wiener Filter, Wiener Cascade, Support Vector Regression, XGBoost, and Dense Neural Net. 101 | - For the Kalman filter, you can set the "lag" - what time bin of the neural data (relative to the output) is used to predict the output. The input format for the Kalman filter is simply the 2d matrix of size "total number of time bins" x "number of neurons," where each entry is the firing rate of a given neuron in a given time bin. 102 | - The output, "y" is a 2d matrix of size "total number of time bins" x "number of output features." 103 | 104 |
Here are all the decoders within "decoders.py" for performing regression: 105 | 1. **WienerFilterDecoder** 106 | - The Wiener Filter is simply multiple linear regression using X_flat as an input. 107 | - It has no input parameters 108 | 2. **WienerCascadeDecoder** 109 | - The Wiener Cascade (also known as a linear nonlinear model) fits a linear regression (the Wiener filter) followed by fitting a static nonlearity. 110 | - It has parameter *degree* (the degree of the polynomial used for the nonlinearity) 111 | 3. **KalmanFilterDecoder** 112 | - We used a Kalman filter similar to that implemented in [Wu et al. 2003](https://papers.nips.cc/paper/2178-neural-decoding-of-cursor-motion-using-a-kalman-filter.pdf). In the Kalman filter, the measurement was the neural spike trains, and the hidden state was the kinematics. 113 | - We have one parameter *C* (which is not in the previous implementation). This parameter scales the noise matrix associated with the transition in kinematic states. It effectively allows changing the weight of the new neural evidence in the current update. 114 | 4. **NaiveBayesDecoder** 115 | - We used a Naive Bayes decoder similar to that implemented in [Zhang et al. 1998](https://www.physiology.org/doi/abs/10.1152/jn.1998.79.2.1017) (see manuscript for details). 116 | - It has parameters *encoding_model* (for either a linear or quadratic encoding model) and *res* (to set the resolution of predicted values) 117 | 5. **SVRDecoder** 118 | - This decoder uses support vector regression using X_flat as an input. 119 | - It has parameters *C* (the penalty of the error term) and *max_iter* (the maximum number of iterations). 120 | - It works best when the output ("y") has been normalized 121 | 6. **XGBoostDecoder** 122 | - We used the Extreme Gradient Boosting [XGBoost](http://xgboost.readthedocs.io/en/latest/model.html) algorithm to relate X_flat to the outputs. XGBoost is based on the idea of boosted trees. 123 | - It has parameters *max_depth* (the maximum depth of the trees), *num_round* (the number of trees that are fit), *eta* (the learning rate), and *gpu* (if you have the [gpu version](https://github.com/dmlc/xgboost/tree/master/plugin/updater_gpu) of XGBoost installed, you can select which gpu to use) 124 | 7. **DenseNNDecoder** 125 | - Using the Keras library, we created a dense feedforward neural network that uses X_flat to predict the outputs. It can have any number of hidden layers. 126 | - It has parameters *units* (the number of units in each layer), *dropout* (the proportion of units that get dropped out), *num_epochs* (the number of epochs used for training), and *verbose* (whether to display progress of the fit after each epoch) 127 | 8. **SimpleRNNDecoder** 128 | - Using the Keras library, we created a neural network architecture where the spiking input (from matrix X) was fed into a standard recurrent neural network (RNN) with a relu activation. The units from this recurrent layer were fully connected to the output layer. 129 | - It has parameters *units*, *dropout*, *num_epochs*, and *verbose* 130 | 9. **GRUDecoder** 131 | - Using the Keras library, we created a neural network architecture where the spiking input (from matrix X) was fed into a network of gated recurrent units (GRUs; a more sophisticated RNN). The units from this recurrent layer were fully connected to the output layer. 132 | - It has parameters *units*, *dropout*, *num_epochs*, and *verbose* 133 | 10. **LSTMDecoder** 134 | - All methods were the same as for the GRUDecoder, except Long Short Term Memory networks (LSTMs; another more sophisticated RNN) were used rather than GRUs. 135 | - It has parameters *units*, *dropout*, *num_epochs*, and *verbose* 136 | 137 | When designing the XGBoost and neural network decoders, there were many additional parameters that could have been utilized (e.g. regularization). To simplify ease of use, we only included parameters that were sufficient for producing good fits. 138 | 139 | ### metrics.py: 140 | The file has functions for metrics to evaluate model fit. It currently has functions to calculate: 141 | - ![equation](https://latex.codecogs.com/gif.latex?%24R%5E2%3D1-%5Csum_%7Bi%3D1%7D%5E%7Bn%7D%7B%7D%5Cfrac%7B%5Cleft%28y_i-%5Cwidehat%7By_i%7D%20%5Cright%20%29%5E2%7D%7B%5Cleft%28y_i-%5Cbar%7By_i%7D%20%5Cright%20%29%5E2%7D) 142 | - ![equation](https://latex.codecogs.com/gif.latex?%24%5Crho%24) : The pearson correlation coefficient 143 | 144 | ### preprocessing_funcs.py 145 | The file contains functions for preprocessing data that may be useful for putting the neural activity and outputs in the correct format for our decoding functions 146 | - **bin_spikes**: converts spike times to the number of spikes within time bins 147 | - **bin_output**: converts a continuous stream of outputs to the average output within time bins 148 | - **get_spikes_with_history**: using binned spikes as input, this function creates a covariate matrix of neural data that incorporates spike history 149 | 150 | ## Paper code 151 | In the folder "Paper_code", we include code used for the manuscript. 152 | - Files starting with "ManyDecoders" use all decoders except the Kalman Filter and Naive Bayes 153 | - Files starting with "KF" use the Kalman filter 154 | - Files starting with "BayesDecoder" use the Naive Bayes decoder 155 | - Files starting with "Plot" create the figures in the paper 156 | - Files ending with "FullData" are for figures 3/4 157 | - Files ending with "DataAmt" are for figures 5/6 158 | - Files ending with "FewNeurons" are for figure 7 159 | - Files ending with "BinSize" are for figure 8 160 | - Files mentioning "Hyperparams" are for figure 9 161 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.16.3 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import setuptools # noqa; we are using a setuptools namespace 3 | # from numpy.distutils.core import setup 4 | from setuptools import setup, Extension 5 | 6 | descr = """A python package that includes many methods for decoding neural activity.""" 7 | 8 | DISTNAME = 'Neural Decoding' 9 | DESCRIPTION = descr 10 | MAINTAINER = 'Josh Glaser' 11 | MAINTAINER_EMAIL = 'joshglaser88@gmail.com ' 12 | LICENSE = 'BSD 3-Clause License' 13 | DOWNLOAD_URL = 'https://github.com/KordingLab/Neural_Decoding.git' 14 | VERSION = '0.1.2.dev' 15 | with open('requirements.txt') as f: 16 | requirements = f.read().splitlines() 17 | 18 | if __name__ == "__main__": 19 | setup(name=DISTNAME, 20 | maintainer=MAINTAINER, 21 | maintainer_email=MAINTAINER_EMAIL, 22 | description=DESCRIPTION, 23 | license=LICENSE, 24 | version=VERSION, 25 | download_url=DOWNLOAD_URL, 26 | long_description=open('README.md').read(), 27 | long_description_content_type= 'text/markdown', 28 | classifiers=[ 29 | 'Intended Audience :: Science/Research', 30 | 'Intended Audience :: Developers', 31 | 'License :: OSI Approved', 32 | 'Programming Language :: Python', 33 | 'Topic :: Software Development', 34 | 'Topic :: Scientific/Engineering', 35 | 'Operating System :: Microsoft :: Windows', 36 | 'Operating System :: POSIX', 37 | 'Operating System :: Unix', 38 | 'Operating System :: MacOS', 39 | ], 40 | platforms='any', 41 | packages=['Neural_Decoding'], 42 | install_requires=requirements, 43 | ) --------------------------------------------------------------------------------