refractoring

This commit is contained in:
2023-05-12 19:50:02 +07:00
parent 668fa77595
commit 89371736e4
5 changed files with 382 additions and 400 deletions

View File

@@ -34,9 +34,11 @@ using .interface
""" """
Todo: Todo:
[*3] no "start learning" use reset learning and "inference", "learning" mode instead [*3] implement "start learning", reset learning and "during_learning", "end_learning and
[4] output neuron connect to multiple compute neuron "inference"
[7] add time-based learning method. Also implement "thinking period" [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 [8] verify that model can complete learning cycle with no error
[5] synaptic connection strength concept [5] synaptic connection strength concept
[6] neuroplasticity() i.e. change connection [6] neuroplasticity() i.e. change connection

View File

@@ -12,7 +12,7 @@ using ..types, ..snn_utils
""" """
function (m::model)(input_data::AbstractVector) function (m::model)(input_data::AbstractVector)
# m.global_tick += 1 # m.global_tick += 1
m.time_stamp += 1 m.timeStep += 1
# process all corresponding KFN # process all corresponding KFN
raw_model_respond = m.knowledgeFn[:I](m, input_data) raw_model_respond = m.knowledgeFn[:I](m, input_data)
@@ -28,9 +28,9 @@ end
""" """
function (kfn::kfn_1)(m::model, input_data::AbstractVector) function (kfn::kfn_1)(m::model, input_data::AbstractVector)
kfn.time_stamp = m.time_stamp kfn.timeStep = m.timeStep
kfn.softreset = m.softreset kfn.softreset = m.softreset
kfn.learning_stage = m.learning_stage kfn.learningStage = m.learningStage
kfn.error = m.error kfn.error = m.error
# generate noise # 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 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) timestep_forward!(n)
end end
for n in kfn.output_neurons_array for n in kfn.outputNeuronsArray
timestep_forward!(n) timestep_forward!(n)
end 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. # pass input_data into input neuron.
# number of data point equals to number of input neuron starting from id 1 # number of data point equals to number of input neuron starting from id 1
for (i, data) in enumerate(input_data) for (i, data) in enumerate(input_data)
kfn.neurons_array[i].z_t1 = data kfn.neuronsArray[i].z_t1 = data
end 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 #CHANGE Threads.@threads for n in kfn.neuronsArray
for n in kfn.neurons_array for n in kfn.neuronsArray
n(kfn) n(kfn)
end end
kfn.snn_firing_state_t1 = [n.z_t1 for n in kfn.neurons_array] kfn.firedNeurons_t1 = [n.z_t1 for n in kfn.neuronsArray]
append!(kfn.firing_neurons_list, findall(kfn.snn_firing_state_t1)) # store id of neuron that fires append!(kfn.firedNeurons, findall(kfn.firedNeurons_t1)) # store id of neuron that fires
if kfn.learning_stage == "end_learning" # use for random new neuron connection if kfn.learningStage == "end_learning"
kfn.firing_neurons_list |> unique! kfn.firedNeurons |> unique! # use for random new neuron connection
end end
# Threads.@threads for n in kfn.output_neurons_array # Threads.@threads for n in kfn.outputNeuronsArray
for n in kfn.output_neurons_array for n in kfn.outputNeuronsArray
n(kfn) n(kfn)
end end
out = [n.out_t1 for n in kfn.output_neurons_array] out = [n.out_t1 for n in kfn.outputNeuronsArray]
return out return out
end end
@@ -96,7 +81,7 @@ end
""" passthrough_neuron forward() """ passthrough_neuron forward()
""" """
function (n::passthrough_neuron)(kfn::knowledgeFn) function (n::passthrough_neuron)(kfn::knowledgeFn)
n.time_stamp = kfn.time_stamp n.timeStep = kfn.timeStep
# n.global_tick = kfn.global_tick # n.global_tick = kfn.global_tick
end end
@@ -105,40 +90,40 @@ end
""" lif_neuron forward() """ lif_neuron forward()
""" """
function (n::lif_neuron)(kfn::knowledgeFn) 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 # 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 = getindex(kfn.firedNeurons_t0, n.subscriptionList)
n.z_i_t .*= n.sub_ExIn_type n.z_i_t .*= n.subExInType
if n.refractory_counter != 0 if n.refractoryCounter != 0
n.refractory_counter -= 1 n.refractoryCounter -= 1
# neuron is in refractory state, skip all calculation # neuron is in refractory state, skip all calculation
n.z_t1 = false # used by timestep_forward() in kfn. Set to zero because neuron spike 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. # 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 # 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 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.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) n.v_t1 = no_negative!.(n.v_t1)
if n.v_t1 > n.v_th if n.v_t1 > n.v_th
n.z_t1 = true n.z_t1 = true
n.refractory_counter = n.refractory_duration n.refractoryCounter = n.refractoryDuration
n.firing_counter += 1 n.firingCounter += 1
n.v_t1 = n.v_t1 - n.v_th n.v_t1 = n.vRest
else else
n.z_t1 = false n.z_t1 = false
end end
# there is a difference from alif formula # 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
end end
@@ -147,41 +132,41 @@ end
""" alif_neuron forward() """ alif_neuron forward()
""" """
function (n::alif_neuron)(kfn::knowledgeFn) 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 = getindex(kfn.firedNeurons_t0, n.subscriptionList)
n.z_i_t .*= n.sub_ExIn_type n.z_i_t .*= n.subExInType
if n.refractory_counter != 0 if n.refractoryCounter != 0
n.refractory_counter -= 1 n.refractoryCounter -= 1
# neuron is in refractory state, skip all calculation # 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.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.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 # 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 n.phi = 0
else else
n.z_t = isnothing(n.z_t) ? false : n.z_t n.z_t = isnothing(n.z_t) ? false : n.z_t
n.a = (n.rho * n.a) + ((1 - n.rho) * 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.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.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) n.v_t1 = no_negative!.(n.v_t1)
if n.v_t1 > n.av_th if n.v_t1 > n.av_th
n.z_t1 = true n.z_t1 = true
n.refractory_counter = n.refractory_duration n.refractoryCounter = n.refractoryDuration
n.firing_counter += 1 n.firingCounter += 1
n.v_t1 = n.v_t1 - n.v_th n.v_t1 = n.vRest
else else
n.z_t1 = false n.z_t1 = false
end end
# there is a difference from lif formula # 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
end end
@@ -191,8 +176,8 @@ end
In this implementation, each output neuron is fully connected to every lif and alif neuron. In this implementation, each output neuron is fully connected to every lif and alif neuron.
""" """
function (n::linear_neuron)(kfn::T) where T<:knowledgeFn function (n::linear_neuron)(kfn::T) where T<:knowledgeFn
n.time_stamp = kfn.time_stamp n.timeStep = kfn.timeStep
n.out_t1 = getindex(kfn.snn_firing_state_t1, n.subscription_list)[1] n.out_t1 = getindex(kfn.firedNeurons_t1, n.subscriptionList)[1]
end end

View File

@@ -10,56 +10,47 @@ export learn!
#------------------------------------------------------------------------------------------------100 #------------------------------------------------------------------------------------------------100
function learn!(m::model, model_respond, correct_answer) function learn!(m::model, modelRespond, correctAnswer=nothing, correctTiming=nothing)
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)
# 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 else
model_error = nothing
end 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 return model_error
end end
# function learn!(m::model, raw_model_respond, correct_answer=nothing) # 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) # model_error = Flux.logitcrossentropy(raw_model_respond, correct_answer)
# output_elements_error = raw_model_respond - correct_answer # output_elements_error = raw_model_respond - correct_answer
@@ -77,18 +68,33 @@ end
""" knowledgeFn learn() """ knowledgeFn learn()
""" """
function learn!(kfn::knowledgeFn, error::Union{Float64,Nothing}=nothing, function learn!(kfn::knowledgeFn, error::Union{Float64,Nothing}=nothing,
output_error::Union{Vector,Nothing}=nothing) outputError::Union{Vector,Nothing}=nothing)
kfn.error = error kfn.error = error
kfn.output_error = output_error kfn.outputError = outputError
# Threads.@threads for n in kfn.neurons_array kfn.learningStage = m.learningStage
for n in kfn.neurons_array 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 learn!(n, kfn) # Neurons are always learning, besides error from model output
end end
if kfn.output_error !== nothing if kfn.outputError !== nothing
# Threads.@threads for n in kfn.output_neurons_array # Threads.@threads for n in kfn.outputNeuronsArray
for n in kfn.output_neurons_array # not use multithreading because 1st output neuron for n in kfn.outputNeuronsArray # not use multithreading because 1st output neuron
# will set learning rate that will be used by # will set learning rate that will be used by
# other output neurons # other output neurons
learn!(n, kfn) learn!(n, kfn)
@@ -96,21 +102,21 @@ function learn!(kfn::knowledgeFn, error::Union{Float64,Nothing}=nothing,
#TODO: put other KFN to learn here #TODO: put other KFN to learn here
# for main loop user's display and training's exit condition # for main loop user's display and training's exit condition
avg_neurons_firing_rate = 0.0 avgNeuronsFiringRate = 0.0
for n in kfn.neurons_array for n in kfn.neuronsArray
if typeof(n) <: compute_neuron if typeof(n) <: compute_neuron
avg_neurons_firing_rate += n.firing_rate avgNeuronsFiringRate += n.firingRate
end end
end end
kfn.avg_neurons_firing_rate = avg_neurons_firing_rate / kfn.avgNeuronsFiringRate = avgNeuronsFiringRate /
kfn.kfn_params[:compute_neuron_number] kfn.kfnParams[:compute_neuron_number]
avg_neurons_v_t1 = 0.0 avgNeurons_v_t1 = 0.0
for n in kfn.neurons_array for n in kfn.neuronsArray
if typeof(n) <: compute_neuron if typeof(n) <: compute_neuron
avg_neurons_v_t1 += n.v_t1 avgNeurons_v_t1 += n.v_t1
end end
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
end end
@@ -125,139 +131,139 @@ end
function learn!(n::lif_neuron, kfn::knowledgeFn) function learn!(n::lif_neuron, kfn::knowledgeFn)
if n.learnable_flag == true if n.learnable_flag == true
n.decayed_epsilon_rec = n.alpha * n.epsilon_rec n.decayedEpsilonRec = n.alpha * n.epsilonRec
n.epsilon_rec = n.decayed_epsilon_rec + n.z_i_t n.epsilonRec = n.decayedEpsilonRec + n.z_i_t
n.e_rec = n.phi * n.epsilon_rec n.eRec = n.phi * n.epsilonRec
end end
# a piece of knowledgeFn error that belongs to this neuron # a piece of knowledgeFn error that belongs to this neuron
n.error = isnothing(kfn.error) ? nothing : kfn.error * n.Bn n.error = isnothing(kfn.error) ? nothing : kfn.error * n.Bn
n.learning_stage = kfn.learning_stage n.learningStage = kfn.learningStage
# accumulate voltage regularization terms # accumulate voltage regularization terms
Snn_utils.cal_v_reg!(n) Snn_utils.cal_v_reg!(n)
if n.learning_stage == "doing_inference" if n.learningStage == "doing_inference"
# no learning # no learning
elseif n.learning_stage == "start_learning" || elseif n.learningStage == "start_learning" ||
n.learning_stage == "start_learning_no_wchange_reset" n.learningStage == "start_learning_no_wchange_reset"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing if n.error !== nothing
Snn_utils.firing_rate!(n) Snn_utils.firing_rate!(n)
Snn_utils.firing_diff!(n) Snn_utils.firing_diff!(n)
n.w_rec_change = n.w_rec_change + n.wRecChange = n.wRecChange +
-apply!(n.optimiser, n.w_rec, -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.firing_rate_regulator!(n) +
-Snn_utils.voltage_regulator!(n) -Snn_utils.voltage_regulator!(n)
end end
elseif n.learning_stage == "during_learning" elseif n.learningStage == "during_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing if n.error !== nothing
Snn_utils.firing_rate!(n) Snn_utils.firing_rate!(n)
Snn_utils.firing_diff!(n) Snn_utils.firing_diff!(n)
n.w_rec_change = n.w_rec_change + n.wRecChange = n.wRecChange +
-apply!(n.optimiser, n.w_rec, -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.firing_rate_regulator!(n) +
-Snn_utils.voltage_regulator!(n) -Snn_utils.voltage_regulator!(n)
end end
elseif n.learning_stage == "end_learning" elseif n.learningStage == "end_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing if n.error !== nothing
Snn_utils.firing_rate!(n) Snn_utils.firing_rate!(n)
Snn_utils.firing_diff!(n) Snn_utils.firing_diff!(n)
n.w_rec_change = n.w_rec_change + n.wRecChange = n.wRecChange +
-apply!(n.optimiser, n.w_rec, -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.firing_rate_regulator!(n) +
-Snn_utils.voltage_regulator!(n) -Snn_utils.voltage_regulator!(n)
end end
not_zero = (!iszero).(n.w_rec) not_zero = (!iszero).(n.w_rec)
# set 0 in w_rec_change update according to 0 in w_rec for hard constrain connection # set 0 in wRecChange update according to 0 in w_rec for hard constrain connection
n.w_rec = n.w_rec + (not_zero .* n.w_rec_change) n.w_rec = n.w_rec + (not_zero .* n.wRecChange)
replace!(x -> x < 0 ? 0 : x, n.w_rec) # no negative weight 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
end end
""" alif_neuron learn() """ alif_neuron learn()
""" """
function learn!(n::alif_neuron, kfn::knowledgeFn) function learn!(n::alif_neuron, kfn::knowledgeFn)
n.decayed_epsilon_rec = n.alpha * n.epsilon_rec n.decayedEpsilonRec = n.alpha * n.epsilonRec
n.epsilon_rec = n.decayed_epsilon_rec + n.z_i_t n.epsilonRec = n.decayedEpsilonRec + n.z_i_t
n.epsilon_rec_a = (n.phi * n.epsilon_rec) + n.epsilonRecA = (n.phi * n.epsilonRec) +
((n.rho - (n.phi * n.beta)) * n.epsilon_rec_a) ((n.rho - (n.phi * n.beta)) * n.epsilonRecA)
n.e_rec_v = n.phi * n.epsilon_rec n.eRec_v = n.phi * n.epsilonRec
n.e_rec_a = -n.phi * n.beta * n.epsilon_rec_a n.eRec_a = -n.phi * n.beta * n.epsilonRecA
n.e_rec = n.e_rec_v + n.e_rec_a n.eRec = n.eRec_v + n.eRec_a
# a piece of knowledgeFn error that belongs to this neuron # a piece of knowledgeFn error that belongs to this neuron
n.error = isnothing(kfn.error) ? nothing : kfn.error * n.Bn 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 # no learning
elseif n.learning_stage == "start_learning" || elseif n.learningStage == "start_learning" ||
n.learning_stage == "start_learning_no_wchange_reset" n.learningStage == "start_learning_no_wchange_reset"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing if n.error !== nothing
Snn_utils.firing_rate!(n) Snn_utils.firing_rate!(n)
Snn_utils.firing_diff!(n) Snn_utils.firing_diff!(n)
n.w_rec_change = n.w_rec_change + n.wRecChange = n.wRecChange +
-apply!(n.optimiser, n.w_rec, -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.firing_rate_regulator!(n) +
-Snn_utils.voltage_regulator!(n) -Snn_utils.voltage_regulator!(n)
end end
elseif n.learning_stage == "during_learning" elseif n.learningStage == "during_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing if n.error !== nothing
Snn_utils.firing_rate!(n) Snn_utils.firing_rate!(n)
Snn_utils.firing_diff!(n) Snn_utils.firing_diff!(n)
n.w_rec_change = n.w_rec_change + n.wRecChange = n.wRecChange +
-apply!(n.optimiser, n.w_rec, -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.firing_rate_regulator!(n) +
-Snn_utils.voltage_regulator!(n) -Snn_utils.voltage_regulator!(n)
end end
elseif n.learning_stage == "end_learning" elseif n.learningStage == "end_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing if n.error !== nothing
Snn_utils.firing_rate!(n) Snn_utils.firing_rate!(n)
Snn_utils.firing_diff!(n) Snn_utils.firing_diff!(n)
n.w_rec_change = n.w_rec_change + n.wRecChange = n.wRecChange +
-apply!(n.optimiser, n.w_rec, -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.firing_rate_regulator!(n) +
-Snn_utils.voltage_regulator!(n) -Snn_utils.voltage_regulator!(n)
end end
not_zero = (!iszero).(n.w_rec) not_zero = (!iszero).(n.w_rec)
# set 0 in w_rec_change update according to 0 in w_rec for hard constrain connection # set 0 in wRecChange update according to 0 in w_rec for hard constrain connection
n.w_rec = n.w_rec + (not_zero .* n.w_rec_change) n.w_rec = n.w_rec + (not_zero .* n.wRecChange)
replace!(x -> x < 0 ? 0 : x, n.w_rec) # no negative weight 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
end end
""" linear_neuron learn() """ linear_neuron learn()
""" """
function learn!(n::linear_neuron, kfn::knowledgeFn) function learn!(n::linear_neuron, kfn::knowledgeFn)
n.error = kfn.output_error[n.id] n.error = kfn.outputError[n.id]
n.learning_stage = kfn.learning_stage n.learningStage = kfn.learningStage
if n.learning_stage == "doing_inference" if n.learningStage == "doing_inference"
# no learning # no learning
elseif n.learning_stage == "start_learning" elseif n.learningStage == "start_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training
Δw = -apply!(n.optimiser, n.w_out, (n.error * n.epsilon_j)) Δ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 Δb = -n.eta * n.error
n.b_change = n.b_change + Δb n.b_change = n.b_change + Δb
elseif n.error !== nothing && n.id !== 1 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 Δw = -n.eta * n.error * n.epsilon_j
n.w_out_change = n.w_out_change + Δw n.w_out_change = n.w_out_change + Δw
Δb = -n.eta * n.error Δb = -n.eta * n.error
n.b_change = n.b_change + Δb n.b_change = n.b_change + Δb
end end
elseif n.learning_stage == "during_learning" elseif n.learningStage == "during_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training
Δw = -apply!(n.optimiser, n.w_out, (n.error * n.epsilon_j)) Δ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 Δb = -n.eta * n.error
n.b_change = n.b_change + Δb n.b_change = n.b_change + Δb
elseif n.error !== nothing && n.id !== 1 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 Δw = -n.eta * n.error * n.epsilon_j
n.w_out_change = n.w_out_change + Δw n.w_out_change = n.w_out_change + Δw
Δb = -n.eta * n.error Δb = -n.eta * n.error
n.b_change = n.b_change + Δb n.b_change = n.b_change + Δb
end end
elseif n.learning_stage == "end_learning" elseif n.learningStage == "end_learning"
# if error signal available then accumulates Δw # if error signal available then accumulates Δw
if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training if n.error !== nothing && n.id == 1 # NOT working w/ multithreading training
Δw = -apply!(n.optimiser, n.w_out, (n.error * n.epsilon_j)) Δ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 Δb = -n.eta * n.error
n.b_change = n.b_change + Δb n.b_change = n.b_change + Δb
elseif n.error !== nothing && n.id !== 1 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 Δw = -n.eta * n.error * n.epsilon_j
n.w_out_change = n.w_out_change + Δw n.w_out_change = n.w_out_change + Δw
Δb = -n.eta * n.error Δb = -n.eta * n.error

View File

@@ -32,24 +32,22 @@ no_negative!(x) = x < 0.0 ? 0.0 : x
precision(x::Array{<:Array}) = ( std(mean.(x)) / mean(mean.(x)) ) * 100 precision(x::Array{<:Array}) = ( std(mean.(x)) / mean(mean.(x)) ) * 100
# reset functions for LIF/ALIF neuron # 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_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_v_t!(n::compute_neuron) = n.v_t = n.v_t_default
reset_z_t!(n::compute_neuron) = n.z_t = false 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!(n::compute_neuron) = n.epsilonRec = n.epsilonRec * 0.0
reset_epsilon_rec_a!(n::alif_neuron) = n.epsilon_rec_a = n.epsilon_rec_a * 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_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_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_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_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_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_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_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_counter!(n::compute_neuron) = n.firingCounter = n.firingCounter * 0.0
reset_firing_diff!(n::Union{compute_neuron, linear_neuron}) = n.firing_diff = n.firing_diff * 0.0 reset_firing_diff!(n::Union{compute_neuron, linear_neuron}) = n.firingDiff = n.firingDiff * 0.0
reset_previous_error!(n::Union{compute_neuron}) =
n.previous_error = n.previous_error * 0.0
# reset function for output neuron # reset function for output neuron
reset_epsilon_j!(n::linear_neuron) = n.epsilon_j = n.epsilon_j * 0.0 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) function store_knowledgefn_error!(kfn::knowledgeFn)
# condition to adjust nueron in KFN plane in addition to weight adjustment inside each neuron # 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 if kfn.recent_knowledgeFn_error === nothing && kfn.knowledgeFn_error === nothing
kfn.recent_knowledgeFn_error = [[]] kfn.recent_knowledgeFn_error = [[]]
elseif kfn.recent_knowledgeFn_error === nothing elseif kfn.recent_knowledgeFn_error === nothing
@@ -161,13 +159,13 @@ function store_knowledgefn_error!(kfn::knowledgeFn)
else else
push!(kfn.recent_knowledgeFn_error, [kfn.knowledgeFn_error]) push!(kfn.recent_knowledgeFn_error, [kfn.knowledgeFn_error])
end end
elseif kfn.learning_stage == "during_learning" elseif kfn.learningStage == "during_learning"
if kfn.knowledgeFn_error === nothing if kfn.knowledgeFn_error === nothing
#skip #skip
else else
push!(kfn.recent_knowledgeFn_error[end], kfn.knowledgeFn_error) push!(kfn.recent_knowledgeFn_error[end], kfn.knowledgeFn_error)
end end
elseif kfn.learning_stage == "end_learning" elseif kfn.learningStage == "end_learning"
if kfn.recent_knowledgeFn_error === nothing if kfn.recent_knowledgeFn_error === nothing
#skip #skip
else else
@@ -184,15 +182,15 @@ end
function update_Bn!(kfn::knowledgeFn) function update_Bn!(kfn::knowledgeFn)
Δw = nothing Δ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 Δ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 n.w_out = n.w_out - (n.Bn_wout_decay * n.w_out) # w_out decay
end 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 input_neuron_number = kfn.kfnParams[:input_neuron_number] # skip input neuron
for i = 1:kfn.kfn_params[:compute_neuron_number] for i = 1:kfn.kfnParams[:compute_neuron_number]
n = kfn.neurons_array[input_neuron_number+i] n = kfn.neuronsArray[input_neuron_number+i]
n.Bn = n.Bn + Δw[i] n.Bn = n.Bn + Δw[i]
n.Bn = n.Bn - (n.Bn_wout_decay * n.Bn) # w_out decay n.Bn = n.Bn - (n.Bn_wout_decay * n.Bn) # w_out decay
end 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 component_b = n.v_t1 - n.v_th < 0 ? 0 : n.v_t1 - n.v_th
#FIXME: not sure the following line is correct #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 end
function cal_v_reg!(n::alif_neuron) 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 component_b = n.v_t1 - n.av_th < 0 ? 0 : n.v_t1 - n.av_th
#FIXME: not sure the following line is correct #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 end
function voltage_error!(n::compute_neuron) function voltage_error!(n::compute_neuron)
@@ -232,23 +230,23 @@ function voltage_regulator!(n::compute_neuron) # running average
return Δw return Δw
end end
function firing_rate_error(kfn::knowledgeFn) function firingRateError(kfn::knowledgeFn)
start_id = kfn.kfn_params[:input_neuron_number] + 1 start_id = kfn.kfnParams[:input_neuron_number] + 1
return 0.5 * sum([(n.firing_diff)^2 for n in kfn.neurons_array[start_id:end]]) return 0.5 * sum([(n.firingDiff)^2 for n in kfn.neuronsArray[start_id:end]])
end end
function firing_rate_regulator!(n::compute_neuron) 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 * Δw = n.optimiser.eta * n.c_reg *
(n.firing_rate - n.firing_rate_target) * n.e_rec (n.firingRate - n.firingRateTarget) * n.eRec
Δw = n.firing_rate > n.firing_rate_target ? Δw : Δw * 0.0 Δw = n.firingRate > n.firingRateTarget ? Δw : Δw * 0.0
return Δw return Δw
end end
firing_rate!(n::compute_neuron) = n.firing_rate = (n.firing_counter / n.time_stamp) * 1000 firing_rate!(n::compute_neuron) = n.firingRate = (n.firingCounter / n.timeStep) * 1000
firing_diff!(n::compute_neuron) = n.firing_diff = n.firing_rate - n.firing_rate_target 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 # if there is 0-weight then replace it with new connection
zero_weight_index = findall(iszero.(n.w_rec)) zero_weight_index = findall(iszero.(n.w_rec))
if length(zero_weight_index) != 0 if length(zero_weight_index) != 0
@@ -257,8 +255,8 @@ function neuroplasticity!(n::compute_neuron, firing_neurons_list::Vector)
not fire = no information not fire = no information
""" """
subscribe_options = filter(x -> x [n.id], firing_neurons_list) # exclude this neuron id from the list subscribe_options = filter(x -> x [n.id], firedNeurons) # exclude this neuron id from the list
filter!(x -> x n.subscription_list, subscribe_options) # exclude this neuron's subscription_list from the list filter!(x -> x n.subscriptionList, subscribe_options) # exclude this neuron's subscriptionList from the list
shuffle!(subscribe_options) shuffle!(subscribe_options)
end 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 percentage = [new_connection_percent, 100.0 - new_connection_percent] / 100.0
for i in zero_weight_index for i in zero_weight_index
if Utils.random_choices([true, false], percentage) 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 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 # RSNN off path. Let weight grow by an optimiser
end end
@@ -283,7 +281,7 @@ function push_epsilon_rec_a!(n::lif_neuron)
end end
function push_epsilon_rec_a!(n::alif_neuron) function push_epsilon_rec_a!(n::alif_neuron)
push!(n.epsilon_rec_a, 0) push!(n.epsilonRecA, 0)
end end

View File

@@ -26,19 +26,19 @@ abstract type compute_neuron <: neuron end
""" """
Base.@kwdef mutable struct model <: Ironpen Base.@kwdef mutable struct model <: Ironpen
knowledgeFn::Union{Dict,Nothing} = nothing knowledgeFn::Union{Dict,Nothing} = nothing
model_params::Union{Dict,Nothing} = nothing modelParams::Union{Dict,Nothing} = nothing
error::Union{Float64,Nothing} = 0.0 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. """ "inference" = no learning params will be collected.
"learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time "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. reset epsilon_j.
"reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """
learning_stage::String = "inference" learningStage::String = "inference"
softreset::Bool = false softreset::Bool = false
time_stamp::Number = 0.0 timeStep::Number = 0.0
end end
""" Model outer constructor """ 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) :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_t => false, # neuron firing status at time = t
:z_t1 => false, # neuron firing status at time = t+1 :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 :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, :delta => 1.0,
:tau_m => 20.0, # membrane time constant in millisecond. The value is from the paper :tau_m => 20.0, # membrane time constant in millisecond. The value is from the paper
:eta => 0.01, # learning rate :eta => 0.01, # learning rate
@@ -59,15 +59,15 @@ end
I_kfn = Ironpen_ai_gpu.knowledgeFn(I_kfnparams, lif_neuron_params, alif_neuron_params, I_kfn = Ironpen_ai_gpu.knowledgeFn(I_kfnparams, lif_neuron_params, alif_neuron_params,
linear_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), :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) function model(params::Dict)
m = model() m = model()
m.model_params = params m.modelParams = params
fields = fieldnames(typeof(m)) fields = fieldnames(typeof(m))
for i in fields for i in fields
@@ -84,39 +84,37 @@ end
""" knowledgeFn struct """ knowledgeFn struct
""" """
Base.@kwdef mutable struct kfn_1 <: knowledgeFn Base.@kwdef mutable struct kfn_1 <: knowledgeFn
knowledgefn_name::Union{String,Nothing} = nothing knowledgeFnName::Union{String,Nothing} = nothing
kfn_params::Union{Dict,Nothing} = nothing # store params of knowledgeFn itself for later use kfnParams::Union{Dict,Nothing} = nothing # store params of knowledgeFn itself for later use
time_stamp::Number = 0.0 timeStep::Number = 0.0
# Bn contain error coefficient for both neurons and output neurons in one place # 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 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 """ put output neuron here. I seperate output neuron because
1. its calculation is difference than other neuron types 1. its calculation is difference than other neuron types
2. other neuron type will not induced to connnect to output neuron 2. other neuron type will not induced to connnect to output neuron
3. output neuron does not induced to connect to its own type """ 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. """ "inference" = no learning params will be collected.
"learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time "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. reset epsilon_j.
"reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """
learning_stage::String = "inference" learningStage::String = "inference"
error::Union{Float64,Nothing} = nothing error::Union{Float64,Nothing} = nothing
output_error::Union{Array,Nothing} = Vector{AbstractFloat}() outputError::Union{Array,Nothing} = Vector{AbstractFloat}()
recent_knowledgeFn_error::Union{Any,Nothing} = nothing
softreset::Bool = false softreset::Bool = false
meta_params::Union{Dict{Any,Any},Nothing} = Dict()
firing_neurons_list::Array{Int64} = Vector{Int64}() # store id of firing neurons firedNeurons::Array{Int64} = Vector{Int64}() # store unique id of firing neurons to be used when random neuron connection
snn_firing_state_t0::Union{Vector{Bool},Nothing} = nothing # store firing state of all neurons at t0 firedNeurons_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_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 avgNeuronsFiringRate::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 avgNeurons_v_t1::Union{Float64,Nothing} = 0.0 # for displaying average v_t1 over all neurons
end end
#------------------------------------------------------------------------------------------------100 #------------------------------------------------------------------------------------------------100
@@ -129,8 +127,8 @@ end
:type => "lif_neuron", :type => "lif_neuron",
:v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :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 :z_t => false, # neuron firing status at time = t
:gamma_pd => 0.3, # discount factor. The value is from the paper :gammaPd => 0.3, # discount factor. The value is from the paper
:refractory_duration => 2.0, # neuron refractory period in tick :refractoryDuration => 2.0, # neuron refractory period in tick
:delta => 1.0, :delta => 1.0,
:tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence :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", :type => "alif_neuron",
:v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :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 :z_t => false, # neuron firing status at time = t
:gamma_pd => 0.3, # discount factor. The value is from the paper :gammaPd => 0.3, # discount factor. The value is from the paper
:refractory_duration => 2.0, # neuron refractory period in millisecond :refractoryDuration => 2.0, # neuron refractory period in millisecond
:delta => 1.0, :delta => 1.0,
:tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence :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( I_kfnparams = Dict(
:knowledgefn_name => "I", :knowledgeFnName => "I",
:lif_neuron_number => 200, :lif_neuron_number => 200,
:alif_neuron_number => 100, # from Allen Institute, ALIF is 40% of LIF :alif_neuron_number => 100, # from Allen Institute, ALIF is 40% of LIF
:linear_neuron_number => 5, # output neuron, this is also the output length :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 :Bn => "random", # error projection coefficient from kfn output's error to each neurons's error
:learning_rate => 0.01, :learning_rate => 0.01,
:neuron_connection_pattern => "100%", # number of each neuron subscribe to other neuron in knowledgeFn.neurons_array :neuron_connection_pattern => "100%", # number of each neuron subscribe to other neuron in knowledgeFn.neuronsArray
:output_neuron_connection_pattern => "100%", # "60%" of kfn.neurons_array or number :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 :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_in_generation_pattern => "random", # number or "random"
:neuron_w_rec_generation_pattern => "random", :neuron_w_rec_generation_pattern => "random",
@@ -180,86 +178,86 @@ end
:meta_params => Dict(:is_first_cycle => true, :meta_params => Dict(:is_first_cycle => true,
:launch_time => 0.0,)) :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_1()
kfn.kfn_params = kfn_params kfn.kfnParams = kfnParams
kfn.knowledgefn_name = kfn.kfn_params[:knowledgefn_name] 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")) throw(error("number of compute neuron must be greater than input neuron"))
end end
# Bn # Bn
if kfn.kfn_params[:Bn] == "random" if kfn.kfnParams[:Bn] == "random"
kfn.Bn = [Random.rand(0:0.001:1) for i in 1:kfn.kfn_params[:compute_neuron_number]] 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 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 end
# assign neurons ID by their position in kfn.neurons array because I think it is # assign neurons ID by their position in kfn.neurons array because I think it is
# straight forward way # straight forward way
# add input port # add input port
for (k, v) in kfn.kfn_params[:input_port] for (k, v) in kfn.kfnParams[:input_port]
current_type = kfn.kfn_params[:input_port][k] current_type = kfn.kfnParams[:input_port][k]
for i = 1:current_type[:numbers] for i = 1:current_type[:numbers]
n_id = length(kfn.neurons_array) + 1 n_id = length(kfn.neuronsArray) + 1
neuron = init_neuron(n_id, current_type[:params], kfn.kfn_params) neuron = init_neuron(n_id, current_type[:params], kfn.kfnParams)
push!(kfn.neurons_array, neuron) push!(kfn.neuronsArray, neuron)
end end
end end
# add compute neurons # add compute neurons
for (k, v) in kfn.kfn_params[:compute_neuron] for (k, v) in kfn.kfnParams[:compute_neuron]
current_type = kfn.kfn_params[:compute_neuron][k] current_type = kfn.kfnParams[:compute_neuron][k]
for i = 1:current_type[:numbers] for i = 1:current_type[:numbers]
n_id = length(kfn.neurons_array) + 1 n_id = length(kfn.neuronsArray) + 1
neuron = init_neuron(n_id, current_type[:params], kfn.kfn_params) neuron = init_neuron(n_id, current_type[:params], kfn.kfnParams)
push!(kfn.neurons_array, neuron) push!(kfn.neuronsArray, neuron)
end end
end end
for i = 1:kfn.kfn_params[:output_port][:numbers] for i = 1:kfn.kfnParams[:output_port][:numbers]
neuron = init_neuron(i, kfn.kfn_params[:output_port][:params], neuron = init_neuron(i, kfn.kfnParams[:output_port][:params],
kfn.kfn_params) kfn.kfnParams)
push!(kfn.output_neurons_array, neuron) push!(kfn.outputNeuronsArray, neuron)
end end
# random which neuron output port subscribed to, 1-compute_neuron for each output port # 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_list = shuffle!([kfn.kfnParams[:total_input_port]+1:length(kfn.neuronsArray)...])
sub_output_neuron = [pop!(sub_list) for i in 1:kfn.kfn_params[:output_port][:numbers]] sub_output_neuron = [pop!(sub_list) for i in 1:kfn.kfnParams[:output_port][:numbers]]
for i in kfn.output_neurons_array for i in kfn.outputNeuronsArray
i.subscription_list = [pop!(sub_output_neuron)] i.subscriptionList = [pop!(sub_output_neuron)]
end end
for n in kfn.neurons_array for n in kfn.neuronsArray
if typeof(n) <: compute_neuron 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
end end
# excitatory neuron to inhabitory neuron = 60:40 % of compute_neuron # 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] 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] in_n = [-1 for i in 1:in_number]
ex_in = shuffle!([ex_n; in_n]) ex_in = shuffle!([ex_n; in_n])
# input neurons are always excitatory, compute_neurons are random between excitatory # input neurons are always excitatory, compute_neurons are random between excitatory
# and inhabitory # and inhabitory
for n in reverse(kfn.neurons_array) for n in reverse(kfn.neuronsArray)
try n.ExIn_type = pop!(ex_in) catch end try n.ExInType = pop!(ex_in) catch end
end end
# add ExIn_type into each compute_neuron sub_ExIn_type # add ExInType into each compute_neuron subExInType
for n in reverse(kfn.neurons_array) for n in reverse(kfn.neuronsArray)
try # input neuron doest have n.subscription_list try # input neuron doest have n.subscriptionList
for sub_id in n.subscription_list for sub_id in n.subscriptionList
n_ExIn_type = kfn.neurons_array[sub_id].ExIn_type n_ExInType = kfn.neuronsArray[sub_id].ExInType
push!(n.sub_ExIn_type, n_ExIn_type) push!(n.subExInType, n_ExInType)
end end
catch catch
end end
@@ -275,11 +273,11 @@ end
Base.@kwdef mutable struct passthrough_neuron <: input_neuron Base.@kwdef mutable struct passthrough_neuron <: input_neuron
id::Union{Int64,Nothing} = nothing # ID of this neuron which is it position in knowledgeFn array id::Union{Int64,Nothing} = nothing # ID of this neuron which is it position in knowledgeFn array
type::String = "passthrough_neuron" 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_t::Bool = false
z_t1::Bool = false z_t1::Bool = false
time_stamp::Number = 0.0 # current time timeStep::Number = 0.0 # current time
ExIn_type::Integer = 1 # 1 excitatory, -1 inhabitory. input neuron is always excitatory ExInType::Integer = 1 # 1 excitatory, -1 inhabitory. input neuron is always excitatory
end end
function passthrough_neuron(params::Dict) function passthrough_neuron(params::Dict)
@@ -305,17 +303,18 @@ end
Base.@kwdef mutable struct lif_neuron <: compute_neuron Base.@kwdef mutable struct lif_neuron <: compute_neuron
id::Union{Int64,Nothing} = nothing # this neuron ID i.e. position of this neuron in knowledgeFn id::Union{Int64,Nothing} = nothing # this neuron ID i.e. position of this neuron in knowledgeFn
type::String = "lif_neuron" 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 # Bn::Union{Float64,Nothing} = Random.rand() # Bias for neuron error
knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to knowledgeFnName::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 subscriptionList::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 subExInType::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons
time_stamp::Number = 0.0 # current time timeStep::Number = 0.0 # current time
w_rec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) 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_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_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_t_default::Union{Float64,Nothing} = 0.0 # default membrane potential voltage
v_th::Float64 = 1.0 # vᵗʰ, neuron firing threshold 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_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 # 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 # 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) 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 # 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 alpha::Union{Float64,Nothing} = nothing # α, neuron membrane potential decay factor
phi::Union{Float64,Nothing} = nothing # ϕ, psuedo derivative phi::Union{Float64,Nothing} = nothing # ϕ, psuedo derivative
epsilon_rec::Union{Array{Float64},Nothing} = nothing # ϵ_rec, eligibility vector for neuron spike epsilonRec::Union{Array{Float64},Nothing} = nothing # ϵ_rec, eligibility vector for neuron spike
decayed_epsilon_rec::Union{Array{Float64},Nothing} = nothing # α * epsilon_rec decayedEpsilonRec::Union{Array{Float64},Nothing} = nothing # α * epsilonRec
e_rec::Union{Array{Float64},Nothing} = nothing # eligibility trace for neuron spike eRec::Union{Array{Float64},Nothing} = nothing # eligibility trace for neuron spike
delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond
last_firing_time::Union{Float64,Nothing} = 0.0 # the last time neuron fires lastFiringTime::Union{Float64,Nothing} = 0.0 # the last time neuron fires
refractory_duration::Union{Float64,Nothing} = 3 # neuron's refratory period in millisecond 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_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 tau_m::Union{Float64,Nothing} = nothing # τ_m, membrane time constant in millisecond
eta::Union{Float64,Nothing} = 0.01 # η, learning rate eta::Union{Float64,Nothing} = 0.01 # η, learning rate
w_rec_change::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change
recurrent_signal::Union{Float64,Nothing} = nothing # incoming recurrent signal recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal
alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t 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 error::Union{Float64,Nothing} = nothing # local neuron error
optimiser::Union{Any,Nothing} = load_optimiser("AdaBelief") # Flux optimizer optimiser::Union{Any,Nothing} = load_optimiser("AdaBelief") # Flux optimizer
firing_counter::Float64 = 0.0 # store how many times neuron fires firingCounter::Float64 = 0.0 # store how many times neuron fires
firing_rate_target::Float64 = 20.0 # neuron's target firing rate in Hz firingRateTarget::Float64 = 20.0 # neuron's target firing rate in Hz
firing_diff::Float64 = 0.0 # e-prop supplement paper equation 5 firingDiff::Float64 = 0.0 # e-prop supplement paper equation 5
firing_rate_error::Float64 = 0.0 # local neuron error w.r.t. firing regularization firingRateError::Float64 = 0.0 # local neuron error w.r.t. firing regularization
firing_rate::Float64 = 0.0 # running average of firing rate in Hz firingRate::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}()
""" "inference" = no learning params will be collected. """ "inference" = no learning params will be collected.
"learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time "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. reset epsilon_j.
"reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """
learning_stage::String = "inference" learningStage::String = "inference"
end end
""" lif neuron outer constructor """ lif neuron outer constructor
@@ -371,8 +366,8 @@ end
:type => "lif_neuron", :type => "lif_neuron",
:v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I use auto generate) :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 :z_t => false, # neuron firing status at time = t
:gamma_pd => 0.3, # discount factor. The value is from the paper :gammaPd => 0.3, # discount factor. The value is from the paper
:refractory_duration => 2.0, # neuron refractory period in tick :refractoryDuration => 2.0, # neuron refractory period in tick
:delta => 1.0, :delta => 1.0,
:tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use for 1 sequence :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 Base.@kwdef mutable struct alif_neuron <: compute_neuron
id::Union{Int64,Nothing} = nothing # this neuron ID i.e. position of this neuron in knowledgeFn id::Union{Int64,Nothing} = nothing # this neuron ID i.e. position of this neuron in knowledgeFn
type::String = "alif_neuron" 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 # Bn::Union{Float64,Nothing} = Random.rand() # Bias for neuron error
knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to knowledgeFnName::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 subscriptionList::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 subExInType::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons
time_stamp::Union{Number,Nothing} = nothing # current time timeStep::Union{Number,Nothing} = nothing # current time
w_rec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) 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_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_t1::Float64 = 0.0 # vᵗ⁺¹, postsynaptic neuron membrane potential at current timestep
v_t_default::Union{Float64,Nothing} = 0.0 v_t_default::Union{Float64,Nothing} = 0.0
v_th::Float64 = 1.0 # vᵗʰ, neuron firing threshold 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_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 # 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 # 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 alpha::Union{Float64,Nothing} = nothing # α, neuron membrane potential decay factor
delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond 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 epsilonRec::Union{Array{Float64},Nothing} = nothing # ϵ_rec(v), eligibility vector for neuron i spike
epsilon_rec_a::Union{Array{Float64},Nothing} = nothing # ϵ_rec(a) epsilonRecA::Union{Array{Float64},Nothing} = nothing # ϵ_rec(a)
decayed_epsilon_rec::Union{Array{Float64},Nothing} = nothing # α * epsilon_rec decayedEpsilonRec::Union{Array{Float64},Nothing} = nothing # α * epsilonRec
e_rec_v::Union{Array{Float64},Nothing} = nothing # a component of neuron's eligibility trace resulted from v_t eRec_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 eRec_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 eRec::Union{Array{Float64},Nothing} = nothing # neuron's eligibility trace
eta::Union{Float64,Nothing} = 0.01 # eta, learning rate eta::Union{Float64,Nothing} = 0.01 # eta, learning rate
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
last_firing_time::Union{Float64,Nothing} = 0.0 # the last time neuron fires lastFiringTime::Union{Float64,Nothing} = 0.0 # the last time neuron fires
phi::Union{Float64,Nothing} = nothing # ϕ, psuedo derivative 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_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 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 wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change
recurrent_signal::Union{Float64,Nothing} = nothing # incoming recurrent signal recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal
alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t 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 error::Union{Float64,Nothing} = nothing # local neuron error
optimiser::Union{Any,Nothing} = load_optimiser("AdaBelief") # Flux optimizer optimiser::Union{Any,Nothing} = load_optimiser("AdaBelief") # Flux optimizer
firing_counter::Float64 = 0.0 # store how many times neuron fires firingCounter::Float64 = 0.0 # store how many times neuron fires
firing_rate_target::Float64 = 20.0 # neuron's target firing rate in Hz firingRateTarget::Float64 = 20.0 # neuron's target firing rate in Hz
firing_diff::Float64 = 0.0 # e-prop supplement paper equation 5 firingDiff::Float64 = 0.0 # e-prop supplement paper equation 5
firing_rate_error::Float64 = 0.0 # local neuron error w.r.t. firing regularization firingRateError::Float64 = 0.0 # local neuron error w.r.t. firing regularization
firing_rate::Float64 = 0.0 # running average of firing rate, Hz firingRate::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}()
tau_a::Union{Float64,Nothing} = nothing # τ_a, adaption time constant in millisecond tau_a::Union{Float64,Nothing} = nothing # τ_a, adaption time constant in millisecond
beta::Union{Float64,Nothing} = 0.15 # β, constant, value from paper 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. """ "inference" = no learning params will be collected.
"learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time "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. reset epsilon_j.
"reflect" = neuron will merge w_rec_change into w_rec then reset w_rec_change. """ "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """
learning_stage::String = "inference" learningStage::String = "inference"
end end
""" alif neuron outer constructor """ 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 :v_th => 1.2, # neuron firing threshold (this value is treated as maximum bound if I
use auto generate) use auto generate)
:z_t => false, # neuron firing status at time = t :z_t => false, # neuron firing status at time = t
:gamma_pd => 0.3, # discount factor. The value is from the paper :gammaPd => 0.3, # discount factor. The value is from the paper
:refractory_duration => 2.0, # neuron refractory period in millisecond :refractoryDuration => 2.0, # neuron refractory period in millisecond
:delta => 1.0, :delta => 1.0,
:tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use :tau_m => 5.0, # membrane time constant in millisecond. It should equals to time use
for 1 sequence for 1 sequence
@@ -516,9 +507,9 @@ end
Base.@kwdef mutable struct linear_neuron <: output_neuron Base.@kwdef mutable struct linear_neuron <: output_neuron
id::Union{Int64,Nothing} = nothing # ID of this neuron which is it position in knowledgeFn array id::Union{Int64,Nothing} = nothing # ID of this neuron which is it position in knowledgeFn array
type::String = "linear_neuron" type::String = "linear_neuron"
knowledgefn_name::Union{String,Nothing} = nothing # knowledgeFn that this neuron belongs to knowledgeFnName::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 subscriptionList::Union{Array{Int64},Nothing} = nothing # list of other neuron that this neuron synapse subscribed to
time_stamp::Union{Number,Nothing} = nothing # current time timeStep::Union{Number,Nothing} = nothing # current time
delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond delta::Union{Float64,Nothing} = 1.0 # δ, discreate timestep size in millisecond
out_t::Bool = false # output of linear neuron BEFORE forward() out_t::Bool = false # output of linear neuron BEFORE forward()
out_t1::Bool = false # output of linear neuron AFTER 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
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.id = id
n.knowledgefn_name = kfn_params[:knowledgefn_name] n.knowledgeFnName = kfnParams[:knowledgeFnName]
end 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.id = id
# n.knowledgefn_name = kfn_params[:knowledgefn_name] # n.knowledgeFnName = kfnParams[:knowledgeFnName]
# subscription_options = shuffle!([1:(kfn_params[:input_neuron_number]+kfn_params[:compute_neuron_number])...]) # subscription_options = shuffle!([1:(kfnParams[:input_neuron_number]+kfnParams[:compute_neuron_number])...])
# if typeof(kfn_params[:synaptic_connection_number]) == String # if typeof(kfnParams[:synaptic_connection_number]) == String
# percent = parse(Int, kfn_params[:synaptic_connection_number][1:end-1]) / 100 # percent = parse(Int, kfnParams[:synaptic_connection_number][1:end-1]) / 100
# synaptic_connection_number = floor(length(subscription_options) * percent) # 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 # end
# filter!(x -> x != n.id, n.subscription_list) # filter!(x -> x != n.id, n.subscriptionList)
# n.epsilon_rec = zeros(length(n.subscription_list)) # n.epsilonRec = zeros(length(n.subscriptionList))
# n.w_rec = Random.rand(length(n.subscription_list)) # n.w_rec = Random.rand(length(n.subscriptionList))
# n.w_rec_change = zeros(length(n.subscription_list)) # n.wRecChange = zeros(length(n.subscriptionList))
# n.reg_voltage_b = zeros(length(n.subscription_list)) # n.reg_voltage_b = zeros(length(n.subscriptionList))
# n.alpha = calculate_α(n) # n.alpha = calculate_α(n)
# end # 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.id = id
n.knowledgefn_name = kfn_params[:knowledgefn_name] n.knowledgeFnName = kfnParams[:knowledgeFnName]
subscription_options = shuffle!([1:kfn_params[:total_neurons]...]) subscription_options = shuffle!([1:kfnParams[:total_neurons]...])
subscription_numbers = Int(floor(n_params[:synaptic_connection_number] * subscription_numbers = Int(floor(n_params[:synaptic_connection_number] *
kfn_params[:total_neurons] / 100.0)) kfnParams[:total_neurons] / 100.0))
n.subscription_list = [pop!(subscription_options) for i = 1:subscription_numbers] n.subscriptionList = [pop!(subscription_options) for i = 1:subscription_numbers]
# prevent subscription to itself by removing this neuron id # 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.epsilonRec = zeros(length(n.subscriptionList))
n.w_rec = Random.rand(length(n.subscription_list)) n.w_rec = Random.rand(length(n.subscriptionList))
n.w_rec_change = zeros(length(n.subscription_list)) n.wRecChange = zeros(length(n.subscriptionList))
# n.reg_voltage_b = zeros(length(n.subscription_list)) # n.reg_voltage_b = zeros(length(n.subscriptionList))
n.alpha = calculate_α(n) n.alpha = calculate_α(n)
end end
function init_neuron!(id::Int64, n::alif_neuron, n_params::Dict, function init_neuron!(id::Int64, n::alif_neuron, n_params::Dict,
kfn_params::Dict) kfnParams::Dict)
n.id = id n.id = id
n.knowledgefn_name = kfn_params[:knowledgefn_name] n.knowledgeFnName = kfnParams[:knowledgeFnName]
subscription_options = shuffle!([1:kfn_params[:total_neurons]...]) subscription_options = shuffle!([1:kfnParams[:total_neurons]...])
subscription_numbers = Int(floor(n_params[:synaptic_connection_number] * subscription_numbers = Int(floor(n_params[:synaptic_connection_number] *
kfn_params[:total_neurons] / 100.0)) kfnParams[:total_neurons] / 100.0))
n.subscription_list = [pop!(subscription_options) for i = 1:subscription_numbers] n.subscriptionList = [pop!(subscription_options) for i = 1:subscription_numbers]
# prevent subscription to itself by removing this neuron id # 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.epsilonRec = zeros(length(n.subscriptionList))
n.w_rec = Random.rand(length(n.subscription_list)) n.w_rec = Random.rand(length(n.subscriptionList))
n.w_rec_change = zeros(length(n.subscription_list)) n.wRecChange = zeros(length(n.subscriptionList))
# n.reg_voltage_b = zeros(length(n.subscription_list)) # n.reg_voltage_b = zeros(length(n.subscriptionList))
n.alpha = calculate_α(n) # the more time has passed from the last time neuron was n.alpha = calculate_α(n) # the more time has passed from the last time neuron was
# activated, the more neuron membrane potential is reduced # activated, the more neuron membrane potential is reduced
n.rho = calculate_ρ(n) n.rho = calculate_ρ(n)
n.epsilon_rec_a = zeros(length(n.subscription_list)) n.epsilonRecA = zeros(length(n.subscriptionList))
end 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.id = id
# n.knowledgefn_name = kfn_params[:knowledgefn_name] # n.knowledgeFnName = kfnParams[:knowledgeFnName]
# start_id = kfn_params[:input_neuron_number] + 1 # don't readout from input neurons # start_id = kfnParams[:input_neuron_number] + 1 # don't readout from input neurons
# n.subscription_list = [start_id:(start_id+kfn_params[:compute_neuron_number]-1)...] # n.subscriptionList = [start_id:(start_id+kfnParams[:compute_neuron_number]-1)...]
# n.epsilon_j = zeros(length(n.subscription_list)) # n.epsilon_j = zeros(length(n.subscriptionList))
# n.w_out = Random.randn(length(n.subscription_list)) # n.w_out = Random.randn(length(n.subscriptionList))
# n.w_out_change = zeros(length(n.subscription_list)) # n.w_out_change = zeros(length(n.subscriptionList))
# n.b = Random.randn() # n.b = Random.randn()
# n.b_change = 0.0 # n.b_change = 0.0
# n.k = calculate_k(n) # n.k = calculate_k(n)
# end # end
#WORKING #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.id = id
n.knowledgefn_name = kfn_params[:knowledgefn_name] n.knowledgeFnName = kfnParams[:knowledgeFnName]
# start_id = kfn_params[:total_input_port] + 1 # don't readout from input neurons # start_id = kfnParams[:total_input_port] + 1 # don't readout from input neurons
# subscription_options = [start_id:(start_id+kfn_params[:total_compute_neuron]-1)...] # subscription_options = [start_id:(start_id+kfnParams[:total_compute_neuron]-1)...]
# n.subscription_list = [rand(subscription_options)] # n.subscriptionList = [rand(subscription_options)]
# n.epsilon_j = zeros(length(n.subscription_list)) # n.epsilon_j = zeros(length(n.subscriptionList))
# n.w_out = Random.randn(length(n.subscription_list)) # n.w_out = Random.randn(length(n.subscriptionList))
# n.w_out_change = zeros(length(n.subscription_list)) # n.w_out_change = zeros(length(n.subscriptionList))
# n.b = Random.randn() # n.b = Random.randn()
# n.b_change = 0.0 # n.b_change = 0.0
# n.k = calculate_k(n) # n.k = calculate_k(n)
@@ -664,9 +655,9 @@ end
""" Make a neuron intended for use with knowledgeFn """ 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) n = instantiate_custom_types(n_params)
init_neuron!(id, n, n_params, kfn_params) init_neuron!(id, n, n_params, kfnParams)
return n return n
end end
@@ -700,22 +691,22 @@ end
""" Add a new neuron into a knowledgeFn """ Add a new neuron into a knowledgeFn
# Example # 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) # function add_neuron!(neuron_Dict::Dict, kfn::knowledgeFn)
# id = length(kfn.neurons_array) + 1 # id = length(kfn.neuronsArray) + 1
# neuron = init_neuron(id, neuron_Dict, kfn.kfn_params, # neuron = init_neuron(id, neuron_Dict, kfn.kfnParams,
# total_neurons = (length(kfn.neurons_array) + 1)) # total_neurons = (length(kfn.neuronsArray) + 1))
# push!(kfn.neurons_array, neuron) # push!(kfn.neuronsArray, neuron)
# # Randomly select an output neuron to add a new neuron to # # 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 # 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) 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.epsilon_j, 0.0)
push!(o_n.w_out, Random.randn(1)[1]) push!(o_n.w_out, Random.randn(1)[1])
push!(o_n.w_out_change, 0.0) push!(o_n.w_out_change, 0.0)