genann.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. * GENANN - Minimal C Artificial Neural Network
  3. *
  4. * Copyright (c) 2015, 2016 Lewis Van Winkle
  5. *
  6. * http://CodePlea.com
  7. *
  8. * This software is provided 'as-is', without any express or implied
  9. * warranty. In no event will the authors be held liable for any damages
  10. * arising from the use of this software.
  11. *
  12. * Permission is granted to anyone to use this software for any purpose,
  13. * including commercial applications, and to alter it and redistribute it
  14. * freely, subject to the following restrictions:
  15. *
  16. * 1. The origin of this software must not be misrepresented; you must not
  17. * claim that you wrote the original software. If you use this software
  18. * in a product, an acknowledgement in the product documentation would be
  19. * appreciated but is not required.
  20. * 2. Altered source versions must be plainly marked as such, and must not be
  21. * misrepresented as being the original software.
  22. * 3. This notice may not be removed or altered from any source distribution.
  23. *
  24. */
  25. #include "genann.h"
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <math.h>
  29. #include <assert.h>
  30. #include <stdio.h>
  31. #define LOOKUP_SIZE 4096
  32. double genann_act_sigmoid(double a) {
  33. if (a < -45.0) return 0;
  34. if (a > 45.0) return 1;
  35. return 1.0 / (1 + exp(-a));
  36. }
  37. double genann_act_sigmoid_cached(double a) {
  38. /* If you're optimizing for memory usage, just
  39. * delete this entire function and replace references
  40. * of genann_act_sigmoid_cached to genann_act_sigmoid
  41. */
  42. const double min = -15.0;
  43. const double max = 15.0;
  44. static double interval;
  45. static int initialized = 0;
  46. static double lookup[LOOKUP_SIZE];
  47. /* Calculate entire lookup table on first run. */
  48. if (!initialized) {
  49. interval = (max - min) / LOOKUP_SIZE;
  50. int i;
  51. for (i = 0; i < LOOKUP_SIZE; ++i) {
  52. lookup[i] = genann_act_sigmoid(min + interval * i);
  53. }
  54. /* This is down here to make this thread safe. */
  55. initialized = 1;
  56. }
  57. int i;
  58. i = (int)((a-min)/interval+0.5);
  59. if (i <= 0) return lookup[0];
  60. if (i >= LOOKUP_SIZE) return lookup[LOOKUP_SIZE-1];
  61. return lookup[i];
  62. }
  63. double genann_act_threshold(double a) {
  64. return a > 0;
  65. }
  66. double genann_act_linear(double a) {
  67. return a;
  68. }
  69. genann *genann_init(int inputs, int hidden_layers, int hidden, int outputs) {
  70. if (hidden_layers < 0) return 0;
  71. if (inputs < 1) return 0;
  72. if (outputs < 1) return 0;
  73. if (hidden_layers > 0 && hidden < 1) return 0;
  74. const int hidden_weights = hidden_layers ? (inputs+1) * hidden + (hidden_layers-1) * (hidden+1) * hidden : 0;
  75. const int output_weights = (hidden_layers ? (hidden+1) : (inputs+1)) * outputs;
  76. const int total_weights = (hidden_weights + output_weights);
  77. const int total_neurons = (inputs + hidden * hidden_layers + outputs);
  78. /* Allocate extra size for weights, outputs, and deltas. */
  79. const int size = sizeof(genann) + sizeof(double) * (total_weights + total_neurons + (total_neurons - inputs));
  80. genann *ret = malloc(size);
  81. if (!ret) return 0;
  82. ret->inputs = inputs;
  83. ret->hidden_layers = hidden_layers;
  84. ret->hidden = hidden;
  85. ret->outputs = outputs;
  86. ret->total_weights = total_weights;
  87. ret->total_neurons = total_neurons;
  88. /* Set pointers. */
  89. ret->weight = (double*)((char*)ret + sizeof(genann));
  90. ret->output = ret->weight + ret->total_weights;
  91. ret->delta = ret->output + ret->total_neurons;
  92. genann_randomize(ret);
  93. ret->activation_hidden = genann_act_sigmoid_cached;
  94. ret->activation_output = genann_act_sigmoid_cached;
  95. return ret;
  96. }
  97. genann *genann_read(FILE *in) {
  98. int inputs, hidden_layers, hidden, outputs;
  99. fscanf(in, "%d %d %d %d", &inputs, &hidden_layers, &hidden, &outputs);
  100. genann *ann = genann_init(inputs, hidden_layers, hidden, outputs);
  101. int i;
  102. for (i = 0; i < ann->total_weights; ++i) {
  103. fscanf(in, " %le", ann->weight + i);
  104. }
  105. return ann;
  106. }
  107. genann *genann_copy(genann const *ann) {
  108. const int size = sizeof(genann) + sizeof(double) * (ann->total_weights + ann->total_neurons + (ann->total_neurons - ann->inputs));
  109. genann *ret = malloc(size);
  110. if (!ret) return 0;
  111. memcpy(ret, ann, size);
  112. /* Set pointers. */
  113. ret->weight = (double*)((char*)ret + sizeof(genann));
  114. ret->output = ret->weight + ret->total_weights;
  115. ret->delta = ret->output + ret->total_neurons;
  116. return ret;
  117. }
  118. void genann_randomize(genann *ann) {
  119. int i;
  120. for (i = 0; i < ann->total_weights; ++i) {
  121. double r = GENANN_RANDOM();
  122. /* Sets weights from -0.5 to 0.5. */
  123. ann->weight[i] = r - 0.5;
  124. }
  125. }
  126. void genann_free(genann *ann) {
  127. /* The weight, output, and delta pointers go to the same buffer. */
  128. free(ann);
  129. }
  130. double const *genann_run(genann const *ann, double const *inputs) {
  131. double const *w = ann->weight;
  132. double *o = ann->output + ann->inputs;
  133. double const *i = ann->output;
  134. /* Copy the inputs to the scratch area, where we also store each neuron's
  135. * output, for consistency. This way the first layer isn't a special case. */
  136. memcpy(ann->output, inputs, sizeof(double) * ann->inputs);
  137. int h, j, k;
  138. const genann_actfun act = ann->activation_hidden;
  139. const genann_actfun acto = ann->activation_output;
  140. /* Figure hidden layers, if any. */
  141. for (h = 0; h < ann->hidden_layers; ++h) {
  142. for (j = 0; j < ann->hidden; ++j) {
  143. double sum = 0;
  144. for (k = 0; k < (h == 0 ? ann->inputs : ann->hidden) + 1; ++k) {
  145. if (k == 0) {
  146. sum += *w++ * -1.0;
  147. } else {
  148. sum += *w++ * i[k-1];
  149. }
  150. }
  151. *o++ = act(sum);
  152. }
  153. i += (h == 0 ? ann->inputs : ann->hidden);
  154. }
  155. double const *ret = o;
  156. /* Figure output layer. */
  157. for (j = 0; j < ann->outputs; ++j) {
  158. double sum = 0;
  159. for (k = 0; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; ++k) {
  160. if (k == 0) {
  161. sum += *w++ * -1.0;
  162. } else {
  163. sum += *w++ * i[k-1];
  164. }
  165. }
  166. *o++ = acto(sum);
  167. }
  168. /* Sanity check that we used all weights and wrote all outputs. */
  169. assert(w - ann->weight == ann->total_weights);
  170. assert(o - ann->output == ann->total_neurons);
  171. return ret;
  172. }
  173. void genann_train(genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate) {
  174. /* To begin with, we must run the network forward. */
  175. genann_run(ann, inputs);
  176. int h, j, k;
  177. /* First set the output layer deltas. */
  178. {
  179. double const *o = ann->output + ann->inputs + ann->hidden * ann->hidden_layers; /* First output. */
  180. double *d = ann->delta + ann->hidden * ann->hidden_layers; /* First delta. */
  181. double const *t = desired_outputs; /* First desired output. */
  182. /* Set output layer deltas. */
  183. if (ann->activation_output == genann_act_linear) {
  184. for (j = 0; j < ann->outputs; ++j) {
  185. *d++ = *t++ - *o++;
  186. }
  187. } else {
  188. for (j = 0; j < ann->outputs; ++j) {
  189. *d++ = (*t - *o) * *o * (1.0 - *o);
  190. ++o; ++t;
  191. }
  192. }
  193. }
  194. /* Set hidden layer deltas, start on last layer and work backwards. */
  195. /* Note that loop is skipped in the case of hidden_layers == 0. */
  196. for (h = ann->hidden_layers - 1; h >= 0; --h) {
  197. /* Find first output and delta in this layer. */
  198. double const *o = ann->output + ann->inputs + (h * ann->hidden);
  199. double *d = ann->delta + (h * ann->hidden);
  200. /* Find first delta in following layer (which may be hidden or output). */
  201. double const * const dd = ann->delta + ((h+1) * ann->hidden);
  202. /* Find first weight in following layer (which may be hidden or output). */
  203. double const * const ww = ann->weight + ((ann->inputs+1) * ann->hidden) + ((ann->hidden+1) * ann->hidden * (h));
  204. for (j = 0; j < ann->hidden; ++j) {
  205. double delta = 0;
  206. for (k = 0; k < (h == ann->hidden_layers-1 ? ann->outputs : ann->hidden); ++k) {
  207. const double forward_delta = dd[k];
  208. const int windex = k * (ann->hidden + 1) + (j + 1);
  209. const double forward_weight = ww[windex];
  210. delta += forward_delta * forward_weight;
  211. }
  212. *d = *o * (1.0-*o) * delta;
  213. ++d; ++o;
  214. }
  215. }
  216. /* Train the outputs. */
  217. {
  218. /* Find first output delta. */
  219. double const *d = ann->delta + ann->hidden * ann->hidden_layers; /* First output delta. */
  220. /* Find first weight to first output delta. */
  221. double *w = ann->weight + (ann->hidden_layers
  222. ? ((ann->inputs+1) * ann->hidden + (ann->hidden+1) * ann->hidden * (ann->hidden_layers-1))
  223. : (0));
  224. /* Find first output in previous layer. */
  225. double const * const i = ann->output + (ann->hidden_layers
  226. ? (ann->inputs + (ann->hidden) * (ann->hidden_layers-1))
  227. : 0);
  228. /* Set output layer weights. */
  229. for (j = 0; j < ann->outputs; ++j) {
  230. for (k = 0; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; ++k) {
  231. if (k == 0) {
  232. *w++ += *d * learning_rate * -1.0;
  233. } else {
  234. *w++ += *d * learning_rate * i[k-1];
  235. }
  236. }
  237. ++d;
  238. }
  239. assert(w - ann->weight == ann->total_weights);
  240. }
  241. /* Train the hidden layers. */
  242. for (h = ann->hidden_layers - 1; h >= 0; --h) {
  243. /* Find first delta in this layer. */
  244. double const *d = ann->delta + (h * ann->hidden);
  245. /* Find first input to this layer. */
  246. double const *i = ann->output + (h
  247. ? (ann->inputs + ann->hidden * (h-1))
  248. : 0);
  249. /* Find first weight to this layer. */
  250. double *w = ann->weight + (h
  251. ? ((ann->inputs+1) * ann->hidden + (ann->hidden+1) * (ann->hidden) * (h-1))
  252. : 0);
  253. for (j = 0; j < ann->hidden; ++j) {
  254. for (k = 0; k < (h == 0 ? ann->inputs : ann->hidden) + 1; ++k) {
  255. if (k == 0) {
  256. *w++ += *d * learning_rate * -1.0;
  257. } else {
  258. *w++ += *d * learning_rate * i[k-1];
  259. }
  260. }
  261. ++d;
  262. }
  263. }
  264. }
  265. void genann_write(genann const *ann, FILE *out) {
  266. fprintf(out, "%d %d %d %d", ann->inputs, ann->hidden_layers, ann->hidden, ann->outputs);
  267. int i;
  268. for (i = 0; i < ann->total_weights; ++i) {
  269. fprintf(out, " %.20e", ann->weight[i]);
  270. }
  271. }