diff --git a/src/Ironpen.jl b/src/Ironpen.jl index 20826e5..6edf4ea 100644 --- a/src/Ironpen.jl +++ b/src/Ironpen.jl @@ -34,8 +34,6 @@ using .interface """ Todo: - - [9] verify that model can complete learning cycle with no error [*5] synaptic connection strength concept. use sigmoid, turn connection offline [8] neuroplasticity() i.e. change connection @@ -58,6 +56,7 @@ using .interface if output neuron activates when it should NOT, use output neuron's (vt*100)/vth as error [DONE] use LinearAlgebra.normalize!(vector, 1) to adjust weight after weight merge + [DONE] reset_epsilonRec after ΔwRecChange is calculated Change from version: v06_36a - diff --git a/src/forward.jl b/src/forward.jl index 3caecfe..bf5785a 100644 --- a/src/forward.jl +++ b/src/forward.jl @@ -102,7 +102,7 @@ function (n::lif_neuron)(kfn::knowledgeFn) # decay of v_t1 n.v_t1 = n.alpha * n.v_t else - n.recSignal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed + n.recSignal = sum(n.wRec .* 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.recSignal @@ -118,7 +118,9 @@ function (n::lif_neuron)(kfn::knowledgeFn) end # there is a difference from alif formula - n.phi = (n.gammaPd / 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) + n.decayedEpsilonRec = n.alpha * n.epsilonRec + n.epsilonRec = n.decayedEpsilonRec + n.z_i_t end end @@ -147,7 +149,7 @@ function (n::alif_neuron)(kfn::knowledgeFn) 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.recSignal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed + n.recSignal = sum(n.wRec .* 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.recSignal n.v_t1 = no_negative!.(n.v_t1) @@ -161,7 +163,9 @@ function (n::alif_neuron)(kfn::knowledgeFn) end # there is a difference from lif formula - n.phi = (n.gammaPd / 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) + n.decayedEpsilonRec = n.alpha * n.epsilonRec + n.epsilonRec = n.decayedEpsilonRec + n.z_i_t end end @@ -188,7 +192,7 @@ function (n::linear_neuron)(kfn::T) where T<:knowledgeFn # decay of v_t1 n.v_t1 = n.alpha * n.v_t else - n.recSignal = sum(n.w_rec .* n.z_i_t) # signal from other neuron that this neuron subscribed + n.recSignal = sum(n.wRec .* 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.recSignal @@ -204,7 +208,9 @@ function (n::linear_neuron)(kfn::T) where T<:knowledgeFn end # there is a difference from alif formula - n.phi = (n.gammaPd / 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) + n.decayedEpsilonRec = n.alpha * n.epsilonRec + n.epsilonRec = n.decayedEpsilonRec + n.z_i_t end end diff --git a/src/learn.jl b/src/learn.jl index 6fdcd89..f1ab14d 100644 --- a/src/learn.jl +++ b/src/learn.jl @@ -59,23 +59,12 @@ function learn!(kfn::kfn_1, correctAnswer::AbstractVector) kfn.firedNeurons_t1 = Vector{Bool}() kfn.learningStage = "learning" - - #TODO prepare for end learning - elseif kfn.learningStage == "end_learning" - resetLearningParams!(n) - - # clear variables - kfn.firedNeurons = Vector{Int64}() - kfn.firedNeurons_t0 = Vector{Bool}() - kfn.firedNeurons_t1 = Vector{Bool}() - - kfn.learningStage = "inference" end # compute kfn error - out = [n.z_t1 for n in kfn.outputNeuronsArray] - for (i, v) in enumerate(out) - if v != correctAnswer[i] # need to adjust weight + outs = [n.z_t1 for n in kfn.outputNeuronsArray] + for (i, out) in enumerate(outs) + if out != correctAnswer[i] # need to adjust weight kfnError = (kfn.outputNeuronsArray[i].v_th - kfn.outputNeuronsArray[i].v_t) * 100 / kfn.outputNeuronsArray[i].v_th @@ -87,55 +76,36 @@ function learn!(kfn::kfn_1, correctAnswer::AbstractVector) learn!(kfn.outputNeuronsArray[i], kfn) end end - #WORKING - - - - - - - - # 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.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) - end - - - # for main loop user's display and training's exit condition - avgNeuronsFiringRate = 0.0 - for n in kfn.neuronsArray - if typeof(n) <: compute_neuron - avgNeuronsFiringRate += n.firingRate - end - end - kfn.avgNeuronsFiringRate = avgNeuronsFiringRate / - kfn.kfnParams[:compute_neuron_number] - avgNeurons_v_t1 = 0.0 - for n in kfn.neuronsArray - if typeof(n) <: compute_neuron - avgNeurons_v_t1 += n.v_t1 - end - end - kfn.avgNeurons_v_t1 = avgNeurons_v_t1 / kfn.kfnParams[:compute_neuron_number] - end - - - - - - # wrap up learning session if kfn.learningStage == "end_learning" - #TODO neuroplasticity + # Threads.@threads for n in kfn.neuronsArray + for n in kfn.neuronsArray + n.wRec += n.wRecChange # merge wRecChange into wRec + wSign = sign.(n.wRec) # check for fliped sign, 1 indicates non-fliped sign + nonFlipedSign = isequal.(n.subExInType, wSign) # 1 not fliped, 0 fliped + LinearAlgebra.normalize!(n.wRec, 1) + n.wRec .*= nonFlipedSign # set weight that fliped sign to 0 for random new connection + + #WORKING synapticConnStrength + + + + #TODO neuroplasticity + end + + for n in kfn.outputNeuronsArray # merge wRecChange into wRec + n.wRec += n.wRecChange + wSign = sign.(n.wRec) # check for fliped sign, 1 indicates non-fliped sign + nonFlipedSign = isequal.(n.subExInType, wSign) # 1 not fliped, 0 fliped + LinearAlgebra.normalize!(n.wRec, 1) + n.wRec .*= nonFlipedSign # set weight that fliped sign to 0 for random new connection + + #TODO synapticConnStrength + #TODO neuroplasticity + end + + resetLearningParams!(n) # clear variables @@ -156,25 +126,16 @@ end """ lif learn() """ function learn!(n::lif_neuron, error::Number) - n.decayedEpsilonRec = n.alpha * n.epsilonRec - n.epsilonRec = n.decayedEpsilonRec + n.z_i_t n.eRec = n.phi * n.epsilonRec ΔwRecChange = n.eta * error n.wRecChange = (n.subExInType * n.wRecChange) + ΔwRecChange - LinearAlgebra.normalize!(n.wRecChange, 1) - - # check for fliped sign, 1 indicates non-fliped sign - wSign = sign.(n.wRecChange) - nonFlipedSign = isequal.(n.subExInType, wSign) # 1 not fliped, 0 fliped - n.wRecChange .*= nonFlipedSign # set weight that fliped sign to 0 for random new connection + reset_epsilonRec!(n) end """ alif_neuron learn() """ function learn!(n::alif_neuron, error::Number) - 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 @@ -183,29 +144,17 @@ function learn!(n::alif_neuron, error::Number) ΔwRecChange = n.eta * error n.wRecChange = (n.subExInType * n.wRecChange) + ΔwRecChange - LinearAlgebra.normalize!(n.wRecChange, 1) - - # check for fliped sign, 1 indicates non-fliped sign - wSign = sign.(n.wRecChange) - nonFlipedSign = isequal.(n.subExInType, wSign) # 1 not fliped, 0 fliped - n.wRecChange .*= nonFlipedSign # set weight that fliped sign to 0 for random new connection + reset_epsilonRec!(n) end """ linear_neuron learn() """ function learn!(n::linear_neuron, error::Number) - n.decayedEpsilonRec = n.alpha * n.epsilonRec - n.epsilonRec = n.decayedEpsilonRec + n.z_i_t n.eRec = n.phi * n.epsilonRec ΔwRecChange = n.eta * error n.wRecChange = (n.subExInType * n.wRecChange) + ΔwRecChange - LinearAlgebra.normalize!(n.wRecChange, 1) - - # check for fliped sign, 1 indicates non-fliped sign - wSign = sign.(n.wRecChange) - nonFlipedSign = isequal.(n.subExInType, wSign) # 1 not fliped, 0 fliped - n.wRecChange .*= nonFlipedSign # set weight that fliped sign to 0 for random new connection + reset_epsilonRec!(n) end diff --git a/src/snn_utils.jl b/src/snn_utils.jl index 1bbc500..3104ea6 100644 --- a/src/snn_utils.jl +++ b/src/snn_utils.jl @@ -3,11 +3,11 @@ module snn_utils using Flux.Optimise: apply! export calculate_α, calculate_ρ, calculate_k, timestep_forward!, init_neuron, no_negative!, precision, calculate_w_change!, store_knowledgefn_error!, interneurons_adjustment!, - reset_z_t!, resetLearningParams!, reset_learning_history_params!, + reset_z_t!, resetLearningParams!, reset_learning_history_params!, reset_epsilonRec!, cal_v_reg!, calculate_w_change_end!, firing_rate_error!, firing_rate_regulator!, update_Bn!, cal_firing_reg!, neuroplasticity!, shakeup!, reset_learning_no_wchange!, adjust_internal_learning_rate!, - gradient_withloss + gradient_withloss using Statistics, Random, LinearAlgebra, Distributions, Zygote, Flux @@ -32,12 +32,13 @@ 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::neuron) = n.v_t = n.vRest reset_z_t!(n::compute_neuron) = n.z_t = false -reset_epsilon_rec!(n::compute_neuron) = n.epsilonRec = n.epsilonRec * 0.0 +reset_epsilonRec!(n::compute_neuron) = n.epsilonRec = n.epsilonRec * 0.0 +reset_epsilonRec!(n::output_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.wRecChange = n.wRecChange * 0.0 +reset_wRecChange!(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 @@ -57,7 +58,7 @@ reset_b_change!(n::linear_neuron) = n.b_change = n.b_change * 0.0 session """ # function reset_learning_no_wchange!(n::lif_neuron) -# reset_epsilon_rec!(n) +# reset_epsilonRec!(n) # # reset_v_t!(n) # # reset_z_t!(n) # # reset_reg_voltage_a!(n) @@ -73,7 +74,7 @@ reset_b_change!(n::linear_neuron) = n.b_change = n.b_change * 0.0 # # reset_refractory_state_active!(n) # end # function reset_learning_no_wchange!(n::Union{alif_neuron, elif_neuron}) -# reset_epsilon_rec!(n) +# reset_epsilonRec!(n) # reset_epsilon_rec_a!(n) # reset_v_t!(n) # reset_z_t!(n) @@ -99,8 +100,8 @@ reset_b_change!(n::linear_neuron) = n.b_change = n.b_change * 0.0 """ Reset all learning-related params at the END of learning session """ function resetLearningParams!(n::lif_neuron) - reset_epsilon_rec!(n) - reset_w_rec_change!(n) + reset_epsilonRec!(n) + reset_wRecChange!(n) # reset_v_t!(n) # reset_z_t!(n) reset_firing_counter!(n) @@ -111,9 +112,9 @@ function resetLearningParams!(n::lif_neuron) reset_refractoryCounter!(n) end function resetLearningParams!(n::alif_neuron) - reset_epsilon_rec!(n) + reset_epsilonRec!(n) reset_epsilon_rec_a!(n) - reset_w_rec_change!(n) + reset_wRecChange!(n) # reset_v_t!(n) # reset_z_t!(n) # reset_a!(n) @@ -133,8 +134,8 @@ function resetLearningParams!(n::passthrough_neuron) end function resetLearningParams!(n::linear_neuron) - reset_epsilon_rec!(n) - reset_w_rec_change!(n) + reset_epsilonRec!(n) + reset_wRecChange!(n) reset_v_t!(n) reset_firing_counter!(n) @@ -245,7 +246,7 @@ firing_diff!(n::compute_neuron) = n.firingDiff = n.firingRate - n.firingRateTarg 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)) + zero_weight_index = findall(iszero.(n.wRec)) if length(zero_weight_index) != 0 """ sampling new connection from list of neurons that fires instead of ramdom choose from all compute neuron because there is no point to connect to neuron that not fires i.e. @@ -262,7 +263,7 @@ function neuroplasticity!(n::compute_neuron, firedNeurons::Vector) for i in zero_weight_index if Utils.random_choices([true, false], percentage) n.subscriptionList[i] = pop!(subscribe_options) - n.w_rec[i] = 0.01 # new connection should not send large signal otherwise it would throw + n.wRec[i] = 0.01 # new connection should not send large signal otherwise it would throw # RSNN off path. Let weight grow by an optimiser end end diff --git a/src/types.jl b/src/types.jl index cf7541a..2885d6a 100644 --- a/src/types.jl +++ b/src/types.jl @@ -34,7 +34,7 @@ Base.@kwdef mutable struct model <: Ironpen "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + "reflect" = neuron will merge wRecChange into wRec then reset wRecChange. """ learningStage::String = "inference" softreset::Bool = false @@ -102,7 +102,7 @@ Base.@kwdef mutable struct kfn_1 <: knowledgeFn "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + "reflect" = neuron will merge wRecChange into wRec then reset wRecChange. """ learningStage::String = "inference" error::Union{Float64,Nothing} = nothing @@ -312,7 +312,7 @@ Base.@kwdef mutable struct lif_neuron <: compute_neuron 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) + wRec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) v_t::Float64 = rand() # 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 @@ -340,7 +340,7 @@ Base.@kwdef mutable struct lif_neuron <: compute_neuron refractoryCounter::Integer = 0 tau_m::Union{Float64,Nothing} = nothing # τ_m, membrane time constant in millisecond eta::Union{Float64,Nothing} = 0.01 # η, learning rate - wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change + wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated wRec change recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t error::Union{Float64,Nothing} = nothing # local neuron error @@ -356,7 +356,7 @@ Base.@kwdef mutable struct lif_neuron <: compute_neuron "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + "reflect" = neuron will merge wRecChange into wRec then reset wRecChange. """ learningStage::String = "inference" end @@ -405,7 +405,7 @@ Base.@kwdef mutable struct alif_neuron <: compute_neuron 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) + wRec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) v_t::Float64 = rand() # 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 @@ -436,7 +436,7 @@ Base.@kwdef mutable struct alif_neuron <: compute_neuron # refractory_state_active::Union{Bool,Nothing} = false # if true, neuron is in refractory state and cannot process new information refractoryCounter::Integer = 0 tau_m::Union{Float64,Nothing} = nothing # τ_m, membrane time constant in millisecond - wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change + wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated wRec change recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t error::Union{Float64,Nothing} = nothing # local neuron error @@ -458,7 +458,7 @@ Base.@kwdef mutable struct alif_neuron <: compute_neuron "learning" = neuron will accumulate epsilon_j, compute Δw_rec_change each time correct answer is available then merge Δw_rec_change into wRecChange then reset epsilon_j. - "reflect" = neuron will merge wRecChange into w_rec then reset wRecChange. """ + "reflect" = neuron will merge wRecChange into wRec then reset wRecChange. """ learningStage::String = "inference" end @@ -513,7 +513,7 @@ Base.@kwdef mutable struct linear_neuron <: output_neuron timeStep::Union{Number,Nothing} = nothing # current time subExInType::Array{Int64} = Vector{Int64}() # store ExIn type of subscribed neurons - w_rec::Union{Array{Float64},Nothing} = nothing # synaptic weight (for receiving signal from other neuron) + wRec::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 @@ -542,7 +542,7 @@ Base.@kwdef mutable struct linear_neuron <: output_neuron refractoryCounter::Integer = 0 tau_out::Union{Float64,Nothing} = nothing # τ_out, membrane time constant in millisecond eta::Union{Float64,Nothing} = 0.01 # η, learning rate - wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated w_rec change + wRecChange::Union{Array{Float64},Nothing} = nothing # Δw_rec, cumulated wRec change recSignal::Union{Float64,Nothing} = nothing # incoming recurrent signal alpha_v_t::Union{Float64,Nothing} = nothing # alpha * v_t error::Union{Float64,Nothing} = nothing # local neuron error @@ -614,7 +614,7 @@ end # end # filter!(x -> x != n.id, n.subscriptionList) # n.epsilonRec = zeros(length(n.subscriptionList)) -# n.w_rec = Random.rand(length(n.subscriptionList)) +# n.wRec = Random.rand(length(n.subscriptionList)) # n.wRecChange = zeros(length(n.subscriptionList)) # n.reg_voltage_b = zeros(length(n.subscriptionList)) # n.alpha = calculate_α(n) @@ -633,7 +633,7 @@ function init_neuron!(id::Int64, n::lif_neuron, n_params::Dict, kfnParams::Dict) n.synapticStrength = normalize!(rand(length(n.subscriptionList)), 1) n.epsilonRec = zeros(length(n.subscriptionList)) - n.w_rec = Random.rand(length(n.subscriptionList)) + n.wRec = Random.rand(length(n.subscriptionList)) n.wRecChange = zeros(length(n.subscriptionList)) n.alpha = calculate_α(n) end @@ -652,7 +652,7 @@ function init_neuron!(id::Int64, n::alif_neuron, n_params::Dict, n.synapticStrength = normalize!(rand(length(n.subscriptionList)), 1) n.epsilonRec = zeros(length(n.subscriptionList)) - n.w_rec = Random.rand(length(n.subscriptionList)) + n.wRec = Random.rand(length(n.subscriptionList)) n.wRecChange = zeros(length(n.subscriptionList)) # the more time has passed from the last time neuron was activated, the more @@ -673,7 +673,7 @@ function init_neuron!(id::Int64, n::linear_neuron, n_params::Dict, kfnParams::Di n.subscriptionList = [pop!(subscription_options) for i = 1:subscription_numbers] n.synapticStrength = normalize!(rand(length(n.subscriptionList)), 1) n.epsilonRec = zeros(length(n.subscriptionList)) - n.w_rec = Random.rand(length(n.subscriptionList)) + n.wRec = Random.rand(length(n.subscriptionList)) n.wRecChange = zeros(length(n.subscriptionList)) n.alpha = calculate_k(n) end