def __init__(mode, connectivity, weight_dependence, post_pre, conv_size, conv_stride, conv_features, weight_sharing, lattice_structure, random_inhibition_prob, top_percent): ''' Network initialization. ''' # setting input parameters this.mode = mode this.connectivity = connectivity this.weight_dependence = weight_dependence this.post_pre = post_pre this.conv_size = conv_size this.conv_features = conv_features this.weight_sharing = weight_sharing this.lattice_structure = lattice_structure this.random_inhibition_prob = random_inhibition_prob # load training or testing data if mode == 'train': start = time.time() this.data = get_labeled_data(MNIST_data_path + 'training') end = time.time() print 'time needed to load training set:', end - start else: start = time.time() this.data = get_labeled_data(MNIST_data_path + 'testing', bTrain = False) end = time.time() print 'time needed to load test set:', end - start # set parameters for simulation based on train / test mode if test_mode: weight_path = top_level_path + 'weights/conv_patch_connectivity_weights/' this.num_examples = 10000 * 1 this.do_plot_performance = False ee_STDP_on = False else: weight_path = top_level_path + 'random/conv_patch_connectivity_random/' this.num_examples = 60000 * 1 this.do_plot_performance = True ee_STDP_on = True # plotting or not do_plot = True # number of inputs to the network this.n_input = 784 this.n_input_sqrt = int(math.sqrt(n_input)) # number of neurons parameters this.n_e = ((n_input_sqrt - conv_size) / conv_stride + 1) ** 2 this.n_e_total = n_e * conv_features this.n_e_sqrt = int(math.sqrt(n_e)) this.n_i = n_e this.conv_features_sqrt = int(math.sqrt(conv_features)) # time (in seconds) per data example presentation and rest period in between, used to calculate total runtime this.single_example_time = 0.35 * b.second this.resting_time = 0.15 * b.second runtime = num_examples * (single_example_time + resting_time) # set the update interval if test_mode: this.update_interval = num_examples else: this.update_interval = 100 # rest potential parameters, reset potential parameters, threshold potential parameters, and refractory periods v_rest_e, v_rest_i = -65. * b.mV, -60. * b.mV v_reset_e, v_reset_i = -65. * b.mV, -45. * b.mV v_thresh_e, v_thresh_i = -52. * b.mV, -40. * b.mV refrac_e, refrac_i = 5. * b.ms, 2. * b.ms # dictionaries for weights and delays weight, delay = {}, {} # populations, connections, saved connections, etc. input_population_names = [ 'X' ] population_names = [ 'A' ] input_connection_names = [ 'XA' ] save_conns = [ 'XeAe', 'AeAe' ] # weird and bad names for variables, I think input_conn_names = [ 'ee_input' ] recurrent_conn_names = [ 'ei', 'ie', 'ee' ] # setting weight, delay, and intensity parameters weight['ee_input'] = (conv_size ** 2) * 0.175 delay['ee_input'] = (0 * b.ms, 10 * b.ms) delay['ei_input'] = (0 * b.ms, 5 * b.ms) input_intensity = start_input_intensity = 2.0 # time constants, learning rates, max weights, weight dependence, etc. tc_pre_ee, tc_post_ee = 20 * b.ms, 20 * b.ms nu_ee_pre, nu_ee_post = 0.0001, 0.01 wmax_ee = 1.0 exp_ee_post = exp_ee_pre = 0.2 w_mu_pre, w_mu_post = 0.2, 0.2 # setting up differential equations (depending on train / test mode) if test_mode: scr_e = 'v = v_reset_e; timer = 0*ms' else: tc_theta = 1e7 * b.ms theta_plus_e = 0.05 * b.mV scr_e = 'v = v_reset_e; theta += theta_plus_e; timer = 0*ms' offset = 20.0 * b.mV v_thresh_e = '(v>(theta - offset + ' + str(v_thresh_e) + ')) * (timer>refrac_e)' # equations for neurons neuron_eqs_e = ''' dv/dt = ((v_rest_e - v) + (I_synE + I_synI) / nS) / (100 * ms) : volt I_synE = ge * nS * -v : amp I_synI = gi * nS * (-100.*mV-v) : amp dge/dt = -ge/(1.0*ms) : 1 dgi/dt = -gi/(2.0*ms) : 1 ''' if test_mode: neuron_eqs_e += '\n theta :volt' else: neuron_eqs_e += '\n dtheta/dt = -theta / (tc_theta) : volt' neuron_eqs_e += '\n dtimer/dt = 100.0 : ms' neuron_eqs_i = ''' dv/dt = ((v_rest_i - v) + (I_synE + I_synI) / nS) / (10*ms) : volt I_synE = ge * nS * -v : amp I_synI = gi * nS * (-85.*mV-v) : amp dge/dt = -ge/(1.0*ms) : 1 dgi/dt = -gi/(2.0*ms) : 1 ''' # creating dictionaries for various objects this.neuron_groups = {} this.input_groups = {} this.connections = {} this.input_connections = {} this.stdp_methods = {} this.rate_monitors = {} this.spike_monitors = {} this.spike_counters = {} # creating excitatory, inhibitory populations this.neuron_groups['e'] = b.NeuronGroup(n_e_total, neuron_eqs_e, threshold=v_thresh_e, refractory=refrac_e, reset=scr_e, compile=True, freeze=True) this.neuron_groups['i'] = b.NeuronGroup(n_e_total, neuron_eqs_i, threshold=v_thresh_i, refractory=refrac_i, reset=v_reset_i, compile=True, freeze=True) # creating subpopulations of excitatory, inhibitory neurons for name in population_names: print '...creating neuron group:', name # get a subgroup of size 'n_e' from all exc neuron_groups[name + 'e'] = neuron_groups['e'].subgroup(conv_features * n_e) # get a subgroup of size 'n_i' from the inhibitory layer neuron_groups[name + 'i'] = neuron_groups['i'].subgroup(conv_features * n_e) # start the membrane potentials of these groups 40mV below their resting potentials neuron_groups[name + 'e'].v = v_rest_e - 40. * b.mV neuron_groups[name + 'i'].v = v_rest_i - 40. * b.mV print '...creating recurrent connections' for name in population_names: # if we're in test mode / using some stored weights if mode == 'test' or weight_path[-8:] == 'weights/conv_patch_connectivity_weights/': # load up adaptive threshold parameters neuron_groups['e'].theta = np.load(weight_path + 'theta_A' + '_' + ending +'.npy') else: # otherwise, set the adaptive additive threshold parameter at 20mV neuron_groups['e'].theta = np.ones((n_e_total)) * 20.0 * b.mV for conn_type in recurrent_conn_names: if conn_type == 'ei': # create connection name (composed of population and connection types) conn_name = name + conn_type[0] + name + conn_type[1] # create a connection from the first group in conn_name with the second group connections[conn_name] = b.Connection(neuron_groups[conn_name[0:2]], neuron_groups[conn_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection for feature in xrange(conv_features): for n in xrange(n_e): connections[conn_name][feature * n_e + n, feature * n_e + n] = 10.4 elif conn_type == 'ie': # create connection name (composed of population and connection types) conn_name = name + conn_type[0] + name + conn_type[1] # create a connection from the first group in conn_name with the second group connections[conn_name] = b.Connection(neuron_groups[conn_name[0:2]], neuron_groups[conn_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection for feature in xrange(conv_features): for other_feature in xrange(conv_features): if feature != other_feature: for n in xrange(n_e): connections[conn_name][feature * n_e + n, other_feature * n_e + n] = 17.4 if random_inhibition_prob != 0.0: for feature in xrange(conv_features): for other_feature in xrange(conv_features): for n_this in xrange(n_e): for n_other in xrange(n_e): if n_this != n_other: if b.random() < random_inhibition_prob: connections[conn_name][feature * n_e + n_this, other_feature * n_e + n_other] = 17.4 elif conn_type == 'ee': # create connection name (composed of population and connection types) conn_name = name + conn_type[0] + name + conn_type[1] # get weights from file if we are in test mode if mode == 'test': weight_matrix = get_matrix_from_file(weight_path + conn_name + '_' + ending + '.npy', conv_features * n_e, conv_features * n_e) # create a connection from the first group in conn_name with the second group connections[conn_name] = b.Connection(neuron_groups[conn_name[0:2]], neuron_groups[conn_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection if connectivity == 'all': for feature in xrange(conv_features): for other_feature in xrange(conv_features): if feature != other_feature: for this_n in xrange(n_e): for other_n in xrange(n_e): if is_lattice_connection(n_e_sqrt, this_n, other_n): if mode == 'test': connections[conn_name][feature * n_e + this_n, other_feature * n_e + other_n] = weight_matrix[feature * n_e + this_n, other_feature * n_e + other_n] else: connections[conn_name][feature * n_e + this_n, other_feature * n_e + other_n] = (b.random() + 0.01) * 0.3 elif connectivity == 'pairs': for feature in xrange(conv_features): if feature % 2 == 0: for this_n in xrange(n_e): for other_n in xrange(n_e): if is_lattice_connection(n_e_sqrt, this_n, other_n): if mode == 'test': connections[conn_name][feature * n_e + this_n, (feature + 1) * n_e + other_n] = weight_matrix[feature * n_e + this_n, (feature + 1) * n_e + other_n] else: connections[conn_name][feature * n_e + this_n, (feature + 1) * n_e + other_n] = (b.random() + 0.01) * 0.3 elif feature % 2 == 1: for this_n in xrange(n_e): for other_n in xrange(n_e): if is_lattimode == 'test'ce_connection(n_e_sqrt, this_n, other_n): if mode == 'test': connections[conn_name][feature * n_e + this_n, (feature - 1) * n_e + other_n] = weight_matrix[feature * n_e + this_n, (feature - 1) * n_e + other_n] else: connections[conn_name][feature * n_e + this_n, (feature - 1) * n_e + other_n] = (b.random() + 0.01) * 0.3 elif connectivity == 'none': pass # if STDP from excitatory -> excitatory is on and this connection is excitatory -> excitatory if ee_STDP_on and 'ee' in recurrent_conn_names: stdp_methods[name + 'e' + name + 'e'] = b.STDP(connections[name + 'e' + name + 'e'], eqs=eqs_stdp_ee, pre=eqs_stdp_pre_ee, post=eqs_stdp_post_ee, wmin=0., wmax=wmax_ee) print '...creating monitors for:', name # spike rate monitors for excitatory and inhibitory neuron populations rate_monitors[name + 'e'] = b.PopulationRateMonitor(neuron_groups[name + 'e'], bin=(single_example_time + resting_time) / b.second) rate_monitors[name + 'i'] = b.PopulationRateMonitor(neuron_groups[name + 'i'], bin=(single_example_time + resting_time) / b.second) spike_counters[name + 'e'] = b.SpikeCounter(neuron_groups[name + 'e']) # record neuron population spikes if specified spike_monitors[name + 'e'] = b.SpikeMonitor(neuron_groups[name + 'e']) spike_monitors[name + 'i'] = b.SpikeMonitor(neuron_groups[name + 'i'])
def build_network(): global fig_num, assignments neuron_groups['e'] = b.NeuronGroup(n_e_total, neuron_eqs_e, threshold=v_thresh_e, refractory=refrac_e, reset=scr_e, compile=True, freeze=True) neuron_groups['i'] = b.NeuronGroup(n_e_total, neuron_eqs_i, threshold=v_thresh_i, refractory=refrac_i, reset=v_reset_i, compile=True, freeze=True) for name in population_names: print '...Creating neuron group:', name # get a subgroup of size 'n_e' from all exc neuron_groups[name + 'e'] = neuron_groups['e'].subgroup(conv_features * n_e) # get a subgroup of size 'n_i' from the inhibitory layer neuron_groups[name + 'i'] = neuron_groups['i'].subgroup(conv_features * n_e) # start the membrane potentials of these groups 40mV below their resting potentials neuron_groups[name + 'e'].v = v_rest_e - 40. * b.mV neuron_groups[name + 'i'].v = v_rest_i - 40. * b.mV print '...Creating recurrent connections' for name in population_names: # if we're in test mode / using some stored weights if test_mode: # load up adaptive threshold parameters if save_best_model: neuron_groups['e'].theta = np.load( os.path.join(best_weights_dir, '_'.join(['theta_A', ending + '_best.npy']))) else: neuron_groups['e'].theta = np.load( os.path.join(end_weights_dir, '_'.join(['theta_A', ending + '_end.npy']))) else: # otherwise, set the adaptive additive threshold parameter at 20mV neuron_groups['e'].theta = np.ones((n_e_total)) * 20.0 * b.mV for conn_type in recurrent_conn_names: if conn_type == 'ei': # create connection name (composed of population and connection types) conn_name = name + conn_type[0] + name + conn_type[1] # create a connection from the first group in conn_name with the second group connections[conn_name] = b.Connection( neuron_groups[conn_name[0:2]], neuron_groups[conn_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection for feature in xrange(conv_features): for n in xrange(n_e): connections[conn_name][feature * n_e + n, feature * n_e + n] = 10.4 elif conn_type == 'ie': # create connection name (composed of population and connections types) conn_name = name + conn_type[0] + name + conn_type[ 1] + '_' + ending # create a connection from the first group in conn_name with the second group connections[conn_name] = b.Connection( neuron_groups[conn_name[0:2]], neuron_groups[conn_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection with the 'weightMatrix' loaded from file for feature in xrange(conv_features): for other_feature in xrange(conv_features): if feature != other_feature: for n in xrange(n_e): connections[conn_name][feature * n_e + n, other_feature * n_e + n] = 17.4 print '...Creating monitors for:', name # spike rate monitors for excitatory and inhibitory neuron populations rate_monitors[name + 'e'] = b.PopulationRateMonitor( neuron_groups[name + 'e'], bin=(single_example_time + resting_time) / b.second) rate_monitors[name + 'i'] = b.PopulationRateMonitor( neuron_groups[name + 'i'], bin=(single_example_time + resting_time) / b.second) spike_counters[name + 'e'] = b.SpikeCounter(neuron_groups[name + 'e']) # record neuron population spikes if specified if record_spikes or plot: spike_monitors[name + 'e'] = b.SpikeMonitor(neuron_groups[name + 'e']) spike_monitors[name + 'i'] = b.SpikeMonitor(neuron_groups[name + 'i']) if record_spikes and plot: b.figure(fig_num, figsize=(8, 6)) fig_num += 1 b.ion() b.subplot(211) b.raster_plot(spike_monitors['Ae'], refresh=1000 * b.ms, showlast=1000 * b.ms, title='Excitatory spikes per neuron') b.subplot(212) b.raster_plot(spike_monitors['Ai'], refresh=1000 * b.ms, showlast=1000 * b.ms, title='Inhibitory spikes per neuron') b.tight_layout() # creating Poission spike train from input image (784 vector, 28x28 image) for name in input_population_names: input_groups[name + 'e'] = b.PoissonGroup(n_input, 0) rate_monitors[name + 'e'] = b.PopulationRateMonitor( input_groups[name + 'e'], bin=(single_example_time + resting_time) / b.second) # creating connections from input Poisson spike train to excitatory neuron population(s) for name in input_connection_names: print '\n...Creating connections between', name[0], 'and', name[1] # for each of the input connection types (in this case, excitatory -> excitatory) for conn_type in input_conn_names: # saved connection name conn_name = name[0] + conn_type[0] + name[1] + conn_type[1] # get weight matrix depending on training or test phase if test_mode: if save_best_model: weight_matrix = np.load( os.path.join( best_weights_dir, '_'.join([conn_name, ending + '_best.npy']))) else: weight_matrix = np.load( os.path.join( end_weights_dir, '_'.join([conn_name, ending + '_end.npy']))) # create connections from the windows of the input group to the neuron population input_connections[conn_name] = b.Connection(input_groups['Xe'], neuron_groups[name[1] + conn_type[1]], \ structure='sparse', state='g' + conn_type[0], delay=True, max_delay=delay[conn_type][1]) if test_mode: for feature in xrange(conv_features): for n in xrange(n_e): for idx in xrange(conv_size**2): input_connections[conn_name][convolution_locations[n][idx], feature * n_e + n] = \ weight_matrix[convolution_locations[n][idx], feature * n_e + n] else: for feature in xrange(conv_features): for n in xrange(n_e): for idx in xrange(conv_size**2): input_connections[conn_name][ convolution_locations[n][idx], feature * n_e + n] = (b.random() + 0.01) * 0.3 if test_mode: if plot: plot_weights_and_assignments(assignments) fig_num += 1 # if excitatory -> excitatory STDP is specified, add it here (input to excitatory populations) if not test_mode: print '...Creating STDP for connection', name # STDP connection name conn_name = name[0] + conn_type[0] + name[1] + conn_type[1] # create the STDP object stdp_methods[conn_name] = b.STDP(input_connections[conn_name], eqs=eqs_stdp_ee, \ pre=eqs_stdp_pre_ee, post=eqs_stdp_post_ee, wmin=0., wmax=wmax_ee) print '\n'
def __init__(self, n_input=784, conv_size=16, conv_stride=4, conv_features=50, connectivity='all', weight_dependence=False, post_pre=True, weight_sharing=False, lattice_structure='4', random_lattice_prob=0.0, random_inhibition_prob=0.0): ''' Constructor for the spiking convolutional neural network model. n_input: (flattened) dimensionality of the input data conv_size: side length of convolution windows used conv_stride: stride (horizontal and vertical) of convolution windows used conv_features: number of convolution features (or patches) used connectivity: connection style between patches; one of 'none', 'pairs', all'; more to be added weight_dependence: whether to use weight STDP with weight dependence post_pre: whether to use STDP with both post- and pre-synpatic traces weight_sharing: whether to impose that all neurons within a convolution patch share a common set of weights lattice_structure: lattice connectivity pattern between patches; one of 'none', '4', '8', and 'all' random_lattice_prob: probability of adding random additional lattice connections between patches random_inhibition_prob: probability of adding random additional inhibition edges from the inhibitory to excitatory population ''' self.n_input, self.conv_size, self.conv_stride, self.conv_features, self.connectivity, self.weight_dependence, \ self.post_pre, self.weight_sharing, self.lattice_structure, self.random_lattice_prob, self.random_inhibition_prob = \ n_input, conv_size, conv_stride, conv_features, connectivity, weight_dependence, post_pre, weight_sharing, lattice_structure, \ random_lattice_prob, random_inhibition_prob # number of inputs to the network self.n_input_sqrt = int(math.sqrt(self.n_input)) self.n_excitatory_patch = ( (self.n_input_sqrt - self.conv_size) / self.conv_stride + 1)**2 self.n_excitatory = self.n_excitatory_patch * self.conv_features self.n_excitatory_patch_sqrt = int(math.sqrt(self.n_excitatory_patch)) self.n_inhibitory_patch = self.n_excitatory_patch self.n_inhibitory = self.n_excitatory self.conv_features_sqrt = int(math.ceil(math.sqrt(self.conv_features))) # time (in seconds) per data example presentation and rest period in between self.single_example_time = 0.35 * b.second self.resting_time = 0.15 * b.second # set update intervals self.update_interval = 100 self.weight_update_interval = 10 self.print_progress_interval = 10 # rest potential parameters, reset potential parameters, threshold potential parameters, and refractory periods v_rest_e, v_rest_i = -65. * b.mV, -60. * b.mV v_reset_e, v_reset_i = -65. * b.mV, -45. * b.mV v_thresh_e, v_thresh_i = -52. * b.mV, -40. * b.mV refrac_e, refrac_i = 5. * b.ms, 2. * b.ms # time constants, learning rates, max weights, weight dependence, etc. tc_pre_ee, tc_post_ee = 20 * b.ms, 20 * b.ms nu_ee_pre, nu_ee_post = 0.0001, 0.01 exp_ee_post = exp_ee_pre = 0.2 w_mu_pre, w_mu_post = 0.2, 0.2 # parameters for neuron equations tc_theta = 1e7 * b.ms theta_plus = 0.05 * b.mV scr_e = 'v = v_reset_e; theta += theta_plus; timer = 0*ms' offset = 20.0 * b.mV v_thresh_e = '(v>(theta - offset + ' + str( v_thresh_e) + ')) * (timer>refrac_e)' # equations for neurons neuron_eqs_e = ''' dv / dt = ((v_rest_e - v) + (I_synE + I_synI) / nS) / (100 * ms) : volt I_synE = ge * nS * - v : amp I_synI = gi * nS * (-100. * mV - v) : amp dge / dt = -ge / (1.0*ms) : 1 dgi / dt = -gi / (2.0*ms) : 1 dtheta / dt = -theta / (tc_theta) : volt dtimer / dt = 100.0 : ms ''' neuron_eqs_i = ''' dv/dt = ((v_rest_i - v) + (I_synE + I_synI) / nS) / (10*ms) : volt I_synE = ge * nS * -v : amp I_synI = gi * nS * (-85.*mV-v) : amp dge/dt = -ge/(1.0*ms) : 1 dgi/dt = -gi/(2.0*ms) : 1 ''' # STDP synaptic traces eqs_stdp_ee = ''' dpre / dt = -pre / tc_pre_ee : 1.0 dpost / dt = -post / tc_post_ee : 1.0 ''' # dictionaries for weights and delays self.weight, self.delay = {}, {} # setting weight, delay, and intensity parameters self.weight['ee_input'] = (conv_size**2) * 0.175 self.delay['ee_input'] = (0 * b.ms, 10 * b.ms) self.delay['ei_input'] = (0 * b.ms, 5 * b.ms) self.input_intensity = self.start_input_intensity = 2.0 self.wmax_ee = 1.0 # populations, connections, saved connections, etc. self.input_population_names = ['X'] self.population_names = ['A'] self.input_connection_names = ['XA'] self.save_connections = ['XeAe', 'AeAe'] self.input_connection_names = ['ee_input'] self.recurrent_connection_names = ['ei', 'ie', 'ee'] # setting STDP update rule if weight_dependence: if post_pre: eqs_stdp_pre_ee = 'pre = 1.; w -= nu_ee_pre * post * w ** exp_ee_pre' eqs_stdp_post_ee = 'w += nu_ee_post * pre * (wmax_ee - w) ** exp_ee_post; post = 1.' else: eqs_stdp_pre_ee = 'pre = 1.' eqs_stdp_post_ee = 'w += nu_ee_post * pre * (wmax_ee - w) ** exp_ee_post; post = 1.' else: if post_pre: eqs_stdp_pre_ee = 'pre = 1.; w -= nu_ee_pre * post' eqs_stdp_post_ee = 'w += nu_ee_post * pre; post = 1.' else: eqs_stdp_pre_ee = 'pre = 1.' eqs_stdp_post_ee = 'w += nu_ee_post * pre; post = 1.' print '\n' # for filesaving purposes stdp_input = '' if self.weight_dependence: stdp_input += 'weight_dependence_' else: stdp_input += 'no_weight_dependence_' if self.post_pre: stdp_input += 'post_pre' else: stdp_input += 'no_post_pre' if self.weight_sharing: use_weight_sharing = 'weight_sharing' else: use_weight_sharing = 'no_weight_sharing' # set ending of filename saves self.ending = self.connectivity + '_' + str(self.conv_size) + '_' + str(self.conv_stride) + '_' + str(self.conv_features) + \ '_' + str(self.n_excitatory_patch) + '_' + stdp_input + '_' + \ use_weight_sharing + '_' + str(self.lattice_structure) + '_' + str(self.random_lattice_prob) + \ '_' + str(self.random_inhibition_prob) self.fig_num = 1 # creating dictionaries for various objects self.neuron_groups, self.input_groups, self.connections, self.input_connections, self.stdp_methods, self.rate_monitors, \ self.spike_monitors, self.spike_counters, self.output_numbers = {}, {}, {}, {}, {}, {}, {}, {}, {} # creating convolution locations inside the input image self.convolution_locations = {} for n in xrange(self.n_excitatory_patch): self.convolution_locations[n] = [ ((n % self.n_excitatory_patch_sqrt) * self.conv_stride + (n // self.n_excitatory_patch_sqrt) \ * self.n_input_sqrt * self.conv_stride) + (x * self.n_input_sqrt) + y \ for y in xrange(self.conv_size) for x in xrange(self.conv_size) ] # instantiating neuron spike / votes monitor self.result_monitor = np.zeros( (self.update_interval, self.conv_features, self.n_excitatory_patch)) # creating overarching neuron populations self.neuron_groups['e'] = b.NeuronGroup(self.n_excitatory, neuron_eqs_e, threshold=v_thresh_e, \ refractory=refrac_e, reset=scr_e, compile=True, freeze=True) self.neuron_groups['i'] = b.NeuronGroup(self.n_inhibitory, neuron_eqs_i, threshold=v_thresh_i, \ refractory=refrac_i, reset=v_reset_i, compile=True, freeze=True) # create neuron subpopulations for name in self.population_names: print '...creating neuron group:', name # get a subgroup of size 'n_e' from all exc self.neuron_groups[name + 'e'] = self.neuron_groups['e'].subgroup( self.conv_features * self.n_excitatory_patch) # get a subgroup of size 'n_i' from the inhibitory layer self.neuron_groups[name + 'i'] = self.neuron_groups['i'].subgroup( self.conv_features * self.n_excitatory_patch) # start the membrane potentials of these groups 40mV below their resting potentials self.neuron_groups[name + 'e'].v = v_rest_e - 40. * b.mV self.neuron_groups[name + 'i'].v = v_rest_i - 40. * b.mV print '...creating recurrent connections' for name in self.population_names: # set the adaptive additive threshold parameter at 20mV self.neuron_groups['e'].theta = np.ones( (self.n_excitatory)) * 20.0 * b.mV for connection_type in self.recurrent_connection_names: if connection_type == 'ei': # create connection name (composed of population and connection types) connection_name = name + connection_type[ 0] + name + connection_type[1] # create a connection from the first group in conn_name with the second group self.connections[connection_name] = b.Connection(self.neuron_groups[connection_name[0:2]], \ self.neuron_groups[connection_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection for feature in xrange(self.conv_features): for n in xrange(self.n_excitatory_patch): self.connections[conn_name][feature * self.n_excitatory_patch + n, \ feature * self.n_excitatory_patch + n] = 10.4 elif connection_type == 'ie': # create connection name (composed of population and connection types) connection_name = name + connection_type[ 0] + name + connection_type[1] # create a connection from the first group in conn_name with the second group self.connections[connection_name] = b.Connection(self.neuron_groups[connection_name[0:2]], \ self.neuron_groups[connection_name[2:4]], structure='sparse', state='g' + conn_type[0]) # instantiate the created connection for feature in xrange(self.conv_features): for other_feature in xrange(self.conv_features): if feature != other_feature: for n in xrange(self.n_excitatory_patch): self.connections[connection_name][feature * self.n_excitatory_patch + n, \ other_feature * self.n_excitatory_patch + n] = 17.4 # adding random inhibitory connections as specified if self.random_inhibition_prob != 0.0: for feature in xrange(self.conv_features): for other_feature in xrange(self.conv_features): for n_this in xrange(self.n_excitatory_patch): for n_other in xrange( self.n_excitatory_patch): if n_this != n_other: if b.random( ) < self.random_inhibition_prob: self.connections[connection_name][feature * self.n_excitatory_patch + n_this, \ other_feature * self.n_excitatory_patch + n_other] = 17.4 elif connection_type == 'ee': # create connection name (composed of population and connection types) connection_name = name + connection_type[ 0] + name + connection_type[1] # create a connection from the first group in conn_name with the second group self.connections[connection_name] = b.Connection(self.neuron_groups[connection_name[0:2]], \ self.neuron_groups[connection_name[2:4]], structure='sparse', state='g' + connection_type[0]) # instantiate the created connection if self.connectivity == 'all': for feature in xrange(self.conv_features): for other_feature in xrange(self.conv_features): if feature != other_feature: for this_n in xrange( self.n_excitatory_patch): for other_n in xrange( self.n_excitatory_patch): if is_lattice_connection( self. n_excitatory_patch_sqrt, this_n, other_n): self.connections[connection_name][feature * self.n_excitatory_patch + this_n, \ other_feature * self.n_excitatory_patch + other_n] = \ (b.random() + 0.01) * 0.3 elif self.connectivity == 'pairs': for feature in xrange(self.conv_features): if feature % 2 == 0: for this_n in xrange(self.n_excitatory_patch): for other_n in xrange( self.n_excitatory_patch): if is_lattice_connection( self.n_excitatory_patch_sqrt, this_n, other_n): self.connections[connection_name][feature * self.n_excitatory_patch + this_n, \ (feature + 1) * self.n_excitatory_patch + other_n] = (b.random() + 0.01) * 0.3 elif feature % 2 == 1: for this_n in xrange(self.n_excitatory_patch): for other_n in xrange( self.n_excitatory_patch): if is_lattice_connection( self.n_excitatory_patch_patch, this_n, other_n): self.connections[connection_name][feature * self.n_excitatory_patch + this_n, \ (feature - 1) * self.n_excitatory_patch + other_n] = (b.random() + 0.01) * 0.3 elif connectivity == 'linear': for feature in xrange(self.conv_features): if feature != self.conv_features - 1: for this_n in xrange(self.n_excitatory_patch): for other_n in xrange( self.n_excitatory_patch): if is_lattice_connection( self.n_excitatory_patch_sqrt, this_n, other_n): self.connections[connection_name][feature * self.n_excitatory_patch + this_n, \ (feature + 1) * self.n_excitatory_patch + other_n] = \ (b.random() + 0.01) * 0.3 if feature != 0: for this_n in xrange(self.n_excitatory_patch): for other_n in xrange( self.n_excitatory_patch): if is_lattice_connection( self.n_excitatory_patch_sqrt, this_n, other_n): self.connections[connection_name][feature * self.n_excitatory_patch + this_n, \ (feature - 1) * self.n_excitatory_patch + other_n] = \ (b.random() + 0.01) * 0.3 elif self.connectivity == 'none': pass # if STDP from excitatory -> excitatory is on and this connection is excitatory -> excitatory if 'ee' in self.recurrent_conn_names: self.stdp_methods[name + 'e' + name + 'e'] = b.STDP(self.connections[name + 'e' + name + 'e'], \ eqs=eqs_stdp_ee, pre=eqs_stdp_pre_ee, \ post=eqs_stdp_post_ee, wmin=0., wmax=self.wmax_ee) print '...creating monitors for:', name # spike rate monitors for excitatory and inhibitory neuron populations self.rate_monitors[name + 'e'] = b.PopulationRateMonitor(self.neuron_groups[name + 'e'], \ bin=(self.single_example_time + self.resting_time) / b.second) self.rate_monitors[name + 'i'] = b.PopulationRateMonitor(self.neuron_groups[name + 'i'], \ bin=(self.single_example_time + self.resting_time) / b.second) self.spike_counters[name + 'e'] = b.SpikeCounter( self.neuron_groups[name + 'e']) # record neuron population spikes self.spike_monitors[name + 'e'] = b.SpikeMonitor( self.neuron_groups[name + 'e']) self.spike_monitors[name + 'i'] = b.SpikeMonitor( self.neuron_groups[name + 'i']) if do_plot: b.figure(self.fig_num) fig_num += 1 b.ion() b.subplot(211) b.raster_plot(self.spike_monitors['Ae'], refresh=1000 * b.ms, showlast=1000 * b.ms) b.subplot(212) b.raster_plot(self.spike_monitors['Ai'], refresh=1000 * b.ms, showlast=1000 * b.ms) # specifying locations of lattice connections self.lattice_locations = {} if self.connectivity == 'all': for this_n in xrange(self.conv_features * self.n_excitatory_patch): self.lattice_locations[this_n] = [ other_n for other_n in xrange(self.conv_features * self.n_excitatory_patch) \ if is_lattice_connection(self.n_excitatory_patch_sqrt, \ this_n % self.n_excitatory_patch, other_n % self.n_excitatory_patch) ] elif self.connectivity == 'pairs': for this_n in xrange(self.conv_features * self.n_excitatory_patch): self.lattice_locations[this_n] = [] for other_n in xrange(self.conv_features * self.n_excitatory_patch): if this_n // self.n_excitatory_patch % 2 == 0: if is_lattice_connection(self.n_excitatory_patch_sqrt, this_n % self.n_excitatory_patch, \ other_n % self.n_excitatory_patch) and \ other_n // self.n_excitatory_patch == this_n // self.n_excitatory_patch + 1: self.lattice_locations[this_n].append(other_n) elif this_n // self.n_excitatory_patch % 2 == 1: if is_lattice_connection(self.n_excitatory_patch_sqrt, this_n % self.n_excitatory_patch, \ other_n % self.n_excitatory_patch) and \ other_n // self.n_excitatory_patch == this_n // self.n_excitatory_patch - 1: self.lattice_locations[this_n].append(other_n) elif self.connectivity == 'linear': for this_n in xrange(self.conv_features * self.n_excitatory_patch): self.lattice_locations[this_n] = [] for other_n in xrange(conv_features * self.n_excitatory_patch): if this_n // self.n_excitatory_patch != self.conv_features - 1: if is_lattice_connection(self.n_excitatory_patch_sqrt, this_n % self.n_excitatory_patch, \ other_n % self.n_excitatory_patch) and \ other_n // self.n_excitatory_patch == this_n // self.n_excitatory_patch + 1: self.lattice_locations[this_n].append(other_n) elif this_n // self.n_excitatory_patch != 0: if is_lattice_connection(self.n_excitatory_patch_sqrt, this_n % self.n_excitatory_patch, \ other_n % self.n_excitatory_patch) and \ other_n // self.n_excitatory_patch == this_n // self.n_excitatory_patch - 1: self.lattice_locations[this_n].append(other_n) # setting up parameters for weight normalization between patches num_lattice_connections = sum( [len(value) for value in lattice_locations.values()]) self.weight['ee_recurr'] = (num_lattice_connections / self.conv_features) * 0.15 # creating Poission spike train from input image (784 vector, 28x28 image) for name in self.input_population_names: self.input_groups[name + 'e'] = b.PoissonGroup(self.n_input, 0) self.rate_monitors[name + 'e'] = b.PopulationRateMonitor(self.input_groups[name + 'e'], \ bin=(self.single_example_time + self.resting_time) / b.second) # creating connections from input Poisson spike train to convolution patch populations for name in self.input_connection_names: print '\n...creating connections between', name[0], 'and', name[1] # for each of the input connection types (in this case, excitatory -> excitatory) for connection_type in self.input_conn_names: # saved connection name connection_name = name[0] + connection_type[0] + name[ 1] + connection_type[1] # create connections from the windows of the input group to the neuron population self.input_connections[connection_name] = b.Connection(self.input_groups['Xe'], \ self.neuron_groups[name[1] + connection_type[1]], structure='sparse', \ state='g' + connection_type[0], delay=True, max_delay=self.delay[connection_type][1]) for feature in xrange(self.conv_features): for n in xrange(self.n_excitatory_patch): for idx in xrange(self.conv_size**2): self.input_connections[connection_name][self.convolution_locations[n][idx], \ feature * self.n_excitatory_patch + n] = (b.random() + 0.01) * 0.3 # if excitatory -> excitatory STDP is specified, add it here (input to excitatory populations) print '...creating STDP for connection', name # STDP connection name connection_name = name[0] + connection_type[0] + name[ 1] + connection_type[1] # create the STDP object self.stdp_methods[connection_name] = b.STDP(self.input_connections[connection_name], \ eqs=eqs_stdp_ee, pre=eqs_stdp_pre_ee, post=eqs_stdp_post_ee, wmin=0., wmax=self.wmax_ee) print '\n'
# creating connections from input Poisson spike train to convolution patch populations for name in input_connection_names: print '\n...creating connections between', name[0], 'and', name[1] # for each of the input connection types (in this case, excitatory -> excitatory) for conn_type in input_conn_names: # saved connection name conn_name = name[0] + conn_type[0] + name[1] + conn_type[1] # get weight matrix depending on training or test phase if test_mode: weight_matrix = np.load( os.path.join(weights_dir, '_'.join([conn_name, ending + '.npy']))) else: weight_matrix = (b.random([n_input, conv_features]) + 0.01) * 0.3 # create connections from the windows of the input group to the neuron population input_connections[conn_name] = b.Connection(input_groups['Xe'], neuron_groups[name[1] + conn_type[1]], \ structure='sparse', state='g' + conn_type[0], delay=True, max_delay=delay[conn_type][1]) input_connections[conn_name].connect(input_groups[conn_name[0:2]], \ neuron_groups[conn_name[2:4]], weight_matrix, delay=delay[conn_type]) # if excitatory -> excitatory STDP is specified, add it here (input to excitatory populations) if not test_mode: print '...Creating STDP for connection', name # STDP connection name conn_name = name[0] + conn_type[0] + name[1] + conn_type[1] # create the STDP object stdp_methods[conn_name] = b.STDP(input_connections[conn_name], eqs=eqs_stdp_ee, \
if test_mode: for feature in xrange(conv_features): for n in xrange(n_e): for idx in xrange(conv_size**2): input_connections[conn_name][ convolution_locations[n][idx], feature * n_e + n] = weight_matrix[convolution_locations[n][idx], feature * n_e + n] else: for feature in xrange(conv_features): for n in xrange(n_e): for idx in xrange(conv_size**2): input_connections[conn_name][ convolution_locations[n][idx], feature * n_e + n] = (b.random() + 0.01) * 0.3 # if excitatory -> excitatory STDP is specified, add it here (input to excitatory populations) if ee_STDP_on: print '...creating STDP for connection', name # STDP connection name conn_name = name[0] + conn_type[0] + name[1] + conn_type[ 1] + '_' + ending # create the STDP object stdp_methods[conn_name] = b.STDP(input_connections[conn_name], eqs=eqs_stdp_ee, pre=eqs_stdp_pre_ee, post=eqs_stdp_post_ee, wmin=0., wmax=wmax_ee)