From 3d54639d4c2dd51994765bfc1632f9b2145a60d9 Mon Sep 17 00:00:00 2001 From: fanasina Date: Sat, 13 Dec 2025 05:00:23 +0100 Subject: [PATCH] debug : nan, it's the learning rate too high,s try to find optimum: 0.001 --- .../src/deepQlearning/learn_to_drive.c | 101 +++- .../src/deepQlearning/learn_to_drive.h | 2 +- deepQlearn_0/src/deepQlearning/vehicle.c | 21 +- deepQlearn_0/test/is_good.c | 16 +- deepQlearn_0/test/l1aunch_is_good_m | Bin 358928 -> 358929 bytes neuron_t/Makefile | 2 +- neuron_t/src/neuron_t/neuron_t.c | 41 +- neuron_t/src/neuron_t/neuron_t.h | 20 +- neuron_t/test/is_good.c | 115 ++++- tensor_t/Makefile | 2 +- tensor_t/src/tensor_t/tensor_t.c | 77 ++- tensor_t/src/tensor_t/tensor_t.h | 1 + tensor_t/test/is_good.c | 460 +++++++++++++++++- .../y_net_neur_net/y_nnn_screen_manager.h | 1 + .../src/y_net_neur_net/y_nnn_manager.c | 23 +- .../src/y_net_neur_net/y_nnn_screen_manager.c | 1 + y_network_neural_network_/test/is_good.c | 99 ++-- ytest_t/libytest.so | Bin 940384 -> 940440 bytes 18 files changed, 834 insertions(+), 148 deletions(-) diff --git a/deepQlearn_0/src/deepQlearning/learn_to_drive.c b/deepQlearn_0/src/deepQlearning/learn_to_drive.c index 0ccf110..2381ccc 100644 --- a/deepQlearn_0/src/deepQlearning/learn_to_drive.c +++ b/deepQlearn_0/src/deepQlearning/learn_to_drive.c @@ -2,17 +2,26 @@ char *action_name[8] = {"LEFT", "CENTER", "RIGHT"}; -#define THRESHOLD_UP 10 +#define UPPER_THRESHOLD 10 +#define DIVIDER__ 1 +#define USE_THRESHOLD 0 float reLU(float x){ - if(x>THRESHOLD_UP) return THRESHOLD_UP; - if(x>0) return x; + if(x!=x){// nan + printf("nan relu "); + } +#if USE_THRESHOLD + if(x>UPPER_THRESHOLD) return UPPER_THRESHOLD; +#endif + if(x>0) return x/DIVIDER__; return 0; } float d_reLU(float x){ - if (x>THRESHOLD_UP) return 0; - if (x>0) return 1; +#if USE_THRESHOLD + if (x>UPPER_THRESHOLD) return 0; +#endif + if (x>0) return 1/DIVIDER__; return 0; } @@ -37,7 +46,16 @@ float id(float x){ return x;} float constOne(float x){return 1;} -struct networks_qlearning * create_nework_qlearning( +void tensorProdTHR_TYPE_FLOAT(tensor_TYPE_FLOAT **MM, tensor_TYPE_FLOAT *M0, tensor_TYPE_FLOAT *M1, size_t nbthread){ + return tensorProd_TYPE_FLOAT(MM,M0,M1); +} + + +void tensorContractnProdTHR_TYPE_FLOAT(tensor_TYPE_FLOAT **MM, tensor_TYPE_FLOAT *M0, tensor_TYPE_FLOAT *M1, size_t contractionNumber, size_t nbthread) { + return tensorContractnProd_TYPE_FLOAT(MM,M0,M1,contractionNumber); +} + +struct networks_qlearning * create_network_qlearning( struct config_layers * config, bool randomize, float minR, float maxR, int randomRange, size_t nb_prod_thread, @@ -55,10 +73,13 @@ struct networks_qlearning * create_nework_qlearning( copy_weight_in_networks_from_main_to_best(qnets); setup_all_layers_functions_TYPE_FLOAT(qnets->main_net, tensorContractnProdThread_TYPE_FLOAT, tensorProdThread_TYPE_FLOAT, D_L2, L2, reLU, d_reLU); + //setup_all_layers_functions_TYPE_FLOAT(qnets->main_net, tensorContractnProdTHR_TYPE_FLOAT, tensorProdTHR_TYPE_FLOAT, D_L2, L2, reLU, d_reLU); setup_all_layers_params_TYPE_FLOAT(qnets->main_net, nb_prod_thread, nb_calc_thread, learning_rate); setup_all_layers_functions_TYPE_FLOAT(qnets->target_net, tensorContractnProdThread_TYPE_FLOAT, tensorProdThread_TYPE_FLOAT, D_L2, L2, reLU, d_reLU); + //setup_all_layers_functions_TYPE_FLOAT(qnets->target_net, tensorContractnProdTHR_TYPE_FLOAT, tensorProdTHR_TYPE_FLOAT, D_L2, L2, reLU, d_reLU); setup_all_layers_params_TYPE_FLOAT(qnets->target_net, nb_prod_thread, nb_calc_thread, learning_rate); setup_all_layers_functions_TYPE_FLOAT(qnets->best_net, tensorContractnProdThread_TYPE_FLOAT, tensorProdThread_TYPE_FLOAT, D_L2, L2, reLU, d_reLU); + //setup_all_layers_functions_TYPE_FLOAT(qnets->best_net, tensorContractnProdTHR_TYPE_FLOAT, tensorProdTHR_TYPE_FLOAT, D_L2, L2, reLU, d_reLU); setup_all_layers_params_TYPE_FLOAT(qnets->best_net, nb_prod_thread, nb_calc_thread, learning_rate); // ne pas mettre fonction d'activation à la sortie , i.e: fonction identité : f(x) = x: @@ -67,12 +88,12 @@ struct networks_qlearning * create_nework_qlearning( neurons_TYPE_FLOAT *tmpBest = qnets->best_net; while(tmpMain){ if(tmpMain->next_layer == NULL){ - tmpMain->f_act = id; - tmpMain->d_f_act = constOne; - tmpTarget->f_act = id; - tmpTarget->d_f_act = constOne; - tmpBest->f_act = id; - tmpBest->d_f_act = constOne; + tmpMain->f_act = id_TYPE_FLOAT;//id; + tmpMain->d_f_act = d_id_TYPE_FLOAT; //constOne; + tmpTarget->f_act = id_TYPE_FLOAT;//id; + tmpTarget->d_f_act = d_id_TYPE_FLOAT;//constOne; + tmpBest->f_act = id_TYPE_FLOAT;//id; + tmpBest->d_f_act = d_id_TYPE_FLOAT;// constOne; } tmpMain = tmpMain->next_layer; tmpTarget= tmpTarget->next_layer; @@ -156,18 +177,18 @@ struct qlearning_params * create_qlearning_params ( ){ struct qlearning_params * qparams = malloc(sizeof(struct qlearning_params)); - qparams->gamma = gamma; - qparams->learning_rate = learning_rate ; - qparams->discount_factor = discount_factor ; + qparams->gamma = gamma; /* taux d'actualisation (discount rate): default : 0.95 */ + qparams->learning_rate = learning_rate ; /* default : 0.001 */ + qparams->discount_factor = discount_factor ; /* */ qparams->exploration_factor = exploration_factor ; qparams->nb_training_before_update_weight_in_target = nb_training_before_update_weight_in_target; qparams->number_episodes = number_episodes; qparams->factor_update_learning_rate = 0.995; - qparams->minimum_threshold_learning_rate = 0.0001 ; - qparams->factor_update_exploration_factor = 0.9995 /*0.995*/; - qparams->minimum_threshold_exploration_factor = 0.01; + qparams->minimum_threshold_learning_rate = 0.00001 ; + qparams->factor_update_exploration_factor = 0.995 /*0.995*/; + qparams->minimum_threshold_exploration_factor = 0.0001; // qparams->threshold_number_same_action = 500; @@ -250,6 +271,9 @@ void free_RL_agent(struct RL_agent *rlAgent){ #define ACCEPTABLE_REWARD 1000 +#define UPDATE_PARAMS 1 +#define UPDATE_EXPLOR_FAC 1 + void train_qlearning(struct RL_agent * rlAgent, int action //, long reward ){ @@ -263,8 +287,11 @@ void train_qlearning(struct RL_agent * rlAgent, calculate_output_by_network_neurons_TYPE_FLOAT(net_target, new_state, &next_action_value); tensor_TYPE_FLOAT * experimental_values = CREATE_TENSOR_FROM_CPY_DIM_TYPE_FLOAT(action_value->dim); + // print_neurons_msg_TYPE_FLOAT(net_main, " net_main "); getchar(); struct game_status * car_status = rlAgent->car->status; +#if UPDATE_PARAMS struct qlearning_params * qlParams = rlAgent->qlearnParams; +#endif copy_tensor_TYPE_FLOAT(experimental_values, action_value) ; //copy_tensor_TYPE_FLOAT(experimental_values, next_action_value) ; // experimental_values === Q-tab learning @@ -277,23 +304,33 @@ void train_qlearning(struct RL_agent * rlAgent, copy_tensor_TYPE_FLOAT(ttmp->target, experimental_values); while(ttmp != net_main){ calc_delta_neurons_TYPE_FLOAT(ttmp); - update_weight_neurons_TYPE_FLOAT(ttmp); + //update_weight_neurons_TYPE_FLOAT(ttmp); ttmp = ttmp->prev_layer; } - + ttmp=net_main->next_layer; + while(ttmp){ + update_weight_neurons_TYPE_FLOAT(ttmp); + ttmp = ttmp->next_layer; + } // *** - if(car_status->cumulative_reward > ACCEPTABLE_REWARD){ +#if UPDATE_PARAMS + if((car_status->cumulative_reward > ACCEPTABLE_REWARD) || (rlAgent->status->nb_episodes % 100 == 0) ){ float new_value = ( (net_main->learning_rate < qlParams->minimum_threshold_learning_rate /*0.0001*/) ? net_main->learning_rate :(net_main->learning_rate ) * qlParams->factor_update_learning_rate /*0.995*/ ); UPDATE_ATTRIBUTE_NEURONE_IN_ALL_LAYERS(TYPE_FLOAT, net_main, learning_rate, new_value); - + qlParams->learning_rate = new_value; +#if UPDATE_EXPLOR_FAC qlParams->exploration_factor = (qlParams->exploration_factor < qlParams->minimum_threshold_exploration_factor) ? qlParams->exploration_factor : qlParams->exploration_factor * qlParams->factor_update_exploration_factor ; +#endif } +#endif // free_tensor_TYPE_FLOAT(action_value); // free_tensor_TYPE_FLOAT(next_action_value); free_tensor_TYPE_FLOAT(experimental_values); } -#define MAX_SUCCESSIVE_ACTION 200 +#define SUCCESSIVE_ACTION_CHECK 1 + +#define MAX_SUCCESSIVE_ACTION 1000 int select_action(struct RL_agent * rlAgent){ //static size_t explore = 0; int action; @@ -313,15 +350,19 @@ int select_action(struct RL_agent * rlAgent){ if(proba_explor > rlAgent->qlearnParams->exploration_factor ){ action = ARG_MAX_ARRAY_TYPE_FLOAT( action_value->x, action_value->dim->rank ); //printf(" STRATEGY : action : %d , factor : %f nb_episodes : %ld \n",action,rlAgent->qlearnParams->exploration_factor, rlAgent->status->nb_episodes); - if(rlAgent->networks->nb_successive_action[action]>MAX_SUCCESSIVE_ACTION){ +#if SUCCESSIVE_ACTION_CHECK + if(rlAgent->networks->nb_successive_action[action]>MAX_SUCCESSIVE_ACTION){ rlAgent->networks->nb_successive_action[action]=0; int recAction=action; while(action==recAction){ action = xrand() % action_value->dim->rank ; //printf("debug: action=%d recAction=%d\n",action, recAction); } - write(1,"#",1); + struct qlearning_params * qlParams = rlAgent->qlearnParams; + write(1,"#",1); + qlParams->exploration_factor = (qlParams->exploration_factor < 1 ) ? qlParams->exploration_factor / qlParams->factor_update_exploration_factor : qlParams->exploration_factor ; } +#endif ////else write(1,".",1); //if(action == ARG_MIN_ARRAY_TYPE_FLOAT( action_value->x, action_value->dim->rank )) //action = xrand() % action_value->dim->rank ; @@ -333,12 +374,13 @@ int select_action(struct RL_agent * rlAgent){ //printf(" EXPLORE : action : %d , factor : %f nb_episodes : %ld \n",action,rlAgent->qlearnParams->exploration_factor, rlAgent->status->nb_episodes); ////write(1,"*",1); } +#if SUCCESSIVE_ACTION_CHECK for(int a=0;anetworks->nb_successive_action[a]=0; } (rlAgent->networks->nb_successive_action[action])++; - +#endif /* if(rlAgent->status->last_action == action){ ++(rlAgent->status->count_last_action); @@ -450,7 +492,12 @@ void* learn_to_drive(void * lrnarg){ ////pthread_create(&threadPrint, NULL, runPrint, (void*)rlAgent); // while(true){ - for(size_t index_episode = 0; (!is_ending(qlStatus)) && (index_episode < qlParams->number_episodes) ; ++index_episode){ + for(size_t index_episode = 0; + (!is_ending(qlStatus)) + //|| (car_status->cumulative_reward > 2 * ACCEPTABLE_REWARD) + //|| (index_episode < qlParams->number_episodes) + ; + ++index_episode){ reset(car); qlStatus->nb_training_after_updated_weight_in_target = 0; qlStatus->index_episode = index_episode; diff --git a/deepQlearn_0/src/deepQlearning/learn_to_drive.h b/deepQlearn_0/src/deepQlearning/learn_to_drive.h index 0aad4bd..7f9b25b 100644 --- a/deepQlearn_0/src/deepQlearning/learn_to_drive.h +++ b/deepQlearn_0/src/deepQlearning/learn_to_drive.h @@ -99,7 +99,7 @@ struct RL_agent { }; -struct networks_qlearning * create_nework_qlearning( +struct networks_qlearning * create_network_qlearning( struct config_layers * config, bool randomize, float minR, float maxR, int randomRange, size_t nb_prod_thread, diff --git a/deepQlearn_0/src/deepQlearning/vehicle.c b/deepQlearn_0/src/deepQlearning/vehicle.c index e768aa3..f53a376 100644 --- a/deepQlearn_0/src/deepQlearning/vehicle.c +++ b/deepQlearn_0/src/deepQlearning/vehicle.c @@ -7,7 +7,11 @@ //#define CENTER 1 //#define RIGHT 2 -#define LIMIT_DISTANCE ((float)((SUBDIVISION-1)/10))/SUBDIVISION +#define MAXDISTANCE 49 +#define DIVISER 10 + +//#define LIMIT_DISTANCE ((float)((SUBDIVISION-1)/10))/SUBDIVISION +#define LIMIT_DISTANCE ((float)((int)(SUBDIVISION/10)-1)/SUBDIVISION) #define REWARD_STOP -1000 #define REWARD_CONTINUE 100 @@ -420,9 +424,12 @@ float distance2_coordinate(coordinate *c0, coordinate *c1){ diStep_sensor->x[0] += step_sensor * cos(direction_radian);\ diStep_sensor->x[1] -= step_sensor * sin(direction_radian);\ }\ - dist = (distance2_coordinate(diStep_sensor, v->coord)/10/*5*/);\ + /*dist = (distance2_coordinate(diStep_sensor, v->coord)/10)*/;\ + dist = (distance2_coordinate(diStep_sensor, v->coord)/DIVISER);\ /*printf("| dist :%f | ",dist);*/\ - v->sensor->x[position] = (float)(MIN((SUBDIVISION-1),dist))/SUBDIVISION ;\ + /*v->sensor->x[position] = (float)(MIN((SUBDIVISION-1),dist))/SUBDIVISION ;*/\ + if(dist>=0) v->sensor->x[position] = (float)(MIN(MAXDISTANCE,dist))/SUBDIVISION;\ + else v->sensor->x[position] = 0;\ #if 0 @@ -581,7 +588,7 @@ void step_vehicle(struct vehicle *v, int action){ } } -#define RANDOM 0 +#define RANDOM 1 void reset(struct vehicle *v){ //static bool init = true; @@ -615,11 +622,11 @@ void reset(struct vehicle *v){ #endif #if RANDOM random = xrand() % 50; - v->direction = 80 - random ; + //v->direction = 80 - random ; //v->direction = 115 - random ; - // v->direction = random - 25 ; + v->direction = random - 25 ; #else - v->direction = 70; //-90; + v->direction = 80; //-90; #endif v->speed = SPEED; read_sensor(v); diff --git a/deepQlearn_0/test/is_good.c b/deepQlearn_0/test/is_good.c index eb89988..b723bf3 100644 --- a/deepQlearn_0/test/is_good.c +++ b/deepQlearn_0/test/is_good.c @@ -458,7 +458,7 @@ TEST(first_learn_vehicle_rev50_8){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.001; // 0.00001 /*0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -622,7 +622,7 @@ TEST(first_learn_vehicle_50__9){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.00001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -792,7 +792,7 @@ TEST(first_learn_vehicle_50__10){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.00001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -964,7 +964,7 @@ copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0; /* 0.000001*/ /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1143,7 +1143,7 @@ copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.0000001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1265,7 +1265,7 @@ TEST(first_learn_vehicle13){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1375,7 +1375,7 @@ TEST(first_learn_vehicle){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1486,7 +1486,7 @@ TEST(first_learn_vehicle){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, diff --git a/deepQlearn_0/test/l1aunch_is_good_m b/deepQlearn_0/test/l1aunch_is_good_m index 274dc8f53b0162918fcc3addeb6d8dce6c00f945..f7a14c2a1bb1d73b34e98057f59a12d31726b405 100755 GIT binary patch delta 27 jcmbR6MRej9(S{br7N!>F7M3lnpB))XwtseHm9+o>pU4Vz delta 25 hcmbREMRdX!(S{br7N!>F7M3lnpB=Ylearning_rate=(nr->initial_learning_rate)*power_##type((nr->decay_rate),(1+(nr->iteration_step))/(nr->drop_rate));\ }\ -\ +type id_##type(type x){ return x;}\ +type d_id_##type(type x){ return 1;}\ void setup_learning_rate_params_neurons_##type(neurons_##type *base,type initial_learning_rate, type decay_rate, size_t drop_rate, void (*update_learning_rate)(neurons_##type *)){\ while(base){\ base->initial_learning_rate = initial_learning_rate;\ @@ -123,10 +124,13 @@ void calc_delta_neurons_##type(neurons_##type *nr){\ /*decrement_dim_var(temp_w_d->dim);*/\ \ if(nr->nb_calc_thread < 2){\ - for(size_t i = 0; i<(nr->net)->dim->rank; ++i)\ + for(size_t i = 0; i<(nr->net)->dim->rank; ++i){\ + if(temp_w_d->x[i]!=temp_w_d->x[i]) printf("debug: temp_w_d[%ld]=nan ",i);\ + if((nr->net)->x[i] != (nr->net)->x[i]) printf("debug : (nr->net)->x[%ld] = nan ",i) ;\ (nr->delta_out)->x[i]=(nr->d_f_act)((nr->net)->x[i]) * temp_w_d->x[i] ;\ + if((nr->delta_out)->x[i] != (nr->delta_out)->x[i] ) printf("debug: (nr->delta_out)->x[%ld]=nan ",i);\ /*print_tensor_msg_##type(nr->delta_out," nr delta_out calc 1 core hidden delta_out");\ - */\ + */}\ }else{\ update_4tensor_func_##type(nr->delta_out, nr->net, temp_w_d,\ funcalc_delta_hidden_out_##type , \ @@ -140,6 +144,8 @@ void calc_delta_neurons_##type(neurons_##type *nr){\ }\ \ type func_only_weight_in_##type(type w0, type w1, type scalar){\ + if(w0 != w0) printf("debug: w0=nan ");\ + if(w1 != w1) printf("debug: w1=nan ");\ return w0 - scalar * w1;\ }\ void only_update_weight_neurons_##type(neurons_##type *nr){\ @@ -703,6 +709,7 @@ void print_data_set_msg_##type(data_set_##type *ds, char *msg){\ size_t learning_online_neurons_##type(neurons_##type *base, data_set_##type *dataset, bool (*condition)(type,size_t)){\ neurons_##type *tmp=NULL, *ttmp;\ size_t nbreps=0;\ + /*char strNbreps[128];*/\ do{\ for(size_t i=0; isize; ++i){\ init_copy_in_out_networks_from_tensors_##type(base, dataset->input[i],dataset->target[i]);\ @@ -714,11 +721,18 @@ size_t learning_online_neurons_##type(neurons_##type *base, data_set_##type *dat }\ while(ttmp != base){\ calc_delta_neurons_##type(ttmp);\ - update_weight_neurons_##type(ttmp);\ + /*update_weight_neurons_##type(ttmp);*/\ ttmp = ttmp->prev_layer;\ }\ + ttmp = base->next_layer;\ + while(ttmp){\ + update_weight_neurons_##type(ttmp);\ + ttmp = ttmp->next_layer;\ + }\ }\ nbreps += (dataset->size);\ + /*sprintf(strNbreps, " base %ld ",nbreps);\ + print_neurons_msg_##type(base, strNbreps ); getchar();*/\ }while(!condition(error_out_##type(base), nbreps));\ \ \ @@ -730,8 +744,9 @@ size_t learning_online2_neurons_##type(neurons_##type *base, data_set_##type *da size_t nbreps=0;\ type err=0;\ bool ending=false;\ + /*char strNbreps[128];*/\ do{\ - for(size_t i=0; isize && !ending; ++i){\ + for(size_t i=0; isize /*&& !ending*/; ++i){\ init_copy_in_out_networks_from_tensors_##type(base, dataset->input[i],dataset->target[i]);\ tmp=base->next_layer;\ while(tmp){\ @@ -744,20 +759,23 @@ size_t learning_online2_neurons_##type(neurons_##type *base, data_set_##type *da /*update_weight_neurons_##type(ttmp);\ */ttmp = ttmp->prev_layer;\ }\ - tmp = ttmp->next_layer;\ + tmp = base/*ttmp*/->next_layer;\ while(tmp){\ update_weight_neurons_##type(tmp);\ tmp = tmp->next_layer;\ }\ - err = ABSMAX(err,error_out_##type(base));\ + /*if(i%20==0){err = error_out_##type(base);} else err = ABSMAX(err,error_out_##type(base));\ + */err = error_out_##type(base);\ ending = condition(err, ++nbreps);\ + /*sprintf(strNbreps, " base %ld ",nbreps );\ + print_neurons_msg_##type(base, strNbreps ); getchar();*/\ }\ \ }while(!ending);\ \ \ - printf(" ### reps : %ld, err:%f \n",nbreps,err);\ - return nbreps;\ + /*printf(" ### reps : %ld, err:%f \n",nbreps,err);\ + */return nbreps;\ }\ \ neurons_##type * calculate_output_by_network_neurons_##type(neurons_##type *base, tensor_##type *input, tensor_##type **output_link){\ @@ -804,8 +822,8 @@ void print_predict_by_network_with_error_neurons_##type(neurons_##type *base, te }\ \ \ - printf(" error : %f\n", error_out_##type(base));\ - print_tensor_msg_##type(input,"from input:");\ + /*printf(" error : %f\n", error_out_##type(base));\ + */print_tensor_msg_##type(input,"from input:");\ \ }\ \ @@ -1021,3 +1039,4 @@ size_t learning_cloneuronset_##type(cloneuronset_##type *clnrnst, data_set_##typ GEN_NEURONS_F_(TYPE_FLOAT) GEN_NEURONS_F_(TYPE_DOUBLE) +GEN_NEURONS_F_(TYPE_L_DOUBLE) diff --git a/neuron_t/src/neuron_t/neuron_t.h b/neuron_t/src/neuron_t/neuron_t.h index a172661..77595ff 100644 --- a/neuron_t/src/neuron_t/neuron_t.h +++ b/neuron_t/src/neuron_t/neuron_t.h @@ -60,6 +60,8 @@ struct func_act_##type {\ void do_not_update_learnig_rate_##type(neurons_##type *N);\ void time_based_update_learning_rate_##type(neurons_##type *nr);\ void step_based_update_learning_rate_##type(neurons_##type *nr);\ +type id_##type(type x);\ +type d_id_##type(type x);\ void setup_learning_rate_params_neurons_##type(neurons_##type *base,type initial_learning_rate, type decay_rate, size_t drop_rate, void (*update_learning_rate)(neurons_##type *));\ /*void calc_net_neurons_##type(neurons_##type *nr);*/\ void calc_out_neurons_##type(neurons_##type *nr);\ @@ -125,6 +127,7 @@ size_t learning_cloneuronset_##type(cloneuronset_##type *clnrnst, data_set_##typ GEN_NEURON_(TYPE_FLOAT) GEN_NEURON_(TYPE_DOUBLE) +GEN_NEURON_(TYPE_L_DOUBLE) #define UPDATE_ATTRIBUTE_NEURONE_IN_ALL_LAYERS(type, neuronVar, attribute, value) \ @@ -164,21 +167,24 @@ GEN_NEURON_(TYPE_DOUBLE) free(vmsg);\ }while(0);\ -#define BASH_PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(type, bash_arg, neuronVar, attribute, msg)\ +#define BASH_PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(type, bash_arg, neuronVar, attribute, msg, putIndexTens)\ do{\ neurons_##type *tmpn = neuronVar;\ char *vmsg=malloc(strlen(msg)+70);\ - size_t i=0;\ - size_t lenVMG=0;\ + /*size_t i=0;\ + size_t lenVMG=0;*/\ size_t lenBSH_T=0;\ while(tmpn){\ - lenVMG = sprintf(vmsg,"%s layer %ld",msg,i++);\ - BASH_WRITE_IF_EXIST(bash_arg,vmsg,lenVMG);\ + /*lenVMG = sprintf(vmsg,"%s layer %ld",msg,i++);\ + BASH_WRITE_IF_EXIST(bash_arg,vmsg,lenVMG);*/\ if(tmpn->attribute){\ char *bashSTR=NULL;\ - lenBSH_T=sprint_tensor_##type(&bashSTR, tmpn->attribute, true);\ - BASH_WRITE_IF_EXIST(bash_arg,bashSTR,lenBSH_T);\ + lenBSH_T=sprint_tensor_##type(&bashSTR, tmpn->attribute, putIndexTens);\ + if(tmpn/*->next_layer==NULL*/){\ + BASH_WRITE_IF_EXIST(bash_arg,bashSTR,lenBSH_T);\ + }\ if(bashSTR) free(bashSTR);\ + /*if(lenBSH_T==0) getchar();*/\ }\ tmpn = tmpn->next_layer;\ }\ diff --git a/neuron_t/test/is_good.c b/neuron_t/test/is_good.c index efd26ec..ac4e5f4 100644 --- a/neuron_t/test/is_good.c +++ b/neuron_t/test/is_good.c @@ -38,6 +38,12 @@ float df(float x){ return exp(-x)/ ((1+exp(-x)) * (1+exp(-x))); } +float __id_(float x){ + return x; +} +float d__id_(float x){ + return 1; +} TEST(init_One){ //endian=false; @@ -94,7 +100,7 @@ TEST(data_set_from_file){ #define epsilon 0.0001 bool cond(float e, size_t nbreps){ - if (nbreps > 20000) return true; + if (nbreps > 2000) return true; if ((e-epsilon)) return true; return false; } @@ -106,6 +112,7 @@ TEST(learning_first){ print_data_set_msg_TYPE_FLOAT(ds,"data"); neurons_TYPE_FLOAT *bn=NULL, *tmp ; setup_networks_OneD_TYPE_FLOAT(&bn, (size_t[]){2,4,1},3,false,0,1,5000); /* 2 input , 1 target; 1 hidden layer with 5 neurons */ + //setup_networks_OneD_TYPE_FLOAT(&bn, (size_t[]){2,4,1},3,true,0,1,5000); /* 2 input , 1 target; 1 hidden layer with 5 neurons */ setup_all_layers_functions_TYPE_FLOAT(bn, tensorContractnProdThread_TYPE_FLOAT, @@ -115,11 +122,23 @@ TEST(learning_first){ f, df); - setup_all_layers_params_TYPE_FLOAT(bn, 5, 1 , 0.1); + setup_all_layers_params_TYPE_FLOAT(bn, 5/*5*/, 3/*1*/ , 0.5); + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } + + LOG("%s","setup done\n"); + print_neurons_msg_TYPE_FLOAT(bn,"bn before "); size_t reps = learning_online_neurons_TYPE_FLOAT(bn,ds,cond); + print_neurons_msg_TYPE_FLOAT(bn,"bn after "); //char msg[256]; for(size_t i=0; isize; ++i){ @@ -136,16 +155,16 @@ TEST(learning_first){ */ } + LOG("reps = %ld error=%f\n",reps, error_out_TYPE_FLOAT(bn)); free_data_set_TYPE_FLOAT(ds); free_neurons_TYPE_FLOAT(bn); - LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; } -TEST(learning_second_PRINT){ +TEST(learning_second_PRINT){ endian=false; bool rec_randomizeInitWeight = randomizeInitWeight; randomizeInitWeight =false; @@ -162,8 +181,15 @@ TEST(learning_second_PRINT){ f, df); - setup_all_layers_params_TYPE_FLOAT(bn, 5, 3 , 0.1); - + setup_all_layers_params_TYPE_FLOAT(bn, 5, 1 , 0.4); + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } size_t reps = learning_online2_neurons_TYPE_FLOAT(bn,ds,cond); @@ -214,8 +240,16 @@ TEST(learning_withconfig2){ f, df); - setup_all_layers_params_TYPE_FLOAT(bn, 5, 1 , 0.1); - + setup_all_layers_params_TYPE_FLOAT(bn, 1, 1 , 0.5); + + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } size_t reps = learning_online2_neurons_TYPE_FLOAT(bn,ds,cond); @@ -230,6 +264,8 @@ TEST(learning_withconfig2){ free_data_set_TYPE_FLOAT(ds); free_neurons_TYPE_FLOAT(bn); + + free_config_layers(pconf); LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; @@ -255,8 +291,15 @@ TEST(learning_cloneuroneset){ f, df); - setup_all_layers_params_TYPE_FLOAT(bn, 5, 1 , 0.1); - + setup_all_layers_params_TYPE_FLOAT(bn, 1, 1 , 0.5); + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } //print_neurons_msg_TYPE_FLOAT(bn,"before create clones"); cloneuronset_TYPE_FLOAT *clnrnst = create_cloneuronset_from_base_conf_TYPE_FLOAT(bn, pconf, 3); @@ -308,7 +351,14 @@ TEST(learning_cloneuroneset_LEARN_RATE){ size_t dropRate = 100; // setup_learning_rate_params_neurons_TYPE_FLOAT(bn, initRate, decayRate, dropRate, time_based_update_learning_rate_TYPE_FLOAT); setup_learning_rate_params_neurons_TYPE_FLOAT(bn, initRate, decayRate, dropRate, step_based_update_learning_rate_TYPE_FLOAT); - + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } //print_neurons_msg_TYPE_FLOAT(bn,"before create clones"); cloneuronset_TYPE_FLOAT *clnrnst = create_cloneuronset_from_base_conf_TYPE_FLOAT(bn, pconf, 3); @@ -331,7 +381,6 @@ TEST(learning_cloneuroneset_LEARN_RATE){ LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; - } TEST(copy_weight_in_neurons){ @@ -356,7 +405,14 @@ TEST(copy_weight_in_neurons){ df); setup_all_layers_params_TYPE_FLOAT(bn, 5, 1 , 0.1); - + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } size_t reps = learning_online2_neurons_TYPE_FLOAT(bn,ds,cond); @@ -396,6 +452,7 @@ TEST(copy_weight_in_neurons){ LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; + free_config_layers(pconf); } @@ -435,7 +492,14 @@ TEST(Extract_weight_in_neurons){ df); setup_all_layers_params_TYPE_FLOAT(cpyn, 5, 1 , 0.1); - + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, cpyn, weight_in, ".ff_bn_weight_in__toExtract.txt") // copy_weight_in_neurons_TYPE_FLOAT(cpyn, bn); EXPORT_TO_FILE_TENSOR_ATTRIBUTE_IN_NNEURONS(TYPE_FLOAT, bn, weight_in, ".ff_bn_weight_in__toExtract___exp.txt") @@ -463,6 +527,8 @@ TEST(Extract_weight_in_neurons){ LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; + + free_config_layers(pconf); } @@ -492,7 +558,14 @@ TEST(Extract_EXPORT_weight_in_neurons){ df); setup_all_layers_params_TYPE_FLOAT(bn, 5, 1 , 0.1); - + neurons_TYPE_FLOAT *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=__id_; + ttmp->d_f_act=d__id_; + } + ttmp=ttmp->next_layer; + } size_t reps = 1;// learning_online2_neurons_TYPE_FLOAT(bn,ds,cond); EXPORT_TO_FILE_TENSOR_ATTRIBUTE_IN_NNEURONS(TYPE_FLOAT, bn, weight_in, ".ff_bn_weight_in__toCMP__.txt") @@ -534,6 +607,8 @@ TEST(Extract_EXPORT_weight_in_neurons){ LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; + + free_config_layers(pconf); } @@ -596,7 +671,14 @@ TEST(Extract_EXPORT_weight_in_neurons_double){ doubledf); setup_all_layers_params_TYPE_DOUBLE(cpyn, 5, 1 , 0.1); - + neurons_TYPE_DOUBLE *ttmp=bn; + while(ttmp){ + if(ttmp->next_layer == NULL){ + ttmp->f_act=id_TYPE_DOUBLE; + ttmp->d_f_act=d_id_TYPE_DOUBLE; + } + ttmp=ttmp->next_layer; + } // EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_DOUBLE, cpyn, weight_in, ".ff_bn_weight_in__toExtract.txt") // copy_weight_in_neurons_TYPE_DOUBLE(cpyn, bn); @@ -623,6 +705,7 @@ TEST(Extract_EXPORT_weight_in_neurons_double){ LOG("reps = %ld\n",reps); randomizeInitWeight = rec_randomizeInitWeight; + free_config_layers(pconf); } diff --git a/tensor_t/Makefile b/tensor_t/Makefile index 5620869..96509d7 100644 --- a/tensor_t/Makefile +++ b/tensor_t/Makefile @@ -10,7 +10,7 @@ INCLUDE_TENS=$(TENSEDIR)/src INCLUDE_PERMDIR=$(PERMDIR)/src INCLUDE_DIMDIR=$(DIMDIR)/src INCLUDE_TOOLDIR=$(TOOLDIR)/include -CFLAGS=-I$(INCLUDE_TOOLDIR) -I$(INCLUDE_PERMDIR) -I$(INCLUDE_DIMDIR) -I$(INCLUDE_TENS) -lpthread +CFLAGS=-I$(INCLUDE_TOOLDIR) -I$(INCLUDE_PERMDIR) -I$(INCLUDE_DIMDIR) -I$(INCLUDE_TENS) -lpthread #SRC_DIR=$(ROOT_DIR)/src #SRC=$(wildcard */*/*.c) diff --git a/tensor_t/src/tensor_t/tensor_t.c b/tensor_t/src/tensor_t/tensor_t.c index 461d673..9103bfc 100644 --- a/tensor_t/src/tensor_t/tensor_t.c +++ b/tensor_t/src/tensor_t/tensor_t.c @@ -23,6 +23,21 @@ void printArraySzt(size_t *a, size_t sz,char *msg){ printf("\n"); } +int checkContractProdTensorDim(dimension *d0, dimension *d1, ssize_t contractionNumber){ + if((d0->size-contractionNumber <0)||(d1->size-contractionNumber <0)) return 0; + if(endian){ + ssize_t beginCommonM0=d0->size-contractionNumber; + for(ssize_t i=0; iperm[beginCommonM0+i] != d1->perm[i]) return 0; + } + }else{ + ssize_t beginCommonM1=d1->size-contractionNumber; + for(ssize_t i=0; iperm[i] != d1->perm[beginCommonM1+i]) return 0; + } + } + return 1; +} /* bool isLessEqThan(long int a, long int b) { return a <= b; } bool isLessThan(long int a, long int b) { return a < b; } @@ -399,11 +414,11 @@ void print_tensor_msg_##type(tensor_##type *T,char *msg) {\ printf(" |#%ld]: %s, ",i,val);\ /*printf(" %s, ",val);*/\ free(val); val=NULL;\ - if(T->x[i] != T->x[i]){\ + /*if(T->x[i] != T->x[i]){\ printf("\nALERT NAN\n");\ char c;\ scanf("%c",&c);\ - }\ + }*/\ if(coord[begin]==(T->dim)->perm[begin]-1){\ size_t count=0;\ for(long int j=begin; cond(j,end); j = iter(j)){\ @@ -456,7 +471,12 @@ void fprint_tensor_##type(char *file_name, tensor_##type *T) {\ else break;\ }\ }\ - /*fprintf(fileWrite," [");\ + if(T->x[i] != T->x[i]){\ + printf("\nALERT NAN\n");\ + ;\ + return;\ + }\ +/*fprintf(fileWrite," [");\ for(size_t k=0; k<(T->dim)->size;++k) fprintf(fileWrite," %ld,",coord[k]);\ */val=type##_TO_STR(T->x[i]);\ fprintf(fileWrite," %s, ",val);\ @@ -540,6 +560,14 @@ size_t sprint_tensor_##type(char **tensorContent,tensor_##type *T, bool withInde (*tensorContent)[cur++]=']';\ (*tensorContent)[cur++]=' ';\ }\ + if(T->x[i] != T->x[i]){\ + /*char *nanStr="ALERT NAN";\ + for(size_t c=0;cx[i]);\ /*printf(" {%ld} %s [",i,val);*/\ (*tensorContent)[cur++]=' ';\ @@ -639,6 +667,12 @@ void tensorContractnProd_##type(tensor_##type** MM, tensor_##type *M0, tensor_## /* if (!checkMatchProdtensor(M0->dim, M1->dim, contractionNumber)) {\ prsize_tf("Deep = %d\n", contractionNumber);\ }*/\ + if(checkContractProdTensorDim(M0->dim, M1->dim, contractionNumber)==0){\ + printf("checkContractProdTensorDim %ld contractionNumber\n", contractionNumber);\ + printDebug_dimension(M0->dim, "M0 dim");\ + printDebug_dimension(M1->dim, "M1 dim");\ + getchar();\ + }\ \ size_t len0 = M0->dim->size - contractionNumber;\ size_t len1 = M1->dim->size - contractionNumber;\ @@ -727,6 +761,7 @@ void* runProd_thread_##type(void *arg){\ return 0;\ }\ \ +\ void tensorProdThread_##type(tensor_##type **MM, tensor_##type *M0, tensor_##type *M1, size_t nbthread) { \ dimension *dd; \ add_dimension(&dd, M0->dim, M1->dim); \ @@ -863,8 +898,14 @@ void* runProdContract_thread_##type(void *arg){\ M[x0,x1,x3..xl x{l+1}...xn] X M[xn,x{n-1},x{n-2}...xl y{l+1} ..ym] = M[x0,x1..xly{l+1}...y{n+m-2l}] (deep = l > 0)\ M[[i][j]]=sum_{[k]}M0[[i][k]]*M[[k][j]]*/\ \ -void tensorContractnProdThread_##type(tensor_##type** MM, tensor_##type *M0, tensor_##type *M1, size_t contractionNumber, size_t nbthread) {\ \ +void tensorContractnProdThread_##type(tensor_##type** MM, tensor_##type *M0, tensor_##type *M1, size_t contractionNumber, size_t nbthread) {\ + if(checkContractProdTensorDim(M0->dim, M1->dim, contractionNumber)==0){\ + printf("checkContractProdTensorDim %ld contractionNumber\n", contractionNumber);\ + printDebug_dimension(M0->dim, "M0 dim");\ + printDebug_dimension(M1->dim, "M1 dim");\ + getchar();\ + }\ size_t len0 = M0->dim->size - contractionNumber;\ size_t len1 = M1->dim->size - contractionNumber;\ \ @@ -962,6 +1003,15 @@ void* runPro2dContract_thread_##type(void *arg){\ M[[i][j]]=sum_{[k]}M0[[i][k]]*M[[k][j]]*/\ \ void tensorContractnPro2dThread_##type(tensor_##type** MM, tensor_##type *M0, tensor_##type *M1, size_t contractionNumber, size_t nbthread) {\ + /*if(checkContractProdTensorDim(M0->dim, M1->dim, contractionNumber)==0){\ + printf("checkContractProdTensorDim %ld contractionNumber\n", contractionNumber);\ + }*/\ + if(checkContractProdTensorDim(M0->dim, M1->dim, contractionNumber)==0){\ + printf("checkContractProdTensorDim %ld contractionNumber\n", contractionNumber);\ + printDebug_dimension(M0->dim, "M0 dim");\ + printDebug_dimension(M1->dim, "M1 dim");\ + getchar();\ + }\ \ size_t len0 = M0->dim->size - contractionNumber;\ size_t len1 = M1->dim->size - contractionNumber;\ @@ -1015,10 +1065,12 @@ void tensorContractnPro2dThread_##type(tensor_##type** MM, tensor_##type *M0, te FREE_dM_S_ ; \ }\ void tensorContractnProdNotOpt_##type(tensor_##type** MM, tensor_##type *M0, tensor_##type *M1, size_t contractionNumber) {\ - /* if (!checkMatchProdtensor(M0->dim, M1->dim, contractionNumber)) {\ - prsize_tf("Deep = %d\n", contractionNumber);\ - }*/\ -\ + if (!checkContractProdTensorDim(M0->dim, M1->dim, contractionNumber)) {\ + printf("error Deep = %ld\n", contractionNumber);\ + printDebug_dimension(M0->dim, "M0 dim");\ + printDebug_dimension(M1->dim, "M1 dim");\ + getchar();\ + }\ size_t len0 = M0->dim->size - contractionNumber;\ size_t len1 = M1->dim->size - contractionNumber;\ \ @@ -1259,8 +1311,8 @@ void parseInputOutput_withDim_to_tensors_##type(tensor_##type **Tpart1, tensor_# if(!filled1){\ ++i1;\ (*Tpart1)->x[i++] = x;\ - printf("++ x: %f, i1:%ld , rkn1: %ld\n",x,i1,ddim1->rank);\ - if(i1 == ddim1->rank){\ + /*printf("++ x: %f, i1:%ld , rkn1: %ld\n",x,i1,ddim1->rank);\ + */if(i1 == ddim1->rank){\ filled1=true;\ i1=0;\ filled2=false;\ @@ -1269,8 +1321,8 @@ void parseInputOutput_withDim_to_tensors_##type(tensor_##type **Tpart1, tensor_# if(!filled2){\ ++i2;\ (*Tpart2)->x[j++] = x;\ - printf("-----++ x: %f, i2:%ld , rknr: %ld\n",x,i2,ddim2->rank);\ - if(i2 == ddim2->rank){\ + /*printf("-----++ x: %f, i2:%ld , rknr: %ld\n",x,i2,ddim2->rank);\ + */if(i2 == ddim2->rank){\ filled2=true;\ i2=0;\ filled1=false;\ @@ -1942,3 +1994,4 @@ void update_6tensor_func_##type(tensor_##type *M0, tensor_##type *M1, \ GEN_FUNC_TENSOR(TYPE_FLOAT); GEN_FUNC_TENSOR(TYPE_DOUBLE); +GEN_FUNC_TENSOR(TYPE_L_DOUBLE); diff --git a/tensor_t/src/tensor_t/tensor_t.h b/tensor_t/src/tensor_t/tensor_t.h index 03fd9da..b16bded 100644 --- a/tensor_t/src/tensor_t/tensor_t.h +++ b/tensor_t/src/tensor_t/tensor_t.h @@ -83,6 +83,7 @@ void update_6tensor_func_##type(tensor_##type *M0, tensor_##type *M1, \ GENERATE_TENSOR_TYPE(TYPE_FLOAT); GENERATE_TENSOR_TYPE(TYPE_DOUBLE); +GENERATE_TENSOR_TYPE(TYPE_L_DOUBLE); #endif /* __TENSOR_T__H__ */ diff --git a/tensor_t/test/is_good.c b/tensor_t/test/is_good.c index cfe7e7d..7b416b7 100644 --- a/tensor_t/test/is_good.c +++ b/tensor_t/test/is_good.c @@ -510,6 +510,106 @@ TEST(SplitOne_randomInit){ } #endif +TEST(tensorProdNoOpt){ + dimension *d0=create_dim(3); + dimension *d1=create_dim(2); +#if VALGRIND_ + d0->perm[0]=5; + d0->perm[1]=2; //3; + d0->perm[2]=3; + + d1->perm[0]=2; + d1->perm[1]=3;//3; + +#else + + + d0->perm[0]=5; + d0->perm[1]=7; //3; + d0->perm[2]=12; + + d1->perm[0]=2; + d1->perm[1]=13;//3; +#endif + + tensor_TYPE_FLOAT *M0 = CREATE_TENSOR_TYPE_FLOAT(d0); + tensor_TYPE_FLOAT *M1 = CREATE_TENSOR_TYPE_FLOAT(d1); + + LOG("M0->dim->rank = %ld\n",M0->dim->rank); + LOG("M1->dim->rank = %ld\n",M1->dim->rank); + for(size_t i=0; idim->rank;++i) M0->x[i]=i*0.1 +1; + for(size_t i=0; idim->rank;++i) M1->x[i]=i*0.003 + 2; + + print_tensor_float(M0,"M0"); + print_tensor_float(M1,"M1"); + + + tensor_TYPE_FLOAT *M=NULL; + tensor_TYPE_FLOAT *Mn=NULL; + + tensorProdNotOpt_TYPE_FLOAT(&Mn,M0,M1); + + float MMM0[d0->perm[0]][d0->perm[1]][d0->perm[2]]; + long int coord3[3]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + coord3[0]=i0; coord3[1]=i1; coord3[2]=i2; + MMM0[i0][i1][i2]=M0->x[signedLineFromCoord(coord3, d0)]; + //printf("M0[%ld][%ld][%ld] = %f \n",i0,i1,i2,MMM0[i0][i1][i2]); + } + } + } + + float MMM1[d1->perm[0]][d1->perm[1]]; + long int coord2[2]; + for(long int j0=0;j0perm[0];++j0){ + for(long int j1=0;j1perm[1];++j1){ + coord2[0]=j0; coord2[1]=j1; + MMM1[j0][j1]=M1->x[signedLineFromCoord(coord2, d1)]; + } + } + //tensorProd_TYPE_FLOAT(&M,M0,M1); + float MMMm[d0->perm[0]][d0->perm[1]][d0->perm[2]][d1->perm[0]][d1->perm[1]]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + for(long int j0=0;j0perm[0];++j0){ + for(long int j1=0;j1perm[1];++j1){ + MMMm[i0][i1][i2][j0][j1]=MMM0[i0][i1][i2] * MMM1[j0][j1]; + //printf("MMMm[%ld][%ld][%ld][%ld][%ld] = %f \n",i0,i1,i2,j0,j1,MMMm[i0][i1][i2][j0][j1]); + } + } + } + } + } + //LOG("M->dim->rank = %ld\n",M->dim->rank); + + //print_tensor_float(M,"M"); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,Mn->x,Mn->dim->rank); + long int coord5[5]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + for(long int j0=0;j0perm[0];++j0){ + for(long int j1=0;j1perm[1];++j1){ + coord5[0]=i0; coord5[1]=i1;coord5[2]=i2; + coord5[3]=j0; coord5[4]=j1; + EXPECT_EQ_TYPE_FLOAT(MMMm[i0][i1][i2][j0][j1], Mn->x[signedLineFromCoord(coord5, Mn->dim)] ); + } + } + } + } + } + + print_tensor_float(Mn,"Mn"); + + free_tensor_TYPE_FLOAT(M0); + free_tensor_TYPE_FLOAT(M1); + //free_tensor_TYPE_FLOAT(M); + free_tensor_TYPE_FLOAT(Mn); +} TEST(tensorProd ){ dimension *d0=create_dim(3); dimension *d1=create_dim(2); @@ -563,7 +663,365 @@ TEST(tensorProd ){ free_tensor_TYPE_FLOAT(Mn); } -TEST(tensorContractnProd_TYPE_FLOAT ){ +TEST(tensorContractnProd_TYPE_FLOATNoOpt ){ + dimension *d0=create_dim(3); + dimension *d1=create_dim(3); +#if VALGRIND_ + d0->perm[0]=5; + d0->perm[1]=2; //3; + d0->perm[2]=3; + + d1->perm[0]=3; + d1->perm[1]=3;//3; + d1->perm[2]=8; + +#else + + + d0->perm[0]=15; + d0->perm[1]=12; //3; + d0->perm[2]=13; + + d1->perm[0]=13; + d1->perm[1]=15;//3; + d1->perm[2]=14; +#endif + + + updateRankDim(d0); + updateRankDim(d1); + + + tensor_TYPE_FLOAT *M0 = CREATE_TENSOR_TYPE_FLOAT(d0); + tensor_TYPE_FLOAT *M1 = CREATE_TENSOR_TYPE_FLOAT(d1); + + LOG("M0->dim->rank = %ld\n",M0->dim->rank); + LOG("M1->dim->rank = %ld\n",M1->dim->rank); + for(size_t i=0; idim->rank;++i) M0->x[i]=i*0.1 +1; + for(size_t i=0; idim->rank;++i) M1->x[i]=i*0.003 + 2; + + print_tensor_float(M0,"M0"); + print_tensor_float(M1,"M1"); + + tensor_TYPE_FLOAT *M=NULL; + tensor_TYPE_FLOAT *MnO=NULL; + + //tensorContractnProd_TYPE_FLOAT(&M, M0,M1,2); + tensorContractnProd_TYPE_FLOAT(&MnO, M0,M1,1); + //tensorContractnProdNotOpt_TYPE_FLOAT(&MnO, M0,M1,1); +printDebug_dimension(MnO->dim, "dim Contr 1"); + float MMM0[d0->perm[0]][d0->perm[1]][d0->perm[2]]; + long int coord3[3]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + coord3[0]=i0; coord3[1]=i1; coord3[2]=i2; + MMM0[i0][i1][i2]=M0->x[signedLineFromCoord(coord3, d0)]; + //printf("M0[%ld][%ld][%ld] = %f \n",i0,i1,i2,MMM0[i0][i1][i2]); + } + } + } + + float MMM1[d1->perm[0]][d1->perm[1]][d1->perm[2]]; + long int coord23[3]; + for(long int j0=0;j0perm[0];++j0){ + for(long int j1=0;j1perm[1];++j1){ + for(long int j2=0;j2perm[2];++j2){ + coord23[0]=j0; coord23[1]=j1; coord23[2]=j2; + MMM1[j0][j1][j2]=M1->x[signedLineFromCoord(coord23, d1)]; + } + } + } + //tensorProd_TYPE_FLOAT(&M,M0,M1); + float MMMm[d0->perm[0]][d0->perm[1]][d1->perm[1]][d1->perm[2]]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int j1=0;j1perm[1];++j1){ + for(long int j2=0;j2perm[2];++j2){ + MMMm[i0][i1][j1][j2]=0; + for(long int i2=0;i2perm[2];++i2){ + MMMm[i0][i1][j1][j2] += MMM0[i0][i1][i2] * MMM1[i2][j1][j2]; + //printf("MMMm[%ld][%ld][%ld][%ld][%ld] = %f \n",i0,i1,i2,j0,j1,MMMm[i0][i1][i2][j0][j1]); + } + } + } + } + } + //LOG("M->dim->rank = %ld\n",M->dim->rank); + + //print_tensor_float(M,"M"); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,Mn->x,Mn->dim->rank); + long int coord4[4]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int j1=0;j1perm[1];++j1){ + for(long int j2=0;j2perm[2];++j2){ + coord4[0]=i0; coord4[1]=i1; coord4[2]=j1; coord4[3]=j2; + //EXPECT_EQ_TYPE_FLOAT(MMMm[i0][i1][j1][j2], MnO->x[signedLineFromCoord(coord4, MnO->dim)] ); + + if(expected_EQ_TYPE_FLOAT(MMMm[i0][i1][j1][j2], MnO->x[signedLineFromCoord(coord4, MnO->dim)] ) == false){ + LOG("[ %ld, %ld, %ld, %ld ] [%ld]\n",i0,i1,j1,j2, + signedLineFromCoord(coord4, MnO->dim) + ); + + } + + } + } + } + } + + //print_tensor_float(M,"M"); + print_tensor_float(MnO,"MnO"); + + // for(size_t i=0;idim->rank;++i) + // EXPECT_EQ_TYPE_FLOAT(M->x[i],MnO->x[i]); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,MnO->x,MnO->dim->rank); + + //free_tensor_TYPE_FLOAT(M); + free_tensor_TYPE_FLOAT(MnO); + free_tensor_TYPE_FLOAT(M0); + free_tensor_TYPE_FLOAT(M1); + +} +TEST(tensorContractnProd_TYPE_FLOATNoOpt2 ){ + endian=true; // + dimension *d0=create_dim(3); + dimension *d1=create_dim(3); +#if VALGRIND_ + d0->perm[0]=5; + d0->perm[1]=2; //3; + d0->perm[2]=3; + + d1->perm[0]=2; + d1->perm[1]=3;//3; + d1->perm[2]=8; + +#else + + + d0->perm[0]=35; + d0->perm[1]=32; //3; + d0->perm[2]=23; + + d1->perm[0]=32; + d1->perm[1]=23;//3; + d1->perm[2]=44; +#endif + + + updateRankDim(d0); + updateRankDim(d1); + + + tensor_TYPE_FLOAT *M0 = CREATE_TENSOR_TYPE_FLOAT(d0); + tensor_TYPE_FLOAT *M1 = CREATE_TENSOR_TYPE_FLOAT(d1); + + LOG("M0->dim->rank = %ld\n",M0->dim->rank); + LOG("M1->dim->rank = %ld\n",M1->dim->rank); + for(size_t i=0; idim->rank;++i) M0->x[i]=i*0.1 +1; + for(size_t i=0; idim->rank;++i) M1->x[i]=i*0.003 + 2; + + print_tensor_float(M0,"M0"); + print_tensor_float(M1,"M1"); + + tensor_TYPE_FLOAT *M=NULL; + tensor_TYPE_FLOAT *MnO=NULL; + + //tensorContractnProd_TYPE_FLOAT(&M, M0,M1,2); + tensorContractnProdNotOpt_TYPE_FLOAT(&MnO, M0,M1,2); + + float MMM0[d0->perm[0]][d0->perm[1]][d0->perm[2]]; + long int coord3[3]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + coord3[0]=i0; coord3[1]=i1; coord3[2]=i2; + MMM0[i0][i1][i2]=M0->x[signedLineFromCoord(coord3, d0)]; + //printf("M0[%ld][%ld][%ld] = %f \n",i0,i1,i2,MMM0[i0][i1][i2]); + } + } + } + + float MMM1[d1->perm[0]][d1->perm[1]][d1->perm[2]]; + long int coord23[3]; + for(long int j0=0;j0perm[0];++j0){ + for(long int j1=0;j1perm[1];++j1){ + for(long int j2=0;j2perm[2];++j2){ + coord23[0]=j0; coord23[1]=j1; coord23[2]=j2; + MMM1[j0][j1][j2]=M1->x[signedLineFromCoord(coord23, d1)]; + } + } + } + //tensorProd_TYPE_FLOAT(&M,M0,M1); + float MMMm[d0->perm[0]][d1->perm[2]]; + for(long int i0=0;i0perm[0];++i0){ + for(long int j2=0;j2perm[2];++j2){ + MMMm[i0][j2]=0; + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + MMMm[i0][j2] += MMM0[i0][i1][i2] * MMM1[i1][i2][j2]; + //printf("MMMm[%ld][%ld][%ld][%ld][%ld] = %f \n",i0,i1,i2,j0,j1,MMMm[i0][i1][i2][j0][j1]); + } + } + } + } + //LOG("M->dim->rank = %ld\n",M->dim->rank); + + //print_tensor_float(M,"M"); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,Mn->x,Mn->dim->rank); + long int coord52[2]; + for(long int i0=0;i0perm[0];++i0){ + for(long int j2=0;j2perm[2];++j2){ + coord52[0]=i0; coord52[1]=j2; + EXPECT_EQ_TYPE_FLOAT(MMMm[i0][j2], MnO->x[signedLineFromCoord(coord52, MnO->dim)] ); + + if(expected_EQ_TYPE_FLOAT(MMMm[i0][j2], MnO->x[signedLineFromCoord(coord52, MnO->dim)] ) == false){ + LOG("[ %ld, %ld] [%ld]\n",i0,j2, + signedLineFromCoord(coord52, MnO->dim) + ); + + } + } + } + + //print_tensor_float(M,"M"); + print_tensor_float(MnO,"MnO"); + + // for(size_t i=0;idim->rank;++i) + // EXPECT_EQ_TYPE_FLOAT(M->x[i],MnO->x[i]); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,MnO->x,MnO->dim->rank); + + //free_tensor_TYPE_FLOAT(M); + free_tensor_TYPE_FLOAT(MnO); + free_tensor_TYPE_FLOAT(M0); + free_tensor_TYPE_FLOAT(M1); + +} +TEST(tensorContractnProd_TYPE_FLOATNoOpt3endianFalse ){ + endian=false; + dimension *d0=create_dim(3); + dimension *d1=create_dim(3); +#if VALGRIND_ + d0->perm[0]=5; + d0->perm[1]=2; //3; + d0->perm[2]=3; + + d1->perm[0]=4; + d1->perm[1]=2;//3; + d1->perm[2]=5; + +#else + + + d0->perm[0]=13; + d0->perm[1]=12; //3; + d0->perm[2]=35; + + d1->perm[0]=32; + d1->perm[1]=12;//3; + d1->perm[2]=13; +#endif + + + updateRankDim(d0); + updateRankDim(d1); + + + tensor_TYPE_FLOAT *M0 = CREATE_TENSOR_TYPE_FLOAT(d0); + tensor_TYPE_FLOAT *M1 = CREATE_TENSOR_TYPE_FLOAT(d1); + + LOG("M0->dim->rank = %ld\n",M0->dim->rank); + LOG("M1->dim->rank = %ld\n",M1->dim->rank); + for(size_t i=0; idim->rank;++i) M0->x[i]=i*0.1 +1; + for(size_t i=0; idim->rank;++i) M1->x[i]=i*0.003 + 2; + + print_tensor_float(M0,"M0"); + print_tensor_float(M1,"M1"); + + tensor_TYPE_FLOAT *M=NULL; + tensor_TYPE_FLOAT *MnO=NULL; + + //tensorContractnProd_TYPE_FLOAT(&M, M0,M1,2); + tensorContractnProdNotOpt_TYPE_FLOAT(&MnO, M0,M1,2); + + float MMM0[d0->perm[0]][d0->perm[1]][d0->perm[2]]; + long int coord3[3]; + for(long int i0=0;i0perm[0];++i0){ + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + coord3[0]=i0; coord3[1]=i1; coord3[2]=i2; + MMM0[i0][i1][i2]=M0->x[signedLineFromCoord(coord3, d0)]; + //printf("M0[%ld][%ld][%ld] = %f \n",i0,i1,i2,MMM0[i0][i1][i2]); + } + } + } + + float MMM1[d1->perm[0]][d1->perm[1]][d1->perm[2]]; + long int coord23[3]; + for(long int j0=0;j0perm[0];++j0){ + for(long int j1=0;j1perm[1];++j1){ + for(long int j2=0;j2perm[2];++j2){ + coord23[0]=j0; coord23[1]=j1; coord23[2]=j2; + MMM1[j0][j1][j2]=M1->x[signedLineFromCoord(coord23, d1)]; + } + } + } + //tensorProd_TYPE_FLOAT(&M,M0,M1); + float MMMm[d0->perm[0]][d1->perm[2]]; + for(long int i0=0;i0perm[0];++i0){ + for(long int j2=0;j2perm[2];++j2){ + MMMm[i0][j2]=0; + for(long int i1=0;i1perm[1];++i1){ + for(long int i2=0;i2perm[2];++i2){ + MMMm[i0][j2] += MMM0[i0][i1][i2] * MMM1[i1][i2][j2]; + //printf("MMMm[%ld][%ld][%ld][%ld][%ld] = %f \n",i0,i1,i2,j0,j1,MMMm[i0][i1][i2][j0][j1]); + } + } + } + } + //LOG("M->dim->rank = %ld\n",M->dim->rank); + + //print_tensor_float(M,"M"); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,Mn->x,Mn->dim->rank); + long int coord52[2]; + for(long int i0=0;i0perm[0];++i0){ + for(long int j2=0;j2perm[2];++j2){ + coord52[0]=i0; coord52[1]=j2; + EXPECT_EQ_TYPE_FLOAT(MMMm[i0][j2], MnO->x[signedLineFromCoord(coord52, MnO->dim)] ); + + if(expected_EQ_TYPE_FLOAT(MMMm[i0][j2], MnO->x[signedLineFromCoord(coord52, MnO->dim)] ) == false){ + LOG("[ %ld, %ld] [%ld]\n",i0,j2, + signedLineFromCoord(coord52, MnO->dim) + ); + + } + } + } + + //print_tensor_float(M,"M"); + print_tensor_float(MnO,"MnO"); + + // for(size_t i=0;idim->rank;++i) + // EXPECT_EQ_TYPE_FLOAT(M->x[i],MnO->x[i]); + + //EXPECT_ARRAY_EQ_TYPE_FLOAT(M->x,M->dim->rank,MnO->x,MnO->dim->rank); + + //free_tensor_TYPE_FLOAT(M); + free_tensor_TYPE_FLOAT(MnO); + free_tensor_TYPE_FLOAT(M0); + free_tensor_TYPE_FLOAT(M1); + +} + + + +TEST(tensorContractnProd_TYPE_FLOAT ){ dimension *d0=create_dim(3); dimension *d1=create_dim(3); #if VALGRIND_ diff --git a/y_network_neural_network_/include/y_net_neur_net/y_nnn_screen_manager.h b/y_network_neural_network_/include/y_net_neur_net/y_nnn_screen_manager.h index 061d0e0..5bc5e6f 100644 --- a/y_network_neural_network_/include/y_net_neur_net/y_nnn_screen_manager.h +++ b/y_network_neural_network_/include/y_net_neur_net/y_nnn_screen_manager.h @@ -61,6 +61,7 @@ struct arg_bash{ pthread_mutex_t *mut_bash_var; pthread_cond_t *cond_bash_var; int go_on; + int weight_net_print; }; struct arg_bash *create_arg_bash(); diff --git a/y_network_neural_network_/src/y_net_neur_net/y_nnn_manager.c b/y_network_neural_network_/src/y_net_neur_net/y_nnn_manager.c index b471a68..8d90099 100644 --- a/y_network_neural_network_/src/y_net_neur_net/y_nnn_manager.c +++ b/y_network_neural_network_/src/y_net_neur_net/y_nnn_manager.c @@ -129,10 +129,11 @@ void* runBashPrint(void *arg){ while( check_go_on_bash(bash_arg) && (new_bash_exist(bash_arg)) && check_go_on_print_params(pprint) && !is_ending(qlStatus)){ if(/*(qlStatus->nb_episodes %125 == 0) &&*/ pprint->printed){ //pthread_mutex_lock(&(pprint->mut_printed)); - pthread_mutex_lock(&(car->mut_coord)); - bash_print_vehicle_n_path(car, pprint->scale_x, pprint->scale_y,bash_arg); - pthread_mutex_unlock(&(car->mut_coord)); - + if(bash_arg->weight_net_print == 0){ + pthread_mutex_lock(&(car->mut_coord)); + bash_print_vehicle_n_path(car, pprint->scale_x, pprint->scale_y,bash_arg); + pthread_mutex_unlock(&(car->mut_coord)); + } //pthread_mutex_unlock(&(pprint->mut_printed)); ////printf("%s ",pprint->string_space); len_buf=sprintf(buf,"%s ",pprint->string_space); @@ -150,7 +151,7 @@ void* runBashPrint(void *arg){ } ////printf("\n< %5.2f > ( %s ) \n", car->direction, action_name[qlStatus->action % COUNT_ACTION]); - len_buf=sprintf(buf,"\n< %5.2f > ( %s ) \n", car->direction, action_name[qlStatus->action % COUNT_ACTION]); + len_buf=sprintf(buf,"\n< %5.2f ( %s ) \n", car->direction, action_name[qlStatus->action % COUNT_ACTION]); BASH_WRITE_IF_EXIST(bash_arg, buf, len_buf) //print_weight_in_neurons_TYPE_FLOAT(net_main, "net_main_wei"); //PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(TYPE_FLOAT, net_main, weight_in, "net_main_we_in"); @@ -158,9 +159,15 @@ void* runBashPrint(void *arg){ //PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(TYPE_FLOAT, net_target, output, "net_target_out"); //PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(TYPE_FLOAT, net_main, input, "net_main_input"); ////printf(" action : %d , factor : %f nb_episodes : %ld \n",qlStatus->action,rlAgent->qlearnParams->exploration_factor, rlAgent->status->nb_episodes); - BASH_PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(TYPE_FLOAT, bash_arg, net_main, weight_in, "net_main_we_in"); - - len_buf=sprintf(buf," action : %d , factor : %f nb_episodes : %ld \n",qlStatus->action,rlAgent->qlearnParams->exploration_factor, rlAgent->status->nb_episodes); + + if(bash_arg->weight_net_print){ + BASH_PRINT_ATTRIBUTE_TENS_IN_ALL_LAYERS(TYPE_FLOAT, bash_arg, net_target, weight_in, "net_main_we_in", false); + } + len_buf=sprintf(buf," action : %d , learning_rate: %f, factor : %f nb_episodes : %ld \n", + qlStatus->action, + rlAgent->qlearnParams->learning_rate, + rlAgent->qlearnParams->exploration_factor, + rlAgent->status->nb_episodes); BASH_WRITE_IF_EXIST(bash_arg, buf, len_buf) FOR_LIST_FORM_BEGIN(TYPE_L_INT, qlStatus->progress_best_cumul){ diff --git a/y_network_neural_network_/src/y_net_neur_net/y_nnn_screen_manager.c b/y_network_neural_network_/src/y_net_neur_net/y_nnn_screen_manager.c index e2b97ac..79a5320 100644 --- a/y_network_neural_network_/src/y_net_neur_net/y_nnn_screen_manager.c +++ b/y_network_neural_network_/src/y_net_neur_net/y_nnn_screen_manager.c @@ -116,6 +116,7 @@ struct arg_bash *create_arg_bash(){ pthread_cond_init(b_arg->cond_bash_var,NULL); b_arg->go_on=1; + b_arg->weight_net_print=0; b_arg->thread_launch=NULL; b_arg->thread_run_newbash=NULL; diff --git a/y_network_neural_network_/test/is_good.c b/y_network_neural_network_/test/is_good.c index 7e6430b..74ef01d 100644 --- a/y_network_neural_network_/test/is_good.c +++ b/y_network_neural_network_/test/is_good.c @@ -336,7 +336,7 @@ HIDE_TEST(_first_learn_vehicle_50__9){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.001 /*0.01*/ /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -364,12 +364,12 @@ struct status_qlearning *qlstatus = create_status_qlearning (); 20/*long int nb_training_before_update_weight_in_target*/, 10000/*size_t number_episodes*/ ); - +#if 0 UPDATE_ATTRIBUTE_NEURONE_IN_ALL_LAYERS(TYPE_FLOAT, nnetworks->main_net, d_f_act , df ); UPDATE_ATTRIBUTE_NEURONE_IN_ALL_LAYERS(TYPE_FLOAT, nnetworks->main_net, f_act, f ); UPDATE_ATTRIBUTE_NEURONE_IN_ALL_LAYERS(TYPE_FLOAT, nnetworks->target_net, d_f_act , df ); UPDATE_ATTRIBUTE_NEURONE_IN_ALL_LAYERS(TYPE_FLOAT, nnetworks->target_net, f_act , f ); - +#endif struct print_params *pprint = create_print_params( 12/*float scale_x*/,12 /*float scale_y*/, dly/*struct delay_params * dly_p*/ @@ -529,7 +529,7 @@ HIDE_TEST(_first_learn_vehicle_50__10){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.00001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -614,33 +614,13 @@ struct status_qlearning *qlstatus = create_status_qlearning (); } #endif -TEST(_first_learn_vehicle_50__11_9){ +HIDE_TEST(_first_learn_vehicle_50__11_9){ size_t nb_block = 7; size_t dim= 2; struct blocks * path = create_blocks(nb_block, dim); #if 1 - - copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); - copy_coordinate(path->upper_bound_block[0], (float[]){100,250}); - copy_coordinate(path->lower_bound_block[1], (float[]){100,0}); - copy_coordinate(path->upper_bound_block[1], (float[]){250,80}); - copy_coordinate(path->lower_bound_block[2], (float[]){250,0}); - copy_coordinate(path->upper_bound_block[2], (float[]){360,140}); - copy_coordinate(path->lower_bound_block[3], (float[]){360,70}); - copy_coordinate(path->upper_bound_block[3], (float[]){600,140}); - copy_coordinate(path->lower_bound_block[4], (float[]){600,90}); - copy_coordinate(path->upper_bound_block[4], (float[]){720,300}); - copy_coordinate(path->lower_bound_block[5], (float[]){300,300}); - copy_coordinate(path->upper_bound_block[5], (float[]){720,350}); - copy_coordinate(path->lower_bound_block[6], (float[]){0,250}); - copy_coordinate(path->upper_bound_block[6], (float[]){410,300}); - - - -#else - copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); copy_coordinate(path->upper_bound_block[0], (float[]){150,250}); copy_coordinate(path->lower_bound_block[1], (float[]){150,0}); @@ -657,9 +637,30 @@ TEST(_first_learn_vehicle_50__11_9){ copy_coordinate(path->upper_bound_block[6], (float[]){410,300}); + + + +#else + + + copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); + copy_coordinate(path->upper_bound_block[0], (float[]){100,250}); + copy_coordinate(path->lower_bound_block[1], (float[]){100,0}); + copy_coordinate(path->upper_bound_block[1], (float[]){250,80}); + copy_coordinate(path->lower_bound_block[2], (float[]){250,0}); + copy_coordinate(path->upper_bound_block[2], (float[]){360,140}); + copy_coordinate(path->lower_bound_block[3], (float[]){360,70}); + copy_coordinate(path->upper_bound_block[3], (float[]){600,140}); + copy_coordinate(path->lower_bound_block[4], (float[]){600,90}); + copy_coordinate(path->upper_bound_block[4], (float[]){720,300}); + copy_coordinate(path->lower_bound_block[5], (float[]){300,300}); + copy_coordinate(path->upper_bound_block[5], (float[]){720,350}); + copy_coordinate(path->lower_bound_block[6], (float[]){0,250}); + copy_coordinate(path->upper_bound_block[6], (float[]){410,300}); + /////////////////////////////////////// - +#if 0 copy_coordinate(path->lower_bound_block[0], (float[]){0,3}); copy_coordinate(path->upper_bound_block[0], (float[]){4,7}); copy_coordinate(path->lower_bound_block[1], (float[]){1,0}); @@ -675,6 +676,7 @@ TEST(_first_learn_vehicle_50__11_9){ copy_coordinate(path->lower_bound_block[6], (float[]){1,7}); copy_coordinate(path->upper_bound_block[6], (float[]){8,9.75}); +#endif #endif @@ -683,15 +685,15 @@ TEST(_first_learn_vehicle_50__11_9){ struct vehicle *car = create_vehicle(path); config_layers *pconf = create_config_layers_from_OneD(4,(size_t[]){3,24,24,3}); /* 3 input , 3 target; 2 hidden layer with 24 neurons each */ - //config_layers *pconf = create_config_layers_from_OneD(4,(size_t[]){3,14,14,3}); /* 3 input , 3 target; 2 hidden layer with 24 neurons each */ + //config_layers *pconf = create_config_layers_from_OneD(4,(size_t[]){3,4,4,3}); /* 3 input , 3 target; 2 hidden layer with 14 neurons each */ bool randomize=true; float minR = -0.5, maxR = 0.5; int randomRange = 500; size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; - float learning_rate = 0.001; /* 0.00001*/ /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + float learning_rate = 0.01; /* 0.00001*/ /* 0.001*/; + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -771,7 +773,7 @@ struct status_qlearning *qlstatus = create_status_qlearning (); } #if 1 -HIDE_TEST(_first_learn_vehicle_50__11){ +TEST(_first_learn_vehicle_50__11){ size_t nb_block = 7; size_t dim= 2; struct blocks * path = create_blocks(nb_block, dim); @@ -873,15 +875,16 @@ copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); struct vehicle *car = create_vehicle(path); config_layers *pconf = create_config_layers_from_OneD(4,(size_t[]){3,24,24,3}); /* 3 input , 3 target; 2 hidden layer with 24 neurons each */ - //config_layers *pconf = create_config_layers_from_OneD(4,(size_t[]){3,14,14,3}); /* 3 input , 3 target; 2 hidden layer with 24 neurons each */ + //config_layers *pconf = create_config_layers_from_OneD(3,(size_t[]){3,24,3}); /* 3 input , 3 target; 2 hidden layer with 24 neurons each */ bool randomize=true; float minR = -0.5, maxR = 0.5; + //float minR = 0, maxR = 1; int randomRange = 500; size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; - float learning_rate = 0; /* 0.000001*/ /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + float learning_rate = 0.0007 /*0.001*//* 0.0001*/; /* 0.000001*/ /* 0.001*/; + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -894,8 +897,8 @@ EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->target_net, wei //EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->main_net, weight_in, ".ff_main_20250508_17h50m56s_26300.txt"); //EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->target_net, weight_in, ".ff_target_20250508_17h50m56s_26300.txt"); -EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->main_net, weight_in, ".ff_main_.symlink"); -EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->target_net, weight_in, ".ff_target_.symlink"); +///EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->main_net, weight_in, ".ff_main_.symlink"); +///EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->target_net, weight_in, ".ff_target_.symlink"); /* EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->main_net, weight_in, ".ff_main_20250508_23h02m40s_29000.txt"); EXTRACT_FILE_TO_TENSOR_ATTRIBUTE_NNEURONS(TYPE_FLOAT, nnetworks->target_net, weight_in, ".ff_target_20250508_23h02m40s_29000.txt"); @@ -910,7 +913,7 @@ struct status_qlearning *qlstatus = create_status_qlearning (); 0.95/*float gamma*/, learning_rate, 0 /* (not used!)float discount_factor*/, - 0.0001/*0.99*/ /*float exploration_factor*/, + 1.0/*0.99*//*0.0001*//*0.99*/ /*float exploration_factor*/, 20/*long int nb_training_before_update_weight_in_target*/, 10000/*size_t number_episodes*/ ); @@ -1086,7 +1089,7 @@ copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.0000001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1233,7 +1236,7 @@ HIDE_TEST(__first_learn_vehicle13){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1371,7 +1374,7 @@ HIDE_TEST(first_learn_vehicle){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -1482,7 +1485,7 @@ HIDE_TEST(first_learn_vehicle){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2006,7 +2009,7 @@ HIDE_TEST(first_learn_vehicle_rev50_8){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.001; // 0.00001 /*0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2170,7 +2173,7 @@ HIDE_TEST(first_learn_vehicle_50__9){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.00001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2340,7 +2343,7 @@ HIDE_TEST(first_learn_vehicle_50__10){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.00001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2512,7 +2515,7 @@ copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0; /* 0.000001*/ /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2691,7 +2694,7 @@ copy_coordinate(path->lower_bound_block[0], (float[]){0,0}); size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.0000001 /* 0.001*/; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2813,7 +2816,7 @@ HIDE_TEST(_first_learn_vehicle13){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -2951,7 +2954,7 @@ HIDE_TEST(first_learn_vehicle){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, @@ -3062,7 +3065,7 @@ HIDE_TEST(first_learn_vehicle){ size_t nb_prod_thread = 2; size_t nb_calc_thread = 4; float learning_rate = 0.1; - struct networks_qlearning *nnetworks = create_nework_qlearning( + struct networks_qlearning *nnetworks = create_network_qlearning( pconf, randomize, minR, maxR, randomRange, nb_prod_thread, nb_calc_thread, diff --git a/ytest_t/libytest.so b/ytest_t/libytest.so index a3c335a0cb3737fb9dcab1666fa5c871cbd6d7cd..b0f16d54cd98e009efe8a45ada906254147e26fc 100755 GIT binary patch delta 20170 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 X_+TI=#619i_vfelrvjC4;hz8h@J;72 delta 20147 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