/*rete di tre layers; layer 1 con 2 neuroni; layer 2 con 3 (ne ho provati 200) neuroni; layer 3 con 1 neurone*/
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
// Precisione dei valori
typedef double T_Precision;
// Struttura di un neurone
struct Neuron
{
T_Precision value;// Uscita del percettrone
T_Precision dEdy;//Errore del neurone
};
//struct Neuron *i_neuron, *h_neuron, *o_neuron;//variabili di tipo Neuron
// Struttura di una connessione tra due neuroni (sinapsi)
struct Synapse
{
T_Precision weight; // Peso della connessione
T_Precision dEdw;//Errore della connessione
T_Precision delta_ij;//derivata di E rispetto al peso ij (termine che va moltiplicato per eta)
};
//struct Synapse *h_synapse, *o_synapse;//variabili di tipo Synapse
//DICHIARAZIONE FUNZIONI
T_Precision max ( T_Precision r, T_Precision z);//dichiarazione funzione massimo tra due valori
T_Precision min ( T_Precision r, T_Precision z);//dichiarazione funzione minimo tra due valori
int sign (float x);//dichiarazione funzione segno
float sigmoid(float x);//dichiarazione funzione sigmoide
float d_sigmoid(float x);//dichiarazione funzione derivata della sigmoide
void run_network( struct Neuron *i_neuron, size_t input_size, //dichiarazione funzione che fa operare la rete di 3 strati,calcola le uscite di
struct Neuron *h_neuron, struct Synapse *h_synapse, size_t hidden_size, //ogni strato, un percettrone alla volta, fino a determinare le uscite
struct Neuron *o_neuron, struct Synapse *o_synapse, size_t output_size );//della rete
void init_weight( struct Synapse *synapse, size_t layer_size, size_t prev_layer_size );//dichiarazione funzione inizializzazione pesi
void init_dEdw( struct Synapse *synapse, size_t layer_size, size_t prev_layer_size );//dichiarazione funzione inizializzazione dEdw
void resilient_backpropagation( struct Neuron *i_neuron, size_t input_size,
struct Neuron *h_neuron, struct Synapse *h_synapse, struct Synapse *h_delta, size_t hidden_size,
struct Neuron *o_neuron, struct Synapse *o_synapse, struct Synapse *o_delta, size_t output_size,
const T_Precision *dx, const T_Precision *dy, size_t sn,
const T_Precision etapiu, const T_Precision etameno, const T_Precision desired_error );//dichiarazione funzione di retropropagazione dell'errore
//FINE DICHIARAZIONI FUNZIONI
int main(void)
{
// Inizializzo il generatore di numeri pseudocasuali
srand( (size_t) time( NULL ) );
// Dimensioni della rete
const size_t input_size = 2;
const size_t hidden_size = 5;
const size_t output_size = 1;
// Creo i neuroni degli strati
struct Neuron i_neuron[ input_size ];
struct Neuron h_neuron[ hidden_size];
struct Neuron o_neuron[ output_size];
// Creo le sinapsi degli strati (+ quella del bias)
struct Synapse h_synapse[ (input_size + 1) * (hidden_size) ];
struct Synapse o_synapse[ (hidden_size + 1) * (output_size) ];
struct Synapse o_delta[ (input_size + 1) * (hidden_size) ];
struct Synapse h_delta[ (hidden_size + 1) * (input_size) ];
// Iteratore
size_t i, j;
// Numero di esempi dell'insieme di addestramento
const size_t sn = 4;
//T_Precision o_delta;
//T_Precision h_delta;
// Preparo l'insieme di addestramento per l'operatore XOR//---sn*input_size
const T_Precision dx[] = { 0,0, 0,1, 1,0, 1,1 };
const T_Precision dy[] = { 0, 1, 1, 0 };//valori desiderati--- sn*output_size
const T_Precision etapiu = 1.2;
const T_Precision etameno = 0.5;
// Tasso di apprendimento
//const T_Precision eta = 0.5;
// Errore desiderato
const T_Precision desired_error = 0.0001;
// Addestro la rete neurale
resilient_backpropagation(i_neuron, input_size,
h_neuron, h_synapse, h_delta, hidden_size,
o_neuron, o_synapse, o_delta, output_size,
dx, dy, sn, etapiu, etameno, desired_error );
// Stampo l'intestazione della tabella
printf( "[x1]\t[x2]\t[y]\n" );
// Provo tutte le combinazioni degli ingressi
for ( j = 0; j <= 1; j++ )
{
for ( i = 0; i <= 1; i++ )
{
// Imposto gli ingressi
i_neuron[0].value = i;
i_neuron[1].value = j;
// Eseguo la rete neurale
run_network( i_neuron, input_size,
h_neuron, h_synapse, hidden_size,
o_neuron, o_synapse, output_size );
// Stampo gli ingressi e la rispettiva uscita
printf( "%.0f\t%.0f\t%.0f\n", i_neuron[0].value, i_neuron[1].value, o_neuron[0].value );
}
}
return 0;
}
T_Precision max ( T_Precision r, T_Precision z) //definizione funzione massimo
{
if (r >= z)
return r;
else
return z;
}
T_Precision min ( T_Precision r, T_Precision z)//definizione funzione minimo
{
if (r <= z)
return r;
else
return z;
}
int sign (float x)//definizione funzione segno
{
int sg;
if (x < 0)
{
sg = -1;
}
else if (x == 0)
{
sg = 0;
}
else
{
sg = 1;
}
return sg;
}
float sigmoid(float x)//definizione funzione sigmoide
{
float exp_value;
float return_value;
/*** Exponential calculation ***/
exp_value = exp((double) -x);
/*** Final sigmoid value ***/
return_value = 1 / (1 + exp_value);
return return_value;
}
float d_sigmoid(float x)//definizione funzione derivata della sigmoide
{
float exp_value;
float return_value;
/*** Exponential calculation ***/
exp_value = exp((double) -x);
/*** Final sigmoid value ***/
return_value = (1 / (1 + exp_value))*(1-(1 / (1 + exp_value)));
return return_value;
}
void run_network(struct Neuron *i_neuron, size_t input_size, //definizione della funzione che fa operare la rete di 3 strati,calcola le uscite di
struct Neuron *h_neuron,struct Synapse *h_synapse, size_t hidden_size, //ogni strato, un percettrone alla volta, fino a determinare le uscite
struct Neuron *o_neuron, struct Synapse *o_synapse, size_t output_size )//della rete
{
// Potenziale di attivazione
float potential;
// Iteratori
size_t j, i;
// Calcolo le uscite dello strato intermedio hidden
for ( i = 0; i < hidden_size; i++ )
{
// Azzero il potenziale di attivazione
potential = 0;
// Calcolo il potenziale di attivazione
for ( j = 0; j < input_size; j++ )
{
potential += i_neuron[j].value * h_synapse[i * ( input_size + 1 ) + j].weight;
}
// Aggiungo il valore del bias
potential += h_synapse[i * ( input_size + 1 ) + j].weight;
// Calcolo l'uscita del neurone sulla base del potenziale di attivazione
h_neuron[i].value = sigmoid( potential ); //uscita del generico neurone nello strato hidden
}
// Calcolo le uscite dello strato di uscita
for ( i = 0; i < output_size; i++ )
{
// Azzero il potenziale di attivazione
potential = 0;
// Calcolo il potenziale di attivazione
for ( j = 0; j < hidden_size; j++ )
{
potential += h_neuron[j].value * o_synapse[i * ( hidden_size + 1 ) + j].weight;
}
// Aggiungo il valore del bias
potential += o_synapse[i * ( hidden_size + 1 ) + j].weight;
// Calcolo l'uscita del neurone sulla base del potenziale di attivazione
o_neuron[i].value = sigmoid( potential );//uscita del neurone output
}
}//fine funzione (procedura) run_network
void init_weight( struct Synapse *synapse, size_t layer_size, size_t prev_layer_size )//definizione funzione inizializzazione pesi
{
// Valore casuale
T_Precision random_;
// Iteratori
size_t j, i = 0;
// Inizializzo i pesi sinaptici con dei valori casuali (inizializzo anche il peso del bias)
for (i=0 ; i < layer_size; i++ )
{
for ( j = 0; j <= prev_layer_size; j++ )
{
// Prelevo un valore casuale
random_ = rand();
// Imposto il valore del peso tra 0 e 1
synapse[i * (prev_layer_size + 1) + j].weight = 5*( sin(random_) * sin(random_));//col seno al quadrato
//ho valori tra zero e uno
}
}
}//fine definizione funzione inizializzazione pesi
void init_dEdw( struct Synapse *synapse, size_t layer_size, size_t prev_layer_size )//definizione funzione inizializzazione dEdw
{
// Valore casuale
T_Precision random_;
// Iteratori
size_t j, i = 0;
// Inizializzo i pesi sinaptici con dei valori casuali (inizializzo anche il peso del bias)
for (i=0 ; i < layer_size; i++ )
{
for ( j = 0; j <= prev_layer_size; j++ )
{
// Imposto il valore del peso tra 0 e 1
synapse[i * (prev_layer_size + 1) + j].dEdw = 0.1; //ho valori tra zero e uno
}
}
}//fine definizione funzione inizializzazione dEdw
void resilient_backpropagation(struct Neuron *i_neuron, size_t input_size,
struct Neuron *h_neuron, struct Synapse *h_synapse, struct Synapse *h_delta, size_t hidden_size,
struct Neuron *o_neuron, struct Synapse *o_synapse, struct Synapse *o_delta, size_t output_size,
const T_Precision *dx, const T_Precision *dy, size_t sn,
const T_Precision eta_piu, const T_Precision eta_meno , const T_Precision desired_error )//definizione funzione di retropropagazione dell'errore
{
// Errore della rete
T_Precision error;
// Modifica del peso sinaptico
T_Precision delta_o;
T_Precision delta_o_eta;
T_Precision delta_h;
T_Precision delta_h_eta;
//valore delta minimo
const T_Precision delta_min = 0.0025;//exp(-6);
//valore delta massimo
const T_Precision delta_max = 50;
// Contatore delle epoche
size_t epochs = 0;
// Iteratori
size_t i,j,t;//ho aggiunto k per ciclare dentro le if
// tasso di apprendimento positivo
const T_Precision etapiu = 1.2;
// tasso di apprendimento negativo
const T_Precision etameno = 0.5;
// Log di lavoro
printf( "Inizio l'addestramento (etapiu = %.2f,etameno = %.2f, errore desiderato = %f).\n\n", etapiu, etameno, desired_error );
// Inizializzo e stampo per ogni neurone i pesi sinaptici con dei valori casuali
init_weight( h_synapse, hidden_size, input_size ); //inizializzazione peso neuroni dello strato nascosto
init_weight( o_synapse, output_size, hidden_size );//inizializzazione peso neuroni dello strato output
/*
printf("pesi inizializzati dei neuroni intermedi\n\n");
for (i=0 ; i < hidden_size; i++ )
{
for ( j = 0; j <= input_size; j++ )
{
printf( "pesi_ o_syn=%f\n", h_synapse[i*(input_size+1)+j].weight );
}
}
printf("\n");
printf("pesi inizializzati dei neuroni in uscita\n\n");
for (i=0 ; i < output_size; i++ )
{
for ( j = 0; j <= hidden_size; j++ )
{
printf( "pesi_ o_syn=%f\n", o_synapse[i*(hidden_size+1)+j].weight );
}
}
*/
//inizializzo e stampo per ogni neurone i dEdw
init_dEdw( h_synapse, hidden_size, input_size ); //inizializzazione dEdw peso neuroni dello strato nascosto sono i delta zero
init_dEdw( o_synapse, output_size, hidden_size );//inizializzazione dEdw peso neuroni dello strato output sono i delta zero
/*
printf("\n");
printf("valori dEdw inizializzati dei neuroni intermedi\n\n");
for (i=0 ; i < hidden_size; i++ )
{
for ( j = 0; j <= input_size; j++ )
{
printf( "dEdw_ o_syn=%f\n", h_synapse[i*(input_size+1)+j].dEdw );
}
}
printf("\n");
printf("valori dEdw inizializzati dei neuroni in uscita\n\n");
for (i=0 ; i < output_size; i++ )
{
for ( j = 0; j <= hidden_size; j++ )
{
printf( "dEdw_ o_syn=%f\n",o_synapse[i*(hidden_size+1)+j].dEdw );
}
}
*/
// Continuo l'addestramento finché non raggiungo
// l'errore desiderato (max 50000 epoche)
do
{
// Azzero l'errore della rete (somma degli errori della rete)
error = 0.0;
// Ripeto per tutti gli esempi nell'insieme di addestramento
for ( t = 0; t < sn; t++ )
{ //primo for
// Prendo gli ingressi dall'esempio
for ( i = 0; i < input_size; i++ )
{
i_neuron[i].value = dx[t * input_size + i];//per sn=0 (0,0); sn=1 (1,0); sn=2 (0,1); sn=3 (1,1)
}
// Eseguo la rete neurale mi da i valori dei vari neuroni (le y)
run_network( i_neuron, input_size,
h_neuron, h_synapse, hidden_size,
o_neuron, o_synapse, output_size );
// CALCOLO ERRORI DEGLI STRATI
// Calcolo l'errore dello strato di uscita (in questo caso un neurone)
for ( i = 0; i < output_size; i++ )
{
// Calcolo l'errore dell'uscita dei neuroni dello strato di uscita dE/dy_i = -(D_i - Y_i); nell'esempio ErrA e ErrB
o_neuron[i].dEdy = -( dy[t * output_size + i] - o_neuron[i].value );
// Aggiungo l'errore all'errore totale della rete
error += (( o_neuron[i].dEdy )*( o_neuron[i].dEdy)) ;
}
// Calcolo l'errore dei neuroni dello strato intermedio
for ( i = 0; i < hidden_size; i++ )
{
// Azzero l'errore dei neuroni dello strato intermedio hidden
h_neuron[i].dEdy = 0;
// Calcolo l'errore dello strato intermedio dE/dz_k = SUM( dE/dy_j * dy_j/dP_j * dP_j/dz_k )
for ( j = 0; j < output_size; j++ )
{
// Calcolo l'errore dell'uscita dei neuroni dello strato intermedio; nell'esempio ErrC=deltaA*wa1 +deltaB*wb1; deltaA = ErrA * A*(1-A)
//ErrA = o_neuron[i].dEdy ; A*(1-A) = d_sigmoid( o_neuron[j].value ); wa1;wb1= o_synapse[j * ( hidden_size + 1 ) + i].weight (sono le sinapsi //provenienti dai neuroni dello strato hidden)
h_neuron[i].dEdy += o_neuron[j].dEdy *
d_sigmoid ( o_neuron[j].value ) * o_synapse[j * ( hidden_size + 1 ) + i].weight;
}
// Calcolo l'errore dell'uscita del bias
h_neuron[i].dEdy += o_neuron[j].dEdy *
d_sigmoid ( o_neuron[j].value ) * o_synapse[j * ( hidden_size + 1 ) + i].weight;
}
//FINE CALCOLO ERRORI DEGLI STRATI
//CALCOLO LE VARIAZIONI DELL ERRORE RISPETTO AI PESI
// calcolo le variazioni delta dei pesi a meno del parametro eta per i neuroni dello strato di uscita; Delta A* C; Delta A * F; DeltaA per il bias
for ( i = 0; i < output_size; i++ )
{
// Correggo il peso sinaptico
for ( j = 0; j < hidden_size; j++ )
{
// Calcolo delta_w = dE/dy_j * dy_j/dP_j * dP_j/dw_jk; nell'esempio DeltaC=ErrC*C(1-C); deltaC * G e' la variazione del peso a meno di eta; G e'l'uscita del neurone dello strato precedente
o_delta[ i *(hidden_size +1) +j ].delta_ij = o_neuron[i].dEdy *
d_sigmoid ( o_neuron[i].value ) * h_neuron[j].value;
if (o_synapse[i].dEdw * o_delta[ i *(hidden_size +1) +j ].delta_ij > 0) {
delta_o = min( (o_synapse[i].dEdw)*etapiu , delta_max);
delta_o_eta = -sign (o_delta[ i *(hidden_size +1) +j ].delta_ij ) * delta_o;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
o_synapse[i * ( hidden_size + 1 ) + j].weight += delta_o_eta;//nuovi pesi strato uscita delta w (t+1)
}
else if (o_synapse[i].dEdw * o_delta[ i *(hidden_size +1) +j ].delta_ij < 0) {
delta_o = max( (o_synapse[i].dEdw)*etameno , delta_min);
o_synapse[i * ( hidden_size + 1 ) + j].weight = (o_delta[ i *(hidden_size +1) +j ].delta_ij) - (o_synapse[i].dEdw);
o_delta[ i *(hidden_size +1) +j ].delta_ij = 0;
}
else {
delta_o_eta = -sign (o_delta[ i *(hidden_size +1) +j ].delta_ij ) * delta_o;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
o_synapse[i * ( hidden_size + 1 ) + j].weight += delta_o_eta;
}
}
// Calcolo la modifica del peso del bias delta_w = - eta * dE/dy_j * dy_j/dP_j * dP_j/dw_jk
o_delta[ i *(hidden_size +1) +j ].delta_ij = o_neuron[i].dEdy *
d_sigmoid ( o_neuron[i].value );
if ((o_synapse[i].dEdw * o_delta[ i *(hidden_size +1) + j ].delta_ij ) > 0) {
//maggiore di zero
// Calcolo la modifica del peso del bias delta_w = - eta * dE/dy_j * dy_j/dP_j * dP_j/dw_jk
delta_o = min( (o_synapse[i].dEdw)*etapiu , delta_max);
delta_o_eta = -sign (o_delta[ i *(hidden_size +1) + j ].delta_ij ) * delta_o;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
o_synapse[i * ( hidden_size + 1 ) + j].weight += delta_o_eta;//nuovi pesi strato uscita delta w (t+1)
}
else if ((o_synapse[i].dEdw * o_delta[ i *(hidden_size +1) + j ].delta_ij ) < 0) {
delta_o = max( (o_synapse[i].dEdw)*etameno , delta_min);
o_synapse[i * ( hidden_size + 1 ) + j].weight = (o_delta[ i *(hidden_size +1) +j ].delta_ij) - (o_synapse[i].dEdw);
o_delta[ i *(hidden_size +1) + j ].delta_ij = 0;
//o_neuron[i].dEdy *
//d_sigmoid ( o_neuron[i].value ) * h_neuron[j].value == 0;
}
else {
delta_o_eta = -sign (o_delta[ i *(hidden_size +1) + j ].delta_ij ) * delta_o;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
o_synapse[i * ( hidden_size + 1 ) + j ].weight += delta_o_eta;
}
}
// calcolo le variazioni delta dei pesi a meno del parametro eta per i neuroni dello strato intermedio; Delta C* G; Delta C * H; DeltaC per il bias
for ( i = 0; i < hidden_size; i++ )
{
// Correggo il peso sinaptico dei neuroni dello strato intermedio
for ( j = 0; j < input_size; j++ )
{
// Calcolo la modifica del peso sinaptico delta_w = - eta * dE/dz_k * dz_k/dP_k * dP_k/dw_ki
h_delta[ i *(input_size +1) +j ].delta_ij = h_neuron[i].dEdy *
d_sigmoid ( h_neuron[i].value ) * i_neuron[j].value;
if ((h_synapse[i].dEdw * h_delta[ i *(input_size +1) +j ].delta_ij ) > 0) {
delta_h = min( (h_synapse[i].dEdw)*etapiu , delta_max);
delta_h_eta = -sign (h_delta[ i *(input_size +1) +j ].delta_ij ) * delta_h;
h_synapse[i * ( input_size + 1 ) + j].weight += delta_h_eta;
}
else if ((h_synapse[i].dEdw * h_delta[ i *(input_size +1) +j ].delta_ij ) < 0) {
delta_h = max( (h_synapse[i].dEdw)*etameno , delta_min);
h_synapse[i * ( input_size + 1 ) + j].weight = (h_delta[ i *(input_size +1) +j ].delta_ij) - (h_synapse[i].dEdw);
h_delta[ i *(input_size +1) +j ].delta_ij = 0;
}
else {
delta_h_eta = -sign (h_delta[ i *(input_size +1) +j ].delta_ij ) * delta_h;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
h_synapse[i * ( input_size + 1 ) + j].weight += delta_h_eta;
}
// Applico la modifica al peso sinaptico w = w + delta_w
//h_synapse[i * ( input_size + 1 ) + j].weight += delta_w;
}
// Calcolo la modifica del peso del bias delta_w = - eta * dE/dz_k * dz_k/dP_k * dP_k/dw_ki
h_delta[ i *(input_size +1) +j ].delta_ij = h_neuron[i].dEdy *
d_sigmoid ( h_neuron[i].value );
if ((h_synapse[i].dEdw * h_delta[ i *(input_size +1) + j ].delta_ij ) > 0) {
//maggiore di zero
// Calcolo la modifica del peso del bias delta_w = - eta * dE/dy_j * dy_j/dP_j * dP_j/dw_jk
delta_h = min( (h_synapse[i].dEdw)*etapiu , delta_max);
delta_h_eta = -sign (h_delta[ i *(input_size +1) + j ].delta_ij ) * delta_h;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
h_synapse[i * ( input_size + 1 ) + j].weight += delta_h_eta;//nuovi pesi strato uscita delta w (t+1)
}
else if ((h_synapse[i].dEdw * h_delta[ i *(input_size +1) + j ].delta_ij ) > 0) {
delta_h = max( (h_synapse[i].dEdw)*etameno , delta_min);
h_synapse[i * ( input_size + 1 ) + j].weight = (h_delta[ i *(input_size +1) +j ].delta_ij) - (h_synapse[i].dEdw);
h_delta[ i *(input_size +1) +j ].delta_ij = 0;
//o_neuron[i].dEdy *
//d_sigmoid ( o_neuron[i].value ) * h_neuron[j].value == 0;
}
else {
delta_h_eta = -sign (h_delta[ i *(input_size +1) +j ].delta_ij ) * delta_h;
// Applico la modifica al peso sinaptico w = w + delta_w dei neuroni in uscita (uno in questo caso)
h_synapse[i * ( input_size + 1 ) + j].weight += delta_h_eta;
}
// Aggiungo la correzione del bias w = w + delta_w
//h_synapse[i * ( input_size + 1 ) + j].weight += delta_w;
}
//FINE AGGIORNAMENTO DEI PESI DEI VARI STRATI
}//fine del primo for (quello con il numero delle coppie sn)
// Calcolo l'errore quadratico medio della rete (MSE) E(x) = SUM( e^2 ) / n_samples
error /= ( input_size * sn );
//printf ("\n");
// Ogni 1000 epoche stampo il log di addestramento
if ( epochs % 1000 == 0 )
{
printf( "Epoca #%zu, MSE=%f\n", epochs, error ); //MSE errore della rete
}
/*
for (i=0 ; i < hidden_size; i++ )//strato nascosto
{
for ( j = 0; j <= input_size; j++ )
{
//h_synapse[i].dEdw = h_neuron[i].dEdy * d_sigmoid ( h_neuron[i].value ) * i_neuron[j].value;
printf( " h_syn=%f\n",h_synapse[i].dEdw * (h_neuron[i].dEdy *
d_sigmoid ( h_neuron[i].value ) * i_neuron[j].value) );
}
}
for (i=0 ; i < output_size; i++ )//strato uscita
{
for ( j = 0; j <= hidden_size; j++ )
{
//o_synapse[i].dEdw = o_neuron[i].dEdy * d_sigmoid ( o_neuron[i].value ) * h_neuron[j].value;
printf( " o_syn=%f\n",o_synapse[i].dEdw * (o_neuron[i].dEdy *
d_sigmoid ( o_neuron[i].value ) * h_neuron[j].value));
}
}
*/
// Incremento il numero delle epoche
epochs++;
} while ( error > desired_error && epochs < 50000 );//fine ciclo do-while
//printf ("\n");
// Log di lavoro
printf( "Addestramento terminato dopo %zu epoche.\n\n", epochs );
}//fine definizione funzione di retropropagazione dell'errore