|
|
|
12 | int main( int argc, char *argv[ ] ) { |
13 | printf ( "Train a small ANN on the XOR function using backpropagation." ); |
20 | const double input[4][2] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}}; |
21 | const double output[4] = {0, 1, 1, 0}; |
22 | double t1 = atoi ( argv[1] ); |
23 | double t2 = atoi ( argv[2] ); |
24 | double t3 = atoi ( argv[3] ); |
25 | double t4 = atoi ( argv[4] ); |
29 | genann *ann = genann_init( 2, 1, 2, 1 ); |
33 | for ( i = 0; i < 300; ++i ) { |
34 | genann_train( ann, input[0], &t1, 3 ); |
35 | genann_train( ann, input[1], &t2, 3 ); |
36 | genann_train( ann, input[2], &t3, 3 ); |
37 | genann_train( ann, input[3], &t4, 3 ); |
41 | if ( ( strcmp (argv[5], "0" ) == 0 ) && ( strcmp (argv[6], "0" ) == 0 ) ) |
42 | printf ( "0 XOR 0 = %1.f.\n" , *genann_run( ann, input[0] ) ); |
43 | else if ( ( strcmp (argv[5], "0" ) == 0 ) && ( strcmp (argv[6], "1" ) == 0 ) ) |
44 | printf ( "0 XOR 1 = %1.f.\n" , *genann_run( ann, input[1] ) ); |
45 | else if ( ( strcmp (argv[5], "1" ) == 0 ) && ( strcmp (argv[6], "0" ) == 0 ) ) |
46 | printf ( "1 XOR 0 = %1.f.\n" , *genann_run( ann, input[2] ) ); |
47 | else if ( ( strcmp (argv[5], "1" ) == 0 ) && ( strcmp (argv[6], "1" ) == 0 ) ) |
48 | printf ( "1 XOR 1 = %1.f.\n" , *genann_run( ann, input[3] ) ); |
|
|
22 | #define GENANN_RANDOM( ) ( ( (double) rand( ) ) / RAND_MAX ) |
27 | typedef double (*genann_actfun) ( const struct genann *ann, double a ); |
29 | typedef struct genann { |
31 | int inputs, hidden_layers, hidden, outputs; |
34 | genann_actfun activation_hidden; |
37 | genann_actfun activation_output; |
58 | genann *genann_init( int inputs, int hidden_layers, int hidden, int outputs ); |
61 | genann *genann_read( FILE *in ); |
64 | void genann_randomize( genann *ann ); |
67 | genann *genann_copy( genann const *ann ); |
70 | void genann_free( genann *ann ); |
73 | double const *genann_run( genann const *ann, double const *inputs ); |
76 | void genann_train( genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate ); |
79 | void genann_write( genann const *ann, FILE *out ); |
81 | void genann_init_sigmoid_lookup( const genann *ann ); |
82 | double genann_act_sigmoid( const genann *ann, double a ); |
83 | double genann_act_sigmoid_cached( const genann *ann, double a ); |
84 | double genann_act_threshold( const genann *ann, double a ); |
85 | double genann_act_linear( const genann *ann, double a ); |
|
|
011 | #define genann_act_hidden genann_act_hidden_indirect |
012 | #define genann_act_output genann_act_output_indirect |
014 | #define genann_act_hidden genann_act |
015 | #define genann_act_output genann_act |
018 | #define LOOKUP_SIZE 4015. |
020 | double genann_act_hidden_indirect( const struct genann *ann, double a ) { |
021 | return ann->activation_hidden( ann, a ); |
024 | double genann_act_output_indirect( const struct genann *ann, double a ) { |
025 | return ann->activation_output( ann, a ); |
028 | const double sigmoid_dom_min = -15.0; |
029 | const double sigmoid_dom_max = 15.0; |
031 | double lookup[LOOKUP_SIZE]; |
034 | #define likely(x) __builtin_expect(!!(x), 1) |
035 | #define unlikely(x) __builtin_expect(!!(x), 0) |
036 | #define unused __attribute__((unused)) |
041 | #pragma warning(disable : 415.6) /* For fscanf */ |
044 | double genann_act_sigmoid( const genann *ann unused, double a ) { |
045 | if ( a < -45.0 ) return 0; |
046 | if ( a > 45.0 ) return 1; |
047 | return 1.0 / ( 1 + exp ( -a ) ); |
050 | void genann_init_sigmoid_lookup( const genann *ann ) { |
051 | const double f = (sigmoid_dom_max - sigmoid_dom_min) / LOOKUP_SIZE; |
053 | interval = LOOKUP_SIZE / ( sigmoid_dom_max - sigmoid_dom_min ); |
054 | for ( i = 0; i < LOOKUP_SIZE; ++i ) { |
055 | lookup[i] = genann_act_sigmoid( ann, sigmoid_dom_min + f * i ); |
059 | double genann_act_sigmoid_cached( const genann *ann unused, double a ) { |
060 | assert ( !isnan( a ) ); |
061 | if ( a < sigmoid_dom_min ) return lookup[0]; |
062 | if ( a >= sigmoid_dom_max ) return lookup[LOOKUP_SIZE - 1]; |
063 | size_t j = ( size_t )( ( a-sigmoid_dom_min ) * interval + 0.5 ); |
065 | if ( unlikely( j >= LOOKUP_SIZE ) ) return lookup[LOOKUP_SIZE - 1]; |
069 | double genann_act_linear( const struct genann *ann unused, double a ) { |
073 | double genann_act_threshold( const struct genann *ann unused, double a ) { |
077 | genann *genann_init( int inputs, int hidden_layers, int hidden, int outputs ) { |
078 | if ( hidden_layers < 0 ) return 0; |
079 | if ( inputs < 1 ) return 0; |
080 | if ( outputs < 1 ) return 0; |
081 | if ( hidden_layers > 0 && hidden < 1 ) return 0; |
083 | const int hidden_weights = hidden_layers ? (inputs+1) * hidden + (hidden_layers-1) * (hidden+1) * hidden : 0; |
084 | const int output_weights = (hidden_layers ? (hidden+1) : (inputs+1)) * outputs; |
085 | const int total_weights = (hidden_weights + output_weights); |
086 | const int total_neurons = (inputs + hidden * hidden_layers + outputs); |
089 | const int size = sizeof (genann) + sizeof ( double ) * (total_weights + total_neurons + (total_neurons - inputs)); |
090 | genann *ret = malloc (size); |
091 | if ( !ret ) return 0; |
093 | ret->inputs = inputs; |
094 | ret->hidden_layers = hidden_layers; |
095 | ret->hidden = hidden; |
096 | ret->outputs = outputs; |
098 | ret->total_weights = total_weights; |
099 | ret->total_neurons = total_neurons; |
102 | ret->weight = ( double *) (( char *)ret + sizeof (genann)); |
103 | ret->output = ret->weight + ret->total_weights; |
104 | ret->delta = ret->output + ret->total_neurons; |
106 | genann_randomize( ret ); |
107 | ret->activation_hidden = genann_act_sigmoid_cached; |
108 | ret->activation_output = genann_act_sigmoid_cached; |
109 | genann_init_sigmoid_lookup( ret ); |
113 | genann *genann_read( FILE *in ) { |
114 | int inputs, hidden_layers, hidden, outputs; |
118 | rc = fscanf ( in, "%d %d %d %d" , &inputs, &hidden_layers, &hidden, &outputs ); |
119 | if ( rc < 4 || errno != 0 ) { |
123 | genann *ann = genann_init( inputs, hidden_layers, hidden, outputs ); |
125 | for ( i = 0; i < ann->total_weights; ++i ) { |
127 | rc = fscanf (in, " %le" , ann->weight + i ); |
128 | if ( rc < 1 || errno != 0 ) { |
137 | genann *genann_copy( genann const *ann ) { |
138 | const int size = sizeof (genann) + sizeof ( double ) * (ann->total_weights + ann->total_neurons + (ann->total_neurons - ann->inputs) ); |
139 | genann *ret = malloc ( size ); |
140 | if ( !ret ) return 0; |
141 | memcpy ( ret, ann, size ); |
143 | ret->weight = ( double *) ( ( char *)ret + sizeof (genann) ); |
144 | ret->output = ret->weight + ret->total_weights; |
145 | ret->delta = ret->output + ret->total_neurons; |
149 | void genann_randomize( genann *ann ) { |
151 | for ( i = 0; i < ann->total_weights; ++i ) { |
152 | double r = GENANN_RANDOM( ); |
154 | ann->weight[i] = r - 0.5; |
158 | void genann_free( genann *ann ) { |
163 | double const *genann_run(genann const *ann, double const *inputs) { |
164 | double const *w = ann->weight; |
165 | double *o = ann->output + ann->inputs; |
166 | double const *i = ann->output; |
170 | memcpy (ann->output, inputs, sizeof ( double ) * ann->inputs); |
172 | if ( !ann->hidden_layers ) { |
174 | for ( j = 0; j < ann->outputs; ++j ) { |
175 | double sum = *w++ * -1.0; |
176 | for ( k = 0; k < ann->inputs; ++k ) { |
179 | *o++ = genann_act_output( ann, sum ); |
185 | for ( j = 0; j < ann->hidden; ++j ) { |
186 | double sum = *w++ * -1.0; |
187 | for ( k = 0; k < ann->inputs; ++k ) { |
190 | *o++ = genann_act_hidden( ann, sum ); |
194 | for ( h = 1; h < ann->hidden_layers; ++h ) { |
195 | for ( j = 0; j < ann->hidden; ++j ) { |
196 | double sum = *w++ * -1.0; |
197 | for ( k = 0; k < ann->hidden; ++k ) { |
200 | *o++ = genann_act_hidden( ann, sum ); |
204 | double const *ret = o; |
206 | for ( j = 0; j < ann->outputs; ++j ) { |
207 | double sum = *w++ * -1.0; |
208 | for ( k = 0; k < ann->hidden; ++k ) { |
211 | *o++ = genann_act_output( ann, sum ); |
214 | assert ( w - ann->weight == ann->total_weights ); |
215 | assert ( o - ann->output == ann->total_neurons ); |
220 | void genann_train( genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate ) { |
222 | genann_run( ann, inputs ); |
226 | double const *o = ann->output + ann->inputs + ann->hidden * ann->hidden_layers; |
227 | double *d = ann->delta + ann->hidden * ann->hidden_layers; |
228 | double const *t = desired_outputs; |
230 | if ( genann_act_output == genann_act_linear || |
231 | ann->activation_output == genann_act_linear ) { |
232 | for ( j = 0; j < ann->outputs; ++j ) { |
237 | for ( j = 0; j < ann->outputs; ++j ) { |
238 | *d++ = ( *t - *o ) * *o * ( 1.0 - *o ); |
245 | for ( h = ann->hidden_layers - 1; h >= 0; --h ) { |
247 | double const *o = ann->output + ann->inputs + ( h * ann->hidden ); |
248 | double *d = ann->delta + ( h * ann->hidden ); |
251 | double const * const dd = ann->delta + ( (h+1) * ann->hidden ); |
254 | double const * const ww = ann->weight + ((ann->inputs+1) * ann->hidden) + ((ann->hidden+1) * ann->hidden * (h)); |
255 | for ( j = 0; j < ann->hidden; ++j ) { |
257 | for ( k = 0; k < ( h == ann->hidden_layers-1 ? ann->outputs : ann->hidden ); ++k ) { |
258 | const double forward_delta = dd[k]; |
259 | const int windex = k * ( ann->hidden + 1 ) + ( j + 1 ); |
260 | const double forward_weight = ww[windex]; |
261 | delta += forward_delta * forward_weight; |
263 | *d = *o * (1.0-*o) * delta; |
270 | double const *d = ann->delta + ann->hidden * ann->hidden_layers; |
272 | double *w = ann->weight + (ann->hidden_layers |
273 | ? ( (ann->inputs+1) * ann->hidden + (ann->hidden+1) * ann->hidden * (ann->hidden_layers-1) ) |
276 | double const * const i = ann->output + (ann->hidden_layers |
277 | ? ( ann->inputs + (ann->hidden) * (ann->hidden_layers-1) ) |
280 | for ( j = 0; j < ann->outputs; ++j ) { |
281 | *w++ += *d * learning_rate * -1.0; |
282 | for ( k = 1; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; ++k ) { |
283 | *w++ += *d * learning_rate * i[k-1]; |
287 | assert ( w - ann->weight == ann->total_weights ); |
290 | for ( h = ann->hidden_layers - 1; h >= 0; --h ) { |
292 | double const *d = ann->delta + ( h * ann->hidden ); |
294 | double const *i = ann->output + ( h |
295 | ? ( ann->inputs + ann->hidden * (h-1) ) |
298 | double *w = ann->weight + ( h |
299 | ? ( (ann->inputs+1) * ann->hidden + (ann->hidden+1) * (ann->hidden) * (h-1) ) |
301 | for ( j = 0; j < ann->hidden; ++j ) { |
302 | *w++ += *d * learning_rate * -1.0; |
303 | for ( k = 1; k < ( h == 0 ? ann->inputs : ann->hidden ) + 1; ++k ) { |
304 | *w++ += *d * learning_rate * i[k-1]; |
311 | void genann_write( genann const *ann, FILE *out ) { |
312 | fprintf ( out, "%d %d %d %d" , ann->inputs, ann->hidden_layers, ann->hidden, ann->outputs ); |
315 | for ( i = 0; i < ann->total_weights; ++i ) { |
316 | fprintf ( out, " %.20e" , ann->weight[i] ); |
|