State Variables
[1]:
# uncomment the command below to install the nnodely package
#!pip install nnodely
from nnodely import *
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>-- nnodely_v1.5.0 --<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
State Definition
Use the keyword ‘State’ to define a state variable (like you will do for an Input). You can specify the dimension of the variable.
[2]:
clearNames('x_state')
x_state = Input('x_state', dimensions=1)
x_out = Fir(x_state.tw(0.5))
Closed Loop
Every relation inside NNodely can update a state variable. closing a state in a loop means that at the end of each forward pass the result of the selected relation will update the selected state variable.
[3]:
clearNames('out')
x_out.closedLoop(x_state)
out = Output('out',x_out)
or you can use the ClosedLoop block
[4]:
clearNames('out')
x_out = ClosedLoop(x_out, x_state)
out = Output('out',x_out)
Connect
Every relation inside NNodely can update a state variable. connecting a relation to a state means that at each forward pass the result of the selected relation will immediately update the selected state variable.
(Note: you must re-define the relation in order to change the update of the state variable)
[5]:
clearNames()
x_out = Fir(x_state.tw(0.5))
x_out.connect(x_state)
out = Output('out',x_out)
or you can use the Connect block
[6]:
clearNames('out')
x_out = Connect(x_out, x_state)
out = Output('out',x_out)
Connect two models together
[9]:
clearNames(['a','b_t','c','d_t','b_in','shared','b','A','B','C','D','d'])
import numpy as np
def linear_function(x, k1, k2):
return x*k1 + k2
data_a = np.arange(1,101, dtype=np.float32)
data_b_t = linear_function(data_a, 2, 3)
data_c = np.arange(1,101, dtype=np.float32)
data_b_in = np.arange(5,105, dtype=np.float32)
data_d_t = linear_function(data_c, 5, 1)
dataset = {'a': data_a, 'b_t': data_b_t, 'c':data_c, 'b_in': data_b_in, 'd_t':data_d_t }
## Model a
a = Input('a')
b_t = Input('b_t')
shared = Parameter('shared',dimensions=(1,1))
output_relation = Linear(W=shared)(a.last())+Linear(W='A')(Fir(W='B')(a.tw(0.5)))
b = Output('b',output_relation)
model = Modely(seed=42)
model.addModel('b_model', b)
model.addMinimize('b_min', b, b_t.last())
model.neuralizeModel(0.1)
# Model d
c = Input('c')
d_t = Input('d_t')
b_in = Input('b_in')
output_relation.connect(b_in)
d = Output('d',Linear(W=shared)(c.last())+Fir(W='C')(c.tw(0.5))+Fir(W='D')(b_in.tw(0.3)))
model.addModel('d_model', [b,d])
model.addMinimize('d_min', d, d_t.last())
model.neuralizeModel(0.1)
model.loadData('dataset', dataset)
params = {'num_of_epochs': 1,
'train_batch_size': 8,
'val_batch_size': 8,
'test_batch_size':1,
'lr':0.1}
## training dei parametri di tutti i modelli
_ = model.trainModel(splits=[100,0,0], training_params=params, prediction_samples=4)
================================ nnodely Model =================================
{'Constants': {},
'Functions': {},
'Info': {'SampleTime': 0.1,
'nnodely_version': '1.5.0',
'ns': [5, 0],
'ntot': 5,
'num_parameters': 7},
'Inputs': {'a': {'dim': 1,
'ns': [5, 0],
'ntot': 5,
'sw': [-1, 0],
'tw': [-0.5, 0]},
'b_t': {'dim': 1, 'ns': [1, 0], 'ntot': 1, 'sw': [-1, 0]}},
'Minimizers': {'b_min': {'A': 'Add47', 'B': 'SamplePart49', 'loss': 'mse'}},
'Models': 'b_model',
'Outputs': {'b': 'Add47'},
'Parameters': {'A': {'dim': [1, 1], 'values': [[0.600895345211029]]},
'B': {'dim': 1,
'tw': 0.5,
'values': [[0.8822692632675171],
[0.9150039553642273],
[0.38286375999450684],
[0.9593056440353394],
[0.3904482126235962]]},
'shared': {'dim': [1, 1], 'values': [[0.2565724849700928]]}},
'Relations': {'Add47': ['Add', ['Linear42', 'Linear46']],
'Fir45': ['Fir', ['TimePart44'], 'B', None, 0],
'Linear42': ['Linear', ['SamplePart41'], 'shared', None, 0],
'Linear46': ['Linear', ['Fir45'], 'A', None, 0],
'SamplePart41': ['SamplePart', ['a'], -1, [-1, 0]],
'SamplePart49': ['SamplePart', ['b_t'], -1, [-1, 0]],
'TimePart44': ['TimePart', ['a'], -1, [-0.5, 0]]}}
================================================================================
================================ nnodely Model =================================
{'Constants': {},
'Functions': {},
'Info': {'SampleTime': 0.1,
'nnodely_version': '1.5.0',
'ns': [5, 0],
'ntot': 5,
'num_parameters': 15},
'Inputs': {'a': {'dim': 1,
'ns': [5, 0],
'ntot': 5,
'sw': [-1, 0],
'tw': [-0.5, 0]},
'b_in': {'dim': 1, 'ns': [3, 0], 'ntot': 3, 'tw': [-0.3, 0]},
'b_t': {'dim': 1, 'ns': [1, 0], 'ntot': 1, 'sw': [-1, 0]},
'c': {'dim': 1,
'ns': [5, 0],
'ntot': 5,
'sw': [-1, 0],
'tw': [-0.5, 0]},
'd_t': {'dim': 1, 'ns': [1, 0], 'ntot': 1, 'sw': [-1, 0]}},
'Minimizers': {'b_min': {'A': 'Add47', 'B': 'SamplePart49', 'loss': 'mse'},
'd_min': {'A': 'Add60', 'B': 'SamplePart62', 'loss': 'mse'}},
'Models': {'b_model': {'Constants': [],
'Functions': [],
'Inputs': ['b_t', 'a'],
'Outputs': ['b'],
'Parameters': ['B', 'A', 'shared'],
'Relations': ['SamplePart49',
'TimePart44',
'Fir45',
'Linear46',
'SamplePart41',
'Linear42',
'Add47']},
'd_model': {'Constants': [],
'Functions': [],
'Inputs': ['b_in', 'c', 'a'],
'Outputs': ['d', 'b'],
'Parameters': ['D', 'C', 'shared', 'B', 'A'],
'Relations': ['TimePart58',
'Fir59',
'TimePart54',
'Fir55',
'SamplePart51',
'Linear52',
'Add56',
'Add60',
'TimePart44',
'Fir45',
'Linear46',
'SamplePart41',
'Linear42',
'Add47']}},
'Outputs': {'b': 'Add47', 'd': 'Add60'},
'Parameters': {'A': {'dim': [1, 1], 'values': [[0.600895345211029]]},
'B': {'dim': 1,
'tw': 0.5,
'values': [[0.8822692632675171],
[0.9150039553642273],
[0.38286375999450684],
[0.9593056440353394],
[0.3904482126235962]]},
'C': {'dim': 1,
'tw': 0.5,
'values': [[0.9345980882644653],
[0.5935796499252319],
[0.8694044351577759],
[0.5677152872085571],
[0.7410940527915955]]},
'D': {'dim': 1,
'tw': 0.3,
'values': [[0.7936413288116455],
[0.9407714605331421],
[0.13318592309951782]]},
'shared': {'dim': [1, 1], 'values': [[0.2565724849700928]]}},
'Relations': {'Add47': ['Add', ['Linear42', 'Linear46']],
'Add56': ['Add', ['Linear52', 'Fir55']],
'Add60': ['Add', ['Add56', 'Fir59']],
'Fir45': ['Fir', ['TimePart44'], 'B', None, 0],
'Fir55': ['Fir', ['TimePart54'], 'C', None, 0],
'Fir59': ['Fir', ['TimePart58'], 'D', None, 0],
'Linear42': ['Linear', ['SamplePart41'], 'shared', None, 0],
'Linear46': ['Linear', ['Fir45'], 'A', None, 0],
'Linear52': ['Linear', ['SamplePart51'], 'shared', None, 0],
'SamplePart41': ['SamplePart', ['a'], -1, [-1, 0]],
'SamplePart49': ['SamplePart', ['b_t'], -1, [-1, 0]],
'SamplePart51': ['SamplePart', ['c'], -1, [-1, 0]],
'SamplePart62': ['SamplePart', ['d_t'], -1, [-1, 0]],
'TimePart44': ['TimePart', ['a'], -1, [-0.5, 0]],
'TimePart54': ['TimePart', ['c'], -1, [-0.5, 0]],
'TimePart58': ['TimePart', ['b_in'], -1, [-0.3, 0]]}}
================================================================================
============================ nnodely Model Dataset =============================
Dataset Name: dataset
Number of files: 1
Total number of samples: 96
Shape of d_t: (96, 1, 1)
Shape of b_in: (96, 3, 1)
Shape of c: (96, 5, 1)
Shape of a: (96, 5, 1)
Shape of b_t: (96, 1, 1)
================================================================================
======================== nnodely Model Train Parameters ========================
models: ['b_model', 'd_model']
num of epochs: 1
update per epochs: 12
└>(n_samples-batch_size)/batch_size+1
shuffle _data: True
train dataset: dataset_train
- num of samples: 96
- batch size: 8
- unused samples: 0
└>n_samples-update_per_epochs*batch_size
minimizers: {'b_min': {'A': 'Add47',
'B': 'SamplePart49',
'loss': 'mse'},
'd_min': {'A': 'Add60',
'B': 'SamplePart62',
'loss': 'mse'}}
optimizer: Adam
optimizer defaults: {'lr': 0.1}
optimizer params: [{'params': 'A'},
{'params': 'B'},
{'params': 'C'},
{'params': 'D'},
{'params': 'shared'}]
================================================================================
=========================== nnodely Training ===========================
| Epoch | b_min | d_min | Total |
| | Loss | Loss | Loss |
| | train | train | train |
|----------------------------------------------------------------------|
| 1/1 | 2.324e+02 | 3.749e+02 | 3.036e+02 |
|----------------------------------------------------------------------|
============================ nnodely Training Time =============================
Total time of Training: 0.014888763427734375
================================================================================
Recurrent Train
In order to do a recurrent training of the network using the State variables is mandatory to specify the window of prediction (prediction_samples).
[13]:
import numpy as np
clearNames(['x','x_state','y_state','out'])
x = Input('x', dimensions=3)
x_state = Input('x_state', dimensions=3)
y_state = Input('y_state', dimensions=3)
x_out = Linear(output_dimension=3)(x_state.tw(0.5))
y_out = Linear(output_dimension=3)(y_state.tw(0.5))
x_out.closedLoop(x_state)
y_out.closedLoop(y_state)
out = Output('out',x_out+y_out)
test = Modely(seed=42)
test.addModel('model', out)
test.addMinimize('error', out, x.tw(0.5))
test.neuralizeModel(0.1)
dataset = {'x':np.array([np.random.uniform(1,4,300)]).reshape(100,3).tolist()}
test.loadData(name='dataset', source=dataset)
# Training non ricorrente
params = {'num_of_epochs': 10, 'train_batch_size': 1, 'val_batch_size':1, 'lr':0.01}
tp = test.trainModel(splits=[70,20,10], prediction_samples=5, shuffle_data=False, training_params=params)
print('finale state: ', test._states)
================================ nnodely Model =================================
{'Constants': {},
'Functions': {},
'Info': {'SampleTime': 0.1,
'nnodely_version': '1.5.0',
'ns': [5, 0],
'ntot': 5,
'num_parameters': 18},
'Inputs': {'x': {'dim': 3, 'ns': [5, 0], 'ntot': 5, 'tw': [-0.5, 0]},
'x_state': {'closedLoop': 'Linear92',
'dim': 3,
'local': 1,
'ns': [5, 0],
'ntot': 5,
'tw': [-0.5, 0]},
'y_state': {'closedLoop': 'Linear95',
'dim': 3,
'local': 1,
'ns': [5, 0],
'ntot': 5,
'tw': [-0.5, 0]}},
'Minimizers': {'error': {'A': 'Add96', 'B': 'TimePart98', 'loss': 'mse'}},
'Models': 'model',
'Outputs': {'out': 'Add96'},
'Parameters': {'PLinear75W': {'dim': [3, 3],
'values': [[0.13318592309951782,
0.9345980882644653,
0.5935796499252319],
[0.8694044351577759,
0.5677152872085571,
0.7410940527915955],
[0.42940449714660645,
0.8854429125785828,
0.5739044547080994]]},
'PLinear77W': {'dim': [3, 3],
'values': [[0.8822692632675171,
0.9150039553642273,
0.38286375999450684],
[0.9593056440353394,
0.3904482126235962,
0.600895345211029],
[0.2565724849700928,
0.7936413288116455,
0.9407714605331421]]}},
'Relations': {'Add96': ['Add', ['Linear92', 'Linear95']],
'Linear92': ['Linear', ['TimePart91'], 'PLinear75W', None, 0],
'Linear95': ['Linear', ['TimePart94'], 'PLinear77W', None, 0],
'TimePart91': ['TimePart', ['x_state'], -1, [-0.5, 0]],
'TimePart94': ['TimePart', ['y_state'], -1, [-0.5, 0]],
'TimePart98': ['TimePart', ['x'], -1, [-0.5, 0]]}}
================================================================================
============================ nnodely Model Dataset =============================
Dataset Name: dataset
Number of files: 1
Total number of samples: 96
Shape of x: (96, 5, 3)
================================================================================
======================== nnodely Model Train Parameters ========================
models: ['model']
num of epochs: 10
update per epochs: 62
└>(n_samples-batch_size-prediction_samples+1)/(batch_size+step-1)+1
prediction samples: 5
step: 0
closed loop: {}
connect: {}
train dataset: dataset_train
- num of samples: 67
- batch size: 1
- unused samples: 0
└>n_samples-prediction_samples-update_per_epochs*(batch_size+step-1)
val dataset: None
val {batch size, samples}: {1, 19}
minimizers: {'error': {'A': 'Add96',
'B': 'TimePart98',
'loss': 'mse'}}
optimizer: Adam
optimizer defaults: {'lr': 0.01}
optimizer params: [{'params': 'PLinear75W'},
{'params': 'PLinear77W'}]
================================================================================
================= nnodely Training =================
| Epoch | error | Total |
| | Loss | Loss |
| | train | val | train | val |
|--------------------------------------------------|
| 1/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 2/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 3/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 4/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 5/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 6/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 7/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 8/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 9/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
| 10/10 |6.723e+00|7.729e+00|6.723e+00|7.729e+00|
|--------------------------------------------------|
============================ nnodely Training Time =============================
Total time of Training: 2.2795727252960205
================================================================================
finale state: {'y_state': tensor([[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]]), 'x_state': tensor([[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]])}
Clear State
use the specific function to manually clear the state of a state variable
[14]:
test.resetStates()
print('finale state: ', test.states)
finale state: {'y_state': [[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]], 'x_state': [[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]]}
State update at training time
States variables can also be created at training time. These variables will exist only during the training process.
To create the you have to define a dictionary containing {input:output}. In that case, the ‘input’ will become a state during the training process and the ‘output’ will be the relation updating it.
[15]:
import numpy as np
clearNames()
x = Input('x', dimensions=3)
x_s = Input('x_s', dimensions=3)
y_s = Input('y_s', dimensions=3)
x_out = Linear(output_dimension=3)(x_s.tw(0.5))
y_out = Linear(output_dimension=3)(y_s.tw(0.5))
out = Output('out',x_out+y_out)
out_x = Output('out_x',x_out)
out_y = Output('out_y',y_out)
test = Modely(seed=42)
test.addModel('model', [out,out_x,out_y])
test.addMinimize('error', out, x.tw(0.5))
test.neuralizeModel(0.1)
dataset = {'x':np.array([np.random.uniform(1,4,300)]).reshape(100,3).tolist()}
test.loadData(name='dataset', source=dataset)
# Training non ricorrente
params = {'num_of_epochs': 10, 'train_batch_size': 4, 'val_batch_size':4, 'test_batch_size':1, 'lr':0.01}
test.trainModel(splits=[70,20,10], prediction_samples=3, shuffle_data=False, closed_loop={'x_s':'out_x','y_s':'out_y'}, training_params=params)
print('finale state: ', test.states)
================================ nnodely Model =================================
{'Constants': {},
'Functions': {},
'Info': {'SampleTime': 0.1,
'nnodely_version': '1.5.0',
'ns': [5, 0],
'ntot': 5,
'num_parameters': 18},
'Inputs': {'x': {'dim': 3, 'ns': [5, 0], 'ntot': 5, 'tw': [-0.5, 0]},
'x_s': {'dim': 3, 'ns': [5, 0], 'ntot': 5, 'tw': [-0.5, 0]},
'y_s': {'dim': 3, 'ns': [5, 0], 'ntot': 5, 'tw': [-0.5, 0]}},
'Minimizers': {'error': {'A': 'Add105', 'B': 'TimePart107', 'loss': 'mse'}},
'Models': 'model',
'Outputs': {'out': 'Add105', 'out_x': 'Linear101', 'out_y': 'Linear104'},
'Parameters': {'PLinear3W': {'dim': [3, 3],
'values': [[0.13318592309951782,
0.9345980882644653,
0.5935796499252319],
[0.8694044351577759,
0.5677152872085571,
0.7410940527915955],
[0.42940449714660645,
0.8854429125785828,
0.5739044547080994]]},
'PLinear5W': {'dim': [3, 3],
'values': [[0.8822692632675171,
0.9150039553642273,
0.38286375999450684],
[0.9593056440353394,
0.3904482126235962,
0.600895345211029],
[0.2565724849700928,
0.7936413288116455,
0.9407714605331421]]}},
'Relations': {'Add105': ['Add', ['Linear101', 'Linear104']],
'Linear101': ['Linear', ['TimePart100'], 'PLinear3W', None, 0],
'Linear104': ['Linear', ['TimePart103'], 'PLinear5W', None, 0],
'TimePart100': ['TimePart', ['x_s'], -1, [-0.5, 0]],
'TimePart103': ['TimePart', ['y_s'], -1, [-0.5, 0]],
'TimePart107': ['TimePart', ['x'], -1, [-0.5, 0]]}}
================================================================================
============================ nnodely Model Dataset =============================
Dataset Name: dataset
Number of files: 1
Total number of samples: 96
Shape of x: (96, 5, 3)
================================================================================
======================== nnodely Model Train Parameters ========================
models: ['model']
num of epochs: 10
update per epochs: 16
└>(n_samples-batch_size-prediction_samples+1)/(batch_size+step-1)+1
prediction samples: 3
step: 0
closed loop: {'x_s': 'out_x', 'y_s': 'out_y'}
connect: {}
train dataset: dataset_train
- num of samples: 67
- batch size: 4
- unused samples: 0
└>n_samples-prediction_samples-update_per_epochs*(batch_size+step-1)
val dataset: None
val {batch size, samples}: {4, 19}
minimizers: {'error': {'A': 'Add105',
'B': 'TimePart107',
'loss': 'mse'}}
optimizer: Adam
optimizer defaults: {'lr': 0.01}
optimizer params: [{'params': 'PLinear3W'}, {'params': 'PLinear5W'}]
================================================================================
================= nnodely Training =================
| Epoch | error | Total |
| | Loss | Loss |
| | train | val | train | val |
|--------------------------------------------------|
| 1/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 2/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 3/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 4/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 5/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 6/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 7/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 8/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 9/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
| 10/10 |6.726e+00|7.676e+00|6.726e+00|7.676e+00|
|--------------------------------------------------|
============================ nnodely Training Time =============================
Total time of Training: 0.4841020107269287
================================================================================
finale state: {}