The code is quite long and I will write here the function I’m talking on.
From Nonlinear Adaptive Control example I’m looking at the add_adaptation
function.
This is the original code:
def add_adaptation(model, learning_rate=1e-5):
with model:
# create ensemble to adapt to unmodeled dynamics
adapt = nengo.Ensemble(n_neurons=500, dimensions=2, radius=np.sqrt(2))
means = np.array([0.75, 2.25])
scaling = np.array([0.75, 1.25])
scale_node = nengo.Node(
output=lambda t, x: (x - means) / scaling, size_in=2, size_out=2
)
# to send target info to ensemble
nengo.Connection(model.arm_node[:2], scale_node)
nengo.Connection(scale_node, adapt)
# create the learning connection from adapt to the arm simulation
learn_conn = nengo.Connection(
adapt,
model.arm_node[2:4],
function=lambda x: np.zeros(2),
learning_rule_type=nengo.PES(learning_rate),
synapse=0.05,
)
# connect up the osc signal as the training signal
nengo.Connection(model.osc_node, learn_conn.learning_rule, transform=-1)
return model
Here they use a learning rate of 1e-5
but then multiply the post output by 10 times.
Instead of using 1e-5
, they could add a dedicated node for scaling the error (and reduce the learning rate by 10 times), or change the transform
property in the last nengo.Connection
to -0.1
like:
def add_adaptation(model, learning_rate=1e-4): # !! CHANGE !!
with model:
# create ensemble to adapt to unmodeled dynamics
adapt = nengo.Ensemble(n_neurons=500, dimensions=2, radius=np.sqrt(2))
means = np.array([0.75, 2.25])
scaling = np.array([0.75, 1.25])
scale_node = nengo.Node(
output=lambda t, x: (x - means) / scaling, size_in=2, size_out=2
)
# to send target info to ensemble
nengo.Connection(model.arm_node[:2], scale_node)
nengo.Connection(scale_node, adapt)
# create the learning connection from adapt to the arm simulation
learn_conn = nengo.Connection(
adapt,
model.arm_node[2:4],
function=lambda x: np.zeros(2),
learning_rule_type=nengo.PES(learning_rate),
synapse=0.05,
)
# connect up the osc signal as the training signal
nengo.Connection(model.osc_node, learn_conn.learning_rule, transform=-0.1) # !! CHANGE !!
return model
In that way, they can keep the representational range of the ensemble ([-1,1]
) without changing the learning rate. So what is the motivation to keep the learning rate low instead of modifying the transform
or adding a scale node (and creating a cleaner code)?