// // nnskelet.cc // C++-programma voor neuraal netwerk (NN) met \'e\'en output-knoop // TODO: 1,2,3,4,5,6,7 // Zie www.liacs.leidenuniv.nl/~kosterswa/AI/nnhelp.pdf // 4 april 2024 // Compileren: g++ -Wall -O3 -o nn nnskelet.cc // Gebruik: ./nn // Voorbeeld: ./nn 2 3 100000 1 1234 // Let op: de inputs komen in input[1],...,input[inputs] (tot en // met inputs) en de verborgen knopen worden geindiceerd met // 0,1,...,hiddens (tot en met hiddens) // Als = 0: random (via de tijd) // 1: sigmoid; 2: softplus; 3: ReLU // #include #include #include #include #include using namespace std; const int MAX = 20; const double ALPHA = 0.1; const double BETA = 1.0; // activatie-functie (sigmoid) double sigmoid (double x) { return 1 / ( 1 + exp ( - BETA * x ) ); }//sigmoid // afgeleide van sigmoid double sigmoidprime (double x) { return BETA * sigmoid (x) * ( 1 - sigmoid (x) ); }//sigmoidprime // activatie-functie (softplus) double softplus (double x) { return log ( 1 + exp (x) ); }//softplus // afgeleide van softplus double softplusprime (double x) { return exp (x) / ( 1 + exp (x) ); }//softplusprime // activatie-functie (ReLU) double ReLU (double x) { if ( x >= 0 ) { return x; }//if return 0; }//ReLU // "afgeleide" van ReLU double ReLUprime (double x) { if ( x >= 0 ) { return 1; }//if return 0; }//ReLUprime // activatie-functie van verschillend type double g (double x, int type) { if ( type == 1 ) { return sigmoid (x); }//if else if ( type == 2 ) { return softplus (x); }//if else if ( type == 3 ) { return ReLU (x); }//if return 0; }//g // afgeleide van activatie-functie van verschillend type double gprime (double x, int type) { if ( type == 1 ) { return sigmoidprime (x); }//if else if ( type == 2 ) { return softplusprime (x); }//if else if ( type == 3 ) { return ReLUprime (x); }//if return 0; }//gprime // haal een voorbeeld (input,target) uit poker-dataset void getexample (ifstream & pokerdata, double input[ ], double & target) { char kar = pokerdata.get ( ); if ( pokerdata.eof ( ) ) { pokerdata.close ( ); pokerdata.open ("poker-hand-testing.data"); kar = pokerdata.get ( ); }//if int getal = 0; int count = 1; bool lastnumber = false; target = 0; while ( count < 12 && ! pokerdata.eof ( ) ) { if ( '0' <= kar && kar <= '9' ) { getal = 10 * getal + kar - '0'; lastnumber = true; }//if else if ( lastnumber ) { if ( count < 11 ) { input[count] = getal; }//if else { target = getal; }//else count++; getal = 0; lastnumber = false; }//if kar = pokerdata.get ( ); }//while }//getexample int main (int argc, char* argv[ ]) { int inputs, hiddens; // aantal invoer- en verborgen knopen // inputs is exclusief de bias-knoop! double input[MAX]; // de invoer is input[1]...input[inputs] double inputtohidden[MAX][MAX]; // gewichten van invoerknopen 0..inputs // naar verborgen knopen 1..hiddens double hiddentooutput[MAX]; // gewichten van verborgen knopen 0..hiddens // naar de ene uitvoerknoop double inhidden[MAX]; // invoer voor de verborgen knopen 1..hiddens double acthidden[MAX]; // en de uitvoer daarvan double inoutput; // invoer voor de ene uitvoerknoop double netoutput; // en de uitvoer daarvan: de net-uitvoer double target; // gewenste uitvoer double error; // verschil tussen gewenste en // geproduceerde uitvoer double delta; // de delta voor de uitvoerknoop double deltahidden[MAX]; // de delta's voor de verborgen // knopen 1..hiddens int epochs; // aantal trainingsvoorbeelden int cnt, i, j; // tellertjes int type; // welke activatie-functie? int seed; // voor random number generator if ( argc != 6 ) { cout << "Gebruik: " << argv[0] << " " << " " << endl; return 1; }//if inputs = atoi (argv[1]); if ( inputs >= MAX ) { cout << "Aantal inputs < " << MAX << "!" << endl; return 1; }//if hiddens = atoi (argv[2]); if ( hiddens >= MAX ) { cout << "Aantal verborgen knopen < " << MAX << "!" << endl; return 1; }//if epochs = atoi (argv[3]); type = atoi (argv[4]); seed = atoi (argv[5]); input[0] = -1; // invoer bias-knoop: altijd -1 acthidden[0] = -1; // verborgen bias-knoop: altijd -1 if ( seed == 0 ) { srand (time (NULL)); }//if else { srand (seed); }//else //LATER TODO-7 gebruik de poker-dataset (met inputs = 10): // ifstream pokerdata ("poker-hand-testing.data",ios::in); // if ( pokerdata.fail ( ) ) { // cout << "Inputfile bestaat niet!" << endl; // return 1; // }//if // // nu kun je met // getexample (pokerdata,input,target); // een voorbeeld uit de poker-dataset halen // vergeet niet tussen 0 en 1 te normaliseren, en zeker de target // // en aan het eind van het programma eigenlijk: // pokerdata.close ( ); //TODO-1 initialiseer de gewichten random tussen -1 en 1: // inputtohidden en hiddentooutput // rand ( ) levert geheel randomgetal tussen 0 en RAND_MAX; denk aan casten for ( cnt = 1; cnt <= epochs; cnt++ ) { //TODO-2 lees voorbeeld in naar input/target, of genereer ter plekke: // als voorbeeld: de XOR-functie, waarvoor geldt dat inputs = 2 // int x = rand ( ) % 2; int y = rand ( ) % 2; int dexor = ( x + y ) % 2; // input[1] = x; input[2] = y; target = dexor; //LATER TODO-7 poker, met inputs = 10: // getexample (pokerdata,input,target); //TODO-3 stuur het voorbeeld door het netwerk // reken inhidden's uit, acthidden's, inoutput en netoutput //TODO-4 bereken error, delta, en deltahidden //TODO-5 update gewichten hiddentooutput en inputtohidden //TODO-6 beoordeel het netwerk en rapporteer // bijvoorbeeld (totaal)fout bij laatste 1000 voorbeelden }//for return 0; }//main