From 89371736e4a3cfef09d26bdc5e70ce639ba5be3f Mon Sep 17 00:00:00 2001 From: tonaerospace Date: Fri, 12 May 2023 19:50:02 +0700 Subject: [PATCH] refractoring --- src/Ironpen.jl | 8 +- src/forward.jl | 105 ++++++------- src/learn.jl | 218 +++++++++++++------------- src/snn_utils.jl | 60 ++++---- src/types.jl | 391 +++++++++++++++++++++++------------------------ 5 files changed, 382 insertions(+), 400 deletions(-) diff --git a/src/Ironpen.jl b/src/Ironpen.jl index 7da9acb..260e6c8 100644 --- a/src/Ironpen.jl +++ b/src/Ironpen.jl @@ -34,9 +34,11 @@ using .interface """ Todo: - [*3] no "start learning" use reset learning and "inference", "learning" mode instead - [4] output neuron connect to multiple compute neuron - [7] add time-based learning method. Also implement "thinking period" + [*3] implement "start learning", reset learning and "during_learning", "end_learning and + "inference" + [4] output neuron connect to random multiple compute neurons + [7] add time-based learning method. + [] implement "thinking period" [8] verify that model can complete learning cycle with no error [5] synaptic connection strength concept [6] neuroplasticity() i.e. change connection diff --git a/src/forward.jl b/src/forward.jl index 43a3450..503bab3 100644 --- a/src/forward.jl +++ b/src/forward.jl @@ -12,7 +12,7 @@ using ..types, ..snn_utils """ function (m::model)(input_data::AbstractVector) # m.global_tick += 1 - m.time_stamp += 1 + m.timeStep += 1 # process all corresponding KFN raw_model_respond = m.knowledgeFn[:I](m, input_data) @@ -28,9 +28,9 @@ end """ function (kfn::kfn_1)(m::model, input_data::AbstractVector) - kfn.time_stamp = m.time_stamp + kfn.timeStep = m.timeStep kfn.softreset = m.softreset - kfn.learning_stage = m.learning_stage + kfn.learningStage = m.learningStage kfn.error = m.error # generate noise @@ -40,53 +40,38 @@ function (kfn::kfn_1)(m::model, input_data::AbstractVector) input_data = [noise; input_data] # noise start from neuron id 1 - for n in kfn.neurons_array + for n in kfn.neuronsArray timestep_forward!(n) end - for n in kfn.output_neurons_array + for n in kfn.outputNeuronsArray timestep_forward!(n) end - kfn.learning_stage = m.learning_stage - if kfn.learning_stage == "start_learning" - # reset params here instead of at the end_learning so that neuron's parameter data - # don't gets wiped and can be logged for visualization later - for n in kfn.neurons_array - # epsilon_rec need to be reset because it counting how many each synaptic fires and - # use this info to calculate how much synaptic weight should be adjust - reset_learning_params!(n) - end - - # clear variables - kfn.firing_neurons_list = Vector{Int64}() - kfn.outputs = nothing - end - # pass input_data into input neuron. # number of data point equals to number of input neuron starting from id 1 for (i, data) in enumerate(input_data) - kfn.neurons_array[i].z_t1 = data + kfn.neuronsArray[i].z_t1 = data end - kfn.snn_firing_state_t0 = [n.z_t for n in kfn.neurons_array] #TODO check if it is used? + kfn.firedNeurons_t0 = [n.z_t for n in kfn.neuronsArray] #TODO check if it is used? - #CHANGE Threads.@threads for n in kfn.neurons_array - for n in kfn.neurons_array + #CHANGE Threads.@threads for n in kfn.neuronsArray + for n in kfn.neuronsArray n(kfn) end - kfn.snn_firing_state_t1 = [n.z_t1 for n in kfn.neurons_array] - append!(kfn.firing_neurons_list, findall(kfn.snn_firing_state_t1)) # store id of neuron that fires - if kfn.learning_stage == "end_learning" # use for random new neuron connection - kfn.firing_neurons_list |> unique! + kfn.firedNeurons_t1 = [n.z_t1 for n in kfn.neuronsArray] + append!(kfn.firedNeurons, findall(kfn.firedNeurons_t1)) # store id of neuron that fires + if kfn.learningStage == "end_learning" + kfn.firedNeurons |> unique! # use for random new neuron connection end - # Threads.@threads for n in kfn.output_neurons_array - for n in kfn.output_neurons_array + # Threads.@threads for n in kfn.outputNeuronsArray + for n in kfn.outputNeuronsArray n(kfn) end - out = [n.out_t1 for n in kfn.output_neurons_array] + out = [n.out_t1 for n in kfn.outputNeuronsArray] return out end @@ -96,7 +81,7 @@ end """ passthrough_neuron forward() """ function (n::passthrough_neuron)(kfn::knowledgeFn) - n.time_stamp = kfn.time_stamp + n.timeStep = kfn.timeStep # n.global_tick = kfn.global_tick end @@ -105,40 +90,40 @@ end """ lif_neuron forward() """ function (n::lif_neuron)(kfn::knowledgeFn) - n.time_stamp = kfn.time_stamp + n.timeStep = kfn.timeStep # pulling other neuron's firing status at time t - n.z_i_t = getindex(kfn.snn_firing_state_t0, n.subscription_list) - n.z_i_t .*= n.sub_ExIn_type + n.z_i_t = getindex(kfn.firedNeurons_t0, n.subscriptionList) + n.z_i_t .*= n.subExInType - if n.refractory_counter != 0 - n.refractory_counter -= 1 + if n.refractoryCounter != 0 + n.refractoryCounter -= 1 # neuron is in refractory state, skip all calculation n.z_t1 = false # used by timestep_forward() in kfn. Set to zero because neuron spike # last only 1 timestep follow by a period of refractory. - n.recurrent_signal = n.recurrent_signal * 0.0 + n.recSignal = n.recSignal * 0.0 # Exponantial decay of v_t1 - n.v_t1 = n.v_t * n.alpha^(n.time_stamp - n.last_firing_time) # or n.v_t1 = n.alpha * n.v_t + n.v_t1 = n.v_t * n.alpha^(n.timeStep - n.lastFiringTime) # or n.v_t1 = n.alpha * n.v_t else - n.recurrent_signal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed + n.recSignal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed n.alpha_v_t = n.alpha * n.v_t - n.v_t1 = n.alpha_v_t + n.recurrent_signal + n.v_t1 = n.alpha_v_t + n.recSignal n.v_t1 = no_negative!.(n.v_t1) if n.v_t1 > n.v_th n.z_t1 = true - n.refractory_counter = n.refractory_duration - n.firing_counter += 1 - n.v_t1 = n.v_t1 - n.v_th + n.refractoryCounter = n.refractoryDuration + n.firingCounter += 1 + n.v_t1 = n.vRest else n.z_t1 = false end # there is a difference from alif formula - n.phi = (n.gamma_pd / n.v_th) * max(0, 1 - (n.v_t1 - n.v_th) / n.v_th) + n.phi = (n.gammaPd / n.v_th) * max(0, 1 - (n.v_t1 - n.v_th) / n.v_th) end end @@ -147,41 +132,41 @@ end """ alif_neuron forward() """ function (n::alif_neuron)(kfn::knowledgeFn) - n.time_stamp = kfn.time_stamp + n.timeStep = kfn.timeStep - n.z_i_t = getindex(kfn.snn_firing_state_t0, n.subscription_list) - n.z_i_t .*= n.sub_ExIn_type + n.z_i_t = getindex(kfn.firedNeurons_t0, n.subscriptionList) + n.z_i_t .*= n.subExInType - if n.refractory_counter != 0 - n.refractory_counter -= 1 + if n.refractoryCounter != 0 + n.refractoryCounter -= 1 # neuron is in refractory state, skip all calculation n.z_t1 = false # used by timestep_forward() in kfn. Set to zero because neuron spike last only 1 timestep follow by a period of refractory. n.a = (n.rho * n.a) + ((1 - n.rho) * n.z_t) - n.recurrent_signal = n.recurrent_signal * 0.0 + n.recSignal = n.recSignal * 0.0 # Exponantial decay of v_t1 - n.v_t1 = n.v_t * n.alpha^(n.time_stamp - n.last_firing_time) # or n.v_t1 = n.alpha * n.v_t + n.v_t1 = n.v_t * n.alpha^(n.timeStep - n.lastFiringTime) # or n.v_t1 = n.alpha * n.v_t n.phi = 0 else n.z_t = isnothing(n.z_t) ? false : n.z_t n.a = (n.rho * n.a) + ((1 - n.rho) * n.z_t) n.av_th = n.v_th + (n.beta * n.a) - n.recurrent_signal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed + n.recSignal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed n.alpha_v_t = n.alpha * n.v_t - n.v_t1 = n.alpha_v_t + n.recurrent_signal + n.v_t1 = n.alpha_v_t + n.recSignal n.v_t1 = no_negative!.(n.v_t1) if n.v_t1 > n.av_th n.z_t1 = true - n.refractory_counter = n.refractory_duration - n.firing_counter += 1 - n.v_t1 = n.v_t1 - n.v_th + n.refractoryCounter = n.refractoryDuration + n.firingCounter += 1 + n.v_t1 = n.vRest else n.z_t1 = false end # there is a difference from lif formula - n.phi = (n.gamma_pd / n.v_th) * max(0, 1 - (n.v_t1 - n.av_th) / n.v_th) + n.phi = (n.gammaPd / n.v_th) * max(0, 1 - (n.v_t1 - n.av_th) / n.v_th) end end @@ -191,8 +176,8 @@ end In this implementation, each output neuron is fully connected to every lif and alif neuron. """ function (n::linear_neuron)(kfn::T) where T<:knowledgeFn - n.time_stamp = kfn.time_stamp - n.out_t1 = getindex(kfn.snn_firing_state_t1, n.subscription_list)[1] + n.timeStep = kfn.timeStep + n.out_t1 = getindex(kfn.firedNeurons_t1, n.subscriptionList)[1] end diff --git a/src/learn.jl b/src/learn.jl index 8ab4e48..71e79ff 100644 --- a/src/learn.jl +++ b/src/learn.jl @@ -10,56 +10,47 @@ export learn! #------------------------------------------------------------------------------------------------100 -function learn!(m::model, model_respond, correct_answer) - if m.learning_stage == "learning" - #WORKING compute error - if m.time_stamp < m.model_params[:perfect_timing] - too_early = m.model_params[:perfect_timing] - m.time_stamp - model_error = (model_respond .- correct_answer) * too_early - - - - - - - - - - - - - model_error = Flux.logitcrossentropy(model_respond, correct_answer) - output_elements_error = model_respond - correct_answer - - learn!(m.knowledgeFn[:I], model_error, output_elements_error) - - - - - - - - - - - - - - - - - +function learn!(m::model, modelRespond, correctAnswer=nothing, correctTiming=nothing) + # set all KFN + if m.learningStage == "start_learning" + m.knowledgeFn[:I].learningStage = "start_learning" + elseif m.learningStage == "end_learning" + m.knowledgeFn[:I].learningStage = "end_learning" else - model_error = nothing end + + #WORKING compute error + # timingError = + + + too_early = m.modelParams[:perfect_timing] - m.timeStep + model_error = (model_respond .- correct_answer) * too_early + + + + + + + + + + model_error = Flux.logitcrossentropy(model_respond, correct_answer) + output_elements_error = model_respond - correct_answer + + learn!(m.knowledgeFn[:I], model_error, output_elements_error) + + + + + return model_error end # function learn!(m::model, raw_model_respond, correct_answer=nothing) -# if m.learning_stage != "doing_inference" +# if m.learningStage != "doing_inference" # model_error = Flux.logitcrossentropy(raw_model_respond, correct_answer) # output_elements_error = raw_model_respond - correct_answer @@ -77,18 +68,33 @@ end """ knowledgeFn learn() """ function learn!(kfn::knowledgeFn, error::Union{Float64,Nothing}=nothing, - output_error::Union{Vector,Nothing}=nothing) + outputError::Union{Vector,Nothing}=nothing) kfn.error = error - kfn.output_error = output_error + kfn.outputError = outputError - # Threads.@threads for n in kfn.neurons_array - for n in kfn.neurons_array + kfn.learningStage = m.learningStage + if m.learningStage == "start_learning" + # reset params here instead of at the end_learning so that neuron's parameter data + # don't gets wiped and can be logged for visualization later + for n in kfn.neuronsArray + # epsilonRec need to be reset because it counting how many each synaptic fires and + # use this info to calculate how much synaptic weight should be adjust + reset_learning_params!(n) + end + + # clear variables + kfn.firedNeurons = Vector{Int64}() + kfn.outputs = nothing + end + + # Threads.@threads for n in kfn.neuronsArray + for n in kfn.neuronsArray learn!(n, kfn) # Neurons are always learning, besides error from model output end - if kfn.output_error !== nothing - # Threads.@threads for n in kfn.output_neurons_array - for n in kfn.output_neurons_array # not use multithreading because 1st output neuron + if kfn.outputError !== nothing + # Threads.@threads for n in kfn.outputNeuronsArray + for n in kfn.outputNeuronsArray # not use multithreading because 1st output neuron # will set learning rate that will be used by # other output neurons learn!(n, kfn) @@ -96,21 +102,21 @@ function learn!(kfn::knowledgeFn, error::Union{Float64,Nothing}=nothing, #TODO: put other KFN to learn here # for main loop user's display and training's exit condition - avg_neurons_firing_rate = 0.0 - for n in kfn.neurons_array + avgNeuronsFiringRate = 0.0 + for n in kfn.neuronsArray if typeof(n) <: compute_neuron - avg_neurons_firing_rate += n.firing_rate + avgNeuronsFiringRate += n.firingRate end end - kfn.avg_neurons_firing_rate = avg_neurons_firing_rate / - kfn.kfn_params[:compute_neuron_number] - avg_neurons_v_t1 = 0.0 - for n in kfn.neurons_array + kfn.avgNeuronsFiringRate = avgNeuronsFiringRate / + kfn.kfnParams[:compute_neuron_number] + avgNeurons_v_t1 = 0.0 + for n in kfn.neuronsArray if typeof(n) <: compute_neuron - avg_neurons_v_t1 += n.v_t1 + avgNeurons_v_t1 += n.v_t1 end end - kfn.avg_neurons_v_t1 = avg_neurons_v_t1 / kfn.kfn_params[:compute_neuron_number] + kfn.avgNeurons_v_t1 = avgNeurons_v_t1 / kfn.kfnParams[:compute_neuron_number] end end @@ -125,139 +131,139 @@ end function learn!(n::lif_neuron, kfn::knowledgeFn) if n.learnable_flag == true - n.decayed_epsilon_rec = n.alpha * n.epsilon_rec - n.epsilon_rec = n.decayed_epsilon_rec + n.z_i_t - n.e_rec = n.phi * n.epsilon_rec + n.decayedEpsilonRec = n.alpha * n.epsilonRec + n.epsilonRec = n.decayedEpsilonRec + n.z_i_t + n.eRec = n.phi * n.epsilonRec end # a piece of knowledgeFn error that belongs to this neuron n.error = isnothing(kfn.error) ? nothing : kfn.error * n.Bn - n.learning_stage = kfn.learning_stage + n.learningStage = kfn.learningStage # accumulate voltage regularization terms Snn_utils.cal_v_reg!(n) - if n.learning_stage == "doing_inference" + if n.learningStage == "doing_inference" # no learning - elseif n.learning_stage == "start_learning" || - n.learning_stage == "start_learning_no_wchange_reset" + elseif n.learningStage == "start_learning" || + n.learningStage == "start_learning_no_wchange_reset" # if error signal available then accumulates Δw if n.error !== nothing Snn_utils.firing_rate!(n) Snn_utils.firing_diff!(n) - n.w_rec_change = n.w_rec_change + + n.wRecChange = n.wRecChange + -apply!(n.optimiser, n.w_rec, - (n.error + Snn_utils.voltage_error!(n) + n.firing_rate_error) * n.e_rec) + + (n.error + Snn_utils.voltage_error!(n) + n.firingRateError) * n.eRec) + -Snn_utils.firing_rate_regulator!(n) + -Snn_utils.voltage_regulator!(n) end - elseif n.learning_stage == "during_learning" + elseif n.learningStage == "during_learning" # if error signal available then accumulates Δw if n.error !== nothing Snn_utils.firing_rate!(n) Snn_utils.firing_diff!(n) - n.w_rec_change = n.w_rec_change + + n.wRecChange = n.wRecChange + -apply!(n.optimiser, n.w_rec, - (n.error + Snn_utils.voltage_error!(n) + n.firing_rate_error) * n.e_rec) + + (n.error + Snn_utils.voltage_error!(n) + n.firingRateError) * n.eRec) + -Snn_utils.firing_rate_regulator!(n) + -Snn_utils.voltage_regulator!(n) end - elseif n.learning_stage == "end_learning" + elseif n.learningStage == "end_learning" # if error signal available then accumulates Δw if n.error !== nothing Snn_utils.firing_rate!(n) Snn_utils.firing_diff!(n) - n.w_rec_change = n.w_rec_change + + n.wRecChange = n.wRecChange + -apply!(n.optimiser, n.w_rec, - (n.error + Snn_utils.voltage_error!(n) + n.firing_rate_error) * n.e_rec) + + (n.error + Snn_utils.voltage_error!(n) + n.firingRateError) * n.eRec) + -Snn_utils.firing_rate_regulator!(n) + -Snn_utils.voltage_regulator!(n) end not_zero = (!iszero).(n.w_rec) - # set 0 in w_rec_change update according to 0 in w_rec for hard constrain connection - n.w_rec = n.w_rec + (not_zero .* n.w_rec_change) + # set 0 in wRecChange update according to 0 in w_rec for hard constrain connection + n.w_rec = n.w_rec + (not_zero .* n.wRecChange) replace!(x -> x < 0 ? 0 : x, n.w_rec) # no negative weight - Snn_utils.neuroplasticity!(n, kfn.firing_neurons_list) + Snn_utils.neuroplasticity!(n, kfn.firedNeurons) end end """ alif_neuron learn() """ function learn!(n::alif_neuron, kfn::knowledgeFn) - n.decayed_epsilon_rec = n.alpha * n.epsilon_rec - n.epsilon_rec = n.decayed_epsilon_rec + n.z_i_t - n.epsilon_rec_a = (n.phi * n.epsilon_rec) + - ((n.rho - (n.phi * n.beta)) * n.epsilon_rec_a) - n.e_rec_v = n.phi * n.epsilon_rec - n.e_rec_a = -n.phi * n.beta * n.epsilon_rec_a - n.e_rec = n.e_rec_v + n.e_rec_a + n.decayedEpsilonRec = n.alpha * n.epsilonRec + n.epsilonRec = n.decayedEpsilonRec + n.z_i_t + n.epsilonRecA = (n.phi * n.epsilonRec) + + ((n.rho - (n.phi * n.beta)) * n.epsilonRecA) + n.eRec_v = n.phi * n.epsilonRec + n.eRec_a = -n.phi * n.beta * n.epsilonRecA + n.eRec = n.eRec_v + n.eRec_a # a piece of knowledgeFn error that belongs to this neuron n.error = isnothing(kfn.error) ? nothing : kfn.error * n.Bn - n.learning_stage = kfn.learning_stage + n.learningStage = kfn.learningStage - if n.learning_stage == "doing_inference" + if n.learningStage == "doing_inference" # no learning - elseif n.learning_stage == "start_learning" || - n.learning_stage == "start_learning_no_wchange_reset" + elseif n.learningStage == "start_learning" || + n.learningStage == "start_learning_no_wchange_reset" # if error signal available then accumulates Δw if n.error !== nothing Snn_utils.firing_rate!(n) Snn_utils.firing_diff!(n) - n.w_rec_change = n.w_rec_change + + n.wRecChange = n.wRecChange + -apply!(n.optimiser, n.w_rec, - (n.error + Snn_utils.voltage_error!(n) + n.firing_rate_error) * n.e_rec) + + (n.error + Snn_utils.voltage_error!(n) + n.firingRateError) * n.eRec) + -Snn_utils.firing_rate_regulator!(n) + -Snn_utils.voltage_regulator!(n) end - elseif n.learning_stage == "during_learning" + elseif n.learningStage == "during_learning" # if error signal available then accumulates Δw if n.error !== nothing Snn_utils.firing_rate!(n) Snn_utils.firing_diff!(n) - n.w_rec_change = n.w_rec_change + + n.wRecChange = n.wRecChange + -apply!(n.optimiser, n.w_rec, - (n.error + Snn_utils.voltage_error!(n) + n.firing_rate_error) * n.e_rec) + + (n.error + Snn_utils.voltage_error!(n) + n.firingRateError) * n.eRec) + -Snn_utils.firing_rate_regulator!(n) + -Snn_utils.voltage_regulator!(n) end - elseif n.learning_stage == "end_learning" + elseif n.learningStage == "end_learning" # if error signal available then accumulates Δw if n.error !== nothing Snn_utils.firing_rate!(n) Snn_utils.firing_diff!(n) - n.w_rec_change = n.w_rec_change + + n.wRecChange = n.wRecChange + -apply!(n.optimiser, n.w_rec, - (n.error + Snn_utils.voltage_error!(n) + n.firing_rate_error) * n.e_rec) + + (n.error + Snn_utils.voltage_error!(n) + n.firingRateError) * n.eRec) + -Snn_utils.firing_rate_regulator!(n) + -Snn_utils.voltage_regulator!(n) end not_zero = (!iszero).(n.w_rec) - # set 0 in w_rec_change update according to 0 in w_rec for hard constrain connection - n.w_rec = n.w_rec + (not_zero .* n.w_rec_change) + # set 0 in wRecChange update according to 0 in w_rec for hard constrain connection + n.w_rec = n.w_rec + (not_zero .* n.wRecChange) replace!(x -> x < 0 ? 0 : x, n.w_rec) # no negative weight - Snn_utils.neuroplasticity!(n, kfn.firing_neurons_list) + Snn_utils.neuroplasticity!(n, kfn.firedNeurons) end end """ linear_neuron learn() """ function learn!(n::linear_neuron, kfn::knowledgeFn) - n.error = kfn.output_error[n.id] - n.learning_stage = kfn.learning_stage + n.error = kfn.outputError[n.id] + n.learningStage = kfn.learningStage - if n.learning_stage == "doing_inference" + if n.learningStage == "doing_inference" # no learning - elseif n.learning_stage == "start_learning" + elseif n.learningStage == "start_learning" # if error signal available then accumulates Δw if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training Δw = -apply!(n.optimiser, n.w_out, (n.error * n.epsilon_j)) @@ -266,13 +272,13 @@ function learn!(n::linear_neuron, kfn::knowledgeFn) Δb = -n.eta * n.error n.b_change = n.b_change + Δb elseif n.error !== nothing && n.id !== 1 - n.eta = kfn.output_neurons_array[1].eta + n.eta = kfn.outputNeuronsArray[1].eta Δw = -n.eta * n.error * n.epsilon_j n.w_out_change = n.w_out_change + Δw Δb = -n.eta * n.error n.b_change = n.b_change + Δb end - elseif n.learning_stage == "during_learning" + elseif n.learningStage == "during_learning" # if error signal available then accumulates Δw if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training Δw = -apply!(n.optimiser, n.w_out, (n.error * n.epsilon_j)) @@ -281,13 +287,13 @@ function learn!(n::linear_neuron, kfn::knowledgeFn) Δb = -n.eta * n.error n.b_change = n.b_change + Δb elseif n.error !== nothing && n.id !== 1 - n.eta = kfn.output_neurons_array[1].eta + n.eta = kfn.outputNeuronsArray[1].eta Δw = -n.eta * n.error * n.epsilon_j n.w_out_change = n.w_out_change + Δw Δb = -n.eta * n.error n.b_change = n.b_change + Δb end - elseif n.learning_stage == "end_learning" + elseif n.learningStage == "end_learning" # if error signal available then accumulates Δw if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training Δw = -apply!(n.optimiser, n.w_out, (n.error * n.epsilon_j)) @@ -296,7 +302,7 @@ function learn!(n::linear_neuron, kfn::knowledgeFn) Δb = -n.eta * n.error n.b_change = n.b_change + Δb elseif n.error !== nothing && n.id !== 1 - n.eta = kfn.output_neurons_array[1].eta + n.eta = kfn.outputNeuronsArray[1].eta Δw = -n.eta * n.error * n.epsilon_j n.w_out_change = n.w_out_change + Δw Δb = -n.eta * n.error diff --git a/src/snn_utils.jl b/src/snn_utils.jl index e5fb725..c9b1b86 100644 --- a/src/snn_utils.jl +++ b/src/snn_utils.jl @@ -32,24 +32,22 @@ no_negative!(x) = x < 0.0 ? 0.0 : x precision(x::Array{<:Array}) = ( std(mean.(x)) / mean(mean.(x)) ) * 100 # reset functions for LIF/ALIF neuron -reset_last_firing_time!(n::compute_neuron) = n.last_firing_time = 0.0 +reset_last_firing_time!(n::compute_neuron) = n.lastFiringTime = 0.0 reset_refractory_state_active!(n::compute_neuron) = n.refractory_state_active = false reset_v_t!(n::compute_neuron) = n.v_t = n.v_t_default reset_z_t!(n::compute_neuron) = n.z_t = false -reset_epsilon_rec!(n::compute_neuron) = n.epsilon_rec = n.epsilon_rec * 0.0 -reset_epsilon_rec_a!(n::alif_neuron) = n.epsilon_rec_a = n.epsilon_rec_a * 0.0 +reset_epsilon_rec!(n::compute_neuron) = n.epsilonRec = n.epsilonRec * 0.0 +reset_epsilon_rec_a!(n::alif_neuron) = n.epsilonRecA = n.epsilonRecA * 0.0 reset_epsilon_in!(n::compute_neuron) = n.epsilon_in = isnothing(n.epsilon_in) ? nothing : n.epsilon_in * 0.0 reset_error!(n::Union{compute_neuron, linear_neuron}) = n.error = nothing reset_w_in_change!(n::compute_neuron) = n.w_in_change = isnothing(n.w_in_change) ? nothing : n.w_in_change * 0.0 -reset_w_rec_change!(n::compute_neuron) = n.w_rec_change = n.w_rec_change * 0.0 +reset_w_rec_change!(n::compute_neuron) = n.wRecChange = n.wRecChange * 0.0 reset_a!(n::alif_neuron) = n.a = n.a * 0.0 reset_reg_voltage_a!(n::compute_neuron) = n.reg_voltage_a = n.reg_voltage_a * 0.0 reset_reg_voltage_b!(n::compute_neuron) = n.reg_voltage_b = n.reg_voltage_b * 0.0 reset_reg_voltage_error!(n::compute_neuron) = n.reg_voltage_error = n.reg_voltage_error * 0.0 -reset_firing_counter!(n::compute_neuron) = n.firing_counter = n.firing_counter * 0.0 -reset_firing_diff!(n::Union{compute_neuron, linear_neuron}) = n.firing_diff = n.firing_diff * 0.0 -reset_previous_error!(n::Union{compute_neuron}) = - n.previous_error = n.previous_error * 0.0 +reset_firing_counter!(n::compute_neuron) = n.firingCounter = n.firingCounter * 0.0 +reset_firing_diff!(n::Union{compute_neuron, linear_neuron}) = n.firingDiff = n.firingDiff * 0.0 # reset function for output neuron reset_epsilon_j!(n::linear_neuron) = n.epsilon_j = n.epsilon_j * 0.0 @@ -151,7 +149,7 @@ end function store_knowledgefn_error!(kfn::knowledgeFn) # condition to adjust nueron in KFN plane in addition to weight adjustment inside each neuron - if kfn.learning_stage == "start_learning" + if kfn.learningStage == "start_learning" if kfn.recent_knowledgeFn_error === nothing && kfn.knowledgeFn_error === nothing kfn.recent_knowledgeFn_error = [[]] elseif kfn.recent_knowledgeFn_error === nothing @@ -161,13 +159,13 @@ function store_knowledgefn_error!(kfn::knowledgeFn) else push!(kfn.recent_knowledgeFn_error, [kfn.knowledgeFn_error]) end - elseif kfn.learning_stage == "during_learning" + elseif kfn.learningStage == "during_learning" if kfn.knowledgeFn_error === nothing #skip else push!(kfn.recent_knowledgeFn_error[end], kfn.knowledgeFn_error) end - elseif kfn.learning_stage == "end_learning" + elseif kfn.learningStage == "end_learning" if kfn.recent_knowledgeFn_error === nothing #skip else @@ -184,15 +182,15 @@ end function update_Bn!(kfn::knowledgeFn) Δw = nothing - for n in kfn.output_neurons_array + for n in kfn.outputNeuronsArray Δw = Δw === nothing ? n.w_out_change : Δw + n.w_out_change n.w_out = n.w_out - (n.Bn_wout_decay * n.w_out) # w_out decay end - # Δw = Δw / kfn.kfn_params[:linear_neuron_number] # average + # Δw = Δw / kfn.kfnParams[:linear_neuron_number] # average - input_neuron_number = kfn.kfn_params[:input_neuron_number] # skip input neuron - for i = 1:kfn.kfn_params[:compute_neuron_number] - n = kfn.neurons_array[input_neuron_number+i] + input_neuron_number = kfn.kfnParams[:input_neuron_number] # skip input neuron + for i = 1:kfn.kfnParams[:compute_neuron_number] + n = kfn.neuronsArray[input_neuron_number+i] n.Bn = n.Bn + Δw[i] n.Bn = n.Bn - (n.Bn_wout_decay * n.Bn) # w_out decay end @@ -208,7 +206,7 @@ function cal_v_reg!(n::lif_neuron) component_b = n.v_t1 - n.v_th < 0 ? 0 : n.v_t1 - n.v_th #FIXME: not sure the following line is correct - n.reg_voltage_b = n.reg_voltage_b + (component_b * n.epsilon_rec) + n.reg_voltage_b = n.reg_voltage_b + (component_b * n.epsilonRec) end function cal_v_reg!(n::alif_neuron) @@ -219,7 +217,7 @@ function cal_v_reg!(n::alif_neuron) component_b = n.v_t1 - n.av_th < 0 ? 0 : n.v_t1 - n.av_th #FIXME: not sure the following line is correct - n.reg_voltage_b = n.reg_voltage_b + (component_b * (n.epsilon_rec - n.epsilon_rec_a)) + n.reg_voltage_b = n.reg_voltage_b + (component_b * (n.epsilonRec - n.epsilonRecA)) end function voltage_error!(n::compute_neuron) @@ -232,23 +230,23 @@ function voltage_regulator!(n::compute_neuron) # running average return Δw end -function firing_rate_error(kfn::knowledgeFn) - start_id = kfn.kfn_params[:input_neuron_number] + 1 - return 0.5 * sum([(n.firing_diff)^2 for n in kfn.neurons_array[start_id:end]]) +function firingRateError(kfn::knowledgeFn) + start_id = kfn.kfnParams[:input_neuron_number] + 1 + return 0.5 * sum([(n.firingDiff)^2 for n in kfn.neuronsArray[start_id:end]]) end function firing_rate_regulator!(n::compute_neuron) - # n.firing_rate NOT running average (average over learning batch) + # n.firingRate NOT running average (average over learning batch) Δw = n.optimiser.eta * n.c_reg * - (n.firing_rate - n.firing_rate_target) * n.e_rec - Δw = n.firing_rate > n.firing_rate_target ? Δw : Δw * 0.0 + (n.firingRate - n.firingRateTarget) * n.eRec + Δw = n.firingRate > n.firingRateTarget ? Δw : Δw * 0.0 return Δw end -firing_rate!(n::compute_neuron) = n.firing_rate = (n.firing_counter / n.time_stamp) * 1000 -firing_diff!(n::compute_neuron) = n.firing_diff = n.firing_rate - n.firing_rate_target +firing_rate!(n::compute_neuron) = n.firingRate = (n.firingCounter / n.timeStep) * 1000 +firing_diff!(n::compute_neuron) = n.firingDiff = n.firingRate - n.firingRateTarget -function neuroplasticity!(n::compute_neuron, firing_neurons_list::Vector) +function neuroplasticity!(n::compute_neuron, firedNeurons::Vector) # if there is 0-weight then replace it with new connection zero_weight_index = findall(iszero.(n.w_rec)) if length(zero_weight_index) != 0 @@ -257,8 +255,8 @@ function neuroplasticity!(n::compute_neuron, firing_neurons_list::Vector) not fire = no information """ - subscribe_options = filter(x -> x ∉ [n.id], firing_neurons_list) # exclude this neuron id from the list - filter!(x -> x ∉ n.subscription_list, subscribe_options) # exclude this neuron's subscription_list from the list + subscribe_options = filter(x -> x ∉ [n.id], firedNeurons) # exclude this neuron id from the list + filter!(x -> x ∉ n.subscriptionList, subscribe_options) # exclude this neuron's subscriptionList from the list shuffle!(subscribe_options) end @@ -266,7 +264,7 @@ function neuroplasticity!(n::compute_neuron, firing_neurons_list::Vector) percentage = [new_connection_percent, 100.0 - new_connection_percent] / 100.0 for i in zero_weight_index if Utils.random_choices([true, false], percentage) - n.subscription_list[i] = pop!(subscribe_options) + n.subscriptionList[i] = pop!(subscribe_options) n.w_rec[i] = 0.01 # new connection should not send large signal otherwise it would throw # RSNN off path. Let weight grow by an optimiser end @@ -283,7 +281,7 @@ function push_epsilon_rec_a!(n::lif_neuron) end function push_epsilon_rec_a!(n::alif_neuron) - push!(n.epsilon_rec_a, 0) + push!(n.epsilonRecA, 0) end diff --git a/src/types.jl b/src/types.jl index f84842b..b754881 100644 --- a/src/types.jl +++ b/src/types.jl @@ -26,19 +26,19 @@ abstract type compute_neuron <: neuron end """ Base.@kwdef mutable struct model <: Ironpen knowledgeFn::Union{Dict,Nothing} = nothing - model_params::Union{Dict,Nothing} = nothing + modelParams::Union{Dict,Nothing} = nothing error::Union{Float64,Nothing} = 0.0 - output_error::Union{Array,Nothing} = Vector{AbstractFloat}() + outputError::Union{Array,Nothing} = Vector{AbstractFloat}() """ "inference" = no learning params will be collected. "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time - correct answer is available then merge Δw_rec_change into w_rec_change then + correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ - learning_stage::String = "inference" + "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + learningStage::String = "inference" softreset::Bool = false - time_stamp::Number = 0.0 + timeStep::Number = 0.0 end """ Model outer constructor @@ -49,9 +49,9 @@ end :v_th => 2.0, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :z_t => false, # neuron firing status at time = t :z_t1 => false, # neuron firing status at time = t+1 - :gamma_pd => 0.3, # discount factor. The value is from the paper + :gammaPd => 0.3, # discount factor. The value is from the paper :phi => 0.0, # psuedo derivative - :refractory_duration => 2.0, # neuron refractory period in tick + :refractoryDuration => 2.0, # neuron refractory period in tick :delta => 1.0, :tau_m => 20.0, # membrane time constant in millisecond. The value is from the paper :eta => 0.01, # learning rate @@ -59,15 +59,15 @@ end I_kfn = Ironpen_ai_gpu.knowledgeFn(I_kfnparams, lif_neuron_params, alif_neuron_params, linear_neuron_params) - model_params_1 = Dict(:knowledgeFn => Dict(:I => I_kfn, + modelParams_1 = Dict(:knowledgeFn => Dict(:I => I_kfn, :run => run_kfn), - :learning_stage => "doing_inference",) + :learningStage => "doing_inference",) - model_1 = Ironpen_ai_gpu.model(model_params_1) + model_1 = Ironpen_ai_gpu.model(modelParams_1) """ function model(params::Dict) m = model() - m.model_params = params + m.modelParams = params fields = fieldnames(typeof(m)) for i in fields @@ -84,39 +84,37 @@ end """ knowledgeFn struct """ Base.@kwdef mutable struct kfn_1 <: knowledgeFn - knowledgefn_name::Union{String,Nothing} = nothing - kfn_params::Union{Dict,Nothing} = nothing # store params of knowledgeFn itself for later use - time_stamp::Number = 0.0 + knowledgeFnName::Union{String,Nothing} = nothing + kfnParams::Union{Dict,Nothing} = nothing # store params of knowledgeFn itself for later use + timeStep::Number = 0.0 # Bn contain error coefficient for both neurons and output neurons in one place Bn::Vector{Float64} = Vector{Float64}() # error projection coefficient from kfn output's error to each neurons's error - neurons_array::Union{Array,Nothing} = [] # put neurons here + neuronsArray::Union{Array,Nothing} = [] # put neurons here """ put output neuron here. I seperate output neuron because 1. its calculation is difference than other neuron types 2. other neuron type will not induced to connnect to output neuron 3. output neuron does not induced to connect to its own type """ - output_neurons_array::Union{Array,Nothing} = [] + outputNeuronsArray::Union{Array,Nothing} = [] """ "inference" = no learning params will be collected. "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time - correct answer is available then merge Δw_rec_change into w_rec_change then + correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ - learning_stage::String = "inference" + "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + learningStage::String = "inference" error::Union{Float64,Nothing} = nothing - output_error::Union{Array,Nothing} = Vector{AbstractFloat}() - recent_knowledgeFn_error::Union{Any,Nothing} = nothing + outputError::Union{Array,Nothing} = Vector{AbstractFloat}() softreset::Bool = false - meta_params::Union{Dict{Any,Any},Nothing} = Dict() - firing_neurons_list::Array{Int64} = Vector{Int64}() # store id of firing neurons - snn_firing_state_t0::Union{Vector{Bool},Nothing} = nothing # store firing state of all neurons at t0 - snn_firing_state_t1::Union{Vector{Bool},Nothing} = nothing # store firing state of all neurons at t1 + firedNeurons::Array{Int64} = Vector{Int64}() # store unique id of firing neurons to be used when random neuron connection + firedNeurons_t0::Union{Vector{Bool},Nothing} = nothing # store firing state of all neurons at t0 + firedNeurons_t1::Union{Vector{Bool},Nothing} = nothing # store firing state of all neurons at t1 - avg_neurons_firing_rate::Union{Float64,Nothing} = 0.0 # for displaying average firing rate over all neurons - avg_neurons_v_t1::Union{Float64,Nothing} = 0.0 # for displaying average v_t1 over all neurons + avgNeuronsFiringRate::Union{Float64,Nothing} = 0.0 # for displaying average firing rate over all neurons + avgNeurons_v_t1::Union{Float64,Nothing} = 0.0 # for displaying average v_t1 over all neurons end #------------------------------------------------------------------------------------------------100 @@ -129,8 +127,8 @@ end :type => "lif_neuron", :v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :z_t => false, # neuron firing status at time = t - :gamma_pd => 0.3, # discount factor. The value is from the paper - :refractory_duration => 2.0, # neuron refractory period in tick + :gammaPd => 0.3, # discount factor. The value is from the paper + :refractoryDuration => 2.0, # neuron refractory period in tick :delta => 1.0, :tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence ) @@ -139,8 +137,8 @@ end :type => "alif_neuron", :v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :z_t => false, # neuron firing status at time = t - :gamma_pd => 0.3, # discount factor. The value is from the paper - :refractory_duration => 2.0, # neuron refractory period in millisecond + :gammaPd => 0.3, # discount factor. The value is from the paper + :refractoryDuration => 2.0, # neuron refractory period in millisecond :delta => 1.0, :tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence @@ -159,14 +157,14 @@ end ) I_kfnparams = Dict( - :knowledgefn_name => "I", + :knowledgeFnName => "I", :lif_neuron_number => 200, :alif_neuron_number => 100, # from Allen Institute, ALIF is 40% of LIF :linear_neuron_number => 5, # output neuron, this is also the output length :Bn => "random", # error projection coefficient from kfn output's error to each neurons's error :learning_rate => 0.01, - :neuron_connection_pattern => "100%", # number of each neuron subscribe to other neuron in knowledgeFn.neurons_array - :output_neuron_connection_pattern => "100%", # "60%" of kfn.neurons_array or number + :neuron_connection_pattern => "100%", # number of each neuron subscribe to other neuron in knowledgeFn.neuronsArray + :output_neuron_connection_pattern => "100%", # "60%" of kfn.neuronsArray or number :maximum_input_data_length => 5, # in case of GloVe word encoding, it is 300 :neuron_w_in_generation_pattern => "random", # number or "random" :neuron_w_rec_generation_pattern => "random", @@ -180,86 +178,86 @@ end :meta_params => Dict(:is_first_cycle => true, :launch_time => 0.0,)) - kfn1 = knowledgeFn(kfn_params, lif_neuron_params, alif_neuron_params, linear_neuron_params) + kfn1 = knowledgeFn(kfnParams, lif_neuron_params, alif_neuron_params, linear_neuron_params) """ -function kfn_1(kfn_params::Dict) +function kfn_1(kfnParams::Dict) kfn = kfn_1() - kfn.kfn_params = kfn_params - kfn.knowledgefn_name = kfn.kfn_params[:knowledgefn_name] + kfn.kfnParams = kfnParams + kfn.knowledgeFnName = kfn.kfnParams[:knowledgeFnName] - if kfn.kfn_params[:compute_neuron_number] < kfn.kfn_params[:total_input_port] + if kfn.kfnParams[:compute_neuron_number] < kfn.kfnParams[:total_input_port] throw(error("number of compute neuron must be greater than input neuron")) end # Bn - if kfn.kfn_params[:Bn] == "random" - kfn.Bn = [Random.rand(0:0.001:1) for i in 1:kfn.kfn_params[:compute_neuron_number]] + if kfn.kfnParams[:Bn] == "random" + kfn.Bn = [Random.rand(0:0.001:1) for i in 1:kfn.kfnParams[:compute_neuron_number]] else # in case I want to specify manually - kfn.Bn = [kfn.kfn_params[:Bn] for i in 1:kfn.kfn_params[:compute_neuron_number]] + kfn.Bn = [kfn.kfnParams[:Bn] for i in 1:kfn.kfnParams[:compute_neuron_number]] end # assign neurons ID by their position in kfn.neurons array because I think it is # straight forward way # add input port - for (k, v) in kfn.kfn_params[:input_port] - current_type = kfn.kfn_params[:input_port][k] + for (k, v) in kfn.kfnParams[:input_port] + current_type = kfn.kfnParams[:input_port][k] for i = 1:current_type[:numbers] - n_id = length(kfn.neurons_array) + 1 - neuron = init_neuron(n_id, current_type[:params], kfn.kfn_params) - push!(kfn.neurons_array, neuron) + n_id = length(kfn.neuronsArray) + 1 + neuron = init_neuron(n_id, current_type[:params], kfn.kfnParams) + push!(kfn.neuronsArray, neuron) end end # add compute neurons - for (k, v) in kfn.kfn_params[:compute_neuron] - current_type = kfn.kfn_params[:compute_neuron][k] + for (k, v) in kfn.kfnParams[:compute_neuron] + current_type = kfn.kfnParams[:compute_neuron][k] for i = 1:current_type[:numbers] - n_id = length(kfn.neurons_array) + 1 - neuron = init_neuron(n_id, current_type[:params], kfn.kfn_params) - push!(kfn.neurons_array, neuron) + n_id = length(kfn.neuronsArray) + 1 + neuron = init_neuron(n_id, current_type[:params], kfn.kfnParams) + push!(kfn.neuronsArray, neuron) end end - for i = 1:kfn.kfn_params[:output_port][:numbers] - neuron = init_neuron(i, kfn.kfn_params[:output_port][:params], - kfn.kfn_params) - push!(kfn.output_neurons_array, neuron) + for i = 1:kfn.kfnParams[:output_port][:numbers] + neuron = init_neuron(i, kfn.kfnParams[:output_port][:params], + kfn.kfnParams) + push!(kfn.outputNeuronsArray, neuron) end # random which neuron output port subscribed to, 1-compute_neuron for each output port - sub_list = shuffle!([kfn.kfn_params[:total_input_port]+1:length(kfn.neurons_array)...]) - sub_output_neuron = [pop!(sub_list) for i in 1:kfn.kfn_params[:output_port][:numbers]] - for i in kfn.output_neurons_array - i.subscription_list = [pop!(sub_output_neuron)] + sub_list = shuffle!([kfn.kfnParams[:total_input_port]+1:length(kfn.neuronsArray)...]) + sub_output_neuron = [pop!(sub_list) for i in 1:kfn.kfnParams[:output_port][:numbers]] + for i in kfn.outputNeuronsArray + i.subscriptionList = [pop!(sub_output_neuron)] end - for n in kfn.neurons_array + for n in kfn.neuronsArray if typeof(n) <: compute_neuron - n.firing_rate_target = kfn.kfn_params[:neuron_firing_rate_target] + n.firingRateTarget = kfn.kfnParams[:neuron_firing_rate_target] end end # excitatory neuron to inhabitory neuron = 60:40 % of compute_neuron - ex_number = Int(floor(0.6 * kfn.kfn_params[:compute_neuron_number])) + ex_number = Int(floor(0.6 * kfn.kfnParams[:compute_neuron_number])) ex_n = [1 for i in 1:ex_number] - in_number = kfn.kfn_params[:compute_neuron_number] - ex_number + in_number = kfn.kfnParams[:compute_neuron_number] - ex_number in_n = [-1 for i in 1:in_number] ex_in = shuffle!([ex_n; in_n]) # input neurons are always excitatory, compute_neurons are random between excitatory # and inhabitory - for n in reverse(kfn.neurons_array) - try n.ExIn_type = pop!(ex_in) catch end + for n in reverse(kfn.neuronsArray) + try n.ExInType = pop!(ex_in) catch end end - # add ExIn_type into each compute_neuron sub_ExIn_type - for n in reverse(kfn.neurons_array) - try # input neuron doest have n.subscription_list - for sub_id in n.subscription_list - n_ExIn_type = kfn.neurons_array[sub_id].ExIn_type - push!(n.sub_ExIn_type, n_ExIn_type) + # add ExInType into each compute_neuron subExInType + for n in reverse(kfn.neuronsArray) + try # input neuron doest have n.subscriptionList + for sub_id in n.subscriptionList + n_ExInType = kfn.neuronsArray[sub_id].ExInType + push!(n.subExInType, n_ExInType) end catch end @@ -275,11 +273,11 @@ end Base.@kwdef mutable struct passthrough_neuron <: input_neuron id::Union{Int64,Nothing} = nothing # ID of this neuron which is it position in knowledgeFn array type::String = "passthrough_neuron" - knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to + knowledgeFnName::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to z_t::Bool = false z_t1::Bool = false - time_stamp::Number = 0.0 # current time - ExIn_type::Integer = 1 # 1 excitatory, -1 inhabitory. input neuron is always excitatory + timeStep::Number = 0.0 # current time + ExInType::Integer = 1 # 1 excitatory, -1 inhabitory. input neuron is always excitatory end function passthrough_neuron(params::Dict) @@ -305,17 +303,18 @@ end Base.@kwdef mutable struct lif_neuron <: compute_neuron id::Union{Int64,Nothing} = nothing # this neuron ID i.e. position of this neuron in knowledgeFn type::String = "lif_neuron" - ExIn_type::Integer = 1 # 1 excitatory, -1 inhabitory + ExInType::Integer = 1 # 1 excitatory, -1 inhabitory # Bn::Union{Float64,Nothing} = Random.rand() # Bias for neuron error - knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to - subscription_list::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to - sub_ExIn_type::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons - time_stamp::Number = 0.0 # current time + knowledgeFnName::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to + subscriptionList::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to + subExInType::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons + timeStep::Number = 0.0 # current time w_rec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) v_t::Float64 = 0.0 # vᵗ, postsynaptic neuron membrane potential of previous timestep v_t1::Float64 = 0.0 # vᵗ⁺¹, postsynaptic neuron membrane potential at current timestep v_t_default::Union{Float64,Nothing} = 0.0 # default membrane potential voltage v_th::Float64 = 1.0 # vᵗʰ, neuron firing threshold + vRest::Float64 = 0.0 # resting potential after neuron fired z_t::Bool = false # zᵗ, neuron postsynaptic firing of previous timestep # zᵗ⁺¹, neuron firing status at time = t+1. I need this because the way I calculate all # neurons forward function at each timestep-by-timestep is to do every neuron @@ -325,42 +324,38 @@ Base.@kwdef mutable struct lif_neuron <: compute_neuron z_i_t::Union{Array{Bool},Nothing} = nothing # neuron presynaptic firing at current timestep (which is other neuron postsynaptic firing of previous timestep) # Bn_wout_decay::Union{Float64,Nothing} = 0.01 # use to balance Bn and w_out - gamma_pd::Union{Float64,Nothing} = 0.3 # γ_pd, discount factor, value from paper + gammaPd::Union{Float64,Nothing} = 0.3 # γ_pd, discount factor, value from paper alpha::Union{Float64,Nothing} = nothing # α, neuron membrane potential decay factor phi::Union{Float64,Nothing} = nothing # ϕ, psuedo derivative - epsilon_rec::Union{Array{Float64},Nothing} = nothing # ϵ_rec, eligibility vector for neuron spike - decayed_epsilon_rec::Union{Array{Float64},Nothing} = nothing # α * epsilon_rec - e_rec::Union{Array{Float64},Nothing} = nothing # eligibility trace for neuron spike + epsilonRec::Union{Array{Float64},Nothing} = nothing # ϵ_rec, eligibility vector for neuron spike + decayedEpsilonRec::Union{Array{Float64},Nothing} = nothing # α * epsilonRec + eRec::Union{Array{Float64},Nothing} = nothing # eligibility trace for neuron spike delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond - last_firing_time::Union{Float64,Nothing} = 0.0 # the last time neuron fires - refractory_duration::Union{Float64,Nothing} = 3 # neuron's refratory period in millisecond + lastFiringTime::Union{Float64,Nothing} = 0.0 # the last time neuron fires + refractoryDuration::Union{Float64,Nothing} = 3 # neuron's refratory period in millisecond # refractory_state_active::Union{Bool,Nothing} = false # if true, neuron is in refractory state and cannot process new information - refractory_counter::Integer = 0 + refractoryCounter::Integer = 0 tau_m::Union{Float64,Nothing} = nothing # τ_m, membrane time constant in millisecond eta::Union{Float64,Nothing} = 0.01 # η, learning rate - w_rec_change::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change - recurrent_signal::Union{Float64,Nothing} = nothing # incoming recurrent signal + wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change + recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t - voltage_drop_percentage::Union{Float64,Nothing} = 1.0 # voltage drop as a percentage of v_th + voltageDropPercentage::Union{Float64,Nothing} = 1.0 # voltage drop as a percentage of v_th error::Union{Float64,Nothing} = nothing # local neuron error optimiser::Union{Any,Nothing} = load_optimiser("AdaBelief") # Flux optimizer - firing_counter::Float64 = 0.0 # store how many times neuron fires - firing_rate_target::Float64 = 20.0 # neuron's target firing rate in Hz - firing_diff::Float64 = 0.0 # e-prop supplement paper equation 5 - firing_rate_error::Float64 = 0.0 # local neuron error w.r.t. firing regularization - firing_rate::Float64 = 0.0 # running average of firing rate in Hz - - current_error::Union{Float64,Nothing} = 0.0 - previous_error::Union{Float64,Nothing} = 0.0 - error_diff::Union{Array{Float64},Nothing} = Vector{Float64}() + firingCounter::Float64 = 0.0 # store how many times neuron fires + firingRateTarget::Float64 = 20.0 # neuron's target firing rate in Hz + firingDiff::Float64 = 0.0 # e-prop supplement paper equation 5 + firingRateError::Float64 = 0.0 # local neuron error w.r.t. firing regularization + firingRate::Float64 = 0.0 # running average of firing rate in Hz """ "inference" = no learning params will be collected. "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time - correct answer is available then merge Δw_rec_change into w_rec_change then + correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ - learning_stage::String = "inference" + "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + learningStage::String = "inference" end """ lif neuron outer constructor @@ -371,8 +366,8 @@ end :type => "lif_neuron", :v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :z_t => false, # neuron firing status at time = t - :gamma_pd => 0.3, # discount factor. The value is from the paper - :refractory_duration => 2.0, # neuron refractory period in tick + :gammaPd => 0.3, # discount factor. The value is from the paper + :refractoryDuration => 2.0, # neuron refractory period in tick :delta => 1.0, :tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence ) @@ -402,17 +397,18 @@ end Base.@kwdef mutable struct alif_neuron <: compute_neuron id::Union{Int64,Nothing} = nothing # this neuron ID i.e. position of this neuron in knowledgeFn type::String = "alif_neuron" - ExIn_type::Integer = -1 # 1 excitatory, -1 inhabitory + ExInType::Integer = -1 # 1 excitatory, -1 inhabitory # Bn::Union{Float64,Nothing} = Random.rand() # Bias for neuron error - knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to - subscription_list::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to - sub_ExIn_type::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons - time_stamp::Union{Number,Nothing} = nothing # current time + knowledgeFnName::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to + subscriptionList::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to + subExInType::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons + timeStep::Union{Number,Nothing} = nothing # current time w_rec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) v_t::Float64 = 0.0 # vᵗ, postsynaptic neuron membrane potential of previous timestep v_t1::Float64 = 0.0 # vᵗ⁺¹, postsynaptic neuron membrane potential at current timestep v_t_default::Union{Float64,Nothing} = 0.0 v_th::Float64 = 1.0 # vᵗʰ, neuron firing threshold + vRest::Float64 = 0.0 # resting potential after neuron fired z_t::Bool = false # zᵗ, neuron postsynaptic firing of previous timestep # zᵗ⁺¹, neuron firing status at time = t+1. I need this because the way I calculate all # neurons forward function at each timestep-by-timestep is to do every neuron @@ -424,37 +420,32 @@ Base.@kwdef mutable struct alif_neuron <: compute_neuron alpha::Union{Float64,Nothing} = nothing # α, neuron membrane potential decay factor delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond - epsilon_rec::Union{Array{Float64},Nothing} = nothing # ϵ_rec(v), eligibility vector for neuron i spike - epsilon_rec_a::Union{Array{Float64},Nothing} = nothing # ϵ_rec(a) - decayed_epsilon_rec::Union{Array{Float64},Nothing} = nothing # α * epsilon_rec - e_rec_v::Union{Array{Float64},Nothing} = nothing # a component of neuron's eligibility trace resulted from v_t - e_rec_a::Union{Array{Float64},Nothing} = nothing # a component of neuron's eligibility trace resulted from av_th - e_rec::Union{Array{Float64},Nothing} = nothing # neuron's eligibility trace + epsilonRec::Union{Array{Float64},Nothing} = nothing # ϵ_rec(v), eligibility vector for neuron i spike + epsilonRecA::Union{Array{Float64},Nothing} = nothing # ϵ_rec(a) + decayedEpsilonRec::Union{Array{Float64},Nothing} = nothing # α * epsilonRec + eRec_v::Union{Array{Float64},Nothing} = nothing # a component of neuron's eligibility trace resulted from v_t + eRec_a::Union{Array{Float64},Nothing} = nothing # a component of neuron's eligibility trace resulted from av_th + eRec::Union{Array{Float64},Nothing} = nothing # neuron's eligibility trace eta::Union{Float64,Nothing} = 0.01 # eta, learning rate - gamma_pd::Union{Float64,Nothing} = 0.3 # γ_pd, discount factor, value from paper - last_firing_time::Union{Float64,Nothing} = 0.0 # the last time neuron fires + gammaPd::Union{Float64,Nothing} = 0.3 # γ_pd, discount factor, value from paper + lastFiringTime::Union{Float64,Nothing} = 0.0 # the last time neuron fires phi::Union{Float64,Nothing} = nothing # ϕ, psuedo derivative - refractory_duration::Union{Float64,Nothing} = 3 # neuron's refractory period in millisecond + refractoryDuration::Union{Float64,Nothing} = 3 # neuron's refractory period in millisecond # refractory_state_active::Union{Bool,Nothing} = false # if true, neuron is in refractory state and cannot process new information - refractory_counter::Integer = 0 + refractoryCounter::Integer = 0 tau_m::Union{Float64,Nothing} = nothing # τ_m, membrane time constant in millisecond - w_rec_change::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change - recurrent_signal::Union{Float64,Nothing} = nothing # incoming recurrent signal + wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change + recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t - voltage_drop_percentage::Union{Float64,Nothing} = 1.0 # voltage drop as a percentage of v_th + voltageDropPercentage::Union{Float64,Nothing} = 1.0 # voltage drop as a percentage of v_th error::Union{Float64,Nothing} = nothing # local neuron error optimiser::Union{Any,Nothing} = load_optimiser("AdaBelief") # Flux optimizer - firing_counter::Float64 = 0.0 # store how many times neuron fires - firing_rate_target::Float64 = 20.0 # neuron's target firing rate in Hz - firing_diff::Float64 = 0.0 # e-prop supplement paper equation 5 - firing_rate_error::Float64 = 0.0 # local neuron error w.r.t. firing regularization - firing_rate::Float64 = 0.0 # running average of firing rate, Hz - - - current_error::Union{Float64,Nothing} = 0.0 - previous_error::Union{Float64,Nothing} = 0.0 - error_diff::Union{Array{Float64},Nothing} = Vector{Float64}() + firingCounter::Float64 = 0.0 # store how many times neuron fires + firingRateTarget::Float64 = 20.0 # neuron's target firing rate in Hz + firingDiff::Float64 = 0.0 # e-prop supplement paper equation 5 + firingRateError::Float64 = 0.0 # local neuron error w.r.t. firing regularization + firingRate::Float64 = 0.0 # running average of firing rate, Hz tau_a::Union{Float64,Nothing} = nothing # τ_a, adaption time constant in millisecond beta::Union{Float64,Nothing} = 0.15 # β, constant, value from paper @@ -464,10 +455,10 @@ Base.@kwdef mutable struct alif_neuron <: compute_neuron """ "inference" = no learning params will be collected. "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time - correct answer is available then merge Δw_rec_change into w_rec_change then + correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ - learning_stage::String = "inference" + "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + learningStage::String = "inference" end """ alif neuron outer constructor @@ -479,8 +470,8 @@ end :v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :z_t => false, # neuron firing status at time = t - :gamma_pd => 0.3, # discount factor. The value is from the paper - :refractory_duration => 2.0, # neuron refractory period in millisecond + :gammaPd => 0.3, # discount factor. The value is from the paper + :refractoryDuration => 2.0, # neuron refractory period in millisecond :delta => 1.0, :tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence @@ -516,9 +507,9 @@ end Base.@kwdef mutable struct linear_neuron <: output_neuron id::Union{Int64,Nothing} = nothing # ID of this neuron which is it position in knowledgeFn array type::String = "linear_neuron" - knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to - subscription_list::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to - time_stamp::Union{Number,Nothing} = nothing # current time + knowledgeFnName::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to + subscriptionList::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to + timeStep::Union{Number,Nothing} = nothing # current time delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond out_t::Bool = false # output of linear neuron BEFORE forward() out_t1::Bool = false # output of linear neuron AFTER forward() @@ -572,91 +563,91 @@ function load_optimiser(optimiser_name::String; params::Union{Dict,Nothing} = no end end -function init_neuron!(id::Int64, n::passthrough_neuron, n_params::Dict, kfn_params::Dict) +function init_neuron!(id::Int64, n::passthrough_neuron, n_params::Dict, kfnParams::Dict) n.id = id - n.knowledgefn_name = kfn_params[:knowledgefn_name] + n.knowledgeFnName = kfnParams[:knowledgeFnName] end -# function init_neuron!(id::Int64, n::lif_neuron, kfn_params::Dict) +# function init_neuron!(id::Int64, n::lif_neuron, kfnParams::Dict) # n.id = id -# n.knowledgefn_name = kfn_params[:knowledgefn_name] -# subscription_options = shuffle!([1:(kfn_params[:input_neuron_number]+kfn_params[:compute_neuron_number])...]) -# if typeof(kfn_params[:synaptic_connection_number]) == String -# percent = parse(Int, kfn_params[:synaptic_connection_number][1:end-1]) / 100 +# n.knowledgeFnName = kfnParams[:knowledgeFnName] +# subscription_options = shuffle!([1:(kfnParams[:input_neuron_number]+kfnParams[:compute_neuron_number])...]) +# if typeof(kfnParams[:synaptic_connection_number]) == String +# percent = parse(Int, kfnParams[:synaptic_connection_number][1:end-1]) / 100 # synaptic_connection_number = floor(length(subscription_options) * percent) -# n.subscription_list = [pop!(subscription_options) for i = 1:synaptic_connection_number] +# n.subscriptionList = [pop!(subscription_options) for i = 1:synaptic_connection_number] # end -# filter!(x -> x != n.id, n.subscription_list) -# n.epsilon_rec = zeros(length(n.subscription_list)) -# n.w_rec = Random.rand(length(n.subscription_list)) -# n.w_rec_change = zeros(length(n.subscription_list)) -# n.reg_voltage_b = zeros(length(n.subscription_list)) +# filter!(x -> x != n.id, n.subscriptionList) +# n.epsilonRec = zeros(length(n.subscriptionList)) +# n.w_rec = Random.rand(length(n.subscriptionList)) +# n.wRecChange = zeros(length(n.subscriptionList)) +# n.reg_voltage_b = zeros(length(n.subscriptionList)) # n.alpha = calculate_α(n) # end -function init_neuron!(id::Int64, n::lif_neuron, n_params::Dict, kfn_params::Dict) +function init_neuron!(id::Int64, n::lif_neuron, n_params::Dict, kfnParams::Dict) n.id = id - n.knowledgefn_name = kfn_params[:knowledgefn_name] - subscription_options = shuffle!([1:kfn_params[:total_neurons]...]) + n.knowledgeFnName = kfnParams[:knowledgeFnName] + subscription_options = shuffle!([1:kfnParams[:total_neurons]...]) subscription_numbers = Int(floor(n_params[:synaptic_connection_number] * - kfn_params[:total_neurons] / 100.0)) - n.subscription_list = [pop!(subscription_options) for i = 1:subscription_numbers] + kfnParams[:total_neurons] / 100.0)) + n.subscriptionList = [pop!(subscription_options) for i = 1:subscription_numbers] # prevent subscription to itself by removing this neuron id - filter!(x -> x != n.id, n.subscription_list) + filter!(x -> x != n.id, n.subscriptionList) - n.epsilon_rec = zeros(length(n.subscription_list)) - n.w_rec = Random.rand(length(n.subscription_list)) - n.w_rec_change = zeros(length(n.subscription_list)) - # n.reg_voltage_b = zeros(length(n.subscription_list)) + n.epsilonRec = zeros(length(n.subscriptionList)) + n.w_rec = Random.rand(length(n.subscriptionList)) + n.wRecChange = zeros(length(n.subscriptionList)) + # n.reg_voltage_b = zeros(length(n.subscriptionList)) n.alpha = calculate_α(n) end function init_neuron!(id::Int64, n::alif_neuron, n_params::Dict, - kfn_params::Dict) + kfnParams::Dict) n.id = id - n.knowledgefn_name = kfn_params[:knowledgefn_name] - subscription_options = shuffle!([1:kfn_params[:total_neurons]...]) + n.knowledgeFnName = kfnParams[:knowledgeFnName] + subscription_options = shuffle!([1:kfnParams[:total_neurons]...]) subscription_numbers = Int(floor(n_params[:synaptic_connection_number] * - kfn_params[:total_neurons] / 100.0)) - n.subscription_list = [pop!(subscription_options) for i = 1:subscription_numbers] + kfnParams[:total_neurons] / 100.0)) + n.subscriptionList = [pop!(subscription_options) for i = 1:subscription_numbers] # prevent subscription to itself by removing this neuron id - filter!(x -> x != n.id, n.subscription_list) + filter!(x -> x != n.id, n.subscriptionList) - n.epsilon_rec = zeros(length(n.subscription_list)) - n.w_rec = Random.rand(length(n.subscription_list)) - n.w_rec_change = zeros(length(n.subscription_list)) - # n.reg_voltage_b = zeros(length(n.subscription_list)) + n.epsilonRec = zeros(length(n.subscriptionList)) + n.w_rec = Random.rand(length(n.subscriptionList)) + n.wRecChange = zeros(length(n.subscriptionList)) + # n.reg_voltage_b = zeros(length(n.subscriptionList)) n.alpha = calculate_α(n) # the more time has passed from the last time neuron was # activated, the more neuron membrane potential is reduced n.rho = calculate_ρ(n) - n.epsilon_rec_a = zeros(length(n.subscription_list)) + n.epsilonRecA = zeros(length(n.subscriptionList)) end -# function init_neuron!(id::Int64, n::linear_neuron, kfn_params::Dict) +# function init_neuron!(id::Int64, n::linear_neuron, kfnParams::Dict) # n.id = id -# n.knowledgefn_name = kfn_params[:knowledgefn_name] -# start_id = kfn_params[:input_neuron_number] + 1 # don't readout from input neurons -# n.subscription_list = [start_id:(start_id+kfn_params[:compute_neuron_number]-1)...] -# n.epsilon_j = zeros(length(n.subscription_list)) -# n.w_out = Random.randn(length(n.subscription_list)) -# n.w_out_change = zeros(length(n.subscription_list)) +# n.knowledgeFnName = kfnParams[:knowledgeFnName] +# start_id = kfnParams[:input_neuron_number] + 1 # don't readout from input neurons +# n.subscriptionList = [start_id:(start_id+kfnParams[:compute_neuron_number]-1)...] +# n.epsilon_j = zeros(length(n.subscriptionList)) +# n.w_out = Random.randn(length(n.subscriptionList)) +# n.w_out_change = zeros(length(n.subscriptionList)) # n.b = Random.randn() # n.b_change = 0.0 # n.k = calculate_k(n) # end #WORKING -function init_neuron!(id::Int64, n::linear_neuron, n_params::Dict, kfn_params::Dict) +function init_neuron!(id::Int64, n::linear_neuron, n_params::Dict, kfnParams::Dict) n.id = id - n.knowledgefn_name = kfn_params[:knowledgefn_name] - # start_id = kfn_params[:total_input_port] + 1 # don't readout from input neurons - # subscription_options = [start_id:(start_id+kfn_params[:total_compute_neuron]-1)...] - # n.subscription_list = [rand(subscription_options)] + n.knowledgeFnName = kfnParams[:knowledgeFnName] + # start_id = kfnParams[:total_input_port] + 1 # don't readout from input neurons + # subscription_options = [start_id:(start_id+kfnParams[:total_compute_neuron]-1)...] + # n.subscriptionList = [rand(subscription_options)] - # n.epsilon_j = zeros(length(n.subscription_list)) - # n.w_out = Random.randn(length(n.subscription_list)) - # n.w_out_change = zeros(length(n.subscription_list)) + # n.epsilon_j = zeros(length(n.subscriptionList)) + # n.w_out = Random.randn(length(n.subscriptionList)) + # n.w_out_change = zeros(length(n.subscriptionList)) # n.b = Random.randn() # n.b_change = 0.0 # n.k = calculate_k(n) @@ -664,9 +655,9 @@ end """ Make a neuron intended for use with knowledgeFn """ -function init_neuron(id::Int64, n_params::Dict, kfn_params::Dict) +function init_neuron(id::Int64, n_params::Dict, kfnParams::Dict) n = instantiate_custom_types(n_params) - init_neuron!(id, n, n_params, kfn_params) + init_neuron!(id, n, n_params, kfnParams) return n end @@ -700,22 +691,22 @@ end """ Add a new neuron into a knowledgeFn # Example - add_neuron!(kfn.kfn_params[:lif_neuron_params], kfn) + add_neuron!(kfn.kfnParams[:lif_neuron_params], kfn) """ # function add_neuron!(neuron_Dict::Dict, kfn::knowledgeFn) -# id = length(kfn.neurons_array) + 1 -# neuron = init_neuron(id, neuron_Dict, kfn.kfn_params, -# total_neurons = (length(kfn.neurons_array) + 1)) -# push!(kfn.neurons_array, neuron) +# id = length(kfn.neuronsArray) + 1 +# neuron = init_neuron(id, neuron_Dict, kfn.kfnParams, +# total_neurons = (length(kfn.neuronsArray) + 1)) +# push!(kfn.neuronsArray, neuron) # # Randomly select an output neuron to add a new neuron to -# add_n_output_n!(Random.rand(kfn.output_neurons_array), id) +# add_n_output_n!(Random.rand(kfn.outputNeuronsArray), id) # end -""" Add a new neuron to output neuron's subscription_list +""" Add a new neuron to output neuron's subscriptionList """ function add_n_output_n!(o_n::linear_neuron, id::Int64) - push!(o_n.subscription_list, id) + push!(o_n.subscriptionList, id) push!(o_n.epsilon_j, 0.0) push!(o_n.w_out, Random.randn(1)[1]) push!(o_n.w_out_change, 0.0)