- Descrizione
- Membri del Gruppo
- Dipendenze
- Struttura del Repository
- Organizzazione
- Strutture Principali
- Metodi Principali
- Esempio di Utilizzo
Questo repository ha lo scopo di simulare una serie di possibili errori in una Spiking Neural Network
e di studiarne la resilienza. La serie di errori che
vengono simulati e i vari componenti coinvolti sono indicati in seguito.
Il repository prevede una possibile implementazione di una Spiking Neural Network
ma non prevede il supporto per la fase di training della rete, solo quella
di esecuzione.
- Andrea Sillano s314771
- Lara Moresco s320153
- Davide Palatroni s314819
Rust
(versione 1.56.1)Cargo
(versione 1.56.0)rand
(versione 0.8.5)cli-table
(versione 0.4)strip-ansi-escapes
(versione 0.2.0)
src/
contiene il codice sorgente della libreriaerror_handling/
contiene tutta la simulazione dell'errore sui componentimodels/
contiene le specifiche implementazioni dei modelli (in questo caso soloLIFNeuron
)print_report/
contiene tutte le informazioni relative alla stampa e al calcolo delle statistichesnn/
contiene l'implementazione generica della SNN
La libreria è organizzata come segue:
Il modulo Builder
permette di creare la struttura della rete con i rispettivi layers, neuroni per ciascun layer, i corrispettivi pesi tra neuroni dello stesso layer e tra layer diversi. In particolare il modulo
SnnBuilder
permette di allocare staticamente una Spike Neural Network
prendendo per ciascun layer un vettore statico di neuroni, uno di pesi e un altro di pesi tra i vari layer. La libreria può controllare
la correttezza della struttura della rete a compile time, ma questo implica che tutte le strutture di rete sono allocate nello Stack (Non adatta a reti molto grandi).
Il modulo Network
permette di eseguire la rete dato un determinato input. In particolare Snn
viene creato da SnnBuilder
e permette di processare un dato input attraverso il metodo process()
.
Come SnnBuilder
, Snn
riceve l'input come un vettore statico di inpulsi e produce come output un vettore dinamico di inpulsi. La correttezza dell'input può essere controllata a compile-time.
Il modulo Error Handling
permette di simulare uno tra gli errori richiesti sulla rete. All'interno di esso vi è stata inserita un enum che specifica il tipo di errore da simulare. Le possibilità offerte dalla libreria sono:
ErrorType::Stuck0
: simula loStuck-At-0
, ovvero il bit rimane fisso a 0, anche se richiesto il contrario;ErrorType::Stuck1
: simula loStuck-At-1
, ovvero il bit rimane fisso a 1, anche se richiesto il contrario;ErrorType::Flip
: simula iltransient bit flip
, ovvero il valore del bit viene invertito;ErrorType::None
: simula il corretto funzionamento della rete, senza nessun errore; Tra tali errori, loStuck-At-X
viene applicato forzando il bit al valore definito (0 oppure 1) per tutta la durata dell'inferenza mentre iltransient bit flip
ha validità solo in uno specifico istante di tempo ed eventuali nuove scritture non subiscono tale errore.
Le strutture su cui è possbile studiarne il comportamento sono:
Potenziale di Soglia
Potenziale di Membrana
;Pesi
, che possono essereIntra-Weights
, ovvero i pesi tra due neuroni appartenenti allo stesso layer, oppureExtra-Weights
, ovvero pesi tra due neuroni appartenenti a due layer diversiBlocchi Elaborativi
, che a loro volta possono essere:Adder
, che simula il sommatore;Multiplier
, che simula il moltiplicatore e il divisore; l'errore simulato sui componenti hardware può riguardare l'input (solo uno oppure entrambi) oppure l'output;
La libreria provvede le seguenti strutture:
LIFNeuron
rappresenta il neurone per il modelloLeaky Integrate and Fire
e può venir utilizzato per creare unLayer
di neuroni:
pub struct LIFNeuron{
/* campi costanti */
v_th: f64, /* potenziale di soglia */
v_rest: f64, /* potenziale di riposo */
v_reset: f64, /* potenziale di reset */
tau: f64,
d_t: f64, /* intervallo di tempo tra due istanti successivi */
/*campi mutabili*/
v_mem: f64, /* potenziale di membrana */
t_s: u64 /* ultimo istante di tempo in cui ha ricevuto almeno un impulso */
}
Layer
rappresenta uno strato di neuroni, viene utilizzato per costruire i layer dellaSNN
.
pub struct Layer<N: Neuron+Clone+'static>{
neurons: Vec<N>, /* neuroni del layer */
weights: Vec<Vec<f64>>, /* pesi tra i neuroni di questo layer con quelli del layer precedente */
intra_weights: Vec<Vec<f64>>, /* pesi tra i neuroni dello stesso layer */
prev_output: Vec<u8>, /* impulsi di output al precedente istante */
}
Evento
rappresenta un evento di uno strato di neuroni che si attiva ad un determinato istante di tempo. Incapsula gli impulsi che passano attraverso la rete.
pub struct Evento{
ts: u64, /* istante di tempo in cui viene generato l'output */
spikes: Vec<u8>, /* vettore che contiene tutti gli output */
}
SNN
rappresenta laSpike Neural Network
composta da un vettore diLayer
pub struct SNN<N: Neuron + Clone + 'static, const SNN_INPUT_DIM: usize, const SNN_OUTPUT_DIM: usize> {
layers: Vec<Arc<Mutex<Layer<N>>>>
}
Processor
è l'oggetto che ha il compito di gestire i thread dei layer e processare gli impulsi di input
pub struct Processor { }
SNNBuilder
rappresenta il builder per unaSNN
pub struct SnnBuilder<N: Neuron+Clone+Debug+'static>{
params: SnnParams<N>
}
pub struct SnnParams<N: Neuron+Clone+Debug+'static>{
neurons: Vec<Vec<N>>, /* neuroni per ciascun layer */
extra_weights: Vec<Vec<Vec<f64>>>, /* pesi (positivi) tra i vari layer */
intra_weights: Vec<Vec<Vec<f64>>>, /* pesi (negativi) all'interno dello stesso layer */
}
InfoTable
rappresenta la struttura necessaria per generare il report sulla resilienza. Immagazzina tutte le informazioni riguardo agli errori generati e come influiscono sullaSNN
in questione.
pub struct InfoTable{
layers: Vec<usize>,
neurons: Vec<usize>,
components: Vec<usize>,
bits: Vec<usize>,
error_type: Vec<usize>,
accuracy: Vec<f64>,
counter: i32
}
-
Adder
simula il componente che opera le operazioni di addizione e sottrazione in un sistema di elaborazionepub struct Adder{ error:i32, position: u8, input: Option<(i32,i32)> }
-
Multiplier
simula il componente che opera le operazioni di moltiplicazione e divisione in un sistema di elaborazione
pub struct Multiplier{
error:i32,
position: u8,
input: Option<(i32,i32)>
}
La libreria contiene i seguenti metodi principali:
-
-
Metodi di
SnnBuilder
:- new():
pub fn new() -> Self
crea un nuovo
SnnBuilder
- add_weight():
pub fn add_weight<const NUM_NEURONS: usize, const INPUT_DIM: usize >(&mut self, weights:[[f64; INPUT_DIM]; NUM_NEURONS]) -> &mut SnnBuilder<N>
aggiunge i pesi dal precedente layer al nuovo layer
- add_neurons():
pub fn add_neurons<const NUM_NEURONS: usize>(&mut self, neurons: [N; NUM_NEURONS]) -> &mut SnnBuilder<N>
aggiunge neuroni al layer corrente
- add_intra_weight():
pub fn add_intra_weights<const NUM_NEURONS: usize>(&mut self, intra_weights: [[f64; NUM_NEURONS]; NUM_NEURONS]) -> &mut SnnBuilder<N>
aggiunge i pesi tra i vari neuroni dello stesso layer
- build():
pub fn build<const INPUT_DIM: usize, const OUTPUT_DIM:usize>(&mut self, components: &Vec<i32>, error_type: i32, info_table: &mut InfoTable) -> SNN<N, { INPUT_DIM }, { OUTPUT_DIM }>
costruisce la
SNN
dalle informazioni raccolte fino a quel punto dalSnnBuilder
. Il parametroerror_type
serve per forzare un errore specifico all'interno della rete.
-
-
- Metodi di
Snn
:- process():
processa gli impulsi di input passati come parametri e ritorna gli impulsi di output della retepub fn process<const SPIKES_DURATION: usize>(&mut self, input_spikes: &[[u8; SNN_INPUT_DIM]; SPIKES_DURATION]) -> [[u8; SNN_OUTPUT_DIM]; SPIKES_DURATION]
- Metodi di
-
- Metodi di
Error Handling
:- embed_error():
restituisce il valore di errore difn embed_error(variable:f64, error:ErrorType, info_table: &mut InfoTable)->f64
variable
dopo aver subito un errore di tipoerror
.
- Metodi di
L'esempio seguente mostra come allocare staticamente una Spiking Neural Network
usando SnnBuilder
, e come eseguirlo avendo
un output di 3 instanti per neurone.
let mut binding = SnnBuilder::new();
let builder = binding.add_layer().add_weight([
[0.1, 0.2, 0.5],
[0.3, 0.4, 0.2],
[0.5, 0.6, 0.1]
]).add_neurons([
LIFNeuron::new(0.03, 0.05, 0.1, 1.0, 1.0),
LIFNeuron::new(0.05, 0.05, 0.1, 1.0, 1.0),
LIFNeuron::new(0.09, 0.05, 0.1, 1.0, 1.0),
]).add_intra_weights([
[0.0, -0.25, -0.3],
[-0.10, 0.0, -0.3],
[-0.1, -0.3, 0.0]
])
.add_layer()
.add_weight([
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6]
]).add_neurons([
LIFNeuron::new(0.07, 0.04, 0.4, 1.0, 1.0),
LIFNeuron::new(0.3, 0.01, 0.4, 1.0, 1.0),
]).add_intra_weights([
[0.0, -0.25],
[-0.10, 0.0]
]).add_layer().add_weight([
[0.1, 0.2],
[0.3, 0.4],
[0.5, 0.6]
]).add_neurons([
LIFNeuron::new(0.03, 0.01, 0.1, 1.0, 1.0),
LIFNeuron::new(0.05, 0.03, 0.2, 1.0, 1.0),
LIFNeuron::new(0.09, 0.06, 0.4, 1.0, 1.0),
]).add_intra_weights([
[0.0, -0.25, -0.3],
[-0.10, 0.0, -0.3],
[-0.1, -0.3, 0.0]
]).add_layer()
.add_weight([
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6]
]).add_neurons([
LIFNeuron::new(0.07, 0.01, 0.2, 1.0, 1.0),
LIFNeuron::new(0.03, 0.08, 0.3, 1.0, 1.0),
]).add_intra_weights([
[0.0, -0.25],
[-0.10, 0.0]
]);
let input = [[0,1,1], [0,0,1], [1,1,1]];
/* SNN WITHOUT ANY ERROR */
let mut snn_0_error = builder.clone().build::<3,2>(&Vec::new(), -1, &mut table);
let snn_result_0_error= snn_0_error.process(&input);
/* SNN WITH ERRORS */
for _ in 0..n_faults {
let mut snn = builder.clone().build::<3,2>(&components, error_index, &mut table);
let snn_result= snn.process(&input);
}